From ed9ba7e185ac8512b63033c8380e30128a7b3cf9 Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 30 Dec 2015 20:42:12 +0000 Subject: [PATCH 0001/5615] Add simple example for testing a Redis-backed cache --- .gitignore | 59 +++++++++++++++ pom.xml | 74 +++++++++++++++++++ redis-backed-cache/pom.xml | 27 +++++++ .../main/java/com/mycompany/cache/Cache.java | 27 +++++++ .../com/mycompany/cache/RedisBackedCache.java | 39 ++++++++++ .../src/test/java/RedisBackedCacheTest.java | 49 ++++++++++++ .../src/test/resources/logback-test.xml | 19 +++++ 7 files changed, 294 insertions(+) create mode 100644 .gitignore create mode 100644 pom.xml create mode 100644 redis-backed-cache/pom.xml create mode 100644 redis-backed-cache/src/main/java/com/mycompany/cache/Cache.java create mode 100644 redis-backed-cache/src/main/java/com/mycompany/cache/RedisBackedCache.java create mode 100644 redis-backed-cache/src/test/java/RedisBackedCacheTest.java create mode 100644 redis-backed-cache/src/test/resources/logback-test.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000000..a6a7be722ff --- /dev/null +++ b/.gitignore @@ -0,0 +1,59 @@ +# Created by .ignore support plugin (hsz.mobi) +### Maven template +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio + +*.iml + +## Directory-based project format: +.idea/ +# if you remove the above rule, at least ignore the following: + +# User-specific stuff: +# .idea/workspace.xml +# .idea/tasks.xml +# .idea/dictionaries + +# Sensitive or high-churn files: +# .idea/dataSources.ids +# .idea/dataSources.xml +# .idea/sqlDataSources.xml +# .idea/dynamic.xml +# .idea/uiDesigner.xml + +# Gradle: +# .idea/gradle.xml +# .idea/libraries + +# Mongo Explorer plugin: +# .idea/mongoSettings.xml + +## File-based project format: +*.ipr +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties + diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000000..ffa56cdb4da --- /dev/null +++ b/pom.xml @@ -0,0 +1,74 @@ + + + 4.0.0 + + org.testcontainers.examples + testcontainers-java-examples + NOVERSION + + redis-backed-cache + + + pom + + + + org.testcontainers + testcontainers + 0.9.8 + + + + junit + junit + 4.12 + provided + + + + + org.projectlombok + lombok + 1.16.6 + provided + + + + + org.slf4j + slf4j-api + 1.7.7 + provided + + + ch.qos.logback + logback-classic + 1.1.2 + test + + + org.rnorth.visible-assertions + visible-assertions + 1.0.2 + test + + + + + + + + maven-compiler-plugin + 3.1 + + 1.8 + 1.8 + + + + + + + UTF-8 + + diff --git a/redis-backed-cache/pom.xml b/redis-backed-cache/pom.xml new file mode 100644 index 00000000000..082530329c5 --- /dev/null +++ b/redis-backed-cache/pom.xml @@ -0,0 +1,27 @@ + + + + testcontainers-java-examples + org.testcontainers.examples + NOVERSION + + 4.0.0 + + redis-backed-cache + + + redis.clients + jedis + 2.8.0 + + + com.google.code.gson + gson + 2.5 + + + + + \ No newline at end of file diff --git a/redis-backed-cache/src/main/java/com/mycompany/cache/Cache.java b/redis-backed-cache/src/main/java/com/mycompany/cache/Cache.java new file mode 100644 index 00000000000..996c31ecac7 --- /dev/null +++ b/redis-backed-cache/src/main/java/com/mycompany/cache/Cache.java @@ -0,0 +1,27 @@ +package com.mycompany.cache; + +import java.util.Optional; + +/** + * Cache, for storing data associated with keys. + */ +public interface Cache { + + /** + * Store a value object in the cache with no specific expiry time. The object may be evicted by the cache any time, + * if necessary. + * + * @param key key that may be used to retrieve the object in the future + * @param value the value object to be stored + */ + void put(String key, Object value); + + /** + * Retrieve a value object from the cache. + * @param key the key that was used to insert the object initially + * @param expectedClass for convenience, a class that the object should be cast to before being returned + * @param the class of the returned object + * @return the object if it was in the cache, or an empty Optional if not found. + */ + Optional get(String key, Class expectedClass); +} \ No newline at end of file diff --git a/redis-backed-cache/src/main/java/com/mycompany/cache/RedisBackedCache.java b/redis-backed-cache/src/main/java/com/mycompany/cache/RedisBackedCache.java new file mode 100644 index 00000000000..2c80818c150 --- /dev/null +++ b/redis-backed-cache/src/main/java/com/mycompany/cache/RedisBackedCache.java @@ -0,0 +1,39 @@ +package com.mycompany.cache; + +import com.google.gson.Gson; +import redis.clients.jedis.Jedis; + +import java.util.Optional; + +/** + * An implementation of {@link Cache} that stores data in Redis. + */ +public class RedisBackedCache implements Cache { + + private final Jedis jedis; + private final String cacheName; + private final Gson gson; + + public RedisBackedCache(Jedis jedis, String cacheName) { + this.jedis = jedis; + this.cacheName = cacheName; + this.gson = new Gson(); + } + + @Override + public void put(String key, Object value) { + String jsonValue = gson.toJson(value); + this.jedis.hset(this.cacheName, key, jsonValue); + } + + @Override + public Optional get(String key, Class expectedClass) { + String foundJson = this.jedis.hget(this.cacheName, key); + + if (foundJson == null) { + return Optional.empty(); + } + + return Optional.of(gson.fromJson(foundJson, expectedClass)); + } +} diff --git a/redis-backed-cache/src/test/java/RedisBackedCacheTest.java b/redis-backed-cache/src/test/java/RedisBackedCacheTest.java new file mode 100644 index 00000000000..87bec6c258e --- /dev/null +++ b/redis-backed-cache/src/test/java/RedisBackedCacheTest.java @@ -0,0 +1,49 @@ +import com.mycompany.cache.Cache; +import com.mycompany.cache.RedisBackedCache; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; +import redis.clients.jedis.Jedis; + +import java.util.Optional; + +import static org.rnorth.visibleassertions.VisibleAssertions.*; + +/** + * Integration test for Redis-backed cache implementation. + */ +public class RedisBackedCacheTest { + + @Rule + public GenericContainer redis = new GenericContainer("redis:3.0.6") + .withExposedPorts(6379); + private Cache cache; + + @Before + public void setUp() throws Exception { + Jedis jedis = new Jedis(redis.getIpAddress(), redis.getMappedPort(6379)); + + cache = new RedisBackedCache(jedis, "test"); + } + + @Test + public void testFindingAnInsertedValue() { + cache.put("foo", "FOO"); + Optional foundObject = cache.get("foo", String.class); + + assertTrue("When an object in the cache is retrieved, it can be found", + foundObject.isPresent()); + assertEquals("When we put a String in to the cache and retrieve it, the value is the same", + "FOO", + foundObject.get()); + } + + @Test + public void testNotFindingAValueThatWasNotInserted() { + Optional foundObject = cache.get("bar", String.class); + + assertFalse("When an object that's not in the cache is retrieved, nothing is found", + foundObject.isPresent()); + } +} diff --git a/redis-backed-cache/src/test/resources/logback-test.xml b/redis-backed-cache/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..c52cffac190 --- /dev/null +++ b/redis-backed-cache/src/test/resources/logback-test.xml @@ -0,0 +1,19 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + + + + + + + + + + + + \ No newline at end of file From 2bf8ddb5bd40ca059206900be569b84cf83b3ada Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 30 Dec 2015 21:11:50 +0000 Subject: [PATCH 0002/5615] Add basic README --- README.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000000..c99077f3061 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# testcontainers-java-examples + +> Practical examples of using Testcontainers. + +See [blog post](https://rnorth.org/24/testcontainers-example-testing-a-redis-backed-cache) for details. From 4bcd1fa209ef62c3200072725620782a9fd3cd95 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 3 Jan 2016 22:13:59 +0000 Subject: [PATCH 0003/5615] Add Disque usage examples. --- disque-job-queue/pom.xml | 27 ++++++ .../java/DisqueBackedMailSenderService.java | 73 +++++++++++++++ .../src/main/java/MailApiClient.java | 11 +++ .../src/main/java/MailMessage.java | 12 +++ .../src/main/java/MailSenderService.java | 45 +++++++++ .../test/java/DisqueBackedMailSenderTest.java | 92 +++++++++++++++++++ .../test/java/SingleDisqueInstanceTest.java | 78 ++++++++++++++++ .../src/test/resources/logback-test.xml | 19 ++++ pom.xml | 8 +- 9 files changed, 364 insertions(+), 1 deletion(-) create mode 100644 disque-job-queue/pom.xml create mode 100644 disque-job-queue/src/main/java/DisqueBackedMailSenderService.java create mode 100644 disque-job-queue/src/main/java/MailApiClient.java create mode 100644 disque-job-queue/src/main/java/MailMessage.java create mode 100644 disque-job-queue/src/main/java/MailSenderService.java create mode 100644 disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java create mode 100644 disque-job-queue/src/test/java/SingleDisqueInstanceTest.java create mode 100644 disque-job-queue/src/test/resources/logback-test.xml diff --git a/disque-job-queue/pom.xml b/disque-job-queue/pom.xml new file mode 100644 index 00000000000..b22ff3283f8 --- /dev/null +++ b/disque-job-queue/pom.xml @@ -0,0 +1,27 @@ + + + + testcontainers-java-examples + org.testcontainers.examples + NOVERSION + + 4.0.0 + + disque-job-queue + + + biz.paluch.redis + spinach + 0.2 + + + com.google.code.gson + gson + 2.5 + + + + + \ No newline at end of file diff --git a/disque-job-queue/src/main/java/DisqueBackedMailSenderService.java b/disque-job-queue/src/main/java/DisqueBackedMailSenderService.java new file mode 100644 index 00000000000..d2096106500 --- /dev/null +++ b/disque-job-queue/src/main/java/DisqueBackedMailSenderService.java @@ -0,0 +1,73 @@ +import biz.paluch.spinach.DisqueClient; +import biz.paluch.spinach.api.Job; +import biz.paluch.spinach.api.sync.DisqueCommands; +import com.google.gson.Gson; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +/** + * Simple example of how a mail service can be backed by a Disque job queue. + * + * It's assumed that for actual sending (SMTP, HTTP API, etc), this service calls a {@link MailApiClient}. + * However, this can be expected to be quite unreliable, so use of Disque as a backing store helps keep track + * of sent/failed state. + */ +public class DisqueBackedMailSenderService implements MailSenderService { + + private final DisqueCommands disque; + private final Gson gson; + private final MailApiClient mailApiClient; + + public DisqueBackedMailSenderService(DisqueClient client, MailApiClient mailApiClient) { + this.mailApiClient = mailApiClient; + + // Obtain a Disque connection from DisqueClient + disque = client.connect().sync(); + + gson = new Gson(); + } + + @Override + public String enqueueMessage(MailMessage message) { + String messageAsJson = gson.toJson(message); + + // Enqueue the message as JSON, setting a TTL of 1 day (will expire off the queue if not sent by then) + String jobId = disque.addjob("mail", messageAsJson, 1, TimeUnit.DAYS); + + return jobId; + } + + @Override + public Result doScheduledSend() { + + Set succeededJobIds = new HashSet<>(); + Set failedJobIds = new HashSet<>(); + + // Wait up to 100ms for new messages to arrive on the queue + // Retrieve up to 10 messages + // (Ordinarily we'd make these configurable!) + List> jobs = disque.getjobs(100, TimeUnit.MILLISECONDS, 10, "mail"); + + for (Job job : jobs) { + String jsonBody = job.getBody(); + MailMessage message = gson.fromJson(jsonBody, MailMessage.class); + + try { + mailApiClient.send(message); + succeededJobIds.add(job.getId()); + } catch (MailApiClient.MailSendException e) { + failedJobIds.add(job.getId()); + } + } + + // For any failed messages, proactively return to the queue + disque.nack(failedJobIds.toArray(new String[failedJobIds.size()])); + // For any successful jobs, mark as completed + disque.ackjob(succeededJobIds.toArray(new String[succeededJobIds.size()])); + + return new Result(succeededJobIds.size(), failedJobIds.size()); + } +} diff --git a/disque-job-queue/src/main/java/MailApiClient.java b/disque-job-queue/src/main/java/MailApiClient.java new file mode 100644 index 00000000000..61a90112f32 --- /dev/null +++ b/disque-job-queue/src/main/java/MailApiClient.java @@ -0,0 +1,11 @@ +/** + * Created by rnorth on 03/01/2016. + */ +public interface MailApiClient { + + boolean send(MailMessage message) throws MailSendException; + + class MailSendException extends RuntimeException { + + } +} diff --git a/disque-job-queue/src/main/java/MailMessage.java b/disque-job-queue/src/main/java/MailMessage.java new file mode 100644 index 00000000000..de167199057 --- /dev/null +++ b/disque-job-queue/src/main/java/MailMessage.java @@ -0,0 +1,12 @@ +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * Created by rnorth on 03/01/2016. + */ +@Data @AllArgsConstructor +public class MailMessage { + public final String subject; + public final String recipient; + public final String body; +} diff --git a/disque-job-queue/src/main/java/MailSenderService.java b/disque-job-queue/src/main/java/MailSenderService.java new file mode 100644 index 00000000000..95237bf2eec --- /dev/null +++ b/disque-job-queue/src/main/java/MailSenderService.java @@ -0,0 +1,45 @@ +import lombok.Data; +import lombok.RequiredArgsConstructor; + +import java.util.concurrent.Callable; + +/** + * Created by rnorth on 03/01/2016. + */ +public interface MailSenderService extends Callable { + + /** + * Enqueue a message to be sent on a subsequent {@link DisqueBackedMailSenderService#doScheduledSend()} + * @param message mail message to be sent + * @return an ID for the enqueued message + */ + String enqueueMessage(MailMessage message); + + /** + * Trigger a scheduled send of mail messages. + * + * TODO: In a real implementation this could be called by a {@link java.util.concurrent.ScheduledExecutorService}, + * probably + * + * @return the result of sending queued messages. + */ + MailSenderService.Result doScheduledSend(); + + /** + * call() implementation to allow MailSenderService to be used as a Callable. Simply delegates to + * the {@link DisqueBackedMailSenderService#doScheduledSend()} method. + * + * @return the result of sending queued messages. + * @throws Exception + */ + default MailSenderService.Result call() throws Exception { + return doScheduledSend(); + } + + @Data + @RequiredArgsConstructor + class Result { + public final int successfulCount; + public final int failedCount; + } +} diff --git a/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java b/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java new file mode 100644 index 00000000000..49302241afa --- /dev/null +++ b/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java @@ -0,0 +1,92 @@ +import biz.paluch.spinach.DisqueClient; +import biz.paluch.spinach.DisqueURI; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; + +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.*; +import static org.rnorth.visibleassertions.VisibleAssertions.*; + +/** + * Created by rnorth on 03/01/2016. + */ +public class DisqueBackedMailSenderTest { + + @Rule + public GenericContainer container = new GenericContainer("richnorth/disque:1.0-rc1") + .withExposedPorts(7711); + private DisqueClient disqueClient; + + private MailSenderService service; + private MailApiClient mockMailApiClient; + + private MailMessage dummyMessage1; + private MailMessage dummyMessage2; + private MailMessage dummyMessage3; + + @Before + public void setup() { + context(""); + disqueClient = new DisqueClient(DisqueURI.create(container.getIpAddress(), container.getMappedPort(7711))); + mockMailApiClient = mock(MailApiClient.class); + + service = new DisqueBackedMailSenderService(disqueClient, mockMailApiClient); + + info("Initialized a fresh Disque instance and service instance"); + + dummyMessage1 = new MailMessage("Dummy Message 1", "bob@example.com", "Test body"); + dummyMessage2 = new MailMessage("Dummy Message 2", "bob@example.com", "Test body"); + dummyMessage3 = new MailMessage("Dummy Message 3", "bob@example.com", "Test body"); + } + + @Test + public void testSimpleSuccessfulSending() throws Exception { + service.enqueueMessage(dummyMessage1); + service.enqueueMessage(dummyMessage2); + service.enqueueMessage(dummyMessage3); + + when(mockMailApiClient.send(any(MailMessage.class))).thenReturn(true); + + service.doScheduledSend(); + + verify(mockMailApiClient).send(eq(dummyMessage1)); + verify(mockMailApiClient).send(eq(dummyMessage2)); + verify(mockMailApiClient).send(eq(dummyMessage3)); + } + + @Test + public void testRetryOnFailure() throws Exception { + service.enqueueMessage(dummyMessage1); + service.enqueueMessage(dummyMessage2); + service.enqueueMessage(dummyMessage3); + + info("Message 1 will fail to send on the first attempt"); + when(mockMailApiClient.send(eq(dummyMessage1))).thenThrow(MailApiClient.MailSendException.class).thenReturn(true); + when(mockMailApiClient.send(eq(dummyMessage2))).thenReturn(true); + when(mockMailApiClient.send(eq(dummyMessage3))).thenReturn(true); + + MailSenderService.Result result; + context("Simulating sending messages"); + context("First sending attempt", 4); + result = service.doScheduledSend(); + assertEquals("2 messages were 'sent' successfully", 2, result.successfulCount); + assertEquals("1 messages failed", 1, result.failedCount); + + context("Second attempt", 4); + result = service.doScheduledSend(); + assertEquals("1 message was 'sent' successfully", 1, result.successfulCount); + assertEquals("0 messages failed", 0, result.failedCount); + + context("Third attempt", 4); + info("No messages should be due to send this time"); + result = service.doScheduledSend(); + assertEquals("0 messages were 'sent' successfully", 0, result.successfulCount); + assertEquals("0 messages failed", 0, result.failedCount); + + verify(mockMailApiClient, times(2)).send(eq(dummyMessage1)); + verify(mockMailApiClient).send(eq(dummyMessage2)); + verify(mockMailApiClient).send(eq(dummyMessage3)); + } +} diff --git a/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java b/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java new file mode 100644 index 00000000000..192e5a49fe1 --- /dev/null +++ b/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java @@ -0,0 +1,78 @@ +import biz.paluch.spinach.DisqueClient; +import biz.paluch.spinach.DisqueURI; +import biz.paluch.spinach.api.AddJobArgs; +import biz.paluch.spinach.api.Job; +import biz.paluch.spinach.api.sync.DisqueCommands; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; + +import java.util.concurrent.TimeUnit; + +import static org.rnorth.visibleassertions.VisibleAssertions.*; + +/** + * Created by rnorth on 03/01/2016. + */ +public class SingleDisqueInstanceTest { + + @Rule + public GenericContainer container = new GenericContainer("richnorth/disque:1.0-rc1") + .withExposedPorts(7711); + private DisqueCommands connection; + private AddJobArgs retryAfter1Second; + + @Before + public void setup() { + context(""); + DisqueClient client = new DisqueClient(DisqueURI.create(container.getIpAddress(), container.getMappedPort(7711))); + connection = client.connect().sync(); + retryAfter1Second = AddJobArgs.builder().retry(1, TimeUnit.SECONDS).build(); + + info("Initialized a fresh Disque instance and client"); + } + + @Test + public void testJobStoreAndRetrieve() { + + info("Adding a job to the queue"); + connection.addjob("main_queue", "body", 1, TimeUnit.MINUTES); + + info("Getting a job from the queue"); + Job job = connection.getjob("main_queue"); + + assertEquals("The retrieved job is the same as the one that was added", + "body", + job.getBody()); + + info("Acknowledging the job to mark completion"); + connection.ackjob(job.getId()); + } + + @Test + public void testFailureToAckJobBeforeTimeout() throws InterruptedException { + + info("Adding a job to the queue"); + connection.addjob("main_queue", "body", 1, TimeUnit.MINUTES, retryAfter1Second); + + info("Getting a job from the queue"); + Job job = connection.getjob("main_queue"); + + assertEquals("The retrieved job is the same as the one that was added", + "body", + job.getBody()); + + info("Simulating a failure to ack the job before the timeout (1s)"); + TimeUnit.SECONDS.sleep(2); + + info("Attempting to get another job from the queue - the RETRY setting for the job means it should have reappeared"); + // The timeout specified here is how long the command will wait for a job to appear + Job job2 = connection.getjob(5, TimeUnit.SECONDS, "main_queue"); + + assertNotNull("After re-getting the original job is back on the queue", job2); + assertEquals("The retrieved job is the same as the one that was added", + "body", + job2.getBody()); + } +} diff --git a/disque-job-queue/src/test/resources/logback-test.xml b/disque-job-queue/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..c52cffac190 --- /dev/null +++ b/disque-job-queue/src/test/resources/logback-test.xml @@ -0,0 +1,19 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index ffa56cdb4da..b86d65651ae 100644 --- a/pom.xml +++ b/pom.xml @@ -7,6 +7,7 @@ NOVERSION redis-backed-cache + disque-job-queue pom @@ -52,7 +53,12 @@ 1.0.2 test - + + org.mockito + mockito-all + 1.9.5 + test + From 2c04d3312c0c33b0ee62f972a53e5c8c45948c6c Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 3 Jan 2016 23:20:41 +0000 Subject: [PATCH 0004/5615] Update README.md --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c99077f3061..d85f0d03f9b 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,7 @@ > Practical examples of using Testcontainers. -See [blog post](https://rnorth.org/24/testcontainers-example-testing-a-redis-backed-cache) for details. +The code in this repository accompanies the following blog posts: + +* [JUnit integration testing with Docker and Testcontainers](https://rnorth.org/24/testcontainers-example-testing-a-redis-backed-cache) +* [Fun with Disque, Java and Spinach](https://rnorth.org/25/fun-with-disque-java-and-spinach) From b2d662180860593b4058e23f2eda08878c90d8ae Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 8 Jan 2016 14:21:15 +0000 Subject: [PATCH 0005/5615] Add CircleCI config for examples Update logback-test.xml to match testcontainers docs --- circle.yml | 12 ++++++++++++ disque-job-queue/src/test/resources/logback-test.xml | 1 - .../src/test/resources/logback-test.xml | 1 - 3 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 circle.yml diff --git a/circle.yml b/circle.yml new file mode 100644 index 00000000000..6f904b0eed6 --- /dev/null +++ b/circle.yml @@ -0,0 +1,12 @@ +machine: + java: + version: oraclejdk8 + environment: + DOCKER_HOST: tcp://127.0.0.1:2376 + DOCKER_TLS_VERIFY: 0 + MAVEN_OPTS: -Xmx512m +dependencies: + override: + - "sudo docker -d -e lxc -s btrfs -H 0.0.0.0:2376 -H unix:///var/run/docker.sock --log-level=debug --debug=true": + background: true + - mvn install -DskipTests \ No newline at end of file diff --git a/disque-job-queue/src/test/resources/logback-test.xml b/disque-job-queue/src/test/resources/logback-test.xml index c52cffac190..c53f381fca5 100644 --- a/disque-job-queue/src/test/resources/logback-test.xml +++ b/disque-job-queue/src/test/resources/logback-test.xml @@ -15,5 +15,4 @@ - \ No newline at end of file diff --git a/redis-backed-cache/src/test/resources/logback-test.xml b/redis-backed-cache/src/test/resources/logback-test.xml index c52cffac190..c53f381fca5 100644 --- a/redis-backed-cache/src/test/resources/logback-test.xml +++ b/redis-backed-cache/src/test/resources/logback-test.xml @@ -15,5 +15,4 @@ - \ No newline at end of file From cb27928452baf1a6d859af2ab2aeaa18faeabbf1 Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 11 Jan 2016 11:11:44 +0000 Subject: [PATCH 0006/5615] Change SingletonDockerClient to a singleton provider that provides access to a *ThreadLocal* instance of the docker client --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b86d65651ae..18ea48c6b7a 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ org.testcontainers testcontainers - 0.9.8 + 0.9.9-SNAPSHOT From 7675f237a0a4e3ee8a3d389b91a5640b4e811d06 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 12 Jan 2016 22:31:37 +0000 Subject: [PATCH 0007/5615] Add Selenium container example, and move to v 0.9.9 of testcontainers --- pom.xml | 4 +- selenium-container/pom.xml | 28 +++++++++++++ .../src/test/java/SeleniumContainerTest.java | 42 +++++++++++++++++++ .../src/test/resources/logback-test.xml | 18 ++++++++ 4 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 selenium-container/pom.xml create mode 100644 selenium-container/src/test/java/SeleniumContainerTest.java create mode 100644 selenium-container/src/test/resources/logback-test.xml diff --git a/pom.xml b/pom.xml index 18ea48c6b7a..6cc84b440ed 100644 --- a/pom.xml +++ b/pom.xml @@ -8,6 +8,7 @@ redis-backed-cache disque-job-queue + selenium-container pom @@ -16,7 +17,7 @@ org.testcontainers testcontainers - 0.9.9-SNAPSHOT + ${testcontainers.version} @@ -76,5 +77,6 @@ UTF-8 + 0.9.9 diff --git a/selenium-container/pom.xml b/selenium-container/pom.xml new file mode 100644 index 00000000000..9f8b1d3027a --- /dev/null +++ b/selenium-container/pom.xml @@ -0,0 +1,28 @@ + + + + testcontainers-java-examples + org.testcontainers.examples + NOVERSION + + 4.0.0 + + selenium-container + + + + org.seleniumhq.selenium + selenium-remote-driver + 2.45.0 + + + org.testcontainers + selenium + ${testcontainers.version} + + + + + \ No newline at end of file diff --git a/selenium-container/src/test/java/SeleniumContainerTest.java b/selenium-container/src/test/java/SeleniumContainerTest.java new file mode 100644 index 00000000000..9630a87fe14 --- /dev/null +++ b/selenium-container/src/test/java/SeleniumContainerTest.java @@ -0,0 +1,42 @@ +import org.junit.Rule; +import org.junit.Test; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.remote.DesiredCapabilities; +import org.openqa.selenium.remote.RemoteWebDriver; +import org.testcontainers.containers.BrowserWebDriverContainer; + +import java.io.File; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode.RECORD_ALL; + +/** + * Simple example of plain Selenium usage. + */ +public class SeleniumContainerTest { + + @Rule + public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() + .withDesiredCapabilities(DesiredCapabilities.chrome()) + .withRecordingMode(RECORD_ALL, new File("target")); + + @Test + public void simplePlainSeleniumTest() { + RemoteWebDriver driver = chrome.getWebDriver(); + + driver.get("https://wikipedia.org"); + WebElement searchInput = driver.findElementByName("search"); + + searchInput.sendKeys("Rick Astley"); + searchInput.submit(); + + WebElement otherPage = driver.findElementByLinkText("Rickrolling"); + otherPage.click(); + + boolean expectedTextFound = driver.findElementsByCssSelector("p") + .stream() + .anyMatch(element -> element.getText().contains("meme")); + + assertTrue("The word 'meme' is found on a page about rickrolling", expectedTextFound); + } +} diff --git a/selenium-container/src/test/resources/logback-test.xml b/selenium-container/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..c53f381fca5 --- /dev/null +++ b/selenium-container/src/test/resources/logback-test.xml @@ -0,0 +1,18 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + + + + + + + + + + + \ No newline at end of file From 8fd667084dd6ae20c3aac63aed141d6d3e89d42f Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 12 Jan 2016 22:39:18 +0000 Subject: [PATCH 0008/5615] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d85f0d03f9b..3a50e17d32a 100644 --- a/README.md +++ b/README.md @@ -6,3 +6,4 @@ The code in this repository accompanies the following blog posts: * [JUnit integration testing with Docker and Testcontainers](https://rnorth.org/24/testcontainers-example-testing-a-redis-backed-cache) * [Fun with Disque, Java and Spinach](https://rnorth.org/25/fun-with-disque-java-and-spinach) +* [Better JUnit Selenium testing with Docker and Testcontainers](https://rnorth.org/26/better-junit-selenium-testing-with-docker-and-testcontainers) From 65d557de559703180a32ea23470cef7be750ed68 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 10 Apr 2016 11:39:57 +0100 Subject: [PATCH 0009/5615] Switch examples project to use jitpack exclusively. --- README.md | 4 ++++ disque-job-queue/pom.xml | 5 +++++ pom.xml | 12 ++++++++++-- redis-backed-cache/pom.xml | 5 +++++ selenium-container/pom.xml | 2 +- 5 files changed, 25 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3a50e17d32a..2da77753005 100644 --- a/README.md +++ b/README.md @@ -7,3 +7,7 @@ The code in this repository accompanies the following blog posts: * [JUnit integration testing with Docker and Testcontainers](https://rnorth.org/24/testcontainers-example-testing-a-redis-backed-cache) * [Fun with Disque, Java and Spinach](https://rnorth.org/25/fun-with-disque-java-and-spinach) * [Better JUnit Selenium testing with Docker and Testcontainers](https://rnorth.org/26/better-junit-selenium-testing-with-docker-and-testcontainers) + +Note that this examples project uses JitPack to obtain SNAPSHOT versions of the latest build of TestContainers. For +normal usage, refer to the [documentation](http://testcontainers.viewdocs.io/testcontainers-java/#user-content-maven-dependencies) +for setting the correct maven dependencies for your project. \ No newline at end of file diff --git a/disque-job-queue/pom.xml b/disque-job-queue/pom.xml index b22ff3283f8..7799c8b18ca 100644 --- a/disque-job-queue/pom.xml +++ b/disque-job-queue/pom.xml @@ -21,6 +21,11 @@ gson 2.5 + + com.google.guava + guava + 18.0 + diff --git a/pom.xml b/pom.xml index 6cc84b440ed..65421865605 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ - org.testcontainers + ${testcontainers.group} testcontainers ${testcontainers.version} @@ -77,6 +77,14 @@ UTF-8 - 0.9.9 + com.github.testcontainers.testcontainers-java + -SNAPSHOT + + + + jitpack.io + https://jitpack.io + + diff --git a/redis-backed-cache/pom.xml b/redis-backed-cache/pom.xml index 082530329c5..e95ef6c4475 100644 --- a/redis-backed-cache/pom.xml +++ b/redis-backed-cache/pom.xml @@ -21,6 +21,11 @@ gson 2.5 + + com.google.guava + guava + 18.0 + diff --git a/selenium-container/pom.xml b/selenium-container/pom.xml index 9f8b1d3027a..a9196da252f 100644 --- a/selenium-container/pom.xml +++ b/selenium-container/pom.xml @@ -18,7 +18,7 @@ 2.45.0 - org.testcontainers + ${testcontainers.group} selenium ${testcontainers.version} From 82d6e86f9fb9be33b0d49c57ef24ccb0b6109005 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 7 Jun 2016 08:18:18 +0300 Subject: [PATCH 0010/5615] Add Spring Boot example of usage --- disque-job-queue/pom.xml | 12 ++++ pom.xml | 15 +---- redis-backed-cache/pom.xml | 12 ++++ selenium-container/pom.xml | 12 ++++ spring-boot/pom.xml | 59 +++++++++++++++++++ .../java/com/example/DemoApplication.java | 32 ++++++++++ .../com/example/AbstractIntegrationTest.java | 53 +++++++++++++++++ .../java/com/example/DemoApplicationTest.java | 23 ++++++++ .../src/test/resources/logback-test.xml | 8 +++ 9 files changed, 213 insertions(+), 13 deletions(-) create mode 100644 spring-boot/pom.xml create mode 100644 spring-boot/src/main/java/com/example/DemoApplication.java create mode 100644 spring-boot/src/test/java/com/example/AbstractIntegrationTest.java create mode 100644 spring-boot/src/test/java/com/example/DemoApplicationTest.java create mode 100644 spring-boot/src/test/resources/logback-test.xml diff --git a/disque-job-queue/pom.xml b/disque-job-queue/pom.xml index 7799c8b18ca..256584ba69c 100644 --- a/disque-job-queue/pom.xml +++ b/disque-job-queue/pom.xml @@ -26,6 +26,18 @@ guava 18.0 + + org.slf4j + slf4j-api + 1.7.7 + provided + + + ch.qos.logback + logback-classic + 1.1.2 + test + diff --git a/pom.xml b/pom.xml index 65421865605..259fb6253c1 100644 --- a/pom.xml +++ b/pom.xml @@ -9,6 +9,7 @@ redis-backed-cache disque-job-queue selenium-container + spring-boot pom @@ -36,22 +37,10 @@ - - org.slf4j - slf4j-api - 1.7.7 - provided - - - ch.qos.logback - logback-classic - 1.1.2 - test - org.rnorth.visible-assertions visible-assertions - 1.0.2 + 1.0.5 test diff --git a/redis-backed-cache/pom.xml b/redis-backed-cache/pom.xml index e95ef6c4475..bcb7646f1cf 100644 --- a/redis-backed-cache/pom.xml +++ b/redis-backed-cache/pom.xml @@ -26,6 +26,18 @@ guava 18.0 + + org.slf4j + slf4j-api + 1.7.7 + provided + + + ch.qos.logback + logback-classic + 1.1.2 + test + diff --git a/selenium-container/pom.xml b/selenium-container/pom.xml index a9196da252f..1187b66f680 100644 --- a/selenium-container/pom.xml +++ b/selenium-container/pom.xml @@ -22,6 +22,18 @@ selenium ${testcontainers.version} + + org.slf4j + slf4j-api + 1.7.7 + provided + + + ch.qos.logback + logback-classic + 1.1.2 + test + diff --git a/spring-boot/pom.xml b/spring-boot/pom.xml new file mode 100644 index 00000000000..e0952987c77 --- /dev/null +++ b/spring-boot/pom.xml @@ -0,0 +1,59 @@ + + + + + testcontainers-java-examples + org.testcontainers.examples + NOVERSION + + 4.0.0 + + spring-boot + + + UTF-8 + 1.8 + + + + + + org.springframework.boot + spring-boot-dependencies + 1.3.5.RELEASE + pom + import + + + + + + + org.springframework.boot + spring-boot-starter-redis + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + 1.3.5.RELEASE + + + + + + diff --git a/spring-boot/src/main/java/com/example/DemoApplication.java b/spring-boot/src/main/java/com/example/DemoApplication.java new file mode 100644 index 00000000000..3373643d4d7 --- /dev/null +++ b/spring-boot/src/main/java/com/example/DemoApplication.java @@ -0,0 +1,32 @@ +package com.example; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +@SpringBootApplication +@RestController +public class DemoApplication { + + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } + + @Autowired + StringRedisTemplate stringRedisTemplate; + + @RequestMapping(value = "/foo", method = RequestMethod.GET) + public String get() { + return stringRedisTemplate.opsForValue().get("foo"); + } + + @RequestMapping(value = "/foo", method = RequestMethod.PUT) + public void set(@RequestBody String value) { + stringRedisTemplate.opsForValue().set("foo", value); + } +} diff --git a/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java b/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java new file mode 100644 index 00000000000..3e9bc238092 --- /dev/null +++ b/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java @@ -0,0 +1,53 @@ +package com.example; + +import org.junit.ClassRule; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.boot.test.WebIntegrationTest; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.PropertySource; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.testcontainers.containers.GenericContainer; + +import javax.annotation.PostConstruct; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(DemoApplicationTest.TestConfiguration.class) +@WebIntegrationTest(randomPort = true) +public abstract class AbstractIntegrationTest { + + @ClassRule + public static GenericContainer redis = new GenericContainer("redis:3.0.6").withExposedPorts(6379); + + @Configuration + @Import(DemoApplication.class) + static class TestConfiguration { + + @Autowired + ConfigurableEnvironment environment; + + @PostConstruct + public void init() { + environment.getPropertySources().addFirst(new PropertySource("containers") { + @Override + public Object getProperty(String name) { + switch (name) { + case "spring.redis.host": + return redis.getContainerIpAddress(); + case "spring.redis.port": + return redis.getMappedPort(6379); + default: + return null; + } + } + }); + } + } + + @Value("${local.server.port}") + protected int port; +} diff --git a/spring-boot/src/test/java/com/example/DemoApplicationTest.java b/spring-boot/src/test/java/com/example/DemoApplicationTest.java new file mode 100644 index 00000000000..baa050195cd --- /dev/null +++ b/spring-boot/src/test/java/com/example/DemoApplicationTest.java @@ -0,0 +1,23 @@ +package com.example; + +import org.junit.Test; +import org.springframework.web.client.RestTemplate; + + +import static org.rnorth.visibleassertions.VisibleAssertions.*; + +public class DemoApplicationTest extends AbstractIntegrationTest { + + RestTemplate restTemplate = new RestTemplate(); + + @Test + public void simpleTest() { + String fooResource = "http://localhost:" + port + "/foo"; + + info("putting 'bar' to " + fooResource); + restTemplate.put(fooResource, "bar"); + + assertEquals("value is set", "bar", restTemplate.getForObject(fooResource, String.class)); + } + +} diff --git a/spring-boot/src/test/resources/logback-test.xml b/spring-boot/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..86c4c861a78 --- /dev/null +++ b/spring-boot/src/test/resources/logback-test.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file From a6e4536541aa43344f2b3a33405dc450e35ea185 Mon Sep 17 00:00:00 2001 From: Sean Flanigan Date: Tue, 7 Jun 2016 21:45:50 +1000 Subject: [PATCH 0011/5615] Fix compilation error visible-assertions now uses fixed dependencies instead of ranges: https://github.com/rnorth/visible-assertions/commit/040134e66709346efbf7183909733b654717bae1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 65421865605..f66ebfe4a5f 100644 --- a/pom.xml +++ b/pom.xml @@ -51,7 +51,7 @@ org.rnorth.visible-assertions visible-assertions - 1.0.2 + 1.0.5 test From cbf8c7224307fe058c022ea74c5c9e9066bfb2bf Mon Sep 17 00:00:00 2001 From: Sean Flanigan Date: Tue, 7 Jun 2016 21:48:01 +1000 Subject: [PATCH 0012/5615] Add Windows profile for window-support-SNAPSHOT --- pom.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pom.xml b/pom.xml index f66ebfe4a5f..edab928edb4 100644 --- a/pom.xml +++ b/pom.xml @@ -81,6 +81,19 @@ -SNAPSHOT + + + + + Windows + + + + windows-support-SNAPSHOT + + + + jitpack.io From a87c3c12e7420b74edfdfac099ba997f36d593f7 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 8 Aug 2016 13:21:24 +0300 Subject: [PATCH 0013/5615] code review fixes --- spring-boot/pom.xml | 100 +++++++++--------- .../com/example/AbstractIntegrationTest.java | 21 ++-- 2 files changed, 56 insertions(+), 65 deletions(-) diff --git a/spring-boot/pom.xml b/spring-boot/pom.xml index e0952987c77..0594b26396c 100644 --- a/spring-boot/pom.xml +++ b/spring-boot/pom.xml @@ -1,59 +1,57 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - - testcontainers-java-examples - org.testcontainers.examples - NOVERSION - - 4.0.0 + + testcontainers-java-examples + org.testcontainers.examples + NOVERSION + + 4.0.0 - spring-boot + spring-boot - - UTF-8 - 1.8 - + + UTF-8 + 1.8 + - - - - org.springframework.boot - spring-boot-dependencies - 1.3.5.RELEASE - pom - import - - - - - - - org.springframework.boot - spring-boot-starter-redis - - - org.springframework.boot - spring-boot-starter-web - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - - org.springframework.boot - spring-boot-maven-plugin - 1.3.5.RELEASE - - - - + + + + org.springframework.boot + spring-boot-dependencies + 1.3.5.RELEASE + pom + import + + + + + + org.springframework.boot + spring-boot-starter-redis + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + 1.3.5.RELEASE + + + diff --git a/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java b/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java index 3e9bc238092..d6a03511e21 100644 --- a/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java +++ b/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java @@ -9,7 +9,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.env.PropertySource; +import org.springframework.mock.env.MockPropertySource; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.testcontainers.containers.GenericContainer; @@ -32,19 +32,12 @@ static class TestConfiguration { @PostConstruct public void init() { - environment.getPropertySources().addFirst(new PropertySource("containers") { - @Override - public Object getProperty(String name) { - switch (name) { - case "spring.redis.host": - return redis.getContainerIpAddress(); - case "spring.redis.port": - return redis.getMappedPort(6379); - default: - return null; - } - } - }); + MockPropertySource propertySource = new MockPropertySource("TestContainers"); + + propertySource.setProperty("spring.redis.host", redis.getContainerIpAddress()); + propertySource.setProperty("spring.redis.port", redis.getMappedPort(6379)); + + environment.getPropertySources().addFirst(propertySource); } } From 269c58ca5108e0d779af0968c59899d9167b04d7 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 9 Sep 2016 14:31:56 +0300 Subject: [PATCH 0014/5615] Simplify Spring Boot example --- .../com/example/AbstractIntegrationTest.java | 34 ++++++------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java b/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java index d6a03511e21..3d2f54fc482 100644 --- a/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java +++ b/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java @@ -2,42 +2,30 @@ import org.junit.ClassRule; import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.EnvironmentTestUtils; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.boot.test.WebIntegrationTest; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.mock.env.MockPropertySource; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.testcontainers.containers.GenericContainer; -import javax.annotation.PostConstruct; - @RunWith(SpringJUnit4ClassRunner.class) -@SpringApplicationConfiguration(DemoApplicationTest.TestConfiguration.class) +@SpringApplicationConfiguration(value = DemoApplication.class, initializers = AbstractIntegrationTest.Initializer.class) @WebIntegrationTest(randomPort = true) public abstract class AbstractIntegrationTest { @ClassRule public static GenericContainer redis = new GenericContainer("redis:3.0.6").withExposedPorts(6379); - @Configuration - @Import(DemoApplication.class) - static class TestConfiguration { - - @Autowired - ConfigurableEnvironment environment; - - @PostConstruct - public void init() { - MockPropertySource propertySource = new MockPropertySource("TestContainers"); - - propertySource.setProperty("spring.redis.host", redis.getContainerIpAddress()); - propertySource.setProperty("spring.redis.port", redis.getMappedPort(6379)); - - environment.getPropertySources().addFirst(propertySource); + public static class Initializer implements ApplicationContextInitializer { + @Override + public void initialize(ConfigurableApplicationContext configurableApplicationContext) { + EnvironmentTestUtils.addEnvironment("testcontainers", configurableApplicationContext.getEnvironment(), + "spring.redis.host=" + redis.getContainerIpAddress(), + "spring.redis.port=" + redis.getMappedPort(6379) + ); } } From 16fb9ad696e4410d552d1fe34be6f8b6d4101e8a Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 4 Dec 2016 19:50:36 +0000 Subject: [PATCH 0015/5615] Allow docker image used for selenium to be specified, instead of always trying to figure out from classpath dependencies. Refs #171 --- .../containers/BrowserWebDriverContainer.java | 20 +++++++++++-- ...ecificImageNameWebDriverContainerTest.java | 28 +++++++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index 4c7d6019c3b..17ad2dfbe71 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -36,15 +36,16 @@ public class BrowserWebDriverContainer currentVncRecordings = new ArrayList<>(); private static final Logger LOGGER = LoggerFactory.getLogger(BrowserWebDriverContainer.class); - private static final SimpleDateFormat filenameDateFormat = new SimpleDateFormat("YYYYMMdd-HHmmss"); /** @@ -53,9 +54,22 @@ public BrowserWebDriverContainer() { } + /** + * Constructor taking a specific webdriver container name and tag + * @param dockerImageName + */ + public BrowserWebDriverContainer(String dockerImageName) { + super.setDockerImageName(dockerImageName); + this.imageNameIsSet = true; + } + public SELF withDesiredCapabilities(DesiredCapabilities desiredCapabilities) { - super.setDockerImageName(getImageForCapabilities(desiredCapabilities)); + + if (! imageNameIsSet) { + super.setDockerImageName(getImageForCapabilities(desiredCapabilities)); + } + this.desiredCapabilities = desiredCapabilities; return self(); } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java new file mode 100644 index 00000000000..1db034502ac --- /dev/null +++ b/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java @@ -0,0 +1,28 @@ +package org.testcontainers.junit; + +import org.junit.Rule; +import org.junit.Test; +import org.openqa.selenium.remote.DesiredCapabilities; +import org.testcontainers.containers.BrowserWebDriverContainer; + +import java.io.IOException; + +/** + * + */ +public class SpecificImageNameWebDriverContainerTest extends BaseWebDriverContainerTest { + + @Rule + public BrowserWebDriverContainer firefox = new BrowserWebDriverContainer("selenium/standalone-firefox-debug:2.53.1-beryllium") + .withDesiredCapabilities(DesiredCapabilities.firefox()); + + @Test + public void simpleTest() throws IOException { + doSimpleWebdriverTest(firefox); + } + + @Test + public void simpleExploreTest() throws IOException { + doSimpleExplore(firefox); + } +} From 8efdaa87684821bf6a57ff859eeffad05d485217 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 17 Dec 2016 21:25:17 +0000 Subject: [PATCH 0016/5615] Move logic relating to setting of custom/automatic webdriver image name, and clarify name of field --- .../containers/BrowserWebDriverContainer.java | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index 17ad2dfbe71..1798ec41116 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -16,10 +16,14 @@ import java.net.MalformedURLException; import java.net.URL; import java.text.SimpleDateFormat; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import static com.google.common.base.Preconditions.checkState; + /** * A chrome/firefox/custom container based on SeleniumHQ's standalone container sets. *

@@ -36,7 +40,7 @@ public class BrowserWebDriverContainer Date: Sun, 8 Jan 2017 12:03:00 +0200 Subject: [PATCH 0017/5615] add Slack info to the docs --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index a58d344033e..e064eeb487f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -28,6 +28,7 @@ This project was initially inspired by a [gist](https://gist.github.com/mosheesh ## Contributing * Star the project on [Github](https://github.com/testcontainers/testcontainers-java) and help spread the word :) +* Join our Slack: http://slack.testcontainers.org * See [ROADMAP](ROADMAP.md) to understand the approach behind the project and what may/may not be in store for the future. * [Post an issue](https://github.com/testcontainers/testcontainers-java/issues) if you find any bugs * Contribute improvements or fixes using a [Pull Request](https://github.com/testcontainers/testcontainers-java/pulls). If you're going to contribute, thank you! Please just be sure to: From 035a43c37f5e015f682e6c110a6170709810bf63 Mon Sep 17 00:00:00 2001 From: vkrot Date: Wed, 11 Jan 2017 11:25:25 +0100 Subject: [PATCH 0018/5615] Load jdbc driver with current classloader instead of system classloaded. When running tests in SBT system classloader does not contain jdbc driver - SBT uses separate URLClassloader --- .../org/testcontainers/containers/JdbcDatabaseContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index 1d972b72946..7d594cb1823 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -93,7 +93,7 @@ public Driver getJdbcDriverInstance() { synchronized (DRIVER_LOAD_MUTEX) { if (driver == null) { try { - driver = (Driver) ClassLoader.getSystemClassLoader().loadClass(this.getDriverClassName()).newInstance(); + driver = (Driver) Class.forName(this.getDriverClassName()).newInstance(); } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { throw new RuntimeException("Could not get Driver", e); } From da7975f208cee181f95c26d0e2cc81d6dcd7cc7a Mon Sep 17 00:00:00 2001 From: novy Date: Sun, 15 Jan 2017 13:40:47 +0100 Subject: [PATCH 0019/5615] StartupStatus is now public --- .../containers/startupcheck/StartupCheckStrategy.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/startupcheck/StartupCheckStrategy.java b/core/src/main/java/org/testcontainers/containers/startupcheck/StartupCheckStrategy.java index f48f5958bd3..f592411b4fb 100644 --- a/core/src/main/java/org/testcontainers/containers/startupcheck/StartupCheckStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/startupcheck/StartupCheckStrategy.java @@ -45,7 +45,7 @@ protected InspectContainerResponse.ContainerState getCurrentState(DockerClient d return dockerClient.inspectContainerCmd(containerId).exec().getState(); } - enum StartupStatus { + public enum StartupStatus { NOT_YET_KNOWN, SUCCESSFUL, FAILED } } From 26b3eb2ecc4b77ab776d1785873474113b637a2b Mon Sep 17 00:00:00 2001 From: Aboubacar Toure Date: Wed, 18 Jan 2017 17:46:04 -0500 Subject: [PATCH 0020/5615] Issue 247: Add support for $HOME/.docker/config.json As described in issue 247 ( https://github.com/testcontainers/testcontainers-java/issues/247), docker-java, up to version 3.0.6, hardcoded the private registry authentication file to use ($HOME/.docker/.dockercfg). I updated docker-java to support both config.json and .dockercfg (https://github.com/docker-java/docker-java/pull/756), which has been released in version 3.0.7. This commit upgrades docker-java's version to 3.0.7 --- core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/pom.xml b/core/pom.xml index d563837c7f2..60295c30f5f 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -18,7 +18,7 @@ com.github.docker-java docker-java - 3.0.5 + 3.0.7 From 7158c7b8e59f8e3725f54e25917b45756d85adf3 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 20 Jan 2017 23:30:51 +0200 Subject: [PATCH 0021/5615] fix for Docker for Mac with 1.13 --- .../main/java/org/testcontainers/utility/TestEnvironment.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/utility/TestEnvironment.java b/core/src/main/java/org/testcontainers/utility/TestEnvironment.java index 3e858c35f3b..dbd8e45a98f 100644 --- a/core/src/main/java/org/testcontainers/utility/TestEnvironment.java +++ b/core/src/main/java/org/testcontainers/utility/TestEnvironment.java @@ -21,7 +21,8 @@ public static boolean dockerApiAtLeast(String minimumVersion) { public static boolean dockerExecutionDriverSupportsExec() { String executionDriver = DockerClientFactory.instance().getActiveExecutionDriver(); - return !executionDriver.startsWith("lxc"); + // Could be null starting from Docker 1.13 + return executionDriver == null || !executionDriver.startsWith("lxc"); } public static boolean dockerIsDockerMachine() { From dc1b260085680be1cb09dc2b04b9e30f14531f4c Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 14 Jan 2017 20:46:23 +0000 Subject: [PATCH 0022/5615] Ensure that piped streams are closed automatically. This resolves an apparently deadlock somewhere between netty/docker if a previous test, affected by #263, has failed during copying of a file. --- .../testcontainers/images/builder/ImageFromDockerfile.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java index 78cb598d27f..e51e6d003af 100644 --- a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java +++ b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java @@ -6,6 +6,7 @@ import com.github.dockerjava.api.model.BuildResponseItem; import com.github.dockerjava.core.command.BuildImageResultCallback; import com.google.common.collect.Sets; +import lombok.Cleanup; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; @@ -113,8 +114,8 @@ public void onNext(BuildResponseItem item) { }; // We have to use pipes to avoid high memory consumption since users might want to build really big images - PipedInputStream in = new PipedInputStream(); - PipedOutputStream out = new PipedOutputStream(in); + @Cleanup PipedInputStream in = new PipedInputStream(); + @Cleanup PipedOutputStream out = new PipedOutputStream(in); profiler.start("Configure image"); BuildImageCmd buildImageCmd = dockerClient.buildImageCmd(in); From 398a6164efeb4d5605a703bb92c09840251b8034 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 15 Jan 2017 16:07:05 +0200 Subject: [PATCH 0023/5615] Make it possible to run TestContainers inside a container. Fixes #264 --- .mvn/wrapper/maven-wrapper.jar | Bin 0 -> 49519 bytes .mvn/wrapper/maven-wrapper.properties | 1 + .travis.yml | 14 +- circle.yml | 10 + .../testcontainers/DockerClientFactory.java | 98 ++++---- .../dockerclient/DockerClientConfigUtils.java | 55 +++- ...dSystemPropertyClientProviderStrategy.java | 1 - .../LogToStringContainerCallback.java | 24 ++ .../dockerclient/DockerInDockerTest.java | 37 +++ .../junit/GenericContainerRuleTest.java | 2 +- mvnw | 236 ++++++++++++++++++ mvnw.cmd | 146 +++++++++++ 12 files changed, 559 insertions(+), 65 deletions(-) create mode 100644 .mvn/wrapper/maven-wrapper.jar create mode 100644 .mvn/wrapper/maven-wrapper.properties create mode 100644 core/src/main/java/org/testcontainers/dockerclient/LogToStringContainerCallback.java create mode 100644 core/src/test/java/org/testcontainers/dockerclient/DockerInDockerTest.java create mode 100755 mvnw create mode 100644 mvnw.cmd diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..c6feb8bb6f76f2553e266ff8bf8867105154237e GIT binary patch literal 49519 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%0A$pwhjXzyA0ZzErH{1@M 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 literal 0 HcmV?d00001 diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000000..6637cedb28e --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 7208f5731c6..1e08b79c364 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,8 +19,8 @@ before_install: - docker pull mysql:5.6 - docker pull mysql:5.5 - docker pull postgres:latest - - docker pull selenium/standalone-chrome-debug:2.45.0 - - docker pull selenium/standalone-firefox-debug:2.45.0 + - docker pull selenium/standalone-chrome-debug:2.52.0 + - docker pull selenium/standalone-firefox-debug:2.52.0 - docker pull richnorth/vnc-recorder:latest - docker pull nginx:1.9.4 - docker pull dduportal/docker-compose:1.6.0 @@ -32,6 +32,16 @@ before_install: script: - mvn -B test + # Run Docker-in-Docker tests + # Yes, we use selenium image, but we use it anyway in BrowserContainer's tests + - > + docker run --rm + -v "$HOME/.m2":/root/.m2/ + -v /var/run/docker.sock:/var/run/docker.sock + -v "$(pwd)":/app + -w /app + selenium/standalone-chrome-debug:2.52.0 + ./mvnw -B -pl core test -Dtest=*Docker - mvn -B test -f shade-test/pom.xml cache: diff --git a/circle.yml b/circle.yml index 7587c090bf9..ab3c86df6be 100644 --- a/circle.yml +++ b/circle.yml @@ -18,6 +18,16 @@ dependencies: test: override: - mvn -B test + # Run Docker-in-Docker tests + # Yes, we use selenium image, but we use it anyway in BrowserContainer's tests + - > + docker run --rm + -v "$HOME/.m2":/root/.m2/ + -v /var/run/docker.sock:/var/run/docker.sock + -v "$(pwd)":/app + -w /app + selenium/standalone-chrome-debug:2.52.0 + ./mvnw -B -pl core test -Dtest=*Docker - mvn -B test -f shade-test/pom.xml post: - mkdir -p $CIRCLE_TEST_REPORTS/junit/ diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index de34ce2f2e0..6a12aa0acdb 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -1,36 +1,35 @@ package org.testcontainers; import com.github.dockerjava.api.DockerClient; -import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.CreateContainerCmd; import com.github.dockerjava.api.exception.InternalServerErrorException; import com.github.dockerjava.api.exception.NotFoundException; -import com.github.dockerjava.api.model.Frame; import com.github.dockerjava.api.model.Image; import com.github.dockerjava.api.model.Info; import com.github.dockerjava.api.model.Version; -import com.github.dockerjava.core.command.LogContainerResultCallback; import com.github.dockerjava.core.command.PullImageResultCallback; -import com.github.dockerjava.core.command.WaitContainerResultCallback; import lombok.Synchronized; -import org.slf4j.Logger; +import lombok.extern.slf4j.Slf4j; import org.testcontainers.dockerclient.*; import java.util.List; import java.util.Optional; +import java.util.function.BiFunction; +import java.util.function.Consumer; import static java.util.Arrays.asList; -import static org.slf4j.LoggerFactory.getLogger; /** * Singleton class that provides initialized Docker clients. *

* The correct client configuration to use will be determined on first use, and cached thereafter. */ +@Slf4j public class DockerClientFactory { + private static final String TINY_IMAGE = "alpine:3.2"; private static DockerClientFactory instance; - private static final Logger LOGGER = getLogger(DockerClientFactory.class); // Cached client configuration private DockerClientProviderStrategy strategy; @@ -80,6 +79,8 @@ public DockerClient client() { } strategy = DockerClientProviderStrategy.getFirstValidStrategy(CONFIGURATION_STRATEGIES); + + log.info("Docker host IP address is {}", strategy.getDockerHostIpAddress()); DockerClient client = strategy.getClient(); if (!preconditionsChecked) { @@ -87,13 +88,20 @@ public DockerClient client() { Version version = client.versionCmd().exec(); activeApiVersion = version.getApiVersion(); activeExecutionDriver = dockerInfo.getExecutionDriver(); - LOGGER.info("Connected to docker: \n" + + log.info("Connected to docker: \n" + " Server Version: " + dockerInfo.getServerVersion() + "\n" + " API Version: " + activeApiVersion + "\n" + " Operating System: " + dockerInfo.getOperatingSystem() + "\n" + " Total Memory: " + dockerInfo.getMemTotal() / (1024 * 1024) + " MB"); checkVersion(version.getVersion()); + + List images = client.listImagesCmd().exec(); + // Pull the image we use to perform some checks + if (images.stream().noneMatch(it -> it.getRepoTags() != null && asList(it.getRepoTags()).contains(TINY_IMAGE))) { + client.pullImageCmd(TINY_IMAGE).exec(new PullImageResultCallback()).awaitSuccess(); + } + checkDiskSpaceAndHandleExceptions(client); preconditionsChecked = true; } @@ -121,7 +129,7 @@ private void checkDiskSpaceAndHandleExceptions(DockerClient client) { } catch (NotEnoughDiskSpaceException e) { throw e; } catch (Exception e) { - LOGGER.warn("Encountered and ignored error while checking disk space", e); + log.warn("Encountered and ignored error while checking disk space", e); } } @@ -130,44 +138,47 @@ private void checkDiskSpaceAndHandleExceptions(DockerClient client) { * @param client an active Docker client */ private void checkDiskSpace(DockerClient client) { + DiskSpaceUsage df = runInsideDocker(client, cmd -> cmd.withCmd("df", "-P"), (dockerClient, id) -> { + String logResults = dockerClient.logContainerCmd(id) + .withStdOut(true) + .exec(new LogToStringContainerCallback()) + .toString(); + + return parseAvailableDiskSpace(logResults); + }); + + log.info("Disk utilization in Docker environment is {} ({} )", + df.usedPercent.map(x -> x + "%").orElse("unknown"), + df.availableMB.map(x -> x + " MB available").orElse("unknown available")); + + if (df.availableMB.orElseThrow(NotAbleToGetDiskSpaceUsageException::new) < 2048) { + log.error("Docker environment has less than 2GB free - execution is unlikely to succeed so will be aborted."); + throw new NotEnoughDiskSpaceException("Not enough disk space in Docker environment"); + } + } - List images = client.listImagesCmd().exec(); - if (!images.stream().anyMatch(it -> it.getRepoTags() != null && asList(it.getRepoTags()).contains("alpine:3.2"))) { - PullImageResultCallback callback = client.pullImageCmd("alpine:3.2").exec(new PullImageResultCallback()); - callback.awaitSuccess(); + public T runInsideDocker(Consumer createContainerCmdConsumer, BiFunction block) { + if (strategy == null) { + client(); } + // We can't use client() here because it might create an infinite loop + return runInsideDocker(strategy.getClient(), createContainerCmdConsumer, block); + } - CreateContainerResponse createContainerResponse = client.createContainerCmd("alpine:3.2").withCmd("df", "-P").exec(); - String id = createContainerResponse.getId(); + private T runInsideDocker(DockerClient client, Consumer createContainerCmdConsumer, BiFunction block) { + CreateContainerCmd createContainerCmd = client.createContainerCmd(TINY_IMAGE); + createContainerCmdConsumer.accept(createContainerCmd); + String id = createContainerCmd.exec().getId(); client.startContainerCmd(id).exec(); - LogContainerResultCallback callback = client.logContainerCmd(id).withStdOut(true).exec(new LogContainerCallback()); try { - - WaitContainerResultCallback waitCallback = new WaitContainerResultCallback(); - client.waitContainerCmd(id).exec(waitCallback); - waitCallback.awaitStarted(); - - callback.awaitCompletion(); - String logResults = callback.toString(); - - DiskSpaceUsage df = parseAvailableDiskSpace(logResults); - LOGGER.info("Disk utilization in Docker environment is {} ({} )", - df.usedPercent.map(x -> x.toString() + "%").orElse("unknown"), - df.availableMB.map(x -> x.toString() + " MB available").orElse("unknown available")); - - if (df.availableMB.orElseThrow(NotAbleToGetDiskSpaceUsageException::new) < 2048) { - LOGGER.error("Docker environment has less than 2GB free - execution is unlikely to succeed so will be aborted."); - throw new NotEnoughDiskSpaceException("Not enough disk space in Docker environment"); - } - } catch (InterruptedException e) { - throw new RuntimeException(e); + return block.apply(client, id); } finally { try { client.removeContainerCmd(id).withRemoveVolumes(true).withForce(true).exec(); } catch (NotFoundException | InternalServerErrorException ignored) { - + log.debug("", ignored); } } } @@ -231,18 +242,3 @@ private static class NotAbleToGetDiskSpaceUsageException extends RuntimeExceptio } } } - -class LogContainerCallback extends LogContainerResultCallback { - private final StringBuffer log = new StringBuffer(); - - @Override - public void onNext(Frame frame) { - log.append(new String(frame.getPayload())); - super.onNext(frame); - } - - @Override - public String toString() { - return log.toString(); - } -} \ No newline at end of file diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java index fb32dd6630f..979b8f93a4e 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java @@ -1,18 +1,53 @@ package org.testcontainers.dockerclient; import com.github.dockerjava.core.DockerClientConfig; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.StringUtils; +import org.testcontainers.DockerClientFactory; +import java.io.File; +import java.util.Optional; + +@Slf4j public class DockerClientConfigUtils { + + // See https://github.com/docker/docker/blob/a9fa38b1edf30b23cae3eade0be48b3d4b1de14b/daemon/initlayer/setup_unix.go#L25 + public static final boolean IN_A_CONTAINER = new File("/.dockerenv").exists(); + + @Getter(lazy = true) + private static final Optional detectedDockerHostIp = Optional + .of(IN_A_CONTAINER) + .filter(it -> it) + .map(file -> DockerClientFactory.instance().runInsideDocker( + cmd -> cmd.withCmd("sh", "-c", "ip route|awk '/default/ { print $3 }'"), + (client, id) -> { + try { + return client.logContainerCmd(id) + .withStdOut(true) + .exec(new LogToStringContainerCallback()) + .toString(); + } catch (Exception e) { + log.warn("Can't parse the default gateway IP", e); + return null; + } + } + )) + .map(StringUtils::trimToEmpty) + .filter(StringUtils::isNotBlank); + public static String getDockerHostIpAddress(DockerClientConfig config) { - switch (config.getDockerHost().getScheme()) { - case "http": - case "https": - case "tcp": - return config.getDockerHost().getHost(); - case "unix": - return "localhost"; - default: - return null; - } + return getDetectedDockerHostIp().orElseGet(() -> { + switch (config.getDockerHost().getScheme()) { + case "http": + case "https": + case "tcp": + return config.getDockerHost().getHost(); + case "unix": + return "localhost"; + default: + return null; + } + }); } } diff --git a/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java index 4b414b70d5e..918f341f6b8 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java @@ -27,7 +27,6 @@ public void test() throws InvalidConfigurationException { } LOGGER.info("Found docker client settings from environment"); - LOGGER.info("Docker host IP address is {}", DockerClientConfigUtils.getDockerHostIpAddress(config)); } @Override diff --git a/core/src/main/java/org/testcontainers/dockerclient/LogToStringContainerCallback.java b/core/src/main/java/org/testcontainers/dockerclient/LogToStringContainerCallback.java new file mode 100644 index 00000000000..e0ffce7c3a1 --- /dev/null +++ b/core/src/main/java/org/testcontainers/dockerclient/LogToStringContainerCallback.java @@ -0,0 +1,24 @@ +package org.testcontainers.dockerclient; + +import com.github.dockerjava.api.model.Frame; +import com.github.dockerjava.core.command.LogContainerResultCallback; + +public class LogToStringContainerCallback extends LogContainerResultCallback { + private final StringBuffer log = new StringBuffer(); + + @Override + public void onNext(Frame frame) { + log.append(new String(frame.getPayload())); + super.onNext(frame); + } + + @Override + public String toString() { + try { + awaitCompletion(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + return log.toString(); + } +} diff --git a/core/src/test/java/org/testcontainers/dockerclient/DockerInDockerTest.java b/core/src/test/java/org/testcontainers/dockerclient/DockerInDockerTest.java new file mode 100644 index 00000000000..eda3dc5746a --- /dev/null +++ b/core/src/test/java/org/testcontainers/dockerclient/DockerInDockerTest.java @@ -0,0 +1,37 @@ +package org.testcontainers.dockerclient; + +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; +import org.testcontainers.containers.GenericContainer; + +import static org.junit.Assume.assumeTrue; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.testcontainers.junit.GenericContainerRuleTest.getReaderForContainerPort80; + +public class DockerInDockerTest { + + @ClassRule + public static TestRule assumption = new TestWatcher() { + @Override + public Statement apply(Statement base, Description description) { + assumeTrue("We're inside a container", DockerClientConfigUtils.IN_A_CONTAINER); + return super.apply(base, description); + } + }; + + @Rule + public GenericContainer container = new GenericContainer("alpine:3.2") + .withExposedPorts(80) + .withCommand("/bin/sh", "-c", "while true; do echo \"hello\" | nc -l -p 80; done"); + + @Test + public void testIpDetection() throws Exception { + String line = getReaderForContainerPort80(container).readLine(); + assertEquals("The container is accessible", "hello", line); + } +} diff --git a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java index f91ad463444..5cac8fc30dd 100644 --- a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java +++ b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java @@ -282,7 +282,7 @@ public void extraHostTest() throws IOException { assertTrue("The hosts file of container contains extra host", matcher.find()); } - private BufferedReader getReaderForContainerPort80(GenericContainer container) { + public static BufferedReader getReaderForContainerPort80(GenericContainer container) { return Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, () -> { Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); diff --git a/mvnw b/mvnw new file mode 100755 index 00000000000..6ecc150ae0a --- /dev/null +++ b/mvnw @@ -0,0 +1,236 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # + # Look for the Apple JDKs first to preserve the existing behaviour, and then look + # for the new JDKs provided by Oracle. + # + if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then + # + # Apple JDKs + # + export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then + # + # Apple JDKs + # + export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then + # + # Oracle JDKs + # + export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then + # + # Apple JDKs + # + export JAVA_HOME=`/usr/libexec/java_home` + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Migwn, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + local basedir=$(pwd) + local wdir=$(pwd) + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + wdir=$(cd "$wdir/.."; pwd) + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +# avoid using MAVEN_CMD_LINE_ARGS below since that would loose parameter escaping in $@ +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 00000000000..8bb82754165 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,146 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +set MAVEN_CMD_LINE_ARGS=%MAVEN_CONFIG% %* + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" + +set WRAPPER_JAR=""%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +# avoid using MAVEN_CMD_LINE_ARGS below since that would loose parameter escaping in %* +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% From 55d93aba9a141253005a7f1cea5887b05d7edc3b Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 15 Jan 2017 16:28:27 +0200 Subject: [PATCH 0024/5615] Fix DiD tests pattern using GenericContainerRuleTest for docker-in-docker tests --- .travis.yml | 16 ++++---- circle.yml | 16 ++++---- .../dockerclient/DockerInDockerTest.java | 37 ------------------- .../junit/GenericContainerRuleTest.java | 2 +- 4 files changed, 17 insertions(+), 54 deletions(-) delete mode 100644 core/src/test/java/org/testcontainers/dockerclient/DockerInDockerTest.java diff --git a/.travis.yml b/.travis.yml index 1e08b79c364..48eccc5dad7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,14 +34,14 @@ script: - mvn -B test # Run Docker-in-Docker tests # Yes, we use selenium image, but we use it anyway in BrowserContainer's tests - - > - docker run --rm - -v "$HOME/.m2":/root/.m2/ - -v /var/run/docker.sock:/var/run/docker.sock - -v "$(pwd)":/app - -w /app - selenium/standalone-chrome-debug:2.52.0 - ./mvnw -B -pl core test -Dtest=*Docker + - | + DOCKER_HOST=unix:///var/run/docker.sock DOCKER_TLS_VERIFY= docker run --rm \ + -v "$HOME/.m2":/root/.m2/ \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v "$(pwd)":"$(pwd)" \ + -w "$(pwd)" \ + selenium/standalone-chrome-debug:2.52.0 \ + ./mvnw -B -pl core test -Dtest=*GenericContainerRuleTest - mvn -B test -f shade-test/pom.xml cache: diff --git a/circle.yml b/circle.yml index ab3c86df6be..84896d90d25 100644 --- a/circle.yml +++ b/circle.yml @@ -20,14 +20,14 @@ test: - mvn -B test # Run Docker-in-Docker tests # Yes, we use selenium image, but we use it anyway in BrowserContainer's tests - - > - docker run --rm - -v "$HOME/.m2":/root/.m2/ - -v /var/run/docker.sock:/var/run/docker.sock - -v "$(pwd)":/app - -w /app - selenium/standalone-chrome-debug:2.52.0 - ./mvnw -B -pl core test -Dtest=*Docker + - | + DOCKER_HOST=unix:///var/run/docker.sock DOCKER_TLS_VERIFY= docker run --rm \ + -v "$HOME/.m2":/root/.m2/ \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v "$(pwd)":"$(pwd)" \ + -w "$(pwd)" \ + selenium/standalone-chrome-debug:2.52.0 \ + ./mvnw -B -pl core test -Dtest=*GenericContainerRuleTest - mvn -B test -f shade-test/pom.xml post: - mkdir -p $CIRCLE_TEST_REPORTS/junit/ diff --git a/core/src/test/java/org/testcontainers/dockerclient/DockerInDockerTest.java b/core/src/test/java/org/testcontainers/dockerclient/DockerInDockerTest.java deleted file mode 100644 index eda3dc5746a..00000000000 --- a/core/src/test/java/org/testcontainers/dockerclient/DockerInDockerTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.testcontainers.dockerclient; - -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestRule; -import org.junit.rules.TestWatcher; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import org.testcontainers.containers.GenericContainer; - -import static org.junit.Assume.assumeTrue; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.testcontainers.junit.GenericContainerRuleTest.getReaderForContainerPort80; - -public class DockerInDockerTest { - - @ClassRule - public static TestRule assumption = new TestWatcher() { - @Override - public Statement apply(Statement base, Description description) { - assumeTrue("We're inside a container", DockerClientConfigUtils.IN_A_CONTAINER); - return super.apply(base, description); - } - }; - - @Rule - public GenericContainer container = new GenericContainer("alpine:3.2") - .withExposedPorts(80) - .withCommand("/bin/sh", "-c", "while true; do echo \"hello\" | nc -l -p 80; done"); - - @Test - public void testIpDetection() throws Exception { - String line = getReaderForContainerPort80(container).readLine(); - assertEquals("The container is accessible", "hello", line); - } -} diff --git a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java index 5cac8fc30dd..f91ad463444 100644 --- a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java +++ b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java @@ -282,7 +282,7 @@ public void extraHostTest() throws IOException { assertTrue("The hosts file of container contains extra host", matcher.find()); } - public static BufferedReader getReaderForContainerPort80(GenericContainer container) { + private BufferedReader getReaderForContainerPort80(GenericContainer container) { return Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, () -> { Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); From 7471ffed9e8f5942e7fd03ae7fd62ca315e827ed Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 16 Jan 2017 11:16:52 +0200 Subject: [PATCH 0025/5615] use OpenJDK maven image to run docker-in-docker tests --- .travis.yml | 3 +-- circle.yml | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 48eccc5dad7..3c9ea15ebc9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,14 +33,13 @@ before_install: script: - mvn -B test # Run Docker-in-Docker tests - # Yes, we use selenium image, but we use it anyway in BrowserContainer's tests - | DOCKER_HOST=unix:///var/run/docker.sock DOCKER_TLS_VERIFY= docker run --rm \ -v "$HOME/.m2":/root/.m2/ \ -v /var/run/docker.sock:/var/run/docker.sock \ -v "$(pwd)":"$(pwd)" \ -w "$(pwd)" \ - selenium/standalone-chrome-debug:2.52.0 \ + openjdk:8-jre \ ./mvnw -B -pl core test -Dtest=*GenericContainerRuleTest - mvn -B test -f shade-test/pom.xml diff --git a/circle.yml b/circle.yml index 84896d90d25..9cb5cdce9fa 100644 --- a/circle.yml +++ b/circle.yml @@ -19,14 +19,13 @@ test: override: - mvn -B test # Run Docker-in-Docker tests - # Yes, we use selenium image, but we use it anyway in BrowserContainer's tests - | DOCKER_HOST=unix:///var/run/docker.sock DOCKER_TLS_VERIFY= docker run --rm \ -v "$HOME/.m2":/root/.m2/ \ -v /var/run/docker.sock:/var/run/docker.sock \ -v "$(pwd)":"$(pwd)" \ -w "$(pwd)" \ - selenium/standalone-chrome-debug:2.52.0 \ + openjdk:8-jre \ ./mvnw -B -pl core test -Dtest=*GenericContainerRuleTest - mvn -B test -f shade-test/pom.xml post: From ab9a570267eff238436dfd4ce7e86402b44b0e26 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sat, 21 Jan 2017 10:30:27 +0200 Subject: [PATCH 0026/5615] Add the docs --- docs/SUMMARY.md | 3 ++- docs/compatibility.md | 3 ++- docs/usage.md | 3 ++- docs/usage/dind.md | 44 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 docs/usage/dind.md diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 900cb28788a..e0f8eefcb40 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -8,6 +8,7 @@ * [Usage modes](usage.md#usage-modes) * [Maven dependencies](usage.md#maven-dependencies) * [Logging](usage.md#logging) + * [Docker in Docker](usage/dind.md) ## Generic containers @@ -52,4 +53,4 @@ ## * [License](index.md#license) * [Attributions](index.md#attributions) -* [Contributing](index.md#contributing) \ No newline at end of file +* [Contributing](index.md#contributing) diff --git a/docs/compatibility.md b/docs/compatibility.md index 4a8b1c2c973..a3418fa3c77 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -5,7 +5,8 @@ | Linux - general | Docker v1.10 | | | Linux - Travis CI | Docker v1.10 | See [example .travis.yml](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/.travis.yml) for baseline Travis CI configuration | | Linux - Circle CI (LXC driver) | Docker v1.9.1 | The `exec` feature is not compatible with Circle CI. See [example circle.yml](../circle.yml) for baseline CircleCI configuration | +| Linux - Docker in Docker | Docker v1.12 | See [Docker-in-Docker](usage/dind.md) for the detailed configuration | | Mac OS X - Docker Toolbox | Docker Machine v0.8.0 | | | Mac OS X - Docker for Mac | 1.12.0 | *Support is best-efforts at present*. `getTestHostIpAddress()` is [not currently supported](https://github.com/testcontainers/testcontainers-java/issues/166) due to limitations in Docker for Mac. | | Windows - Docker Toolbox | | *Support is limited at present and this is not currently tested on a regular basis*. | -| Windows - Docker for Windows Beta | | *Not currently supported*. | \ No newline at end of file +| Windows - Docker for Windows Beta | | *Not currently supported*. | diff --git a/docs/usage.md b/docs/usage.md index a60ddc15095..0b4a65ed429 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -19,6 +19,7 @@ Testcontainers will try to connect to a Docker daemon using the following strate * `DOCKER_TLS_VERIFY=1` * `DOCKER_CERT_PATH=~/.docker` * If Docker Machine is installed, the docker machine environment for the *first* machine found. Docker Machine needs to be on the PATH for this to succeed. +* If you're going to run your tests inside a container, please read [Docker in Docker](usage/dind.md) first. ### Usage modes @@ -97,4 +98,4 @@ should be included in your classpath to show a reasonable level of log output: -``` \ No newline at end of file +``` diff --git a/docs/usage/dind.md b/docs/usage/dind.md new file mode 100644 index 00000000000..869f55eec76 --- /dev/null +++ b/docs/usage/dind.md @@ -0,0 +1,44 @@ +# Docker in Docker + +Since TestContainers version 1.1.8, you can run your tests inside a container. +It's very useful for different CI scenarios like running everything in containers on Jenkins. + +TestContainers will automatically detect if it's inside a container and instead of "localhost" will use the default gateway's IP. + +However, an additional configuration is required if you use [volume mapping](options.md#volume-mapping). + +## Docker-only +If you run the tests with just `docker run ...` then make sure you add `-v $PWD:$PWD -w $PWD -v /var/run/docker.sock:/var/run/docker.sock` to the command, so it will look like this: +```bash +$ tree . +. +├── pom.xml +└── src + └── test + └── java + └── MyTestWithTestContainers.java + +$ docker run -it --rm -v $PWD:$PWD -w $PWD -v /var/run/docker.sock:/var/run/docker.sock maven:3 mvn test +``` + +Where: +* `-v $PWD:$PWD` will add your current directory as a volume inside the container +* `-w $PWD` will set the current directory to this volume +* `-v /var/run/docker.sock:/var/run/docker.sock` will map the Docker socket + +## Docker Compose +The same can be achived with Docker Compose: +```yaml +tests: + image: maven:3 + stop_signal: SIGKILL + stdin_open: true + tty: true + working_dir: $PWD + volumes: + - $PWD:$PWD + - /var/run/docker.sock:/var/run/docker.sock + # Maven cache (optional) + - ~/.m2:/root/.m2 + command: mvn test +``` From e6d8be07fc6172af020bb41c92a2289a81bbefcb Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 21 Jan 2017 19:33:36 +0000 Subject: [PATCH 0027/5615] Tweak docs re in-docker usage --- docs/usage/dind.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/usage/dind.md b/docs/usage/dind.md index 869f55eec76..ded3c0f7de7 100644 --- a/docs/usage/dind.md +++ b/docs/usage/dind.md @@ -1,13 +1,18 @@ # Docker in Docker -Since TestContainers version 1.1.8, you can run your tests inside a container. -It's very useful for different CI scenarios like running everything in containers on Jenkins. +> Since 1.1.8 + +Testcontainers itself can be used from inside a container. +This is very useful for different CI scenarios like running everything in containers on Jenkins, or Docker-based CI tools such as Drone. TestContainers will automatically detect if it's inside a container and instead of "localhost" will use the default gateway's IP. -However, an additional configuration is required if you use [volume mapping](options.md#volume-mapping). +However, additional configuration is required if you use [volume mapping](options.md#volume-mapping). The following points need to be considered: + +* The docker socket must be available via a volume mount +* The 'local' source code directory must be volume mounted *at the same path* inside the container that Testcontainers runs in, so that Testcontainers is able to set up the correct volume mounts for the containers it spawns. -## Docker-only +## Docker-only example If you run the tests with just `docker run ...` then make sure you add `-v $PWD:$PWD -w $PWD -v /var/run/docker.sock:/var/run/docker.sock` to the command, so it will look like this: ```bash $ tree . @@ -26,7 +31,7 @@ Where: * `-w $PWD` will set the current directory to this volume * `-v /var/run/docker.sock:/var/run/docker.sock` will map the Docker socket -## Docker Compose +## Docker Compose example The same can be achived with Docker Compose: ```yaml tests: From 1467b2e7bf63f0aeef8b7be8405e341712e790af Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 22 Jan 2017 07:55:42 +0000 Subject: [PATCH 0028/5615] Relax disk space checking to not abort in cases where `df` output shows no available volumes (see #273). While the cause is not known, and Docker may yet fail, relaxing this constraint at least lets Docker try without aborting prematurely. Use functional operation to compare disk space if not null --- .../main/java/org/testcontainers/DockerClientFactory.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 6a12aa0acdb..26facdfbec7 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -151,7 +151,7 @@ private void checkDiskSpace(DockerClient client) { df.usedPercent.map(x -> x + "%").orElse("unknown"), df.availableMB.map(x -> x + " MB available").orElse("unknown available")); - if (df.availableMB.orElseThrow(NotAbleToGetDiskSpaceUsageException::new) < 2048) { + if (df.availableMB.map(it -> it < 2048).orElse(false)) { log.error("Docker environment has less than 2GB free - execution is unlikely to succeed so will be aborted."); throw new NotEnoughDiskSpaceException("Not enough disk space in Docker environment"); } @@ -235,10 +235,4 @@ private static class NotEnoughDiskSpaceException extends RuntimeException { super(message); } } - - private static class NotAbleToGetDiskSpaceUsageException extends RuntimeException { - NotAbleToGetDiskSpaceUsageException() { - super(); - } - } } From 0c0244d8d710b97c3c33d668753f2d9866d481a4 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 22 Jan 2017 08:03:59 +0000 Subject: [PATCH 0029/5615] Rename Docker-in-Docker references to 'running inside docker' to avoid confusion with the DinD project which is not _quite_ the same thing (DinD is probably a _subset_ of what Testcontainers supports) --- docs/SUMMARY.md | 2 +- docs/compatibility.md | 2 +- docs/usage.md | 2 +- docs/usage/{dind.md => inside_docker.md} | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename docs/usage/{dind.md => inside_docker.md} (98%) diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index e0f8eefcb40..4ea04004529 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -8,7 +8,7 @@ * [Usage modes](usage.md#usage-modes) * [Maven dependencies](usage.md#maven-dependencies) * [Logging](usage.md#logging) - * [Docker in Docker](usage/dind.md) + * [Running inside Docker](usage/inside_docker.md) ## Generic containers diff --git a/docs/compatibility.md b/docs/compatibility.md index a3418fa3c77..aca1d8c3d12 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -5,7 +5,7 @@ | Linux - general | Docker v1.10 | | | Linux - Travis CI | Docker v1.10 | See [example .travis.yml](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/.travis.yml) for baseline Travis CI configuration | | Linux - Circle CI (LXC driver) | Docker v1.9.1 | The `exec` feature is not compatible with Circle CI. See [example circle.yml](../circle.yml) for baseline CircleCI configuration | -| Linux - Docker in Docker | Docker v1.12 | See [Docker-in-Docker](usage/dind.md) for the detailed configuration | +| Linux - within a Docker container | Docker v1.12 | See [Running inside Docker](usage/inside_docker.md) for the detailed configuration | | Mac OS X - Docker Toolbox | Docker Machine v0.8.0 | | | Mac OS X - Docker for Mac | 1.12.0 | *Support is best-efforts at present*. `getTestHostIpAddress()` is [not currently supported](https://github.com/testcontainers/testcontainers-java/issues/166) due to limitations in Docker for Mac. | | Windows - Docker Toolbox | | *Support is limited at present and this is not currently tested on a regular basis*. | diff --git a/docs/usage.md b/docs/usage.md index 0b4a65ed429..11811de2f67 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -19,7 +19,7 @@ Testcontainers will try to connect to a Docker daemon using the following strate * `DOCKER_TLS_VERIFY=1` * `DOCKER_CERT_PATH=~/.docker` * If Docker Machine is installed, the docker machine environment for the *first* machine found. Docker Machine needs to be on the PATH for this to succeed. -* If you're going to run your tests inside a container, please read [Docker in Docker](usage/dind.md) first. +* If you're going to run your tests inside a container, please read [Running inside a Docker container](usage/inside_docker.md) first. ### Usage modes diff --git a/docs/usage/dind.md b/docs/usage/inside_docker.md similarity index 98% rename from docs/usage/dind.md rename to docs/usage/inside_docker.md index ded3c0f7de7..f4773dcb147 100644 --- a/docs/usage/dind.md +++ b/docs/usage/inside_docker.md @@ -1,4 +1,4 @@ -# Docker in Docker +# Running inside a Docker container > Since 1.1.8 From 40ddc29feb58828ae344504dc38c5dc8beafdf8c Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 22 Jan 2017 07:58:30 +0000 Subject: [PATCH 0030/5615] Add utility class to allow image names to be configured via a classpath file Replace static string names for ambassador and vnc recorder container images with classpath lookup. Add note to readme re overriding hardcoded image names Refs #259, #276 --- .../containers/AmbassadorContainer.java | 3 +- .../VncRecordingSidekickContainer.java | 3 +- .../utility/TestcontainersConfiguration.java | 55 +++++++++++++++++++ docs/usage.md | 16 ++++++ 4 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java diff --git a/core/src/main/java/org/testcontainers/containers/AmbassadorContainer.java b/core/src/main/java/org/testcontainers/containers/AmbassadorContainer.java index d3730c32b15..756b88d05ab 100644 --- a/core/src/main/java/org/testcontainers/containers/AmbassadorContainer.java +++ b/core/src/main/java/org/testcontainers/containers/AmbassadorContainer.java @@ -5,6 +5,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import org.testcontainers.containers.traits.LinkableContainer; +import org.testcontainers.utility.TestcontainersConfiguration; import java.util.List; @@ -24,7 +25,7 @@ public class AmbassadorContainer> extends private final int servicePort; public AmbassadorContainer(LinkableContainer otherContainer, String serviceName, int servicePort) { - super("richnorth/ambassador:latest"); + super(TestcontainersConfiguration.getInstance().getAmbassadorContainerImage()); /* Use the unique 'identifierPrefix' (random compose project name) so that the ambassador can see diff --git a/core/src/main/java/org/testcontainers/containers/VncRecordingSidekickContainer.java b/core/src/main/java/org/testcontainers/containers/VncRecordingSidekickContainer.java index baef625cd0e..959ae80e46f 100644 --- a/core/src/main/java/org/testcontainers/containers/VncRecordingSidekickContainer.java +++ b/core/src/main/java/org/testcontainers/containers/VncRecordingSidekickContainer.java @@ -3,6 +3,7 @@ import com.github.dockerjava.api.command.InspectContainerResponse; import org.testcontainers.containers.traits.LinkableContainer; import org.testcontainers.containers.traits.VncService; +import org.testcontainers.utility.TestcontainersConfiguration; import java.io.File; import java.io.IOException; @@ -23,7 +24,7 @@ public class VncRecordingSidekickContainer ``` + +## Using custom utility images + +Testcontainers uses additional docker images under some modes of execution: + +* `richnorth/ambassador:latest`, which is a TCP proxy used to expose Docker Compose container ports outside of the compose network. +* `richnorth/vncrecorder:latest`, which is a VNC->FLV recorder, used for capturing Webdriver test videos. + +> *N.B.:* both these images use the 'latest' tag, which could potentially affect repeatability of tests and compatibility with Testcontainers _if the image is ever changed_. This is a [known issue](https://github.com/testcontainers/testcontainers-java/issues/276) which will be addressed in the future. The current 'latest' version of these images will never be changed until they are replaced by a new image altogether. + +If it is necessary to override these image names (e.g. when using a private registry), you should create a file named `testcontainers.properties` and place it on the classpath with the following content: + +```properties +ambassador.container.image=replacement image name here +vncrecorder.container.image=replacement image name here +``` From d8d6df80307a7f296a847cbe69154df7c8a365d1 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 22 Jan 2017 19:28:22 +0000 Subject: [PATCH 0031/5615] Modify JDBC driver connection behaviour to fail early if the delegate JDBC Driver cannot be instantiated. Connection attempts remain inside a retry loop. Refs #230 --- .../org/testcontainers/containers/JdbcDatabaseContainer.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index 7d594cb1823..2d4136580d0 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -117,8 +117,10 @@ public Connection createConnection(String queryString) throws SQLException { info.put("password", this.getPassword()); final String url = this.getJdbcUrl() + queryString; + final Driver jdbcDriverInstance = getJdbcDriverInstance(); + try { - return Unreliables.retryUntilSuccess(120, TimeUnit.SECONDS, () -> getJdbcDriverInstance().connect(url, info)); + return Unreliables.retryUntilSuccess(120, TimeUnit.SECONDS, () -> jdbcDriverInstance.connect(url, info)); } catch (Exception e) { throw new SQLException("Could not create new connection", e); } From 854f47c3a65468a62f67a324fd13309017741f63 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 22 Jan 2017 19:44:36 +0000 Subject: [PATCH 0032/5615] Update changelog for 1.1.8 --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1799f1f93b2..16695da46e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,21 @@ # Change Log All notable changes to this project will be documented in this file. +## [1.1.8] - 2017-01-22 +### Fixed +- Comaptibility fixes for Docker for Mac v1.13.0 (#272) +- Relax docker environment disk space check to accomodate unusual empty `df` output observed on Docker for Mac with OverlayFS (#273, #278) +- Fix inadvertent private-scoping of startup checks' `StartupStatus`, which made implementation of custom startup checks impossible (#266) +- Fix potential resource lead/deadlock when errors are encountered building images from a Dockerfile (#274) + +### Changed +- Add support for execution within a Docker container (#267), correcting resolution of container addresses +- Add support for version 2 of private docker registries, configured via `$HOME/.docker/config.json` (#270) +- Use current classloader instead of system classloader for loading JDBC drivers (#261) +- Allow hardcoded container image names for Ambassador and VNC recorder containers to be changed via a configuration file (#277, #259) +- Allow Selenium Webdriver container image name to be specified as a constructor parameter (#249, #171) + + ## [1.1.7] - 2016-11-19 ### Fixed - Compensate for premature TCP socket opening in Docker for Mac (#160, #236) From 5571e8d7ab57541af75294ba49c4f18fdcf018b9 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 22 Jan 2017 21:05:59 +0000 Subject: [PATCH 0033/5615] Update release version number to 1.1.8 --- core/pom.xml | 2 +- modules/jdbc-test/pom.xml | 2 +- modules/jdbc/pom.xml | 2 +- modules/mariadb/pom.xml | 2 +- modules/mysql/pom.xml | 2 +- modules/nginx/pom.xml | 2 +- modules/oracle-xe/pom.xml | 2 +- modules/postgresql/pom.xml | 2 +- modules/selenium/pom.xml | 2 +- modules/virtuoso/pom.xml | 2 +- pom.xml | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 60295c30f5f..a639b640a56 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -6,7 +6,7 @@ org.testcontainers testcontainers-parent - 1.1.8-SNAPSHOT + 1.1.8 ../pom.xml diff --git a/modules/jdbc-test/pom.xml b/modules/jdbc-test/pom.xml index 1add7cef653..732ebff1e42 100644 --- a/modules/jdbc-test/pom.xml +++ b/modules/jdbc-test/pom.xml @@ -6,7 +6,7 @@ org.testcontainers testcontainers-parent - 1.1.8-SNAPSHOT + 1.1.8 ../../pom.xml diff --git a/modules/jdbc/pom.xml b/modules/jdbc/pom.xml index f86a0067f3b..b46aec557a3 100644 --- a/modules/jdbc/pom.xml +++ b/modules/jdbc/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.8-SNAPSHOT + 1.1.8 ../../pom.xml diff --git a/modules/mariadb/pom.xml b/modules/mariadb/pom.xml index f0913be4831..0f5298ca09e 100644 --- a/modules/mariadb/pom.xml +++ b/modules/mariadb/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.8-SNAPSHOT + 1.1.8 ../../pom.xml diff --git a/modules/mysql/pom.xml b/modules/mysql/pom.xml index 25b7677b9c4..f3ae8b7e3f7 100644 --- a/modules/mysql/pom.xml +++ b/modules/mysql/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.8-SNAPSHOT + 1.1.8 ../../pom.xml diff --git a/modules/nginx/pom.xml b/modules/nginx/pom.xml index b5cf56c961d..9f055a264f1 100644 --- a/modules/nginx/pom.xml +++ b/modules/nginx/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.8-SNAPSHOT + 1.1.8 ../../pom.xml diff --git a/modules/oracle-xe/pom.xml b/modules/oracle-xe/pom.xml index 8456620dfe1..ffd22f5f710 100644 --- a/modules/oracle-xe/pom.xml +++ b/modules/oracle-xe/pom.xml @@ -8,7 +8,7 @@ org.testcontainers testcontainers-parent - 1.1.8-SNAPSHOT + 1.1.8 ../../pom.xml diff --git a/modules/postgresql/pom.xml b/modules/postgresql/pom.xml index 9c8cdec6a17..3a33ee517de 100644 --- a/modules/postgresql/pom.xml +++ b/modules/postgresql/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.8-SNAPSHOT + 1.1.8 ../../pom.xml diff --git a/modules/selenium/pom.xml b/modules/selenium/pom.xml index 6bf00052f1a..e10b659d07a 100644 --- a/modules/selenium/pom.xml +++ b/modules/selenium/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.8-SNAPSHOT + 1.1.8 ../../pom.xml diff --git a/modules/virtuoso/pom.xml b/modules/virtuoso/pom.xml index 75005777f28..0917753c718 100644 --- a/modules/virtuoso/pom.xml +++ b/modules/virtuoso/pom.xml @@ -4,7 +4,7 @@ org.testcontainers testcontainers-parent - 1.1.8-SNAPSHOT + 1.1.8 ../../pom.xml diff --git a/pom.xml b/pom.xml index c6a5b3a85d5..a0f1f538b9f 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.8-SNAPSHOT + 1.1.8 pom From f45945ae418d53d986675eeebdb1fa94c64e2e14 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 22 Jan 2017 21:40:38 +0000 Subject: [PATCH 0034/5615] Update development version number to 1.1.9-SNAPSHOT --- core/pom.xml | 2 +- modules/jdbc-test/pom.xml | 2 +- modules/jdbc/pom.xml | 2 +- modules/mariadb/pom.xml | 2 +- modules/mysql/pom.xml | 2 +- modules/nginx/pom.xml | 2 +- modules/oracle-xe/pom.xml | 2 +- modules/postgresql/pom.xml | 2 +- modules/selenium/pom.xml | 2 +- modules/virtuoso/pom.xml | 2 +- pom.xml | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index a639b640a56..f5c2c772ce0 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -6,7 +6,7 @@ org.testcontainers testcontainers-parent - 1.1.8 + 1.1.9-SNAPSHOT ../pom.xml diff --git a/modules/jdbc-test/pom.xml b/modules/jdbc-test/pom.xml index 732ebff1e42..96fd5c721ca 100644 --- a/modules/jdbc-test/pom.xml +++ b/modules/jdbc-test/pom.xml @@ -6,7 +6,7 @@ org.testcontainers testcontainers-parent - 1.1.8 + 1.1.9-SNAPSHOT ../../pom.xml diff --git a/modules/jdbc/pom.xml b/modules/jdbc/pom.xml index b46aec557a3..8afd3bc04ba 100644 --- a/modules/jdbc/pom.xml +++ b/modules/jdbc/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.8 + 1.1.9-SNAPSHOT ../../pom.xml diff --git a/modules/mariadb/pom.xml b/modules/mariadb/pom.xml index 0f5298ca09e..54c382de56e 100644 --- a/modules/mariadb/pom.xml +++ b/modules/mariadb/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.8 + 1.1.9-SNAPSHOT ../../pom.xml diff --git a/modules/mysql/pom.xml b/modules/mysql/pom.xml index f3ae8b7e3f7..b97dd0e2586 100644 --- a/modules/mysql/pom.xml +++ b/modules/mysql/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.8 + 1.1.9-SNAPSHOT ../../pom.xml diff --git a/modules/nginx/pom.xml b/modules/nginx/pom.xml index 9f055a264f1..9cf89e200d9 100644 --- a/modules/nginx/pom.xml +++ b/modules/nginx/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.8 + 1.1.9-SNAPSHOT ../../pom.xml diff --git a/modules/oracle-xe/pom.xml b/modules/oracle-xe/pom.xml index ffd22f5f710..945993ac74a 100644 --- a/modules/oracle-xe/pom.xml +++ b/modules/oracle-xe/pom.xml @@ -8,7 +8,7 @@ org.testcontainers testcontainers-parent - 1.1.8 + 1.1.9-SNAPSHOT ../../pom.xml diff --git a/modules/postgresql/pom.xml b/modules/postgresql/pom.xml index 3a33ee517de..79c6e379def 100644 --- a/modules/postgresql/pom.xml +++ b/modules/postgresql/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.8 + 1.1.9-SNAPSHOT ../../pom.xml diff --git a/modules/selenium/pom.xml b/modules/selenium/pom.xml index e10b659d07a..b405d371fa8 100644 --- a/modules/selenium/pom.xml +++ b/modules/selenium/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.8 + 1.1.9-SNAPSHOT ../../pom.xml diff --git a/modules/virtuoso/pom.xml b/modules/virtuoso/pom.xml index 0917753c718..dd9e3cdd1cb 100644 --- a/modules/virtuoso/pom.xml +++ b/modules/virtuoso/pom.xml @@ -4,7 +4,7 @@ org.testcontainers testcontainers-parent - 1.1.8 + 1.1.9-SNAPSHOT ../../pom.xml diff --git a/pom.xml b/pom.xml index a0f1f538b9f..460d26da7dd 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.8 + 1.1.9-SNAPSHOT pom From d94edb848a3f556e66382e624e0d7e19b6738a90 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 22 Jan 2017 21:42:42 +0000 Subject: [PATCH 0035/5615] Update shade-test dependencies for new dev version --- shade-test/clashing-deps-jackson/pom.xml | 2 +- shade-test/clashing-deps-jersey/pom.xml | 2 +- shade-test/pom.xml | 2 +- shade-test/service-lookup-dropwizard/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/shade-test/clashing-deps-jackson/pom.xml b/shade-test/clashing-deps-jackson/pom.xml index bce5fc134ec..f0d7b8e8888 100644 --- a/shade-test/clashing-deps-jackson/pom.xml +++ b/shade-test/clashing-deps-jackson/pom.xml @@ -5,7 +5,7 @@ shade-test org.testcontainers - 1.1.8-SNAPSHOT + 1.1.9-SNAPSHOT ../pom.xml 4.0.0 diff --git a/shade-test/clashing-deps-jersey/pom.xml b/shade-test/clashing-deps-jersey/pom.xml index fd2184cf92a..79db89c52d3 100644 --- a/shade-test/clashing-deps-jersey/pom.xml +++ b/shade-test/clashing-deps-jersey/pom.xml @@ -5,7 +5,7 @@ shade-test org.testcontainers - 1.1.8-SNAPSHOT + 1.1.9-SNAPSHOT ../pom.xml 4.0.0 diff --git a/shade-test/pom.xml b/shade-test/pom.xml index 03da6a08395..c2625cbf8e5 100644 --- a/shade-test/pom.xml +++ b/shade-test/pom.xml @@ -5,7 +5,7 @@ testcontainers-parent org.testcontainers - 1.1.8-SNAPSHOT + 1.1.9-SNAPSHOT ../pom.xml 4.0.0 diff --git a/shade-test/service-lookup-dropwizard/pom.xml b/shade-test/service-lookup-dropwizard/pom.xml index e28b958856d..f2c1fd36b39 100644 --- a/shade-test/service-lookup-dropwizard/pom.xml +++ b/shade-test/service-lookup-dropwizard/pom.xml @@ -5,7 +5,7 @@ shade-test org.testcontainers - 1.1.8-SNAPSHOT + 1.1.9-SNAPSHOT ../pom.xml 4.0.0 From dc70d1a8d416fb0e935d43bdac7775d8c7329d1c Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 22 Jan 2017 22:06:57 +0000 Subject: [PATCH 0036/5615] Reinstate Google Analytics tracking for docs site --- docs/template.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/template.html b/docs/template.html index 4c391ce995c..fa439f2064a 100644 --- a/docs/template.html +++ b/docs/template.html @@ -18,7 +18,7 @@

{{NAME}} :: From 6e50e62295336bf39b88ccd9aff9fec260cfd846 Mon Sep 17 00:00:00 2001 From: Pavel Derendyaev Date: Fri, 3 Feb 2017 18:21:39 +0300 Subject: [PATCH 0037/5615] Add ability to override DockerClientFactory.TINY_IMAGE in testcontainers.properties --- core/src/main/java/org/testcontainers/DockerClientFactory.java | 3 ++- .../testcontainers/utility/TestcontainersConfiguration.java | 2 ++ docs/usage.md | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 26facdfbec7..5d1b17d703c 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -12,6 +12,7 @@ import lombok.Synchronized; import lombok.extern.slf4j.Slf4j; import org.testcontainers.dockerclient.*; +import org.testcontainers.utility.TestcontainersConfiguration; import java.util.List; import java.util.Optional; @@ -28,7 +29,7 @@ @Slf4j public class DockerClientFactory { - private static final String TINY_IMAGE = "alpine:3.2"; + private static final String TINY_IMAGE = TestcontainersConfiguration.getInstance().getTinyImage(); private static DockerClientFactory instance; // Cached client configuration diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 560a98e7984..6bc1c4d7ec3 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -24,6 +24,7 @@ public class TestcontainersConfiguration { private String ambassadorContainerImage = "richnorth/ambassador:latest"; private String vncRecordedContainerImage = "richnorth/vnc-recorder:latest"; + private String tinyImage = "alpine:3.2"; private static TestcontainersConfiguration loadConfiguration() { final TestcontainersConfiguration config = new TestcontainersConfiguration(); @@ -42,6 +43,7 @@ private static TestcontainersConfiguration loadConfiguration() { config.ambassadorContainerImage = properties.getProperty("ambassador.container.image", config.ambassadorContainerImage); config.vncRecordedContainerImage = properties.getProperty("vncrecorder.container.image", config.vncRecordedContainerImage); + config.tinyImage = properties.getProperty("tinyimage.container.image", config.vncRecordedContainerImage); log.debug("Testcontainers configuration overrides loaded from {}: {}", configOverrides, config); diff --git a/docs/usage.md b/docs/usage.md index 0569a45c9d5..f6b381c6c8d 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -109,9 +109,12 @@ Testcontainers uses additional docker images under some modes of execution: > *N.B.:* both these images use the 'latest' tag, which could potentially affect repeatability of tests and compatibility with Testcontainers _if the image is ever changed_. This is a [known issue](https://github.com/testcontainers/testcontainers-java/issues/276) which will be addressed in the future. The current 'latest' version of these images will never be changed until they are replaced by a new image altogether. +Last but not least, `alpine:3.2` image is used for Docker host IP address detection in some special cases. + If it is necessary to override these image names (e.g. when using a private registry), you should create a file named `testcontainers.properties` and place it on the classpath with the following content: ```properties ambassador.container.image=replacement image name here vncrecorder.container.image=replacement image name here +tinyimage.container.image=replacement image name here ``` From c4a30d89e62b2b925acd075477f2102ad4f58479 Mon Sep 17 00:00:00 2001 From: Pavel Derendyaev Date: Fri, 3 Feb 2017 18:32:34 +0300 Subject: [PATCH 0038/5615] Typofix --- .../org/testcontainers/utility/TestcontainersConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 6bc1c4d7ec3..c969b08d4f6 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -43,7 +43,7 @@ private static TestcontainersConfiguration loadConfiguration() { config.ambassadorContainerImage = properties.getProperty("ambassador.container.image", config.ambassadorContainerImage); config.vncRecordedContainerImage = properties.getProperty("vncrecorder.container.image", config.vncRecordedContainerImage); - config.tinyImage = properties.getProperty("tinyimage.container.image", config.vncRecordedContainerImage); + config.tinyImage = properties.getProperty("tinyimage.container.image", config.tinyImage); log.debug("Testcontainers configuration overrides loaded from {}: {}", configOverrides, config); From c2c6576fc30b08aec3c7388766819bd2b31f9e66 Mon Sep 17 00:00:00 2001 From: Peter Bollen Date: Tue, 7 Feb 2017 13:21:42 +0100 Subject: [PATCH 0039/5615] add support for withVolumesFrom --- .../testcontainers/containers/Container.java | 9 +++++++++ .../containers/GenericContainer.java | 20 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/core/src/main/java/org/testcontainers/containers/Container.java b/core/src/main/java/org/testcontainers/containers/Container.java index 1bdd6933835..e32cf7103e8 100644 --- a/core/src/main/java/org/testcontainers/containers/Container.java +++ b/core/src/main/java/org/testcontainers/containers/Container.java @@ -129,6 +129,15 @@ public String getStderr() { */ SELF withFileSystemBind(String hostPath, String containerPath, BindMode mode); + /** + * Adds container volumes. + * + * @param otherContainer the other container object to add volumes from + * @param mode the bind mode + * @return this + */ + SELF withVolumesFrom(String otherContainer, BindMode mode); + /** * Set the ports that this container listens on * diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index dde57985759..05551a137c6 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -94,6 +94,9 @@ public class GenericContainer> @NonNull private boolean privilegedMode; + @NonNull + private List volumesFroms = new ArrayList<>(); + @NonNull private Map linkedContainers = new HashMap<>(); @@ -344,6 +347,10 @@ private void applyConfiguration(CreateContainerCmd createCommand) { .toArray(Bind[]::new); createCommand.withBinds(bindsArray); + VolumesFrom[] volumesFromsArray = volumesFroms.stream() + .toArray(VolumesFrom[]::new); + createCommand.withVolumesFrom(volumesFromsArray); + Set allLinks = new HashSet<>(); Set allLinkedContainerNetworks = new HashSet<>(); for (Map.Entry linkEntries : linkedContainers.entrySet()) { @@ -494,6 +501,19 @@ public SELF withFileSystemBind(String hostPath, String containerPath, BindMode m return self(); } + /** + * {@inheritDoc} + */ + @Override + public SELF withVolumesFrom(String otherContainer, BindMode mode) { + addVolumesFrom(otherContainer, mode); + return self(); + } + + private void addVolumesFrom(String otherContainer, BindMode mode) { + volumesFroms.add(new VolumesFrom(otherContainer, mode.accessMode)); + } + @Override public void addLink(LinkableContainer otherContainer, String alias) { this.linkedContainers.put(alias, otherContainer); From 9cefeb7d41553048ff6fbc49189ee15934fa6843 Mon Sep 17 00:00:00 2001 From: Peter Bollen Date: Tue, 7 Feb 2017 13:45:28 +0100 Subject: [PATCH 0040/5615] implemented feedback --- .../java/org/testcontainers/containers/Container.java | 4 ++-- .../org/testcontainers/containers/GenericContainer.java | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/Container.java b/core/src/main/java/org/testcontainers/containers/Container.java index e32cf7103e8..002d5163acd 100644 --- a/core/src/main/java/org/testcontainers/containers/Container.java +++ b/core/src/main/java/org/testcontainers/containers/Container.java @@ -132,11 +132,11 @@ public String getStderr() { /** * Adds container volumes. * - * @param otherContainer the other container object to add volumes from + * @param container the container to add volumes from * @param mode the bind mode * @return this */ - SELF withVolumesFrom(String otherContainer, BindMode mode); + SELF withVolumesFrom(Container container, BindMode mode); /** * Set the ports that this container listens on diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 05551a137c6..4f1d45f9c44 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -505,13 +505,13 @@ public SELF withFileSystemBind(String hostPath, String containerPath, BindMode m * {@inheritDoc} */ @Override - public SELF withVolumesFrom(String otherContainer, BindMode mode) { - addVolumesFrom(otherContainer, mode); + public SELF withVolumesFrom(Container container, BindMode mode) { + addVolumesFrom(container, mode); return self(); } - private void addVolumesFrom(String otherContainer, BindMode mode) { - volumesFroms.add(new VolumesFrom(otherContainer, mode.accessMode)); + private void addVolumesFrom(Container container, BindMode mode) { + volumesFroms.add(new VolumesFrom(container.getContainerName(), mode.accessMode)); } @Override From 25e4be1aef63161c6a2a1b2a9e5e7ba30f79d996 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Sat, 11 Feb 2017 16:44:01 +0100 Subject: [PATCH 0041/5615] Fix UnsatisfiedLinkError when used on platform without glibc In case of an UnsatisfiedLinkError (which will happen if needed system libraries are missing) when trying to access Docker via Unix socket, a proxied Unix socket is used as a fallback on Linux instead. --- .travis.yml | 9 +++++++++ .../java/org/testcontainers/DockerClientFactory.java | 2 +- ...vironmentAndSystemPropertyClientProviderStrategy.java | 2 +- .../ProxiedUnixSocketClientProviderStrategy.java | 7 ++++--- .../dockerclient/UnixSocketClientProviderStrategy.java | 2 +- 5 files changed, 16 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3c9ea15ebc9..660786861ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,6 +41,15 @@ script: -w "$(pwd)" \ openjdk:8-jre \ ./mvnw -B -pl core test -Dtest=*GenericContainerRuleTest + # Run Docker-in-Docker tests inside Alpine + - | + DOCKER_HOST=unix:///var/run/docker.sock DOCKER_TLS_VERIFY= docker run --rm \ + -v "$HOME/.m2":/root/.m2/ \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v "$(pwd)":"$(pwd)" \ + -w "$(pwd)" \ + joepjoosten/openjdk-alpine-bash \ + ./mvnw -B -pl core test -Dtest=*GenericContainerRuleTest - mvn -B test -f shade-test/pom.xml cache: diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 5d1b17d703c..ac769dc384e 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -38,8 +38,8 @@ public class DockerClientFactory { private static final List CONFIGURATION_STRATEGIES = asList(new EnvironmentAndSystemPropertyClientProviderStrategy(), - new ProxiedUnixSocketClientProviderStrategy(), new UnixSocketClientProviderStrategy(), + new ProxiedUnixSocketClientProviderStrategy(), new DockerMachineClientProviderStrategy()); private String activeApiVersion; private String activeExecutionDriver; diff --git a/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java index 918f341f6b8..ed482abc36b 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java @@ -21,7 +21,7 @@ public void test() throws InvalidConfigurationException { final int timeout = Integer.parseInt(System.getProperty(PING_TIMEOUT_PROPERTY_NAME, PING_TIMEOUT_DEFAULT)); ping(client, timeout); - } catch (Exception e) { + } catch (Exception | UnsatisfiedLinkError e) { LOGGER.error("ping failed with configuration {} due to {}", getDescription(), e.toString(), e); throw new InvalidConfigurationException("ping failed"); } diff --git a/core/src/main/java/org/testcontainers/dockerclient/ProxiedUnixSocketClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/ProxiedUnixSocketClientProviderStrategy.java index 024ebf1e124..7036e048b34 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/ProxiedUnixSocketClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/ProxiedUnixSocketClientProviderStrategy.java @@ -9,8 +9,9 @@ public class ProxiedUnixSocketClientProviderStrategy extends UnixSocketClientPro @Override public void test() throws InvalidConfigurationException { - if (!System.getProperty("os.name").toLowerCase().contains("mac")) { - throw new InvalidConfigurationException("this strategy is only applicable to OS X"); + String osName = System.getProperty("os.name").toLowerCase(); + if (!osName.contains("mac") && !osName.contains("linux")) { + throw new InvalidConfigurationException("this strategy is only applicable to OS X and Linux"); } TcpToUnixSocketProxy proxy = new TcpToUnixSocketProxy(new File(DOCKER_SOCK_PATH)); @@ -20,7 +21,7 @@ public void test() throws InvalidConfigurationException { config = tryConfiguration("tcp://localhost:" + proxyPort); - LOGGER.info("Accessing Docker for Mac unix domain socket via TCP proxy (" + DOCKER_SOCK_PATH + " via localhost:" + proxyPort + ")"); + LOGGER.info("Accessing unix domain socket via TCP proxy (" + DOCKER_SOCK_PATH + " via localhost:" + proxyPort + ")"); } catch (Exception e) { proxy.stop(); diff --git a/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java index 99f5153f35b..e4aac02e6ed 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java @@ -28,7 +28,7 @@ public void test() try { config = tryConfiguration(SOCKET_LOCATION); LOGGER.info("Accessing docker with local Unix socket"); - } catch (Exception e) { + } catch (Exception | UnsatisfiedLinkError e) { throw new InvalidConfigurationException("ping failed", e); } } From 99e58a813d0d8b91e92354c72c0ed3172b864971 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Sat, 11 Feb 2017 17:45:14 +0100 Subject: [PATCH 0042/5615] Limit mac specific HostPortWaitStrategy behaviour to macs The deciding factor of the HostPortWaitStrategy is the OS name and not the used DockerClientProviderStrategy. However please note, that the existing mac workaround might not work if using images that don't contain bash or sh. --- .../testcontainers/containers/wait/HostPortWaitStrategy.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java index 743432eaeda..93e79152237 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java @@ -33,7 +33,7 @@ protected void waitUntilReady() { Callable checkStrategy; // Special case for Docker for Mac, see #160 - if (DockerClientFactory.instance().isUsing(ProxiedUnixSocketClientProviderStrategy.class)) { + if (System.getProperty("os.name").toLowerCase().contains("mac")) { List exposedPorts = container.getExposedPorts(); Integer exposedPort = exposedPorts.stream() From 9c4eab9b41e57714862abfc30ed3a3e8405b17de Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Sat, 11 Feb 2017 19:39:47 +0100 Subject: [PATCH 0043/5615] Change HostPortWaitStrategy strategy check to check for mac and socket proxy --- .../containers/wait/HostPortWaitStrategy.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java index 93e79152237..77fa9a78e70 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java @@ -33,7 +33,7 @@ protected void waitUntilReady() { Callable checkStrategy; // Special case for Docker for Mac, see #160 - if (System.getProperty("os.name").toLowerCase().contains("mac")) { + if (isUsingSocketProxyOnMac()) { List exposedPorts = container.getExposedPorts(); Integer exposedPort = exposedPorts.stream() @@ -90,4 +90,9 @@ protected void waitUntilReady() { container.getContainerIpAddress() + ":" + port + " should be listening)"); } } + + private boolean isUsingSocketProxyOnMac() { + return DockerClientFactory.instance().isUsing(ProxiedUnixSocketClientProviderStrategy.class) + && System.getProperty("os.name").toLowerCase().contains("mac"); + } } From 5ae17f7fd5a200082d70208b75cfca6d99622cab Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Sun, 12 Feb 2017 14:00:23 +0100 Subject: [PATCH 0044/5615] Change travis to use tagged Alpine image for integration tests --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 660786861ca..f357a00287c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -48,7 +48,7 @@ script: -v /var/run/docker.sock:/var/run/docker.sock \ -v "$(pwd)":"$(pwd)" \ -w "$(pwd)" \ - joepjoosten/openjdk-alpine-bash \ + kiview/openjdk-alpine-bash:8u111 \ ./mvnw -B -pl core test -Dtest=*GenericContainerRuleTest - mvn -B test -f shade-test/pom.xml From 7571b868ab78e6f2360d9ceeaa8a1eebd27cc6fb Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 12 Feb 2017 20:24:56 +0000 Subject: [PATCH 0045/5615] Update changelog for 1.1.9 release --- CHANGELOG.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16695da46e0..4cfe609a8a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,18 @@ # Change Log All notable changes to this project will be documented in this file. +## [1.1.9] - 2017-02-12 +### Fixed +- Fix inability to run Testcontainers on Alpine linux. Unix-socket-over-TCP is now used in linux environments where netty fails due to lack of glibc libraries (#290) +- Fix slow feedback in the case of missing JDBC drivers by failing-fast if the required driver cannot be found (#280, #230) + +### Changed +- Add ability to change 'tiny image' used for disk space checks (#287) +- Add ability to attach volumes to a container using 'volumes from' (#244, #289) + ## [1.1.8] - 2017-01-22 ### Fixed -- Comaptibility fixes for Docker for Mac v1.13.0 (#272) +- Compatibility fixes for Docker for Mac v1.13.0 (#272) - Relax docker environment disk space check to accomodate unusual empty `df` output observed on Docker for Mac with OverlayFS (#273, #278) - Fix inadvertent private-scoping of startup checks' `StartupStatus`, which made implementation of custom startup checks impossible (#266) - Fix potential resource lead/deadlock when errors are encountered building images from a Dockerfile (#274) From cb4fc7302132d098da568642f41053a851849208 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 12 Feb 2017 20:25:24 +0000 Subject: [PATCH 0046/5615] Update release version number to 1.1.9 --- core/pom.xml | 2 +- modules/jdbc-test/pom.xml | 2 +- modules/jdbc/pom.xml | 2 +- modules/mariadb/pom.xml | 2 +- modules/mysql/pom.xml | 2 +- modules/nginx/pom.xml | 2 +- modules/oracle-xe/pom.xml | 2 +- modules/postgresql/pom.xml | 2 +- modules/selenium/pom.xml | 2 +- modules/virtuoso/pom.xml | 2 +- pom.xml | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index f5c2c772ce0..f2be6c771f7 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -6,7 +6,7 @@ org.testcontainers testcontainers-parent - 1.1.9-SNAPSHOT + 1.1.9 ../pom.xml diff --git a/modules/jdbc-test/pom.xml b/modules/jdbc-test/pom.xml index 96fd5c721ca..4cb6517fa39 100644 --- a/modules/jdbc-test/pom.xml +++ b/modules/jdbc-test/pom.xml @@ -6,7 +6,7 @@ org.testcontainers testcontainers-parent - 1.1.9-SNAPSHOT + 1.1.9 ../../pom.xml diff --git a/modules/jdbc/pom.xml b/modules/jdbc/pom.xml index 8afd3bc04ba..2b2159db050 100644 --- a/modules/jdbc/pom.xml +++ b/modules/jdbc/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.9-SNAPSHOT + 1.1.9 ../../pom.xml diff --git a/modules/mariadb/pom.xml b/modules/mariadb/pom.xml index 54c382de56e..1ed4983814e 100644 --- a/modules/mariadb/pom.xml +++ b/modules/mariadb/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.9-SNAPSHOT + 1.1.9 ../../pom.xml diff --git a/modules/mysql/pom.xml b/modules/mysql/pom.xml index b97dd0e2586..ca3748d76a5 100644 --- a/modules/mysql/pom.xml +++ b/modules/mysql/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.9-SNAPSHOT + 1.1.9 ../../pom.xml diff --git a/modules/nginx/pom.xml b/modules/nginx/pom.xml index 9cf89e200d9..30481fd3b49 100644 --- a/modules/nginx/pom.xml +++ b/modules/nginx/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.9-SNAPSHOT + 1.1.9 ../../pom.xml diff --git a/modules/oracle-xe/pom.xml b/modules/oracle-xe/pom.xml index 945993ac74a..a83e143b4bd 100644 --- a/modules/oracle-xe/pom.xml +++ b/modules/oracle-xe/pom.xml @@ -8,7 +8,7 @@ org.testcontainers testcontainers-parent - 1.1.9-SNAPSHOT + 1.1.9 ../../pom.xml diff --git a/modules/postgresql/pom.xml b/modules/postgresql/pom.xml index 79c6e379def..328e9241619 100644 --- a/modules/postgresql/pom.xml +++ b/modules/postgresql/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.9-SNAPSHOT + 1.1.9 ../../pom.xml diff --git a/modules/selenium/pom.xml b/modules/selenium/pom.xml index b405d371fa8..bea563c2d3c 100644 --- a/modules/selenium/pom.xml +++ b/modules/selenium/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.9-SNAPSHOT + 1.1.9 ../../pom.xml diff --git a/modules/virtuoso/pom.xml b/modules/virtuoso/pom.xml index dd9e3cdd1cb..30127a707ec 100644 --- a/modules/virtuoso/pom.xml +++ b/modules/virtuoso/pom.xml @@ -4,7 +4,7 @@ org.testcontainers testcontainers-parent - 1.1.9-SNAPSHOT + 1.1.9 ../../pom.xml diff --git a/pom.xml b/pom.xml index 460d26da7dd..7e67746dde4 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.9-SNAPSHOT + 1.1.9 pom From a65b293f4382496f6ed136cd8172dcfd2e2f7b02 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 12 Feb 2017 21:07:13 +0000 Subject: [PATCH 0047/5615] Update development version number to 1.1.10-SNAPSHOT --- core/pom.xml | 2 +- modules/jdbc-test/pom.xml | 2 +- modules/jdbc/pom.xml | 2 +- modules/mariadb/pom.xml | 2 +- modules/mysql/pom.xml | 2 +- modules/nginx/pom.xml | 2 +- modules/oracle-xe/pom.xml | 2 +- modules/postgresql/pom.xml | 2 +- modules/selenium/pom.xml | 2 +- modules/virtuoso/pom.xml | 2 +- pom.xml | 2 +- shade-test/clashing-deps-jackson/pom.xml | 2 +- shade-test/clashing-deps-jersey/pom.xml | 2 +- shade-test/pom.xml | 2 +- shade-test/service-lookup-dropwizard/pom.xml | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index f2be6c771f7..ddcb7beb81a 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -6,7 +6,7 @@ org.testcontainers testcontainers-parent - 1.1.9 + 1.1.10-SNAPSHOT ../pom.xml diff --git a/modules/jdbc-test/pom.xml b/modules/jdbc-test/pom.xml index 4cb6517fa39..1fed27a6193 100644 --- a/modules/jdbc-test/pom.xml +++ b/modules/jdbc-test/pom.xml @@ -6,7 +6,7 @@ org.testcontainers testcontainers-parent - 1.1.9 + 1.1.10-SNAPSHOT ../../pom.xml diff --git a/modules/jdbc/pom.xml b/modules/jdbc/pom.xml index 2b2159db050..e54f149d206 100644 --- a/modules/jdbc/pom.xml +++ b/modules/jdbc/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.9 + 1.1.10-SNAPSHOT ../../pom.xml diff --git a/modules/mariadb/pom.xml b/modules/mariadb/pom.xml index 1ed4983814e..c46873bf5a1 100644 --- a/modules/mariadb/pom.xml +++ b/modules/mariadb/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.9 + 1.1.10-SNAPSHOT ../../pom.xml diff --git a/modules/mysql/pom.xml b/modules/mysql/pom.xml index ca3748d76a5..98abbbee234 100644 --- a/modules/mysql/pom.xml +++ b/modules/mysql/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.9 + 1.1.10-SNAPSHOT ../../pom.xml diff --git a/modules/nginx/pom.xml b/modules/nginx/pom.xml index 30481fd3b49..2b79b06911b 100644 --- a/modules/nginx/pom.xml +++ b/modules/nginx/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.9 + 1.1.10-SNAPSHOT ../../pom.xml diff --git a/modules/oracle-xe/pom.xml b/modules/oracle-xe/pom.xml index a83e143b4bd..5af1da1cbee 100644 --- a/modules/oracle-xe/pom.xml +++ b/modules/oracle-xe/pom.xml @@ -8,7 +8,7 @@ org.testcontainers testcontainers-parent - 1.1.9 + 1.1.10-SNAPSHOT ../../pom.xml diff --git a/modules/postgresql/pom.xml b/modules/postgresql/pom.xml index 328e9241619..ef5fcd55ab9 100644 --- a/modules/postgresql/pom.xml +++ b/modules/postgresql/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.9 + 1.1.10-SNAPSHOT ../../pom.xml diff --git a/modules/selenium/pom.xml b/modules/selenium/pom.xml index bea563c2d3c..4cadc2de3a0 100644 --- a/modules/selenium/pom.xml +++ b/modules/selenium/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.9 + 1.1.10-SNAPSHOT ../../pom.xml diff --git a/modules/virtuoso/pom.xml b/modules/virtuoso/pom.xml index 30127a707ec..dd9a09b9387 100644 --- a/modules/virtuoso/pom.xml +++ b/modules/virtuoso/pom.xml @@ -4,7 +4,7 @@ org.testcontainers testcontainers-parent - 1.1.9 + 1.1.10-SNAPSHOT ../../pom.xml diff --git a/pom.xml b/pom.xml index 7e67746dde4..6aaf11fc59a 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.9 + 1.1.10-SNAPSHOT pom diff --git a/shade-test/clashing-deps-jackson/pom.xml b/shade-test/clashing-deps-jackson/pom.xml index f0d7b8e8888..4cd5a28b821 100644 --- a/shade-test/clashing-deps-jackson/pom.xml +++ b/shade-test/clashing-deps-jackson/pom.xml @@ -5,7 +5,7 @@ shade-test org.testcontainers - 1.1.9-SNAPSHOT + 1.1.10-SNAPSHOT ../pom.xml 4.0.0 diff --git a/shade-test/clashing-deps-jersey/pom.xml b/shade-test/clashing-deps-jersey/pom.xml index 79db89c52d3..79e658be862 100644 --- a/shade-test/clashing-deps-jersey/pom.xml +++ b/shade-test/clashing-deps-jersey/pom.xml @@ -5,7 +5,7 @@ shade-test org.testcontainers - 1.1.9-SNAPSHOT + 1.1.10-SNAPSHOT ../pom.xml 4.0.0 diff --git a/shade-test/pom.xml b/shade-test/pom.xml index c2625cbf8e5..8d66ff943d8 100644 --- a/shade-test/pom.xml +++ b/shade-test/pom.xml @@ -5,7 +5,7 @@ testcontainers-parent org.testcontainers - 1.1.9-SNAPSHOT + 1.1.10-SNAPSHOT ../pom.xml 4.0.0 diff --git a/shade-test/service-lookup-dropwizard/pom.xml b/shade-test/service-lookup-dropwizard/pom.xml index f2c1fd36b39..ea81476c706 100644 --- a/shade-test/service-lookup-dropwizard/pom.xml +++ b/shade-test/service-lookup-dropwizard/pom.xml @@ -5,7 +5,7 @@ shade-test org.testcontainers - 1.1.9-SNAPSHOT + 1.1.10-SNAPSHOT ../pom.xml 4.0.0 From b38709c6eb58b092eb50e8bd69905fefcf75c1ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Ko=C5=82odziejczyk?= Date: Fri, 17 Feb 2017 10:27:32 +0100 Subject: [PATCH 0048/5615] changed shadowing scope of guava packages (#292) --- core/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index ddcb7beb81a..db91099b2a0 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -149,8 +149,8 @@ org.testcontainers.shaded.jersey.repackaged - com.google.common - org.testcontainers.shaded.com.google.common + com.google + org.testcontainers.shaded.com.google io.netty From 90e8f6c7c271de986d16ec99b55d8550a80b714c Mon Sep 17 00:00:00 2001 From: peter-evans Date: Sat, 18 Feb 2017 18:52:22 +0900 Subject: [PATCH 0049/5615] Fixed broken links --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2da77753005..33ba2bc390b 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ The code in this repository accompanies the following blog posts: -* [JUnit integration testing with Docker and Testcontainers](https://rnorth.org/24/testcontainers-example-testing-a-redis-backed-cache) -* [Fun with Disque, Java and Spinach](https://rnorth.org/25/fun-with-disque-java-and-spinach) -* [Better JUnit Selenium testing with Docker and Testcontainers](https://rnorth.org/26/better-junit-selenium-testing-with-docker-and-testcontainers) +* [JUnit integration testing with Docker and Testcontainers](https://rnorth.org/junit-integration-testing-with-docker-and-testcontainers) +* [Fun with Disque, Java and Spinach](https://rnorth.org/fun-with-disque-java-and-spinach) +* [Better JUnit Selenium testing with Docker and Testcontainers](https://rnorth.org/better-junit-selenium-testing-with-docker-and-testcontainers) Note that this examples project uses JitPack to obtain SNAPSHOT versions of the latest build of TestContainers. For normal usage, refer to the [documentation](http://testcontainers.viewdocs.io/testcontainers-java/#user-content-maven-dependencies) From fc61541afa7de3d26fcbbd9c57119f895e8ae434 Mon Sep 17 00:00:00 2001 From: peter-evans Date: Wed, 1 Mar 2017 10:57:14 +0900 Subject: [PATCH 0050/5615] Corrected depreciated method in the docs --- docs/usage/generic_containers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/usage/generic_containers.md b/docs/usage/generic_containers.md index 95711fbd683..f5748284586 100644 --- a/docs/usage/generic_containers.md +++ b/docs/usage/generic_containers.md @@ -40,10 +40,10 @@ tests have run. The class rule provides methods for discovering how your tests can interact with the containers: -* `getIpAddress()` returns the IP address where the container is listening +* `getContainerIpAddress()` returns the IP address where the container is listening * `getMappedPort(...)` returns the Docker mapped port for a port that has been exposed on the container For example, with the Redis example above, the following will allow your tests to access the Redis service: ```java -String redisUrl = redis.getIpAddress() + ":" + redis.getMappedPort(6379); +String redisUrl = redis.getContainerIpAddress() + ":" + redis.getMappedPort(6379); ``` \ No newline at end of file From 7b8568240f9d79b7f1bc7f6c0b5396d31baaa0da Mon Sep 17 00:00:00 2001 From: peter-evans Date: Wed, 1 Mar 2017 11:21:28 +0900 Subject: [PATCH 0051/5615] Corrected depreciated method in the examples --- disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java | 2 +- disque-job-queue/src/test/java/SingleDisqueInstanceTest.java | 2 +- redis-backed-cache/src/test/java/RedisBackedCacheTest.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java b/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java index 49302241afa..5c78b3fdbd0 100644 --- a/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java +++ b/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java @@ -29,7 +29,7 @@ public class DisqueBackedMailSenderTest { @Before public void setup() { context(""); - disqueClient = new DisqueClient(DisqueURI.create(container.getIpAddress(), container.getMappedPort(7711))); + disqueClient = new DisqueClient(DisqueURI.create(container.getContainerIpAddress(), container.getMappedPort(7711))); mockMailApiClient = mock(MailApiClient.class); service = new DisqueBackedMailSenderService(disqueClient, mockMailApiClient); diff --git a/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java b/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java index 192e5a49fe1..8137f8fae90 100644 --- a/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java +++ b/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java @@ -26,7 +26,7 @@ public class SingleDisqueInstanceTest { @Before public void setup() { context(""); - DisqueClient client = new DisqueClient(DisqueURI.create(container.getIpAddress(), container.getMappedPort(7711))); + DisqueClient client = new DisqueClient(DisqueURI.create(container.getContainerIpAddress(), container.getMappedPort(7711))); connection = client.connect().sync(); retryAfter1Second = AddJobArgs.builder().retry(1, TimeUnit.SECONDS).build(); diff --git a/redis-backed-cache/src/test/java/RedisBackedCacheTest.java b/redis-backed-cache/src/test/java/RedisBackedCacheTest.java index 87bec6c258e..eaff293e518 100644 --- a/redis-backed-cache/src/test/java/RedisBackedCacheTest.java +++ b/redis-backed-cache/src/test/java/RedisBackedCacheTest.java @@ -22,7 +22,7 @@ public class RedisBackedCacheTest { @Before public void setUp() throws Exception { - Jedis jedis = new Jedis(redis.getIpAddress(), redis.getMappedPort(6379)); + Jedis jedis = new Jedis(redis.getContainerIpAddress(), redis.getMappedPort(6379)); cache = new RedisBackedCache(jedis, "test"); } From 3c0d2ca76dbb8dee71f67a27aba27b253bbed481 Mon Sep 17 00:00:00 2001 From: Simone Scarduzio Date: Sun, 26 Feb 2017 11:57:02 +0100 Subject: [PATCH 0052/5615] Elasticsearch goes in jar hell for org.newsclub.net.unix.AFUNIXSocket --- core/pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/pom.xml b/core/pom.xml index db91099b2a0..9fa5bd99343 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -160,6 +160,10 @@ org.bouncycastle org.testcontainers.shaded.org.bouncycastle + + org.newsclub + org.testcontainers.shaded.org.newsclub + @@ -173,6 +177,7 @@ com.google.guava:* io.netty:* org.bouncycastle:* + org.newsclub.*:* true From 1bb0a1b509620e406a25d6d65230c6cef432c61e Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 21 Feb 2017 12:38:29 +0200 Subject: [PATCH 0053/5615] add "Who is using TestContainers?" to the docs --- docs/index.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/index.md b/docs/index.md index e064eeb487f..db99e34c2ef 100644 --- a/docs/index.md +++ b/docs/index.md @@ -15,6 +15,14 @@ TestContainers makes it easy to launch useful Docker containers for the duration * **Application integration tests**: for running your application in a short-lived test mode with dependencies, such as databases, message queues or web servers. * **UI/Acceptance tests**: use containerized web browsers, compatible with Selenium, for conducting automated UI tests. Each test can get a fresh instance of the browser, with no browser state, plugin variations or automated browser upgrades to worry about. And you get a video recording of each test session, or just each session where tests failed. +## Who is using TestContainers? + + * [ZeroTurnaround](https://zeroturnaround.com) - Testing of the Java Agents, micro-services, Selenium browser automation + * [Zipkin](http://zipkin.io) - MySQL and Cassandra testing + * [Apache Gora](https://gora.apache.org) - CouchDB testing + * [Apache James](https://james.apache.org) - LDAP and Cassandra integration testing + * [StreamSets](https://github.com/streamsets/datacollector) - LDAP, MySQL Vault, MongoDB, Redis integration testing + ## License See [LICENSE](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/LICENSE). From 8c14b9974c73e15e0cc64f9a1fd2498ae298c7fd Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 20 Feb 2017 14:33:06 +0200 Subject: [PATCH 0054/5615] Do not run "docker ps -a" in ResourceReaper to avoid TooLongFrameException from Netty --- .../java/org/testcontainers/utility/ResourceReaper.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index f00d11a3524..e9f10b76b2c 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -85,14 +85,6 @@ public void stopAndRemoveContainer(String containerId, String imageName) { } private void stopContainer(String containerId, String imageName) { - - List allContainers = dockerClient.listContainersCmd().withShowAll(true).exec(); - - if (allContainers.stream().map(Container::getId).noneMatch(containerId::equals)) { - LOGGER.trace("Was going to clean up container but it apparently no longer exists: {}"); - return; - } - boolean running; try { InspectContainerResponse containerInfo = dockerClient.inspectContainerCmd(containerId).exec(); From 935a2db9099a5d96fb79b7125235e74bcbc7cdb8 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 20 Feb 2017 12:12:43 +0200 Subject: [PATCH 0055/5615] mark all containers started by TestContainers with "org.testcontainers: true" label --- .../java/org/testcontainers/containers/GenericContainer.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 4f1d45f9c44..114a6329954 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -418,6 +418,8 @@ private void applyConfiguration(CreateContainerCmd createCommand) { if (privilegedMode) { createCommand.withPrivileged(privilegedMode); } + + createCommand.withLabels(Collections.singletonMap("org.testcontainers", "true")); } /** From 20b5b7e284d9b07ab187083088cfad173817f973 Mon Sep 17 00:00:00 2001 From: peter-evans Date: Thu, 2 Mar 2017 11:10:27 +0900 Subject: [PATCH 0056/5615] Fixed broken link in the docs --- docs/compatibility.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/compatibility.md b/docs/compatibility.md index aca1d8c3d12..f00a93a99b4 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -4,7 +4,7 @@ |-------------------------------------|-----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Linux - general | Docker v1.10 | | | Linux - Travis CI | Docker v1.10 | See [example .travis.yml](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/.travis.yml) for baseline Travis CI configuration | -| Linux - Circle CI (LXC driver) | Docker v1.9.1 | The `exec` feature is not compatible with Circle CI. See [example circle.yml](../circle.yml) for baseline CircleCI configuration | +| Linux - Circle CI (LXC driver) | Docker v1.9.1 | The `exec` feature is not compatible with Circle CI. See [example circle.yml](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/circle.yml) for baseline CircleCI configuration | | Linux - within a Docker container | Docker v1.12 | See [Running inside Docker](usage/inside_docker.md) for the detailed configuration | | Mac OS X - Docker Toolbox | Docker Machine v0.8.0 | | | Mac OS X - Docker for Mac | 1.12.0 | *Support is best-efforts at present*. `getTestHostIpAddress()` is [not currently supported](https://github.com/testcontainers/testcontainers-java/issues/166) due to limitations in Docker for Mac. | From 0bd0c2ff0f414f1f4af3f94a91ab4cb80d1adc05 Mon Sep 17 00:00:00 2001 From: Mihhail Lapushkin Date: Thu, 2 Mar 2017 15:37:02 +0200 Subject: [PATCH 0057/5615] Note about JDBC driver For me it did not work with Spring Boot until I changed the driver: spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver Not sure if this is needed in general. --- docs/usage/database_containers.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/usage/database_containers.md b/docs/usage/database_containers.md index e1ef557a595..7775e5c26f4 100644 --- a/docs/usage/database_containers.md +++ b/docs/usage/database_containers.md @@ -48,8 +48,8 @@ Examples/Tests: ### JDBC URL As long as you have TestContainers and the appropriate JDBC driver on your classpath, you can simply modify regular JDBC connection URLs to get a fresh containerized instance of the database each time your application starts up. - -_N.B: TC needs to be on your application's classpath at runtime for this to work_ +* _TC needs to be on your application's classpath at runtime for this to work_ +* _JDBC driver needs to be set to `org.testcontainers.jdbc.ContainerDatabaseDriver`_ **Original URL**: `jdbc:mysql://somehostname:someport/databasename` @@ -112,4 +112,4 @@ and will be able to override server settings when the container starts. VirtuosoContainer provides access to the SPARQL service URL ```java String sparqlServiceUrl = ((VirtuosoContainer)container).getSparqlUrl(); -``` \ No newline at end of file +``` From 932a60beb4d2b97f3b2299c01df89674bff65071 Mon Sep 17 00:00:00 2001 From: Mihhail Lapushkin Date: Thu, 2 Mar 2017 16:01:07 +0200 Subject: [PATCH 0058/5615] Seems like Spring Boot issue --- docs/usage/database_containers.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/usage/database_containers.md b/docs/usage/database_containers.md index 7775e5c26f4..9da5af91e81 100644 --- a/docs/usage/database_containers.md +++ b/docs/usage/database_containers.md @@ -48,8 +48,10 @@ Examples/Tests: ### JDBC URL As long as you have TestContainers and the appropriate JDBC driver on your classpath, you can simply modify regular JDBC connection URLs to get a fresh containerized instance of the database each time your application starts up. + +_N.B:_ * _TC needs to be on your application's classpath at runtime for this to work_ -* _JDBC driver needs to be set to `org.testcontainers.jdbc.ContainerDatabaseDriver`_ +* _For Spring Boot you need to specify the driver manually `spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver`_ **Original URL**: `jdbc:mysql://somehostname:someport/databasename` From e8bf32e2a661b5dd0cca60db126842adb0e3761d Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 22 Feb 2017 11:45:44 +0200 Subject: [PATCH 0059/5615] #291 Docker for Windows support over TCP --- .../testcontainers/DockerClientFactory.java | 3 +- ...NamedPipeSocketClientProviderStrategy.java | 41 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/org/testcontainers/dockerclient/NamedPipeSocketClientProviderStrategy.java diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index ac769dc384e..2cead2b8331 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -40,7 +40,8 @@ public class DockerClientFactory { asList(new EnvironmentAndSystemPropertyClientProviderStrategy(), new UnixSocketClientProviderStrategy(), new ProxiedUnixSocketClientProviderStrategy(), - new DockerMachineClientProviderStrategy()); + new DockerMachineClientProviderStrategy(), + new NamedPipeSocketClientProviderStrategy()); private String activeApiVersion; private String activeExecutionDriver; diff --git a/core/src/main/java/org/testcontainers/dockerclient/NamedPipeSocketClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/NamedPipeSocketClientProviderStrategy.java new file mode 100644 index 00000000000..85131d2944d --- /dev/null +++ b/core/src/main/java/org/testcontainers/dockerclient/NamedPipeSocketClientProviderStrategy.java @@ -0,0 +1,41 @@ +package org.testcontainers.dockerclient; + +import com.github.dockerjava.core.DefaultDockerClientConfig; +import com.github.dockerjava.core.DockerClientConfig; +import org.jetbrains.annotations.NotNull; + +import java.io.File; + +public class NamedPipeSocketClientProviderStrategy extends DockerClientProviderStrategy { + + private static final int PING_TIMEOUT_DEFAULT = 5; + private static final String PING_TIMEOUT_PROPERTY_NAME = "testcontainers.namedpipesocketprovider.timeout"; + + @Override + public void test() throws InvalidConfigurationException { + if (!new File("\\\\.\\pipe\\docker_engine").exists()) { + throw new InvalidConfigurationException("this strategy only works with Docker for Windows"); + } + + config = tryConfiguration("tcp://localhost:2375"); + } + + @Override + public String getDescription() { + return "Docker for Windows (via TCP port 2375)"; + } + + @NotNull + protected DockerClientConfig tryConfiguration(String dockerHost) { + config = DefaultDockerClientConfig.createDefaultConfigBuilder() + .withDockerHost(dockerHost) + .withDockerTlsVerify(false) + .build(); + client = getClientForConfig(config); + + final int timeout = Integer.getInteger(PING_TIMEOUT_PROPERTY_NAME, PING_TIMEOUT_DEFAULT); + ping(client, timeout); + + return config; + } +} From aaa1fda5f12a7100bfae68329c74d6d4dbb6c316 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 22 Feb 2017 11:48:08 +0200 Subject: [PATCH 0060/5615] #291 relax the condition --- .../main/java/org/testcontainers/DockerClientFactory.java | 2 +- ...erStrategy.java => WindowsClientProviderStrategy.java} | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) rename core/src/main/java/org/testcontainers/dockerclient/{NamedPipeSocketClientProviderStrategy.java => WindowsClientProviderStrategy.java} (79%) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 2cead2b8331..0e68cc87965 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -41,7 +41,7 @@ public class DockerClientFactory { new UnixSocketClientProviderStrategy(), new ProxiedUnixSocketClientProviderStrategy(), new DockerMachineClientProviderStrategy(), - new NamedPipeSocketClientProviderStrategy()); + new WindowsClientProviderStrategy()); private String activeApiVersion; private String activeExecutionDriver; diff --git a/core/src/main/java/org/testcontainers/dockerclient/NamedPipeSocketClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/WindowsClientProviderStrategy.java similarity index 79% rename from core/src/main/java/org/testcontainers/dockerclient/NamedPipeSocketClientProviderStrategy.java rename to core/src/main/java/org/testcontainers/dockerclient/WindowsClientProviderStrategy.java index 85131d2944d..fe4eed817c3 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/NamedPipeSocketClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/WindowsClientProviderStrategy.java @@ -4,19 +4,13 @@ import com.github.dockerjava.core.DockerClientConfig; import org.jetbrains.annotations.NotNull; -import java.io.File; - -public class NamedPipeSocketClientProviderStrategy extends DockerClientProviderStrategy { +public class WindowsClientProviderStrategy extends DockerClientProviderStrategy { private static final int PING_TIMEOUT_DEFAULT = 5; private static final String PING_TIMEOUT_PROPERTY_NAME = "testcontainers.namedpipesocketprovider.timeout"; @Override public void test() throws InvalidConfigurationException { - if (!new File("\\\\.\\pipe\\docker_engine").exists()) { - throw new InvalidConfigurationException("this strategy only works with Docker for Windows"); - } - config = tryConfiguration("tcp://localhost:2375"); } From b41a9f583a59fc6ef27e347a868592b50ca68b92 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 22 Feb 2017 11:59:01 +0200 Subject: [PATCH 0061/5615] Closes #291 change the docs --- docs/compatibility.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/compatibility.md b/docs/compatibility.md index f00a93a99b4..adb84beb976 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -9,4 +9,4 @@ | Mac OS X - Docker Toolbox | Docker Machine v0.8.0 | | | Mac OS X - Docker for Mac | 1.12.0 | *Support is best-efforts at present*. `getTestHostIpAddress()` is [not currently supported](https://github.com/testcontainers/testcontainers-java/issues/166) due to limitations in Docker for Mac. | | Windows - Docker Toolbox | | *Support is limited at present and this is not currently tested on a regular basis*. | -| Windows - Docker for Windows Beta | | *Not currently supported*. | +| Windows - Docker for Windows | | *Supported, but not currently tested on a regular basis.* | From 9b8207e9a2124a2301612929149867fe14642981 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 1 Mar 2017 22:48:19 +0200 Subject: [PATCH 0062/5615] Rename PING_TIMEOUT_PROPERTY_NAME and mention failing Docker Compose tests in the docs --- .../dockerclient/WindowsClientProviderStrategy.java | 2 +- docs/compatibility.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/testcontainers/dockerclient/WindowsClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/WindowsClientProviderStrategy.java index fe4eed817c3..15cd35c48bf 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/WindowsClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/WindowsClientProviderStrategy.java @@ -7,7 +7,7 @@ public class WindowsClientProviderStrategy extends DockerClientProviderStrategy { private static final int PING_TIMEOUT_DEFAULT = 5; - private static final String PING_TIMEOUT_PROPERTY_NAME = "testcontainers.namedpipesocketprovider.timeout"; + private static final String PING_TIMEOUT_PROPERTY_NAME = "testcontainers.windowsprovider.timeout"; @Override public void test() throws InvalidConfigurationException { diff --git a/docs/compatibility.md b/docs/compatibility.md index adb84beb976..79ebbd9d24a 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -9,4 +9,4 @@ | Mac OS X - Docker Toolbox | Docker Machine v0.8.0 | | | Mac OS X - Docker for Mac | 1.12.0 | *Support is best-efforts at present*. `getTestHostIpAddress()` is [not currently supported](https://github.com/testcontainers/testcontainers-java/issues/166) due to limitations in Docker for Mac. | | Windows - Docker Toolbox | | *Support is limited at present and this is not currently tested on a regular basis*. | -| Windows - Docker for Windows | | *Supported, but not currently tested on a regular basis.* | +| Windows - Docker for Windows | | *Supported is limited at present (Docker Compose tests are failing) and not currently tested on a regular basis.* | From 37bd0046fe5a44458b04f9c0c7b5deca5e9698ff Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Fri, 3 Mar 2017 21:39:15 -0600 Subject: [PATCH 0063/5615] Update to Spring Boot 1.5.2 - Uses @SpringBootTest - Uses TestRestTemplate which is aware of the local.server.port - spring-boot-starter-redis was renamed to spring-boot-starter-data-rest --- spring-boot/pom.xml | 8 ++++---- .../java/com/example/AbstractIntegrationTest.java | 14 ++++++-------- .../test/java/com/example/DemoApplicationTest.java | 13 +++++++------ 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/spring-boot/pom.xml b/spring-boot/pom.xml index 0594b26396c..f5bc4ee8833 100644 --- a/spring-boot/pom.xml +++ b/spring-boot/pom.xml @@ -21,7 +21,7 @@ org.springframework.boot spring-boot-dependencies - 1.3.5.RELEASE + 1.5.2.RELEASE pom import @@ -31,20 +31,20 @@ org.springframework.boot - spring-boot-starter-redis + spring-boot-starter-data-redis org.springframework.boot spring-boot-starter-web - + org.springframework.boot spring-boot-starter-test test - + diff --git a/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java b/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java index 3d2f54fc482..32b4b7b4ff0 100644 --- a/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java +++ b/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java @@ -2,18 +2,18 @@ import org.junit.ClassRule; import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.test.EnvironmentTestUtils; -import org.springframework.boot.test.SpringApplicationConfiguration; -import org.springframework.boot.test.WebIntegrationTest; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.testcontainers.containers.GenericContainer; @RunWith(SpringJUnit4ClassRunner.class) -@SpringApplicationConfiguration(value = DemoApplication.class, initializers = AbstractIntegrationTest.Initializer.class) -@WebIntegrationTest(randomPort = true) +@SpringBootTest(classes = DemoApplication.class,webEnvironment = WebEnvironment.RANDOM_PORT) +@ContextConfiguration(initializers = AbstractIntegrationTest.Initializer.class) public abstract class AbstractIntegrationTest { @ClassRule @@ -29,6 +29,4 @@ public void initialize(ConfigurableApplicationContext configurableApplicationCon } } - @Value("${local.server.port}") - protected int port; } diff --git a/spring-boot/src/test/java/com/example/DemoApplicationTest.java b/spring-boot/src/test/java/com/example/DemoApplicationTest.java index baa050195cd..edf1590e6b1 100644 --- a/spring-boot/src/test/java/com/example/DemoApplicationTest.java +++ b/spring-boot/src/test/java/com/example/DemoApplicationTest.java @@ -1,18 +1,19 @@ package com.example; -import org.junit.Test; -import org.springframework.web.client.RestTemplate; - - import static org.rnorth.visibleassertions.VisibleAssertions.*; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.web.client.TestRestTemplate; + public class DemoApplicationTest extends AbstractIntegrationTest { - RestTemplate restTemplate = new RestTemplate(); + @Autowired + TestRestTemplate restTemplate; @Test public void simpleTest() { - String fooResource = "http://localhost:" + port + "/foo"; + String fooResource = "/foo"; info("putting 'bar' to " + fooResource); restTemplate.put(fooResource, "bar"); From 8b309bbf38f361db8206ac4aca6848bff1a22ee1 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Fri, 3 Mar 2017 21:44:36 -0600 Subject: [PATCH 0064/5615] Ignore Eclipse Metadata --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index a6a7be722ff..c271c1e403f 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,9 @@ com_crashlytics_export_strings.xml crashlytics.properties crashlytics-build.properties +# Eclipse +.project +.classpath +.settings +bin + From a6e3e53ae5c54b40a1689276f436b47d7b0db443 Mon Sep 17 00:00:00 2001 From: peter-evans Date: Sun, 5 Mar 2017 02:43:15 -0800 Subject: [PATCH 0065/5615] Added an example for LinkableContainer using PostgreSQL and Redmine --- linked-container/pom.xml | 50 +++++++++++++++++++ .../linkedcontainer/RedmineClient.java | 32 ++++++++++++ .../linkedcontainer/RedmineClientTest.java | 41 +++++++++++++++ .../linkedcontainer/RedmineContainer.java | 49 ++++++++++++++++++ .../src/test/resources/logback-test.xml | 16 ++++++ pom.xml | 1 + 6 files changed, 189 insertions(+) create mode 100644 linked-container/pom.xml create mode 100644 linked-container/src/main/java/com/example/linkedcontainer/RedmineClient.java create mode 100644 linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java create mode 100644 linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java create mode 100644 linked-container/src/test/resources/logback-test.xml diff --git a/linked-container/pom.xml b/linked-container/pom.xml new file mode 100644 index 00000000000..010a56e0315 --- /dev/null +++ b/linked-container/pom.xml @@ -0,0 +1,50 @@ + + + + testcontainers-java-examples + org.testcontainers.examples + NOVERSION + + 4.0.0 + + linked-container + + + + ${testcontainers.group} + postgresql + ${testcontainers.version} + + + org.postgresql + postgresql + 9.4.1212 + test + + + org.slf4j + slf4j-api + 1.7.24 + provided + + + ch.qos.logback + logback-classic + 1.2.1 + test + + + com.squareup.okhttp3 + okhttp + 3.6.0 + + + org.json + json + 20160810 + + + + \ No newline at end of file diff --git a/linked-container/src/main/java/com/example/linkedcontainer/RedmineClient.java b/linked-container/src/main/java/com/example/linkedcontainer/RedmineClient.java new file mode 100644 index 00000000000..be62d932971 --- /dev/null +++ b/linked-container/src/main/java/com/example/linkedcontainer/RedmineClient.java @@ -0,0 +1,32 @@ +package com.example.linkedcontainer; + +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import org.json.JSONObject; + +import java.io.IOException; + +/** + * A crude, partially implemented Redmine client. + */ +public class RedmineClient { + + private String url; + private OkHttpClient client; + + public RedmineClient(String url) { + this.url = url; + client = new OkHttpClient(); + } + + public int getIssueCount() throws IOException { + Request request = new Request.Builder() + .url(url + "/issues.json") + .build(); + + Response response = client.newCall(request).execute(); + JSONObject jsonObject = new JSONObject(response.body().string()); + return jsonObject.getInt("total_count"); + } +} diff --git a/linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java b/linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java new file mode 100644 index 00000000000..9e79b1b042b --- /dev/null +++ b/linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java @@ -0,0 +1,41 @@ +package com.example.linkedcontainer; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.RuleChain; +import org.testcontainers.containers.PostgreSQLContainer; + +import static org.junit.Assert.assertEquals; + +/** + * Tests for RedmineClient. + */ +public class RedmineClientTest { + + private static final String POSTGRES_USERNAME = "redmine"; + private static final String POSTGRES_PASSWORD = "secret"; + + private PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer("postgres:9.6.2") + .withUsername(POSTGRES_USERNAME) + .withPassword(POSTGRES_PASSWORD); + + private RedmineContainer redmineContainer = new RedmineContainer("redmine:3.3.2") + .withLinkToContainer(postgreSQLContainer, "postgres") + .withEnv("POSTGRES_ENV_POSTGRES_USER", POSTGRES_USERNAME) + .withEnv("POSTGRES_ENV_POSTGRES_PASSWORD", POSTGRES_PASSWORD); + + @Rule + public RuleChain chain= RuleChain.outerRule(postgreSQLContainer) + .around(redmineContainer); + + @Test + public void canGetIssueCount() throws Exception { + RedmineClient redmineClient = new RedmineClient( + redmineContainer.getRedmineUrl()); + + assertEquals( + "The issue count can be retrieved.", + 0, + redmineClient.getIssueCount()); + } +} diff --git a/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java b/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java new file mode 100644 index 00000000000..dead067f67e --- /dev/null +++ b/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java @@ -0,0 +1,49 @@ +package com.example.linkedcontainer; + +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.traits.LinkableContainer; +import org.testcontainers.containers.wait.Wait; + +/** + * A linkable Redmine container. + */ +public class RedmineContainer> extends GenericContainer implements LinkableContainer { + + private static final String IMAGE = "redmine"; + private static final int REDMINE_PORT = 3000; + + public RedmineContainer() { + this(IMAGE + ":latest"); + } + + public RedmineContainer(String dockerImageName) { + super.setDockerImageName(dockerImageName); + } + + @Override + protected Integer getLivenessCheckPort() { + return getMappedPort(REDMINE_PORT); + } + + @Override + protected void configure() { + addExposedPort(REDMINE_PORT); + waitingFor(Wait.forHttp("/")); + } + + @Override + public SELF withEnv(String key, String value) { + return super.withEnv(key, value); + } + + public SELF withLinkToContainer(LinkableContainer otherContainer, String alias) { + addLink(otherContainer, alias); + return self(); + } + + public String getRedmineUrl() { + return String.format("http://%s:%d", + this.getContainerIpAddress(), + this.getMappedPort(REDMINE_PORT)); + } +} diff --git a/linked-container/src/test/resources/logback-test.xml b/linked-container/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..23f40bbec32 --- /dev/null +++ b/linked-container/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 1dcf9452d20..a691311778d 100644 --- a/pom.xml +++ b/pom.xml @@ -10,6 +10,7 @@ disque-job-queue selenium-container spring-boot + linked-container pom From 634401d519b273ebf94ed267e0b9daf104854239 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 1 Mar 2017 14:05:58 +0200 Subject: [PATCH 0066/5615] Expose low level API. Makes it possible to override unsupported CreateContainerCmd properties without waiting until TestContainers adds support for it. --- .../testcontainers/containers/Container.java | 20 ++++++++++++++++++ .../containers/GenericContainer.java | 15 ++++++++++++- .../junit/GenericContainerRuleTest.java | 21 +++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/Container.java b/core/src/main/java/org/testcontainers/containers/Container.java index 002d5163acd..b3c0a5298da 100644 --- a/core/src/main/java/org/testcontainers/containers/Container.java +++ b/core/src/main/java/org/testcontainers/containers/Container.java @@ -388,15 +388,35 @@ ExecResult execInContainer(Charset outputCharset, String... command) void setLinkedContainers(Map linkedContainers); + /** + * @deprecated set by GenericContainer and should never be set outside + */ + @Deprecated void setDockerClient(DockerClient dockerClient); + /** + * @deprecated set by GenericContainer and should never be set outside + */ + @Deprecated void setDockerDaemonInfo(Info dockerDaemonInfo); + /** + * @deprecated set by GenericContainer and should never be set outside + */ + @Deprecated void setContainerId(String containerId); + /** + * @deprecated set by GenericContainer and should never be set outside + */ + @Deprecated void setContainerName(String containerName); void setWaitStrategy(WaitStrategy waitStrategy); + /** + * @deprecated set by GenericContainer and should never be set outside + */ + @Deprecated void setContainerInfo(InspectContainerResponse containerInfo); } diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 114a6329954..134baf65328 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -58,7 +58,7 @@ @EqualsAndHashCode(callSuper = false) public class GenericContainer> extends FailureDetectingExternalResource - implements Container { + implements Container, AutoCloseable { private static final Charset UTF8 = Charset.forName("UTF-8"); @@ -134,6 +134,8 @@ public class GenericContainer> private List> logConsumers = new ArrayList<>(); + private final Set> createContainerCmdHooks = new HashSet<>(); + private static final Set AVAILABLE_IMAGE_NAME_CACHE = new HashSet<>(); private static final RateLimiter DOCKER_CLIENT_RATE_LIMITER = RateLimiterBuilder .newBuilder() @@ -191,6 +193,7 @@ private void tryStart(Profiler profiler) { profiler.start("Create container"); CreateContainerCmd createCommand = dockerClient.createContainerCmd(dockerImageName); applyConfiguration(createCommand); + createContainerCmdHooks.forEach(hook -> hook.accept(createCommand)); containerId = createCommand.exec().getId(); ResourceReaper.instance().registerContainerForCleanup(containerId, dockerImageName); @@ -883,6 +886,16 @@ public void withStartupAttempts(int attempts) { this.startupAttempts = attempts; } + @Override + public void close() { + stop(); + } + + public SELF withCustomizer(Consumer hook) { + createContainerCmdHooks.add(hook); + return self(); + } + /** * Convenience class with access to non-public members of GenericContainer. */ diff --git a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java index f91ad463444..efbbc1a58ef 100644 --- a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java +++ b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java @@ -11,6 +11,7 @@ import org.rnorth.ducttape.RetryCountExceededException; import org.rnorth.ducttape.unreliables.Unreliables; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.utility.Base58; import org.testcontainers.utility.TestEnvironment; import java.io.*; @@ -282,6 +283,26 @@ public void extraHostTest() throws IOException { assertTrue("The hosts file of container contains extra host", matcher.find()); } + @Test + public void createContainerCmdHookTest() { + // Use random name to avoid the conflicts between the tests + String randomName = Base58.randomString(5); + try( + GenericContainer container = new GenericContainer<>("redis:3.0.2") + .withCommand("redis-server", "--help") + .withCustomizer(cmd -> cmd.withName("overrideMe")) + // Preserves the order + .withCustomizer(cmd -> cmd.withName(randomName)) + // Allows to override pre-configured values by GenericContainer + .withCustomizer(cmd -> cmd.withCmd("redis-server", "--port", "6379")) + ) { + container.start(); + + assertEquals("Name is configured", "/" + randomName, container.getContainerInfo().getName()); + assertEquals("Command is configured", "[redis-server, --port, 6379]", Arrays.toString(container.getContainerInfo().getConfig().getCmd())); + } + } + private BufferedReader getReaderForContainerPort80(GenericContainer container) { return Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, () -> { From 7d6d200872c3bae21f98ac7a9060dcce9169aa9a Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 3 Mar 2017 11:01:57 +0200 Subject: [PATCH 0067/5615] withCustomizer -> withCreateContainerCmdModifier. Add JavaDoc. --- .../containers/GenericContainer.java | 16 ++++++++++++---- .../junit/GenericContainerRuleTest.java | 6 +++--- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 134baf65328..a166ca925b2 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -134,7 +134,7 @@ public class GenericContainer> private List> logConsumers = new ArrayList<>(); - private final Set> createContainerCmdHooks = new HashSet<>(); + private final Set> createContainerCmdMidifiers = new HashSet<>(); private static final Set AVAILABLE_IMAGE_NAME_CACHE = new HashSet<>(); private static final RateLimiter DOCKER_CLIENT_RATE_LIMITER = RateLimiterBuilder @@ -193,7 +193,7 @@ private void tryStart(Profiler profiler) { profiler.start("Create container"); CreateContainerCmd createCommand = dockerClient.createContainerCmd(dockerImageName); applyConfiguration(createCommand); - createContainerCmdHooks.forEach(hook -> hook.accept(createCommand)); + createContainerCmdMidifiers.forEach(hook -> hook.accept(createCommand)); containerId = createCommand.exec().getId(); ResourceReaper.instance().registerContainerForCleanup(containerId, dockerImageName); @@ -891,8 +891,16 @@ public void close() { stop(); } - public SELF withCustomizer(Consumer hook) { - createContainerCmdHooks.add(hook); + /** + * Allow low level modifications of {@link CreateContainerCmd} after it was pre-configured in {@link #tryStart(Profiler)}. + * Invocation happens eagerly on a moment when container is created. + * Warning: this does expose the underlying docker-java API so might change outside of our control. + * + * @param modifier {@link Consumer} of {@link CreateContainerCmd}. + * @return this + */ + public SELF withCreateContainerCmdModifier(Consumer modifier) { + createContainerCmdMidifiers.add(modifier); return self(); } diff --git a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java index efbbc1a58ef..59d9cb2b34f 100644 --- a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java +++ b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java @@ -290,11 +290,11 @@ public void createContainerCmdHookTest() { try( GenericContainer container = new GenericContainer<>("redis:3.0.2") .withCommand("redis-server", "--help") - .withCustomizer(cmd -> cmd.withName("overrideMe")) + .withCreateContainerCmdModifier(cmd -> cmd.withName("overrideMe")) // Preserves the order - .withCustomizer(cmd -> cmd.withName(randomName)) + .withCreateContainerCmdModifier(cmd -> cmd.withName(randomName)) // Allows to override pre-configured values by GenericContainer - .withCustomizer(cmd -> cmd.withCmd("redis-server", "--port", "6379")) + .withCreateContainerCmdModifier(cmd -> cmd.withCmd("redis-server", "--port", "6379")) ) { container.start(); From 971ff5cce81d8b27ad30f2701d21017cc30ef293 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sat, 4 Mar 2017 12:10:25 +0200 Subject: [PATCH 0068/5615] use LinkedHashSet to keep the order --- .../java/org/testcontainers/containers/GenericContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index a166ca925b2..2b9a5cd1b3b 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -134,7 +134,7 @@ public class GenericContainer> private List> logConsumers = new ArrayList<>(); - private final Set> createContainerCmdMidifiers = new HashSet<>(); + private final Set> createContainerCmdMidifiers = new LinkedHashSet<>(); private static final Set AVAILABLE_IMAGE_NAME_CACHE = new HashSet<>(); private static final RateLimiter DOCKER_CLIENT_RATE_LIMITER = RateLimiterBuilder From 570b4e87c2d7178bdaf49669c0aad8fd28681b21 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 10 Mar 2017 20:01:15 +0200 Subject: [PATCH 0069/5615] fix typo --- .../org/testcontainers/containers/GenericContainer.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 2b9a5cd1b3b..cbacc72ae4c 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -134,7 +134,7 @@ public class GenericContainer> private List> logConsumers = new ArrayList<>(); - private final Set> createContainerCmdMidifiers = new LinkedHashSet<>(); + private final Set> createContainerCmdModifiers = new LinkedHashSet<>(); private static final Set AVAILABLE_IMAGE_NAME_CACHE = new HashSet<>(); private static final RateLimiter DOCKER_CLIENT_RATE_LIMITER = RateLimiterBuilder @@ -193,7 +193,7 @@ private void tryStart(Profiler profiler) { profiler.start("Create container"); CreateContainerCmd createCommand = dockerClient.createContainerCmd(dockerImageName); applyConfiguration(createCommand); - createContainerCmdMidifiers.forEach(hook -> hook.accept(createCommand)); + createContainerCmdModifiers.forEach(hook -> hook.accept(createCommand)); containerId = createCommand.exec().getId(); ResourceReaper.instance().registerContainerForCleanup(containerId, dockerImageName); @@ -900,7 +900,7 @@ public void close() { * @return this */ public SELF withCreateContainerCmdModifier(Consumer modifier) { - createContainerCmdMidifiers.add(modifier); + createContainerCmdModifiers.add(modifier); return self(); } From 5bd2fea540b9032becb5ed34af1a95919b9a6861 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 12 Mar 2017 20:39:35 +0000 Subject: [PATCH 0070/5615] Add docs for `withCreateContainerCmdModifier` --- docs/usage/options.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/docs/usage/options.md b/docs/usage/options.md index 4972a144b2f..83211cc8f64 100644 --- a/docs/usage/options.md +++ b/docs/usage/options.md @@ -47,6 +47,32 @@ new GenericContainer(...) BindMode.READ_ONLY) ``` +### Customizing the container + +It is possible to use the [`docker-java`](https://github.com/docker-java/docker-java) API directly to customize containers before creation. This is useful if there is a need to use advanced Docker features that are not exposed by the Testcontainers API. Any customizations you make using `withCreateContainerCmdModifier` will be applied _on top_ of the container definition that Testcontainers creates, but before it is created. + +For example, this can be used to change the container hostname: +```java +new GenericContainer<>("redis:3.0.2") + .withCreateContainerCmdModifier(cmd -> cmd.withHostName("the-cache")) + ... +``` + +... or modify container memory (see [this](https://fabiokung.com/2014/03/13/memory-inside-linux-containers/) if it does not appear to work): +```java +new GenericContainer<>("alpine:3.3") + .withCreateContainerCmdModifier(cmd -> cmd.withMemory((long) 4 * 1024 * 1024)) + .withCreateContainerCmdModifier(cmd -> cmd.withMemorySwap((long) 4 * 1024 * 1024)) + ... +``` + +> It is recommended to use this sparingly, and follow changes to the `docker-java` API if you choose to use this. It is typically quite stable, though. + +For what is possible, consult the [`docker-java CreateContainerCmd` source code](https://github.com/docker-java/docker-java/blob/master/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java) + + + + ### Startup timeout Ordinarily Testcontainers will wait for up to 60 seconds for the container's first mapped network port to start listening. From 8feb7feb7665b80beb2512c4c7d1a458bc3c705e Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 6 Mar 2017 12:21:59 +0200 Subject: [PATCH 0071/5615] Docker for Windows: Use command-based port checker --- .../containers/wait/HostPortWaitStrategy.java | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java index 77fa9a78e70..64d3be64b1b 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java @@ -7,6 +7,7 @@ import org.testcontainers.containers.ContainerLaunchException; import org.testcontainers.containers.GenericContainer; import org.testcontainers.dockerclient.ProxiedUnixSocketClientProviderStrategy; +import org.testcontainers.dockerclient.WindowsClientProviderStrategy; import java.net.Socket; import java.util.List; @@ -32,8 +33,7 @@ protected void waitUntilReady() { Callable checkStrategy; - // Special case for Docker for Mac, see #160 - if (isUsingSocketProxyOnMac()) { + if (isApplicable()) { List exposedPorts = container.getExposedPorts(); Integer exposedPort = exposedPorts.stream() @@ -91,8 +91,18 @@ protected void waitUntilReady() { } } - private boolean isUsingSocketProxyOnMac() { - return DockerClientFactory.instance().isUsing(ProxiedUnixSocketClientProviderStrategy.class) - && System.getProperty("os.name").toLowerCase().contains("mac"); + private boolean isApplicable() { + // Special case for Docker for Mac, see #160 + if(DockerClientFactory.instance().isUsing(ProxiedUnixSocketClientProviderStrategy.class) + && System.getProperty("os.name").toLowerCase().contains("mac")) { + return true; + } + + // Special case for Docker for Windows, see #160 + if (DockerClientFactory.instance().isUsing(WindowsClientProviderStrategy.class)) { + return true; + } + + return false; } } From 68d292d8c99acc74f5a8dec7490b96fad5d6a733 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 10 Mar 2017 20:15:13 +0200 Subject: [PATCH 0072/5615] rename isApplicable -> shouldCheckWithCommand --- .../testcontainers/containers/wait/HostPortWaitStrategy.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java index 64d3be64b1b..3b3098a5372 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java @@ -33,7 +33,7 @@ protected void waitUntilReady() { Callable checkStrategy; - if (isApplicable()) { + if (shouldCheckWithCommand()) { List exposedPorts = container.getExposedPorts(); Integer exposedPort = exposedPorts.stream() @@ -91,7 +91,7 @@ protected void waitUntilReady() { } } - private boolean isApplicable() { + private boolean shouldCheckWithCommand() { // Special case for Docker for Mac, see #160 if(DockerClientFactory.instance().isUsing(ProxiedUnixSocketClientProviderStrategy.class) && System.getProperty("os.name").toLowerCase().contains("mac")) { From 19905c0f0d79fe05692ea440c5c0d543e1794255 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 15 Jan 2017 13:11:09 +0000 Subject: [PATCH 0073/5615] Introduce an abstraction over files and classpath resources. This encapsulates all the complexity of generating a path that the Docker daemon is about to create a volume mount for. This should resolve a general problem with spaces in paths, which was seen in one particular form as #263. Additional tests for the specific problem in #263 will be added shortly. --- .../containers/GenericContainer.java | 23 +- .../images/builder/traits/ClasspathTrait.java | 14 +- .../testcontainers/utility/MountableFile.java | 198 ++++++++++++++++++ .../org/testcontainers/utility/PathUtils.java | 103 ++------- .../utility/MountableFileTest.java | 77 +++++++ .../containers/JdbcDatabaseContainer.java | 12 +- 6 files changed, 303 insertions(+), 124 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/utility/MountableFile.java create mode 100644 core/src/test/java/org/testcontainers/utility/MountableFileTest.java diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index cbacc72ae4c..cd35991d8e0 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -11,7 +11,6 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NonNull; -import org.apache.commons.lang.SystemUtils; import org.jetbrains.annotations.Nullable; import org.junit.runner.Description; import org.rnorth.ducttape.ratelimits.RateLimiter; @@ -35,7 +34,6 @@ import java.io.File; import java.io.IOException; -import java.net.URL; import java.nio.charset.Charset; import java.nio.file.Path; import java.time.Duration; @@ -485,16 +483,8 @@ public void addEnv(String key, String value) { @Override public void addFileSystemBind(String hostPath, String containerPath, BindMode mode) { - if (hostPath.contains(".jar!")) { - // the host file is inside a JAR resource - copy to a temporary location that Docker can read - hostPath = PathUtils.extractClassPathResourceToTempLocation(hostPath); - } - - if (SystemUtils.IS_OS_WINDOWS) { - hostPath = PathUtils.createMinGWPath(hostPath); - } - - binds.add(new Bind(hostPath, new Volume(containerPath), mode.accessMode)); + final MountableFile mountableFile = MountableFile.forHostPath(hostPath); + binds.add(new Bind(mountableFile.getMountablePath(), new Volume(containerPath), mode.accessMode)); } /** @@ -626,14 +616,9 @@ public SELF withNetworkMode(String networkMode) { */ @Override public SELF withClasspathResourceMapping(String resourcePath, String containerPath, BindMode mode) { - URL resource = GenericContainer.class.getClassLoader().getResource(resourcePath); - - if (resource == null) { - throw new IllegalArgumentException("Could not find classpath resource at provided path: " + resourcePath); - } - String resourceFilePath = resource.getFile(); + final MountableFile mountableFile = MountableFile.forClasspathResource(resourcePath); - this.addFileSystemBind(resourceFilePath, containerPath, mode); + this.addFileSystemBind(mountableFile.getMountablePath(), containerPath, mode); return self(); } diff --git a/core/src/main/java/org/testcontainers/images/builder/traits/ClasspathTrait.java b/core/src/main/java/org/testcontainers/images/builder/traits/ClasspathTrait.java index 4b18cb8eacc..64547d02c57 100644 --- a/core/src/main/java/org/testcontainers/images/builder/traits/ClasspathTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/traits/ClasspathTrait.java @@ -1,7 +1,7 @@ package org.testcontainers.images.builder.traits; -import java.io.File; -import java.net.URL; +import org.testcontainers.utility.MountableFile; + import java.nio.file.Paths; /** @@ -11,14 +11,8 @@ public interface ClasspathTrait & BuildContextBuilderTrait & FilesTrait> { default SELF withFileFromClasspath(String path, String resourcePath) { - URL resource = ClasspathTrait.class.getClassLoader().getResource(resourcePath); - - if (resource == null) { - throw new IllegalArgumentException("Could not find classpath resource at provided path: " + resourcePath); - } - - String resourceFilePath = new File(resource.getFile()).getAbsolutePath(); + final MountableFile mountableFile = MountableFile.forClasspathResource(resourcePath); - return ((SELF) this).withFileFromPath(path, Paths.get(resourceFilePath)); + return ((SELF) this).withFileFromPath(path, Paths.get(mountableFile.getMountablePath())); } } diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java new file mode 100644 index 00000000000..f6a21de5961 --- /dev/null +++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java @@ -0,0 +1,198 @@ +package org.testcontainers.utility; + +import com.google.common.base.Charsets; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.SystemUtils; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLDecoder; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import static org.testcontainers.utility.PathUtils.recursiveDeleteDir; + +/** + * An abstraction over files and classpath resources aimed at encapsulating all the complexity of generating + * a path that the Docker daemon is about to create a volume mount for. + */ +@Slf4j +public class MountableFile { + + private final String path; + private String resolvedPath = null; + + private MountableFile(final String path) { + this.path = path; + } + + /** + * Obtains a {@link MountableFile} corresponding to a resource on the classpath (including resources in JAR files) + * + * @param resourceName the classpath path to the resource + * @return a {@link MountableFile} that may be used to obtain a mountable path + */ + public static MountableFile forClasspathResource(@NotNull final String resourceName) { + return new MountableFile(getClasspathResource(resourceName, new HashSet<>()).toString()); + } + + /** + * Obtains a {@link MountableFile} corresponding to a file on the docker host filesystem. + * + * @param path the path to the resource + * @return a {@link MountableFile} that may be used to obtain a mountable path + */ + public static MountableFile forHostPath(@NotNull final String path) { + return new MountableFile(new File(path).toURI().toString()); + } + + /** + * Obtain a path that the Docker daemon should be able to use to volume mount a file/resource + * into a container. If this is a classpath resource residing in a JAR, it will be extracted to + * a temporary location so that the Docker daemon is able to access it. + * + * @return a volume-mountable path. + */ + public String getMountablePath() { + + // Don't recompute if already resolved + if (resolvedPath != null) { + return resolvedPath; + } + + if (path.contains(".jar!")) { + resolvedPath = extractClassPathResourceToTempLocation(this.path); + } else { + resolvedPath = unencodeResourceURIToFilePath(path); + } + + if (SystemUtils.IS_OS_WINDOWS) { + resolvedPath = PathUtils.createMinGWPath(resolvedPath); + } + + return resolvedPath; + } + + @NotNull + private static URL getClasspathResource(@NotNull final String resourcePath, @NotNull final Set classLoaders) { + + // try context and system classloaders as well + classLoaders.add(Thread.currentThread().getContextClassLoader()); + classLoaders.add(ClassLoader.getSystemClassLoader()); + classLoaders.add(MountableFile.class.getClassLoader()); + + for (final ClassLoader classLoader : classLoaders) { + URL resource = classLoader.getResource(resourcePath); + if (resource != null) { + return resource; + } + + // Be lenient if an absolute path was given + if (resourcePath.startsWith("/")) { + resource = classLoader.getResource(resourcePath.replaceFirst("/", "")); + if (resource != null) { + return resource; + } + } + } + + throw new IllegalArgumentException("Resource with path " + resourcePath + " could not be found on any of these classloaders: " + classLoaders); + } + + private static String unencodeResourceURIToFilePath(@NotNull final String resource) { + try { + // Convert any url-encoded characters (e.g. spaces) back into unencoded form + return new URI(resource).getPath(); + } catch (URISyntaxException e) { + throw new IllegalStateException(e); + } + } + + /** + * Extract a file or directory tree from a JAR file to a temporary location. + * This allows Docker to mount classpath resources as files. + * + * @param hostPath the path on the host, expected to be of the format 'file:/path/to/some.jar!/classpath/path/to/resource' + * @return the path of the temporary file/directory + */ + private String extractClassPathResourceToTempLocation(final String hostPath) { + File tmpLocation = new File(".testcontainers-tmp-" + Base58.randomString(5)); + //noinspection ResultOfMethodCallIgnored + tmpLocation.delete(); + + String jarPath = hostPath.replaceFirst("jar:", "").replaceFirst("file:", "").replaceAll("!.*", ""); + String urldecodedJarPath; + try { + urldecodedJarPath = URLDecoder.decode(jarPath, Charsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + throw new IllegalArgumentException("Could not URLDecode path with UTF-8 encoding: " + hostPath, e); + } + String internalPath = hostPath.replaceAll("[^!]*!/", ""); + + try (JarFile jarFile = new JarFile(urldecodedJarPath)) { + Enumeration entries = jarFile.entries(); + + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + final String name = entry.getName(); + if (name.startsWith(internalPath)) { + log.debug("Copying classpath resource(s) from {} to {} to permit Docker to bind", + hostPath, + tmpLocation); + copyFromJarToLocation(jarFile, entry, internalPath, tmpLocation); + } + } + + } catch (IOException e) { + throw new IllegalStateException("Failed to process JAR file when extracting classpath resource: " + hostPath, e); + } + + // Mark temporary files/dirs for deletion at JVM shutdown + deleteOnExit(tmpLocation.toPath()); + + return tmpLocation.getAbsolutePath(); + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + private void copyFromJarToLocation(final JarFile jarFile, + final JarEntry entry, + final String fromRoot, + final File toRoot) throws IOException { + + String destinationName = entry.getName().replaceFirst(fromRoot, ""); + File newFile = new File(toRoot, destinationName); + + log.debug("Copying resource {} from JAR file {}", + fromRoot, + jarFile.getName()); + + if (!entry.isDirectory()) { + // Create parent directories + newFile.mkdirs(); + newFile.delete(); + newFile.deleteOnExit(); + + try (InputStream is = jarFile.getInputStream(entry)) { + Files.copy(is, newFile.toPath()); + } catch (IOException e) { + log.error("Failed to extract classpath resource " + entry.getName() + " from JAR file " + jarFile.getName(), e); + throw e; + } + } + } + + private void deleteOnExit(final Path path) { + Runtime.getRuntime().addShutdownHook(new Thread(() -> recursiveDeleteDir(path))); + } +} diff --git a/core/src/main/java/org/testcontainers/utility/PathUtils.java b/core/src/main/java/org/testcontainers/utility/PathUtils.java index 70798638ec3..2dcf9db8a8d 100644 --- a/core/src/main/java/org/testcontainers/utility/PathUtils.java +++ b/core/src/main/java/org/testcontainers/utility/PathUtils.java @@ -1,28 +1,24 @@ package org.testcontainers.utility; import lombok.NonNull; -import org.slf4j.Logger; +import lombok.experimental.UtilityClass; -import java.io.File; import java.io.IOException; -import java.io.InputStream; -import java.nio.file.*; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; -import java.util.Enumeration; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; - -import static org.slf4j.LoggerFactory.getLogger; /** * Filesystem operation utility methods. */ +@UtilityClass public class PathUtils { - private static final Logger log = getLogger(PathUtils.class); - /** * Recursively delete a directory and all its subdirectories and files. + * * @param directory path to the directory to delete. */ public static void recursiveDeleteDir(final @NonNull Path directory) { @@ -55,89 +51,24 @@ public static void mkdirp(Path directory) { throw new IllegalStateException("Failed to create directory at: " + directory); } } - + /** * Create a MinGW compatible path based on usual Windows path - * @param path a usual windows path + * + * @param path a usual windows path * @return a MinGW compatible path */ public static String createMinGWPath(String path) { - String mingwPath = path.replace('\\', '/'); - int driveLetterIndex = 1; - if(mingwPath.matches("^[a-zA-Z]:\\/.*")) { - driveLetterIndex = 0; + String mingwPath = path.replace('\\', '/'); + int driveLetterIndex = 1; + if (mingwPath.matches("^[a-zA-Z]:\\/.*")) { + driveLetterIndex = 0; } - + // drive-letter must be lower case mingwPath = "//" + Character.toLowerCase(mingwPath.charAt(driveLetterIndex)) + - mingwPath.substring(driveLetterIndex+1); - mingwPath = mingwPath.replace(":",""); + mingwPath.substring(driveLetterIndex + 1); + mingwPath = mingwPath.replace(":", ""); return mingwPath; } - - /** - * Extract a file or directory tree from a JAR file to a temporary location. - * This allows Docker to mount classpath resources as files. - * @param hostPath the path on the host, expected to be of the format 'file:/path/to/some.jar!/classpath/path/to/resource' - * @return the path of the temporary file/directory - */ - public static String extractClassPathResourceToTempLocation(final String hostPath) { - File tmpLocation = new File(".testcontainers-tmp-" + Base58.randomString(5)); - //noinspection ResultOfMethodCallIgnored - tmpLocation.delete(); - - String jarPath = hostPath.replaceFirst("file:", "").replaceAll("!.*", ""); - String internalPath = hostPath.replaceAll("[^!]*!/", ""); - - try (JarFile jarFile = new JarFile(jarPath)) { - Enumeration entries = jarFile.entries(); - - while (entries.hasMoreElements()) { - JarEntry entry = entries.nextElement(); - final String name = entry.getName(); - if (name.startsWith(internalPath)) { - log.debug("Copying classpath resource(s) from {} to {} to permit Docker to bind", - hostPath, - tmpLocation); - copyFromJarToLocation(jarFile, entry, internalPath, tmpLocation); - } - } - - } catch (IOException e) { - throw new IllegalStateException("Failed to process JAR file when extracting classpath resource: " + hostPath, e); - } - - // Mark temporary files/dirs for deletion at JVM shutdown - deleteOnExit(tmpLocation.toPath()); - - return tmpLocation.getAbsolutePath(); - } - - @SuppressWarnings("ResultOfMethodCallIgnored") - private static void copyFromJarToLocation(final JarFile jarFile, - final JarEntry entry, - final String fromRoot, - final File toRoot) throws IOException { - - String destinationName = entry.getName().replaceFirst(fromRoot, ""); - File newFile = new File(toRoot, destinationName); - - if (!entry.isDirectory()) { - // Create parent directories - newFile.mkdirs(); - newFile.delete(); - newFile.deleteOnExit(); - - try (InputStream is = jarFile.getInputStream(entry)) { - Files.copy(is, newFile.toPath()); - } catch (IOException e) { - log.error("Failed to extract classpath resource " + entry.getName() + " from JAR file " + jarFile.getName(), e); - throw e; - } - } - } - - public static void deleteOnExit(final Path path) { - Runtime.getRuntime().addShutdownHook(new Thread(() -> recursiveDeleteDir(path))); - } } diff --git a/core/src/test/java/org/testcontainers/utility/MountableFileTest.java b/core/src/test/java/org/testcontainers/utility/MountableFileTest.java new file mode 100644 index 00000000000..fa774699f7a --- /dev/null +++ b/core/src/test/java/org/testcontainers/utility/MountableFileTest.java @@ -0,0 +1,77 @@ +package org.testcontainers.utility; + +import org.jetbrains.annotations.NotNull; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; + +public class MountableFileTest { + + @Test + public void forClasspathResource() throws Exception { + final MountableFile mountableFile = MountableFile.forClasspathResource("mappable-resource/test-resource.txt"); + + performChecks(mountableFile); + } + + @Test + public void forClasspathResourceWithAbsolutePath() throws Exception { + final MountableFile mountableFile = MountableFile.forClasspathResource("/mappable-resource/test-resource.txt"); + + performChecks(mountableFile); + } + + @Test + public void forClasspathResourceFromJar() throws Exception { + final MountableFile mountableFile = MountableFile.forClasspathResource("docker-java.properties"); + + performChecks(mountableFile); + } + + @Test + public void forHostPath() throws Exception { + final Path file = createTempFile("somepath"); + final MountableFile mountableFile = MountableFile.forHostPath(file.toString()); + + performChecks(mountableFile); + } + + @Test + public void forHostPathWithSpaces() throws Exception { + final Path file = createTempFile("some path"); + final MountableFile mountableFile = MountableFile.forHostPath(file.toString()); + + performChecks(mountableFile); + + assertTrue("The resolved path contains the original space", mountableFile.getMountablePath().contains(" "));assertFalse("The resolved path does not contain an escaped space", mountableFile.getMountablePath().contains("\\ ")); + } + + /* + * + */ + + @SuppressWarnings("ResultOfMethodCallIgnored") + @NotNull + private Path createTempFile(final String name) throws IOException { + final File tempParentDir = File.createTempFile("testcontainers", ""); + tempParentDir.delete(); + tempParentDir.mkdirs(); + final Path file = new File(tempParentDir, name).toPath(); + + Files.copy(MountableFileTest.class.getResourceAsStream("/mappable-resource/test-resource.txt"), file); + return file; + } + + private void performChecks(final MountableFile mountableFile) { + final String mountablePath = mountableFile.getMountablePath(); + assertTrue("The resolved path can be found", new File(mountablePath).exists()); + assertFalse("The resolved path does not contain any URL escaping", mountablePath.contains("%20")); + } + +} \ No newline at end of file diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index 2d4136580d0..35a67b7344f 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -5,8 +5,8 @@ import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; import org.rnorth.ducttape.unreliables.Unreliables; import org.testcontainers.containers.traits.LinkableContainer; +import org.testcontainers.utility.MountableFile; -import java.net.URL; import java.sql.Connection; import java.sql.Driver; import java.sql.SQLException; @@ -130,14 +130,8 @@ protected void optionallyMapResourceParameterAsVolume(@NotNull String paramName, String resourceName = parameters.getOrDefault(paramName, defaultResource); if (resourceName != null) { - URL classPathResource = ClassLoader.getSystemClassLoader().getResource(resourceName); - if (classPathResource == null) { - throw new ContainerLaunchException("Could not locate a classpath resource for " + paramName +" of " + resourceName); - } - - final String resourceFile = classPathResource.getFile(); - - addFileSystemBind(resourceFile, pathNameInContainer, BindMode.READ_ONLY); + final MountableFile mountableFile = MountableFile.forClasspathResource(resourceName); + addFileSystemBind(mountableFile.getMountablePath(), pathNameInContainer, BindMode.READ_ONLY); } } From 4caddcc2b51d05aaf5689a189caf535001273da8 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 5 Feb 2017 20:11:30 +0000 Subject: [PATCH 0074/5615] Clean up following review comments --- .../containers/GenericContainer.java | 4 +-- .../images/builder/traits/ClasspathTrait.java | 2 +- .../testcontainers/utility/MountableFile.java | 36 +++++++++---------- .../utility/MountableFileTest.java | 4 +-- .../containers/JdbcDatabaseContainer.java | 2 +- 5 files changed, 23 insertions(+), 25 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index cd35991d8e0..167ea5864b9 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -484,7 +484,7 @@ public void addEnv(String key, String value) { public void addFileSystemBind(String hostPath, String containerPath, BindMode mode) { final MountableFile mountableFile = MountableFile.forHostPath(hostPath); - binds.add(new Bind(mountableFile.getMountablePath(), new Volume(containerPath), mode.accessMode)); + binds.add(new Bind(mountableFile.getResolvedPath(), new Volume(containerPath), mode.accessMode)); } /** @@ -618,7 +618,7 @@ public SELF withNetworkMode(String networkMode) { public SELF withClasspathResourceMapping(String resourcePath, String containerPath, BindMode mode) { final MountableFile mountableFile = MountableFile.forClasspathResource(resourcePath); - this.addFileSystemBind(mountableFile.getMountablePath(), containerPath, mode); + this.addFileSystemBind(mountableFile.getResolvedPath(), containerPath, mode); return self(); } diff --git a/core/src/main/java/org/testcontainers/images/builder/traits/ClasspathTrait.java b/core/src/main/java/org/testcontainers/images/builder/traits/ClasspathTrait.java index 64547d02c57..5617365f2cd 100644 --- a/core/src/main/java/org/testcontainers/images/builder/traits/ClasspathTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/traits/ClasspathTrait.java @@ -13,6 +13,6 @@ public interface ClasspathTrait & BuildContext default SELF withFileFromClasspath(String path, String resourcePath) { final MountableFile mountableFile = MountableFile.forClasspathResource(resourcePath); - return ((SELF) this).withFileFromPath(path, Paths.get(mountableFile.getMountablePath())); + return ((SELF) this).withFileFromPath(path, Paths.get(mountableFile.getResolvedPath())); } } diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java index f6a21de5961..31ee6008751 100644 --- a/core/src/main/java/org/testcontainers/utility/MountableFile.java +++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java @@ -1,6 +1,8 @@ package org.testcontainers.utility; import com.google.common.base.Charsets; +import lombok.Getter; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.SystemUtils; import org.jetbrains.annotations.NotNull; @@ -21,21 +23,21 @@ import java.util.jar.JarEntry; import java.util.jar.JarFile; +import static lombok.AccessLevel.PRIVATE; import static org.testcontainers.utility.PathUtils.recursiveDeleteDir; /** * An abstraction over files and classpath resources aimed at encapsulating all the complexity of generating * a path that the Docker daemon is about to create a volume mount for. */ +@RequiredArgsConstructor(access = PRIVATE) @Slf4j public class MountableFile { private final String path; - private String resolvedPath = null; - private MountableFile(final String path) { - this.path = path; - } + @Getter(lazy = true) + private final String resolvedPath = resolvePath(); /** * Obtains a {@link MountableFile} corresponding to a resource on the classpath (including resources in JAR files) @@ -64,35 +66,31 @@ public static MountableFile forHostPath(@NotNull final String path) { * * @return a volume-mountable path. */ - public String getMountablePath() { - - // Don't recompute if already resolved - if (resolvedPath != null) { - return resolvedPath; - } - + private String resolvePath() { + String result; if (path.contains(".jar!")) { - resolvedPath = extractClassPathResourceToTempLocation(this.path); + result = extractClassPathResourceToTempLocation(this.path); } else { - resolvedPath = unencodeResourceURIToFilePath(path); + result = unencodeResourceURIToFilePath(path); } if (SystemUtils.IS_OS_WINDOWS) { - resolvedPath = PathUtils.createMinGWPath(resolvedPath); + result = PathUtils.createMinGWPath(result); } - return resolvedPath; + return result; } @NotNull private static URL getClasspathResource(@NotNull final String resourcePath, @NotNull final Set classLoaders) { + final Set classLoadersToSearch = new HashSet<>(classLoaders); // try context and system classloaders as well - classLoaders.add(Thread.currentThread().getContextClassLoader()); - classLoaders.add(ClassLoader.getSystemClassLoader()); - classLoaders.add(MountableFile.class.getClassLoader()); + classLoadersToSearch.add(Thread.currentThread().getContextClassLoader()); + classLoadersToSearch.add(ClassLoader.getSystemClassLoader()); + classLoadersToSearch.add(MountableFile.class.getClassLoader()); - for (final ClassLoader classLoader : classLoaders) { + for (final ClassLoader classLoader : classLoadersToSearch) { URL resource = classLoader.getResource(resourcePath); if (resource != null) { return resource; diff --git a/core/src/test/java/org/testcontainers/utility/MountableFileTest.java b/core/src/test/java/org/testcontainers/utility/MountableFileTest.java index fa774699f7a..e42d92f07c1 100644 --- a/core/src/test/java/org/testcontainers/utility/MountableFileTest.java +++ b/core/src/test/java/org/testcontainers/utility/MountableFileTest.java @@ -49,7 +49,7 @@ public void forHostPathWithSpaces() throws Exception { performChecks(mountableFile); - assertTrue("The resolved path contains the original space", mountableFile.getMountablePath().contains(" "));assertFalse("The resolved path does not contain an escaped space", mountableFile.getMountablePath().contains("\\ ")); + assertTrue("The resolved path contains the original space", mountableFile.getResolvedPath().contains(" "));assertFalse("The resolved path does not contain an escaped space", mountableFile.getResolvedPath().contains("\\ ")); } /* @@ -69,7 +69,7 @@ private Path createTempFile(final String name) throws IOException { } private void performChecks(final MountableFile mountableFile) { - final String mountablePath = mountableFile.getMountablePath(); + final String mountablePath = mountableFile.getResolvedPath(); assertTrue("The resolved path can be found", new File(mountablePath).exists()); assertFalse("The resolved path does not contain any URL escaping", mountablePath.contains("%20")); } diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index 35a67b7344f..21a9da73c95 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -131,7 +131,7 @@ protected void optionallyMapResourceParameterAsVolume(@NotNull String paramName, if (resourceName != null) { final MountableFile mountableFile = MountableFile.forClasspathResource(resourceName); - addFileSystemBind(mountableFile.getMountablePath(), pathNameInContainer, BindMode.READ_ONLY); + addFileSystemBind(mountableFile.getResolvedPath(), pathNameInContainer, BindMode.READ_ONLY); } } From 1ef01937a532219fe91e76cabb2efa75505ca4e7 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 12 Mar 2017 20:58:35 +0000 Subject: [PATCH 0075/5615] Update CHANGELOG for 1.2.0 release: ## [1.2.0] - 2017-03-12 ### Fixed - Fix various escaping issues that may arise when paths contain spaces (#263, #279) - General documentation fixes/improvements (#300, #303, #304) - Improve reliability of `ResourceReaper` when there are a large number of containers returned by `docker ps -a` (#295) ### Changed - Support Docker for Windows via TCP socket connection (#291, #297, #309). _Note that Docker Compose is not yet supported under Docker for Windows (see #306) - Expose `docker-java`'s `CreateContainerCmd` API for low-level container tweaking (#301) - Shade `org.newsclub` and Guava dependencies (#299, #292) - Add `org.testcontainers` label to all containers created by Testcontainers (#294) --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cfe609a8a6..d32dfbcb781 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,18 @@ # Change Log All notable changes to this project will be documented in this file. +## [1.2.0] - 2017-03-12 +### Fixed +- Fix various escaping issues that may arise when paths contain spaces (#263, #279) +- General documentation fixes/improvements (#300, #303, #304) +- Improve reliability of `ResourceReaper` when there are a large number of containers returned by `docker ps -a` (#295) + +### Changed +- Support Docker for Windows via TCP socket connection (#291, #297, #309). _Note that Docker Compose is not yet supported under Docker for Windows (see #306) +- Expose `docker-java`'s `CreateContainerCmd` API for low-level container tweaking (#301) +- Shade `org.newsclub` and Guava dependencies (#299, #292) +- Add `org.testcontainers` label to all containers created by Testcontainers (#294) + ## [1.1.9] - 2017-02-12 ### Fixed - Fix inability to run Testcontainers on Alpine linux. Unix-socket-over-TCP is now used in linux environments where netty fails due to lack of glibc libraries (#290) @@ -243,6 +255,9 @@ All notable changes to this project will be documented in this file. ## [0.9] - 2015-04-29 Initial release +[1.2.0]: https://github.com/testcontainers/testcontainers-java/releases/tag/testcontainers-1.2.0 +[1.1.9]: https://github.com/testcontainers/testcontainers-java/releases/tag/testcontainers-1.1.9 +[1.1.8]: https://github.com/testcontainers/testcontainers-java/releases/tag/testcontainers-1.1.8 [1.1.7]: https://github.com/testcontainers/testcontainers-java/releases/tag/testcontainers-1.1.7 [1.1.6]: https://github.com/testcontainers/testcontainers-java/releases/tag/testcontainers-1.1.6 [1.1.5]: https://github.com/testcontainers/testcontainers-java/releases/tag/testcontainers-1.1.5 From a59f4a7f6fff82e44d401de31c116d9b34f8dc0b Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 12 Mar 2017 21:13:01 +0000 Subject: [PATCH 0076/5615] Update release version number to 1.2.0 --- core/pom.xml | 2 +- modules/jdbc-test/pom.xml | 2 +- modules/jdbc/pom.xml | 2 +- modules/mariadb/pom.xml | 2 +- modules/mysql/pom.xml | 2 +- modules/nginx/pom.xml | 2 +- modules/oracle-xe/pom.xml | 2 +- modules/postgresql/pom.xml | 2 +- modules/selenium/pom.xml | 2 +- modules/virtuoso/pom.xml | 2 +- pom.xml | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 9fa5bd99343..8cd913bc54f 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -6,7 +6,7 @@ org.testcontainers testcontainers-parent - 1.1.10-SNAPSHOT + 1.2.0 ../pom.xml diff --git a/modules/jdbc-test/pom.xml b/modules/jdbc-test/pom.xml index 1fed27a6193..cb207061b09 100644 --- a/modules/jdbc-test/pom.xml +++ b/modules/jdbc-test/pom.xml @@ -6,7 +6,7 @@ org.testcontainers testcontainers-parent - 1.1.10-SNAPSHOT + 1.2.0 ../../pom.xml diff --git a/modules/jdbc/pom.xml b/modules/jdbc/pom.xml index e54f149d206..cc8999215fb 100644 --- a/modules/jdbc/pom.xml +++ b/modules/jdbc/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.10-SNAPSHOT + 1.2.0 ../../pom.xml diff --git a/modules/mariadb/pom.xml b/modules/mariadb/pom.xml index c46873bf5a1..d2b2cb895a8 100644 --- a/modules/mariadb/pom.xml +++ b/modules/mariadb/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.10-SNAPSHOT + 1.2.0 ../../pom.xml diff --git a/modules/mysql/pom.xml b/modules/mysql/pom.xml index 98abbbee234..016ece4e1bf 100644 --- a/modules/mysql/pom.xml +++ b/modules/mysql/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.10-SNAPSHOT + 1.2.0 ../../pom.xml diff --git a/modules/nginx/pom.xml b/modules/nginx/pom.xml index 2b79b06911b..f6224c84a02 100644 --- a/modules/nginx/pom.xml +++ b/modules/nginx/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.10-SNAPSHOT + 1.2.0 ../../pom.xml diff --git a/modules/oracle-xe/pom.xml b/modules/oracle-xe/pom.xml index 5af1da1cbee..576b42a8f72 100644 --- a/modules/oracle-xe/pom.xml +++ b/modules/oracle-xe/pom.xml @@ -8,7 +8,7 @@ org.testcontainers testcontainers-parent - 1.1.10-SNAPSHOT + 1.2.0 ../../pom.xml diff --git a/modules/postgresql/pom.xml b/modules/postgresql/pom.xml index ef5fcd55ab9..878684d79d7 100644 --- a/modules/postgresql/pom.xml +++ b/modules/postgresql/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.10-SNAPSHOT + 1.2.0 ../../pom.xml diff --git a/modules/selenium/pom.xml b/modules/selenium/pom.xml index 4cadc2de3a0..8065a534c22 100644 --- a/modules/selenium/pom.xml +++ b/modules/selenium/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.10-SNAPSHOT + 1.2.0 ../../pom.xml diff --git a/modules/virtuoso/pom.xml b/modules/virtuoso/pom.xml index dd9a09b9387..84853a7dd41 100644 --- a/modules/virtuoso/pom.xml +++ b/modules/virtuoso/pom.xml @@ -4,7 +4,7 @@ org.testcontainers testcontainers-parent - 1.1.10-SNAPSHOT + 1.2.0 ../../pom.xml diff --git a/pom.xml b/pom.xml index 6aaf11fc59a..c96af8f96ad 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.1.10-SNAPSHOT + 1.2.0 pom From 302fec2b630262c98358301dfcc9781c955acd4b Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 12 Mar 2017 21:17:14 +0000 Subject: [PATCH 0077/5615] Update development version number to 1.2.1-SNAPSHOT Update test project version --- core/pom.xml | 2 +- modules/jdbc-test/pom.xml | 2 +- modules/jdbc/pom.xml | 2 +- modules/mariadb/pom.xml | 2 +- modules/mysql/pom.xml | 2 +- modules/nginx/pom.xml | 2 +- modules/oracle-xe/pom.xml | 2 +- modules/postgresql/pom.xml | 2 +- modules/selenium/pom.xml | 2 +- modules/virtuoso/pom.xml | 2 +- pom.xml | 2 +- shade-test/clashing-deps-jackson/pom.xml | 2 +- shade-test/clashing-deps-jersey/pom.xml | 2 +- shade-test/pom.xml | 2 +- shade-test/service-lookup-dropwizard/pom.xml | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 8cd913bc54f..0cb5a0c37f6 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -6,7 +6,7 @@ org.testcontainers testcontainers-parent - 1.2.0 + 1.2.1-SNAPSHOT ../pom.xml diff --git a/modules/jdbc-test/pom.xml b/modules/jdbc-test/pom.xml index cb207061b09..c7b274726bf 100644 --- a/modules/jdbc-test/pom.xml +++ b/modules/jdbc-test/pom.xml @@ -6,7 +6,7 @@ org.testcontainers testcontainers-parent - 1.2.0 + 1.2.1-SNAPSHOT ../../pom.xml diff --git a/modules/jdbc/pom.xml b/modules/jdbc/pom.xml index cc8999215fb..83024402cbc 100644 --- a/modules/jdbc/pom.xml +++ b/modules/jdbc/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.2.0 + 1.2.1-SNAPSHOT ../../pom.xml diff --git a/modules/mariadb/pom.xml b/modules/mariadb/pom.xml index d2b2cb895a8..572f5b4822a 100644 --- a/modules/mariadb/pom.xml +++ b/modules/mariadb/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.2.0 + 1.2.1-SNAPSHOT ../../pom.xml diff --git a/modules/mysql/pom.xml b/modules/mysql/pom.xml index 016ece4e1bf..c3837173126 100644 --- a/modules/mysql/pom.xml +++ b/modules/mysql/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.2.0 + 1.2.1-SNAPSHOT ../../pom.xml diff --git a/modules/nginx/pom.xml b/modules/nginx/pom.xml index f6224c84a02..7745e049104 100644 --- a/modules/nginx/pom.xml +++ b/modules/nginx/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.2.0 + 1.2.1-SNAPSHOT ../../pom.xml diff --git a/modules/oracle-xe/pom.xml b/modules/oracle-xe/pom.xml index 576b42a8f72..c68885d1f1f 100644 --- a/modules/oracle-xe/pom.xml +++ b/modules/oracle-xe/pom.xml @@ -8,7 +8,7 @@ org.testcontainers testcontainers-parent - 1.2.0 + 1.2.1-SNAPSHOT ../../pom.xml diff --git a/modules/postgresql/pom.xml b/modules/postgresql/pom.xml index 878684d79d7..66d2930b7a5 100644 --- a/modules/postgresql/pom.xml +++ b/modules/postgresql/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.2.0 + 1.2.1-SNAPSHOT ../../pom.xml diff --git a/modules/selenium/pom.xml b/modules/selenium/pom.xml index 8065a534c22..51b1ab8ae88 100644 --- a/modules/selenium/pom.xml +++ b/modules/selenium/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.2.0 + 1.2.1-SNAPSHOT ../../pom.xml diff --git a/modules/virtuoso/pom.xml b/modules/virtuoso/pom.xml index 84853a7dd41..0db625f83ef 100644 --- a/modules/virtuoso/pom.xml +++ b/modules/virtuoso/pom.xml @@ -4,7 +4,7 @@ org.testcontainers testcontainers-parent - 1.2.0 + 1.2.1-SNAPSHOT ../../pom.xml diff --git a/pom.xml b/pom.xml index c96af8f96ad..ed8598949a9 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.2.0 + 1.2.1-SNAPSHOT pom diff --git a/shade-test/clashing-deps-jackson/pom.xml b/shade-test/clashing-deps-jackson/pom.xml index 4cd5a28b821..cc4de2625b2 100644 --- a/shade-test/clashing-deps-jackson/pom.xml +++ b/shade-test/clashing-deps-jackson/pom.xml @@ -5,7 +5,7 @@ shade-test org.testcontainers - 1.1.10-SNAPSHOT + 1.2.1-SNAPSHOT ../pom.xml 4.0.0 diff --git a/shade-test/clashing-deps-jersey/pom.xml b/shade-test/clashing-deps-jersey/pom.xml index 79e658be862..84768aa2441 100644 --- a/shade-test/clashing-deps-jersey/pom.xml +++ b/shade-test/clashing-deps-jersey/pom.xml @@ -5,7 +5,7 @@ shade-test org.testcontainers - 1.1.10-SNAPSHOT + 1.2.1-SNAPSHOT ../pom.xml 4.0.0 diff --git a/shade-test/pom.xml b/shade-test/pom.xml index 8d66ff943d8..c8ef9e4a8d1 100644 --- a/shade-test/pom.xml +++ b/shade-test/pom.xml @@ -5,7 +5,7 @@ testcontainers-parent org.testcontainers - 1.1.10-SNAPSHOT + 1.2.1-SNAPSHOT ../pom.xml 4.0.0 diff --git a/shade-test/service-lookup-dropwizard/pom.xml b/shade-test/service-lookup-dropwizard/pom.xml index ea81476c706..09f318f74c1 100644 --- a/shade-test/service-lookup-dropwizard/pom.xml +++ b/shade-test/service-lookup-dropwizard/pom.xml @@ -5,7 +5,7 @@ shade-test org.testcontainers - 1.1.10-SNAPSHOT + 1.2.1-SNAPSHOT ../pom.xml 4.0.0 From 82f7b487be97b00bf366d305edf111e48d2250a5 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 13 Mar 2017 07:20:21 +0200 Subject: [PATCH 0078/5615] add "Customizing the container" section to SUMMARY.md --- docs/SUMMARY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 4ea04004529..62f1d877a92 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -21,6 +21,7 @@ * [Environment variables](usage/options.md#environment-variables) * [Command](usage/options.md#command) * [Volume mapping](usage/options.md#volume-mapping) + * [Customizing the container](usage/options.md#customizing-the-container) * [Startup timeout](usage/options.md#startup-timeout) * [Following container output](usage/options.md#following-container-output) * [Executing a command](usage/options.md#executing-a-command) From 07ac0924adcc24d02085b9746f25ff0302f194c8 Mon Sep 17 00:00:00 2001 From: peter-evans Date: Thu, 16 Mar 2017 09:50:45 +0900 Subject: [PATCH 0079/5615] Removed use of "latest" --- .../java/com/example/linkedcontainer/RedmineContainer.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java b/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java index dead067f67e..fc0f8f918c3 100644 --- a/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java +++ b/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java @@ -9,13 +9,8 @@ */ public class RedmineContainer> extends GenericContainer implements LinkableContainer { - private static final String IMAGE = "redmine"; private static final int REDMINE_PORT = 3000; - public RedmineContainer() { - this(IMAGE + ":latest"); - } - public RedmineContainer(String dockerImageName) { super.setDockerImageName(dockerImageName); } From 4664f3d81da0f8adca6fd25eec9a6aba80e8ef13 Mon Sep 17 00:00:00 2001 From: peter-evans Date: Thu, 16 Mar 2017 09:52:28 +0900 Subject: [PATCH 0080/5615] Added missing space --- .../java/com/example/linkedcontainer/RedmineClientTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java b/linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java index 9e79b1b042b..82275dcc71c 100644 --- a/linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java +++ b/linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java @@ -25,7 +25,7 @@ public class RedmineClientTest { .withEnv("POSTGRES_ENV_POSTGRES_PASSWORD", POSTGRES_PASSWORD); @Rule - public RuleChain chain= RuleChain.outerRule(postgreSQLContainer) + public RuleChain chain = RuleChain.outerRule(postgreSQLContainer) .around(redmineContainer); @Test From c15155f861c7ea5584c29ea8aa46cf11afa5acc7 Mon Sep 17 00:00:00 2001 From: peter-evans Date: Thu, 16 Mar 2017 16:30:05 +0900 Subject: [PATCH 0081/5615] Simplified RedmineContainer and removed unnecessary overrides --- .../linkedcontainer/RedmineContainer.java | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java b/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java index fc0f8f918c3..4dabf74d107 100644 --- a/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java +++ b/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java @@ -7,17 +7,12 @@ /** * A linkable Redmine container. */ -public class RedmineContainer> extends GenericContainer implements LinkableContainer { +public class RedmineContainer extends GenericContainer { private static final int REDMINE_PORT = 3000; public RedmineContainer(String dockerImageName) { - super.setDockerImageName(dockerImageName); - } - - @Override - protected Integer getLivenessCheckPort() { - return getMappedPort(REDMINE_PORT); + super(dockerImageName); } @Override @@ -26,14 +21,9 @@ protected void configure() { waitingFor(Wait.forHttp("/")); } - @Override - public SELF withEnv(String key, String value) { - return super.withEnv(key, value); - } - - public SELF withLinkToContainer(LinkableContainer otherContainer, String alias) { + public RedmineContainer withLinkToContainer(LinkableContainer otherContainer, String alias) { addLink(otherContainer, alias); - return self(); + return this; } public String getRedmineUrl() { From e5366407c9a3d0bc6600f7cce01a7128ed5de4c1 Mon Sep 17 00:00:00 2001 From: peter-evans Date: Thu, 16 Mar 2017 16:34:28 +0900 Subject: [PATCH 0082/5615] Updated class comment as it doesn't need to be Linkable itself --- .../test/java/com/example/linkedcontainer/RedmineContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java b/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java index 4dabf74d107..a4e8cceb558 100644 --- a/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java +++ b/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java @@ -5,7 +5,7 @@ import org.testcontainers.containers.wait.Wait; /** - * A linkable Redmine container. + * A Redmine container. */ public class RedmineContainer extends GenericContainer { From c5cbbbefc03c8372faaa379a9de652488cd87917 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Sun, 2 Apr 2017 14:27:43 +0200 Subject: [PATCH 0083/5615] Fix dead links Not sure if the updated links are the ones you intended, but that's the location of each of these files... --- docs/usage/database_containers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/usage/database_containers.md b/docs/usage/database_containers.md index 9da5af91e81..94ee888b5ac 100644 --- a/docs/usage/database_containers.md +++ b/docs/usage/database_containers.md @@ -40,8 +40,8 @@ Note that if you use `@Rule`, you will be given an isolated container for each t Examples/Tests: - * [MySQL](https://github.com/testcontainers/testcontainers-java/blob/master/modules/mysql/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java) - * [PostgreSQL](https://github.com/testcontainers/testcontainers-java/blob/master/modules/postgresql/src/test/java/org/testcontainers/junit/SimplePostgreSQLTest.java) + * [MySQL](https://github.com/testcontainers/testcontainers-java/blob/master/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java) + * [PostgreSQL](https://github.com/testcontainers/testcontainers-java/blob/master/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimplePostgreSQLTest.java) * [Oracle-XE](https://github.com/testcontainers/testcontainers-java/blob/master/modules/oracle-xe/src/test/java/org/testcontainers/junit/SimpleOracleTest.java) * [Virtuoso](https://github.com/testcontainers/testcontainers-java/blob/master/modules/virtuoso/src/test/java/org/testcontainers/junit/SimpleVirtuosoTest.java) From 9319ada6d530279a625eaa4f5c093740938526b6 Mon Sep 17 00:00:00 2001 From: gorelikov Date: Thu, 6 Apr 2017 08:08:26 +0300 Subject: [PATCH 0084/5615] #323 Fix for missed TINY_IMAGE during DockerClient initialization (#324) * Fix for missed TINY_IMAGE during DockerClient initialization in case of DinD environment * Clean up following review comments --- .../testcontainers/DockerClientFactory.java | 18 ++++++----- .../DockerClientFactoryTest.java | 30 +++++++++++++++++++ 2 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 core/src/test/java/org/testcontainers/DockerClientFactoryTest.java diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 0e68cc87965..693571802ad 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -97,13 +97,6 @@ public DockerClient client() { " Total Memory: " + dockerInfo.getMemTotal() / (1024 * 1024) + " MB"); checkVersion(version.getVersion()); - - List images = client.listImagesCmd().exec(); - // Pull the image we use to perform some checks - if (images.stream().noneMatch(it -> it.getRepoTags() != null && asList(it.getRepoTags()).contains(TINY_IMAGE))) { - client.pullImageCmd(TINY_IMAGE).exec(new PullImageResultCallback()).awaitSuccess(); - } - checkDiskSpaceAndHandleExceptions(client); preconditionsChecked = true; } @@ -111,6 +104,16 @@ public DockerClient client() { return client; } + /** + * Check whether the image is available locally and pull it otherwise + */ + private void checkAndPullImage(DockerClient client, String image) { + List images = client.listImagesCmd().withImageNameFilter(image).exec(); + if (images.isEmpty()) { + client.pullImageCmd(image).exec(new PullImageResultCallback()).awaitSuccess(); + } + } + /** * @return the IP address of the host running Docker */ @@ -168,6 +171,7 @@ public T runInsideDocker(Consumer createContainerCmdCons } private T runInsideDocker(DockerClient client, Consumer createContainerCmdConsumer, BiFunction block) { + checkAndPullImage(client, TINY_IMAGE); CreateContainerCmd createContainerCmd = client.createContainerCmd(TINY_IMAGE); createContainerCmdConsumer.accept(createContainerCmd); String id = createContainerCmd.exec().getId(); diff --git a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java new file mode 100644 index 00000000000..bf67cc6885a --- /dev/null +++ b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java @@ -0,0 +1,30 @@ +package org.testcontainers; + +import org.junit.Test; +import org.testcontainers.dockerclient.LogToStringContainerCallback; +import org.testcontainers.utility.TestcontainersConfiguration; + +/** + * Test for {@link DockerClientFactory}. + */ +public class DockerClientFactoryTest { + + @Test + public void runCommandInsideDockerShouldNotFailIfImageDoesNotExistsLocally() { + + final DockerClientFactory dockFactory = DockerClientFactory.instance(); + //remove tiny image, so it will be pulled during next command run + dockFactory.client() + .removeImageCmd(TestcontainersConfiguration.getInstance().getTinyImage()) + .withForce(true).exec(); + + dockFactory.runInsideDocker( + cmd -> cmd.withCmd("sh", "-c", "echo 'SUCCESS'"), + (client, id) -> + client.logContainerCmd(id) + .withStdOut(true) + .exec(new LogToStringContainerCallback()) + .toString() + ); + } +} \ No newline at end of file From aa92c8382dd14f33f9b8326b653a83c07cf7381e Mon Sep 17 00:00:00 2001 From: David Byron Date: Wed, 15 Mar 2017 17:06:19 -0700 Subject: [PATCH 0085/5615] shade aopalliance to reduce the chance for conflicts --- core/pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/pom.xml b/core/pom.xml index 0cb5a0c37f6..6ac4598ce06 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -120,6 +120,10 @@ org.glassfish org.testcontainers.shaded.org.glassfish + + org.aopalliance + org.testcontainers.shaded.org.aopalliance + org.jvnet org.testcontainers.shaded.org.jvnet @@ -169,6 +173,7 @@ org.apache.httpcomponents:* org.glassfish.*:* + org.aopalliance.*:* javax.annotation:* javax.inject:* javax.ws.rs:* From 7f7fe80771ae0979f4951cfea3847446779f15cc Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 6 Apr 2017 20:30:17 +0100 Subject: [PATCH 0086/5615] Update release version number to 1.2.1 --- CHANGELOG.md | 8 ++++++++ core/pom.xml | 2 +- modules/jdbc-test/pom.xml | 2 +- modules/jdbc/pom.xml | 2 +- modules/mariadb/pom.xml | 2 +- modules/mysql/pom.xml | 2 +- modules/nginx/pom.xml | 2 +- modules/oracle-xe/pom.xml | 2 +- modules/postgresql/pom.xml | 2 +- modules/selenium/pom.xml | 2 +- modules/virtuoso/pom.xml | 2 +- pom.xml | 2 +- 12 files changed, 19 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d32dfbcb781..a72ff55a0e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ # Change Log All notable changes to this project will be documented in this file. +## [1.2.1] - 2017-04-06 +### Fixed +- Fix bug in space detection when `alpine:3.5` image has not yet been pulled (#323, #324) +- Minor documentation fixes + +### Changed +- Add AOP Alliance dependencies to shaded deps to reduce chance of conflicts (#315) + ## [1.2.0] - 2017-03-12 ### Fixed - Fix various escaping issues that may arise when paths contain spaces (#263, #279) diff --git a/core/pom.xml b/core/pom.xml index 6ac4598ce06..913d1eab037 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -6,7 +6,7 @@ org.testcontainers testcontainers-parent - 1.2.1-SNAPSHOT + 1.2.1 ../pom.xml diff --git a/modules/jdbc-test/pom.xml b/modules/jdbc-test/pom.xml index c7b274726bf..e6ed410de68 100644 --- a/modules/jdbc-test/pom.xml +++ b/modules/jdbc-test/pom.xml @@ -6,7 +6,7 @@ org.testcontainers testcontainers-parent - 1.2.1-SNAPSHOT + 1.2.1 ../../pom.xml diff --git a/modules/jdbc/pom.xml b/modules/jdbc/pom.xml index 83024402cbc..4d3e1d45eee 100644 --- a/modules/jdbc/pom.xml +++ b/modules/jdbc/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.2.1-SNAPSHOT + 1.2.1 ../../pom.xml diff --git a/modules/mariadb/pom.xml b/modules/mariadb/pom.xml index 572f5b4822a..6f9edaedc4e 100644 --- a/modules/mariadb/pom.xml +++ b/modules/mariadb/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.2.1-SNAPSHOT + 1.2.1 ../../pom.xml diff --git a/modules/mysql/pom.xml b/modules/mysql/pom.xml index c3837173126..16e6c10daa6 100644 --- a/modules/mysql/pom.xml +++ b/modules/mysql/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.2.1-SNAPSHOT + 1.2.1 ../../pom.xml diff --git a/modules/nginx/pom.xml b/modules/nginx/pom.xml index 7745e049104..652790f1ed2 100644 --- a/modules/nginx/pom.xml +++ b/modules/nginx/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.2.1-SNAPSHOT + 1.2.1 ../../pom.xml diff --git a/modules/oracle-xe/pom.xml b/modules/oracle-xe/pom.xml index c68885d1f1f..e7d6cd5e813 100644 --- a/modules/oracle-xe/pom.xml +++ b/modules/oracle-xe/pom.xml @@ -8,7 +8,7 @@ org.testcontainers testcontainers-parent - 1.2.1-SNAPSHOT + 1.2.1 ../../pom.xml diff --git a/modules/postgresql/pom.xml b/modules/postgresql/pom.xml index 66d2930b7a5..fd9d0fe0b05 100644 --- a/modules/postgresql/pom.xml +++ b/modules/postgresql/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.2.1-SNAPSHOT + 1.2.1 ../../pom.xml diff --git a/modules/selenium/pom.xml b/modules/selenium/pom.xml index 51b1ab8ae88..602f54ab96a 100644 --- a/modules/selenium/pom.xml +++ b/modules/selenium/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.2.1-SNAPSHOT + 1.2.1 ../../pom.xml diff --git a/modules/virtuoso/pom.xml b/modules/virtuoso/pom.xml index 0db625f83ef..97f54c5081d 100644 --- a/modules/virtuoso/pom.xml +++ b/modules/virtuoso/pom.xml @@ -4,7 +4,7 @@ org.testcontainers testcontainers-parent - 1.2.1-SNAPSHOT + 1.2.1 ../../pom.xml diff --git a/pom.xml b/pom.xml index ed8598949a9..ea5b0cfb114 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.2.1-SNAPSHOT + 1.2.1 pom From a8cd6776c3ac062cc6c78c0914fc21d446a52ab5 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 6 Apr 2017 21:04:13 +0100 Subject: [PATCH 0087/5615] Update development version number to 1.2.2-SNAPSHOT --- core/pom.xml | 2 +- modules/jdbc-test/pom.xml | 2 +- modules/jdbc/pom.xml | 2 +- modules/mariadb/pom.xml | 2 +- modules/mysql/pom.xml | 2 +- modules/nginx/pom.xml | 2 +- modules/oracle-xe/pom.xml | 2 +- modules/postgresql/pom.xml | 2 +- modules/selenium/pom.xml | 2 +- modules/virtuoso/pom.xml | 2 +- pom.xml | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 913d1eab037..3469815e7ae 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -6,7 +6,7 @@ org.testcontainers testcontainers-parent - 1.2.1 + 1.2.2-SNAPSHOT ../pom.xml diff --git a/modules/jdbc-test/pom.xml b/modules/jdbc-test/pom.xml index e6ed410de68..d025dc7780f 100644 --- a/modules/jdbc-test/pom.xml +++ b/modules/jdbc-test/pom.xml @@ -6,7 +6,7 @@ org.testcontainers testcontainers-parent - 1.2.1 + 1.2.2-SNAPSHOT ../../pom.xml diff --git a/modules/jdbc/pom.xml b/modules/jdbc/pom.xml index 4d3e1d45eee..ff1ca92bedd 100644 --- a/modules/jdbc/pom.xml +++ b/modules/jdbc/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.2.1 + 1.2.2-SNAPSHOT ../../pom.xml diff --git a/modules/mariadb/pom.xml b/modules/mariadb/pom.xml index 6f9edaedc4e..efb117eb0c0 100644 --- a/modules/mariadb/pom.xml +++ b/modules/mariadb/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.2.1 + 1.2.2-SNAPSHOT ../../pom.xml diff --git a/modules/mysql/pom.xml b/modules/mysql/pom.xml index 16e6c10daa6..86b9f3e1764 100644 --- a/modules/mysql/pom.xml +++ b/modules/mysql/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.2.1 + 1.2.2-SNAPSHOT ../../pom.xml diff --git a/modules/nginx/pom.xml b/modules/nginx/pom.xml index 652790f1ed2..a61627f039d 100644 --- a/modules/nginx/pom.xml +++ b/modules/nginx/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.2.1 + 1.2.2-SNAPSHOT ../../pom.xml diff --git a/modules/oracle-xe/pom.xml b/modules/oracle-xe/pom.xml index e7d6cd5e813..06394588115 100644 --- a/modules/oracle-xe/pom.xml +++ b/modules/oracle-xe/pom.xml @@ -8,7 +8,7 @@ org.testcontainers testcontainers-parent - 1.2.1 + 1.2.2-SNAPSHOT ../../pom.xml diff --git a/modules/postgresql/pom.xml b/modules/postgresql/pom.xml index fd9d0fe0b05..d5cefbb0cb5 100644 --- a/modules/postgresql/pom.xml +++ b/modules/postgresql/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.2.1 + 1.2.2-SNAPSHOT ../../pom.xml diff --git a/modules/selenium/pom.xml b/modules/selenium/pom.xml index 602f54ab96a..676a7241082 100644 --- a/modules/selenium/pom.xml +++ b/modules/selenium/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.2.1 + 1.2.2-SNAPSHOT ../../pom.xml diff --git a/modules/virtuoso/pom.xml b/modules/virtuoso/pom.xml index 97f54c5081d..5de1e9716ac 100644 --- a/modules/virtuoso/pom.xml +++ b/modules/virtuoso/pom.xml @@ -4,7 +4,7 @@ org.testcontainers testcontainers-parent - 1.2.1 + 1.2.2-SNAPSHOT ../../pom.xml diff --git a/pom.xml b/pom.xml index ea5b0cfb114..c9a7a15eee2 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.2.1 + 1.2.2-SNAPSHOT pom From 4a4ff3da741d8cfb9351720f565bc84e555168fd Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 6 Apr 2017 21:05:26 +0100 Subject: [PATCH 0088/5615] Update shade-test dependencies --- shade-test/clashing-deps-jackson/pom.xml | 2 +- shade-test/clashing-deps-jersey/pom.xml | 2 +- shade-test/pom.xml | 2 +- shade-test/service-lookup-dropwizard/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/shade-test/clashing-deps-jackson/pom.xml b/shade-test/clashing-deps-jackson/pom.xml index cc4de2625b2..50917f67d10 100644 --- a/shade-test/clashing-deps-jackson/pom.xml +++ b/shade-test/clashing-deps-jackson/pom.xml @@ -5,7 +5,7 @@ shade-test org.testcontainers - 1.2.1-SNAPSHOT + 1.2.2-SNAPSHOT ../pom.xml 4.0.0 diff --git a/shade-test/clashing-deps-jersey/pom.xml b/shade-test/clashing-deps-jersey/pom.xml index 84768aa2441..652ad2474e0 100644 --- a/shade-test/clashing-deps-jersey/pom.xml +++ b/shade-test/clashing-deps-jersey/pom.xml @@ -5,7 +5,7 @@ shade-test org.testcontainers - 1.2.1-SNAPSHOT + 1.2.2-SNAPSHOT ../pom.xml 4.0.0 diff --git a/shade-test/pom.xml b/shade-test/pom.xml index c8ef9e4a8d1..21a5dcaf2dd 100644 --- a/shade-test/pom.xml +++ b/shade-test/pom.xml @@ -5,7 +5,7 @@ testcontainers-parent org.testcontainers - 1.2.1-SNAPSHOT + 1.2.2-SNAPSHOT ../pom.xml 4.0.0 diff --git a/shade-test/service-lookup-dropwizard/pom.xml b/shade-test/service-lookup-dropwizard/pom.xml index 09f318f74c1..b7410583d8a 100644 --- a/shade-test/service-lookup-dropwizard/pom.xml +++ b/shade-test/service-lookup-dropwizard/pom.xml @@ -5,7 +5,7 @@ shade-test org.testcontainers - 1.2.1-SNAPSHOT + 1.2.2-SNAPSHOT ../pom.xml 4.0.0 From c2eae85232653273748aaa4f5f765f48c28e0f87 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Mon, 10 Apr 2017 20:34:22 +0100 Subject: [PATCH 0089/5615] Add LogMessageWaitStrategy (#322) * Add LogMessageWaitStrategy * Change LogMessageWaitStrategy to use matches() * Change LogUtils to follow output since container start * Add 'times' parameter to WaitingConsumer and LogMessageWaitStrategy --- .../containers/output/WaitingConsumer.java | 31 ++++++++++--- .../wait/LogMessageWaitStrategy.java | 44 +++++++++++++++++++ .../org/testcontainers/utility/LogUtils.java | 3 +- .../wait/LogMessageWaitStrategyTest.java | 43 ++++++++++++++++++ 4 files changed, 115 insertions(+), 6 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/containers/wait/LogMessageWaitStrategy.java create mode 100644 core/src/test/java/org/testcontainers/junit/wait/LogMessageWaitStrategyTest.java diff --git a/core/src/main/java/org/testcontainers/containers/output/WaitingConsumer.java b/core/src/main/java/org/testcontainers/containers/output/WaitingConsumer.java index 7c9bcec644e..add8907595d 100644 --- a/core/src/main/java/org/testcontainers/containers/output/WaitingConsumer.java +++ b/core/src/main/java/org/testcontainers/containers/output/WaitingConsumer.java @@ -44,7 +44,7 @@ public LinkedBlockingDeque getFrames() { */ public void waitUntil(Predicate predicate) throws TimeoutException { // ~2.9 million centuries ought to be enough for anyone - waitUntil(predicate, Long.MAX_VALUE); + waitUntil(predicate, Long.MAX_VALUE, 1); } /** @@ -57,13 +57,30 @@ public void waitUntil(Predicate predicate) throws TimeoutException * @param limit maximum time to wait * @param limitUnit maximum time to wait (units) */ - public void waitUntil(Predicate predicate, long limit, TimeUnit limitUnit) throws TimeoutException { + public void waitUntil(Predicate predicate, int limit, TimeUnit limitUnit) throws TimeoutException { + waitUntil(predicate, limit, limitUnit, 1); + } + + /** + * Wait until any frame (usually, line) of output matches the provided predicate. + *

+ * Note that lines will often have a trailing newline character, and this is not stripped off before the + * predicate is tested. + * + * @param predicate a predicate to test against each frame + * @param limit maximum time to wait + * @param limitUnit maximum time to wait (units) + * @param times number of times the predicate has to match + */ + public void waitUntil(Predicate predicate, long limit, TimeUnit limitUnit, int times) throws TimeoutException { long expiry = limitUnit.toMillis(limit) + System.currentTimeMillis(); - waitUntil(predicate, expiry); + waitUntil(predicate, expiry, times); } - private void waitUntil(Predicate predicate, long expiry) throws TimeoutException { + private void waitUntil(Predicate predicate, long expiry, int times) throws TimeoutException { + + int numberOfMatches = 0; while (System.currentTimeMillis() < expiry) { try { OutputFrame frame = frames.pollLast(100, TimeUnit.MILLISECONDS); @@ -72,7 +89,11 @@ private void waitUntil(Predicate predicate, long expiry) throws Tim LOGGER.debug("{}: {}", frame.getType(), frame.getUtf8String()); if (predicate.test(frame)) { - return; + numberOfMatches++; + + if (numberOfMatches == times) { + return; + } } } diff --git a/core/src/main/java/org/testcontainers/containers/wait/LogMessageWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/LogMessageWaitStrategy.java new file mode 100644 index 00000000000..404731136d2 --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/wait/LogMessageWaitStrategy.java @@ -0,0 +1,44 @@ +package org.testcontainers.containers.wait; + +import org.testcontainers.containers.ContainerLaunchException; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.OutputFrame; +import org.testcontainers.containers.output.WaitingConsumer; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Predicate; + +/** + * Waits until containers logs expected content. + */ +public class LogMessageWaitStrategy extends GenericContainer.AbstractWaitStrategy { + private String regEx; + + private int times = 1; + + @Override + protected void waitUntilReady() { + WaitingConsumer waitingConsumer = new WaitingConsumer(); + container.followOutput(waitingConsumer); + + Predicate waitPredicate = outputFrame -> + outputFrame.getUtf8String().matches(regEx); + + try { + waitingConsumer.waitUntil(waitPredicate, startupTimeout.getSeconds(), TimeUnit.SECONDS, times); + } catch (TimeoutException e) { + throw new ContainerLaunchException("Timed out waiting for log output matching '" + regEx + "'"); + } + } + + public LogMessageWaitStrategy withRegEx(String regEx) { + this.regEx = regEx; + return this; + } + + public LogMessageWaitStrategy withTimes(int times) { + this.times = times; + return this; + } +} diff --git a/core/src/main/java/org/testcontainers/utility/LogUtils.java b/core/src/main/java/org/testcontainers/utility/LogUtils.java index 30db5a0a972..75a78002366 100644 --- a/core/src/main/java/org/testcontainers/utility/LogUtils.java +++ b/core/src/main/java/org/testcontainers/utility/LogUtils.java @@ -23,7 +23,8 @@ public void followOutput(DockerClient dockerClient, String containerId, Consumer consumer, OutputFrame.OutputType... types) { final LogContainerCmd cmd = dockerClient.logContainerCmd(containerId) - .withFollowStream(true); + .withFollowStream(true) + .withSince(0); final FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); for (OutputFrame.OutputType type : types) { diff --git a/core/src/test/java/org/testcontainers/junit/wait/LogMessageWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/LogMessageWaitStrategyTest.java new file mode 100644 index 00000000000..4596f546d15 --- /dev/null +++ b/core/src/test/java/org/testcontainers/junit/wait/LogMessageWaitStrategyTest.java @@ -0,0 +1,43 @@ +package org.testcontainers.junit.wait; + +import org.jetbrains.annotations.NotNull; +import org.junit.Test; +import org.testcontainers.containers.wait.LogMessageWaitStrategy; + +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Tests for {@link LogMessageWaitStrategy}. + */ +public class LogMessageWaitStrategyTest extends AbstractWaitStrategyTest { + + private static final String READY_MESSAGE = "I'm ready!"; + + @Test + public void testWaitUntilReady_Success() { + waitUntilReadyAndSucceed("echo -e \"" + READY_MESSAGE + "\";" + + "echo -e \"foobar\";" + + "echo -e \"" + READY_MESSAGE + "\";" + + "sleep 300"); + } + + @Test + public void testWaitUntilReady_Timeout() { + waitUntilReadyAndTimeout("echo -e \"" + READY_MESSAGE + "\";" + + "echo -e \"foobar\";" + + "sleep 300"); + } + + @NotNull + @Override + protected LogMessageWaitStrategy buildWaitStrategy(AtomicBoolean ready) { + + return new LogMessageWaitStrategy() { + @Override + protected void waitUntilReady() { + super.waitUntilReady(); + ready.set(true); + } + }.withRegEx(".*ready.*\\s").withTimes(2); + } +} From 7642f98582c806b59c1bee2eafb93b2f0f4af1ce Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 14 Apr 2017 10:52:58 +0100 Subject: [PATCH 0090/5615] Introduce an abstraction over files and classpath resources. (#313) This encapsulates all the complexity of generating a path that the Docker daemon is about to create a volume mount for. This should resolve a general problem with spaces in paths, which was seen in one particular form as #263. Use recursive copy for docker context TAR to allow contents of directories to be used in Dockerfiles and Docker Compose contexts --- core/pom.xml | 14 ++ .../containers/DockerComposeContainer.java | 18 +- .../images/builder/ImageFromDockerfile.java | 13 +- .../images/builder/Transferable.java | 32 +++- .../builder/traits/DockerfileTrait.java | 11 +- .../images/builder/traits/FilesTrait.java | 33 +--- .../images/builder/traits/StringsTrait.java | 15 +- .../testcontainers/utility/MountableFile.java | 166 +++++++++++++----- .../testcontainers/junit/DockerfileTest.java | 14 +- .../utility/DirectoryTarResourceTest.java | 71 ++++++++ .../utility/MountableFileTest.java | 12 +- .../test/resources/test-recursive-file.txt | 1 + core/testlib/META-INF/dummy_unique_name.txt | 1 + core/testlib/README.md | 3 + core/testlib/create_fakejar.sh | 5 + core/testlib/recursive/dir/content.txt | 1 + .../repo/fakejar/fakejar/0/fakejar-0.jar | Bin 0 -> 949 bytes .../repo/fakejar/fakejar/0/fakejar-0.pom | 9 + .../fakejar/fakejar/maven-metadata-local.xml | 12 ++ 19 files changed, 312 insertions(+), 119 deletions(-) create mode 100644 core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java create mode 100644 core/src/test/resources/test-recursive-file.txt create mode 100644 core/testlib/META-INF/dummy_unique_name.txt create mode 100644 core/testlib/README.md create mode 100755 core/testlib/create_fakejar.sh create mode 100644 core/testlib/recursive/dir/content.txt create mode 100644 core/testlib/repo/fakejar/fakejar/0/fakejar-0.jar create mode 100644 core/testlib/repo/fakejar/fakejar/0/fakejar-0.pom create mode 100644 core/testlib/repo/fakejar/fakejar/maven-metadata-local.xml diff --git a/core/pom.xml b/core/pom.xml index 3469815e7ae..9edcaefa9b2 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -82,6 +82,13 @@ + + + fakejar + fakejar + 0 + test + @@ -232,4 +239,11 @@ + + + + testlib + file://${project.basedir}/testlib/repo + + diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index ecd23770db6..b69e2abcf8f 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -8,7 +8,6 @@ import com.google.common.base.Splitter; import com.google.common.collect.Maps; import com.google.common.util.concurrent.Uninterruptibles; -import org.apache.commons.lang.SystemUtils; import org.junit.runner.Description; import org.rnorth.ducttape.ratelimits.RateLimiter; import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; @@ -32,6 +31,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.stream.Collectors.toList; import static org.testcontainers.containers.BindMode.READ_ONLY; import static org.testcontainers.containers.BindMode.READ_WRITE; @@ -196,7 +196,7 @@ private List listChildContainers() { .exec().stream() .filter(container -> Arrays.stream(container.getNames()).anyMatch(name -> name.startsWith("/" + identifier))) - .collect(Collectors.toList()); + .collect(toList()); } private void startAmbassadorContainers(Profiler profiler) { @@ -394,15 +394,13 @@ public ContainerisedDockerCompose(List composeFiles, String identifier) { // Map the docker compose file into the container final File dockerComposeBaseFile = composeFiles.get(0); final String pwd = dockerComposeBaseFile.getAbsoluteFile().getParentFile().getAbsolutePath(); - final String containerPwd; - if (SystemUtils.IS_OS_WINDOWS) { - containerPwd = PathUtils.createMinGWPath(pwd).substring(1); - } else { - containerPwd = pwd; - } + final String containerPwd = MountableFile.forHostPath(pwd).getResolvedPath(); - final List absoluteDockerComposeFiles = composeFiles.stream().map( - file -> containerPwd + "/" + file.getAbsoluteFile().getName()).collect(Collectors.toList()); + final List absoluteDockerComposeFiles = composeFiles.stream() + .map(File::getAbsolutePath) + .map(MountableFile::forHostPath) + .map(MountableFile::getResolvedPath) + .collect(toList()); final String composeFileEnvVariableValue = Joiner.on(File.pathSeparator).join(absoluteDockerComposeFiles); logger().debug("Set env COMPOSE_FILE={}", composeFileEnvVariableValue); addEnv(ENV_COMPOSE_FILE, composeFileEnvVariableValue); diff --git a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java index e51e6d003af..6d9427b61f3 100644 --- a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java +++ b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java @@ -9,7 +9,6 @@ import lombok.Cleanup; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; @@ -128,16 +127,12 @@ public void onNext(BuildResponseItem item) { profiler.start("Send context as TAR"); try (TarArchiveOutputStream tarArchive = new TarArchiveOutputStream(new GZIPOutputStream(out))) { + tarArchive.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); + for (Map.Entry entry : transferables.entrySet()) { - TarArchiveEntry tarEntry = new TarArchiveEntry(entry.getKey()); Transferable transferable = entry.getValue(); - tarEntry.setSize(transferable.getSize()); - tarEntry.setMode(transferable.getFileMode()); - - tarArchive.putArchiveEntry(tarEntry); - transferable.transferTo(tarArchive); - tarArchive.closeArchiveEntry(); - + final String destination = entry.getKey(); + transferable.transferTo(tarArchive, destination); } tarArchive.finish(); } diff --git a/core/src/main/java/org/testcontainers/images/builder/Transferable.java b/core/src/main/java/org/testcontainers/images/builder/Transferable.java index eb245172596..b68ae95ac43 100644 --- a/core/src/main/java/org/testcontainers/images/builder/Transferable.java +++ b/core/src/main/java/org/testcontainers/images/builder/Transferable.java @@ -1,16 +1,21 @@ package org.testcontainers.images.builder; -import java.io.OutputStream; +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; +import org.apache.commons.io.IOUtils; + +import java.io.IOException; public interface Transferable { int DEFAULT_FILE_MODE = 0100644; + int DEFAULT_DIR_MODE = 040755; /** * Get file mode. Default is 0100644. - * @see Transferable#DEFAULT_FILE_MODE * * @return file mode + * @see Transferable#DEFAULT_FILE_MODE */ default int getFileMode() { return DEFAULT_FILE_MODE; @@ -26,7 +31,26 @@ default int getFileMode() { /** * transfer content of this Transferable to the output stream. Must not close the stream. * - * @param outputStream stream to output + * @param tarArchiveOutputStream stream to output + * @param destination */ - void transferTo(OutputStream outputStream); + default void transferTo(TarArchiveOutputStream tarArchiveOutputStream, final String destination) { + TarArchiveEntry tarEntry = new TarArchiveEntry(destination); + tarEntry.setSize(getSize()); + tarEntry.setMode(getFileMode()); + + try { + tarArchiveOutputStream.putArchiveEntry(tarEntry); + IOUtils.write(getBytes(), tarArchiveOutputStream); + tarArchiveOutputStream.closeArchiveEntry(); + } catch (IOException e) { + throw new RuntimeException("Can't transfer " + getDescription(), e); + } + } + + default byte[] getBytes() { + return new byte[0]; + } + + String getDescription(); } diff --git a/core/src/main/java/org/testcontainers/images/builder/traits/DockerfileTrait.java b/core/src/main/java/org/testcontainers/images/builder/traits/DockerfileTrait.java index a4740220fc8..07ed13ed0fb 100644 --- a/core/src/main/java/org/testcontainers/images/builder/traits/DockerfileTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/traits/DockerfileTrait.java @@ -1,12 +1,9 @@ package org.testcontainers.images.builder.traits; import lombok.Getter; -import org.apache.commons.io.IOUtils; import org.testcontainers.images.builder.Transferable; import org.testcontainers.images.builder.dockerfile.DockerfileBuilder; -import java.io.IOException; -import java.io.OutputStream; import java.util.function.Consumer; /** @@ -33,12 +30,8 @@ public long getSize() { } @Override - public void transferTo(OutputStream outputStream) { - try { - IOUtils.write(getBytes(), outputStream); - } catch (IOException e) { - throw new RuntimeException("Can't transfer Dockerfile", e); - } + public String getDescription() { + return "Dockerfile: " + builder; } }); } diff --git a/core/src/main/java/org/testcontainers/images/builder/traits/FilesTrait.java b/core/src/main/java/org/testcontainers/images/builder/traits/FilesTrait.java index d837d360a75..882c0e4231b 100644 --- a/core/src/main/java/org/testcontainers/images/builder/traits/FilesTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/traits/FilesTrait.java @@ -1,11 +1,8 @@ package org.testcontainers.images.builder.traits; -import org.testcontainers.images.builder.Transferable; +import org.testcontainers.utility.MountableFile; import java.io.File; -import java.io.IOException; -import java.io.OutputStream; -import java.nio.file.Files; import java.nio.file.Path; /** @@ -19,31 +16,7 @@ default SELF withFileFromFile(String path, File file) { } default SELF withFileFromPath(String path, Path filePath) { - return ((SELF) this).withFileFromTransferable(path, new Transferable() { - - @Override - public long getSize() { - try { - return Files.size(filePath); - } catch (IOException e) { - throw new RuntimeException("Can't get size from " + filePath, e); - } - } - - @Override - public int getFileMode() { - return DEFAULT_FILE_MODE | (Files.isExecutable(filePath) ? 0755 : 0); - } - - @Override - public void transferTo(OutputStream outputStream) { - try { - Files.copy(filePath, outputStream); - } catch (IOException e) { - throw new RuntimeException("Can't transfer file " + filePath, e); - } - } - - }); + final MountableFile mountableFile = MountableFile.forHostPath(filePath); + return ((SELF) this).withFileFromTransferable(path, mountableFile); } } diff --git a/core/src/main/java/org/testcontainers/images/builder/traits/StringsTrait.java b/core/src/main/java/org/testcontainers/images/builder/traits/StringsTrait.java index 5a3f6bf0f95..51423068ec9 100644 --- a/core/src/main/java/org/testcontainers/images/builder/traits/StringsTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/traits/StringsTrait.java @@ -1,12 +1,9 @@ package org.testcontainers.images.builder.traits; -import org.apache.commons.io.IOUtils; +import lombok.Getter; import org.apache.commons.lang.StringUtils; import org.testcontainers.images.builder.Transferable; -import java.io.IOException; -import java.io.OutputStream; - /** * BuildContextBuilder's trait for String-based manipulations. * @@ -16,6 +13,7 @@ public interface StringsTrait & BuildContextBuil default SELF withFileFromString(String path, String content) { return ((SELF) this).withFileFromTransferable(path, new Transferable() { + @Getter byte[] bytes = content.getBytes(); @Override @@ -24,14 +22,9 @@ public long getSize() { } @Override - public void transferTo(OutputStream outputStream) { - try { - IOUtils.write(bytes, outputStream); - } catch (IOException e) { - throw new RuntimeException("Can't transfer string " + StringUtils.abbreviate(content, 100), e); - } + public String getDescription() { + return "String: " + StringUtils.abbreviate(content, 100); } - }); } } diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java index 31ee6008751..8fef823274c 100644 --- a/core/src/main/java/org/testcontainers/utility/MountableFile.java +++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java @@ -4,35 +4,34 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.apache.commons.lang.SystemUtils; import org.jetbrains.annotations.NotNull; +import org.testcontainers.images.builder.Transferable; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.net.URISyntaxException; +import java.io.*; import java.net.URL; import java.net.URLDecoder; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Enumeration; import java.util.HashSet; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; -import static lombok.AccessLevel.PRIVATE; +import static lombok.AccessLevel.PACKAGE; import static org.testcontainers.utility.PathUtils.recursiveDeleteDir; /** * An abstraction over files and classpath resources aimed at encapsulating all the complexity of generating * a path that the Docker daemon is about to create a volume mount for. */ -@RequiredArgsConstructor(access = PRIVATE) +@RequiredArgsConstructor(access = PACKAGE) @Slf4j -public class MountableFile { +public class MountableFile implements Transferable { private final String path; @@ -60,25 +59,13 @@ public static MountableFile forHostPath(@NotNull final String path) { } /** - * Obtain a path that the Docker daemon should be able to use to volume mount a file/resource - * into a container. If this is a classpath resource residing in a JAR, it will be extracted to - * a temporary location so that the Docker daemon is able to access it. + * Obtains a {@link MountableFile} corresponding to a file on the docker host filesystem. * - * @return a volume-mountable path. + * @param path the path to the resource + * @return a {@link MountableFile} that may be used to obtain a mountable path */ - private String resolvePath() { - String result; - if (path.contains(".jar!")) { - result = extractClassPathResourceToTempLocation(this.path); - } else { - result = unencodeResourceURIToFilePath(path); - } - - if (SystemUtils.IS_OS_WINDOWS) { - result = PathUtils.createMinGWPath(result); - } - - return result; + public static MountableFile forHostPath(final Path path) { + return new MountableFile(path.toAbsolutePath().toString()); } @NotNull @@ -105,18 +92,43 @@ private static URL getClasspathResource(@NotNull final String resourcePath, @Not } } - throw new IllegalArgumentException("Resource with path " + resourcePath + " could not be found on any of these classloaders: " + classLoaders); + throw new IllegalArgumentException("Resource with path " + resourcePath + " could not be found on any of these classloaders: " + classLoadersToSearch); } private static String unencodeResourceURIToFilePath(@NotNull final String resource) { try { // Convert any url-encoded characters (e.g. spaces) back into unencoded form - return new URI(resource).getPath(); - } catch (URISyntaxException e) { + return URLDecoder.decode(resource, Charsets.UTF_8.name()) + .replaceFirst("jar:", "") + .replaceFirst("file:", "") + .replaceAll("!.*", ""); + } catch (UnsupportedEncodingException e) { throw new IllegalStateException(e); } } + /** + * Obtain a path that the Docker daemon should be able to use to volume mount a file/resource + * into a container. If this is a classpath resource residing in a JAR, it will be extracted to + * a temporary location so that the Docker daemon is able to access it. + * + * @return a volume-mountable path. + */ + private String resolvePath() { + String result; + if (path.contains(".jar!")) { + result = extractClassPathResourceToTempLocation(this.path); + } else { + result = unencodeResourceURIToFilePath(path); + } + + if (SystemUtils.IS_OS_WINDOWS) { + result = PathUtils.createMinGWPath(result); + } + + return result; + } + /** * Extract a file or directory tree from a JAR file to a temporary location. * This allows Docker to mount classpath resources as files. @@ -129,13 +141,7 @@ private String extractClassPathResourceToTempLocation(final String hostPath) { //noinspection ResultOfMethodCallIgnored tmpLocation.delete(); - String jarPath = hostPath.replaceFirst("jar:", "").replaceFirst("file:", "").replaceAll("!.*", ""); - String urldecodedJarPath; - try { - urldecodedJarPath = URLDecoder.decode(jarPath, Charsets.UTF_8.name()); - } catch (UnsupportedEncodingException e) { - throw new IllegalArgumentException("Could not URLDecode path with UTF-8 encoding: " + hostPath, e); - } + String urldecodedJarPath = unencodeResourceURIToFilePath(hostPath); String internalPath = hostPath.replaceAll("[^!]*!/", ""); try (JarFile jarFile = new JarFile(urldecodedJarPath)) { @@ -164,9 +170,9 @@ private String extractClassPathResourceToTempLocation(final String hostPath) { @SuppressWarnings("ResultOfMethodCallIgnored") private void copyFromJarToLocation(final JarFile jarFile, - final JarEntry entry, - final String fromRoot, - final File toRoot) throws IOException { + final JarEntry entry, + final String fromRoot, + final File toRoot) throws IOException { String destinationName = entry.getName().replaceFirst(fromRoot, ""); File newFile = new File(toRoot, destinationName); @@ -193,4 +199,84 @@ private void copyFromJarToLocation(final JarFile jarFile, private void deleteOnExit(final Path path) { Runtime.getRuntime().addShutdownHook(new Thread(() -> recursiveDeleteDir(path))); } -} + + /** + * {@inheritDoc} + */ + @Override + public void transferTo(final TarArchiveOutputStream outputStream, String destinationPathInTar) { + recursiveTar(destinationPathInTar, this.getResolvedPath(), this.getResolvedPath(), outputStream); + } + + /* + * Recursively copies a file/directory into a TarArchiveOutputStream + */ + private void recursiveTar(String destination, String sourceRootDir, String sourceCurrentItem, TarArchiveOutputStream tarArchive) { + try { + final File sourceFile = new File(sourceCurrentItem).getCanonicalFile(); // e.g. /foo/bar/baz + final File sourceRootFile = new File(sourceRootDir).getCanonicalFile(); // e.g. /foo + final String relativePathToSourceFile = sourceRootFile.toPath().relativize(sourceFile.toPath()).toFile().toString(); // e.g. /bar/baz + + final TarArchiveEntry tarEntry = new TarArchiveEntry(sourceFile, destination + "/" + relativePathToSourceFile); // entry filename e.g. /xyz/bar/baz + + // TarArchiveEntry automatically sets the mode for file/directory, but we can update to ensure that the mode is set exactly (inc executable bits) + tarEntry.setMode(getUnixFileMode(sourceCurrentItem)); + tarArchive.putArchiveEntry(tarEntry); + + if (sourceFile.isFile()) { + Files.copy(sourceFile.toPath(), tarArchive); + } + // a directory entry merely needs to exist in the TAR file - there is no data stored yet + tarArchive.closeArchiveEntry(); + + final File[] children = sourceFile.listFiles(); + if (children != null) { + // recurse into child files/directories + for (final File child : children) { + recursiveTar(destination, sourceRootDir + File.separator, child.getCanonicalPath(), tarArchive); + } + } + } catch (IOException e) { + log.error("Error when copying TAR file entry: {}", sourceCurrentItem, e); + throw new UncheckedIOException(e); // fail fast + } + } + + @Override + public long getSize() { + + final File file = new File(this.getResolvedPath()); + if (file.isFile()) { + return file.length(); + } else { + return 0; + } + } + + @Override + public String getDescription() { + return this.getResolvedPath(); + } + + @Override + public int getFileMode() { + return getUnixFileMode(this.getResolvedPath()); + } + + private int getUnixFileMode(final String pathAsString) { + final Path path = Paths.get(pathAsString); + try { + return (int) Files.getAttribute(path, "unix:mode"); + } catch (IOException e) { + // fallback for non-posix environments + int mode = DEFAULT_FILE_MODE; + if (Files.isDirectory(path)) { + mode = DEFAULT_DIR_MODE; + } else if (Files.isExecutable(path)) { + mode |= 0111; // equiv to +x for user/group/others + } + + return mode; + } + } +} \ No newline at end of file diff --git a/core/src/test/java/org/testcontainers/junit/DockerfileTest.java b/core/src/test/java/org/testcontainers/junit/DockerfileTest.java index a2b11e5bc9d..7390b21fce2 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerfileTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerfileTest.java @@ -10,7 +10,6 @@ import org.testcontainers.images.builder.ImageFromDockerfile; import org.testcontainers.images.builder.Transferable; -import java.io.OutputStream; import java.util.Arrays; import java.util.List; import java.util.concurrent.TimeUnit; @@ -85,14 +84,21 @@ public long getSize() { } @Override - public int getFileMode() { - return 0123; + public byte[] getBytes() { + return new byte[0]; } @Override - public void transferTo(OutputStream outputStream) { + public String getDescription() { + return "test file"; + } + @Override + public int getFileMode() { + return 0123; } + + }) .withDockerfileFromBuilder(builder -> builder .from("alpine:3.2") diff --git a/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java b/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java new file mode 100644 index 00000000000..c4b719ea1ca --- /dev/null +++ b/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java @@ -0,0 +1,71 @@ +package org.testcontainers.utility; + +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.ToStringConsumer; +import org.testcontainers.containers.output.WaitingConsumer; +import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; +import org.testcontainers.images.builder.ImageFromDockerfile; + +import java.io.File; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; + +public class DirectoryTarResourceTest { + + @Test + public void simpleRecursiveFileTest() throws TimeoutException { + + WaitingConsumer wait = new WaitingConsumer(); + + final ToStringConsumer toString = new ToStringConsumer(); + + GenericContainer container = new GenericContainer( + new ImageFromDockerfile() + .withDockerfileFromBuilder(builder -> + builder.from("alpine:3.3") + .copy("/tmp/foo", "/foo") + .cmd("cat /foo/src/test/resources/test-recursive-file.txt") + .build() + ).withFileFromFile("/tmp/foo", new File("."))) // '.' is expected to be the project base directory, so all source code/resources should be copied in + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .withLogConsumer(wait.andThen(toString)); + + container.start(); + wait.waitUntilEnd(60, TimeUnit.SECONDS); + + final String results = toString.toUtf8String(); + + assertTrue("The container has a file that was copied in via a recursive copy", results.contains("Used for DirectoryTarResourceTest")); + } + + @Test + public void simpleRecursiveClasspathResourceTest() throws TimeoutException { + // This test combines the copying of classpath resources from JAR files with the recursive TAR approach, to allow JARed classpath resources to be copied in to an image + + WaitingConsumer wait = new WaitingConsumer(); + + final ToStringConsumer toString = new ToStringConsumer(); + + GenericContainer container = new GenericContainer( + new ImageFromDockerfile() + .withDockerfileFromBuilder(builder -> + builder.from("alpine:3.3") + .copy("/tmp/foo", "/foo") + .cmd("ls -lRt /foo") + .build() + ).withFileFromClasspath("/tmp/foo", "/recursive/dir")) // here we use /org/junit as a directory that really should exist on the classpath + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .withLogConsumer(wait.andThen(toString)); + + container.start(); + wait.waitUntilEnd(60, TimeUnit.SECONDS); + + final String results = toString.toUtf8String(); + + // ExternalResource.class is known to exist in a subdirectory of /org/junit so should be successfully copied in + assertTrue("The container has a file that was copied in via a recursive copy from a JAR resource", results.contains("content.txt")); + } +} diff --git a/core/src/test/java/org/testcontainers/utility/MountableFileTest.java b/core/src/test/java/org/testcontainers/utility/MountableFileTest.java index e42d92f07c1..5ac1a1c0ed8 100644 --- a/core/src/test/java/org/testcontainers/utility/MountableFileTest.java +++ b/core/src/test/java/org/testcontainers/utility/MountableFileTest.java @@ -29,7 +29,14 @@ public void forClasspathResourceWithAbsolutePath() throws Exception { @Test public void forClasspathResourceFromJar() throws Exception { - final MountableFile mountableFile = MountableFile.forClasspathResource("docker-java.properties"); + final MountableFile mountableFile = MountableFile.forClasspathResource("META-INF/dummy_unique_name.txt"); + + performChecks(mountableFile); + } + + @Test + public void forClasspathResourceFromJarWithAbsolutePath() throws Exception { + final MountableFile mountableFile = MountableFile.forClasspathResource("/META-INF/dummy_unique_name.txt"); performChecks(mountableFile); } @@ -49,7 +56,8 @@ public void forHostPathWithSpaces() throws Exception { performChecks(mountableFile); - assertTrue("The resolved path contains the original space", mountableFile.getResolvedPath().contains(" "));assertFalse("The resolved path does not contain an escaped space", mountableFile.getResolvedPath().contains("\\ ")); + assertTrue("The resolved path contains the original space", mountableFile.getResolvedPath().contains(" ")); + assertFalse("The resolved path does not contain an escaped space", mountableFile.getResolvedPath().contains("\\ ")); } /* diff --git a/core/src/test/resources/test-recursive-file.txt b/core/src/test/resources/test-recursive-file.txt new file mode 100644 index 00000000000..067480d972b --- /dev/null +++ b/core/src/test/resources/test-recursive-file.txt @@ -0,0 +1 @@ +Used for DirectoryTarResourceTest \ No newline at end of file diff --git a/core/testlib/META-INF/dummy_unique_name.txt b/core/testlib/META-INF/dummy_unique_name.txt new file mode 100644 index 00000000000..48162f49928 --- /dev/null +++ b/core/testlib/META-INF/dummy_unique_name.txt @@ -0,0 +1 @@ +This is a file inside a JAR archive \ No newline at end of file diff --git a/core/testlib/README.md b/core/testlib/README.md new file mode 100644 index 00000000000..437680a18fd --- /dev/null +++ b/core/testlib/README.md @@ -0,0 +1,3 @@ +This directory contains a synthetic JAR (`fakejar.jar`) that is needed for `org.testcontainers.utility.MountableFileTest`. + +The `create_fakejar.sh` script may be used to recreate it. \ No newline at end of file diff --git a/core/testlib/create_fakejar.sh b/core/testlib/create_fakejar.sh new file mode 100755 index 00000000000..e944cec94bc --- /dev/null +++ b/core/testlib/create_fakejar.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +rm fakejar.jar +zip -r fakejar.jar META-INF/ recursive/ +mv fakejar.jar repo/fakejar/fakejar/0/fakejar-0.jar \ No newline at end of file diff --git a/core/testlib/recursive/dir/content.txt b/core/testlib/recursive/dir/content.txt new file mode 100644 index 00000000000..d4e6444e225 --- /dev/null +++ b/core/testlib/recursive/dir/content.txt @@ -0,0 +1 @@ +This is example content to show recursive mounting of files from inside a JAR archive. \ No newline at end of file diff --git a/core/testlib/repo/fakejar/fakejar/0/fakejar-0.jar b/core/testlib/repo/fakejar/fakejar/0/fakejar-0.jar new file mode 100644 index 0000000000000000000000000000000000000000..9cd86a3c13747f306a3a5565d1f7b70f9e1153f2 GIT binary patch literal 949 zcmWIWW@h1H0D(zMyS%^*D8b1f!{F;0;;8HC=cXST!pXqAlxbY@D3!uivN9*P?^ER{Fwh=Of~IKFH-s^htU zHW#HPmlhRgmZd^mZ}0@C>mg=vu0S<|55%p!<6LAcFcMm-~j+K5=#K^o;t|epuod=A@m>1y9ZYn7cw@v&T`1UwWv@3r1ieVo01pl zf88y($!h|O)5FksiKmI@_!qvOkg#(BYpb+nS0;P+XHSnC*JMu?%`N}Q7~svwB*%;^ zswIH#1OWktw~inhDdJfn5swk+AmeaFJH$9(0%2Iv=!|R}jzj>o1C$7`+JP&Ak)6C3 zXa{n11I+_PHy-maBOBQ~MxfIX=0Tz!XeKD0u$qZ0;*rC_6T?iDWPr + + 4.0.0 + fakejar + fakejar + 0 + POM was created from install:install-file + diff --git a/core/testlib/repo/fakejar/fakejar/maven-metadata-local.xml b/core/testlib/repo/fakejar/fakejar/maven-metadata-local.xml new file mode 100644 index 00000000000..cca5c37c2f1 --- /dev/null +++ b/core/testlib/repo/fakejar/fakejar/maven-metadata-local.xml @@ -0,0 +1,12 @@ + + + fakejar + fakejar + + 0 + + 0 + + 20170414082010 + + From 764f9f8a2d06681ca5ceffe0f69960d99455dff5 Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 14 Apr 2017 11:45:50 +0100 Subject: [PATCH 0091/5615] Fix fakejar resolution - add missing files --- .../images/RemoteDockerImage.java | 11 ++++- .../junit/PullCachePerformanceTest.java | 48 +++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 core/src/test/java/org/testcontainers/junit/PullCachePerformanceTest.java diff --git a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java index 01ba3ad6894..fb686c82e19 100644 --- a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java +++ b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java @@ -1,6 +1,7 @@ package org.testcontainers.images; import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.command.ListImagesCmd; import com.github.dockerjava.api.exception.DockerClientException; import com.github.dockerjava.api.model.Image; import com.github.dockerjava.core.command.PullImageResultCallback; @@ -20,7 +21,7 @@ public class RemoteDockerImage extends LazyFuture { - private static final Set AVAILABLE_IMAGE_NAME_CACHE = new HashSet<>(); + public static final Set AVAILABLE_IMAGE_NAME_CACHE = new HashSet<>(); private final String dockerImageName; @@ -55,7 +56,13 @@ protected final String resolve() { } // Update the cache - List updatedImages = dockerClient.listImagesCmd().exec(); + ListImagesCmd listImagesCmd = dockerClient.listImagesCmd(); + + if (Boolean.parseBoolean(System.getProperty("useFilter"))) { + listImagesCmd = listImagesCmd.withImageNameFilter(dockerImageName); + } + + List updatedImages = listImagesCmd.exec(); for (Image image : updatedImages) { if (image.getRepoTags() != null) { Collections.addAll(AVAILABLE_IMAGE_NAME_CACHE, image.getRepoTags()); diff --git a/core/src/test/java/org/testcontainers/junit/PullCachePerformanceTest.java b/core/src/test/java/org/testcontainers/junit/PullCachePerformanceTest.java new file mode 100644 index 00000000000..82b8a373cd3 --- /dev/null +++ b/core/src/test/java/org/testcontainers/junit/PullCachePerformanceTest.java @@ -0,0 +1,48 @@ +package org.testcontainers.junit; + +import com.google.common.base.Stopwatch; +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.images.RemoteDockerImage; + +import java.util.concurrent.TimeUnit; + +/** + * Created by rnorth on 14/04/2017. + */ +public class PullCachePerformanceTest { + + @Test + public void testWithoutNameFilter() { + System.setProperty("useFilter", "false"); + + doLoop("without"); + } + + @Test + public void testWithNameFilter() { + System.setProperty("useFilter", "true"); + + doLoop("with"); + } + + private void doLoop(String name) { + + // warmup + try (GenericContainer container = new GenericContainer<>("alpine:3.2").withCommand("date")) { + container.start(); + } + + // run + Stopwatch stopwatch = Stopwatch.createStarted(); + for (int i = 0; i < 50; i++) { + RemoteDockerImage.AVAILABLE_IMAGE_NAME_CACHE.clear(); + + try (GenericContainer container = new GenericContainer<>("alpine:3.2").withCommand("date")) { + container.start(); + } + } + stopwatch.stop(); + System.err.printf("%20s: %s \n", name, stopwatch.elapsed(TimeUnit.SECONDS)); + } +} From 79ecfc830fb5c9bf54d0bb91ef1149e177bbfcf5 Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 17 Apr 2017 06:21:44 +0100 Subject: [PATCH 0092/5615] Remove accidentally committed test --- .../junit/PullCachePerformanceTest.java | 48 ------------------- 1 file changed, 48 deletions(-) delete mode 100644 core/src/test/java/org/testcontainers/junit/PullCachePerformanceTest.java diff --git a/core/src/test/java/org/testcontainers/junit/PullCachePerformanceTest.java b/core/src/test/java/org/testcontainers/junit/PullCachePerformanceTest.java deleted file mode 100644 index 82b8a373cd3..00000000000 --- a/core/src/test/java/org/testcontainers/junit/PullCachePerformanceTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.testcontainers.junit; - -import com.google.common.base.Stopwatch; -import org.junit.Test; -import org.testcontainers.containers.GenericContainer; -import org.testcontainers.images.RemoteDockerImage; - -import java.util.concurrent.TimeUnit; - -/** - * Created by rnorth on 14/04/2017. - */ -public class PullCachePerformanceTest { - - @Test - public void testWithoutNameFilter() { - System.setProperty("useFilter", "false"); - - doLoop("without"); - } - - @Test - public void testWithNameFilter() { - System.setProperty("useFilter", "true"); - - doLoop("with"); - } - - private void doLoop(String name) { - - // warmup - try (GenericContainer container = new GenericContainer<>("alpine:3.2").withCommand("date")) { - container.start(); - } - - // run - Stopwatch stopwatch = Stopwatch.createStarted(); - for (int i = 0; i < 50; i++) { - RemoteDockerImage.AVAILABLE_IMAGE_NAME_CACHE.clear(); - - try (GenericContainer container = new GenericContainer<>("alpine:3.2").withCommand("date")) { - container.start(); - } - } - stopwatch.stop(); - System.err.printf("%20s: %s \n", name, stopwatch.elapsed(TimeUnit.SECONDS)); - } -} From f8fdccbc12e59516b3e7495ebe73714917fde997 Mon Sep 17 00:00:00 2001 From: David Byron Date: Sun, 30 Apr 2017 00:15:41 -0700 Subject: [PATCH 0093/5615] Upgrade docker-java to version 3.0.9 (#332) --- core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/pom.xml b/core/pom.xml index 9edcaefa9b2..83c212c7ff6 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -18,7 +18,7 @@ com.github.docker-java docker-java - 3.0.7 + 3.0.9 From 36613b88bcf3ab60ad046bfde7ee2eabd538dacf Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Sun, 30 Apr 2017 20:48:36 +0200 Subject: [PATCH 0094/5615] Change PostgreSQLContainer wait behaviour (fix #317) (#327) * Update to latest postgres jdbc driver to make error logs visible * Change PostgreSQLContainer to use WaitingConsumer for waiting instead of SQL query (#317) Fix #317 * Make OutputFrame return empty String if containing no bytes * Change PostgreSQLContainer to use LogMessageWaitStrategy (#317) Fix #317 --- .../containers/output/OutputFrame.java | 2 +- modules/jdbc-test/pom.xml | 2 +- .../containers/PostgreSQLContainer.java | 19 +++++++++++++++++-- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/output/OutputFrame.java b/core/src/main/java/org/testcontainers/containers/output/OutputFrame.java index 9948bb451d1..d5ddcada8c8 100644 --- a/core/src/main/java/org/testcontainers/containers/output/OutputFrame.java +++ b/core/src/main/java/org/testcontainers/containers/output/OutputFrame.java @@ -30,7 +30,7 @@ public byte[] getBytes() { public String getUtf8String() { if (bytes == null) { - return null; + return ""; } return new String(bytes, Charsets.UTF_8); diff --git a/modules/jdbc-test/pom.xml b/modules/jdbc-test/pom.xml index d025dc7780f..2ce69f20476 100644 --- a/modules/jdbc-test/pom.xml +++ b/modules/jdbc-test/pom.xml @@ -34,7 +34,7 @@ org.postgresql postgresql - 9.3-1101-jdbc41 + 42.0.0 test diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index 725f73d639c..f8229f4131c 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -1,11 +1,17 @@ package org.testcontainers.containers; +import org.testcontainers.containers.wait.LogMessageWaitStrategy; + +import java.time.Duration; + +import static java.time.temporal.ChronoUnit.SECONDS; + /** * @author richardnorth */ public class PostgreSQLContainer> extends JdbcDatabaseContainer { - public static final String NAME = "postgresql"; - public static final String IMAGE = "postgres"; + static final String NAME = "postgresql"; + static final String IMAGE = "postgres"; public static final Integer POSTGRESQL_PORT = 5432; private String databaseName = "test"; private String username = "test"; @@ -17,6 +23,10 @@ public PostgreSQLContainer() { public PostgreSQLContainer(final String dockerImageName) { super(dockerImageName); + this.waitStrategy = new LogMessageWaitStrategy() + .withRegEx(".*database system is ready to accept connections.*\\s") + .withTimes(2) + .withStartupTimeout(Duration.of(60, SECONDS)); } @Override @@ -73,4 +83,9 @@ public SELF withPassword(final String password) { this.password = password; return self(); } + + @Override + protected void waitUntilContainerStarted() { + getWaitStrategy().waitUntilReady(this); + } } From b369ab391c9ef55afb836f99cc2015b7e8527757 Mon Sep 17 00:00:00 2001 From: David Byron Date: Sun, 30 Apr 2017 11:49:33 -0700 Subject: [PATCH 0095/5615] use version 1.2.3 of logback-classic to stay up to date (#333) * use version 1.2.3 of logback-classic to stay up to date * bring slf4j up to date, at least because logback >= 1.1.4 requires slf4j >= 1.7.16 according to https://logback.qos.ch/news.html --- core/pom.xml | 2 +- pom.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 83c212c7ff6..3251ede0877 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -47,7 +47,7 @@ org.slf4j slf4j-ext - 1.7.2 + 1.7.25 diff --git a/pom.xml b/pom.xml index c9a7a15eee2..89e073fcd1b 100644 --- a/pom.xml +++ b/pom.xml @@ -87,12 +87,12 @@ org.slf4j slf4j-api - 1.7.7 + 1.7.25 ch.qos.logback logback-classic - 1.1.2 + 1.2.3 test From c03fba329264684eb527abccdbc76a75e622fe46 Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 1 May 2017 20:30:00 +0100 Subject: [PATCH 0096/5615] Switch browser containers to use LogMessageWaitStrategy (#328) * Resolve #326, making browser container startup timeout configurable. Reduces default browser container startup timeout to 15s, down from an unnecessarily high 120s. This also replaces inheritance-based `waitUntilContainerStarted` with composition-based `WaitStrategy`. The new `LogMessageWaitStrategy` allows us to wait based upon the Selenium container log output, rather than the relatively expensive process of repeatedly trying to create and connect a `RemoteWebDriver` instance. * Resolve shading-related inheritance issues with BrowserWebDriverContainer This change makes docker-java NO LONGER SHADED into testcontainers core JAR * Trim WaitingConsumer output frame text to eliminate double-newline effect --- core/pom.xml | 7 ++-- .../containers/GenericContainer.java | 3 +- .../containers/output/WaitingConsumer.java | 3 +- modules/selenium/pom.xml | 10 +++++- .../containers/BrowserWebDriverContainer.java | 35 ++++++++----------- ...stomWaitTimeoutWebDriverContainerTest.java | 27 ++++++++++++++ pom.xml | 1 + 7 files changed, 58 insertions(+), 28 deletions(-) create mode 100644 modules/selenium/src/test/java/org/testcontainers/junit/CustomWaitTimeoutWebDriverContainerTest.java diff --git a/core/pom.xml b/core/pom.xml index 3251ede0877..7d6eb11395e 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -18,7 +18,8 @@ com.github.docker-java docker-java - 3.0.9 + ${docker-java.version} + compile @@ -151,10 +152,6 @@ com.fasterxml.jackson org.testcontainers.shaded.com.fasterxml.jackson - - com.github.dockerjava - org.testcontainers.shaded.com.github.dockerjava - jersey.repackaged org.testcontainers.shaded.jersey.repackaged diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 167ea5864b9..8055650ef41 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -867,8 +867,9 @@ public ExecResult execInContainer(Charset outputCharset, String... command) * * @param attempts number of attempts */ - public void withStartupAttempts(int attempts) { + public SELF withStartupAttempts(int attempts) { this.startupAttempts = attempts; + return self(); } @Override diff --git a/core/src/main/java/org/testcontainers/containers/output/WaitingConsumer.java b/core/src/main/java/org/testcontainers/containers/output/WaitingConsumer.java index add8907595d..3cd0a7cecca 100644 --- a/core/src/main/java/org/testcontainers/containers/output/WaitingConsumer.java +++ b/core/src/main/java/org/testcontainers/containers/output/WaitingConsumer.java @@ -86,7 +86,8 @@ private void waitUntil(Predicate predicate, long expiry, int times) OutputFrame frame = frames.pollLast(100, TimeUnit.MILLISECONDS); if (frame != null) { - LOGGER.debug("{}: {}", frame.getType(), frame.getUtf8String()); + final String trimmedFrameText = frame.getUtf8String().replaceFirst("\n$", ""); + LOGGER.debug("{}: {}", frame.getType(), trimmedFrameText); if (predicate.test(frame)) { numberOfMatches++; diff --git a/modules/selenium/pom.xml b/modules/selenium/pom.xml index 676a7241082..76aa623396c 100644 --- a/modules/selenium/pom.xml +++ b/modules/selenium/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 @@ -18,6 +19,12 @@ testcontainers ${project.version} + + com.github.docker-java + docker-java + ${docker-java.version} + provided + com.google.guava @@ -39,6 +46,7 @@ org.mortbay.jetty jetty 6.1.25 + test diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index 1798ec41116..2003c91894c 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -1,28 +1,28 @@ package org.testcontainers.containers; -import com.google.common.util.concurrent.Uninterruptibles; +import com.github.dockerjava.api.command.InspectContainerResponse; import org.jetbrains.annotations.Nullable; import org.junit.runner.Description; import org.openqa.selenium.remote.BrowserType; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.remote.RemoteWebDriver; -import org.rnorth.ducttape.unreliables.Unreliables; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testcontainers.containers.traits.LinkableContainer; import org.testcontainers.containers.traits.VncService; +import org.testcontainers.containers.wait.LogMessageWaitStrategy; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.text.SimpleDateFormat; +import java.time.Duration; import java.util.ArrayList; import java.util.Collection; import java.util.Date; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; import static com.google.common.base.Preconditions.checkState; +import static java.time.temporal.ChronoUnit.SECONDS; /** * A chrome/firefox/custom container based on SeleniumHQ's standalone container sets. @@ -55,7 +55,9 @@ public class BrowserWebDriverContainer { - Uninterruptibles.sleepUninterruptibly(backoff.getAndUpdate(current -> (int)(current * 1.5)), TimeUnit.MILLISECONDS); - RemoteWebDriver driver = new RemoteWebDriver(getSeleniumAddress(), desiredCapabilities); - driver.getCurrentUrl(); - - logger().info("Obtained a connection to container ({})", BrowserWebDriverContainer.this.getSeleniumAddress()); - return driver; - }); - + protected void containerIsStarted(InspectContainerResponse containerInfo) { if (recordingMode != VncRecordingMode.SKIP) { LOGGER.debug("Starting VNC recording"); - VncRecordingSidekickContainer recordingSidekickContainer = new VncRecordingSidekickContainer<>(this); + + // Use multiple startup attempts due to race condition between Selenium being available and VNC being available + VncRecordingSidekickContainer recordingSidekickContainer = new VncRecordingSidekickContainer<>(this) + .withStartupAttempts(3); + recordingSidekickContainer.start(); currentVncRecordings.add(recordingSidekickContainer); } + this.driver = new RemoteWebDriver(getSeleniumAddress(), desiredCapabilities); } /** diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/CustomWaitTimeoutWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/CustomWaitTimeoutWebDriverContainerTest.java new file mode 100644 index 00000000000..d189ba34a24 --- /dev/null +++ b/modules/selenium/src/test/java/org/testcontainers/junit/CustomWaitTimeoutWebDriverContainerTest.java @@ -0,0 +1,27 @@ +package org.testcontainers.junit; + +import org.junit.Rule; +import org.junit.Test; +import org.openqa.selenium.remote.DesiredCapabilities; +import org.testcontainers.containers.BrowserWebDriverContainer; + +import java.io.IOException; +import java.time.Duration; + +import static java.time.temporal.ChronoUnit.SECONDS; + +/** + * + */ +public class CustomWaitTimeoutWebDriverContainerTest extends BaseWebDriverContainerTest { + + @Rule + public BrowserWebDriverContainer chromeWithCustomTimeout = new BrowserWebDriverContainer<>() + .withDesiredCapabilities(DesiredCapabilities.chrome()) + .withStartupTimeout(Duration.of(30, SECONDS)); + + @Test + public void simpleTest() throws IOException { + doSimpleWebdriverTest(chromeWithCustomTimeout); + } +} diff --git a/pom.xml b/pom.xml index 89e073fcd1b..4c4310cef0b 100644 --- a/pom.xml +++ b/pom.xml @@ -157,6 +157,7 @@ UTF-8 + 3.0.9 From 88ec3723bf1a47670e1d9ff9f0f260758eaa0fe7 Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 1 May 2017 20:30:40 +0100 Subject: [PATCH 0097/5615] Add audit logging, to capture structured information about actions (#329) Add audit logging, to capture structured information about important actions via SLF4J. This is intended for use in environments where docker daemons are shared between large numbers of test executions, and where there is a need to collect logs to diagnose faults. --- .../containers/DockerComposeContainer.java | 2 + .../AuditLoggingDockerClient.java | 121 ++++++++++++++++++ .../DockerClientProviderStrategy.java | 2 +- .../testcontainers/utility/AuditLogger.java | 67 ++++++++++ 4 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/org/testcontainers/dockerclient/AuditLoggingDockerClient.java create mode 100644 core/src/main/java/org/testcontainers/utility/AuditLogger.java diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index b69e2abcf8f..eeca26b413a 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -429,6 +429,8 @@ public void start() { Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); } logger().info("Docker Compose has finished running"); + + AuditLogger.doComposeLog(this.getCommandParts(), this.getEnv()); } } diff --git a/core/src/main/java/org/testcontainers/dockerclient/AuditLoggingDockerClient.java b/core/src/main/java/org/testcontainers/dockerclient/AuditLoggingDockerClient.java new file mode 100644 index 00000000000..4250b884a74 --- /dev/null +++ b/core/src/main/java/org/testcontainers/dockerclient/AuditLoggingDockerClient.java @@ -0,0 +1,121 @@ +package org.testcontainers.dockerclient; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.command.*; +import lombok.experimental.Delegate; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.Proxy; +import java.util.function.BiConsumer; + +import static org.testcontainers.utility.AuditLogger.doLog; + +/** + * Wrapper for {@link DockerClient} to facilitate 'audit logging' of potentially destruction actions using + * {@link org.testcontainers.utility.AuditLogger}. + */ +@Slf4j +@SuppressWarnings("unchecked") +public class AuditLoggingDockerClient implements DockerClient { + + @Delegate(excludes = InterceptedMethods.class) + private final DockerClient wrappedClient; + + public AuditLoggingDockerClient(DockerClient wrappedClient) { + this.wrappedClient = wrappedClient; + } + + @Override + public CreateContainerCmd createContainerCmd(@NotNull String image) { + return wrappedCommand(CreateContainerCmd.class, + wrappedClient.createContainerCmd(image), + (cmd, res) -> doLog("CREATE", image, res.getId(), cmd), + (cmd, e) -> doLog("CREATE", image, null, cmd, e)); + + } + + @Override + public StartContainerCmd startContainerCmd(@NotNull String containerId) { + return wrappedCommand(StartContainerCmd.class, + wrappedClient.startContainerCmd(containerId), + (cmd, res) -> doLog("START", null, containerId, cmd), + (cmd, e) -> doLog("START", null, containerId, cmd, e)); + } + + @Override + public RemoveContainerCmd removeContainerCmd(@NotNull String containerId) { + return wrappedCommand(RemoveContainerCmd.class, + wrappedClient.removeContainerCmd(containerId), + (cmd, res) -> doLog("REMOVE", null, containerId, cmd), + (cmd, e) -> doLog("REMOVE", null, containerId, cmd, e)); + } + + @Override + public StopContainerCmd stopContainerCmd(@NotNull String containerId) { + return wrappedCommand(StopContainerCmd.class, + wrappedClient.stopContainerCmd(containerId), + (cmd, res) -> doLog("STOP", null, containerId, cmd), + (cmd, e) -> doLog("STOP", null, containerId, cmd, e)); + } + + @Override + public KillContainerCmd killContainerCmd(@NotNull String containerId) { + return wrappedCommand(KillContainerCmd.class, + wrappedClient.killContainerCmd(containerId), + (cmd, res) -> doLog("KILL", null, containerId, cmd), + (cmd, e) -> doLog("KILL", null, containerId, cmd, e)); + } + + @Override + public CreateNetworkCmd createNetworkCmd() { + return wrappedCommand(CreateNetworkCmd.class, + wrappedClient.createNetworkCmd(), + (cmd, res) -> doLog("CREATE_NETWORK", null, null, cmd), + (cmd, e) -> doLog("CREATE_NETWORK", null, null, cmd, e)); + } + + @Override + public RemoveNetworkCmd removeNetworkCmd(@NotNull String networkId) { + return wrappedCommand(RemoveNetworkCmd.class, + wrappedClient.removeNetworkCmd(networkId), + (cmd, res) -> doLog("REMOVE_NETWORK", null, null, cmd), + (cmd, e) -> doLog("REMOVE_NETWORK", null, null, cmd, e)); + } + + private , R> T wrappedCommand(Class clazz, + T cmd, + BiConsumer successConsumer, + BiConsumer failureConsumer) { + + return (T) Proxy.newProxyInstance( + clazz.getClassLoader(), + new Class[]{clazz}, + (proxy, method, args) -> { + + if (method.getName().equals("exec")) { + try { + R r = (R) method.invoke(cmd, args); + successConsumer.accept(cmd, r); + return r; + } catch (Exception e) { + failureConsumer.accept(cmd, e); + throw e; + } + } else { + return method.invoke(cmd, args); + } + }); + } + + @SuppressWarnings("unused") + private interface InterceptedMethods { + CreateContainerCmd createContainerCmd(String image); + StartContainerCmd startContainerCmd(String containerId); + RemoveContainerCmd removeContainerCmd(String containerId); + StopContainerCmd stopContainerCmd(String containerId); + KillContainerCmd killContainerCmd(String containerId); + CreateNetworkCmd createNetworkCmd(); + RemoveNetworkCmd removeNetworkCmd(String networkId); + } +} diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index 7c1a65525c9..48b556d68a5 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -94,7 +94,7 @@ public static DockerClientProviderStrategy getFirstValidStrategy(List + * Users should configure their test logging to apply appropriate filters/storage so that these logs are + * captured appropriately. + */ +@Slf4j +@UtilityClass +public class AuditLogger { + + private static final ObjectMapper objectMapper = new ObjectMapper(); + public static final String MDC_PREFIX = AuditLogger.class.getCanonicalName(); + + public static void doLog(String action, String image, String containerId, DockerCmd cmd) { + doLog(action, image, containerId, cmd, null); + } + + public static void doLog(String action, String image, String containerId, DockerCmd cmd, @Nullable Exception e) { + MDC.put(MDC_PREFIX + ".Action", action); + MDC.put(MDC_PREFIX + ".Image", image); + MDC.put(MDC_PREFIX + ".ContainerId", containerId); + try { + MDC.put(MDC_PREFIX + ".Command", objectMapper.writeValueAsString(cmd)); + } catch (JsonProcessingException ignored) { + } + + if (e != null) { + MDC.put(MDC_PREFIX + ".Exception", e.getLocalizedMessage()); + log.trace("{} action with image: {}, containerId: {}", action, image, containerId, e); + } else { + log.trace("{} action with image: {}, containerId: {}", action, image, containerId); + } + + MDC.remove(MDC_PREFIX + ".Action"); + MDC.remove(MDC_PREFIX + ".Image"); + MDC.remove(MDC_PREFIX + ".ContainerId"); + MDC.remove(MDC_PREFIX + ".Command"); + MDC.remove(MDC_PREFIX + ".Exception"); + } + + public static void doComposeLog(String[] commandParts, List env) { + MDC.put(MDC_PREFIX + ".Action", "COMPOSE"); + final String command = StringUtils.join(commandParts, ' '); + MDC.put(MDC_PREFIX + ".Compose.Command", command); + MDC.put(MDC_PREFIX + ".Compose.Env", env.toString()); + + log.trace("COMPOSE action with command: {}, env: {}", command, env); + + MDC.remove(MDC_PREFIX + ".Action"); + MDC.remove(MDC_PREFIX + ".Compose.Command"); + MDC.remove(MDC_PREFIX + ".Compose.Env"); + } +} From c76b8a14a0511b9f356f9f2bbf9438b58d9693a6 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 23 May 2017 07:35:07 +0100 Subject: [PATCH 0098/5615] Update index.md --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index db99e34c2ef..d0489f697f7 100644 --- a/docs/index.md +++ b/docs/index.md @@ -31,7 +31,7 @@ See [LICENSE](https://raw.githubusercontent.com/testcontainers/testcontainers-ja This project includes a modified class (ScriptUtils) taken from the Spring JDBC project, adapted under the terms of the Apache license. Copyright for that class remains with the original authors. -This project was initially inspired by a [gist](https://gist.github.com/mosheeshel/c427b43c36b256731a0b) by Mosche Eschel. +This project was initially inspired by a [gist](https://gist.github.com/mosheeshel/c427b43c36b256731a0b) by [Moshe Eschel](https://github.com/mosheeshel). ## Contributing From 96bf1880ef6c2ca26eb63586308f38edc34b1782 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 23 May 2017 07:39:52 +0100 Subject: [PATCH 0099/5615] Update index.md --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index d0489f697f7..24b6e05af3e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -31,7 +31,7 @@ See [LICENSE](https://raw.githubusercontent.com/testcontainers/testcontainers-ja This project includes a modified class (ScriptUtils) taken from the Spring JDBC project, adapted under the terms of the Apache license. Copyright for that class remains with the original authors. -This project was initially inspired by a [gist](https://gist.github.com/mosheeshel/c427b43c36b256731a0b) by [Moshe Eschel](https://github.com/mosheeshel). +This project was initially inspired by a [gist](https://gist.github.com/mosheeshel/c427b43c36b256731a0b) by [Moshe Eshel](https://github.com/mosheeshel). ## Contributing From ab56a277460486bf0bfac25daed7beb05984296f Mon Sep 17 00:00:00 2001 From: tbcs Date: Mon, 29 May 2017 13:25:57 +0200 Subject: [PATCH 0100/5615] Cleanup if start failed (#336) Closes #335 * prevent potential NPE in DockerComposeContainer * add regression test for #335 * ensure cleanup if starting fails in FailureDetectingExternalResource (#335) --- .../containers/DockerComposeContainer.java | 2 +- .../FailureDetectingExternalResource.java | 3 +- .../FailureDetectingExternalResourceTest.java | 28 +++++++++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 core/src/test/java/org/testcontainers/containers/FailureDetectingExternalResourceTest.java diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index eeca26b413a..0cd71e75317 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -46,7 +46,7 @@ public class DockerComposeContainer> e private final String identifier; private final Map ambassadorContainers = new HashMap<>(); private final List composeFiles; - private Set spawnedContainerIds; + private Set spawnedContainerIds = Collections.emptySet(); private Map scalingPreferences = new HashMap<>(); private DockerClient dockerClient; private boolean localCompose; diff --git a/core/src/main/java/org/testcontainers/containers/FailureDetectingExternalResource.java b/core/src/main/java/org/testcontainers/containers/FailureDetectingExternalResource.java index cb103b04278..29a032d320b 100644 --- a/core/src/main/java/org/testcontainers/containers/FailureDetectingExternalResource.java +++ b/core/src/main/java/org/testcontainers/containers/FailureDetectingExternalResource.java @@ -25,9 +25,8 @@ public void evaluate() throws Throwable { List errors = new ArrayList(); - starting(description); - try { + starting(description); base.evaluate(); succeeded(description); } catch (Throwable e) { diff --git a/core/src/test/java/org/testcontainers/containers/FailureDetectingExternalResourceTest.java b/core/src/test/java/org/testcontainers/containers/FailureDetectingExternalResourceTest.java new file mode 100644 index 00000000000..47d594c4c02 --- /dev/null +++ b/core/src/test/java/org/testcontainers/containers/FailureDetectingExternalResourceTest.java @@ -0,0 +1,28 @@ +package org.testcontainers.containers; + +import org.junit.Test; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +public class FailureDetectingExternalResourceTest { + + @Test + public void finishedIsCalledForCleanupIfStartingThrows() throws Throwable { + FailureDetectingExternalResource res = spy(FailureDetectingExternalResource.class); + Statement stmt = res.apply(mock(Statement.class), Description.EMPTY); + doThrow(new RuntimeException()).when(res).starting(any()); + try { + stmt.evaluate(); + } catch (Throwable t) { + // ignore + } + verify(res).starting(any()); + verify(res).finished(any()); + } + +} From e7c6ed2d520f2b1a6fa5c2d0ce17a8cee6acd5bc Mon Sep 17 00:00:00 2001 From: Aniket Bhatnagar Date: Mon, 29 May 2017 14:34:53 +0100 Subject: [PATCH 0101/5615] Upgraded Docker-Java to 3.0.10 (#349) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4c4310cef0b..3b2f4d6a9db 100644 --- a/pom.xml +++ b/pom.xml @@ -157,7 +157,7 @@ UTF-8 - 3.0.9 + 3.0.10 From 51a3a563840616045702f19818c4dd641b18d75f Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 3 Jun 2017 18:57:28 +0100 Subject: [PATCH 0102/5615] Enable automated release to Maven Central via Travis --- .travis.yml | 29 ++++++++++++-- pom.xml | 75 +++++++++++++++++++++++++++++++++--- release/.gitignore | 1 + release/codesigning.asc.enc | Bin 0 -> 8560 bytes release/deploy.sh | 7 ++++ release/settings.xml | 23 +++++++++++ 6 files changed, 125 insertions(+), 10 deletions(-) create mode 100644 release/.gitignore create mode 100644 release/codesigning.asc.enc create mode 100755 release/deploy.sh create mode 100644 release/settings.xml diff --git a/.travis.yml b/.travis.yml index f357a00287c..c16fc603a94 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,18 @@ language: java jdk: - oraclejdk8 + +env: + global: + - secure: "lw8tahEBcjos2c5QMMoeE19AKZDDU+WoEg7bumZJ+wuLErBEwlJENYU1t1oW834s1s7wgJ1kgt3jQgO6AWxR9wkFgZC+ZMPK0y/92T4MRsmmrMJFzUiu77UGS9MBj/Hu8M7aBzFNtLf0w3JrN2IsVI1xsX2z8PkK6L/G8C/Gzac=" + - # ^^ OSSRH_JIRA_USERNAME + - secure: "p8rkrCcHH5/EM7CKiPKQZCn1Lhzh7a+drGCHFrzHqWsZCapEXIgmqM0SYMnievaIBI+8IljdSkkW/BNfmIxW3fYDckLj91eEslLEhlIKDb91NtklDpr96o7on7wGF/VvpiEzIahKgp+LVsun/G1v3NU0T/RPs9QJoMmnQi4K+8I=" + - # ^^ OSSRH_JIRA_PASSWORD + - secure: "CWGSIr/ig2rIWSJqnDtmrK+XcUayl80ydh+fbzwi0v4hFsbbcNzm4SXFR2+HBGTBlWpWapvJoyG2iii2RHbgxVwYSyG6fEBrxtQV1oD8B/JAJoc/VJgvCsNnO54WRACqEPgV8fUX7MfKThA4hpXL3jnhM2A8J7wM969H8e4jNHk=" + - # ^^ GPG_KEY_NAME + - secure: "n2RjM3CIvtqMGMXYxjMOXjXB2R7k2lbZ2aV3fcQ0uSlrFqbzyRcTB/oa+Sft3ayUzU5S/qYjCPnNbb7P49vk1K3q5z2A9RVofFk9H90JPBz4cuuLcsuW7apcDPohDNuk47QAccsL0K+KA2AjdiQ/cIe81LWJykV+ogDI7DVXBDY=" + - # ^^ GPG_PASSPHRASE + sudo: required services: - docker @@ -30,8 +42,10 @@ before_install: - export DOCKER_TLS_VERIFY=0 - env +install: mvn install -P !build-extras -DskipTests=true -Dmaven.javadoc.skip=true -B -V + script: - - mvn -B test + - mvn -B -P !build-extras test # Run Docker-in-Docker tests - | DOCKER_HOST=unix:///var/run/docker.sock DOCKER_TLS_VERIFY= docker run --rm \ @@ -40,7 +54,7 @@ script: -v "$(pwd)":"$(pwd)" \ -w "$(pwd)" \ openjdk:8-jre \ - ./mvnw -B -pl core test -Dtest=*GenericContainerRuleTest + ./mvnw -B -P !build-extras -pl core test -Dtest=*GenericContainerRuleTest # Run Docker-in-Docker tests inside Alpine - | DOCKER_HOST=unix:///var/run/docker.sock DOCKER_TLS_VERIFY= docker run --rm \ @@ -49,8 +63,15 @@ script: -v "$(pwd)":"$(pwd)" \ -w "$(pwd)" \ kiview/openjdk-alpine-bash:8u111 \ - ./mvnw -B -pl core test -Dtest=*GenericContainerRuleTest - - mvn -B test -f shade-test/pom.xml + ./mvnw -B -P !build-extras -pl core test -Dtest=*GenericContainerRuleTest + - mvn -B -P !build-extras test -f shade-test/pom.xml + +deploy: + provider: script + script: release/deploy.sh + on: + tags: true + branch: master cache: directories: diff --git a/pom.xml b/pom.xml index 3b2f4d6a9db..8c8aa18446a 100644 --- a/pom.xml +++ b/pom.xml @@ -152,6 +152,17 @@ testcontainers-${project.version} + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.6 + true + + ossrh + https://oss.sonatype.org/ + false + + @@ -182,6 +193,64 @@ shade + + sign + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + + + + + + + + build-extras + + true + + + + + org.apache.maven.plugins + maven-source-plugin + 2.4 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.3 + + + attach-javadocs + + jar + + + + + + + @@ -191,10 +260,4 @@ HEAD - - - bintray - https://api.bintray.com/maven/richnorth/maven/test-containers - - diff --git a/release/.gitignore b/release/.gitignore new file mode 100644 index 00000000000..1546d6762de --- /dev/null +++ b/release/.gitignore @@ -0,0 +1 @@ +codesigning.asc diff --git a/release/codesigning.asc.enc b/release/codesigning.asc.enc new file mode 100644 index 0000000000000000000000000000000000000000..547a33b55e482eabce79af619060fea61e6dcf42 GIT binary patch literal 8560 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 + + + ossrh + ${env.OSSRH_JIRA_USERNAME} + ${env.OSSRH_JIRA_PASSWORD} + + + + + + ossrh + + true + + + gpg + ${env.GPG_KEY_NAME} + ${env.GPG_PASSPHRASE} + + + + \ No newline at end of file From 0569e0bbb1ce6c69349ddd3299451045b23541e6 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 3 Jun 2017 20:46:21 +0100 Subject: [PATCH 0103/5615] Modify Selenium container startup detection (#351) * Modify Selenium container startup detection, using alternative readiness string observed in v3.x of Selenium containers. * Simplify string * Revert simplification (needed for 2.45.0) * Update test following comments --- .../containers/BrowserWebDriverContainer.java | 2 +- .../testcontainers/junit/Selenium3xTest.java | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 modules/selenium/src/test/java/org/testcontainers/junit/Selenium3xTest.java diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index 2003c91894c..3ea70d42566 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -56,7 +56,7 @@ public class BrowserWebDriverContainer Date: Sat, 3 Jun 2017 21:12:49 +0100 Subject: [PATCH 0104/5615] Update CHANGELOG for forthcoming 1.3.0 release --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a72ff55a0e1..385ce8e5005 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,18 @@ # Change Log All notable changes to this project will be documented in this file. +## [1.3.0] - TBD +### Fixed +- Improved container cleanup if startup failed (#336, #335) + +### Changed +- Upgraded docker-java library to 3.0.10 (#349) +- Added basic audit logging of Testcontainers' actions via a specific SLF4J logger name with metadata captured via MDC. Intended for use in highly shared Docker environments. +- Use string-based detection of Selenium container startup (#328, #351) +- Use string-based detection of PostgreSQL container startup (#327, #317) +- Update libraries to recent versions (#333) +- Introduce abstraction over files and classpath resources, allowing recursive copying of directories (#313) + ## [1.2.1] - 2017-04-06 ### Fixed - Fix bug in space detection when `alpine:3.5` image has not yet been pulled (#323, #324) From 1c1bf4222f21778d91c27a04d161b15a864d2b93 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 4 Jun 2017 14:45:28 +0100 Subject: [PATCH 0105/5615] Reverse over-optimisation of MySQL/MariaDB thread_stack size setting (#356) The default value has now been removed to allow DB default to be used (typically 192K) Closes #347 --- modules/mysql/src/main/resources/mysql-default-conf/my.cnf | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/mysql/src/main/resources/mysql-default-conf/my.cnf b/modules/mysql/src/main/resources/mysql-default-conf/my.cnf index b0dce6488c3..d608cb9c923 100644 --- a/modules/mysql/src/main/resources/mysql-default-conf/my.cnf +++ b/modules/mysql/src/main/resources/mysql-default-conf/my.cnf @@ -11,7 +11,6 @@ sort_buffer_size = 64K read_buffer_size = 256K read_rnd_buffer_size = 256K net_buffer_length = 2K -thread_stack = 128K skip-host-cache skip-name-resolve From 1a227a48efc2a1092466863d33f55d57a44319f5 Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 5 Jun 2017 06:16:28 +0100 Subject: [PATCH 0106/5615] Update changelog for release --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 385ce8e5005..d69b17658f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # Change Log All notable changes to this project will be documented in this file. -## [1.3.0] - TBD +## [1.3.0] - 2017-06-05 ### Fixed - Improved container cleanup if startup failed (#336, #335) From 06ad35100a5a7480831e433344274dda6e6e6a9b Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 5 Jun 2017 09:18:11 +0100 Subject: [PATCH 0107/5615] Skip deployment of JDBC Test module --- modules/jdbc-test/pom.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/modules/jdbc-test/pom.xml b/modules/jdbc-test/pom.xml index 2ce69f20476..eb5633b6917 100644 --- a/modules/jdbc-test/pom.xml +++ b/modules/jdbc-test/pom.xml @@ -124,6 +124,15 @@ + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.6 + true + + true + + From 1ae18311514e4cb3845577aed1243d66cfa44e7e Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 5 Jun 2017 11:56:44 +0100 Subject: [PATCH 0108/5615] Remove jdbc-test module from profile used during deploy --- modules/jdbc-test/pom.xml | 9 --------- pom.xml | 10 +++++++++- release/deploy.sh | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/modules/jdbc-test/pom.xml b/modules/jdbc-test/pom.xml index eb5633b6917..2ce69f20476 100644 --- a/modules/jdbc-test/pom.xml +++ b/modules/jdbc-test/pom.xml @@ -124,15 +124,6 @@ - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.6 - true - - true - - diff --git a/pom.xml b/pom.xml index 8c8aa18446a..b3fb3361c7c 100644 --- a/pom.xml +++ b/pom.xml @@ -179,7 +179,6 @@ modules/selenium modules/nginx modules/mariadb - modules/jdbc-test @@ -193,6 +192,15 @@ shade + + test-only + + true + + + modules/jdbc-test + + sign diff --git a/release/deploy.sh b/release/deploy.sh index d1de92008ef..c734fb028c6 100755 --- a/release/deploy.sh +++ b/release/deploy.sh @@ -4,4 +4,4 @@ openssl aes-256-cbc -K $encrypted_291cb5a97669_key -iv $encrypted_291cb5a97669_i gpg --fast-import release/codesigning.asc mvn versions:set -DnewVersion=$TRAVIS_TAG -mvn deploy -P sign,build-extras -DskipTests --settings release/settings.xml +mvn deploy -P sign,build-extras,!test-only -DskipTests --settings release/settings.xml From 092fba871442b330f7c78bb672b21eca428471e6 Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 15 May 2017 21:20:38 +0100 Subject: [PATCH 0109/5615] Ensure test connections are always closed --- .../containers/JdbcDatabaseContainer.java | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index 21a9da73c95..033ca7ae5c0 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -71,21 +71,22 @@ protected void waitUntilContainerStarted() { throw new ContainerLaunchException("Container failed to start"); } - Connection connection = DB_CONNECT_RATE_LIMIT.getWhenReady(() -> createConnection("")); - - boolean success = connection.createStatement().execute(JdbcDatabaseContainer.this.getTestQueryString()); - - if (success) { - logger().info("Obtained a connection to container ({})", JdbcDatabaseContainer.this.getJdbcUrl()); - return connection; - } else { - throw new SQLException("Failed to execute test query"); + try (Connection connection = DB_CONNECT_RATE_LIMIT.getWhenReady(() -> createConnection(""))) { + boolean success = connection.createStatement().execute(JdbcDatabaseContainer.this.getTestQueryString()); + + if (success) { + logger().info("Obtained a connection to container ({})", JdbcDatabaseContainer.this.getJdbcUrl()); + return null; + } else { + throw new SQLException("Failed to execute test query"); + } } }); } /** * Obtain an instance of the correct JDBC driver for this particular database container type + * * @return a JDBC Driver */ public Driver getJdbcDriverInstance() { @@ -106,9 +107,9 @@ public Driver getJdbcDriverInstance() { /** * Creates a connection to the underlying containerized database instance. * - * @param queryString any special query string parameters that should be appended to the JDBC connection URL. The - * '?' character must be included - * @return a Connection + * @param queryString any special query string parameters that should be appended to the JDBC connection URL. The + * '?' character must be included + * @return a Connection * @throws SQLException if there is a repeated failure to create the connection */ public Connection createConnection(String queryString) throws SQLException { From 86070d3fe724556c25f60cb34380b85ce94a83c0 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Fri, 9 Jun 2017 23:26:48 +0200 Subject: [PATCH 0110/5615] Extract MariaDB into a separate repository. See #337 --- .../testcontainers/jdbc/JDBCDriverTest.java | 6 -- .../junit/SimpleMariaDBTest.java | 87 ------------------- .../test/resources/somepath/init_mariadb.sql | 5 -- .../somepath/mariadb_conf_override/my.cnf | 53 ----------- modules/mariadb/pom.xml | 59 ------------- .../containers/MariaDBContainer.java | 73 ---------------- .../containers/MariaDBContainerProvider.java | 16 ---- ...s.containers.JdbcDatabaseContainerProvider | 1 - .../resources/mariadb-default-conf/my.cnf | 46 ---------- pom.xml | 2 +- 10 files changed, 1 insertion(+), 347 deletions(-) delete mode 100644 modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMariaDBTest.java delete mode 100644 modules/jdbc-test/src/test/resources/somepath/init_mariadb.sql delete mode 100644 modules/jdbc-test/src/test/resources/somepath/mariadb_conf_override/my.cnf delete mode 100644 modules/mariadb/pom.xml delete mode 100644 modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java delete mode 100644 modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java delete mode 100644 modules/mariadb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider delete mode 100644 modules/mariadb/src/main/resources/mariadb-default-conf/my.cnf diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java index f29a19c14f1..8e663df8a45 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java @@ -42,13 +42,7 @@ public static Iterable data() { {"jdbc:tc:mysql://hostname/databasename?useUnicode=yes&characterEncoding=utf8", false, true, false}, {"jdbc:tc:mysql://hostname/databasename", false, false, false}, {"jdbc:tc:postgresql://hostname/databasename", false, false, false}, - {"jdbc:tc:mariadb:10.1.16://hostname/databasename", false, false, false}, - {"jdbc:tc:mariadb://hostname/databasename", false, false, false}, - {"jdbc:tc:mariadb://hostname/databasename?useUnicode=yes&characterEncoding=utf8", false, true, false}, - {"jdbc:tc:mariadb://hostname/databasename?TC_INITSCRIPT=somepath/init_mariadb.sql", true, false, false}, - {"jdbc:tc:mariadb://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", true, false, false}, {"jdbc:tc:mysql:5.6://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override", false, false, true}, - {"jdbc:tc:mariadb:10.1.16://hostname/databasename?TC_MY_CNF=somepath/mariadb_conf_override", false, false, true} }); } diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMariaDBTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMariaDBTest.java deleted file mode 100644 index 1ad65e65465..00000000000 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMariaDBTest.java +++ /dev/null @@ -1,87 +0,0 @@ -package org.testcontainers.junit; - -import com.zaxxer.hikari.HikariConfig; -import com.zaxxer.hikari.HikariDataSource; -import lombok.NonNull; - -import org.apache.commons.lang.SystemUtils; -import org.junit.Test; -import org.testcontainers.containers.MariaDBContainer; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.junit.Assume.assumeFalse; - - -/** - * @author Miguel Gonzalez Sanchez - */ -public class SimpleMariaDBTest { - - @Test - public void testSimple() throws SQLException { - MariaDBContainer mariadb = new MariaDBContainer(); - mariadb.start(); - - try { - ResultSet resultSet = performQuery(mariadb, "SELECT 1"); - int resultSetInt = resultSet.getInt(1); - - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); - } finally { - mariadb.stop(); - } - } - - @Test - public void testSpecificVersion() throws SQLException { - MariaDBContainer mariadbOldVersion = new MariaDBContainer("mariadb:5.5.51"); - mariadbOldVersion.start(); - - try { - ResultSet resultSet = performQuery(mariadbOldVersion, "SELECT VERSION()"); - String resultSetString = resultSet.getString(1); - - assertTrue("The database version can be set using a container rule parameter", resultSetString.startsWith("5.5.51")); - } finally { - mariadbOldVersion.stop(); - } - } - - @Test - public void testMariaDBWithCustomIniFile() throws SQLException { - assumeFalse(SystemUtils.IS_OS_WINDOWS); - MariaDBContainer mariadbCustomConfig = new MariaDBContainer("mariadb:10.1.16") - .withConfigurationOverride("somepath/mariadb_conf_override"); - mariadbCustomConfig.start(); - - try { - ResultSet resultSet = performQuery(mariadbCustomConfig, "SELECT @@GLOBAL.innodb_file_format"); - String result = resultSet.getString(1); - - assertEquals("The InnoDB file format has been set by the ini file content", "Barracuda", result); - } finally { - mariadbCustomConfig.stop(); - } - } - - @NonNull - protected ResultSet performQuery(MariaDBContainer containerRule, String sql) throws SQLException { - HikariConfig hikariConfig = new HikariConfig(); - hikariConfig.setJdbcUrl(containerRule.getJdbcUrl()); - hikariConfig.setUsername(containerRule.getUsername()); - hikariConfig.setPassword(containerRule.getPassword()); - - HikariDataSource ds = new HikariDataSource(hikariConfig); - Statement statement = ds.getConnection().createStatement(); - statement.execute(sql); - ResultSet resultSet = statement.getResultSet(); - - resultSet.next(); - return resultSet; - } -} diff --git a/modules/jdbc-test/src/test/resources/somepath/init_mariadb.sql b/modules/jdbc-test/src/test/resources/somepath/init_mariadb.sql deleted file mode 100644 index 2b00ee968b0..00000000000 --- a/modules/jdbc-test/src/test/resources/somepath/init_mariadb.sql +++ /dev/null @@ -1,5 +0,0 @@ -CREATE TABLE bar ( - foo VARCHAR(255) -); - -INSERT INTO bar (foo) VALUES ('hello world'); \ No newline at end of file diff --git a/modules/jdbc-test/src/test/resources/somepath/mariadb_conf_override/my.cnf b/modules/jdbc-test/src/test/resources/somepath/mariadb_conf_override/my.cnf deleted file mode 100644 index 220060976ab..00000000000 --- a/modules/jdbc-test/src/test/resources/somepath/mariadb_conf_override/my.cnf +++ /dev/null @@ -1,53 +0,0 @@ -[mysqld] -port = 3306 -#socket = /tmp/mysql.sock -skip-external-locking -key_buffer_size = 16K -max_allowed_packet = 1M -table_open_cache = 4 -sort_buffer_size = 64K -read_buffer_size = 256K -read_rnd_buffer_size = 256K -net_buffer_length = 2K -thread_stack = 128K - - -innodb_file_format=Barracuda - - - - - -# Don't listen on a TCP/IP port at all. This can be a security enhancement, -# if all processes that need to connect to mysqld run on the same host. -# All interaction with mysqld must be made via Unix sockets or named pipes. -# Note that using this option without enabling named pipes on Windows -# (using the "enable-named-pipe" option) will render mysqld useless! -# -#skip-networking -#server-id = 1 - -# Uncomment the following if you want to log updates -#log-bin=mysql-bin - -# binary logging format - mixed recommended -#binlog_format=mixed - -# Causes updates to non-transactional engines using statement format to be -# written directly to binary log. Before using this option make sure that -# there are no dependencies between transactional and non-transactional -# tables such as in the statement INSERT INTO t_myisam SELECT * FROM -# t_innodb; otherwise, slaves may diverge from the master. -#binlog_direct_non_transactional_updates=TRUE - -# Uncomment the following if you are using InnoDB tables -innodb_data_file_path = ibdata1:10M:autoextend -# You can set .._buffer_pool_size up to 50 - 80 % -# of RAM but beware of setting memory usage too high -innodb_buffer_pool_size = 16M -#innodb_additional_mem_pool_size = 2M -# Set .._log_file_size to 25 % of buffer pool size -innodb_log_file_size = 5M -innodb_log_buffer_size = 8M -innodb_flush_log_at_trx_commit = 1 -innodb_lock_wait_timeout = 50 \ No newline at end of file diff --git a/modules/mariadb/pom.xml b/modules/mariadb/pom.xml deleted file mode 100644 index efb117eb0c0..00000000000 --- a/modules/mariadb/pom.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - 4.0.0 - - - org.testcontainers - testcontainers-parent - 1.2.2-SNAPSHOT - ../../pom.xml - - - mariadb - TestContainers :: JDBC :: MariaDB - - - - ${project.groupId} - testcontainers - ${project.version} - - - ${project.groupId} - jdbc - ${project.version} - - - - - org.mariadb.jdbc - mariadb-java-client - 1.4.6 - test - - - - - com.zaxxer - HikariCP-java6 - 2.3.8 - test - - - commons-dbutils - commons-dbutils - 1.6 - test - - - org.apache.tomcat - tomcat-jdbc - 8.5.4 - - - org.vibur - vibur-dbcp - 9.0 - - - diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java deleted file mode 100644 index 2bea9b8ad7b..00000000000 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.testcontainers.containers; - -/** - * Container implementation for the MariaDB project. - * - * @author Miguel Gonzalez Sanchez - */ -public class MariaDBContainer> extends JdbcDatabaseContainer { - - public static final String NAME = "mariadb"; - public static final String IMAGE = "mariadb"; - private static final Integer MARIADB_PORT = 3306; - private static final String MARIADB_USER = "test"; - private static final String MARIADB_PASSWORD = "test"; - private static final String MARIADB_DATABASE = "test"; - private static final String MY_CNF_CONFIG_OVERRIDE_PARAM_NAME = "TC_MY_CNF"; - - public MariaDBContainer() { - super(IMAGE + ":latest"); - } - - public MariaDBContainer(String dockerImageName) { - super(dockerImageName); - } - - @Override - protected Integer getLivenessCheckPort() { - return getMappedPort(MARIADB_PORT); - } - - @Override - protected void configure() { - optionallyMapResourceParameterAsVolume(MY_CNF_CONFIG_OVERRIDE_PARAM_NAME, "/etc/mysql/conf.d", "mariadb-default-conf"); - - addExposedPort(MARIADB_PORT); - addEnv("MYSQL_DATABASE", MARIADB_DATABASE); - addEnv("MYSQL_USER", MARIADB_USER); - addEnv("MYSQL_PASSWORD", MARIADB_PASSWORD); - addEnv("MYSQL_ROOT_PASSWORD", MARIADB_PASSWORD); - setCommand("mysqld"); - setStartupAttempts(3); - } - - @Override - public String getDriverClassName() { - return "org.mariadb.jdbc.Driver"; - } - - @Override - public String getJdbcUrl() { - return "jdbc:mariadb://" + getContainerIpAddress() + ":" + getMappedPort(MARIADB_PORT) + "/test"; - } - - @Override - public String getUsername() { - return MARIADB_USER; - } - - @Override - public String getPassword() { - return MARIADB_PASSWORD; - } - - @Override - public String getTestQueryString() { - return "SELECT 1"; - } - - public SELF withConfigurationOverride(String s) { - parameters.put(MY_CNF_CONFIG_OVERRIDE_PARAM_NAME, s); - return self(); - } -} diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java deleted file mode 100644 index b3171b02911..00000000000 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.testcontainers.containers; - -/** - * Factory for MariaDB containers. - */ -public class MariaDBContainerProvider extends JdbcDatabaseContainerProvider { - @Override - public boolean supports(String databaseType) { - return databaseType.equals(MariaDBContainer.NAME); - } - - @Override - public JdbcDatabaseContainer newInstance(String tag) { - return new MariaDBContainer(MariaDBContainer.IMAGE + ":" + tag); - } -} diff --git a/modules/mariadb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider b/modules/mariadb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider deleted file mode 100644 index f5b51466d19..00000000000 --- a/modules/mariadb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider +++ /dev/null @@ -1 +0,0 @@ -org.testcontainers.containers.MariaDBContainerProvider \ No newline at end of file diff --git a/modules/mariadb/src/main/resources/mariadb-default-conf/my.cnf b/modules/mariadb/src/main/resources/mariadb-default-conf/my.cnf deleted file mode 100644 index 3f6c80633e2..00000000000 --- a/modules/mariadb/src/main/resources/mariadb-default-conf/my.cnf +++ /dev/null @@ -1,46 +0,0 @@ -[mysqld] -port = 3306 -#socket = /tmp/mysql.sock -skip-external-locking -key_buffer_size = 16K -max_allowed_packet = 1M -table_open_cache = 4 -sort_buffer_size = 64K -read_buffer_size = 256K -read_rnd_buffer_size = 256K -net_buffer_length = 2K -thread_stack = 128K - -# Don't listen on a TCP/IP port at all. This can be a security enhancement, -# if all processes that need to connect to mysqld run on the same host. -# All interaction with mysqld must be made via Unix sockets or named pipes. -# Note that using this option without enabling named pipes on Windows -# (using the "enable-named-pipe" option) will render mysqld useless! -# -#skip-networking -#server-id = 1 - -# Uncomment the following if you want to log updates -#log-bin=mysql-bin - -# binary logging format - mixed recommended -#binlog_format=mixed - -# Causes updates to non-transactional engines using statement format to be -# written directly to binary log. Before using this option make sure that -# there are no dependencies between transactional and non-transactional -# tables such as in the statement INSERT INTO t_myisam SELECT * FROM -# t_innodb; otherwise, slaves may diverge from the master. -#binlog_direct_non_transactional_updates=TRUE - -# Uncomment the following if you are using InnoDB tables -innodb_data_file_path = ibdata1:10M:autoextend -# You can set .._buffer_pool_size up to 50 - 80 % -# of RAM but beware of setting memory usage too high -innodb_buffer_pool_size = 16M -#innodb_additional_mem_pool_size = 2M -# Set .._log_file_size to 25 % of buffer pool size -innodb_log_file_size = 5M -innodb_log_buffer_size = 8M -innodb_flush_log_at_trx_commit = 1 -innodb_lock_wait_timeout = 50 \ No newline at end of file diff --git a/pom.xml b/pom.xml index b3fb3361c7c..105f2361123 100644 --- a/pom.xml +++ b/pom.xml @@ -178,7 +178,7 @@ modules/postgresql modules/selenium modules/nginx - modules/mariadb + modules/jdbc-test From 05192e67f784cd3961acf85f02e81558fc366553 Mon Sep 17 00:00:00 2001 From: Igor Nikolaev Date: Sun, 11 Jun 2017 11:58:16 +0300 Subject: [PATCH 0111/5615] Allow to start daemon database container (#360) * Allow to start daemon database container * Add documentation for TC_DAEMON parameter * Add tests for TC_DAEMON parameter * Update changelog --- CHANGELOG.md | 4 ++ docs/usage/database_containers.md | 8 +++ .../jdbc/DatabaseDriverShutdownTest.java | 50 +++++++++++++++++++ .../jdbc/ContainerDatabaseDriver.java | 19 ++++++- 4 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index d69b17658f8..1069e33dde8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # Change Log All notable changes to this project will be documented in this file. +## UNRELEASED +### Changed +- Added `TC_DAEMON` JDBC URL flag to prevent `ContainerDatabaseDriver` from shutting down containers at the time all connections are closed. (#359, #360) + ## [1.3.0] - 2017-06-05 ### Fixed - Improved container cleanup if startup failed (#336, #335) diff --git a/docs/usage/database_containers.md b/docs/usage/database_containers.md index 94ee888b5ac..59e73e75d90 100644 --- a/docs/usage/database_containers.md +++ b/docs/usage/database_containers.md @@ -97,6 +97,14 @@ public class JDBCDriverTest { ... ``` +#### Running container in daemon mode + +By default database container is being stopped as soon as last connection is closed. There are cases when you might need to start container and keep it running till you stop it explicitly or JVM is shutdown. To do this, add `TC_DAEMON` parameter to the URL as follows: + + `jdbc:tc:mysql://hostname/databasename?TC_DAEMON=true` + +With this parameter database container will keep running even when there're no open connections. + #### Overriding MySQL my.cnf settings For MySQL databases, it is possible to override configuration settings using resources on the classpath. Assuming `somepath/mysql_conf_override` diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java new file mode 100644 index 00000000000..a6bfe756d10 --- /dev/null +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java @@ -0,0 +1,50 @@ +package org.testcontainers.jdbc; + +import org.junit.AfterClass; +import org.junit.Test; +import org.testcontainers.containers.JdbcDatabaseContainer; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; +import static org.rnorth.visibleassertions.VisibleAssertions.assertNull; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; + +/** + * Created by inikolaev on 08/06/2017. + */ +public class DatabaseDriverShutdownTest { + @AfterClass + public static void testCleanup() { + ContainerDatabaseDriver.killContainers(); + } + + @Test + public void shouldStopContainerWhenAllConnectionsClosed() throws SQLException { + final String jdbcUrl = "jdbc:tc:postgresql://hostname/databasename"; + + getConnectionAndClose(jdbcUrl); + + JdbcDatabaseContainer container = ContainerDatabaseDriver.getContainer(jdbcUrl); + assertNull("Database container instance is null as expected", container); + } + + @Test + public void shouldNotStopDaemonContainerWhenAllConnectionsClosed() throws SQLException { + final String jdbcUrl = "jdbc:tc:postgresql://hostname/databasename?TC_DAEMON=true"; + + getConnectionAndClose(jdbcUrl); + + JdbcDatabaseContainer container = ContainerDatabaseDriver.getContainer(jdbcUrl); + assertNotNull("Database container instance is not null as expected", container); + assertTrue("Database container is running as expected", container.isRunning()); + } + + private void getConnectionAndClose(String jdbcUrl) throws SQLException { + try (Connection connection = DriverManager.getConnection(jdbcUrl)) { + assertNotNull("Obtained connection as expected", connection); + } + } +} diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java index 937ca61536f..55b9e83184e 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java @@ -1,5 +1,6 @@ package org.testcontainers.jdbc; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Charsets; import com.google.common.io.Resources; import org.slf4j.LoggerFactory; @@ -37,6 +38,7 @@ public class ContainerDatabaseDriver implements Driver { private static final Pattern URL_MATCHING_PATTERN = Pattern.compile("jdbc:tc:([a-z]+)(:([^:]+))?://[^\\?]+(\\?.*)?"); + private static final Pattern DAEMON_MATCHING_PATTERN = Pattern.compile(".*([\\?&]?)TC_DAEMON=([^\\?&]+).*"); private static final Pattern INITSCRIPT_MATCHING_PATTERN = Pattern.compile(".*([\\?&]?)TC_INITSCRIPT=([^\\?&]+).*"); private static final Pattern INITFUNCTION_MATCHING_PATTERN = Pattern.compile(".*([\\?&]?)TC_INITFUNCTION=" + "((\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*\\.)*\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)" + @@ -186,6 +188,9 @@ private Map getContainerParameters(String url) { * @return the connection, wrapped */ private Connection wrapConnection(final Connection connection, final JdbcDatabaseContainer container, final String url) { + final Matcher matcher = DAEMON_MATCHING_PATTERN.matcher(url); + final boolean isDaemon = matcher.matches() ? Boolean.parseBoolean(matcher.group(2)) : false; + Set connections = containerConnections.get(container.getContainerId()); if (connections == null) { @@ -199,7 +204,7 @@ private Connection wrapConnection(final Connection connection, final JdbcDatabas return new ConnectionWrapper(connection, () -> { finalConnections.remove(connection); - if (finalConnections.isEmpty()) { + if (!isDaemon && finalConnections.isEmpty()) { container.stop(); jdbcUrlContainerCache.remove(url); } @@ -313,4 +318,16 @@ public static void killContainer(String jdbcUrl) { } } } + + /** + * Utility method to get an instance of a database container given its JDBC URL. + * @param jdbcUrl the JDBC URL of the container instance to get + * @return an instance of database container or null if no container associated with JDBC URL + */ + @VisibleForTesting + static JdbcDatabaseContainer getContainer(String jdbcUrl) { + synchronized (jdbcUrlContainerCache) { + return jdbcUrlContainerCache.get(jdbcUrl); + } + } } From 41dba7c9c979ac120f09232030c5422c03e5b92f Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 13 Jun 2017 07:48:25 +0200 Subject: [PATCH 0112/5615] Remove deprecated wrongly generated setters from GenericContainer (#364) --- .../testcontainers/containers/Container.java | 30 ------------------- .../containers/GenericContainer.java | 10 +++++-- 2 files changed, 7 insertions(+), 33 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/Container.java b/core/src/main/java/org/testcontainers/containers/Container.java index b3c0a5298da..5b2f60b7232 100644 --- a/core/src/main/java/org/testcontainers/containers/Container.java +++ b/core/src/main/java/org/testcontainers/containers/Container.java @@ -388,35 +388,5 @@ ExecResult execInContainer(Charset outputCharset, String... command) void setLinkedContainers(Map linkedContainers); - /** - * @deprecated set by GenericContainer and should never be set outside - */ - @Deprecated - void setDockerClient(DockerClient dockerClient); - - /** - * @deprecated set by GenericContainer and should never be set outside - */ - @Deprecated - void setDockerDaemonInfo(Info dockerDaemonInfo); - - /** - * @deprecated set by GenericContainer and should never be set outside - */ - @Deprecated - void setContainerId(String containerId); - - /** - * @deprecated set by GenericContainer and should never be set outside - */ - @Deprecated - void setContainerName(String containerName); - void setWaitStrategy(WaitStrategy waitStrategy); - - /** - * @deprecated set by GenericContainer and should never be set outside - */ - @Deprecated - void setContainerInfo(InspectContainerResponse containerInfo); } diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 8055650ef41..49a020b0889 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -8,9 +8,7 @@ import com.github.dockerjava.api.model.*; import com.google.common.base.Preconditions; import com.google.common.base.Strings; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NonNull; +import lombok.*; import org.jetbrains.annotations.Nullable; import org.junit.runner.Description; import org.rnorth.ducttape.ratelimits.RateLimiter; @@ -108,17 +106,22 @@ public class GenericContainer> /* * Unique instance of DockerClient for use by this container object. */ + @Setter(AccessLevel.NONE) protected DockerClient dockerClient = DockerClientFactory.instance().client(); /* * Info about the Docker server; lazily fetched. */ + @Setter(AccessLevel.NONE) protected Info dockerDaemonInfo = null; /* * Set during container startup */ + @Setter(AccessLevel.NONE) protected String containerId; + + @Setter(AccessLevel.NONE) protected String containerName; /** @@ -128,6 +131,7 @@ public class GenericContainer> protected WaitStrategy waitStrategy = Wait.defaultWaitStrategy(); @Nullable + @Setter(AccessLevel.NONE) private InspectContainerResponse containerInfo; private List> logConsumers = new ArrayList<>(); From bd228ba43c3830de2e1b5aa1091065c020e42696 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 14 Jun 2017 23:10:06 +0200 Subject: [PATCH 0113/5615] Remove Jersey dependencies (#361) * Remove Jersey dependencies because we use Netty * add Changelog entry --- CHANGELOG.md | 1 + core/pom.xml | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1069e33dde8..006659a92d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file. ## UNRELEASED ### Changed - Added `TC_DAEMON` JDBC URL flag to prevent `ContainerDatabaseDriver` from shutting down containers at the time all connections are closed. (#359, #360) +- Removed unused Jersey dependencies (#361) ## [1.3.0] - 2017-06-05 ### Fixed diff --git a/core/pom.xml b/core/pom.xml index 7d6eb11395e..e7e8935720e 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -26,9 +26,23 @@ de.gesellix unix-socket-factory + + org.glassfish.jersey.core + * + + + org.glassfish.jersey.connectors + * + + + javax.ws.rs + javax.ws.rs-api + 2.0.1 + + org.rnorth tcp-unix-socket-proxy From cb2c38285a97754e6c2b02c81796b85743ee13f9 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 14 Jun 2017 23:18:13 +0200 Subject: [PATCH 0114/5615] Travis optimizations (#366) * Speed up Travis configuration * remove cache warmup * cache whole ~/.m2 folder * Update surefire plugin (better, colorful logging) --- .mvn/wrapper/maven-wrapper.jar | Bin 49519 -> 47610 bytes .mvn/wrapper/maven-wrapper.properties | 2 +- .travis.yml | 91 +++--- modules/jdbc-test/pom.xml | 8 +- modules/nginx/pom.xml | 20 -- .../testcontainers/junit/SimpleNginxTest.java | 33 +- modules/selenium/pom.xml | 7 + .../junit/LinkedContainerTest.java | 59 ++++ mvnw | 73 ++--- mvnw.cmd | 289 +++++++++--------- pom.xml | 5 + 11 files changed, 299 insertions(+), 288 deletions(-) mode change 100644 => 100755 .mvn/wrapper/maven-wrapper.jar mode change 100644 => 100755 .mvn/wrapper/maven-wrapper.properties create mode 100644 modules/selenium/src/test/java/org/testcontainers/junit/LinkedContainerTest.java mode change 100644 => 100755 mvnw.cmd diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar old mode 100644 new mode 100755 index c6feb8bb6f76f2553e266ff8bf8867105154237e..9cc84ea9b4d95453115d0c26488d6a78694e0bc6 GIT binary patch literal 47610 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 literal 49519 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%0A$pwhjXzyA0ZzErH{1@M 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 diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties old mode 100644 new mode 100755 index 6637cedb28e..56bb0164ec1 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1 +1 @@ -distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip \ No newline at end of file +distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index c16fc603a94..85762826124 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,62 +17,53 @@ sudo: required services: - docker -before_install: +cache: + directories: + - '$HOME/.m2' + +install: + - echo "MAVEN_OPTS='-XX:+TieredCompilation -XX:TieredStopAtLevel=1'" > ~/.mavenrc - sudo apt-get update -qq - sudo apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confnew install docker-engine - - env -# Pre-fetch docker images - seems to need to be done before docker env vars are set - - docker pull redis:3.0.2 - - docker pull rabbitmq:3.5.3 - - docker pull mongo:3.1.5 - - docker pull alpine:3.2 - - docker pull mysql:latest - - docker pull mysql:5.5.43 - - docker pull mysql:5.6 - - docker pull mysql:5.5 - - docker pull postgres:latest - - docker pull selenium/standalone-chrome-debug:2.52.0 - - docker pull selenium/standalone-firefox-debug:2.52.0 - - docker pull richnorth/vnc-recorder:latest - - docker pull nginx:1.9.4 - - docker pull dduportal/docker-compose:1.6.0 - - docker pull richnorth/ambassador:latest -# Update env vars required by testcontainers + - docker info + # Update env vars required by testcontainers - export DOCKER_HOST=tcp://127.0.0.1:2375 - export DOCKER_TLS_VERIFY=0 - env + - ./mvnw -T4 -DskipTests=true -Dmaven.javadoc.skip=true install -install: mvn install -P !build-extras -DskipTests=true -Dmaven.javadoc.skip=true -B -V +jobs: + include: + - stage: test + env: [ NAME=core ] + script: ./mvnw -pl core test -script: - - mvn -B -P !build-extras test - # Run Docker-in-Docker tests - - | - DOCKER_HOST=unix:///var/run/docker.sock DOCKER_TLS_VERIFY= docker run --rm \ - -v "$HOME/.m2":/root/.m2/ \ - -v /var/run/docker.sock:/var/run/docker.sock \ - -v "$(pwd)":"$(pwd)" \ - -w "$(pwd)" \ - openjdk:8-jre \ - ./mvnw -B -P !build-extras -pl core test -Dtest=*GenericContainerRuleTest - # Run Docker-in-Docker tests inside Alpine - - | - DOCKER_HOST=unix:///var/run/docker.sock DOCKER_TLS_VERIFY= docker run --rm \ - -v "$HOME/.m2":/root/.m2/ \ - -v /var/run/docker.sock:/var/run/docker.sock \ - -v "$(pwd)":"$(pwd)" \ - -w "$(pwd)" \ - kiview/openjdk-alpine-bash:8u111 \ - ./mvnw -B -P !build-extras -pl core test -Dtest=*GenericContainerRuleTest - - mvn -B -P !build-extras test -f shade-test/pom.xml + - env: [ NAME=selenium ] + script: ./mvnw -pl modules/selenium test -deploy: - provider: script - script: release/deploy.sh - on: - tags: true - branch: master + - env: [ NAME=modules ] + script: ./mvnw -pl !core,!modules/selenium test -cache: - directories: - - '$HOME/.m2/repository' + # Run Docker-in-Docker tests inside Alpine + - env: [ NAME=docker-in-alpine-docker ] + script: | + DOCKER_HOST=unix:///var/run/docker.sock DOCKER_TLS_VERIFY= docker run -t --rm \ + -v "$HOME/.m2":/root/.m2/ \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v "$(pwd)":"$(pwd)" \ + -w "$(pwd)" \ + openjdk:8-jdk-alpine \ + ./mvnw -pl core test -Dtest=*GenericContainerRuleTest + + - env: [ NAME=shade ] + script: ./mvnw test -f shade-test/pom.xml + + - stage: deploy + install: skip + script: skip + deploy: + provider: script + script: release/deploy.sh + on: + tags: true + branch: master diff --git a/modules/jdbc-test/pom.xml b/modules/jdbc-test/pom.xml index 2ce69f20476..57fa0f0b4fe 100644 --- a/modules/jdbc-test/pom.xml +++ b/modules/jdbc-test/pom.xml @@ -24,10 +24,12 @@ postgresql ${project.version} + - ${project.groupId} - mariadb - ${project.version} + com.google.guava + guava + 18.0 + test diff --git a/modules/nginx/pom.xml b/modules/nginx/pom.xml index a61627f039d..bd3a7a41a18 100644 --- a/modules/nginx/pom.xml +++ b/modules/nginx/pom.xml @@ -18,25 +18,5 @@ testcontainers ${project.version} - - - ${project.groupId} - selenium - ${project.version} - test - - - - org.seleniumhq.selenium - selenium-api - 2.45.0 - test - - - org.seleniumhq.selenium - selenium-remote-driver - 2.45.0 - test - \ No newline at end of file diff --git a/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java b/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java index 750d88ad066..fe5c7f85c11 100644 --- a/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java +++ b/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java @@ -1,12 +1,9 @@ package org.testcontainers.junit; +import lombok.Cleanup; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; -import org.openqa.selenium.By; -import org.openqa.selenium.remote.DesiredCapabilities; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.testcontainers.containers.BrowserWebDriverContainer; import org.testcontainers.containers.NginxContainer; import java.io.*; @@ -20,18 +17,14 @@ */ public class SimpleNginxTest { - @Rule - public NginxContainer nginx = new NginxContainer() - .withCustomContent(System.getProperty("user.home") + "/.tmp-test-container"); + private static File contentFolder = new File(System.getProperty("user.home") + "/.tmp-test-container"); @Rule - public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() - .withDesiredCapabilities(DesiredCapabilities.chrome()) - .withLinkToContainer(nginx, "nginx"); + public NginxContainer nginx = new NginxContainer() + .withCustomContent(contentFolder.toString()); @BeforeClass public static void setupContent() throws FileNotFoundException { - File contentFolder = new File(System.getProperty("user.home") + "/.tmp-test-container"); contentFolder.mkdir(); contentFolder.setReadable(true, false); contentFolder.setWritable(true, false); @@ -43,9 +36,8 @@ public static void setupContent() throws FileNotFoundException { indexFile.setWritable(true, false); indexFile.setExecutable(true, false); - PrintStream printStream = new PrintStream(new FileOutputStream(indexFile)); + @Cleanup PrintStream printStream = new PrintStream(new FileOutputStream(indexFile)); printStream.println("This worked"); - printStream.close(); } @Test @@ -54,21 +46,10 @@ public void testSimple() throws Exception { info("Base URL is " + nginx.getBaseUrl("http", 80)); URLConnection urlConnection = nginx.getBaseUrl("http", 80).openConnection(); - String line = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())).readLine(); + @Cleanup BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); + String line = reader.readLine(); System.out.println(line); assertTrue("Using URLConnection, an HTTP GET from the nginx server returns the index.html from the custom content directory", line.contains("This worked")); } - - @Test - public void testWebDriverToNginxContainerAccessViaContainerLink() throws Exception { - - info("Base URL is " + nginx.getBaseUrl("http", 80)); - - RemoteWebDriver driver = chrome.getWebDriver(); - - driver.get("http://nginx/"); - - assertEquals("Using selenium, an HTTP GET from the nginx server returns the index.html from the custom content directory", "This worked", driver.findElement(By.tagName("body")).getText()); - } } diff --git a/modules/selenium/pom.xml b/modules/selenium/pom.xml index 76aa623396c..ec8277a9c7e 100644 --- a/modules/selenium/pom.xml +++ b/modules/selenium/pom.xml @@ -48,6 +48,13 @@ 6.1.25 test + + + ${project.groupId} + nginx + ${project.version} + test + diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/LinkedContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/LinkedContainerTest.java new file mode 100644 index 00000000000..b135e92a754 --- /dev/null +++ b/modules/selenium/src/test/java/org/testcontainers/junit/LinkedContainerTest.java @@ -0,0 +1,59 @@ +package org.testcontainers.junit; + +import lombok.Cleanup; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.remote.DesiredCapabilities; +import org.openqa.selenium.remote.RemoteWebDriver; +import org.testcontainers.containers.BrowserWebDriverContainer; +import org.testcontainers.containers.NginxContainer; + +import java.io.*; + +import static org.rnorth.visibleassertions.VisibleAssertions.*; + + +/** + * @author richardnorth + */ +public class LinkedContainerTest { + + private static File contentFolder = new File(System.getProperty("user.home") + "/.tmp-test-container"); + + @Rule + public NginxContainer nginx = new NginxContainer() + .withCustomContent(contentFolder.toString()); + + @Rule + public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() + .withDesiredCapabilities(DesiredCapabilities.chrome()) + .withLinkToContainer(nginx, "nginx"); + + @BeforeClass + public static void setupContent() throws FileNotFoundException { + contentFolder.mkdir(); + contentFolder.setReadable(true, false); + contentFolder.setWritable(true, false); + contentFolder.setExecutable(true, false); + + + File indexFile = new File(contentFolder, "index.html"); + indexFile.setReadable(true, false); + indexFile.setWritable(true, false); + indexFile.setExecutable(true, false); + + @Cleanup PrintStream printStream = new PrintStream(new FileOutputStream(indexFile)); + printStream.println("This worked"); + } + + @Test + public void testWebDriverToNginxContainerAccessViaContainerLink() throws Exception { + RemoteWebDriver driver = chrome.getWebDriver(); + + driver.get("http://nginx/"); + + assertEquals("Using selenium, an HTTP GET from the nginx server returns the index.html from the custom content directory", "This worked", driver.findElement(By.tagName("body")).getText()); + } +} diff --git a/mvnw b/mvnw index 6ecc150ae0a..5bf251c0774 100755 --- a/mvnw +++ b/mvnw @@ -54,38 +54,16 @@ case "`uname`" in CYGWIN*) cygwin=true ;; MINGW*) mingw=true;; Darwin*) darwin=true - # - # Look for the Apple JDKs first to preserve the existing behaviour, and then look - # for the new JDKs provided by Oracle. - # - if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then - # - # Apple JDKs - # - export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home - fi - - if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then - # - # Apple JDKs - # - export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home - fi - - if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then - # - # Oracle JDKs - # - export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home - fi - - if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then - # - # Apple JDKs - # - export JAVA_HOME=`/usr/libexec/java_home` - fi - ;; + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; esac if [ -z "$JAVA_HOME" ] ; then @@ -187,14 +165,25 @@ CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher # traverses directory structure from process work directory to filesystem root # first directory with .mvn subdirectory is considered project base directory find_maven_basedir() { - local basedir=$(pwd) - local wdir=$(pwd) + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" while [ "$wdir" != '/' ] ; do if [ -d "$wdir"/.mvn ] ; then basedir=$wdir break fi - wdir=$(cd "$wdir/.."; pwd) + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround done echo "${basedir}" } @@ -206,7 +195,13 @@ concat_lines() { fi } -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +echo $MAVEN_PROJECTBASEDIR MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" # For Cygwin, switch paths to Windows format before running java @@ -221,14 +216,8 @@ if $cygwin; then MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` fi -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" -export MAVEN_CMD_LINE_ARGS - WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain -# avoid using MAVEN_CMD_LINE_ARGS below since that would loose parameter escaping in $@ exec "$JAVACMD" \ $MAVEN_OPTS \ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ diff --git a/mvnw.cmd b/mvnw.cmd old mode 100644 new mode 100755 index 8bb82754165..48c810ef37a --- a/mvnw.cmd +++ b/mvnw.cmd @@ -1,146 +1,143 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM http://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven2 Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -set MAVEN_CMD_LINE_ARGS=%MAVEN_CONFIG% %* - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" - -set WRAPPER_JAR=""%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -# avoid using MAVEN_CMD_LINE_ARGS below since that would loose parameter escaping in %* -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" + +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml index 105f2361123..8a3719927b4 100644 --- a/pom.xml +++ b/pom.xml @@ -163,6 +163,11 @@ false + + org.apache.maven.plugins + maven-surefire-plugin + 2.20 + From b7f834d451d77773daf73c59efe3ad368309337d Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 18 Jun 2017 07:18:00 +0100 Subject: [PATCH 0115/5615] Add notes on automated release. (#365) * Add notes on automated release. * Make maven publication fully automated (no release confirmation in OSSRH) * Remove redundant release script. * Change dev version to 0-SNAPSHOT --- RELEASING.md | 20 ++++++++++++++++++++ core/pom.xml | 2 +- modules/jdbc-test/pom.xml | 2 +- modules/jdbc/pom.xml | 2 +- modules/mysql/pom.xml | 2 +- modules/nginx/pom.xml | 2 +- modules/oracle-xe/pom.xml | 2 +- modules/postgresql/pom.xml | 2 +- modules/selenium/pom.xml | 2 +- modules/virtuoso/pom.xml | 2 +- pom.xml | 4 ++-- release.sh | 7 ------- 12 files changed, 31 insertions(+), 18 deletions(-) create mode 100644 RELEASING.md delete mode 100755 release.sh diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 00000000000..3a5688291a1 --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,20 @@ +# Release process + +Testcontainers' release process is automated as a Travis deployment. This describes the basic steps for a project member to perform a release. + +## Steps + +1. Ensure that the master branch is building and that tests are passing. +1. Ensure that the [`CHANGELOG`](CHANGELOG.md) file is up to date and includes all merged features. +1. Create a new release on GitHub. **The tag name is used as the Maven version**, so please keep the tag name plain (e.g. 1.2.3). +1. Check that the Travis build passed. +1. Release of published artifacts is fully automated. Once the Travis build completes, there are no further actions to perform on the repository. +1. When available through Maven Central, poke [@whichrich](https://twitter.com/whichrich) to announce the release on Twitter! + +## Internal details + +* The signing and publishing steps are initiated as a script-type Travis CI deployment phase. The [`release/deploy.sh`](release/deploy.sh) script performs this process. +* `mvn version:set` is applied to use the tag name to the version number for the build. +* An encrypted GPG key within the `release` directory is used for signing. This key file is decrypted using Travis secrets. +* Travis secrets also hold key passphrase and Maven Central API username/passwords that are used for publishing. +* The publication process is automated using `nexus-staging-maven-plugin`. diff --git a/core/pom.xml b/core/pom.xml index e7e8935720e..e54426a3fc1 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -6,7 +6,7 @@ org.testcontainers testcontainers-parent - 1.2.2-SNAPSHOT + 0-SNAPSHOT ../pom.xml diff --git a/modules/jdbc-test/pom.xml b/modules/jdbc-test/pom.xml index 57fa0f0b4fe..a9542e70882 100644 --- a/modules/jdbc-test/pom.xml +++ b/modules/jdbc-test/pom.xml @@ -6,7 +6,7 @@ org.testcontainers testcontainers-parent - 1.2.2-SNAPSHOT + 0-SNAPSHOT ../../pom.xml diff --git a/modules/jdbc/pom.xml b/modules/jdbc/pom.xml index ff1ca92bedd..cc06c27bf42 100644 --- a/modules/jdbc/pom.xml +++ b/modules/jdbc/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.2.2-SNAPSHOT + 0-SNAPSHOT ../../pom.xml diff --git a/modules/mysql/pom.xml b/modules/mysql/pom.xml index 86b9f3e1764..5cb792f1b9d 100644 --- a/modules/mysql/pom.xml +++ b/modules/mysql/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.2.2-SNAPSHOT + 0-SNAPSHOT ../../pom.xml diff --git a/modules/nginx/pom.xml b/modules/nginx/pom.xml index bd3a7a41a18..33b7bb8a88f 100644 --- a/modules/nginx/pom.xml +++ b/modules/nginx/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.2.2-SNAPSHOT + 0-SNAPSHOT ../../pom.xml diff --git a/modules/oracle-xe/pom.xml b/modules/oracle-xe/pom.xml index 06394588115..f99ab6870b8 100644 --- a/modules/oracle-xe/pom.xml +++ b/modules/oracle-xe/pom.xml @@ -8,7 +8,7 @@ org.testcontainers testcontainers-parent - 1.2.2-SNAPSHOT + 0-SNAPSHOT ../../pom.xml diff --git a/modules/postgresql/pom.xml b/modules/postgresql/pom.xml index d5cefbb0cb5..7ef8f9eb091 100644 --- a/modules/postgresql/pom.xml +++ b/modules/postgresql/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.2.2-SNAPSHOT + 0-SNAPSHOT ../../pom.xml diff --git a/modules/selenium/pom.xml b/modules/selenium/pom.xml index ec8277a9c7e..d5995b0c0e0 100644 --- a/modules/selenium/pom.xml +++ b/modules/selenium/pom.xml @@ -6,7 +6,7 @@ org.testcontainers testcontainers-parent - 1.2.2-SNAPSHOT + 0-SNAPSHOT ../../pom.xml diff --git a/modules/virtuoso/pom.xml b/modules/virtuoso/pom.xml index 5de1e9716ac..31db0c52880 100644 --- a/modules/virtuoso/pom.xml +++ b/modules/virtuoso/pom.xml @@ -4,7 +4,7 @@ org.testcontainers testcontainers-parent - 1.2.2-SNAPSHOT + 0-SNAPSHOT ../../pom.xml diff --git a/pom.xml b/pom.xml index 8a3719927b4..e7ab613465b 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.testcontainers testcontainers-parent - 1.2.2-SNAPSHOT + 0-SNAPSHOT pom @@ -160,7 +160,7 @@ ossrh https://oss.sonatype.org/ - false + true diff --git a/release.sh b/release.sh deleted file mode 100755 index 600be040400..00000000000 --- a/release.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -set -eou pipefail - -mvn release:prepare -Pproprietary-deps - -mvn release:perform -Pproprietary-deps \ No newline at end of file From 23e599570a5a27b6fe7175d740237de265927903 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 18 Jun 2017 12:21:05 +0200 Subject: [PATCH 0116/5615] Implement pre-flight checks (#363) * Implement pre-flight checks (space, exec, volume mappings, network) * CHANGELOG.md entry * Split checks into separate methods * use tinyImage in GenericContainer and change "Boolean disableChecks" to "boolean" * getDisableChecks -> isDisableChecks --- CHANGELOG.md | 1 + core/pom.xml | 5 + .../testcontainers/DockerClientFactory.java | 151 ++++++++++++------ .../containers/GenericContainer.java | 2 +- .../utility/TestcontainersConfiguration.java | 4 +- pom.xml | 6 - 6 files changed, 109 insertions(+), 60 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 006659a92d9..830bd5c4713 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file. ## UNRELEASED ### Changed - Added `TC_DAEMON` JDBC URL flag to prevent `ContainerDatabaseDriver` from shutting down containers at the time all connections are closed. (#359, #360) +- Added pre-flight checks (can be disabled with `checks.disable` configuration property) (#363) - Removed unused Jersey dependencies (#361) ## [1.3.0] - 2017-06-05 diff --git a/core/pom.xml b/core/pom.xml index e54426a3fc1..0bf0c5b7af2 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -64,6 +64,11 @@ slf4j-ext 1.7.25 + + org.rnorth.visible-assertions + visible-assertions + 1.0.5 + diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 693571802ad..816c058dce6 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -2,18 +2,28 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.CreateContainerCmd; +import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.exception.InternalServerErrorException; import com.github.dockerjava.api.exception.NotFoundException; -import com.github.dockerjava.api.model.Image; -import com.github.dockerjava.api.model.Info; -import com.github.dockerjava.api.model.Version; +import com.github.dockerjava.api.model.*; +import com.github.dockerjava.core.command.ExecStartResultCallback; import com.github.dockerjava.core.command.PullImageResultCallback; - import lombok.Synchronized; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.IOUtils; +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.dockerclient.*; +import org.testcontainers.utility.ComparableVersion; +import org.testcontainers.utility.MountableFile; import org.testcontainers.utility.TestcontainersConfiguration; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.Socket; +import java.nio.charset.Charset; import java.util.List; import java.util.Optional; import java.util.function.BiFunction; @@ -82,7 +92,8 @@ public DockerClient client() { strategy = DockerClientProviderStrategy.getFirstValidStrategy(CONFIGURATION_STRATEGIES); - log.info("Docker host IP address is {}", strategy.getDockerHostIpAddress()); + String hostIpAddress = strategy.getDockerHostIpAddress(); + log.info("Docker host IP address is {}", hostIpAddress); DockerClient client = strategy.getClient(); if (!preconditionsChecked) { @@ -96,15 +107,93 @@ public DockerClient client() { " Operating System: " + dockerInfo.getOperatingSystem() + "\n" + " Total Memory: " + dockerInfo.getMemTotal() / (1024 * 1024) + " MB"); - checkVersion(version.getVersion()); - checkDiskSpaceAndHandleExceptions(client); + if (!TestcontainersConfiguration.getInstance().isDisableChecks()) { + VisibleAssertions.info("Checking the system..."); + + checkDockerVersion(version.getVersion()); + + MountableFile mountableFile = MountableFile.forClasspathResource(this.getClass().getName().replace(".", "/") + ".class"); + + runInsideDocker( + client, + cmd -> cmd + .withCmd("/bin/sh", "-c", "while true; do printf 'hello' | nc -l -p 80; done") + .withBinds(new Bind(mountableFile.getResolvedPath(), new Volume("/dummy"), AccessMode.ro)) + .withExposedPorts(new ExposedPort(80)) + .withPublishAllPorts(true), + (dockerClient, id) -> { + + checkDiskSpace(dockerClient, id); + checkMountableFile(dockerClient, id); + checkExposedPort(hostIpAddress, dockerClient, id); + + return null; + }); + } preconditionsChecked = true; } return client; } - /** + private void checkDockerVersion(String dockerVersion) { + VisibleAssertions.assertThat("Docker version", dockerVersion, new BaseMatcher() { + @Override + public boolean matches(Object o) { + return new ComparableVersion(o.toString()).compareTo(new ComparableVersion("1.6.0")) >= 0; + } + + @Override + public void describeTo(Description description) { + description.appendText("is newer than 1.6.0"); + } + }); + } + + private void checkDiskSpace(DockerClient dockerClient, String id) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + try { + dockerClient + .execStartCmd(dockerClient.execCreateCmd(id).withAttachStdout(true).withCmd("df", "-P").exec().getId()) + .exec(new ExecStartResultCallback(outputStream, null)) + .awaitCompletion(); + } catch (Exception e) { + log.debug("Can't exec disk checking command", e); + } + + DiskSpaceUsage df = parseAvailableDiskSpace(outputStream.toString()); + + VisibleAssertions.assertTrue( + "Docker environment has more than 2GB free", + df.availableMB.map(it -> it >= 2048).orElse(true) + ); + } + + private void checkMountableFile(DockerClient dockerClient, String id) { + try (InputStream stream = dockerClient.copyArchiveFromContainerCmd(id, "/dummy").exec()) { + stream.read(); + VisibleAssertions.pass("File should be mountable"); + } catch (Exception e) { + VisibleAssertions.fail("File should be mountable but fails with " + e.getMessage()); + } + } + + private void checkExposedPort(String hostIpAddress, DockerClient dockerClient, String id) { + InspectContainerResponse inspectedContainer = dockerClient.inspectContainerCmd(id).exec(); + + String portSpec = inspectedContainer.getNetworkSettings().getPorts().getBindings().values().iterator().next()[0].getHostPortSpec(); + + String response; + try (Socket socket = new Socket(hostIpAddress, Integer.parseInt(portSpec))) { + response = IOUtils.toString(socket.getInputStream(), Charset.defaultCharset()); + } catch (IOException e) { + response = e.getMessage(); + } + VisibleAssertions.assertEquals("Exposed port is accessible", "hello", response); + } + + /** * Check whether the image is available locally and pull it otherwise */ private void checkAndPullImage(DockerClient client, String image) { @@ -121,47 +210,6 @@ public String dockerHostIpAddress() { return strategy.getDockerHostIpAddress(); } - private void checkVersion(String version) { - String[] splitVersion = version.split("\\."); - if (Integer.valueOf(splitVersion[0]) <= 1 && Integer.valueOf(splitVersion[1]) < 6) { - throw new IllegalStateException("Docker version 1.6.0+ is required, but version " + version + " was found"); - } - } - - private void checkDiskSpaceAndHandleExceptions(DockerClient client) { - try { - checkDiskSpace(client); - } catch (NotEnoughDiskSpaceException e) { - throw e; - } catch (Exception e) { - log.warn("Encountered and ignored error while checking disk space", e); - } - } - - /** - * Check whether this docker installation is likely to have disk space problems - * @param client an active Docker client - */ - private void checkDiskSpace(DockerClient client) { - DiskSpaceUsage df = runInsideDocker(client, cmd -> cmd.withCmd("df", "-P"), (dockerClient, id) -> { - String logResults = dockerClient.logContainerCmd(id) - .withStdOut(true) - .exec(new LogToStringContainerCallback()) - .toString(); - - return parseAvailableDiskSpace(logResults); - }); - - log.info("Disk utilization in Docker environment is {} ({} )", - df.usedPercent.map(x -> x + "%").orElse("unknown"), - df.availableMB.map(x -> x + " MB available").orElse("unknown available")); - - if (df.availableMB.map(it -> it < 2048).orElse(false)) { - log.error("Docker environment has less than 2GB free - execution is unlikely to succeed so will be aborted."); - throw new NotEnoughDiskSpaceException("Not enough disk space in Docker environment"); - } - } - public T runInsideDocker(Consumer createContainerCmdConsumer, BiFunction block) { if (strategy == null) { client(); @@ -176,9 +224,8 @@ private T runInsideDocker(DockerClient client, Consumer createContainerCmdConsumer.accept(createContainerCmd); String id = createContainerCmd.exec().getId(); - client.startContainerCmd(id).exec(); - try { + client.startContainerCmd(id).exec(); return block.apply(client, id); } finally { try { @@ -199,7 +246,7 @@ private DiskSpaceUsage parseAvailableDiskSpace(String dfOutput) { String[] lines = dfOutput.split("\n"); for (String line : lines) { String[] fields = line.split("\\s+"); - if (fields[5].equals("/")) { + if (fields.length > 5 && fields[5].equals("/")) { int availableKB = Integer.valueOf(fields[3]); df.availableMB = Optional.of(availableKB / 1024); df.usedPercent = Optional.of(Integer.valueOf(fields[4].replace("%", ""))); diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 49a020b0889..859def3d3ba 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -147,7 +147,7 @@ public class GenericContainer> public GenericContainer() { - this("alpine:3.2"); + this(TestcontainersConfiguration.getInstance().getTinyImage()); } public GenericContainer(@NonNull final String dockerImageName) { diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index c969b08d4f6..1c90a1fabae 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -24,7 +24,8 @@ public class TestcontainersConfiguration { private String ambassadorContainerImage = "richnorth/ambassador:latest"; private String vncRecordedContainerImage = "richnorth/vnc-recorder:latest"; - private String tinyImage = "alpine:3.2"; + private String tinyImage = "alpine:3.5"; + private boolean disableChecks = false; private static TestcontainersConfiguration loadConfiguration() { final TestcontainersConfiguration config = new TestcontainersConfiguration(); @@ -44,6 +45,7 @@ private static TestcontainersConfiguration loadConfiguration() { config.ambassadorContainerImage = properties.getProperty("ambassador.container.image", config.ambassadorContainerImage); config.vncRecordedContainerImage = properties.getProperty("vncrecorder.container.image", config.vncRecordedContainerImage); config.tinyImage = properties.getProperty("tinyimage.container.image", config.tinyImage); + config.disableChecks = Boolean.parseBoolean(properties.getProperty("checks.disable", config.disableChecks + "")); log.debug("Testcontainers configuration overrides loaded from {}: {}", configOverrides, config); diff --git a/pom.xml b/pom.xml index e7ab613465b..ba3ecd4c0ad 100644 --- a/pom.xml +++ b/pom.xml @@ -95,12 +95,6 @@ 1.2.3 test - - org.rnorth.visible-assertions - visible-assertions - 1.0.5 - test - From 82485c93abff22907dba4a022f1ed33e17f7bb01 Mon Sep 17 00:00:00 2001 From: glebsts Date: Tue, 20 Jun 2017 15:00:19 +0300 Subject: [PATCH 0117/5615] #371 'posix fallback bug' - added fallback in case of UnsupportedOperationException (#376) * #371 'posix fallback bug' - added fallback in case of UnsupportOperationException * #371 'posix fallback bug' - changelog update --- CHANGELOG.md | 1 + .../src/main/java/org/testcontainers/utility/MountableFile.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 830bd5c4713..48d631b3b4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. - Added `TC_DAEMON` JDBC URL flag to prevent `ContainerDatabaseDriver` from shutting down containers at the time all connections are closed. (#359, #360) - Added pre-flight checks (can be disabled with `checks.disable` configuration property) (#363) - Removed unused Jersey dependencies (#361) +- Fixed non-POSIX fallback for file attribute reading (#371) ## [1.3.0] - 2017-06-05 ### Fixed diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java index 8fef823274c..629c0d803b0 100644 --- a/core/src/main/java/org/testcontainers/utility/MountableFile.java +++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java @@ -267,7 +267,7 @@ private int getUnixFileMode(final String pathAsString) { final Path path = Paths.get(pathAsString); try { return (int) Files.getAttribute(path, "unix:mode"); - } catch (IOException e) { + } catch (IOException | UnsupportedOperationException e) { // fallback for non-posix environments int mode = DEFAULT_FILE_MODE; if (Files.isDirectory(path)) { From 6411ce599c537ccceac08e6f1ff92d28350343b3 Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 21 Jun 2017 06:43:12 +0100 Subject: [PATCH 0118/5615] Add additional guards for cases where AuditLogger params may be null (#370) Also add @Nullable/@NotNull annotations to enable detection of bugs in future --- .../testcontainers/utility/AuditLogger.java | 41 +++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/AuditLogger.java b/core/src/main/java/org/testcontainers/utility/AuditLogger.java index a4aa6493bed..567132da05d 100644 --- a/core/src/main/java/org/testcontainers/utility/AuditLogger.java +++ b/core/src/main/java/org/testcontainers/utility/AuditLogger.java @@ -6,11 +6,14 @@ import lombok.experimental.UtilityClass; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.MDC; import java.util.List; +import static com.google.common.base.Strings.nullToEmpty; + /** * Logger for tracking potentially destructive actions, intended for usage in a shared Docker environment where * traceability is needed. This class uses SLF4J, logging at TRACE level and capturing common fields as MDC fields. @@ -25,14 +28,28 @@ public class AuditLogger { private static final ObjectMapper objectMapper = new ObjectMapper(); public static final String MDC_PREFIX = AuditLogger.class.getCanonicalName(); - public static void doLog(String action, String image, String containerId, DockerCmd cmd) { + public static void doLog(@NotNull String action, + @Nullable String image, + @Nullable String containerId, + @NotNull DockerCmd cmd) { + doLog(action, image, containerId, cmd, null); } - public static void doLog(String action, String image, String containerId, DockerCmd cmd, @Nullable Exception e) { - MDC.put(MDC_PREFIX + ".Action", action); - MDC.put(MDC_PREFIX + ".Image", image); - MDC.put(MDC_PREFIX + ".ContainerId", containerId); + public static void doLog(@NotNull String action, + @Nullable String image, + @Nullable String containerId, + @NotNull DockerCmd cmd, + @Nullable Exception e) { + + if (! log.isTraceEnabled()) { + return; + } + + MDC.put(MDC_PREFIX + ".Action", nullToEmpty(action)); + MDC.put(MDC_PREFIX + ".Image", nullToEmpty(image)); + MDC.put(MDC_PREFIX + ".ContainerId", nullToEmpty(containerId)); + try { MDC.put(MDC_PREFIX + ".Command", objectMapper.writeValueAsString(cmd)); } catch (JsonProcessingException ignored) { @@ -52,11 +69,21 @@ public static void doLog(String action, String image, String containerId, Docker MDC.remove(MDC_PREFIX + ".Exception"); } - public static void doComposeLog(String[] commandParts, List env) { + public static void doComposeLog(@NotNull String[] commandParts, + @Nullable List env) { + + if (! log.isTraceEnabled()) { + return; + } + MDC.put(MDC_PREFIX + ".Action", "COMPOSE"); + + if (env != null) { + MDC.put(MDC_PREFIX + ".Compose.Env", env.toString()); + } + final String command = StringUtils.join(commandParts, ' '); MDC.put(MDC_PREFIX + ".Compose.Command", command); - MDC.put(MDC_PREFIX + ".Compose.Env", env.toString()); log.trace("COMPOSE action with command: {}, env: {}", command, env); From 7af0dc57929993d1fe9183e5b10a3b5a6ecaab76 Mon Sep 17 00:00:00 2001 From: Josh C Date: Wed, 21 Jun 2017 00:45:29 -0500 Subject: [PATCH 0119/5615] add container arguments to specify SELinux contexts for mounts (#334) * add container arguments to specify SELinux contexts for mounts * remove unused import in test * address review requests comments for formatting, lombok annotations, and clarity around SELinux --- .../testcontainers/containers/Container.java | 36 ++++++++++++++++--- .../containers/GenericContainer.java | 16 ++++++--- .../containers/SelinuxContext.java | 17 +++++++++ .../junit/GenericContainerRuleTest.java | 17 +++++++++ 4 files changed, 78 insertions(+), 8 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/containers/SelinuxContext.java diff --git a/core/src/main/java/org/testcontainers/containers/Container.java b/core/src/main/java/org/testcontainers/containers/Container.java index 5b2f60b7232..35abe058168 100644 --- a/core/src/main/java/org/testcontainers/containers/Container.java +++ b/core/src/main/java/org/testcontainers/containers/Container.java @@ -80,11 +80,24 @@ public String getStderr() { * Adds a file system binding. Consider using {@link #withFileSystemBind(String, String, BindMode)} * for building a container in a fluent style. * - * @param hostPath the file system path on the host + * @param hostPath the file system path on the host * @param containerPath the file system path inside the container - * @param mode the bind mode + * @param mode the bind mode + */ + default void addFileSystemBind(final String hostPath, final String containerPath, final BindMode mode) { + addFileSystemBind(hostPath, containerPath, mode, SelinuxContext.NONE); + } + + /** + * Adds a file system binding. Consider using {@link #withFileSystemBind(String, String, BindMode)} + * for building a container in a fluent style. + * + * @param hostPath the file system path on the host + * @param containerPath the file system path inside the container + * @param mode the bind mode + * @param selinuxContext selinux context argument to use for this file */ - void addFileSystemBind(String hostPath, String containerPath, BindMode mode); + void addFileSystemBind(String hostPath, String containerPath, BindMode mode, SelinuxContext selinuxContext); /** * Add a link to another container. @@ -205,7 +218,22 @@ public String getStderr() { * @param mode access mode for the file * @return this */ - SELF withClasspathResourceMapping(String resourcePath, String containerPath, BindMode mode); + default SELF withClasspathResourceMapping(final String resourcePath, final String containerPath, final BindMode mode) { + withClasspathResourceMapping(resourcePath, containerPath, mode, SelinuxContext.NONE); + return self(); + } + + /** + * Map a resource (file or directory) on the classpath to a path inside the container. + * This will only work if you are running your tests outside a Docker container. + * + * @param resourcePath path to the resource on the classpath (relative to the classpath root; should not start with a leading slash) + * @param containerPath path this should be mapped to inside the container + * @param mode access mode for the file + * @param selinuxContext selinux context argument to use for this file + * @return this + */ + SELF withClasspathResourceMapping(String resourcePath, String containerPath, BindMode mode, SelinuxContext selinuxContext); /** * Set the duration of waiting time until container treated as started. diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 859def3d3ba..09494c78610 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -485,10 +485,10 @@ public void addEnv(String key, String value) { * {@inheritDoc} */ @Override - public void addFileSystemBind(String hostPath, String containerPath, BindMode mode) { + public void addFileSystemBind(final String hostPath, final String containerPath, final BindMode mode, final SelinuxContext selinuxContext) { final MountableFile mountableFile = MountableFile.forHostPath(hostPath); - binds.add(new Bind(mountableFile.getResolvedPath(), new Volume(containerPath), mode.accessMode)); + binds.add(new Bind(mountableFile.getResolvedPath(), new Volume(containerPath), mode.accessMode, selinuxContext.selContext)); } /** @@ -619,10 +619,18 @@ public SELF withNetworkMode(String networkMode) { * {@inheritDoc} */ @Override - public SELF withClasspathResourceMapping(String resourcePath, String containerPath, BindMode mode) { + public SELF withClasspathResourceMapping(final String resourcePath, final String containerPath, final BindMode mode) { + return withClasspathResourceMapping(resourcePath, containerPath, mode, SelinuxContext.NONE); + } + + /** + * {@inheritDoc} + */ + @Override + public SELF withClasspathResourceMapping(final String resourcePath, final String containerPath, final BindMode mode, final SelinuxContext selinuxContext) { final MountableFile mountableFile = MountableFile.forClasspathResource(resourcePath); - this.addFileSystemBind(mountableFile.getResolvedPath(), containerPath, mode); + this.addFileSystemBind(mountableFile.getResolvedPath(), containerPath, mode, selinuxContext); return self(); } diff --git a/core/src/main/java/org/testcontainers/containers/SelinuxContext.java b/core/src/main/java/org/testcontainers/containers/SelinuxContext.java new file mode 100644 index 00000000000..735d3e3a277 --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/SelinuxContext.java @@ -0,0 +1,17 @@ +package org.testcontainers.containers; + +import com.github.dockerjava.api.model.SELContext; +import lombok.AllArgsConstructor; + +/** + * Possible contexts for use with SELinux + */ +@AllArgsConstructor +public enum SelinuxContext { + SHARED(SELContext.shared), + SINGLE(SELContext.single), + NONE(SELContext.none); + + public final SELContext selContext; + +} diff --git a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java index 59d9cb2b34f..45c1a78d10f 100644 --- a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java +++ b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java @@ -25,6 +25,8 @@ import static org.rnorth.visibleassertions.VisibleAssertions.*; import static org.testcontainers.containers.BindMode.READ_ONLY; +import static org.testcontainers.containers.BindMode.READ_WRITE; +import static org.testcontainers.containers.SelinuxContext.SHARED; /** * Tests for GenericContainerRules @@ -101,6 +103,15 @@ public static void setupContent() throws FileNotFoundException { .withClasspathResourceMapping("mappable-resource/test-resource.txt", "/content.txt", READ_ONLY) .withCommand("/bin/sh", "-c", "while true; do cat /content.txt | nc -l -p 80; done"); + /** + * Map a file on the classpath to a file in the container, and then expose the content for testing. + */ + @ClassRule + public static GenericContainer alpineClasspathResourceSelinux = new GenericContainer("alpine:3.2") + .withExposedPorts(80) + .withClasspathResourceMapping("mappable-resource/test-resource.txt", "/content.txt", READ_WRITE, SHARED) + .withCommand("/bin/sh", "-c", "while true; do cat /content.txt | nc -l -p 80; done"); + /** * Create a container with an extra host entry and expose the content of /etc/hosts for testing. */ @@ -203,6 +214,12 @@ public void customClasspathResourceMappingTest() throws IOException { assertEquals("Resource on the classpath can be mapped using calls to withClasspathResourceMapping", "FOOBAR", line); } + @Test + public void customClasspathResourceMappingWithSelinuxTest() throws IOException { + String line = getReaderForContainerPort80(alpineClasspathResourceSelinux).readLine(); + assertEquals("Resource on the classpath can be mapped using calls to withClasspathResourceMappingSelinux", "FOOBAR", line); + } + @Test public void exceptionThrownWhenMappedPortNotFound() throws IOException { assertThrows("When the requested port is not mapped, getMappedPort() throws an exception", From 0997c0f7badaf7b7aec77bac53849964ed3324ba Mon Sep 17 00:00:00 2001 From: bsideup Date: Wed, 21 Jun 2017 19:13:30 +0200 Subject: [PATCH 0120/5615] fix Travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 85762826124..1c5c34814f8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,7 +24,7 @@ cache: install: - echo "MAVEN_OPTS='-XX:+TieredCompilation -XX:TieredStopAtLevel=1'" > ~/.mavenrc - sudo apt-get update -qq - - sudo apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confnew install docker-engine + - sudo apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confnew install docker-ce - docker info # Update env vars required by testcontainers - export DOCKER_HOST=tcp://127.0.0.1:2375 From 81952c429385eb03cf1070c82608f679af7163a4 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 21 Jun 2017 22:32:43 +0200 Subject: [PATCH 0121/5615] Support prioritization of DockerClientProviderStrategies (#362) * Support prioritization of DockerClientProviderStrategies * Support storing the global configuration in user's home folder. Store selected DockerClientProviderStrategy globally. * Add changelog and priority JavaDoc --- CHANGELOG.md | 2 + .../DockerClientProviderStrategy.java | 137 ++++++++++++------ .../DockerMachineClientProviderStrategy.java | 16 +- ...dSystemPropertyClientProviderStrategy.java | 27 +++- ...oxiedUnixSocketClientProviderStrategy.java | 25 +++- .../UnixSocketClientProviderStrategy.java | 17 ++- .../WindowsClientProviderStrategy.java | 6 + .../utility/TestcontainersConfiguration.java | 115 +++++++++++---- 8 files changed, 248 insertions(+), 97 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48d631b3b4b..ebe1a458678 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ All notable changes to this project will be documented in this file. - Added pre-flight checks (can be disabled with `checks.disable` configuration property) (#363) - Removed unused Jersey dependencies (#361) - Fixed non-POSIX fallback for file attribute reading (#371) +- Improved startup time by adding dynamic priorities to DockerClientProviderStrategy (#362) +- Added global configuration file `~/.testcontainers.properties` (#362) ## [1.3.0] - 2017-06-05 ### Fixed diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index 48b556d68a5..bc1cf2ea174 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -5,16 +5,22 @@ import com.github.dockerjava.core.DockerClientConfig; import com.github.dockerjava.netty.NettyDockerCmdExecFactory; import com.google.common.base.Throwables; +import org.apache.commons.io.IOUtils; import org.jetbrains.annotations.Nullable; +import org.rnorth.ducttape.TimeoutException; import org.rnorth.ducttape.ratelimits.RateLimiter; import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; import org.rnorth.ducttape.unreliables.Unreliables; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.testcontainers.utility.TestcontainersConfiguration; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; +import java.util.Objects; import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; /** * Mechanism to find a viable Docker client configuration according to the host system environment. @@ -39,6 +45,17 @@ public abstract class DockerClientProviderStrategy { */ public abstract String getDescription(); + protected boolean isApplicable() { + return true; + } + + /** + * @return highest to lowest priority value + */ + protected int getPriority() { + return 0; + } + protected static final Logger LOGGER = LoggerFactory.getLogger(DockerClientProviderStrategy.class); /** @@ -49,45 +66,70 @@ public abstract class DockerClientProviderStrategy { public static DockerClientProviderStrategy getFirstValidStrategy(List strategies) { List configurationFailures = new ArrayList<>(); - for (DockerClientProviderStrategy strategy : strategies) { - try { - strategy.test(); - LOGGER.info("Looking for Docker environment. Tried {}", strategy.getDescription()); - return strategy; - } catch (Exception | ExceptionInInitializerError | NoClassDefFoundError e) { - @Nullable String throwableMessage = e.getMessage(); - @SuppressWarnings("ThrowableResultOfMethodCallIgnored") - Throwable rootCause = Throwables.getRootCause(e); - @Nullable String rootCauseMessage = rootCause.getMessage(); - - String failureDescription; - if (throwableMessage != null && throwableMessage.equals(rootCauseMessage)) { - failureDescription = String.format("%s: failed with exception %s (%s)", - strategy.getClass().getSimpleName(), - e.getClass().getSimpleName(), - throwableMessage); - } else { - failureDescription = String.format("%s: failed with exception %s (%s). Root cause %s (%s)", - strategy.getClass().getSimpleName(), - e.getClass().getSimpleName(), - throwableMessage, - rootCause.getClass().getSimpleName(), - rootCauseMessage - ); - } - configurationFailures.add(failureDescription); - - LOGGER.debug(failureDescription); - } - } - - LOGGER.error("Could not find a valid Docker environment. Please check configuration. Attempted configurations were:"); - for (String failureMessage : configurationFailures) { - LOGGER.error(" " + failureMessage); - } - LOGGER.error("As no valid configuration was found, execution cannot continue"); - - throw new IllegalStateException("Could not find a valid Docker environment. Please see logs and check configuration"); + return Stream + .concat( + Stream + .of(TestcontainersConfiguration.getInstance().getDockerClientStrategyClassName()) + .filter(Objects::nonNull) + .flatMap(it -> { + try { + Class strategyClass = (Class) Thread.currentThread().getContextClassLoader().loadClass(it); + return Stream.of(strategyClass.newInstance()); + } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { + LOGGER.warn("Can't instantiate a strategy from " + it, e); + return Stream.empty(); + } + }), + strategies + .stream() + .filter(DockerClientProviderStrategy::isApplicable) + .sorted(Comparator.comparing(DockerClientProviderStrategy::getPriority).reversed()) + ) + .flatMap(strategy -> { + try { + strategy.test(); + LOGGER.info("Found Docker environment with {}", strategy.getDescription()); + + TestcontainersConfiguration.getInstance().updateGlobalConfig("docker.client.strategy", strategy.getClass().getName()); + + return Stream.of(strategy); + } catch (Exception | ExceptionInInitializerError | NoClassDefFoundError e) { + @Nullable String throwableMessage = e.getMessage(); + @SuppressWarnings("ThrowableResultOfMethodCallIgnored") + Throwable rootCause = Throwables.getRootCause(e); + @Nullable String rootCauseMessage = rootCause.getMessage(); + + String failureDescription; + if (throwableMessage != null && throwableMessage.equals(rootCauseMessage)) { + failureDescription = String.format("%s: failed with exception %s (%s)", + strategy.getClass().getSimpleName(), + e.getClass().getSimpleName(), + throwableMessage); + } else { + failureDescription = String.format("%s: failed with exception %s (%s). Root cause %s (%s)", + strategy.getClass().getSimpleName(), + e.getClass().getSimpleName(), + throwableMessage, + rootCause.getClass().getSimpleName(), + rootCauseMessage + ); + } + configurationFailures.add(failureDescription); + + LOGGER.debug(failureDescription); + return Stream.empty(); + } + }) + .findAny() + .orElseThrow(() -> { + LOGGER.error("Could not find a valid Docker environment. Please check configuration. Attempted configurations were:"); + for (String failureMessage : configurationFailures) { + LOGGER.error(" " + failureMessage); + } + LOGGER.error("As no valid configuration was found, execution cannot continue"); + + return new IllegalStateException("Could not find a valid Docker environment. Please see logs and check configuration"); + }); } /** @@ -105,13 +147,18 @@ protected DockerClient getClientForConfig(DockerClientConfig config) { } protected void ping(DockerClient client, int timeoutInSeconds) { - Unreliables.retryUntilSuccess(timeoutInSeconds, TimeUnit.SECONDS, () -> { - return PING_RATE_LIMITER.getWhenReady(() -> { - LOGGER.debug("Pinging docker daemon..."); - client.pingCmd().exec(); - return true; + try { + Unreliables.retryUntilSuccess(timeoutInSeconds, TimeUnit.SECONDS, () -> { + return PING_RATE_LIMITER.getWhenReady(() -> { + LOGGER.debug("Pinging docker daemon..."); + client.pingCmd().exec(); + return true; + }); }); - }); + } catch (TimeoutException e) { + IOUtils.closeQuietly(client); + throw e; + } } public String getDockerHostIpAddress() { diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java index 59211e317a2..b06dc0fa697 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java @@ -1,6 +1,7 @@ package org.testcontainers.dockerclient; import com.github.dockerjava.core.DefaultDockerClientConfig; +import lombok.extern.slf4j.Slf4j; import org.testcontainers.utility.CommandLine; import org.testcontainers.utility.DockerMachineClient; @@ -13,10 +14,21 @@ /** * Use Docker machine (if available on the PATH) to locate a Docker environment. */ +@Slf4j public class DockerMachineClientProviderStrategy extends DockerClientProviderStrategy { private static final String PING_TIMEOUT_DEFAULT = "30"; private static final String PING_TIMEOUT_PROPERTY_NAME = "testcontainers.dockermachineprovider.timeout"; + @Override + protected boolean isApplicable() { + return DockerMachineClient.instance().isInstalled(); + } + + @Override + protected int getPriority() { + return ProxiedUnixSocketClientProviderStrategy.PRIORITY - 10; + } + @Override public void test() throws InvalidConfigurationException { @@ -28,13 +40,13 @@ public void test() throws InvalidConfigurationException { checkArgument(machineNameOptional.isPresent(), "docker-machine is installed but no default machine could be found"); String machineName = machineNameOptional.get(); - LOGGER.info("Found docker-machine, and will use machine named {}", machineName); + log.info("Found docker-machine, and will use machine named {}", machineName); DockerMachineClient.instance().ensureMachineRunning(machineName); String dockerDaemonIpAddress = DockerMachineClient.instance().getDockerDaemonIpAddress(machineName); - LOGGER.info("Docker daemon IP address for docker machine {} is {}", machineName, dockerDaemonIpAddress); + log.info("Docker daemon IP address for docker machine {} is {}", machineName, dockerDaemonIpAddress); config = DefaultDockerClientConfig.createDefaultConfigBuilder() .withDockerHost("tcp://" + dockerDaemonIpAddress + ":2376") diff --git a/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java index ed482abc36b..195a07a4024 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java @@ -2,31 +2,50 @@ import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.core.DockerClientConfig; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.SystemUtils; /** * Use environment variables and system properties (as supported by the underlying DockerClient DefaultConfigBuilder) * to try and locate a docker environment. */ +@Slf4j public class EnvironmentAndSystemPropertyClientProviderStrategy extends DockerClientProviderStrategy { + + public static final int PRIORITY = 100; + private static final String PING_TIMEOUT_DEFAULT = "10"; private static final String PING_TIMEOUT_PROPERTY_NAME = "testcontainers.environmentprovider.timeout"; + public EnvironmentAndSystemPropertyClientProviderStrategy() { + // Try using environment variables + config = DefaultDockerClientConfig.createDefaultConfigBuilder().build(); + } + + @Override + protected boolean isApplicable() { + return "tcp".equalsIgnoreCase(config.getDockerHost().getScheme()) || SystemUtils.IS_OS_LINUX; + } + + @Override + protected int getPriority() { + return PRIORITY; + } + @Override public void test() throws InvalidConfigurationException { try { - // Try using environment variables - config = DefaultDockerClientConfig.createDefaultConfigBuilder().build(); client = getClientForConfig(config); final int timeout = Integer.parseInt(System.getProperty(PING_TIMEOUT_PROPERTY_NAME, PING_TIMEOUT_DEFAULT)); ping(client, timeout); } catch (Exception | UnsatisfiedLinkError e) { - LOGGER.error("ping failed with configuration {} due to {}", getDescription(), e.toString(), e); + log.error("ping failed with configuration {} due to {}", getDescription(), e.toString(), e); throw new InvalidConfigurationException("ping failed"); } - LOGGER.info("Found docker client settings from environment"); + log.info("Found docker client settings from environment"); } @Override diff --git a/core/src/main/java/org/testcontainers/dockerclient/ProxiedUnixSocketClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/ProxiedUnixSocketClientProviderStrategy.java index 7036e048b34..ffd689fcb9e 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/ProxiedUnixSocketClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/ProxiedUnixSocketClientProviderStrategy.java @@ -1,27 +1,38 @@ package org.testcontainers.dockerclient; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.SystemUtils; import org.rnorth.tcpunixsocketproxy.TcpToUnixSocketProxy; import java.io.File; +@Slf4j public class ProxiedUnixSocketClientProviderStrategy extends UnixSocketClientProviderStrategy { + public static final int PRIORITY = EnvironmentAndSystemPropertyClientProviderStrategy.PRIORITY - 10; + + private final File socketFile = new File(DOCKER_SOCK_PATH); + @Override - public void test() throws InvalidConfigurationException { + protected boolean isApplicable() { + return !SystemUtils.IS_OS_LINUX && socketFile.exists(); + } - String osName = System.getProperty("os.name").toLowerCase(); - if (!osName.contains("mac") && !osName.contains("linux")) { - throw new InvalidConfigurationException("this strategy is only applicable to OS X and Linux"); - } + @Override + protected int getPriority() { + return PRIORITY; + } - TcpToUnixSocketProxy proxy = new TcpToUnixSocketProxy(new File(DOCKER_SOCK_PATH)); + @Override + public void test() throws InvalidConfigurationException { + TcpToUnixSocketProxy proxy = new TcpToUnixSocketProxy(socketFile); try { int proxyPort = proxy.start().getPort(); config = tryConfiguration("tcp://localhost:" + proxyPort); - LOGGER.info("Accessing unix domain socket via TCP proxy (" + DOCKER_SOCK_PATH + " via localhost:" + proxyPort + ")"); + log.debug("Accessing unix domain socket via TCP proxy (" + DOCKER_SOCK_PATH + " via localhost:" + proxyPort + ")"); } catch (Exception e) { proxy.stop(); diff --git a/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java index e4aac02e6ed..774f3184ef3 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java @@ -2,6 +2,8 @@ import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.core.DockerClientConfig; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.SystemUtils; import org.jetbrains.annotations.NotNull; import java.io.IOException; @@ -9,6 +11,7 @@ import java.nio.file.Path; import java.nio.file.Paths; +@Slf4j public class UnixSocketClientProviderStrategy extends DockerClientProviderStrategy { protected static final String DOCKER_SOCK_PATH = "/var/run/docker.sock"; private static final String SOCKET_LOCATION = "unix://" + DOCKER_SOCK_PATH; @@ -16,18 +19,16 @@ public class UnixSocketClientProviderStrategy extends DockerClientProviderStrate private static final String PING_TIMEOUT_DEFAULT = "10"; private static final String PING_TIMEOUT_PROPERTY_NAME = "testcontainers.unixsocketprovider.timeout"; - @Override - public void test() - throws InvalidConfigurationException { - - if (!System.getProperty("os.name").toLowerCase().contains("linux")) { - throw new InvalidConfigurationException("this strategy is only applicable to Linux"); - } + protected boolean isApplicable() { + return SystemUtils.IS_OS_LINUX; + } + @Override + public void test() throws InvalidConfigurationException { try { config = tryConfiguration(SOCKET_LOCATION); - LOGGER.info("Accessing docker with local Unix socket"); + log.info("Accessing docker with local Unix socket"); } catch (Exception | UnsatisfiedLinkError e) { throw new InvalidConfigurationException("ping failed", e); } diff --git a/core/src/main/java/org/testcontainers/dockerclient/WindowsClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/WindowsClientProviderStrategy.java index 15cd35c48bf..f53ac10d044 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/WindowsClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/WindowsClientProviderStrategy.java @@ -2,6 +2,7 @@ import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.core.DockerClientConfig; +import org.apache.commons.lang.SystemUtils; import org.jetbrains.annotations.NotNull; public class WindowsClientProviderStrategy extends DockerClientProviderStrategy { @@ -9,6 +10,11 @@ public class WindowsClientProviderStrategy extends DockerClientProviderStrategy private static final int PING_TIMEOUT_DEFAULT = 5; private static final String PING_TIMEOUT_PROPERTY_NAME = "testcontainers.windowsprovider.timeout"; + @Override + protected boolean isApplicable() { + return SystemUtils.IS_OS_WINDOWS; + } + @Override public void test() throws InvalidConfigurationException { config = tryConfiguration("tcp://localhost:2375"); diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 1c90a1fabae..a47b2b90637 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -1,57 +1,110 @@ package org.testcontainers.utility; -import com.google.common.base.MoreObjects; -import lombok.AccessLevel; -import lombok.Data; -import lombok.Getter; -import lombok.NoArgsConstructor; +import lombok.*; import lombok.extern.slf4j.Slf4j; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; +import java.io.*; +import java.net.MalformedURLException; +import java.util.Objects; import java.util.Properties; +import java.util.stream.Stream; /** * Provides a mechanism for fetching configuration/defaults from the classpath. */ @Data -@Slf4j @NoArgsConstructor(access = AccessLevel.PRIVATE) +@Slf4j +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) public class TestcontainersConfiguration { + private static String PROPERTIES_FILE_NAME = "testcontainers.properties"; + + private static File GLOBAL_CONFIG_FILE = new File(System.getProperty("user.home"), "." + PROPERTIES_FILE_NAME); + @Getter(lazy = true) private static final TestcontainersConfiguration instance = loadConfiguration(); - private String ambassadorContainerImage = "richnorth/ambassador:latest"; - private String vncRecordedContainerImage = "richnorth/vnc-recorder:latest"; - private String tinyImage = "alpine:3.5"; - private boolean disableChecks = false; + private final Properties properties; - private static TestcontainersConfiguration loadConfiguration() { - final TestcontainersConfiguration config = new TestcontainersConfiguration(); + public String getAmbassadorContainerImage() { + return (String) properties.getOrDefault("ambassador.container.image", "richnorth/ambassador:latest"); + } - ClassLoader loader = MoreObjects.firstNonNull( - Thread.currentThread().getContextClassLoader(), - TestcontainersConfiguration.class.getClassLoader()); - final URL configOverrides = loader.getResource("testcontainers.properties"); - if (configOverrides != null) { + public String getVncRecordedContainerImage() { + return (String) properties.getOrDefault("vncrecorder.container.image", "richnorth/vnc-recorder:latest"); + } - log.debug("Testcontainers configuration overrides will be loaded from {}", configOverrides); + public String getTinyImage() { + return (String) properties.getOrDefault("tinyimage.container.image", "alpine:3.5"); + } - final Properties properties = new Properties(); - try (final InputStream inputStream = configOverrides.openStream()) { - properties.load(inputStream); + public boolean isDisableChecks() { + return Boolean.parseBoolean((String) properties.getOrDefault("checks.disable", "false")); + } - config.ambassadorContainerImage = properties.getProperty("ambassador.container.image", config.ambassadorContainerImage); - config.vncRecordedContainerImage = properties.getProperty("vncrecorder.container.image", config.vncRecordedContainerImage); - config.tinyImage = properties.getProperty("tinyimage.container.image", config.tinyImage); - config.disableChecks = Boolean.parseBoolean(properties.getProperty("checks.disable", config.disableChecks + "")); + public String getDockerClientStrategyClassName() { + return (String) properties.get("docker.client.strategy"); + } - log.debug("Testcontainers configuration overrides loaded from {}: {}", configOverrides, config); + @Synchronized + public boolean updateGlobalConfig(@NonNull String prop, @NonNull String value) { + try { + Properties globalProperties = new Properties(); + GLOBAL_CONFIG_FILE.createNewFile(); + try (InputStream inputStream = new FileInputStream(GLOBAL_CONFIG_FILE)) { + globalProperties.load(inputStream); + } - } catch (IOException e) { - log.error("Testcontainers config override was found on classpath but could not be loaded", e); + if (value.equals(globalProperties.get(prop))) { + return false; } + + globalProperties.setProperty(prop, value); + + try (OutputStream outputStream = new FileOutputStream(GLOBAL_CONFIG_FILE)) { + globalProperties.store(outputStream, "Modified by Testcontainers"); + } + + // Update internal state only if global config was successfully updated + properties.setProperty(prop, value); + return true; + } catch (Exception e) { + log.debug("Can't store global property {} in {}", prop, GLOBAL_CONFIG_FILE); + return false; + } + } + + @SneakyThrows(MalformedURLException.class) + private static TestcontainersConfiguration loadConfiguration() { + final TestcontainersConfiguration config = new TestcontainersConfiguration( + Stream + .of( + TestcontainersConfiguration.class.getClassLoader().getResource(PROPERTIES_FILE_NAME), + Thread.currentThread().getContextClassLoader().getResource(PROPERTIES_FILE_NAME), + GLOBAL_CONFIG_FILE.toURI().toURL() + ) + .filter(Objects::nonNull) + .map(it -> { + log.debug("Testcontainers configuration overrides will be loaded from {}", it); + + final Properties subProperties = new Properties(); + try (final InputStream inputStream = it.openStream()) { + subProperties.load(inputStream); + } catch (FileNotFoundException e) { + log.trace("Testcontainers config override was found on " + it + " but the file was not found", e); + } catch (IOException e) { + log.warn("Testcontainers config override was found on " + it + " but could not be loaded", e); + } + return subProperties; + }) + .reduce(new Properties(), (a, b) -> { + a.putAll(b); + return a; + }) + ); + + if (!config.getProperties().isEmpty()) { + log.debug("Testcontainers configuration overrides loaded from {}", config); } return config; From 0932d5b5e29abcd2108ad291bc0cf322bbec8253 Mon Sep 17 00:00:00 2001 From: bsideup Date: Thu, 22 Jun 2017 16:41:53 +0200 Subject: [PATCH 0122/5615] re #361 put back jersey-common dependency (it's required for docker-machine integration) --- core/pom.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/core/pom.xml b/core/pom.xml index 0bf0c5b7af2..40caa201479 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -37,6 +37,20 @@ + + + org.glassfish.jersey.core + jersey-common + 2.23.1 + + + + * + * + + + + javax.ws.rs javax.ws.rs-api From 3daaa2608e79a4d9fef324e428b914dba878a6d3 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 22 Jun 2017 21:07:39 +0100 Subject: [PATCH 0123/5615] Update changelog for 1.3.1 release --- CHANGELOG.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ebe1a458678..c819feec4e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,21 @@ # Change Log All notable changes to this project will be documented in this file. -## UNRELEASED +## [1.3.1] - 2017-06-22 +### Fixed +- Fixed non-POSIX fallback for file attribute reading (#371) +- Fixed NullPointerException in AuditLogger when running using slf4j-log4j12 bridge (#375) +- Improved cleanup of JDBC connections during database container startup checks + ### Changed +- Extracted MariaDB into a separate repository (#337) - Added `TC_DAEMON` JDBC URL flag to prevent `ContainerDatabaseDriver` from shutting down containers at the time all connections are closed. (#359, #360) - Added pre-flight checks (can be disabled with `checks.disable` configuration property) (#363) -- Removed unused Jersey dependencies (#361) -- Fixed non-POSIX fallback for file attribute reading (#371) - Improved startup time by adding dynamic priorities to DockerClientProviderStrategy (#362) - Added global configuration file `~/.testcontainers.properties` (#362) +- Added container arguments to specify SELinux contexts for mounts (#334) +- Removed unused Jersey dependencies (#361) +- Removed deprecated, wrongly-generated setters from `GenericContainer` ## [1.3.0] - 2017-06-05 ### Fixed From f7897a811fe1795b6335a160699d532b4e58cfdf Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 22 Jun 2017 21:17:09 +0100 Subject: [PATCH 0124/5615] Trigger build of proprietary-deps (Oracle, Virtuoso) during automated release --- release/deploy.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release/deploy.sh b/release/deploy.sh index c734fb028c6..fc4203014be 100755 --- a/release/deploy.sh +++ b/release/deploy.sh @@ -3,5 +3,5 @@ openssl aes-256-cbc -K $encrypted_291cb5a97669_key -iv $encrypted_291cb5a97669_iv -in release/codesigning.asc.enc -out release/codesigning.asc -d gpg --fast-import release/codesigning.asc -mvn versions:set -DnewVersion=$TRAVIS_TAG -mvn deploy -P sign,build-extras,!test-only -DskipTests --settings release/settings.xml +mvn versions:set -P proprietary-deps -DnewVersion=$TRAVIS_TAG +mvn deploy -P proprietary-deps,sign,build-extras,!test-only -DskipTests --settings release/settings.xml From 78fab02ae0d9cb0a28563e3cb141bb42164ba846 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 22 Jun 2017 23:19:25 +0100 Subject: [PATCH 0125/5615] Revert "Trigger build of proprietary-deps (Oracle, Virtuoso) during automated release" f7897a811fe1795b6335a160699d532b4e58cfdf --- release/deploy.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release/deploy.sh b/release/deploy.sh index fc4203014be..c734fb028c6 100755 --- a/release/deploy.sh +++ b/release/deploy.sh @@ -3,5 +3,5 @@ openssl aes-256-cbc -K $encrypted_291cb5a97669_key -iv $encrypted_291cb5a97669_iv -in release/codesigning.asc.enc -out release/codesigning.asc -d gpg --fast-import release/codesigning.asc -mvn versions:set -P proprietary-deps -DnewVersion=$TRAVIS_TAG -mvn deploy -P proprietary-deps,sign,build-extras,!test-only -DskipTests --settings release/settings.xml +mvn versions:set -DnewVersion=$TRAVIS_TAG +mvn deploy -P sign,build-extras,!test-only -DskipTests --settings release/settings.xml From 82d9d15f96e0a3a29ce324edeb09e255e60325ae Mon Sep 17 00:00:00 2001 From: bsideup Date: Fri, 23 Jun 2017 10:27:11 +0200 Subject: [PATCH 0126/5615] add "UNRELEASED" section to CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c819feec4e5..a95fa857274 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # Change Log All notable changes to this project will be documented in this file. +## UNRELEASED +### Fixed + +### Changed + ## [1.3.1] - 2017-06-22 ### Fixed - Fixed non-POSIX fallback for file attribute reading (#371) From 3e513bea8e2d62b5a5a1c37874094d169e27c46f Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 25 Jun 2017 18:23:59 +0200 Subject: [PATCH 0127/5615] Add support for Docker networks. (#372) Add support for Docker networks --- CHANGELOG.md | 1 + .../testcontainers/containers/Container.java | 18 ++++ .../containers/GenericContainer.java | 23 ++++- .../testcontainers/containers/Network.java | 78 +++++++++++++++ .../utility/ResourceReaper.java | 43 +++++---- .../containers/NetworkTest.java | 94 +++++++++++++++++++ .../junit/LinkedContainerTest.java | 14 ++- 7 files changed, 249 insertions(+), 22 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/containers/Network.java create mode 100644 core/src/test/java/org/testcontainers/containers/NetworkTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index a95fa857274..27aa66bb4f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. ### Fixed ### Changed +- Added support for Docker networks (#372) ## [1.3.1] - 2017-06-22 ### Fixed diff --git a/core/src/main/java/org/testcontainers/containers/Container.java b/core/src/main/java/org/testcontainers/containers/Container.java index 35abe058168..2f2d9fa0917 100644 --- a/core/src/main/java/org/testcontainers/containers/Container.java +++ b/core/src/main/java/org/testcontainers/containers/Container.java @@ -209,6 +209,24 @@ default void addFileSystemBind(final String hostPath, final String containerPath */ SELF withNetworkMode(String networkMode); + /** + * Set the network for this container, similar to the --network <name> + * option on the docker CLI. + * + * @param network the instance of {@link Network} + * @return this + */ + SELF withNetwork(Network network); + + /** + * Set the network aliases for this container, similar to the --network-alias <my-service> + * option on the docker CLI. + * + * @param aliases the list of aliases + * @return this + */ + SELF withNetworkAliases(String... aliases); + /** * Map a resource (file or directory) on the classpath to a path inside the container. * This will only work if you are running your tests outside a Docker container. diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 09494c78610..6b055860256 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -75,6 +75,12 @@ public class GenericContainer> @NonNull private String networkMode; + @NonNull + private Network network; + + @NonNull + private List networkAliases = new ArrayList<>(); + @NonNull private Future image; @@ -412,7 +418,10 @@ private void applyConfiguration(CreateContainerCmd createCommand) { .toArray(String[]::new); createCommand.withExtraHosts(extraHostsArray); - if (networkMode != null) { + if (network != null) { + createCommand.withNetworkMode(network.getId()); + createCommand.withAliases(this.networkAliases); + } else if (networkMode != null) { createCommand.withNetworkMode(networkMode); } @@ -615,6 +624,18 @@ public SELF withNetworkMode(String networkMode) { return self(); } + @Override + public SELF withNetwork(Network network) { + this.network = network; + return self(); + } + + @Override + public SELF withNetworkAliases(String... aliases) { + Collections.addAll(this.networkAliases, aliases); + return self(); + } + /** * {@inheritDoc} */ diff --git a/core/src/main/java/org/testcontainers/containers/Network.java b/core/src/main/java/org/testcontainers/containers/Network.java new file mode 100644 index 00000000000..4d1da15cb69 --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/Network.java @@ -0,0 +1,78 @@ +package org.testcontainers.containers; + +import com.github.dockerjava.api.command.CreateNetworkCmd; +import lombok.Builder; +import lombok.Getter; +import lombok.Singular; +import org.junit.rules.ExternalResource; +import org.junit.rules.TestRule; +import org.testcontainers.DockerClientFactory; +import org.testcontainers.utility.ResourceReaper; + +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.UUID; +import java.util.function.Consumer; + +public interface Network extends AutoCloseable, TestRule { + + String getId(); + + @Override + default void close() { + ResourceReaper.instance().removeNetworks(getId()); + } + + static Network newNetwork() { + return builder().build(); + } + + static NetworkImpl.NetworkImplBuilder builder() { + return NetworkImpl.builder(); + } + + @Builder + @Getter + class NetworkImpl extends ExternalResource implements Network { + + private final String name = UUID.randomUUID().toString(); + + private Boolean enableIpv6; + + private String driver; + + @Singular + private Set> createNetworkCmdModifiers = new LinkedHashSet<>(); + + @Getter(lazy = true) + private final String id = create(); + + private String create() { + ResourceReaper.instance().registerNetworkForCleanup(name); + + CreateNetworkCmd createNetworkCmd = DockerClientFactory.instance().client().createNetworkCmd(); + + createNetworkCmd.withName(name); + createNetworkCmd.withCheckDuplicate(true); + + if (enableIpv6 != null) { + createNetworkCmd.withEnableIpv6(enableIpv6); + } + + if (driver != null) { + createNetworkCmd.withDriver(driver); + } + + for (Consumer consumer : createNetworkCmdModifiers) { + consumer.accept(createNetworkCmd); + } + + return createNetworkCmd.exec().getId(); + } + + @Override + protected void after() { + close(); + } + } +} diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index e9f10b76b2c..a9ed8f1ba45 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -5,15 +5,12 @@ import com.github.dockerjava.api.exception.DockerException; import com.github.dockerjava.api.exception.InternalServerErrorException; import com.github.dockerjava.api.exception.NotFoundException; -import com.github.dockerjava.api.model.Container; import com.github.dockerjava.api.model.Network; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testcontainers.DockerClientFactory; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** @@ -25,7 +22,7 @@ public final class ResourceReaper { private static ResourceReaper instance; private final DockerClient dockerClient; private Map registeredContainers = new ConcurrentHashMap<>(); - private List registeredNetworks = new ArrayList<>(); + private Set registeredNetworks = Collections.newSetFromMap(new ConcurrentHashMap<>()); private ResourceReaper() { dockerClient = DockerClientFactory.instance().client(); @@ -145,22 +142,34 @@ public void removeNetworks(String identifier) { } private void removeNetwork(String networkName) { - List networks; try { - networks = dockerClient.listNetworksCmd().withNameFilter(networkName).exec(); - } catch (DockerException e) { - LOGGER.trace("Error encountered when looking up network for removal (name: {}) - it may not have been removed", networkName); - return; - } + try { + // First try to remove by name + dockerClient.removeNetworkCmd(networkName).exec(); + } catch (Exception e) { + LOGGER.trace("Error encountered removing network by name ({}) - it may not have been removed", networkName); + } - for (Network network : networks) { + List networks; try { - dockerClient.removeNetworkCmd(network.getId()).exec(); - registeredNetworks.remove(network.getId()); - LOGGER.debug("Removed network: {}", networkName); - } catch (DockerException e) { - LOGGER.trace("Error encountered removing network (name: {}) - it may not have been removed", network.getName()); + // Then try to list all networks with the same name + networks = dockerClient.listNetworksCmd().withNameFilter(networkName).exec(); + } catch (Exception e) { + LOGGER.trace("Error encountered when looking up network for removal (name: {}) - it may not have been removed", networkName); + return; + } + + for (Network network : networks) { + try { + dockerClient.removeNetworkCmd(network.getId()).exec(); + registeredNetworks.remove(network.getId()); + LOGGER.debug("Removed network: {}", networkName); + } catch (Exception e) { + LOGGER.trace("Error encountered removing network (name: {}) - it may not have been removed", network.getName()); + } } + } finally { + registeredNetworks.remove(networkName); } } } diff --git a/core/src/test/java/org/testcontainers/containers/NetworkTest.java b/core/src/test/java/org/testcontainers/containers/NetworkTest.java new file mode 100644 index 00000000000..70b55c09928 --- /dev/null +++ b/core/src/test/java/org/testcontainers/containers/NetworkTest.java @@ -0,0 +1,94 @@ +package org.testcontainers.containers; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.runners.Enclosed; +import org.junit.runner.RunWith; +import org.testcontainers.DockerClientFactory; + +import static org.rnorth.visibleassertions.VisibleAssertions.*; +import static org.testcontainers.containers.Network.newNetwork; + +@RunWith(Enclosed.class) +public class NetworkTest { + + public static class WithRules { + + @Rule + public Network network = newNetwork(); + + @Rule + public GenericContainer foo = new GenericContainer() + .withNetwork(network) + .withNetworkAliases("foo") + .withCommand("/bin/sh", "-c", "while true ; do printf 'HTTP/1.1 200 OK\\n\\nyay' | nc -l -p 8080; done"); + + @Rule + public GenericContainer bar = new GenericContainer() + .withNetwork(network) + .withCommand("top"); + + @Test + public void testNetworkSupport() throws Exception { + String response = bar.execInContainer("wget", "-O", "-", "http://foo:8080").getStdout(); + assertEquals("received response", "yay", response); + } + } + + public static class WithoutRules { + + @Test + public void testNetworkSupport() throws Exception { + try ( + Network network = newNetwork(); + + GenericContainer foo = new GenericContainer() + .withNetwork(network) + .withNetworkAliases("foo") + .withCommand("/bin/sh", "-c", "while true ; do printf 'HTTP/1.1 200 OK\\n\\nyay' | nc -l -p 8080; done"); + + GenericContainer bar = new GenericContainer() + .withNetwork(network) + .withCommand("top") + ) { + foo.start(); + bar.start(); + + String response = bar.execInContainer("wget", "-O", "-", "http://foo:8080").getStdout(); + assertEquals("received response", "yay", response); + } + } + + @Test + public void testBuilder() throws Exception { + try ( + Network network = Network.builder() + .driver("macvlan") + .build(); + ) { + String id = network.getId(); + assertEquals( + "Flag is set", + "macvlan", + DockerClientFactory.instance().client().inspectNetworkCmd().withNetworkId(id).exec().getDriver() + ); + } + } + + @Test + public void testModifiers() throws Exception { + try ( + Network network = Network.builder() + .createNetworkCmdModifier(cmd -> cmd.withDriver("macvlan")) + .build(); + ) { + String id = network.getId(); + assertEquals( + "Flag is set", + "macvlan", + DockerClientFactory.instance().client().inspectNetworkCmd().withNetworkId(id).exec().getDriver() + ); + } + } + } +} diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/LinkedContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/LinkedContainerTest.java index b135e92a754..862a985ad8f 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/LinkedContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/LinkedContainerTest.java @@ -8,6 +8,7 @@ import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.remote.RemoteWebDriver; import org.testcontainers.containers.BrowserWebDriverContainer; +import org.testcontainers.containers.Network; import org.testcontainers.containers.NginxContainer; import java.io.*; @@ -23,13 +24,18 @@ public class LinkedContainerTest { private static File contentFolder = new File(System.getProperty("user.home") + "/.tmp-test-container"); @Rule - public NginxContainer nginx = new NginxContainer() + public Network network = Network.newNetwork(); + + @Rule + public NginxContainer nginx = new NginxContainer<>() + .withNetwork(network) + .withNetworkAliases("nginx") .withCustomContent(contentFolder.toString()); @Rule - public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() - .withDesiredCapabilities(DesiredCapabilities.chrome()) - .withLinkToContainer(nginx, "nginx"); + public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() + .withNetwork(network) + .withDesiredCapabilities(DesiredCapabilities.chrome()); @BeforeClass public static void setupContent() throws FileNotFoundException { From ed63472f02af7603525a0e0078d1dd5547b2af23 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 27 Jun 2017 21:51:37 +0200 Subject: [PATCH 0128/5615] Added `getFirstMappedPort` method (#377) * Added no-args `getMappedPort` overloaded method (will use first exposed port) --- CHANGELOG.md | 1 + .../org/testcontainers/containers/Container.java | 14 ++++++++++++++ .../junit/GenericContainerRuleTest.java | 2 +- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27aa66bb4f8..a7ab6ae7df8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. ### Changed - Added support for Docker networks (#372) +- Added `getFirstMappedPort` method ## [1.3.1] - 2017-06-22 ### Fixed diff --git a/core/src/main/java/org/testcontainers/containers/Container.java b/core/src/main/java/org/testcontainers/containers/Container.java index 2f2d9fa0917..e8ad7f0ba75 100644 --- a/core/src/main/java/org/testcontainers/containers/Container.java +++ b/core/src/main/java/org/testcontainers/containers/Container.java @@ -304,6 +304,20 @@ default SELF withClasspathResourceMapping(final String resourcePath, final Strin */ Boolean isRunning(); + /** + * Get the actual mapped port for a first port exposed by the container. + * + * @return the port that the exposed port is mapped to + * @throws IllegalStateException if there are no exposed ports + */ + default Integer getFirstMappedPort() { + return getExposedPorts() + .stream() + .findFirst() + .map(this::getMappedPort) + .orElseThrow(() -> new IllegalStateException("Container doesn't expose any ports")); + } + /** * Get the actual mapped port for a given port exposed by the container. * diff --git a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java index 45c1a78d10f..b71cef1f7b4 100644 --- a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java +++ b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java @@ -325,7 +325,7 @@ private BufferedReader getReaderForContainerPort80(GenericContainer container) { return Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, () -> { Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); - Socket socket = new Socket(container.getContainerIpAddress(), container.getMappedPort(80)); + Socket socket = new Socket(container.getContainerIpAddress(), container.getFirstMappedPort()); return new BufferedReader(new InputStreamReader(socket.getInputStream())); }); } From 1b89280ae95667611f783cb5a8c84d2237877778 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 27 Jun 2017 21:58:05 +0200 Subject: [PATCH 0129/5615] Fix number overflow in the disk space detection. Closes #379 (#380) * Fix number overflow in the disk space detection. Closes #379 --- CHANGELOG.md | 1 + .../testcontainers/DockerClientFactory.java | 18 +++++++++++------- .../DockerClientFactoryTest.java | 11 +++++++++++ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7ab6ae7df8..6cbf00b9f7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. ## UNRELEASED ### Fixed +- Fixed the case when disk's size is bigger than Integer's max value (#379, #380) ### Changed - Added support for Docker networks (#372) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 816c058dce6..bb40de7fa3b 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -8,6 +8,7 @@ import com.github.dockerjava.api.model.*; import com.github.dockerjava.core.command.ExecStartResultCallback; import com.github.dockerjava.core.command.PullImageResultCallback; +import com.google.common.annotations.VisibleForTesting; import lombok.Synchronized; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; @@ -235,21 +236,24 @@ private T runInsideDocker(DockerClient client, Consumer } } } - - private static class DiskSpaceUsage { - Optional availableMB = Optional.empty(); + + @VisibleForTesting + static class DiskSpaceUsage { + Optional availableMB = Optional.empty(); Optional usedPercent = Optional.empty(); } - - private DiskSpaceUsage parseAvailableDiskSpace(String dfOutput) { + + @VisibleForTesting + DiskSpaceUsage parseAvailableDiskSpace(String dfOutput) { DiskSpaceUsage df = new DiskSpaceUsage(); String[] lines = dfOutput.split("\n"); for (String line : lines) { String[] fields = line.split("\\s+"); if (fields.length > 5 && fields[5].equals("/")) { - int availableKB = Integer.valueOf(fields[3]); - df.availableMB = Optional.of(availableKB / 1024); + long availableKB = Long.valueOf(fields[3]); + df.availableMB = Optional.of(availableKB / 1024L); df.usedPercent = Optional.of(Integer.valueOf(fields[4].replace("%", ""))); + break; } } return df; diff --git a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java index bf67cc6885a..a6cb304d4e3 100644 --- a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java +++ b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java @@ -1,6 +1,8 @@ package org.testcontainers; import org.junit.Test; +import org.rnorth.visibleassertions.VisibleAssertions; +import org.testcontainers.DockerClientFactory.DiskSpaceUsage; import org.testcontainers.dockerclient.LogToStringContainerCallback; import org.testcontainers.utility.TestcontainersConfiguration; @@ -27,4 +29,13 @@ public void runCommandInsideDockerShouldNotFailIfImageDoesNotExistsLocally() { .toString() ); } + + @Test + public void shouldHandleBigDiskSize() throws Exception { + String dfOutput = "/dev/disk1 2982480572 1491240286 2982480572 31% /"; + DiskSpaceUsage usage = DockerClientFactory.instance().parseAvailableDiskSpace(dfOutput); + + VisibleAssertions.assertEquals("Available MB is correct", 2982480572L / 1024L, usage.availableMB.orElse(0L)); + VisibleAssertions.assertEquals("Available percentage is correct", 31, usage.usedPercent.orElse(0)); + } } \ No newline at end of file From 302fa39be8dbec2437dbe81db458792ef4dd0975 Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 28 Jun 2017 08:00:52 +0100 Subject: [PATCH 0130/5615] Fix erroneous version reference used during CI testing of shaded dependencies (#383) * Re-pin shade-test dependencies to use dev `0-SNAPSHOT` version of lib * Update changelog --- CHANGELOG.md | 1 + shade-test/clashing-deps-jackson/pom.xml | 2 +- shade-test/clashing-deps-jersey/pom.xml | 2 +- shade-test/pom.xml | 2 +- shade-test/service-lookup-dropwizard/pom.xml | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cbf00b9f7a..49f2296308c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file. ## UNRELEASED ### Fixed - Fixed the case when disk's size is bigger than Integer's max value (#379, #380) +- Fix erroneous version reference used during CI testing of shaded dependencies ### Changed - Added support for Docker networks (#372) diff --git a/shade-test/clashing-deps-jackson/pom.xml b/shade-test/clashing-deps-jackson/pom.xml index 50917f67d10..ae42ca1a51a 100644 --- a/shade-test/clashing-deps-jackson/pom.xml +++ b/shade-test/clashing-deps-jackson/pom.xml @@ -5,7 +5,7 @@ shade-test org.testcontainers - 1.2.2-SNAPSHOT + 0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/shade-test/clashing-deps-jersey/pom.xml b/shade-test/clashing-deps-jersey/pom.xml index 652ad2474e0..6ed4a65c49b 100644 --- a/shade-test/clashing-deps-jersey/pom.xml +++ b/shade-test/clashing-deps-jersey/pom.xml @@ -5,7 +5,7 @@ shade-test org.testcontainers - 1.2.2-SNAPSHOT + 0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/shade-test/pom.xml b/shade-test/pom.xml index 21a5dcaf2dd..41de7e9e7ef 100644 --- a/shade-test/pom.xml +++ b/shade-test/pom.xml @@ -5,7 +5,7 @@ testcontainers-parent org.testcontainers - 1.2.2-SNAPSHOT + 0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/shade-test/service-lookup-dropwizard/pom.xml b/shade-test/service-lookup-dropwizard/pom.xml index b7410583d8a..5455079d350 100644 --- a/shade-test/service-lookup-dropwizard/pom.xml +++ b/shade-test/service-lookup-dropwizard/pom.xml @@ -5,7 +5,7 @@ shade-test org.testcontainers - 1.2.2-SNAPSHOT + 0-SNAPSHOT ../pom.xml 4.0.0 From b98af3f7453d724d43fc1b632b7f43f51285eebf Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 28 Jun 2017 13:26:17 +0100 Subject: [PATCH 0131/5615] Add proper `test` scope to Vibur and Tomcat-JDBC dependencies used for testing (#382) * Add proper `test` scope to Vibur and Tomcat-JDBC dependencies used for testing * Update changelog * Update CHANGELOG.md --- CHANGELOG.md | 1 + modules/jdbc-test/pom.xml | 2 ++ modules/mysql/pom.xml | 2 ++ 3 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49f2296308c..222ccc204e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. ### Fixed - Fixed the case when disk's size is bigger than Integer's max value (#379, #380) - Fix erroneous version reference used during CI testing of shaded dependencies +- Fix leakage of Vibur and Tomcat JDBC test dependencies in `jdbc-test` and `mysql` modules (#382) ### Changed - Added support for Docker networks (#372) diff --git a/modules/jdbc-test/pom.xml b/modules/jdbc-test/pom.xml index a9542e70882..3a311119187 100644 --- a/modules/jdbc-test/pom.xml +++ b/modules/jdbc-test/pom.xml @@ -63,11 +63,13 @@ org.apache.tomcat tomcat-jdbc 8.5.4 + test org.vibur vibur-dbcp 9.0 + test diff --git a/modules/mysql/pom.xml b/modules/mysql/pom.xml index 5cb792f1b9d..b367bc48766 100644 --- a/modules/mysql/pom.xml +++ b/modules/mysql/pom.xml @@ -49,11 +49,13 @@ org.apache.tomcat tomcat-jdbc 8.5.4 + test org.vibur vibur-dbcp 9.0 + test \ No newline at end of file From 2ac72e03b6bbea027b646483ec52dc079f801e5f Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 28 Jun 2017 13:28:38 +0100 Subject: [PATCH 0132/5615] Add time limit and retry to instantiation of RemoteWebDriver (#381) * Add time limit and retry to instantiation of RemoteWebDriver Resolves #373 * Update changelog --- CHANGELOG.md | 1 + .../containers/BrowserWebDriverContainer.java | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 222ccc204e8..ce261c8e4df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. - Fixed the case when disk's size is bigger than Integer's max value (#379, #380) - Fix erroneous version reference used during CI testing of shaded dependencies - Fix leakage of Vibur and Tomcat JDBC test dependencies in `jdbc-test` and `mysql` modules (#382) +- Add timeout and retries for creation of `RemoteWebDriver` (#381, #373, #257) ### Changed - Added support for Docker networks (#372) diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index 3ea70d42566..7ae9b3787d9 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -6,6 +6,8 @@ import org.openqa.selenium.remote.BrowserType; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.remote.RemoteWebDriver; +import org.rnorth.ducttape.timeouts.Timeouts; +import org.rnorth.ducttape.unreliables.Unreliables; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testcontainers.containers.traits.LinkableContainer; @@ -20,6 +22,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Date; +import java.util.concurrent.TimeUnit; import static com.google.common.base.Preconditions.checkState; import static java.time.temporal.ChronoUnit.SECONDS; @@ -157,7 +160,11 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { recordingSidekickContainer.start(); currentVncRecordings.add(recordingSidekickContainer); } - this.driver = new RemoteWebDriver(getSeleniumAddress(), desiredCapabilities); + + driver = Unreliables.retryUntilSuccess(30, TimeUnit.SECONDS, + Timeouts.getWithTimeout(10, TimeUnit.SECONDS, + () -> + () -> new RemoteWebDriver(getSeleniumAddress(), desiredCapabilities))); } /** From 255a1e39ec22c0a3ab2a6fb9220b53f063b8d1ed Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sat, 1 Jul 2017 20:08:05 +0200 Subject: [PATCH 0133/5615] Speed up Travis (#386) * remove custom Docker installation (with the latest Travis release they have Docker 2017.03-ce) * merge alpine + shade tests (-1 allocated node. Travis limits it to 5) * remove "sudo" from deploy's stage (boot time dropped from 20-50s to 1-6s, see https://docs.travis-ci.com/user/ci-environment/ ) --- .travis.yml | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1c5c34814f8..d886f219b5f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,13 +23,6 @@ cache: install: - echo "MAVEN_OPTS='-XX:+TieredCompilation -XX:TieredStopAtLevel=1'" > ~/.mavenrc - - sudo apt-get update -qq - - sudo apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confnew install docker-ce - - docker info - # Update env vars required by testcontainers - - export DOCKER_HOST=tcp://127.0.0.1:2375 - - export DOCKER_TLS_VERIFY=0 - - env - ./mvnw -T4 -DskipTests=true -Dmaven.javadoc.skip=true install jobs: @@ -44,9 +37,10 @@ jobs: - env: [ NAME=modules ] script: ./mvnw -pl !core,!modules/selenium test - # Run Docker-in-Docker tests inside Alpine - - env: [ NAME=docker-in-alpine-docker ] - script: | + # Run Docker-in-Docker tests inside Alpine and shading tests + - env: [ NAME="docker-in-alpine-docker&shade" ] + script: + - | DOCKER_HOST=unix:///var/run/docker.sock DOCKER_TLS_VERIFY= docker run -t --rm \ -v "$HOME/.m2":/root/.m2/ \ -v /var/run/docker.sock:/var/run/docker.sock \ @@ -55,10 +49,11 @@ jobs: openjdk:8-jdk-alpine \ ./mvnw -pl core test -Dtest=*GenericContainerRuleTest - - env: [ NAME=shade ] - script: ./mvnw test -f shade-test/pom.xml + - ./mvnw test -f shade-test/pom.xml - stage: deploy + sudo: false + services: [] install: skip script: skip deploy: From 46b652e07c8dc5d522fbd45f8bff77937e1a9a7b Mon Sep 17 00:00:00 2001 From: Alexis Tual Date: Sat, 1 Jul 2017 21:32:52 +0200 Subject: [PATCH 0134/5615] Upgrading maven-shade-plugin to 3.0.0 (#388) --- core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/pom.xml b/core/pom.xml index 40caa201479..2d981bf050a 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -136,7 +136,7 @@ maven-shade-plugin - 2.4.3 + 3.0.0 package From 3b1bbebb47e953ef4660bec1f5e996580eb42604 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 2 Jul 2017 10:43:45 +0200 Subject: [PATCH 0135/5615] workaround listNetwork's filter until it's fixed in docker-java (#385) * workaround listNetwork's filter until it's fixed in docker-java (see https://github.com/docker-java/docker-java/issues/857 ) * Delete Docker Compose's networks by id instead of name --- CHANGELOG.md | 1 + .../containers/DockerComposeContainer.java | 2 +- .../testcontainers/containers/Network.java | 6 +- .../DockerClientProviderStrategy.java | 3 +- .../TestcontainersDockerCmdExecFactory.java | 55 +++++++++++++++++++ 5 files changed, 61 insertions(+), 6 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/dockerclient/TestcontainersDockerCmdExecFactory.java diff --git a/CHANGELOG.md b/CHANGELOG.md index ce261c8e4df..345bb0e52d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. - Fix erroneous version reference used during CI testing of shaded dependencies - Fix leakage of Vibur and Tomcat JDBC test dependencies in `jdbc-test` and `mysql` modules (#382) - Add timeout and retries for creation of `RemoteWebDriver` (#381, #373, #257) +- Fixed double encoding of listNetwork's filter until it's fixed in docker-java (#385) ### Changed - Added support for Docker networks (#372) diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 0cd71e75317..00d772b3126 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -176,7 +176,7 @@ private void registerContainersForShutdown() { // Compose can define their own networks as well; ensure these are cleaned up dockerClient.listNetworksCmd().exec().forEach(network -> { if (network.getName().contains(identifier)) { - ResourceReaper.instance().registerNetworkForCleanup(network.getName()); + ResourceReaper.instance().registerNetworkForCleanup(network.getId()); } }); diff --git a/core/src/main/java/org/testcontainers/containers/Network.java b/core/src/main/java/org/testcontainers/containers/Network.java index 4d1da15cb69..7b731e67367 100644 --- a/core/src/main/java/org/testcontainers/containers/Network.java +++ b/core/src/main/java/org/testcontainers/containers/Network.java @@ -48,8 +48,6 @@ class NetworkImpl extends ExternalResource implements Network { private final String id = create(); private String create() { - ResourceReaper.instance().registerNetworkForCleanup(name); - CreateNetworkCmd createNetworkCmd = DockerClientFactory.instance().client().createNetworkCmd(); createNetworkCmd.withName(name); @@ -67,7 +65,9 @@ private String create() { consumer.accept(createNetworkCmd); } - return createNetworkCmd.exec().getId(); + String id = createNetworkCmd.exec().getId(); + ResourceReaper.instance().registerNetworkForCleanup(id); + return id; } @Override diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index bc1cf2ea174..e932e509750 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -3,7 +3,6 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.core.DockerClientBuilder; import com.github.dockerjava.core.DockerClientConfig; -import com.github.dockerjava.netty.NettyDockerCmdExecFactory; import com.google.common.base.Throwables; import org.apache.commons.io.IOUtils; import org.jetbrains.annotations.Nullable; @@ -142,7 +141,7 @@ public DockerClient getClient() { protected DockerClient getClientForConfig(DockerClientConfig config) { return DockerClientBuilder .getInstance(config) - .withDockerCmdExecFactory(new NettyDockerCmdExecFactory()) + .withDockerCmdExecFactory(new TestcontainersDockerCmdExecFactory()) .build(); } diff --git a/core/src/main/java/org/testcontainers/dockerclient/TestcontainersDockerCmdExecFactory.java b/core/src/main/java/org/testcontainers/dockerclient/TestcontainersDockerCmdExecFactory.java new file mode 100644 index 00000000000..3ddc2e9dc08 --- /dev/null +++ b/core/src/main/java/org/testcontainers/dockerclient/TestcontainersDockerCmdExecFactory.java @@ -0,0 +1,55 @@ +package org.testcontainers.dockerclient; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.github.dockerjava.api.command.ListNetworksCmd; +import com.github.dockerjava.api.model.Network; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.core.util.FiltersEncoder; +import com.github.dockerjava.netty.MediaType; +import com.github.dockerjava.netty.NettyDockerCmdExecFactory; +import com.github.dockerjava.netty.WebTarget; +import com.github.dockerjava.netty.exec.AbstrSyncDockerCmdExec; +import lombok.SneakyThrows; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Field; +import java.util.List; + +class TestcontainersDockerCmdExecFactory extends NettyDockerCmdExecFactory { + + @Override + @SneakyThrows + public ListNetworksCmd.Exec createListNetworksCmdExec() { + Field baseResourceField = NettyDockerCmdExecFactory.class.getDeclaredField("baseResource"); + baseResourceField.setAccessible(true); + + // FIXME Workaround for https://github.com/docker-java/docker-java/issues/857 + return new ListNetworksCmdExec((WebTarget) baseResourceField.get(this), getDockerClientConfig()); + } + + private static class ListNetworksCmdExec extends AbstrSyncDockerCmdExec> implements ListNetworksCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(com.github.dockerjava.netty.exec.ListNetworksCmdExec.class); + + public ListNetworksCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected List execute(ListNetworksCmd command) { + WebTarget webTarget = getBaseResource().path("/networks"); + + if (command.getFilters() != null && !command.getFilters().isEmpty()) { + // Next line was changed (urlPathSegmentEscaper was removed) + webTarget = webTarget.queryParam("filters", FiltersEncoder.jsonEncode(command.getFilters())); + } + + LOGGER.trace("GET: {}", webTarget); + + return webTarget.request().accept(MediaType.APPLICATION_JSON).get(new TypeReference>() { + }); + } + } + +} \ No newline at end of file From b255c42e0237df34f8aca59511c8c82c4089a008 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 2 Jul 2017 12:20:13 +0100 Subject: [PATCH 0136/5615] Extract oracle into separate repo (#389) * Extract Oracle Container into a separate repository * Update changelog * Update changelog * Remove oracle-xe from proprietary-deps profile --- CHANGELOG.md | 7 +- modules/oracle-xe/pom.xml | 51 -------------- .../containers/OracleContainer.java | 70 ------------------- .../containers/OracleContainerProvider.java | 16 ----- ...s.containers.JdbcDatabaseContainerProvider | 1 - .../containers/OracleJDBCDriverTest.java | 48 ------------- .../junit/SimpleOracleTest.java | 39 ----------- .../src/test/resources/logback-test.xml | 29 -------- pom.xml | 1 - 9 files changed, 4 insertions(+), 258 deletions(-) delete mode 100644 modules/oracle-xe/pom.xml delete mode 100644 modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java delete mode 100644 modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainerProvider.java delete mode 100644 modules/oracle-xe/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider delete mode 100644 modules/oracle-xe/src/test/java/org/testcontainers/containers/OracleJDBCDriverTest.java delete mode 100644 modules/oracle-xe/src/test/java/org/testcontainers/junit/SimpleOracleTest.java delete mode 100644 modules/oracle-xe/src/test/resources/logback-test.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index 345bb0e52d1..d415feeb3b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,14 +4,15 @@ All notable changes to this project will be documented in this file. ## UNRELEASED ### Fixed - Fixed the case when disk's size is bigger than Integer's max value (#379, #380) -- Fix erroneous version reference used during CI testing of shaded dependencies -- Fix leakage of Vibur and Tomcat JDBC test dependencies in `jdbc-test` and `mysql` modules (#382) -- Add timeout and retries for creation of `RemoteWebDriver` (#381, #373, #257) +- Fixed erroneous version reference used during CI testing of shaded dependencies +- Fixed leakage of Vibur and Tomcat JDBC test dependencies in `jdbc-test` and `mysql` modules (#382) +- Added timeout and retries for creation of `RemoteWebDriver` (#381, #373, #257) - Fixed double encoding of listNetwork's filter until it's fixed in docker-java (#385) ### Changed - Added support for Docker networks (#372) - Added `getFirstMappedPort` method +- Extracted Oracle XE container into a separate repository ([testcontainers/testcontainers-java-module-oracle-xe](https://github.com/testcontainers/testcontainers-java-module-oracle-xe)) ## [1.3.1] - 2017-06-22 ### Fixed diff --git a/modules/oracle-xe/pom.xml b/modules/oracle-xe/pom.xml deleted file mode 100644 index f99ab6870b8..00000000000 --- a/modules/oracle-xe/pom.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - 4.0.0 - - oracle-xe - TestContainers :: JDBC :: Oracle - - - org.testcontainers - testcontainers-parent - 0-SNAPSHOT - ../../pom.xml - - - - - ${project.groupId} - testcontainers - ${project.version} - - - ${project.groupId} - jdbc - ${project.version} - - - - - com.zaxxer - HikariCP-java6 - 2.3.5 - test - - - commons-dbutils - commons-dbutils - 1.6 - test - - - - - com.oracle - ojdbc6 - 11.2.0.4 - provided - - - - - diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java deleted file mode 100644 index f0fe016a5e9..00000000000 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java +++ /dev/null @@ -1,70 +0,0 @@ -package org.testcontainers.containers; - -/** - * @author gusohal - */ -public class OracleContainer extends JdbcDatabaseContainer { - - public static final String NAME = "oracle"; - public static final String IMAGE = "wnameless/oracle-xe-11g"; - private static final int ORACLE_PORT = 1521; - private static final int APEX_HTTP_PORT = 8080; - - public OracleContainer() { - super(IMAGE + ":latest"); - } - - public OracleContainer(String dockerImageName) { - super(dockerImageName); - } - - @Override - protected Integer getLivenessCheckPort() { - return getMappedPort(ORACLE_PORT); - } - - @Override - protected void configure() { - - addExposedPorts(ORACLE_PORT, APEX_HTTP_PORT); - } - - @Override - public String getDriverClassName() { - return "oracle.jdbc.OracleDriver"; - } - - @Override - public String getJdbcUrl() { - return "jdbc:oracle:thin:" + getUsername() + "/" + getPassword() + "@//" + getContainerIpAddress() + ":" + getOraclePort() + "/" + getSid(); - } - - @Override - public String getUsername() { - return "system"; - } - - @Override - public String getPassword() { - return "oracle"; - } - - @SuppressWarnings("SameReturnValue") - public String getSid() { - return "xe"; - } - - public Integer getOraclePort() { - return getMappedPort(ORACLE_PORT); - } - - @SuppressWarnings("unused") - public Integer getWebPort() { - return getMappedPort(APEX_HTTP_PORT); - } - - @Override - public String getTestQueryString() { - return "SELECT 1 FROM DUAL"; - } -} \ No newline at end of file diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainerProvider.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainerProvider.java deleted file mode 100644 index 1aea9f7e443..00000000000 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainerProvider.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.testcontainers.containers; - -/** - * Factory for Oracle containers. - */ -public class OracleContainerProvider extends JdbcDatabaseContainerProvider { - @Override - public boolean supports(String databaseType) { - return databaseType.equals(OracleContainer.NAME); - } - - @Override - public JdbcDatabaseContainer newInstance(String tag) { - return new OracleContainer(OracleContainer.IMAGE + ":" + tag); - } -} diff --git a/modules/oracle-xe/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider b/modules/oracle-xe/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider deleted file mode 100644 index f031b7f955a..00000000000 --- a/modules/oracle-xe/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider +++ /dev/null @@ -1 +0,0 @@ -org.testcontainers.containers.OracleContainerProvider \ No newline at end of file diff --git a/modules/oracle-xe/src/test/java/org/testcontainers/containers/OracleJDBCDriverTest.java b/modules/oracle-xe/src/test/java/org/testcontainers/containers/OracleJDBCDriverTest.java deleted file mode 100644 index 61a8d9b8d4b..00000000000 --- a/modules/oracle-xe/src/test/java/org/testcontainers/containers/OracleJDBCDriverTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.testcontainers.containers; - -import com.zaxxer.hikari.HikariConfig; -import com.zaxxer.hikari.HikariDataSource; -import org.apache.commons.dbutils.QueryRunner; -import org.apache.commons.dbutils.ResultSetHandler; -import org.junit.Test; - -import java.sql.ResultSet; -import java.sql.SQLException; - -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; - -/** - * @author gusohal - */ -public class OracleJDBCDriverTest { - - @Test - public void testOracleWithNoSpecifiedVersion() throws SQLException { - performSimpleTest("jdbc:tc:oracle://hostname/databasename"); - } - - - private void performSimpleTest(String jdbcUrl) throws SQLException { - HikariDataSource dataSource = getDataSource(jdbcUrl, 1); - new QueryRunner(dataSource).query("SELECT 1 FROM dual", new ResultSetHandler() { - @Override - public Object handle(ResultSet rs) throws SQLException { - rs.next(); - int resultSetInt = rs.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); - return true; - } - }); - dataSource.close(); - } - - private HikariDataSource getDataSource(String jdbcUrl, int poolSize) { - HikariConfig hikariConfig = new HikariConfig(); - hikariConfig.setJdbcUrl(jdbcUrl); - hikariConfig.setConnectionTestQuery("SELECT 1 FROM dual"); - hikariConfig.setMinimumIdle(1); - hikariConfig.setMaximumPoolSize(poolSize); - - return new HikariDataSource(hikariConfig); - } -} diff --git a/modules/oracle-xe/src/test/java/org/testcontainers/junit/SimpleOracleTest.java b/modules/oracle-xe/src/test/java/org/testcontainers/junit/SimpleOracleTest.java deleted file mode 100644 index 5c47d3e2f92..00000000000 --- a/modules/oracle-xe/src/test/java/org/testcontainers/junit/SimpleOracleTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.testcontainers.junit; - -import com.zaxxer.hikari.HikariConfig; -import com.zaxxer.hikari.HikariDataSource; -import org.junit.Rule; -import org.junit.Test; -import org.testcontainers.containers.OracleContainer; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; - -/** - * @author gusohal - */ -public class SimpleOracleTest { - - @Rule - public OracleContainer oracle = new OracleContainer(); - - @Test - public void testSimple() throws SQLException { - HikariConfig hikariConfig = new HikariConfig(); - hikariConfig.setJdbcUrl(oracle.getJdbcUrl()); - hikariConfig.setUsername(oracle.getUsername()); - hikariConfig.setPassword(oracle.getPassword()); - - HikariDataSource ds = new HikariDataSource(hikariConfig); - Statement statement = ds.getConnection().createStatement(); - statement.execute("SELECT 1 FROM dual"); - ResultSet resultSet = statement.getResultSet(); - - resultSet.next(); - int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); - } -} diff --git a/modules/oracle-xe/src/test/resources/logback-test.xml b/modules/oracle-xe/src/test/resources/logback-test.xml deleted file mode 100644 index ed0e5b2659e..00000000000 --- a/modules/oracle-xe/src/test/resources/logback-test.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - %d{HH:mm:ss.SSS} %-5level %logger - %msg%n - - - - - - - - - - - - - - - - - - - PROFILER - DENY - - \ No newline at end of file diff --git a/pom.xml b/pom.xml index ba3ecd4c0ad..cac5baef31f 100644 --- a/pom.xml +++ b/pom.xml @@ -184,7 +184,6 @@ proprietary-deps - modules/oracle-xe modules/virtuoso From 1505fe8f8a3addfe4745b5a27aa5703461242311 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 6 Jul 2017 22:50:56 +0200 Subject: [PATCH 0137/5615] Add a ticket number to getFirstMappedPort's line --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d415feeb3b3..1ec598840be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ All notable changes to this project will be documented in this file. ### Changed - Added support for Docker networks (#372) -- Added `getFirstMappedPort` method +- Added `getFirstMappedPort` method (#377) - Extracted Oracle XE container into a separate repository ([testcontainers/testcontainers-java-module-oracle-xe](https://github.com/testcontainers/testcontainers-java-module-oracle-xe)) ## [1.3.1] - 2017-06-22 From a32e9804815a94b9db4279b3d31d043aaad4971b Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 6 Jul 2017 22:51:52 +0200 Subject: [PATCH 0138/5615] Add shading tests (#390) * Add shading tests * put back docker-java * fix test * do not shade jdbc module * fix missing space --- CHANGELOG.md | 2 + core/pom.xml | 12 +++ modules/jdbc/pom.xml | 71 ----------------- modules/mysql/pom.xml | 5 -- modules/postgresql/pom.xml | 5 -- modules/selenium/pom.xml | 54 ------------- .../containers/BrowserWebDriverContainer.java | 8 +- modules/virtuoso/pom.xml | 5 -- shade-test/clashing-deps-jackson/pom.xml | 5 ++ shade-test/clashing-deps-jersey/pom.xml | 5 ++ shade-test/jar-file/pom.xml | 24 ++++++ .../testcontainers/JarFileShadingTest.java | 79 +++++++++++++++++++ shade-test/pom.xml | 14 +--- shade-test/service-lookup-dropwizard/pom.xml | 5 ++ 14 files changed, 138 insertions(+), 156 deletions(-) create mode 100644 shade-test/jar-file/pom.xml create mode 100644 shade-test/jar-file/src/test/java/org/testcontainers/JarFileShadingTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ec598840be..329d58cd062 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,11 +8,13 @@ All notable changes to this project will be documented in this file. - Fixed leakage of Vibur and Tomcat JDBC test dependencies in `jdbc-test` and `mysql` modules (#382) - Added timeout and retries for creation of `RemoteWebDriver` (#381, #373, #257) - Fixed double encoding of listNetwork's filter until it's fixed in docker-java (#385) +- Fixed various shading issues ### Changed - Added support for Docker networks (#372) - Added `getFirstMappedPort` method (#377) - Extracted Oracle XE container into a separate repository ([testcontainers/testcontainers-java-module-oracle-xe](https://github.com/testcontainers/testcontainers-java-module-oracle-xe)) +- Added shading tests ## [1.3.1] - 2017-06-22 ### Fixed diff --git a/core/pom.xml b/core/pom.xml index 2d981bf050a..755e3fc0156 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -34,6 +34,10 @@ org.glassfish.jersey.connectors * + + log4j + log4j + @@ -228,9 +232,17 @@ *:* + META-INF/NOTICE + META-INF/LICENSE + META-INF/DEPENDENCIES + META-INF/maven/ META-INF/*.SF META-INF/*.DSA META-INF/*.RSA + META-INF/io.netty.versions.properties + mozilla/public-suffix-list.txt + + META-INF/services/README.md META-INF/services/com.fasterxml.jackson.core.* META-INF/services/com.github.dockerjava.api.command.* META-INF/services/javax.ws.rs.ext.* diff --git a/modules/jdbc/pom.xml b/modules/jdbc/pom.xml index cc06c27bf42..0a3178d8e01 100644 --- a/modules/jdbc/pom.xml +++ b/modules/jdbc/pom.xml @@ -26,75 +26,4 @@ - - - - maven-shade-plugin - 2.4.3 - - - package - - shade - - - - - - - - - - - com.google.common - org.testcontainers.shaded.com.google.common - - - org.glassfish - org.testcontainers.shaded.org.glassfish - - - javax.ws.rs - org.testcontainers.shaded.javax.ws.rs - - - com.fasterxml.jackson - org.testcontainers.shaded.com.fasterxml.jackson - - - - - org.bouncycastle:* - - META-INF/*.SF - META-INF/*.DSA - META-INF/*.RSA - - - - *:* - - META-INF/services/com.fasterxml.jackson.core.* - META-INF/services/com.github.dockerjava.api.command.* - META-INF/services/javax.ws.rs.ext.* - META-INF/services/org.glassfish.hk2.extension.* - META-INF/services/org.jvnet.hk2.external.generator.* - META-INF/services/org.glassfish.jersey.internal.spi.* - - - - - - com.google.guava:* - org.glassfish.*:* - - - true - false - - - - diff --git a/modules/mysql/pom.xml b/modules/mysql/pom.xml index b367bc48766..d9b75b57f24 100644 --- a/modules/mysql/pom.xml +++ b/modules/mysql/pom.xml @@ -13,11 +13,6 @@ TestContainers :: JDBC :: MySQL - - ${project.groupId} - testcontainers - ${project.version} - ${project.groupId} jdbc diff --git a/modules/postgresql/pom.xml b/modules/postgresql/pom.xml index 7ef8f9eb091..f5100801283 100644 --- a/modules/postgresql/pom.xml +++ b/modules/postgresql/pom.xml @@ -13,11 +13,6 @@ TestContainers :: JDBC :: PostgreSQL - - ${project.groupId} - testcontainers - ${project.version} - ${project.groupId} jdbc diff --git a/modules/selenium/pom.xml b/modules/selenium/pom.xml index d5995b0c0e0..af4e894365b 100644 --- a/modules/selenium/pom.xml +++ b/modules/selenium/pom.xml @@ -19,19 +19,6 @@ testcontainers ${project.version} - - com.github.docker-java - docker-java - ${docker-java.version} - provided - - - - com.google.guava - guava - 18.0 - provided - @@ -57,45 +44,4 @@ - - - - maven-shade-plugin - 2.4.3 - - - package - - shade - - - - - - - com.google.common - org.testcontainers.shaded.com.google.common - - - - - org.bouncycastle:* - - META-INF/*.SF - META-INF/*.DSA - META-INF/*.RSA - - - - - - com.google.guava:* - - - true - false - - - - \ No newline at end of file diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index 7ae9b3787d9..219eae9ec39 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -24,7 +24,6 @@ import java.util.Date; import java.util.concurrent.TimeUnit; -import static com.google.common.base.Preconditions.checkState; import static java.time.temporal.ChronoUnit.SECONDS; /** @@ -87,8 +86,11 @@ protected Integer getLivenessCheckPort() { @Override protected void configure() { - checkState(desiredCapabilities != null); - if (! customImageNameIsSet) { + if (desiredCapabilities == null) { + throw new IllegalStateException(); + } + + if (!customImageNameIsSet) { super.setDockerImageName(getImageForCapabilities(desiredCapabilities)); } diff --git a/modules/virtuoso/pom.xml b/modules/virtuoso/pom.xml index 31db0c52880..7884346e640 100644 --- a/modules/virtuoso/pom.xml +++ b/modules/virtuoso/pom.xml @@ -19,11 +19,6 @@ - - ${project.groupId} - testcontainers - ${project.version} - ${project.groupId} jdbc diff --git a/shade-test/clashing-deps-jackson/pom.xml b/shade-test/clashing-deps-jackson/pom.xml index ae42ca1a51a..16812788ff3 100644 --- a/shade-test/clashing-deps-jackson/pom.xml +++ b/shade-test/clashing-deps-jackson/pom.xml @@ -13,6 +13,11 @@ clashing-deps-jackson + + org.testcontainers + testcontainers + ${project.version} + redis.clients jedis diff --git a/shade-test/clashing-deps-jersey/pom.xml b/shade-test/clashing-deps-jersey/pom.xml index 6ed4a65c49b..639f8965ca4 100644 --- a/shade-test/clashing-deps-jersey/pom.xml +++ b/shade-test/clashing-deps-jersey/pom.xml @@ -13,6 +13,11 @@ clashing-deps-jersey + + org.testcontainers + testcontainers + ${project.version} + com.sun.jersey jersey-client diff --git a/shade-test/jar-file/pom.xml b/shade-test/jar-file/pom.xml new file mode 100644 index 00000000000..2f07b4f10f9 --- /dev/null +++ b/shade-test/jar-file/pom.xml @@ -0,0 +1,24 @@ + + + + shade-test + org.testcontainers + 0-SNAPSHOT + ../pom.xml + + 4.0.0 + + jar-file + + + + org.assertj + assertj-core + 3.6.2 + test + + + + \ No newline at end of file diff --git a/shade-test/jar-file/src/test/java/org/testcontainers/JarFileShadingTest.java b/shade-test/jar-file/src/test/java/org/testcontainers/JarFileShadingTest.java new file mode 100644 index 00000000000..42dc9a0e631 --- /dev/null +++ b/shade-test/jar-file/src/test/java/org/testcontainers/JarFileShadingTest.java @@ -0,0 +1,79 @@ +package org.testcontainers; + +import org.assertj.core.api.ListAssert; +import org.junit.*; + +import java.io.IOException; +import java.net.URI; +import java.nio.file.*; + +import static java.util.Collections.emptyMap; +import static org.assertj.core.api.Assertions.assertThat; + +public class JarFileShadingTest { + + private static FileSystem fileSystem; + + private static Path root; + + @BeforeClass + public static void setUp() throws Exception { + Path path = Paths.get("..", "..", "core", "target", "testcontainers-0-SNAPSHOT.jar"); + + fileSystem = FileSystems.newFileSystem(URI.create("jar:" + path.toUri()), emptyMap()); + + root = fileSystem.getRootDirectories().iterator().next(); + } + + @AfterClass + public static void tearDown() throws Exception { + fileSystem.close(); + } + + @Test + public void testPackages() throws Exception { + assertThatFileList(root).containsOnly( + "docker-java.properties", + "org/", + "META-INF/", + "com/" + ); + + assertThatFileList(root.resolve("org")).containsOnly( + "testcontainers/" + ); + + assertThatFileList(root.resolve("com")).containsOnly( + "github/" + ); + + assertThatFileList(root.resolve("com").resolve("github")).containsOnly( + "dockerjava/" + ); + } + + @Test + public void testMetaInf() throws Exception { + assertThatFileList(root.resolve("META-INF")).containsOnly( + "MANIFEST.MF", + "services/", + "native/" + ); + + assertThatFileList(root.resolve("META-INF").resolve("native")).containsOnly( + "liborg-testcontainers-shaded-netty-transport-native-epoll.so" + ); + } + + @Test + public void testMetaInfServices() throws Exception { + assertThatFileList(root.resolve("META-INF").resolve("services")) + .allMatch(it -> it.startsWith("org.testcontainers.")); + } + + private ListAssert assertThatFileList(Path path) throws IOException { + return (ListAssert) assertThat(Files.list(path)) + .extracting(Path::getFileName) + .extracting(Object::toString); + } +} diff --git a/shade-test/pom.xml b/shade-test/pom.xml index 41de7e9e7ef..bfbca79b8a1 100644 --- a/shade-test/pom.xml +++ b/shade-test/pom.xml @@ -17,18 +17,6 @@ clashing-deps-jackson clashing-deps-jersey service-lookup-dropwizard + jar-file - - - - org.testcontainers - testcontainers - ${project.version} - - - org.rnorth.visible-assertions - visible-assertions - 1.0.5 - - \ No newline at end of file diff --git a/shade-test/service-lookup-dropwizard/pom.xml b/shade-test/service-lookup-dropwizard/pom.xml index 5455079d350..8fdc7b27192 100644 --- a/shade-test/service-lookup-dropwizard/pom.xml +++ b/shade-test/service-lookup-dropwizard/pom.xml @@ -12,6 +12,11 @@ service-lookup-dropwizard + + org.testcontainers + testcontainers + ${project.version} + io.dropwizard dropwizard-core From 3cce55a417f6566fe8e791dc5caef2ee1d91920b Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 7 Jul 2017 11:17:48 +0200 Subject: [PATCH 0139/5615] Update docker-java to 3.0.12 (#393) * Update docker-java to 3.0.12. Removes custom Netty factory and tcp-to-unixsocket proxy. * changelog * simplify UnixSocketClientProviderStrategy's condition * shade kqueue native dependency --- CHANGELOG.md | 2 +- core/pom.xml | 13 +---- .../testcontainers/DockerClientFactory.java | 1 - .../containers/wait/HostPortWaitStrategy.java | 4 +- .../DockerClientProviderStrategy.java | 3 +- .../DockerMachineClientProviderStrategy.java | 5 +- ...dSystemPropertyClientProviderStrategy.java | 2 +- ...oxiedUnixSocketClientProviderStrategy.java | 50 ----------------- .../TestcontainersDockerCmdExecFactory.java | 55 ------------------- .../UnixSocketClientProviderStrategy.java | 11 +++- pom.xml | 2 +- .../testcontainers/JarFileShadingTest.java | 3 +- 12 files changed, 25 insertions(+), 126 deletions(-) delete mode 100644 core/src/main/java/org/testcontainers/dockerclient/ProxiedUnixSocketClientProviderStrategy.java delete mode 100644 core/src/main/java/org/testcontainers/dockerclient/TestcontainersDockerCmdExecFactory.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 329d58cd062..fbf1e14c2b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,6 @@ All notable changes to this project will be documented in this file. - Fixed erroneous version reference used during CI testing of shaded dependencies - Fixed leakage of Vibur and Tomcat JDBC test dependencies in `jdbc-test` and `mysql` modules (#382) - Added timeout and retries for creation of `RemoteWebDriver` (#381, #373, #257) -- Fixed double encoding of listNetwork's filter until it's fixed in docker-java (#385) - Fixed various shading issues ### Changed @@ -15,6 +14,7 @@ All notable changes to this project will be documented in this file. - Added `getFirstMappedPort` method (#377) - Extracted Oracle XE container into a separate repository ([testcontainers/testcontainers-java-module-oracle-xe](https://github.com/testcontainers/testcontainers-java-module-oracle-xe)) - Added shading tests +- Updated docker-java to 3.0.12 (#393) ## [1.3.1] - 2017-06-22 ### Fixed diff --git a/core/pom.xml b/core/pom.xml index 755e3fc0156..0238b2b2f64 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -21,11 +21,6 @@ ${docker-java.version} compile - - - de.gesellix - unix-socket-factory - org.glassfish.jersey.core * @@ -61,12 +56,6 @@ 2.0.1 - - org.rnorth - tcp-unix-socket-proxy - 1.0.1 - - org.zeroturnaround zt-exec @@ -269,6 +258,8 @@ dest="${project.build.directory}/exploded/"/> + diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index bb40de7fa3b..84995ef072d 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -50,7 +50,6 @@ public class DockerClientFactory { private static final List CONFIGURATION_STRATEGIES = asList(new EnvironmentAndSystemPropertyClientProviderStrategy(), new UnixSocketClientProviderStrategy(), - new ProxiedUnixSocketClientProviderStrategy(), new DockerMachineClientProviderStrategy(), new WindowsClientProviderStrategy()); private String activeApiVersion; diff --git a/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java index 3b3098a5372..5df83da47c6 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java @@ -6,7 +6,7 @@ import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.ContainerLaunchException; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.dockerclient.ProxiedUnixSocketClientProviderStrategy; +import org.testcontainers.dockerclient.DockerMachineClientProviderStrategy; import org.testcontainers.dockerclient.WindowsClientProviderStrategy; import java.net.Socket; @@ -93,7 +93,7 @@ protected void waitUntilReady() { private boolean shouldCheckWithCommand() { // Special case for Docker for Mac, see #160 - if(DockerClientFactory.instance().isUsing(ProxiedUnixSocketClientProviderStrategy.class) + if(!DockerClientFactory.instance().isUsing(DockerMachineClientProviderStrategy.class) && System.getProperty("os.name").toLowerCase().contains("mac")) { return true; } diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index e932e509750..bc1cf2ea174 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -3,6 +3,7 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.core.DockerClientBuilder; import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.NettyDockerCmdExecFactory; import com.google.common.base.Throwables; import org.apache.commons.io.IOUtils; import org.jetbrains.annotations.Nullable; @@ -141,7 +142,7 @@ public DockerClient getClient() { protected DockerClient getClientForConfig(DockerClientConfig config) { return DockerClientBuilder .getInstance(config) - .withDockerCmdExecFactory(new TestcontainersDockerCmdExecFactory()) + .withDockerCmdExecFactory(new NettyDockerCmdExecFactory()) .build(); } diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java index b06dc0fa697..175d215e376 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java @@ -16,6 +16,9 @@ */ @Slf4j public class DockerMachineClientProviderStrategy extends DockerClientProviderStrategy { + + public static final int PRIORITY = EnvironmentAndSystemPropertyClientProviderStrategy.PRIORITY - 10; + private static final String PING_TIMEOUT_DEFAULT = "30"; private static final String PING_TIMEOUT_PROPERTY_NAME = "testcontainers.dockermachineprovider.timeout"; @@ -26,7 +29,7 @@ protected boolean isApplicable() { @Override protected int getPriority() { - return ProxiedUnixSocketClientProviderStrategy.PRIORITY - 10; + return PRIORITY; } @Override diff --git a/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java index 195a07a4024..451da5b7152 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java @@ -12,7 +12,7 @@ @Slf4j public class EnvironmentAndSystemPropertyClientProviderStrategy extends DockerClientProviderStrategy { - public static final int PRIORITY = 100; + public static final int PRIORITY = UnixSocketClientProviderStrategy.PRIORITY - 10; private static final String PING_TIMEOUT_DEFAULT = "10"; private static final String PING_TIMEOUT_PROPERTY_NAME = "testcontainers.environmentprovider.timeout"; diff --git a/core/src/main/java/org/testcontainers/dockerclient/ProxiedUnixSocketClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/ProxiedUnixSocketClientProviderStrategy.java deleted file mode 100644 index ffd689fcb9e..00000000000 --- a/core/src/main/java/org/testcontainers/dockerclient/ProxiedUnixSocketClientProviderStrategy.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.testcontainers.dockerclient; - -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang.SystemUtils; -import org.rnorth.tcpunixsocketproxy.TcpToUnixSocketProxy; - -import java.io.File; - -@Slf4j -public class ProxiedUnixSocketClientProviderStrategy extends UnixSocketClientProviderStrategy { - - public static final int PRIORITY = EnvironmentAndSystemPropertyClientProviderStrategy.PRIORITY - 10; - - private final File socketFile = new File(DOCKER_SOCK_PATH); - - @Override - protected boolean isApplicable() { - return !SystemUtils.IS_OS_LINUX && socketFile.exists(); - } - - @Override - protected int getPriority() { - return PRIORITY; - } - - @Override - public void test() throws InvalidConfigurationException { - TcpToUnixSocketProxy proxy = new TcpToUnixSocketProxy(socketFile); - - try { - int proxyPort = proxy.start().getPort(); - - config = tryConfiguration("tcp://localhost:" + proxyPort); - - log.debug("Accessing unix domain socket via TCP proxy (" + DOCKER_SOCK_PATH + " via localhost:" + proxyPort + ")"); - } catch (Exception e) { - - proxy.stop(); - - throw new InvalidConfigurationException("ping failed", e); - } - - } - - @Override - public String getDescription() { - return "local Unix socket (via TCP proxy)"; - } - -} diff --git a/core/src/main/java/org/testcontainers/dockerclient/TestcontainersDockerCmdExecFactory.java b/core/src/main/java/org/testcontainers/dockerclient/TestcontainersDockerCmdExecFactory.java deleted file mode 100644 index 3ddc2e9dc08..00000000000 --- a/core/src/main/java/org/testcontainers/dockerclient/TestcontainersDockerCmdExecFactory.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.testcontainers.dockerclient; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.github.dockerjava.api.command.ListNetworksCmd; -import com.github.dockerjava.api.model.Network; -import com.github.dockerjava.core.DockerClientConfig; -import com.github.dockerjava.core.util.FiltersEncoder; -import com.github.dockerjava.netty.MediaType; -import com.github.dockerjava.netty.NettyDockerCmdExecFactory; -import com.github.dockerjava.netty.WebTarget; -import com.github.dockerjava.netty.exec.AbstrSyncDockerCmdExec; -import lombok.SneakyThrows; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.lang.reflect.Field; -import java.util.List; - -class TestcontainersDockerCmdExecFactory extends NettyDockerCmdExecFactory { - - @Override - @SneakyThrows - public ListNetworksCmd.Exec createListNetworksCmdExec() { - Field baseResourceField = NettyDockerCmdExecFactory.class.getDeclaredField("baseResource"); - baseResourceField.setAccessible(true); - - // FIXME Workaround for https://github.com/docker-java/docker-java/issues/857 - return new ListNetworksCmdExec((WebTarget) baseResourceField.get(this), getDockerClientConfig()); - } - - private static class ListNetworksCmdExec extends AbstrSyncDockerCmdExec> implements ListNetworksCmd.Exec { - - private static final Logger LOGGER = LoggerFactory.getLogger(com.github.dockerjava.netty.exec.ListNetworksCmdExec.class); - - public ListNetworksCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { - super(baseResource, dockerClientConfig); - } - - @Override - protected List execute(ListNetworksCmd command) { - WebTarget webTarget = getBaseResource().path("/networks"); - - if (command.getFilters() != null && !command.getFilters().isEmpty()) { - // Next line was changed (urlPathSegmentEscaper was removed) - webTarget = webTarget.queryParam("filters", FiltersEncoder.jsonEncode(command.getFilters())); - } - - LOGGER.trace("GET: {}", webTarget); - - return webTarget.request().accept(MediaType.APPLICATION_JSON).get(new TypeReference>() { - }); - } - } - -} \ No newline at end of file diff --git a/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java index 774f3184ef3..09d93c2610e 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java @@ -6,6 +6,7 @@ import org.apache.commons.lang.SystemUtils; import org.jetbrains.annotations.NotNull; +import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -13,6 +14,9 @@ @Slf4j public class UnixSocketClientProviderStrategy extends DockerClientProviderStrategy { + + public static final int PRIORITY = 100; + protected static final String DOCKER_SOCK_PATH = "/var/run/docker.sock"; private static final String SOCKET_LOCATION = "unix://" + DOCKER_SOCK_PATH; private static final int SOCKET_FILE_MODE_MASK = 0xc000; @@ -21,7 +25,12 @@ public class UnixSocketClientProviderStrategy extends DockerClientProviderStrate @Override protected boolean isApplicable() { - return SystemUtils.IS_OS_LINUX; + return SystemUtils.IS_OS_UNIX && new File(DOCKER_SOCK_PATH).exists(); + } + + @Override + protected int getPriority() { + return PRIORITY; } @Override diff --git a/pom.xml b/pom.xml index cac5baef31f..390827b249b 100644 --- a/pom.xml +++ b/pom.xml @@ -167,7 +167,7 @@ UTF-8 - 3.0.10 + 3.0.12 diff --git a/shade-test/jar-file/src/test/java/org/testcontainers/JarFileShadingTest.java b/shade-test/jar-file/src/test/java/org/testcontainers/JarFileShadingTest.java index 42dc9a0e631..37296d7ad6d 100644 --- a/shade-test/jar-file/src/test/java/org/testcontainers/JarFileShadingTest.java +++ b/shade-test/jar-file/src/test/java/org/testcontainers/JarFileShadingTest.java @@ -61,7 +61,8 @@ public void testMetaInf() throws Exception { ); assertThatFileList(root.resolve("META-INF").resolve("native")).containsOnly( - "liborg-testcontainers-shaded-netty-transport-native-epoll.so" + "liborg-testcontainers-shaded-netty-transport-native-epoll.so", + "liborg-testcontainers-shaded-netty-transport-native-kqueue.jnilib" ); } From 552937e2acd58f3412f7829261c27696dd1b86af Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 9 Jul 2017 20:46:29 +0100 Subject: [PATCH 0140/5615] Improve cleanup for docker-compose. (#394) * Improve cleanup for docker-compose. Reduce unnecessary cleanup attempts which cause errors to be logged. docker-compose down is now trusted to have cleanup up properly if it exits with a 0 status code. * Swap order of removal of containers vs networks if docker-compose down failed * Update changelog * Further improvements following code review * Reinstate semi-public methods and mark as deprecated Improve comments to aid clarity in removeNetwork method --- CHANGELOG.md | 1 + .../containers/DockerComposeContainer.java | 75 ++++++++++++------- .../testcontainers/containers/Network.java | 4 +- .../utility/ResourceReaper.java | 70 ++++++++++++----- 4 files changed, 104 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fbf1e14c2b6..cb19d7bce06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. - Fixed leakage of Vibur and Tomcat JDBC test dependencies in `jdbc-test` and `mysql` modules (#382) - Added timeout and retries for creation of `RemoteWebDriver` (#381, #373, #257) - Fixed various shading issues +- Improved removal of containers/networks when using Docker Compose, eliminating irrelevant errors during cleanup (#342, #394) ### Changed - Added support for Docker networks (#372) diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 00d772b3126..42f42c71398 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -8,6 +8,7 @@ import com.google.common.base.Splitter; import com.google.common.collect.Maps; import com.google.common.util.concurrent.Uninterruptibles; +import org.apache.commons.lang.StringUtils; import org.junit.runner.Description; import org.rnorth.ducttape.ratelimits.RateLimiter; import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; @@ -46,8 +47,9 @@ public class DockerComposeContainer> e private final String identifier; private final Map ambassadorContainers = new HashMap<>(); private final List composeFiles; - private Set spawnedContainerIds = Collections.emptySet(); - private Map scalingPreferences = new HashMap<>(); + private final Set spawnedContainerIds = new HashSet<>(); + private final Set spawnedNetworkIds = new HashSet<>(); + private final Map scalingPreferences = new HashMap<>(); private DockerClient dockerClient; private boolean localCompose; private boolean pull = true; @@ -116,15 +118,13 @@ public void starting(Description description) { } private void pullImages() { - getDockerCompose("pull") - .start(); + runWithCompose("pull"); } private void createServices() { - // Start the docker-compose container, which starts up the services - getDockerCompose("up -d") - .start(); + // Run the docker-compose container, which starts up the services + runWithCompose("up -d"); } private void tailChildContainerLogs() { @@ -137,16 +137,18 @@ private void tailChildContainerLogs() { ); } - private DockerCompose getDockerCompose(String cmd) { + private void runWithCompose(String cmd) { final DockerCompose dockerCompose; if (localCompose) { dockerCompose = new LocalDockerCompose(composeFiles, identifier); } else { dockerCompose = new ContainerisedDockerCompose(composeFiles, identifier); } - return dockerCompose + + dockerCompose .withCommand(cmd) - .withEnv(env); + .withEnv(env) + .invoke(); } private void applyScaling() { @@ -157,8 +159,7 @@ private void applyScaling() { sb.append(" ").append(scale.getKey()).append("=").append(scale.getValue()); } - getDockerCompose(sb.toString()) - .start(); + runWithCompose(sb.toString()); } } @@ -171,19 +172,18 @@ private void registerContainersForShutdown() { containers.forEach(container -> ResourceReaper.instance().registerContainerForCleanup(container.getId(), container.getNames()[0])); - // Ensure that the default network for this compose environment, if any, is also cleaned up - ResourceReaper.instance().registerNetworkForCleanup(identifier + "_default"); // Compose can define their own networks as well; ensure these are cleaned up dockerClient.listNetworksCmd().exec().forEach(network -> { if (network.getName().contains(identifier)) { - ResourceReaper.instance().registerNetworkForCleanup(network.getId()); + spawnedNetworkIds.add(network.getId()); + ResourceReaper.instance().registerNetworkIdForCleanup(network.getId()); } }); // remember the IDs to allow containers to be killed as soon as we reach stop() - spawnedContainerIds = containers.stream() + spawnedContainerIds.addAll(containers.stream() .map(Container::getId) - .collect(Collectors.toSet()); + .collect(Collectors.toSet())); } catch (DockerException e) { logger().debug("Failed to stop a service container with exception", e); @@ -240,15 +240,25 @@ public void finished(Description description) { ambassadorContainers.forEach((String address, AmbassadorContainer container) -> container.stop()); // Kill the services using docker-compose - getDockerCompose("down -v") - .start(); + try { + runWithCompose("down -v"); + + // If we reach here then docker-compose down has cleared networks and containers; + // we can unregister from ResourceReaper + spawnedContainerIds.forEach(ResourceReaper.instance()::unregisterContainer); + spawnedNetworkIds.forEach(ResourceReaper.instance()::unregisterNetwork); + } catch (Exception e) { + // docker-compose down failed; use ResourceReaper to ensure cleanup - // remove the networks before removing the containers - ResourceReaper.instance().removeNetworks(identifier); + // kill the spawned service containers + spawnedContainerIds.forEach(ResourceReaper.instance()::stopAndRemoveContainer); + + // remove the networks after removing the containers + spawnedNetworkIds.forEach(ResourceReaper.instance()::removeNetworkById); + } - // kill the spawned service containers - spawnedContainerIds.forEach(id -> ResourceReaper.instance().stopAndRemoveContainer(id)); spawnedContainerIds.clear(); + spawnedNetworkIds.clear(); } } @@ -372,7 +382,7 @@ interface DockerCompose { DockerCompose withEnv(Map env); - void start(); + void invoke(); default void validateFileList(List composeFiles) { checkNotNull(composeFiles); @@ -417,7 +427,7 @@ public ContainerisedDockerCompose(List composeFiles, String identifier) { } @Override - public void start() { + public void invoke() { super.start(); this.followOutput(new Slf4jLogConsumer(logger())); @@ -431,6 +441,19 @@ public void start() { logger().info("Docker Compose has finished running"); AuditLogger.doComposeLog(this.getCommandParts(), this.getEnv()); + + final Integer exitCode = this.dockerClient.inspectContainerCmd(containerId) + .exec() + .getState() + .getExitCode(); + + if (exitCode == null || exitCode != 0) { + throw new ContainerLaunchException( + "Containerised Docker Compose exited abnormally with code " + + exitCode + + " whilst running command: " + + StringUtils.join(this.getCommandParts(), ' ')); + } } } @@ -468,7 +491,7 @@ public DockerCompose withEnv(Map env) { } @Override - public void start() { + public void invoke() { // bail out early if (!CommandLine.executableExists(COMPOSE_EXECUTABLE)) { throw new ContainerLaunchException("Local Docker Compose not found. Is " + COMPOSE_EXECUTABLE + " on the PATH?"); diff --git a/core/src/main/java/org/testcontainers/containers/Network.java b/core/src/main/java/org/testcontainers/containers/Network.java index 7b731e67367..36d15271e6c 100644 --- a/core/src/main/java/org/testcontainers/containers/Network.java +++ b/core/src/main/java/org/testcontainers/containers/Network.java @@ -20,7 +20,7 @@ public interface Network extends AutoCloseable, TestRule { @Override default void close() { - ResourceReaper.instance().removeNetworks(getId()); + ResourceReaper.instance().removeNetworkById(getId()); } static Network newNetwork() { @@ -66,7 +66,7 @@ private String create() { } String id = createNetworkCmd.exec().getId(); - ResourceReaper.instance().registerNetworkForCleanup(id); + ResourceReaper.instance().registerNetworkIdForCleanup(id); return id; } diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index a9ed8f1ba45..bac8772f842 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -10,7 +10,10 @@ import org.slf4j.LoggerFactory; import org.testcontainers.DockerClientFactory; -import java.util.*; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** @@ -127,49 +130,80 @@ private void stopContainer(String containerId, String imageName) { /** * Register a network to be cleaned up at JVM shutdown. * - * @param networkName the image name of the network + * @param id the ID of the network */ + public void registerNetworkIdForCleanup(String id) { + registeredNetworks.add(id); + } + + /** + * @param networkName the name of the network + * @deprecated see {@link ResourceReaper#registerNetworkIdForCleanup(String)} + */ + @Deprecated public void registerNetworkForCleanup(String networkName) { - registeredNetworks.add(networkName); + try { + // Try to find the network by name, so that we can register its ID for later deletion + dockerClient.listNetworksCmd() + .withNameFilter(networkName) + .exec() + .forEach(network -> registerNetworkIdForCleanup(network.getId())); + } catch (Exception e) { + LOGGER.trace("Error encountered when looking up network (name: {})", networkName); + } + } + + /** + * Removes a network by ID. + * @param id + */ + public void removeNetworkById(String id) { + removeNetwork(id); } /** - * Removes any networks that contain the identifier. + * Removes a network by ID. * @param identifier + * @deprecated see {@link ResourceReaper#removeNetworkById(String)} */ + @Deprecated public void removeNetworks(String identifier) { - removeNetwork(identifier); + removeNetworkById(identifier); } - private void removeNetwork(String networkName) { + private void removeNetwork(String id) { try { - try { - // First try to remove by name - dockerClient.removeNetworkCmd(networkName).exec(); - } catch (Exception e) { - LOGGER.trace("Error encountered removing network by name ({}) - it may not have been removed", networkName); - } - List networks; try { - // Then try to list all networks with the same name - networks = dockerClient.listNetworksCmd().withNameFilter(networkName).exec(); + // Try to find the network if it still exists + // Listing by ID first prevents docker-java logging an error if we just go blindly into removeNetworkCmd + networks = dockerClient.listNetworksCmd().withIdFilter(id).exec(); } catch (Exception e) { - LOGGER.trace("Error encountered when looking up network for removal (name: {}) - it may not have been removed", networkName); + LOGGER.trace("Error encountered when looking up network for removal (name: {}) - it may not have been removed", id); return; } + // at this point networks should contain either 0 or 1 entries, depending on whether the network exists + // using a for loop we essentially treat the network like an optional, only applying the removal if it exists for (Network network : networks) { try { dockerClient.removeNetworkCmd(network.getId()).exec(); registeredNetworks.remove(network.getId()); - LOGGER.debug("Removed network: {}", networkName); + LOGGER.debug("Removed network: {}", id); } catch (Exception e) { LOGGER.trace("Error encountered removing network (name: {}) - it may not have been removed", network.getName()); } } } finally { - registeredNetworks.remove(networkName); + registeredNetworks.remove(id); } } + + public void unregisterNetwork(String identifier) { + registeredNetworks.remove(identifier); + } + + public void unregisterContainer(String identifier) { + registeredContainers.remove(identifier); + } } From 8707770a73de7f181b87c2d8065d00a09c1492ff Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 9 Jul 2017 22:01:59 +0200 Subject: [PATCH 0141/5615] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb19d7bce06..f9ff3226975 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ All notable changes to this project will be documented in this file. ## UNRELEASED ### Fixed + +### Changed + +## [1.4.0] - 2017-07-09 +### Fixed - Fixed the case when disk's size is bigger than Integer's max value (#379, #380) - Fixed erroneous version reference used during CI testing of shaded dependencies - Fixed leakage of Vibur and Tomcat JDBC test dependencies in `jdbc-test` and `mysql` modules (#382) From 22339fcab3729440589e9f2e76033a764bfc9a6b Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 10 Jul 2017 13:03:30 +0200 Subject: [PATCH 0142/5615] Fix Guava shading in jdbc module (#399) * Fix Guava shading in jdbc module * fix shading tests --- CHANGELOG.md | 1 + core/pom.xml | 12 +++++++++++ modules/jdbc/pom.xml | 47 +++++++++++++++++++++++++++++++++++++++++++- pom.xml | 12 ----------- shade-test/pom.xml | 9 +++++++++ 5 files changed, 68 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9ff3226975..a794aa8a747 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. ## UNRELEASED ### Fixed +- Fixed Guava shading in `jdbc` module ### Changed diff --git a/core/pom.xml b/core/pom.xml index 0238b2b2f64..a1e128c8466 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -15,6 +15,18 @@ TestContainers Core + + junit + junit + 4.12 + + + + org.slf4j + slf4j-api + 1.7.25 + + com.github.docker-java docker-java diff --git a/modules/jdbc/pom.xml b/modules/jdbc/pom.xml index 0a3178d8e01..7027cac5356 100644 --- a/modules/jdbc/pom.xml +++ b/modules/jdbc/pom.xml @@ -22,8 +22,53 @@ com.google.guava guava 18.0 - provided + + + + maven-shade-plugin + 3.0.0 + + + package + + shade + + + + + + + com.google + org.testcontainers.shaded.com.google + + + + + com.google.guava:* + + + false + false + + + *:* + + META-INF/NOTICE + META-INF/LICENSE + META-INF/DEPENDENCIES + META-INF/maven/ + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + + diff --git a/pom.xml b/pom.xml index 390827b249b..bf158718386 100644 --- a/pom.xml +++ b/pom.xml @@ -69,12 +69,6 @@ - - junit - junit - 4.12 - - org.projectlombok @@ -83,12 +77,6 @@ provided - - - org.slf4j - slf4j-api - 1.7.25 - ch.qos.logback logback-classic diff --git a/shade-test/pom.xml b/shade-test/pom.xml index bfbca79b8a1..0e761a940e3 100644 --- a/shade-test/pom.xml +++ b/shade-test/pom.xml @@ -19,4 +19,13 @@ service-lookup-dropwizard jar-file + + + + junit + junit + 4.12 + test + + \ No newline at end of file From 20755cfca899c360ddc192b039f324bc2066d2cb Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 10 Jul 2017 14:08:30 +0200 Subject: [PATCH 0143/5615] Update CHANGELOG.md --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a794aa8a747..8e911b03709 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,10 +3,13 @@ All notable changes to this project will be documented in this file. ## UNRELEASED ### Fixed -- Fixed Guava shading in `jdbc` module ### Changed +## [1.4.1] - 2017-07-10 +### Fixed +- Fixed Guava shading in `jdbc` module + ## [1.4.0] - 2017-07-09 ### Fixed - Fixed the case when disk's size is bigger than Integer's max value (#379, #380) From 464138368a0facab93ad85afd7cf179293355bf9 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 18 Jul 2017 10:21:08 +0200 Subject: [PATCH 0144/5615] Remove Guava from JDBC module (#401) * Remove Guava from JDBC module --- CHANGELOG.md | 1 + modules/jdbc/pom.xml | 51 ------------------- .../jdbc/ContainerDatabaseDriver.java | 18 ++++--- .../testcontainers/jdbc/ext/ScriptUtils.java | 21 +++++--- 4 files changed, 25 insertions(+), 66 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e911b03709..5d7e29d0ff0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. ### Fixed ### Changed +- Removed Guava usage from `jdbc` module (#401) ## [1.4.1] - 2017-07-10 ### Fixed diff --git a/modules/jdbc/pom.xml b/modules/jdbc/pom.xml index 7027cac5356..0fe42c3f4f0 100644 --- a/modules/jdbc/pom.xml +++ b/modules/jdbc/pom.xml @@ -18,57 +18,6 @@ testcontainers ${project.version} - - com.google.guava - guava - 18.0 - - - - - maven-shade-plugin - 3.0.0 - - - package - - shade - - - - - - - com.google - org.testcontainers.shaded.com.google - - - - - com.google.guava:* - - - false - false - - - *:* - - META-INF/NOTICE - META-INF/LICENSE - META-INF/DEPENDENCIES - META-INF/maven/ - META-INF/*.SF - META-INF/*.DSA - META-INF/*.RSA - - - - - - - - diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java index 55b9e83184e..d965fb83d03 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java @@ -1,8 +1,6 @@ package org.testcontainers.jdbc; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Charsets; -import com.google.common.io.Resources; +import org.apache.commons.io.IOUtils; import org.slf4j.LoggerFactory; import org.testcontainers.containers.JdbcDatabaseContainer; import org.testcontainers.containers.JdbcDatabaseContainerProvider; @@ -13,6 +11,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.sql.*; import java.util.*; import java.util.logging.Logger; @@ -223,10 +222,16 @@ private void runInitScriptIfRequired(String url, Connection connection) throws S if (matcher.matches()) { String initScriptPath = matcher.group(2); try { - URL resource = Resources.getResource(initScriptPath); - String sql = Resources.toString(resource, Charsets.UTF_8); + URL resource = Thread.currentThread().getContextClassLoader().getResource(initScriptPath); + + if (resource == null) { + LOGGER.warn("Could not load classpath init script: {}", initScriptPath); + throw new SQLException("Could not load classpath init script: " + initScriptPath + ". Resource not found."); + } + + String sql = IOUtils.toString(resource, StandardCharsets.UTF_8); ScriptUtils.executeSqlScript(connection, initScriptPath, sql); - } catch (IOException | IllegalArgumentException e) { + } catch (IOException e) { LOGGER.warn("Could not load classpath init script: {}", initScriptPath); throw new SQLException("Could not load classpath init script: " + initScriptPath, e); } catch (ScriptException e) { @@ -324,7 +329,6 @@ public static void killContainer(String jdbcUrl) { * @param jdbcUrl the JDBC URL of the container instance to get * @return an instance of database container or null if no container associated with JDBC URL */ - @VisibleForTesting static JdbcDatabaseContainer getContainer(String jdbcUrl) { synchronized (jdbcUrlContainerCache) { return jdbcUrlContainerCache.get(jdbcUrl); diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ext/ScriptUtils.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ext/ScriptUtils.java index 6533deb0008..1d7a12fed7a 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ext/ScriptUtils.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ext/ScriptUtils.java @@ -16,8 +16,7 @@ package org.testcontainers.jdbc.ext; -import com.google.common.base.Preconditions; -import com.google.common.base.Strings; +import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -110,11 +109,11 @@ private ScriptUtils() { public static void splitSqlScript(String resource, String script, String separator, String commentPrefix, String blockCommentStartDelimiter, String blockCommentEndDelimiter, List statements) { - Preconditions.checkArgument(!Strings.isNullOrEmpty(script), "script must not be null or empty"); - Preconditions.checkArgument(separator != null, "separator must not be null"); - Preconditions.checkArgument(!Strings.isNullOrEmpty(commentPrefix), "commentPrefix must not be null or empty"); - Preconditions.checkArgument(!Strings.isNullOrEmpty(blockCommentStartDelimiter), "blockCommentStartDelimiter must not be null or empty"); - Preconditions.checkArgument(!Strings.isNullOrEmpty(blockCommentEndDelimiter), "blockCommentEndDelimiter must not be null or empty"); + checkArgument(StringUtils.isNotEmpty(script), "script must not be null or empty"); + checkArgument(separator != null, "separator must not be null"); + checkArgument(StringUtils.isNotEmpty(commentPrefix), "commentPrefix must not be null or empty"); + checkArgument(StringUtils.isNotEmpty(blockCommentStartDelimiter), "blockCommentStartDelimiter must not be null or empty"); + checkArgument(StringUtils.isNotEmpty(blockCommentEndDelimiter), "blockCommentEndDelimiter must not be null or empty"); StringBuilder sb = new StringBuilder(); boolean inLiteral = false; @@ -183,11 +182,17 @@ else if (c == ' ' || c == '\n' || c == '\t') { } sb.append(c); } - if (!Strings.isNullOrEmpty(sb.toString())) { + if (StringUtils.isNotEmpty(sb.toString())) { statements.add(sb.toString()); } } + private static void checkArgument(boolean expression, String errorMessage) { + if (!expression) { + throw new IllegalArgumentException(errorMessage); + } + } + /** * Does the provided SQL script contain the specified delimiter? * @param script the SQL script From d7710b9fd83091b685eb9de694a76baca24dcdad Mon Sep 17 00:00:00 2001 From: kosmaty Date: Tue, 18 Jul 2017 10:22:19 +0200 Subject: [PATCH 0145/5615] fix #406 - script path is displayed in log message (#407) --- .../src/main/java/org/testcontainers/jdbc/ext/ScriptUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ext/ScriptUtils.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ext/ScriptUtils.java index 1d7a12fed7a..286ea324f42 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ext/ScriptUtils.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ext/ScriptUtils.java @@ -246,7 +246,7 @@ public static void executeSqlScript(Connection connection, String scriptPath, St try { if (LOGGER.isInfoEnabled()) { - LOGGER.info("Executing SQL script from " + script); + LOGGER.info("Executing SQL script from " + scriptPath); } long startTime = System.currentTimeMillis(); From 51d1d45def544026728c53aaf56c2fe2818dcf27 Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 24 Jul 2017 21:49:10 +0100 Subject: [PATCH 0146/5615] Use unix socket proxy on OS X instead of netty KQueue route (#410) Adjust docker client provider strategies so that a proxied (TCP) connection is used to connect to the Docker daemon on < OS X 10.12. Fixes #402. --- CHANGELOG.md | 1 + core/pom.xml | 6 +++ .../testcontainers/DockerClientFactory.java | 1 + .../containers/wait/HostPortWaitStrategy.java | 5 +- .../DockerClientProviderStrategy.java | 10 +++- .../DockerMachineClientProviderStrategy.java | 5 +- ...dSystemPropertyClientProviderStrategy.java | 2 +- ...oxiedUnixSocketClientProviderStrategy.java | 54 +++++++++++++++++++ .../UnixSocketClientProviderStrategy.java | 19 +++---- .../utility/ComparableVersion.java | 10 ++++ .../testcontainers/JarFileShadingTest.java | 4 +- 11 files changed, 98 insertions(+), 19 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/dockerclient/ProxiedUnixSocketClientProviderStrategy.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d7e29d0ff0..7f859fd803b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. ## UNRELEASED ### Fixed +- Worked around incompatibility between Netty's Unix socket support and OS X 10.11. Reinstated use of TCP-Unix Socket proxy when running on OS X prior to v10.12. (Fixes #402) ### Changed - Removed Guava usage from `jdbc` module (#401) diff --git a/core/pom.xml b/core/pom.xml index a1e128c8466..4a93ed35ba7 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -68,6 +68,12 @@ 2.0.1 + + org.rnorth + tcp-unix-socket-proxy + 1.0.1 + + org.zeroturnaround zt-exec diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 84995ef072d..bb40de7fa3b 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -50,6 +50,7 @@ public class DockerClientFactory { private static final List CONFIGURATION_STRATEGIES = asList(new EnvironmentAndSystemPropertyClientProviderStrategy(), new UnixSocketClientProviderStrategy(), + new ProxiedUnixSocketClientProviderStrategy(), new DockerMachineClientProviderStrategy(), new WindowsClientProviderStrategy()); private String activeApiVersion; diff --git a/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java index 5df83da47c6..babe0e8d592 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java @@ -1,6 +1,7 @@ package org.testcontainers.containers.wait; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.SystemUtils; import org.rnorth.ducttape.TimeoutException; import org.rnorth.ducttape.unreliables.Unreliables; import org.testcontainers.DockerClientFactory; @@ -93,8 +94,8 @@ protected void waitUntilReady() { private boolean shouldCheckWithCommand() { // Special case for Docker for Mac, see #160 - if(!DockerClientFactory.instance().isUsing(DockerMachineClientProviderStrategy.class) - && System.getProperty("os.name").toLowerCase().contains("mac")) { + if (! DockerClientFactory.instance().isUsing(DockerMachineClientProviderStrategy.class) && + SystemUtils.IS_OS_MAC_OSX) { return true; } diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index bc1cf2ea174..94a5a0e81ad 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -75,8 +75,14 @@ public static DockerClientProviderStrategy getFirstValidStrategy(List strategyClass = (Class) Thread.currentThread().getContextClassLoader().loadClass(it); return Stream.of(strategyClass.newInstance()); - } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { - LOGGER.warn("Can't instantiate a strategy from " + it, e); + } catch (ClassNotFoundException e) { + LOGGER.warn("Can't instantiate a strategy from {} (ClassNotFoundException). " + + "This probably means that cached configuration refers to a client provider " + + "class that is not available in this version of Testcontainers. Other " + + "strategies will be tried instead.", it); + return Stream.empty(); + } catch (InstantiationException | IllegalAccessException e) { + LOGGER.warn("Can't instantiate a strategy from {}", it, e); return Stream.empty(); } }), diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java index 175d215e376..b06dc0fa697 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java @@ -16,9 +16,6 @@ */ @Slf4j public class DockerMachineClientProviderStrategy extends DockerClientProviderStrategy { - - public static final int PRIORITY = EnvironmentAndSystemPropertyClientProviderStrategy.PRIORITY - 10; - private static final String PING_TIMEOUT_DEFAULT = "30"; private static final String PING_TIMEOUT_PROPERTY_NAME = "testcontainers.dockermachineprovider.timeout"; @@ -29,7 +26,7 @@ protected boolean isApplicable() { @Override protected int getPriority() { - return PRIORITY; + return ProxiedUnixSocketClientProviderStrategy.PRIORITY - 10; } @Override diff --git a/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java index 451da5b7152..195a07a4024 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java @@ -12,7 +12,7 @@ @Slf4j public class EnvironmentAndSystemPropertyClientProviderStrategy extends DockerClientProviderStrategy { - public static final int PRIORITY = UnixSocketClientProviderStrategy.PRIORITY - 10; + public static final int PRIORITY = 100; private static final String PING_TIMEOUT_DEFAULT = "10"; private static final String PING_TIMEOUT_PROPERTY_NAME = "testcontainers.environmentprovider.timeout"; diff --git a/core/src/main/java/org/testcontainers/dockerclient/ProxiedUnixSocketClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/ProxiedUnixSocketClientProviderStrategy.java new file mode 100644 index 00000000000..63785f0c86d --- /dev/null +++ b/core/src/main/java/org/testcontainers/dockerclient/ProxiedUnixSocketClientProviderStrategy.java @@ -0,0 +1,54 @@ +package org.testcontainers.dockerclient; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.SystemUtils; +import org.rnorth.tcpunixsocketproxy.TcpToUnixSocketProxy; +import org.testcontainers.utility.ComparableVersion; + +import java.io.File; + +@Slf4j +public class ProxiedUnixSocketClientProviderStrategy extends UnixSocketClientProviderStrategy { + + public static final int PRIORITY = EnvironmentAndSystemPropertyClientProviderStrategy.PRIORITY - 10; + + private final File socketFile = new File(DOCKER_SOCK_PATH); + + @Override + protected boolean isApplicable() { + final boolean nettyDoesNotSupportMacUnixSockets = SystemUtils.IS_OS_MAC_OSX && + ComparableVersion.OS_VERSION.isLessThan("10.12"); + + return nettyDoesNotSupportMacUnixSockets && socketFile.exists(); + } + + @Override + protected int getPriority() { + return PRIORITY; + } + + @Override + public void test() throws InvalidConfigurationException { + TcpToUnixSocketProxy proxy = new TcpToUnixSocketProxy(socketFile); + + try { + int proxyPort = proxy.start().getPort(); + + config = tryConfiguration("tcp://localhost:" + proxyPort); + + log.debug("Accessing unix domain socket via TCP proxy (" + DOCKER_SOCK_PATH + " via localhost:" + proxyPort + ")"); + } catch (Exception e) { + + proxy.stop(); + + throw new InvalidConfigurationException("ping failed", e); + } + + } + + @Override + public String getDescription() { + return "local Unix socket (via TCP proxy)"; + } + +} diff --git a/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java index 09d93c2610e..ea192f9b557 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java @@ -5,8 +5,8 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.SystemUtils; import org.jetbrains.annotations.NotNull; +import org.testcontainers.utility.ComparableVersion; -import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -14,23 +14,20 @@ @Slf4j public class UnixSocketClientProviderStrategy extends DockerClientProviderStrategy { - - public static final int PRIORITY = 100; - protected static final String DOCKER_SOCK_PATH = "/var/run/docker.sock"; private static final String SOCKET_LOCATION = "unix://" + DOCKER_SOCK_PATH; private static final int SOCKET_FILE_MODE_MASK = 0xc000; private static final String PING_TIMEOUT_DEFAULT = "10"; private static final String PING_TIMEOUT_PROPERTY_NAME = "testcontainers.unixsocketprovider.timeout"; + public static final int PRIORITY = EnvironmentAndSystemPropertyClientProviderStrategy.PRIORITY - 20; + @Override protected boolean isApplicable() { - return SystemUtils.IS_OS_UNIX && new File(DOCKER_SOCK_PATH).exists(); - } + final boolean nettyDoesSupportMacUnixSockets = SystemUtils.IS_OS_MAC_OSX && + ComparableVersion.OS_VERSION.isGreaterThanOrEqualTo("10.12"); - @Override - protected int getPriority() { - return PRIORITY; + return SystemUtils.IS_OS_LINUX || nettyDoesSupportMacUnixSockets; } @Override @@ -75,4 +72,8 @@ public String getDescription() { return "local Unix socket (" + SOCKET_LOCATION + ")"; } + @Override + protected int getPriority() { + return PRIORITY; + } } diff --git a/core/src/main/java/org/testcontainers/utility/ComparableVersion.java b/core/src/main/java/org/testcontainers/utility/ComparableVersion.java index daa438cf54c..71418711391 100644 --- a/core/src/main/java/org/testcontainers/utility/ComparableVersion.java +++ b/core/src/main/java/org/testcontainers/utility/ComparableVersion.java @@ -6,6 +6,8 @@ public class ComparableVersion implements Comparable { private final String[] parts; + public static final ComparableVersion OS_VERSION = new ComparableVersion(System.getProperty("os.version")); + public ComparableVersion(String version) { this.parts = version.split("\\."); } @@ -24,4 +26,12 @@ public int compareTo(@NotNull ComparableVersion other) { return 0; } + + public boolean isLessThan(String other) { + return this.compareTo(new ComparableVersion(other)) < 0; + } + + public boolean isGreaterThanOrEqualTo(String other) { + return this.compareTo(new ComparableVersion(other)) >= 0; + } } diff --git a/shade-test/jar-file/src/test/java/org/testcontainers/JarFileShadingTest.java b/shade-test/jar-file/src/test/java/org/testcontainers/JarFileShadingTest.java index 37296d7ad6d..a5c84361c01 100644 --- a/shade-test/jar-file/src/test/java/org/testcontainers/JarFileShadingTest.java +++ b/shade-test/jar-file/src/test/java/org/testcontainers/JarFileShadingTest.java @@ -1,7 +1,9 @@ package org.testcontainers; import org.assertj.core.api.ListAssert; -import org.junit.*; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; import java.io.IOException; import java.net.URI; From 99b10848aeb2c722d4182d68f2ae0431ec2194a4 Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 24 Jul 2017 21:53:06 +0100 Subject: [PATCH 0147/5615] Upgrade to v2.0.0 of Visible Assertions (#409) * Upgrade to v2.0.0 of Visible Assertions Removes usage of Jansi - fixes #395 * Update changelog --- CHANGELOG.md | 1 + core/pom.xml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f859fd803b..dd614aece03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file. ## UNRELEASED ### Fixed - Worked around incompatibility between Netty's Unix socket support and OS X 10.11. Reinstated use of TCP-Unix Socket proxy when running on OS X prior to v10.12. (Fixes #402) +- Changed to use version 2.0 of the Visible Assertions library for startup pre-flight checks. This no longer has a dependency on Jansi, and is intended to resolve a JVM crash issue apparently caused by native lib version conflicts (#395). Please note that the newer ANSI code is less mature and thus has had less testing, particularly in interesting terminal environments such as Windows. If issues are encountered, coloured assertion output may be disabled by setting the system property `visibleassertions.ansi.enabled` to `true`. ### Changed - Removed Guava usage from `jdbc` module (#401) diff --git a/core/pom.xml b/core/pom.xml index 4a93ed35ba7..de1b56d8cd5 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -92,7 +92,7 @@ org.rnorth.visible-assertions visible-assertions - 1.0.5 + 2.0.0 From 94d76416cdecb1789b359358118c2de092d07d0c Mon Sep 17 00:00:00 2001 From: Konstantin Date: Tue, 25 Jul 2017 11:06:08 +0300 Subject: [PATCH 0148/5615] Fix NPE for GenericContainer::isRunning (#411) (#412) * Fix NPE for GenericContainer::isRunning (#411) * Add test for GenericContainer::isRunning (#411) * Fix not stopped container in test (#411) * Added comment to CHANGELOG.md (#411) --- CHANGELOG.md | 1 + .../org/testcontainers/containers/GenericContainer.java | 2 +- .../testcontainers/junit/GenericContainerRuleTest.java | 9 +++++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd614aece03..276646e5081 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. ### Fixed - Worked around incompatibility between Netty's Unix socket support and OS X 10.11. Reinstated use of TCP-Unix Socket proxy when running on OS X prior to v10.12. (Fixes #402) - Changed to use version 2.0 of the Visible Assertions library for startup pre-flight checks. This no longer has a dependency on Jansi, and is intended to resolve a JVM crash issue apparently caused by native lib version conflicts (#395). Please note that the newer ANSI code is less mature and thus has had less testing, particularly in interesting terminal environments such as Windows. If issues are encountered, coloured assertion output may be disabled by setting the system property `visibleassertions.ansi.enabled` to `true`. +- Fixed NullPointerException when calling GenericContainer#isRunning on not started container (#411) ### Changed - Removed Guava usage from `jdbc` module (#401) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 6b055860256..a4276a09c9a 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -723,7 +723,7 @@ public String getIpAddress() { @Override public Boolean isRunning() { try { - return dockerClient.inspectContainerCmd(containerId).exec().getState().getRunning(); + return containerId != null && dockerClient.inspectContainerCmd(containerId).exec().getState().getRunning(); } catch (DockerException e) { return false; } diff --git a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java index b71cef1f7b4..b2a4d753b2d 100644 --- a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java +++ b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java @@ -144,6 +144,15 @@ public static void setupContent() throws FileNotFoundException { // assertTrue("The list contains an item that was put in (redis is working!)", testList2.contains("baz")); // } + @Test + public void testIsRunning() { + try (GenericContainer container = new GenericContainer()) { + assertFalse("Container is not started and not running", container.isRunning()); + container.start(); + assertTrue("Container is started and running", container.isRunning()); + } + } + @Test public void simpleRabbitMqTest() throws IOException, TimeoutException { ConnectionFactory factory = new ConnectionFactory(); From 6c8fb1b75e007305e991326584e8d8e1e40d3c9e Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 25 Jul 2017 11:09:22 +0100 Subject: [PATCH 0149/5615] Update changelog for 1.4.2 release --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 276646e5081..f8821a733c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # Change Log All notable changes to this project will be documented in this file. -## UNRELEASED +## [1.4.2] - 2017-07-25 ### Fixed - Worked around incompatibility between Netty's Unix socket support and OS X 10.11. Reinstated use of TCP-Unix Socket proxy when running on OS X prior to v10.12. (Fixes #402) - Changed to use version 2.0 of the Visible Assertions library for startup pre-flight checks. This no longer has a dependency on Jansi, and is intended to resolve a JVM crash issue apparently caused by native lib version conflicts (#395). Please note that the newer ANSI code is less mature and thus has had less testing, particularly in interesting terminal environments such as Windows. If issues are encountered, coloured assertion output may be disabled by setting the system property `visibleassertions.ansi.enabled` to `true`. @@ -320,6 +320,9 @@ All notable changes to this project will be documented in this file. ## [0.9] - 2015-04-29 Initial release +[1.4.2]: https://github.com/testcontainers/testcontainers-java/releases/tag/1.4.2 +[1.4.1]: https://github.com/testcontainers/testcontainers-java/releases/tag/1.4.1 +[1.4.0]: https://github.com/testcontainers/testcontainers-java/releases/tag/1.4.0 [1.2.0]: https://github.com/testcontainers/testcontainers-java/releases/tag/testcontainers-1.2.0 [1.1.9]: https://github.com/testcontainers/testcontainers-java/releases/tag/testcontainers-1.1.9 [1.1.8]: https://github.com/testcontainers/testcontainers-java/releases/tag/testcontainers-1.1.8 From 32b655190ddc7d2b24d136370cf4ca562a536903 Mon Sep 17 00:00:00 2001 From: "Gregor B. Rosenauer" Date: Tue, 1 Aug 2017 17:38:54 +0200 Subject: [PATCH 0150/5615] making docker compose container configurable, fixes #422 (#425) --- .../org/testcontainers/containers/DockerComposeContainer.java | 2 +- .../testcontainers/utility/TestcontainersConfiguration.java | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 42f42c71398..f34149c3900 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -396,7 +396,7 @@ default void validateFileList(List composeFiles) { class ContainerisedDockerCompose extends GenericContainer implements DockerCompose { public ContainerisedDockerCompose(List composeFiles, String identifier) { - super("docker/compose:1.8.0"); + super(TestcontainersConfiguration.getInstance().getDockerComposeContainerImage()); validateFileList(composeFiles); addEnv(ENV_PROJECT_NAME, identifier); diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index a47b2b90637..bb7acb0bc9e 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -34,6 +34,10 @@ public String getVncRecordedContainerImage() { return (String) properties.getOrDefault("vncrecorder.container.image", "richnorth/vnc-recorder:latest"); } + public String getDockerComposeContainerImage() { + return (String) properties.getOrDefault("compose.container.image", "docker/compose:1.8.0"); + } + public String getTinyImage() { return (String) properties.getOrDefault("tinyimage.container.image", "alpine:3.5"); } From 1a047811f87b0c14dacd367cef5a1dcbd2ee701a Mon Sep 17 00:00:00 2001 From: Barry Commins Date: Fri, 11 Aug 2017 09:16:58 +0100 Subject: [PATCH 0151/5615] Setting platform dependent docker-compose executable name (#437) * Setting platform dependent docker-compose executable name Fixes #416 * Changed docker-compose executable naming to be more concise * Reverted Compose executable variable name, added change to CHANGELOG.md --- CHANGELOG.md | 6 ++++++ .../testcontainers/containers/DockerComposeContainer.java | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8821a733c6..b0a7a5ea703 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # Change Log All notable changes to this project will be documented in this file. +## UNRELEASED +### Fixed +- Fixed local Docker Compose executable name resolution on Windows (#416) + +### Changed + ## [1.4.2] - 2017-07-25 ### Fixed - Worked around incompatibility between Netty's Unix socket support and OS X 10.11. Reinstated use of TCP-Unix Socket proxy when running on OS X prior to v10.12. (Fixes #402) diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index f34149c3900..b5b30fd4a99 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -9,6 +9,7 @@ import com.google.common.collect.Maps; import com.google.common.util.concurrent.Uninterruptibles; import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.SystemUtils; import org.junit.runner.Description; import org.rnorth.ducttape.ratelimits.RateLimiter; import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; @@ -464,7 +465,7 @@ class LocalDockerCompose implements DockerCompose { /** * Executable name for Docker Compose. */ - private static final String COMPOSE_EXECUTABLE = "docker-compose"; + private static final String COMPOSE_EXECUTABLE = SystemUtils.IS_OS_WINDOWS ? "docker-compose.exe" : "docker-compose"; private final List composeFiles; private final String identifier; From 27ac21fe00f524c726616473a638e34dae05e6cf Mon Sep 17 00:00:00 2001 From: Jorge Date: Sun, 27 Aug 2017 21:42:32 +0200 Subject: [PATCH 0152/5615] Improve documentation with Gitlab CI (#420) * add CI with Gitlab * explain docker command * gitlab doc improved * remove comment about network host --- docs/SUMMARY.md | 5 ++++- docs/ci/ci.md | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 docs/ci/ci.md diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 62f1d877a92..2d76338088d 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -46,11 +46,14 @@ * [Dockerfile containers](usage/dockerfile.md) * [Windows support](usage/windows_support.md) +## Continuous Integration + +* [GitLab CI](ci/ci.md#gitlab) + ## Examples * [Selenium](https://github.com/testcontainers/testcontainers-java-examples/blob/master/selenium-container/src/test/java/SeleniumContainerTest.java) * [Custom Redis container](https://github.com/testcontainers/testcontainers-java-examples/blob/master/redis-backed-cache/src/test/java/RedisBackedCacheTest.java) * [Spring Boot testing with Redis](https://github.com/testcontainers/testcontainers-java-examples/tree/master/spring-boot/src/test/java/com/example) - ## * [License](index.md#license) * [Attributions](index.md#attributions) diff --git a/docs/ci/ci.md b/docs/ci/ci.md new file mode 100644 index 00000000000..25d11eb2c28 --- /dev/null +++ b/docs/ci/ci.md @@ -0,0 +1,39 @@ +# Continuous Integration + +## GitLab + + +To be able to run your test container into the pipeline you need to run the job as a docker container (see [Running inside Docker](usage/inside_docker.md)), +so you need Docker-In-Docker (docker:dind) service and mount the docker's socket. Also if you have "Permission denied" you +need to run your container as root user: + +```yml +# to improve performance +variables: + DOCKER_DRIVER: overlay2 + +# we need DinD service +services: + - docker:dind + +test: + image: docker:latest + stage: test + script: + - > + docker run -t --rm + -v /var/run/docker.sock:/var/run/docker.sock + -v "$(pwd)":"$(pwd)" + -w "$(pwd)" + -u 0:0 + gradle:3.4 ./gradlew test + only: + - master +``` + +In this job we run in a multiline command a docker container: +* using the official gradle image +* to be delete when finished +* use the root user if you are running a Shared Runner + + From 022ef1575819a251fffebbd5daa57595128b6c7c Mon Sep 17 00:00:00 2001 From: Stefan Rempfer Date: Sun, 27 Aug 2017 21:47:25 +0200 Subject: [PATCH 0153/5615] Load DockerClientProviderStrategy via ServiceLoader (#434) (#435) --- .../org/testcontainers/DockerClientFactory.java | 16 ++++++---------- .../DockerClientProviderStrategy.java | 12 ------------ .../InvalidConfigurationException.java | 15 +++++++++++++++ ...ers.dockerclient.DockerClientProviderStrategy | 5 +++++ 4 files changed, 26 insertions(+), 22 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/dockerclient/InvalidConfigurationException.java create mode 100644 core/src/main/resources/META-INF/services/org.testcontainers.dockerclient.DockerClientProviderStrategy diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index bb40de7fa3b..51d26695eee 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -25,13 +25,13 @@ import java.io.InputStream; import java.net.Socket; import java.nio.charset.Charset; +import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.ServiceLoader; import java.util.function.BiFunction; import java.util.function.Consumer; -import static java.util.Arrays.asList; - /** * Singleton class that provides initialized Docker clients. *

@@ -46,13 +46,6 @@ public class DockerClientFactory { // Cached client configuration private DockerClientProviderStrategy strategy; private boolean preconditionsChecked = false; - - private static final List CONFIGURATION_STRATEGIES = - asList(new EnvironmentAndSystemPropertyClientProviderStrategy(), - new UnixSocketClientProviderStrategy(), - new ProxiedUnixSocketClientProviderStrategy(), - new DockerMachineClientProviderStrategy(), - new WindowsClientProviderStrategy()); private String activeApiVersion; private String activeExecutionDriver; @@ -91,7 +84,10 @@ public DockerClient client() { return strategy.getClient(); } - strategy = DockerClientProviderStrategy.getFirstValidStrategy(CONFIGURATION_STRATEGIES); + List configurationStrategies = new ArrayList(); + ServiceLoader.load(DockerClientProviderStrategy.class).forEach( cs -> configurationStrategies.add( cs ) ); + + strategy = DockerClientProviderStrategy.getFirstValidStrategy(configurationStrategies); String hostIpAddress = strategy.getDockerHostIpAddress(); log.info("Docker host IP address is {}", hostIpAddress); diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index 94a5a0e81ad..5a7dcc9f466 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -170,16 +170,4 @@ protected void ping(DockerClient client, int timeoutInSeconds) { public String getDockerHostIpAddress() { return DockerClientConfigUtils.getDockerHostIpAddress(this.config); } - - - class InvalidConfigurationException extends RuntimeException { - - public InvalidConfigurationException(String s) { - super(s); - } - - public InvalidConfigurationException(String message, Throwable cause) { - super(message, cause); - } - } } diff --git a/core/src/main/java/org/testcontainers/dockerclient/InvalidConfigurationException.java b/core/src/main/java/org/testcontainers/dockerclient/InvalidConfigurationException.java new file mode 100644 index 00000000000..ef834c9cec5 --- /dev/null +++ b/core/src/main/java/org/testcontainers/dockerclient/InvalidConfigurationException.java @@ -0,0 +1,15 @@ +package org.testcontainers.dockerclient; + +/** + * Exception to indicate that a {@link DockerClientProviderStrategy} fails. + */ +public class InvalidConfigurationException extends RuntimeException { + + public InvalidConfigurationException(String s) { + super(s); + } + + public InvalidConfigurationException(String message, Throwable cause) { + super(message, cause); + } +} \ No newline at end of file diff --git a/core/src/main/resources/META-INF/services/org.testcontainers.dockerclient.DockerClientProviderStrategy b/core/src/main/resources/META-INF/services/org.testcontainers.dockerclient.DockerClientProviderStrategy new file mode 100644 index 00000000000..9c8db41a429 --- /dev/null +++ b/core/src/main/resources/META-INF/services/org.testcontainers.dockerclient.DockerClientProviderStrategy @@ -0,0 +1,5 @@ +org.testcontainers.dockerclient.EnvironmentAndSystemPropertyClientProviderStrategy +org.testcontainers.dockerclient.UnixSocketClientProviderStrategy +org.testcontainers.dockerclient.ProxiedUnixSocketClientProviderStrategy +org.testcontainers.dockerclient.DockerMachineClientProviderStrategy +org.testcontainers.dockerclient.WindowsClientProviderStrategy \ No newline at end of file From 1d7cd9e94c4d580984f39e23c11f643fcf652d1f Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 27 Aug 2017 20:49:07 +0100 Subject: [PATCH 0154/5615] Add #435 to changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0a7a5ea703..cf2a734d3c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. - Fixed local Docker Compose executable name resolution on Windows (#416) ### Changed +- Load `DockerClientProviderStrategy` via Service Loader (#434, #435) ## [1.4.2] - 2017-07-25 ### Fixed From bb235fec2e4e01362ef264af553bf1953caf25d5 Mon Sep 17 00:00:00 2001 From: locofocos Date: Sat, 16 Sep 2017 13:15:25 -0500 Subject: [PATCH 0155/5615] Updated Windows documentation (#454) --- docs/usage/windows_support.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/usage/windows_support.md b/docs/usage/windows_support.md index 8a5c749dc8b..a52ac6967fe 100644 --- a/docs/usage/windows_support.md +++ b/docs/usage/windows_support.md @@ -1,8 +1,9 @@ # Windows Support (Alpha) ## Prerequisites -* [Docker Toolbox for Windows](https://docs.docker.com/engine/installation/windows/) needs to be installed -* Currently Docker for Windows without Toolbox (Linux VM) is not supported +* [Docker for Windows](https://docs.docker.com/docker-for-windows/) needs to be installed + * Docker version 17.06 is confirmed to work on Windows 10 with Hyper-V. + * On the General page in Settings, "Expose daemon on tcp://localhost:2375 without TLS" must be checked. ## Limitations The following features are not available or do not work correctly so make sure you do not use them or use them with From ecb32865bb8d202086ad671985422abe9e014597 Mon Sep 17 00:00:00 2001 From: Bogdan Ilchyshyn Date: Sat, 16 Sep 2017 20:16:56 +0200 Subject: [PATCH 0156/5615] using newArrayList for withExposedPorts method in order to allow addExposedPort later (#453) resolves #451 --- .../org/testcontainers/containers/GenericContainer.java | 4 ++-- .../testcontainers/junit/GenericContainerRuleTest.java | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index a4276a09c9a..14f09448817 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -42,7 +42,7 @@ import java.util.function.Consumer; import java.util.stream.Collectors; -import static java.util.Arrays.asList; +import static com.google.common.collect.Lists.newArrayList; import static org.testcontainers.containers.output.OutputFrame.OutputType.STDERR; import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; import static org.testcontainers.utility.CommandLine.runShellCommand; @@ -554,7 +554,7 @@ protected void finished(Description description) { */ @Override public SELF withExposedPorts(Integer... ports) { - this.setExposedPorts(asList(ports)); + this.setExposedPorts(newArrayList(ports)); return self(); } diff --git a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java index b2a4d753b2d..b5ba5e1d0ed 100644 --- a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java +++ b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java @@ -23,6 +23,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import static org.hamcrest.CoreMatchers.equalTo; import static org.rnorth.visibleassertions.VisibleAssertions.*; import static org.testcontainers.containers.BindMode.READ_ONLY; import static org.testcontainers.containers.BindMode.READ_WRITE; @@ -338,4 +339,12 @@ private BufferedReader getReaderForContainerPort80(GenericContainer container) { return new BufferedReader(new InputStreamReader(socket.getInputStream())); }); } + + @Test + public void addExposedPortAfterWithExposedPortsTest() { + redis.addExposedPort(8987); + assertThat("Both ports should be exposed", redis.getExposedPorts().size(), equalTo(2)); + assertTrue("withExposedPort should be exposed", redis.getExposedPorts().contains(REDIS_PORT)); + assertTrue("addExposedPort should be exposed", redis.getExposedPorts().contains(8987)); + } } From 76ef41806b2f7897d7dd0047a44382d5f4c38762 Mon Sep 17 00:00:00 2001 From: dpapworth Date: Sat, 16 Sep 2017 19:20:43 +0100 Subject: [PATCH 0157/5615] Stop creation of temporary directory prior to creating temporary file. (#443) --- .../main/java/org/testcontainers/utility/MountableFile.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java index 629c0d803b0..dc13f9960fa 100644 --- a/core/src/main/java/org/testcontainers/utility/MountableFile.java +++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java @@ -183,8 +183,8 @@ private void copyFromJarToLocation(final JarFile jarFile, if (!entry.isDirectory()) { // Create parent directories - newFile.mkdirs(); - newFile.delete(); + Path parent = newFile.getAbsoluteFile().toPath().getParent(); + parent.toFile().mkdirs(); newFile.deleteOnExit(); try (InputStream is = jarFile.getInputStream(entry)) { From 540f2eec45b4aa43d9ff9f997627ee6bcf2fff26 Mon Sep 17 00:00:00 2001 From: glebsts Date: Sun, 17 Sep 2017 21:54:37 +0300 Subject: [PATCH 0158/5615] #444 'MountableFile path on Windows' - added separate filesystem path for file source (#445) * #444 'MountableFile path on Windows' - added separate filesystem path for file source * #444 'MountableFile path on Windows' - made FS path only remove first slash on Windows; made recursiveTar work with file real FS paths on any platform; changed file attribute reading to work with FS path; minor renames and better exc message; * #444 'MountableFile path on Windows' - small changelog update --- CHANGELOG.md | 1 + .../images/builder/traits/ClasspathTrait.java | 2 +- .../testcontainers/utility/MountableFile.java | 58 ++++++++++++++----- .../utility/MountableFileTest.java | 10 +--- 4 files changed, 47 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf2a734d3c3..ff47c168711 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file. ## UNRELEASED ### Fixed - Fixed local Docker Compose executable name resolution on Windows (#416) +- Made tar composing work on Windows as well (#444) ### Changed - Load `DockerClientProviderStrategy` via Service Loader (#434, #435) diff --git a/core/src/main/java/org/testcontainers/images/builder/traits/ClasspathTrait.java b/core/src/main/java/org/testcontainers/images/builder/traits/ClasspathTrait.java index 5617365f2cd..8203f81d494 100644 --- a/core/src/main/java/org/testcontainers/images/builder/traits/ClasspathTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/traits/ClasspathTrait.java @@ -13,6 +13,6 @@ public interface ClasspathTrait & BuildContext default SELF withFileFromClasspath(String path, String resourcePath) { final MountableFile mountableFile = MountableFile.forClasspathResource(resourcePath); - return ((SELF) this).withFileFromPath(path, Paths.get(mountableFile.getResolvedPath())); + return ((SELF) this).withFileFromPath(path, Paths.get(mountableFile.getFilesystemPath())); } } diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java index dc13f9960fa..06d08c2996d 100644 --- a/core/src/main/java/org/testcontainers/utility/MountableFile.java +++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java @@ -38,6 +38,11 @@ public class MountableFile implements Transferable { @Getter(lazy = true) private final String resolvedPath = resolvePath(); + @Getter(lazy = true) + private final String filesystemPath = resolveFilesystemPath(); + + private String resourcePath; + /** * Obtains a {@link MountableFile} corresponding to a resource on the classpath (including resources in JAR files) * @@ -115,12 +120,7 @@ private static String unencodeResourceURIToFilePath(@NotNull final String resour * @return a volume-mountable path. */ private String resolvePath() { - String result; - if (path.contains(".jar!")) { - result = extractClassPathResourceToTempLocation(this.path); - } else { - result = unencodeResourceURIToFilePath(path); - } + String result = getResourcePath(); if (SystemUtils.IS_OS_WINDOWS) { result = PathUtils.createMinGWPath(result); @@ -129,6 +129,32 @@ private String resolvePath() { return result; } + /** + * Obtain a path in local filesystem that the Docker daemon should be able to use to volume mount a file/resource + * into a container. If this is a classpath resource residing in a JAR, it will be extracted to + * a temporary location so that the Docker daemon is able to access it. + * + * @return + */ + private String resolveFilesystemPath() { + String result = getResourcePath(); + + if (SystemUtils.IS_OS_WINDOWS && result.startsWith("/")) { + result = result.substring(1); + } + + return result; + } + + private String getResourcePath() { + if (path.contains(".jar!")) { + resourcePath = extractClassPathResourceToTempLocation(this.path); + } else { + resourcePath = unencodeResourceURIToFilePath(path); + } + return resourcePath; + } + /** * Extract a file or directory tree from a JAR file to a temporary location. * This allows Docker to mount classpath resources as files. @@ -205,22 +231,22 @@ private void deleteOnExit(final Path path) { */ @Override public void transferTo(final TarArchiveOutputStream outputStream, String destinationPathInTar) { - recursiveTar(destinationPathInTar, this.getResolvedPath(), this.getResolvedPath(), outputStream); + recursiveTar(destinationPathInTar, this.getFilesystemPath(), this.getFilesystemPath(), outputStream); } /* * Recursively copies a file/directory into a TarArchiveOutputStream */ - private void recursiveTar(String destination, String sourceRootDir, String sourceCurrentItem, TarArchiveOutputStream tarArchive) { + private void recursiveTar(String entryFilename, String rootPath, String itemPath, TarArchiveOutputStream tarArchive) { try { - final File sourceFile = new File(sourceCurrentItem).getCanonicalFile(); // e.g. /foo/bar/baz - final File sourceRootFile = new File(sourceRootDir).getCanonicalFile(); // e.g. /foo + final File sourceFile = new File(itemPath).getCanonicalFile(); // e.g. /foo/bar/baz + final File sourceRootFile = new File(rootPath).getCanonicalFile(); // e.g. /foo final String relativePathToSourceFile = sourceRootFile.toPath().relativize(sourceFile.toPath()).toFile().toString(); // e.g. /bar/baz - final TarArchiveEntry tarEntry = new TarArchiveEntry(sourceFile, destination + "/" + relativePathToSourceFile); // entry filename e.g. /xyz/bar/baz + final TarArchiveEntry tarEntry = new TarArchiveEntry(sourceFile, entryFilename + "/" + relativePathToSourceFile); // entry filename e.g. /xyz/bar/baz // TarArchiveEntry automatically sets the mode for file/directory, but we can update to ensure that the mode is set exactly (inc executable bits) - tarEntry.setMode(getUnixFileMode(sourceCurrentItem)); + tarEntry.setMode(getUnixFileMode(itemPath)); tarArchive.putArchiveEntry(tarEntry); if (sourceFile.isFile()) { @@ -233,11 +259,11 @@ private void recursiveTar(String destination, String sourceRootDir, String sourc if (children != null) { // recurse into child files/directories for (final File child : children) { - recursiveTar(destination, sourceRootDir + File.separator, child.getCanonicalPath(), tarArchive); + recursiveTar(entryFilename, sourceRootFile.getCanonicalPath(), child.getCanonicalPath(), tarArchive); } } } catch (IOException e) { - log.error("Error when copying TAR file entry: {}", sourceCurrentItem, e); + log.error("Error when copying TAR file entry: {}", itemPath, e); throw new UncheckedIOException(e); // fail fast } } @@ -245,7 +271,7 @@ private void recursiveTar(String destination, String sourceRootDir, String sourc @Override public long getSize() { - final File file = new File(this.getResolvedPath()); + final File file = new File(this.getFilesystemPath()); if (file.isFile()) { return file.length(); } else { @@ -260,7 +286,7 @@ public String getDescription() { @Override public int getFileMode() { - return getUnixFileMode(this.getResolvedPath()); + return getUnixFileMode(this.getFilesystemPath()); } private int getUnixFileMode(final String pathAsString) { diff --git a/core/src/test/java/org/testcontainers/utility/MountableFileTest.java b/core/src/test/java/org/testcontainers/utility/MountableFileTest.java index 5ac1a1c0ed8..8e4a40fc5cc 100644 --- a/core/src/test/java/org/testcontainers/utility/MountableFileTest.java +++ b/core/src/test/java/org/testcontainers/utility/MountableFileTest.java @@ -60,10 +60,6 @@ public void forHostPathWithSpaces() throws Exception { assertFalse("The resolved path does not contain an escaped space", mountableFile.getResolvedPath().contains("\\ ")); } - /* - * - */ - @SuppressWarnings("ResultOfMethodCallIgnored") @NotNull private Path createTempFile(final String name) throws IOException { @@ -77,9 +73,9 @@ private Path createTempFile(final String name) throws IOException { } private void performChecks(final MountableFile mountableFile) { - final String mountablePath = mountableFile.getResolvedPath(); - assertTrue("The resolved path can be found", new File(mountablePath).exists()); - assertFalse("The resolved path does not contain any URL escaping", mountablePath.contains("%20")); + final String mountablePath = mountableFile.getFilesystemPath(); + assertTrue("The filesystem path '" + mountablePath + "' can be found", new File(mountablePath).exists()); + assertFalse("The filesystem path '" + mountablePath + "' does not contain any URL escaping", mountablePath.contains("%20")); } } \ No newline at end of file From ba48bc1e60cde9b5f7d51f9762c6959c15258444 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 17 Sep 2017 20:12:04 +0100 Subject: [PATCH 0159/5615] Update changelog --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff47c168711..1e578a74875 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,14 @@ All notable changes to this project will be documented in this file. ## UNRELEASED ### Fixed - Fixed local Docker Compose executable name resolution on Windows (#416) -- Made tar composing work on Windows as well (#444) +- Fix TAR composition on Windows (#444) +- Allow `addExposedPort` to be used after ports have been specified with `withExposedPorts` (#453) +- Stop creation of temporary directory prior to creating temporary file (#443) ### Changed - Load `DockerClientProviderStrategy` via Service Loader (#434, #435) +- Make it possible to specify docker compose container is configuration (#422, #425) + ## [1.4.2] - 2017-07-25 ### Fixed From 184546d29c32e3c6fd14ff16358ec5ab54f191c9 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 17 Sep 2017 20:30:13 +0100 Subject: [PATCH 0160/5615] Fix typo in changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e578a74875..b348b289fc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ All notable changes to this project will be documented in this file. ### Changed - Load `DockerClientProviderStrategy` via Service Loader (#434, #435) -- Make it possible to specify docker compose container is configuration (#422, #425) +- Make it possible to specify docker compose container in configuration (#422, #425) ## [1.4.2] - 2017-07-25 From 8eea37cc4913e792fff4ee87f4d853cbb2805fff Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 18 Sep 2017 21:44:56 +0100 Subject: [PATCH 0161/5615] Remember failure to ping docker daemon to reduce log garbage and reduce run time (#456) --- CHANGELOG.md | 3 +++ .../dockerclient/DockerClientProviderStrategy.java | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b348b289fc1..e37fc8df5e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ All notable changes to this project will be documented in this file. ## UNRELEASED +### Changed +- Added caching of failure to find a docker daemon, so that subsequent tests fail fast. This is likely to be a significant improvement in situations where there is no docker daemon available, dramatically reducing run time and log output when further attempts to find the docker daemon cannot succeed. + ### Fixed - Fixed local Docker Compose executable name resolution on Windows (#416) - Fix TAR composition on Windows (#444) diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index 5a7dcc9f466..8c89feed2ca 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Objects; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Stream; /** @@ -35,6 +36,8 @@ public abstract class DockerClientProviderStrategy { .withConstantThroughput() .build(); + private static final AtomicBoolean FAIL_FAST_ALWAYS = new AtomicBoolean(false); + /** * @throws InvalidConfigurationException if this strategy fails */ @@ -64,6 +67,11 @@ protected int getPriority() { * @return a working DockerClientConfig, as determined by successful execution of a ping command */ public static DockerClientProviderStrategy getFirstValidStrategy(List strategies) { + + if (FAIL_FAST_ALWAYS.get()) { + throw new IllegalStateException("Previous attempts to find a Docker environment failed. Will not retry. Please see logs and check configuration"); + } + List configurationFailures = new ArrayList<>(); return Stream @@ -134,6 +142,7 @@ public static DockerClientProviderStrategy getFirstValidStrategy(List Date: Mon, 18 Sep 2017 22:05:08 +0100 Subject: [PATCH 0162/5615] Adjust wording of pre-flight check messages to be clearer (#457) * Adjust wording of pre-flight check messages to be clearer when presented as an AssertionError. Replaces #436. * Update changelog --- CHANGELOG.md | 1 + .../main/java/org/testcontainers/DockerClientFactory.java | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e37fc8df5e0..93c65126bc5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ All notable changes to this project will be documented in this file. ### Changed - Load `DockerClientProviderStrategy` via Service Loader (#434, #435) - Make it possible to specify docker compose container in configuration (#422, #425) +- Clarify wording of pre-flight check messages (#457, #436) ## [1.4.2] - 2017-07-25 diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 51d26695eee..ef0edab403c 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -142,7 +142,7 @@ public boolean matches(Object o) { @Override public void describeTo(Description description) { - description.appendText("is newer than 1.6.0"); + description.appendText("should be at least 1.6.0"); } }); } @@ -162,7 +162,7 @@ private void checkDiskSpace(DockerClient dockerClient, String id) { DiskSpaceUsage df = parseAvailableDiskSpace(outputStream.toString()); VisibleAssertions.assertTrue( - "Docker environment has more than 2GB free", + "Docker environment should have more than 2GB free disk space", df.availableMB.map(it -> it >= 2048).orElse(true) ); } @@ -187,7 +187,7 @@ private void checkExposedPort(String hostIpAddress, DockerClient dockerClient, S } catch (IOException e) { response = e.getMessage(); } - VisibleAssertions.assertEquals("Exposed port is accessible", "hello", response); + VisibleAssertions.assertEquals("A port exposed by a docker container should be accessible", "hello", response); } /** From 56513a44e21d21dc15d14416806775b80c252efd Mon Sep 17 00:00:00 2001 From: Nick Babcock Date: Mon, 18 Sep 2017 16:09:15 -0500 Subject: [PATCH 0163/5615] Jdbc containers with user, passwd, and db customizations (#400) --- .../junit/CustomizableMysqlTest.java | 47 +++++++++++++++++++ .../containers/JdbcDatabaseContainer.java | 13 +++++ .../containers/MySQLContainer.java | 35 +++++++++++--- .../containers/PostgreSQLContainer.java | 3 ++ 4 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomizableMysqlTest.java diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomizableMysqlTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomizableMysqlTest.java new file mode 100644 index 00000000000..11ad8f111ee --- /dev/null +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomizableMysqlTest.java @@ -0,0 +1,47 @@ +package org.testcontainers.junit; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import org.junit.Rule; +import org.junit.Test; +import org.testcontainers.containers.MySQLContainer; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; + +public class CustomizableMysqlTest { + private static final String DB_NAME = "foo"; + private static final String USER = "bar"; + private static final String PWD = "baz"; + + // Add MYSQL_ROOT_HOST environment so that we can root login from anywhere for testing purposes + @Rule + public MySQLContainer mysql = (MySQLContainer) new MySQLContainer("mysql:5.5") + .withDatabaseName(DB_NAME) + .withUsername(USER) + .withPassword(PWD) + .withEnv("MYSQL_ROOT_HOST", "%"); + + @Test + public void testSimple() throws SQLException { + HikariConfig hikariConfig = new HikariConfig(); + hikariConfig.setJdbcUrl("jdbc:mysql://" + + mysql.getContainerIpAddress() + + ":" + mysql.getMappedPort(MySQLContainer.MYSQL_PORT) + + "/" + DB_NAME); + hikariConfig.setUsername(USER); + hikariConfig.setPassword(PWD); + + HikariDataSource ds = new HikariDataSource(hikariConfig); + Statement statement = ds.getConnection().createStatement(); + statement.execute("SELECT 1"); + ResultSet resultSet = statement.getResultSet(); + + assertEquals("There is a result", resultSet.next(), true); + int resultSetInt = resultSet.getInt(1); + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + } +} diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index 033ca7ae5c0..fb44c90a6a2 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -60,6 +60,19 @@ public JdbcDatabaseContainer(String dockerImageName) { */ protected abstract String getTestQueryString(); + public SELF withUsername(String username) { + throw new UnsupportedOperationException(); + } + + public SELF withPassword(String password){ + throw new UnsupportedOperationException(); + } + + public SELF withDatabaseName(String dbName) { + throw new UnsupportedOperationException(); + + } + @Override protected void waitUntilContainerStarted() { // Repeatedly try and open a connection to the DB and execute a test query diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java index 3f2332e0143..9401dce238d 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java @@ -8,7 +8,10 @@ public class MySQLContainer> extends JdbcDatab public static final String NAME = "mysql"; public static final String IMAGE = "mysql"; private static final String MY_CNF_CONFIG_OVERRIDE_PARAM_NAME = "TC_MY_CNF"; - private static final Integer MYSQL_PORT = 3306; + public static final Integer MYSQL_PORT = 3306; + private String databaseName = "test"; + private String username = "test"; + private String password = "test"; public MySQLContainer() { super(IMAGE + ":latest"); @@ -28,9 +31,9 @@ protected void configure() { optionallyMapResourceParameterAsVolume(MY_CNF_CONFIG_OVERRIDE_PARAM_NAME, "/etc/mysql/conf.d", "mysql-default-conf"); addExposedPort(3306); - addEnv("MYSQL_DATABASE", "test"); - addEnv("MYSQL_USER", "test"); - addEnv("MYSQL_PASSWORD", "test"); + addEnv("MYSQL_DATABASE", databaseName); + addEnv("MYSQL_USER", username); + addEnv("MYSQL_PASSWORD", password); addEnv("MYSQL_ROOT_PASSWORD", "test"); setCommand("mysqld"); setStartupAttempts(3); @@ -43,17 +46,17 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return "jdbc:mysql://" + getContainerIpAddress() + ":" + getMappedPort(MYSQL_PORT) + "/test"; + return "jdbc:mysql://" + getContainerIpAddress() + ":" + getMappedPort(MYSQL_PORT) + "/" + databaseName; } @Override public String getUsername() { - return "test"; + return username; } @Override public String getPassword() { - return "test"; + return password; } @Override @@ -65,4 +68,22 @@ public SELF withConfigurationOverride(String s) { parameters.put(MY_CNF_CONFIG_OVERRIDE_PARAM_NAME, s); return self(); } + + @Override + public SELF withDatabaseName(final String databaseName) { + this.databaseName = databaseName; + return self(); + } + + @Override + public SELF withUsername(final String username) { + this.username = username; + return self(); + } + + @Override + public SELF withPassword(final String password) { + this.password = password; + return self(); + } } diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index f8229f4131c..9ba7d23b7aa 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -69,16 +69,19 @@ public String getTestQueryString() { return "SELECT 1"; } + @Override public SELF withDatabaseName(final String databaseName) { this.databaseName = databaseName; return self(); } + @Override public SELF withUsername(final String username) { this.username = username; return self(); } + @Override public SELF withPassword(final String password) { this.password = password; return self(); From 135da1b824ea5c35995a5d4b90eda7a932bb9ed5 Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 18 Sep 2017 22:12:58 +0100 Subject: [PATCH 0164/5615] Update changelog to reference #400 Misc wording/style changes to latest changelog entries --- CHANGELOG.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93c65126bc5..8510d6c0410 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,19 +2,18 @@ All notable changes to this project will be documented in this file. ## UNRELEASED -### Changed -- Added caching of failure to find a docker daemon, so that subsequent tests fail fast. This is likely to be a significant improvement in situations where there is no docker daemon available, dramatically reducing run time and log output when further attempts to find the docker daemon cannot succeed. - ### Fixed - Fixed local Docker Compose executable name resolution on Windows (#416) -- Fix TAR composition on Windows (#444) -- Allow `addExposedPort` to be used after ports have been specified with `withExposedPorts` (#453) -- Stop creation of temporary directory prior to creating temporary file (#443) +- Fixed TAR composition on Windows (#444) +- Allowing `addExposedPort` to be used after ports have been specified with `withExposedPorts` (#453) +- Stopping creation of temporary directory prior to creating temporary file (#443) ### Changed -- Load `DockerClientProviderStrategy` via Service Loader (#434, #435) -- Make it possible to specify docker compose container in configuration (#422, #425) -- Clarify wording of pre-flight check messages (#457, #436) +- Changed `DockerClientProviderStrategy` to be loaded via Service Loader (#434, #435) +- Made it possible to specify docker compose container in configuration (#422, #425) +- Clarified wording of pre-flight check messages (#457, #436) +- Added caching of failure to find a docker daemon, so that subsequent tests fail fast. This is likely to be a significant improvement in situations where there is no docker daemon available, dramatically reducing run time and log output when further attempts to find the docker daemon cannot succeed. +- Allowing JDBC containers' username, password and DB name to be customized (#400, #354) ## [1.4.2] - 2017-07-25 From 66bd7c06007d3680f4be24e0290ac48a517c69ea Mon Sep 17 00:00:00 2001 From: Barry Commins Date: Tue, 19 Sep 2017 10:54:37 +0100 Subject: [PATCH 0165/5615] Add response body checking to HttpWaitStrategy (#441) Add forResponsePredicate() method to HttpWaitStrategy class. --- CHANGELOG.md | 2 +- .../containers/wait/HttpWaitStrategy.java | 39 ++++++++++++++++++- .../junit/wait/HttpWaitStrategyTest.java | 29 +++++++++++--- 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8510d6c0410..087c2d3d68b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,13 +9,13 @@ All notable changes to this project will be documented in this file. - Stopping creation of temporary directory prior to creating temporary file (#443) ### Changed +- Added `forResponsePredicate` method to HttpWaitStrategy to test response body (#441) - Changed `DockerClientProviderStrategy` to be loaded via Service Loader (#434, #435) - Made it possible to specify docker compose container in configuration (#422, #425) - Clarified wording of pre-flight check messages (#457, #436) - Added caching of failure to find a docker daemon, so that subsequent tests fail fast. This is likely to be a significant improvement in situations where there is no docker daemon available, dramatically reducing run time and log output when further attempts to find the docker daemon cannot succeed. - Allowing JDBC containers' username, password and DB name to be customized (#400, #354) - ## [1.4.2] - 2017-07-25 ### Fixed - Worked around incompatibility between Netty's Unix socket support and OS X 10.11. Reinstated use of TCP-Unix Socket proxy when running on OS X prior to v10.12. (Fixes #402) diff --git a/core/src/main/java/org/testcontainers/containers/wait/HttpWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/HttpWaitStrategy.java index abe3e110676..bc0aacb00ce 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/HttpWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/HttpWaitStrategy.java @@ -3,15 +3,17 @@ import com.google.common.base.Strings; import com.google.common.io.BaseEncoding; import org.rnorth.ducttape.TimeoutException; -import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.ContainerLaunchException; import org.testcontainers.containers.GenericContainer; +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URI; import java.net.URL; import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; import static org.rnorth.ducttape.unreliables.Unreliables.retryUntilSuccess; @@ -36,6 +38,7 @@ public class HttpWaitStrategy extends GenericContainer.AbstractWaitStrategy { private boolean tlsEnabled; private String username; private String password; + private Predicate responsePredicate; /** * Waits for the given status code. @@ -82,6 +85,16 @@ public HttpWaitStrategy withBasicCredentials(String username, String password) { return this; } + /** + * Waits for the response to pass the given predicate + * @param responsePredicate The predicate to test the response against + * @return this + */ + public HttpWaitStrategy forResponsePredicate(Predicate responsePredicate) { + this.responsePredicate = responsePredicate; + return this; + } + @Override protected void waitUntilReady() { final Integer livenessCheckPort = getLivenessCheckPort(); @@ -114,6 +127,14 @@ protected void waitUntilReady() { connection.getResponseCode())); } + if(responsePredicate != null) { + String responseBody = getResponseBody(connection); + if(!responsePredicate.test(responseBody)) { + throw new RuntimeException(String.format("Response: %s did not match predicate", + responseBody)); + } + } + } catch (IOException e) { throw new RuntimeException(e); } @@ -155,4 +176,20 @@ private URI buildLivenessUri(int livenessCheckPort) { private String buildAuthString(String username, String password) { return AUTH_BASIC + BaseEncoding.base64().encode((username + ":" + password).getBytes()); } + + private String getResponseBody(HttpURLConnection connection) throws IOException { + BufferedReader reader; + if (200 <= connection.getResponseCode() && connection.getResponseCode() <= 299) { + reader = new BufferedReader(new InputStreamReader((connection.getInputStream()))); + } else { + reader = new BufferedReader(new InputStreamReader((connection.getErrorStream()))); + } + + StringBuilder builder = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + builder.append(line); + } + return builder.toString(); + } } diff --git a/core/src/test/java/org/testcontainers/junit/wait/HttpWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/HttpWaitStrategyTest.java index 2c345ae6ca4..e434ff90466 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/HttpWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/HttpWaitStrategyTest.java @@ -14,16 +14,18 @@ */ public class HttpWaitStrategyTest extends AbstractWaitStrategyTest { /** - * Doubly-escaped newline sequence indicating end of the HTTP header. + * newline sequence indicating end of the HTTP header. */ - private static final String DOUBLE_NEWLINE = "\\\r\\\n\\\r\\\n"; + private static final String NEWLINE = "\r\n"; + + private static final String GOOD_RESPONSE_BODY = "Good Response Body"; /** * Expects that the WaitStrategy returns successfully after receiving an HTTP 200 response from the container. */ @Test public void testWaitUntilReady_Success() { - waitUntilReadyAndSucceed("while true; do echo -e \"HTTP/1.1 200 OK" + DOUBLE_NEWLINE + "\" | nc -lp 8080; done"); + waitUntilReadyAndSucceed(createShellCommand("200 OK", GOOD_RESPONSE_BODY)); } /** @@ -32,7 +34,16 @@ public void testWaitUntilReady_Success() { */ @Test public void testWaitUntilReady_Timeout() { - waitUntilReadyAndTimeout("while true; do echo -e \"HTTP/1.1 400 Bad Request" + DOUBLE_NEWLINE + "\" | nc -lp 8080; done"); + waitUntilReadyAndTimeout(createShellCommand("400 Bad Request", GOOD_RESPONSE_BODY)); + } + + /** + * Expects that the WaitStrategy throws a {@link RetryCountExceededException} after not the expected response body + * from the container within the timeout period. + */ + @Test + public void testWaitUntilReady_Timeout_BadResponseBody() { + waitUntilReadyAndTimeout(createShellCommand("200 OK", "Bad Response")); } /** @@ -48,6 +59,14 @@ protected void waitUntilReady() { super.waitUntilReady(); ready.set(true); } - }; + }.forResponsePredicate(s -> s.equals(GOOD_RESPONSE_BODY)); + } + + private String createShellCommand(String header, String responseBody) { + int length = responseBody.getBytes().length; + return "while true; do { echo -e \"HTTP/1.1 "+header+NEWLINE+ + "Content-Type: text/html"+NEWLINE+ + "Content-Length: "+length +NEWLINE+ "\";" + +" echo \""+responseBody+"\";} | nc -lp 8080; done"; } } From 5ccb305eabac3aa22da1355cd044300e41f8b056 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 20 Sep 2017 20:47:45 +0200 Subject: [PATCH 0166/5615] Public API tests (#391) * Add shading tests * do not shade jdbc module * Add public API tests to make sure that we don't expose classes from shaded packages --- shade-test/jar-file/pom.xml | 9 +- .../testcontainers/AbstractJarFileTest.java | 26 ++++ .../testcontainers/JarFileShadingTest.java | 27 +--- .../testcontainers/PublicBinaryAPITest.java | 124 ++++++++++++++++++ 4 files changed, 161 insertions(+), 25 deletions(-) create mode 100644 shade-test/jar-file/src/test/java/org/testcontainers/AbstractJarFileTest.java create mode 100644 shade-test/jar-file/src/test/java/org/testcontainers/PublicBinaryAPITest.java diff --git a/shade-test/jar-file/pom.xml b/shade-test/jar-file/pom.xml index 2f07b4f10f9..d731ac33642 100644 --- a/shade-test/jar-file/pom.xml +++ b/shade-test/jar-file/pom.xml @@ -16,7 +16,14 @@ org.assertj assertj-core - 3.6.2 + 3.8.0 + test + + + + org.ow2.asm + asm-debug-all + 5.2 test diff --git a/shade-test/jar-file/src/test/java/org/testcontainers/AbstractJarFileTest.java b/shade-test/jar-file/src/test/java/org/testcontainers/AbstractJarFileTest.java new file mode 100644 index 00000000000..47b74ede860 --- /dev/null +++ b/shade-test/jar-file/src/test/java/org/testcontainers/AbstractJarFileTest.java @@ -0,0 +1,26 @@ +package org.testcontainers; + +import java.io.IOException; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static java.util.Collections.emptyMap; + +public abstract class AbstractJarFileTest { + + public static Path root; + + static { + Path path = Paths.get("..", "..", "core", "target", "testcontainers-0-SNAPSHOT.jar"); + + try { + FileSystem fileSystem = FileSystems.newFileSystem(URI.create("jar:" + path.toUri()), emptyMap()); + root = fileSystem.getPath("/"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/shade-test/jar-file/src/test/java/org/testcontainers/JarFileShadingTest.java b/shade-test/jar-file/src/test/java/org/testcontainers/JarFileShadingTest.java index a5c84361c01..8e4e95b63c5 100644 --- a/shade-test/jar-file/src/test/java/org/testcontainers/JarFileShadingTest.java +++ b/shade-test/jar-file/src/test/java/org/testcontainers/JarFileShadingTest.java @@ -1,36 +1,15 @@ package org.testcontainers; import org.assertj.core.api.ListAssert; -import org.junit.AfterClass; -import org.junit.BeforeClass; import org.junit.Test; import java.io.IOException; -import java.net.URI; -import java.nio.file.*; +import java.nio.file.Files; +import java.nio.file.Path; -import static java.util.Collections.emptyMap; import static org.assertj.core.api.Assertions.assertThat; -public class JarFileShadingTest { - - private static FileSystem fileSystem; - - private static Path root; - - @BeforeClass - public static void setUp() throws Exception { - Path path = Paths.get("..", "..", "core", "target", "testcontainers-0-SNAPSHOT.jar"); - - fileSystem = FileSystems.newFileSystem(URI.create("jar:" + path.toUri()), emptyMap()); - - root = fileSystem.getRootDirectories().iterator().next(); - } - - @AfterClass - public static void tearDown() throws Exception { - fileSystem.close(); - } +public class JarFileShadingTest extends AbstractJarFileTest { @Test public void testPackages() throws Exception { diff --git a/shade-test/jar-file/src/test/java/org/testcontainers/PublicBinaryAPITest.java b/shade-test/jar-file/src/test/java/org/testcontainers/PublicBinaryAPITest.java new file mode 100644 index 00000000000..6d995e7e0be --- /dev/null +++ b/shade-test/jar-file/src/test/java/org/testcontainers/PublicBinaryAPITest.java @@ -0,0 +1,124 @@ +package org.testcontainers; + +import lombok.RequiredArgsConstructor; +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.MethodNode; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * This test checks that we don't expose any shaded class in our public API. + * We use {@link Parameterized} runner here to create a test per public class in Testcontainers' JAR file. + */ +@RunWith(Parameterized.class) +@RequiredArgsConstructor +public class PublicBinaryAPITest extends AbstractJarFileTest { + + private static String SHADED_PACKAGE = "org.testcontainers.shaded."; + private static String SHADED_PACKAGE_PATH = SHADED_PACKAGE.replaceAll("\\.", "/"); + + static { + Assertions.registerFormatterForType(ClassNode.class, it -> it.name); + Assertions.registerFormatterForType(FieldNode.class, it -> it.name); + Assertions.registerFormatterForType(MethodNode.class, it -> it.name + it.desc); + } + + @Parameters(name = "{0}") + public static List data() throws Exception { + List result = new ArrayList<>(); + + Files.walkFileTree(root, new SimpleFileVisitor() { + + @Override + public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException { + String fileName = path.toString(); + + if (!fileName.endsWith(".class")) { + return super.visitFile(path, attrs); + } + + if (!fileName.startsWith("/org/testcontainers/") ) { + return super.visitFile(path, attrs); + } + + if (fileName.startsWith("/" + SHADED_PACKAGE_PATH)) { + return super.visitFile(path, attrs); + } + + try(InputStream inputStream = Files.newInputStream(path)) { + ClassReader reader = new ClassReader(inputStream); + ClassNode node = new ClassNode(); + reader.accept(node, ClassReader.SKIP_CODE); + if ((node.access & Opcodes.ACC_PUBLIC) != 0) { + result.add(new Object[]{ fileName, node }); + } + } + + return super.visitFile(path, attrs); + } + }); + return result; + } + + private final String fileName; + + private final ClassNode classNode; + + @Test + public void testSuperClass() { + assertThat(classNode.superName) + .doesNotStartWith(SHADED_PACKAGE_PATH); + } + + @Test + public void testInterfaces() { + assertThat(classNode.interfaces) + .allSatisfy(it -> assertThat(it).doesNotStartWith(SHADED_PACKAGE_PATH)); + } + + @Test + public void testMethodReturnTypes() { + assertThat(classNode.methods) + .filteredOn(it -> (it.access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) != 0) + .allSatisfy(it -> assertThat(Type.getReturnType(it.desc).getClassName()).doesNotStartWith(SHADED_PACKAGE)); + } + + @Test + public void testMethodArguments() { + assertThat(classNode.methods) + .filteredOn(it -> (it.access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) != 0) + .allSatisfy(method -> assertThat(Arrays.asList(Type.getArgumentTypes(method.desc))) + .extracting(Type::getClassName) + .allSatisfy(it -> assertThat(it).doesNotStartWith(SHADED_PACKAGE)) + ); + } + + @Test + public void testFields() { + assertThat(classNode.fields) + .filteredOn(it -> (it.access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) != 0) + .allSatisfy(it -> assertThat(Type.getType(it.desc).getClassName()) + .doesNotStartWith(SHADED_PACKAGE) + ); + } +} From 0da7fca49481eb0344bbdffdca3c9445d89d68cd Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 21 Sep 2017 07:50:06 +0100 Subject: [PATCH 0167/5615] Fix apparent race condition in test, observed in docker-machine on Mac (#459) Fix apparent race condition in test, observed in docker-machine on Mac Startup success detection appeared to be seeing container as having exited already For the purposes of this test, all we need is a positive `isRunning` state, so a container long running command (`top`) is sufficient to achieve this --- .../java/org/testcontainers/junit/GenericContainerRuleTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java index b5ba5e1d0ed..230e8241f0d 100644 --- a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java +++ b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java @@ -147,7 +147,7 @@ public static void setupContent() throws FileNotFoundException { @Test public void testIsRunning() { - try (GenericContainer container = new GenericContainer()) { + try (GenericContainer container = new GenericContainer().withCommand("top")) { assertFalse("Container is not started and not running", container.isRunning()); container.start(); assertTrue("Container is started and running", container.isRunning()); From 540f5672df90aa5233dde1dde7e8a9bc021c6e88 Mon Sep 17 00:00:00 2001 From: Bogdan Snisar Date: Fri, 29 Sep 2017 12:20:19 +0300 Subject: [PATCH 0168/5615] Fix docker-compose executable check for Win10 (#461) * Use .exe suffix on Windows when looking for docker-compose executable Fixes #460 --- CHANGELOG.md | 2 +- .../containers/DockerComposeContainer.java | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 087c2d3d68b..e71113ccc5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. ## UNRELEASED ### Fixed -- Fixed local Docker Compose executable name resolution on Windows (#416) +- Fixed local Docker Compose executable name resolution on Windows (#416, #460) - Fixed TAR composition on Windows (#444) - Allowing `addExposedPort` to be used after ports have been specified with `withExposedPorts` (#453) - Stopping creation of temporary directory prior to creating temporary file (#443) diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index b5b30fd4a99..de342cb6745 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -494,7 +494,7 @@ public DockerCompose withEnv(Map env) { @Override public void invoke() { // bail out early - if (!CommandLine.executableExists(COMPOSE_EXECUTABLE)) { + if (!dockerComposeExecutableExists()) { throw new ContainerLaunchException("Local Docker Compose not found. Is " + COMPOSE_EXECUTABLE + " on the PATH?"); } @@ -531,6 +531,14 @@ public void invoke() { } } + private boolean dockerComposeExecutableExists() { + if (SystemUtils.IS_OS_WINDOWS) { + return CommandLine.executableExists(COMPOSE_EXECUTABLE + ".exe"); + } else { + return CommandLine.executableExists(COMPOSE_EXECUTABLE); + } + } + /** * @return a logger */ From d017025b5e2be3db1683bf2d8a80675f7af4f583 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 14 Oct 2017 13:05:46 +0100 Subject: [PATCH 0169/5615] Update changelog for release --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e71113ccc5e..c6f7037d3a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # Change Log All notable changes to this project will be documented in this file. -## UNRELEASED +## [1.4.3] - 2017-10-14 ### Fixed - Fixed local Docker Compose executable name resolution on Windows (#416, #460) - Fixed TAR composition on Windows (#444) From a816debfb825cd98ffa9401518019e90bd65b20a Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 15 Oct 2017 10:52:54 +0100 Subject: [PATCH 0170/5615] Replace all issue refs in CHANGELOG with links --- CHANGELOG.md | 170 +++++++++++++++++++++++++-------------------------- 1 file changed, 85 insertions(+), 85 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6f7037d3a2..1d3959067d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,27 +3,27 @@ All notable changes to this project will be documented in this file. ## [1.4.3] - 2017-10-14 ### Fixed -- Fixed local Docker Compose executable name resolution on Windows (#416, #460) -- Fixed TAR composition on Windows (#444) -- Allowing `addExposedPort` to be used after ports have been specified with `withExposedPorts` (#453) -- Stopping creation of temporary directory prior to creating temporary file (#443) +- Fixed local Docker Compose executable name resolution on Windows ([\#416](https://github.com/testcontainers/testcontainers-java/issues/416), [\#460](https://github.com/testcontainers/testcontainers-java/issues/460)) +- Fixed TAR composition on Windows ([\#444](https://github.com/testcontainers/testcontainers-java/issues/444)) +- Allowing `addExposedPort` to be used after ports have been specified with `withExposedPorts` ([\#453](https://github.com/testcontainers/testcontainers-java/issues/453)) +- Stopping creation of temporary directory prior to creating temporary file ([\#443](https://github.com/testcontainers/testcontainers-java/issues/443)) ### Changed -- Added `forResponsePredicate` method to HttpWaitStrategy to test response body (#441) -- Changed `DockerClientProviderStrategy` to be loaded via Service Loader (#434, #435) -- Made it possible to specify docker compose container in configuration (#422, #425) -- Clarified wording of pre-flight check messages (#457, #436) +- Added `forResponsePredicate` method to HttpWaitStrategy to test response body ([\#441](https://github.com/testcontainers/testcontainers-java/issues/441)) +- Changed `DockerClientProviderStrategy` to be loaded via Service Loader ([\#434](https://github.com/testcontainers/testcontainers-java/issues/434), [\#435](https://github.com/testcontainers/testcontainers-java/issues/435)) +- Made it possible to specify docker compose container in configuration ([\#422](https://github.com/testcontainers/testcontainers-java/issues/422), [\#425](https://github.com/testcontainers/testcontainers-java/issues/425)) +- Clarified wording of pre-flight check messages ([\#457](https://github.com/testcontainers/testcontainers-java/issues/457), [\#436](https://github.com/testcontainers/testcontainers-java/issues/436)) - Added caching of failure to find a docker daemon, so that subsequent tests fail fast. This is likely to be a significant improvement in situations where there is no docker daemon available, dramatically reducing run time and log output when further attempts to find the docker daemon cannot succeed. -- Allowing JDBC containers' username, password and DB name to be customized (#400, #354) +- Allowing JDBC containers' username, password and DB name to be customized ([\#400](https://github.com/testcontainers/testcontainers-java/issues/400), [\#354](https://github.com/testcontainers/testcontainers-java/issues/354)) ## [1.4.2] - 2017-07-25 ### Fixed -- Worked around incompatibility between Netty's Unix socket support and OS X 10.11. Reinstated use of TCP-Unix Socket proxy when running on OS X prior to v10.12. (Fixes #402) -- Changed to use version 2.0 of the Visible Assertions library for startup pre-flight checks. This no longer has a dependency on Jansi, and is intended to resolve a JVM crash issue apparently caused by native lib version conflicts (#395). Please note that the newer ANSI code is less mature and thus has had less testing, particularly in interesting terminal environments such as Windows. If issues are encountered, coloured assertion output may be disabled by setting the system property `visibleassertions.ansi.enabled` to `true`. -- Fixed NullPointerException when calling GenericContainer#isRunning on not started container (#411) +- Worked around incompatibility between Netty's Unix socket support and OS X 10.11. Reinstated use of TCP-Unix Socket proxy when running on OS X prior to v10.12. (Fixes [\#402](https://github.com/testcontainers/testcontainers-java/issues/402)) +- Changed to use version 2.0 of the Visible Assertions library for startup pre-flight checks. This no longer has a dependency on Jansi, and is intended to resolve a JVM crash issue apparently caused by native lib version conflicts ([\#395](https://github.com/testcontainers/testcontainers-java/issues/395)). Please note that the newer ANSI code is less mature and thus has had less testing, particularly in interesting terminal environments such as Windows. If issues are encountered, coloured assertion output may be disabled by setting the system property `visibleassertions.ansi.enabled` to `true`. +- Fixed NullPointerException when calling GenericContainer#isRunning on not started container ([\#411](https://github.com/testcontainers/testcontainers-java/issues/411)) ### Changed -- Removed Guava usage from `jdbc` module (#401) +- Removed Guava usage from `jdbc` module ([\#401](https://github.com/testcontainers/testcontainers-java/issues/401)) ## [1.4.1] - 2017-07-10 ### Fixed @@ -31,140 +31,140 @@ All notable changes to this project will be documented in this file. ## [1.4.0] - 2017-07-09 ### Fixed -- Fixed the case when disk's size is bigger than Integer's max value (#379, #380) +- Fixed the case when disk's size is bigger than Integer's max value ([\#379](https://github.com/testcontainers/testcontainers-java/issues/379), [\#380](https://github.com/testcontainers/testcontainers-java/issues/380)) - Fixed erroneous version reference used during CI testing of shaded dependencies -- Fixed leakage of Vibur and Tomcat JDBC test dependencies in `jdbc-test` and `mysql` modules (#382) -- Added timeout and retries for creation of `RemoteWebDriver` (#381, #373, #257) +- Fixed leakage of Vibur and Tomcat JDBC test dependencies in `jdbc-test` and `mysql` modules ([\#382](https://github.com/testcontainers/testcontainers-java/issues/382)) +- Added timeout and retries for creation of `RemoteWebDriver` ([\#381](https://github.com/testcontainers/testcontainers-java/issues/381), [\#373](https://github.com/testcontainers/testcontainers-java/issues/373), [\#257](https://github.com/testcontainers/testcontainers-java/issues/257)) - Fixed various shading issues -- Improved removal of containers/networks when using Docker Compose, eliminating irrelevant errors during cleanup (#342, #394) +- Improved removal of containers/networks when using Docker Compose, eliminating irrelevant errors during cleanup ([\#342](https://github.com/testcontainers/testcontainers-java/issues/342), [\#394](https://github.com/testcontainers/testcontainers-java/issues/394)) ### Changed -- Added support for Docker networks (#372) -- Added `getFirstMappedPort` method (#377) +- Added support for Docker networks ([\#372](https://github.com/testcontainers/testcontainers-java/issues/372)) +- Added `getFirstMappedPort` method ([\#377](https://github.com/testcontainers/testcontainers-java/issues/377)) - Extracted Oracle XE container into a separate repository ([testcontainers/testcontainers-java-module-oracle-xe](https://github.com/testcontainers/testcontainers-java-module-oracle-xe)) - Added shading tests -- Updated docker-java to 3.0.12 (#393) +- Updated docker-java to 3.0.12 ([\#393](https://github.com/testcontainers/testcontainers-java/issues/393)) ## [1.3.1] - 2017-06-22 ### Fixed -- Fixed non-POSIX fallback for file attribute reading (#371) -- Fixed NullPointerException in AuditLogger when running using slf4j-log4j12 bridge (#375) +- Fixed non-POSIX fallback for file attribute reading ([\#371](https://github.com/testcontainers/testcontainers-java/issues/371)) +- Fixed NullPointerException in AuditLogger when running using slf4j-log4j12 bridge ([\#375](https://github.com/testcontainers/testcontainers-java/issues/375)) - Improved cleanup of JDBC connections during database container startup checks ### Changed -- Extracted MariaDB into a separate repository (#337) -- Added `TC_DAEMON` JDBC URL flag to prevent `ContainerDatabaseDriver` from shutting down containers at the time all connections are closed. (#359, #360) -- Added pre-flight checks (can be disabled with `checks.disable` configuration property) (#363) -- Improved startup time by adding dynamic priorities to DockerClientProviderStrategy (#362) -- Added global configuration file `~/.testcontainers.properties` (#362) -- Added container arguments to specify SELinux contexts for mounts (#334) -- Removed unused Jersey dependencies (#361) +- Extracted MariaDB into a separate repository ([\#337](https://github.com/testcontainers/testcontainers-java/issues/337)) +- Added `TC_DAEMON` JDBC URL flag to prevent `ContainerDatabaseDriver` from shutting down containers at the time all connections are closed. ([\#359](https://github.com/testcontainers/testcontainers-java/issues/359), [\#360](https://github.com/testcontainers/testcontainers-java/issues/360)) +- Added pre-flight checks (can be disabled with `checks.disable` configuration property) ([\#363](https://github.com/testcontainers/testcontainers-java/issues/363)) +- Improved startup time by adding dynamic priorities to DockerClientProviderStrategy ([\#362](https://github.com/testcontainers/testcontainers-java/issues/362)) +- Added global configuration file `~/.testcontainers.properties` ([\#362](https://github.com/testcontainers/testcontainers-java/issues/362)) +- Added container arguments to specify SELinux contexts for mounts ([\#334](https://github.com/testcontainers/testcontainers-java/issues/334)) +- Removed unused Jersey dependencies ([\#361](https://github.com/testcontainers/testcontainers-java/issues/361)) - Removed deprecated, wrongly-generated setters from `GenericContainer` ## [1.3.0] - 2017-06-05 ### Fixed -- Improved container cleanup if startup failed (#336, #335) +- Improved container cleanup if startup failed ([\#336](https://github.com/testcontainers/testcontainers-java/issues/336), [\#335](https://github.com/testcontainers/testcontainers-java/issues/335)) ### Changed -- Upgraded docker-java library to 3.0.10 (#349) +- Upgraded docker-java library to 3.0.10 ([\#349](https://github.com/testcontainers/testcontainers-java/issues/349)) - Added basic audit logging of Testcontainers' actions via a specific SLF4J logger name with metadata captured via MDC. Intended for use in highly shared Docker environments. -- Use string-based detection of Selenium container startup (#328, #351) -- Use string-based detection of PostgreSQL container startup (#327, #317) -- Update libraries to recent versions (#333) -- Introduce abstraction over files and classpath resources, allowing recursive copying of directories (#313) +- Use string-based detection of Selenium container startup ([\#328](https://github.com/testcontainers/testcontainers-java/issues/328), [\#351](https://github.com/testcontainers/testcontainers-java/issues/351)) +- Use string-based detection of PostgreSQL container startup ([\#327](https://github.com/testcontainers/testcontainers-java/issues/327), [\#317](https://github.com/testcontainers/testcontainers-java/issues/317)) +- Update libraries to recent versions ([\#333](https://github.com/testcontainers/testcontainers-java/issues/333)) +- Introduce abstraction over files and classpath resources, allowing recursive copying of directories ([\#313](https://github.com/testcontainers/testcontainers-java/issues/313)) ## [1.2.1] - 2017-04-06 ### Fixed -- Fix bug in space detection when `alpine:3.5` image has not yet been pulled (#323, #324) +- Fix bug in space detection when `alpine:3.5` image has not yet been pulled ([\#323](https://github.com/testcontainers/testcontainers-java/issues/323), [\#324](https://github.com/testcontainers/testcontainers-java/issues/324)) - Minor documentation fixes ### Changed -- Add AOP Alliance dependencies to shaded deps to reduce chance of conflicts (#315) +- Add AOP Alliance dependencies to shaded deps to reduce chance of conflicts ([\#315](https://github.com/testcontainers/testcontainers-java/issues/315)) ## [1.2.0] - 2017-03-12 ### Fixed -- Fix various escaping issues that may arise when paths contain spaces (#263, #279) -- General documentation fixes/improvements (#300, #303, #304) -- Improve reliability of `ResourceReaper` when there are a large number of containers returned by `docker ps -a` (#295) +- Fix various escaping issues that may arise when paths contain spaces ([\#263](https://github.com/testcontainers/testcontainers-java/issues/263), [\#279](https://github.com/testcontainers/testcontainers-java/issues/279)) +- General documentation fixes/improvements ([\#300](https://github.com/testcontainers/testcontainers-java/issues/300), [\#303](https://github.com/testcontainers/testcontainers-java/issues/303), [\#304](https://github.com/testcontainers/testcontainers-java/issues/304)) +- Improve reliability of `ResourceReaper` when there are a large number of containers returned by `docker ps -a` ([\#295](https://github.com/testcontainers/testcontainers-java/issues/295)) ### Changed -- Support Docker for Windows via TCP socket connection (#291, #297, #309). _Note that Docker Compose is not yet supported under Docker for Windows (see #306) -- Expose `docker-java`'s `CreateContainerCmd` API for low-level container tweaking (#301) -- Shade `org.newsclub` and Guava dependencies (#299, #292) -- Add `org.testcontainers` label to all containers created by Testcontainers (#294) +- Support Docker for Windows via TCP socket connection ([\#291](https://github.com/testcontainers/testcontainers-java/issues/291), [\#297](https://github.com/testcontainers/testcontainers-java/issues/297), [\#309](https://github.com/testcontainers/testcontainers-java/issues/309)). _Note that Docker Compose is not yet supported under Docker for Windows (see [\#306](https://github.com/testcontainers/testcontainers-java/issues/306)) +- Expose `docker-java`'s `CreateContainerCmd` API for low-level container tweaking ([\#301](https://github.com/testcontainers/testcontainers-java/issues/301)) +- Shade `org.newsclub` and Guava dependencies ([\#299](https://github.com/testcontainers/testcontainers-java/issues/299), [\#292](https://github.com/testcontainers/testcontainers-java/issues/292)) +- Add `org.testcontainers` label to all containers created by Testcontainers ([\#294](https://github.com/testcontainers/testcontainers-java/issues/294)) ## [1.1.9] - 2017-02-12 ### Fixed -- Fix inability to run Testcontainers on Alpine linux. Unix-socket-over-TCP is now used in linux environments where netty fails due to lack of glibc libraries (#290) -- Fix slow feedback in the case of missing JDBC drivers by failing-fast if the required driver cannot be found (#280, #230) +- Fix inability to run Testcontainers on Alpine linux. Unix-socket-over-TCP is now used in linux environments where netty fails due to lack of glibc libraries ([\#290](https://github.com/testcontainers/testcontainers-java/issues/290)) +- Fix slow feedback in the case of missing JDBC drivers by failing-fast if the required driver cannot be found ([\#280](https://github.com/testcontainers/testcontainers-java/issues/280), [\#230](https://github.com/testcontainers/testcontainers-java/issues/230)) ### Changed -- Add ability to change 'tiny image' used for disk space checks (#287) -- Add ability to attach volumes to a container using 'volumes from' (#244, #289) +- Add ability to change 'tiny image' used for disk space checks ([\#287](https://github.com/testcontainers/testcontainers-java/issues/287)) +- Add ability to attach volumes to a container using 'volumes from' ([\#244](https://github.com/testcontainers/testcontainers-java/issues/244), [\#289](https://github.com/testcontainers/testcontainers-java/issues/289)) ## [1.1.8] - 2017-01-22 ### Fixed -- Compatibility fixes for Docker for Mac v1.13.0 (#272) -- Relax docker environment disk space check to accomodate unusual empty `df` output observed on Docker for Mac with OverlayFS (#273, #278) -- Fix inadvertent private-scoping of startup checks' `StartupStatus`, which made implementation of custom startup checks impossible (#266) -- Fix potential resource lead/deadlock when errors are encountered building images from a Dockerfile (#274) +- Compatibility fixes for Docker for Mac v1.13.0 ([\#272](https://github.com/testcontainers/testcontainers-java/issues/272)) +- Relax docker environment disk space check to accomodate unusual empty `df` output observed on Docker for Mac with OverlayFS ([\#273](https://github.com/testcontainers/testcontainers-java/issues/273), [\#278](https://github.com/testcontainers/testcontainers-java/issues/278)) +- Fix inadvertent private-scoping of startup checks' `StartupStatus`, which made implementation of custom startup checks impossible ([\#266](https://github.com/testcontainers/testcontainers-java/issues/266)) +- Fix potential resource lead/deadlock when errors are encountered building images from a Dockerfile ([\#274](https://github.com/testcontainers/testcontainers-java/issues/274)) ### Changed -- Add support for execution within a Docker container (#267), correcting resolution of container addresses -- Add support for version 2 of private docker registries, configured via `$HOME/.docker/config.json` (#270) -- Use current classloader instead of system classloader for loading JDBC drivers (#261) -- Allow hardcoded container image names for Ambassador and VNC recorder containers to be changed via a configuration file (#277, #259) -- Allow Selenium Webdriver container image name to be specified as a constructor parameter (#249, #171) +- Add support for execution within a Docker container ([\#267](https://github.com/testcontainers/testcontainers-java/issues/267)), correcting resolution of container addresses +- Add support for version 2 of private docker registries, configured via `$HOME/.docker/config.json` ([\#270](https://github.com/testcontainers/testcontainers-java/issues/270)) +- Use current classloader instead of system classloader for loading JDBC drivers ([\#261](https://github.com/testcontainers/testcontainers-java/issues/261)) +- Allow hardcoded container image names for Ambassador and VNC recorder containers to be changed via a configuration file ([\#277](https://github.com/testcontainers/testcontainers-java/issues/277), [\#259](https://github.com/testcontainers/testcontainers-java/issues/259)) +- Allow Selenium Webdriver container image name to be specified as a constructor parameter ([\#249](https://github.com/testcontainers/testcontainers-java/issues/249), [\#171](https://github.com/testcontainers/testcontainers-java/issues/171)) ## [1.1.7] - 2016-11-19 ### Fixed -- Compensate for premature TCP socket opening in Docker for Mac (#160, #236) -- (Internal) Stabilise various parts of Testcontainers' self test suite (#241) -- Fix mounting of classpath resources when those resources are in a JAR file (#213) -- Reduce misleading error messages caused mainly by trying to perform operations on stopped containers (#243) +- Compensate for premature TCP socket opening in Docker for Mac ([\#160](https://github.com/testcontainers/testcontainers-java/issues/160), [\#236](https://github.com/testcontainers/testcontainers-java/issues/236)) +- (Internal) Stabilise various parts of Testcontainers' self test suite ([\#241](https://github.com/testcontainers/testcontainers-java/issues/241)) +- Fix mounting of classpath resources when those resources are in a JAR file ([\#213](https://github.com/testcontainers/testcontainers-java/issues/213)) +- Reduce misleading error messages caused mainly by trying to perform operations on stopped containers ([\#243](https://github.com/testcontainers/testcontainers-java/issues/243)) ### Changed -- Uses a default MySQL and MariaDB configuration to reduce memory footprint (#209, #243) -- Docker Compose can optionally now use a local `docker-compose` executable rather than running inside a container (#200) -- Add support for privileged mode containers (#234, #235) -- Allow container/network cleanup (ResourceReaper) to be triggered programmatically (#231) -- Add optional tailing of logs for containers spawned by Docker Compose (#233) +- Uses a default MySQL and MariaDB configuration to reduce memory footprint ([\#209](https://github.com/testcontainers/testcontainers-java/issues/209), [\#243](https://github.com/testcontainers/testcontainers-java/issues/243)) +- Docker Compose can optionally now use a local `docker-compose` executable rather than running inside a container ([\#200](https://github.com/testcontainers/testcontainers-java/issues/200)) +- Add support for privileged mode containers ([\#234](https://github.com/testcontainers/testcontainers-java/issues/234), [\#235](https://github.com/testcontainers/testcontainers-java/issues/235)) +- Allow container/network cleanup (ResourceReaper) to be triggered programmatically ([\#231](https://github.com/testcontainers/testcontainers-java/issues/231)) +- Add optional tailing of logs for containers spawned by Docker Compose ([\#233](https://github.com/testcontainers/testcontainers-java/issues/233)) - (Internal) Relocate non-proprietary database container tests to a single module ## [1.1.6] - 2016-09-22 ### Fixed -- Fix logging of discovered Docker environment variables (#218) -- Adopt longer timeout periods for testing docker client configurations, and allow these to be further customised through system properties (#217, see *ClientProviderStrategy classes) -- Fix docker compose directory mounting on windows (#224) -- Handle and ignore further categories of failure in retrieval of docker environment disk space (#225) +- Fix logging of discovered Docker environment variables ([\#218](https://github.com/testcontainers/testcontainers-java/issues/218)) +- Adopt longer timeout periods for testing docker client configurations, and allow these to be further customised through system properties ([\#217](https://github.com/testcontainers/testcontainers-java/issues/217), see *ClientProviderStrategy classes) +- Fix docker compose directory mounting on windows ([\#224](https://github.com/testcontainers/testcontainers-java/issues/224)) +- Handle and ignore further categories of failure in retrieval of docker environment disk space ([\#225](https://github.com/testcontainers/testcontainers-java/issues/225)) ### Changed -- Add extra configurability options (database name, username, password) for PostgreSQL DB containers (#220) -- Add MariaDB container type (#215) +- Add extra configurability options (database name, username, password) for PostgreSQL DB containers ([\#220](https://github.com/testcontainers/testcontainers-java/issues/220)) +- Add MariaDB container type ([\#215](https://github.com/testcontainers/testcontainers-java/issues/215)) - Use Docker Compose `down` action for more robust teardown of compose environments -- Ensure that Docker Compose operations run sequentially rather than concurrently if JUnit tests are parallelized (#226) -- Allow multiple Docker Compose files to be specified, to allow for extension/composition of services (#227) +- Ensure that Docker Compose operations run sequentially rather than concurrently if JUnit tests are parallelized ([\#226](https://github.com/testcontainers/testcontainers-java/issues/226)) +- Allow multiple Docker Compose files to be specified, to allow for extension/composition of services ([\#227](https://github.com/testcontainers/testcontainers-java/issues/227)) ## [1.1.5] - 2016-08-22 ### Fixed -- Fix Docker Compose environment variable passthrough (#208) +- Fix Docker Compose environment variable passthrough ([\#208](https://github.com/testcontainers/testcontainers-java/issues/208)) ### Changed -- Remove Docker Compose networks when containers are shut down (#211) as well as at JVM shutdown +- Remove Docker Compose networks when containers are shut down ([\#211](https://github.com/testcontainers/testcontainers-java/issues/211)) as well as at JVM shutdown ## [1.1.4] - 2016-08-16 ### Fixed -- Fix JDBC proxy driver behaviour when used with Tomcat connection pool to avoid spawning excessive numbers of containers (#195) -- Shade Jersey dependencies in JDBC module to avoid classpath conflicts (#202) -- Fix NullPointerException when docker host has untagged images (#201) -- Fix relative paths for volumes mounted in docker-compose containers (#189) +- Fix JDBC proxy driver behaviour when used with Tomcat connection pool to avoid spawning excessive numbers of containers ([\#195](https://github.com/testcontainers/testcontainers-java/issues/195)) +- Shade Jersey dependencies in JDBC module to avoid classpath conflicts ([\#202](https://github.com/testcontainers/testcontainers-java/issues/202)) +- Fix NullPointerException when docker host has untagged images ([\#201](https://github.com/testcontainers/testcontainers-java/issues/201)) +- Fix relative paths for volumes mounted in docker-compose containers ([\#189](https://github.com/testcontainers/testcontainers-java/issues/189)) ### Changed - Update to v3.0.2 of docker-java library -- Switch to a shared, single instance docker client rather than a separate client instance per container rule (#193) -- Ensure that docker-compose pulls images (with no timeout), prior to trying to start (#188) -- Use official `docker/compose` image for running docker-compose (#190) +- Switch to a shared, single instance docker client rather than a separate client instance per container rule ([\#193](https://github.com/testcontainers/testcontainers-java/issues/193)) +- Ensure that docker-compose pulls images (with no timeout), prior to trying to start ([\#188](https://github.com/testcontainers/testcontainers-java/issues/188)) +- Use official `docker/compose` image for running docker-compose ([\#190](https://github.com/testcontainers/testcontainers-java/issues/190)) ## [1.1.3] - 2016-07-27 ### Fixed @@ -185,8 +185,8 @@ All notable changes to this project will be documented in this file. ## [1.1.1] - 2016-07-17 ### Fixed -- Improve shutdown of unnecessary docker clients (#170) -- Shade `io.netty` dependencies into the testcontainers core JAR to reduce conflicts (#170 and #157) +- Improve shutdown of unnecessary docker clients ([\#170](https://github.com/testcontainers/testcontainers-java/issues/170)) +- Shade `io.netty` dependencies into the testcontainers core JAR to reduce conflicts ([\#170](https://github.com/testcontainers/testcontainers-java/issues/170) and [\#157](https://github.com/testcontainers/testcontainers-java/issues/157)) - Remove timeouts for docker compose execution, particularly useful when image pulls are involved - Improve output logging from docker-compose, pausing to log output in case of failure rather than letting logs intermingle. From 4c59a28bddff405057622be129a82ecb7a5ef951 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 14 Oct 2017 19:23:56 +0100 Subject: [PATCH 0171/5615] Add links to contrib modules --- docs/index.md | 1 + docs/usage/modules.md | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 docs/usage/modules.md diff --git a/docs/index.md b/docs/index.md index 24b6e05af3e..2dc6738948f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -14,6 +14,7 @@ TestContainers makes it easy to launch useful Docker containers for the duration * **Data access layer integration tests**: use a containerized instance of a MySQL, PostgreSQL or Oracle database to test your data access layer code for complete compatibility, but without requiring complex setup on developers' machines and safe in the knowledge that your tests will always start with a known DB state. Any other database type that can be containerized can also be used. * **Application integration tests**: for running your application in a short-lived test mode with dependencies, such as databases, message queues or web servers. * **UI/Acceptance tests**: use containerized web browsers, compatible with Selenium, for conducting automated UI tests. Each test can get a fresh instance of the browser, with no browser state, plugin variations or automated browser upgrades to worry about. And you get a video recording of each test session, or just each session where tests failed. + * **Much more!** Check out the various [contributed modules](modules.md) or create your own custom container classes using [`GenericContainer`](generic_containers.md) as a base. ## Who is using TestContainers? diff --git a/docs/usage/modules.md b/docs/usage/modules.md new file mode 100644 index 00000000000..e8b91487625 --- /dev/null +++ b/docs/usage/modules.md @@ -0,0 +1,10 @@ +# Contributed modules + +The following modules have been largely developed outside of the Testcontainers core project and are maintained and released separately: + +* [MS SQL Server](https://github.com/testcontainers/testcontainers-java-module-mssqlserver) +* [Oracle XE](https://github.com/testcontainers/testcontainers-java-module-oracle-xe) +* [Hashicorp Vault](https://github.com/testcontainers/testcontainers-java-module-vault) +* [Dynalite](https://github.com/testcontainers/testcontainers-java-module-dynalite) (Amazon DynamoDB emulation) +* [Atlassian Localstack](https://github.com/testcontainers/testcontainers-java-module-localstack) (emulation of a broad set of various AWS services) +* [MariaDB](https://github.com/testcontainers/testcontainers-java-module-mariadb) \ No newline at end of file From f505ea3e1f68a2e8d148a270425ddc7dc28bfeaf Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 15 Oct 2017 20:07:36 +0100 Subject: [PATCH 0172/5615] Add UNRELEASED section to changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d3959067d3..a605bd99893 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # Change Log All notable changes to this project will be documented in this file. +## UNRELEASED +_No changes yet_ + ## [1.4.3] - 2017-10-14 ### Fixed - Fixed local Docker Compose executable name resolution on Windows ([\#416](https://github.com/testcontainers/testcontainers-java/issues/416), [\#460](https://github.com/testcontainers/testcontainers-java/issues/460)) From c42599ff51fa9143bce919d890b8472d2f46faa0 Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 16 Oct 2017 21:33:58 +0100 Subject: [PATCH 0173/5615] Update path to Oracle test in docs. Fixes #430. --- docs/usage/database_containers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage/database_containers.md b/docs/usage/database_containers.md index 59e73e75d90..fd922ef5654 100644 --- a/docs/usage/database_containers.md +++ b/docs/usage/database_containers.md @@ -42,7 +42,7 @@ Examples/Tests: * [MySQL](https://github.com/testcontainers/testcontainers-java/blob/master/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java) * [PostgreSQL](https://github.com/testcontainers/testcontainers-java/blob/master/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimplePostgreSQLTest.java) - * [Oracle-XE](https://github.com/testcontainers/testcontainers-java/blob/master/modules/oracle-xe/src/test/java/org/testcontainers/junit/SimpleOracleTest.java) + * [Oracle-XE](https://github.com/testcontainers/testcontainers-java-module-oracle-xe/blob/master/src/test/java/org/testcontainers/junit/SimpleOracleTest.java) * [Virtuoso](https://github.com/testcontainers/testcontainers-java/blob/master/modules/virtuoso/src/test/java/org/testcontainers/junit/SimpleVirtuosoTest.java) ### JDBC URL From 357d5752a134bc340b1054acedf2eb78f7f24e64 Mon Sep 17 00:00:00 2001 From: Barry Commins Date: Thu, 19 Oct 2017 16:51:27 +0100 Subject: [PATCH 0174/5615] Fix regression when using Docker Compose on Windows (#474) Fixes #439 --- CHANGELOG.md | 5 ++++- .../containers/DockerComposeContainer.java | 10 +--------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a605bd99893..87ed33e3b4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,10 @@ All notable changes to this project will be documented in this file. ## UNRELEASED -_No changes yet_ +### Fixed +- Fixed regression in 1.4.3 when using Docker Compose on Windows ([\#439](https://github.com/testcontainers/testcontainers-java/issues/439)) + +### Changed ## [1.4.3] - 2017-10-14 ### Fixed diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index de342cb6745..b5b30fd4a99 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -494,7 +494,7 @@ public DockerCompose withEnv(Map env) { @Override public void invoke() { // bail out early - if (!dockerComposeExecutableExists()) { + if (!CommandLine.executableExists(COMPOSE_EXECUTABLE)) { throw new ContainerLaunchException("Local Docker Compose not found. Is " + COMPOSE_EXECUTABLE + " on the PATH?"); } @@ -531,14 +531,6 @@ public void invoke() { } } - private boolean dockerComposeExecutableExists() { - if (SystemUtils.IS_OS_WINDOWS) { - return CommandLine.executableExists(COMPOSE_EXECUTABLE + ".exe"); - } else { - return CommandLine.executableExists(COMPOSE_EXECUTABLE); - } - } - /** * @return a logger */ From 635f13737216b0cbea379d1bda36500eea42ddab Mon Sep 17 00:00:00 2001 From: Ivan Vasyliev Date: Mon, 23 Oct 2017 11:13:16 +0300 Subject: [PATCH 0175/5615] Add playtika as project user (#475) --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index 2dc6738948f..6343779a905 100644 --- a/docs/index.md +++ b/docs/index.md @@ -23,6 +23,7 @@ TestContainers makes it easy to launch useful Docker containers for the duration * [Apache Gora](https://gora.apache.org) - CouchDB testing * [Apache James](https://james.apache.org) - LDAP and Cassandra integration testing * [StreamSets](https://github.com/streamsets/datacollector) - LDAP, MySQL Vault, MongoDB, Redis integration testing + * [Playtika](http://www.playtika.com) - Kafka, Couchbase, MariaDB, Redis, Neo4j, Aerospike, MemSQL ## License From 1c66a143e6513d25b4d60044303b2fabf1c6c0fb Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 6 Nov 2017 13:15:39 +0300 Subject: [PATCH 0176/5615] Make Network instances reusable. Closes #469 (#470) * Make Network instances reusable. Closes #469 * Update CHANGELOG.md * Update CHANGELOG.md --- CHANGELOG.md | 1 + .../testcontainers/containers/Network.java | 30 +++++++++++++------ .../containers/NetworkTest.java | 19 ++++++++++++ 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87ed33e3b4f..e1f2ae3348e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. - Fixed regression in 1.4.3 when using Docker Compose on Windows ([\#439](https://github.com/testcontainers/testcontainers-java/issues/439)) ### Changed +- Make Network instances reusable (i.e. work with `@ClassRule`) ([\#469](https://github.com/testcontainers/testcontainers-java/issues/469)) ## [1.4.3] - 2017-10-14 ### Fixed diff --git a/core/src/main/java/org/testcontainers/containers/Network.java b/core/src/main/java/org/testcontainers/containers/Network.java index 36d15271e6c..1e1fbf31781 100644 --- a/core/src/main/java/org/testcontainers/containers/Network.java +++ b/core/src/main/java/org/testcontainers/containers/Network.java @@ -9,20 +9,15 @@ import org.testcontainers.DockerClientFactory; import org.testcontainers.utility.ResourceReaper; -import java.util.LinkedHashSet; import java.util.Set; import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; public interface Network extends AutoCloseable, TestRule { String getId(); - @Override - default void close() { - ResourceReaper.instance().removeNetworkById(getId()); - } - static Network newNetwork() { return builder().build(); } @@ -42,10 +37,20 @@ class NetworkImpl extends ExternalResource implements Network { private String driver; @Singular - private Set> createNetworkCmdModifiers = new LinkedHashSet<>(); + private Set> createNetworkCmdModifiers; - @Getter(lazy = true) - private final String id = create(); + private String id; + + private final AtomicBoolean initialized = new AtomicBoolean(); + + @Override + public String getId() { + if (initialized.compareAndSet(false, true)) { + id = create(); + } + + return id; + } private String create() { CreateNetworkCmd createNetworkCmd = DockerClientFactory.instance().client().createNetworkCmd(); @@ -74,5 +79,12 @@ private String create() { protected void after() { close(); } + + @Override + public void close() { + if (initialized.getAndSet(false)) { + ResourceReaper.instance().removeNetworkById(id); + } + } } } diff --git a/core/src/test/java/org/testcontainers/containers/NetworkTest.java b/core/src/test/java/org/testcontainers/containers/NetworkTest.java index 70b55c09928..04edf42c6d1 100644 --- a/core/src/test/java/org/testcontainers/containers/NetworkTest.java +++ b/core/src/test/java/org/testcontainers/containers/NetworkTest.java @@ -90,5 +90,24 @@ public void testModifiers() throws Exception { ); } } + + @Test + public void testReusability() throws Exception { + try (Network network = Network.newNetwork()) { + String firstId = network.getId(); + assertNotNull( + "Network exists", + DockerClientFactory.instance().client().inspectNetworkCmd().withNetworkId(firstId).exec() + ); + + network.close(); + + assertNotEquals( + "New network created", + firstId, + DockerClientFactory.instance().client().inspectNetworkCmd().withNetworkId(network.getId()).exec().getId() + ); + } + } } } From 2ad57da4a4f7b0d3396c4883294aa9c32ef480e7 Mon Sep 17 00:00:00 2001 From: Gleb Stsenov Date: Sat, 11 Nov 2017 20:35:43 +0000 Subject: [PATCH 0177/5615] #446 'set permission when copying' --- CHANGELOG.md | 1 + .../images/builder/traits/FilesTrait.java | 38 ++++++++++- .../testcontainers/utility/MountableFile.java | 59 +++++++++++++++-- .../utility/DirectoryTarResourceTest.java | 63 +++++++++++++++++++ .../utility/MountableFileTest.java | 35 +++++++++++ 5 files changed, 190 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1f2ae3348e..79bf3eef296 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. ### Changed - Make Network instances reusable (i.e. work with `@ClassRule`) ([\#469](https://github.com/testcontainers/testcontainers-java/issues/469)) +- Added support for explicitly setting file mode when copying file into container (#446) ## [1.4.3] - 2017-10-14 ### Fixed diff --git a/core/src/main/java/org/testcontainers/images/builder/traits/FilesTrait.java b/core/src/main/java/org/testcontainers/images/builder/traits/FilesTrait.java index 882c0e4231b..26ef980c25b 100644 --- a/core/src/main/java/org/testcontainers/images/builder/traits/FilesTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/traits/FilesTrait.java @@ -11,12 +11,46 @@ */ public interface FilesTrait & BuildContextBuilderTrait> { + /** + * Adds file to tarball copied into container. + * @param path in tarball + * @param file in host filesystem + * @return self + */ default SELF withFileFromFile(String path, File file) { - return withFileFromPath(path, file.toPath()); + return withFileFromPath(path, file.toPath(), 0); } + /** + * Adds file to tarball copied into container. + * @param path in tarball + * @param filePath in host filesystem + * @return self + */ default SELF withFileFromPath(String path, Path filePath) { - final MountableFile mountableFile = MountableFile.forHostPath(filePath); + return withFileFromPath(path, filePath, 0); + } + + /** + * Adds file with given mode to tarball copied into container. + * @param path in tarball + * @param file in host filesystem + * @param mode octal value of posix file mode (000..777) + * @return self + */ + default SELF withFileFromFile(String path, File file, int mode) { + return withFileFromPath(path, file.toPath(), mode); + } + + /** + * Adds file with given mode to tarball copied into container. + * @param path in tarball + * @param filePath in host filesystem + * @param mode octal value of posix file mode (000..777) + * @return self + */ + default SELF withFileFromPath(String path, Path filePath, int mode) { + final MountableFile mountableFile = MountableFile.forHostPath(filePath, mode); return ((SELF) this).withFileFromTransferable(path, mountableFile); } } diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java index 06d08c2996d..9fb4e815a83 100644 --- a/core/src/main/java/org/testcontainers/utility/MountableFile.java +++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java @@ -10,7 +10,11 @@ import org.jetbrains.annotations.NotNull; import org.testcontainers.images.builder.Transferable; -import java.io.*; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLDecoder; import java.nio.file.Files; @@ -33,7 +37,11 @@ @Slf4j public class MountableFile implements Transferable { + private static final int BASE_FILE_MODE = 0100000; + private static final int BASE_DIR_MODE = 0040000; + private final String path; + private final int forcedFileMode; @Getter(lazy = true) private final String resolvedPath = resolvePath(); @@ -50,7 +58,7 @@ public class MountableFile implements Transferable { * @return a {@link MountableFile} that may be used to obtain a mountable path */ public static MountableFile forClasspathResource(@NotNull final String resourceName) { - return new MountableFile(getClasspathResource(resourceName, new HashSet<>()).toString()); + return forClasspathResource(resourceName, -1); } /** @@ -60,7 +68,7 @@ public static MountableFile forClasspathResource(@NotNull final String resourceN * @return a {@link MountableFile} that may be used to obtain a mountable path */ public static MountableFile forHostPath(@NotNull final String path) { - return new MountableFile(new File(path).toURI().toString()); + return forHostPath(path, -1); } /** @@ -70,9 +78,43 @@ public static MountableFile forHostPath(@NotNull final String path) { * @return a {@link MountableFile} that may be used to obtain a mountable path */ public static MountableFile forHostPath(final Path path) { - return new MountableFile(path.toAbsolutePath().toString()); + return forHostPath(path, -1); + } + + /** + * Obtains a {@link MountableFile} corresponding to a resource on the classpath (including resources in JAR files) + * + * @param resourceName the classpath path to the resource + * @param mode octal value of posix file mode (000..777) + * @return a {@link MountableFile} that may be used to obtain a mountable path + */ + public static MountableFile forClasspathResource(@NotNull final String resourceName, int mode) { + return new MountableFile(getClasspathResource(resourceName, new HashSet<>()).toString(), mode); + } + + /** + * Obtains a {@link MountableFile} corresponding to a file on the docker host filesystem. + * + * @param path the path to the resource + * @param mode octal value of posix file mode (000..777) + * @return a {@link MountableFile} that may be used to obtain a mountable path + */ + public static MountableFile forHostPath(@NotNull final String path, int mode) { + return new MountableFile(new File(path).toURI().toString(), mode); } + /** + * Obtains a {@link MountableFile} corresponding to a file on the docker host filesystem. + * + * @param path the path to the resource + * @param mode octal value of posix file mode (000..777) + * @return a {@link MountableFile} that may be used to obtain a mountable path + */ + public static MountableFile forHostPath(final Path path, int mode) { + return new MountableFile(path.toAbsolutePath().toString(), mode); + } + + @NotNull private static URL getClasspathResource(@NotNull final String resourcePath, @NotNull final Set classLoaders) { @@ -291,6 +333,10 @@ public int getFileMode() { private int getUnixFileMode(final String pathAsString) { final Path path = Paths.get(pathAsString); + if (this.forcedFileMode > -1) { + return this.getModeValue(path); + } + try { return (int) Files.getAttribute(path, "unix:mode"); } catch (IOException | UnsupportedOperationException e) { @@ -305,4 +351,9 @@ private int getUnixFileMode(final String pathAsString) { return mode; } } + + private int getModeValue(final Path path) { + int result = Files.isDirectory(path) ? BASE_DIR_MODE : BASE_FILE_MODE; + return result | this.forcedFileMode; + } } \ No newline at end of file diff --git a/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java b/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java index c4b719ea1ca..70c366e3c80 100644 --- a/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java +++ b/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java @@ -1,5 +1,8 @@ package org.testcontainers.utility; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.core.IsCollectionContaining; import org.junit.Test; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.output.ToStringConsumer; @@ -8,9 +11,13 @@ import org.testcontainers.images.builder.ImageFromDockerfile; import java.io.File; +import java.util.Arrays; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import static org.hamcrest.CoreMatchers.allOf; +import static org.hamcrest.CoreMatchers.containsString; +import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; public class DirectoryTarResourceTest { @@ -41,6 +48,35 @@ public void simpleRecursiveFileTest() throws TimeoutException { assertTrue("The container has a file that was copied in via a recursive copy", results.contains("Used for DirectoryTarResourceTest")); } + @Test + public void simpleRecursiveFileWithPermissionTest() throws TimeoutException { + + WaitingConsumer wait = new WaitingConsumer(); + + final ToStringConsumer toString = new ToStringConsumer(); + + GenericContainer container = new GenericContainer( + new ImageFromDockerfile() + .withDockerfileFromBuilder(builder -> + builder.from("alpine:3.3") + .copy("/tmp/foo", "/foo") + .cmd("ls", "-al", "/") + .build() + ).withFileFromFile("/tmp/foo", new File("/mappable-resource/test-resource.txt"), + 0754)) + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .withLogConsumer(wait.andThen(toString)); + + container.start(); + wait.waitUntilEnd(60, TimeUnit.SECONDS); + + String listing = toString.toUtf8String(); + + assertThat("Listing shows that file is copied with mode requested.", + Arrays.asList(listing.split("\\n")), + exactlyNItems(1, allOf(containsString("-rwxr-xr--"), containsString("foo")))); + } + @Test public void simpleRecursiveClasspathResourceTest() throws TimeoutException { // This test combines the copying of classpath resources from JAR files with the recursive TAR approach, to allow JARed classpath resources to be copied in to an image @@ -68,4 +104,31 @@ public void simpleRecursiveClasspathResourceTest() throws TimeoutException { // ExternalResource.class is known to exist in a subdirectory of /org/junit so should be successfully copied in assertTrue("The container has a file that was copied in via a recursive copy from a JAR resource", results.contains("content.txt")); } + + public static Matcher> exactlyNItems(final int n, Matcher elementMatcher) { + return new IsCollectionContaining(elementMatcher) { + @Override + protected boolean matchesSafely(Iterable collection, Description mismatchDescription) { + int count = 0; + boolean isPastFirst = false; + + for (Object item : collection) { + + if (elementMatcher.matches(item)) { + count++; + } + if (isPastFirst) { + mismatchDescription.appendText(", "); + } + elementMatcher.describeMismatch(item, mismatchDescription); + isPastFirst = true; + } + + if (count != n) { + mismatchDescription.appendText(". Expected exactly " + n + " but got " + count); + } + return count == n; + } + }; + } } diff --git a/core/src/test/java/org/testcontainers/utility/MountableFileTest.java b/core/src/test/java/org/testcontainers/utility/MountableFileTest.java index 8e4a40fc5cc..c278953c112 100644 --- a/core/src/test/java/org/testcontainers/utility/MountableFileTest.java +++ b/core/src/test/java/org/testcontainers/utility/MountableFileTest.java @@ -8,11 +8,16 @@ import java.nio.file.Files; import java.nio.file.Path; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; public class MountableFileTest { + private static final int TEST_FILE_MODE = 0532; + private static final int BASE_FILE_MODE = 0100000; + private static final int BASE_DIR_MODE = 0040000; + @Test public void forClasspathResource() throws Exception { final MountableFile mountableFile = MountableFile.forClasspathResource("mappable-resource/test-resource.txt"); @@ -60,6 +65,31 @@ public void forHostPathWithSpaces() throws Exception { assertFalse("The resolved path does not contain an escaped space", mountableFile.getResolvedPath().contains("\\ ")); } + @Test + public void forClasspathResourceWithPermission() throws Exception { + final MountableFile mountableFile = MountableFile.forClasspathResource("mappable-resource/test-resource.txt", + TEST_FILE_MODE); + + performChecks(mountableFile); + assertEquals("Valid file mode.", BASE_FILE_MODE | TEST_FILE_MODE, mountableFile.getFileMode()); + } + + @Test + public void forHostFilePathWithPermission() throws Exception { + final Path file = createTempFile("somepath"); + final MountableFile mountableFile = MountableFile.forHostPath(file.toString(), TEST_FILE_MODE); + performChecks(mountableFile); + assertEquals("Valid file mode.", BASE_FILE_MODE | TEST_FILE_MODE, mountableFile.getFileMode()); + } + + @Test + public void forHostDirPathWithPermission() throws Exception { + final Path dir = createTempDir(); + final MountableFile mountableFile = MountableFile.forHostPath(dir.toString(), TEST_FILE_MODE); + performChecks(mountableFile); + assertEquals("Valid dir mode.", BASE_DIR_MODE | TEST_FILE_MODE, mountableFile.getFileMode()); + } + @SuppressWarnings("ResultOfMethodCallIgnored") @NotNull private Path createTempFile(final String name) throws IOException { @@ -72,6 +102,11 @@ private Path createTempFile(final String name) throws IOException { return file; } + @NotNull + private Path createTempDir() throws IOException { + return Files.createTempDirectory("testcontainers"); + } + private void performChecks(final MountableFile mountableFile) { final String mountablePath = mountableFile.getFilesystemPath(); assertTrue("The filesystem path '" + mountablePath + "' can be found", new File(mountablePath).exists()); From 49d8f6135019aae9de59385672a77efe51b1c4a5 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 11 Nov 2017 20:40:44 +0000 Subject: [PATCH 0178/5615] Update changelog refs --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79bf3eef296..758bcd3e037 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ All notable changes to this project will be documented in this file. ### Changed - Make Network instances reusable (i.e. work with `@ClassRule`) ([\#469](https://github.com/testcontainers/testcontainers-java/issues/469)) -- Added support for explicitly setting file mode when copying file into container (#446) +- Added support for explicitly setting file mode when copying file into container ([\#446](https://github.com/testcontainers/testcontainers-java/issues/446), [\#467](https://github.com/testcontainers/testcontainers-java/issues/467)) ## [1.4.3] - 2017-10-14 ### Fixed From b4d3c99bb38194849463b797dbfd210c4ff6cf92 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 12 Nov 2017 10:41:21 +0100 Subject: [PATCH 0179/5615] Add info about "properties" to the documentation (#485) * Document properties * Document macOS location of .testcontainers.properties * Fix markdown syntax --- docs/SUMMARY.md | 1 + docs/usage/properties.md | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 docs/usage/properties.md diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 2d76338088d..03c400b32dd 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -8,6 +8,7 @@ * [Usage modes](usage.md#usage-modes) * [Maven dependencies](usage.md#maven-dependencies) * [Logging](usage.md#logging) + * [Properties](usage/properties.md) * [Running inside Docker](usage/inside_docker.md) ## Generic containers diff --git a/docs/usage/properties.md b/docs/usage/properties.md new file mode 100644 index 00000000000..8890c5581e0 --- /dev/null +++ b/docs/usage/properties.md @@ -0,0 +1,38 @@ +# Custom properties + +You can override some default properties if your environment requires that. + +## Configuration file location +The configuration will be loaded from multiple locations. Properties are considered in the following order: +1. `.testcontainers.properties` in user's home folder. Example locations: +**Linux:** `/home/myuser/.testcontainers.properties` +**Windows:** `C:/Users/myuser/.testcontainers.properties` +**macOS:** `/Users/myuser/.testcontainers.properties` +2. `testcontainers.properties` on classpath + +## Disabling the startup checks +> **checks.disable = [true|false]** + +Before running any containers Testcontainers will perform a set of startup checks to ensure that your environment is configured correctly. Usually they look like this: +``` + ℹ︎ Checking the system... + ✔ Docker version should be at least 1.6.0 + ✔ Docker environment should have more than 2GB free disk space + ✔ File should be mountable + ✔ A port exposed by a docker container should be accessible +``` +It takes a couple of seconds, but if you want to speed up your tests, you can disable the checks once you have everything configured. Add `checks.disable=true` to your `$HOME/.testcontainers.properties` to completely disable them. + +## Customizing images +> **tinyimage.container.image = alpine:3.5** +> Used by Testcontainers' core + +> **vncrecorder.container.image = richnorth/vnc-recorder:latest** +> Used by VNC recorder in Testcontainers' Seleniun integration + +> **ambassador.container.image = richnorth/ambassador:latest** +> **compose.container.image = docker/compose:1.8.0** +> Used by Docker Compose integration + +Testcontainers uses public Docker images to perform different actions like startup checks, VNC recording and others. +Some companies disallow the usage of Docker Hub, but you can override `*.image` properties with your own images from your private registry to workaround that. From 74eecd4774fffc5141b2c1fe40026642b0364577 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 12 Nov 2017 16:06:11 +0000 Subject: [PATCH 0180/5615] Mark link-related APIs as deprecated pending future removal (#487) * Mark link-related APIs as deprecated pending future removal See #465 * Update changelog re deprecated links features --- CHANGELOG.md | 1 + .../java/org/testcontainers/containers/Container.java | 10 ++++++++++ .../testcontainers/containers/GenericContainer.java | 8 ++++++++ .../containers/traits/LinkableContainer.java | 3 +++ .../containers/BrowserWebDriverContainer.java | 3 +++ 5 files changed, 25 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 758bcd3e037..1f211fc882b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. ### Changed - Make Network instances reusable (i.e. work with `@ClassRule`) ([\#469](https://github.com/testcontainers/testcontainers-java/issues/469)) - Added support for explicitly setting file mode when copying file into container ([\#446](https://github.com/testcontainers/testcontainers-java/issues/446), [\#467](https://github.com/testcontainers/testcontainers-java/issues/467)) +- Mark all links functionality as deprecated. This is pending removal in a later release. Please see [\#465](https://github.com/testcontainers/testcontainers-java/issues/465). {@link Network} features should be used instead. ## [1.4.3] - 2017-10-14 ### Fixed diff --git a/core/src/main/java/org/testcontainers/containers/Container.java b/core/src/main/java/org/testcontainers/containers/Container.java index e8ad7f0ba75..880867401e8 100644 --- a/core/src/main/java/org/testcontainers/containers/Container.java +++ b/core/src/main/java/org/testcontainers/containers/Container.java @@ -104,7 +104,9 @@ default void addFileSystemBind(final String hostPath, final String containerPath * * @param otherContainer the other container object to link to * @param alias the alias (for the other container) that this container should be able to use + * @deprecated Links are deprecated (see #465). Please use {@link Network} features instead. */ + @Deprecated void addLink(LinkableContainer otherContainer, String alias); /** @@ -420,6 +422,10 @@ ExecResult execInContainer(Charset outputCharset, String... command) List getBinds(); + /** + * @deprecated Links are deprecated (see #465). Please use {@link Network} features instead. + */ + @Deprecated Map getLinkedContainers(); DockerClient getDockerClient(); @@ -446,6 +452,10 @@ ExecResult execInContainer(Charset outputCharset, String... command) void setBinds(List binds); + /** + * @deprecated Links are deprecated (see #465). Please use {@link Network} features instead. + */ + @Deprecated void setLinkedContainers(Map linkedContainers); void setWaitStrategy(WaitStrategy waitStrategy); diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 14f09448817..4932d12b7b6 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -99,7 +99,11 @@ public class GenericContainer> @NonNull private List volumesFroms = new ArrayList<>(); + /** + * @deprecated Links are deprecated (see #465). Please use {@link Network} features instead. + */ @NonNull + @Deprecated private Map linkedContainers = new HashMap<>(); private StartupCheckStrategy startupCheckStrategy = new IsRunningStartupCheckStrategy(); @@ -522,6 +526,10 @@ private void addVolumesFrom(Container container, BindMode mode) { volumesFroms.add(new VolumesFrom(container.getContainerName(), mode.accessMode)); } + /** + * @deprecated Links are deprecated (see #465). Please use {@link Network} features instead. + */ + @Deprecated @Override public void addLink(LinkableContainer otherContainer, String alias) { this.linkedContainers.put(alias, otherContainer); diff --git a/core/src/main/java/org/testcontainers/containers/traits/LinkableContainer.java b/core/src/main/java/org/testcontainers/containers/traits/LinkableContainer.java index b3593470674..76e71378969 100644 --- a/core/src/main/java/org/testcontainers/containers/traits/LinkableContainer.java +++ b/core/src/main/java/org/testcontainers/containers/traits/LinkableContainer.java @@ -2,7 +2,10 @@ /** * A container which can be linked to by other containers. + * + * @deprecated Links are deprecated (see #465). Please use {@link org.testcontainers.containers.Network} features instead. */ +@Deprecated public interface LinkableContainer { String getContainerName(); diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index 219eae9ec39..443ddde9591 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -229,7 +229,10 @@ private void stopAndRetainRecording(Description description) { * @param otherContainer the container rule to link to * @param alias the alias (hostname) that this other container should be referred to by * @return this + * + * @deprecated Links are deprecated (see #465). Please use {@link Network} features instead. */ + @Deprecated public SELF withLinkToContainer(LinkableContainer otherContainer, String alias) { addLink(otherContainer, alias); return self(); From aefdc9153271f417fd889cacf7809f1c00abf77b Mon Sep 17 00:00:00 2001 From: Vladislav Rassokhin Date: Mon, 13 Nov 2017 16:19:11 +0100 Subject: [PATCH 0181/5615] Add JetBrains as project user (#491) --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index 6343779a905..43834f3fbdf 100644 --- a/docs/index.md +++ b/docs/index.md @@ -24,6 +24,7 @@ TestContainers makes it easy to launch useful Docker containers for the duration * [Apache James](https://james.apache.org) - LDAP and Cassandra integration testing * [StreamSets](https://github.com/streamsets/datacollector) - LDAP, MySQL Vault, MongoDB, Redis integration testing * [Playtika](http://www.playtika.com) - Kafka, Couchbase, MariaDB, Redis, Neo4j, Aerospike, MemSQL + * [JetBrains](https://www.jetbrains.com/) - Testing of the TeamCity plugin for HashiCorp Vault ## License From 1f93f081206da4a99072e51431f8411be85b9cfe Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 19 Nov 2017 21:22:02 +0000 Subject: [PATCH 0182/5615] Update Visible Assertions dependency to 2.1.0 (#488) * Update Visible Assertions dependency to 2.1.0 to remove risk of dependency clashes with JNR-POSIX --- CHANGELOG.md | 1 + core/pom.xml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f211fc882b..068e0d33e12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. ### Changed - Make Network instances reusable (i.e. work with `@ClassRule`) ([\#469](https://github.com/testcontainers/testcontainers-java/issues/469)) - Added support for explicitly setting file mode when copying file into container ([\#446](https://github.com/testcontainers/testcontainers-java/issues/446), [\#467](https://github.com/testcontainers/testcontainers-java/issues/467)) +- Use Visible Assertions 2.1.0 for pre-flight test output (eliminating Jansi/JNR-POSIX dependencies for lower likelihood of conflict. JNA is now used internally by Visible Assertions instead). - Mark all links functionality as deprecated. This is pending removal in a later release. Please see [\#465](https://github.com/testcontainers/testcontainers-java/issues/465). {@link Network} features should be used instead. ## [1.4.3] - 2017-10-14 diff --git a/core/pom.xml b/core/pom.xml index de1b56d8cd5..00cc48d8e6d 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -92,7 +92,7 @@ org.rnorth.visible-assertions visible-assertions - 2.0.0 + 2.1.0 From be13df336406d65b7f6b7d911e965df20e5f3bbe Mon Sep 17 00:00:00 2001 From: Ihor Banadiga Date: Fri, 1 Sep 2017 15:12:21 +0300 Subject: [PATCH 0183/5615] Create temp files in a temp directory --- .../org/testcontainers/utility/MountableFile.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java index 9fb4e815a83..d990082a199 100644 --- a/core/src/main/java/org/testcontainers/utility/MountableFile.java +++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java @@ -205,7 +205,7 @@ private String getResourcePath() { * @return the path of the temporary file/directory */ private String extractClassPathResourceToTempLocation(final String hostPath) { - File tmpLocation = new File(".testcontainers-tmp-" + Base58.randomString(5)); + File tmpLocation = createTempDirectory(); //noinspection ResultOfMethodCallIgnored tmpLocation.delete(); @@ -236,6 +236,14 @@ private String extractClassPathResourceToTempLocation(final String hostPath) { return tmpLocation.getAbsolutePath(); } + private File createTempDirectory() { + try { + return Files.createTempDirectory(Paths.get(System.getProperty("java.io.tmpdir")), TESTCONTAINERS_TMP_DIR_PREFIX).toFile(); + } catch (IOException e) { + return new File(TESTCONTAINERS_TMP_DIR_PREFIX + Base58.randomString(5)); + } + } + @SuppressWarnings("ResultOfMethodCallIgnored") private void copyFromJarToLocation(final JarFile jarFile, final JarEntry entry, @@ -356,4 +364,4 @@ private int getModeValue(final Path path) { int result = Files.isDirectory(path) ? BASE_DIR_MODE : BASE_FILE_MODE; return result | this.forcedFileMode; } -} \ No newline at end of file +} From 48d8cb13d8da83c024dd359b38e3651ce81c989d Mon Sep 17 00:00:00 2001 From: Ihor Banadiga Date: Fri, 1 Sep 2017 15:44:25 +0300 Subject: [PATCH 0184/5615] CHANGELOG updated --- CHANGELOG.md | 5 ++++ .../testcontainers/utility/MountableFile.java | 24 +++++++++---------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 068e0d33e12..7215cb716c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. ## UNRELEASED ### Fixed - Fixed regression in 1.4.3 when using Docker Compose on Windows ([\#439](https://github.com/testcontainers/testcontainers-java/issues/439)) +- Fixed local Docker Compose executable name resolution on Windows (#416) +- Fixed TAR composition on Windows (#444) +- Allowing `addExposedPort` to be used after ports have been specified with `withExposedPorts` (#453) +- Stopping creation of temporary directory prior to creating temporary file (#443) +- Fixed temp files should be created in a temp directory (#423) ### Changed - Make Network instances reusable (i.e. work with `@ClassRule`) ([\#469](https://github.com/testcontainers/testcontainers-java/issues/469)) diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java index d990082a199..51fd0fd36f2 100644 --- a/core/src/main/java/org/testcontainers/utility/MountableFile.java +++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java @@ -1,15 +1,9 @@ package org.testcontainers.utility; -import com.google.common.base.Charsets; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.compress.archivers.tar.TarArchiveEntry; -import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; -import org.apache.commons.lang.SystemUtils; -import org.jetbrains.annotations.NotNull; -import org.testcontainers.images.builder.Transferable; +import static lombok.AccessLevel.PACKAGE; +import static org.testcontainers.utility.PathUtils.recursiveDeleteDir; +import com.google.common.base.Charsets; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -25,9 +19,14 @@ import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; - -import static lombok.AccessLevel.PACKAGE; -import static org.testcontainers.utility.PathUtils.recursiveDeleteDir; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; +import org.apache.commons.lang.SystemUtils; +import org.jetbrains.annotations.NotNull; +import org.testcontainers.images.builder.Transferable; /** * An abstraction over files and classpath resources aimed at encapsulating all the complexity of generating @@ -37,6 +36,7 @@ @Slf4j public class MountableFile implements Transferable { + private static final String TESTCONTAINERS_TMP_DIR_PREFIX = ".testcontainers-tmp-"; private static final int BASE_FILE_MODE = 0100000; private static final int BASE_DIR_MODE = 0040000; From 977572bf9101d32aa801d00922810568afb3dc05 Mon Sep 17 00:00:00 2001 From: Ihor Banadiga Date: Wed, 20 Sep 2017 20:16:24 +0300 Subject: [PATCH 0185/5615] Create tmp dir on MacOs. --- .../main/java/org/testcontainers/utility/MountableFile.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java index 51fd0fd36f2..6019eacae34 100644 --- a/core/src/main/java/org/testcontainers/utility/MountableFile.java +++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java @@ -37,6 +37,7 @@ public class MountableFile implements Transferable { private static final String TESTCONTAINERS_TMP_DIR_PREFIX = ".testcontainers-tmp-"; + public static final String OS_MAC_TMP_DIR = "/tmp"; private static final int BASE_FILE_MODE = 0100000; private static final int BASE_DIR_MODE = 0040000; @@ -238,7 +239,10 @@ private String extractClassPathResourceToTempLocation(final String hostPath) { private File createTempDirectory() { try { - return Files.createTempDirectory(Paths.get(System.getProperty("java.io.tmpdir")), TESTCONTAINERS_TMP_DIR_PREFIX).toFile(); + if (SystemUtils.IS_OS_MAC) { + return Files.createTempDirectory(Paths.get(OS_MAC_TMP_DIR), TESTCONTAINERS_TMP_DIR_PREFIX).toFile(); + } + return Files.createTempDirectory(TESTCONTAINERS_TMP_DIR_PREFIX).toFile(); } catch (IOException e) { return new File(TESTCONTAINERS_TMP_DIR_PREFIX + Base58.randomString(5)); } From 0da5f23ea9d591d0fa92526277455f8f2ed29e32 Mon Sep 17 00:00:00 2001 From: Ihor Banadiga Date: Thu, 21 Sep 2017 22:10:45 +0300 Subject: [PATCH 0186/5615] Update MountableFile.java --- .../src/main/java/org/testcontainers/utility/MountableFile.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java index 6019eacae34..ac07c0230f5 100644 --- a/core/src/main/java/org/testcontainers/utility/MountableFile.java +++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java @@ -37,7 +37,7 @@ public class MountableFile implements Transferable { private static final String TESTCONTAINERS_TMP_DIR_PREFIX = ".testcontainers-tmp-"; - public static final String OS_MAC_TMP_DIR = "/tmp"; + private static final String OS_MAC_TMP_DIR = "/tmp"; private static final int BASE_FILE_MODE = 0100000; private static final int BASE_DIR_MODE = 0040000; From 2570b2824ca9452c6005cae568b0ac17a57f7d7a Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 19 Nov 2017 22:00:30 +0000 Subject: [PATCH 0187/5615] Rephrase changelog message --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7215cb716c3..6e4d7b45053 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ All notable changes to this project will be documented in this file. - Fixed TAR composition on Windows (#444) - Allowing `addExposedPort` to be used after ports have been specified with `withExposedPorts` (#453) - Stopping creation of temporary directory prior to creating temporary file (#443) -- Fixed temp files should be created in a temp directory (#423) +- Ensure that temp files are created in a temp directory (#423) ### Changed - Make Network instances reusable (i.e. work with `@ClassRule`) ([\#469](https://github.com/testcontainers/testcontainers-java/issues/469)) From 0e898fc6a7f1e5a4b24ad91e3090ec402b8b427a Mon Sep 17 00:00:00 2001 From: Ivan Vasyliev Date: Sun, 26 Nov 2017 12:33:50 +0200 Subject: [PATCH 0188/5615] update playtika link to point to usage code (#501) --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 43834f3fbdf..15560b7a704 100644 --- a/docs/index.md +++ b/docs/index.md @@ -23,7 +23,7 @@ TestContainers makes it easy to launch useful Docker containers for the duration * [Apache Gora](https://gora.apache.org) - CouchDB testing * [Apache James](https://james.apache.org) - LDAP and Cassandra integration testing * [StreamSets](https://github.com/streamsets/datacollector) - LDAP, MySQL Vault, MongoDB, Redis integration testing - * [Playtika](http://www.playtika.com) - Kafka, Couchbase, MariaDB, Redis, Neo4j, Aerospike, MemSQL + * [Playtika](https://github.com/Playtika/testcontainers-spring-boot) - Kafka, Couchbase, MariaDB, Redis, Neo4j, Aerospike, MemSQL * [JetBrains](https://www.jetbrains.com/) - Testing of the TeamCity plugin for HashiCorp Vault ## License From 524b957b2137f032a2f4a1b8eb4c9b899f5bc4c7 Mon Sep 17 00:00:00 2001 From: lukidzi Date: Sun, 25 Jun 2017 20:30:07 +0200 Subject: [PATCH 0189/5615] Add methods which copy files to/from containers at run time Resolves #378 --- .../testcontainers/containers/Container.java | 22 ++++++++ .../containers/GenericContainer.java | 29 +++++++++++ .../junit/GenericContainerRuleTest.java | 52 +++++++++++++++++++ .../test/resources/test_copy_to_container.txt | 2 + 4 files changed, 105 insertions(+) create mode 100644 core/src/test/resources/test_copy_to_container.txt diff --git a/core/src/main/java/org/testcontainers/containers/Container.java b/core/src/main/java/org/testcontainers/containers/Container.java index 880867401e8..2d5158e1833 100644 --- a/core/src/main/java/org/testcontainers/containers/Container.java +++ b/core/src/main/java/org/testcontainers/containers/Container.java @@ -10,6 +10,7 @@ import org.testcontainers.containers.traits.LinkableContainer; import org.testcontainers.containers.wait.Wait; import org.testcontainers.containers.wait.WaitStrategy; +import org.testcontainers.utility.MountableFile; import java.io.IOException; import java.nio.charset.Charset; @@ -408,6 +409,27 @@ ExecResult execInContainer(String... command) ExecResult execInContainer(Charset outputCharset, String... command) throws UnsupportedOperationException, IOException, InterruptedException; + /** + * + * Copies a file which resides inside the classpath to the container. + * + * @param mountableLocalFile file which is copied into the container + * @param containerPath destination path inside the container + * @throws IOException if there's an issue communicating with Docker + * @throws InterruptedException if the thread waiting for the response is interrupted + */ + void copyFileToContainer(MountableFile mountableLocalFile, String containerPath) throws IOException, InterruptedException; + + /** + * Copies a file which resides inside the container to user defined directory + * + * @param containerPath path to file which is copied from container + * @param destinationPath destination path to which file is copied with file name + * @throws IOException if there's an issue communicating with Docker or receiving entry from TarArchiveInputStream + * @throws InterruptedException if the thread waiting for the response is interrupted + */ + void copyFileFromContainer(String containerPath, String destinationPath) throws IOException, InterruptedException; + List getExposedPorts(); List getPortBindings(); diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 4932d12b7b6..36899533d14 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -9,6 +9,8 @@ import com.google.common.base.Preconditions; import com.google.common.base.Strings; import lombok.*; +import org.apache.commons.compress.utils.IOUtils; +import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.jetbrains.annotations.Nullable; import org.junit.runner.Description; import org.rnorth.ducttape.ratelimits.RateLimiter; @@ -31,6 +33,7 @@ import org.testcontainers.utility.*; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Path; @@ -858,6 +861,32 @@ public ExecResult execInContainer(String... command) return execInContainer(UTF8, command); } + /** + * {@inheritDoc} + */ + @Override + public void copyFileToContainer(MountableFile mountableLocalFile, String containerPath) throws IOException, InterruptedException { + + this.dockerClient + .copyArchiveToContainerCmd(this.containerId) + .withHostResource(mountableLocalFile.getResolvedPath()) + .withRemotePath(containerPath) + .exec(); + } + + /** + * {@inheritDoc} + */ + @Override + public void copyFileFromContainer(String containerPath, String destinationPath) throws IOException, InterruptedException { + try (final TarArchiveInputStream tarInputStream = new TarArchiveInputStream(this.dockerClient + .copyArchiveFromContainerCmd(this.containerId, containerPath) + .exec())) { + tarInputStream.getNextTarEntry(); + IOUtils.copy(tarInputStream, new FileOutputStream(destinationPath)); + } + } + /** * {@inheritDoc} */ diff --git a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java index 230e8241f0d..61dfea7d9d8 100644 --- a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java +++ b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java @@ -1,17 +1,21 @@ package org.testcontainers.junit; +import com.github.dockerjava.api.exception.NotFoundException; import com.google.common.collect.ImmutableMap; +import com.google.common.io.Files; import com.google.common.util.concurrent.Uninterruptibles; import com.mongodb.MongoClient; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; import com.rabbitmq.client.*; +import org.apache.commons.io.FileUtils; import org.bson.Document; import org.junit.*; import org.rnorth.ducttape.RetryCountExceededException; import org.rnorth.ducttape.unreliables.Unreliables; import org.testcontainers.containers.GenericContainer; import org.testcontainers.utility.Base58; +import org.testcontainers.utility.MountableFile; import org.testcontainers.utility.TestEnvironment; import java.io.*; @@ -330,6 +334,54 @@ public void createContainerCmdHookTest() { } } + @Test + public void copyToContainerTest() throws Exception { + final File tempResultFolder = Files.createTempDir(); + + try (final GenericContainer alpineCopyToContainer = new GenericContainer("alpine:3.2") + .withCommand("top")){ + + alpineCopyToContainer.start(); + final MountableFile mountableFile = MountableFile.forClasspathResource("test_copy_to_container.txt"); + alpineCopyToContainer.copyFileToContainer(mountableFile, "/home/"); + alpineCopyToContainer.copyFileFromContainer("/home/test_copy_to_container.txt", + tempResultFolder.getAbsolutePath() + "/test_copy_to_container.txt"); + + File expectedFile = new File(mountableFile.getResolvedPath()); + File actualFile = new File(tempResultFolder.getAbsolutePath() + "/test_copy_to_container.txt"); + assertTrue("Files aren't same ", FileUtils.contentEquals(expectedFile,actualFile)); + } + } + + @Test(expected = NotFoundException.class) + public void copyFromContainerShouldFailBecauseNoFileTest() throws NotFoundException, IOException, InterruptedException { + + try (final GenericContainer alpineCopyToContainer = new GenericContainer("alpine:3.2") + .withCommand("top")) { + alpineCopyToContainer.start(); + alpineCopyToContainer.copyFileFromContainer("/home/test.txt", "src/test/resources/copy-from/test.txt"); + } + } + + @Test + public void shouldCopyFileFromContainerTest() throws IOException, InterruptedException { + final File tempResultFolder = Files.createTempDir(); + + try (final GenericContainer alpineCopyToContainer = new GenericContainer("alpine:3.2") + .withCommand("top")) { + + alpineCopyToContainer.start(); + final MountableFile mountableFile = MountableFile.forClasspathResource("test_copy_to_container.txt"); + alpineCopyToContainer.copyFileToContainer(mountableFile, "/home/"); + alpineCopyToContainer.copyFileFromContainer("/home/test_copy_to_container.txt", + tempResultFolder.getAbsolutePath() + "/test_copy_from_container.txt"); + + File expectedFile = new File(mountableFile.getResolvedPath()); + File actualFile = new File(tempResultFolder.getAbsolutePath() + "/test_copy_from_container.txt"); + assertTrue("Files aren't same ", FileUtils.contentEquals(expectedFile,actualFile)); + } + } + private BufferedReader getReaderForContainerPort80(GenericContainer container) { return Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, () -> { diff --git a/core/src/test/resources/test_copy_to_container.txt b/core/src/test/resources/test_copy_to_container.txt new file mode 100644 index 00000000000..2c377842030 --- /dev/null +++ b/core/src/test/resources/test_copy_to_container.txt @@ -0,0 +1,2 @@ +Some Test +Message \ No newline at end of file From c4ae20f15eba62b7acb7e9b93412ce01031ac46e Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 26 Nov 2017 11:34:03 +0000 Subject: [PATCH 0190/5615] Add checks that the container is running before copy to/from actions --- .../containers/GenericContainer.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 36899533d14..f9d96e85ef9 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -865,7 +865,11 @@ public ExecResult execInContainer(String... command) * {@inheritDoc} */ @Override - public void copyFileToContainer(MountableFile mountableLocalFile, String containerPath) throws IOException, InterruptedException { + public void copyFileToContainer(MountableFile mountableLocalFile, String containerPath) { + + if (!isRunning()) { + throw new IllegalStateException("copyFileToContainer can only be used while the Container is running"); + } this.dockerClient .copyArchiveToContainerCmd(this.containerId) @@ -878,7 +882,12 @@ public void copyFileToContainer(MountableFile mountableLocalFile, String contain * {@inheritDoc} */ @Override - public void copyFileFromContainer(String containerPath, String destinationPath) throws IOException, InterruptedException { + public void copyFileFromContainer(String containerPath, String destinationPath) throws IOException { + + if (!isRunning()) { + throw new IllegalStateException("copyFileToContainer can only be used while the Container is running"); + } + try (final TarArchiveInputStream tarInputStream = new TarArchiveInputStream(this.dockerClient .copyArchiveFromContainerCmd(this.containerId, containerPath) .exec())) { @@ -902,7 +911,7 @@ public ExecResult execInContainer(Charset outputCharset, String... command) } if (!isRunning()) { - throw new IllegalStateException("Container is not running so exec cannot be run"); + throw new IllegalStateException("execInContainer can only be used while the Container is running"); } this.dockerClient From 18fa16a1a0646794f6244d00b0b21b25d72cd50d Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 26 Nov 2017 11:45:30 +0000 Subject: [PATCH 0191/5615] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e4d7b45053..01b6a253ec8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ All notable changes to this project will be documented in this file. - Added support for explicitly setting file mode when copying file into container ([\#446](https://github.com/testcontainers/testcontainers-java/issues/446), [\#467](https://github.com/testcontainers/testcontainers-java/issues/467)) - Use Visible Assertions 2.1.0 for pre-flight test output (eliminating Jansi/JNR-POSIX dependencies for lower likelihood of conflict. JNA is now used internally by Visible Assertions instead). - Mark all links functionality as deprecated. This is pending removal in a later release. Please see [\#465](https://github.com/testcontainers/testcontainers-java/issues/465). {@link Network} features should be used instead. +- Added support for copying files to/from running containers ([\#378](https://github.com/testcontainers/testcontainers-java/issues/378)) ## [1.4.3] - 2017-10-14 ### Fixed From b7e7d52b985c85a0b27b6b9ec27c80caabfaad89 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 26 Nov 2017 20:57:05 +0000 Subject: [PATCH 0192/5615] Fix #493: Use null to indicate no overriding file mode instead of `-1` (#503) Eliminate use of a `0` default parameter that was introduced by mistake --- .../images/builder/traits/FilesTrait.java | 8 ++++---- .../testcontainers/utility/MountableFile.java | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/org/testcontainers/images/builder/traits/FilesTrait.java b/core/src/main/java/org/testcontainers/images/builder/traits/FilesTrait.java index 26ef980c25b..61524b59f8a 100644 --- a/core/src/main/java/org/testcontainers/images/builder/traits/FilesTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/traits/FilesTrait.java @@ -18,7 +18,7 @@ public interface FilesTrait & BuildContextBuilderT * @return self */ default SELF withFileFromFile(String path, File file) { - return withFileFromPath(path, file.toPath(), 0); + return withFileFromPath(path, file.toPath(), null); } /** @@ -28,7 +28,7 @@ default SELF withFileFromFile(String path, File file) { * @return self */ default SELF withFileFromPath(String path, Path filePath) { - return withFileFromPath(path, filePath, 0); + return withFileFromPath(path, filePath, null); } /** @@ -38,7 +38,7 @@ default SELF withFileFromPath(String path, Path filePath) { * @param mode octal value of posix file mode (000..777) * @return self */ - default SELF withFileFromFile(String path, File file, int mode) { + default SELF withFileFromFile(String path, File file, Integer mode) { return withFileFromPath(path, file.toPath(), mode); } @@ -49,7 +49,7 @@ default SELF withFileFromFile(String path, File file, int mode) { * @param mode octal value of posix file mode (000..777) * @return self */ - default SELF withFileFromPath(String path, Path filePath, int mode) { + default SELF withFileFromPath(String path, Path filePath, Integer mode) { final MountableFile mountableFile = MountableFile.forHostPath(filePath, mode); return ((SELF) this).withFileFromTransferable(path, mountableFile); } diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java index ac07c0230f5..f92b8b4e887 100644 --- a/core/src/main/java/org/testcontainers/utility/MountableFile.java +++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java @@ -42,7 +42,7 @@ public class MountableFile implements Transferable { private static final int BASE_DIR_MODE = 0040000; private final String path; - private final int forcedFileMode; + private final Integer forcedFileMode; @Getter(lazy = true) private final String resolvedPath = resolvePath(); @@ -59,7 +59,7 @@ public class MountableFile implements Transferable { * @return a {@link MountableFile} that may be used to obtain a mountable path */ public static MountableFile forClasspathResource(@NotNull final String resourceName) { - return forClasspathResource(resourceName, -1); + return forClasspathResource(resourceName, null); } /** @@ -69,7 +69,7 @@ public static MountableFile forClasspathResource(@NotNull final String resourceN * @return a {@link MountableFile} that may be used to obtain a mountable path */ public static MountableFile forHostPath(@NotNull final String path) { - return forHostPath(path, -1); + return forHostPath(path, null); } /** @@ -79,7 +79,7 @@ public static MountableFile forHostPath(@NotNull final String path) { * @return a {@link MountableFile} that may be used to obtain a mountable path */ public static MountableFile forHostPath(final Path path) { - return forHostPath(path, -1); + return forHostPath(path, null); } /** @@ -89,7 +89,7 @@ public static MountableFile forHostPath(final Path path) { * @param mode octal value of posix file mode (000..777) * @return a {@link MountableFile} that may be used to obtain a mountable path */ - public static MountableFile forClasspathResource(@NotNull final String resourceName, int mode) { + public static MountableFile forClasspathResource(@NotNull final String resourceName, Integer mode) { return new MountableFile(getClasspathResource(resourceName, new HashSet<>()).toString(), mode); } @@ -100,7 +100,7 @@ public static MountableFile forClasspathResource(@NotNull final String resourceN * @param mode octal value of posix file mode (000..777) * @return a {@link MountableFile} that may be used to obtain a mountable path */ - public static MountableFile forHostPath(@NotNull final String path, int mode) { + public static MountableFile forHostPath(@NotNull final String path, Integer mode) { return new MountableFile(new File(path).toURI().toString(), mode); } @@ -111,7 +111,7 @@ public static MountableFile forHostPath(@NotNull final String path, int mode) { * @param mode octal value of posix file mode (000..777) * @return a {@link MountableFile} that may be used to obtain a mountable path */ - public static MountableFile forHostPath(final Path path, int mode) { + public static MountableFile forHostPath(final Path path, Integer mode) { return new MountableFile(path.toAbsolutePath().toString(), mode); } @@ -345,7 +345,7 @@ public int getFileMode() { private int getUnixFileMode(final String pathAsString) { final Path path = Paths.get(pathAsString); - if (this.forcedFileMode > -1) { + if (this.forcedFileMode != null) { return this.getModeValue(path); } From ad68a836f31d3118a7fa6a15441aa12e1f24864b Mon Sep 17 00:00:00 2001 From: Nikita Salnikov-Tarnovski Date: Sat, 2 Dec 2017 08:13:18 +0200 Subject: [PATCH 0193/5615] Added Plumbr to "Who is using Testcontainers" (#510) --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index 15560b7a704..b5797d6487e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -25,6 +25,7 @@ TestContainers makes it easy to launch useful Docker containers for the duration * [StreamSets](https://github.com/streamsets/datacollector) - LDAP, MySQL Vault, MongoDB, Redis integration testing * [Playtika](https://github.com/Playtika/testcontainers-spring-boot) - Kafka, Couchbase, MariaDB, Redis, Neo4j, Aerospike, MemSQL * [JetBrains](https://www.jetbrains.com/) - Testing of the TeamCity plugin for HashiCorp Vault + * [Plumbr](https://plumbr.io) - Integration testing of data processing pipeline micro-services ## License From 9f1486e329db44b0e7d7e5a9b0dbd1e1b03de167 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 3 Dec 2017 07:16:49 +0000 Subject: [PATCH 0194/5615] Fix random test failure by removing dependency on Alpine package server. (#505) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix random test failure by removing dependency on Alpine package server. This fixes an occasional random failure during docker image build, seemingly due to a failure to download alpine package indices: ``` ERROR: http://dl-cdn.alpinelinux.org/alpine/v3.3/main: temporary error (try again later) ... ERROR 🐳 [testcontainers/cbwpthsp79aee52s] - The command '/bin/sh -c apk add --update nginx' returned a non-zero code: 1 ``` --- .../ParameterizedDockerfileContainerTest.java | 57 ++++++++----------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java b/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java index b0790345deb..4ea261e68fd 100644 --- a/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java @@ -1,21 +1,12 @@ package org.testcontainers.junit; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import org.rnorth.ducttape.unreliables.Unreliables; import org.testcontainers.containers.GenericContainer; import org.testcontainers.images.builder.ImageFromDockerfile; -import java.io.IOException; -import java.util.concurrent.TimeUnit; - -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; /** @@ -25,40 +16,38 @@ @RunWith(Parameterized.class) public class ParameterizedDockerfileContainerTest { - @Parameterized.Parameters(name = "{0}") - public static Object[] data() { - return new Object[] { "alpine:3.2", "alpine:3.3" }; - } + private final String expectedVersion; + + @Rule + public GenericContainer container; - public ParameterizedDockerfileContainerTest(String baseImage) { + public ParameterizedDockerfileContainerTest(String baseImage, String expectedVersion) { container = new GenericContainer(new ImageFromDockerfile().withDockerfileFromBuilder(builder -> { builder .from(baseImage) - .run("apk add --update nginx") - .cmd("nginx", "-g", "daemon off;") + // Could potentially customise the image here, e.g. adding files, running + // commands, etc. .build(); - })).withExposedPorts(80); + })).withCommand("top"); + this.expectedVersion = expectedVersion; } - @Rule - public GenericContainer container; + @Parameterized.Parameters(name = "{0}") + public static Object[][] data() { + return new Object[][] { + { "alpine:3.2", "3.2"}, + { "alpine:3.3", "3.3"}, + { "alpine:3.4", "3.4"}, + { "alpine:3.5", "3.5"}, + { "alpine:3.6", "3.6"} + }; + } @Test - public void simpleTest() throws IOException { - String address = String.format("http://%s:%s", container.getContainerIpAddress(), container.getMappedPort(80)); - - CloseableHttpClient httpClient = HttpClientBuilder.create().build(); - HttpGet get = new HttpGet(address); + public void simpleTest() throws Exception { + final String release = container.execInContainer("cat", "/etc/alpine-release").getStdout(); - Unreliables.retryUntilSuccess(5, TimeUnit.SECONDS, () -> { - try (CloseableHttpResponse response = httpClient.execute(get)) { - assertEquals("A container built from a dockerfile can run nginx as expected, and returns a good status code", - 200, - response.getStatusLine().getStatusCode()); - assertTrue("A container built from a dockerfile can run nginx as expected, and returns an expected Server header", - response.getHeaders("Server")[0].getValue().contains("nginx")); - } - return true; - }); + assertTrue("/etc/alpine-release starts with " + expectedVersion, + release.startsWith(expectedVersion)); } } From 6620d23de67e839fc98d1d6b37aa7791af9c1bf5 Mon Sep 17 00:00:00 2001 From: ldeck Date: Sat, 9 Dec 2017 19:38:03 +1100 Subject: [PATCH 0195/5615] Add RecordingFileFactory with default implementation (#507) Add RecordingFileFactory with default implementation By default this returns a file in the given vlc recording directory named '---.flv', where RESULT is either 'PASSED' or 'FAILED'. A custom factory can be provided to BrowserWebDriverContainer#withRecordingFileFactory. The factory is only applicable if the BrowserWebDriverContainer#recordingMode enables the retention of recordings. Fixes #500. --- CHANGELOG.md | 1 + docs/usage/webdriver_containers.md | 9 +++ .../containers/BrowserWebDriverContainer.java | 20 +++---- .../DefaultRecordingFileFactory.java | 27 +++++++++ .../containers/RecordingFileFactory.java | 9 +++ .../DefaultRecordingFileFactoryTest.java | 60 +++++++++++++++++++ ...ChromeRecordingWebDriverContainerTest.java | 4 +- 7 files changed, 119 insertions(+), 11 deletions(-) create mode 100644 modules/selenium/src/main/java/org/testcontainers/containers/DefaultRecordingFileFactory.java create mode 100644 modules/selenium/src/main/java/org/testcontainers/containers/RecordingFileFactory.java create mode 100644 modules/selenium/src/test/java/org/testcontainers/containers/DefaultRecordingFileFactoryTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 01b6a253ec8..795c7fb1ee7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ All notable changes to this project will be documented in this file. - Use Visible Assertions 2.1.0 for pre-flight test output (eliminating Jansi/JNR-POSIX dependencies for lower likelihood of conflict. JNA is now used internally by Visible Assertions instead). - Mark all links functionality as deprecated. This is pending removal in a later release. Please see [\#465](https://github.com/testcontainers/testcontainers-java/issues/465). {@link Network} features should be used instead. - Added support for copying files to/from running containers ([\#378](https://github.com/testcontainers/testcontainers-java/issues/378)) +- Added support for customising the recording file name ([\#500](https://github.com/testcontainers/testcontainers-java/issues/500)) ## [1.4.3] - 2017-10-14 ### Fixed diff --git a/docs/usage/webdriver_containers.md b/docs/usage/webdriver_containers.md index 621324c7ee1..246b1943373 100644 --- a/docs/usage/webdriver_containers.md +++ b/docs/usage/webdriver_containers.md @@ -80,6 +80,15 @@ new BrowserWebDriverContainer() ``` Note that the seconds parameter to `withRecordingMode` should be a directory where recordings can be saved. +If you would like to customise the file name of the recording, or provide a different directory at runtime based on the description of the test and/or its success or failure, you may provide a custom recording file factory as follows: +```java +new BrowserWebDriverContainer() + //... + .withRecordingFileFactory(new CustomRecordingFileFactory()) +``` + +Note the factory must implement `org.testcontainers.containers.RecordingFileFactory`. + ## More examples A few different examples are shown in [ChromeWebDriverContainerTest.java](https://github.com/testcontainers/testcontainers-java/blob/master/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java). \ No newline at end of file diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index 443ddde9591..81c84786f6d 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -17,11 +17,9 @@ import java.io.File; import java.net.MalformedURLException; import java.net.URL; -import java.text.SimpleDateFormat; import java.time.Duration; import java.util.ArrayList; import java.util.Collection; -import java.util.Date; import java.util.concurrent.TimeUnit; import static java.time.temporal.ChronoUnit.SECONDS; @@ -47,12 +45,12 @@ public class BrowserWebDriverContainer currentVncRecordings = new ArrayList<>(); private static final Logger LOGGER = LoggerFactory.getLogger(BrowserWebDriverContainer.class); - private static final SimpleDateFormat filenameDateFormat = new SimpleDateFormat("YYYYMMdd-HHmmss"); /** */ @@ -60,6 +58,7 @@ public BrowserWebDriverContainer() { this.waitStrategy = new LogMessageWaitStrategy() .withRegEx(".*(RemoteWebDriver instances should connect to|Selenium Server is up and running).*\n") .withStartupTimeout(Duration.of(15, SECONDS)); + this.withRecordingFileFactory(new DefaultRecordingFileFactory()); } /** @@ -183,11 +182,10 @@ public RemoteWebDriver getWebDriver() { @Override protected void failed(Throwable e, Description description) { - switch (recordingMode) { case RECORD_FAILING: case RECORD_ALL: - stopAndRetainRecording(description); + stopAndRetainRecordingForDescriptionAndSuccessState(description, false); break; } currentVncRecordings.clear(); @@ -195,10 +193,9 @@ protected void failed(Throwable e, Description description) { @Override protected void succeeded(Description description) { - switch (recordingMode) { case RECORD_ALL: - stopAndRetainRecording(description); + stopAndRetainRecordingForDescriptionAndSuccessState(description, true); break; } currentVncRecordings.clear(); @@ -212,9 +209,8 @@ protected void finished(Description description) { this.stop(); } - private void stopAndRetainRecording(Description description) { - File recordingFile = new File(vncRecordingDirectory, "recording-" + filenameDateFormat.format(new Date()) + ".flv"); - + private void stopAndRetainRecordingForDescriptionAndSuccessState(Description description, boolean succeeded) { + File recordingFile = recordingFileFactory.recordingFileForTest(vncRecordingDirectory, description, succeeded); LOGGER.info("Screen recordings for test {} will be stored at: {}", description.getDisplayName(), recordingFile); for (VncRecordingSidekickContainer container : currentVncRecordings) { @@ -244,6 +240,10 @@ public SELF withRecordingMode(VncRecordingMode recordingMode, File vncRecordingD return self(); } + public SELF withRecordingFileFactory(RecordingFileFactory recordingFileFactory) { + this.recordingFileFactory = recordingFileFactory; + return self(); + } public enum VncRecordingMode { SKIP, RECORD_ALL, RECORD_FAILING diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/DefaultRecordingFileFactory.java b/modules/selenium/src/main/java/org/testcontainers/containers/DefaultRecordingFileFactory.java new file mode 100644 index 00000000000..b68b03d46a9 --- /dev/null +++ b/modules/selenium/src/main/java/org/testcontainers/containers/DefaultRecordingFileFactory.java @@ -0,0 +1,27 @@ +package org.testcontainers.containers; + +import org.junit.runner.Description; + +import java.io.File; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class DefaultRecordingFileFactory implements RecordingFileFactory { + + private static final SimpleDateFormat filenameDateFormat = new SimpleDateFormat("YYYYMMdd-HHmmss"); + private static final String PASSED = "PASSED"; + private static final String FAILED = "FAILED"; + private static final String FILENAME_FORMAT = "%s-%s-%s-%s.flv"; + + @Override + public File recordingFileForTest(File vncRecordingDirectory, Description description, boolean succeeded) { + final String prefix = succeeded ? PASSED : FAILED; + final String fileName = String.format(FILENAME_FORMAT, + prefix, + description.getTestClass().getSimpleName(), + description.getMethodName(), + filenameDateFormat.format(new Date()) + ); + return new File(vncRecordingDirectory, fileName); + } +} diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/RecordingFileFactory.java b/modules/selenium/src/main/java/org/testcontainers/containers/RecordingFileFactory.java new file mode 100644 index 00000000000..cbd91562301 --- /dev/null +++ b/modules/selenium/src/main/java/org/testcontainers/containers/RecordingFileFactory.java @@ -0,0 +1,9 @@ +package org.testcontainers.containers; + +import org.junit.runner.Description; + +import java.io.File; + +public interface RecordingFileFactory { + File recordingFileForTest(File vncRecordingDirectory, Description description, boolean succeeded); +} diff --git a/modules/selenium/src/test/java/org/testcontainers/containers/DefaultRecordingFileFactoryTest.java b/modules/selenium/src/test/java/org/testcontainers/containers/DefaultRecordingFileFactoryTest.java new file mode 100644 index 00000000000..ef3bd50772e --- /dev/null +++ b/modules/selenium/src/test/java/org/testcontainers/containers/DefaultRecordingFileFactoryTest.java @@ -0,0 +1,60 @@ +package org.testcontainers.containers; + +import lombok.Value; +import org.junit.Test; +import org.junit.runner.Description; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.File; +import java.nio.file.Files; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import static java.lang.Boolean.FALSE; +import static java.lang.Boolean.TRUE; +import static java.lang.String.format; +import static java.time.LocalDateTime.now; +import static org.hamcrest.core.IsCollectionContaining.hasItem; +import static org.junit.Assert.assertThat; +import static org.junit.runner.Description.createTestDescription; + +@RunWith(Parameterized.class) +@Value +public class DefaultRecordingFileFactoryTest { + + private static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("YYYYMMdd-HHmmss"); + + private final DefaultRecordingFileFactory factory = new DefaultRecordingFileFactory(); + private final String methodName; + private final String prefix; + private final boolean success; + + @Parameterized.Parameters + public static Collection data() { + Collection args = new ArrayList<>(); + args.add(new Object[]{"testMethod1", "FAILED", FALSE}); + args.add(new Object[]{"testMethod2", "PASSED", TRUE}); + return args; + } + + @Test + public void recordingFileThatShouldDescribeTheTestResultAtThePresentTime() throws Exception { + File vncRecordingDirectory = Files.createTempDirectory("recording").toFile(); + Description description = createTestDescription(getClass().getCanonicalName(), methodName, Test.class); + + File recordingFile = factory.recordingFileForTest(vncRecordingDirectory, description, success); + + String expectedFilePrefix = format("%s-%s-%s", prefix, getClass().getSimpleName(), methodName); + + List expectedPossibleFileNames = Arrays.asList( + new File(vncRecordingDirectory, format("%s-%s.flv", expectedFilePrefix, now().format(DATETIME_FORMATTER))), + new File(vncRecordingDirectory, format("%s-%s.flv", expectedFilePrefix, now().minusSeconds(1L).format(DATETIME_FORMATTER))) + ); + + assertThat(expectedPossibleFileNames, hasItem(recordingFile)); + } +} \ No newline at end of file diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java index 5698b083339..6eba1ae478d 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java @@ -4,6 +4,7 @@ import org.junit.Test; import org.openqa.selenium.remote.DesiredCapabilities; import org.testcontainers.containers.BrowserWebDriverContainer; +import org.testcontainers.containers.DefaultRecordingFileFactory; import java.io.File; @@ -17,7 +18,8 @@ public class ChromeRecordingWebDriverContainerTest extends BaseWebDriverContaine @Rule public BrowserWebDriverContainer chromeThatRecordsAllTests = new BrowserWebDriverContainer() .withDesiredCapabilities(DesiredCapabilities.chrome()) - .withRecordingMode(RECORD_ALL, new File("./target/")); + .withRecordingMode(RECORD_ALL, new File("./target/")) + .withRecordingFileFactory(new DefaultRecordingFileFactory()); @Rule public BrowserWebDriverContainer chromeThatRecordsFailingTests = new BrowserWebDriverContainer() From f1ce2b139233da7e6932778944f376ef6e6e7743 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 10 Dec 2017 08:28:36 +0000 Subject: [PATCH 0196/5615] Wait robustness (#504) Refactor port awaits for improved robustness: * We now allow a list of multiple startup liveness check ports to be defined for a container. This helps, e.g. for the browser containers, and lets us wait for both Selenium and VNC ports to be listening. I think this will help eliminate random flapping tests in this area and should fix #466 * For exposed/bound ports, we now check that the port is accepting connections both (1) from within the container, and (2) from the testcontainers host. Previously, the internal check was done for Docker on Mac/Win and the external check for Linux. Now both are used for all environments, which simplifies the logic and gives a solid guarantee that the port truly is listening. (For reference, the internal check was done due to issues with the Docker networking stack opening a listening socket before the containerized service itself was listening). * Also, we now have a WaitAllStrategy that lets more than one wait strategy be used. For browser containers, we now wait for (a) a log message, and (b) the listening ports to be available. * Broken out some aspects of the wait strategies/port detection into separate classes and used this to help improve test coverage. * Refactored/tidied some code in network link configuration that was redundant, using Docker API filters instead of streamed filtering of containers --- CHANGELOG.md | 4 + .../containers/GenericContainer.java | 91 +++++++++++++++---- .../VncRecordingSidekickContainer.java | 9 +- .../containers/wait/HostPortWaitStrategy.java | 90 +++++------------- .../containers/wait/WaitAllStrategy.java | 38 ++++++++ .../internal/ExternalPortListeningCheck.java | 32 +++++++ .../InternalCommandPortListeningCheck.java | 49 ++++++++++ .../containers/wait/WaitAllStrategyTest.java | 69 ++++++++++++++ .../ExternalPortListeningCheckTest.java | 71 +++++++++++++++ ...InternalCommandPortListeningCheckTest.java | 33 +++++++ .../containers/JdbcDatabaseContainer.java | 3 - .../containers/MySQLContainer.java | 10 +- .../containers/NginxContainer.java | 8 +- .../containers/PostgreSQLContainer.java | 8 +- .../containers/BrowserWebDriverContainer.java | 23 +++-- .../junit/FirefoxWebDriverContainerTest.java | 6 +- 16 files changed, 434 insertions(+), 110 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/containers/wait/WaitAllStrategy.java create mode 100644 core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java create mode 100644 core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java create mode 100644 core/src/test/java/org/testcontainers/containers/wait/WaitAllStrategyTest.java create mode 100644 core/src/test/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheckTest.java create mode 100644 core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 795c7fb1ee7..9a0d764da7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ All notable changes to this project will be documented in this file. - Allowing `addExposedPort` to be used after ports have been specified with `withExposedPorts` (#453) - Stopping creation of temporary directory prior to creating temporary file (#443) - Ensure that temp files are created in a temp directory (#423) +- Added `WaitAllStrategy` as a mechanism for composing multiple startup `WaitStrategy` objects together +- Changed `BrowserWebDriverContainer` to use improved wait strategies, to eliminate race conditions when starting VNC recording containers. This should lead to far fewer 'error' messages logged when starting up selenium containers, and less exposure to race related bugs (fixes #466). ### Changed - Make Network instances reusable (i.e. work with `@ClassRule`) ([\#469](https://github.com/testcontainers/testcontainers-java/issues/469)) @@ -16,6 +18,8 @@ All notable changes to this project will be documented in this file. - Use Visible Assertions 2.1.0 for pre-flight test output (eliminating Jansi/JNR-POSIX dependencies for lower likelihood of conflict. JNA is now used internally by Visible Assertions instead). - Mark all links functionality as deprecated. This is pending removal in a later release. Please see [\#465](https://github.com/testcontainers/testcontainers-java/issues/465). {@link Network} features should be used instead. - Added support for copying files to/from running containers ([\#378](https://github.com/testcontainers/testcontainers-java/issues/378)) +- Add `getLivenessCheckPorts` as an eventual replacement for `getLivenessCheckPort`; this allows multiple ports to be included in post-startup wait strategies. +- Refactor wait strategy port checking and improve test coverage. - Added support for customising the recording file name ([\#500](https://github.com/testcontainers/testcontainers-java/issues/500)) ## [1.4.3] - 2017-10-14 diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index f9d96e85ef9..d8ebb36ec93 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -9,8 +9,9 @@ import com.google.common.base.Preconditions; import com.google.common.base.Strings; import lombok.*; -import org.apache.commons.compress.utils.IOUtils; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; +import org.apache.commons.compress.utils.IOUtils; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.junit.runner.Description; import org.rnorth.ducttape.ratelimits.RateLimiter; @@ -44,6 +45,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.stream.Collectors; +import java.util.stream.Stream; import static com.google.common.collect.Lists.newArrayList; import static org.testcontainers.containers.output.OutputFrame.OutputType.STDERR; @@ -96,7 +98,6 @@ public class GenericContainer> @NonNull private List binds = new ArrayList<>(); - @NonNull private boolean privilegedMode; @NonNull @@ -326,8 +327,11 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { /** * @return the port on which to check if the container is ready + * @deprecated see {@link GenericContainer#getLivenessCheckPorts()} for replacement */ + @Deprecated protected Integer getLivenessCheckPort() { + // legacy implementation for backwards compatibility if (exposedPorts.size() > 0) { return getMappedPort(exposedPorts.get(0)); } else if (portBindings.size() > 0) { @@ -337,6 +341,39 @@ protected Integer getLivenessCheckPort() { } } + /** + * @return the ports on which to check if the container is ready + */ + @NotNull + @NonNull + protected Set getLivenessCheckPorts() { + final Set result = new HashSet<>(); + result.addAll(getExposedPortNumbers()); + result.addAll(getBoundPortNumbers()); + + // for backwards compatibility + if (this.getLivenessCheckPort() != null) { + result.add(this.getLivenessCheckPort()); + } + + return result; + } + + private List getExposedPortNumbers() { + return exposedPorts.stream() + .map(this::getMappedPort) + .collect(Collectors.toList()); + } + + private List getBoundPortNumbers() { + return portBindings.stream() + .map(PortBinding::parse) + .map(PortBinding::getBinding) + .map(Ports.Binding::getHostPortSpec) + .map(Integer::valueOf) + .collect(Collectors.toList()); + } + private void applyConfiguration(CreateContainerCmd createCommand) { // Set up exposed ports (where there are no host port bindings defined) @@ -376,31 +413,16 @@ private void applyConfiguration(CreateContainerCmd createCommand) { String alias = linkEntries.getKey(); LinkableContainer linkableContainer = linkEntries.getValue(); - Set links = dockerClient.listContainersCmd().exec().stream() - .filter(container -> container.getNames()[0].endsWith(linkableContainer.getContainerName())) - .map(container -> new Link(container.getNames()[0], alias)) - .collect(Collectors.toSet()); + Set links = findLinksFromThisContainer(alias, linkableContainer); allLinks.addAll(links); - boolean linkableContainerIsRunning = dockerClient.listContainersCmd().exec().stream() - .filter(container -> container.getNames()[0].endsWith(linkableContainer.getContainerName())) - .map(com.github.dockerjava.api.model.Container::getId) - .map(id -> dockerClient.inspectContainerCmd(id).exec()) - .anyMatch(linkableContainerInspectResponse -> linkableContainerInspectResponse.getState().getRunning()); - - if (!linkableContainerIsRunning) { + if (allLinks.size() == 0) { throw new ContainerLaunchException("Aborting attempt to link to container " + linkableContainer.getContainerName() + " as it is not running"); } - Set linkedContainerNetworks = dockerClient.listContainersCmd().exec().stream() - .filter(container -> container.getNames()[0].endsWith(linkableContainer.getContainerName())) - .filter(container -> container.getNetworkSettings() != null && - container.getNetworkSettings().getNetworks() != null) - .flatMap(container -> container.getNetworkSettings().getNetworks().keySet().stream()) - .distinct() - .collect(Collectors.toSet()); + Set linkedContainerNetworks = findAllNetworksForLinkedContainers(linkableContainer); allLinkedContainerNetworks.addAll(linkedContainerNetworks); } @@ -443,6 +465,26 @@ private void applyConfiguration(CreateContainerCmd createCommand) { createCommand.withLabels(Collections.singletonMap("org.testcontainers", "true")); } + private Set findLinksFromThisContainer(String alias, LinkableContainer linkableContainer) { + return dockerClient.listContainersCmd() + .withStatusFilter("running") + .exec().stream() + .flatMap(container -> Stream.of(container.getNames())) + .filter(name -> name.endsWith(linkableContainer.getContainerName())) + .map(name -> new Link(name, alias)) + .collect(Collectors.toSet()); + } + + private Set findAllNetworksForLinkedContainers(LinkableContainer linkableContainer) { + return dockerClient.listContainersCmd().exec().stream() + .filter(container -> container.getNames()[0].endsWith(linkableContainer.getContainerName())) + .filter(container -> container.getNetworkSettings() != null && + container.getNetworkSettings().getNetworks() != null) + .flatMap(container -> container.getNetworkSettings().getNetworks().keySet().stream()) + .distinct() + .collect(Collectors.toSet()); + } + /** * {@inheritDoc} */ @@ -1015,11 +1057,20 @@ protected Logger logger() { /** * @return the port on which to check if the container is ready + * @deprecated see {@link AbstractWaitStrategy#getLivenessCheckPorts()} */ + @Deprecated protected Integer getLivenessCheckPort() { return container.getLivenessCheckPort(); } + /** + * @return the ports on which to check if the container is ready + */ + protected Set getLivenessCheckPorts() { + return container.getLivenessCheckPorts(); + } + /** * @return the rate limiter to use */ diff --git a/core/src/main/java/org/testcontainers/containers/VncRecordingSidekickContainer.java b/core/src/main/java/org/testcontainers/containers/VncRecordingSidekickContainer.java index 959ae80e46f..04f01c33623 100644 --- a/core/src/main/java/org/testcontainers/containers/VncRecordingSidekickContainer.java +++ b/core/src/main/java/org/testcontainers/containers/VncRecordingSidekickContainer.java @@ -1,6 +1,7 @@ package org.testcontainers.containers; import com.github.dockerjava.api.command.InspectContainerResponse; +import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.traits.LinkableContainer; import org.testcontainers.containers.traits.VncService; import org.testcontainers.utility.TestcontainersConfiguration; @@ -10,6 +11,9 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; +import java.util.Set; + +import static java.util.Collections.emptySet; /** * 'Sidekick container' with the sole purpose of recording the VNC screen output from another container. @@ -46,10 +50,11 @@ protected void containerIsStarting(InspectContainerResponse containerInfo) { // do nothing } + @NotNull @Override - protected Integer getLivenessCheckPort() { + protected Set getLivenessCheckPorts() { // no liveness check needed - return null; + return emptySet(); } @Override diff --git a/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java index babe0e8d592..b64535503c0 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java @@ -1,19 +1,18 @@ package org.testcontainers.containers.wait; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang.SystemUtils; import org.rnorth.ducttape.TimeoutException; import org.rnorth.ducttape.unreliables.Unreliables; -import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.ContainerLaunchException; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.dockerclient.DockerMachineClientProviderStrategy; -import org.testcontainers.dockerclient.WindowsClientProviderStrategy; +import org.testcontainers.containers.wait.internal.ExternalPortListeningCheck; +import org.testcontainers.containers.wait.internal.InternalCommandPortListeningCheck; -import java.net.Socket; import java.util.List; +import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; /** * Waits until a socket connection can be established on a port exposed or mapped by the container. @@ -23,87 +22,40 @@ @Slf4j public class HostPortWaitStrategy extends GenericContainer.AbstractWaitStrategy { - private static final String SUCCESS_MARKER = "TESTCONTAINERS_SUCCESS"; @Override protected void waitUntilReady() { - final Integer port = getLivenessCheckPort(); - if (null == port) { - log.debug("Liveness check port of {} is empty. Not waiting.", container.getContainerName()); + final Set externalLivenessCheckPorts = getLivenessCheckPorts(); + if (externalLivenessCheckPorts.isEmpty()) { + log.debug("Liveness check ports of {} is empty. Not waiting.", container.getContainerName()); return; } - Callable checkStrategy; + @SuppressWarnings("unchecked") + List exposedPorts = container.getExposedPorts(); - if (shouldCheckWithCommand()) { - List exposedPorts = container.getExposedPorts(); + final Set internalPorts = getInternalPorts(externalLivenessCheckPorts, exposedPorts); - Integer exposedPort = exposedPorts.stream() - .filter(it -> port.equals(container.getMappedPort(it))) - .findFirst() - .orElse(null); + Callable internalCheck = new InternalCommandPortListeningCheck(container, internalPorts); - if (null == exposedPort) { - log.warn("Liveness check port of {} is set to {}, but it's not listed in exposed ports.", - container.getContainerName(), port); - return; - } - - String[][] commands = { - { "/bin/sh", "-c", "nc -vz -w 1 localhost " + exposedPort + " && echo " + SUCCESS_MARKER }, - { "/bin/bash", "-c", " { - for (String[] command : commands) { - try { - if (container.execInContainer(command).getStdout().contains(SUCCESS_MARKER)) { - return true; - } - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (Exception e) { - continue; - } - } - - return false; - }; - } else { - checkStrategy = () -> { - new Socket(container.getContainerIpAddress(), port).close(); - return true; - }; - } + Callable externalCheck = new ExternalPortListeningCheck(container, externalLivenessCheckPorts); try { Unreliables.retryUntilTrue((int) startupTimeout.getSeconds(), TimeUnit.SECONDS, () -> { - return getRateLimiter().getWhenReady(() -> { - try { - return checkStrategy.call(); - } catch (Exception e) { - throw new RuntimeException(e); - } - }); + return getRateLimiter().getWhenReady(() -> internalCheck.call() && externalCheck.call()); }); } catch (TimeoutException e) { throw new ContainerLaunchException("Timed out waiting for container port to open (" + - container.getContainerIpAddress() + ":" + port + " should be listening)"); + container.getContainerIpAddress() + + " ports: " + + externalLivenessCheckPorts + + " should be listening)"); } } - private boolean shouldCheckWithCommand() { - // Special case for Docker for Mac, see #160 - if (! DockerClientFactory.instance().isUsing(DockerMachineClientProviderStrategy.class) && - SystemUtils.IS_OS_MAC_OSX) { - return true; - } - - // Special case for Docker for Windows, see #160 - if (DockerClientFactory.instance().isUsing(WindowsClientProviderStrategy.class)) { - return true; - } - - return false; + private Set getInternalPorts(Set externalLivenessCheckPorts, List exposedPorts) { + return exposedPorts.stream() + .filter(it -> externalLivenessCheckPorts.contains(container.getMappedPort(it))) + .collect(Collectors.toSet()); } } diff --git a/core/src/main/java/org/testcontainers/containers/wait/WaitAllStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/WaitAllStrategy.java new file mode 100644 index 00000000000..b4bf27ef98b --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/wait/WaitAllStrategy.java @@ -0,0 +1,38 @@ +package org.testcontainers.containers.wait; + +import org.rnorth.ducttape.timeouts.Timeouts; +import org.testcontainers.containers.GenericContainer; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * Wait strategy that waits for a number of other strategies to pass in series. + */ +public class WaitAllStrategy implements WaitStrategy { + + private final List strategies = new ArrayList<>(); + private Duration timeout = Duration.ofSeconds(30); + + @Override + public void waitUntilReady(GenericContainer container) { + Timeouts.doWithTimeout((int) timeout.toMillis(), TimeUnit.MILLISECONDS, () -> { + for (WaitStrategy strategy : strategies) { + strategy.waitUntilReady(container); + } + }); + } + + public WaitAllStrategy withStrategy(WaitStrategy strategy) { + this.strategies.add(strategy); + return this; + } + + @Override + public WaitStrategy withStartupTimeout(Duration startupTimeout) { + this.timeout = startupTimeout; + return this; + } +} diff --git a/core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java b/core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java new file mode 100644 index 00000000000..65f6182b2bc --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java @@ -0,0 +1,32 @@ +package org.testcontainers.containers.wait.internal; + +import lombok.RequiredArgsConstructor; +import org.testcontainers.containers.Container; + +import java.io.IOException; +import java.net.Socket; +import java.util.Set; +import java.util.concurrent.Callable; + +/** + * Mechanism for testing that a socket is listening when run from the test host. + */ +@RequiredArgsConstructor +public class ExternalPortListeningCheck implements Callable { + private final Container container; + private final Set externalLivenessCheckPorts; + + @Override + public Boolean call() { + String address = container.getContainerIpAddress(); + + for (Integer externalPort : externalLivenessCheckPorts) { + try { + new Socket(address, externalPort).close(); + } catch (IOException e) { + throw new IllegalStateException("Socket not listening yet: " + externalPort); + } + } + return true; + } +} diff --git a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java new file mode 100644 index 00000000000..a6f51d9a2de --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java @@ -0,0 +1,49 @@ +package org.testcontainers.containers.wait.internal; + +import lombok.RequiredArgsConstructor; +import org.testcontainers.containers.Container; + +import java.util.Set; + +import static java.lang.String.format; + +/** + * Mechanism for testing that a socket is listening when run from the container being checked. + */ +@RequiredArgsConstructor +public class InternalCommandPortListeningCheck implements java.util.concurrent.Callable { + + private static final String SUCCESS_MARKER = "TESTCONTAINERS_SUCCESS"; + + private final Container container; + private final Set internalPorts; + + @Override + public Boolean call() { + for (Integer internalPort : internalPorts) { + tryPort(internalPort); + } + + return true; + } + + private void tryPort(Integer internalPort) { + String[][] commands = { + {"/bin/sh", "-c", format("cat /proc/net/tcp | awk '{print $2}' | grep :%x && echo %s", internalPort, SUCCESS_MARKER)}, + {"/bin/sh", "-c", format("nc -vz -w 1 localhost %d && echo %s", internalPort, SUCCESS_MARKER)}, + {"/bin/bash", "-c", format(" { + Uninterruptibles.sleepUninterruptibly(20, TimeUnit.MILLISECONDS); + return null; + }).when(strategy1).waitUntilReady(eq(container)); + + assertThrows("The outer strategy timeout applies", TimeoutException.class, () -> { + underTest.waitUntilReady(container); + }); + } +} \ No newline at end of file diff --git a/core/src/test/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheckTest.java b/core/src/test/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheckTest.java new file mode 100644 index 00000000000..231942bf568 --- /dev/null +++ b/core/src/test/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheckTest.java @@ -0,0 +1,71 @@ +package org.testcontainers.containers.wait.internal; + +import com.google.common.collect.ImmutableSet; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.rnorth.visibleassertions.VisibleAssertions; +import org.testcontainers.containers.Container; + +import java.net.ServerSocket; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; + +public class ExternalPortListeningCheckTest { + + private ServerSocket listeningSocket1; + private ServerSocket listeningSocket2; + private ServerSocket nonListeningSocket; + private Container mockContainer; + + @Before + public void setUp() throws Exception { + listeningSocket1 = new ServerSocket(0); + listeningSocket2 = new ServerSocket(0); + + nonListeningSocket = new ServerSocket(0); + nonListeningSocket.close(); + + mockContainer = mock(Container.class); + when(mockContainer.getContainerIpAddress()).thenReturn("127.0.0.1"); + } + + @Test + public void singleListening() { + + final ExternalPortListeningCheck check = new ExternalPortListeningCheck(mockContainer, ImmutableSet.of(listeningSocket1.getLocalPort())); + + final Boolean result = check.call(); + + VisibleAssertions.assertTrue("ExternalPortListeningCheck identifies a single listening port", result); + } + + @Test + public void multipleListening() { + + final ExternalPortListeningCheck check = new ExternalPortListeningCheck(mockContainer, ImmutableSet.of(listeningSocket1.getLocalPort(), listeningSocket2.getLocalPort())); + + final Boolean result = check.call(); + + VisibleAssertions.assertTrue("ExternalPortListeningCheck identifies multiple listening port", result); + } + + @Test + public void oneNotListening() { + + final ExternalPortListeningCheck check = new ExternalPortListeningCheck(mockContainer, ImmutableSet.of(listeningSocket1.getLocalPort(), nonListeningSocket.getLocalPort())); + + assertThrows("ExternalPortListeningCheck detects a non-listening port among many", + IllegalStateException.class, + (Runnable) check::call); + + } + + @After + public void tearDown() throws Exception { + listeningSocket1.close(); + listeningSocket2.close(); + } +} \ No newline at end of file diff --git a/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java b/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java new file mode 100644 index 00000000000..75684256997 --- /dev/null +++ b/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java @@ -0,0 +1,33 @@ +package org.testcontainers.containers.wait.internal; + +import com.google.common.collect.ImmutableSet; +import org.junit.Rule; +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; + +public class InternalCommandPortListeningCheckTest { + + @Rule + public GenericContainer nginx = new GenericContainer<>("nginx:1.9.4"); + + @Test + public void singleListening() { + final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(nginx, ImmutableSet.of(80)); + + final Boolean result = check.call(); + + assertTrue("InternalCommandPortListeningCheck identifies a single listening port", result); + } + + @Test + public void nonListening() { + final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(nginx, ImmutableSet.of(80, 1234)); + + assertThrows("InternalCommandPortListeningCheck detects a non-listening port among many", + IllegalStateException.class, + (Runnable) check::call); + } +} \ No newline at end of file diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index fb44c90a6a2..5f7fb72241a 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -149,9 +149,6 @@ protected void optionallyMapResourceParameterAsVolume(@NotNull String paramName, } } - @Override - protected abstract Integer getLivenessCheckPort(); - public void setParameters(Map parameters) { this.parameters = parameters; } diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java index 9401dce238d..75a94824b82 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java @@ -1,5 +1,10 @@ package org.testcontainers.containers; +import org.jetbrains.annotations.NotNull; + +import java.util.HashSet; +import java.util.Set; + /** * @author richardnorth */ @@ -21,9 +26,10 @@ public MySQLContainer(String dockerImageName) { super(dockerImageName); } + @NotNull @Override - protected Integer getLivenessCheckPort() { - return getMappedPort(MYSQL_PORT); + protected Set getLivenessCheckPorts() { + return new HashSet<>(getMappedPort(MYSQL_PORT)); } @Override diff --git a/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java b/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java index 2b0257ca409..1e2ad9e47fc 100644 --- a/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java +++ b/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java @@ -1,9 +1,12 @@ package org.testcontainers.containers; +import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.traits.LinkableContainer; import java.net.MalformedURLException; import java.net.URL; +import java.util.HashSet; +import java.util.Set; /** * @author richardnorth @@ -16,9 +19,10 @@ public NginxContainer() { super("nginx:1.9.4"); } + @NotNull @Override - protected Integer getLivenessCheckPort() { - return getMappedPort(80); + protected Set getLivenessCheckPorts() { + return new HashSet<>(getMappedPort(80)); } @Override diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index 9ba7d23b7aa..325aad44332 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -1,8 +1,11 @@ package org.testcontainers.containers; +import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.wait.LogMessageWaitStrategy; import java.time.Duration; +import java.util.HashSet; +import java.util.Set; import static java.time.temporal.ChronoUnit.SECONDS; @@ -29,9 +32,10 @@ public PostgreSQLContainer(final String dockerImageName) { .withStartupTimeout(Duration.of(60, SECONDS)); } + @NotNull @Override - protected Integer getLivenessCheckPort() { - return getMappedPort(POSTGRESQL_PORT); + protected Set getLivenessCheckPorts() { + return new HashSet<>(getMappedPort(POSTGRESQL_PORT)); } @Override diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index 81c84786f6d..d3f66dd372d 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -1,6 +1,8 @@ package org.testcontainers.containers; import com.github.dockerjava.api.command.InspectContainerResponse; +import com.google.common.collect.ImmutableSet; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.junit.runner.Description; import org.openqa.selenium.remote.BrowserType; @@ -12,7 +14,10 @@ import org.slf4j.LoggerFactory; import org.testcontainers.containers.traits.LinkableContainer; import org.testcontainers.containers.traits.VncService; +import org.testcontainers.containers.wait.HostPortWaitStrategy; import org.testcontainers.containers.wait.LogMessageWaitStrategy; +import org.testcontainers.containers.wait.WaitAllStrategy; +import org.testcontainers.containers.wait.WaitStrategy; import java.io.File; import java.net.MalformedURLException; @@ -20,6 +25,7 @@ import java.time.Duration; import java.util.ArrayList; import java.util.Collection; +import java.util.Set; import java.util.concurrent.TimeUnit; import static java.time.temporal.ChronoUnit.SECONDS; @@ -55,9 +61,15 @@ public class BrowserWebDriverContainer getLivenessCheckPorts() { + return ImmutableSet.of(getMappedPort(SELENIUM_PORT), getMappedPort(VNC_PORT)); } @Override @@ -154,9 +167,7 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { if (recordingMode != VncRecordingMode.SKIP) { LOGGER.debug("Starting VNC recording"); - // Use multiple startup attempts due to race condition between Selenium being available and VNC being available - VncRecordingSidekickContainer recordingSidekickContainer = new VncRecordingSidekickContainer<>(this) - .withStartupAttempts(3); + VncRecordingSidekickContainer recordingSidekickContainer = new VncRecordingSidekickContainer<>(this); recordingSidekickContainer.start(); currentVncRecordings.add(recordingSidekickContainer); diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/FirefoxWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/FirefoxWebDriverContainerTest.java index 7bd7e3b9ad1..6b8f915498a 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/FirefoxWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/FirefoxWebDriverContainerTest.java @@ -5,8 +5,6 @@ import org.openqa.selenium.remote.DesiredCapabilities; import org.testcontainers.containers.BrowserWebDriverContainer; -import java.io.IOException; - /** * */ @@ -17,12 +15,12 @@ public class FirefoxWebDriverContainerTest extends BaseWebDriverContainerTest { .withDesiredCapabilities(DesiredCapabilities.firefox()); @Test - public void simpleTest() throws IOException { + public void simpleTest() { doSimpleWebdriverTest(firefox); } @Test - public void simpleExploreTest() throws IOException { + public void simpleExploreTest() { doSimpleExplore(firefox); } } From 87b85015122fddf0887c2ae77ad108fe54573848 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Tue, 12 Dec 2017 00:33:52 +0100 Subject: [PATCH 0197/5615] Fix problems with copying files and Docker-Compose on Windows (#514) For docker socket mounting on Windows, the socket path must be prefixed with an additional "/". Also used Windows style paths when interacting with files for Docker for Windows compatibility. --- .gitattributes | 1 + CHANGELOG.md | 2 + .../containers/DockerComposeContainer.java | 18 +++++++-- .../images/builder/traits/ClasspathTrait.java | 2 +- .../testcontainers/utility/MountableFile.java | 37 ++++++++++--------- .../utility/MountableFileTest.java | 2 +- 6 files changed, 39 insertions(+), 23 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000000..526c8a38d4a --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.sh text eol=lf \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a0d764da7b..d4a39b400b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ All notable changes to this project will be documented in this file. ## UNRELEASED ### Fixed +- Problems with using container based docker-compose on Windows ([\#514](https://github.com/testcontainers/testcontainers-java/pull/514)) +- Problems with copying files on Windows ([\#514](https://github.com/testcontainers/testcontainers-java/pull/514)) - Fixed regression in 1.4.3 when using Docker Compose on Windows ([\#439](https://github.com/testcontainers/testcontainers-java/issues/439)) - Fixed local Docker Compose executable name resolution on Windows (#416) - Fixed TAR composition on Windows (#444) diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index b5b30fd4a99..d4061d9e499 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -395,6 +395,10 @@ default void validateFileList(List composeFiles) { * Use Docker Compose container. */ class ContainerisedDockerCompose extends GenericContainer implements DockerCompose { + + private static final String DOCKER_SOCKET_PATH = "/var/run/docker.sock"; + public static final char UNIX_PATH_SEPERATOR = ':'; + public ContainerisedDockerCompose(List composeFiles, String identifier) { super(TestcontainersConfiguration.getInstance().getDockerComposeContainerImage()); @@ -405,14 +409,14 @@ public ContainerisedDockerCompose(List composeFiles, String identifier) { // Map the docker compose file into the container final File dockerComposeBaseFile = composeFiles.get(0); final String pwd = dockerComposeBaseFile.getAbsoluteFile().getParentFile().getAbsolutePath(); - final String containerPwd = MountableFile.forHostPath(pwd).getResolvedPath(); + final String containerPwd = MountableFile.forHostPath(pwd).getFilesystemPath(); final List absoluteDockerComposeFiles = composeFiles.stream() .map(File::getAbsolutePath) .map(MountableFile::forHostPath) - .map(MountableFile::getResolvedPath) + .map(MountableFile::getFilesystemPath) .collect(toList()); - final String composeFileEnvVariableValue = Joiner.on(File.pathSeparator).join(absoluteDockerComposeFiles); + final String composeFileEnvVariableValue = Joiner.on(UNIX_PATH_SEPERATOR).join(absoluteDockerComposeFiles); // we always need the UNIX path separator logger().debug("Set env COMPOSE_FILE={}", composeFileEnvVariableValue); addEnv(ENV_COMPOSE_FILE, composeFileEnvVariableValue); addFileSystemBind(pwd, containerPwd, READ_ONLY); @@ -421,12 +425,18 @@ public ContainerisedDockerCompose(List composeFiles, String identifier) { // as the docker daemon, just mapping the docker control socket is OK. // As there seems to be a problem with mapping to the /var/run directory in certain environments (e.g. CircleCI) // we map the socket file outside of /var/run, as just /docker.sock - addFileSystemBind("/var/run/docker.sock", "/docker.sock", READ_WRITE); + addFileSystemBind(getDockerSocketHostPath(), "/docker.sock", READ_WRITE); addEnv("DOCKER_HOST", "unix:///docker.sock"); setStartupCheckStrategy(new IndefiniteWaitOneShotStartupCheckStrategy()); setWorkingDirectory(containerPwd); } + private String getDockerSocketHostPath() { + return SystemUtils.IS_OS_WINDOWS + ? "/" + DOCKER_SOCKET_PATH + : DOCKER_SOCKET_PATH; + } + @Override public void invoke() { super.start(); diff --git a/core/src/main/java/org/testcontainers/images/builder/traits/ClasspathTrait.java b/core/src/main/java/org/testcontainers/images/builder/traits/ClasspathTrait.java index 8203f81d494..5617365f2cd 100644 --- a/core/src/main/java/org/testcontainers/images/builder/traits/ClasspathTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/traits/ClasspathTrait.java @@ -13,6 +13,6 @@ public interface ClasspathTrait & BuildContext default SELF withFileFromClasspath(String path, String resourcePath) { final MountableFile mountableFile = MountableFile.forClasspathResource(resourcePath); - return ((SELF) this).withFileFromPath(path, Paths.get(mountableFile.getFilesystemPath())); + return ((SELF) this).withFileFromPath(path, Paths.get(mountableFile.getResolvedPath())); } } diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java index f92b8b4e887..9f99fe064af 100644 --- a/core/src/main/java/org/testcontainers/utility/MountableFile.java +++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java @@ -1,9 +1,15 @@ package org.testcontainers.utility; -import static lombok.AccessLevel.PACKAGE; -import static org.testcontainers.utility.PathUtils.recursiveDeleteDir; - import com.google.common.base.Charsets; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; +import org.apache.commons.lang.SystemUtils; +import org.jetbrains.annotations.NotNull; +import org.testcontainers.images.builder.Transferable; + import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -19,14 +25,9 @@ import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.compress.archivers.tar.TarArchiveEntry; -import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; -import org.apache.commons.lang.SystemUtils; -import org.jetbrains.annotations.NotNull; -import org.testcontainers.images.builder.Transferable; + +import static lombok.AccessLevel.PACKAGE; +import static org.testcontainers.utility.PathUtils.recursiveDeleteDir; /** * An abstraction over files and classpath resources aimed at encapsulating all the complexity of generating @@ -165,8 +166,8 @@ private static String unencodeResourceURIToFilePath(@NotNull final String resour private String resolvePath() { String result = getResourcePath(); - if (SystemUtils.IS_OS_WINDOWS) { - result = PathUtils.createMinGWPath(result); + if (SystemUtils.IS_OS_WINDOWS && result.startsWith("/")) { + result = result.substring(1); } return result; @@ -177,13 +178,15 @@ private String resolvePath() { * into a container. If this is a classpath resource residing in a JAR, it will be extracted to * a temporary location so that the Docker daemon is able to access it. * + * TODO: rename method accordingly and check if really needed like this + * * @return */ private String resolveFilesystemPath() { String result = getResourcePath(); if (SystemUtils.IS_OS_WINDOWS && result.startsWith("/")) { - result = result.substring(1); + result = PathUtils.createMinGWPath(result).substring(1); } return result; @@ -285,7 +288,7 @@ private void deleteOnExit(final Path path) { */ @Override public void transferTo(final TarArchiveOutputStream outputStream, String destinationPathInTar) { - recursiveTar(destinationPathInTar, this.getFilesystemPath(), this.getFilesystemPath(), outputStream); + recursiveTar(destinationPathInTar, this.getResolvedPath(), this.getResolvedPath(), outputStream); } /* @@ -325,7 +328,7 @@ private void recursiveTar(String entryFilename, String rootPath, String itemPath @Override public long getSize() { - final File file = new File(this.getFilesystemPath()); + final File file = new File(this.getResolvedPath()); if (file.isFile()) { return file.length(); } else { @@ -340,7 +343,7 @@ public String getDescription() { @Override public int getFileMode() { - return getUnixFileMode(this.getFilesystemPath()); + return getUnixFileMode(this.getResolvedPath()); } private int getUnixFileMode(final String pathAsString) { diff --git a/core/src/test/java/org/testcontainers/utility/MountableFileTest.java b/core/src/test/java/org/testcontainers/utility/MountableFileTest.java index c278953c112..7bdaeeaeeeb 100644 --- a/core/src/test/java/org/testcontainers/utility/MountableFileTest.java +++ b/core/src/test/java/org/testcontainers/utility/MountableFileTest.java @@ -108,7 +108,7 @@ private Path createTempDir() throws IOException { } private void performChecks(final MountableFile mountableFile) { - final String mountablePath = mountableFile.getFilesystemPath(); + final String mountablePath = mountableFile.getResolvedPath(); assertTrue("The filesystem path '" + mountablePath + "' can be found", new File(mountablePath).exists()); assertFalse("The filesystem path '" + mountablePath + "' does not contain any URL escaping", mountablePath.contains("%20")); } From fad664473e2ecdc7b23bce555a004c5fe03d24b2 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 12 Dec 2017 19:52:01 +0000 Subject: [PATCH 0198/5615] Update changelog for 1.5.0 release --- CHANGELOG.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4a39b400b7..9f70c9b8a24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,18 +1,19 @@ # Change Log All notable changes to this project will be documented in this file. -## UNRELEASED +## [1.5.0] - 2017-12-12 ### Fixed -- Problems with using container based docker-compose on Windows ([\#514](https://github.com/testcontainers/testcontainers-java/pull/514)) -- Problems with copying files on Windows ([\#514](https://github.com/testcontainers/testcontainers-java/pull/514)) +- Fixed problems with using container based docker-compose on Windows ([\#514](https://github.com/testcontainers/testcontainers-java/pull/514)) +- Fixed problems with copying files on Windows ([\#514](https://github.com/testcontainers/testcontainers-java/pull/514)) - Fixed regression in 1.4.3 when using Docker Compose on Windows ([\#439](https://github.com/testcontainers/testcontainers-java/issues/439)) -- Fixed local Docker Compose executable name resolution on Windows (#416) -- Fixed TAR composition on Windows (#444) -- Allowing `addExposedPort` to be used after ports have been specified with `withExposedPorts` (#453) -- Stopping creation of temporary directory prior to creating temporary file (#443) -- Ensure that temp files are created in a temp directory (#423) +- Fixed local Docker Compose executable name resolution on Windows ([\#416](https://github.com/testcontainers/testcontainers-java/issues/#416)) +- Fixed TAR composition on Windows ([\#444](https://github.com/testcontainers/testcontainers-java/issues/#444)) +- Allowing `addExposedPort` to be used after ports have been specified with `withExposedPorts` ([\#453](https://github.com/testcontainers/testcontainers-java/issues/#453)) +- Stopping creation of temporary directory prior to creating temporary file ([\#443](https://github.com/testcontainers/testcontainers-java/issues/#443)) +- Ensure that temp files are created in a temp directory ([\#423](https://github.com/testcontainers/testcontainers-java/issues/#423)) - Added `WaitAllStrategy` as a mechanism for composing multiple startup `WaitStrategy` objects together -- Changed `BrowserWebDriverContainer` to use improved wait strategies, to eliminate race conditions when starting VNC recording containers. This should lead to far fewer 'error' messages logged when starting up selenium containers, and less exposure to race related bugs (fixes #466). +- Changed `BrowserWebDriverContainer` to use improved wait strategies, to eliminate race conditions when starting VNC recording containers. This should lead to far fewer 'error' messages logged when starting up selenium containers, and less exposure to race related bugs (fixes [\#466](https://github.com/testcontainers/testcontainers-java/issues/#466)). +- Add retry logic around checkExposedPort pre-flight check for improved robustness. ### Changed - Make Network instances reusable (i.e. work with `@ClassRule`) ([\#469](https://github.com/testcontainers/testcontainers-java/issues/469)) @@ -358,6 +359,8 @@ All notable changes to this project will be documented in this file. ## [0.9] - 2015-04-29 Initial release +[1.4.2]: https://github.com/testcontainers/testcontainers-java/releases/tag/1.5.0 +[1.4.2]: https://github.com/testcontainers/testcontainers-java/releases/tag/1.4.3 [1.4.2]: https://github.com/testcontainers/testcontainers-java/releases/tag/1.4.2 [1.4.1]: https://github.com/testcontainers/testcontainers-java/releases/tag/1.4.1 [1.4.0]: https://github.com/testcontainers/testcontainers-java/releases/tag/1.4.0 From 95225c4efd413795dfd144f27e31edb8dca259ce Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 12 Dec 2017 21:15:49 +0000 Subject: [PATCH 0199/5615] Fix broken issues links in changelog --- CHANGELOG.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f70c9b8a24..304606f8e89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,13 +6,13 @@ All notable changes to this project will be documented in this file. - Fixed problems with using container based docker-compose on Windows ([\#514](https://github.com/testcontainers/testcontainers-java/pull/514)) - Fixed problems with copying files on Windows ([\#514](https://github.com/testcontainers/testcontainers-java/pull/514)) - Fixed regression in 1.4.3 when using Docker Compose on Windows ([\#439](https://github.com/testcontainers/testcontainers-java/issues/439)) -- Fixed local Docker Compose executable name resolution on Windows ([\#416](https://github.com/testcontainers/testcontainers-java/issues/#416)) -- Fixed TAR composition on Windows ([\#444](https://github.com/testcontainers/testcontainers-java/issues/#444)) -- Allowing `addExposedPort` to be used after ports have been specified with `withExposedPorts` ([\#453](https://github.com/testcontainers/testcontainers-java/issues/#453)) -- Stopping creation of temporary directory prior to creating temporary file ([\#443](https://github.com/testcontainers/testcontainers-java/issues/#443)) -- Ensure that temp files are created in a temp directory ([\#423](https://github.com/testcontainers/testcontainers-java/issues/#423)) +- Fixed local Docker Compose executable name resolution on Windows ([\#416](https://github.com/testcontainers/testcontainers-java/issues/416)) +- Fixed TAR composition on Windows ([\#444](https://github.com/testcontainers/testcontainers-java/issues/444)) +- Allowing `addExposedPort` to be used after ports have been specified with `withExposedPorts` ([\#453](https://github.com/testcontainers/testcontainers-java/issues/453)) +- Stopping creation of temporary directory prior to creating temporary file ([\#443](https://github.com/testcontainers/testcontainers-java/issues/443)) +- Ensure that temp files are created in a temp directory ([\#423](https://github.com/testcontainers/testcontainers-java/issues/423)) - Added `WaitAllStrategy` as a mechanism for composing multiple startup `WaitStrategy` objects together -- Changed `BrowserWebDriverContainer` to use improved wait strategies, to eliminate race conditions when starting VNC recording containers. This should lead to far fewer 'error' messages logged when starting up selenium containers, and less exposure to race related bugs (fixes [\#466](https://github.com/testcontainers/testcontainers-java/issues/#466)). +- Changed `BrowserWebDriverContainer` to use improved wait strategies, to eliminate race conditions when starting VNC recording containers. This should lead to far fewer 'error' messages logged when starting up selenium containers, and less exposure to race related bugs (fixes [\#466](https://github.com/testcontainers/testcontainers-java/issues/466)). - Add retry logic around checkExposedPort pre-flight check for improved robustness. ### Changed From e7b65f94d6eab9bd2c07015c7d8386e2a0134382 Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 15 Dec 2017 07:59:15 +0000 Subject: [PATCH 0200/5615] Fix copy/paste error in changelog message --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 304606f8e89..f5c5d73fa82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ All notable changes to this project will be documented in this file. - Make Network instances reusable (i.e. work with `@ClassRule`) ([\#469](https://github.com/testcontainers/testcontainers-java/issues/469)) - Added support for explicitly setting file mode when copying file into container ([\#446](https://github.com/testcontainers/testcontainers-java/issues/446), [\#467](https://github.com/testcontainers/testcontainers-java/issues/467)) - Use Visible Assertions 2.1.0 for pre-flight test output (eliminating Jansi/JNR-POSIX dependencies for lower likelihood of conflict. JNA is now used internally by Visible Assertions instead). -- Mark all links functionality as deprecated. This is pending removal in a later release. Please see [\#465](https://github.com/testcontainers/testcontainers-java/issues/465). {@link Network} features should be used instead. +- Mark all links functionality as deprecated. This is pending removal in a later release. Please see [\#465](https://github.com/testcontainers/testcontainers-java/issues/465). `Network` features should be used instead. - Added support for copying files to/from running containers ([\#378](https://github.com/testcontainers/testcontainers-java/issues/378)) - Add `getLivenessCheckPorts` as an eventual replacement for `getLivenessCheckPort`; this allows multiple ports to be included in post-startup wait strategies. - Refactor wait strategy port checking and improve test coverage. From fcefad377cc67c39c21f503fd449cee5c0971326 Mon Sep 17 00:00:00 2001 From: David Byron Date: Sat, 16 Dec 2017 12:21:36 -0800 Subject: [PATCH 0201/5615] Add name accessor for JDBC containers (#520) Container classes which want to support this feature need to override getDatabaseName() method. Fixes #473 --- CHANGELOG.md | 15 +++++++++------ .../containers/JdbcDatabaseContainer.java | 7 +++++++ .../testcontainers/containers/MySQLContainer.java | 7 ++++++- .../containers/PostgreSQLContainer.java | 5 +++++ 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5c5d73fa82..d5cb11b2c57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # Change Log All notable changes to this project will be documented in this file. +### Changed +- Added `getDatabaseName` method to JdbcDatabaseContainer, MySQLContainer, PostgreSQLContainer ([\#473](https://github.com/testcontainers/testcontainers-java/issues/473)) + ## [1.5.0] - 2017-12-12 ### Fixed - Fixed problems with using container based docker-compose on Windows ([\#514](https://github.com/testcontainers/testcontainers-java/pull/514)) @@ -12,12 +15,12 @@ All notable changes to this project will be documented in this file. - Stopping creation of temporary directory prior to creating temporary file ([\#443](https://github.com/testcontainers/testcontainers-java/issues/443)) - Ensure that temp files are created in a temp directory ([\#423](https://github.com/testcontainers/testcontainers-java/issues/423)) - Added `WaitAllStrategy` as a mechanism for composing multiple startup `WaitStrategy` objects together -- Changed `BrowserWebDriverContainer` to use improved wait strategies, to eliminate race conditions when starting VNC recording containers. This should lead to far fewer 'error' messages logged when starting up selenium containers, and less exposure to race related bugs (fixes [\#466](https://github.com/testcontainers/testcontainers-java/issues/466)). +- Changed `BrowserWebDriverContainer` to use improved wait strategies, to eliminate race conditions when starting VNC recording containers. This should lead to far fewer 'error' messages logged when starting up selenium containers, and less exposure to race related bugs (fixes [\#466](https://github.com/testcontainers/testcontainers-java/issues/466)). - Add retry logic around checkExposedPort pre-flight check for improved robustness. ### Changed - Make Network instances reusable (i.e. work with `@ClassRule`) ([\#469](https://github.com/testcontainers/testcontainers-java/issues/469)) -- Added support for explicitly setting file mode when copying file into container ([\#446](https://github.com/testcontainers/testcontainers-java/issues/446), [\#467](https://github.com/testcontainers/testcontainers-java/issues/467)) +- Added support for explicitly setting file mode when copying file into container ([\#446](https://github.com/testcontainers/testcontainers-java/issues/446), [\#467](https://github.com/testcontainers/testcontainers-java/issues/467)) - Use Visible Assertions 2.1.0 for pre-flight test output (eliminating Jansi/JNR-POSIX dependencies for lower likelihood of conflict. JNA is now used internally by Visible Assertions instead). - Mark all links functionality as deprecated. This is pending removal in a later release. Please see [\#465](https://github.com/testcontainers/testcontainers-java/issues/465). `Network` features should be used instead. - Added support for copying files to/from running containers ([\#378](https://github.com/testcontainers/testcontainers-java/issues/378)) @@ -83,7 +86,7 @@ All notable changes to this project will be documented in this file. - Added global configuration file `~/.testcontainers.properties` ([\#362](https://github.com/testcontainers/testcontainers-java/issues/362)) - Added container arguments to specify SELinux contexts for mounts ([\#334](https://github.com/testcontainers/testcontainers-java/issues/334)) - Removed unused Jersey dependencies ([\#361](https://github.com/testcontainers/testcontainers-java/issues/361)) -- Removed deprecated, wrongly-generated setters from `GenericContainer` +- Removed deprecated, wrongly-generated setters from `GenericContainer` ## [1.3.0] - 2017-06-05 ### Fixed @@ -127,7 +130,7 @@ All notable changes to this project will be documented in this file. - Add ability to attach volumes to a container using 'volumes from' ([\#244](https://github.com/testcontainers/testcontainers-java/issues/244), [\#289](https://github.com/testcontainers/testcontainers-java/issues/289)) ## [1.1.8] - 2017-01-22 -### Fixed +### Fixed - Compatibility fixes for Docker for Mac v1.13.0 ([\#272](https://github.com/testcontainers/testcontainers-java/issues/272)) - Relax docker environment disk space check to accomodate unusual empty `df` output observed on Docker for Mac with OverlayFS ([\#273](https://github.com/testcontainers/testcontainers-java/issues/273), [\#278](https://github.com/testcontainers/testcontainers-java/issues/278)) - Fix inadvertent private-scoping of startup checks' `StartupStatus`, which made implementation of custom startup checks impossible ([\#266](https://github.com/testcontainers/testcontainers-java/issues/266)) @@ -146,14 +149,14 @@ All notable changes to this project will be documented in this file. - Compensate for premature TCP socket opening in Docker for Mac ([\#160](https://github.com/testcontainers/testcontainers-java/issues/160), [\#236](https://github.com/testcontainers/testcontainers-java/issues/236)) - (Internal) Stabilise various parts of Testcontainers' self test suite ([\#241](https://github.com/testcontainers/testcontainers-java/issues/241)) - Fix mounting of classpath resources when those resources are in a JAR file ([\#213](https://github.com/testcontainers/testcontainers-java/issues/213)) -- Reduce misleading error messages caused mainly by trying to perform operations on stopped containers ([\#243](https://github.com/testcontainers/testcontainers-java/issues/243)) +- Reduce misleading error messages caused mainly by trying to perform operations on stopped containers ([\#243](https://github.com/testcontainers/testcontainers-java/issues/243)) ### Changed - Uses a default MySQL and MariaDB configuration to reduce memory footprint ([\#209](https://github.com/testcontainers/testcontainers-java/issues/209), [\#243](https://github.com/testcontainers/testcontainers-java/issues/243)) - Docker Compose can optionally now use a local `docker-compose` executable rather than running inside a container ([\#200](https://github.com/testcontainers/testcontainers-java/issues/200)) - Add support for privileged mode containers ([\#234](https://github.com/testcontainers/testcontainers-java/issues/234), [\#235](https://github.com/testcontainers/testcontainers-java/issues/235)) - Allow container/network cleanup (ResourceReaper) to be triggered programmatically ([\#231](https://github.com/testcontainers/testcontainers-java/issues/231)) -- Add optional tailing of logs for containers spawned by Docker Compose ([\#233](https://github.com/testcontainers/testcontainers-java/issues/233)) +- Add optional tailing of logs for containers spawned by Docker Compose ([\#233](https://github.com/testcontainers/testcontainers-java/issues/233)) - (Internal) Relocate non-proprietary database container tests to a single module ## [1.1.6] - 2016-09-22 diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index 5f7fb72241a..d4c0ef0e15d 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -45,6 +45,13 @@ public JdbcDatabaseContainer(String dockerImageName) { */ public abstract String getJdbcUrl(); + /** + * @return the database name + */ + public String getDatabaseName() { + throw new UnsupportedOperationException(); + } + /** * @return the standard database username that should be used for connections */ diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java index 75a94824b82..6e782cd4d2a 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java @@ -36,7 +36,7 @@ protected Set getLivenessCheckPorts() { protected void configure() { optionallyMapResourceParameterAsVolume(MY_CNF_CONFIG_OVERRIDE_PARAM_NAME, "/etc/mysql/conf.d", "mysql-default-conf"); - addExposedPort(3306); + addExposedPort(MYSQL_PORT); addEnv("MYSQL_DATABASE", databaseName); addEnv("MYSQL_USER", username); addEnv("MYSQL_PASSWORD", password); @@ -55,6 +55,11 @@ public String getJdbcUrl() { return "jdbc:mysql://" + getContainerIpAddress() + ":" + getMappedPort(MYSQL_PORT) + "/" + databaseName; } + @Override + public String getDatabaseName() { + return databaseName; + } + @Override public String getUsername() { return username; diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index 325aad44332..23107764644 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -58,6 +58,11 @@ public String getJdbcUrl() { return "jdbc:postgresql://" + getContainerIpAddress() + ":" + getMappedPort(POSTGRESQL_PORT) + "/" + databaseName; } + @Override + public String getDatabaseName() { + return databaseName; + } + @Override public String getUsername() { return username; From ed64c1febb15cead7f149b3b816cdb1e46b35429 Mon Sep 17 00:00:00 2001 From: Reinhard Prechtl Date: Sun, 17 Dec 2017 12:13:03 +0100 Subject: [PATCH 0202/5615] Fix case-sensitivity bug for port numbers in command (#524) * Fix case-sensitivity bug for port numbers in command This bug shows itself when a port number contains letters in the hexadecimal conversion. * Make grep ignore casing * Rename and cleanup Nginx test configuration file * Update the changelog for PR #524 * Fix a typo in the changelog --- CHANGELOG.md | 3 +++ .../internal/InternalCommandPortListeningCheck.java | 2 +- .../InternalCommandPortListeningCheckTest.java | 10 +++++++--- core/src/test/resources/nginx_on_8080.conf | 5 +++++ 4 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 core/src/test/resources/nginx_on_8080.conf diff --git a/CHANGELOG.md b/CHANGELOG.md index d5cb11b2c57..52f2147c666 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # Change Log All notable changes to this project will be documented in this file. +### Fixed + - Fixed problem with case-sensitivity when checking internal port. ([\#524](https://github.com/testcontainers/testcontainers-java/pull/524)) + ### Changed - Added `getDatabaseName` method to JdbcDatabaseContainer, MySQLContainer, PostgreSQLContainer ([\#473](https://github.com/testcontainers/testcontainers-java/issues/473)) diff --git a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java index a6f51d9a2de..555bfe81437 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java +++ b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java @@ -29,7 +29,7 @@ public Boolean call() { private void tryPort(Integer internalPort) { String[][] commands = { - {"/bin/sh", "-c", format("cat /proc/net/tcp | awk '{print $2}' | grep :%x && echo %s", internalPort, SUCCESS_MARKER)}, + {"/bin/sh", "-c", format("cat /proc/net/tcp | awk '{print $2}' | grep -i :%x && echo %s", internalPort, SUCCESS_MARKER)}, {"/bin/sh", "-c", format("nc -vz -w 1 localhost %d && echo %s", internalPort, SUCCESS_MARKER)}, {"/bin/bash", "-c", format("("nginx:1.9.4"); + public GenericContainer nginx = new GenericContainer<>("nginx:1.9.4") + .withClasspathResourceMapping("nginx_on_8080.conf", "/etc/nginx/conf.d/default.conf", BindMode.READ_ONLY); @Test public void singleListening() { - final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(nginx, ImmutableSet.of(80)); + final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(nginx, ImmutableSet.of(8080)); final Boolean result = check.call(); @@ -24,7 +28,7 @@ public void singleListening() { @Test public void nonListening() { - final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(nginx, ImmutableSet.of(80, 1234)); + final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(nginx, ImmutableSet.of(8080, 1234)); assertThrows("InternalCommandPortListeningCheck detects a non-listening port among many", IllegalStateException.class, diff --git a/core/src/test/resources/nginx_on_8080.conf b/core/src/test/resources/nginx_on_8080.conf new file mode 100644 index 00000000000..c7927d46265 --- /dev/null +++ b/core/src/test/resources/nginx_on_8080.conf @@ -0,0 +1,5 @@ +# This configuration makes Nginx listen on port port 8080 + +server { + listen 8080; +} \ No newline at end of file From 6d42f413de84f3130e57d4e73488851ddebbb8da Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 18 Dec 2017 18:45:05 +0000 Subject: [PATCH 0203/5615] Add retry logic around checkExposedPort pre-flight check for improved robustness (fixes #513). (#516) Fix erroneous changelog entry for 1.5.0 release --- CHANGELOG.md | 6 +++--- .../testcontainers/DockerClientFactory.java | 20 +++++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52f2147c666..dc1a6927a0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,8 @@ All notable changes to this project will be documented in this file. ### Fixed - - Fixed problem with case-sensitivity when checking internal port. ([\#524](https://github.com/testcontainers/testcontainers-java/pull/524)) +- Fixed problem with case-sensitivity when checking internal port. ([\#524](https://github.com/testcontainers/testcontainers-java/pull/524)) +- Add retry logic around checkExposedPort pre-flight check for improved robustness ([\#513](https://github.com/testcontainers/testcontainers-java/issues/513)) ### Changed - Added `getDatabaseName` method to JdbcDatabaseContainer, MySQLContainer, PostgreSQLContainer ([\#473](https://github.com/testcontainers/testcontainers-java/issues/473)) @@ -18,8 +19,7 @@ All notable changes to this project will be documented in this file. - Stopping creation of temporary directory prior to creating temporary file ([\#443](https://github.com/testcontainers/testcontainers-java/issues/443)) - Ensure that temp files are created in a temp directory ([\#423](https://github.com/testcontainers/testcontainers-java/issues/423)) - Added `WaitAllStrategy` as a mechanism for composing multiple startup `WaitStrategy` objects together -- Changed `BrowserWebDriverContainer` to use improved wait strategies, to eliminate race conditions when starting VNC recording containers. This should lead to far fewer 'error' messages logged when starting up selenium containers, and less exposure to race related bugs (fixes [\#466](https://github.com/testcontainers/testcontainers-java/issues/466)). -- Add retry logic around checkExposedPort pre-flight check for improved robustness. +- Changed `BrowserWebDriverContainer` to use improved wait strategies, to eliminate race conditions when starting VNC recording containers. This should lead to far fewer 'error' messages logged when starting up selenium containers, and less exposure to race related bugs (fixes [\#466](https://github.com/testcontainers/testcontainers-java/issues/466)). ### Changed - Make Network instances reusable (i.e. work with `@ClassRule`) ([\#469](https://github.com/testcontainers/testcontainers-java/issues/469)) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index ef0edab403c..c7568fd8b40 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -14,6 +14,7 @@ import org.apache.commons.io.IOUtils; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; +import org.rnorth.ducttape.unreliables.Unreliables; import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.dockerclient.*; import org.testcontainers.utility.ComparableVersion; @@ -29,6 +30,7 @@ import java.util.List; import java.util.Optional; import java.util.ServiceLoader; +import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; import java.util.function.Consumer; @@ -177,16 +179,18 @@ private void checkMountableFile(DockerClient dockerClient, String id) { } private void checkExposedPort(String hostIpAddress, DockerClient dockerClient, String id) { - InspectContainerResponse inspectedContainer = dockerClient.inspectContainerCmd(id).exec(); + String response = Unreliables.retryUntilSuccess(3, TimeUnit.SECONDS, () -> { + InspectContainerResponse inspectedContainer = dockerClient.inspectContainerCmd(id).exec(); - String portSpec = inspectedContainer.getNetworkSettings().getPorts().getBindings().values().iterator().next()[0].getHostPortSpec(); + String portSpec = inspectedContainer.getNetworkSettings().getPorts().getBindings().values().iterator().next()[0].getHostPortSpec(); + + try (Socket socket = new Socket(hostIpAddress, Integer.parseInt(portSpec))) { + return IOUtils.toString(socket.getInputStream(), Charset.defaultCharset()); + } catch (IOException e) { + return e.getMessage(); + } + }); - String response; - try (Socket socket = new Socket(hostIpAddress, Integer.parseInt(portSpec))) { - response = IOUtils.toString(socket.getInputStream(), Charset.defaultCharset()); - } catch (IOException e) { - response = e.getMessage(); - } VisibleAssertions.assertEquals("A port exposed by a docker container should be accessible", "hello", response); } From b7bd98b4ee1315acdd906e4f9ad112f344c249fc Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 19 Dec 2017 13:26:19 +0100 Subject: [PATCH 0204/5615] Add "VncRecordingContainer" (#526) Add "VncRecordingContainer" - Network-based, attachable re-implementation of VncRecordingSidekickContainer --- CHANGELOG.md | 1 + .../containers/GenericContainer.java | 4 +- .../testcontainers/containers/Network.java | 8 + .../containers/VncRecordingContainer.java | 137 ++++++++++++++++++ .../VncRecordingSidekickContainer.java | 3 + .../containers/traits/VncService.java | 1 + .../containers/BrowserWebDriverContainer.java | 83 +++++++---- .../junit/BaseWebDriverContainerTest.java | 8 +- ...ChromeRecordingWebDriverContainerTest.java | 41 +++--- 9 files changed, 233 insertions(+), 53 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java diff --git a/CHANGELOG.md b/CHANGELOG.md index dc1a6927a0d..8238ea46596 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. ### Changed - Added `getDatabaseName` method to JdbcDatabaseContainer, MySQLContainer, PostgreSQLContainer ([\#473](https://github.com/testcontainers/testcontainers-java/issues/473)) +- Added `VncRecordingContainer` - Network-based, attachable re-implementation of `VncRecordingSidekickContainer` ([\#526](https://github.com/testcontainers/testcontainers-java/pull/526)) ## [1.5.0] - 2017-12-12 ### Fixed diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index d8ebb36ec93..9cd915ffe35 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -84,7 +84,9 @@ public class GenericContainer> private Network network; @NonNull - private List networkAliases = new ArrayList<>(); + private List networkAliases = new ArrayList<>(Arrays.asList( + "tc-" + Base58.randomString(8) + )); @NonNull private Future image; diff --git a/core/src/main/java/org/testcontainers/containers/Network.java b/core/src/main/java/org/testcontainers/containers/Network.java index 1e1fbf31781..dc848b02b8c 100644 --- a/core/src/main/java/org/testcontainers/containers/Network.java +++ b/core/src/main/java/org/testcontainers/containers/Network.java @@ -9,6 +9,7 @@ import org.testcontainers.DockerClientFactory; import org.testcontainers.utility.ResourceReaper; +import java.util.Collections; import java.util.Set; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; @@ -16,6 +17,13 @@ public interface Network extends AutoCloseable, TestRule { + Network SHARED = new NetworkImpl(false, null, Collections.emptySet(), null) { + @Override + public void close() { + // Do not allow users to close SHARED network, only ResourceReaper is allowed to close (destroy) it + } + }; + String getId(); static Network newNetwork() { diff --git a/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java b/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java new file mode 100644 index 00000000000..b8a26bfe65b --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java @@ -0,0 +1,137 @@ +package org.testcontainers.containers; + +import com.github.dockerjava.api.model.Frame; +import lombok.Getter; +import lombok.NonNull; +import lombok.SneakyThrows; +import lombok.ToString; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; +import org.rnorth.ducttape.TimeoutException; +import org.rnorth.ducttape.unreliables.Unreliables; +import org.testcontainers.containers.output.FrameConsumerResultCallback; +import org.testcontainers.utility.TestcontainersConfiguration; + +import java.io.Closeable; +import java.io.File; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * 'Sidekick container' with the sole purpose of recording the VNC screen output from another container. + * + */ +@Getter +@ToString +public class VncRecordingContainer extends GenericContainer { + + private static final String RECORDING_FILE_NAME = "/screen.flv"; + + public static final String DEFAULT_VNC_PASSWORD = "secret"; + + public static final int DEFAULT_VNC_PORT = 5900; + + private final String targetNetworkAlias; + + private String vncPassword = DEFAULT_VNC_PASSWORD; + + private int vncPort = 5900; + + private int frameRate = 30; + + public VncRecordingContainer(@NonNull GenericContainer targetContainer) { + this( + targetContainer.getNetwork(), + targetContainer.getNetworkAliases().stream() + .findFirst() + .orElseThrow(() -> new IllegalStateException("Target container must have a network alias")) + ); + } + + /** + * Create a sidekick container and attach it to another container. The VNC output of that container will be recorded. + */ + public VncRecordingContainer(@NonNull Network network, @NonNull String targetNetworkAlias) throws IllegalStateException { + super(TestcontainersConfiguration.getInstance().getVncRecordedContainerImage()); + + this.targetNetworkAlias = targetNetworkAlias; + withNetwork(network); + + waitingFor(new AbstractWaitStrategy() { + + @Override + protected void waitUntilReady() { + try { + Unreliables.retryUntilTrue((int) startupTimeout.toMillis(), TimeUnit.MILLISECONDS, () -> { + CountDownLatch latch = new CountDownLatch(1); + + FrameConsumerResultCallback callback = new FrameConsumerResultCallback() { + @Override + public void onNext(Frame frame) { + if (frame != null && new String(frame.getPayload()).contains("Connected")) { + latch.countDown(); + } + } + }; + + try ( + Closeable __ = dockerClient.logContainerCmd(containerId) + .withFollowStream(true) + .withSince(0) + .withStdErr(true) + .exec(callback) + ) { + return latch.await(1, TimeUnit.SECONDS); + } + }); + } catch (TimeoutException e) { + throw new ContainerLaunchException("Timed out waiting for log output", e); + } + } + }); + } + + public VncRecordingContainer withVncPassword(@NonNull String vncPassword) { + this.vncPassword = vncPassword; + return this; + } + + public VncRecordingContainer withVncPort(int vncPort) { + this.vncPort = vncPort; + return this; + } + + public VncRecordingContainer withFrameRate(int frameRate) { + this.frameRate = frameRate; + return this; + } + + @Override + protected void configure() { + withCreateContainerCmdModifier(it -> it.withEntrypoint("/bin/sh")); + setCommand( + "-c", + "echo '" + Base64.encodeBase64String(vncPassword.getBytes()) + "' | base64 -d > /vnc_password && " + + "flvrec.py -o " + RECORDING_FILE_NAME + " -d -r " + frameRate + " -P /vnc_password " + targetNetworkAlias + " " + vncPort + ); + } + + @SneakyThrows + public InputStream streamRecording() { + TarArchiveInputStream archiveInputStream = new TarArchiveInputStream( + dockerClient.copyArchiveFromContainerCmd(containerId, RECORDING_FILE_NAME).exec() + ); + archiveInputStream.getNextEntry(); + return archiveInputStream; + } + + @SneakyThrows + public void saveRecordingToFile(File file) { + try(InputStream inputStream = streamRecording()) { + Files.copy(inputStream, file.toPath(), StandardCopyOption.REPLACE_EXISTING); + } + } +} \ No newline at end of file diff --git a/core/src/main/java/org/testcontainers/containers/VncRecordingSidekickContainer.java b/core/src/main/java/org/testcontainers/containers/VncRecordingSidekickContainer.java index 04f01c33623..d1dfb5350c0 100644 --- a/core/src/main/java/org/testcontainers/containers/VncRecordingSidekickContainer.java +++ b/core/src/main/java/org/testcontainers/containers/VncRecordingSidekickContainer.java @@ -17,7 +17,10 @@ /** * 'Sidekick container' with the sole purpose of recording the VNC screen output from another container. + * + * @deprecated please use {@link VncRecordingContainer} */ +@Deprecated public class VncRecordingSidekickContainer, T extends VncService & LinkableContainer> extends GenericContainer { private final T vncServiceContainer; private final Path tempDir; diff --git a/core/src/main/java/org/testcontainers/containers/traits/VncService.java b/core/src/main/java/org/testcontainers/containers/traits/VncService.java index 6329805b74d..c0f6458c2c0 100644 --- a/core/src/main/java/org/testcontainers/containers/traits/VncService.java +++ b/core/src/main/java/org/testcontainers/containers/traits/VncService.java @@ -3,6 +3,7 @@ /** * A container which exposes a VNC server. */ +@Deprecated public interface VncService { /** * @return a URL which can be used to connect to the VNC server from the machine running the tests. Exposed for convenience, e.g. to aid manual debugging diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index d3f66dd372d..6e2375f5082 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -23,8 +23,6 @@ import java.net.MalformedURLException; import java.net.URL; import java.time.Duration; -import java.util.ArrayList; -import java.util.Collection; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -54,7 +52,7 @@ public class BrowserWebDriverContainer currentVncRecordings = new ArrayList<>(); + private VncRecordingContainer vncRecordingContainer = null; private static final Logger LOGGER = LoggerFactory.getLogger(BrowserWebDriverContainer.class); @@ -102,6 +100,16 @@ protected void configure() { throw new IllegalStateException(); } + if (recordingMode != VncRecordingMode.SKIP) { + if (getNetwork() == null) { + withNetwork(Network.SHARED); + } + + vncRecordingContainer = new VncRecordingContainer(this) + .withVncPassword(DEFAULT_PASSWORD) + .withVncPort(VNC_PORT); + } + if (!customImageNameIsSet) { super.setDockerImageName(getImageForCapabilities(desiredCapabilities)); } @@ -164,19 +172,15 @@ public int getPort() { @Override protected void containerIsStarted(InspectContainerResponse containerInfo) { - if (recordingMode != VncRecordingMode.SKIP) { - LOGGER.debug("Starting VNC recording"); - - VncRecordingSidekickContainer recordingSidekickContainer = new VncRecordingSidekickContainer<>(this); - - recordingSidekickContainer.start(); - currentVncRecordings.add(recordingSidekickContainer); - } - driver = Unreliables.retryUntilSuccess(30, TimeUnit.SECONDS, Timeouts.getWithTimeout(10, TimeUnit.SECONDS, () -> () -> new RemoteWebDriver(getSeleniumAddress(), desiredCapabilities))); + + if (vncRecordingContainer != null) { + LOGGER.debug("Starting VNC recording"); + vncRecordingContainer.start(); + } } /** @@ -193,39 +197,54 @@ public RemoteWebDriver getWebDriver() { @Override protected void failed(Throwable e, Description description) { - switch (recordingMode) { - case RECORD_FAILING: - case RECORD_ALL: - stopAndRetainRecordingForDescriptionAndSuccessState(description, false); - break; - } - currentVncRecordings.clear(); + stopAndRetainRecordingForDescriptionAndSuccessState(description, false); } @Override protected void succeeded(Description description) { - switch (recordingMode) { - case RECORD_ALL: - stopAndRetainRecordingForDescriptionAndSuccessState(description, true); - break; - } - currentVncRecordings.clear(); + stopAndRetainRecordingForDescriptionAndSuccessState(description, true); } @Override - protected void finished(Description description) { + public void stop() { if (driver != null) { - driver.quit(); + try { + driver.quit(); + } catch (Exception e) { + LOGGER.debug("Failed to quit the driver", e); + } } - this.stop(); + + if (vncRecordingContainer != null) { + try { + vncRecordingContainer.stop(); + } catch (Exception e) { + LOGGER.debug("Failed to stop vncRecordingContainer", e); + } + } + + super.stop(); } private void stopAndRetainRecordingForDescriptionAndSuccessState(Description description, boolean succeeded) { - File recordingFile = recordingFileFactory.recordingFileForTest(vncRecordingDirectory, description, succeeded); - LOGGER.info("Screen recordings for test {} will be stored at: {}", description.getDisplayName(), recordingFile); + final boolean shouldRecord; + switch (recordingMode) { + case RECORD_ALL: + shouldRecord = true; + break; + case RECORD_FAILING: + shouldRecord = !succeeded; + break; + default: + shouldRecord = false; + break; + } + + if (shouldRecord) { + File recordingFile = recordingFileFactory.recordingFileForTest(vncRecordingDirectory, description, succeeded); + LOGGER.info("Screen recordings for test {} will be stored at: {}", description.getDisplayName(), recordingFile); - for (VncRecordingSidekickContainer container : currentVncRecordings) { - container.stopAndRetainRecording(recordingFile); + vncRecordingContainer.saveRecordingToFile(recordingFile); } } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java index d9c76626a9e..1f8a68f857f 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java @@ -8,6 +8,7 @@ import java.util.concurrent.TimeUnit; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; /** * @@ -28,15 +29,18 @@ protected void doSimpleWebdriverTest(BrowserWebDriverContainer rule) { } @NotNull - private RemoteWebDriver setupDriverFromRule(BrowserWebDriverContainer rule) { + private static RemoteWebDriver setupDriverFromRule(BrowserWebDriverContainer rule) { RemoteWebDriver driver = rule.getWebDriver(); driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS); return driver; } - protected void doSimpleExplore(BrowserWebDriverContainer rule) { + protected static void doSimpleExplore(BrowserWebDriverContainer rule) { RemoteWebDriver driver = setupDriverFromRule(rule); driver.get("http://en.wikipedia.org/wiki/Randomness"); + + // Oh! The irony! + assertTrue("Randomness' description has the word 'pattern'", driver.findElementByPartialLinkText("pattern").isDisplayed()); } } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java index 6eba1ae478d..770e8e8bc30 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java @@ -2,6 +2,8 @@ import org.junit.Rule; import org.junit.Test; +import org.junit.experimental.runners.Enclosed; +import org.junit.runner.RunWith; import org.openqa.selenium.remote.DesiredCapabilities; import org.testcontainers.containers.BrowserWebDriverContainer; import org.testcontainers.containers.DefaultRecordingFileFactory; @@ -10,29 +12,32 @@ import static org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode.RECORD_ALL; -/** - * - */ +@RunWith(Enclosed.class) public class ChromeRecordingWebDriverContainerTest extends BaseWebDriverContainerTest { - @Rule - public BrowserWebDriverContainer chromeThatRecordsAllTests = new BrowserWebDriverContainer() - .withDesiredCapabilities(DesiredCapabilities.chrome()) - .withRecordingMode(RECORD_ALL, new File("./target/")) - .withRecordingFileFactory(new DefaultRecordingFileFactory()); + public static class ChromeThatRecordsAllTests { - @Rule - public BrowserWebDriverContainer chromeThatRecordsFailingTests = new BrowserWebDriverContainer() - .withDesiredCapabilities(DesiredCapabilities.chrome()); + @Rule + public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() + .withDesiredCapabilities(DesiredCapabilities.chrome()) + .withRecordingMode(RECORD_ALL, new File("./target/")) + .withRecordingFileFactory(new DefaultRecordingFileFactory()); - - @Test - public void recordingTestThatShouldBeRecordedButDeleted() { - doSimpleExplore(chromeThatRecordsFailingTests); + @Test + public void recordingTestThatShouldBeRecordedAndRetained() { + doSimpleExplore(chrome); + } } - @Test - public void recordingTestThatShouldBeRecordedAndRetained() { - doSimpleExplore(chromeThatRecordsAllTests); + public static class ChromeThatRecordsFailingTests { + + @Rule + public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() + .withDesiredCapabilities(DesiredCapabilities.chrome()); + + @Test + public void recordingTestThatShouldBeRecordedButDeleted() { + doSimpleExplore(chrome); + } } } From 95bbecc343fa13eb1ae8d9d2b165fda820c8cc8b Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 19 Dec 2017 12:27:34 +0000 Subject: [PATCH 0205/5615] Update changelog for 1.5.1 release --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8238ea46596..af34ade7b78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Change Log All notable changes to this project will be documented in this file. +## [1.5.1] - 2017-12-19 + ### Fixed - Fixed problem with case-sensitivity when checking internal port. ([\#524](https://github.com/testcontainers/testcontainers-java/pull/524)) - Add retry logic around checkExposedPort pre-flight check for improved robustness ([\#513](https://github.com/testcontainers/testcontainers-java/issues/513)) @@ -366,7 +368,8 @@ All notable changes to this project will be documented in this file. ## [0.9] - 2015-04-29 Initial release -[1.4.2]: https://github.com/testcontainers/testcontainers-java/releases/tag/1.5.0 +[1.5.1]: https://github.com/testcontainers/testcontainers-java/releases/tag/1.5.1 +[1.5.0]: https://github.com/testcontainers/testcontainers-java/releases/tag/1.5.0 [1.4.2]: https://github.com/testcontainers/testcontainers-java/releases/tag/1.4.3 [1.4.2]: https://github.com/testcontainers/testcontainers-java/releases/tag/1.4.2 [1.4.1]: https://github.com/testcontainers/testcontainers-java/releases/tag/1.4.1 From c38f42700fefcd995bfacfef46205eae7ec9a9e7 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Wed, 10 Jan 2018 18:59:44 +0100 Subject: [PATCH 0206/5615] Fix wrong link address for modules and GenericContainer usage (fix #538) --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index b5797d6487e..b7985f3508a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -14,7 +14,7 @@ TestContainers makes it easy to launch useful Docker containers for the duration * **Data access layer integration tests**: use a containerized instance of a MySQL, PostgreSQL or Oracle database to test your data access layer code for complete compatibility, but without requiring complex setup on developers' machines and safe in the knowledge that your tests will always start with a known DB state. Any other database type that can be containerized can also be used. * **Application integration tests**: for running your application in a short-lived test mode with dependencies, such as databases, message queues or web servers. * **UI/Acceptance tests**: use containerized web browsers, compatible with Selenium, for conducting automated UI tests. Each test can get a fresh instance of the browser, with no browser state, plugin variations or automated browser upgrades to worry about. And you get a video recording of each test session, or just each session where tests failed. - * **Much more!** Check out the various [contributed modules](modules.md) or create your own custom container classes using [`GenericContainer`](generic_containers.md) as a base. + * **Much more!** Check out the various [contributed modules](https://github.com/testcontainers) or create your own custom container classes using [`GenericContainer`](usage/generic_containers.md) as a base. ## Who is using TestContainers? From 4e79301487b71fcadb7b505ddd5bc19caf2bccb7 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Wed, 10 Jan 2018 19:00:12 +0100 Subject: [PATCH 0207/5615] Remove obsolete comment about having to HTTP/HTTPS docker connection --- docs/usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage.md b/docs/usage.md index f6b381c6c8d..8a166c6b1bc 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -11,7 +11,7 @@ If you want to use TestContainers on Windows you can try the [alpha release](usa Testcontainers will try to connect to a Docker daemon using the following strategies in order: * Environment variables: - * `DOCKER_HOST` (this should be set to an HTTP/HTTPS connection rather than a unix socket at present) + * `DOCKER_HOST` * `DOCKER_TLS_VERIFY` * `DOCKER_CERT_PATH` * Defaults: From fcb44812ea31b76ad7e2dfb64bb0a8aaec9f758f Mon Sep 17 00:00:00 2001 From: jochenhebbrecht Date: Fri, 12 Jan 2018 10:40:33 +0100 Subject: [PATCH 0208/5615] Make sure the Docker Host IP address is retrieved correctly when running inside docker (#541) * fix: introduce 'withFollowStream' and 'awaitCompletion' methods to await the completion of one-off command * Fixed retrieval of Docker host IP when running inside Docker (#479) * fix: withStdErr() is not needed - withTailAll() is not needed (default: tail=all) * Update CHANGELOG.md --- CHANGELOG.md | 3 +++ .../dockerclient/DockerClientConfigUtils.java | 13 +++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af34ade7b78..6bf12b52095 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # Change Log All notable changes to this project will be documented in this file. +### Fixed +- Fixed retrieval of Docker host IP when running inside Docker. ([\#479](https://github.com/testcontainers/testcontainers-java/issues/479)) + ## [1.5.1] - 2017-12-19 ### Fixed diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java index 979b8f93a4e..7b7c948ec4c 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java @@ -9,6 +9,8 @@ import java.io.File; import java.util.Optional; +import static java.util.concurrent.TimeUnit.SECONDS; + @Slf4j public class DockerClientConfigUtils { @@ -23,10 +25,13 @@ public class DockerClientConfigUtils { cmd -> cmd.withCmd("sh", "-c", "ip route|awk '/default/ { print $3 }'"), (client, id) -> { try { - return client.logContainerCmd(id) - .withStdOut(true) - .exec(new LogToStringContainerCallback()) - .toString(); + LogToStringContainerCallback loggingCallback = new LogToStringContainerCallback(); + client.logContainerCmd(id).withStdOut(true) + .withFollowStream(true) + .exec(loggingCallback) + .awaitStarted(); + loggingCallback.awaitCompletion(3, SECONDS); + return loggingCallback.toString(); } catch (Exception e) { log.warn("Can't parse the default gateway IP", e); return null; From 35a6ddea5bce4183e03753e88bc07018756137a6 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 21 Jan 2018 20:32:24 +0100 Subject: [PATCH 0209/5615] Update circle.yml (#549) * Update circle.yml * Update circle.yml --- circle.yml | 43 +++++++------------------------------------ 1 file changed, 7 insertions(+), 36 deletions(-) diff --git a/circle.yml b/circle.yml index 9cb5cdce9fa..cb0043ef053 100644 --- a/circle.yml +++ b/circle.yml @@ -1,36 +1,7 @@ -machine: - java: - version: oraclejdk8 - environment: - DOCKER_HOST: tcp://127.0.0.1:2376 - DOCKER_TLS_VERIFY: 0 - MAVEN_OPTS: -Xmx512m - _JAVA_OPTIONS: "-Xms512m -Xmx1024m" - services: - - docker - pre: - - sudo curl -L -o /usr/bin/docker 'http://s3-external-1.amazonaws.com/circle-downloads/docker-1.9.1-circleci'; sudo chmod 0755 /usr/bin/docker; true -dependencies: - override: - - "sudo docker -d -e lxc -s btrfs -H 0.0.0.0:2376 -H unix:///var/run/docker.sock --log-level=debug --debug=true": - background: true - - mvn -B install -DskipTests -test: - override: - - mvn -B test - # Run Docker-in-Docker tests - - | - DOCKER_HOST=unix:///var/run/docker.sock DOCKER_TLS_VERIFY= docker run --rm \ - -v "$HOME/.m2":/root/.m2/ \ - -v /var/run/docker.sock:/var/run/docker.sock \ - -v "$(pwd)":"$(pwd)" \ - -w "$(pwd)" \ - openjdk:8-jre \ - ./mvnw -B -pl core test -Dtest=*GenericContainerRuleTest - - mvn -B test -f shade-test/pom.xml - post: - - mkdir -p $CIRCLE_TEST_REPORTS/junit/ - - find . -type f -regex ".*/target/surefire-reports/.*xml" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \; -notify: - webhooks: - - url: https://webhooks.gitter.im/e/1bf1c6aa3b2aa8408c5f +version: 2 +executorType: machine +jobs: + build: + steps: + - checkout + - run: ./mvnw -B test From 8d5f7a9d9b3e5a2ec26bfa1c0790f9f0959b79bf Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sat, 27 Jan 2018 11:48:55 +0100 Subject: [PATCH 0210/5615] Add Kafka module (#546) * Add Kafka module * Replace AmbassadorContainer with SocatContainer in DockerComposeContainer * make it possible to use KafkaContainer with external Zookeeper * fix typo * fix Kafka tests * Add to CHANGELOG.md, name SocatContainer, add listeners explanation comment to KafkaContainer * listen on alias * rename myNetworkAlias -> networkAlias --- CHANGELOG.md | 3 + .../containers/AmbassadorContainer.java | 3 + .../containers/DockerComposeContainer.java | 72 ++++------- .../testcontainers/containers/Network.java | 2 +- .../containers/SocatContainer.java | 42 +++++++ .../utility/TestcontainersConfiguration.java | 4 + modules/kafka/pom.xml | 45 +++++++ .../KafkaContainer.java | 82 +++++++++++++ .../main/resources/tc-zookeeper.properties | 3 + .../containers/KafkaContainerTest.java | 116 ++++++++++++++++++ .../kafka/src/test/resources/logback-test.xml | 18 +++ pom.xml | 1 + 12 files changed, 343 insertions(+), 48 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/containers/SocatContainer.java create mode 100644 modules/kafka/pom.xml create mode 100644 modules/kafka/src/main/java/org.testcontainers.containers/KafkaContainer.java create mode 100644 modules/kafka/src/main/resources/tc-zookeeper.properties create mode 100644 modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java create mode 100644 modules/kafka/src/test/resources/logback-test.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bf12b52095..54f50a67436 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file. ### Fixed - Fixed retrieval of Docker host IP when running inside Docker. ([\#479](https://github.com/testcontainers/testcontainers-java/issues/479)) +### Changed +- Added Kafka module ([\#546](https://github.com/testcontainers/testcontainers-java/pull/546)) + ## [1.5.1] - 2017-12-19 ### Fixed diff --git a/core/src/main/java/org/testcontainers/containers/AmbassadorContainer.java b/core/src/main/java/org/testcontainers/containers/AmbassadorContainer.java index 756b88d05ab..0b86821d293 100644 --- a/core/src/main/java/org/testcontainers/containers/AmbassadorContainer.java +++ b/core/src/main/java/org/testcontainers/containers/AmbassadorContainer.java @@ -15,9 +15,12 @@ * An ambassador container is used as a TCP proxy, enabling any TCP port of another linked container to be exposed * publicly, even if that container does not make the port public itself. The richnorth/ambassador:latest * container is used (based on HAProxy). + * + * @deprecated use {@link SocatContainer} */ @EqualsAndHashCode(callSuper = false) @Data +@Deprecated public class AmbassadorContainer> extends GenericContainer { private final String otherContainerName; diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index d4061d9e499..b718351c86b 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -11,9 +11,6 @@ import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.SystemUtils; import org.junit.runner.Description; -import org.rnorth.ducttape.ratelimits.RateLimiter; -import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; -import org.rnorth.ducttape.unreliables.Unreliables; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.profiler.Profiler; @@ -27,8 +24,15 @@ import org.zeroturnaround.exec.stream.slf4j.Slf4jStream; import java.io.File; -import java.util.*; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkArgument; @@ -46,7 +50,6 @@ public class DockerComposeContainer> e * Random identifier which will become part of spawned containers names, so we can shut them down */ private final String identifier; - private final Map ambassadorContainers = new HashMap<>(); private final List composeFiles; private final Set spawnedContainerIds = new HashSet<>(); private final Set spawnedNetworkIds = new HashSet<>(); @@ -56,6 +59,10 @@ public class DockerComposeContainer> e private boolean pull = true; private boolean tailChildContainers; + private final AtomicInteger nextAmbassadorPort = new AtomicInteger(2000); + private final Map> ambassadorPortMappings = new ConcurrentHashMap<>(); + private final SocatContainer ambassadorContainer = new SocatContainer(); + private static final Object MUTEX = new Object(); /** @@ -64,12 +71,6 @@ public class DockerComposeContainer> e */ private Map env = new HashMap<>(); - private static final RateLimiter AMBASSADOR_CREATION_RATE_LIMITER = RateLimiterBuilder - .newBuilder() - .withRate(6, TimeUnit.MINUTES) - .withConstantThroughput() - .build(); - @Deprecated public DockerComposeContainer(File composeFile, String identifier) { this(identifier, composeFile); @@ -201,31 +202,9 @@ private List listChildContainers() { } private void startAmbassadorContainers(Profiler profiler) { - for (final Map.Entry address : ambassadorContainers.entrySet()) { - - try { - // Start any ambassador containers we need - profiler.start("Ambassador container startup"); - - final AmbassadorContainer ambassadorContainer = address.getValue(); - Unreliables.retryUntilSuccess(120, TimeUnit.SECONDS, () -> { - - AMBASSADOR_CREATION_RATE_LIMITER.doWhenReady(() -> { - Profiler localProfiler = profiler.startNested("Ambassador container: " + ambassadorContainer.getContainerName()); - - localProfiler.start("Start ambassador container"); - - ambassadorContainer.start(); - }); - - return null; - }); - } catch (Exception e) { - logger().warn("Exception during ambassador container startup!", e); - } finally { - profiler.stop().log(); - } - } + profiler.start("Ambassador container startup"); + ambassadorContainer.start(); + profiler.stop().log(); } private Logger logger() { @@ -237,8 +216,8 @@ public void finished(Description description) { synchronized (MUTEX) { - // shut down all the ambassador containers - ambassadorContainers.forEach((String address, AmbassadorContainer container) -> container.stop()); + // shut down the ambassador container + ambassadorContainer.stop(); // Kill the services using docker-compose try { @@ -270,7 +249,7 @@ public SELF withExposedService(String serviceName, int servicePort) { } /* - * For every service/port pair that needs to be exposed, we have to start an 'ambassador container'. + * For every service/port pair that needs to be exposed, we register a target on an 'ambassador container'. * * The ambassador container's role is to link (within the Docker network) to one of the * compose services, and proxy TCP network I/O out to a port that the ambassador container @@ -282,13 +261,12 @@ public SELF withExposedService(String serviceName, int servicePort) { * {@link GenericContainer} should ensure that the ambassador container is on the same network * as the rest of the compose environment. */ - AmbassadorContainer ambassadorContainer = - new AmbassadorContainer<>(new FutureContainer(this.identifier + "_" + serviceName), serviceName, servicePort) - .withEnv(env); - - // Ambassador containers will all be started together after docker compose has started - ambassadorContainers.put(serviceName + ":" + servicePort, ambassadorContainer); + // Ambassador container will be started together after docker compose has started + int ambassadorPort = nextAmbassadorPort.getAndIncrement(); + ambassadorPortMappings.computeIfAbsent(serviceName, __ -> new ConcurrentHashMap<>()).put(servicePort, ambassadorPort); + ambassadorContainer.withTarget(ambassadorPort, serviceName, servicePort); + ambassadorContainer.addLink(new FutureContainer(this.identifier + "_" + serviceName), serviceName); return self(); } @@ -307,7 +285,7 @@ public DockerComposeContainer withExposedService(String serviceName, int instanc * @return a host IP address or hostname that can be used for accessing the service container. */ public String getServiceHost(String serviceName, Integer servicePort) { - return ambassadorContainers.get(serviceName + ":" + servicePort).getContainerIpAddress(); + return ambassadorContainer.getContainerIpAddress(); } /** @@ -321,7 +299,7 @@ public String getServiceHost(String serviceName, Integer servicePort) { * @return a port that can be used for accessing the service container. */ public Integer getServicePort(String serviceName, Integer servicePort) { - return ambassadorContainers.get(serviceName + ":" + servicePort).getMappedPort(servicePort); + return ambassadorContainer.getMappedPort(ambassadorPortMappings.get(serviceName).get(servicePort)); } public SELF withScaledService(String serviceBaseName, int numInstances) { diff --git a/core/src/main/java/org/testcontainers/containers/Network.java b/core/src/main/java/org/testcontainers/containers/Network.java index dc848b02b8c..c2ea8a7b993 100644 --- a/core/src/main/java/org/testcontainers/containers/Network.java +++ b/core/src/main/java/org/testcontainers/containers/Network.java @@ -52,7 +52,7 @@ class NetworkImpl extends ExternalResource implements Network { private final AtomicBoolean initialized = new AtomicBoolean(); @Override - public String getId() { + public synchronized String getId() { if (initialized.compareAndSet(false, true)) { id = create(); } diff --git a/core/src/main/java/org/testcontainers/containers/SocatContainer.java b/core/src/main/java/org/testcontainers/containers/SocatContainer.java new file mode 100644 index 00000000000..5949a14490d --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/SocatContainer.java @@ -0,0 +1,42 @@ +package org.testcontainers.containers; + +import org.testcontainers.utility.Base58; +import org.testcontainers.utility.TestcontainersConfiguration; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * A socat container is used as a TCP proxy, enabling any TCP port of another container to be exposed + * publicly, even if that container does not make the port public itself. + */ +public class SocatContainer extends GenericContainer { + + private final Map targets = new HashMap<>(); + + public SocatContainer() { + super(TestcontainersConfiguration.getInstance().getSocatContainerImage()); + withCreateContainerCmdModifier(it -> it.withEntrypoint("/bin/sh")); + withCreateContainerCmdModifier(it -> it.withName("testcontainers-socat-" + Base58.randomString(8))); + } + + public SocatContainer withTarget(int exposedPort, String host) { + return withTarget(exposedPort, host, exposedPort); + } + + public SocatContainer withTarget(int exposedPort, String host, int internalPort) { + addExposedPort(exposedPort); + targets.put(exposedPort, String.format("%s:%s", host, internalPort)); + return self(); + } + + @Override + protected void configure() { + withCommand("-c", + targets.entrySet().stream() + .map(entry -> "socat TCP-LISTEN:" + entry.getKey() + ",fork,reuseaddr TCP:" + entry.getValue()) + .collect(Collectors.joining(" & ")) + ); + } +} \ No newline at end of file diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index bb7acb0bc9e..890cd0d4279 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -30,6 +30,10 @@ public String getAmbassadorContainerImage() { return (String) properties.getOrDefault("ambassador.container.image", "richnorth/ambassador:latest"); } + public String getSocatContainerImage() { + return (String) properties.getOrDefault("socat.container.image", "alpine/socat:latest"); + } + public String getVncRecordedContainerImage() { return (String) properties.getOrDefault("vncrecorder.container.image", "richnorth/vnc-recorder:latest"); } diff --git a/modules/kafka/pom.xml b/modules/kafka/pom.xml new file mode 100644 index 00000000000..09a20d01fe0 --- /dev/null +++ b/modules/kafka/pom.xml @@ -0,0 +1,45 @@ + + + 4.0.0 + + + org.testcontainers + testcontainers-parent + 0-SNAPSHOT + ../../pom.xml + + + kafka + TestContainers :: Apache Kafka + + + + ${project.groupId} + testcontainers + ${project.version} + + + + org.apache.kafka + kafka-clients + 1.0.0 + test + + + + org.assertj + assertj-core + 3.8.0 + test + + + + com.google.guava + guava + 23.0 + test + + + + \ No newline at end of file diff --git a/modules/kafka/src/main/java/org.testcontainers.containers/KafkaContainer.java b/modules/kafka/src/main/java/org.testcontainers.containers/KafkaContainer.java new file mode 100644 index 00000000000..228dc8a3399 --- /dev/null +++ b/modules/kafka/src/main/java/org.testcontainers.containers/KafkaContainer.java @@ -0,0 +1,82 @@ +package org.testcontainers.containers; + +import org.testcontainers.utility.Base58; + +import java.util.stream.Stream; + +public class KafkaContainer extends GenericContainer { + + public static final int KAFKA_PORT = 9092; + + public static final int ZOOKEEPER_PORT = 2181; + + protected String externalZookeeperConnect = null; + + protected SocatContainer proxy; + + public KafkaContainer() { + this("4.0.0"); + } + + public KafkaContainer(String confluentPlatformVersion) { + super("confluentinc/cp-kafka:" + confluentPlatformVersion); + + withNetwork(Network.newNetwork()); + String networkAlias = "kafka-" + Base58.randomString(6); + withNetworkAliases(networkAlias); + withExposedPorts(KAFKA_PORT); + + // Use two listeners with different names, it will force Kafka to communicate with itself via internal + // listener when KAFKA_INTER_BROKER_LISTENER_NAME is set, otherwise Kafka will try to use the advertised listener + withEnv("KAFKA_LISTENERS", "PLAINTEXT://0.0.0.0:9092,BROKER://" + networkAlias + ":9093"); + withEnv("KAFKA_LISTENER_SECURITY_PROTOCOL_MAP", "BROKER:PLAINTEXT,PLAINTEXT:PLAINTEXT"); + withEnv("KAFKA_INTER_BROKER_LISTENER_NAME", "BROKER"); + + withEnv("KAFKA_BROKER_ID", "1"); + withEnv("KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR", "1"); + withEnv("KAFKA_OFFSETS_TOPIC_NUM_PARTITIONS", "1"); + withEnv("KAFKA_LOG_FLUSH_INTERVAL_MESSAGES", Long.MAX_VALUE + ""); + } + + public KafkaContainer withEmbeddedZookeeper() { + externalZookeeperConnect = null; + return self(); + } + + public KafkaContainer withExternalZookeeper(String connectString) { + externalZookeeperConnect = connectString; + return self(); + } + + public String getBootstrapServers() { + return String.format("PLAINTEXT://%s:%s", proxy.getContainerIpAddress(), proxy.getFirstMappedPort()); + } + + @Override + public void start() { + String networkAlias = getNetworkAliases().get(0); + proxy = new SocatContainer() + .withNetwork(getNetwork()) + .withTarget(9092, networkAlias) + .withTarget(2181, networkAlias); + + proxy.start(); + withEnv("KAFKA_ADVERTISED_LISTENERS", "BROKER://" + networkAlias + ":9093,PLAINTEXT://" + proxy.getContainerIpAddress() + ":" + proxy.getFirstMappedPort()); + + if (externalZookeeperConnect != null) { + withEnv("KAFKA_ZOOKEEPER_CONNECT", externalZookeeperConnect); + } else { + addExposedPort(ZOOKEEPER_PORT); + withEnv("KAFKA_ZOOKEEPER_CONNECT", "localhost:2181"); + withClasspathResourceMapping("tc-zookeeper.properties", "/zookeeper.properties", BindMode.READ_ONLY); + withCommand("sh", "-c", "zookeeper-server-start /zookeeper.properties & /etc/confluent/docker/run"); + } + + super.start(); + } + + @Override + public void stop() { + Stream.of(super::stop, proxy::stop).parallel().forEach(Runnable::run); + } +} \ No newline at end of file diff --git a/modules/kafka/src/main/resources/tc-zookeeper.properties b/modules/kafka/src/main/resources/tc-zookeeper.properties new file mode 100644 index 00000000000..f0cb437927d --- /dev/null +++ b/modules/kafka/src/main/resources/tc-zookeeper.properties @@ -0,0 +1,3 @@ +clientPort=2181 +dataDir=/var/lib/zookeeper/data +dataLogDir=/var/lib/zookeeper/log \ No newline at end of file diff --git a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java new file mode 100644 index 00000000000..f88cb1703ed --- /dev/null +++ b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java @@ -0,0 +1,116 @@ +package org.testcontainers.containers; + +import com.google.common.collect.ImmutableMap; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.apache.kafka.common.serialization.StringSerializer; +import org.junit.Test; +import org.rnorth.ducttape.unreliables.Unreliables; + +import java.util.Arrays; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + +public class KafkaContainerTest { + + @Test + public void testUsage() throws Exception { + try (KafkaContainer kafka = new KafkaContainer()) { + kafka.start(); + testKafkaFunctionality(kafka.getBootstrapServers()); + } + } + + @Test + public void testExternalZookeeperWithKafkaNetwork() throws Exception { + try ( + KafkaContainer kafka = new KafkaContainer() + .withExternalZookeeper("zookeeper:2181"); + + GenericContainer zookeeper = new GenericContainer("confluentinc/cp-zookeeper:4.0.0") + .withNetwork(kafka.getNetwork()) + .withNetworkAliases("zookeeper") + .withEnv("ZOOKEEPER_CLIENT_PORT", "2181"); + ) { + Stream.of(kafka, zookeeper).parallel().forEach(GenericContainer::start); + + testKafkaFunctionality(kafka.getBootstrapServers()); + } + } + + @Test + public void testExternalZookeeperWithExternalNetwork() throws Exception { + try ( + Network network = Network.newNetwork(); + + KafkaContainer kafka = new KafkaContainer() + .withNetwork(network) + .withExternalZookeeper("zookeeper:2181"); + + GenericContainer zookeeper = new GenericContainer("confluentinc/cp-zookeeper:4.0.0") + .withNetwork(network) + .withNetworkAliases("zookeeper") + .withEnv("ZOOKEEPER_CLIENT_PORT", "2181"); + ) { + Stream.of(kafka, zookeeper).parallel().forEach(GenericContainer::start); + + testKafkaFunctionality(kafka.getBootstrapServers()); + } + } + + protected void testKafkaFunctionality(String bootstrapServers) throws Exception { + try ( + KafkaProducer producer = new KafkaProducer<>( + ImmutableMap.of( + ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers, + ProducerConfig.CLIENT_ID_CONFIG, UUID.randomUUID().toString() + ), + new StringSerializer(), + new StringSerializer() + ); + + KafkaConsumer consumer = new KafkaConsumer<>( + ImmutableMap.of( + ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers, + ConsumerConfig.GROUP_ID_CONFIG, "tc-" + UUID.randomUUID(), + ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest" + ), + new StringDeserializer(), + new StringDeserializer() + ); + ) { + String topicName = "messages"; + consumer.subscribe(Arrays.asList(topicName)); + + producer.send(new ProducerRecord<>(topicName, "testcontainers", "rulezzz")).get(); + + Unreliables.retryUntilTrue(10, TimeUnit.SECONDS, () -> { + ConsumerRecords records = consumer.poll(100); + + if (records.isEmpty()) { + return false; + } + + assertThat(records) + .hasSize(1) + .extracting(ConsumerRecord::topic, ConsumerRecord::key, ConsumerRecord::value) + .containsExactly(tuple(topicName, "testcontainers", "rulezzz")); + + return true; + }); + + consumer.unsubscribe(); + } + } + +} \ No newline at end of file diff --git a/modules/kafka/src/test/resources/logback-test.xml b/modules/kafka/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..7bd6a94d827 --- /dev/null +++ b/modules/kafka/src/test/resources/logback-test.xml @@ -0,0 +1,18 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index bf158718386..c73552febcf 100644 --- a/pom.xml +++ b/pom.xml @@ -165,6 +165,7 @@ modules/postgresql modules/selenium modules/nginx + modules/kafka modules/jdbc-test From b6e38a1fb259dacaa5572f73c2e336e634761438 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sat, 27 Jan 2018 11:55:49 +0100 Subject: [PATCH 0211/5615] fix Selenium with custom image but without VNC (#528) --- .../containers/BrowserWebDriverContainer.java | 9 ++++++++- .../junit/SpecificImageNameWebDriverContainerTest.java | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index 6e2375f5082..9a527d998dd 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -79,6 +79,8 @@ public BrowserWebDriverContainer(String dockerImageName) { this(); super.setDockerImageName(dockerImageName); this.customImageNameIsSet = true; + // We have to force SKIP mode for the recording by default because we don't know if the image has VNC or not + recordingMode = VncRecordingMode.SKIP; } @@ -90,7 +92,12 @@ public SELF withDesiredCapabilities(DesiredCapabilities desiredCapabilities) { @NotNull @Override protected Set getLivenessCheckPorts() { - return ImmutableSet.of(getMappedPort(SELENIUM_PORT), getMappedPort(VNC_PORT)); + Integer seleniumPort = getMappedPort(SELENIUM_PORT); + if (recordingMode == VncRecordingMode.SKIP) { + return ImmutableSet.of(seleniumPort); + } else { + return ImmutableSet.of(seleniumPort, getMappedPort(VNC_PORT)); + } } @Override diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java index 1db034502ac..c0392371f0c 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java @@ -13,7 +13,7 @@ public class SpecificImageNameWebDriverContainerTest extends BaseWebDriverContainerTest { @Rule - public BrowserWebDriverContainer firefox = new BrowserWebDriverContainer("selenium/standalone-firefox-debug:2.53.1-beryllium") + public BrowserWebDriverContainer firefox = new BrowserWebDriverContainer("selenium/standalone-firefox:2.53.1-beryllium") .withDesiredCapabilities(DesiredCapabilities.firefox()); @Test From 39d5275c574af39cbe61506294360e741b29c31a Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sat, 27 Jan 2018 12:13:41 +0100 Subject: [PATCH 0212/5615] Improve the usability of GenericContainer (#550) * Improve the usability of GenericContainer * Add withEnv with mapper * Document in CHANGELOG.md, add deprecation comments --- CHANGELOG.md | 3 ++ .../testcontainers/containers/Container.java | 47 +++++++++++++++++++ .../containers/GenericContainer.java | 35 ++++++++++++-- .../junit/GenericContainerRuleTest.java | 5 +- 4 files changed, 85 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54f50a67436..2f9e9946c84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ All notable changes to this project will be documented in this file. ### Changed - Added Kafka module ([\#546](https://github.com/testcontainers/testcontainers-java/pull/546)) +- Environment variables are now stored as Map instead of List ([\#550](https://github.com/testcontainers/testcontainers-java/pull/550)) +- Added `withEnv(String name, Function, String> mapper)` with optional previous value ([\#550](https://github.com/testcontainers/testcontainers-java/pull/550)) +- Added `withFileSystemBind` overloaded method with `READ_WRITE` file mode by default ([\#550](https://github.com/testcontainers/testcontainers-java/pull/550)) ## [1.5.1] - 2017-12-19 diff --git a/core/src/main/java/org/testcontainers/containers/Container.java b/core/src/main/java/org/testcontainers/containers/Container.java index 2d5158e1833..3b877dfde0d 100644 --- a/core/src/main/java/org/testcontainers/containers/Container.java +++ b/core/src/main/java/org/testcontainers/containers/Container.java @@ -17,8 +17,10 @@ import java.time.Duration; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.Future; import java.util.function.Consumer; +import java.util.function.Function; public interface Container> extends LinkableContainer { @@ -135,6 +137,17 @@ default void addFileSystemBind(final String hostPath, final String containerPath */ SELF waitingFor(@NonNull WaitStrategy waitStrategy); + /** + * Adds a file system binding. + * + * @param hostPath the file system path on the host + * @param containerPath the file system path inside the container + * @return this + */ + default SELF withFileSystemBind(String hostPath, String containerPath) { + return withFileSystemBind(hostPath, containerPath, BindMode.READ_WRITE); + } + /** * Adds a file system binding. * @@ -171,6 +184,18 @@ default void addFileSystemBind(final String hostPath, final String containerPath */ SELF withEnv(String key, String value); + /** + * Add an environment variable to be passed to the container. + * + * @param key environment variable key + * @param mapper environment variable value mapper, accepts old value as an argument + * @return this + */ + default SELF withEnv(String key, Function, String> mapper) { + Optional oldValue = Optional.ofNullable(getEnvMap().get(key)); + return withEnv(key, mapper.apply(oldValue)); + } + /** * Add environment variables to be passed to the container. * @@ -383,6 +408,11 @@ default Integer getFirstMappedPort() { */ SELF withLogConsumer(Consumer consumer); + /** + * + * @deprecated please use {@code org.testcontainers.DockerClientFactory.instance().client().infoCmd().exec()} + */ + @Deprecated Info fetchDockerDaemonInfo() throws IOException; /** @@ -438,8 +468,15 @@ ExecResult execInContainer(Charset outputCharset, String... command) Future getImage(); + /** + * + * @deprecated use getEnvMap + */ + @Deprecated List getEnv(); + Map getEnvMap(); + String[] getCommandParts(); List getBinds(); @@ -452,12 +489,22 @@ ExecResult execInContainer(Charset outputCharset, String... command) DockerClient getDockerClient(); + /** + * + * @deprecated please use {@code org.testcontainers.DockerClientFactory.instance().client().infoCmd().exec()} + */ + @Deprecated Info getDockerDaemonInfo(); String getContainerId(); String getContainerName(); + /** + * + * @deprecated please use {@code org.testcontainers.DockerClientFactory.instance().client().inspectContainerCmd(container.getContainerId()).exec()} + */ + @Deprecated InspectContainerResponse getContainerInfo(); void setExposedPorts(List exposedPorts); diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 9cd915ffe35..932c00e4c9a 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -92,7 +92,7 @@ public class GenericContainer> private Future image; @NonNull - private List env = new ArrayList<>(); + private Map env = new HashMap<>(); @NonNull private String[] commandParts = new String[0]; @@ -396,7 +396,8 @@ private void applyConfiguration(CreateContainerCmd createCommand) { createCommand.withCmd(commandParts); } - String[] envArray = env.stream() + String[] envArray = env.entrySet().stream() + .map(it -> it.getKey() + "=" + it.getValue()) .toArray(String[]::new); createCommand.withEnv(envArray); @@ -533,12 +534,37 @@ public void setCommand(@NonNull String... commandParts) { this.commandParts = commandParts; } + @Override + public Map getEnvMap() { + return env; + } + + /** + * {@inheritDoc} + */ + @Override + public List getEnv() { + return env.entrySet().stream() + .map(it -> it.getKey() + "=" + it.getValue()) + .collect(Collectors.toList()); + } + + @Override + public void setEnv(List env) { + this.env = env.stream() + .map(it -> it.split("=")) + .collect(Collectors.toMap( + it -> it[0], + it -> it[1] + )); + } + /** * {@inheritDoc} */ @Override public void addEnv(String key, String value) { - env.add(key + "=" + value); + env.put(key, value); } /** @@ -886,6 +912,9 @@ public SELF withLogConsumer(Consumer consumer) { return self(); } + /** + * {@inheritDoc} + */ @Override public synchronized Info fetchDockerDaemonInfo() throws IOException { diff --git a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java index 61dfea7d9d8..677f97d67d9 100644 --- a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java +++ b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java @@ -81,9 +81,10 @@ public static void setupContent() throws FileNotFoundException { * dirty way for testing. */ @ClassRule - public static GenericContainer alpineEnvVar = new GenericContainer("alpine:3.2") + public static GenericContainer alpineEnvVar = new GenericContainer<>("alpine:3.2") .withExposedPorts(80) - .withEnv("MAGIC_NUMBER", "42") + .withEnv("MAGIC_NUMBER", "4") + .withEnv("MAGIC_NUMBER", oldValue -> oldValue.orElse("") + "2") .withCommand("/bin/sh", "-c", "while true; do echo \"$MAGIC_NUMBER\" | nc -l -p 80; done"); /** From 283e2f072e985322fcbde3283929e12d27f2ece7 Mon Sep 17 00:00:00 2001 From: Babur Date: Sat, 27 Jan 2018 13:34:34 +0100 Subject: [PATCH 0213/5615] Add CircleCI 2.0 configuration example (#556) * Add CircleCI 2.0 configuration example * Adjust docs according to comments * Adjust docs according to comments --- docs/ci/ci.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/ci/ci.md b/docs/ci/ci.md index 25d11eb2c28..1c626451a55 100644 --- a/docs/ci/ci.md +++ b/docs/ci/ci.md @@ -37,3 +37,22 @@ In this job we run in a multiline command a docker container: * use the root user if you are running a Shared Runner +## CircleCI 2.0 + +Your CircleCI configuration should use a dedicated VM for testcontainers to work. You can achieve this by specifying the +executor type in your `.circleci/config.yml` to be `machine` instead of the default `docker` executor ( see [Choosing an Executor Type](https://circleci.com/docs/2.0/executor-types/) for more info ). + +Here is a sample CircleCI configuration that does a checkout of a project and runs maven: + +```yml +# Check https://circleci.com/docs/2.0/language-java/ for more details +# +version: 2 +executorType: machine +jobs: + build: + steps: + - checkout + + - run: mvn -B clean install +``` From 2dc109f2988221c1a9b8fb3791a03ca0c5dad542 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sat, 27 Jan 2018 17:27:10 +0100 Subject: [PATCH 0214/5615] Death Note :) (#545) * Start a helper "Ryuk" container to make sure that we clean the containers after the execution even if the JVM was "kill -9"ed * fix DockerComposeContainer.java * always encode filters, fix binds * use checkAndPullImage, add synchronized to the initial filter adding, update Ryuk image to 0.2.2 with timeout fix * CR fixes, fix DockerClientFactoryTest, set shutdown hook lazily, rename REGISTERED_FILTERS -> DEATH_NOTE for more fun :) * CHANGELOG.md * expand "tc" to "testcontainers", log the purpose of Ryuk * fix after merge --- CHANGELOG.md | 1 + .../testcontainers/DockerClientFactory.java | 83 ++++------ .../containers/DockerComposeContainer.java | 94 +++++------ .../containers/GenericContainer.java | 9 +- .../testcontainers/containers/Network.java | 11 +- .../utility/ResourceReaper.java | 152 +++++++++++++++++- .../utility/TestcontainersConfiguration.java | 4 + .../DockerClientFactoryTest.java | 37 +++-- 8 files changed, 261 insertions(+), 130 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f9e9946c84..aef8cff36cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. ### Changed - Added Kafka module ([\#546](https://github.com/testcontainers/testcontainers-java/pull/546)) +- Added "Death Note" to track & kill spawned containers even if the JVM was "kill -9"ed ([\#545](https://github.com/testcontainers/testcontainers-java/pull/545)) - Environment variables are now stored as Map instead of List ([\#550](https://github.com/testcontainers/testcontainers-java/pull/550)) - Added `withEnv(String name, Function, String> mapper)` with optional previous value ([\#550](https://github.com/testcontainers/testcontainers-java/pull/550)) - Added `withFileSystemBind` overloaded method with `READ_WRITE` file mode by default ([\#550](https://github.com/testcontainers/testcontainers-java/pull/550)) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index c7568fd8b40..a2177c886a6 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -2,35 +2,32 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.CreateContainerCmd; -import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.exception.InternalServerErrorException; import com.github.dockerjava.api.exception.NotFoundException; import com.github.dockerjava.api.model.*; import com.github.dockerjava.core.command.ExecStartResultCallback; import com.github.dockerjava.core.command.PullImageResultCallback; import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableMap; import lombok.Synchronized; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.io.IOUtils; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; -import org.rnorth.ducttape.unreliables.Unreliables; import org.rnorth.visibleassertions.VisibleAssertions; -import org.testcontainers.dockerclient.*; +import org.testcontainers.dockerclient.DockerClientProviderStrategy; +import org.testcontainers.dockerclient.DockerMachineClientProviderStrategy; import org.testcontainers.utility.ComparableVersion; -import org.testcontainers.utility.MountableFile; +import org.testcontainers.utility.ResourceReaper; import org.testcontainers.utility.TestcontainersConfiguration; import java.io.ByteArrayOutputStream; -import java.io.IOException; import java.io.InputStream; -import java.net.Socket; -import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.ServiceLoader; -import java.util.concurrent.TimeUnit; +import java.util.UUID; import java.util.function.BiFunction; import java.util.function.Consumer; @@ -42,12 +39,22 @@ @Slf4j public class DockerClientFactory { + public static final String TESTCONTAINERS_LABEL = DockerClientFactory.class.getPackage().getName(); + public static final String TESTCONTAINERS_SESSION_ID_LABEL = TESTCONTAINERS_LABEL + ".sessionId"; + + public static final String SESSION_ID = UUID.randomUUID().toString(); + + public static final Map DEFAULT_LABELS = ImmutableMap.of( + TESTCONTAINERS_LABEL, "true", + TESTCONTAINERS_SESSION_ID_LABEL, SESSION_ID + ); + private static final String TINY_IMAGE = TestcontainersConfiguration.getInstance().getTinyImage(); private static DockerClientFactory instance; // Cached client configuration private DockerClientProviderStrategy strategy; - private boolean preconditionsChecked = false; + private boolean initialized = false; private String activeApiVersion; private String activeExecutionDriver; @@ -95,7 +102,7 @@ public DockerClient client() { log.info("Docker host IP address is {}", hostIpAddress); DockerClient client = strategy.getClient(); - if (!preconditionsChecked) { + if (!initialized) { Info dockerInfo = client.infoCmd().exec(); Version version = client.versionCmd().exec(); activeApiVersion = version.getApiVersion(); @@ -106,30 +113,19 @@ public DockerClient client() { " Operating System: " + dockerInfo.getOperatingSystem() + "\n" + " Total Memory: " + dockerInfo.getMemTotal() / (1024 * 1024) + " MB"); - if (!TestcontainersConfiguration.getInstance().isDisableChecks()) { - VisibleAssertions.info("Checking the system..."); - - checkDockerVersion(version.getVersion()); - - MountableFile mountableFile = MountableFile.forClasspathResource(this.getClass().getName().replace(".", "/") + ".class"); + String ryukContainerId = ResourceReaper.start(hostIpAddress, client); + log.info("Ryuk started - will monitor and terminate Testcontainers containers on JVM exit"); - runInsideDocker( - client, - cmd -> cmd - .withCmd("/bin/sh", "-c", "while true; do printf 'hello' | nc -l -p 80; done") - .withBinds(new Bind(mountableFile.getResolvedPath(), new Volume("/dummy"), AccessMode.ro)) - .withExposedPorts(new ExposedPort(80)) - .withPublishAllPorts(true), - (dockerClient, id) -> { + VisibleAssertions.info("Checking the system..."); - checkDiskSpace(dockerClient, id); - checkMountableFile(dockerClient, id); - checkExposedPort(hostIpAddress, dockerClient, id); + checkDockerVersion(version.getVersion()); - return null; - }); + if (!TestcontainersConfiguration.getInstance().isDisableChecks()) { + checkDiskSpace(client, ryukContainerId); + checkMountableFile(client, ryukContainerId); } - preconditionsChecked = true; + + initialized = true; } return client; @@ -178,26 +174,10 @@ private void checkMountableFile(DockerClient dockerClient, String id) { } } - private void checkExposedPort(String hostIpAddress, DockerClient dockerClient, String id) { - String response = Unreliables.retryUntilSuccess(3, TimeUnit.SECONDS, () -> { - InspectContainerResponse inspectedContainer = dockerClient.inspectContainerCmd(id).exec(); - - String portSpec = inspectedContainer.getNetworkSettings().getPorts().getBindings().values().iterator().next()[0].getHostPortSpec(); - - try (Socket socket = new Socket(hostIpAddress, Integer.parseInt(portSpec))) { - return IOUtils.toString(socket.getInputStream(), Charset.defaultCharset()); - } catch (IOException e) { - return e.getMessage(); - } - }); - - VisibleAssertions.assertEquals("A port exposed by a docker container should be accessible", "hello", response); - } - /** * Check whether the image is available locally and pull it otherwise */ - private void checkAndPullImage(DockerClient client, String image) { + public void checkAndPullImage(DockerClient client, String image) { List images = client.listImagesCmd().withImageNameFilter(image).exec(); if (images.isEmpty()) { client.pullImageCmd(image).exec(new PullImageResultCallback()).awaitSuccess(); @@ -221,7 +201,8 @@ public T runInsideDocker(Consumer createContainerCmdCons private T runInsideDocker(DockerClient client, Consumer createContainerCmdConsumer, BiFunction block) { checkAndPullImage(client, TINY_IMAGE); - CreateContainerCmd createContainerCmd = client.createContainerCmd(TINY_IMAGE); + CreateContainerCmd createContainerCmd = client.createContainerCmd(TINY_IMAGE) + .withLabels(DEFAULT_LABELS); createContainerCmdConsumer.accept(createContainerCmd); String id = createContainerCmd.exec().getId(); @@ -263,7 +244,7 @@ DiskSpaceUsage parseAvailableDiskSpace(String dfOutput) { * @return the docker API version of the daemon that we have connected to */ public String getActiveApiVersion() { - if (!preconditionsChecked) { + if (!initialized) { client(); } return activeApiVersion; @@ -273,7 +254,7 @@ public String getActiveApiVersion() { * @return the docker execution driver of the daemon that we have connected to */ public String getActiveExecutionDriver() { - if (!preconditionsChecked) { + if (!initialized) { client(); } return activeExecutionDriver; diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index b718351c86b..ecc91dfe0a2 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -1,7 +1,6 @@ package org.testcontainers.containers; import com.github.dockerjava.api.DockerClient; -import com.github.dockerjava.api.exception.DockerException; import com.github.dockerjava.api.model.Container; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; @@ -24,16 +23,11 @@ import org.zeroturnaround.exec.stream.slf4j.Slf4jStream; import java.io.File; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; +import java.util.AbstractMap.SimpleEntry; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; @@ -59,6 +53,8 @@ public class DockerComposeContainer> e private boolean pull = true; private boolean tailChildContainers; + private String project; + private final AtomicInteger nextAmbassadorPort = new AtomicInteger(2000); private final Map> ambassadorPortMappings = new ConcurrentHashMap<>(); private final SocatContainer ambassadorContainer = new SocatContainer(); @@ -94,6 +90,7 @@ public DockerComposeContainer(String identifier, List composeFiles) { // Use a unique identifier so that containers created for this compose environment can be identified this.identifier = identifier; + project = randomProjectId(); this.dockerClient = DockerClientFactory.instance().client(); } @@ -106,6 +103,7 @@ public void starting(Description description) { profiler.start("Docker Compose container startup"); synchronized (MUTEX) { + registerContainersForShutdown(); if (pull) { pullImages(); } @@ -114,7 +112,6 @@ public void starting(Description description) { if (tailChildContainers) { tailChildContainerLogs(); } - registerContainersForShutdown(); startAmbassadorContainers(profiler); } } @@ -142,9 +139,9 @@ private void tailChildContainerLogs() { private void runWithCompose(String cmd) { final DockerCompose dockerCompose; if (localCompose) { - dockerCompose = new LocalDockerCompose(composeFiles, identifier); + dockerCompose = new LocalDockerCompose(composeFiles, project); } else { - dockerCompose = new ContainerisedDockerCompose(composeFiles, identifier); + dockerCompose = new ContainerisedDockerCompose(composeFiles, project); } dockerCompose @@ -166,30 +163,9 @@ private void applyScaling() { } private void registerContainersForShutdown() { - // Ensure that all service containers that were launched by compose will be killed at shutdown - try { - final List containers = listChildContainers(); - - // register with ResourceReaper to ensure final shutdown with JVM - containers.forEach(container -> - ResourceReaper.instance().registerContainerForCleanup(container.getId(), container.getNames()[0])); - - // Compose can define their own networks as well; ensure these are cleaned up - dockerClient.listNetworksCmd().exec().forEach(network -> { - if (network.getName().contains(identifier)) { - spawnedNetworkIds.add(network.getId()); - ResourceReaper.instance().registerNetworkIdForCleanup(network.getId()); - } - }); - - // remember the IDs to allow containers to be killed as soon as we reach stop() - spawnedContainerIds.addAll(containers.stream() - .map(Container::getId) - .collect(Collectors.toSet())); - - } catch (DockerException e) { - logger().debug("Failed to stop a service container with exception", e); - } + ResourceReaper.instance().registerFilterForCleanup(Arrays.asList( + new SimpleEntry<>("label", "com.docker.compose.project=" + project) + )); } private List listChildContainers() { @@ -197,7 +173,7 @@ private List listChildContainers() { .withShowAll(true) .exec().stream() .filter(container -> Arrays.stream(container.getNames()).anyMatch(name -> - name.startsWith("/" + identifier))) + name.startsWith("/" + project))) .collect(toList()); } @@ -216,29 +192,33 @@ public void finished(Description description) { synchronized (MUTEX) { - // shut down the ambassador container - ambassadorContainer.stop(); - - // Kill the services using docker-compose try { - runWithCompose("down -v"); + // shut down the ambassador container + ambassadorContainer.stop(); - // If we reach here then docker-compose down has cleared networks and containers; - // we can unregister from ResourceReaper - spawnedContainerIds.forEach(ResourceReaper.instance()::unregisterContainer); - spawnedNetworkIds.forEach(ResourceReaper.instance()::unregisterNetwork); - } catch (Exception e) { - // docker-compose down failed; use ResourceReaper to ensure cleanup + // Kill the services using docker-compose + try { + runWithCompose("down -v"); - // kill the spawned service containers - spawnedContainerIds.forEach(ResourceReaper.instance()::stopAndRemoveContainer); + // If we reach here then docker-compose down has cleared networks and containers; + // we can unregister from ResourceReaper + spawnedContainerIds.forEach(ResourceReaper.instance()::unregisterContainer); + spawnedNetworkIds.forEach(ResourceReaper.instance()::unregisterNetwork); + } catch (Exception e) { + // docker-compose down failed; use ResourceReaper to ensure cleanup - // remove the networks after removing the containers - spawnedNetworkIds.forEach(ResourceReaper.instance()::removeNetworkById); - } + // kill the spawned service containers + spawnedContainerIds.forEach(ResourceReaper.instance()::stopAndRemoveContainer); + + // remove the networks after removing the containers + spawnedNetworkIds.forEach(ResourceReaper.instance()::removeNetworkById); + } - spawnedContainerIds.clear(); - spawnedNetworkIds.clear(); + spawnedContainerIds.clear(); + spawnedNetworkIds.clear(); + } finally { + project = randomProjectId(); + } } } @@ -266,7 +246,7 @@ public SELF withExposedService(String serviceName, int servicePort) { int ambassadorPort = nextAmbassadorPort.getAndIncrement(); ambassadorPortMappings.computeIfAbsent(serviceName, __ -> new ConcurrentHashMap<>()).put(servicePort, ambassadorPort); ambassadorContainer.withTarget(ambassadorPort, serviceName, servicePort); - ambassadorContainer.addLink(new FutureContainer(this.identifier + "_" + serviceName), serviceName); + ambassadorContainer.addLink(new FutureContainer(this.project + "_" + serviceName), serviceName); return self(); } @@ -351,6 +331,10 @@ public SELF withTailChildContainers(boolean tailChildContainers) { private SELF self() { return (SELF) this; } + + private String randomProjectId() { + return identifier + Base58.randomString(6).toLowerCase(); + } } interface DockerCompose { diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 932c00e4c9a..a3a0ab47e78 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -211,10 +211,8 @@ private void tryStart(Profiler profiler) { profiler.start("Create container"); CreateContainerCmd createCommand = dockerClient.createContainerCmd(dockerImageName); applyConfiguration(createCommand); - createContainerCmdModifiers.forEach(hook -> hook.accept(createCommand)); containerId = createCommand.exec().getId(); - ResourceReaper.instance().registerContainerForCleanup(containerId, dockerImageName); logger().info("Starting container with ID: {}", containerId); profiler.start("Start container"); @@ -465,7 +463,12 @@ private void applyConfiguration(CreateContainerCmd createCommand) { createCommand.withPrivileged(privilegedMode); } - createCommand.withLabels(Collections.singletonMap("org.testcontainers", "true")); + createContainerCmdModifiers.forEach(hook -> hook.accept(createCommand)); + + Map labels = createCommand.getLabels(); + labels = new HashMap<>(labels != null ? labels : Collections.emptyMap()); + labels.putAll(DockerClientFactory.DEFAULT_LABELS); + createCommand.withLabels(labels); } private Set findLinksFromThisContainer(String alias, LinkableContainer linkableContainer) { diff --git a/core/src/main/java/org/testcontainers/containers/Network.java b/core/src/main/java/org/testcontainers/containers/Network.java index c2ea8a7b993..b71a74a455c 100644 --- a/core/src/main/java/org/testcontainers/containers/Network.java +++ b/core/src/main/java/org/testcontainers/containers/Network.java @@ -10,6 +10,8 @@ import org.testcontainers.utility.ResourceReaper; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; @@ -78,9 +80,12 @@ private String create() { consumer.accept(createNetworkCmd); } - String id = createNetworkCmd.exec().getId(); - ResourceReaper.instance().registerNetworkIdForCleanup(id); - return id; + Map labels = createNetworkCmd.getLabels(); + labels = new HashMap<>(labels != null ? labels : Collections.emptyMap()); + labels.putAll(DockerClientFactory.DEFAULT_LABELS); + createNetworkCmd.withLabels(labels); + + return createNetworkCmd.exec().getId(); } @Override diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index bac8772f842..6e169114e57 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -1,37 +1,164 @@ package org.testcontainers.utility; +import com.fasterxml.jackson.annotation.JsonProperty; import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.exception.DockerException; import com.github.dockerjava.api.exception.InternalServerErrorException; import com.github.dockerjava.api.exception.NotFoundException; +import com.github.dockerjava.api.model.AccessMode; +import com.github.dockerjava.api.model.Bind; +import com.github.dockerjava.api.model.ExposedPort; +import com.github.dockerjava.api.model.HostConfig; import com.github.dockerjava.api.model.Network; +import com.github.dockerjava.api.model.Ports; +import com.github.dockerjava.api.model.Volume; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.http.client.utils.URLEncodedUtils; +import org.apache.http.message.BasicNameValuePair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testcontainers.DockerClientFactory; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.Socket; +import java.util.AbstractMap.SimpleEntry; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Component that responsible for container removal and automatic cleanup of dead containers at JVM shutdown. */ +@Slf4j public final class ResourceReaper { private static final Logger LOGGER = LoggerFactory.getLogger(ResourceReaper.class); + + private static final List>> DEATH_NOTE = new ArrayList<>(); + private static ResourceReaper instance; private final DockerClient dockerClient; private Map registeredContainers = new ConcurrentHashMap<>(); private Set registeredNetworks = Collections.newSetFromMap(new ConcurrentHashMap<>()); + private AtomicBoolean hookIsSet = new AtomicBoolean(false); private ResourceReaper() { dockerClient = DockerClientFactory.instance().client(); + } + + @SneakyThrows(InterruptedException.class) + public static String start(String hostIpAddress, DockerClient client) { + String ryukImage = TestcontainersConfiguration.getInstance().getRyukImage(); + DockerClientFactory.instance().checkAndPullImage(client, ryukImage); + + MountableFile mountableFile = MountableFile.forClasspathResource(ResourceReaper.class.getName().replace(".", "/") + ".class"); + + String ryukContainerId = client.createContainerCmd(ryukImage) + .withHostConfig(new HostConfig() { + @JsonProperty("AutoRemove") + boolean autoRemove = true; + }) + .withExposedPorts(new ExposedPort(8080)) + .withPublishAllPorts(true) + .withName("testcontainers-ryuk-" + DockerClientFactory.SESSION_ID) + .withLabels(Collections.singletonMap(DockerClientFactory.TESTCONTAINERS_LABEL, "true")) + .withBinds( + new Bind("//var/run/docker.sock", new Volume("/var/run/docker.sock")), + // Not needed for Ryuk, but we perform pre-flight checks with it (micro optimization) + new Bind(mountableFile.getResolvedPath(), new Volume("/dummy"), AccessMode.ro) + ) + .exec() + .getId(); + + client.startContainerCmd(ryukContainerId).exec(); + + InspectContainerResponse inspectedContainer = client.inspectContainerCmd(ryukContainerId).exec(); + + Integer ryukPort = inspectedContainer.getNetworkSettings().getPorts().getBindings().values().stream() + .flatMap(Stream::of) + .findFirst() + .map(Ports.Binding::getHostPortSpec) + .map(Integer::parseInt) + .get(); + + CountDownLatch ryukScheduledLatch = new CountDownLatch(1); + + synchronized (DEATH_NOTE) { + DEATH_NOTE.add( + DockerClientFactory.DEFAULT_LABELS.entrySet().stream() + .>map(it -> new SimpleEntry<>("label", it.getKey() + "=" + it.getValue())) + .collect(Collectors.toList()) + ); + } + + Thread kiraThread = new Thread( + () -> { + while (true) { + int index = 0; + try(Socket clientSocket = new Socket(hostIpAddress, ryukPort)) { + OutputStream out = clientSocket.getOutputStream(); + BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); + + synchronized (DEATH_NOTE) { + while (true) { + if (DEATH_NOTE.size() <= index) { + try { + DEATH_NOTE.wait(1_000); + continue; + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + List> filters = DEATH_NOTE.get(index); + + String query = URLEncodedUtils.format( + filters.stream() + .map(it -> new BasicNameValuePair(it.getKey(), it.getValue())) + .collect(Collectors.toList()), + (String) null + ); - // If the JVM stops without containers being stopped, try and stop the container. - Runtime.getRuntime().addShutdownHook(new Thread(this::performCleanup)); + log.debug("Sending '{}' to Ryuk", query); + out.write(query.getBytes()); + out.write('\n'); + out.flush(); + + while (!"ACK".equalsIgnoreCase(in.readLine())) { + } + + ryukScheduledLatch.countDown(); + index++; + } + } + } catch (IOException e) { + log.warn("Can not connect to Ryuk at {}:{}", hostIpAddress, ryukPort, e); + } + } + }, + "testcontainers-ryuk" + ); + kiraThread.setDaemon(true); + kiraThread.start(); + + // We need to wait before we can start any containers to make sure that we delete them + if (!ryukScheduledLatch.await(5, TimeUnit.SECONDS)) { + throw new IllegalStateException("Can not connect to Ryuk"); + } + + return ryukContainerId; } public synchronized static ResourceReaper instance() { @@ -51,6 +178,18 @@ public synchronized void performCleanup() { registeredNetworks.forEach(this::removeNetwork); } + /** + * Register a filter to be cleaned up. + * + * @param filter the filter + */ + public void registerFilterForCleanup(List> filter) { + synchronized (DEATH_NOTE) { + DEATH_NOTE.add(filter); + DEATH_NOTE.notifyAll(); + } + } + /** * Register a container to be cleaned up, either on explicit call to stopAndRemoveContainer, or at JVM shutdown. * @@ -58,6 +197,7 @@ public synchronized void performCleanup() { * @param imageName the image name of the container (used for logging) */ public void registerContainerForCleanup(String containerId, String imageName) { + setHook(); registeredContainers.put(containerId, imageName); } @@ -133,6 +273,7 @@ private void stopContainer(String containerId, String imageName) { * @param id the ID of the network */ public void registerNetworkIdForCleanup(String id) { + setHook(); registeredNetworks.add(id); } @@ -206,4 +347,11 @@ public void unregisterNetwork(String identifier) { public void unregisterContainer(String identifier) { registeredContainers.remove(identifier); } + + private void setHook() { + if (hookIsSet.compareAndSet(false, true)) { + // If the JVM stops without containers being stopped, try and stop the container. + Runtime.getRuntime().addShutdownHook(new Thread(this::performCleanup)); + } + } } diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 890cd0d4279..d62728a4338 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -46,6 +46,10 @@ public String getTinyImage() { return (String) properties.getOrDefault("tinyimage.container.image", "alpine:3.5"); } + public String getRyukImage() { + return (String) properties.getOrDefault("ryuk.container.image", "bsideup/moby-ryuk:0.2.2"); + } + public boolean isDisableChecks() { return Boolean.parseBoolean((String) properties.getOrDefault("checks.disable", "false")); } diff --git a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java index a6cb304d4e3..e13c3875f31 100644 --- a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java +++ b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java @@ -1,5 +1,6 @@ package org.testcontainers; +import com.github.dockerjava.api.exception.NotFoundException; import org.junit.Test; import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.DockerClientFactory.DiskSpaceUsage; @@ -11,24 +12,28 @@ */ public class DockerClientFactoryTest { - @Test - public void runCommandInsideDockerShouldNotFailIfImageDoesNotExistsLocally() { + @Test + public void runCommandInsideDockerShouldNotFailIfImageDoesNotExistsLocally() { - final DockerClientFactory dockFactory = DockerClientFactory.instance(); - //remove tiny image, so it will be pulled during next command run - dockFactory.client() - .removeImageCmd(TestcontainersConfiguration.getInstance().getTinyImage()) - .withForce(true).exec(); + final DockerClientFactory dockFactory = DockerClientFactory.instance(); + try { + //remove tiny image, so it will be pulled during next command run + dockFactory.client() + .removeImageCmd(TestcontainersConfiguration.getInstance().getTinyImage()) + .withForce(true).exec(); + } catch (NotFoundException ignored) { + // Do not fail if it's not pulled yet + } - dockFactory.runInsideDocker( - cmd -> cmd.withCmd("sh", "-c", "echo 'SUCCESS'"), - (client, id) -> - client.logContainerCmd(id) - .withStdOut(true) - .exec(new LogToStringContainerCallback()) - .toString() - ); - } + dockFactory.runInsideDocker( + cmd -> cmd.withCmd("sh", "-c", "echo 'SUCCESS'"), + (client, id) -> + client.logContainerCmd(id) + .withStdOut(true) + .exec(new LogToStringContainerCallback()) + .toString() + ); + } @Test public void shouldHandleBigDiskSize() throws Exception { From dc06d88cc11fcc29f9c9c699418442230c136de3 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Sat, 27 Jan 2018 15:53:10 +0100 Subject: [PATCH 0215/5615] Update docs to better reflect current state of Windows support --- docs/compatibility.md | 2 +- docs/usage/windows_support.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/compatibility.md b/docs/compatibility.md index 79ebbd9d24a..ed24705b485 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -9,4 +9,4 @@ | Mac OS X - Docker Toolbox | Docker Machine v0.8.0 | | | Mac OS X - Docker for Mac | 1.12.0 | *Support is best-efforts at present*. `getTestHostIpAddress()` is [not currently supported](https://github.com/testcontainers/testcontainers-java/issues/166) due to limitations in Docker for Mac. | | Windows - Docker Toolbox | | *Support is limited at present and this is not currently tested on a regular basis*. | -| Windows - Docker for Windows | | *Supported is limited at present (Docker Compose tests are failing) and not currently tested on a regular basis.* | +| Windows - Docker for Windows | | *Support is best-efforts at present.* Only Linux Containers (LCOW) are supported at the moment. See [Windows Support](usage/windows_support.md) | diff --git a/docs/usage/windows_support.md b/docs/usage/windows_support.md index a52ac6967fe..c5eaa476590 100644 --- a/docs/usage/windows_support.md +++ b/docs/usage/windows_support.md @@ -19,6 +19,10 @@ effort. ### MySQL containers * MySQL server prevents custom configuration file (ini-script) from being loaded due to security measures ([link to feature description](database_containers.md#using-an-init-script)) +### Windows Container on Windows (WCOW) + +* WCOW is currently not supported, since Testcontainers uses auxiliary Linux containers for certain tasks and Docker for Windows does not support hybrid engine mode at the time of writing. + ## Reporting issues Please report any issues with the Windows build of Testcontainers [here](https://github.com/testcontainers/testcontainers-java/issues) From 4df5012a384c6d08b617ac89fe792379023c2bcc Mon Sep 17 00:00:00 2001 From: David Pilato Date: Sat, 27 Jan 2018 19:58:20 +0100 Subject: [PATCH 0216/5615] Update Lombok to 1.16.20 for a better Java9 support While working on elasticsearch module, I had issues when running `mvn clean install` under Java9: ``` [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.7.0:compile (default-compile) on project testcontainers-elasticsearch: Fatal error compiling: java.lang.NoSuchFieldError: pid -> [Help 1] ``` Upgrading Lombok to 1.16.20 fixes the problem. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c73552febcf..90453b8839d 100644 --- a/pom.xml +++ b/pom.xml @@ -73,7 +73,7 @@ org.projectlombok lombok - 1.16.6 + 1.16.20 provided From 3b8b8c985e411cfe015eb02cb497b19bb8bcc0af Mon Sep 17 00:00:00 2001 From: Nikem Date: Sat, 27 Jan 2018 21:21:24 +0200 Subject: [PATCH 0217/5615] Issue 534. Now properly handles command override for MySQL containers --- .../testcontainers/junit/SimpleMySQLTest.java | 16 ++++++++++++++++ .../containers/MySQLContainer.java | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java index b7169f6d6aa..d1ee8100709 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java @@ -94,6 +94,22 @@ public void testMySQLWithCustomIniFile() throws SQLException { } } + @Test + public void testCommandOverride() throws SQLException { + MySQLContainer mysqlCustomConfig = (MySQLContainer) new MySQLContainer().withCommand("mysqld --auto_increment_increment=42"); + mysqlCustomConfig.start(); + + try { + ResultSet resultSet = performQuery(mysqlCustomConfig, "show variables like 'auto_increment_increment'"); + String result = resultSet.getString("Value"); + + assertEquals("Auto increment increment should be overriden by command line", "42", result); + } finally { + mysqlCustomConfig.stop(); + } + + } + @NonNull protected ResultSet performQuery(MySQLContainer containerRule, String sql) throws SQLException { HikariConfig hikariConfig = new HikariConfig(); diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java index 6e782cd4d2a..284926fff8f 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java @@ -20,6 +20,7 @@ public class MySQLContainer> extends JdbcDatab public MySQLContainer() { super(IMAGE + ":latest"); + setCommand("mysqld"); } public MySQLContainer(String dockerImageName) { @@ -41,7 +42,6 @@ protected void configure() { addEnv("MYSQL_USER", username); addEnv("MYSQL_PASSWORD", password); addEnv("MYSQL_ROOT_PASSWORD", "test"); - setCommand("mysqld"); setStartupAttempts(3); } From d7551de0e82e911b9758a1fe13e1cf90d19d5039 Mon Sep 17 00:00:00 2001 From: Nikem Date: Sun, 28 Jan 2018 08:47:13 +0200 Subject: [PATCH 0218/5615] Removed manual setting of default command. Added Changelog entry. --- CHANGELOG.md | 1 + .../main/java/org/testcontainers/containers/MySQLContainer.java | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aef8cff36cb..dfa688bf51c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. ### Fixed - Fixed retrieval of Docker host IP when running inside Docker. ([\#479](https://github.com/testcontainers/testcontainers-java/issues/479)) +- Fixed overriding MySQL image command. ([\#534](https://github.com/testcontainers/testcontainers-java/issues/534)) ### Changed - Added Kafka module ([\#546](https://github.com/testcontainers/testcontainers-java/pull/546)) diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java index 284926fff8f..559586f7ec1 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java @@ -20,7 +20,6 @@ public class MySQLContainer> extends JdbcDatab public MySQLContainer() { super(IMAGE + ":latest"); - setCommand("mysqld"); } public MySQLContainer(String dockerImageName) { From af7847bdb23b8857b924b694fe47b3a16dd50231 Mon Sep 17 00:00:00 2001 From: Nikita Salnikov-Tarnovski Date: Sun, 28 Jan 2018 14:56:29 +0200 Subject: [PATCH 0219/5615] Don't try to use SSL while waiting for jdbc container to start. Upgrade mysql connector to expose this problem in the first place. --- CHANGELOG.md | 3 +++ modules/jdbc-test/pom.xml | 2 +- .../testcontainers/containers/JdbcDatabaseContainer.java | 7 ++++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bf12b52095..14a69a37259 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file. ### Fixed - Fixed retrieval of Docker host IP when running inside Docker. ([\#479](https://github.com/testcontainers/testcontainers-java/issues/479)) +### Changed +- All connections to MySQL containers don't use SSL anymore. ([\#374](https://github.com/testcontainers/testcontainers-java/issues/374)) + ## [1.5.1] - 2017-12-19 ### Fixed diff --git a/modules/jdbc-test/pom.xml b/modules/jdbc-test/pom.xml index 3a311119187..d8c1816c140 100644 --- a/modules/jdbc-test/pom.xml +++ b/modules/jdbc-test/pom.xml @@ -42,7 +42,7 @@ mysql mysql-connector-java - 5.1.35 + 5.1.45 test diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index d4c0ef0e15d..1a4437ed738 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -136,7 +136,7 @@ public Connection createConnection(String queryString) throws SQLException { final Properties info = new Properties(); info.put("user", this.getUsername()); info.put("password", this.getPassword()); - final String url = this.getJdbcUrl() + queryString; + final String url = disableSSL(this.getJdbcUrl() + queryString); final Driver jdbcDriverInstance = getJdbcDriverInstance(); @@ -147,6 +147,11 @@ public Connection createConnection(String queryString) throws SQLException { } } + private String disableSSL(String connectionString){ + String separator = connectionString.contains("?") ? "&" : "?"; + return connectionString + separator + "useSSL=false"; + } + protected void optionallyMapResourceParameterAsVolume(@NotNull String paramName, @NotNull String pathNameInContainer, @NotNull String defaultResource) { String resourceName = parameters.getOrDefault(paramName, defaultResource); From 459da94168d3cfee42e5298a0131840d335219f9 Mon Sep 17 00:00:00 2001 From: Nikita Salnikov-Tarnovski Date: Sun, 28 Jan 2018 15:29:01 +0200 Subject: [PATCH 0220/5615] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d120c00c0fc..d969ab0fed4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ All notable changes to this project will be documented in this file. - Environment variables are now stored as Map instead of List ([\#550](https://github.com/testcontainers/testcontainers-java/pull/550)) - Added `withEnv(String name, Function, String> mapper)` with optional previous value ([\#550](https://github.com/testcontainers/testcontainers-java/pull/550)) - Added `withFileSystemBind` overloaded method with `READ_WRITE` file mode by default ([\#550](https://github.com/testcontainers/testcontainers-java/pull/550)) -- All connections to MySQL containers don't use SSL anymore. ([\#374](https://github.com/testcontainers/testcontainers-java/issues/374)) +- All connections to JDBC containers (e.g. MySQL) don't use SSL anymore. ([\#374](https://github.com/testcontainers/testcontainers-java/issues/374)) ## [1.5.1] - 2017-12-19 From 0e760f9fccdb1850bc608b9dc7c83548435b9c12 Mon Sep 17 00:00:00 2001 From: Nikem Date: Sun, 28 Jan 2018 19:01:14 +0200 Subject: [PATCH 0221/5615] Renamed a method --- .../org/testcontainers/containers/JdbcDatabaseContainer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index 1a4437ed738..f5447f4010e 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -136,7 +136,7 @@ public Connection createConnection(String queryString) throws SQLException { final Properties info = new Properties(); info.put("user", this.getUsername()); info.put("password", this.getPassword()); - final String url = disableSSL(this.getJdbcUrl() + queryString); + final String url = appendDisableSslConfig(this.getJdbcUrl() + queryString); final Driver jdbcDriverInstance = getJdbcDriverInstance(); @@ -147,7 +147,7 @@ public Connection createConnection(String queryString) throws SQLException { } } - private String disableSSL(String connectionString){ + private String appendDisableSslConfig(String connectionString){ String separator = connectionString.contains("?") ? "&" : "?"; return connectionString + separator + "useSSL=false"; } From f602d82d12923cd3741ac7a8decce7c168f1bf1a Mon Sep 17 00:00:00 2001 From: Yury Bubnov Date: Mon, 8 Jan 2018 16:42:26 -0800 Subject: [PATCH 0222/5615] fix for https://github.com/testcontainers/testcontainers-java/issues/536 --- .../containers/DockerComposeContainer.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index ecc91dfe0a2..06ea6f4933b 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -23,6 +23,9 @@ import org.zeroturnaround.exec.stream.slf4j.Slf4jStream; import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.*; import java.util.AbstractMap.SimpleEntry; import java.util.concurrent.ConcurrentHashMap; @@ -359,6 +362,7 @@ default void validateFileList(List composeFiles) { class ContainerisedDockerCompose extends GenericContainer implements DockerCompose { private static final String DOCKER_SOCKET_PATH = "/var/run/docker.sock"; + private static final String DOCKER_CONFIG_PATH = "/root/.docker"; public static final char UNIX_PATH_SEPERATOR = ':'; public ContainerisedDockerCompose(List composeFiles, String identifier) { @@ -391,6 +395,11 @@ public ContainerisedDockerCompose(List composeFiles, String identifier) { addEnv("DOCKER_HOST", "unix:///docker.sock"); setStartupCheckStrategy(new IndefiniteWaitOneShotStartupCheckStrategy()); setWorkingDirectory(containerPwd); + Path dockerConfig = Paths.get(System.getProperty("user.home"), ".docker"); + if (Files.exists(dockerConfig)){ + addFileSystemBind(dockerConfig.toString(), DOCKER_CONFIG_PATH, READ_ONLY); + } + } private String getDockerSocketHostPath() { From 37e63c8cc7b6774b1849903e2ef9779b2f96c477 Mon Sep 17 00:00:00 2001 From: Yury Bubnov Date: Sat, 13 Jan 2018 01:09:35 -0800 Subject: [PATCH 0223/5615] added possibility to specify Docker config.json via environment variable, java property or by default location --- .../containers/DockerComposeContainer.java | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 06ea6f4933b..d7f6f7d5446 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -190,7 +190,8 @@ private Logger logger() { return LoggerFactory.getLogger(DockerComposeContainer.class); } - @Override @VisibleForTesting + @Override + @VisibleForTesting public void finished(Description description) { @@ -227,7 +228,7 @@ public void finished(Description description) { public SELF withExposedService(String serviceName, int servicePort) { - if (! serviceName.matches(".*_[0-9]+")) { + if (!serviceName.matches(".*_[0-9]+")) { serviceName += "_1"; // implicit first instance of this service } @@ -362,7 +363,9 @@ default void validateFileList(List composeFiles) { class ContainerisedDockerCompose extends GenericContainer implements DockerCompose { private static final String DOCKER_SOCKET_PATH = "/var/run/docker.sock"; - private static final String DOCKER_CONFIG_PATH = "/root/.docker"; + private static final String DOCKER_CONFIG_FILE = "/root/.docker/config.json"; + private static final String DOCKER_CONFIG_ENV = "DOCKER_CONFIG_FILE"; + private static final String DOCKER_CONFIG_PROPERTY = "dockerConfigFile"; public static final char UNIX_PATH_SEPERATOR = ':'; public ContainerisedDockerCompose(List composeFiles, String identifier) { @@ -378,10 +381,10 @@ public ContainerisedDockerCompose(List composeFiles, String identifier) { final String containerPwd = MountableFile.forHostPath(pwd).getFilesystemPath(); final List absoluteDockerComposeFiles = composeFiles.stream() - .map(File::getAbsolutePath) - .map(MountableFile::forHostPath) - .map(MountableFile::getFilesystemPath) - .collect(toList()); + .map(File::getAbsolutePath) + .map(MountableFile::forHostPath) + .map(MountableFile::getFilesystemPath) + .collect(toList()); final String composeFileEnvVariableValue = Joiner.on(UNIX_PATH_SEPERATOR).join(absoluteDockerComposeFiles); // we always need the UNIX path separator logger().debug("Set env COMPOSE_FILE={}", composeFileEnvVariableValue); addEnv(ENV_COMPOSE_FILE, composeFileEnvVariableValue); @@ -395,9 +398,17 @@ public ContainerisedDockerCompose(List composeFiles, String identifier) { addEnv("DOCKER_HOST", "unix:///docker.sock"); setStartupCheckStrategy(new IndefiniteWaitOneShotStartupCheckStrategy()); setWorkingDirectory(containerPwd); - Path dockerConfig = Paths.get(System.getProperty("user.home"), ".docker"); - if (Files.exists(dockerConfig)){ - addFileSystemBind(dockerConfig.toString(), DOCKER_CONFIG_PATH, READ_ONLY); + + String dockerConfigEnv = System.getenv(DOCKER_CONFIG_ENV); + String dockerConfigProperty = System.getProperty(DOCKER_CONFIG_PROPERTY); + Path dockerConfig = Paths.get(System.getProperty("user.home"), ".docker", "config.json"); + + if (dockerConfigEnv != null && !dockerConfigEnv.trim().isEmpty()) { + addFileSystemBind(dockerConfigEnv.toString(), DOCKER_CONFIG_FILE, READ_ONLY); + } else if (dockerConfigProperty != null && !dockerConfigProperty.trim().isEmpty()) { + addFileSystemBind(dockerConfigProperty.toString(), DOCKER_CONFIG_FILE, READ_ONLY); + } else if (Files.exists(dockerConfig)) { + addFileSystemBind(dockerConfig.toString(), DOCKER_CONFIG_FILE, READ_ONLY); } } From ffd34967b0f404d29e34ea500abcca943518f9be Mon Sep 17 00:00:00 2001 From: Dmitry Fedosov Date: Sun, 28 Jan 2018 13:20:19 -0500 Subject: [PATCH 0224/5615] Fixed shading for javax.annotation.CheckForNull --- core/pom.xml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/pom.xml b/core/pom.xml index 00cc48d8e6d..16aa8b3e2fb 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -68,6 +68,12 @@ 2.0.1 + + com.google.code.findbugs + jsr305 + 3.0.2 + + org.rnorth tcp-unix-socket-proxy @@ -222,7 +228,7 @@ org.apache.httpcomponents:* org.glassfish.*:* org.aopalliance.*:* - javax.annotation:* + com.google.code.findbugs:jsr305 javax.inject:* javax.ws.rs:* com.fasterxml.*:* From 5e9b70d78e15589ed98d5654f322617d6e2ed2e6 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 28 Jan 2018 18:20:48 +0000 Subject: [PATCH 0225/5615] added record to CHANGELOG and added usage documentation --- CHANGELOG.md | 1 + docs/usage/docker_compose.md | 41 ++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfa688bf51c..eb00ac922c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. ### Fixed - Fixed retrieval of Docker host IP when running inside Docker. ([\#479](https://github.com/testcontainers/testcontainers-java/issues/479)) +- Compose can now pull images from private repositories. - Fixed overriding MySQL image command. ([\#534](https://github.com/testcontainers/testcontainers-java/issues/534)) ### Changed diff --git a/docs/usage/docker_compose.md b/docs/usage/docker_compose.md index dec5278dab7..86d11a2486d 100644 --- a/docs/usage/docker_compose.md +++ b/docs/usage/docker_compose.md @@ -53,3 +53,44 @@ String redisUrl = environment.getServiceHost("redis_1", REDIS_PORT) + ":" + environment.getServicePort("redis_1", REDIS_PORT); ``` + +## Using private repositories in Docker compose +When Docker Compose is used in container mode (not local), it's needs to be made aware of Docker settings for private repositories. +By default, those setting are located in `$HOME/.docker/config.json`. + +There are 3 ways to specify location of the `config.json` for Docker Compose +* Use `DOCKER_CONFIG_FILE` environment variable. + + `export DOCKER_CONFIG_FILE=/some/location/config.json` + +* Use `dockerConfigFile` java property + + `java -DdockerConfigFile=/some/location/config.json` + +* Don't specify anything, in this case default location `$HOME/.docker/config.json`, if present, will be used + +####Note to OSX users +By default, Docker for mac uses Keychain to store private repositories' keys. So, your `config.json` looks like +```$json +{ + "auths" : { + "https://index.docker.io/v1/" : { + } + }, + "credsStore" : "osxkeychain" +} +``` + +Docker Compose in container cannot access the Keychain, thus making the configuration useless. +To work around this problem, create `config.json` in separate location with real authentication keys, like +```$json +{ + "auths" : { + "https://index.docker.io/v1/" : { + "auth": "QWEADSZXC..." + } + }, + "credsStore" : "osxkeychain" +} +``` +and specify the location to TestContainers using any of the two first methods from above. \ No newline at end of file From 24f2d1f388e5c6c2465baaf17d57f736126f148d Mon Sep 17 00:00:00 2001 From: Yury Bubnov Date: Sat, 13 Jan 2018 01:34:47 -0800 Subject: [PATCH 0226/5615] added check for config file existence --- .../testcontainers/containers/DockerComposeContainer.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index d7f6f7d5446..949a1cceb29 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -403,10 +403,10 @@ public ContainerisedDockerCompose(List composeFiles, String identifier) { String dockerConfigProperty = System.getProperty(DOCKER_CONFIG_PROPERTY); Path dockerConfig = Paths.get(System.getProperty("user.home"), ".docker", "config.json"); - if (dockerConfigEnv != null && !dockerConfigEnv.trim().isEmpty()) { - addFileSystemBind(dockerConfigEnv.toString(), DOCKER_CONFIG_FILE, READ_ONLY); - } else if (dockerConfigProperty != null && !dockerConfigProperty.trim().isEmpty()) { - addFileSystemBind(dockerConfigProperty.toString(), DOCKER_CONFIG_FILE, READ_ONLY); + if (dockerConfigEnv != null && !dockerConfigEnv.trim().isEmpty() && Files.exists(Paths.get(dockerConfigEnv))) { + addFileSystemBind(dockerConfigEnv, DOCKER_CONFIG_FILE, READ_ONLY); + } else if (dockerConfigProperty != null && !dockerConfigProperty.trim().isEmpty() && Files.exists(Paths.get(dockerConfigProperty))) { + addFileSystemBind(dockerConfigProperty, DOCKER_CONFIG_FILE, READ_ONLY); } else if (Files.exists(dockerConfig)) { addFileSystemBind(dockerConfig.toString(), DOCKER_CONFIG_FILE, READ_ONLY); } From d1498fc24104b86d47d3d87aa1e7f55b832f8f9f Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 28 Jan 2018 18:21:16 +0000 Subject: [PATCH 0227/5615] moved path detection to separate method --- CHANGELOG.md | 2 +- .../containers/DockerComposeContainer.java | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb00ac922c1..9fdf6deccb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. ### Fixed - Fixed retrieval of Docker host IP when running inside Docker. ([\#479](https://github.com/testcontainers/testcontainers-java/issues/479)) -- Compose can now pull images from private repositories. +- Compose is now able to pull images from private repositories. ([\#536](https://github.com/testcontainers/testcontainers-java/issues/536)) - Fixed overriding MySQL image command. ([\#534](https://github.com/testcontainers/testcontainers-java/issues/534)) ### Changed diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 949a1cceb29..80d8f88b659 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -399,18 +399,26 @@ public ContainerisedDockerCompose(List composeFiles, String identifier) { setStartupCheckStrategy(new IndefiniteWaitOneShotStartupCheckStrategy()); setWorkingDirectory(containerPwd); + String dockerConfigPath = determineDockerConfigPath(); + if (dockerConfigPath != null && dockerConfigPath.isEmpty()) { + addFileSystemBind(dockerConfigPath, DOCKER_CONFIG_FILE, READ_ONLY); + } + } + + private String determineDockerConfigPath() { String dockerConfigEnv = System.getenv(DOCKER_CONFIG_ENV); String dockerConfigProperty = System.getProperty(DOCKER_CONFIG_PROPERTY); Path dockerConfig = Paths.get(System.getProperty("user.home"), ".docker", "config.json"); if (dockerConfigEnv != null && !dockerConfigEnv.trim().isEmpty() && Files.exists(Paths.get(dockerConfigEnv))) { - addFileSystemBind(dockerConfigEnv, DOCKER_CONFIG_FILE, READ_ONLY); + return dockerConfigEnv; } else if (dockerConfigProperty != null && !dockerConfigProperty.trim().isEmpty() && Files.exists(Paths.get(dockerConfigProperty))) { - addFileSystemBind(dockerConfigProperty, DOCKER_CONFIG_FILE, READ_ONLY); + return dockerConfigProperty; } else if (Files.exists(dockerConfig)) { - addFileSystemBind(dockerConfig.toString(), DOCKER_CONFIG_FILE, READ_ONLY); + return dockerConfig.toString(); + } else { + return null; } - } private String getDockerSocketHostPath() { From 5ca323510493a016c4012e0099dbb3034b26cd40 Mon Sep 17 00:00:00 2001 From: Yury Bubnov Date: Thu, 18 Jan 2018 13:59:53 -0800 Subject: [PATCH 0228/5615] Fix typo --- .../org/testcontainers/containers/DockerComposeContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 80d8f88b659..9b0356b55ea 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -400,7 +400,7 @@ public ContainerisedDockerCompose(List composeFiles, String identifier) { setWorkingDirectory(containerPwd); String dockerConfigPath = determineDockerConfigPath(); - if (dockerConfigPath != null && dockerConfigPath.isEmpty()) { + if (dockerConfigPath != null && !dockerConfigPath.isEmpty()) { addFileSystemBind(dockerConfigPath, DOCKER_CONFIG_FILE, READ_ONLY); } } From 1cbfec66e8d3a26082a853fa990c957994e72f68 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Sun, 28 Jan 2018 19:36:35 +0100 Subject: [PATCH 0229/5615] Add jdk9 build to travis (#562) * Add jdk9 build to travis * Add jaxb-api dependency for jdk9 compatibility --- .travis.yml | 6 ++++++ CHANGELOG.md | 2 ++ core/pom.xml | 8 ++++++++ 3 files changed, 16 insertions(+) diff --git a/.travis.yml b/.travis.yml index d886f219b5f..aa2400ce261 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,6 +31,12 @@ jobs: env: [ NAME=core ] script: ./mvnw -pl core test + - stage: test + env: [ NAME=core ] + jdk: oraclejdk9 + script: + - ./mvnw -pl core test + - env: [ NAME=selenium ] script: ./mvnw -pl modules/selenium test diff --git a/CHANGELOG.md b/CHANGELOG.md index eca3bcb8ae8..bb5c592a41f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,13 @@ All notable changes to this project will be documented in this file. ### Fixed +- Fixed incompatibility of Docker-Compose container with JDK9. ([\#562](https://github.com/testcontainers/testcontainers-java/pull/562)) - Fixed retrieval of Docker host IP when running inside Docker. ([\#479](https://github.com/testcontainers/testcontainers-java/issues/479)) - Compose is now able to pull images from private repositories. ([\#536](https://github.com/testcontainers/testcontainers-java/issues/536)) - Fixed overriding MySQL image command. ([\#534](https://github.com/testcontainers/testcontainers-java/issues/534)) ### Changed +- Added JDK9 build and tests to Travis-CI. ([\#562](https://github.com/testcontainers/testcontainers-java/pull/562)) - Added Kafka module ([\#546](https://github.com/testcontainers/testcontainers-java/pull/546)) - Added "Death Note" to track & kill spawned containers even if the JVM was "kill -9"ed ([\#545](https://github.com/testcontainers/testcontainers-java/pull/545)) - Environment variables are now stored as Map instead of List ([\#550](https://github.com/testcontainers/testcontainers-java/pull/550)) diff --git a/core/pom.xml b/core/pom.xml index 00cc48d8e6d..390450b2bb3 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -48,6 +48,14 @@ + + + + javax.xml.bind + jaxb-api + 2.3.0 + + org.glassfish.jersey.core From c6390cd863d3a6cf675273b97f86877097c182f6 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 28 Jan 2018 19:26:24 +0000 Subject: [PATCH 0230/5615] Add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eca3bcb8ae8..3ba83290df9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. - Fixed retrieval of Docker host IP when running inside Docker. ([\#479](https://github.com/testcontainers/testcontainers-java/issues/479)) - Compose is now able to pull images from private repositories. ([\#536](https://github.com/testcontainers/testcontainers-java/issues/536)) - Fixed overriding MySQL image command. ([\#534](https://github.com/testcontainers/testcontainers-java/issues/534)) +- Fixed shading for javax.annotation.CheckForNull ([\#563](https://github.com/testcontainers/testcontainers-java/issues/563) and [testcontainers/testcontainers-scala\#11](https://github.com/testcontainers/testcontainers-scala/issues/11)). ### Changed - Added Kafka module ([\#546](https://github.com/testcontainers/testcontainers-java/pull/546)) From 5ae9c4956101cbe56a8c7077439f1244c5981496 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 28 Jan 2018 22:32:29 +0000 Subject: [PATCH 0231/5615] Update changelog for 1.6.0 release --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40b6d556445..79adf406867 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Change Log All notable changes to this project will be documented in this file. +## [1.6.0] - 2018-01-28 + ### Fixed - Fixed incompatibility of Docker-Compose container with JDK9. ([\#562](https://github.com/testcontainers/testcontainers-java/pull/562)) - Fixed retrieval of Docker host IP when running inside Docker. ([\#479](https://github.com/testcontainers/testcontainers-java/issues/479)) From 387ee16e91d459b525f46446371d88135636062a Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 29 Jan 2018 08:16:00 +0100 Subject: [PATCH 0232/5615] Add JavaDoc to KafkaContainer --- .../java/org.testcontainers.containers/KafkaContainer.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/kafka/src/main/java/org.testcontainers.containers/KafkaContainer.java b/modules/kafka/src/main/java/org.testcontainers.containers/KafkaContainer.java index 228dc8a3399..924dc122442 100644 --- a/modules/kafka/src/main/java/org.testcontainers.containers/KafkaContainer.java +++ b/modules/kafka/src/main/java/org.testcontainers.containers/KafkaContainer.java @@ -4,6 +4,10 @@ import java.util.stream.Stream; +/** + * This container wraps Confluent Kafka and Zookeeper (optionally) + * + */ public class KafkaContainer extends GenericContainer { public static final int KAFKA_PORT = 9092; @@ -79,4 +83,4 @@ public void start() { public void stop() { Stream.of(super::stop, proxy::stop).parallel().forEach(Runnable::run); } -} \ No newline at end of file +} From 4edf9e2ff1e1a075b2976c2e3a34902d48b6aade Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 29 Jan 2018 08:04:21 +0000 Subject: [PATCH 0233/5615] Relocate KafkaContainer sources to correct directory structure --- .../testcontainers/containers}/KafkaContainer.java | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename modules/kafka/src/main/java/{org.testcontainers.containers => org/testcontainers/containers}/KafkaContainer.java (100%) diff --git a/modules/kafka/src/main/java/org.testcontainers.containers/KafkaContainer.java b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java similarity index 100% rename from modules/kafka/src/main/java/org.testcontainers.containers/KafkaContainer.java rename to modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java From a6d8bf2c72fbf3ee3849c6967da5401ed5b1d0b7 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 3 Feb 2018 20:35:15 +0000 Subject: [PATCH 0234/5615] Fix extraneous use of connection string `useSSL` flag (#569) Limit use of `useSSL` flag to MySQL container Add template method allowing JDBCContainer subclasses to have a specific form of JDBC URL used for establishing Connections. Fixes #568 --- CHANGELOG.md | 6 +++++ .../testcontainers/jdbc/JDBCDriverTest.java | 1 + .../containers/JdbcDatabaseContainer.java | 24 +++++++++++++------ .../containers/MySQLContainer.java | 12 ++++++++++ 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79adf406867..97bfdc666cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # Change Log All notable changes to this project will be documented in this file. +## [1.6.1] - 2018-01-31 + +### Fixed + +- Fixed extraneous insertion of `useSSL=false` in all JDBC URL strings, even for DBs that do not understand it. Usage is now restricted to MySQL by default and can be overridden by authors of `JdbcDatabaseContainer` subclasses ([\#568](https://github.com/testcontainers/testcontainers-java/issues/568)) + ## [1.6.0] - 2018-01-28 ### Fixed diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java index 8e663df8a45..c87f7bce365 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java @@ -41,6 +41,7 @@ public static Iterable data() { {"jdbc:tc:mysql://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", true, false, false}, {"jdbc:tc:mysql://hostname/databasename?useUnicode=yes&characterEncoding=utf8", false, true, false}, {"jdbc:tc:mysql://hostname/databasename", false, false, false}, + {"jdbc:tc:mysql://hostname/databasename?useSSL=false", false, false, false}, {"jdbc:tc:postgresql://hostname/databasename", false, false, false}, {"jdbc:tc:mysql:5.6://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override", false, false, true}, }); diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index f5447f4010e..b2eefaf3f31 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -71,7 +71,7 @@ public SELF withUsername(String username) { throw new UnsupportedOperationException(); } - public SELF withPassword(String password){ + public SELF withPassword(String password) { throw new UnsupportedOperationException(); } @@ -127,8 +127,9 @@ public Driver getJdbcDriverInstance() { /** * Creates a connection to the underlying containerized database instance. * - * @param queryString any special query string parameters that should be appended to the JDBC connection URL. The - * '?' character must be included + * @param queryString + * query string parameters that should be appended to the JDBC connection URL. + * The '?' character must be included * @return a Connection * @throws SQLException if there is a repeated failure to create the connection */ @@ -136,7 +137,7 @@ public Connection createConnection(String queryString) throws SQLException { final Properties info = new Properties(); info.put("user", this.getUsername()); info.put("password", this.getPassword()); - final String url = appendDisableSslConfig(this.getJdbcUrl() + queryString); + final String url = constructUrlForConnection(queryString); final Driver jdbcDriverInstance = getJdbcDriverInstance(); @@ -147,9 +148,18 @@ public Connection createConnection(String queryString) throws SQLException { } } - private String appendDisableSslConfig(String connectionString){ - String separator = connectionString.contains("?") ? "&" : "?"; - return connectionString + separator + "useSSL=false"; + /** + * Template method for constructing the JDBC URL to be used for creating {@link Connection}s. + * This should be overridden if the JDBC URL and query string concatenation or URL string + * construction needs to be different to normal. + * + * @param queryString + * query string parameters that should be appended to the JDBC connection URL. + * The '?' character must be included + * @return a full JDBC URL including queryString + */ + protected String constructUrlForConnection(String queryString) { + return getJdbcUrl() + queryString; } protected void optionallyMapResourceParameterAsVolume(@NotNull String paramName, @NotNull String pathNameInContainer, @NotNull String defaultResource) { diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java index 559586f7ec1..74ac5c7e8b5 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java @@ -54,6 +54,18 @@ public String getJdbcUrl() { return "jdbc:mysql://" + getContainerIpAddress() + ":" + getMappedPort(MYSQL_PORT) + "/" + databaseName; } + @Override + protected String constructUrlForConnection(String queryString) { + String url = super.constructUrlForConnection(queryString); + + if (! url.contains("useSSL=")) { + String separator = url.contains("?") ? "&" : "?"; + return url + separator + "useSSL=false"; + } else { + return url; + } + } + @Override public String getDatabaseName() { return databaseName; From 198fa8ce3fe8a3ec76a41e9d79cf5dc1f692e4be Mon Sep 17 00:00:00 2001 From: Eugeny Karpov Date: Sat, 3 Feb 2018 20:36:15 +0000 Subject: [PATCH 0235/5615] #551: Abstracted and changed database init script functionality to support use of SQL-like scripts with non-JDBC connections. Supports #525 --- CHANGELOG.md | 6 +- modules/database-commons/pom.xml | 23 ++ .../delegate/AbstractDatabaseDelegate.java | 55 +++ .../delegate/DatabaseDelegate.java | 31 ++ .../ConnectionCreationException.java | 13 + .../org/testcontainers/ext/ScriptUtils.java | 298 +++++++++++++++ modules/jdbc/pom.xml | 6 + .../jdbc/ContainerDatabaseDriver.java | 12 +- .../jdbc/ContainerLessJdbcDelegate.java | 36 ++ .../jdbc/JdbcDatabaseDelegate.java | 60 +++ .../testcontainers/jdbc/ext/ScriptUtils.java | 345 +++--------------- pom.xml | 1 + 12 files changed, 592 insertions(+), 294 deletions(-) create mode 100644 modules/database-commons/pom.xml create mode 100644 modules/database-commons/src/main/java/org/testcontainers/delegate/AbstractDatabaseDelegate.java create mode 100644 modules/database-commons/src/main/java/org/testcontainers/delegate/DatabaseDelegate.java create mode 100644 modules/database-commons/src/main/java/org/testcontainers/exception/ConnectionCreationException.java create mode 100644 modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java create mode 100644 modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerLessJdbcDelegate.java create mode 100644 modules/jdbc/src/main/java/org/testcontainers/jdbc/JdbcDatabaseDelegate.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 97bfdc666cb..aeb90f7cbe3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,14 @@ # Change Log All notable changes to this project will be documented in this file. -## [1.6.1] - 2018-01-31 +## UNRELEASED ### Fixed - - Fixed extraneous insertion of `useSSL=false` in all JDBC URL strings, even for DBs that do not understand it. Usage is now restricted to MySQL by default and can be overridden by authors of `JdbcDatabaseContainer` subclasses ([\#568](https://github.com/testcontainers/testcontainers-java/issues/568)) +### Changed +- Abstracted and changed database init script functionality to support use of SQL-like scripts with non-JDBC connections. ([\#551](https://github.com/testcontainers/testcontainers-java/pull/551)) + ## [1.6.0] - 2018-01-28 ### Fixed diff --git a/modules/database-commons/pom.xml b/modules/database-commons/pom.xml new file mode 100644 index 00000000000..965c0c0070c --- /dev/null +++ b/modules/database-commons/pom.xml @@ -0,0 +1,23 @@ + + + 4.0.0 + + + org.testcontainers + testcontainers-parent + 0-SNAPSHOT + ../../pom.xml + + + database-commons + TestContainers :: Database-Commons + + + + ${project.groupId} + testcontainers + ${project.version} + + + + diff --git a/modules/database-commons/src/main/java/org/testcontainers/delegate/AbstractDatabaseDelegate.java b/modules/database-commons/src/main/java/org/testcontainers/delegate/AbstractDatabaseDelegate.java new file mode 100644 index 00000000000..14572dec421 --- /dev/null +++ b/modules/database-commons/src/main/java/org/testcontainers/delegate/AbstractDatabaseDelegate.java @@ -0,0 +1,55 @@ +package org.testcontainers.delegate; + +import java.util.Collection; + +/** + * @param connection to the database + * @author Eugeny Karpov + */ +public abstract class AbstractDatabaseDelegate implements DatabaseDelegate { + + /** + * Database connection + */ + private CONNECTION connection; + + private boolean isConnectionStarted = false; + + /** + * Get or create new connection to the database + */ + protected CONNECTION getConnection() { + if (!isConnectionStarted) { + connection = createNewConnection(); + isConnectionStarted = true; + } + return connection; + } + + @Override + public void execute(Collection statements, String scriptPath, boolean continueOnError, boolean ignoreFailedDrops) { + int lineNumber = 0; + for (String statement : statements) { + lineNumber++; + execute(statement, scriptPath, lineNumber, continueOnError, ignoreFailedDrops); + } + } + + @Override + public void close() { + if (isConnectionStarted) { + closeConnectionQuietly(connection); + isConnectionStarted = false; + } + } + + /** + * Quietly close the connection + */ + protected abstract void closeConnectionQuietly(CONNECTION connection); + + /** + * Template method for creating new connections to the database + */ + protected abstract CONNECTION createNewConnection(); +} diff --git a/modules/database-commons/src/main/java/org/testcontainers/delegate/DatabaseDelegate.java b/modules/database-commons/src/main/java/org/testcontainers/delegate/DatabaseDelegate.java new file mode 100644 index 00000000000..55886d88143 --- /dev/null +++ b/modules/database-commons/src/main/java/org/testcontainers/delegate/DatabaseDelegate.java @@ -0,0 +1,31 @@ +package org.testcontainers.delegate; + +import java.util.Collection; + +/** + * Database delegate + * + * Gives an abstraction from concrete database + * + * @author Eugeny Karpov + */ +public interface DatabaseDelegate extends AutoCloseable { + + /** + * Execute statement by the implementation of the delegate + */ + void execute(String statement, String scriptPath, int lineNumber, boolean continueOnError, boolean ignoreFailedDrops); + + /** + * Execute collection of statements + */ + void execute(Collection statements, String scriptPath, boolean continueOnError, boolean ignoreFailedDrops); + + /** + * Close connection to the database + * + * Overridden to suppress throwing Exception + */ + @Override + void close(); +} diff --git a/modules/database-commons/src/main/java/org/testcontainers/exception/ConnectionCreationException.java b/modules/database-commons/src/main/java/org/testcontainers/exception/ConnectionCreationException.java new file mode 100644 index 00000000000..3b8852dbddc --- /dev/null +++ b/modules/database-commons/src/main/java/org/testcontainers/exception/ConnectionCreationException.java @@ -0,0 +1,13 @@ +package org.testcontainers.exception; + +/** + * Inability to create connection to the database + * + * @author Eugeny Karpov + */ +public class ConnectionCreationException extends RuntimeException { + + public ConnectionCreationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java b/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java new file mode 100644 index 00000000000..e9fa2d18a80 --- /dev/null +++ b/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java @@ -0,0 +1,298 @@ +/* + * Copyright 2002-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.testcontainers.ext; + +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.delegate.DatabaseDelegate; + +import javax.script.ScriptException; +import java.util.LinkedList; +import java.util.List; + +/** + * This is a modified version of the Spring-JDBC ScriptUtils class, adapted to reduce + * dependencies and slightly alter the API. + * + * Generic utility methods for working with SQL scripts. Mainly for internal use + * within the framework. + * + * @author Thomas Risberg + * @author Sam Brannen + * @author Juergen Hoeller + * @author Keith Donald + * @author Dave Syer + * @author Chris Beams + * @author Oliver Gierke + * @author Chris Baldwin + * @since 4.0.3 + */ +public abstract class ScriptUtils { + + private static final Logger LOGGER = LoggerFactory.getLogger(ScriptUtils.class); + + /** + * Default statement separator within SQL scripts. + */ + public static final String DEFAULT_STATEMENT_SEPARATOR = ";"; + + /** + * Fallback statement separator within SQL scripts. + *

Used if neither a custom defined separator nor the + * {@link #DEFAULT_STATEMENT_SEPARATOR} is present in a given script. + */ + public static final String FALLBACK_STATEMENT_SEPARATOR = "\n"; + + /** + * Default prefix for line comments within SQL scripts. + */ + public static final String DEFAULT_COMMENT_PREFIX = "--"; + + /** + * Default start delimiter for block comments within SQL scripts. + */ + public static final String DEFAULT_BLOCK_COMMENT_START_DELIMITER = "/*"; + + /** + * Default end delimiter for block comments within SQL scripts. + */ + public static final String DEFAULT_BLOCK_COMMENT_END_DELIMITER = "*/"; + + + /** + * Prevent instantiation of this utility class. + */ + private ScriptUtils() { + /* no-op */ + } + + /** + * Split an SQL script into separate statements delimited by the provided + * separator string. Each individual statement will be added to the provided + * {@code List}. + *

Within the script, the provided {@code commentPrefix} will be honored: + * any text beginning with the comment prefix and extending to the end of the + * line will be omitted from the output. Similarly, the provided + * {@code blockCommentStartDelimiter} and {@code blockCommentEndDelimiter} + * delimiters will be honored: any text enclosed in a block comment will be + * omitted from the output. In addition, multiple adjacent whitespace characters + * will be collapsed into a single space. + * @param resource the resource from which the script was read + * @param script the SQL script; never {@code null} or empty + * @param separator text separating each statement — typically a ';' or + * newline character; never {@code null} + * @param commentPrefix the prefix that identifies SQL line comments — + * typically "--"; never {@code null} or empty + * @param blockCommentStartDelimiter the start block comment delimiter; + * never {@code null} or empty + * @param blockCommentEndDelimiter the end block comment delimiter; + * never {@code null} or empty + * @param statements the list that will contain the individual statements + */ + public static void splitSqlScript(String resource, String script, String separator, String commentPrefix, + String blockCommentStartDelimiter, String blockCommentEndDelimiter, List statements) { + + checkArgument(StringUtils.isNotEmpty(script), "script must not be null or empty"); + checkArgument(separator != null, "separator must not be null"); + checkArgument(StringUtils.isNotEmpty(commentPrefix), "commentPrefix must not be null or empty"); + checkArgument(StringUtils.isNotEmpty(blockCommentStartDelimiter), "blockCommentStartDelimiter must not be null or empty"); + checkArgument(StringUtils.isNotEmpty(blockCommentEndDelimiter), "blockCommentEndDelimiter must not be null or empty"); + + StringBuilder sb = new StringBuilder(); + boolean inLiteral = false; + boolean inEscape = false; + char[] content = script.toCharArray(); + for (int i = 0; i < script.length(); i++) { + char c = content[i]; + if (inEscape) { + inEscape = false; + sb.append(c); + continue; + } + // MySQL style escapes + if (c == '\\') { + inEscape = true; + sb.append(c); + continue; + } + if (c == '\'') { + inLiteral = !inLiteral; + } + if (!inLiteral) { + if (script.startsWith(separator, i)) { + // we've reached the end of the current statement + if (sb.length() > 0) { + statements.add(sb.toString()); + sb = new StringBuilder(); + } + i += separator.length() - 1; + continue; + } + else if (script.startsWith(commentPrefix, i)) { + // skip over any content from the start of the comment to the EOL + int indexOfNextNewline = script.indexOf("\n", i); + if (indexOfNextNewline > i) { + i = indexOfNextNewline; + continue; + } + else { + // if there's no EOL, we must be at the end + // of the script, so stop here. + break; + } + } + else if (script.startsWith(blockCommentStartDelimiter, i)) { + // skip over any block comments + int indexOfCommentEnd = script.indexOf(blockCommentEndDelimiter, i); + if (indexOfCommentEnd > i) { + i = indexOfCommentEnd + blockCommentEndDelimiter.length() - 1; + continue; + } + else { + throw new ScriptParseException(String.format("Missing block comment end delimiter [%s].", + blockCommentEndDelimiter), resource); + } + } + else if (c == ' ' || c == '\n' || c == '\t') { + // avoid multiple adjacent whitespace characters + if (sb.length() > 0 && sb.charAt(sb.length() - 1) != ' ') { + c = ' '; + } + else { + continue; + } + } + } + sb.append(c); + } + if (StringUtils.isNotEmpty(sb.toString())) { + statements.add(sb.toString()); + } + } + + private static void checkArgument(boolean expression, String errorMessage) { + if (!expression) { + throw new IllegalArgumentException(errorMessage); + } + } + + /** + * Does the provided SQL script contain the specified delimiter? + * @param script the SQL script + * @param delim String delimiting each statement - typically a ';' character + */ + public static boolean containsSqlScriptDelimiters(String script, String delim) { + boolean inLiteral = false; + char[] content = script.toCharArray(); + for (int i = 0; i < script.length(); i++) { + if (content[i] == '\'') { + inLiteral = !inLiteral; + } + if (!inLiteral && script.startsWith(delim, i)) { + return true; + } + } + return false; + } + + public static void executeDatabaseScript(DatabaseDelegate databaseDelegate, String scriptPath, String script) throws ScriptException { + executeDatabaseScript(databaseDelegate, scriptPath, script, false, false, DEFAULT_COMMENT_PREFIX, DEFAULT_STATEMENT_SEPARATOR, DEFAULT_BLOCK_COMMENT_START_DELIMITER, DEFAULT_BLOCK_COMMENT_END_DELIMITER); + } + + /** + * Execute the given database script. + *

Statement separators and comments will be removed before executing + * individual statements within the supplied script. + *

Do not use this method to execute DDL if you expect rollback. + * @param databaseDelegate database delegate for script execution + * @param scriptPath the resource (potentially associated with a specific encoding) + * to load the SQL script from + * @param script the raw script content + *@param continueOnError whether or not to continue without throwing an exception + * in the event of an error + * @param ignoreFailedDrops whether or not to continue in the event of specifically +* an error on a {@code DROP} statement + * @param commentPrefix the prefix that identifies comments in the SQL script — +* typically "--" + * @param separator the script statement separator; defaults to +* {@value #DEFAULT_STATEMENT_SEPARATOR} if not specified and falls back to +* {@value #FALLBACK_STATEMENT_SEPARATOR} as a last resort + * @param blockCommentStartDelimiter the start block comment delimiter; never +* {@code null} or empty + * @param blockCommentEndDelimiter the end block comment delimiter; never +* {@code null} or empty @throws ScriptException if an error occurred while executing the SQL script + */ + public static void executeDatabaseScript(DatabaseDelegate databaseDelegate, String scriptPath, String script, boolean continueOnError, + boolean ignoreFailedDrops, String commentPrefix, String separator, String blockCommentStartDelimiter, + String blockCommentEndDelimiter) throws ScriptException { + + try { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Executing database script from " + scriptPath); + } + + long startTime = System.currentTimeMillis(); + List statements = new LinkedList<>(); + + if (separator == null) { + separator = DEFAULT_STATEMENT_SEPARATOR; + } + if (!containsSqlScriptDelimiters(script, separator)) { + separator = FALLBACK_STATEMENT_SEPARATOR; + } + + splitSqlScript(scriptPath, script, separator, commentPrefix, blockCommentStartDelimiter, + blockCommentEndDelimiter, statements); + + try (DatabaseDelegate closeableDelegate = databaseDelegate) { + closeableDelegate.execute(statements, scriptPath, continueOnError, ignoreFailedDrops); + } + + long elapsedTime = System.currentTimeMillis() - startTime; + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Executed database script from " + scriptPath + " in " + elapsedTime + " ms."); + } + } + catch (Exception ex) { + if (ex instanceof ScriptException) { + throw (ScriptException) ex; + } + + throw new UncategorizedScriptException( + "Failed to execute database script from resource [" + script + "]", ex); + } + } + + private static class ScriptParseException extends RuntimeException { + public ScriptParseException(String format, String scriptPath) { + super(String.format(format, scriptPath)); + } + } + + public static class ScriptStatementFailedException extends RuntimeException { + public ScriptStatementFailedException(String statement, int lineNumber, String scriptPath, Exception ex) { + super(String.format("Script execution failed (%s:%d): %s", scriptPath, lineNumber, statement), ex); + } + } + + private static class UncategorizedScriptException extends RuntimeException { + public UncategorizedScriptException(String s, Exception ex) { + super(s, ex); + } + } +} diff --git a/modules/jdbc/pom.xml b/modules/jdbc/pom.xml index 0fe42c3f4f0..72614a410e6 100644 --- a/modules/jdbc/pom.xml +++ b/modules/jdbc/pom.xml @@ -18,6 +18,12 @@ testcontainers ${project.version} + + + ${project.groupId} + database-commons + ${project.version} + diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java index d965fb83d03..b7bd9fb4925 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java @@ -4,7 +4,8 @@ import org.slf4j.LoggerFactory; import org.testcontainers.containers.JdbcDatabaseContainer; import org.testcontainers.containers.JdbcDatabaseContainerProvider; -import org.testcontainers.jdbc.ext.ScriptUtils; +import org.testcontainers.delegate.DatabaseDelegate; +import org.testcontainers.ext.ScriptUtils; import javax.script.ScriptException; import java.io.IOException; @@ -153,7 +154,8 @@ public synchronized Connection connect(String url, final Properties info) throws an init script or function has been specified, use it */ if (!initializedContainers.contains(container.getContainerId())) { - runInitScriptIfRequired(url, connection); + DatabaseDelegate databaseDelegate = new JdbcDatabaseDelegate(container); + runInitScriptIfRequired(url, databaseDelegate); runInitFunctionIfRequired(url, connection); initializedContainers.add(container.getContainerId()); } @@ -214,10 +216,10 @@ private Connection wrapConnection(final Connection connection, final JdbcDatabas * Run an init script from the classpath. * * @param url the JDBC URL to check for init script declarations. - * @param connection JDBC connection to apply init scripts to. + * @param databaseDelegate database delegate to apply init scripts to the database * @throws SQLException on script or DB error */ - private void runInitScriptIfRequired(String url, Connection connection) throws SQLException { + private void runInitScriptIfRequired(String url, DatabaseDelegate databaseDelegate) throws SQLException { Matcher matcher = INITSCRIPT_MATCHING_PATTERN.matcher(url); if (matcher.matches()) { String initScriptPath = matcher.group(2); @@ -230,7 +232,7 @@ private void runInitScriptIfRequired(String url, Connection connection) throws S } String sql = IOUtils.toString(resource, StandardCharsets.UTF_8); - ScriptUtils.executeSqlScript(connection, initScriptPath, sql); + ScriptUtils.executeDatabaseScript(databaseDelegate, initScriptPath, sql); } catch (IOException e) { LOGGER.warn("Could not load classpath init script: {}", initScriptPath); throw new SQLException("Could not load classpath init script: " + initScriptPath, e); diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerLessJdbcDelegate.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerLessJdbcDelegate.java new file mode 100644 index 00000000000..7a22d139bea --- /dev/null +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerLessJdbcDelegate.java @@ -0,0 +1,36 @@ +package org.testcontainers.jdbc; + +import lombok.extern.slf4j.Slf4j; +import org.testcontainers.exception.ConnectionCreationException; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +/** + * Containerless jdbc database delegate + * + * Is used only with deprecated ScriptUtils + * + * @see org.testcontainers.ext.ScriptUtils + */ +@Slf4j +public class ContainerLessJdbcDelegate extends JdbcDatabaseDelegate { + + private Connection connection; + + public ContainerLessJdbcDelegate(Connection connection) { + super(null); + this.connection = connection; + } + + @Override + protected Statement createNewConnection() { + try { + return connection.createStatement(); + } catch (SQLException e) { + log.error("Could create JDBC statement"); + throw new ConnectionCreationException("Could create JDBC statement", e); + } + } +} diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/JdbcDatabaseDelegate.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/JdbcDatabaseDelegate.java new file mode 100644 index 00000000000..e490da87c4a --- /dev/null +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/JdbcDatabaseDelegate.java @@ -0,0 +1,60 @@ +package org.testcontainers.jdbc; + +import lombok.extern.slf4j.Slf4j; +import org.testcontainers.containers.JdbcDatabaseContainer; +import org.testcontainers.delegate.AbstractDatabaseDelegate; +import org.testcontainers.exception.ConnectionCreationException; +import org.testcontainers.ext.ScriptUtils; + +import java.sql.SQLException; +import java.sql.Statement; + +/** + * JDBC database delegate + * + * @author Eugeny Karpov + */ +@Slf4j +public class JdbcDatabaseDelegate extends AbstractDatabaseDelegate { + + private JdbcDatabaseContainer container; + + public JdbcDatabaseDelegate(JdbcDatabaseContainer container) { + this.container = container; + } + + @Override + protected Statement createNewConnection() { + try { + return container.createConnection("").createStatement(); + } catch (SQLException e) { + log.error("Could not obtain JDBC connection"); + throw new ConnectionCreationException("Could not obtain JDBC connection", e); + } + } + + + @Override + public void execute(String statement, String scriptPath, int lineNumber, boolean continueOnError, boolean ignoreFailedDrops) { + try { + boolean rowsAffected = getConnection().execute(statement); + log.debug("{} returned as updateCount for SQL: {}", rowsAffected, statement); + } catch (SQLException ex) { + boolean dropStatement = statement.trim().toLowerCase().startsWith("drop"); + if (continueOnError || (dropStatement && ignoreFailedDrops)) { + log.debug("Failed to execute SQL script statement at line {} of resource {}: {}", lineNumber, scriptPath, statement, ex); + } else { + throw new ScriptUtils.ScriptStatementFailedException(statement, lineNumber, scriptPath, ex); + } + } + } + + @Override + protected void closeConnectionQuietly(Statement statement) { + try { + statement.close(); + } catch (Exception e) { + log.error("Could not close JDBC connection", e); + } + } +} diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ext/ScriptUtils.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ext/ScriptUtils.java index 286ea324f42..76b7a044c9d 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ext/ScriptUtils.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ext/ScriptUtils.java @@ -16,316 +16,87 @@ package org.testcontainers.jdbc.ext; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.testcontainers.jdbc.ContainerLessJdbcDelegate; import javax.script.ScriptException; import java.sql.Connection; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.LinkedList; import java.util.List; /** - * This is a modified version of the Spring-JDBC ScriptUtils class, adapted to reduce - * dependencies and slightly alter the API. + * Wrapper for database-agnostic ScriptUtils * - * Generic utility methods for working with SQL scripts. Mainly for internal use - * within the framework. - * - * @author Thomas Risberg - * @author Sam Brannen - * @author Juergen Hoeller - * @author Keith Donald - * @author Dave Syer - * @author Chris Beams - * @author Oliver Gierke - * @author Chris Baldwin - * @since 4.0.3 + * @see org.testcontainers.ext.ScriptUtils + * @deprecated Needed only to keep binary compatibility for this internal API. Consider using database-agnostic ScriptUtils */ public abstract class ScriptUtils { - private static final Logger LOGGER = LoggerFactory.getLogger(ScriptUtils.class); - - /** - * Default statement separator within SQL scripts. - */ - public static final String DEFAULT_STATEMENT_SEPARATOR = ";"; - - /** - * Fallback statement separator within SQL scripts. - *

Used if neither a custom defined separator nor the - * {@link #DEFAULT_STATEMENT_SEPARATOR} is present in a given script. - */ - public static final String FALLBACK_STATEMENT_SEPARATOR = "\n"; - - /** - * Default prefix for line comments within SQL scripts. - */ - public static final String DEFAULT_COMMENT_PREFIX = "--"; - - /** - * Default start delimiter for block comments within SQL scripts. - */ - public static final String DEFAULT_BLOCK_COMMENT_START_DELIMITER = "/*"; - - /** - * Default end delimiter for block comments within SQL scripts. - */ - public static final String DEFAULT_BLOCK_COMMENT_END_DELIMITER = "*/"; - - - /** - * Prevent instantiation of this utility class. - */ - private ScriptUtils() { - /* no-op */ - } - /** - * Split an SQL script into separate statements delimited by the provided - * separator string. Each individual statement will be added to the provided - * {@code List}. - *

Within the script, the provided {@code commentPrefix} will be honored: - * any text beginning with the comment prefix and extending to the end of the - * line will be omitted from the output. Similarly, the provided - * {@code blockCommentStartDelimiter} and {@code blockCommentEndDelimiter} - * delimiters will be honored: any text enclosed in a block comment will be - * omitted from the output. In addition, multiple adjacent whitespace characters - * will be collapsed into a single space. - * @param resource the resource from which the script was read - * @param script the SQL script; never {@code null} or empty - * @param separator text separating each statement — typically a ';' or - * newline character; never {@code null} - * @param commentPrefix the prefix that identifies SQL line comments — - * typically "--"; never {@code null} or empty - * @param blockCommentStartDelimiter the start block comment delimiter; - * never {@code null} or empty - * @param blockCommentEndDelimiter the end block comment delimiter; - * never {@code null} or empty - * @param statements the list that will contain the individual statements - */ - public static void splitSqlScript(String resource, String script, String separator, String commentPrefix, - String blockCommentStartDelimiter, String blockCommentEndDelimiter, List statements) { + * Default statement separator within SQL scripts. + */ + public static final String DEFAULT_STATEMENT_SEPARATOR = ";"; - checkArgument(StringUtils.isNotEmpty(script), "script must not be null or empty"); - checkArgument(separator != null, "separator must not be null"); - checkArgument(StringUtils.isNotEmpty(commentPrefix), "commentPrefix must not be null or empty"); - checkArgument(StringUtils.isNotEmpty(blockCommentStartDelimiter), "blockCommentStartDelimiter must not be null or empty"); - checkArgument(StringUtils.isNotEmpty(blockCommentEndDelimiter), "blockCommentEndDelimiter must not be null or empty"); + /** + * Fallback statement separator within SQL scripts. + *

Used if neither a custom defined separator nor the + * {@link #DEFAULT_STATEMENT_SEPARATOR} is present in a given script. + */ + public static final String FALLBACK_STATEMENT_SEPARATOR = "\n"; - StringBuilder sb = new StringBuilder(); - boolean inLiteral = false; - boolean inEscape = false; - char[] content = script.toCharArray(); - for (int i = 0; i < script.length(); i++) { - char c = content[i]; - if (inEscape) { - inEscape = false; - sb.append(c); - continue; - } - // MySQL style escapes - if (c == '\\') { - inEscape = true; - sb.append(c); - continue; - } - if (c == '\'') { - inLiteral = !inLiteral; - } - if (!inLiteral) { - if (script.startsWith(separator, i)) { - // we've reached the end of the current statement - if (sb.length() > 0) { - statements.add(sb.toString()); - sb = new StringBuilder(); - } - i += separator.length() - 1; - continue; - } - else if (script.startsWith(commentPrefix, i)) { - // skip over any content from the start of the comment to the EOL - int indexOfNextNewline = script.indexOf("\n", i); - if (indexOfNextNewline > i) { - i = indexOfNextNewline; - continue; - } - else { - // if there's no EOL, we must be at the end - // of the script, so stop here. - break; - } - } - else if (script.startsWith(blockCommentStartDelimiter, i)) { - // skip over any block comments - int indexOfCommentEnd = script.indexOf(blockCommentEndDelimiter, i); - if (indexOfCommentEnd > i) { - i = indexOfCommentEnd + blockCommentEndDelimiter.length() - 1; - continue; - } - else { - throw new ScriptParseException(String.format("Missing block comment end delimiter [%s].", - blockCommentEndDelimiter), resource); - } - } - else if (c == ' ' || c == '\n' || c == '\t') { - // avoid multiple adjacent whitespace characters - if (sb.length() > 0 && sb.charAt(sb.length() - 1) != ' ') { - c = ' '; - } - else { - continue; - } - } - } - sb.append(c); - } - if (StringUtils.isNotEmpty(sb.toString())) { - statements.add(sb.toString()); - } - } + /** + * Default prefix for line comments within SQL scripts. + */ + public static final String DEFAULT_COMMENT_PREFIX = "--"; - private static void checkArgument(boolean expression, String errorMessage) { - if (!expression) { - throw new IllegalArgumentException(errorMessage); - } - } + /** + * Default start delimiter for block comments within SQL scripts. + */ + public static final String DEFAULT_BLOCK_COMMENT_START_DELIMITER = "/*"; /** - * Does the provided SQL script contain the specified delimiter? - * @param script the SQL script - * @param delim String delimiting each statement - typically a ';' character - */ - public static boolean containsSqlScriptDelimiters(String script, String delim) { - boolean inLiteral = false; - char[] content = script.toCharArray(); - for (int i = 0; i < script.length(); i++) { - if (content[i] == '\'') { - inLiteral = !inLiteral; - } - if (!inLiteral && script.startsWith(delim, i)) { - return true; - } - } - return false; - } + * Default end delimiter for block comments within SQL scripts. + */ + public static final String DEFAULT_BLOCK_COMMENT_END_DELIMITER = "*/"; - public static void executeSqlScript(Connection connection, String scriptPath, String script) throws ScriptException { - executeSqlScript(connection, scriptPath, script, false, false, DEFAULT_COMMENT_PREFIX, DEFAULT_STATEMENT_SEPARATOR, DEFAULT_BLOCK_COMMENT_START_DELIMITER, DEFAULT_BLOCK_COMMENT_END_DELIMITER); + /** + * Prevent instantiation of this utility class. + */ + private ScriptUtils() { + /* no-op */ } /** - * Execute the given SQL script. - *

Statement separators and comments will be removed before executing - * individual statements within the supplied script. - *

Do not use this method to execute DDL if you expect rollback. - * @param connection the JDBC connection to use to execute the script; already - * configured and ready to use - * @param scriptPath the resource (potentially associated with a specific encoding) - * to load the SQL script from - * @param script the raw script content - *@param continueOnError whether or not to continue without throwing an exception - * in the event of an error - * @param ignoreFailedDrops whether or not to continue in the event of specifically -* an error on a {@code DROP} statement - * @param commentPrefix the prefix that identifies comments in the SQL script — -* typically "--" - * @param separator the script statement separator; defaults to -* {@value #DEFAULT_STATEMENT_SEPARATOR} if not specified and falls back to -* {@value #FALLBACK_STATEMENT_SEPARATOR} as a last resort - * @param blockCommentStartDelimiter the start block comment delimiter; never -* {@code null} or empty - * @param blockCommentEndDelimiter the end block comment delimiter; never -* {@code null} or empty @throws ScriptException if an error occurred while executing the SQL script - */ - public static void executeSqlScript(Connection connection, String scriptPath, String script, boolean continueOnError, - boolean ignoreFailedDrops, String commentPrefix, String separator, String blockCommentStartDelimiter, - String blockCommentEndDelimiter) throws ScriptException { - - try { - if (LOGGER.isInfoEnabled()) { - LOGGER.info("Executing SQL script from " + scriptPath); - } - - long startTime = System.currentTimeMillis(); - List statements = new LinkedList<>(); - - if (separator == null) { - separator = DEFAULT_STATEMENT_SEPARATOR; - } - if (!containsSqlScriptDelimiters(script, separator)) { - separator = FALLBACK_STATEMENT_SEPARATOR; - } - - splitSqlScript(scriptPath, script, separator, commentPrefix, blockCommentStartDelimiter, - blockCommentEndDelimiter, statements); - int lineNumber = 0; - Statement stmt = connection.createStatement(); - try { - for (String statement : statements) { - lineNumber++; - try { - stmt.execute(statement); - int rowsAffected = stmt.getUpdateCount(); - if (LOGGER.isDebugEnabled()) { - LOGGER.debug(rowsAffected + " returned as updateCount for SQL: " + statement); - } - } - catch (SQLException ex) { - boolean dropStatement = statement.trim().toLowerCase().startsWith("drop"); - if (continueOnError || (dropStatement && ignoreFailedDrops)) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Failed to execute SQL script statement at line " + lineNumber - + " of resource " + scriptPath + ": " + statement, ex); - } - } - else { - throw new ScriptStatementFailedException(statement, lineNumber, scriptPath, ex); - } - } - } - } - finally { - try { - stmt.close(); - } - catch (Throwable ex) { - LOGGER.debug("Could not close JDBC Statement", ex); - } - } - - long elapsedTime = System.currentTimeMillis() - startTime; - if (LOGGER.isInfoEnabled()) { - LOGGER.info("Executed SQL script from " + scriptPath + " in " + elapsedTime + " ms."); - } - } - catch (Exception ex) { - if (ex instanceof ScriptException) { - throw (ScriptException) ex; - } - - throw new UncategorizedScriptException( - "Failed to execute database script from resource [" + script + "]", ex); - } - } + * @see org.testcontainers.ext.ScriptUtils + * @deprecated Needed only to keep binary compatibility for this internal API. Consider using database-agnostic ScriptUtils + */ + public static void splitSqlScript(String resource, String script, String separator, String commentPrefix, + String blockCommentStartDelimiter, String blockCommentEndDelimiter, List statements) { + org.testcontainers.ext.ScriptUtils.splitSqlScript(resource, script, separator, commentPrefix, blockCommentStartDelimiter, blockCommentEndDelimiter, statements); + } - private static class ScriptParseException extends RuntimeException { - public ScriptParseException(String format, String scriptPath) { - super(String.format(format, scriptPath)); - } + /** + * @see org.testcontainers.ext.ScriptUtils + * @deprecated Needed only to keep binary compatibility for this internal API. Consider using database-agnostic ScriptUtils + */ + public static boolean containsSqlScriptDelimiters(String script, String delim) { + return org.testcontainers.ext.ScriptUtils.containsSqlScriptDelimiters(script, delim); } - private static class ScriptStatementFailedException extends RuntimeException { - public ScriptStatementFailedException(String statement, int lineNumber, String scriptPath, SQLException ex) { - super(String.format("Script execution failed (%s:%d): %s", scriptPath, lineNumber, statement), ex); - } + /** + * @see org.testcontainers.ext.ScriptUtils + * @deprecated Needed only to keep binary compatibility for this internal API. Consider using database-agnostic ScriptUtils + */ + public static void executeSqlScript(Connection connection, String scriptPath, String script) throws ScriptException { + org.testcontainers.ext.ScriptUtils.executeDatabaseScript(new ContainerLessJdbcDelegate(connection), scriptPath, script); } - private static class UncategorizedScriptException extends RuntimeException { - public UncategorizedScriptException(String s, Exception ex) { - super(s, ex); - } + /** + * @see org.testcontainers.ext.ScriptUtils + * @deprecated Needed only to keep binary compatibility for this internal API. Consider using database-agnostic ScriptUtils + */ + public static void executeSqlScript(Connection connection, String scriptPath, String script, boolean continueOnError, + boolean ignoreFailedDrops, String commentPrefix, String separator, String blockCommentStartDelimiter, + String blockCommentEndDelimiter) throws ScriptException { + org.testcontainers.ext.ScriptUtils.executeDatabaseScript(new ContainerLessJdbcDelegate(connection), scriptPath, + script, continueOnError, ignoreFailedDrops, commentPrefix, separator, blockCommentStartDelimiter, blockCommentEndDelimiter); } -} +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 90453b8839d..d291d174523 100644 --- a/pom.xml +++ b/pom.xml @@ -167,6 +167,7 @@ modules/nginx modules/kafka modules/jdbc-test + modules/database-commons From a66cfb038f70859ebdf5e728f0e8b21721ca753e Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 8 Feb 2018 20:52:30 +0000 Subject: [PATCH 0236/5615] Change capitalisation of the project name to the commonly used form, Testcontainers (#573) --- README.md | 4 ++-- book.js | 2 +- core/pom.xml | 2 +- docs/index.md | 8 ++++---- docs/usage.md | 6 +++--- docs/usage/database_containers.md | 18 +++++++++--------- docs/usage/docker_compose.md | 2 +- docs/usage/inside_docker.md | 4 ++-- modules/database-commons/pom.xml | 2 +- modules/jdbc-test/pom.xml | 2 +- modules/jdbc/pom.xml | 2 +- modules/kafka/pom.xml | 2 +- modules/mysql/pom.xml | 2 +- modules/nginx/pom.xml | 2 +- modules/postgresql/pom.xml | 2 +- modules/selenium/pom.xml | 2 +- modules/virtuoso/pom.xml | 2 +- pom.xml | 2 +- 18 files changed, 33 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 47e257ed54f..16bcd0120ec 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# TestContainers +# Testcontainers -> TestContainers is a Java 8 library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container. +> Testcontainers is a Java 8 library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container. [![Build Status](https://travis-ci.org/testcontainers/testcontainers-java.svg?branch=master)](https://travis-ci.org/testcontainers/testcontainers-java) diff --git a/book.js b/book.js index eaa8f94536d..5dbad218194 100644 --- a/book.js +++ b/book.js @@ -1,7 +1,7 @@ module.exports = { root: "./docs", - title: "TestContainers", + title: "Testcontainers", structure: { readme: "index.md" diff --git a/core/pom.xml b/core/pom.xml index 482a1d5f18f..fe13988ec63 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ testcontainers - TestContainers Core + Testcontainers Core diff --git a/docs/index.md b/docs/index.md index b7985f3508a..f47eadd04a8 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,6 @@ -# TestContainers +# Testcontainers -> TestContainers is a Java library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container. +> Testcontainers is a Java library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container. [![Build Status](https://travis-ci.org/testcontainers/testcontainers-java.svg?branch=master)](https://travis-ci.org/testcontainers/testcontainers-java) [View on Github](https://github.com/testcontainers/testcontainers-java) @@ -9,14 +9,14 @@ ## Use Cases -TestContainers makes it easy to launch useful Docker containers for the duration of JUnit tests. +Testcontainers makes it easy to launch useful Docker containers for the duration of JUnit tests. * **Data access layer integration tests**: use a containerized instance of a MySQL, PostgreSQL or Oracle database to test your data access layer code for complete compatibility, but without requiring complex setup on developers' machines and safe in the knowledge that your tests will always start with a known DB state. Any other database type that can be containerized can also be used. * **Application integration tests**: for running your application in a short-lived test mode with dependencies, such as databases, message queues or web servers. * **UI/Acceptance tests**: use containerized web browsers, compatible with Selenium, for conducting automated UI tests. Each test can get a fresh instance of the browser, with no browser state, plugin variations or automated browser upgrades to worry about. And you get a video recording of each test session, or just each session where tests failed. * **Much more!** Check out the various [contributed modules](https://github.com/testcontainers) or create your own custom container classes using [`GenericContainer`](usage/generic_containers.md) as a base. -## Who is using TestContainers? +## Who is using Testcontainers? * [ZeroTurnaround](https://zeroturnaround.com) - Testing of the Java Agents, micro-services, Selenium browser automation * [Zipkin](http://zipkin.io) - MySQL and Cassandra testing diff --git a/docs/usage.md b/docs/usage.md index 8a166c6b1bc..0a1401733c1 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -2,9 +2,9 @@ ### Prerequisites -Docker or docker-machine (for OS X) must be installed on the machine you are running tests on. TestContainers currently requires JDK 1.8 and is compatible with JUnit. +Docker or docker-machine (for OS X) must be installed on the machine you are running tests on. Testcontainers currently requires JDK 1.8 and is compatible with JUnit. -If you want to use TestContainers on Windows you can try the [alpha release](usage/windows_support.md). +If you want to use Testcontainers on Windows you can try the [alpha release](usage/windows_support.md). ### Docker environment discovery @@ -31,7 +31,7 @@ Testcontainers will try to connect to a Docker daemon using the following strate ## Maven dependencies -TestContainers is distributed in a handful of Maven modules: +Testcontainers is distributed in a handful of Maven modules: * **testcontainers** for just core functionality, generic containers and docker-compose support * **mysql**, **postgresql** or **oracle-xe** for database container support diff --git a/docs/usage/database_containers.md b/docs/usage/database_containers.md index fd922ef5654..e67c97cc7e8 100644 --- a/docs/usage/database_containers.md +++ b/docs/usage/database_containers.md @@ -2,19 +2,19 @@ ## Benefits -You might want to use TestContainers' database support: +You might want to use Testcontainers' database support: - * **Instead of H2 database for DAO unit tests that depend on database features that H2 doesn't emulate.** TestContainers is not as performant as H2, but does give you the benefit of 100% database compatibility (since it runs a real DB inside of a container). - * **Instead of a database running on the local machine or in a VM** for DAO unit tests or end-to-end integration tests that need a database to be present. In this context, the benefit of TestContainers is that the database always starts in a known state, without any contamination between test runs or on developers' local machines. + * **Instead of H2 database for DAO unit tests that depend on database features that H2 doesn't emulate.** Testcontainers is not as performant as H2, but does give you the benefit of 100% database compatibility (since it runs a real DB inside of a container). + * **Instead of a database running on the local machine or in a VM** for DAO unit tests or end-to-end integration tests that need a database to be present. In this context, the benefit of Testcontainers is that the database always starts in a known state, without any contamination between test runs or on developers' local machines. > Note: Of course, it's still important to have as few tests that hit the database as possible, and make good use of mocks for components higher up the stack. You can obtain a temporary database in one of two ways: * **JUnit @Rule/@ClassRule**: this mode starts a database inside a container before your tests and tears it down afterwards. - * **Using a specially modified JDBC URL**: after making a very simple modification to your system's JDBC URL string, TestContainers will provide a disposable stand-in database that can be used without requiring modification to your application code. + * **Using a specially modified JDBC URL**: after making a very simple modification to your system's JDBC URL string, Testcontainers will provide a disposable stand-in database that can be used without requiring modification to your application code. -TestContainers currently supports MySQL, PostgreSQL, Oracle XE and Virtuoso. +Testcontainers currently supports MySQL, PostgreSQL, Oracle XE and Virtuoso. > Note: Oracle XE support does not bundle the proprietary Oracle JDBC drivers - you must provide these yourself. @@ -47,7 +47,7 @@ Examples/Tests: ### JDBC URL -As long as you have TestContainers and the appropriate JDBC driver on your classpath, you can simply modify regular JDBC connection URLs to get a fresh containerized instance of the database each time your application starts up. +As long as you have Testcontainers and the appropriate JDBC driver on your classpath, you can simply modify regular JDBC connection URLs to get a fresh containerized instance of the database each time your application starts up. _N.B:_ * _TC needs to be on your application's classpath at runtime for this to work_ @@ -59,13 +59,13 @@ Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database ### JDBC URL examples -#### Simple TestContainers JDBC driver usage +#### Simple Testcontainers JDBC driver usage `jdbc:tc:mysql://somehostname:someport/databasename` *(Note: this will use the latest version of MySQL)* -#### Using TestContainers with a fixed version +#### Using Testcontainers with a fixed version `jdbc:tc:mysql:5.6.23://somehostname:someport/databasename` @@ -76,7 +76,7 @@ Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database ## Using an init script -TestContainers can run an initscript after the database container is started, but before your code is given a connection to it. The script must be on the classpath, and is referenced as follows: +Testcontainers can run an initscript after the database container is started, but before your code is given a connection to it. The script must be on the classpath, and is referenced as follows: `jdbc:tc:mysql://hostname/databasename?TC_INITSCRIPT=somepath/init_mysql.sql` diff --git a/docs/usage/docker_compose.md b/docs/usage/docker_compose.md index 86d11a2486d..2da864f16ca 100644 --- a/docs/usage/docker_compose.md +++ b/docs/usage/docker_compose.md @@ -93,4 +93,4 @@ To work around this problem, create `config.json` in separate location with real "credsStore" : "osxkeychain" } ``` -and specify the location to TestContainers using any of the two first methods from above. \ No newline at end of file +and specify the location to Testcontainers using any of the two first methods from above. \ No newline at end of file diff --git a/docs/usage/inside_docker.md b/docs/usage/inside_docker.md index f4773dcb147..7dfd25c8f60 100644 --- a/docs/usage/inside_docker.md +++ b/docs/usage/inside_docker.md @@ -5,7 +5,7 @@ Testcontainers itself can be used from inside a container. This is very useful for different CI scenarios like running everything in containers on Jenkins, or Docker-based CI tools such as Drone. -TestContainers will automatically detect if it's inside a container and instead of "localhost" will use the default gateway's IP. +Testcontainers will automatically detect if it's inside a container and instead of "localhost" will use the default gateway's IP. However, additional configuration is required if you use [volume mapping](options.md#volume-mapping). The following points need to be considered: @@ -21,7 +21,7 @@ $ tree . └── src └── test └── java - └── MyTestWithTestContainers.java + └── MyTestWithTestcontainers.java $ docker run -it --rm -v $PWD:$PWD -w $PWD -v /var/run/docker.sock:/var/run/docker.sock maven:3 mvn test ``` diff --git a/modules/database-commons/pom.xml b/modules/database-commons/pom.xml index 965c0c0070c..3c4ec520a24 100644 --- a/modules/database-commons/pom.xml +++ b/modules/database-commons/pom.xml @@ -10,7 +10,7 @@ database-commons - TestContainers :: Database-Commons + Testcontainers :: Database-Commons diff --git a/modules/jdbc-test/pom.xml b/modules/jdbc-test/pom.xml index d8c1816c140..f7013501e3f 100644 --- a/modules/jdbc-test/pom.xml +++ b/modules/jdbc-test/pom.xml @@ -11,7 +11,7 @@ jdbc-test - TestContainers :: JDBC :: Tests + Testcontainers :: JDBC :: Tests diff --git a/modules/jdbc/pom.xml b/modules/jdbc/pom.xml index 72614a410e6..a9efce8c04d 100644 --- a/modules/jdbc/pom.xml +++ b/modules/jdbc/pom.xml @@ -10,7 +10,7 @@ jdbc - TestContainers :: JDBC + Testcontainers :: JDBC diff --git a/modules/kafka/pom.xml b/modules/kafka/pom.xml index 09a20d01fe0..8d004cab1f1 100644 --- a/modules/kafka/pom.xml +++ b/modules/kafka/pom.xml @@ -11,7 +11,7 @@ kafka - TestContainers :: Apache Kafka + Testcontainers :: Apache Kafka diff --git a/modules/mysql/pom.xml b/modules/mysql/pom.xml index d9b75b57f24..712ce9d4980 100644 --- a/modules/mysql/pom.xml +++ b/modules/mysql/pom.xml @@ -10,7 +10,7 @@ mysql - TestContainers :: JDBC :: MySQL + Testcontainers :: JDBC :: MySQL diff --git a/modules/nginx/pom.xml b/modules/nginx/pom.xml index 33b7bb8a88f..686fc42b842 100644 --- a/modules/nginx/pom.xml +++ b/modules/nginx/pom.xml @@ -10,7 +10,7 @@ nginx - TestContainers :: nginx + Testcontainers :: nginx diff --git a/modules/postgresql/pom.xml b/modules/postgresql/pom.xml index f5100801283..8f1b091277e 100644 --- a/modules/postgresql/pom.xml +++ b/modules/postgresql/pom.xml @@ -10,7 +10,7 @@ postgresql - TestContainers :: JDBC :: PostgreSQL + Testcontainers :: JDBC :: PostgreSQL diff --git a/modules/selenium/pom.xml b/modules/selenium/pom.xml index af4e894365b..496445d8eed 100644 --- a/modules/selenium/pom.xml +++ b/modules/selenium/pom.xml @@ -11,7 +11,7 @@ selenium - TestContainers :: Selenium + Testcontainers :: Selenium diff --git a/modules/virtuoso/pom.xml b/modules/virtuoso/pom.xml index 7884346e640..bab0a5dfe1f 100644 --- a/modules/virtuoso/pom.xml +++ b/modules/virtuoso/pom.xml @@ -9,7 +9,7 @@ virtuoso - TestContainers :: JDBC :: Virtuoso + Testcontainers :: JDBC :: Virtuoso diff --git a/pom.xml b/pom.xml index d291d174523..1b1d1e7335b 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ pom - TestContainers Parent POM + Testcontainers Parent POM Isolated container management for Java code testing From a9da708e96552181f421ea732fc9c97af46dd99a Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 8 Feb 2018 20:56:07 +0000 Subject: [PATCH 0237/5615] Fix broken link Fixes #578 --- docs/ci/ci.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ci/ci.md b/docs/ci/ci.md index 1c626451a55..cdec2554637 100644 --- a/docs/ci/ci.md +++ b/docs/ci/ci.md @@ -3,7 +3,7 @@ ## GitLab -To be able to run your test container into the pipeline you need to run the job as a docker container (see [Running inside Docker](usage/inside_docker.md)), +To be able to run your test container into the pipeline you need to run the job as a docker container (see [Running inside Docker](../usage/inside_docker.md)), so you need Docker-In-Docker (docker:dind) service and mount the docker's socket. Also if you have "Permission denied" you need to run your container as root user: From 4e15af823c2feacffd42065fdfcc423854be60d3 Mon Sep 17 00:00:00 2001 From: Ivan Rasikhin Date: Fri, 9 Feb 2018 20:12:50 +0300 Subject: [PATCH 0238/5615] Add ability to specify docker image in KafkaContainer. (#580) * Add ability to specify docker image in KafkaContainer. * Added 'kafka.container.image' property to TestcontainersConfiguration in order to have an opportunity to replace default image with testcontainers.properties. * Return version argument to KafkaContainer constructor. --- .../testcontainers/utility/TestcontainersConfiguration.java | 4 ++++ docs/usage/properties.md | 3 +++ .../java/org/testcontainers/containers/KafkaContainer.java | 3 ++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index d62728a4338..3a6ef4d2537 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -50,6 +50,10 @@ public String getRyukImage() { return (String) properties.getOrDefault("ryuk.container.image", "bsideup/moby-ryuk:0.2.2"); } + public String getKafkaImage() { + return (String) properties.getOrDefault("kafka.container.image", "confluentinc/cp-kafka"); + } + public boolean isDisableChecks() { return Boolean.parseBoolean((String) properties.getOrDefault("checks.disable", "false")); } diff --git a/docs/usage/properties.md b/docs/usage/properties.md index 8890c5581e0..bfe9233dcb5 100644 --- a/docs/usage/properties.md +++ b/docs/usage/properties.md @@ -34,5 +34,8 @@ It takes a couple of seconds, but if you want to speed up your tests, you can di > **compose.container.image = docker/compose:1.8.0** > Used by Docker Compose integration +> **kafka.container.image = confluentinc/cp-kafka** +> Used by KafkaContainer + Testcontainers uses public Docker images to perform different actions like startup checks, VNC recording and others. Some companies disallow the usage of Docker Hub, but you can override `*.image` properties with your own images from your private registry to workaround that. diff --git a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java index 924dc122442..b9d7afeeaa5 100644 --- a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java +++ b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java @@ -1,6 +1,7 @@ package org.testcontainers.containers; import org.testcontainers.utility.Base58; +import org.testcontainers.utility.TestcontainersConfiguration; import java.util.stream.Stream; @@ -23,7 +24,7 @@ public KafkaContainer() { } public KafkaContainer(String confluentPlatformVersion) { - super("confluentinc/cp-kafka:" + confluentPlatformVersion); + super(TestcontainersConfiguration.getInstance().getKafkaImage() + ":" + confluentPlatformVersion); withNetwork(Network.newNetwork()); String networkAlias = "kafka-" + Base58.randomString(6); From fe62ccff8724ec58ec71b4cef8b520f56ed78833 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 11 Feb 2018 17:48:24 +0100 Subject: [PATCH 0239/5615] Gradle (#574) * Add Gradle with dependency sync from Maven * use Gradle on CircleCI * run Maven before Gradle to create initial folders structure * Shadowing, Maven publishing, build cache * Use Gradle 4.5 * store test results in CircleCI * fix tests * Create 'target' directory before running the tests, run tests in parallel on CircleCI * run selenium tests separately * change only Kafka module * now Travis * fix Travis config * mount .gradle into alpine container * polish Gradle config, fix publishing, add Delombok, move JAR file tests to Gradle * fix tab space * do not force verbose console * fix core tests * revert KafkaContainerTest change * push only from CircleCI's master branch * remove Maven, add more info to the generated pom, remove shade-test * flat modules structure, bintray publishing, BOM, remove virtuoso module * fix CI configs (use 'testcontainers' instead of 'core') * fix TravisCI config * release with Gradle * use Gradle for database-commons * simplify BOM's code * Force LF line endings for all text files This is introduced to allow the build remote cache on Windows. * Change capitalisation of the project name to the commonly used form, Testcontainers (#573) * Fix broken link Fixes #578 * Add ability to specify docker image in KafkaContainer. (#580) * Add ability to specify docker image in KafkaContainer. * Added 'kafka.container.image' property to TestcontainersConfiguration in order to have an opportunity to replace default image with testcontainers.properties. * Return version argument to KafkaContainer constructor. * use Gradle's `check` task instead of just `test`, document how to disable build caching, * fix Travis' install --- .gitattributes | 2 +- .gitignore | 3 + .mvn/wrapper/maven-wrapper.jar | Bin 47610 -> 0 bytes .mvn/wrapper/maven-wrapper.properties | 1 - .travis.yml | 37 +-- RELEASING.md | 10 +- bom/build.gradle | 22 ++ build.gradle | 55 +++ circle.yml | 44 ++- core/build.gradle | 137 ++++++++ core/pom.xml | 314 ------------------ .../testcontainers/AbstractJarFileTest.java | 8 +- .../testcontainers/JarFileShadingTest.java | 0 .../testcontainers/PublicBinaryAPITest.java | 0 docs/index.md | 3 +- gradle.properties | 2 + gradle/bintray.gradle | 23 ++ gradle/publishing.gradle | 61 ++++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54708 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 172 ++++++++++ gradlew.bat | 84 +++++ modules/database-commons/build.gradle | 5 + modules/database-commons/pom.xml | 23 -- modules/jdbc-test/build.gradle | 13 + modules/jdbc-test/pom.xml | 135 -------- modules/jdbc/build.gradle | 5 + modules/jdbc/pom.xml | 29 -- modules/kafka/build.gradle | 9 + modules/kafka/pom.xml | 45 --- .../containers/KafkaContainerTest.java | 2 +- modules/mysql/build.gradle | 11 + modules/mysql/pom.xml | 56 ---- modules/nginx/build.gradle | 5 + modules/nginx/pom.xml | 22 -- modules/postgresql/build.gradle | 9 + modules/postgresql/pom.xml | 44 --- modules/selenium/build.gradle | 10 + modules/selenium/pom.xml | 47 --- ...ChromeRecordingWebDriverContainerTest.java | 2 +- .../junit/FlakyContainerCreationTest.java | 2 +- modules/virtuoso/pom.xml | 51 --- .../containers/VirtuosoContainer.java | 106 ------ .../containers/VirtuosoContainerProvider.java | 17 - ...s.containers.JdbcDatabaseContainerProvider | 1 - .../junit/SimpleVirtuosoTest.java | 37 --- .../src/test/resources/logback-test.xml | 30 -- mvnw | 225 ------------- mvnw.cmd | 143 -------- pom.xml | 259 --------------- release/.gitignore | 1 - release/codesigning.asc.enc | Bin 8560 -> 0 bytes release/deploy.sh | 7 - release/settings.xml | 23 -- settings.gradle | 38 +++ shade-test/README.md | 6 - shade-test/clashing-deps-jackson/pom.xml | 32 -- .../clashingdeps/jackson/cache/Cache.java | 27 -- .../cache/RedisJacksonBackedCache.java | 54 --- .../java/RedisJacksonBackedCacheTest.java | 64 ---- .../src/test/resources/logback-test.xml | 30 -- shade-test/clashing-deps-jersey/pom.xml | 27 -- .../src/test/java/OldJerseyClientTest.java | 23 -- .../src/test/resources/logback-test.xml | 30 -- shade-test/jar-file/pom.xml | 31 -- shade-test/pom.xml | 31 -- shade-test/service-lookup-dropwizard/pom.xml | 33 -- .../src/main/java/JerseyResource.java | 14 - .../src/test/java/SimpleResourceTest.java | 23 -- .../src/test/resources/logback-test.xml | 30 -- 70 files changed, 735 insertions(+), 2116 deletions(-) delete mode 100755 .mvn/wrapper/maven-wrapper.jar delete mode 100755 .mvn/wrapper/maven-wrapper.properties create mode 100644 bom/build.gradle create mode 100644 build.gradle create mode 100644 core/build.gradle delete mode 100644 core/pom.xml rename {shade-test/jar-file/src/test => core/src/jarFileTest}/java/org/testcontainers/AbstractJarFileTest.java (66%) rename {shade-test/jar-file/src/test => core/src/jarFileTest}/java/org/testcontainers/JarFileShadingTest.java (100%) rename {shade-test/jar-file/src/test => core/src/jarFileTest}/java/org/testcontainers/PublicBinaryAPITest.java (100%) create mode 100644 gradle.properties create mode 100644 gradle/bintray.gradle create mode 100644 gradle/publishing.gradle create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 modules/database-commons/build.gradle delete mode 100644 modules/database-commons/pom.xml create mode 100644 modules/jdbc-test/build.gradle delete mode 100644 modules/jdbc-test/pom.xml create mode 100644 modules/jdbc/build.gradle delete mode 100644 modules/jdbc/pom.xml create mode 100644 modules/kafka/build.gradle delete mode 100644 modules/kafka/pom.xml create mode 100644 modules/mysql/build.gradle delete mode 100644 modules/mysql/pom.xml create mode 100644 modules/nginx/build.gradle delete mode 100644 modules/nginx/pom.xml create mode 100644 modules/postgresql/build.gradle delete mode 100644 modules/postgresql/pom.xml create mode 100644 modules/selenium/build.gradle delete mode 100644 modules/selenium/pom.xml delete mode 100644 modules/virtuoso/pom.xml delete mode 100644 modules/virtuoso/src/main/java/org/testcontainers/containers/VirtuosoContainer.java delete mode 100644 modules/virtuoso/src/main/java/org/testcontainers/containers/VirtuosoContainerProvider.java delete mode 100644 modules/virtuoso/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider delete mode 100644 modules/virtuoso/src/test/java/org/testcontainers/junit/SimpleVirtuosoTest.java delete mode 100644 modules/virtuoso/src/test/resources/logback-test.xml delete mode 100755 mvnw delete mode 100755 mvnw.cmd delete mode 100644 pom.xml delete mode 100644 release/.gitignore delete mode 100644 release/codesigning.asc.enc delete mode 100755 release/deploy.sh delete mode 100644 release/settings.xml create mode 100644 settings.gradle delete mode 100644 shade-test/README.md delete mode 100644 shade-test/clashing-deps-jackson/pom.xml delete mode 100644 shade-test/clashing-deps-jackson/src/main/java/clashingdeps/jackson/cache/Cache.java delete mode 100644 shade-test/clashing-deps-jackson/src/main/java/clashingdeps/jackson/cache/RedisJacksonBackedCache.java delete mode 100644 shade-test/clashing-deps-jackson/src/test/java/RedisJacksonBackedCacheTest.java delete mode 100644 shade-test/clashing-deps-jackson/src/test/resources/logback-test.xml delete mode 100644 shade-test/clashing-deps-jersey/pom.xml delete mode 100644 shade-test/clashing-deps-jersey/src/test/java/OldJerseyClientTest.java delete mode 100644 shade-test/clashing-deps-jersey/src/test/resources/logback-test.xml delete mode 100644 shade-test/jar-file/pom.xml delete mode 100644 shade-test/pom.xml delete mode 100644 shade-test/service-lookup-dropwizard/pom.xml delete mode 100644 shade-test/service-lookup-dropwizard/src/main/java/JerseyResource.java delete mode 100644 shade-test/service-lookup-dropwizard/src/test/java/SimpleResourceTest.java delete mode 100644 shade-test/service-lookup-dropwizard/src/test/resources/logback-test.xml diff --git a/.gitattributes b/.gitattributes index 526c8a38d4a..07764a78d98 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1 @@ -*.sh text eol=lf \ No newline at end of file +* text eol=lf \ No newline at end of file diff --git a/.gitignore b/.gitignore index 698be93b05e..8e548131d5d 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,6 @@ dependency-reduced-pom.xml # Gitbook _book/ node_modules/ + +.gradle/ +build/ diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar deleted file mode 100755 index 9cc84ea9b4d95453115d0c26488d6a78694e0bc6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47610 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 diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties deleted file mode 100755 index 56bb0164ec1..00000000000 --- a/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1 +0,0 @@ -distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index aa2400ce261..544f0056c47 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,60 +2,45 @@ language: java jdk: - oraclejdk8 -env: - global: - - secure: "lw8tahEBcjos2c5QMMoeE19AKZDDU+WoEg7bumZJ+wuLErBEwlJENYU1t1oW834s1s7wgJ1kgt3jQgO6AWxR9wkFgZC+ZMPK0y/92T4MRsmmrMJFzUiu77UGS9MBj/Hu8M7aBzFNtLf0w3JrN2IsVI1xsX2z8PkK6L/G8C/Gzac=" - - # ^^ OSSRH_JIRA_USERNAME - - secure: "p8rkrCcHH5/EM7CKiPKQZCn1Lhzh7a+drGCHFrzHqWsZCapEXIgmqM0SYMnievaIBI+8IljdSkkW/BNfmIxW3fYDckLj91eEslLEhlIKDb91NtklDpr96o7on7wGF/VvpiEzIahKgp+LVsun/G1v3NU0T/RPs9QJoMmnQi4K+8I=" - - # ^^ OSSRH_JIRA_PASSWORD - - secure: "CWGSIr/ig2rIWSJqnDtmrK+XcUayl80ydh+fbzwi0v4hFsbbcNzm4SXFR2+HBGTBlWpWapvJoyG2iii2RHbgxVwYSyG6fEBrxtQV1oD8B/JAJoc/VJgvCsNnO54WRACqEPgV8fUX7MfKThA4hpXL3jnhM2A8J7wM969H8e4jNHk=" - - # ^^ GPG_KEY_NAME - - secure: "n2RjM3CIvtqMGMXYxjMOXjXB2R7k2lbZ2aV3fcQ0uSlrFqbzyRcTB/oa+Sft3ayUzU5S/qYjCPnNbb7P49vk1K3q5z2A9RVofFk9H90JPBz4cuuLcsuW7apcDPohDNuk47QAccsL0K+KA2AjdiQ/cIe81LWJykV+ogDI7DVXBDY=" - - # ^^ GPG_PASSPHRASE - sudo: required services: - docker cache: directories: - - '$HOME/.m2' + - '$HOME/.gradle' install: - - echo "MAVEN_OPTS='-XX:+TieredCompilation -XX:TieredStopAtLevel=1'" > ~/.mavenrc - - ./mvnw -T4 -DskipTests=true -Dmaven.javadoc.skip=true install + - ./gradlew build -x check jobs: include: - stage: test env: [ NAME=core ] - script: ./mvnw -pl core test + script: ./gradlew testcontainers:check - stage: test env: [ NAME=core ] jdk: oraclejdk9 - script: - - ./mvnw -pl core test + script: ./gradlew testcontainers:check - env: [ NAME=selenium ] - script: ./mvnw -pl modules/selenium test + script: ./gradlew selenium:check - env: [ NAME=modules ] - script: ./mvnw -pl !core,!modules/selenium test + script: ./gradlew check -x testcontainers:check -x selenium:check - # Run Docker-in-Docker tests inside Alpine and shading tests - - env: [ NAME="docker-in-alpine-docker&shade" ] + # Run Docker-in-Docker tests inside Alpine + - env: [ NAME="docker-in-alpine-docker" ] script: - | DOCKER_HOST=unix:///var/run/docker.sock DOCKER_TLS_VERIFY= docker run -t --rm \ - -v "$HOME/.m2":/root/.m2/ \ + -v "$HOME/.gradle":/root/.gradle/ \ -v /var/run/docker.sock:/var/run/docker.sock \ -v "$(pwd)":"$(pwd)" \ -w "$(pwd)" \ openjdk:8-jdk-alpine \ - ./mvnw -pl core test -Dtest=*GenericContainerRuleTest - - - ./mvnw test -f shade-test/pom.xml + ./gradlew testcontainers:test --tests '*GenericContainerRuleTest' - stage: deploy sudo: false @@ -64,7 +49,7 @@ jobs: script: skip deploy: provider: script - script: release/deploy.sh + script: ./gradlew -Pversion=$TRAVIS_TAG release on: tags: true branch: master diff --git a/RELEASING.md b/RELEASING.md index 3a5688291a1..c479bc5ef54 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -6,15 +6,13 @@ Testcontainers' release process is automated as a Travis deployment. This descri 1. Ensure that the master branch is building and that tests are passing. 1. Ensure that the [`CHANGELOG`](CHANGELOG.md) file is up to date and includes all merged features. -1. Create a new release on GitHub. **The tag name is used as the Maven version**, so please keep the tag name plain (e.g. 1.2.3). +1. Create a new release on GitHub. **The tag name is used as the version**, so please keep the tag name plain (e.g. 1.2.3). 1. Check that the Travis build passed. 1. Release of published artifacts is fully automated. Once the Travis build completes, there are no further actions to perform on the repository. 1. When available through Maven Central, poke [@whichrich](https://twitter.com/whichrich) to announce the release on Twitter! ## Internal details -* The signing and publishing steps are initiated as a script-type Travis CI deployment phase. The [`release/deploy.sh`](release/deploy.sh) script performs this process. -* `mvn version:set` is applied to use the tag name to the version number for the build. -* An encrypted GPG key within the `release` directory is used for signing. This key file is decrypted using Travis secrets. -* Travis secrets also hold key passphrase and Maven Central API username/passwords that are used for publishing. -* The publication process is automated using `nexus-staging-maven-plugin`. +* The process is done with Gradle and Bintray. +* Bintray will automatically promote the release to Maven Central. +* Travis secrets hold Bintray username/passwords that are used for publishing. diff --git a/bom/build.gradle b/bom/build.gradle new file mode 100644 index 00000000000..32805571109 --- /dev/null +++ b/bom/build.gradle @@ -0,0 +1,22 @@ +description = "Testcontainers :: BOM" + +publishing { + publications { + mavenJava(MavenPublication) { publication -> + artifactId = "testcontainers-bom" + artifacts = [] + + pom.withXml { + def dependencyManagementNode = asNode().appendNode('dependencyManagement').appendNode('dependencies') + + rootProject.subprojects.findAll { it != project }.each { subProject -> + dependencyManagementNode.appendNode('dependency').with { + appendNode('groupId', subProject.group) + appendNode('artifactId',subProject.name) + appendNode('version', subProject.version) + } + } + } + } + } +} diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000000..dfc60e5107c --- /dev/null +++ b/build.gradle @@ -0,0 +1,55 @@ +plugins { + id 'io.franzbecker.gradle-lombok' version '1.11' + id 'nebula.provided-base' version '3.0.3' + id 'com.github.johnrengelman.shadow' version '2.0.2' + id "com.jfrog.bintray" version "1.8.0" apply false +} + +subprojects { + apply plugin: 'nebula.provided-base' + apply plugin: 'java' + apply plugin: 'idea' + apply plugin: 'io.franzbecker.gradle-lombok' + apply from: "$rootDir/gradle/publishing.gradle" + apply from: "$rootDir/gradle/bintray.gradle" + + group = "org.testcontainers" + + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + compileJava.options.encoding = 'UTF-8' + + lombok { + version = '1.16.20' + } + + task delombok(type: io.franzbecker.gradle.lombok.task.DelombokTask) { + def outputDir = file("$buildDir/delombok") + outputs.dir(outputDir) + for (srcDir in project.sourceSets.main.java.srcDirs) { + inputs.dir(srcDir) + args(srcDir, "-d", outputDir) + } + } + + project.tasks.sourceJar.from(delombok) + + task release(dependsOn: bintrayUpload) + + test { + testLogging { + displayGranularity 1 + showStackTraces = true + exceptionFormat = 'full' + events "STARTED", "PASSED", "FAILED", "SKIPPED" + } + } + + repositories { + jcenter() + } + + dependencies { + testCompile 'ch.qos.logback:logback-classic:1.2.3' + } +} diff --git a/circle.yml b/circle.yml index cb0043ef053..43aff9d8f3d 100644 --- a/circle.yml +++ b/circle.yml @@ -1,7 +1,47 @@ version: 2 executorType: machine jobs: - build: + core: steps: - checkout - - run: ./mvnw -B test + - run: ./gradlew testcontainers:check + - run: + name: Save test results + command: | + mkdir -p ~/junit/ + find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; + when: always + - store_test_results: + path: ~/junit + modules: + steps: + - checkout + - run: ./gradlew check -x testcontainers:check -x selenium:check + - run: + name: Save test results + command: | + mkdir -p ~/junit/ + find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; + when: always + - store_test_results: + path: ~/junit + selenium: + steps: + - checkout + - run: ./gradlew selenium:check + - run: + name: Save test results + command: | + mkdir -p ~/junit/ + find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; + when: always + - store_test_results: + path: ~/junit + +workflows: + version: 2 + test_all: + jobs: + - core + - modules + - selenium diff --git a/core/build.gradle b/core/build.gradle new file mode 100644 index 00000000000..b2cf84fca2d --- /dev/null +++ b/core/build.gradle @@ -0,0 +1,137 @@ +apply plugin: 'com.github.johnrengelman.shadow' + +description = "Testcontainers Core" + +publishing { + publications { + mavenJava(MavenPublication) { publication -> + artifacts.removeAll { it.classifier == null } + artifact project.tasks.shadowJar + } + } +} + +sourceSets { + jarFileTest +} + +idea.module.testSourceDirs += sourceSets.jarFileTest.allSource.srcDirs + +configurations { + shaded + [runtime, compileOnly, testCompile]*.extendsFrom shaded +} + +shadowJar { + configurations = [project.configurations.shaded] + classifier = null + + mergeServiceFiles() + + [ + 'META-INF/io.netty.versions.properties', + 'META-INF/NOTICE', + 'META-INF/NOTICE.txt', + 'META-INF/LICENSE', + 'META-INF/LICENSE.txt', + 'META-INF/DEPENDENCIES', + 'META-INF/maven/', + 'META-INF/services/README.md', + 'META-INF/services/com.fasterxml.jackson.core.*', + 'META-INF/services/com.github.dockerjava.api.command.*', + 'META-INF/services/javax.ws.rs.ext.*', + 'META-INF/services/org.glassfish.hk2.extension.*', + 'META-INF/services/org.jvnet.hk2.external.generator.*', + 'META-INF/services/org.glassfish.jersey.internal.spi.*', + 'mozilla/public-suffix-list.txt', + ].each { exclude(it) } + + relocate('META-INF/native/libnetty', 'META-INF/native/liborg-testcontainers-shaded-netty') + + [ + "org.apache.http", + "org.apache.commons.lang", + "org.glassfish", + "org.aopalliance", + "org.jvnet", + "javax.annotation", + "javax.inject", + "javax.ws.rs", + "com.fasterxml.jackson", + "jersey.repackaged", + "com.google", + "io.netty", + "org.bouncycastle", + "org.newsclub", + "org.zeroturnaround" + ].each { relocate(it, "org.testcontainers.shaded.$it") } + + dependencies { + include(dependency('org.apache.httpcomponents:.*')) + include(dependency('org.glassfish.*:.*')) + include(dependency('org.aopalliance.*:.*')) + include(dependency('javax.ws.rs:.*')) + include(dependency('com.fasterxml.*:.*')) + include(dependency('com.github.docker-java:.*')) + include(dependency('com.google.guava:.*')) + include(dependency('io.netty:.*')) + include(dependency('org.bouncycastle:.*')) + include(dependency('org.newsclub.*:.*')) + include(dependency('org.zeroturnaround:zt-exec')) + include(dependency('commons-lang:commons-lang')) + } +} + +task jarFileTest(type: Test) { + testClassesDirs = sourceSets.jarFileTest.output.classesDirs + classpath = sourceSets.jarFileTest.runtimeClasspath + + systemProperty("jarFile", shadowJar.outputs.files.singleFile) + + dependsOn(shadowJar) +} +project.tasks.check.dependsOn(jarFileTest) + +dependencies { + compile 'junit:junit:4.12' + compile 'org.slf4j:slf4j-api:1.7.25' + compile 'org.slf4j:slf4j-ext:1.7.25' + compile 'org.jetbrains:annotations:15.0' + compile 'javax.annotation:javax.annotation-api:1.3.1' + compile 'com.google.code.findbugs:jsr305:3.0.2' + compile 'org.apache.commons:commons-compress:1.15' + // Added for JDK9 compatibility since it's missing this package + compile 'javax.xml.bind:jaxb-api:2.3.0' + compile 'org.rnorth.duct-tape:duct-tape:1.0.6' + compile 'org.rnorth.visible-assertions:visible-assertions:2.1.0' + + shaded ('com.github.docker-java:docker-java:3.0.12') { + exclude(group: 'org.glassfish.jersey.core') + exclude(group: 'org.glassfish.jersey.connectors') + exclude(group: 'log4j') + exclude(group: 'com.google.code.findbug') + } + shaded 'javax.ws.rs:javax.ws.rs-api:2.0.1' + shaded 'org.rnorth:tcp-unix-socket-proxy:1.0.1' + shaded 'org.zeroturnaround:zt-exec:1.8' + shaded 'commons-lang:commons-lang:2.6' + // docker-java uses SslConfigurator from jersey-common for TLS + shaded ('org.glassfish.jersey.core:jersey-common:2.23.1') { + // SslConfigurator doesn't use classes from dependencies + exclude(module: '*') + } + + testCompile 'redis.clients:jedis:2.8.0' + testCompile 'com.rabbitmq:amqp-client:3.5.3' + testCompile 'org.mongodb:mongo-java-driver:3.0.2' + testCompile ('org.mockito:mockito-core:1.10.19') { + exclude(module: 'hamcrest-core') + } + // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest + testCompile files('testlib/repo/fakejar/fakejar/0/fakejar-0.jar') + + jarFileTestCompileOnly 'org.projectlombok:lombok:1.16.20' + jarFileTestCompile 'junit:junit:4.12' + jarFileTestCompile 'org.assertj:assertj-core:3.8.0' + jarFileTestCompile 'org.ow2.asm:asm-debug-all:5.2' +} diff --git a/core/pom.xml b/core/pom.xml deleted file mode 100644 index fe13988ec63..00000000000 --- a/core/pom.xml +++ /dev/null @@ -1,314 +0,0 @@ - - - 4.0.0 - - - org.testcontainers - testcontainers-parent - 0-SNAPSHOT - ../pom.xml - - - testcontainers - - Testcontainers Core - - - - junit - junit - 4.12 - - - - org.slf4j - slf4j-api - 1.7.25 - - - - com.github.docker-java - docker-java - ${docker-java.version} - compile - - - org.glassfish.jersey.core - * - - - org.glassfish.jersey.connectors - * - - - log4j - log4j - - - - - - - - javax.xml.bind - jaxb-api - 2.3.0 - - - - - org.glassfish.jersey.core - jersey-common - 2.23.1 - - - - * - * - - - - - - javax.ws.rs - javax.ws.rs-api - 2.0.1 - - - - com.google.code.findbugs - jsr305 - 3.0.2 - - - - org.rnorth - tcp-unix-socket-proxy - 1.0.1 - - - - org.zeroturnaround - zt-exec - 1.8 - - - org.rnorth.duct-tape - duct-tape - 1.0.6 - - - org.slf4j - slf4j-ext - 1.7.25 - - - org.rnorth.visible-assertions - visible-assertions - 2.1.0 - - - - - redis.clients - jedis - 2.8.0 - test - - - com.rabbitmq - amqp-client - 3.5.3 - test - - - org.mongodb - mongo-java-driver - 3.0.2 - test - - - org.mockito - mockito-core - 1.10.19 - test - - - org.hamcrest - hamcrest-core - - - - - - - fakejar - fakejar - 0 - test - - - - - - - - - - - - - - maven-shade-plugin - 3.0.0 - - - package - - shade - - - - - - - - - - - org.apache.http - org.testcontainers.shaded.org.apache.http - - - org.glassfish - org.testcontainers.shaded.org.glassfish - - - org.aopalliance - org.testcontainers.shaded.org.aopalliance - - - org.jvnet - org.testcontainers.shaded.org.jvnet - - - javax.annotation - org.testcontainers.shaded.javax.annotation - - - javax.inject - org.testcontainers.shaded.javax.inject - - - javax.ws.rs - org.testcontainers.shaded.javax.ws.rs - - - com.fasterxml.jackson - org.testcontainers.shaded.com.fasterxml.jackson - - - jersey.repackaged - org.testcontainers.shaded.jersey.repackaged - - - com.google - org.testcontainers.shaded.com.google - - - io.netty - org.testcontainers.shaded.io.netty - - - org.bouncycastle - org.testcontainers.shaded.org.bouncycastle - - - org.newsclub - org.testcontainers.shaded.org.newsclub - - - - - org.apache.httpcomponents:* - org.glassfish.*:* - org.aopalliance.*:* - com.google.code.findbugs:jsr305 - javax.inject:* - javax.ws.rs:* - com.fasterxml.*:* - com.github.docker-java:* - com.google.guava:* - io.netty:* - org.bouncycastle:* - org.newsclub.*:* - - - true - false - - - *:* - - META-INF/NOTICE - META-INF/LICENSE - META-INF/DEPENDENCIES - META-INF/maven/ - META-INF/*.SF - META-INF/*.DSA - META-INF/*.RSA - META-INF/io.netty.versions.properties - mozilla/public-suffix-list.txt - - META-INF/services/README.md - META-INF/services/com.fasterxml.jackson.core.* - META-INF/services/com.github.dockerjava.api.command.* - META-INF/services/javax.ws.rs.ext.* - META-INF/services/org.glassfish.hk2.extension.* - META-INF/services/org.jvnet.hk2.external.generator.* - META-INF/services/org.glassfish.jersey.internal.spi.* - - - - - - - org.apache.maven.plugins - maven-antrun-plugin - 1.7 - - - unpack - package - - - - - - - - - - - run - - - - - - - - - - testlib - file://${project.basedir}/testlib/repo - - - diff --git a/shade-test/jar-file/src/test/java/org/testcontainers/AbstractJarFileTest.java b/core/src/jarFileTest/java/org/testcontainers/AbstractJarFileTest.java similarity index 66% rename from shade-test/jar-file/src/test/java/org/testcontainers/AbstractJarFileTest.java rename to core/src/jarFileTest/java/org/testcontainers/AbstractJarFileTest.java index 47b74ede860..f3960e63bdb 100644 --- a/shade-test/jar-file/src/test/java/org/testcontainers/AbstractJarFileTest.java +++ b/core/src/jarFileTest/java/org/testcontainers/AbstractJarFileTest.java @@ -1,11 +1,9 @@ package org.testcontainers; -import java.io.IOException; import java.net.URI; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Path; -import java.nio.file.Paths; import static java.util.Collections.emptyMap; @@ -14,12 +12,10 @@ public abstract class AbstractJarFileTest { public static Path root; static { - Path path = Paths.get("..", "..", "core", "target", "testcontainers-0-SNAPSHOT.jar"); - try { - FileSystem fileSystem = FileSystems.newFileSystem(URI.create("jar:" + path.toUri()), emptyMap()); + FileSystem fileSystem = FileSystems.newFileSystem(URI.create("jar:file://" + System.getProperty("jarFile")), emptyMap()); root = fileSystem.getPath("/"); - } catch (IOException e) { + } catch (Exception e) { throw new RuntimeException(e); } } diff --git a/shade-test/jar-file/src/test/java/org/testcontainers/JarFileShadingTest.java b/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java similarity index 100% rename from shade-test/jar-file/src/test/java/org/testcontainers/JarFileShadingTest.java rename to core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java diff --git a/shade-test/jar-file/src/test/java/org/testcontainers/PublicBinaryAPITest.java b/core/src/jarFileTest/java/org/testcontainers/PublicBinaryAPITest.java similarity index 100% rename from shade-test/jar-file/src/test/java/org/testcontainers/PublicBinaryAPITest.java rename to core/src/jarFileTest/java/org/testcontainers/PublicBinaryAPITest.java diff --git a/docs/index.md b/docs/index.md index f47eadd04a8..9664ae037a3 100644 --- a/docs/index.md +++ b/docs/index.md @@ -47,7 +47,8 @@ This project was initially inspired by a [gist](https://gist.github.com/mosheesh * discuss with the authors on an issue ticket prior to doing anything big * follow the style, naming and structure conventions of the rest of the project * make commits atomic and easy to merge - * verify all tests are passing. Build the project with `mvn clean install -Pproprietary-deps` to do this. + * verify all tests are passing. Build the project with `./gradlew check` to do this. + **N.B.** Gradle's Build Cache is enabled by default, but you can add `--no-build-cache` flag to disable it. ## Copyright diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000000..dad41a4425a --- /dev/null +++ b/gradle.properties @@ -0,0 +1,2 @@ +org.gradle.parallel=false +org.gradle.caching=true diff --git a/gradle/bintray.gradle b/gradle/bintray.gradle new file mode 100644 index 00000000000..6990393401e --- /dev/null +++ b/gradle/bintray.gradle @@ -0,0 +1,23 @@ +apply plugin: "com.jfrog.bintray" + +bintray { + user = project.hasProperty('bintrayUser') ? project.property('bintrayUser') : System.getenv('BINTRAY_USER') + key = project.hasProperty('bintrayApiKey') ? project.property('bintrayApiKey') : System.getenv('BINTRAY_API_KEY') + + publications = ['mavenJava'] + + pkg { + userOrg = 'testcontainers' + repo = 'releases' + name = 'testcontainers' + + publish = true + + version { + name = project.version + vcsTag = project.version + released = new Date() + gpg.sign = true + } + } +} diff --git a/gradle/publishing.gradle b/gradle/publishing.gradle new file mode 100644 index 00000000000..6417f290e58 --- /dev/null +++ b/gradle/publishing.gradle @@ -0,0 +1,61 @@ +apply plugin: 'maven-publish' + +task sourceJar(type: Jar) { + classifier = 'sources' +} + +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc +} + +publishing { + publications { + mavenJava(MavenPublication) { publication -> + artifactId = project.name + + artifact project.tasks.jar + artifact sourceJar + artifact javadocJar + + pom.withXml { + def rootNode = asNode() + rootNode.children().last() + { + resolveStrategy = Closure.DELEGATE_FIRST + + name project.description + url 'https://testcontainers.org' + issueManagement { + system 'GitHub' + url 'https://github.com/testcontainers/testcontainers-java/issues' + } + licenses { + license { + name 'MIT' + url 'http://opensource.org/licenses/MIT' + } + } + scm { + url 'https://github.com/testcontainers/testcontainers-java/' + connection 'scm:git:git://github.com/testcontainers/testcontainers-java.git' + developerConnection 'scm:git:ssh://git@github.com/testcontainers/testcontainers-java.git' + } + } + + def dependenciesNode = rootNode.appendNode('dependencies') + + def addDependency = { dependency, scope -> + dependenciesNode.appendNode('dependency').with { + appendNode('groupId', dependency.group) + appendNode('artifactId', dependency.name) + appendNode('version', dependency.version) + appendNode('scope', scope) + } + } + + project.configurations.compile.dependencies.each { addDependency(it, 'compile') } + project.configurations.provided.dependencies.each { addDependency(it, 'provided') } + } + } + } +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..736fb7d3f94c051b359fc7ae7212d351bc094bdd GIT binary patch literal 54708 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 ZypaY2tb=~UE;0eV1Nd9`dw%@&{{Y^v%e4Ri literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000000..2f6d30c9a6b --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon Jan 29 20:32:52 CET 2018 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.5-all.zip diff --git a/gradlew b/gradlew new file mode 100755 index 00000000000..cccdd3d517f --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000000..f9553162f12 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/modules/database-commons/build.gradle b/modules/database-commons/build.gradle new file mode 100644 index 00000000000..8b9c288b9d4 --- /dev/null +++ b/modules/database-commons/build.gradle @@ -0,0 +1,5 @@ +description = "Testcontainers :: Database-Commons" + +dependencies { + compile project(':testcontainers') +} diff --git a/modules/database-commons/pom.xml b/modules/database-commons/pom.xml deleted file mode 100644 index 3c4ec520a24..00000000000 --- a/modules/database-commons/pom.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - 4.0.0 - - - org.testcontainers - testcontainers-parent - 0-SNAPSHOT - ../../pom.xml - - - database-commons - Testcontainers :: Database-Commons - - - - ${project.groupId} - testcontainers - ${project.version} - - - - diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle new file mode 100644 index 00000000000..262035ce027 --- /dev/null +++ b/modules/jdbc-test/build.gradle @@ -0,0 +1,13 @@ +dependencies { + compile project(':mysql') + compile project(':postgresql') + + testCompile 'com.google.guava:guava:18.0' + testCompile 'org.postgresql:postgresql:42.0.0' + testCompile 'mysql:mysql-connector-java:5.1.45' + testCompile 'org.mariadb.jdbc:mariadb-java-client:1.4.6' + testCompile 'com.zaxxer:HikariCP-java6:2.3.8' + testCompile 'org.apache.tomcat:tomcat-jdbc:8.5.4' + testCompile 'org.vibur:vibur-dbcp:9.0' + testCompile 'commons-dbutils:commons-dbutils:1.6' +} diff --git a/modules/jdbc-test/pom.xml b/modules/jdbc-test/pom.xml deleted file mode 100644 index f7013501e3f..00000000000 --- a/modules/jdbc-test/pom.xml +++ /dev/null @@ -1,135 +0,0 @@ - - - 4.0.0 - - - org.testcontainers - testcontainers-parent - 0-SNAPSHOT - ../../pom.xml - - - jdbc-test - Testcontainers :: JDBC :: Tests - - - - ${project.groupId} - mysql - ${project.version} - - - ${project.groupId} - postgresql - ${project.version} - - - - com.google.guava - guava - 18.0 - test - - - - - org.postgresql - postgresql - 42.0.0 - test - - - mysql - mysql-connector-java - 5.1.45 - test - - - org.mariadb.jdbc - mariadb-java-client - 1.4.6 - test - - - - - com.zaxxer - HikariCP-java6 - 2.3.8 - test - - - org.apache.tomcat - tomcat-jdbc - 8.5.4 - test - - - org.vibur - vibur-dbcp - 9.0 - test - - - - commons-dbutils - commons-dbutils - 1.6 - test - - - - - - - org.apache.maven.plugins - maven-deploy-plugin - 2.7 - - true - - - - - - - - proprietary-deps - - - ${project.groupId} - oracle-xe - ${project.version} - - - - - com.oracle - ojdbc6 - 11.2.0.4 - provided - - - - - - maven-surefire-plugin - 2.19.1 - - - test-proprietary-deps - - test - - - src/testProprietary/java - - - - - - - - - diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle new file mode 100644 index 00000000000..2278b14006d --- /dev/null +++ b/modules/jdbc/build.gradle @@ -0,0 +1,5 @@ +description = "Testcontainers :: JDBC" + +dependencies { + compile project(':database-commons') +} diff --git a/modules/jdbc/pom.xml b/modules/jdbc/pom.xml deleted file mode 100644 index a9efce8c04d..00000000000 --- a/modules/jdbc/pom.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - 4.0.0 - - - org.testcontainers - testcontainers-parent - 0-SNAPSHOT - ../../pom.xml - - - jdbc - Testcontainers :: JDBC - - - - ${project.groupId} - testcontainers - ${project.version} - - - - ${project.groupId} - database-commons - ${project.version} - - - - diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle new file mode 100644 index 00000000000..f52248045b1 --- /dev/null +++ b/modules/kafka/build.gradle @@ -0,0 +1,9 @@ +description = "Testcontainers :: Kafka" + +dependencies { + compile project(':testcontainers') + + testCompile 'org.apache.kafka:kafka-clients:1.0.0' + testCompile 'org.assertj:assertj-core:3.8.0' + testCompile 'com.google.guava:guava:23.0' +} diff --git a/modules/kafka/pom.xml b/modules/kafka/pom.xml deleted file mode 100644 index 8d004cab1f1..00000000000 --- a/modules/kafka/pom.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - 4.0.0 - - - org.testcontainers - testcontainers-parent - 0-SNAPSHOT - ../../pom.xml - - - kafka - Testcontainers :: Apache Kafka - - - - ${project.groupId} - testcontainers - ${project.version} - - - - org.apache.kafka - kafka-clients - 1.0.0 - test - - - - org.assertj - assertj-core - 3.8.0 - test - - - - com.google.guava - guava - 23.0 - test - - - - \ No newline at end of file diff --git a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java index f88cb1703ed..fc36a3f4356 100644 --- a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java +++ b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java @@ -113,4 +113,4 @@ protected void testKafkaFunctionality(String bootstrapServers) throws Exception } } -} \ No newline at end of file +} diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle new file mode 100644 index 00000000000..ef67e0729d4 --- /dev/null +++ b/modules/mysql/build.gradle @@ -0,0 +1,11 @@ +description = "Testcontainers :: JDBC :: MySQL" + +dependencies { + compile project(':jdbc') + + testCompile 'mysql:mysql-connector-java:5.1.35' + testCompile 'com.zaxxer:HikariCP-java6:2.3.8' + testCompile 'commons-dbutils:commons-dbutils:1.6' + testCompile 'org.apache.tomcat:tomcat-jdbc:8.5.4' + testCompile 'org.vibur:vibur-dbcp:9.0' +} diff --git a/modules/mysql/pom.xml b/modules/mysql/pom.xml deleted file mode 100644 index 712ce9d4980..00000000000 --- a/modules/mysql/pom.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - 4.0.0 - - - org.testcontainers - testcontainers-parent - 0-SNAPSHOT - ../../pom.xml - - - mysql - Testcontainers :: JDBC :: MySQL - - - - ${project.groupId} - jdbc - ${project.version} - - - - - mysql - mysql-connector-java - 5.1.35 - test - - - - - com.zaxxer - HikariCP-java6 - 2.3.8 - test - - - commons-dbutils - commons-dbutils - 1.6 - test - - - org.apache.tomcat - tomcat-jdbc - 8.5.4 - test - - - org.vibur - vibur-dbcp - 9.0 - test - - - \ No newline at end of file diff --git a/modules/nginx/build.gradle b/modules/nginx/build.gradle new file mode 100644 index 00000000000..e5003b4e103 --- /dev/null +++ b/modules/nginx/build.gradle @@ -0,0 +1,5 @@ +description = "Testcontainers :: Nginx" + +dependencies { + compile project(':testcontainers') +} diff --git a/modules/nginx/pom.xml b/modules/nginx/pom.xml deleted file mode 100644 index 686fc42b842..00000000000 --- a/modules/nginx/pom.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - 4.0.0 - - - org.testcontainers - testcontainers-parent - 0-SNAPSHOT - ../../pom.xml - - - nginx - Testcontainers :: nginx - - - - ${project.groupId} - testcontainers - ${project.version} - - - \ No newline at end of file diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle new file mode 100644 index 00000000000..211471f1c34 --- /dev/null +++ b/modules/postgresql/build.gradle @@ -0,0 +1,9 @@ +description = "Testcontainers :: JDBC :: PostgreSQL" + +dependencies { + compile project(':jdbc') + + testCompile 'org.postgresql:postgresql:9.3-1101-jdbc41' + testCompile 'com.zaxxer:HikariCP-java6:2.3.8' + testCompile 'commons-dbutils:commons-dbutils:1.6' +} diff --git a/modules/postgresql/pom.xml b/modules/postgresql/pom.xml deleted file mode 100644 index 8f1b091277e..00000000000 --- a/modules/postgresql/pom.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - 4.0.0 - - - org.testcontainers - testcontainers-parent - 0-SNAPSHOT - ../../pom.xml - - - postgresql - Testcontainers :: JDBC :: PostgreSQL - - - - ${project.groupId} - jdbc - ${project.version} - - - - - org.postgresql - postgresql - 9.3-1101-jdbc41 - test - - - - - com.zaxxer - HikariCP-java6 - 2.3.8 - test - - - commons-dbutils - commons-dbutils - 1.6 - test - - - \ No newline at end of file diff --git a/modules/selenium/build.gradle b/modules/selenium/build.gradle new file mode 100644 index 00000000000..1a3d33b0258 --- /dev/null +++ b/modules/selenium/build.gradle @@ -0,0 +1,10 @@ +description = "Testcontainers :: Selenium" + +dependencies { + compile project(':testcontainers') + + provided 'org.seleniumhq.selenium:selenium-remote-driver:2.52.0' + + testCompile 'org.mortbay.jetty:jetty:6.1.25' + testCompile project(':nginx') +} diff --git a/modules/selenium/pom.xml b/modules/selenium/pom.xml deleted file mode 100644 index 496445d8eed..00000000000 --- a/modules/selenium/pom.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - 4.0.0 - - - org.testcontainers - testcontainers-parent - 0-SNAPSHOT - ../../pom.xml - - - selenium - Testcontainers :: Selenium - - - - ${project.groupId} - testcontainers - ${project.version} - - - - - org.seleniumhq.selenium - selenium-remote-driver - 2.52.0 - provided - - - - - org.mortbay.jetty - jetty - 6.1.25 - test - - - - ${project.groupId} - nginx - ${project.version} - test - - - - \ No newline at end of file diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java index 770e8e8bc30..f0f011d5e89 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java @@ -20,7 +20,7 @@ public static class ChromeThatRecordsAllTests { @Rule public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() .withDesiredCapabilities(DesiredCapabilities.chrome()) - .withRecordingMode(RECORD_ALL, new File("./target/")) + .withRecordingMode(RECORD_ALL, new File("./build/")) .withRecordingFileFactory(new DefaultRecordingFileFactory()); @Test diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/FlakyContainerCreationTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/FlakyContainerCreationTest.java index 56eea12a325..20229ea9c8f 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/FlakyContainerCreationTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/FlakyContainerCreationTest.java @@ -18,7 +18,7 @@ public void testCreationOfManyContainers() { for (int i = 0; i < 50; i++) { BrowserWebDriverContainer container = new BrowserWebDriverContainer() .withDesiredCapabilities(DesiredCapabilities.chrome()) - .withRecordingMode(BrowserWebDriverContainer.VncRecordingMode.RECORD_FAILING, new File("target")); + .withRecordingMode(BrowserWebDriverContainer.VncRecordingMode.RECORD_FAILING, new File("build")); container.start(); RemoteWebDriver driver = container.getWebDriver(); diff --git a/modules/virtuoso/pom.xml b/modules/virtuoso/pom.xml deleted file mode 100644 index bab0a5dfe1f..00000000000 --- a/modules/virtuoso/pom.xml +++ /dev/null @@ -1,51 +0,0 @@ - - 4.0.0 - - - org.testcontainers - testcontainers-parent - 0-SNAPSHOT - ../../pom.xml - - - virtuoso - Testcontainers :: JDBC :: Virtuoso - - - - ldstack - http://mvn.linkeddata.org/content/groups/linkeddata.org/ - - - - - - ${project.groupId} - jdbc - ${project.version} - - - - - virtuoso - jdbc-driver - 4.0 - test - - - - - com.zaxxer - HikariCP-java6 - 2.3.8 - test - - - commons-dbutils - commons-dbutils - 1.6 - test - - - - \ No newline at end of file diff --git a/modules/virtuoso/src/main/java/org/testcontainers/containers/VirtuosoContainer.java b/modules/virtuoso/src/main/java/org/testcontainers/containers/VirtuosoContainer.java deleted file mode 100644 index 3f601b22812..00000000000 --- a/modules/virtuoso/src/main/java/org/testcontainers/containers/VirtuosoContainer.java +++ /dev/null @@ -1,106 +0,0 @@ -package org.testcontainers.containers; - -import org.rnorth.ducttape.inconsistents.Inconsistents; -import org.rnorth.ducttape.ratelimits.RateLimiter; -import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; - -import java.sql.Connection; -import java.sql.SQLException; -import java.util.concurrent.TimeUnit; - -public class VirtuosoContainer> extends JdbcDatabaseContainer { - - public static final String NAME = "virtuoso"; - public static final String IMAGE = "tenforce/virtuoso"; - public static final Integer JDBC_PORT = 1111; - public static final Integer SPARQL_SERVICE_PORT = 8890; - private static final RateLimiter LIVENESS_RATE_LIMITER = RateLimiterBuilder.newBuilder() - .withConstantThroughput() - .withRate(1, TimeUnit.SECONDS) - .build(); - - public VirtuosoContainer() { - super(IMAGE + ":1.0.0-virtuoso7.2.2"); - } - - public VirtuosoContainer(String dockerImageName) { - super(dockerImageName); - } - - @Override - protected void configure() { - addExposedPort(JDBC_PORT); - addExposedPort(SPARQL_SERVICE_PORT); - addEnv("DBA_PASSWORD", getPassword()); - addEnv("SPARQL_UPDATE", "true"); - addEnv("DEFAULT_GRAPH", "http://localhost:8890/DAV"); - addExposedPorts(JDBC_PORT, SPARQL_SERVICE_PORT); - } - - @Override - protected String getDriverClassName() { - return "virtuoso.jdbc4.Driver"; - } - - @Override - public String getJdbcUrl() { - return "jdbc:virtuoso://" + getContainerIpAddress() + ":" + getMappedPort(JDBC_PORT); - } - - public String getSparqlUrl() { - return "http://" + getContainerIpAddress() + ":" + getMappedPort(SPARQL_SERVICE_PORT) + "/sparql"; - } - - @Override - public String getUsername() { - return "dba"; - } - - @Override - public String getPassword() { - return "myDbaPassword"; - } - - @Override - protected String getTestQueryString() { - return "SELECT 1"; - } - - @Override - protected Integer getLivenessCheckPort() { - return getMappedPort(JDBC_PORT); - } - - @Override - protected void waitUntilContainerStarted() { - // Repeatedly try and open a connection to the DB and execute a test query - - logger().info("Waiting for database connection to become available at {} using query '{}'", getJdbcUrl(), getTestQueryString()); - - // Wait for consecutive JDBC connection successes over a period of time. The Virtuoso container seems - // to initially return a connection that fails on subsequent attempts, so wait for a consistently stable connection - Inconsistents.retryUntilConsistent(5, 120, TimeUnit.SECONDS, () -> { - //noinspection CodeBlock2Expr - return LIVENESS_RATE_LIMITER.getWhenReady(() -> { - if (!isRunning()) { - throw new ContainerLaunchException("Container failed to start"); - } - - try { - Connection connection = createConnection(""); - - boolean success = connection.createStatement().execute(this.getTestQueryString()); - - if (success) { - logger().info("Obtained a connection to container ({})", this.getJdbcUrl()); - return true; - } else { - throw new SQLException("Failed to execute test query"); - } - } catch (SQLException e) { - throw new ContainerLaunchException(e.getMessage()); - } - }); - }); - } -} diff --git a/modules/virtuoso/src/main/java/org/testcontainers/containers/VirtuosoContainerProvider.java b/modules/virtuoso/src/main/java/org/testcontainers/containers/VirtuosoContainerProvider.java deleted file mode 100644 index b98f809078f..00000000000 --- a/modules/virtuoso/src/main/java/org/testcontainers/containers/VirtuosoContainerProvider.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.testcontainers.containers; - -import org.testcontainers.containers.JdbcDatabaseContainerProvider; - -public class VirtuosoContainerProvider extends JdbcDatabaseContainerProvider { - - @Override - public boolean supports(String databaseType) { - return databaseType.equals(VirtuosoContainer.NAME); - } - - @Override - public VirtuosoContainer newInstance(String tag) { - return new VirtuosoContainer(VirtuosoContainer.IMAGE + ":" + tag); - } - -} diff --git a/modules/virtuoso/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider b/modules/virtuoso/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider deleted file mode 100644 index c17bf3f8196..00000000000 --- a/modules/virtuoso/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider +++ /dev/null @@ -1 +0,0 @@ -org.testcontainers.containers.VirtuosoContainerProvider \ No newline at end of file diff --git a/modules/virtuoso/src/test/java/org/testcontainers/junit/SimpleVirtuosoTest.java b/modules/virtuoso/src/test/java/org/testcontainers/junit/SimpleVirtuosoTest.java deleted file mode 100644 index c6d6cd1b17f..00000000000 --- a/modules/virtuoso/src/test/java/org/testcontainers/junit/SimpleVirtuosoTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.testcontainers.junit; - -import com.zaxxer.hikari.HikariConfig; -import com.zaxxer.hikari.HikariDataSource; -import org.junit.Rule; -import org.junit.Test; -import org.testcontainers.containers.VirtuosoContainer; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; - -public class SimpleVirtuosoTest { - - @Rule - public VirtuosoContainer virtuoso = new VirtuosoContainer(); - - @Test - public void testSimple() throws SQLException { - HikariConfig hikariConfig = new HikariConfig(); - hikariConfig.setJdbcUrl(virtuoso.getJdbcUrl()); - hikariConfig.setUsername(virtuoso.getUsername()); - hikariConfig.setPassword(virtuoso.getPassword()); - hikariConfig.setConnectionTestQuery("SELECT 1"); - - HikariDataSource ds = new HikariDataSource(hikariConfig); - Statement statement = ds.getConnection().createStatement(); - statement.execute("SELECT 1"); - ResultSet resultSet = statement.getResultSet(); - - resultSet.next(); - int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); - } -} diff --git a/modules/virtuoso/src/test/resources/logback-test.xml b/modules/virtuoso/src/test/resources/logback-test.xml deleted file mode 100644 index 3160c151056..00000000000 --- a/modules/virtuoso/src/test/resources/logback-test.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - %d{HH:mm:ss.SSS} %-5level %logger - %msg%n - - - - - - - - - - - - - - - - - - - - PROFILER - DENY - - \ No newline at end of file diff --git a/mvnw b/mvnw deleted file mode 100755 index 5bf251c0774..00000000000 --- a/mvnw +++ /dev/null @@ -1,225 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Maven2 Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir -# -# Optional ENV vars -# ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files -# ---------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" - fi - fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` - fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For Migwn, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" - # TODO classpath? -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`which java`" - fi -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi - - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` - fi - # end of workaround - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; -fi - -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -echo $MAVEN_PROJECTBASEDIR -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -exec "$JAVACMD" \ - $MAVEN_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd deleted file mode 100755 index 48c810ef37a..00000000000 --- a/mvnw.cmd +++ /dev/null @@ -1,143 +0,0 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM http://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven2 Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" - -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml deleted file mode 100644 index 1b1d1e7335b..00000000000 --- a/pom.xml +++ /dev/null @@ -1,259 +0,0 @@ - - - 4.0.0 - - org.testcontainers - testcontainers-parent - 0-SNAPSHOT - - pom - - Testcontainers Parent POM - - Isolated container management for Java code testing - - https://github.com/testcontainers/testcontainers-java - - - MIT - http://opensource.org/licenses/MIT - - - - - rnorth - Richard North - rich.north@gmail.com - - - swissarmykirpan - Gurpreet Sohal - gurpreet@gurpreetsohal.com - - - alexboldt - Alex Boldt - boldtalex@gmail.com - - - rpozarickij - Robert Požarickij - robert.pozarickij@gmail.com - - - krystiannowak - Krystian Nowak - krystian.nowak@gmail.com - - - mbaechler - Matthieu Baechler - matthieu.baechler@gmail.com - - - v-schulz - Viktor Schulz - vschulz@mail.uni-mannheim.de - - - pcornish - Pete Cornish - outofcoffee@gmail.com - - - mgonzalez - Miguel Gonzalez Sanchez - miguel-gonzalez@gmx.de - - - - - - - org.projectlombok - lombok - 1.16.20 - provided - - - - ch.qos.logback - logback-classic - 1.2.3 - test - - - - - - - maven-compiler-plugin - 3.1 - - 1.8 - 1.8 - - - - maven-release-plugin - 2.5.1 - - false - true - - - - maven-source-plugin - 2.4 - - - attach-sources - - jar - - - - - - maven-javadoc-plugin - 2.10.3 - - - attach-javadocs - - jar - - - - - - maven-scm-plugin - 1.9.4 - - testcontainers-${project.version} - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.6 - true - - ossrh - https://oss.sonatype.org/ - true - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.20 - - - - - - UTF-8 - 3.0.12 - - - - core - modules/jdbc - modules/mysql - modules/postgresql - modules/selenium - modules/nginx - modules/kafka - modules/jdbc-test - modules/database-commons - - - - - proprietary-deps - - modules/virtuoso - - - - shade - - - test-only - - true - - - modules/jdbc-test - - - - sign - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.6 - - - sign-artifacts - verify - - sign - - - - - - - - - - build-extras - - true - - - - - org.apache.maven.plugins - maven-source-plugin - 2.4 - - - attach-sources - - jar-no-fork - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.10.3 - - - attach-javadocs - - jar - - - - - - - - - - - scm:git:https://github.com/testcontainers/testcontainers-java.git - scm:git:git@github.com:testcontainers/testcontainers-java.git - https://github.com/testcontainers/testcontainers-java - HEAD - - - diff --git a/release/.gitignore b/release/.gitignore deleted file mode 100644 index 1546d6762de..00000000000 --- a/release/.gitignore +++ /dev/null @@ -1 +0,0 @@ -codesigning.asc diff --git a/release/codesigning.asc.enc b/release/codesigning.asc.enc deleted file mode 100644 index 547a33b55e482eabce79af619060fea61e6dcf42..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8560 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 - - - ossrh - ${env.OSSRH_JIRA_USERNAME} - ${env.OSSRH_JIRA_PASSWORD} - - - - - - ossrh - - true - - - gpg - ${env.GPG_KEY_NAME} - ${env.GPG_PASSPHRASE} - - - - \ No newline at end of file diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 00000000000..301b8eea057 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,38 @@ +buildscript { + repositories { + maven { + url "https://plugins.gradle.org/m2/" + } + } + dependencies { + classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.4.1" + } +} + +apply plugin: 'ch.myniva.s3-build-cache' + +ext.isMasterBuild = System.getenv("CIRCLE_BRANCH") == "master" + +buildCache { + local { + enabled = !isMasterBuild + } + + remote(ch.myniva.gradle.caching.s3.AwsS3BuildCache) { + region = 'us-east-1' + bucket = 'testcontainers-build-cache' + push = isMasterBuild + } +} + +rootProject.name = 'testcontainers-java' + +include "bom" + +include "testcontainers" +project(':testcontainers').projectDir = "$rootDir/core" as File + +file('modules').eachDir { dir -> + include dir.name + project(":${dir.name}").projectDir = dir +} \ No newline at end of file diff --git a/shade-test/README.md b/shade-test/README.md deleted file mode 100644 index 1d9f4cd22b9..00000000000 --- a/shade-test/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# shade-test - -This module contains what are effectively 'integration tests' for the shaded testcontainers core JAR. - -These test modules should do things that will break without shading being used - i.e. depend upon incompatible versions - of core's dependencies. \ No newline at end of file diff --git a/shade-test/clashing-deps-jackson/pom.xml b/shade-test/clashing-deps-jackson/pom.xml deleted file mode 100644 index 16812788ff3..00000000000 --- a/shade-test/clashing-deps-jackson/pom.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - shade-test - org.testcontainers - 0-SNAPSHOT - ../pom.xml - - 4.0.0 - - clashing-deps-jackson - - - - org.testcontainers - testcontainers - ${project.version} - - - redis.clients - jedis - 2.8.0 - - - com.fasterxml.jackson.core - jackson-databind - 2.6.3 - - - \ No newline at end of file diff --git a/shade-test/clashing-deps-jackson/src/main/java/clashingdeps/jackson/cache/Cache.java b/shade-test/clashing-deps-jackson/src/main/java/clashingdeps/jackson/cache/Cache.java deleted file mode 100644 index d1326e7c46d..00000000000 --- a/shade-test/clashing-deps-jackson/src/main/java/clashingdeps/jackson/cache/Cache.java +++ /dev/null @@ -1,27 +0,0 @@ -package clashingdeps.jackson.cache; - -import java.util.Optional; - -/** - * Cache, for storing data associated with keys. - */ -public interface Cache { - - /** - * Store a value object in the cache with no specific expiry time. The object may be evicted by the cache any time, - * if necessary. - * - * @param key key that may be used to retrieve the object in the future - * @param value the value object to be stored - */ - void put(String key, Object value); - - /** - * Retrieve a value object from the cache. - * @param key the key that was used to insert the object initially - * @param expectedClass for convenience, a class that the object should be cast to before being returned - * @param the class of the returned object - * @return the object if it was in the cache, or an empty Optional if not found. - */ - Optional get(String key, Class expectedClass); -} \ No newline at end of file diff --git a/shade-test/clashing-deps-jackson/src/main/java/clashingdeps/jackson/cache/RedisJacksonBackedCache.java b/shade-test/clashing-deps-jackson/src/main/java/clashingdeps/jackson/cache/RedisJacksonBackedCache.java deleted file mode 100644 index 1024eb49517..00000000000 --- a/shade-test/clashing-deps-jackson/src/main/java/clashingdeps/jackson/cache/RedisJacksonBackedCache.java +++ /dev/null @@ -1,54 +0,0 @@ -package clashingdeps.jackson.cache; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import redis.clients.jedis.Jedis; - -import java.io.IOException; -import java.util.Optional; - -/** - * An implementation of {@link Cache} that stores data in Redis. - * - * This implementation uses Jackson for JSON serialization/deserialization. - */ -public class RedisJacksonBackedCache implements Cache { - - private final Jedis jedis; - private final String cacheName; - private final ObjectMapper objectMapper; - - public RedisJacksonBackedCache(Jedis jedis, String cacheName) { - this.jedis = jedis; - this.cacheName = cacheName; - this.objectMapper = new ObjectMapper(); - } - - @Override - public void put(String key, Object value) { - String jsonValue = null; - try { - jsonValue = objectMapper.writeValueAsString(value); - } catch (JsonProcessingException e) { - e.printStackTrace(); - // Do something with this in reality - } - this.jedis.hset(this.cacheName, key, jsonValue); - } - - @Override - public Optional get(String key, Class expectedClass) { - String foundJson = this.jedis.hget(this.cacheName, key); - - if (foundJson == null) { - return Optional.empty(); - } - - try { - return Optional.of(objectMapper.readValue(foundJson, expectedClass)); - } catch (IOException e) { - return Optional.empty(); - // Do something with this in reality - } - } -} diff --git a/shade-test/clashing-deps-jackson/src/test/java/RedisJacksonBackedCacheTest.java b/shade-test/clashing-deps-jackson/src/test/java/RedisJacksonBackedCacheTest.java deleted file mode 100644 index 8271233bb98..00000000000 --- a/shade-test/clashing-deps-jackson/src/test/java/RedisJacksonBackedCacheTest.java +++ /dev/null @@ -1,64 +0,0 @@ -import clashingdeps.jackson.cache.Cache; -import clashingdeps.jackson.cache.RedisJacksonBackedCache; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.rnorth.ducttape.unreliables.Unreliables; -import org.testcontainers.containers.GenericContainer; -import redis.clients.jedis.Jedis; - -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -import static org.rnorth.visibleassertions.VisibleAssertions.*; - -/** - * Integration test for Redis-backed cache implementation. - */ -public class RedisJacksonBackedCacheTest { - - @Rule - public GenericContainer redis = new GenericContainer("redis:3.0.6") - .withExposedPorts(6379) - .waitingFor(new RedisWaitStrategy()); - private Cache cache; - - @Before - public void setUp() throws Exception { - Jedis jedis = new Jedis(redis.getContainerIpAddress(), redis.getMappedPort(6379)); - - cache = new RedisJacksonBackedCache(jedis, "test"); - } - - @Test - public void testFindingAnInsertedValue() { - cache.put("foo", "FOO"); - Optional foundObject = cache.get("foo", String.class); - - assertTrue("When an object in the cache is retrieved, it can be found", - foundObject.isPresent()); - assertEquals("When we put a String in to the cache and retrieve it, the value is the same", - "FOO", - foundObject.get()); - } - - @Test - public void testNotFindingAValueThatWasNotInserted() { - Optional foundObject = cache.get("bar", String.class); - - assertFalse("When an object that's not in the cache is retrieved, nothing is found", - foundObject.isPresent()); - } - - private class RedisWaitStrategy extends GenericContainer.AbstractWaitStrategy { - @Override - protected void waitUntilReady() { - //noinspection LoopStatementThatDoesntLoop - Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, () -> - { - Jedis jedis = new Jedis(redis.getContainerIpAddress(), redis.getMappedPort(6379)); - return jedis.ping(); - }); - } - } -} diff --git a/shade-test/clashing-deps-jackson/src/test/resources/logback-test.xml b/shade-test/clashing-deps-jackson/src/test/resources/logback-test.xml deleted file mode 100644 index d12e392071a..00000000000 --- a/shade-test/clashing-deps-jackson/src/test/resources/logback-test.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - %d{HH:mm:ss.SSS} %-5level %logger - %msg%n - - - - - - - - - - - - - - - - - - - - PROFILER - DENY - - \ No newline at end of file diff --git a/shade-test/clashing-deps-jersey/pom.xml b/shade-test/clashing-deps-jersey/pom.xml deleted file mode 100644 index 639f8965ca4..00000000000 --- a/shade-test/clashing-deps-jersey/pom.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - shade-test - org.testcontainers - 0-SNAPSHOT - ../pom.xml - - 4.0.0 - - clashing-deps-jersey - - - - org.testcontainers - testcontainers - ${project.version} - - - com.sun.jersey - jersey-client - 1.19 - - - \ No newline at end of file diff --git a/shade-test/clashing-deps-jersey/src/test/java/OldJerseyClientTest.java b/shade-test/clashing-deps-jersey/src/test/java/OldJerseyClientTest.java deleted file mode 100644 index cffc275240d..00000000000 --- a/shade-test/clashing-deps-jersey/src/test/java/OldJerseyClientTest.java +++ /dev/null @@ -1,23 +0,0 @@ -import com.sun.jersey.api.client.Client; -import org.junit.Rule; -import org.junit.Test; -import org.testcontainers.containers.GenericContainer; - -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; - -/** - * Created by rnorth on 14/01/2016. - */ -public class OldJerseyClientTest { - - @Rule - public GenericContainer httpd = new GenericContainer("httpd:2.4").withExposedPorts(80); - - @Test - public void clientRequestTest() { - Client client = Client.create(); - String s = client.resource("http://" + httpd.getContainerIpAddress() + ":" + httpd.getMappedPort(80)).get(String.class); - - assertTrue("httpd is displaying its default placeholder page", s.contains("It works")); - } -} diff --git a/shade-test/clashing-deps-jersey/src/test/resources/logback-test.xml b/shade-test/clashing-deps-jersey/src/test/resources/logback-test.xml deleted file mode 100644 index d12e392071a..00000000000 --- a/shade-test/clashing-deps-jersey/src/test/resources/logback-test.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - %d{HH:mm:ss.SSS} %-5level %logger - %msg%n - - - - - - - - - - - - - - - - - - - - PROFILER - DENY - - \ No newline at end of file diff --git a/shade-test/jar-file/pom.xml b/shade-test/jar-file/pom.xml deleted file mode 100644 index d731ac33642..00000000000 --- a/shade-test/jar-file/pom.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - shade-test - org.testcontainers - 0-SNAPSHOT - ../pom.xml - - 4.0.0 - - jar-file - - - - org.assertj - assertj-core - 3.8.0 - test - - - - org.ow2.asm - asm-debug-all - 5.2 - test - - - - \ No newline at end of file diff --git a/shade-test/pom.xml b/shade-test/pom.xml deleted file mode 100644 index 0e761a940e3..00000000000 --- a/shade-test/pom.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - testcontainers-parent - org.testcontainers - 0-SNAPSHOT - ../pom.xml - - 4.0.0 - - shade-test - pom - - - clashing-deps-jackson - clashing-deps-jersey - service-lookup-dropwizard - jar-file - - - - - junit - junit - 4.12 - test - - - \ No newline at end of file diff --git a/shade-test/service-lookup-dropwizard/pom.xml b/shade-test/service-lookup-dropwizard/pom.xml deleted file mode 100644 index 8fdc7b27192..00000000000 --- a/shade-test/service-lookup-dropwizard/pom.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - shade-test - org.testcontainers - 0-SNAPSHOT - ../pom.xml - - 4.0.0 - - service-lookup-dropwizard - - - org.testcontainers - testcontainers - ${project.version} - - - io.dropwizard - dropwizard-core - 0.9.2 - - - io.dropwizard - dropwizard-testing - 0.9.2 - - - - - \ No newline at end of file diff --git a/shade-test/service-lookup-dropwizard/src/main/java/JerseyResource.java b/shade-test/service-lookup-dropwizard/src/main/java/JerseyResource.java deleted file mode 100644 index 2ddde90110f..00000000000 --- a/shade-test/service-lookup-dropwizard/src/main/java/JerseyResource.java +++ /dev/null @@ -1,14 +0,0 @@ -import javax.ws.rs.GET; -import javax.ws.rs.Path; - -/** - * Created by rnorth on 18/02/2016. - */ -@Path("/foo") -public class JerseyResource { - - @GET - public String getMessage() { - return "bar"; - } -} diff --git a/shade-test/service-lookup-dropwizard/src/test/java/SimpleResourceTest.java b/shade-test/service-lookup-dropwizard/src/test/java/SimpleResourceTest.java deleted file mode 100644 index cfc7af4c364..00000000000 --- a/shade-test/service-lookup-dropwizard/src/test/java/SimpleResourceTest.java +++ /dev/null @@ -1,23 +0,0 @@ -import io.dropwizard.testing.junit.ResourceTestRule; -import org.junit.ClassRule; -import org.junit.Test; - -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; - -/** - * Created by rnorth on 18/02/2016. - */ -public class SimpleResourceTest { - - @ClassRule - public static final ResourceTestRule resources = ResourceTestRule.builder() - .addResource(new JerseyResource()) - .build(); - - @Test - public void simpleTest() { - assertEquals("the response from the resource", - "bar", - resources.client().target("/foo").request().get(String.class)); - } -} diff --git a/shade-test/service-lookup-dropwizard/src/test/resources/logback-test.xml b/shade-test/service-lookup-dropwizard/src/test/resources/logback-test.xml deleted file mode 100644 index d12e392071a..00000000000 --- a/shade-test/service-lookup-dropwizard/src/test/resources/logback-test.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - %d{HH:mm:ss.SSS} %-5level %logger - %msg%n - - - - - - - - - - - - - - - - - - - - PROFILER - DENY - - \ No newline at end of file From 4471878f332dd8e7880fc8a55879a45834556e5e Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 11 Feb 2018 18:12:38 +0100 Subject: [PATCH 0240/5615] fix "JarFileShadingTest" on Java 9 --- .../testcontainers/JarFileShadingTest.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java b/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java index 8e4e95b63c5..aa66116c080 100644 --- a/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java +++ b/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java @@ -15,21 +15,21 @@ public class JarFileShadingTest extends AbstractJarFileTest { public void testPackages() throws Exception { assertThatFileList(root).containsOnly( "docker-java.properties", - "org/", - "META-INF/", - "com/" + "org", + "META-INF", + "com" ); assertThatFileList(root.resolve("org")).containsOnly( - "testcontainers/" + "testcontainers" ); assertThatFileList(root.resolve("com")).containsOnly( - "github/" + "github" ); assertThatFileList(root.resolve("com").resolve("github")).containsOnly( - "dockerjava/" + "dockerjava" ); } @@ -37,8 +37,8 @@ public void testPackages() throws Exception { public void testMetaInf() throws Exception { assertThatFileList(root.resolve("META-INF")).containsOnly( "MANIFEST.MF", - "services/", - "native/" + "services", + "native" ); assertThatFileList(root.resolve("META-INF").resolve("native")).containsOnly( @@ -56,6 +56,7 @@ public void testMetaInfServices() throws Exception { private ListAssert assertThatFileList(Path path) throws IOException { return (ListAssert) assertThat(Files.list(path)) .extracting(Path::getFileName) - .extracting(Object::toString); + .extracting(Path::toString) + .extracting(it -> it.endsWith("/") ? it.substring(0, it.length() - 1) : it); } } From 0c6ea401dcc1b26b44978fe52448869a3087b18d Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 13 Feb 2018 10:17:32 +0100 Subject: [PATCH 0241/5615] Temporary revert .gitattributes change --- .gitattributes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 07764a78d98..526c8a38d4a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1 @@ -* text eol=lf \ No newline at end of file +*.sh text eol=lf \ No newline at end of file From 2bd025baf9fefb6584fec1efc09729c929cf44b8 Mon Sep 17 00:00:00 2001 From: Nikita Salnikov-Tarnovski Date: Tue, 13 Feb 2018 17:28:59 +0200 Subject: [PATCH 0242/5615] Fixes #543 by adding JdbcDatabaseContainer(Future) constructor (#583) --- CHANGELOG.md | 1 + .../testcontainers/containers/JdbcDatabaseContainer.java | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aeb90f7cbe3..16b5549784a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. ### Changed - Abstracted and changed database init script functionality to support use of SQL-like scripts with non-JDBC connections. ([\#551](https://github.com/testcontainers/testcontainers-java/pull/551)) +- Added `JdbcDatabaseContainer(Future)` constructor. ([\#543](https://github.com/testcontainers/testcontainers-java/issues/543)) ## [1.6.0] - 2018-01-28 diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index b2eefaf3f31..3ebaa87223e 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -1,5 +1,7 @@ package org.testcontainers.containers; +import java.util.concurrent.Future; +import lombok.NonNull; import org.jetbrains.annotations.NotNull; import org.rnorth.ducttape.ratelimits.RateLimiter; import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; @@ -31,10 +33,14 @@ public abstract class JdbcDatabaseContainer image) { + super(image); + } + /** * @return the name of the actual JDBC driver to use */ From 87e22ec774e4c6b3899166eb27f262c721e0895f Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Wed, 21 Feb 2018 18:59:31 +0300 Subject: [PATCH 0243/5615] Extend 'addFixedExposedPort' method with InternetProtocol argument (#586) * Extend 'addFixedExposedPort' method with InternetProtocol argument * 'InternetProtocol' enum introduced to avoid docker-java in public API * 'InternetProtocol' enum values in upper case in accordance with code style conventions --- .../FixedHostPortGenericContainer.java | 16 ++++++++++++++-- .../containers/GenericContainer.java | 19 +++++++++++++++++-- .../containers/InternetProtocol.java | 18 ++++++++++++++++++ 3 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/containers/InternetProtocol.java diff --git a/core/src/main/java/org/testcontainers/containers/FixedHostPortGenericContainer.java b/core/src/main/java/org/testcontainers/containers/FixedHostPortGenericContainer.java index 83c749c8624..33c1c8e89bd 100644 --- a/core/src/main/java/org/testcontainers/containers/FixedHostPortGenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/FixedHostPortGenericContainer.java @@ -17,14 +17,26 @@ public FixedHostPortGenericContainer(@NotNull String dockerImageName) { } /** - * Bind a fixed port on the docker host to a container port + * Bind a fixed TCP port on the docker host to a container port * @param hostPort a port on the docker host, which must be available * @param containerPort a port in the container * @return this container */ public SELF withFixedExposedPort(int hostPort, int containerPort) { - super.addFixedExposedPort(hostPort, containerPort); + return withFixedExposedPort(hostPort, containerPort, InternetProtocol.TCP); + } + + /** + * Bind a fixed port on the docker host to a container port + * @param hostPort a port on the docker host, which must be available + * @param containerPort a port in the container + * @param protocol an internet protocol (tcp or udp) + * @return this container + */ + public SELF withFixedExposedPort(int hostPort, int containerPort, InternetProtocol protocol) { + + super.addFixedExposedPort(hostPort, containerPort, protocol); return self(); } diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index a3a0ab47e78..859b452ea71 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -644,7 +644,7 @@ public SELF withExposedPorts(Integer... ports) { } /** - * Add a container port that should be bound to a fixed port on the docker host. + * Add a TCP container port that should be bound to a fixed port on the docker host. *

* Note that this method is protected scope to discourage use, as clashes or instability are more likely when * using fixed port mappings. If you need to use this method from a test, please use {@link FixedHostPortGenericContainer} @@ -654,7 +654,22 @@ public SELF withExposedPorts(Integer... ports) { * @param containerPort */ protected void addFixedExposedPort(int hostPort, int containerPort) { - portBindings.add(String.format("%d:%d", hostPort, containerPort)); + addFixedExposedPort(hostPort, containerPort, InternetProtocol.TCP); + } + + /** + * Add a container port that should be bound to a fixed port on the docker host. + *

+ * Note that this method is protected scope to discourage use, as clashes or instability are more likely when + * using fixed port mappings. If you need to use this method from a test, please use {@link FixedHostPortGenericContainer} + * instead of GenericContainer. + * + * @param hostPort + * @param containerPort + * @param protocol + */ + protected void addFixedExposedPort(int hostPort, int containerPort, InternetProtocol protocol) { + portBindings.add(String.format("%d:%d/%s", hostPort, containerPort, protocol.toDockerNotation())); } /** diff --git a/core/src/main/java/org/testcontainers/containers/InternetProtocol.java b/core/src/main/java/org/testcontainers/containers/InternetProtocol.java new file mode 100644 index 00000000000..0df0480a1bf --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/InternetProtocol.java @@ -0,0 +1,18 @@ +package org.testcontainers.containers; + +/** + * The IP protocols supported by Docker. + */ +public enum InternetProtocol { + + TCP, + UDP; + + public String toDockerNotation() { + return name().toLowerCase(); + } + + public static InternetProtocol fromDockerNotation(String protocol) { + return valueOf(protocol.toUpperCase()); + } +} From 3cf1f852a7e8cd9ace12ca718a4b18df0c9a7721 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 25 Feb 2018 06:59:08 +0000 Subject: [PATCH 0244/5615] Fix 'custom ini' used for MySQL tests to prevent startup failures (#589) --- .../src/test/resources/somepath/mysql_conf_override/my.cnf | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/jdbc-test/src/test/resources/somepath/mysql_conf_override/my.cnf b/modules/jdbc-test/src/test/resources/somepath/mysql_conf_override/my.cnf index 220060976ab..78012cc4bfe 100644 --- a/modules/jdbc-test/src/test/resources/somepath/mysql_conf_override/my.cnf +++ b/modules/jdbc-test/src/test/resources/somepath/mysql_conf_override/my.cnf @@ -1,4 +1,5 @@ [mysqld] +user = mysql port = 3306 #socket = /tmp/mysql.sock skip-external-locking From 14159e707a24b9d215ccff3931b4605ce66cbf57 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 25 Feb 2018 07:10:52 +0000 Subject: [PATCH 0245/5615] Use HttpWaitStrategy for SimpleNginxTest (#590) To ensure proper waits and avoid race conditions (flaky test) --- .../test/java/org/testcontainers/junit/SimpleNginxTest.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java b/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java index fe5c7f85c11..239f7a18877 100644 --- a/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java +++ b/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java @@ -5,6 +5,7 @@ import org.junit.Rule; import org.junit.Test; import org.testcontainers.containers.NginxContainer; +import org.testcontainers.containers.wait.HttpWaitStrategy; import java.io.*; import java.net.URLConnection; @@ -20,8 +21,9 @@ public class SimpleNginxTest { private static File contentFolder = new File(System.getProperty("user.home") + "/.tmp-test-container"); @Rule - public NginxContainer nginx = new NginxContainer() - .withCustomContent(contentFolder.toString()); + public NginxContainer nginx = new NginxContainer<>() + .withCustomContent(contentFolder.toString()) + .waitingFor(new HttpWaitStrategy()); @BeforeClass public static void setupContent() throws FileNotFoundException { From 242605aad0c54a49e7d04765b7954d007ce46f7c Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 25 Feb 2018 19:42:13 +0000 Subject: [PATCH 0246/5615] Add editorconfig for a degree of IDE formatting consistency (#588) --- .editorconfig | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000000..ce20a2be466 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false From 6f151da27ac510eac2562d20f48003b731fa7a08 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 26 Feb 2018 10:40:21 +0100 Subject: [PATCH 0247/5615] Mark DockerMachineClientProviderStrategy as not persistable (#593) * Mark DockerMachineClientProviderStrategy as not persistable * Update CHANGELOG.md --- CHANGELOG.md | 1 + .../dockerclient/DockerClientProviderStrategy.java | 13 +++++++++++-- .../DockerMachineClientProviderStrategy.java | 5 +++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16b5549784a..a9d1fc83dee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file. ### Changed - Abstracted and changed database init script functionality to support use of SQL-like scripts with non-JDBC connections. ([\#551](https://github.com/testcontainers/testcontainers-java/pull/551)) - Added `JdbcDatabaseContainer(Future)` constructor. ([\#543](https://github.com/testcontainers/testcontainers-java/issues/543)) +- Mark DockerMachineClientProviderStrategy as not persistable ([\#593](https://github.com/testcontainers/testcontainers-java/pull/593)) ## [1.6.0] - 2018-01-28 diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index 8c89feed2ca..f4dc5b2c4dd 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -52,6 +52,10 @@ protected boolean isApplicable() { return true; } + protected boolean isPersistable() { + return true; + } + /** * @return highest to lowest priority value */ @@ -93,7 +97,10 @@ public static DockerClientProviderStrategy getFirstValidStrategy(List LOGGER.info("Loaded {} from ~/.testcontainers.properties, will try it first", strategy.getClass().getName())), strategies .stream() .filter(DockerClientProviderStrategy::isApplicable) @@ -104,7 +111,9 @@ public static DockerClientProviderStrategy getFirstValidStrategy(List Date: Sun, 25 Feb 2018 16:06:31 +0100 Subject: [PATCH 0248/5615] Add spock module --- modules/spock/README.md | 65 +++++++++++ modules/spock/build.gradle | 17 +++ .../spock/Testcontainers.groovy | 15 +++ .../spock/TestcontainersExtension.groovy | 17 +++ .../TestcontainersMethodInterceptor.groovy | 108 ++++++++++++++++++ .../spock/ComposeContainerIT.groovy | 34 ++++++ .../spock/MySqlContainerIT.groovy | 26 +++++ .../spock/PostgresContainerIT.groovy | 44 +++++++ .../spock/SharedComposeContainerIT.groovy | 36 ++++++ .../spock/TestHierarchyIT.groovy | 25 ++++ ...TestcontainersRestartBetweenTestsIT.groovy | 31 +++++ .../TestcontainersSharedContainerIT.groovy | 47 ++++++++ .../src/test/resources/docker-compose.yml | 4 + .../spock/src/test/resources/logback-test.xml | 15 +++ 14 files changed, 484 insertions(+) create mode 100644 modules/spock/README.md create mode 100644 modules/spock/build.gradle create mode 100644 modules/spock/src/main/groovy/org/testcontainers/spock/Testcontainers.groovy create mode 100644 modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersExtension.groovy create mode 100644 modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersMethodInterceptor.groovy create mode 100644 modules/spock/src/test/groovy/org/testcontainers/spock/ComposeContainerIT.groovy create mode 100644 modules/spock/src/test/groovy/org/testcontainers/spock/MySqlContainerIT.groovy create mode 100644 modules/spock/src/test/groovy/org/testcontainers/spock/PostgresContainerIT.groovy create mode 100644 modules/spock/src/test/groovy/org/testcontainers/spock/SharedComposeContainerIT.groovy create mode 100644 modules/spock/src/test/groovy/org/testcontainers/spock/TestHierarchyIT.groovy create mode 100644 modules/spock/src/test/groovy/org/testcontainers/spock/TestcontainersRestartBetweenTestsIT.groovy create mode 100644 modules/spock/src/test/groovy/org/testcontainers/spock/TestcontainersSharedContainerIT.groovy create mode 100644 modules/spock/src/test/resources/docker-compose.yml create mode 100644 modules/spock/src/test/resources/logback-test.xml diff --git a/modules/spock/README.md b/modules/spock/README.md new file mode 100644 index 00000000000..e201ce3180e --- /dev/null +++ b/modules/spock/README.md @@ -0,0 +1,65 @@ +# TestContainers-Spock +[Spock](https://github.com/spockframework/spock) extension for [TestContainers](https://github.com/testcontainers/testcontainers-java) library, which allows to use Docker containers inside of Spock tests. + +# Usage + +## @Testcontainers class-annotation + +Specifying the `@Testcontainers` annotation will instruct Spock to start and stop all testcontainers accordingly. This annotation +can be mixed with Spock's `@Shared` annotation to indicate, that containers shouldn't be restarted between tests. + +```groovy +@Testcontainers +class DatabaseTest extends Specification { + + @Shared + PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer() + .withDatabaseName("foo") + .withUsername("foo") + .withPassword("secret") + + def "database is accessible"() { + + given: "a jdbc connection" + HikariConfig hikariConfig = new HikariConfig() + hikariConfig.setJdbcUrl(postgreSQLContainer.jdbcUrl) + hikariConfig.setUsername("foo") + hikariConfig.setPassword("secret") + HikariDataSource ds = new HikariDataSource(hikariConfig) + + when: "querying the database" + Statement statement = ds.getConnection().createStatement() + statement.execute("SELECT 1") + ResultSet resultSet = statement.getResultSet() + resultSet.next() + + then: "result is returned" + int resultSetInt = resultSet.getInt(1) + resultSetInt == 1 + } +} +``` + +## General TestContainers usage + +See the [TestContainers documentation](https://www.testcontainers.org/) for more information about the underlying library. + +# Attributions +The initial version of this project was heavily inspired by the excellent [JUnit5 docker extension](https://github.com/FaustXVI/junit5-docker) by [FaustXVI](https://github.com/FaustXVI). + +# License +Copyright 2016 Kevin Wittek + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this project except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +See [LICENSE](LICENSE). diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle new file mode 100644 index 00000000000..40a668f5411 --- /dev/null +++ b/modules/spock/build.gradle @@ -0,0 +1,17 @@ +plugins { + id 'groovy' +} + +description = "Testcontainers :: Spock-Extension" + +dependencies { + compile project(':testcontainers') + compile 'org.spockframework:spock-core:1.0-groovy-2.4' + + testCompile project(':mysql') + testCompile project(':postgresql') + testCompile 'com.zaxxer:HikariCP:2.6.1' + + testRuntime 'org.postgresql:postgresql:42.0.0' + testRuntime 'mysql:mysql-connector-java:6.0.6' +} \ No newline at end of file diff --git a/modules/spock/src/main/groovy/org/testcontainers/spock/Testcontainers.groovy b/modules/spock/src/main/groovy/org/testcontainers/spock/Testcontainers.groovy new file mode 100644 index 00000000000..7bbd47a4629 --- /dev/null +++ b/modules/spock/src/main/groovy/org/testcontainers/spock/Testcontainers.groovy @@ -0,0 +1,15 @@ +package org.testcontainers.spock + +import org.spockframework.runtime.extension.ExtensionAnnotation + +import java.lang.annotation.ElementType +import java.lang.annotation.Retention +import java.lang.annotation.RetentionPolicy +import java.lang.annotation.Target + +@Retention(RetentionPolicy.RUNTIME) +@Target([ElementType.TYPE, ElementType.METHOD]) +@ExtensionAnnotation(TestcontainersExtension) +@interface Testcontainers { + +} \ No newline at end of file diff --git a/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersExtension.groovy b/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersExtension.groovy new file mode 100644 index 00000000000..9f26cf95451 --- /dev/null +++ b/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersExtension.groovy @@ -0,0 +1,17 @@ +package org.testcontainers.spock + +import org.spockframework.runtime.extension.AbstractAnnotationDrivenExtension +import org.spockframework.runtime.model.SpecInfo + +class TestcontainersExtension extends AbstractAnnotationDrivenExtension { + + @Override + void visitSpecAnnotation(Testcontainers annotation, SpecInfo spec) { + def interceptor = new TestcontainersMethodInterceptor(spec) + spec.addSetupSpecInterceptor(interceptor) + spec.addCleanupSpecInterceptor(interceptor) + spec.addSetupInterceptor(interceptor) + spec.addCleanupInterceptor(interceptor) + } + +} diff --git a/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersMethodInterceptor.groovy b/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersMethodInterceptor.groovy new file mode 100644 index 00000000000..d4b6f3ccc9d --- /dev/null +++ b/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersMethodInterceptor.groovy @@ -0,0 +1,108 @@ +package org.testcontainers.spock + +import org.spockframework.runtime.extension.AbstractMethodInterceptor +import org.spockframework.runtime.extension.IMethodInvocation +import org.spockframework.runtime.model.FieldInfo +import org.spockframework.runtime.model.SpecInfo +import org.testcontainers.containers.DockerComposeContainer +import org.testcontainers.containers.GenericContainer + +class TestcontainersMethodInterceptor extends AbstractMethodInterceptor { + + private final SpecInfo spec + + TestcontainersMethodInterceptor(SpecInfo spec) { + this.spec = spec + } + + @Override + void interceptSetupSpecMethod(IMethodInvocation invocation) throws Throwable { + def containers = findAllContainers(true) + startContainers(containers, invocation) + + def compose = findAllComposeContainers(true) + startComposeContainers(compose, invocation) + + invocation.proceed() + } + + void interceptCleanupSpecMethod(IMethodInvocation invocation) throws Throwable { + def containers = findAllContainers(true) + stopContainers(containers, invocation) + + def compose = findAllComposeContainers(true) + stopComposeContainers(compose, invocation) + + invocation.proceed() + } + + @Override + void interceptSetupMethod(IMethodInvocation invocation) throws Throwable { + def containers = findAllContainers(false) + startContainers(containers, invocation) + + def compose = findAllComposeContainers(false) + startComposeContainers(compose, invocation) + + invocation.proceed() + } + + + @Override + void interceptCleanupMethod(IMethodInvocation invocation) throws Throwable { + def containers = findAllContainers(false) + stopContainers(containers, invocation) + + def compose = findAllComposeContainers(false) + stopComposeContainers(compose, invocation) + + invocation.proceed() + } + + private List findAllContainers(boolean shared) { + spec.allFields.findAll { FieldInfo f -> + GenericContainer.isAssignableFrom(f.type) && f.shared == shared + } + } + + private List findAllComposeContainers(boolean shared) { + spec.allFields.findAll { FieldInfo f -> + DockerComposeContainer.isAssignableFrom(f.type) && f.shared == shared + } + } + + private static void startContainers(List containers, IMethodInvocation invocation) { + containers.each { FieldInfo f -> + GenericContainer container = readContainerFromField(f, invocation) + if(!container.isRunning()){ + container.start() + } + } + } + + private static void stopContainers(List containers, IMethodInvocation invocation) { + containers.each { FieldInfo f -> + GenericContainer container = readContainerFromField(f, invocation) + container.stop() + } + } + + private static void startComposeContainers(List compose, IMethodInvocation invocation) { + compose.each { FieldInfo f -> + DockerComposeContainer c = f.readValue(invocation.instance) as DockerComposeContainer + c.starting(null) + } + } + + private static void stopComposeContainers(List compose, IMethodInvocation invocation) { + compose.each { FieldInfo f -> + DockerComposeContainer c = f.readValue(invocation.instance) as DockerComposeContainer + c.finished(null) + } + } + + + private static GenericContainer readContainerFromField(FieldInfo f, IMethodInvocation invocation) { + f.readValue(invocation.instance) as GenericContainer + } +} diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/ComposeContainerIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/ComposeContainerIT.groovy new file mode 100644 index 00000000000..f90d7f7a4a7 --- /dev/null +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/ComposeContainerIT.groovy @@ -0,0 +1,34 @@ +package org.testcontainers.spock + +import org.apache.http.client.methods.HttpGet +import org.apache.http.impl.client.HttpClientBuilder +import org.testcontainers.containers.DockerComposeContainer +import spock.lang.Specification + +@Testcontainers +class ComposeContainerIT extends Specification { + + DockerComposeContainer composeContainer = new DockerComposeContainer( + new File("src/test/resources/docker-compose.yml")) + .withExposedService("whoami_1", 80) + + String host + + int port + + def setup() { + host = composeContainer.getServiceHost("whoami_1", 80) + port = composeContainer.getServicePort("whoami_1", 80) + } + + def "running compose defined container is accessible on configured port"() { + given: "a http client" + def client = HttpClientBuilder.create().build() + + when: "accessing web server" + def response = client.execute(new HttpGet("http://$host:$port")) + + then: "docker container is running and returns http status code 200" + response.statusLine.statusCode == 200 + } +} diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/MySqlContainerIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/MySqlContainerIT.groovy new file mode 100644 index 00000000000..7a68302289f --- /dev/null +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/MySqlContainerIT.groovy @@ -0,0 +1,26 @@ +package org.testcontainers.spock + +import org.testcontainers.containers.MySQLContainer +import spock.lang.Shared +import spock.lang.Specification + +/** + * This test verifies, that setup and cleanup of containers works correctly. + * It's easily achieved using the MySQLContainer, since it will fail + * if the same image is running. + * + * @see Second container is started when stopping old container + */ +@Testcontainers +class MySqlContainerIT extends Specification { + + @Shared + MySQLContainer mySQLContainer = new MySQLContainer() + + def "dummy test"() { + expect: + mySQLContainer.isRunning() + } + + +} diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/PostgresContainerIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/PostgresContainerIT.groovy new file mode 100644 index 00000000000..6e090e77c33 --- /dev/null +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/PostgresContainerIT.groovy @@ -0,0 +1,44 @@ +package org.testcontainers.spock + +import com.zaxxer.hikari.HikariConfig +import com.zaxxer.hikari.HikariDataSource +import org.testcontainers.containers.PostgreSQLContainer +import spock.lang.Shared +import spock.lang.Specification + +import java.sql.ResultSet +import java.sql.Statement + +@Testcontainers +class PostgresContainerIT extends Specification { + + @Shared + PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer() + .withDatabaseName("foo") + .withUsername("foo") + .withPassword("secret") + + def "waits until postgres accepts jdbc connections"() { + + given: "a jdbc connection" + HikariConfig hikariConfig = new HikariConfig() + hikariConfig.setJdbcUrl(postgreSQLContainer.jdbcUrl) + hikariConfig.setUsername("foo") + hikariConfig.setPassword("secret") + HikariDataSource ds = new HikariDataSource(hikariConfig) + + when: "querying the database" + Statement statement = ds.getConnection().createStatement() + statement.execute("SELECT 1") + ResultSet resultSet = statement.getResultSet() + resultSet.next() + + then: "result is returned" + int resultSetInt = resultSet.getInt(1) + resultSetInt == 1 + + cleanup: + ds.close() + } + +} diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/SharedComposeContainerIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/SharedComposeContainerIT.groovy new file mode 100644 index 00000000000..091ac4cb044 --- /dev/null +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/SharedComposeContainerIT.groovy @@ -0,0 +1,36 @@ +package org.testcontainers.spock + +import org.apache.http.client.methods.HttpGet +import org.apache.http.impl.client.HttpClientBuilder +import org.testcontainers.containers.DockerComposeContainer +import spock.lang.Shared +import spock.lang.Specification + +@Testcontainers +class SharedComposeContainerIT extends Specification { + + @Shared + DockerComposeContainer composeContainer = new DockerComposeContainer( + new File("src/test/resources/docker-compose.yml")) + .withExposedService("whoami_1", 80) + + String host + + int port + + def setup() { + host = composeContainer.getServiceHost("whoami_1", 80) + port = composeContainer.getServicePort("whoami_1", 80) + } + + def "running compose defined container is accessible on configured port"() { + given: "a http client" + def client = HttpClientBuilder.create().build() + + when: "accessing web server" + def response = client.execute(new HttpGet("http://$host:$port")) + + then: "docker container is running and returns http status code 200" + response.statusLine.statusCode == 200 + } +} diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/TestHierarchyIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/TestHierarchyIT.groovy new file mode 100644 index 00000000000..123828327bc --- /dev/null +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/TestHierarchyIT.groovy @@ -0,0 +1,25 @@ +package org.testcontainers.spock + +import org.testcontainers.containers.PostgreSQLContainer +import spock.lang.Shared + +/** + * This test verifies that integration tests can subclass each other + */ +@Testcontainers +class TestHierarchyIT extends MySqlContainerIT { + + @Shared + PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer() + .withDatabaseName("foo") + .withUsername("foo") + .withPassword("secret") + + def "both containers are running"() { + expect: + postgreSQLContainer.isRunning() + mySQLContainer.isRunning() + + } + +} diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/TestcontainersRestartBetweenTestsIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/TestcontainersRestartBetweenTestsIT.groovy new file mode 100644 index 00000000000..b4b3741e580 --- /dev/null +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/TestcontainersRestartBetweenTestsIT.groovy @@ -0,0 +1,31 @@ +package org.testcontainers.spock + +import org.testcontainers.containers.GenericContainer +import spock.lang.Shared +import spock.lang.Specification +import spock.lang.Stepwise + +@Stepwise +@Testcontainers +class TestcontainersRestartBetweenTestsIT extends Specification { + + GenericContainer genericContainer = new GenericContainer("httpd:2.4-alpine") + .withExposedPorts(80) + + @Shared + String lastContainerId + + def "retrieving first id"() { + when: + lastContainerId = genericContainer.containerId + + then: + true + } + + def "containers is recreated between tests"() { + expect: + genericContainer.containerId != lastContainerId + } + +} diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/TestcontainersSharedContainerIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/TestcontainersSharedContainerIT.groovy new file mode 100644 index 00000000000..014b25eac1f --- /dev/null +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/TestcontainersSharedContainerIT.groovy @@ -0,0 +1,47 @@ +package org.testcontainers.spock + +import org.apache.http.client.methods.CloseableHttpResponse +import org.apache.http.client.methods.HttpGet +import org.apache.http.impl.client.CloseableHttpClient +import org.apache.http.impl.client.HttpClientBuilder +import org.testcontainers.containers.GenericContainer +import spock.lang.Shared +import spock.lang.Specification +import spock.lang.Stepwise + +@Stepwise +@Testcontainers +class TestcontainersSharedContainerIT extends Specification { + + @Shared + GenericContainer genericContainer = new GenericContainer("httpd:2.4-alpine") + .withExposedPorts(80) + + @Shared + String lastContainerId + + def "starts accessible docker container"() { + given: "a http client" + def client = HttpClientBuilder.create().build() + lastContainerId = genericContainer.containerId + + when: "accessing web server" + CloseableHttpResponse response = performHttpRequest(client) + + then: "docker container is running and returns http status code 200" + response.statusLine.statusCode == 200 + } + + def "containers keeps on running between features"() { + expect: + genericContainer.containerId == lastContainerId + } + + private CloseableHttpResponse performHttpRequest(CloseableHttpClient client) { + String ip = genericContainer.containerIpAddress + String port = genericContainer.getMappedPort(80) + def response = client.execute(new HttpGet("http://$ip:$port")) + response + } + +} diff --git a/modules/spock/src/test/resources/docker-compose.yml b/modules/spock/src/test/resources/docker-compose.yml new file mode 100644 index 00000000000..ec1015f106a --- /dev/null +++ b/modules/spock/src/test/resources/docker-compose.yml @@ -0,0 +1,4 @@ +version: '2' +services: + whoami: + image: emilevauge/whoami diff --git a/modules/spock/src/test/resources/logback-test.xml b/modules/spock/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..1d1a7695915 --- /dev/null +++ b/modules/spock/src/test/resources/logback-test.xml @@ -0,0 +1,15 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + \ No newline at end of file From 8e313f17f64bbaaa264980ea9ed3b84a7a60f242 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Sun, 25 Feb 2018 23:15:28 +0100 Subject: [PATCH 0249/5615] Remove LICENSE block from spock module README --- modules/spock/README.md | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/modules/spock/README.md b/modules/spock/README.md index e201ce3180e..0baae0e9c91 100644 --- a/modules/spock/README.md +++ b/modules/spock/README.md @@ -46,20 +46,3 @@ See the [TestContainers documentation](https://www.testcontainers.org/) for more # Attributions The initial version of this project was heavily inspired by the excellent [JUnit5 docker extension](https://github.com/FaustXVI/junit5-docker) by [FaustXVI](https://github.com/FaustXVI). - -# License -Copyright 2016 Kevin Wittek - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this project except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -See [LICENSE](LICENSE). From b3d290aa1dcd1f70404dcea994de0adfdcec7273 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Mon, 26 Feb 2018 22:43:42 +0100 Subject: [PATCH 0250/5615] Fix problems with Travis gradle cache --- .travis.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 544f0056c47..2075d76f400 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,9 +6,13 @@ sudo: required services: - docker +before_cache: + - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock + - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ cache: directories: - - '$HOME/.gradle' + - $HOME/.gradle/caches/ + - $HOME/.gradle/wrapper/ install: - ./gradlew build -x check @@ -40,7 +44,7 @@ jobs: -v "$(pwd)":"$(pwd)" \ -w "$(pwd)" \ openjdk:8-jdk-alpine \ - ./gradlew testcontainers:test --tests '*GenericContainerRuleTest' + ./gradlew --no-daemon testcontainers:test --tests '*GenericContainerRuleTest' - stage: deploy sudo: false From baa6fe4e2068faf798be4507d704576598e0d630 Mon Sep 17 00:00:00 2001 From: Tom Caflisch Date: Sun, 11 Mar 2018 07:38:39 -0500 Subject: [PATCH 0251/5615] Update usage.md (#601) Added missing db's to usage --- docs/usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage.md b/docs/usage.md index 0a1401733c1..8ffabb3a96b 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -23,7 +23,7 @@ Testcontainers will try to connect to a Docker daemon using the following strate ### Usage modes -* [Temporary database containers](usage/database_containers.md) - specialized MySQL, PostgreSQL, Oracle XE and Virtuoso container support +* [Temporary database containers](usage/database_containers.md) - specialized Microsoft SQL Server, MariaDB, MySQL, PostgreSQL, Oracle XE and Virtuoso container support * [Webdriver containers](usage/webdriver_containers.md) - run a Dockerized Chrome or Firefox browser ready for Selenium/Webdriver operations - complete with automatic video recording * [Generic containers](usage/generic_containers.md) - run any Docker container as a test dependency * [Docker compose](usage/docker_compose.md) - reuse services defined in a Docker Compose YAML file From c2116ca69b3b01bf19e9177bdde3fda787be24ea Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 13 Mar 2018 21:50:28 +0100 Subject: [PATCH 0252/5615] Do not add /dummy bind to Ryuk if checks are disabled (#604) * Do not add /dummy bind to Ryuk if checks are disabled * use the flag --- .../testcontainers/DockerClientFactory.java | 6 ++++-- .../utility/ResourceReaper.java | 19 +++++++++++++------ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index a2177c886a6..22f31123f5a 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -113,14 +113,16 @@ public DockerClient client() { " Operating System: " + dockerInfo.getOperatingSystem() + "\n" + " Total Memory: " + dockerInfo.getMemTotal() / (1024 * 1024) + " MB"); - String ryukContainerId = ResourceReaper.start(hostIpAddress, client); + boolean checksEnabled = !TestcontainersConfiguration.getInstance().isDisableChecks(); + + String ryukContainerId = ResourceReaper.start(hostIpAddress, client, checksEnabled); log.info("Ryuk started - will monitor and terminate Testcontainers containers on JVM exit"); VisibleAssertions.info("Checking the system..."); checkDockerVersion(version.getVersion()); - if (!TestcontainersConfiguration.getInstance().isDisableChecks()) { + if (checksEnabled) { checkDiskSpace(client, ryukContainerId); checkMountableFile(client, ryukContainerId); } diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index 6e169114e57..7c2cd109b8d 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -59,13 +59,24 @@ private ResourceReaper() { dockerClient = DockerClientFactory.instance().client(); } - @SneakyThrows(InterruptedException.class) public static String start(String hostIpAddress, DockerClient client) { + return start(hostIpAddress, client, false); + } + + @SneakyThrows(InterruptedException.class) + public static String start(String hostIpAddress, DockerClient client, boolean withDummyMount) { String ryukImage = TestcontainersConfiguration.getInstance().getRyukImage(); DockerClientFactory.instance().checkAndPullImage(client, ryukImage); MountableFile mountableFile = MountableFile.forClasspathResource(ResourceReaper.class.getName().replace(".", "/") + ".class"); + List binds = new ArrayList<>(); + binds.add(new Bind("//var/run/docker.sock", new Volume("/var/run/docker.sock"))); + if (withDummyMount) { + // Not needed for Ryuk, but we perform pre-flight checks with it (micro optimization) + binds.add(new Bind(mountableFile.getResolvedPath(), new Volume("/dummy"), AccessMode.ro)); + } + String ryukContainerId = client.createContainerCmd(ryukImage) .withHostConfig(new HostConfig() { @JsonProperty("AutoRemove") @@ -75,11 +86,7 @@ public static String start(String hostIpAddress, DockerClient client) { .withPublishAllPorts(true) .withName("testcontainers-ryuk-" + DockerClientFactory.SESSION_ID) .withLabels(Collections.singletonMap(DockerClientFactory.TESTCONTAINERS_LABEL, "true")) - .withBinds( - new Bind("//var/run/docker.sock", new Volume("/var/run/docker.sock")), - // Not needed for Ryuk, but we perform pre-flight checks with it (micro optimization) - new Bind(mountableFile.getResolvedPath(), new Volume("/dummy"), AccessMode.ro) - ) + .withBinds(binds) .exec() .getId(); From 85582030fb78ec28ae76f43417715f5ce7c1991f Mon Sep 17 00:00:00 2001 From: Barry Commins Date: Mon, 19 Mar 2018 06:38:05 +0000 Subject: [PATCH 0253/5615] Support for WaitStrategy on DockerComposeContainer - alternative approach (#600) * Support for WaitStrategy on DockerComposeContainer As discussed in #174 * CI code quality fixes * CI code quality fixes * Another attempt at adding WaitStrategy support for docker-compose * Another attempt at adding WaitStrategy support for docker-compose fixed some tests and added some files missed from previous commit * Changes after code review comments Added new WaitStrategyTarget interface, plus moved some methods into new LogFollower and CommandExecutor interfaces * Changes after code review comments Removed CommandExecutor and LogFollower interfaces. Added ExecInContainerPattern utility class. Changed existing WaitStrategy implementations to inherit from new ones * CI code quality fixes * Changes from code review. Moved getMappedPort to default implementation in ContainerState. Using proxyContainer in ComposeStrategyWaitServiceTarget to get ip. Removed unnecessary null checks. Using lazy getter for containerInfo in ComposeStrategyWaitServiceTarget. * Changes from code review. Removed getLogger() method. Removed getContainerName from Container and ContainerState. Removed getExposedPortNumbers from ContainerState. Added waitingFor method to DockerComposeContainer. Removed StartupTimeout interface. --- CHANGELOG.md | 4 + .../ComposeServiceWaitStrategyTarget.java | 68 ++++++ .../testcontainers/containers/Container.java | 78 ++----- .../containers/ContainerState.java | 114 ++++++++++ .../containers/DockerComposeContainer.java | 111 ++++++++-- .../containers/ExecInContainerPattern.java | 102 +++++++++ .../containers/GenericContainer.java | 174 +++++----------- .../containers/wait/HostPortWaitStrategy.java | 52 +---- .../containers/wait/HttpWaitStrategy.java | 138 +------------ .../wait/LogMessageWaitStrategy.java | 29 +-- .../testcontainers/containers/wait/Wait.java | 14 ++ .../containers/wait/WaitAllStrategy.java | 3 + .../containers/wait/WaitStrategy.java | 17 +- .../internal/ExternalPortListeningCheck.java | 6 +- .../InternalCommandPortListeningCheck.java | 7 +- .../wait/strategy/AbstractWaitStrategy.java | 65 ++++++ .../wait/strategy/HostPortWaitStrategy.java | 59 ++++++ .../wait/strategy/HttpWaitStrategy.java | 195 ++++++++++++++++++ .../wait/strategy/LogMessageWaitStrategy.java | 43 ++++ .../containers/wait/strategy/Wait.java | 64 ++++++ .../wait/strategy/WaitAllStrategy.java | 34 +++ .../wait/strategy/WaitStrategy.java | 10 + .../wait/strategy/WaitStrategyTarget.java | 19 ++ .../org/testcontainers/utility/LogUtils.java | 4 + .../ExternalPortListeningCheckTest.java | 8 +- .../junit/BaseDockerComposeTest.java | 25 +-- .../junit/DockerComposePassthroughTest.java | 72 ++++--- .../junit/DockerComposeWaitStrategyTest.java | 65 ++++++ .../strategy/HostPortWaitStrategyTest.java | 30 +++ .../resources/v2-compose-test-passthrough.yml | 2 +- docs/usage/docker_compose.md | 57 ++++- .../spock/ComposeContainerIT.groovy | 3 +- .../spock/SharedComposeContainerIT.groovy | 3 +- 33 files changed, 1203 insertions(+), 472 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/containers/ComposeServiceWaitStrategyTarget.java create mode 100644 core/src/main/java/org/testcontainers/containers/ContainerState.java create mode 100644 core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java create mode 100644 core/src/main/java/org/testcontainers/containers/wait/strategy/AbstractWaitStrategy.java create mode 100644 core/src/main/java/org/testcontainers/containers/wait/strategy/HostPortWaitStrategy.java create mode 100644 core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java create mode 100644 core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java create mode 100644 core/src/main/java/org/testcontainers/containers/wait/strategy/Wait.java create mode 100644 core/src/main/java/org/testcontainers/containers/wait/strategy/WaitAllStrategy.java create mode 100644 core/src/main/java/org/testcontainers/containers/wait/strategy/WaitStrategy.java create mode 100644 core/src/main/java/org/testcontainers/containers/wait/strategy/WaitStrategyTarget.java create mode 100644 core/src/test/java/org/testcontainers/junit/DockerComposeWaitStrategyTest.java create mode 100644 core/src/test/java/org/testcontainers/junit/wait/strategy/HostPortWaitStrategyTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index a9d1fc83dee..8b6aa92c254 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ All notable changes to this project will be documented in this file. - Abstracted and changed database init script functionality to support use of SQL-like scripts with non-JDBC connections. ([\#551](https://github.com/testcontainers/testcontainers-java/pull/551)) - Added `JdbcDatabaseContainer(Future)` constructor. ([\#543](https://github.com/testcontainers/testcontainers-java/issues/543)) - Mark DockerMachineClientProviderStrategy as not persistable ([\#593](https://github.com/testcontainers/testcontainers-java/pull/593)) +- Added `waitingFor(String serviceName, WaitStrategy waitStrategy)` and overloaded `withExposedService()` methods to `DockerComposeContainer` to allow user to define `WaitStrategy` for compose containers. ([\#174](https://github.com/testcontainers/testcontainers-java/issues/174) and [\#515](https://github.com/testcontainers/testcontainers-java/issues/515)) +- Deprecated `WaitStrategy` and implementations in favour of classes with same names in `org.testcontainers.containers.strategy` +- Added `ContainerState` interface representing the state of a started container +- Added `WaitStrategyTarget` interface which is the target of the new `WaitStrategy` ## [1.6.0] - 2018-01-28 diff --git a/core/src/main/java/org/testcontainers/containers/ComposeServiceWaitStrategyTarget.java b/core/src/main/java/org/testcontainers/containers/ComposeServiceWaitStrategyTarget.java new file mode 100644 index 00000000000..d4ad51dc1da --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/ComposeServiceWaitStrategyTarget.java @@ -0,0 +1,68 @@ +package org.testcontainers.containers; + +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.model.Container; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NonNull; +import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.wait.strategy.WaitStrategyTarget; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +/** + * Class to provide a wait strategy target for services started through docker-compose + */ +@EqualsAndHashCode +class ComposeServiceWaitStrategyTarget implements WaitStrategyTarget { + + private final Container container; + private final GenericContainer proxyContainer; + @NonNull + private Map mappedPorts; + @Getter(lazy=true) + private final InspectContainerResponse containerInfo = DockerClientFactory.instance().client().inspectContainerCmd(getContainerId()).exec(); + + ComposeServiceWaitStrategyTarget(Container container, GenericContainer proxyContainer, + @NonNull Map mappedPorts) { + this.container = container; + this.proxyContainer = proxyContainer; + this.mappedPorts = new HashMap<>(mappedPorts); + } + + /** + * {@inheritDoc} + */ + @Override + public List getExposedPorts() { + return new ArrayList<>(this.mappedPorts.keySet()); + } + + /** + * {@inheritDoc} + */ + @Override + public Integer getMappedPort(int originalPort) { + return this.proxyContainer.getMappedPort(this.mappedPorts.get(originalPort)); + } + + /** + * {@inheritDoc} + */ + @Override + public String getContainerIpAddress() { + return proxyContainer.getContainerIpAddress(); + } + + /** + * {@inheritDoc} + */ + @Override + public String getContainerId() { + return this.container.getId(); + } +} diff --git a/core/src/main/java/org/testcontainers/containers/Container.java b/core/src/main/java/org/testcontainers/containers/Container.java index 3b877dfde0d..2a4e260509c 100644 --- a/core/src/main/java/org/testcontainers/containers/Container.java +++ b/core/src/main/java/org/testcontainers/containers/Container.java @@ -1,15 +1,15 @@ package org.testcontainers.containers; import com.github.dockerjava.api.DockerClient; -import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.model.Bind; import com.github.dockerjava.api.model.Info; import lombok.NonNull; +import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.startupcheck.StartupCheckStrategy; import org.testcontainers.containers.traits.LinkableContainer; -import org.testcontainers.containers.wait.Wait; -import org.testcontainers.containers.wait.WaitStrategy; +import org.testcontainers.containers.wait.strategy.WaitStrategy; +import org.testcontainers.utility.LogUtils; import org.testcontainers.utility.MountableFile; import java.io.IOException; @@ -22,7 +22,7 @@ import java.util.function.Consumer; import java.util.function.Function; -public interface Container> extends LinkableContainer { +public interface Container> extends LinkableContainer, ContainerState { /** * @return a reference to this container instance, cast to the expected generic type. @@ -131,7 +131,7 @@ default void addFileSystemBind(final String hostPath, final String containerPath /** * Specify the {@link WaitStrategy} to use to determine if the container is ready. * - * @see Wait#defaultWaitStrategy() + * @see org.testcontainers.containers.wait.strategy.Wait#defaultWaitStrategy() * @param waitStrategy the WaitStrategy to use * @return this */ @@ -283,7 +283,7 @@ default SELF withClasspathResourceMapping(final String resourcePath, final Strin /** * Set the duration of waiting time until container treated as started. - * @see WaitStrategy#waitUntilReady(GenericContainer) + * @see WaitStrategy#waitUntilReady(org.testcontainers.containers.wait.strategy.WaitStrategyTarget) * * @param startupTimeout timeout * @return this @@ -297,13 +297,6 @@ default SELF withClasspathResourceMapping(final String resourcePath, final Strin */ SELF withPrivilegedMode(boolean mode); - /** - * Get the IP address that this container may be reached on (may not be the local machine). - * - * @return an IP address - */ - String getContainerIpAddress(); - /** * Only consider a container to have successfully started if it has been running for this duration. The default * value is null; if that's the value, ignore this check. @@ -327,33 +320,6 @@ default SELF withClasspathResourceMapping(final String resourcePath, final Strin */ SELF withWorkingDirectory(String workDir); - /** - * @return is the container currently running? - */ - Boolean isRunning(); - - /** - * Get the actual mapped port for a first port exposed by the container. - * - * @return the port that the exposed port is mapped to - * @throws IllegalStateException if there are no exposed ports - */ - default Integer getFirstMappedPort() { - return getExposedPorts() - .stream() - .findFirst() - .map(this::getMappedPort) - .orElseThrow(() -> new IllegalStateException("Container doesn't expose any ports")); - } - - /** - * Get the actual mapped port for a given port exposed by the container. - * - * @param originalPort the original TCP port that is exposed - * @return the port that the exposed port is mapped to, or null if it is not exposed - */ - Integer getMappedPort(int originalPort); - /** * Resolve Docker image and set it. * @@ -386,7 +352,9 @@ default Integer getFirstMappedPort() { * * @param consumer consumer that the frames should be sent to */ - void followOutput(Consumer consumer); + default void followOutput(Consumer consumer) { + LogUtils.followOutput(DockerClientFactory.instance().client(), getContainerId(), consumer); + } /** * Follow container output, sending each frame (usually, line) to a consumer. This method allows Stdout and/or stderr @@ -395,7 +363,9 @@ default Integer getFirstMappedPort() { * @param consumer consumer that the frames should be sent to * @param types types that should be followed (one or both of STDOUT, STDERR) */ - void followOutput(Consumer consumer, OutputFrame.OutputType... types); + default void followOutput(Consumer consumer, OutputFrame.OutputType... types) { + LogUtils.followOutput(DockerClientFactory.instance().client(), getContainerId(), consumer, types); + } /** @@ -419,7 +389,7 @@ default Integer getFirstMappedPort() { * Run a command inside a running container, as though using "docker exec", and interpreting * the output as UTF8. *

- * @see #execInContainer(Charset, String...) + * @see ExecInContainerPattern#execInContainer(com.github.dockerjava.api.command.InspectContainerResponse, String...) */ ExecResult execInContainer(String... command) throws UnsupportedOperationException, IOException, InterruptedException; @@ -427,14 +397,7 @@ ExecResult execInContainer(String... command) /** * Run a command inside a running container, as though using "docker exec". *

- * This functionality is not available on a docker daemon running the older "lxc" execution driver. At - * the time of writing, CircleCI was using this driver. - * @param outputCharset the character set used to interpret the output. - * @param command the parts of the command to run - * @return the result of execution - * @throws IOException if there's an issue communicating with Docker - * @throws InterruptedException if the thread waiting for the response is interrupted - * @throws UnsupportedOperationException if the docker daemon you're connecting to doesn't support "exec". + * @see ExecInContainerPattern#execInContainer(com.github.dockerjava.api.command.InspectContainerResponse, Charset, String...) */ ExecResult execInContainer(Charset outputCharset, String... command) throws UnsupportedOperationException, IOException, InterruptedException; @@ -460,8 +423,6 @@ ExecResult execInContainer(Charset outputCharset, String... command) */ void copyFileFromContainer(String containerPath, String destinationPath) throws IOException, InterruptedException; - List getExposedPorts(); - List getPortBindings(); List getExtraHosts(); @@ -496,17 +457,6 @@ ExecResult execInContainer(Charset outputCharset, String... command) @Deprecated Info getDockerDaemonInfo(); - String getContainerId(); - - String getContainerName(); - - /** - * - * @deprecated please use {@code org.testcontainers.DockerClientFactory.instance().client().inspectContainerCmd(container.getContainerId()).exec()} - */ - @Deprecated - InspectContainerResponse getContainerInfo(); - void setExposedPorts(List exposedPorts); void setPortBindings(List portBindings); diff --git a/core/src/main/java/org/testcontainers/containers/ContainerState.java b/core/src/main/java/org/testcontainers/containers/ContainerState.java new file mode 100644 index 00000000000..842a1cb6bde --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/ContainerState.java @@ -0,0 +1,114 @@ +package org.testcontainers.containers; + +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.exception.DockerException; +import com.github.dockerjava.api.model.ExposedPort; +import com.github.dockerjava.api.model.PortBinding; +import com.github.dockerjava.api.model.Ports; +import com.google.common.base.Preconditions; +import org.testcontainers.DockerClientFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public interface ContainerState { + + /** + * Get the IP address that this container may be reached on (may not be the local machine). + * + * @return an IP address + */ + default String getContainerIpAddress() { + return DockerClientFactory.instance().dockerHostIpAddress(); + } + + /** + * @return is the container currently running? + */ + default Boolean isRunning() { + try { + return getContainerId() != null && DockerClientFactory.instance().client().inspectContainerCmd(getContainerId()).exec().getState().getRunning(); + } catch (DockerException e) { + return false; + } + } + + /** + * Get the actual mapped port for a first port exposed by the container. + * + * @return the port that the exposed port is mapped to + * @throws IllegalStateException if there are no exposed ports + */ + default Integer getFirstMappedPort() { + return getExposedPorts() + .stream() + .findFirst() + .map(this::getMappedPort) + .orElseThrow(() -> new IllegalStateException("Container doesn't expose any ports")); + } + + /** + * Get the actual mapped port for a given port exposed by the container. + * + * @param originalPort the original TCP port that is exposed + * @return the port that the exposed port is mapped to, or null if it is not exposed + */ + default Integer getMappedPort(int originalPort) { + Preconditions.checkState(this.getContainerId() != null, "Mapped port can only be obtained after the container is started"); + + Ports.Binding[] binding = new Ports.Binding[0]; + final InspectContainerResponse containerInfo = this.getContainerInfo(); + if (containerInfo != null) { + binding = containerInfo.getNetworkSettings().getPorts().getBindings().get(new ExposedPort(originalPort)); + } + + if (binding != null && binding.length > 0 && binding[0] != null) { + return Integer.valueOf(binding[0].getHostPortSpec()); + } else { + throw new IllegalArgumentException("Requested port (" + originalPort + ") is not mapped"); + } + } + + /** + * @return the exposed ports + */ + List getExposedPorts(); + + /** + * @return the port bindings + */ + default List getPortBindings() { + List portBindings = new ArrayList<>(); + final Ports hostPortBindings = this.getContainerInfo().getHostConfig().getPortBindings(); + for (Map.Entry binding : hostPortBindings.getBindings().entrySet()) { + for (Ports.Binding portBinding : binding.getValue()) { + portBindings.add(String.format("%s:%s", portBinding.toString(), binding.getKey())); + } + } + return portBindings; + } + + /** + * @return the bound port numbers + */ + default List getBoundPortNumbers() { + return getPortBindings().stream() + .map(PortBinding::parse) + .map(PortBinding::getBinding) + .map(Ports.Binding::getHostPortSpec) + .map(Integer::valueOf) + .collect(Collectors.toList()); + } + + /** + * @return the id of the container + */ + String getContainerId(); + + /** + * @return the container info + */ + InspectContainerResponse getContainerInfo(); +} diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 9b0356b55ea..49f31448616 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -7,6 +7,7 @@ import com.google.common.base.Splitter; import com.google.common.collect.Maps; import com.google.common.util.concurrent.Uninterruptibles; +import lombok.NonNull; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.SystemUtils; import org.junit.runner.Description; @@ -14,9 +15,11 @@ import org.slf4j.LoggerFactory; import org.slf4j.profiler.Profiler; import org.testcontainers.DockerClientFactory; -import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.output.Slf4jLogConsumer; import org.testcontainers.containers.startupcheck.IndefiniteWaitOneShotStartupCheckStrategy; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.containers.wait.strategy.WaitAllStrategy; +import org.testcontainers.containers.wait.strategy.WaitStrategy; import org.testcontainers.utility.*; import org.zeroturnaround.exec.InvalidExitValueException; import org.zeroturnaround.exec.ProcessExecutor; @@ -26,8 +29,14 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.*; +import java.time.Duration; import java.util.AbstractMap.SimpleEntry; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -60,6 +69,8 @@ public class DockerComposeContainer> e private final AtomicInteger nextAmbassadorPort = new AtomicInteger(2000); private final Map> ambassadorPortMappings = new ConcurrentHashMap<>(); + private final Map serviceInstanceMap = new ConcurrentHashMap<>(); + private final Map waitStrategyMap = new ConcurrentHashMap<>(); private final SocatContainer ambassadorContainer = new SocatContainer(); private static final Object MUTEX = new Object(); @@ -112,10 +123,8 @@ public void starting(Description description) { } applyScaling(); // scale before up, so that all scaled instances are available first for linking createServices(); - if (tailChildContainers) { - tailChildContainerLogs(); - } startAmbassadorContainers(profiler); + waitUntilServiceStarted(); } } @@ -129,14 +138,34 @@ private void createServices() { runWithCompose("up -d"); } - private void tailChildContainerLogs() { - listChildContainers().forEach(container -> - LogUtils.followOutput(dockerClient, - container.getId(), - new Slf4jLogConsumer(logger()).withPrefix(container.getNames()[0]), - OutputFrame.OutputType.STDOUT, - OutputFrame.OutputType.STDERR) - ); + private void waitUntilServiceStarted() { + listChildContainers().forEach(this::createServiceInstance); + serviceInstanceMap.forEach(this::waitUntilServiceStarted); + } + + private void createServiceInstance(Container container) { + String serviceName = getServiceNameFromContainer(container); + final ComposeServiceWaitStrategyTarget containerInstance = new ComposeServiceWaitStrategyTarget(container, + ambassadorContainer, ambassadorPortMappings.getOrDefault(serviceName, new HashMap<>())); + + if (tailChildContainers) { + LogUtils.followOutput(DockerClientFactory.instance().client(), containerInstance.getContainerId(), + new Slf4jLogConsumer(logger()).withPrefix(container.getNames()[0])); + } + serviceInstanceMap.putIfAbsent(serviceName, containerInstance); + } + + private void waitUntilServiceStarted(String serviceName, ComposeServiceWaitStrategyTarget serviceInstance) { + final WaitAllStrategy waitAllStrategy = waitStrategyMap.get(serviceName); + if(waitAllStrategy != null) { + waitAllStrategy.waitUntilReady(serviceInstance); + } + } + + private String getServiceNameFromContainer(Container container) { + final String containerName = container.getLabels().get("com.docker.compose.service"); + final String containerNumber = container.getLabels().get("com.docker.compose.container-number"); + return String.format("%s_%s", containerName, containerNumber); } private void runWithCompose(String cmd) { @@ -227,10 +256,20 @@ public void finished(Description description) { } public SELF withExposedService(String serviceName, int servicePort) { + return withExposedService(serviceName, servicePort, Wait.defaultWaitStrategy()); + } - if (!serviceName.matches(".*_[0-9]+")) { - serviceName += "_1"; // implicit first instance of this service - } + public DockerComposeContainer withExposedService(String serviceName, int instance, int servicePort) { + return withExposedService(serviceName + "_" + instance, servicePort); + } + + public DockerComposeContainer withExposedService(String serviceName, int instance, int servicePort, WaitStrategy waitStrategy) { + return withExposedService(serviceName + "_" + instance, servicePort, waitStrategy); + } + + public SELF withExposedService(String serviceName, int servicePort, @NonNull WaitStrategy waitStrategy) { + + String serviceInstanceName = getServiceInstanceName(serviceName); /* * For every service/port pair that needs to be exposed, we register a target on an 'ambassador container'. @@ -248,14 +287,44 @@ public SELF withExposedService(String serviceName, int servicePort) { // Ambassador container will be started together after docker compose has started int ambassadorPort = nextAmbassadorPort.getAndIncrement(); - ambassadorPortMappings.computeIfAbsent(serviceName, __ -> new ConcurrentHashMap<>()).put(servicePort, ambassadorPort); - ambassadorContainer.withTarget(ambassadorPort, serviceName, servicePort); - ambassadorContainer.addLink(new FutureContainer(this.project + "_" + serviceName), serviceName); + ambassadorPortMappings.computeIfAbsent(serviceInstanceName, __ -> new ConcurrentHashMap<>()).put(servicePort, ambassadorPort); + ambassadorContainer.withTarget(ambassadorPort, serviceInstanceName, servicePort); + ambassadorContainer.addLink(new FutureContainer(this.project + "_" + serviceInstanceName), serviceInstanceName); + addWaitStrategy(serviceInstanceName, waitStrategy); return self(); } - public DockerComposeContainer withExposedService(String serviceName, int instance, int servicePort) { - return withExposedService(serviceName + "_" + instance, servicePort); + private String getServiceInstanceName(String serviceName) { + String serviceInstanceName = serviceName; + if (!serviceInstanceName.matches(".*_[0-9]+")) { + serviceInstanceName += "_1"; // implicit first instance of this service + } + return serviceInstanceName; + } + + /* + * can have multiple wait strategies for a single container, e.g. if waiting on several ports + * if no wait strategy is defined, the WaitAllStrategy will return immediately. + * The WaitAllStrategy uses an long timeout, because timeouts should be handled by the inner strategies. + */ + private void addWaitStrategy(String serviceInstanceName, @NonNull WaitStrategy waitStrategy) { + final WaitAllStrategy waitAllStrategy = waitStrategyMap.computeIfAbsent(serviceInstanceName, __ -> + (WaitAllStrategy) new WaitAllStrategy().withStartupTimeout(Duration.ofMinutes(30))); + waitAllStrategy.withStrategy(waitStrategy); + } + + /** + Specify the {@link WaitStrategy} to use to determine if the container is ready. + * + * @see org.testcontainers.containers.wait.strategy.Wait#defaultWaitStrategy() + * @param serviceName the name of the service to wait for + * @param waitStrategy the WaitStrategy to use + * @return this + */ + public SELF waitingFor(String serviceName, @NonNull WaitStrategy waitStrategy) { + String serviceInstanceName = getServiceInstanceName(serviceName); + addWaitStrategy(serviceInstanceName, waitStrategy); + return self(); } /** diff --git a/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java b/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java new file mode 100644 index 00000000000..976ec4a54f0 --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java @@ -0,0 +1,102 @@ +package org.testcontainers.containers; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.command.ExecCreateCmdResponse; +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.exception.DockerException; +import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; +import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.output.FrameConsumerResultCallback; +import org.testcontainers.containers.output.OutputFrame; +import org.testcontainers.containers.output.ToStringConsumer; +import org.testcontainers.utility.TestEnvironment; + +import java.io.IOException; +import java.nio.charset.Charset; + +/** + * Provides utility methods for executing commands in containers + */ +@UtilityClass +@Slf4j +public class ExecInContainerPattern { + + /** + * Run a command inside a running container, as though using "docker exec", and interpreting + * the output as UTF8. + *

+ * @param containerInfo the container info + * @param command the command to execute + * @see #execInContainer(InspectContainerResponse, Charset, String...) + */ + public Container.ExecResult execInContainer(InspectContainerResponse containerInfo, String... command) + throws UnsupportedOperationException, IOException, InterruptedException { + return execInContainer(containerInfo, Charset.forName("UTF-8"), command); + } + + /** + * Run a command inside a running container, as though using "docker exec". + *

+ * This functionality is not available on a docker daemon running the older "lxc" execution driver. At + * the time of writing, CircleCI was using this driver. + * @param containerInfo the container info + * @param outputCharset the character set used to interpret the output. + * @param command the parts of the command to run + * @return the result of execution + * @throws IOException if there's an issue communicating with Docker + * @throws InterruptedException if the thread waiting for the response is interrupted + * @throws UnsupportedOperationException if the docker daemon you're connecting to doesn't support "exec". + */ + public Container.ExecResult execInContainer(InspectContainerResponse containerInfo, Charset outputCharset, String... command) + throws UnsupportedOperationException, IOException, InterruptedException { + if (!TestEnvironment.dockerExecutionDriverSupportsExec()) { + // at time of writing, this is the expected result in CircleCI. + throw new UnsupportedOperationException( + "Your docker daemon is running the \"lxc\" driver, which doesn't support \"docker exec\"."); + + } + + if (!isRunning(containerInfo)) { + throw new IllegalStateException("execInContainer can only be used while the Container is running"); + } + + String containerId = containerInfo.getId(); + String containerName = containerInfo.getName(); + + DockerClient dockerClient = DockerClientFactory.instance().client(); + + dockerClient + .execCreateCmd(containerId) + .withCmd(command); + + log.debug("{}: Running \"exec\" command: {}", containerName, String.join(" ", command)); + final ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(containerId) + .withAttachStdout(true).withAttachStderr(true).withCmd(command).exec(); + + final ToStringConsumer stdoutConsumer = new ToStringConsumer(); + final ToStringConsumer stderrConsumer = new ToStringConsumer(); + + FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); + callback.addConsumer(OutputFrame.OutputType.STDOUT, stdoutConsumer); + callback.addConsumer(OutputFrame.OutputType.STDERR, stderrConsumer); + + dockerClient.execStartCmd(execCreateCmdResponse.getId()).exec(callback).awaitCompletion(); + + final Container.ExecResult result = new Container.ExecResult( + stdoutConsumer.toString(outputCharset), + stderrConsumer.toString(outputCharset)); + + log.trace("{}: stdout: {}", containerName, result.getStdout()); + log.trace("{}: stderr: {}", containerName, result.getStderr()); + return result; + } + + private boolean isRunning(InspectContainerResponse containerInfo) { + try { + return containerInfo != null && containerInfo.getState().getRunning(); + } catch (DockerException e) { + return false; + } + } +} diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 859b452ea71..86949d755a4 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -2,13 +2,20 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.CreateContainerCmd; -import com.github.dockerjava.api.command.ExecCreateCmdResponse; import com.github.dockerjava.api.command.InspectContainerResponse; -import com.github.dockerjava.api.exception.DockerException; -import com.github.dockerjava.api.model.*; -import com.google.common.base.Preconditions; +import com.github.dockerjava.api.model.Bind; +import com.github.dockerjava.api.model.ExposedPort; +import com.github.dockerjava.api.model.Info; +import com.github.dockerjava.api.model.Link; +import com.github.dockerjava.api.model.PortBinding; +import com.github.dockerjava.api.model.Volume; +import com.github.dockerjava.api.model.VolumesFrom; import com.google.common.base.Strings; -import lombok.*; +import lombok.AccessLevel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NonNull; +import lombok.Setter; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.utils.IOUtils; import org.jetbrains.annotations.NotNull; @@ -23,13 +30,13 @@ import org.testcontainers.containers.output.FrameConsumerResultCallback; import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.output.Slf4jLogConsumer; -import org.testcontainers.containers.output.ToStringConsumer; import org.testcontainers.containers.startupcheck.IsRunningStartupCheckStrategy; import org.testcontainers.containers.startupcheck.MinimumDurationRunningStartupCheckStrategy; import org.testcontainers.containers.startupcheck.StartupCheckStrategy; import org.testcontainers.containers.traits.LinkableContainer; import org.testcontainers.containers.wait.Wait; import org.testcontainers.containers.wait.WaitStrategy; +import org.testcontainers.containers.wait.strategy.WaitStrategyTarget; import org.testcontainers.images.RemoteDockerImage; import org.testcontainers.utility.*; @@ -39,7 +46,16 @@ import java.nio.charset.Charset; import java.nio.file.Path; import java.time.Duration; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -59,7 +75,7 @@ @EqualsAndHashCode(callSuper = false) public class GenericContainer> extends FailureDetectingExternalResource - implements Container, AutoCloseable { + implements Container, AutoCloseable, WaitStrategyTarget { private static final Charset UTF8 = Charset.forName("UTF-8"); @@ -140,15 +156,14 @@ public class GenericContainer> @Setter(AccessLevel.NONE) protected String containerName; + @Setter(AccessLevel.NONE) + private InspectContainerResponse containerInfo; + /** * The approach to determine if the container is ready. */ @NonNull - protected WaitStrategy waitStrategy = Wait.defaultWaitStrategy(); - - @Nullable - @Setter(AccessLevel.NONE) - private InspectContainerResponse containerInfo; + protected org.testcontainers.containers.wait.strategy.WaitStrategy waitStrategy = Wait.defaultWaitStrategy(); private List> logConsumers = new ArrayList<>(); @@ -343,13 +358,13 @@ protected Integer getLivenessCheckPort() { /** * @return the ports on which to check if the container is ready + * @deprecated use {@link #getLivenessCheckPortNumbers()} instead */ @NotNull @NonNull + @Deprecated protected Set getLivenessCheckPorts() { - final Set result = new HashSet<>(); - result.addAll(getExposedPortNumbers()); - result.addAll(getBoundPortNumbers()); + final Set result = WaitStrategyTarget.super.getLivenessCheckPortNumbers(); // for backwards compatibility if (this.getLivenessCheckPort() != null) { @@ -359,19 +374,9 @@ protected Set getLivenessCheckPorts() { return result; } - private List getExposedPortNumbers() { - return exposedPorts.stream() - .map(this::getMappedPort) - .collect(Collectors.toList()); - } - - private List getBoundPortNumbers() { - return portBindings.stream() - .map(PortBinding::parse) - .map(PortBinding::getBinding) - .map(Ports.Binding::getHostPortSpec) - .map(Integer::valueOf) - .collect(Collectors.toList()); + @Override + public Set getLivenessCheckPortNumbers() { + return this.getLivenessCheckPorts(); } private void applyConfiguration(CreateContainerCmd createCommand) { @@ -495,7 +500,7 @@ private Set findAllNetworksForLinkedContainers(LinkableContainer linkabl * {@inheritDoc} */ @Override - public SELF waitingFor(@NonNull WaitStrategy waitStrategy) { + public SELF waitingFor(@NonNull org.testcontainers.containers.wait.strategy.WaitStrategy waitStrategy) { this.waitStrategy = waitStrategy; return self(); } @@ -506,16 +511,21 @@ public SELF waitingFor(@NonNull WaitStrategy waitStrategy) { * * @return the {@link WaitStrategy} to use */ - protected WaitStrategy getWaitStrategy() { + protected org.testcontainers.containers.wait.strategy.WaitStrategy getWaitStrategy() { return waitStrategy; } + @Override + public void setWaitStrategy(org.testcontainers.containers.wait.strategy.WaitStrategy waitStrategy) { + this.waitStrategy = waitStrategy; + } + /** * Wait until the container has started. The default implementation simply * waits for a port to start listening; other implementations are available - * as implementations of {@link WaitStrategy} + * as implementations of {@link org.testcontainers.containers.wait.strategy.WaitStrategy} * - * @see #waitingFor(WaitStrategy) + * @see #waitingFor(org.testcontainers.containers.wait.strategy.WaitStrategy) */ protected void waitUntilContainerStarted() { getWaitStrategy().waitUntilReady(this); @@ -770,14 +780,6 @@ public SELF withPrivilegedMode(boolean mode) { return self(); } - /** - * {@inheritDoc} - */ - @Override - public String getContainerIpAddress() { - return DockerClientFactory.instance().dockerHostIpAddress(); - } - /** * {@inheritDoc} */ @@ -816,38 +818,6 @@ public String getIpAddress() { return getContainerIpAddress(); } - /** - * {@inheritDoc} - */ - @Override - public Boolean isRunning() { - try { - return containerId != null && dockerClient.inspectContainerCmd(containerId).exec().getState().getRunning(); - } catch (DockerException e) { - return false; - } - } - - /** - * {@inheritDoc} - */ - @Override - public Integer getMappedPort(final int originalPort) { - - Preconditions.checkState(containerId != null, "Mapped port can only be obtained after the container is started"); - - Ports.Binding[] binding = new Ports.Binding[0]; - if (containerInfo != null) { - binding = containerInfo.getNetworkSettings().getPorts().getBindings().get(new ExposedPort(originalPort)); - } - - if (binding != null && binding.length > 0 && binding[0] != null) { - return Integer.valueOf(binding[0].getHostPortSpec()); - } else { - throw new IllegalArgumentException("Requested port (" + originalPort + ") is not mapped"); - } - } - /** * {@inheritDoc} */ @@ -904,22 +874,6 @@ public String getTestHostIpAddress() { } } - /** - * {@inheritDoc} - */ - @Override - public void followOutput(Consumer consumer) { - this.followOutput(consumer, OutputFrame.OutputType.STDOUT, OutputFrame.OutputType.STDERR); - } - - /** - * {@inheritDoc} - */ - @Override - public void followOutput(Consumer consumer, OutputFrame.OutputType... types) { - LogUtils.followOutput(dockerClient, containerId, consumer, types); - } - /** * {@inheritDoc} */ @@ -993,42 +947,7 @@ public void copyFileFromContainer(String containerPath, String destinationPath) @Override public ExecResult execInContainer(Charset outputCharset, String... command) throws UnsupportedOperationException, IOException, InterruptedException { - - if (!TestEnvironment.dockerExecutionDriverSupportsExec()) { - // at time of writing, this is the expected result in CircleCI. - throw new UnsupportedOperationException( - "Your docker daemon is running the \"lxc\" driver, which doesn't support \"docker exec\"."); - - } - - if (!isRunning()) { - throw new IllegalStateException("execInContainer can only be used while the Container is running"); - } - - this.dockerClient - .execCreateCmd(this.containerId) - .withCmd(command); - - logger().debug("Running \"exec\" command: " + String.join(" ", command)); - final ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(this.containerId) - .withAttachStdout(true).withAttachStderr(true).withCmd(command).exec(); - - final ToStringConsumer stdoutConsumer = new ToStringConsumer(); - final ToStringConsumer stderrConsumer = new ToStringConsumer(); - - FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); - callback.addConsumer(OutputFrame.OutputType.STDOUT, stdoutConsumer); - callback.addConsumer(OutputFrame.OutputType.STDERR, stderrConsumer); - - dockerClient.execStartCmd(execCreateCmdResponse.getId()).exec(callback).awaitCompletion(); - - final ExecResult result = new ExecResult( - stdoutConsumer.toString(outputCharset), - stderrConsumer.toString(outputCharset)); - - logger().trace("stdout: " + result.getStdout()); - logger().trace("stderr: " + result.getStderr()); - return result; + return ExecInContainerPattern.execInContainer(getContainerInfo(), outputCharset, command); } /** @@ -1062,8 +981,11 @@ public SELF withCreateContainerCmdModifier(Consumer modifier /** * Convenience class with access to non-public members of GenericContainer. + * + * @deprecated use {@link org.testcontainers.containers.wait.strategy.AbstractWaitStrategy} */ - public static abstract class AbstractWaitStrategy implements WaitStrategy { + @Deprecated + public static abstract class AbstractWaitStrategy extends org.testcontainers.containers.wait.strategy.AbstractWaitStrategy implements WaitStrategy { protected GenericContainer container; @NonNull diff --git a/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java index b64535503c0..e208b80345d 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java @@ -1,61 +1,31 @@ package org.testcontainers.containers.wait; import lombok.extern.slf4j.Slf4j; -import org.rnorth.ducttape.TimeoutException; -import org.rnorth.ducttape.unreliables.Unreliables; -import org.testcontainers.containers.ContainerLaunchException; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.wait.internal.ExternalPortListeningCheck; -import org.testcontainers.containers.wait.internal.InternalCommandPortListeningCheck; -import java.util.List; -import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; +import java.time.Duration; /** * Waits until a socket connection can be established on a port exposed or mapped by the container. * * @author richardnorth + * + * @deprecated Use {@link org.testcontainers.containers.wait.strategy.HostPortWaitStrategy} */ +@Deprecated @Slf4j public class HostPortWaitStrategy extends GenericContainer.AbstractWaitStrategy { + private org.testcontainers.containers.wait.strategy.HostPortWaitStrategy delegateStrategy = new org.testcontainers.containers.wait.strategy.HostPortWaitStrategy(); + @Override protected void waitUntilReady() { - final Set externalLivenessCheckPorts = getLivenessCheckPorts(); - if (externalLivenessCheckPorts.isEmpty()) { - log.debug("Liveness check ports of {} is empty. Not waiting.", container.getContainerName()); - return; - } - - @SuppressWarnings("unchecked") - List exposedPorts = container.getExposedPorts(); - - final Set internalPorts = getInternalPorts(externalLivenessCheckPorts, exposedPorts); - - Callable internalCheck = new InternalCommandPortListeningCheck(container, internalPorts); - - Callable externalCheck = new ExternalPortListeningCheck(container, externalLivenessCheckPorts); - - try { - Unreliables.retryUntilTrue((int) startupTimeout.getSeconds(), TimeUnit.SECONDS, () -> { - return getRateLimiter().getWhenReady(() -> internalCheck.call() && externalCheck.call()); - }); - - } catch (TimeoutException e) { - throw new ContainerLaunchException("Timed out waiting for container port to open (" + - container.getContainerIpAddress() + - " ports: " + - externalLivenessCheckPorts + - " should be listening)"); - } + delegateStrategy.waitUntilReady(this.waitStrategyTarget); } - private Set getInternalPorts(Set externalLivenessCheckPorts, List exposedPorts) { - return exposedPorts.stream() - .filter(it -> externalLivenessCheckPorts.contains(container.getMappedPort(it))) - .collect(Collectors.toSet()); + @Override + public WaitStrategy withStartupTimeout(Duration startupTimeout) { + delegateStrategy.withStartupTimeout(startupTimeout); + return super.withStartupTimeout(startupTimeout); } } diff --git a/core/src/main/java/org/testcontainers/containers/wait/HttpWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/HttpWaitStrategy.java index bc0aacb00ce..d27133c499d 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/HttpWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/HttpWaitStrategy.java @@ -1,44 +1,20 @@ package org.testcontainers.containers.wait; -import com.google.common.base.Strings; -import com.google.common.io.BaseEncoding; -import org.rnorth.ducttape.TimeoutException; -import org.testcontainers.containers.ContainerLaunchException; import org.testcontainers.containers.GenericContainer; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.URI; -import java.net.URL; -import java.util.concurrent.TimeUnit; import java.util.function.Predicate; -import static org.rnorth.ducttape.unreliables.Unreliables.retryUntilSuccess; - /** * Waits until an HTTP(S) endpoint returns a given status code. * * @author Pete Cornish {@literal } + * + * @deprecated Use {@link org.testcontainers.containers.wait.strategy.HttpWaitStrategy} */ +@Deprecated public class HttpWaitStrategy extends GenericContainer.AbstractWaitStrategy { - /** - * Authorization HTTP header. - */ - private static final String HEADER_AUTHORIZATION = "Authorization"; - /** - * Basic Authorization scheme prefix. - */ - private static final String AUTH_BASIC = "Basic "; - - private String path = "/"; - private int statusCode = HttpURLConnection.HTTP_OK; - private boolean tlsEnabled; - private String username; - private String password; - private Predicate responsePredicate; + private org.testcontainers.containers.wait.strategy.HttpWaitStrategy delegateStrategy = new org.testcontainers.containers.wait.strategy.HttpWaitStrategy(); /** * Waits for the given status code. @@ -47,7 +23,7 @@ public class HttpWaitStrategy extends GenericContainer.AbstractWaitStrategy { * @return this */ public HttpWaitStrategy forStatusCode(int statusCode) { - this.statusCode = statusCode; + delegateStrategy.forStatusCode(statusCode); return this; } @@ -58,7 +34,7 @@ public HttpWaitStrategy forStatusCode(int statusCode) { * @return this */ public HttpWaitStrategy forPath(String path) { - this.path = path; + delegateStrategy.forPath(path); return this; } @@ -68,7 +44,7 @@ public HttpWaitStrategy forPath(String path) { * @return this */ public HttpWaitStrategy usingTls() { - this.tlsEnabled = true; + delegateStrategy.usingTls(); return this; } @@ -80,8 +56,7 @@ public HttpWaitStrategy usingTls() { * @return this */ public HttpWaitStrategy withBasicCredentials(String username, String password) { - this.username = username; - this.password = password; + delegateStrategy.withBasicCredentials(username, password); return this; } @@ -91,105 +66,12 @@ public HttpWaitStrategy withBasicCredentials(String username, String password) { * @return this */ public HttpWaitStrategy forResponsePredicate(Predicate responsePredicate) { - this.responsePredicate = responsePredicate; + delegateStrategy.forResponsePredicate(responsePredicate); return this; } @Override protected void waitUntilReady() { - final Integer livenessCheckPort = getLivenessCheckPort(); - if (null == livenessCheckPort) { - logger().warn("No exposed ports or mapped ports - cannot wait for status"); - return; - } - - final String uri = buildLivenessUri(livenessCheckPort).toString(); - logger().info("Waiting for {} seconds for URL: {}", startupTimeout.getSeconds(), uri); - - // try to connect to the URL - try { - retryUntilSuccess((int) startupTimeout.getSeconds(), TimeUnit.SECONDS, () -> { - getRateLimiter().doWhenReady(() -> { - try { - final HttpURLConnection connection = (HttpURLConnection) new URL(uri).openConnection(); - - // authenticate - if (!Strings.isNullOrEmpty(username)) { - connection.setRequestProperty(HEADER_AUTHORIZATION, buildAuthString(username, password)); - connection.setUseCaches(false); - } - - connection.setRequestMethod("GET"); - connection.connect(); - - if (statusCode != connection.getResponseCode()) { - throw new RuntimeException(String.format("HTTP response code was: %s", - connection.getResponseCode())); - } - - if(responsePredicate != null) { - String responseBody = getResponseBody(connection); - if(!responsePredicate.test(responseBody)) { - throw new RuntimeException(String.format("Response: %s did not match predicate", - responseBody)); - } - } - - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - return true; - }); - - } catch (TimeoutException e) { - throw new ContainerLaunchException(String.format( - "Timed out waiting for URL to be accessible (%s should return HTTP %s)", uri, statusCode)); - } - } - - /** - * Build the URI on which to check if the container is ready. - * - * @param livenessCheckPort the liveness port - * @return the liveness URI - */ - private URI buildLivenessUri(int livenessCheckPort) { - final String scheme = (tlsEnabled ? "https" : "http") + "://"; - final String host = container.getContainerIpAddress(); - - final String portSuffix; - if ((tlsEnabled && 443 == livenessCheckPort) || (!tlsEnabled && 80 == livenessCheckPort)) { - portSuffix = ""; - } else { - portSuffix = ":" + String.valueOf(livenessCheckPort); - } - - return URI.create(scheme + host + portSuffix + path); - } - - /** - * @param username the username - * @param password the password - * @return a basic authentication string for the given credentials - */ - private String buildAuthString(String username, String password) { - return AUTH_BASIC + BaseEncoding.base64().encode((username + ":" + password).getBytes()); - } - - private String getResponseBody(HttpURLConnection connection) throws IOException { - BufferedReader reader; - if (200 <= connection.getResponseCode() && connection.getResponseCode() <= 299) { - reader = new BufferedReader(new InputStreamReader((connection.getInputStream()))); - } else { - reader = new BufferedReader(new InputStreamReader((connection.getErrorStream()))); - } - - StringBuilder builder = new StringBuilder(); - String line; - while ((line = reader.readLine()) != null) { - builder.append(line); - } - return builder.toString(); + delegateStrategy.waitUntilReady(this.waitStrategyTarget); } } diff --git a/core/src/main/java/org/testcontainers/containers/wait/LogMessageWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/LogMessageWaitStrategy.java index 404731136d2..a21b6b66a59 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/LogMessageWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/LogMessageWaitStrategy.java @@ -1,44 +1,29 @@ package org.testcontainers.containers.wait; -import org.testcontainers.containers.ContainerLaunchException; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.output.OutputFrame; -import org.testcontainers.containers.output.WaitingConsumer; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.function.Predicate; /** * Waits until containers logs expected content. + * + * @deprecated Use {@link org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy} */ +@Deprecated public class LogMessageWaitStrategy extends GenericContainer.AbstractWaitStrategy { - private String regEx; - private int times = 1; + private org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy delegateWaitStrategy = new org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy(); @Override protected void waitUntilReady() { - WaitingConsumer waitingConsumer = new WaitingConsumer(); - container.followOutput(waitingConsumer); - - Predicate waitPredicate = outputFrame -> - outputFrame.getUtf8String().matches(regEx); - - try { - waitingConsumer.waitUntil(waitPredicate, startupTimeout.getSeconds(), TimeUnit.SECONDS, times); - } catch (TimeoutException e) { - throw new ContainerLaunchException("Timed out waiting for log output matching '" + regEx + "'"); - } + delegateWaitStrategy.waitUntilReady(this.waitStrategyTarget); } public LogMessageWaitStrategy withRegEx(String regEx) { - this.regEx = regEx; + delegateWaitStrategy.withRegEx(regEx); return this; } public LogMessageWaitStrategy withTimes(int times) { - this.times = times; + delegateWaitStrategy.withTimes(times); return this; } } diff --git a/core/src/main/java/org/testcontainers/containers/wait/Wait.java b/core/src/main/java/org/testcontainers/containers/wait/Wait.java index 4a6717df36b..ac96d0b7de5 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/Wait.java +++ b/core/src/main/java/org/testcontainers/containers/wait/Wait.java @@ -6,7 +6,10 @@ * Convenience class with logic for building common {@link WaitStrategy} instances. * * @author Pete Cornish {@literal } + * + * @deprecated Use {@link org.testcontainers.containers.wait.strategy.Wait} */ +@Deprecated public class Wait { /** * Convenience method to return the default WaitStrategy. @@ -51,4 +54,15 @@ public static HttpWaitStrategy forHttps(String path) { return forHttp(path) .usingTls(); } + + /** + * Convenience method to return a WaitStrategy for log messages. + * + * @param regex the regex pattern to check for + * @param times the number of times the pattern is expected + * @return LogMessageWaitStrategy + */ + public static LogMessageWaitStrategy forLogMessage(String regex, int times) { + return new LogMessageWaitStrategy().withRegEx(regex).withTimes(times); + } } diff --git a/core/src/main/java/org/testcontainers/containers/wait/WaitAllStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/WaitAllStrategy.java index b4bf27ef98b..7e3032cd76d 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/WaitAllStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/WaitAllStrategy.java @@ -10,7 +10,10 @@ /** * Wait strategy that waits for a number of other strategies to pass in series. + * + * @deprecated Use {@link org.testcontainers.containers.wait.strategy.WaitAllStrategy} */ +@Deprecated public class WaitAllStrategy implements WaitStrategy { private final List strategies = new ArrayList<>(); diff --git a/core/src/main/java/org/testcontainers/containers/wait/WaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/WaitStrategy.java index 871c314a88d..ee2c14d90ae 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/WaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/WaitStrategy.java @@ -1,6 +1,7 @@ package org.testcontainers.containers.wait; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.WaitStrategyTarget; import java.time.Duration; @@ -8,18 +9,30 @@ * Approach to determine whether a container is ready. * * @author Pete Cornish {@literal } + * + * @deprecated Use {@link org.testcontainers.containers.wait.strategy.WaitStrategy} */ -public interface WaitStrategy { +@Deprecated +public interface WaitStrategy extends org.testcontainers.containers.wait.strategy.WaitStrategy { /** * Wait until the container has started. * * @param container the container for which to wait */ - void waitUntilReady(GenericContainer container); + default void waitUntilReady(GenericContainer container) { + this.waitUntilReady((WaitStrategyTarget)container); + } /** * @param startupTimeout the duration for which to wait * @return this */ WaitStrategy withStartupTimeout(Duration startupTimeout); + + /** + * {@inheritDoc} + */ + default void waitUntilReady(WaitStrategyTarget waitStrategyTarget) { + //default method for backwards compatibility + } } diff --git a/core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java b/core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java index 65f6182b2bc..3283880b569 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java +++ b/core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java @@ -1,7 +1,7 @@ package org.testcontainers.containers.wait.internal; import lombok.RequiredArgsConstructor; -import org.testcontainers.containers.Container; +import org.testcontainers.containers.ContainerState; import java.io.IOException; import java.net.Socket; @@ -13,12 +13,12 @@ */ @RequiredArgsConstructor public class ExternalPortListeningCheck implements Callable { - private final Container container; + private final ContainerState containerState; private final Set externalLivenessCheckPorts; @Override public Boolean call() { - String address = container.getContainerIpAddress(); + String address = containerState.getContainerIpAddress(); for (Integer externalPort : externalLivenessCheckPorts) { try { diff --git a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java index 555bfe81437..2cf7092f1ae 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java +++ b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java @@ -1,7 +1,8 @@ package org.testcontainers.containers.wait.internal; import lombok.RequiredArgsConstructor; -import org.testcontainers.containers.Container; +import org.testcontainers.containers.ExecInContainerPattern; +import org.testcontainers.containers.wait.strategy.WaitStrategyTarget; import java.util.Set; @@ -15,7 +16,7 @@ public class InternalCommandPortListeningCheck implements java.util.concurrent.C private static final String SUCCESS_MARKER = "TESTCONTAINERS_SUCCESS"; - private final Container container; + private final WaitStrategyTarget waitStrategyTarget; private final Set internalPorts; @Override @@ -36,7 +37,7 @@ private void tryPort(Integer internalPort) { for (String[] command : commands) { try { - if (container.execInContainer(command).getStdout().contains(SUCCESS_MARKER)) { + if (ExecInContainerPattern.execInContainer(waitStrategyTarget.getContainerInfo(), command).getStdout().contains(SUCCESS_MARKER)) { return; } } catch (Exception e) { diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/AbstractWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/AbstractWaitStrategy.java new file mode 100644 index 00000000000..e0d87578e98 --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/AbstractWaitStrategy.java @@ -0,0 +1,65 @@ +package org.testcontainers.containers.wait.strategy; + +import lombok.NonNull; +import org.rnorth.ducttape.ratelimits.RateLimiter; +import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; + +import java.time.Duration; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +public abstract class AbstractWaitStrategy implements WaitStrategy { + + protected WaitStrategyTarget waitStrategyTarget; + + @NonNull + protected Duration startupTimeout = Duration.ofSeconds(60); + + private static final RateLimiter DOCKER_CLIENT_RATE_LIMITER = RateLimiterBuilder + .newBuilder() + .withRate(1, TimeUnit.SECONDS) + .withConstantThroughput() + .build(); + + /** + * Wait until the target has started. + * + * @param waitStrategyTarget the target of the WaitStrategy + */ + @Override + public void waitUntilReady(WaitStrategyTarget waitStrategyTarget) { + this.waitStrategyTarget = waitStrategyTarget; + waitUntilReady(); + } + + /** + * Wait until {@link #waitStrategyTarget} has started. + */ + protected abstract void waitUntilReady(); + + /** + * Set the duration of waiting time until container treated as started. + * + * @param startupTimeout timeout + * @return this + * @see WaitStrategy#waitUntilReady(WaitStrategyTarget) + */ + public WaitStrategy withStartupTimeout(Duration startupTimeout) { + this.startupTimeout = startupTimeout; + return this; + } + + /** + * @return the ports on which to check if the container is ready + */ + protected Set getLivenessCheckPorts() { + return waitStrategyTarget.getLivenessCheckPortNumbers(); + } + + /** + * @return the rate limiter to use + */ + protected RateLimiter getRateLimiter() { + return DOCKER_CLIENT_RATE_LIMITER; + } +} diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/HostPortWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/HostPortWaitStrategy.java new file mode 100644 index 00000000000..ac506b4ed3b --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/HostPortWaitStrategy.java @@ -0,0 +1,59 @@ +package org.testcontainers.containers.wait.strategy; + +import lombok.extern.slf4j.Slf4j; +import org.rnorth.ducttape.TimeoutException; +import org.rnorth.ducttape.unreliables.Unreliables; +import org.testcontainers.containers.ContainerLaunchException; +import org.testcontainers.containers.wait.internal.ExternalPortListeningCheck; +import org.testcontainers.containers.wait.internal.InternalCommandPortListeningCheck; + +import java.util.List; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * Waits until a socket connection can be established on a port exposed or mapped by the container. + * + * @author richardnorth + */ +@Slf4j +public class HostPortWaitStrategy extends AbstractWaitStrategy { + + @Override + protected void waitUntilReady() { + final Set externalLivenessCheckPorts = getLivenessCheckPorts(); + if (externalLivenessCheckPorts.isEmpty()) { + log.debug("Liveness check ports of {} is empty. Not waiting.", waitStrategyTarget.getContainerInfo().getName()); + return; + } + + @SuppressWarnings("unchecked") + List exposedPorts = waitStrategyTarget.getExposedPorts(); + + final Set internalPorts = getInternalPorts(externalLivenessCheckPorts, exposedPorts); + + Callable internalCheck = new InternalCommandPortListeningCheck(waitStrategyTarget, internalPorts); + + Callable externalCheck = new ExternalPortListeningCheck(waitStrategyTarget, externalLivenessCheckPorts); + + try { + Unreliables.retryUntilTrue((int) startupTimeout.getSeconds(), TimeUnit.SECONDS, + () -> getRateLimiter().getWhenReady(() -> internalCheck.call() && externalCheck.call())); + + } catch (TimeoutException e) { + throw new ContainerLaunchException("Timed out waiting for container port to open (" + + waitStrategyTarget.getContainerIpAddress() + + " ports: " + + externalLivenessCheckPorts + + " should be listening)"); + } + } + + private Set getInternalPorts(Set externalLivenessCheckPorts, List exposedPorts) { + return exposedPorts.stream() + .filter(it -> externalLivenessCheckPorts.contains(waitStrategyTarget.getMappedPort(it))) + .collect(Collectors.toSet()); + } +} diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java new file mode 100644 index 00000000000..bf63e9708d5 --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java @@ -0,0 +1,195 @@ +package org.testcontainers.containers.wait.strategy; + +import com.google.common.base.Strings; +import com.google.common.io.BaseEncoding; +import lombok.extern.slf4j.Slf4j; +import org.rnorth.ducttape.TimeoutException; +import org.testcontainers.containers.ContainerLaunchException; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URL; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; + +import static org.rnorth.ducttape.unreliables.Unreliables.retryUntilSuccess; + +@Slf4j +public class HttpWaitStrategy extends AbstractWaitStrategy { + + /** + * Authorization HTTP header. + */ + private static final String HEADER_AUTHORIZATION = "Authorization"; + + /** + * Basic Authorization scheme prefix. + */ + private static final String AUTH_BASIC = "Basic "; + + private String path = "/"; + private int statusCode = HttpURLConnection.HTTP_OK; + private boolean tlsEnabled; + private String username; + private String password; + private Predicate responsePredicate; + + /** + * Waits for the given status code. + * + * @param statusCode the expected status code + * @return this + */ + public HttpWaitStrategy forStatusCode(int statusCode) { + this.statusCode = statusCode; + return this; + } + + /** + * Waits for the given path. + * + * @param path the path to check + * @return this + */ + public HttpWaitStrategy forPath(String path) { + this.path = path; + return this; + } + + /** + * Indicates that the status check should use HTTPS. + * + * @return this + */ + public HttpWaitStrategy usingTls() { + this.tlsEnabled = true; + return this; + } + + /** + * Authenticate with HTTP Basic Authorization credentials. + * + * @param username the username + * @param password the password + * @return this + */ + public HttpWaitStrategy withBasicCredentials(String username, String password) { + this.username = username; + this.password = password; + return this; + } + + /** + * Waits for the response to pass the given predicate + * @param responsePredicate The predicate to test the response against + * @return this + */ + public HttpWaitStrategy forResponsePredicate(Predicate responsePredicate) { + this.responsePredicate = responsePredicate; + return this; + } + + @Override + protected void waitUntilReady() { + final String containerName = waitStrategyTarget.getContainerInfo().getName(); + final Set livenessCheckPorts = getLivenessCheckPorts(); + if (livenessCheckPorts == null || livenessCheckPorts.isEmpty()) { + log.warn("{}: No exposed ports or mapped ports - cannot wait for status", containerName); + return; + } + + final Integer livenessCheckPort = livenessCheckPorts.iterator().next(); + final String uri = buildLivenessUri(livenessCheckPort).toString(); + log.info("{}: Waiting for {} seconds for URL: {}", containerName, startupTimeout.getSeconds(), uri); + + // try to connect to the URL + try { + retryUntilSuccess((int) startupTimeout.getSeconds(), TimeUnit.SECONDS, () -> { + getRateLimiter().doWhenReady(() -> { + try { + final HttpURLConnection connection = (HttpURLConnection) new URL(uri).openConnection(); + + // authenticate + if (!Strings.isNullOrEmpty(username)) { + connection.setRequestProperty(HEADER_AUTHORIZATION, buildAuthString(username, password)); + connection.setUseCaches(false); + } + + connection.setRequestMethod("GET"); + connection.connect(); + + if (statusCode != connection.getResponseCode()) { + throw new RuntimeException(String.format("HTTP response code was: %s", + connection.getResponseCode())); + } + + if(responsePredicate != null) { + String responseBody = getResponseBody(connection); + if(!responsePredicate.test(responseBody)) { + throw new RuntimeException(String.format("Response: %s did not match predicate", + responseBody)); + } + } + + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + return true; + }); + + } catch (TimeoutException e) { + throw new ContainerLaunchException(String.format( + "Timed out waiting for URL to be accessible (%s should return HTTP %s)", uri, statusCode)); + } + } + + /** + * Build the URI on which to check if the container is ready. + * + * @param livenessCheckPort the liveness port + * @return the liveness URI + */ + private URI buildLivenessUri(int livenessCheckPort) { + final String scheme = (tlsEnabled ? "https" : "http") + "://"; + final String host = waitStrategyTarget.getContainerIpAddress(); + + final String portSuffix; + if ((tlsEnabled && 443 == livenessCheckPort) || (!tlsEnabled && 80 == livenessCheckPort)) { + portSuffix = ""; + } else { + portSuffix = ":" + String.valueOf(livenessCheckPort); + } + + return URI.create(scheme + host + portSuffix + path); + } + + /** + * @param username the username + * @param password the password + * @return a basic authentication string for the given credentials + */ + private String buildAuthString(String username, String password) { + return AUTH_BASIC + BaseEncoding.base64().encode((username + ":" + password).getBytes()); + } + + private String getResponseBody(HttpURLConnection connection) throws IOException { + BufferedReader reader; + if (200 <= connection.getResponseCode() && connection.getResponseCode() <= 299) { + reader = new BufferedReader(new InputStreamReader((connection.getInputStream()))); + } else { + reader = new BufferedReader(new InputStreamReader((connection.getErrorStream()))); + } + + StringBuilder builder = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + builder.append(line); + } + return builder.toString(); + } +} diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java new file mode 100644 index 00000000000..a25b1a86d8e --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java @@ -0,0 +1,43 @@ +package org.testcontainers.containers.wait.strategy; + +import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.ContainerLaunchException; +import org.testcontainers.containers.output.OutputFrame; +import org.testcontainers.containers.output.WaitingConsumer; +import org.testcontainers.utility.LogUtils; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Predicate; + +public class LogMessageWaitStrategy extends AbstractWaitStrategy { + + private String regEx; + + private int times = 1; + + @Override + protected void waitUntilReady() { + WaitingConsumer waitingConsumer = new WaitingConsumer(); + LogUtils.followOutput(DockerClientFactory.instance().client(), waitStrategyTarget.getContainerId(), waitingConsumer); + + Predicate waitPredicate = outputFrame -> + outputFrame.getUtf8String().matches(regEx); + + try { + waitingConsumer.waitUntil(waitPredicate, startupTimeout.getSeconds(), TimeUnit.SECONDS, times); + } catch (TimeoutException e) { + throw new ContainerLaunchException("Timed out waiting for log output matching '" + regEx + "'"); + } + } + + public LogMessageWaitStrategy withRegEx(String regEx) { + this.regEx = regEx; + return this; + } + + public LogMessageWaitStrategy withTimes(int times) { + this.times = times; + return this; + } +} diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/Wait.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/Wait.java new file mode 100644 index 00000000000..789927060b3 --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/Wait.java @@ -0,0 +1,64 @@ +package org.testcontainers.containers.wait.strategy; + +import java.net.HttpURLConnection; + +/** + * Convenience class with logic for building common {@link WaitStrategy} instances. + * + */ +public class Wait { + /** + * Convenience method to return the default WaitStrategy. + * + * @return a WaitStrategy + */ + public static WaitStrategy defaultWaitStrategy() { + return forListeningPort(); + } + + /** + * Convenience method to return a WaitStrategy for an exposed or mapped port. + * + * @return the WaitStrategy + * @see HostPortWaitStrategy + */ + public static HostPortWaitStrategy forListeningPort() { + return new HostPortWaitStrategy(); + } + + /** + * Convenience method to return a WaitStrategy for an HTTP endpoint. + * + * @param path the path to check + * @return the WaitStrategy + * @see HttpWaitStrategy + */ + public static HttpWaitStrategy forHttp(String path) { + return new HttpWaitStrategy() + .forPath(path) + .forStatusCode(HttpURLConnection.HTTP_OK); + } + + /** + * Convenience method to return a WaitStrategy for an HTTPS endpoint. + * + * @param path the path to check + * @return the WaitStrategy + * @see HttpWaitStrategy + */ + public static HttpWaitStrategy forHttps(String path) { + return forHttp(path) + .usingTls(); + } + + /** + * Convenience method to return a WaitStrategy for log messages. + * + * @param regex the regex pattern to check for + * @param times the number of times the pattern is expected + * @return LogMessageWaitStrategy + */ + public static LogMessageWaitStrategy forLogMessage(String regex, int times) { + return new LogMessageWaitStrategy().withRegEx(regex).withTimes(times); + } +} diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitAllStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitAllStrategy.java new file mode 100644 index 00000000000..1cc76c0747c --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitAllStrategy.java @@ -0,0 +1,34 @@ +package org.testcontainers.containers.wait.strategy; + +import org.rnorth.ducttape.timeouts.Timeouts; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +public class WaitAllStrategy implements WaitStrategy { + + private final List strategies = new ArrayList<>(); + private Duration timeout = Duration.ofSeconds(30); + + @Override + public void waitUntilReady(WaitStrategyTarget waitStrategyTarget) { + Timeouts.doWithTimeout((int) timeout.toMillis(), TimeUnit.MILLISECONDS, () -> { + for (WaitStrategy strategy : strategies) { + strategy.waitUntilReady(waitStrategyTarget); + } + }); + } + + public WaitAllStrategy withStrategy(WaitStrategy strategy) { + this.strategies.add(strategy); + return this; + } + + @Override + public WaitStrategy withStartupTimeout(Duration startupTimeout) { + this.timeout = startupTimeout; + return this; + } +} diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitStrategy.java new file mode 100644 index 00000000000..25703174502 --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitStrategy.java @@ -0,0 +1,10 @@ +package org.testcontainers.containers.wait.strategy; + +import java.time.Duration; + +public interface WaitStrategy { + + void waitUntilReady(WaitStrategyTarget waitStrategyTarget); + + WaitStrategy withStartupTimeout(Duration startupTimeout); +} diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitStrategyTarget.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitStrategyTarget.java new file mode 100644 index 00000000000..49f88fb7711 --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitStrategyTarget.java @@ -0,0 +1,19 @@ +package org.testcontainers.containers.wait.strategy; + +import org.testcontainers.containers.ContainerState; + +import java.util.Set; +import java.util.stream.Collectors; + +public interface WaitStrategyTarget extends ContainerState { + + /** + * @return the ports on which to check if the container is ready + */ + default Set getLivenessCheckPortNumbers() { + final Set result = getExposedPorts().stream() + .map(this::getMappedPort).distinct().collect(Collectors.toSet()); + result.addAll(getBoundPortNumbers()); + return result; + } +} diff --git a/core/src/main/java/org/testcontainers/utility/LogUtils.java b/core/src/main/java/org/testcontainers/utility/LogUtils.java index 75a78002366..8a17be9f65d 100644 --- a/core/src/main/java/org/testcontainers/utility/LogUtils.java +++ b/core/src/main/java/org/testcontainers/utility/LogUtils.java @@ -35,4 +35,8 @@ public void followOutput(DockerClient dockerClient, String containerId, cmd.exec(callback); } + + public void followOutput(DockerClient dockerClient, String containerId, Consumer consumer) { + followOutput(dockerClient, containerId, consumer, STDOUT, STDERR); + } } diff --git a/core/src/test/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheckTest.java b/core/src/test/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheckTest.java index 231942bf568..69bf9d06ce7 100644 --- a/core/src/test/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheckTest.java +++ b/core/src/test/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheckTest.java @@ -5,7 +5,7 @@ import org.junit.Before; import org.junit.Test; import org.rnorth.visibleassertions.VisibleAssertions; -import org.testcontainers.containers.Container; +import org.testcontainers.containers.wait.strategy.WaitStrategyTarget; import java.net.ServerSocket; @@ -18,7 +18,7 @@ public class ExternalPortListeningCheckTest { private ServerSocket listeningSocket1; private ServerSocket listeningSocket2; private ServerSocket nonListeningSocket; - private Container mockContainer; + private WaitStrategyTarget mockContainer; @Before public void setUp() throws Exception { @@ -28,7 +28,7 @@ public void setUp() throws Exception { nonListeningSocket = new ServerSocket(0); nonListeningSocket.close(); - mockContainer = mock(Container.class); + mockContainer = mock(WaitStrategyTarget.class); when(mockContainer.getContainerIpAddress()).thenReturn("127.0.0.1"); } @@ -68,4 +68,4 @@ public void tearDown() throws Exception { listeningSocket1.close(); listeningSocket2.close(); } -} \ No newline at end of file +} diff --git a/core/src/test/java/org/testcontainers/junit/BaseDockerComposeTest.java b/core/src/test/java/org/testcontainers/junit/BaseDockerComposeTest.java index 8024eac22a4..7f8b5d79953 100644 --- a/core/src/test/java/org/testcontainers/junit/BaseDockerComposeTest.java +++ b/core/src/test/java/org/testcontainers/junit/BaseDockerComposeTest.java @@ -1,9 +1,11 @@ package org.testcontainers.junit; import com.github.dockerjava.api.model.Network; -import org.jetbrains.annotations.NotNull; -import org.junit.*; -import org.rnorth.ducttape.unreliables.Unreliables; +import org.junit.After; +import org.junit.Assume; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.DockerComposeContainer; import org.testcontainers.utility.TestEnvironment; @@ -11,8 +13,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static org.hamcrest.CoreMatchers.is; @@ -39,9 +39,6 @@ public static void checkVersion() { public void simpleTest() { Jedis jedis = new Jedis(getEnvironment().getServiceHost("redis_1", REDIS_PORT), getEnvironment().getServicePort("redis_1", REDIS_PORT)); - // TODO: remove following resolution of #160 - Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, getLivenessCheck(jedis)); - jedis.incr("test"); jedis.incr("test"); jedis.incr("test"); @@ -54,9 +51,6 @@ public void secondTest() { // used in manual checking for cleanup in between tests Jedis jedis = new Jedis(getEnvironment().getServiceHost("redis_1", REDIS_PORT), getEnvironment().getServicePort("redis_1", REDIS_PORT)); - // TODO: remove following resolution of #160 - Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, getLivenessCheck(jedis)); - jedis.incr("test"); jedis.incr("test"); jedis.incr("test"); @@ -82,13 +76,4 @@ private List findAllNetworks() { .sorted() .collect(Collectors.toList()); } - - @NotNull - private Callable getLivenessCheck(Jedis jedis) { - return () -> { - jedis.connect(); - jedis.ping(); - return true; - }; - } } diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposePassthroughTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposePassthroughTest.java index 48c5b0db15f..cdeea00d201 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposePassthroughTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposePassthroughTest.java @@ -1,29 +1,29 @@ package org.testcontainers.junit; -import com.google.common.util.concurrent.Uninterruptibles; import org.junit.Assume; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; -import org.rnorth.ducttape.unreliables.Unreliables; +import org.testcontainers.containers.ContainerState; import org.testcontainers.containers.DockerComposeContainer; +import org.testcontainers.containers.wait.strategy.HostPortWaitStrategy; import org.testcontainers.utility.TestEnvironment; -import java.io.BufferedReader; import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.Socket; -import java.util.concurrent.TimeUnit; +import java.util.Arrays; +import java.util.Objects; -import static org.rnorth.visibleassertions.VisibleAssertions.info; -import static org.rnorth.visibleassertions.VisibleAssertions.pass; +import static org.hamcrest.CoreMatchers.hasItem; +import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; +import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; /** * Created by rnorth on 11/06/2016. */ public class DockerComposePassthroughTest { + private final TestWaitStrategy waitStrategy = new TestWaitStrategy(); + @BeforeClass public static void checkVersion() { Assume.assumeTrue(TestEnvironment.dockerApiAtLeast("1.22")); @@ -31,31 +31,35 @@ public static void checkVersion() { @Rule public DockerComposeContainer compose = - new DockerComposeContainer(new File("src/test/resources/v2-compose-test-passthrough.yml")) - .withEnv("foo", "bar") - .withExposedService("alpine_1", 3000); - - @Test(timeout = 30_000) - public void testEnvVar() throws IOException { - BufferedReader br = Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, () -> { - Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); - - Socket socket = new Socket(compose.getServiceHost("alpine_1", 3000), compose.getServicePort("alpine_1", 3000)); - return new BufferedReader(new InputStreamReader(socket.getInputStream())); - }); - - Unreliables.retryUntilTrue(10, TimeUnit.SECONDS, () -> { - while (br.ready()) { - String line = br.readLine(); - if (line.contains("bar=bar")) { - pass("Mapped environment variable was found"); - return true; - } - } - info("Mapped environment variable was not found yet - process probably not ready"); - Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); - return false; - }); + new DockerComposeContainer(new File("src/test/resources/v2-compose-test-passthrough.yml")) + .withEnv("foo", "bar") + .withExposedService("alpine_1", 3000, waitStrategy); + + + @Test + public void testContainerInstanceProperties() { + final ContainerState container = waitStrategy.getContainer(); + + //check environment variable was set + assertThat("Environment variable set correctly", Arrays.asList(Objects.requireNonNull(container.getContainerInfo() + .getConfig().getEnv())), hasItem("bar=bar")); + + //check other container properties + assertNotNull("Container id is not null", container.getContainerId()); + assertNotNull("Port mapped", container.getMappedPort(3000)); + assertThat("Exposed Ports", container.getExposedPorts(), hasItem(3000)); + + } + + /* + * WaitStrategy is the only class that has access to the DockerComposeServiceInstance reference + * Using a custom WaitStrategy to expose the reference for testability + */ + class TestWaitStrategy extends HostPortWaitStrategy { + @SuppressWarnings("unchecked") + public ContainerState getContainer() { + return this.waitStrategyTarget; + } } } diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeWaitStrategyTest.java new file mode 100644 index 00000000000..7724b048b69 --- /dev/null +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeWaitStrategyTest.java @@ -0,0 +1,65 @@ +package org.testcontainers.junit; + +import org.junit.Test; +import org.junit.runner.Description; +import org.rnorth.visibleassertions.VisibleAssertions; +import org.testcontainers.containers.DockerComposeContainer; +import org.testcontainers.containers.wait.strategy.Wait; + +import java.io.File; +import java.time.Duration; + +public class DockerComposeWaitStrategyTest { + + private static final int REDIS_PORT = 6379; + + @Test + public void testWaitOnListeningPort() { + final DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/compose-test.yml")) + .withExposedService("redis_1", REDIS_PORT, Wait.forListeningPort()); + + try { + environment.starting(Description.createTestDescription(Object.class, "name")); + VisibleAssertions.pass("Docker compose should start after waiting for listening port"); + } catch (RuntimeException e) { + VisibleAssertions.fail("Docker compose should start after waiting for listening port with failed with: " + e); + } + } + + @Test + public void testWaitOnMultipleStrategiesPassing() { + final DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/compose-test.yml")) + .withExposedService("redis_1", REDIS_PORT, Wait.forListeningPort()) + .withExposedService("db_1", 3306, Wait.forLogMessage(".*ready for connections.*\\s", 1)) + .withTailChildContainers(true); + + try { + environment.starting(Description.createTestDescription(Object.class, "name")); + VisibleAssertions.pass("Docker compose should start after waiting for listening port"); + } catch (RuntimeException e) { + VisibleAssertions.fail("Docker compose should start after waiting for listening port with failed with: " + e); + } + } + + @Test + public void testWaitingFails() { + final DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/compose-test.yml")) + .withExposedService("redis_1", REDIS_PORT, Wait.forHttp("/test").withStartupTimeout(Duration.ofSeconds(10))); + VisibleAssertions.assertThrows("waiting on an invalid http path times out", + RuntimeException.class, + () -> environment.starting(Description.createTestDescription(Object.class, "name"))); + } + + @Test + public void testWaitOnOneOfMultipleStrategiesFailing() { + final DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/compose-test.yml")) + .withExposedService("redis_1", REDIS_PORT, Wait.forListeningPort().withStartupTimeout(Duration.ofSeconds(10))) + .waitingFor("db_1", Wait.forLogMessage(".*test test test.*\\s", 1).withStartupTimeout(Duration.ofSeconds(10))) + .withTailChildContainers(true); + + VisibleAssertions.assertThrows("waiting on one failing strategy to time out", + RuntimeException.class, + () -> environment.starting(Description.createTestDescription(Object.class, "name"))); + } + +} diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/HostPortWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/HostPortWaitStrategyTest.java new file mode 100644 index 00000000000..840d8864a9b --- /dev/null +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/HostPortWaitStrategyTest.java @@ -0,0 +1,30 @@ +package org.testcontainers.junit.wait.strategy; + +import org.junit.ClassRule; +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; + +import java.time.Duration; + +import static org.rnorth.visibleassertions.VisibleAssertions.pass; + +/** + * Test wait strategy with overloaded waitingFor methods. + * Other implementations of WaitStrategy are tested through backwards compatible wait strategy tests + */ +public class HostPortWaitStrategyTest { + + private static final String IMAGE_NAME = "alpine:latest"; + + @ClassRule + public static GenericContainer container = new GenericContainer(IMAGE_NAME).withExposedPorts() + .withCommand("sh", "-c", "while true; do nc -lp 8080; done") + .withExposedPorts(8080) + .waitingFor(Wait.forListeningPort().withStartupTimeout(Duration.ofSeconds(10))); + + @Test + public void testWaiting() { + pass("Container starts after waiting"); + } +} diff --git a/core/src/test/resources/v2-compose-test-passthrough.yml b/core/src/test/resources/v2-compose-test-passthrough.yml index 6b0cd807158..b52f2d28b0e 100644 --- a/core/src/test/resources/v2-compose-test-passthrough.yml +++ b/core/src/test/resources/v2-compose-test-passthrough.yml @@ -3,4 +3,4 @@ services: alpine: build: compose-dockerfile environment: - bar: ${foo} \ No newline at end of file + bar: ${foo} diff --git a/docs/usage/docker_compose.md b/docs/usage/docker_compose.md index 2da864f16ca..f786992b07c 100644 --- a/docs/usage/docker_compose.md +++ b/docs/usage/docker_compose.md @@ -54,6 +54,61 @@ String redisUrl = environment.getServiceHost("redis_1", REDIS_PORT) environment.getServicePort("redis_1", REDIS_PORT); ``` +## Startup timeout +Ordinarily Testcontainers will wait for up to 60 seconds for each exposed container's first mapped network port to start listening. + +This simple measure provides a basic check whether a container is ready for use. + +There are overloaded `withExposedService` methods that take a `WaitStrategy` so you can specify a timeout strategy per container. + +### Waiting for startup examples + +Waiting for exposed port to start listening: +```java +@ClassRule +public static DockerComposeContainer environment = + new DockerComposeContainer(new File("src/test/resources/compose-test.yml")) + .withStartupTimeout(Duration.ofSeconds(30)) + .withExposedService("redis_1", REDIS_PORT, Wait.forListeningPort()); +``` + +Wait for arbitrary status code on an HTTPS endpoint: +```java +@ClassRule +public static DockerComposeContainer environment = + new DockerComposeContainer(new File("src/test/resources/compose-test.yml")) + .withStartupTimeout(Duration.ofSeconds(30)) + .withExposedService("elasticsearch_1", ELASTICSEARCH_PORT, + Wait.forHttp("/all") + .forStatusCode(301) + .usingTls()); +``` + +Separate wait strategies for each container: +```java +@ClassRule +public static DockerComposeContainer environment = + new DockerComposeContainer(new File("src/test/resources/compose-test.yml")) + .withStartupTimeout(Duration.ofSeconds(30)) + .withExposedService("redis_1", REDIS_PORT, Wait.forListeningPort()) + .withExposedService("elasticsearch_1", ELASTICSEARCH_PORT, + Wait.forHttp("/all") + .forStatusCode(301) + .usingTls()); +``` + +Alternatively, you can use `waitingFor(serviceName, waitStrategy)`, +for example if you need to wait on a log message from a service, but don't need to expose a port. + +```java +@ClassRule +public static DockerComposeContainer environment = + new DockerComposeContainer(new File("src/test/resources/compose-test.yml")) + .withStartupTimeout(Duration.ofSeconds(30)) + .withExposedService("redis_1", REDIS_PORT, Wait.forListeningPort()) + .waitingFor("db_1", Wait.forLogMessage("started", 1)); +``` + ## Using private repositories in Docker compose When Docker Compose is used in container mode (not local), it's needs to be made aware of Docker settings for private repositories. By default, those setting are located in `$HOME/.docker/config.json`. @@ -93,4 +148,4 @@ To work around this problem, create `config.json` in separate location with real "credsStore" : "osxkeychain" } ``` -and specify the location to Testcontainers using any of the two first methods from above. \ No newline at end of file +and specify the location to Testcontainers using any of the two first methods from above. diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/ComposeContainerIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/ComposeContainerIT.groovy index f90d7f7a4a7..b952dd1c5dc 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/ComposeContainerIT.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/ComposeContainerIT.groovy @@ -3,6 +3,7 @@ package org.testcontainers.spock import org.apache.http.client.methods.HttpGet import org.apache.http.impl.client.HttpClientBuilder import org.testcontainers.containers.DockerComposeContainer +import org.testcontainers.containers.wait.strategy.Wait import spock.lang.Specification @Testcontainers @@ -10,7 +11,7 @@ class ComposeContainerIT extends Specification { DockerComposeContainer composeContainer = new DockerComposeContainer( new File("src/test/resources/docker-compose.yml")) - .withExposedService("whoami_1", 80) + .withExposedService("whoami_1", 80, Wait.forHttp("/")) String host diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/SharedComposeContainerIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/SharedComposeContainerIT.groovy index 091ac4cb044..dd2ce9f5b3c 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/SharedComposeContainerIT.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/SharedComposeContainerIT.groovy @@ -3,6 +3,7 @@ package org.testcontainers.spock import org.apache.http.client.methods.HttpGet import org.apache.http.impl.client.HttpClientBuilder import org.testcontainers.containers.DockerComposeContainer +import org.testcontainers.containers.wait.strategy.Wait import spock.lang.Shared import spock.lang.Specification @@ -12,7 +13,7 @@ class SharedComposeContainerIT extends Specification { @Shared DockerComposeContainer composeContainer = new DockerComposeContainer( new File("src/test/resources/docker-compose.yml")) - .withExposedService("whoami_1", 80) + .withExposedService("whoami_1", 80, Wait.forHttp("/")) String host From 7e2f48c7ed233bfd4d8a91769f063c7768e1fa8e Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 19 Mar 2018 07:57:55 +0100 Subject: [PATCH 0254/5615] Update CHANGELOG.md --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b6aa92c254..ef21e10f9cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,10 +10,10 @@ All notable changes to this project will be documented in this file. - Abstracted and changed database init script functionality to support use of SQL-like scripts with non-JDBC connections. ([\#551](https://github.com/testcontainers/testcontainers-java/pull/551)) - Added `JdbcDatabaseContainer(Future)` constructor. ([\#543](https://github.com/testcontainers/testcontainers-java/issues/543)) - Mark DockerMachineClientProviderStrategy as not persistable ([\#593](https://github.com/testcontainers/testcontainers-java/pull/593)) -- Added `waitingFor(String serviceName, WaitStrategy waitStrategy)` and overloaded `withExposedService()` methods to `DockerComposeContainer` to allow user to define `WaitStrategy` for compose containers. ([\#174](https://github.com/testcontainers/testcontainers-java/issues/174) and [\#515](https://github.com/testcontainers/testcontainers-java/issues/515)) -- Deprecated `WaitStrategy` and implementations in favour of classes with same names in `org.testcontainers.containers.strategy` -- Added `ContainerState` interface representing the state of a started container -- Added `WaitStrategyTarget` interface which is the target of the new `WaitStrategy` +- Added `waitingFor(String serviceName, WaitStrategy waitStrategy)` and overloaded `withExposedService()` methods to `DockerComposeContainer` to allow user to define `WaitStrategy` for compose containers. ([\#174](https://github.com/testcontainers/testcontainers-java/issues/174), [\#515](https://github.com/testcontainers/testcontainers-java/issues/515) and ([\#600](https://github.com/testcontainers/testcontainers-java/pull/600))) +- Deprecated `WaitStrategy` and implementations in favour of classes with same names in `org.testcontainers.containers.strategy` ([\#600](https://github.com/testcontainers/testcontainers-java/pull/600)) +- Added `ContainerState` interface representing the state of a started container ([\#600](https://github.com/testcontainers/testcontainers-java/pull/600)) +- Added `WaitStrategyTarget` interface which is the target of the new `WaitStrategy` ([\#600](https://github.com/testcontainers/testcontainers-java/pull/600)) ## [1.6.0] - 2018-01-28 From 20b93a5e59173558de652c4ae856ff815c6aa2e0 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 19 Mar 2018 11:00:06 +0100 Subject: [PATCH 0255/5615] fix JavaDoc build --- .../org/testcontainers/containers/ExecInContainerPattern.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java b/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java index 976ec4a54f0..af1e7e8d503 100644 --- a/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java +++ b/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java @@ -25,7 +25,7 @@ public class ExecInContainerPattern { /** * Run a command inside a running container, as though using "docker exec", and interpreting * the output as UTF8. - *

+ *

* @param containerInfo the container info * @param command the command to execute * @see #execInContainer(InspectContainerResponse, Charset, String...) From 0d3f41475898c077a28dadb34238121e5e128fbd Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 23 Mar 2018 10:38:10 +0100 Subject: [PATCH 0256/5615] shade commons-io (#615) --- core/build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/build.gradle b/core/build.gradle index b2cf84fca2d..a1c4829f7e7 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -51,6 +51,7 @@ shadowJar { [ "org.apache.http", "org.apache.commons.lang", + "org.apache.commons.io", "org.glassfish", "org.aopalliance", "org.jvnet", @@ -79,6 +80,7 @@ shadowJar { include(dependency('org.newsclub.*:.*')) include(dependency('org.zeroturnaround:zt-exec')) include(dependency('commons-lang:commons-lang')) + include(dependency('commons-io:commons-io')) } } @@ -115,6 +117,7 @@ dependencies { shaded 'org.rnorth:tcp-unix-socket-proxy:1.0.1' shaded 'org.zeroturnaround:zt-exec:1.8' shaded 'commons-lang:commons-lang:2.6' + shaded 'commons-io:commons-io:2.5' // docker-java uses SslConfigurator from jersey-common for TLS shaded ('org.glassfish.jersey.core:jersey-common:2.23.1') { // SslConfigurator doesn't use classes from dependencies From 058378c7dfcc43a46c3958bc2095fffce5311f2e Mon Sep 17 00:00:00 2001 From: Andrei Tognolo Date: Sun, 25 Mar 2018 08:47:42 +0100 Subject: [PATCH 0257/5615] Add Circle CI to summary (#622) --- docs/SUMMARY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 03c400b32dd..6362e2f56af 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -50,6 +50,7 @@ ## Continuous Integration * [GitLab CI](ci/ci.md#gitlab) +* [Circle CI](ci/ci.md#circleci-20) ## Examples * [Selenium](https://github.com/testcontainers/testcontainers-java-examples/blob/master/selenium-container/src/test/java/SeleniumContainerTest.java) From 56ffbfd0335a43a917944c70251829a1bc2dfb58 Mon Sep 17 00:00:00 2001 From: Barry Commins Date: Sun, 25 Mar 2018 20:04:05 +0100 Subject: [PATCH 0258/5615] Fixed docker-compose examples in docs (#623) --- docs/usage/docker_compose.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/docs/usage/docker_compose.md b/docs/usage/docker_compose.md index f786992b07c..b4a6e822106 100644 --- a/docs/usage/docker_compose.md +++ b/docs/usage/docker_compose.md @@ -34,9 +34,9 @@ elasticsearch: Note that it is not necessary to define ports to be exposed in the YAML file; this would inhibit reuse/inclusion of the file in other contexts. -Instead, Testcontainers will spin up a small 'ambassador' container for every exposed service port, which will proxy -between the Compose-managed container and a port that's accessible to your tests. This is done using a separate, minimal -container that runs HAProxy in TCP proxying mode. +Instead, Testcontainers will spin up a small 'ambassador' container, which will proxy +between the Compose-managed containers and ports that are accessible to your tests. This is done using a separate, minimal +container that runs socat as a TCP proxy. ## Accessing a container from tests @@ -68,8 +68,8 @@ Waiting for exposed port to start listening: @ClassRule public static DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/compose-test.yml")) - .withStartupTimeout(Duration.ofSeconds(30)) - .withExposedService("redis_1", REDIS_PORT, Wait.forListeningPort()); + .withExposedService("redis_1", REDIS_PORT, + Wait.forListeningPort().withStartupTimeout(Duration.ofSeconds(30))); ``` Wait for arbitrary status code on an HTTPS endpoint: @@ -77,7 +77,6 @@ Wait for arbitrary status code on an HTTPS endpoint: @ClassRule public static DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/compose-test.yml")) - .withStartupTimeout(Duration.ofSeconds(30)) .withExposedService("elasticsearch_1", ELASTICSEARCH_PORT, Wait.forHttp("/all") .forStatusCode(301) @@ -89,7 +88,6 @@ Separate wait strategies for each container: @ClassRule public static DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/compose-test.yml")) - .withStartupTimeout(Duration.ofSeconds(30)) .withExposedService("redis_1", REDIS_PORT, Wait.forListeningPort()) .withExposedService("elasticsearch_1", ELASTICSEARCH_PORT, Wait.forHttp("/all") @@ -104,7 +102,6 @@ for example if you need to wait on a log message from a service, but don't need @ClassRule public static DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/compose-test.yml")) - .withStartupTimeout(Duration.ofSeconds(30)) .withExposedService("redis_1", REDIS_PORT, Wait.forListeningPort()) .waitingFor("db_1", Wait.forLogMessage("started", 1)); ``` From 37456aecc3a06dd08a9db465140a12d23f0e8bd0 Mon Sep 17 00:00:00 2001 From: Barry Commins Date: Mon, 26 Mar 2018 22:04:22 +0100 Subject: [PATCH 0259/5615] Fixed getServicePort on DockerComposeContainer throws NullPointerException if service instance number in not used. (#619) --- CHANGELOG.md | 1 + .../containers/DockerComposeContainer.java | 2 +- .../junit/DockerComposeContainerTest.java | 13 +++++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef21e10f9cb..8701480fc13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. ### Fixed - Fixed extraneous insertion of `useSSL=false` in all JDBC URL strings, even for DBs that do not understand it. Usage is now restricted to MySQL by default and can be overridden by authors of `JdbcDatabaseContainer` subclasses ([\#568](https://github.com/testcontainers/testcontainers-java/issues/568)) +- Fixed `getServicePort` on `DockerComposeContainer` throws NullPointerException if service instance number in not used. ([\#619](https://github.com/testcontainers/testcontainers-java/issues/619)) ### Changed - Abstracted and changed database init script functionality to support use of SQL-like scripts with non-JDBC connections. ([\#551](https://github.com/testcontainers/testcontainers-java/pull/551)) diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 49f31448616..c284002c83b 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -352,7 +352,7 @@ public String getServiceHost(String serviceName, Integer servicePort) { * @return a port that can be used for accessing the service container. */ public Integer getServicePort(String serviceName, Integer servicePort) { - return ambassadorContainer.getMappedPort(ambassadorPortMappings.get(serviceName).get(servicePort)); + return ambassadorContainer.getMappedPort(ambassadorPortMappings.get(getServiceInstanceName(serviceName)).get(servicePort)); } public SELF withScaledService(String serviceBaseName, int numInstances) { diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java index 8d2addf7b5e..bf69656ac04 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java @@ -1,10 +1,14 @@ package org.testcontainers.junit; import org.junit.Rule; +import org.junit.Test; import org.testcontainers.containers.DockerComposeContainer; import java.io.File; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; + /** * Created by rnorth on 08/08/2015. */ @@ -20,4 +24,13 @@ public class DockerComposeContainerTest extends BaseDockerComposeTest { protected DockerComposeContainer getEnvironment() { return environment; } + + @Test + public void testGetServicePort() { + int serviceWithInstancePort = environment.getServicePort("redis_1", REDIS_PORT); + assertNotNull("Port is set for service with instance number", serviceWithInstancePort); + int serviceWithoutInstancePort = environment.getServicePort("redis", REDIS_PORT); + assertNotNull("Port is set for service with instance number", serviceWithoutInstancePort); + assertEquals("Service ports are the same", serviceWithInstancePort, serviceWithoutInstancePort); + } } From 23478fac6f86c4dd8d0eb964386089144cb26f6e Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 26 Mar 2018 22:16:27 +0100 Subject: [PATCH 0260/5615] Relocate modules (#607) ## General * Docs update referring to new location of modules * Update copyright and thanks * Add tests for pulling various permutations of image registry/name/tag * Include pointers to latest versions available through Maven Central ## MariaDB * Relocate MariaDB module into main testcontainers-java repository Also enable parallel running of Jdbc Driver tests for speed reasons ## Oracle XE * Relocate Oracle XE module into main testcontainers-java repository Also enable use of sha256 hashes instead of image tags, for situations where image tags are not easily pinnable * Extend pull/startup timeout for Oracle container * Change rate limiter usage to prevent excessive connection creation Intended to eliminate `ORA-12516, TNS:listener could not find available handler` error * Add getConnectTimeoutSeconds as a separate timeout for JDBC containers to establish a JDBC connection ## Localstack * Relocate Localstack module into main testcontainers-java repository * Use latest localstack image ## Dynalite * Relocate Dynalite module into main testcontainers-java repository * Migrate dynalite image to quay.io ## MS SQL Server * Relocate MS SQL Server module into main testcontainers-java repository * Introduce assertion for license acceptance, e.g. for the SQL Server EULA ## Vault * Relocate Vault module into main testcontainers-java repository --- .github/CODEOWNERS | 14 ++ README.md | 6 +- circle.yml | 33 +++- .../containers/GenericContainer.java | 2 +- .../images/RemoteDockerImage.java | 65 ++++--- .../dockerfile/traits/FromStatementTrait.java | 2 +- .../utility/DockerImageName.java | 171 +++++++++++++++++- .../utility/DockerLoggerFactory.java | 12 +- .../utility/LicenseAcceptance.java | 36 ++++ .../dockerclient/ImagePullTest.java | 42 +++++ .../utility/DockerImageNameTest.java | 43 ++++- .../utility/LicenseAcceptanceTest.java | 17 ++ .../container-license-acceptance.txt | 3 + docs/usage/modules.md | 14 +- modules/dynalite/README.md | 47 +++++ modules/dynalite/build.gradle | 8 + .../dynamodb/DynaliteContainer.java | 76 ++++++++ .../dynamodb/DynaliteContainerTest.java | 52 ++++++ .../src/test/resources/logback-test.xml | 24 +++ modules/jdbc-test/build.gradle | 14 ++ .../testcontainers/jdbc/JDBCDriverTest.java | 37 ++-- .../jdbc/JDBCDriverWithPoolTest.java | 3 +- .../junit/SimpleMSSQLServerTest.java | 62 +++++++ .../junit/SimpleMariaDBTest.java | 87 +++++++++ .../junit/SimpleOracleTest.java | 39 ++++ .../container-license-acceptance.txt | 1 + .../test/resources/somepath/init_mariadb.sql | 5 + .../somepath/mariadb_conf_override/my.cnf | 55 ++++++ .../containers/JdbcDatabaseContainer.java | 40 ++-- modules/localstack/README.md | 45 +++++ modules/localstack/build.gradle | 8 + .../localstack/LocalStackContainer.java | 166 +++++++++++++++++ .../localstack/SimpleLocalstackS3Test.java | 52 ++++++ .../src/test/resources/logback-test.xml | 27 +++ modules/mariadb/README.md | 40 ++++ modules/mariadb/build.gradle | 11 ++ .../containers/MariaDBContainer.java | 73 ++++++++ .../containers/MariaDBContainerProvider.java | 16 ++ ...s.containers.JdbcDatabaseContainerProvider | 1 + .../resources/mariadb-default-conf/my.cnf | 48 +++++ modules/mssqlserver/AUTHORS | 1 + modules/mssqlserver/LICENSE | 21 +++ modules/mssqlserver/README.md | 52 ++++++ modules/mssqlserver/build.gradle | 5 + .../containers/MSSQLServerContainer.java | 62 +++++++ .../MSSQLServerContainerProvider.java | 16 ++ ...s.containers.JdbcDatabaseContainerProvider | 1 + modules/oracle-xe/README.md | 40 ++++ modules/oracle-xe/build.gradle | 17 ++ .../containers/OracleContainer.java | 78 ++++++++ .../containers/OracleContainerProvider.java | 16 ++ ...s.containers.JdbcDatabaseContainerProvider | 1 + .../containers/OracleJDBCDriverTest.java | 48 +++++ modules/vault/AUTHORS | 1 + modules/vault/LICENSE | 21 +++ modules/vault/README.md | 68 +++++++ modules/vault/build.gradle | 7 + .../testcontainers/vault/VaultContainer.java | 133 ++++++++++++++ .../vault/VaultContainerTest.java | 79 ++++++++ .../vault/src/test/resources/logback-test.xml | 24 +++ 60 files changed, 2098 insertions(+), 90 deletions(-) create mode 100644 .github/CODEOWNERS create mode 100644 core/src/main/java/org/testcontainers/utility/LicenseAcceptance.java create mode 100644 core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java create mode 100644 core/src/test/java/org/testcontainers/utility/LicenseAcceptanceTest.java create mode 100644 core/src/test/resources/container-license-acceptance.txt create mode 100644 modules/dynalite/README.md create mode 100644 modules/dynalite/build.gradle create mode 100644 modules/dynalite/src/main/java/org/testcontainers/dynamodb/DynaliteContainer.java create mode 100644 modules/dynalite/src/test/java/org/testcontainers/dynamodb/DynaliteContainerTest.java create mode 100644 modules/dynalite/src/test/resources/logback-test.xml create mode 100644 modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMSSQLServerTest.java create mode 100644 modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMariaDBTest.java create mode 100644 modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleOracleTest.java create mode 100644 modules/jdbc-test/src/test/resources/container-license-acceptance.txt create mode 100644 modules/jdbc-test/src/test/resources/somepath/init_mariadb.sql create mode 100644 modules/jdbc-test/src/test/resources/somepath/mariadb_conf_override/my.cnf create mode 100644 modules/localstack/README.md create mode 100644 modules/localstack/build.gradle create mode 100644 modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java create mode 100644 modules/localstack/src/test/java/org/testcontainers/containers/localstack/SimpleLocalstackS3Test.java create mode 100644 modules/localstack/src/test/resources/logback-test.xml create mode 100644 modules/mariadb/README.md create mode 100644 modules/mariadb/build.gradle create mode 100644 modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java create mode 100644 modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java create mode 100644 modules/mariadb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider create mode 100644 modules/mariadb/src/main/resources/mariadb-default-conf/my.cnf create mode 100644 modules/mssqlserver/AUTHORS create mode 100644 modules/mssqlserver/LICENSE create mode 100644 modules/mssqlserver/README.md create mode 100644 modules/mssqlserver/build.gradle create mode 100644 modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java create mode 100644 modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainerProvider.java create mode 100644 modules/mssqlserver/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider create mode 100644 modules/oracle-xe/README.md create mode 100644 modules/oracle-xe/build.gradle create mode 100644 modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java create mode 100644 modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainerProvider.java create mode 100644 modules/oracle-xe/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider create mode 100644 modules/oracle-xe/src/test/java/org/testcontainers/containers/OracleJDBCDriverTest.java create mode 100644 modules/vault/AUTHORS create mode 100644 modules/vault/LICENSE create mode 100644 modules/vault/README.md create mode 100644 modules/vault/build.gradle create mode 100644 modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java create mode 100644 modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java create mode 100644 modules/vault/src/test/resources/logback-test.xml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000000..add6048a437 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,14 @@ +# Each line is a file pattern followed by one or more owners. + +# These owners will be the default reviewers for everything in +# the repo. + +* @rnorth @bsideup @kiview + +# The last matching pattern takes the most +# precedence. + +# Contributed modules can have different reviewers + +modules/mssqlserver/ @StefanHufschmidt @rnorth @bsideup @kiview +modules/vault/ @mikeoswald @rnorth @bsideup @kiview diff --git a/README.md b/README.md index 16bcd0120ec..92cc9972cd9 100644 --- a/README.md +++ b/README.md @@ -16,4 +16,8 @@ See [LICENSE](LICENSE). Copyright (c) 2015, 2016 Richard North and other authors. -See [AUTHORS](AUTHORS) for contributors. +MS SQL Server module is (c) 2017 - 2019 G DATA Software AG and other authors. + +Hashicorp Vault module is (c) 2017 Capital One Services, LLC and other authors. + +See [AUTHORS](AUTHORS) for all contributors. diff --git a/circle.yml b/circle.yml index 43aff9d8f3d..a793bf8cb6a 100644 --- a/circle.yml +++ b/circle.yml @@ -4,7 +4,24 @@ jobs: core: steps: - checkout - - run: ./gradlew testcontainers:check + - run: + command: ./gradlew testcontainers:check + - run: + name: Save test results + command: | + mkdir -p ~/junit/ + find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; + when: always + - store_test_results: + path: ~/junit + modules-no-jdbc-test-no-selenium: + steps: + - checkout + - run: + command: ./gradlew check -x testcontainers:check -x selenium:check -x jdbc-test:check + environment: + # Oracle JDBC drivers require a timezone to be set + TZ: "/usr/share/zoneinfo/ETC/UTC" - run: name: Save test results command: | @@ -13,10 +30,14 @@ jobs: when: always - store_test_results: path: ~/junit - modules: + modules-jdbc-test: steps: - checkout - - run: ./gradlew check -x testcontainers:check -x selenium:check + - run: + command: ./gradlew jdbc-test:check + environment: + # Oracle JDBC drivers require a timezone to be set + TZ: "/usr/share/zoneinfo/ETC/UTC" - run: name: Save test results command: | @@ -28,7 +49,8 @@ jobs: selenium: steps: - checkout - - run: ./gradlew selenium:check + - run: + command: ./gradlew selenium:check - run: name: Save test results command: | @@ -43,5 +65,6 @@ workflows: test_all: jobs: - core - - modules + - modules-no-jdbc-test-no-selenium + - modules-jdbc-test - selenium diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 86949d755a4..7d3001a5651 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -838,7 +838,7 @@ public String getDockerImageName() { try { return image.get(); } catch (Exception e) { - throw new ContainerFetchException("Can't get Docker image name from " + image, e); + throw new ContainerFetchException("Can't get Docker image: " + image, e); } } diff --git a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java index fb686c82e19..59f1c2539f5 100644 --- a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java +++ b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java @@ -6,6 +6,7 @@ import com.github.dockerjava.api.model.Image; import com.github.dockerjava.core.command.PullImageResultCallback; import lombok.NonNull; +import lombok.ToString; import org.slf4j.Logger; import org.slf4j.profiler.Profiler; import org.testcontainers.DockerClientFactory; @@ -14,30 +15,32 @@ import org.testcontainers.utility.DockerLoggerFactory; import org.testcontainers.utility.LazyFuture; -import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +@ToString public class RemoteDockerImage extends LazyFuture { - public static final Set AVAILABLE_IMAGE_NAME_CACHE = new HashSet<>(); + public static final Set AVAILABLE_IMAGE_NAME_CACHE = new HashSet<>(); - private final String dockerImageName; + private DockerImageName imageName; public RemoteDockerImage(String dockerImageName) { - DockerImageName.validate(dockerImageName); - this.dockerImageName = dockerImageName; + imageName = new DockerImageName(dockerImageName); } public RemoteDockerImage(@NonNull String repository, @NonNull String tag) { - this.dockerImageName = repository + ":" + tag; + imageName = new DockerImageName(repository, tag); } @Override protected final String resolve() { Profiler profiler = new Profiler("Rule creation - prefetch image"); - Logger logger = DockerLoggerFactory.getLogger(dockerImageName); + Logger logger = DockerLoggerFactory.getLogger(imageName.toString()); profiler.setLogger(logger); Profiler nested = profiler.startNested("Obtaining client"); @@ -48,10 +51,11 @@ protected final String resolve() { profiler.start("Check local images"); int attempts = 0; + DockerClientException lastException = null; while (true) { // Does our cache already know the image? - if (AVAILABLE_IMAGE_NAME_CACHE.contains(dockerImageName)) { - logger.trace("{} is already in image name cache", dockerImageName); + if (AVAILABLE_IMAGE_NAME_CACHE.contains(imageName)) { + logger.trace("{} is already in image name cache", imageName); break; } @@ -59,47 +63,52 @@ protected final String resolve() { ListImagesCmd listImagesCmd = dockerClient.listImagesCmd(); if (Boolean.parseBoolean(System.getProperty("useFilter"))) { - listImagesCmd = listImagesCmd.withImageNameFilter(dockerImageName); + listImagesCmd = listImagesCmd.withImageNameFilter(imageName.toString()); } List updatedImages = listImagesCmd.exec(); - for (Image image : updatedImages) { - if (image.getRepoTags() != null) { - Collections.addAll(AVAILABLE_IMAGE_NAME_CACHE, image.getRepoTags()); - } - } + updatedImages.stream() + .map(Image::getRepoTags) + .filter(Objects::nonNull) + .flatMap(Stream::of) + .map(DockerImageName::new) + .collect(Collectors.toCollection(() -> AVAILABLE_IMAGE_NAME_CACHE)); // And now? - if (AVAILABLE_IMAGE_NAME_CACHE.contains(dockerImageName)) { - logger.trace("{} is in image name cache following listing of images", dockerImageName); + if (AVAILABLE_IMAGE_NAME_CACHE.contains(imageName)) { + logger.trace("{} is in image name cache following listing of images", imageName); break; } // Log only on first attempt if (attempts == 0) { - logger.info("Pulling docker image: {}. Please be patient; this may take some time but only needs to be done once.", dockerImageName); + logger.info("Pulling docker image: {}. Please be patient; this may take some time but only needs to be done once.", imageName); profiler.start("Pull image"); } if (attempts++ >= 3) { - logger.error("Retry limit reached while trying to pull image: " + dockerImageName + ". Please check output of `docker pull " + dockerImageName + "`"); - throw new ContainerFetchException("Retry limit reached while trying to pull image: " + dockerImageName); + logger.error("Retry limit reached while trying to pull image: {}. Please check output of `docker pull {}`", imageName, imageName); + throw new ContainerFetchException("Retry limit reached while trying to pull image: " + imageName, lastException); } // The image is not available locally - pull it try { - dockerClient.pullImageCmd(dockerImageName).exec(new PullImageResultCallback()).awaitCompletion(); - } catch (InterruptedException e) { - throw new ContainerFetchException("Failed to fetch container image for " + dockerImageName, e); + final PullImageResultCallback callback = new PullImageResultCallback(); + dockerClient + .pullImageCmd(imageName.getUnversionedPart()) + .withTag(imageName.getVersionPart()) + .exec(callback); + callback.awaitSuccess(); + AVAILABLE_IMAGE_NAME_CACHE.add(imageName); + break; + } catch (DockerClientException e) { + lastException = e; } - - // Do not break here, but step into the next iteration, where it will be verified with listImagesCmd(). - // see https://github.com/docker/docker/issues/10708 } - return dockerImageName; + return imageName.toString(); } catch (DockerClientException e) { - throw new ContainerFetchException("Failed to get Docker client for " + dockerImageName, e); + throw new ContainerFetchException("Failed to get Docker client for " + imageName, e); } finally { profiler.stop().log(); } diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/FromStatementTrait.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/FromStatementTrait.java index c148bff106b..2a87cb08223 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/FromStatementTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/FromStatementTrait.java @@ -6,7 +6,7 @@ public interface FromStatementTrait & DockerfileBuilderTrait> { default SELF from(String dockerImageName) { - DockerImageName.validate(dockerImageName); + new DockerImageName(dockerImageName).assertValid(); return ((SELF) this).withStatement(new SingleArgumentStatement("FROM", dockerImageName)); } diff --git a/core/src/main/java/org/testcontainers/utility/DockerImageName.java b/core/src/main/java/org/testcontainers/utility/DockerImageName.java index 42345223aff..212b81a20eb 100644 --- a/core/src/main/java/org/testcontainers/utility/DockerImageName.java +++ b/core/src/main/java/org/testcontainers/utility/DockerImageName.java @@ -1,20 +1,173 @@ package org.testcontainers.utility; + +import com.google.common.net.HostAndPort; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.regex.Pattern; + +@EqualsAndHashCode public final class DockerImageName { - public static void validate(String dockerImageName) throws IllegalArgumentException { - int repoSeparatorIndex = dockerImageName.indexOf('/'); - int tagSeparatorIndex; - if (repoSeparatorIndex == -1) { - tagSeparatorIndex = dockerImageName.indexOf(':'); + /* Regex patterns used for validation */ + private static final String ALPHA_NUMERIC = "[a-z0-9]+"; + private static final String SEPARATOR = "([\\.]{1}|_{1,2}|-+)"; + private static final String REPO_NAME_PART = ALPHA_NUMERIC + "(" + SEPARATOR + ALPHA_NUMERIC + ")*"; + private static final Pattern REPO_NAME = Pattern.compile(REPO_NAME_PART + "(/" + REPO_NAME_PART + ")*"); + + private final String rawName; + private final String registry; + private final String repo; + private final Versioning versioning; + + public DockerImageName(String name) { + this.rawName = name; + final int slashIndex = name.indexOf('/'); + + String remoteName; + if (slashIndex == -1 || + (!name.substring(0, slashIndex).contains(".") && + !name.substring(0, slashIndex).contains(":") && + !name.substring(0, slashIndex).equals("localhost"))) { + registry = ""; + remoteName = name; } else { - tagSeparatorIndex = dockerImageName.indexOf(':', repoSeparatorIndex); + registry = name.substring(0, slashIndex); + remoteName = name.substring(slashIndex + 1); + } + + if (remoteName.contains("@sha256:")) { + repo = remoteName.split("@sha256:")[0]; + versioning = new Sha256Versioning(remoteName.split("@sha256:")[1]); + } else if (remoteName.contains(":")) { + repo = remoteName.split(":")[0]; + versioning = new TagVersioning(remoteName.split(":")[1]); + } else { + repo = remoteName; + versioning = null; + } + } + + public DockerImageName(String name, String tag) { + this.rawName = name; + final int slashIndex = name.indexOf('/'); + + String remoteName; + if (slashIndex == -1 || + (!name.substring(0, slashIndex).contains(".") && + !name.substring(0, slashIndex).contains(":") && + !name.substring(0, slashIndex).equals("localhost"))) { + registry = ""; + remoteName = name; + } else { + registry = name.substring(0, slashIndex - 1); + remoteName = name.substring(slashIndex + 1); + } + + if (tag.startsWith("sha256:")) { + repo = remoteName; + versioning = new Sha256Versioning(tag); + } else { + repo = remoteName; + versioning = new TagVersioning(tag); + } + } + + /** + * @return the unversioned (non 'tag') part of this name + */ + public String getUnversionedPart() { + if (!"".equals(registry)) { + return registry + "/" + repo; + } else { + return repo; + } + } + + /** + * @return the versioned part of this name (tag or sha256) + */ + public String getVersionPart() { + return versioning.toString(); + } + + @Override + public String toString() { + return getUnversionedPart() + versioning.getSeparator() + versioning.toString(); + } + + /** + * Is the image name valid? + * + * @throws IllegalArgumentException if not valid + */ + public void assertValid() { + HostAndPort.fromString(registry); + if (!REPO_NAME.matcher(repo).matches()) { + throw new IllegalArgumentException(repo + " is not a valid Docker image name (in " + rawName + ")"); } - if (tagSeparatorIndex == -1) { + if (versioning == null) { throw new IllegalArgumentException("No image tag was specified in docker image name " + - "(" + dockerImageName + "). Please provide a tag; this may be 'latest' or a specific version"); + "(" + rawName + "). Please provide a tag; this may be 'latest' or a specific version"); + } + if (!versioning.isValid()) { + throw new IllegalArgumentException(versioning + " is not a valid image versioning identifier (in " + rawName + ")"); } } - private DockerImageName() {} + private interface Versioning { + boolean isValid(); + String getSeparator(); + } + + @Data + private static class TagVersioning implements Versioning { + public static final String TAG_REGEX = "[\\w][\\w\\.\\-]{0,127}"; + private final String tag; + + TagVersioning(String tag) { + this.tag = tag; + } + + @Override + public boolean isValid() { + return tag.matches(TAG_REGEX); + } + + @Override + public String getSeparator() { + return ":"; + } + + @Override + public String toString() { + return tag; + } + } + + @Data + private class Sha256Versioning implements Versioning { + public static final String HASH_REGEX = "[0-9a-fA-F]{32,}"; + private final String hash; + + Sha256Versioning(String hash) { + this.hash = hash; + } + + @Override + public boolean isValid() { + return hash.matches(HASH_REGEX); + } + + @Override + public String getSeparator() { + return "@"; + } + + @Override + public String toString() { + return "sha256:" + hash; + } + } } diff --git a/core/src/main/java/org/testcontainers/utility/DockerLoggerFactory.java b/core/src/main/java/org/testcontainers/utility/DockerLoggerFactory.java index 6c013dcd692..69f7b998da2 100644 --- a/core/src/main/java/org/testcontainers/utility/DockerLoggerFactory.java +++ b/core/src/main/java/org/testcontainers/utility/DockerLoggerFactory.java @@ -9,10 +9,18 @@ public final class DockerLoggerFactory { public static Logger getLogger(String dockerImageName) { + + final String abbreviatedName; + if (dockerImageName.contains("@sha256")) { + abbreviatedName = dockerImageName.substring(0, dockerImageName.indexOf("@sha256") + 14) + "..."; + } else { + abbreviatedName = dockerImageName; + } + if ("UTF-8".equals(System.getProperty("file.encoding"))) { - return LoggerFactory.getLogger("\uD83D\uDC33 [" + dockerImageName + "]"); + return LoggerFactory.getLogger("\uD83D\uDC33 [" + abbreviatedName + "]"); } else { - return LoggerFactory.getLogger("docker[" + dockerImageName + "]"); + return LoggerFactory.getLogger("docker[" + abbreviatedName + "]"); } } } diff --git a/core/src/main/java/org/testcontainers/utility/LicenseAcceptance.java b/core/src/main/java/org/testcontainers/utility/LicenseAcceptance.java new file mode 100644 index 00000000000..55037d8978c --- /dev/null +++ b/core/src/main/java/org/testcontainers/utility/LicenseAcceptance.java @@ -0,0 +1,36 @@ +package org.testcontainers.utility; + +import com.google.common.base.Charsets; +import com.google.common.io.Resources; +import lombok.experimental.UtilityClass; + +import java.net.URL; +import java.util.List; + +/** + * Utility class to ensure that licenses have been accepted by the developer. + */ +@UtilityClass +public class LicenseAcceptance { + + private static final String ACCEPTANCE_FILE_NAME = "container-license-acceptance.txt"; + + public static void assertLicenseAccepted(final String imageName) { + try { + final URL url = Resources.getResource(ACCEPTANCE_FILE_NAME); + final List acceptedLicences = Resources.readLines(url, Charsets.UTF_8); + + if (acceptedLicences.stream().map(String::trim).anyMatch(imageName::equals)) { + return; + } + } catch (Exception ignored) { + // suppressed + } + + throw new IllegalStateException("The image " + imageName + " requires you to accept a license agreement. " + + "Please place a file at the root of the classpath named " + ACCEPTANCE_FILE_NAME + ", e.g. at " + + "src/test/resources/" + ACCEPTANCE_FILE_NAME + ". This file should contain the line:\n " + + imageName); + + } +} diff --git a/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java b/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java new file mode 100644 index 00000000000..f06ff40fc40 --- /dev/null +++ b/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java @@ -0,0 +1,42 @@ +package org.testcontainers.dockerclient; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; + +@RunWith(Parameterized.class) +public class ImagePullTest { + + private String image; + + @Parameterized.Parameters(name = "{0}") + public static String[] parameters() { + return new String[] { + "alpine:latest", + "alpine:3.6", + "alpine@sha256:8fd4b76819e1e5baac82bd0a3d03abfe3906e034cc5ee32100d12aaaf3956dc7", + "gliderlabs/alpine:latest", + "gliderlabs/alpine:3.5", + "gliderlabs/alpine@sha256:a19aa4a17a525c97e5a90a0c53a9f3329d2dc61b0a14df5447757a865671c085", + "quay.io/testcontainers/ryuk:latest", + "quay.io/testcontainers/ryuk:0.2.2", + "quay.io/testcontainers/ryuk@sha256:4b606e54c4bba1af4fd814019d342e4664d51e28d3ba2d18d24406edbefd66da" + }; + } + + public ImagePullTest(String image) { + this.image = image; + } + + @Test + public void test() { + try (final GenericContainer container = new GenericContainer<>(image) + .withCommand("/bin/sh", "-c", "sleep 0") + .withStartupCheckStrategy(new OneShotStartupCheckStrategy())) { + container.start(); + // do nothing other than start and stop + } + } +} diff --git a/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java b/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java index aea13f69068..3fedbc19df3 100644 --- a/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java +++ b/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java @@ -2,17 +2,46 @@ import org.junit.Test; +import static org.junit.Assert.fail; + public class DockerImageNameTest { @Test public void validNames() { - DockerImageName.validate("myname:latest"); // no repo - DockerImageName.validate("repo/my-name:1.0"); // no repo - DockerImageName.validate("repo.foo.com:1234/my-name:1.0"); // no repo + testValid("myname:latest"); + testValid("myname:latest"); + testValid("repo/my-name:1.0"); + testValid("repo.foo.com:1234/my-name:1.0"); + testValid("repo.foo.com/my-name:1.0"); + testValid("repo.foo.com:1234/repo_here/my-name:1.0"); + testValid("repo.foo.com:1234/repo-here/my-name@sha256:1234abcd1234abcd1234abcd1234abcd"); + testValid("repo.foo.com:1234/my-name@sha256:1234abcd1234abcd1234abcd1234abcd"); + testValid("1.2.3.4/my-name:1.0"); + testValid("1.2.3.4:1234/my-name:1.0"); + testValid("1.2.3.4/repo-here/my-name:1.0"); + testValid("1.2.3.4:1234/repo-here/my-name:1.0"); + } + + @Test + public void invalidNames() { + testInvalid("myname"); + testInvalid(":latest"); + testInvalid("/myname:latest"); + testInvalid("/myname@sha256:latest"); + testInvalid("/myname@sha256:gggggggggggggggggggggggggggggggg"); + testInvalid("repo:notaport/myname:latest"); } - @Test(expected = IllegalArgumentException.class) - public void missingTag() { - DockerImageName.validate("myname"); + private void testValid(String s) { + new DockerImageName(s).assertValid(); + } + + private void testInvalid(String myname) { + try { + new DockerImageName(myname).assertValid(); + fail(); + } catch (IllegalArgumentException expected) { + + } } -} \ No newline at end of file +} diff --git a/core/src/test/java/org/testcontainers/utility/LicenseAcceptanceTest.java b/core/src/test/java/org/testcontainers/utility/LicenseAcceptanceTest.java new file mode 100644 index 00000000000..63da017c88e --- /dev/null +++ b/core/src/test/java/org/testcontainers/utility/LicenseAcceptanceTest.java @@ -0,0 +1,17 @@ +package org.testcontainers.utility; + +import org.junit.Test; + +public class LicenseAcceptanceTest { + + @Test + public void testForExistingNames() { + LicenseAcceptance.assertLicenseAccepted("a"); + LicenseAcceptance.assertLicenseAccepted("b"); + } + + @Test(expected = IllegalStateException.class) + public void testForMissingNames() { + LicenseAcceptance.assertLicenseAccepted("c"); + } +} diff --git a/core/src/test/resources/container-license-acceptance.txt b/core/src/test/resources/container-license-acceptance.txt new file mode 100644 index 00000000000..bfdaa0f1c34 --- /dev/null +++ b/core/src/test/resources/container-license-acceptance.txt @@ -0,0 +1,3 @@ +a +b + diff --git a/docs/usage/modules.md b/docs/usage/modules.md index e8b91487625..788c5f561b2 100644 --- a/docs/usage/modules.md +++ b/docs/usage/modules.md @@ -1,10 +1,10 @@ # Contributed modules -The following modules have been largely developed outside of the Testcontainers core project and are maintained and released separately: +The following modules are available for specialised use cases: -* [MS SQL Server](https://github.com/testcontainers/testcontainers-java-module-mssqlserver) -* [Oracle XE](https://github.com/testcontainers/testcontainers-java-module-oracle-xe) -* [Hashicorp Vault](https://github.com/testcontainers/testcontainers-java-module-vault) -* [Dynalite](https://github.com/testcontainers/testcontainers-java-module-dynalite) (Amazon DynamoDB emulation) -* [Atlassian Localstack](https://github.com/testcontainers/testcontainers-java-module-localstack) (emulation of a broad set of various AWS services) -* [MariaDB](https://github.com/testcontainers/testcontainers-java-module-mariadb) \ No newline at end of file +* [MS SQL Server](../../modules/mssqlserver/README.md) - thank you to G DATA Software AG for contributing this module. +* [Oracle XE](../../modules/oracle-xe/README.md) +* [Dynalite](../../modules/dynalite/README.md) (Amazon DynamoDB emulation) +* [Atlassian Localstack](../../modules/localstack/README.md) (emulation of a broad set of various AWS services) +* [MariaDB](../../modules/mariadb/README.md) +* [Hashicorp Vault](../../modules/vault/README.md) - thank you to Capital One Services, LLC for contributing this module. diff --git a/modules/dynalite/README.md b/modules/dynalite/README.md new file mode 100644 index 00000000000..6fd01895eb5 --- /dev/null +++ b/modules/dynalite/README.md @@ -0,0 +1,47 @@ +# Testcontainers Dynalite Module + +Testcontainers module for [Dynalite](https://github.com/mhart/dynalite). Dynalite is a clone of DynamoDB, enabling local testing. + +## Usage example + +Running Dynalite as a stand-in for DynamoDB in a test: + +```java +public class SomeTest { + + @Rule + public DynaliteContainer dynamoDB = new DynaliteContainer(); + + @Test + public void someTestMethod() { + // getClient() returns a preconfigured DynamoDB client that is connected to the + // dynalite container + final AmazonDynamoDB client = dynamoDB.getClient(); + + ... interact with client as if using DynamoDB normally +``` + +## Why Dynalite for DynamoDB testing? + +In part, because it's light and quick to run. Also, please see the [reasons given](https://github.com/mhart/dynalite#why-not-amazons-dynamodb-local) by the author of Dynalite and the [problems with Amazon's DynamoDB Local](https://github.com/mhart/dynalite#problems-with-amazons-dynamodb-local-updated-2016-04-19). + +## Dependency information + +Replace `VERSION` with the [latest version available on Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.testcontainers%22). + +### Maven + +``` + + org.testcontainers + dynalite + VERSION + +``` + +### Gradle + +``` +compile group: 'org.testcontainers', name: 'dynalite', version: 'VERSION' +``` + diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle new file mode 100644 index 00000000000..1250ba677b3 --- /dev/null +++ b/modules/dynalite/build.gradle @@ -0,0 +1,8 @@ +description = "Testcontainers :: Dynalite" + +dependencies { + compile project(':testcontainers') + + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.126' + testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.126' +} diff --git a/modules/dynalite/src/main/java/org/testcontainers/dynamodb/DynaliteContainer.java b/modules/dynalite/src/main/java/org/testcontainers/dynamodb/DynaliteContainer.java new file mode 100644 index 00000000000..31a3a7914e8 --- /dev/null +++ b/modules/dynalite/src/main/java/org/testcontainers/dynamodb/DynaliteContainer.java @@ -0,0 +1,76 @@ +package org.testcontainers.dynamodb; + +import com.amazonaws.auth.AWSCredentialsProvider; +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.client.builder.AwsClientBuilder; +import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; +import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder; +import org.junit.rules.ExternalResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; + +/** + * Container for Dynalite, a DynamoDB clone. + */ +public class DynaliteContainer extends ExternalResource { + + private static final Logger LOGGER = LoggerFactory.getLogger(DynaliteContainer.class); + private final GenericContainer delegate; + + public DynaliteContainer() { + this("quay.io/testcontainers/dynalite:v1.2.1-1"); + } + + public DynaliteContainer(String imageName) { + this.delegate = new GenericContainer(imageName) + .withExposedPorts(4567) + .withLogConsumer(new Slf4jLogConsumer(LOGGER)); + } + + /** + * Gets a preconfigured {@link AmazonDynamoDB} client object for connecting to this + * container. + * + * @return preconfigured client + */ + public AmazonDynamoDB getClient() { + return AmazonDynamoDBClientBuilder.standard() + .withEndpointConfiguration(getEndpointConfiguration()) + .withCredentials(getCredentials()) + .build(); + } + + /** + * Gets {@link AwsClientBuilder.EndpointConfiguration} + * that may be used to connect to this container. + * + * @return endpoint configuration + */ + public AwsClientBuilder.EndpointConfiguration getEndpointConfiguration() { + return new AwsClientBuilder.EndpointConfiguration("http://" + + this.delegate.getContainerIpAddress() + ":" + + this.delegate.getMappedPort(4567), null); + } + + /** + * Gets an {@link AWSCredentialsProvider} that may be used to connect to this container. + * + * @return dummy AWS credentials + */ + public AWSCredentialsProvider getCredentials() { + return new AWSStaticCredentialsProvider(new BasicAWSCredentials("dummy", "dummy")); + } + + @Override + protected void before() throws Throwable { + delegate.start(); + } + + @Override + protected void after() { + delegate.stop(); + } +} diff --git a/modules/dynalite/src/test/java/org/testcontainers/dynamodb/DynaliteContainerTest.java b/modules/dynalite/src/test/java/org/testcontainers/dynamodb/DynaliteContainerTest.java new file mode 100644 index 00000000000..2d414aecb3b --- /dev/null +++ b/modules/dynalite/src/test/java/org/testcontainers/dynamodb/DynaliteContainerTest.java @@ -0,0 +1,52 @@ +package org.testcontainers.dynamodb; + +import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; +import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder; +import com.amazonaws.services.dynamodbv2.model.*; +import org.junit.Rule; +import org.junit.Test; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; + +public class DynaliteContainerTest { + + @Rule + public DynaliteContainer dynamoDB = new DynaliteContainer(); + + @Test + public void simpleTestWithManualClientCreation() { + final AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard() + .withEndpointConfiguration(dynamoDB.getEndpointConfiguration()) + .withCredentials(dynamoDB.getCredentials()) + .build(); + + runTest(client); + } + + @Test + public void simpleTestWithProvidedClient() { + final AmazonDynamoDB client = dynamoDB.getClient(); + + runTest(client); + } + + private void runTest(AmazonDynamoDB client) { + CreateTableRequest request = new CreateTableRequest() + .withAttributeDefinitions(new AttributeDefinition( + "Name", ScalarAttributeType.S)) + .withKeySchema(new KeySchemaElement("Name", KeyType.HASH)) + .withProvisionedThroughput(new ProvisionedThroughput( + new Long(10), new Long(10))) + .withTableName("foo"); + + + client.createTable(request); + + final TableDescription tableDescription = client.describeTable("foo").getTable(); + + assertNotNull("the description is not null", tableDescription); + assertEquals("the table has the right name", "foo", tableDescription.getTableName()); + assertEquals("the name has the right primary key", "Name", tableDescription.getKeySchema().get(0).getAttributeName()); + } +} \ No newline at end of file diff --git a/modules/dynalite/src/test/resources/logback-test.xml b/modules/dynalite/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..5d27573a3cb --- /dev/null +++ b/modules/dynalite/src/test/resources/logback-test.xml @@ -0,0 +1,24 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 262035ce027..0223781eecb 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -1,13 +1,27 @@ +repositories { + maven { + url "https://maven.atlassian.com/3rdparty/" + } +} + dependencies { compile project(':mysql') compile project(':postgresql') + compile project(':mariadb') + compile project(':oracle-xe') + compile project(':mssqlserver') testCompile 'com.google.guava:guava:18.0' testCompile 'org.postgresql:postgresql:42.0.0' testCompile 'mysql:mysql-connector-java:5.1.45' testCompile 'org.mariadb.jdbc:mariadb-java-client:1.4.6' + testCompile 'com.oracle:ojdbc6:12.1.0.1-atlassian-hosted' + testCompile 'com.microsoft.sqlserver:mssql-jdbc:6.1.0.jre8' + testCompile 'com.zaxxer:HikariCP-java6:2.3.8' testCompile 'org.apache.tomcat:tomcat-jdbc:8.5.4' testCompile 'org.vibur:vibur-dbcp:9.0' testCompile 'commons-dbutils:commons-dbutils:1.6' + + testCompile 'com.googlecode.junit-toolbox:junit-toolbox:2.4' } diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java index c87f7bce365..eee537a4821 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java @@ -1,5 +1,6 @@ package org.testcontainers.jdbc; +import com.googlecode.junittoolbox.ParallelParameterized; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import org.apache.commons.dbutils.QueryRunner; @@ -20,7 +21,7 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -@RunWith(Parameterized.class) +@RunWith(ParallelParameterized.class) public class JDBCDriverTest { @Parameter @@ -35,26 +36,32 @@ public class JDBCDriverTest { @Parameterized.Parameters(name = "{index} - {0}") public static Iterable data() { return asList( - new Object[][]{ - {"jdbc:tc:mysql:5.5.43://hostname/databasename", false, false, false}, - {"jdbc:tc:mysql://hostname/databasename?TC_INITSCRIPT=somepath/init_mysql.sql", true, false, false}, - {"jdbc:tc:mysql://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", true, false, false}, - {"jdbc:tc:mysql://hostname/databasename?useUnicode=yes&characterEncoding=utf8", false, true, false}, - {"jdbc:tc:mysql://hostname/databasename", false, false, false}, - {"jdbc:tc:mysql://hostname/databasename?useSSL=false", false, false, false}, - {"jdbc:tc:postgresql://hostname/databasename", false, false, false}, - {"jdbc:tc:mysql:5.6://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override", false, false, true}, - }); + new Object[][]{ + {"jdbc:tc:mysql:5.5.43://hostname/databasename", false, false, false}, + {"jdbc:tc:mysql://hostname/databasename?TC_INITSCRIPT=somepath/init_mysql.sql", true, false, false}, + {"jdbc:tc:mysql://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", true, false, false}, + {"jdbc:tc:mysql://hostname/databasename?useUnicode=yes&characterEncoding=utf8", false, true, false}, + {"jdbc:tc:mysql://hostname/databasename", false, false, false}, + {"jdbc:tc:mysql://hostname/databasename?useSSL=false", false, false, false}, + {"jdbc:tc:postgresql://hostname/databasename", false, false, false}, + {"jdbc:tc:mysql:5.6://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override", false, false, true}, + {"jdbc:tc:mariadb:10.1.16://hostname/databasename", false, false, false}, + {"jdbc:tc:mariadb://hostname/databasename", false, false, false}, + {"jdbc:tc:mariadb://hostname/databasename?useUnicode=yes&characterEncoding=utf8", false, true, false}, + {"jdbc:tc:mariadb://hostname/databasename?TC_INITSCRIPT=somepath/init_mariadb.sql", true, false, false}, + {"jdbc:tc:mariadb://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", true, false, false}, + {"jdbc:tc:mariadb:10.1.16://hostname/databasename?TC_MY_CNF=somepath/mariadb_conf_override", false, false, true} + }); } public static void sampleInitFunction(Connection connection) throws SQLException { connection.createStatement().execute("CREATE TABLE bar (\n" + - " foo VARCHAR(255)\n" + - ");"); + " foo VARCHAR(255)\n" + + ");"); connection.createStatement().execute("INSERT INTO bar (foo) VALUES ('hello world');"); connection.createStatement().execute("CREATE TABLE my_counter (\n" + - " n INT\n" + - ");"); + " n INT\n" + + ");"); } @AfterClass diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverWithPoolTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverWithPoolTest.java index 4c7e119996d..8f936863e7e 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverWithPoolTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverWithPoolTest.java @@ -1,5 +1,6 @@ package org.testcontainers.jdbc; +import com.googlecode.junittoolbox.ParallelParameterized; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import org.apache.commons.dbutils.QueryRunner; @@ -24,7 +25,7 @@ /** * */ -@RunWith(Parameterized.class) +@RunWith(ParallelParameterized.class) public class JDBCDriverWithPoolTest { public static final String URL = "jdbc:tc:mysql://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverWithPoolTest::sampleInitFunction"; diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMSSQLServerTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMSSQLServerTest.java new file mode 100644 index 00000000000..93904cfb94a --- /dev/null +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMSSQLServerTest.java @@ -0,0 +1,62 @@ +package org.testcontainers.junit; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import org.junit.Rule; +import org.junit.Test; +import org.testcontainers.containers.MSSQLServerContainer; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; + +/** + * @author Stefan Hufschmidt + */ +public class SimpleMSSQLServerTest { + + @Rule + public MSSQLServerContainer mssqlServer = new MSSQLServerContainer(); + + @Test + public void testSimple() throws SQLException { + HikariConfig hikariConfig = new HikariConfig(); + hikariConfig.setJdbcUrl(mssqlServer.getJdbcUrl()); + hikariConfig.setUsername(mssqlServer.getUsername()); + hikariConfig.setPassword(mssqlServer.getPassword()); + + HikariDataSource ds = new HikariDataSource(hikariConfig); + Statement statement = ds.getConnection().createStatement(); + statement.execute("SELECT 1"); + ResultSet resultSet = statement.getResultSet(); + + resultSet.next(); + int resultSetInt = resultSet.getInt(1); + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + } + + @Test + public void testSetupDatabase() throws SQLException { + HikariConfig hikariConfig = new HikariConfig(); + hikariConfig.setJdbcUrl(mssqlServer.getJdbcUrl()); + hikariConfig.setUsername(mssqlServer.getUsername()); + hikariConfig.setPassword(mssqlServer.getPassword()); + + HikariDataSource ds = new HikariDataSource(hikariConfig); + Statement statement = ds.getConnection().createStatement(); + statement.executeUpdate("CREATE DATABASE [test];"); + statement = ds.getConnection().createStatement(); + statement.executeUpdate("CREATE TABLE [test].[dbo].[Foo](ID INT PRIMARY KEY);"); + statement = ds.getConnection().createStatement(); + statement.executeUpdate("INSERT INTO [test].[dbo].[Foo] (ID) VALUES (3);"); + statement = ds.getConnection().createStatement(); + statement.execute("SELECT * FROM [test].[dbo].[Foo];"); + ResultSet resultSet = statement.getResultSet(); + + resultSet.next(); + int resultSetInt = resultSet.getInt("ID"); + assertEquals("A basic SELECT query succeeds", 3, resultSetInt); + } +} diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMariaDBTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMariaDBTest.java new file mode 100644 index 00000000000..1ad65e65465 --- /dev/null +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMariaDBTest.java @@ -0,0 +1,87 @@ +package org.testcontainers.junit; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import lombok.NonNull; + +import org.apache.commons.lang.SystemUtils; +import org.junit.Test; +import org.testcontainers.containers.MariaDBContainer; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.junit.Assume.assumeFalse; + + +/** + * @author Miguel Gonzalez Sanchez + */ +public class SimpleMariaDBTest { + + @Test + public void testSimple() throws SQLException { + MariaDBContainer mariadb = new MariaDBContainer(); + mariadb.start(); + + try { + ResultSet resultSet = performQuery(mariadb, "SELECT 1"); + int resultSetInt = resultSet.getInt(1); + + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + } finally { + mariadb.stop(); + } + } + + @Test + public void testSpecificVersion() throws SQLException { + MariaDBContainer mariadbOldVersion = new MariaDBContainer("mariadb:5.5.51"); + mariadbOldVersion.start(); + + try { + ResultSet resultSet = performQuery(mariadbOldVersion, "SELECT VERSION()"); + String resultSetString = resultSet.getString(1); + + assertTrue("The database version can be set using a container rule parameter", resultSetString.startsWith("5.5.51")); + } finally { + mariadbOldVersion.stop(); + } + } + + @Test + public void testMariaDBWithCustomIniFile() throws SQLException { + assumeFalse(SystemUtils.IS_OS_WINDOWS); + MariaDBContainer mariadbCustomConfig = new MariaDBContainer("mariadb:10.1.16") + .withConfigurationOverride("somepath/mariadb_conf_override"); + mariadbCustomConfig.start(); + + try { + ResultSet resultSet = performQuery(mariadbCustomConfig, "SELECT @@GLOBAL.innodb_file_format"); + String result = resultSet.getString(1); + + assertEquals("The InnoDB file format has been set by the ini file content", "Barracuda", result); + } finally { + mariadbCustomConfig.stop(); + } + } + + @NonNull + protected ResultSet performQuery(MariaDBContainer containerRule, String sql) throws SQLException { + HikariConfig hikariConfig = new HikariConfig(); + hikariConfig.setJdbcUrl(containerRule.getJdbcUrl()); + hikariConfig.setUsername(containerRule.getUsername()); + hikariConfig.setPassword(containerRule.getPassword()); + + HikariDataSource ds = new HikariDataSource(hikariConfig); + Statement statement = ds.getConnection().createStatement(); + statement.execute(sql); + ResultSet resultSet = statement.getResultSet(); + + resultSet.next(); + return resultSet; + } +} diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleOracleTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleOracleTest.java new file mode 100644 index 00000000000..5c47d3e2f92 --- /dev/null +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleOracleTest.java @@ -0,0 +1,39 @@ +package org.testcontainers.junit; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import org.junit.Rule; +import org.junit.Test; +import org.testcontainers.containers.OracleContainer; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; + +/** + * @author gusohal + */ +public class SimpleOracleTest { + + @Rule + public OracleContainer oracle = new OracleContainer(); + + @Test + public void testSimple() throws SQLException { + HikariConfig hikariConfig = new HikariConfig(); + hikariConfig.setJdbcUrl(oracle.getJdbcUrl()); + hikariConfig.setUsername(oracle.getUsername()); + hikariConfig.setPassword(oracle.getPassword()); + + HikariDataSource ds = new HikariDataSource(hikariConfig); + Statement statement = ds.getConnection().createStatement(); + statement.execute("SELECT 1 FROM dual"); + ResultSet resultSet = statement.getResultSet(); + + resultSet.next(); + int resultSetInt = resultSet.getInt(1); + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + } +} diff --git a/modules/jdbc-test/src/test/resources/container-license-acceptance.txt b/modules/jdbc-test/src/test/resources/container-license-acceptance.txt new file mode 100644 index 00000000000..b0c43ef2bff --- /dev/null +++ b/modules/jdbc-test/src/test/resources/container-license-acceptance.txt @@ -0,0 +1 @@ +microsoft/mssql-server-linux:latest diff --git a/modules/jdbc-test/src/test/resources/somepath/init_mariadb.sql b/modules/jdbc-test/src/test/resources/somepath/init_mariadb.sql new file mode 100644 index 00000000000..2b00ee968b0 --- /dev/null +++ b/modules/jdbc-test/src/test/resources/somepath/init_mariadb.sql @@ -0,0 +1,5 @@ +CREATE TABLE bar ( + foo VARCHAR(255) +); + +INSERT INTO bar (foo) VALUES ('hello world'); \ No newline at end of file diff --git a/modules/jdbc-test/src/test/resources/somepath/mariadb_conf_override/my.cnf b/modules/jdbc-test/src/test/resources/somepath/mariadb_conf_override/my.cnf new file mode 100644 index 00000000000..dcba17aff5c --- /dev/null +++ b/modules/jdbc-test/src/test/resources/somepath/mariadb_conf_override/my.cnf @@ -0,0 +1,55 @@ +[mysqld] +port = 3306 +#socket = /tmp/mysql.sock +skip-external-locking +key_buffer_size = 16K +max_allowed_packet = 1M +table_open_cache = 4 +sort_buffer_size = 64K +read_buffer_size = 256K +read_rnd_buffer_size = 256K +net_buffer_length = 2K +thread_stack = 128K +skip-host-cache +skip-name-resolve + + +innodb_file_format=Barracuda + + + + + +# Don't listen on a TCP/IP port at all. This can be a security enhancement, +# if all processes that need to connect to mysqld run on the same host. +# All interaction with mysqld must be made via Unix sockets or named pipes. +# Note that using this option without enabling named pipes on Windows +# (using the "enable-named-pipe" option) will render mysqld useless! +# +#skip-networking +#server-id = 1 + +# Uncomment the following if you want to log updates +#log-bin=mysql-bin + +# binary logging format - mixed recommended +#binlog_format=mixed + +# Causes updates to non-transactional engines using statement format to be +# written directly to binary log. Before using this option make sure that +# there are no dependencies between transactional and non-transactional +# tables such as in the statement INSERT INTO t_myisam SELECT * FROM +# t_innodb; otherwise, slaves may diverge from the master. +#binlog_direct_non_transactional_updates=TRUE + +# Uncomment the following if you are using InnoDB tables +innodb_data_file_path = ibdata1:10M:autoextend +# You can set .._buffer_pool_size up to 50 - 80 % +# of RAM but beware of setting memory usage too high +innodb_buffer_pool_size = 16M +#innodb_additional_mem_pool_size = 2M +# Set .._log_file_size to 25 % of buffer pool size +innodb_log_file_size = 5M +innodb_log_buffer_size = 8M +innodb_flush_log_at_trx_commit = 1 +innodb_lock_wait_timeout = 50 \ No newline at end of file diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index 3ebaa87223e..140aaa7dde3 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -1,6 +1,5 @@ package org.testcontainers.containers; -import java.util.concurrent.Future; import lombok.NonNull; import org.jetbrains.annotations.NotNull; import org.rnorth.ducttape.ratelimits.RateLimiter; @@ -15,6 +14,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Properties; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; /** @@ -29,9 +29,9 @@ public abstract class JdbcDatabaseContainer parameters = new HashMap<>(); private static final RateLimiter DB_CONNECT_RATE_LIMIT = RateLimiterBuilder.newBuilder() - .withRate(10, TimeUnit.SECONDS) - .withConstantThroughput() - .build(); + .withRate(10, TimeUnit.SECONDS) + .withConstantThroughput() + .build(); public JdbcDatabaseContainer(@NonNull final String dockerImageName) { super(dockerImageName); @@ -91,13 +91,13 @@ protected void waitUntilContainerStarted() { // Repeatedly try and open a connection to the DB and execute a test query logger().info("Waiting for database connection to become available at {} using query '{}'", getJdbcUrl(), getTestQueryString()); - Unreliables.retryUntilSuccess(120, TimeUnit.SECONDS, () -> { + Unreliables.retryUntilSuccess(getStartupTimeoutSeconds(), TimeUnit.SECONDS, () -> { if (!isRunning()) { throw new ContainerLaunchException("Container failed to start"); } - try (Connection connection = DB_CONNECT_RATE_LIMIT.getWhenReady(() -> createConnection(""))) { + try (Connection connection = createConnection("")) { boolean success = connection.createStatement().execute(JdbcDatabaseContainer.this.getTestQueryString()); if (success) { @@ -133,9 +133,8 @@ public Driver getJdbcDriverInstance() { /** * Creates a connection to the underlying containerized database instance. * - * @param queryString - * query string parameters that should be appended to the JDBC connection URL. - * The '?' character must be included + * @param queryString query string parameters that should be appended to the JDBC connection URL. + * The '?' character must be included * @return a Connection * @throws SQLException if there is a repeated failure to create the connection */ @@ -148,7 +147,9 @@ public Connection createConnection(String queryString) throws SQLException { final Driver jdbcDriverInstance = getJdbcDriverInstance(); try { - return Unreliables.retryUntilSuccess(120, TimeUnit.SECONDS, () -> jdbcDriverInstance.connect(url, info)); + return Unreliables.retryUntilSuccess(getConnectTimeoutSeconds(), TimeUnit.SECONDS, () -> + DB_CONNECT_RATE_LIMIT.getWhenReady(() -> + jdbcDriverInstance.connect(url, info))); } catch (Exception e) { throw new SQLException("Could not create new connection", e); } @@ -159,9 +160,8 @@ public Connection createConnection(String queryString) throws SQLException { * This should be overridden if the JDBC URL and query string concatenation or URL string * construction needs to be different to normal. * - * @param queryString - * query string parameters that should be appended to the JDBC connection URL. - * The '?' character must be included + * @param queryString query string parameters that should be appended to the JDBC connection URL. + * The '?' character must be included * @return a full JDBC URL including queryString */ protected String constructUrlForConnection(String queryString) { @@ -185,4 +185,18 @@ public void setParameters(Map parameters) { public void addParameter(String paramName, String value) { this.parameters.put(paramName, value); } + + /** + * @return startup time to allow, including image pull time, in seconds + */ + protected int getStartupTimeoutSeconds() { + return 120; + } + + /** + * @return time to allow for the database to start and establish an initial connection, in seconds + */ + protected int getConnectTimeoutSeconds() { + return 120; + } } diff --git a/modules/localstack/README.md b/modules/localstack/README.md new file mode 100644 index 00000000000..520cb0c147c --- /dev/null +++ b/modules/localstack/README.md @@ -0,0 +1,45 @@ +# Testcontainers LocalStack AWS testing module + +Testcontainers module for the Atlassian's [LocalStack](https://github.com/localstack/localstack), 'a fully functional local AWS cloud stack'. + +## Usage example + +Running LocalStack as a stand-in for AWS S3 during a test: + +```java +public class SomeTest { + + @Rule + public LocalStackContainer localstack = new LocalStackContainer() + .withServices(S3); + + @Test + public void someTestMethod() { + AmazonS3 s3 = AmazonS3ClientBuilder + .standard() + .withEndpointConfiguration(localstack.getEndpointConfiguration(S3)) + .withCredentials(localstack.getDefaultCredentialsProvider()) + .build(); + + s3.createBucket("foo"); + s3.putObject("foo", "bar", "baz"); +``` + +## Dependency information + +Replace `VERSION` with the [latest version available on Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.testcontainers%22). + +### Maven +``` + + org.testcontainers + localstack + VERSION + +``` + +### Gradle + +``` +compile group: 'org.testcontainers', name: 'localstack', version: 'VERSION' +``` diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle new file mode 100644 index 00000000000..94f0ee4070f --- /dev/null +++ b/modules/localstack/build.gradle @@ -0,0 +1,8 @@ +description = "Testcontainers :: Localstack" + +dependencies { + compile project(':testcontainers') + + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.126' + testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.126' +} diff --git a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java new file mode 100644 index 00000000000..18738de6ca6 --- /dev/null +++ b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java @@ -0,0 +1,166 @@ +package org.testcontainers.containers.localstack; + +import com.amazonaws.auth.AWSCredentialsProvider; +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.client.builder.AwsClientBuilder; +import org.jetbrains.annotations.Nullable; +import org.junit.rules.ExternalResource; +import org.rnorth.ducttape.Preconditions; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.LogMessageWaitStrategy; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.stream.Collectors; + +import static org.testcontainers.containers.BindMode.READ_WRITE; + +/** + *

Container for Atlassian Labs Localstack, 'A fully functional local AWS cloud stack'.

+ *

{@link LocalStackContainer#withServices(Service...)} should be used to select which services + * are to be launched. See {@link Service} for available choices. It is advised that + * {@link LocalStackContainer#getEndpointConfiguration(Service)} and + * {@link LocalStackContainer#getDefaultCredentialsProvider()} + * be used to obtain compatible endpoint configuration and credentials, respectively.

+ */ +public class LocalStackContainer extends ExternalResource { + + @Nullable private GenericContainer delegate; + private Service[] services; + + @Override + protected void before() throws Throwable { + + Preconditions.check("services list must not be empty", services != null && services.length > 0); + + final String servicesList = Arrays + .stream(services) + .map(Service::getLocalStackName) + .collect(Collectors.joining(",")); + + final Integer[] portsList = Arrays + .stream(services) + .map(Service::getPort) + .collect(Collectors.toSet()).toArray(new Integer[]{}); + + delegate = new GenericContainer("localstack/localstack:0.8.5") + .withExposedPorts(portsList) + .withFileSystemBind("//var/run/docker.sock", "/var/run/docker.sock", READ_WRITE) + .waitingFor(new LogMessageWaitStrategy().withRegEx(".*Ready\\.\n")) + .withEnv("SERVICES", servicesList); + + delegate.start(); + } + + @Override + protected void after() { + + Preconditions.check("delegate must have been created by before()", delegate != null); + + delegate.stop(); + } + + /** + * Declare a set of simulated AWS services that should be launched by this container. + * @param services one or more service names + * @return this container object + */ + public LocalStackContainer withServices(Service... services) { + this.services = services; + return this; + } + + /** + * Provides an endpoint configuration that is preconfigured to communicate with a given simulated service. + * The provided endpoint configuration should be set in the AWS Java SDK when building a client, e.g.: + *
AmazonS3 s3 = AmazonS3ClientBuilder
+            .standard()
+            .withEndpointConfiguration(localstack.getEndpointConfiguration(S3))
+            .withCredentials(localstack.getDefaultCredentialsProvider())
+            .build();
+     
+ * @param service the service that is to be accessed + * @return an {@link AwsClientBuilder.EndpointConfiguration} + */ + public AwsClientBuilder.EndpointConfiguration getEndpointConfiguration(Service service) { + + if (delegate == null) { + throw new IllegalStateException("LocalStack has not been started yet!"); + } + + final String address = delegate.getContainerIpAddress(); + String ipAddress = address; + try { + ipAddress = InetAddress.getByName(address).getHostAddress(); + } catch (UnknownHostException ignored) { + + } + ipAddress = ipAddress + ".xip.io"; + while (true) { + try { + //noinspection ResultOfMethodCallIgnored + InetAddress.getAllByName(ipAddress); + break; + } catch (UnknownHostException ignored) { + + } + } + + return new AwsClientBuilder.EndpointConfiguration( + "http://" + + ipAddress + + ":" + + delegate.getMappedPort(service.getPort()), "us-east-1"); + } + + /** + * Provides a {@link AWSCredentialsProvider} that is preconfigured to communicate with a given simulated service. + * The credentials provider should be set in the AWS Java SDK when building a client, e.g.: + *
AmazonS3 s3 = AmazonS3ClientBuilder
+            .standard()
+            .withEndpointConfiguration(localstack.getEndpointConfiguration(S3))
+            .withCredentials(localstack.getDefaultCredentialsProvider())
+            .build();
+     
+ * @return an {@link AWSCredentialsProvider} + */ + public AWSCredentialsProvider getDefaultCredentialsProvider() { + return new AWSStaticCredentialsProvider(new BasicAWSCredentials("accesskey", "secretkey")); + } + + public enum Service { + API_GATEWAY("apigateway", 4567), + KINESIS("kinesis", 4568), + DYNAMODB("dynamodb", 4569), + DYNAMODB_STREAMS("dynamodbstreams", 4570), + // TODO: Clarify usage for ELASTICSEARCH and ELASTICSEARCH_SERVICE +// ELASTICSEARCH("es", 4571), + S3("s3", 4572), + FIREHOSE("firehose", 4573), + LAMBDA("lambda", 4574), + SNS("sns", 4575), + SQS("sqs", 4576), + REDSHIFT("redshift", 4577), +// ELASTICSEARCH_SERVICE("", 4578), + SES("ses", 4579), + ROUTE53("route53", 4580), + CLOUDFORMATION("cloudformation", 4581), + CLOUDWATCH("cloudwatch", 4582); + + private final String localStackName; + private final int port; + + Service(String localstackName, int port) { + this.localStackName = localstackName; + this.port = port; + } + + public String getLocalStackName() { + return localStackName; + } + + public Integer getPort() { return port; } + } +} diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/SimpleLocalstackS3Test.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/SimpleLocalstackS3Test.java new file mode 100644 index 00000000000..f00423d31b7 --- /dev/null +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/SimpleLocalstackS3Test.java @@ -0,0 +1,52 @@ +package org.testcontainers.containers.localstack; + + +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import com.amazonaws.services.s3.model.Bucket; +import com.amazonaws.services.s3.model.ObjectListing; +import com.amazonaws.services.s3.model.S3Object; +import org.apache.commons.io.IOUtils; +import org.junit.Rule; +import org.junit.Test; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.List; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.testcontainers.containers.localstack.LocalStackContainer.Service.S3; + +public class SimpleLocalstackS3Test { + + @Rule + public LocalStackContainer localstack = new LocalStackContainer() + .withServices(S3); + + @Test + public void s3Test() throws IOException { + AmazonS3 s3 = AmazonS3ClientBuilder + .standard() + .withEndpointConfiguration(localstack.getEndpointConfiguration(S3)) + .withCredentials(localstack.getDefaultCredentialsProvider()) + .build(); + + s3.createBucket("foo"); + s3.putObject("foo", "bar", "baz"); + + final List buckets = s3.listBuckets(); + assertEquals("The created bucket is present", 1, buckets.size()); + final Bucket bucket = buckets.get(0); + + assertEquals("The created bucket has the right name", "foo", bucket.getName()); + assertEquals("The created bucket has the right name", "foo", bucket.getName()); + + final ObjectListing objectListing = s3.listObjects("foo"); + assertEquals("The created bucket has 1 item in it", 1, objectListing.getObjectSummaries().size()); + + final S3Object object = s3.getObject("foo", "bar"); + final String content = IOUtils.toString(object.getObjectContent(), Charset.forName("UTF-8")); + assertEquals("The object can be retrieved", "baz", content); + + } +} diff --git a/modules/localstack/src/test/resources/logback-test.xml b/modules/localstack/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..cc69fdf0ce0 --- /dev/null +++ b/modules/localstack/src/test/resources/logback-test.xml @@ -0,0 +1,27 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/mariadb/README.md b/modules/mariadb/README.md new file mode 100644 index 00000000000..20649b03582 --- /dev/null +++ b/modules/mariadb/README.md @@ -0,0 +1,40 @@ +# Testcontainers MariaDB Module + +Testcontainers module for the MariaDB database. + +## Usage example + +Running MariaDB as a stand-in for in a test: + +```java +public class SomeTest { + + @Rule + public MariaDBContainer mariaDB = new MariaDBContainer(); + + @Test + public void someTestMethod() { + String url = mariaDB.getJdbcUrl(); + + ... create a connection and run test as normal +``` + +## Dependency information + +Replace `VERSION` with the [latest version available on Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.testcontainers%22). + +### Maven + +``` + + org.testcontainers + mariadb + VERSION + +``` + +### Gradle + +``` +compile group: 'org.testcontainers', name: 'mariadb', version: 'VERSION' +``` diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle new file mode 100644 index 00000000000..ba6f4b97dcf --- /dev/null +++ b/modules/mariadb/build.gradle @@ -0,0 +1,11 @@ +description = "Testcontainers :: JDBC :: MariaDB" + +dependencies { + compile project(':jdbc') + + testCompile 'org.mariadb.jdbc:mariadb-java-client:1.5.9' + testCompile 'com.zaxxer:HikariCP-java6:2.3.8' + testCompile 'commons-dbutils:commons-dbutils:1.6' + testCompile 'org.apache.tomcat:tomcat-jdbc:8.5.4' + testCompile 'org.vibur:vibur-dbcp:9.0' +} diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java new file mode 100644 index 00000000000..2bea9b8ad7b --- /dev/null +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java @@ -0,0 +1,73 @@ +package org.testcontainers.containers; + +/** + * Container implementation for the MariaDB project. + * + * @author Miguel Gonzalez Sanchez + */ +public class MariaDBContainer> extends JdbcDatabaseContainer { + + public static final String NAME = "mariadb"; + public static final String IMAGE = "mariadb"; + private static final Integer MARIADB_PORT = 3306; + private static final String MARIADB_USER = "test"; + private static final String MARIADB_PASSWORD = "test"; + private static final String MARIADB_DATABASE = "test"; + private static final String MY_CNF_CONFIG_OVERRIDE_PARAM_NAME = "TC_MY_CNF"; + + public MariaDBContainer() { + super(IMAGE + ":latest"); + } + + public MariaDBContainer(String dockerImageName) { + super(dockerImageName); + } + + @Override + protected Integer getLivenessCheckPort() { + return getMappedPort(MARIADB_PORT); + } + + @Override + protected void configure() { + optionallyMapResourceParameterAsVolume(MY_CNF_CONFIG_OVERRIDE_PARAM_NAME, "/etc/mysql/conf.d", "mariadb-default-conf"); + + addExposedPort(MARIADB_PORT); + addEnv("MYSQL_DATABASE", MARIADB_DATABASE); + addEnv("MYSQL_USER", MARIADB_USER); + addEnv("MYSQL_PASSWORD", MARIADB_PASSWORD); + addEnv("MYSQL_ROOT_PASSWORD", MARIADB_PASSWORD); + setCommand("mysqld"); + setStartupAttempts(3); + } + + @Override + public String getDriverClassName() { + return "org.mariadb.jdbc.Driver"; + } + + @Override + public String getJdbcUrl() { + return "jdbc:mariadb://" + getContainerIpAddress() + ":" + getMappedPort(MARIADB_PORT) + "/test"; + } + + @Override + public String getUsername() { + return MARIADB_USER; + } + + @Override + public String getPassword() { + return MARIADB_PASSWORD; + } + + @Override + public String getTestQueryString() { + return "SELECT 1"; + } + + public SELF withConfigurationOverride(String s) { + parameters.put(MY_CNF_CONFIG_OVERRIDE_PARAM_NAME, s); + return self(); + } +} diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java new file mode 100644 index 00000000000..fbe9d807af4 --- /dev/null +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java @@ -0,0 +1,16 @@ +package org.testcontainers.containers; + +/** + * Factory for MariaDB org.testcontainers.containers. + */ +public class MariaDBContainerProvider extends JdbcDatabaseContainerProvider { + @Override + public boolean supports(String databaseType) { + return databaseType.equals(MariaDBContainer.NAME); + } + + @Override + public JdbcDatabaseContainer newInstance(String tag) { + return new MariaDBContainer(MariaDBContainer.IMAGE + ":" + tag); + } +} diff --git a/modules/mariadb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider b/modules/mariadb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider new file mode 100644 index 00000000000..f5b51466d19 --- /dev/null +++ b/modules/mariadb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider @@ -0,0 +1 @@ +org.testcontainers.containers.MariaDBContainerProvider \ No newline at end of file diff --git a/modules/mariadb/src/main/resources/mariadb-default-conf/my.cnf b/modules/mariadb/src/main/resources/mariadb-default-conf/my.cnf new file mode 100644 index 00000000000..87d4cfecd20 --- /dev/null +++ b/modules/mariadb/src/main/resources/mariadb-default-conf/my.cnf @@ -0,0 +1,48 @@ +[mysqld] +port = 3306 +#socket = /tmp/mysql.sock +skip-external-locking +key_buffer_size = 16K +max_allowed_packet = 1M +table_open_cache = 4 +sort_buffer_size = 64K +read_buffer_size = 256K +read_rnd_buffer_size = 256K +net_buffer_length = 2K +thread_stack = 512K +skip-host-cache +skip-name-resolve + +# Don't listen on a TCP/IP port at all. This can be a security enhancement, +# if all processes that need to connect to mysqld run on the same host. +# All interaction with mysqld must be made via Unix sockets or named pipes. +# Note that using this option without enabling named pipes on Windows +# (using the "enable-named-pipe" option) will render mysqld useless! +# +#skip-networking +#server-id = 1 + +# Uncomment the following if you want to log updates +#log-bin=mysql-bin + +# binary logging format - mixed recommended +#binlog_format=mixed + +# Causes updates to non-transactional engines using statement format to be +# written directly to binary log. Before using this option make sure that +# there are no dependencies between transactional and non-transactional +# tables such as in the statement INSERT INTO t_myisam SELECT * FROM +# t_innodb; otherwise, slaves may diverge from the master. +#binlog_direct_non_transactional_updates=TRUE + +# Uncomment the following if you are using InnoDB tables +innodb_data_file_path = ibdata1:10M:autoextend +# You can set .._buffer_pool_size up to 50 - 80 % +# of RAM but beware of setting memory usage too high +innodb_buffer_pool_size = 16M +#innodb_additional_mem_pool_size = 2M +# Set .._log_file_size to 25 % of buffer pool size +innodb_log_file_size = 5M +innodb_log_buffer_size = 8M +innodb_flush_log_at_trx_commit = 1 +innodb_lock_wait_timeout = 50 diff --git a/modules/mssqlserver/AUTHORS b/modules/mssqlserver/AUTHORS new file mode 100644 index 00000000000..bc3ac920da0 --- /dev/null +++ b/modules/mssqlserver/AUTHORS @@ -0,0 +1 @@ +Stefan Hufschmidt \ No newline at end of file diff --git a/modules/mssqlserver/LICENSE b/modules/mssqlserver/LICENSE new file mode 100644 index 00000000000..a3c58006a9c --- /dev/null +++ b/modules/mssqlserver/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 - 2019 G DATA Software AG and other authors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/modules/mssqlserver/README.md b/modules/mssqlserver/README.md new file mode 100644 index 00000000000..b0bf21fe19b --- /dev/null +++ b/modules/mssqlserver/README.md @@ -0,0 +1,52 @@ +# TestContainers MS SQL Server Module + +Testcontainers module for the MS SQL Server database. + +## Usage example + +Running MS SQL Server as a stand-in for in a test: + +```java +public class SomeTest { + + @Rule + public MSSQLServerContainer mssqlserver = new MSSQLServerContainer(); + + @Test + public void someTestMethod() { + String url = mssqlserver.getJdbcUrl(); + + ... create a connection and run test as normal +``` + +> *Note:* Due to licencing restrictions you are required to accept an EULA for this container image. To indicate that you accept the MS SQL Server image EULA, Please place a file at the root of the classpath named `container-license-acceptance.txt`, e.g. at `src/test/resources/container-license-acceptance.txt`. This file should contain the line: `microsoft/mssql-server-linux:latest` + +## Dependency information + +Replace `VERSION` with the [latest version available on Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.testcontainers%22). + +### Maven + +``` + + org.testcontainers + mssqlserver + VERSION + +``` + +### Gradle + +``` +compile group: 'org.testcontainers', name: 'mssqlserver', version: 'VERSION' +``` + +## License + +See [LICENSE](LICENSE). + +## Copyright + +Copyright (c) 2017 - 2019 G DATA Software AG and other authors. + +See [AUTHORS](AUTHORS) for contributors. diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle new file mode 100644 index 00000000000..62434722d63 --- /dev/null +++ b/modules/mssqlserver/build.gradle @@ -0,0 +1,5 @@ +description = "Testcontainers :: MS SQL Server" + +dependencies { + compile project(':jdbc') +} diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java new file mode 100644 index 00000000000..a44662a7f36 --- /dev/null +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java @@ -0,0 +1,62 @@ +package org.testcontainers.containers; + +import org.testcontainers.utility.LicenseAcceptance; + +/** + * @author Stefan Hufschmidt + */ +public class MSSQLServerContainer> extends JdbcDatabaseContainer { + static final String NAME = "mssqlserver"; + static final String IMAGE = "microsoft/mssql-server-linux"; + public static final Integer MS_SQL_SERVER_PORT = 1433; + private String username = "SA"; + private String password = "A_Str0ng_Required_Password"; + + public MSSQLServerContainer() { + this(IMAGE + ":latest"); + } + + public MSSQLServerContainer(final String dockerImageName) { + super(dockerImageName); + } + + @Override + protected Integer getLivenessCheckPort() { + return getMappedPort(MS_SQL_SERVER_PORT); + } + + @Override + protected void configure() { + addExposedPort(MS_SQL_SERVER_PORT); + + LicenseAcceptance.assertLicenseAccepted(this.getDockerImageName()); + addEnv("ACCEPT_EULA", "Y"); + + addEnv("SA_PASSWORD", password); + } + + @Override + public String getDriverClassName() { + return "com.microsoft.sqlserver.jdbc.SQLServerDriver"; + } + + @Override + public String getJdbcUrl() { + return "jdbc:sqlserver://" + getContainerIpAddress() + ":" + getMappedPort(MS_SQL_SERVER_PORT); + } + + @Override + public String getUsername() { + return username; + } + + @Override + public String getPassword() { + return password; + } + + @Override + public String getTestQueryString() { + return "SELECT 1"; + } +} diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainerProvider.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainerProvider.java new file mode 100644 index 00000000000..f7768482a45 --- /dev/null +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainerProvider.java @@ -0,0 +1,16 @@ +package org.testcontainers.containers; + +/** + * Factory for MS SQL Server containers. + */ +public class MSSQLServerContainerProvider extends JdbcDatabaseContainerProvider { + @Override + public boolean supports(String databaseType) { + return databaseType.equals(MSSQLServerContainer.NAME); + } + + @Override + public JdbcDatabaseContainer newInstance(String tag) { + return new MSSQLServerContainer(MSSQLServerContainer.IMAGE + ":" + tag); + } +} diff --git a/modules/mssqlserver/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider b/modules/mssqlserver/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider new file mode 100644 index 00000000000..09b0a068a7f --- /dev/null +++ b/modules/mssqlserver/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider @@ -0,0 +1 @@ +org.testcontainers.containers.MSSQLServerContainerProvider \ No newline at end of file diff --git a/modules/oracle-xe/README.md b/modules/oracle-xe/README.md new file mode 100644 index 00000000000..38def171fab --- /dev/null +++ b/modules/oracle-xe/README.md @@ -0,0 +1,40 @@ +# Testcontainers Oracle XE Module + +Testcontainers module for the Oracle XE database. + +## Usage example + +Running Oracle XE as a stand-in for in a test: + +```java +public class SomeTest { + + @Rule + public OracleContainer oracle = new OracleContainer(); + + @Test + public void someTestMethod() { + String url = oracle.getJdbcUrl(); + + ... create a connection and run test as normal +``` + +## Dependency information + +Replace `VERSION` with the [latest version available on Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.testcontainers%22). + +### Maven + +``` + + org.testcontainers + oracle-xe + VERSION + +``` + +### Gradle + +``` +compile group: 'org.testcontainers', name: 'oracle-xe', version: 'VERSION' +``` diff --git a/modules/oracle-xe/build.gradle b/modules/oracle-xe/build.gradle new file mode 100644 index 00000000000..6b4ab1af763 --- /dev/null +++ b/modules/oracle-xe/build.gradle @@ -0,0 +1,17 @@ +description = "Testcontainers :: JDBC :: Oracle XE" + +repositories { + maven { + url "https://maven.atlassian.com/3rdparty/" + } +} + +dependencies { + compile project(':jdbc') + + testCompile 'com.oracle:ojdbc6:12.1.0.1-atlassian-hosted' + testCompile 'com.zaxxer:HikariCP-java6:2.3.8' + testCompile 'commons-dbutils:commons-dbutils:1.6' + testCompile 'org.apache.tomcat:tomcat-jdbc:8.5.4' + testCompile 'org.vibur:vibur-dbcp:9.0' +} diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java new file mode 100644 index 00000000000..41dce1693a7 --- /dev/null +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java @@ -0,0 +1,78 @@ +package org.testcontainers.containers; + +import org.testcontainers.utility.TestcontainersConfiguration; + +/** + * @author gusohal + */ +public class OracleContainer extends JdbcDatabaseContainer { + + public static final String NAME = "oracle"; + public static final String IMAGE = TestcontainersConfiguration.getInstance() + .getProperties().getProperty("oracle.container.image","wnameless/oracle-xe-11g"); + private static final int ORACLE_PORT = 1521; + private static final int APEX_HTTP_PORT = 8080; + + public OracleContainer() { + super(IMAGE + "@sha256:15ff9ef50b4f90613c9780b589c57d98a8a9d496decec854316d96396ec5c085"); + } + + public OracleContainer(String dockerImageName) { + super(dockerImageName); + } + + @Override + protected Integer getLivenessCheckPort() { + return getMappedPort(ORACLE_PORT); + } + + @Override + protected void configure() { + + addExposedPorts(ORACLE_PORT, APEX_HTTP_PORT); + } + + @Override + public String getDriverClassName() { + return "oracle.jdbc.OracleDriver"; + } + + @Override + public String getJdbcUrl() { + return "jdbc:oracle:thin:" + getUsername() + "/" + getPassword() + "@//" + getContainerIpAddress() + ":" + getOraclePort() + "/" + getSid(); + } + + @Override + public String getUsername() { + return "system"; + } + + @Override + public String getPassword() { + return "oracle"; + } + + @SuppressWarnings("SameReturnValue") + public String getSid() { + return "xe"; + } + + public Integer getOraclePort() { + return getMappedPort(ORACLE_PORT); + } + + @SuppressWarnings("unused") + public Integer getWebPort() { + return getMappedPort(APEX_HTTP_PORT); + } + + @Override + public String getTestQueryString() { + return "SELECT 1 FROM DUAL"; + } + + @Override + protected int getStartupTimeoutSeconds() { + return 240; + } +} diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainerProvider.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainerProvider.java new file mode 100644 index 00000000000..1aea9f7e443 --- /dev/null +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainerProvider.java @@ -0,0 +1,16 @@ +package org.testcontainers.containers; + +/** + * Factory for Oracle containers. + */ +public class OracleContainerProvider extends JdbcDatabaseContainerProvider { + @Override + public boolean supports(String databaseType) { + return databaseType.equals(OracleContainer.NAME); + } + + @Override + public JdbcDatabaseContainer newInstance(String tag) { + return new OracleContainer(OracleContainer.IMAGE + ":" + tag); + } +} diff --git a/modules/oracle-xe/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider b/modules/oracle-xe/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider new file mode 100644 index 00000000000..f031b7f955a --- /dev/null +++ b/modules/oracle-xe/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider @@ -0,0 +1 @@ +org.testcontainers.containers.OracleContainerProvider \ No newline at end of file diff --git a/modules/oracle-xe/src/test/java/org/testcontainers/containers/OracleJDBCDriverTest.java b/modules/oracle-xe/src/test/java/org/testcontainers/containers/OracleJDBCDriverTest.java new file mode 100644 index 00000000000..61a8d9b8d4b --- /dev/null +++ b/modules/oracle-xe/src/test/java/org/testcontainers/containers/OracleJDBCDriverTest.java @@ -0,0 +1,48 @@ +package org.testcontainers.containers; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import org.apache.commons.dbutils.QueryRunner; +import org.apache.commons.dbutils.ResultSetHandler; +import org.junit.Test; + +import java.sql.ResultSet; +import java.sql.SQLException; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; + +/** + * @author gusohal + */ +public class OracleJDBCDriverTest { + + @Test + public void testOracleWithNoSpecifiedVersion() throws SQLException { + performSimpleTest("jdbc:tc:oracle://hostname/databasename"); + } + + + private void performSimpleTest(String jdbcUrl) throws SQLException { + HikariDataSource dataSource = getDataSource(jdbcUrl, 1); + new QueryRunner(dataSource).query("SELECT 1 FROM dual", new ResultSetHandler() { + @Override + public Object handle(ResultSet rs) throws SQLException { + rs.next(); + int resultSetInt = rs.getInt(1); + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + return true; + } + }); + dataSource.close(); + } + + private HikariDataSource getDataSource(String jdbcUrl, int poolSize) { + HikariConfig hikariConfig = new HikariConfig(); + hikariConfig.setJdbcUrl(jdbcUrl); + hikariConfig.setConnectionTestQuery("SELECT 1 FROM dual"); + hikariConfig.setMinimumIdle(1); + hikariConfig.setMaximumPoolSize(poolSize); + + return new HikariDataSource(hikariConfig); + } +} diff --git a/modules/vault/AUTHORS b/modules/vault/AUTHORS new file mode 100644 index 00000000000..528a7856aca --- /dev/null +++ b/modules/vault/AUTHORS @@ -0,0 +1 @@ +Michael Oswald \ No newline at end of file diff --git a/modules/vault/LICENSE b/modules/vault/LICENSE new file mode 100644 index 00000000000..957404f59b9 --- /dev/null +++ b/modules/vault/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Capital One Services, LLC and other authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/modules/vault/README.md b/modules/vault/README.md new file mode 100644 index 00000000000..9c2eb6bed7c --- /dev/null +++ b/modules/vault/README.md @@ -0,0 +1,68 @@ +# TestContainers Vault Module + +Testcontainers module for [Vault](https://github.com/hashicorp/vault). Vault is a tool for managing secrets. More information on Vault [here](https://www.vaultproject.io/). + +## Usage example + +Running Vault in your Junit tests is easily done with an @Rule or @ClassRule such as the following: + +```java +public class SomeTest { + + @ClassRule + public static VaultContainer vaultContainer = new VaultContainer<>() + .withVaultToken("my-root-token") + .withVaultPort(8200) + .withSecretInVault("secret/testing", "top_secret=password1","db_password=dbpassword1"); + + @Test + public void someTestMethod() { + //interact with Vault via the container's host, port and Vault token. + + //There are many integration clients for Vault so let's just define a general one here: + VaultClient client = new VaultClient( + vaultContainer.getContainerIpAddress(), + vaultContainer.getMappedPort(8200), + "my-root-token"); + + List secrets = client.readSecret("secret/testing"); + + } +``` + +## Why Vault in Junit tests? + +With the increasing popularity of Vault and secret management, applications are now needing to source secrets from Vault. +This can prove challenging in the development phase without a running Vault instance readily on hand. This library +aims to solve your apps integration testing with Vault. You can also use it to +test how your application behaves with Vault by writing different test scenarios in Junit. + +## Dependency information + +Replace `VERSION` with the [latest version available on Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.testcontainers%22). + +### Maven + +``` + + org.testcontainers + vault + VERSION + +``` + +### Gradle + +``` +compile group: 'org.testcontainers', name: 'vault', version: 'VERSION' +``` + +## License + +See [LICENSE](LICENSE). + +## Copyright + +Copyright (c) 2017 Capital One Services, LLC and other authors. + +See [AUTHORS](AUTHORS) for contributors. diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle new file mode 100644 index 00000000000..abff412b9a9 --- /dev/null +++ b/modules/vault/build.gradle @@ -0,0 +1,7 @@ +description = "Testcontainers :: Vault" + +dependencies { + compile project(':testcontainers') + + testCompile 'io.rest-assured:rest-assured:3.0.0' +} diff --git a/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java b/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java new file mode 100644 index 00000000000..3f0f7684e82 --- /dev/null +++ b/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java @@ -0,0 +1,133 @@ +package org.testcontainers.vault; + +import com.github.dockerjava.api.command.InspectContainerResponse; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.traits.LinkableContainer; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.github.dockerjava.api.model.Capability.IPC_LOCK; + + +/** + * GenericContainer subclass for Vault specific configuration and features. The main feature is the + * withSecretInVault method, where users can specify which secrets to be pre-loaded into Vault for + * their specific test scenario. + * + * Other helpful features include the withVaultPort, and withVaultToken methods for convenience. + */ +public class VaultContainer> extends GenericContainer + implements LinkableContainer { + + private static final String VAULT_PORT = "8200"; + + private boolean vaultPortRequested = false; + + private Map> secretsMap = new HashMap<>(); + + public VaultContainer() { + this("vault:0.7.0"); + } + + public VaultContainer(String dockerImageName) { + super(dockerImageName); + } + + @Override + protected void configure() { + setStartupAttempts(3); + withCreateContainerCmdModifier(cmd -> cmd.withCapAdd(IPC_LOCK)); + if(!isVaultPortRequested()){ + withEnv("VAULT_ADDR", "http://0.0.0.0:" + VAULT_PORT); + } + } + + @Override + protected void containerIsStarted(InspectContainerResponse containerInfo) { + addSecrets(); + } + + private void addSecrets() { + if(!secretsMap.isEmpty()){ + try { + this.execInContainer(buildExecCommand(secretsMap)).getStdout().contains("Success"); + } + catch (IOException | InterruptedException e) { + logger().error("Failed to add these secrets {} into Vault via exec command. Exception message: {}", secretsMap, e.getMessage()); + } + } + } + + private String[] buildExecCommand(Map> map) { + StringBuilder stringBuilder = new StringBuilder(); + map.forEach((path, secrets) -> { + stringBuilder.append(" && vault write " + path); + secrets.forEach(item -> stringBuilder.append(" " + item)); + }); + return new String[] { "/bin/sh", "-c", stringBuilder.toString().substring(4)}; + } + + /** + * Sets the Vault root token for the container so application tests can source secrets using the token + * + * @param token the root token value to set for Vault. + * @return this + */ + public SELF withVaultToken(String token) { + withEnv("VAULT_DEV_ROOT_TOKEN_ID", token); + withEnv("VAULT_TOKEN", token); + return self(); + } + + /** + * Sets the Vault port in the container as well as the port bindings for the host to reach the container over HTTP. + * + * @param port the port number you want to have the Vault container listen on for tests. + * @return this + */ + public SELF withVaultPort(int port){ + setVaultPortRequested(true); + String vaultPort = String.valueOf(port); + withEnv("VAULT_ADDR", "http://0.0.0.0:" + VAULT_PORT); + setPortBindings(Arrays.asList(vaultPort + ":" + VAULT_PORT)); + return self(); + } + + /** + * Pre-loads secrets into Vault container. User may specify one or more secrets and all will be added to each path + * that is specified. Thus this can be called more than once for multiple paths to be added to Vault. + * + * The secrets are added to vault directly after the container is up via the + * {@link #addSecrets() addSecrets}, called from {@link #containerIsStarted(InspectContainerResponse) containerIsStarted} + * + * @param path specific Vault path to store specified secrets + * @param firstSecret first secret to add to specifed path + * @param remainingSecrets var args list of secrets to add to specified path + * @return this + */ + public SELF withSecretInVault(String path, String firstSecret, String... remainingSecrets) { + List list = new ArrayList<>(); + list.add(firstSecret); + for(String secret : remainingSecrets) { + list.add(secret); + } + if (secretsMap.containsKey(path)) { + list.addAll(list); + } + secretsMap.putIfAbsent(path,list); + return self(); + } + + private void setVaultPortRequested(boolean vaultPortRequested) { + this.vaultPortRequested = vaultPortRequested; + } + + private boolean isVaultPortRequested() { + return vaultPortRequested; + } +} \ No newline at end of file diff --git a/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java b/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java new file mode 100644 index 00000000000..5e1c55b33ac --- /dev/null +++ b/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java @@ -0,0 +1,79 @@ +package org.testcontainers.vault; + +import org.junit.ClassRule; +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.Wait; + +import java.io.IOException; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertThat; + +/** + * This test shows the pattern to use the VaultContainer @ClassRule for a junit test. It also has tests that ensure + * the secrets were added correctly by reading from Vault with the CLI and over HTTP. + */ +public class VaultContainerTest { + + private static final int VAULT_PORT = 8201; //using non-default port to show other ports can be passed besides 8200 + + private static final String VAULT_TOKEN = "my-root-token"; + + @ClassRule + public static VaultContainer vaultContainer = new VaultContainer<>() + .withVaultToken(VAULT_TOKEN) + .withVaultPort(VAULT_PORT) + .withSecretInVault("secret/testing1", "top_secret=password123") + .withSecretInVault("secret/testing2", "secret_one=password1", + "secret_two=password2", "secret_three=password3", "secret_three=password3", + "secret_four=password4") + .waitingFor(Wait.forHttp("/v1/secret/testing1").forStatusCode(400)); + + @Test + public void readFirstSecretPathWithCli() throws IOException, InterruptedException { + GenericContainer.ExecResult result = vaultContainer.execInContainer("vault", + "read", "-field=top_secret", "secret/testing1"); + assertThat(result.getStdout(), containsString("password123")); + } + + @Test + public void readSecondSecretPathWithCli() throws IOException, InterruptedException { + GenericContainer.ExecResult result = vaultContainer.execInContainer("vault", + "read", "secret/testing2"); + String output = result.getStdout(); + assertThat(output, containsString("password1")); + assertThat(output, containsString("password2")); + assertThat(output, containsString("password3")); + assertThat(output, containsString("password4")); + } + + @Test + public void readFirstSecretPathOverHttpApi() throws InterruptedException { + given(). + header("X-Vault-Token", VAULT_TOKEN). + when(). + get("http://"+getHostAndPort()+"/v1/secret/testing1"). + then(). + assertThat().body("data.top_secret", equalTo("password123")); + } + + @Test + public void readSecondecretPathOverHttpApi() throws InterruptedException { + given(). + header("X-Vault-Token", VAULT_TOKEN). + when(). + get("http://"+getHostAndPort()+"/v1/secret/testing2"). + then(). + assertThat().body("data.secret_one", containsString("password1")). + assertThat().body("data.secret_two", containsString("password2")). + assertThat().body("data.secret_three", containsString("password3")). + assertThat().body("data.secret_four", containsString("password4")); + } + + private String getHostAndPort(){ + return vaultContainer.getContainerIpAddress()+":"+vaultContainer.getMappedPort(8200); + } +} diff --git a/modules/vault/src/test/resources/logback-test.xml b/modules/vault/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..5d27573a3cb --- /dev/null +++ b/modules/vault/src/test/resources/logback-test.xml @@ -0,0 +1,24 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + + + + + + + + + \ No newline at end of file From ac1d4affa6c54f6798fe85021498fadeb20e0f3d Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Tue, 3 Apr 2018 13:04:42 +0200 Subject: [PATCH 0261/5615] Add DockerHealthcheckWaitStrategy (#618) Also adds a isHealthy() method to the ContainerState interface. --- CHANGELOG.md | 1 + .../containers/ContainerState.java | 33 +++++++++++++++-- .../DockerHealthcheckWaitStrategy.java | 25 +++++++++++++ .../containers/wait/strategy/Wait.java | 9 +++++ .../DockerHealthcheckWaitStrategyTest.java | 36 +++++++++++++++++++ .../Dockerfile | 8 +++++ .../write_file_and_loop.sh | 8 +++++ docs/usage/options.md | 8 +++++ 8 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/containers/wait/strategy/DockerHealthcheckWaitStrategy.java create mode 100644 core/src/test/java/org/testcontainers/containers/wait/strategy/DockerHealthcheckWaitStrategyTest.java create mode 100644 core/src/test/resources/health-wait-strategy-dockerfile/Dockerfile create mode 100755 core/src/test/resources/health-wait-strategy-dockerfile/write_file_and_loop.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 8701480fc13..4ee347a4ebf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ All notable changes to this project will be documented in this file. - Deprecated `WaitStrategy` and implementations in favour of classes with same names in `org.testcontainers.containers.strategy` ([\#600](https://github.com/testcontainers/testcontainers-java/pull/600)) - Added `ContainerState` interface representing the state of a started container ([\#600](https://github.com/testcontainers/testcontainers-java/pull/600)) - Added `WaitStrategyTarget` interface which is the target of the new `WaitStrategy` ([\#600](https://github.com/testcontainers/testcontainers-java/pull/600)) +- Added `DockerHealthcheckWaitStrategy` that is based on Docker's built-in [healthcheck](https://docs.docker.com/engine/reference/builder/#healthcheck) ([\#618](https://github.com/testcontainers/testcontainers-java/pull/618)). ## [1.6.0] - 2018-01-28 diff --git a/core/src/main/java/org/testcontainers/containers/ContainerState.java b/core/src/main/java/org/testcontainers/containers/ContainerState.java index 842a1cb6bde..4fc46c64989 100644 --- a/core/src/main/java/org/testcontainers/containers/ContainerState.java +++ b/core/src/main/java/org/testcontainers/containers/ContainerState.java @@ -15,6 +15,8 @@ public interface ContainerState { + String STATE_HEALTHY = "healthy"; + /** * Get the IP address that this container may be reached on (may not be the local machine). * @@ -27,14 +29,41 @@ default String getContainerIpAddress() { /** * @return is the container currently running? */ - default Boolean isRunning() { + default boolean isRunning() { + if (getContainerId() == null) { + return false; + } + + try { + Boolean running = getCurrentContainerInfo().getState().getRunning(); + return Boolean.TRUE.equals(running); + } catch (DockerException e) { + return false; + } + } + + /** + * @return has the container health state 'healthy'? + */ + default boolean isHealthy() { + if (getContainerId() == null) { + return false; + } + try { - return getContainerId() != null && DockerClientFactory.instance().client().inspectContainerCmd(getContainerId()).exec().getState().getRunning(); + InspectContainerResponse inspectContainerResponse = getCurrentContainerInfo(); + String healthStatus = inspectContainerResponse.getState().getHealth().getStatus(); + + return healthStatus.equals(STATE_HEALTHY); } catch (DockerException e) { return false; } } + default InspectContainerResponse getCurrentContainerInfo() { + return DockerClientFactory.instance().client().inspectContainerCmd(getContainerId()).exec(); + } + /** * Get the actual mapped port for a first port exposed by the container. * diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/DockerHealthcheckWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/DockerHealthcheckWaitStrategy.java new file mode 100644 index 00000000000..f2fa797327b --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/DockerHealthcheckWaitStrategy.java @@ -0,0 +1,25 @@ +package org.testcontainers.containers.wait.strategy; + +import org.rnorth.ducttape.TimeoutException; +import org.rnorth.ducttape.unreliables.Unreliables; +import org.testcontainers.containers.ContainerLaunchException; + +import java.util.concurrent.TimeUnit; + +/** + * Wait strategy leveraging Docker's built-in healthcheck mechanism. + * + * @see https://docs.docker.com/engine/reference/builder/#healthcheck + */ +public class DockerHealthcheckWaitStrategy extends AbstractWaitStrategy { + + @Override + protected void waitUntilReady() { + + try { + Unreliables.retryUntilTrue((int) startupTimeout.getSeconds(), TimeUnit.SECONDS, waitStrategyTarget::isHealthy); + } catch (TimeoutException e) { + throw new ContainerLaunchException("Timed out waiting for container to become healthy"); + } + } +} diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/Wait.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/Wait.java index 789927060b3..e839a11d91f 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/Wait.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/Wait.java @@ -61,4 +61,13 @@ public static HttpWaitStrategy forHttps(String path) { public static LogMessageWaitStrategy forLogMessage(String regex, int times) { return new LogMessageWaitStrategy().withRegEx(regex).withTimes(times); } + + /** + * Convenience method to return a WaitStrategy leveraging Docker's built-in healthcheck. + * + * @return DockerHealthcheckWaitStrategy + */ + public static DockerHealthcheckWaitStrategy forHealthcheck() { + return new DockerHealthcheckWaitStrategy(); + } } diff --git a/core/src/test/java/org/testcontainers/containers/wait/strategy/DockerHealthcheckWaitStrategyTest.java b/core/src/test/java/org/testcontainers/containers/wait/strategy/DockerHealthcheckWaitStrategyTest.java new file mode 100644 index 00000000000..d867233f5d7 --- /dev/null +++ b/core/src/test/java/org/testcontainers/containers/wait/strategy/DockerHealthcheckWaitStrategyTest.java @@ -0,0 +1,36 @@ +package org.testcontainers.containers.wait.strategy; + +import org.junit.Before; +import org.junit.Test; +import org.testcontainers.containers.ContainerLaunchException; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.images.builder.ImageFromDockerfile; + +import java.time.Duration; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; + +public class DockerHealthcheckWaitStrategyTest { + + private GenericContainer container; + + @Before + public void setUp() { + // Using a Dockerfile here, since Dockerfile builder DSL doesn't support HEALTHCHECK + container = new GenericContainer(new ImageFromDockerfile() + .withFileFromClasspath("write_file_and_loop.sh", "health-wait-strategy-dockerfile/write_file_and_loop.sh") + .withFileFromClasspath("Dockerfile", "health-wait-strategy-dockerfile/Dockerfile")) + .waitingFor(Wait.forHealthcheck().withStartupTimeout(Duration.ofSeconds(3))); + } + + @Test + public void startsOnceHealthy() { + container.start(); + } + + @Test + public void containerStartFailsIfContainerIsUnhealthy() { + container.withCommand("tail", "-f", "/dev/null"); + assertThrows("Container launch fails when unhealthy", ContainerLaunchException.class, container::start); + } +} diff --git a/core/src/test/resources/health-wait-strategy-dockerfile/Dockerfile b/core/src/test/resources/health-wait-strategy-dockerfile/Dockerfile new file mode 100644 index 00000000000..ddb0f82bce7 --- /dev/null +++ b/core/src/test/resources/health-wait-strategy-dockerfile/Dockerfile @@ -0,0 +1,8 @@ +FROM alpine:3.7 + +HEALTHCHECK --interval=1s CMD test -e /testfile + +COPY write_file_and_loop.sh write_file_and_loop.sh +RUN chmod +x write_file_and_loop.sh + +CMD ["/write_file_and_loop.sh"] diff --git a/core/src/test/resources/health-wait-strategy-dockerfile/write_file_and_loop.sh b/core/src/test/resources/health-wait-strategy-dockerfile/write_file_and_loop.sh new file mode 100755 index 00000000000..a102129394b --- /dev/null +++ b/core/src/test/resources/health-wait-strategy-dockerfile/write_file_and_loop.sh @@ -0,0 +1,8 @@ +#!/bin/ash + +echo sleeping +sleep 2 +echo writing file +touch /testfile + +while true; do sleep 1; done diff --git a/docs/usage/options.md b/docs/usage/options.md index 83211cc8f64..232cedb3780 100644 --- a/docs/usage/options.md +++ b/docs/usage/options.md @@ -109,6 +109,14 @@ public static GenericContainer elasticsearch = .usingTls()); ``` +If the used image supports Docker's [Healthcheck](https://docs.docker.com/engine/reference/builder/#healthcheck) feature, you can directly leverage the `healthy` state of the container as your wait condition: +```java +@ClassRule2.32.3 +public static GenericContainer container = + new GenericContainer("image-with-healthcheck:4.2") + .waitingFor(Wait.forHealthcheck()); +``` + For futher options, check out the `Wait` convenience class, or the various subclasses of `WaitStrategy`. If none of these options meet your requirements, you can create your own subclass of `AbstractWaitStrategy` with an appropriate wait mechanism in `waitUntilReady()`. The `GenericContainer.waitingFor()` method accepts any valid `WaitStrategy`. From 0abaf4a8e6865dbbc4ef3b797a014058078a19bd Mon Sep 17 00:00:00 2001 From: LAMY Julien Date: Wed, 4 Apr 2018 21:51:49 +0200 Subject: [PATCH 0262/5615] Add compatibility with selenium greater than 3.X (#632) * Add compatibility with selenium greater than 3.X * Add unit tests for compatibility Selenium > 3 --- AUTHORS | 1 + CHANGELOG.md | 1 + .../containers/SeleniumUtils.java | 35 ++++++++++++----- .../junit/SeleniumUtilsTest.java | 39 +++++++++++++++++++ .../resources/manifests/MANIFEST-2.45.0.MF | 11 ++++++ .../resources/manifests/MANIFEST-3.5.2.MF | 8 ++++ 6 files changed, 86 insertions(+), 9 deletions(-) create mode 100644 modules/selenium/src/test/java/org/testcontainers/junit/SeleniumUtilsTest.java create mode 100644 modules/selenium/src/test/resources/manifests/MANIFEST-2.45.0.MF create mode 100755 modules/selenium/src/test/resources/manifests/MANIFEST-3.5.2.MF diff --git a/AUTHORS b/AUTHORS index f67f444bf02..26ab8313e53 100644 --- a/AUTHORS +++ b/AUTHORS @@ -9,3 +9,4 @@ Asaf Mesika Sergei Egorov Pete Cornish Miguel Gonzalez Sanchez +Julien LAMY diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ee347a4ebf..c214d89bdf5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. - Fixed `getServicePort` on `DockerComposeContainer` throws NullPointerException if service instance number in not used. ([\#619](https://github.com/testcontainers/testcontainers-java/issues/619)) ### Changed +- Added compatibility with selenium greater than 3.X ([\#611](https://github.com/testcontainers/testcontainers-java/issues/611)) - Abstracted and changed database init script functionality to support use of SQL-like scripts with non-JDBC connections. ([\#551](https://github.com/testcontainers/testcontainers-java/pull/551)) - Added `JdbcDatabaseContainer(Future)` constructor. ([\#543](https://github.com/testcontainers/testcontainers-java/issues/543)) - Mark DockerMachineClientProviderStrategy as not persistable ([\#593](https://github.com/testcontainers/testcontainers-java/pull/593)) diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/SeleniumUtils.java b/modules/selenium/src/main/java/org/testcontainers/containers/SeleniumUtils.java index 9c0fd822a66..1a6319d3103 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/SeleniumUtils.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/SeleniumUtils.java @@ -38,17 +38,12 @@ public static String determineClasspathSeleniumVersion() { Manifest manifest = new Manifest(); manifest.read(is); - Attributes buildInfo = manifest.getAttributes("Build-Info"); - if (buildInfo != null) { - String seleniumVersion = buildInfo.getValue("Selenium-Version"); - - if (seleniumVersion != null) { - seleniumVersions.add(seleniumVersion); - LOGGER.info("Selenium API version {} detected on classpath", seleniumVersion); - } + String seleniumVersion = getSeleniumVersionFromManifest(manifest); + if (seleniumVersion != null) { + seleniumVersions.add(seleniumVersion); + LOGGER.info("Selenium API version {} detected on classpath", seleniumVersion); } } - } } catch (Exception e) { @@ -67,4 +62,26 @@ public static String determineClasspathSeleniumVersion() { return foundVersion; } + + /** + * Read Manifest to get Selenium Version. + * @param manifest + * @return Selenium Version detected + */ + public static String getSeleniumVersionFromManifest(Manifest manifest) { + String seleniumVersion = null; + Attributes buildInfo = manifest.getAttributes("Build-Info"); + if (buildInfo != null) { + seleniumVersion = buildInfo.getValue("Selenium-Version"); + } + + // Compatibility Selenium > 3.X + if(seleniumVersion == null) { + Attributes seleniumInfo = manifest.getAttributes("Selenium"); + if (seleniumInfo != null) { + seleniumVersion = seleniumInfo.getValue("Selenium-Version"); + } + } + return seleniumVersion; + } } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/SeleniumUtilsTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/SeleniumUtilsTest.java new file mode 100644 index 00000000000..d706d0fc390 --- /dev/null +++ b/modules/selenium/src/test/java/org/testcontainers/junit/SeleniumUtilsTest.java @@ -0,0 +1,39 @@ +package org.testcontainers.junit; + +import org.junit.Test; +import org.testcontainers.containers.SeleniumUtils; + +import java.io.IOException; +import java.util.jar.Manifest; + +import static org.rnorth.visibleassertions.VisibleAssertions.*; + + +/** + * Created by Julien LAMY + */ +public class SeleniumUtilsTest { + + @Test + public void detectSeleniumVersionUnder3() throws IOException { + checkSeleniumVersionDetected("manifests/MANIFEST-2.45.0.MF", "2.45.0"); + } + + @Test + public void detectSeleniumVersionUpper3() throws IOException { + checkSeleniumVersionDetected("manifests/MANIFEST-3.5.2.MF", "3.5.2"); + } + + /** + * Check if Selenium Version detected is the correct one. + * @param urlManifest : manifest file + * @throws IOException + */ + private void checkSeleniumVersionDetected(String urlManifest, String expectedVersion) throws IOException { + Manifest manifest = new Manifest(); + manifest.read(this.getClass().getClassLoader().getResourceAsStream(urlManifest)); + String seleniumVersion = SeleniumUtils.getSeleniumVersionFromManifest(manifest); + assertEquals("Check if Selenium Version detected is the correct one.", expectedVersion, seleniumVersion); + } + +} diff --git a/modules/selenium/src/test/resources/manifests/MANIFEST-2.45.0.MF b/modules/selenium/src/test/resources/manifests/MANIFEST-2.45.0.MF new file mode 100644 index 00000000000..ad494a3ec0e --- /dev/null +++ b/modules/selenium/src/test/resources/manifests/MANIFEST-2.45.0.MF @@ -0,0 +1,11 @@ +Manifest-Version: 1.0 +Built-By: linman +Build-Jdk: 1.7.0_65 +Created-By: Apache Maven 3.1.1 +Archiver-Version: Plexus Archiver + +Name: Build-Info +Selenium-Revision: 5017cb8e7ca8e37638dc3091b2440b90a1d8686f +Selenium-Version: 2.45.0 +Selenium-Build-Time: 2015-02-27 09:10:26 + diff --git a/modules/selenium/src/test/resources/manifests/MANIFEST-3.5.2.MF b/modules/selenium/src/test/resources/manifests/MANIFEST-3.5.2.MF new file mode 100755 index 00000000000..772380fed70 --- /dev/null +++ b/modules/selenium/src/test/resources/manifests/MANIFEST-3.5.2.MF @@ -0,0 +1,8 @@ +Manifest-Version: 1.0 + +Name: Build-Info +Build-Revision: 21ac65f960 +Build-Time: 2017-08-21T20:29:34.71Z +Build-User: lamaille +Selenium-Version: 3.5.2 + From be3d206e288da129b978f2b616bac55d7466a2ef Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 7 Apr 2018 16:36:46 +0100 Subject: [PATCH 0263/5615] Remove hardcoded Oracle image name (#628) --- CHANGELOG.md | 1 + .../test/resources/testcontainers.properties | 1 + .../containers/OracleContainer.java | 17 +++++++++-- .../containers/OracleContainerProvider.java | 7 ++++- .../src/test/resources/logback-test.xml | 29 +++++++++++++++++++ .../test/resources/testcontainers.properties | 1 + 6 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 modules/jdbc-test/src/test/resources/testcontainers.properties create mode 100644 modules/oracle-xe/src/test/resources/logback-test.xml create mode 100644 modules/oracle-xe/src/test/resources/testcontainers.properties diff --git a/CHANGELOG.md b/CHANGELOG.md index c214d89bdf5..0c836a63903 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ All notable changes to this project will be documented in this file. - Deprecated `WaitStrategy` and implementations in favour of classes with same names in `org.testcontainers.containers.strategy` ([\#600](https://github.com/testcontainers/testcontainers-java/pull/600)) - Added `ContainerState` interface representing the state of a started container ([\#600](https://github.com/testcontainers/testcontainers-java/pull/600)) - Added `WaitStrategyTarget` interface which is the target of the new `WaitStrategy` ([\#600](https://github.com/testcontainers/testcontainers-java/pull/600)) +- *Breaking:* Removed hard-coded `wnameless` Oracle database image name. Users should instead place a file on the classpath named `testcontainers.properties` containing `oracle.container.image=IMAGE`, where IMAGE is a suitable image name and tag/SHA hash. For information, the approach recommended by Oracle for creating an Oracle XE docker image is described [here](https://blogs.oracle.com/oraclewebcentersuite/implement-oracle-database-xe-as-docker-containers). - Added `DockerHealthcheckWaitStrategy` that is based on Docker's built-in [healthcheck](https://docs.docker.com/engine/reference/builder/#healthcheck) ([\#618](https://github.com/testcontainers/testcontainers-java/pull/618)). ## [1.6.0] - 2018-01-28 diff --git a/modules/jdbc-test/src/test/resources/testcontainers.properties b/modules/jdbc-test/src/test/resources/testcontainers.properties new file mode 100644 index 00000000000..19b5c4680e8 --- /dev/null +++ b/modules/jdbc-test/src/test/resources/testcontainers.properties @@ -0,0 +1 @@ +oracle.container.image=wnameless/oracle-xe-11g@sha256:15ff9ef50b4f90613c9780b589c57d98a8a9d496decec854316d96396ec5c085 diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java index 41dce1693a7..3cb6d888ebd 100644 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java @@ -8,13 +8,24 @@ public class OracleContainer extends JdbcDatabaseContainer { public static final String NAME = "oracle"; - public static final String IMAGE = TestcontainersConfiguration.getInstance() - .getProperties().getProperty("oracle.container.image","wnameless/oracle-xe-11g"); + private static final int ORACLE_PORT = 1521; private static final int APEX_HTTP_PORT = 8080; + private static String resolveImageName() { + String image = TestcontainersConfiguration.getInstance() + .getProperties().getProperty("oracle.container.image"); + + if (image == null) { + throw new IllegalStateException("An image to use for Oracle containers must be configured. " + + "To do this, please place a file on the classpath named `testcontainers.properties`, " + + "containing `oracle.container.image=IMAGE`, where IMAGE is a suitable image name and tag."); + } + return image; + } + public OracleContainer() { - super(IMAGE + "@sha256:15ff9ef50b4f90613c9780b589c57d98a8a9d496decec854316d96396ec5c085"); + super(resolveImageName()); } public OracleContainer(String dockerImageName) { diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainerProvider.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainerProvider.java index 1aea9f7e443..4ce0fb2cdfb 100644 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainerProvider.java +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainerProvider.java @@ -11,6 +11,11 @@ public boolean supports(String databaseType) { @Override public JdbcDatabaseContainer newInstance(String tag) { - return new OracleContainer(OracleContainer.IMAGE + ":" + tag); + + if (!tag.equalsIgnoreCase("latest")) { + throw new UnsupportedOperationException("Oracle database tag should be set in the configured image name"); + } + + return new OracleContainer(); } } diff --git a/modules/oracle-xe/src/test/resources/logback-test.xml b/modules/oracle-xe/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..ed0e5b2659e --- /dev/null +++ b/modules/oracle-xe/src/test/resources/logback-test.xml @@ -0,0 +1,29 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + + + + + + + + + + + PROFILER + DENY + + \ No newline at end of file diff --git a/modules/oracle-xe/src/test/resources/testcontainers.properties b/modules/oracle-xe/src/test/resources/testcontainers.properties new file mode 100644 index 00000000000..19b5c4680e8 --- /dev/null +++ b/modules/oracle-xe/src/test/resources/testcontainers.properties @@ -0,0 +1 @@ +oracle.container.image=wnameless/oracle-xe-11g@sha256:15ff9ef50b4f90613c9780b589c57d98a8a9d496decec854316d96396ec5c085 From 2cb142916cd580083f668a920d4fb7c9a95d215b Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sat, 7 Apr 2018 17:38:40 +0200 Subject: [PATCH 0264/5615] #621 increase Ryuk's timeout and make it configurable (#635) * #621 increase Ryuk's timeout and make it configurable * Add to CHANGELOG.md --- CHANGELOG.md | 1 + .../main/java/org/testcontainers/utility/ResourceReaper.java | 2 +- .../testcontainers/utility/TestcontainersConfiguration.java | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c836a63903..610c2a0f046 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. ### Fixed - Fixed extraneous insertion of `useSSL=false` in all JDBC URL strings, even for DBs that do not understand it. Usage is now restricted to MySQL by default and can be overridden by authors of `JdbcDatabaseContainer` subclasses ([\#568](https://github.com/testcontainers/testcontainers-java/issues/568)) - Fixed `getServicePort` on `DockerComposeContainer` throws NullPointerException if service instance number in not used. ([\#619](https://github.com/testcontainers/testcontainers-java/issues/619)) +- Increase Ryuk's timeout and make it configurable with `ryuk.container.timeout`. ([\#621](https://github.com/testcontainers/testcontainers-java/issues/621)[\#635](https://github.com/testcontainers/testcontainers-java/issues/635)) ### Changed - Added compatibility with selenium greater than 3.X ([\#611](https://github.com/testcontainers/testcontainers-java/issues/611)) diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index 7c2cd109b8d..4c56277b244 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -161,7 +161,7 @@ public static String start(String hostIpAddress, DockerClient client, boolean wi kiraThread.start(); // We need to wait before we can start any containers to make sure that we delete them - if (!ryukScheduledLatch.await(5, TimeUnit.SECONDS)) { + if (!ryukScheduledLatch.await(TestcontainersConfiguration.getInstance().getRyukTimeout(), TimeUnit.SECONDS)) { throw new IllegalStateException("Can not connect to Ryuk"); } diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 3a6ef4d2537..739a087fc89 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -50,6 +50,10 @@ public String getRyukImage() { return (String) properties.getOrDefault("ryuk.container.image", "bsideup/moby-ryuk:0.2.2"); } + public Integer getRyukTimeout() { + return (Integer) properties.getOrDefault("ryuk.container.timeout", 30); + } + public String getKafkaImage() { return (String) properties.getOrDefault("kafka.container.image", "confluentinc/cp-kafka"); } From cacf0bca91a487a019d357dd7a0dfa318132c3f1 Mon Sep 17 00:00:00 2001 From: Barry Commins Date: Sat, 7 Apr 2018 17:32:43 +0100 Subject: [PATCH 0265/5615] Added withLogConsumer() method to DockerComposeContainer (#629) * Added withLogConsumer() method to DockerComposeContainer Fixes #605 * Added try-finally block to DockerComposeLogConsumerTest --- CHANGELOG.md | 1 + .../containers/DockerComposeContainer.java | 34 +++++++++++++++++-- .../junit/DockerComposeLogConsumerTest.java | 30 ++++++++++++++++ 3 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 core/src/test/java/org/testcontainers/junit/DockerComposeLogConsumerTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 610c2a0f046..6735de7f434 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ All notable changes to this project will be documented in this file. - Added `WaitStrategyTarget` interface which is the target of the new `WaitStrategy` ([\#600](https://github.com/testcontainers/testcontainers-java/pull/600)) - *Breaking:* Removed hard-coded `wnameless` Oracle database image name. Users should instead place a file on the classpath named `testcontainers.properties` containing `oracle.container.image=IMAGE`, where IMAGE is a suitable image name and tag/SHA hash. For information, the approach recommended by Oracle for creating an Oracle XE docker image is described [here](https://blogs.oracle.com/oraclewebcentersuite/implement-oracle-database-xe-as-docker-containers). - Added `DockerHealthcheckWaitStrategy` that is based on Docker's built-in [healthcheck](https://docs.docker.com/engine/reference/builder/#healthcheck) ([\#618](https://github.com/testcontainers/testcontainers-java/pull/618)). +- Added `withLogConsumer(String serviceName, Consumer consumer)` method to `DockerComposeContainer` ([\#605](https://github.com/testcontainers/testcontainers-java/issues/605)) ## [1.6.0] - 2018-01-28 diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index c284002c83b..f6b8a647bd8 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -15,6 +15,7 @@ import org.slf4j.LoggerFactory; import org.slf4j.profiler.Profiler; import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.output.Slf4jLogConsumer; import org.testcontainers.containers.startupcheck.IndefiniteWaitOneShotStartupCheckStrategy; import org.testcontainers.containers.wait.strategy.Wait; @@ -31,7 +32,9 @@ import java.nio.file.Paths; import java.time.Duration; import java.util.AbstractMap.SimpleEntry; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -40,6 +43,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; @@ -72,6 +76,7 @@ public class DockerComposeContainer> e private final Map serviceInstanceMap = new ConcurrentHashMap<>(); private final Map waitStrategyMap = new ConcurrentHashMap<>(); private final SocatContainer ambassadorContainer = new SocatContainer(); + private final Map>> logConsumers = new ConcurrentHashMap<>(); private static final Object MUTEX = new Object(); @@ -148,11 +153,13 @@ private void createServiceInstance(Container container) { final ComposeServiceWaitStrategyTarget containerInstance = new ComposeServiceWaitStrategyTarget(container, ambassadorContainer, ambassadorPortMappings.getOrDefault(serviceName, new HashMap<>())); + String containerId = containerInstance.getContainerId(); if (tailChildContainers) { - LogUtils.followOutput(DockerClientFactory.instance().client(), containerInstance.getContainerId(), - new Slf4jLogConsumer(logger()).withPrefix(container.getNames()[0])); + followLogs(containerId, new Slf4jLogConsumer(logger()).withPrefix(container.getNames()[0])); } - serviceInstanceMap.putIfAbsent(serviceName, containerInstance); + //follow logs using registered consumers for this service + logConsumers.getOrDefault(serviceName, Collections.emptyList()).forEach(consumer -> followLogs(containerId, consumer)); + serviceInstanceMap.putIfAbsent(serviceName, containerInstance); } private void waitUntilServiceStarted(String serviceName, ComposeServiceWaitStrategyTarget serviceInstance) { @@ -401,6 +408,27 @@ public SELF withTailChildContainers(boolean tailChildContainers) { return self(); } + /** + * Attach an output consumer at container startup, enabling stdout and stderr to be followed, waited on, etc. + *

+ * More than one consumer may be registered. + * + * @param serviceName the name of the service as set in the docker-compose.yml file + * @param consumer consumer that output frames should be sent to + * @return this instance, for chaining + */ + public SELF withLogConsumer(String serviceName, Consumer consumer) { + String serviceInstanceName = getServiceInstanceName(serviceName); + final List> consumers = this.logConsumers.getOrDefault(serviceInstanceName, new ArrayList<>()); + consumers.add(consumer); + this.logConsumers.putIfAbsent(serviceInstanceName, consumers); + return self(); + } + + private void followLogs(String containerId, Consumer consumer) { + LogUtils.followOutput(DockerClientFactory.instance().client(), containerId, consumer); + } + private SELF self() { return (SELF) this; } diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeLogConsumerTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeLogConsumerTest.java new file mode 100644 index 00000000000..6cd90d3cd9c --- /dev/null +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeLogConsumerTest.java @@ -0,0 +1,30 @@ +package org.testcontainers.junit; + +import org.junit.Test; +import org.junit.runner.Description; +import org.testcontainers.containers.DockerComposeContainer; +import org.testcontainers.containers.output.WaitingConsumer; + +import java.io.File; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; + +public class DockerComposeLogConsumerTest { + + @Test + public void testLogConsumer() throws TimeoutException { + WaitingConsumer logConsumer = new WaitingConsumer(); + DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/v2-compose-test.yml")) + .withExposedService("redis_1", 6379) + .withLogConsumer("redis_1", logConsumer); + + try { + environment.starting(Description.EMPTY); + logConsumer.waitUntil(frame -> frame.getType() == STDOUT && frame.getUtf8String().contains("Ready to accept connections"), 5, TimeUnit.SECONDS); + } finally { + environment.finished(Description.EMPTY); + } + } +} From 7e972835fe3a9966ad3dbbe0b1f86a8b1d73eed9 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sat, 7 Apr 2018 19:01:32 +0200 Subject: [PATCH 0266/5615] Update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6735de7f434..fa67b433a9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. ## UNRELEASED +### Fixed + +### Changed + +## [1.7.0] - 2018-04-07 + ### Fixed - Fixed extraneous insertion of `useSSL=false` in all JDBC URL strings, even for DBs that do not understand it. Usage is now restricted to MySQL by default and can be overridden by authors of `JdbcDatabaseContainer` subclasses ([\#568](https://github.com/testcontainers/testcontainers-java/issues/568)) - Fixed `getServicePort` on `DockerComposeContainer` throws NullPointerException if service instance number in not used. ([\#619](https://github.com/testcontainers/testcontainers-java/issues/619)) From fd1a0798cfdcbbacd91c061313edc2d8ff54d8cf Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sat, 7 Apr 2018 19:29:16 +0200 Subject: [PATCH 0267/5615] satisfy Maven Central requirements (add description and devs) --- gradle/publishing.gradle | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/gradle/publishing.gradle b/gradle/publishing.gradle index 6417f290e58..aecb64b0aa2 100644 --- a/gradle/publishing.gradle +++ b/gradle/publishing.gradle @@ -24,6 +24,7 @@ publishing { resolveStrategy = Closure.DELEGATE_FIRST name project.description + description 'Isolated container management for Java code testing' url 'https://testcontainers.org' issueManagement { system 'GitHub' @@ -40,6 +41,13 @@ publishing { connection 'scm:git:git://github.com/testcontainers/testcontainers-java.git' developerConnection 'scm:git:ssh://git@github.com/testcontainers/testcontainers-java.git' } + developers { + developer { + id 'rnorth' + name 'Richard North' + email 'rich.north@gmail.com' + } + } } def dependenciesNode = rootNode.appendNode('dependencies') From b5181f784c63b91a61ec68cd58acc522ce4d2dbc Mon Sep 17 00:00:00 2001 From: Barry Commins Date: Wed, 11 Apr 2018 22:07:30 +0100 Subject: [PATCH 0268/5615] Fixed `HostPortWaitStrategy` throws `NumberFormatException` when port is exposed but not mapped (#640) * Fixed `HostPortWaitStrategy` throws `NumberFormatException` when port is exposed but not mapped * updated exposed port tests are suggested in code review --- CHANGELOG.md | 1 + .../java/org/testcontainers/containers/ContainerState.java | 2 ++ core/src/test/resources/v2-compose-test.yml | 4 +++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa67b433a9d..357bb3313a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file. ## UNRELEASED ### Fixed +- Fixed `HostPortWaitStrategy` throws `NumberFormatException` when port is exposed but not mapped [\#640](https://github.com/testcontainers/testcontainers-java/issues/640)) ### Changed diff --git a/core/src/main/java/org/testcontainers/containers/ContainerState.java b/core/src/main/java/org/testcontainers/containers/ContainerState.java index 4fc46c64989..1eb638232f5 100644 --- a/core/src/main/java/org/testcontainers/containers/ContainerState.java +++ b/core/src/main/java/org/testcontainers/containers/ContainerState.java @@ -11,6 +11,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; public interface ContainerState { @@ -127,6 +128,7 @@ default List getBoundPortNumbers() { .map(PortBinding::parse) .map(PortBinding::getBinding) .map(Ports.Binding::getHostPortSpec) + .filter(Objects::nonNull) .map(Integer::valueOf) .collect(Collectors.toList()); } diff --git a/core/src/test/resources/v2-compose-test.yml b/core/src/test/resources/v2-compose-test.yml index b264c61ebb6..d439945d236 100644 --- a/core/src/test/resources/v2-compose-test.yml +++ b/core/src/test/resources/v2-compose-test.yml @@ -1,4 +1,6 @@ version: '2' services: redis: - image: redis \ No newline at end of file + image: redis + ports: + - 6379 From bcd58a597ff1e74bf373a722afeb38b75eeafb0e Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 12 Apr 2018 10:13:46 +0200 Subject: [PATCH 0269/5615] #641 fix missing commons-codec shaded dependency (#642) --- core/build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/build.gradle b/core/build.gradle index a1c4829f7e7..645e2581b86 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -52,6 +52,7 @@ shadowJar { "org.apache.http", "org.apache.commons.lang", "org.apache.commons.io", + "org.apache.commons.codec", "org.glassfish", "org.aopalliance", "org.jvnet", @@ -81,6 +82,7 @@ shadowJar { include(dependency('org.zeroturnaround:zt-exec')) include(dependency('commons-lang:commons-lang')) include(dependency('commons-io:commons-io')) + include(dependency('commons-codec:commons-codec')) } } @@ -118,6 +120,7 @@ dependencies { shaded 'org.zeroturnaround:zt-exec:1.8' shaded 'commons-lang:commons-lang:2.6' shaded 'commons-io:commons-io:2.5' + shaded 'commons-codec:commons-codec:1.11' // docker-java uses SslConfigurator from jersey-common for TLS shaded ('org.glassfish.jersey.core:jersey-common:2.23.1') { // SslConfigurator doesn't use classes from dependencies From bb78b9a3f4c1f307cccabef6f21b038d476afa6a Mon Sep 17 00:00:00 2001 From: Chr1st0ph Date: Thu, 12 Apr 2018 21:15:06 +0200 Subject: [PATCH 0270/5615] Implement MariaDBContainer#getDatabaseName (#638) --- .../java/org/testcontainers/containers/MariaDBContainer.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java index 2bea9b8ad7b..c3717bb579e 100644 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java @@ -51,6 +51,11 @@ public String getJdbcUrl() { return "jdbc:mariadb://" + getContainerIpAddress() + ":" + getMappedPort(MARIADB_PORT) + "/test"; } + @Override + public String getDatabaseName() { + return MARIADB_DATABASE; + } + @Override public String getUsername() { return MARIADB_USER; From 1d85a3834bd937f80aad3a4cec249c027f31aeb4 Mon Sep 17 00:00:00 2001 From: David Pilato Date: Fri, 13 Apr 2018 14:04:50 +0200 Subject: [PATCH 0271/5615] Support multiple HTTP status codes for HttpWaitStrategy (#630) * Support multiple HTTP status codes for HttpWaitStrategy In the context of elasticsearch test containers module, I'd like to add an ElasticsearchWaitStrategy class which extends the HttpWaitStrategy with some default settings so it will be even easier for a end user to start an ElasticsearchTestContainer. Anyway, in this context, I found helpful that the HttpWaitStrategy expects more than on status code. For example, you can imagine running elasticsearch in secure mode or without any security. In which cases the elasticsearch service might answer 200 or 401. This commit proposes this change. * Revert changes in Deprecated class * Revert changes in Deprecated class * Make forStatusCode chainable and add forStatusCodeMatching method `forStatusCodeMatching()` comes with a default Predicate which checks status codes that have been provided with `forStatusCode()`. Also copied the default tests which were using the deprecated package to the new one to make sure we test the new methods. * Fix quality * Fix compile issue * Support both Predicates and Status codes Also use a Set instead of a List to avoid duplicates. * Fix setter * Revert unrelated change * Fix error message (note that with predicates it might be incorrect though) * Fix default predicate * Move WaitStrategy tests to the right package And stop testing deprecated methods * Add a test (and fix the bug!) * Add more tests * Add entry in changelog --- CHANGELOG.md | 2 + .../testcontainers/containers/wait/Wait.java | 3 +- .../wait/strategy/HttpWaitStrategy.java | 25 +++- .../containers/wait/strategy/Wait.java | 3 +- .../junit/wait/HostPortWaitStrategyTest.java | 48 ------ .../junit/wait/HttpWaitStrategyTest.java | 72 --------- .../AbstractWaitStrategyTest.java | 58 +++++--- .../wait/strategy/HttpWaitStrategyTest.java | 138 ++++++++++++++++++ .../LogMessageWaitStrategyTest.java | 4 +- docs/usage/docker_compose.md | 8 +- docs/usage/options.md | 19 ++- 11 files changed, 227 insertions(+), 153 deletions(-) delete mode 100644 core/src/test/java/org/testcontainers/junit/wait/HostPortWaitStrategyTest.java delete mode 100644 core/src/test/java/org/testcontainers/junit/wait/HttpWaitStrategyTest.java rename core/src/test/java/org/testcontainers/junit/wait/{ => strategy}/AbstractWaitStrategyTest.java (59%) create mode 100644 core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java rename core/src/test/java/org/testcontainers/junit/wait/{ => strategy}/LogMessageWaitStrategyTest.java (90%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 357bb3313a4..eab6321cf43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ All notable changes to this project will be documented in this file. ### Changed +- Support multiple HTTP status codes for HttpWaitStrategy ([\#630](https://github.com/testcontainers/testcontainers-java/issues/630)) + ## [1.7.0] - 2018-04-07 ### Fixed diff --git a/core/src/main/java/org/testcontainers/containers/wait/Wait.java b/core/src/main/java/org/testcontainers/containers/wait/Wait.java index ac96d0b7de5..66d055395c5 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/Wait.java +++ b/core/src/main/java/org/testcontainers/containers/wait/Wait.java @@ -39,8 +39,7 @@ public static HostPortWaitStrategy forListeningPort() { */ public static HttpWaitStrategy forHttp(String path) { return new HttpWaitStrategy() - .forPath(path) - .forStatusCode(HttpURLConnection.HTTP_OK); + .forPath(path); } /** diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java index bf63e9708d5..f74b51c602b 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java @@ -12,6 +12,7 @@ import java.net.HttpURLConnection; import java.net.URI; import java.net.URL; +import java.util.HashSet; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; @@ -32,11 +33,16 @@ public class HttpWaitStrategy extends AbstractWaitStrategy { private static final String AUTH_BASIC = "Basic "; private String path = "/"; - private int statusCode = HttpURLConnection.HTTP_OK; + private Set statusCodes = new HashSet<>(); private boolean tlsEnabled; private String username; private String password; private Predicate responsePredicate; + private Predicate statusCodePredicate = responseCode -> { + // If we did not provide any status code, we assume by default HttpURLConnection.HTTP_OK + if (statusCodes.isEmpty() && HttpURLConnection.HTTP_OK == responseCode) return true; + return statusCodes.contains(responseCode); + }; /** * Waits for the given status code. @@ -45,7 +51,17 @@ public class HttpWaitStrategy extends AbstractWaitStrategy { * @return this */ public HttpWaitStrategy forStatusCode(int statusCode) { - this.statusCode = statusCode; + statusCodes.add(statusCode); + return this; + } + + /** + * Waits for the status code to pass the given predicate + * @param statusCodePredicate The predicate to test the response against + * @return this + */ + public HttpWaitStrategy forStatusCodeMatching(Predicate statusCodePredicate) { + this.statusCodePredicate = this.statusCodePredicate.or(statusCodePredicate); return this; } @@ -122,7 +138,7 @@ protected void waitUntilReady() { connection.setRequestMethod("GET"); connection.connect(); - if (statusCode != connection.getResponseCode()) { + if (!statusCodePredicate.test(connection.getResponseCode())) { throw new RuntimeException(String.format("HTTP response code was: %s", connection.getResponseCode())); } @@ -144,7 +160,8 @@ protected void waitUntilReady() { } catch (TimeoutException e) { throw new ContainerLaunchException(String.format( - "Timed out waiting for URL to be accessible (%s should return HTTP %s)", uri, statusCode)); + "Timed out waiting for URL to be accessible (%s should return HTTP %s)", uri, statusCodes.isEmpty() ? + HttpURLConnection.HTTP_OK : statusCodes)); } } diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/Wait.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/Wait.java index e839a11d91f..034a83a2949 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/Wait.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/Wait.java @@ -35,8 +35,7 @@ public static HostPortWaitStrategy forListeningPort() { */ public static HttpWaitStrategy forHttp(String path) { return new HttpWaitStrategy() - .forPath(path) - .forStatusCode(HttpURLConnection.HTTP_OK); + .forPath(path); } /** diff --git a/core/src/test/java/org/testcontainers/junit/wait/HostPortWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/HostPortWaitStrategyTest.java deleted file mode 100644 index 114750c6d0f..00000000000 --- a/core/src/test/java/org/testcontainers/junit/wait/HostPortWaitStrategyTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.testcontainers.junit.wait; - -import org.jetbrains.annotations.NotNull; -import org.junit.Test; -import org.rnorth.ducttape.RetryCountExceededException; -import org.testcontainers.containers.wait.HostPortWaitStrategy; - -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * Tests for {@link HostPortWaitStrategy}. - * - * @author Pete Cornish {@literal } - */ -public class HostPortWaitStrategyTest extends AbstractWaitStrategyTest { - /** - * Expects that the WaitStrategy returns successfully after connection to a container with a listening port. - */ - @Test - public void testWaitUntilReady_Success() { - waitUntilReadyAndSucceed("while true; do nc -lp 8080; done"); - } - - /** - * Expects that the WaitStrategy throws a {@link RetryCountExceededException} after unsuccessful connection - * to a container with a listening port within the timeout period. - */ - @Test - public void testWaitUntilReady_Timeout() { - waitUntilReadyAndTimeout("exit 1"); - } - - /** - * @param ready the AtomicBoolean on which to indicate success - * @return the WaitStrategy under test - */ - @NotNull - protected HostPortWaitStrategy buildWaitStrategy(final AtomicBoolean ready) { - return new HostPortWaitStrategy() { - @Override - protected void waitUntilReady() { - // blocks until ready or timeout occurs - super.waitUntilReady(); - ready.set(true); - } - }; - } -} diff --git a/core/src/test/java/org/testcontainers/junit/wait/HttpWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/HttpWaitStrategyTest.java deleted file mode 100644 index e434ff90466..00000000000 --- a/core/src/test/java/org/testcontainers/junit/wait/HttpWaitStrategyTest.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.testcontainers.junit.wait; - -import org.jetbrains.annotations.NotNull; -import org.junit.Test; -import org.rnorth.ducttape.RetryCountExceededException; -import org.testcontainers.containers.wait.HttpWaitStrategy; - -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * Tests for {@link HttpWaitStrategy}. - * - * @author Pete Cornish {@literal } - */ -public class HttpWaitStrategyTest extends AbstractWaitStrategyTest { - /** - * newline sequence indicating end of the HTTP header. - */ - private static final String NEWLINE = "\r\n"; - - private static final String GOOD_RESPONSE_BODY = "Good Response Body"; - - /** - * Expects that the WaitStrategy returns successfully after receiving an HTTP 200 response from the container. - */ - @Test - public void testWaitUntilReady_Success() { - waitUntilReadyAndSucceed(createShellCommand("200 OK", GOOD_RESPONSE_BODY)); - } - - /** - * Expects that the WaitStrategy throws a {@link RetryCountExceededException} after not receiving an HTTP 200 - * response from the container within the timeout period. - */ - @Test - public void testWaitUntilReady_Timeout() { - waitUntilReadyAndTimeout(createShellCommand("400 Bad Request", GOOD_RESPONSE_BODY)); - } - - /** - * Expects that the WaitStrategy throws a {@link RetryCountExceededException} after not the expected response body - * from the container within the timeout period. - */ - @Test - public void testWaitUntilReady_Timeout_BadResponseBody() { - waitUntilReadyAndTimeout(createShellCommand("200 OK", "Bad Response")); - } - - /** - * @param ready the AtomicBoolean on which to indicate success - * @return the WaitStrategy under test - */ - @NotNull - protected HttpWaitStrategy buildWaitStrategy(final AtomicBoolean ready) { - return new HttpWaitStrategy() { - @Override - protected void waitUntilReady() { - // blocks until ready or timeout occurs - super.waitUntilReady(); - ready.set(true); - } - }.forResponsePredicate(s -> s.equals(GOOD_RESPONSE_BODY)); - } - - private String createShellCommand(String header, String responseBody) { - int length = responseBody.getBytes().length; - return "while true; do { echo -e \"HTTP/1.1 "+header+NEWLINE+ - "Content-Type: text/html"+NEWLINE+ - "Content-Length: "+length +NEWLINE+ "\";" - +" echo \""+responseBody+"\";} | nc -lp 8080; done"; - } -} diff --git a/core/src/test/java/org/testcontainers/junit/wait/AbstractWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java similarity index 59% rename from core/src/test/java/org/testcontainers/junit/wait/AbstractWaitStrategyTest.java rename to core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java index 6578a430b69..c6216f0b184 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/AbstractWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java @@ -1,4 +1,4 @@ -package org.testcontainers.junit.wait; +package org.testcontainers.junit.wait.strategy; import org.jetbrains.annotations.NotNull; import org.junit.Before; @@ -6,12 +6,12 @@ import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.containers.ContainerLaunchException; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.wait.WaitStrategy; +import org.testcontainers.containers.wait.strategy.WaitStrategy; import java.time.Duration; import java.util.concurrent.atomic.AtomicBoolean; -import static org.junit.Assert.*; +import static org.junit.Assert.assertTrue; /** * Common test methods for {@link WaitStrategy} implementations. @@ -19,13 +19,13 @@ * @author Pete Cornish {@literal } */ public abstract class AbstractWaitStrategyTest { - private static final long WAIT_TIMEOUT_MILLIS = 3000; - private static final String IMAGE_NAME = "alpine:latest"; + static final long WAIT_TIMEOUT_MILLIS = 3000; + static final String IMAGE_NAME = "alpine:latest"; /** * Indicates that the WaitStrategy has completed waiting successfully. */ - private AtomicBoolean ready; + AtomicBoolean ready; /** * Subclasses should return an instance of {@link W} that sets {@code ready} to {@code true}, @@ -50,14 +50,23 @@ public void setUp() throws Exception { * @return the (unstarted) container */ private GenericContainer startContainerWithCommand(String shellCommand) { - final WaitStrategy waitStrategy = buildWaitStrategy(ready) - .withStartupTimeout(Duration.ofMillis(WAIT_TIMEOUT_MILLIS)); + return startContainerWithCommand(shellCommand, buildWaitStrategy(ready)); + } + /** + * Starts a GenericContainer with the {@link #IMAGE_NAME} image, passing the given {@code shellCommand} as + * a parameter to {@literal sh -c} (the container CMD) and apply a give wait strategy. + * Note that the timeout will be overwritten if any with {@link #WAIT_TIMEOUT_MILLIS}. + * @param shellCommand the shell command to execute + * @param waitStrategy The wait strategy to apply + * @return the (unstarted) container + */ + protected GenericContainer startContainerWithCommand(String shellCommand, WaitStrategy waitStrategy) { // apply WaitStrategy to container return new GenericContainer(IMAGE_NAME) .withExposedPorts(8080) .withCommand("sh", "-c", shellCommand) - .waitingFor(waitStrategy); + .waitingFor(waitStrategy.withStartupTimeout(Duration.ofMillis(WAIT_TIMEOUT_MILLIS))); } /** @@ -66,13 +75,7 @@ private GenericContainer startContainerWithCommand(String shellCommand) { * @param shellCommand the shell command to execute */ protected void waitUntilReadyAndSucceed(String shellCommand) { - final GenericContainer container = startContainerWithCommand(shellCommand); - - // start() blocks until successful or timeout - container.start(); - - assertTrue(String.format("Expected container to be ready after timeout of %sms", - WAIT_TIMEOUT_MILLIS), ready.get()); + waitUntilReadyAndSucceed(startContainerWithCommand(shellCommand)); } /** @@ -82,12 +85,33 @@ protected void waitUntilReadyAndSucceed(String shellCommand) { * @param shellCommand the shell command to execute */ protected void waitUntilReadyAndTimeout(String shellCommand) { - final GenericContainer container = startContainerWithCommand(shellCommand); + waitUntilReadyAndTimeout(startContainerWithCommand(shellCommand)); + } + /** + * Expects that the WaitStrategy throws a {@link RetryCountExceededException} after unsuccessful connection + * to a container with a listening port. + * + * @param container the container to start + */ + protected void waitUntilReadyAndTimeout(GenericContainer container) { // start() blocks until successful or timeout VisibleAssertions.assertThrows("an exception is thrown when timeout occurs (" + WAIT_TIMEOUT_MILLIS + "ms)", ContainerLaunchException.class, container::start); } + + /** + * Expects that the WaitStrategy returns successfully after connection to a container with a listening port. + * + * @param container the container to start + */ + protected void waitUntilReadyAndSucceed(GenericContainer container) { + // start() blocks until successful or timeout + container.start(); + + assertTrue(String.format("Expected container to be ready after timeout of %sms", + WAIT_TIMEOUT_MILLIS), ready.get()); + } } diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java new file mode 100644 index 00000000000..56c58fa0b2d --- /dev/null +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java @@ -0,0 +1,138 @@ +package org.testcontainers.junit.wait.strategy; + +import org.jetbrains.annotations.NotNull; +import org.junit.Test; +import org.rnorth.ducttape.RetryCountExceededException; +import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Predicate; + +/** + * Tests for {@link HttpWaitStrategy}. + * + * @author Pete Cornish {@literal } + */ +public class HttpWaitStrategyTest extends AbstractWaitStrategyTest { + /** + * newline sequence indicating end of the HTTP header. + */ + private static final String NEWLINE = "\r\n"; + + private static final String GOOD_RESPONSE_BODY = "Good Response Body"; + + /** + * Expects that the WaitStrategy returns successfully after receiving an HTTP 200 response from the container. + */ + @Test + public void testWaitUntilReadyWithSuccess() { + waitUntilReadyAndSucceed(createShellCommand("200 OK", GOOD_RESPONSE_BODY)); + } + + /** + * Expects that the WaitStrategy returns successfully after receiving an HTTP 401 response from the container. + * This 401 response is checked with a lambda using {@link HttpWaitStrategy#forStatusCodeMatching(Predicate)} + */ + @Test + public void testWaitUntilReadyWithUnauthorizedWithLambda() { + waitUntilReadyAndSucceed(startContainerWithCommand(createShellCommand("401 UNAUTHORIZED", GOOD_RESPONSE_BODY), + createHttpWaitStrategy(ready) + .forStatusCodeMatching(it -> it >= 200 && it < 300 || it == 401) + )); + } + + /** + * Expects that the WaitStrategy returns successfully after receiving an HTTP 401 response from the container. + * This 401 response is checked with many status codes using {@link HttpWaitStrategy#forStatusCode(int)} + */ + @Test + public void testWaitUntilReadyWithManyStatusCodes() { + waitUntilReadyAndSucceed(startContainerWithCommand(createShellCommand("401 UNAUTHORIZED", GOOD_RESPONSE_BODY), + createHttpWaitStrategy(ready) + .forStatusCode(300) + .forStatusCode(401) + .forStatusCode(500) + )); + } + + /** + * Expects that the WaitStrategy returns successfully after receiving an HTTP 401 response from the container. + * This 401 response is checked with with many status codes using {@link HttpWaitStrategy#forStatusCode(int)} + * and a lambda using {@link HttpWaitStrategy#forStatusCodeMatching(Predicate)} + */ + @Test + public void testWaitUntilReadyWithManyStatusCodesAndLambda() { + waitUntilReadyAndSucceed(startContainerWithCommand(createShellCommand("401 UNAUTHORIZED", GOOD_RESPONSE_BODY), + createHttpWaitStrategy(ready) + .forStatusCode(300) + .forStatusCode(500) + .forStatusCodeMatching(it -> it == 401) + )); + } + + /** + * Expects that the WaitStrategy throws a {@link RetryCountExceededException} after not receiving any of the + * error code defined with {@link HttpWaitStrategy#forStatusCode(int)} + * and {@link HttpWaitStrategy#forStatusCodeMatching(Predicate)} + */ + @Test + public void testWaitUntilReadyWithTimeoutAndWithManyStatusCodesAndLambda() { + waitUntilReadyAndTimeout(startContainerWithCommand(createShellCommand("401 UNAUTHORIZED", GOOD_RESPONSE_BODY), + createHttpWaitStrategy(ready) + .forStatusCode(300) + .forStatusCodeMatching(it -> it == 500) + )); + } + + /** + * Expects that the WaitStrategy throws a {@link RetryCountExceededException} after not receiving an HTTP 200 + * response from the container within the timeout period. + */ + @Test + public void testWaitUntilReadyWithTimeout() { + waitUntilReadyAndTimeout(createShellCommand("400 Bad Request", GOOD_RESPONSE_BODY)); + } + + /** + * Expects that the WaitStrategy throws a {@link RetryCountExceededException} after not the expected response body + * from the container within the timeout period. + */ + @Test + public void testWaitUntilReadyWithTimeoutAndBadResponseBody() { + waitUntilReadyAndTimeout(createShellCommand("200 OK", "Bad Response")); + } + + /** + * @param ready the AtomicBoolean on which to indicate success + * @return the WaitStrategy under test + */ + @NotNull + protected HttpWaitStrategy buildWaitStrategy(final AtomicBoolean ready) { + return createHttpWaitStrategy(ready) + .forResponsePredicate(s -> s.equals(GOOD_RESPONSE_BODY)); + } + + /** + * Create a HttpWaitStrategy instance with a waitUntilReady implementation + * @param ready Indicates that the WaitStrategy has completed waiting successfully. + * @return the HttpWaitStrategy instance + */ + private HttpWaitStrategy createHttpWaitStrategy(final AtomicBoolean ready) { + return new HttpWaitStrategy() { + @Override + protected void waitUntilReady() { + // blocks until ready or timeout occurs + super.waitUntilReady(); + ready.set(true); + } + }; + } + + private String createShellCommand(String header, String responseBody) { + int length = responseBody.getBytes().length; + return "while true; do { echo -e \"HTTP/1.1 "+header+NEWLINE+ + "Content-Type: text/html"+NEWLINE+ + "Content-Length: "+length +NEWLINE+ "\";" + +" echo \""+responseBody+"\";} | nc -lp 8080; done"; + } +} diff --git a/core/src/test/java/org/testcontainers/junit/wait/LogMessageWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/LogMessageWaitStrategyTest.java similarity index 90% rename from core/src/test/java/org/testcontainers/junit/wait/LogMessageWaitStrategyTest.java rename to core/src/test/java/org/testcontainers/junit/wait/strategy/LogMessageWaitStrategyTest.java index 4596f546d15..7154bab06fa 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/LogMessageWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/LogMessageWaitStrategyTest.java @@ -1,8 +1,8 @@ -package org.testcontainers.junit.wait; +package org.testcontainers.junit.wait.strategy; import org.jetbrains.annotations.NotNull; import org.junit.Test; -import org.testcontainers.containers.wait.LogMessageWaitStrategy; +import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import java.util.concurrent.atomic.AtomicBoolean; diff --git a/docs/usage/docker_compose.md b/docs/usage/docker_compose.md index b4a6e822106..ee693fa45a4 100644 --- a/docs/usage/docker_compose.md +++ b/docs/usage/docker_compose.md @@ -72,14 +72,15 @@ public static DockerComposeContainer environment = Wait.forListeningPort().withStartupTimeout(Duration.ofSeconds(30))); ``` -Wait for arbitrary status code on an HTTPS endpoint: +Wait for arbitrary status codes on an HTTPS endpoint: ```java @ClassRule public static DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/compose-test.yml")) .withExposedService("elasticsearch_1", ELASTICSEARCH_PORT, Wait.forHttp("/all") - .forStatusCode(301) + .forStatusCode(200) + .forStatusCode(401) .usingTls()); ``` @@ -91,7 +92,8 @@ public static DockerComposeContainer environment = .withExposedService("redis_1", REDIS_PORT, Wait.forListeningPort()) .withExposedService("elasticsearch_1", ELASTICSEARCH_PORT, Wait.forHttp("/all") - .forStatusCode(301) + .forStatusCode(200) + .forStatusCode(401) .usingTls()); ``` diff --git a/docs/usage/options.md b/docs/usage/options.md index 232cedb3780..d26d5a319d5 100644 --- a/docs/usage/options.md +++ b/docs/usage/options.md @@ -97,7 +97,7 @@ public static GenericContainer elasticsearch = .waitingFor(Wait.forHttp("/all")); ``` -Wait for arbitrary status code on an HTTPS endpoint: +Wait for 200 or 401 status codes on an HTTPS endpoint: ```java @ClassRule public static GenericContainer elasticsearch = @@ -105,9 +105,22 @@ public static GenericContainer elasticsearch = .withExposedPorts(9200) .waitingFor( Wait.forHttp("/all") - .forStatusCode(301) + .forStatusCode(200) + .forStatusCode(401) .usingTls()); - ``` +``` + +Wait for 200...299 or 401 status codes on an HTTPS endpoint: +```java +@ClassRule +public static GenericContainer elasticsearch = + new GenericContainer("elasticsearch:2.3") + .withExposedPorts(9200) + .waitingFor( + Wait.forHttp("/all") + .forStatusCodeMatching(it -> it >= 200 && it < 300 || it == 401) + .usingTls()); +``` If the used image supports Docker's [Healthcheck](https://docs.docker.com/engine/reference/builder/#healthcheck) feature, you can directly leverage the `healthy` state of the container as your wait condition: ```java From 5109ef4654a188a337c59c1bc1101b0e636f451f Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 13 Apr 2018 14:07:24 +0200 Subject: [PATCH 0272/5615] Update CHANGELOG.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eab6321cf43..b15da46df70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,10 @@ All notable changes to this project will be documented in this file. ## UNRELEASED ### Fixed -- Fixed `HostPortWaitStrategy` throws `NumberFormatException` when port is exposed but not mapped [\#640](https://github.com/testcontainers/testcontainers-java/issues/640)) +- Fixed missing `commons-codec` dependency ([\#641](https://github.com/testcontainers/testcontainers-java/issues/641)) +- Fixed `HostPortWaitStrategy` throws `NumberFormatException` when port is exposed but not mapped ([\#640](https://github.com/testcontainers/testcontainers-java/issues/640)) ### Changed - - Support multiple HTTP status codes for HttpWaitStrategy ([\#630](https://github.com/testcontainers/testcontainers-java/issues/630)) ## [1.7.0] - 2018-04-07 From 11db027f29a16ea772de6329f309b7cbb6911015 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 13 Apr 2018 14:08:12 +0200 Subject: [PATCH 0273/5615] fix ticket number in CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b15da46df70..4beef7174fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. ## UNRELEASED ### Fixed -- Fixed missing `commons-codec` dependency ([\#641](https://github.com/testcontainers/testcontainers-java/issues/641)) +- Fixed missing `commons-codec` dependency ([\#642](https://github.com/testcontainers/testcontainers-java/issues/642)) - Fixed `HostPortWaitStrategy` throws `NumberFormatException` when port is exposed but not mapped ([\#640](https://github.com/testcontainers/testcontainers-java/issues/640)) ### Changed From 9fb8e4be24f20b55c9ba1e224018fc0114e8ea68 Mon Sep 17 00:00:00 2001 From: Victor Verbitsky Date: Tue, 17 Apr 2018 12:15:09 +0300 Subject: [PATCH 0274/5615] More accurate log processing. (#643) * More accurate log processing. The Docker logs can come in non normalized form. The line breaks can be not at end of line, the frame's boundary can split multibyte unicode symbols in the middle and so on. This patch address this problems and normalize logs so, that they can be processed strictly line by line. To achive this, the extra base class BaseConsumer have been added. That class normalize the incoming in method accept() logs and forward it to method process() for it's child classes. Other *Consumer classes have been reworked to be child to the BaseConsumer class and only do own work. Adititionally, BaseConsumer have new withRemoveAnsiCodes(boolean) method for ability to disable ANSI color codes removing (true by default). * Move normalization logic directly to FrameConsumerResultCallback It's help to not ruin the API of *Consumer classes. * Added some tests for log normalization in class FrameConsumerResultCallback. * Fix Codacity warning * More test for the FrameConsumerResultCallback class * Stabilize tests * Log consumers, that not derive BaseConsumer class, is not receive color codes now. Added record to the changelog. * One more test for FrameConsumerResultCallback class. * Fixes due to the code review recommendations * Fixes due to the code review recommendations (Part 2) * One more use case for FrameConsumerResultCallback class. And unit test for it. If StreamType is STDERR or STDOUT the log always have newline at line end. Therefore preprocessor should trim it to be consistent with RAW type processing. * Roll back previouse change doe to failing tests. Doing newline trimming directly in Slf4jLogConsumer class. * Fixes due to the code review recommendations (Part 3) --- CHANGELOG.md | 1 + .../containers/output/BaseConsumer.java | 17 ++ .../output/FrameConsumerResultCallback.java | 106 ++++++++++- .../containers/output/Slf4jLogConsumer.java | 42 ++--- .../containers/output/ToStringConsumer.java | 9 +- .../containers/output/WaitingConsumer.java | 3 +- .../FrameConsumerResultCallbackTest.java | 177 ++++++++++++++++++ 7 files changed, 319 insertions(+), 36 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/containers/output/BaseConsumer.java create mode 100644 core/src/test/java/org/testcontainers/containers/output/FrameConsumerResultCallbackTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 4beef7174fc..2d43b67e7b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. ### Fixed - Fixed missing `commons-codec` dependency ([\#642](https://github.com/testcontainers/testcontainers-java/issues/642)) - Fixed `HostPortWaitStrategy` throws `NumberFormatException` when port is exposed but not mapped ([\#640](https://github.com/testcontainers/testcontainers-java/issues/640)) +- Fixed log processing: multibyte unicode, linebreaks and ASCII color codes. Color codes can be turned on with `withRemoveAnsiCodes(false)` ([PR \#643](https://github.com/testcontainers/testcontainers-java/pull/643)) ### Changed - Support multiple HTTP status codes for HttpWaitStrategy ([\#630](https://github.com/testcontainers/testcontainers-java/issues/630)) diff --git a/core/src/main/java/org/testcontainers/containers/output/BaseConsumer.java b/core/src/main/java/org/testcontainers/containers/output/BaseConsumer.java new file mode 100644 index 00000000000..2967eb4d33a --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/output/BaseConsumer.java @@ -0,0 +1,17 @@ +package org.testcontainers.containers.output; + +import lombok.Getter; +import lombok.Setter; + +import java.util.function.Consumer; + +public abstract class BaseConsumer> implements Consumer { + @Getter + @Setter + private boolean removeColorCodes = true; + + public SELF withRemoveAnsiCodes(boolean removeAnsiCodes) { + this.removeColorCodes = removeAnsiCodes; + return (SELF) this; + } +} diff --git a/core/src/main/java/org/testcontainers/containers/output/FrameConsumerResultCallback.java b/core/src/main/java/org/testcontainers/containers/output/FrameConsumerResultCallback.java index 6860563344b..6894b9ec4b8 100644 --- a/core/src/main/java/org/testcontainers/containers/output/FrameConsumerResultCallback.java +++ b/core/src/main/java/org/testcontainers/containers/output/FrameConsumerResultCallback.java @@ -2,28 +2,45 @@ import com.github.dockerjava.api.model.Frame; +import com.github.dockerjava.api.model.StreamType; import com.github.dockerjava.core.async.ResultCallbackTemplate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.function.Consumer; +import java.util.regex.Pattern; /** * This class can be used as a generic callback for docker-java commands that produce Frames. */ public class FrameConsumerResultCallback extends ResultCallbackTemplate { - private final static Logger LOGGER = LoggerFactory.getLogger(FrameConsumerResultCallback.class); + private static final Logger LOGGER = LoggerFactory.getLogger(FrameConsumerResultCallback.class); + + private static final byte[] EMPTY_LINE = new byte[0]; + + private static final Pattern ANSI_COLOR_PATTERN = Pattern.compile("\u001B\\[[0-9;]+m"); + + private static final String LINE_BREAK_REGEX = "((\\r?\\n)|(\\r))"; + + static final String LINE_BREAK_AT_END_REGEX = LINE_BREAK_REGEX + "$"; private Map> consumers; private CountDownLatch completionLatch = new CountDownLatch(1); + private StringBuilder logString = new StringBuilder(); + + private OutputFrame brokenFrame; + public FrameConsumerResultCallback() { consumers = new HashMap<>(); } @@ -45,9 +62,13 @@ public void onNext(Frame frame) { if (outputFrame != null) { Consumer consumer = consumers.get(outputFrame.getType()); if (consumer == null) { - LOGGER.error("got frame with type " + frame.getStreamType() + ", for which no handler is configured"); - } else { - consumer.accept(outputFrame); + LOGGER.error("got frame with type {}, for which no handler is configured", frame.getStreamType()); + } else if (outputFrame.getBytes() != null && outputFrame.getBytes().length > 0) { + if (frame.getStreamType() == StreamType.RAW) { + processRawFrame(outputFrame, consumer); + } else { + processOtherFrame(outputFrame, consumer); + } } } } @@ -63,8 +84,17 @@ public void onError(Throwable throwable) { @Override public void close() throws IOException { + OutputFrame lastLine = null; + + if (logString.length() > 0) { + lastLine = new OutputFrame(OutputFrame.OutputType.STDOUT, logString.toString().getBytes()); + } + // send an END frame to every consumer... but only once per consumer. for (Consumer consumer : new HashSet<>(consumers.values())) { + if (lastLine != null) { + consumer.accept(lastLine); + } consumer.accept(OutputFrame.END); } super.close(); @@ -78,4 +108,72 @@ public void close() throws IOException { public CountDownLatch getCompletionLatch() { return completionLatch; } + + private synchronized void processRawFrame(OutputFrame outputFrame, Consumer consumer) { + String utf8String = outputFrame.getUtf8String(); + byte[] bytes = outputFrame.getBytes(); + + // Merging the strings by bytes to solve the problem breaking non-latin unicode symbols. + if (brokenFrame != null) { + bytes = merge(brokenFrame.getBytes(), bytes); + utf8String = new String(bytes); + brokenFrame = null; + } + // Logger chunks can break the string in middle of multibyte unicode character. + // Backup the bytes to reconstruct proper char sequence with bytes from next frame. + int lastCharacterType = Character.getType(utf8String.charAt(utf8String.length() - 1)); + if (lastCharacterType == Character.OTHER_SYMBOL) { + brokenFrame = new OutputFrame(outputFrame.getType(), bytes); + return; + } + + utf8String = processAnsiColorCodes(utf8String, consumer); + normalizeLogLines(utf8String, consumer); + } + + private synchronized void processOtherFrame(OutputFrame outputFrame, Consumer consumer) { + String utf8String = outputFrame.getUtf8String(); + + utf8String = processAnsiColorCodes(utf8String, consumer); + consumer.accept(new OutputFrame(outputFrame.getType(), utf8String.getBytes())); + } + + private void normalizeLogLines(String utf8String, Consumer consumer) { + // Reformat strings to normalize new lines. + List lines = new ArrayList<>(Arrays.asList(utf8String.split(LINE_BREAK_REGEX))); + if (lines.isEmpty()) { + consumer.accept(new OutputFrame(OutputFrame.OutputType.STDOUT, EMPTY_LINE)); + return; + } + if (utf8String.startsWith("\n") || utf8String.startsWith("\r")) { + lines.add(0, ""); + } + if (utf8String.endsWith("\n") || utf8String.endsWith("\r")) { + lines.add(""); + } + for (int i = 0; i < lines.size() - 1; i++) { + String line = lines.get(i); + if (i == 0 && logString.length() > 0) { + line = logString.toString() + line; + logString.setLength(0); + } + consumer.accept(new OutputFrame(OutputFrame.OutputType.STDOUT, line.getBytes())); + } + logString.append(lines.get(lines.size() - 1)); + } + + private String processAnsiColorCodes(String utf8String, Consumer consumer) { + if (!(consumer instanceof BaseConsumer) || ((BaseConsumer) consumer).isRemoveColorCodes()) { + return ANSI_COLOR_PATTERN.matcher(utf8String).replaceAll(""); + } + return utf8String; + } + + + private byte[] merge(byte[] str1, byte[] str2) { + byte[] mergedString = new byte[str1.length + str2.length]; + System.arraycopy(str1, 0, mergedString, 0, str1.length); + System.arraycopy(str2, 0, mergedString, str1.length, str2.length); + return mergedString; + } } diff --git a/core/src/main/java/org/testcontainers/containers/output/Slf4jLogConsumer.java b/core/src/main/java/org/testcontainers/containers/output/Slf4jLogConsumer.java index 7b04007ef72..26b017d4a36 100644 --- a/core/src/main/java/org/testcontainers/containers/output/Slf4jLogConsumer.java +++ b/core/src/main/java/org/testcontainers/containers/output/Slf4jLogConsumer.java @@ -2,18 +2,13 @@ import org.slf4j.Logger; -import java.util.function.Consumer; -import java.util.regex.Pattern; - /** * A consumer for container output that logs output to an SLF4J logger. */ -public class Slf4jLogConsumer implements Consumer { +public class Slf4jLogConsumer extends BaseConsumer { private final Logger logger; private String prefix = ""; - private static final Pattern ANSI_CODE_PATTERN = Pattern.compile("\\[\\d[ABCD]"); - public Slf4jLogConsumer(Logger logger) { this.logger = logger; } @@ -25,28 +20,19 @@ public Slf4jLogConsumer withPrefix(String prefix) { @Override public void accept(OutputFrame outputFrame) { - if (outputFrame != null) { - String utf8String = outputFrame.getUtf8String(); - - if (utf8String != null) { - OutputFrame.OutputType outputType = outputFrame.getType(); - String message = utf8String.trim(); - - if (ANSI_CODE_PATTERN.matcher(message).matches()) { - return; - } - - switch (outputType) { - case END: - break; - case STDOUT: - case STDERR: - logger.info("{}{}: {}", prefix, outputType, message); - break; - default: - throw new IllegalArgumentException("Unexpected outputType " + outputType); - } - } + OutputFrame.OutputType outputType = outputFrame.getType(); + + String utf8String = outputFrame.getUtf8String(); + utf8String = utf8String.replaceAll(FrameConsumerResultCallback.LINE_BREAK_AT_END_REGEX, ""); + switch (outputType) { + case END: + break; + case STDOUT: + case STDERR: + logger.info("{}{}: {}", prefix, outputType, utf8String); + break; + default: + throw new IllegalArgumentException("Unexpected outputType " + outputType); } } } diff --git a/core/src/main/java/org/testcontainers/containers/output/ToStringConsumer.java b/core/src/main/java/org/testcontainers/containers/output/ToStringConsumer.java index f46e4b9a318..a12325865aa 100644 --- a/core/src/main/java/org/testcontainers/containers/output/ToStringConsumer.java +++ b/core/src/main/java/org/testcontainers/containers/output/ToStringConsumer.java @@ -5,21 +5,26 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.Charset; -import java.util.function.Consumer; /** * Created by rnorth on 26/03/2016. */ -public class ToStringConsumer implements Consumer { +public class ToStringConsumer extends BaseConsumer { + private static final byte[] NEW_LINE = "\n".getBytes(); + private boolean firstLine = true; private ByteArrayOutputStream stringBuffer = new ByteArrayOutputStream(); @Override public void accept(OutputFrame outputFrame) { try { if (outputFrame.getBytes() != null) { + if (!firstLine) { + stringBuffer.write(NEW_LINE); + } stringBuffer.write(outputFrame.getBytes()); stringBuffer.flush(); + firstLine = false; } } catch (IOException e) { throw new RuntimeException(e); diff --git a/core/src/main/java/org/testcontainers/containers/output/WaitingConsumer.java b/core/src/main/java/org/testcontainers/containers/output/WaitingConsumer.java index 3cd0a7cecca..367581528c2 100644 --- a/core/src/main/java/org/testcontainers/containers/output/WaitingConsumer.java +++ b/core/src/main/java/org/testcontainers/containers/output/WaitingConsumer.java @@ -6,14 +6,13 @@ import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.function.Consumer; import java.util.function.Predicate; /** * A consumer for container output that buffers lines in a {@link java.util.concurrent.BlockingDeque} and enables tests * to wait for a matching condition. */ -public class WaitingConsumer implements Consumer { +public class WaitingConsumer extends BaseConsumer { private static final Logger LOGGER = LoggerFactory.getLogger(WaitingConsumer.class); diff --git a/core/src/test/java/org/testcontainers/containers/output/FrameConsumerResultCallbackTest.java b/core/src/test/java/org/testcontainers/containers/output/FrameConsumerResultCallbackTest.java new file mode 100644 index 00000000000..88c6e22d6c9 --- /dev/null +++ b/core/src/test/java/org/testcontainers/containers/output/FrameConsumerResultCallbackTest.java @@ -0,0 +1,177 @@ +package org.testcontainers.containers.output; + +import com.github.dockerjava.api.model.Frame; +import com.github.dockerjava.api.model.StreamType; +import org.junit.Test; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.testcontainers.containers.output.OutputFrame.OutputType; + +public class FrameConsumerResultCallbackTest { + private static final String FRAME_PAYLOAD = "\u001B[0;32mТест1\u001B[0m\n\u001B[1;33mTest2\u001B[0m\n\u001B[0;31mTest3\u001B[0m"; + private static final String LOG_RESULT = "Тест1\nTest2\nTest3"; + + @Test + public void passStderrFrameWithoutColors() { + FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); + ToStringConsumer consumer = new ToStringConsumer(); + callback.addConsumer(OutputType.STDERR, consumer); + callback.onNext(new Frame(StreamType.STDERR, FRAME_PAYLOAD.getBytes())); + assertEquals(LOG_RESULT, consumer.toUtf8String()); + } + + @Test + public void passStderrFrameWithColors() { + FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); + ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); + callback.addConsumer(OutputType.STDERR, consumer); + callback.onNext(new Frame(StreamType.STDERR, FRAME_PAYLOAD.getBytes())); + assertEquals(FRAME_PAYLOAD, consumer.toUtf8String()); + } + + @Test + public void passStdoutFrameWithoutColors() { + FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); + ToStringConsumer consumer = new ToStringConsumer(); + callback.addConsumer(OutputType.STDOUT, consumer); + callback.onNext(new Frame(StreamType.STDOUT, FRAME_PAYLOAD.getBytes())); + assertEquals(LOG_RESULT, consumer.toUtf8String()); + } + + @Test + public void passStdoutFrameWithColors() { + FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); + ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); + callback.addConsumer(OutputType.STDOUT, consumer); + callback.onNext(new Frame(StreamType.STDOUT, FRAME_PAYLOAD.getBytes())); + assertEquals(FRAME_PAYLOAD, consumer.toUtf8String()); + } + + @Test + public void basicConsumer() { + FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); + BasicConsumer consumer = new BasicConsumer(); + callback.addConsumer(OutputType.STDOUT, consumer); + callback.onNext(new Frame(StreamType.STDOUT, FRAME_PAYLOAD.getBytes())); + assertEquals(LOG_RESULT, consumer.toString()); + } + + @Test + public void passStdoutNull() { + FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); + ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); + callback.addConsumer(OutputType.STDOUT, consumer); + callback.onNext(new Frame(StreamType.STDOUT, null)); + assertEquals("", consumer.toUtf8String()); + } + + @Test + public void passStdoutEmptyLine() { + String payload = ""; + FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); + ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); + callback.addConsumer(OutputType.STDOUT, consumer); + callback.onNext(new Frame(StreamType.STDOUT, payload.getBytes())); + assertEquals(payload, consumer.toUtf8String()); + } + + @Test + public void passStdoutSingleLine() { + String payload = "Test"; + FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); + ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); + callback.addConsumer(OutputType.STDOUT, consumer); + callback.onNext(new Frame(StreamType.STDOUT, payload.getBytes())); + assertEquals(payload, consumer.toUtf8String()); + } + + @Test + public void passStdoutSingleLineWithNewline() { + String payload = "Test\n"; + FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); + ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); + callback.addConsumer(OutputType.STDOUT, consumer); + callback.onNext(new Frame(StreamType.STDOUT, payload.getBytes())); + assertEquals(payload, consumer.toUtf8String()); + } + + @Test + public void passRawFrameWithoutColors() throws TimeoutException, IOException { + FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); + WaitingConsumer waitConsumer = new WaitingConsumer(); + callback.addConsumer(OutputType.STDOUT, waitConsumer); + callback.onNext(new Frame(StreamType.RAW, FRAME_PAYLOAD.getBytes())); + waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("Test2"), 1, TimeUnit.SECONDS); + waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("Тест1"), 1, TimeUnit.SECONDS); + Exception exception = null; + try { + waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("Test3"), 1, TimeUnit.SECONDS); + } catch (Exception e) { + exception = e; + } + assertTrue(exception instanceof TimeoutException); + callback.close(); + waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("Test3"), 1, TimeUnit.SECONDS); + } + + @Test + public void passRawFrameWithColors() throws TimeoutException, IOException { + FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); + WaitingConsumer waitConsumer = new WaitingConsumer().withRemoveAnsiCodes(false); + callback.addConsumer(OutputType.STDOUT, waitConsumer); + callback.onNext(new Frame(StreamType.RAW, FRAME_PAYLOAD.getBytes())); + waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("\u001B[1;33mTest2\u001B[0m"), 1, TimeUnit.SECONDS); + waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("\u001B[0;32mТест1\u001B[0m"), 1, TimeUnit.SECONDS); + Exception exception = null; + try { + waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("\u001B[0;31mTest3\u001B[0m"), 1, TimeUnit.SECONDS); + } catch (Exception e) { + exception = e; + } + assertTrue(exception instanceof TimeoutException); + callback.close(); + waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("\u001B[0;31mTest3\u001B[0m"), 1, TimeUnit.SECONDS); + } + + @Test + public void reconstructBreakedUnicode() throws IOException { + String payload = "Тест"; + byte[] payloadBytes = payload.getBytes(); + byte[] bytes1 = new byte[5]; + byte[] bytes2 = new byte[3]; + System.arraycopy(payloadBytes, 0, bytes1, 0, 5); + System.arraycopy(payloadBytes, 5, bytes2, 0, 3); + FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); + ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); + callback.addConsumer(OutputType.STDOUT, consumer); + callback.onNext(new Frame(StreamType.RAW, bytes1)); + callback.onNext(new Frame(StreamType.RAW, bytes2)); + callback.close(); + assertEquals(payload, consumer.toUtf8String()); + } + + private static class BasicConsumer implements Consumer { + private boolean firstLine = true; + private StringBuilder input = new StringBuilder(); + + @Override + public void accept(OutputFrame outputFrame) { + if (!firstLine) { + input.append('\n'); + } + firstLine = false; + input.append(outputFrame.getUtf8String()); + } + + @Override + public String toString() { + return input.toString(); + } + } +} From aca9a935b85710824bd1e293da1dc64cd4a881c0 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 18 Apr 2018 07:18:05 +0200 Subject: [PATCH 0275/5615] #369 Daemonize threads (#646) * #369 Mark all long-living threads started by Testcontainers as daemons and group them * simplify the classpath logic * fixes * Add a line about the logs to CHANGELOG and explain the DaemonTest and why we fork --- CHANGELOG.md | 2 + core/build.gradle | 4 +- .../testcontainers/DockerClientFactory.java | 1 + .../containers/GenericContainer.java | 4 +- .../DockerClientProviderStrategy.java | 4 +- .../TestcontainersDockerCmdExecFactory.java | 304 ++++++++++++++++++ .../images/builder/ImageFromDockerfile.java | 2 +- .../testcontainers/utility/MountableFile.java | 3 +- .../utility/ResourceReaper.java | 3 +- .../java/org/testcontainers/DaemonTest.java | 61 ++++ 10 files changed, 379 insertions(+), 9 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java create mode 100644 core/src/test/java/org/testcontainers/DaemonTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d43b67e7b5..932893f84b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ All notable changes to this project will be documented in this file. ### Changed - Support multiple HTTP status codes for HttpWaitStrategy ([\#630](https://github.com/testcontainers/testcontainers-java/issues/630)) +- Mark all long-living threads started by Testcontainers as daemons and group them. ([\#646](https://github.com/testcontainers/testcontainers-java/issues/646)) +- Remove noisy `DEBUG` logging of Netty packets ([\#646](https://github.com/testcontainers/testcontainers-java/issues/646)) ## [1.7.0] - 2018-04-07 diff --git a/core/build.gradle b/core/build.gradle index 645e2581b86..bf7bda5a8bf 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -106,10 +106,10 @@ dependencies { compile 'org.apache.commons:commons-compress:1.15' // Added for JDK9 compatibility since it's missing this package compile 'javax.xml.bind:jaxb-api:2.3.0' - compile 'org.rnorth.duct-tape:duct-tape:1.0.6' + compile 'org.rnorth.duct-tape:duct-tape:1.0.7' compile 'org.rnorth.visible-assertions:visible-assertions:2.1.0' - shaded ('com.github.docker-java:docker-java:3.0.12') { + shaded ('com.github.docker-java:docker-java:3.1.0-rc-2') { exclude(group: 'org.glassfish.jersey.core') exclude(group: 'org.glassfish.jersey.connectors') exclude(group: 'log4j') diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 22f31123f5a..499c4ee4d85 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -39,6 +39,7 @@ @Slf4j public class DockerClientFactory { + public static final ThreadGroup TESTCONTAINERS_THREAD_GROUP = new ThreadGroup("testcontainers"); public static final String TESTCONTAINERS_LABEL = DockerClientFactory.class.getPackage().getName(); public static final String TESTCONTAINERS_SESSION_ID_LABEL = TESTCONTAINERS_LABEL + ".sessionId"; diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 7d3001a5651..527e391718d 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -321,7 +321,7 @@ protected Path createVolumeDirectory(boolean temporary) { Path directory = new File(".tmp-volume-" + System.currentTimeMillis()).toPath(); PathUtils.mkdirp(directory); - if (temporary) Runtime.getRuntime().addShutdownHook(new Thread(() -> { + if (temporary) Runtime.getRuntime().addShutdownHook(new Thread(DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, () -> { PathUtils.recursiveDeleteDir(directory); })); @@ -478,7 +478,7 @@ private void applyConfiguration(CreateContainerCmd createCommand) { private Set findLinksFromThisContainer(String alias, LinkableContainer linkableContainer) { return dockerClient.listContainersCmd() - .withStatusFilter("running") + .withStatusFilter(Arrays.asList("running")) .exec().stream() .flatMap(container -> Stream.of(container.getNames())) .filter(name -> name.endsWith(linkableContainer.getContainerName())) diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index f4dc5b2c4dd..f078aeac5fc 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -3,7 +3,6 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.core.DockerClientBuilder; import com.github.dockerjava.core.DockerClientConfig; -import com.github.dockerjava.netty.NettyDockerCmdExecFactory; import com.google.common.base.Throwables; import org.apache.commons.io.IOUtils; import org.jetbrains.annotations.Nullable; @@ -13,6 +12,7 @@ import org.rnorth.ducttape.unreliables.Unreliables; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.testcontainers.dockerclient.transport.TestcontainersDockerCmdExecFactory; import org.testcontainers.utility.TestcontainersConfiguration; import java.util.ArrayList; @@ -166,7 +166,7 @@ public DockerClient getClient() { protected DockerClient getClientForConfig(DockerClientConfig config) { return DockerClientBuilder .getInstance(config) - .withDockerCmdExecFactory(new NettyDockerCmdExecFactory()) + .withDockerCmdExecFactory(new TestcontainersDockerCmdExecFactory()) .build(); } diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java b/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java new file mode 100644 index 00000000000..1a0c52df5ac --- /dev/null +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java @@ -0,0 +1,304 @@ +package org.testcontainers.dockerclient.transport; + +import com.github.dockerjava.api.command.DockerCmdExecFactory; +import com.github.dockerjava.core.AbstractDockerCmdExecFactory; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.core.SSLConfig; +import com.github.dockerjava.core.WebTarget; +import com.github.dockerjava.netty.NettyWebTarget; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.*; +import io.netty.channel.epoll.EpollDomainSocketChannel; +import io.netty.channel.epoll.EpollEventLoopGroup; +import io.netty.channel.kqueue.KQueueDomainSocketChannel; +import io.netty.channel.kqueue.KQueueEventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.DuplexChannel; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.channel.unix.DomainSocketAddress; +import io.netty.channel.unix.UnixChannel; +import io.netty.handler.codec.http.HttpClientCodec; +import io.netty.handler.ssl.SslHandler; +import io.netty.handler.timeout.IdleState; +import io.netty.handler.timeout.IdleStateEvent; +import io.netty.handler.timeout.IdleStateHandler; +import io.netty.util.concurrent.DefaultThreadFactory; +import org.apache.commons.lang.SystemUtils; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.testcontainers.DockerClientFactory; + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLParameters; +import java.io.IOException; +import java.net.SocketTimeoutException; +import java.security.Security; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * This class is a modified version of docker-java's NettyDockerCmdExecFactory v3.1.0-rc-2 + * Changes: + * - daemonized thread factory + * - thread group + * - the logging handler removed + * - + */ +public class TestcontainersDockerCmdExecFactory extends AbstractDockerCmdExecFactory implements DockerCmdExecFactory { + + private static final String THREAD_PREFIX = "testcontainers-netty"; + + /* + * useful links: + * + * http://stackoverflow.com/questions/33296749/netty-connect-to-unix-domain-socket-failed + * http://netty.io/wiki/native-transports.html + * https://github.com/netty/netty/blob/master/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClient.java + * https://github.com/slandelle/netty-request-chunking/blob/master/src/test/java/slandelle/ChunkingTest.java + */ + + private Bootstrap bootstrap; + + private EventLoopGroup eventLoopGroup; + + private NettyInitializer nettyInitializer; + + private WebTarget baseResource; + + private Integer connectTimeout = null; + + private Integer readTimeout = null; + + @Override + public void init(DockerClientConfig dockerClientConfig) { + super.init(dockerClientConfig); + + bootstrap = new Bootstrap(); + + String scheme = dockerClientConfig.getDockerHost().getScheme(); + + if ("unix".equals(scheme)) { + nettyInitializer = new UnixDomainSocketInitializer(); + } else if ("tcp".equals(scheme)) { + nettyInitializer = new InetSocketInitializer(); + } + + eventLoopGroup = nettyInitializer.init(bootstrap, dockerClientConfig); + + baseResource = new NettyWebTarget(this::connect).path(dockerClientConfig.getApiVersion().asWebPathPart()); + } + + private DuplexChannel connect() { + try { + return connect(bootstrap); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + private DuplexChannel connect(final Bootstrap bootstrap) throws InterruptedException { + return nettyInitializer.connect(bootstrap); + } + + private interface NettyInitializer { + EventLoopGroup init(final Bootstrap bootstrap, DockerClientConfig dockerClientConfig); + + DuplexChannel connect(final Bootstrap bootstrap) throws InterruptedException; + } + + private class UnixDomainSocketInitializer implements NettyInitializer { + @Override + public EventLoopGroup init(Bootstrap bootstrap, DockerClientConfig dockerClientConfig) { + if (SystemUtils.IS_OS_LINUX) { + return epollGroup(); + } else if (SystemUtils.IS_OS_MAC_OSX) { + return kqueueGroup(); + } + throw new RuntimeException("Unspported OS"); + } + + private ThreadFactory createThreadFactory() { + return new DefaultThreadFactory(THREAD_PREFIX, true, Thread.NORM_PRIORITY, DockerClientFactory.TESTCONTAINERS_THREAD_GROUP); + } + + public EventLoopGroup epollGroup() { + EventLoopGroup epollEventLoopGroup = new EpollEventLoopGroup(0, createThreadFactory()); + + ChannelFactory factory = () -> configure(new EpollDomainSocketChannel()); + + bootstrap.group(epollEventLoopGroup).channelFactory(factory).handler(new ChannelInitializer() { + @Override + protected void initChannel(final UnixChannel channel) throws Exception { + channel.pipeline().addLast(new HttpClientCodec()); + } + }); + return epollEventLoopGroup; + } + + public EventLoopGroup kqueueGroup() { + EventLoopGroup nioEventLoopGroup = new KQueueEventLoopGroup(0, createThreadFactory()); + + bootstrap.group(nioEventLoopGroup).channel(KQueueDomainSocketChannel.class) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(final KQueueDomainSocketChannel channel) throws Exception { + channel.pipeline().addLast(new HttpClientCodec()); + } + }); + + return nioEventLoopGroup; + } + + @Override + public DuplexChannel connect(Bootstrap bootstrap) throws InterruptedException { + return (DuplexChannel) bootstrap.connect(new DomainSocketAddress("/var/run/docker.sock")).sync().channel(); + } + } + + private class InetSocketInitializer implements NettyInitializer { + @Override + public EventLoopGroup init(Bootstrap bootstrap, final DockerClientConfig dockerClientConfig) { + EventLoopGroup nioEventLoopGroup = new NioEventLoopGroup(0, new DefaultThreadFactory(THREAD_PREFIX)); + + // TODO do we really need BouncyCastle? + Security.addProvider(new BouncyCastleProvider()); + + ChannelFactory factory = () -> configure(new NioSocketChannel()); + + bootstrap.group(nioEventLoopGroup).channelFactory(factory) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(final SocketChannel channel) throws Exception { + channel.pipeline().addLast(new HttpClientCodec()); + } + }); + + return nioEventLoopGroup; + } + + @Override + public DuplexChannel connect(Bootstrap bootstrap) throws InterruptedException { + DockerClientConfig dockerClientConfig = getDockerClientConfig(); + String host = dockerClientConfig.getDockerHost().getHost(); + int port = dockerClientConfig.getDockerHost().getPort(); + + if (port == -1) { + throw new RuntimeException("no port configured for " + host); + } + + DuplexChannel channel = (DuplexChannel) bootstrap.connect(host, port).sync().channel(); + + final SslHandler ssl = initSsl(dockerClientConfig); + + if (ssl != null) { + channel.pipeline().addFirst(ssl); + } + + return channel; + } + + private SslHandler initSsl(DockerClientConfig dockerClientConfig) { + SslHandler ssl = null; + + try { + String host = dockerClientConfig.getDockerHost().getHost(); + int port = dockerClientConfig.getDockerHost().getPort(); + + final SSLConfig sslConfig = dockerClientConfig.getSSLConfig(); + + if (sslConfig != null && sslConfig.getSSLContext() != null) { + + SSLEngine engine = sslConfig.getSSLContext().createSSLEngine(host, port); + engine.setUseClientMode(true); + engine.setSSLParameters(enableHostNameVerification(engine.getSSLParameters())); + + // in the future we may use HostnameVerifier like here: + // https://github.com/AsyncHttpClient/async-http-client/blob/1.8.x/src/main/java/com/ning/http/client/providers/netty/NettyConnectListener.java#L76 + + ssl = new SslHandler(engine); + } + + } catch (Exception e) { + throw new RuntimeException(e); + } + + return ssl; + } + } + + public SSLParameters enableHostNameVerification(SSLParameters sslParameters) { + sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); + return sslParameters; + } + + @Override + public void close() throws IOException { + checkNotNull(eventLoopGroup, "Factory not initialized. You probably forgot to call init()!"); + + eventLoopGroup.shutdownGracefully(); + } + + /** + * Configure connection timeout in milliseconds + */ + public TestcontainersDockerCmdExecFactory withConnectTimeout(Integer connectTimeout) { + this.connectTimeout = connectTimeout; + return this; + } + + /** + * Configure read timeout in milliseconds + */ + public TestcontainersDockerCmdExecFactory withReadTimeout(Integer readTimeout) { + this.readTimeout = readTimeout; + return this; + } + + private T configure(T channel) { + ChannelConfig channelConfig = channel.config(); + + if (connectTimeout != null) { + channelConfig.setConnectTimeoutMillis(connectTimeout); + } + if (readTimeout != null) { + channel.pipeline().addLast("readTimeoutHandler", new ReadTimeoutHandler()); + } + + return channel; + } + + private final class ReadTimeoutHandler extends IdleStateHandler { + private boolean alreadyTimedOut; + + ReadTimeoutHandler() { + super(readTimeout, 0, 0, TimeUnit.MILLISECONDS); + } + + /** + * Called when a read timeout was detected. + */ + @Override + protected synchronized void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception { + assert evt.state() == IdleState.READER_IDLE; + final Channel channel = ctx.channel(); + if (channel == null || !channel.isActive() || alreadyTimedOut) { + return; + } + DockerClientConfig dockerClientConfig = getDockerClientConfig(); + final Object dockerAPIEndpoint = dockerClientConfig.getDockerHost(); + final String msg = "Read timed out: No data received within " + readTimeout + + "ms. Perhaps the docker API (" + dockerAPIEndpoint + + ") is not responding normally, or perhaps you need to increase the readTimeout value."; + final Exception ex = new SocketTimeoutException(msg); + ctx.fireExceptionCaught(ex); + alreadyTimedOut = true; + } + } + + protected WebTarget getBaseResource() { + checkNotNull(baseResource, "Factory not initialized, baseResource not set. You probably forgot to call init()!"); + return baseResource; + } +} diff --git a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java index 6d9427b61f3..71caa59566b 100644 --- a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java +++ b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java @@ -39,7 +39,7 @@ public class ImageFromDockerfile extends LazyFuture implements private static final Set imagesToDelete = Sets.newConcurrentHashSet(); static { - Runtime.getRuntime().addShutdownHook(new Thread(() -> { + Runtime.getRuntime().addShutdownHook(new Thread(DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, () -> { DockerClient dockerClientForCleaning = DockerClientFactory.instance().client(); try { for (String dockerImageName : imagesToDelete) { diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java index 9f99fe064af..0dc4c1afaf8 100644 --- a/core/src/main/java/org/testcontainers/utility/MountableFile.java +++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java @@ -8,6 +8,7 @@ import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.apache.commons.lang.SystemUtils; import org.jetbrains.annotations.NotNull; +import org.testcontainers.DockerClientFactory; import org.testcontainers.images.builder.Transferable; import java.io.File; @@ -280,7 +281,7 @@ private void copyFromJarToLocation(final JarFile jarFile, } private void deleteOnExit(final Path path) { - Runtime.getRuntime().addShutdownHook(new Thread(() -> recursiveDeleteDir(path))); + Runtime.getRuntime().addShutdownHook(new Thread(DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, () -> recursiveDeleteDir(path))); } /** diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index 4c56277b244..054b216dd19 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -112,6 +112,7 @@ public static String start(String hostIpAddress, DockerClient client, boolean wi } Thread kiraThread = new Thread( + DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, () -> { while (true) { int index = 0; @@ -358,7 +359,7 @@ public void unregisterContainer(String identifier) { private void setHook() { if (hookIsSet.compareAndSet(false, true)) { // If the JVM stops without containers being stopped, try and stop the container. - Runtime.getRuntime().addShutdownHook(new Thread(this::performCleanup)); + Runtime.getRuntime().addShutdownHook(new Thread(DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, this::performCleanup)); } } } diff --git a/core/src/test/java/org/testcontainers/DaemonTest.java b/core/src/test/java/org/testcontainers/DaemonTest.java new file mode 100644 index 00000000000..e339f30acc9 --- /dev/null +++ b/core/src/test/java/org/testcontainers/DaemonTest.java @@ -0,0 +1,61 @@ +package org.testcontainers; + +import org.junit.Test; +import org.rnorth.visibleassertions.VisibleAssertions; +import org.testcontainers.containers.GenericContainer; + +import java.io.File; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.junit.Assert.assertEquals; + +/** + * This test forks a new JVM, otherwise it's not possible to reliably diff the threads + */ +public class DaemonTest { + + public static void main(String[] args) { + Thread mainThread = Thread.currentThread(); + + GenericContainer genericContainer = null; + + try { + genericContainer = new GenericContainer().withCommand("top"); + genericContainer.start(); + + Set threads = new HashSet<>(Thread.getAllStackTraces().keySet()); + threads.remove(mainThread); + + Set nonDaemonThreads = threads.stream().filter(it -> !it.isDaemon()).collect(Collectors.toSet()); + + if (nonDaemonThreads.isEmpty()) { + VisibleAssertions.pass("All threads marked as daemon"); + } else { + String nonDaemonThreadNames = nonDaemonThreads.stream() + .map(Thread::getName) + .collect(Collectors.joining("\n", "\n", "")); + + VisibleAssertions.fail("Expected all threads to be daemons but the following are not:\n" + nonDaemonThreadNames); + } + } finally { + if (genericContainer != null) { + genericContainer.stop(); + } + } + } + + @Test + public void testThatAllThreadsAreDaemons() throws Exception { + ProcessBuilder processBuilder = new ProcessBuilder( + new File(System.getProperty("java.home")).toPath().resolve("bin").resolve("java").toString(), + "-ea", + "-classpath", + System.getProperty("java.class.path"), + DaemonTest.class.getCanonicalName() + ); + + assertEquals(0, processBuilder.inheritIO().start().waitFor()); + } +} From 4a258a4355691905b9f62210196879b00fb06412 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 18 Apr 2018 10:34:25 +0200 Subject: [PATCH 0276/5615] Mention docker-java update in CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 932893f84b3..2f502173cfd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ All notable changes to this project will be documented in this file. - Support multiple HTTP status codes for HttpWaitStrategy ([\#630](https://github.com/testcontainers/testcontainers-java/issues/630)) - Mark all long-living threads started by Testcontainers as daemons and group them. ([\#646](https://github.com/testcontainers/testcontainers-java/issues/646)) - Remove noisy `DEBUG` logging of Netty packets ([\#646](https://github.com/testcontainers/testcontainers-java/issues/646)) +- Updated docker-java to 3.1.0-rc-2 ([\#646](https://github.com/testcontainers/testcontainers-java/issues/646)) ## [1.7.0] - 2018-04-07 From 2e7d69fd3a6a7e94a9787892fde57907485a04bb Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 18 Apr 2018 18:04:14 +0200 Subject: [PATCH 0277/5615] fail fast if the event loop is shutting down --- .../transport/TestcontainersDockerCmdExecFactory.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java b/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java index 1a0c52df5ac..fa084a5a577 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java @@ -37,6 +37,7 @@ import java.util.concurrent.TimeUnit; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; /** * This class is a modified version of docker-java's NettyDockerCmdExecFactory v3.1.0-rc-2 @@ -91,6 +92,8 @@ public void init(DockerClientConfig dockerClientConfig) { } private DuplexChannel connect() { + checkState(!eventLoopGroup.isShuttingDown(), "EventLoop is shutting down"); + try { return connect(bootstrap); } catch (InterruptedException e) { From 6ddfe34e52b906fc7b8e2544bb8383f2a2fcb248 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 19 Apr 2018 09:47:08 +0200 Subject: [PATCH 0278/5615] use `createThreadFactory` in `InetSocketInitializer` --- .../transport/TestcontainersDockerCmdExecFactory.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java b/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java index fa084a5a577..9334b9bca41 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java @@ -111,6 +111,10 @@ private interface NettyInitializer { DuplexChannel connect(final Bootstrap bootstrap) throws InterruptedException; } + private ThreadFactory createThreadFactory() { + return new DefaultThreadFactory(THREAD_PREFIX, true, Thread.NORM_PRIORITY, DockerClientFactory.TESTCONTAINERS_THREAD_GROUP); + } + private class UnixDomainSocketInitializer implements NettyInitializer { @Override public EventLoopGroup init(Bootstrap bootstrap, DockerClientConfig dockerClientConfig) { @@ -122,10 +126,6 @@ public EventLoopGroup init(Bootstrap bootstrap, DockerClientConfig dockerClientC throw new RuntimeException("Unspported OS"); } - private ThreadFactory createThreadFactory() { - return new DefaultThreadFactory(THREAD_PREFIX, true, Thread.NORM_PRIORITY, DockerClientFactory.TESTCONTAINERS_THREAD_GROUP); - } - public EventLoopGroup epollGroup() { EventLoopGroup epollEventLoopGroup = new EpollEventLoopGroup(0, createThreadFactory()); @@ -163,7 +163,7 @@ public DuplexChannel connect(Bootstrap bootstrap) throws InterruptedException { private class InetSocketInitializer implements NettyInitializer { @Override public EventLoopGroup init(Bootstrap bootstrap, final DockerClientConfig dockerClientConfig) { - EventLoopGroup nioEventLoopGroup = new NioEventLoopGroup(0, new DefaultThreadFactory(THREAD_PREFIX)); + EventLoopGroup nioEventLoopGroup = new NioEventLoopGroup(0, createThreadFactory()); // TODO do we really need BouncyCastle? Security.addProvider(new BouncyCastleProvider()); From 6a5834e8ff667b7fd30d3d10f2406ec92ffab7f1 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 19 Apr 2018 09:50:41 +0200 Subject: [PATCH 0279/5615] update `org.rnorth:tcp-unix-socket-proxy` to 1.0.2 --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index bf7bda5a8bf..4124bf31549 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -116,7 +116,7 @@ dependencies { exclude(group: 'com.google.code.findbug') } shaded 'javax.ws.rs:javax.ws.rs-api:2.0.1' - shaded 'org.rnorth:tcp-unix-socket-proxy:1.0.1' + shaded 'org.rnorth:tcp-unix-socket-proxy:1.0.2' shaded 'org.zeroturnaround:zt-exec:1.8' shaded 'commons-lang:commons-lang:2.6' shaded 'commons-io:commons-io:2.5' From 58a24f917f1951171aacf9eb6f41e213c03c7244 Mon Sep 17 00:00:00 2001 From: Bengt Brodersen Date: Thu, 19 Apr 2018 21:30:11 +0200 Subject: [PATCH 0280/5615] only determine default gateway within docker container if docker host differ from default (#648) --- .../dockerclient/DockerClientConfigUtils.java | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java index 7b7c948ec4c..d9c1dd2ce58 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java @@ -17,11 +17,13 @@ public class DockerClientConfigUtils { // See https://github.com/docker/docker/blob/a9fa38b1edf30b23cae3eade0be48b3d4b1de14b/daemon/initlayer/setup_unix.go#L25 public static final boolean IN_A_CONTAINER = new File("/.dockerenv").exists(); + @Deprecated @Getter(lazy = true) - private static final Optional detectedDockerHostIp = Optional - .of(IN_A_CONTAINER) - .filter(it -> it) - .map(file -> DockerClientFactory.instance().runInsideDocker( + private static final Optional detectedDockerHostIp = IN_A_CONTAINER ? getDefaultGateway() : Optional.empty(); + + @Getter(lazy = true) + private static final Optional defaultGateway = Optional + .ofNullable(DockerClientFactory.instance().runInsideDocker( cmd -> cmd.withCmd("sh", "-c", "ip route|awk '/default/ { print $3 }'"), (client, id) -> { try { @@ -41,18 +43,21 @@ public class DockerClientConfigUtils { .map(StringUtils::trimToEmpty) .filter(StringUtils::isNotBlank); + + public static String getDockerHostIpAddress(DockerClientConfig config) { - return getDetectedDockerHostIp().orElseGet(() -> { - switch (config.getDockerHost().getScheme()) { - case "http": - case "https": - case "tcp": - return config.getDockerHost().getHost(); - case "unix": - return "localhost"; - default: - return null; - } - }); + switch (config.getDockerHost().getScheme()) { + case "http": + case "https": + case "tcp": + return config.getDockerHost().getHost(); + case "unix": + if (IN_A_CONTAINER) { + return getDefaultGateway().orElse("localhost"); + } + return "localhost"; + default: + return null; + } } } From e2b408f0c784aa725f7b440d63fbd9cb08e84fd0 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 19 Apr 2018 21:36:12 +0200 Subject: [PATCH 0281/5615] add #648 to CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f502173cfd..00417f0e0ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,8 @@ All notable changes to this project will be documented in this file. ### Fixed - Fixed missing `commons-codec` dependency ([\#642](https://github.com/testcontainers/testcontainers-java/issues/642)) - Fixed `HostPortWaitStrategy` throws `NumberFormatException` when port is exposed but not mapped ([\#640](https://github.com/testcontainers/testcontainers-java/issues/640)) -- Fixed log processing: multibyte unicode, linebreaks and ASCII color codes. Color codes can be turned on with `withRemoveAnsiCodes(false)` ([PR \#643](https://github.com/testcontainers/testcontainers-java/pull/643)) +- Fixed log processing: multibyte unicode, linebreaks and ASCII color codes. Color codes can be turned on with `withRemoveAnsiCodes(false)` ([\#643](https://github.com/testcontainers/testcontainers-java/pull/643)) +- Fixed Docker host IP detection within docker container (detect only if not explicitly set) ([\#648](https://github.com/testcontainers/testcontainers-java/pull/648)) ### Changed - Support multiple HTTP status codes for HttpWaitStrategy ([\#630](https://github.com/testcontainers/testcontainers-java/issues/630)) From d07356c70f269899303bbe9a502ca38ca85eb4fd Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 20 Apr 2018 10:39:20 +0200 Subject: [PATCH 0282/5615] Update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00417f0e0ad..208185460f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. ## UNRELEASED +### Fixed + +### Changed + +## [1.7.1] - 2018-04-20 + ### Fixed - Fixed missing `commons-codec` dependency ([\#642](https://github.com/testcontainers/testcontainers-java/issues/642)) - Fixed `HostPortWaitStrategy` throws `NumberFormatException` when port is exposed but not mapped ([\#640](https://github.com/testcontainers/testcontainers-java/issues/640)) From 60f985ffb00f22420f7e493a19b91c3ab0d98ca3 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 24 Apr 2018 08:54:46 +0200 Subject: [PATCH 0283/5615] Add BinTray's "latest version" badge (#653) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 92cc9972cd9..54d96bb5384 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ > Testcontainers is a Java 8 library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container. -[![Build Status](https://travis-ci.org/testcontainers/testcontainers-java.svg?branch=master)](https://travis-ci.org/testcontainers/testcontainers-java) +[![Build Status](https://travis-ci.org/testcontainers/testcontainers-java.svg?branch=master)](https://travis-ci.org/testcontainers/testcontainers-java)[ ![Download](https://api.bintray.com/packages/testcontainers/releases/testcontainers/images/download.svg) ](https://bintray.com/testcontainers/releases/testcontainers/_latestVersion) ![Testcontainers logo](docs/logo.png) From 0c587599d2407f54d4266a7ad49e7f8c58913da9 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 25 Apr 2018 12:06:47 +0200 Subject: [PATCH 0284/5615] remove caching on TravisCI --- .travis.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2075d76f400..2d214f22821 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,14 +6,6 @@ sudo: required services: - docker -before_cache: - - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock - - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ -cache: - directories: - - $HOME/.gradle/caches/ - - $HOME/.gradle/wrapper/ - install: - ./gradlew build -x check From e6368be963a750c0fb21a7ef1210063208a147e3 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 27 Apr 2018 15:51:37 +0200 Subject: [PATCH 0285/5615] use MySQL v5 in JDBCDriverWithPoolTest --- .../java/org/testcontainers/jdbc/JDBCDriverWithPoolTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverWithPoolTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverWithPoolTest.java index 8f936863e7e..c40721223b0 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverWithPoolTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverWithPoolTest.java @@ -28,7 +28,7 @@ @RunWith(ParallelParameterized.class) public class JDBCDriverWithPoolTest { - public static final String URL = "jdbc:tc:mysql://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverWithPoolTest::sampleInitFunction"; + public static final String URL = "jdbc:tc:mysql:5://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverWithPoolTest::sampleInitFunction"; private final DataSource dataSource; @Parameterized.Parameters From 2b483067563943a99c065a668d44fffbcbabb84f Mon Sep 17 00:00:00 2001 From: Jens Goldhammer Date: Sat, 28 Apr 2018 22:03:42 +0200 Subject: [PATCH 0286/5615] Remove invalid characters in ClassRule annotation (#670) The classRule annotation cannot contain characters at the end. It will give the user a compile error. --- docs/usage/options.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage/options.md b/docs/usage/options.md index d26d5a319d5..dbd45804c7c 100644 --- a/docs/usage/options.md +++ b/docs/usage/options.md @@ -124,7 +124,7 @@ public static GenericContainer elasticsearch = If the used image supports Docker's [Healthcheck](https://docs.docker.com/engine/reference/builder/#healthcheck) feature, you can directly leverage the `healthy` state of the container as your wait condition: ```java -@ClassRule2.32.3 +@ClassRule public static GenericContainer container = new GenericContainer("image-with-healthcheck:4.2") .waitingFor(Wait.forHealthcheck()); From 67cde07c401ef3f98420f9d794b574c5bf3e3845 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sat, 28 Apr 2018 23:00:37 +0200 Subject: [PATCH 0287/5615] #662 Retry any exceptions (not just `DockerClientException`) on image pull (#663) --- CHANGELOG.md | 1 + .../java/org/testcontainers/images/RemoteDockerImage.java | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 208185460f2..0aa17a04ad6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file. ## UNRELEASED ### Fixed +- Retry any exceptions (not just `DockerClientException`) on image pull ([\#662](https://github.com/testcontainers/testcontainers-java/issues/662)) ### Changed diff --git a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java index 59f1c2539f5..9554793c004 100644 --- a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java +++ b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java @@ -51,7 +51,7 @@ protected final String resolve() { profiler.start("Check local images"); int attempts = 0; - DockerClientException lastException = null; + Exception lastException = null; while (true) { // Does our cache already know the image? if (AVAILABLE_IMAGE_NAME_CACHE.contains(imageName)) { @@ -98,10 +98,10 @@ protected final String resolve() { .pullImageCmd(imageName.getUnversionedPart()) .withTag(imageName.getVersionPart()) .exec(callback); - callback.awaitSuccess(); + callback.awaitCompletion(); AVAILABLE_IMAGE_NAME_CACHE.add(imageName); break; - } catch (DockerClientException e) { + } catch (Exception e) { lastException = e; } } From bead0b3bbd93aeb5b53e1187ba8037b005400d21 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 29 Apr 2018 13:36:29 +0100 Subject: [PATCH 0288/5615] Pin tags (#671) * Pin tags where it makes sense to do so * Update changelog and docs * Use fixed tag names for JDBC URLs when none specified (equivalent to current 'latest') --- CHANGELOG.md | 1 + .../strategy/AbstractWaitStrategyTest.java | 2 +- .../strategy/HostPortWaitStrategyTest.java | 2 +- docs/usage/database_containers.md | 23 ++++++------------- .../jdbc/DatabaseDriverShutdownTest.java | 4 ++-- .../testcontainers/jdbc/JDBCDriverTest.java | 22 +++++++++--------- .../junit/CustomizablePostgreSQLTest.java | 2 +- .../container-license-acceptance.txt | 2 +- .../JdbcDatabaseContainerProvider.java | 10 ++++++++ .../jdbc/ContainerDatabaseDriver.java | 10 ++++---- .../containers/MariaDBContainer.java | 8 ++++--- .../containers/MariaDBContainerProvider.java | 5 ++++ .../containers/MSSQLServerContainer.java | 8 ++++--- .../MSSQLServerContainerProvider.java | 5 ++++ .../containers/MySQLContainer.java | 4 +++- .../containers/MySQLContainerProvider.java | 5 ++++ .../containers/OracleContainerProvider.java | 8 +++++-- .../containers/PostgreSQLContainer.java | 8 ++++--- .../PostgreSQLContainerProvider.java | 5 ++++ 19 files changed, 85 insertions(+), 49 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0aa17a04ad6..a0c2cb9ba12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. - Retry any exceptions (not just `DockerClientException`) on image pull ([\#662](https://github.com/testcontainers/testcontainers-java/issues/662)) ### Changed +- Database container images are now pinned to a specific version rather than using `latest`. The tags selected are the most recent as of the time of this change. If a JDBC URL is used with no tag specified, a WARN level log message is output, pending a future change to make tags mandatory in the JDBC URL. ([\#671](https://github.com/testcontainers/testcontainers-java/issues/671)) ## [1.7.1] - 2018-04-20 diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java index c6216f0b184..574cef5e2df 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java @@ -20,7 +20,7 @@ */ public abstract class AbstractWaitStrategyTest { static final long WAIT_TIMEOUT_MILLIS = 3000; - static final String IMAGE_NAME = "alpine:latest"; + static final String IMAGE_NAME = "alpine:3.7"; /** * Indicates that the WaitStrategy has completed waiting successfully. diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/HostPortWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/HostPortWaitStrategyTest.java index 840d8864a9b..d715b25aaf6 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/HostPortWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/HostPortWaitStrategyTest.java @@ -15,7 +15,7 @@ */ public class HostPortWaitStrategyTest { - private static final String IMAGE_NAME = "alpine:latest"; + private static final String IMAGE_NAME = "alpine:3.7"; @ClassRule public static GenericContainer container = new GenericContainer(IMAGE_NAME).withExposedPorts() diff --git a/docs/usage/database_containers.md b/docs/usage/database_containers.md index e67c97cc7e8..95e85bad3de 100644 --- a/docs/usage/database_containers.md +++ b/docs/usage/database_containers.md @@ -53,17 +53,17 @@ _N.B:_ * _TC needs to be on your application's classpath at runtime for this to work_ * _For Spring Boot you need to specify the driver manually `spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver`_ -**Original URL**: `jdbc:mysql://somehostname:someport/databasename` +**Original URL**: `jdbc:mysql:5.7.22://somehostname:someport/databasename` Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database name will be ignored; you can leave these as-is or set them to any value. ### JDBC URL examples -#### Simple Testcontainers JDBC driver usage +#### DEPRECATED: Simple Testcontainers JDBC driver usage `jdbc:tc:mysql://somehostname:someport/databasename` -*(Note: this will use the latest version of MySQL)* +*(Note: this will use a fixed version of the database. You should typically specify the version you desire via a tag parameter, as below).* #### Using Testcontainers with a fixed version @@ -71,14 +71,14 @@ Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database #### Using PostgreSQL -`jdbc:tc:postgresql://hostname/databasename` +`jdbc:tc:postgresql:9.6.8://hostname/databasename` ## Using an init script Testcontainers can run an initscript after the database container is started, but before your code is given a connection to it. The script must be on the classpath, and is referenced as follows: -`jdbc:tc:mysql://hostname/databasename?TC_INITSCRIPT=somepath/init_mysql.sql` +`jdbc:tc:mysql:5.7.22://hostname/databasename?TC_INITSCRIPT=somepath/init_mysql.sql` This is useful if you have a fixed script for setting up database schema, etc. @@ -86,7 +86,7 @@ This is useful if you have a fixed script for setting up database schema, etc. Instead of running a fixed script for DB setup, it may be useful to call a Java function that you define. This is intended to allow you to trigger database schema migration tools. To do this, add TC_INITFUNCTION to the URL as follows, passing a full path to the class name and method: - `jdbc:tc:mysql://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction` + `jdbc:tc:mysql:5.7.22://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction` The init function must be a public static method which takes a `java.sql.Connection` as its only parameter, e.g. ```java @@ -101,7 +101,7 @@ public class JDBCDriverTest { By default database container is being stopped as soon as last connection is closed. There are cases when you might need to start container and keep it running till you stop it explicitly or JVM is shutdown. To do this, add `TC_DAEMON` parameter to the URL as follows: - `jdbc:tc:mysql://hostname/databasename?TC_DAEMON=true` + `jdbc:tc:mysql:5.7.22://hostname/databasename?TC_DAEMON=true` With this parameter database container will keep running even when there're no open connections. @@ -114,12 +114,3 @@ is a directory on the classpath containing .cnf files, the following URL can be Any .cnf files in this classpath directory will be mapped into the database container's /etc/mysql/conf.d directory, and will be able to override server settings when the container starts. - -### Additional Non-standard Methods - -#### Virtuoso SPARQL Service URL - -VirtuosoContainer provides access to the SPARQL service URL -```java -String sparqlServiceUrl = ((VirtuosoContainer)container).getSparqlUrl(); -``` diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java index a6bfe756d10..855658ada56 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java @@ -23,7 +23,7 @@ public static void testCleanup() { @Test public void shouldStopContainerWhenAllConnectionsClosed() throws SQLException { - final String jdbcUrl = "jdbc:tc:postgresql://hostname/databasename"; + final String jdbcUrl = "jdbc:tc:postgresql:9.6.8://hostname/databasename"; getConnectionAndClose(jdbcUrl); @@ -33,7 +33,7 @@ public void shouldStopContainerWhenAllConnectionsClosed() throws SQLException { @Test public void shouldNotStopDaemonContainerWhenAllConnectionsClosed() throws SQLException { - final String jdbcUrl = "jdbc:tc:postgresql://hostname/databasename?TC_DAEMON=true"; + final String jdbcUrl = "jdbc:tc:postgresql:9.6.8://hostname/databasename?TC_DAEMON=true"; getConnectionAndClose(jdbcUrl); diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java index eee537a4821..4ef75c2c516 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java @@ -37,20 +37,20 @@ public class JDBCDriverTest { public static Iterable data() { return asList( new Object[][]{ - {"jdbc:tc:mysql:5.5.43://hostname/databasename", false, false, false}, - {"jdbc:tc:mysql://hostname/databasename?TC_INITSCRIPT=somepath/init_mysql.sql", true, false, false}, - {"jdbc:tc:mysql://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", true, false, false}, - {"jdbc:tc:mysql://hostname/databasename?useUnicode=yes&characterEncoding=utf8", false, true, false}, {"jdbc:tc:mysql://hostname/databasename", false, false, false}, - {"jdbc:tc:mysql://hostname/databasename?useSSL=false", false, false, false}, - {"jdbc:tc:postgresql://hostname/databasename", false, false, false}, + {"jdbc:tc:mysql:5.5.43://hostname/databasename?TC_INITSCRIPT=somepath/init_mysql.sql", true, false, false}, + {"jdbc:tc:mysql:5.5.43://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", true, false, false}, + {"jdbc:tc:mysql:5.5.43://hostname/databasename?useUnicode=yes&characterEncoding=utf8", false, true, false}, + {"jdbc:tc:mysql:5.5.43://hostname/databasename", false, false, false}, + {"jdbc:tc:mysql:5.5.43://hostname/databasename?useSSL=false", false, false, false}, + {"jdbc:tc:postgresql:9.6.8://hostname/databasename", false, false, false}, {"jdbc:tc:mysql:5.6://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override", false, false, true}, - {"jdbc:tc:mariadb:10.1.16://hostname/databasename", false, false, false}, {"jdbc:tc:mariadb://hostname/databasename", false, false, false}, - {"jdbc:tc:mariadb://hostname/databasename?useUnicode=yes&characterEncoding=utf8", false, true, false}, - {"jdbc:tc:mariadb://hostname/databasename?TC_INITSCRIPT=somepath/init_mariadb.sql", true, false, false}, - {"jdbc:tc:mariadb://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", true, false, false}, - {"jdbc:tc:mariadb:10.1.16://hostname/databasename?TC_MY_CNF=somepath/mariadb_conf_override", false, false, true} + {"jdbc:tc:mariadb:10.2.14://hostname/databasename", false, false, false}, + {"jdbc:tc:mariadb:10.2.14://hostname/databasename?useUnicode=yes&characterEncoding=utf8", false, true, false}, + {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITSCRIPT=somepath/init_mariadb.sql", true, false, false}, + {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", true, false, false}, + {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_MY_CNF=somepath/mariadb_conf_override", false, false, true} }); } diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomizablePostgreSQLTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomizablePostgreSQLTest.java index 8180ae2a872..ef1c7b212db 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomizablePostgreSQLTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomizablePostgreSQLTest.java @@ -21,7 +21,7 @@ public class CustomizablePostgreSQLTest { private static final String PWD = "baz"; @Rule - public PostgreSQLContainer postgres = new PostgreSQLContainer("postgres:latest") + public PostgreSQLContainer postgres = new PostgreSQLContainer("postgres:9.6.8") .withDatabaseName(DB_NAME) .withUsername(USER) .withPassword(PWD); diff --git a/modules/jdbc-test/src/test/resources/container-license-acceptance.txt b/modules/jdbc-test/src/test/resources/container-license-acceptance.txt index b0c43ef2bff..d706237de16 100644 --- a/modules/jdbc-test/src/test/resources/container-license-acceptance.txt +++ b/modules/jdbc-test/src/test/resources/container-license-acceptance.txt @@ -1 +1 @@ -microsoft/mssql-server-linux:latest +microsoft/mssql-server-linux:2017-CU6 diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainerProvider.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainerProvider.java index dda947b9dd4..aadc266f6f9 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainerProvider.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainerProvider.java @@ -1,11 +1,21 @@ package org.testcontainers.containers; +import lombok.extern.slf4j.Slf4j; + /** * Base class for classes that can provide a JDBC container. */ +@Slf4j public abstract class JdbcDatabaseContainerProvider { public abstract boolean supports(String databaseType); + public JdbcDatabaseContainer newInstance() { + log.warn("No explicit version tag was provided in JDBC URL and this class ({}) does not " + + "override newInstance() to set a default tag. `latest` will be used but results may " + + "be unreliable!", this.getClass().getCanonicalName()); + return this.newInstance("latest"); + } + public abstract JdbcDatabaseContainer newInstance(String tag); } diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java index b7bd9fb4925..569dc794213 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java @@ -102,9 +102,6 @@ public synchronized Connection connect(String url, final Properties info) throws } String databaseType = urlMatcher.group(1); String tag = urlMatcher.group(3); - if (tag == null) { - tag = "latest"; - } queryString = urlMatcher.group(4); if (queryString == null) { @@ -119,7 +116,12 @@ public synchronized Connection connect(String url, final Properties info) throws ServiceLoader databaseContainers = ServiceLoader.load(JdbcDatabaseContainerProvider.class); for (JdbcDatabaseContainerProvider candidateContainerType : databaseContainers) { if (candidateContainerType.supports(databaseType)) { - container = candidateContainerType.newInstance(tag); + + if (tag != null) { + container = candidateContainerType.newInstance(tag); + } else { + container = candidateContainerType.newInstance(); + } delegate = container.getJdbcDriverInstance(); } } diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java index c3717bb579e..e5acd9e0ddb 100644 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java @@ -2,13 +2,15 @@ /** * Container implementation for the MariaDB project. - * + * * @author Miguel Gonzalez Sanchez */ public class MariaDBContainer> extends JdbcDatabaseContainer { public static final String NAME = "mariadb"; public static final String IMAGE = "mariadb"; + public static final String DEFAULT_TAG = "10.3.6"; + private static final Integer MARIADB_PORT = 3306; private static final String MARIADB_USER = "test"; private static final String MARIADB_PASSWORD = "test"; @@ -16,7 +18,7 @@ public class MariaDBContainer> extends JdbcD private static final String MY_CNF_CONFIG_OVERRIDE_PARAM_NAME = "TC_MY_CNF"; public MariaDBContainer() { - super(IMAGE + ":latest"); + super(IMAGE + ":" + DEFAULT_TAG); } public MariaDBContainer(String dockerImageName) { @@ -31,7 +33,7 @@ protected Integer getLivenessCheckPort() { @Override protected void configure() { optionallyMapResourceParameterAsVolume(MY_CNF_CONFIG_OVERRIDE_PARAM_NAME, "/etc/mysql/conf.d", "mariadb-default-conf"); - + addExposedPort(MARIADB_PORT); addEnv("MYSQL_DATABASE", MARIADB_DATABASE); addEnv("MYSQL_USER", MARIADB_USER); diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java index fbe9d807af4..31504b95953 100644 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java @@ -9,6 +9,11 @@ public boolean supports(String databaseType) { return databaseType.equals(MariaDBContainer.NAME); } + @Override + public JdbcDatabaseContainer newInstance() { + return newInstance(MariaDBContainer.DEFAULT_TAG); + } + @Override public JdbcDatabaseContainer newInstance(String tag) { return new MariaDBContainer(MariaDBContainer.IMAGE + ":" + tag); diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java index a44662a7f36..a308560aeb3 100644 --- a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java @@ -6,14 +6,16 @@ * @author Stefan Hufschmidt */ public class MSSQLServerContainer> extends JdbcDatabaseContainer { - static final String NAME = "mssqlserver"; - static final String IMAGE = "microsoft/mssql-server-linux"; + public static final String NAME = "mssqlserver"; + public static final String IMAGE = "microsoft/mssql-server-linux"; + public static final String DEFAULT_TAG = "2017-CU6"; + public static final Integer MS_SQL_SERVER_PORT = 1433; private String username = "SA"; private String password = "A_Str0ng_Required_Password"; public MSSQLServerContainer() { - this(IMAGE + ":latest"); + this(IMAGE + ":" + DEFAULT_TAG); } public MSSQLServerContainer(final String dockerImageName) { diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainerProvider.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainerProvider.java index f7768482a45..daaa82ee771 100644 --- a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainerProvider.java +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainerProvider.java @@ -9,6 +9,11 @@ public boolean supports(String databaseType) { return databaseType.equals(MSSQLServerContainer.NAME); } + @Override + public JdbcDatabaseContainer newInstance() { + return newInstance(MSSQLServerContainer.DEFAULT_TAG); + } + @Override public JdbcDatabaseContainer newInstance(String tag) { return new MSSQLServerContainer(MSSQLServerContainer.IMAGE + ":" + tag); diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java index 74ac5c7e8b5..8c675e4e930 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java @@ -12,6 +12,8 @@ public class MySQLContainer> extends JdbcDatab public static final String NAME = "mysql"; public static final String IMAGE = "mysql"; + public static final String DEFAULT_TAG = "5.7.22"; + private static final String MY_CNF_CONFIG_OVERRIDE_PARAM_NAME = "TC_MY_CNF"; public static final Integer MYSQL_PORT = 3306; private String databaseName = "test"; @@ -19,7 +21,7 @@ public class MySQLContainer> extends JdbcDatab private String password = "test"; public MySQLContainer() { - super(IMAGE + ":latest"); + super(IMAGE + ":" + DEFAULT_TAG); } public MySQLContainer(String dockerImageName) { diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainerProvider.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainerProvider.java index f3e45673f05..f503330d0e5 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainerProvider.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainerProvider.java @@ -9,6 +9,11 @@ public boolean supports(String databaseType) { return databaseType.equals(MySQLContainer.NAME); } + @Override + public JdbcDatabaseContainer newInstance() { + return newInstance(MySQLContainer.DEFAULT_TAG); + } + @Override public JdbcDatabaseContainer newInstance(String tag) { return new MySQLContainer(MySQLContainer.IMAGE + ":" + tag); diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainerProvider.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainerProvider.java index 4ce0fb2cdfb..853ffdaf17a 100644 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainerProvider.java +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainerProvider.java @@ -10,9 +10,13 @@ public boolean supports(String databaseType) { } @Override - public JdbcDatabaseContainer newInstance(String tag) { + public JdbcDatabaseContainer newInstance() { + return new OracleContainer(); + } - if (!tag.equalsIgnoreCase("latest")) { + @Override + public JdbcDatabaseContainer newInstance(String tag) { + if (tag != null) { throw new UnsupportedOperationException("Oracle database tag should be set in the configured image name"); } diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index 23107764644..232daa89284 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -13,15 +13,17 @@ * @author richardnorth */ public class PostgreSQLContainer> extends JdbcDatabaseContainer { - static final String NAME = "postgresql"; - static final String IMAGE = "postgres"; + public static final String NAME = "postgresql"; + public static final String IMAGE = "postgres"; + public static final String DEFAULT_TAG = "9.6.8"; + public static final Integer POSTGRESQL_PORT = 5432; private String databaseName = "test"; private String username = "test"; private String password = "test"; public PostgreSQLContainer() { - this(IMAGE + ":latest"); + this(IMAGE + ":" + DEFAULT_TAG); } public PostgreSQLContainer(final String dockerImageName) { diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainerProvider.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainerProvider.java index fae051b6d97..394fd5d49b8 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainerProvider.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainerProvider.java @@ -9,6 +9,11 @@ public boolean supports(String databaseType) { return databaseType.equals(PostgreSQLContainer.NAME); } + @Override + public JdbcDatabaseContainer newInstance() { + return newInstance(PostgreSQLContainer.DEFAULT_TAG); + } + @Override public JdbcDatabaseContainer newInstance(String tag) { return new PostgreSQLContainer(PostgreSQLContainer.IMAGE + ":" + tag); From 988e16a839f98dd7956825c32037ecd3261d8217 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 29 Apr 2018 22:50:05 +0200 Subject: [PATCH 0289/5615] #664 Fixed handling of the paths with `+` in them (#665) --- CHANGELOG.md | 1 + .../org/testcontainers/utility/MountableFile.java | 2 +- .../testcontainers/utility/MountableFileTest.java | 13 ++++++++++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0c2cb9ba12..1ff008a4bbe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. ### Fixed - Retry any exceptions (not just `DockerClientException`) on image pull ([\#662](https://github.com/testcontainers/testcontainers-java/issues/662)) +- Fixed handling of the paths with `+` in them ([\#664](https://github.com/testcontainers/testcontainers-java/issues/664)) ### Changed - Database container images are now pinned to a specific version rather than using `latest`. The tags selected are the most recent as of the time of this change. If a JDBC URL is used with no tag specified, a WARN level log message is output, pending a future change to make tags mandatory in the JDBC URL. ([\#671](https://github.com/testcontainers/testcontainers-java/issues/671)) diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java index 0dc4c1afaf8..795917fffc4 100644 --- a/core/src/main/java/org/testcontainers/utility/MountableFile.java +++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java @@ -148,7 +148,7 @@ private static URL getClasspathResource(@NotNull final String resourcePath, @Not private static String unencodeResourceURIToFilePath(@NotNull final String resource) { try { // Convert any url-encoded characters (e.g. spaces) back into unencoded form - return URLDecoder.decode(resource, Charsets.UTF_8.name()) + return URLDecoder.decode(resource.replaceAll("\\+", "%2B"), Charsets.UTF_8.name()) .replaceFirst("jar:", "") .replaceFirst("file:", "") .replaceAll("!.*", ""); diff --git a/core/src/test/java/org/testcontainers/utility/MountableFileTest.java b/core/src/test/java/org/testcontainers/utility/MountableFileTest.java index 7bdaeeaeeeb..9b1fbb55a53 100644 --- a/core/src/test/java/org/testcontainers/utility/MountableFileTest.java +++ b/core/src/test/java/org/testcontainers/utility/MountableFileTest.java @@ -65,6 +65,17 @@ public void forHostPathWithSpaces() throws Exception { assertFalse("The resolved path does not contain an escaped space", mountableFile.getResolvedPath().contains("\\ ")); } + @Test + public void forHostPathWithPlus() throws Exception { + final Path file = createTempFile("some+path"); + final MountableFile mountableFile = MountableFile.forHostPath(file.toString()); + + performChecks(mountableFile); + + assertTrue("The resolved path contains the original space", mountableFile.getResolvedPath().contains("+")); + assertFalse("The resolved path does not contain an escaped space", mountableFile.getResolvedPath().contains(" ")); + } + @Test public void forClasspathResourceWithPermission() throws Exception { final MountableFile mountableFile = MountableFile.forClasspathResource("mappable-resource/test-resource.txt", @@ -113,4 +124,4 @@ private void performChecks(final MountableFile mountableFile) { assertFalse("The filesystem path '" + mountablePath + "' does not contain any URL escaping", mountablePath.contains("%20")); } -} \ No newline at end of file +} From 860193aa5239757f92f04dd7f6dc4a22226f1e52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Grill?= Date: Mon, 30 Apr 2018 10:56:56 +0200 Subject: [PATCH 0290/5615] Upgrade gradle to 4.7 (#649) --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 54708 -> 54329 bytes gradle/wrapper/gradle-wrapper.properties | 3 +- gradlew.bat | 168 +++++++++++------------ 4 files changed, 86 insertions(+), 87 deletions(-) diff --git a/build.gradle b/build.gradle index dfc60e5107c..db9d4fcf1c2 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'io.franzbecker.gradle-lombok' version '1.11' + id 'io.franzbecker.gradle-lombok' version '1.12' id 'nebula.provided-base' version '3.0.3' id 'com.github.johnrengelman.shadow' version '2.0.2' id "com.jfrog.bintray" version "1.8.0" apply false diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 736fb7d3f94c051b359fc7ae7212d351bc094bdd..a5fe1cb94b9ee5ce57e6113458225bcba12d83e3 100644 GIT binary patch delta 15814 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

=dO+g3E5Ycz28^Djhlur9JMyN&5nwh(% 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2f6d30c9a6b..73bb13d5561 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Mon Jan 29 20:32:52 CET 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-all.zip diff --git a/gradlew.bat b/gradlew.bat index f9553162f12..e95643d6a2c 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,84 +1,84 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega From 9eef9b39493664611f23cc25e12be1f572ed6b68 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 30 Apr 2018 11:17:46 +0200 Subject: [PATCH 0291/5615] Updated docker-java to 3.1.0-rc-3, enforced `org.jetbrains:annotations:15.0` (#652) (#672) --- CHANGELOG.md | 1 + core/build.gradle | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ff008a4bbe..ebe0875e3e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file. ### Changed - Database container images are now pinned to a specific version rather than using `latest`. The tags selected are the most recent as of the time of this change. If a JDBC URL is used with no tag specified, a WARN level log message is output, pending a future change to make tags mandatory in the JDBC URL. ([\#671](https://github.com/testcontainers/testcontainers-java/issues/671)) +- Updated docker-java to 3.1.0-rc-3, enforced `org.jetbrains:annotations:15.0`. ([\#672](https://github.com/testcontainers/testcontainers-java/issues/672)) ## [1.7.1] - 2018-04-20 diff --git a/core/build.gradle b/core/build.gradle index 4124bf31549..7c15d50e891 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -106,10 +106,13 @@ dependencies { compile 'org.apache.commons:commons-compress:1.15' // Added for JDK9 compatibility since it's missing this package compile 'javax.xml.bind:jaxb-api:2.3.0' - compile 'org.rnorth.duct-tape:duct-tape:1.0.7' + compile ('org.rnorth.duct-tape:duct-tape:1.0.7') { + exclude(group: 'org.jetbrains', module: 'annotations') + } + compile 'org.rnorth.visible-assertions:visible-assertions:2.1.0' - shaded ('com.github.docker-java:docker-java:3.1.0-rc-2') { + shaded ('com.github.docker-java:docker-java:3.1.0-rc-3') { exclude(group: 'org.glassfish.jersey.core') exclude(group: 'org.glassfish.jersey.connectors') exclude(group: 'log4j') From 0ca1d83b18a48881c0b0e7a5328dce5bad813a20 Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 30 Apr 2018 17:09:01 +0100 Subject: [PATCH 0292/5615] Update changelog for 1.7.2 release --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ebe0875e3e1..7e80bd71196 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # Change Log All notable changes to this project will be documented in this file. -## UNRELEASED +## [1.7.2] - 2018-04-30 ### Fixed - Retry any exceptions (not just `DockerClientException`) on image pull ([\#662](https://github.com/testcontainers/testcontainers-java/issues/662)) From 21c95b3489e466e7a37d28708faffd73880228bc Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 30 Apr 2018 18:29:12 +0100 Subject: [PATCH 0293/5615] Add unreleased changelog section for next release --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e80bd71196..c256d198e95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # Change Log All notable changes to this project will be documented in this file. +## UNRELEASED +### Fixed + +### Changed + ## [1.7.2] - 2018-04-30 ### Fixed From bf692d8656403c5f6d5425ce0e55a93e3d147198 Mon Sep 17 00:00:00 2001 From: jamieswogger Date: Wed, 9 May 2018 10:14:07 -0400 Subject: [PATCH 0294/5615] Fix for setting ryuk.container.timeout causes a ClassCastException (#685) * Fix for setting ryuk.container.timeout causes a ClassCastException * Add comment to CHANGELOG.md * Update CHANGELOG.md --- CHANGELOG.md | 1 + .../org/testcontainers/utility/TestcontainersConfiguration.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c256d198e95..f7a0287426b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. ## UNRELEASED ### Fixed +- Fix for setting `ryuk.container.timeout` causes a `ClassCastException` ([\#684](https://github.com/testcontainers/testcontainers-java/issues/684)) ### Changed diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 739a087fc89..1b5f9ce9a59 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -51,7 +51,7 @@ public String getRyukImage() { } public Integer getRyukTimeout() { - return (Integer) properties.getOrDefault("ryuk.container.timeout", 30); + return Integer.parseInt((String) properties.getOrDefault("ryuk.container.timeout", "30")); } public String getKafkaImage() { From 922289710ed5cbbea91f4e135829e1801d88b399 Mon Sep 17 00:00:00 2001 From: Nick Babcock Date: Mon, 14 May 2018 03:59:25 -0500 Subject: [PATCH 0295/5615] Update commons-compress to 1.16.1 (#677) --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 7c15d50e891..cdc9172517c 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -103,7 +103,7 @@ dependencies { compile 'org.jetbrains:annotations:15.0' compile 'javax.annotation:javax.annotation-api:1.3.1' compile 'com.google.code.findbugs:jsr305:3.0.2' - compile 'org.apache.commons:commons-compress:1.15' + compile 'org.apache.commons:commons-compress:1.16.1' // Added for JDK9 compatibility since it's missing this package compile 'javax.xml.bind:jaxb-api:2.3.0' compile ('org.rnorth.duct-tape:duct-tape:1.0.7') { From 469136eacf2c3e79ddcd2ca9298352ca70fa81d0 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 15 May 2018 17:02:55 +0200 Subject: [PATCH 0296/5615] Update Gradle Lombok plugin (fixes Lombok compatibility after the update of Gradle) (#692) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index db9d4fcf1c2..882529fbd0f 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'io.franzbecker.gradle-lombok' version '1.12' + id 'io.franzbecker.gradle-lombok' version '1.14' id 'nebula.provided-base' version '3.0.3' id 'com.github.johnrengelman.shadow' version '2.0.2' id "com.jfrog.bintray" version "1.8.0" apply false From a36451e77265b7113996b3fb3a973dba7db450a7 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 15 May 2018 21:59:37 +0200 Subject: [PATCH 0297/5615] Fixed provided but shaded dependencies in modules by configuring a global shading rule (#693) --- CHANGELOG.md | 1 + build.gradle | 37 +++++++++++++++++++++++++++++++++++++ core/build.gradle | 32 -------------------------------- 3 files changed, 38 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7a0287426b..89b99684ec9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file. ## UNRELEASED ### Fixed - Fix for setting `ryuk.container.timeout` causes a `ClassCastException` ([\#684](https://github.com/testcontainers/testcontainers-java/issues/684)) +- Fixed provided but shaded dependencies in modules ([\#693](https://github.com/testcontainers/testcontainers-java/issues/693)) ### Changed diff --git a/build.gradle b/build.gradle index 882529fbd0f..8a0190349e5 100644 --- a/build.gradle +++ b/build.gradle @@ -10,6 +10,7 @@ subprojects { apply plugin: 'java' apply plugin: 'idea' apply plugin: 'io.franzbecker.gradle-lombok' + apply plugin: 'com.github.johnrengelman.shadow' apply from: "$rootDir/gradle/publishing.gradle" apply from: "$rootDir/gradle/bintray.gradle" @@ -49,6 +50,42 @@ subprojects { jcenter() } + shadowJar { + configurations = [] + classifier = null + + relocate('META-INF/native/libnetty', 'META-INF/native/liborg-testcontainers-shaded-netty') + + [ + "org.apache.http", + "org.apache.commons.lang", + "org.apache.commons.io", + "org.apache.commons.codec", + "org.glassfish", + "org.aopalliance", + "org.jvnet", + "javax.annotation", + "javax.inject", + "javax.ws.rs", + "com.fasterxml.jackson", + "jersey.repackaged", + "com.google", + "io.netty", + "org.bouncycastle", + "org.newsclub", + "org.zeroturnaround" + ].each { relocate(it, "org.testcontainers.shaded.$it") } + } + + publishing { + publications { + mavenJava(MavenPublication) { publication -> + artifacts.removeAll { it.classifier == null } + artifact project.tasks.shadowJar + } + } + } + dependencies { testCompile 'ch.qos.logback:logback-classic:1.2.3' } diff --git a/core/build.gradle b/core/build.gradle index cdc9172517c..dbd18ac072d 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -2,15 +2,6 @@ apply plugin: 'com.github.johnrengelman.shadow' description = "Testcontainers Core" -publishing { - publications { - mavenJava(MavenPublication) { publication -> - artifacts.removeAll { it.classifier == null } - artifact project.tasks.shadowJar - } - } -} - sourceSets { jarFileTest } @@ -24,7 +15,6 @@ configurations { shadowJar { configurations = [project.configurations.shaded] - classifier = null mergeServiceFiles() @@ -46,28 +36,6 @@ shadowJar { 'mozilla/public-suffix-list.txt', ].each { exclude(it) } - relocate('META-INF/native/libnetty', 'META-INF/native/liborg-testcontainers-shaded-netty') - - [ - "org.apache.http", - "org.apache.commons.lang", - "org.apache.commons.io", - "org.apache.commons.codec", - "org.glassfish", - "org.aopalliance", - "org.jvnet", - "javax.annotation", - "javax.inject", - "javax.ws.rs", - "com.fasterxml.jackson", - "jersey.repackaged", - "com.google", - "io.netty", - "org.bouncycastle", - "org.newsclub", - "org.zeroturnaround" - ].each { relocate(it, "org.testcontainers.shaded.$it") } - dependencies { include(dependency('org.apache.httpcomponents:.*')) include(dependency('org.glassfish.*:.*')) From c2e3f80a365ccee6e112e23f7ff5639dac9ad54a Mon Sep 17 00:00:00 2001 From: Ihor Banadiga Date: Wed, 16 May 2018 06:15:00 +0300 Subject: [PATCH 0298/5615] Add InfluxDB support (#686) * influx-db-container * influx-db-container fixed after code review * Add InfluxDB module --- CHANGELOG.md | 1 + modules/influxdb/README.md | 41 +++++ modules/influxdb/build.gradle | 8 + .../containers/InfluxDBContainer.java | 145 ++++++++++++++++++ .../containers/InfluxDBContainerTest.java | 44 ++++++ .../InfluxDBContainerWithUserTest.java | 72 +++++++++ .../src/test/resources/logback-test.xml | 24 +++ 7 files changed, 335 insertions(+) create mode 100644 modules/influxdb/README.md create mode 100644 modules/influxdb/build.gradle create mode 100644 modules/influxdb/src/main/java/org/testcontainers/containers/InfluxDBContainer.java create mode 100644 modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerTest.java create mode 100644 modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerWithUserTest.java create mode 100644 modules/influxdb/src/test/resources/logback-test.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index 89b99684ec9..548d5567725 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. - Fixed provided but shaded dependencies in modules ([\#693](https://github.com/testcontainers/testcontainers-java/issues/693)) ### Changed +- Added InfluxDB module ([\#686](https://github.com/testcontainers/testcontainers-java/pull/686)) ## [1.7.2] - 2018-04-30 diff --git a/modules/influxdb/README.md b/modules/influxdb/README.md new file mode 100644 index 00000000000..f86ea2ccd86 --- /dev/null +++ b/modules/influxdb/README.md @@ -0,0 +1,41 @@ +# Testcontainers InfluxDB testing module + +Testcontainers module for the InfluxData [InfluxDB](https://github.com/influxdata/influxdb). + +## Usage example + +Running influxDbContainer as a stand-in for InfluxDB in a test: + +```java +public class SomeTest { + + @Rule + public InfluxDBContainer influxDbContainer = new InfluxDBContainer(); + + @Test + public void someTestMethod() { + InfluxDB influxDB = influxDbContainer.getNewInfluxDB(); + ... +``` + +## Dependency information + +Replace `VERSION` with the [latest version available on Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.testcontainers%22). + +[![](https://api.bintray.com/packages/testcontainers/releases/testcontainers/images/download.svg)](https://bintray.com/testcontainers/releases/testcontainers/_latestVersion) + + +### Maven +``` + + org.testcontainers + influxdb + VERSION + +``` + +### Gradle + +``` +compile group: 'org.testcontainers', name: 'influxdb', version: 'VERSION' +``` diff --git a/modules/influxdb/build.gradle b/modules/influxdb/build.gradle new file mode 100644 index 00000000000..e0cd0a4a53b --- /dev/null +++ b/modules/influxdb/build.gradle @@ -0,0 +1,8 @@ +description = "Testcontainers :: InfluxDB" + +dependencies { + compile project(':testcontainers') + + compileOnly 'org.influxdb:influxdb-java:2.10' + testCompile 'org.influxdb:influxdb-java:2.10' +} diff --git a/modules/influxdb/src/main/java/org/testcontainers/containers/InfluxDBContainer.java b/modules/influxdb/src/main/java/org/testcontainers/containers/InfluxDBContainer.java new file mode 100644 index 00000000000..00f69c6e4ec --- /dev/null +++ b/modules/influxdb/src/main/java/org/testcontainers/containers/InfluxDBContainer.java @@ -0,0 +1,145 @@ +package org.testcontainers.containers; + +import org.influxdb.InfluxDB; +import org.influxdb.InfluxDBFactory; +import org.testcontainers.containers.traits.LinkableContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.containers.wait.strategy.WaitAllStrategy; + +/** + * @link https://store.docker.com/images/influxdb + */ +public class InfluxDBContainer> extends GenericContainer + implements LinkableContainer { + + public static final String VERSION = "1.4.3"; + public static final Integer INFLUXDB_PORT = 8086; + + private static final String IMAGE_NAME = "influxdb"; + + private boolean authEnabled = true; + private String admin = "admin"; + private String adminPassword = "password"; + + private String database; + private String username = "any"; + private String password = "any"; + + + public InfluxDBContainer() { + this(VERSION); + } + + public InfluxDBContainer(final String version) { + super(IMAGE_NAME + ":" + version); + waitStrategy = new WaitAllStrategy() + .withStrategy(Wait.forHttp("/ping").withBasicCredentials(username, password).forStatusCode(204)) + .withStrategy(Wait.forListeningPort()); + } + + @Override + protected void configure() { + addExposedPort(INFLUXDB_PORT); + + addEnv("INFLUXDB_ADMIN_USER", admin); + addEnv("INFLUXDB_ADMIN_PASSWORD", adminPassword); + + addEnv("INFLUXDB_HTTP_AUTH_ENABLED", String.valueOf(authEnabled)); + + addEnv("INFLUXDB_DB", database); + addEnv("INFLUXDB_USER", username); + addEnv("INFLUXDB_USER_PASSWORD", password); + } + + @Override + protected Integer getLivenessCheckPort() { + return getMappedPort(INFLUXDB_PORT); + } + + + /** + * Set env variable `INFLUXDB_HTTP_AUTH_ENABLED`. + * + * @param authEnabled Enables authentication. + * @return a reference to this container instance + */ + public SELF withAuthEnabled(final boolean authEnabled) { + this.authEnabled = authEnabled; + return self(); + } + + /** + * Set env variable `INFLUXDB_ADMIN_USER`. + * + * @param admin The name of the admin user to be created. If this is unset, no admin user is created. + * @return a reference to this container instance + */ + public SELF withAdmin(final String admin) { + this.admin = admin; + return self(); + } + + /** + * Set env variable `INFLUXDB_ADMIN_PASSWORD`. + * + * @param adminPassword TThe password for the admin user configured with `INFLUXDB_ADMIN_USER`. If this is unset, a + * random password is generated and printed to standard out. + * @return a reference to this container instance + */ + public SELF withAdminPassword(final String adminPassword) { + this.adminPassword = adminPassword; + return self(); + } + + /** + * Set env variable `INFLUXDB_DB`. + * + * @param database Automatically initializes a database with the name of this environment variable. + * @return a reference to this container instance + */ + public SELF withDatabase(final String database) { + this.database = database; + return self(); + } + + /** + * Set env variable `INFLUXDB_USER`. + * + * @param username The name of a user to be created with no privileges. If `INFLUXDB_DB` is set, this user will + * be granted read and write permissions for that database. + * @return a reference to this container instance + */ + public SELF withUsername(final String username) { + this.username = username; + return self(); + } + + /** + * Set env variable `INFLUXDB_USER_PASSWORD`. + * + * @param password The password for the user configured with `INFLUXDB_USER`. If this is unset, a random password + * is generated and printed to standard out. + * @return a reference to this container instance + */ + public SELF withPassword(final String password) { + this.password = password; + return self(); + } + + + /** + * @return a url to influxDb + */ + public String getUrl() { + return "http://" + getContainerIpAddress() + ":" + getLivenessCheckPort(); + } + + /** + * @return a influxDb client + */ + public InfluxDB getNewInfluxDB() { + InfluxDB influxDB = InfluxDBFactory.connect(getUrl(), username, password); + influxDB.setDatabase(database); + return influxDB; + } +} diff --git a/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerTest.java b/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerTest.java new file mode 100644 index 00000000000..fa2e54fd0c9 --- /dev/null +++ b/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerTest.java @@ -0,0 +1,44 @@ +package org.testcontainers.containers; + +import org.influxdb.InfluxDB; +import org.junit.ClassRule; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.junit.Assert.assertThat; + +public class InfluxDBContainerTest { + + @ClassRule + public static InfluxDBContainer influxDBContainer = new InfluxDBContainer(); + + @Test + public void getUrl() { + String actual = influxDBContainer.getUrl(); + + assertThat(actual, notNullValue()); + } + + @Test + public void getNewInfluxDB() { + InfluxDB actual = influxDBContainer.getNewInfluxDB(); + + assertThat(actual, notNullValue()); + assertThat(actual.ping(), notNullValue()); + } + + @Test + public void getLivenessCheckPort() { + Integer actual = influxDBContainer.getLivenessCheckPort(); + + assertThat(actual, notNullValue()); + } + + @Test + public void isRunning() { + boolean actual = influxDBContainer.isRunning(); + + assertThat(actual, is(true)); + } +} diff --git a/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerWithUserTest.java b/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerWithUserTest.java new file mode 100644 index 00000000000..81ad62a7b47 --- /dev/null +++ b/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerWithUserTest.java @@ -0,0 +1,72 @@ +package org.testcontainers.containers; + +import org.influxdb.InfluxDB; +import org.influxdb.dto.Point; +import org.influxdb.dto.Query; +import org.influxdb.dto.QueryResult; +import org.junit.Rule; +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; + +public class InfluxDBContainerWithUserTest { + + private static final String TEST_VERSION = "1.4.3"; + private static final String DATABASE = "test"; + private static final String USER = "test-user"; + private static final String PASSWORD = "test-password"; + + @Rule + public InfluxDBContainer influxDBContainer = new InfluxDBContainer(TEST_VERSION) + .withDatabase(DATABASE) + .withUsername(USER) + .withPassword(PASSWORD); + + @Test + public void describeDatabases() { + InfluxDB actual = influxDBContainer.getNewInfluxDB(); + + assertThat(actual, notNullValue()); + assertThat(actual.describeDatabases(), hasItem(DATABASE)); + } + + @Test + public void checkVersion() { + InfluxDB actual = influxDBContainer.getNewInfluxDB(); + + assertThat(actual, notNullValue()); + + assertThat(actual.ping(), notNullValue()); + assertThat(actual.ping().getVersion(), is(TEST_VERSION)); + + assertThat(actual.version(), is(TEST_VERSION)); + } + + @Test + public void queryForWriteAndRead() { + InfluxDB influxDB = influxDBContainer.getNewInfluxDB(); + + Point point = Point.measurement("cpu") + .time(System.currentTimeMillis(), TimeUnit.MILLISECONDS) + .addField("idle", 90L) + .addField("user", 9L) + .addField("system", 1L) + .build(); + influxDB.write(point); + + Query query = new Query("SELECT idle FROM cpu", DATABASE); + QueryResult actual = influxDB.query(query); + + assertThat(actual, notNullValue()); + assertThat(actual.getError(), nullValue()); + assertThat(actual.getResults(), notNullValue()); + assertThat(actual.getResults().size(), is(1)); + + } +} diff --git a/modules/influxdb/src/test/resources/logback-test.xml b/modules/influxdb/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..e8f35ff1c79 --- /dev/null +++ b/modules/influxdb/src/test/resources/logback-test.xml @@ -0,0 +1,24 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + + + + + + + + + From b8eb097288692cddf6209b945398d2d5141298bc Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 16 May 2018 05:15:25 +0200 Subject: [PATCH 0299/5615] Fix macOS < 10.12 (#689) * Fix macOS < 10.12 (shaded native library, missing dependency inclusion). Also, always fallback to TCP proxy if kqueue failed * Fix priority --- core/build.gradle | 5 ++++- .../ProxiedUnixSocketClientProviderStrategy.java | 9 ++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index dbd18ac072d..167c4234910 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -80,6 +80,10 @@ dependencies { compile 'org.rnorth.visible-assertions:visible-assertions:2.1.0' + compile ('org.rnorth:tcp-unix-socket-proxy:1.0.2') { + exclude(group: "log4j", module: "log4j") + } + shaded ('com.github.docker-java:docker-java:3.1.0-rc-3') { exclude(group: 'org.glassfish.jersey.core') exclude(group: 'org.glassfish.jersey.connectors') @@ -87,7 +91,6 @@ dependencies { exclude(group: 'com.google.code.findbug') } shaded 'javax.ws.rs:javax.ws.rs-api:2.0.1' - shaded 'org.rnorth:tcp-unix-socket-proxy:1.0.2' shaded 'org.zeroturnaround:zt-exec:1.8' shaded 'commons-lang:commons-lang:2.6' shaded 'commons-io:commons-io:2.5' diff --git a/core/src/main/java/org/testcontainers/dockerclient/ProxiedUnixSocketClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/ProxiedUnixSocketClientProviderStrategy.java index 63785f0c86d..84671e32efd 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/ProxiedUnixSocketClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/ProxiedUnixSocketClientProviderStrategy.java @@ -1,25 +1,20 @@ package org.testcontainers.dockerclient; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang.SystemUtils; import org.rnorth.tcpunixsocketproxy.TcpToUnixSocketProxy; -import org.testcontainers.utility.ComparableVersion; import java.io.File; @Slf4j public class ProxiedUnixSocketClientProviderStrategy extends UnixSocketClientProviderStrategy { - public static final int PRIORITY = EnvironmentAndSystemPropertyClientProviderStrategy.PRIORITY - 10; + public static final int PRIORITY = EnvironmentAndSystemPropertyClientProviderStrategy.PRIORITY - 100; private final File socketFile = new File(DOCKER_SOCK_PATH); @Override protected boolean isApplicable() { - final boolean nettyDoesNotSupportMacUnixSockets = SystemUtils.IS_OS_MAC_OSX && - ComparableVersion.OS_VERSION.isLessThan("10.12"); - - return nettyDoesNotSupportMacUnixSockets && socketFile.exists(); + return socketFile.exists(); } @Override From b1187712deeec0e04a9afe3f5be79e715256d82c Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 16 May 2018 05:27:19 +0200 Subject: [PATCH 0300/5615] Add JavaDoc step to CI --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2d214f22821..52e2adcf814 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ jobs: include: - stage: test env: [ NAME=core ] - script: ./gradlew testcontainers:check + script: ./gradlew testcontainers:check javadoc - stage: test env: [ NAME=core ] From ba62d9e69643fa121952a7b1c40fc686b6b863f4 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 16 May 2018 05:34:45 +0200 Subject: [PATCH 0301/5615] fix InfluxDBContainer's JavaDoc and remove deprecated APIs --- .../containers/InfluxDBContainer.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/influxdb/src/main/java/org/testcontainers/containers/InfluxDBContainer.java b/modules/influxdb/src/main/java/org/testcontainers/containers/InfluxDBContainer.java index 00f69c6e4ec..ee5b449f961 100644 --- a/modules/influxdb/src/main/java/org/testcontainers/containers/InfluxDBContainer.java +++ b/modules/influxdb/src/main/java/org/testcontainers/containers/InfluxDBContainer.java @@ -2,15 +2,16 @@ import org.influxdb.InfluxDB; import org.influxdb.InfluxDBFactory; -import org.testcontainers.containers.traits.LinkableContainer; import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.containers.wait.strategy.WaitAllStrategy; +import java.util.Collections; +import java.util.Set; + /** - * @link https://store.docker.com/images/influxdb + * See https://store.docker.com/images/influxdb */ -public class InfluxDBContainer> extends GenericContainer - implements LinkableContainer { +public class InfluxDBContainer> extends GenericContainer { public static final String VERSION = "1.4.3"; public static final Integer INFLUXDB_PORT = 8086; @@ -52,11 +53,10 @@ protected void configure() { } @Override - protected Integer getLivenessCheckPort() { - return getMappedPort(INFLUXDB_PORT); + public Set getLivenessCheckPortNumbers() { + return Collections.singleton(getMappedPort(INFLUXDB_PORT)); } - /** * Set env variable `INFLUXDB_HTTP_AUTH_ENABLED`. * From ca2dd7a4cc45b450749eb67b643dc8cba3abc311 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 16 May 2018 10:39:33 +0200 Subject: [PATCH 0302/5615] Modernize LocalStackContainer (#695) --- .../localstack/LocalStackContainer.java | 86 ++++++++----------- 1 file changed, 34 insertions(+), 52 deletions(-) diff --git a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java index 18738de6ca6..319581e9f31 100644 --- a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java +++ b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java @@ -4,19 +4,20 @@ import com.amazonaws.auth.AWSStaticCredentialsProvider; import com.amazonaws.auth.BasicAWSCredentials; import com.amazonaws.client.builder.AwsClientBuilder; -import org.jetbrains.annotations.Nullable; -import org.junit.rules.ExternalResource; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; import org.rnorth.ducttape.Preconditions; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.wait.LogMessageWaitStrategy; +import org.testcontainers.containers.wait.strategy.Wait; import java.net.InetAddress; import java.net.UnknownHostException; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.stream.Collectors; -import static org.testcontainers.containers.BindMode.READ_WRITE; - /** *

Container for Atlassian Labs Localstack, 'A fully functional local AWS cloud stack'.

*

{@link LocalStackContainer#withServices(Service...)} should be used to select which services @@ -25,41 +26,34 @@ * {@link LocalStackContainer#getDefaultCredentialsProvider()} * be used to obtain compatible endpoint configuration and credentials, respectively.

*/ -public class LocalStackContainer extends ExternalResource { - - @Nullable private GenericContainer delegate; - private Service[] services; - - @Override - protected void before() throws Throwable { +public class LocalStackContainer extends GenericContainer { - Preconditions.check("services list must not be empty", services != null && services.length > 0); + public static final String VERSION = "0.8.6"; - final String servicesList = Arrays - .stream(services) - .map(Service::getLocalStackName) - .collect(Collectors.joining(",")); + private final List services = new ArrayList<>(); - final Integer[] portsList = Arrays - .stream(services) - .map(Service::getPort) - .collect(Collectors.toSet()).toArray(new Integer[]{}); + public LocalStackContainer() { + this(VERSION); + } - delegate = new GenericContainer("localstack/localstack:0.8.5") - .withExposedPorts(portsList) - .withFileSystemBind("//var/run/docker.sock", "/var/run/docker.sock", READ_WRITE) - .waitingFor(new LogMessageWaitStrategy().withRegEx(".*Ready\\.\n")) - .withEnv("SERVICES", servicesList); + public LocalStackContainer(String version) { + super("localstack/localstack:" + version); - delegate.start(); + withFileSystemBind("//var/run/docker.sock", "/var/run/docker.sock"); + waitingFor(Wait.forLogMessage(".*Ready\\.\n", 1)); } @Override - protected void after() { + protected void configure() { + super.configure(); + + Preconditions.check("services list must not be empty", !services.isEmpty()); - Preconditions.check("delegate must have been created by before()", delegate != null); + withEnv("SERVICES", services.stream().map(Service::getLocalStackName).collect(Collectors.joining(","))); - delegate.stop(); + for (Service service : services) { + addExposedPort(service.getPort()); + } } /** @@ -68,8 +62,8 @@ protected void after() { * @return this container object */ public LocalStackContainer withServices(Service... services) { - this.services = services; - return this; + this.services.addAll(Arrays.asList(services)); + return self(); } /** @@ -85,19 +79,14 @@ public LocalStackContainer withServices(Service... services) { * @return an {@link AwsClientBuilder.EndpointConfiguration} */ public AwsClientBuilder.EndpointConfiguration getEndpointConfiguration(Service service) { - - if (delegate == null) { - throw new IllegalStateException("LocalStack has not been started yet!"); - } - - final String address = delegate.getContainerIpAddress(); + final String address = getContainerIpAddress(); String ipAddress = address; try { ipAddress = InetAddress.getByName(address).getHostAddress(); } catch (UnknownHostException ignored) { } - ipAddress = ipAddress + ".xip.io"; + ipAddress = ipAddress + ".nip.io"; while (true) { try { //noinspection ResultOfMethodCallIgnored @@ -112,7 +101,7 @@ public AwsClientBuilder.EndpointConfiguration getEndpointConfiguration(Service s "http://" + ipAddress + ":" + - delegate.getMappedPort(service.getPort()), "us-east-1"); + getMappedPort(service.getPort()), "us-east-1"); } /** @@ -130,6 +119,9 @@ public AWSCredentialsProvider getDefaultCredentialsProvider() { return new AWSStaticCredentialsProvider(new BasicAWSCredentials("accesskey", "secretkey")); } + @RequiredArgsConstructor + @Getter + @FieldDefaults(makeFinal = true) public enum Service { API_GATEWAY("apigateway", 4567), KINESIS("kinesis", 4568), @@ -149,18 +141,8 @@ public enum Service { CLOUDFORMATION("cloudformation", 4581), CLOUDWATCH("cloudwatch", 4582); - private final String localStackName; - private final int port; - - Service(String localstackName, int port) { - this.localStackName = localstackName; - this.port = port; - } - - public String getLocalStackName() { - return localStackName; - } + String localStackName; - public Integer getPort() { return port; } + int port; } } From 0a357ffc5dd4d30c9a0df8342f702556cd629c2c Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 16 May 2018 10:39:51 +0200 Subject: [PATCH 0303/5615] Mockserver module (#696) * Add MockServer module * CHANGELOG.md * remove delegate --- CHANGELOG.md | 1 + modules/mockserver/build.gradle | 8 ++++ .../containers/MockServerContainer.java | 38 +++++++++++++++++++ .../containers/MockServerContainerTest.java | 34 +++++++++++++++++ 4 files changed, 81 insertions(+) create mode 100644 modules/mockserver/build.gradle create mode 100644 modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java create mode 100644 modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 548d5567725..bbea3303457 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. ### Changed - Added InfluxDB module ([\#686](https://github.com/testcontainers/testcontainers-java/pull/686)) +- Added MockServer module ([\#696](https://github.com/testcontainers/testcontainers-java/pull/696)) ## [1.7.2] - 2018-04-30 diff --git a/modules/mockserver/build.gradle b/modules/mockserver/build.gradle new file mode 100644 index 00000000000..09a4077e859 --- /dev/null +++ b/modules/mockserver/build.gradle @@ -0,0 +1,8 @@ +description = "Testcontainers :: MockServer" + +dependencies { + compile project(':testcontainers') + + compileOnly 'org.mock-server:mockserver-client-java:5.3.0' + testCompile 'org.mock-server:mockserver-client-java:5.3.0' +} diff --git a/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java b/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java new file mode 100644 index 00000000000..5580010be1f --- /dev/null +++ b/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java @@ -0,0 +1,38 @@ +package org.testcontainers.containers; + +import com.github.dockerjava.api.command.InspectContainerResponse; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.mockserver.client.server.MockServerClient; + +@Slf4j +public class MockServerContainer extends GenericContainer { + + public static final String VERSION = "5.3.0"; + + public static final int PORT = 80; + + @Getter + private MockServerClient client; + + public MockServerContainer() { + this(VERSION); + } + + public MockServerContainer(String version) { + super("jamesdbloom/mockserver:mockserver-" + version); + withCommand("/opt/mockserver/run_mockserver.sh -logLevel INFO -serverPort " + PORT); + addExposedPorts(PORT); + } + + @Override + protected void containerIsStarted(InspectContainerResponse containerInfo) { + super.containerIsStarted(containerInfo); + + client = new MockServerClient(getContainerIpAddress(), getMappedPort(PORT)); + } + + public String getEndpoint() { + return String.format("http://%s:%d", getContainerIpAddress(), getMappedPort(PORT)); + } +} diff --git a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java new file mode 100644 index 00000000000..4e51dc34a47 --- /dev/null +++ b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java @@ -0,0 +1,34 @@ +package org.testcontainers.containers; + +import lombok.Cleanup; +import org.junit.ClassRule; +import org.junit.Test; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.URL; +import java.net.URLConnection; + +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; + +public class MockServerContainerTest { + + @ClassRule + public static MockServerContainer mockServer = new MockServerContainer(); + + @Test + public void testBasicScenario() throws Exception { + mockServer.getClient() + .when(request("/hello")) + .respond(response("Hello World!")); + + URLConnection urlConnection = new URL(mockServer.getEndpoint() + "/hello").openConnection(); + @Cleanup BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); + String line = reader.readLine(); + System.out.println(line); + + assertTrue("MockServer returns correct result", line.contains("Hello World!")); + } +} From 02e38b4090155fca6246c08876f470e1f1ce5021 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 16 May 2018 11:00:24 +0200 Subject: [PATCH 0304/5615] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbea3303457..5cb96e27ce2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file. ### Changed - Added InfluxDB module ([\#686](https://github.com/testcontainers/testcontainers-java/pull/686)) - Added MockServer module ([\#696](https://github.com/testcontainers/testcontainers-java/pull/696)) +- Changed LocalStackContainer to extend GenericContainer ([\#695](https://github.com/testcontainers/testcontainers-java/pull/695)) ## [1.7.2] - 2018-04-30 From 423df8c50fd769faea7db3546c0bfcd3b1de1aab Mon Sep 17 00:00:00 2001 From: Stefan Hufschmidt Date: Wed, 16 May 2018 13:46:52 +0200 Subject: [PATCH 0305/5615] Spring boot2.0 (#13) * Raised Spring-Boot version to 2.0.1.RELEASE * Moved Web-Endpoint to Controller class * Renamed test to match introduced controller * Changed deprecated usage of EnvironmentTestUtils to TestPropertyValues --- spring-boot/pom.xml | 4 ++-- .../java/com/example/DemoApplication.java | 19 --------------- .../main/java/com/example/DemoController.java | 24 +++++++++++++++++++ .../com/example/AbstractIntegrationTest.java | 5 ++-- ...ationTest.java => DemoControllerTest.java} | 2 +- 5 files changed, 30 insertions(+), 24 deletions(-) create mode 100644 spring-boot/src/main/java/com/example/DemoController.java rename spring-boot/src/test/java/com/example/{DemoApplicationTest.java => DemoControllerTest.java} (89%) diff --git a/spring-boot/pom.xml b/spring-boot/pom.xml index f5bc4ee8833..f01eaccc9e7 100644 --- a/spring-boot/pom.xml +++ b/spring-boot/pom.xml @@ -21,7 +21,7 @@ org.springframework.boot spring-boot-dependencies - 1.5.2.RELEASE + 2.0.1.RELEASE pom import @@ -50,7 +50,7 @@ org.springframework.boot spring-boot-maven-plugin - 1.3.5.RELEASE + 2.0.1.RELEASE diff --git a/spring-boot/src/main/java/com/example/DemoApplication.java b/spring-boot/src/main/java/com/example/DemoApplication.java index 3373643d4d7..ad5a9d9fed3 100644 --- a/spring-boot/src/main/java/com/example/DemoApplication.java +++ b/spring-boot/src/main/java/com/example/DemoApplication.java @@ -1,32 +1,13 @@ package com.example; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; @SpringBootApplication -@RestController public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } - @Autowired - StringRedisTemplate stringRedisTemplate; - - @RequestMapping(value = "/foo", method = RequestMethod.GET) - public String get() { - return stringRedisTemplate.opsForValue().get("foo"); - } - - @RequestMapping(value = "/foo", method = RequestMethod.PUT) - public void set(@RequestBody String value) { - stringRedisTemplate.opsForValue().set("foo", value); - } } diff --git a/spring-boot/src/main/java/com/example/DemoController.java b/spring-boot/src/main/java/com/example/DemoController.java new file mode 100644 index 00000000000..c1be9d5b611 --- /dev/null +++ b/spring-boot/src/main/java/com/example/DemoController.java @@ -0,0 +1,24 @@ +package com.example; + +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.web.bind.annotation.*; + +@RestController +public class DemoController { + + private final StringRedisTemplate stringRedisTemplate; + + public DemoController(StringRedisTemplate stringRedisTemplate){ + this.stringRedisTemplate = stringRedisTemplate; + } + + @GetMapping("/foo") + public String get() { + return stringRedisTemplate.opsForValue().get("foo"); + } + + @PutMapping("/foo") + public void set(@RequestBody String value) { + stringRedisTemplate.opsForValue().set("foo", value); + } +} diff --git a/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java b/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java index 32b4b7b4ff0..40e60f45b25 100644 --- a/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java +++ b/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java @@ -4,7 +4,7 @@ import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.util.EnvironmentTestUtils; +import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.test.context.ContextConfiguration; @@ -22,10 +22,11 @@ public abstract class AbstractIntegrationTest { public static class Initializer implements ApplicationContextInitializer { @Override public void initialize(ConfigurableApplicationContext configurableApplicationContext) { - EnvironmentTestUtils.addEnvironment("testcontainers", configurableApplicationContext.getEnvironment(), + TestPropertyValues values = TestPropertyValues.of( "spring.redis.host=" + redis.getContainerIpAddress(), "spring.redis.port=" + redis.getMappedPort(6379) ); + values.applyTo(configurableApplicationContext); } } diff --git a/spring-boot/src/test/java/com/example/DemoApplicationTest.java b/spring-boot/src/test/java/com/example/DemoControllerTest.java similarity index 89% rename from spring-boot/src/test/java/com/example/DemoApplicationTest.java rename to spring-boot/src/test/java/com/example/DemoControllerTest.java index edf1590e6b1..2b2d35963bc 100644 --- a/spring-boot/src/test/java/com/example/DemoApplicationTest.java +++ b/spring-boot/src/test/java/com/example/DemoControllerTest.java @@ -6,7 +6,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.web.client.TestRestTemplate; -public class DemoApplicationTest extends AbstractIntegrationTest { +public class DemoControllerTest extends AbstractIntegrationTest { @Autowired TestRestTemplate restTemplate; From b44c558d8defc273e1dd803e2ed74fba49537b85 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 16 May 2018 23:04:51 +0200 Subject: [PATCH 0306/5615] Update CHANGELOG.md --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cb96e27ce2..39f4105097d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. ## UNRELEASED + +### Fixed + +### Changed + +## [1.7.3] - 2018-05-16 + ### Fixed - Fix for setting `ryuk.container.timeout` causes a `ClassCastException` ([\#684](https://github.com/testcontainers/testcontainers-java/issues/684)) - Fixed provided but shaded dependencies in modules ([\#693](https://github.com/testcontainers/testcontainers-java/issues/693)) From 0aeaec32714027ef4b0815780787243576e9c735 Mon Sep 17 00:00:00 2001 From: Sijie Guo Date: Fri, 18 May 2018 11:34:42 -0700 Subject: [PATCH 0307/5615] Allow testcontainer to wait for a specific port (#703) * Allow testcontainer to wait for a specific port *Motivation* A container might expose multiple ports for different purposes. for example, it can have a port for http endpoint, while having another port for grpc endpoint. So when doing a liveness check, it would be better to allow configuring checking a specific port, rather than checking the first exposed port. *Modification* Modify the `HttpWaitStrategy` to allow configuring the liveness port. * - change logging from info to trace - add an item in CHANGELOG.md * Update CHANGELOG.md --- CHANGELOG.md | 1 + .../wait/strategy/HttpWaitStrategy.java | 34 ++++++++++++++++--- .../strategy/AbstractWaitStrategyTest.java | 6 +++- .../wait/strategy/HttpWaitStrategyTest.java | 20 ++++++++++- 4 files changed, 54 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39f4105097d..65ff3441cb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. ### Fixed ### Changed +- Allow `HttpWaitStrategy` to wait for a specific port ([\#703](https://github.com/testcontainers/testcontainers-java/pull/703)) ## [1.7.3] - 2018-05-16 diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java index f74b51c602b..29f5a64b6df 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java @@ -2,6 +2,7 @@ import com.google.common.base.Strings; import com.google.common.io.BaseEncoding; +import java.util.Optional; import lombok.extern.slf4j.Slf4j; import org.rnorth.ducttape.TimeoutException; import org.testcontainers.containers.ContainerLaunchException; @@ -43,6 +44,7 @@ public class HttpWaitStrategy extends AbstractWaitStrategy { if (statusCodes.isEmpty() && HttpURLConnection.HTTP_OK == responseCode) return true; return statusCodes.contains(responseCode); }; + private Optional livenessPort = Optional.empty(); /** * Waits for the given status code. @@ -76,6 +78,17 @@ public HttpWaitStrategy forPath(String path) { return this; } + /** + * Wait for the given port. + * + * @param port the given port + * @return this + */ + public HttpWaitStrategy forPort(int port) { + this.livenessPort = Optional.of(port); + return this; + } + /** * Indicates that the status check should use HTTPS. * @@ -112,13 +125,19 @@ public HttpWaitStrategy forResponsePredicate(Predicate responsePredicate @Override protected void waitUntilReady() { final String containerName = waitStrategyTarget.getContainerInfo().getName(); - final Set livenessCheckPorts = getLivenessCheckPorts(); - if (livenessCheckPorts == null || livenessCheckPorts.isEmpty()) { - log.warn("{}: No exposed ports or mapped ports - cannot wait for status", containerName); + + final Integer livenessCheckPort = livenessPort.map(waitStrategyTarget::getMappedPort).orElseGet(() -> { + final Set livenessCheckPorts = getLivenessCheckPorts(); + if (livenessCheckPorts == null || livenessCheckPorts.isEmpty()) { + log.warn("{}: No exposed ports or mapped ports - cannot wait for status", containerName); + return -1; + } + return livenessCheckPorts.iterator().next(); + }); + + if (null == livenessCheckPort || -1 == livenessCheckPort) { return; } - - final Integer livenessCheckPort = livenessCheckPorts.iterator().next(); final String uri = buildLivenessUri(livenessCheckPort).toString(); log.info("{}: Waiting for {} seconds for URL: {}", containerName, startupTimeout.getSeconds(), uri); @@ -138,6 +157,8 @@ protected void waitUntilReady() { connection.setRequestMethod("GET"); connection.connect(); + log.trace("Get response code {}", connection.getResponseCode()); + if (!statusCodePredicate.test(connection.getResponseCode())) { throw new RuntimeException(String.format("HTTP response code was: %s", connection.getResponseCode())); @@ -145,6 +166,9 @@ protected void waitUntilReady() { if(responsePredicate != null) { String responseBody = getResponseBody(connection); + + log.trace("Get response {}", responseBody); + if(!responsePredicate.test(responseBody)) { throw new RuntimeException(String.format("Response: %s did not match predicate", responseBody)); diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java index 574cef5e2df..3d3c2f0606d 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java @@ -62,9 +62,13 @@ private GenericContainer startContainerWithCommand(String shellCommand) { * @return the (unstarted) container */ protected GenericContainer startContainerWithCommand(String shellCommand, WaitStrategy waitStrategy) { + return startContainerWithCommand(shellCommand, waitStrategy, 8080); + } + + protected GenericContainer startContainerWithCommand(String shellCommand, WaitStrategy waitStrategy, Integer... ports) { // apply WaitStrategy to container return new GenericContainer(IMAGE_NAME) - .withExposedPorts(8080) + .withExposedPorts(ports) .withCommand("sh", "-c", shellCommand) .waitingFor(waitStrategy.withStartupTimeout(Duration.ofMillis(WAIT_TIMEOUT_MILLIS))); } diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java index 56c58fa0b2d..65a693f514f 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java @@ -102,6 +102,20 @@ public void testWaitUntilReadyWithTimeoutAndBadResponseBody() { waitUntilReadyAndTimeout(createShellCommand("200 OK", "Bad Response")); } + + /** + * Expects the WaitStrategy probing the right port. + */ + @Test + public void testWaitUntilReadyWithSpecificPort() { + waitUntilReadyAndSucceed(startContainerWithCommand( + createShellCommand("200 OK", GOOD_RESPONSE_BODY, 9090), + createHttpWaitStrategy(ready) + .forPort(9090), + 7070, 8080, 9090 + )); + } + /** * @param ready the AtomicBoolean on which to indicate success * @return the WaitStrategy under test @@ -129,10 +143,14 @@ protected void waitUntilReady() { } private String createShellCommand(String header, String responseBody) { + return createShellCommand(header, responseBody, 8080); + } + + private String createShellCommand(String header, String responseBody, int port) { int length = responseBody.getBytes().length; return "while true; do { echo -e \"HTTP/1.1 "+header+NEWLINE+ "Content-Type: text/html"+NEWLINE+ "Content-Length: "+length +NEWLINE+ "\";" - +" echo \""+responseBody+"\";} | nc -lp 8080; done"; + +" echo \""+responseBody+"\";} | nc -lp " + port + "; done"; } } From c9aac9a65b7014659b7163ef39780d49c88fac48 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 22 May 2018 10:22:53 +0200 Subject: [PATCH 0308/5615] fix Oracle image --- modules/jdbc-test/src/test/resources/testcontainers.properties | 2 +- modules/oracle-xe/src/test/resources/testcontainers.properties | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 modules/oracle-xe/src/test/resources/testcontainers.properties diff --git a/modules/jdbc-test/src/test/resources/testcontainers.properties b/modules/jdbc-test/src/test/resources/testcontainers.properties index 19b5c4680e8..6335223e8b2 100644 --- a/modules/jdbc-test/src/test/resources/testcontainers.properties +++ b/modules/jdbc-test/src/test/resources/testcontainers.properties @@ -1 +1 @@ -oracle.container.image=wnameless/oracle-xe-11g@sha256:15ff9ef50b4f90613c9780b589c57d98a8a9d496decec854316d96396ec5c085 +oracle.container.image=wnameless/oracle-xe-11g@sha256:825ba799432809fc7200bb1d7ef954973a8991d7702a860c87177fe05301f7da diff --git a/modules/oracle-xe/src/test/resources/testcontainers.properties b/modules/oracle-xe/src/test/resources/testcontainers.properties deleted file mode 100644 index 19b5c4680e8..00000000000 --- a/modules/oracle-xe/src/test/resources/testcontainers.properties +++ /dev/null @@ -1 +0,0 @@ -oracle.container.image=wnameless/oracle-xe-11g@sha256:15ff9ef50b4f90613c9780b589c57d98a8a9d496decec854316d96396ec5c085 From 5b3c15dd22d1696a43998927e70b7a57c779ae02 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 22 May 2018 10:26:22 +0200 Subject: [PATCH 0309/5615] add removed file --- modules/oracle-xe/src/test/resources/testcontainers.properties | 1 + 1 file changed, 1 insertion(+) create mode 100644 modules/oracle-xe/src/test/resources/testcontainers.properties diff --git a/modules/oracle-xe/src/test/resources/testcontainers.properties b/modules/oracle-xe/src/test/resources/testcontainers.properties new file mode 100644 index 00000000000..6335223e8b2 --- /dev/null +++ b/modules/oracle-xe/src/test/resources/testcontainers.properties @@ -0,0 +1 @@ +oracle.container.image=wnameless/oracle-xe-11g@sha256:825ba799432809fc7200bb1d7ef954973a8991d7702a860c87177fe05301f7da From 479e7e9a257ab490eb5a632f564d06580c34f48b Mon Sep 17 00:00:00 2001 From: Vedran Pavic Date: Wed, 30 May 2018 07:54:58 +0200 Subject: [PATCH 0310/5615] Add Spring Session to Testcontainers users (#714) --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index 9664ae037a3..b83aead5add 100644 --- a/docs/index.md +++ b/docs/index.md @@ -26,6 +26,7 @@ Testcontainers makes it easy to launch useful Docker containers for the duration * [Playtika](https://github.com/Playtika/testcontainers-spring-boot) - Kafka, Couchbase, MariaDB, Redis, Neo4j, Aerospike, MemSQL * [JetBrains](https://www.jetbrains.com/) - Testing of the TeamCity plugin for HashiCorp Vault * [Plumbr](https://plumbr.io) - Integration testing of data processing pipeline micro-services + * [Spring Session](https://projects.spring.io/spring-session/) - Redis, PostgreSQL, MySQL and MariaDB integration testing ## License From 331aaeb72f2db4ddf9751dd13c11ab3a615b0252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricard=20N=C3=A0cher=20Roig?= Date: Wed, 30 May 2018 15:47:41 +0200 Subject: [PATCH 0311/5615] ignore Eclipse IDE project files (#720) * ignore Eclipse IDE project files * ignore .classpath too --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index 8e548131d5d..c7170052b0a 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,9 @@ node_modules/ .gradle/ build/ + +# Eclipse IDE +.settings/ +.classpath +.project +bin/ From 2fdf6e78dfb01f324ddd3f4aea6720b69a8073e3 Mon Sep 17 00:00:00 2001 From: Ali Ahmed Date: Wed, 30 May 2018 14:03:54 -0700 Subject: [PATCH 0312/5615] Add Streamlio to Users (#724) --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index b83aead5add..eeff80283bc 100644 --- a/docs/index.md +++ b/docs/index.md @@ -26,6 +26,7 @@ Testcontainers makes it easy to launch useful Docker containers for the duration * [Playtika](https://github.com/Playtika/testcontainers-spring-boot) - Kafka, Couchbase, MariaDB, Redis, Neo4j, Aerospike, MemSQL * [JetBrains](https://www.jetbrains.com/) - Testing of the TeamCity plugin for HashiCorp Vault * [Plumbr](https://plumbr.io) - Integration testing of data processing pipeline micro-services + * [Streamlio](https://streaml.io/) - Integration and Chaos Testing of our fast data platform based on Apache Puslar, Apache Bookeeper and Apache Heron. * [Spring Session](https://projects.spring.io/spring-session/) - Redis, PostgreSQL, MySQL and MariaDB integration testing ## License From 0fa4118721b2409405acebbfe9e1818d295c9fac Mon Sep 17 00:00:00 2001 From: Ali Ahmed Date: Fri, 1 Jun 2018 02:51:41 -0700 Subject: [PATCH 0313/5615] Add Apache Pulsar Support (#713) * Add Apache Pulsar Support * Reafctor container class * Change property configuration * Remove unused imports --- .../utility/TestcontainersConfiguration.java | 4 ++ modules/pulsar/build.gradle | 8 +++ .../containers/PulsarContainer.java | 33 ++++++++++++ .../containers/PulsarContainerTest.java | 50 +++++++++++++++++++ .../src/test/resources/logback-test.xml | 18 +++++++ 5 files changed, 113 insertions(+) create mode 100644 modules/pulsar/build.gradle create mode 100644 modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java create mode 100644 modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java create mode 100644 modules/pulsar/src/test/resources/logback-test.xml diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 1b5f9ce9a59..1d570a93591 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -58,6 +58,10 @@ public String getKafkaImage() { return (String) properties.getOrDefault("kafka.container.image", "confluentinc/cp-kafka"); } + public String getPulsarImage() { + return (String) properties.getOrDefault("pulsar.container.image", "apachepulsar/pulsar"); + } + public boolean isDisableChecks() { return Boolean.parseBoolean((String) properties.getOrDefault("checks.disable", "false")); } diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle new file mode 100644 index 00000000000..3c5a37013cd --- /dev/null +++ b/modules/pulsar/build.gradle @@ -0,0 +1,8 @@ +description = "Testcontainers :: Pulsar" + +dependencies { + compile project(':testcontainers') + + testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.0.0-rc1-incubating' + testCompile group: 'org.assertj', name: 'assertj-core', version: '3.10.0' +} diff --git a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java new file mode 100644 index 00000000000..692c463cb65 --- /dev/null +++ b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java @@ -0,0 +1,33 @@ +package org.testcontainers.containers; + +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.utility.TestcontainersConfiguration; + +/** + * This container wraps Apache pulsar running in stanalone mode + */ +public class PulsarContainer extends GenericContainer { + + public static final int PULSAR_PORT = 6850; + + public PulsarContainer() { + this("2.0.0-rc1-incubating"); + } + + public PulsarContainer(String pulsarVersion) { + super(TestcontainersConfiguration.getInstance().getPulsarImage() + ":" + pulsarVersion); + withExposedPorts(PULSAR_PORT); + withCommand("/bin/bash", "-c", "" + + "servicePort=6850 webServicePort=8280 webServicePortTls=8643 bin/apply-config-from-env.py conf/proxy.conf && " + + "bin/pulsar standalone & " + + "bin/pulsar proxy --zookeeper-servers localhost:2181 --global-zookeeper-servers localhost:2181" + ); + + waitingFor(Wait.forLogMessage(".*messaging service is ready.*\\s", 1)); + } + + public String getPulsarBrokerUrl() { + return String.format("pulsar://%s:%s", this.getContainerIpAddress(), this.getFirstMappedPort()); + } + +} diff --git a/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java b/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java new file mode 100644 index 00000000000..cacf79729e0 --- /dev/null +++ b/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java @@ -0,0 +1,50 @@ +package org.testcontainers.containers; + +import org.apache.pulsar.client.api.Consumer; +import org.apache.pulsar.client.api.Message; +import org.apache.pulsar.client.api.Producer; +import org.apache.pulsar.client.api.PulsarClient; +import org.junit.Test; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; + +public class PulsarContainerTest { + + public static final String TEST_TOPIC = "test_topic"; + + @Test + public void testUsage() throws Exception { + try (PulsarContainer pulsar = new PulsarContainer()) { + pulsar.start(); + testPulsarFunctionality(pulsar.getPulsarBrokerUrl()); + } + } + + protected void testPulsarFunctionality(String pulsarBrokerUrl) throws Exception { + + try ( + PulsarClient client = PulsarClient.builder() + .serviceUrl(pulsarBrokerUrl) + .build(); + Consumer consumer = client.newConsumer() + .topic(TEST_TOPIC) + .subscriptionName("test-subs") + .subscribe(); + Producer producer = client.newProducer() + .topic(TEST_TOPIC) + .create() + ) { + + producer.send("test containers".getBytes()); + CompletableFuture future = consumer.receiveAsync(); + Message message = future.get(5, TimeUnit.SECONDS); + + assertThat(new String(message.getData())) + .isEqualTo("test containers"); + } + } + +} diff --git a/modules/pulsar/src/test/resources/logback-test.xml b/modules/pulsar/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..7bd6a94d827 --- /dev/null +++ b/modules/pulsar/src/test/resources/logback-test.xml @@ -0,0 +1,18 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + + + \ No newline at end of file From db02d798873c75c9ce914243fa4ccc1842809ae2 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 1 Jun 2018 11:52:58 +0200 Subject: [PATCH 0314/5615] add #713 to CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65ff3441cb3..407ce1391d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. ### Changed - Allow `HttpWaitStrategy` to wait for a specific port ([\#703](https://github.com/testcontainers/testcontainers-java/pull/703)) +- New module: Apache Pulsar ([\#713](https://github.com/testcontainers/testcontainers-java/pull/713)) ## [1.7.3] - 2018-05-16 From a2c0981d6d310a2f1d2c48e20006b25f04b02a86 Mon Sep 17 00:00:00 2001 From: Martin Gansler Date: Mon, 4 Jun 2018 11:20:45 +0200 Subject: [PATCH 0315/5615] Add support for container labels (#725) Adds additional builder methods for defining container labels. --- .../testcontainers/containers/Container.java | 16 +++++++++ .../containers/GenericContainer.java | 36 ++++++++++++++++--- .../junit/GenericContainerRuleTest.java | 29 ++++++++++++++- docs/usage/generic_containers.md | 2 +- docs/usage/options.md | 8 +++++ 5 files changed, 85 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/Container.java b/core/src/main/java/org/testcontainers/containers/Container.java index 2a4e260509c..da42a6f8036 100644 --- a/core/src/main/java/org/testcontainers/containers/Container.java +++ b/core/src/main/java/org/testcontainers/containers/Container.java @@ -204,6 +204,22 @@ default SELF withEnv(String key, Function, String> mapper) { */ SELF withEnv(Map env); + /** + * Add a label to the container. + * + * @param key label key + * @param value label value + * @return this + */ + SELF withLabel(String key, String value); + + /** + * Add labels to the container. + * @param labels map of labels + * @return this + */ + SELF withLabels(Map labels); + /** * Set the command that should be run in the container * diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 527e391718d..9ec38a2f2bf 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -110,6 +110,9 @@ public class GenericContainer> @NonNull private Map env = new HashMap<>(); + @NonNull + private Map labels = new HashMap<>(); + @NonNull private String[] commandParts = new String[0]; @@ -470,10 +473,14 @@ private void applyConfiguration(CreateContainerCmd createCommand) { createContainerCmdModifiers.forEach(hook -> hook.accept(createCommand)); - Map labels = createCommand.getLabels(); - labels = new HashMap<>(labels != null ? labels : Collections.emptyMap()); - labels.putAll(DockerClientFactory.DEFAULT_LABELS); - createCommand.withLabels(labels); + Map combinedLabels = new HashMap<>(); + combinedLabels.putAll(labels); + if (createCommand.getLabels() != null) { + combinedLabels.putAll(createCommand.getLabels()); + } + combinedLabels.putAll(DockerClientFactory.DEFAULT_LABELS); + + createCommand.withLabels(combinedLabels); } private Set findLinksFromThisContainer(String alias, LinkableContainer linkableContainer) { @@ -700,6 +707,27 @@ public SELF withEnv(Map env) { return self(); } + /** + * {@inheritDoc} + */ + @Override + public SELF withLabel(String key, String value) { + if (key.startsWith("org.testcontainers")) { + throw new IllegalArgumentException("The org.testcontainers namespace is reserved for interal use"); + } + labels.put(key, value); + return self(); + } + + /** + * {@inheritDoc} + */ + @Override + public SELF withLabels(Map labels) { + labels.forEach(this::withLabel); + return self(); + } + /** * {@inheritDoc} */ diff --git a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java index 677f97d67d9..7325cb809fb 100644 --- a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java +++ b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java @@ -22,6 +22,7 @@ import java.net.Socket; import java.time.Duration; import java.util.Arrays; +import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.regex.Matcher; @@ -220,6 +221,32 @@ public void environmentFromMapTest() throws IOException { assertEquals("Environment variables can be passed into a command from a map", "42 and 50", line); } + @Test + public void customLabelTest() { + try (final GenericContainer alpineCustomLabel = new GenericContainer("alpine:3.2") + .withLabel("our.custom", "label") + .withCommand("top")) { + + alpineCustomLabel.start(); + + Map labels = alpineCustomLabel.getCurrentContainerInfo().getConfig().getLabels(); + assertTrue("org.testcontainers label is present", labels.containsKey("org.testcontainers")); + assertTrue("our.custom label is present", labels.containsKey("our.custom")); + assertEquals("our.custom label value is label", labels.get("our.custom"), "label"); + } + } + + @Test + public void exceptionThrownWhenTryingToOverrideTestcontainersLabels() { + assertThrows("When trying to overwrite an 'org.testcontainers' label, withLabel() throws an exception", + IllegalArgumentException.class, + () -> { + new GenericContainer("alpine:3.2") + .withLabel("org.testcontainers.foo", "false"); + } + ); + } + @Test public void customClasspathResourceMappingTest() throws IOException { // Note: This functionality doesn't work if you are running your build inside a Docker container; @@ -341,7 +368,7 @@ public void copyToContainerTest() throws Exception { try (final GenericContainer alpineCopyToContainer = new GenericContainer("alpine:3.2") .withCommand("top")){ - + alpineCopyToContainer.start(); final MountableFile mountableFile = MountableFile.forClasspathResource("test_copy_to_container.txt"); alpineCopyToContainer.copyFileToContainer(mountableFile, "/home/"); diff --git a/docs/usage/generic_containers.md b/docs/usage/generic_containers.md index f5748284586..f848abb1414 100644 --- a/docs/usage/generic_containers.md +++ b/docs/usage/generic_containers.md @@ -46,4 +46,4 @@ The class rule provides methods for discovering how your tests can interact with For example, with the Redis example above, the following will allow your tests to access the Redis service: ```java String redisUrl = redis.getContainerIpAddress() + ":" + redis.getMappedPort(6379); -``` \ No newline at end of file +``` diff --git a/docs/usage/options.md b/docs/usage/options.md index dbd45804c7c..deaf429816d 100644 --- a/docs/usage/options.md +++ b/docs/usage/options.md @@ -29,6 +29,14 @@ new GenericContainer(...) .withEnv("API_TOKEN", "foo") ``` +### Labels + +To add a custom label to the container, use `withLabel`: +```java +new GenericContainer(...) + .withLabel("your.custom", "label") +``` + ### Command By default the container will execute whatever command is specified in the image's Dockerfile. To override this, and specify a different command, use `withCommand`: From b3f08bd725f02843a047eba3498bdf421fe80dc8 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Mon, 4 Jun 2018 11:22:52 +0200 Subject: [PATCH 0316/5615] Add #725 to CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 407ce1391d6..0cf11ea6756 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. ### Changed - Allow `HttpWaitStrategy` to wait for a specific port ([\#703](https://github.com/testcontainers/testcontainers-java/pull/703)) - New module: Apache Pulsar ([\#713](https://github.com/testcontainers/testcontainers-java/pull/713)) +- Add support for defining container labels ([\#725](https://github.com/testcontainers/testcontainers-java/pull/725)) ## [1.7.3] - 2018-05-16 From 6ab7c79047c0b23040f8f8329f5ea1ec56c6345a Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 5 Jun 2018 11:26:29 +0200 Subject: [PATCH 0317/5615] Use `quay.io/testcontainers/ryuk` instead of `bsideup/ryuk` (#721) --- .../org/testcontainers/utility/TestcontainersConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 1d570a93591..39c756a31b8 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -47,7 +47,7 @@ public String getTinyImage() { } public String getRyukImage() { - return (String) properties.getOrDefault("ryuk.container.image", "bsideup/moby-ryuk:0.2.2"); + return (String) properties.getOrDefault("ryuk.container.image", "quay.io/testcontainers/ryuk:0.2.2"); } public Integer getRyukTimeout() { From 12a93096c5b58f3fb3f4fcfc1c9d976221059889 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 5 Jun 2018 11:29:02 +0200 Subject: [PATCH 0318/5615] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cf11ea6756..1268b3fa8f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file. - Allow `HttpWaitStrategy` to wait for a specific port ([\#703](https://github.com/testcontainers/testcontainers-java/pull/703)) - New module: Apache Pulsar ([\#713](https://github.com/testcontainers/testcontainers-java/pull/713)) - Add support for defining container labels ([\#725](https://github.com/testcontainers/testcontainers-java/pull/725)) +- Use `quay.io/testcontainers/ryuk` instead of `bsideup/ryuk` ([\#721](https://github.com/testcontainers/testcontainers-java/pull/721)) ## [1.7.3] - 2018-05-16 From b4f3bbf36144703ebad8593e2ffb761f7d9c4877 Mon Sep 17 00:00:00 2001 From: Luca Burgazzoli Date: Tue, 5 Jun 2018 17:03:49 +0200 Subject: [PATCH 0319/5615] Add reference to Apache Camel (#735) --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index eeff80283bc..87894043127 100644 --- a/docs/index.md +++ b/docs/index.md @@ -28,6 +28,7 @@ Testcontainers makes it easy to launch useful Docker containers for the duration * [Plumbr](https://plumbr.io) - Integration testing of data processing pipeline micro-services * [Streamlio](https://streaml.io/) - Integration and Chaos Testing of our fast data platform based on Apache Puslar, Apache Bookeeper and Apache Heron. * [Spring Session](https://projects.spring.io/spring-session/) - Redis, PostgreSQL, MySQL and MariaDB integration testing + * [Apache Camel](https://camel.apache.org) - Testing Camel agains native service such as Consul, Etcd and so on. ## License From 38e05e6a3dd459b9fe80a058c32525d4a44eebc5 Mon Sep 17 00:00:00 2001 From: Arne Zelasko Date: Fri, 8 Jun 2018 14:11:37 +0200 Subject: [PATCH 0320/5615] Update webdriver_containers.md (#701) Update docs to reflect the renaming of API method to getTestHostIpAddress() --- docs/usage/webdriver_containers.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/usage/webdriver_containers.md b/docs/usage/webdriver_containers.md index 246b1943373..7a8023f685e 100644 --- a/docs/usage/webdriver_containers.md +++ b/docs/usage/webdriver_containers.md @@ -39,9 +39,9 @@ You can then use this driver instance like a regular WebDriver. Note that, if you want to test a **web application running on the host machine** (the machine the JUnit tests are running on - which is quite likely), you'll need to replace any references to `localhost` with an IP address that the -Docker container can reach. Use the `getHostIpAddress()` method, e.g.: +Docker container can reach. Use the `getTestHostIpAddress()` method, e.g.: ```java -driver.get("http://" + chrome.getHostIpAddress() + ":8080/"); +driver.get("http://" + chrome.getTestHostIpAddress() + ":8080/"); ``` ## Options @@ -91,4 +91,4 @@ Note the factory must implement `org.testcontainers.containers.RecordingFileFact ## More examples -A few different examples are shown in [ChromeWebDriverContainerTest.java](https://github.com/testcontainers/testcontainers-java/blob/master/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java). \ No newline at end of file +A few different examples are shown in [ChromeWebDriverContainerTest.java](https://github.com/testcontainers/testcontainers-java/blob/master/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java). From fc2f169fac91c413f7af5658d93445902470a9c6 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 8 Jun 2018 14:17:54 +0200 Subject: [PATCH 0321/5615] fix flaky WaitAllStrategyTest#appliesOuterTimeout test (#731) --- .../testcontainers/containers/wait/WaitAllStrategyTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/test/java/org/testcontainers/containers/wait/WaitAllStrategyTest.java b/core/src/test/java/org/testcontainers/containers/wait/WaitAllStrategyTest.java index e3adba9e427..f47a7212a5c 100644 --- a/core/src/test/java/org/testcontainers/containers/wait/WaitAllStrategyTest.java +++ b/core/src/test/java/org/testcontainers/containers/wait/WaitAllStrategyTest.java @@ -58,7 +58,7 @@ public void appliesOuterTimeout() { .withStartupTimeout(Duration.ofMillis(10)); doAnswer(invocation -> { - Uninterruptibles.sleepUninterruptibly(20, TimeUnit.MILLISECONDS); + Uninterruptibles.sleepUninterruptibly(10, TimeUnit.SECONDS); return null; }).when(strategy1).waitUntilReady(eq(container)); @@ -66,4 +66,4 @@ public void appliesOuterTimeout() { underTest.waitUntilReady(container); }); } -} \ No newline at end of file +} From 95d4e04b8982c4ef9baeb18fe13143fdb7ea0472 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Fri, 8 Jun 2018 14:50:44 +0200 Subject: [PATCH 0322/5615] Fix small typo in "Using" section of docs (#740) --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 87894043127..07b07ecf33a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -28,7 +28,7 @@ Testcontainers makes it easy to launch useful Docker containers for the duration * [Plumbr](https://plumbr.io) - Integration testing of data processing pipeline micro-services * [Streamlio](https://streaml.io/) - Integration and Chaos Testing of our fast data platform based on Apache Puslar, Apache Bookeeper and Apache Heron. * [Spring Session](https://projects.spring.io/spring-session/) - Redis, PostgreSQL, MySQL and MariaDB integration testing - * [Apache Camel](https://camel.apache.org) - Testing Camel agains native service such as Consul, Etcd and so on. + * [Apache Camel](https://camel.apache.org) - Testing Camel against native services such as Consul, Etcd and so on ## License From 3b974e98629e3bb2220263129acf14b5bda1ab55 Mon Sep 17 00:00:00 2001 From: Tayeb Chlyah Date: Fri, 8 Jun 2018 14:19:26 +0000 Subject: [PATCH 0323/5615] Couchbase module (#688) * First implementation * Activate primary index by default * Add compatibility with Couchbase v5 * Fix backward compatibility with older version * Use more lombok annotations to reduce boilerplate code * Move src to modules/couchbase * Add build with gradle * Replace shaded libraries with real ones * Fix waiting for Couchbase start * Add AUTHORS and README * Fix Couchbase version * Add Cleanup lombok annotation to auto close resources * Update README * Use new AbstractWaitStrategy implementation * Respect Codacy quality standards * Some optimizations * Update CHANGELOG * Replace CouchbaseWaitStrategy by HttpWaitStrategy and ResponsePredicate --- CHANGELOG.md | 1 + modules/couchbase/AUTHORS | 1 + modules/couchbase/README.md | 59 ++++ modules/couchbase/build.gradle | 6 + .../couchbase/AbstractCouchbaseTest.java | 59 ++++ .../couchbase/CouchbaseContainer.java | 276 ++++++++++++++++++ .../CouchbaseQueryServiceWaitStrategy.java | 55 ++++ .../couchbase/CouchbaseContainerTest.java | 41 +++ .../src/test/resources/logback-test.xml | 29 ++ 9 files changed, 527 insertions(+) create mode 100644 modules/couchbase/AUTHORS create mode 100644 modules/couchbase/README.md create mode 100644 modules/couchbase/build.gradle create mode 100644 modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java create mode 100644 modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java create mode 100644 modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseQueryServiceWaitStrategy.java create mode 100644 modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java create mode 100644 modules/couchbase/src/test/resources/logback-test.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index 1268b3fa8f0..d783aca09a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file. - New module: Apache Pulsar ([\#713](https://github.com/testcontainers/testcontainers-java/pull/713)) - Add support for defining container labels ([\#725](https://github.com/testcontainers/testcontainers-java/pull/725)) - Use `quay.io/testcontainers/ryuk` instead of `bsideup/ryuk` ([\#721](https://github.com/testcontainers/testcontainers-java/pull/721)) +- Added Couchbase module ([\#688](https://github.com/testcontainers/testcontainers-java/pull/688)) ## [1.7.3] - 2018-05-16 diff --git a/modules/couchbase/AUTHORS b/modules/couchbase/AUTHORS new file mode 100644 index 00000000000..db292de72f1 --- /dev/null +++ b/modules/couchbase/AUTHORS @@ -0,0 +1 @@ +Tayeb Chlyah diff --git a/modules/couchbase/README.md b/modules/couchbase/README.md new file mode 100644 index 00000000000..7a18a235eb4 --- /dev/null +++ b/modules/couchbase/README.md @@ -0,0 +1,59 @@ + + +# TestContainers Couchbase Module +Testcontainers module for Couchbase. [Couchbase](https://www.couchbase.com/) is a Document oriented NoSQL database. + +## Usage example + +Running Couchbase as a stand-in in a test: + +### Create you own bucket + +```java +public class SomeTest { + + @Rule + public CouchbaseContainer couchbase = new CouchbaseContainer() + .withNewBucket(DefaultBucketSettings.builder() + .enableFlush(true) + .name('bucket-name') + .quota(100) + .type(BucketType.COUCHBASE) + .build()); + + @Test + public void someTestMethod() { + Bucket bucket = couchbase.getCouchbaseCluster().openBucket('bucket-name') + + ... interact with client as if using Couchbase normally +``` + +### Use preconfigured default bucket + +Bucket is cleared after each test + +```java +public class SomeTest extends AbstractCouchbaseTest { + + @Test + public void someTestMethod() { + Bucket bucket = getBucket(); + + ... interact with client as if using Couchbase normally +``` + +### Special consideration + +Couchbase container is configured to use random available [ports](https://developer.couchbase.com/documentation/server/current/install/install-ports.html) for some ports only, as [Couchbase Java SDK](https://developer.couchbase.com/documentation/server/current/sdk/java/start-using-sdk.html) permit to configure only some ports : +- **8091** : REST/HTTP traffic ([bootstrapHttpDirectPort](http://docs.couchbase.com/sdk-api/couchbase-java-client-2.4.6/com/couchbase/client/java/env/DefaultCouchbaseEnvironment.Builder.html#bootstrapCarrierDirectPort-int-)) +- **18091** : REST/HTTP traffic with SSL ([bootstrapHttpSslPort](http://docs.couchbase.com/sdk-api/couchbase-java-client-2.4.6/com/couchbase/client/java/env/DefaultCouchbaseEnvironment.Builder.html#bootstrapCarrierSslPort-int-)) +- **11210** : memcached ([bootstrapCarrierDirectPort](http://docs.couchbase.com/sdk-api/couchbase-java-client-2.4.6/com/couchbase/client/java/env/DefaultCouchbaseEnvironment.Builder.html#bootstrapCarrierDirectPort-int-)) +- **11207** : memcached SSL ([bootstrapCarrierSslPort](http://docs.couchbase.com/sdk-api/couchbase-java-client-2.4.6/com/couchbase/client/java/env/DefaultCouchbaseEnvironment.Builder.html#bootstrapCarrierSslPort-int-)) + +All other ports cannot be changed by Java SDK, there are sadly fixed : +- **8092** : Queries, views, XDCR +- **8093** : REST/HTTP Query service +- **8094** : REST/HTTP Search Service +- **8095** : REST/HTTP Analytic service + +So if you disable Query, Search and Analytic service, you can run multiple instance of this container, otherwise, you're stuck with one instance, for now. diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle new file mode 100644 index 00000000000..218adce2d5a --- /dev/null +++ b/modules/couchbase/build.gradle @@ -0,0 +1,6 @@ +description = "Testcontainers :: Couchbase" + +dependencies { + compile project(':testcontainers') + compile 'com.couchbase.client:java-client:2.5.7' +} diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java new file mode 100644 index 00000000000..b8cdaa14cb2 --- /dev/null +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java @@ -0,0 +1,59 @@ +package org.testcontainers.couchbase; + +import com.couchbase.client.java.Bucket; +import com.couchbase.client.java.CouchbaseCluster; +import com.couchbase.client.java.bucket.BucketType; +import com.couchbase.client.java.cluster.DefaultBucketSettings; +import com.couchbase.client.java.query.N1qlParams; +import com.couchbase.client.java.query.N1qlQuery; +import com.couchbase.client.java.query.consistency.ScanConsistency; +import lombok.Getter; +import org.junit.After; + +/** + * @author ctayeb + */ +public abstract class AbstractCouchbaseTest { + + public static final String TEST_BUCKET = "test"; + + public static final String DEFAULT_PASSWORD = "password"; + + @Getter(lazy = true) + private final static CouchbaseContainer couchbaseContainer = initCouchbaseContainer(); + + @Getter(lazy = true) + private final static Bucket bucket = openBucket(TEST_BUCKET, DEFAULT_PASSWORD); + + @After + public void clear() { + if (getCouchbaseContainer().isIndex() && getCouchbaseContainer().isQuery() && getCouchbaseContainer().isPrimaryIndex()) { + getBucket().query( + N1qlQuery.simple(String.format("DELETE FROM `%s`", getBucket().name()), + N1qlParams.build().consistency(ScanConsistency.STATEMENT_PLUS))); + } else { + getBucket().bucketManager().flush(); + } + } + + private static CouchbaseContainer initCouchbaseContainer() { + CouchbaseContainer couchbaseContainer = new CouchbaseContainer() + .withNewBucket(DefaultBucketSettings.builder() + .enableFlush(true) + .name(TEST_BUCKET) + .password(DEFAULT_PASSWORD) + .quota(100) + .replicas(0) + .type(BucketType.COUCHBASE) + .build()); + couchbaseContainer.start(); + return couchbaseContainer; + } + + private static Bucket openBucket(String bucketName, String password) { + CouchbaseCluster cluster = getCouchbaseContainer().getCouchbaseCluster(); + Bucket bucket = cluster.openBucket(bucketName, password); + Runtime.getRuntime().addShutdownHook(new Thread(bucket::close)); + return bucket; + } +} diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java new file mode 100644 index 00000000000..356bbc91fea --- /dev/null +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2016 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.testcontainers.couchbase; + +import com.couchbase.client.core.utils.Base64; +import com.couchbase.client.java.Bucket; +import com.couchbase.client.java.CouchbaseCluster; +import com.couchbase.client.java.cluster.*; +import com.couchbase.client.java.env.CouchbaseEnvironment; +import com.couchbase.client.java.env.DefaultCouchbaseEnvironment; +import com.couchbase.client.java.query.Index; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dockerjava.api.command.InspectContainerResponse; +import lombok.AllArgsConstructor; +import lombok.Cleanup; +import lombok.Getter; +import lombok.SneakyThrows; +import lombok.experimental.Wither; +import org.apache.commons.compress.utils.Sets; +import org.jetbrains.annotations.NotNull; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.util.*; + +import static java.net.HttpURLConnection.HTTP_OK; + +/** + * Based on Laurent Doguin version, + *

+ * optimized by Tayeb Chlyah + */ +@AllArgsConstructor +public class CouchbaseContainer extends GenericContainer { + + public static final String VERSION = "5.1.0"; + public static final ObjectMapper MAPPER = new ObjectMapper(); + + @Wither + private String memoryQuota = "300"; + + @Wither + private String indexMemoryQuota = "300"; + + @Wither + private String clusterUsername = "Administrator"; + + @Wither + private String clusterPassword = "password"; + + @Wither + private boolean keyValue = true; + + @Getter + @Wither + private boolean query = true; + + @Getter + @Wither + private boolean index = true; + + @Getter + @Wither + private boolean primaryIndex = true; + + @Getter + @Wither + private boolean fts = false; + + @Wither + private boolean beerSample = false; + + @Wither + private boolean travelSample = false; + + @Wither + private boolean gamesIMSample = false; + + @Getter(lazy = true) + private final CouchbaseEnvironment couchbaseEnvironment = createCouchbaseEnvironment(); + + @Getter(lazy = true) + private final CouchbaseCluster couchbaseCluster = createCouchbaseCluster(); + + private List newBuckets = new ArrayList<>(); + + private String urlBase; + + public CouchbaseContainer() { + super("couchbase/server:" + VERSION); + } + + public CouchbaseContainer(String containerName) { + super(containerName); + } + + @Override + protected Integer getLivenessCheckPort() { + return getMappedPort(8091); + } + + @Override + public Set getLivenessCheckPortNumbers() { + return Sets.newHashSet(getLivenessCheckPort()); + } + + @Override + protected void configure() { + // Configurable ports + addExposedPorts(11210, 11207, 8091, 18091); + + // Non configurable ports + addFixedExposedPort(8092, 8092); + addFixedExposedPort(8093, 8093); + addFixedExposedPort(8094, 8094); + addFixedExposedPort(8095, 8095); + addFixedExposedPort(18092, 18092); + addFixedExposedPort(18093, 18093); + setWaitStrategy(new HttpWaitStrategy().forPath("/ui/index.html#/")); + } + + public CouchbaseContainer withNewBucket(BucketSettings bucketSettings) { + newBuckets.add(bucketSettings); + return self(); + } + + @SneakyThrows + public void initCluster() { + urlBase = String.format("http://%s:%s", getContainerIpAddress(), getMappedPort(8091)); + String poolURL = "/pools/default"; + String poolPayload = "memoryQuota=" + URLEncoder.encode(memoryQuota, "UTF-8") + "&indexMemoryQuota=" + URLEncoder.encode(indexMemoryQuota, "UTF-8"); + + String setupServicesURL = "/node/controller/setupServices"; + StringBuilder servicePayloadBuilder = new StringBuilder(); + if (keyValue) { + servicePayloadBuilder.append("kv,"); + } + if (query) { + servicePayloadBuilder.append("n1ql,"); + } + if (index) { + servicePayloadBuilder.append("index,"); + } + if (fts) { + servicePayloadBuilder.append("fts,"); + } + String setupServiceContent = "services=" + URLEncoder.encode(servicePayloadBuilder.toString(), "UTF-8"); + + String webSettingsURL = "/settings/web"; + String webSettingsContent = "username=" + URLEncoder.encode(clusterUsername, "UTF-8") + "&password=" + URLEncoder.encode(clusterPassword, "UTF-8") + "&port=8091"; + + String bucketURL = "/sampleBuckets/install"; + + StringBuilder sampleBucketPayloadBuilder = new StringBuilder(); + sampleBucketPayloadBuilder.append('['); + if (travelSample) { + sampleBucketPayloadBuilder.append("\"travel-sample\","); + } + if (beerSample) { + sampleBucketPayloadBuilder.append("\"beer-sample\","); + } + if (gamesIMSample) { + sampleBucketPayloadBuilder.append("\"gamesim-sample\","); + } + sampleBucketPayloadBuilder.append(']'); + + callCouchbaseRestAPI(poolURL, poolPayload); + callCouchbaseRestAPI(setupServicesURL, setupServiceContent); + callCouchbaseRestAPI(webSettingsURL, webSettingsContent); + callCouchbaseRestAPI(bucketURL, sampleBucketPayloadBuilder.toString()); + + createNodeWaitStrategy().waitUntilReady(this); + callCouchbaseRestAPI("/settings/indexes", "indexerThreads=0&logLevel=info&maxRollbackPoints=5&storageMode=memory_optimized"); + } + + @NotNull + private HttpWaitStrategy createNodeWaitStrategy() { + return new HttpWaitStrategy() + .forPath("/pools/default/") + .withBasicCredentials(clusterUsername, clusterPassword) + .forStatusCode(HTTP_OK) + .forResponsePredicate(response -> { + try { + return Optional.of(MAPPER.readTree(response)) + .map(n -> n.at("/nodes/0/status")) + .map(JsonNode::asText) + .map("healthy"::equals) + .orElse(false); + } catch (IOException e) { + logger().error("Unable to parse response {}", response); + return false; + } + }); + } + + public void createBucket(BucketSettings bucketSetting, boolean primaryIndex) { + ClusterManager clusterManager = getCouchbaseCluster().clusterManager(clusterUsername, clusterPassword); + // Insert Bucket + BucketSettings bucketSettings = clusterManager.insertBucket(bucketSetting); + // Insert Bucket admin user + UserSettings userSettings = UserSettings.build() + .password(bucketSetting.password()) + .roles(Collections.singletonList(new UserRole("bucket_admin", bucketSetting.name()))); + try { + clusterManager.upsertUser(AuthDomain.LOCAL, bucketSetting.name(), userSettings); + } catch (Exception e) { + logger().warn("Unable to insert user '" + bucketSetting.name() + "', maybe you are using older version"); + } + if (index) { + Bucket bucket = getCouchbaseCluster().openBucket(bucketSettings.name(), bucketSettings.password()); + new CouchbaseQueryServiceWaitStrategy(bucket).waitUntilReady(this); + if (primaryIndex) { + bucket.query(Index.createPrimaryIndex().on(bucketSetting.name())); + } + } + } + + public void callCouchbaseRestAPI(String url, String payload) throws IOException { + String fullUrl = urlBase + url; + @Cleanup("disconnect") + HttpURLConnection httpConnection = (HttpURLConnection) ((new URL(fullUrl).openConnection())); + httpConnection.setDoOutput(true); + httpConnection.setRequestMethod("POST"); + httpConnection.setRequestProperty("Content-Type", + "application/x-www-form-urlencoded"); + String encoded = Base64.encode((clusterUsername + ":" + clusterPassword).getBytes("UTF-8")); + httpConnection.setRequestProperty("Authorization", "Basic " + encoded); + @Cleanup + DataOutputStream out = new DataOutputStream(httpConnection.getOutputStream()); + out.writeBytes(payload); + out.flush(); + httpConnection.getResponseCode(); + } + + @Override + protected void containerIsStarted(InspectContainerResponse containerInfo) { + if (!newBuckets.isEmpty()) { + for (BucketSettings bucketSetting : newBuckets) { + createBucket(bucketSetting, primaryIndex); + } + } + } + + private CouchbaseCluster createCouchbaseCluster() { + return CouchbaseCluster.create(getCouchbaseEnvironment(), getContainerIpAddress()); + } + + private DefaultCouchbaseEnvironment createCouchbaseEnvironment() { + initCluster(); + return DefaultCouchbaseEnvironment.builder() + .bootstrapCarrierDirectPort(getMappedPort(11210)) + .bootstrapCarrierSslPort(getMappedPort(11207)) + .bootstrapHttpDirectPort(getMappedPort(8091)) + .bootstrapHttpSslPort(getMappedPort(18091)) + .build(); + } +} diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseQueryServiceWaitStrategy.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseQueryServiceWaitStrategy.java new file mode 100644 index 00000000000..853dd7d8eb7 --- /dev/null +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseQueryServiceWaitStrategy.java @@ -0,0 +1,55 @@ +package org.testcontainers.couchbase; + +import com.couchbase.client.core.message.cluster.GetClusterConfigRequest; +import com.couchbase.client.core.message.cluster.GetClusterConfigResponse; +import com.couchbase.client.core.service.ServiceType; +import com.couchbase.client.java.Bucket; +import lombok.extern.slf4j.Slf4j; +import org.rnorth.ducttape.TimeoutException; +import org.testcontainers.containers.ContainerLaunchException; +import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +import static org.rnorth.ducttape.unreliables.Unreliables.retryUntilSuccess; + +/** + * @author ctayeb + * Created on 06/06/2017 + */ +@Slf4j +public class CouchbaseQueryServiceWaitStrategy extends AbstractWaitStrategy { + + private final Bucket bucket; + + public CouchbaseQueryServiceWaitStrategy(Bucket bucket) { + this.bucket = bucket; + startupTimeout = Duration.ofSeconds(120); + } + + @Override + protected void waitUntilReady() { + log.info("Waiting for {} seconds for QUERY service", startupTimeout.getSeconds()); + + // try to connect to the URL + try { + retryUntilSuccess((int) startupTimeout.getSeconds(), TimeUnit.SECONDS, () -> { + getRateLimiter().doWhenReady(() -> { + GetClusterConfigResponse clusterConfig = bucket.core() + .send(new GetClusterConfigRequest()) + .toBlocking().single(); + boolean queryServiceEnabled = clusterConfig.config() + .bucketConfig(bucket.name()) + .serviceEnabled(ServiceType.QUERY); + if (!queryServiceEnabled) { + throw new ContainerLaunchException("Query service not ready yet"); + } + }); + return true; + }); + } catch (TimeoutException e) { + throw new ContainerLaunchException("Timed out waiting for QUERY service"); + } + } +} diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java new file mode 100644 index 00000000000..52660787796 --- /dev/null +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java @@ -0,0 +1,41 @@ +package org.testcontainers.couchbase; + +import com.couchbase.client.java.document.RawJsonDocument; +import com.couchbase.client.java.query.N1qlQuery; +import com.couchbase.client.java.query.N1qlQueryResult; +import com.couchbase.client.java.query.N1qlQueryRow; +import org.junit.Assert; +import org.junit.Test; + +import java.util.List; + +/** + * @author ctayeb + * created on 18/06/2017 + */ +public class CouchbaseContainerTest extends AbstractCouchbaseTest { + + private static final String ID = "toto"; + + private static final String DOCUMENT = "{\"name\":\"toto\"}"; + + @Test + public void shouldInsertDocument() { + RawJsonDocument expected = RawJsonDocument.create(ID, DOCUMENT); + getBucket().upsert(expected); + RawJsonDocument result = getBucket().get(ID, RawJsonDocument.class); + Assert.assertEquals(expected.content(), result.content()); + } + + @Test + public void shouldExecuteN1ql() { + getBucket().query(N1qlQuery.simple("INSERT INTO " + TEST_BUCKET + " (KEY, VALUE) VALUES ('" + ID + "', " + DOCUMENT + ")")); + + N1qlQueryResult query = getBucket().query(N1qlQuery.simple("SELECT * FROM " + TEST_BUCKET + " USE KEYS '" + ID + "'")); + Assert.assertTrue(query.parseSuccess()); + Assert.assertTrue(query.finalSuccess()); + List n1qlQueryRows = query.allRows(); + Assert.assertEquals(1, n1qlQueryRows.size()); + Assert.assertEquals(DOCUMENT, n1qlQueryRows.get(0).value().get(TEST_BUCKET).toString()); + } +} diff --git a/modules/couchbase/src/test/resources/logback-test.xml b/modules/couchbase/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..fe273feb881 --- /dev/null +++ b/modules/couchbase/src/test/resources/logback-test.xml @@ -0,0 +1,29 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + + + + + + + + + + + PROFILER + DENY + + \ No newline at end of file From acf904dc44fad601c46257a6aa7b63a144350906 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 11 Jun 2018 10:29:57 +0200 Subject: [PATCH 0324/5615] use standard port 9092 in KafkaContainer (#733) * use standard port 9092 in KafkaContainer * use constants where possible --- .../testcontainers/containers/KafkaContainer.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java index b9d7afeeaa5..9a222c7e974 100644 --- a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java +++ b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java @@ -11,7 +11,7 @@ */ public class KafkaContainer extends GenericContainer { - public static final int KAFKA_PORT = 9092; + public static final int KAFKA_PORT = 9093; public static final int ZOOKEEPER_PORT = 2181; @@ -27,13 +27,12 @@ public KafkaContainer(String confluentPlatformVersion) { super(TestcontainersConfiguration.getInstance().getKafkaImage() + ":" + confluentPlatformVersion); withNetwork(Network.newNetwork()); - String networkAlias = "kafka-" + Base58.randomString(6); - withNetworkAliases(networkAlias); + withNetworkAliases("kafka-" + Base58.randomString(6)); withExposedPorts(KAFKA_PORT); // Use two listeners with different names, it will force Kafka to communicate with itself via internal // listener when KAFKA_INTER_BROKER_LISTENER_NAME is set, otherwise Kafka will try to use the advertised listener - withEnv("KAFKA_LISTENERS", "PLAINTEXT://0.0.0.0:9092,BROKER://" + networkAlias + ":9093"); + withEnv("KAFKA_LISTENERS", "PLAINTEXT://0.0.0.0:" + KAFKA_PORT + ",BROKER://0.0.0.0:9092"); withEnv("KAFKA_LISTENER_SECURITY_PROTOCOL_MAP", "BROKER:PLAINTEXT,PLAINTEXT:PLAINTEXT"); withEnv("KAFKA_INTER_BROKER_LISTENER_NAME", "BROKER"); @@ -62,11 +61,11 @@ public void start() { String networkAlias = getNetworkAliases().get(0); proxy = new SocatContainer() .withNetwork(getNetwork()) - .withTarget(9092, networkAlias) - .withTarget(2181, networkAlias); + .withTarget(KAFKA_PORT, networkAlias) + .withTarget(ZOOKEEPER_PORT, networkAlias); proxy.start(); - withEnv("KAFKA_ADVERTISED_LISTENERS", "BROKER://" + networkAlias + ":9093,PLAINTEXT://" + proxy.getContainerIpAddress() + ":" + proxy.getFirstMappedPort()); + withEnv("KAFKA_ADVERTISED_LISTENERS", "BROKER://" + networkAlias + ":" + KAFKA_PORT +",PLAINTEXT://" + proxy.getContainerIpAddress() + ":" + proxy.getFirstMappedPort()); if (externalZookeeperConnect != null) { withEnv("KAFKA_ZOOKEEPER_CONNECT", externalZookeeperConnect); From c7d8a65dd809139fa105c28bc5cff93cf25cf204 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 11 Jun 2018 10:31:25 +0200 Subject: [PATCH 0325/5615] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d783aca09a3..f11cf332407 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file. - Add support for defining container labels ([\#725](https://github.com/testcontainers/testcontainers-java/pull/725)) - Use `quay.io/testcontainers/ryuk` instead of `bsideup/ryuk` ([\#721](https://github.com/testcontainers/testcontainers-java/pull/721)) - Added Couchbase module ([\#688](https://github.com/testcontainers/testcontainers-java/pull/688)) +- Changed **internal** port of KafkaContainer back to 9092 ([\#733](https://github.com/testcontainers/testcontainers-java/pull/733)) ## [1.7.3] - 2018-05-16 From 2affee9b7572eee33a4208e4c9cbdcbf797928a5 Mon Sep 17 00:00:00 2001 From: Rinus Wiskerke Date: Tue, 12 Jun 2018 14:50:46 +0200 Subject: [PATCH 0326/5615] Add additional constructor to OracleContainer (#734) --- CHANGELOG.md | 1 + .../java/org/testcontainers/containers/OracleContainer.java | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f11cf332407..6503cffbc68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ All notable changes to this project will be documented in this file. - Use `quay.io/testcontainers/ryuk` instead of `bsideup/ryuk` ([\#721](https://github.com/testcontainers/testcontainers-java/pull/721)) - Added Couchbase module ([\#688](https://github.com/testcontainers/testcontainers-java/pull/688)) - Changed **internal** port of KafkaContainer back to 9092 ([\#733](https://github.com/testcontainers/testcontainers-java/pull/733)) +- Add support for Dockerfile based images to OracleContainer ([\#734](https://github.com/testcontainers/testcontainers-java/pull/734)) ## [1.7.3] - 2018-05-16 diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java index 3cb6d888ebd..375efa27176 100644 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java @@ -2,6 +2,8 @@ import org.testcontainers.utility.TestcontainersConfiguration; +import java.util.concurrent.Future; + /** * @author gusohal */ @@ -32,6 +34,10 @@ public OracleContainer(String dockerImageName) { super(dockerImageName); } + public OracleContainer(Future dockerImageName) { + super(dockerImageName); + } + @Override protected Integer getLivenessCheckPort() { return getMappedPort(ORACLE_PORT); From 5cdd8dc55565d3a47aaf01f8348b73f836409c95 Mon Sep 17 00:00:00 2001 From: Maxim Kolesnikov Date: Tue, 12 Jun 2018 22:24:49 +0200 Subject: [PATCH 0327/5615] Fixed typo in exception message (#746) --- .../transport/TestcontainersDockerCmdExecFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java b/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java index 9334b9bca41..976bb23c91d 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java @@ -123,7 +123,7 @@ public EventLoopGroup init(Bootstrap bootstrap, DockerClientConfig dockerClientC } else if (SystemUtils.IS_OS_MAC_OSX) { return kqueueGroup(); } - throw new RuntimeException("Unspported OS"); + throw new RuntimeException("Unsupported OS"); } public EventLoopGroup epollGroup() { From 360702aafe71abd60a9693a047b3cbe9eeb39715 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Wed, 13 Jun 2018 09:49:43 +0200 Subject: [PATCH 0328/5615] Use latest tag as default if no tag is specified (#744) Fixes #676 --- .../main/java/org/testcontainers/utility/DockerImageName.java | 2 +- .../java/org/testcontainers/dockerclient/ImagePullTest.java | 1 + .../java/org/testcontainers/utility/DockerImageNameTest.java | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/DockerImageName.java b/core/src/main/java/org/testcontainers/utility/DockerImageName.java index 212b81a20eb..50b2d57b743 100644 --- a/core/src/main/java/org/testcontainers/utility/DockerImageName.java +++ b/core/src/main/java/org/testcontainers/utility/DockerImageName.java @@ -45,7 +45,7 @@ public DockerImageName(String name) { versioning = new TagVersioning(remoteName.split(":")[1]); } else { repo = remoteName; - versioning = null; + versioning = new TagVersioning("latest"); } } diff --git a/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java b/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java index f06ff40fc40..bfd03d2c9be 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java @@ -16,6 +16,7 @@ public static String[] parameters() { return new String[] { "alpine:latest", "alpine:3.6", + "alpine", // omitting the tag should work and default to latest "alpine@sha256:8fd4b76819e1e5baac82bd0a3d03abfe3906e034cc5ee32100d12aaaf3956dc7", "gliderlabs/alpine:latest", "gliderlabs/alpine:3.5", diff --git a/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java b/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java index 3fedbc19df3..caef1bcf9ec 100644 --- a/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java +++ b/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java @@ -24,7 +24,6 @@ public void validNames() { @Test public void invalidNames() { - testInvalid("myname"); testInvalid(":latest"); testInvalid("/myname:latest"); testInvalid("/myname@sha256:latest"); From 8402356b2784734010c445949eff76ae69159a1e Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Wed, 13 Jun 2018 09:52:11 +0200 Subject: [PATCH 0329/5615] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6503cffbc68..b6e2b9226fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file. ## UNRELEASED ### Fixed +- Use `latest` tag as default image tag ([\#676](https://github.com/testcontainers/testcontainers-java/issues/676)) ### Changed - Allow `HttpWaitStrategy` to wait for a specific port ([\#703](https://github.com/testcontainers/testcontainers-java/pull/703)) From cf72d080b0cc5b20c06270695b5266a411b3bf16 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 13 Jun 2018 22:12:29 +0200 Subject: [PATCH 0330/5615] Read from both `/proc/net/tcp` and `/proc/net/tcp6` in `InternalCommandPortListeningCheck` (#750) --- .../wait/internal/InternalCommandPortListeningCheck.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java index 2cf7092f1ae..a4ba013b9d7 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java +++ b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java @@ -30,7 +30,7 @@ public Boolean call() { private void tryPort(Integer internalPort) { String[][] commands = { - {"/bin/sh", "-c", format("cat /proc/net/tcp | awk '{print $2}' | grep -i :%x && echo %s", internalPort, SUCCESS_MARKER)}, + {"/bin/sh", "-c", format("cat /proc/net/tcp{,6} | awk '{print $2}' | grep -i :%x && echo %s", internalPort, SUCCESS_MARKER)}, {"/bin/sh", "-c", format("nc -vz -w 1 localhost %d && echo %s", internalPort, SUCCESS_MARKER)}, {"/bin/bash", "-c", format(" Date: Wed, 13 Jun 2018 22:13:11 +0200 Subject: [PATCH 0331/5615] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6e2b9226fd..820d34de6ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ All notable changes to this project will be documented in this file. - Added Couchbase module ([\#688](https://github.com/testcontainers/testcontainers-java/pull/688)) - Changed **internal** port of KafkaContainer back to 9092 ([\#733](https://github.com/testcontainers/testcontainers-java/pull/733)) - Add support for Dockerfile based images to OracleContainer ([\#734](https://github.com/testcontainers/testcontainers-java/pull/734)) +- Read from both `/proc/net/tcp` and `/proc/net/tcp6` in `InternalCommandPortListeningCheck` ([\#750](https://github.com/testcontainers/testcontainers-java/pull/750)) ## [1.7.3] - 2018-05-16 From 793626774a94597bc3fd271403885e988e4e4771 Mon Sep 17 00:00:00 2001 From: David Byron Date: Wed, 13 Jun 2018 13:18:38 -0700 Subject: [PATCH 0332/5615] update CHANGELOG with info about withFixedExposedPort(int hostPort, int containerPort, InternetProtocol protocol) (#747) --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 820d34de6ff..d4e17365aa2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,9 +67,10 @@ All notable changes to this project will be documented in this file. - Deprecated `WaitStrategy` and implementations in favour of classes with same names in `org.testcontainers.containers.strategy` ([\#600](https://github.com/testcontainers/testcontainers-java/pull/600)) - Added `ContainerState` interface representing the state of a started container ([\#600](https://github.com/testcontainers/testcontainers-java/pull/600)) - Added `WaitStrategyTarget` interface which is the target of the new `WaitStrategy` ([\#600](https://github.com/testcontainers/testcontainers-java/pull/600)) -- *Breaking:* Removed hard-coded `wnameless` Oracle database image name. Users should instead place a file on the classpath named `testcontainers.properties` containing `oracle.container.image=IMAGE`, where IMAGE is a suitable image name and tag/SHA hash. For information, the approach recommended by Oracle for creating an Oracle XE docker image is described [here](https://blogs.oracle.com/oraclewebcentersuite/implement-oracle-database-xe-as-docker-containers). +- *Breaking:* Removed hard-coded `wnameless` Oracle database image name. Users should instead place a file on the classpath named `testcontainers.properties` containing `oracle.container.image=IMAGE`, where IMAGE is a suitable image name and tag/SHA hash. For information, the approach recommended by Oracle for creating an Oracle XE docker image is described [here](https://blogs.oracle.com/oraclewebcentersuite/implement-oracle-database-xe-as-docker-containers). - Added `DockerHealthcheckWaitStrategy` that is based on Docker's built-in [healthcheck](https://docs.docker.com/engine/reference/builder/#healthcheck) ([\#618](https://github.com/testcontainers/testcontainers-java/pull/618)). - Added `withLogConsumer(String serviceName, Consumer consumer)` method to `DockerComposeContainer` ([\#605](https://github.com/testcontainers/testcontainers-java/issues/605)) +- Added `withFixedExposedPort(int hostPort, int containerPort, InternetProtocol protocol)` method to `FixedHostPortGenericContainer` and `addFixedExposedPort(int hostPort, int containerPort, InternetProtocol protocol)` to `GenericContainer` ([\#586](https://github.com/testcontainers/testcontainers-java/pull/586)) ## [1.6.0] - 2018-01-28 @@ -110,7 +111,7 @@ All notable changes to this project will be documented in this file. - Stopping creation of temporary directory prior to creating temporary file ([\#443](https://github.com/testcontainers/testcontainers-java/issues/443)) - Ensure that temp files are created in a temp directory ([\#423](https://github.com/testcontainers/testcontainers-java/issues/423)) - Added `WaitAllStrategy` as a mechanism for composing multiple startup `WaitStrategy` objects together -- Changed `BrowserWebDriverContainer` to use improved wait strategies, to eliminate race conditions when starting VNC recording containers. This should lead to far fewer 'error' messages logged when starting up selenium containers, and less exposure to race related bugs (fixes [\#466](https://github.com/testcontainers/testcontainers-java/issues/466)). +- Changed `BrowserWebDriverContainer` to use improved wait strategies, to eliminate race conditions when starting VNC recording containers. This should lead to far fewer 'error' messages logged when starting up selenium containers, and less exposure to race related bugs (fixes [\#466](https://github.com/testcontainers/testcontainers-java/issues/466)). ### Changed - Make Network instances reusable (i.e. work with `@ClassRule`) ([\#469](https://github.com/testcontainers/testcontainers-java/issues/469)) From b4920c6c9a788efde90525c225bc57272cb5aa03 Mon Sep 17 00:00:00 2001 From: Stefan Hufschmidt Date: Wed, 13 Jun 2018 22:21:26 +0200 Subject: [PATCH 0333/5615] Added builder for timeouts in JdbcDatabaseContainer (#715) (#748) * Added builder for timeouts in JdbcDatabaseContainer (#715) * Adjusted Oracle and SQL Server to use other default timeouts (#715) --- .../containers/JdbcDatabaseContainer.java | 33 +++++++++++++++++-- .../containers/MSSQLServerContainer.java | 5 +++ .../containers/OracleContainer.java | 15 +++++---- 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index 140aaa7dde3..77589395c19 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -33,6 +33,9 @@ public abstract class JdbcDatabaseContainer> exten private String username = "SA"; private String password = "A_Str0ng_Required_Password"; + private static final int DEFAULT_STARTUP_TIMEOUT_SECONDS = 240; + private static final int DEFAULT_CONNECT_TIMEOUT_SECONDS = 240; + public MSSQLServerContainer() { this(IMAGE + ":" + DEFAULT_TAG); } public MSSQLServerContainer(final String dockerImageName) { super(dockerImageName); + withStartupTimeoutSeconds(DEFAULT_STARTUP_TIMEOUT_SECONDS); + withConnectTimeoutSeconds(DEFAULT_CONNECT_TIMEOUT_SECONDS); } @Override diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java index 375efa27176..c28c2ba82af 100644 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java @@ -14,6 +14,9 @@ public class OracleContainer extends JdbcDatabaseContainer { private static final int ORACLE_PORT = 1521; private static final int APEX_HTTP_PORT = 8080; + private static final int DEFAULT_STARTUP_TIMEOUT_SECONDS = 240; + private static final int DEFAULT_CONNECT_TIMEOUT_SECONDS = 120; + private static String resolveImageName() { String image = TestcontainersConfiguration.getInstance() .getProperties().getProperty("oracle.container.image"); @@ -27,15 +30,19 @@ private static String resolveImageName() { } public OracleContainer() { - super(resolveImageName()); + this(resolveImageName()); } public OracleContainer(String dockerImageName) { super(dockerImageName); + withStartupTimeoutSeconds(DEFAULT_STARTUP_TIMEOUT_SECONDS); + withConnectTimeoutSeconds(DEFAULT_CONNECT_TIMEOUT_SECONDS); } public OracleContainer(Future dockerImageName) { super(dockerImageName); + withStartupTimeoutSeconds(DEFAULT_STARTUP_TIMEOUT_SECONDS); + withConnectTimeoutSeconds(DEFAULT_CONNECT_TIMEOUT_SECONDS); } @Override @@ -45,7 +52,6 @@ protected Integer getLivenessCheckPort() { @Override protected void configure() { - addExposedPorts(ORACLE_PORT, APEX_HTTP_PORT); } @@ -87,9 +93,4 @@ public Integer getWebPort() { public String getTestQueryString() { return "SELECT 1 FROM DUAL"; } - - @Override - protected int getStartupTimeoutSeconds() { - return 240; - } } From e7078e23f074c5ef418ea153714351504acc4245 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 13 Jun 2018 22:22:10 +0200 Subject: [PATCH 0334/5615] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4e17365aa2..40d21d6016d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ All notable changes to this project will be documented in this file. - Changed **internal** port of KafkaContainer back to 9092 ([\#733](https://github.com/testcontainers/testcontainers-java/pull/733)) - Add support for Dockerfile based images to OracleContainer ([\#734](https://github.com/testcontainers/testcontainers-java/pull/734)) - Read from both `/proc/net/tcp` and `/proc/net/tcp6` in `InternalCommandPortListeningCheck` ([\#750](https://github.com/testcontainers/testcontainers-java/pull/750)) +- Added builder methods for timeouts in `JdbcDatabaseContainer` ([\#748](https://github.com/testcontainers/testcontainers-java/pull/748)) ## [1.7.3] - 2018-05-16 From dbb92674f3dd3de0f8b8de5c2c1c88b39dd72f00 Mon Sep 17 00:00:00 2001 From: Manik Magar Date: Wed, 13 Jun 2018 16:22:44 -0400 Subject: [PATCH 0335/5615] Enhancements and Fixes around JDBC URL Based Containers (#617) * Enhancements and Fixes around JDBC URL Based Containers (Rebased and Squashed) * Reinstate environment for Oracle module * Clean up diff --- .gitignore | 11 +- CHANGELOG.md | 6 + .../testcontainers/jdbc/JDBCDriverTest.java | 86 ++++++-- .../JdbcDatabaseContainerProvider.java | 31 +++ .../testcontainers/jdbc/ConnectionUrl.java | 204 ++++++++++++++++++ .../jdbc/ContainerDatabaseDriver.java | 105 +++------ .../jdbc/ConnectionUrlDriversTests.java | 57 +++++ .../jdbc/ConnectionUrlTest.java | 88 ++++++++ .../containers/MySQLContainerProvider.java | 37 +++- 9 files changed, 523 insertions(+), 102 deletions(-) create mode 100644 modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java create mode 100644 modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java create mode 100644 modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java diff --git a/.gitignore b/.gitignore index c7170052b0a..4c3f24f5bb4 100644 --- a/.gitignore +++ b/.gitignore @@ -47,8 +47,9 @@ node_modules/ .gradle/ build/ -# Eclipse IDE -.settings/ -.classpath -.project -bin/ +# Eclipse IDE files +**/.project +**/.classpath +**/.settings +**/bin/ +**/out/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 40d21d6016d..a43956e9b03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file. ## UNRELEASED ### Fixed +- Fixed JDBC URL Regex Pattern to ensure all supported Database URL's are accepted ([\#596](https://github.com/testcontainers/testcontainers-java/issues/596)) +- Filtered out TestContainer parameters (TC_*) from query string before passing to database ([\#345](https://github.com/testcontainers/testcontainers-java/issues/345)) - Use `latest` tag as default image tag ([\#676](https://github.com/testcontainers/testcontainers-java/issues/676)) ### Changed @@ -12,6 +14,10 @@ All notable changes to this project will be documented in this file. - Add support for defining container labels ([\#725](https://github.com/testcontainers/testcontainers-java/pull/725)) - Use `quay.io/testcontainers/ryuk` instead of `bsideup/ryuk` ([\#721](https://github.com/testcontainers/testcontainers-java/pull/721)) - Added Couchbase module ([\#688](https://github.com/testcontainers/testcontainers-java/pull/688)) +- Enhancements and Fixes for JDBC URL usage to create Containers ([\#594](https://github.com/testcontainers/testcontainers-java/pull/594)) + - Extracted JDBC URL manipulations to a separate class - `ConnectionUrl`. + - Added an overloaded method `JdbcDatabaseContainerProvider.newInstance(ConnectionUrl)`, with default implementation delegating to the existing `newInstance(tag)` method. (Relates to [\#566](https://github.com/testcontainers/testcontainers-java/issues/566)) + - Added an implementation of `MySQLContainerProvider.newInstance(ConnectionUrl)` that uses Database Name, User, and Password from JDBC URL while creating new MySQL Container. ([\#566](https://github.com/testcontainers/testcontainers-java/issues/566) for MySQL Container) - Changed **internal** port of KafkaContainer back to 9092 ([\#733](https://github.com/testcontainers/testcontainers-java/pull/733)) - Add support for Dockerfile based images to OracleContainer ([\#734](https://github.com/testcontainers/testcontainers-java/pull/734)) - Read from both `/proc/net/tcp` and `/proc/net/tcp6` in `InternalCommandPortListeningCheck` ([\#750](https://github.com/testcontainers/testcontainers-java/pull/750)) diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java index 4ef75c2c516..49c889fcb5d 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java @@ -32,26 +32,29 @@ public class JDBCDriverTest { public boolean performTestForCharacterSet; @Parameter(3) public boolean performTestForCustomIniFile; + @Parameter(4) + public boolean performTestForJDBCParams; @Parameterized.Parameters(name = "{index} - {0}") public static Iterable data() { return asList( new Object[][]{ - {"jdbc:tc:mysql://hostname/databasename", false, false, false}, - {"jdbc:tc:mysql:5.5.43://hostname/databasename?TC_INITSCRIPT=somepath/init_mysql.sql", true, false, false}, - {"jdbc:tc:mysql:5.5.43://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", true, false, false}, - {"jdbc:tc:mysql:5.5.43://hostname/databasename?useUnicode=yes&characterEncoding=utf8", false, true, false}, - {"jdbc:tc:mysql:5.5.43://hostname/databasename", false, false, false}, - {"jdbc:tc:mysql:5.5.43://hostname/databasename?useSSL=false", false, false, false}, - {"jdbc:tc:postgresql:9.6.8://hostname/databasename", false, false, false}, - {"jdbc:tc:mysql:5.6://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override", false, false, true}, - {"jdbc:tc:mariadb://hostname/databasename", false, false, false}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename", false, false, false}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?useUnicode=yes&characterEncoding=utf8", false, true, false}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITSCRIPT=somepath/init_mariadb.sql", true, false, false}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", true, false, false}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_MY_CNF=somepath/mariadb_conf_override", false, false, true} - }); + {"jdbc:tc:mysql://hostname/databasename", false, false, false, false}, + {"jdbc:tc:mysql://hostname/databasename?user=someuser&TC_INITSCRIPT=somepath/init_mysql.sql", true, false, false, true}, + {"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", true, false, false, true}, + {"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=somepath/init_mysql.sql", true, false, false, true}, + {"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&password=somepwd&TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", true, false, false, true}, + {"jdbc:tc:mysql:5.5.43://hostname/databasename?useUnicode=yes&characterEncoding=utf8", false, true, false, false}, + {"jdbc:tc:mysql:5.5.43://hostname/databasename", false, false, false, false}, + {"jdbc:tc:mysql:5.5.43://hostname/databasename?useSSL=false", false, false, false, false}, + {"jdbc:tc:postgresql:9.6.8://hostname/databasename", false, false, false, false}, + {"jdbc:tc:mysql:5.6://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override", false, false, true, false}, + {"jdbc:tc:mariadb://hostname/databasename", false, false, false, false}, + {"jdbc:tc:mariadb:10.2.14://hostname/databasename", false, false, false, false}, + {"jdbc:tc:mariadb:10.2.14://hostname/databasename?useUnicode=yes&characterEncoding=utf8", false, true, false, false}, + {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITSCRIPT=somepath/init_mariadb.sql", true, false, false, false}, + {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", true, false, false, false}, + {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_MY_CNF=somepath/mariadb_conf_override", false, false, true, false}}); } public static void sampleInitFunction(Connection connection) throws SQLException { @@ -77,7 +80,14 @@ public void test() throws SQLException { performTestForScriptedSchema(jdbcUrl); } + if (performTestForJDBCParams) { + performTestForJDBCParamUsage(jdbcUrl); + } + if (performTestForCharacterSet) { + //Called twice to ensure that the query string parameters are used when + //connections are created from cached containers. + performSimpleTestWithCharacterSet(jdbcUrl); performSimpleTestWithCharacterSet(jdbcUrl); } @@ -107,25 +117,57 @@ private void performTestForScriptedSchema(String jdbcUrl) throws SQLException { assertEquals("A basic SELECT query succeeds where the schema has been applied from a script", "hello world", resultSetString); return true; }); - - assertTrue("The database returned a record as expected", result); - } } - private void performSimpleTestWithCharacterSet(String jdbcUrl) throws SQLException { + private void performTestForJDBCParamUsage(String jdbcUrl) throws SQLException { try (HikariDataSource dataSource = getDataSource(jdbcUrl, 1)) { - boolean result = new QueryRunner(dataSource).query("SHOW VARIABLES LIKE 'character\\_set\\_connection'", rs -> { + boolean result = new QueryRunner(dataSource).query("select CURRENT_USER()", rs -> { + rs.next(); + String resultUser = rs.getString(1); + assertEquals("User from query param is created.", "someuser@%", resultUser); + return true; + }); + + result = new QueryRunner(dataSource).query("SELECT DATABASE()", rs -> { rs.next(); - String resultSetInt = rs.getString(2); - assertEquals("Passing query parameters to set DB connection encoding is successful", "utf8", resultSetInt); + String resultDB = rs.getString(1); + assertEquals("Database name from URL String is used.", "databasename", resultDB); return true; }); assertTrue("The database returned a record as expected", result); + } } + /** + * This method intentionally verifies encoding twice to ensure that the query string parameters are used when + * Connections are created from cached containers. + * + * @param jdbcUrl + * @throws SQLException + */ + private void performSimpleTestWithCharacterSet(String jdbcUrl) throws SQLException { + HikariDataSource datasource1 = verifyCharacterSet(jdbcUrl); + HikariDataSource datasource2 = verifyCharacterSet(jdbcUrl); + datasource1.close(); + datasource2.close(); + } + + private HikariDataSource verifyCharacterSet(String jdbcUrl) throws SQLException { + HikariDataSource dataSource = getDataSource(jdbcUrl, 1); + boolean result = new QueryRunner(dataSource).query("SHOW VARIABLES LIKE 'character\\_set\\_connection'", rs -> { + rs.next(); + String resultSetInt = rs.getString(2); + assertEquals("Passing query parameters to set DB connection encoding is successful", "utf8", resultSetInt); + return true; + }); + + assertTrue("The database returned a record as expected", result); + return dataSource; + } + private void performTestForCustomIniFile(final String jdbcUrl) throws SQLException { assumeFalse(SystemUtils.IS_OS_WINDOWS); try (HikariDataSource ds = getDataSource(jdbcUrl, 1)) { diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainerProvider.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainerProvider.java index aadc266f6f9..2a73879a572 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainerProvider.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainerProvider.java @@ -1,5 +1,7 @@ package org.testcontainers.containers; +import org.testcontainers.jdbc.ConnectionUrl; + import lombok.extern.slf4j.Slf4j; /** @@ -8,8 +10,19 @@ @Slf4j public abstract class JdbcDatabaseContainerProvider { + /** + * Tests if the specified database type is supported by this Container Provider. It should match to the base image name. + * @param databaseType {@link String} + * @return true when provider can handle this database type, else false. + */ public abstract boolean supports(String databaseType); + /** + * Instantiate a new {@link JdbcDatabaseContainer} without any specified image tag. Subclasses should + * override this method if possible, to provide a default tag that is more stable than latest`. + * + * @return Instance of {@link JdbcDatabaseContainer} + */ public JdbcDatabaseContainer newInstance() { log.warn("No explicit version tag was provided in JDBC URL and this class ({}) does not " + "override newInstance() to set a default tag. `latest` will be used but results may " + @@ -17,5 +30,23 @@ public JdbcDatabaseContainer newInstance() { return this.newInstance("latest"); } + /** + * Instantiate a new {@link JdbcDatabaseContainer} with specified image tag. + * @param tag + * @return Instance of {@link JdbcDatabaseContainer} + */ public abstract JdbcDatabaseContainer newInstance(String tag); + + /** + * Instantiate a new {@link JdbcDatabaseContainer} using information provided with {@link ConnectionUrl}. + * @param url {@link ConnectionUrl} + * @return Instance of {@link JdbcDatabaseContainer} + */ + public JdbcDatabaseContainer newInstance(ConnectionUrl url) { + if (url.getImageTag().isPresent()) { + return newInstance(url.getImageTag().get()); + } else { + return newInstance(); + } + } } diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java new file mode 100644 index 00000000000..7c5a53ede65 --- /dev/null +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java @@ -0,0 +1,204 @@ +package org.testcontainers.jdbc; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; + +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * This is an Immutable class holding JDBC Connection Url and its parsed components, used by {@link ContainerDatabaseDriver}. + *

+ * {@link ConnectionUrl#parseUrl()} method must be called after instantiating this class. + * + * @author manikmagar + */ +@EqualsAndHashCode(of = "url") @Getter +public class ConnectionUrl { + + private String url; + + private String databaseType; + + private Optional imageTag; + + /** + * This is a part of the connection string that may specify host:port/databasename. + * It may vary for different clients and so clients can parse it as needed. + */ + private String dbHostString; + + private boolean inDaemonMode = false; + + private Optional databaseHost = Optional.empty(); + + private Optional databasePort = Optional.empty(); + + private Optional databaseName = Optional.empty(); + + private Optional initScriptPath = Optional.empty(); + + private Optional initFunction = Optional.empty(); + + private Optional queryString; + + private Map containerParameters; + + private Map queryParameters; + + public static ConnectionUrl newInstance(final String url){ + ConnectionUrl connectionUrl = new ConnectionUrl(url); + connectionUrl.parseUrl(); + return connectionUrl; + } + + private ConnectionUrl(final String url) { + this.url = Objects.requireNonNull(url, "Connection URL cannot be null"); + } + + public static boolean accepts(final String url) { + return url.startsWith("jdbc:tc:"); + } + + /** + * This method applies various REGEX Patterns to parse the URL associated with this instance. + * This is called from a @{@link ConnectionUrl#newInstance(String)} static factory method to create immutable instance of {@link ConnectionUrl}. + * To avoid mutation after class is instantiated, this method should not be publicly accessible. + */ + private void parseUrl() { + /* + Extract from the JDBC connection URL: + * The database type (e.g. mysql, postgresql, ...) + * The docker tag, if provided. + * The URL query string, if provided + */ + Matcher urlMatcher = Patterns.URL_MATCHING_PATTERN.matcher(this.getUrl()); + if (!urlMatcher.matches()) { + //Try for Oracle pattern + urlMatcher = Patterns.ORACLE_URL_MATCHING_PATTERN.matcher(this.getUrl()); + if (!urlMatcher.matches()) { + throw new IllegalArgumentException("JDBC URL matches jdbc:tc: prefix but the database or tag name could not be identified"); + } + } + databaseType = urlMatcher.group(1); + + imageTag = Optional.ofNullable(urlMatcher.group(3)); + + //String like hostname:port/database name, which may vary based on target database. + //Clients can further parse it as needed. + dbHostString = urlMatcher.group(4); + + //In case it matches to the default pattern + Matcher dbInstanceMatcher = Patterns.DB_INSTANCE_MATCHING_PATTERN.matcher(dbHostString); + if (dbInstanceMatcher.matches()) { + databaseHost = Optional.of(dbInstanceMatcher.group(1)); + databasePort = Optional.ofNullable(dbInstanceMatcher.group(3)).map(value -> Integer.valueOf(value)); + databaseName = Optional.of(dbInstanceMatcher.group(4)); + } + + queryParameters = Collections.unmodifiableMap( + parseQueryParameters( + Optional.ofNullable(urlMatcher.group(5)).orElse(""))); + + String query = queryParameters + .entrySet() + .stream() + .map(e -> e.getKey() + "=" + e.getValue()) + .collect(Collectors.joining("&")); + + if (query == null || query.trim().length() == 0) { + queryString = Optional.empty(); + } else { + queryString = Optional.of("?" + query); + } + + containerParameters = Collections.unmodifiableMap(parseContainerParameters()); + + initScriptPath = Optional.ofNullable(containerParameters.get("TC_INITSCRIPT")); + + Matcher funcMatcher = Patterns.INITFUNCTION_MATCHING_PATTERN.matcher(this.getUrl()); + if (funcMatcher.matches()) { + initFunction = Optional.of(new InitFunctionDef(funcMatcher.group(2), funcMatcher.group(4))); + } + + Matcher daemonMatcher = Patterns.DAEMON_MATCHING_PATTERN.matcher(this.getUrl()); + inDaemonMode = daemonMatcher.matches() && Boolean.parseBoolean(daemonMatcher.group(2)); + + } + + /** + * Get the TestContainers Parameters such as Init Function, Init Script path etc. + * + * @return {@link Map} + */ + private Map parseContainerParameters() { + + Map results = new HashMap<>(); + + Matcher matcher = Patterns.TC_PARAM_MATCHING_PATTERN.matcher(this.getUrl()); + while (matcher.find()) { + String key = matcher.group(1); + String value = matcher.group(2); + results.put(key, value); + } + + return results; + } + + /** + * Get all Query parameters specified in the Connection URL after ?. This DOES NOT include TestContainers (TC_*) parameters. + * + * @return {@link Map} + */ + private Map parseQueryParameters(final String queryString) { + + Map results = new HashMap<>(); + Matcher matcher = Patterns.QUERY_PARAM_MATCHING_PATTERN.matcher(queryString); + while (matcher.find()) { + String key = matcher.group(1); + String value = matcher.group(2); + if(!key.matches(Patterns.TC_PARAM_NAME_PATTERN)) results.put(key, value); + } + + return results; + } + + /** + * This interface defines the Regex Patterns used by {@link ConnectionUrl}. + * + * @author manikmagar + */ + public interface Patterns { + Pattern URL_MATCHING_PATTERN = Pattern.compile("jdbc:tc:([a-z]+)(:([^:]+))?://([^\\?]+)(\\?.*)?"); + + Pattern ORACLE_URL_MATCHING_PATTERN = Pattern.compile("jdbc:tc:([a-z]+)(:([^(thin:)]+))?:thin:@([^\\?]+)(\\?.*)?"); + + //Matches to part of string - hostname:port/databasename + Pattern DB_INSTANCE_MATCHING_PATTERN = Pattern.compile("([^:]+)(:([0-9]+))?/([^\\\\?]+)"); + + Pattern DAEMON_MATCHING_PATTERN = Pattern.compile(".*([\\?&]?)TC_DAEMON=([^\\?&]+).*"); + Pattern INITSCRIPT_MATCHING_PATTERN = Pattern.compile(".*([\\?&]?)TC_INITSCRIPT=([^\\?&]+).*"); + Pattern INITFUNCTION_MATCHING_PATTERN = Pattern.compile(".*([\\?&]?)TC_INITFUNCTION=" + + "((\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*\\.)*\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)" + + "::" + + "(\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)" + + ".*"); + + String TC_PARAM_NAME_PATTERN = "(TC_[A-Z_]+)"; + + Pattern TC_PARAM_MATCHING_PATTERN = Pattern.compile(TC_PARAM_NAME_PATTERN + "=([^\\?&]+)"); + + Pattern QUERY_PARAM_MATCHING_PATTERN = Pattern.compile("([^\\?&=]+)=([^\\?&]+)"); + + } + + @Getter + @AllArgsConstructor + public class InitFunctionDef { + private String className; + private String methodName; + } +} diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java index 569dc794213..f5a6dc57731 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java @@ -16,8 +16,6 @@ import java.sql.*; import java.util.*; import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** * Test Containers JDBC proxy driver. This driver will handle JDBC URLs of the form: @@ -37,16 +35,6 @@ */ public class ContainerDatabaseDriver implements Driver { - private static final Pattern URL_MATCHING_PATTERN = Pattern.compile("jdbc:tc:([a-z]+)(:([^:]+))?://[^\\?]+(\\?.*)?"); - private static final Pattern DAEMON_MATCHING_PATTERN = Pattern.compile(".*([\\?&]?)TC_DAEMON=([^\\?&]+).*"); - private static final Pattern INITSCRIPT_MATCHING_PATTERN = Pattern.compile(".*([\\?&]?)TC_INITSCRIPT=([^\\?&]+).*"); - private static final Pattern INITFUNCTION_MATCHING_PATTERN = Pattern.compile(".*([\\?&]?)TC_INITFUNCTION=" + - "((\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*\\.)*\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)" + - "::" + - "(\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)" + - ".*"); - - private static final Pattern TC_PARAM_MATCHING_PATTERN = Pattern.compile("([A-Z_]+)=([^\\?&]+)"); private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(ContainerDatabaseDriver.class); private Driver delegate; @@ -81,52 +69,34 @@ public synchronized Connection connect(String url, final Properties info) throws return null; } + ConnectionUrl connectionUrl = ConnectionUrl.newInstance(url); + synchronized (jdbcUrlContainerCache) { - String queryString = ""; + String queryString = connectionUrl.getQueryString().orElse(""); /* If we already have a running container for this exact connection string, we want to connect to that rather than create a new container */ - JdbcDatabaseContainer container = jdbcUrlContainerCache.get(url); + JdbcDatabaseContainer container = jdbcUrlContainerCache.get(connectionUrl.getUrl()); if (container == null) { - /* - Extract from the JDBC connection URL: - * The database type (e.g. mysql, postgresql, ...) - * The docker tag, if provided. - * The URL query string, if provided - */ - Matcher urlMatcher = URL_MATCHING_PATTERN.matcher(url); - if (!urlMatcher.matches()) { - throw new IllegalArgumentException("JDBC URL matches jdbc:tc: prefix but the database or tag name could not be identified"); - } - String databaseType = urlMatcher.group(1); - String tag = urlMatcher.group(3); - queryString = urlMatcher.group(4); - if (queryString == null) { - queryString = ""; - } + LOGGER.debug("Container not found in cache, creating new instance"); - Map parameters = getContainerParameters(url); + Map parameters = connectionUrl.getContainerParameters(); /* Find a matching container type using ServiceLoader. */ ServiceLoader databaseContainers = ServiceLoader.load(JdbcDatabaseContainerProvider.class); for (JdbcDatabaseContainerProvider candidateContainerType : databaseContainers) { - if (candidateContainerType.supports(databaseType)) { - - if (tag != null) { - container = candidateContainerType.newInstance(tag); - } else { - container = candidateContainerType.newInstance(); - } + if (candidateContainerType.supports(connectionUrl.getDatabaseType())) { + container = candidateContainerType.newInstance(connectionUrl); delegate = container.getJdbcDriverInstance(); } } if (container == null) { - throw new UnsupportedOperationException("Database name " + databaseType + " not supported"); + throw new UnsupportedOperationException("Database name " + connectionUrl.getDatabaseType() + " not supported"); } /* @@ -157,42 +127,29 @@ public synchronized Connection connect(String url, final Properties info) throws */ if (!initializedContainers.contains(container.getContainerId())) { DatabaseDelegate databaseDelegate = new JdbcDatabaseDelegate(container); - runInitScriptIfRequired(url, databaseDelegate); - runInitFunctionIfRequired(url, connection); + runInitScriptIfRequired(connectionUrl, databaseDelegate); + runInitFunctionIfRequired(connectionUrl, connection); initializedContainers.add(container.getContainerId()); } - return wrapConnection(connection, container, url); + return wrapConnection(connection, container, connectionUrl); } } - private Map getContainerParameters(String url) { - - Map results = new HashMap<>(); - - Matcher matcher = TC_PARAM_MATCHING_PATTERN.matcher(url); - while (matcher.find()) { - String key = matcher.group(1); - String value = matcher.group(2); - results.put(key, value); - } - - return results; - } /** * Wrap the connection, setting up a callback to be called when the connection is closed. *

* When there are no more open connections, the container itself will be stopped. * - * @param connection the new connection to be wrapped - * @param container the container which the connection is associated with - * @param url the testcontainers JDBC URL for this connection + * @param connection the new connection to be wrapped + * @param container the container which the connection is associated with + * @param connectionUrl {@link ConnectionUrl} instance representing JDBC Url for this connection * @return the connection, wrapped */ - private Connection wrapConnection(final Connection connection, final JdbcDatabaseContainer container, final String url) { - final Matcher matcher = DAEMON_MATCHING_PATTERN.matcher(url); - final boolean isDaemon = matcher.matches() ? Boolean.parseBoolean(matcher.group(2)) : false; + private Connection wrapConnection(final Connection connection, final JdbcDatabaseContainer container, final ConnectionUrl connectionUrl) { + + final boolean isDaemon = connectionUrl.isInDaemonMode(); Set connections = containerConnections.get(container.getContainerId()); @@ -209,7 +166,7 @@ private Connection wrapConnection(final Connection connection, final JdbcDatabas finalConnections.remove(connection); if (!isDaemon && finalConnections.isEmpty()) { container.stop(); - jdbcUrlContainerCache.remove(url); + jdbcUrlContainerCache.remove(connectionUrl.getUrl()); } }); } @@ -217,14 +174,13 @@ private Connection wrapConnection(final Connection connection, final JdbcDatabas /** * Run an init script from the classpath. * - * @param url the JDBC URL to check for init script declarations. + * @param connectionUrl {@link ConnectionUrl} instance representing JDBC Url with init script. * @param databaseDelegate database delegate to apply init scripts to the database * @throws SQLException on script or DB error */ - private void runInitScriptIfRequired(String url, DatabaseDelegate databaseDelegate) throws SQLException { - Matcher matcher = INITSCRIPT_MATCHING_PATTERN.matcher(url); - if (matcher.matches()) { - String initScriptPath = matcher.group(2); + private void runInitScriptIfRequired(final ConnectionUrl connectionUrl, DatabaseDelegate databaseDelegate) throws SQLException { + if (connectionUrl.getInitScriptPath().isPresent()) { + String initScriptPath = connectionUrl.getInitScriptPath().get(); try { URL resource = Thread.currentThread().getContextClassLoader().getResource(initScriptPath); @@ -248,15 +204,14 @@ private void runInitScriptIfRequired(String url, DatabaseDelegate databaseDelega /** * Run an init function (must be a public static method on an accessible class). * - * @param url the JDBC URL to check for init function declarations. - * @param connection JDBC connection to apply init functions to. + * @param connectionUrl {@link ConnectionUrl} instance representing JDBC Url with r init function declarations. + * @param connection JDBC connection to apply init functions to. * @throws SQLException on script or DB error */ - private void runInitFunctionIfRequired(String url, Connection connection) throws SQLException { - Matcher matcher = INITFUNCTION_MATCHING_PATTERN.matcher(url); - if (matcher.matches()) { - String className = matcher.group(2); - String methodName = matcher.group(4); + private void runInitFunctionIfRequired(final ConnectionUrl connectionUrl, Connection connection) throws SQLException { + if (connectionUrl.getInitFunction().isPresent()) { + String className = connectionUrl.getInitFunction().get().getClassName(); + String methodName = connectionUrl.getInitFunction().get().getMethodName(); try { Class initFunctionClazz = Class.forName(className); @@ -314,6 +269,7 @@ public static void killContainers() { * Utility method to kill a database container directly from test support code. It shouldn't be necessary to use this, * but it is provided for convenience - e.g. for situations where many different database containers are being * tested and cleanup is needed to limit resource usage. + * * @param jdbcUrl the JDBC URL of the container which should be killed */ public static void killContainer(String jdbcUrl) { @@ -330,6 +286,7 @@ public static void killContainer(String jdbcUrl) { /** * Utility method to get an instance of a database container given its JDBC URL. + * * @param jdbcUrl the JDBC URL of the container instance to get * @return an instance of database container or null if no container associated with JDBC URL */ diff --git a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java new file mode 100644 index 00000000000..3f0673d834f --- /dev/null +++ b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java @@ -0,0 +1,57 @@ +package org.testcontainers.jdbc; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; + +import java.util.Optional; + +import static java.util.Arrays.asList; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; + +/** + * This Test class validates that all supported JDBC URL's can be parsed by ConnectionUrl class. + * + * @author ManikMagar + */ +@RunWith(Parameterized.class) +public class ConnectionUrlDriversTests { + + @Parameter + public String jdbcUrl; + @Parameter(1) + public String databaseType; + @Parameter(2) + public Optional tag; + @Parameter(3) + public String dbHostString; + @Parameter(4) + public String databaseName; + + @Parameterized.Parameters(name = "{index} - {0}") + public static Iterable data() { + return asList( + new Object[][]{ + {"jdbc:tc:mysql:5.5.43://hostname/test", "mysql", Optional.of("5.5.43"), "hostname/test", "test"}, + {"jdbc:tc:mysql://hostname/test", "mysql", Optional.empty(), "hostname/test", "test"}, + {"jdbc:tc:postgresql:1.2.3://hostname/test", "postgresql", Optional.of("1.2.3"), "hostname/test", "test"}, + {"jdbc:tc:postgresql://hostname/test", "postgresql", Optional.empty(), "hostname/test", "test"}, + {"jdbc:tc:sqlserver:1.2.3://localhost;instance=SQLEXPRESS:1433;databaseName=test", "sqlserver", Optional.of("1.2.3"), "localhost;instance=SQLEXPRESS:1433;databaseName=test", ""}, + {"jdbc:tc:sqlserver://localhost;instance=SQLEXPRESS:1433;databaseName=test", "sqlserver", Optional.empty(), "localhost;instance=SQLEXPRESS:1433;databaseName=test", ""}, + {"jdbc:tc:mariadb:1.2.3://localhost:3306/test", "mariadb", Optional.of("1.2.3"), "localhost:3306/test", "test"}, + {"jdbc:tc:mariadb://localhost:3306/test", "mariadb", Optional.empty(), "localhost:3306/test", "test"}, + {"jdbc:tc:oracle:1.2.3:thin:@localhost:1521/test", "oracle", Optional.of("1.2.3"), "localhost:1521/test", "test"}, + {"jdbc:tc:oracle:thin:@localhost:1521/test", "oracle", Optional.empty(), "localhost:1521/test", "test"} + }); + } + + @Test + public void test() { + ConnectionUrl url = ConnectionUrl.newInstance(jdbcUrl); + assertEquals("Database Type is as expected", databaseType, url.getDatabaseType()); + assertEquals("Image tag is as expected", tag, url.getImageTag()); + assertEquals("Database Host String is as expected", dbHostString, url.getDbHostString()); + assertEquals("Database Name is as expected", databaseName, url.getDatabaseName().orElse("")); + } +} diff --git a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java new file mode 100644 index 00000000000..a5c36d8f065 --- /dev/null +++ b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java @@ -0,0 +1,88 @@ +package org.testcontainers.jdbc; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import java.util.Optional; + +import static org.rnorth.visibleassertions.VisibleAssertions.*; + +public class ConnectionUrlTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + + @Test + public void testConnectionUrl1() { + String urlString = "jdbc:tc:mysql:5.6.23://somehostname:3306/databasename?a=b&c=d"; + ConnectionUrl url = ConnectionUrl.newInstance(urlString); + + assertEquals("Database Type value is as expected", "mysql", url.getDatabaseType()); + assertEquals("Database Image tag value is as expected", "5.6.23", url.getImageTag().get()); + assertEquals("Database Host String is as expected", "somehostname:3306/databasename", url.getDbHostString()); + assertEquals("Query String value is as expected", "?a=b&c=d", url.getQueryString().get()); + assertEquals("Database Host value is as expected", "somehostname", url.getDatabaseHost().get()); + assertEquals("Database Port value is as expected", 3306, url.getDatabasePort().get()); + assertEquals("Database Name value is as expected", "databasename", url.getDatabaseName().get()); + + assertEquals("Parameter a is captured", "b", url.getQueryParameters().get("a")); + assertEquals("Parameter c is captured", "d", url.getQueryParameters().get("c")); + } + + + @Test + public void testConnectionUrl2() { + String urlString = "jdbc:tc:mysql://somehostname/databasename"; + ConnectionUrl url = ConnectionUrl.newInstance(urlString); + + assertEquals("Database Type value is as expected", "mysql", url.getDatabaseType()); + assertFalse("Database Image tag value is as expected", url.getImageTag().isPresent()); + assertEquals("Database Host String is as expected", "somehostname/databasename", url.getDbHostString()); + assertEquals("Query String value is as expected", Optional.empty(), url.getQueryString()); + assertEquals("Database Host value is as expected", "somehostname", url.getDatabaseHost().get()); + assertFalse("Database Port is null as expected", url.getDatabasePort().isPresent()); + assertEquals("Database Name value is as expected", "databasename", url.getDatabaseName().get()); + + assertTrue("Connection Parameters set is empty", url.getQueryParameters().isEmpty()); + + } + + @Test + public void testInitScriptPathCapture() { + String urlString = "jdbc:tc:mysql:5.6.23://somehostname:3306/databasename?a=b&c=d&TC_INITSCRIPT=somepath/init_mysql.sql"; + ConnectionUrl url = ConnectionUrl.newInstance(urlString); + + assertEquals("Database Type value is as expected", "somepath/init_mysql.sql", url.getInitScriptPath().get()); + assertEquals("Query String value is as expected", "?a=b&c=d", url.getQueryString().get()); + assertEquals("INIT SCRIPT Path exists in Container Parameters", "somepath/init_mysql.sql", url.getContainerParameters().get("TC_INITSCRIPT")); + + //Parameter sets are unmodifiable + thrown.expect(UnsupportedOperationException.class); + url.getContainerParameters().remove("TC_INITSCRIPT"); + url.getQueryParameters().remove("a"); + + } + + @Test + public void testInitFunctionCapture() { + String urlString = "jdbc:tc:mysql:5.6.23://somehostname:3306/databasename?a=b&c=d&TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction"; + ConnectionUrl url = ConnectionUrl.newInstance(urlString); + + assertTrue("Init Function parameter exists", url.getInitFunction().isPresent()); + + assertEquals("Init function class is as expected", "org.testcontainers.jdbc.JDBCDriverTest", url.getInitFunction().get().getClassName()); + assertEquals("Init function class is as expected", "sampleInitFunction", url.getInitFunction().get().getMethodName()); + + } + + @Test + public void testDaemonCapture() { + String urlString = "jdbc:tc:mysql:5.6.23://somehostname:3306/databasename?a=b&c=d&TC_DAEMON=true"; + ConnectionUrl url = ConnectionUrl.newInstance(urlString); + + assertTrue("Daemon flag is set to true.", url.isInDaemonMode()); + + } +} diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainerProvider.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainerProvider.java index f503330d0e5..cfc072707da 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainerProvider.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainerProvider.java @@ -1,9 +1,18 @@ package org.testcontainers.containers; +import org.testcontainers.jdbc.ConnectionUrl; + +import java.util.Objects; + /** * Factory for MySQL containers. */ public class MySQLContainerProvider extends JdbcDatabaseContainerProvider { + + private static final String USER_PARAM = "user"; + + private static final String PASSWORD_PARAM = "password"; + @Override public boolean supports(String databaseType) { return databaseType.equals(MySQLContainer.NAME); @@ -16,6 +25,32 @@ public JdbcDatabaseContainer newInstance() { @Override public JdbcDatabaseContainer newInstance(String tag) { - return new MySQLContainer(MySQLContainer.IMAGE + ":" + tag); + if (tag != null) { + return new MySQLContainer(MySQLContainer.IMAGE + ":" + tag); + } else { + return newInstance(); + } + } + + @Override + public JdbcDatabaseContainer newInstance(ConnectionUrl connectionUrl) { + Objects.requireNonNull(connectionUrl, "Connection URL cannot be null"); + + final String databaseName = connectionUrl.getDatabaseName().orElse("test"); + final String user = connectionUrl.getQueryParameters().getOrDefault(USER_PARAM, "test"); + final String password = connectionUrl.getQueryParameters().getOrDefault(PASSWORD_PARAM, "test"); + + final JdbcDatabaseContainer instance; + if (connectionUrl.getImageTag().isPresent()) { + instance = newInstance(connectionUrl.getImageTag().get()); + } else { + instance = newInstance(); + } + + return instance + .withDatabaseName(databaseName) + .withUsername(user) + .withPassword(password); } + } From 85321e1a9d1ef40aa45449c191ab43206c76fbc2 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 13 Jun 2018 22:45:17 +0200 Subject: [PATCH 0336/5615] Add optional OkHttp transport (instead of Netty) (#710) * add OkHttp transport * implement chunked response handling based on Netty implementation * handle response errors * do not block caller's thread * split files, support more protocols, fix blocking call * mark OkHttpInvocationBuilder & OkHttpWebTarget as internal classes * re-throw ExecutionException's cause * always close the response * reuse OkHttpClient instance * shade okhttp / okio, disable pooling of unix-socket based transport * do not block caller's thread (well, again :D) * retry on connection failure * do not use mutable "." directory in DirectoryTarResourceTest * replace docker-filesocket with plain AFUNIXSocket factory * fix shaded dependencies, rename handleStreamedResponse -> executeAndStream * fix shading * remove isOpen check * try jnr-unixsocket * remove cglib * try org.scala-sbt.ipcsocket:ipcsocket * experimental npipe support * fix pipe name * fix double escaping * shade ipcsocket * disable read timeout * make OkHttp opt-in-able * fix properties file name * fix typo * fix Codacy warnings * workaround docker-java (does not support npipe) * support npipe in DockerClientConfigUtils --- build.gradle | 6 +- circle.yml | 16 + core/build.gradle | 15 +- .../dockerclient/DockerClientConfigUtils.java | 1 + .../DockerClientProviderStrategy.java | 22 +- .../NpipeSocketClientProviderStrategy.java | 78 +++++ .../okhttp/NamedPipeSocketFactory.java | 79 +++++ .../okhttp/OkHttpDockerCmdExecFactory.java | 153 +++++++++ .../okhttp/OkHttpInvocationBuilder.java | 325 ++++++++++++++++++ .../transport/okhttp/OkHttpWebTarget.java | 113 ++++++ .../transport/okhttp/UnixSocketFactory.java | 78 +++++ .../utility/TestcontainersConfiguration.java | 4 + ....dockerclient.DockerClientProviderStrategy | 3 +- .../utility/DirectoryTarResourceTest.java | 7 +- core/src/test/resources/logback-test.xml | 10 +- 15 files changed, 894 insertions(+), 16 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java create mode 100644 core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/NamedPipeSocketFactory.java create mode 100644 core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpDockerCmdExecFactory.java create mode 100644 core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java create mode 100644 core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpWebTarget.java create mode 100644 core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/UnixSocketFactory.java diff --git a/build.gradle b/build.gradle index 8a0190349e5..edee9d34d28 100644 --- a/build.gradle +++ b/build.gradle @@ -72,8 +72,10 @@ subprojects { "com.google", "io.netty", "org.bouncycastle", - "org.newsclub", - "org.zeroturnaround" + "org.zeroturnaround", + "okhttp3", + "okio", + "org.scalasbt.ipcsocket", ].each { relocate(it, "org.testcontainers.shaded.$it") } } diff --git a/circle.yml b/circle.yml index a793bf8cb6a..b216656417f 100644 --- a/circle.yml +++ b/circle.yml @@ -14,6 +14,21 @@ jobs: when: always - store_test_results: path: ~/junit + okhttp: + steps: + - checkout + - run: + command: | + echo "transport.type=okhttp" >> core/src/test/resources/testcontainers.properties + ./gradlew testcontainers:check + - run: + name: Save test results + command: | + mkdir -p ~/junit/ + find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; + when: always + - store_test_results: + path: ~/junit modules-no-jdbc-test-no-selenium: steps: - checkout @@ -65,6 +80,7 @@ workflows: test_all: jobs: - core + - okhttp - modules-no-jdbc-test-no-selenium - modules-jdbc-test - selenium diff --git a/core/build.gradle b/core/build.gradle index 167c4234910..15a0300738f 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -18,6 +18,8 @@ shadowJar { mergeServiceFiles() + exclude 'org/newsclub/**' + [ 'META-INF/io.netty.versions.properties', 'META-INF/NOTICE', @@ -46,11 +48,13 @@ shadowJar { include(dependency('com.google.guava:.*')) include(dependency('io.netty:.*')) include(dependency('org.bouncycastle:.*')) - include(dependency('org.newsclub.*:.*')) include(dependency('org.zeroturnaround:zt-exec')) include(dependency('commons-lang:commons-lang')) include(dependency('commons-io:commons-io')) include(dependency('commons-codec:commons-codec')) + include(dependency('com.squareup.okhttp3:.*')) + include(dependency('com.squareup.okio:.*')) + include(dependency('org.scala-sbt.ipcsocket:ipcsocket')) } } @@ -84,12 +88,21 @@ dependencies { exclude(group: "log4j", module: "log4j") } + compile "net.java.dev.jna:jna-platform:4.5.1" + + shaded ('org.scala-sbt.ipcsocket:ipcsocket:1.0.0') { + exclude(group: "net.java.dev.jna") + } + shaded ('com.github.docker-java:docker-java:3.1.0-rc-3') { exclude(group: 'org.glassfish.jersey.core') exclude(group: 'org.glassfish.jersey.connectors') exclude(group: 'log4j') exclude(group: 'com.google.code.findbug') + exclude(group: 'com.kohlschutter.junixsocket') } + shaded 'com.squareup.okhttp3:okhttp:3.10.0' + shaded 'javax.ws.rs:javax.ws.rs-api:2.0.1' shaded 'org.zeroturnaround:zt-exec:1.8' shaded 'commons-lang:commons-lang:2.6' diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java index d9c1dd2ce58..bfeabc63f8a 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java @@ -52,6 +52,7 @@ public static String getDockerHostIpAddress(DockerClientConfig config) { case "tcp": return config.getDockerHost().getHost(); case "unix": + case "npipe": if (IN_A_CONTAINER) { return getDefaultGateway().orElse("localhost"); } diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index f078aeac5fc..117acece7db 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -13,6 +13,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testcontainers.dockerclient.transport.TestcontainersDockerCmdExecFactory; +import org.testcontainers.dockerclient.transport.okhttp.OkHttpDockerCmdExecFactory; import org.testcontainers.utility.TestcontainersConfiguration; import java.util.ArrayList; @@ -164,10 +165,23 @@ public DockerClient getClient() { } protected DockerClient getClientForConfig(DockerClientConfig config) { - return DockerClientBuilder - .getInstance(config) - .withDockerCmdExecFactory(new TestcontainersDockerCmdExecFactory()) - .build(); + DockerClientBuilder clientBuilder = DockerClientBuilder + .getInstance(config); + + String transportType = TestcontainersConfiguration.getInstance().getTransportType(); + if ("okhttp".equals(transportType)) { + clientBuilder + .withDockerCmdExecFactory(new OkHttpDockerCmdExecFactory()); + } else if ("netty".equals(transportType)) { + clientBuilder + .withDockerCmdExecFactory(new TestcontainersDockerCmdExecFactory()); + } else { + throw new IllegalArgumentException("Unknown transport type: " + transportType); + } + + LOGGER.info("Will use '{}' transport", transportType); + + return clientBuilder.build(); } protected void ping(DockerClient client, int timeoutInSeconds) { diff --git a/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java new file mode 100644 index 00000000000..b8d85f11623 --- /dev/null +++ b/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java @@ -0,0 +1,78 @@ +package org.testcontainers.dockerclient; + +import com.github.dockerjava.core.DefaultDockerClientConfig; +import com.github.dockerjava.core.DockerClientConfig; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Delegate; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.SystemUtils; +import org.jetbrains.annotations.NotNull; + +import java.net.URI; + +@Slf4j +public class NpipeSocketClientProviderStrategy extends DockerClientProviderStrategy { + + protected static final String DOCKER_SOCK_PATH = "//./pipe/docker_engine"; + private static final String SOCKET_LOCATION = "npipe://" + DOCKER_SOCK_PATH; + + private static final String PING_TIMEOUT_DEFAULT = "10"; + private static final String PING_TIMEOUT_PROPERTY_NAME = "testcontainers.npipesocketprovider.timeout"; + + public static final int PRIORITY = EnvironmentAndSystemPropertyClientProviderStrategy.PRIORITY - 20; + + @Override + protected boolean isApplicable() { + return SystemUtils.IS_OS_WINDOWS; + } + + @Override + public void test() throws InvalidConfigurationException { + try { + config = tryConfiguration(); + log.info("Accessing docker with {}", getDescription()); + } catch (Exception | UnsatisfiedLinkError e) { + throw new InvalidConfigurationException("ping failed", e); + } + } + + @NotNull + private DockerClientConfig tryConfiguration() { + URI dockerHost = URI.create(SOCKET_LOCATION); + + config = new DelegatingDockerClientConfig( + DefaultDockerClientConfig.createDefaultConfigBuilder() + .withDockerHost("tcp://localhost:0") + .withDockerTlsVerify(false) + .build() + ) { + @Override + public URI getDockerHost() { + return dockerHost; + } + }; + client = getClientForConfig(config); + + final int timeout = Integer.parseInt(System.getProperty(PING_TIMEOUT_PROPERTY_NAME, PING_TIMEOUT_DEFAULT)); + ping(client, timeout); + + return config; + } + + @Override + public String getDescription() { + return "local Npipe socket (" + SOCKET_LOCATION + ")"; + } + + @Override + protected int getPriority() { + return PRIORITY; + } + + @RequiredArgsConstructor + private static class DelegatingDockerClientConfig implements DockerClientConfig { + + @Delegate + final DockerClientConfig dockerClientConfig; + } +} diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/NamedPipeSocketFactory.java b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/NamedPipeSocketFactory.java new file mode 100644 index 00000000000..0071dea036d --- /dev/null +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/NamedPipeSocketFactory.java @@ -0,0 +1,79 @@ +package org.testcontainers.dockerclient.transport.okhttp; + +import lombok.SneakyThrows; +import lombok.Value; +import org.scalasbt.ipcsocket.Win32NamedPipeSocket; + +import javax.net.SocketFactory; +import java.io.FilterInputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketAddress; + +@Value +public class NamedPipeSocketFactory extends SocketFactory { + + String socketPath; + + @Override + @SneakyThrows + public Socket createSocket() { + return new Win32NamedPipeSocket(socketPath.replace("/", "\\")) { + + @Override + public void connect(SocketAddress endpoint, int timeout) throws IOException { + // Do nothing since it's not "connectable" + } + + @Override + public InputStream getInputStream() { + return new FilterInputStream(super.getInputStream()) { + @Override + public void close() throws IOException { + shutdownInput(); + } + }; + } + + @Override + public OutputStream getOutputStream() { + return new FilterOutputStream(super.getOutputStream()) { + + @Override + public void write(byte[] b, int off, int len) throws IOException { + out.write(b, off, len); + } + + @Override + public void close() throws IOException { + shutdownOutput(); + } + }; + } + }; + } + + @Override + public Socket createSocket(String s, int i) { + throw new UnsupportedOperationException(); + } + + @Override + public Socket createSocket(String s, int i, InetAddress inetAddress, int i1) { + throw new UnsupportedOperationException(); + } + + @Override + public Socket createSocket(InetAddress inetAddress, int i) { + throw new UnsupportedOperationException(); + } + + @Override + public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddress1, int i1) { + throw new UnsupportedOperationException(); + } +} diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpDockerCmdExecFactory.java b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpDockerCmdExecFactory.java new file mode 100644 index 00000000000..08e0dde046b --- /dev/null +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpDockerCmdExecFactory.java @@ -0,0 +1,153 @@ +package org.testcontainers.dockerclient.transport.okhttp; + +import com.github.dockerjava.api.command.PingCmd; +import com.github.dockerjava.core.AbstractDockerCmdExecFactory; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.core.SSLConfig; +import com.github.dockerjava.core.WebTarget; +import com.github.dockerjava.core.exec.PingCmdExec; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.MultimapBuilder; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import okhttp3.ConnectionPool; +import okhttp3.Dns; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.internal.Internal; +import org.apache.commons.io.IOUtils; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.X509TrustManager; +import java.io.IOException; +import java.net.InetAddress; +import java.net.URI; +import java.security.cert.X509Certificate; +import java.util.Collections; +import java.util.concurrent.TimeUnit; + +@Slf4j +public class OkHttpDockerCmdExecFactory extends AbstractDockerCmdExecFactory { + + private static final String SOCKET_SUFFIX = ".socket"; + + private OkHttpClient okHttpClient; + + private HttpUrl baseUrl; + + @Override + @SneakyThrows + public void init(DockerClientConfig dockerClientConfig) { + super.init(dockerClientConfig); + + OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder() + .readTimeout(0, TimeUnit.SECONDS) + .retryOnConnectionFailure(true); + + URI dockerHost = dockerClientConfig.getDockerHost(); + switch (dockerHost.getScheme()) { + case "unix": + case "npipe": + String socketPath = dockerHost.getPath(); + + if ("unix".equals(dockerHost.getScheme())) { + clientBuilder + .socketFactory(new UnixSocketFactory(socketPath)); + } else { + clientBuilder + .socketFactory(new NamedPipeSocketFactory(socketPath)); + } + + clientBuilder + // Disable pooling + .connectionPool(new ConnectionPool(0, 1, TimeUnit.SECONDS)) + .dns(hostname -> { + if (hostname.endsWith(SOCKET_SUFFIX)) { + return Collections.singletonList(InetAddress.getByAddress(hostname, new byte[]{0, 0, 0, 0})); + } else { + return Dns.SYSTEM.lookup(hostname); + } + }); + default: + } + + SSLConfig sslConfig = dockerClientConfig.getSSLConfig(); + if (sslConfig != null) { + SSLContext sslContext = sslConfig.getSSLContext(); + if (sslContext != null) { + clientBuilder + .sslSocketFactory(sslContext.getSocketFactory(), new TrustAllX509TrustManager()); + } + } + + okHttpClient = clientBuilder.build(); + + HttpUrl.Builder baseUrlBuilder; + + switch (dockerHost.getScheme()) { + case "unix": + case "npipe": + baseUrlBuilder = new HttpUrl.Builder() + .scheme("http") + .host("docker" + SOCKET_SUFFIX); + break; + case "tcp": + baseUrlBuilder = new HttpUrl.Builder() + .scheme(sslConfig != null && sslConfig.getSSLContext() != null ? "https" : "http") + .host(dockerHost.getHost()) + .port(dockerHost.getPort()); + break; + default: + baseUrlBuilder = Internal.instance.getHttpUrlChecked(dockerHost.toString()).newBuilder(); + } + baseUrl = baseUrlBuilder.build(); + } + + @Override + protected WebTarget getBaseResource() { + return new OkHttpWebTarget( + okHttpClient, + baseUrl, + ImmutableList.of(), + MultimapBuilder.hashKeys().hashSetValues().build() + ); + } + + @Override + public PingCmd.Exec createPingCmdExec() { + return new PingCmdExec(getBaseResource(), getDockerClientConfig()) { + + @Override + protected Void execute(PingCmd command) { + WebTarget webResource = getBaseResource().path("/_ping"); + + // TODO contribute to docker-java, make it close the stream + IOUtils.closeQuietly(webResource.request().get()); + + return null; + } + }; + } + + @Override + public void close() throws IOException { + + } + + private static class TrustAllX509TrustManager implements X509TrustManager { + @Override + public void checkClientTrusted(X509Certificate[] x509Certificates, String s) { + + } + + @Override + public void checkServerTrusted(X509Certificate[] x509Certificates, String s) { + + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + } +} diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java new file mode 100644 index 00000000000..17727aa3c16 --- /dev/null +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java @@ -0,0 +1,325 @@ +package org.testcontainers.dockerclient.transport.okhttp; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dockerjava.api.async.ResultCallback; +import com.github.dockerjava.api.exception.BadRequestException; +import com.github.dockerjava.api.exception.ConflictException; +import com.github.dockerjava.api.exception.DockerException; +import com.github.dockerjava.api.exception.InternalServerErrorException; +import com.github.dockerjava.api.exception.NotAcceptableException; +import com.github.dockerjava.api.exception.NotFoundException; +import com.github.dockerjava.api.exception.NotModifiedException; +import com.github.dockerjava.api.exception.UnauthorizedException; +import com.github.dockerjava.api.model.Frame; +import com.github.dockerjava.core.InvocationBuilder; +import com.github.dockerjava.netty.handler.FramedResponseStreamHandler; +import com.github.dockerjava.netty.handler.JsonResponseCallbackHandler; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.SimpleChannelInboundHandler; +import lombok.AccessLevel; +import lombok.SneakyThrows; +import lombok.experimental.FieldDefaults; +import lombok.extern.slf4j.Slf4j; +import okhttp3.HttpUrl; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.internal.connection.RealConnection; +import okio.BufferedSink; +import okio.BufferedSource; +import okio.Okio; +import okio.Source; + +import javax.annotation.Nullable; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.util.concurrent.atomic.AtomicBoolean; + +@Slf4j +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +class OkHttpInvocationBuilder implements InvocationBuilder { + + ObjectMapper objectMapper; + + OkHttpClient okHttpClient; + + Request.Builder requestBuilder; + + public OkHttpInvocationBuilder(ObjectMapper objectMapper, OkHttpClient okHttpClient, HttpUrl httpUrl) { + this.objectMapper = objectMapper; + this.okHttpClient = okHttpClient; + + requestBuilder = new Request.Builder() + .url(httpUrl); + } + + @Override + public OkHttpInvocationBuilder accept(com.github.dockerjava.core.MediaType mediaType) { + return header("accept", mediaType.getMediaType()); + } + + @Override + public OkHttpInvocationBuilder header(String name, String value) { + requestBuilder.header(name, value); + return this; + } + + @Override + @SneakyThrows + public void delete() { + Request request = requestBuilder + .delete() + .build(); + + execute(request).close(); + } + + @Override + @SneakyThrows + public void get(ResultCallback resultCallback) { + Request request = requestBuilder + .get() + .build(); + + executeAndStream( + request, + resultCallback, + new FramedResponseStreamHandler(resultCallback) + ); + } + + @Override + @SneakyThrows(IOException.class) + public T get(TypeReference typeReference) { + try (InputStream inputStream = get()) { + return objectMapper.readValue(inputStream, typeReference); + } + } + + @Override + public void get(TypeReference typeReference, ResultCallback resultCallback) { + // FIXME + throw new IllegalStateException("doesn't seem to be used in docker-java"); + } + + @Override + @SneakyThrows + public InputStream post(Object entity) { + Request request = requestBuilder + .post(RequestBody.create(null, objectMapper.writeValueAsBytes(entity))) + .build(); + + return execute(request).body().byteStream(); + } + + @Override + @SneakyThrows + public T post(Object entity, TypeReference typeReference) { + Request request = requestBuilder + .post(RequestBody.create(MediaType.parse("application/json"), objectMapper.writeValueAsBytes(entity))) + .build(); + + try (Response response = execute(request)) { + String inputStream = response.body().string(); + return objectMapper.readValue(inputStream, typeReference); + } + } + + @Override + @SneakyThrows(JsonProcessingException.class) + public void post(Object entity, TypeReference typeReference, ResultCallback resultCallback) { + post(typeReference, resultCallback, new ByteArrayInputStream(objectMapper.writeValueAsBytes(entity))); + } + + @Override + @SneakyThrows(IOException.class) + public T post(TypeReference typeReference, InputStream body) { + try (InputStream inputStream = post(body)) { + return objectMapper.readValue(inputStream, typeReference); + } + } + + @Override + @SneakyThrows + public void post(Object entity, InputStream stdin, ResultCallback resultCallback) { + Request request = requestBuilder + .post(RequestBody.create(MediaType.parse("application/json"), objectMapper.writeValueAsBytes(entity))) + .build(); + + OkHttpClient okHttpClient = this.okHttpClient; + + if (stdin != null) { + // FIXME there must be a better way of handling it + okHttpClient = okHttpClient.newBuilder() + .addNetworkInterceptor(chain -> { + Response response = chain.proceed(chain.request()); + if (response.isSuccessful()) { + Thread thread = new Thread() { + @Override + @SneakyThrows + public void run() { + Field sinkField = RealConnection.class.getDeclaredField("sink"); + sinkField.setAccessible(true); + + try ( + BufferedSink sink = (BufferedSink) sinkField.get(chain.connection()); + Source source = Okio.source(stdin); + ) { + sink.writeAll(source); + } + } + }; + thread.start(); + } + return response; + }) + .build(); + } + + executeAndStream( + okHttpClient, + request, + resultCallback, + new FramedResponseStreamHandler(resultCallback) + ); + } + + @Override + public void post(TypeReference typeReference, ResultCallback resultCallback, InputStream body) { + Request request = requestBuilder + .post(toRequestBody(body, null)) + .build(); + + executeAndStream( + request, + resultCallback, + new JsonResponseCallbackHandler<>(typeReference, resultCallback) + ); + } + + @Override + @SneakyThrows + public void postStream(InputStream body) { + Request request = requestBuilder + .post(toRequestBody(body, null)) + .build(); + + execute(request).close(); + } + + @Override + @SneakyThrows + public InputStream get() { + Request request = requestBuilder + .get() + .build(); + + return execute(request).body().byteStream(); + } + + @Override + @SneakyThrows + public void put(InputStream body, com.github.dockerjava.core.MediaType mediaType) { + Request request = requestBuilder + .put(toRequestBody(body, mediaType.toString())) + .build(); + + execute(request).close(); + } + + protected RequestBody toRequestBody(InputStream body, @Nullable String mediaType) { + return new RequestBody() { + @Nullable + @Override + public MediaType contentType() { + if (mediaType == null) { + return null; + } + return MediaType.parse(mediaType); + } + + @Override + public void writeTo(BufferedSink sink) throws IOException { + try(Source source = Okio.source(body)) { + sink.writeAll(source); + } + } + }; + } + + protected Response execute(Request request) { + return execute(okHttpClient, request); + } + + @SneakyThrows(IOException.class) + protected Response execute(OkHttpClient okHttpClient, Request request) { + Response response = okHttpClient.newCall(request).execute(); + if (!response.isSuccessful()) { + String body = response.body().string(); + switch (response.code()) { + case 304: + throw new NotModifiedException(body); + case 400: + throw new BadRequestException(body); + case 401: + throw new UnauthorizedException(body); + case 404: + throw new NotFoundException(body); + case 406: + throw new NotAcceptableException(body); + case 409: + throw new ConflictException(body); + case 500: + throw new InternalServerErrorException(body); + default: + throw new DockerException(body, response.code()); + } + } else { + return response; + } + } + + protected void executeAndStream(Request request, ResultCallback callback, SimpleChannelInboundHandler handler) { + executeAndStream(okHttpClient, request, callback, handler); + } + + protected void executeAndStream(OkHttpClient okHttpClient, Request request, ResultCallback callback, SimpleChannelInboundHandler handler) { + // TODO proper thread management + Thread thread = new Thread() { + @Override + @SneakyThrows + public void run() { + try ( + Response response = execute(okHttpClient, request.newBuilder().tag("streaming").build()); + BufferedSource source = response.body().source(); + InputStream inputStream = source.inputStream(); + ) { + AtomicBoolean shouldStop = new AtomicBoolean(); + callback.onStart(() -> { + shouldStop.set(true); + response.close(); + }); + + byte[] buffer = new byte[4 * 1024]; + while (!(shouldStop.get() || source.exhausted())) { + int bytesReceived = inputStream.read(buffer); + + handler.channelRead(null, Unpooled.wrappedBuffer(buffer, 0, bytesReceived)); + } + callback.onComplete(); + } catch (Exception e) { + callback.onError(e); + } + } + }; + + thread.start(); + } +} diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpWebTarget.java b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpWebTarget.java new file mode 100644 index 00000000000..85fe8c22288 --- /dev/null +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpWebTarget.java @@ -0,0 +1,113 @@ +package org.testcontainers.dockerclient.transport.okhttp; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dockerjava.core.InvocationBuilder; +import com.github.dockerjava.core.WebTarget; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.SetMultimap; +import lombok.SneakyThrows; +import lombok.Value; +import lombok.experimental.Wither; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import org.apache.commons.lang.StringUtils; + +import java.util.Collection; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +@Wither +@Value +class OkHttpWebTarget implements WebTarget { + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + OkHttpClient okHttpClient; + + HttpUrl baseUrl; + + ImmutableList path; + + SetMultimap queryParams; + + @Override + @SneakyThrows + public InvocationBuilder request() { + String resource = StringUtils.join(path, "/"); + + if (!resource.startsWith("/")) { + resource = "/" + resource; + } + + HttpUrl.Builder baseUrlBuilder = baseUrl.newBuilder() + .encodedPath(resource); + + for (Map.Entry> queryParamEntry : queryParams.asMap().entrySet()) { + String key = queryParamEntry.getKey(); + for (String paramValue : queryParamEntry.getValue()) { + baseUrlBuilder.addQueryParameter(key, paramValue); + } + } + + return new OkHttpInvocationBuilder( + MAPPER, + okHttpClient, + baseUrlBuilder.build() + ); + } + + @Override + public OkHttpWebTarget path(String... components) { + return this.withPath( + ImmutableList.builder() + .addAll(path) + .add(components) + .build() + ); + } + + @Override + public OkHttpWebTarget resolveTemplate(String name, Object value) { + ImmutableList.Builder newPath = ImmutableList.builder(); + for (String component : path) { + component = component.replaceAll("\\{" + name + "\\}", value.toString()); + newPath.add(component); + } + return this.withPath(newPath.build()); + } + + @Override + public OkHttpWebTarget queryParam(String name, Object value) { + if (value == null) { + return this; + } + + SetMultimap newQueryParams = HashMultimap.create(queryParams); + newQueryParams.put(name, value.toString()); + + return this.withQueryParams(newQueryParams); + } + + @Override + public OkHttpWebTarget queryParamsSet(String name, Set values) { + SetMultimap newQueryParams = HashMultimap.create(queryParams); + newQueryParams.replaceValues(name, values.stream().filter(Objects::nonNull).map(Object::toString).collect(Collectors.toSet())); + + return this.withQueryParams(newQueryParams); + } + + @Override + @SneakyThrows(JsonProcessingException.class) + public OkHttpWebTarget queryParamsJsonMap(String name, Map values) { + if (values == null || values.isEmpty()) { + return this; + } + + // when param value is JSON string + return queryParam(name, MAPPER.writeValueAsString(values)); + } +} diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/UnixSocketFactory.java b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/UnixSocketFactory.java new file mode 100644 index 00000000000..c23575c2307 --- /dev/null +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/UnixSocketFactory.java @@ -0,0 +1,78 @@ +package org.testcontainers.dockerclient.transport.okhttp; + +import lombok.SneakyThrows; +import lombok.Value; +import org.scalasbt.ipcsocket.UnixDomainSocket; + +import javax.net.SocketFactory; +import java.io.FilterInputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketAddress; + +@Value +public class UnixSocketFactory extends SocketFactory { + + String socketPath; + + @Override + @SneakyThrows + public Socket createSocket() { + return new UnixDomainSocket(socketPath) { + @Override + public void connect(SocketAddress endpoint, int timeout) throws IOException { + // Do nothing since it's not "connectable" + } + + @Override + public InputStream getInputStream() { + return new FilterInputStream(super.getInputStream()) { + @Override + public void close() throws IOException { + shutdownInput(); + } + }; + } + + @Override + public OutputStream getOutputStream() { + return new FilterOutputStream(super.getOutputStream()) { + + @Override + public void write(byte[] b, int off, int len) throws IOException { + out.write(b, off, len); + } + + @Override + public void close() throws IOException { + shutdownOutput(); + } + }; + } + }; + } + + @Override + public Socket createSocket(String s, int i) { + throw new UnsupportedOperationException(); + } + + @Override + public Socket createSocket(String s, int i, InetAddress inetAddress, int i1) { + throw new UnsupportedOperationException(); + } + + @Override + public Socket createSocket(InetAddress inetAddress, int i) { + throw new UnsupportedOperationException(); + } + + @Override + public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddress1, int i1) { + throw new UnsupportedOperationException(); + } +} diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 39c756a31b8..567970bc6fc 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -70,6 +70,10 @@ public String getDockerClientStrategyClassName() { return (String) properties.get("docker.client.strategy"); } + public String getTransportType() { + return properties.getProperty("transport.type", "netty"); + } + @Synchronized public boolean updateGlobalConfig(@NonNull String prop, @NonNull String value) { try { diff --git a/core/src/main/resources/META-INF/services/org.testcontainers.dockerclient.DockerClientProviderStrategy b/core/src/main/resources/META-INF/services/org.testcontainers.dockerclient.DockerClientProviderStrategy index 9c8db41a429..25bbb30d725 100644 --- a/core/src/main/resources/META-INF/services/org.testcontainers.dockerclient.DockerClientProviderStrategy +++ b/core/src/main/resources/META-INF/services/org.testcontainers.dockerclient.DockerClientProviderStrategy @@ -2,4 +2,5 @@ org.testcontainers.dockerclient.EnvironmentAndSystemPropertyClientProviderStrate org.testcontainers.dockerclient.UnixSocketClientProviderStrategy org.testcontainers.dockerclient.ProxiedUnixSocketClientProviderStrategy org.testcontainers.dockerclient.DockerMachineClientProviderStrategy -org.testcontainers.dockerclient.WindowsClientProviderStrategy \ No newline at end of file +org.testcontainers.dockerclient.WindowsClientProviderStrategy +org.testcontainers.dockerclient.NpipeSocketClientProviderStrategy diff --git a/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java b/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java index 70c366e3c80..b73bfeed46e 100644 --- a/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java +++ b/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java @@ -29,14 +29,17 @@ public void simpleRecursiveFileTest() throws TimeoutException { final ToStringConsumer toString = new ToStringConsumer(); + // 'src' is expected to be the project base directory, so all source code/resources should be copied in + File directory = new File("src"); + GenericContainer container = new GenericContainer( new ImageFromDockerfile() .withDockerfileFromBuilder(builder -> builder.from("alpine:3.3") .copy("/tmp/foo", "/foo") - .cmd("cat /foo/src/test/resources/test-recursive-file.txt") + .cmd("cat /foo/test/resources/test-recursive-file.txt") .build() - ).withFileFromFile("/tmp/foo", new File("."))) // '.' is expected to be the project base directory, so all source code/resources should be copied in + ).withFileFromFile("/tmp/foo", directory)) .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) .withLogConsumer(wait.andThen(toString)); diff --git a/core/src/test/resources/logback-test.xml b/core/src/test/resources/logback-test.xml index ed0e5b2659e..42a23f5fe13 100644 --- a/core/src/test/resources/logback-test.xml +++ b/core/src/test/resources/logback-test.xml @@ -8,22 +8,20 @@ - + + + - - - - PROFILER DENY - \ No newline at end of file + From 85b76d7e199940dc6038f4255287776b01744ed3 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 13 Jun 2018 22:47:41 +0200 Subject: [PATCH 0337/5615] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a43956e9b03..eaaf00f74ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ All notable changes to this project will be documented in this file. - Add support for Dockerfile based images to OracleContainer ([\#734](https://github.com/testcontainers/testcontainers-java/pull/734)) - Read from both `/proc/net/tcp` and `/proc/net/tcp6` in `InternalCommandPortListeningCheck` ([\#750](https://github.com/testcontainers/testcontainers-java/pull/750)) - Added builder methods for timeouts in `JdbcDatabaseContainer` ([\#748](https://github.com/testcontainers/testcontainers-java/pull/748)) +- Added an alternative experimental transport based on OkHttp. Enable it with `transport.type=okhttp` property ([\#710](https://github.com/testcontainers/testcontainers-java/pull/710)) ## [1.7.3] - 2018-05-16 From b333e29211b5d1e5e4e0475fd80c1b49cd5c98a0 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 13 Jun 2018 23:03:28 +0200 Subject: [PATCH 0338/5615] Framework-agnostic container & test lifecycle (#702) * Implement framework-agnostic container & test lifecycle * Simpliy TestDescription interface * {before,after}TestBlock -> {before,after}Test --- .../containers/DockerComposeContainer.java | 39 +++++++-- .../containers/GenericContainer.java | 80 ++++++++++++++++--- .../testcontainers/lifecycle/Startable.java | 13 +++ .../lifecycle/TestDescription.java | 8 ++ .../lifecycle/TestLifecycleAware.java | 14 ++++ .../containers/KafkaContainer.java | 4 +- .../containers/BrowserWebDriverContainer.java | 21 +++-- .../DefaultRecordingFileFactory.java | 15 ++-- .../containers/RecordingFileFactory.java | 8 +- 9 files changed, 158 insertions(+), 44 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/lifecycle/Startable.java create mode 100644 core/src/main/java/org/testcontainers/lifecycle/TestDescription.java create mode 100644 core/src/main/java/org/testcontainers/lifecycle/TestLifecycleAware.java diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index f6b8a647bd8..a206652637e 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -2,7 +2,6 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.model.Container; -import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.base.Splitter; import com.google.common.collect.Maps; @@ -11,6 +10,7 @@ import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.SystemUtils; import org.junit.runner.Description; +import org.junit.runners.model.Statement; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.profiler.Profiler; @@ -21,6 +21,7 @@ import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.containers.wait.strategy.WaitAllStrategy; import org.testcontainers.containers.wait.strategy.WaitStrategy; +import org.testcontainers.lifecycle.Startable; import org.testcontainers.utility.*; import org.zeroturnaround.exec.InvalidExitValueException; import org.zeroturnaround.exec.ProcessExecutor; @@ -54,7 +55,7 @@ /** * Container which launches Docker Compose, for the purposes of launching a defined set of containers. */ -public class DockerComposeContainer> extends FailureDetectingExternalResource { +public class DockerComposeContainer> extends FailureDetectingExternalResource implements Startable { /** * Random identifier which will become part of spawned containers names, so we can shut them down @@ -115,8 +116,35 @@ public DockerComposeContainer(String identifier, List composeFiles) { } @Override - @VisibleForTesting + @Deprecated + public Statement apply(Statement base, Description description) { + return super.apply(base, description); + } + + @Override + @Deprecated public void starting(Description description) { + start(); + } + + @Override + @Deprecated + protected void succeeded(Description description) { + } + + @Override + @Deprecated + protected void failed(Throwable e, Description description) { + } + + @Override + @Deprecated + public void finished(Description description) { + stop(); + } + + @Override + public void start() { final Profiler profiler = new Profiler("Docker Compose container rule"); profiler.setLogger(logger()); profiler.start("Docker Compose container startup"); @@ -227,10 +255,7 @@ private Logger logger() { } @Override - @VisibleForTesting - public void finished(Description description) { - - + public void stop() { synchronized (MUTEX) { try { // shut down the ambassador container diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 9ec38a2f2bf..5df1a065d59 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -21,6 +21,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.junit.runner.Description; +import org.junit.runners.model.Statement; import org.rnorth.ducttape.ratelimits.RateLimiter; import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; import org.rnorth.ducttape.unreliables.Unreliables; @@ -38,6 +39,9 @@ import org.testcontainers.containers.wait.WaitStrategy; import org.testcontainers.containers.wait.strategy.WaitStrategyTarget; import org.testcontainers.images.RemoteDockerImage; +import org.testcontainers.lifecycle.Startable; +import org.testcontainers.lifecycle.TestLifecycleAware; +import org.testcontainers.lifecycle.TestDescription; import org.testcontainers.utility.*; import java.io.File; @@ -75,7 +79,7 @@ @EqualsAndHashCode(callSuper = false) public class GenericContainer> extends FailureDetectingExternalResource - implements Container, AutoCloseable, WaitStrategyTarget { + implements Container, AutoCloseable, WaitStrategyTarget, Startable { private static final Charset UTF8 = Charset.forName("UTF-8"); @@ -195,7 +199,15 @@ public GenericContainer(@NonNull final Future image) { /** * Starts the container using docker, pulling an image if necessary. */ + @Override public void start() { + if (containerId != null) { + return; + } + doStart(); + } + + protected void doStart() { Profiler profiler = new Profiler("Container startup"); profiler.setLogger(logger()); @@ -288,21 +300,27 @@ private void tryStart(Profiler profiler) { /** * Stops the container. */ + @Override public void stop() { if (containerId == null) { return; } - String imageName; - try { - imageName = image.get(); - } catch (Exception e) { - imageName = ""; - } + String imageName; + + try { + imageName = image.get(); + } catch (Exception e) { + imageName = ""; + } - ResourceReaper.instance().stopAndRemoveContainer(containerId, imageName); + ResourceReaper.instance().stopAndRemoveContainer(containerId, imageName); + } finally { + containerId = null; + containerInfo = null; + } } /** @@ -640,12 +658,53 @@ public void addExposedPorts(int... ports) { } } + private TestDescription toDescription(Description description) { + return new TestDescription() { + @Override + public String getTestId() { + return description.getDisplayName(); + } + + @Override + public String getFilesystemFriendlyName() { + return description.getClassName() + "-" + description.getMethodName(); + } + }; + } + @Override + @Deprecated + public Statement apply(Statement base, Description description) { + return super.apply(base, description); + } + + @Override + @Deprecated protected void starting(Description description) { + if (this instanceof TestLifecycleAware) { + ((TestLifecycleAware) this).beforeTest(toDescription(description)); + } this.start(); } @Override + @Deprecated + protected void succeeded(Description description) { + if (this instanceof TestLifecycleAware) { + ((TestLifecycleAware) this).afterTest(toDescription(description), Optional.empty()); + } + } + + @Override + @Deprecated + protected void failed(Throwable e, Description description) { + if (this instanceof TestLifecycleAware) { + ((TestLifecycleAware) this).afterTest(toDescription(description), Optional.of(e)); + } + } + + @Override + @Deprecated protected void finished(Description description) { this.stop(); } @@ -989,11 +1048,6 @@ public SELF withStartupAttempts(int attempts) { return self(); } - @Override - public void close() { - stop(); - } - /** * Allow low level modifications of {@link CreateContainerCmd} after it was pre-configured in {@link #tryStart(Profiler)}. * Invocation happens eagerly on a moment when container is created. diff --git a/core/src/main/java/org/testcontainers/lifecycle/Startable.java b/core/src/main/java/org/testcontainers/lifecycle/Startable.java new file mode 100644 index 00000000000..fa2bfd9dfc5 --- /dev/null +++ b/core/src/main/java/org/testcontainers/lifecycle/Startable.java @@ -0,0 +1,13 @@ +package org.testcontainers.lifecycle; + +public interface Startable extends AutoCloseable { + + void start(); + + void stop(); + + @Override + default void close() { + stop(); + } +} diff --git a/core/src/main/java/org/testcontainers/lifecycle/TestDescription.java b/core/src/main/java/org/testcontainers/lifecycle/TestDescription.java new file mode 100644 index 00000000000..c27f59fb0fc --- /dev/null +++ b/core/src/main/java/org/testcontainers/lifecycle/TestDescription.java @@ -0,0 +1,8 @@ +package org.testcontainers.lifecycle; + +public interface TestDescription { + + String getTestId(); + + String getFilesystemFriendlyName(); +} diff --git a/core/src/main/java/org/testcontainers/lifecycle/TestLifecycleAware.java b/core/src/main/java/org/testcontainers/lifecycle/TestLifecycleAware.java new file mode 100644 index 00000000000..178e081c083 --- /dev/null +++ b/core/src/main/java/org/testcontainers/lifecycle/TestLifecycleAware.java @@ -0,0 +1,14 @@ +package org.testcontainers.lifecycle; + +import java.util.Optional; + +public interface TestLifecycleAware { + + default void beforeTest(TestDescription description) { + + } + + default void afterTest(TestDescription description, Optional throwable) { + + } +} diff --git a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java index 9a222c7e974..e202c55cf9e 100644 --- a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java +++ b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java @@ -57,7 +57,7 @@ public String getBootstrapServers() { } @Override - public void start() { + protected void doStart() { String networkAlias = getNetworkAliases().get(0); proxy = new SocatContainer() .withNetwork(getNetwork()) @@ -76,7 +76,7 @@ public void start() { withCommand("sh", "-c", "zookeeper-server-start /zookeeper.properties & /etc/confluent/docker/run"); } - super.start(); + super.doStart(); } @Override diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index 9a527d998dd..fcc8b6f9ae2 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -4,7 +4,6 @@ import com.google.common.collect.ImmutableSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.junit.runner.Description; import org.openqa.selenium.remote.BrowserType; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.remote.RemoteWebDriver; @@ -18,11 +17,14 @@ import org.testcontainers.containers.wait.LogMessageWaitStrategy; import org.testcontainers.containers.wait.WaitAllStrategy; import org.testcontainers.containers.wait.WaitStrategy; +import org.testcontainers.lifecycle.TestDescription; +import org.testcontainers.lifecycle.TestLifecycleAware; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.time.Duration; +import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -33,7 +35,7 @@ *

* The container should expose Selenium remote control protocol and VNC. */ -public class BrowserWebDriverContainer> extends GenericContainer implements VncService, LinkableContainer { +public class BrowserWebDriverContainer> extends GenericContainer implements VncService, LinkableContainer, TestLifecycleAware { private static final String CHROME_IMAGE = "selenium/standalone-chrome-debug:%s"; private static final String FIREFOX_IMAGE = "selenium/standalone-firefox-debug:%s"; @@ -203,13 +205,8 @@ public RemoteWebDriver getWebDriver() { } @Override - protected void failed(Throwable e, Description description) { - stopAndRetainRecordingForDescriptionAndSuccessState(description, false); - } - - @Override - protected void succeeded(Description description) { - stopAndRetainRecordingForDescriptionAndSuccessState(description, true); + public void afterTest(TestDescription description, Optional throwable) { + retainRecordingIfNeeded(description.getFilesystemFriendlyName(), throwable.isPresent()); } @Override @@ -233,7 +230,7 @@ public void stop() { super.stop(); } - private void stopAndRetainRecordingForDescriptionAndSuccessState(Description description, boolean succeeded) { + private void retainRecordingIfNeeded(String prefix, boolean succeeded) { final boolean shouldRecord; switch (recordingMode) { case RECORD_ALL: @@ -248,8 +245,8 @@ private void stopAndRetainRecordingForDescriptionAndSuccessState(Description des } if (shouldRecord) { - File recordingFile = recordingFileFactory.recordingFileForTest(vncRecordingDirectory, description, succeeded); - LOGGER.info("Screen recordings for test {} will be stored at: {}", description.getDisplayName(), recordingFile); + File recordingFile = recordingFileFactory.recordingFileForTest(vncRecordingDirectory, prefix, succeeded); + LOGGER.info("Screen recordings for test {} will be stored at: {}", prefix, recordingFile); vncRecordingContainer.saveRecordingToFile(recordingFile); } diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/DefaultRecordingFileFactory.java b/modules/selenium/src/main/java/org/testcontainers/containers/DefaultRecordingFileFactory.java index b68b03d46a9..64e8bbe7055 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/DefaultRecordingFileFactory.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/DefaultRecordingFileFactory.java @@ -1,7 +1,5 @@ package org.testcontainers.containers; -import org.junit.runner.Description; - import java.io.File; import java.text.SimpleDateFormat; import java.util.Date; @@ -11,16 +9,15 @@ public class DefaultRecordingFileFactory implements RecordingFileFactory { private static final SimpleDateFormat filenameDateFormat = new SimpleDateFormat("YYYYMMdd-HHmmss"); private static final String PASSED = "PASSED"; private static final String FAILED = "FAILED"; - private static final String FILENAME_FORMAT = "%s-%s-%s-%s.flv"; + private static final String FILENAME_FORMAT = "%s-%s-%s.flv"; @Override - public File recordingFileForTest(File vncRecordingDirectory, Description description, boolean succeeded) { - final String prefix = succeeded ? PASSED : FAILED; + public File recordingFileForTest(File vncRecordingDirectory, String prefix, boolean succeeded) { + final String resultMarker = succeeded ? PASSED : FAILED; final String fileName = String.format(FILENAME_FORMAT, - prefix, - description.getTestClass().getSimpleName(), - description.getMethodName(), - filenameDateFormat.format(new Date()) + resultMarker, + prefix, + filenameDateFormat.format(new Date()) ); return new File(vncRecordingDirectory, fileName); } diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/RecordingFileFactory.java b/modules/selenium/src/main/java/org/testcontainers/containers/RecordingFileFactory.java index cbd91562301..2cbd373493b 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/RecordingFileFactory.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/RecordingFileFactory.java @@ -5,5 +5,11 @@ import java.io.File; public interface RecordingFileFactory { - File recordingFileForTest(File vncRecordingDirectory, Description description, boolean succeeded); + + @Deprecated + default File recordingFileForTest(File vncRecordingDirectory, Description description, boolean succeeded) { + return recordingFileForTest(vncRecordingDirectory, description.getTestClass().getSimpleName() + "-" + description.getMethodName(), succeeded); + } + + File recordingFileForTest(File vncRecordingDirectory, String prefix, boolean succeeded); } From 893d0e8d8c185460fe7df5d38f485ca0ee2d5bd0 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 13 Jun 2018 23:04:00 +0200 Subject: [PATCH 0339/5615] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eaaf00f74ab..6a4565f28a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ All notable changes to this project will be documented in this file. - Read from both `/proc/net/tcp` and `/proc/net/tcp6` in `InternalCommandPortListeningCheck` ([\#750](https://github.com/testcontainers/testcontainers-java/pull/750)) - Added builder methods for timeouts in `JdbcDatabaseContainer` ([\#748](https://github.com/testcontainers/testcontainers-java/pull/748)) - Added an alternative experimental transport based on OkHttp. Enable it with `transport.type=okhttp` property ([\#710](https://github.com/testcontainers/testcontainers-java/pull/710)) +- Framework-agnostic container & test lifecycle ([\#702](https://github.com/testcontainers/testcontainers-java/pull/702)) ## [1.7.3] - 2018-05-16 From 9045db60b5a1179917c67fa371efe7355dda1308 Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 13 Jun 2018 22:49:56 +0100 Subject: [PATCH 0340/5615] Update README.md (#751) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 54d96bb5384..3734cd2e1ae 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,9 @@ See [LICENSE](LICENSE). ## Copyright -Copyright (c) 2015, 2016 Richard North and other authors. +Copyright (c) 2015 - 2018 Richard North and other authors. -MS SQL Server module is (c) 2017 - 2019 G DATA Software AG and other authors. +MS SQL Server module is (c) 2017 - 2018 G DATA Software AG and other authors. Hashicorp Vault module is (c) 2017 Capital One Services, LLC and other authors. From 2b230650263ba8847857cf731a777e440b45279d Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 14 Jun 2018 11:53:16 +0200 Subject: [PATCH 0341/5615] Fix Kafka's inner communication bug introduced in #733 (#752) --- .../main/java/org/testcontainers/containers/KafkaContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java index e202c55cf9e..9c914faf8d3 100644 --- a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java +++ b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java @@ -65,7 +65,7 @@ protected void doStart() { .withTarget(ZOOKEEPER_PORT, networkAlias); proxy.start(); - withEnv("KAFKA_ADVERTISED_LISTENERS", "BROKER://" + networkAlias + ":" + KAFKA_PORT +",PLAINTEXT://" + proxy.getContainerIpAddress() + ":" + proxy.getFirstMappedPort()); + withEnv("KAFKA_ADVERTISED_LISTENERS", "BROKER://" + networkAlias + ":9092" + "," + getBootstrapServers()); if (externalZookeeperConnect != null) { withEnv("KAFKA_ZOOKEEPER_CONNECT", externalZookeeperConnect); From 89b60a3daf1581dbfa9c075791ca11782a23ac38 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 14 Jun 2018 13:44:11 +0100 Subject: [PATCH 0342/5615] Update changelog for 1.8.0 release --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a4565f28a9..f49c8b5b753 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to this project will be documented in this file. ## UNRELEASED +### Fixed +### Changed + +## [1.8.0] - 2018-06-14 ### Fixed - Fixed JDBC URL Regex Pattern to ensure all supported Database URL's are accepted ([\#596](https://github.com/testcontainers/testcontainers-java/issues/596)) From 6a83e85766dc196f2c673aecfe9630bc8b530c36 Mon Sep 17 00:00:00 2001 From: Stanislav Date: Sat, 16 Jun 2018 22:21:49 +0300 Subject: [PATCH 0343/5615] Put all docker compose files into COMPOSE_FILE environment variable (#755) --- .../containers/DockerComposeContainer.java | 15 +++- .../junit/DockerComposeDoNotOverrideTest.java | 67 --------------- .../junit/DockerComposeLocalOverrideTest.java | 0 .../junit/DockerComposeOverrideTest.java | 64 --------------- .../junit/DockerComposeOverridesTest.java | 82 +++++++++++++++++++ 5 files changed, 94 insertions(+), 134 deletions(-) delete mode 100644 core/src/test/java/org/testcontainers/junit/DockerComposeDoNotOverrideTest.java create mode 100644 core/src/test/java/org/testcontainers/junit/DockerComposeLocalOverrideTest.java delete mode 100644 core/src/test/java/org/testcontainers/junit/DockerComposeOverrideTest.java create mode 100644 core/src/test/java/org/testcontainers/junit/DockerComposeOverridesTest.java diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index a206652637e..c98d092029b 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -48,9 +48,11 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; import static org.testcontainers.containers.BindMode.READ_ONLY; import static org.testcontainers.containers.BindMode.READ_WRITE; +import static org.testcontainers.containers.ContainerisedDockerCompose.UNIX_PATH_SEPERATOR; /** * Container which launches Docker Compose, for the purposes of launching a defined set of containers. @@ -623,9 +625,16 @@ public void invoke() { final Map environment = Maps.newHashMap(env); environment.put(ENV_PROJECT_NAME, identifier); - final File dockerComposeBaseFile = composeFiles.get(0); - final File pwd = dockerComposeBaseFile.getAbsoluteFile().getParentFile().getAbsoluteFile(); - environment.put(ENV_COMPOSE_FILE, new File(pwd, dockerComposeBaseFile.getAbsoluteFile().getName()).getAbsolutePath()); + + final List absoluteDockerComposeFiles = composeFiles.stream() + .map(File::getAbsolutePath) + .map(MountableFile::forHostPath) + .map(MountableFile::getFilesystemPath) + .collect(toList()); + final String composeFileEnvVariableValue = absoluteDockerComposeFiles.stream().collect(joining(UNIX_PATH_SEPERATOR + "")); // we always need the UNIX path separator + logger().debug("Set env COMPOSE_FILE={}", composeFileEnvVariableValue); + final File pwd = composeFiles.get(0).getAbsoluteFile().getParentFile().getAbsoluteFile(); + environment.put(ENV_COMPOSE_FILE, composeFileEnvVariableValue); logger().info("Local Docker Compose is running command: {}", cmd); diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeDoNotOverrideTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeDoNotOverrideTest.java deleted file mode 100644 index 0037f48b6d6..00000000000 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeDoNotOverrideTest.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.testcontainers.junit; - -import com.google.common.util.concurrent.Uninterruptibles; -import org.junit.Assume; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.rnorth.ducttape.unreliables.Unreliables; -import org.testcontainers.containers.DockerComposeContainer; -import org.testcontainers.utility.TestEnvironment; - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.Socket; -import java.util.concurrent.TimeUnit; - -import static org.rnorth.visibleassertions.VisibleAssertions.info; -import static org.rnorth.visibleassertions.VisibleAssertions.pass; - -/** - * Created by rnorth on 11/06/2016. - */ -public class DockerComposeDoNotOverrideTest { - - static final String DOCKER_COMPOSE_OVERRIDE_TEST_BASE_YML = "src/test/resources/docker-compose-base.yml"; - static final String DOCKER_COMPOSE_OVERRIDE_TEST_BASE_ENV = "bar=base"; - - static final String DOCKER_COMPOSE_OVERRIDE_TEST_OVERRIDE_YML = "src/test/resources/docker-compose-non-default-override.yml"; - static final String DOCKER_COMPOSE_OVERRIDE_TEST_OVERRIDE_ENV = "bar=overwritten"; - - @Rule - public DockerComposeContainer compose = - new DockerComposeContainer(new File(DOCKER_COMPOSE_OVERRIDE_TEST_BASE_YML)) - .withExposedService("alpine_1", 3000); - - @BeforeClass - public static void checkVersion() { - Assume.assumeTrue(TestEnvironment.dockerApiAtLeast("1.22")); - } - - - @Test(timeout = 30_000) - public void testEnvVar() throws IOException { - BufferedReader br = Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, () -> { - Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); - - Socket socket = new Socket(compose.getServiceHost("alpine_1", 3000), compose.getServicePort("alpine_1", 3000)); - return new BufferedReader(new InputStreamReader(socket.getInputStream())); - }); - - Unreliables.retryUntilTrue(10, TimeUnit.SECONDS, () -> { - while (br.ready()) { - String line = br.readLine(); - if (line.contains(DOCKER_COMPOSE_OVERRIDE_TEST_BASE_ENV)) { - pass("Mapped environment variable was found"); - return true; - } - } - info("Mapped environment variable was not found yet - process probably not ready"); - Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); - return false; - }); - - } -} diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeLocalOverrideTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeLocalOverrideTest.java new file mode 100644 index 00000000000..e69de29bb2d diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeOverrideTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeOverrideTest.java deleted file mode 100644 index 8d72d8d2533..00000000000 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeOverrideTest.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.testcontainers.junit; - -import com.google.common.util.concurrent.Uninterruptibles; -import org.junit.Assume; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.rnorth.ducttape.unreliables.Unreliables; -import org.testcontainers.containers.DockerComposeContainer; -import org.testcontainers.utility.TestEnvironment; - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.Socket; -import java.util.concurrent.TimeUnit; - -import static org.rnorth.visibleassertions.VisibleAssertions.info; -import static org.rnorth.visibleassertions.VisibleAssertions.pass; -import static org.testcontainers.junit.DockerComposeDoNotOverrideTest.*; - -/** - * Created by rnorth on 11/06/2016. - */ -public class DockerComposeOverrideTest { - - @Rule - public DockerComposeContainer compose = - new DockerComposeContainer( - new File(DOCKER_COMPOSE_OVERRIDE_TEST_BASE_YML), - new File(DOCKER_COMPOSE_OVERRIDE_TEST_OVERRIDE_YML)) - .withExposedService("alpine_1", 3000); - - @BeforeClass - public static void checkVersion() { - Assume.assumeTrue(TestEnvironment.dockerApiAtLeast("1.22")); - } - - - @Test(timeout = 30_000) - public void testEnvVar() throws IOException { - BufferedReader br = Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, () -> { - Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); - - Socket socket = new Socket(compose.getServiceHost("alpine_1", 3000), compose.getServicePort("alpine_1", 3000)); - return new BufferedReader(new InputStreamReader(socket.getInputStream())); - }); - - Unreliables.retryUntilTrue(10, TimeUnit.SECONDS, () -> { - while (br.ready()) { - String line = br.readLine(); - if (line.contains(DOCKER_COMPOSE_OVERRIDE_TEST_OVERRIDE_ENV)) { - pass("Mapped environment variable was found"); - return true; - } - } - info("Mapped environment variable was not found yet - process probably not ready"); - Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); - return false; - }); - - } -} diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeOverridesTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeOverridesTest.java new file mode 100644 index 00000000000..6413694d1c5 --- /dev/null +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeOverridesTest.java @@ -0,0 +1,82 @@ +package org.testcontainers.junit; + +import com.google.common.util.concurrent.Uninterruptibles; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import org.rnorth.ducttape.unreliables.Unreliables; +import org.testcontainers.containers.DockerComposeContainer; + +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStreamReader; +import java.net.Socket; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; + +import static org.rnorth.visibleassertions.VisibleAssertions.info; +import static org.rnorth.visibleassertions.VisibleAssertions.pass; + +@RunWith(Parameterized.class) +public class DockerComposeOverridesTest { + + private static final File BASE_COMPOSE_FILE = new File("src/test/resources/docker-compose-base.yml"); + private static final String BASE_ENV_VAR = "bar=base"; + private static final File OVERRIDE_COMPOSE_FILE = new File("src/test/resources/docker-compose-non-default-override.yml"); + private static final String OVERRIDE_ENV_VAR = "bar=overwritten"; + private static final int SERVICE_PORT = 3000; + private static final String SERVICE_NAME = "alpine_1"; + + private final boolean localMode; + private final String expectedEnvVar; + private final File[] composeFiles; + + public DockerComposeOverridesTest(boolean localMode, String expectedEnvVar, File... composeFiles) { + this.localMode = localMode; + this.expectedEnvVar = expectedEnvVar; + this.composeFiles = composeFiles; + } + + @Parameters(name = "{index}: local[{0}], composeFiles[{2}], expectedEnvVar[{1}]") + public static Iterable data() { + return Arrays.asList(new Object[][]{ + {true, BASE_ENV_VAR, new File[]{BASE_COMPOSE_FILE}}, + {true, OVERRIDE_ENV_VAR, new File[]{BASE_COMPOSE_FILE, OVERRIDE_COMPOSE_FILE}}, + {false, BASE_ENV_VAR, new File[]{BASE_COMPOSE_FILE}}, + {false, OVERRIDE_ENV_VAR, new File[]{BASE_COMPOSE_FILE, OVERRIDE_COMPOSE_FILE}}, + }); + } + + @Test + public void test() { + try (DockerComposeContainer compose = + new DockerComposeContainer(composeFiles) + .withLocalCompose(localMode) + .withExposedService(SERVICE_NAME, SERVICE_PORT)) { + + compose.start(); + + BufferedReader br = Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, () -> { + Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); + + Socket socket = new Socket(compose.getServiceHost(SERVICE_NAME, SERVICE_PORT), compose.getServicePort(SERVICE_NAME, SERVICE_PORT)); + return new BufferedReader(new InputStreamReader(socket.getInputStream())); + }); + + Unreliables.retryUntilTrue(10, TimeUnit.SECONDS, () -> { + while (br.ready()) { + String line = br.readLine(); + if (line.contains(expectedEnvVar)) { + pass("Mapped environment variable was found"); + return true; + } + } + info("Mapped environment variable was not found yet - process probably not ready"); + Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); + return false; + }); + } + } + +} From 92266b2a5a8130dac7716448562fbaa92008a92c Mon Sep 17 00:00:00 2001 From: Stanislav Baiduzhyi Date: Sat, 16 Jun 2018 21:23:14 +0200 Subject: [PATCH 0344/5615] Credential helpers (#729) See also https://github.com/testcontainers/testcontainers-java/pull/647 for previous discussions. --- .gitignore | 2 + CHANGELOG.md | 4 + circle.yml | 8 + core/build.gradle | 2 +- .../images/RemoteDockerImage.java | 6 + .../utility/DockerImageName.java | 10 +- .../utility/RegistryAuthLocator.java | 184 ++++++++++++++++++ .../dockerclient/ImagePullTest.java | 2 +- .../utility/RegistryAuthLocatorTest.java | 86 ++++++++ .../config-empty-auth-with-helper.json | 10 + .../resources/auth-config/config-empty.json | 5 + .../config-existing-auth-with-helper.json | 14 ++ .../config-with-helper-and-store.json | 12 ++ .../auth-config/config-with-helper.json | 11 ++ .../auth-config/config-with-store.json | 9 + .../auth-config/docker-credential-fake | 13 ++ 16 files changed, 375 insertions(+), 3 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java create mode 100644 core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java create mode 100644 core/src/test/resources/auth-config/config-empty-auth-with-helper.json create mode 100644 core/src/test/resources/auth-config/config-empty.json create mode 100644 core/src/test/resources/auth-config/config-existing-auth-with-helper.json create mode 100644 core/src/test/resources/auth-config/config-with-helper-and-store.json create mode 100644 core/src/test/resources/auth-config/config-with-helper.json create mode 100644 core/src/test/resources/auth-config/config-with-store.json create mode 100755 core/src/test/resources/auth-config/docker-credential-fake diff --git a/.gitignore b/.gitignore index 4c3f24f5bb4..c95a6ece0cd 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,8 @@ node_modules/ .gradle/ build/ +out/ +*.class # Eclipse IDE files **/.project diff --git a/CHANGELOG.md b/CHANGELOG.md index f49c8b5b753..9cb73ecbed5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,7 +42,10 @@ All notable changes to this project will be documented in this file. ## [1.7.2] - 2018-04-30 +- Add support for private repositories using docker credential stores/helpers (fixes [\#567](https://github.com/testcontainers/testcontainers-java/issues/567)) + ### Fixed +- Add support for private repositories using docker credential stores/helpers (fixes [\#567](https://github.com/testcontainers/testcontainers-java/issues/567)) - Retry any exceptions (not just `DockerClientException`) on image pull ([\#662](https://github.com/testcontainers/testcontainers-java/issues/662)) - Fixed handling of the paths with `+` in them ([\#664](https://github.com/testcontainers/testcontainers-java/issues/664)) @@ -57,6 +60,7 @@ All notable changes to this project will be documented in this file. - Fixed `HostPortWaitStrategy` throws `NumberFormatException` when port is exposed but not mapped ([\#640](https://github.com/testcontainers/testcontainers-java/issues/640)) - Fixed log processing: multibyte unicode, linebreaks and ASCII color codes. Color codes can be turned on with `withRemoveAnsiCodes(false)` ([\#643](https://github.com/testcontainers/testcontainers-java/pull/643)) - Fixed Docker host IP detection within docker container (detect only if not explicitly set) ([\#648](https://github.com/testcontainers/testcontainers-java/pull/648)) +- Add support for private repositories using docker credential stores/helpers ([PR \#647](https://github.com/testcontainers/testcontainers-java/pull/647), fixes [\#567](https://github.com/testcontainers/testcontainers-java/issues/567)) ### Changed - Support multiple HTTP status codes for HttpWaitStrategy ([\#630](https://github.com/testcontainers/testcontainers-java/issues/630)) diff --git a/circle.yml b/circle.yml index b216656417f..625c5026db0 100644 --- a/circle.yml +++ b/circle.yml @@ -14,6 +14,8 @@ jobs: when: always - store_test_results: path: ~/junit + - store_artifacts: + path: ~/junit okhttp: steps: - checkout @@ -45,6 +47,8 @@ jobs: when: always - store_test_results: path: ~/junit + - store_artifacts: + path: ~/junit modules-jdbc-test: steps: - checkout @@ -61,6 +65,8 @@ jobs: when: always - store_test_results: path: ~/junit + - store_artifacts: + path: ~/junit selenium: steps: - checkout @@ -74,6 +80,8 @@ jobs: when: always - store_test_results: path: ~/junit + - store_artifacts: + path: ~/junit workflows: version: 2 diff --git a/core/build.gradle b/core/build.gradle index 15a0300738f..3781c90002f 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -104,7 +104,7 @@ dependencies { shaded 'com.squareup.okhttp3:okhttp:3.10.0' shaded 'javax.ws.rs:javax.ws.rs-api:2.0.1' - shaded 'org.zeroturnaround:zt-exec:1.8' + shaded 'org.zeroturnaround:zt-exec:1.10' shaded 'commons-lang:commons-lang:2.6' shaded 'commons-io:commons-io:2.5' shaded 'commons-codec:commons-codec:1.11' diff --git a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java index 9554793c004..bb0d0c969ce 100644 --- a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java +++ b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java @@ -3,6 +3,7 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.ListImagesCmd; import com.github.dockerjava.api.exception.DockerClientException; +import com.github.dockerjava.api.model.AuthConfig; import com.github.dockerjava.api.model.Image; import com.github.dockerjava.core.command.PullImageResultCallback; import lombok.NonNull; @@ -14,6 +15,7 @@ import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.DockerLoggerFactory; import org.testcontainers.utility.LazyFuture; +import org.testcontainers.utility.RegistryAuthLocator; import java.util.HashSet; import java.util.List; @@ -93,10 +95,14 @@ protected final String resolve() { // The image is not available locally - pull it try { + final RegistryAuthLocator authLocator = new RegistryAuthLocator(dockerClient.authConfig()); + final AuthConfig effectiveAuthConfig = authLocator.lookupAuthConfig(imageName); + final PullImageResultCallback callback = new PullImageResultCallback(); dockerClient .pullImageCmd(imageName.getUnversionedPart()) .withTag(imageName.getVersionPart()) + .withAuthConfig(effectiveAuthConfig) .exec(callback); callback.awaitCompletion(); AVAILABLE_IMAGE_NAME_CACHE.add(imageName); diff --git a/core/src/main/java/org/testcontainers/utility/DockerImageName.java b/core/src/main/java/org/testcontainers/utility/DockerImageName.java index 50b2d57b743..3f6d7124891 100644 --- a/core/src/main/java/org/testcontainers/utility/DockerImageName.java +++ b/core/src/main/java/org/testcontainers/utility/DockerImageName.java @@ -94,7 +94,11 @@ public String getVersionPart() { @Override public String toString() { - return getUnversionedPart() + versioning.getSeparator() + versioning.toString(); + if (versioning == null) { + return getUnversionedPart(); + } else { + return getUnversionedPart() + versioning.getSeparator() + versioning.toString(); + } } /** @@ -116,6 +120,10 @@ public void assertValid() { } } + public String getRegistry() { + return registry; + } + private interface Versioning { boolean isValid(); String getSeparator(); diff --git a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java new file mode 100644 index 00000000000..eb290cae115 --- /dev/null +++ b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java @@ -0,0 +1,184 @@ +package org.testcontainers.utility; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dockerjava.api.model.AuthConfig; +import com.google.common.annotations.VisibleForTesting; +import org.apache.commons.lang.SystemUtils; +import org.slf4j.Logger; +import org.zeroturnaround.exec.ProcessExecutor; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Utility to look up registry authentication information for an image. + */ +public class RegistryAuthLocator { + + private static final Logger log = getLogger(RegistryAuthLocator.class); + + private final AuthConfig defaultAuthConfig; + private final File configFile; + private final String commandPathPrefix; + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + @VisibleForTesting + RegistryAuthLocator(AuthConfig defaultAuthConfig, File configFile, String commandPathPrefix) { + this.defaultAuthConfig = defaultAuthConfig; + this.configFile = configFile; + this.commandPathPrefix = commandPathPrefix; + } + + /** + * @param defaultAuthConfig an AuthConfig object that should be returned if there is no overriding authentication + * available for images that are looked up + */ + public RegistryAuthLocator(AuthConfig defaultAuthConfig) { + this.defaultAuthConfig = defaultAuthConfig; + final String dockerConfigLocation = System.getenv().getOrDefault("DOCKER_CONFIG", + System.getProperty("user.home") + "/.docker"); + this.configFile = new File(dockerConfigLocation + "/config.json"); + this.commandPathPrefix = ""; + } + + /** + * Looks up an AuthConfig for a given image name. + *

+ * Lookup is performed in following order: + *

    + *
  1. {@code auths} is checked for existing credentials for the specified registry.
  2. + *
  3. if no existing auth is found, {@code credHelpers} are checked for helper for the specified registry.
  4. + *
  5. if no suitable {@code credHelpers} found, {@code credsStore} is used.
  6. + *
  7. if no {@code credsStore} is found then the default configuration is returned.
  8. + *
+ * + * @param dockerImageName image name to be looked up (potentially including a registry URL part) + * @return an AuthConfig that is applicable to this specific image OR the defaultAuthConfig that has been set for + * this {@link RegistryAuthLocator}. + */ + public AuthConfig lookupAuthConfig(DockerImageName dockerImageName) { + + if (SystemUtils.IS_OS_WINDOWS) { + log.debug("RegistryAuthLocator is not supported on Windows. Please help test or improve it and update " + + "https://github.com/testcontainers/testcontainers-java/issues/756"); + return defaultAuthConfig; + } + + log.debug("Looking up auth config for image: {}", dockerImageName); + + log.debug("RegistryAuthLocator has configFile: {} ({}) and commandPathPrefix: {}", + configFile, + configFile.exists() ? "exists" : "does not exist", + commandPathPrefix); + + try { + final JsonNode config = OBJECT_MAPPER.readTree(configFile); + final String reposName = dockerImageName.getRegistry(); + + final AuthConfig existingAuthConfig = findExistingAuthConfig(config, reposName); + if (existingAuthConfig != null) { + return existingAuthConfig; + } + // auths is empty, using helper: + final AuthConfig helperAuthConfig = authConfigUsingHelper(config, reposName); + if (helperAuthConfig != null) { + return helperAuthConfig; + } + // no credsHelper to use, using credsStore: + final AuthConfig storeAuthConfig = authConfigUsingStore(config, reposName); + if (storeAuthConfig != null) { + return storeAuthConfig; + } + // otherwise, defaultAuthConfig should already contain any credentials available + } catch (Exception e) { + log.error("Failure when attempting to lookup auth config (dockerImageName: {}, configFile: {}. " + + "Falling back to docker-java default behaviour", + dockerImageName, + configFile, + e); + } + return defaultAuthConfig; + } + + private AuthConfig findExistingAuthConfig(final JsonNode config, final String reposName) throws Exception { + final Map.Entry entry = findAuthNode(config, reposName); + if (entry != null && entry.getValue() != null && entry.getValue().size() > 0) { + return OBJECT_MAPPER + .treeToValue(entry.getValue(), AuthConfig.class) + .withRegistryAddress(entry.getKey()); + } + return null; + } + + private AuthConfig authConfigUsingHelper(final JsonNode config, final String reposName) throws Exception { + final JsonNode credHelpers = config.get("credHelpers"); + if (credHelpers != null && credHelpers.size() > 0) { + final JsonNode helperNode = credHelpers.get(reposName); + if (helperNode != null && helperNode.isTextual()) { + final String helper = helperNode.asText(); + return runCredentialProvider(reposName, helper); + } + } + return null; + } + + private AuthConfig authConfigUsingStore(final JsonNode config, final String reposName) throws Exception { + final JsonNode credsStoreNode = config.get("credsStore"); + if (credsStoreNode != null && !credsStoreNode.isMissingNode() && credsStoreNode.isTextual()) { + final String credsStore = credsStoreNode.asText(); + return runCredentialProvider(reposName, credsStore); + } + return null; + } + + private Map.Entry findAuthNode(final JsonNode config, final String reposName) throws Exception { + final JsonNode auths = config.get("auths"); + if (auths != null && auths.size() > 0) { + final Iterator> fields = auths.fields(); + while (fields.hasNext()) { + final Map.Entry entry = fields.next(); + if (entry.getKey().endsWith("://" + reposName)) { + return entry; + } + } + } + return null; + } + + private AuthConfig runCredentialProvider(String hostName, String credHelper) throws Exception { + final String credentialHelperName = commandPathPrefix + "docker-credential-" + credHelper; + String data; + + log.debug("Executing docker credential helper: {} to locate auth config for: {}", + credentialHelperName, hostName); + + try { + data = new ProcessExecutor() + .command(credentialHelperName, "get") + .redirectInput(new ByteArrayInputStream(hostName.getBytes())) + .readOutput(true) + .exitValueNormal() + .timeout(30, TimeUnit.SECONDS) + .execute() + .outputUTF8() + .trim(); + } catch (Exception e) { + log.error("Failure running docker credential helper ({})", credentialHelperName); + throw e; + } + + final JsonNode helperResponse = OBJECT_MAPPER.readTree(data); + log.debug("Credential helper provided auth config for: {}", hostName); + + return new AuthConfig() + .withRegistryAddress(helperResponse.at("/ServerURL").asText()) + .withUsername(helperResponse.at("/Username").asText()) + .withPassword(helperResponse.at("/Secret").asText()); + } +} diff --git a/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java b/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java index bfd03d2c9be..cb87caf16d2 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java @@ -23,7 +23,7 @@ public static String[] parameters() { "gliderlabs/alpine@sha256:a19aa4a17a525c97e5a90a0c53a9f3329d2dc61b0a14df5447757a865671c085", "quay.io/testcontainers/ryuk:latest", "quay.io/testcontainers/ryuk:0.2.2", - "quay.io/testcontainers/ryuk@sha256:4b606e54c4bba1af4fd814019d342e4664d51e28d3ba2d18d24406edbefd66da" + "quay.io/testcontainers/ryuk@sha256:4b606e54c4bba1af4fd814019d342e4664d51e28d3ba2d18d24406edbefd66da", }; } diff --git a/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java b/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java new file mode 100644 index 00000000000..7a79f6bd40c --- /dev/null +++ b/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java @@ -0,0 +1,86 @@ +package org.testcontainers.utility; + +import com.github.dockerjava.api.model.AuthConfig; +import com.google.common.io.Resources; +import org.apache.commons.lang.SystemUtils; +import org.jetbrains.annotations.NotNull; +import org.junit.Assume; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.File; +import java.net.URISyntaxException; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertNull; + +public class RegistryAuthLocatorTest { + + @BeforeClass + public static void nonWindowsTest() throws Exception { + Assume.assumeFalse(SystemUtils.IS_OS_WINDOWS); + } + + @Test + public void lookupAuthConfigWithoutCredentials() throws URISyntaxException { + final RegistryAuthLocator authLocator = createTestAuthLocator("config-empty.json"); + + final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("unauthenticated.registry.org/org/repo")); + + assertEquals("Default docker registry URL is set on auth config", "https://index.docker.io/v1/", authConfig.getRegistryAddress()); + assertNull("No username is set", authConfig.getUsername()); + assertNull("No password is set", authConfig.getPassword()); + } + + @Test + public void lookupAuthConfigUsingStore() throws URISyntaxException { + final RegistryAuthLocator authLocator = createTestAuthLocator("config-with-store.json"); + + final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("registry.example.com/org/repo")); + + assertEquals("Correct server URL is obtained from a credential store", "url", authConfig.getRegistryAddress()); + assertEquals("Correct username is obtained from a credential store", "username", authConfig.getUsername()); + assertEquals("Correct secret is obtained from a credential store", "secret", authConfig.getPassword()); + } + + @Test + public void lookupAuthConfigUsingHelper() throws URISyntaxException { + final RegistryAuthLocator authLocator = createTestAuthLocator("config-with-helper.json"); + + final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("registry.example.com/org/repo")); + + assertEquals("Correct server URL is obtained from a credential store", "url", authConfig.getRegistryAddress()); + assertEquals("Correct username is obtained from a credential store", "username", authConfig.getUsername()); + assertEquals("Correct secret is obtained from a credential store", "secret", authConfig.getPassword()); + } + + @Test + public void lookupUsingHelperEmptyAuth() throws URISyntaxException { + final RegistryAuthLocator authLocator = createTestAuthLocator("config-empty-auth-with-helper.json"); + + final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("registry.example.com/org/repo")); + + assertEquals("Correct server URL is obtained from a credential store", "url", authConfig.getRegistryAddress()); + assertEquals("Correct username is obtained from a credential store", "username", authConfig.getUsername()); + assertEquals("Correct secret is obtained from a credential store", "secret", authConfig.getPassword()); + } + + @Test + public void lookupNonEmptyAuthWithHelper() throws URISyntaxException { + final RegistryAuthLocator authLocator = createTestAuthLocator("config-existing-auth-with-helper.json"); + + final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("registry.example.com/org/repo")); + + assertEquals("Correct server URL is obtained from a credential store", "https://registry.example.com", authConfig.getRegistryAddress()); + assertNull("No username is set", authConfig.getUsername()); + assertEquals("Correct email is obtained from a credential store", "not@val.id", authConfig.getEmail()); + assertEquals("Correct auth is obtained from a credential store", "encoded auth token", authConfig.getAuth()); + } + + @NotNull + private RegistryAuthLocator createTestAuthLocator(String configName) throws URISyntaxException { + final File configFile = new File(Resources.getResource("auth-config/" + configName).toURI()); + return new RegistryAuthLocator(new AuthConfig(), configFile, configFile.getParentFile().getAbsolutePath() + "/"); + } + +} diff --git a/core/src/test/resources/auth-config/config-empty-auth-with-helper.json b/core/src/test/resources/auth-config/config-empty-auth-with-helper.json new file mode 100644 index 00000000000..8d8864815e3 --- /dev/null +++ b/core/src/test/resources/auth-config/config-empty-auth-with-helper.json @@ -0,0 +1,10 @@ +{ + "auths": { + }, + "HttpHeaders": { + "User-Agent": "Docker-Client/18.03.0-ce (darwin)" + }, + "credHelpers": { + "registry.example.com": "fake" + } +} diff --git a/core/src/test/resources/auth-config/config-empty.json b/core/src/test/resources/auth-config/config-empty.json new file mode 100644 index 00000000000..38a103a2459 --- /dev/null +++ b/core/src/test/resources/auth-config/config-empty.json @@ -0,0 +1,5 @@ +{ + "HttpHeaders": { + "User-Agent": "Docker-Client/18.03.0-ce (darwin)" + } +} diff --git a/core/src/test/resources/auth-config/config-existing-auth-with-helper.json b/core/src/test/resources/auth-config/config-existing-auth-with-helper.json new file mode 100644 index 00000000000..2c877b88f90 --- /dev/null +++ b/core/src/test/resources/auth-config/config-existing-auth-with-helper.json @@ -0,0 +1,14 @@ +{ + "auths": { + "https://registry.example.com": { + "email": "not@val.id", + "auth": "encoded auth token" + } + }, + "HttpHeaders": { + "User-Agent": "Docker-Client/18.03.0-ce (darwin)" + }, + "credHelpers": { + "registry.example.com": "fake" + } +} diff --git a/core/src/test/resources/auth-config/config-with-helper-and-store.json b/core/src/test/resources/auth-config/config-with-helper-and-store.json new file mode 100644 index 00000000000..cd623369d78 --- /dev/null +++ b/core/src/test/resources/auth-config/config-with-helper-and-store.json @@ -0,0 +1,12 @@ +{ + "auths": { + "registry.example.com": {} + }, + "HttpHeaders": { + "User-Agent": "Docker-Client/18.03.0-ce (darwin)" + }, + "credsStore": "fake", + "credHelpers": { + "registry.example.com": "fake" + } +} diff --git a/core/src/test/resources/auth-config/config-with-helper.json b/core/src/test/resources/auth-config/config-with-helper.json new file mode 100644 index 00000000000..eaa670e4296 --- /dev/null +++ b/core/src/test/resources/auth-config/config-with-helper.json @@ -0,0 +1,11 @@ +{ + "auths": { + "registry.example.com": {} + }, + "HttpHeaders": { + "User-Agent": "Docker-Client/18.03.0-ce (darwin)" + }, + "credHelpers": { + "registry.example.com": "fake" + } +} diff --git a/core/src/test/resources/auth-config/config-with-store.json b/core/src/test/resources/auth-config/config-with-store.json new file mode 100644 index 00000000000..d165997e52f --- /dev/null +++ b/core/src/test/resources/auth-config/config-with-store.json @@ -0,0 +1,9 @@ +{ + "auths": { + "registry.example.com": {} + }, + "HttpHeaders": { + "User-Agent": "Docker-Client/18.03.0-ce (darwin)" + }, + "credsStore": "fake" +} diff --git a/core/src/test/resources/auth-config/docker-credential-fake b/core/src/test/resources/auth-config/docker-credential-fake new file mode 100755 index 00000000000..0cfa0dfb72d --- /dev/null +++ b/core/src/test/resources/auth-config/docker-credential-fake @@ -0,0 +1,13 @@ +#!/bin/bash + +if [[ $1 != "get" ]]; then + exit 1 +fi + +read > /dev/null + +echo '{' \ + ' "ServerURL": "url",' \ + ' "Username": "username",' \ + ' "Secret": "secret"' \ + '}' From 4e7ae6d9a10e4d423bbcf1a8c4def3c78207d324 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 16 Jun 2018 20:28:59 +0100 Subject: [PATCH 0345/5615] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cb73ecbed5..5a034f45a37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ All notable changes to this project will be documented in this file. ## UNRELEASED ### Fixed +- Linux/Mac: Added support for docker credential helpers so that images may be pulled from private registries. See [\#729](https://github.com/testcontainers/testcontainers-java/issues/729), [\#647](https://github.com/testcontainers/testcontainers-java/issues/647) and [\#567](https://github.com/testcontainers/testcontainers-java/issues/567). +- Ensure that the `COMPOSE_FILE` environment variable is populated with all relevant compose file names when running docker-compose in local mode [\#755](https://github.com/testcontainers/testcontainers-java/issues/755). ### Changed ## [1.8.0] - 2018-06-14 From 609f9a10c3c110be405b9cd96fc340c992b79b3b Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 16 Jun 2018 20:33:21 +0100 Subject: [PATCH 0346/5615] Update docs --- docs/usage/windows_support.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/usage/windows_support.md b/docs/usage/windows_support.md index c5eaa476590..a2d889da31c 100644 --- a/docs/usage/windows_support.md +++ b/docs/usage/windows_support.md @@ -23,7 +23,11 @@ effort. * WCOW is currently not supported, since Testcontainers uses auxiliary Linux containers for certain tasks and Docker for Windows does not support hybrid engine mode at the time of writing. +### Docker credential helpers + +Testcontainers supports credential helpers on Linux and MacOS, to allow pulling images from private registries. This feature is disabled on Windows due to lack of testing - if it is of value to you please help us test it. See [\#756](https://github.com/testcontainers/testcontainers-java/issues/756). + ## Reporting issues Please report any issues with the Windows build of Testcontainers [here](https://github.com/testcontainers/testcontainers-java/issues) -and be sure to note that you are using this on Windows. \ No newline at end of file +and be sure to note that you are using this on Windows. From 3bcc4088dc3c413d97b76126f8e10ba2b6073c71 Mon Sep 17 00:00:00 2001 From: haphut Date: Fri, 22 Jun 2018 11:05:30 +0300 Subject: [PATCH 0347/5615] Update Pulsar to 2.0.1 (#760) --- modules/pulsar/build.gradle | 2 +- .../java/org/testcontainers/containers/PulsarContainer.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 3c5a37013cd..755b9c6eff5 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Pulsar" dependencies { compile project(':testcontainers') - testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.0.0-rc1-incubating' + testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.0.1-incubating' testCompile group: 'org.assertj', name: 'assertj-core', version: '3.10.0' } diff --git a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java index 692c463cb65..c3680e0517e 100644 --- a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java +++ b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java @@ -11,7 +11,7 @@ public class PulsarContainer extends GenericContainer { public static final int PULSAR_PORT = 6850; public PulsarContainer() { - this("2.0.0-rc1-incubating"); + this("2.0.1-incubating"); } public PulsarContainer(String pulsarVersion) { From 246cc7de44de0984f39757aad7751243e42ddbd4 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 22 Jun 2018 10:06:54 +0200 Subject: [PATCH 0348/5615] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a034f45a37..b9d34ce062c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,9 @@ All notable changes to this project will be documented in this file. ### Fixed - Linux/Mac: Added support for docker credential helpers so that images may be pulled from private registries. See [\#729](https://github.com/testcontainers/testcontainers-java/issues/729), [\#647](https://github.com/testcontainers/testcontainers-java/issues/647) and [\#567](https://github.com/testcontainers/testcontainers-java/issues/567). - Ensure that the `COMPOSE_FILE` environment variable is populated with all relevant compose file names when running docker-compose in local mode [\#755](https://github.com/testcontainers/testcontainers-java/issues/755). + ### Changed +- Update Apache Pulsar module to 2.0.1 [\#760](https://github.com/testcontainers/testcontainers-java/issues/760). ## [1.8.0] - 2018-06-14 From f0e2072b7f7f0d723c72d59a1be6a7cf60ddce36 Mon Sep 17 00:00:00 2001 From: Vedran Pavic Date: Fri, 22 Jun 2018 14:55:11 +0200 Subject: [PATCH 0349/5615] Make JdbcDatabaseContainer#getDriverClassName public (#743) --- .../testcontainers/junit/SimpleMySQLTest.java | 19 +++++++++++++++++++ .../containers/JdbcDatabaseContainer.java | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java index d1ee8100709..572921d8a1e 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java @@ -110,9 +110,28 @@ public void testCommandOverride() throws SQLException { } + @Test + public void testMySQL8() throws SQLException { + assumeFalse(SystemUtils.IS_OS_WINDOWS); + MySQLContainer container = new MySQLContainer<>("mysql:8.0.11") + .withCommand("mysqld --default-authentication-plugin=mysql_native_password"); + container.start(); + + try { + ResultSet resultSet = performQuery(container, "SELECT VERSION()"); + String resultSetString = resultSet.getString(1); + + assertTrue("The database version can be set using a container rule parameter", "8.0.11".equals(resultSetString)); + } + finally { + container.stop(); + } + } + @NonNull protected ResultSet performQuery(MySQLContainer containerRule, String sql) throws SQLException { HikariConfig hikariConfig = new HikariConfig(); + hikariConfig.setDriverClassName(containerRule.getDriverClassName()); hikariConfig.setJdbcUrl(containerRule.getJdbcUrl()); hikariConfig.setUsername(containerRule.getUsername()); hikariConfig.setPassword(containerRule.getPassword()); diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index 77589395c19..5cd62b68220 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -47,7 +47,7 @@ public JdbcDatabaseContainer(@NonNull final Future image) { /** * @return the name of the actual JDBC driver to use */ - protected abstract String getDriverClassName(); + public abstract String getDriverClassName(); /** * @return a JDBC URL that may be used to connect to the dockerized DB From f113979ad5207464490cc4f19f488bb27795802c Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Fri, 22 Jun 2018 14:56:33 +0200 Subject: [PATCH 0350/5615] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9d34ce062c..2c434fbe881 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. ### Changed - Update Apache Pulsar module to 2.0.1 [\#760](https://github.com/testcontainers/testcontainers-java/issues/760). +- Make JdbcDatabaseContainer#getDriverClassName public [\#743](https://github.com/testcontainers/testcontainers-java/pull/743). ## [1.8.0] - 2018-06-14 From 577b509c8ee88b5b82dbc1a5c7e5a07676aba967 Mon Sep 17 00:00:00 2001 From: Paul Dailly Date: Tue, 3 Jul 2018 12:51:45 +0100 Subject: [PATCH 0351/5615] Fix overriden CMD in MariaDB container (#767) (#534) --- CHANGELOG.md | 1 + .../junit/SimpleMariaDBTest.java | 19 ++++++++++++++++++- .../containers/MariaDBContainer.java | 1 - 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c434fbe881..f8808707984 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. ### Fixed - Linux/Mac: Added support for docker credential helpers so that images may be pulled from private registries. See [\#729](https://github.com/testcontainers/testcontainers-java/issues/729), [\#647](https://github.com/testcontainers/testcontainers-java/issues/647) and [\#567](https://github.com/testcontainers/testcontainers-java/issues/567). - Ensure that the `COMPOSE_FILE` environment variable is populated with all relevant compose file names when running docker-compose in local mode [\#755](https://github.com/testcontainers/testcontainers-java/issues/755). +- Fixed issue whereby specified command in MariaDB image was not being applied. ([\#534](https://github.com/testcontainers/testcontainers-java/issues/534)) ### Changed - Update Apache Pulsar module to 2.0.1 [\#760](https://github.com/testcontainers/testcontainers-java/issues/760). diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMariaDBTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMariaDBTest.java index 1ad65e65465..042582ed890 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMariaDBTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMariaDBTest.java @@ -56,7 +56,7 @@ public void testSpecificVersion() throws SQLException { public void testMariaDBWithCustomIniFile() throws SQLException { assumeFalse(SystemUtils.IS_OS_WINDOWS); MariaDBContainer mariadbCustomConfig = new MariaDBContainer("mariadb:10.1.16") - .withConfigurationOverride("somepath/mariadb_conf_override"); + .withConfigurationOverride("somepath/mariadb_conf_override"); mariadbCustomConfig.start(); try { @@ -69,6 +69,23 @@ public void testMariaDBWithCustomIniFile() throws SQLException { } } + @Test + public void testMariaDBWithCommandOverride() throws SQLException { + + MariaDBContainer mariadbCustomConfig = (MariaDBContainer) new MariaDBContainer("mariadb:10.1.16") + .withCommand("mysqld --auto_increment_increment=10"); + mariadbCustomConfig.start(); + + try { + ResultSet resultSet = performQuery(mariadbCustomConfig, "show variables like 'auto_increment_increment'"); + String result = resultSet.getString("Value"); + + assertEquals("Auto increment increment should be overriden by command line", "10", result); + } finally { + mariadbCustomConfig.stop(); + } + } + @NonNull protected ResultSet performQuery(MariaDBContainer containerRule, String sql) throws SQLException { HikariConfig hikariConfig = new HikariConfig(); diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java index e5acd9e0ddb..e52dc5b52c1 100644 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java @@ -39,7 +39,6 @@ protected void configure() { addEnv("MYSQL_USER", MARIADB_USER); addEnv("MYSQL_PASSWORD", MARIADB_PASSWORD); addEnv("MYSQL_ROOT_PASSWORD", MARIADB_PASSWORD); - setCommand("mysqld"); setStartupAttempts(3); } From 22cab1f8bdf1944b6af5439dc77ae4357dac5a81 Mon Sep 17 00:00:00 2001 From: dharanikesav Date: Tue, 10 Jul 2018 10:08:30 +0200 Subject: [PATCH 0352/5615] enable copyFileToContainer feature during container startup (#742) --- .../testcontainers/containers/Container.java | 9 +++++++ .../containers/ContainerState.java | 16 ++++++++++++ .../containers/GenericContainer.java | 16 ++++++++++-- .../junit/CopyFileToContainerTest.java | 25 +++++++++++++++++++ 4 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java diff --git a/core/src/main/java/org/testcontainers/containers/Container.java b/core/src/main/java/org/testcontainers/containers/Container.java index da42a6f8036..7aeb015e8f2 100644 --- a/core/src/main/java/org/testcontainers/containers/Container.java +++ b/core/src/main/java/org/testcontainers/containers/Container.java @@ -175,6 +175,15 @@ default SELF withFileSystemBind(String hostPath, String containerPath) { */ SELF withExposedPorts(Integer... ports); + /** + * Set the file to be copied before starting a created container + * + * @param mountableFile a Mountable file with path of source file / folder on host machine + * @param containerPath a destination path on conatiner to which the files / folders to be copied + * @return this + */ + SELF withCopyFileToContainer(MountableFile mountableFile, String containerPath); + /** * Add an environment variable to be passed to the container. * diff --git a/core/src/main/java/org/testcontainers/containers/ContainerState.java b/core/src/main/java/org/testcontainers/containers/ContainerState.java index 1eb638232f5..26eec8c4ebe 100644 --- a/core/src/main/java/org/testcontainers/containers/ContainerState.java +++ b/core/src/main/java/org/testcontainers/containers/ContainerState.java @@ -43,6 +43,22 @@ default boolean isRunning() { } } + /** + * @return is the container created? + */ + default boolean isCreated() { + if (getContainerId() == null) { + return false; + } + + try { + String status = getCurrentContainerInfo().getState().getStatus(); + return "created".equalsIgnoreCase(status) || isRunning(); + } catch (DockerException e) { + return false; + } + } + /** * @return has the container health state 'healthy'? */ diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 5df1a065d59..22c3f1ce09c 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -142,6 +142,8 @@ public class GenericContainer> @Nullable private String workingDirectory = null; + private Map copyToFileContainerPathMap = new HashMap<>(); + /* * Unique instance of DockerClient for use by this container object. */ @@ -243,6 +245,7 @@ private void tryStart(Profiler profiler) { applyConfiguration(createCommand); containerId = createCommand.exec().getId(); + copyToFileContainerPathMap.forEach(this::copyFileToContainer); logger().info("Starting container with ID: {}", containerId); profiler.start("Start container"); @@ -894,6 +897,15 @@ public SELF withWorkingDirectory(String workDir) { return self(); } + /** + * {@inheritDoc} + */ + @Override + public SELF withCopyFileToContainer(MountableFile mountableFile, String containerPath) { + copyToFileContainerPathMap.put(mountableFile, containerPath); + return self(); + } + /** * Get the IP address that this container may be reached on (may not be the local machine). * @@ -999,8 +1011,8 @@ public ExecResult execInContainer(String... command) @Override public void copyFileToContainer(MountableFile mountableLocalFile, String containerPath) { - if (!isRunning()) { - throw new IllegalStateException("copyFileToContainer can only be used while the Container is running"); + if (!isCreated()) { + throw new IllegalStateException("copyFileToContainer can only be used with created / running container"); } this.dockerClient diff --git a/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java b/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java new file mode 100644 index 00000000000..f4c902cfa46 --- /dev/null +++ b/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java @@ -0,0 +1,25 @@ +package org.testcontainers.junit; + +import org.junit.Assert; +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.utility.MountableFile; +import java.io.IOException; + +public class CopyFileToContainerTest { + private static String containerPath = "/tmp"; + private static String fileName = "test-resource.txt"; + + @Test + public void checkFileCopied() throws IOException, InterruptedException { + try( + GenericContainer container = new GenericContainer("alpine:latest") + .withCommand("sleep","3000") + .withCopyFileToContainer(MountableFile.forClasspathResource("/mappable-resource/"), containerPath) + ) { + container.start(); + String filesList = container.execInContainer("ls","/tmp/mappable-resource").getStdout(); + Assert.assertTrue(filesList.contains(fileName)); + } + } +} From fb697557522dc0c4c6398990ed0f0f5cb7bc3dba Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 10 Jul 2018 10:11:31 +0200 Subject: [PATCH 0353/5615] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8808707984..51eab6d3af9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file. ### Changed - Update Apache Pulsar module to 2.0.1 [\#760](https://github.com/testcontainers/testcontainers-java/issues/760). - Make JdbcDatabaseContainer#getDriverClassName public [\#743](https://github.com/testcontainers/testcontainers-java/pull/743). +- enable `copyFileToContainer` feature during container startup [\#742](https://github.com/testcontainers/testcontainers-java/pull/742). ## [1.8.0] - 2018-06-14 From 2ffb8a3d43001c0570d03046a46755b1f8368a31 Mon Sep 17 00:00:00 2001 From: Sandro Giacomozzi Date: Tue, 10 Jul 2018 06:44:29 -0300 Subject: [PATCH 0354/5615] Changed Oracle thin URL for support Oracle 11 and 12 XE (#769) --- .../java/org/testcontainers/containers/OracleContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java index c28c2ba82af..d9ff393b266 100644 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java @@ -62,7 +62,7 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return "jdbc:oracle:thin:" + getUsername() + "/" + getPassword() + "@//" + getContainerIpAddress() + ":" + getOraclePort() + "/" + getSid(); + return "jdbc:oracle:thin:" + getUsername() + "/" + getPassword() + "@" + getContainerIpAddress() + ":" + getOraclePort() + ":" + getSid(); } @Override From 5b4d0ee09e38ea6f2cab59792fde4326bee22f6b Mon Sep 17 00:00:00 2001 From: Artem Dmitriev Date: Tue, 10 Jul 2018 12:47:40 +0300 Subject: [PATCH 0355/5615] Pass queryString to the JdbcDatabaseDelegate (#741) Fix for the #727 issue. --- .../testcontainers/jdbc/JDBCDriverTest.java | 20 +++++++++++++++++-- .../resources/somepath/init_unicode_mysql.sql | 5 +++++ .../jdbc/ContainerDatabaseDriver.java | 9 ++------- .../jdbc/ContainerLessJdbcDelegate.java | 2 +- .../jdbc/JdbcDatabaseDelegate.java | 6 ++++-- 5 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 modules/jdbc-test/src/test/resources/somepath/init_unicode_mysql.sql diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java index 49c889fcb5d..e303fa85565 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java @@ -44,14 +44,14 @@ public static Iterable data() { {"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", true, false, false, true}, {"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=somepath/init_mysql.sql", true, false, false, true}, {"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&password=somepwd&TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", true, false, false, true}, - {"jdbc:tc:mysql:5.5.43://hostname/databasename?useUnicode=yes&characterEncoding=utf8", false, true, false, false}, + {"jdbc:tc:mysql:5.5.43://hostname/databasename?TC_INITSCRIPT=somepath/init_unicode_mysql.sql&useUnicode=yes&characterEncoding=utf8", false, true, false, false}, {"jdbc:tc:mysql:5.5.43://hostname/databasename", false, false, false, false}, {"jdbc:tc:mysql:5.5.43://hostname/databasename?useSSL=false", false, false, false, false}, {"jdbc:tc:postgresql:9.6.8://hostname/databasename", false, false, false, false}, {"jdbc:tc:mysql:5.6://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override", false, false, true, false}, {"jdbc:tc:mariadb://hostname/databasename", false, false, false, false}, {"jdbc:tc:mariadb:10.2.14://hostname/databasename", false, false, false, false}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?useUnicode=yes&characterEncoding=utf8", false, true, false, false}, + {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITSCRIPT=somepath/init_unicode_mysql.sql&useUnicode=yes&characterEncoding=utf8", false, true, false, false}, {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITSCRIPT=somepath/init_mariadb.sql", true, false, false, false}, {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", true, false, false, false}, {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_MY_CNF=somepath/mariadb_conf_override", false, false, true, false}}); @@ -89,6 +89,8 @@ public void test() throws SQLException { //connections are created from cached containers. performSimpleTestWithCharacterSet(jdbcUrl); performSimpleTestWithCharacterSet(jdbcUrl); + + performTestForCharacterEncodingForInitialScriptConnection(jdbcUrl); } if (performTestForCustomIniFile) { @@ -129,6 +131,8 @@ private void performTestForJDBCParamUsage(String jdbcUrl) throws SQLException { return true; }); + assertTrue("The database returned a record as expected", result); + result = new QueryRunner(dataSource).query("SELECT DATABASE()", rs -> { rs.next(); String resultDB = rs.getString(1); @@ -137,7 +141,19 @@ private void performTestForJDBCParamUsage(String jdbcUrl) throws SQLException { }); assertTrue("The database returned a record as expected", result); + } + } + + private void performTestForCharacterEncodingForInitialScriptConnection(String jdbcUrl) throws SQLException { + try (HikariDataSource dataSource = getDataSource(jdbcUrl, 1)) { + boolean result = new QueryRunner(dataSource).query("SELECT foo FROM bar WHERE foo LIKE '%мир'", rs -> { + rs.next(); + String resultSetString = rs.getString(1); + assertEquals("A SELECT query succeed and the correct charset has been applied for the init script", "привет мир", resultSetString); + return true; + }); + assertTrue("The database returned a record as expected", result); } } diff --git a/modules/jdbc-test/src/test/resources/somepath/init_unicode_mysql.sql b/modules/jdbc-test/src/test/resources/somepath/init_unicode_mysql.sql new file mode 100644 index 00000000000..f88969b0f30 --- /dev/null +++ b/modules/jdbc-test/src/test/resources/somepath/init_unicode_mysql.sql @@ -0,0 +1,5 @@ +CREATE TABLE bar ( + foo varchar(255) character set utf8 +); + +INSERT INTO bar (foo) VALUES ('привет мир'); diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java index f5a6dc57731..163c67f486d 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java @@ -126,7 +126,7 @@ public synchronized Connection connect(String url, final Properties info) throws an init script or function has been specified, use it */ if (!initializedContainers.contains(container.getContainerId())) { - DatabaseDelegate databaseDelegate = new JdbcDatabaseDelegate(container); + DatabaseDelegate databaseDelegate = new JdbcDatabaseDelegate(container, queryString); runInitScriptIfRequired(connectionUrl, databaseDelegate); runInitFunctionIfRequired(connectionUrl, connection); initializedContainers.add(container.getContainerId()); @@ -151,12 +151,7 @@ private Connection wrapConnection(final Connection connection, final JdbcDatabas final boolean isDaemon = connectionUrl.isInDaemonMode(); - Set connections = containerConnections.get(container.getContainerId()); - - if (connections == null) { - connections = new HashSet<>(); - containerConnections.put(container.getContainerId(), connections); - } + Set connections = containerConnections.computeIfAbsent(container.getContainerId(), k -> new HashSet<>()); connections.add(connection); diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerLessJdbcDelegate.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerLessJdbcDelegate.java index 7a22d139bea..0806ddd2c31 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerLessJdbcDelegate.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerLessJdbcDelegate.java @@ -20,7 +20,7 @@ public class ContainerLessJdbcDelegate extends JdbcDatabaseDelegate { private Connection connection; public ContainerLessJdbcDelegate(Connection connection) { - super(null); + super(null, ""); this.connection = connection; } diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/JdbcDatabaseDelegate.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/JdbcDatabaseDelegate.java index e490da87c4a..6173a8f6a37 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/JdbcDatabaseDelegate.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/JdbcDatabaseDelegate.java @@ -18,15 +18,17 @@ public class JdbcDatabaseDelegate extends AbstractDatabaseDelegate { private JdbcDatabaseContainer container; + private String queryString; - public JdbcDatabaseDelegate(JdbcDatabaseContainer container) { + public JdbcDatabaseDelegate(JdbcDatabaseContainer container, String queryString) { this.container = container; + this.queryString = queryString; } @Override protected Statement createNewConnection() { try { - return container.createConnection("").createStatement(); + return container.createConnection(queryString).createStatement(); } catch (SQLException e) { log.error("Could not obtain JDBC connection"); throw new ConnectionCreationException("Could not obtain JDBC connection", e); From ea500c6a64c95f8b6b97b323103d5d1ef3e74d17 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 10 Jul 2018 11:55:45 +0200 Subject: [PATCH 0356/5615] avoid using file mounting in Kafka (useful when you run tests against a remote Docker engine) (#775) --- .../org/testcontainers/containers/KafkaContainer.java | 10 ++++++++-- .../kafka/src/main/resources/tc-zookeeper.properties | 3 --- 2 files changed, 8 insertions(+), 5 deletions(-) delete mode 100644 modules/kafka/src/main/resources/tc-zookeeper.properties diff --git a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java index 9c914faf8d3..b9957e8d7c2 100644 --- a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java +++ b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java @@ -72,8 +72,14 @@ protected void doStart() { } else { addExposedPort(ZOOKEEPER_PORT); withEnv("KAFKA_ZOOKEEPER_CONNECT", "localhost:2181"); - withClasspathResourceMapping("tc-zookeeper.properties", "/zookeeper.properties", BindMode.READ_ONLY); - withCommand("sh", "-c", "zookeeper-server-start /zookeeper.properties & /etc/confluent/docker/run"); + withCommand( + "sh", + "-c", + // Use command to create the file to avoid file mounting (useful when you run your tests against a remote Docker daemon) + "printf 'clientPort=2181\ndataDir=/var/lib/zookeeper/data\ndataLogDir=/var/lib/zookeeper/log' > /zookeeper.properties" + + " && zookeeper-server-start /zookeeper.properties" + + " & /etc/confluent/docker/run" + ); } super.doStart(); diff --git a/modules/kafka/src/main/resources/tc-zookeeper.properties b/modules/kafka/src/main/resources/tc-zookeeper.properties deleted file mode 100644 index f0cb437927d..00000000000 --- a/modules/kafka/src/main/resources/tc-zookeeper.properties +++ /dev/null @@ -1,3 +0,0 @@ -clientPort=2181 -dataDir=/var/lib/zookeeper/data -dataLogDir=/var/lib/zookeeper/log \ No newline at end of file From e8bc27915b6dc509350f6bb21cc3e107844d1de4 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 10 Jul 2018 11:56:31 +0200 Subject: [PATCH 0357/5615] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51eab6d3af9..90b09818687 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file. - Update Apache Pulsar module to 2.0.1 [\#760](https://github.com/testcontainers/testcontainers-java/issues/760). - Make JdbcDatabaseContainer#getDriverClassName public [\#743](https://github.com/testcontainers/testcontainers-java/pull/743). - enable `copyFileToContainer` feature during container startup [\#742](https://github.com/testcontainers/testcontainers-java/pull/742). +- avoid using file mounting in KafkaContainer [\#775](https://github.com/testcontainers/testcontainers-java/pull/775). ## [1.8.0] - 2018-06-14 From e178313227c065021b498c3a3b80c484fa3718a0 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 10 Jul 2018 16:36:16 +0200 Subject: [PATCH 0358/5615] Add Cassandra module (#776) * Implement Cassandra support * use lombok in CassandraContainer, rephase comment, add import, add getCluster() method, remove excess exception usage * remove excess casting from CassandraContainerTest * split comma-separated dependencies * update Cassandra driver * use new wait strategy logic, fix the Docker image, remove Netty workaround --- modules/cassandra/build.gradle | 6 + .../containers/CassandraContainer.java | 162 +++ .../delegate/CassandraDatabaseDelegate.java | 58 + .../wait/CassandraQueryWaitStrategy.java | 43 + .../containers/CassandraContainerTest.java | 131 ++ .../cassandra.yaml | 1233 +++++++++++++++++ .../cassandra/src/test/resources/initial.cql | 7 + .../src/test/resources/logback-test.xml | 17 + .../org/testcontainers/ext/ScriptUtils.java | 36 +- 9 files changed, 1682 insertions(+), 11 deletions(-) create mode 100644 modules/cassandra/build.gradle create mode 100644 modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java create mode 100644 modules/cassandra/src/main/java/org/testcontainers/containers/delegate/CassandraDatabaseDelegate.java create mode 100644 modules/cassandra/src/main/java/org/testcontainers/containers/wait/CassandraQueryWaitStrategy.java create mode 100644 modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java create mode 100644 modules/cassandra/src/test/resources/cassandra-test-configuration-example/cassandra.yaml create mode 100644 modules/cassandra/src/test/resources/initial.cql create mode 100644 modules/cassandra/src/test/resources/logback-test.xml diff --git a/modules/cassandra/build.gradle b/modules/cassandra/build.gradle new file mode 100644 index 00000000000..ef0706c58c6 --- /dev/null +++ b/modules/cassandra/build.gradle @@ -0,0 +1,6 @@ +description = "TestContainers :: Cassandra" + +dependencies { + compile project(":database-commons") + compile "com.datastax.cassandra:cassandra-driver-core:3.5.1" +} diff --git a/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java b/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java new file mode 100644 index 00000000000..0525f0af1f9 --- /dev/null +++ b/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java @@ -0,0 +1,162 @@ +package org.testcontainers.containers; + +import com.datastax.driver.core.Cluster; +import com.github.dockerjava.api.command.InspectContainerResponse; +import org.apache.commons.io.IOUtils; +import org.testcontainers.containers.delegate.CassandraDatabaseDelegate; +import org.testcontainers.delegate.DatabaseDelegate; +import org.testcontainers.ext.ScriptUtils; +import org.testcontainers.ext.ScriptUtils.ScriptLoadException; +import org.testcontainers.utility.MountableFile; + +import javax.script.ScriptException; +import java.io.IOException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Optional; + +/** + * Cassandra container + * + * Supports 2.x and 3.x Cassandra versions + * + * @author Eugeny Karpov + */ +public class CassandraContainer> extends GenericContainer { + + public static final String IMAGE = "cassandra"; + public static final Integer CQL_PORT = 9042; + private static final String CONTAINER_CONFIG_LOCATION = "/etc/cassandra"; + private static final String USERNAME = "cassandra"; + private static final String PASSWORD = "cassandra"; + + private String configLocation; + private String initScriptPath; + + public CassandraContainer() { + this(IMAGE + ":3.11.2"); + } + + public CassandraContainer(String dockerImageName) { + super(dockerImageName); + addExposedPort(CQL_PORT); + setStartupAttempts(3); + } + + @Override + protected void configure() { + optionallyMapResourceParameterAsVolume(CONTAINER_CONFIG_LOCATION, configLocation); + } + + @Override + protected void containerIsStarted(InspectContainerResponse containerInfo) { + runInitScriptIfRequired(); + } + + /** + * Load init script content and apply it to the database if initScriptPath is set + */ + private void runInitScriptIfRequired() { + if (initScriptPath != null) { + try { + URL resource = Thread.currentThread().getContextClassLoader().getResource(initScriptPath); + if (resource == null) { + logger().warn("Could not load classpath init script: {}", initScriptPath); + throw new ScriptLoadException("Could not load classpath init script: " + initScriptPath + ". Resource not found."); + } + String cql = IOUtils.toString(resource, StandardCharsets.UTF_8); + DatabaseDelegate databaseDelegate = getDatabaseDelegate(); + ScriptUtils.executeDatabaseScript(databaseDelegate, initScriptPath, cql); + } catch (IOException e) { + logger().warn("Could not load classpath init script: {}", initScriptPath); + throw new ScriptLoadException("Could not load classpath init script: " + initScriptPath, e); + } catch (ScriptException e) { + logger().error("Error while executing init script: {}", initScriptPath, e); + throw new ScriptUtils.UncategorizedScriptException("Error while executing init script: " + initScriptPath, e); + } + } + } + + /** + * Map (effectively replace) directory in Docker with the content of resourceLocation if resource location is not null + * + * Protected to allow for changing implementation by extending the class + * + * @param pathNameInContainer path in docker + * @param resourceLocation relative classpath to resource + */ + protected void optionallyMapResourceParameterAsVolume(String pathNameInContainer, String resourceLocation) { + Optional.ofNullable(resourceLocation) + .map(MountableFile::forClasspathResource) + .ifPresent(mountableFile -> addFileSystemBind(mountableFile.getResolvedPath(), pathNameInContainer, BindMode.READ_WRITE)); + } + + /** + * Initialize Cassandra with the custom overridden Cassandra configuration + *

+ * Be aware, that Docker effectively replaces all /etc/cassandra content with the content of config location, so if + * Cassandra.yaml in configLocation is absent or corrupted, then Cassandra just won't launch + * + * @param configLocation relative classpath with the directory that contains cassandra.yaml and other configuration files + */ + public SELF withConfigurationOverride(String configLocation) { + this.configLocation = configLocation; + return self(); + } + + /** + * Initialize Cassandra with init CQL script + *

+ * CQL script will be applied after container is started (see using WaitStrategy) + * + * @param initScriptPath relative classpath resource + */ + public SELF withInitScript(String initScriptPath) { + this.initScriptPath = initScriptPath; + return self(); + } + + /** + * Get username + * + * By default Cassandra has authenticator: AllowAllAuthenticator in cassandra.yaml + * If username and password need to be used, then authenticator should be set as PasswordAuthenticator + * (through custom Cassandra configuration) and through CQL with default cassandra-cassandra credentials + * user management should be modified + */ + public String getUsername() { + return USERNAME; + } + + /** + * Get password + * + * By default Cassandra has authenticator: AllowAllAuthenticator in cassandra.yaml + * If username and password need to be used, then authenticator should be set as PasswordAuthenticator + * (through custom Cassandra configuration) and through CQL with default cassandra-cassandra credentials + * user management should be modified + */ + public String getPassword() { + return PASSWORD; + } + + /** + * Get configured Cluster + * + * Can be used to obtain connections to Cassandra in the container + */ + public Cluster getCluster() { + return getCluster(this); + } + + public static Cluster getCluster(ContainerState containerState) { + return Cluster.builder() + .addContactPoint(containerState.getContainerIpAddress()) + .withPort(containerState.getMappedPort(CQL_PORT)) + .build(); + } + + private DatabaseDelegate getDatabaseDelegate() { + return new CassandraDatabaseDelegate(this); + } +} diff --git a/modules/cassandra/src/main/java/org/testcontainers/containers/delegate/CassandraDatabaseDelegate.java b/modules/cassandra/src/main/java/org/testcontainers/containers/delegate/CassandraDatabaseDelegate.java new file mode 100644 index 00000000000..e55dd31b0ff --- /dev/null +++ b/modules/cassandra/src/main/java/org/testcontainers/containers/delegate/CassandraDatabaseDelegate.java @@ -0,0 +1,58 @@ +package org.testcontainers.containers.delegate; + +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.exceptions.DriverException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.testcontainers.containers.CassandraContainer; +import org.testcontainers.containers.ContainerState; +import org.testcontainers.delegate.AbstractDatabaseDelegate; +import org.testcontainers.exception.ConnectionCreationException; +import org.testcontainers.ext.ScriptUtils.ScriptStatementFailedException; + +/** + * Cassandra database delegate + * + * @author Eugeny Karpov + */ +@Slf4j +@RequiredArgsConstructor +public class CassandraDatabaseDelegate extends AbstractDatabaseDelegate { + + private final ContainerState container; + + @Override + protected Session createNewConnection() { + try { + return CassandraContainer.getCluster(container) + .newSession(); + } catch (DriverException e) { + log.error("Could not obtain cassandra connection"); + throw new ConnectionCreationException("Could not obtain cassandra connection", e); + } + } + + @Override + public void execute(String statement, String scriptPath, int lineNumber, boolean continueOnError, boolean ignoreFailedDrops) { + try { + ResultSet result = getConnection().execute(statement); + if (result.wasApplied()) { + log.debug("Statement {} was applied", statement); + } else { + throw new ScriptStatementFailedException(statement, lineNumber, scriptPath); + } + } catch (DriverException e) { + throw new ScriptStatementFailedException(statement, lineNumber, scriptPath, e); + } + } + + @Override + protected void closeConnectionQuietly(Session session) { + try { + session.getCluster().close(); + } catch (Exception e) { + log.error("Could not close cassandra connection", e); + } + } +} diff --git a/modules/cassandra/src/main/java/org/testcontainers/containers/wait/CassandraQueryWaitStrategy.java b/modules/cassandra/src/main/java/org/testcontainers/containers/wait/CassandraQueryWaitStrategy.java new file mode 100644 index 00000000000..cf7f3769985 --- /dev/null +++ b/modules/cassandra/src/main/java/org/testcontainers/containers/wait/CassandraQueryWaitStrategy.java @@ -0,0 +1,43 @@ +package org.testcontainers.containers.wait; + +import org.rnorth.ducttape.TimeoutException; +import org.testcontainers.containers.ContainerLaunchException; +import org.testcontainers.containers.delegate.CassandraDatabaseDelegate; +import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy; +import org.testcontainers.delegate.DatabaseDelegate; + +import java.util.concurrent.TimeUnit; + +import static org.rnorth.ducttape.unreliables.Unreliables.retryUntilSuccess; + +/** + * Waits until Cassandra returns its version + * + * @author Eugeny Karpov + */ +public class CassandraQueryWaitStrategy extends AbstractWaitStrategy { + + private static final String SELECT_VERSION_QUERY = "SELECT release_version FROM system.local"; + private static final String TIMEOUT_ERROR = "Timed out waiting for Cassandra to be accessible for query execution"; + + @Override + protected void waitUntilReady() { + // execute select version query until success or timeout + try { + retryUntilSuccess((int) startupTimeout.getSeconds(), TimeUnit.SECONDS, () -> { + getRateLimiter().doWhenReady(() -> { + try (DatabaseDelegate databaseDelegate = getDatabaseDelegate()) { + databaseDelegate.execute(SELECT_VERSION_QUERY, "", 1, false, false); + } + }); + return true; + }); + } catch (TimeoutException e) { + throw new ContainerLaunchException(TIMEOUT_ERROR); + } + } + + private DatabaseDelegate getDatabaseDelegate() { + return new CassandraDatabaseDelegate(waitStrategyTarget); + } +} diff --git a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java new file mode 100644 index 00000000000..6105aa275c9 --- /dev/null +++ b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java @@ -0,0 +1,131 @@ +package org.testcontainers.containers; + +import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.Session; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; +import org.testcontainers.containers.wait.CassandraQueryWaitStrategy; + +import static org.junit.Assert.*; + +/** + * @author Eugeny Karpov + */ +@Slf4j +public class CassandraContainerTest { + + private static final String TEST_CLUSTER_NAME_IN_CONF = "Test Cluster Integration Test"; + + @Test + public void testSimple() { + try (CassandraContainer cassandraContainer = new CassandraContainer<>()) { + cassandraContainer.start(); + ResultSet resultSet = performQuery(cassandraContainer, "SELECT release_version FROM system.local"); + assertTrue("Query was not applied", resultSet.wasApplied()); + assertNotNull("Result set has no release_version", resultSet.one().getString(0)); + } + } + + @Test + public void testSpecificVersion() { + String cassandraVersion = "3.0.15"; + try (CassandraContainer cassandraContainer = new CassandraContainer<>("cassandra:" + cassandraVersion)) { + cassandraContainer.start(); + ResultSet resultSet = performQuery(cassandraContainer, "SELECT release_version FROM system.local"); + assertTrue("Query was not applied", resultSet.wasApplied()); + assertEquals("Cassandra has wrong version", cassandraVersion, resultSet.one().getString(0)); + } + } + + @Test + public void testConfigurationOverride() { + try ( + CassandraContainer cassandraContainer = new CassandraContainer<>() + .withConfigurationOverride("cassandra-test-configuration-example") + ) { + cassandraContainer.start(); + ResultSet resultSet = performQuery(cassandraContainer, "SELECT cluster_name FROM system.local"); + assertTrue("Query was not applied", resultSet.wasApplied()); + assertEquals("Cassandra configuration is not overridden", TEST_CLUSTER_NAME_IN_CONF, resultSet.one().getString(0)); + } + } + + @Test(expected = ContainerLaunchException.class) + public void testEmptyConfigurationOverride() { + try ( + CassandraContainer cassandraContainer = new CassandraContainer<>() + .withConfigurationOverride("cassandra-empty-configuration") + ) { + cassandraContainer.start(); + } + } + + @Test + public void testInitScript() { + try ( + CassandraContainer cassandraContainer = new CassandraContainer<>() + .withInitScript("initial.cql") + ) { + cassandraContainer.start(); + testInitScript(cassandraContainer); + } + } + + @Test + public void testInitScriptWithLegacyCassandra() { + try ( + CassandraContainer cassandraContainer = new CassandraContainer<>("cassandra:2.2.11") + .withInitScript("initial.cql") + ) { + cassandraContainer.start(); + testInitScript(cassandraContainer); + } + } + + @Test + public void testCassandraQueryWaitStrategy() { + try ( + CassandraContainer cassandraContainer = new CassandraContainer<>() + .waitingFor(new CassandraQueryWaitStrategy()) + ) { + cassandraContainer.start(); + ResultSet resultSet = performQuery(cassandraContainer, "SELECT release_version FROM system.local"); + assertTrue("Query was not applied", resultSet.wasApplied()); + } + } + + @Test + public void testCassandraGetCluster() { + try (CassandraContainer cassandraContainer = new CassandraContainer<>()) { + cassandraContainer.start(); + ResultSet resultSet = performQuery(cassandraContainer.getCluster(), "SELECT release_version FROM system.local"); + assertTrue("Query was not applied", resultSet.wasApplied()); + assertNotNull("Result set has no release_version", resultSet.one().getString(0)); + } + } + + private void testInitScript(CassandraContainer cassandraContainer) { + ResultSet resultSet = performQuery(cassandraContainer, "SELECT * FROM keySpaceTest.catalog_category"); + assertTrue("Query was not applied", resultSet.wasApplied()); + Row row = resultSet.one(); + assertEquals("Inserted row is not in expected state", 1, row.getLong(0)); + assertEquals("Inserted row is not in expected state", "test_category", row.getString(1)); + } + + private ResultSet performQuery(CassandraContainer cassandraContainer, String cql) { + Cluster explicitCluster = Cluster.builder() + .addContactPoint(cassandraContainer.getContainerIpAddress()) + .withPort(cassandraContainer.getMappedPort(CassandraContainer.CQL_PORT)) + .build(); + return performQuery(explicitCluster, cql); + } + + private ResultSet performQuery(Cluster cluster, String cql) { + try (Cluster closeableCluster = cluster) { + Session session = closeableCluster.newSession(); + return session.execute(cql); + } + } +} diff --git a/modules/cassandra/src/test/resources/cassandra-test-configuration-example/cassandra.yaml b/modules/cassandra/src/test/resources/cassandra-test-configuration-example/cassandra.yaml new file mode 100644 index 00000000000..5b57b2a8e58 --- /dev/null +++ b/modules/cassandra/src/test/resources/cassandra-test-configuration-example/cassandra.yaml @@ -0,0 +1,1233 @@ +# Cassandra storage config YAML + +# NOTE: +# See http://wiki.apache.org/cassandra/StorageConfiguration for +# full explanations of configuration directives +# /NOTE + +# The name of the cluster. This is mainly used to prevent machines in +# one logical cluster from joining another. +cluster_name: 'Test Cluster Integration Test' + +# This defines the number of tokens randomly assigned to this node on the ring +# The more tokens, relative to other nodes, the larger the proportion of data +# that this node will store. You probably want all nodes to have the same number +# of tokens assuming they have equal hardware capability. +# +# If you leave this unspecified, Cassandra will use the default of 1 token for legacy compatibility, +# and will use the initial_token as described below. +# +# Specifying initial_token will override this setting on the node's initial start, +# on subsequent starts, this setting will apply even if initial token is set. +# +# If you already have a cluster with 1 token per node, and wish to migrate to +# multiple tokens per node, see http://wiki.apache.org/cassandra/Operations +num_tokens: 256 + +# Triggers automatic allocation of num_tokens tokens for this node. The allocation +# algorithm attempts to choose tokens in a way that optimizes replicated load over +# the nodes in the datacenter for the replication strategy used by the specified +# keyspace. +# +# The load assigned to each node will be close to proportional to its number of +# vnodes. +# +# Only supported with the Murmur3Partitioner. +# allocate_tokens_for_keyspace: KEYSPACE + +# initial_token allows you to specify tokens manually. While you can use it with +# vnodes (num_tokens > 1, above) -- in which case you should provide a +# comma-separated list -- it's primarily used when adding nodes to legacy clusters +# that do not have vnodes enabled. +# initial_token: + +# See http://wiki.apache.org/cassandra/HintedHandoff +# May either be "true" or "false" to enable globally +hinted_handoff_enabled: true + +# When hinted_handoff_enabled is true, a black list of data centers that will not +# perform hinted handoff +# hinted_handoff_disabled_datacenters: +# - DC1 +# - DC2 + +# this defines the maximum amount of time a dead host will have hints +# generated. After it has been dead this long, new hints for it will not be +# created until it has been seen alive and gone down again. +max_hint_window_in_ms: 10800000 # 3 hours + +# Maximum throttle in KBs per second, per delivery thread. This will be +# reduced proportionally to the number of nodes in the cluster. (If there +# are two nodes in the cluster, each delivery thread will use the maximum +# rate; if there are three, each will throttle to half of the maximum, +# since we expect two nodes to be delivering hints simultaneously.) +hinted_handoff_throttle_in_kb: 1024 + +# Number of threads with which to deliver hints; +# Consider increasing this number when you have multi-dc deployments, since +# cross-dc handoff tends to be slower +max_hints_delivery_threads: 2 + +# Directory where Cassandra should store hints. +# If not set, the default directory is $CASSANDRA_HOME/data/hints. +# hints_directory: /var/lib/cassandra/hints + +# How often hints should be flushed from the internal buffers to disk. +# Will *not* trigger fsync. +hints_flush_period_in_ms: 10000 + +# Maximum size for a single hints file, in megabytes. +max_hints_file_size_in_mb: 128 + +# Compression to apply to the hint files. If omitted, hints files +# will be written uncompressed. LZ4, Snappy, and Deflate compressors +# are supported. +#hints_compression: +# - class_name: LZ4Compressor +# parameters: +# - + +# Maximum throttle in KBs per second, total. This will be +# reduced proportionally to the number of nodes in the cluster. +batchlog_replay_throttle_in_kb: 1024 + +# Authentication backend, implementing IAuthenticator; used to identify users +# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthenticator, +# PasswordAuthenticator}. +# +# - AllowAllAuthenticator performs no checks - set it to disable authentication. +# - PasswordAuthenticator relies on username/password pairs to authenticate +# users. It keeps usernames and hashed passwords in system_auth.roles table. +# Please increase system_auth keyspace replication factor if you use this authenticator. +# If using PasswordAuthenticator, CassandraRoleManager must also be used (see below) +authenticator: AllowAllAuthenticator + +# Authorization backend, implementing IAuthorizer; used to limit access/provide permissions +# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthorizer, +# CassandraAuthorizer}. +# +# - AllowAllAuthorizer allows any action to any user - set it to disable authorization. +# - CassandraAuthorizer stores permissions in system_auth.role_permissions table. Please +# increase system_auth keyspace replication factor if you use this authorizer. +authorizer: AllowAllAuthorizer + +# Part of the Authentication & Authorization backend, implementing IRoleManager; used +# to maintain grants and memberships between roles. +# Out of the box, Cassandra provides org.apache.cassandra.auth.CassandraRoleManager, +# which stores role information in the system_auth keyspace. Most functions of the +# IRoleManager require an authenticated login, so unless the configured IAuthenticator +# actually implements authentication, most of this functionality will be unavailable. +# +# - CassandraRoleManager stores role data in the system_auth keyspace. Please +# increase system_auth keyspace replication factor if you use this role manager. +role_manager: CassandraRoleManager + +# Validity period for roles cache (fetching granted roles can be an expensive +# operation depending on the role manager, CassandraRoleManager is one example) +# Granted roles are cached for authenticated sessions in AuthenticatedUser and +# after the period specified here, become eligible for (async) reload. +# Defaults to 2000, set to 0 to disable caching entirely. +# Will be disabled automatically for AllowAllAuthenticator. +roles_validity_in_ms: 2000 + +# Refresh interval for roles cache (if enabled). +# After this interval, cache entries become eligible for refresh. Upon next +# access, an async reload is scheduled and the old value returned until it +# completes. If roles_validity_in_ms is non-zero, then this must be +# also. +# Defaults to the same value as roles_validity_in_ms. +# roles_update_interval_in_ms: 2000 + +# Validity period for permissions cache (fetching permissions can be an +# expensive operation depending on the authorizer, CassandraAuthorizer is +# one example). Defaults to 2000, set to 0 to disable. +# Will be disabled automatically for AllowAllAuthorizer. +permissions_validity_in_ms: 2000 + +# Refresh interval for permissions cache (if enabled). +# After this interval, cache entries become eligible for refresh. Upon next +# access, an async reload is scheduled and the old value returned until it +# completes. If permissions_validity_in_ms is non-zero, then this must be +# also. +# Defaults to the same value as permissions_validity_in_ms. +# permissions_update_interval_in_ms: 2000 + +# Validity period for credentials cache. This cache is tightly coupled to +# the provided PasswordAuthenticator implementation of IAuthenticator. If +# another IAuthenticator implementation is configured, this cache will not +# be automatically used and so the following settings will have no effect. +# Please note, credentials are cached in their encrypted form, so while +# activating this cache may reduce the number of queries made to the +# underlying table, it may not bring a significant reduction in the +# latency of individual authentication attempts. +# Defaults to 2000, set to 0 to disable credentials caching. +credentials_validity_in_ms: 2000 + +# Refresh interval for credentials cache (if enabled). +# After this interval, cache entries become eligible for refresh. Upon next +# access, an async reload is scheduled and the old value returned until it +# completes. If credentials_validity_in_ms is non-zero, then this must be +# also. +# Defaults to the same value as credentials_validity_in_ms. +# credentials_update_interval_in_ms: 2000 + +# The partitioner is responsible for distributing groups of rows (by +# partition key) across nodes in the cluster. You should leave this +# alone for new clusters. The partitioner can NOT be changed without +# reloading all data, so when upgrading you should set this to the +# same partitioner you were already using. +# +# Besides Murmur3Partitioner, partitioners included for backwards +# compatibility include RandomPartitioner, ByteOrderedPartitioner, and +# OrderPreservingPartitioner. +# +partitioner: org.apache.cassandra.dht.Murmur3Partitioner + +# Directories where Cassandra should store data on disk. Cassandra +# will spread data evenly across them, subject to the granularity of +# the configured compaction strategy. +# If not set, the default directory is $CASSANDRA_HOME/data/data. +data_file_directories: + - /var/lib/cassandra/data + +# commit log. when running on magnetic HDD, this should be a +# separate spindle than the data directories. +# If not set, the default directory is $CASSANDRA_HOME/data/commitlog. +commitlog_directory: /var/lib/cassandra/commitlog + +# Enable / disable CDC functionality on a per-node basis. This modifies the logic used +# for write path allocation rejection (standard: never reject. cdc: reject Mutation +# containing a CDC-enabled table if at space limit in cdc_raw_directory). +cdc_enabled: false + +# CommitLogSegments are moved to this directory on flush if cdc_enabled: true and the +# segment contains mutations for a CDC-enabled table. This should be placed on a +# separate spindle than the data directories. If not set, the default directory is +# $CASSANDRA_HOME/data/cdc_raw. +# cdc_raw_directory: /var/lib/cassandra/cdc_raw + +# Policy for data disk failures: +# +# die +# shut down gossip and client transports and kill the JVM for any fs errors or +# single-sstable errors, so the node can be replaced. +# +# stop_paranoid +# shut down gossip and client transports even for single-sstable errors, +# kill the JVM for errors during startup. +# +# stop +# shut down gossip and client transports, leaving the node effectively dead, but +# can still be inspected via JMX, kill the JVM for errors during startup. +# +# best_effort +# stop using the failed disk and respond to requests based on +# remaining available sstables. This means you WILL see obsolete +# data at CL.ONE! +# +# ignore +# ignore fatal errors and let requests fail, as in pre-1.2 Cassandra +disk_failure_policy: stop + +# Policy for commit disk failures: +# +# die +# shut down gossip and Thrift and kill the JVM, so the node can be replaced. +# +# stop +# shut down gossip and Thrift, leaving the node effectively dead, but +# can still be inspected via JMX. +# +# stop_commit +# shutdown the commit log, letting writes collect but +# continuing to service reads, as in pre-2.0.5 Cassandra +# +# ignore +# ignore fatal errors and let the batches fail +commit_failure_policy: stop + +# Maximum size of the native protocol prepared statement cache +# +# Valid values are either "auto" (omitting the value) or a value greater 0. +# +# Note that specifying a too large value will result in long running GCs and possbily +# out-of-memory errors. Keep the value at a small fraction of the heap. +# +# If you constantly see "prepared statements discarded in the last minute because +# cache limit reached" messages, the first step is to investigate the root cause +# of these messages and check whether prepared statements are used correctly - +# i.e. use bind markers for variable parts. +# +# Do only change the default value, if you really have more prepared statements than +# fit in the cache. In most cases it is not neccessary to change this value. +# Constantly re-preparing statements is a performance penalty. +# +# Default value ("auto") is 1/256th of the heap or 10MB, whichever is greater +prepared_statements_cache_size_mb: + +# Maximum size of the Thrift prepared statement cache +# +# If you do not use Thrift at all, it is safe to leave this value at "auto". +# +# See description of 'prepared_statements_cache_size_mb' above for more information. +# +# Default value ("auto") is 1/256th of the heap or 10MB, whichever is greater +thrift_prepared_statements_cache_size_mb: + +# Maximum size of the key cache in memory. +# +# Each key cache hit saves 1 seek and each row cache hit saves 2 seeks at the +# minimum, sometimes more. The key cache is fairly tiny for the amount of +# time it saves, so it's worthwhile to use it at large numbers. +# The row cache saves even more time, but must contain the entire row, +# so it is extremely space-intensive. It's best to only use the +# row cache if you have hot rows or static rows. +# +# NOTE: if you reduce the size, you may not get you hottest keys loaded on startup. +# +# Default value is empty to make it "auto" (min(5% of Heap (in MB), 100MB)). Set to 0 to disable key cache. +key_cache_size_in_mb: + +# Duration in seconds after which Cassandra should +# save the key cache. Caches are saved to saved_caches_directory as +# specified in this configuration file. +# +# Saved caches greatly improve cold-start speeds, and is relatively cheap in +# terms of I/O for the key cache. Row cache saving is much more expensive and +# has limited use. +# +# Default is 14400 or 4 hours. +key_cache_save_period: 14400 + +# Number of keys from the key cache to save +# Disabled by default, meaning all keys are going to be saved +# key_cache_keys_to_save: 100 + +# Row cache implementation class name. Available implementations: +# +# org.apache.cassandra.cache.OHCProvider +# Fully off-heap row cache implementation (default). +# +# org.apache.cassandra.cache.SerializingCacheProvider +# This is the row cache implementation availabile +# in previous releases of Cassandra. +# row_cache_class_name: org.apache.cassandra.cache.OHCProvider + +# Maximum size of the row cache in memory. +# Please note that OHC cache implementation requires some additional off-heap memory to manage +# the map structures and some in-flight memory during operations before/after cache entries can be +# accounted against the cache capacity. This overhead is usually small compared to the whole capacity. +# Do not specify more memory that the system can afford in the worst usual situation and leave some +# headroom for OS block level cache. Do never allow your system to swap. +# +# Default value is 0, to disable row caching. +row_cache_size_in_mb: 0 + +# Duration in seconds after which Cassandra should save the row cache. +# Caches are saved to saved_caches_directory as specified in this configuration file. +# +# Saved caches greatly improve cold-start speeds, and is relatively cheap in +# terms of I/O for the key cache. Row cache saving is much more expensive and +# has limited use. +# +# Default is 0 to disable saving the row cache. +row_cache_save_period: 0 + +# Number of keys from the row cache to save. +# Specify 0 (which is the default), meaning all keys are going to be saved +# row_cache_keys_to_save: 100 + +# Maximum size of the counter cache in memory. +# +# Counter cache helps to reduce counter locks' contention for hot counter cells. +# In case of RF = 1 a counter cache hit will cause Cassandra to skip the read before +# write entirely. With RF > 1 a counter cache hit will still help to reduce the duration +# of the lock hold, helping with hot counter cell updates, but will not allow skipping +# the read entirely. Only the local (clock, count) tuple of a counter cell is kept +# in memory, not the whole counter, so it's relatively cheap. +# +# NOTE: if you reduce the size, you may not get you hottest keys loaded on startup. +# +# Default value is empty to make it "auto" (min(2.5% of Heap (in MB), 50MB)). Set to 0 to disable counter cache. +# NOTE: if you perform counter deletes and rely on low gcgs, you should disable the counter cache. +counter_cache_size_in_mb: + +# Duration in seconds after which Cassandra should +# save the counter cache (keys only). Caches are saved to saved_caches_directory as +# specified in this configuration file. +# +# Default is 7200 or 2 hours. +counter_cache_save_period: 7200 + +# Number of keys from the counter cache to save +# Disabled by default, meaning all keys are going to be saved +# counter_cache_keys_to_save: 100 + +# saved caches +# If not set, the default directory is $CASSANDRA_HOME/data/saved_caches. +saved_caches_directory: /var/lib/cassandra/saved_caches + +# commitlog_sync may be either "periodic" or "batch." +# +# When in batch mode, Cassandra won't ack writes until the commit log +# has been fsynced to disk. It will wait +# commitlog_sync_batch_window_in_ms milliseconds between fsyncs. +# This window should be kept short because the writer threads will +# be unable to do extra work while waiting. (You may need to increase +# concurrent_writes for the same reason.) +# +# commitlog_sync: batch +# commitlog_sync_batch_window_in_ms: 2 +# +# the other option is "periodic" where writes may be acked immediately +# and the CommitLog is simply synced every commitlog_sync_period_in_ms +# milliseconds. +commitlog_sync: periodic +commitlog_sync_period_in_ms: 10000 + +# The size of the individual commitlog file segments. A commitlog +# segment may be archived, deleted, or recycled once all the data +# in it (potentially from each columnfamily in the system) has been +# flushed to sstables. +# +# The default size is 32, which is almost always fine, but if you are +# archiving commitlog segments (see commitlog_archiving.properties), +# then you probably want a finer granularity of archiving; 8 or 16 MB +# is reasonable. +# Max mutation size is also configurable via max_mutation_size_in_kb setting in +# cassandra.yaml. The default is half the size commitlog_segment_size_in_mb * 1024. +# This should be positive and less than 2048. +# +# NOTE: If max_mutation_size_in_kb is set explicitly then commitlog_segment_size_in_mb must +# be set to at least twice the size of max_mutation_size_in_kb / 1024 +# +commitlog_segment_size_in_mb: 32 + +# Compression to apply to the commit log. If omitted, the commit log +# will be written uncompressed. LZ4, Snappy, and Deflate compressors +# are supported. +# commitlog_compression: +# - class_name: LZ4Compressor +# parameters: +# - + +# any class that implements the SeedProvider interface and has a +# constructor that takes a Map of parameters will do. +seed_provider: + # Addresses of hosts that are deemed contact points. + # Cassandra nodes use this list of hosts to find each other and learn + # the topology of the ring. You must change this if you are running + # multiple nodes! + - class_name: org.apache.cassandra.locator.SimpleSeedProvider + parameters: + # seeds is actually a comma-delimited list of addresses. + # Ex: ",," + - seeds: "172.17.0.2" + +# For workloads with more data than can fit in memory, Cassandra's +# bottleneck will be reads that need to fetch data from +# disk. "concurrent_reads" should be set to (16 * number_of_drives) in +# order to allow the operations to enqueue low enough in the stack +# that the OS and drives can reorder them. Same applies to +# "concurrent_counter_writes", since counter writes read the current +# values before incrementing and writing them back. +# +# On the other hand, since writes are almost never IO bound, the ideal +# number of "concurrent_writes" is dependent on the number of cores in +# your system; (8 * number_of_cores) is a good rule of thumb. +concurrent_reads: 32 +concurrent_writes: 32 +concurrent_counter_writes: 32 + +# For materialized view writes, as there is a read involved, so this should +# be limited by the less of concurrent reads or concurrent writes. +concurrent_materialized_view_writes: 32 + +# Maximum memory to use for sstable chunk cache and buffer pooling. +# 32MB of this are reserved for pooling buffers, the rest is used as an +# cache that holds uncompressed sstable chunks. +# Defaults to the smaller of 1/4 of heap or 512MB. This pool is allocated off-heap, +# so is in addition to the memory allocated for heap. The cache also has on-heap +# overhead which is roughly 128 bytes per chunk (i.e. 0.2% of the reserved size +# if the default 64k chunk size is used). +# Memory is only allocated when needed. +# file_cache_size_in_mb: 512 + +# Flag indicating whether to allocate on or off heap when the sstable buffer +# pool is exhausted, that is when it has exceeded the maximum memory +# file_cache_size_in_mb, beyond which it will not cache buffers but allocate on request. + +# buffer_pool_use_heap_if_exhausted: true + +# The strategy for optimizing disk read +# Possible values are: +# ssd (for solid state disks, the default) +# spinning (for spinning disks) +# disk_optimization_strategy: ssd + +# Total permitted memory to use for memtables. Cassandra will stop +# accepting writes when the limit is exceeded until a flush completes, +# and will trigger a flush based on memtable_cleanup_threshold +# If omitted, Cassandra will set both to 1/4 the size of the heap. +# memtable_heap_space_in_mb: 2048 +# memtable_offheap_space_in_mb: 2048 + +# memtable_cleanup_threshold is deprecated. The default calculation +# is the only reasonable choice. See the comments on memtable_flush_writers +# for more information. +# +# Ratio of occupied non-flushing memtable size to total permitted size +# that will trigger a flush of the largest memtable. Larger mct will +# mean larger flushes and hence less compaction, but also less concurrent +# flush activity which can make it difficult to keep your disks fed +# under heavy write load. +# +# memtable_cleanup_threshold defaults to 1 / (memtable_flush_writers + 1) +# memtable_cleanup_threshold: 0.11 + +# Specify the way Cassandra allocates and manages memtable memory. +# Options are: +# +# heap_buffers +# on heap nio buffers +# +# offheap_buffers +# off heap (direct) nio buffers +# +# offheap_objects +# off heap objects +memtable_allocation_type: heap_buffers + +# Total space to use for commit logs on disk. +# +# If space gets above this value, Cassandra will flush every dirty CF +# in the oldest segment and remove it. So a small total commitlog space +# will tend to cause more flush activity on less-active columnfamilies. +# +# The default value is the smaller of 8192, and 1/4 of the total space +# of the commitlog volume. +# +# commitlog_total_space_in_mb: 8192 + +# This sets the number of memtable flush writer threads per disk +# as well as the total number of memtables that can be flushed concurrently. +# These are generally a combination of compute and IO bound. +# +# Memtable flushing is more CPU efficient than memtable ingest and a single thread +# can keep up with the ingest rate of a whole server on a single fast disk +# until it temporarily becomes IO bound under contention typically with compaction. +# At that point you need multiple flush threads. At some point in the future +# it may become CPU bound all the time. +# +# You can tell if flushing is falling behind using the MemtablePool.BlockedOnAllocation +# metric which should be 0, but will be non-zero if threads are blocked waiting on flushing +# to free memory. +# +# memtable_flush_writers defaults to two for a single data directory. +# This means that two memtables can be flushed concurrently to the single data directory. +# If you have multiple data directories the default is one memtable flushing at a time +# but the flush will use a thread per data directory so you will get two or more writers. +# +# Two is generally enough to flush on a fast disk [array] mounted as a single data directory. +# Adding more flush writers will result in smaller more frequent flushes that introduce more +# compaction overhead. +# +# There is a direct tradeoff between number of memtables that can be flushed concurrently +# and flush size and frequency. More is not better you just need enough flush writers +# to never stall waiting for flushing to free memory. +# +#memtable_flush_writers: 2 + +# Total space to use for change-data-capture logs on disk. +# +# If space gets above this value, Cassandra will throw WriteTimeoutException +# on Mutations including tables with CDC enabled. A CDCCompactor is responsible +# for parsing the raw CDC logs and deleting them when parsing is completed. +# +# The default value is the min of 4096 mb and 1/8th of the total space +# of the drive where cdc_raw_directory resides. +# cdc_total_space_in_mb: 4096 + +# When we hit our cdc_raw limit and the CDCCompactor is either running behind +# or experiencing backpressure, we check at the following interval to see if any +# new space for cdc-tracked tables has been made available. Default to 250ms +# cdc_free_space_check_interval_ms: 250 + +# A fixed memory pool size in MB for for SSTable index summaries. If left +# empty, this will default to 5% of the heap size. If the memory usage of +# all index summaries exceeds this limit, SSTables with low read rates will +# shrink their index summaries in order to meet this limit. However, this +# is a best-effort process. In extreme conditions Cassandra may need to use +# more than this amount of memory. +index_summary_capacity_in_mb: + +# How frequently index summaries should be resampled. This is done +# periodically to redistribute memory from the fixed-size pool to sstables +# proportional their recent read rates. Setting to -1 will disable this +# process, leaving existing index summaries at their current sampling level. +index_summary_resize_interval_in_minutes: 60 + +# Whether to, when doing sequential writing, fsync() at intervals in +# order to force the operating system to flush the dirty +# buffers. Enable this to avoid sudden dirty buffer flushing from +# impacting read latencies. Almost always a good idea on SSDs; not +# necessarily on platters. +trickle_fsync: false +trickle_fsync_interval_in_kb: 10240 + +# TCP port, for commands and data +# For security reasons, you should not expose this port to the internet. Firewall it if needed. +storage_port: 7000 + +# SSL port, for encrypted communication. Unused unless enabled in +# encryption_options +# For security reasons, you should not expose this port to the internet. Firewall it if needed. +ssl_storage_port: 7001 + +# Address or interface to bind to and tell other Cassandra nodes to connect to. +# You _must_ change this if you want multiple nodes to be able to communicate! +# +# Set listen_address OR listen_interface, not both. +# +# Leaving it blank leaves it up to InetAddress.getLocalHost(). This +# will always do the Right Thing _if_ the node is properly configured +# (hostname, name resolution, etc), and the Right Thing is to use the +# address associated with the hostname (it might not be). +# +# Setting listen_address to 0.0.0.0 is always wrong. +# +listen_address: 172.17.0.2 + +# Set listen_address OR listen_interface, not both. Interfaces must correspond +# to a single address, IP aliasing is not supported. +# listen_interface: eth0 + +# If you choose to specify the interface by name and the interface has an ipv4 and an ipv6 address +# you can specify which should be chosen using listen_interface_prefer_ipv6. If false the first ipv4 +# address will be used. If true the first ipv6 address will be used. Defaults to false preferring +# ipv4. If there is only one address it will be selected regardless of ipv4/ipv6. +# listen_interface_prefer_ipv6: false + +# Address to broadcast to other Cassandra nodes +# Leaving this blank will set it to the same value as listen_address +broadcast_address: 172.17.0.2 + +# When using multiple physical network interfaces, set this +# to true to listen on broadcast_address in addition to +# the listen_address, allowing nodes to communicate in both +# interfaces. +# Ignore this property if the network configuration automatically +# routes between the public and private networks such as EC2. +# listen_on_broadcast_address: false + +# Internode authentication backend, implementing IInternodeAuthenticator; +# used to allow/disallow connections from peer nodes. +# internode_authenticator: org.apache.cassandra.auth.AllowAllInternodeAuthenticator + +# Whether to start the native transport server. +# Please note that the address on which the native transport is bound is the +# same as the rpc_address. The port however is different and specified below. +start_native_transport: true +# port for the CQL native transport to listen for clients on +# For security reasons, you should not expose this port to the internet. Firewall it if needed. +native_transport_port: 9042 +# Enabling native transport encryption in client_encryption_options allows you to either use +# encryption for the standard port or to use a dedicated, additional port along with the unencrypted +# standard native_transport_port. +# Enabling client encryption and keeping native_transport_port_ssl disabled will use encryption +# for native_transport_port. Setting native_transport_port_ssl to a different value +# from native_transport_port will use encryption for native_transport_port_ssl while +# keeping native_transport_port unencrypted. +# native_transport_port_ssl: 9142 +# The maximum threads for handling requests when the native transport is used. +# This is similar to rpc_max_threads though the default differs slightly (and +# there is no native_transport_min_threads, idle threads will always be stopped +# after 30 seconds). +# native_transport_max_threads: 128 +# +# The maximum size of allowed frame. Frame (requests) larger than this will +# be rejected as invalid. The default is 256MB. If you're changing this parameter, +# you may want to adjust max_value_size_in_mb accordingly. This should be positive and less than 2048. +# native_transport_max_frame_size_in_mb: 256 + +# The maximum number of concurrent client connections. +# The default is -1, which means unlimited. +# native_transport_max_concurrent_connections: -1 + +# The maximum number of concurrent client connections per source ip. +# The default is -1, which means unlimited. +# native_transport_max_concurrent_connections_per_ip: -1 + +# Whether to start the thrift rpc server. +start_rpc: false + +# The address or interface to bind the Thrift RPC service and native transport +# server to. +# +# Set rpc_address OR rpc_interface, not both. +# +# Leaving rpc_address blank has the same effect as on listen_address +# (i.e. it will be based on the configured hostname of the node). +# +# Note that unlike listen_address, you can specify 0.0.0.0, but you must also +# set broadcast_rpc_address to a value other than 0.0.0.0. +# +# For security reasons, you should not expose this port to the internet. Firewall it if needed. +rpc_address: 0.0.0.0 + +# Set rpc_address OR rpc_interface, not both. Interfaces must correspond +# to a single address, IP aliasing is not supported. +# rpc_interface: eth1 + +# If you choose to specify the interface by name and the interface has an ipv4 and an ipv6 address +# you can specify which should be chosen using rpc_interface_prefer_ipv6. If false the first ipv4 +# address will be used. If true the first ipv6 address will be used. Defaults to false preferring +# ipv4. If there is only one address it will be selected regardless of ipv4/ipv6. +# rpc_interface_prefer_ipv6: false + +# port for Thrift to listen for clients on +rpc_port: 9160 + +# RPC address to broadcast to drivers and other Cassandra nodes. This cannot +# be set to 0.0.0.0. If left blank, this will be set to the value of +# rpc_address. If rpc_address is set to 0.0.0.0, broadcast_rpc_address must +# be set. +broadcast_rpc_address: 172.17.0.2 + +# enable or disable keepalive on rpc/native connections +rpc_keepalive: true + +# Cassandra provides two out-of-the-box options for the RPC Server: +# +# sync +# One thread per thrift connection. For a very large number of clients, memory +# will be your limiting factor. On a 64 bit JVM, 180KB is the minimum stack size +# per thread, and that will correspond to your use of virtual memory (but physical memory +# may be limited depending on use of stack space). +# +# hsha +# Stands for "half synchronous, half asynchronous." All thrift clients are handled +# asynchronously using a small number of threads that does not vary with the amount +# of thrift clients (and thus scales well to many clients). The rpc requests are still +# synchronous (one thread per active request). If hsha is selected then it is essential +# that rpc_max_threads is changed from the default value of unlimited. +# +# The default is sync because on Windows hsha is about 30% slower. On Linux, +# sync/hsha performance is about the same, with hsha of course using less memory. +# +# Alternatively, can provide your own RPC server by providing the fully-qualified class name +# of an o.a.c.t.TServerFactory that can create an instance of it. +rpc_server_type: sync + +# Uncomment rpc_min|max_thread to set request pool size limits. +# +# Regardless of your choice of RPC server (see above), the number of maximum requests in the +# RPC thread pool dictates how many concurrent requests are possible (but if you are using the sync +# RPC server, it also dictates the number of clients that can be connected at all). +# +# The default is unlimited and thus provides no protection against clients overwhelming the server. You are +# encouraged to set a maximum that makes sense for you in production, but do keep in mind that +# rpc_max_threads represents the maximum number of client requests this server may execute concurrently. +# +# rpc_min_threads: 16 +# rpc_max_threads: 2048 + +# uncomment to set socket buffer sizes on rpc connections +# rpc_send_buff_size_in_bytes: +# rpc_recv_buff_size_in_bytes: + +# Uncomment to set socket buffer size for internode communication +# Note that when setting this, the buffer size is limited by net.core.wmem_max +# and when not setting it it is defined by net.ipv4.tcp_wmem +# See also: +# /proc/sys/net/core/wmem_max +# /proc/sys/net/core/rmem_max +# /proc/sys/net/ipv4/tcp_wmem +# /proc/sys/net/ipv4/tcp_wmem +# and 'man tcp' +# internode_send_buff_size_in_bytes: + +# Uncomment to set socket buffer size for internode communication +# Note that when setting this, the buffer size is limited by net.core.wmem_max +# and when not setting it it is defined by net.ipv4.tcp_wmem +# internode_recv_buff_size_in_bytes: + +# Frame size for thrift (maximum message length). +thrift_framed_transport_size_in_mb: 15 + +# Set to true to have Cassandra create a hard link to each sstable +# flushed or streamed locally in a backups/ subdirectory of the +# keyspace data. Removing these links is the operator's +# responsibility. +incremental_backups: false + +# Whether or not to take a snapshot before each compaction. Be +# careful using this option, since Cassandra won't clean up the +# snapshots for you. Mostly useful if you're paranoid when there +# is a data format change. +snapshot_before_compaction: false + +# Whether or not a snapshot is taken of the data before keyspace truncation +# or dropping of column families. The STRONGLY advised default of true +# should be used to provide data safety. If you set this flag to false, you will +# lose data on truncation or drop. +auto_snapshot: true + +# Granularity of the collation index of rows within a partition. +# Increase if your rows are large, or if you have a very large +# number of rows per partition. The competing goals are these: +# +# - a smaller granularity means more index entries are generated +# and looking up rows withing the partition by collation column +# is faster +# - but, Cassandra will keep the collation index in memory for hot +# rows (as part of the key cache), so a larger granularity means +# you can cache more hot rows +column_index_size_in_kb: 64 + +# Per sstable indexed key cache entries (the collation index in memory +# mentioned above) exceeding this size will not be held on heap. +# This means that only partition information is held on heap and the +# index entries are read from disk. +# +# Note that this size refers to the size of the +# serialized index information and not the size of the partition. +column_index_cache_size_in_kb: 2 + +# Number of simultaneous compactions to allow, NOT including +# validation "compactions" for anti-entropy repair. Simultaneous +# compactions can help preserve read performance in a mixed read/write +# workload, by mitigating the tendency of small sstables to accumulate +# during a single long running compactions. The default is usually +# fine and if you experience problems with compaction running too +# slowly or too fast, you should look at +# compaction_throughput_mb_per_sec first. +# +# concurrent_compactors defaults to the smaller of (number of disks, +# number of cores), with a minimum of 2 and a maximum of 8. +# +# If your data directories are backed by SSD, you should increase this +# to the number of cores. +#concurrent_compactors: 1 + +# Throttles compaction to the given total throughput across the entire +# system. The faster you insert data, the faster you need to compact in +# order to keep the sstable count down, but in general, setting this to +# 16 to 32 times the rate you are inserting data is more than sufficient. +# Setting this to 0 disables throttling. Note that this account for all types +# of compaction, including validation compaction. +compaction_throughput_mb_per_sec: 16 + +# When compacting, the replacement sstable(s) can be opened before they +# are completely written, and used in place of the prior sstables for +# any range that has been written. This helps to smoothly transfer reads +# between the sstables, reducing page cache churn and keeping hot rows hot +sstable_preemptive_open_interval_in_mb: 50 + +# Throttles all outbound streaming file transfers on this node to the +# given total throughput in Mbps. This is necessary because Cassandra does +# mostly sequential IO when streaming data during bootstrap or repair, which +# can lead to saturating the network connection and degrading rpc performance. +# When unset, the default is 200 Mbps or 25 MB/s. +# stream_throughput_outbound_megabits_per_sec: 200 + +# Throttles all streaming file transfer between the datacenters, +# this setting allows users to throttle inter dc stream throughput in addition +# to throttling all network stream traffic as configured with +# stream_throughput_outbound_megabits_per_sec +# When unset, the default is 200 Mbps or 25 MB/s +# inter_dc_stream_throughput_outbound_megabits_per_sec: 200 + +# How long the coordinator should wait for read operations to complete +read_request_timeout_in_ms: 5000 +# How long the coordinator should wait for seq or index scans to complete +range_request_timeout_in_ms: 10000 +# How long the coordinator should wait for writes to complete +write_request_timeout_in_ms: 2000 +# How long the coordinator should wait for counter writes to complete +counter_write_request_timeout_in_ms: 5000 +# How long a coordinator should continue to retry a CAS operation +# that contends with other proposals for the same row +cas_contention_timeout_in_ms: 1000 +# How long the coordinator should wait for truncates to complete +# (This can be much longer, because unless auto_snapshot is disabled +# we need to flush first so we can snapshot before removing the data.) +truncate_request_timeout_in_ms: 60000 +# The default timeout for other, miscellaneous operations +request_timeout_in_ms: 10000 + +# How long before a node logs slow queries. Select queries that take longer than +# this timeout to execute, will generate an aggregated log message, so that slow queries +# can be identified. Set this value to zero to disable slow query logging. +slow_query_log_timeout_in_ms: 500 + +# Enable operation timeout information exchange between nodes to accurately +# measure request timeouts. If disabled, replicas will assume that requests +# were forwarded to them instantly by the coordinator, which means that +# under overload conditions we will waste that much extra time processing +# already-timed-out requests. +# +# Warning: before enabling this property make sure to ntp is installed +# and the times are synchronized between the nodes. +cross_node_timeout: false + +# Set keep-alive period for streaming +# This node will send a keep-alive message periodically with this period. +# If the node does not receive a keep-alive message from the peer for +# 2 keep-alive cycles the stream session times out and fail +# Default value is 300s (5 minutes), which means stalled stream +# times out in 10 minutes by default +# streaming_keep_alive_period_in_secs: 300 + +# phi value that must be reached for a host to be marked down. +# most users should never need to adjust this. +# phi_convict_threshold: 8 + +# endpoint_snitch -- Set this to a class that implements +# IEndpointSnitch. The snitch has two functions: +# +# - it teaches Cassandra enough about your network topology to route +# requests efficiently +# - it allows Cassandra to spread replicas around your cluster to avoid +# correlated failures. It does this by grouping machines into +# "datacenters" and "racks." Cassandra will do its best not to have +# more than one replica on the same "rack" (which may not actually +# be a physical location) +# +# CASSANDRA WILL NOT ALLOW YOU TO SWITCH TO AN INCOMPATIBLE SNITCH +# ONCE DATA IS INSERTED INTO THE CLUSTER. This would cause data loss. +# This means that if you start with the default SimpleSnitch, which +# locates every node on "rack1" in "datacenter1", your only options +# if you need to add another datacenter are GossipingPropertyFileSnitch +# (and the older PFS). From there, if you want to migrate to an +# incompatible snitch like Ec2Snitch you can do it by adding new nodes +# under Ec2Snitch (which will locate them in a new "datacenter") and +# decommissioning the old ones. +# +# Out of the box, Cassandra provides: +# +# SimpleSnitch: +# Treats Strategy order as proximity. This can improve cache +# locality when disabling read repair. Only appropriate for +# single-datacenter deployments. +# +# GossipingPropertyFileSnitch +# This should be your go-to snitch for production use. The rack +# and datacenter for the local node are defined in +# cassandra-rackdc.properties and propagated to other nodes via +# gossip. If cassandra-topology.properties exists, it is used as a +# fallback, allowing migration from the PropertyFileSnitch. +# +# PropertyFileSnitch: +# Proximity is determined by rack and data center, which are +# explicitly configured in cassandra-topology.properties. +# +# Ec2Snitch: +# Appropriate for EC2 deployments in a single Region. Loads Region +# and Availability Zone information from the EC2 API. The Region is +# treated as the datacenter, and the Availability Zone as the rack. +# Only private IPs are used, so this will not work across multiple +# Regions. +# +# Ec2MultiRegionSnitch: +# Uses public IPs as broadcast_address to allow cross-region +# connectivity. (Thus, you should set seed addresses to the public +# IP as well.) You will need to open the storage_port or +# ssl_storage_port on the public IP firewall. (For intra-Region +# traffic, Cassandra will switch to the private IP after +# establishing a connection.) +# +# RackInferringSnitch: +# Proximity is determined by rack and data center, which are +# assumed to correspond to the 3rd and 2nd octet of each node's IP +# address, respectively. Unless this happens to match your +# deployment conventions, this is best used as an example of +# writing a custom Snitch class and is provided in that spirit. +# +# You can use a custom Snitch by setting this to the full class name +# of the snitch, which will be assumed to be on your classpath. +endpoint_snitch: SimpleSnitch + +# controls how often to perform the more expensive part of host score +# calculation +dynamic_snitch_update_interval_in_ms: 100 +# controls how often to reset all host scores, allowing a bad host to +# possibly recover +dynamic_snitch_reset_interval_in_ms: 600000 +# if set greater than zero and read_repair_chance is < 1.0, this will allow +# 'pinning' of replicas to hosts in order to increase cache capacity. +# The badness threshold will control how much worse the pinned host has to be +# before the dynamic snitch will prefer other replicas over it. This is +# expressed as a double which represents a percentage. Thus, a value of +# 0.2 means Cassandra would continue to prefer the static snitch values +# until the pinned host was 20% worse than the fastest. +dynamic_snitch_badness_threshold: 0.1 + +# request_scheduler -- Set this to a class that implements +# RequestScheduler, which will schedule incoming client requests +# according to the specific policy. This is useful for multi-tenancy +# with a single Cassandra cluster. +# NOTE: This is specifically for requests from the client and does +# not affect inter node communication. +# org.apache.cassandra.scheduler.NoScheduler - No scheduling takes place +# org.apache.cassandra.scheduler.RoundRobinScheduler - Round robin of +# client requests to a node with a separate queue for each +# request_scheduler_id. The scheduler is further customized by +# request_scheduler_options as described below. +request_scheduler: org.apache.cassandra.scheduler.NoScheduler + +# Scheduler Options vary based on the type of scheduler +# +# NoScheduler +# Has no options +# +# RoundRobin +# throttle_limit +# The throttle_limit is the number of in-flight +# requests per client. Requests beyond +# that limit are queued up until +# running requests can complete. +# The value of 80 here is twice the number of +# concurrent_reads + concurrent_writes. +# default_weight +# default_weight is optional and allows for +# overriding the default which is 1. +# weights +# Weights are optional and will default to 1 or the +# overridden default_weight. The weight translates into how +# many requests are handled during each turn of the +# RoundRobin, based on the scheduler id. +# +# request_scheduler_options: +# throttle_limit: 80 +# default_weight: 5 +# weights: +# Keyspace1: 1 +# Keyspace2: 5 + +# request_scheduler_id -- An identifier based on which to perform +# the request scheduling. Currently the only valid option is keyspace. +# request_scheduler_id: keyspace + +# Enable or disable inter-node encryption +# JVM defaults for supported SSL socket protocols and cipher suites can +# be replaced using custom encryption options. This is not recommended +# unless you have policies in place that dictate certain settings, or +# need to disable vulnerable ciphers or protocols in case the JVM cannot +# be updated. +# FIPS compliant settings can be configured at JVM level and should not +# involve changing encryption settings here: +# https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/FIPS.html +# *NOTE* No custom encryption options are enabled at the moment +# The available internode options are : all, none, dc, rack +# +# If set to dc cassandra will encrypt the traffic between the DCs +# If set to rack cassandra will encrypt the traffic between the racks +# +# The passwords used in these options must match the passwords used when generating +# the keystore and truststore. For instructions on generating these files, see: +# http://download.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html#CreateKeystore +# +server_encryption_options: + internode_encryption: none + keystore: conf/.keystore + keystore_password: cassandra + truststore: conf/.truststore + truststore_password: cassandra + # More advanced defaults below: + # protocol: TLS + # algorithm: SunX509 + # store_type: JKS + # cipher_suites: [TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA] + # require_client_auth: false + # require_endpoint_verification: false + +# enable or disable client/server encryption. +client_encryption_options: + enabled: false + # If enabled and optional is set to true encrypted and unencrypted connections are handled. + optional: false + keystore: conf/.keystore + keystore_password: cassandra + # require_client_auth: false + # Set trustore and truststore_password if require_client_auth is true + # truststore: conf/.truststore + # truststore_password: cassandra + # More advanced defaults below: + # protocol: TLS + # algorithm: SunX509 + # store_type: JKS + # cipher_suites: [TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA] + +# internode_compression controls whether traffic between nodes is +# compressed. +# Can be: +# +# all +# all traffic is compressed +# +# dc +# traffic between different datacenters is compressed +# +# none +# nothing is compressed. +internode_compression: dc + +# Enable or disable tcp_nodelay for inter-dc communication. +# Disabling it will result in larger (but fewer) network packets being sent, +# reducing overhead from the TCP protocol itself, at the cost of increasing +# latency if you block for cross-datacenter responses. +inter_dc_tcp_nodelay: false + +# TTL for different trace types used during logging of the repair process. +tracetype_query_ttl: 86400 +tracetype_repair_ttl: 604800 + +# By default, Cassandra logs GC Pauses greater than 200 ms at INFO level +# This threshold can be adjusted to minimize logging if necessary +# gc_log_threshold_in_ms: 200 + +# If unset, all GC Pauses greater than gc_log_threshold_in_ms will log at +# INFO level +# UDFs (user defined functions) are disabled by default. +# As of Cassandra 3.0 there is a sandbox in place that should prevent execution of evil code. +enable_user_defined_functions: false + +# Enables scripted UDFs (JavaScript UDFs). +# Java UDFs are always enabled, if enable_user_defined_functions is true. +# Enable this option to be able to use UDFs with "language javascript" or any custom JSR-223 provider. +# This option has no effect, if enable_user_defined_functions is false. +enable_scripted_user_defined_functions: false + +# The default Windows kernel timer and scheduling resolution is 15.6ms for power conservation. +# Lowering this value on Windows can provide much tighter latency and better throughput, however +# some virtualized environments may see a negative performance impact from changing this setting +# below their system default. The sysinternals 'clockres' tool can confirm your system's default +# setting. +windows_timer_interval: 1 + + +# Enables encrypting data at-rest (on disk). Different key providers can be plugged in, but the default reads from +# a JCE-style keystore. A single keystore can hold multiple keys, but the one referenced by +# the "key_alias" is the only key that will be used for encrypt opertaions; previously used keys +# can still (and should!) be in the keystore and will be used on decrypt operations +# (to handle the case of key rotation). +# +# It is strongly recommended to download and install Java Cryptography Extension (JCE) +# Unlimited Strength Jurisdiction Policy Files for your version of the JDK. +# (current link: http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html) +# +# Currently, only the following file types are supported for transparent data encryption, although +# more are coming in future cassandra releases: commitlog, hints +transparent_data_encryption_options: + enabled: false + chunk_length_kb: 64 + cipher: AES/CBC/PKCS5Padding + key_alias: testing:1 + # CBC IV length for AES needs to be 16 bytes (which is also the default size) + # iv_length: 16 + key_provider: + - class_name: org.apache.cassandra.security.JKSKeyProvider + parameters: + - keystore: conf/.keystore + keystore_password: cassandra + store_type: JCEKS + key_password: cassandra + + +##################### +# SAFETY THRESHOLDS # +##################### + +# When executing a scan, within or across a partition, we need to keep the +# tombstones seen in memory so we can return them to the coordinator, which +# will use them to make sure other replicas also know about the deleted rows. +# With workloads that generate a lot of tombstones, this can cause performance +# problems and even exaust the server heap. +# (http://www.datastax.com/dev/blog/cassandra-anti-patterns-queues-and-queue-like-datasets) +# Adjust the thresholds here if you understand the dangers and want to +# scan more tombstones anyway. These thresholds may also be adjusted at runtime +# using the StorageService mbean. +tombstone_warn_threshold: 1000 +tombstone_failure_threshold: 100000 + +# Log WARN on any multiple-partition batch size exceeding this value. 5kb per batch by default. +# Caution should be taken on increasing the size of this threshold as it can lead to node instability. +batch_size_warn_threshold_in_kb: 5 + +# Fail any multiple-partition batch exceeding this value. 50kb (10x warn threshold) by default. +batch_size_fail_threshold_in_kb: 50 + +# Log WARN on any batches not of type LOGGED than span across more partitions than this limit +unlogged_batch_across_partitions_warn_threshold: 10 + +# Log a warning when compacting partitions larger than this value +compaction_large_partition_warning_threshold_mb: 100 + +# GC Pauses greater than gc_warn_threshold_in_ms will be logged at WARN level +# Adjust the threshold based on your application throughput requirement +# By default, Cassandra logs GC Pauses greater than 200 ms at INFO level +gc_warn_threshold_in_ms: 1000 + +# Maximum size of any value in SSTables. Safety measure to detect SSTable corruption +# early. Any value size larger than this threshold will result into marking an SSTable +# as corrupted. This should be positive and less than 2048. +# max_value_size_in_mb: 256 + +# Back-pressure settings # +# If enabled, the coordinator will apply the back-pressure strategy specified below to each mutation +# sent to replicas, with the aim of reducing pressure on overloaded replicas. +back_pressure_enabled: false +# The back-pressure strategy applied. +# The default implementation, RateBasedBackPressure, takes three arguments: +# high ratio, factor, and flow type, and uses the ratio between incoming mutation responses and outgoing mutation requests. +# If below high ratio, outgoing mutations are rate limited according to the incoming rate decreased by the given factor; +# if above high ratio, the rate limiting is increased by the given factor; +# such factor is usually best configured between 1 and 10, use larger values for a faster recovery +# at the expense of potentially more dropped mutations; +# the rate limiting is applied according to the flow type: if FAST, it's rate limited at the speed of the fastest replica, +# if SLOW at the speed of the slowest one. +# New strategies can be added. Implementors need to implement org.apache.cassandra.net.BackpressureStrategy and +# provide a public constructor accepting a Map. +back_pressure_strategy: + - class_name: org.apache.cassandra.net.RateBasedBackPressure + parameters: + - high_ratio: 0.90 + factor: 5 + flow: FAST + +# Coalescing Strategies # +# Coalescing multiples messages turns out to significantly boost message processing throughput (think doubling or more). +# On bare metal, the floor for packet processing throughput is high enough that many applications won't notice, but in +# virtualized environments, the point at which an application can be bound by network packet processing can be +# surprisingly low compared to the throughput of task processing that is possible inside a VM. It's not that bare metal +# doesn't benefit from coalescing messages, it's that the number of packets a bare metal network interface can process +# is sufficient for many applications such that no load starvation is experienced even without coalescing. +# There are other benefits to coalescing network messages that are harder to isolate with a simple metric like messages +# per second. By coalescing multiple tasks together, a network thread can process multiple messages for the cost of one +# trip to read from a socket, and all the task submission work can be done at the same time reducing context switching +# and increasing cache friendliness of network message processing. +# See CASSANDRA-8692 for details. + +# Strategy to use for coalescing messages in OutboundTcpConnection. +# Can be fixed, movingaverage, timehorizon, disabled (default). +# You can also specify a subclass of CoalescingStrategies.CoalescingStrategy by name. +# otc_coalescing_strategy: DISABLED + +# How many microseconds to wait for coalescing. For fixed strategy this is the amount of time after the first +# message is received before it will be sent with any accompanying messages. For moving average this is the +# maximum amount of time that will be waited as well as the interval at which messages must arrive on average +# for coalescing to be enabled. +# otc_coalescing_window_us: 200 + +# Do not try to coalesce messages if we already got that many messages. This should be more than 2 and less than 128. +# otc_coalescing_enough_coalesced_messages: 8 + +# How many milliseconds to wait between two expiration runs on the backlog (queue) of the OutboundTcpConnection. +# Expiration is done if messages are piling up in the backlog. Droppable messages are expired to free the memory +# taken by expired messages. The interval should be between 0 and 1000, and in most installations the default value +# will be appropriate. A smaller value could potentially expire messages slightly sooner at the expense of more CPU +# time and queue contention while iterating the backlog of messages. +# An interval of 0 disables any wait time, which is the behavior of former Cassandra versions. +# +# otc_backlog_expiration_interval_ms: 200 \ No newline at end of file diff --git a/modules/cassandra/src/test/resources/initial.cql b/modules/cassandra/src/test/resources/initial.cql new file mode 100644 index 00000000000..2caad3746f8 --- /dev/null +++ b/modules/cassandra/src/test/resources/initial.cql @@ -0,0 +1,7 @@ +CREATE KEYSPACE keySpaceTest WITH replication = {'class': 'SimpleStrategy', 'replication_factor' : 1}; + +USE keySpaceTest; + +CREATE TABLE catalog_category (id bigint primary key, name text); + +INSERT INTO catalog_category (id, name) VALUES (1, 'test_category'); \ No newline at end of file diff --git a/modules/cassandra/src/test/resources/logback-test.xml b/modules/cassandra/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..fc48b14c7de --- /dev/null +++ b/modules/cassandra/src/test/resources/logback-test.xml @@ -0,0 +1,17 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + + \ No newline at end of file diff --git a/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java b/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java index e9fa2d18a80..7d89517f200 100644 --- a/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java +++ b/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java @@ -278,19 +278,33 @@ public static void executeDatabaseScript(DatabaseDelegate databaseDelegate, Stri } } - private static class ScriptParseException extends RuntimeException { - public ScriptParseException(String format, String scriptPath) { - super(String.format(format, scriptPath)); - } - } + public static class ScriptLoadException extends RuntimeException { + public ScriptLoadException(String message) { + super(message); + } - public static class ScriptStatementFailedException extends RuntimeException { - public ScriptStatementFailedException(String statement, int lineNumber, String scriptPath, Exception ex) { - super(String.format("Script execution failed (%s:%d): %s", scriptPath, lineNumber, statement), ex); - } - } + public ScriptLoadException(String message, Throwable cause) { + super(message, cause); + } + } + + private static class ScriptParseException extends RuntimeException { + public ScriptParseException(String format, String scriptPath) { + super(String.format(format, scriptPath)); + } + } + + public static class ScriptStatementFailedException extends RuntimeException { + public ScriptStatementFailedException(String statement, int lineNumber, String scriptPath) { + this(statement, lineNumber, scriptPath, null); + } + + public ScriptStatementFailedException(String statement, int lineNumber, String scriptPath, Exception ex) { + super(String.format("Script execution failed (%s:%d): %s", scriptPath, lineNumber, statement), ex); + } + } - private static class UncategorizedScriptException extends RuntimeException { + public static class UncategorizedScriptException extends RuntimeException { public UncategorizedScriptException(String s, Exception ex) { super(s, ex); } From 2918f4288bc24a18ac560c5c0f6467fe43fc8286 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 10 Jul 2018 16:37:26 +0200 Subject: [PATCH 0359/5615] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90b09818687..033009d251f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ All notable changes to this project will be documented in this file. - Make JdbcDatabaseContainer#getDriverClassName public [\#743](https://github.com/testcontainers/testcontainers-java/pull/743). - enable `copyFileToContainer` feature during container startup [\#742](https://github.com/testcontainers/testcontainers-java/pull/742). - avoid using file mounting in KafkaContainer [\#775](https://github.com/testcontainers/testcontainers-java/pull/775). +- Added Apache Cassandra module [\#776](https://github.com/testcontainers/testcontainers-java/pull/776). ## [1.8.0] - 2018-06-14 From bc495a5c899559b7e4452dcd2ec49c4856bdbbc7 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 10 Jul 2018 22:41:13 +0200 Subject: [PATCH 0360/5615] Generate "exclusions" in POM (#772) --- gradle/publishing.gradle | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/gradle/publishing.gradle b/gradle/publishing.gradle index aecb64b0aa2..14c385144ba 100644 --- a/gradle/publishing.gradle +++ b/gradle/publishing.gradle @@ -58,6 +58,16 @@ publishing { appendNode('artifactId', dependency.name) appendNode('version', dependency.version) appendNode('scope', scope) + + if (dependency instanceof ModuleDependency && !dependency.excludeRules.empty) { + def excludesNode = appendNode('exclusions') + for (rule in dependency.excludeRules) { + excludesNode.appendNode('exclusion').with { + appendNode('groupId', rule.group) + appendNode('artifactId', rule.module) + } + } + } } } From 0292ce20125088f88d26b79bc9b3a6375dc68224 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 10 Jul 2018 21:52:06 +0100 Subject: [PATCH 0361/5615] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 033009d251f..390997379ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ All notable changes to this project will be documented in this file. - Linux/Mac: Added support for docker credential helpers so that images may be pulled from private registries. See [\#729](https://github.com/testcontainers/testcontainers-java/issues/729), [\#647](https://github.com/testcontainers/testcontainers-java/issues/647) and [\#567](https://github.com/testcontainers/testcontainers-java/issues/567). - Ensure that the `COMPOSE_FILE` environment variable is populated with all relevant compose file names when running docker-compose in local mode [\#755](https://github.com/testcontainers/testcontainers-java/issues/755). - Fixed issue whereby specified command in MariaDB image was not being applied. ([\#534](https://github.com/testcontainers/testcontainers-java/issues/534)) +- Changed Oracle thin URL to support both Oracle 11 and 12 XE ([\#769](https://github.com/testcontainers/testcontainers-java/issues/769)) +- Ensure that full JDBC URL query string is passed to JdbcDatabaseDelegate during initscript invocation ([\#741](https://github.com/testcontainers/testcontainers-java/issues/741); fixes [\#727](https://github.com/testcontainers/testcontainers-java/issues/727)) +- Ensure that necessary transitive dependency inclusions are applied to generated project POMs ([\#772](https://github.com/testcontainers/testcontainers-java/issues/772); fixes [\#753](https://github.com/testcontainers/testcontainers-java/issues/753) and [\#652](https://github.com/testcontainers/testcontainers-java/issues/652)) ### Changed - Update Apache Pulsar module to 2.0.1 [\#760](https://github.com/testcontainers/testcontainers-java/issues/760). From 3c583b3cffe4b0f9a9b5d01956763f2151c8ec72 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 10 Jul 2018 22:05:47 +0100 Subject: [PATCH 0362/5615] Update changelog for 1.8.1 release --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 390997379ac..14a125a8809 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # Change Log All notable changes to this project will be documented in this file. -## UNRELEASED +## [1.8.1] - 2018-07-10 ### Fixed - Linux/Mac: Added support for docker credential helpers so that images may be pulled from private registries. See [\#729](https://github.com/testcontainers/testcontainers-java/issues/729), [\#647](https://github.com/testcontainers/testcontainers-java/issues/647) and [\#567](https://github.com/testcontainers/testcontainers-java/issues/567). - Ensure that the `COMPOSE_FILE` environment variable is populated with all relevant compose file names when running docker-compose in local mode [\#755](https://github.com/testcontainers/testcontainers-java/issues/755). From a714e529bcdf90eaf28cdf5397de8f6c7ccd2a34 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 11 Jul 2018 07:34:41 +0200 Subject: [PATCH 0363/5615] Update CHANGELOG.md --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14a125a8809..d4f14974087 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,14 @@ # Change Log All notable changes to this project will be documented in this file. +## UNRELEASED + +### Fixed + +### Changed + ## [1.8.1] - 2018-07-10 + ### Fixed - Linux/Mac: Added support for docker credential helpers so that images may be pulled from private registries. See [\#729](https://github.com/testcontainers/testcontainers-java/issues/729), [\#647](https://github.com/testcontainers/testcontainers-java/issues/647) and [\#567](https://github.com/testcontainers/testcontainers-java/issues/567). - Ensure that the `COMPOSE_FILE` environment variable is populated with all relevant compose file names when running docker-compose in local mode [\#755](https://github.com/testcontainers/testcontainers-java/issues/755). From 72ce82ab2919adde727785adec0c19d1969fd0be Mon Sep 17 00:00:00 2001 From: Arne Zelasko Date: Mon, 23 Jul 2018 15:09:32 +0200 Subject: [PATCH 0364/5615] Update Visible Assertions to 2.1.1 (#779) --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 3781c90002f..32901007b72 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -82,7 +82,7 @@ dependencies { exclude(group: 'org.jetbrains', module: 'annotations') } - compile 'org.rnorth.visible-assertions:visible-assertions:2.1.0' + compile 'org.rnorth.visible-assertions:visible-assertions:2.1.1' compile ('org.rnorth:tcp-unix-socket-proxy:1.0.2') { exclude(group: "log4j", module: "log4j") From f684292bb8f7951333df184a23bfb01e795cc9ea Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Mon, 23 Jul 2018 15:11:20 +0200 Subject: [PATCH 0365/5615] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4f14974087..d3290901ae8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ All notable changes to this project will be documented in this file. ### Changed +- Update Visible Assertions to 2.1.1 ([\#779](https://github.com/testcontainers/testcontainers-java/pull/779)). + ## [1.8.1] - 2018-07-10 ### Fixed From c1de818ec26fdf9ec215229ceb1fc464a202a678 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 25 Jul 2018 09:51:00 +0200 Subject: [PATCH 0366/5615] remove duplicates --- examples/.gitignore | 65 --------------------------------------------- examples/circle.yml | 12 --------- 2 files changed, 77 deletions(-) delete mode 100644 examples/.gitignore delete mode 100644 examples/circle.yml diff --git a/examples/.gitignore b/examples/.gitignore deleted file mode 100644 index c271c1e403f..00000000000 --- a/examples/.gitignore +++ /dev/null @@ -1,65 +0,0 @@ -# Created by .ignore support plugin (hsz.mobi) -### Maven template -target/ -pom.xml.tag -pom.xml.releaseBackup -pom.xml.versionsBackup -pom.xml.next -release.properties -dependency-reduced-pom.xml -buildNumber.properties -.mvn/timing.properties -### JetBrains template -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio - -*.iml - -## Directory-based project format: -.idea/ -# if you remove the above rule, at least ignore the following: - -# User-specific stuff: -# .idea/workspace.xml -# .idea/tasks.xml -# .idea/dictionaries - -# Sensitive or high-churn files: -# .idea/dataSources.ids -# .idea/dataSources.xml -# .idea/sqlDataSources.xml -# .idea/dynamic.xml -# .idea/uiDesigner.xml - -# Gradle: -# .idea/gradle.xml -# .idea/libraries - -# Mongo Explorer plugin: -# .idea/mongoSettings.xml - -## File-based project format: -*.ipr -*.iws - -## Plugin-specific files: - -# IntelliJ -/out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties - -# Eclipse -.project -.classpath -.settings -bin - diff --git a/examples/circle.yml b/examples/circle.yml deleted file mode 100644 index 6f904b0eed6..00000000000 --- a/examples/circle.yml +++ /dev/null @@ -1,12 +0,0 @@ -machine: - java: - version: oraclejdk8 - environment: - DOCKER_HOST: tcp://127.0.0.1:2376 - DOCKER_TLS_VERIFY: 0 - MAVEN_OPTS: -Xmx512m -dependencies: - override: - - "sudo docker -d -e lxc -s btrfs -H 0.0.0.0:2376 -H unix:///var/run/docker.sock --log-level=debug --debug=true": - background: true - - mvn install -DskipTests \ No newline at end of file From 6e7c8a6ef28002050325fe9e59deeca0fcd79a31 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 25 Jul 2018 15:56:14 +0200 Subject: [PATCH 0367/5615] Set KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS to 0 in KafkaContainer to speed up the tests (#782) --- .../main/java/org/testcontainers/containers/KafkaContainer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java index b9957e8d7c2..6396d6bcd7f 100644 --- a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java +++ b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java @@ -40,6 +40,7 @@ public KafkaContainer(String confluentPlatformVersion) { withEnv("KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR", "1"); withEnv("KAFKA_OFFSETS_TOPIC_NUM_PARTITIONS", "1"); withEnv("KAFKA_LOG_FLUSH_INTERVAL_MESSAGES", Long.MAX_VALUE + ""); + withEnv("KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS", "0"); } public KafkaContainer withEmbeddedZookeeper() { From 81d79f582d4758c8a7e12654a464660354f99100 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 25 Jul 2018 15:57:44 +0200 Subject: [PATCH 0368/5615] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3290901ae8..7d6c92f10a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. ### Changed - Update Visible Assertions to 2.1.1 ([\#779](https://github.com/testcontainers/testcontainers-java/pull/779)). +- KafkaContainer optimization (`group.initial.rebalance.delay.ms=0`) ([\#782](https://github.com/testcontainers/testcontainers-java/pull/782)). ## [1.8.1] - 2018-07-10 From 6217f32fa2944b4954be7511ee4691295d265bdd Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Thu, 26 Jul 2018 11:38:46 +0200 Subject: [PATCH 0369/5615] Add support for transparently using local images with docker-compose (#798) --- .../containers/DockerComposeContainer.java | 6 ++++- .../junit/DockerComposeLocalImageTest.java | 27 +++++++++++++++++++ .../src/test/resources/local-compose-test.yml | 6 +++++ 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 core/src/test/java/org/testcontainers/junit/DockerComposeLocalImageTest.java create mode 100644 core/src/test/resources/local-compose-test.yml diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index c98d092029b..272b4bc17f7 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -154,7 +154,11 @@ public void start() { synchronized (MUTEX) { registerContainersForShutdown(); if (pull) { - pullImages(); + try { + pullImages(); + } catch (ContainerLaunchException e) { + logger().warn("Exception while pulling images, using local images if available", e); + } } applyScaling(); // scale before up, so that all scaled instances are available first for linking createServices(); diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeLocalImageTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeLocalImageTest.java new file mode 100644 index 00000000000..890fa34b0f8 --- /dev/null +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeLocalImageTest.java @@ -0,0 +1,27 @@ +package org.testcontainers.junit; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.core.command.PullImageResultCallback; +import org.junit.Test; +import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.DockerComposeContainer; + +import java.io.File; + +public class DockerComposeLocalImageTest { + + @Test + public void usesLocalImageEvenWhenPullFails() throws InterruptedException { + tagImage("redis:4.0.10", "redis-local", "latest"); + + DockerComposeContainer composeContainer = new DockerComposeContainer(new File("src/test/resources/local-compose-test.yml")) + .withExposedService("redis", 6379); + composeContainer.start(); + } + + private void tagImage(String sourceImage, String targetImage, String targetTag) throws InterruptedException { + DockerClient client = DockerClientFactory.instance().client(); + client.pullImageCmd(sourceImage).exec(new PullImageResultCallback()).awaitCompletion(); + client.tagImageCmd(sourceImage, targetImage, targetTag).exec(); + } +} diff --git a/core/src/test/resources/local-compose-test.yml b/core/src/test/resources/local-compose-test.yml new file mode 100644 index 00000000000..8d4f20e6924 --- /dev/null +++ b/core/src/test/resources/local-compose-test.yml @@ -0,0 +1,6 @@ +version: '2' +services: + redis: + image: redis-local:latest + ports: + - 6379 From cdaf02cbf719ea12d9316b40c0ec9a72121d2f39 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Thu, 26 Jul 2018 11:42:58 +0200 Subject: [PATCH 0370/5615] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d6c92f10a8..6c6ff6941e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ All notable changes to this project will be documented in this file. ### Fixed +- Add support for transparently using local images with docker-compose ([\#798](https://github.com/testcontainers/testcontainers-java/pull/798), fixes [\#674](https://github.com/testcontainers/testcontainers-java/issues/674)) + ### Changed - Update Visible Assertions to 2.1.1 ([\#779](https://github.com/testcontainers/testcontainers-java/pull/779)). From 89072264898437fb32e1b530781b6a637b74fa78 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sat, 28 Jul 2018 17:57:16 +0200 Subject: [PATCH 0371/5615] fix okhttp transport --- .../dockerclient/transport/okhttp/OkHttpInvocationBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java index 17727aa3c16..6ded6d893b4 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java @@ -113,7 +113,7 @@ public void get(TypeReference typeReference, ResultCallback resultCall @SneakyThrows public InputStream post(Object entity) { Request request = requestBuilder - .post(RequestBody.create(null, objectMapper.writeValueAsBytes(entity))) + .post(RequestBody.create(MediaType.parse("application/json"), objectMapper.writeValueAsBytes(entity))) .build(); return execute(request).body().byteStream(); From 3593c128ed9471c55b41c255513cbc534f4b077e Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 30 Jul 2018 08:15:23 +0100 Subject: [PATCH 0372/5615] Fix #680: Avoid trailing slashes in tar entry filenames (#808) --- .../testcontainers/utility/MountableFile.java | 9 ++++- .../utility/MountableFileTest.java | 38 +++++++++++++++++-- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java index 795917fffc4..cd83a81781f 100644 --- a/core/src/main/java/org/testcontainers/utility/MountableFile.java +++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java @@ -301,7 +301,14 @@ private void recursiveTar(String entryFilename, String rootPath, String itemPath final File sourceRootFile = new File(rootPath).getCanonicalFile(); // e.g. /foo final String relativePathToSourceFile = sourceRootFile.toPath().relativize(sourceFile.toPath()).toFile().toString(); // e.g. /bar/baz - final TarArchiveEntry tarEntry = new TarArchiveEntry(sourceFile, entryFilename + "/" + relativePathToSourceFile); // entry filename e.g. /xyz/bar/baz + final String tarEntryFilename; + if (relativePathToSourceFile.isEmpty()) { + tarEntryFilename = entryFilename; // entry filename e.g. xyz => xyz + } else { + tarEntryFilename = entryFilename + "/" + relativePathToSourceFile; // entry filename e.g. /xyz/bar/baz => /foo/bar/baz + } + + final TarArchiveEntry tarEntry = new TarArchiveEntry(sourceFile, tarEntryFilename.replaceAll("^/", "")); // TarArchiveEntry automatically sets the mode for file/directory, but we can update to ensure that the mode is set exactly (inc executable bits) tarEntry.setMode(getUnixFileMode(itemPath)); diff --git a/core/src/test/java/org/testcontainers/utility/MountableFileTest.java b/core/src/test/java/org/testcontainers/utility/MountableFileTest.java index 9b1fbb55a53..c7dcaad2908 100644 --- a/core/src/test/java/org/testcontainers/utility/MountableFileTest.java +++ b/core/src/test/java/org/testcontainers/utility/MountableFileTest.java @@ -1,16 +1,21 @@ package org.testcontainers.utility; +import lombok.Cleanup; +import org.apache.commons.compress.archivers.ArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; +import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.jetbrains.annotations.NotNull; import org.junit.Test; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.function.Consumer; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.rnorth.visibleassertions.VisibleAssertions.*; public class MountableFileTest { @@ -79,7 +84,7 @@ public void forHostPathWithPlus() throws Exception { @Test public void forClasspathResourceWithPermission() throws Exception { final MountableFile mountableFile = MountableFile.forClasspathResource("mappable-resource/test-resource.txt", - TEST_FILE_MODE); + TEST_FILE_MODE); performChecks(mountableFile); assertEquals("Valid file mode.", BASE_FILE_MODE | TEST_FILE_MODE, mountableFile.getFileMode()); @@ -101,6 +106,31 @@ public void forHostDirPathWithPermission() throws Exception { assertEquals("Valid dir mode.", BASE_DIR_MODE | TEST_FILE_MODE, mountableFile.getFileMode()); } + @Test + public void noTrailingSlashesInTarEntryNames() throws Exception { + final MountableFile mountableFile = MountableFile.forClasspathResource("mappable-resource/test-resource.txt"); + + @Cleanup final TarArchiveInputStream tais = intoTarArchive((taos) -> { + mountableFile.transferTo(taos, "/some/path.txt"); + mountableFile.transferTo(taos, "/path.txt"); + mountableFile.transferTo(taos, "path.txt"); + }); + + ArchiveEntry entry; + while ((entry = tais.getNextEntry()) != null) { + assertFalse("no entries should have a trailing slash", entry.getName().endsWith("/")); + } + } + + private TarArchiveInputStream intoTarArchive(Consumer consumer) throws IOException { + @Cleanup final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + @Cleanup final TarArchiveOutputStream taos = new TarArchiveOutputStream(baos); + consumer.accept(taos); + taos.close(); + + return new TarArchiveInputStream(new ByteArrayInputStream(baos.toByteArray())); + } + @SuppressWarnings("ResultOfMethodCallIgnored") @NotNull private Path createTempFile(final String name) throws IOException { From 7448bd2d6fde826e4661423dc417a354a9d08e50 Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 30 Jul 2018 08:26:22 +0100 Subject: [PATCH 0373/5615] Update changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c6ff6941e5..2747d4059b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,12 @@ # Change Log All notable changes to this project will be documented in this file. -## UNRELEASED +## [1.8.2] - UNRELEASED ### Fixed - Add support for transparently using local images with docker-compose ([\#798](https://github.com/testcontainers/testcontainers-java/pull/798), fixes [\#674](https://github.com/testcontainers/testcontainers-java/issues/674)) +- Fix bug with Dockerfile image creation with Docker for Mac 18.06-ce ([\#808](https://github.com/testcontainers/testcontainers-java/pull/808), fixes [\#680](https://github.com/testcontainers/testcontainers-java/issues/680)) ### Changed From ba8a89fcc3a0a5ee46a0e1ec25774d669a274a1f Mon Sep 17 00:00:00 2001 From: Alex Savin Date: Mon, 30 Jul 2018 10:35:17 +0300 Subject: [PATCH 0374/5615] Fixes #801: Class org.testcontainers.shaded.javax.annotation.CheckForNull not found (#802) --- core/build.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/build.gradle b/core/build.gradle index 32901007b72..0a63339e52b 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -55,6 +55,7 @@ shadowJar { include(dependency('com.squareup.okhttp3:.*')) include(dependency('com.squareup.okio:.*')) include(dependency('org.scala-sbt.ipcsocket:ipcsocket')) + include(dependency('com.google.code.findbugs:jsr305')) } } @@ -113,6 +114,7 @@ dependencies { // SslConfigurator doesn't use classes from dependencies exclude(module: '*') } + shaded 'com.google.code.findbugs:jsr305:3.0.2' testCompile 'redis.clients:jedis:2.8.0' testCompile 'com.rabbitmq:amqp-client:3.5.3' From e8b00c7df59cde674d23034868a0134d694ba277 Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 30 Jul 2018 08:43:58 +0100 Subject: [PATCH 0375/5615] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2747d4059b0..bbe967ecb6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. - Add support for transparently using local images with docker-compose ([\#798](https://github.com/testcontainers/testcontainers-java/pull/798), fixes [\#674](https://github.com/testcontainers/testcontainers-java/issues/674)) - Fix bug with Dockerfile image creation with Docker for Mac 18.06-ce ([\#808](https://github.com/testcontainers/testcontainers-java/pull/808), fixes [\#680](https://github.com/testcontainers/testcontainers-java/issues/680)) +- Fix missing `jsr305` dependency which caused an error with Scala compiler and `-Xfatal-warnings` flag ([\#802](https://github.com/testcontainers/testcontainers-java/pull/802), fixes [\#801](https://github.com/testcontainers/testcontainers-java/issues/801)) ### Changed From 35c50461babd9cf3c9db74d15797a431ea26bbfb Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 30 Jul 2018 10:32:41 +0100 Subject: [PATCH 0376/5615] Revert "Fixes #801: Class org.testcontainers.shaded.javax.annotation.CheckForNull not found" (#809) Reverts testcontainers/testcontainers-java#802 Sorry @alesavin - post-merge, we realised we want to do a bit more digging on this issue. We'll still try and, very soon, either (a) fix the problem another way, or (b) re-add this. --- core/build.gradle | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index 0a63339e52b..32901007b72 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -55,7 +55,6 @@ shadowJar { include(dependency('com.squareup.okhttp3:.*')) include(dependency('com.squareup.okio:.*')) include(dependency('org.scala-sbt.ipcsocket:ipcsocket')) - include(dependency('com.google.code.findbugs:jsr305')) } } @@ -114,7 +113,6 @@ dependencies { // SslConfigurator doesn't use classes from dependencies exclude(module: '*') } - shaded 'com.google.code.findbugs:jsr305:3.0.2' testCompile 'redis.clients:jedis:2.8.0' testCompile 'com.rabbitmq:amqp-client:3.5.3' From cb5da70d8b95a57d1159aef6ce240c8f223efc30 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 31 Jul 2018 08:18:47 +0100 Subject: [PATCH 0377/5615] Enter release date for 1.8.2 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbe967ecb6a..4053dc627ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # Change Log All notable changes to this project will be documented in this file. -## [1.8.2] - UNRELEASED +## [1.8.2] - 2018-07-31 ### Fixed From e1bf156c1c8a4a0e69df6717b158520b6c7944ed Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 31 Jul 2018 08:20:56 +0100 Subject: [PATCH 0378/5615] Revert changelog mention of #802 --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4053dc627ab..c992f851956 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,6 @@ All notable changes to this project will be documented in this file. - Add support for transparently using local images with docker-compose ([\#798](https://github.com/testcontainers/testcontainers-java/pull/798), fixes [\#674](https://github.com/testcontainers/testcontainers-java/issues/674)) - Fix bug with Dockerfile image creation with Docker for Mac 18.06-ce ([\#808](https://github.com/testcontainers/testcontainers-java/pull/808), fixes [\#680](https://github.com/testcontainers/testcontainers-java/issues/680)) -- Fix missing `jsr305` dependency which caused an error with Scala compiler and `-Xfatal-warnings` flag ([\#802](https://github.com/testcontainers/testcontainers-java/pull/802), fixes [\#801](https://github.com/testcontainers/testcontainers-java/issues/801)) ### Changed From 4d5a9819d1e5d89742034597147af8645f1175b9 Mon Sep 17 00:00:00 2001 From: Dominik Date: Tue, 31 Jul 2018 11:56:25 +0200 Subject: [PATCH 0379/5615] - Replaced Wither annotation with own with methods (#810) These methods do not clone the instance and therefore do not default to the standard alpine:3.5 image - Added test to verify that the image name is not overridden by withXXX - Made CouchbaseContainer conforming to other modules' containers Replaced the extends mechanism by the project standard --- .../couchbase/CouchbaseContainer.java | 71 +++++++++++++++---- .../couchbase/CouchbaseContainerTest.java | 8 +++ 2 files changed, 65 insertions(+), 14 deletions(-) diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index 356bbc91fea..e0a7882255b 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -29,7 +29,6 @@ import lombok.Cleanup; import lombok.Getter; import lombok.SneakyThrows; -import lombok.experimental.Wither; import org.apache.commons.compress.utils.Sets; import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.GenericContainer; @@ -53,46 +52,35 @@ public class CouchbaseContainer extends GenericContainer { public static final String VERSION = "5.1.0"; + public static final String DOCKER_IMAGE_NAME = "couchbase/server:"; public static final ObjectMapper MAPPER = new ObjectMapper(); - @Wither private String memoryQuota = "300"; - @Wither private String indexMemoryQuota = "300"; - @Wither private String clusterUsername = "Administrator"; - @Wither private String clusterPassword = "password"; - @Wither private boolean keyValue = true; @Getter - @Wither private boolean query = true; @Getter - @Wither private boolean index = true; @Getter - @Wither private boolean primaryIndex = true; @Getter - @Wither private boolean fts = false; - @Wither private boolean beerSample = false; - @Wither private boolean travelSample = false; - @Wither private boolean gamesIMSample = false; @Getter(lazy = true) @@ -106,7 +94,7 @@ public class CouchbaseContainer extends GenericContainer { private String urlBase; public CouchbaseContainer() { - super("couchbase/server:" + VERSION); + super(DOCKER_IMAGE_NAME + VERSION); } public CouchbaseContainer(String containerName) { @@ -273,4 +261,59 @@ private DefaultCouchbaseEnvironment createCouchbaseEnvironment() { .bootstrapHttpSslPort(getMappedPort(18091)) .build(); } + + public CouchbaseContainer withMemoryQuota(String memoryQuota) { + this.memoryQuota = memoryQuota; + return self(); + } + + public CouchbaseContainer withIndexMemoryQuota(String indexMemoryQuota) { + this.indexMemoryQuota = indexMemoryQuota; + return self(); + } + + public CouchbaseContainer withClusterPassword(String clusterPassword) { + this.clusterPassword = clusterPassword; + return self(); + } + + public CouchbaseContainer withKeyValue(boolean keyValue) { + this.keyValue = keyValue; + return self(); + } + + public CouchbaseContainer withQuery(boolean query) { + this.query = query; + return self(); + } + + public CouchbaseContainer withIndex(boolean index) { + this.index = index; + return self(); + } + + public CouchbaseContainer withPrimaryIndex(boolean primaryIndex) { + this.primaryIndex = primaryIndex; + return self(); + } + + public CouchbaseContainer withFts(boolean fts) { + this.fts = fts; + return self(); + } + + public CouchbaseContainer withBeerSample(boolean beerSample) { + this.beerSample = beerSample; + return self(); + } + + public CouchbaseContainer withTravelSample(boolean travelSample) { + this.travelSample = travelSample; + return self(); + } + + public CouchbaseContainer withGamesIMSample(boolean gamesIMSample) { + this.gamesIMSample = gamesIMSample; + return self(); + } } diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java index 52660787796..cba4ddb5a87 100644 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java @@ -38,4 +38,12 @@ public void shouldExecuteN1ql() { Assert.assertEquals(1, n1qlQueryRows.size()); Assert.assertEquals(DOCUMENT, n1qlQueryRows.get(0).value().get(TEST_BUCKET).toString()); } + + @Test + public void shouldUseCorrectDockerImage() { + CouchbaseContainer couchbaseContainer = new CouchbaseContainer().withBeerSample(true); + + Assert.assertEquals(CouchbaseContainer.DOCKER_IMAGE_NAME + CouchbaseContainer.VERSION, + couchbaseContainer.getDockerImageName()); + } } From 834b19f2df704c0a8ceb657d663ca7bbc4c6e002 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 31 Jul 2018 11:58:31 +0200 Subject: [PATCH 0380/5615] Update CHANGELOG.md --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c992f851956..8c5b4b47086 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ # Change Log All notable changes to this project will be documented in this file. +## UNRELEASED + +### Fixed + +- Fixed `with*` methods of `CouchbaseContainer` ([\#810](https://github.com/testcontainers/testcontainers-java/pull/810)) + +### Changed + ## [1.8.2] - 2018-07-31 ### Fixed From 9d6d6bbc2e702a961b9057be14005c2a9db01a3e Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 5 Aug 2018 07:09:00 +0100 Subject: [PATCH 0381/5615] Add decompression support to netty exec factory (#817) Fixes problem with gzip encoded streams (e.g. copy file from container) --- .../transport/TestcontainersDockerCmdExecFactory.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java b/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java index 976bb23c91d..179d24d37f6 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java @@ -19,6 +19,7 @@ import io.netty.channel.unix.DomainSocketAddress; import io.netty.channel.unix.UnixChannel; import io.netty.handler.codec.http.HttpClientCodec; +import io.netty.handler.codec.http.HttpContentDecompressor; import io.netty.handler.ssl.SslHandler; import io.netty.handler.timeout.IdleState; import io.netty.handler.timeout.IdleStateEvent; @@ -135,6 +136,7 @@ public EventLoopGroup epollGroup() { @Override protected void initChannel(final UnixChannel channel) throws Exception { channel.pipeline().addLast(new HttpClientCodec()); + channel.pipeline().addLast(new HttpContentDecompressor()); } }); return epollEventLoopGroup; @@ -148,6 +150,7 @@ public EventLoopGroup kqueueGroup() { @Override protected void initChannel(final KQueueDomainSocketChannel channel) throws Exception { channel.pipeline().addLast(new HttpClientCodec()); + channel.pipeline().addLast(new HttpContentDecompressor()); } }); @@ -175,6 +178,7 @@ public EventLoopGroup init(Bootstrap bootstrap, final DockerClientConfig dockerC @Override protected void initChannel(final SocketChannel channel) throws Exception { channel.pipeline().addLast(new HttpClientCodec()); + channel.pipeline().addLast(new HttpContentDecompressor()); } }); From 3012e8efc478ce1e6b339d48c4588fa5fc3c90b9 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 5 Aug 2018 15:45:26 +0100 Subject: [PATCH 0382/5615] Add default template for release-drafter bot --- .github/release-drafter.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .github/release-drafter.yml diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 00000000000..1176fe471a8 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,4 @@ +template: | + ## What's Changed + + $CHANGES From d2ff0730eaafcf45076fe8a8d4a760a10c8b0b58 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 5 Aug 2018 21:28:20 +0100 Subject: [PATCH 0383/5615] Update changelog --- CHANGELOG.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c5b4b47086..ceccc9319f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,10 +5,16 @@ All notable changes to this project will be documented in this file. ### Fixed -- Fixed `with*` methods of `CouchbaseContainer` ([\#810](https://github.com/testcontainers/testcontainers-java/pull/810)) - ### Changed + +## [1.8.3] - 2018-08-05 + +### Fixed + +- Fixed `with*` methods of `CouchbaseContainer` ([\#810](https://github.com/testcontainers/testcontainers-java/pull/810)) +- Fix problem with gzip encoded streams (e.g. copy file from container), by adding decompression support to netty exec factory (#817, fixes #681, relates to docker-java/docker-java#1079) + ## [1.8.2] - 2018-07-31 ### Fixed From 7e99051ac135d241faecf8f25d405e8fa8a37346 Mon Sep 17 00:00:00 2001 From: Dominik Date: Tue, 7 Aug 2018 11:40:19 +0200 Subject: [PATCH 0384/5615] Socat sidecar proxy for Couchbase container to support random ports (#815) * Use Socat sidecar container in Couchbase to randomize the ports * add dynamic couchbase port mappings: - updated couchbase sdk to support 5.5.0 - added all client ports to port mappings - configuring couchbase through static_config file * restructured couchbase tests to support multiple couchbase versions * - added memcached_ssl_port - added @ClassRule for the couchbase container tests instead of static variables to prevent race conditions - configured socat to expose the bootstrap ports as target. These bootstrap ports are not added to the couchbase config file. * moved shouldUseCorrectDockerImage to its own test class * - increased kvTimeout for busy ci systems - tool pleasing * - couchbase sdk update - minor code style and naming improvements --- .../containers/GenericContainer.java | 15 +- modules/couchbase/build.gradle | 2 +- .../couchbase/AbstractCouchbaseTest.java | 44 ++-- .../couchbase/CouchbaseContainer.java | 200 +++++++++++++++--- .../couchbase/BaseCouchbaseContainerTest.java | 37 ++++ .../couchbase/Couchbase4_6Test.java | 13 ++ .../couchbase/Couchbase5_1Test.java | 13 ++ .../couchbase/Couchbase5_5Test.java | 13 ++ .../couchbase/CouchbaseContainerTest.java | 38 +--- 9 files changed, 287 insertions(+), 88 deletions(-) create mode 100644 modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java create mode 100644 modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase4_6Test.java create mode 100644 modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_1Test.java create mode 100644 modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_5Test.java diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 22c3f1ce09c..6d675d3a9a1 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -247,6 +247,8 @@ private void tryStart(Profiler profiler) { containerId = createCommand.exec().getId(); copyToFileContainerPathMap.forEach(this::copyFileToContainer); + containerIsCreated(containerId); + logger().info("Starting container with ID: {}", containerId); profiler.start("Start container"); dockerClient.startContainerCmd(containerId).exec(); @@ -356,6 +358,10 @@ protected void configure() { } + @SuppressWarnings({"EmptyMethod", "UnusedParameters"}) + protected void containerIsCreated(String containerId) { + } + @SuppressWarnings({"EmptyMethod", "UnusedParameters"}) protected void containerIsStarting(InspectContainerResponse containerInfo) { } @@ -556,7 +562,10 @@ public void setWaitStrategy(org.testcontainers.containers.wait.strategy.WaitStra * @see #waitingFor(org.testcontainers.containers.wait.strategy.WaitStrategy) */ protected void waitUntilContainerStarted() { - getWaitStrategy().waitUntilReady(this); + org.testcontainers.containers.wait.strategy.WaitStrategy waitStrategy = getWaitStrategy(); + if (waitStrategy != null) { + waitStrategy.waitUntilReady(this); + } } /** @@ -1028,8 +1037,8 @@ public void copyFileToContainer(MountableFile mountableLocalFile, String contain @Override public void copyFileFromContainer(String containerPath, String destinationPath) throws IOException { - if (!isRunning()) { - throw new IllegalStateException("copyFileToContainer can only be used while the Container is running"); + if (!isCreated()) { + throw new IllegalStateException("copyFileFromContainer can only be used when the Container is created."); } try (final TarArchiveInputStream tarInputStream = new TarArchiveInputStream(this.dockerClient diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 218adce2d5a..c89648ba2b7 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -2,5 +2,5 @@ description = "Testcontainers :: Couchbase" dependencies { compile project(':testcontainers') - compile 'com.couchbase.client:java-client:2.5.7' + compile 'com.couchbase.client:java-client:2.6.1' } diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java index b8cdaa14cb2..1d713887253 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java @@ -7,11 +7,10 @@ import com.couchbase.client.java.query.N1qlParams; import com.couchbase.client.java.query.N1qlQuery; import com.couchbase.client.java.query.consistency.ScanConsistency; -import lombok.Getter; import org.junit.After; /** - * @author ctayeb + * Basic class that can be used for couchbase tests. It will clear the database after every test. */ public abstract class AbstractCouchbaseTest { @@ -19,11 +18,7 @@ public abstract class AbstractCouchbaseTest { public static final String DEFAULT_PASSWORD = "password"; - @Getter(lazy = true) - private final static CouchbaseContainer couchbaseContainer = initCouchbaseContainer(); - - @Getter(lazy = true) - private final static Bucket bucket = openBucket(TEST_BUCKET, DEFAULT_PASSWORD); + private Bucket bucket; @After public void clear() { @@ -36,24 +31,31 @@ public void clear() { } } - private static CouchbaseContainer initCouchbaseContainer() { - CouchbaseContainer couchbaseContainer = new CouchbaseContainer() - .withNewBucket(DefaultBucketSettings.builder() - .enableFlush(true) - .name(TEST_BUCKET) - .password(DEFAULT_PASSWORD) - .quota(100) - .replicas(0) - .type(BucketType.COUCHBASE) - .build()); + protected abstract CouchbaseContainer getCouchbaseContainer(); + + protected static CouchbaseContainer initCouchbaseContainer(String imageName) { + CouchbaseContainer couchbaseContainer = (imageName == null) ? new CouchbaseContainer() : new CouchbaseContainer(imageName); + couchbaseContainer.withNewBucket(DefaultBucketSettings.builder() + .enableFlush(true) + .name(TEST_BUCKET) + .password(DEFAULT_PASSWORD) + .quota(100) + .replicas(0) + .type(BucketType.COUCHBASE) + .build()); couchbaseContainer.start(); return couchbaseContainer; } - private static Bucket openBucket(String bucketName, String password) { - CouchbaseCluster cluster = getCouchbaseContainer().getCouchbaseCluster(); - Bucket bucket = cluster.openBucket(bucketName, password); - Runtime.getRuntime().addShutdownHook(new Thread(bucket::close)); + protected synchronized Bucket getBucket() { + if (bucket == null) { + bucket = openBucket(TEST_BUCKET, DEFAULT_PASSWORD); + } return bucket; } + + private Bucket openBucket(String bucketName, String password) { + CouchbaseCluster cluster = getCouchbaseContainer().getCouchbaseCluster(); + return cluster.openBucket(bucketName, password); + } } diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index e0a7882255b..16a5f83a176 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -24,24 +24,40 @@ import com.couchbase.client.java.query.Index; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dockerjava.api.command.ExecCreateCmdResponse; import com.github.dockerjava.api.command.InspectContainerResponse; -import lombok.AllArgsConstructor; -import lombok.Cleanup; -import lombok.Getter; -import lombok.SneakyThrows; +import com.github.dockerjava.core.command.ExecStartResultCallback; +import com.google.common.collect.Lists; +import lombok.*; import org.apache.commons.compress.utils.Sets; +import org.apache.commons.io.FileUtils; import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.Network; +import org.testcontainers.containers.SocatContainer; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; +import org.testcontainers.utility.Base58; +import org.testcontainers.utility.MountableFile; import java.io.DataOutputStream; +import java.io.File; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; -import java.util.*; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Stream; import static java.net.HttpURLConnection.HTTP_OK; +import static org.testcontainers.couchbase.CouchbaseContainer.CouchbasePort.CAPI; +import static org.testcontainers.couchbase.CouchbaseContainer.CouchbasePort.MEMCACHED; +import static org.testcontainers.couchbase.CouchbaseContainer.CouchbasePort.MEMCACHED_SSL; +import static org.testcontainers.couchbase.CouchbaseContainer.CouchbasePort.REST; +import static org.testcontainers.couchbase.CouchbaseContainer.CouchbasePort.REST_SSL; /** * Based on Laurent Doguin version, @@ -54,6 +70,9 @@ public class CouchbaseContainer extends GenericContainer { public static final String VERSION = "5.1.0"; public static final String DOCKER_IMAGE_NAME = "couchbase/server:"; public static final ObjectMapper MAPPER = new ObjectMapper(); + public static final String STATIC_CONFIG_NAME = "static_config"; + public static final String STATIC_CONFIG_PATH = "/opt/couchbase/etc/couchbase/"; + public static final String STATIC_CONFIG_LOCATION = STATIC_CONFIG_PATH + STATIC_CONFIG_NAME; private String memoryQuota = "300"; @@ -93,37 +112,98 @@ public class CouchbaseContainer extends GenericContainer { private String urlBase; + private SocatContainer proxy; + public CouchbaseContainer() { - super(DOCKER_IMAGE_NAME + VERSION); + this(DOCKER_IMAGE_NAME + VERSION); } public CouchbaseContainer(String containerName) { super(containerName); + + withNetwork(Network.SHARED); + withNetworkAliases("couchbase-" + Base58.randomString(6)); + setWaitStrategy(new HttpWaitStrategy().forPath("/ui/index.html")); } @Override - protected Integer getLivenessCheckPort() { - return getMappedPort(8091); + public Set getLivenessCheckPortNumbers() { + return Sets.newHashSet(getMappedPort(REST)); } @Override - public Set getLivenessCheckPortNumbers() { - return Sets.newHashSet(getLivenessCheckPort()); + @SneakyThrows + protected void doStart() { + String networkAlias = getNetworkAliases().get(0); + startProxy(networkAlias); + + for (CouchbasePort port : CouchbasePort.values()) { + exposePortThroughProxy(networkAlias, port.getOriginalPort(), getMappedPort(port)); + + // CAPI port has a special configuration file which is set via env + if (port == CAPI) { + withEnv(port.getName(), getMappedPort(port) + ""); + } + } + super.doStart(); + } + + private void startProxy(String networkAlias) { + proxy = new SocatContainer().withNetwork(getNetwork()); + + for (CouchbasePort port : CouchbasePort.values()) { + if (port.isDynamic()) { + proxy.withTarget(port.getOriginalPort(), networkAlias); + } else { + proxy.addExposedPort(port.getOriginalPort()); + } + } + proxy.setWaitStrategy(null); + proxy.start(); + } + + private void exposePortThroughProxy(String networkAlias, int originalPort, int mappedPort) { + ExecCreateCmdResponse createCmdResponse = dockerClient + .execCreateCmd(proxy.getContainerId()) + .withCmd("/usr/bin/socat", "TCP-LISTEN:" + originalPort + ",fork,reuseaddr", "TCP:" + networkAlias + ":" + mappedPort) + .exec(); + + dockerClient.execStartCmd(createCmdResponse.getId()) + .exec(new ExecStartResultCallback()); + } + + @Override + public List getExposedPorts() { + return proxy.getExposedPorts(); + } + + @Override + public String getContainerIpAddress() { + return proxy.getContainerIpAddress(); } @Override - protected void configure() { - // Configurable ports - addExposedPorts(11210, 11207, 8091, 18091); + public Integer getMappedPort(int originalPort) { + return proxy.getMappedPort(originalPort); + } - // Non configurable ports - addFixedExposedPort(8092, 8092); - addFixedExposedPort(8093, 8093); - addFixedExposedPort(8094, 8094); - addFixedExposedPort(8095, 8095); - addFixedExposedPort(18092, 18092); - addFixedExposedPort(18093, 18093); - setWaitStrategy(new HttpWaitStrategy().forPath("/ui/index.html#/")); + protected Integer getMappedPort(CouchbasePort port) { + return getMappedPort(port.getOriginalPort()); + } + + @Override + public List getBoundPortNumbers() { + return proxy.getBoundPortNumbers(); + } + + @Override + public void stop() { + Stream.of(super::stop, proxy::stop, this::stopCluster).parallel().forEach(Runnable::run); + } + + private void stopCluster() { + getCouchbaseCluster().disconnect(); + getCouchbaseEnvironment().shutdown(); } public CouchbaseContainer withNewBucket(BucketSettings bucketSettings) { @@ -133,7 +213,7 @@ public CouchbaseContainer withNewBucket(BucketSettings bucketSettings) { @SneakyThrows public void initCluster() { - urlBase = String.format("http://%s:%s", getContainerIpAddress(), getMappedPort(8091)); + urlBase = String.format("http://%s:%s", getContainerIpAddress(), getMappedPort(REST)); String poolURL = "/pools/default"; String poolPayload = "memoryQuota=" + URLEncoder.encode(memoryQuota, "UTF-8") + "&indexMemoryQuota=" + URLEncoder.encode(indexMemoryQuota, "UTF-8"); @@ -207,7 +287,7 @@ public void createBucket(BucketSettings bucketSetting, boolean primaryIndex) { // Insert Bucket admin user UserSettings userSettings = UserSettings.build() .password(bucketSetting.password()) - .roles(Collections.singletonList(new UserRole("bucket_admin", bucketSetting.name()))); + .roles(getAdminRoles(bucketSetting.name())); try { clusterManager.upsertUser(AuthDomain.LOCAL, bucketSetting.name(), userSettings); } catch (Exception e) { @@ -222,6 +302,18 @@ public void createBucket(BucketSettings bucketSetting, boolean primaryIndex) { } } + private List getAdminRoles(String bucketName) { + return Lists.newArrayList( + new UserRole("bucket_admin", bucketName), + new UserRole("views_admin", bucketName), + new UserRole("query_manage_index", bucketName), + new UserRole("query_update", bucketName), + new UserRole("query_select", bucketName), + new UserRole("query_insert", bucketName), + new UserRole("query_delete", bucketName) + ); + } + public void callCouchbaseRestAPI(String url, String payload) throws IOException { String fullUrl = urlBase + url; @Cleanup("disconnect") @@ -239,6 +331,33 @@ public void callCouchbaseRestAPI(String url, String payload) throws IOException httpConnection.getResponseCode(); } + @Override + @SneakyThrows + protected void containerIsCreated(String containerId) { + File tempFile = copyConfigFromContainer(containerId); + appendPortsToConfig(tempFile); + copyFileToContainer(MountableFile.forHostPath(tempFile.toPath()), STATIC_CONFIG_PATH); + } + + private File copyConfigFromContainer(String containerId) throws IOException { + File tempDirectory = Files.createTempDirectory("testcontainer_" + containerId + "_").toFile(); + tempDirectory.deleteOnExit(); + File tempFile = new File(tempDirectory, STATIC_CONFIG_NAME); + tempFile.deleteOnExit(); + logger().debug("Storing static_config in [{}].", tempFile.getAbsolutePath()); + copyFileFromContainer(STATIC_CONFIG_LOCATION, tempFile.getPath()); + return tempFile; + } + + private void appendPortsToConfig(File tempFile) throws IOException { + for (CouchbasePort port : CouchbasePort.values()) { + if (!port.isDynamic()) { + String config = String.format("{%s, %d}.\n", port.name, getMappedPort(port)); + FileUtils.writeStringToFile(tempFile, config, "UTF-8", true); + } + } + } + @Override protected void containerIsStarted(InspectContainerResponse containerInfo) { if (!newBuckets.isEmpty()) { @@ -255,10 +374,11 @@ private CouchbaseCluster createCouchbaseCluster() { private DefaultCouchbaseEnvironment createCouchbaseEnvironment() { initCluster(); return DefaultCouchbaseEnvironment.builder() - .bootstrapCarrierDirectPort(getMappedPort(11210)) - .bootstrapCarrierSslPort(getMappedPort(11207)) - .bootstrapHttpDirectPort(getMappedPort(8091)) - .bootstrapHttpSslPort(getMappedPort(18091)) + .kvTimeout(10000) + .bootstrapCarrierDirectPort(getMappedPort(MEMCACHED)) + .bootstrapCarrierSslPort(getMappedPort(MEMCACHED_SSL)) + .bootstrapHttpDirectPort(getMappedPort(REST)) + .bootstrapHttpSslPort(getMappedPort(REST_SSL)) .build(); } @@ -316,4 +436,30 @@ public CouchbaseContainer withGamesIMSample(boolean gamesIMSample) { this.gamesIMSample = gamesIMSample; return self(); } + + @Getter + @RequiredArgsConstructor + protected enum CouchbasePort { + REST("rest_port", 8091, true), + CAPI("capi_port", 8092, false), + QUERY("query_port", 8093, false), + FTS("fts_http_port", 8094, false), + CBAS("cbas_http_port", 8095, false), + EVENTING("eventing_http_port", 8096, false), + MEMCACHED_SSL("memcached_ssl_port", 11207, false), + MEMCACHED("memcached_port", 11210, false), + REST_SSL("ssl_rest_port", 18091, true), + CAPI_SSL("ssl_capi_port", 18092, false), + QUERY_SSL("ssl_query_port", 18093, false), + FTS_SSL("fts_ssl_port", 18094, false), + CBAS_SSL("cbas_ssl_port", 18095, false), + EVENTING_SSL("eventing_ssl_port", 18096, false), + ; + + final String name; + + final int originalPort; + + final boolean dynamic; + } } diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java new file mode 100644 index 00000000000..d6ecb41c654 --- /dev/null +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java @@ -0,0 +1,37 @@ +package org.testcontainers.couchbase; + +import com.couchbase.client.java.document.RawJsonDocument; +import com.couchbase.client.java.query.N1qlQuery; +import com.couchbase.client.java.query.N1qlQueryResult; +import com.couchbase.client.java.query.N1qlQueryRow; +import org.junit.Assert; +import org.junit.Test; + +import java.util.List; + +public abstract class BaseCouchbaseContainerTest extends AbstractCouchbaseTest { + + private static final String ID = "toto"; + + private static final String DOCUMENT = "{\"name\":\"toto\"}"; + + @Test + public void shouldInsertDocument() { + RawJsonDocument expected = RawJsonDocument.create(ID, DOCUMENT); + getBucket().upsert(expected); + RawJsonDocument result = getBucket().get(ID, RawJsonDocument.class); + Assert.assertEquals(expected.content(), result.content()); + } + + @Test + public void shouldExecuteN1ql() { + getBucket().query(N1qlQuery.simple("INSERT INTO " + TEST_BUCKET + " (KEY, VALUE) VALUES ('" + ID + "', " + DOCUMENT + ")")); + + N1qlQueryResult query = getBucket().query(N1qlQuery.simple("SELECT * FROM " + TEST_BUCKET + " USE KEYS '" + ID + "'")); + Assert.assertTrue(query.parseSuccess()); + Assert.assertTrue(query.finalSuccess()); + List n1qlQueryRows = query.allRows(); + Assert.assertEquals(1, n1qlQueryRows.size()); + Assert.assertEquals(DOCUMENT, n1qlQueryRows.get(0).value().get(TEST_BUCKET).toString()); + } +} diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase4_6Test.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase4_6Test.java new file mode 100644 index 00000000000..7781d705d41 --- /dev/null +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase4_6Test.java @@ -0,0 +1,13 @@ +package org.testcontainers.couchbase; + +import org.junit.ClassRule; + +public class Couchbase4_6Test extends BaseCouchbaseContainerTest { + @ClassRule + public static CouchbaseContainer container = initCouchbaseContainer("couchbase/server:4.6.5"); + + @Override + public CouchbaseContainer getCouchbaseContainer() { + return container; + } +} diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_1Test.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_1Test.java new file mode 100644 index 00000000000..bc1f66a2847 --- /dev/null +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_1Test.java @@ -0,0 +1,13 @@ +package org.testcontainers.couchbase; + +import org.junit.ClassRule; + +public class Couchbase5_1Test extends BaseCouchbaseContainerTest { + @ClassRule + public static CouchbaseContainer container = initCouchbaseContainer("couchbase/server:5.1.0"); + + @Override + public CouchbaseContainer getCouchbaseContainer() { + return container; + } +} diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_5Test.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_5Test.java new file mode 100644 index 00000000000..8a3635f03be --- /dev/null +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_5Test.java @@ -0,0 +1,13 @@ +package org.testcontainers.couchbase; + +import org.junit.ClassRule; + +public class Couchbase5_5Test extends BaseCouchbaseContainerTest { + @ClassRule + public static CouchbaseContainer container = initCouchbaseContainer("couchbase/server:5.5.0"); + + @Override + public CouchbaseContainer getCouchbaseContainer() { + return container; + } +} diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java index cba4ddb5a87..b15a13b1c18 100644 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java @@ -1,49 +1,15 @@ package org.testcontainers.couchbase; -import com.couchbase.client.java.document.RawJsonDocument; -import com.couchbase.client.java.query.N1qlQuery; -import com.couchbase.client.java.query.N1qlQueryResult; -import com.couchbase.client.java.query.N1qlQueryRow; import org.junit.Assert; import org.junit.Test; -import java.util.List; - -/** - * @author ctayeb - * created on 18/06/2017 - */ -public class CouchbaseContainerTest extends AbstractCouchbaseTest { - - private static final String ID = "toto"; - - private static final String DOCUMENT = "{\"name\":\"toto\"}"; - - @Test - public void shouldInsertDocument() { - RawJsonDocument expected = RawJsonDocument.create(ID, DOCUMENT); - getBucket().upsert(expected); - RawJsonDocument result = getBucket().get(ID, RawJsonDocument.class); - Assert.assertEquals(expected.content(), result.content()); - } - - @Test - public void shouldExecuteN1ql() { - getBucket().query(N1qlQuery.simple("INSERT INTO " + TEST_BUCKET + " (KEY, VALUE) VALUES ('" + ID + "', " + DOCUMENT + ")")); - - N1qlQueryResult query = getBucket().query(N1qlQuery.simple("SELECT * FROM " + TEST_BUCKET + " USE KEYS '" + ID + "'")); - Assert.assertTrue(query.parseSuccess()); - Assert.assertTrue(query.finalSuccess()); - List n1qlQueryRows = query.allRows(); - Assert.assertEquals(1, n1qlQueryRows.size()); - Assert.assertEquals(DOCUMENT, n1qlQueryRows.get(0).value().get(TEST_BUCKET).toString()); - } +public class CouchbaseContainerTest { @Test public void shouldUseCorrectDockerImage() { CouchbaseContainer couchbaseContainer = new CouchbaseContainer().withBeerSample(true); Assert.assertEquals(CouchbaseContainer.DOCKER_IMAGE_NAME + CouchbaseContainer.VERSION, - couchbaseContainer.getDockerImageName()); + couchbaseContainer.getDockerImageName()); } } From 609a973ac64c1bb66e55a8a22237a7f0a5a5a21b Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 13 Aug 2018 12:42:11 +0100 Subject: [PATCH 0385/5615] Ensure that nginx test waits properly to reduce flakiness (#818) --- .../org/testcontainers/containers/NginxContainer.java | 4 ++-- .../java/org/testcontainers/junit/SimpleNginxTest.java | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java b/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java index 1e2ad9e47fc..1badffa1cee 100644 --- a/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java +++ b/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java @@ -5,7 +5,7 @@ import java.net.MalformedURLException; import java.net.URL; -import java.util.HashSet; +import java.util.Collections; import java.util.Set; /** @@ -22,7 +22,7 @@ public NginxContainer() { @NotNull @Override protected Set getLivenessCheckPorts() { - return new HashSet<>(getMappedPort(80)); + return Collections.singleton(getMappedPort(80)); } @Override diff --git a/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java b/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java index 239f7a18877..ad9026b6e4c 100644 --- a/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java +++ b/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java @@ -5,12 +5,13 @@ import org.junit.Rule; import org.junit.Test; import org.testcontainers.containers.NginxContainer; -import org.testcontainers.containers.wait.HttpWaitStrategy; +import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import java.io.*; import java.net.URLConnection; -import static org.rnorth.visibleassertions.VisibleAssertions.*; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.rnorth.visibleassertions.VisibleAssertions.info; /** @@ -22,9 +23,10 @@ public class SimpleNginxTest { @Rule public NginxContainer nginx = new NginxContainer<>() - .withCustomContent(contentFolder.toString()) - .waitingFor(new HttpWaitStrategy()); + .withCustomContent(contentFolder.toString()) + .waitingFor(new HttpWaitStrategy()); + @SuppressWarnings({"Duplicates", "ResultOfMethodCallIgnored"}) @BeforeClass public static void setupContent() throws FileNotFoundException { contentFolder.mkdir(); From 40f13a6ec5b7201c6197a29fcb65d3972de964d3 Mon Sep 17 00:00:00 2001 From: kiptix Date: Mon, 13 Aug 2018 13:42:53 +0200 Subject: [PATCH 0386/5615] support to override default username and password in oracle-xe (#823) --- .../containers/OracleContainer.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java index d9ff393b266..d7410373fd9 100644 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java @@ -17,6 +17,9 @@ public class OracleContainer extends JdbcDatabaseContainer { private static final int DEFAULT_STARTUP_TIMEOUT_SECONDS = 240; private static final int DEFAULT_CONNECT_TIMEOUT_SECONDS = 120; + private String username = "system"; + private String password = "oracle"; + private static String resolveImageName() { String image = TestcontainersConfiguration.getInstance() .getProperties().getProperty("oracle.container.image"); @@ -67,12 +70,24 @@ public String getJdbcUrl() { @Override public String getUsername() { - return "system"; + return username; } @Override public String getPassword() { - return "oracle"; + return password; + } + + @Override + public OracleContainer withUsername(String username) { + this.username = username; + return this; + } + + @Override + public OracleContainer withPassword(String password) { + this.password = password; + return this; } @SuppressWarnings("SameReturnValue") From 827790f9c185fa61a0f55153c24d931996a9620b Mon Sep 17 00:00:00 2001 From: Kirill Merkushev Date: Tue, 14 Aug 2018 15:43:11 +0200 Subject: [PATCH 0387/5615] update mockserver to 5.4.1 (#832) related https://github.com/jamesdbloom/mockserver/issues/451 --- modules/mockserver/build.gradle | 4 ++-- .../org/testcontainers/containers/MockServerContainer.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/mockserver/build.gradle b/modules/mockserver/build.gradle index 09a4077e859..3fae7db0757 100644 --- a/modules/mockserver/build.gradle +++ b/modules/mockserver/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: MockServer" dependencies { compile project(':testcontainers') - compileOnly 'org.mock-server:mockserver-client-java:5.3.0' - testCompile 'org.mock-server:mockserver-client-java:5.3.0' + compileOnly 'org.mock-server:mockserver-client-java:5.4.1' + testCompile 'org.mock-server:mockserver-client-java:5.4.1' } diff --git a/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java b/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java index 5580010be1f..d245275a7af 100644 --- a/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java +++ b/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java @@ -3,12 +3,12 @@ import com.github.dockerjava.api.command.InspectContainerResponse; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import org.mockserver.client.server.MockServerClient; +import org.mockserver.client.MockServerClient; @Slf4j public class MockServerContainer extends GenericContainer { - public static final String VERSION = "5.3.0"; + public static final String VERSION = "5.4.1"; public static final int PORT = 80; From c8bf1c4e0948acecd82e4e21636a48e6f33cb510 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 15 Aug 2018 09:45:01 +0200 Subject: [PATCH 0388/5615] Fix inverted logic in Selenium module (#821) --- .../testcontainers/containers/BrowserWebDriverContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index fcc8b6f9ae2..76feff2840f 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -206,7 +206,7 @@ public RemoteWebDriver getWebDriver() { @Override public void afterTest(TestDescription description, Optional throwable) { - retainRecordingIfNeeded(description.getFilesystemFriendlyName(), throwable.isPresent()); + retainRecordingIfNeeded(description.getFilesystemFriendlyName(), !throwable.isPresent()); } @Override From dabe7dab141fef9e975bf8e852be9b58d3fbd177 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 16 Aug 2018 15:28:58 +0200 Subject: [PATCH 0389/5615] use ssh port forwarding to expose host's ports to the containers (#806) * use ssh port forwarding to expose host's ports to the containers * make the test more readable * fix shading * hide sshConnection from public API * use withExtra hosts, do not connect "none" or "host" networks * use random password * extract `connectToPortForwardingNetwork` method --- build.gradle | 1 + core/build.gradle | 3 + .../org/testcontainers/Testcontainers.java | 14 ++++ .../containers/GenericContainer.java | 18 +++++ .../containers/PortForwardingContainer.java | 69 +++++++++++++++++++ .../utility/TestcontainersConfiguration.java | 4 ++ .../containers/ExposedHostTest.java | 64 +++++++++++++++++ 7 files changed, 173 insertions(+) create mode 100644 core/src/main/java/org/testcontainers/Testcontainers.java create mode 100644 core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java create mode 100644 core/src/test/java/org/testcontainers/containers/ExposedHostTest.java diff --git a/build.gradle b/build.gradle index edee9d34d28..2e09985de17 100644 --- a/build.gradle +++ b/build.gradle @@ -76,6 +76,7 @@ subprojects { "okhttp3", "okio", "org.scalasbt.ipcsocket", + "com.trilead.ssh2", ].each { relocate(it, "org.testcontainers.shaded.$it") } } diff --git a/core/build.gradle b/core/build.gradle index 32901007b72..abaa30bf318 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -55,6 +55,7 @@ shadowJar { include(dependency('com.squareup.okhttp3:.*')) include(dependency('com.squareup.okio:.*')) include(dependency('org.scala-sbt.ipcsocket:ipcsocket')) + include(dependency('org.glassfish.main.external:trilead-ssh2-repackaged')) } } @@ -103,6 +104,8 @@ dependencies { } shaded 'com.squareup.okhttp3:okhttp:3.10.0' + shaded 'org.glassfish.main.external:trilead-ssh2-repackaged:4.1.2' + shaded 'javax.ws.rs:javax.ws.rs-api:2.0.1' shaded 'org.zeroturnaround:zt-exec:1.10' shaded 'commons-lang:commons-lang:2.6' diff --git a/core/src/main/java/org/testcontainers/Testcontainers.java b/core/src/main/java/org/testcontainers/Testcontainers.java new file mode 100644 index 00000000000..4d7f5af4753 --- /dev/null +++ b/core/src/main/java/org/testcontainers/Testcontainers.java @@ -0,0 +1,14 @@ +package org.testcontainers; + +import lombok.experimental.UtilityClass; +import org.testcontainers.containers.PortForwardingContainer; + +@UtilityClass +public class Testcontainers { + + public void exposeHostPorts(int... ports) { + for (int port : ports) { + PortForwardingContainer.INSTANCE.exposeHostPort(port); + } + } +} diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 6d675d3a9a1..bab40d51ffb 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -4,6 +4,7 @@ import com.github.dockerjava.api.command.CreateContainerCmd; import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.model.Bind; +import com.github.dockerjava.api.model.ContainerNetwork; import com.github.dockerjava.api.model.ExposedPort; import com.github.dockerjava.api.model.Info; import com.github.dockerjava.api.model.Link; @@ -85,6 +86,8 @@ public class GenericContainer> public static final int CONTAINER_RUNNING_TIMEOUT_SEC = 30; + public static final String INTERNAL_HOST_HOSTNAME = "host.testcontainers.internal"; + /* * Default settings */ @@ -245,6 +248,9 @@ private void tryStart(Profiler profiler) { applyConfiguration(createCommand); containerId = createCommand.exec().getId(); + + connectToPortForwardingNetwork(createCommand.getNetworkMode()); + copyToFileContainerPathMap.forEach(this::copyFileToContainer); containerIsCreated(containerId); @@ -302,6 +308,14 @@ private void tryStart(Profiler profiler) { } } + private void connectToPortForwardingNetwork(String networkMode) { + PortForwardingContainer.INSTANCE.getNetwork().map(ContainerNetwork::getNetworkID).ifPresent(networkId -> { + if (!Arrays.asList(networkId, "none", "host").contains(networkMode)) { + dockerClient.connectToNetworkCmd().withContainerId(containerId).withNetworkId(networkId).exec(); + } + }); + } + /** * Stops the container. */ @@ -479,6 +493,10 @@ private void applyConfiguration(CreateContainerCmd createCommand) { createCommand.withPublishAllPorts(true); + PortForwardingContainer.INSTANCE.getNetwork().ifPresent(it -> { + withExtraHost(INTERNAL_HOST_HOSTNAME, it.getIpAddress()); + }); + String[] extraHostsArray = extraHosts.stream() .toArray(String[]::new); createCommand.withExtraHosts(extraHostsArray); diff --git a/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java b/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java new file mode 100644 index 00000000000..576ac33d148 --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java @@ -0,0 +1,69 @@ +package org.testcontainers.containers; + +import com.github.dockerjava.api.model.ContainerNetwork; +import com.trilead.ssh2.Connection; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.SneakyThrows; +import org.testcontainers.utility.TestcontainersConfiguration; + +import java.time.Duration; +import java.util.Collections; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public enum PortForwardingContainer { + INSTANCE; + + private GenericContainer container; + + private final Set exposedPorts = Collections.newSetFromMap(new ConcurrentHashMap<>()); + + @Getter(value = AccessLevel.PRIVATE, lazy = true) + private final Connection sshConnection = createSSHSession(); + + @SneakyThrows + private Connection createSSHSession() { + String password = UUID.randomUUID().toString(); + container = new GenericContainer<>(TestcontainersConfiguration.getInstance().getSSHdImage()) + .withExposedPorts(22) + .withEnv("PASSWORD", password) + .withCommand( + "sh", + "-c", + // Disable ipv6 & Make it listen on all interfaces, not just localhost + "echo \"root:$PASSWORD\" | chpasswd && /usr/sbin/sshd -D -o PermitRootLogin=yes -o AddressFamily=inet -o GatewayPorts=yes" + ); + container.start(); + + Connection connection = new Connection(container.getContainerIpAddress(), container.getMappedPort(22)); + + connection.setTCPNoDelay(true); + connection.connect( + (hostname, port, serverHostKeyAlgorithm, serverHostKey) -> true, + (int) Duration.ofSeconds(30).toMillis(), + (int) Duration.ofSeconds(30).toMillis() + ); + + if (!connection.authenticateWithPassword("root", password)) { + throw new IllegalStateException("Authentication failed."); + } + + return connection; + } + + @SneakyThrows + public void exposeHostPort(int port) { + if (exposedPorts.add(port)) { + getSshConnection().requestRemotePortForwarding("", port, "localhost", port); + } + } + + Optional getNetwork() { + return Optional.ofNullable(container) + .map(GenericContainer::getContainerInfo) + .flatMap(it -> it.getNetworkSettings().getNetworks().values().stream().findFirst()); + } +} diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 567970bc6fc..df05ce21b4f 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -50,6 +50,10 @@ public String getRyukImage() { return (String) properties.getOrDefault("ryuk.container.image", "quay.io/testcontainers/ryuk:0.2.2"); } + public String getSSHdImage() { + return (String) properties.getOrDefault("sshd.container.image", "quay.io/testcontainers/sshd@sha256:18aa929c653284189fc9cefa45b731021857b6047a0a1757e909f958f258f088"); + } + public Integer getRyukTimeout() { return Integer.parseInt((String) properties.getOrDefault("ryuk.container.timeout", "30")); } diff --git a/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java b/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java new file mode 100644 index 00000000000..30bb6573533 --- /dev/null +++ b/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java @@ -0,0 +1,64 @@ +package org.testcontainers.containers; + +import com.sun.net.httpserver.HttpServer; +import lombok.SneakyThrows; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.testcontainers.Testcontainers; + +import java.io.OutputStream; +import java.net.InetSocketAddress; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; + +public class ExposedHostTest { + + private static HttpServer server; + + @BeforeClass + public static void setUpClass() throws Exception { + server = HttpServer.create(new InetSocketAddress(0), 0); + server.createContext("/", exchange -> { + byte[] content = "Hello World!".getBytes(); + exchange.sendResponseHeaders(200, content.length); + try (OutputStream responseBody = exchange.getResponseBody()) { + responseBody.write(content); + responseBody.flush(); + } + }); + + server.start(); + Testcontainers.exposeHostPorts(server.getAddress().getPort()); + } + + @AfterClass + public static void tearDownClass() throws Exception { + server.stop(0); + } + + @Test + public void testExposedHost() throws Exception { + assertResponse(new GenericContainer().withCommand("top")); + } + + @Test + public void testExposedHostWithNetwork() throws Exception { + try (Network network = Network.newNetwork()) { + assertResponse(new GenericContainer().withNetwork(network).withCommand("top")); + } + } + + @SneakyThrows + protected void assertResponse(GenericContainer container) { + try { + container.start(); + + String response = container.execInContainer("wget", "-O", "-", "http://host.testcontainers.internal:" + server.getAddress().getPort()).getStdout(); + + assertEquals("received response", "Hello World!", response); + } finally { + container.stop(); + } + } +} From 92d720c66524f2c86fa7c85da64ced53b875888b Mon Sep 17 00:00:00 2001 From: Nikita Salnikov-Tarnovski Date: Sun, 26 Aug 2018 10:42:27 +0300 Subject: [PATCH 0390/5615] Added documentation for use of Kafka Test containers (#840) --- docs/usage.md | 3 +- docs/usage/kafka_containers.md | 51 ++++++++++++++++++++++++++++++ docs/usage/webdriver_containers.md | 6 ++-- 3 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 docs/usage/kafka_containers.md diff --git a/docs/usage.md b/docs/usage.md index 8ffabb3a96b..5460c97164c 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -24,7 +24,8 @@ Testcontainers will try to connect to a Docker daemon using the following strate ### Usage modes * [Temporary database containers](usage/database_containers.md) - specialized Microsoft SQL Server, MariaDB, MySQL, PostgreSQL, Oracle XE and Virtuoso container support -* [Webdriver containers](usage/webdriver_containers.md) - run a Dockerized Chrome or Firefox browser ready for Selenium/Webdriver operations - complete with automatic video recording +* [Webdriver containers](usage/webdriver_containers.md) - run a dockerized Chrome or Firefox browser ready for Selenium/Webdriver operations - complete with automatic video recording +* [Kafka containers](usage/kafka_containers.md) - run a dockerized Kafka, a distributed streaming platform * [Generic containers](usage/generic_containers.md) - run any Docker container as a test dependency * [Docker compose](usage/docker_compose.md) - reuse services defined in a Docker Compose YAML file * [Dockerfile containers](usage/dockerfile.md) - run a container that is built on-the-fly from a Dockerfile diff --git a/docs/usage/kafka_containers.md b/docs/usage/kafka_containers.md new file mode 100644 index 00000000000..286a9b7cd67 --- /dev/null +++ b/docs/usage/kafka_containers.md @@ -0,0 +1,51 @@ +# Kafka Containers + +Testcontainers can be used to automatically instantiate and manage [Apache Kafka](https://kafka.apache.org) containers. +More precisely Testcontainers uses the official Docker images for [Confluent OSS Platform](https://hub.docker.com/r/confluentinc/cp-kafka/) + +## Benefits + +* Running a single node Kafka installation with just one line of code +* No need to manage external Zookeeper installation, required by Kafka. But see [below](#zookeeper) + +## Example + +The following field in your JUnit test class will prepare a container running Kafka: +```java +@Rule +public KafkaContainer kafka = new KafkaContainer(); +``` + +Now your tests or any other process running on your machine can get access to running Kafka broker by using the following bootstrap server location: +```java +kafka.getBootstrapServers() +``` + +## Options + +### Selecting Kafka version + +You can select a version of Confluent Platform by passing it to the container's constructor: +```java +new KafkaContainer("4.1.2") +``` +The correspondence between Confluent Platform versions and Kafka versions can be seen [in Confluent documentation](https://docs.confluent.io/current/installation/versions-interoperability.html#cp-and-apache-kafka-compatibility) + +### Using external Zookeeper + +If for some reason you want to use an externally running Zookeeper, then just pass its location during construction: +```java +new KafkaContainer().withExternalZookeeper("localhost:2181") +``` + +## Multi-container usage + +If your test needs to run some other Docker container which needs access to the Kafka, do the following: + +* Run you other container on the same network as Kafka container. E.g. as following: +```java +new GenericContainer("myImage").withNetwork(kafka.getNetwork()) +``` +* Use `kafka.getNetworkAliases().get(0)+":9092"` as bootstrap server location. +Or just give your Kafka container a network alias of your liking. + diff --git a/docs/usage/webdriver_containers.md b/docs/usage/webdriver_containers.md index 7a8023f685e..926257ccff9 100644 --- a/docs/usage/webdriver_containers.md +++ b/docs/usage/webdriver_containers.md @@ -1,6 +1,6 @@ # Webdriver Containers -Test Containers can be used to automatically instantiate and manage containers that include web browsers, such as those +Testcontainers can be used to automatically instantiate and manage containers that include web browsers, such as those from SeleniumHQ's [docker-selenium](https://github.com/SeleniumHQ/docker-selenium) project. ## Benefits @@ -13,7 +13,7 @@ from SeleniumHQ's [docker-selenium](https://github.com/SeleniumHQ/docker-seleniu * Compatibility between browser version and the Selenium API is assured: a compatible version of the browser docker images will be automatically selected to match the version of `selenium-api-*.jar` on the classpath * Additionally the use of a clean browser prevents leakage of cookies, cached data or other state between tests. -* **VNC screen recording**: Test Containers can automatically record video of test runs (optionally capturing just +* **VNC screen recording**: Testcontainers can automatically record video of test runs (optionally capturing just failing tests) Creation of browser containers is fast, so it's actually quite feasible to have a totally fresh browser instance for @@ -62,7 +62,7 @@ new BrowserWebDriverContainer() ### Recording videos -By default, no videos will be recorded. However, you can instruct Test Containers to capture videos for all tests or +By default, no videos will be recorded. However, you can instruct Testcontainers to capture videos for all tests or just for failing tests. To do this, simply add extra parameters to the rule constructor: From eaa1a8580f38a99d43b0e34f29fd750183244b4b Mon Sep 17 00:00:00 2001 From: Vladislav Rassokhin Date: Thu, 30 Aug 2018 22:35:44 +0300 Subject: [PATCH 0391/5615] JDBCDriverTest: replace boolean parameters with EnumSet (#849) --- .../testcontainers/jdbc/JDBCDriverTest.java | 56 ++++++++++--------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java index e303fa85565..762e2ab639a 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java @@ -15,6 +15,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.util.EnumSet; import static java.util.Arrays.asList; import static org.junit.Assume.assumeFalse; @@ -24,37 +25,38 @@ @RunWith(ParallelParameterized.class) public class JDBCDriverTest { + private enum Options { + ScriptedSchema, + CharacterSet, + CustomIniFile, + JDBCParams, + } + @Parameter public String jdbcUrl; @Parameter(1) - public boolean performTestForScriptedSchema; - @Parameter(2) - public boolean performTestForCharacterSet; - @Parameter(3) - public boolean performTestForCustomIniFile; - @Parameter(4) - public boolean performTestForJDBCParams; + public EnumSet options; @Parameterized.Parameters(name = "{index} - {0}") public static Iterable data() { return asList( new Object[][]{ - {"jdbc:tc:mysql://hostname/databasename", false, false, false, false}, - {"jdbc:tc:mysql://hostname/databasename?user=someuser&TC_INITSCRIPT=somepath/init_mysql.sql", true, false, false, true}, - {"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", true, false, false, true}, - {"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=somepath/init_mysql.sql", true, false, false, true}, - {"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&password=somepwd&TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", true, false, false, true}, - {"jdbc:tc:mysql:5.5.43://hostname/databasename?TC_INITSCRIPT=somepath/init_unicode_mysql.sql&useUnicode=yes&characterEncoding=utf8", false, true, false, false}, - {"jdbc:tc:mysql:5.5.43://hostname/databasename", false, false, false, false}, - {"jdbc:tc:mysql:5.5.43://hostname/databasename?useSSL=false", false, false, false, false}, - {"jdbc:tc:postgresql:9.6.8://hostname/databasename", false, false, false, false}, - {"jdbc:tc:mysql:5.6://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override", false, false, true, false}, - {"jdbc:tc:mariadb://hostname/databasename", false, false, false, false}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename", false, false, false, false}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITSCRIPT=somepath/init_unicode_mysql.sql&useUnicode=yes&characterEncoding=utf8", false, true, false, false}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITSCRIPT=somepath/init_mariadb.sql", true, false, false, false}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", true, false, false, false}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_MY_CNF=somepath/mariadb_conf_override", false, false, true, false}}); + {"jdbc:tc:mysql://hostname/databasename", EnumSet.noneOf(Options.class)}, + {"jdbc:tc:mysql://hostname/databasename?user=someuser&TC_INITSCRIPT=somepath/init_mysql.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, + {"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, + {"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=somepath/init_mysql.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, + {"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&password=somepwd&TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, + {"jdbc:tc:mysql:5.5.43://hostname/databasename?TC_INITSCRIPT=somepath/init_unicode_mysql.sql&useUnicode=yes&characterEncoding=utf8", EnumSet.of(Options.CharacterSet)}, + {"jdbc:tc:mysql:5.5.43://hostname/databasename", EnumSet.noneOf(Options.class)}, + {"jdbc:tc:mysql:5.5.43://hostname/databasename?useSSL=false", EnumSet.noneOf(Options.class)}, + {"jdbc:tc:postgresql:9.6.8://hostname/databasename", EnumSet.noneOf(Options.class)}, + {"jdbc:tc:mysql:5.6://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override", EnumSet.of(Options.CustomIniFile)}, + {"jdbc:tc:mariadb://hostname/databasename", EnumSet.noneOf(Options.class)}, + {"jdbc:tc:mariadb:10.2.14://hostname/databasename", EnumSet.noneOf(Options.class)}, + {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITSCRIPT=somepath/init_unicode_mysql.sql&useUnicode=yes&characterEncoding=utf8", EnumSet.of(Options.CharacterSet)}, + {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITSCRIPT=somepath/init_mariadb.sql", EnumSet.of(Options.ScriptedSchema)}, + {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema)}, + {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_MY_CNF=somepath/mariadb_conf_override", EnumSet.of(Options.CustomIniFile)}}); } public static void sampleInitFunction(Connection connection) throws SQLException { @@ -76,15 +78,15 @@ public static void testCleanup() { public void test() throws SQLException { performSimpleTest(jdbcUrl); - if (performTestForScriptedSchema) { + if (options.contains(Options.ScriptedSchema)) { performTestForScriptedSchema(jdbcUrl); } - if (performTestForJDBCParams) { + if (options.contains(Options.JDBCParams)) { performTestForJDBCParamUsage(jdbcUrl); } - if (performTestForCharacterSet) { + if (options.contains(Options.CharacterSet)) { //Called twice to ensure that the query string parameters are used when //connections are created from cached containers. performSimpleTestWithCharacterSet(jdbcUrl); @@ -93,7 +95,7 @@ public void test() throws SQLException { performTestForCharacterEncodingForInitialScriptConnection(jdbcUrl); } - if (performTestForCustomIniFile) { + if (options.contains(Options.CustomIniFile)) { performTestForCustomIniFile(jdbcUrl); } } From 5d3904e1580f581dc42beaaaecd4f5ba3e5bc05d Mon Sep 17 00:00:00 2001 From: haphut Date: Thu, 30 Aug 2018 22:36:36 +0300 Subject: [PATCH 0392/5615] Update Pulsar to 2.1.0 (#824) --- modules/pulsar/build.gradle | 2 +- .../java/org/testcontainers/containers/PulsarContainer.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 755b9c6eff5..d4faaa3c610 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Pulsar" dependencies { compile project(':testcontainers') - testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.0.1-incubating' + testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.1.0-incubating' testCompile group: 'org.assertj', name: 'assertj-core', version: '3.10.0' } diff --git a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java index c3680e0517e..59ca04eab9d 100644 --- a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java +++ b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java @@ -11,7 +11,7 @@ public class PulsarContainer extends GenericContainer { public static final int PULSAR_PORT = 6850; public PulsarContainer() { - this("2.0.1-incubating"); + this("2.1.0-incubating"); } public PulsarContainer(String pulsarVersion) { From 6f60cdfa0c12385b29ff36f4181e6dc8e1544b71 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 3 Sep 2018 13:28:21 +0200 Subject: [PATCH 0393/5615] switch to OkHttp as default transport (with opt-inable Npipe support) (#853) --- .../dockerclient/NpipeSocketClientProviderStrategy.java | 3 ++- .../testcontainers/utility/TestcontainersConfiguration.java | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java index b8d85f11623..ae9b888f117 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java @@ -7,6 +7,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.SystemUtils; import org.jetbrains.annotations.NotNull; +import org.testcontainers.utility.TestcontainersConfiguration; import java.net.URI; @@ -23,7 +24,7 @@ public class NpipeSocketClientProviderStrategy extends DockerClientProviderStrat @Override protected boolean isApplicable() { - return SystemUtils.IS_OS_WINDOWS; + return SystemUtils.IS_OS_WINDOWS && TestcontainersConfiguration.getInstance().isNpipeForced(); } @Override diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index df05ce21b4f..69b5ede2d41 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -75,7 +75,11 @@ public String getDockerClientStrategyClassName() { } public String getTransportType() { - return properties.getProperty("transport.type", "netty"); + return properties.getProperty("transport.type", "okhttp"); + } + + public boolean isNpipeForced() { + return Boolean.parseBoolean(properties.getProperty("transport.npipe.forced", "false")); } @Synchronized From c91513d9ebfb3051c2ddaf9b0581e7dbb10e236a Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 3 Sep 2018 21:58:06 +0200 Subject: [PATCH 0394/5615] use copyFileToContainer where possible (#829) * use copyFileToContainer where possible * fix Javadoc * call `Transferable`-based `copyFileToContainer` method from the `MountableFile`-based one, fix `CopyFileToContainerTest` * do not use `File` in `copyFileToContainer` * fix empty remotePath * Use getResolvedPath() Also rename variables in copyFileToContainer() for better readability. --- .../testcontainers/containers/Container.java | 26 ++++- .../containers/GenericContainer.java | 65 +++++++++-- .../VncRecordingSidekickContainer.java | 94 ---------------- .../images/builder/Transferable.java | 27 ++++- .../utility/ThrowingFunction.java | 6 + .../junit/CopyFileToContainerTest.java | 2 +- .../junit/FileOperationsTest.java | 105 ++++++++++++++++++ .../junit/GenericContainerRuleTest.java | 52 --------- .../containers/CassandraContainer.java | 2 +- .../couchbase/CouchbaseContainer.java | 37 +++--- .../containers/JdbcDatabaseContainer.java | 2 +- 11 files changed, 230 insertions(+), 188 deletions(-) delete mode 100644 core/src/main/java/org/testcontainers/containers/VncRecordingSidekickContainer.java create mode 100644 core/src/main/java/org/testcontainers/utility/ThrowingFunction.java create mode 100644 core/src/test/java/org/testcontainers/junit/FileOperationsTest.java diff --git a/core/src/main/java/org/testcontainers/containers/Container.java b/core/src/main/java/org/testcontainers/containers/Container.java index 7aeb015e8f2..6c5e547f7fa 100644 --- a/core/src/main/java/org/testcontainers/containers/Container.java +++ b/core/src/main/java/org/testcontainers/containers/Container.java @@ -9,10 +9,13 @@ import org.testcontainers.containers.startupcheck.StartupCheckStrategy; import org.testcontainers.containers.traits.LinkableContainer; import org.testcontainers.containers.wait.strategy.WaitStrategy; +import org.testcontainers.images.builder.Transferable; import org.testcontainers.utility.LogUtils; import org.testcontainers.utility.MountableFile; +import org.testcontainers.utility.ThrowingFunction; import java.io.IOException; +import java.io.InputStream; import java.nio.charset.Charset; import java.time.Duration; import java.util.List; @@ -429,14 +432,23 @@ ExecResult execInContainer(Charset outputCharset, String... command) /** * - * Copies a file which resides inside the classpath to the container. + * Copies a file or directory to the container. * - * @param mountableLocalFile file which is copied into the container + * @param mountableFile file or directory which is copied into the container * @param containerPath destination path inside the container * @throws IOException if there's an issue communicating with Docker * @throws InterruptedException if the thread waiting for the response is interrupted */ - void copyFileToContainer(MountableFile mountableLocalFile, String containerPath) throws IOException, InterruptedException; + void copyFileToContainer(MountableFile mountableFile, String containerPath) throws IOException, InterruptedException; + + /** + * + * Copies a file to the container. + * + * @param transferable file which is copied into the container + * @param containerPath destination path inside the container + */ + void copyFileToContainer(Transferable transferable, String containerPath); /** * Copies a file which resides inside the container to user defined directory @@ -448,6 +460,14 @@ ExecResult execInContainer(Charset outputCharset, String... command) */ void copyFileFromContainer(String containerPath, String destinationPath) throws IOException, InterruptedException; + /** + * Streams a file which resides inside the container + * + * @param containerPath path to file which is copied from container + * @param function function that takes InputStream of the copied file + */ + T copyFileFromContainer(String containerPath, ThrowingFunction function); + List getPortBindings(); List getExtraHosts(); diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index bab40d51ffb..b7bfe0d1e8e 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -17,8 +17,11 @@ import lombok.EqualsAndHashCode; import lombok.NonNull; import lombok.Setter; +import lombok.SneakyThrows; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; +import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.apache.commons.compress.utils.IOUtils; +import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.junit.runner.Description; @@ -40,14 +43,18 @@ import org.testcontainers.containers.wait.WaitStrategy; import org.testcontainers.containers.wait.strategy.WaitStrategyTarget; import org.testcontainers.images.RemoteDockerImage; +import org.testcontainers.images.builder.Transferable; import org.testcontainers.lifecycle.Startable; -import org.testcontainers.lifecycle.TestLifecycleAware; import org.testcontainers.lifecycle.TestDescription; +import org.testcontainers.lifecycle.TestLifecycleAware; import org.testcontainers.utility.*; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.nio.charset.Charset; import java.nio.file.Path; import java.time.Duration; @@ -1036,34 +1043,70 @@ public ExecResult execInContainer(String... command) * {@inheritDoc} */ @Override - public void copyFileToContainer(MountableFile mountableLocalFile, String containerPath) { + public void copyFileToContainer(MountableFile mountableFile, String containerPath) { + File sourceFile = new File(mountableFile.getResolvedPath()); + + if (containerPath.endsWith("/") && sourceFile.isFile()) { + logger().warn("folder-like containerPath in copyFileToContainer is deprecated, please explicitly specify a file path"); + copyFileToContainer((Transferable) mountableFile, containerPath + sourceFile.getName()); + } else { + copyFileToContainer((Transferable) mountableFile, containerPath); + } + } + @Override + @SneakyThrows(IOException.class) + public void copyFileToContainer(Transferable transferable, String containerPath) { if (!isCreated()) { throw new IllegalStateException("copyFileToContainer can only be used with created / running container"); } - this.dockerClient - .copyArchiveToContainerCmd(this.containerId) - .withHostResource(mountableLocalFile.getResolvedPath()) - .withRemotePath(containerPath) + try ( + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + TarArchiveOutputStream tarArchive = new TarArchiveOutputStream(byteArrayOutputStream) + ) { + tarArchive.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); + + int lastSlashIndex = StringUtils.removeEnd(containerPath, "/").lastIndexOf("/"); + String extractArchiveTo = containerPath.substring(0, lastSlashIndex + 1); + String pathInArchive = containerPath.substring(lastSlashIndex + 1); + transferable.transferTo(tarArchive, pathInArchive); + tarArchive.finish(); + + dockerClient + .copyArchiveToContainerCmd(containerId) + .withTarInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray())) + .withRemotePath(extractArchiveTo) .exec(); + } } /** * {@inheritDoc} */ @Override - public void copyFileFromContainer(String containerPath, String destinationPath) throws IOException { + public void copyFileFromContainer(String containerPath, String destinationPath) { + copyFileFromContainer(containerPath, inputStream -> { + try(FileOutputStream output = new FileOutputStream(destinationPath)) { + IOUtils.copy(inputStream, output); + return null; + } + }); + } + @Override + @SneakyThrows(Exception.class) + public T copyFileFromContainer(String containerPath, ThrowingFunction consumer) { if (!isCreated()) { throw new IllegalStateException("copyFileFromContainer can only be used when the Container is created."); } - try (final TarArchiveInputStream tarInputStream = new TarArchiveInputStream(this.dockerClient - .copyArchiveFromContainerCmd(this.containerId, containerPath) - .exec())) { + try ( + InputStream inputStream = dockerClient.copyArchiveFromContainerCmd(containerId, containerPath).exec(); + TarArchiveInputStream tarInputStream = new TarArchiveInputStream(inputStream) + ) { tarInputStream.getNextTarEntry(); - IOUtils.copy(tarInputStream, new FileOutputStream(destinationPath)); + return consumer.apply(tarInputStream); } } diff --git a/core/src/main/java/org/testcontainers/containers/VncRecordingSidekickContainer.java b/core/src/main/java/org/testcontainers/containers/VncRecordingSidekickContainer.java deleted file mode 100644 index d1dfb5350c0..00000000000 --- a/core/src/main/java/org/testcontainers/containers/VncRecordingSidekickContainer.java +++ /dev/null @@ -1,94 +0,0 @@ -package org.testcontainers.containers; - -import com.github.dockerjava.api.command.InspectContainerResponse; -import org.jetbrains.annotations.NotNull; -import org.testcontainers.containers.traits.LinkableContainer; -import org.testcontainers.containers.traits.VncService; -import org.testcontainers.utility.TestcontainersConfiguration; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardCopyOption; -import java.util.Set; - -import static java.util.Collections.emptySet; - -/** - * 'Sidekick container' with the sole purpose of recording the VNC screen output from another container. - * - * @deprecated please use {@link VncRecordingContainer} - */ -@Deprecated -public class VncRecordingSidekickContainer, T extends VncService & LinkableContainer> extends GenericContainer { - private final T vncServiceContainer; - private final Path tempDir; - - /** - * Create a sidekick container and link it to another container. The VNC output of that container will be recorded. - * - * @param vncServiceContainer the container whose screen should be recorded. This container must implement VncService and LinkableContainer. - */ - public VncRecordingSidekickContainer(T vncServiceContainer) { - super(TestcontainersConfiguration.getInstance().getVncRecordedContainerImage()); - - this.vncServiceContainer = vncServiceContainer; - - try { - // Use a temporary volume directory that the container should record a file into - this.tempDir = createVolumeDirectory(true); - - // flvrec.py needs to read the VNC password from a file - Path passwordFile = tempDir.resolve("password"); - Files.write(passwordFile, vncServiceContainer.getPassword().getBytes()); - } catch (IOException e) { - throw new RuntimeException(e); - } - - } - - @Override - protected void containerIsStarting(InspectContainerResponse containerInfo) { - // do nothing - } - - @NotNull - @Override - protected Set getLivenessCheckPorts() { - // no liveness check needed - return emptySet(); - } - - @Override - protected void configure() { - - addFileSystemBind(tempDir.toAbsolutePath().toString(), "/recording", BindMode.READ_WRITE); - addLink(vncServiceContainer, "vnchost"); - setCommand(// the container entrypoint sets the executable name - "-o", - "/recording/screen.flv", - "-P", - "/recording/password", - "vnchost", - String.valueOf(vncServiceContainer.getPort())); - } - - public Path getRecordingPath() { - return this.tempDir.resolve("screen.flv"); - } - - /** - * Stop the container and move the recording file to a suitable destination - * @param destination the location the recording should be moved to, overwriting any existing file - */ - public void stopAndRetainRecording(File destination) { - super.stop(); - - try { - Files.move(getRecordingPath(), destination.toPath(), StandardCopyOption.REPLACE_EXISTING); - } catch (IOException e) { - throw new RuntimeException("Could not move recording file from " + getRecordingPath() + " to " + destination, e); - } - } -} diff --git a/core/src/main/java/org/testcontainers/images/builder/Transferable.java b/core/src/main/java/org/testcontainers/images/builder/Transferable.java index b68ae95ac43..3f068f98a18 100644 --- a/core/src/main/java/org/testcontainers/images/builder/Transferable.java +++ b/core/src/main/java/org/testcontainers/images/builder/Transferable.java @@ -11,6 +11,29 @@ public interface Transferable { int DEFAULT_FILE_MODE = 0100644; int DEFAULT_DIR_MODE = 040755; + static Transferable of(byte[] bytes) { + return of(bytes, DEFAULT_FILE_MODE); + } + + static Transferable of(byte[] bytes, int fileMode) { + return new Transferable() { + @Override + public long getSize() { + return bytes.length; + } + + @Override + public byte[] getBytes() { + return bytes; + } + + @Override + public int getFileMode() { + return fileMode; + } + }; + } + /** * Get file mode. Default is 0100644. * @@ -52,5 +75,7 @@ default byte[] getBytes() { return new byte[0]; } - String getDescription(); + default String getDescription() { + return ""; + } } diff --git a/core/src/main/java/org/testcontainers/utility/ThrowingFunction.java b/core/src/main/java/org/testcontainers/utility/ThrowingFunction.java new file mode 100644 index 00000000000..6b2cef907e1 --- /dev/null +++ b/core/src/main/java/org/testcontainers/utility/ThrowingFunction.java @@ -0,0 +1,6 @@ +package org.testcontainers.utility; + +public interface ThrowingFunction { + + R apply(T t) throws Exception; +} diff --git a/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java b/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java index f4c902cfa46..5164ea6c6e1 100644 --- a/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java @@ -7,7 +7,7 @@ import java.io.IOException; public class CopyFileToContainerTest { - private static String containerPath = "/tmp"; + private static String containerPath = "/tmp/mappable-resource/"; private static String fileName = "test-resource.txt"; @Test diff --git a/core/src/test/java/org/testcontainers/junit/FileOperationsTest.java b/core/src/test/java/org/testcontainers/junit/FileOperationsTest.java new file mode 100644 index 00000000000..95de9ef1b85 --- /dev/null +++ b/core/src/test/java/org/testcontainers/junit/FileOperationsTest.java @@ -0,0 +1,105 @@ +package org.testcontainers.junit; + +import com.github.dockerjava.api.exception.NotFoundException; +import org.apache.commons.io.FileUtils; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.utility.MountableFile; + +import java.io.File; +import java.io.IOException; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; + +public class FileOperationsTest { + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Test + public void copyFileToContainerFileTest() throws Exception { + try ( + GenericContainer alpineCopyToContainer = new GenericContainer("alpine:3.2") + .withCommand("top") + ) { + alpineCopyToContainer.start(); + final MountableFile mountableFile = MountableFile.forClasspathResource("test_copy_to_container.txt"); + alpineCopyToContainer.copyFileToContainer(mountableFile, "/test.txt"); + + File actualFile = new File(temporaryFolder.getRoot().getAbsolutePath() + "/test_copy_to_container.txt"); + alpineCopyToContainer.copyFileFromContainer("/test.txt", actualFile.getPath()); + + File expectedFile = new File(mountableFile.getResolvedPath()); + assertTrue("Files aren't same ", FileUtils.contentEquals(expectedFile, actualFile)); + } + } + + @Test + public void copyFileToContainerFolderTest() throws Exception { + try ( + GenericContainer alpineCopyToContainer = new GenericContainer("alpine:3.2") + .withCommand("top") + ) { + alpineCopyToContainer.start(); + final MountableFile mountableFile = MountableFile.forClasspathResource("test_copy_to_container.txt"); + alpineCopyToContainer.copyFileToContainer(mountableFile, "/home/"); + + File actualFile = new File(temporaryFolder.getRoot().getAbsolutePath() + "/test_copy_to_container.txt"); + alpineCopyToContainer.copyFileFromContainer("/home/test_copy_to_container.txt", actualFile.getPath()); + + File expectedFile = new File(mountableFile.getResolvedPath()); + assertTrue("Files aren't same ", FileUtils.contentEquals(expectedFile, actualFile)); + } + } + + @Test + public void copyFolderToContainerFolderTest() throws Exception { + try ( + GenericContainer alpineCopyToContainer = new GenericContainer("alpine:3.2") + .withCommand("top") + ) { + + alpineCopyToContainer.start(); + final MountableFile mountableFile = MountableFile.forClasspathResource("mappable-resource/"); + alpineCopyToContainer.copyFileToContainer(mountableFile, "/home/test/"); + + File actualFile = new File(temporaryFolder.getRoot().getAbsolutePath() + "/test_copy_to_container.txt"); + alpineCopyToContainer.copyFileFromContainer("/home/test/test-resource.txt", actualFile.getPath()); + + File expectedFile = new File(mountableFile.getResolvedPath() + "/test-resource.txt"); + assertTrue("Files aren't same ", FileUtils.contentEquals(expectedFile, actualFile)); + } + } + + @Test(expected = NotFoundException.class) + public void copyFromContainerShouldFailBecauseNoFileTest() throws NotFoundException { + try ( + GenericContainer alpineCopyToContainer = new GenericContainer("alpine:3.2") + .withCommand("top") + ) { + alpineCopyToContainer.start(); + alpineCopyToContainer.copyFileFromContainer("/home/test.txt", "src/test/resources/copy-from/test.txt"); + } + } + + @Test + public void shouldCopyFileFromContainerTest() throws IOException { + try ( + GenericContainer alpineCopyToContainer = new GenericContainer("alpine:3.2") + .withCommand("top") + ) { + + alpineCopyToContainer.start(); + final MountableFile mountableFile = MountableFile.forClasspathResource("test_copy_to_container.txt"); + alpineCopyToContainer.copyFileToContainer(mountableFile, "/home/"); + + File actualFile = new File(temporaryFolder.getRoot().getAbsolutePath() + "/test_copy_from_container.txt"); + alpineCopyToContainer.copyFileFromContainer("/home/test_copy_to_container.txt", actualFile.getPath()); + + File expectedFile = new File(mountableFile.getResolvedPath()); + assertTrue("Files aren't same ", FileUtils.contentEquals(expectedFile, actualFile)); + } + } +} diff --git a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java index 7325cb809fb..deb13453287 100644 --- a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java +++ b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java @@ -1,21 +1,17 @@ package org.testcontainers.junit; -import com.github.dockerjava.api.exception.NotFoundException; import com.google.common.collect.ImmutableMap; -import com.google.common.io.Files; import com.google.common.util.concurrent.Uninterruptibles; import com.mongodb.MongoClient; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; import com.rabbitmq.client.*; -import org.apache.commons.io.FileUtils; import org.bson.Document; import org.junit.*; import org.rnorth.ducttape.RetryCountExceededException; import org.rnorth.ducttape.unreliables.Unreliables; import org.testcontainers.containers.GenericContainer; import org.testcontainers.utility.Base58; -import org.testcontainers.utility.MountableFile; import org.testcontainers.utility.TestEnvironment; import java.io.*; @@ -362,54 +358,6 @@ public void createContainerCmdHookTest() { } } - @Test - public void copyToContainerTest() throws Exception { - final File tempResultFolder = Files.createTempDir(); - - try (final GenericContainer alpineCopyToContainer = new GenericContainer("alpine:3.2") - .withCommand("top")){ - - alpineCopyToContainer.start(); - final MountableFile mountableFile = MountableFile.forClasspathResource("test_copy_to_container.txt"); - alpineCopyToContainer.copyFileToContainer(mountableFile, "/home/"); - alpineCopyToContainer.copyFileFromContainer("/home/test_copy_to_container.txt", - tempResultFolder.getAbsolutePath() + "/test_copy_to_container.txt"); - - File expectedFile = new File(mountableFile.getResolvedPath()); - File actualFile = new File(tempResultFolder.getAbsolutePath() + "/test_copy_to_container.txt"); - assertTrue("Files aren't same ", FileUtils.contentEquals(expectedFile,actualFile)); - } - } - - @Test(expected = NotFoundException.class) - public void copyFromContainerShouldFailBecauseNoFileTest() throws NotFoundException, IOException, InterruptedException { - - try (final GenericContainer alpineCopyToContainer = new GenericContainer("alpine:3.2") - .withCommand("top")) { - alpineCopyToContainer.start(); - alpineCopyToContainer.copyFileFromContainer("/home/test.txt", "src/test/resources/copy-from/test.txt"); - } - } - - @Test - public void shouldCopyFileFromContainerTest() throws IOException, InterruptedException { - final File tempResultFolder = Files.createTempDir(); - - try (final GenericContainer alpineCopyToContainer = new GenericContainer("alpine:3.2") - .withCommand("top")) { - - alpineCopyToContainer.start(); - final MountableFile mountableFile = MountableFile.forClasspathResource("test_copy_to_container.txt"); - alpineCopyToContainer.copyFileToContainer(mountableFile, "/home/"); - alpineCopyToContainer.copyFileFromContainer("/home/test_copy_to_container.txt", - tempResultFolder.getAbsolutePath() + "/test_copy_from_container.txt"); - - File expectedFile = new File(mountableFile.getResolvedPath()); - File actualFile = new File(tempResultFolder.getAbsolutePath() + "/test_copy_from_container.txt"); - assertTrue("Files aren't same ", FileUtils.contentEquals(expectedFile,actualFile)); - } - } - private BufferedReader getReaderForContainerPort80(GenericContainer container) { return Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, () -> { diff --git a/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java b/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java index 0525f0af1f9..dbb3316158f 100644 --- a/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java +++ b/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java @@ -88,7 +88,7 @@ private void runInitScriptIfRequired() { protected void optionallyMapResourceParameterAsVolume(String pathNameInContainer, String resourceLocation) { Optional.ofNullable(resourceLocation) .map(MountableFile::forClasspathResource) - .ifPresent(mountableFile -> addFileSystemBind(mountableFile.getResolvedPath(), pathNameInContainer, BindMode.READ_WRITE)); + .ifPresent(mountableFile -> withCopyFileToContainer(mountableFile, pathNameInContainer)); } /** diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index 16a5f83a176..7cd3b3afb5d 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -30,26 +30,26 @@ import com.google.common.collect.Lists; import lombok.*; import org.apache.commons.compress.utils.Sets; -import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.SocatContainer; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; +import org.testcontainers.images.builder.Transferable; import org.testcontainers.utility.Base58; -import org.testcontainers.utility.MountableFile; import java.io.DataOutputStream; -import java.io.File; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; -import java.nio.file.Files; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import java.util.stream.Stream; import static java.net.HttpURLConnection.HTTP_OK; @@ -332,30 +332,19 @@ public void callCouchbaseRestAPI(String url, String payload) throws IOException } @Override - @SneakyThrows protected void containerIsCreated(String containerId) { - File tempFile = copyConfigFromContainer(containerId); - appendPortsToConfig(tempFile); - copyFileToContainer(MountableFile.forHostPath(tempFile.toPath()), STATIC_CONFIG_PATH); - } + String originalConfig = copyFileFromContainer(STATIC_CONFIG_LOCATION, inputStream -> IOUtils.toString(inputStream, StandardCharsets.UTF_8)); + + byte[] configBytes = (originalConfig + "\n" + generateConfig()).getBytes(StandardCharsets.UTF_8); - private File copyConfigFromContainer(String containerId) throws IOException { - File tempDirectory = Files.createTempDirectory("testcontainer_" + containerId + "_").toFile(); - tempDirectory.deleteOnExit(); - File tempFile = new File(tempDirectory, STATIC_CONFIG_NAME); - tempFile.deleteOnExit(); - logger().debug("Storing static_config in [{}].", tempFile.getAbsolutePath()); - copyFileFromContainer(STATIC_CONFIG_LOCATION, tempFile.getPath()); - return tempFile; + copyFileToContainer(Transferable.of(configBytes), STATIC_CONFIG_LOCATION); } - private void appendPortsToConfig(File tempFile) throws IOException { - for (CouchbasePort port : CouchbasePort.values()) { - if (!port.isDynamic()) { - String config = String.format("{%s, %d}.\n", port.name, getMappedPort(port)); - FileUtils.writeStringToFile(tempFile, config, "UTF-8", true); - } - } + private String generateConfig() { + return Stream.of(CouchbasePort.values()) + .filter(port -> !port.isDynamic()) + .map(port -> String.format("{%s, %d}.", port.name, getMappedPort(port))) + .collect(Collectors.joining("\n")); } @Override diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index 5cd62b68220..c43b74105dc 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -198,7 +198,7 @@ protected void optionallyMapResourceParameterAsVolume(@NotNull String paramName, if (resourceName != null) { final MountableFile mountableFile = MountableFile.forClasspathResource(resourceName); - addFileSystemBind(mountableFile.getResolvedPath(), pathNameInContainer, BindMode.READ_ONLY); + withCopyFileToContainer(mountableFile, pathNameInContainer); } } From f928c4d7e307f765a4bb97df620db30e83bbd245 Mon Sep 17 00:00:00 2001 From: Tellier Benoit Date: Tue, 4 Sep 2018 02:58:26 +0700 Subject: [PATCH 0395/5615] ISSUE-837 Allow specifying the rate limiter for AbstractWaitStrategy (#838) The default rate limit for AbstractWaitStrategy is a constant rate of one try per second. While this safe choice makes sense, some quick-to-start container would benefit from higher rates and thus making testcontainer faster. --- .../wait/strategy/AbstractWaitStrategy.java | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/AbstractWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/AbstractWaitStrategy.java index e0d87578e98..dd9421cccf3 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/AbstractWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/AbstractWaitStrategy.java @@ -10,17 +10,20 @@ public abstract class AbstractWaitStrategy implements WaitStrategy { - protected WaitStrategyTarget waitStrategyTarget; - - @NonNull - protected Duration startupTimeout = Duration.ofSeconds(60); - private static final RateLimiter DOCKER_CLIENT_RATE_LIMITER = RateLimiterBuilder .newBuilder() .withRate(1, TimeUnit.SECONDS) .withConstantThroughput() .build(); + protected WaitStrategyTarget waitStrategyTarget; + + @NonNull + protected Duration startupTimeout = Duration.ofSeconds(60); + + @NonNull + private RateLimiter rateLimiter = DOCKER_CLIENT_RATE_LIMITER; + /** * Wait until the target has started. * @@ -60,6 +63,17 @@ protected Set getLivenessCheckPorts() { * @return the rate limiter to use */ protected RateLimiter getRateLimiter() { - return DOCKER_CLIENT_RATE_LIMITER; + return rateLimiter; + } + + /** + * Set the rate limiter being used + * + * @param rateLimiter rateLimiter + * @return this + */ + public WaitStrategy withRateLimiter(RateLimiter rateLimiter) { + this.rateLimiter = rateLimiter; + return this; } } From 7ea148317e608e88f4046bd453f5eddc7181c558 Mon Sep 17 00:00:00 2001 From: Thomas Hammerl Date: Wed, 5 Sep 2018 13:45:26 +0200 Subject: [PATCH 0396/5615] Fix deadlock when ryuk does not acknowledge filters (#843) * Fix deadlock when ryuk does not acknowledge filters * Extract FilterRegistry in ResourceReaper for unit-testability * Use explicit scoping for FilterRegistry#register * Restructure tests * Update CHANGELOG.md --- .../utility/ResourceReaper.java | 76 ++++++++++++++----- .../utility/FilterRegistryTest.java | 70 +++++++++++++++++ 2 files changed, 126 insertions(+), 20 deletions(-) create mode 100644 core/src/test/java/org/testcontainers/utility/FilterRegistryTest.java diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index 054b216dd19..8d791064643 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -13,6 +13,7 @@ import com.github.dockerjava.api.model.Network; import com.github.dockerjava.api.model.Ports; import com.github.dockerjava.api.model.Volume; +import com.google.common.annotations.VisibleForTesting; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.http.client.utils.URLEncodedUtils; @@ -23,6 +24,7 @@ import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.Socket; @@ -117,8 +119,7 @@ public static String start(String hostIpAddress, DockerClient client, boolean wi while (true) { int index = 0; try(Socket clientSocket = new Socket(hostIpAddress, ryukPort)) { - OutputStream out = clientSocket.getOutputStream(); - BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); + FilterRegistry registry = new FilterRegistry(clientSocket.getInputStream(), clientSocket.getOutputStream()); synchronized (DEATH_NOTE) { while (true) { @@ -131,24 +132,14 @@ public static String start(String hostIpAddress, DockerClient client, boolean wi } } List> filters = DEATH_NOTE.get(index); - - String query = URLEncodedUtils.format( - filters.stream() - .map(it -> new BasicNameValuePair(it.getKey(), it.getValue())) - .collect(Collectors.toList()), - (String) null - ); - - log.debug("Sending '{}' to Ryuk", query); - out.write(query.getBytes()); - out.write('\n'); - out.flush(); - - while (!"ACK".equalsIgnoreCase(in.readLine())) { + boolean isAcknowledged = registry.register(filters); + if (isAcknowledged) { + log.debug("Received 'ACK' from Ryuk"); + ryukScheduledLatch.countDown(); + index++; + } else { + log.debug("Didn't receive 'ACK' from Ryuk. Will retry to send filters."); } - - ryukScheduledLatch.countDown(); - index++; } } } catch (IOException e) { @@ -179,7 +170,6 @@ public synchronized static ResourceReaper instance() { /** * Perform a cleanup. - * */ public synchronized void performCleanup() { registeredContainers.forEach(this::stopContainer); @@ -362,4 +352,50 @@ private void setHook() { Runtime.getRuntime().addShutdownHook(new Thread(DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, this::performCleanup)); } } + + static class FilterRegistry { + + @VisibleForTesting + static final String ACKNOWLEDGMENT = "ACK"; + + private final BufferedReader in; + private final OutputStream out; + + FilterRegistry(InputStream ryukInputStream, OutputStream ryukOutputStream) { + this.in = new BufferedReader(new InputStreamReader(ryukInputStream)); + this.out = ryukOutputStream; + } + + /** + * Registers the given filters with Ryuk + * + * @param filters the filter to register + * @return true if the filters have been registered successfuly, false otherwise + * @throws IOException if communication with Ryuk fails + */ + protected boolean register(List> filters) throws IOException { + String query = URLEncodedUtils.format( + filters.stream() + .map(it -> new BasicNameValuePair(it.getKey(), it.getValue())) + .collect(Collectors.toList()), + (String) null + ); + + log.debug("Sending '{}' to Ryuk", query); + out.write(query.getBytes()); + out.write('\n'); + out.flush(); + + return waitForAcknowledgment(in); + } + + private static boolean waitForAcknowledgment(BufferedReader in) throws IOException { + String line = in.readLine(); + while (line != null && !ACKNOWLEDGMENT.equalsIgnoreCase(line)) { + line = in.readLine(); + } + return ACKNOWLEDGMENT.equalsIgnoreCase(line); + } + + } } diff --git a/core/src/test/java/org/testcontainers/utility/FilterRegistryTest.java b/core/src/test/java/org/testcontainers/utility/FilterRegistryTest.java new file mode 100644 index 00000000000..49bc870e8d0 --- /dev/null +++ b/core/src/test/java/org/testcontainers/utility/FilterRegistryTest.java @@ -0,0 +1,70 @@ +package org.testcontainers.utility; + +import org.junit.Test; +import org.testcontainers.utility.ResourceReaper.FilterRegistry; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.AbstractMap.SimpleEntry; +import java.util.List; +import java.util.Map.Entry; + +import static java.util.Arrays.asList; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class FilterRegistryTest { + + private static final List> FILTERS = asList( + new SimpleEntry<>("key1!", "value2?"), new SimpleEntry<>("key2#", "value2%") + ); + private static final String URL_ENCODED_FILTERS = "key1%21=value2%3F&key2%23=value2%25"; + private static final byte[] ACKNOWLEDGEMENT = FilterRegistry.ACKNOWLEDGMENT.getBytes(); + private static final byte[] NO_ACKNOWLEDGEMENT = "".getBytes(); + private static final String NEW_LINE = "\n"; + + @Test + public void registerReturnsTrueIfAcknowledgementIsReadFromInputStream() throws IOException { + FilterRegistry registry = new FilterRegistry(inputStream(ACKNOWLEDGEMENT), anyOutputStream()); + + boolean successful = registry.register(FILTERS); + + assertTrue(successful); + } + + @Test + public void registerReturnsFalseIfNoAcknowledgementIsReadFromInputStream() throws IOException { + FilterRegistry registry = new FilterRegistry(inputStream(NO_ACKNOWLEDGEMENT), anyOutputStream()); + + boolean successful = registry.register(FILTERS); + + assertFalse(successful); + } + + @Test + public void registerWritesUrlEncodedFiltersAndNewlineToOutputStream() throws IOException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + FilterRegistry registry = new FilterRegistry(anyInputStream(), outputStream); + + registry.register(FILTERS); + + assertEquals(URL_ENCODED_FILTERS + NEW_LINE, new String(outputStream.toByteArray())); + } + + private static InputStream inputStream(byte[] bytes) { + return new ByteArrayInputStream(bytes); + } + + private static InputStream anyInputStream() { + return inputStream(ACKNOWLEDGEMENT); + } + + private static OutputStream anyOutputStream() { + return new ByteArrayOutputStream(); + } + +} From ac05988143e8d433dece58d8824cbb479b29b0c3 Mon Sep 17 00:00:00 2001 From: Dominik Date: Wed, 5 Sep 2018 15:16:53 +0200 Subject: [PATCH 0397/5615] couchbase container fixes (#830) CouchbaseContainer fixes --- .../couchbase/CouchbaseContainer.java | 92 ++++++------------- .../couchbase/BaseCouchbaseContainerTest.java | 21 +++++ .../couchbase/Couchbase5_1Test.java | 2 +- .../couchbase/Couchbase5_5Test.java | 2 +- .../couchbase/CouchbaseContainerTest.java | 2 +- 5 files changed, 51 insertions(+), 68 deletions(-) diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index 7cd3b3afb5d..a22375e8877 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -38,6 +38,7 @@ import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.images.builder.Transferable; import org.testcontainers.utility.Base58; +import org.testcontainers.utility.ThrowingFunction; import java.io.DataOutputStream; import java.io.IOException; @@ -45,19 +46,12 @@ import java.net.URL; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; import static java.net.HttpURLConnection.HTTP_OK; -import static org.testcontainers.couchbase.CouchbaseContainer.CouchbasePort.CAPI; -import static org.testcontainers.couchbase.CouchbaseContainer.CouchbasePort.MEMCACHED; -import static org.testcontainers.couchbase.CouchbaseContainer.CouchbasePort.MEMCACHED_SSL; -import static org.testcontainers.couchbase.CouchbaseContainer.CouchbasePort.REST; -import static org.testcontainers.couchbase.CouchbaseContainer.CouchbasePort.REST_SSL; +import static org.testcontainers.couchbase.CouchbaseContainer.CouchbasePort.*; /** * Based on Laurent Doguin version, @@ -67,12 +61,11 @@ @AllArgsConstructor public class CouchbaseContainer extends GenericContainer { - public static final String VERSION = "5.1.0"; + public static final String VERSION = "5.5.1"; public static final String DOCKER_IMAGE_NAME = "couchbase/server:"; public static final ObjectMapper MAPPER = new ObjectMapper(); - public static final String STATIC_CONFIG_NAME = "static_config"; - public static final String STATIC_CONFIG_PATH = "/opt/couchbase/etc/couchbase/"; - public static final String STATIC_CONFIG_LOCATION = STATIC_CONFIG_PATH + STATIC_CONFIG_NAME; + public static final String STATIC_CONFIG = "/opt/couchbase/etc/couchbase/static_config"; + public static final String CAPI_CONFIG = "/opt/couchbase/etc/couchdb/default.d/capi.ini"; private String memoryQuota = "300"; @@ -96,12 +89,6 @@ public class CouchbaseContainer extends GenericContainer { @Getter private boolean fts = false; - private boolean beerSample = false; - - private boolean travelSample = false; - - private boolean gamesIMSample = false; - @Getter(lazy = true) private final CouchbaseEnvironment couchbaseEnvironment = createCouchbaseEnvironment(); @@ -139,11 +126,6 @@ protected void doStart() { for (CouchbasePort port : CouchbasePort.values()) { exposePortThroughProxy(networkAlias, port.getOriginalPort(), getMappedPort(port)); - - // CAPI port has a special configuration file which is set via env - if (port == CAPI) { - withEnv(port.getName(), getMappedPort(port) + ""); - } } super.doStart(); } @@ -236,25 +218,9 @@ public void initCluster() { String webSettingsURL = "/settings/web"; String webSettingsContent = "username=" + URLEncoder.encode(clusterUsername, "UTF-8") + "&password=" + URLEncoder.encode(clusterPassword, "UTF-8") + "&port=8091"; - String bucketURL = "/sampleBuckets/install"; - - StringBuilder sampleBucketPayloadBuilder = new StringBuilder(); - sampleBucketPayloadBuilder.append('['); - if (travelSample) { - sampleBucketPayloadBuilder.append("\"travel-sample\","); - } - if (beerSample) { - sampleBucketPayloadBuilder.append("\"beer-sample\","); - } - if (gamesIMSample) { - sampleBucketPayloadBuilder.append("\"gamesim-sample\","); - } - sampleBucketPayloadBuilder.append(']'); - callCouchbaseRestAPI(poolURL, poolPayload); callCouchbaseRestAPI(setupServicesURL, setupServiceContent); callCouchbaseRestAPI(webSettingsURL, webSettingsContent); - callCouchbaseRestAPI(bucketURL, sampleBucketPayloadBuilder.toString()); createNodeWaitStrategy().waitUntilReady(this); callCouchbaseRestAPI("/settings/indexes", "indexerThreads=0&logLevel=info&maxRollbackPoints=5&storageMode=memory_optimized"); @@ -333,18 +299,29 @@ public void callCouchbaseRestAPI(String url, String payload) throws IOException @Override protected void containerIsCreated(String containerId) { - String originalConfig = copyFileFromContainer(STATIC_CONFIG_LOCATION, inputStream -> IOUtils.toString(inputStream, StandardCharsets.UTF_8)); - - byte[] configBytes = (originalConfig + "\n" + generateConfig()).getBytes(StandardCharsets.UTF_8); + patchConfig(STATIC_CONFIG, this::addMappedPorts); + // capi needs a special configuration, see https://developer.couchbase.com/documentation/server/current/install/install-ports.html + patchConfig(CAPI_CONFIG, this::replaceCapiPort); + } - copyFileToContainer(Transferable.of(configBytes), STATIC_CONFIG_LOCATION); + private void patchConfig(String configLocation, ThrowingFunction patchFunction) { + String patchedConfig = copyFileFromContainer(configLocation, + inputStream -> patchFunction.apply(IOUtils.toString(inputStream, StandardCharsets.UTF_8))); + copyFileToContainer(Transferable.of(patchedConfig.getBytes(StandardCharsets.UTF_8)), configLocation); } - private String generateConfig() { - return Stream.of(CouchbasePort.values()) + private String addMappedPorts(String originalConfig) { + String portConfig = Stream.of(CouchbasePort.values()) .filter(port -> !port.isDynamic()) .map(port -> String.format("{%s, %d}.", port.name, getMappedPort(port))) .collect(Collectors.joining("\n")); + return String.format("%s\n%s", originalConfig, portConfig); + } + + private String replaceCapiPort(String originalConfig) { + return Arrays.stream(originalConfig.split("\n")) + .map(s -> (s.matches("port\\s*=\\s*" + CAPI.getOriginalPort())) ? "port = " + getMappedPort(CAPI) : s) + .collect(Collectors.joining("\n")); } @Override @@ -381,8 +358,9 @@ public CouchbaseContainer withIndexMemoryQuota(String indexMemoryQuota) { return self(); } - public CouchbaseContainer withClusterPassword(String clusterPassword) { - this.clusterPassword = clusterPassword; + public CouchbaseContainer withClusterAdmin(String username, String password) { + this.clusterUsername = username; + this.clusterPassword = password; return self(); } @@ -411,21 +389,6 @@ public CouchbaseContainer withFts(boolean fts) { return self(); } - public CouchbaseContainer withBeerSample(boolean beerSample) { - this.beerSample = beerSample; - return self(); - } - - public CouchbaseContainer withTravelSample(boolean travelSample) { - this.travelSample = travelSample; - return self(); - } - - public CouchbaseContainer withGamesIMSample(boolean gamesIMSample) { - this.gamesIMSample = gamesIMSample; - return self(); - } - @Getter @RequiredArgsConstructor protected enum CouchbasePort { @@ -442,8 +405,7 @@ protected enum CouchbasePort { QUERY_SSL("ssl_query_port", 18093, false), FTS_SSL("fts_ssl_port", 18094, false), CBAS_SSL("cbas_ssl_port", 18095, false), - EVENTING_SSL("eventing_ssl_port", 18096, false), - ; + EVENTING_SSL("eventing_ssl_port", 18096, false); final String name; diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java index d6ecb41c654..845d9bfaf5e 100644 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java @@ -4,6 +4,10 @@ import com.couchbase.client.java.query.N1qlQuery; import com.couchbase.client.java.query.N1qlQueryResult; import com.couchbase.client.java.query.N1qlQueryRow; +import com.couchbase.client.java.view.DefaultView; +import com.couchbase.client.java.view.DesignDocument; +import com.couchbase.client.java.view.View; +import com.google.common.collect.Lists; import org.junit.Assert; import org.junit.Test; @@ -11,6 +15,12 @@ public abstract class BaseCouchbaseContainerTest extends AbstractCouchbaseTest { + private static final String VIEW_NAME = "testview"; + private static final String VIEW_FUNCTION = "function (doc, meta) {\n" + + " emit(meta.id, null);\n" + + "}"; + + private static final String ID = "toto"; private static final String DOCUMENT = "{\"name\":\"toto\"}"; @@ -34,4 +44,15 @@ public void shouldExecuteN1ql() { Assert.assertEquals(1, n1qlQueryRows.size()); Assert.assertEquals(DOCUMENT, n1qlQueryRows.get(0).value().get(TEST_BUCKET).toString()); } + + @Test + public void shouldCreateView() { + View view = DefaultView.create(VIEW_NAME, VIEW_FUNCTION); + DesignDocument document = DesignDocument.create(VIEW_NAME, Lists.newArrayList(view)); + getBucket().bucketManager().insertDesignDocument(document); + DesignDocument result = getBucket().bucketManager().getDesignDocument(VIEW_NAME); + Assert.assertEquals(1, result.views().size()); + View resultView = result.views().get(0); + Assert.assertEquals(VIEW_NAME, resultView.name()); + } } diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_1Test.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_1Test.java index bc1f66a2847..8334088fdb3 100644 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_1Test.java +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_1Test.java @@ -4,7 +4,7 @@ public class Couchbase5_1Test extends BaseCouchbaseContainerTest { @ClassRule - public static CouchbaseContainer container = initCouchbaseContainer("couchbase/server:5.1.0"); + public static CouchbaseContainer container = initCouchbaseContainer("couchbase/server:5.1.1"); @Override public CouchbaseContainer getCouchbaseContainer() { diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_5Test.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_5Test.java index 8a3635f03be..40a44235f20 100644 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_5Test.java +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_5Test.java @@ -4,7 +4,7 @@ public class Couchbase5_5Test extends BaseCouchbaseContainerTest { @ClassRule - public static CouchbaseContainer container = initCouchbaseContainer("couchbase/server:5.5.0"); + public static CouchbaseContainer container = initCouchbaseContainer("couchbase/server:5.5.1"); @Override public CouchbaseContainer getCouchbaseContainer() { diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java index b15a13b1c18..9e0372ec3c5 100644 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java @@ -7,7 +7,7 @@ public class CouchbaseContainerTest { @Test public void shouldUseCorrectDockerImage() { - CouchbaseContainer couchbaseContainer = new CouchbaseContainer().withBeerSample(true); + CouchbaseContainer couchbaseContainer = new CouchbaseContainer().withClusterAdmin("admin", "foobar"); Assert.assertEquals(CouchbaseContainer.DOCKER_IMAGE_NAME + CouchbaseContainer.VERSION, couchbaseContainer.getDockerImageName()); From 0901ddf6d1d87cd1d353a10bc82c67eed1a93f07 Mon Sep 17 00:00:00 2001 From: Alexandros Antoniadis Date: Wed, 5 Sep 2018 16:44:09 +0300 Subject: [PATCH 0398/5615] UnixDomainSocketInitializer uses configured docker host in connect() (#855) --- .../transport/TestcontainersDockerCmdExecFactory.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java b/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java index 179d24d37f6..1aa9377ce2b 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java @@ -159,7 +159,8 @@ protected void initChannel(final KQueueDomainSocketChannel channel) throws Excep @Override public DuplexChannel connect(Bootstrap bootstrap) throws InterruptedException { - return (DuplexChannel) bootstrap.connect(new DomainSocketAddress("/var/run/docker.sock")).sync().channel(); + String socketPath = getDockerClientConfig().getDockerHost().getPath(); + return (DuplexChannel) bootstrap.connect(new DomainSocketAddress(socketPath)).sync().channel(); } } From 29570469df18f999645e7d29290fb4ac5dda2672 Mon Sep 17 00:00:00 2001 From: Douglas Chimento Date: Wed, 5 Sep 2018 09:45:03 -0400 Subject: [PATCH 0399/5615] make MYSQL_ROOT_PASSWORD and MYSQL_PASSWORD the same value (#857) --- .../main/java/org/testcontainers/containers/MySQLContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java index 8c675e4e930..2dbc303407f 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java @@ -42,7 +42,7 @@ protected void configure() { addEnv("MYSQL_DATABASE", databaseName); addEnv("MYSQL_USER", username); addEnv("MYSQL_PASSWORD", password); - addEnv("MYSQL_ROOT_PASSWORD", "test"); + addEnv("MYSQL_ROOT_PASSWORD", password); setStartupAttempts(3); } From b5564a598ab14f1dd65fa8bfe29abd3848807cef Mon Sep 17 00:00:00 2001 From: Vladislav Rassokhin Date: Thu, 6 Sep 2018 09:06:45 +0300 Subject: [PATCH 0400/5615] Add ClickHouse support (#846) Add ClickHouse module --- modules/clickhouse/build.gradle | 6 ++ .../containers/ClickHouseContainer.java | 72 +++++++++++++++++++ .../containers/ClickHouseProvider.java | 13 ++++ ...s.containers.JdbcDatabaseContainerProvider | 1 + modules/jdbc-test/build.gradle | 2 + .../testcontainers/jdbc/JDBCDriverTest.java | 7 +- .../junit/SimpleClickhouseTest.java | 36 ++++++++++ 7 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 modules/clickhouse/build.gradle create mode 100644 modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java create mode 100644 modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseProvider.java create mode 100644 modules/clickhouse/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider create mode 100644 modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleClickhouseTest.java diff --git a/modules/clickhouse/build.gradle b/modules/clickhouse/build.gradle new file mode 100644 index 00000000000..4f0e1a1fd90 --- /dev/null +++ b/modules/clickhouse/build.gradle @@ -0,0 +1,6 @@ +description = "Testcontainers :: JDBC :: ClickHouse" + +dependencies { + compile project(':testcontainers') + compile project(':jdbc') +} diff --git a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java new file mode 100644 index 00000000000..0fd6174bf3b --- /dev/null +++ b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java @@ -0,0 +1,72 @@ +package org.testcontainers.containers; + +import org.testcontainers.containers.wait.HttpWaitStrategy; + +import java.time.Duration; + +public class ClickHouseContainer extends JdbcDatabaseContainer { + public static final String NAME = "clickhouse"; + public static final String IMAGE = "yandex/clickhouse-server"; + public static final String DEFAULT_TAG = "18.10.3"; + + public static final Integer HTTP_PORT = 8123; + public static final Integer NATIVE_PORT = 9000; + + private static final String DRIVER_CLASS_NAME = "ru.yandex.clickhouse.ClickHouseDriver"; + private static final String JDBC_URL_PREFIX = "jdbc:" + NAME + "://"; + private static final String TEST_QUERY = "SELECT 1"; + + private String databaseName = "default"; + private String username = "default"; + private String password = ""; + + public ClickHouseContainer() { + super(IMAGE + ":" + DEFAULT_TAG); + } + + public ClickHouseContainer(String dockerImageName) { + super(dockerImageName); + } + + @Override + protected void configure() { + withExposedPorts(HTTP_PORT, NATIVE_PORT); + waitingFor( + new HttpWaitStrategy() + .forStatusCode(200) + .forResponsePredicate(responseBody -> "Ok.".equals(responseBody)) + .withStartupTimeout(Duration.ofMinutes(1)) + ); + } + + @Override + protected Integer getLivenessCheckPort() { + return getMappedPort(HTTP_PORT); + } + + @Override + public String getDriverClassName() { + return DRIVER_CLASS_NAME; + } + + @Override + public String getJdbcUrl() { + return JDBC_URL_PREFIX + getContainerIpAddress() + ":" + getMappedPort(HTTP_PORT) + "/" + databaseName; + } + + @Override + public String getUsername() { + return username; + } + + @Override + public String getPassword() { + return password; + } + + @Override + public String getTestQueryString() { + return TEST_QUERY; + } + +} diff --git a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseProvider.java b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseProvider.java new file mode 100644 index 00000000000..be5a01657f0 --- /dev/null +++ b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseProvider.java @@ -0,0 +1,13 @@ +package org.testcontainers.containers; + +public class ClickHouseProvider extends JdbcDatabaseContainerProvider { + @Override + public boolean supports(String databaseType) { + return databaseType.equals(ClickHouseContainer.NAME); + } + + @Override + public JdbcDatabaseContainer newInstance(String tag) { + return new ClickHouseContainer(ClickHouseContainer.IMAGE + ":" + tag); + } +} diff --git a/modules/clickhouse/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider b/modules/clickhouse/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider new file mode 100644 index 00000000000..63e99284d2b --- /dev/null +++ b/modules/clickhouse/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider @@ -0,0 +1 @@ +org.testcontainers.containers.ClickHouseProvider \ No newline at end of file diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 0223781eecb..e631d2fa1a3 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -10,6 +10,7 @@ dependencies { compile project(':mariadb') compile project(':oracle-xe') compile project(':mssqlserver') + compile project(':clickhouse') testCompile 'com.google.guava:guava:18.0' testCompile 'org.postgresql:postgresql:42.0.0' @@ -17,6 +18,7 @@ dependencies { testCompile 'org.mariadb.jdbc:mariadb-java-client:1.4.6' testCompile 'com.oracle:ojdbc6:12.1.0.1-atlassian-hosted' testCompile 'com.microsoft.sqlserver:mssql-jdbc:6.1.0.jre8' + testCompile 'ru.yandex.clickhouse:clickhouse-jdbc:0.1.41' testCompile 'com.zaxxer:HikariCP-java6:2.3.8' testCompile 'org.apache.tomcat:tomcat-jdbc:8.5.4' diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java index 762e2ab639a..5cd5f25ae3c 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java @@ -30,6 +30,7 @@ private enum Options { CharacterSet, CustomIniFile, JDBCParams, + PmdKnownBroken } @Parameter @@ -56,7 +57,9 @@ public static Iterable data() { {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITSCRIPT=somepath/init_unicode_mysql.sql&useUnicode=yes&characterEncoding=utf8", EnumSet.of(Options.CharacterSet)}, {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITSCRIPT=somepath/init_mariadb.sql", EnumSet.of(Options.ScriptedSchema)}, {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema)}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_MY_CNF=somepath/mariadb_conf_override", EnumSet.of(Options.CustomIniFile)}}); + {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_MY_CNF=somepath/mariadb_conf_override", EnumSet.of(Options.CustomIniFile)}, + {"jdbc:tc:clickhouse://hostname/databasename", EnumSet.of(Options.PmdKnownBroken)}, + }); } public static void sampleInitFunction(Connection connection) throws SQLException { @@ -102,7 +105,7 @@ public void test() throws SQLException { private void performSimpleTest(String jdbcUrl) throws SQLException { try (HikariDataSource dataSource = getDataSource(jdbcUrl, 1)) { - boolean result = new QueryRunner(dataSource).query("SELECT 1", rs -> { + boolean result = new QueryRunner(dataSource, options.contains(Options.PmdKnownBroken)).query("SELECT 1", rs -> { rs.next(); int resultSetInt = rs.getInt(1); assertEquals("A basic SELECT query succeeds", 1, resultSetInt); diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleClickhouseTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleClickhouseTest.java new file mode 100644 index 00000000000..4e123617b8d --- /dev/null +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleClickhouseTest.java @@ -0,0 +1,36 @@ +package org.testcontainers.junit; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import org.junit.Rule; +import org.junit.Test; +import org.testcontainers.containers.ClickHouseContainer; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; + +public class SimpleClickhouseTest { + + @Rule + public ClickHouseContainer clickhouse = new ClickHouseContainer(); + + @Test + public void testSimple() throws SQLException { + HikariConfig hikariConfig = new HikariConfig(); + hikariConfig.setJdbcUrl(clickhouse.getJdbcUrl()); + hikariConfig.setUsername(clickhouse.getUsername()); + hikariConfig.setPassword(clickhouse.getPassword()); + + HikariDataSource ds = new HikariDataSource(hikariConfig); + Statement statement = ds.getConnection().createStatement(); + statement.execute("SELECT 1"); + ResultSet resultSet = statement.getResultSet(); + + resultSet.next(); + int resultSetInt = resultSet.getInt(1); + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + } +} From f589e60798abc4c66479074bae8bac7fb6883b28 Mon Sep 17 00:00:00 2001 From: David Nault Date: Sun, 9 Sep 2018 01:43:26 -0700 Subject: [PATCH 0401/5615] Fix java.net.SocketException exception in CouchbaseContainer.stop() (#859) Motivation ========== When stop() was called without a prior call to createCouchbaseEnvironment(), it would throw a SocketException because stopCluster() would end up trying to initialize the cluster at the same time the container is being shut down. Modifications ============= Call stopCluster() before stopping the container, instead of in parallel. Result ====== stopCluster() might still call createCouchbaseEnvironment(), but the call succeeds and the environment is immediately cleaned up. --- .../org/testcontainers/couchbase/CouchbaseContainer.java | 3 ++- .../testcontainers/couchbase/CouchbaseContainerTest.java | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index a22375e8877..784eb246458 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -180,7 +180,8 @@ public List getBoundPortNumbers() { @Override public void stop() { - Stream.of(super::stop, proxy::stop, this::stopCluster).parallel().forEach(Runnable::run); + stopCluster(); + Stream.of(super::stop, proxy::stop).parallel().forEach(Runnable::run); } private void stopCluster() { diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java index 9e0372ec3c5..6e9afe96865 100644 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java @@ -12,4 +12,11 @@ public void shouldUseCorrectDockerImage() { Assert.assertEquals(CouchbaseContainer.DOCKER_IMAGE_NAME + CouchbaseContainer.VERSION, couchbaseContainer.getDockerImageName()); } + + @Test + public void shouldStopWithoutThrowingException() { + CouchbaseContainer couchbaseContainer = new CouchbaseContainer(); + couchbaseContainer.start(); + couchbaseContainer.stop(); + } } From d127fd799bccbb4ee4d006dc2edd0f56c0c908c2 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 9 Sep 2018 13:08:09 +0100 Subject: [PATCH 0402/5615] Add support for docker hub private registry credentials (#845) * Fix authentication for use of base64-encoded credentials and credential helpers with private registries and docker hub * Update following review by @bsideup * Update to docker-java 3.1.0-rc-4 * fix shading --- core/build.gradle | 3 +- .../testcontainers/JarFileShadingTest.java | 4 +- .../DockerClientProviderStrategy.java | 7 +- .../AuthDelegatingDockerClientConfig.java | 52 ++++++++ .../TestcontainersDockerCmdExecFactory.java | 7 +- .../okhttp/NamedPipeSocketFactory.java | 8 +- .../transport/okhttp/UnixSocketFactory.java | 2 + .../images/RemoteDockerImage.java | 6 - .../utility/AuthConfigUtil.java | 34 +++++ .../utility/DockerImageName.java | 2 +- .../org/testcontainers/utility/LogUtils.java | 4 + .../utility/RegistryAuthLocator.java | 108 +++++++++++----- .../utility/AuthenticatedImagePullTest.java | 118 ++++++++++++++++++ .../utility/RegistryAuthLocatorTest.java | 30 +++-- .../auth-config/config-basic-auth.json | 8 ++ .../config-existing-auth-with-helper.json | 2 +- 16 files changed, 332 insertions(+), 63 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/dockerclient/auth/AuthDelegatingDockerClientConfig.java create mode 100644 core/src/main/java/org/testcontainers/utility/AuthConfigUtil.java create mode 100644 core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java create mode 100644 core/src/test/resources/auth-config/config-basic-auth.json diff --git a/core/build.gradle b/core/build.gradle index abaa30bf318..fec6cd6ee71 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -35,6 +35,7 @@ shadowJar { 'META-INF/services/org.glassfish.hk2.extension.*', 'META-INF/services/org.jvnet.hk2.external.generator.*', 'META-INF/services/org.glassfish.jersey.internal.spi.*', + 'META-INF/services/java.security.Provider', 'mozilla/public-suffix-list.txt', ].each { exclude(it) } @@ -95,7 +96,7 @@ dependencies { exclude(group: "net.java.dev.jna") } - shaded ('com.github.docker-java:docker-java:3.1.0-rc-3') { + shaded ('com.github.docker-java:docker-java:3.1.0-rc-4') { exclude(group: 'org.glassfish.jersey.core') exclude(group: 'org.glassfish.jersey.connectors') exclude(group: 'log4j') diff --git a/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java b/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java index aa66116c080..ee950f4698c 100644 --- a/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java +++ b/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java @@ -42,8 +42,8 @@ public void testMetaInf() throws Exception { ); assertThatFileList(root.resolve("META-INF").resolve("native")).containsOnly( - "liborg-testcontainers-shaded-netty-transport-native-epoll.so", - "liborg-testcontainers-shaded-netty-transport-native-kqueue.jnilib" + "liborg-testcontainers-shaded-netty_transport_native_epoll_x86_64.so", + "liborg-testcontainers-shaded-netty_transport_native_kqueue_x86_64.jnilib" ); } diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index 117acece7db..186fd194131 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -12,6 +12,7 @@ import org.rnorth.ducttape.unreliables.Unreliables; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.testcontainers.dockerclient.auth.AuthDelegatingDockerClientConfig; import org.testcontainers.dockerclient.transport.TestcontainersDockerCmdExecFactory; import org.testcontainers.dockerclient.transport.okhttp.OkHttpDockerCmdExecFactory; import org.testcontainers.utility.TestcontainersConfiguration; @@ -39,6 +40,8 @@ public abstract class DockerClientProviderStrategy { private static final AtomicBoolean FAIL_FAST_ALWAYS = new AtomicBoolean(false); + protected static final Logger LOGGER = LoggerFactory.getLogger(DockerClientProviderStrategy.class); + /** * @throws InvalidConfigurationException if this strategy fails */ @@ -64,8 +67,6 @@ protected int getPriority() { return 0; } - protected static final Logger LOGGER = LoggerFactory.getLogger(DockerClientProviderStrategy.class); - /** * Determine the right DockerClientConfig to use for building clients by trial-and-error. * @@ -166,7 +167,7 @@ public DockerClient getClient() { protected DockerClient getClientForConfig(DockerClientConfig config) { DockerClientBuilder clientBuilder = DockerClientBuilder - .getInstance(config); + .getInstance(new AuthDelegatingDockerClientConfig(config)); String transportType = TestcontainersConfiguration.getInstance().getTransportType(); if ("okhttp".equals(transportType)) { diff --git a/core/src/main/java/org/testcontainers/dockerclient/auth/AuthDelegatingDockerClientConfig.java b/core/src/main/java/org/testcontainers/dockerclient/auth/AuthDelegatingDockerClientConfig.java new file mode 100644 index 00000000000..d75f3ce6bc1 --- /dev/null +++ b/core/src/main/java/org/testcontainers/dockerclient/auth/AuthDelegatingDockerClientConfig.java @@ -0,0 +1,52 @@ +package org.testcontainers.dockerclient.auth; + +import com.github.dockerjava.api.model.AuthConfig; +import com.github.dockerjava.core.DockerClientConfig; +import lombok.experimental.Delegate; +import lombok.extern.slf4j.Slf4j; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.RegistryAuthLocator; + +import static org.testcontainers.utility.AuthConfigUtil.toSafeString; + +/** + * Facade implementation for {@link DockerClientConfig} which overrides how authentication + * configuration is obtained. A delegate {@link DockerClientConfig} will be called first + * to try and obtain auth credentials, but after that {@link RegistryAuthLocator} will be + * used to try and improve the auth resolution (e.g. using credential helpers). + */ +@Slf4j +public class AuthDelegatingDockerClientConfig implements DockerClientConfig { + + @Delegate(excludes = DelegateExclusions.class) + private DockerClientConfig delegate; + + public AuthDelegatingDockerClientConfig(DockerClientConfig delegate) { + this.delegate = delegate; + } + + public AuthConfig effectiveAuthConfig(String imageName) { + // allow docker-java auth config to be used as a fallback + AuthConfig fallbackAuthConfig; + try { + fallbackAuthConfig = delegate.effectiveAuthConfig(imageName); + } catch (Exception e) { + log.debug("Delegate call to effectiveAuthConfig failed with cause: '{}'. " + + "Resolution of auth config will continue using RegistryAuthLocator.", + e.getMessage()); + fallbackAuthConfig = new AuthConfig(); + } + + // try and obtain more accurate auth config using our resolution + final DockerImageName parsed = new DockerImageName(imageName); + final AuthConfig effectiveAuthConfig = RegistryAuthLocator.instance() + .lookupAuthConfig(parsed, fallbackAuthConfig); + + log.debug("Effective auth config [{}]", toSafeString(effectiveAuthConfig)); + return effectiveAuthConfig; + } + + private interface DelegateExclusions { + AuthConfig effectiveAuthConfig(String imageName); + } +} diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java b/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java index 1aa9377ce2b..1ec43e2b251 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java @@ -80,16 +80,21 @@ public void init(DockerClientConfig dockerClientConfig) { bootstrap = new Bootstrap(); String scheme = dockerClientConfig.getDockerHost().getScheme(); + String host = ""; if ("unix".equals(scheme)) { nettyInitializer = new UnixDomainSocketInitializer(); + host = "DUMMY"; } else if ("tcp".equals(scheme)) { nettyInitializer = new InetSocketInitializer(); + host = dockerClientConfig.getDockerHost().getHost() + ":" + + Integer.toString(dockerClientConfig.getDockerHost().getPort()); } eventLoopGroup = nettyInitializer.init(bootstrap, dockerClientConfig); - baseResource = new NettyWebTarget(this::connect).path(dockerClientConfig.getApiVersion().asWebPathPart()); + baseResource = new NettyWebTarget(this::connect, host) + .path(dockerClientConfig.getApiVersion().asWebPathPart()); } private DuplexChannel connect() { diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/NamedPipeSocketFactory.java b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/NamedPipeSocketFactory.java index 0071dea036d..5811c605e21 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/NamedPipeSocketFactory.java +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/NamedPipeSocketFactory.java @@ -1,20 +1,18 @@ package org.testcontainers.dockerclient.transport.okhttp; +import lombok.EqualsAndHashCode; import lombok.SneakyThrows; import lombok.Value; import org.scalasbt.ipcsocket.Win32NamedPipeSocket; import javax.net.SocketFactory; -import java.io.FilterInputStream; -import java.io.FilterOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.io.*; import java.net.InetAddress; import java.net.Socket; import java.net.SocketAddress; @Value +@EqualsAndHashCode(callSuper = false) public class NamedPipeSocketFactory extends SocketFactory { String socketPath; diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/UnixSocketFactory.java b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/UnixSocketFactory.java index c23575c2307..a9eb9f30086 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/UnixSocketFactory.java +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/UnixSocketFactory.java @@ -1,5 +1,6 @@ package org.testcontainers.dockerclient.transport.okhttp; +import lombok.EqualsAndHashCode; import lombok.SneakyThrows; import lombok.Value; import org.scalasbt.ipcsocket.UnixDomainSocket; @@ -15,6 +16,7 @@ import java.net.SocketAddress; @Value +@EqualsAndHashCode(callSuper = false) public class UnixSocketFactory extends SocketFactory { String socketPath; diff --git a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java index bb0d0c969ce..9554793c004 100644 --- a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java +++ b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java @@ -3,7 +3,6 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.ListImagesCmd; import com.github.dockerjava.api.exception.DockerClientException; -import com.github.dockerjava.api.model.AuthConfig; import com.github.dockerjava.api.model.Image; import com.github.dockerjava.core.command.PullImageResultCallback; import lombok.NonNull; @@ -15,7 +14,6 @@ import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.DockerLoggerFactory; import org.testcontainers.utility.LazyFuture; -import org.testcontainers.utility.RegistryAuthLocator; import java.util.HashSet; import java.util.List; @@ -95,14 +93,10 @@ protected final String resolve() { // The image is not available locally - pull it try { - final RegistryAuthLocator authLocator = new RegistryAuthLocator(dockerClient.authConfig()); - final AuthConfig effectiveAuthConfig = authLocator.lookupAuthConfig(imageName); - final PullImageResultCallback callback = new PullImageResultCallback(); dockerClient .pullImageCmd(imageName.getUnversionedPart()) .withTag(imageName.getVersionPart()) - .withAuthConfig(effectiveAuthConfig) .exec(callback); callback.awaitCompletion(); AVAILABLE_IMAGE_NAME_CACHE.add(imageName); diff --git a/core/src/main/java/org/testcontainers/utility/AuthConfigUtil.java b/core/src/main/java/org/testcontainers/utility/AuthConfigUtil.java new file mode 100644 index 00000000000..77b670d374b --- /dev/null +++ b/core/src/main/java/org/testcontainers/utility/AuthConfigUtil.java @@ -0,0 +1,34 @@ +package org.testcontainers.utility; + +import com.github.dockerjava.api.model.AuthConfig; +import com.google.common.base.MoreObjects; +import lombok.experimental.UtilityClass; +import org.jetbrains.annotations.NotNull; + +import static com.google.common.base.Strings.isNullOrEmpty; + +/** + * TODO: Javadocs + */ +@UtilityClass +public class AuthConfigUtil { + public static String toSafeString(AuthConfig authConfig) { + if (authConfig == null) { + return "null"; + } + + return MoreObjects.toStringHelper(authConfig) + .add("username", authConfig.getUsername()) + .add("password", obfuscated(authConfig.getPassword())) + .add("auth", obfuscated(authConfig.getAuth())) + .add("email", authConfig.getEmail()) + .add("registryAddress", authConfig.getRegistryAddress()) + .add("registryToken", obfuscated(authConfig.getRegistrytoken())) + .toString(); + } + + @NotNull + private static String obfuscated(String value) { + return isNullOrEmpty(value) ? "blank" : "hidden non-blank value"; + } +} diff --git a/core/src/main/java/org/testcontainers/utility/DockerImageName.java b/core/src/main/java/org/testcontainers/utility/DockerImageName.java index 3f6d7124891..bd1c713e26a 100644 --- a/core/src/main/java/org/testcontainers/utility/DockerImageName.java +++ b/core/src/main/java/org/testcontainers/utility/DockerImageName.java @@ -7,7 +7,7 @@ import java.util.regex.Pattern; -@EqualsAndHashCode +@EqualsAndHashCode(exclude = "rawName") public final class DockerImageName { /* Regex patterns used for validation */ diff --git a/core/src/main/java/org/testcontainers/utility/LogUtils.java b/core/src/main/java/org/testcontainers/utility/LogUtils.java index 8a17be9f65d..100a79a8395 100644 --- a/core/src/main/java/org/testcontainers/utility/LogUtils.java +++ b/core/src/main/java/org/testcontainers/utility/LogUtils.java @@ -2,12 +2,15 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.LogContainerCmd; +import com.github.dockerjava.api.model.AuthConfig; +import com.google.common.base.MoreObjects; import lombok.experimental.UtilityClass; import org.testcontainers.containers.output.FrameConsumerResultCallback; import org.testcontainers.containers.output.OutputFrame; import java.util.function.Consumer; +import static com.google.common.base.Strings.isNullOrEmpty; import static org.testcontainers.containers.output.OutputFrame.OutputType.STDERR; import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; @@ -39,4 +42,5 @@ public void followOutput(DockerClient dockerClient, String containerId, public void followOutput(DockerClient dockerClient, String containerId, Consumer consumer) { followOutput(dockerClient, containerId, consumer, STDOUT, STDERR); } + } diff --git a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java index eb290cae115..615e8326b62 100644 --- a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java +++ b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java @@ -4,17 +4,21 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.github.dockerjava.api.model.AuthConfig; import com.google.common.annotations.VisibleForTesting; +import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.SystemUtils; import org.slf4j.Logger; import org.zeroturnaround.exec.ProcessExecutor; import java.io.ByteArrayInputStream; import java.io.File; +import java.util.Base64; import java.util.Iterator; import java.util.Map; import java.util.concurrent.TimeUnit; +import static org.apache.commons.lang.StringUtils.isBlank; import static org.slf4j.LoggerFactory.getLogger; +import static org.testcontainers.utility.AuthConfigUtil.toSafeString; /** * Utility to look up registry authentication information for an image. @@ -22,47 +26,58 @@ public class RegistryAuthLocator { private static final Logger log = getLogger(RegistryAuthLocator.class); - - private final AuthConfig defaultAuthConfig; - private final File configFile; - private final String commandPathPrefix; + private static final String DEFAULT_REGISTRY_NAME = "index.docker.io"; private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + private static RegistryAuthLocator instance; + private final String commandPathPrefix; + private final File configFile; @VisibleForTesting - RegistryAuthLocator(AuthConfig defaultAuthConfig, File configFile, String commandPathPrefix) { - this.defaultAuthConfig = defaultAuthConfig; + RegistryAuthLocator(File configFile, String commandPathPrefix) { this.configFile = configFile; this.commandPathPrefix = commandPathPrefix; } /** - * @param defaultAuthConfig an AuthConfig object that should be returned if there is no overriding authentication - * available for images that are looked up */ - public RegistryAuthLocator(AuthConfig defaultAuthConfig) { - this.defaultAuthConfig = defaultAuthConfig; + protected RegistryAuthLocator() { final String dockerConfigLocation = System.getenv().getOrDefault("DOCKER_CONFIG", System.getProperty("user.home") + "/.docker"); this.configFile = new File(dockerConfigLocation + "/config.json"); this.commandPathPrefix = ""; } + public synchronized static RegistryAuthLocator instance() { + if (instance == null) { + instance = new RegistryAuthLocator(); + } + + return instance; + } + + @VisibleForTesting + static void setInstance(RegistryAuthLocator overrideInstance) { + instance = overrideInstance; + } + /** * Looks up an AuthConfig for a given image name. *

- * Lookup is performed in following order: + * Lookup is performed in following order, as per + * https://docs.docker.com/engine/reference/commandline/cli/: *

    - *
  1. {@code auths} is checked for existing credentials for the specified registry.
  2. - *
  3. if no existing auth is found, {@code credHelpers} are checked for helper for the specified registry.
  4. - *
  5. if no suitable {@code credHelpers} found, {@code credsStore} is used.
  6. - *
  7. if no {@code credsStore} is found then the default configuration is returned.
  8. + *
  9. {@code credHelpers}
  10. + *
  11. {@code credsStore}
  12. + *
  13. Hard-coded Base64 encoded auth in {@code auths}
  14. + *
  15. otherwise, if no credentials have been found then behaviour falls back to docker-java's + * implementation
  16. *
* * @param dockerImageName image name to be looked up (potentially including a registry URL part) - * @return an AuthConfig that is applicable to this specific image OR the defaultAuthConfig that has been set for - * this {@link RegistryAuthLocator}. + * @param defaultAuthConfig an AuthConfig object that should be returned if there is no overriding authentication available for images that are looked up + * @return an AuthConfig that is applicable to this specific image OR the defaultAuthConfig. */ - public AuthConfig lookupAuthConfig(DockerImageName dockerImageName) { + public AuthConfig lookupAuthConfig(DockerImageName dockerImageName, AuthConfig defaultAuthConfig) { if (SystemUtils.IS_OS_WINDOWS) { log.debug("RegistryAuthLocator is not supported on Windows. Please help test or improve it and update " + @@ -79,39 +94,62 @@ public AuthConfig lookupAuthConfig(DockerImageName dockerImageName) { try { final JsonNode config = OBJECT_MAPPER.readTree(configFile); - final String reposName = dockerImageName.getRegistry(); + final String registryName = effectiveRegistryName(dockerImageName); + log.debug("registryName [{}] for dockerImageName [{}]", registryName, dockerImageName); - final AuthConfig existingAuthConfig = findExistingAuthConfig(config, reposName); - if (existingAuthConfig != null) { - return existingAuthConfig; - } - // auths is empty, using helper: - final AuthConfig helperAuthConfig = authConfigUsingHelper(config, reposName); + // use helper preferentially (per https://docs.docker.com/engine/reference/commandline/cli/) + final AuthConfig helperAuthConfig = authConfigUsingHelper(config, registryName); if (helperAuthConfig != null) { + log.debug("found helper auth config [{}]", toSafeString(helperAuthConfig)); return helperAuthConfig; } // no credsHelper to use, using credsStore: - final AuthConfig storeAuthConfig = authConfigUsingStore(config, reposName); + final AuthConfig storeAuthConfig = authConfigUsingStore(config, registryName); if (storeAuthConfig != null) { + log.debug("found creds store auth config [{}]", toSafeString(storeAuthConfig)); return storeAuthConfig; } + // fall back to base64 encoded auth hardcoded in config file + final AuthConfig existingAuthConfig = findExistingAuthConfig(config, registryName); + if (existingAuthConfig != null) { + log.debug("found existing auth config [{}]", toSafeString(existingAuthConfig)); + return existingAuthConfig; + } + + log.debug("no matching Auth Configs - falling back to defaultAuthConfig [{}]", toSafeString(defaultAuthConfig)); // otherwise, defaultAuthConfig should already contain any credentials available } catch (Exception e) { - log.error("Failure when attempting to lookup auth config (dockerImageName: {}, configFile: {}. " + - "Falling back to docker-java default behaviour", + log.debug("Failure when attempting to lookup auth config (dockerImageName: {}, configFile: {}. Falling back to docker-java default behaviour. Exception message: {}", dockerImageName, configFile, - e); + e.getMessage()); } return defaultAuthConfig; } private AuthConfig findExistingAuthConfig(final JsonNode config, final String reposName) throws Exception { + final Map.Entry entry = findAuthNode(config, reposName); + if (entry != null && entry.getValue() != null && entry.getValue().size() > 0) { - return OBJECT_MAPPER + final AuthConfig deserializedAuth = OBJECT_MAPPER .treeToValue(entry.getValue(), AuthConfig.class) .withRegistryAddress(entry.getKey()); + + if (isBlank(deserializedAuth.getUsername()) && + isBlank(deserializedAuth.getPassword()) && + !isBlank(deserializedAuth.getAuth())) { + + final String rawAuth = new String(Base64.getDecoder().decode(deserializedAuth.getAuth())); + final String[] splitRawAuth = rawAuth.split(":"); + + if (splitRawAuth.length == 2) { + deserializedAuth.withUsername(splitRawAuth[0]); + deserializedAuth.withPassword(splitRawAuth[1]); + } + } + + return deserializedAuth; } return null; } @@ -137,13 +175,13 @@ private AuthConfig authConfigUsingStore(final JsonNode config, final String repo return null; } - private Map.Entry findAuthNode(final JsonNode config, final String reposName) throws Exception { + private Map.Entry findAuthNode(final JsonNode config, final String reposName) { final JsonNode auths = config.get("auths"); if (auths != null && auths.size() > 0) { final Iterator> fields = auths.fields(); while (fields.hasNext()) { final Map.Entry entry = fields.next(); - if (entry.getKey().endsWith("://" + reposName)) { + if (entry.getKey().contains("://" + reposName) || entry.getKey().equals(reposName)) { return entry; } } @@ -169,7 +207,7 @@ private AuthConfig runCredentialProvider(String hostName, String credHelper) thr .outputUTF8() .trim(); } catch (Exception e) { - log.error("Failure running docker credential helper ({})", credentialHelperName); + log.debug("Failure running docker credential helper ({})", credentialHelperName); throw e; } @@ -181,4 +219,8 @@ private AuthConfig runCredentialProvider(String hostName, String credHelper) thr .withUsername(helperResponse.at("/Username").asText()) .withPassword(helperResponse.at("/Secret").asText()); } + + private String effectiveRegistryName(DockerImageName dockerImageName) { + return StringUtils.defaultIfEmpty(dockerImageName.getRegistry(), DEFAULT_REGISTRY_NAME); + } } diff --git a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java new file mode 100644 index 00000000000..19b933d174c --- /dev/null +++ b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java @@ -0,0 +1,118 @@ +package org.testcontainers.utility; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.model.AuthConfig; +import com.github.dockerjava.core.command.PullImageResultCallback; +import com.github.dockerjava.core.command.PushImageResultCallback; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.mockito.Mockito; +import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; +import org.testcontainers.images.builder.ImageFromDockerfile; + +import java.util.concurrent.TimeUnit; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.when; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; + +/** + * This test checks the integration between Testcontainers and an authenticated registry, but uses + * a mock instance of {@link RegistryAuthLocator} - the purpose of the test is solely to ensure that + * the auth locator is utilised, and that the credentials it provides flow through to the registry. + * + * {@link RegistryAuthLocatorTest} covers actual credential scenarios at a lower level, which are + * impractical to test end-to-end. + */ +public class AuthenticatedImagePullTest { + + @ClassRule + public static GenericContainer authenticatedRegistry = new GenericContainer(new ImageFromDockerfile() + .withDockerfileFromBuilder(builder -> { + builder.from("registry:2") + .run("htpasswd -Bbn testuser notasecret > /htpasswd") + .env("REGISTRY_AUTH", "htpasswd") + .env("REGISTRY_AUTH_HTPASSWD_PATH", "/htpasswd") + .env("REGISTRY_AUTH_HTPASSWD_REALM", "Test"); + })) + .withExposedPorts(5000) + .waitingFor(new HttpWaitStrategy()); + + private static RegistryAuthLocator originalAuthLocatorSingleton; + private static DockerClient client; + + private static String testRegistryAddress; + private static String testImageName; + private static String testImageNameWithTag; + + @BeforeClass + public static void setUp() { + originalAuthLocatorSingleton = RegistryAuthLocator.instance(); + client = DockerClientFactory.instance().client(); + + testRegistryAddress = authenticatedRegistry.getContainerIpAddress() + ":" + authenticatedRegistry.getFirstMappedPort(); + testImageName = testRegistryAddress + "/alpine"; + testImageNameWithTag = testImageName + ":latest"; + } + + @AfterClass + public static void tearDown() { + RegistryAuthLocator.setInstance(originalAuthLocatorSingleton); + client.removeImageCmd(testImageNameWithTag).withForce(true).exec(); + } + + @Test + public void testThatAuthLocatorIsUsed() throws Exception { + + final DockerImageName expectedName = new DockerImageName(testImageNameWithTag); + final AuthConfig authConfig = new AuthConfig() + .withUsername("testuser") + .withPassword("notasecret") + .withRegistryAddress("http://" + testRegistryAddress); + + // Replace the RegistryAuthLocator singleton with our mock, for the duration of this test + final RegistryAuthLocator mockAuthLocator = Mockito.mock(RegistryAuthLocator.class); + RegistryAuthLocator.setInstance(mockAuthLocator); + when(mockAuthLocator.lookupAuthConfig(eq(expectedName), any())) + .thenReturn(authConfig); + + // a push will use the auth locator for authentication, although that isn't the goal of this test + putImageInRegistry(); + + // actually start a container, which will require an authenticated pull + try (final GenericContainer container = new GenericContainer<>(testImageNameWithTag) + .withCommand("/bin/sh", "-c", "sleep 10")) { + container.start(); + + assertTrue("container started following an authenticated pull", container.isRunning()); + } + } + + private void putImageInRegistry() throws InterruptedException { + // It doesn't matter which image we use for this test, but use one that's likely to have been pulled already + final String dummySourceImage = TestcontainersConfiguration.getInstance().getRyukImage(); + + client.pullImageCmd(dummySourceImage) + .exec(new PullImageResultCallback()) + .awaitCompletion(1, TimeUnit.MINUTES); + + final String id = client.inspectImageCmd(dummySourceImage) + .exec() + .getId(); + + // push the image to the registry + client.tagImageCmd(id, testImageName, "latest").exec(); + + client.pushImageCmd(testImageNameWithTag) + .exec(new PushImageResultCallback()) + .awaitCompletion(1, TimeUnit.MINUTES); + + // remove the image tag from local docker so that it must be pulled before use + client.removeImageCmd(testImageNameWithTag).withForce(true).exec(); + } +} diff --git a/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java b/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java index 7a79f6bd40c..5d1611ae77e 100644 --- a/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java +++ b/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java @@ -25,18 +25,29 @@ public static void nonWindowsTest() throws Exception { public void lookupAuthConfigWithoutCredentials() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-empty.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("unauthenticated.registry.org/org/repo")); + final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("unauthenticated.registry.org/org/repo"), new AuthConfig()); assertEquals("Default docker registry URL is set on auth config", "https://index.docker.io/v1/", authConfig.getRegistryAddress()); assertNull("No username is set", authConfig.getUsername()); assertNull("No password is set", authConfig.getPassword()); } + @Test + public void lookupAuthConfigWithBasicAuthCredentials() throws URISyntaxException { + final RegistryAuthLocator authLocator = createTestAuthLocator("config-basic-auth.json"); + + final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("registry.example.com/org/repo"), new AuthConfig()); + + assertEquals("Default docker registry URL is set on auth config", "https://registry.example.com", authConfig.getRegistryAddress()); + assertEquals("Username is set", "user", authConfig.getUsername()); + assertEquals("Password is set", "pass", authConfig.getPassword()); + } + @Test public void lookupAuthConfigUsingStore() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-with-store.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("registry.example.com/org/repo")); + final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("registry.example.com/org/repo"), new AuthConfig()); assertEquals("Correct server URL is obtained from a credential store", "url", authConfig.getRegistryAddress()); assertEquals("Correct username is obtained from a credential store", "username", authConfig.getUsername()); @@ -47,7 +58,7 @@ public void lookupAuthConfigUsingStore() throws URISyntaxException { public void lookupAuthConfigUsingHelper() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-with-helper.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("registry.example.com/org/repo")); + final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("registry.example.com/org/repo"), new AuthConfig()); assertEquals("Correct server URL is obtained from a credential store", "url", authConfig.getRegistryAddress()); assertEquals("Correct username is obtained from a credential store", "username", authConfig.getUsername()); @@ -58,7 +69,7 @@ public void lookupAuthConfigUsingHelper() throws URISyntaxException { public void lookupUsingHelperEmptyAuth() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-empty-auth-with-helper.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("registry.example.com/org/repo")); + final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("registry.example.com/org/repo"), new AuthConfig()); assertEquals("Correct server URL is obtained from a credential store", "url", authConfig.getRegistryAddress()); assertEquals("Correct username is obtained from a credential store", "username", authConfig.getUsername()); @@ -69,18 +80,17 @@ public void lookupUsingHelperEmptyAuth() throws URISyntaxException { public void lookupNonEmptyAuthWithHelper() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-existing-auth-with-helper.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("registry.example.com/org/repo")); + final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("registry.example.com/org/repo"), new AuthConfig()); - assertEquals("Correct server URL is obtained from a credential store", "https://registry.example.com", authConfig.getRegistryAddress()); - assertNull("No username is set", authConfig.getUsername()); - assertEquals("Correct email is obtained from a credential store", "not@val.id", authConfig.getEmail()); - assertEquals("Correct auth is obtained from a credential store", "encoded auth token", authConfig.getAuth()); + assertEquals("Correct server URL is obtained from a credential helper", "url", authConfig.getRegistryAddress()); + assertEquals("Correct username is obtained from a credential helper", "username", authConfig.getUsername()); + assertEquals("Correct password is obtained from a credential helper", "secret", authConfig.getPassword()); } @NotNull private RegistryAuthLocator createTestAuthLocator(String configName) throws URISyntaxException { final File configFile = new File(Resources.getResource("auth-config/" + configName).toURI()); - return new RegistryAuthLocator(new AuthConfig(), configFile, configFile.getParentFile().getAbsolutePath() + "/"); + return new RegistryAuthLocator(configFile, configFile.getParentFile().getAbsolutePath() + "/"); } } diff --git a/core/src/test/resources/auth-config/config-basic-auth.json b/core/src/test/resources/auth-config/config-basic-auth.json new file mode 100644 index 00000000000..4a2c6983058 --- /dev/null +++ b/core/src/test/resources/auth-config/config-basic-auth.json @@ -0,0 +1,8 @@ +{ + "auths": { + "https://registry.example.com": { + "email": "user@example.com", + "auth": "dXNlcjpwYXNz" + } + } +} diff --git a/core/src/test/resources/auth-config/config-existing-auth-with-helper.json b/core/src/test/resources/auth-config/config-existing-auth-with-helper.json index 2c877b88f90..7e0fd003342 100644 --- a/core/src/test/resources/auth-config/config-existing-auth-with-helper.json +++ b/core/src/test/resources/auth-config/config-existing-auth-with-helper.json @@ -2,7 +2,7 @@ "auths": { "https://registry.example.com": { "email": "not@val.id", - "auth": "encoded auth token" + "auth": "dXNlcjpwYXNz" } }, "HttpHeaders": { From 31bde6ac594bfc47f56f874bf16af7ffdaa825af Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Wed, 12 Sep 2018 11:40:47 +0200 Subject: [PATCH 0403/5615] Fix local docker-compose support on Windows10 (#863) --- .../containers/DockerComposeContainer.java | 22 +++++++------------ .../junit/DockerComposeLocalOverrideTest.java | 0 2 files changed, 8 insertions(+), 14 deletions(-) delete mode 100644 core/src/test/java/org/testcontainers/junit/DockerComposeLocalOverrideTest.java diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 272b4bc17f7..72997053100 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -33,18 +33,12 @@ import java.nio.file.Paths; import java.time.Duration; import java.util.AbstractMap.SimpleEntry; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; +import java.util.stream.Stream; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; @@ -52,7 +46,6 @@ import static java.util.stream.Collectors.toList; import static org.testcontainers.containers.BindMode.READ_ONLY; import static org.testcontainers.containers.BindMode.READ_WRITE; -import static org.testcontainers.containers.ContainerisedDockerCompose.UNIX_PATH_SEPERATOR; /** * Container which launches Docker Compose, for the purposes of launching a defined set of containers. @@ -630,13 +623,14 @@ public void invoke() { environment.put(ENV_PROJECT_NAME, identifier); - final List absoluteDockerComposeFiles = composeFiles.stream() + final Stream absoluteDockerComposeFilePaths = composeFiles.stream() .map(File::getAbsolutePath) - .map(MountableFile::forHostPath) - .map(MountableFile::getFilesystemPath) - .collect(toList()); - final String composeFileEnvVariableValue = absoluteDockerComposeFiles.stream().collect(joining(UNIX_PATH_SEPERATOR + "")); // we always need the UNIX path separator + .map(Objects::toString); + + final String composeFileEnvVariableValue = absoluteDockerComposeFilePaths.collect( + joining(File.pathSeparator + "")); logger().debug("Set env COMPOSE_FILE={}", composeFileEnvVariableValue); + final File pwd = composeFiles.get(0).getAbsoluteFile().getParentFile().getAbsoluteFile(); environment.put(ENV_COMPOSE_FILE, composeFileEnvVariableValue); diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeLocalOverrideTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeLocalOverrideTest.java deleted file mode 100644 index e69de29bb2d..00000000000 From 3eb73c5fc9b9de9d3d0a87dd014d570df07fc201 Mon Sep 17 00:00:00 2001 From: Fabian Lange Date: Wed, 12 Sep 2018 19:18:43 +0200 Subject: [PATCH 0404/5615] Added Instana as users (#866) --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index 07b07ecf33a..3a7c8a716f5 100644 --- a/docs/index.md +++ b/docs/index.md @@ -29,6 +29,7 @@ Testcontainers makes it easy to launch useful Docker containers for the duration * [Streamlio](https://streaml.io/) - Integration and Chaos Testing of our fast data platform based on Apache Puslar, Apache Bookeeper and Apache Heron. * [Spring Session](https://projects.spring.io/spring-session/) - Redis, PostgreSQL, MySQL and MariaDB integration testing * [Apache Camel](https://camel.apache.org) - Testing Camel against native services such as Consul, Etcd and so on + * [Instana](https://www.instana.com) - Testing agents and stream processing backends ## License From 1d0140aba9956f6e53cbfdcb50c4f2537a9fe64d Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Thu, 13 Sep 2018 09:27:55 +0200 Subject: [PATCH 0405/5615] Add support for Postgis to JDBC driver (#864) --- docs/usage/database_containers.md | 3 +++ .../testcontainers/jdbc/JDBCDriverTest.java | 2 ++ .../containers/PostgisContainerProvider.java | 26 +++++++++++++++++++ ...s.containers.JdbcDatabaseContainerProvider | 3 ++- 4 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java diff --git a/docs/usage/database_containers.md b/docs/usage/database_containers.md index 95e85bad3de..f0479280452 100644 --- a/docs/usage/database_containers.md +++ b/docs/usage/database_containers.md @@ -73,6 +73,9 @@ Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database `jdbc:tc:postgresql:9.6.8://hostname/databasename` +### Using PostGIS + +`jdbc:tc:postgis:9.6://hostname/databasename` ## Using an init script diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java index 5cd5f25ae3c..9ff882b6a68 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java @@ -51,6 +51,8 @@ public static Iterable data() { {"jdbc:tc:mysql:5.5.43://hostname/databasename", EnumSet.noneOf(Options.class)}, {"jdbc:tc:mysql:5.5.43://hostname/databasename?useSSL=false", EnumSet.noneOf(Options.class)}, {"jdbc:tc:postgresql:9.6.8://hostname/databasename", EnumSet.noneOf(Options.class)}, + {"jdbc:tc:postgis://hostname/databasename", EnumSet.noneOf(Options.class)}, + {"jdbc:tc:postgis:9.6://hostname/databasename", EnumSet.noneOf(Options.class)}, {"jdbc:tc:mysql:5.6://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override", EnumSet.of(Options.CustomIniFile)}, {"jdbc:tc:mariadb://hostname/databasename", EnumSet.noneOf(Options.class)}, {"jdbc:tc:mariadb:10.2.14://hostname/databasename", EnumSet.noneOf(Options.class)}, diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java new file mode 100644 index 00000000000..644a5ef8c84 --- /dev/null +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java @@ -0,0 +1,26 @@ +package org.testcontainers.containers; + +/** + * Factory for PostGIS containers, which are a special flavour of PostgreSQL. + */ +public class PostgisContainerProvider extends JdbcDatabaseContainerProvider { + + private static final String NAME = "postgis"; + private static final String DEFAULT_TAG = "10"; + private static final String DEFAULT_IMAGE = "mdillon/postgis"; + + @Override + public boolean supports(String databaseType) { + return databaseType.equals(NAME); + } + + @Override + public JdbcDatabaseContainer newInstance() { + return newInstance(DEFAULT_TAG); + } + + @Override + public JdbcDatabaseContainer newInstance(String tag) { + return new PostgreSQLContainer(DEFAULT_IMAGE + ":" + tag); + } +} diff --git a/modules/postgresql/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider b/modules/postgresql/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider index 4b07faac03e..79b2f29340a 100644 --- a/modules/postgresql/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider +++ b/modules/postgresql/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider @@ -1 +1,2 @@ -org.testcontainers.containers.PostgreSQLContainerProvider \ No newline at end of file +org.testcontainers.containers.PostgreSQLContainerProvider +org.testcontainers.containers.PostgisContainerProvider From 66e89ec1107efb77ddc5df9839cff71c69700427 Mon Sep 17 00:00:00 2001 From: Ali Ahmed Date: Thu, 13 Sep 2018 01:05:27 -0700 Subject: [PATCH 0406/5615] Update Pulsar Container (#858) --- .../containers/PulsarContainer.java | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java index 59ca04eab9d..1d942727550 100644 --- a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java +++ b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java @@ -4,30 +4,33 @@ import org.testcontainers.utility.TestcontainersConfiguration; /** - * This container wraps Apache pulsar running in stanalone mode + * This container wraps Apache Pulsar running in standalone mode */ public class PulsarContainer extends GenericContainer { - public static final int PULSAR_PORT = 6850; + public static final int BROKER_PORT = 6650; + public static final int BROKER_HTTP_PORT = 8080; + public static final String METRICS_ENDPOINT = "/metrics"; + + private static final String PULSAR_VERSION = "2.1.0-incubating"; public PulsarContainer() { - this("2.1.0-incubating"); + this(PULSAR_VERSION); } public PulsarContainer(String pulsarVersion) { super(TestcontainersConfiguration.getInstance().getPulsarImage() + ":" + pulsarVersion); - withExposedPorts(PULSAR_PORT); - withCommand("/bin/bash", "-c", "" + - "servicePort=6850 webServicePort=8280 webServicePortTls=8643 bin/apply-config-from-env.py conf/proxy.conf && " + - "bin/pulsar standalone & " + - "bin/pulsar proxy --zookeeper-servers localhost:2181 --global-zookeeper-servers localhost:2181" - ); - - waitingFor(Wait.forLogMessage(".*messaging service is ready.*\\s", 1)); + withExposedPorts(BROKER_PORT, BROKER_HTTP_PORT); + withCommand("/pulsar/bin/pulsar", "standalone", "--no-functions-worker", "-nss"); + waitingFor(Wait.forHttp(METRICS_ENDPOINT).forStatusCode(200).forPort(BROKER_HTTP_PORT)); } public String getPulsarBrokerUrl() { - return String.format("pulsar://%s:%s", this.getContainerIpAddress(), this.getFirstMappedPort()); + return String.format("pulsar://%s:%s", getContainerIpAddress(), getMappedPort(BROKER_PORT)); } + public String getHttpServiceUrl() { + return String.format("http://%s:%s", getContainerIpAddress(), getMappedPort(BROKER_HTTP_PORT)); + } } + From 52d7126c19cbde76937dcabe1b589662fd15f9a5 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 13 Sep 2018 16:05:00 +0200 Subject: [PATCH 0407/5615] Mark Windows npipe support as stable (with a fix) (#865) * attempt to fix Npipe protocol by setting `WinNT.FILE_FLAG_OVERLAPPED` * try gesellix' npipe * fix NPE * yet another fix * try MariaDB's npipe implementation * use MariaDB's implementation from their java client * Mark Windows npipe support as stable (with a fix) * Fix faulty reconstructBreakedUnicode() test * Add custom NamedPipeSocketFactory * Remove mariadb-client dependency * Fix AbstractJarFileTest to work on Windows * cleanup * explain the workaround in OkHttpInvocationBuilder * Update docs * Update docs --- build.gradle | 1 + .../testcontainers/AbstractJarFileTest.java | 5 +- .../NpipeSocketClientProviderStrategy.java | 3 +- .../okhttp/NamedPipeSocketFactory.java | 78 +++++++++++++++---- .../okhttp/OkHttpInvocationBuilder.java | 11 ++- .../utility/TestcontainersConfiguration.java | 4 - .../FrameConsumerResultCallbackTest.java | 11 +-- docs/usage/windows_support.md | 4 +- 8 files changed, 85 insertions(+), 32 deletions(-) diff --git a/build.gradle b/build.gradle index 2e09985de17..c26914002f0 100644 --- a/build.gradle +++ b/build.gradle @@ -38,6 +38,7 @@ subprojects { task release(dependsOn: bintrayUpload) test { + defaultCharacterEncoding = "UTF-8" testLogging { displayGranularity 1 showStackTraces = true diff --git a/core/src/jarFileTest/java/org/testcontainers/AbstractJarFileTest.java b/core/src/jarFileTest/java/org/testcontainers/AbstractJarFileTest.java index f3960e63bdb..d6043422640 100644 --- a/core/src/jarFileTest/java/org/testcontainers/AbstractJarFileTest.java +++ b/core/src/jarFileTest/java/org/testcontainers/AbstractJarFileTest.java @@ -4,6 +4,7 @@ import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Path; +import java.nio.file.Paths; import static java.util.Collections.emptyMap; @@ -13,7 +14,9 @@ public abstract class AbstractJarFileTest { static { try { - FileSystem fileSystem = FileSystems.newFileSystem(URI.create("jar:file://" + System.getProperty("jarFile")), emptyMap()); + Path jarFilePath = Paths.get(System.getProperty("jarFile")); + URI jarFileUri = new URI("jar", jarFilePath.toUri().toString(), null); + FileSystem fileSystem = FileSystems.newFileSystem(jarFileUri, emptyMap()); root = fileSystem.getPath("/"); } catch (Exception e) { throw new RuntimeException(e); diff --git a/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java index ae9b888f117..b8d85f11623 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java @@ -7,7 +7,6 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.SystemUtils; import org.jetbrains.annotations.NotNull; -import org.testcontainers.utility.TestcontainersConfiguration; import java.net.URI; @@ -24,7 +23,7 @@ public class NpipeSocketClientProviderStrategy extends DockerClientProviderStrat @Override protected boolean isApplicable() { - return SystemUtils.IS_OS_WINDOWS && TestcontainersConfiguration.getInstance().isNpipeForced(); + return SystemUtils.IS_OS_WINDOWS; } @Override diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/NamedPipeSocketFactory.java b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/NamedPipeSocketFactory.java index 5811c605e21..ce272d6e1a6 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/NamedPipeSocketFactory.java +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/NamedPipeSocketFactory.java @@ -1,57 +1,101 @@ package org.testcontainers.dockerclient.transport.okhttp; +import com.sun.jna.platform.win32.Kernel32; import lombok.EqualsAndHashCode; import lombok.SneakyThrows; import lombok.Value; -import org.scalasbt.ipcsocket.Win32NamedPipeSocket; +import org.rnorth.ducttape.unreliables.Unreliables; import javax.net.SocketFactory; import java.io.*; import java.net.InetAddress; import java.net.Socket; import java.net.SocketAddress; +import java.util.concurrent.TimeUnit; @Value @EqualsAndHashCode(callSuper = false) public class NamedPipeSocketFactory extends SocketFactory { - String socketPath; + String socketFileName; @Override @SneakyThrows public Socket createSocket() { - return new Win32NamedPipeSocket(socketPath.replace("/", "\\")) { + return new Socket() { + + RandomAccessFile file; + InputStream is; + OutputStream os; @Override - public void connect(SocketAddress endpoint, int timeout) throws IOException { - // Do nothing since it's not "connectable" + public void close() throws IOException { + if (file != null) { + file.close(); + file = null; + } } @Override - public InputStream getInputStream() { - return new FilterInputStream(super.getInputStream()) { + public void connect(SocketAddress endpoint) { + connect(endpoint, 0); + } + + @Override + public void connect(SocketAddress endpoint, int timeout) { + file = Unreliables.retryUntilSuccess(Math.max(timeout, 10_000), TimeUnit.MILLISECONDS, () -> { + try { + return new RandomAccessFile(socketFileName, "rw"); + } catch (FileNotFoundException e) { + Kernel32.INSTANCE.WaitNamedPipe(socketFileName, 100); + throw e; + } + }); + + is = new InputStream() { + @Override + public int read(byte[] bytes, int off, int len) throws IOException { + return file.read(bytes, off, len); + } + + @Override + public int read() throws IOException { + return file.read(); + } + @Override - public void close() throws IOException { - shutdownInput(); + public int read(byte[] bytes) throws IOException { + return file.read(bytes); } }; - } - @Override - public OutputStream getOutputStream() { - return new FilterOutputStream(super.getOutputStream()) { + os = new OutputStream() { + @Override + public void write(byte[] bytes, int off, int len) throws IOException { + file.write(bytes, off, len); + } @Override - public void write(byte[] b, int off, int len) throws IOException { - out.write(b, off, len); + public void write(int value) throws IOException { + file.write(value); } @Override - public void close() throws IOException { - shutdownOutput(); + public void write(byte[] bytes) throws IOException { + file.write(bytes); } }; } + + @Override + public InputStream getInputStream() { + return is; + } + + @Override + public OutputStream getOutputStream() { + return os; + } }; } diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java index 6ded6d893b4..bc84e44a323 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java @@ -311,7 +311,16 @@ public void run() { while (!(shouldStop.get() || source.exhausted())) { int bytesReceived = inputStream.read(buffer); - handler.channelRead(null, Unpooled.wrappedBuffer(buffer, 0, bytesReceived)); + int offset = 0; + for (int i = 0; i < bytesReceived; i++) { + // some handlers like JsonResponseCallbackHandler do not work with multi-line buffers + boolean isLineBreak = buffer[i] == '\n'; + boolean isEndOfBuffer = i == bytesReceived - 1; + if (isLineBreak || isEndOfBuffer) { + handler.channelRead(null, Unpooled.wrappedBuffer(buffer, offset, i - offset + 1)); + offset = i + 1; + } + } } callback.onComplete(); } catch (Exception e) { diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 69b5ede2d41..7a7811590ce 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -78,10 +78,6 @@ public String getTransportType() { return properties.getProperty("transport.type", "okhttp"); } - public boolean isNpipeForced() { - return Boolean.parseBoolean(properties.getProperty("transport.npipe.forced", "false")); - } - @Synchronized public boolean updateGlobalConfig(@NonNull String prop, @NonNull String value) { try { diff --git a/core/src/test/java/org/testcontainers/containers/output/FrameConsumerResultCallbackTest.java b/core/src/test/java/org/testcontainers/containers/output/FrameConsumerResultCallbackTest.java index 88c6e22d6c9..325cccdd378 100644 --- a/core/src/test/java/org/testcontainers/containers/output/FrameConsumerResultCallbackTest.java +++ b/core/src/test/java/org/testcontainers/containers/output/FrameConsumerResultCallbackTest.java @@ -5,6 +5,7 @@ import org.junit.Test; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.Consumer; @@ -142,11 +143,11 @@ public void passRawFrameWithColors() throws TimeoutException, IOException { @Test public void reconstructBreakedUnicode() throws IOException { String payload = "Тест"; - byte[] payloadBytes = payload.getBytes(); - byte[] bytes1 = new byte[5]; - byte[] bytes2 = new byte[3]; - System.arraycopy(payloadBytes, 0, bytes1, 0, 5); - System.arraycopy(payloadBytes, 5, bytes2, 0, 3); + byte[] payloadBytes = payload.getBytes(StandardCharsets.UTF_8); + byte[] bytes1 = new byte[(int) (payloadBytes.length * 0.6)]; + byte[] bytes2 = new byte[payloadBytes.length - bytes1.length]; + System.arraycopy(payloadBytes, 0, bytes1, 0, bytes1.length); + System.arraycopy(payloadBytes, bytes1.length, bytes2, 0, bytes2.length); FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); callback.addConsumer(OutputType.STDOUT, consumer); diff --git a/docs/usage/windows_support.md b/docs/usage/windows_support.md index a2d889da31c..cd740f3776e 100644 --- a/docs/usage/windows_support.md +++ b/docs/usage/windows_support.md @@ -3,7 +3,7 @@ ## Prerequisites * [Docker for Windows](https://docs.docker.com/docker-for-windows/) needs to be installed * Docker version 17.06 is confirmed to work on Windows 10 with Hyper-V. - * On the General page in Settings, "Expose daemon on tcp://localhost:2375 without TLS" must be checked. + * Testcontainers supports communication with Docker on Docker for Windows using named pipes. ## Limitations The following features are not available or do not work correctly so make sure you do not use them or use them with @@ -11,7 +11,7 @@ caution. The list may not be complete. ### Testing -Testcontainers is not regularly tested on Windows, so please consider it to be at an alpha level of readiness. +Testcontainers is not regularly tested on Windows, so please consider it to be at an beta level of readiness. If you wish to use Testcontainers on Windows, please confirm that it works correctly for you before investing significant effort. From 9ed75d7259ab64767320b05d01fdd6dfb88ef552 Mon Sep 17 00:00:00 2001 From: Kirill Merkushev Date: Fri, 14 Sep 2018 10:17:10 +0200 Subject: [PATCH 0408/5615] MockServerContainer: remove client getter (breaking change) (#833) * remove dependency to java api originally that was not good idea to depend on something which can brake binary compatibility :) this change will allow to start any mockserver container version, doesn't matter which version of the client in the classpath * add server port getter --- modules/mockserver/build.gradle | 1 - .../containers/MockServerContainer.java | 17 ++++------------- .../containers/MockServerContainerTest.java | 3 ++- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/modules/mockserver/build.gradle b/modules/mockserver/build.gradle index 3fae7db0757..f0d9140e214 100644 --- a/modules/mockserver/build.gradle +++ b/modules/mockserver/build.gradle @@ -3,6 +3,5 @@ description = "Testcontainers :: MockServer" dependencies { compile project(':testcontainers') - compileOnly 'org.mock-server:mockserver-client-java:5.4.1' testCompile 'org.mock-server:mockserver-client-java:5.4.1' } diff --git a/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java b/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java index d245275a7af..3546aff7070 100644 --- a/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java +++ b/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java @@ -1,9 +1,6 @@ package org.testcontainers.containers; -import com.github.dockerjava.api.command.InspectContainerResponse; -import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import org.mockserver.client.MockServerClient; @Slf4j public class MockServerContainer extends GenericContainer { @@ -12,9 +9,6 @@ public class MockServerContainer extends GenericContainer { public static final int PORT = 80; - @Getter - private MockServerClient client; - public MockServerContainer() { this(VERSION); } @@ -25,14 +19,11 @@ public MockServerContainer(String version) { addExposedPorts(PORT); } - @Override - protected void containerIsStarted(InspectContainerResponse containerInfo) { - super.containerIsStarted(containerInfo); - - client = new MockServerClient(getContainerIpAddress(), getMappedPort(PORT)); - } - public String getEndpoint() { return String.format("http://%s:%d", getContainerIpAddress(), getMappedPort(PORT)); } + + public Integer getServerPort() { + return getMappedPort(PORT); + } } diff --git a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java index 4e51dc34a47..ae1b870fc7a 100644 --- a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java +++ b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java @@ -3,6 +3,7 @@ import lombok.Cleanup; import org.junit.ClassRule; import org.junit.Test; +import org.mockserver.client.MockServerClient; import java.io.BufferedReader; import java.io.InputStreamReader; @@ -20,7 +21,7 @@ public class MockServerContainerTest { @Test public void testBasicScenario() throws Exception { - mockServer.getClient() + new MockServerClient(mockServer.getContainerIpAddress(), mockServer.getServerPort()) .when(request("/hello")) .respond(response("Hello World!")); From faf5bc7e64cc6ae89706fe31925a422c54036cc0 Mon Sep 17 00:00:00 2001 From: doliu Date: Mon, 17 Sep 2018 13:44:52 +0800 Subject: [PATCH 0409/5615] add eBay as a user (#873) --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index 3a7c8a716f5..21d6abc58e6 100644 --- a/docs/index.md +++ b/docs/index.md @@ -30,6 +30,7 @@ Testcontainers makes it easy to launch useful Docker containers for the duration * [Spring Session](https://projects.spring.io/spring-session/) - Redis, PostgreSQL, MySQL and MariaDB integration testing * [Apache Camel](https://camel.apache.org) - Testing Camel against native services such as Consul, Etcd and so on * [Instana](https://www.instana.com) - Testing agents and stream processing backends + * [eBay Marketing](https://www.ebay.com) - Testing for MySQL, Cassandra, Redis, Couchbase, Kafka, etc. ## License From 53e5ece48b7d53d71db4c3c3e25f17f7e3b7044a Mon Sep 17 00:00:00 2001 From: Alar Aule Date: Thu, 20 Sep 2018 23:03:29 +0300 Subject: [PATCH 0410/5615] Improved RegistryAuthLocator and added tests for Windows (#868) #756 tested on Windows. Fixed RegistryAuthLocatorTest on Windows and also allowed better fallbacks from running credential provider (to allow lookup alternative AuthConfigs), when: 1) there is no hostName, then there is no point to ask credentials 2) when credential helper response with "credentials not found in native keychain" to try other resources Main reason for failing for me on Windows machine was #710 changes. When i used Netty or OkHttp together with npipe, then it worked fine. Yesterday evening i found out the reason and today morning i found also fix in master for that :-) - #865, breaking docker response by line breaks. --- .../utility/RegistryAuthLocator.java | 151 +++++++++++++++--- .../utility/RegistryAuthLocatorTest.java | 50 ++++-- .../auth-config/docker-credential-fake | 11 +- .../win/docker-credential-fake.bat | 21 +++ 4 files changed, 197 insertions(+), 36 deletions(-) create mode 100644 core/src/test/resources/auth-config/win/docker-credential-fake.bat diff --git a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java index 615e8326b62..efe7303d581 100644 --- a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java +++ b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java @@ -5,16 +5,19 @@ import com.github.dockerjava.api.model.AuthConfig; import com.google.common.annotations.VisibleForTesting; import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.SystemUtils; import org.slf4j.Logger; +import org.zeroturnaround.exec.InvalidResultException; import org.zeroturnaround.exec.ProcessExecutor; import java.io.ByteArrayInputStream; import java.io.File; +import java.io.IOException; import java.util.Base64; +import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import static org.apache.commons.lang.StringUtils.isBlank; import static org.slf4j.LoggerFactory.getLogger; @@ -28,14 +31,27 @@ public class RegistryAuthLocator { private static final Logger log = getLogger(RegistryAuthLocator.class); private static final String DEFAULT_REGISTRY_NAME = "index.docker.io"; private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + private static RegistryAuthLocator instance; + private final String commandPathPrefix; + private final String commandExtension; private final File configFile; + /** + * key - credential helper's name + * value - helper's response for "credentials not found" use case + */ + private final Map CREDENTIALS_HELPERS_NOT_FOUND_MESSAGE_CACHE; + @VisibleForTesting - RegistryAuthLocator(File configFile, String commandPathPrefix) { + RegistryAuthLocator(File configFile, String commandPathPrefix, String commandExtension, + Map notFoundMessageHolderReference) { this.configFile = configFile; this.commandPathPrefix = commandPathPrefix; + this.commandExtension = commandExtension; + + this.CREDENTIALS_HELPERS_NOT_FOUND_MESSAGE_CACHE = notFoundMessageHolderReference; } /** @@ -45,6 +61,9 @@ protected RegistryAuthLocator() { System.getProperty("user.home") + "/.docker"); this.configFile = new File(dockerConfigLocation + "/config.json"); this.commandPathPrefix = ""; + this.commandExtension = ""; + + this.CREDENTIALS_HELPERS_NOT_FOUND_MESSAGE_CACHE = new HashMap<>(); } public synchronized static RegistryAuthLocator instance() { @@ -79,12 +98,6 @@ static void setInstance(RegistryAuthLocator overrideInstance) { */ public AuthConfig lookupAuthConfig(DockerImageName dockerImageName, AuthConfig defaultAuthConfig) { - if (SystemUtils.IS_OS_WINDOWS) { - log.debug("RegistryAuthLocator is not supported on Windows. Please help test or improve it and update " + - "https://github.com/testcontainers/testcontainers-java/issues/756"); - return defaultAuthConfig; - } - log.debug("Looking up auth config for image: {}", dockerImageName); log.debug("RegistryAuthLocator has configFile: {} ({}) and commandPathPrefix: {}", @@ -119,7 +132,7 @@ public AuthConfig lookupAuthConfig(DockerImageName dockerImageName, AuthConfig d log.debug("no matching Auth Configs - falling back to defaultAuthConfig [{}]", toSafeString(defaultAuthConfig)); // otherwise, defaultAuthConfig should already contain any credentials available } catch (Exception e) { - log.debug("Failure when attempting to lookup auth config (dockerImageName: {}, configFile: {}. Falling back to docker-java default behaviour. Exception message: {}", + log.warn("Failure when attempting to lookup auth config (dockerImageName: {}, configFile: {}. Falling back to docker-java default behaviour. Exception message: {}", dockerImageName, configFile, e.getMessage()); @@ -189,30 +202,50 @@ private Map.Entry findAuthNode(final JsonNode config, final St return null; } - private AuthConfig runCredentialProvider(String hostName, String credHelper) throws Exception { - final String credentialHelperName = commandPathPrefix + "docker-credential-" + credHelper; - String data; + private AuthConfig runCredentialProvider(String hostName, String helperOrStoreName) throws Exception { + + if (isBlank(hostName)) { + log.debug("There is no point to locate AuthConfig for blank hostName. Return NULL to allow fallback"); + return null; + } + + final String credentialProgramName = getCredentialProgramName(helperOrStoreName); + final String data; - log.debug("Executing docker credential helper: {} to locate auth config for: {}", - credentialHelperName, hostName); + log.debug("Executing docker credential provider: {} to locate auth config for: {}", + credentialProgramName, hostName); try { - data = new ProcessExecutor() - .command(credentialHelperName, "get") - .redirectInput(new ByteArrayInputStream(hostName.getBytes())) - .readOutput(true) - .exitValueNormal() - .timeout(30, TimeUnit.SECONDS) - .execute() - .outputUTF8() - .trim(); + data = runCredentialProgram(hostName, credentialProgramName); + } catch (InvalidResultException e) { + + final String responseErrorMsg = extractCredentialProviderErrorMessage(e); + + if (!isBlank(responseErrorMsg)) { + String credentialsNotFoundMsg = getGenericCredentialsNotFoundMsg(credentialProgramName); + if (credentialsNotFoundMsg != null && credentialsNotFoundMsg.equals(responseErrorMsg)) { + log.info("Credentials not found for host ({}) when using credential helper/store ({})", + hostName, + credentialProgramName); + + return null; + } + + log.debug("Failure running docker credential helper/store ({}) with output '{}'", + credentialProgramName, responseErrorMsg); + + } else { + log.debug("Failure running docker credential helper/store ({})", credentialProgramName); + } + + throw e; } catch (Exception e) { - log.debug("Failure running docker credential helper ({})", credentialHelperName); + log.debug("Failure running docker credential helper/store ({})", credentialProgramName); throw e; } final JsonNode helperResponse = OBJECT_MAPPER.readTree(data); - log.debug("Credential helper provided auth config for: {}", hostName); + log.debug("Credential helper/store provided auth config for: {}", hostName); return new AuthConfig() .withRegistryAddress(helperResponse.at("/ServerURL").asText()) @@ -220,7 +253,75 @@ private AuthConfig runCredentialProvider(String hostName, String credHelper) thr .withPassword(helperResponse.at("/Secret").asText()); } + private String getCredentialProgramName(String credHelper) { + return commandPathPrefix + "docker-credential-" + credHelper + commandExtension; + } + private String effectiveRegistryName(DockerImageName dockerImageName) { return StringUtils.defaultIfEmpty(dockerImageName.getRegistry(), DEFAULT_REGISTRY_NAME); } + + private String getGenericCredentialsNotFoundMsg(String credentialHelperName) { + if (!CREDENTIALS_HELPERS_NOT_FOUND_MESSAGE_CACHE.containsKey(credentialHelperName)) { + String credentialsNotFoundMsg = discoverCredentialsHelperNotFoundMessage(credentialHelperName); + if (!isBlank(credentialsNotFoundMsg)) { + CREDENTIALS_HELPERS_NOT_FOUND_MESSAGE_CACHE.put(credentialHelperName, credentialsNotFoundMsg); + } + } + + return CREDENTIALS_HELPERS_NOT_FOUND_MESSAGE_CACHE.get(credentialHelperName); + } + + private String discoverCredentialsHelperNotFoundMessage(String credentialHelperName) { + // will do fake call to given credential helper to find out with which message + // it response when there are no credentials for given hostName + + // hostName should be valid, but most probably not existing + // IF its not enough, then should probably run 'list' command first to be sure... + final String notExistentFakeHostName = "https://not.a.real.registry/url"; + + String credentialsNotFoundMsg = null; + try { + runCredentialProgram(notExistentFakeHostName, credentialHelperName); + + // should not reach here + log.warn("Failure running docker credential helper ({}) with fake call, expected 'credentials not found' response", + credentialHelperName); + } catch(Exception e) { + if (e instanceof InvalidResultException) { + credentialsNotFoundMsg = extractCredentialProviderErrorMessage((InvalidResultException)e); + } + + if (isBlank(credentialsNotFoundMsg)) { + log.warn("Failure running docker credential helper ({}) with fake call, expected 'credentials not found' response. Exception message: {}", + credentialHelperName, + e.getMessage()); + } else { + log.debug("Got credentials not found error message from docker credential helper - {}", credentialsNotFoundMsg); + } + } + + return credentialsNotFoundMsg; + } + + private String extractCredentialProviderErrorMessage(InvalidResultException invalidResultEx) { + if (invalidResultEx.getResult() != null && invalidResultEx.getResult().hasOutput()) { + return invalidResultEx.getResult().outputString().trim(); + } + return null; + } + + private String runCredentialProgram(String hostName, String credentialHelperName) + throws InvalidResultException, InterruptedException, TimeoutException, IOException { + + return new ProcessExecutor() + .command(credentialHelperName, "get") + .redirectInput(new ByteArrayInputStream(hostName.getBytes())) + .readOutput(true) + .exitValueNormal() + .timeout(30, TimeUnit.SECONDS) + .execute() + .outputUTF8() + .trim(); + } } diff --git a/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java b/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java index 5d1611ae77e..fa1770df4f1 100644 --- a/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java +++ b/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java @@ -4,23 +4,17 @@ import com.google.common.io.Resources; import org.apache.commons.lang.SystemUtils; import org.jetbrains.annotations.NotNull; -import org.junit.Assume; -import org.junit.BeforeClass; import org.junit.Test; import java.io.File; import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertNull; public class RegistryAuthLocatorTest { - - @BeforeClass - public static void nonWindowsTest() throws Exception { - Assume.assumeFalse(SystemUtils.IS_OS_WINDOWS); - } - @Test public void lookupAuthConfigWithoutCredentials() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-empty.json"); @@ -87,10 +81,46 @@ public void lookupNonEmptyAuthWithHelper() throws URISyntaxException { assertEquals("Correct password is obtained from a credential helper", "secret", authConfig.getPassword()); } + @Test + public void lookupAuthConfigWithCredentialsNotFound() throws URISyntaxException { + Map notFoundMessagesReference = new HashMap<>(); + final RegistryAuthLocator authLocator = createTestAuthLocator("config-with-store.json", notFoundMessagesReference); + + DockerImageName dockerImageName = new DockerImageName("registry2.example.com/org/repo"); + final AuthConfig authConfig = authLocator.lookupAuthConfig(dockerImageName, new AuthConfig()); + + assertNull("No username should have been obtained from a credential store", authConfig.getUsername()); + assertNull("No secret should have been obtained from a credential store", authConfig.getPassword()); + assertEquals("Should have one 'credentials not found' message discovered", 1, notFoundMessagesReference.size()); + + String discoveredMessage = notFoundMessagesReference.values().iterator().next(); + + assertEquals( + "Not correct message discovered", + "Fake credentials not found on credentials store 'https://not.a.real.registry/url'", + discoveredMessage); + } + @NotNull private RegistryAuthLocator createTestAuthLocator(String configName) throws URISyntaxException { - final File configFile = new File(Resources.getResource("auth-config/" + configName).toURI()); - return new RegistryAuthLocator(configFile, configFile.getParentFile().getAbsolutePath() + "/"); + return createTestAuthLocator(configName, new HashMap<>()); } + @NotNull + private RegistryAuthLocator createTestAuthLocator(String configName, Map notFoundMessagesReference) throws URISyntaxException { + final File configFile = new File(Resources.getResource("auth-config/" + configName).toURI()); + + String commandPathPrefix = configFile.getParentFile().getAbsolutePath() + "/"; + String commandExtension = ""; + + if (SystemUtils.IS_OS_WINDOWS) { + commandPathPrefix += "win/"; + + // need to provide executable extension otherwise won't run it + // with real docker wincredential exe there is no problem + commandExtension = ".bat"; + } + + return new RegistryAuthLocator(configFile, commandPathPrefix, commandExtension, notFoundMessagesReference); + } } diff --git a/core/src/test/resources/auth-config/docker-credential-fake b/core/src/test/resources/auth-config/docker-credential-fake index 0cfa0dfb72d..0fc0d44621e 100755 --- a/core/src/test/resources/auth-config/docker-credential-fake +++ b/core/src/test/resources/auth-config/docker-credential-fake @@ -4,7 +4,16 @@ if [[ $1 != "get" ]]; then exit 1 fi -read > /dev/null +read inputLine + +if [[ $inputLine == "registry2.example.com" ]]; then + echo Fake credentials not found on credentials store \'$inputLine\' 1>&2 + exit 1 +fi +if [[ $inputLine == "https://not.a.real.registry/url" ]]; then + echo Fake credentials not found on credentials store \'$inputLine\' 1>&2 + exit 1 +fi echo '{' \ ' "ServerURL": "url",' \ diff --git a/core/src/test/resources/auth-config/win/docker-credential-fake.bat b/core/src/test/resources/auth-config/win/docker-credential-fake.bat new file mode 100644 index 00000000000..7b6b27df5a6 --- /dev/null +++ b/core/src/test/resources/auth-config/win/docker-credential-fake.bat @@ -0,0 +1,21 @@ +@echo off +if not "%1" == "get" ( + exit 1 +) + +set /p inputLine="" + +if "%inputLine%" == "registry2.example.com" ( + echo Fake credentials not found on credentials store '%inputLine%' 1>&2 + exit 1 +) +if "%inputLine%" == "https://not.a.real.registry/url" ( + echo Fake credentials not found on credentials store '%inputLine%' 1>&2 + exit 1 +) + +echo { +echo "ServerURL": "url", +echo "Username": "username", +echo "Secret": "secret" +echo } From 75e0ee4e9d14bc387224a62e179963a023ca08cf Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 20 Sep 2018 22:04:35 +0200 Subject: [PATCH 0411/5615] remove Netty classes from OkHttpInvocationBuilder (#869) * remove Netty classes from OkHttpInvocationBuilder * use `BufferedReader` * simplify JsonSink * code cleanup --- .../okhttp/OkHttpInvocationBuilder.java | 144 ++++++++++++------ .../transport/okhttp/OkHttpWebTarget.java | 4 +- 2 files changed, 99 insertions(+), 49 deletions(-) diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java index bc84e44a323..ee35cde2ce8 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java @@ -13,13 +13,10 @@ import com.github.dockerjava.api.exception.NotModifiedException; import com.github.dockerjava.api.exception.UnauthorizedException; import com.github.dockerjava.api.model.Frame; +import com.github.dockerjava.api.model.StreamType; import com.github.dockerjava.core.InvocationBuilder; -import com.github.dockerjava.netty.handler.FramedResponseStreamHandler; -import com.github.dockerjava.netty.handler.JsonResponseCallbackHandler; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.SimpleChannelInboundHandler; import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.experimental.FieldDefaults; import lombok.extern.slf4j.Slf4j; @@ -34,13 +31,15 @@ import okio.BufferedSource; import okio.Okio; import okio.Source; +import org.testcontainers.DockerClientFactory; import javax.annotation.Nullable; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; -import java.util.concurrent.atomic.AtomicBoolean; +import java.util.Objects; +import java.util.function.Consumer; @Slf4j @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) @@ -91,7 +90,7 @@ public void get(ResultCallback resultCallback) { executeAndStream( request, resultCallback, - new FramedResponseStreamHandler(resultCallback) + new FramedSink(resultCallback) ); } @@ -187,7 +186,7 @@ public void run() { okHttpClient, request, resultCallback, - new FramedResponseStreamHandler(resultCallback) + new FramedSink(resultCallback) ); } @@ -200,7 +199,7 @@ public void post(TypeReference typeReference, ResultCallback resultCal executeAndStream( request, resultCallback, - new JsonResponseCallbackHandler<>(typeReference, resultCallback) + new JsonSink<>(objectMapper, typeReference, resultCallback) ); } @@ -247,7 +246,7 @@ public MediaType contentType() { @Override public void writeTo(BufferedSink sink) throws IOException { - try(Source source = Okio.source(body)) { + try (Source source = Okio.source(body)) { sink.writeAll(source); } } @@ -286,49 +285,98 @@ protected Response execute(OkHttpClient okHttpClient, Request request) { } } - protected void executeAndStream(Request request, ResultCallback callback, SimpleChannelInboundHandler handler) { - executeAndStream(okHttpClient, request, callback, handler); + protected void executeAndStream(Request request, ResultCallback callback, Consumer sourceConsumer) { + executeAndStream(okHttpClient, request, callback, sourceConsumer); } - protected void executeAndStream(OkHttpClient okHttpClient, Request request, ResultCallback callback, SimpleChannelInboundHandler handler) { - // TODO proper thread management - Thread thread = new Thread() { - @Override - @SneakyThrows - public void run() { - try ( - Response response = execute(okHttpClient, request.newBuilder().tag("streaming").build()); - BufferedSource source = response.body().source(); - InputStream inputStream = source.inputStream(); - ) { - AtomicBoolean shouldStop = new AtomicBoolean(); - callback.onStart(() -> { - shouldStop.set(true); - response.close(); - }); - - byte[] buffer = new byte[4 * 1024]; - while (!(shouldStop.get() || source.exhausted())) { - int bytesReceived = inputStream.read(buffer); - - int offset = 0; - for (int i = 0; i < bytesReceived; i++) { - // some handlers like JsonResponseCallbackHandler do not work with multi-line buffers - boolean isLineBreak = buffer[i] == '\n'; - boolean isEndOfBuffer = i == bytesReceived - 1; - if (isLineBreak || isEndOfBuffer) { - handler.channelRead(null, Unpooled.wrappedBuffer(buffer, offset, i - offset + 1)); - offset = i + 1; - } - } + protected void executeAndStream(OkHttpClient okHttpClient, Request request, ResultCallback callback, Consumer sourceConsumer) { + Thread thread = new Thread(DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, () -> { + try ( + Response response = execute(okHttpClient, request.newBuilder().tag("streaming").build()); + BufferedSource source = response.body().source(); + ) { + callback.onStart(response); + sourceConsumer.accept(source); + callback.onComplete(); + } catch (Exception e) { + callback.onError(e); + } + }, "tc-okhttp-stream-" + Objects.hashCode(request)); + thread.setDaemon(true); + + thread.start(); + } + + @RequiredArgsConstructor + @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) + private static class JsonSink implements Consumer { + + ObjectMapper objectMapper; + + TypeReference typeReference; + + ResultCallback resultCallback; + + @Override + public void accept(BufferedSource source) { + try { + while (true) { + String line = source.readUtf8Line(); + if (line == null) { + break; } - callback.onComplete(); - } catch (Exception e) { - callback.onError(e); + + resultCallback.onNext(objectMapper.readValue(line, typeReference)); } + } catch (Exception e) { + resultCallback.onError(e); } - }; + } + } - thread.start(); + @RequiredArgsConstructor + @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) + private static class FramedSink implements Consumer { + + private static final int HEADER_SIZE = 8; + + ResultCallback resultCallback; + + @Override + public void accept(BufferedSource source) { + try { + while (!source.exhausted()) { + // See https://docs.docker.com/engine/api/v1.37/#operation/ContainerAttach + if(!source.request(HEADER_SIZE)) { + return; + } + StreamType streamType = streamType(source.readByte()); + source.skip(3); + int payloadSize = source.readInt(); + + if(!source.request(payloadSize)) { + return; + } + byte[] payload = source.readByteArray(payloadSize); + + resultCallback.onNext(new Frame(streamType, payload)); + } + } catch (Exception e) { + resultCallback.onError(e); + } + } + + private static StreamType streamType(byte streamType) { + switch (streamType) { + case 0: + return StreamType.STDIN; + case 1: + return StreamType.STDOUT; + case 2: + return StreamType.STDERR; + default: + return StreamType.RAW; + } + } } } diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpWebTarget.java b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpWebTarget.java index 85fe8c22288..7e12b0b4541 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpWebTarget.java +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpWebTarget.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; import com.github.dockerjava.core.InvocationBuilder; import com.github.dockerjava.core.WebTarget; import com.google.common.collect.HashMultimap; @@ -24,7 +25,8 @@ @Value class OkHttpWebTarget implements WebTarget { - private static final ObjectMapper MAPPER = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper() + .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); OkHttpClient okHttpClient; From b75083ec015711c6fe8c59f7464d68eba69dd545 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Fri, 21 Sep 2018 18:09:46 +0200 Subject: [PATCH 0412/5615] Fix missing link to kafka docs --- docs/SUMMARY.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 6362e2f56af..18c068814c5 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -42,7 +42,8 @@ * [Example](usage/webdriver_containers.md#example) * [Other browsers](usage/webdriver_containers.md#other-browsers) * [Recording videos](usage/webdriver_containers.md#recording-videos) - + +* [Kafka containers](usage/kafka_containers.md) * [Docker Compose](usage/docker_compose.md) * [Dockerfile containers](usage/dockerfile.md) * [Windows support](usage/windows_support.md) From c0f596a0ee1a821f15ec065b0eae9025bce55640 Mon Sep 17 00:00:00 2001 From: haphut Date: Sun, 23 Sep 2018 19:30:18 +0300 Subject: [PATCH 0413/5615] Update Pulsar to 2.1.1 (#874) --- modules/pulsar/build.gradle | 2 +- .../java/org/testcontainers/containers/PulsarContainer.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index d4faaa3c610..1bccc7460f1 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Pulsar" dependencies { compile project(':testcontainers') - testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.1.0-incubating' + testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.1.1-incubating' testCompile group: 'org.assertj', name: 'assertj-core', version: '3.10.0' } diff --git a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java index 1d942727550..48e6ff029f9 100644 --- a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java +++ b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java @@ -12,7 +12,7 @@ public class PulsarContainer extends GenericContainer { public static final int BROKER_HTTP_PORT = 8080; public static final String METRICS_ENDPOINT = "/metrics"; - private static final String PULSAR_VERSION = "2.1.0-incubating"; + private static final String PULSAR_VERSION = "2.1.1-incubating"; public PulsarContainer() { this(PULSAR_VERSION); From 8acee4d804c125beb3774f509744f31b58ad7fc8 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 23 Sep 2018 19:03:27 +0200 Subject: [PATCH 0414/5615] Check FS mounting lazily (#875) * Check FS mounting lazily * extract boolean + wording * wording * rename field to `fileMountingSupported` --- .../testcontainers/DockerClientFactory.java | 31 +++++++++++++++---- .../containers/GenericContainer.java | 12 +++++++ .../utility/ResourceReaper.java | 14 +++------ 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 499c4ee4d85..725f04ca6d1 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -9,6 +9,7 @@ import com.github.dockerjava.core.command.PullImageResultCallback; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableMap; +import lombok.Getter; import lombok.Synchronized; import lombok.extern.slf4j.Slf4j; import org.hamcrest.BaseMatcher; @@ -17,6 +18,7 @@ import org.testcontainers.dockerclient.DockerClientProviderStrategy; import org.testcontainers.dockerclient.DockerMachineClientProviderStrategy; import org.testcontainers.utility.ComparableVersion; +import org.testcontainers.utility.MountableFile; import org.testcontainers.utility.ResourceReaper; import org.testcontainers.utility.TestcontainersConfiguration; @@ -59,6 +61,9 @@ public class DockerClientFactory { private String activeApiVersion; private String activeExecutionDriver; + @Getter(lazy = true) + private final boolean fileMountingSupported = checkMountableFile(); + static { System.setProperty("org.testcontainers.shaded.io.netty.packagePrefix", "org.testcontainers.shaded."); } @@ -125,7 +130,6 @@ public DockerClient client() { if (checksEnabled) { checkDiskSpace(client, ryukContainerId); - checkMountableFile(client, ryukContainerId); } initialized = true; @@ -168,12 +172,27 @@ private void checkDiskSpace(DockerClient dockerClient, String id) { ); } - private void checkMountableFile(DockerClient dockerClient, String id) { - try (InputStream stream = dockerClient.copyArchiveFromContainerCmd(id, "/dummy").exec()) { - stream.read(); - VisibleAssertions.pass("File should be mountable"); + private boolean checkMountableFile() { + DockerClient dockerClient = client(); + + MountableFile mountableFile = MountableFile.forClasspathResource(ResourceReaper.class.getName().replace(".", "/") + ".class"); + + Volume volume = new Volume("/dummy"); + try { + return runInsideDocker( + createContainerCmd -> createContainerCmd.withBinds(new Bind(mountableFile.getResolvedPath(), volume, AccessMode.ro)), + (__, containerId) -> { + try (InputStream stream = dockerClient.copyArchiveFromContainerCmd(containerId, volume.getPath()).exec()) { + stream.read(); + return true; + } catch (Exception e) { + return false; + } + } + ); } catch (Exception e) { - VisibleAssertions.fail("File should be mountable but fails with " + e.getMessage()); + log.debug("Failure while checking for mountable file support", e); + return false; } } diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index b7bfe0d1e8e..0c4c2dd5a6a 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -29,6 +29,7 @@ import org.rnorth.ducttape.ratelimits.RateLimiter; import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; import org.rnorth.ducttape.unreliables.Unreliables; +import org.rnorth.visibleassertions.VisibleAssertions; import org.slf4j.Logger; import org.slf4j.profiler.Profiler; import org.testcontainers.DockerClientFactory; @@ -455,6 +456,17 @@ private void applyConfiguration(CreateContainerCmd createCommand) { .toArray(String[]::new); createCommand.withEnv(envArray); + boolean shouldCheckFileMountingSupport = binds.size() > 0 && !TestcontainersConfiguration.getInstance().isDisableChecks(); + if (shouldCheckFileMountingSupport) { + if (!DockerClientFactory.instance().isFileMountingSupported()) { + VisibleAssertions.warn( + "Unable to mount a file from test host into a running container. " + + "This may be a misconfiguration or limitation of your Docker environment. " + + "Some features might not work." + ); + } + } + Bind[] bindsArray = binds.stream() .toArray(Bind[]::new); createCommand.withBinds(bindsArray); diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index 8d791064643..4e3ccc70fa2 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -6,7 +6,6 @@ import com.github.dockerjava.api.exception.DockerException; import com.github.dockerjava.api.exception.InternalServerErrorException; import com.github.dockerjava.api.exception.NotFoundException; -import com.github.dockerjava.api.model.AccessMode; import com.github.dockerjava.api.model.Bind; import com.github.dockerjava.api.model.ExposedPort; import com.github.dockerjava.api.model.HostConfig; @@ -61,23 +60,18 @@ private ResourceReaper() { dockerClient = DockerClientFactory.instance().client(); } - public static String start(String hostIpAddress, DockerClient client) { - return start(hostIpAddress, client, false); + @Deprecated + public static String start(String hostIpAddress, DockerClient client, boolean withDummyMount) { + return start(hostIpAddress, client); } @SneakyThrows(InterruptedException.class) - public static String start(String hostIpAddress, DockerClient client, boolean withDummyMount) { + public static String start(String hostIpAddress, DockerClient client) { String ryukImage = TestcontainersConfiguration.getInstance().getRyukImage(); DockerClientFactory.instance().checkAndPullImage(client, ryukImage); - MountableFile mountableFile = MountableFile.forClasspathResource(ResourceReaper.class.getName().replace(".", "/") + ".class"); - List binds = new ArrayList<>(); binds.add(new Bind("//var/run/docker.sock", new Volume("/var/run/docker.sock"))); - if (withDummyMount) { - // Not needed for Ryuk, but we perform pre-flight checks with it (micro optimization) - binds.add(new Bind(mountableFile.getResolvedPath(), new Volume("/dummy"), AccessMode.ro)); - } String ryukContainerId = client.createContainerCmd(ryukImage) .withHostConfig(new HostConfig() { From 05b3c0100837f3b70aa017e0ebc7e094f2e043ee Mon Sep 17 00:00:00 2001 From: kstrek <38620687+kstrek@users.noreply.github.com> Date: Sun, 23 Sep 2018 19:42:20 +0200 Subject: [PATCH 0415/5615] Allow configuration of user settings to support couchbase community version (#777) --- .../couchbase/AbstractCouchbaseTest.java | 13 ++-- .../couchbase/CouchbaseContainer.java | 60 ++++++++++++------- .../couchbase/CouchbaseCommunity5_1Test.java | 31 ++++++++++ 3 files changed, 79 insertions(+), 25 deletions(-) create mode 100644 modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseCommunity5_1Test.java diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java index 1d713887253..d0eca23f2e6 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java @@ -3,6 +3,7 @@ import com.couchbase.client.java.Bucket; import com.couchbase.client.java.CouchbaseCluster; import com.couchbase.client.java.bucket.BucketType; +import com.couchbase.client.java.cluster.BucketSettings; import com.couchbase.client.java.cluster.DefaultBucketSettings; import com.couchbase.client.java.query.N1qlParams; import com.couchbase.client.java.query.N1qlQuery; @@ -35,16 +36,20 @@ public void clear() { protected static CouchbaseContainer initCouchbaseContainer(String imageName) { CouchbaseContainer couchbaseContainer = (imageName == null) ? new CouchbaseContainer() : new CouchbaseContainer(imageName); - couchbaseContainer.withNewBucket(DefaultBucketSettings.builder() + couchbaseContainer.withNewBucket(getDefaultBucketSettings()); + couchbaseContainer.start(); + return couchbaseContainer; + } + + protected static BucketSettings getDefaultBucketSettings() { + return DefaultBucketSettings.builder() .enableFlush(true) .name(TEST_BUCKET) .password(DEFAULT_PASSWORD) .quota(100) .replicas(0) .type(BucketType.COUCHBASE) - .build()); - couchbaseContainer.start(); - return couchbaseContainer; + .build(); } protected synchronized Bucket getBucket() { diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index 784eb246458..f463d81279e 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -95,7 +95,7 @@ public class CouchbaseContainer extends GenericContainer { @Getter(lazy = true) private final CouchbaseCluster couchbaseCluster = createCouchbaseCluster(); - private List newBuckets = new ArrayList<>(); + private List newBuckets = new ArrayList<>(); private String urlBase; @@ -190,7 +190,12 @@ private void stopCluster() { } public CouchbaseContainer withNewBucket(BucketSettings bucketSettings) { - newBuckets.add(bucketSettings); + newBuckets.add(new BucketAndUserSettings(bucketSettings)); + return self(); + } + + public CouchbaseContainer withNewBucket(BucketSettings bucketSettings, UserSettings userSettings) { + newBuckets.add(new BucketAndUserSettings(bucketSettings, userSettings)); return self(); } @@ -247,15 +252,12 @@ private HttpWaitStrategy createNodeWaitStrategy() { }); } - public void createBucket(BucketSettings bucketSetting, boolean primaryIndex) { + public void createBucket(BucketSettings bucketSetting, UserSettings userSettings, boolean primaryIndex) { ClusterManager clusterManager = getCouchbaseCluster().clusterManager(clusterUsername, clusterPassword); // Insert Bucket BucketSettings bucketSettings = clusterManager.insertBucket(bucketSetting); - // Insert Bucket admin user - UserSettings userSettings = UserSettings.build() - .password(bucketSetting.password()) - .roles(getAdminRoles(bucketSetting.name())); try { + // Insert Bucket user clusterManager.upsertUser(AuthDomain.LOCAL, bucketSetting.name(), userSettings); } catch (Exception e) { logger().warn("Unable to insert user '" + bucketSetting.name() + "', maybe you are using older version"); @@ -269,18 +271,6 @@ public void createBucket(BucketSettings bucketSetting, boolean primaryIndex) { } } - private List getAdminRoles(String bucketName) { - return Lists.newArrayList( - new UserRole("bucket_admin", bucketName), - new UserRole("views_admin", bucketName), - new UserRole("query_manage_index", bucketName), - new UserRole("query_update", bucketName), - new UserRole("query_select", bucketName), - new UserRole("query_insert", bucketName), - new UserRole("query_delete", bucketName) - ); - } - public void callCouchbaseRestAPI(String url, String payload) throws IOException { String fullUrl = urlBase + url; @Cleanup("disconnect") @@ -328,8 +318,8 @@ private String replaceCapiPort(String originalConfig) { @Override protected void containerIsStarted(InspectContainerResponse containerInfo) { if (!newBuckets.isEmpty()) { - for (BucketSettings bucketSetting : newBuckets) { - createBucket(bucketSetting, primaryIndex); + for (BucketAndUserSettings bucket : newBuckets) { + createBucket(bucket.getBucketSettings(), bucket.getUserSettings(), primaryIndex); } } } @@ -414,4 +404,32 @@ protected enum CouchbasePort { final boolean dynamic; } + + @Value + @AllArgsConstructor + private class BucketAndUserSettings { + + private final BucketSettings bucketSettings; + private final UserSettings userSettings; + + public BucketAndUserSettings(final BucketSettings bucketSettings) { + this.bucketSettings = bucketSettings; + this.userSettings = UserSettings.build() + .password(bucketSettings.password()) + .roles(getDefaultAdminRoles(bucketSettings.name())); + } + + private List getDefaultAdminRoles(String bucketName) { + return Lists.newArrayList( + new UserRole("bucket_admin", bucketName), + new UserRole("views_admin", bucketName), + new UserRole("query_manage_index", bucketName), + new UserRole("query_update", bucketName), + new UserRole("query_select", bucketName), + new UserRole("query_insert", bucketName), + new UserRole("query_delete", bucketName) + ); + } + + } } diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseCommunity5_1Test.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseCommunity5_1Test.java new file mode 100644 index 00000000000..74298c990cf --- /dev/null +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseCommunity5_1Test.java @@ -0,0 +1,31 @@ +package org.testcontainers.couchbase; + +import java.util.Collections; + +import org.junit.ClassRule; + +import com.couchbase.client.java.cluster.UserRole; +import com.couchbase.client.java.cluster.UserSettings; + +public class CouchbaseCommunity5_1Test extends BaseCouchbaseContainerTest { + @ClassRule + public static CouchbaseContainer container = initCouchbaseContainer(); + + private static CouchbaseContainer initCouchbaseContainer() { + CouchbaseContainer couchbaseContainer = new CouchbaseContainer("couchbase/server:community-5.1.1"); + + final UserSettings communityAdminUserSettings = + UserSettings.build() + .password(getDefaultBucketSettings().password()) + .roles(Collections.singletonList(new UserRole("admin"))); + + couchbaseContainer.withNewBucket(getDefaultBucketSettings(), communityAdminUserSettings); + couchbaseContainer.start(); + return couchbaseContainer; + } + + @Override + public CouchbaseContainer getCouchbaseContainer() { + return container; + } +} From 2307b796cf467059597ff57d959f3d84b9fa8af2 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Sun, 23 Sep 2018 21:55:49 +0200 Subject: [PATCH 0416/5615] Remove unnecessary start() call in initCouchbaseContainer() method (#877) --- .../java/org/testcontainers/couchbase/AbstractCouchbaseTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java index d0eca23f2e6..e1aec0ba424 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java @@ -37,7 +37,6 @@ public void clear() { protected static CouchbaseContainer initCouchbaseContainer(String imageName) { CouchbaseContainer couchbaseContainer = (imageName == null) ? new CouchbaseContainer() : new CouchbaseContainer(imageName); couchbaseContainer.withNewBucket(getDefaultBucketSettings()); - couchbaseContainer.start(); return couchbaseContainer; } From 007c3b9df12145f5175a7dd17c476a01e6f1f5b8 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 23 Sep 2018 20:56:29 +0100 Subject: [PATCH 0417/5615] Guard against potential NPE if startup fails before container creation (#876) --- .../containers/GenericContainer.java | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 0c4c2dd5a6a..32cae84951a 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -296,18 +296,20 @@ private void tryStart(Profiler profiler) { } catch (Exception e) { logger().error("Could not start container", e); - // Log output if startup failed, either due to a container failure or exception (including timeout) - logger().error("Container log output (if any) will follow:"); - FrameConsumerResultCallback resultCallback = new FrameConsumerResultCallback(); - resultCallback.addConsumer(STDOUT, new Slf4jLogConsumer(logger())); - resultCallback.addConsumer(STDERR, new Slf4jLogConsumer(logger())); - dockerClient.logContainerCmd(containerId).withStdOut(true).withStdErr(true).exec(resultCallback); - - // Try to ensure that container log output is shown before proceeding - try { - resultCallback.getCompletionLatch().await(1, TimeUnit.MINUTES); - } catch (InterruptedException ignored) { - // Cannot do anything at this point + if (containerId != null) { + // Log output if startup failed, either due to a container failure or exception (including timeout) + logger().error("Container log output (if any) will follow:"); + FrameConsumerResultCallback resultCallback = new FrameConsumerResultCallback(); + resultCallback.addConsumer(STDOUT, new Slf4jLogConsumer(logger())); + resultCallback.addConsumer(STDERR, new Slf4jLogConsumer(logger())); + dockerClient.logContainerCmd(containerId).withStdOut(true).withStdErr(true).exec(resultCallback); + + // Try to ensure that container log output is shown before proceeding + try { + resultCallback.getCompletionLatch().await(1, TimeUnit.MINUTES); + } catch (InterruptedException ignored) { + // Cannot do anything at this point + } } throw new ContainerLaunchException("Could not create/start container", e); From e13f7817818aabbf7bd241ff956d8f1f5eefcca3 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 25 Sep 2018 08:08:45 +0200 Subject: [PATCH 0418/5615] Fix AutoRemove of Ryuk container (#882) --- .../main/java/org/testcontainers/utility/ResourceReaper.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index 4e3ccc70fa2..8e631273257 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -74,10 +74,7 @@ public static String start(String hostIpAddress, DockerClient client) { binds.add(new Bind("//var/run/docker.sock", new Volume("/var/run/docker.sock"))); String ryukContainerId = client.createContainerCmd(ryukImage) - .withHostConfig(new HostConfig() { - @JsonProperty("AutoRemove") - boolean autoRemove = true; - }) + .withHostConfig(new HostConfig().withAutoRemove(true)) .withExposedPorts(new ExposedPort(8080)) .withPublishAllPorts(true) .withName("testcontainers-ryuk-" + DockerClientFactory.SESSION_ID) From ef9b592d95d7ccd3ab024833f7875ec53a8d11ff Mon Sep 17 00:00:00 2001 From: David Pilato Date: Tue, 25 Sep 2018 10:07:34 +0200 Subject: [PATCH 0419/5615] Fix HttpWaitStrategy.forStatusCodeMatching used with HttpWaitStrategy.forStatusCode (#881) In #630 we introduced predicates but with a default one which is always present whatever is passed in the `forStatusCodeMatching()` method. This commit adds a test that demonstrates the issue: * We have a service returning `200 OK` * The predicate expects anything which is a code >= to `300` * The test should throw a Timeout as this condition is never reached but without the current fix, the test never throws the Timeout as 200 matches the default builtin predicate. This commit fixes the problem by checking at startup time what is/are the predicates that needs to be applied. Note that in most cases, an HTTP service is expected to throw a `200 OK` status so that fix might not fix actually any real problem and might be a theory only. But I'd prefer to have code that actually implements what is supposed to work. Closes #880. --- .../wait/strategy/HttpWaitStrategy.java | 25 +++++++++++++------ .../wait/strategy/HttpWaitStrategyTest.java | 15 +++++++++++ 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java index 29f5a64b6df..6096e745e36 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java @@ -39,11 +39,7 @@ public class HttpWaitStrategy extends AbstractWaitStrategy { private String username; private String password; private Predicate responsePredicate; - private Predicate statusCodePredicate = responseCode -> { - // If we did not provide any status code, we assume by default HttpURLConnection.HTTP_OK - if (statusCodes.isEmpty() && HttpURLConnection.HTTP_OK == responseCode) return true; - return statusCodes.contains(responseCode); - }; + private Predicate statusCodePredicate = null; private Optional livenessPort = Optional.empty(); /** @@ -63,7 +59,7 @@ public HttpWaitStrategy forStatusCode(int statusCode) { * @return this */ public HttpWaitStrategy forStatusCodeMatching(Predicate statusCodePredicate) { - this.statusCodePredicate = this.statusCodePredicate.or(statusCodePredicate); + this.statusCodePredicate = statusCodePredicate; return this; } @@ -159,7 +155,22 @@ protected void waitUntilReady() { log.trace("Get response code {}", connection.getResponseCode()); - if (!statusCodePredicate.test(connection.getResponseCode())) { + // Choose the statusCodePredicate strategy depending on what we defined. + Predicate predicate; + if (statusCodes.isEmpty() && statusCodePredicate == null) { + // We have no status code and no predicate so we expect a 200 OK response code + predicate = responseCode -> HttpURLConnection.HTTP_OK == responseCode; + } else if (!statusCodes.isEmpty() && statusCodePredicate == null) { + // We use the default status predicate checker when we only have status codes + predicate = responseCode -> statusCodes.contains(responseCode); + } else if (statusCodes.isEmpty()) { + // We only have a predicate + predicate = statusCodePredicate; + } else { + // We have both predicate and status code + predicate = statusCodePredicate.or(responseCode -> statusCodes.contains(responseCode)); + } + if (!predicate.test(connection.getResponseCode())) { throw new RuntimeException(String.format("HTTP response code was: %s", connection.getResponseCode())); } diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java index 65a693f514f..5820333245a 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java @@ -84,6 +84,21 @@ public void testWaitUntilReadyWithTimeoutAndWithManyStatusCodesAndLambda() { )); } + /** + * Expects that the WaitStrategy throws a {@link RetryCountExceededException} after not receiving any of the + * error code defined with {@link HttpWaitStrategy#forStatusCode(int)} + * and {@link HttpWaitStrategy#forStatusCodeMatching(Predicate)}. Note that a 200 status code should not + * be considered as a successful return as not explicitly set. + * Test case for: https://github.com/testcontainers/testcontainers-java/issues/880 + */ + @Test + public void testWaitUntilReadyWithTimeoutAndWithLambdaShouldNotMatchOk() { + waitUntilReadyAndTimeout(startContainerWithCommand(createShellCommand("200 OK", GOOD_RESPONSE_BODY), + createHttpWaitStrategy(ready) + .forStatusCodeMatching(it -> it >= 300) + )); + } + /** * Expects that the WaitStrategy throws a {@link RetryCountExceededException} after not receiving an HTTP 200 * response from the container within the timeout period. From 0f25d00688ba54215fbb6afdeb0a4649dc0cb128 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 25 Sep 2018 10:10:01 +0100 Subject: [PATCH 0420/5615] Point readers to 'Releases' --- CHANGELOG.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ceccc9319f6..aac8c306777 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,9 @@ # Change Log -All notable changes to this project will be documented in this file. +~All notable changes to this project will be documented in this file.~ -## UNRELEASED +# MOVED -### Fixed - -### Changed +**After version 1.8.3 all future releases will _only_ be documented in the [Releases](https://github.com/testcontainers/testcontainers-java/releases) section of the GitHub repository. This changelog file will eventually be removed.** ## [1.8.3] - 2018-08-05 From 0639becfef923a09dfb7d27c4b016313daef8a37 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 27 Sep 2018 12:51:27 +0200 Subject: [PATCH 0421/5615] OpenJDK 11 compatibility and tests in Travis (#883) --- .travis.yml | 2 +- build.gradle | 2 +- core/build.gradle | 3 ++- gradle/wrapper/gradle-wrapper.properties | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 52e2adcf814..a5cfcc0f3eb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ jobs: - stage: test env: [ NAME=core ] - jdk: oraclejdk9 + jdk: openjdk11 script: ./gradlew testcontainers:check - env: [ NAME=selenium ] diff --git a/build.gradle b/build.gradle index c26914002f0..4a725afb9df 100644 --- a/build.gradle +++ b/build.gradle @@ -21,7 +21,7 @@ subprojects { compileJava.options.encoding = 'UTF-8' lombok { - version = '1.16.20' + version = '1.18.2' } task delombok(type: io.franzbecker.gradle.lombok.task.DelombokTask) { diff --git a/core/build.gradle b/core/build.gradle index fec6cd6ee71..bda81410355 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -127,7 +127,8 @@ dependencies { // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest testCompile files('testlib/repo/fakejar/fakejar/0/fakejar-0.jar') - jarFileTestCompileOnly 'org.projectlombok:lombok:1.16.20' + + jarFileTestCompileOnly "org.projectlombok:lombok:${lombok.version}" jarFileTestCompile 'junit:junit:4.12' jarFileTestCompile 'org.assertj:assertj-core:3.8.0' jarFileTestCompile 'org.ow2.asm:asm-debug-all:5.2' diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 73bb13d5561..019065d1d65 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip From 01b71e67ebe2a333f585ab2d7d82a26825658dd4 Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 28 Sep 2018 18:38:12 +0100 Subject: [PATCH 0422/5615] Add Skyscanner to users list (#889) --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index 21d6abc58e6..01aa278bda7 100644 --- a/docs/index.md +++ b/docs/index.md @@ -31,6 +31,7 @@ Testcontainers makes it easy to launch useful Docker containers for the duration * [Apache Camel](https://camel.apache.org) - Testing Camel against native services such as Consul, Etcd and so on * [Instana](https://www.instana.com) - Testing agents and stream processing backends * [eBay Marketing](https://www.ebay.com) - Testing for MySQL, Cassandra, Redis, Couchbase, Kafka, etc. + * [Skyscanner](https://www.skyscanner.net/) - Integration testing against HTTP service mocks and various data stores ## License From 1e4e413c5fc33965091891bff914f1cf2a89ea3a Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 1 Oct 2018 09:38:58 +0100 Subject: [PATCH 0423/5615] Use latest available VNC recorder image (#888) This updates us to use `quay.io/testcontainers/vnc-recorder:1.1.0`, rather than the legacy `richnorth/vnc-recorder` image as the default VNC recorder. This new base image is based upon testcontainers/vnc-recorder#1 and is intended to avoid an apparent segfault bug with the older version of debian that affects some users. --- .../org/testcontainers/utility/TestcontainersConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 7a7811590ce..448e4184e55 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -35,7 +35,7 @@ public String getSocatContainerImage() { } public String getVncRecordedContainerImage() { - return (String) properties.getOrDefault("vncrecorder.container.image", "richnorth/vnc-recorder:latest"); + return (String) properties.getOrDefault("vncrecorder.container.image", "quay.io/testcontainers/vnc-recorder:1.1.0"); } public String getDockerComposeContainerImage() { From dfed94ef7305bd27cae4e132d6bcd611500d2caf Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 2 Oct 2018 11:11:06 +0100 Subject: [PATCH 0424/5615] Extract Contributing section to an easier-to-find CONTRIBUTING.md file (#891) --- CONTRIBUTING.md | 12 ++++++++++++ docs/index.md | 11 +---------- 2 files changed, 13 insertions(+), 10 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000000..db3a45bc775 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,12 @@ +# Contributing + +* Star the project on [Github](https://github.com/testcontainers/testcontainers-java) and help spread the word :) +* Join our Slack: http://slack.testcontainers.org +* See [ROADMAP](ROADMAP.md) to understand the approach behind the project and what may/may not be in store for the future. +* [Post an issue](https://github.com/testcontainers/testcontainers-java/issues) if you find any bugs +* Contribute improvements or fixes using a [Pull Request](https://github.com/testcontainers/testcontainers-java/pulls). If you're going to contribute, thank you! Please just be sure to: + * discuss with the authors on an issue ticket prior to doing anything big + * follow the style, naming and structure conventions of the rest of the project + * make commits atomic and easy to merge + * verify all tests are passing. Build the project with `./gradlew check` to do this. + **N.B.** Gradle's Build Cache is enabled by default, but you can add `--no-build-cache` flag to disable it. \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index 01aa278bda7..91b1ff33a17 100644 --- a/docs/index.md +++ b/docs/index.md @@ -45,16 +45,7 @@ This project was initially inspired by a [gist](https://gist.github.com/mosheesh ## Contributing -* Star the project on [Github](https://github.com/testcontainers/testcontainers-java) and help spread the word :) -* Join our Slack: http://slack.testcontainers.org -* See [ROADMAP](ROADMAP.md) to understand the approach behind the project and what may/may not be in store for the future. -* [Post an issue](https://github.com/testcontainers/testcontainers-java/issues) if you find any bugs -* Contribute improvements or fixes using a [Pull Request](https://github.com/testcontainers/testcontainers-java/pulls). If you're going to contribute, thank you! Please just be sure to: - * discuss with the authors on an issue ticket prior to doing anything big - * follow the style, naming and structure conventions of the rest of the project - * make commits atomic and easy to merge - * verify all tests are passing. Build the project with `./gradlew check` to do this. - **N.B.** Gradle's Build Cache is enabled by default, but you can add `--no-build-cache` flag to disable it. +See [CONTRIBUTING](https://github.com/testcontainers/testcontainers-java/blob/master/CONTRIBUTING.md). ## Copyright From c05ad88b6cd2d5e450d6a3b09598b59f85323d94 Mon Sep 17 00:00:00 2001 From: Frederik Hahne Date: Wed, 3 Oct 2018 10:43:06 +0200 Subject: [PATCH 0425/5615] Add missing argument to log statement (#895) --- .../main/java/org/testcontainers/utility/ResourceReaper.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index 8e631273257..e8b3a5565a3 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -1,6 +1,5 @@ package org.testcontainers.utility; -import com.fasterxml.jackson.annotation.JsonProperty; import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.exception.DockerException; @@ -219,7 +218,7 @@ private void stopContainer(String containerId, String imageName) { InspectContainerResponse containerInfo = dockerClient.inspectContainerCmd(containerId).exec(); running = containerInfo.getState().getRunning(); } catch (NotFoundException e) { - LOGGER.trace("Was going to stop container but it apparently no longer exists: {}"); + LOGGER.trace("Was going to stop container but it apparently no longer exists: {}", containerId); return; } catch (DockerException e) { LOGGER.trace("Error encountered when checking container for shutdown (ID: {}) - it may not have been stopped, or may already be stopped: {}", containerId, e.getMessage()); @@ -239,7 +238,7 @@ private void stopContainer(String containerId, String imageName) { try { dockerClient.inspectContainerCmd(containerId).exec(); } catch (NotFoundException e) { - LOGGER.trace("Was going to remove container but it apparently no longer exists: {}"); + LOGGER.trace("Was going to remove container but it apparently no longer exists: {}", containerId); return; } From 08ba43f4f371f9f126f13105537001c02ee8e172 Mon Sep 17 00:00:00 2001 From: Vijayakrishna Pondala Date: Thu, 4 Oct 2018 18:28:00 +1000 Subject: [PATCH 0426/5615] Added an example using TestNG (#778) (#893) The tests in this example are same as redis-backend-cache, but runs tests using TestNG instead of JUnit. Closes #778 --- examples/pom.xml | 1 + examples/redis-backed-cache-testng/pom.xml | 51 ++++++++++++++++ .../main/java/com/mycompany/cache/Cache.java | 27 +++++++++ .../com/mycompany/cache/RedisBackedCache.java | 39 ++++++++++++ .../src/test/java/RedisBackedCacheTest.java | 59 +++++++++++++++++++ .../src/test/resources/logback-test.xml | 18 ++++++ 6 files changed, 195 insertions(+) create mode 100644 examples/redis-backed-cache-testng/pom.xml create mode 100644 examples/redis-backed-cache-testng/src/main/java/com/mycompany/cache/Cache.java create mode 100644 examples/redis-backed-cache-testng/src/main/java/com/mycompany/cache/RedisBackedCache.java create mode 100644 examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java create mode 100644 examples/redis-backed-cache-testng/src/test/resources/logback-test.xml diff --git a/examples/pom.xml b/examples/pom.xml index a691311778d..63fea191f18 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -7,6 +7,7 @@ NOVERSION redis-backed-cache + redis-backed-cache-testng disque-job-queue selenium-container spring-boot diff --git a/examples/redis-backed-cache-testng/pom.xml b/examples/redis-backed-cache-testng/pom.xml new file mode 100644 index 00000000000..4da8266c9b6 --- /dev/null +++ b/examples/redis-backed-cache-testng/pom.xml @@ -0,0 +1,51 @@ + + + + testcontainers-java-examples + org.testcontainers.examples + NOVERSION + + 4.0.0 + + redis-backed-cache-testng + Redis Backend Cache testing example using TestNG + + + redis.clients + jedis + 2.8.0 + + + com.google.code.gson + gson + 2.5 + + + com.google.guava + guava + 18.0 + + + org.slf4j + slf4j-api + 1.7.7 + provided + + + ch.qos.logback + logback-classic + 1.1.2 + test + + + org.testng + testng + 6.9.10 + test + + + + + \ No newline at end of file diff --git a/examples/redis-backed-cache-testng/src/main/java/com/mycompany/cache/Cache.java b/examples/redis-backed-cache-testng/src/main/java/com/mycompany/cache/Cache.java new file mode 100644 index 00000000000..996c31ecac7 --- /dev/null +++ b/examples/redis-backed-cache-testng/src/main/java/com/mycompany/cache/Cache.java @@ -0,0 +1,27 @@ +package com.mycompany.cache; + +import java.util.Optional; + +/** + * Cache, for storing data associated with keys. + */ +public interface Cache { + + /** + * Store a value object in the cache with no specific expiry time. The object may be evicted by the cache any time, + * if necessary. + * + * @param key key that may be used to retrieve the object in the future + * @param value the value object to be stored + */ + void put(String key, Object value); + + /** + * Retrieve a value object from the cache. + * @param key the key that was used to insert the object initially + * @param expectedClass for convenience, a class that the object should be cast to before being returned + * @param the class of the returned object + * @return the object if it was in the cache, or an empty Optional if not found. + */ + Optional get(String key, Class expectedClass); +} \ No newline at end of file diff --git a/examples/redis-backed-cache-testng/src/main/java/com/mycompany/cache/RedisBackedCache.java b/examples/redis-backed-cache-testng/src/main/java/com/mycompany/cache/RedisBackedCache.java new file mode 100644 index 00000000000..2c80818c150 --- /dev/null +++ b/examples/redis-backed-cache-testng/src/main/java/com/mycompany/cache/RedisBackedCache.java @@ -0,0 +1,39 @@ +package com.mycompany.cache; + +import com.google.gson.Gson; +import redis.clients.jedis.Jedis; + +import java.util.Optional; + +/** + * An implementation of {@link Cache} that stores data in Redis. + */ +public class RedisBackedCache implements Cache { + + private final Jedis jedis; + private final String cacheName; + private final Gson gson; + + public RedisBackedCache(Jedis jedis, String cacheName) { + this.jedis = jedis; + this.cacheName = cacheName; + this.gson = new Gson(); + } + + @Override + public void put(String key, Object value) { + String jsonValue = gson.toJson(value); + this.jedis.hset(this.cacheName, key, jsonValue); + } + + @Override + public Optional get(String key, Class expectedClass) { + String foundJson = this.jedis.hget(this.cacheName, key); + + if (foundJson == null) { + return Optional.empty(); + } + + return Optional.of(gson.fromJson(foundJson, expectedClass)); + } +} diff --git a/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java b/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java new file mode 100644 index 00000000000..ad2d62a2f04 --- /dev/null +++ b/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java @@ -0,0 +1,59 @@ +import com.mycompany.cache.Cache; +import com.mycompany.cache.RedisBackedCache; +import org.testcontainers.containers.GenericContainer; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import redis.clients.jedis.Jedis; + +import java.util.Optional; + +import static org.rnorth.visibleassertions.VisibleAssertions.*; + +/** + * Integration test for Redis-backed cache implementation. + */ +public class RedisBackedCacheTest { + + private static GenericContainer redis = new GenericContainer("redis:3.0.6").withExposedPorts(6379); + + private Cache cache; + + @BeforeClass + public static void startContainer() { + redis.start(); + } + + @AfterClass + public static void stopContainer() { + redis.stop(); + } + + @BeforeMethod + public void setUp() { + Jedis jedis = new Jedis(redis.getContainerIpAddress(), redis.getMappedPort(6379)); + + cache = new RedisBackedCache(jedis, "test"); + } + + @Test + public void testFindingAnInsertedValue() { + cache.put("foo", "FOO"); + Optional foundObject = cache.get("foo", String.class); + + assertTrue("When an object in the cache is retrieved, it can be found", + foundObject.isPresent()); + assertEquals("When we put a String in to the cache and retrieve it, the value is the same", + "FOO", + foundObject.get()); + } + + @Test + public void testNotFindingAValueThatWasNotInserted() { + Optional foundObject = cache.get("bar", String.class); + + assertFalse("When an object that's not in the cache is retrieved, nothing is found", + foundObject.isPresent()); + } +} diff --git a/examples/redis-backed-cache-testng/src/test/resources/logback-test.xml b/examples/redis-backed-cache-testng/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..c53f381fca5 --- /dev/null +++ b/examples/redis-backed-cache-testng/src/test/resources/logback-test.xml @@ -0,0 +1,18 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + + + + + + + + + + + \ No newline at end of file From d402295dd717cbe8df945bcdff93f12697ee9599 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Thu, 4 Oct 2018 11:41:33 +0200 Subject: [PATCH 0427/5615] Add CircleCI job for examples (#897) Also fixes failing SeleniumContainerTest in examples. --- circle.yml | 16 ++++++++++++++++ .../src/test/java/SeleniumContainerTest.java | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 625c5026db0..690d3d9da80 100644 --- a/circle.yml +++ b/circle.yml @@ -82,6 +82,21 @@ jobs: path: ~/junit - store_artifacts: path: ~/junit + examples: + steps: + - checkout + - run: + command: mvn -B -f examples/pom.xml test + - run: + name: Save test results + command: | + mkdir -p ~/junit/ + find examples -type f -regex ".*/target/surefire-reports/.*xml" -exec cp {} ~/junit/ \; + when: always + - store_test_results: + path: ~/junit + - store_artifacts: + path: ~/junit workflows: version: 2 @@ -92,3 +107,4 @@ workflows: - modules-no-jdbc-test-no-selenium - modules-jdbc-test - selenium + - examples diff --git a/examples/selenium-container/src/test/java/SeleniumContainerTest.java b/examples/selenium-container/src/test/java/SeleniumContainerTest.java index 9630a87fe14..528397fa1fd 100644 --- a/examples/selenium-container/src/test/java/SeleniumContainerTest.java +++ b/examples/selenium-container/src/test/java/SeleniumContainerTest.java @@ -30,7 +30,7 @@ public void simplePlainSeleniumTest() { searchInput.sendKeys("Rick Astley"); searchInput.submit(); - WebElement otherPage = driver.findElementByLinkText("Rickrolling"); + WebElement otherPage = driver.findElementByPartialLinkText("Rickrolling"); otherPage.click(); boolean expectedTextFound = driver.findElementsByCssSelector("p") From aa762fb9dd59891b6f5cc01e05119124a8b9140c Mon Sep 17 00:00:00 2001 From: Chung Nguyen Date: Fri, 5 Oct 2018 18:32:56 +0700 Subject: [PATCH 0428/5615] Fix ROADMAP link in CONTRIBUTING.md (#901) --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index db3a45bc775..cd3822df3a2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ * Star the project on [Github](https://github.com/testcontainers/testcontainers-java) and help spread the word :) * Join our Slack: http://slack.testcontainers.org -* See [ROADMAP](ROADMAP.md) to understand the approach behind the project and what may/may not be in store for the future. +* See [ROADMAP](docs/ROADMAP.md) to understand the approach behind the project and what may/may not be in store for the future. * [Post an issue](https://github.com/testcontainers/testcontainers-java/issues) if you find any bugs * Contribute improvements or fixes using a [Pull Request](https://github.com/testcontainers/testcontainers-java/pulls). If you're going to contribute, thank you! Please just be sure to: * discuss with the authors on an issue ticket prior to doing anything big From 16326e2e2495798eaca10a8f889c43ae6d57d4ba Mon Sep 17 00:00:00 2001 From: Frederik Hahne Date: Wed, 10 Oct 2018 11:52:02 +0200 Subject: [PATCH 0429/5615] Add property to start ryuk in privileged mode (#894) Closes #871 --- .../java/org/testcontainers/utility/ResourceReaper.java | 1 + .../testcontainers/utility/TestcontainersConfiguration.java | 4 ++++ docs/usage/properties.md | 6 ++++++ 3 files changed, 11 insertions(+) diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index e8b3a5565a3..717ba2635a8 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -79,6 +79,7 @@ public static String start(String hostIpAddress, DockerClient client) { .withName("testcontainers-ryuk-" + DockerClientFactory.SESSION_ID) .withLabels(Collections.singletonMap(DockerClientFactory.TESTCONTAINERS_LABEL, "true")) .withBinds(binds) + .withPrivileged(TestcontainersConfiguration.getInstance().isRyukPrivileged()) .exec() .getId(); diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 448e4184e55..1e1155053b6 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -46,6 +46,10 @@ public String getTinyImage() { return (String) properties.getOrDefault("tinyimage.container.image", "alpine:3.5"); } + public boolean isRyukPrivileged() { + return Boolean.parseBoolean((String) properties.getOrDefault("ryuk.container.privileged", "false")); + } + public String getRyukImage() { return (String) properties.getOrDefault("ryuk.container.image", "quay.io/testcontainers/ryuk:0.2.2"); } diff --git a/docs/usage/properties.md b/docs/usage/properties.md index bfe9233dcb5..2b968c81f5a 100644 --- a/docs/usage/properties.md +++ b/docs/usage/properties.md @@ -37,5 +37,11 @@ It takes a couple of seconds, but if you want to speed up your tests, you can di > **kafka.container.image = confluentinc/cp-kafka** > Used by KafkaContainer +## Customizing ryuk resource reaper +> **ryuk.container.image = quay.io/testcontainers/ryuk:0.2.2** +> The resource reaper is responsible for container removal and automatic cleanup of dead containers at JVM shutdown +> **ryuk.container.privileged = false** +> In some environments ryuk must be started in privileged mode to work properly (--privileged flag) + Testcontainers uses public Docker images to perform different actions like startup checks, VNC recording and others. Some companies disallow the usage of Docker Hub, but you can override `*.image` properties with your own images from your private registry to workaround that. From 21284b4ba40668e02263eca3a6a98926bb526128 Mon Sep 17 00:00:00 2001 From: Artjom Kalita Date: Mon, 15 Oct 2018 10:36:10 +0300 Subject: [PATCH 0430/5615] Add description for JUnit 5 usage (#911) --- docs/SUMMARY.md | 1 + docs/usage.md | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 18c068814c5..317afd570db 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -8,6 +8,7 @@ * [Usage modes](usage.md#usage-modes) * [Maven dependencies](usage.md#maven-dependencies) * [Logging](usage.md#logging) + * [JUnit](usage.md#junit) * [Properties](usage/properties.md) * [Running inside Docker](usage/inside_docker.md) diff --git a/docs/usage.md b/docs/usage.md index 5460c97164c..35c58cdba65 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -119,3 +119,34 @@ ambassador.container.image=replacement image name here vncrecorder.container.image=replacement image name here tinyimage.container.image=replacement image name here ``` + +## JUnit + +### Junit 4 + +**JUnit4 @Rule/@ClassRule**: This mode starts container before your tests and tears it down afterwards. + +Add a @Rule or @ClassRule to your test class, e.g.: + +```java +public class SimpleMySQLTest { + @Rule + public MySQLContainer mysql = new MySQLContainer(); +``` + +### JUnit 5 + +**JUnit5**: You need to manually start container in **@BeforeAll/@BeforeEach** annotated method in test (on JVM exit tear down will be done automatically ). + +Start container in method annotated @BeforeAll/@BeforeEach: + +```java +public class SimpleMySQLTest { + public MySQLContainer mysql = new MySQLContainer(); + @BeforeEach + void before() { + mysql.start(); + } + +``` + From 3e94b65eb25ae320268a2b73864a6bd288e587d4 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Mon, 15 Oct 2018 09:57:49 +0200 Subject: [PATCH 0431/5615] Update JUnit5 docs (#917) --- docs/usage.md | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index 35c58cdba65..23fc443006f 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -124,29 +124,40 @@ tinyimage.container.image=replacement image name here ### Junit 4 -**JUnit4 @Rule/@ClassRule**: This mode starts container before your tests and tears it down afterwards. +**JUnit4 `@Rule`/`@ClassRule`**: This mode starts the container before your tests and tears it down afterwards. -Add a @Rule or @ClassRule to your test class, e.g.: +Add a `@Rule` or `@ClassRule` annotated field to your test class, e.g.: ```java public class SimpleMySQLTest { @Rule public MySQLContainer mysql = new MySQLContainer(); + + // [...] +} ``` ### JUnit 5 -**JUnit5**: You need to manually start container in **@BeforeAll/@BeforeEach** annotated method in test (on JVM exit tear down will be done automatically ). +**JUnit5**: As of now, you need to manually start the container in a `@BeforeAll`/`@BeforeEach` annotated method in your tests. Tear down will be done automatically on JVM exit, but you can of course also use an `@AfterAll`/`@AfterEach` annotated method to manually call the `close()` method on your container. -Start container in method annotated @BeforeAll/@BeforeEach: +Example of starting a container in a `@BeforeEach` annotated method: ```java public class SimpleMySQLTest { public MySQLContainer mysql = new MySQLContainer(); + @BeforeEach void before() { mysql.start(); - } - + } + + @AfterEach + void after() { + mysql.stop(); + } + + // [...] +} ``` From 3126c548315501fef3479646e0024d1fd65f8a2b Mon Sep 17 00:00:00 2001 From: Chung Nguyen Date: Mon, 15 Oct 2018 14:58:46 +0700 Subject: [PATCH 0432/5615] Support empty password for MySQL (#899) --- .../junit/EmptyPasswordMysqlTest.java | 46 +++++++++++++++++++ .../testcontainers/junit/SimpleMySQLTest.java | 35 ++++++++++---- .../containers/MySQLContainer.java | 14 ++++-- 3 files changed, 84 insertions(+), 11 deletions(-) create mode 100644 modules/jdbc-test/src/test/java/org/testcontainers/junit/EmptyPasswordMysqlTest.java diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/EmptyPasswordMysqlTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/EmptyPasswordMysqlTest.java new file mode 100644 index 00000000000..0dfc3149ef5 --- /dev/null +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/EmptyPasswordMysqlTest.java @@ -0,0 +1,46 @@ +package org.testcontainers.junit; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import org.junit.Rule; +import org.junit.Test; +import org.testcontainers.containers.MySQLContainer; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; + +public class EmptyPasswordMysqlTest { + private static final String DB_NAME = "foo"; + private static final String USER = "root"; // MySQL allows empty password with root only + + // Add MYSQL_ROOT_HOST environment so that we can root login from anywhere for testing purposes + @Rule + public MySQLContainer mysql = (MySQLContainer) new MySQLContainer("mysql:5.5") + .withDatabaseName(DB_NAME) + .withUsername(USER) + .withPassword("") + .withEnv("MYSQL_ROOT_HOST", "%"); + + @Test + public void testSimple() throws SQLException { + HikariConfig hikariConfig = new HikariConfig(); + hikariConfig.setJdbcUrl("jdbc:mysql://" + + mysql.getContainerIpAddress() + + ":" + mysql.getMappedPort(MySQLContainer.MYSQL_PORT) + + "/" + DB_NAME); + hikariConfig.setUsername(USER); + hikariConfig.setPassword(""); + + HikariDataSource ds = new HikariDataSource(hikariConfig); + Statement statement = ds.getConnection().createStatement(); + statement.execute("SELECT 1"); + ResultSet resultSet = statement.getResultSet(); + + assertEquals("There is a result", resultSet.next(), true); + int resultSetInt = resultSet.getInt(1); + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + } +} diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java index 572921d8a1e..4c3731fcaf9 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java @@ -1,22 +1,26 @@ package org.testcontainers.junit; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeFalse; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; -import lombok.NonNull; + import org.apache.commons.lang.SystemUtils; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.testcontainers.containers.ContainerLaunchException; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.containers.output.Slf4jLogConsumer; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -import static org.junit.Assume.assumeFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import lombok.NonNull; /** @@ -128,6 +132,21 @@ public void testMySQL8() throws SQLException { } } + @Test + public void testEmptyPasswordWithNonRootUser() { + + MySQLContainer container = (MySQLContainer) new MySQLContainer("mysql:5.5").withDatabaseName("TEST") + .withUsername("test").withPassword("").withEnv("MYSQL_ROOT_HOST", "%"); + + try { + container.start(); + fail("ContainerLaunchException expected to be thrown"); + } catch (ContainerLaunchException e) { + } finally { + container.stop(); + } + } + @NonNull protected ResultSet performQuery(MySQLContainer containerRule, String sql) throws SQLException { HikariConfig hikariConfig = new HikariConfig(); diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java index 2dbc303407f..6d9139fa971 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java @@ -19,6 +19,7 @@ public class MySQLContainer> extends JdbcDatab private String databaseName = "test"; private String username = "test"; private String password = "test"; + private static final String MYSQL_ROOT_USER = "root"; public MySQLContainer() { super(IMAGE + ":" + DEFAULT_TAG); @@ -36,13 +37,20 @@ protected Set getLivenessCheckPorts() { @Override protected void configure() { - optionallyMapResourceParameterAsVolume(MY_CNF_CONFIG_OVERRIDE_PARAM_NAME, "/etc/mysql/conf.d", "mysql-default-conf"); + optionallyMapResourceParameterAsVolume(MY_CNF_CONFIG_OVERRIDE_PARAM_NAME, "/etc/mysql/conf.d", + "mysql-default-conf"); addExposedPort(MYSQL_PORT); addEnv("MYSQL_DATABASE", databaseName); addEnv("MYSQL_USER", username); - addEnv("MYSQL_PASSWORD", password); - addEnv("MYSQL_ROOT_PASSWORD", password); + if (password != null && !password.isEmpty()) { + addEnv("MYSQL_PASSWORD", password); + addEnv("MYSQL_ROOT_PASSWORD", password); + } else if (MYSQL_ROOT_USER.equalsIgnoreCase(username)) { + addEnv("MYSQL_ALLOW_EMPTY_PASSWORD", "yes"); + } else { + throw new ContainerLaunchException("Empty password can be used only with the root user"); + } setStartupAttempts(3); } From b1c9547896840f46885b0c05dcb1ee2c045a6fca Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Mon, 15 Oct 2018 10:01:50 +0200 Subject: [PATCH 0433/5615] Fix indentation in docs --- docs/usage.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index 23fc443006f..649e0515d73 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -149,12 +149,12 @@ public class SimpleMySQLTest { @BeforeEach void before() { - mysql.start(); + mysql.start(); } @AfterEach void after() { - mysql.stop(); + mysql.stop(); } // [...] From 3ee82e93bcbf8584b873469cc49652d1dbc7dec3 Mon Sep 17 00:00:00 2001 From: Tim Riemer Date: Mon, 15 Oct 2018 11:47:57 +0200 Subject: [PATCH 0434/5615] Remove public modifier from JUnit5 example (#918) --- docs/usage.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index 649e0515d73..1df0d8c8585 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -144,8 +144,8 @@ public class SimpleMySQLTest { Example of starting a container in a `@BeforeEach` annotated method: ```java -public class SimpleMySQLTest { - public MySQLContainer mysql = new MySQLContainer(); +class SimpleMySQLTest { + private MySQLContainer mysql = new MySQLContainer(); @BeforeEach void before() { From f9ab71309e593ad0fd3ddf11f40eb1d4dddda426 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Sat, 27 Oct 2018 22:01:48 +0200 Subject: [PATCH 0435/5615] Fix examples in Couchbase README.md (#935) See #934 --- modules/couchbase/README.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/modules/couchbase/README.md b/modules/couchbase/README.md index 7a18a235eb4..357a21468a3 100644 --- a/modules/couchbase/README.md +++ b/modules/couchbase/README.md @@ -1,7 +1,7 @@ -# TestContainers Couchbase Module -Testcontainers module for Couchbase. [Couchbase](https://www.couchbase.com/) is a Document oriented NoSQL database. +# Testcontainers Couchbase Module +Testcontainers module for Couchbase. [Couchbase](https://www.couchbase.com/) is a document oriented NoSQL database. ## Usage example @@ -14,18 +14,22 @@ public class SomeTest { @Rule public CouchbaseContainer couchbase = new CouchbaseContainer() + .withClusterAdmin("admin", "secret") .withNewBucket(DefaultBucketSettings.builder() .enableFlush(true) - .name('bucket-name') + .name("bucket-name") + .password("secret") .quota(100) .type(BucketType.COUCHBASE) .build()); @Test public void someTestMethod() { - Bucket bucket = couchbase.getCouchbaseCluster().openBucket('bucket-name') + Bucket bucket = couchbase.getCouchbaseCluster().openBucket("bucket-name"); - ... interact with client as if using Couchbase normally + // ... interact with client as if using Couchbase normally + } +} ``` ### Use preconfigured default bucket @@ -39,7 +43,9 @@ public class SomeTest extends AbstractCouchbaseTest { public void someTestMethod() { Bucket bucket = getBucket(); - ... interact with client as if using Couchbase normally + // ... interact with client as if using Couchbase normally + } +} ``` ### Special consideration From e541ee3ad3724f4e24e44662eb0d72baf3bc65d9 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 27 Oct 2018 21:31:46 +0100 Subject: [PATCH 0436/5615] Add configuration for probot-stale bot (#907) * Add configuration for probot-stale bot * Update stale.yml --- .github/stale.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/stale.yml diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 00000000000..a071ce18a5f --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,23 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 90 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 14 +# Issues with these labels will never be considered stale +exemptLabels: + - pinned + - security + - "resolution/acknowledged" +# Label to use when marking an issue as stale +staleLabel: stale +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. If you believe + this is a mistake, please reply to this comment to keep it open. If there isn't + one already, a PR to fix or at least reproduce the problem in a test case will + always help us get back on track to tackle this. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: > + This issue has been automatically closed due to inactivity. We apologise if + this is still an active problem for you, and would ask you to re-open the + issue if this is the case. From 5af0a751cf1afbc1037aa956aeb2a5c9756898ca Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 27 Oct 2018 21:32:55 +0100 Subject: [PATCH 0437/5615] Remove old docs (#890) * Update deprecated 'docs' files * Remove Vagrantfile, as it's well out of date * Update RELEASING.md to reflect current truth --- AUTHORS | 15 +++------------ README.md | 2 +- RELEASING.md | 5 +++-- Vagrantfile | 18 ------------------ docs/ROADMAP.md | 13 +------------ 5 files changed, 8 insertions(+), 45 deletions(-) delete mode 100644 Vagrantfile diff --git a/AUTHORS b/AUTHORS index 26ab8313e53..b5423dc9519 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,12 +1,3 @@ -Richard North -Gurpreet Sohal -Alex Boldt -Robert Požarickij -Matthieu Baechler -Krystian Nowak -Viktor Schulz -Asaf Mesika -Sergei Egorov -Pete Cornish -Miguel Gonzalez Sanchez -Julien LAMY +This file is deprecated. Please see the [contributors](https://github.com/testcontainers/testcontainers-java/graphs/contributors) listing. + +[Richard North](https://github.com/rnorth), [Sergei Egorov](https://github.com/bsideup) and [Kevin Wittek](https://github.com/kiview) are the core maintainers. diff --git a/README.md b/README.md index 3734cd2e1ae..8f18b9edd6d 100644 --- a/README.md +++ b/README.md @@ -20,4 +20,4 @@ MS SQL Server module is (c) 2017 - 2018 G DATA Software AG and other authors. Hashicorp Vault module is (c) 2017 Capital One Services, LLC and other authors. -See [AUTHORS](AUTHORS) for all contributors. +See [contributors](https://github.com/testcontainers/testcontainers-java/graphs/contributors) for all contributors. diff --git a/RELEASING.md b/RELEASING.md index c479bc5ef54..aaba172d20e 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -8,8 +8,9 @@ Testcontainers' release process is automated as a Travis deployment. This descri 1. Ensure that the [`CHANGELOG`](CHANGELOG.md) file is up to date and includes all merged features. 1. Create a new release on GitHub. **The tag name is used as the version**, so please keep the tag name plain (e.g. 1.2.3). 1. Check that the Travis build passed. -1. Release of published artifacts is fully automated. Once the Travis build completes, there are no further actions to perform on the repository. -1. When available through Maven Central, poke [@whichrich](https://twitter.com/whichrich) to announce the release on Twitter! +1. Release of published artifacts to Bintray is fully automated. +1. After successful publication to Bintray, the artifacts must be synced to Maven Central. +1. When available through Maven Central, poke [Richard North](https://github.com/rnorth) to announce the release on Twitter! ## Internal details diff --git a/Vagrantfile b/Vagrantfile deleted file mode 100644 index a31b6dd114d..00000000000 --- a/Vagrantfile +++ /dev/null @@ -1,18 +0,0 @@ -# -*- mode: ruby -*- -# vi: set ft=ruby : - -Vagrant.configure(2) do |config| - - config.vm.box = "box-cutter/ubuntu1504-docker" - - config.vm.network "private_network", ip: "192.168.33.10" - - config.vm.provider "virtualbox" do |vb| - vb.memory = "2048" - end - - config.vm.provision "shell", inline: <<-SHELL - sudo apt-get update - sudo apt-get install -y openjdk-8-jdk maven - SHELL -end diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index e315796a9d1..a144db23c65 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -10,16 +10,5 @@ Exact future features aren't set in stone, but here are the principles we should * In line with the above, we should aim for feature completeness soon. * Similarly, it should leverage good things that already exist (like reliable public Docker images) and be composable with other tools. * That said, don't lock us in to more third-party components than absolutely necessary, and only use well maintained dependencies. -* It must be possible for a developer to use _or build_ this library with just minimal standard tools (Java JDK, Maven), and a local Docker environment. Do not use any non-public dependencies in the core lib or standard modules. (see the `proprietary-deps` maven profile for how we're dealing with the Oracle XE module that has a test-time dependency on the properitary Oracle JDBC driver JAR) +* It must be possible for a developer to use _or build_ this library with just minimal standard tools (Java JDK, Maven), and a local Docker environment. Do not use any non-public dependencies in the core lib or standard modules. -## Future goals/anti-goals - -This list may cross over with the [issues list](https://github.com/testcontainers/testcontainers-java/issues) some times. - -* **Better documentation and log messages**. We need this. -* **Database state snapshotting**. Some projects have complex test data that is time consuming to reload. This means that either testing takes a very long time (test data has to be reloaded often), or tests aren't sufficiently independent (test data is not reloaded and therefore tests aren't independent). Testcontainers should provide a way to snapshot DB state and quickly reload that state. Docker [native Checkpoint/Restore In Userspace](http://blog.kubernetes.io/2015/07/how-did-quake-demo-from-dockercon-work.html) looks like the best way to accomplish this. -* **Support docker images pulled from private registries** -* **Support integration testing in other languages/environments**. .NET and NodeJS seem like sensible options here. -* **Allow a JUnit @Rule to point to a local `Dockerfile` rather than an imagename**. Perhaps. -* **Don't enter the build tools space** -* **Don't become involved in live run-time environments**. This is a tool for testing. Some features could technically be misused in a live environment, but this isn't something we should want to support. \ No newline at end of file From eaa21aa2c9686b381711d93cb693fad769e71066 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Sun, 28 Oct 2018 16:53:11 +0100 Subject: [PATCH 0438/5615] Add WARN log if provided Couchbase password offends default policy (#936) Add WARN log if provided Couchbase password doesn't meet requirements See #934 --- .../testcontainers/couchbase/CouchbaseContainer.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index f463d81279e..ad6fe3d18a9 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -67,6 +67,8 @@ public class CouchbaseContainer extends GenericContainer { public static final String STATIC_CONFIG = "/opt/couchbase/etc/couchbase/static_config"; public static final String CAPI_CONFIG = "/opt/couchbase/etc/couchdb/default.d/capi.ini"; + private static final int REQUIRED_DEFAULT_PASSWORD_LENGTH = 6; + private String memoryQuota = "300"; private String indexMemoryQuota = "300"; @@ -118,6 +120,14 @@ public Set getLivenessCheckPortNumbers() { return Sets.newHashSet(getMappedPort(REST)); } + @Override + protected void configure() { + if (clusterPassword.length() < REQUIRED_DEFAULT_PASSWORD_LENGTH) { + logger().warn("The provided cluster admin password length is less then the default password policy length. " + + "Cluster start will fail if configured password requirements are not met."); + } + } + @Override @SneakyThrows protected void doStart() { From b1b37fecfda40e300c6d580d56ed42450aeb3734 Mon Sep 17 00:00:00 2001 From: Nick Babcock Date: Fri, 2 Nov 2018 03:50:21 -0500 Subject: [PATCH 0439/5615] Update commons-compress to 1.18 (#943) --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index bda81410355..a4f4d0d71af 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -77,7 +77,7 @@ dependencies { compile 'org.jetbrains:annotations:15.0' compile 'javax.annotation:javax.annotation-api:1.3.1' compile 'com.google.code.findbugs:jsr305:3.0.2' - compile 'org.apache.commons:commons-compress:1.16.1' + compile 'org.apache.commons:commons-compress:1.18' // Added for JDK9 compatibility since it's missing this package compile 'javax.xml.bind:jaxb-api:2.3.0' compile ('org.rnorth.duct-tape:duct-tape:1.0.7') { From d7c4708cacc0ceaf593f98ba7a3c4484de834970 Mon Sep 17 00:00:00 2001 From: Benedikt Ritter Date: Fri, 2 Nov 2018 09:54:59 +0100 Subject: [PATCH 0440/5615] Add JUnit Jupiter (JUnit 5) extension (#887) --- modules/junit-jupiter/README.md | 132 ++++++++++++++++++ modules/junit-jupiter/build.gradle | 22 +++ .../junit/jupiter/Container.java | 17 +++ .../junit/jupiter/Testcontainers.java | 57 ++++++++ .../jupiter/TestcontainersExtension.java | 125 +++++++++++++++++ .../junit/jupiter/ComposeContainerTests.java | 42 ++++++ .../junit/jupiter/PostgresContainerTests.java | 40 ++++++ ...ntainersNestedRestartedContainerTests.java | 69 +++++++++ ...tcontainersNestedSharedContainerTests.java | 38 +++++ .../TestcontainersRestartBetweenTests.java | 35 +++++ .../TestcontainersSharedContainerTests.java | 35 +++++ .../jupiter/inheritance/AbstractTestBase.java | 17 +++ .../jupiter/inheritance/InheritedTests.java | 28 ++++ .../jupiter/inheritance/RedisContainer.java | 20 +++ .../src/test/resources/docker-compose.yml | 4 + .../src/test/resources/logback-test.xml | 15 ++ 16 files changed, 696 insertions(+) create mode 100644 modules/junit-jupiter/README.md create mode 100644 modules/junit-jupiter/build.gradle create mode 100644 modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Container.java create mode 100644 modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Testcontainers.java create mode 100644 modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java create mode 100644 modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/ComposeContainerTests.java create mode 100644 modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/PostgresContainerTests.java create mode 100644 modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java create mode 100644 modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedSharedContainerTests.java create mode 100644 modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersRestartBetweenTests.java create mode 100644 modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersSharedContainerTests.java create mode 100644 modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/AbstractTestBase.java create mode 100644 modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/InheritedTests.java create mode 100644 modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/RedisContainer.java create mode 100644 modules/junit-jupiter/src/test/resources/docker-compose.yml create mode 100644 modules/junit-jupiter/src/test/resources/logback-test.xml diff --git a/modules/junit-jupiter/README.md b/modules/junit-jupiter/README.md new file mode 100644 index 00000000000..9395f08501f --- /dev/null +++ b/modules/junit-jupiter/README.md @@ -0,0 +1,132 @@ +# TestContainers JUnit Jupiter module + +While Testcontainers is tightly coupled with the JUnit 4.x rule API, this module provides +an API that is based on the [JUnit Jupiter](https://junit.org/junit5/) extension model. + +The extension supports two modes: + +- containers that are restarted for every test method +- containers that are shared between all methods of a test class + +## Examples + +To use the Testcontainers extension annotate your test class with `@Testcontainers`. + +### Restarted containers + +To define a restarted container, define an instance field inside your test class and annotate it with +the `@Container` annotation. + +```java +@Testcontainers +class SomeTest { + + @Container + private MySQLContainer mySQLContainer = new MySQLContainer(); + + @Test + void someTestMethod() { + String url = mySQLContainer.getJdbcUrl(); + + // create a connection and run test as normal + } + + @Nested + class NestedTests { + + @Container + private final PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer(); + + void nestedTestMethod() { + // top level container is restarted for nested methods + String mySqlUrl = mySQLContainer.getJdbcUrl(); + + // nested containers are only available inside their nested class + String postgresUrl = postgreSQLContainer.getJdbcUrl(); + } + } +} +``` + +### Shared containers + +Shared containers are defined as static fields in a top level test class and have to be annotated with `@Container`. +Note that shared containers can't be declared inside nested test classes. +This is because nested test classes have to be defined non-static and can't therefore have static fields. + +```java +@Testcontainers +class SomeTest { + + @Container + private static final MySQLContainer MY_SQL_CONTAINER = new MySQLContainer(); + + @Test + void someTestMethod() { + String url = MY_SQL_CONTAINER.getJdbcUrl(); + + // create a connection and run test as normal + } +} +``` + +### Singleton containers + +Sometimes it might be useful to define a container that is only started once for several test classes. +There is no special support for this use case provided by the Testcontainers extension. +Instead this can be implemented using the following pattern: + +```java +abstract class AbstractContainerBaseTest { + + static final MySQLContainer MY_SQL_CONTAINER; + + static { + MY_SQL_CONTAINER = new MySQLContainer(); + MY_SQL_CONTAINER.start(); + } +} + +class FirstTest extends AbstractContainerBaseTest { + + @Test + void someTestMethod() { + String url = MY_SQL_CONTAINER.getJdbcUrl(); + + // create a connection and run test as normal + } +} +``` + +The singleton container is started only once when the base class is loaded. +The container can then be used by all inheriting test classes. +At the end of the test suite the [Ryuk container](https://github.com/testcontainers/moby-ryuk) +that is started by Testcontainers core will take care of stopping the singleton container. + +## Dependency information + +Replace `VERSION` with the [latest version available on Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.testcontainers%22) or use the Testcontainers BOM. + +### Maven + +``` + + org.testcontainers + junit-jupiter + VERSION + +``` + +### Gradle + +``` +compile group: 'org.testcontainers', name: 'junit-jupiter', version: 'VERSION' +``` + +## Limitations + +Since this module has a dependency onto JUnit Jupiter and on Testcontainers core, which +has a dependency onto JUnit 4.x, projects using this module will end up with both, JUnit Jupiter +and JUnit 4.x in the test classpath. + +This extension has only be tested with sequential test execution. Using it with parallel test execution is unsupported and may have unintended side effects. diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle new file mode 100644 index 00000000000..8429a0e9514 --- /dev/null +++ b/modules/junit-jupiter/build.gradle @@ -0,0 +1,22 @@ +description = "Testcontainers :: JUnit Jupiter Extension" + +dependencies { + compile project(':testcontainers') + compile 'org.junit.jupiter:junit-jupiter-api:5.3.1' + + testCompile project(':mysql') + testCompile project(':postgresql') + testCompile 'com.zaxxer:HikariCP:2.6.1' + testCompile 'redis.clients:jedis:2.8.0' + + testRuntime 'org.postgresql:postgresql:42.0.0' + testRuntime 'mysql:mysql-connector-java:6.0.6' + testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.3.1' +} + +test { + useJUnitPlatform() + testLogging { + events "passed", "skipped", "failed" + } +} diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Container.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Container.java new file mode 100644 index 00000000000..a075786d0d5 --- /dev/null +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Container.java @@ -0,0 +1,17 @@ +package org.testcontainers.junit.jupiter; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The {@code @Container} annotation is used in conjunction with the {@link Testcontainers} annotation + * to mark containers that should be managed by the Testcontainers extension. + * + * @see Testcontainers + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Container { +} diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Testcontainers.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Testcontainers.java new file mode 100644 index 00000000000..e71bac3b1a2 --- /dev/null +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Testcontainers.java @@ -0,0 +1,57 @@ +package org.testcontainers.junit.jupiter; + +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; + +/** + * {@code @Testcontainers} is a JUnit Jupiter extension to activate automatic + * startup and stop of containers used in a test case. + * + *

The test containers extension finds all fields of type + * {@link org.testcontainers.lifecycle.Startable} that are annotated with + * {@link Container} and calls their container lifecycle methods. Containers + * declared as static fields will be shared between test methods. They will be + * started only once before any test method is executed and stopped after the + * last test method has executed. Containers declared as instance fields will + * be started and stopped for every test method.

+ * + *

Note: This extension has only be tested with sequential + * test execution. Using it with parallel test execution is unsupported and + * may have unintended side effects.

+ * + *

Example:

+ * + *
+ * @Testcontainers
+ * class MyTestcontainersTests {
+ *
+ *     // will be shared between test methods
+ *     @Container
+ *     private static final MySQLContainer MY_SQL_CONTAINER = new MySQLContainer();
+ *
+ *     // will be started before and stopped after each test method
+ *     @Container
+ *     private PostgreSQLContainer postgresqlContainer = new PostgreSQLContainer()
+ *             .withDatabaseName("foo")
+ *             .withUsername("foo")
+ *             .withPassword("secret");
+ *
+ *     @Test
+ *     void test() {
+ *         assertTrue(MY_SQL_CONTAINER.isRunning());
+ *         assertTrue(postgresqlContainer.isRunning());
+ *     }
+ * }
+ * 
+ * + * @see Container + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@ExtendWith(TestcontainersExtension.class) +public @interface Testcontainers { +} diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java new file mode 100644 index 00000000000..77ee95904cd --- /dev/null +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java @@ -0,0 +1,125 @@ +package org.testcontainers.junit.jupiter; + +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionConfigurationException; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ExtensionContext.Namespace; +import org.junit.jupiter.api.extension.ExtensionContext.Store.CloseableResource; +import org.junit.jupiter.api.extension.TestInstancePostProcessor; +import org.junit.platform.commons.support.AnnotationSupport; +import org.junit.platform.commons.util.Preconditions; +import org.junit.platform.commons.util.ReflectionUtils; +import org.testcontainers.lifecycle.Startable; + +import java.lang.reflect.Field; +import java.util.LinkedHashSet; +import java.util.Optional; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Stream; + +class TestcontainersExtension implements TestInstancePostProcessor, BeforeEachCallback { + + private static final Namespace NAMESPACE = Namespace.create(TestcontainersExtension.class); + + private static final String TEST_INSTANCE = "testInstance"; + + @Override + public void postProcessTestInstance(final Object testInstance, final ExtensionContext context) { + ExtensionContext.Store store = context.getStore(NAMESPACE); + store.put(TEST_INSTANCE, testInstance); + + findSharedContainers(testInstance) + .forEach(adapter -> store.getOrComputeIfAbsent(adapter.key, k -> adapter.start())); + } + + @Override + public void beforeEach(final ExtensionContext context) { + collectParentTestInstances(context) + .parallelStream() + .flatMap(this::findRestartContainers) + .forEach(adapter -> context.getStore(NAMESPACE) + .getOrComputeIfAbsent(adapter.key, k -> adapter.start())); + } + + private Set collectParentTestInstances(final ExtensionContext context) { + Set testInstances = new LinkedHashSet<>(); + Optional current = Optional.of(context); + while(current.isPresent()) { + ExtensionContext ctx = current.get(); + Object testInstance = ctx.getStore(NAMESPACE).remove(TEST_INSTANCE); + if (testInstance != null) { + testInstances.add(testInstance); + } + current = ctx.getParent(); + } + return testInstances; + } + + private Stream findSharedContainers(Object testInstance) { + return ReflectionUtils.findFields( + testInstance.getClass(), + isSharedContainer(), + ReflectionUtils.HierarchyTraversalMode.TOP_DOWN) + .stream() + .map(f -> getContainerInstance(testInstance, f)); + } + + private Predicate isSharedContainer() { + return isContainer().and(ReflectionUtils::isStatic); + } + + private Stream findRestartContainers(Object testInstance) { + return ReflectionUtils.findFields( + testInstance.getClass(), + isRestartContainer(), + ReflectionUtils.HierarchyTraversalMode.TOP_DOWN) + .stream() + .map(f -> getContainerInstance(testInstance, f)); + } + + private Predicate isRestartContainer() { + return isContainer().and(ReflectionUtils::isNotStatic); + } + + private static Predicate isContainer() { + return field -> Startable.class.isAssignableFrom(field.getType()) && AnnotationSupport.isAnnotated(field, Container.class); + } + + private static StoreAdapter getContainerInstance(final Object testInstance, final Field field) { + try { + field.setAccessible(true); + Startable containerInstance = Preconditions.notNull((Startable) field.get(testInstance), "Container " + field.getName() + " needs to be initialized"); + return new StoreAdapter(field.getDeclaringClass(), field.getName(), containerInstance); + } catch (IllegalAccessException e) { + throw new ExtensionConfigurationException("Can not access container defined in field " + field.getName()); + } + } + + /** + * An adapter for {@link Startable} that implement {@link CloseableResource} + * thereby letting the JUnit automatically stop containers once the current + * {@link ExtensionContext} is closed. + */ + private static class StoreAdapter implements CloseableResource { + + private String key; + + private Startable container; + + private StoreAdapter(Class declaringClass, String fieldName, Startable container) { + this.key = declaringClass.getName() + "." + fieldName; + this.container = container; + } + + private StoreAdapter start() { + container.start(); + return this; + } + + @Override + public void close() { + container.stop(); + } + } +} diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/ComposeContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/ComposeContainerTests.java new file mode 100644 index 00000000000..11b9027b392 --- /dev/null +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/ComposeContainerTests.java @@ -0,0 +1,42 @@ +package org.testcontainers.junit.jupiter; + +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.HttpClientBuilder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.DockerComposeContainer; +import org.testcontainers.containers.wait.strategy.Wait; + +import java.io.File; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@Testcontainers +class ComposeContainerTests { + + @Container + private DockerComposeContainer composeContainer = new DockerComposeContainer( + new File("src/test/resources/docker-compose.yml")) + .withExposedService("whoami_1", 80, Wait.forHttp("/")); + + private String host; + + private int port; + + @BeforeEach + void setup() { + host = composeContainer.getServiceHost("whoami_1", 80); + port = composeContainer.getServicePort("whoami_1", 80); + } + + @Test + void running_compose_defined_container_is_accessible_on_configured_port() throws Exception { + HttpClient client = HttpClientBuilder.create().build(); + + HttpResponse response = client.execute(new HttpGet("http://" + host + ":" + port)); + + assertEquals(200, response.getStatusLine().getStatusCode()); + } +} diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/PostgresContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/PostgresContainerTests.java new file mode 100644 index 00000000000..765a9f6ff6b --- /dev/null +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/PostgresContainerTests.java @@ -0,0 +1,40 @@ +package org.testcontainers.junit.jupiter; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.PostgreSQLContainer; + +import java.sql.ResultSet; +import java.sql.Statement; + +import static org.junit.Assert.assertEquals; + +@Testcontainers +class PostgresContainerTests { + + @Container + private static final PostgreSQLContainer POSTGRE_SQL_CONTAINER = new PostgreSQLContainer() + .withDatabaseName("foo") + .withUsername("foo") + .withPassword("secret"); + + @Test + void waits_until_postgres_accepts_jdbc_connections() throws Exception { + HikariConfig hikariConfig = new HikariConfig(); + hikariConfig.setJdbcUrl(POSTGRE_SQL_CONTAINER.getJdbcUrl()); + hikariConfig.setUsername("foo"); + hikariConfig.setPassword("secret"); + + try (HikariDataSource ds = new HikariDataSource(hikariConfig)) { + Statement statement = ds.getConnection().createStatement(); + statement.execute("SELECT 1"); + ResultSet resultSet = statement.getResultSet(); + resultSet.next(); + + int resultSetInt = resultSet.getInt(1); + assertEquals(1, resultSetInt); + } + } + +} diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java new file mode 100644 index 00000000000..4d27c410979 --- /dev/null +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java @@ -0,0 +1,69 @@ +package org.testcontainers.junit.jupiter; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.GenericContainer; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@Testcontainers +class TestcontainersNestedRestartedContainerTests { + + @Container + private final GenericContainer topLevelContainer = new GenericContainer("httpd:2.4-alpine") + .withExposedPorts(80); + + private static String topLevelContainerId; + + private static String nestedContainerId; + + @Test + void top_level_container_should_be_running() { + assertTrue(topLevelContainer.isRunning()); + topLevelContainerId = topLevelContainer.getContainerId(); + } + + @Nested + class NestedTestCase { + + @Container + private final GenericContainer nestedContainer = new GenericContainer("httpd:2.4-alpine") + .withExposedPorts(80); + + @Test + void both_containers_should_be_running() { + assertTrue(topLevelContainer.isRunning()); + assertTrue(nestedContainer.isRunning()); + + if (nestedContainerId == null) { + nestedContainerId = nestedContainer.getContainerId(); + } else { + assertNotEquals(nestedContainerId, nestedContainer.getContainerId()); + } + } + + @Test + void containers_should_not_be_the_same() { + assertNotEquals(topLevelContainer.getContainerId(), nestedContainer.getContainerId()); + + if (nestedContainerId == null) { + nestedContainerId = nestedContainer.getContainerId(); + } else { + assertNotEquals(nestedContainerId, nestedContainer.getContainerId()); + } + } + + @Test + void ids_should_not_change() { + assertNotEquals(topLevelContainerId, topLevelContainer.getContainerId()); + + if (nestedContainerId == null) { + nestedContainerId = nestedContainer.getContainerId(); + } else { + assertNotEquals(nestedContainerId, nestedContainer.getContainerId()); + } + } + } +} diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedSharedContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedSharedContainerTests.java new file mode 100644 index 00000000000..371a5bae178 --- /dev/null +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedSharedContainerTests.java @@ -0,0 +1,38 @@ +package org.testcontainers.junit.jupiter; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.GenericContainer; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@Testcontainers +class TestcontainersNestedSharedContainerTests { + + @Container + private static final GenericContainer TOP_LEVEL_CONTAINER = new GenericContainer("httpd:2.4-alpine") + .withExposedPorts(80); + + private static String topLevelContainerId; + + @Test + void top_level_container_should_be_running() { + assertTrue(TOP_LEVEL_CONTAINER.isRunning()); + topLevelContainerId = TOP_LEVEL_CONTAINER.getContainerId(); + } + + @Nested + class NestedTestCase { + + @Test + void top_level_containers_should_be_running() { + assertTrue(TOP_LEVEL_CONTAINER.isRunning()); + } + + @Test + void ids_should_not_change() { + assertEquals(topLevelContainerId, TOP_LEVEL_CONTAINER.getContainerId()); + } + } +} diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersRestartBetweenTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersRestartBetweenTests.java new file mode 100644 index 00000000000..2d6959ae065 --- /dev/null +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersRestartBetweenTests.java @@ -0,0 +1,35 @@ +package org.testcontainers.junit.jupiter; + +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.GenericContainer; + +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +@Testcontainers +class TestcontainersRestartBetweenTests { + + @Container + private GenericContainer genericContainer = new GenericContainer("httpd:2.4-alpine") + .withExposedPorts(80); + + private static String lastContainerId; + + @Test + void first_test() { + if (lastContainerId == null) { + lastContainerId = genericContainer.getContainerId(); + } else { + assertNotEquals(lastContainerId, genericContainer.getContainerId()); + } + } + + @Test + void second_test() { + if (lastContainerId == null) { + lastContainerId = genericContainer.getContainerId(); + } else { + assertNotEquals(lastContainerId, genericContainer.getContainerId()); + } + } + +} diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersSharedContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersSharedContainerTests.java new file mode 100644 index 00000000000..8c6859b1022 --- /dev/null +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersSharedContainerTests.java @@ -0,0 +1,35 @@ +package org.testcontainers.junit.jupiter; + +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.GenericContainer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@Testcontainers +class TestcontainersSharedContainerTests { + + @Container + private static final GenericContainer GENERIC_CONTAINER = new GenericContainer("httpd:2.4-alpine") + .withExposedPorts(80); + + private static String lastContainerId; + + @Test + void first_test() { + if (lastContainerId == null) { + lastContainerId = GENERIC_CONTAINER.getContainerId(); + } else { + assertEquals(lastContainerId, GENERIC_CONTAINER.getContainerId()); + } + } + + @Test + void second_test() { + if (lastContainerId == null) { + lastContainerId = GENERIC_CONTAINER.getContainerId(); + } else { + assertEquals(lastContainerId, GENERIC_CONTAINER.getContainerId()); + } + } + +} diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/AbstractTestBase.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/AbstractTestBase.java new file mode 100644 index 00000000000..930f16f035d --- /dev/null +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/AbstractTestBase.java @@ -0,0 +1,17 @@ +package org.testcontainers.junit.jupiter.inheritance; + +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.util.concurrent.atomic.AtomicLong; + +@Testcontainers +abstract class AbstractTestBase { + + @Container + static RedisContainer redisPerClass = new RedisContainer(); + + @Container + RedisContainer redisPerTest = new RedisContainer(); + +} diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/InheritedTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/InheritedTests.java new file mode 100644 index 00000000000..37e535d25e7 --- /dev/null +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/InheritedTests.java @@ -0,0 +1,28 @@ +package org.testcontainers.junit.jupiter.inheritance; + +import org.junit.jupiter.api.Test; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@Testcontainers +class InheritedTests extends AbstractTestBase { + + @Container + private RedisContainer myRedis = new RedisContainer(); + + @Test + void step1() { + assertEquals(1, redisPerClass.getJedis().incr("key").longValue()); + assertEquals(1, redisPerTest.getJedis().incr("key").longValue()); + assertEquals(1, myRedis.getJedis().incr("key").longValue()); + } + + @Test + void step2() { + assertEquals(2, redisPerClass.getJedis().incr("key").longValue()); + assertEquals(1, redisPerTest.getJedis().incr("key").longValue()); + assertEquals(1, myRedis.getJedis().incr("key").longValue()); + } +} diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/RedisContainer.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/RedisContainer.java new file mode 100644 index 00000000000..bae3885527d --- /dev/null +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/RedisContainer.java @@ -0,0 +1,20 @@ +package org.testcontainers.junit.jupiter.inheritance; + +import org.testcontainers.containers.GenericContainer; +import redis.clients.jedis.Jedis; + +public class RedisContainer extends GenericContainer { + + public RedisContainer() { + this("redis:3.2.11"); + } + + public RedisContainer(String dockerImageName) { + super(dockerImageName); + withExposedPorts(6379); + } + + public Jedis getJedis() { + return new Jedis(getContainerIpAddress(), getMappedPort(6379)); + } +} diff --git a/modules/junit-jupiter/src/test/resources/docker-compose.yml b/modules/junit-jupiter/src/test/resources/docker-compose.yml new file mode 100644 index 00000000000..ec1015f106a --- /dev/null +++ b/modules/junit-jupiter/src/test/resources/docker-compose.yml @@ -0,0 +1,4 @@ +version: '2' +services: + whoami: + image: emilevauge/whoami diff --git a/modules/junit-jupiter/src/test/resources/logback-test.xml b/modules/junit-jupiter/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..1d1a7695915 --- /dev/null +++ b/modules/junit-jupiter/src/test/resources/logback-test.xml @@ -0,0 +1,15 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + \ No newline at end of file From 46b6865b258f67ec4a3f24a7d9bf0777d27e3329 Mon Sep 17 00:00:00 2001 From: David Pilato Date: Sun, 4 Nov 2018 16:32:31 +0100 Subject: [PATCH 0441/5615] Add Elasticsearch module (#826) This change moves the Elasticsearch Testcontainers project from https://github.com/dadoonet/testcontainers-java-module-elasticsearch to the testcontainers-java project so it will benefit from automatic releases, closer integration and will get more exposure. --- docs/SUMMARY.md | 4 +- docs/usage.md | 27 +++-- docs/usage/elasticsearch_container.md | 43 ++++++++ modules/elasticsearch/build.gradle | 6 ++ .../elasticsearch/ElasticsearchContainer.java | 62 +++++++++++ .../ElasticsearchContainerTest.java | 100 ++++++++++++++++++ .../src/test/resources/logback-test.xml | 21 ++++ 7 files changed, 255 insertions(+), 8 deletions(-) create mode 100644 docs/usage/elasticsearch_container.md create mode 100644 modules/elasticsearch/build.gradle create mode 100644 modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java create mode 100644 modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java create mode 100644 modules/elasticsearch/src/test/resources/logback-test.xml diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 317afd570db..673a5edd4d5 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -6,7 +6,7 @@ * [Prerequisites](usage.md#prerequisites) * [Docker environment discovery](usage.md#docker-environment-discovery) * [Usage modes](usage.md#usage-modes) - * [Maven dependencies](usage.md#maven-dependencies) + * [Gradle/Maven dependencies](usage.md#gradlemaven-dependencies) * [Logging](usage.md#logging) * [JUnit](usage.md#junit) * [Properties](usage/properties.md) @@ -38,6 +38,8 @@ * [JDBC URL](usage/database_containers.md#jdbc-url) * [Using an init script](usage/database_containers.md#using-an-init-script) +* [Elasticsearch containers](usage/elasticsearch_container.md) + * [Selenium WebDriver containers](usage/webdriver_containers.md) * [Benefits](usage/webdriver_containers.md#benefits) * [Example](usage/webdriver_containers.md#example) diff --git a/docs/usage.md b/docs/usage.md index 1df0d8c8585..92f5405ca15 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -24,28 +24,41 @@ Testcontainers will try to connect to a Docker daemon using the following strate ### Usage modes * [Temporary database containers](usage/database_containers.md) - specialized Microsoft SQL Server, MariaDB, MySQL, PostgreSQL, Oracle XE and Virtuoso container support +* [Elasticsearch container](usage/elasticsearch_container.md) - Elasticsearch container support * [Webdriver containers](usage/webdriver_containers.md) - run a dockerized Chrome or Firefox browser ready for Selenium/Webdriver operations - complete with automatic video recording * [Kafka containers](usage/kafka_containers.md) - run a dockerized Kafka, a distributed streaming platform * [Generic containers](usage/generic_containers.md) - run any Docker container as a test dependency * [Docker compose](usage/docker_compose.md) - reuse services defined in a Docker Compose YAML file * [Dockerfile containers](usage/dockerfile.md) - run a container that is built on-the-fly from a Dockerfile -## Maven dependencies +## Gradle/Maven dependencies -Testcontainers is distributed in a handful of Maven modules: +Testcontainers is distributed in a handful of Gradle/Maven modules: * **testcontainers** for just core functionality, generic containers and docker-compose support * **mysql**, **postgresql** or **oracle-xe** for database container support +* **elasticsearch** for elasticsearch container support * **selenium** for selenium/webdriver support * **nginx** for nginx container support In the dependency description below, replace `--artifact name--` as appropriate and `--latest version--` with the [latest version available on Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.testcontainers%22): - - org.testcontainers - --artifact name-- - --latest version-- - +Maven style: + +```xml + + org.testcontainers + --artifact name-- + --latest version-- + +``` + +Gradle style: + +``` +compile group: 'org.testcontainers', name: '--artifact name--', version: '--latest version--' +``` + ### JitPack (unreleased versions) diff --git a/docs/usage/elasticsearch_container.md b/docs/usage/elasticsearch_container.md new file mode 100644 index 00000000000..56bb45e64ac --- /dev/null +++ b/docs/usage/elasticsearch_container.md @@ -0,0 +1,43 @@ +# Elasticsearch container + +This module helps running [elasticsearch](https://www.elastic.co/products/elasticsearch) using +Testcontainers. + +Note that it's based on the [official Docker image](https://www.elastic.co/guide/en/elasticsearch/reference/6.3/docker.html) provided by elastic. + +## Usage example + +You can start an elasticsearch container instance from any Java application by using: + +```java +// Create the elasticsearch container. +ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:6.4.1"); + +// Start the container. This step might take some time... +container.start(); + +// Do whatever you want here. +final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); +credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("elastic", "changeme")); +RestClient client = RestClient.builder(container.getHost()) + .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)) + .build(); +Response response = client.performRequest("GET", "/"); + +// Stop the container. +container.stop(); +``` + +Note that if you are still using the [TransportClient](https://www.elastic.co/guide/en/elasticsearch/client/java-api/6.3/transport-client.html) +(not recommended as deprecated), the default cluster name is set to `docker-cluster` so you need to change `cluster.name` setting +or set `client.transport.ignore_cluster_name` to `true`. + +## Choose your Elasticsearch license + +If you prefer to start a Docker image with the pure OSS version (which means with no security or +other advanced features), you can use this instead: + +```java +// Create the elasticsearch container. +ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch-oss:6.4.1"); +``` diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle new file mode 100644 index 00000000000..f64fc8d4e56 --- /dev/null +++ b/modules/elasticsearch/build.gradle @@ -0,0 +1,6 @@ +description = "TestContainers :: elasticsearch" + +dependencies { + compile project(':testcontainers') + testCompile "org.elasticsearch.client:elasticsearch-rest-client:6.4.1" +} diff --git a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java new file mode 100644 index 00000000000..2bf7f7ebd53 --- /dev/null +++ b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java @@ -0,0 +1,62 @@ +package org.testcontainers.elasticsearch; + +import org.apache.http.HttpHost; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; +import org.testcontainers.utility.Base58; + +import java.time.Duration; + +import static java.net.HttpURLConnection.HTTP_OK; +import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED; + +/** + * Represents an elasticsearch docker instance which exposes by default port 9200 and 9300 (transport.tcp.port) + * The docker image is by default fetched from docker.elastic.co/elasticsearch/elasticsearch + */ +public class ElasticsearchContainer extends GenericContainer { + + /** + * Elasticsearch Default HTTP port + */ + private static final int ELASTICSEARCH_DEFAULT_PORT = 9200; + + /** + * Elasticsearch Default Transport port + */ + private static final int ELASTICSEARCH_DEFAULT_TCP_PORT = 9300; + + /** + * Elasticsearch Docker base URL + */ + private static final String ELASTICSEARCH_DEFAULT_IMAGE = "docker.elastic.co/elasticsearch/elasticsearch"; + + /** + * Elasticsearch Default version + */ + protected static final String ELASTICSEARCH_DEFAULT_VERSION = "6.4.1"; + + public ElasticsearchContainer() { + this(ELASTICSEARCH_DEFAULT_IMAGE + ":" + ELASTICSEARCH_DEFAULT_VERSION); + } + + /** + * Create an Elasticsearch Container by passing the full docker image name + * @param dockerImageName Full docker image name, like: docker.elastic.co/elasticsearch/elasticsearch:6.4.1 + */ + public ElasticsearchContainer(String dockerImageName) { + super(dockerImageName); + logger().info("Starting an elasticsearch container using [{}]", dockerImageName); + withNetworkAliases("elasticsearch-" + Base58.randomString(6)); + withEnv("discovery.type", "single-node"); + addExposedPorts(ELASTICSEARCH_DEFAULT_PORT, ELASTICSEARCH_DEFAULT_TCP_PORT); + setWaitStrategy(new HttpWaitStrategy() + .forPort(ELASTICSEARCH_DEFAULT_PORT) + .forStatusCodeMatching(response -> response == HTTP_OK || response == HTTP_UNAUTHORIZED) + .withStartupTimeout(Duration.ofMinutes(2))); + } + + public HttpHost getHost() { + return new HttpHost(getContainerIpAddress(), getMappedPort(ELASTICSEARCH_DEFAULT_PORT)); + } +} diff --git a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java new file mode 100644 index 00000000000..ac00aaf25ba --- /dev/null +++ b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java @@ -0,0 +1,100 @@ +package org.testcontainers.elasticsearch; + + +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.util.EntityUtils; +import org.elasticsearch.client.Request; +import org.elasticsearch.client.Response; +import org.elasticsearch.client.ResponseException; +import org.elasticsearch.client.RestClient; +import org.junit.After; +import org.junit.Test; + +import java.io.IOException; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; +import static org.testcontainers.elasticsearch.ElasticsearchContainer.ELASTICSEARCH_DEFAULT_VERSION; + +public class ElasticsearchContainerTest { + + /** + * Elasticsearch default username, when secured with a license > basic + */ + private static final String ELASTICSEARCH_USERNAME = "elastic"; + + /** + * Elasticsearch 5.x default password. In 6.x images, there's no security by default as shipped with a basic license. + */ + private static final String ELASTICSEARCH_PASSWORD = "changeme"; + + private RestClient client = null; + + @After + public void stopRestClient() throws IOException { + if (client != null) { + client.close(); + client = null; + } + } + + @Test + public void elasticsearchDefaultTest() throws IOException { + try (ElasticsearchContainer container = new ElasticsearchContainer()){ + container.start(); + Response response = getClient(container).performRequest(new Request("GET", "/")); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + assertThat(EntityUtils.toString(response.getEntity()), containsString(ELASTICSEARCH_DEFAULT_VERSION)); + + // The default image is running with the features under Elastic License + response = getClient(container).performRequest(new Request("GET", "/_xpack/")); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + // For now we test that we have the monitoring feature available + assertThat(EntityUtils.toString(response.getEntity()), containsString("monitoring")); + } + } + + @Test + public void elasticsearchVersion() throws IOException { + try (ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:5.6.12")) { + container.start(); + Response response = getClient(container).performRequest(new Request("GET", "/")); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + String responseAsString = EntityUtils.toString(response.getEntity()); + assertThat(responseAsString, containsString("5.6.12")); + } + } + + @Test + public void elasticsearchOssImage() throws IOException { + try (ElasticsearchContainer container = + new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch-oss:" + ELASTICSEARCH_DEFAULT_VERSION)) { + container.start(); + Response response = getClient(container).performRequest(new Request("GET", "/")); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + // The OSS image does not have any feature under Elastic License + assertThrows("We should not have /_xpack endpoint with an OSS License", + ResponseException.class, + () -> getClient(container).performRequest(new Request("GET", "/_xpack/"))); + } + } + + private RestClient getClient(ElasticsearchContainer container) { + if (client == null) { + final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials(AuthScope.ANY, + new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ELASTICSEARCH_PASSWORD)); + + client = RestClient.builder(container.getHost()) + .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)) + .build(); + } + + return client; + } +} diff --git a/modules/elasticsearch/src/test/resources/logback-test.xml b/modules/elasticsearch/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..52e9ef70225 --- /dev/null +++ b/modules/elasticsearch/src/test/resources/logback-test.xml @@ -0,0 +1,21 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + + + PROFILER + DENY + + From 4abfc4f644a4c0fecf347abd795e87d04fe39ba7 Mon Sep 17 00:00:00 2001 From: haphut Date: Sun, 4 Nov 2018 22:06:42 +0200 Subject: [PATCH 0442/5615] Update Pulsar to 2.2.0 (#941) --- modules/pulsar/build.gradle | 2 +- .../java/org/testcontainers/containers/PulsarContainer.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 1bccc7460f1..4f6b0f3288f 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Pulsar" dependencies { compile project(':testcontainers') - testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.1.1-incubating' + testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.2.0' testCompile group: 'org.assertj', name: 'assertj-core', version: '3.10.0' } diff --git a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java index 48e6ff029f9..49f0437f95a 100644 --- a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java +++ b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java @@ -12,7 +12,7 @@ public class PulsarContainer extends GenericContainer { public static final int BROKER_HTTP_PORT = 8080; public static final String METRICS_ENDPOINT = "/metrics"; - private static final String PULSAR_VERSION = "2.1.1-incubating"; + private static final String PULSAR_VERSION = "2.2.0"; public PulsarContainer() { this(PULSAR_VERSION); From 78a0f69e47a72ebd88b33968037c204d3f4208a4 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Sun, 4 Nov 2018 21:29:51 +0100 Subject: [PATCH 0443/5615] Small improvements to Jupiter extension code style (#947) * Small improvements to Jupiter extension code style Follow up to #887 * Only raise exception if annotated field is not Startable --- .../jupiter/TestcontainersExtension.java | 21 +++++++++++++++---- .../jupiter/WrongAnnotationUsageTests.java | 20 ++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/WrongAnnotationUsageTests.java diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java index 77ee95904cd..b64d607148d 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java @@ -1,5 +1,6 @@ package org.testcontainers.junit.jupiter; +import lombok.Getter; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionConfigurationException; import org.junit.jupiter.api.extension.ExtensionContext; @@ -30,7 +31,7 @@ public void postProcessTestInstance(final Object testInstance, final ExtensionCo store.put(TEST_INSTANCE, testInstance); findSharedContainers(testInstance) - .forEach(adapter -> store.getOrComputeIfAbsent(adapter.key, k -> adapter.start())); + .forEach(adapter -> store.getOrComputeIfAbsent(adapter.getKey(), k -> adapter.start())); } @Override @@ -39,13 +40,13 @@ public void beforeEach(final ExtensionContext context) { .parallelStream() .flatMap(this::findRestartContainers) .forEach(adapter -> context.getStore(NAMESPACE) - .getOrComputeIfAbsent(adapter.key, k -> adapter.start())); + .getOrComputeIfAbsent(adapter.getKey(), k -> adapter.start())); } private Set collectParentTestInstances(final ExtensionContext context) { Set testInstances = new LinkedHashSet<>(); Optional current = Optional.of(context); - while(current.isPresent()) { + while (current.isPresent()) { ExtensionContext ctx = current.get(); Object testInstance = ctx.getStore(NAMESPACE).remove(TEST_INSTANCE); if (testInstance != null) { @@ -83,7 +84,18 @@ private Predicate isRestartContainer() { } private static Predicate isContainer() { - return field -> Startable.class.isAssignableFrom(field.getType()) && AnnotationSupport.isAnnotated(field, Container.class); + return field -> { + boolean isAnnotatedWithContainer = AnnotationSupport.isAnnotated(field, Container.class); + if (isAnnotatedWithContainer) { + boolean isStartable = Startable.class.isAssignableFrom(field.getType()); + + if (!isStartable) { + throw new ExtensionConfigurationException("Annotation is only supported for Startable types"); + } + return true; + } + return false; + }; } private static StoreAdapter getContainerInstance(final Object testInstance, final Field field) { @@ -103,6 +115,7 @@ private static StoreAdapter getContainerInstance(final Object testInstance, fina */ private static class StoreAdapter implements CloseableResource { + @Getter private String key; private Startable container; diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/WrongAnnotationUsageTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/WrongAnnotationUsageTests.java new file mode 100644 index 00000000000..bec99894307 --- /dev/null +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/WrongAnnotationUsageTests.java @@ -0,0 +1,20 @@ +package org.testcontainers.junit.jupiter; + + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + + +@Disabled +@Testcontainers +class WrongAnnotationUsageTests { + + @Container + private String notStartable = "foobar"; + + @Test + void extension_throws_exception() { + assert true; + } + +} From 043e90a6f3ff45354512401eae86435e9f1572d7 Mon Sep 17 00:00:00 2001 From: David Byron Date: Mon, 5 Nov 2018 06:40:20 -0800 Subject: [PATCH 0444/5615] Remove com.google.code.findbugs:jsr305 dependency and use org.jetbrains.annotations.Nullable (#920) Closes #912 --- core/build.gradle | 1 - .../dockerclient/transport/okhttp/OkHttpInvocationBuilder.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index a4f4d0d71af..a39f0ef4e2a 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -76,7 +76,6 @@ dependencies { compile 'org.slf4j:slf4j-ext:1.7.25' compile 'org.jetbrains:annotations:15.0' compile 'javax.annotation:javax.annotation-api:1.3.1' - compile 'com.google.code.findbugs:jsr305:3.0.2' compile 'org.apache.commons:commons-compress:1.18' // Added for JDK9 compatibility since it's missing this package compile 'javax.xml.bind:jaxb-api:2.3.0' diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java index ee35cde2ce8..e701c16fefa 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java @@ -31,9 +31,9 @@ import okio.BufferedSource; import okio.Okio; import okio.Source; +import org.jetbrains.annotations.Nullable; import org.testcontainers.DockerClientFactory; -import javax.annotation.Nullable; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; From e2e8a2abd5ebb18cfcf5ea6d80ede45c45a77f6d Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Mon, 5 Nov 2018 16:33:42 +0100 Subject: [PATCH 0445/5615] Mention more jdbc modules in the docs Fixes #597 --- docs/usage.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/usage.md b/docs/usage.md index 92f5405ca15..ddee88234db 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -36,10 +36,11 @@ Testcontainers will try to connect to a Docker daemon using the following strate Testcontainers is distributed in a handful of Gradle/Maven modules: * **testcontainers** for just core functionality, generic containers and docker-compose support -* **mysql**, **postgresql** or **oracle-xe** for database container support +* **mysql**, **mariadb**, **postgresql**, **mssqlserver** or **oracle-xe** for database container support * **elasticsearch** for elasticsearch container support * **selenium** for selenium/webdriver support * **nginx** for nginx container support +* and many more! In the dependency description below, replace `--artifact name--` as appropriate and `--latest version--` with the [latest version available on Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.testcontainers%22): From 8f4554f5f945cd5213016f551cc553d6740887ce Mon Sep 17 00:00:00 2001 From: Eugeny Karpov Date: Mon, 5 Nov 2018 23:06:58 +0300 Subject: [PATCH 0446/5615] Support init script at JdbcDatabaseContainer level (#575) Currently init script can be applied only through ContainerDatabaseDriver which doesn't support private Docker registries. This limitation also denies generic container approach of usage. More information about the problem is in the corresponding issue: https://github.com/testcontainers/testcontainers-java/issues/532 So I've implemented an init script support for all Jdbc containers. Resolve #532 --- .../org/testcontainers/ext/ScriptUtils.java | 28 +++++++++++++ .../testcontainers/junit/SimpleMySQLTest.java | 14 +++++++ .../junit/SimplePostgreSQLTest.java | 41 ++++++++++++++----- .../resources/somepath/init_postgresql.sql | 5 +++ .../containers/JdbcDatabaseContainer.java | 28 +++++++++++++ 5 files changed, 105 insertions(+), 11 deletions(-) create mode 100644 modules/jdbc-test/src/test/resources/somepath/init_postgresql.sql diff --git a/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java b/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java index 7d89517f200..73c49c0947c 100644 --- a/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java +++ b/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java @@ -16,12 +16,16 @@ package org.testcontainers.ext; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testcontainers.delegate.DatabaseDelegate; import javax.script.ScriptException; +import java.io.IOException; +import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.LinkedList; import java.util.List; @@ -210,6 +214,30 @@ public static boolean containsSqlScriptDelimiters(String script, String delim) { return false; } + /** + * Load script from classpath and apply it to the given database + * + * @param databaseDelegate database delegate for script execution + * @param initScriptPath the resource to load the init script from + */ + public static void runInitScript(DatabaseDelegate databaseDelegate, String initScriptPath) { + try { + URL resource = ScriptUtils.class.getClassLoader().getResource(initScriptPath); + if (resource == null) { + LOGGER.warn("Could not load classpath init script: {}", initScriptPath); + throw new ScriptLoadException("Could not load classpath init script: " + initScriptPath + ". Resource not found."); + } + String scripts = IOUtils.toString(resource, StandardCharsets.UTF_8); + executeDatabaseScript(databaseDelegate, initScriptPath, scripts); + } catch (IOException e) { + LOGGER.warn("Could not load classpath init script: {}", initScriptPath); + throw new ScriptLoadException("Could not load classpath init script: " + initScriptPath, e); + } catch (ScriptException e) { + LOGGER.error("Error while executing init script: {}", initScriptPath, e); + throw new UncategorizedScriptException("Error while executing init script: " + initScriptPath, e); + } + } + public static void executeDatabaseScript(DatabaseDelegate databaseDelegate, String scriptPath, String script) throws ScriptException { executeDatabaseScript(databaseDelegate, scriptPath, script, false, false, DEFAULT_COMMENT_PREFIX, DEFAULT_STATEMENT_SEPARATOR, DEFAULT_BLOCK_COMMENT_START_DELIMITER, DEFAULT_BLOCK_COMMENT_END_DELIMITER); } diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java index 4c3731fcaf9..5c8e4133a22 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java @@ -132,6 +132,20 @@ public void testMySQL8() throws SQLException { } } + @Test + public void testExplicitInitScript() throws SQLException { + try (MySQLContainer container = (MySQLContainer) new MySQLContainer() + .withInitScript("somepath/init_mysql.sql") + .withLogConsumer(new Slf4jLogConsumer(logger))) { + container.start(); + + ResultSet resultSet = performQuery(container, "SELECT foo FROM bar"); + String firstColumnValue = resultSet.getString(1); + + assertEquals("Value from init script should equal real value", "hello world", firstColumnValue); + } + } + @Test public void testEmptyPasswordWithNonRootUser() { diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimplePostgreSQLTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimplePostgreSQLTest.java index 942d8f1c1ba..2581240cd84 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimplePostgreSQLTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimplePostgreSQLTest.java @@ -2,8 +2,8 @@ import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; -import org.junit.Rule; import org.junit.Test; +import org.testcontainers.containers.JdbcDatabaseContainer; import org.testcontainers.containers.PostgreSQLContainer; import java.sql.ResultSet; @@ -17,23 +17,42 @@ */ public class SimplePostgreSQLTest { - @Rule - public PostgreSQLContainer postgres = new PostgreSQLContainer(); - @Test public void testSimple() throws SQLException { + try (PostgreSQLContainer postgres = new PostgreSQLContainer<>()) { + postgres.start(); + + ResultSet resultSet = performQuery(postgres, "SELECT 1"); + + int resultSetInt = resultSet.getInt(1); + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + } + } + + @Test + public void testExplicitInitScript() throws SQLException { + try (PostgreSQLContainer postgres = new PostgreSQLContainer<>() + .withInitScript("somepath/init_postgresql.sql")) { + postgres.start(); + + ResultSet resultSet = performQuery(postgres, "SELECT foo FROM bar"); + + String firstColumnValue = resultSet.getString(1); + assertEquals("Value from init script should equal real value", "hello world", firstColumnValue); + } + } + + private ResultSet performQuery(JdbcDatabaseContainer container, String sql) throws SQLException { HikariConfig hikariConfig = new HikariConfig(); - hikariConfig.setJdbcUrl(postgres.getJdbcUrl()); - hikariConfig.setUsername(postgres.getUsername()); - hikariConfig.setPassword(postgres.getPassword()); + hikariConfig.setJdbcUrl(container.getJdbcUrl()); + hikariConfig.setUsername(container.getUsername()); + hikariConfig.setPassword(container.getPassword()); HikariDataSource ds = new HikariDataSource(hikariConfig); Statement statement = ds.getConnection().createStatement(); - statement.execute("SELECT 1"); + statement.execute(sql); ResultSet resultSet = statement.getResultSet(); - resultSet.next(); - int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + return resultSet; } } diff --git a/modules/jdbc-test/src/test/resources/somepath/init_postgresql.sql b/modules/jdbc-test/src/test/resources/somepath/init_postgresql.sql new file mode 100644 index 00000000000..2b00ee968b0 --- /dev/null +++ b/modules/jdbc-test/src/test/resources/somepath/init_postgresql.sql @@ -0,0 +1,5 @@ +CREATE TABLE bar ( + foo VARCHAR(255) +); + +INSERT INTO bar (foo) VALUES ('hello world'); \ No newline at end of file diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index c43b74105dc..e480070168f 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -1,11 +1,15 @@ package org.testcontainers.containers; import lombok.NonNull; +import com.github.dockerjava.api.command.InspectContainerResponse; import org.jetbrains.annotations.NotNull; import org.rnorth.ducttape.ratelimits.RateLimiter; import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; import org.rnorth.ducttape.unreliables.Unreliables; import org.testcontainers.containers.traits.LinkableContainer; +import org.testcontainers.delegate.DatabaseDelegate; +import org.testcontainers.ext.ScriptUtils; +import org.testcontainers.jdbc.JdbcDatabaseDelegate; import org.testcontainers.utility.MountableFile; import java.sql.Connection; @@ -26,6 +30,7 @@ public abstract class JdbcDatabaseContainer parameters = new HashMap<>(); private static final RateLimiter DB_CONNECT_RATE_LIMIT = RateLimiterBuilder.newBuilder() @@ -111,6 +116,11 @@ public SELF withConnectTimeoutSeconds(int connectTimeoutSeconds) { return self(); } + public SELF withInitScript(String initScriptPath) { + this.initScriptPath = initScriptPath; + return self(); + } + @Override protected void waitUntilContainerStarted() { // Repeatedly try and open a connection to the DB and execute a test query @@ -135,6 +145,11 @@ protected void waitUntilContainerStarted() { }); } + @Override + protected void containerIsStarted(InspectContainerResponse containerInfo) { + runInitScriptIfRequired(); + } + /** * Obtain an instance of the correct JDBC driver for this particular database container type * @@ -202,6 +217,15 @@ protected void optionallyMapResourceParameterAsVolume(@NotNull String paramName, } } + /** + * Load init script content and apply it to the database if initScriptPath is set + */ + protected void runInitScriptIfRequired() { + if (initScriptPath != null) { + ScriptUtils.runInitScript(getDatabaseDelegate(), initScriptPath); + } + } + public void setParameters(Map parameters) { this.parameters = parameters; } @@ -228,4 +252,8 @@ protected int getStartupTimeoutSeconds() { protected int getConnectTimeoutSeconds() { return connectTimeoutSeconds; } + + protected DatabaseDelegate getDatabaseDelegate() { + return new JdbcDatabaseDelegate(this, ""); + } } From 0b04261a9ecae22fed534e27175dd0870030ce28 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Mon, 5 Nov 2018 21:07:57 +0100 Subject: [PATCH 0447/5615] Add JUnit5/Jupiter usage instructions to docs (#954) --- docs/usage.md | 43 +++++++++++++++++-- .../junit/jupiter/Testcontainers.java | 3 +- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index ddee88234db..2cb5fbd661a 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -151,11 +151,48 @@ public class SimpleMySQLTest { } ``` -### JUnit 5 +### Jupiter / JUnit 5 + +#### Extension + +Jupiter integration is provided by means of the `@Testcontainers` annotation. + +The extension finds all fields that are annotated with `@Container` and calls their container lifecycle +methods (methods on the `Startable` interface). Containers declared as static fields will be shared between test +methods. They will be started only once before any test method is executed and stopped after the last test method has +executed. Containers declared as instance fields will be started and stopped for every test method. + +**Note:** This extension has only be tested with sequential test execution. Using it with parallel test execution is +unsupported and may have unintended side effects. + +*Example:* +```java +@Testcontainers +class MyTestcontainersTests { + + // will be shared between test methods + @Container + private static final MySQLContainer MY_SQL_CONTAINER = new MySQLContainer(); + + // will be started before and stopped after each test method + @Container + private PostgreSQLContainer postgresqlContainer = new PostgreSQLContainer() + .withDatabaseName("foo") + .withUsername("foo") + .withPassword("secret"); + @Test + void test() { + assertTrue(MY_SQL_CONTAINER.isRunning()); + assertTrue(postgresqlContainer.isRunning()); + } +} +``` + +#### Vanilla Integration -**JUnit5**: As of now, you need to manually start the container in a `@BeforeAll`/`@BeforeEach` annotated method in your tests. Tear down will be done automatically on JVM exit, but you can of course also use an `@AfterAll`/`@AfterEach` annotated method to manually call the `close()` method on your container. +As an alternative, you can manually start the container in a `@BeforeAll`/`@BeforeEach` annotated method in your tests. Tear down will be done automatically on JVM exit, but you can of course also use an `@AfterAll`/`@AfterEach` annotated method to manually call the `close()` method on your container. -Example of starting a container in a `@BeforeEach` annotated method: +*Example of starting a container in a `@BeforeEach` annotated method:* ```java class SimpleMySQLTest { diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Testcontainers.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Testcontainers.java index e71bac3b1a2..ecf59ff1399 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Testcontainers.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Testcontainers.java @@ -11,8 +11,7 @@ * {@code @Testcontainers} is a JUnit Jupiter extension to activate automatic * startup and stop of containers used in a test case. * - *

The test containers extension finds all fields of type - * {@link org.testcontainers.lifecycle.Startable} that are annotated with + *

The test containers extension finds all fields that are annotated with * {@link Container} and calls their container lifecycle methods. Containers * declared as static fields will be shared between test methods. They will be * started only once before any test method is executed and stopped after the From 0bf68e9a44160bc61962fa16cb9c2284422cab46 Mon Sep 17 00:00:00 2001 From: Benedikt Ritter Date: Mon, 5 Nov 2018 21:18:20 +0100 Subject: [PATCH 0448/5615] Activate gradle build scans for CI builds (#916) As discussed [via twitter](https://twitter.com/Kiview/status/1050713692541734913) this PR will activate build scans for CI builds. For an example see [this scan](https://scans.gradle.com/s/i4grzev4man3y) of a local build on my machine. I had to add some configuration to `build.gralde` for this: - The build scan plugin is applied first. Otherwise it will [not collect all data for Gradle < 4.3](https://docs.gradle.com/build-scan-plugin/#for_individual_builds). - The terms of service are accepted via the corresponding configuration in `build.gradle`. Every build that is called with the `--scan` option will be published. @melix [suggested](https://twitter.com/CedricChampeau/status/1050717559006392320) to add a [more sophisticated configuration](https://github.com/apache/groovy/blob/master/gradle/build-scans.gradle), that will also publish all local builds if user accept it. For now I've only added the simple configuration. Furthermore I've added the `--scan` option to all travis stages. --- .travis.yml | 14 +++++++------- build.gradle | 6 ++++++ circle.yml | 10 +++++----- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index a5cfcc0f3eb..6b0e158343a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,24 +7,24 @@ services: - docker install: - - ./gradlew build -x check + - ./gradlew build -x check --scan jobs: include: - stage: test env: [ NAME=core ] - script: ./gradlew testcontainers:check javadoc + script: ./gradlew testcontainers:check javadoc --scan - stage: test env: [ NAME=core ] jdk: openjdk11 - script: ./gradlew testcontainers:check + script: ./gradlew testcontainers:check --scan - env: [ NAME=selenium ] - script: ./gradlew selenium:check + script: ./gradlew selenium:check --scan - env: [ NAME=modules ] - script: ./gradlew check -x testcontainers:check -x selenium:check + script: ./gradlew check -x testcontainers:check -x selenium:check --scan # Run Docker-in-Docker tests inside Alpine - env: [ NAME="docker-in-alpine-docker" ] @@ -36,7 +36,7 @@ jobs: -v "$(pwd)":"$(pwd)" \ -w "$(pwd)" \ openjdk:8-jdk-alpine \ - ./gradlew --no-daemon testcontainers:test --tests '*GenericContainerRuleTest' + ./gradlew --no-daemon testcontainers:test --tests '*GenericContainerRuleTest' --scan - stage: deploy sudo: false @@ -45,7 +45,7 @@ jobs: script: skip deploy: provider: script - script: ./gradlew -Pversion=$TRAVIS_TAG release + script: ./gradlew -Pversion=$TRAVIS_TAG release --scan on: tags: true branch: master diff --git a/build.gradle b/build.gradle index 4a725afb9df..875b4346744 100644 --- a/build.gradle +++ b/build.gradle @@ -1,4 +1,5 @@ plugins { + id 'com.gradle.build-scan' version '1.16' id 'io.franzbecker.gradle-lombok' version '1.14' id 'nebula.provided-base' version '3.0.3' id 'com.github.johnrengelman.shadow' version '2.0.2' @@ -94,3 +95,8 @@ subprojects { testCompile 'ch.qos.logback:logback-classic:1.2.3' } } + +buildScan { + termsOfServiceUrl = 'https://gradle.com/terms-of-service' + termsOfServiceAgree = 'yes' +} diff --git a/circle.yml b/circle.yml index 690d3d9da80..4129d133c61 100644 --- a/circle.yml +++ b/circle.yml @@ -5,7 +5,7 @@ jobs: steps: - checkout - run: - command: ./gradlew testcontainers:check + command: ./gradlew testcontainers:check --scan - run: name: Save test results command: | @@ -22,7 +22,7 @@ jobs: - run: command: | echo "transport.type=okhttp" >> core/src/test/resources/testcontainers.properties - ./gradlew testcontainers:check + ./gradlew testcontainers:check --scan - run: name: Save test results command: | @@ -35,7 +35,7 @@ jobs: steps: - checkout - run: - command: ./gradlew check -x testcontainers:check -x selenium:check -x jdbc-test:check + command: ./gradlew check -x testcontainers:check -x selenium:check -x jdbc-test:check --scan environment: # Oracle JDBC drivers require a timezone to be set TZ: "/usr/share/zoneinfo/ETC/UTC" @@ -53,7 +53,7 @@ jobs: steps: - checkout - run: - command: ./gradlew jdbc-test:check + command: ./gradlew jdbc-test:check --scan environment: # Oracle JDBC drivers require a timezone to be set TZ: "/usr/share/zoneinfo/ETC/UTC" @@ -71,7 +71,7 @@ jobs: steps: - checkout - run: - command: ./gradlew selenium:check + command: ./gradlew selenium:check --scan - run: name: Save test results command: | From 921726df423b1d532e85cd68571ebc357bad4ffd Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 5 Nov 2018 22:22:42 +0000 Subject: [PATCH 0449/5615] Upgrade bintray gradle plugin Resolves https://github.com/bintray/gradle-bintray-plugin/issues/253 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 875b4346744..b59ba776494 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { id 'io.franzbecker.gradle-lombok' version '1.14' id 'nebula.provided-base' version '3.0.3' id 'com.github.johnrengelman.shadow' version '2.0.2' - id "com.jfrog.bintray" version "1.8.0" apply false + id "com.jfrog.bintray" version "1.8.4" apply false } subprojects { From 524bd10ac4f25512cbd93aea6cbbd4a272a3a105 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 6 Nov 2018 10:26:16 +0000 Subject: [PATCH 0450/5615] Disable shading of Apache HTTP Components/Core libs (#959) Fixes #958 --- build.gradle | 1 - circle.yml | 2 ++ core/build.gradle | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index b59ba776494..602f040c654 100644 --- a/build.gradle +++ b/build.gradle @@ -59,7 +59,6 @@ subprojects { relocate('META-INF/native/libnetty', 'META-INF/native/liborg-testcontainers-shaded-netty') [ - "org.apache.http", "org.apache.commons.lang", "org.apache.commons.io", "org.apache.commons.codec", diff --git a/circle.yml b/circle.yml index 4129d133c61..941b6bff021 100644 --- a/circle.yml +++ b/circle.yml @@ -4,6 +4,8 @@ jobs: core: steps: - checkout + - run: + command: ./gradlew testcontainers:build -x test -x check - run: command: ./gradlew testcontainers:check --scan - run: diff --git a/core/build.gradle b/core/build.gradle index a39f0ef4e2a..755d5bdebb1 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -19,6 +19,7 @@ shadowJar { mergeServiceFiles() exclude 'org/newsclub/**' + exclude 'org/apache/http/**' [ 'META-INF/io.netty.versions.properties', @@ -40,7 +41,6 @@ shadowJar { ].each { exclude(it) } dependencies { - include(dependency('org.apache.httpcomponents:.*')) include(dependency('org.glassfish.*:.*')) include(dependency('org.aopalliance.*:.*')) include(dependency('javax.ws.rs:.*')) From a5ce2449d920d1fd66859c7fc3969b491d7d22c0 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Wed, 7 Nov 2018 09:56:08 +0100 Subject: [PATCH 0451/5615] Publish artifacts to Maven Local before running examples on CircleCI (#903) --- circle.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 941b6bff021..81b77d76355 100644 --- a/circle.yml +++ b/circle.yml @@ -87,8 +87,13 @@ jobs: examples: steps: - checkout + - restore_cache: + key: examples-maven - run: - command: mvn -B -f examples/pom.xml test + name: Publish JARs to Maven Local + command: ./gradlew --no-daemon publishToMavenLocal + - run: + command: mvn -B -f examples/pom.xml -Dtestcontainers.group=org.testcontainers -Dtestcontainers.version=unspecified test - run: name: Save test results command: | @@ -99,6 +104,10 @@ jobs: path: ~/junit - store_artifacts: path: ~/junit + - save_cache: + paths: + - ~/.m2 + key: examples-maven workflows: version: 2 From 53e2657d2c75d06f3295492d3b8e5874dadf8cd0 Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 7 Nov 2018 21:53:19 +0000 Subject: [PATCH 0452/5615] Reshade httphost (#964) FYI @dadoonet, relating to #958 As httpcore is a transitive dependency that we don't want to conflict with user's libs, we unfortunately need to: * roll back #959, where we disabled the shading of this module. As is, we're at risk of internal breakage when transitive deps clash. * in turn, stop exposing `HttpHost` in our public API. We should take a general stance of keeping our public APIs free of transitive deps (especially shaded ones) The API method in ElasticsearchContainer will thus change, from: ```java public HttpHost getHost() ``` to: ```java public String getHttpHostAddress() ``` Usage in turn changes from: ```java RestClient client = RestClient.builder(container.getHost()).build(); ``` to the slightly more verbose but equivalent: ```java RestClient client = RestClient.builder(HttpHost.create(container.getHttpHostAddress())).build(); ``` @bsideup, @kiview and I have discussed at length and concluded that this is the only approach that we're comfortable with - despite the fact that this raises an unfortunate breaking change in the API of the Elasticsearch module, which we reluctantly have to do. We are sorry that we did not catch this particular issue at PR stage - we're going to upgrade our static analysis to guard against inadvertent leakage of shaded dependencies. In the longer term we also, obviously, wish to reduce the weight of our dependency chain, both unshaded and shaded, so that this does not happen again. We intend for this change to go out as 1.10.1. I hope this is OK with you @dadoonet - our apologies again for not catching this before release. --- build.gradle | 1 + core/build.gradle | 2 +- .../testcontainers/elasticsearch/ElasticsearchContainer.java | 5 ++--- .../elasticsearch/ElasticsearchContainerTest.java | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 602f040c654..b59ba776494 100644 --- a/build.gradle +++ b/build.gradle @@ -59,6 +59,7 @@ subprojects { relocate('META-INF/native/libnetty', 'META-INF/native/liborg-testcontainers-shaded-netty') [ + "org.apache.http", "org.apache.commons.lang", "org.apache.commons.io", "org.apache.commons.codec", diff --git a/core/build.gradle b/core/build.gradle index 755d5bdebb1..a39f0ef4e2a 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -19,7 +19,6 @@ shadowJar { mergeServiceFiles() exclude 'org/newsclub/**' - exclude 'org/apache/http/**' [ 'META-INF/io.netty.versions.properties', @@ -41,6 +40,7 @@ shadowJar { ].each { exclude(it) } dependencies { + include(dependency('org.apache.httpcomponents:.*')) include(dependency('org.glassfish.*:.*')) include(dependency('org.aopalliance.*:.*')) include(dependency('javax.ws.rs:.*')) diff --git a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java index 2bf7f7ebd53..1d80fe6499e 100644 --- a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java +++ b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java @@ -1,6 +1,5 @@ package org.testcontainers.elasticsearch; -import org.apache.http.HttpHost; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.utility.Base58; @@ -56,7 +55,7 @@ public ElasticsearchContainer(String dockerImageName) { .withStartupTimeout(Duration.ofMinutes(2))); } - public HttpHost getHost() { - return new HttpHost(getContainerIpAddress(), getMappedPort(ELASTICSEARCH_DEFAULT_PORT)); + public String getHttpHostAddress() { + return getContainerIpAddress() + ":" + getMappedPort(ELASTICSEARCH_DEFAULT_PORT); } } diff --git a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java index ac00aaf25ba..86225c0eac6 100644 --- a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java +++ b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java @@ -1,6 +1,7 @@ package org.testcontainers.elasticsearch; +import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; @@ -90,7 +91,7 @@ private RestClient getClient(ElasticsearchContainer container) { credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ELASTICSEARCH_PASSWORD)); - client = RestClient.builder(container.getHost()) + client = RestClient.builder(HttpHost.create(container.getHttpHostAddress())) .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)) .build(); } From 62b270d0fedbecc72de08e43501b6b05fe07a612 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Wed, 7 Nov 2018 23:30:11 +0100 Subject: [PATCH 0453/5615] Add self-typing generics to ElasticsearchContainer (#963) Fixes #962 --- .../testcontainers/elasticsearch/ElasticsearchContainer.java | 2 +- .../elasticsearch/ElasticsearchContainerTest.java | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java index 1d80fe6499e..e8eff8a341f 100644 --- a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java +++ b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java @@ -13,7 +13,7 @@ * Represents an elasticsearch docker instance which exposes by default port 9200 and 9300 (transport.tcp.port) * The docker image is by default fetched from docker.elastic.co/elasticsearch/elasticsearch */ -public class ElasticsearchContainer extends GenericContainer { +public class ElasticsearchContainer extends GenericContainer { /** * Elasticsearch Default HTTP port diff --git a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java index 86225c0eac6..fefbccc1b90 100644 --- a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java +++ b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java @@ -46,7 +46,9 @@ public void stopRestClient() throws IOException { @Test public void elasticsearchDefaultTest() throws IOException { - try (ElasticsearchContainer container = new ElasticsearchContainer()){ + try (ElasticsearchContainer container = new ElasticsearchContainer() + .withEnv("foo", "bar") // dummy env for compiler checking correct generics usage + ){ container.start(); Response response = getClient(container).performRequest(new Request("GET", "/")); assertThat(response.getStatusLine().getStatusCode(), is(200)); From 25ad069589c9772ac92cf0929cbfafd9f9584fbe Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 8 Nov 2018 07:12:55 +0000 Subject: [PATCH 0454/5615] Update docs for 1.10.1 change to API --- docs/usage/elasticsearch_container.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage/elasticsearch_container.md b/docs/usage/elasticsearch_container.md index 56bb45e64ac..5e02408ab7b 100644 --- a/docs/usage/elasticsearch_container.md +++ b/docs/usage/elasticsearch_container.md @@ -19,7 +19,7 @@ container.start(); // Do whatever you want here. final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("elastic", "changeme")); -RestClient client = RestClient.builder(container.getHost()) +RestClient client = RestClient.builder(HttpHost.create(container.getHttpHostAddress())) .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)) .build(); Response response = client.performRequest("GET", "/"); From 12f7ce5abf1f51026ffc1a22841ff8917d37b7ef Mon Sep 17 00:00:00 2001 From: Benjamin Muschko Date: Fri, 9 Nov 2018 00:10:39 -0700 Subject: [PATCH 0455/5615] Dependencies should apply to the proper scope/configuration (#969) Only test case implementations use TestContainer dependencies, not production source code. Those dependencies need to be made available for src/test/java. --- docs/usage.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/usage.md b/docs/usage.md index 2cb5fbd661a..3244a292a7b 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -51,13 +51,14 @@ Maven style: org.testcontainers --artifact name-- --latest version-- + test ``` Gradle style: ``` -compile group: 'org.testcontainers', name: '--artifact name--', version: '--latest version--' +testImplementation group: 'org.testcontainers', name: '--artifact name--', version: '--latest version--' ``` @@ -70,6 +71,7 @@ Use the following dependency description instead: com.github.testcontainers.testcontainers-java --artifact name-- -SNAPSHOT + test A specific git revision (such as `093a3a4628`) can be used as a fixed version instead. The JitPack maven repository must also be declared, e.g.: From e823ca2740d7b868a0ae8d7a093ae19c93257ac7 Mon Sep 17 00:00:00 2001 From: Oded Arbel Date: Sat, 10 Nov 2018 18:20:14 +0200 Subject: [PATCH 0456/5615] Support using MariaDB JDBC URL to set database name, user and password (#950) Implement feature compatibility with MySQL container requested in issue #949 --- .../testcontainers/jdbc/JDBCDriverTest.java | 7 ++- .../containers/MariaDBContainer.java | 47 ++++++++++++++----- .../containers/MariaDBContainerProvider.java | 31 ++++++++++++ 3 files changed, 72 insertions(+), 13 deletions(-) diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java index 9ff882b6a68..941a224e7a0 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java @@ -55,10 +55,13 @@ public static Iterable data() { {"jdbc:tc:postgis:9.6://hostname/databasename", EnumSet.noneOf(Options.class)}, {"jdbc:tc:mysql:5.6://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override", EnumSet.of(Options.CustomIniFile)}, {"jdbc:tc:mariadb://hostname/databasename", EnumSet.noneOf(Options.class)}, + {"jdbc:tc:mariadb://hostname/databasename?user=someuser&TC_INITSCRIPT=somepath/init_mariadb.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, {"jdbc:tc:mariadb:10.2.14://hostname/databasename", EnumSet.noneOf(Options.class)}, {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITSCRIPT=somepath/init_unicode_mysql.sql&useUnicode=yes&characterEncoding=utf8", EnumSet.of(Options.CharacterSet)}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITSCRIPT=somepath/init_mariadb.sql", EnumSet.of(Options.ScriptedSchema)}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema)}, + {"jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&TC_INITSCRIPT=somepath/init_mariadb.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, + {"jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, + {"jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=somepath/init_mariadb.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, + {"jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&password=somepwd&TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_MY_CNF=somepath/mariadb_conf_override", EnumSet.of(Options.CustomIniFile)}, {"jdbc:tc:clickhouse://hostname/databasename", EnumSet.of(Options.PmdKnownBroken)}, }); diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java index e52dc5b52c1..c41136c0bf2 100644 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java @@ -12,9 +12,10 @@ public class MariaDBContainer> extends JdbcD public static final String DEFAULT_TAG = "10.3.6"; private static final Integer MARIADB_PORT = 3306; - private static final String MARIADB_USER = "test"; - private static final String MARIADB_PASSWORD = "test"; - private static final String MARIADB_DATABASE = "test"; + private String databaseName = "test"; + private String username = "test"; + private String password = "test"; + private static final String MARIADB_ROOT_USER = "root"; private static final String MY_CNF_CONFIG_OVERRIDE_PARAM_NAME = "TC_MY_CNF"; public MariaDBContainer() { @@ -35,10 +36,16 @@ protected void configure() { optionallyMapResourceParameterAsVolume(MY_CNF_CONFIG_OVERRIDE_PARAM_NAME, "/etc/mysql/conf.d", "mariadb-default-conf"); addExposedPort(MARIADB_PORT); - addEnv("MYSQL_DATABASE", MARIADB_DATABASE); - addEnv("MYSQL_USER", MARIADB_USER); - addEnv("MYSQL_PASSWORD", MARIADB_PASSWORD); - addEnv("MYSQL_ROOT_PASSWORD", MARIADB_PASSWORD); + addEnv("MYSQL_DATABASE", databaseName); + addEnv("MYSQL_USER", username); + if (password != null && !password.isEmpty()) { + addEnv("MYSQL_PASSWORD", password); + addEnv("MYSQL_ROOT_PASSWORD", password); + } else if (MARIADB_ROOT_USER.equalsIgnoreCase(username)) { + addEnv("MYSQL_ALLOW_EMPTY_PASSWORD", "yes"); + } else { + throw new ContainerLaunchException("Empty password can be used only with the root user"); + } setStartupAttempts(3); } @@ -49,22 +56,22 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return "jdbc:mariadb://" + getContainerIpAddress() + ":" + getMappedPort(MARIADB_PORT) + "/test"; + return "jdbc:mariadb://" + getContainerIpAddress() + ":" + getMappedPort(MARIADB_PORT) + "/" + databaseName; } @Override public String getDatabaseName() { - return MARIADB_DATABASE; + return databaseName; } @Override public String getUsername() { - return MARIADB_USER; + return username; } @Override public String getPassword() { - return MARIADB_PASSWORD; + return password; } @Override @@ -76,4 +83,22 @@ public SELF withConfigurationOverride(String s) { parameters.put(MY_CNF_CONFIG_OVERRIDE_PARAM_NAME, s); return self(); } + + @Override + public SELF withDatabaseName(final String databaseName) { + this.databaseName = databaseName; + return self(); + } + + @Override + public SELF withUsername(final String username) { + this.username = username; + return self(); + } + + @Override + public SELF withPassword(final String password) { + this.password = password; + return self(); + } } diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java index 31504b95953..deb1b2c5c5e 100644 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java @@ -1,9 +1,18 @@ package org.testcontainers.containers; +import org.testcontainers.jdbc.ConnectionUrl; + +import java.util.Objects; + /** * Factory for MariaDB org.testcontainers.containers. */ public class MariaDBContainerProvider extends JdbcDatabaseContainerProvider { + + private static final String USER_PARAM = "user"; + + private static final String PASSWORD_PARAM = "password"; + @Override public boolean supports(String databaseType) { return databaseType.equals(MariaDBContainer.NAME); @@ -18,4 +27,26 @@ public JdbcDatabaseContainer newInstance() { public JdbcDatabaseContainer newInstance(String tag) { return new MariaDBContainer(MariaDBContainer.IMAGE + ":" + tag); } + + @Override + public JdbcDatabaseContainer newInstance(ConnectionUrl connectionUrl) { + Objects.requireNonNull(connectionUrl, "Connection URL cannot be null"); + + final String databaseName = connectionUrl.getDatabaseName().orElse("test"); + final String user = connectionUrl.getQueryParameters().getOrDefault(USER_PARAM, "test"); + final String password = connectionUrl.getQueryParameters().getOrDefault(PASSWORD_PARAM, "test"); + + final JdbcDatabaseContainer instance; + if (connectionUrl.getImageTag().isPresent()) { + instance = newInstance(connectionUrl.getImageTag().get()); + } else { + instance = newInstance(); + } + + return instance + .withDatabaseName(databaseName) + .withUsername(user) + .withPassword(password); + } + } From 48f2af76b80e8ae299cfcea75adb04ad2d3340e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9da=20Housni=20Alaoui?= Date: Tue, 20 Nov 2018 10:58:09 +0100 Subject: [PATCH 0457/5615] Add TcpHost to ElasticsearchContainer (#956) Applications have 2 ways to communicate with ElasticSearch: - via REST client - via transport client ElasticsearchContainer currently only allows the usage of the REST client. This allows the application to connect to the container via transport client. --- docs/usage/elasticsearch_container.md | 13 ++++++-- modules/elasticsearch/build.gradle | 1 + .../elasticsearch/ElasticsearchContainer.java | 5 +++ .../ElasticsearchContainerTest.java | 31 ++++++++++++++++--- 4 files changed, 43 insertions(+), 7 deletions(-) diff --git a/docs/usage/elasticsearch_container.md b/docs/usage/elasticsearch_container.md index 5e02408ab7b..62d60fb82be 100644 --- a/docs/usage/elasticsearch_container.md +++ b/docs/usage/elasticsearch_container.md @@ -16,13 +16,20 @@ ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co // Start the container. This step might take some time... container.start(); -// Do whatever you want here. +// Do whatever you want with the rest client ... final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("elastic", "changeme")); -RestClient client = RestClient.builder(HttpHost.create(container.getHttpHostAddress())) +RestClient restClient = RestClient.builder(HttpHost.create(container.getHttpHostAddress())) .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)) .build(); -Response response = client.performRequest("GET", "/"); +Response response = restClient.performRequest("GET", "/"); + +// ... or the transport client +TransportAddress transportAddress = new TransportAddress(container.getTcpHost()); +Settings settings = Settings.builder().put("cluster.name", "docker-cluster").build(); +TransportClient transportClient = new PreBuiltTransportClient(settings) + .addTransportAddress(transportAddress); +ClusterHealthResponse healths = transportClient.admin().cluster().prepareHealth().get(); // Stop the container. container.stop(); diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index f64fc8d4e56..5b1f7abb4ac 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,4 +3,5 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') testCompile "org.elasticsearch.client:elasticsearch-rest-client:6.4.1" + testCompile "org.elasticsearch.client:transport:6.4.1" } diff --git a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java index e8eff8a341f..3eb12969bed 100644 --- a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java +++ b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java @@ -4,6 +4,7 @@ import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.utility.Base58; +import java.net.InetSocketAddress; import java.time.Duration; import static java.net.HttpURLConnection.HTTP_OK; @@ -58,4 +59,8 @@ public ElasticsearchContainer(String dockerImageName) { public String getHttpHostAddress() { return getContainerIpAddress() + ":" + getMappedPort(ELASTICSEARCH_DEFAULT_PORT); } + + public InetSocketAddress getTcpHost() { + return new InetSocketAddress(getContainerIpAddress(), getMappedPort(ELASTICSEARCH_DEFAULT_TCP_PORT)); + } } diff --git a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java index fefbccc1b90..7aaf49d3af7 100644 --- a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java +++ b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java @@ -7,10 +7,15 @@ import org.apache.http.client.CredentialsProvider; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.util.EntityUtils; +import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; import org.elasticsearch.client.Request; import org.elasticsearch.client.Response; import org.elasticsearch.client.ResponseException; import org.elasticsearch.client.RestClient; +import org.elasticsearch.client.transport.TransportClient; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.transport.TransportAddress; +import org.elasticsearch.transport.client.PreBuiltTransportClient; import org.junit.After; import org.junit.Test; @@ -48,7 +53,7 @@ public void stopRestClient() throws IOException { public void elasticsearchDefaultTest() throws IOException { try (ElasticsearchContainer container = new ElasticsearchContainer() .withEnv("foo", "bar") // dummy env for compiler checking correct generics usage - ){ + ) { container.start(); Response response = getClient(container).performRequest(new Request("GET", "/")); assertThat(response.getStatusLine().getStatusCode(), is(200)); @@ -87,17 +92,35 @@ public void elasticsearchOssImage() throws IOException { } } + @Test + public void transportClientClusterHealth() { + try (ElasticsearchContainer container = new ElasticsearchContainer()) { + container.start(); + + TransportAddress transportAddress = new TransportAddress(container.getTcpHost()); + String expectedClusterName = "docker-cluster"; + Settings settings = Settings.builder().put("cluster.name", expectedClusterName).build(); + try (TransportClient transportClient = new PreBuiltTransportClient(settings) + .addTransportAddress(transportAddress)) { + ClusterHealthResponse healths = transportClient.admin().cluster().prepareHealth().get(); + String clusterName = healths.getClusterName(); + assertThat(clusterName, is(expectedClusterName)); + } + } + } + private RestClient getClient(ElasticsearchContainer container) { if (client == null) { final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials(AuthScope.ANY, - new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ELASTICSEARCH_PASSWORD)); + new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ELASTICSEARCH_PASSWORD)); client = RestClient.builder(HttpHost.create(container.getHttpHostAddress())) - .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)) - .build(); + .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)) + .build(); } return client; } + } From cd359930cd6f0797da511f2ff1c28f059308dfcf Mon Sep 17 00:00:00 2001 From: Chung Nguyen Date: Mon, 26 Nov 2018 22:26:10 +0700 Subject: [PATCH 0458/5615] Update MS SQL Server image to new official location (#992) Fixes #944 --- .../src/test/resources/container-license-acceptance.txt | 2 +- modules/mssqlserver/README.md | 2 +- .../org/testcontainers/containers/MSSQLServerContainer.java | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/jdbc-test/src/test/resources/container-license-acceptance.txt b/modules/jdbc-test/src/test/resources/container-license-acceptance.txt index d706237de16..8a704b9c066 100644 --- a/modules/jdbc-test/src/test/resources/container-license-acceptance.txt +++ b/modules/jdbc-test/src/test/resources/container-license-acceptance.txt @@ -1 +1 @@ -microsoft/mssql-server-linux:2017-CU6 +mcr.microsoft.com/mssql/server:2017-CU12 diff --git a/modules/mssqlserver/README.md b/modules/mssqlserver/README.md index b0bf21fe19b..86e187002d2 100644 --- a/modules/mssqlserver/README.md +++ b/modules/mssqlserver/README.md @@ -19,7 +19,7 @@ public class SomeTest { ... create a connection and run test as normal ``` -> *Note:* Due to licencing restrictions you are required to accept an EULA for this container image. To indicate that you accept the MS SQL Server image EULA, Please place a file at the root of the classpath named `container-license-acceptance.txt`, e.g. at `src/test/resources/container-license-acceptance.txt`. This file should contain the line: `microsoft/mssql-server-linux:latest` +> *Note:* Due to licencing restrictions you are required to accept an EULA for this container image. To indicate that you accept the MS SQL Server image EULA, Please place a file at the root of the classpath named `container-license-acceptance.txt`, e.g. at `src/test/resources/container-license-acceptance.txt`. This file should contain the line: `mcr.microsoft.com/mssql/server:latest` ## Dependency information diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java index 2ab5181bc9e..6d843f34438 100644 --- a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java @@ -7,8 +7,8 @@ */ public class MSSQLServerContainer> extends JdbcDatabaseContainer { public static final String NAME = "mssqlserver"; - public static final String IMAGE = "microsoft/mssql-server-linux"; - public static final String DEFAULT_TAG = "2017-CU6"; + public static final String IMAGE = "mcr.microsoft.com/mssql/server"; + public static final String DEFAULT_TAG = "2017-CU12"; public static final Integer MS_SQL_SERVER_PORT = 1433; private String username = "SA"; From 58a6c69855126143825687230578157c36b86a56 Mon Sep 17 00:00:00 2001 From: Farid Zakaria Date: Mon, 26 Nov 2018 11:41:19 -0800 Subject: [PATCH 0459/5615] Allow to set Shared Memory size (shmSize) for GenericContainer (#957) --- .../containers/GenericContainer.java | 33 ++++++++++++++++++- .../junit/GenericContainerRuleTest.java | 16 +++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 32cae84951a..0554fd0e2de 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -6,6 +6,7 @@ import com.github.dockerjava.api.model.Bind; import com.github.dockerjava.api.model.ContainerNetwork; import com.github.dockerjava.api.model.ExposedPort; +import com.github.dockerjava.api.model.HostConfig; import com.github.dockerjava.api.model.Info; import com.github.dockerjava.api.model.Link; import com.github.dockerjava.api.model.PortBinding; @@ -153,6 +154,13 @@ public class GenericContainer> @Nullable private String workingDirectory = null; + /** + * The shared memory size to use when starting the container. + * This value is in bytes. + */ + @Nullable + private Long shmSize; + private Map copyToFileContainerPathMap = new HashMap<>(); /* @@ -318,6 +326,17 @@ private void tryStart(Profiler profiler) { } } + /** + * Set any custom settings for the create command such as shared memory size. + */ + private HostConfig buildHostConfig() { + HostConfig config = new HostConfig(); + if (shmSize != null) { + config.withShmSize(shmSize); + } + return config; + } + private void connectToPortForwardingNetwork(String networkMode) { PortForwardingContainer.INSTANCE.getNetwork().map(ContainerNetwork::getNetworkID).ifPresent(networkId -> { if (!Arrays.asList(networkId, "none", "host").contains(networkMode)) { @@ -434,7 +453,9 @@ public Set getLivenessCheckPortNumbers() { } private void applyConfiguration(CreateContainerCmd createCommand) { - + HostConfig hostConfig = buildHostConfig(); + createCommand.withHostConfig(hostConfig); + // Set up exposed ports (where there are no host port bindings defined) ExposedPort[] portArray = exposedPorts.stream() .map(ExposedPort::new) @@ -1157,6 +1178,16 @@ public SELF withCreateContainerCmdModifier(Consumer modifier return self(); } + /** + * Size of /dev/shm + * @param bytes The number of megabytes to assign the shared memory. If null, it will apply the Docker default which is 64 MB. + * @return this + */ + public SELF withSharedMemorySize(Long bytes) { + this.shmSize = bytes; + return self(); + } + /** * Convenience class with access to non-public members of GenericContainer. * diff --git a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java index deb13453287..f1aa5bd86b1 100644 --- a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java +++ b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java @@ -1,11 +1,13 @@ package org.testcontainers.junit; +import com.github.dockerjava.api.model.HostConfig; import com.google.common.collect.ImmutableMap; import com.google.common.util.concurrent.Uninterruptibles; import com.mongodb.MongoClient; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; import com.rabbitmq.client.*; +import org.apache.commons.io.FileUtils; import org.bson.Document; import org.junit.*; import org.rnorth.ducttape.RetryCountExceededException; @@ -375,4 +377,18 @@ public void addExposedPortAfterWithExposedPortsTest() { assertTrue("withExposedPort should be exposed", redis.getExposedPorts().contains(REDIS_PORT)); assertTrue("addExposedPort should be exposed", redis.getExposedPorts().contains(8987)); } + + @Test + public void sharedMemorySetTest() { + try (GenericContainer containerWithSharedMemory = new GenericContainer("busybox:1.29") + .withSharedMemorySize(1024L * FileUtils.ONE_MB)) { + + containerWithSharedMemory.start(); + + HostConfig hostConfig = + containerWithSharedMemory.getDockerClient().inspectContainerCmd(containerWithSharedMemory.getContainerId()) + .exec().getHostConfig(); + assertEquals("Shared memory not set on container", hostConfig.getShmSize(), 1024 * FileUtils.ONE_MB); + } + } } From d1a4820356c91cb967edca9c91560484ef71f1e1 Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Tue, 27 Nov 2018 14:33:07 +0100 Subject: [PATCH 0460/5615] Add Neo4j-OGM to 'Who is using Testcontainers' (#996) --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index 91b1ff33a17..e6888b6c2a1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -32,6 +32,7 @@ Testcontainers makes it easy to launch useful Docker containers for the duration * [Instana](https://www.instana.com) - Testing agents and stream processing backends * [eBay Marketing](https://www.ebay.com) - Testing for MySQL, Cassandra, Redis, Couchbase, Kafka, etc. * [Skyscanner](https://www.skyscanner.net/) - Integration testing against HTTP service mocks and various data stores + * [Neo4j-OGM](https://neo4j.com/developer/neo4j-ogm/) - Testing new, reactive client implementations ## License From 49529bb6cf026632602c6d578ec3e8c2f45ac594 Mon Sep 17 00:00:00 2001 From: Benjamin Muschko Date: Sun, 2 Dec 2018 15:06:51 -0700 Subject: [PATCH 0461/5615] [Docs] Change `withDockerfileFromBuilder` example to use expression Lamda (#975) Likely the preferable syntax. I've seen other examples in the user guide use this style e.g. under "Customizing the container". --- docs/usage/dockerfile.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/usage/dockerfile.md b/docs/usage/dockerfile.md index 88c3fda2d17..37716c7d6ad 100644 --- a/docs/usage/dockerfile.md +++ b/docs/usage/dockerfile.md @@ -39,12 +39,12 @@ programmatically), there is a DSL available to allow Dockerfiles to be defined i ```java new GenericContainer( new ImageFromDockerfile() - .withDockerfileFromBuilder(builder -> { + .withDockerfileFromBuilder(builder -> builder .from("alpine:3.2") .run("apk add --update nginx") .cmd("nginx", "-g", "daemon off;") - .build(); })) + .build())) .withExposedPorts(80); ``` @@ -63,4 +63,4 @@ By default the no-args constructor will use an image name of the form `testconta * `public ImageFromDockerfile()` * `public ImageFromDockerfile(String dockerImageName)` -* `public ImageFromDockerfile(String dockerImageName, boolean deleteOnExit)` \ No newline at end of file +* `public ImageFromDockerfile(String dockerImageName, boolean deleteOnExit)` From 13137ed5317c7658e28dc0ffe8790b5e5a76658c Mon Sep 17 00:00:00 2001 From: Bartosz Bednarek Date: Sun, 2 Dec 2018 23:08:40 +0100 Subject: [PATCH 0462/5615] Bump org.mock-server dependency set from 5.4.1 to 5.5.0 (#983) --- modules/mockserver/build.gradle | 2 +- .../java/org/testcontainers/containers/MockServerContainer.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/mockserver/build.gradle b/modules/mockserver/build.gradle index f0d9140e214..1fc8665e054 100644 --- a/modules/mockserver/build.gradle +++ b/modules/mockserver/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: MockServer" dependencies { compile project(':testcontainers') - testCompile 'org.mock-server:mockserver-client-java:5.4.1' + testCompile 'org.mock-server:mockserver-client-java:5.5.0' } diff --git a/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java b/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java index 3546aff7070..ca14e3e2953 100644 --- a/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java +++ b/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java @@ -5,7 +5,7 @@ @Slf4j public class MockServerContainer extends GenericContainer { - public static final String VERSION = "5.4.1"; + public static final String VERSION = "5.5.0"; public static final int PORT = 80; From a8e1708ca8084176c3838dad496f0d80be221c30 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 4 Dec 2018 10:15:11 +0000 Subject: [PATCH 0463/5615] Disable Gradle daemon in Travis CI builds (#1004) Disable Gradle daemon Attempt to resolve https://github.com/gradle/gradle/issues/3318 --- .travis.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6b0e158343a..9d72b3eb086 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,24 +7,24 @@ services: - docker install: - - ./gradlew build -x check --scan + - ./gradlew build -x check --scan --no-daemon jobs: include: - stage: test env: [ NAME=core ] - script: ./gradlew testcontainers:check javadoc --scan + script: ./gradlew testcontainers:check javadoc --scan --no-daemon - stage: test env: [ NAME=core ] jdk: openjdk11 - script: ./gradlew testcontainers:check --scan + script: ./gradlew testcontainers:check --scan --no-daemon - env: [ NAME=selenium ] - script: ./gradlew selenium:check --scan + script: ./gradlew selenium:check --scan --no-daemon - env: [ NAME=modules ] - script: ./gradlew check -x testcontainers:check -x selenium:check --scan + script: ./gradlew check -x testcontainers:check -x selenium:check --scan --no-daemon # Run Docker-in-Docker tests inside Alpine - env: [ NAME="docker-in-alpine-docker" ] @@ -45,7 +45,7 @@ jobs: script: skip deploy: provider: script - script: ./gradlew -Pversion=$TRAVIS_TAG release --scan + script: ./gradlew -Pversion=$TRAVIS_TAG release --scan --no-daemon on: tags: true branch: master From 3ad8d80e2484864e554744a4800a81f6b7982168 Mon Sep 17 00:00:00 2001 From: pavelpp Date: Tue, 4 Dec 2018 17:19:42 +0200 Subject: [PATCH 0464/5615] Use ChromeOptions and FirefoxOptions instead of DesiredCapabilities (#995) --- examples/selenium-container/pom.xml | 14 ++++- .../src/test/java/SeleniumContainerTest.java | 4 +- modules/selenium/build.gradle | 5 +- .../containers/BrowserWebDriverContainer.java | 51 +++++++++++++------ .../containers/SeleniumUtils.java | 2 +- .../junit/BaseWebDriverContainerTest.java | 28 +++++++--- ...ChromeRecordingWebDriverContainerTest.java | 6 +-- .../junit/ChromeWebDriverContainerTest.java | 16 +++--- .../ContainerWithoutCapabilitiesTest.java | 21 ++++++++ ...stomWaitTimeoutWebDriverContainerTest.java | 7 ++- .../junit/FirefoxWebDriverContainerTest.java | 10 +++- .../junit/FlakyContainerCreationTest.java | 4 +- .../junit/LinkedContainerTest.java | 6 +-- .../LocalServerWebDriverContainerTest.java | 6 +-- .../testcontainers/junit/Selenium3xTest.java | 4 +- ...ecificImageNameWebDriverContainerTest.java | 10 ++-- 16 files changed, 136 insertions(+), 58 deletions(-) create mode 100644 modules/selenium/src/test/java/org/testcontainers/junit/ContainerWithoutCapabilitiesTest.java diff --git a/examples/selenium-container/pom.xml b/examples/selenium-container/pom.xml index 1187b66f680..384a6f382af 100644 --- a/examples/selenium-container/pom.xml +++ b/examples/selenium-container/pom.xml @@ -15,7 +15,17 @@ org.seleniumhq.selenium selenium-remote-driver - 2.45.0 + 3.141.59 + + + org.seleniumhq.selenium + selenium-firefox-driver + 3.141.59 + + + org.seleniumhq.selenium + selenium-chrome-driver + 3.141.59 ${testcontainers.group} @@ -37,4 +47,4 @@ - \ No newline at end of file + diff --git a/examples/selenium-container/src/test/java/SeleniumContainerTest.java b/examples/selenium-container/src/test/java/SeleniumContainerTest.java index 528397fa1fd..4443e712be5 100644 --- a/examples/selenium-container/src/test/java/SeleniumContainerTest.java +++ b/examples/selenium-container/src/test/java/SeleniumContainerTest.java @@ -1,8 +1,8 @@ import org.junit.Rule; import org.junit.Test; import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.remote.RemoteWebDriver; +import org.openqa.selenium.chrome.ChromeOptions; import org.testcontainers.containers.BrowserWebDriverContainer; import java.io.File; @@ -17,7 +17,7 @@ public class SeleniumContainerTest { @Rule public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() - .withDesiredCapabilities(DesiredCapabilities.chrome()) + .withCapabilities(new ChromeOptions()) .withRecordingMode(RECORD_ALL, new File("target")); @Test diff --git a/modules/selenium/build.gradle b/modules/selenium/build.gradle index 1a3d33b0258..46699088b88 100644 --- a/modules/selenium/build.gradle +++ b/modules/selenium/build.gradle @@ -3,7 +3,10 @@ description = "Testcontainers :: Selenium" dependencies { compile project(':testcontainers') - provided 'org.seleniumhq.selenium:selenium-remote-driver:2.52.0' + provided 'org.seleniumhq.selenium:selenium-remote-driver:3.141.59' + provided 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' + testCompile 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' + testCompile 'org.seleniumhq.selenium:selenium-support:3.141.59' testCompile 'org.mortbay.jetty:jetty:6.1.25' testCompile project(':nginx') diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index 76feff2840f..94374235cbb 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -1,9 +1,13 @@ package org.testcontainers.containers; -import com.github.dockerjava.api.command.InspectContainerResponse; import com.google.common.collect.ImmutableSet; + +import com.github.dockerjava.api.command.InspectContainerResponse; + import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.openqa.selenium.Capabilities; +import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.remote.BrowserType; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.remote.RemoteWebDriver; @@ -45,7 +49,7 @@ public class BrowserWebDriverContainer getLivenessCheckPorts() { @Override protected void configure() { - if (desiredCapabilities == null) { - throw new IllegalStateException(); + String seleniumVersion = SeleniumUtils.determineClasspathSeleniumVersion(); + + if (capabilities == null) { + if (seleniumVersion.startsWith("2.")) { + logger().info("No capabilities provided, falling back to DesiredCapabilities.chrome()"); + capabilities = DesiredCapabilities.chrome(); + } else { + logger().info("No capabilities provided, falling back to ChromeOptions"); + capabilities = new ChromeOptions(); + } } if (recordingMode != VncRecordingMode.SKIP) { @@ -120,7 +145,7 @@ protected void configure() { } if (!customImageNameIsSet) { - super.setDockerImageName(getImageForCapabilities(desiredCapabilities)); + super.setDockerImageName(getImageForCapabilities(capabilities, seleniumVersion)); } String timeZone = System.getProperty("user.timezone"); @@ -140,11 +165,9 @@ protected void configure() { setStartupAttempts(3); } - public static String getImageForCapabilities(DesiredCapabilities desiredCapabilities) { + public static String getImageForCapabilities(Capabilities capabilities, String seleniumVersion) { - String seleniumVersion = SeleniumUtils.determineClasspathSeleniumVersion(); - - String browserName = desiredCapabilities.getBrowserName(); + String browserName = capabilities.getBrowserName(); switch (browserName) { case BrowserType.CHROME: return String.format(CHROME_IMAGE, seleniumVersion); @@ -184,7 +207,7 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { driver = Unreliables.retryUntilSuccess(30, TimeUnit.SECONDS, Timeouts.getWithTimeout(10, TimeUnit.SECONDS, () -> - () -> new RemoteWebDriver(getSeleniumAddress(), desiredCapabilities))); + () -> new RemoteWebDriver(getSeleniumAddress(), capabilities))); if (vncRecordingContainer != null) { LOGGER.debug("Starting VNC recording"); @@ -282,6 +305,4 @@ public SELF withRecordingFileFactory(RecordingFileFactory recordingFileFactory) public enum VncRecordingMode { SKIP, RECORD_ALL, RECORD_FAILING } - - } diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/SeleniumUtils.java b/modules/selenium/src/main/java/org/testcontainers/containers/SeleniumUtils.java index 1a6319d3103..131f5a953ea 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/SeleniumUtils.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/SeleniumUtils.java @@ -65,7 +65,7 @@ public static String determineClasspathSeleniumVersion() { /** * Read Manifest to get Selenium Version. - * @param manifest + * @param manifest manifest * @return Selenium Version detected */ public static String getSeleniumVersionFromManifest(Manifest manifest) { diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java index 1f8a68f857f..0f41b2ce1d2 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java @@ -2,12 +2,16 @@ import org.jetbrains.annotations.NotNull; import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; import org.openqa.selenium.remote.RemoteWebDriver; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; import org.testcontainers.containers.BrowserWebDriverContainer; +import java.util.List; import java.util.concurrent.TimeUnit; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static java.lang.String.format; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; /** @@ -20,12 +24,24 @@ protected void doSimpleWebdriverTest(BrowserWebDriverContainer rule) { System.out.println("Selenium remote URL is: " + rule.getSeleniumAddress()); System.out.println("VNC URL is: " + rule.getVncAddress()); - //Runtime.getRuntime().exec("open " + rule.getVncUrl(driver)); // For debugging, on a Mac - driver.get("http://www.google.com"); - driver.findElement(By.name("q")).sendKeys("testcontainers"); - driver.findElement(By.name("q")).submit(); - assertEquals("the word 'testcontainers' appears in the search box", "testcontainers", driver.findElement(By.name("q")).getAttribute("value")); + WebElement search = driver.findElement(By.name("q")); + search.sendKeys("testcontainers"); + search.submit(); + + List results = new WebDriverWait(driver, 15) + .until(ExpectedConditions.visibilityOfAllElementsLocatedBy(By.cssSelector("#search h3"))); + + assertTrue("the word 'testcontainers' appears in search results", + results.stream() + .anyMatch(el -> el.getText().contains("testcontainers"))); + } + + protected void assertBrowserNameIs(BrowserWebDriverContainer rule, String expectedName) { + RemoteWebDriver driver = setupDriverFromRule(rule); + String actual = driver.getCapabilities().getBrowserName(); + assertTrue(format("actual browser name is %s", actual), + actual.equals(expectedName)); } @NotNull diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java index f0f011d5e89..6601242efa7 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java @@ -4,7 +4,7 @@ import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; -import org.openqa.selenium.remote.DesiredCapabilities; +import org.openqa.selenium.chrome.ChromeOptions; import org.testcontainers.containers.BrowserWebDriverContainer; import org.testcontainers.containers.DefaultRecordingFileFactory; @@ -19,7 +19,7 @@ public static class ChromeThatRecordsAllTests { @Rule public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() - .withDesiredCapabilities(DesiredCapabilities.chrome()) + .withCapabilities(new ChromeOptions()) .withRecordingMode(RECORD_ALL, new File("./build/")) .withRecordingFileFactory(new DefaultRecordingFileFactory()); @@ -33,7 +33,7 @@ public static class ChromeThatRecordsFailingTests { @Rule public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() - .withDesiredCapabilities(DesiredCapabilities.chrome()); + .withCapabilities(new ChromeOptions()); @Test public void recordingTestThatShouldBeRecordedButDeleted() { diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java index 66468baf092..7914e1bd810 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java @@ -1,12 +1,11 @@ package org.testcontainers.junit; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.openqa.selenium.remote.DesiredCapabilities; +import org.openqa.selenium.chrome.ChromeOptions; import org.testcontainers.containers.BrowserWebDriverContainer; -import java.io.IOException; - /** * */ @@ -14,15 +13,20 @@ public class ChromeWebDriverContainerTest extends BaseWebDriverContainerTest { @Rule public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() - .withDesiredCapabilities(DesiredCapabilities.chrome()); + .withCapabilities(new ChromeOptions()); + + @Before + public void checkBrowserIsIndeedChrome() { + assertBrowserNameIs(chrome, "chrome"); + } @Test - public void simpleTest() throws IOException { + public void simpleTest() { doSimpleWebdriverTest(chrome); } @Test - public void simpleExploreTest() throws IOException { + public void simpleExploreTest() { doSimpleExplore(chrome); } } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/ContainerWithoutCapabilitiesTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/ContainerWithoutCapabilitiesTest.java new file mode 100644 index 00000000000..c319395211c --- /dev/null +++ b/modules/selenium/src/test/java/org/testcontainers/junit/ContainerWithoutCapabilitiesTest.java @@ -0,0 +1,21 @@ +package org.testcontainers.junit; + +import org.junit.Rule; +import org.junit.Test; +import org.testcontainers.containers.BrowserWebDriverContainer; + +public class ContainerWithoutCapabilitiesTest extends BaseWebDriverContainerTest{ + + @Rule + public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer(); + + @Test + public void chromeIsStartedIfNoCapabilitiesProvided() { + assertBrowserNameIs(chrome, "chrome"); + } + + @Test + public void simpleExploreTestWhenNoCapabilitiesProvided(){ + doSimpleExplore(chrome); + } +} diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/CustomWaitTimeoutWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/CustomWaitTimeoutWebDriverContainerTest.java index d189ba34a24..1d0f895b682 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/CustomWaitTimeoutWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/CustomWaitTimeoutWebDriverContainerTest.java @@ -2,10 +2,9 @@ import org.junit.Rule; import org.junit.Test; -import org.openqa.selenium.remote.DesiredCapabilities; +import org.openqa.selenium.chrome.ChromeOptions; import org.testcontainers.containers.BrowserWebDriverContainer; -import java.io.IOException; import java.time.Duration; import static java.time.temporal.ChronoUnit.SECONDS; @@ -17,11 +16,11 @@ public class CustomWaitTimeoutWebDriverContainerTest extends BaseWebDriverContai @Rule public BrowserWebDriverContainer chromeWithCustomTimeout = new BrowserWebDriverContainer<>() - .withDesiredCapabilities(DesiredCapabilities.chrome()) + .withCapabilities(new ChromeOptions()) .withStartupTimeout(Duration.of(30, SECONDS)); @Test - public void simpleTest() throws IOException { + public void simpleTest() { doSimpleWebdriverTest(chromeWithCustomTimeout); } } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/FirefoxWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/FirefoxWebDriverContainerTest.java index 6b8f915498a..21b03164102 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/FirefoxWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/FirefoxWebDriverContainerTest.java @@ -1,8 +1,9 @@ package org.testcontainers.junit; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.openqa.selenium.remote.DesiredCapabilities; +import org.openqa.selenium.firefox.FirefoxOptions; import org.testcontainers.containers.BrowserWebDriverContainer; /** @@ -12,7 +13,12 @@ public class FirefoxWebDriverContainerTest extends BaseWebDriverContainerTest { @Rule public BrowserWebDriverContainer firefox = new BrowserWebDriverContainer() - .withDesiredCapabilities(DesiredCapabilities.firefox()); + .withCapabilities(new FirefoxOptions()); + + @Before + public void checkBrowserIsIndeedFirefox() { + assertBrowserNameIs(firefox, "firefox"); + } @Test public void simpleTest() { diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/FlakyContainerCreationTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/FlakyContainerCreationTest.java index 20229ea9c8f..d33a4c9c07e 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/FlakyContainerCreationTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/FlakyContainerCreationTest.java @@ -2,7 +2,7 @@ import org.junit.Ignore; import org.junit.Test; -import org.openqa.selenium.remote.DesiredCapabilities; +import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.remote.RemoteWebDriver; import org.testcontainers.containers.BrowserWebDriverContainer; @@ -17,7 +17,7 @@ public class FlakyContainerCreationTest { public void testCreationOfManyContainers() { for (int i = 0; i < 50; i++) { BrowserWebDriverContainer container = new BrowserWebDriverContainer() - .withDesiredCapabilities(DesiredCapabilities.chrome()) + .withCapabilities(new ChromeOptions()) .withRecordingMode(BrowserWebDriverContainer.VncRecordingMode.RECORD_FAILING, new File("build")); container.start(); diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/LinkedContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/LinkedContainerTest.java index 862a985ad8f..1a703ad3ea7 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/LinkedContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/LinkedContainerTest.java @@ -5,7 +5,7 @@ import org.junit.Rule; import org.junit.Test; import org.openqa.selenium.By; -import org.openqa.selenium.remote.DesiredCapabilities; +import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.remote.RemoteWebDriver; import org.testcontainers.containers.BrowserWebDriverContainer; import org.testcontainers.containers.Network; @@ -35,7 +35,7 @@ public class LinkedContainerTest { @Rule public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() .withNetwork(network) - .withDesiredCapabilities(DesiredCapabilities.chrome()); + .withCapabilities(new ChromeOptions()); @BeforeClass public static void setupContent() throws FileNotFoundException { @@ -55,7 +55,7 @@ public static void setupContent() throws FileNotFoundException { } @Test - public void testWebDriverToNginxContainerAccessViaContainerLink() throws Exception { + public void testWebDriverToNginxContainerAccessViaContainerLink() { RemoteWebDriver driver = chrome.getWebDriver(); driver.get("http://nginx/"); diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java index 255d1eb69f7..0cee0cf49e2 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java @@ -5,7 +5,7 @@ import org.mortbay.jetty.bio.SocketConnector; import org.mortbay.jetty.handler.ResourceHandler; import org.openqa.selenium.By; -import org.openqa.selenium.remote.DesiredCapabilities; +import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.remote.RemoteWebDriver; import org.testcontainers.containers.BrowserWebDriverContainer; import org.testcontainers.utility.TestEnvironment; @@ -20,7 +20,7 @@ public class LocalServerWebDriverContainerTest { @Rule - public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer().withDesiredCapabilities(DesiredCapabilities.chrome()); + public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer().withCapabilities(new ChromeOptions()); private int localPort; /** @@ -48,7 +48,7 @@ public void setupLocalServer() throws Exception { } @Test - public void testConnection() throws InterruptedException { + public void testConnection() { RemoteWebDriver driver = chrome.getWebDriver(); // Construct a URL that the browser container can access diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/Selenium3xTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/Selenium3xTest.java index 380d05097d3..f7415471bc8 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/Selenium3xTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/Selenium3xTest.java @@ -3,7 +3,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import org.openqa.selenium.remote.DesiredCapabilities; +import org.openqa.selenium.chrome.ChromeOptions; import org.testcontainers.containers.BrowserWebDriverContainer; /** @@ -23,7 +23,7 @@ public static String[] data() { @Test public void testAdditionalStartupString() { try (BrowserWebDriverContainer chrome = new BrowserWebDriverContainer("selenium/standalone-chrome-debug:" + tag) - .withDesiredCapabilities(DesiredCapabilities.chrome())) { + .withCapabilities(new ChromeOptions())) { chrome.start(); } } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java index c0392371f0c..01edd70c102 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java @@ -2,11 +2,9 @@ import org.junit.Rule; import org.junit.Test; -import org.openqa.selenium.remote.DesiredCapabilities; +import org.openqa.selenium.firefox.FirefoxOptions; import org.testcontainers.containers.BrowserWebDriverContainer; -import java.io.IOException; - /** * */ @@ -14,15 +12,15 @@ public class SpecificImageNameWebDriverContainerTest extends BaseWebDriverContai @Rule public BrowserWebDriverContainer firefox = new BrowserWebDriverContainer("selenium/standalone-firefox:2.53.1-beryllium") - .withDesiredCapabilities(DesiredCapabilities.firefox()); + .withCapabilities(new FirefoxOptions()); @Test - public void simpleTest() throws IOException { + public void simpleTest() { doSimpleWebdriverTest(firefox); } @Test - public void simpleExploreTest() throws IOException { + public void simpleExploreTest() { doSimpleExplore(firefox); } } From dce6264d9870e2e1caed0868b044a70e6da4b122 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 8 Dec 2018 16:06:41 +0000 Subject: [PATCH 0465/5615] Remove SLF4J Profiler (#1002) This was useful while we were working to optimise performance, but now I feel it's just clutter and can be safely removed. --- core/build.gradle | 1 - .../containers/DockerComposeContainer.java | 11 +--- .../containers/GenericContainer.java | 58 +++---------------- .../images/RemoteDockerImage.java | 11 ---- .../images/builder/ImageFromDockerfile.java | 11 ---- core/src/test/resources/logback-test.xml | 4 -- .../src/test/resources/logback-test.xml | 6 +- .../src/test/resources/logback-test.xml | 4 -- .../src/test/resources/logback-test.xml | 6 +- .../nginx/src/test/resources/logback-test.xml | 6 +- .../src/test/resources/logback-test.xml | 6 +- .../src/test/resources/logback-test.xml | 6 +- 12 files changed, 15 insertions(+), 115 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index a39f0ef4e2a..237f5422f16 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -73,7 +73,6 @@ project.tasks.check.dependsOn(jarFileTest) dependencies { compile 'junit:junit:4.12' compile 'org.slf4j:slf4j-api:1.7.25' - compile 'org.slf4j:slf4j-ext:1.7.25' compile 'org.jetbrains:annotations:15.0' compile 'javax.annotation:javax.annotation-api:1.3.1' compile 'org.apache.commons:commons-compress:1.18' diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 72997053100..d2958129e90 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -13,7 +13,6 @@ import org.junit.runners.model.Statement; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.slf4j.profiler.Profiler; import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.output.Slf4jLogConsumer; @@ -140,10 +139,6 @@ public void finished(Description description) { @Override public void start() { - final Profiler profiler = new Profiler("Docker Compose container rule"); - profiler.setLogger(logger()); - profiler.start("Docker Compose container startup"); - synchronized (MUTEX) { registerContainersForShutdown(); if (pull) { @@ -155,7 +150,7 @@ public void start() { } applyScaling(); // scale before up, so that all scaled instances are available first for linking createServices(); - startAmbassadorContainers(profiler); + startAmbassadorContainers(); waitUntilServiceStarted(); } } @@ -243,10 +238,8 @@ private List listChildContainers() { .collect(toList()); } - private void startAmbassadorContainers(Profiler profiler) { - profiler.start("Ambassador container startup"); + private void startAmbassadorContainers() { ambassadorContainer.start(); - profiler.stop().log(); } private Logger logger() { diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 0554fd0e2de..2fbc496a571 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -3,22 +3,9 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.CreateContainerCmd; import com.github.dockerjava.api.command.InspectContainerResponse; -import com.github.dockerjava.api.model.Bind; -import com.github.dockerjava.api.model.ContainerNetwork; -import com.github.dockerjava.api.model.ExposedPort; -import com.github.dockerjava.api.model.HostConfig; -import com.github.dockerjava.api.model.Info; -import com.github.dockerjava.api.model.Link; -import com.github.dockerjava.api.model.PortBinding; -import com.github.dockerjava.api.model.Volume; -import com.github.dockerjava.api.model.VolumesFrom; +import com.github.dockerjava.api.model.*; import com.google.common.base.Strings; -import lombok.AccessLevel; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NonNull; -import lombok.Setter; -import lombok.SneakyThrows; +import lombok.*; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.apache.commons.compress.utils.IOUtils; @@ -32,7 +19,6 @@ import org.rnorth.ducttape.unreliables.Unreliables; import org.rnorth.visibleassertions.VisibleAssertions; import org.slf4j.Logger; -import org.slf4j.profiler.Profiler; import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.output.FrameConsumerResultCallback; import org.testcontainers.containers.output.OutputFrame; @@ -51,25 +37,11 @@ import org.testcontainers.lifecycle.TestLifecycleAware; import org.testcontainers.utility.*; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; +import java.io.*; import java.nio.charset.Charset; import java.nio.file.Path; import java.time.Duration; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -229,11 +201,7 @@ public void start() { } protected void doStart() { - Profiler profiler = new Profiler("Container startup"); - profiler.setLogger(logger()); - try { - profiler.start("Prepare container configuration and host configuration"); configure(); logger().debug("Starting container: {}", getDockerImageName()); @@ -242,24 +210,21 @@ protected void doStart() { AtomicInteger attempt = new AtomicInteger(0); Unreliables.retryUntilSuccess(startupAttempts, () -> { logger().debug("Trying to start container: {} (attempt {}/{})", image.get(), attempt.incrementAndGet(), startupAttempts); - tryStart(profiler.startNested("Container startup attempt")); + tryStart(); return true; }); } catch (Exception e) { throw new ContainerLaunchException("Container startup failed", e); - } finally { - profiler.stop().log(); } } - private void tryStart(Profiler profiler) { + private void tryStart() { try { String dockerImageName = image.get(); logger().debug("Starting container: {}", dockerImageName); logger().info("Creating container for image: {}", dockerImageName); - profiler.start("Create container"); CreateContainerCmd createCommand = dockerClient.createContainerCmd(dockerImageName); applyConfiguration(createCommand); @@ -272,7 +237,6 @@ private void tryStart(Profiler profiler) { containerIsCreated(containerId); logger().info("Starting container with ID: {}", containerId); - profiler.start("Start container"); dockerClient.startContainerCmd(containerId).exec(); // For all registered output consumers, start following as close to container startup as possible @@ -281,14 +245,11 @@ private void tryStart(Profiler profiler) { logger().info("Container {} is starting: {}", dockerImageName, containerId); // Tell subclasses that we're starting - profiler.start("Inspecting container"); containerInfo = dockerClient.inspectContainerCmd(containerId).exec(); containerName = containerInfo.getName(); - profiler.start("Call containerIsStarting on subclasses"); containerIsStarting(containerInfo); // Wait until the container is running (may not be fully started) - profiler.start("Wait until container has started properly, or there's evidence it failed to start."); if (!this.startupCheckStrategy.waitUntilStartupSuccessful(dockerClient, containerId)) { // Bail out, don't wait for the port to start listening. @@ -296,7 +257,6 @@ private void tryStart(Profiler profiler) { throw new IllegalStateException("Container did not start correctly."); } - profiler.start("Wait until container started properly"); waitUntilContainerStarted(); logger().info("Container {} started", dockerImageName); @@ -321,8 +281,6 @@ private void tryStart(Profiler profiler) { } throw new ContainerLaunchException("Could not create/start container", e); - } finally { - profiler.stop(); } } @@ -455,7 +413,7 @@ public Set getLivenessCheckPortNumbers() { private void applyConfiguration(CreateContainerCmd createCommand) { HostConfig hostConfig = buildHostConfig(); createCommand.withHostConfig(hostConfig); - + // Set up exposed ports (where there are no host port bindings defined) ExposedPort[] portArray = exposedPorts.stream() .map(ExposedPort::new) @@ -1166,7 +1124,7 @@ public SELF withStartupAttempts(int attempts) { } /** - * Allow low level modifications of {@link CreateContainerCmd} after it was pre-configured in {@link #tryStart(Profiler)}. + * Allow low level modifications of {@link CreateContainerCmd} after it was pre-configured in {@link #tryStart()}. * Invocation happens eagerly on a moment when container is created. * Warning: this does expose the underlying docker-java API so might change outside of our control. * diff --git a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java index 9554793c004..d0bc47d6b12 100644 --- a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java +++ b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java @@ -8,7 +8,6 @@ import lombok.NonNull; import lombok.ToString; import org.slf4j.Logger; -import org.slf4j.profiler.Profiler; import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.ContainerFetchException; import org.testcontainers.utility.DockerImageName; @@ -39,17 +38,10 @@ public RemoteDockerImage(@NonNull String repository, @NonNull String tag) { @Override protected final String resolve() { - Profiler profiler = new Profiler("Rule creation - prefetch image"); Logger logger = DockerLoggerFactory.getLogger(imageName.toString()); - profiler.setLogger(logger); - Profiler nested = profiler.startNested("Obtaining client"); DockerClient dockerClient = DockerClientFactory.instance().client(); try { - nested.stop(); - - profiler.start("Check local images"); - int attempts = 0; Exception lastException = null; while (true) { @@ -83,7 +75,6 @@ protected final String resolve() { // Log only on first attempt if (attempts == 0) { logger.info("Pulling docker image: {}. Please be patient; this may take some time but only needs to be done once.", imageName); - profiler.start("Pull image"); } if (attempts++ >= 3) { @@ -109,8 +100,6 @@ protected final String resolve() { return imageName.toString(); } catch (DockerClientException e) { throw new ContainerFetchException("Failed to get Docker client for " + imageName, e); - } finally { - profiler.stop().log(); } } } diff --git a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java index 71caa59566b..ed109acc4c3 100644 --- a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java +++ b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java @@ -12,7 +12,6 @@ import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; -import org.slf4j.profiler.Profiler; import org.testcontainers.DockerClientFactory; import org.testcontainers.images.builder.traits.*; import org.testcontainers.utility.Base58; @@ -90,9 +89,6 @@ public ImageFromDockerfile withFileFromTransferable(String path, Transferable tr protected final String resolve() { Logger logger = DockerLoggerFactory.getLogger(dockerImageName); - Profiler profiler = new Profiler("Rule creation - build image"); - profiler.setLogger(logger); - DockerClient dockerClient = DockerClientFactory.instance().client(); try { if (deleteOnExit) { @@ -116,16 +112,12 @@ public void onNext(BuildResponseItem item) { @Cleanup PipedInputStream in = new PipedInputStream(); @Cleanup PipedOutputStream out = new PipedOutputStream(in); - profiler.start("Configure image"); BuildImageCmd buildImageCmd = dockerClient.buildImageCmd(in); configure(buildImageCmd); - profiler.start("Build image"); BuildImageResultCallback exec = buildImageCmd.exec(resultCallback); // To build an image, we have to send the context to Docker in TAR archive format - profiler.start("Send context as TAR"); - try (TarArchiveOutputStream tarArchive = new TarArchiveOutputStream(new GZIPOutputStream(out))) { tarArchive.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); @@ -137,14 +129,11 @@ public void onNext(BuildResponseItem item) { tarArchive.finish(); } - profiler.start("Wait for an image id"); exec.awaitImageId(); return dockerImageName; } catch(IOException e) { throw new RuntimeException("Can't close DockerClient", e); - } finally { - profiler.stop().log(); } } diff --git a/core/src/test/resources/logback-test.xml b/core/src/test/resources/logback-test.xml index 42a23f5fe13..57a53cdd3aa 100644 --- a/core/src/test/resources/logback-test.xml +++ b/core/src/test/resources/logback-test.xml @@ -20,8 +20,4 @@ - - PROFILER - DENY - diff --git a/modules/couchbase/src/test/resources/logback-test.xml b/modules/couchbase/src/test/resources/logback-test.xml index fe273feb881..d163f3ececb 100644 --- a/modules/couchbase/src/test/resources/logback-test.xml +++ b/modules/couchbase/src/test/resources/logback-test.xml @@ -22,8 +22,4 @@ - - PROFILER - DENY - - \ No newline at end of file + diff --git a/modules/elasticsearch/src/test/resources/logback-test.xml b/modules/elasticsearch/src/test/resources/logback-test.xml index 52e9ef70225..6d3eb183989 100644 --- a/modules/elasticsearch/src/test/resources/logback-test.xml +++ b/modules/elasticsearch/src/test/resources/logback-test.xml @@ -14,8 +14,4 @@ - - PROFILER - DENY - diff --git a/modules/jdbc-test/src/test/resources/logback-test.xml b/modules/jdbc-test/src/test/resources/logback-test.xml index ed0e5b2659e..b0f6b00e3e0 100644 --- a/modules/jdbc-test/src/test/resources/logback-test.xml +++ b/modules/jdbc-test/src/test/resources/logback-test.xml @@ -22,8 +22,4 @@ - - PROFILER - DENY - - \ No newline at end of file + diff --git a/modules/nginx/src/test/resources/logback-test.xml b/modules/nginx/src/test/resources/logback-test.xml index ed0e5b2659e..b0f6b00e3e0 100644 --- a/modules/nginx/src/test/resources/logback-test.xml +++ b/modules/nginx/src/test/resources/logback-test.xml @@ -22,8 +22,4 @@ - - PROFILER - DENY - - \ No newline at end of file + diff --git a/modules/oracle-xe/src/test/resources/logback-test.xml b/modules/oracle-xe/src/test/resources/logback-test.xml index ed0e5b2659e..b0f6b00e3e0 100644 --- a/modules/oracle-xe/src/test/resources/logback-test.xml +++ b/modules/oracle-xe/src/test/resources/logback-test.xml @@ -22,8 +22,4 @@ - - PROFILER - DENY - - \ No newline at end of file + diff --git a/modules/selenium/src/test/resources/logback-test.xml b/modules/selenium/src/test/resources/logback-test.xml index ed0e5b2659e..b0f6b00e3e0 100644 --- a/modules/selenium/src/test/resources/logback-test.xml +++ b/modules/selenium/src/test/resources/logback-test.xml @@ -22,8 +22,4 @@ - - PROFILER - DENY - - \ No newline at end of file + From c9bda9142a067765c7e53004705abcfddacef72d Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 8 Dec 2018 16:19:02 +0000 Subject: [PATCH 0466/5615] Simplify selenium example Previous test relied on the content/layout of wikipedia remaining the same, which led to unexpected failures --- .../src/test/java/SeleniumContainerTest.java | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/examples/selenium-container/src/test/java/SeleniumContainerTest.java b/examples/selenium-container/src/test/java/SeleniumContainerTest.java index 528397fa1fd..2e6d7430925 100644 --- a/examples/selenium-container/src/test/java/SeleniumContainerTest.java +++ b/examples/selenium-container/src/test/java/SeleniumContainerTest.java @@ -6,6 +6,7 @@ import org.testcontainers.containers.BrowserWebDriverContainer; import java.io.File; +import java.util.List; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; import static org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode.RECORD_ALL; @@ -25,18 +26,8 @@ public void simplePlainSeleniumTest() { RemoteWebDriver driver = chrome.getWebDriver(); driver.get("https://wikipedia.org"); - WebElement searchInput = driver.findElementByName("search"); + List searchInput = driver.findElementsByName("search"); - searchInput.sendKeys("Rick Astley"); - searchInput.submit(); - - WebElement otherPage = driver.findElementByPartialLinkText("Rickrolling"); - otherPage.click(); - - boolean expectedTextFound = driver.findElementsByCssSelector("p") - .stream() - .anyMatch(element -> element.getText().contains("meme")); - - assertTrue("The word 'meme' is found on a page about rickrolling", expectedTextFound); + assertTrue("The search input box is found", searchInput != null && searchInput.size() > 0); } } From e435dd4df487bbc978d0a4077b8baf497fe92908 Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Sat, 8 Dec 2018 17:41:08 +0100 Subject: [PATCH 0467/5615] Provide a Testcontainer for Neo4j. (#993) I'd like to propose a Testcontainer for Neo4j, an open source graph database as in this pull request. The test container is build with our official Docker image and provides the following features to the user: * To retrieve a Bolt url for usage with one of the official drivers, most likely the Java driver * To retrieve HTTP and HTTPS urls for the transactional REST api * Especially to disable authentication or chose a password * Also explicitly use the enterprise version of Neo4j While we offer a test harness and users may opt to use an embedded version of our database during test, it seems that many people think it's a better practice to test any database access against "the real thing". The container would be useful to us and allegedly to some other projects using Neo4j as well. We have been using a container very similar while spiking our efforts of a [reactive client for Neo4j](https://github.com/michael-simons/neo4j-reactive-java-client). --- docs/SUMMARY.md | 1 + docs/usage.md | 1 + docs/usage/neo4j_container.md | 102 +++++++++++ modules/neo4j/.gitignore | 1 + modules/neo4j/build.gradle | 8 + .../containers/Neo4jContainer.java | 173 ++++++++++++++++++ .../Neo4jContainerJUnitIntegrationTest.java | 56 ++++++ .../containers/Neo4jContainerTest.java | 82 +++++++++ .../example-container-license-acceptance.txt | 1 + 9 files changed, 425 insertions(+) create mode 100644 docs/usage/neo4j_container.md create mode 100644 modules/neo4j/.gitignore create mode 100644 modules/neo4j/build.gradle create mode 100644 modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java create mode 100644 modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerJUnitIntegrationTest.java create mode 100644 modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java create mode 100644 modules/neo4j/src/test/resources/example-container-license-acceptance.txt diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 673a5edd4d5..852fc37288b 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -47,6 +47,7 @@ * [Recording videos](usage/webdriver_containers.md#recording-videos) * [Kafka containers](usage/kafka_containers.md) +* [Neo4j containers](usage/neo4j_container.md) * [Docker Compose](usage/docker_compose.md) * [Dockerfile containers](usage/dockerfile.md) * [Windows support](usage/windows_support.md) diff --git a/docs/usage.md b/docs/usage.md index 3244a292a7b..65d29c07ecf 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -27,6 +27,7 @@ Testcontainers will try to connect to a Docker daemon using the following strate * [Elasticsearch container](usage/elasticsearch_container.md) - Elasticsearch container support * [Webdriver containers](usage/webdriver_containers.md) - run a dockerized Chrome or Firefox browser ready for Selenium/Webdriver operations - complete with automatic video recording * [Kafka containers](usage/kafka_containers.md) - run a dockerized Kafka, a distributed streaming platform +* [Neo4j container](usage/neo4j_container.md) - Neo4j container support * [Generic containers](usage/generic_containers.md) - run any Docker container as a test dependency * [Docker compose](usage/docker_compose.md) - reuse services defined in a Docker Compose YAML file * [Dockerfile containers](usage/dockerfile.md) - run a container that is built on-the-fly from a Dockerfile diff --git a/docs/usage/neo4j_container.md b/docs/usage/neo4j_container.md new file mode 100644 index 00000000000..5c50aedde7d --- /dev/null +++ b/docs/usage/neo4j_container.md @@ -0,0 +1,102 @@ +# Neo4j container + +This module helps running [Neo4j](https://neo4j.com/download/) using Testcontainers. + +Note that it's based on the [official Docker image](https://hub.docker.com/_/neo4j/) provided by Neo4j, Inc. + +## Dependencies + +Add the Neo4j Testcontainer module: + +```groovy +testCompile "org.testcontainers:neo4j" +``` + +and the Neo4j Java driver if you plan to access the Testcontainer via Bolt: + +```groovy +compile "org.neo4j.driver:neo4j-java-driver:1.7.1" +``` + +## Usage example + +Declare your Testcontainer as a `@ClassRule` or `@Rule` in a JUnit 4 test or as static or member attribute of a JUnit 5 test annotated with `@Container` as you would with other Testcontainers. +You can either use call `getHttpUrl()` or `getBoltUrl()` on the Neo4j container. +`getHttpUrl()` gives you the HTTP-address of the transactional HTTP endpoint while `getBoltUrl()` is meant to be used with one of the [official Bolt drivers](https://neo4j.com/docs/developer-manual/preview/drivers/). +On the JVM you would most likely use the [Java driver](https://github.com/neo4j/neo4j-java-driver). + +The following example uses the JUnit 5 extension `@Testcontainers` and demonstrates both the usage of the Java Driver and the REST endpoint: + +```java +@Testcontainers +public class ExampleTest { + + @Container + private static Neo4jContainer neo4jContainer = new Neo4jContainer() + .withAdminPassword(null); // Disable password + + @Test + void testSomethingUsingBolt() { + + // Retrieve the Bolt URL from the container + String boltUrl = neo4jContainer.getBoltUrl(); + try ( + Driver driver = GraphDatabase.driver(boltUrl, AuthTokens.none()); + Session session = driver.session() + ) { + long one = session.run("RETURN 1", Collections.emptyMap()).next().get(0).asLong(); + assertThat(one, is(1L)); + } catch (Exception e) { + fail(e.getMessage()); + } + } + + @Test + void testSomethingUsingHttp() throws IOException { + + // Retrieve the HTTP URL from the container + String httpUrl = neo4jContainer.getHttpUrl(); + + URL url = new URL(httpUrl + "/db/data/transaction/commit"); + HttpURLConnection con = (HttpURLConnection) url.openConnection(); + + con.setRequestMethod("POST"); + con.setRequestProperty("Content-Type", "application/json"); + con.setDoOutput(true); + + try (Writer out = new OutputStreamWriter(con.getOutputStream())) { + out.write("{\"statements\":[{\"statement\":\"RETURN 1\"}]}"); + out.flush(); + } + + assertThat(con.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + try (BufferedReader buffer = new BufferedReader(new InputStreamReader(con.getInputStream()))) { + String expectedResponse = + "{\"results\":[{\"columns\":[\"1\"],\"data\":[{\"row\":[1],\"meta\":[null]}]}],\"errors\":[]}"; + String response = buffer.lines().collect(Collectors.joining("\n")); + assertThat(response, is(expectedResponse)); + } + } +} +``` + +You are not limited to Unit tests and can of course use an instance of the Neo4j Testcontainer in vanilla Java code as well. + + +## Choose your Neo4j license + +If you need the Neo4j enterprise license, you can declare your Neo4j container like this: + +```java +@Testcontainers +public class ExampleTest { + @ClassRule + public static Neo4jContainer neo4jContainer = new Neo4jContainer() + .withEnterpriseEdition(); +} +``` + +This creates a Testcontainer based on the Docker image build with the Enterprise version of Neo4j. +The call to `withEnterpriseEdition` adds the required environment variable that you accepted the terms and condition of the enterprise version. +You accept those by adding a file named `container-license-acceptance.txt` to the root of your classpath containing the text `neo4j:3.5.0-enterprise` in one line. +You'll find more information about licensing Neo4j here: [About Neo4j Licenses](https://neo4j.com/licensing/). diff --git a/modules/neo4j/.gitignore b/modules/neo4j/.gitignore new file mode 100644 index 00000000000..486b6e376b8 --- /dev/null +++ b/modules/neo4j/.gitignore @@ -0,0 +1 @@ +container-license-acceptance.txt diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle new file mode 100644 index 00000000000..21f5ac07822 --- /dev/null +++ b/modules/neo4j/build.gradle @@ -0,0 +1,8 @@ +description = "TestContainers :: Neo4j" + +dependencies { + compile project(":testcontainers") + + testCompile "org.neo4j.driver:neo4j-java-driver:1.7.1" + testCompile 'org.assertj:assertj-core:3.11.1' +} diff --git a/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java new file mode 100644 index 00000000000..48e4f2f626d --- /dev/null +++ b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java @@ -0,0 +1,173 @@ +package org.testcontainers.containers; + +import static java.net.HttpURLConnection.*; +import static java.util.stream.Collectors.*; + +import java.time.Duration; +import java.util.Set; +import java.util.stream.Stream; + +import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; +import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; +import org.testcontainers.containers.wait.strategy.WaitAllStrategy; +import org.testcontainers.containers.wait.strategy.WaitStrategy; +import org.testcontainers.utility.LicenseAcceptance; + +/** + * Testcontainer for Neo4j. + * + * @param "SELF" to be used in the withXXX methods. + * @author Michael J. Simons + */ +public final class Neo4jContainer> extends GenericContainer { + + /** + * The image defaults to the official Neo4j image: Neo4j. + */ + private static final String DEFAULT_IMAGE_NAME = "neo4j"; + + /** + * The default tag (version) to use. + */ + private static final String DEFAULT_TAG = "3.5.0"; + + private static final String DOCKER_IMAGE_NAME = DEFAULT_IMAGE_NAME + ":" + DEFAULT_TAG; + + /** + * Default port for the binary Bolt protocol. + */ + private static final int DEFAULT_BOLT_PORT = 7687; + + /** + * The port of the transactional HTTPS endpoint: Neo4j REST API. + */ + private static final int DEFAULT_HTTPS_PORT = 7473; + + /** + * The port of the transactional HTTP endpoint: Neo4j REST API. + */ + private static final int DEFAULT_HTTP_PORT = 7474; + + /** + * The official image requires a change of password by default from "neo4j" to something else. This defaults to "password". + */ + private static final String DEFAULT_ADMIN_PASSWORD = "password"; + + private static final String AUTH_FORMAT = "neo4j/%s"; + + private String adminPassword = DEFAULT_ADMIN_PASSWORD; + + private boolean defaultImage = false; + + /** + * Creates a Testcontainer using the official Neo4j docker image. + */ + public Neo4jContainer() { + this(DOCKER_IMAGE_NAME); + + this.defaultImage = true; + } + + /** + * Creates a Testcontainer using a specific docker image. + * + * @param dockerImageName The docker image to use. + */ + public Neo4jContainer(String dockerImageName) { + super(dockerImageName); + + WaitStrategy waitForBolt = new LogMessageWaitStrategy() + .withRegEx(String.format(".*Bolt enabled on 0\\.0\\.0\\.0:%d\\.\n", DEFAULT_BOLT_PORT)); + WaitStrategy waitForHttp = new HttpWaitStrategy() + .forPort(DEFAULT_HTTP_PORT) + .forStatusCodeMatching(response -> response == HTTP_OK); + + this.waitStrategy = new WaitAllStrategy() + .withStrategy(waitForBolt) + .withStrategy(waitForHttp) + .withStartupTimeout(Duration.ofMinutes(2)); + } + + @Override + public Set getLivenessCheckPortNumbers() { + + return Stream.of(DEFAULT_BOLT_PORT, DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT) + .map(this::getMappedPort) + .collect(toSet()); + } + + @Override + protected void configure() { + + addExposedPorts(DEFAULT_BOLT_PORT, DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT); + + boolean emptyAdminPassword = this.adminPassword == null || this.adminPassword.isEmpty(); + String neo4jAuth = emptyAdminPassword ? "none" : String.format(AUTH_FORMAT, this.adminPassword); + addEnv("NEO4J_AUTH", neo4jAuth); + } + + /** + * @return Bolt URL for use with Neo4j's Java-Driver. + */ + public String getBoltUrl() { + return String.format("bolt://" + getContainerIpAddress() + ":" + getMappedPort(DEFAULT_BOLT_PORT)); + } + + /** + * @return URL of the transactional HTTP endpoint. + */ + public String getHttpUrl() { + return String.format("http://" + getContainerIpAddress() + ":" + getMappedPort(DEFAULT_HTTP_PORT)); + } + + /** + * @return URL of the transactional HTTPS endpoint. + */ + public String getHttpsUrl() { + return String.format("https://" + getContainerIpAddress() + ":" + getMappedPort(DEFAULT_HTTPS_PORT)); + } + + /** + * Configures the container to use the enterprise edition of the default docker image. + *

+ * Please have a look at the Neo4j Licensing page. While the Neo4j + * Community Edition can be used for free in your projects under the GPL v3 license, Neo4j Enterprise edition + * needs either a commercial, education or evaluation license. + * + * @return This container. + */ + public S withEnterpriseEdition() { + + if (!defaultImage) { + throw new IllegalStateException( + String.format("Cannot use enterprise version with alternative image %s.", getDockerImageName())); + } + + setDockerImageName(DOCKER_IMAGE_NAME + "-enterprise"); + LicenseAcceptance.assertLicenseAccepted(getDockerImageName()); + + addEnv("NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes"); + + return self(); + } + + /** + * Sets the admin password for the default account (which is

neo4j
). A null value or an empty string + * disables authentication. + * + * @param adminPassword The admin password for the default database account. + * @return This container. + */ + public S withAdminPassword(final String adminPassword) { + + this.adminPassword = adminPassword; + return self(); + } + + /** + * @return The admin password for the neo4j account or literal null if auth is disabled. + */ + public String getAdminPassword() { + return adminPassword; + } +} diff --git a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerJUnitIntegrationTest.java b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerJUnitIntegrationTest.java new file mode 100644 index 00000000000..96b7a48b96f --- /dev/null +++ b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerJUnitIntegrationTest.java @@ -0,0 +1,56 @@ +package org.testcontainers.containers; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Collections; + +import org.junit.ClassRule; +import org.junit.Test; +import org.neo4j.driver.v1.AuthTokens; +import org.neo4j.driver.v1.Driver; +import org.neo4j.driver.v1.GraphDatabase; +import org.neo4j.driver.v1.Session; + +/** + * Test for basic functionality when used as a @ClassRule. + * + * @author Michael J. Simons + */ +public class Neo4jContainerJUnitIntegrationTest { + + @ClassRule + public static Neo4jContainer neo4jContainer = new Neo4jContainer(); + + @Test + public void shouldStart() { + + boolean actual = neo4jContainer.isRunning(); + assertThat(actual).isTrue(); + + try (Driver driver = GraphDatabase + .driver(neo4jContainer.getBoltUrl(), AuthTokens.basic("neo4j", "password")); + Session session = driver.session() + ) { + long one = session.run("RETURN 1", Collections.emptyMap()).next().get(0).asLong(); + assertThat(one).isEqualTo(1L); + } catch (Exception e) { + fail(e.getMessage()); + } + } + + @Test + public void shouldReturnBoltUrl() { + String actual = neo4jContainer.getBoltUrl(); + + assertThat(actual).isNotNull(); + assertThat(actual).startsWith("bolt://"); + } + + @Test + public void shouldReturnHttpUrl() { + String actual = neo4jContainer.getHttpUrl(); + + assertThat(actual).isNotNull(); + assertThat(actual).startsWith("http://"); + } +} diff --git a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java new file mode 100644 index 00000000000..5908ffbd80d --- /dev/null +++ b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java @@ -0,0 +1,82 @@ +package org.testcontainers.containers; + +import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assumptions.*; + +import java.util.Collections; + +import org.junit.Test; +import org.neo4j.driver.v1.AuthToken; +import org.neo4j.driver.v1.AuthTokens; +import org.neo4j.driver.v1.Driver; +import org.neo4j.driver.v1.GraphDatabase; +import org.neo4j.driver.v1.Session; + +/** + * Tests of functionality special to the Neo4jContainer. + * + * @author Michael J. Simons + */ +public class Neo4jContainerTest { + + // See org.testcontainers.utility.LicenseAcceptance#ACCEPTANCE_FILE_NAME + private static final String ACCEPTANCE_FILE_LOCATION = "/container-license-acceptance.txt"; + + @Test + public void shouldDisableAuthentication() { + + try ( + Neo4jContainer neo4jContainer = new Neo4jContainer().withAdminPassword(null); + ) { + neo4jContainer.start(); + try (Driver driver = getDriver(neo4jContainer); + Session session = driver.session() + ) { + long one = session.run("RETURN 1", Collections.emptyMap()).next().get(0).asLong(); + assertThat(one).isEqualTo(1L); + } + } + } + + @Test + public void shouldCheckEnterpriseLicense() { + assumeThat(Neo4jContainerTest.class.getResource(ACCEPTANCE_FILE_LOCATION)).isNull(); + + String expectedImageName = "neo4j:3.5.0-enterprise"; + + assertThatExceptionOfType(IllegalStateException.class) + .isThrownBy(() -> new Neo4jContainer().withEnterpriseEdition()) + .withMessageContaining("The image " + expectedImageName + " requires you to accept a license agreement."); + } + + @Test + public void shouldRunEnterprise() { + assumeThat(Neo4jContainerTest.class.getResource(ACCEPTANCE_FILE_LOCATION)).isNotNull(); + + try ( + Neo4jContainer neo4jContainer = new Neo4jContainer() + .withEnterpriseEdition() + .withAdminPassword("Picard123") + ) { + neo4jContainer.start(); + try ( + Driver driver = getDriver(neo4jContainer); + Session session = driver.session() + ) { + String edition = session + .run("CALL dbms.components() YIELD edition RETURN edition", Collections.emptyMap()) + .next().get(0).asString(); + assertThat(edition).isEqualTo("enterprise"); + } + } + } + + private static Driver getDriver(Neo4jContainer container) { + + AuthToken authToken = AuthTokens.none(); + if (container.getAdminPassword() != null) { + authToken = AuthTokens.basic("neo4j", container.getAdminPassword()); + } + return GraphDatabase.driver(container.getBoltUrl(), authToken); + } +} diff --git a/modules/neo4j/src/test/resources/example-container-license-acceptance.txt b/modules/neo4j/src/test/resources/example-container-license-acceptance.txt new file mode 100644 index 00000000000..9b92a79c737 --- /dev/null +++ b/modules/neo4j/src/test/resources/example-container-license-acceptance.txt @@ -0,0 +1 @@ +neo4j:3.5.0-enterprise From d4260448f6ce76ea1e6cc92f62886a1c8d16d6d2 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 8 Dec 2018 16:39:00 +0000 Subject: [PATCH 0468/5615] Add logback-test.xml to reduce test logging verbosity --- .../neo4j/src/test/resources/logback-test.xml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 modules/neo4j/src/test/resources/logback-test.xml diff --git a/modules/neo4j/src/test/resources/logback-test.xml b/modules/neo4j/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..7bd6a94d827 --- /dev/null +++ b/modules/neo4j/src/test/resources/logback-test.xml @@ -0,0 +1,18 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + + + \ No newline at end of file From c8ec853e5095b33dcdeb7a7342243ae4a649f914 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 13 Dec 2018 13:07:07 +0000 Subject: [PATCH 0469/5615] Reinstate links to Slack and GH issues --- docs/index.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/index.md b/docs/index.md index e6888b6c2a1..c23a27aab5f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -34,6 +34,13 @@ Testcontainers makes it easy to launch useful Docker containers for the duration * [Skyscanner](https://www.skyscanner.net/) - Integration testing against HTTP service mocks and various data stores * [Neo4j-OGM](https://neo4j.com/developer/neo4j-ogm/) - Testing new, reactive client implementations +## Getting help + +You can: + +* Join our Slack: http://slack.testcontainers.org +* [Search our issues tracker](https://github.com/testcontainers/testcontainers-java/issues), or raise a new issue if you find any bugs or have suggested improvements + ## License See [LICENSE](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/LICENSE). From 1085da90dd297405c6613bd889fd4734121ead29 Mon Sep 17 00:00:00 2001 From: "Doroszlai, Attila" <6454655+adoroszlai@users.noreply.github.com> Date: Wed, 19 Dec 2018 12:09:48 +0100 Subject: [PATCH 0470/5615] Fix broken link to Oracle test in docs (#1018) Oracle module was moved back to this repo in #607. --- docs/usage/database_containers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage/database_containers.md b/docs/usage/database_containers.md index f0479280452..f22ea97ebc2 100644 --- a/docs/usage/database_containers.md +++ b/docs/usage/database_containers.md @@ -42,7 +42,7 @@ Examples/Tests: * [MySQL](https://github.com/testcontainers/testcontainers-java/blob/master/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java) * [PostgreSQL](https://github.com/testcontainers/testcontainers-java/blob/master/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimplePostgreSQLTest.java) - * [Oracle-XE](https://github.com/testcontainers/testcontainers-java-module-oracle-xe/blob/master/src/test/java/org/testcontainers/junit/SimpleOracleTest.java) + * [Oracle-XE](https://github.com/testcontainers/testcontainers-java/blob/master/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleOracleTest.java) * [Virtuoso](https://github.com/testcontainers/testcontainers-java/blob/master/modules/virtuoso/src/test/java/org/testcontainers/junit/SimpleVirtuosoTest.java) ### JDBC URL From cec788c8af8dd385804f33349644db5cbba61a5c Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 23 Dec 2018 13:59:05 +0000 Subject: [PATCH 0471/5615] Enable automatic release version numbering Using https://github.com/toolmantim/release-drafter/releases/tag/v4.0.0 If we need to manually specify a version (e.g. for a minor/major bump) we can still do that. --- .github/release-drafter.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 1176fe471a8..15ae17655c1 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -1,3 +1,5 @@ +name-template: $NEXT_PATCH_VERSION +tag-template: $NEXT_PATCH_VERSION template: | ## What's Changed From fbd29314edaec40ad93fe6848691c015c7e853d1 Mon Sep 17 00:00:00 2001 From: Tjeu Kayim <15987676+TjeuKayim@users.noreply.github.com> Date: Sun, 23 Dec 2018 15:11:52 +0100 Subject: [PATCH 0472/5615] Simplify GitLab CI documentation (#1016) --- docs/ci/ci.md | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/docs/ci/ci.md b/docs/ci/ci.md index cdec2554637..4f81c794fd3 100644 --- a/docs/ci/ci.md +++ b/docs/ci/ci.md @@ -2,41 +2,28 @@ ## GitLab +In order to use Testcontainers in a Gitlab CI pipeline, you need to run the job as a Docker container (see [Running inside Docker](../usage/inside_docker.md)). +So edit your `.gitlab-ci.yml` to include the [Docker-In-Docker service](https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#use-docker-in-docker-executor) (`docker:dind`) and set the `DOCKER_HOST` variable to `tcp://docker:2375`. -To be able to run your test container into the pipeline you need to run the job as a docker container (see [Running inside Docker](../usage/inside_docker.md)), -so you need Docker-In-Docker (docker:dind) service and mount the docker's socket. Also if you have "Permission denied" you -need to run your container as root user: +Here is a sample `.gitlab-ci.yml` that executes test with gradle: ```yml -# to improve performance -variables: - DOCKER_DRIVER: overlay2 - -# we need DinD service +# DinD service is required for Testcontainers services: - docker:dind +variables: + # Instruct Testcontainers to use the daemon of DinD. + DOCKER_HOST: "tcp://docker:2375" + # Improve performance with overlayfs. + DOCKER_DRIVER: overlay2 + test: - image: docker:latest + image: gradle:5.0 stage: test - script: - - > - docker run -t --rm - -v /var/run/docker.sock:/var/run/docker.sock - -v "$(pwd)":"$(pwd)" - -w "$(pwd)" - -u 0:0 - gradle:3.4 ./gradlew test - only: - - master + script: ./gradlew test ``` -In this job we run in a multiline command a docker container: -* using the official gradle image -* to be delete when finished -* use the root user if you are running a Shared Runner - - ## CircleCI 2.0 Your CircleCI configuration should use a dedicated VM for testcontainers to work. You can achieve this by specifying the From df2e02cc84868945ec24816fefa6659fb054f207 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 23 Dec 2018 14:14:52 +0000 Subject: [PATCH 0473/5615] Add dependabot.yml (#1026) See https://github.com/dependabot/feedback/issues/70 This is a first attempt to configure dependabot to work with our multi-module repo structure. The config file is a beta feature, and I'm not yet sure how well this will work. If it works OK for the `/core` directory, then I'd expect we may have to add an entry for each of the `/modules/*` directories as well. Hopefully this would change if dependabot gained the ability to understand multi-module gradle builds natively, but for now I hope that this works. --- .github/dependabot.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..cd43515ae5d --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +# See https://github.com/dependabot/feedback/issues/70 for current (beta) configurability +version: 1 +update_configs: + - package_manager: "java:gradle" + directory: "/core" + update_schedule: "daily" From 2c639163dd5994dca9808857a226d81b493a4925 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 23 Dec 2018 14:51:46 +0000 Subject: [PATCH 0474/5615] Testing alternative dependabot config location --- .github/.dependabot/config.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .github/.dependabot/config.yml diff --git a/.github/.dependabot/config.yml b/.github/.dependabot/config.yml new file mode 100644 index 00000000000..cd43515ae5d --- /dev/null +++ b/.github/.dependabot/config.yml @@ -0,0 +1,6 @@ +# See https://github.com/dependabot/feedback/issues/70 for current (beta) configurability +version: 1 +update_configs: + - package_manager: "java:gradle" + directory: "/core" + update_schedule: "daily" From 2b83b25b67c53b03cc976f229c73ea69452a55ad Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 23 Dec 2018 14:56:40 +0000 Subject: [PATCH 0475/5615] Delete config.yml --- .github/.dependabot/config.yml | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 .github/.dependabot/config.yml diff --git a/.github/.dependabot/config.yml b/.github/.dependabot/config.yml deleted file mode 100644 index cd43515ae5d..00000000000 --- a/.github/.dependabot/config.yml +++ /dev/null @@ -1,6 +0,0 @@ -# See https://github.com/dependabot/feedback/issues/70 for current (beta) configurability -version: 1 -update_configs: - - package_manager: "java:gradle" - directory: "/core" - update_schedule: "daily" From 325faf2d537ab5b3b675b97f4210ee5cf4ee7b89 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 23 Dec 2018 15:39:27 +0000 Subject: [PATCH 0476/5615] Move dependabot config --- .github/dependabot.yml => .dependabot/config.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/dependabot.yml => .dependabot/config.yml (100%) diff --git a/.github/dependabot.yml b/.dependabot/config.yml similarity index 100% rename from .github/dependabot.yml rename to .dependabot/config.yml From b5f4d8abcab5f42e6bd1910f465ef10bb70dba12 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 23 Dec 2018 16:12:26 +0000 Subject: [PATCH 0477/5615] Extend dependabot coverage to all modules and examples --- .dependabot/config.yml | 82 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/.dependabot/config.yml b/.dependabot/config.yml index cd43515ae5d..86b303d19f6 100644 --- a/.dependabot/config.yml +++ b/.dependabot/config.yml @@ -4,3 +4,85 @@ update_configs: - package_manager: "java:gradle" directory: "/core" update_schedule: "daily" + +# Explicit entry for each module + - package_manager: "java:gradle" + directory: "/modules/cassandra" + update_schedule: "daily" + - package_manager: "java:gradle" + directory: "/modules/ceph" + update_schedule: "daily" + - package_manager: "java:gradle" + directory: "/modules/clickhouse" + update_schedule: "daily" + - package_manager: "java:gradle" + directory: "/modules/couchbase" + update_schedule: "daily" + - package_manager: "java:gradle" + directory: "/modules/database-commons" + update_schedule: "daily" + - package_manager: "java:gradle" + directory: "/modules/dynalite" + update_schedule: "daily" + - package_manager: "java:gradle" + directory: "/modules/elasticsearch" + update_schedule: "daily" + - package_manager: "java:gradle" + directory: "/modules/influxdb" + update_schedule: "daily" + - package_manager: "java:gradle" + directory: "/modules/jdbc" + update_schedule: "daily" + - package_manager: "java:gradle" + directory: "/modules/jdbc-test" + update_schedule: "daily" + - package_manager: "java:gradle" + directory: "/modules/junit-jupiter" + update_schedule: "daily" + - package_manager: "java:gradle" + directory: "/modules/kafka" + update_schedule: "daily" + - package_manager: "java:gradle" + directory: "/modules/localstack" + update_schedule: "daily" + - package_manager: "java:gradle" + directory: "/modules/mariadb" + update_schedule: "daily" + - package_manager: "java:gradle" + directory: "/modules/mockserver" + update_schedule: "daily" + - package_manager: "java:gradle" + directory: "/modules/mssqlserver" + update_schedule: "daily" + - package_manager: "java:gradle" + directory: "/modules/mysql" + update_schedule: "daily" + - package_manager: "java:gradle" + directory: "/modules/neo4j" + update_schedule: "daily" + - package_manager: "java:gradle" + directory: "/modules/nginx" + update_schedule: "daily" + - package_manager: "java:gradle" + directory: "/modules/oracle-xe" + update_schedule: "daily" + - package_manager: "java:gradle" + directory: "/modules/postgresql" + update_schedule: "daily" + - package_manager: "java:gradle" + directory: "/modules/pulsar" + update_schedule: "daily" + - package_manager: "java:gradle" + directory: "/modules/selenium" + update_schedule: "daily" + - package_manager: "java:gradle" + directory: "/modules/spock" + update_schedule: "daily" + - package_manager: "java:gradle" + directory: "/modules/vault" + update_schedule: "daily" + +# Examples + - package_manager: "java:maven" + directory: "/examples" + update_schedule: "daily" From ef7c5163ba8cdc19f9ee73488d71770ead2ddbcd Mon Sep 17 00:00:00 2001 From: Artem Gavrilov Date: Sun, 23 Dec 2018 19:14:18 +0300 Subject: [PATCH 0478/5615] Support TmpFS mappings (#1021) --- .../containers/GenericContainer.java | 16 +++++++++++++ .../junit/GenericContainerRuleTest.java | 23 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 2fbc496a571..abf535869da 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -176,6 +176,9 @@ public class GenericContainer> .withConstantThroughput() .build(); + @Nullable + private Map tmpFsMapping; + public GenericContainer() { this(TestcontainersConfiguration.getInstance().getTinyImage()); @@ -292,6 +295,9 @@ private HostConfig buildHostConfig() { if (shmSize != null) { config.withShmSize(shmSize); } + if (tmpFsMapping != null) { + config.withTmpFs(tmpFsMapping); + } return config; } @@ -1146,6 +1152,16 @@ public SELF withSharedMemorySize(Long bytes) { return self(); } + /** + * First class support for configuring tmpfs + * @param mapping path and params of tmpfs/mount flag for container + * @return this + */ + public SELF withTmpFs(Map mapping) { + this.tmpFsMapping = mapping; + return self(); + } + /** * Convenience class with access to non-public members of GenericContainer. * diff --git a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java index f1aa5bd86b1..a991ebcf60a 100644 --- a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java +++ b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java @@ -12,6 +12,7 @@ import org.junit.*; import org.rnorth.ducttape.RetryCountExceededException; import org.rnorth.ducttape.unreliables.Unreliables; +import org.testcontainers.containers.Container; import org.testcontainers.containers.GenericContainer; import org.testcontainers.utility.Base58; import org.testcontainers.utility.TestEnvironment; @@ -26,6 +27,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import static java.util.Collections.singletonMap; import static org.hamcrest.CoreMatchers.equalTo; import static org.rnorth.visibleassertions.VisibleAssertions.*; import static org.testcontainers.containers.BindMode.READ_ONLY; @@ -158,6 +160,27 @@ public void testIsRunning() { } } + @Test + public void withTmpFsTest() throws Exception { + try ( + GenericContainer container = new GenericContainer() + .withCommand("top") + .withTmpFs(singletonMap("/testtmpfs", "rw")) + ) { + container.start(); + // check file doesn't exist + String path = "/testtmpfs/test.file"; + Container.ExecResult execResult = container.execInContainer("ls", path); + assertEquals("tmpfs inside container works fine", execResult.getStderr(), + "ls: /testtmpfs/test.file: No such file or directory\n"); + // touch && check file does exist + container.execInContainer("touch", path); + execResult = container.execInContainer("ls", path); + assertEquals("tmpfs inside container works fine", execResult.getStdout(), path + "\n"); + } + } + + @Test public void simpleRabbitMqTest() throws IOException, TimeoutException { ConnectionFactory factory = new ConnectionFactory(); From 1eb16a2f0b33ba001d488701f2df43f399977370 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 23 Dec 2018 16:34:49 +0000 Subject: [PATCH 0479/5615] Remove not-present module from dependabot config --- .dependabot/config.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.dependabot/config.yml b/.dependabot/config.yml index 86b303d19f6..3c88d798542 100644 --- a/.dependabot/config.yml +++ b/.dependabot/config.yml @@ -9,9 +9,6 @@ update_configs: - package_manager: "java:gradle" directory: "/modules/cassandra" update_schedule: "daily" - - package_manager: "java:gradle" - directory: "/modules/ceph" - update_schedule: "daily" - package_manager: "java:gradle" directory: "/modules/clickhouse" update_schedule: "daily" From 0ac9d700a71e815d23439dfdc03725b648593f6c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 23 Dec 2018 17:02:50 +0000 Subject: [PATCH 0480/5615] Bump annotations from 15.0 to 16.0.3 in /core (#1027) Bumps [annotations](https://github.com/JetBrains/java-annotations) from 15.0 to 16.0.3.
Release notes *Sourced from [annotations's releases](https://github.com/JetBrains/java-annotations/releases).* > ## 16.0.3 > Added Automatic-Module-Name
Commits - See full diff in [compare view](https://github.com/JetBrains/java-annotations/commits/16.0.3)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=org.jetbrains:annotations&package-manager=gradle&previous-version=15.0&new-version=16.0.3)](https://dependabot.com/compatibility-score.html?dependency-name=org.jetbrains:annotations&package-manager=gradle&previous-version=15.0&new-version=16.0.3) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 237f5422f16..72b8a229e63 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -73,7 +73,7 @@ project.tasks.check.dependsOn(jarFileTest) dependencies { compile 'junit:junit:4.12' compile 'org.slf4j:slf4j-api:1.7.25' - compile 'org.jetbrains:annotations:15.0' + compile 'org.jetbrains:annotations:16.0.3' compile 'javax.annotation:javax.annotation-api:1.3.1' compile 'org.apache.commons:commons-compress:1.18' // Added for JDK9 compatibility since it's missing this package From 7740a4635df2de978bf9560d153d4a39648004a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 23 Dec 2018 17:04:00 +0000 Subject: [PATCH 0481/5615] Bump slf4j-api from 1.7.7 to 1.7.25 in /examples (#1034) Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 1.7.7 to 1.7.25.
Commits - [`a81440e`](https://github.com/qos-ch/slf4j/commit/a81440ea36676f3b090f5a4ccba70d17c7b80f6e) fix date issue in news.html - [`c2a7cca`](https://github.com/qos-ch/slf4j/commit/c2a7cca6cbbba49e452522d2fa3358f83ccb2c99) prepare release 1.7.25 - [`a848912`](https://github.com/qos-ch/slf4j/commit/a8489127b08bd5b8e1fa7ca5e5d33f93c0676986) change serialVersionUID since internals changed - [`2fc2dc1`](https://github.com/qos-ch/slf4j/commit/2fc2dc1deb3767b2c84a0d50eb5935f7fba59217) Merge pull request [#173](https://github-redirect.dependabot.com/qos-ch/slf4j/issues/173) from hazendaz/master - [`60eb95e`](https://github.com/qos-ch/slf4j/commit/60eb95e0f58728dea8e71f770cf5951ca8270ed2) [license] Update copyright year to 2017 - [`2f06933`](https://github.com/qos-ch/slf4j/commit/2f06933ccd22748fbe5d6ab45314eae5c586977b) use SLF4J formatter - [`97472ca`](https://github.com/qos-ch/slf4j/commit/97472ca10ae82c3244f5c8e6d6fea5847cb90731) fix deprecation issues - [`4a8c166`](https://github.com/qos-ch/slf4j/commit/4a8c16635cf59a88521fdcbba74735244dec9f6b) fix SLF4J-395 - [`509c344`](https://github.com/qos-ch/slf4j/commit/509c3444ac72913309999367782055fb40cbe184) fix SLF4J-394 - [`898e222`](https://github.com/qos-ch/slf4j/commit/898e22225909c4c84ef4885b6d346ca0a8565e38) type fix - Additional commits viewable in [compare view](https://github.com/qos-ch/slf4j/compare/v1.7.7...v_1.7.25)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=org.slf4j:slf4j-api&package-manager=maven&previous-version=1.7.7&new-version=1.7.25)](https://dependabot.com/compatibility-score.html?dependency-name=org.slf4j:slf4j-api&package-manager=maven&previous-version=1.7.7&new-version=1.7.25) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- examples/disque-job-queue/pom.xml | 2 +- examples/linked-container/pom.xml | 2 +- examples/redis-backed-cache-testng/pom.xml | 2 +- examples/redis-backed-cache/pom.xml | 2 +- examples/selenium-container/pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/disque-job-queue/pom.xml b/examples/disque-job-queue/pom.xml index 256584ba69c..fd117e2dd28 100644 --- a/examples/disque-job-queue/pom.xml +++ b/examples/disque-job-queue/pom.xml @@ -29,7 +29,7 @@ org.slf4j slf4j-api - 1.7.7 + 1.7.25 provided diff --git a/examples/linked-container/pom.xml b/examples/linked-container/pom.xml index 010a56e0315..fab65fb14bf 100644 --- a/examples/linked-container/pom.xml +++ b/examples/linked-container/pom.xml @@ -26,7 +26,7 @@ org.slf4j slf4j-api - 1.7.24 + 1.7.25 provided diff --git a/examples/redis-backed-cache-testng/pom.xml b/examples/redis-backed-cache-testng/pom.xml index 4da8266c9b6..f5115ab3b59 100644 --- a/examples/redis-backed-cache-testng/pom.xml +++ b/examples/redis-backed-cache-testng/pom.xml @@ -30,7 +30,7 @@ org.slf4j slf4j-api - 1.7.7 + 1.7.25 provided diff --git a/examples/redis-backed-cache/pom.xml b/examples/redis-backed-cache/pom.xml index bcb7646f1cf..6fa03969067 100644 --- a/examples/redis-backed-cache/pom.xml +++ b/examples/redis-backed-cache/pom.xml @@ -29,7 +29,7 @@ org.slf4j slf4j-api - 1.7.7 + 1.7.25 provided diff --git a/examples/selenium-container/pom.xml b/examples/selenium-container/pom.xml index 384a6f382af..ebc69dea3c3 100644 --- a/examples/selenium-container/pom.xml +++ b/examples/selenium-container/pom.xml @@ -35,7 +35,7 @@ org.slf4j slf4j-api - 1.7.7 + 1.7.25 provided From 3d7cef4d1b68b0ad94547db3491b943f13f18289 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 23 Dec 2018 21:38:51 +0100 Subject: [PATCH 0482/5615] Bump jedis from 2.8.0 to 3.0.0 in /core (#1031) Bumps [jedis](https://github.com/xetorthio/jedis) from 2.8.0 to 3.0.0. - [Release notes](https://github.com/xetorthio/jedis/releases) - [Commits](https://github.com/xetorthio/jedis/compare/jedis-2.8.0...jedis-3.0.0) Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 72b8a229e63..d999f20bea9 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -116,7 +116,7 @@ dependencies { exclude(module: '*') } - testCompile 'redis.clients:jedis:2.8.0' + testCompile 'redis.clients:jedis:3.0.0' testCompile 'com.rabbitmq:amqp-client:3.5.3' testCompile 'org.mongodb:mongo-java-driver:3.0.2' testCompile ('org.mockito:mockito-core:1.10.19') { From a43877a3b2f2dd90bcacbcf3ee25702d283d180b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 23 Dec 2018 21:10:45 +0000 Subject: [PATCH 0483/5615] Bump rest-assured from 3.0.0 to 3.2.0 in /modules/vault (#1044) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [rest-assured](https://github.com/jayway/rest-assured) from 3.0.0 to 3.2.0.
Changelog *Sourced from [rest-assured's changelog](https://github.com/rest-assured/rest-assured/blob/master/changelog.txt).* > Changelog 3.2.0 (2018-10-19) > ---------------------------- > * Added OSGi support (thanks to ponziani for pull request) > * Make it clear that junit is only a test scope dependency (thanks to Eric Pabst for pull request) > * Changed MockMvcParamConfig.attributeUpdateStrategy() to actually return the attributeUpdateStrategy and not the formParamsUpdateStrategy. > * Added a spring-web-test-client module which let's you unit test Spring Webflux Controllers using the REST Assured DSL. Depend on artifact id "spring-web-test-client" using group id "io.rest-assured" from Maven to use it. Then use io.restassured.module.webtestclient.RestAssuredWebTestClient to get started (thanks to Olga Maciaszek-Sharma for pull request) > * Upgraded to from Groovy 2.4.12 to 2.4.15 > * Upgraded Scala from version 2.11.12 to 2.12.7, this also means that the Scala support module now requires Java 8. > * Rest Assured now requires Java 6 (previously Java 5 was required). > * Moved XmlAssertion from io.restassured.assertion package to io.restassured.internal.path.xml since it's an internal class (issue 1051) > > Changelog 3.1.1 (2018-08-17) > ---------------------------- > * Ensure RequestSpecBuilder picks up static config (issue 1012). (thanks to Peter Major for pull request) > * Support multiple 'Set-Cookie' headers in a response (issue 1016). (thanks to runtarinn for pull request) > * Fixed OAuth 1 for GET requests (thanks to Corneliu Duplachi for pull request) > * Add detailed cookie matching to RequestSpecBuilder (thanks to Peter Major for pull request) > * Add File Size to InputStreamEntity (issue 988) (thanks to vjykumar for pull request) > * Properly encodes JSON from InputStream (issue 1040) (thanks to Maksymilian Pawlak for pull request) > * Implemented ability to add headers to a multipart using the MultiPartSpecBuilder > > Changelog 3.1.0 (2018-04-20) > ----------------------------- > * Fixed generics handling of the detailed cookie matcher (thanks to Rafał Siwiec for pull request) > * Now using Type instead of Class in the API for mapping to Java Objects. For users of the REST Assured API the change is most prominent in the > "ResponseBodyExtractionOptions" interface where the "as" method now takes a "java.lang.Type" instead of "java.lang.Class". This should not cause > any backward incompatibilities. However this change also applies to ObjectMapperFactory's where there is a chance of backward incompatibilities > to arise. For example if you previously had a custom JAXBObjectMapperFactory that looked like this: > > public class MyJAXBObjectMapperFactory implements JAXBObjectMapperFactory { > public JAXBContext create(Class cls, String charset) { > ... > } > } > > you now need to change it to: > > public class MyJAXBObjectMapperFactory implements JAXBObjectMapperFactory { > public JAXBContext create(Type cls, String charset) { > ... > } > } > > (note the change from Class to Type). This was needed for swagger integration (issue 980). (thanks to Victor Orlovsky for pull request) > * Add better integration for standard HTTP methods with Apache HttpClient which also solves an issue content-type header being generated for empty GET requests (issue 974) (thanks to Daniel Dyląg for pull request) > * No longer using DEF_CONTENT_CHARSET from Apache HttpClient since it caused compatibility issues (issue 757) > * Fix for [#979](https://github-redirect.dependabot.com/jayway/rest-assured/issues/979) Removing Authorization header when setting auth().none() (issue 979) (thanks to jovanovicivan for pull request) > * Fixed so that header equals is case-insensitive (issue 999) (thanks to Todd Bradley for pull request) > * Allow querying (extracting values out of) a request specification using the io.restassured.specification.SpecificationQuerier. For example: > RequestSpecification spec = ... > ... (truncated)
Commits - [`7393ba7`](https://github.com/rest-assured/rest-assured/commit/7393ba7bf1e23378700c648ad1d33bf904afc84f) [maven-release-plugin] prepare release rest-assured-3.2.0 - [`083a2cc`](https://github.com/rest-assured/rest-assured/commit/083a2ccbb6f257d4db0beb4323d0aa038100bc61) Preparing changelog for release 3.2.0 - [`4f21537`](https://github.com/rest-assured/rest-assured/commit/4f21537829c9988805f06f118bc4f9b1c2b0b44b) Trying another way to specify profiles - [`4178f9f`](https://github.com/rest-assured/rest-assured/commit/4178f9f2dd418202ebacecbda3396a452716dfdf) Caching m2 directory for fast build times - [`a1cda9f`](https://github.com/rest-assured/rest-assured/commit/a1cda9f79eca994ccb47cda81c84f47c6317dd46) Trying empty space in profile - [`c3fd06a`](https://github.com/rest-assured/rest-assured/commit/c3fd06ad8796a922161d72378b319fe61da0483d) Using jdk11 - [`b8bfbaa`](https://github.com/rest-assured/rest-assured/commit/b8bfbaae39da61c0906b1a6efe37be38811bf90b) Trying travis matrix config - [`c855e9d`](https://github.com/rest-assured/rest-assured/commit/c855e9ddc4f0a4a6080464378b08ba793af10a8d) Trying to fix travis tests (again) - [`68736e8`](https://github.com/rest-assured/rest-assured/commit/68736e8d42142ec3c11948ac1483080464047223) Updated changelog to reflect latest changes - [`571dc1a`](https://github.com/rest-assured/rest-assured/commit/571dc1ac436d3ac0391defe1ff33ae7acc09f197) Moved XmlPath to internal package where it belongs - Additional commits viewable in [compare view](https://github.com/jayway/rest-assured/compare/rest-assured-3.0.0...rest-assured-3.2.0)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=io.rest-assured:rest-assured&package-manager=gradle&previous-version=3.0.0&new-version=3.2.0)](https://dependabot.com/compatibility-score.html?dependency-name=io.rest-assured:rest-assured&package-manager=gradle&previous-version=3.0.0&new-version=3.2.0) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index abff412b9a9..efb4e61acb0 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: Vault" dependencies { compile project(':testcontainers') - testCompile 'io.rest-assured:rest-assured:3.0.0' + testCompile 'io.rest-assured:rest-assured:3.2.0' } From b7dc39af2beff78b8b3866a4f84c5b1772467592 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 23 Dec 2018 21:20:12 +0000 Subject: [PATCH 0484/5615] Bump vibur-dbcp from 9.0 to 22.2 in /modules/mysql (#1042) Bumps [vibur-dbcp](https://github.com/vibur/vibur-dbcp) from 9.0 to 22.2.
Changelog *Sourced from [vibur-dbcp's changelog](https://github.com/vibur/vibur-dbcp/blob/master/CHANGELOG).* > Version 22.2 (05/05/2018) - RECOMMENDED > ======================================= > * Small performance update. > > Version 22.1 (30/03/2018) > ========================= > * Adopted changes / improvements from Vibur Object Pool 22.1. > * Various other small improvements, refactoring, and javadoc updates. > > Version 22.0 (08/02/2018) > ========================= > * Added logLineRegex configuration option - the application can use this option in conjunction with the > logTakenConnectionsOnTimeout option in order to filter the generated stack trace lines. > * Added takenConnectionsFormatter configuration option - allows the application to implement custom formatting > to String of the TakenConnection[] array. Note that the default formatter uses the logLineRegex for filtering. > * Added GetConnectionTimeout programming hook that is invoked only if the call to DataSource.getConnection() > timeouts. The application can use this hook to format and redirect the generated taken-connections log to a different > than the usual log location, for example, to Amazon S3. Note that in order to enable the taken-connections tracking > the application must set poolEnableConnectionTracking to true. The default hook uses the takenConnectionsFormatter; > it can be disabled via setting logTakenConnectionsOnTimeout to false. > * Fixed a bug in the CloseConnection hook takenNanos parameter calculation, before its value was sometimes incorrect. > > Version 21.3 (28/01/2018) > ========================= > * Fixed a minor bug in the ResultSet size calculation where the size was sometimes calculated off by one. > > Version 21.2 (29/11/2017) > ========================= > * Fixed a bug in the implicit closing of the current ResultSet functionality, introduced in version 21.0. > * Fixed a rarely occurring bug related to the possibility of Statement.getResultSet() to return null. > > Version 21.1 (27/11/2017) > ========================= > * Added resultSetNanoTime parameter to the ResultSetRetrieval programming hook. This parameter complements the existing > resultSetSize parameter and represents the total time taken to iterate over and process the retrieved ResultSet. > > Version 21.0 (22/11/2017) > ========================= > * Moved the getConnection() retry mechanism (for lazily created connections) from the ConnectionFactory level > to the PoolOperations level - this improves the connections reuse in certain situations. > * Implemented implicit closing of the current ResultSet when the parent Statement is closed. > * Changed some configuration defaults: > a) poolInitialSize 10 -> 5; > b) poolMaxSize 100 -> 50; > c) reducerTimeIntervalInSeconds 60 -> 30; > d) reducerSamples 20 -> 15; > e) connectionTimeoutInMs 30_000 -> 15_000; > f) acquireRetryDelayInMs 1000 -> 500; > * Adopted changes / improvements from Vibur Object Pool 21.0. > * Various other small fixes, refactoring and improvements. > ... (truncated)
Commits - [`a3bd654`](https://github.com/vibur/vibur-dbcp/commit/a3bd6540856d5d9f1bfcf4d3da62c22e73e1f2d5) [maven-release-plugin] prepare release 22.2 - [`d8eb05c`](https://github.com/vibur/vibur-dbcp/commit/d8eb05c39740c1e08b8c3015238a6b1b52c75b1c) osgi update - [`1751f77`](https://github.com/vibur/vibur-dbcp/commit/1751f7748f03cc84ba0292379eadb72b014adef8) prepare release 22.2 - [`a61e28c`](https://github.com/vibur/vibur-dbcp/commit/a61e28cd06ef3a62dae9b5783fceb4c416c51b89) updated contributors - [`8857333`](https://github.com/vibur/vibur-dbcp/commit/885733396aa7e1854daeffcf8dff0ac7afbfb18b) Automated by IntelliJ refactoring to introduce curly braces after if/else/for... - [`b79116b`](https://github.com/vibur/vibur-dbcp/commit/b79116b2dee3150cefe40048c551657a6611a09e) Merge pull request [#10](https://github-redirect.dependabot.com/vibur/vibur-dbcp/issues/10) from rbalamohan/issue/9 - [`7b183e4`](https://github.com/vibur/vibur-dbcp/commit/7b183e40775b68e533965e39c1de7fa34cccd7ed) Issue 9: Consider optimising AbstractInvocationHandler::invoke - [`f602813`](https://github.com/vibur/vibur-dbcp/commit/f6028137ffc32c57550a6b8509e05ac6930ce1b8) Issue 9: Consider optimising AbstractInvocationHandler::invoke - [`3eea6b1`](https://github.com/vibur/vibur-dbcp/commit/3eea6b1ffd918d927152f8ffe36aec3e0c2871b2) Issue 9: Consider optimising AbstractInvocationHandler::invoke - [`5b3349a`](https://github.com/vibur/vibur-dbcp/commit/5b3349a039c7603c1e673cee2d9954f05f7ab8cb) Issue 9: Consider optimising AbstractInvocationHandler::invoke - Additional commits viewable in [compare view](https://github.com/vibur/vibur-dbcp/compare/9.0...22.2)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=org.vibur:vibur-dbcp&package-manager=gradle&previous-version=9.0&new-version=22.2)](https://dependabot.com/compatibility-score.html?dependency-name=org.vibur:vibur-dbcp&package-manager=gradle&previous-version=9.0&new-version=22.2) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index ef67e0729d4..03bd0156d51 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -7,5 +7,5 @@ dependencies { testCompile 'com.zaxxer:HikariCP-java6:2.3.8' testCompile 'commons-dbutils:commons-dbutils:1.6' testCompile 'org.apache.tomcat:tomcat-jdbc:8.5.4' - testCompile 'org.vibur:vibur-dbcp:9.0' + testCompile 'org.vibur:vibur-dbcp:22.2' } From 6c4514f2db1d911cd5f135b5e3b80ec146acf6d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 23 Dec 2018 21:21:00 +0000 Subject: [PATCH 0485/5615] Bump postgresql from 9.4.1212 to 42.2.5 in /examples (#1037) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 9.4.1212 to 42.2.5.
Changelog *Sourced from [postgresql's changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md).* > ## [42.2.5] (2018-08-27) > ### Known issues > - 1ms per async copy call [issue 1312](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/issues/1312) > > ### Changed > - `ssl=true` implies `sslmode=verify-full`, that is it requires valid server certificate [cdeeaca4](https://github.com/pgjdbc/pgjdbc/commit/cdeeaca47dc3bc6f727c79a582c9e4123099526e) > > ### Added > - Support for `sslmode=allow/prefer/require` [cdeeaca4](https://github.com/pgjdbc/pgjdbc/commit/cdeeaca47dc3bc6f727c79a582c9e4123099526e) > > ### Fixed > - Security: added server hostname verification for non-default SSL factories in `sslmode=verify-full` (CVE-2018-10936) [cdeeaca4](https://github.com/pgjdbc/pgjdbc/commit/cdeeaca47dc3bc6f727c79a582c9e4123099526e) > - Updated documentation on SSL configuration [fa032732](https://github.com/pgjdbc/pgjdbc/commit/fa032732acfe51c6e663ee646dd5c1beaa1af857) > - Updated Japanese translations [PR 1275](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1275) > - IndexOutOfBounds on prepared multistatement with insert values [c2885dd0](https://github.com/pgjdbc/pgjdbc/commit/c2885dd0cfc793f81e5dd3ed2300bb32476eb14a) > > ## [42.2.4] (2018-07-14) > ### Changed > - PreparedStatement.setNull(int parameterIndex, int t, String typeName) no longer ignores the typeName > argument if it is not null [PR 1160](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1160) > > ### Fixed > - Fix treatment of SQL_TSI_YEAR, SQL_TSI_WEEK, SQL_TSI_MINUTE [PR 1250](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1250) > - Map integrity constraint violation to XA_RBINTEGRITY instead of XAER_RMFAIL [PR 1175](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1175) [f2d1352c](https://github.com/pgjdbc/pgjdbc/commit/f2d1352c2b3ea98492beb6127cd6d95039a0b92f) > > ## [42.2.3] (2018-07-12) > ### Known issues > - SQL_TSI_YEAR is treated as hour, SQL_TSI_WEEK is treated as hour, SQL_TSI_MINUTE is treated as second > > ### Changed > - Reduce the severity of the error log messages when an exception is re-thrown. The error will be > thrown to caller to be dealt with so no need to log at this verbosity by pgjdbc [PR 1187](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1187) > - Deprecate Fastpath API [PR 903](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/903) > - Support parenthesis in {oj ...} JDBC escape syntax [PR 1204](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1204) > - ubenchmark module moved pgjdbc/benchmarks repository due to licensing issues [PR 1215](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1215) > - Include section on how to submit a bug report in CONTRIBUTING.md [PR 951](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/951) > > ### Fixed > - getString for PGObject-based types returned "null" string instead of null [PR 1154](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1154) > - Field metadata cache can be disabled via databaseMetadataCacheFields=0 [PR 1052](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1052) > - Properly encode special symbols in passwords in BaseDataSource [PR 1201](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1201) > - Adjust date, hour, minute, second when rounding nanosecond part of a timestamp [PR 1212](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1212) > - perf: reduce memory allocations in query cache [PR 1227](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1227) > - perf: reduce memory allocations in SQL parser [PR 1230](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1230), [PR 1233](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1233) > - Encode URL parameters in BaseDataSource [PR 1201](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1201) > - Improve JavaDoc formatting [PR 1236](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1236) > > ## [42.2.2] (2018-03-15) > ### Added > - Documentation on server-side prepared statements [PR 1135](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1135) > ... (truncated)
Commits - [`a1a5ae4`](https://github.com/pgjdbc/pgjdbc/commit/a1a5ae4f2283d4557f36756d1a0228310a3acccb) [maven-release-plugin] prepare release REL42.2.5 - [`c8a639e`](https://github.com/pgjdbc/pgjdbc/commit/c8a639edbd0cf39453048f1fd7d9412ec8e09293) docs: update site for 42.2.5 - [`237a89b`](https://github.com/pgjdbc/pgjdbc/commit/237a89bf3058a16a3de37b8c92d2a4d850c6c056) Update changelog for 42.2.5 - [`fa03273`](https://github.com/pgjdbc/pgjdbc/commit/fa032732acfe51c6e663ee646dd5c1beaa1af857) docs: improve documentation on SSL - [`cdeeaca`](https://github.com/pgjdbc/pgjdbc/commit/cdeeaca47dc3bc6f727c79a582c9e4123099526e) security: implement SSL hostname verification for non-default (LibPQFactory) ... - [`c2885dd`](https://github.com/pgjdbc/pgjdbc/commit/c2885dd0cfc793f81e5dd3ed2300bb32476eb14a) fix: IndexOutOfBounds on prepared multistatement with insert values - [`9534e9c`](https://github.com/pgjdbc/pgjdbc/commit/9534e9ca0e1840445ad5f4eee75bc1e2ac102dde) docs: fix typos detected by github.com/client9/misspell ([#1287](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/issues/1287)) - [`203a106`](https://github.com/pgjdbc/pgjdbc/commit/203a106ddc9eb0d94cc94838f4fb0924e37f441a) fix: Correct typo in CopyManager comment ([#1285](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/issues/1285)) - [`b5c19af`](https://github.com/pgjdbc/pgjdbc/commit/b5c19af627c8650410495ad8e3f2ee85e687e3c1) move issue template and pull request template into github specific di… ([#1283](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/issues/1283)) - [`c66bf71`](https://github.com/pgjdbc/pgjdbc/commit/c66bf7108dd36f50aacebfd4f09e383aed02424b) Add issue templates ([#1263](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/issues/1263)) - Additional commits viewable in [compare view](https://github.com/pgjdbc/pgjdbc/compare/REL9.4.1212...REL42.2.5)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=org.postgresql:postgresql&package-manager=maven&previous-version=9.4.1212&new-version=42.2.5)](https://dependabot.com/compatibility-score.html?dependency-name=org.postgresql:postgresql&package-manager=maven&previous-version=9.4.1212&new-version=42.2.5) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- examples/linked-container/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/pom.xml b/examples/linked-container/pom.xml index fab65fb14bf..af9e9e40328 100644 --- a/examples/linked-container/pom.xml +++ b/examples/linked-container/pom.xml @@ -20,7 +20,7 @@ org.postgresql postgresql - 9.4.1212 + 42.2.5 test From 70ad90aa8632c478d8453a37f7b95e8d9b847f72 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 23 Dec 2018 21:39:04 +0000 Subject: [PATCH 0486/5615] Bump lombok from 1.16.6 to 1.18.4 in /examples (#1039) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [lombok](https://github.com/rzwitserloot/lombok) from 1.16.6 to 1.18.4.
Changelog *Sourced from [lombok's changelog](https://github.com/rzwitserloot/lombok/blob/master/doc/changelog.markdown).* > ### v1.18.4 (October 30th, 2018) > * PLATFORM: Support for Eclipse Photon. [Issue #1831](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1831) > * PLATFORM: Angular IDE is now recognized by the installer [Issue #1830](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1830) > * PLATFORM: Many improvements for lombok's JDK10/11 support. > * BREAKING CHANGE: The `[**FieldNameConstants**](https://github.com/FieldNameConstants)` feature has been completely redesigned. [Issue #1774](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1774) [FieldNameConstants documentation](https://projectlombok.org/features/experimental/FieldNameConstants) > * BREAKING CHANGE: Lombok will now always copy specific annotations around (from field to getter, from field to builder 'setter', etcetera): A specific curated list of known annotations where that is the right thing to do (generally, `[**NonNull**](https://github.com/NonNull)` style annotations from various libraries), as well as any annotations you explicitly list in the `lombok.copyableAnnotations` config key in your `lombok.config` file. Also, lombok is more consistent about copying these annotations. (Previous behaviour: Lombok used to copy any annotation whose simple name was `NonNull`, `Nullable`, or `CheckForNull`). [Issue #1570](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1570) and [Issue #1634](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1634) > * FEATURE: Lombok's `[**NonNull**](https://github.com/NonNull)` annotation can now be used on type usages (annotation on type usages has been introduced in JDK 8). `[**Builder**](https://github.com/Builder)`'s `[**Singular**](https://github.com/Singular)` annotation now properly deals with annotations on the generics type on the collection: `[**Singular**](https://github.com/Singular) List<[**NonNull**](https://github.com/NonNull) String> names;` now does the right thing. > * FEATURE: You can now mix `[**SuperBuilder**](https://github.com/SuperBuilder)` and `toBuilder`, and `toBuilder` no longer throws `NullPointerException` if a `[**Singular**](https://github.com/Singular)`-marked collection field is `null`. [Issue #1324](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1324) > * FEATURE: delombok now supports module paths via the `--module-path` option, and will automatically add lombok itself to the module path. This should make it possible to delombok your modularized projects. [Issue #1848](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1848) > * FEATURE: You can pass `[**args**](https://github.com/args).txt` to `delombok` to read args from the text file; useful if you have really long classpaths you need to pass to delombok. [Issue #1795](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1795) > * BUGFIX: `[**NoArgsConstructor**](https://github.com/NoArgsConstructor)(force=true)` would try to initialize already initialized final fields in Eclipse. [Issue #1829](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1829) > * BUGFIX: When using lombok to compile modularized (`module-info.java`-style) code, if the module name has dots in it, it wouldn't work. [Issue #1808](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1808) > * BUGFIX: Errors about lombok not reading a module providing `org.mapstruct.ap.spi` when trying to use lombok in jigsaw-mode on JDK 11. [Issue #1806](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1806) > * BUGFIX: Fix NetBeans compile on save. [Issue #1770](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1770) > * BUGFIX: If you manually write your builder class so you can add a few methods of your own, and those methods refer to generated methods, you'd usually run into various bizarre error messages, but only on JDK9/10/11. This one is hard to describe, but we fixed it. [Issue #1907](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1907) > > > ### v1.18.2 (July 26th, 2018) > * BUGFIX: mapstruct + lombok in eclipse should hopefully work again. [Issue #1359](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1359) and [mapstruct issue #1159](https://github-redirect.dependabot.com/mapstruct/mapstruct/issues/1159) > * BUGFIX: Equals and hashCode again exclude transient fields by default. [Issue #1724](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1724) > * BUGFIX: Eclipse 'organize imports' feature (either explicitly, or if automatically triggered on saving via 'save actions') would remove the import for `lombok.var`. [Issue #1783](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1783) > * BUGFIX: Lombok and gradle v4.9 didn't work together; that's been fixed. [Issue #1716](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1716) and [gradle-apt-plugin issue #87](https://github-redirect.dependabot.com/tbroyer/gradle-apt-plugin/issues/87) > * FEATURE: You can now make builders for type hierarchies, using the new (experimental) `[**SuperBuilder**](https://github.com/SuperBuilder)` annotation. Thanks for the contribution, Jan Rieke. [`[**SuperBuilder**](https://github.com/SuperBuilder)` documentation](https://projectlombok.org/features/experimental/SuperBuilder) > * FEATURE: `[**NoArgsConstructor**](https://github.com/NoArgsConstructor)`, including forcing one with `lombok.config: lombok.noArgsConstructor.extraPrivate=true` now take any defaults set with `[**Builder**](https://github.com/Builder).Default` into account. [Issue #1347](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1347) > ### v1.18.0 (June 5th, 2018) > * BREAKING CHANGE: The in 1.16.22 introduced configuration key `lombok.noArgsConstructor.extraPrivate` is now `false` by default. [Issue #1708](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1708) > * BUGFIX: Do not generate a private no-args constructor if that breaks the code. [Issue #1703](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1703), [Issue #1704](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1704), [Issue #1712](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1712) > * BUGFIX: Using boolean parameters in lombok annotations would fail. [Issue #1709](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1709) > * BUGFIX: Delombok would give an error message. [Issue #1705](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1705) > * BUGFIX: Eclipse java10 var support didn't work if lombok was installed in your eclipse. [Issue #1676](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1676) > * FEATURE: Google's [Flogger (a.k.a. FluentLogger)](https://google.github.io/flogger/) is now available via `[**Flogger**](https://github.com/Flogger)`. [Issue #1697](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1697) > * FEATURE: `[**FieldNameConstants**](https://github.com/FieldNameConstants)` has been extended to support prefixes and suffixes. By default, the generated constants are prefixed with `FIELD_`. [Docs on [**FieldNameConstants**](https://github.com/FieldNameConstants)](https://projectlombok.org/features/experimental/FieldNameConstants). > > ### v1.16.22 "Envious Ferret" (May 29th, 2018) > * FEATURE: Private no-args constructor for `[**Data**](https://github.com/Data)` and `[**Value**](https://github.com/Value)` to enable deserialization frameworks (like Jackson) to operate out-of-the-box. Use `lombok.noArgsConstructor.extraPrivate = false` to disable this behavior. > * FEATURE: Methods can now be marked for inclusion in `toString`, `equals`, and `hashCode` generation. There is a new mechanism to mark which fields (and now, methods) are to be included or excluded for the generation of these methods: mark the relevant member with for example `[**ToString**](https://github.com/ToString).Include` or `[**EqualsAndHashCode**](https://github.com/EqualsAndHashCode).Exclude`. [ToString documentation](https://projectlombok.org/features/ToString) [EqualsAndHashCode documentation](https://projectlombok.org/features/EqualsAndHashCode) > * FEATURE: `[**Getter**](https://github.com/Getter)` and `[**Setter**](https://github.com/Setter)` also allow `onMethod` and `onParam` when put on a type. [Issue #1653](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1653) > * FEATURE: `[**FieldNameConstants**](https://github.com/FieldNameConstants)` is a new feature that generates string constants for your field names. [Docs on [**FieldNameConstants**](https://github.com/FieldNameConstants)](https://projectlombok.org/features/experimental/FieldNameConstants). > * PLATFORM: Lombok can be compiled on JDK10, and should run on JDK10. [Issue #1693](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1693) > * PLATFORM: lombok now counts as an _incremental annotation processor_ for gradle. Should speed up your gradle builds considerably! [Issue #1580](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1580) > * PLATFORM: Fix for using lombok together with JDK9+'s new `module-info.java` feature. [Issue #985](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/985) > * BUGFIX: Solved some issues in eclipse that resulted in error 'A save participant caused problems'. [Issue #879](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/879) > * BUGFIX: Netbeans on jdk9. [Issue #1617](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1617) > * BUGFIX: Netbeans < 9. [Issue #1555](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1555) > * PROMOTION: `var` has been promoted from experimental to the main package with no changes. The 'old' experimental one is still around but is deprecated, and is an alias for the new main package one. [var documentation](https://projectlombok.org/features/var.html). > * OLD-CRUFT: `lombok.experimental.Builder` and `lombok.experimental.Value` are deprecated remnants of when these features were still in experimental. They are now removed entirely. If your project is dependent on an older version of lombok which still has those; fret not, lombok still processes these annotations. It just no longer includes them in the jar. > > ### v1.16.20 (January 9th, 2018) > * PLATFORM: Better support for jdk9 in the new IntelliJ, Netbeans and for Gradle. > * BREAKING CHANGE: _lombok config_ key `lombok.addJavaxGeneratedAnnotation` now defaults to `false` instead of true. Oracle broke this annotation with the release of JDK9, necessitating this breaking change. > ... (truncated)
Commits - [`8451c88`](https://github.com/rzwitserloot/lombok/commit/8451c88bfa7667d4bc85fb9d1b88196ff8dc6b7c) pre-release version bump - [`e960f48`](https://github.com/rzwitserloot/lombok/commit/e960f482a92755f87c67ae59c53bb05890ab58c1) cleaning up the changelog in preparation for a release. - [`9b06018`](https://github.com/rzwitserloot/lombok/commit/9b0601809b221ceb9fdbd6789354ae6b1ac32180) [fixes #1907] This one is hard to describe; due to builder being a bit overze... - [`f5b1069`](https://github.com/rzwitserloot/lombok/commit/f5b106971a6e27b2d97cbda250b7e79d8561fab5) add jdk11 to docker builds - [`0336537`](https://github.com/rzwitserloot/lombok/commit/03365378e91414a5a475cb234ca86ef2697ce9a9) fixing the tests added in the previous commits by janrieke to match alternati... - [`d8de0e3`](https://github.com/rzwitserloot/lombok/commit/d8de0e3c49c97450c3cd78a7d1766a8f21150591) Merge branch 'wildcardsSingularFix' of git://github.com/janrieke/lombok into ... - [`eca219e`](https://github.com/rzwitserloot/lombok/commit/eca219ee6433cd964f0549a114a791ca4eb9f0fa) eliminate ‘you are using private API’ warnings by streamlining all reflective... - [`182cb0c`](https://github.com/rzwitserloot/lombok/commit/182cb0cb9e8db6341fb4633c3849b5e90ba6d088) [java-11] up dependency on lombok.patcher, including asm7 - [`c02263a`](https://github.com/rzwitserloot/lombok/commit/c02263a5e296d068a7e8468a5f797480d54cbcb4) Merge pull request [#1923](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1923) from abargnesi/fix-maven-issue-management - [`4cf36b2`](https://github.com/rzwitserloot/lombok/commit/4cf36b2788198e23f05a2ac218a2face755056ba) Merge pull request [#1917](https://github-redirect.dependabot.com/rzwitserloot/lombok/issues/1917) from kkocel/master - Additional commits viewable in [compare view](https://github.com/rzwitserloot/lombok/compare/v1.16.6...v1.18.4)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=org.projectlombok:lombok&package-manager=maven&previous-version=1.16.6&new-version=1.18.4)](https://dependabot.com/compatibility-score.html?dependency-name=org.projectlombok:lombok&package-manager=maven&previous-version=1.16.6&new-version=1.18.4) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- examples/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/pom.xml b/examples/pom.xml index 63fea191f18..c26d36efe3d 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -34,7 +34,7 @@ org.projectlombok lombok - 1.16.6 + 1.18.4 provided From b4e60867ea59ca12cbc71c1abe09f514c65ac41a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 23 Dec 2018 21:40:04 +0000 Subject: [PATCH 0487/5615] Bump aws-java-sdk-s3 from 1.11.126 to 1.11.475 in /modules/localstack (#1046) Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.11.126 to 1.11.475.
Changelog *Sourced from [aws-java-sdk-s3's changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md).* > # __1.11.475__ __2018-12-21__ > ## __AWS Certificate Manager Private Certificate Authority__ > - ### Features > - This release marks the introduction of waiters in ACM PCA, which allow you to control the progression of your code based on the presence or state of certain resources. Waiters can be implemented in the DescribeCertificateAuthorityAuditReport, GetCertificate, and GetCertificateAuthorityCsr API operations. > > ## __AWS Step Functions__ > - ### Features > - This release adds support for cost allocation tagging. You can now create, delete, and list tags for AWS Step Functions activity and state machine resources. For more information about tagging, see AWS Tagging Strategies. > > ## __Amazon DynamoDB__ > - ### Features > - Added provisionedThroughPut exception on the request level for transaction APIs. > > ## __Amazon Pinpoint SMS and Voice Service__ > - ### Features > - Configuration sets can now use Amazon SNS as an event destination. > > # __1.11.474__ __2018-12-20__ > ## __AWS Elemental MediaLive__ > - ### Features > - This release provides support for ID3 tags and video quality setting for subgop_length. > > ## __Amazon Cognito Identity Provider__ > - ### Features > - Amazon Cognito now has API support for updating the Secure Sockets Layer (SSL) certificate for the custom domain for your user pool. > > ## __Amazon Comprehend__ > - ### Features > - This SDK release adds functionality to stop training Custom Document Classifier or Custom Entity Recognizer in Amazon Comprehend. > > ## __Amazon Kinesis Firehose__ > - ### Features > - Support for specifying customized s3 keys and supplying a separate prefix for failed-records > > ## __Amazon Transcribe Service__ > - ### Features > - With this release, Amazon Transcribe now supports transcriptions from audio sources in Italian (it-IT). > > # __1.11.473__ __2018-12-19__ > ## __AWS WAF__ > - ### Features > - This release adds rule-level control for rule group. If a rule group contains a rule that blocks legitimate traffic, previously you had to override the entire rule group to COUNT in order to allow the traffic. You can now use the UpdateWebACL API to exclude specific rules within a rule group. Excluding rules changes the action for the individual rules to COUNT. Excluded rules will be recorded in the new "excludedRules" attribute of the WAF logs. > > ## __AWS WAF Regional__ > - ### Features > - This release adds rule-level control for rule group. If a rule group contains a rule that blocks legitimate traffic, previously you had to override the entire rule group to COUNT in order to allow the traffic. You can now use the UpdateWebACL API to exclude specific rules within a rule group. Excluding rules changes the action for the individual rules to COUNT. Excluded rules will be recorded in the new "excludedRules" attribute of the WAF logs. > > ## __Amazon Elastic Compute Cloud__ > - ### Features > - This release adds support for specifying partition as a strategy for EC2 Placement Groups. This new strategy allows one to launch instances into partitions that do not share certain underlying hardware between partitions, to assist with building and deploying highly available replicated applications. > ... (truncated)
Commits - See full diff in [compare view](https://github.com/aws/aws-sdk-java/commits/1.11.475)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=com.amazonaws:aws-java-sdk-s3&package-manager=gradle&previous-version=1.11.126&new-version=1.11.475)](https://dependabot.com/compatibility-score.html?dependency-name=com.amazonaws:aws-java-sdk-s3&package-manager=gradle&previous-version=1.11.126&new-version=1.11.475) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 94f0ee4070f..7ba8ac8f175 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.126' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.475' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.126' } From e93fe7159ce2d332060bfa7da57c113740cb96f6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 23 Dec 2018 21:41:01 +0000 Subject: [PATCH 0488/5615] Bump okhttp from 3.6.0 to 3.12.0 in /examples (#1035) Bumps [okhttp](https://github.com/square/okhttp) from 3.6.0 to 3.12.0.
Changelog *Sourced from [okhttp's changelog](https://github.com/square/okhttp/blob/master/CHANGELOG.md).* > ## Version 3.12.0 > > _2018-11-16_ > > * **OkHttp now supports TLS 1.3.** This requires either Conscrypt or Java 11+. > > * **Proxy authenticators are now asked for preemptive authentication.** OkHttp will now request > authentication credentials before creating TLS tunnels through HTTP proxies (HTTP `CONNECT`). > Authenticators should identify preemptive authentications by the presence of a challenge whose > scheme is "OkHttp-Preemptive". > > * **OkHttp now offers full-operation timeouts.** This sets a limit on how long the entire call may > take and covers resolving DNS, connecting, writing the request body, server processing, and > reading the full response body. If a call requires redirects or retries all must complete within > one timeout period. > > Use `OkHttpClient.Builder.callTimeout()` to specify the default duration and `Call.timeout()` to > specify the timeout of an individual call. > > * New: Return values and fields are now non-null unless otherwise annotated. > * New: `LoggingEventListener` makes it easy to get basic visibility into a call's performance. > This class is in the `logging-interceptor` artifact. > * New: `Headers.Builder.addUnsafeNonAscii()` allows non-ASCII values to be added without an > immediate exception. > * New: Headers can be redacted in `HttpLoggingInterceptor`. > * New: `Headers.Builder` now accepts dates. > * New: OkHttp now accepts `java.time.Duration` for timeouts on Java 8+ and Android 26+. > * New: `Challenge` includes all authentication parameters. > * New: Upgrade to BouncyCastle 1.60, Conscrypt 1.4.0, and Okio 1.15.0. We don't yet require > Kotlin-friendly Okio 2.x but OkHttp works fine with that series. > > ```kotlin > implementation("org.bouncycastle:bcprov-jdk15on:1.60") > implementation("org.conscrypt:conscrypt-openjdk-uber:1.4.0") > implementation("com.squareup.okio:okio:1.15.0") > ``` > > * Fix: Handle dispatcher executor shutdowns gracefully. When there aren't any threads to carry a > call its callback now gets a `RejectedExecutionException`. > * Fix: Don't permanently cache responses with `Cache-Control: immutable`. We misunderstood the > original `immutable` proposal! > * Fix: Change `Authenticator`'s `Route` parameter to be nullable. This was marked as non-null but > could be called with null in some cases. > * Fix: Don't create malformed URLs when `MockWebServer` is reached via an IPv6 address. > * Fix: Don't crash if the system default authenticator is null. > * Fix: Don't crash generating elliptic curve certificates on Android. > * Fix: Don't crash doing platform detection on RoboVM. > * Fix: Don't leak socket connections when web socket upgrades fail. > > > ... (truncated)
Commits - [`7f63a35`](https://github.com/square/okhttp/commit/7f63a35ab1a8344279d2e84e07884a45f45f0690) [maven-release-plugin] prepare release parent-3.12.0 - [`9e195fa`](https://github.com/square/okhttp/commit/9e195fa37d0e1a48fdb2c7d8457a4177238c66fe) Update changelog for OkHttp 3.12. - [`2198975`](https://github.com/square/okhttp/commit/2198975a425c57e1211cc366a42f0465776469b1) Merge pull request [#4388](https://github-redirect.dependabot.com/square/okhttp/issues/4388) from square/jwilson.1114.use_ec_not_ecdsa - [`d062472`](https://github.com/square/okhttp/commit/d062472253f4936c2e9aae2d858290e692295f05) Specify "EC" to generate eliptic curve keys, not ECDSA - [`1f7e796`](https://github.com/square/okhttp/commit/1f7e796e6e658df34a98276b2092a81de118937d) Cleanup HttpLoggingInterceptor ([#4346](https://github-redirect.dependabot.com/square/okhttp/issues/4346)) - [`ef34a41`](https://github.com/square/okhttp/commit/ef34a41d09fbcc8e03c179cb9b121c918f671f88) Add a LoggingEventListener and use it in okcurl ([#4353](https://github-redirect.dependabot.com/square/okhttp/issues/4353)) - [`8a01554`](https://github.com/square/okhttp/commit/8a01554770d10062bb9d24176a8166e88412fbe2) Merge pull request [#4376](https://github-redirect.dependabot.com/square/okhttp/issues/4376) from square/jwilson.1107.preemptive_auth - [`5a316c0`](https://github.com/square/okhttp/commit/5a316c0b403809558d9a2227a4d8820642ec34f5) Preemptive auth for proxy CONNECT - [`568a91c`](https://github.com/square/okhttp/commit/568a91c44a118b2c2ba62d310a331582c567b24a) Merge pull request [#4381](https://github-redirect.dependabot.com/square/okhttp/issues/4381) from square/jwilson.1112.weaken_immutable - [`764b31b`](https://github.com/square/okhttp/commit/764b31b3e2367dcadacc08b818739b065b1c4bef) Relax handling of Cache-Control: immutable - Additional commits viewable in [compare view](https://github.com/square/okhttp/compare/parent-3.6.0...parent-3.12.0)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=com.squareup.okhttp3:okhttp&package-manager=maven&previous-version=3.6.0&new-version=3.12.0)](https://dependabot.com/compatibility-score.html?dependency-name=com.squareup.okhttp3:okhttp&package-manager=maven&previous-version=3.6.0&new-version=3.12.0) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- examples/linked-container/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/pom.xml b/examples/linked-container/pom.xml index af9e9e40328..3ee79bf6d51 100644 --- a/examples/linked-container/pom.xml +++ b/examples/linked-container/pom.xml @@ -38,7 +38,7 @@ com.squareup.okhttp3 okhttp - 3.6.0 + 3.12.0 org.json From 703c4b3ba5450d01a569ff2b7ecacebfaecad818 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 23 Dec 2018 21:43:33 +0000 Subject: [PATCH 0489/5615] Bump jaxb-api from 2.3.0 to 2.3.1 in /core (#1029) Bumps [jaxb-api](https://github.com/javaee/jaxb-spec) from 2.3.0 to 2.3.1.
Commits - [`7de2ca1`](https://github.com/javaee/jaxb-spec/commit/7de2ca118a0cfc4a373872915aef59148dff5f93) Preparing for release 2.3.1 - [`5cbac0c`](https://github.com/javaee/jaxb-spec/commit/5cbac0ca2e5c136ffa888d0df86b437e3ed6a590) Merge pull request [#62](https://github-redirect.dependabot.com/javaee/jaxb-spec/issues/62) from lukasj/fix - [`c61c8df`](https://github.com/javaee/jaxb-spec/commit/c61c8df68b40cd719aa65545f9e7f203f6e882da) fix cp year - [`bd6cdf6`](https://github.com/javaee/jaxb-spec/commit/bd6cdf69bcfaac10e81709345ebc82f9ea4a5264) Revert "Removed usage of .internal package pointing to JavaSE." - [`4d09bb1`](https://github.com/javaee/jaxb-spec/commit/4d09bb12f5e21f473971e9bb23a8247d3b38a64a) allow running in OSGi on newer jdks - [`e9625c8`](https://github.com/javaee/jaxb-spec/commit/e9625c89af6fc1cf78db5066bb7f2e752254173a) Preparing for development 2.3.2-SNAPSHOT - [`9ef650f`](https://github.com/javaee/jaxb-spec/commit/9ef650f2416263068742df413b171322733d301b) Preparing for release 2.3.1-RC1 - [`7acf248`](https://github.com/javaee/jaxb-spec/commit/7acf248d5e09d9e8faea076058570ab526edfe6e) Preparing for development 2.3.2-SNAPSHOT - [`c44722d`](https://github.com/javaee/jaxb-spec/commit/c44722d4318b00cccdb8f7eb830d631620486b5e) Preparing for release 2.3.1-RC1 - [`f786240`](https://github.com/javaee/jaxb-spec/commit/f7862405fb19cde4749df0bf7a03a3065dc40033) Preparing for development 2.3.2-SNAPSHOT - Additional commits viewable in [compare view](https://github.com/javaee/jaxb-spec/compare/2.3.0...2.3.1)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=javax.xml.bind:jaxb-api&package-manager=gradle&previous-version=2.3.0&new-version=2.3.1)](https://dependabot.com/compatibility-score.html?dependency-name=javax.xml.bind:jaxb-api&package-manager=gradle&previous-version=2.3.0&new-version=2.3.1) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index d999f20bea9..0af9c412fd2 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -77,7 +77,7 @@ dependencies { compile 'javax.annotation:javax.annotation-api:1.3.1' compile 'org.apache.commons:commons-compress:1.18' // Added for JDK9 compatibility since it's missing this package - compile 'javax.xml.bind:jaxb-api:2.3.0' + compile 'javax.xml.bind:jaxb-api:2.3.1' compile ('org.rnorth.duct-tape:duct-tape:1.0.7') { exclude(group: 'org.jetbrains', module: 'annotations') } From 4b07ad836ef2ae3fc585b5d8ce6ce636cf5187d8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 23 Dec 2018 21:50:01 +0000 Subject: [PATCH 0490/5615] Bump neo4j-java-driver from 1.7.1 to 1.7.2 in /modules/neo4j (#1045) Bumps [neo4j-java-driver](https://github.com/neo4j/neo4j-java-driver) from 1.7.1 to 1.7.2.
Commits - [`7165cef`](https://github.com/neo4j/neo4j-java-driver/commit/7165cef0d4b602da30b65613977744ad661c2e67) Merge branch 1.6 into 1.7 - [`15fc20c`](https://github.com/neo4j/neo4j-java-driver/commit/15fc20c6405787ee732db7ce296fe1d5befd4df1) Merge pull request [#548](https://github-redirect.dependabot.com/neo4j/neo4j-java-driver/issues/548) from ali-ince/1.6-fix-auto-read-for-routing - [`3940159`](https://github.com/neo4j/neo4j-java-driver/commit/39401592e73d1dc9a44790cfae5c385eb0fd4573) Update stub server test scripts for latest boltkit - [`23153f4`](https://github.com/neo4j/neo4j-java-driver/commit/23153f4d3f3c67b0ca394a8baba86c859ba851d6) Make channel auto-read management to work on routing driver - See full diff in [compare view](https://github.com/neo4j/neo4j-java-driver/compare/1.7.1...1.7.2)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=org.neo4j.driver:neo4j-java-driver&package-manager=gradle&previous-version=1.7.1&new-version=1.7.2)](https://dependabot.com/compatibility-score.html?dependency-name=org.neo4j.driver:neo4j-java-driver&package-manager=gradle&previous-version=1.7.1&new-version=1.7.2) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 21f5ac07822..29634671a69 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -3,6 +3,6 @@ description = "TestContainers :: Neo4j" dependencies { compile project(":testcontainers") - testCompile "org.neo4j.driver:neo4j-java-driver:1.7.1" + testCompile "org.neo4j.driver:neo4j-java-driver:1.7.2" testCompile 'org.assertj:assertj-core:3.11.1' } From ae4d0894669bf7a1f2c5c5e0812c5a45ea3b6f2f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 23 Dec 2018 21:57:15 +0000 Subject: [PATCH 0491/5615] Bump jetty from 6.1.25 to 6.1.26 in /modules/selenium (#1047) Bumps jetty from 6.1.25 to 6.1.26. [![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=org.mortbay.jetty:jetty&package-manager=gradle&previous-version=6.1.25&new-version=6.1.26)](https://dependabot.com/compatibility-score.html?dependency-name=org.mortbay.jetty:jetty&package-manager=gradle&previous-version=6.1.25&new-version=6.1.26) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/selenium/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/selenium/build.gradle b/modules/selenium/build.gradle index 46699088b88..f7fcb75107d 100644 --- a/modules/selenium/build.gradle +++ b/modules/selenium/build.gradle @@ -8,6 +8,6 @@ dependencies { testCompile 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' testCompile 'org.seleniumhq.selenium:selenium-support:3.141.59' - testCompile 'org.mortbay.jetty:jetty:6.1.25' + testCompile 'org.mortbay.jetty:jetty:6.1.26' testCompile project(':nginx') } From b7ff19ad6cb72a9f1bc8d931b9bf9289411081f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 23 Dec 2018 22:00:18 +0000 Subject: [PATCH 0492/5615] [Security] Bump logback-classic from 1.1.2 to 1.2.3 in /examples (#1060) Bumps logback-classic from 1.1.2 to 1.2.3. **This update includes security fixes.**
Vulnerabilities fixed *Sourced from [The Sonatype OSS Index](https://ossindex.sonatype.org/vuln/391196a7-f007-430b-b47f-cd9a3fec6374).* > **[CVE-2017-5929] Deserialization of Untrusted Data** > QOS.ch Logback before 1.2.0 has a serialization vulnerability affecting the SocketServer and ServerSocketReceiver components. > > Affected versions: (, 1.1.10]

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=ch.qos.logback:logback-classic&package-manager=maven&previous-version=1.1.2&new-version=1.2.3)](https://dependabot.com/compatibility-score.html?dependency-name=ch.qos.logback:logback-classic&package-manager=maven&previous-version=1.1.2&new-version=1.2.3) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- examples/disque-job-queue/pom.xml | 2 +- examples/linked-container/pom.xml | 2 +- examples/redis-backed-cache-testng/pom.xml | 2 +- examples/redis-backed-cache/pom.xml | 2 +- examples/selenium-container/pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/disque-job-queue/pom.xml b/examples/disque-job-queue/pom.xml index fd117e2dd28..beaa63fd181 100644 --- a/examples/disque-job-queue/pom.xml +++ b/examples/disque-job-queue/pom.xml @@ -35,7 +35,7 @@ ch.qos.logback logback-classic - 1.1.2 + 1.2.3 test diff --git a/examples/linked-container/pom.xml b/examples/linked-container/pom.xml index 3ee79bf6d51..0ae81cfdfb8 100644 --- a/examples/linked-container/pom.xml +++ b/examples/linked-container/pom.xml @@ -32,7 +32,7 @@ ch.qos.logback logback-classic - 1.2.1 + 1.2.3 test diff --git a/examples/redis-backed-cache-testng/pom.xml b/examples/redis-backed-cache-testng/pom.xml index f5115ab3b59..09a339d7c39 100644 --- a/examples/redis-backed-cache-testng/pom.xml +++ b/examples/redis-backed-cache-testng/pom.xml @@ -36,7 +36,7 @@ ch.qos.logback logback-classic - 1.1.2 + 1.2.3 test diff --git a/examples/redis-backed-cache/pom.xml b/examples/redis-backed-cache/pom.xml index 6fa03969067..3cecb5fd482 100644 --- a/examples/redis-backed-cache/pom.xml +++ b/examples/redis-backed-cache/pom.xml @@ -35,7 +35,7 @@ ch.qos.logback logback-classic - 1.1.2 + 1.2.3 test diff --git a/examples/selenium-container/pom.xml b/examples/selenium-container/pom.xml index ebc69dea3c3..44fab3b30ff 100644 --- a/examples/selenium-container/pom.xml +++ b/examples/selenium-container/pom.xml @@ -41,7 +41,7 @@ ch.qos.logback logback-classic - 1.1.2 + 1.2.3 test From e201308e71abada33f9ea97b2cc5e19a6defae6b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 23 Dec 2018 22:02:10 +0000 Subject: [PATCH 0493/5615] Bump commons-dbutils from 1.6 to 1.7 in /modules/mysql (#1040) Bumps commons-dbutils from 1.6 to 1.7. [![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=commons-dbutils:commons-dbutils&package-manager=gradle&previous-version=1.6&new-version=1.7)](https://dependabot.com/compatibility-score.html?dependency-name=commons-dbutils:commons-dbutils&package-manager=gradle&previous-version=1.6&new-version=1.7) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 03bd0156d51..5c0656cf78e 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -5,7 +5,7 @@ dependencies { testCompile 'mysql:mysql-connector-java:5.1.35' testCompile 'com.zaxxer:HikariCP-java6:2.3.8' - testCompile 'commons-dbutils:commons-dbutils:1.6' + testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.apache.tomcat:tomcat-jdbc:8.5.4' testCompile 'org.vibur:vibur-dbcp:22.2' } From 794d36b58163dc052e3a87fcd25f2b84d4d8b50a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 23 Dec 2018 22:02:35 +0000 Subject: [PATCH 0494/5615] Bump vibur-dbcp from 9.0 to 22.2 in /modules/mariadb (#1063) Bumps [vibur-dbcp](https://github.com/vibur/vibur-dbcp) from 9.0 to 22.2.
Changelog *Sourced from [vibur-dbcp's changelog](https://github.com/vibur/vibur-dbcp/blob/master/CHANGELOG).* > Version 22.2 (05/05/2018) - RECOMMENDED > ======================================= > * Small performance update. > > Version 22.1 (30/03/2018) > ========================= > * Adopted changes / improvements from Vibur Object Pool 22.1. > * Various other small improvements, refactoring, and javadoc updates. > > Version 22.0 (08/02/2018) > ========================= > * Added logLineRegex configuration option - the application can use this option in conjunction with the > logTakenConnectionsOnTimeout option in order to filter the generated stack trace lines. > * Added takenConnectionsFormatter configuration option - allows the application to implement custom formatting > to String of the TakenConnection[] array. Note that the default formatter uses the logLineRegex for filtering. > * Added GetConnectionTimeout programming hook that is invoked only if the call to DataSource.getConnection() > timeouts. The application can use this hook to format and redirect the generated taken-connections log to a different > than the usual log location, for example, to Amazon S3. Note that in order to enable the taken-connections tracking > the application must set poolEnableConnectionTracking to true. The default hook uses the takenConnectionsFormatter; > it can be disabled via setting logTakenConnectionsOnTimeout to false. > * Fixed a bug in the CloseConnection hook takenNanos parameter calculation, before its value was sometimes incorrect. > > Version 21.3 (28/01/2018) > ========================= > * Fixed a minor bug in the ResultSet size calculation where the size was sometimes calculated off by one. > > Version 21.2 (29/11/2017) > ========================= > * Fixed a bug in the implicit closing of the current ResultSet functionality, introduced in version 21.0. > * Fixed a rarely occurring bug related to the possibility of Statement.getResultSet() to return null. > > Version 21.1 (27/11/2017) > ========================= > * Added resultSetNanoTime parameter to the ResultSetRetrieval programming hook. This parameter complements the existing > resultSetSize parameter and represents the total time taken to iterate over and process the retrieved ResultSet. > > Version 21.0 (22/11/2017) > ========================= > * Moved the getConnection() retry mechanism (for lazily created connections) from the ConnectionFactory level > to the PoolOperations level - this improves the connections reuse in certain situations. > * Implemented implicit closing of the current ResultSet when the parent Statement is closed. > * Changed some configuration defaults: > a) poolInitialSize 10 -> 5; > b) poolMaxSize 100 -> 50; > c) reducerTimeIntervalInSeconds 60 -> 30; > d) reducerSamples 20 -> 15; > e) connectionTimeoutInMs 30_000 -> 15_000; > f) acquireRetryDelayInMs 1000 -> 500; > * Adopted changes / improvements from Vibur Object Pool 21.0. > * Various other small fixes, refactoring and improvements. > ... (truncated)
Commits - [`a3bd654`](https://github.com/vibur/vibur-dbcp/commit/a3bd6540856d5d9f1bfcf4d3da62c22e73e1f2d5) [maven-release-plugin] prepare release 22.2 - [`d8eb05c`](https://github.com/vibur/vibur-dbcp/commit/d8eb05c39740c1e08b8c3015238a6b1b52c75b1c) osgi update - [`1751f77`](https://github.com/vibur/vibur-dbcp/commit/1751f7748f03cc84ba0292379eadb72b014adef8) prepare release 22.2 - [`a61e28c`](https://github.com/vibur/vibur-dbcp/commit/a61e28cd06ef3a62dae9b5783fceb4c416c51b89) updated contributors - [`8857333`](https://github.com/vibur/vibur-dbcp/commit/885733396aa7e1854daeffcf8dff0ac7afbfb18b) Automated by IntelliJ refactoring to introduce curly braces after if/else/for... - [`b79116b`](https://github.com/vibur/vibur-dbcp/commit/b79116b2dee3150cefe40048c551657a6611a09e) Merge pull request [#10](https://github-redirect.dependabot.com/vibur/vibur-dbcp/issues/10) from rbalamohan/issue/9 - [`7b183e4`](https://github.com/vibur/vibur-dbcp/commit/7b183e40775b68e533965e39c1de7fa34cccd7ed) Issue 9: Consider optimising AbstractInvocationHandler::invoke - [`f602813`](https://github.com/vibur/vibur-dbcp/commit/f6028137ffc32c57550a6b8509e05ac6930ce1b8) Issue 9: Consider optimising AbstractInvocationHandler::invoke - [`3eea6b1`](https://github.com/vibur/vibur-dbcp/commit/3eea6b1ffd918d927152f8ffe36aec3e0c2871b2) Issue 9: Consider optimising AbstractInvocationHandler::invoke - [`5b3349a`](https://github.com/vibur/vibur-dbcp/commit/5b3349a039c7603c1e673cee2d9954f05f7ab8cb) Issue 9: Consider optimising AbstractInvocationHandler::invoke - Additional commits viewable in [compare view](https://github.com/vibur/vibur-dbcp/compare/9.0...22.2)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=org.vibur:vibur-dbcp&package-manager=gradle&previous-version=9.0&new-version=22.2)](https://dependabot.com/compatibility-score.html?dependency-name=org.vibur:vibur-dbcp&package-manager=gradle&previous-version=9.0&new-version=22.2) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index ba6f4b97dcf..1d0aa45e12a 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -7,5 +7,5 @@ dependencies { testCompile 'com.zaxxer:HikariCP-java6:2.3.8' testCompile 'commons-dbutils:commons-dbutils:1.6' testCompile 'org.apache.tomcat:tomcat-jdbc:8.5.4' - testCompile 'org.vibur:vibur-dbcp:9.0' + testCompile 'org.vibur:vibur-dbcp:22.2' } From ade7f6da9094ed4ff2dcfbe9a0fc6ce6cf400777 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 23 Dec 2018 22:03:22 +0000 Subject: [PATCH 0495/5615] Bump HikariCP from 2.6.1 to 3.2.0 in /modules/spock (#1053) Bumps [HikariCP](https://github.com/brettwooldridge/HikariCP) from 2.6.1 to 3.2.0.
Changelog *Sourced from [HikariCP's changelog](https://github.com/brettwooldridge/HikariCP/blob/dev/CHANGES).* > HikariCP Changes > > Changes in 3.2.0 > > * check connection closed condition before attempting to set network timeout to avoid spurios exceptions > in the log. > > * updated validation failure message to include recommendation to check maxLifetime value. > > * fixed 1141 do not throw SQLException when calling isClosed() or close() on a already closed unwrapped > Connection, as per JDBC specification. > > * fixed 1137 support changing user/password at runtime for DriverDataSource-wrapped driver connections. > > * fixed 1136 log loaded driver when using jdbcUrl. > > * pull 1135 extract sealed pool check into independent method. > > * fixed 1126 correct error in JavaDoc for HikariConfig.setInitializationFailTimeout(). > > * fixed 1114 removed unreachable code. > > Changes in 3.1.0 > > * Add get/setCatalog() to HikariConfigMXBean, allowing the catalog to be changed at runtime. > The catalog should only be changed while the pool is suspended, and after evicting existing > connections via HikariPoolMXBean.softEvictConnections(). > > Changes in 3.0.0 > > * Removed previously deprecated methods; > HikariConfig.copyState() > HikariConfig.getScheduledExecutorService() > HikariConfig.setScheduledExecutorService() > HikariConfig.isInitializationFailFast() > HikariConfig.setInitializationFailFast() > HikariConfig.isJdbc4ConnectionTest() > HikariConfig.setJdbc4ConnectionTest() > > HikariDataSource.copyState() > HikariDataSource.getScheduledExecutorService() > HikariDataSource.setScheduledExecutorService() > HikariDataSource.suspendPool() > HikariDataSource.resumePool() > HikariDataSource.shutdown() > HikariDataSource.isInitializationFailFast() > HikariDataSource.setInitializationFailFast() > HikariDataSource.isJdbc4ConnectionTest() > HikariDataSource.setJdbc4ConnectionTest() > > ... (truncated)
Commits - [`ad827db`](https://github.com/brettwooldridge/HikariCP/commit/ad827dbb34422f4c7fd79343fb42a8a3f5c5660a) [maven-release-plugin] prepare release HikariCP-3.2.0 - [`0f06b56`](https://github.com/brettwooldridge/HikariCP/commit/0f06b56325551233b37106d7bd958713067d5d03) Update pom.xml - [`3498a14`](https://github.com/brettwooldridge/HikariCP/commit/3498a143a582e3d12913174bf05a5d554bf0cb46) Update CHANGES - [`88b57c3`](https://github.com/brettwooldridge/HikariCP/commit/88b57c3bd7cd3d40c229f178344f6b1fc6c8629e) Check closed condition before attempting to set network timeout. - [`6a13980`](https://github.com/brettwooldridge/HikariCP/commit/6a139803127b712316087af9b0412d45ad158340) Update validation failure message. - [`603951f`](https://github.com/brettwooldridge/HikariCP/commit/603951febd167ce086c2a65ace1668cf8488db5e) Fixes [#1126](https://github-redirect.dependabot.com/brettwooldridge/HikariCP/issues/1126) correct error in JavaDoc for HikariConfig.setInitializationFailTi... - [`5607d8b`](https://github.com/brettwooldridge/HikariCP/commit/5607d8b91330648ff147f45aeea9a3c6421727cf) Fixes [#1141](https://github-redirect.dependabot.com/brettwooldridge/HikariCP/issues/1141) do not throw SQLException when calling isClosed() or close() on a... - [`8921bb0`](https://github.com/brettwooldridge/HikariCP/commit/8921bb08c86a7435b6bf21c78c9b2d470963d874) Fixes [#1137](https://github-redirect.dependabot.com/brettwooldridge/HikariCP/issues/1137) support changing user/password at runtime for DriverDataSource-wr... - [`851e2d4`](https://github.com/brettwooldridge/HikariCP/commit/851e2d4592b52a9c367ada2c76f013b1d4e20ac3) Fixes [#1137](https://github-redirect.dependabot.com/brettwooldridge/HikariCP/issues/1137) support changing user/password at runtime for DriverDataSource-wr... - [`71e8688`](https://github.com/brettwooldridge/HikariCP/commit/71e8688ed41d4d13fbb09dec7572ce7504edffc1) Closes [#1114](https://github-redirect.dependabot.com/brettwooldridge/HikariCP/issues/1114) Remove unneeded code. - Additional commits viewable in [compare view](https://github.com/brettwooldridge/HikariCP/compare/HikariCP-2.6.1...HikariCP-3.2.0)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=com.zaxxer:HikariCP&package-manager=gradle&previous-version=2.6.1&new-version=3.2.0)](https://dependabot.com/compatibility-score.html?dependency-name=com.zaxxer:HikariCP&package-manager=gradle&previous-version=2.6.1&new-version=3.2.0) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 40a668f5411..149eec0ebca 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -10,7 +10,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') - testCompile 'com.zaxxer:HikariCP:2.6.1' + testCompile 'com.zaxxer:HikariCP:3.2.0' testRuntime 'org.postgresql:postgresql:42.0.0' testRuntime 'mysql:mysql-connector-java:6.0.6' From 4f1d6310dec11a56012ba38485cca1371ec059bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 23 Dec 2018 22:04:32 +0000 Subject: [PATCH 0496/5615] Bump HikariCP-java6 from 2.3.8 to 2.3.13 in /modules/mariadb (#1064) Bumps [HikariCP-java6](https://github.com/brettwooldridge/HikariCP) from 2.3.8 to 2.3.13.
Changelog *Sourced from [HikariCP-java6's changelog](https://github.com/brettwooldridge/HikariCP/blob/dev/CHANGES).* > HikariCP Changes > > Changes in 3.2.0 > > * check connection closed condition before attempting to set network timeout to avoid spurios exceptions > in the log. > > * updated validation failure message to include recommendation to check maxLifetime value. > > * fixed 1141 do not throw SQLException when calling isClosed() or close() on a already closed unwrapped > Connection, as per JDBC specification. > > * fixed 1137 support changing user/password at runtime for DriverDataSource-wrapped driver connections. > > * fixed 1136 log loaded driver when using jdbcUrl. > > * pull 1135 extract sealed pool check into independent method. > > * fixed 1126 correct error in JavaDoc for HikariConfig.setInitializationFailTimeout(). > > * fixed 1114 removed unreachable code. > > Changes in 3.1.0 > > * Add get/setCatalog() to HikariConfigMXBean, allowing the catalog to be changed at runtime. > The catalog should only be changed while the pool is suspended, and after evicting existing > connections via HikariPoolMXBean.softEvictConnections(). > > Changes in 3.0.0 > > * Removed previously deprecated methods; > HikariConfig.copyState() > HikariConfig.getScheduledExecutorService() > HikariConfig.setScheduledExecutorService() > HikariConfig.isInitializationFailFast() > HikariConfig.setInitializationFailFast() > HikariConfig.isJdbc4ConnectionTest() > HikariConfig.setJdbc4ConnectionTest() > > HikariDataSource.copyState() > HikariDataSource.getScheduledExecutorService() > HikariDataSource.setScheduledExecutorService() > HikariDataSource.suspendPool() > HikariDataSource.resumePool() > HikariDataSource.shutdown() > HikariDataSource.isInitializationFailFast() > HikariDataSource.setInitializationFailFast() > HikariDataSource.isJdbc4ConnectionTest() > HikariDataSource.setJdbc4ConnectionTest() > > ... (truncated)
Commits - [`390c569`](https://github.com/brettwooldridge/HikariCP/commit/390c5699beeafb19d802ce4bc4313063c5734be5) [maven-release-plugin] prepare release HikariCP-2.3.13 - [`e94d920`](https://github.com/brettwooldridge/HikariCP/commit/e94d920595beeac13344b38c3f6df6f098df8d1c) Fixes [#512](https://github-redirect.dependabot.com/brettwooldridge/HikariCP/issues/512) back port to 2.3.x stream. - [`d5e496c`](https://github.com/brettwooldridge/HikariCP/commit/d5e496cecdfc5539942f581832ca2a9443f1c4c8) [maven-release-plugin] prepare for next development iteration - [`260ac29`](https://github.com/brettwooldridge/HikariCP/commit/260ac29c17a01edee9e6261887b72486f6475403) [maven-release-plugin] prepare release HikariCP-2.3.12 - [`a312c5a`](https://github.com/brettwooldridge/HikariCP/commit/a312c5a408f6c53e4941ca6a3343182194cbe8c3) Skip proxy generation during test runs - [`f0ba4b7`](https://github.com/brettwooldridge/HikariCP/commit/f0ba4b7188c215bf8bddda7364876fda566fd6b4) Fixed issue with new proxy class generation whereby Java 6 classes were gener... - [`fe6eb41`](https://github.com/brettwooldridge/HikariCP/commit/fe6eb4123e4536f97d474eddd6a79e4999f16e5a) [maven-release-plugin] prepare for next development iteration - [`af5ba3b`](https://github.com/brettwooldridge/HikariCP/commit/af5ba3ba7956a2d5d1e26b9934ee501d6a32bf35) [maven-release-plugin] prepare release HikariCP-2.3.11 - [`2c632e7`](https://github.com/brettwooldridge/HikariCP/commit/2c632e7566faffef329e5c0c7a82ef93f98486c4) Fixes major class version number for generated proxy classes. - [`e2f1915`](https://github.com/brettwooldridge/HikariCP/commit/e2f1915429b5ce8873b817f52e8120dd9b1822dd) [maven-release-plugin] prepare for next development iteration - Additional commits viewable in [compare view](https://github.com/brettwooldridge/HikariCP/compare/HikariCP-2.3.8...HikariCP-2.3.13)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=com.zaxxer:HikariCP-java6&package-manager=gradle&previous-version=2.3.8&new-version=2.3.13)](https://dependabot.com/compatibility-score.html?dependency-name=com.zaxxer:HikariCP-java6&package-manager=gradle&previous-version=2.3.8&new-version=2.3.13) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 1d0aa45e12a..ff7be6c9a1c 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -4,7 +4,7 @@ dependencies { compile project(':jdbc') testCompile 'org.mariadb.jdbc:mariadb-java-client:1.5.9' - testCompile 'com.zaxxer:HikariCP-java6:2.3.8' + testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.6' testCompile 'org.apache.tomcat:tomcat-jdbc:8.5.4' testCompile 'org.vibur:vibur-dbcp:22.2' From 9e24ec0dfbe689eb94f8389c2f206e29ed8a1790 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 23 Dec 2018 22:05:08 +0000 Subject: [PATCH 0497/5615] Bump HikariCP-java6 from 2.3.8 to 2.3.13 in /modules/postgresql (#1051) Bumps [HikariCP-java6](https://github.com/brettwooldridge/HikariCP) from 2.3.8 to 2.3.13.
Changelog *Sourced from [HikariCP-java6's changelog](https://github.com/brettwooldridge/HikariCP/blob/dev/CHANGES).* > HikariCP Changes > > Changes in 3.2.0 > > * check connection closed condition before attempting to set network timeout to avoid spurios exceptions > in the log. > > * updated validation failure message to include recommendation to check maxLifetime value. > > * fixed 1141 do not throw SQLException when calling isClosed() or close() on a already closed unwrapped > Connection, as per JDBC specification. > > * fixed 1137 support changing user/password at runtime for DriverDataSource-wrapped driver connections. > > * fixed 1136 log loaded driver when using jdbcUrl. > > * pull 1135 extract sealed pool check into independent method. > > * fixed 1126 correct error in JavaDoc for HikariConfig.setInitializationFailTimeout(). > > * fixed 1114 removed unreachable code. > > Changes in 3.1.0 > > * Add get/setCatalog() to HikariConfigMXBean, allowing the catalog to be changed at runtime. > The catalog should only be changed while the pool is suspended, and after evicting existing > connections via HikariPoolMXBean.softEvictConnections(). > > Changes in 3.0.0 > > * Removed previously deprecated methods; > HikariConfig.copyState() > HikariConfig.getScheduledExecutorService() > HikariConfig.setScheduledExecutorService() > HikariConfig.isInitializationFailFast() > HikariConfig.setInitializationFailFast() > HikariConfig.isJdbc4ConnectionTest() > HikariConfig.setJdbc4ConnectionTest() > > HikariDataSource.copyState() > HikariDataSource.getScheduledExecutorService() > HikariDataSource.setScheduledExecutorService() > HikariDataSource.suspendPool() > HikariDataSource.resumePool() > HikariDataSource.shutdown() > HikariDataSource.isInitializationFailFast() > HikariDataSource.setInitializationFailFast() > HikariDataSource.isJdbc4ConnectionTest() > HikariDataSource.setJdbc4ConnectionTest() > > ... (truncated)
Commits - [`390c569`](https://github.com/brettwooldridge/HikariCP/commit/390c5699beeafb19d802ce4bc4313063c5734be5) [maven-release-plugin] prepare release HikariCP-2.3.13 - [`e94d920`](https://github.com/brettwooldridge/HikariCP/commit/e94d920595beeac13344b38c3f6df6f098df8d1c) Fixes [#512](https://github-redirect.dependabot.com/brettwooldridge/HikariCP/issues/512) back port to 2.3.x stream. - [`d5e496c`](https://github.com/brettwooldridge/HikariCP/commit/d5e496cecdfc5539942f581832ca2a9443f1c4c8) [maven-release-plugin] prepare for next development iteration - [`260ac29`](https://github.com/brettwooldridge/HikariCP/commit/260ac29c17a01edee9e6261887b72486f6475403) [maven-release-plugin] prepare release HikariCP-2.3.12 - [`a312c5a`](https://github.com/brettwooldridge/HikariCP/commit/a312c5a408f6c53e4941ca6a3343182194cbe8c3) Skip proxy generation during test runs - [`f0ba4b7`](https://github.com/brettwooldridge/HikariCP/commit/f0ba4b7188c215bf8bddda7364876fda566fd6b4) Fixed issue with new proxy class generation whereby Java 6 classes were gener... - [`fe6eb41`](https://github.com/brettwooldridge/HikariCP/commit/fe6eb4123e4536f97d474eddd6a79e4999f16e5a) [maven-release-plugin] prepare for next development iteration - [`af5ba3b`](https://github.com/brettwooldridge/HikariCP/commit/af5ba3ba7956a2d5d1e26b9934ee501d6a32bf35) [maven-release-plugin] prepare release HikariCP-2.3.11 - [`2c632e7`](https://github.com/brettwooldridge/HikariCP/commit/2c632e7566faffef329e5c0c7a82ef93f98486c4) Fixes major class version number for generated proxy classes. - [`e2f1915`](https://github.com/brettwooldridge/HikariCP/commit/e2f1915429b5ce8873b817f52e8120dd9b1822dd) [maven-release-plugin] prepare for next development iteration - Additional commits viewable in [compare view](https://github.com/brettwooldridge/HikariCP/compare/HikariCP-2.3.8...HikariCP-2.3.13)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=com.zaxxer:HikariCP-java6&package-manager=gradle&previous-version=2.3.8&new-version=2.3.13)](https://dependabot.com/compatibility-score.html?dependency-name=com.zaxxer:HikariCP-java6&package-manager=gradle&previous-version=2.3.8&new-version=2.3.13) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 211471f1c34..2247862640b 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -4,6 +4,6 @@ dependencies { compile project(':jdbc') testCompile 'org.postgresql:postgresql:9.3-1101-jdbc41' - testCompile 'com.zaxxer:HikariCP-java6:2.3.8' + testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.6' } From 31518595113e259f60faf5cd6faeb73d32e012b7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 23 Dec 2018 22:05:57 +0000 Subject: [PATCH 0498/5615] Bump commons-dbutils from 1.6 to 1.7 in /modules/postgresql (#1048) Bumps commons-dbutils from 1.6 to 1.7. Signed-off-by: dependabot[bot] --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 2247862640b..05db9d8bdd7 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -4,6 +4,6 @@ dependencies { compile project(':jdbc') testCompile 'org.postgresql:postgresql:9.3-1101-jdbc41' + testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' - testCompile 'commons-dbutils:commons-dbutils:1.6' } From 1bac2a1211eaa1361db2d83ff8d418ef226edc90 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 23 Dec 2018 22:06:49 +0000 Subject: [PATCH 0499/5615] Bump tomcat-jdbc from 8.5.4 to 9.0.14 in /modules/mariadb (#1061) Bumps tomcat-jdbc from 8.5.4 to 9.0.14. Signed-off-by: dependabot[bot] --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index ff7be6c9a1c..6ef081e8133 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -6,6 +6,6 @@ dependencies { testCompile 'org.mariadb.jdbc:mariadb-java-client:1.5.9' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.6' - testCompile 'org.apache.tomcat:tomcat-jdbc:8.5.4' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.14' testCompile 'org.vibur:vibur-dbcp:22.2' } From b8119fcd22f8584dcaf3b680b3c021865b70b99d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 23 Dec 2018 22:08:03 +0000 Subject: [PATCH 0500/5615] Bump tomcat-jdbc from 8.5.4 to 9.0.14 in /modules/mysql (#1038) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [//]: # (dependabot-start) ⚠️ **Dependabot is rebasing this PR** ⚠️ If you make any changes to it yourself then they will take precedence over the rebase. --- [//]: # (dependabot-end) Bumps tomcat-jdbc from 8.5.4 to 9.0.14. [![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=org.apache.tomcat:tomcat-jdbc&package-manager=gradle&previous-version=8.5.4&new-version=9.0.14)](https://dependabot.com/compatibility-score.html?dependency-name=org.apache.tomcat:tomcat-jdbc&package-manager=gradle&previous-version=8.5.4&new-version=9.0.14) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 5c0656cf78e..bc718645bcd 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -5,7 +5,7 @@ dependencies { testCompile 'mysql:mysql-connector-java:5.1.35' testCompile 'com.zaxxer:HikariCP-java6:2.3.8' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.14' testCompile 'commons-dbutils:commons-dbutils:1.7' - testCompile 'org.apache.tomcat:tomcat-jdbc:8.5.4' testCompile 'org.vibur:vibur-dbcp:22.2' } From 05145887151cb602e63836a196950169e7ed5ba3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 23 Dec 2018 22:08:51 +0000 Subject: [PATCH 0501/5615] Bump aws-java-sdk-dynamodb from 1.11.126 to 1.11.475 in /modules/dynalite (#1059) Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.11.126 to 1.11.475.
Changelog *Sourced from [aws-java-sdk-dynamodb's changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md).* > # __1.11.475__ __2018-12-21__ > ## __AWS Certificate Manager Private Certificate Authority__ > - ### Features > - This release marks the introduction of waiters in ACM PCA, which allow you to control the progression of your code based on the presence or state of certain resources. Waiters can be implemented in the DescribeCertificateAuthorityAuditReport, GetCertificate, and GetCertificateAuthorityCsr API operations. > > ## __AWS Step Functions__ > - ### Features > - This release adds support for cost allocation tagging. You can now create, delete, and list tags for AWS Step Functions activity and state machine resources. For more information about tagging, see AWS Tagging Strategies. > > ## __Amazon DynamoDB__ > - ### Features > - Added provisionedThroughPut exception on the request level for transaction APIs. > > ## __Amazon Pinpoint SMS and Voice Service__ > - ### Features > - Configuration sets can now use Amazon SNS as an event destination. > > # __1.11.474__ __2018-12-20__ > ## __AWS Elemental MediaLive__ > - ### Features > - This release provides support for ID3 tags and video quality setting for subgop_length. > > ## __Amazon Cognito Identity Provider__ > - ### Features > - Amazon Cognito now has API support for updating the Secure Sockets Layer (SSL) certificate for the custom domain for your user pool. > > ## __Amazon Comprehend__ > - ### Features > - This SDK release adds functionality to stop training Custom Document Classifier or Custom Entity Recognizer in Amazon Comprehend. > > ## __Amazon Kinesis Firehose__ > - ### Features > - Support for specifying customized s3 keys and supplying a separate prefix for failed-records > > ## __Amazon Transcribe Service__ > - ### Features > - With this release, Amazon Transcribe now supports transcriptions from audio sources in Italian (it-IT). > > # __1.11.473__ __2018-12-19__ > ## __AWS WAF__ > - ### Features > - This release adds rule-level control for rule group. If a rule group contains a rule that blocks legitimate traffic, previously you had to override the entire rule group to COUNT in order to allow the traffic. You can now use the UpdateWebACL API to exclude specific rules within a rule group. Excluding rules changes the action for the individual rules to COUNT. Excluded rules will be recorded in the new "excludedRules" attribute of the WAF logs. > > ## __AWS WAF Regional__ > - ### Features > - This release adds rule-level control for rule group. If a rule group contains a rule that blocks legitimate traffic, previously you had to override the entire rule group to COUNT in order to allow the traffic. You can now use the UpdateWebACL API to exclude specific rules within a rule group. Excluding rules changes the action for the individual rules to COUNT. Excluded rules will be recorded in the new "excludedRules" attribute of the WAF logs. > > ## __Amazon Elastic Compute Cloud__ > - ### Features > - This release adds support for specifying partition as a strategy for EC2 Placement Groups. This new strategy allows one to launch instances into partitions that do not share certain underlying hardware between partitions, to assist with building and deploying highly available replicated applications. > ... (truncated)
Commits - See full diff in [compare view](https://github.com/aws/aws-sdk-java/commits/1.11.475)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=com.amazonaws:aws-java-sdk-dynamodb&package-manager=gradle&previous-version=1.11.126&new-version=1.11.475)](https://dependabot.com/compatibility-score.html?dependency-name=com.amazonaws:aws-java-sdk-dynamodb&package-manager=gradle&previous-version=1.11.126&new-version=1.11.475) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/dynalite/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 1250ba677b3..03031943d85 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Dynalite" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.126' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.475' testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.126' } From 6edb190e5c7178135ba18ae24d6095b928bf4497 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 23 Dec 2018 22:09:52 +0000 Subject: [PATCH 0502/5615] Bump HikariCP-java6 from 2.3.8 to 2.3.13 in /modules/mysql (#1041) Bumps [HikariCP-java6](https://github.com/brettwooldridge/HikariCP) from 2.3.8 to 2.3.13. - [Release notes](https://github.com/brettwooldridge/HikariCP/releases) - [Changelog](https://github.com/brettwooldridge/HikariCP/blob/dev/CHANGES) - [Commits](https://github.com/brettwooldridge/HikariCP/compare/HikariCP-2.3.8...HikariCP-2.3.13) Signed-off-by: dependabot[bot] --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index bc718645bcd..6ff4c55af02 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -4,7 +4,7 @@ dependencies { compile project(':jdbc') testCompile 'mysql:mysql-connector-java:5.1.35' - testCompile 'com.zaxxer:HikariCP-java6:2.3.8' + testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.14' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.vibur:vibur-dbcp:22.2' From bee21011edd75784b14a1a360c4222c3a448cae2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 23 Dec 2018 22:10:15 +0000 Subject: [PATCH 0503/5615] Bump HikariCP from 2.6.1 to 3.2.0 in /modules/junit-jupiter (#1072) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [//]: # (dependabot-start) ⚠️ **Dependabot is rebasing this PR** ⚠️ If you make any changes to it yourself then they will take precedence over the rebase. --- [//]: # (dependabot-end) Bumps [HikariCP](https://github.com/brettwooldridge/HikariCP) from 2.6.1 to 3.2.0.
Changelog *Sourced from [HikariCP's changelog](https://github.com/brettwooldridge/HikariCP/blob/dev/CHANGES).* > HikariCP Changes > > Changes in 3.2.0 > > * check connection closed condition before attempting to set network timeout to avoid spurios exceptions > in the log. > > * updated validation failure message to include recommendation to check maxLifetime value. > > * fixed 1141 do not throw SQLException when calling isClosed() or close() on a already closed unwrapped > Connection, as per JDBC specification. > > * fixed 1137 support changing user/password at runtime for DriverDataSource-wrapped driver connections. > > * fixed 1136 log loaded driver when using jdbcUrl. > > * pull 1135 extract sealed pool check into independent method. > > * fixed 1126 correct error in JavaDoc for HikariConfig.setInitializationFailTimeout(). > > * fixed 1114 removed unreachable code. > > Changes in 3.1.0 > > * Add get/setCatalog() to HikariConfigMXBean, allowing the catalog to be changed at runtime. > The catalog should only be changed while the pool is suspended, and after evicting existing > connections via HikariPoolMXBean.softEvictConnections(). > > Changes in 3.0.0 > > * Removed previously deprecated methods; > HikariConfig.copyState() > HikariConfig.getScheduledExecutorService() > HikariConfig.setScheduledExecutorService() > HikariConfig.isInitializationFailFast() > HikariConfig.setInitializationFailFast() > HikariConfig.isJdbc4ConnectionTest() > HikariConfig.setJdbc4ConnectionTest() > > HikariDataSource.copyState() > HikariDataSource.getScheduledExecutorService() > HikariDataSource.setScheduledExecutorService() > HikariDataSource.suspendPool() > HikariDataSource.resumePool() > HikariDataSource.shutdown() > HikariDataSource.isInitializationFailFast() > HikariDataSource.setInitializationFailFast() > HikariDataSource.isJdbc4ConnectionTest() > HikariDataSource.setJdbc4ConnectionTest() > > ... (truncated)
Commits - [`ad827db`](https://github.com/brettwooldridge/HikariCP/commit/ad827dbb34422f4c7fd79343fb42a8a3f5c5660a) [maven-release-plugin] prepare release HikariCP-3.2.0 - [`0f06b56`](https://github.com/brettwooldridge/HikariCP/commit/0f06b56325551233b37106d7bd958713067d5d03) Update pom.xml - [`3498a14`](https://github.com/brettwooldridge/HikariCP/commit/3498a143a582e3d12913174bf05a5d554bf0cb46) Update CHANGES - [`88b57c3`](https://github.com/brettwooldridge/HikariCP/commit/88b57c3bd7cd3d40c229f178344f6b1fc6c8629e) Check closed condition before attempting to set network timeout. - [`6a13980`](https://github.com/brettwooldridge/HikariCP/commit/6a139803127b712316087af9b0412d45ad158340) Update validation failure message. - [`603951f`](https://github.com/brettwooldridge/HikariCP/commit/603951febd167ce086c2a65ace1668cf8488db5e) Fixes [#1126](https://github-redirect.dependabot.com/brettwooldridge/HikariCP/issues/1126) correct error in JavaDoc for HikariConfig.setInitializationFailTi... - [`5607d8b`](https://github.com/brettwooldridge/HikariCP/commit/5607d8b91330648ff147f45aeea9a3c6421727cf) Fixes [#1141](https://github-redirect.dependabot.com/brettwooldridge/HikariCP/issues/1141) do not throw SQLException when calling isClosed() or close() on a... - [`8921bb0`](https://github.com/brettwooldridge/HikariCP/commit/8921bb08c86a7435b6bf21c78c9b2d470963d874) Fixes [#1137](https://github-redirect.dependabot.com/brettwooldridge/HikariCP/issues/1137) support changing user/password at runtime for DriverDataSource-wr... - [`851e2d4`](https://github.com/brettwooldridge/HikariCP/commit/851e2d4592b52a9c367ada2c76f013b1d4e20ac3) Fixes [#1137](https://github-redirect.dependabot.com/brettwooldridge/HikariCP/issues/1137) support changing user/password at runtime for DriverDataSource-wr... - [`71e8688`](https://github.com/brettwooldridge/HikariCP/commit/71e8688ed41d4d13fbb09dec7572ce7504edffc1) Closes [#1114](https://github-redirect.dependabot.com/brettwooldridge/HikariCP/issues/1114) Remove unneeded code. - Additional commits viewable in [compare view](https://github.com/brettwooldridge/HikariCP/compare/HikariCP-2.6.1...HikariCP-3.2.0)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=com.zaxxer:HikariCP&package-manager=gradle&previous-version=2.6.1&new-version=3.2.0)](https://dependabot.com/compatibility-score.html?dependency-name=com.zaxxer:HikariCP&package-manager=gradle&previous-version=2.6.1&new-version=3.2.0) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 8429a0e9514..e93641091d0 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -6,7 +6,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') - testCompile 'com.zaxxer:HikariCP:2.6.1' + testCompile 'com.zaxxer:HikariCP:3.2.0' testCompile 'redis.clients:jedis:2.8.0' testRuntime 'org.postgresql:postgresql:42.0.0' From dd88880eee0a8f115c9222d45189bd23621d94d9 Mon Sep 17 00:00:00 2001 From: Bengt Brodersen <44030308+bengtbrodersen0042@users.noreply.github.com> Date: Sun, 23 Dec 2018 23:14:38 +0100 Subject: [PATCH 0504/5615] Enable line terminator matching for LogMessageWaitStrategy (#982) Enable line terminator matching to make expressions like ".*" matching even the end of a log line e.g. "waiting for connections on port 20017\n" --- .../wait/strategy/LogMessageWaitStrategy.java | 3 ++- .../strategy/LogMessageWaitStrategyTest.java | 20 ++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java index a25b1a86d8e..bd164096300 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java @@ -22,7 +22,8 @@ protected void waitUntilReady() { LogUtils.followOutput(DockerClientFactory.instance().client(), waitStrategyTarget.getContainerId(), waitingConsumer); Predicate waitPredicate = outputFrame -> - outputFrame.getUtf8String().matches(regEx); + // (?s) enables line terminator matching (equivalent to Pattern.DOTALL) + outputFrame.getUtf8String().matches("(?s)" + regEx); try { waitingConsumer.waitUntil(waitPredicate, startupTimeout.getSeconds(), TimeUnit.SECONDS, times); diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/LogMessageWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/LogMessageWaitStrategyTest.java index 7154bab06fa..a6ef9f11ba4 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/LogMessageWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/LogMessageWaitStrategyTest.java @@ -2,6 +2,8 @@ import org.jetbrains.annotations.NotNull; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import java.util.concurrent.atomic.AtomicBoolean; @@ -9,8 +11,24 @@ /** * Tests for {@link LogMessageWaitStrategy}. */ +@RunWith(Parameterized.class) public class LogMessageWaitStrategyTest extends AbstractWaitStrategyTest { + private final String pattern; + + @Parameterized.Parameters(name = "{0}") + public static Object[] parameters() { + return new String[] { + ".*ready.*\\s", // previous recommended style (explicit line ending) + ".*ready!\\s", // explicit line ending without wildcard after expected text + ".*ready.*" // new style (line ending matched by wildcard) + }; + } + + public LogMessageWaitStrategyTest(String pattern) { + this.pattern = pattern; + } + private static final String READY_MESSAGE = "I'm ready!"; @Test @@ -38,6 +56,6 @@ protected void waitUntilReady() { super.waitUntilReady(); ready.set(true); } - }.withRegEx(".*ready.*\\s").withTimes(2); + }.withRegEx(pattern).withTimes(2); } } From c0957ad0193022746eded5b8ae3fd25d55e83236 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 24 Dec 2018 05:29:48 +0000 Subject: [PATCH 0505/5615] Bump postgresql from 42.0.0 to 42.2.5 in /modules/junit-jupiter Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.0.0 to 42.2.5. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.0.0...REL42.2.5) Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index e93641091d0..f9bd917e78f 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,7 +9,7 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.2.0' testCompile 'redis.clients:jedis:2.8.0' - testRuntime 'org.postgresql:postgresql:42.0.0' + testRuntime 'org.postgresql:postgresql:42.2.5' testRuntime 'mysql:mysql-connector-java:6.0.6' testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.3.1' } From d0a4a34da236fc77fbe4d0368e398f74951a8150 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 24 Dec 2018 08:47:35 +0000 Subject: [PATCH 0506/5615] Bump jedis from 2.8.0 to 3.0.0 in /examples (#1085) Bumps [jedis](https://github.com/xetorthio/jedis) from 2.8.0 to 3.0.0.
Release notes *Sourced from [jedis's releases](https://github.com/xetorthio/jedis/releases).* > ## 3.0.0 > No release notes provided. > > ## 3.0.0-m1 > No release notes provided. > > ## 2.10.0 > No release notes provided. > > ## 2.10.0-m1 > No release notes provided. > > ## 2.9.1 > No release notes provided. > > ## 2.9.0 > # Changes: > - Add specific exception con evalsha doesn't have a script defined > - Add ipv6 compatibility > - Add HashTag version of SCAN methods for Redis Cluster > - Add BITFIELD command > - Add support for password in JedisCluster > - Add SSL support > > Complete list can be found [HERE](https://github-redirect.dependabot.com/xetorthio/jedis/issues?utf8=%E2%9C%93&q=milestone%3A2.9.0%20) > > ## 2.8.2 > # Changes > - Add fix in JedisPubSub to support ping/pong on subscription > - Cleanup cache state from failed attempts of cluster discovering > - Avoid queueing multiple cluster discovers of the same type > - Intoduce dedicated excpetion for Redis error "busy" > > Complete list can be found [HERE](https://github-redirect.dependabot.com/xetorthio/jedis/issues?utf8=%E2%9C%93&q=milestone%3A2.8.2) > > ## 2.8.1 > # Changes: > - Fix JedisCluster psubscribe calling wrong method > - Backport some JedisCluster constructors that weren't downmerged > - Add support to invoke Binary scripts from pipeline and multi.
Commits - [`087a9f8`](https://github.com/xetorthio/jedis/commit/087a9f8d3472e08b93984a279ad82c830a0028f8) Merge pull request [#1897](https://github-redirect.dependabot.com/xetorthio/jedis/issues/1897) from xetorthio/gkorland-patch-1 - [`10b391e`](https://github.com/xetorthio/jedis/commit/10b391e13590e61c2ec80e4c5522362cc7e9b646) Bug fix in JedisCluster scan ([#1902](https://github-redirect.dependabot.com/xetorthio/jedis/issues/1902)) - [`de16de1`](https://github.com/xetorthio/jedis/commit/de16de1032ae8a11a3cc513654354fcb64139369) Jedis.close() should call super.close() ([#1730](https://github-redirect.dependabot.com/xetorthio/jedis/issues/1730)) - [`3ba3154`](https://github.com/xetorthio/jedis/commit/3ba3154b490f59f5dabcbdee8ef4bc487b3e21ee) [#1867](https://github-redirect.dependabot.com/xetorthio/jedis/issues/1867) add waitReplica in JedisCluster ([#1868](https://github-redirect.dependabot.com/xetorthio/jedis/issues/1868)) - [`fc05550`](https://github.com/xetorthio/jedis/commit/fc05550b235f70e467fac0484fdc1bf2b4a376b7) CLIENT PAUSE command implementation ([#1847](https://github-redirect.dependabot.com/xetorthio/jedis/issues/1847)) - [`306ba28`](https://github.com/xetorthio/jedis/commit/306ba28554e0eb4cab5410d4bbda7bd5e09b9c6c) Protect rediscovering with double-checked locking ([#1856](https://github-redirect.dependabot.com/xetorthio/jedis/issues/1856)) - [`ca8ad49`](https://github.com/xetorthio/jedis/commit/ca8ad49ef835d885d895978185da31eb899e481a) Update README.md - [`53194dd`](https://github.com/xetorthio/jedis/commit/53194dd03bf397211566ba48a60e9f1e549c85ae) Update README with SNAPSHOT ([#1893](https://github-redirect.dependabot.com/xetorthio/jedis/issues/1893)) - [`b7120ee`](https://github.com/xetorthio/jedis/commit/b7120ee559ccd225f7392cbe44fc55835a656710) Clean warning - add template type (byte[]) ([#1869](https://github-redirect.dependabot.com/xetorthio/jedis/issues/1869)) - [`364199b`](https://github.com/xetorthio/jedis/commit/364199bd7a696e56f6df773f6b919263e6d6edd4) Fix typos and add JDoc to waitReplicas ([#1866](https://github-redirect.dependabot.com/xetorthio/jedis/issues/1866)) - Additional commits viewable in [compare view](https://github.com/xetorthio/jedis/compare/jedis-2.8.0...jedis-3.0.0)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=redis.clients:jedis&package-manager=maven&previous-version=2.8.0&new-version=3.0.0)](https://dependabot.com/compatibility-score.html?dependency-name=redis.clients:jedis&package-manager=maven&previous-version=2.8.0&new-version=3.0.0) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- examples/redis-backed-cache-testng/pom.xml | 2 +- examples/redis-backed-cache/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/redis-backed-cache-testng/pom.xml b/examples/redis-backed-cache-testng/pom.xml index 09a339d7c39..344ee5ce50b 100644 --- a/examples/redis-backed-cache-testng/pom.xml +++ b/examples/redis-backed-cache-testng/pom.xml @@ -15,7 +15,7 @@ redis.clients jedis - 2.8.0 + 3.0.0 com.google.code.gson diff --git a/examples/redis-backed-cache/pom.xml b/examples/redis-backed-cache/pom.xml index 3cecb5fd482..ec5394e2494 100644 --- a/examples/redis-backed-cache/pom.xml +++ b/examples/redis-backed-cache/pom.xml @@ -14,7 +14,7 @@ redis.clients jedis - 2.8.0 + 3.0.0 com.google.code.gson From 267742ddb7c2e9c94006422b3448e3628e2eac01 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 24 Dec 2018 08:47:48 +0000 Subject: [PATCH 0507/5615] Bump maven-compiler-plugin from 3.1 to 3.8.0 in /examples (#1084) Bumps [maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.1 to 3.8.0. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.1...maven-compiler-plugin-3.8.0) Signed-off-by: dependabot[bot] --- examples/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/pom.xml b/examples/pom.xml index c26d36efe3d..48ad43685df 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -57,7 +57,7 @@ maven-compiler-plugin - 3.1 + 3.8.0 1.8 1.8 From 6223403fde25a441713c4d746359b6b9d10233d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 24 Dec 2018 08:48:06 +0000 Subject: [PATCH 0508/5615] Bump mockito-all from 1.9.5 to 1.10.19 in /examples (#1083) Bumps [mockito-all](https://github.com/mockito/mockito) from 1.9.5 to 1.10.19. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/commits) Signed-off-by: dependabot[bot] --- examples/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/pom.xml b/examples/pom.xml index 48ad43685df..be5556f4684 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -48,7 +48,7 @@ org.mockito mockito-all - 1.9.5 + 1.10.19 test From 5694374e9cd63c72cd3703786aa9ee2620b7bf82 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 24 Dec 2018 08:48:30 +0000 Subject: [PATCH 0509/5615] Bump okhttp from 3.12.0 to 3.12.1 in /examples (#1081) Bumps [okhttp](https://github.com/square/okhttp) from 3.12.0 to 3.12.1. - [Release notes](https://github.com/square/okhttp/releases) - [Changelog](https://github.com/square/okhttp/blob/master/CHANGELOG.md) - [Commits](https://github.com/square/okhttp/compare/parent-3.12.0...parent-3.12.1) Signed-off-by: dependabot[bot] --- examples/linked-container/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/pom.xml b/examples/linked-container/pom.xml index 0ae81cfdfb8..4579949af09 100644 --- a/examples/linked-container/pom.xml +++ b/examples/linked-container/pom.xml @@ -38,7 +38,7 @@ com.squareup.okhttp3 okhttp - 3.12.0 + 3.12.1 org.json From 079cf15deea48e558fac0d380d2ec355ca7199a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 24 Dec 2018 08:51:57 +0000 Subject: [PATCH 0510/5615] Bump spinach from 0.2 to 0.3 in /examples (#1032) Bumps [spinach](https://github.com/mp911de/spinach) from 0.2 to 0.3.
Release notes *Sourced from [spinach's releases](https://github.com/mp911de/spinach/releases).* > ## 0.3 > ## SocketAddressSupplier API > > The SocketAddressSupplier API allows to control the used > connection points/SocketAddresses for initial connection and reconnection. > The use case for controlling the source and sequence of > connection points are failover and cluster node discovery. > > The API exposes a factory for `SocketAddressSupplier`s which accepts > a `RedisURI`. By default, multiple addresses are utilized in with RoundRobin. > > The predefined methods can be found in the `SocketAddressSupplierFactory.Factories` enum: > - `ROUND_ROBIN`: Cyclic use of multiple addresses specified by the `RedisURI` > - `HELLO_CLUSTER`: Uses `ROUND_ROBIN` for the initial connect. Once a connection is > established the mechanism obtains the cluster nodes using the `HELLO` > command _AT STARTUP_. Periodical scheduling/updating is currently not implemented. > This can be however achieved by implementing an own factory > that calls the `reloadNodes()` method periodically/on demand. > > New mechanisms can be implemented by implementing the > `SocketAddressSupplier`/`SocketAddressSupplierFactory` interfaces. > > Submit your mechanism by opening a Pull-Request if you want to contribute to spinach. > > ``` > DisqueClient client = new DisqueClient(); > > DisqueConnection connection = client.connect(new Utf8StringCodec(), disqueURI, > SocketAddressSupplierFactory.Factories.ROUND_ROBIN); > > > DisqueConnection connection = client.connect(new Utf8StringCodec(), disqueURI, > SocketAddressSupplierFactory.Factories.HELLO_CLUSTER); > ``` > > ## QueueListener API > > Spinach allows a push pattern to obtain jobs from Disque. The QueueListener API > introduces a listener/notification style by utilizing rx-java Observables. > The QueueListener API exposes an `Observable` that emits jobs. > The jobs can be processed asynchronously by applying transformations > or `doOnNext` steps. Each `Observable` allocates at the time > of subscription resources. The resources are released when unsubscribing > from the subscription. > > ``` > QueueListenerFactory queueListenerFactory = QueueListenerFactory.create(disqueURI, > new Utf8StringCodec(), "queue"); // 1 > > Observable> getjobs = queueListenerFactory.getjobs(); // 2 > ... (truncated)
Changelog *Sourced from [spinach's changelog](https://github.com/mp911de/spinach/blob/master/RELEASE-NOTES.md).* > spinach 0.3 RELEASE NOTES > > > SocketAddressSupplier API > ------------------------- > > The SocketAddressSupplier API allows to control the used > connection points/SocketAddresses for initial connection and reconnection. > The use case for controlling the source and sequence of > connection points are failover and cluster node discovery. > > The API exposes a factory for `SocketAddressSupplier`s which accepts > a `RedisURI`. By default, multiple addresses are utilized in with RoundRobin. > > The predefined methods can be found in the `SocketAddressSupplierFactory.Factories` enum: > > * `ROUND_ROBIN`: Cyclic use of multiple addresses specified by the `RedisURI` > * `HELLO_CLUSTER`: Uses `ROUND_ROBIN` for the initial connect. Once a connection is > established the mechanism obtains the cluster nodes using the `HELLO` > command *AT STARTUP*. Periodical scheduling/updating is currently not implemented. > This can be however achieved by implementing an own factory > that calls the `reloadNodes()` method periodically/on demand. > > New mechanisms can be implemented by implementing the > `SocketAddressSupplier`/`SocketAddressSupplierFactory` interfaces. > > Submit your mechanism by opening a Pull-Request if you want to contribute to spinach. > > > ``` > DisqueClient client = new DisqueClient(); > > DisqueConnection connection = client.connect(new Utf8StringCodec(), disqueURI, > SocketAddressSupplierFactory.Factories.ROUND_ROBIN); > > > DisqueConnection connection = client.connect(new Utf8StringCodec(), disqueURI, > SocketAddressSupplierFactory.Factories.HELLO_CLUSTER); > ``` > > QueueListener API > ----------------- > > Spinach allows a push pattern to obtain jobs from Disque. The QueueListener API > introduces a listener/notification style by utilizing rx-java Observables. > The QueueListener API exposes an `Observable` that emits jobs. > The jobs can be processed asynchronously by applying transformations > or `doOnNext` steps. Each `Observable` allocates at the time > of subscription resources. The resources are released when unsubscribing > from the subscription. > ... (truncated)
Commits - [`0b02fbc`](https://github.com/mp911de/spinach/commit/0b02fbcd3688b9b857906fde22da2a76972657ed) [maven-release-plugin] prepare release 0.3 - [`1608d33`](https://github.com/mp911de/spinach/commit/1608d33351200c038d6a11a9aca3ccfa6c7313c4) Improve test stability - [`af07f39`](https://github.com/mp911de/spinach/commit/af07f3960a6bc79c2626a0086a43917c88392774) Fix site name and copies - [`f3467b7`](https://github.com/mp911de/spinach/commit/f3467b7844c97b8f899e5418a41971a486f507f8) Prepare release - [`e61d5cb`](https://github.com/mp911de/spinach/commit/e61d5cbb1dee677bdcc71a9d32f9e3d9d808e561) Upgrade netty to 4.0.34.Final, use lettuce 3.4.1 release instead of snapshot - [`da1a9eb`](https://github.com/mp911de/spinach/commit/da1a9eb98fc503d2b6e15287503cc665ece36f3e) Update release notes - [`0574f58`](https://github.com/mp911de/spinach/commit/0574f58da88616aeab5f6d71a82548791bc9ff8f) Add factory methods and client resources, upgrade to lettuce 3.4 - [`cee5bdb`](https://github.com/mp911de/spinach/commit/cee5bdb871264d4d72dcf85d5b8c9f21e1cf9e2b) Fix addjob documentation about timeout/TTL [#21](https://github-redirect.dependabot.com/mp911de/spinach/issues/21) - [`e0932b8`](https://github.com/mp911de/spinach/commit/e0932b86049b68915065715405a3f3ecca52d86f) Polishing - [`80542ad`](https://github.com/mp911de/spinach/commit/80542ad5c3f5cb0591f8233fa77f236c6cf86138) Implement QSTAT command [#20](https://github-redirect.dependabot.com/mp911de/spinach/issues/20) - Additional commits viewable in [compare view](https://github.com/mp911de/spinach/compare/0.2...0.3)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=biz.paluch.redis:spinach&package-manager=maven&previous-version=0.2&new-version=0.3)](https://dependabot.com/compatibility-score.html?dependency-name=biz.paluch.redis:spinach&package-manager=maven&previous-version=0.2&new-version=0.3) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- examples/disque-job-queue/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/disque-job-queue/pom.xml b/examples/disque-job-queue/pom.xml index beaa63fd181..3e820e0cd98 100644 --- a/examples/disque-job-queue/pom.xml +++ b/examples/disque-job-queue/pom.xml @@ -14,7 +14,7 @@ biz.paluch.redis spinach - 0.2 + 0.3 com.google.code.gson From 59d9262f9b0244651ae1a84e9541b7780f03b744 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 24 Dec 2018 08:52:30 +0000 Subject: [PATCH 0511/5615] Bump commons-dbutils from 1.6 to 1.7 in /modules/mariadb (#1073) Bumps commons-dbutils from 1.6 to 1.7. [![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=commons-dbutils:commons-dbutils&package-manager=gradle&previous-version=1.6&new-version=1.7)](https://dependabot.com/compatibility-score.html?dependency-name=commons-dbutils:commons-dbutils&package-manager=gradle&previous-version=1.6&new-version=1.7) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 6ef081e8133..b9ab673e121 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -5,7 +5,7 @@ dependencies { testCompile 'org.mariadb.jdbc:mariadb-java-client:1.5.9' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' - testCompile 'commons-dbutils:commons-dbutils:1.6' + testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.14' testCompile 'org.vibur:vibur-dbcp:22.2' } From 291824321dc95e5849fbb0280d4f6afb91a5cd25 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 24 Dec 2018 09:35:11 +0000 Subject: [PATCH 0512/5615] Bump amqp-client from 3.5.3 to 5.5.1 in /core (#1076) Bumps [amqp-client](https://github.com/rabbitmq/rabbitmq-java-client) from 3.5.3 to 5.5.1.
Release notes *Sourced from [amqp-client's releases](https://github.com/rabbitmq/rabbitmq-java-client/releases).* > ## 5.5.1 > This is a patch release with a bug fix. All users of the 5.x.x series are encouraged to upgrade to this version. > > # Changes between 5.5.0 and 5.5.1 > > ## Record recoverable entities in RPC channel methods > > GitHub issue: [#425](https://github-redirect.dependabot.com/rabbitmq/rabbitmq-java-client/issues/425) > > ## 5.5.0 > This is a maintenance release with 2 new features and a bug fix. All users of the 5.x.x series are encouraged to upgrade to this version. > > # Changes between 5.4.3 and 5.5.0 > > ## Add traffic listener > > GitHub issue: [#411](https://github-redirect.dependabot.com/rabbitmq/rabbitmq-java-client/issues/411) > > ## Connection recovery runs into timeouts when using NIO > > GitHub issue: [#413](https://github-redirect.dependabot.com/rabbitmq/rabbitmq-java-client/issues/413) > > ## Provide factories to create NIO artefacts > > GitHub issue: [#410](https://github-redirect.dependabot.com/rabbitmq/rabbitmq-java-client/issues/410) > > ## 4.9.0 > This is a maintenance release with 2 new features and a bug fix. All users of the 4.x.x and 3.6.x series are encouraged to upgrade to this version. > > # Changes between 4.8.3 and 4.9.0 > > ## Add traffic listener > > GitHub issue: [#411](https://github-redirect.dependabot.com/rabbitmq/rabbitmq-java-client/issues/411) > > ## Connection recovery runs into timeouts when using NIO > > GitHub issue: [#413](https://github-redirect.dependabot.com/rabbitmq/rabbitmq-java-client/issues/413) > > ## Provide factories to create NIO artefacts > > GitHub issue: [#410](https://github-redirect.dependabot.com/rabbitmq/rabbitmq-java-client/issues/410) > > ## 5.5.0.RC1 > This is a pre-release for 5.5.0, a maintenance release with 2 new features and a bug fix. All users of the 5.x.x series are encouraged to test this version. > > # Changes between 5.4.0 and 5.5.0.RC1 > > ## Add traffic listener > > ... (truncated)
Changelog *Sourced from [amqp-client's changelog](https://github.com/rabbitmq/rabbitmq-java-client/blob/v5.5.1/release-versions.txt).* > RELEASE_VERSION="5.5.1" > DEVELOPMENT_VERSION="5.5.2-SNAPSHOT"
Commits - See full diff in [compare view](https://github.com/rabbitmq/rabbitmq-java-client/commits/v5.5.1)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=com.rabbitmq:amqp-client&package-manager=gradle&previous-version=3.5.3&new-version=5.5.1)](https://dependabot.com/compatibility-score.html?dependency-name=com.rabbitmq:amqp-client&package-manager=gradle&previous-version=3.5.3&new-version=5.5.1) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 0af9c412fd2..2c0457f498d 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -117,7 +117,7 @@ dependencies { } testCompile 'redis.clients:jedis:3.0.0' - testCompile 'com.rabbitmq:amqp-client:3.5.3' + testCompile 'com.rabbitmq:amqp-client:5.5.1' testCompile 'org.mongodb:mongo-java-driver:3.0.2' testCompile ('org.mockito:mockito-core:1.10.19') { exclude(module: 'hamcrest-core') From 0577bb62db3527e8eab92c64f639611be13d6945 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 24 Dec 2018 09:35:43 +0000 Subject: [PATCH 0513/5615] Bump postgresql from 9.3-1101-jdbc41 to 42.2.5 in /modules/postgresql (#1049) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 9.3-1101-jdbc41 to 42.2.5.
Changelog *Sourced from [postgresql's changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md).* > ## [42.2.5] (2018-08-27) > ### Known issues > - 1ms per async copy call [issue 1312](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/issues/1312) > > ### Changed > - `ssl=true` implies `sslmode=verify-full`, that is it requires valid server certificate [cdeeaca4](https://github.com/pgjdbc/pgjdbc/commit/cdeeaca47dc3bc6f727c79a582c9e4123099526e) > > ### Added > - Support for `sslmode=allow/prefer/require` [cdeeaca4](https://github.com/pgjdbc/pgjdbc/commit/cdeeaca47dc3bc6f727c79a582c9e4123099526e) > > ### Fixed > - Security: added server hostname verification for non-default SSL factories in `sslmode=verify-full` (CVE-2018-10936) [cdeeaca4](https://github.com/pgjdbc/pgjdbc/commit/cdeeaca47dc3bc6f727c79a582c9e4123099526e) > - Updated documentation on SSL configuration [fa032732](https://github.com/pgjdbc/pgjdbc/commit/fa032732acfe51c6e663ee646dd5c1beaa1af857) > - Updated Japanese translations [PR 1275](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1275) > - IndexOutOfBounds on prepared multistatement with insert values [c2885dd0](https://github.com/pgjdbc/pgjdbc/commit/c2885dd0cfc793f81e5dd3ed2300bb32476eb14a) > > ## [42.2.4] (2018-07-14) > ### Changed > - PreparedStatement.setNull(int parameterIndex, int t, String typeName) no longer ignores the typeName > argument if it is not null [PR 1160](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1160) > > ### Fixed > - Fix treatment of SQL_TSI_YEAR, SQL_TSI_WEEK, SQL_TSI_MINUTE [PR 1250](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1250) > - Map integrity constraint violation to XA_RBINTEGRITY instead of XAER_RMFAIL [PR 1175](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1175) [f2d1352c](https://github.com/pgjdbc/pgjdbc/commit/f2d1352c2b3ea98492beb6127cd6d95039a0b92f) > > ## [42.2.3] (2018-07-12) > ### Known issues > - SQL_TSI_YEAR is treated as hour, SQL_TSI_WEEK is treated as hour, SQL_TSI_MINUTE is treated as second > > ### Changed > - Reduce the severity of the error log messages when an exception is re-thrown. The error will be > thrown to caller to be dealt with so no need to log at this verbosity by pgjdbc [PR 1187](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1187) > - Deprecate Fastpath API [PR 903](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/903) > - Support parenthesis in {oj ...} JDBC escape syntax [PR 1204](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1204) > - ubenchmark module moved pgjdbc/benchmarks repository due to licensing issues [PR 1215](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1215) > - Include section on how to submit a bug report in CONTRIBUTING.md [PR 951](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/951) > > ### Fixed > - getString for PGObject-based types returned "null" string instead of null [PR 1154](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1154) > - Field metadata cache can be disabled via databaseMetadataCacheFields=0 [PR 1052](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1052) > - Properly encode special symbols in passwords in BaseDataSource [PR 1201](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1201) > - Adjust date, hour, minute, second when rounding nanosecond part of a timestamp [PR 1212](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1212) > - perf: reduce memory allocations in query cache [PR 1227](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1227) > - perf: reduce memory allocations in SQL parser [PR 1230](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1230), [PR 1233](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1233) > - Encode URL parameters in BaseDataSource [PR 1201](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1201) > - Improve JavaDoc formatting [PR 1236](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1236) > > ## [42.2.2] (2018-03-15) > ### Added > - Documentation on server-side prepared statements [PR 1135](https://github-redirect.dependabot.com/pgjdbc/pgjdbc/pull/1135) > ... (truncated)
Commits - See full diff in [compare view](https://github.com/pgjdbc/pgjdbc/commits/REL42.2.5)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=org.postgresql:postgresql&package-manager=gradle&previous-version=9.3-1101-jdbc41&new-version=42.2.5)](https://dependabot.com/compatibility-score.html?dependency-name=org.postgresql:postgresql&package-manager=gradle&previous-version=9.3-1101-jdbc41&new-version=42.2.5) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 05db9d8bdd7..0ef6df998e3 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: JDBC :: PostgreSQL" dependencies { compile project(':jdbc') - testCompile 'org.postgresql:postgresql:9.3-1101-jdbc41' + testCompile 'org.postgresql:postgresql:42.2.5' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' } From 855906480f00948e047ff6dddf18c00f59f5c994 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 24 Dec 2018 09:37:08 +0000 Subject: [PATCH 0514/5615] Bump elasticsearch-rest-client from 6.4.1 to 6.5.4 in /modules/elasticsearch (#1058) Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 6.4.1 to 6.5.4.
Commits - See full diff in [compare view](https://github.com/elastic/elasticsearch/commits)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=org.elasticsearch.client:elasticsearch-rest-client&package-manager=gradle&previous-version=6.4.1&new-version=6.5.4)](https://dependabot.com/compatibility-score.html?dependency-name=org.elasticsearch.client:elasticsearch-rest-client&package-manager=gradle&previous-version=6.4.1&new-version=6.5.4) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 5b1f7abb4ac..4527ee18adc 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,6 +2,6 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') - testCompile "org.elasticsearch.client:elasticsearch-rest-client:6.4.1" + testCompile "org.elasticsearch.client:elasticsearch-rest-client:6.5.4" testCompile "org.elasticsearch.client:transport:6.4.1" } From 86daef780092ab3b4ef79c71e623cbd8cada3d2d Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 24 Dec 2018 09:39:02 +0000 Subject: [PATCH 0515/5615] Add categories to release drafter (#1074) --- .github/release-drafter.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 15ae17655c1..1316fd5050e 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -4,3 +4,10 @@ template: | ## What's Changed $CHANGES +categories: + - title: 🚀 Features + label: type/feature + - title: 🐛 Bug Fixes + label: type/bug + - title: 📦 Dependency updates + label: dependencies From dea4387f01b8cc35d7988be29b282040b423fc5f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 24 Dec 2018 09:39:50 +0000 Subject: [PATCH 0516/5615] Bump mysql-connector-java from 6.0.6 to 8.0.13 in /modules/junit-jupiter (#1071) Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 6.0.6 to 8.0.13.
Changelog *Sourced from [mysql-connector-java's changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES).* > # Changelog > # https://dev.mysql.com/doc/relnotes/connector-j/8.0/en/ > > Version 8.0.13 > > - Fix for Bug#91317 (28207422), Wrong defaults on collation mappings. > > - WL#12245, DevAPI: Implement connect timeout. > > - Fix for Bug#21774249, UNIT TEST FAILS WITH ERROR " 'CEST' IS UNRECOGNIZED TIME ZONE". > > - WL#11857, DevAPI: Implement connection pooling for xprotocol. > > - Fix for Bug#91873 (28444461), REMOVE USEOLDUTF8BEHAVIOR CONNECTION PROPERTY. > > - Fix for Bug#92264 (28594434), JSONPARSER PUTS UNNECESSARY MAXIMUM LIMIT ON JSONNUMBER TO 10 DIGITS. > > - WL#12110, Extend PropertyDefinitions.PropertyKey usage. > > - Fix for Bug#81063 (23098159), w/ rewriteBatchedStatements, when 2 tables involved, the rewriting not correct. > > - Fix for Bug#84813 (25501750), rewriteBatchedStatements fails in INSERT. > > - Fix for Bug#81196 (23227334), CONNECTOR/J NOT FOLLOWING DATABASE CHARACTER SET. > > - Fix for Bug#72609 (18749544), SETDATE() NOT USING A PROLEPTIC GREGORIAN CALENDAR. > > - Fix for Bug#87534 (26730196), UNION ALL query fails when useServerPrepStmts=true on database connection. > Test case only. Base bug fixed in MySQL 5.7.22. > > - Fix for Bug#89948 (27658489), Batched statements are not committed for useLocalTransactionState=true. > > - Fix for BUG#22305979, WRONG RECORD UPDATED IF SENDFRACTIONALSECONDS=FALSE AND SMT IS SCROLLABLE. > > - Fix for Bug#27102307, CHANGE USESSL AND VERIFYSERVERCERTIFICATE TO SSLMODE OPTION. > > - Fix for Bug#28150662, CONNECTOR/J 8 MALFORMED DATABASE URL EXCEPTION WHIT CORRECT URL STRING. > > - Fix for Bug#91421 (28246270), ALLOWED VALUES FOR ZERODATETIMEBEHAVIOR ARE INCOMPATIBLE WITH NETBEANS. > > - Fix for Bug#23045604, XSESSION.GETURI() RETURNS NPE. > > - Fix for Bug#21914769, NPE WHEN TRY TO EXECUTE INVALID JSON STRING. > > - Fix for Bug#BUG#90887 (28034570), DATABASEMETADATAUSINGINFOSCHEMA#GETTABLES FAILS IF METHOD ARGUMENTS ARE NULL. > > - Fix for Bug#28207088, C/JAVA: UPDATECLOB(INT COLUMNLABEL, JAVA.SQL.CLOB CLOB) IS FAILING. > > - Fix for Bug#27629553, NPE FROM GETSESSION() FOR SSL CONNECTION WHEN NO PASSWORD PASSED. > > ... (truncated)
Commits - [`66459e9`](https://github.com/mysql/mysql-connector-j/commit/66459e9d39c8fd09767992bc592acd2053279be6) Update for Bug#27102307 fix. - [`8838bbc`](https://github.com/mysql/mysql-connector-j/commit/8838bbc6413c48de369da90aa222c04ce7ca5a7a) Fix for testPooledSessions. - [`20e3aaa`](https://github.com/mysql/mysql-connector-j/commit/20e3aaabbb63b49f01039eee7ea7e04dd6850889) GPL license book update. - [`264f746`](https://github.com/mysql/mysql-connector-j/commit/264f746930fc6bcde0c6af8982544533addb63ee) Fix for testConnectionTimeout. - [`415c3cb`](https://github.com/mysql/mysql-connector-j/commit/415c3cb88b0b3f43a2508ef79835b90c4902a6cb) Fix for Bug#91317 (28207422), Wrong defaults on collation mappings. - [`33da3f3`](https://github.com/mysql/mysql-connector-j/commit/33da3f3987029bab0068eac3beb7ea79e8e62162) Protobuf version updated to 3.6.1. - [`47982f4`](https://github.com/mysql/mysql-connector-j/commit/47982f42ae2bb858c51c9f711133b7592e3a3a85) Fix for Bug#21774249. - [`8b59b9e`](https://github.com/mysql/mysql-connector-j/commit/8b59b9ebab61cc97bafe77ee05f79829b1376e47) WL#12245, DevAPI: Implement connect timeout. - [`edc3f92`](https://github.com/mysql/mysql-connector-j/commit/edc3f92195ab7e837e525111996743fea641dd96) Fix for Bug#21774249, UNIT TEST FAILS WITH ERROR " 'CEST' IS UNRECOGNIZED TIM... - [`94c7244`](https://github.com/mysql/mysql-connector-j/commit/94c7244d0fcac698707f62080e49760c73321899) Fix for property descriptions. - Additional commits viewable in [compare view](https://github.com/mysql/mysql-connector-j/compare/6.0.6...8.0.13)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=mysql:mysql-connector-java&package-manager=gradle&previous-version=6.0.6&new-version=8.0.13)](https://dependabot.com/compatibility-score.html?dependency-name=mysql:mysql-connector-java&package-manager=gradle&previous-version=6.0.6&new-version=8.0.13) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index f9bd917e78f..b6ef9550122 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -10,7 +10,7 @@ dependencies { testCompile 'redis.clients:jedis:2.8.0' testRuntime 'org.postgresql:postgresql:42.2.5' - testRuntime 'mysql:mysql-connector-java:6.0.6' + testRuntime 'mysql:mysql-connector-java:8.0.13' testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.3.1' } From 48e0004e46071d17ef86c62869e5e2697a779ab7 Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 24 Dec 2018 09:43:20 +0000 Subject: [PATCH 0517/5615] Add documentation category --- .github/release-drafter.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 1316fd5050e..1bfa59ca589 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -1,7 +1,7 @@ name-template: $NEXT_PATCH_VERSION tag-template: $NEXT_PATCH_VERSION template: | - ## What's Changed + # What's Changed $CHANGES categories: @@ -9,5 +9,7 @@ categories: label: type/feature - title: 🐛 Bug Fixes label: type/bug + - title: 📖 Documentation + label: type/docs - title: 📦 Dependency updates label: dependencies From 093086746617c4943eb0065c6925f8eccf79ce31 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 24 Dec 2018 09:43:46 +0000 Subject: [PATCH 0518/5615] Bump visible-assertions from 1.0.5 to 2.1.1 in /examples (#1082) Bumps [visible-assertions](https://github.com/rnorth/visible-assertions) from 1.0.5 to 2.1.1.
Changelog *Sourced from [visible-assertions's changelog](https://github.com/rnorth/visible-assertions/blob/master/CHANGELOG.md).* > ## [2.1.1] - 2018-07-11 > > * Bump JNA version > > ## [2.1.0] - 2017-11-11 > > * Switch TTY detection to be based on JNA, for smaller dependency chain. Special case behaviour is in place for tests running in IntelliJ IDEA and Maven, which are both capable of correctly handling ANSI output even though there is not a TTY. > > ## [2.0.0] - 2017-07-13 > > * Remove Jansi to reduce likelihood of native lib class conflicts when used in Gradle projects. ANSI output is implemented within the library now, with pnr-posix providing isatty support. > * Allow system properties to be used to control visibility of output (see [README](https://github.com/rnorth/visible-assertions/blob/master/README.md)). > * Add `assertVisiblyEquals` and `assertRoughlyEquals` methods for approximate matching.
Commits - [`1e72293`](https://github.com/rnorth/visible-assertions/commit/1e722935da787edb98743f23c53b51b96e90ce1a) Update changelog - [`e128e84`](https://github.com/rnorth/visible-assertions/commit/e128e843763877673993c68b966e8b3fdebf0164) update version of 'net.java.dev.jna' in pom.xml ([#9](https://github-redirect.dependabot.com/rnorth/visible-assertions/issues/9)) - [`1e31bd9`](https://github.com/rnorth/visible-assertions/commit/1e31bd9f2b5c55bad97fb73fb5dff25657a2206a) Revert pwd for deployment script execution - [`838a044`](https://github.com/rnorth/visible-assertions/commit/838a0441b7e9bdcdb8241e0b3604795f5a70c1e2) Use JNA for TTY detection instead of JNR-POSIX ([#7](https://github-redirect.dependabot.com/rnorth/visible-assertions/issues/7)) - [`db9e1fe`](https://github.com/rnorth/visible-assertions/commit/db9e1fe5df5cc3435c648c0cecf05c7efefa8b32) Update for ongoing development - [`48289dd`](https://github.com/rnorth/visible-assertions/commit/48289dd6acf60e485c9846aacd0ebc35d623dba1) Add profiles for Maven Central release - [`e05b106`](https://github.com/rnorth/visible-assertions/commit/e05b106907cb5e2a782d53aef9473bb6ea94edab) Add codesigning resources - [`108d0b4`](https://github.com/rnorth/visible-assertions/commit/108d0b435bea9dd23a4b830d0688749a676f19c9) Update changelog for 2.0.0 release - [`712e4d6`](https://github.com/rnorth/visible-assertions/commit/712e4d6e80075a0c596586d4241d856fd4b6b43f) Update docs and allow overriding of output via System properties - [`e0953ad`](https://github.com/rnorth/visible-assertions/commit/e0953ad32e1cc015638a117c8b236b22a0c0ffd5) Add ability to silence passes/failures via environment variables - Additional commits viewable in [compare view](https://github.com/rnorth/visible-assertions/compare/visible-assertions-1.0.5...2.1.1)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=org.rnorth.visible-assertions:visible-assertions&package-manager=maven&previous-version=1.0.5&new-version=2.1.1)](https://dependabot.com/compatibility-score.html?dependency-name=org.rnorth.visible-assertions:visible-assertions&package-manager=maven&previous-version=1.0.5&new-version=2.1.1) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- examples/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/pom.xml b/examples/pom.xml index be5556f4684..14442f235e6 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -42,7 +42,7 @@ org.rnorth.visible-assertions visible-assertions - 1.0.5 + 2.1.1 test From 57120de5d921998fbec00b8f9973d1a2ed93f783 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 24 Dec 2018 09:44:29 +0000 Subject: [PATCH 0519/5615] Bump mysql-connector-java from 5.1.35 to 8.0.13 in /modules/mysql (#1043) Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 5.1.35 to 8.0.13.
Changelog *Sourced from [mysql-connector-java's changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES).* > # Changelog > # https://dev.mysql.com/doc/relnotes/connector-j/8.0/en/ > > Version 8.0.13 > > - Fix for Bug#91317 (28207422), Wrong defaults on collation mappings. > > - WL#12245, DevAPI: Implement connect timeout. > > - Fix for Bug#21774249, UNIT TEST FAILS WITH ERROR " 'CEST' IS UNRECOGNIZED TIME ZONE". > > - WL#11857, DevAPI: Implement connection pooling for xprotocol. > > - Fix for Bug#91873 (28444461), REMOVE USEOLDUTF8BEHAVIOR CONNECTION PROPERTY. > > - Fix for Bug#92264 (28594434), JSONPARSER PUTS UNNECESSARY MAXIMUM LIMIT ON JSONNUMBER TO 10 DIGITS. > > - WL#12110, Extend PropertyDefinitions.PropertyKey usage. > > - Fix for Bug#81063 (23098159), w/ rewriteBatchedStatements, when 2 tables involved, the rewriting not correct. > > - Fix for Bug#84813 (25501750), rewriteBatchedStatements fails in INSERT. > > - Fix for Bug#81196 (23227334), CONNECTOR/J NOT FOLLOWING DATABASE CHARACTER SET. > > - Fix for Bug#72609 (18749544), SETDATE() NOT USING A PROLEPTIC GREGORIAN CALENDAR. > > - Fix for Bug#87534 (26730196), UNION ALL query fails when useServerPrepStmts=true on database connection. > Test case only. Base bug fixed in MySQL 5.7.22. > > - Fix for Bug#89948 (27658489), Batched statements are not committed for useLocalTransactionState=true. > > - Fix for BUG#22305979, WRONG RECORD UPDATED IF SENDFRACTIONALSECONDS=FALSE AND SMT IS SCROLLABLE. > > - Fix for Bug#27102307, CHANGE USESSL AND VERIFYSERVERCERTIFICATE TO SSLMODE OPTION. > > - Fix for Bug#28150662, CONNECTOR/J 8 MALFORMED DATABASE URL EXCEPTION WHIT CORRECT URL STRING. > > - Fix for Bug#91421 (28246270), ALLOWED VALUES FOR ZERODATETIMEBEHAVIOR ARE INCOMPATIBLE WITH NETBEANS. > > - Fix for Bug#23045604, XSESSION.GETURI() RETURNS NPE. > > - Fix for Bug#21914769, NPE WHEN TRY TO EXECUTE INVALID JSON STRING. > > - Fix for Bug#BUG#90887 (28034570), DATABASEMETADATAUSINGINFOSCHEMA#GETTABLES FAILS IF METHOD ARGUMENTS ARE NULL. > > - Fix for Bug#28207088, C/JAVA: UPDATECLOB(INT COLUMNLABEL, JAVA.SQL.CLOB CLOB) IS FAILING. > > - Fix for Bug#27629553, NPE FROM GETSESSION() FOR SSL CONNECTION WHEN NO PASSWORD PASSED. > > ... (truncated)
Commits - [`66459e9`](https://github.com/mysql/mysql-connector-j/commit/66459e9d39c8fd09767992bc592acd2053279be6) Update for Bug#27102307 fix. - [`8838bbc`](https://github.com/mysql/mysql-connector-j/commit/8838bbc6413c48de369da90aa222c04ce7ca5a7a) Fix for testPooledSessions. - [`20e3aaa`](https://github.com/mysql/mysql-connector-j/commit/20e3aaabbb63b49f01039eee7ea7e04dd6850889) GPL license book update. - [`264f746`](https://github.com/mysql/mysql-connector-j/commit/264f746930fc6bcde0c6af8982544533addb63ee) Fix for testConnectionTimeout. - [`415c3cb`](https://github.com/mysql/mysql-connector-j/commit/415c3cb88b0b3f43a2508ef79835b90c4902a6cb) Fix for Bug#91317 (28207422), Wrong defaults on collation mappings. - [`33da3f3`](https://github.com/mysql/mysql-connector-j/commit/33da3f3987029bab0068eac3beb7ea79e8e62162) Protobuf version updated to 3.6.1. - [`47982f4`](https://github.com/mysql/mysql-connector-j/commit/47982f42ae2bb858c51c9f711133b7592e3a3a85) Fix for Bug#21774249. - [`8b59b9e`](https://github.com/mysql/mysql-connector-j/commit/8b59b9ebab61cc97bafe77ee05f79829b1376e47) WL#12245, DevAPI: Implement connect timeout. - [`edc3f92`](https://github.com/mysql/mysql-connector-j/commit/edc3f92195ab7e837e525111996743fea641dd96) Fix for Bug#21774249, UNIT TEST FAILS WITH ERROR " 'CEST' IS UNRECOGNIZED TIM... - [`94c7244`](https://github.com/mysql/mysql-connector-j/commit/94c7244d0fcac698707f62080e49760c73321899) Fix for property descriptions. - Additional commits viewable in [compare view](https://github.com/mysql/mysql-connector-j/compare/5.1.35...8.0.13)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=mysql:mysql-connector-java&package-manager=gradle&previous-version=5.1.35&new-version=8.0.13)](https://dependabot.com/compatibility-score.html?dependency-name=mysql:mysql-connector-java&package-manager=gradle&previous-version=5.1.35&new-version=8.0.13) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 6ff4c55af02..2f37cc9f795 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: JDBC :: MySQL" dependencies { compile project(':jdbc') - testCompile 'mysql:mysql-connector-java:5.1.35' + testCompile 'mysql:mysql-connector-java:8.0.13' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.14' testCompile 'commons-dbutils:commons-dbutils:1.7' From 1d03cbacf398f82758bd4c8d71d609c155b40fa5 Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 24 Dec 2018 11:07:39 +0000 Subject: [PATCH 0520/5615] SQL compound statement awareness in ScriptUtils (#579) * Fixes #570: Fix handling of SQL compound statements Prevents compound statements from being naively split by ScriptUtils * Simplify contentMatches method * Test for and fix more splitting scenarios * Update modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java Co-Authored-By: rnorth --- .../org/testcontainers/ext/ScriptUtils.java | 48 +++++++++++++++++-- .../testcontainers/ext/ScriptUtilsTest.java | 31 ++++++++++++ .../src/test/resources/splittable.sql | 45 +++++++++++++++++ .../test/resources/somepath/init_mysql.sql | 42 +++++++++++++++- 4 files changed, 161 insertions(+), 5 deletions(-) create mode 100644 modules/database-commons/src/test/java/org/testcontainers/ext/ScriptUtilsTest.java create mode 100644 modules/database-commons/src/test/resources/splittable.sql diff --git a/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java b/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java index 73c49c0947c..0f917a1561f 100644 --- a/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java +++ b/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java @@ -118,8 +118,12 @@ public static void splitSqlScript(String resource, String script, String separat checkArgument(StringUtils.isNotEmpty(blockCommentEndDelimiter), "blockCommentEndDelimiter must not be null or empty"); StringBuilder sb = new StringBuilder(); - boolean inLiteral = false; boolean inEscape = false; + boolean inLineComment = false; + boolean inBlockComment = false; + Character currentLiteralDelimiter = null; + + int compoundStatementDepth = 0; char[] content = script.toCharArray(); for (int i = 0; i < script.length(); i++) { char c = content[i]; @@ -134,10 +138,39 @@ public static void splitSqlScript(String resource, String script, String separat sb.append(c); continue; } - if (c == '\'') { - inLiteral = !inLiteral; + // Determine whether we're entering/leaving a string literal + if (!inBlockComment && !inLineComment && (c == '\'' || c == '"' || c == '`')) { + if (currentLiteralDelimiter == null) { // ignore delimiters within an existing string literal + currentLiteralDelimiter = c; + } else if (currentLiteralDelimiter == c) { // find end of string literal + currentLiteralDelimiter = null; + } + } + final boolean inLiteral = currentLiteralDelimiter != null; + + if (!inLiteral && containsSubstringAtOffset(script, commentPrefix, i)) { + inLineComment = true; + } + if (inLineComment && c == '\n') { + inLineComment = false; + } + if (!inLiteral && containsSubstringAtOffset(script, blockCommentStartDelimiter, i)) { + inBlockComment = true; } - if (!inLiteral) { + if (!inLiteral && inBlockComment && containsSubstringAtOffset(script, blockCommentEndDelimiter, i)) { + inBlockComment = false; + } + final boolean inComment = inLineComment || inBlockComment; + + if (!inLiteral && !inComment && containsSubstringAtOffset(script, "BEGIN", i)) { + compoundStatementDepth++; + } + if (!inLiteral && !inComment && containsSubstringAtOffset(script, "END", i)) { + compoundStatementDepth--; + } + final boolean inCompoundStatement = compoundStatementDepth != 0; + + if (!inLiteral && !inCompoundStatement) { if (script.startsWith(separator, i)) { // we've reached the end of the current statement if (sb.length() > 0) { @@ -189,6 +222,13 @@ else if (c == ' ' || c == '\n' || c == '\t') { } } + private static boolean containsSubstringAtOffset(String string, String substring, int offset) { + String lowercaseString = string.toLowerCase(); + String lowercaseSubstring = substring.toLowerCase(); + + return lowercaseString.startsWith(lowercaseSubstring, offset); + } + private static void checkArgument(boolean expression, String errorMessage) { if (!expression) { throw new IllegalArgumentException(errorMessage); diff --git a/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptUtilsTest.java b/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptUtilsTest.java new file mode 100644 index 00000000000..7a50026cd93 --- /dev/null +++ b/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptUtilsTest.java @@ -0,0 +1,31 @@ +package org.testcontainers.ext; + +import com.google.common.base.Charsets; +import com.google.common.io.Resources; +import org.junit.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +public class ScriptUtilsTest { + + /* + * Test ScriptUtils script splitting with some ugly/hard-to-split cases + */ + @Test + public void testSplit() throws IOException { + final String script = Resources.toString(Resources.getResource("splittable.sql"), Charsets.UTF_8); + + final List statements = new ArrayList<>(); + ScriptUtils.splitSqlScript("resourcename", script, ";", "--", "/*", "*/", statements); + + assertEquals(7, statements.size()); + assertEquals("SELECT \"a /* string literal containing comment characters like -- here\"", statements.get(2)); + assertEquals("SELECT \"a 'quoting' \\\"scenario ` involving BEGIN keyword\\\" here\"", statements.get(3)); + assertEquals("SELECT * from `bar`", statements.get(4)); + assertEquals("INSERT INTO bar (foo) VALUES ('hello world')", statements.get(6)); + } +} diff --git a/modules/database-commons/src/test/resources/splittable.sql b/modules/database-commons/src/test/resources/splittable.sql new file mode 100644 index 00000000000..addede19dd7 --- /dev/null +++ b/modules/database-commons/src/test/resources/splittable.sql @@ -0,0 +1,45 @@ +CREATE TABLE bar ( + foo VARCHAR(255) +); + +DROP PROCEDURE IF EXISTS -- ; + count_foo; + +SELECT "a /* string literal containing comment characters like -- here"; +SELECT "a 'quoting' \"scenario ` involving BEGIN keyword\" here"; +SELECT * from `bar`; + +-- What about a line comment containing imbalanced string delimiters? " + +CREATE PROCEDURE count_foo() + BEGIN + + BEGIN + SELECT * + FROM bar; + SELECT 1 + FROM dual; + END; + + BEGIN + select * from bar; + END; + + -- we can do comments + + /* including block + comments + */ + + /* what if BEGIN appears inside a comment? */ + + select "or what if BEGIN appears inside a literal?"; + + END /*; */; + +/* or a block comment + containing imbalanced string delimiters? + ' " + */ + +INSERT INTO bar (foo) /* ; */ VALUES ('hello world'); diff --git a/modules/jdbc-test/src/test/resources/somepath/init_mysql.sql b/modules/jdbc-test/src/test/resources/somepath/init_mysql.sql index 2b00ee968b0..addede19dd7 100644 --- a/modules/jdbc-test/src/test/resources/somepath/init_mysql.sql +++ b/modules/jdbc-test/src/test/resources/somepath/init_mysql.sql @@ -2,4 +2,44 @@ CREATE TABLE bar ( foo VARCHAR(255) ); -INSERT INTO bar (foo) VALUES ('hello world'); \ No newline at end of file +DROP PROCEDURE IF EXISTS -- ; + count_foo; + +SELECT "a /* string literal containing comment characters like -- here"; +SELECT "a 'quoting' \"scenario ` involving BEGIN keyword\" here"; +SELECT * from `bar`; + +-- What about a line comment containing imbalanced string delimiters? " + +CREATE PROCEDURE count_foo() + BEGIN + + BEGIN + SELECT * + FROM bar; + SELECT 1 + FROM dual; + END; + + BEGIN + select * from bar; + END; + + -- we can do comments + + /* including block + comments + */ + + /* what if BEGIN appears inside a comment? */ + + select "or what if BEGIN appears inside a literal?"; + + END /*; */; + +/* or a block comment + containing imbalanced string delimiters? + ' " + */ + +INSERT INTO bar (foo) /* ; */ VALUES ('hello world'); From f986243b2381eb71a8600e1bd920f4005e0ee74f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 24 Dec 2018 11:10:05 +0000 Subject: [PATCH 0521/5615] Bump influxdb-java from 2.10 to 2.14 in /modules/influxdb (#1070) Bumps [influxdb-java](https://github.com/influxdata/influxdb-java) from 2.10 to 2.14.
Changelog *Sourced from [influxdb-java's changelog](https://github.com/influxdata/influxdb-java/blob/master/CHANGELOG.md).* > ## 2.14 [2018-10-12] > > ### Fixes > > - Fixed chunked query exception handling [Issue #523](https://github-redirect.dependabot.com/influxdata/influxdb-java/issues/523) > - Memory leak in StringBuilder cache for Point.lineprotocol() [Issue #526](https://github-redirect.dependabot.com/influxdata/influxdb-java/issues/521) > > ## 2.13 [2018-09-12] > > ### Fixes > - MessagePack queries: Exception during parsing InfluxDB version [macOS] [PR #487](https://github-redirect.dependabot.com/influxdata/influxdb-java/issues/487) > - The InfluxDBResultMapper is able to handle results with a different time precision [PR #501](https://github-redirect.dependabot.com/influxdata/influxdb-java/pull/501) > - UDP target host address is cached [PR #502](https://github-redirect.dependabot.com/influxdata/influxdb-java/issues/502) > - Error messages from server not parsed correctly when using msgpack [PR #506](https://github-redirect.dependabot.com/influxdata/influxdb-java/issues/506) > - Response body must be closed properly in case of JSON response [PR #514](https://github-redirect.dependabot.com/influxdata/influxdb-java/issues/514) > - Time is serialized not consistently in MsgPack and Json, missing millis and nanos in MsgPack[PR #517](https://github-redirect.dependabot.com/influxdata/influxdb-java/issues/517) > > ### Features > > - Support for Basic Authentication [PR #492](https://github-redirect.dependabot.com/influxdata/influxdb-java/pull/492) > - Added possibility to reuse client as a core part of [influxdb-java-reactive](https://github.com/bonitoo-io/influxdb-java-reactive) client [PR #493](https://github-redirect.dependabot.com/influxdata/influxdb-java/pull/493) > - Retry capability for writing of BatchPoints [PR #503](https://github-redirect.dependabot.com/influxdata/influxdb-java/issues/503) > - Added `BiConsumer` with capability to discontinue a streaming query [Issue #515](https://github-redirect.dependabot.com/influxdata/influxdb-java/issues/515) > - Added `onComplete` action that is invoked after successfully end of streaming query [Issue #515](https://github-redirect.dependabot.com/influxdata/influxdb-java/issues/515) > > ## 2.12 [2018-07-31] > > ### Fixes > > - Remove code which checks for unsupported influxdb versions [PR #474](https://github-redirect.dependabot.com/influxdata/influxdb-java/pull/474) > - Unpredictable errors when OkHttpClient.Builder instance is reused [PR #478](https://github-redirect.dependabot.com/influxdata/influxdb-java/pull/478) > > ### Features > > - Support for MessagePack [PR #471](https://github-redirect.dependabot.com/influxdata/influxdb-java/pull/471) > - Cache version per influxdb instance and reduce ping() calls for every query call [PR #472](https://github-redirect.dependabot.com/influxdata/influxdb-java/pull/472) > - FAQ list for influxdb-java [PR #475](https://github-redirect.dependabot.com/influxdata/influxdb-java/pull/475) > > ### Improvements > > - Test: Unit test to ensure tags should be sorted by key in line protocol (to reduce db server overheads) [PR #476](https://github-redirect.dependabot.com/influxdata/influxdb-java/pull/476) > > ## 2.11 [2018-07-02] > > ### Features > > - Allow write precision of TimeUnit other than Nanoseconds [PR #321](https://github-redirect.dependabot.com/influxdata/influxdb-java/pull/321) > - Support dynamic measurement name in InfluxDBResultMapper [PR #423](https://github-redirect.dependabot.com/influxdata/influxdb-java/pull/423) > - Debug mode which allows HTTP requests being sent to the database to be logged [PR #450](https://github-redirect.dependabot.com/influxdata/influxdb-java/pull/450) > - Fix problem of connecting to the influx api with URL which does not points to the url root (e.g. localhots:80/influx-api/) [PR #400] (https://github-redirect.dependabot.com/influxdata/influxdb-java/pull/400) > ... (truncated)
Commits - [`91d0f09`](https://github.com/influxdata/influxdb-java/commit/91d0f09f0c19bf5142d4c2e246155b80fc3f8ade) [maven-release-plugin] prepare release influxdb-java-2.14 - [`8ffaeb9`](https://github.com/influxdata/influxdb-java/commit/8ffaeb9c290983916e5aff196dede98126f2c128) Revert "[maven-release-plugin] prepare release influxdb-java-2.14" - [`2781da2`](https://github.com/influxdata/influxdb-java/commit/2781da2d1b988ca414e04acbd630c16134266b0d) [maven-release-plugin] prepare release influxdb-java-2.14 - [`19c69ed`](https://github.com/influxdata/influxdb-java/commit/19c69ed70d3e8d6322a6c0b5d7ff2bc9e378e3da) [maven-release-plugin] prepare for next development iteration - [`c6d7f25`](https://github.com/influxdata/influxdb-java/commit/c6d7f25b70fd498afc0d5dc46d1f7ca1adeb88e3) [maven-release-plugin] prepare release influxdb-java-2.14 - [`2f4c594`](https://github.com/influxdata/influxdb-java/commit/2f4c594fdc429e044e4ed614570d26898896f467) Merge pull request [#531](https://github-redirect.dependabot.com/influxdata/influxdb-java/issues/531) from heshengbang/master - [`f653e62`](https://github.com/influxdata/influxdb-java/commit/f653e620ac191cdca57c9cfaf9b859f734df6256) Easy to use try-with-resources, add README.md - [`c7be9b0`](https://github.com/influxdata/influxdb-java/commit/c7be9b0fe146a6a28bca75b8dfc5129cf84bdcd7) Easy to use try-with-resources - [`4590d18`](https://github.com/influxdata/influxdb-java/commit/4590d1873ee3b49bfe1162dbd0bbfb3a5f784f65) - added automated SNAPSHOT publishing to Maven Central repository - [`ce65a41`](https://github.com/influxdata/influxdb-java/commit/ce65a4111021dfabc5ab0dcecff13b32a0ce0401) - added automated SNAPSHOT publishing to Maven Central repository - Additional commits viewable in [compare view](https://github.com/influxdata/influxdb-java/compare/influxdb-java-2.10...influxdb-java-2.14)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=org.influxdb:influxdb-java&package-manager=gradle&previous-version=2.10&new-version=2.14)](https://dependabot.com/compatibility-score.html?dependency-name=org.influxdb:influxdb-java&package-manager=gradle&previous-version=2.10&new-version=2.14) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/influxdb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/influxdb/build.gradle b/modules/influxdb/build.gradle index e0cd0a4a53b..b0ce14b562e 100644 --- a/modules/influxdb/build.gradle +++ b/modules/influxdb/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: InfluxDB" dependencies { compile project(':testcontainers') - compileOnly 'org.influxdb:influxdb-java:2.10' + compileOnly 'org.influxdb:influxdb-java:2.14' testCompile 'org.influxdb:influxdb-java:2.10' } From 52ad19b52fad605e4153a2c482b1c1fa81d53f66 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 24 Dec 2018 13:03:32 +0100 Subject: [PATCH 0522/5615] Bump jersey-common from 2.23.1 to 2.27 in /core (#1078) Bumps jersey-common from 2.23.1 to 2.27. Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 2c0457f498d..899ee80c6e1 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -111,7 +111,7 @@ dependencies { shaded 'commons-io:commons-io:2.5' shaded 'commons-codec:commons-codec:1.11' // docker-java uses SslConfigurator from jersey-common for TLS - shaded ('org.glassfish.jersey.core:jersey-common:2.23.1') { + shaded ('org.glassfish.jersey.core:jersey-common:2.27') { // SslConfigurator doesn't use classes from dependencies exclude(module: '*') } From e6b8bcf089b53f3c817bef7b955d19508c907ba5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 24 Dec 2018 14:19:32 +0000 Subject: [PATCH 0523/5615] Bump java-client from 2.6.1 to 2.7.2 in /modules/couchbase (#1036) Bumps [java-client](https://github.com/couchbase/couchbase-java-client) from 2.6.1 to 2.7.2.
Commits - [`7fc04f0`](https://github.com/couchbase/couchbase-java-client/commit/7fc04f00a00deecb1b88e031e6a504996fb1949f) Prepare 2.7.2 release - [`37dbcb9`](https://github.com/couchbase/couchbase-java-client/commit/37dbcb94cc056b7803d00ae7cc373d7041289cbc) JCBC-1171 Allow to add hints for hash and nested loop joins in Query dsl - [`db9b353`](https://github.com/couchbase/couchbase-java-client/commit/db9b3532a17aba1d95ec2bb4f642e6172daf797b) JCBC-1258 Add support for analytics deferred queries - [`0e3e423`](https://github.com/couchbase/couchbase-java-client/commit/0e3e4234b9d2adba023a1233236dc6472b48e539) JCBC-1276 Allow to use hostnames and dont do premature reverse lookups - [`e191146`](https://github.com/couchbase/couchbase-java-client/commit/e1911466387727ad1161dfa3d9284a40b2973128) Fix ci failures - [`c3132fe`](https://github.com/couchbase/couchbase-java-client/commit/c3132fe75033da19e2789cde9a6845a502b6ccb3) Various fixes for ci - [`8424282`](https://github.com/couchbase/couchbase-java-client/commit/8424282fb1d609d8f5df03341077def985c45c9b) Prepare java-client to run in CI under the mock. - [`87abaef`](https://github.com/couchbase/couchbase-java-client/commit/87abaef43819d7c32596a7d6316153de22b5d939) JCBC-1254: Experimental Analytics Ingest Utility - [`64d2af3`](https://github.com/couchbase/couchbase-java-client/commit/64d2af3c477ed9c0bfff62e4e30853aca1fd2b29) JCBC-1266: Allow to compile against Java 11 - [`67cd46d`](https://github.com/couchbase/couchbase-java-client/commit/67cd46d6f8ba7dc25eebd70cebe6bd26bde4ed16) Bump to Java 1.7 as compat version - Additional commits viewable in [compare view](https://github.com/couchbase/couchbase-java-client/compare/2.6.1...2.7.2)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=com.couchbase.client:java-client&package-manager=gradle&previous-version=2.6.1&new-version=2.7.2)](https://dependabot.com/compatibility-score.html?dependency-name=com.couchbase.client:java-client&package-manager=gradle&previous-version=2.6.1&new-version=2.7.2) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index c89648ba2b7..50cf41038a7 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -2,5 +2,5 @@ description = "Testcontainers :: Couchbase" dependencies { compile project(':testcontainers') - compile 'com.couchbase.client:java-client:2.6.1' + compile 'com.couchbase.client:java-client:2.7.2' } From 0e17abdd5d3092c93dc8d7b4489efd58a287bcc7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 24 Dec 2018 14:37:42 +0000 Subject: [PATCH 0524/5615] Bump mysql-connector-java from 6.0.6 to 8.0.13 in /modules/spock (#1056) Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 6.0.6 to 8.0.13.
Changelog *Sourced from [mysql-connector-java's changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES).* > # Changelog > # https://dev.mysql.com/doc/relnotes/connector-j/8.0/en/ > > Version 8.0.13 > > - Fix for Bug#91317 (28207422), Wrong defaults on collation mappings. > > - WL#12245, DevAPI: Implement connect timeout. > > - Fix for Bug#21774249, UNIT TEST FAILS WITH ERROR " 'CEST' IS UNRECOGNIZED TIME ZONE". > > - WL#11857, DevAPI: Implement connection pooling for xprotocol. > > - Fix for Bug#91873 (28444461), REMOVE USEOLDUTF8BEHAVIOR CONNECTION PROPERTY. > > - Fix for Bug#92264 (28594434), JSONPARSER PUTS UNNECESSARY MAXIMUM LIMIT ON JSONNUMBER TO 10 DIGITS. > > - WL#12110, Extend PropertyDefinitions.PropertyKey usage. > > - Fix for Bug#81063 (23098159), w/ rewriteBatchedStatements, when 2 tables involved, the rewriting not correct. > > - Fix for Bug#84813 (25501750), rewriteBatchedStatements fails in INSERT. > > - Fix for Bug#81196 (23227334), CONNECTOR/J NOT FOLLOWING DATABASE CHARACTER SET. > > - Fix for Bug#72609 (18749544), SETDATE() NOT USING A PROLEPTIC GREGORIAN CALENDAR. > > - Fix for Bug#87534 (26730196), UNION ALL query fails when useServerPrepStmts=true on database connection. > Test case only. Base bug fixed in MySQL 5.7.22. > > - Fix for Bug#89948 (27658489), Batched statements are not committed for useLocalTransactionState=true. > > - Fix for BUG#22305979, WRONG RECORD UPDATED IF SENDFRACTIONALSECONDS=FALSE AND SMT IS SCROLLABLE. > > - Fix for Bug#27102307, CHANGE USESSL AND VERIFYSERVERCERTIFICATE TO SSLMODE OPTION. > > - Fix for Bug#28150662, CONNECTOR/J 8 MALFORMED DATABASE URL EXCEPTION WHIT CORRECT URL STRING. > > - Fix for Bug#91421 (28246270), ALLOWED VALUES FOR ZERODATETIMEBEHAVIOR ARE INCOMPATIBLE WITH NETBEANS. > > - Fix for Bug#23045604, XSESSION.GETURI() RETURNS NPE. > > - Fix for Bug#21914769, NPE WHEN TRY TO EXECUTE INVALID JSON STRING. > > - Fix for Bug#BUG#90887 (28034570), DATABASEMETADATAUSINGINFOSCHEMA#GETTABLES FAILS IF METHOD ARGUMENTS ARE NULL. > > - Fix for Bug#28207088, C/JAVA: UPDATECLOB(INT COLUMNLABEL, JAVA.SQL.CLOB CLOB) IS FAILING. > > - Fix for Bug#27629553, NPE FROM GETSESSION() FOR SSL CONNECTION WHEN NO PASSWORD PASSED. > > ... (truncated)
Commits - [`66459e9`](https://github.com/mysql/mysql-connector-j/commit/66459e9d39c8fd09767992bc592acd2053279be6) Update for Bug#27102307 fix. - [`8838bbc`](https://github.com/mysql/mysql-connector-j/commit/8838bbc6413c48de369da90aa222c04ce7ca5a7a) Fix for testPooledSessions. - [`20e3aaa`](https://github.com/mysql/mysql-connector-j/commit/20e3aaabbb63b49f01039eee7ea7e04dd6850889) GPL license book update. - [`264f746`](https://github.com/mysql/mysql-connector-j/commit/264f746930fc6bcde0c6af8982544533addb63ee) Fix for testConnectionTimeout. - [`415c3cb`](https://github.com/mysql/mysql-connector-j/commit/415c3cb88b0b3f43a2508ef79835b90c4902a6cb) Fix for Bug#91317 (28207422), Wrong defaults on collation mappings. - [`33da3f3`](https://github.com/mysql/mysql-connector-j/commit/33da3f3987029bab0068eac3beb7ea79e8e62162) Protobuf version updated to 3.6.1. - [`47982f4`](https://github.com/mysql/mysql-connector-j/commit/47982f42ae2bb858c51c9f711133b7592e3a3a85) Fix for Bug#21774249. - [`8b59b9e`](https://github.com/mysql/mysql-connector-j/commit/8b59b9ebab61cc97bafe77ee05f79829b1376e47) WL#12245, DevAPI: Implement connect timeout. - [`edc3f92`](https://github.com/mysql/mysql-connector-j/commit/edc3f92195ab7e837e525111996743fea641dd96) Fix for Bug#21774249, UNIT TEST FAILS WITH ERROR " 'CEST' IS UNRECOGNIZED TIM... - [`94c7244`](https://github.com/mysql/mysql-connector-j/commit/94c7244d0fcac698707f62080e49760c73321899) Fix for property descriptions. - Additional commits viewable in [compare view](https://github.com/mysql/mysql-connector-j/compare/6.0.6...8.0.13)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=mysql:mysql-connector-java&package-manager=gradle&previous-version=6.0.6&new-version=8.0.13)](https://dependabot.com/compatibility-score.html?dependency-name=mysql:mysql-connector-java&package-manager=gradle&previous-version=6.0.6&new-version=8.0.13) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 149eec0ebca..553a87007ca 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -13,5 +13,5 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.2.0' testRuntime 'org.postgresql:postgresql:42.0.0' - testRuntime 'mysql:mysql-connector-java:6.0.6' + testRuntime 'mysql:mysql-connector-java:8.0.13' } \ No newline at end of file From d7b47fbe7d9a45b6a9338c649753c199b04ebac4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 25 Dec 2018 09:54:54 +0100 Subject: [PATCH 0525/5615] Bump spring-boot-dependencies (#1096) Bumps [spring-boot-dependencies](https://github.com/spring-projects/spring-boot) from 2.0.1.RELEASE to 2.1.1.RELEASE. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.0.1.RELEASE...v2.1.1.RELEASE) Signed-off-by: dependabot[bot] --- examples/spring-boot/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot/pom.xml b/examples/spring-boot/pom.xml index f01eaccc9e7..241e9ceae64 100644 --- a/examples/spring-boot/pom.xml +++ b/examples/spring-boot/pom.xml @@ -21,7 +21,7 @@ org.springframework.boot spring-boot-dependencies - 2.0.1.RELEASE + 2.1.1.RELEASE pom import From 7374f77936c8d2f197d0d06fdc622778c5e16e60 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 25 Dec 2018 09:55:07 +0100 Subject: [PATCH 0526/5615] Bump testng from 6.9.10 to 6.14.3 in /examples (#1095) Bumps [testng](https://github.com/cbeust/testng) from 6.9.10 to 6.14.3. - [Release notes](https://github.com/cbeust/testng/releases) - [Changelog](https://github.com/cbeust/testng/blob/master/CHANGES.txt) - [Commits](https://github.com/cbeust/testng/compare/6.9.10...6.14.3) Signed-off-by: dependabot[bot] --- examples/redis-backed-cache-testng/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/redis-backed-cache-testng/pom.xml b/examples/redis-backed-cache-testng/pom.xml index 344ee5ce50b..1f0b804eee4 100644 --- a/examples/redis-backed-cache-testng/pom.xml +++ b/examples/redis-backed-cache-testng/pom.xml @@ -42,7 +42,7 @@ org.testng testng - 6.9.10 + 6.14.3 test From 1196ec4f4c113fe64331934731eebe2e5771639a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 25 Dec 2018 09:55:22 +0100 Subject: [PATCH 0527/5615] Bump visible-assertions from 2.1.1 to 2.1.2 in /examples (#1094) Bumps [visible-assertions](https://github.com/rnorth/visible-assertions) from 2.1.1 to 2.1.2. - [Release notes](https://github.com/rnorth/visible-assertions/releases) - [Changelog](https://github.com/rnorth/visible-assertions/blob/master/CHANGELOG.md) - [Commits](https://github.com/rnorth/visible-assertions/compare/2.1.1...2.1.2) Signed-off-by: dependabot[bot] --- examples/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/pom.xml b/examples/pom.xml index 14442f235e6..bb48db6100c 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -42,7 +42,7 @@ org.rnorth.visible-assertions visible-assertions - 2.1.1 + 2.1.2 test From 615c77b2a10c0db283f446b993961024afde8057 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 25 Dec 2018 09:55:39 +0100 Subject: [PATCH 0528/5615] Bump gson from 2.5 to 2.8.5 in /examples (#1092) Bumps [gson](https://github.com/google/gson) from 2.5 to 2.8.5. - [Release notes](https://github.com/google/gson/releases) - [Changelog](https://github.com/google/gson/blob/master/CHANGELOG.md) - [Commits](https://github.com/google/gson/compare/gson-parent-2.5...gson-parent-2.8.5) Signed-off-by: dependabot[bot] --- examples/disque-job-queue/pom.xml | 2 +- examples/redis-backed-cache-testng/pom.xml | 2 +- examples/redis-backed-cache/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/disque-job-queue/pom.xml b/examples/disque-job-queue/pom.xml index 3e820e0cd98..c18c7477e07 100644 --- a/examples/disque-job-queue/pom.xml +++ b/examples/disque-job-queue/pom.xml @@ -19,7 +19,7 @@ com.google.code.gson gson - 2.5 + 2.8.5 com.google.guava diff --git a/examples/redis-backed-cache-testng/pom.xml b/examples/redis-backed-cache-testng/pom.xml index 1f0b804eee4..881c45a6b5a 100644 --- a/examples/redis-backed-cache-testng/pom.xml +++ b/examples/redis-backed-cache-testng/pom.xml @@ -20,7 +20,7 @@ com.google.code.gson gson - 2.5 + 2.8.5 com.google.guava diff --git a/examples/redis-backed-cache/pom.xml b/examples/redis-backed-cache/pom.xml index ec5394e2494..180e16cd7f3 100644 --- a/examples/redis-backed-cache/pom.xml +++ b/examples/redis-backed-cache/pom.xml @@ -19,7 +19,7 @@ com.google.code.gson gson - 2.5 + 2.8.5 com.google.guava From 30207a6d728a3771e660fbea0b6b8f06485f2dbd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 25 Dec 2018 10:10:48 +0100 Subject: [PATCH 0529/5615] Bump kafka-clients from 1.0.0 to 2.1.0 in /modules/kafka (#1062) Bumps kafka-clients from 1.0.0 to 2.1.0. Signed-off-by: dependabot[bot] --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index f52248045b1..462efe387ba 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Kafka" dependencies { compile project(':testcontainers') - testCompile 'org.apache.kafka:kafka-clients:1.0.0' + testCompile 'org.apache.kafka:kafka-clients:2.1.0' testCompile 'org.assertj:assertj-core:3.8.0' testCompile 'com.google.guava:guava:23.0' } From 6ef49c021019aa6a2670b1cd30e35a6b8390989c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 25 Dec 2018 10:12:22 +0100 Subject: [PATCH 0530/5615] Bump commons-io from 2.5 to 2.6 in /core (#1086) Bumps commons-io from 2.5 to 2.6. Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 899ee80c6e1..14c81882a3b 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -108,7 +108,7 @@ dependencies { shaded 'javax.ws.rs:javax.ws.rs-api:2.0.1' shaded 'org.zeroturnaround:zt-exec:1.10' shaded 'commons-lang:commons-lang:2.6' - shaded 'commons-io:commons-io:2.5' + shaded 'commons-io:commons-io:2.6' shaded 'commons-codec:commons-codec:1.11' // docker-java uses SslConfigurator from jersey-common for TLS shaded ('org.glassfish.jersey.core:jersey-common:2.27') { From a4c4cbe8bfda72661a0f4965e1629e9138c416bd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 25 Dec 2018 10:14:29 +0100 Subject: [PATCH 0531/5615] Bump jedis from 2.8.0 to 3.0.0 in /modules/junit-jupiter (#1065) Bumps [jedis](https://github.com/xetorthio/jedis) from 2.8.0 to 3.0.0. - [Release notes](https://github.com/xetorthio/jedis/releases) - [Commits](https://github.com/xetorthio/jedis/compare/jedis-2.8.0...jedis-3.0.0) Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index b6ef9550122..a12e6974a7d 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -7,7 +7,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') testCompile 'com.zaxxer:HikariCP:3.2.0' - testCompile 'redis.clients:jedis:2.8.0' + testCompile 'redis.clients:jedis:3.0.0' testRuntime 'org.postgresql:postgresql:42.2.5' testRuntime 'mysql:mysql-connector-java:8.0.13' From 5195402f9aac6f446550ca6a5512f777a289e902 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 25 Dec 2018 10:14:45 +0100 Subject: [PATCH 0532/5615] Bump mongo-java-driver from 3.0.2 to 3.9.1 in /core (#1089) Bumps [mongo-java-driver](https://github.com/mongodb/mongo-java-driver) from 3.0.2 to 3.9.1. - [Release notes](https://github.com/mongodb/mongo-java-driver/releases) - [Commits](https://github.com/mongodb/mongo-java-driver/compare/r3.0.2...r3.9.1) Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 14c81882a3b..aff84668171 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -118,7 +118,7 @@ dependencies { testCompile 'redis.clients:jedis:3.0.0' testCompile 'com.rabbitmq:amqp-client:5.5.1' - testCompile 'org.mongodb:mongo-java-driver:3.0.2' + testCompile 'org.mongodb:mongo-java-driver:3.9.1' testCompile ('org.mockito:mockito-core:1.10.19') { exclude(module: 'hamcrest-core') } From 0eeb05a260ed3fbc80e4419a23032db3749caf2e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 25 Dec 2018 10:15:09 +0100 Subject: [PATCH 0533/5615] Bump postgresql from 42.0.0 to 42.2.5 in /modules/spock (#1057) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.0.0 to 42.2.5. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.0.0...REL42.2.5) Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 553a87007ca..860de0c10f8 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -12,6 +12,6 @@ dependencies { testCompile project(':postgresql') testCompile 'com.zaxxer:HikariCP:3.2.0' - testRuntime 'org.postgresql:postgresql:42.0.0' + testRuntime 'org.postgresql:postgresql:42.2.5' testRuntime 'mysql:mysql-connector-java:8.0.13' } \ No newline at end of file From cc41a8be94268bffbe4c1a104301728c5f6d19a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 25 Dec 2018 10:15:26 +0100 Subject: [PATCH 0534/5615] Bump spring-boot-maven-plugin (#1093) Bumps [spring-boot-maven-plugin](https://github.com/spring-projects/spring-boot) from 2.0.1.RELEASE to 2.1.1.RELEASE. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.0.1.RELEASE...v2.1.1.RELEASE) Signed-off-by: dependabot[bot] --- examples/spring-boot/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot/pom.xml b/examples/spring-boot/pom.xml index 241e9ceae64..270e8b6cc55 100644 --- a/examples/spring-boot/pom.xml +++ b/examples/spring-boot/pom.xml @@ -50,7 +50,7 @@ org.springframework.boot spring-boot-maven-plugin - 2.0.1.RELEASE + 2.1.1.RELEASE
From f48a15234b512b658c8647fdd2e86570a7cceda1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 25 Dec 2018 10:16:39 +0100 Subject: [PATCH 0535/5615] Bump mariadb-java-client from 1.5.9 to 2.3.0 in /modules/mariadb (#1068) Bumps [mariadb-java-client](https://github.com/MariaDB/mariadb-connector-j) from 1.5.9 to 2.3.0. - [Release notes](https://github.com/MariaDB/mariadb-connector-j/releases) - [Commits](https://github.com/MariaDB/mariadb-connector-j/compare/1.5.9...2.3.0) Signed-off-by: dependabot[bot] --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index b9ab673e121..868fa87ccf5 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: JDBC :: MariaDB" dependencies { compile project(':jdbc') - testCompile 'org.mariadb.jdbc:mariadb-java-client:1.5.9' + testCompile 'org.mariadb.jdbc:mariadb-java-client:2.3.0' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.14' From 4d713986305c1e5e50f1f08e347a1969c999f491 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 25 Dec 2018 10:16:56 +0100 Subject: [PATCH 0536/5615] Bump junit-jupiter-engine from 5.3.1 to 5.3.2 in /modules/junit-jupiter (#1066) Bumps [junit-jupiter-engine](https://github.com/junit-team/junit5) from 5.3.1 to 5.3.2. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.3.1...r5.3.2) Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index a12e6974a7d..7528820f5df 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -11,7 +11,7 @@ dependencies { testRuntime 'org.postgresql:postgresql:42.2.5' testRuntime 'mysql:mysql-connector-java:8.0.13' - testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.3.1' + testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.3.2' } test { From bd0aff50781b3bcf2edd82e084440ffcfc5ec7bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 25 Dec 2018 10:17:15 +0100 Subject: [PATCH 0537/5615] Bump transport from 6.4.1 to 6.5.4 in /modules/elasticsearch (#1055) Bumps [transport](https://github.com/elastic/elasticsearch) from 6.4.1 to 6.5.4. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/commits) Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 4527ee18adc..ac51908c92e 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,5 +3,5 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') testCompile "org.elasticsearch.client:elasticsearch-rest-client:6.5.4" - testCompile "org.elasticsearch.client:transport:6.4.1" + testCompile "org.elasticsearch.client:transport:6.5.4" } From b840c5d77239747995938b9a123e8a5722d89d9e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 25 Dec 2018 22:23:17 +0100 Subject: [PATCH 0538/5615] Bump okhttp from 3.10.0 to 3.12.1 in /core (#1080) * Bump okhttp from 3.10.0 to 3.12.1 in /core Bumps [okhttp](https://github.com/square/okhttp) from 3.10.0 to 3.12.1. - [Release notes](https://github.com/square/okhttp/releases) - [Changelog](https://github.com/square/okhttp/blob/master/CHANGELOG.md) - [Commits](https://github.com/square/okhttp/compare/parent-3.10.0...parent-3.12.1) Signed-off-by: dependabot[bot] * Replace OkHttp's internal API usage with public * exclude `META-INF/proguard/` from the shaded JAR --- core/build.gradle | 3 ++- .../transport/okhttp/OkHttpDockerCmdExecFactory.java | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index aff84668171..c03c870045b 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -28,6 +28,7 @@ shadowJar { 'META-INF/LICENSE.txt', 'META-INF/DEPENDENCIES', 'META-INF/maven/', + 'META-INF/proguard/', 'META-INF/services/README.md', 'META-INF/services/com.fasterxml.jackson.core.*', 'META-INF/services/com.github.dockerjava.api.command.*', @@ -101,7 +102,7 @@ dependencies { exclude(group: 'com.google.code.findbug') exclude(group: 'com.kohlschutter.junixsocket') } - shaded 'com.squareup.okhttp3:okhttp:3.10.0' + shaded 'com.squareup.okhttp3:okhttp:3.12.1' shaded 'org.glassfish.main.external:trilead-ssh2-repackaged:4.1.2' diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpDockerCmdExecFactory.java b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpDockerCmdExecFactory.java index 08e0dde046b..841d012e2b3 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpDockerCmdExecFactory.java +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpDockerCmdExecFactory.java @@ -14,7 +14,6 @@ import okhttp3.Dns; import okhttp3.HttpUrl; import okhttp3.OkHttpClient; -import okhttp3.internal.Internal; import org.apache.commons.io.IOUtils; import javax.net.ssl.SSLContext; @@ -98,7 +97,7 @@ public void init(DockerClientConfig dockerClientConfig) { .port(dockerHost.getPort()); break; default: - baseUrlBuilder = Internal.instance.getHttpUrlChecked(dockerHost.toString()).newBuilder(); + baseUrlBuilder = HttpUrl.get(dockerHost.toString()).newBuilder(); } baseUrl = baseUrlBuilder.build(); } From de2821a969c48d13d26a1e1f27bc8d6bbe6fdc9d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Wed, 26 Dec 2018 06:43:37 +0000 Subject: [PATCH 0539/5615] Bump json from 20160810 to 20180813 in /examples Bumps [json](https://github.com/douglascrockford/JSON-java) from 20160810 to 20180813. - [Release notes](https://github.com/douglascrockford/JSON-java/releases) - [Commits](https://github.com/douglascrockford/JSON-java/compare/20160810...20180813) Signed-off-by: dependabot[bot] --- examples/linked-container/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/pom.xml b/examples/linked-container/pom.xml index 4579949af09..e39c76d56ab 100644 --- a/examples/linked-container/pom.xml +++ b/examples/linked-container/pom.xml @@ -43,7 +43,7 @@ org.json json - 20160810 + 20180813 From c4d46e784d8ca8d6eb9d6332381aa0233353199b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Wed, 26 Dec 2018 06:44:16 +0000 Subject: [PATCH 0540/5615] Bump guava from 18.0 to 23.0 in /examples Bumps [guava](https://github.com/google/guava) from 18.0 to 23.0. - [Release notes](https://github.com/google/guava/releases) - [Commits](https://github.com/google/guava/compare/v18.0...v23.0) Signed-off-by: dependabot[bot] --- examples/disque-job-queue/pom.xml | 2 +- examples/redis-backed-cache-testng/pom.xml | 2 +- examples/redis-backed-cache/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/disque-job-queue/pom.xml b/examples/disque-job-queue/pom.xml index c18c7477e07..ba94d220798 100644 --- a/examples/disque-job-queue/pom.xml +++ b/examples/disque-job-queue/pom.xml @@ -24,7 +24,7 @@ com.google.guava guava - 18.0 + 23.0 org.slf4j diff --git a/examples/redis-backed-cache-testng/pom.xml b/examples/redis-backed-cache-testng/pom.xml index 881c45a6b5a..fdf84456ba3 100644 --- a/examples/redis-backed-cache-testng/pom.xml +++ b/examples/redis-backed-cache-testng/pom.xml @@ -25,7 +25,7 @@ com.google.guava guava - 18.0 + 23.0 org.slf4j diff --git a/examples/redis-backed-cache/pom.xml b/examples/redis-backed-cache/pom.xml index 180e16cd7f3..e0a15f44042 100644 --- a/examples/redis-backed-cache/pom.xml +++ b/examples/redis-backed-cache/pom.xml @@ -24,7 +24,7 @@ com.google.guava guava - 18.0 + 23.0 org.slf4j From be94b0496be3d0df8f79845cb14a662f186eecd3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Wed, 26 Dec 2018 18:54:32 +0000 Subject: [PATCH 0541/5615] Bump mockito-core from 1.10.19 to 2.23.4 in /core (#1077) Bumps [mockito-core](https://github.com/mockito/mockito) from 1.10.19 to 2.23.4. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/commits/v2.23.4) Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index c03c870045b..0a4daf5d27c 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -120,7 +120,7 @@ dependencies { testCompile 'redis.clients:jedis:3.0.0' testCompile 'com.rabbitmq:amqp-client:5.5.1' testCompile 'org.mongodb:mongo-java-driver:3.9.1' - testCompile ('org.mockito:mockito-core:1.10.19') { + testCompile ('org.mockito:mockito-core:2.23.4') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From 0bb2cfa17ded940bca7f38a557380d98001cdb3b Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 26 Dec 2018 19:55:01 +0100 Subject: [PATCH 0542/5615] Auto merge `/examples` dependencies (#1097) --- .dependabot/config.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.dependabot/config.yml b/.dependabot/config.yml index 3c88d798542..26d00d96c1c 100644 --- a/.dependabot/config.yml +++ b/.dependabot/config.yml @@ -83,3 +83,6 @@ update_configs: - package_manager: "java:maven" directory: "/examples" update_schedule: "daily" + automerged_updates: + - match: { dependency_group: "development", kind: "all" } + - match: { dependency_group: "runtime", kind: "all" } From bf3b7bcc9a40996c39aa667585ec21391eb662d1 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Wed, 26 Dec 2018 21:44:25 +0100 Subject: [PATCH 0543/5615] Start shared containers in beforeAll callback (Jupiter) (#1020) Fixes #1019 --- .../jupiter/TestcontainersExtension.java | 23 +++++++++++-------- .../TestcontainersSharedContainerTests.java | 7 ++++++ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java index b64d607148d..7c5f8b205ef 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java @@ -1,12 +1,9 @@ package org.testcontainers.junit.jupiter; import lombok.Getter; -import org.junit.jupiter.api.extension.BeforeEachCallback; -import org.junit.jupiter.api.extension.ExtensionConfigurationException; -import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.*; import org.junit.jupiter.api.extension.ExtensionContext.Namespace; import org.junit.jupiter.api.extension.ExtensionContext.Store.CloseableResource; -import org.junit.jupiter.api.extension.TestInstancePostProcessor; import org.junit.platform.commons.support.AnnotationSupport; import org.junit.platform.commons.util.Preconditions; import org.junit.platform.commons.util.ReflectionUtils; @@ -19,7 +16,7 @@ import java.util.function.Predicate; import java.util.stream.Stream; -class TestcontainersExtension implements TestInstancePostProcessor, BeforeEachCallback { +class TestcontainersExtension implements BeforeEachCallback, BeforeAllCallback, TestInstancePostProcessor { private static final Namespace NAMESPACE = Namespace.create(TestcontainersExtension.class); @@ -29,8 +26,16 @@ class TestcontainersExtension implements TestInstancePostProcessor, BeforeEachCa public void postProcessTestInstance(final Object testInstance, final ExtensionContext context) { ExtensionContext.Store store = context.getStore(NAMESPACE); store.put(TEST_INSTANCE, testInstance); + } + + @Override + public void beforeAll(ExtensionContext context) { + Class testClass = context.getTestClass() + .orElseThrow(() -> new ExtensionConfigurationException("TestcontainersExtension is only supported for classes.")); - findSharedContainers(testInstance) + ExtensionContext.Store store = context.getStore(NAMESPACE); + + findSharedContainers(testClass) .forEach(adapter -> store.getOrComputeIfAbsent(adapter.getKey(), k -> adapter.start())); } @@ -57,13 +62,13 @@ private Set collectParentTestInstances(final ExtensionContext context) { return testInstances; } - private Stream findSharedContainers(Object testInstance) { + private Stream findSharedContainers(Class testClass) { return ReflectionUtils.findFields( - testInstance.getClass(), + testClass, isSharedContainer(), ReflectionUtils.HierarchyTraversalMode.TOP_DOWN) .stream() - .map(f -> getContainerInstance(testInstance, f)); + .map(f -> getContainerInstance(null, f)); } private Predicate isSharedContainer() { diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersSharedContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersSharedContainerTests.java index 8c6859b1022..321c0d6282f 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersSharedContainerTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersSharedContainerTests.java @@ -1,9 +1,11 @@ package org.testcontainers.junit.jupiter; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.testcontainers.containers.GenericContainer; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; @Testcontainers class TestcontainersSharedContainerTests { @@ -14,6 +16,11 @@ class TestcontainersSharedContainerTests { private static String lastContainerId; + @BeforeAll + static void doSomethingWithAContainer() { + assertTrue(GENERIC_CONTAINER.isRunning()); + } + @Test void first_test() { if (lastContainerId == null) { From 93312be42d1e634e6b26229008b7d1e658a26038 Mon Sep 17 00:00:00 2001 From: Victor Verbitsky Date: Thu, 27 Dec 2018 14:42:18 +0300 Subject: [PATCH 0544/5615] Fix StreamType.RAW frame processing (#1101) * Issue #1100. Fixes processing logs of the RAW type. * Issue #1100. Remove the redundant import. * Issue #1100. Fixed formatting. Using the OneShotStartupCheckStrategy for faster tests. * Issue #1100. Fixed generic use of GenericContainer. * Issue #1100. Remove the redundant import. * Issue #1100. Added timeouts for tests. --- .../okhttp/OkHttpInvocationBuilder.java | 19 ++-- .../junit/OutputStreamWithTTYTest.java | 89 +++++++++++++++++++ 2 files changed, 99 insertions(+), 9 deletions(-) create mode 100644 core/src/test/java/org/testcontainers/junit/OutputStreamWithTTYTest.java diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java index e701c16fefa..a03914d5f07 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java @@ -27,10 +27,7 @@ import okhttp3.RequestBody; import okhttp3.Response; import okhttp3.internal.connection.RealConnection; -import okio.BufferedSink; -import okio.BufferedSource; -import okio.Okio; -import okio.Source; +import okio.*; import org.jetbrains.annotations.Nullable; import org.testcontainers.DockerClientFactory; @@ -354,12 +351,16 @@ public void accept(BufferedSource source) { source.skip(3); int payloadSize = source.readInt(); - if(!source.request(payloadSize)) { - return; - } - byte[] payload = source.readByteArray(payloadSize); + if (streamType != StreamType.RAW) { + if (!source.request(payloadSize)) { + return; + } + byte[] payload = source.readByteArray(payloadSize); - resultCallback.onNext(new Frame(streamType, payload)); + resultCallback.onNext(new Frame(streamType, payload)); + } else { + resultCallback.onNext(new Frame(streamType, source.readByteArray())); + } } } catch (Exception e) { resultCallback.onError(e); diff --git a/core/src/test/java/org/testcontainers/junit/OutputStreamWithTTYTest.java b/core/src/test/java/org/testcontainers/junit/OutputStreamWithTTYTest.java new file mode 100644 index 00000000000..0e7b66534b7 --- /dev/null +++ b/core/src/test/java/org/testcontainers/junit/OutputStreamWithTTYTest.java @@ -0,0 +1,89 @@ +package org.testcontainers.junit; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.Timeout; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.OutputFrame; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.containers.output.ToStringConsumer; +import org.testcontainers.containers.output.WaitingConsumer; +import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; + +import static org.rnorth.visibleassertions.VisibleAssertions.*; +import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; + +@Slf4j +public class OutputStreamWithTTYTest { + + @Rule + public GenericContainer container = new GenericContainer<>("alpine:3.2") + .withCommand("ls -1") + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .withCreateContainerCmdModifier(command -> command.withTty(true)); + + @Rule + public Timeout globalTimeout = Timeout.seconds(10); + + @Test + public void testFetchStdout() throws TimeoutException { + WaitingConsumer consumer = new WaitingConsumer(); + + container.followOutput(consumer, STDOUT); + + consumer.waitUntil(frame -> frame.getType() == STDOUT && frame.getUtf8String().contains("home"), 4, TimeUnit.SECONDS); + } + + @Test + public void testFetchStdoutWithTimeout() { + WaitingConsumer consumer = new WaitingConsumer(); + + container.followOutput(consumer, STDOUT); + + assertThrows("a TimeoutException should be thrown", TimeoutException.class, () -> { + consumer.waitUntil(frame -> frame.getType() == STDOUT && frame.getUtf8String().contains("qqq"), 1, TimeUnit.SECONDS); + return true; + }); + } + + @Test + public void testFetchStdoutWithNoLimit() throws TimeoutException { + WaitingConsumer consumer = new WaitingConsumer(); + + container.followOutput(consumer, STDOUT); + + consumer.waitUntil(frame -> frame.getType() == STDOUT && frame.getUtf8String().contains("home")); + } + + @Test + public void testLogConsumer() throws TimeoutException { + WaitingConsumer waitingConsumer = new WaitingConsumer(); + Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(log); + + Consumer composedConsumer = logConsumer.andThen(waitingConsumer); + container.followOutput(composedConsumer); + + waitingConsumer.waitUntil(frame -> frame.getType() == STDOUT && frame.getUtf8String().contains("home")); + } + + @Test + public void testToStringConsumer() throws TimeoutException { + WaitingConsumer waitingConsumer = new WaitingConsumer(); + ToStringConsumer toStringConsumer = new ToStringConsumer(); + + Consumer composedConsumer = toStringConsumer.andThen(waitingConsumer); + container.followOutput(composedConsumer); + + waitingConsumer.waitUntilEnd(4, TimeUnit.SECONDS); + + String utf8String = toStringConsumer.toUtf8String(); + assertTrue("the expected first value was found", utf8String.contains("home")); + assertTrue("the expected last value was found", utf8String.contains("media")); + assertFalse("a non-expected value was found", utf8String.contains("qqq")); + } +} From dd8e2f8f8062121ef597f95f596ecdc1a859ebf5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Thu, 27 Dec 2018 17:25:37 +0100 Subject: [PATCH 0545/5615] Bump pulsar-client from 2.2.0 to 2.2.1 in /modules/pulsar (#1091) Bumps [pulsar-client](https://github.com/apache/pulsar) from 2.2.0 to 2.2.1. - [Release notes](https://github.com/apache/pulsar/releases) - [Commits](https://github.com/apache/pulsar/compare/v2.2.0...v2.2.1) Signed-off-by: dependabot[bot] --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 4f6b0f3288f..ae34dee384b 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Pulsar" dependencies { compile project(':testcontainers') - testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.2.0' + testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.2.1' testCompile group: 'org.assertj', name: 'assertj-core', version: '3.10.0' } From c08a954a573e9ff80e551ed627ceea3e871870cf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Thu, 27 Dec 2018 17:26:15 +0100 Subject: [PATCH 0546/5615] Bump assertj-core from 3.8.0 to 3.11.1 in /modules/kafka (#1067) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.8.0 to 3.11.1. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.8.0...assertj-core-3.11.1) Signed-off-by: dependabot[bot] --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index 462efe387ba..6814d0fca1a 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -4,6 +4,6 @@ dependencies { compile project(':testcontainers') testCompile 'org.apache.kafka:kafka-clients:2.1.0' - testCompile 'org.assertj:assertj-core:3.8.0' + testCompile 'org.assertj:assertj-core:3.11.1' testCompile 'com.google.guava:guava:23.0' } From a77e73f20e7918f3f1ff1d7f39a6b2a8aa391370 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Thu, 27 Dec 2018 17:26:35 +0100 Subject: [PATCH 0547/5615] Bump visible-assertions from 2.1.1 to 2.1.2 in /core (#1088) Bumps [visible-assertions](https://github.com/rnorth/visible-assertions) from 2.1.1 to 2.1.2. - [Release notes](https://github.com/rnorth/visible-assertions/releases) - [Changelog](https://github.com/rnorth/visible-assertions/blob/master/CHANGELOG.md) - [Commits](https://github.com/rnorth/visible-assertions/compare/2.1.1...2.1.2) Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 0a4daf5d27c..57966db8cad 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -83,7 +83,7 @@ dependencies { exclude(group: 'org.jetbrains', module: 'annotations') } - compile 'org.rnorth.visible-assertions:visible-assertions:2.1.1' + compile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' compile ('org.rnorth:tcp-unix-socket-proxy:1.0.2') { exclude(group: "log4j", module: "log4j") From 1f75cdc7f40f01102d13fcbe28e8d94ad5056631 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Thu, 27 Dec 2018 17:28:33 +0100 Subject: [PATCH 0548/5615] Bump javax.annotation-api from 1.3.1 to 1.3.2 in /core (#1087) Bumps [javax.annotation-api](https://github.com/javaee/javax.annotation) from 1.3.1 to 1.3.2. - [Release notes](https://github.com/javaee/javax.annotation/releases) - [Commits](https://github.com/javaee/javax.annotation/compare/1.3.1...1.3.2) Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 57966db8cad..43889555078 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -75,7 +75,7 @@ dependencies { compile 'junit:junit:4.12' compile 'org.slf4j:slf4j-api:1.7.25' compile 'org.jetbrains:annotations:16.0.3' - compile 'javax.annotation:javax.annotation-api:1.3.1' + compile 'javax.annotation:javax.annotation-api:1.3.2' compile 'org.apache.commons:commons-compress:1.18' // Added for JDK9 compatibility since it's missing this package compile 'javax.xml.bind:jaxb-api:2.3.1' From fbf8b76415988e1f8424ecb6ebda3eb4ae2d2799 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Thu, 27 Dec 2018 17:29:06 +0100 Subject: [PATCH 0549/5615] Bump junit-jupiter-api from 5.3.1 to 5.3.2 in /modules/junit-jupiter (#1069) Bumps [junit-jupiter-api](https://github.com/junit-team/junit5) from 5.3.1 to 5.3.2. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.3.1...r5.3.2) Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 7528820f5df..7bdfce61425 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -2,7 +2,7 @@ description = "Testcontainers :: JUnit Jupiter Extension" dependencies { compile project(':testcontainers') - compile 'org.junit.jupiter:junit-jupiter-api:5.3.1' + compile 'org.junit.jupiter:junit-jupiter-api:5.3.2' testCompile project(':mysql') testCompile project(':postgresql') From f9f4aa1be02cfa9936d3b2fc37edc6b9570ea4e5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Thu, 27 Dec 2018 17:29:31 +0100 Subject: [PATCH 0550/5615] Bump jna-platform from 4.5.1 to 5.2.0 in /core (#1079) Bumps [jna-platform](https://github.com/java-native-access/jna) from 4.5.1 to 5.2.0. - [Release notes](https://github.com/java-native-access/jna/releases) - [Changelog](https://github.com/java-native-access/jna/blob/master/CHANGES.md) - [Commits](https://github.com/java-native-access/jna/compare/4.5.1...5.2.0) Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 43889555078..a60c9c1b8ef 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -89,7 +89,7 @@ dependencies { exclude(group: "log4j", module: "log4j") } - compile "net.java.dev.jna:jna-platform:4.5.1" + compile "net.java.dev.jna:jna-platform:5.2.0" shaded ('org.scala-sbt.ipcsocket:ipcsocket:1.0.0') { exclude(group: "net.java.dev.jna") From 07eef6084ffbdd37226895ed7e3f3c4f7c3ff3e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Fri, 28 Dec 2018 08:06:05 +0100 Subject: [PATCH 0551/5615] Bump jedis from 3.0.0 to 3.0.1 in /examples (#1103) Bumps [jedis](https://github.com/xetorthio/jedis) from 3.0.0 to 3.0.1. - [Release notes](https://github.com/xetorthio/jedis/releases) - [Commits](https://github.com/xetorthio/jedis/compare/jedis-3.0.0...jedis-3.0.1) Signed-off-by: dependabot[bot] --- examples/redis-backed-cache-testng/pom.xml | 2 +- examples/redis-backed-cache/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/redis-backed-cache-testng/pom.xml b/examples/redis-backed-cache-testng/pom.xml index fdf84456ba3..1012f2ddc32 100644 --- a/examples/redis-backed-cache-testng/pom.xml +++ b/examples/redis-backed-cache-testng/pom.xml @@ -15,7 +15,7 @@ redis.clients jedis - 3.0.0 + 3.0.1 com.google.code.gson diff --git a/examples/redis-backed-cache/pom.xml b/examples/redis-backed-cache/pom.xml index e0a15f44042..1cff4291376 100644 --- a/examples/redis-backed-cache/pom.xml +++ b/examples/redis-backed-cache/pom.xml @@ -14,7 +14,7 @@ redis.clients jedis - 3.0.0 + 3.0.1 com.google.code.gson From 83ce69ca6cc8ef1daba899e324b92874858ebed7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Fri, 28 Dec 2018 08:06:22 +0100 Subject: [PATCH 0552/5615] Bump jedis from 3.0.0 to 3.0.1 in /core (#1105) Bumps [jedis](https://github.com/xetorthio/jedis) from 3.0.0 to 3.0.1. - [Release notes](https://github.com/xetorthio/jedis/releases) - [Commits](https://github.com/xetorthio/jedis/compare/jedis-3.0.0...jedis-3.0.1) Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index a60c9c1b8ef..af4955583d7 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -117,7 +117,7 @@ dependencies { exclude(module: '*') } - testCompile 'redis.clients:jedis:3.0.0' + testCompile 'redis.clients:jedis:3.0.1' testCompile 'com.rabbitmq:amqp-client:5.5.1' testCompile 'org.mongodb:mongo-java-driver:3.9.1' testCompile ('org.mockito:mockito-core:2.23.4') { From 74c8a2fc29ac2ae32f865b4785a6661fcd845e2e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Fri, 28 Dec 2018 08:06:37 +0100 Subject: [PATCH 0553/5615] Bump jedis from 3.0.0 to 3.0.1 in /modules/junit-jupiter (#1104) Bumps [jedis](https://github.com/xetorthio/jedis) from 3.0.0 to 3.0.1. - [Release notes](https://github.com/xetorthio/jedis/releases) - [Commits](https://github.com/xetorthio/jedis/compare/jedis-3.0.0...jedis-3.0.1) Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 7bdfce61425..ee49d817419 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -7,7 +7,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') testCompile 'com.zaxxer:HikariCP:3.2.0' - testCompile 'redis.clients:jedis:3.0.0' + testCompile 'redis.clients:jedis:3.0.1' testRuntime 'org.postgresql:postgresql:42.2.5' testRuntime 'mysql:mysql-connector-java:8.0.13' From 589ec76937ef150f1c65094ef117d32cfd84faae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Fri, 28 Dec 2018 08:06:54 +0100 Subject: [PATCH 0554/5615] Bump assertj-core from 3.8.0 to 3.11.1 in /core (#1090) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.8.0 to 3.11.1. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.8.0...assertj-core-3.11.1) Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index af4955583d7..bbea849c37b 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -129,6 +129,6 @@ dependencies { jarFileTestCompileOnly "org.projectlombok:lombok:${lombok.version}" jarFileTestCompile 'junit:junit:4.12' - jarFileTestCompile 'org.assertj:assertj-core:3.8.0' + jarFileTestCompile 'org.assertj:assertj-core:3.11.1' jarFileTestCompile 'org.ow2.asm:asm-debug-all:5.2' } From 8d4c1d2b39ecfe65cd6bb69ab4c676c755d07f45 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Fri, 28 Dec 2018 16:51:33 +0000 Subject: [PATCH 0555/5615] Bump assertj-core from 3.10.0 to 3.11.1 in /modules/pulsar (#1054) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.10.0 to 3.11.1. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.10.0...assertj-core-3.11.1) Signed-off-by: dependabot[bot] --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index ae34dee384b..8e1d707f818 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -4,5 +4,5 @@ dependencies { compile project(':testcontainers') testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.2.1' - testCompile group: 'org.assertj', name: 'assertj-core', version: '3.10.0' + testCompile group: 'org.assertj', name: 'assertj-core', version: '3.11.1' } From 30b50529b9ec71617d61f5a93e9bf7fc7cd70f56 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Fri, 28 Dec 2018 19:47:58 +0000 Subject: [PATCH 0556/5615] Bump cassandra-driver-core from 3.5.1 to 3.6.0 in /modules/cassandra (#1050) Bumps [cassandra-driver-core](https://github.com/datastax/java-driver) from 3.5.1 to 3.6.0.
Commits - [`46a4e7c`](https://github.com/datastax/java-driver/commit/46a4e7c73c75946073559681e1ad215fa919e00d) [maven-release-plugin] prepare release 3.6.0 - [`61377b1`](https://github.com/datastax/java-driver/commit/61377b1f667b5e201d7e37035ac598306e01dd1b) Update versions in docs to 3.6.0 - [`1df1125`](https://github.com/datastax/java-driver/commit/1df1125e0c9c98919bf245483144718552319af2) Update Guava range in OSGi FAQ - [`ce64618`](https://github.com/datastax/java-driver/commit/ce64618b7e64d5e83aaa7966c3bee45c7c08ef6a) Include ServerError in downgrade criteria from peers_v2 to peers ([#1085](https://github-redirect.dependabot.com/datastax/java-driver/issues/1085)) - [`8b37656`](https://github.com/datastax/java-driver/commit/8b37656c1f4de025e1c0a7a61bb2fb1a9a44f9a3) JAVA-1211: Improve cluster init and shutdown ([#1074](https://github-redirect.dependabot.com/datastax/java-driver/issues/1074)) - [`0c0eb6c`](https://github.com/datastax/java-driver/commit/0c0eb6cd7549c539c58fcc020241ba2e492a3cf0) JAVA-1944: Surface Read and WriteFailureException to RetryPolicy ([#1084](https://github-redirect.dependabot.com/datastax/java-driver/issues/1084)) - [`192b11b`](https://github.com/datastax/java-driver/commit/192b11b4a84750824b8bdadd4681784d70729fa6) Fix default CL in PreparedStatement javadoc - [`7ecb3af`](https://github.com/datastax/java-driver/commit/7ecb3af8e159217a42fcd042b88e5fc9d7a0c35f) JAVA-1810: Note which setters are not propagated to PreparedStatement - [`598c5cb`](https://github.com/datastax/java-driver/commit/598c5cb078829c4803028195bc552ea906c019ef) JAVA-1388: Add dynamic port discovery for system.peers_v2 ([#1065](https://github-redirect.dependabot.com/datastax/java-driver/issues/1065)) - [`ce29eea`](https://github.com/datastax/java-driver/commit/ce29eea80573eddff088e72599e47c7b67ec0776) Add documentation about QueryBuilder ([#1031](https://github-redirect.dependabot.com/datastax/java-driver/issues/1031)) - Additional commits viewable in [compare view](https://github.com/datastax/java-driver/compare/3.5.1...3.6.0)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=com.datastax.cassandra:cassandra-driver-core&package-manager=gradle&previous-version=3.5.1&new-version=3.6.0)](https://dependabot.com/compatibility-score.html?dependency-name=com.datastax.cassandra:cassandra-driver-core&package-manager=gradle&previous-version=3.5.1&new-version=3.6.0) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/cassandra/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cassandra/build.gradle b/modules/cassandra/build.gradle index ef0706c58c6..0ec1cb8f2af 100644 --- a/modules/cassandra/build.gradle +++ b/modules/cassandra/build.gradle @@ -2,5 +2,5 @@ description = "TestContainers :: Cassandra" dependencies { compile project(":database-commons") - compile "com.datastax.cassandra:cassandra-driver-core:3.5.1" + compile "com.datastax.cassandra:cassandra-driver-core:3.6.0" } From b21168ba323bacfd29513d4645c85bd291d0b06b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Thu, 3 Jan 2019 09:31:30 +0100 Subject: [PATCH 0557/5615] Bump HikariCP from 3.2.0 to 3.3.0 in /modules/spock (#1117) Bumps [HikariCP](https://github.com/brettwooldridge/HikariCP) from 3.2.0 to 3.3.0. - [Release notes](https://github.com/brettwooldridge/HikariCP/releases) - [Changelog](https://github.com/brettwooldridge/HikariCP/blob/dev/CHANGES) - [Commits](https://github.com/brettwooldridge/HikariCP/compare/HikariCP-3.2.0...HikariCP-3.3.0) Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 860de0c10f8..3d097e5c5ca 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -10,7 +10,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') - testCompile 'com.zaxxer:HikariCP:3.2.0' + testCompile 'com.zaxxer:HikariCP:3.3.0' testRuntime 'org.postgresql:postgresql:42.2.5' testRuntime 'mysql:mysql-connector-java:8.0.13' From 4dbba1971c4e045b46fe1a0c26ad90a2dd611237 Mon Sep 17 00:00:00 2001 From: Philip Harrison Date: Tue, 8 Jan 2019 08:54:06 +0000 Subject: [PATCH 0558/5615] Fix breaking changes to Dependabot config.yml (#1128) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 👋 we just made some breaking changes to the config file: https://github.com/dependabot/feedback/issues/70#issuecomment-451994832 Aiming to finalise it today so hopefully no more breaking changes! --- .dependabot/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.dependabot/config.yml b/.dependabot/config.yml index 26d00d96c1c..c5ed0c759fb 100644 --- a/.dependabot/config.yml +++ b/.dependabot/config.yml @@ -84,5 +84,5 @@ update_configs: directory: "/examples" update_schedule: "daily" automerged_updates: - - match: { dependency_group: "development", kind: "all" } - - match: { dependency_group: "runtime", kind: "all" } + - match: { dependency_type: "development", update_type: "all" } + - match: { dependency_type: "production", update_type: "all" } From bb221fbb6266639e32e3169420a061727cd4cba7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 8 Jan 2019 20:52:37 +0100 Subject: [PATCH 0559/5615] Bump aws-java-sdk-s3 from 1.11.475 to 1.11.479 in /modules/localstack (#1130) Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.11.475 to 1.11.479. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.475...1.11.479) Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 7ba8ac8f175..18a410ef1a8 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.475' - testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.126' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' + testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' } From 9856185bbcda90fa02149a6090150b60905cc307 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 8 Jan 2019 20:57:48 +0100 Subject: [PATCH 0560/5615] Bump aws-java-sdk-dynamodb (#1129) Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.11.475 to 1.11.479. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.475...1.11.479) Signed-off-by: dependabot[bot] --- modules/dynalite/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 03031943d85..32d522aa964 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Dynalite" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.475' - testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.126' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.479' + testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.479' } From 466c2d5489e16df3886d189995ade5ff41f85912 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Wed, 9 Jan 2019 12:22:12 +0000 Subject: [PATCH 0561/5615] Bump amqp-client from 5.5.1 to 5.5.2 in /core (#1122) Bumps [amqp-client](https://github.com/rabbitmq/rabbitmq-java-client) from 5.5.1 to 5.5.2.
Release notes *Sourced from [amqp-client's releases](https://github.com/rabbitmq/rabbitmq-java-client/releases).* > ## 5.5.2 > This is a patch release with a bug fix. All users of the 5.x.x series are encouraged to upgrade to this version. > > Thanks to Andrew Steinborn for his contribution on this release. > > # Changes between 5.5.1 and 5.5.2 > > ## Spurious warnings emitted by ClientVersion when the library is relocated > > GitHub issue: [#436](https://github-redirect.dependabot.com/rabbitmq/rabbitmq-java-client/issues/436)
Changelog *Sourced from [amqp-client's changelog](https://github.com/rabbitmq/rabbitmq-java-client/blob/v5.5.2/release-versions.txt).* > RELEASE_VERSION="5.5.2" > DEVELOPMENT_VERSION="5.5.3-SNAPSHOT"
Commits - [`0310c49`](https://github.com/rabbitmq/rabbitmq-java-client/commit/0310c495d7794663af295a2fc0895a091d27b4dd) [maven-release-plugin] prepare release v5.5.2 - [`5835658`](https://github.com/rabbitmq/rabbitmq-java-client/commit/583565817f36f2c989c00d30bef8306f8edd15f3) Set release version to 5.5.2 - [`d2e12e1`](https://github.com/rabbitmq/rabbitmq-java-client/commit/d2e12e1c0a7f6484f2c5369b86823592170cf880) Add link to issue in comment - [`f2470e0`](https://github.com/rabbitmq/rabbitmq-java-client/commit/f2470e0a121967f75cb0d01a118f4abf96003e14) Fix warnings in ClientVersion when amqp-client is relocated - [`1eb5590`](https://github.com/rabbitmq/rabbitmq-java-client/commit/1eb5590114f6200ef0331f45538d66a9366e2943) Increase expiry epsilon in dead letter test - [`d67b90d`](https://github.com/rabbitmq/rabbitmq-java-client/commit/d67b90da991ef03ef3b349778d9ce3086ce23e90) Set release version to 5.5.2.RC1 - [`7157d43`](https://github.com/rabbitmq/rabbitmq-java-client/commit/7157d434a31ade002e654210f46c33bce8c24116) [maven-release-plugin] prepare for next development iteration - See full diff in [compare view](https://github.com/rabbitmq/rabbitmq-java-client/compare/v5.5.1...v5.5.2)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=com.rabbitmq:amqp-client&package-manager=gradle&previous-version=5.5.1&new-version=5.5.2)](https://dependabot.com/compatibility-score.html?dependency-name=com.rabbitmq:amqp-client&package-manager=gradle&previous-version=5.5.1&new-version=5.5.2) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index bbea849c37b..8c36aba5893 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -118,7 +118,7 @@ dependencies { } testCompile 'redis.clients:jedis:3.0.1' - testCompile 'com.rabbitmq:amqp-client:5.5.1' + testCompile 'com.rabbitmq:amqp-client:5.5.2' testCompile 'org.mongodb:mongo-java-driver:3.9.1' testCompile ('org.mockito:mockito-core:2.23.4') { exclude(module: 'hamcrest-core') From cb6a489a0ccb3e62074a514c1ec85e0dd45028d4 Mon Sep 17 00:00:00 2001 From: Biju Kunjummen Date: Wed, 9 Jan 2019 04:29:06 -0800 Subject: [PATCH 0562/5615] Extends dynalite container from GenericContainer - for JUnit5 support (#1114) Fixes an issue where dynalite containers don't work with JUnit5 `@TestContainers` annotation. The issue is that the containers are expected to be `Startable` which it is not today. The change makes `DynaliteContainer` derive from `GenericContainer` instead of `ExternalResource` --- .../dynamodb/DynaliteContainer.java | 29 +++++-------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/modules/dynalite/src/main/java/org/testcontainers/dynamodb/DynaliteContainer.java b/modules/dynalite/src/main/java/org/testcontainers/dynamodb/DynaliteContainer.java index 31a3a7914e8..66397452921 100644 --- a/modules/dynalite/src/main/java/org/testcontainers/dynamodb/DynaliteContainer.java +++ b/modules/dynalite/src/main/java/org/testcontainers/dynamodb/DynaliteContainer.java @@ -6,28 +6,23 @@ import com.amazonaws.client.builder.AwsClientBuilder; import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder; -import org.junit.rules.ExternalResource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.output.Slf4jLogConsumer; /** * Container for Dynalite, a DynamoDB clone. */ -public class DynaliteContainer extends ExternalResource { +public class DynaliteContainer extends GenericContainer { - private static final Logger LOGGER = LoggerFactory.getLogger(DynaliteContainer.class); - private final GenericContainer delegate; + private static final String IMAGE_NAME = "quay.io/testcontainers/dynalite:v1.2.1-1"; + private static final int MAPPED_PORT = 4567; public DynaliteContainer() { - this("quay.io/testcontainers/dynalite:v1.2.1-1"); + this(IMAGE_NAME); + withExposedPorts(MAPPED_PORT); } public DynaliteContainer(String imageName) { - this.delegate = new GenericContainer(imageName) - .withExposedPorts(4567) - .withLogConsumer(new Slf4jLogConsumer(LOGGER)); + super(imageName); } /** @@ -51,8 +46,8 @@ public AmazonDynamoDB getClient() { */ public AwsClientBuilder.EndpointConfiguration getEndpointConfiguration() { return new AwsClientBuilder.EndpointConfiguration("http://" + - this.delegate.getContainerIpAddress() + ":" + - this.delegate.getMappedPort(4567), null); + this.getContainerIpAddress() + ":" + + this.getMappedPort(MAPPED_PORT), null); } /** @@ -64,13 +59,5 @@ public AWSCredentialsProvider getCredentials() { return new AWSStaticCredentialsProvider(new BasicAWSCredentials("dummy", "dummy")); } - @Override - protected void before() throws Throwable { - delegate.start(); - } - @Override - protected void after() { - delegate.stop(); - } } From 59d35bcfc54084c4404d9ed99469d3dad2ba80ee Mon Sep 17 00:00:00 2001 From: Kirill Merkushev Date: Wed, 9 Jan 2019 14:36:43 +0100 Subject: [PATCH 0563/5615] Update mockserver to 5.5.1 and bind to default port, 1080 (#1109) In 5.5.1 they don't run container as root, so binding to 80 port doesn't work anymore https://github.com/jamesdbloom/mockserver/commit/6d33bd78d515fa1a26ff721c9d6bc6e40c77b4e9#diff-ebacf6f6ae4ee68078bb16454b23247d --- modules/mockserver/build.gradle | 2 +- .../containers/MockServerContainer.java | 4 +- .../containers/MockServerContainerTest.java | 46 ++++++++++++++----- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/modules/mockserver/build.gradle b/modules/mockserver/build.gradle index 1fc8665e054..8855121360c 100644 --- a/modules/mockserver/build.gradle +++ b/modules/mockserver/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: MockServer" dependencies { compile project(':testcontainers') - testCompile 'org.mock-server:mockserver-client-java:5.5.0' + testCompile 'org.mock-server:mockserver-client-java:5.5.1' } diff --git a/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java b/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java index ca14e3e2953..f191adea06d 100644 --- a/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java +++ b/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java @@ -5,9 +5,9 @@ @Slf4j public class MockServerContainer extends GenericContainer { - public static final String VERSION = "5.5.0"; + public static final String VERSION = "5.5.1"; - public static final int PORT = 80; + public static final int PORT = 1080; public MockServerContainer() { this(VERSION); diff --git a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java index ae1b870fc7a..8cb8ac88448 100644 --- a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java +++ b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java @@ -1,35 +1,57 @@ package org.testcontainers.containers; import lombok.Cleanup; -import org.junit.ClassRule; import org.junit.Test; import org.mockserver.client.MockServerClient; import java.io.BufferedReader; +import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; import java.net.URLConnection; +import static org.hamcrest.CoreMatchers.containsString; import static org.mockserver.model.HttpRequest.request; import static org.mockserver.model.HttpResponse.response; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; public class MockServerContainerTest { - @ClassRule - public static MockServerContainer mockServer = new MockServerContainer(); + @Test + public void shouldCallActualMockserverVersion() throws Exception { + String actualVersion = MockServerClient.class.getPackage().getImplementationVersion(); + try (MockServerContainer mockServer = new MockServerContainer(actualVersion)) { + mockServer.start(); + + String expectedBody = "Hello World!"; + + assertThat("MockServer returns correct result", + responseFromMockserver(mockServer, expectedBody, "/hello"), + containsString(expectedBody) + ); + } + } @Test - public void testBasicScenario() throws Exception { + public void shouldCallDefaultMockserverVersion() throws Exception { + try (MockServerContainer mockServerDefault = new MockServerContainer()) { + mockServerDefault.start(); + String expectedBody = "Hello Default World!"; + + assertThat("MockServer returns correct result for default constructor", + responseFromMockserver(mockServerDefault, expectedBody, "/hellodefault"), + containsString(expectedBody) + ); + } + } + + private static String responseFromMockserver(MockServerContainer mockServer, String expectedBody, String path) throws IOException { new MockServerClient(mockServer.getContainerIpAddress(), mockServer.getServerPort()) - .when(request("/hello")) - .respond(response("Hello World!")); + .when(request(path)) + .respond(response(expectedBody)); - URLConnection urlConnection = new URL(mockServer.getEndpoint() + "/hello").openConnection(); + URLConnection urlConnection = new URL(mockServer.getEndpoint() + path).openConnection(); @Cleanup BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); - String line = reader.readLine(); - System.out.println(line); - - assertTrue("MockServer returns correct result", line.contains("Hello World!")); + return reader.readLine(); } } From bcecd5cd9f9325517fd45db585312df2624315bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Wed, 9 Jan 2019 13:43:19 +0000 Subject: [PATCH 0564/5615] Bump HikariCP from 3.2.0 to 3.3.0 in /modules/junit-jupiter (#1120) Bumps [HikariCP](https://github.com/brettwooldridge/HikariCP) from 3.2.0 to 3.3.0.
Commits - [`ec5e85e`](https://github.com/brettwooldridge/HikariCP/commit/ec5e85e4316c6aa30bc2e115630f80c3b70e685e) [maven-release-plugin] prepare release HikariCP-3.3.0 - [`1744719`](https://github.com/brettwooldridge/HikariCP/commit/17447199f507e5b7d2fe984487e2e9d431b9215f) Fixes [#1305](https://github-redirect.dependabot.com/brettwooldridge/HikariCP/issues/1305) ensure that ConcurrentBag.add() does not spin under high load, wh... - [`1ac94f1`](https://github.com/brettwooldridge/HikariCP/commit/1ac94f16a2eef27be3e35a02606c435e33afd825) Fixes [#1287](https://github-redirect.dependabot.com/brettwooldridge/HikariCP/issues/1287) when system property blockUntilFilled is set, use multiple thread... - [`4333989`](https://github.com/brettwooldridge/HikariCP/commit/4333989ca27b97cae2ae36b10eef13c7b4f6f9a7) Fixes [#1305](https://github-redirect.dependabot.com/brettwooldridge/HikariCP/issues/1305) ensure that ConcurrentBag.add() does not spin under high load, wh... - [`110f97e`](https://github.com/brettwooldridge/HikariCP/commit/110f97e4c1bb13f4270b27236777458763a0ef25) Bump log4j.version from 2.9.0 to 2.11.1 ([#1302](https://github-redirect.dependabot.com/brettwooldridge/HikariCP/issues/1302)) - [`d035231`](https://github.com/brettwooldridge/HikariCP/commit/d035231e49dbd48f435e4b13fc2ff4a67f1e846b) Bump mockito-core from 2.10.0 to 2.23.4 ([#1301](https://github-redirect.dependabot.com/brettwooldridge/HikariCP/issues/1301)) - [`f0676c8`](https://github.com/brettwooldridge/HikariCP/commit/f0676c8727f6a5e817686addb82f8ecf83b12b2b) Replace anonymous class with lambda ([#1292](https://github-redirect.dependabot.com/brettwooldridge/HikariCP/issues/1292)) - [`50c3fd1`](https://github.com/brettwooldridge/HikariCP/commit/50c3fd1b788361a276faf0ef16a7d831a25a84f9) Call newInstance on Constructor of driverClass ([#1290](https://github-redirect.dependabot.com/brettwooldridge/HikariCP/issues/1290)) - [`503a1d6`](https://github.com/brettwooldridge/HikariCP/commit/503a1d6c590c3e49fec2baacb05b8a075dd2c5a9) Update README.md - [`45912eb`](https://github.com/brettwooldridge/HikariCP/commit/45912ebb570ff9d54c010da61d1f2c26cc5cd39b) Fix unit test breakage. - Additional commits viewable in [compare view](https://github.com/brettwooldridge/HikariCP/compare/HikariCP-3.2.0...HikariCP-3.3.0)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=com.zaxxer:HikariCP&package-manager=gradle&previous-version=3.2.0&new-version=3.3.0)](https://dependabot.com/compatibility-score.html?dependency-name=com.zaxxer:HikariCP&package-manager=gradle&previous-version=3.2.0&new-version=3.3.0) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index ee49d817419..d2f761cdf59 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -6,7 +6,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') - testCompile 'com.zaxxer:HikariCP:3.2.0' + testCompile 'com.zaxxer:HikariCP:3.3.0' testCompile 'redis.clients:jedis:3.0.1' testRuntime 'org.postgresql:postgresql:42.2.5' From 0233bd327f1d4a6313a84e49eea8338c62dd89b7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 15 Jan 2019 06:16:10 +0000 Subject: [PATCH 0565/5615] Bump spring-boot-dependencies Bumps [spring-boot-dependencies](https://github.com/spring-projects/spring-boot) from 2.1.1.RELEASE to 2.1.2.RELEASE. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.1.1.RELEASE...v2.1.2.RELEASE) Signed-off-by: dependabot[bot] --- examples/spring-boot/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot/pom.xml b/examples/spring-boot/pom.xml index 270e8b6cc55..b389d2adc97 100644 --- a/examples/spring-boot/pom.xml +++ b/examples/spring-boot/pom.xml @@ -21,7 +21,7 @@ org.springframework.boot spring-boot-dependencies - 2.1.1.RELEASE + 2.1.2.RELEASE pom import From 691268bb6d77c1e4d58183931324aedff8b6da28 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 15 Jan 2019 09:53:43 +0100 Subject: [PATCH 0566/5615] Bump spring-boot-maven-plugin (#1148) Bumps [spring-boot-maven-plugin](https://github.com/spring-projects/spring-boot) from 2.1.1.RELEASE to 2.1.2.RELEASE. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.1.1.RELEASE...v2.1.2.RELEASE) Signed-off-by: dependabot[bot] --- examples/spring-boot/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot/pom.xml b/examples/spring-boot/pom.xml index b389d2adc97..fb882b500a3 100644 --- a/examples/spring-boot/pom.xml +++ b/examples/spring-boot/pom.xml @@ -50,7 +50,7 @@ org.springframework.boot spring-boot-maven-plugin - 2.1.1.RELEASE + 2.1.2.RELEASE From 9712ac10fe14e809e5eae2d2e018b6f0a91b4e5a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 15 Jan 2019 09:55:05 +0100 Subject: [PATCH 0567/5615] Bump rest-assured from 3.2.0 to 3.3.0 in /modules/vault (#1143) Bumps [rest-assured](https://github.com/jayway/rest-assured) from 3.2.0 to 3.3.0. - [Release notes](https://github.com/jayway/rest-assured/releases) - [Changelog](https://github.com/rest-assured/rest-assured/blob/master/changelog.txt) - [Commits](https://github.com/jayway/rest-assured/compare/rest-assured-3.2.0...rest-assured-3.3.0) Signed-off-by: dependabot[bot] --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index efb4e61acb0..d5c791acd32 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: Vault" dependencies { compile project(':testcontainers') - testCompile 'io.rest-assured:rest-assured:3.2.0' + testCompile 'io.rest-assured:rest-assured:3.3.0' } From 9039acb89f92074acf92e2fc390b80ca972592f0 Mon Sep 17 00:00:00 2001 From: stsefanenko Date: Wed, 16 Jan 2019 17:55:05 +0300 Subject: [PATCH 0568/5615] "exposing host port" example. Closes #1136 (#1152) * added spring boot webapp test with exposed host port for BrowserWebDriverContainer * fixes after review * fixes after review update to random port * fixes after review update to static resource * fixes after review update initializer * fixes after review remove unused dependency --- examples/selenium-container/pom.xml | 37 ++++++++++++++----- .../java/com/example/DemoApplication.java | 13 +++++++ .../src/main/resources/static/foo.html | 10 +++++ .../src/test/java/SeleniumContainerTest.java | 35 ++++++++++++++++-- 4 files changed, 81 insertions(+), 14 deletions(-) create mode 100644 examples/selenium-container/src/main/java/com/example/DemoApplication.java create mode 100644 examples/selenium-container/src/main/resources/static/foo.html diff --git a/examples/selenium-container/pom.xml b/examples/selenium-container/pom.xml index 44fab3b30ff..1ef62843bb7 100644 --- a/examples/selenium-container/pom.xml +++ b/examples/selenium-container/pom.xml @@ -11,40 +11,57 @@ selenium-container + + + + org.springframework.boot + spring-boot-dependencies + 2.1.2.RELEASE + pom + import + + + + org.seleniumhq.selenium selenium-remote-driver - 3.141.59 org.seleniumhq.selenium selenium-firefox-driver - 3.141.59 org.seleniumhq.selenium selenium-chrome-driver - 3.141.59 ${testcontainers.group} selenium ${testcontainers.version} + - org.slf4j - slf4j-api - 1.7.25 - provided + org.springframework.boot + spring-boot-starter-web + - ch.qos.logback - logback-classic - 1.2.3 + org.springframework.boot + spring-boot-starter-test test + + + + org.springframework.boot + spring-boot-maven-plugin + 2.1.2.RELEASE + + + diff --git a/examples/selenium-container/src/main/java/com/example/DemoApplication.java b/examples/selenium-container/src/main/java/com/example/DemoApplication.java new file mode 100644 index 00000000000..909a1ec36e1 --- /dev/null +++ b/examples/selenium-container/src/main/java/com/example/DemoApplication.java @@ -0,0 +1,13 @@ +package com.example; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class DemoApplication { + + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } + +} diff --git a/examples/selenium-container/src/main/resources/static/foo.html b/examples/selenium-container/src/main/resources/static/foo.html new file mode 100644 index 00000000000..bab56ecb35f --- /dev/null +++ b/examples/selenium-container/src/main/resources/static/foo.html @@ -0,0 +1,10 @@ + + + + Getting Started: Serving Web Content + + + +Hello World + + \ No newline at end of file diff --git a/examples/selenium-container/src/test/java/SeleniumContainerTest.java b/examples/selenium-container/src/test/java/SeleniumContainerTest.java index 44385a4d2bd..a084243b6e9 100644 --- a/examples/selenium-container/src/test/java/SeleniumContainerTest.java +++ b/examples/selenium-container/src/test/java/SeleniumContainerTest.java @@ -1,8 +1,19 @@ +import com.example.DemoApplication; import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; import org.openqa.selenium.chrome.ChromeOptions; +import org.openqa.selenium.remote.RemoteWebDriver; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.web.context.WebServerInitializedEvent; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ApplicationListener; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.testcontainers.Testcontainers; import org.testcontainers.containers.BrowserWebDriverContainer; import java.io.File; @@ -14,8 +25,14 @@ /** * Simple example of plain Selenium usage. */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(classes = DemoApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ContextConfiguration(initializers = SeleniumContainerTest.Initializer.class) public class SeleniumContainerTest { + @LocalServerPort + private int port; + @Rule public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() .withCapabilities(new ChromeOptions()) @@ -25,9 +42,19 @@ public class SeleniumContainerTest { public void simplePlainSeleniumTest() { RemoteWebDriver driver = chrome.getWebDriver(); - driver.get("https://wikipedia.org"); - List searchInput = driver.findElementsByName("search"); + driver.get("http://host.testcontainers.internal:" + port + "/foo.html"); + List hElement = driver.findElementsByTagName("h"); - assertTrue("The search input box is found", searchInput != null && searchInput.size() > 0); + assertTrue("The h element is found", hElement != null && hElement.size() > 0); } + + public static class Initializer implements ApplicationContextInitializer { + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + applicationContext.addApplicationListener((ApplicationListener) event -> { + Testcontainers.exposeHostPorts(event.getWebServer().getPort()); + }); + } + } + } From 8cab5ce097ccb2fbfb6684ac2289496bddf105d2 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 20 Jan 2019 12:55:28 +0000 Subject: [PATCH 0569/5615] Redo of documentation with mkdocs (#1150) --- .envrc | 1 + .gitignore | 8 + LICENSE | 4 +- Pipfile | 15 + Pipfile.lock | 158 ++++++ book.js | 21 - build.gradle | 2 +- docs/ROADMAP.md | 14 - docs/SUMMARY.md | 67 --- docs/_redirects | 21 + docs/ci/ci.md | 45 -- docs/contributing.md | 12 + docs/contributing_docs.md | 105 ++++ docs/css/_normalize.less | 406 -------------- docs/css/extra.css | 10 + docs/css/style.css | 513 ------------------ docs/css/style.less | 186 ------- docs/examples/build.gradle | 10 + .../java/quickstart/RedisBackedCache.java | 22 + .../test/java/generic/CmdModifierTest.java | 39 ++ .../src/test/java/generic/CommandsTest.java | 21 + .../java/generic/ContainerCreationTest.java | 36 ++ .../test/java/generic/ContainerLabelTest.java | 22 + .../src/test/java/generic/ExecTest.java | 30 + .../java/generic/HostPortExposedTest.java | 65 +++ .../generic/MultiplePortsExposedTest.java | 43 ++ .../test/java/generic/WaitStrategiesTest.java | 70 +++ .../quickstart/RedisBackedCacheIntTest.java | 38 ++ .../RedisBackedCacheIntTestStep0.java | 27 + .../src/test/resources/logback-test.xml | 27 + docs/favicon.ico | Bin 0 -> 15086 bytes docs/features/advanced_options.md | 38 ++ docs/features/commands.md | 33 ++ .../configuration.md} | 13 +- docs/features/container_logs.md | 56 ++ docs/features/creating_container.md | 33 ++ .../creating_images.md} | 4 +- docs/features/files.md | 11 + docs/features/networking.md | 77 +++ docs/features/startup_and_waits.md | 76 +++ docs/getting_help.md | 10 + docs/index.md | 100 ++-- docs/jitpack_dependencies.md | 24 + docs/logo.svg | 9 + docs/modules/databases/cassandra.md | 18 + docs/modules/databases/clickhouse.md | 22 + .../modules/databases/couchbase.md | 29 +- .../modules/databases/dynalite.md | 23 +- .../databases/index.md} | 33 +- docs/modules/databases/influxdb.md | 39 ++ docs/modules/databases/mariadb.md | 41 ++ docs/modules/databases/mssqlserver.md | 54 ++ docs/modules/databases/mysql.md | 34 ++ .../databases/neo4j.md} | 57 +- docs/modules/databases/oraclexe.md | 49 ++ docs/modules/databases/postgres.md | 25 + docs/{usage => modules}/docker_compose.md | 88 ++- .../elasticsearch.md} | 17 + .../kafka_containers.md => modules/kafka.md} | 16 + .../README.md => docs/modules/localstack.md | 20 +- docs/modules/mockserver.md | 18 + docs/modules/nginx.md | 18 + docs/modules/pulsar.md | 18 + .../vault/README.md => docs/modules/vault.md | 26 +- .../webdriver_containers.md | 34 ++ docs/quickstart/junit_4_quickstart.md | 82 +++ docs/styles/website.css | 16 - .../continuous_integration/circle_ci.md | 19 + .../continuous_integration/dind_patterns.md} | 10 +- .../continuous_integration/drone.md | 3 + .../continuous_integration/gitlab_ci.md | 23 + .../continuous_integration/travis.md | 3 + .../index.md} | 23 +- .../logging_config.md | 22 + .../windows.md} | 8 +- docs/template.html | 28 - docs/test_framework_integration/junit_4.md | 46 ++ .../test_framework_integration/junit_5.md | 101 ++-- .../manual_lifecycle_control.md | 50 ++ .../test_framework_integration/spock.md | 29 +- docs/usage.md | 217 -------- docs/usage/generic_containers.md | 49 -- docs/usage/modules.md | 10 - docs/usage/options.md | 217 -------- gradle/wrapper/gradle-wrapper.jar | Bin 54329 -> 56177 bytes gradle/wrapper/gradle-wrapper.properties | 1 + mkdocs.yml | 79 +++ modules/influxdb/README.md | 41 -- modules/mariadb/README.md | 40 -- modules/mssqlserver/README.md | 52 -- modules/oracle-xe/README.md | 40 -- requirements.txt | 4 + runtime.txt | 1 + settings.gradle | 5 +- 94 files changed, 2231 insertions(+), 2189 deletions(-) create mode 100644 .envrc create mode 100644 Pipfile create mode 100644 Pipfile.lock delete mode 100644 book.js delete mode 100644 docs/ROADMAP.md delete mode 100644 docs/SUMMARY.md create mode 100644 docs/_redirects delete mode 100644 docs/ci/ci.md create mode 100644 docs/contributing.md create mode 100644 docs/contributing_docs.md delete mode 100755 docs/css/_normalize.less create mode 100644 docs/css/extra.css delete mode 100644 docs/css/style.css delete mode 100644 docs/css/style.less create mode 100644 docs/examples/build.gradle create mode 100644 docs/examples/src/main/java/quickstart/RedisBackedCache.java create mode 100644 docs/examples/src/test/java/generic/CmdModifierTest.java create mode 100644 docs/examples/src/test/java/generic/CommandsTest.java create mode 100644 docs/examples/src/test/java/generic/ContainerCreationTest.java create mode 100644 docs/examples/src/test/java/generic/ContainerLabelTest.java create mode 100644 docs/examples/src/test/java/generic/ExecTest.java create mode 100644 docs/examples/src/test/java/generic/HostPortExposedTest.java create mode 100644 docs/examples/src/test/java/generic/MultiplePortsExposedTest.java create mode 100644 docs/examples/src/test/java/generic/WaitStrategiesTest.java create mode 100644 docs/examples/src/test/java/quickstart/RedisBackedCacheIntTest.java create mode 100644 docs/examples/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java create mode 100644 docs/examples/src/test/resources/logback-test.xml create mode 100644 docs/favicon.ico create mode 100644 docs/features/advanced_options.md create mode 100644 docs/features/commands.md rename docs/{usage/properties.md => features/configuration.md} (93%) create mode 100644 docs/features/container_logs.md create mode 100644 docs/features/creating_container.md rename docs/{usage/dockerfile.md => features/creating_images.md} (98%) create mode 100644 docs/features/files.md create mode 100644 docs/features/networking.md create mode 100644 docs/features/startup_and_waits.md create mode 100644 docs/getting_help.md create mode 100644 docs/jitpack_dependencies.md create mode 100644 docs/logo.svg create mode 100644 docs/modules/databases/cassandra.md create mode 100644 docs/modules/databases/clickhouse.md rename modules/couchbase/README.md => docs/modules/databases/couchbase.md (85%) rename modules/dynalite/README.md => docs/modules/databases/dynalite.md (68%) rename docs/{usage/database_containers.md => modules/databases/index.md} (75%) create mode 100644 docs/modules/databases/influxdb.md create mode 100644 docs/modules/databases/mariadb.md create mode 100644 docs/modules/databases/mssqlserver.md create mode 100644 docs/modules/databases/mysql.md rename docs/{usage/neo4j_container.md => modules/databases/neo4j.md} (80%) create mode 100644 docs/modules/databases/oraclexe.md create mode 100644 docs/modules/databases/postgres.md rename docs/{usage => modules}/docker_compose.md (66%) rename docs/{usage/elasticsearch_container.md => modules/elasticsearch.md} (84%) rename docs/{usage/kafka_containers.md => modules/kafka.md} (82%) rename modules/localstack/README.md => docs/modules/localstack.md (72%) create mode 100644 docs/modules/mockserver.md create mode 100644 docs/modules/nginx.md create mode 100644 docs/modules/pulsar.md rename modules/vault/README.md => docs/modules/vault.md (75%) rename docs/{usage => modules}/webdriver_containers.md (78%) create mode 100644 docs/quickstart/junit_4_quickstart.md delete mode 100644 docs/styles/website.css create mode 100644 docs/supported_docker_environment/continuous_integration/circle_ci.md rename docs/{usage/inside_docker.md => supported_docker_environment/continuous_integration/dind_patterns.md} (87%) create mode 100644 docs/supported_docker_environment/continuous_integration/drone.md create mode 100644 docs/supported_docker_environment/continuous_integration/gitlab_ci.md create mode 100644 docs/supported_docker_environment/continuous_integration/travis.md rename docs/{compatibility.md => supported_docker_environment/index.md} (78%) create mode 100644 docs/supported_docker_environment/logging_config.md rename docs/{usage/windows_support.md => supported_docker_environment/windows.md} (75%) delete mode 100644 docs/template.html create mode 100644 docs/test_framework_integration/junit_4.md rename modules/junit-jupiter/README.md => docs/test_framework_integration/junit_5.md (56%) create mode 100644 docs/test_framework_integration/manual_lifecycle_control.md rename modules/spock/README.md => docs/test_framework_integration/spock.md (73%) delete mode 100644 docs/usage.md delete mode 100644 docs/usage/generic_containers.md delete mode 100644 docs/usage/modules.md delete mode 100644 docs/usage/options.md create mode 100644 mkdocs.yml delete mode 100644 modules/influxdb/README.md delete mode 100644 modules/mariadb/README.md delete mode 100644 modules/mssqlserver/README.md delete mode 100644 modules/oracle-xe/README.md create mode 100644 requirements.txt create mode 100644 runtime.txt diff --git a/.envrc b/.envrc new file mode 100644 index 00000000000..3e6a147e52b --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use python 3.6.0 diff --git a/.gitignore b/.gitignore index c95a6ece0cd..1b6070306a2 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,11 @@ out/ **/.settings **/bin/ **/out/ + +# Generated docs +site/ +.direnv/ +src/mkdocs-codeinclude-plugin +src/pip-delete-this-directory.txt + +.DS_Store diff --git a/LICENSE b/LICENSE index e6d35668f53..9c9e8bc5563 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015 Richard North +Copyright (c) 2015-2019 Richard North Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/Pipfile b/Pipfile new file mode 100644 index 00000000000..145ddf858a2 --- /dev/null +++ b/Pipfile @@ -0,0 +1,15 @@ +[[source]] +name = "pypi" +url = "https://pypi.org/simple" +verify_ssl = true + +[dev-packages] + +[packages] +mkdocs = "*" +mkdocs-codeinclude-plugin = {editable = true,git = "https://github.com/rnorth/mkdocs-codeinclude-plugin"} +mkdocs-material = "*" +mkdocs-markdownextradata-plugin = "*" + +[requires] +python_version = "3.7" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 00000000000..852b507f886 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,158 @@ +{ + "_meta": { + "hash": { + "sha256": "c9a90b5f198797e8a6165fa7cb6c2143158bed425d0a8c5f63dc37ea4dcab632" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.7" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "click": { + "hashes": [ + "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", + "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7" + ], + "version": "==7.0" + }, + "jinja2": { + "hashes": [ + "sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd", + "sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4" + ], + "version": "==2.10" + }, + "livereload": { + "hashes": [ + "sha256:29cadfabcedd12eed792e0131991235b9d4764d4474bed75cf525f57109ec0a2", + "sha256:e632a6cd1d349155c1d7f13a65be873b38f43ef02961804a1bba8d817fa649a7" + ], + "version": "==2.6.0" + }, + "markdown": { + "hashes": [ + "sha256:c00429bd503a47ec88d5e30a751e147dcb4c6889663cd3e2ba0afe858e009baa", + "sha256:d02e0f9b04c500cde6637c11ad7c72671f359b87b9fe924b2383649d8841db7c" + ], + "version": "==3.0.1" + }, + "markupsafe": { + "hashes": [ + "sha256:048ef924c1623740e70204aa7143ec592504045ae4429b59c30054cb31e3c432", + "sha256:130f844e7f5bdd8e9f3f42e7102ef1d49b2e6fdf0d7526df3f87281a532d8c8b", + "sha256:19f637c2ac5ae9da8bfd98cef74d64b7e1bb8a63038a3505cd182c3fac5eb4d9", + "sha256:1b8a7a87ad1b92bd887568ce54b23565f3fd7018c4180136e1cf412b405a47af", + "sha256:1c25694ca680b6919de53a4bb3bdd0602beafc63ff001fea2f2fc16ec3a11834", + "sha256:1f19ef5d3908110e1e891deefb5586aae1b49a7440db952454b4e281b41620cd", + "sha256:1fa6058938190ebe8290e5cae6c351e14e7bb44505c4a7624555ce57fbbeba0d", + "sha256:31cbb1359e8c25f9f48e156e59e2eaad51cd5242c05ed18a8de6dbe85184e4b7", + "sha256:3e835d8841ae7863f64e40e19477f7eb398674da6a47f09871673742531e6f4b", + "sha256:4e97332c9ce444b0c2c38dd22ddc61c743eb208d916e4265a2a3b575bdccb1d3", + "sha256:525396ee324ee2da82919f2ee9c9e73b012f23e7640131dd1b53a90206a0f09c", + "sha256:52b07fbc32032c21ad4ab060fec137b76eb804c4b9a1c7c7dc562549306afad2", + "sha256:52ccb45e77a1085ec5461cde794e1aa037df79f473cbc69b974e73940655c8d7", + "sha256:5c3fbebd7de20ce93103cb3183b47671f2885307df4a17a0ad56a1dd51273d36", + "sha256:5e5851969aea17660e55f6a3be00037a25b96a9b44d2083651812c99d53b14d1", + "sha256:5edfa27b2d3eefa2210fb2f5d539fbed81722b49f083b2c6566455eb7422fd7e", + "sha256:7d263e5770efddf465a9e31b78362d84d015cc894ca2c131901a4445eaa61ee1", + "sha256:83381342bfc22b3c8c06f2dd93a505413888694302de25add756254beee8449c", + "sha256:857eebb2c1dc60e4219ec8e98dfa19553dae33608237e107db9c6078b1167856", + "sha256:98e439297f78fca3a6169fd330fbe88d78b3bb72f967ad9961bcac0d7fdd1550", + "sha256:bf54103892a83c64db58125b3f2a43df6d2cb2d28889f14c78519394feb41492", + "sha256:d9ac82be533394d341b41d78aca7ed0e0f4ba5a2231602e2f05aa87f25c51672", + "sha256:e982fe07ede9fada6ff6705af70514a52beb1b2c3d25d4e873e82114cf3c5401", + "sha256:edce2ea7f3dfc981c4ddc97add8a61381d9642dc3273737e756517cc03e84dd6", + "sha256:efdc45ef1afc238db84cb4963aa689c0408912a0239b0721cb172b4016eb31d6", + "sha256:f137c02498f8b935892d5c0172560d7ab54bc45039de8805075e19079c639a9c", + "sha256:f82e347a72f955b7017a39708a3667f106e6ad4d10b25f237396a7115d8ed5fd", + "sha256:fb7c206e01ad85ce57feeaaa0bf784b97fa3cad0d4a5737bc5295785f5c613a1" + ], + "version": "==1.1.0" + }, + "mkdocs": { + "hashes": [ + "sha256:17d34329aad75d5de604b9ed4e31df3a4d235afefdc46ce7b1964fddb2e1e939", + "sha256:8cc8b38325456b9e942c981a209eaeb1e9f3f77b493ad755bfef889b9c8d356a" + ], + "version": "==1.0.4" + }, + "mkdocs-codeinclude-plugin": { + "editable": true, + "git": "https://github.com/rnorth/mkdocs-codeinclude-plugin", + "ref": "99417fdad0634ccfeea7eaa8ba648a08c7513e7c" + }, + "mkdocs-markdownextradata-plugin": { + "hashes": [ + "sha256:64d1c966b288d653f51f7531c03204eb988d0d77e56055c9d703d99105259a36" + ], + "index": "pypi", + "version": "==0.0.5" + }, + "mkdocs-material": { + "hashes": [ + "sha256:524debb6ee8ee89cee08886f2a67c3c3875c0ee9579c598d7448cbd2607cd3b7", + "sha256:62ae84082fa9f077c86b7db63e7bedf392005041b451defc850f8d0887a11e91" + ], + "index": "pypi", + "version": "==3.2.0" + }, + "pygments": { + "hashes": [ + "sha256:5ffada19f6203563680669ee7f53b64dabbeb100eb51b61996085e99c03b284a", + "sha256:e8218dd399a61674745138520d0d4cf2621d7e032439341bc3f647bff125818d" + ], + "version": "==2.3.1" + }, + "pymdown-extensions": { + "hashes": [ + "sha256:25b0a7967fa697b5035e23340a48594e3e93acb10b06d74574218ace3347d1df", + "sha256:6cf0cf36b5a03b291ace22dc2f320f4789ce56fbdb6635a3be5fadbf5d7694dd" + ], + "version": "==6.0" + }, + "pyyaml": { + "hashes": [ + "sha256:3d7da3009c0f3e783b2c873687652d83b1bbfd5c88e9813fb7e5b03c0dd3108b", + "sha256:3ef3092145e9b70e3ddd2c7ad59bdd0252a94dfe3949721633e41344de00a6bf", + "sha256:40c71b8e076d0550b2e6380bada1f1cd1017b882f7e16f09a65be98e017f211a", + "sha256:558dd60b890ba8fd982e05941927a3911dc409a63dcb8b634feaa0cda69330d3", + "sha256:a7c28b45d9f99102fa092bb213aa12e0aaf9a6a1f5e395d36166639c1f96c3a1", + "sha256:aa7dd4a6a427aed7df6fb7f08a580d68d9b118d90310374716ae90b710280af1", + "sha256:bc558586e6045763782014934bfaf39d48b8ae85a2713117d16c39864085c613", + "sha256:d46d7982b62e0729ad0175a9bc7e10a566fc07b224d2c79fafb5e032727eaa04", + "sha256:d5eef459e30b09f5a098b9cea68bebfeb268697f78d647bd255a085371ac7f3f", + "sha256:e01d3203230e1786cd91ccfdc8f8454c8069c91bee3962ad93b87a4b2860f537", + "sha256:e170a9e6fcfd19021dd29845af83bb79236068bf5fd4df3327c1be18182b2531" + ], + "version": "==3.13" + }, + "six": { + "hashes": [ + "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", + "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" + ], + "version": "==1.12.0" + }, + "tornado": { + "hashes": [ + "sha256:0662d28b1ca9f67108c7e3b77afabfb9c7e87bde174fbda78186ecedc2499a9d", + "sha256:4e5158d97583502a7e2739951553cbd88a72076f152b4b11b64b9a10c4c49409", + "sha256:732e836008c708de2e89a31cb2fa6c0e5a70cb60492bee6f1ea1047500feaf7f", + "sha256:8154ec22c450df4e06b35f131adc4f2f3a12ec85981a203301d310abf580500f", + "sha256:8e9d728c4579682e837c92fdd98036bd5cdefa1da2aaf6acf26947e6dd0c01c5", + "sha256:d4b3e5329f572f055b587efc57d29bd051589fb5a43ec8898c77a47ec2fa2bbb", + "sha256:e5f2585afccbff22390cddac29849df463b252b711aa2ce7c5f3f342a5b3b444" + ], + "version": "==5.1.1" + } + }, + "develop": {} +} diff --git a/book.js b/book.js deleted file mode 100644 index 5dbad218194..00000000000 --- a/book.js +++ /dev/null @@ -1,21 +0,0 @@ -module.exports = { - root: "./docs", - - title: "Testcontainers", - - structure: { - readme: "index.md" - }, - - plugins: [ - "-highlight", - "prism", - "github", - "expandable-chapters", - ], - "pluginsConfig": { - "github": { - "url": "https://github.com/testcontainers/testcontainers-java/" - } - }, -}; diff --git a/build.gradle b/build.gradle index b59ba776494..ade1ea3bcf0 100644 --- a/build.gradle +++ b/build.gradle @@ -22,7 +22,7 @@ subprojects { compileJava.options.encoding = 'UTF-8' lombok { - version = '1.18.2' + version = '1.18.2' } task delombok(type: io.franzbecker.gradle.lombok.task.DelombokTask) { diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md deleted file mode 100644 index a144db23c65..00000000000 --- a/docs/ROADMAP.md +++ /dev/null @@ -1,14 +0,0 @@ -# Roadmap - -## Principles for the project - -Exact future features aren't set in stone, but here are the principles we should follow for this project: - -* Testcontainers is intended to utilize containers to, primarily, make integration testing of systems easier, quicker and more reliable. -* It's designed primarily to help developers. Make it easy to do the right thing, with sensible defaults, a fairly limited surface area to discover, and helpful log/error messages. -* It's not intended to be the solution to every problem under the sun. Don't try and solve every problem with this one library; if it takes a lot of work to incorporate a feature then perhaps it's the job of another tool. -* In line with the above, we should aim for feature completeness soon. -* Similarly, it should leverage good things that already exist (like reliable public Docker images) and be composable with other tools. -* That said, don't lock us in to more third-party components than absolutely necessary, and only use well maintained dependencies. -* It must be possible for a developer to use _or build_ this library with just minimal standard tools (Java JDK, Maven), and a local Docker environment. Do not use any non-public dependencies in the core lib or standard modules. - diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md deleted file mode 100644 index 852fc37288b..00000000000 --- a/docs/SUMMARY.md +++ /dev/null @@ -1,67 +0,0 @@ -# Summary - -* [Introduction](index.md) -* [Compatibility](compatibility.md) -* [Usage](usage.md) - * [Prerequisites](usage.md#prerequisites) - * [Docker environment discovery](usage.md#docker-environment-discovery) - * [Usage modes](usage.md#usage-modes) - * [Gradle/Maven dependencies](usage.md#gradlemaven-dependencies) - * [Logging](usage.md#logging) - * [JUnit](usage.md#junit) - * [Properties](usage/properties.md) - * [Running inside Docker](usage/inside_docker.md) - -## Generic containers - -* [Benefits](usage/generic_containers.md#benefits) -* [Example](usage/generic_containers.md#example) -* [Accessing a container from tests](usage/generic_containers.md#accessing-a-container-from-tests) -* [Options](usage/options.md) - * [Specifying image name](usage/options.md#image) - * [Exposing ports](usage/options.md#exposing-ports) - * [Environment variables](usage/options.md#environment-variables) - * [Command](usage/options.md#command) - * [Volume mapping](usage/options.md#volume-mapping) - * [Customizing the container](usage/options.md#customizing-the-container) - * [Startup timeout](usage/options.md#startup-timeout) - * [Following container output](usage/options.md#following-container-output) - * [Executing a command](usage/options.md#executing-a-command) - - -## Specialised container types - -* [Temporary database containers](usage/database_containers.md) - * [Benefits](usage/database_containers.md#benefits) - * [Examples and options](usage/database_containers.md#examples-and-options) - * [JUnit rule](usage/database_containers.md#junit-rule) - * [JDBC URL](usage/database_containers.md#jdbc-url) - * [Using an init script](usage/database_containers.md#using-an-init-script) - -* [Elasticsearch containers](usage/elasticsearch_container.md) - -* [Selenium WebDriver containers](usage/webdriver_containers.md) - * [Benefits](usage/webdriver_containers.md#benefits) - * [Example](usage/webdriver_containers.md#example) - * [Other browsers](usage/webdriver_containers.md#other-browsers) - * [Recording videos](usage/webdriver_containers.md#recording-videos) - -* [Kafka containers](usage/kafka_containers.md) -* [Neo4j containers](usage/neo4j_container.md) -* [Docker Compose](usage/docker_compose.md) -* [Dockerfile containers](usage/dockerfile.md) -* [Windows support](usage/windows_support.md) - -## Continuous Integration - -* [GitLab CI](ci/ci.md#gitlab) -* [Circle CI](ci/ci.md#circleci-20) - -## Examples -* [Selenium](https://github.com/testcontainers/testcontainers-java-examples/blob/master/selenium-container/src/test/java/SeleniumContainerTest.java) -* [Custom Redis container](https://github.com/testcontainers/testcontainers-java-examples/blob/master/redis-backed-cache/src/test/java/RedisBackedCacheTest.java) -* [Spring Boot testing with Redis](https://github.com/testcontainers/testcontainers-java-examples/tree/master/spring-boot/src/test/java/com/example) -## -* [License](index.md#license) -* [Attributions](index.md#attributions) -* [Contributing](index.md#contributing) diff --git a/docs/_redirects b/docs/_redirects new file mode 100644 index 00000000000..99fe10d6900 --- /dev/null +++ b/docs/_redirects @@ -0,0 +1,21 @@ +# Each redirect rule must be listed on a separate line, with the original path followed by the new path or URL. + +/usage/docker_compose.html /modules/docker_compose/ +/usage/generic_containers.html /features/creating_container/ +/usage/windows_support.html /supported_docker_environment/windows/ +/usage/dockerfile.html /features/creating_images/ +/usage/inside_docker.html /supported_docker_environment/continuous_integration/dind_patterns/ +/usage/webdriver_containers.html /modules/webdriver_containers/ +/usage/properties.html /features/configuration/ +/usage/kafka_containers.html /modules/kafka/ +/usage/elasticsearch_container.html /modules/elasticsearch/ +/usage/database_containers.html /modules/databases/ +/usage/neo4j_container.html /modules/databases/neo4j/ +/compatibility.html /supported_docker_environment/ + +# No great 1:1 mapping exists for the following, so redirect to somewhere where at least a sensible sidebar will be shown + +/usage/options.html /features/creating_container/ +/ci/ci.html /supported_docker_environment/ +/usage.html / + diff --git a/docs/ci/ci.md b/docs/ci/ci.md deleted file mode 100644 index 4f81c794fd3..00000000000 --- a/docs/ci/ci.md +++ /dev/null @@ -1,45 +0,0 @@ -# Continuous Integration - -## GitLab - -In order to use Testcontainers in a Gitlab CI pipeline, you need to run the job as a Docker container (see [Running inside Docker](../usage/inside_docker.md)). -So edit your `.gitlab-ci.yml` to include the [Docker-In-Docker service](https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#use-docker-in-docker-executor) (`docker:dind`) and set the `DOCKER_HOST` variable to `tcp://docker:2375`. - -Here is a sample `.gitlab-ci.yml` that executes test with gradle: - -```yml -# DinD service is required for Testcontainers -services: - - docker:dind - -variables: - # Instruct Testcontainers to use the daemon of DinD. - DOCKER_HOST: "tcp://docker:2375" - # Improve performance with overlayfs. - DOCKER_DRIVER: overlay2 - -test: - image: gradle:5.0 - stage: test - script: ./gradlew test -``` - -## CircleCI 2.0 - -Your CircleCI configuration should use a dedicated VM for testcontainers to work. You can achieve this by specifying the -executor type in your `.circleci/config.yml` to be `machine` instead of the default `docker` executor ( see [Choosing an Executor Type](https://circleci.com/docs/2.0/executor-types/) for more info ). - -Here is a sample CircleCI configuration that does a checkout of a project and runs maven: - -```yml -# Check https://circleci.com/docs/2.0/language-java/ for more details -# -version: 2 -executorType: machine -jobs: - build: - steps: - - checkout - - - run: mvn -B clean install -``` diff --git a/docs/contributing.md b/docs/contributing.md new file mode 100644 index 00000000000..96ba5975aeb --- /dev/null +++ b/docs/contributing.md @@ -0,0 +1,12 @@ +# Contributing + +* Star the project on [Github](https://github.com/testcontainers/testcontainers-java) and help spread the word :) +* Join our [Slack group](http://slack.testcontainers.org) +* [Post an issue](https://github.com/testcontainers/testcontainers-java/issues) if you find any bugs +* Contribute improvements or fixes using a [Pull Request](https://github.com/testcontainers/testcontainers-java/pulls). If you're going to contribute, thank you! Please just be sure to: + * discuss with the authors on an issue ticket prior to doing anything big + * follow the style, naming and structure conventions of the rest of the project + * make commits atomic and easy to merge + * when updating documentation, please see [our guidance for documentation contributions](contributing_docs.md). + * verify all tests are passing. Build the project with `./gradlew check` to do this. + **N.B.** Gradle's Build Cache is enabled by default, but you can add `--no-build-cache` flag to disable it. diff --git a/docs/contributing_docs.md b/docs/contributing_docs.md new file mode 100644 index 00000000000..294ae54e0de --- /dev/null +++ b/docs/contributing_docs.md @@ -0,0 +1,105 @@ +# Contributing to documentation + +The testcontainers-java documentation is a static site built with [MkDocs](https://www.mkdocs.org/). +We use the [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) theme, which offers a number of useful extensions to MkDocs. + +In addition we use a [custom plugin](https://github.com/rnorth/mkdocs-codeinclude-plugin) for inclusion of code snippets. + +We publish our documentation using Netlify. + +## Previewing rendered content + +### Previewing docs locally + +* Ensure that you have Python 3.6.0 or higher. +* Set up a virtualenv and run `pip install -r requirements.txt` in the `testcontainers-java` root directory. +* Once Python dependencies have been installed, run `mkdocs serve` to start a local auto-updating MkDocs server. + +### PR Preview deployments + +Note that documentation for pull requests will automatically be published by Netlify as 'deploy previews'. +These deployment previews can be accessed via the `deploy/netlify` check that appears for each pull request. + +## Codeincludes + +The Gradle project under `docs/examples` is intended to hold compilable, runnable example code that can be included as +snippets into the documentation at build-time. + +As a result, we can have more confidence that code samples shown in the documentation is valid. + +We use a custom plugin for MkDocs to include snippets into our docs. + +A codeinclude block will resemble a regular markdown link surrounded by a pair of XML comments, e.g.: + + + +
<!--codeinclude-->
+[Human readable title for snippet](./relative_path_to_example_code.java) targeting_expression
+<!--/codeinclude-->
+
+ +Where `targeting_expression` could be: + +* `block:someString` or +* `inside_block:someString` + +If these are provided, the macro will seek out any line containing the token `someString` and grab the next curly brace +delimited block that it finds. `block` will grab the starting line and closing brace, whereas `inside_block` will omit +these. + +e.g., given: +```java + +public class FooService { + + public void doFoo() { + foo.doSomething(); + } + + ... + +``` + +If we use `block:doFoo` as our targeting expression, we will have the following content included into our page: + +```java +public void doFoo() { + foo.doSomething(); +} +``` + +Whereas using `inside_block:doFoo` we would just have the inner content of the method included: + +```java +foo.doSomething(); +``` + +Note that: + +* Any code included will be have its indentation reduced +* Every line in the source file will be searched for an instance of the token (e.g. `doFoo`). If more than one line + includes that token, then potentially more than one block could be targeted for inclusion. It is advisable to use a + specific, unique token to avoid unexpected behaviour. + +When we wish to include a section of code that does not naturally appear within braces, we can simply insert our token, +with matching braces, in a comment. +While a little ugly, this has the benefit of working in any context and is easy to understand. +For example: + +```java +public class FooService { + + public void boringMethod() { + doSomethingBoring(); + + // doFoo { + doTheThingThatWeActuallyWantToShow(); + // } + } + + +``` diff --git a/docs/css/_normalize.less b/docs/css/_normalize.less deleted file mode 100755 index c2de8df9426..00000000000 --- a/docs/css/_normalize.less +++ /dev/null @@ -1,406 +0,0 @@ -/*! normalize.css v2.1.3 | MIT License | git.io/normalize */ - -/* ========================================================================== - HTML5 display definitions - ========================================================================== */ - -/** - * Correct `block` display not defined in IE 8/9. - */ - -article, -aside, -details, -figcaption, -figure, -footer, -header, -hgroup, -main, -nav, -section, -summary { - display: block; -} - -/** - * Correct `inline-block` display not defined in IE 8/9. - */ - -audio, -canvas, -video { - display: inline-block; -} - -/** - * Prevent modern browsers from displaying `audio` without controls. - * Remove excess height in iOS 5 devices. - */ - -audio:not([controls]) { - display: none; - height: 0; -} - -/** - * Address `[hidden]` styling not present in IE 8/9. - * Hide the `template` element in IE, Safari, and Firefox < 22. - */ - -[hidden], -template { - display: none; -} - -/* ========================================================================== - Base - ========================================================================== */ - -/** - * 1. Set default font family to sans-serif. - * 2. Prevent iOS text size adjust after orientation change, without disabling - * user zoom. - */ - -html { - font-family: sans-serif; /* 1 */ - -ms-text-size-adjust: 100%; /* 2 */ - -webkit-text-size-adjust: 100%; /* 2 */ -} - -/** - * Remove default margin. - */ - -body { - margin: 0; -} - -/* ========================================================================== - Links - ========================================================================== */ - -/** - * Remove the gray background color from active links in IE 10. - */ - -a { - background: transparent; -} - -/** - * Address `outline` inconsistency between Chrome and other browsers. - */ - -a:focus { - outline: thin dotted; -} - -/** - * Improve readability when focused and also mouse hovered in all browsers. - */ - -a:active, -a:hover { - outline: 0; -} - -/* ========================================================================== - Typography - ========================================================================== */ - -/** - * Address variable `h1` font-size and margin within `section` and `article` - * contexts in Firefox 4+, Safari 5, and Chrome. - */ - -h1 { - font-size: 2em; - margin: 0.67em 0; -} - -/** - * Address styling not present in IE 8/9, Safari 5, and Chrome. - */ - -abbr[title] { - border-bottom: 1px dotted; -} - -/** - * Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome. - */ - -b, -strong { - font-weight: bold; -} - -/** - * Address styling not present in Safari 5 and Chrome. - */ - -dfn { - font-style: italic; -} - -/** - * Address differences between Firefox and other browsers. - */ - -hr { - -moz-box-sizing: content-box; - box-sizing: content-box; - height: 0; -} - -/** - * Address styling not present in IE 8/9. - */ - -mark { - background: #ff0; - color: #000; -} - -/** - * Correct font family set oddly in Safari 5 and Chrome. - */ - -code, -kbd, -pre, -samp { - font-family: monospace, serif; - font-size: 1em; -} - -/** - * Improve readability of pre-formatted text in all browsers. - */ - -pre { - white-space: pre-wrap; -} - -/** - * Set consistent quote types. - */ - -q { - quotes: "\201C" "\201D" "\2018" "\2019"; -} - -/** - * Address inconsistent and variable font size in all browsers. - */ - -small { - font-size: 80%; -} - -/** - * Prevent `sub` and `sup` affecting `line-height` in all browsers. - */ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sup { - top: -0.5em; -} - -sub { - bottom: -0.25em; -} - -/* ========================================================================== - Embedded content - ========================================================================== */ - -/** - * Remove border when inside `a` element in IE 8/9. - */ - -img { - border: 0; -} - -/** - * Correct overflow displayed oddly in IE 9. - */ - -svg:not(:root) { - overflow: hidden; -} - -/* ========================================================================== - Figures - ========================================================================== */ - -/** - * Address margin not present in IE 8/9 and Safari 5. - */ - -figure { - margin: 0; -} - -/* ========================================================================== - Forms - ========================================================================== */ - -/** - * Define consistent border, margin, and padding. - */ - -fieldset { - border: 1px solid #c0c0c0; - margin: 0 2px; - padding: 0.35em 0.625em 0.75em; -} - -/** - * 1. Correct `color` not being inherited in IE 8/9. - * 2. Remove padding so people aren't caught out if they zero out fieldsets. - */ - -legend { - border: 0; /* 1 */ - padding: 0; /* 2 */ -} - -/** - * 1. Correct font family not being inherited in all browsers. - * 2. Correct font size not being inherited in all browsers. - * 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. - */ - -button, -input, -select, -textarea { - font-family: inherit; /* 1 */ - font-size: 100%; /* 2 */ - margin: 0; /* 3 */ -} - -/** - * Address Firefox 4+ setting `line-height` on `input` using `!important` in - * the UA stylesheet. - */ - -button, -input { - line-height: normal; -} - -/** - * Address inconsistent `text-transform` inheritance for `button` and `select`. - * All other form control elements do not inherit `text-transform` values. - * Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+. - * Correct `select` style inheritance in Firefox 4+ and Opera. - */ - -button, -select { - text-transform: none; -} - -/** - * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` - * and `video` controls. - * 2. Correct inability to style clickable `input` types in iOS. - * 3. Improve usability and consistency of cursor style between image-type - * `input` and others. - */ - -button, -html input[type="button"], /* 1 */ -input[type="reset"], -input[type="submit"] { - -webkit-appearance: button; /* 2 */ - cursor: pointer; /* 3 */ -} - -/** - * Re-set default cursor for disabled elements. - */ - -button[disabled], -html input[disabled] { - cursor: default; -} - -/** - * 1. Address box sizing set to `content-box` in IE 8/9/10. - * 2. Remove excess padding in IE 8/9/10. - */ - -input[type="checkbox"], -input[type="radio"] { - box-sizing: border-box; /* 1 */ - padding: 0; /* 2 */ -} - -/** - * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. - * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome - * (include `-moz` to future-proof). - */ - -input[type="search"] { - -webkit-appearance: textfield; /* 1 */ - -moz-box-sizing: content-box; - -webkit-box-sizing: content-box; /* 2 */ - box-sizing: content-box; -} - -/** - * Remove inner padding and search cancel button in Safari 5 and Chrome - * on OS X. - */ - -input[type="search"]::-webkit-search-cancel-button, -input[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} - -/** - * Remove inner padding and border in Firefox 4+. - */ - -button::-moz-focus-inner, -input::-moz-focus-inner { - border: 0; - padding: 0; -} - -/** - * 1. Remove default vertical scrollbar in IE 8/9. - * 2. Improve readability and alignment in all browsers. - */ - -textarea { - overflow: auto; /* 1 */ - vertical-align: top; /* 2 */ -} - -/* ========================================================================== - Tables - ========================================================================== */ - -/** - * Remove most spacing between table cells. - */ - -table { - border-collapse: collapse; - border-spacing: 0; -} diff --git a/docs/css/extra.css b/docs/css/extra.css new file mode 100644 index 00000000000..d7e7e6b68a3 --- /dev/null +++ b/docs/css/extra.css @@ -0,0 +1,10 @@ +@import url('https://fonts.googleapis.com/css?family=Inconsolata|Lato|Merriweather+Sans|Open+Sans|Source+Sans+Pro'); +body { + font-family: 'Lato', sans-serif; + +} + +.md-typeset pre { + font-family: 'Inconsolata', monospace; + font-size: 100%; +} \ No newline at end of file diff --git a/docs/css/style.css b/docs/css/style.css deleted file mode 100644 index 2b078367dec..00000000000 --- a/docs/css/style.css +++ /dev/null @@ -1,513 +0,0 @@ -/*! normalize.css v2.1.3 | MIT License | git.io/normalize */ -/* ========================================================================== - HTML5 display definitions - ========================================================================== */ -/** - * Correct `block` display not defined in IE 8/9. - */ -@import url(//fonts.googleapis.com/css?family=Source+Serif+Pro); -@import url(//fonts.googleapis.com/css?family=Source+Code+Pro); -article, -aside, -details, -figcaption, -figure, -footer, -header, -hgroup, -main, -nav, -section, -summary { - display: block; -} -/** - * Correct `inline-block` display not defined in IE 8/9. - */ -audio, -canvas, -video { - display: inline-block; -} -/** - * Prevent modern browsers from displaying `audio` without controls. - * Remove excess height in iOS 5 devices. - */ -audio:not([controls]) { - display: none; - height: 0; -} -/** - * Address `[hidden]` styling not present in IE 8/9. - * Hide the `template` element in IE, Safari, and Firefox < 22. - */ -[hidden], -template { - display: none; -} -/* ========================================================================== - Base - ========================================================================== */ -/** - * 1. Set default font family to sans-serif. - * 2. Prevent iOS text size adjust after orientation change, without disabling - * user zoom. - */ -html { - font-family: sans-serif; - /* 1 */ - -ms-text-size-adjust: 100%; - /* 2 */ - -webkit-text-size-adjust: 100%; - /* 2 */ -} -/** - * Remove default margin. - */ -body { - margin: 0; -} -/* ========================================================================== - Links - ========================================================================== */ -/** - * Remove the gray background color from active links in IE 10. - */ -a { - background: transparent; -} -/** - * Address `outline` inconsistency between Chrome and other browsers. - */ -a:focus { - outline: thin dotted; -} -/** - * Improve readability when focused and also mouse hovered in all browsers. - */ -a:active, -a:hover { - outline: 0; -} -/* ========================================================================== - Typography - ========================================================================== */ -/** - * Address variable `h1` font-size and margin within `section` and `article` - * contexts in Firefox 4+, Safari 5, and Chrome. - */ -h1 { - font-size: 2em; - margin: 0.67em 0; -} -/** - * Address styling not present in IE 8/9, Safari 5, and Chrome. - */ -abbr[title] { - border-bottom: 1px dotted; -} -/** - * Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome. - */ -b, -strong { - font-weight: bold; -} -/** - * Address styling not present in Safari 5 and Chrome. - */ -dfn { - font-style: italic; -} -/** - * Address differences between Firefox and other browsers. - */ -hr { - -moz-box-sizing: content-box; - box-sizing: content-box; - height: 0; -} -/** - * Address styling not present in IE 8/9. - */ -mark { - background: #ff0; - color: #000; -} -/** - * Correct font family set oddly in Safari 5 and Chrome. - */ -code, -kbd, -pre, -samp { - font-family: monospace, serif; - font-size: 1em; -} -/** - * Improve readability of pre-formatted text in all browsers. - */ -pre { - white-space: pre-wrap; -} -/** - * Set consistent quote types. - */ -q { - quotes: "\201C" "\201D" "\2018" "\2019"; -} -/** - * Address inconsistent and variable font size in all browsers. - */ -small { - font-size: 80%; -} -/** - * Prevent `sub` and `sup` affecting `line-height` in all browsers. - */ -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} -sup { - top: -0.5em; -} -sub { - bottom: -0.25em; -} -/* ========================================================================== - Embedded content - ========================================================================== */ -/** - * Remove border when inside `a` element in IE 8/9. - */ -img { - border: 0; -} -/** - * Correct overflow displayed oddly in IE 9. - */ -svg:not(:root) { - overflow: hidden; -} -/* ========================================================================== - Figures - ========================================================================== */ -/** - * Address margin not present in IE 8/9 and Safari 5. - */ -figure { - margin: 0; -} -/* ========================================================================== - Forms - ========================================================================== */ -/** - * Define consistent border, margin, and padding. - */ -fieldset { - border: 1px solid #c0c0c0; - margin: 0 2px; - padding: 0.35em 0.625em 0.75em; -} -/** - * 1. Correct `color` not being inherited in IE 8/9. - * 2. Remove padding so people aren't caught out if they zero out fieldsets. - */ -legend { - border: 0; - /* 1 */ - padding: 0; - /* 2 */ -} -/** - * 1. Correct font family not being inherited in all browsers. - * 2. Correct font size not being inherited in all browsers. - * 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. - */ -button, -input, -select, -textarea { - font-family: inherit; - /* 1 */ - font-size: 100%; - /* 2 */ - margin: 0; - /* 3 */ -} -/** - * Address Firefox 4+ setting `line-height` on `input` using `!important` in - * the UA stylesheet. - */ -button, -input { - line-height: normal; -} -/** - * Address inconsistent `text-transform` inheritance for `button` and `select`. - * All other form control elements do not inherit `text-transform` values. - * Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+. - * Correct `select` style inheritance in Firefox 4+ and Opera. - */ -button, -select { - text-transform: none; -} -/** - * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` - * and `video` controls. - * 2. Correct inability to style clickable `input` types in iOS. - * 3. Improve usability and consistency of cursor style between image-type - * `input` and others. - */ -button, -html input[type="button"], -input[type="reset"], -input[type="submit"] { - -webkit-appearance: button; - /* 2 */ - cursor: pointer; - /* 3 */ -} -/** - * Re-set default cursor for disabled elements. - */ -button[disabled], -html input[disabled] { - cursor: default; -} -/** - * 1. Address box sizing set to `content-box` in IE 8/9/10. - * 2. Remove excess padding in IE 8/9/10. - */ -input[type="checkbox"], -input[type="radio"] { - box-sizing: border-box; - /* 1 */ - padding: 0; - /* 2 */ -} -/** - * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. - * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome - * (include `-moz` to future-proof). - */ -input[type="search"] { - -webkit-appearance: textfield; - /* 1 */ - -moz-box-sizing: content-box; - -webkit-box-sizing: content-box; - /* 2 */ - box-sizing: content-box; -} -/** - * Remove inner padding and search cancel button in Safari 5 and Chrome - * on OS X. - */ -input[type="search"]::-webkit-search-cancel-button, -input[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} -/** - * Remove inner padding and border in Firefox 4+. - */ -button::-moz-focus-inner, -input::-moz-focus-inner { - border: 0; - padding: 0; -} -/** - * 1. Remove default vertical scrollbar in IE 8/9. - * 2. Improve readability and alignment in all browsers. - */ -textarea { - overflow: auto; - /* 1 */ - vertical-align: top; - /* 2 */ -} -/* ========================================================================== - Tables - ========================================================================== */ -/** - * Remove most spacing between table cells. - */ -table { - border-collapse: collapse; - border-spacing: 0; -} -body { - background-color: #fff; - margin: 0 auto; - height: 100%; - zoom: 1; - letter-spacing: normal; - word-spacing: normal; - color: #111; - font: 20px "Source Sans Pro", "Calibri", "Helvetica", "Arial", "Freesans", "Clean", sans-serif; - line-height: 1.4; - -webkit-font-smoothing: antialiased; - width: 960px; - text-align: left; - text-rendering: optimizelegibility; - font-variant-ligatures: common-ligatures discretionary-ligatures; - font-feature-settings: "kern", "onum", "lnum" off; -} -@media screen and (max-width: 659px) { - body { - width: 100%; - } -} -@media screen and (min-width: 660px) { - body { - width: 660px; - margin: 0 auto; - } -} -h1, -h2 { - border-top: 2px solid #ddd; -} -h1, -h2, -h3 { - margin-top: 3em; - padding-top: 0.5em; - margin-bottom: 0.3em; -} -h4, -h4, -h5 { - margin-top: 2em; -} -h1 + h2, -h2 + h3, -h3 + h4 { - border-top: none; - margin-top: 2em; -} -h1, -h2, -h3, -h4, -h5, -h6 { - font-family: "Source Serif Pro", serif; -} -h1 a, -h2 a, -h3 a, -h4 a, -h5 a, -h6 a { - text-decoration: none; - color: #000; -} -h1 a:hover, -h2 a:hover, -h3 a:hover, -h4 a:hover, -h5 a:hover, -h6 a:hover { - text-underline-mode: dotted; - text-underline: dotted; -} -h1 a:hover::after, -h2 a:hover::after, -h3 a:hover::after, -h4 a:hover::after, -h5 a:hover::after, -h6 a:hover::after { - font: 80% "Source Sans Pro", "Calibri", "Helvetica", "Arial", "Freesans", "Clean", sans-serif; - content: "¶"; - margin-left: 1em; - color: #ccc; - text-decoration: none; -} -h1 .gistid, -h2 .gistid, -h3 .gistid, -h4 .gistid, -h5 .gistid, -h6 .gistid { - font-size: 20%; -} -p, -li, -dd { - line-height: 130%; - text-align: justify; -} -img { - max-width: 100%; -} -a { - text-decoration: none; - color: #46c; -} -code { - background-color: #f6f6f6; - font: 90% "Source Code Pro", "Monaco", "DejaVu Sans Mono", "Courier New", monospace; - padding: 2px 3px; - line-height: 150%; -} -pre { - background-color: #f6f6f6; - border: 1px solid #dddddd; - padding: 3px; - display: block; - font: 70% "Source Code Pro", "Monaco", "DejaVu Sans Mono", "Courier New", monospace; -} -pre code { - line-height: 140%; -} -blockquote { - border-left: 4px solid #ddd; - padding-left: 1em; - font-style: italic; -} -ul > li { - margin-left: -2em; -} -ul > li li { - margin-left: 0; -} -ul { - margin-bottom: 3em; -} -li.task-list-item { - list-style: none; -} -li.task-list-item input[type=checkbox] { - margin-right: 0.5em; -} -table { - margin: 2em 0 3em 0; - border-bottom: 1px solid #dddddd; - font-feature-settings: "tnum", "pnum" off; -} -table tr th { - padding: 5px 2px; - border-bottom: 1px solid #dddddd; - text-align: left; - font-family: "Source Serif Pro", serif; -} -table tr td { - padding: 5px 2px; -} -table tr { - border-bottom: 0.5px solid #e9e9e9; -} -h1#gistid { - font-size: 60%; - margin: 0px; - color: #ccc; - border-top: none; -} -h1#gistid a { - color: #ccc; -} diff --git a/docs/css/style.less b/docs/css/style.less deleted file mode 100644 index f9adf7171d5..00000000000 --- a/docs/css/style.less +++ /dev/null @@ -1,186 +0,0 @@ -@import "_normalize.less"; -// vars -@import url(//fonts.googleapis.com/css?family=Source+Serif+Pro); -@import url(//fonts.googleapis.com/css?family=Source+Code+Pro); - -@sans-font: "Source Sans Pro", "Calibri", "Helvetica", "Arial", "Freesans", "Clean", sans-serif; -@serif-font: "Source Serif Pro", serif; -@monospace-font: "Source Code Pro", "Monaco", "DejaVu Sans Mono", "Courier New", monospace; - -@text-color: #111; -@heading-color: #000; -@link-color: #46c; - -@code-bg-color: #f6f6f6; -@code-border-color: darken(@code-bg-color, 10%); -@table-row-color: #f6f6f6; - -@faint-color: #ccc; - -// layout -body { - background-color: #fff; - margin: 0 auto; - height: 100%; - zoom: 1; - letter-spacing: normal; - word-spacing: normal; - color: @text-color; - font: 20px @sans-font; - line-height: 1.4; - -webkit-font-smoothing: antialiased; - width: 960px; - text-align: left; - text-rendering: optimizelegibility; - font-variant-ligatures: common-ligatures discretionary-ligatures; - font-feature-settings: "kern", "onum", "lnum" off; -} - -@media screen and (max-width: 659px) { - body { - width: 100%; - } -} - -@media screen and (min-width: 660px) { - body { - width: 660px; - margin: 0 auto; - } -} - -h1, h2 { - border-top: 2px solid #ddd; -} - -h1, h2, h3 { - margin-top: 3em; - padding-top: 0.5em; - margin-bottom: 0.3em; -} - -h4, h4, h5 { - margin-top: 2em; -} - -h1 + h2, h2 + h3, h3 + h4 { - border-top: none; - margin-top: 2em; -} - -h1, h2, h3, h4, h5, h6 { - font-family: @serif-font; - - a { - text-decoration: none; - color: @heading-color; - - &:hover { - text-underline-mode: dotted; - text-underline: dotted; - } - - &:hover::after { - font: 80% @sans-font; - content: "¶"; - margin-left: 1em; - color: @faint-color; - text-decoration: none; - } - } - - .gistid { - font-size: 20%; - } -} - -p, li, dd { - line-height: 130%; - text-align: justify; -} - -img { - max-width: 100%; -} - -a { - text-decoration: none; - color: @link-color; -} - -code { - background-color: @code-bg-color; - font: 90% @monospace-font; - padding: 2px 3px; - line-height: 150%; -} - -pre { - background-color: @code-bg-color; - border: 1px solid @code-border-color; - padding: 3px; - display: block; - font: 70% @monospace-font; - - code { - line-height: 140%; - } -} - -blockquote { - border-left: 4px solid #ddd; - padding-left: 1em; - font-style: italic; -} - -ul > li { - margin-left: -2em; - li { - margin-left: 0; - } -} - -ul { - margin-bottom: 3em; -} - -li.task-list-item { - list-style: none; - input[type=checkbox] { - margin-right: 0.5em; - } -} - -table { - margin: 2em 0 3em 0; - - border-bottom: 1px solid @code-border-color; - - font-feature-settings: "tnum", "pnum" off; - - tr th { - padding: 5px 2px; - border-bottom: 1px solid @code-border-color; - text-align: left; - font-family: @serif-font; - } - - tr td { - padding: 5px 2px; - } - - tr { - border-bottom: 0.5px solid lighten(@code-border-color, 5%); - } -} - -h1#gistid { - font-size: 60%; - margin: 0px; - color: @faint-color; - border-top: none; - - a { - color: @faint-color; - } -} \ No newline at end of file diff --git a/docs/examples/build.gradle b/docs/examples/build.gradle new file mode 100644 index 00000000000..e5247a6e9d8 --- /dev/null +++ b/docs/examples/build.gradle @@ -0,0 +1,10 @@ +description = "Examples for docs" + +dependencies { + compile "io.lettuce:lettuce-core:5.1.1.RELEASE" + + testCompile "junit:junit:4.12" + testCompile project(":testcontainers") + testCompile project(":selenium") + testCompile "org.seleniumhq.selenium:selenium-api:3.141.59" +} diff --git a/docs/examples/src/main/java/quickstart/RedisBackedCache.java b/docs/examples/src/main/java/quickstart/RedisBackedCache.java new file mode 100644 index 00000000000..1123858ca17 --- /dev/null +++ b/docs/examples/src/main/java/quickstart/RedisBackedCache.java @@ -0,0 +1,22 @@ +package quickstart; + +import io.lettuce.core.RedisClient; +import io.lettuce.core.api.StatefulRedisConnection; + +public class RedisBackedCache { + + private final StatefulRedisConnection connection; + + public RedisBackedCache(String hostname, Integer port) { + RedisClient client = RedisClient.create(String.format("redis://%s:%d/0", hostname, port)); + connection = client.connect(); + } + + public String get(String key) { + return connection.sync().get(key); + } + + public void put(String key, String value) { + connection.sync().set(key, value); + } +} diff --git a/docs/examples/src/test/java/generic/CmdModifierTest.java b/docs/examples/src/test/java/generic/CmdModifierTest.java new file mode 100644 index 00000000000..9f87a4da21c --- /dev/null +++ b/docs/examples/src/test/java/generic/CmdModifierTest.java @@ -0,0 +1,39 @@ +package generic; + +import org.junit.Rule; +import org.junit.Test; +import org.testcontainers.containers.Container; +import org.testcontainers.containers.GenericContainer; + +import java.io.IOException; + +import static org.junit.Assert.assertEquals; + +public class CmdModifierTest { + + // hostname { + @Rule + public GenericContainer theCache = new GenericContainer<>("redis:3.0.2") + .withCreateContainerCmdModifier(cmd -> cmd.withHostName("the-cache")); + // } + + // memory { + @Rule + public GenericContainer memoryLimitedRedis = new GenericContainer<>("redis:3.0.2") + .withCreateContainerCmdModifier(cmd -> cmd.withMemory((long) 4 * 1024 * 1024)) + .withCreateContainerCmdModifier(cmd -> cmd.withMemorySwap((long) 4 * 1024 * 1024)); + // } + + + @Test + public void testHostnameModified() throws IOException, InterruptedException { + final Container.ExecResult execResult = theCache.execInContainer("hostname"); + assertEquals("the-cache", execResult.getStdout().trim()); + } + + @Test + public void testMemoryLimitModified() throws IOException, InterruptedException { + final Container.ExecResult execResult = memoryLimitedRedis.execInContainer("cat", "/sys/fs/cgroup/memory/memory.limit_in_bytes"); + assertEquals("4194304", execResult.getStdout().trim()); + } +} diff --git a/docs/examples/src/test/java/generic/CommandsTest.java b/docs/examples/src/test/java/generic/CommandsTest.java new file mode 100644 index 00000000000..486464c7dda --- /dev/null +++ b/docs/examples/src/test/java/generic/CommandsTest.java @@ -0,0 +1,21 @@ +package generic; + +import org.junit.Rule; +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; + +import static org.junit.Assert.assertTrue; + +public class CommandsTest { + @Rule + // startupCommand { + public GenericContainer redisWithCustomPort = new GenericContainer("redis:5.0") + .withCommand("redis-server --port 7777") + // } + .withExposedPorts(7777); + + @Test + public void testStartupCommandOverrideApplied() { + assertTrue(redisWithCustomPort.isRunning()); // good enough to check that the container started listening + } +} diff --git a/docs/examples/src/test/java/generic/ContainerCreationTest.java b/docs/examples/src/test/java/generic/ContainerCreationTest.java new file mode 100644 index 00000000000..19a40a7c43a --- /dev/null +++ b/docs/examples/src/test/java/generic/ContainerCreationTest.java @@ -0,0 +1,36 @@ +package generic; + +import org.junit.ClassRule; +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; + +import static org.junit.Assert.assertTrue; + +public class ContainerCreationTest { + + // simple { + @ClassRule + public static GenericContainer redis = + new GenericContainer("redis:3.0.2") + .withExposedPorts(6379); + // } + + // withOptions { + // Set up a plain OS container and customize environment, + // command and exposed ports. This just listens on port 80 + // and always returns '42' + @ClassRule + public static GenericContainer alpine = + new GenericContainer("alpine:3.2") + .withExposedPorts(80) + .withEnv("MAGIC_NUMBER", "42") + .withCommand("/bin/sh", "-c", + "while true; do echo \"$MAGIC_NUMBER\" | nc -l -p 80; done"); + // } + + @Test + public void testStartup() { + assertTrue(redis.isRunning()); // good enough to check that the container started listening + assertTrue(alpine.isRunning()); // good enough to check that the container started listening + } +} diff --git a/docs/examples/src/test/java/generic/ContainerLabelTest.java b/docs/examples/src/test/java/generic/ContainerLabelTest.java new file mode 100644 index 00000000000..f8f26eaa5fe --- /dev/null +++ b/docs/examples/src/test/java/generic/ContainerLabelTest.java @@ -0,0 +1,22 @@ +package generic; + +import org.testcontainers.containers.GenericContainer; + +import java.util.HashMap; +import java.util.Map; + +public class ContainerLabelTest { + // single_label { + public GenericContainer containerWithLabel = new GenericContainer("alpine:3.6") + .withLabel("key", "value"); + // } + + + // multiple_labels { + private Map mapOfLabels = new HashMap<>(); + // populate map, e.g. mapOfLabels.put("key1", "value1"); + + public GenericContainer containerWithMultipleLabels = new GenericContainer("alpine:3.6") + .withLabels(mapOfLabels); + // } +} diff --git a/docs/examples/src/test/java/generic/ExecTest.java b/docs/examples/src/test/java/generic/ExecTest.java new file mode 100644 index 00000000000..3ff67956d39 --- /dev/null +++ b/docs/examples/src/test/java/generic/ExecTest.java @@ -0,0 +1,30 @@ +package generic; + +import org.junit.Rule; +import org.junit.Test; +import org.testcontainers.containers.Container; +import org.testcontainers.containers.GenericContainer; + +import java.io.IOException; + +import static org.junit.Assert.assertTrue; + +public class ExecTest { + + @Rule + public GenericContainer container = new GenericContainer("alpine:3.6") + .withCommand("top"); + + @Test + public void testSimpleExec() throws IOException, InterruptedException { + // standaloneExec { + container.execInContainer("touch", "/somefile.txt"); + // } + + // execReadingStdout { + Container.ExecResult lsResult = container.execInContainer("ls", "-al", "/"); + String stdout = lsResult.getStdout(); + assertTrue(stdout.contains("somefile.txt")); + // } + } +} diff --git a/docs/examples/src/test/java/generic/HostPortExposedTest.java b/docs/examples/src/test/java/generic/HostPortExposedTest.java new file mode 100644 index 00000000000..1d15c2553b8 --- /dev/null +++ b/docs/examples/src/test/java/generic/HostPortExposedTest.java @@ -0,0 +1,65 @@ +package generic; + +import com.sun.net.httpserver.HttpServer; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.openqa.selenium.chrome.ChromeOptions; +import org.openqa.selenium.remote.RemoteWebDriver; +import org.testcontainers.Testcontainers; +import org.testcontainers.containers.BrowserWebDriverContainer; + +import java.io.OutputStream; +import java.net.InetSocketAddress; + +import static org.junit.Assert.assertTrue; + +public class HostPortExposedTest { + + private static HttpServer server; + private static int localServerPort; + + @BeforeClass + public static void setUp() throws Exception { + server = HttpServer.create(new InetSocketAddress(0), 0); + server.createContext("/", exchange -> { + byte[] content = "Hello World!".getBytes(); + exchange.sendResponseHeaders(200, content.length); + try (OutputStream responseBody = exchange.getResponseBody()) { + responseBody.write(content); + responseBody.flush(); + } + }); + + server.start(); + localServerPort = server.getAddress().getPort(); + + // exposePort { + Testcontainers.exposeHostPorts(localServerPort); + // } + } + + @AfterClass + public static void tearDown() throws Exception { + server.stop(0); + } + + @Rule + public BrowserWebDriverContainer browser = new BrowserWebDriverContainer() + .withCapabilities(new ChromeOptions()); + + @Test + public void testContainerRunningAgainstExposedHostPort() { + // useHostExposedPort { + final String rootUrl = + String.format("http://host.testcontainers.internal:%d/", localServerPort); + + final RemoteWebDriver webDriver = browser.getWebDriver(); + webDriver.get(rootUrl); + // } + + final String pageSource = webDriver.getPageSource(); + assertTrue(pageSource.contains("Hello World!")); + } +} diff --git a/docs/examples/src/test/java/generic/MultiplePortsExposedTest.java b/docs/examples/src/test/java/generic/MultiplePortsExposedTest.java new file mode 100644 index 00000000000..f22bc55defd --- /dev/null +++ b/docs/examples/src/test/java/generic/MultiplePortsExposedTest.java @@ -0,0 +1,43 @@ +package generic; + +import org.junit.Rule; +import org.junit.Test; +import org.slf4j.Logger; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; + +import static org.slf4j.LoggerFactory.getLogger; + +public class MultiplePortsExposedTest { + private static final Logger log = getLogger(MultiplePortsExposedTest.class); + + + @Rule + // rule { + public GenericContainer container = new GenericContainer("orientdb:3.0.13") + .withExposedPorts(2424, 2480) + .withLogConsumer(new Slf4jLogConsumer(log)); + // } + + @Test + public void fetchPortsByNumber() { + Integer firstMappedPort = container.getMappedPort(2424); + Integer secondMappedPort = container.getMappedPort(2480); + } + + @Test + public void fetchFirstMappedPort() { + Integer firstMappedPort = container.getFirstMappedPort(); + } + + @Test + public void getContainerIpAddressOnly() { + String ipAddress = container.getContainerIpAddress(); + } + + @Test + public void getContainerIpAddressAndMappedPort() { + String address = + container.getContainerIpAddress() + ":" + container.getMappedPort(2424); + } +} diff --git a/docs/examples/src/test/java/generic/WaitStrategiesTest.java b/docs/examples/src/test/java/generic/WaitStrategiesTest.java new file mode 100644 index 00000000000..0bb5eb40bc1 --- /dev/null +++ b/docs/examples/src/test/java/generic/WaitStrategiesTest.java @@ -0,0 +1,70 @@ +package generic; + +import org.junit.Rule; +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.containers.wait.strategy.WaitStrategy; + +import static org.junit.Assert.assertTrue; + +public class WaitStrategiesTest { + + @Rule + // waitForNetworkListening { + public GenericContainer nginx = new GenericContainer("nginx:1.9.4") + .withExposedPorts(80); + // } + + @Rule + // waitForSimpleHttp { + public GenericContainer nginxWithHttpWait = new GenericContainer("nginx:1.9.4") + .withExposedPorts(80) + .waitingFor(Wait.forHttp("/")); + // } + + @Rule + // logMessageWait { + public GenericContainer containerWithLogWait = new GenericContainer("redis:5.0.3") + .withExposedPorts(6379) + .waitingFor( + Wait.forLogMessage(".*Ready to accept connections.*\\n", 1) + ); + // } + + private static final HttpWaitStrategy MULTI_CODE_HTTP_WAIT = + // waitForHttpWithMultipleStatusCodes { + Wait.forHttp("/") + .forStatusCode(200) + .forStatusCode(301) + // } + ; + + private static final HttpWaitStrategy PREDICATE_HTTP_WAIT = + // waitForHttpWithStatusCodePredicate { + Wait.forHttp("/all") + .forStatusCodeMatching(it -> it >= 200 && it < 300 || it == 401) + // } + ; + + private static final HttpWaitStrategy TLS_HTTP_WAIT = + // waitForHttpWithTls { + Wait.forHttp("/all") + .usingTls() + // } + ; + + private static final WaitStrategy HEALTHCHECK_WAIT = + // healthcheckWait { + Wait.forHealthcheck() + // } + ; + + @Test + public void testContainersAllStarted() { + assertTrue(nginx.isRunning()); + assertTrue(nginxWithHttpWait.isRunning()); + assertTrue(containerWithLogWait.isRunning()); + } +} diff --git a/docs/examples/src/test/java/quickstart/RedisBackedCacheIntTest.java b/docs/examples/src/test/java/quickstart/RedisBackedCacheIntTest.java new file mode 100644 index 00000000000..ac3ae7746b4 --- /dev/null +++ b/docs/examples/src/test/java/quickstart/RedisBackedCacheIntTest.java @@ -0,0 +1,38 @@ +package quickstart; + + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; + +import static org.junit.Assert.assertEquals; + +public class RedisBackedCacheIntTest { + + private RedisBackedCache underTest; + + // rule { + @Rule + public GenericContainer redis = new GenericContainer<>("redis:5.0.3-alpine") + .withExposedPorts(6379); + // } + + + @Before + public void setUp() { + String address = redis.getContainerIpAddress(); + Integer port = redis.getFirstMappedPort(); + + // Now we have an address and port for Redis, no matter where it is running + underTest = new RedisBackedCache(address, port); + } + + @Test + public void testSimplePutAndGet() { + underTest.put("test", "example"); + + String retrieved = underTest.get("test"); + assertEquals("example", retrieved); + } +} diff --git a/docs/examples/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java b/docs/examples/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java new file mode 100644 index 00000000000..61a86ac3f8b --- /dev/null +++ b/docs/examples/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java @@ -0,0 +1,27 @@ +package quickstart; + + +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +@Ignore("This test class is deliberately invalid, as it relies on a non-existent local Redis") +public class RedisBackedCacheIntTestStep0 { + private RedisBackedCache underTest; + + @Before + public void setUp() { + // Assume that we have Redis running locally? + underTest = new RedisBackedCache("localhost", 6379); + } + + @Test + public void testSimplePutAndGet() { + underTest.put("test", "example"); + + String retrieved = underTest.get("test"); + assertEquals("example", retrieved); + } +} diff --git a/docs/examples/src/test/resources/logback-test.xml b/docs/examples/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..49da0be9709 --- /dev/null +++ b/docs/examples/src/test/resources/logback-test.xml @@ -0,0 +1,27 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + + + + + + + + + PROFILER + DENY + + diff --git a/docs/favicon.ico b/docs/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..004282385c3e9b51ece573d194b64693145dc2e1 GIT binary patch literal 15086 zcmeHOe{2)i9Y30_tZETd>N;uKL?%|PTO}4dAy9@0q8e+rL6bPyTA+-wt%FVdqusQP zNgdwQLE}3okOFNOA@QB>Y)pu4g_SXlQMDawV;lMdI%wJwC@pk_62bz3B))x~ojWeh z`T3oFNuz2fUEbZj?|Yxm_r2%$y?^0LU!{K`c3pgmFdb#Q(|<37y)Qkp;U z!EsR%91l)JA0i3ilBd-9N%|1(^pt?-agD24C<$ffvhXK$aTRs(n5v?SALlC)7k&=D{brnv-^BW| z|4{g++LOdPonb(B$Rx6EPVja*(moDG`^W} z;D3gZKdFnP#5+ArO{Jf;(+^cuCq?3(UxC}TOn$lErG{DWTi~B+Pq2@*dP-;5;X_AU zxD6ctl8a}yc(K0xbRPWShqKqmJC_&YL-dd^AG}UyHjKDL#ae~jN@ zg(J;Y+BE0g@#Y^+2+cH3R^p_McfT{<{Lxqb*$PLRt+Z+G+vCsw`uv^7jU`TMoZB_t z{G%~X6AL#M8|mqJ$L-)x=G@G`-vs|G3pc9Gzn;7j2rfOY_^Jk_hS|po^}$O26OHp* zsoaLwhS_X(@Bj4)MXI@|NG?^orDqnB-$8%AepulTUR$zpqkivvYAD#Y@`%PcGn98M z?h(DEr}FW^691U6nab0o{CB_b#D|Jp`#~n>QSV~4Dm8xVs8t`V@DC^Wr2f zA<4vp^1Ee8FF(7GjM-O>epulT?)v<{{YCeMfc%4#EVy>s$UDA&K@>{$wVNgWG2TPU zf7jke{|!!8?c`-CFN_ub#<@pGJy_w7GWVwDE(Nv&OI#^f{-#AI(9il<=WlKD(mODJ z*>4xV4fz}n_*@QF{=|-GlCtD_1lS|yVK#8l=X4Bt4Je}P4^2&v1N zvcfC3t>1M-mTQ?chg@}|_+884!?|(rV-RJ&;km~9Cg#e-FD~uY03|K zbGXQSO$(D1>G%HW>9A*NI_xe=hwe$~Fg3^H)Fj7Mko5f=H=Tq%95QzVSnJ?R79#UO!*6k){%D@s;Th#;d1JkoPO8J zQ7cy!7qL%=_|$gl}LSn>vq^ItRr& z&a`^Ixk!s2%Md=V0=9mh2Kv3G6^_XsyXH&urMfA=mC5?ucmw}}QP040r(JvG8g@tA zQGox^Xnb9#@`bkCbxS_@dtTpg zD}0V{UTm_HE(LYho6QSptggu(I z$bZ0yL3@_GWme8{?A@{6@%e1^5sC3XqhXoFN%gaIEsW9*biER1CRb;}A8uprqKrYO#2KvM+&}``VYVSr@r9M zWv|Pv_n*gafPEj6f*(!rKQ;Q6?YV9LZ@UzK&H3x=nd&6=gJbY9sr#vwcV?0cTl<|CR-Zfq!ij2MT^9=2>CH4`MP4oBO$M`(*oEQZk`gd^QvAB6ntPWjdVVB zEx2pNYoqe9f@xywf94*te{`AF{@VV&wi5eTXYIz;^J`grUTU1TB3u7Yz89by<Pav@5 zi)MM|^DRR+p<(x+uX;Eij9h6_x(D_>P`;*pA@t6Dfx!J=&XrH0@ruti6CC1(dhR8A zLWSmQB|XZbNS11TgR$rIJ_bET{mi+qlfCsGSg}AChPcAK7Oxrj+Q?d98sRe z^h8HUrH^?ALg7{aME_yBui@XiuoGo>ndD&I2aR~b__-ZNs{bi3U*ApPU$?`UV7a_&zDTPtTEp97<~SlAyX z>v`;Ju0|fcxQ9>|b@}Yik})IK{wUEllk1@CPUpBD>=<+xaoqEiy9c|(#5P$$G*OQG EAH!e_G5`Po literal 0 HcmV?d00001 diff --git a/docs/features/advanced_options.md b/docs/features/advanced_options.md new file mode 100644 index 00000000000..385cdc3a4c8 --- /dev/null +++ b/docs/features/advanced_options.md @@ -0,0 +1,38 @@ +# Advanced options + +## Container labels + +To add a custom label to the container, use `withLabel`: + + +[Adding a single label](../examples/src/test/java/generic/ContainerLabelTest.java) inside_block:single_label + + +Additionally, multiple labels may be applied together from a map: + + +[Adding multiple labels](../examples/src/test/java/generic/ContainerLabelTest.java) inside_block:multiple_labels + + + +## Customizing the container + +It is possible to use the [`docker-java`](https://github.com/docker-java/docker-java) API directly to customize containers before creation. This is useful if there is a need to use advanced Docker features that are not exposed by the Testcontainers API. Any customizations you make using `withCreateContainerCmdModifier` will be applied _on top_ of the container definition that Testcontainers creates, but before it is created. + +For example, this can be used to change the container hostname: + + +[Using modifier to change hostname](../examples/src/test/java/generic/CmdModifierTest.java) inside_block:hostname + + +... or modify container memory (see [this](https://fabiokung.com/2014/03/13/memory-inside-linux-containers/) if it does not appear to work): + + +[Using modifier to change memory limits](../examples/src/test/java/generic/CmdModifierTest.java) inside_block:memory + + +!!! note + It is recommended to use this sparingly, and follow changes to the `docker-java` API if you choose to use this. + It is typically quite stable, though. + +For what is possible, consult the [`docker-java CreateContainerCmd` source code](https://github.com/docker-java/docker-java/blob/master/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java). diff --git a/docs/features/commands.md b/docs/features/commands.md new file mode 100644 index 00000000000..238c140765e --- /dev/null +++ b/docs/features/commands.md @@ -0,0 +1,33 @@ +# Executing commands + +## Container startup command + +By default the container will execute whatever command is specified in the image's Dockerfile. To override this, and specify a different command, use `withCommand`. For example: + + +[Specifying a startup command](../examples/src/test/java/generic/CommandsTest.java) inside_block:startupCommand + + +## Executing a command + +Your test can execute a command inside a running container, similar to a `docker exec` call: + + +[Executing a command inside a running container](../examples/src/test/java/generic/ExecTest.java) inside_block:standaloneExec + + +This can be useful for software that has a command line administration tool. You can also get the output (stdout/stderr) from the command - for example: + + +[Executing a command inside a running container and reading the result](../examples/src/test/java/generic/ExecTest.java) inside_block:execReadingStdout + + +Note that there is no way to get the return code of the executed command, only stdout/stderr. + +## Environment variables + +To add environment variables to the container, use `withEnv`: +```java +new GenericContainer(...) + .withEnv("API_TOKEN", "foo") +``` diff --git a/docs/usage/properties.md b/docs/features/configuration.md similarity index 93% rename from docs/usage/properties.md rename to docs/features/configuration.md index 2b968c81f5a..9d2328f3b41 100644 --- a/docs/usage/properties.md +++ b/docs/features/configuration.md @@ -1,9 +1,10 @@ -# Custom properties +# Custom configuration You can override some default properties if your environment requires that. ## Configuration file location The configuration will be loaded from multiple locations. Properties are considered in the following order: + 1. `.testcontainers.properties` in user's home folder. Example locations: **Linux:** `/home/myuser/.testcontainers.properties` **Windows:** `C:/Users/myuser/.testcontainers.properties` @@ -24,10 +25,14 @@ Before running any containers Testcontainers will perform a set of startup check It takes a couple of seconds, but if you want to speed up your tests, you can disable the checks once you have everything configured. Add `checks.disable=true` to your `$HOME/.testcontainers.properties` to completely disable them. ## Customizing images + +Testcontainers uses public Docker images to perform different actions like startup checks, VNC recording and others. +Some companies disallow the usage of Docker Hub, but you can override `*.image` properties with your own images from your private registry to workaround that. + > **tinyimage.container.image = alpine:3.5** > Used by Testcontainers' core -> **vncrecorder.container.image = richnorth/vnc-recorder:latest** +> **vncrecorder.container.image = quay.io/testcontainers/vnc-recorder:1.1.0** > Used by VNC recorder in Testcontainers' Seleniun integration > **ambassador.container.image = richnorth/ambassador:latest** @@ -38,10 +43,10 @@ It takes a couple of seconds, but if you want to speed up your tests, you can di > Used by KafkaContainer ## Customizing ryuk resource reaper + > **ryuk.container.image = quay.io/testcontainers/ryuk:0.2.2** > The resource reaper is responsible for container removal and automatic cleanup of dead containers at JVM shutdown + > **ryuk.container.privileged = false** > In some environments ryuk must be started in privileged mode to work properly (--privileged flag) -Testcontainers uses public Docker images to perform different actions like startup checks, VNC recording and others. -Some companies disallow the usage of Docker Hub, but you can override `*.image` properties with your own images from your private registry to workaround that. diff --git a/docs/features/container_logs.md b/docs/features/container_logs.md new file mode 100644 index 00000000000..389dc57c631 --- /dev/null +++ b/docs/features/container_logs.md @@ -0,0 +1,56 @@ +# Accessing container logs + +It is possible to capture container output using the `followOutput()` method. This method accepts a Consumer and (optionally) +a varargs list stating which of STDOUT, STDERR, or both, should be followed. If not specified, both will be followed. + +At present, container output will always begin from the time of container creation. + +Testcontainers includes some out-of-the-box Consumer implementations that can be used; examples follow. + +## Streaming container output to an SLF4J logger + +Given an existing SLF4J logger instance named LOGGER: +```java +Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(LOGGER); +container.followOutput(logConsumer); +``` + +## Capturing container output as a String +```java +ToStringConsumer toStringConsumer = new ToStringConsumer(); +container.followOutput(toStringConsumer, OutputType.STDOUT); + +String utf8String = toStringConsumer.toUtf8String(); + +// Or if the container output is not UTF-8 +String otherString = toStringConsumer.toString(CharSet.forName("ISO-8859-1")); +``` + +## Waiting for container output to contain expected content + +`WaitingConsumer` will block until a frame of container output (usually a line) matches a provided predicate. + +A timeout may be specified, as shown in this example. +```java +WaitingConsumer consumer = new WaitingConsumer(); + +container.followOutput(consumer, STDOUT); + +consumer.waitUntil(frame -> + frame.getUtf8String().contains("STARTED"), 30, TimeUnit.SECONDS); +``` + +Additionally, as the Java 8 Consumer functional interface is used, Consumers may be composed together. This is +useful, for example, to capture all the container output but only when a matching string has been found. e.g.: +```java +WaitingConsumer waitingConsumer = new WaitingConsumer(); +ToStringConsumer toStringConsumer = new ToStringConsumer(); + +Consumer composedConsumer = toStringConsumer.andThen(waitingConsumer); +container.followOutput(composedConsumer); + +waitingConsumer.waitUntil(frame -> + frame.getUtf8String().contains("STARTED"), 30, TimeUnit.SECONDS); + +String utf8String = toStringConsumer.toUtf8String(); +``` diff --git a/docs/features/creating_container.md b/docs/features/creating_container.md new file mode 100644 index 00000000000..16413924197 --- /dev/null +++ b/docs/features/creating_container.md @@ -0,0 +1,33 @@ +# Creating a container + +## Creating a generic container based on an image + +Testcontainers' generic container support offers the most flexibility, and makes it easy to use virtually any container +images as temporary test dependencies. For example, if you might use it to test interactions with: + +* NoSQL databases or other data stores (e.g. redis, elasticsearch, mongo) +* Web servers/proxies (e.g. nginx, apache) +* Log services (e.g. logstash, kibana) +* Other services developed by your team/organization which are already dockerized + +With a generic container, you set the container image using a parameter to the rule constructor, e.g.: +```java +new GenericContainer("jboss/wildfly:9.0.1.Final") +``` + +### Examples + +A generic container rule can be used with any public docker image; for example: + + +[Creating a Redis container (JUnit 4)](../examples/src/test/java/generic/ContainerCreationTest.java) inside_block:simple + + +Further options may be specified: + + +[Creating a container with more options (JUnit 4)](../examples/src/test/java/generic/ContainerCreationTest.java) inside_block:withOptions + + +These containers, as `@ClassRule`s, will be started before any tests in the class run, and will be destroyed after all +tests have run. diff --git a/docs/usage/dockerfile.md b/docs/features/creating_images.md similarity index 98% rename from docs/usage/dockerfile.md rename to docs/features/creating_images.md index 37716c7d6ad..0437e6d8a8e 100644 --- a/docs/usage/dockerfile.md +++ b/docs/features/creating_images.md @@ -1,6 +1,6 @@ -# Dockerfile containers +# Creating images on-the-fly -## Benefits +## Overview In situations where there is no pre-existing Docker image, Testcontainers can create a new temporary image on-the-fly from a Dockerfile. For example, when the component under test is the Docker image itself, or when an existing base diff --git a/docs/features/files.md b/docs/features/files.md new file mode 100644 index 00000000000..a74b1add134 --- /dev/null +++ b/docs/features/files.md @@ -0,0 +1,11 @@ +# Files and volumes + +## Volume mapping + +It is possible to map a file or directory **on the classpath** into the container as a volume using `withClasspathResourceMapping`: +```java +new GenericContainer(...) + .withClasspathResourceMapping("redis.conf", + "/etc/redis.conf", + BindMode.READ_ONLY) +``` diff --git a/docs/features/networking.md b/docs/features/networking.md new file mode 100644 index 00000000000..d0007df878b --- /dev/null +++ b/docs/features/networking.md @@ -0,0 +1,77 @@ +# Networking and communicating with containers + +## Exposing container ports to the host + +It is common to want to connect to a container from your test process, running on the test 'host' machine. +For example, you may be testing a class that needs to connect to a backend or data store container. + +Generally, each required port needs to be explicitly exposed. For example, we can specify one or more ports as follows: + + +[Exposing ports](../examples/src/test/java/generic/MultiplePortsExposedTest.java) inside_block:rule + + +Note that this exposed port number is from the *perspective of the container*. + +*From the host's perspective* Testcontainers actually exposes this on a random free port. +This is by design, to avoid port collisions that may arise with locally running software or in between parallel test runs. + +Because there is this layer of indirection, it is necessary to ask Testcontainers for the actual mapped port at runtime. +This can be done using the `getMappedPort` method, which takes the original (container) port as an argument: + + +[Retrieving actual ports at runtime](../examples/src/test/java/generic/MultiplePortsExposedTest.java) inside_block:fetchPortsByNumber + + +!!! warning + Because the randomised port mapping happens during container startup, the container must be running at the time `getMappedPort` is called. + You may need to ensure that the startup order of components in your tests caters for this. + +There is also a `getFirstMappedPort` method for convenience, for the fairly common scenario of a container that only exposes one port: + + +[Retrieving the first mapped port](../examples/src/test/java/generic/MultiplePortsExposedTest.java) inside_block:fetchFirstMappedPort + + +## Getting the container IP address + +When running with a local Docker daemon, exposed ports will usually be reachable on `localhost`. +However, in some CI environments they may instead be reachable on a different host. + +As such, Testcontainers provides a convenience method to obtain an IP address on which the container should be reachable from the host machine. + + +[Getting the container IP address](../examples/src/test/java/generic/MultiplePortsExposedTest.java) inside_block:getContainerIpAddressOnly + + +It is normally advisable to use `getContainerIpAddress` and `getMappedPort` together when constructing addresses - for example: + + +[Getting the container IP address and mapped port](../examples/src/test/java/generic/MultiplePortsExposedTest.java) inside_block:getContainerIpAddressAndMappedPort + + +## Exposing host ports to the container + +In some cases it is necessary to make a network connection from a container to a socket that is listening on the host machine. +Natively, Docker has limited support for this model across platforms. +Testcontainers, however, makes this possible. + +In this example, assume that `localServerPort` is a port on our test host machine where a server (e.g. a web application) is running. + +We need to tell Testcontainers to prepare to expose this port to containers: + + +[Exposing the host port](../examples/src/test/java/generic/HostPortExposedTest.java) inside_block:exposePort + + +!!! warning + Note that the above command should be invoked _before_ containers are started. + +Having done so, we can now access this port from any containers that are launched. +From a container's perspective, the hostname will be `host.testcontainers.internal` and the port will be the same value as `localServerPort`. + +For example, here we construct an HTTP URL for our local web application and tell a Selenium container to get a page from it: + + +[Accessing the exposed host port from a container](../examples/src/test/java/generic/HostPortExposedTest.java) inside_block:useHostExposedPort + diff --git a/docs/features/startup_and_waits.md b/docs/features/startup_and_waits.md new file mode 100644 index 00000000000..a7e277b9892 --- /dev/null +++ b/docs/features/startup_and_waits.md @@ -0,0 +1,76 @@ +# Waiting for containers to start or be ready + +!!! info "Wait strategies vs Startup strategies" + + **Wait strategy:** is the container in a state that is useful for testing. This is generally approximated as 'can we talk to this container over the network'. However, there are quite a few variations and nuances. + + **Startup strategy:** did a container reach the desired running state. *Almost always* this just means 'wait until the container is running' - for a daemon process in a container this is the goal. Sometimes we need to wait until the container reaches a running state and then exits - this is the 'one shot startup' strategy, only used for cases where we need to run a one off command in a container but not a daemon. + + +## Wait Strategies + +Ordinarily Testcontainers will wait for up to 60 seconds for the container's first mapped network port to start listening. + +This simple measure provides a basic check whether a container is ready for use. + + +[Waiting for the first exposed port to start listening](../examples/src/test/java/generic/WaitStrategiesTest.java) inside_block:waitForNetworkListening + + +If the default 60s timeout is not sufficient, it can be altered with the `withStartupTimeout()` method. + +If waiting for a listening TCP port is not sufficient to establish whether the container is ready, you can use the +`waitingFor()` method with other [`WaitStrategy`](http://static.javadoc.io/org.testcontainers/testcontainers/{{ latest_version }}/org/testcontainers/containers/wait/strategy/WaitStrategy.html) implementations as shown below. + +### HTTP Wait strategy examples + +You can choose to wait for an HTTP(S) endpoint to return a particular status code. + +#### Waiting for 200 OK + +[](../examples/src/test/java/generic/WaitStrategiesTest.java) inside_block:waitForSimpleHttp + + +Variations on the HTTP wait strategy are supported, including: + +#### Waiting for multiple possible status codes + +[](../examples/src/test/java/generic/WaitStrategiesTest.java) inside_block:waitForHttpWithMultipleStatusCodes + + +#### Waiting for a status code that matches a predicate + +[Waiting for a status code that matches a predicate](../examples/src/test/java/generic/WaitStrategiesTest.java) inside_block:waitForHttpWithStatusCodePredicate + + +#### Using TLS + +[](../examples/src/test/java/generic/WaitStrategiesTest.java) inside_block:waitForHttpWithTls + + +### Healthcheck Wait strategy examples + +If the used image supports Docker's [Healthcheck](https://docs.docker.com/engine/reference/builder/#healthcheck) feature, you can directly leverage the `healthy` state of the container as your wait condition: + + +[](../examples/src/test/java/generic/WaitStrategiesTest.java) inside_block:healthcheckWait + + +### Log output Wait Strategy + +In some situations a container's log output is a simple way to determine if it is ready or not. +For example, we can wait for a `Ready' message in the container's logs as follows: + + +[](../examples/src/test/java/generic/WaitStrategiesTest.java) inside_block:logMessageWait + + +### Other Wait Strategies + +For further options, check out the [`Wait`](http://static.javadoc.io/org.testcontainers/testcontainers/{{ latest_version }}/org/testcontainers/containers/wait/strategy/Wait.html) convenience class, or the various subclasses of [`WaitStrategy`](http://static.javadoc.io/org.testcontainers/testcontainers/{{ latest_version }}/org/testcontainers/containers/wait/strategy/WaitStrategy.html). + +If none of these options meet your requirements, you can create your own subclass of +[`AbstractWaitStrategy`](http://static.javadoc.io/org.testcontainers/testcontainers/{{ latest_version }}/org/testcontainers/containers/wait/strategy/AbstractWaitStrategy.html) with an +appropriate wait mechanism in `waitUntilReady()`. +The `GenericContainer.waitingFor()` method accepts any valid [`WaitStrategy`](http://static.javadoc.io/org.testcontainers/testcontainers/{{ latest_version }}/org/testcontainers/containers/wait/strategy/WaitStrategy.html). + diff --git a/docs/getting_help.md b/docs/getting_help.md new file mode 100644 index 00000000000..e7fc053948d --- /dev/null +++ b/docs/getting_help.md @@ -0,0 +1,10 @@ +# Getting help + +We hope that you find Testcontainers intuitive to use and reliable. +However, sometimes things don't go the way we'd expect, and we'd like to try and help out if we can. + +To contact the Testcontainers team and other users you can: + +* Join our [Slack team](https://slack.testcontainers.org) +* [Search our issues tracker](https://github.com/testcontainers/testcontainers-java/issues), or raise a new issue if you find any bugs or have suggested improvements +* [Search Stack Overflow](https://stackoverflow.com/questions/tagged/testcontainers), especially among posts tagged with `testcontainers` diff --git a/docs/index.md b/docs/index.md index c23a27aab5f..b6b7213e581 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,45 +1,75 @@ # Testcontainers -> Testcontainers is a Java library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container. +![Testcontainers logo](./logo.png) -[![Build Status](https://travis-ci.org/testcontainers/testcontainers-java.svg?branch=master)](https://travis-ci.org/testcontainers/testcontainers-java) -[View on Github](https://github.com/testcontainers/testcontainers-java) +## About -![Testcontainers logo](https://rnorth.org/public/testcontainers/logo.png) +Testcontainers is a Java library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container. -## Use Cases +Testcontainers make the following kinds of tests easier: -Testcontainers makes it easy to launch useful Docker containers for the duration of JUnit tests. +* **Data access layer integration tests**: use a containerized instance of a MySQL, PostgreSQL or Oracle database to test your data access layer code for complete compatibility, but without requiring complex setup on developers' machines and safe in the knowledge that your tests will always start with a known DB state. Any other database type that can be containerized can also be used. +* **Application integration tests**: for running your application in a short-lived test mode with dependencies, such as databases, message queues or web servers. +* **UI/Acceptance tests**: use containerized web browsers, compatible with Selenium, for conducting automated UI tests. Each test can get a fresh instance of the browser, with no browser state, plugin variations or automated browser upgrades to worry about. And you get a video recording of each test session, or just each session where tests failed. +* **Much more!** Check out the various contributed modules or create your own custom container classes using [`GenericContainer`](features/creating_container.md) as a base. - * **Data access layer integration tests**: use a containerized instance of a MySQL, PostgreSQL or Oracle database to test your data access layer code for complete compatibility, but without requiring complex setup on developers' machines and safe in the knowledge that your tests will always start with a known DB state. Any other database type that can be containerized can also be used. - * **Application integration tests**: for running your application in a short-lived test mode with dependencies, such as databases, message queues or web servers. - * **UI/Acceptance tests**: use containerized web browsers, compatible with Selenium, for conducting automated UI tests. Each test can get a fresh instance of the browser, with no browser state, plugin variations or automated browser upgrades to worry about. And you get a video recording of each test session, or just each session where tests failed. - * **Much more!** Check out the various [contributed modules](https://github.com/testcontainers) or create your own custom container classes using [`GenericContainer`](usage/generic_containers.md) as a base. +## Prerequisites + +* Docker - please see [General Docker requirements](supported_docker_environment/index.md) +* A supported JVM testing framework: + * [JUnit 4](test_framework_integration/junit_4.md) - See the [JUnit 4 Quickstart Guide](quickstart/junit_4_quickstart.md) + * [Jupiter/JUnit 5](test_framework_integration/junit_5.md) + * [Spock](test_framework_integration/spock.md) + * *Or* manually add code to control the container/test lifecycle (See [hints for this approach](test_framework_integration/junit_4.md#manually-controlling-container-lifecycle) + +## Maven dependencies + +Testcontainers is distributed as separate JARs with a common version number: + +* A core JAR file for core functionality, generic containers and docker-compose support +* A separate JAR file for each of the specialised modules. Each module's documentation describes the Maven/Gradle dependency to add to your project's build. + +For the core library, the latest Maven/Gradle dependency is as follows: + +```groovy tab='Gradle' +testCompile "org.testcontainers:testcontainers:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + testcontainers + {{latest_version}} + test + +``` + +You can also [check the latest version available on Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.testcontainers%22). + +[JitPack](jitpack_dependencies.md) builds are available for pre-release versions. + +!!! warning "Shaded dependencies" + Testcontainers uses the docker-java client library, which in turn depends on JAX-RS, Jersey and Jackson libraries. + These libraries in particular seem to be especially prone to conflicts with test code/application under test code. + As such, **these libraries are 'shaded' into the core testcontainers JAR** and relocated under `org.testcontainers.shaded` to prevent class conflicts. ## Who is using Testcontainers? - * [ZeroTurnaround](https://zeroturnaround.com) - Testing of the Java Agents, micro-services, Selenium browser automation - * [Zipkin](http://zipkin.io) - MySQL and Cassandra testing - * [Apache Gora](https://gora.apache.org) - CouchDB testing - * [Apache James](https://james.apache.org) - LDAP and Cassandra integration testing - * [StreamSets](https://github.com/streamsets/datacollector) - LDAP, MySQL Vault, MongoDB, Redis integration testing - * [Playtika](https://github.com/Playtika/testcontainers-spring-boot) - Kafka, Couchbase, MariaDB, Redis, Neo4j, Aerospike, MemSQL - * [JetBrains](https://www.jetbrains.com/) - Testing of the TeamCity plugin for HashiCorp Vault - * [Plumbr](https://plumbr.io) - Integration testing of data processing pipeline micro-services - * [Streamlio](https://streaml.io/) - Integration and Chaos Testing of our fast data platform based on Apache Puslar, Apache Bookeeper and Apache Heron. - * [Spring Session](https://projects.spring.io/spring-session/) - Redis, PostgreSQL, MySQL and MariaDB integration testing - * [Apache Camel](https://camel.apache.org) - Testing Camel against native services such as Consul, Etcd and so on - * [Instana](https://www.instana.com) - Testing agents and stream processing backends - * [eBay Marketing](https://www.ebay.com) - Testing for MySQL, Cassandra, Redis, Couchbase, Kafka, etc. - * [Skyscanner](https://www.skyscanner.net/) - Integration testing against HTTP service mocks and various data stores - * [Neo4j-OGM](https://neo4j.com/developer/neo4j-ogm/) - Testing new, reactive client implementations - -## Getting help - -You can: - -* Join our Slack: http://slack.testcontainers.org -* [Search our issues tracker](https://github.com/testcontainers/testcontainers-java/issues), or raise a new issue if you find any bugs or have suggested improvements +* [ZeroTurnaround](https://zeroturnaround.com) - Testing of the Java Agents, micro-services, Selenium browser automation +* [Zipkin](https://zipkin.io) - MySQL and Cassandra testing +* [Apache Gora](https://gora.apache.org) - CouchDB testing +* [Apache James](https://james.apache.org) - LDAP and Cassandra integration testing +* [StreamSets](https://github.com/streamsets/datacollector) - LDAP, MySQL Vault, MongoDB, Redis integration testing +* [Playtika](https://github.com/Playtika/testcontainers-spring-boot) - Kafka, Couchbase, MariaDB, Redis, Neo4j, Aerospike, MemSQL +* [JetBrains](https://www.jetbrains.com/) - Testing of the TeamCity plugin for HashiCorp Vault +* [Plumbr](https://plumbr.io) - Integration testing of data processing pipeline micro-services +* [Streamlio](https://streaml.io/) - Integration and Chaos Testing of our fast data platform based on Apache Puslar, Apache Bookeeper and Apache Heron. +* [Spring Session](https://projects.spring.io/spring-session/) - Redis, PostgreSQL, MySQL and MariaDB integration testing +* [Apache Camel](https://camel.apache.org) - Testing Camel against native services such as Consul, Etcd and so on +* [Instana](https://www.instana.com) - Testing agents and stream processing backends +* [eBay Marketing](https://www.ebay.com) - Testing for MySQL, Cassandra, Redis, Couchbase, Kafka, etc. +* [Skyscanner](https://www.skyscanner.net/) - Integration testing against HTTP service mocks and various data stores +* [Neo4j-OGM](https://neo4j.com/developer/neo4j-ogm/) - Testing new, reactive client implementations ## License @@ -51,12 +81,8 @@ This project includes a modified class (ScriptUtils) taken from the Spring JDBC This project was initially inspired by a [gist](https://gist.github.com/mosheeshel/c427b43c36b256731a0b) by [Moshe Eshel](https://github.com/mosheeshel). -## Contributing - -See [CONTRIBUTING](https://github.com/testcontainers/testcontainers-java/blob/master/CONTRIBUTING.md). - ## Copyright -Copyright (c) 2015, 2016 Richard North and other authors. +Copyright (c) 2015-2019 Richard North and other authors. See [AUTHORS](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/AUTHORS) for contributors. diff --git a/docs/jitpack_dependencies.md b/docs/jitpack_dependencies.md new file mode 100644 index 00000000000..7689772de92 --- /dev/null +++ b/docs/jitpack_dependencies.md @@ -0,0 +1,24 @@ +# JitPack (unreleased versions) + +If you like to live on the bleeding edge, [jitpack.io](https://jitpack.io) can be used to obtain SNAPSHOT versions. +Use the following dependency description instead: + +```xml + + com.github.testcontainers.testcontainers-java + --artifact name-- + -SNAPSHOT + +``` + +A specific git revision (such as `093a3a4628`) can be used as a fixed version instead. The JitPack maven repository must also be declared, e.g.: + +```xml + + + jitpack.io + https://jitpack.io + + +``` + diff --git a/docs/logo.svg b/docs/logo.svg new file mode 100644 index 00000000000..3a8db707fc8 --- /dev/null +++ b/docs/logo.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/docs/modules/databases/cassandra.md b/docs/modules/databases/cassandra.md new file mode 100644 index 00000000000..0d9a52a0166 --- /dev/null +++ b/docs/modules/databases/cassandra.md @@ -0,0 +1,18 @@ +# Cassandra Module + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:cassandra:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + cassandra + {{latest_version}} + test + +``` diff --git a/docs/modules/databases/clickhouse.md b/docs/modules/databases/clickhouse.md new file mode 100644 index 00000000000..37f68c41e18 --- /dev/null +++ b/docs/modules/databases/clickhouse.md @@ -0,0 +1,22 @@ +# Clickhouse Module + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:clickhouse:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + clickhouse + {{latest_version}} + test + +``` + +!!! hint + Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency. + diff --git a/modules/couchbase/README.md b/docs/modules/databases/couchbase.md similarity index 85% rename from modules/couchbase/README.md rename to docs/modules/databases/couchbase.md index 357a21468a3..f6d014a42d4 100644 --- a/modules/couchbase/README.md +++ b/docs/modules/databases/couchbase.md @@ -1,13 +1,14 @@ +# Couchbase Module + -# Testcontainers Couchbase Module Testcontainers module for Couchbase. [Couchbase](https://www.couchbase.com/) is a document oriented NoSQL database. ## Usage example Running Couchbase as a stand-in in a test: -### Create you own bucket +### Create your own bucket ```java public class SomeTest { @@ -50,16 +51,36 @@ public class SomeTest extends AbstractCouchbaseTest { ### Special consideration -Couchbase container is configured to use random available [ports](https://developer.couchbase.com/documentation/server/current/install/install-ports.html) for some ports only, as [Couchbase Java SDK](https://developer.couchbase.com/documentation/server/current/sdk/java/start-using-sdk.html) permit to configure only some ports : +Couchbase container is configured to use random available [ports](https://developer.couchbase.com/documentation/server/current/install/install-ports.html) for some ports only, as [Couchbase Java SDK](https://developer.couchbase.com/documentation/server/current/sdk/java/start-using-sdk.html) permit to configure only some ports: + - **8091** : REST/HTTP traffic ([bootstrapHttpDirectPort](http://docs.couchbase.com/sdk-api/couchbase-java-client-2.4.6/com/couchbase/client/java/env/DefaultCouchbaseEnvironment.Builder.html#bootstrapCarrierDirectPort-int-)) - **18091** : REST/HTTP traffic with SSL ([bootstrapHttpSslPort](http://docs.couchbase.com/sdk-api/couchbase-java-client-2.4.6/com/couchbase/client/java/env/DefaultCouchbaseEnvironment.Builder.html#bootstrapCarrierSslPort-int-)) - **11210** : memcached ([bootstrapCarrierDirectPort](http://docs.couchbase.com/sdk-api/couchbase-java-client-2.4.6/com/couchbase/client/java/env/DefaultCouchbaseEnvironment.Builder.html#bootstrapCarrierDirectPort-int-)) - **11207** : memcached SSL ([bootstrapCarrierSslPort](http://docs.couchbase.com/sdk-api/couchbase-java-client-2.4.6/com/couchbase/client/java/env/DefaultCouchbaseEnvironment.Builder.html#bootstrapCarrierSslPort-int-)) -All other ports cannot be changed by Java SDK, there are sadly fixed : +All other ports cannot be changed by Java SDK, there are sadly fixed: + - **8092** : Queries, views, XDCR - **8093** : REST/HTTP Query service - **8094** : REST/HTTP Search Service - **8095** : REST/HTTP Analytic service So if you disable Query, Search and Analytic service, you can run multiple instance of this container, otherwise, you're stuck with one instance, for now. + + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:couchbase:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + couchbase + {{latest_version}} + test + +``` diff --git a/modules/dynalite/README.md b/docs/modules/databases/dynalite.md similarity index 68% rename from modules/dynalite/README.md rename to docs/modules/databases/dynalite.md index 6fd01895eb5..2d5c7f7492d 100644 --- a/modules/dynalite/README.md +++ b/docs/modules/databases/dynalite.md @@ -1,4 +1,4 @@ -# Testcontainers Dynalite Module +# Dynalite Module Testcontainers module for [Dynalite](https://github.com/mhart/dynalite). Dynalite is a clone of DynamoDB, enabling local testing. @@ -25,23 +25,22 @@ public class SomeTest { In part, because it's light and quick to run. Also, please see the [reasons given](https://github.com/mhart/dynalite#why-not-amazons-dynamodb-local) by the author of Dynalite and the [problems with Amazon's DynamoDB Local](https://github.com/mhart/dynalite#problems-with-amazons-dynamodb-local-updated-2016-04-19). -## Dependency information +## Adding this module to your project dependencies -Replace `VERSION` with the [latest version available on Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.testcontainers%22). - -### Maven +Add the following dependency to your `pom.xml`/`build.gradle` file: +```groovy tab='Gradle' +testCompile "org.testcontainers:dynalite:{{latest_version}}" ``` + +```xml tab='Maven' org.testcontainers dynalite - VERSION + {{latest_version}} + test ``` -### Gradle - -``` -compile group: 'org.testcontainers', name: 'dynalite', version: 'VERSION' -``` - +!!! hint + Adding this Testcontainers library JAR will not automatically add an AWS SDK JAR to your project. You should ensure that your project also has a suitable AWS SDK JAR as a dependency. diff --git a/docs/usage/database_containers.md b/docs/modules/databases/index.md similarity index 75% rename from docs/usage/database_containers.md rename to docs/modules/databases/index.md index f22ea97ebc2..4fd498b7248 100644 --- a/docs/usage/database_containers.md +++ b/docs/modules/databases/index.md @@ -1,26 +1,21 @@ # Database containers -## Benefits +## Overview You might want to use Testcontainers' database support: * **Instead of H2 database for DAO unit tests that depend on database features that H2 doesn't emulate.** Testcontainers is not as performant as H2, but does give you the benefit of 100% database compatibility (since it runs a real DB inside of a container). * **Instead of a database running on the local machine or in a VM** for DAO unit tests or end-to-end integration tests that need a database to be present. In this context, the benefit of Testcontainers is that the database always starts in a known state, without any contamination between test runs or on developers' local machines. -> Note: Of course, it's still important to have as few tests that hit the database as possible, and make good use of mocks for components higher up the stack. +!!! note + Of course, it's still important to have as few tests that hit the database as possible, and make good use of mocks for components higher up the stack. You can obtain a temporary database in one of two ways: * **JUnit @Rule/@ClassRule**: this mode starts a database inside a container before your tests and tears it down afterwards. * **Using a specially modified JDBC URL**: after making a very simple modification to your system's JDBC URL string, Testcontainers will provide a disposable stand-in database that can be used without requiring modification to your application code. -Testcontainers currently supports MySQL, PostgreSQL, Oracle XE and Virtuoso. - -> Note: Oracle XE support does not bundle the proprietary Oracle JDBC drivers - you must provide these yourself. - -## Examples and options - -### JUnit rule +## Database container objects Add a @Rule or @ClassRule to your test class, e.g.: @@ -42,10 +37,8 @@ Examples/Tests: * [MySQL](https://github.com/testcontainers/testcontainers-java/blob/master/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java) * [PostgreSQL](https://github.com/testcontainers/testcontainers-java/blob/master/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimplePostgreSQLTest.java) - * [Oracle-XE](https://github.com/testcontainers/testcontainers-java/blob/master/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleOracleTest.java) - * [Virtuoso](https://github.com/testcontainers/testcontainers-java/blob/master/modules/virtuoso/src/test/java/org/testcontainers/junit/SimpleVirtuosoTest.java) -### JDBC URL +## Database containers launched via JDBC URL scheme As long as you have Testcontainers and the appropriate JDBC driver on your classpath, you can simply modify regular JDBC connection URLs to get a fresh containerized instance of the database each time your application starts up. @@ -59,12 +52,6 @@ Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database ### JDBC URL examples -#### DEPRECATED: Simple Testcontainers JDBC driver usage - -`jdbc:tc:mysql://somehostname:someport/databasename` - -*(Note: this will use a fixed version of the database. You should typically specify the version you desire via a tag parameter, as below).* - #### Using Testcontainers with a fixed version `jdbc:tc:mysql:5.6.23://somehostname:someport/databasename` @@ -107,13 +94,3 @@ By default database container is being stopped as soon as last connection is clo `jdbc:tc:mysql:5.7.22://hostname/databasename?TC_DAEMON=true` With this parameter database container will keep running even when there're no open connections. - -#### Overriding MySQL my.cnf settings - -For MySQL databases, it is possible to override configuration settings using resources on the classpath. Assuming `somepath/mysql_conf_override` -is a directory on the classpath containing .cnf files, the following URL can be used: - - `jdbc:tc:mysql:5.6://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override` - -Any .cnf files in this classpath directory will be mapped into the database container's /etc/mysql/conf.d directory, -and will be able to override server settings when the container starts. diff --git a/docs/modules/databases/influxdb.md b/docs/modules/databases/influxdb.md new file mode 100644 index 00000000000..740cf69982c --- /dev/null +++ b/docs/modules/databases/influxdb.md @@ -0,0 +1,39 @@ +# InfluxDB Module + +Testcontainers module for InfluxData [InfluxDB](https://github.com/influxdata/influxdb). + +## Usage example + +Running influxDbContainer as a stand-in for InfluxDB in a test: + +```java +public class SomeTest { + + @Rule + public InfluxDBContainer influxDbContainer = new InfluxDBContainer(); + + @Test + public void someTestMethod() { + InfluxDB influxDB = influxDbContainer.getNewInfluxDB(); + ... +``` + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:influxdb:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + influxdb + {{latest_version}} + test + +``` + +!!! hint + Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency. diff --git a/docs/modules/databases/mariadb.md b/docs/modules/databases/mariadb.md new file mode 100644 index 00000000000..74a4c6e061e --- /dev/null +++ b/docs/modules/databases/mariadb.md @@ -0,0 +1,41 @@ +# MariaDB Module + +See [Database containers](./index.md) for documentation and usage that is common to all relational database container types. + +## Usage example + +Running MariaDB as a stand-in for in a test: + +```java +public class SomeTest { + + @Rule + public MariaDBContainer mariaDB = new MariaDBContainer(); + + @Test + public void someTestMethod() { + String url = mariaDB.getJdbcUrl(); + + ... create a connection and run test as normal +``` + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:mariadb:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + mariadb + {{latest_version}} + test + +``` + +!!! hint + Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency. + diff --git a/docs/modules/databases/mssqlserver.md b/docs/modules/databases/mssqlserver.md new file mode 100644 index 00000000000..9d63cee9494 --- /dev/null +++ b/docs/modules/databases/mssqlserver.md @@ -0,0 +1,54 @@ +# MS SQL Server Module + +See [Database containers](./index.md) for documentation and usage that is common to all relational database container types. + +## Usage example + +Running MS SQL Server as a stand-in for in a test: + +```java +public class SomeTest { + + @Rule + public MSSQLServerContainer mssqlserver = new MSSQLServerContainer(); + + @Test + public void someTestMethod() { + String url = mssqlserver.getJdbcUrl(); + + ... create a connection and run test as normal +``` + +!!! warning "EULA Acceptance" + Due to licencing restrictions you are required to accept an EULA for this container image. To indicate that you accept the MS SQL Server image EULA, Please place a file at the root of the classpath named `container-license-acceptance.txt`, e.g. at `src/test/resources/container-license-acceptance.txt`. This file should contain the line: `mcr.microsoft.com/mssql/server:latest` + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:mssqlserver:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + mssqlserver + {{latest_version}} + test + +``` + + +!!! hint + Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency. + +## License + +See [LICENSE](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/modules/mssqlserver/LICENSE). + +## Copyright + +Copyright (c) 2017 - 2019 G DATA Software AG and other authors. + +See [AUTHORS](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/modules/mssqlserver/AUTHORS) for contributors. diff --git a/docs/modules/databases/mysql.md b/docs/modules/databases/mysql.md new file mode 100644 index 00000000000..b32373f3363 --- /dev/null +++ b/docs/modules/databases/mysql.md @@ -0,0 +1,34 @@ +# MySQL Module + +See [Database containers](./index.md) for documentation and usage that is common to all relational database container types. + +## Overriding MySQL my.cnf settings + +For MySQL databases, it is possible to override configuration settings using resources on the classpath. Assuming `somepath/mysql_conf_override` +is a directory on the classpath containing .cnf files, the following URL can be used: + + `jdbc:tc:mysql:5.6://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override` + +Any .cnf files in this classpath directory will be mapped into the database container's /etc/mysql/conf.d directory, +and will be able to override server settings when the container starts. + + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:mysql:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + mysql + {{latest_version}} + test + +``` + +!!! hint + Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency. diff --git a/docs/usage/neo4j_container.md b/docs/modules/databases/neo4j.md similarity index 80% rename from docs/usage/neo4j_container.md rename to docs/modules/databases/neo4j.md index 5c50aedde7d..6762d01a6a8 100644 --- a/docs/usage/neo4j_container.md +++ b/docs/modules/databases/neo4j.md @@ -1,33 +1,19 @@ -# Neo4j container +# Neo4j Module This module helps running [Neo4j](https://neo4j.com/download/) using Testcontainers. Note that it's based on the [official Docker image](https://hub.docker.com/_/neo4j/) provided by Neo4j, Inc. -## Dependencies - -Add the Neo4j Testcontainer module: - -```groovy -testCompile "org.testcontainers:neo4j" -``` - -and the Neo4j Java driver if you plan to access the Testcontainer via Bolt: - -```groovy -compile "org.neo4j.driver:neo4j-java-driver:1.7.1" -``` - ## Usage example Declare your Testcontainer as a `@ClassRule` or `@Rule` in a JUnit 4 test or as static or member attribute of a JUnit 5 test annotated with `@Container` as you would with other Testcontainers. You can either use call `getHttpUrl()` or `getBoltUrl()` on the Neo4j container. -`getHttpUrl()` gives you the HTTP-address of the transactional HTTP endpoint while `getBoltUrl()` is meant to be used with one of the [official Bolt drivers](https://neo4j.com/docs/developer-manual/preview/drivers/). +`getHttpUrl()` gives you the HTTP-address of the transactional HTTP endpoint while `getBoltUrl()` is meant to be used with one of the [official Bolt drivers](https://neo4j.com/developer/language-guides/). On the JVM you would most likely use the [Java driver](https://github.com/neo4j/neo4j-java-driver). The following example uses the JUnit 5 extension `@Testcontainers` and demonstrates both the usage of the Java Driver and the REST endpoint: -```java +```java tab="JUnit 5 example" @Testcontainers public class ExampleTest { @@ -100,3 +86,40 @@ This creates a Testcontainer based on the Docker image build with the Enterprise The call to `withEnterpriseEdition` adds the required environment variable that you accepted the terms and condition of the enterprise version. You accept those by adding a file named `container-license-acceptance.txt` to the root of your classpath containing the text `neo4j:3.5.0-enterprise` in one line. You'll find more information about licensing Neo4j here: [About Neo4j Licenses](https://neo4j.com/licensing/). + + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:neo4j:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + neo4j + {{latest_version}} + test + +``` + +!!! hint + Add the Neo4j Java driver if you plan to access the Testcontainer via Bolt: + + ```groovy tab='Gradle' + compile "org.neo4j.driver:neo4j-java-driver:1.7.1" + ``` + + ```xml tab='Maven' + + org.neo4j.driver + neo4j-java-driver + 1.7.1 + + ``` + + + + diff --git a/docs/modules/databases/oraclexe.md b/docs/modules/databases/oraclexe.md new file mode 100644 index 00000000000..86a9a5f938c --- /dev/null +++ b/docs/modules/databases/oraclexe.md @@ -0,0 +1,49 @@ +# Oracle-XE Module + +See [Database containers](./index.md) for documentation and usage that is common to all relational database container types. + +## Usage example + +Running Oracle XE as a stand-in for in a test: + +```java +public class SomeTest { + + @Rule + public OracleContainer oracle = new OracleContainer("name_of_your_oracle_xe_image"); + + @Test + public void someTestMethod() { + String url = oracle.getJdbcUrl(); + + ... create a connection and run test as normal +``` + +## Specifying a docker image name via config + +If you do not pass an image name to the `OracleContainer` constructor, a suitable image name should be placed in +configuration instead. +To do this, please place a file on the classpath named `testcontainers.properties`, +containing `oracle.container.image=IMAGE`, where IMAGE is a suitable image name and tag. + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:oracle-xe:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + oracle-xe + {{latest_version}} + test + +``` + +!!! hint + Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency. + + diff --git a/docs/modules/databases/postgres.md b/docs/modules/databases/postgres.md new file mode 100644 index 00000000000..b526b1c566b --- /dev/null +++ b/docs/modules/databases/postgres.md @@ -0,0 +1,25 @@ +# Postgres Module + +See [Database containers](./index.md) for documentation and usage that is common to all relational database container types. + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:postgres:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + postgres + {{latest_version}} + test + +``` + +!!! hint + Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency. + + diff --git a/docs/usage/docker_compose.md b/docs/modules/docker_compose.md similarity index 66% rename from docs/usage/docker_compose.md rename to docs/modules/docker_compose.md index ee693fa45a4..965b6c7084a 100644 --- a/docs/usage/docker_compose.md +++ b/docs/modules/docker_compose.md @@ -1,8 +1,8 @@ -# Docker Compose +# Docker Compose Module ## Benefits -Similar to [generic containers](generic_containers.md) support, it's also possible to run a bespoke set of services +Similar to generic containers support, it's also possible to run a bespoke set of services specified in a `docker-compose.yml` file. This is intended to be useful on projects where Docker Compose is already used in dev or other environments to define @@ -108,43 +108,79 @@ public static DockerComposeContainer environment = .waitingFor("db_1", Wait.forLogMessage("started", 1)); ``` +## 'Local compose' mode + +You can override Testcontainers' default behaviour and make it use a `docker-compose` binary installed on the local machine. +This will generally yield an experience that is closer to running docker-compose locally, with the caveat that Docker Compose needs to be present on dev and CI machines. + ## Using private repositories in Docker compose When Docker Compose is used in container mode (not local), it's needs to be made aware of Docker settings for private repositories. By default, those setting are located in `$HOME/.docker/config.json`. -There are 3 ways to specify location of the `config.json` for Docker Compose +There are 3 ways to specify location of the `config.json` for Docker Compose: + * Use `DOCKER_CONFIG_FILE` environment variable. - `export DOCKER_CONFIG_FILE=/some/location/config.json` + `export DOCKER_CONFIG_FILE=/some/location/config.json` * Use `dockerConfigFile` java property `java -DdockerConfigFile=/some/location/config.json` -* Don't specify anything, in this case default location `$HOME/.docker/config.json`, if present, will be used +* Don't specify anything. In this case default location `$HOME/.docker/config.json`, if present, will be used. -####Note to OSX users -By default, Docker for mac uses Keychain to store private repositories' keys. So, your `config.json` looks like -```$json -{ - "auths" : { - "https://index.docker.io/v1/" : { +!!! note "Docker Compose and Credential Store / Credential Helpers" + Modern Docker tends to store credentials using the credential store/helper mechanism rather than storing credentials in Docker's configuration file. So, your `config.json` may look something like: + + ```json + { + "auths" : { + "https://index.docker.io/v1/" : { + } + }, + "credsStore" : "osxkeychain" } - }, - "credsStore" : "osxkeychain" -} + ``` + + When run inside a container, Docker Compose cannot access the Keychain, thus making the configuration useless. + To work around this problem, there are two options: + + ##### Putting auths in a config file + Create a `config.json` in separate location with real authentication keys, like: + + ```json + { + "auths" : { + "https://index.docker.io/v1/" : { + "auth": "QWEADSZXC..." + } + }, + "credsStore" : "osxkeychain" + } + ``` + and specify the location to Testcontainers using any of the two first methods from above. + + ##### Using 'local compose' mode + + [Local Compose mode](#local-compose-mode), mentioned above, will allow compose to directly access the Docker auth system (to the same extent that running the `docker-compose` CLI manually works). + + +## Adding this module to your project dependencies + +*Docker Compose support is part of the core Testcontainers library.* + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:testcontainers:{{latest_version}}" ``` -Docker Compose in container cannot access the Keychain, thus making the configuration useless. -To work around this problem, create `config.json` in separate location with real authentication keys, like -```$json -{ - "auths" : { - "https://index.docker.io/v1/" : { - "auth": "QWEADSZXC..." - } - }, - "credsStore" : "osxkeychain" -} +```xml tab='Maven' + + org.testcontainers + testcontainers + {{latest_version}} + test + ``` -and specify the location to Testcontainers using any of the two first methods from above. + diff --git a/docs/usage/elasticsearch_container.md b/docs/modules/elasticsearch.md similarity index 84% rename from docs/usage/elasticsearch_container.md rename to docs/modules/elasticsearch.md index 62d60fb82be..3f683e570f9 100644 --- a/docs/usage/elasticsearch_container.md +++ b/docs/modules/elasticsearch.md @@ -48,3 +48,20 @@ other advanced features), you can use this instead: // Create the elasticsearch container. ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch-oss:6.4.1"); ``` + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:elasticsearch:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + elasticsearch + {{latest_version}} + test + +``` diff --git a/docs/usage/kafka_containers.md b/docs/modules/kafka.md similarity index 82% rename from docs/usage/kafka_containers.md rename to docs/modules/kafka.md index 286a9b7cd67..c0b85b67a62 100644 --- a/docs/usage/kafka_containers.md +++ b/docs/modules/kafka.md @@ -49,3 +49,19 @@ new GenericContainer("myImage").withNetwork(kafka.getNetwork()) * Use `kafka.getNetworkAliases().get(0)+":9092"` as bootstrap server location. Or just give your Kafka container a network alias of your liking. +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:kafka:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + kafka + {{latest_version}} + test + +``` diff --git a/modules/localstack/README.md b/docs/modules/localstack.md similarity index 72% rename from modules/localstack/README.md rename to docs/modules/localstack.md index 520cb0c147c..7062b684931 100644 --- a/modules/localstack/README.md +++ b/docs/modules/localstack.md @@ -1,4 +1,4 @@ -# Testcontainers LocalStack AWS testing module +# Localstack Module Testcontainers module for the Atlassian's [LocalStack](https://github.com/localstack/localstack), 'a fully functional local AWS cloud stack'. @@ -25,21 +25,19 @@ public class SomeTest { s3.putObject("foo", "bar", "baz"); ``` -## Dependency information +## Adding this module to your project dependencies -Replace `VERSION` with the [latest version available on Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.testcontainers%22). +Add the following dependency to your `pom.xml`/`build.gradle` file: -### Maven +```groovy tab='Gradle' +testCompile "org.testcontainers:localstack:{{latest_version}}" ``` + +```xml tab='Maven' org.testcontainers localstack - VERSION + {{latest_version}} + test ``` - -### Gradle - -``` -compile group: 'org.testcontainers', name: 'localstack', version: 'VERSION' -``` diff --git a/docs/modules/mockserver.md b/docs/modules/mockserver.md new file mode 100644 index 00000000000..b6c0cd92457 --- /dev/null +++ b/docs/modules/mockserver.md @@ -0,0 +1,18 @@ +# Mockserver Module + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:mockserver:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + mockserver + {{latest_version}} + test + +``` diff --git a/docs/modules/nginx.md b/docs/modules/nginx.md new file mode 100644 index 00000000000..32d668e150a --- /dev/null +++ b/docs/modules/nginx.md @@ -0,0 +1,18 @@ +# Nginx Module + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:nginx:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + nginx + {{latest_version}} + test + +``` diff --git a/docs/modules/pulsar.md b/docs/modules/pulsar.md new file mode 100644 index 00000000000..8e6441586f8 --- /dev/null +++ b/docs/modules/pulsar.md @@ -0,0 +1,18 @@ +# Apache Pulsar Module + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:pulsar:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + pulsar + {{latest_version}} + test + +``` diff --git a/modules/vault/README.md b/docs/modules/vault.md similarity index 75% rename from modules/vault/README.md rename to docs/modules/vault.md index 9c2eb6bed7c..43d8ff57468 100644 --- a/modules/vault/README.md +++ b/docs/modules/vault.md @@ -1,4 +1,4 @@ -# TestContainers Vault Module +# Hashicorp Vault Module Testcontainers module for [Vault](https://github.com/hashicorp/vault). Vault is a tool for managing secrets. More information on Vault [here](https://www.vaultproject.io/). @@ -37,32 +37,30 @@ This can prove challenging in the development phase without a running Vault inst aims to solve your apps integration testing with Vault. You can also use it to test how your application behaves with Vault by writing different test scenarios in Junit. -## Dependency information +## Adding this module to your project dependencies -Replace `VERSION` with the [latest version available on Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.testcontainers%22). - -### Maven +Add the following dependency to your `pom.xml`/`build.gradle` file: +```groovy tab='Gradle' +testCompile "org.testcontainers:vault:{{latest_version}}" ``` + +```xml tab='Maven' org.testcontainers vault - VERSION + {{latest_version}} + test ``` -### Gradle - -``` -compile group: 'org.testcontainers', name: 'vault', version: 'VERSION' -``` - ## License -See [LICENSE](LICENSE). +See [LICENSE](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/modules/vault/LICENSE). ## Copyright Copyright (c) 2017 Capital One Services, LLC and other authors. -See [AUTHORS](AUTHORS) for contributors. +See [AUTHORS](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/modules/vault/AUTHORS) for contributors. + diff --git a/docs/usage/webdriver_containers.md b/docs/modules/webdriver_containers.md similarity index 78% rename from docs/usage/webdriver_containers.md rename to docs/modules/webdriver_containers.md index 926257ccff9..c9526ad39d4 100644 --- a/docs/usage/webdriver_containers.md +++ b/docs/modules/webdriver_containers.md @@ -92,3 +92,37 @@ Note the factory must implement `org.testcontainers.containers.RecordingFileFact ## More examples A few different examples are shown in [ChromeWebDriverContainerTest.java](https://github.com/testcontainers/testcontainers-java/blob/master/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java). + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:selenium:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + selenium + {{latest_version}} + test + +``` + +!!! hint + Adding this Testcontainers library JAR will not automatically add a Selenium Webdriver JAR to your project. You should ensure that your project also has suitable Selenium dependencies in place, for example: + + ```groovy tab='Gradle' + compile "org.seleniumhq.selenium:selenium-remote-driver:3.141.59" + ``` + + ```xml tab='Maven' + + org.seleniumhq.selenium + selenium-remote-driver + 3.141.59 + + ``` + + Testcontainers will try and match the version of the Dockerized browser to whichever version of Selenium is found on the classpath diff --git a/docs/quickstart/junit_4_quickstart.md b/docs/quickstart/junit_4_quickstart.md new file mode 100644 index 00000000000..93ca7e9a024 --- /dev/null +++ b/docs/quickstart/junit_4_quickstart.md @@ -0,0 +1,82 @@ +# JUnit 4 Quickstart + +It's easy to add Testcontainers to your project - let's walk through a quick example to see how. + +Let's imagine we have a simple program that has a dependency on Redis, and we want to add some tests for it. +In our imaginary program, there is a `RedisBackedCache` class which stores data in Redis. + +You can see an example test that could have been written for it (without using Testcontainers): + + +[Pre-Testcontainers test code](../examples/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java) block:RedisBackedCacheIntTestStep0 + + +Notice that the existing test has a problem - it's relying on a local installation of Redis, which is a red flag for test reliability. +This may work if we were sure that every developer and CI machine had Redis installed, but would fail otherwise. +We might also have problems if we attempted to run tests in parallel, such as state bleeding between tests, or port clashes. + +Let's start from here, and see how to improve the test with Testcontainers: + +## 1. Add Testcontainers as a test-scoped dependency + +First, add Testcontainers as a dependency as follows: + +```groovy tab='Gradle' +testCompile "org.testcontainers:testcontainers:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + testcontainers + {{latest_version}} + test + +``` + +## 2. Get Testcontainers to run a Redis container during our tests + +Simply add the following to the body of our test class: + + +[JUnit 4 Rule](../examples/src/test/java/quickstart/RedisBackedCacheIntTest.java) inside_block:rule + + +The `@Rule` annotation tells JUnit to notify this field about various events in the test lifecycle. +In this case, our rule object is a Testcontainers `GenericContainer`, configured to use a specific Redis image from Docker Hub, and configured to expose a port. + +If we run our test as-is, then regardless of the actual test outcome, we'll see logs showing us that Testcontainers: + +* was activated before our test method ran +* discovered and quickly tested our local Docker setup +* pulled the image if necessary +* started a new container and waited for it to be ready +* shut down and deleted the container after the test + +## 3. Make sure our code can talk to the container + +Before Testcontainers, we might have hardcoded an address like `localhost:6379` into our tests. + +Testcontainers uses *randomized ports* for each container it starts, but makes it easy to obtain the actual port at runtime. +We can do this in our test `setUp` method, to set up our component under test: + + +[Obtaining a mapped port](../examples/src/test/java/quickstart/RedisBackedCacheIntTest.java) inside_block:setUp + + +!!! tip + Notice that we also ask Testcontainers for the container's actual address with `redis.getContainerIpAddress();`, + rather than hard-coding `localhost`. `localhost` may work in some environments but not others - for example it may + not work on your current or future CI environment. As such, **avoid hard-coding** the address, and use + `getContainerIpAddress()` instead. + +## 4. Run the tests! + +That's it! + +Let's look at our complete test class to see how little we had to add to get up and running with Testcontainers: + + +[RedisBackedCacheIntTest](../examples/src/test/java/quickstart/RedisBackedCacheIntTest.java) block:RedisBackedCacheIntTest + + diff --git a/docs/styles/website.css b/docs/styles/website.css deleted file mode 100644 index 344be3c56c3..00000000000 --- a/docs/styles/website.css +++ /dev/null @@ -1,16 +0,0 @@ -.summary a.custom-link[href^="https://www.gitbook.com/"] { - display: none !important; -} - -.summary .divider:first-of-type { - display: none !important; -} - -.book .book-summary .exc-trigger:before { - content: "\f105"; -} - -.book .book-summary .expanded > a .exc-trigger:before, -.book .book-summary .expanded > span .exc-trigger:before { - content: "\f107"; -} diff --git a/docs/supported_docker_environment/continuous_integration/circle_ci.md b/docs/supported_docker_environment/continuous_integration/circle_ci.md new file mode 100644 index 00000000000..1ec2a32b4cd --- /dev/null +++ b/docs/supported_docker_environment/continuous_integration/circle_ci.md @@ -0,0 +1,19 @@ +# CircleCI 2.0 + +Your CircleCI configuration should use a dedicated VM for testcontainers to work. You can achieve this by specifying the +executor type in your `.circleci/config.yml` to be `machine` instead of the default `docker` executor ( see [Choosing an Executor Type](https://circleci.com/docs/2.0/executor-types/) for more info ). + +Here is a sample CircleCI configuration that does a checkout of a project and runs maven: + +```yml +# Check https://circleci.com/docs/2.0/language-java/ for more details +# +version: 2 +executorType: machine +jobs: + build: + steps: + - checkout + + - run: mvn -B clean install +``` diff --git a/docs/usage/inside_docker.md b/docs/supported_docker_environment/continuous_integration/dind_patterns.md similarity index 87% rename from docs/usage/inside_docker.md rename to docs/supported_docker_environment/continuous_integration/dind_patterns.md index 7dfd25c8f60..38bcd82c752 100644 --- a/docs/usage/inside_docker.md +++ b/docs/supported_docker_environment/continuous_integration/dind_patterns.md @@ -1,18 +1,18 @@ -# Running inside a Docker container +# Patterns for running tests inside a Docker container -> Since 1.1.8 +## 'Docker wormhole' pattern - Sibling docker containers Testcontainers itself can be used from inside a container. This is very useful for different CI scenarios like running everything in containers on Jenkins, or Docker-based CI tools such as Drone. Testcontainers will automatically detect if it's inside a container and instead of "localhost" will use the default gateway's IP. -However, additional configuration is required if you use [volume mapping](options.md#volume-mapping). The following points need to be considered: +However, additional configuration is required if you use [volume mapping](../../features/files.md). The following points need to be considered: * The docker socket must be available via a volume mount * The 'local' source code directory must be volume mounted *at the same path* inside the container that Testcontainers runs in, so that Testcontainers is able to set up the correct volume mounts for the containers it spawns. -## Docker-only example +### Docker-only example If you run the tests with just `docker run ...` then make sure you add `-v $PWD:$PWD -w $PWD -v /var/run/docker.sock:/var/run/docker.sock` to the command, so it will look like this: ```bash $ tree . @@ -31,7 +31,7 @@ Where: * `-w $PWD` will set the current directory to this volume * `-v /var/run/docker.sock:/var/run/docker.sock` will map the Docker socket -## Docker Compose example +### Docker Compose example The same can be achived with Docker Compose: ```yaml tests: diff --git a/docs/supported_docker_environment/continuous_integration/drone.md b/docs/supported_docker_environment/continuous_integration/drone.md new file mode 100644 index 00000000000..2549bdf4f48 --- /dev/null +++ b/docs/supported_docker_environment/continuous_integration/drone.md @@ -0,0 +1,3 @@ +# Drone CI + +> TODO diff --git a/docs/supported_docker_environment/continuous_integration/gitlab_ci.md b/docs/supported_docker_environment/continuous_integration/gitlab_ci.md new file mode 100644 index 00000000000..0cb5017bb87 --- /dev/null +++ b/docs/supported_docker_environment/continuous_integration/gitlab_ci.md @@ -0,0 +1,23 @@ +# GitLab CI + +In order to use Testcontainers in a Gitlab CI pipeline, you need to run the job as a Docker container (see [Patterns for running inside Docker](dind_patterns.md)). +So edit your `.gitlab-ci.yml` to include the [Docker-In-Docker service](https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#use-docker-in-docker-executor) (`docker:dind`) and set the `DOCKER_HOST` variable to `tcp://docker:2375`. + +Here is a sample `.gitlab-ci.yml` that executes test with gradle: + +```yml +# DinD service is required for Testcontainers +services: + - docker:dind + +variables: + # Instruct Testcontainers to use the daemon of DinD. + DOCKER_HOST: "tcp://docker:2375" + # Improve performance with overlayfs. + DOCKER_DRIVER: overlay2 + +test: + image: gradle:5.0 + stage: test + script: ./gradlew test +``` diff --git a/docs/supported_docker_environment/continuous_integration/travis.md b/docs/supported_docker_environment/continuous_integration/travis.md new file mode 100644 index 00000000000..860c07f675d --- /dev/null +++ b/docs/supported_docker_environment/continuous_integration/travis.md @@ -0,0 +1,3 @@ +# Travis + +> TODO diff --git a/docs/compatibility.md b/docs/supported_docker_environment/index.md similarity index 78% rename from docs/compatibility.md rename to docs/supported_docker_environment/index.md index ed24705b485..3477275c2f2 100644 --- a/docs/compatibility.md +++ b/docs/supported_docker_environment/index.md @@ -1,12 +1,29 @@ -# Compatibility +# General Docker requirements + +## Overview | Host Operating System / Environment | Minimum recommended docker versions | Known issues / tips | |-------------------------------------|-----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Linux - general | Docker v1.10 | | | Linux - Travis CI | Docker v1.10 | See [example .travis.yml](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/.travis.yml) for baseline Travis CI configuration | | Linux - Circle CI (LXC driver) | Docker v1.9.1 | The `exec` feature is not compatible with Circle CI. See [example circle.yml](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/circle.yml) for baseline CircleCI configuration | -| Linux - within a Docker container | Docker v1.12 | See [Running inside Docker](usage/inside_docker.md) for the detailed configuration | +| Linux - within a Docker container | Docker v1.12 | See [Running inside Docker](continuous_integration/dind_patterns.md) for Docker-in-Docker and Docker wormhole patterns | | Mac OS X - Docker Toolbox | Docker Machine v0.8.0 | | | Mac OS X - Docker for Mac | 1.12.0 | *Support is best-efforts at present*. `getTestHostIpAddress()` is [not currently supported](https://github.com/testcontainers/testcontainers-java/issues/166) due to limitations in Docker for Mac. | | Windows - Docker Toolbox | | *Support is limited at present and this is not currently tested on a regular basis*. | -| Windows - Docker for Windows | | *Support is best-efforts at present.* Only Linux Containers (LCOW) are supported at the moment. See [Windows Support](usage/windows_support.md) | +| Windows - Docker for Windows | | *Support is best-efforts at present.* Only Linux Containers (LCOW) are supported at the moment. See [Windows Support](windows.md) | + +## Docker environment discovery + +Testcontainers will try to connect to a Docker daemon using the following strategies in order: + +* Environment variables: + * `DOCKER_HOST` + * `DOCKER_TLS_VERIFY` + * `DOCKER_CERT_PATH` +* Defaults: + * `DOCKER_HOST=https://localhost:2376` + * `DOCKER_TLS_VERIFY=1` + * `DOCKER_CERT_PATH=~/.docker` +* If Docker Machine is installed, the docker machine environment for the *first* machine found. Docker Machine needs to be on the PATH for this to succeed. +* If you're going to run your tests inside a container, please read [Patterns for running tests inside a docker container](continuous_integration/dind_patterns.md) first. diff --git a/docs/supported_docker_environment/logging_config.md b/docs/supported_docker_environment/logging_config.md new file mode 100644 index 00000000000..5c7abcd211c --- /dev/null +++ b/docs/supported_docker_environment/logging_config.md @@ -0,0 +1,22 @@ +# Recommended logback configuration + +Testcontainers, and many of the libraries it uses, utilize SLF4J for logging. In order to see logs from Testcontainers, +your project should include an SLF4J implementation (Logback is recommended). The following example `logback-test.xml` +should be included in your classpath to show a reasonable level of log output: + +```xml + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + + + + + + + + + + +``` diff --git a/docs/usage/windows_support.md b/docs/supported_docker_environment/windows.md similarity index 75% rename from docs/usage/windows_support.md rename to docs/supported_docker_environment/windows.md index cd740f3776e..122965ef133 100644 --- a/docs/usage/windows_support.md +++ b/docs/supported_docker_environment/windows.md @@ -1,4 +1,4 @@ -# Windows Support (Alpha) +# Windows Support ## Prerequisites * [Docker for Windows](https://docs.docker.com/docker-for-windows/) needs to be installed @@ -17,16 +17,12 @@ If you wish to use Testcontainers on Windows, please confirm that it works corre effort. ### MySQL containers -* MySQL server prevents custom configuration file (ini-script) from being loaded due to security measures ([link to feature description](database_containers.md#using-an-init-script)) +* MySQL server prevents custom configuration file (ini-script) from being loaded due to security measures ([link to feature description](../modules/databases/index.md#using-an-init-script)) ### Windows Container on Windows (WCOW) * WCOW is currently not supported, since Testcontainers uses auxiliary Linux containers for certain tasks and Docker for Windows does not support hybrid engine mode at the time of writing. -### Docker credential helpers - -Testcontainers supports credential helpers on Linux and MacOS, to allow pulling images from private registries. This feature is disabled on Windows due to lack of testing - if it is of value to you please help us test it. See [\#756](https://github.com/testcontainers/testcontainers-java/issues/756). - ## Reporting issues Please report any issues with the Windows build of Testcontainers [here](https://github.com/testcontainers/testcontainers-java/issues) diff --git a/docs/template.html b/docs/template.html deleted file mode 100644 index fa439f2064a..00000000000 --- a/docs/template.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - {{NAME}} :: viewdocs.io - - - -
-
-

{{NAME}} :: index

-
-
- {{CONTENT}} -
-
- - - - - - diff --git a/docs/test_framework_integration/junit_4.md b/docs/test_framework_integration/junit_4.md new file mode 100644 index 00000000000..f2f46636442 --- /dev/null +++ b/docs/test_framework_integration/junit_4.md @@ -0,0 +1,46 @@ +# JUnit 4 + +## `@Rule`/`@ClassRule` integration + +**JUnit4 `@Rule`/`@ClassRule`**: This mode starts the container before your tests and tears it down afterwards. + +Add a `@Rule` or `@ClassRule` annotated field to your test class, e.g.: + +```java +public class SimpleMySQLTest { + @Rule + public MySQLContainer mysql = new MySQLContainer(); + + // [...] +} +``` + + +## Manually controlling container lifecycle + +As an alternative, you can manually start the container in a `@BeforeClass`/`@Before` annotated method in your tests. Tear down will be done automatically on JVM exit, but you can of course also use an `@AfterClass`/`@After` annotated method to manually call the `close()` method on your container. + +*Example of starting a container in a `@Before` annotated method:* + +```java +class SimpleMySQLTest { + private MySQLContainer mysql = new MySQLContainer(); + + @Before + void before() { + mysql.start(); + } + + @After + void after() { + mysql.stop(); + } + + // [...] +} +``` + +## Singleton containers + +Note that the [singleton container pattern](manual_lifecycle_control.md#singleton-containers) is also an option when +using JUnit 4. diff --git a/modules/junit-jupiter/README.md b/docs/test_framework_integration/junit_5.md similarity index 56% rename from modules/junit-jupiter/README.md rename to docs/test_framework_integration/junit_5.md index 9395f08501f..442fb485185 100644 --- a/modules/junit-jupiter/README.md +++ b/docs/test_framework_integration/junit_5.md @@ -1,4 +1,4 @@ -# TestContainers JUnit Jupiter module +# Jupiter / JUnit 5 While Testcontainers is tightly coupled with the JUnit 4.x rule API, this module provides an API that is based on the [JUnit Jupiter](https://junit.org/junit5/) extension model. @@ -8,6 +8,44 @@ The extension supports two modes: - containers that are restarted for every test method - containers that are shared between all methods of a test class +Note that Jupiter/JUnit 5 integration is packaged as a separate library JAR; see [below](#adding-testcontainers-junit-5-support-to-your-project-dependencies) for details. + +## Extension + +Jupiter integration is provided by means of the `@Testcontainers` annotation. + +The extension finds all fields that are annotated with `@Container` and calls their container lifecycle +methods (methods on the `Startable` interface). Containers declared as static fields will be shared between test +methods. They will be started only once before any test method is executed and stopped after the last test method has +executed. Containers declared as instance fields will be started and stopped for every test method. + +**Note:** This extension has only be tested with sequential test execution. Using it with parallel test execution is +unsupported and may have unintended side effects. + +*Example:* +```java +@Testcontainers +class MyTestcontainersTests { + + // will be shared between test methods + @Container + private static final MySQLContainer MY_SQL_CONTAINER = new MySQLContainer(); + + // will be started before and stopped after each test method + @Container + private PostgreSQLContainer postgresqlContainer = new PostgreSQLContainer() + .withDatabaseName("foo") + .withUsername("foo") + .withPassword("secret"); + @Test + void test() { + assertTrue(MY_SQL_CONTAINER.isRunning()); + assertTrue(postgresqlContainer.isRunning()); + } +} +``` + + ## Examples To use the Testcontainers extension annotate your test class with `@Testcontainers`. @@ -70,63 +108,32 @@ class SomeTest { } ``` -### Singleton containers +## Singleton containers -Sometimes it might be useful to define a container that is only started once for several test classes. -There is no special support for this use case provided by the Testcontainers extension. -Instead this can be implemented using the following pattern: +Note that the [singleton container pattern](manual_lifecycle_control.md#singleton-containers) is also an option when +using JUnit 5. -```java -abstract class AbstractContainerBaseTest { +## Limitations - static final MySQLContainer MY_SQL_CONTAINER; +Since this module has a dependency onto JUnit Jupiter and on Testcontainers core, which +has a dependency onto JUnit 4.x, projects using this module will end up with both, JUnit Jupiter +and JUnit 4.x in the test classpath. - static { - MY_SQL_CONTAINER = new MySQLContainer(); - MY_SQL_CONTAINER.start(); - } -} +This extension has only be tested with sequential test execution. Using it with parallel test execution is unsupported and may have unintended side effects. -class FirstTest extends AbstractContainerBaseTest { +## Adding Testcontainers JUnit 5 support to your project dependencies - @Test - void someTestMethod() { - String url = MY_SQL_CONTAINER.getJdbcUrl(); +Add the following dependency to your `pom.xml`/`build.gradle` file: - // create a connection and run test as normal - } -} +```groovy tab='Gradle' +testCompile "org.testcontainers:junit-jupiter:{{latest_version}}" ``` -The singleton container is started only once when the base class is loaded. -The container can then be used by all inheriting test classes. -At the end of the test suite the [Ryuk container](https://github.com/testcontainers/moby-ryuk) -that is started by Testcontainers core will take care of stopping the singleton container. - -## Dependency information - -Replace `VERSION` with the [latest version available on Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.testcontainers%22) or use the Testcontainers BOM. - -### Maven - -``` +```xml tab='Maven' org.testcontainers junit-jupiter - VERSION + {{latest_version}} + test ``` - -### Gradle - -``` -compile group: 'org.testcontainers', name: 'junit-jupiter', version: 'VERSION' -``` - -## Limitations - -Since this module has a dependency onto JUnit Jupiter and on Testcontainers core, which -has a dependency onto JUnit 4.x, projects using this module will end up with both, JUnit Jupiter -and JUnit 4.x in the test classpath. - -This extension has only be tested with sequential test execution. Using it with parallel test execution is unsupported and may have unintended side effects. diff --git a/docs/test_framework_integration/manual_lifecycle_control.md b/docs/test_framework_integration/manual_lifecycle_control.md new file mode 100644 index 00000000000..17e478c1d16 --- /dev/null +++ b/docs/test_framework_integration/manual_lifecycle_control.md @@ -0,0 +1,50 @@ +# Manual container lifecycle control + +While Testcontainers was originally built with JUnit 4 integration in mind, it is fully usable with other test +frameworks, or no with framework at all. + +## Manually starting/stopping containers + +Containers can be started and stopped in code using `start()` and `stop()` methods. Additionally, container classes +implement `AutoCloseable`. This enables better assurance that the container will be stopped at the appropriate time. + +```java +try (GenericContainer container = new GenericContainer("imagename")) { + container.start(); + // ... use the container + // no need to call stop() afterwards +} +``` + +## Singleton containers + +Sometimes it might be useful to define a container that is only started once for several test classes. +There is no special support for this use case provided by the Testcontainers extension. +Instead this can be implemented using the following pattern: + +```java +abstract class AbstractContainerBaseTest { + + static final MySQLContainer MY_SQL_CONTAINER; + + static { + MY_SQL_CONTAINER = new MySQLContainer(); + MY_SQL_CONTAINER.start(); + } +} + +class FirstTest extends AbstractContainerBaseTest { + + @Test + void someTestMethod() { + String url = MY_SQL_CONTAINER.getJdbcUrl(); + + // create a connection and run test as normal + } +} +``` + +The singleton container is started only once when the base class is loaded. +The container can then be used by all inheriting test classes. +At the end of the test suite the [Ryuk container](https://github.com/testcontainers/moby-ryuk) +that is started by Testcontainers core will take care of stopping the singleton container. diff --git a/modules/spock/README.md b/docs/test_framework_integration/spock.md similarity index 73% rename from modules/spock/README.md rename to docs/test_framework_integration/spock.md index 0baae0e9c91..bbe06a43e80 100644 --- a/modules/spock/README.md +++ b/docs/test_framework_integration/spock.md @@ -1,9 +1,10 @@ -# TestContainers-Spock -[Spock](https://github.com/spockframework/spock) extension for [TestContainers](https://github.com/testcontainers/testcontainers-java) library, which allows to use Docker containers inside of Spock tests. +# Spock -# Usage +[Spock](https://github.com/spockframework/spock) extension for [Testcontainers](https://github.com/testcontainers/testcontainers-java) library, which allows to use Docker containers inside of Spock tests. -## @Testcontainers class-annotation +## Usage + +### `@Testcontainers` class-annotation Specifying the `@Testcontainers` annotation will instruct Spock to start and stop all testcontainers accordingly. This annotation can be mixed with Spock's `@Shared` annotation to indicate, that containers shouldn't be restarted between tests. @@ -40,9 +41,23 @@ class DatabaseTest extends Specification { } ``` -## General TestContainers usage +## Adding Testcontainers Spock support to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:spock:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + spock + {{latest_version}} + test + +``` -See the [TestContainers documentation](https://www.testcontainers.org/) for more information about the underlying library. -# Attributions +## Attributions The initial version of this project was heavily inspired by the excellent [JUnit5 docker extension](https://github.com/FaustXVI/junit5-docker) by [FaustXVI](https://github.com/FaustXVI). diff --git a/docs/usage.md b/docs/usage.md deleted file mode 100644 index 65d29c07ecf..00000000000 --- a/docs/usage.md +++ /dev/null @@ -1,217 +0,0 @@ -# Usage - -### Prerequisites - -Docker or docker-machine (for OS X) must be installed on the machine you are running tests on. Testcontainers currently requires JDK 1.8 and is compatible with JUnit. - -If you want to use Testcontainers on Windows you can try the [alpha release](usage/windows_support.md). - -### Docker environment discovery - -Testcontainers will try to connect to a Docker daemon using the following strategies in order: - -* Environment variables: - * `DOCKER_HOST` - * `DOCKER_TLS_VERIFY` - * `DOCKER_CERT_PATH` -* Defaults: - * `DOCKER_HOST=https://localhost:2376` - * `DOCKER_TLS_VERIFY=1` - * `DOCKER_CERT_PATH=~/.docker` -* If Docker Machine is installed, the docker machine environment for the *first* machine found. Docker Machine needs to be on the PATH for this to succeed. -* If you're going to run your tests inside a container, please read [Running inside a Docker container](usage/inside_docker.md) first. - -### Usage modes - -* [Temporary database containers](usage/database_containers.md) - specialized Microsoft SQL Server, MariaDB, MySQL, PostgreSQL, Oracle XE and Virtuoso container support -* [Elasticsearch container](usage/elasticsearch_container.md) - Elasticsearch container support -* [Webdriver containers](usage/webdriver_containers.md) - run a dockerized Chrome or Firefox browser ready for Selenium/Webdriver operations - complete with automatic video recording -* [Kafka containers](usage/kafka_containers.md) - run a dockerized Kafka, a distributed streaming platform -* [Neo4j container](usage/neo4j_container.md) - Neo4j container support -* [Generic containers](usage/generic_containers.md) - run any Docker container as a test dependency -* [Docker compose](usage/docker_compose.md) - reuse services defined in a Docker Compose YAML file -* [Dockerfile containers](usage/dockerfile.md) - run a container that is built on-the-fly from a Dockerfile - -## Gradle/Maven dependencies - -Testcontainers is distributed in a handful of Gradle/Maven modules: - -* **testcontainers** for just core functionality, generic containers and docker-compose support -* **mysql**, **mariadb**, **postgresql**, **mssqlserver** or **oracle-xe** for database container support -* **elasticsearch** for elasticsearch container support -* **selenium** for selenium/webdriver support -* **nginx** for nginx container support -* and many more! - -In the dependency description below, replace `--artifact name--` as appropriate and `--latest version--` with the [latest version available on Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.testcontainers%22): - -Maven style: - -```xml - - org.testcontainers - --artifact name-- - --latest version-- - test - -``` - -Gradle style: - -``` -testImplementation group: 'org.testcontainers', name: '--artifact name--', version: '--latest version--' -``` - - -### JitPack (unreleased versions) - -Alternatively, if you like to live on the bleeding edge, jitpack.io can be used to obtain SNAPSHOT versions. -Use the following dependency description instead: - - - com.github.testcontainers.testcontainers-java - --artifact name-- - -SNAPSHOT - test - - -A specific git revision (such as `093a3a4628`) can be used as a fixed version instead. The JitPack maven repository must also be declared, e.g.: - - - - jitpack.io - https://jitpack.io - - - -The [testcontainers examples project](https://github.com/testcontainers/testcontainers-java-examples) uses JitPack to fetch the latest, master version. - -### Shaded dependencies - -**Note**: Testcontainers uses the docker-java client library, which in turn depends on JAX-RS, Jersey and Jackson -libraries. These libraries in particular seem to be especially prone to conflicts with test code/applciation under test - code. As such, **these libraries are 'shaded' into the core testcontainers JAR** and relocated - under `org.testcontainers.shaded` to prevent class conflicts. - -## Logging - -Testcontainers, and many of the libraries it uses, utilize slf4j for logging. In order to see logs from Testcontainers, -your project should include an SLF4J implementation (Logback is recommended). The following example `logback-test.xml` -should be included in your classpath to show a reasonable level of log output: - -```xml - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n - - - - - - - - - - - - -``` - -## Using custom utility images - -Testcontainers uses additional docker images under some modes of execution: - -* `richnorth/ambassador:latest`, which is a TCP proxy used to expose Docker Compose container ports outside of the compose network. -* `richnorth/vncrecorder:latest`, which is a VNC->FLV recorder, used for capturing Webdriver test videos. - -> *N.B.:* both these images use the 'latest' tag, which could potentially affect repeatability of tests and compatibility with Testcontainers _if the image is ever changed_. This is a [known issue](https://github.com/testcontainers/testcontainers-java/issues/276) which will be addressed in the future. The current 'latest' version of these images will never be changed until they are replaced by a new image altogether. - -Last but not least, `alpine:3.2` image is used for Docker host IP address detection in some special cases. - -If it is necessary to override these image names (e.g. when using a private registry), you should create a file named `testcontainers.properties` and place it on the classpath with the following content: - -```properties -ambassador.container.image=replacement image name here -vncrecorder.container.image=replacement image name here -tinyimage.container.image=replacement image name here -``` - -## JUnit - -### Junit 4 - -**JUnit4 `@Rule`/`@ClassRule`**: This mode starts the container before your tests and tears it down afterwards. - -Add a `@Rule` or `@ClassRule` annotated field to your test class, e.g.: - -```java -public class SimpleMySQLTest { - @Rule - public MySQLContainer mysql = new MySQLContainer(); - - // [...] -} -``` - -### Jupiter / JUnit 5 - -#### Extension - -Jupiter integration is provided by means of the `@Testcontainers` annotation. - -The extension finds all fields that are annotated with `@Container` and calls their container lifecycle -methods (methods on the `Startable` interface). Containers declared as static fields will be shared between test -methods. They will be started only once before any test method is executed and stopped after the last test method has -executed. Containers declared as instance fields will be started and stopped for every test method. - -**Note:** This extension has only be tested with sequential test execution. Using it with parallel test execution is -unsupported and may have unintended side effects. - -*Example:* -```java -@Testcontainers -class MyTestcontainersTests { - - // will be shared between test methods - @Container - private static final MySQLContainer MY_SQL_CONTAINER = new MySQLContainer(); - - // will be started before and stopped after each test method - @Container - private PostgreSQLContainer postgresqlContainer = new PostgreSQLContainer() - .withDatabaseName("foo") - .withUsername("foo") - .withPassword("secret"); - @Test - void test() { - assertTrue(MY_SQL_CONTAINER.isRunning()); - assertTrue(postgresqlContainer.isRunning()); - } -} -``` - -#### Vanilla Integration - -As an alternative, you can manually start the container in a `@BeforeAll`/`@BeforeEach` annotated method in your tests. Tear down will be done automatically on JVM exit, but you can of course also use an `@AfterAll`/`@AfterEach` annotated method to manually call the `close()` method on your container. - -*Example of starting a container in a `@BeforeEach` annotated method:* - -```java -class SimpleMySQLTest { - private MySQLContainer mysql = new MySQLContainer(); - - @BeforeEach - void before() { - mysql.start(); - } - - @AfterEach - void after() { - mysql.stop(); - } - - // [...] -} -``` - diff --git a/docs/usage/generic_containers.md b/docs/usage/generic_containers.md deleted file mode 100644 index f848abb1414..00000000000 --- a/docs/usage/generic_containers.md +++ /dev/null @@ -1,49 +0,0 @@ -# Generic containers - -## Benefits - -Testcontainers' generic container support offers the most flexibility, and makes it easy to use virtually any container -images as temporary test dependencies. For example, if you might use it to test interactions with: - -* NoSQL databases or other data stores (e.g. redis, elasticsearch, mongo) -* Web servers/proxies (e.g. nginx, apache) -* Log services (e.g. logstash, kibana) -* Other services developed by your team/organization which are already dockerized - -## Example - -A generic container rule can be used with any public docker image; for example: -```java -// Set up a redis container -@ClassRule -public static GenericContainer redis = - new GenericContainer("redis:3.0.2") - .withExposedPorts(6379); - - -// Set up a plain OS container and customize environment, -// command and exposed ports. This just listens on port 80 -// and always returns '42' -@ClassRule -public static GenericContainer alpine = - new GenericContainer("alpine:3.2") - .withExposedPorts(80) - .withEnv("MAGIC_NUMBER", "42") - .withCommand("/bin/sh", "-c", - "while true; do echo \"$MAGIC_NUMBER\" | nc -l -p 80; done"); -``` - -These containers, as `@ClassRule`s, will be started before any tests in the class run, and will be destroyed after all -tests have run. - -## Accessing a container from tests - -The class rule provides methods for discovering how your tests can interact with the containers: - -* `getContainerIpAddress()` returns the IP address where the container is listening -* `getMappedPort(...)` returns the Docker mapped port for a port that has been exposed on the container - -For example, with the Redis example above, the following will allow your tests to access the Redis service: -```java -String redisUrl = redis.getContainerIpAddress() + ":" + redis.getMappedPort(6379); -``` diff --git a/docs/usage/modules.md b/docs/usage/modules.md deleted file mode 100644 index 788c5f561b2..00000000000 --- a/docs/usage/modules.md +++ /dev/null @@ -1,10 +0,0 @@ -# Contributed modules - -The following modules are available for specialised use cases: - -* [MS SQL Server](../../modules/mssqlserver/README.md) - thank you to G DATA Software AG for contributing this module. -* [Oracle XE](../../modules/oracle-xe/README.md) -* [Dynalite](../../modules/dynalite/README.md) (Amazon DynamoDB emulation) -* [Atlassian Localstack](../../modules/localstack/README.md) (emulation of a broad set of various AWS services) -* [MariaDB](../../modules/mariadb/README.md) -* [Hashicorp Vault](../../modules/vault/README.md) - thank you to Capital One Services, LLC for contributing this module. diff --git a/docs/usage/options.md b/docs/usage/options.md deleted file mode 100644 index deaf429816d..00000000000 --- a/docs/usage/options.md +++ /dev/null @@ -1,217 +0,0 @@ -# Options - -### Image - -With a generic container, you set the container image using a parameter to the rule constructor, e.g.: -```java -new GenericContainer("jboss/wildfly:9.0.1.Final") -``` - -### Exposing ports - -If you need to expose ports on the container, use the `withExposedPorts` method on the rule passing integer port numbers: -```java -new GenericContainer(...) - .withExposedPorts(22, 80, 8080) -``` - -or strings (optionally specifying tcp): -```java -new GenericContainer(...) - .withExposedPorts("22", "80/tcp", "8080/tcp") -``` - -### Environment variables - -To add environment variables to the container, use `withEnv`: -```java -new GenericContainer(...) - .withEnv("API_TOKEN", "foo") -``` - -### Labels - -To add a custom label to the container, use `withLabel`: -```java -new GenericContainer(...) - .withLabel("your.custom", "label") -``` - -### Command - -By default the container will execute whatever command is specified in the image's Dockerfile. To override this, and specify a different command, use `withCommand`: -```java -new GenericContainer(...) - .withCommand("/app/start.sh") -``` - -### Volume mapping - -It is possible to map a file or directory **on the classpath** into the container as a volume using `withClasspathResourceMapping`: -```java -new GenericContainer(...) - .withClasspathResourceMapping("redis.conf", - "/etc/redis.conf", - BindMode.READ_ONLY) -``` - -### Customizing the container - -It is possible to use the [`docker-java`](https://github.com/docker-java/docker-java) API directly to customize containers before creation. This is useful if there is a need to use advanced Docker features that are not exposed by the Testcontainers API. Any customizations you make using `withCreateContainerCmdModifier` will be applied _on top_ of the container definition that Testcontainers creates, but before it is created. - -For example, this can be used to change the container hostname: -```java -new GenericContainer<>("redis:3.0.2") - .withCreateContainerCmdModifier(cmd -> cmd.withHostName("the-cache")) - ... -``` - -... or modify container memory (see [this](https://fabiokung.com/2014/03/13/memory-inside-linux-containers/) if it does not appear to work): -```java -new GenericContainer<>("alpine:3.3") - .withCreateContainerCmdModifier(cmd -> cmd.withMemory((long) 4 * 1024 * 1024)) - .withCreateContainerCmdModifier(cmd -> cmd.withMemorySwap((long) 4 * 1024 * 1024)) - ... -``` - -> It is recommended to use this sparingly, and follow changes to the `docker-java` API if you choose to use this. It is typically quite stable, though. - -For what is possible, consult the [`docker-java CreateContainerCmd` source code](https://github.com/docker-java/docker-java/blob/master/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java) - - - - -### Startup timeout - -Ordinarily Testcontainers will wait for up to 60 seconds for the container's first mapped network port to start listening. - -This simple measure provides a basic check whether a container is ready for use. - -If the default 60s timeout is not sufficient, it can be altered with the `withStartupTimeout()` method. - -If waiting for a listening TCP port is not sufficient to establish whether the container is ready, you can use the -`waitingFor()` method with other `WaitStrategy` implementations as shown below. - -#### Waiting for startup examples - -You can choose to wait for an HTTP(S) endpoint to return a particular status code. - -Waiting for 200 OK: -```java -@ClassRule -public static GenericContainer elasticsearch = - new GenericContainer("elasticsearch:2.3") - .withExposedPorts(9200) - .waitingFor(Wait.forHttp("/all")); -``` - -Wait for 200 or 401 status codes on an HTTPS endpoint: -```java -@ClassRule -public static GenericContainer elasticsearch = - new GenericContainer("elasticsearch:2.3") - .withExposedPorts(9200) - .waitingFor( - Wait.forHttp("/all") - .forStatusCode(200) - .forStatusCode(401) - .usingTls()); -``` - -Wait for 200...299 or 401 status codes on an HTTPS endpoint: -```java -@ClassRule -public static GenericContainer elasticsearch = - new GenericContainer("elasticsearch:2.3") - .withExposedPorts(9200) - .waitingFor( - Wait.forHttp("/all") - .forStatusCodeMatching(it -> it >= 200 && it < 300 || it == 401) - .usingTls()); -``` - -If the used image supports Docker's [Healthcheck](https://docs.docker.com/engine/reference/builder/#healthcheck) feature, you can directly leverage the `healthy` state of the container as your wait condition: -```java -@ClassRule -public static GenericContainer container = - new GenericContainer("image-with-healthcheck:4.2") - .waitingFor(Wait.forHealthcheck()); -``` - -For futher options, check out the `Wait` convenience class, or the various subclasses of `WaitStrategy`. If none of these options -meet your requirements, you can create your own subclass of `AbstractWaitStrategy` with an appropriate wait -mechanism in `waitUntilReady()`. The `GenericContainer.waitingFor()` method accepts any valid `WaitStrategy`. - -### Following container output - -It is possible to capture container output using the `followOutput()` method. This method accepts a Consumer and (optionally) -a varargs list stating which of STDOUT, STDERR, or both, should be followed. If not specified, both will be followed. - -At present, container output will always begin from the time of container creation. - -Testcontainers includes some out-of-the-box Consumer implementations that can be used; examples follow. - -#### Streaming container output to an SLF4J logger - -Given an existing SLF4J logger instance named LOGGER: -```java -Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(LOGGER); -container.followOutput(logConsumer); -``` - -#### Capturing container output as a String -```java -ToStringConsumer toStringConsumer = new ToStringConsumer(); -container.followOutput(toStringConsumer, OutputType.STDOUT); - -String utf8String = toStringConsumer.toUtf8String(); - -// Or if the container output is not UTF-8 -String otherString = toStringConsumer.toString(CharSet.forName("ISO-8859-1")); -``` - -#### Waiting for container output to contain expected content - -`WaitingConsumer` will block until a frame of container output (usually a line) matches a provided predicate. - -A timeout may be specified, as shown in this example. -```java -WaitingConsumer consumer = new WaitingConsumer(); - -container.followOutput(consumer, STDOUT); - -consumer.waitUntil(frame -> - frame.getUtf8String().contains("STARTED"), 30, TimeUnit.SECONDS); -``` - -Additionally, as the Java 8 Consumer functional interface is used, Consumers may be composed together. This is -useful, for example, to capture all the container output but only when a matching string has been found. e.g.: -```java -WaitingConsumer waitingConsumer = new WaitingConsumer(); -ToStringConsumer toStringConsumer = new ToStringConsumer(); - -Consumer composedConsumer = toStringConsumer.andThen(waitingConsumer); -container.followOutput(composedConsumer); - -waitingConsumer.waitUntil(frame -> - frame.getUtf8String().contains("STARTED"), 30, TimeUnit.SECONDS); - -String utf8String = toStringConsumer.toUtf8String(); -``` - -### Executing a command - -Your test can execute a command inside a running container, similar to a `docker exec` call: -```java -myContainer.execInContainer("touch", "/tmp/foo"); -``` - -This can be useful for software that has a command line administration tool. You can also get the output from the command: -```java -ExecResult result = myContainer.execInContainer("tail", "-1", "/var/logs/foo"); -assertThat(result.getStdout().contains("message")); -``` - -There are two limitations: -* There's no way to get the return code of the executed command -* This isn't supported if your docker daemon uses the older "lxc" execution engine. diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index a5fe1cb94b9ee5ce57e6113458225bcba12d83e3..29953ea141f55e3b8fc691d31b5ca8816d89fa87 100644 GIT binary patch delta 49492 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 RFPk2)@CprFU(Y|X{|C|a-r@iN diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 019065d1d65..d6e4e61c358 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,3 +1,4 @@ +#Wed Jan 16 08:01:33 GMT 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 00000000000..4570bbf7735 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,79 @@ +site_name: Testcontainers +plugins: + - search + - codeinclude + - markdownextradata +theme: + name: 'material' + palette: + primary: 'blue' + accent: 'teal' + font: false + logo: 'logo.svg' + favicon: 'favicon.ico' +extra_css: + - 'css/extra.css' +repo_name: 'testcontainers-java' +repo_url: 'https://github.com/testcontainers/testcontainers-java' +markdown_extensions: + - admonition + - codehilite: + linenums: False + - pymdownx.superfences + - pymdownx.snippets +nav: + - Home: index.md + - Quickstart: + - quickstart/junit_4_quickstart.md + - Features: + - features/creating_container.md + - features/networking.md + - features/commands.md + - features/files.md + - features/startup_and_waits.md + - features/container_logs.md + - features/creating_images.md + - features/configuration.md + - features/advanced_options.md + - Modules: + - Databases: + - modules/databases/index.md + - modules/databases/mysql.md + - modules/databases/mariadb.md + - modules/databases/postgres.md + - modules/databases/oraclexe.md + - modules/databases/mssqlserver.md + - modules/databases/cassandra.md + - modules/databases/couchbase.md + - modules/databases/clickhouse.md + - modules/databases/influxdb.md + - modules/databases/dynalite.md + - modules/databases/neo4j.md + - modules/webdriver_containers.md + - modules/docker_compose.md + - modules/elasticsearch.md + - modules/kafka.md + - modules/pulsar.md + - modules/localstack.md + - modules/mockserver.md + - modules/nginx.md + - modules/vault.md + - Test framework integration: + - test_framework_integration/junit_4.md + - test_framework_integration/junit_5.md + - test_framework_integration/spock.md + - test_framework_integration/manual_lifecycle_control.md + - System Requirements: + - supported_docker_environment/index.md + - Continuous Integration: + - supported_docker_environment/continuous_integration/dind_patterns.md + - supported_docker_environment/continuous_integration/circle_ci.md + - supported_docker_environment/continuous_integration/gitlab_ci.md + - supported_docker_environment/windows.md + - supported_docker_environment/logging_config.md + - Getting help: getting_help.md + - Contributing: + - contributing.md + - contributing_docs.md +extra: + latest_version: 1.10.5 diff --git a/modules/influxdb/README.md b/modules/influxdb/README.md deleted file mode 100644 index f86ea2ccd86..00000000000 --- a/modules/influxdb/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# Testcontainers InfluxDB testing module - -Testcontainers module for the InfluxData [InfluxDB](https://github.com/influxdata/influxdb). - -## Usage example - -Running influxDbContainer as a stand-in for InfluxDB in a test: - -```java -public class SomeTest { - - @Rule - public InfluxDBContainer influxDbContainer = new InfluxDBContainer(); - - @Test - public void someTestMethod() { - InfluxDB influxDB = influxDbContainer.getNewInfluxDB(); - ... -``` - -## Dependency information - -Replace `VERSION` with the [latest version available on Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.testcontainers%22). - -[![](https://api.bintray.com/packages/testcontainers/releases/testcontainers/images/download.svg)](https://bintray.com/testcontainers/releases/testcontainers/_latestVersion) - - -### Maven -``` - - org.testcontainers - influxdb - VERSION - -``` - -### Gradle - -``` -compile group: 'org.testcontainers', name: 'influxdb', version: 'VERSION' -``` diff --git a/modules/mariadb/README.md b/modules/mariadb/README.md deleted file mode 100644 index 20649b03582..00000000000 --- a/modules/mariadb/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# Testcontainers MariaDB Module - -Testcontainers module for the MariaDB database. - -## Usage example - -Running MariaDB as a stand-in for in a test: - -```java -public class SomeTest { - - @Rule - public MariaDBContainer mariaDB = new MariaDBContainer(); - - @Test - public void someTestMethod() { - String url = mariaDB.getJdbcUrl(); - - ... create a connection and run test as normal -``` - -## Dependency information - -Replace `VERSION` with the [latest version available on Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.testcontainers%22). - -### Maven - -``` - - org.testcontainers - mariadb - VERSION - -``` - -### Gradle - -``` -compile group: 'org.testcontainers', name: 'mariadb', version: 'VERSION' -``` diff --git a/modules/mssqlserver/README.md b/modules/mssqlserver/README.md deleted file mode 100644 index 86e187002d2..00000000000 --- a/modules/mssqlserver/README.md +++ /dev/null @@ -1,52 +0,0 @@ -# TestContainers MS SQL Server Module - -Testcontainers module for the MS SQL Server database. - -## Usage example - -Running MS SQL Server as a stand-in for in a test: - -```java -public class SomeTest { - - @Rule - public MSSQLServerContainer mssqlserver = new MSSQLServerContainer(); - - @Test - public void someTestMethod() { - String url = mssqlserver.getJdbcUrl(); - - ... create a connection and run test as normal -``` - -> *Note:* Due to licencing restrictions you are required to accept an EULA for this container image. To indicate that you accept the MS SQL Server image EULA, Please place a file at the root of the classpath named `container-license-acceptance.txt`, e.g. at `src/test/resources/container-license-acceptance.txt`. This file should contain the line: `mcr.microsoft.com/mssql/server:latest` - -## Dependency information - -Replace `VERSION` with the [latest version available on Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.testcontainers%22). - -### Maven - -``` - - org.testcontainers - mssqlserver - VERSION - -``` - -### Gradle - -``` -compile group: 'org.testcontainers', name: 'mssqlserver', version: 'VERSION' -``` - -## License - -See [LICENSE](LICENSE). - -## Copyright - -Copyright (c) 2017 - 2019 G DATA Software AG and other authors. - -See [AUTHORS](AUTHORS) for contributors. diff --git a/modules/oracle-xe/README.md b/modules/oracle-xe/README.md deleted file mode 100644 index 38def171fab..00000000000 --- a/modules/oracle-xe/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# Testcontainers Oracle XE Module - -Testcontainers module for the Oracle XE database. - -## Usage example - -Running Oracle XE as a stand-in for in a test: - -```java -public class SomeTest { - - @Rule - public OracleContainer oracle = new OracleContainer(); - - @Test - public void someTestMethod() { - String url = oracle.getJdbcUrl(); - - ... create a connection and run test as normal -``` - -## Dependency information - -Replace `VERSION` with the [latest version available on Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.testcontainers%22). - -### Maven - -``` - - org.testcontainers - oracle-xe - VERSION - -``` - -### Gradle - -``` -compile group: 'org.testcontainers', name: 'oracle-xe', version: 'VERSION' -``` diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000000..a936d8a0a95 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +mkdocs +-e git+https://github.com/rnorth/mkdocs-codeinclude-plugin#egg=mkdocs_codeinclude_plugin +mkdocs-material +mkdocs-markdownextradata-plugin diff --git a/runtime.txt b/runtime.txt new file mode 100644 index 00000000000..d70c8f8d89f --- /dev/null +++ b/runtime.txt @@ -0,0 +1 @@ +3.6 diff --git a/settings.gradle b/settings.gradle index 301b8eea057..dbbd0b6a25c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -35,4 +35,7 @@ project(':testcontainers').projectDir = "$rootDir/core" as File file('modules').eachDir { dir -> include dir.name project(":${dir.name}").projectDir = dir -} \ No newline at end of file +} + +include "docs-examples" +project(":docs-examples").projectDir = file("docs/examples") From c10fa3f4d8684562674b738ae9d631322956f1ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 22 Jan 2019 21:13:38 +0100 Subject: [PATCH 0570/5615] Bump mysql-connector-java from 8.0.13 to 8.0.14 in /modules/spock (#1173) Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.13 to 8.0.14. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.13...8.0.14) Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 3d097e5c5ca..206437e8b66 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -13,5 +13,5 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.3.0' testRuntime 'org.postgresql:postgresql:42.2.5' - testRuntime 'mysql:mysql-connector-java:8.0.13' + testRuntime 'mysql:mysql-connector-java:8.0.14' } \ No newline at end of file From 1c4f804942c8ae6d59422135496cacaf939d0c0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 22 Jan 2019 21:13:54 +0100 Subject: [PATCH 0571/5615] Bump mysql-connector-java in /modules/junit-jupiter (#1172) Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.13 to 8.0.14. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.13...8.0.14) Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index d2f761cdf59..3ccb89a3543 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -10,7 +10,7 @@ dependencies { testCompile 'redis.clients:jedis:3.0.1' testRuntime 'org.postgresql:postgresql:42.2.5' - testRuntime 'mysql:mysql-connector-java:8.0.13' + testRuntime 'mysql:mysql-connector-java:8.0.14' testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.3.2' } From 97e88ddd4e5c0467811511def09c45e7ec1179a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 22 Jan 2019 21:14:39 +0100 Subject: [PATCH 0572/5615] Bump mysql-connector-java from 8.0.13 to 8.0.14 in /modules/mysql (#1171) Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.13 to 8.0.14. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.13...8.0.14) Signed-off-by: dependabot[bot] --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 2f37cc9f795..645212ec49d 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: JDBC :: MySQL" dependencies { compile project(':jdbc') - testCompile 'mysql:mysql-connector-java:8.0.13' + testCompile 'mysql:mysql-connector-java:8.0.14' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.14' testCompile 'commons-dbutils:commons-dbutils:1.7' From d1c029262aa7e2062241e80c484d6aa118c87743 Mon Sep 17 00:00:00 2001 From: Artjom Kalita Date: Thu, 24 Jan 2019 11:51:44 +0200 Subject: [PATCH 0573/5615] modernize Spring boot example (#1175) * Use static approach for configuring redis container * Adds postgresql test to example * Use jdbc url scheme to startup postresql database * Cleanup + moving test specific configuration from main config * Removes unnecessary test config file, fixes annotation formatting * Removes default JDBC parameters, polishes test result message * Update application.yml * Update AbstractIntegrationTest.java * Update DemoControllerTest.java --- examples/pom.xml | 17 +++------- examples/spring-boot/pom.xml | 10 +++++- .../java/com/example/DemoApplication.java | 2 ++ .../main/java/com/example/DemoController.java | 9 +++++- .../src/main/java/com/example/DemoEntity.java | 21 ++++++++++++ .../main/java/com/example/DemoRepository.java | 7 ++++ .../main/java/com/example/DemoService.java | 18 +++++++++++ .../src/main/resources/application.yml | 11 +++++++ .../com/example/AbstractIntegrationTest.java | 32 ++++++++----------- .../java/com/example/DemoControllerTest.java | 19 +++++++++-- 10 files changed, 111 insertions(+), 35 deletions(-) create mode 100644 examples/spring-boot/src/main/java/com/example/DemoEntity.java create mode 100644 examples/spring-boot/src/main/java/com/example/DemoRepository.java create mode 100644 examples/spring-boot/src/main/java/com/example/DemoService.java create mode 100644 examples/spring-boot/src/main/resources/application.yml diff --git a/examples/pom.xml b/examples/pom.xml index bb48db6100c..5c1a7c1ef0e 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -51,6 +51,11 @@ 1.10.19 test
+ + org.postgresql + postgresql + 42.2.5 + @@ -72,18 +77,6 @@ -SNAPSHOT - - - - - Windows - - - - windows-support-SNAPSHOT - - - diff --git a/examples/spring-boot/pom.xml b/examples/spring-boot/pom.xml index fb882b500a3..d534782a1af 100644 --- a/examples/spring-boot/pom.xml +++ b/examples/spring-boot/pom.xml @@ -37,12 +37,20 @@ org.springframework.boot spring-boot-starter-web
- + + org.springframework.boot + spring-boot-starter-data-jpa + org.springframework.boot spring-boot-starter-test test + + ${testcontainers.group} + postgresql + ${testcontainers.version} + diff --git a/examples/spring-boot/src/main/java/com/example/DemoApplication.java b/examples/spring-boot/src/main/java/com/example/DemoApplication.java index ad5a9d9fed3..c968eb42b78 100644 --- a/examples/spring-boot/src/main/java/com/example/DemoApplication.java +++ b/examples/spring-boot/src/main/java/com/example/DemoApplication.java @@ -2,8 +2,10 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; @SpringBootApplication +@EnableJpaRepositories public class DemoApplication { public static void main(String[] args) { diff --git a/examples/spring-boot/src/main/java/com/example/DemoController.java b/examples/spring-boot/src/main/java/com/example/DemoController.java index c1be9d5b611..79f48711cad 100644 --- a/examples/spring-boot/src/main/java/com/example/DemoController.java +++ b/examples/spring-boot/src/main/java/com/example/DemoController.java @@ -7,9 +7,11 @@ public class DemoController { private final StringRedisTemplate stringRedisTemplate; + private final DemoService demoService; - public DemoController(StringRedisTemplate stringRedisTemplate){ + public DemoController(StringRedisTemplate stringRedisTemplate, DemoService demoService) { this.stringRedisTemplate = stringRedisTemplate; + this.demoService = demoService; } @GetMapping("/foo") @@ -21,4 +23,9 @@ public String get() { public void set(@RequestBody String value) { stringRedisTemplate.opsForValue().set("foo", value); } + + @GetMapping("/{id}") + public DemoEntity getDemoEntity(@PathVariable("id") Long id) { + return demoService.getDemoEntity(id); + } } diff --git a/examples/spring-boot/src/main/java/com/example/DemoEntity.java b/examples/spring-boot/src/main/java/com/example/DemoEntity.java new file mode 100644 index 00000000000..47c0fa94f75 --- /dev/null +++ b/examples/spring-boot/src/main/java/com/example/DemoEntity.java @@ -0,0 +1,21 @@ +package com.example; + +import lombok.Data; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + + +@Data +@Entity +public class DemoEntity { + + @Id + @GeneratedValue + private Long id; + + @Column + private String value; +} diff --git a/examples/spring-boot/src/main/java/com/example/DemoRepository.java b/examples/spring-boot/src/main/java/com/example/DemoRepository.java new file mode 100644 index 00000000000..89f3775deb0 --- /dev/null +++ b/examples/spring-boot/src/main/java/com/example/DemoRepository.java @@ -0,0 +1,7 @@ +package com.example; + + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface DemoRepository extends JpaRepository { +} diff --git a/examples/spring-boot/src/main/java/com/example/DemoService.java b/examples/spring-boot/src/main/java/com/example/DemoService.java new file mode 100644 index 00000000000..18e8690a99f --- /dev/null +++ b/examples/spring-boot/src/main/java/com/example/DemoService.java @@ -0,0 +1,18 @@ +package com.example; + +import org.springframework.stereotype.Service; + +@Service +public class DemoService { + private final DemoRepository demoRepository; + + public DemoService(DemoRepository demoRepository) { + this.demoRepository = demoRepository; + } + + + public DemoEntity getDemoEntity(Long id) { + return demoRepository.findById(id) + .orElseThrow(() ->new RuntimeException("Entity not found")); + } +} diff --git a/examples/spring-boot/src/main/resources/application.yml b/examples/spring-boot/src/main/resources/application.yml new file mode 100644 index 00000000000..ed10c24ce75 --- /dev/null +++ b/examples/spring-boot/src/main/resources/application.yml @@ -0,0 +1,11 @@ +spring: + jpa: + hibernate: + ddl-auto: create + show-sql: true + properties: + hibernate: + jdbc: + lob: + non_contextual_creation: true + diff --git a/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java b/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java index 40e60f45b25..3b092a05ca0 100644 --- a/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java +++ b/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java @@ -1,33 +1,27 @@ package com.example; -import org.junit.ClassRule; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.util.TestPropertyValues; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.testcontainers.containers.GenericContainer; @RunWith(SpringJUnit4ClassRunner.class) -@SpringBootTest(classes = DemoApplication.class,webEnvironment = WebEnvironment.RANDOM_PORT) -@ContextConfiguration(initializers = AbstractIntegrationTest.Initializer.class) +@SpringBootTest(classes = DemoApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT, properties = { + "spring.datasource.url=jdbc:tc:postgresql:11-alpine:///databasename", + "spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver" +}) +@ActiveProfiles("test") public abstract class AbstractIntegrationTest { - @ClassRule - public static GenericContainer redis = new GenericContainer("redis:3.0.6").withExposedPorts(6379); + static { + GenericContainer redis = new GenericContainer("redis:3-alpine") + .withExposedPorts(6379); + redis.start(); - public static class Initializer implements ApplicationContextInitializer { - @Override - public void initialize(ConfigurableApplicationContext configurableApplicationContext) { - TestPropertyValues values = TestPropertyValues.of( - "spring.redis.host=" + redis.getContainerIpAddress(), - "spring.redis.port=" + redis.getMappedPort(6379) - ); - values.applyTo(configurableApplicationContext); - } - } + System.setProperty("spring.redis.host", redis.getContainerIpAddress()); + System.setProperty("spring.redis.port", redis.getFirstMappedPort() + ""); + } } diff --git a/examples/spring-boot/src/test/java/com/example/DemoControllerTest.java b/examples/spring-boot/src/test/java/com/example/DemoControllerTest.java index 2b2d35963bc..286ce41ffbf 100644 --- a/examples/spring-boot/src/test/java/com/example/DemoControllerTest.java +++ b/examples/spring-boot/src/test/java/com/example/DemoControllerTest.java @@ -1,16 +1,20 @@ package com.example; -import static org.rnorth.visibleassertions.VisibleAssertions.*; - import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.web.client.TestRestTemplate; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.info; + public class DemoControllerTest extends AbstractIntegrationTest { @Autowired TestRestTemplate restTemplate; + @Autowired + DemoRepository demoRepository; + @Test public void simpleTest() { String fooResource = "/foo"; @@ -21,4 +25,15 @@ public void simpleTest() { assertEquals("value is set", "bar", restTemplate.getForObject(fooResource, String.class)); } + @Test + public void simpleJPATest() { + DemoEntity demoEntity = new DemoEntity(); + demoEntity.setValue("Some value"); + demoRepository.save(demoEntity); + + DemoEntity result = restTemplate.getForObject("/" + demoEntity.getId(), DemoEntity.class); + + assertEquals("value is set", "Some value", result.getValue()); + } + } From 7b2dd01e5838a56656c4d2b4d9ebf8e9f4b1fd5b Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 25 Jan 2019 16:12:16 +0100 Subject: [PATCH 0574/5615] Fix typo in Postgresql gradle dependency (#1182) The correct dependency is org.testcontainers:postgresql no org.testcontainers:postgres --- docs/modules/databases/postgres.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/databases/postgres.md b/docs/modules/databases/postgres.md index b526b1c566b..51eca471f4c 100644 --- a/docs/modules/databases/postgres.md +++ b/docs/modules/databases/postgres.md @@ -7,7 +7,7 @@ See [Database containers](./index.md) for documentation and usage that is common Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:postgres:{{latest_version}}" +testCompile "org.testcontainers:postgresql:{{latest_version}}" ``` ```xml tab='Maven' From 40cdd583b16939fc0c63b64837f9a82f26261e72 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 27 Jan 2019 15:43:02 +0100 Subject: [PATCH 0575/5615] Make it possible to turn off Ryuk with an environment variable (#1181) * Make it possible to turn off Ryuk with an environment variable * Reverse the condition * Document how to disable the Ryuk --- .../testcontainers/DockerClientFactory.java | 28 +++++++++++++++---- docs/features/configuration.md | 11 +++++++- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 725f04ca6d1..408767ea7ed 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -119,17 +119,35 @@ public DockerClient client() { " Operating System: " + dockerInfo.getOperatingSystem() + "\n" + " Total Memory: " + dockerInfo.getMemTotal() / (1024 * 1024) + " MB"); - boolean checksEnabled = !TestcontainersConfiguration.getInstance().isDisableChecks(); - - String ryukContainerId = ResourceReaper.start(hostIpAddress, client, checksEnabled); - log.info("Ryuk started - will monitor and terminate Testcontainers containers on JVM exit"); + String ryukContainerId = null; + boolean useRyuk = !Boolean.parseBoolean(System.getenv("TESTCONTAINERS_RYUK_DISABLED")); + if (useRyuk) { + ryukContainerId = ResourceReaper.start(hostIpAddress, client); + log.info("Ryuk started - will monitor and terminate Testcontainers containers on JVM exit"); + } VisibleAssertions.info("Checking the system..."); checkDockerVersion(version.getVersion()); + boolean checksEnabled = !TestcontainersConfiguration.getInstance().isDisableChecks(); if (checksEnabled) { - checkDiskSpace(client, ryukContainerId); + if (ryukContainerId != null) { + checkDiskSpace(client, ryukContainerId); + } else { + runInsideDocker( + client, + createContainerCmd -> { + createContainerCmd.withName("testcontainers-checks-" + SESSION_ID); + createContainerCmd.getHostConfig().withAutoRemove(true); + createContainerCmd.withCmd("tail", "-f", "/dev/null"); + }, + (__, containerId) -> { + checkDiskSpace(client, containerId); + return ""; + } + ); + } } initialized = true; diff --git a/docs/features/configuration.md b/docs/features/configuration.md index 9d2328f3b41..f511d24a1f0 100644 --- a/docs/features/configuration.md +++ b/docs/features/configuration.md @@ -42,7 +42,7 @@ Some companies disallow the usage of Docker Hub, but you can override `*.image` > **kafka.container.image = confluentinc/cp-kafka** > Used by KafkaContainer -## Customizing ryuk resource reaper +## Customizing Ryuk resource reaper > **ryuk.container.image = quay.io/testcontainers/ryuk:0.2.2** > The resource reaper is responsible for container removal and automatic cleanup of dead containers at JVM shutdown @@ -50,3 +50,12 @@ Some companies disallow the usage of Docker Hub, but you can override `*.image` > **ryuk.container.privileged = false** > In some environments ryuk must be started in privileged mode to work properly (--privileged flag) +### Disabling Ryuk +Ryuk must be started as a privileged container. +If your environment already implements automatic cleanup of containers after the execution, +but does not allow starting privileged containers, you can turn off the Ryuk container by setting +`TESTCONTAINERS_RYUK_DISABLED` **environment variable** to `true`. + +!!!tip + Note that Testcontainers will continue doing the cleanup at JVM's shutdown, unless you `kill -9` your JVM process. + From e6e3331eac00d70e2f30782712dbc18b6c1190c8 Mon Sep 17 00:00:00 2001 From: Artem Yakshin Date: Sun, 27 Jan 2019 17:58:37 +0300 Subject: [PATCH 0576/5615] Correct sqlserver jdbc url should be supported by jdbc driver proxy (#989) --- .../src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java | 1 + .../org/testcontainers/containers/MSSQLServerContainer.java | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java index 941a224e7a0..f61f6276c5a 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java @@ -64,6 +64,7 @@ public static Iterable data() { {"jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&password=somepwd&TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_MY_CNF=somepath/mariadb_conf_override", EnumSet.of(Options.CustomIniFile)}, {"jdbc:tc:clickhouse://hostname/databasename", EnumSet.of(Options.PmdKnownBroken)}, + {"jdbc:tc:sqlserver:2017-CU12://hostname:hostport;databaseName=databasename", EnumSet.noneOf(Options.class)}, }); } diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java index 6d843f34438..ec657b90bad 100644 --- a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java @@ -6,7 +6,8 @@ * @author Stefan Hufschmidt */ public class MSSQLServerContainer> extends JdbcDatabaseContainer { - public static final String NAME = "mssqlserver"; + + public static final String NAME = "sqlserver"; public static final String IMAGE = "mcr.microsoft.com/mssql/server"; public static final String DEFAULT_TAG = "2017-CU12"; From 6a988ddfd263af0f3d724330a880652275f716c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 27 Jan 2019 19:26:41 +0000 Subject: [PATCH 0577/5615] Bump amqp-client from 5.5.2 to 5.5.3 in /core (#1170) Bumps [amqp-client](https://github.com/rabbitmq/rabbitmq-java-client) from 5.5.2 to 5.5.3. - [Release notes](https://github.com/rabbitmq/rabbitmq-java-client/releases) - [Changelog](https://github.com/rabbitmq/rabbitmq-java-client/blob/v5.5.3/release-versions.txt) - [Commits](https://github.com/rabbitmq/rabbitmq-java-client/compare/v5.5.2...v5.5.3) Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 8c36aba5893..d3d1a4cbe09 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -118,7 +118,7 @@ dependencies { } testCompile 'redis.clients:jedis:3.0.1' - testCompile 'com.rabbitmq:amqp-client:5.5.2' + testCompile 'com.rabbitmq:amqp-client:5.5.3' testCompile 'org.mongodb:mongo-java-driver:3.9.1' testCompile ('org.mockito:mockito-core:2.23.4') { exclude(module: 'hamcrest-core') From c2e2a0fa96ce8c1687120ffa45c347a9f5a46e27 Mon Sep 17 00:00:00 2001 From: Chung Nguyen Date: Tue, 29 Jan 2019 23:33:13 +0700 Subject: [PATCH 0578/5615] Add "singleton container" example (#1178) Closes #1138 * add example for singleton container * address review comment. * change method name to not starts with capital letter * Update examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java Co-Authored-By: chungngoops * Update FooConcreteTestClass.java * Update BarConcreteTestClass.java * Update assert texts * Update FooConcreteTestClass.java --- examples/singleton-container/pom.xml | 43 +++++++++++++++++++ .../main/java/com/example/cache/Cache.java | 10 +++++ .../com/example/cache/RedisBackedCache.java | 34 +++++++++++++++ .../com/example/AbstractIntegrationTest.java | 13 ++++++ .../com/example/BarConcreteTestClass.java | 34 +++++++++++++++ .../com/example/FooConcreteTestClass.java | 33 ++++++++++++++ 6 files changed, 167 insertions(+) create mode 100644 examples/singleton-container/pom.xml create mode 100644 examples/singleton-container/src/main/java/com/example/cache/Cache.java create mode 100644 examples/singleton-container/src/main/java/com/example/cache/RedisBackedCache.java create mode 100644 examples/singleton-container/src/test/java/com/example/AbstractIntegrationTest.java create mode 100644 examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java create mode 100644 examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java diff --git a/examples/singleton-container/pom.xml b/examples/singleton-container/pom.xml new file mode 100644 index 00000000000..710c32d6b80 --- /dev/null +++ b/examples/singleton-container/pom.xml @@ -0,0 +1,43 @@ + + + + testcontainers-java-examples + org.testcontainers.examples + NOVERSION + + 4.0.0 + + singleton-container + + + + redis.clients + jedis + 3.0.1 + + + com.google.code.gson + gson + 2.8.5 + + + com.google.guava + guava + 23.0 + + + org.slf4j + slf4j-api + 1.7.25 + provided + + + ch.qos.logback + logback-classic + 1.2.3 + test + + + diff --git a/examples/singleton-container/src/main/java/com/example/cache/Cache.java b/examples/singleton-container/src/main/java/com/example/cache/Cache.java new file mode 100644 index 00000000000..5c5ba253d6e --- /dev/null +++ b/examples/singleton-container/src/main/java/com/example/cache/Cache.java @@ -0,0 +1,10 @@ +package com.example.cache; + +import java.util.Optional; + +public interface Cache { + + void put(String key, Object value); + + Optional get(String key, Class expectedClass); +} \ No newline at end of file diff --git a/examples/singleton-container/src/main/java/com/example/cache/RedisBackedCache.java b/examples/singleton-container/src/main/java/com/example/cache/RedisBackedCache.java new file mode 100644 index 00000000000..12585cafe16 --- /dev/null +++ b/examples/singleton-container/src/main/java/com/example/cache/RedisBackedCache.java @@ -0,0 +1,34 @@ +package com.example.cache; + +import com.google.gson.Gson; +import redis.clients.jedis.Jedis; + +import java.util.Optional; + +public class RedisBackedCache implements Cache { + + private final Jedis jedis; + private final String cacheName; + private final Gson gson; + + public RedisBackedCache(Jedis jedis, String cacheName) { + this.jedis = jedis; + this.cacheName = cacheName; + this.gson = new Gson(); + } + + public void put(String key, Object value) { + String jsonValue = gson.toJson(value); + this.jedis.hset(this.cacheName, key, jsonValue); + } + + public Optional get(String key, Class expectedClass) { + String foundJson = this.jedis.hget(this.cacheName, key); + + if (foundJson == null) { + return Optional.empty(); + } + + return Optional.of(gson.fromJson(foundJson, expectedClass)); + } +} diff --git a/examples/singleton-container/src/test/java/com/example/AbstractIntegrationTest.java b/examples/singleton-container/src/test/java/com/example/AbstractIntegrationTest.java new file mode 100644 index 00000000000..6daff319753 --- /dev/null +++ b/examples/singleton-container/src/test/java/com/example/AbstractIntegrationTest.java @@ -0,0 +1,13 @@ +package com.example; + +import org.testcontainers.containers.GenericContainer; + +public abstract class AbstractIntegrationTest { + + public static final GenericContainer redis = new GenericContainer("redis:3.0.6") + .withExposedPorts(6379); + + static { + redis.start(); + } +} diff --git a/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java b/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java new file mode 100644 index 00000000000..08a5ebfe2f5 --- /dev/null +++ b/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java @@ -0,0 +1,34 @@ +package com.example; + +import com.example.cache.Cache; +import com.example.cache.RedisBackedCache; +import org.junit.Before; +import org.junit.Test; +import redis.clients.jedis.Jedis; + +import java.util.Optional; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; + +public class BarConcreteTestClass extends AbstractIntegrationTest { + + private Cache cache; + + @Before + public void setUp() { + Jedis jedis = new Jedis(redis.getContainerIpAddress(), redis.getMappedPort(6379)); + + cache = new RedisBackedCache(jedis, "bar"); + } + + @Test + public void testInsertValue() { + cache.put("bar", "BAR"); + Optional foundObject = cache.get("bar", String.class); + + assertTrue("When inserting an object into the cache, it can be retrieved", foundObject.isPresent()); + assertEquals("When accessing the value of a retrieved object, the value must be the same", "BAR", foundObject.get()); + } + +} diff --git a/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java b/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java new file mode 100644 index 00000000000..1196348b258 --- /dev/null +++ b/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java @@ -0,0 +1,33 @@ +package com.example; + +import com.example.cache.Cache; +import com.example.cache.RedisBackedCache; +import org.junit.Before; +import org.junit.Test; +import redis.clients.jedis.Jedis; + +import java.util.Optional; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; + +public class FooConcreteTestClass extends AbstractIntegrationTest { + + private Cache cache; + + @Before + public void setUp() throws Exception { + Jedis jedis = new Jedis(redis.getContainerIpAddress(), redis.getMappedPort(6379)); + + cache = new RedisBackedCache(jedis, "foo"); + } + + @Test + public void testInsertValue() { + cache.put("foo", "FOO"); + Optional foundObject = cache.get("foo", String.class); + + assertTrue("When inserting an object into the cache, it can be retrieved", foundObject.isPresent()); + assertEquals("When accessing the value of a retrieved object, the value must be the same", "FOO", foundObject.get()); + } +} From 9d8c10a4ed88cd51387321f03057837b56016c91 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 29 Jan 2019 18:21:09 +0100 Subject: [PATCH 0579/5615] Set `latest_version` to `1.10.6` --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 4570bbf7735..0aad56f0314 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -76,4 +76,4 @@ nav: - contributing.md - contributing_docs.md extra: - latest_version: 1.10.5 + latest_version: 1.10.6 From 1672e7c8774111374ea7739a1ce5120780c31710 Mon Sep 17 00:00:00 2001 From: Pablo Porto Date: Thu, 31 Jan 2019 11:38:14 +0100 Subject: [PATCH 0580/5615] Allow extension of Neo4jContainer class (#1195) My team is trying to use Neo4jContainer in a Kotlin codebase but we have an issue with type inference. Seems like Kotlin cannot infer the type when creating an instance of Neo4jContainer. The following code causes a type inference error: ``` val myNeo4jContainer = Neo4jContainer("imageName") ``` To fix it, a custom class that extends from Neo4jContainer needs to be declared, for example: ``` class KNeo4jContainer(imageName: String) : Neo4jContainer(imageName) val myNeo4jContainer = KNeo4jContainer("imageName") ``` This change enables extending the Neo4jContainer class. --- .../main/java/org/testcontainers/containers/Neo4jContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java index 48e4f2f626d..a8072d350ba 100644 --- a/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java +++ b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java @@ -19,7 +19,7 @@ * @param "SELF" to be used in the withXXX methods. * @author Michael J. Simons */ -public final class Neo4jContainer> extends GenericContainer { +public class Neo4jContainer> extends GenericContainer { /** * The image defaults to the official Neo4j image: Neo4j. From a6ae00102a60f482af37c6299e20d2c7bfc5d88f Mon Sep 17 00:00:00 2001 From: Thomas Treuthardt Date: Sat, 2 Feb 2019 17:01:22 +0100 Subject: [PATCH 0581/5615] Correct maven artifactId for postgresql (#1203) --- docs/index.md | 2 +- docs/modules/databases/postgres.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/index.md b/docs/index.md index b6b7213e581..dc038d374be 100644 --- a/docs/index.md +++ b/docs/index.md @@ -20,7 +20,7 @@ Testcontainers make the following kinds of tests easier: * [JUnit 4](test_framework_integration/junit_4.md) - See the [JUnit 4 Quickstart Guide](quickstart/junit_4_quickstart.md) * [Jupiter/JUnit 5](test_framework_integration/junit_5.md) * [Spock](test_framework_integration/spock.md) - * *Or* manually add code to control the container/test lifecycle (See [hints for this approach](test_framework_integration/junit_4.md#manually-controlling-container-lifecycle) + * *Or* manually add code to control the container/test lifecycle (See [hints for this approach](test_framework_integration/junit_4.md#manually-controlling-container-lifecycle)) ## Maven dependencies diff --git a/docs/modules/databases/postgres.md b/docs/modules/databases/postgres.md index 51eca471f4c..07831766d1b 100644 --- a/docs/modules/databases/postgres.md +++ b/docs/modules/databases/postgres.md @@ -13,7 +13,7 @@ testCompile "org.testcontainers:postgresql:{{latest_version}}" ```xml tab='Maven' org.testcontainers - postgres + postgresql {{latest_version}} test From 74a9bc183197174f356ba13c2bce20dea49ed320 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sat, 2 Feb 2019 16:03:00 +0000 Subject: [PATCH 0582/5615] Bump annotations from 16.0.3 to 17.0.0 in /core (#1194) Bumps [annotations](https://github.com/JetBrains/java-annotations) from 16.0.3 to 17.0.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Commits](https://github.com/JetBrains/java-annotations/compare/16.0.3...17.0.0) Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index d3d1a4cbe09..0c4dffaa1c3 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -74,7 +74,7 @@ project.tasks.check.dependsOn(jarFileTest) dependencies { compile 'junit:junit:4.12' compile 'org.slf4j:slf4j-api:1.7.25' - compile 'org.jetbrains:annotations:16.0.3' + compile 'org.jetbrains:annotations:17.0.0' compile 'javax.annotation:javax.annotation-api:1.3.2' compile 'org.apache.commons:commons-compress:1.18' // Added for JDK9 compatibility since it's missing this package From a43a53623e6ec3cb1952a2f4c8b6427d1d1a4e68 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sat, 2 Feb 2019 16:04:10 +0000 Subject: [PATCH 0583/5615] Bump transport from 6.5.4 to 6.6.0 in /modules/elasticsearch (#1191) Bumps [transport](https://github.com/elastic/elasticsearch) from 6.5.4 to 6.6.0. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v6.5.4...v6.6.0) Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index ac51908c92e..d045d3775d3 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,5 +3,5 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') testCompile "org.elasticsearch.client:elasticsearch-rest-client:6.5.4" - testCompile "org.elasticsearch.client:transport:6.5.4" + testCompile "org.elasticsearch.client:transport:6.6.0" } From 60d8a8fc6cb81e462bb8502184ec0f643f1c0e62 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sat, 2 Feb 2019 16:38:52 +0000 Subject: [PATCH 0584/5615] Bump elasticsearch-rest-client in /modules/elasticsearch (#1190) Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 6.5.4 to 6.6.0. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v6.5.4...v6.6.0) Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index d045d3775d3..8405a9000c0 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,6 +2,6 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') - testCompile "org.elasticsearch.client:elasticsearch-rest-client:6.5.4" + testCompile "org.elasticsearch.client:elasticsearch-rest-client:6.6.0" testCompile "org.elasticsearch.client:transport:6.6.0" } From 6bd880d3c6e0b6b4e638ddad1c1e2714dea90e6d Mon Sep 17 00:00:00 2001 From: Michael Kutz Date: Sat, 2 Feb 2019 18:21:43 +0100 Subject: [PATCH 0585/5615] Use withCapabilities in Webdriver docs (#1197) This contributes to #1196 --- docs/modules/webdriver_containers.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/modules/webdriver_containers.md b/docs/modules/webdriver_containers.md index c9526ad39d4..70c06d9dbdd 100644 --- a/docs/modules/webdriver_containers.md +++ b/docs/modules/webdriver_containers.md @@ -26,7 +26,7 @@ The following field in your JUnit UI test class will prepare a container running @Rule public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() - .withDesiredCapabilities(DesiredCapabilities.chrome()); + .withCapabilities(new ChromeOptions()); ``` Now, instead of instantiating an instance of WebDriver directly, use the following to obtain an instance inside your @@ -51,13 +51,13 @@ driver.get("http://" + chrome.getTestHostIpAddress() + ":8080/"); At the moment, Chrome and Firefox are supported. To switch, simply change the first parameter to the rule constructor: ```java new BrowserWebDriverContainer() - .withDesiredCapabilities(DesiredCapabilities.chrome()); + .withCapabilities(new ChromeOptions()); ``` or ```java new BrowserWebDriverContainer() - .withDesiredCapabilities(DesiredCapabilities.firefox()); + .withCapabilities(new FirefoxOptions()); ``` ### Recording videos @@ -68,23 +68,23 @@ just for failing tests. To do this, simply add extra parameters to the rule constructor: ```java new BrowserWebDriverContainer() - .withDesiredCapabilities(DesiredCapabilities.chrome()) - .withRecordingMode(VncRecordingMode.RECORD_ALL, new File("./target/")) + .withCapabilities(new ChromeOptions()) + .withRecordingMode(VncRecordingMode.RECORD_ALL, new File("./target/")) ``` or if you only want videos for test failures: ```java new BrowserWebDriverContainer() - .withDesiredCapabilities(DesiredCapabilities.chrome()) - .withRecordingMode(VncRecordingMode.RECORD_FAILING, new File("./target/")) + .withCapabilities(new ChromeOptions()) + .withRecordingMode(VncRecordingMode.RECORD_FAILING, new File("./target/")) ``` Note that the seconds parameter to `withRecordingMode` should be a directory where recordings can be saved. If you would like to customise the file name of the recording, or provide a different directory at runtime based on the description of the test and/or its success or failure, you may provide a custom recording file factory as follows: ```java new BrowserWebDriverContainer() - //... - .withRecordingFileFactory(new CustomRecordingFileFactory()) + //... + .withRecordingFileFactory(new CustomRecordingFileFactory()) ``` Note the factory must implement `org.testcontainers.containers.RecordingFileFactory`. From 29667be2457d66407dbb7c06ef4c8fd7eed875a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sat, 2 Feb 2019 17:22:19 +0000 Subject: [PATCH 0586/5615] Bump amqp-client from 5.5.3 to 5.6.0 in /core (#1186) Bumps [amqp-client](https://github.com/rabbitmq/rabbitmq-java-client) from 5.5.3 to 5.6.0. - [Release notes](https://github.com/rabbitmq/rabbitmq-java-client/releases) - [Changelog](https://github.com/rabbitmq/rabbitmq-java-client/blob/v5.6.0/release-versions.txt) - [Commits](https://github.com/rabbitmq/rabbitmq-java-client/compare/v5.5.3...v5.6.0) Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 0c4dffaa1c3..6cd99be0a02 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -118,7 +118,7 @@ dependencies { } testCompile 'redis.clients:jedis:3.0.1' - testCompile 'com.rabbitmq:amqp-client:5.5.3' + testCompile 'com.rabbitmq:amqp-client:5.6.0' testCompile 'org.mongodb:mongo-java-driver:3.9.1' testCompile ('org.mockito:mockito-core:2.23.4') { exclude(module: 'hamcrest-core') From 905ae0bb771ef2bb4300c86a05ee065743174ee2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sat, 2 Feb 2019 17:22:37 +0000 Subject: [PATCH 0587/5615] Bump mariadb-java-client from 2.3.0 to 2.4.0 in /modules/mariadb (#1189) Bumps [mariadb-java-client](https://github.com/MariaDB/mariadb-connector-j) from 2.3.0 to 2.4.0. - [Release notes](https://github.com/MariaDB/mariadb-connector-j/releases) - [Commits](https://github.com/MariaDB/mariadb-connector-j/compare/2.3.0...2.4.0) Signed-off-by: dependabot[bot] --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 868fa87ccf5..5078348c8b4 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: JDBC :: MariaDB" dependencies { compile project(':jdbc') - testCompile 'org.mariadb.jdbc:mariadb-java-client:2.3.0' + testCompile 'org.mariadb.jdbc:mariadb-java-client:2.4.0' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.14' From d49f0178021e51de229a5f45c8e26b6ea1c13976 Mon Sep 17 00:00:00 2001 From: Aida Manna Date: Tue, 5 Feb 2019 18:19:03 +0100 Subject: [PATCH 0588/5615] Change megabytes for bytes in withSharedMemorySize method documentation (#1215) --- .../java/org/testcontainers/containers/GenericContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index abf535869da..b00061b3d97 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -1144,7 +1144,7 @@ public SELF withCreateContainerCmdModifier(Consumer modifier /** * Size of /dev/shm - * @param bytes The number of megabytes to assign the shared memory. If null, it will apply the Docker default which is 64 MB. + * @param bytes The number of bytes to assign the shared memory. If null, it will apply the Docker default which is 64 MB. * @return this */ public SELF withSharedMemorySize(Long bytes) { From 2e1676bda9abfd9d17d58e05ed1b5cbedfc07b94 Mon Sep 17 00:00:00 2001 From: alelech <4152206+alelech@users.noreply.github.com> Date: Wed, 6 Feb 2019 13:19:33 +0100 Subject: [PATCH 0589/5615] Update ryuk references to latest version - 0.2.3 (#1212) * Update ryuk version to match latest release * Update doc reference to ryuk version so that it matches defaults in code * Update test to check for pulling latest version --- .../testcontainers/utility/TestcontainersConfiguration.java | 2 +- .../java/org/testcontainers/dockerclient/ImagePullTest.java | 4 ++-- docs/features/configuration.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 1e1155053b6..2d478b4cc0a 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -51,7 +51,7 @@ public boolean isRyukPrivileged() { } public String getRyukImage() { - return (String) properties.getOrDefault("ryuk.container.image", "quay.io/testcontainers/ryuk:0.2.2"); + return (String) properties.getOrDefault("ryuk.container.image", "quay.io/testcontainers/ryuk:0.2.3"); } public String getSSHdImage() { diff --git a/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java b/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java index cb87caf16d2..47ad456dc26 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java @@ -22,8 +22,8 @@ public static String[] parameters() { "gliderlabs/alpine:3.5", "gliderlabs/alpine@sha256:a19aa4a17a525c97e5a90a0c53a9f3329d2dc61b0a14df5447757a865671c085", "quay.io/testcontainers/ryuk:latest", - "quay.io/testcontainers/ryuk:0.2.2", - "quay.io/testcontainers/ryuk@sha256:4b606e54c4bba1af4fd814019d342e4664d51e28d3ba2d18d24406edbefd66da", + "quay.io/testcontainers/ryuk:0.2.3", + "quay.io/testcontainers/ryuk@sha256:bb5a635cac4bd96c93cc476969ce11dc56436238ec7cd028d0524462f4739dd9", }; } diff --git a/docs/features/configuration.md b/docs/features/configuration.md index f511d24a1f0..72b5cac65f7 100644 --- a/docs/features/configuration.md +++ b/docs/features/configuration.md @@ -44,7 +44,7 @@ Some companies disallow the usage of Docker Hub, but you can override `*.image` ## Customizing Ryuk resource reaper -> **ryuk.container.image = quay.io/testcontainers/ryuk:0.2.2** +> **ryuk.container.image = quay.io/testcontainers/ryuk:0.2.3** > The resource reaper is responsible for container removal and automatic cleanup of dead containers at JVM shutdown > **ryuk.container.privileged = false** From d0a4bab11fad745e47683ca287484e6d57bb4bc7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Fri, 8 Feb 2019 08:39:23 +0100 Subject: [PATCH 0590/5615] Bump HikariCP from 3.3.0 to 3.3.1 in /modules/spock (#1224) Bumps [HikariCP](https://github.com/brettwooldridge/HikariCP) from 3.3.0 to 3.3.1. - [Release notes](https://github.com/brettwooldridge/HikariCP/releases) - [Changelog](https://github.com/brettwooldridge/HikariCP/blob/dev/CHANGES) - [Commits](https://github.com/brettwooldridge/HikariCP/compare/HikariCP-3.3.0...HikariCP-3.3.1) Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 206437e8b66..948b167fb6d 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -10,7 +10,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') - testCompile 'com.zaxxer:HikariCP:3.3.0' + testCompile 'com.zaxxer:HikariCP:3.3.1' testRuntime 'org.postgresql:postgresql:42.2.5' testRuntime 'mysql:mysql-connector-java:8.0.14' From 17c0aac07973efc4a1d2efe41193c13dcdda6ff2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Fri, 8 Feb 2019 08:45:46 +0100 Subject: [PATCH 0591/5615] Bump junit-jupiter from 5.3.2 to 5.4.0 in /modules/junit-jupiter (#1228) * Bump junit-jupiter-engine from 5.3.2 to 5.4.0 in /modules/junit-jupiter Bumps [junit-jupiter-engine](https://github.com/junit-team/junit5) from 5.3.2 to 5.4.0. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.3.2...r5.4.0) Signed-off-by: dependabot[bot] Bumps [junit-jupiter-api](https://github.com/junit-team/junit5) from 5.3.2 to 5.4.0. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](junit-team/junit5@r5.3.2...r5.4.0) --- modules/junit-jupiter/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 3ccb89a3543..9b04fb796b5 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -2,7 +2,7 @@ description = "Testcontainers :: JUnit Jupiter Extension" dependencies { compile project(':testcontainers') - compile 'org.junit.jupiter:junit-jupiter-api:5.3.2' + compile 'org.junit.jupiter:junit-jupiter-api:5.4.0' testCompile project(':mysql') testCompile project(':postgresql') @@ -11,7 +11,7 @@ dependencies { testRuntime 'org.postgresql:postgresql:42.2.5' testRuntime 'mysql:mysql-connector-java:8.0.14' - testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.3.2' + testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.4.0' } test { From 144b8865424a45ec85bff87c725a7647ddf771dc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Fri, 8 Feb 2019 08:07:16 +0000 Subject: [PATCH 0592/5615] Bump HikariCP from 3.3.0 to 3.3.1 in /modules/junit-jupiter (#1227) --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 9b04fb796b5..4ca7f024c5d 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -6,7 +6,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') - testCompile 'com.zaxxer:HikariCP:3.3.0' + testCompile 'com.zaxxer:HikariCP:3.3.1' testCompile 'redis.clients:jedis:3.0.1' testRuntime 'org.postgresql:postgresql:42.2.5' From ebfb7c95a383bf952060b59a56ecdd8d922ee093 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sat, 9 Feb 2019 08:46:01 +0000 Subject: [PATCH 0593/5615] Bump mysql-connector-java from 8.0.14 to 8.0.15 in /modules/spock (#1208) Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.14 to 8.0.15.
Changelog *Sourced from [mysql-connector-java's changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES).* > # Changelog > # https://dev.mysql.com/doc/relnotes/connector-j/8.0/en/ > > Version 8.0.15 > > - Fix for Bug#94051 (29261254), Not recommended default for 'allowLoadLocalInfile'. > > Version 8.0.14 > > - WL#12298, Connectors: Expose metadata about source and binaries in unified way. > > - Fix for Bug#93111 (28894344), ConnectionUrl.java contains char U+00A7 (section sign). > > - WL#12621, DevAPI: Handling of Default Schema. > > - Fix for Bug#93340 (28970166), C/J BUILD SCRIPT IS TOO VERBOSE > > - WL#12462, DevAPI: Be prepared for initial notice on connection. > > - Fix for Bug#28924137, WL#12463:IF COLLECTION DOESN'T EXIST, COLL.COUNT() IS GIVING A WRONG ERROR MESSAGE. > > - WL#12463, DevAPI: Standardize count method. > > - Fix for Bug#92508 (28747636), mysql-connector in bootclasspath causing memory leak. > > - Fix for Bug#25650514, UPDATEROW() CALL FAILS WITH NPE WHEN SSPS=TRUE AND TABLE HAS MULTI-FLD KEY. > > - Fix for Bug#25650482, REFRESHROW() CALL AFTER UPDATEROW() API FAILS WHEN USESERVERPREPSTMTS=TRUE. > > - Fix for Bug#92536 (28692243), UPDATEING SERVER SIDE PREPSTMTS RESULTSET FAIL. > > - Fix for Bug#92625 (28731795), CONTRIBUTION: FIX OBSERVED NPE IN CLEARINPUTSTREAM. > Thanks to Henning Schmiedehausen for his contribution. > > - Fix for Bug#23045642, ADDING NO-DOC (MYSQLCONNJ-696) RESULTS IN EXCEPTION. > > - Fix for Bug#91065 (28101003), ZERODATETIMEBEHAVIOR=CONVERT_TO_NULL SHOULD NOT APPLY TO 00:00:00 TIME COLUMNS. > > - Fix for Bug#92574 (28706219), WHEN CONVERTING FROM VARCHAR TO JAVA BOOLEAN, 'N' IS NOT SUPPORTED. > > - Fix for Bug#25642226, CHANGEUSER() NOT SETTING THE DATABASE PROPERLY WITH SHA USER. > > - Fix for Bug#28606708, NAMED PIPE CONNECTION FOR X PROTOCOL RETURNS NPE, EXPECTED PROPER ERROR MESSAGE. > > Version 8.0.13 > > - Fix for Bug#91317 (28207422), Wrong defaults on collation mappings. > > - WL#12245, DevAPI: Implement connect timeout. > > ... (truncated)
Commits - [`79a4336`](https://github.com/mysql/mysql-connector-j/commit/79a4336f140499bd22dd07f02b708e163844e3d5) Fix for Bug#94051 (29261254), Not recommended default for 'allowLoadLocalInfi... - [`76adb77`](https://github.com/mysql/mysql-connector-j/commit/76adb77ec3d11c0c34b08acbf21c700b702726a3) Merge tag '8.0.14' into 'release/8.0.15'. - [`d11fc1f`](https://github.com/mysql/mysql-connector-j/commit/d11fc1ff0d8d77562ec60c340b8f030e86643fff) Post-release changes. - See full diff in [compare view](https://github.com/mysql/mysql-connector-j/compare/8.0.14...8.0.15)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=mysql:mysql-connector-java&package-manager=gradle&previous-version=8.0.14&new-version=8.0.15)](https://dependabot.com/compatibility-score.html?dependency-name=mysql:mysql-connector-java&package-manager=gradle&previous-version=8.0.14&new-version=8.0.15) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot ignore this [patch|minor|major] version` will close this PR and stop Dependabot creating any more for this minor/major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 948b167fb6d..d891e179a94 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -13,5 +13,5 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.3.1' testRuntime 'org.postgresql:postgresql:42.2.5' - testRuntime 'mysql:mysql-connector-java:8.0.14' + testRuntime 'mysql:mysql-connector-java:8.0.15' } \ No newline at end of file From 266d687436c3a1f6bbb1eb6f30ac5c7cbc6715c7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sat, 9 Feb 2019 08:46:30 +0000 Subject: [PATCH 0594/5615] Bump mysql-connector-java from 8.0.14 to 8.0.15 in /modules/junit-jupiter (#1209) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [//]: # (dependabot-start) ⚠️ **Dependabot is rebasing this PR** ⚠️ If you make any changes to it yourself then they will take precedence over the rebase. --- [//]: # (dependabot-end) Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.14 to 8.0.15.
Changelog *Sourced from [mysql-connector-java's changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES).* > # Changelog > # https://dev.mysql.com/doc/relnotes/connector-j/8.0/en/ > > Version 8.0.15 > > - Fix for Bug#94051 (29261254), Not recommended default for 'allowLoadLocalInfile'. > > Version 8.0.14 > > - WL#12298, Connectors: Expose metadata about source and binaries in unified way. > > - Fix for Bug#93111 (28894344), ConnectionUrl.java contains char U+00A7 (section sign). > > - WL#12621, DevAPI: Handling of Default Schema. > > - Fix for Bug#93340 (28970166), C/J BUILD SCRIPT IS TOO VERBOSE > > - WL#12462, DevAPI: Be prepared for initial notice on connection. > > - Fix for Bug#28924137, WL#12463:IF COLLECTION DOESN'T EXIST, COLL.COUNT() IS GIVING A WRONG ERROR MESSAGE. > > - WL#12463, DevAPI: Standardize count method. > > - Fix for Bug#92508 (28747636), mysql-connector in bootclasspath causing memory leak. > > - Fix for Bug#25650514, UPDATEROW() CALL FAILS WITH NPE WHEN SSPS=TRUE AND TABLE HAS MULTI-FLD KEY. > > - Fix for Bug#25650482, REFRESHROW() CALL AFTER UPDATEROW() API FAILS WHEN USESERVERPREPSTMTS=TRUE. > > - Fix for Bug#92536 (28692243), UPDATEING SERVER SIDE PREPSTMTS RESULTSET FAIL. > > - Fix for Bug#92625 (28731795), CONTRIBUTION: FIX OBSERVED NPE IN CLEARINPUTSTREAM. > Thanks to Henning Schmiedehausen for his contribution. > > - Fix for Bug#23045642, ADDING NO-DOC (MYSQLCONNJ-696) RESULTS IN EXCEPTION. > > - Fix for Bug#91065 (28101003), ZERODATETIMEBEHAVIOR=CONVERT_TO_NULL SHOULD NOT APPLY TO 00:00:00 TIME COLUMNS. > > - Fix for Bug#92574 (28706219), WHEN CONVERTING FROM VARCHAR TO JAVA BOOLEAN, 'N' IS NOT SUPPORTED. > > - Fix for Bug#25642226, CHANGEUSER() NOT SETTING THE DATABASE PROPERLY WITH SHA USER. > > - Fix for Bug#28606708, NAMED PIPE CONNECTION FOR X PROTOCOL RETURNS NPE, EXPECTED PROPER ERROR MESSAGE. > > Version 8.0.13 > > - Fix for Bug#91317 (28207422), Wrong defaults on collation mappings. > > - WL#12245, DevAPI: Implement connect timeout. > > ... (truncated)
Commits - [`79a4336`](https://github.com/mysql/mysql-connector-j/commit/79a4336f140499bd22dd07f02b708e163844e3d5) Fix for Bug#94051 (29261254), Not recommended default for 'allowLoadLocalInfi... - [`76adb77`](https://github.com/mysql/mysql-connector-j/commit/76adb77ec3d11c0c34b08acbf21c700b702726a3) Merge tag '8.0.14' into 'release/8.0.15'. - [`d11fc1f`](https://github.com/mysql/mysql-connector-j/commit/d11fc1ff0d8d77562ec60c340b8f030e86643fff) Post-release changes. - See full diff in [compare view](https://github.com/mysql/mysql-connector-j/compare/8.0.14...8.0.15)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=mysql:mysql-connector-java&package-manager=gradle&previous-version=8.0.14&new-version=8.0.15)](https://dependabot.com/compatibility-score.html?dependency-name=mysql:mysql-connector-java&package-manager=gradle&previous-version=8.0.14&new-version=8.0.15) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot ignore this [patch|minor|major] version` will close this PR and stop Dependabot creating any more for this minor/major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 4ca7f024c5d..fff8099606d 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -10,7 +10,7 @@ dependencies { testCompile 'redis.clients:jedis:3.0.1' testRuntime 'org.postgresql:postgresql:42.2.5' - testRuntime 'mysql:mysql-connector-java:8.0.14' + testRuntime 'mysql:mysql-connector-java:8.0.15' testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.4.0' } From 4495e3ab6338c5961941bdbf8c6d92b412b45a4a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sat, 9 Feb 2019 08:46:40 +0000 Subject: [PATCH 0595/5615] Bump tomcat-jdbc from 9.0.14 to 9.0.16 in /modules/mysql (#1226) Bumps tomcat-jdbc from 9.0.14 to 9.0.16. Signed-off-by: dependabot[bot] --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 645212ec49d..deaf441f633 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -5,7 +5,7 @@ dependencies { testCompile 'mysql:mysql-connector-java:8.0.14' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.14' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.16' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.vibur:vibur-dbcp:22.2' } From cce7b93dd42768ca2fccf83a821bf03266b16c3b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sat, 9 Feb 2019 08:46:50 +0000 Subject: [PATCH 0596/5615] Bump tomcat-jdbc from 9.0.14 to 9.0.16 in /modules/mariadb (#1225) Bumps tomcat-jdbc from 9.0.14 to 9.0.16. Signed-off-by: dependabot[bot] --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 5078348c8b4..3cebd55943d 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -6,6 +6,6 @@ dependencies { testCompile 'org.mariadb.jdbc:mariadb-java-client:2.4.0' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.14' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.16' testCompile 'org.vibur:vibur-dbcp:22.2' } From b7649a88c32fb80d8e7d58d7cbd616566eb2eefe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sat, 9 Feb 2019 08:47:04 +0000 Subject: [PATCH 0597/5615] Bump java-client from 2.7.2 to 2.7.3 in /modules/couchbase (#1222) Bumps [java-client](https://github.com/couchbase/couchbase-java-client) from 2.7.2 to 2.7.3. - [Release notes](https://github.com/couchbase/couchbase-java-client/releases) - [Commits](https://github.com/couchbase/couchbase-java-client/compare/2.7.2...2.7.3) Signed-off-by: dependabot[bot] --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 50cf41038a7..bbbea2b036d 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -2,5 +2,5 @@ description = "Testcontainers :: Couchbase" dependencies { compile project(':testcontainers') - compile 'com.couchbase.client:java-client:2.7.2' + compile 'com.couchbase.client:java-client:2.7.3' } From 3db05a430c8cffed1ef0bbef28c5a4fe35ad248e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sat, 9 Feb 2019 08:48:37 +0000 Subject: [PATCH 0598/5615] Bump mockito-core from 2.23.4 to 2.24.0 in /core (#1210) Bumps [mockito-core](https://github.com/mockito/mockito) from 2.23.4 to 2.24.0.
Commits - [`aacacb1`](https://github.com/mockito/mockito/commit/aacacb156166b54b7eba3dfe03dd7bacda54c8d8) 2.24.0 release (previous 2.23.20) + release notes updated by CI build 3859 - [`e4c5df7`](https://github.com/mockito/mockito/commit/e4c5df7cc7f7145ff78d5e850e03bf8bc473bb9f) [ci maven-central-release] Release 2.24.0 - [`5de598c`](https://github.com/mockito/mockito/commit/5de598cd806f61f5247f2a70908f2c6d42c80e79) 2.23.20 release (previous 2.23.19) + release notes updated by CI build 3857 - [`4affa66`](https://github.com/mockito/mockito/commit/4affa6618bb134fb8278a10d9dac3f9cc6729652) Fixes [#1578](https://github-redirect.dependabot.com/mockito/mockito/issues/1578): handle multiple invocations on MockitoSession ([#1596](https://github-redirect.dependabot.com/mockito/mockito/issues/1596)) - [`7a64410`](https://github.com/mockito/mockito/commit/7a64410c8445533b1d96b0117f375ac3fa8a90ff) 2.23.19 release (previous 2.23.18) + release notes updated by CI build 3854 - [`074c06d`](https://github.com/mockito/mockito/commit/074c06ddaaca1c0b3f12fb11daf7e0fc23b3d198) Allow mockito-logger configurable as Mockito extension ([#1430](https://github-redirect.dependabot.com/mockito/mockito/issues/1430)) - [`dad3ef7`](https://github.com/mockito/mockito/commit/dad3ef7845a52197f4f41abd082775a68811ff8c) 2.23.18 release (previous 2.23.17) + release notes updated by CI build 3851 - [`00cc06b`](https://github.com/mockito/mockito/commit/00cc06b387c3af8aaaed3a14fcb38df7d69db6c1) Fix [#1584](https://github-redirect.dependabot.com/mockito/mockito/issues/1584) create a MultipleParentClassLoader with also classloaders of supert... - [`738b562`](https://github.com/mockito/mockito/commit/738b562ac967392747d47e5924b185aaed3cc544) 2.23.17 release (previous 2.23.16) + release notes updated by CI build 3846 - [`3108ff1`](https://github.com/mockito/mockito/commit/3108ff10cd26fa49580df255b3eefb2edb53d16f) Merge branch 'module-fix' into release/2.x - Additional commits viewable in [compare view](https://github.com/mockito/mockito/compare/v2.23.4...v2.24.0)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=org.mockito:mockito-core&package-manager=gradle&previous-version=2.23.4&new-version=2.24.0)](https://dependabot.com/compatibility-score.html?dependency-name=org.mockito:mockito-core&package-manager=gradle&previous-version=2.23.4&new-version=2.24.0) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot ignore this [patch|minor|major] version` will close this PR and stop Dependabot creating any more for this minor/major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 6cd99be0a02..419cd98d9f8 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -120,7 +120,7 @@ dependencies { testCompile 'redis.clients:jedis:3.0.1' testCompile 'com.rabbitmq:amqp-client:5.6.0' testCompile 'org.mongodb:mongo-java-driver:3.9.1' - testCompile ('org.mockito:mockito-core:2.23.4') { + testCompile ('org.mockito:mockito-core:2.24.0') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From c5ce1cbbdf3ee209d89fa69a5ef6824664c6761b Mon Sep 17 00:00:00 2001 From: fredclose Date: Sat, 9 Feb 2019 09:49:04 +0100 Subject: [PATCH 0599/5615] minor word inversion (#1219) --- docs/test_framework_integration/manual_lifecycle_control.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/test_framework_integration/manual_lifecycle_control.md b/docs/test_framework_integration/manual_lifecycle_control.md index 17e478c1d16..6de3cb47196 100644 --- a/docs/test_framework_integration/manual_lifecycle_control.md +++ b/docs/test_framework_integration/manual_lifecycle_control.md @@ -1,7 +1,7 @@ # Manual container lifecycle control While Testcontainers was originally built with JUnit 4 integration in mind, it is fully usable with other test -frameworks, or no with framework at all. +frameworks, or with no framework at all. ## Manually starting/stopping containers From 375bdaea6e47b79b9bf75fc023455d98e258a23e Mon Sep 17 00:00:00 2001 From: Alexey Vinogradov Date: Tue, 12 Feb 2019 13:51:32 +0700 Subject: [PATCH 0600/5615] Check Docker version according to checksEnabled flag (#1231) Fixes #1220 --- core/src/main/java/org/testcontainers/DockerClientFactory.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 408767ea7ed..5125993e3f7 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -128,10 +128,9 @@ public DockerClient client() { VisibleAssertions.info("Checking the system..."); - checkDockerVersion(version.getVersion()); - boolean checksEnabled = !TestcontainersConfiguration.getInstance().isDisableChecks(); if (checksEnabled) { + checkDockerVersion(version.getVersion()); if (ryukContainerId != null) { checkDiskSpace(client, ryukContainerId); } else { From 819f3e1eab8bb0b5f1ae1de5ea43fcaec5bd8b1e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 12 Feb 2019 09:16:27 +0100 Subject: [PATCH 0601/5615] Bump lombok from 1.18.4 to 1.18.6 in /examples (#1233) Bumps [lombok](https://github.com/rzwitserloot/lombok) from 1.18.4 to 1.18.6. - [Release notes](https://github.com/rzwitserloot/lombok/releases) - [Changelog](https://github.com/rzwitserloot/lombok/blob/master/doc/changelog.markdown) - [Commits](https://github.com/rzwitserloot/lombok/compare/v1.18.4...v1.18.6) Signed-off-by: dependabot[bot] --- examples/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/pom.xml b/examples/pom.xml index 5c1a7c1ef0e..f5eb3826fc6 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -34,7 +34,7 @@ org.projectlombok lombok - 1.18.4 + 1.18.6 provided From ca4505acef4415a7dbe8cd21cf02287acfee0c1b Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Tue, 12 Feb 2019 09:34:29 +0100 Subject: [PATCH 0602/5615] Move "checking the system" log to checksEnabled body Follow up on #1231 --- .../main/java/org/testcontainers/DockerClientFactory.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 5125993e3f7..72cff0217cd 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -125,11 +125,10 @@ public DockerClient client() { ryukContainerId = ResourceReaper.start(hostIpAddress, client); log.info("Ryuk started - will monitor and terminate Testcontainers containers on JVM exit"); } - - VisibleAssertions.info("Checking the system..."); - + boolean checksEnabled = !TestcontainersConfiguration.getInstance().isDisableChecks(); if (checksEnabled) { + VisibleAssertions.info("Checking the system..."); checkDockerVersion(version.getVersion()); if (ryukContainerId != null) { checkDiskSpace(client, ryukContainerId); From b6de1ac6de2bc1a68c5a789661f48341f8cb858c Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 12 Feb 2019 12:19:30 +0100 Subject: [PATCH 0603/5615] Add OpenJDK EA builds to the matrix (#1154) * Add OpenJDK EA builds to the matrix * Update gradle-wrapper.properties * Update Lombok to 1.18.5 * Update to Lombok 1.18.6 * Update gradle-wrapper.properties --- .travis.yml | 5 +++++ build.gradle | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9d72b3eb086..450556b2269 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,11 @@ jobs: jdk: openjdk11 script: ./gradlew testcontainers:check --scan --no-daemon + - stage: test + env: [ NAME=core ] + jdk: openjdk-ea + script: ./gradlew testcontainers:check --scan --no-daemon + - env: [ NAME=selenium ] script: ./gradlew selenium:check --scan --no-daemon diff --git a/build.gradle b/build.gradle index ade1ea3bcf0..318562a8936 100644 --- a/build.gradle +++ b/build.gradle @@ -22,7 +22,7 @@ subprojects { compileJava.options.encoding = 'UTF-8' lombok { - version = '1.18.2' + version = '1.18.6' } task delombok(type: io.franzbecker.gradle.lombok.task.DelombokTask) { From 885686ebacb90b116defa79119971dac9d8703c5 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 14 Feb 2019 13:00:06 +0000 Subject: [PATCH 0604/5615] Disable Oracle tests (#1240) --- .../test/java/org/testcontainers/junit/SimpleOracleTest.java | 2 ++ modules/jdbc-test/src/test/resources/testcontainers.properties | 1 - .../org/testcontainers/containers/OracleJDBCDriverTest.java | 2 ++ modules/oracle-xe/src/test/resources/testcontainers.properties | 1 - 4 files changed, 4 insertions(+), 2 deletions(-) delete mode 100644 modules/jdbc-test/src/test/resources/testcontainers.properties delete mode 100644 modules/oracle-xe/src/test/resources/testcontainers.properties diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleOracleTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleOracleTest.java index 5c47d3e2f92..2fedcaf08e1 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleOracleTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleOracleTest.java @@ -2,6 +2,7 @@ import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.testcontainers.containers.OracleContainer; @@ -15,6 +16,7 @@ /** * @author gusohal */ +@Ignore public class SimpleOracleTest { @Rule diff --git a/modules/jdbc-test/src/test/resources/testcontainers.properties b/modules/jdbc-test/src/test/resources/testcontainers.properties deleted file mode 100644 index 6335223e8b2..00000000000 --- a/modules/jdbc-test/src/test/resources/testcontainers.properties +++ /dev/null @@ -1 +0,0 @@ -oracle.container.image=wnameless/oracle-xe-11g@sha256:825ba799432809fc7200bb1d7ef954973a8991d7702a860c87177fe05301f7da diff --git a/modules/oracle-xe/src/test/java/org/testcontainers/containers/OracleJDBCDriverTest.java b/modules/oracle-xe/src/test/java/org/testcontainers/containers/OracleJDBCDriverTest.java index 61a8d9b8d4b..fcc09ab69a4 100644 --- a/modules/oracle-xe/src/test/java/org/testcontainers/containers/OracleJDBCDriverTest.java +++ b/modules/oracle-xe/src/test/java/org/testcontainers/containers/OracleJDBCDriverTest.java @@ -4,6 +4,7 @@ import com.zaxxer.hikari.HikariDataSource; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.ResultSetHandler; +import org.junit.Ignore; import org.junit.Test; import java.sql.ResultSet; @@ -14,6 +15,7 @@ /** * @author gusohal */ +@Ignore public class OracleJDBCDriverTest { @Test diff --git a/modules/oracle-xe/src/test/resources/testcontainers.properties b/modules/oracle-xe/src/test/resources/testcontainers.properties deleted file mode 100644 index 6335223e8b2..00000000000 --- a/modules/oracle-xe/src/test/resources/testcontainers.properties +++ /dev/null @@ -1 +0,0 @@ -oracle.container.image=wnameless/oracle-xe-11g@sha256:825ba799432809fc7200bb1d7ef954973a8991d7702a860c87177fe05301f7da From 946307fd83065d9b1dfb083d2e612ec054f508f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Fri, 15 Feb 2019 05:36:58 +0000 Subject: [PATCH 0605/5615] Bump assertj-core from 3.11.1 to 3.12.0 in /modules/neo4j Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.11.1 to 3.12.0. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.11.1...assertj-core-3.12.0) Signed-off-by: dependabot[bot] --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 29634671a69..74860e014a1 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -4,5 +4,5 @@ dependencies { compile project(":testcontainers") testCompile "org.neo4j.driver:neo4j-java-driver:1.7.2" - testCompile 'org.assertj:assertj-core:3.11.1' + testCompile 'org.assertj:assertj-core:3.12.0' } From 9358a51a96ffd17483f05a01174ef2de3d5b34e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Fri, 15 Feb 2019 06:55:56 +0000 Subject: [PATCH 0606/5615] Bump assertj-core from 3.11.1 to 3.12.0 in /core Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.11.1 to 3.12.0. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.11.1...assertj-core-3.12.0) Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 419cd98d9f8..556d3cddef9 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -129,6 +129,6 @@ dependencies { jarFileTestCompileOnly "org.projectlombok:lombok:${lombok.version}" jarFileTestCompile 'junit:junit:4.12' - jarFileTestCompile 'org.assertj:assertj-core:3.11.1' + jarFileTestCompile 'org.assertj:assertj-core:3.12.0' jarFileTestCompile 'org.ow2.asm:asm-debug-all:5.2' } From 521292f05084daefd38d2607481f49cbe8e4eb1c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Fri, 15 Feb 2019 07:34:32 +0000 Subject: [PATCH 0607/5615] Bump assertj-core from 3.11.1 to 3.12.0 in /modules/pulsar Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.11.1 to 3.12.0. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.11.1...assertj-core-3.12.0) Signed-off-by: dependabot[bot] --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 8e1d707f818..2a0d0bfa345 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -4,5 +4,5 @@ dependencies { compile project(':testcontainers') testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.2.1' - testCompile group: 'org.assertj', name: 'assertj-core', version: '3.11.1' + testCompile group: 'org.assertj', name: 'assertj-core', version: '3.12.0' } From 47de158be349b1c8cd81d3660cc7112e393d1128 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Fri, 15 Feb 2019 08:13:33 +0000 Subject: [PATCH 0608/5615] Bump assertj-core from 3.11.1 to 3.12.0 in /modules/kafka (#1243) --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index 6814d0fca1a..b2b767a2dcc 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -4,6 +4,6 @@ dependencies { compile project(':testcontainers') testCompile 'org.apache.kafka:kafka-clients:2.1.0' - testCompile 'org.assertj:assertj-core:3.11.1' + testCompile 'org.assertj:assertj-core:3.12.0' testCompile 'com.google.guava:guava:23.0' } From e0f361ae5372d7ba52da019f0065b89ec90f593c Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Tue, 19 Feb 2019 21:46:41 +0100 Subject: [PATCH 0609/5615] Migrate examples project to Gradle subproject (#1177) --- .dependabot/config.yml | 2 +- .travis.yml | 3 + circle.yml | 13 +-- examples/README.md | 4 - examples/disque-job-queue/build.gradle | 17 ++++ examples/disque-job-queue/pom.xml | 44 ---------- examples/linked-container/build.gradle | 16 ++++ examples/linked-container/pom.xml | 50 ----------- examples/pom.xml | 87 ------------------- .../redis-backed-cache-testng/build.gradle | 21 +++++ examples/redis-backed-cache-testng/pom.xml | 51 ----------- examples/redis-backed-cache/build.gradle | 17 ++++ examples/redis-backed-cache/pom.xml | 44 ---------- examples/selenium-container/build.gradle | 18 ++++ examples/selenium-container/pom.xml | 67 -------------- .../src/test/java/SeleniumContainerTest.java | 2 +- examples/settings.gradle | 14 +++ examples/singleton-container/build.gradle | 18 ++++ .../main/java/com/example/cache/Cache.java | 4 +- .../com/example/cache/RedisBackedCache.java | 48 +++++----- .../com/example/AbstractIntegrationTest.java | 12 +-- .../com/example/BarConcreteTestClass.java | 26 +++--- .../com/example/FooConcreteTestClass.java | 16 ++-- examples/spring-boot/build.gradle | 19 ++++ examples/spring-boot/pom.xml | 65 -------------- modules/build.gradle | 0 settings.gradle | 2 +- 27 files changed, 201 insertions(+), 479 deletions(-) create mode 100644 examples/disque-job-queue/build.gradle delete mode 100644 examples/disque-job-queue/pom.xml create mode 100644 examples/linked-container/build.gradle delete mode 100644 examples/linked-container/pom.xml delete mode 100644 examples/pom.xml create mode 100644 examples/redis-backed-cache-testng/build.gradle delete mode 100644 examples/redis-backed-cache-testng/pom.xml create mode 100644 examples/redis-backed-cache/build.gradle delete mode 100644 examples/redis-backed-cache/pom.xml create mode 100644 examples/selenium-container/build.gradle delete mode 100644 examples/selenium-container/pom.xml create mode 100644 examples/settings.gradle create mode 100644 examples/singleton-container/build.gradle create mode 100644 examples/spring-boot/build.gradle delete mode 100644 examples/spring-boot/pom.xml create mode 100644 modules/build.gradle diff --git a/.dependabot/config.yml b/.dependabot/config.yml index c5ed0c759fb..45dc11c034b 100644 --- a/.dependabot/config.yml +++ b/.dependabot/config.yml @@ -80,7 +80,7 @@ update_configs: update_schedule: "daily" # Examples - - package_manager: "java:maven" + - package_manager: "java:gradle" directory: "/examples" update_schedule: "daily" automerged_updates: diff --git a/.travis.yml b/.travis.yml index 450556b2269..e5a282092d3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,6 +31,9 @@ jobs: - env: [ NAME=modules ] script: ./gradlew check -x testcontainers:check -x selenium:check --scan --no-daemon + - env: [ NAME=examples ] + script: cd examples && ../gradlew check --scan --no-daemon + # Run Docker-in-Docker tests inside Alpine - env: [ NAME="docker-in-alpine-docker" ] script: diff --git a/circle.yml b/circle.yml index 81b77d76355..eeafac5237f 100644 --- a/circle.yml +++ b/circle.yml @@ -87,27 +87,18 @@ jobs: examples: steps: - checkout - - restore_cache: - key: examples-maven - run: - name: Publish JARs to Maven Local - command: ./gradlew --no-daemon publishToMavenLocal - - run: - command: mvn -B -f examples/pom.xml -Dtestcontainers.group=org.testcontainers -Dtestcontainers.version=unspecified test + command: cd examples && ../gradlew check - run: name: Save test results command: | mkdir -p ~/junit/ - find examples -type f -regex ".*/target/surefire-reports/.*xml" -exec cp {} ~/junit/ \; + find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; when: always - store_test_results: path: ~/junit - store_artifacts: path: ~/junit - - save_cache: - paths: - - ~/.m2 - key: examples-maven workflows: version: 2 diff --git a/examples/README.md b/examples/README.md index 33ba2bc390b..42d199db865 100644 --- a/examples/README.md +++ b/examples/README.md @@ -7,7 +7,3 @@ The code in this repository accompanies the following blog posts: * [JUnit integration testing with Docker and Testcontainers](https://rnorth.org/junit-integration-testing-with-docker-and-testcontainers) * [Fun with Disque, Java and Spinach](https://rnorth.org/fun-with-disque-java-and-spinach) * [Better JUnit Selenium testing with Docker and Testcontainers](https://rnorth.org/better-junit-selenium-testing-with-docker-and-testcontainers) - -Note that this examples project uses JitPack to obtain SNAPSHOT versions of the latest build of TestContainers. For -normal usage, refer to the [documentation](http://testcontainers.viewdocs.io/testcontainers-java/#user-content-maven-dependencies) -for setting the correct maven dependencies for your project. \ No newline at end of file diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle new file mode 100644 index 00000000000..000b94b9d98 --- /dev/null +++ b/examples/disque-job-queue/build.gradle @@ -0,0 +1,17 @@ +plugins { + id 'java' +} + +repositories { + jcenter() +} + +dependencies { + compileOnly 'org.projectlombok:lombok:1.18.4' + implementation 'biz.paluch.redis:spinach:0.3' + implementation 'com.google.code.gson:gson:2.8.5' + implementation 'com.google.guava:guava:23.0' + testImplementation 'ch.qos.logback:logback-classic:1.2.3' + testImplementation 'org.mockito:mockito-all:1.10.19' + testImplementation 'org.testcontainers:testcontainers' +} diff --git a/examples/disque-job-queue/pom.xml b/examples/disque-job-queue/pom.xml deleted file mode 100644 index ba94d220798..00000000000 --- a/examples/disque-job-queue/pom.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - testcontainers-java-examples - org.testcontainers.examples - NOVERSION - - 4.0.0 - - disque-job-queue - - - biz.paluch.redis - spinach - 0.3 - - - com.google.code.gson - gson - 2.8.5 - - - com.google.guava - guava - 23.0 - - - org.slf4j - slf4j-api - 1.7.25 - provided - - - ch.qos.logback - logback-classic - 1.2.3 - test - - - - - \ No newline at end of file diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle new file mode 100644 index 00000000000..79c2403fdfb --- /dev/null +++ b/examples/linked-container/build.gradle @@ -0,0 +1,16 @@ +plugins { + id 'java' +} + +repositories { + jcenter() +} +dependencies { + compileOnly 'org.slf4j:slf4j-api:1.7.25' + implementation 'com.squareup.okhttp3:okhttp:3.12.1' + implementation 'org.json:json:20180813' + testImplementation 'org.postgresql:postgresql:42.2.5' + testImplementation 'ch.qos.logback:logback-classic:1.2.3' + testImplementation 'org.testcontainers:postgresql' +} + diff --git a/examples/linked-container/pom.xml b/examples/linked-container/pom.xml deleted file mode 100644 index e39c76d56ab..00000000000 --- a/examples/linked-container/pom.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - testcontainers-java-examples - org.testcontainers.examples - NOVERSION - - 4.0.0 - - linked-container - - - - ${testcontainers.group} - postgresql - ${testcontainers.version} - - - org.postgresql - postgresql - 42.2.5 - test - - - org.slf4j - slf4j-api - 1.7.25 - provided - - - ch.qos.logback - logback-classic - 1.2.3 - test - - - com.squareup.okhttp3 - okhttp - 3.12.1 - - - org.json - json - 20180813 - - - - \ No newline at end of file diff --git a/examples/pom.xml b/examples/pom.xml deleted file mode 100644 index f5eb3826fc6..00000000000 --- a/examples/pom.xml +++ /dev/null @@ -1,87 +0,0 @@ - - - 4.0.0 - - org.testcontainers.examples - testcontainers-java-examples - NOVERSION - - redis-backed-cache - redis-backed-cache-testng - disque-job-queue - selenium-container - spring-boot - linked-container - - - pom - - - - ${testcontainers.group} - testcontainers - ${testcontainers.version} - - - - junit - junit - 4.12 - provided - - - - - org.projectlombok - lombok - 1.18.6 - provided - - - - - org.rnorth.visible-assertions - visible-assertions - 2.1.2 - test - - - org.mockito - mockito-all - 1.10.19 - test - - - org.postgresql - postgresql - 42.2.5 - - - - - - - maven-compiler-plugin - 3.8.0 - - 1.8 - 1.8 - - - - - - - UTF-8 - com.github.testcontainers.testcontainers-java - -SNAPSHOT - - - - - - jitpack.io - https://jitpack.io - - - diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle new file mode 100644 index 00000000000..c213af0b205 --- /dev/null +++ b/examples/redis-backed-cache-testng/build.gradle @@ -0,0 +1,21 @@ +plugins { + id 'java' +} + +repositories { + jcenter() +} + +dependencies { + compileOnly 'org.slf4j:slf4j-api:1.7.25' + implementation 'redis.clients:jedis:3.0.1' + implementation 'com.google.code.gson:gson:2.8.5' + implementation 'com.google.guava:guava:23.0' + testImplementation 'org.testcontainers:testcontainers' + testImplementation 'ch.qos.logback:logback-classic:1.2.3' + testImplementation 'org.testng:testng:6.14.3' +} + +test { + useTestNG() +} diff --git a/examples/redis-backed-cache-testng/pom.xml b/examples/redis-backed-cache-testng/pom.xml deleted file mode 100644 index 1012f2ddc32..00000000000 --- a/examples/redis-backed-cache-testng/pom.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - testcontainers-java-examples - org.testcontainers.examples - NOVERSION - - 4.0.0 - - redis-backed-cache-testng - Redis Backend Cache testing example using TestNG - - - redis.clients - jedis - 3.0.1 - - - com.google.code.gson - gson - 2.8.5 - - - com.google.guava - guava - 23.0 - - - org.slf4j - slf4j-api - 1.7.25 - provided - - - ch.qos.logback - logback-classic - 1.2.3 - test - - - org.testng - testng - 6.14.3 - test - - - - - \ No newline at end of file diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle new file mode 100644 index 00000000000..c6370fceac9 --- /dev/null +++ b/examples/redis-backed-cache/build.gradle @@ -0,0 +1,17 @@ +plugins { + id 'java' +} + +repositories { + jcenter() +} + +dependencies { + compileOnly 'org.slf4j:slf4j-api:1.7.25' + implementation 'redis.clients:jedis:3.0.1' + implementation 'com.google.code.gson:gson:2.8.5' + implementation 'com.google.guava:guava:23.0' + testImplementation 'org.testcontainers:testcontainers' + testImplementation 'junit:junit:4.12' + testImplementation 'ch.qos.logback:logback-classic:1.2.3' +} diff --git a/examples/redis-backed-cache/pom.xml b/examples/redis-backed-cache/pom.xml deleted file mode 100644 index 1cff4291376..00000000000 --- a/examples/redis-backed-cache/pom.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - testcontainers-java-examples - org.testcontainers.examples - NOVERSION - - 4.0.0 - - redis-backed-cache - - - redis.clients - jedis - 3.0.1 - - - com.google.code.gson - gson - 2.8.5 - - - com.google.guava - guava - 23.0 - - - org.slf4j - slf4j-api - 1.7.25 - provided - - - ch.qos.logback - logback-classic - 1.2.3 - test - - - - - \ No newline at end of file diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle new file mode 100644 index 00000000000..17e7e21c776 --- /dev/null +++ b/examples/selenium-container/build.gradle @@ -0,0 +1,18 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '2.1.2.RELEASE' +} +apply plugin: 'io.spring.dependency-management' + +repositories { + jcenter() +} + +dependencies { + implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.14.0' + implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.14.0' + implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' + implementation 'org.springframework.boot:spring-boot-starter-web' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.testcontainers:selenium' +} diff --git a/examples/selenium-container/pom.xml b/examples/selenium-container/pom.xml deleted file mode 100644 index 1ef62843bb7..00000000000 --- a/examples/selenium-container/pom.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - - testcontainers-java-examples - org.testcontainers.examples - NOVERSION - - 4.0.0 - - selenium-container - - - - - org.springframework.boot - spring-boot-dependencies - 2.1.2.RELEASE - pom - import - - - - - - - org.seleniumhq.selenium - selenium-remote-driver - - - org.seleniumhq.selenium - selenium-firefox-driver - - - org.seleniumhq.selenium - selenium-chrome-driver - - - ${testcontainers.group} - selenium - ${testcontainers.version} - - - - org.springframework.boot - spring-boot-starter-web - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - - org.springframework.boot - spring-boot-maven-plugin - 2.1.2.RELEASE - - - - - diff --git a/examples/selenium-container/src/test/java/SeleniumContainerTest.java b/examples/selenium-container/src/test/java/SeleniumContainerTest.java index a084243b6e9..c21d6bf4eca 100644 --- a/examples/selenium-container/src/test/java/SeleniumContainerTest.java +++ b/examples/selenium-container/src/test/java/SeleniumContainerTest.java @@ -36,7 +36,7 @@ public class SeleniumContainerTest { @Rule public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() .withCapabilities(new ChromeOptions()) - .withRecordingMode(RECORD_ALL, new File("target")); + .withRecordingMode(RECORD_ALL, new File("build")); @Test public void simplePlainSeleniumTest() { diff --git a/examples/settings.gradle b/examples/settings.gradle new file mode 100644 index 00000000000..05070116149 --- /dev/null +++ b/examples/settings.gradle @@ -0,0 +1,14 @@ +rootProject.name = 'testcontainers-examples' + +includeBuild '..' + +include( + 'disque-job-queue', + 'linked-container', + 'redis-backed-cache', + 'redis-backed-cache-testng', + 'selenium-container', + 'singleton-container', + 'spring-boot' +) + diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle new file mode 100644 index 00000000000..3c3e3b043eb --- /dev/null +++ b/examples/singleton-container/build.gradle @@ -0,0 +1,18 @@ +plugins { + id 'java' +} + +repositories { + jcenter() +} + +dependencies { + + implementation 'redis.clients:jedis:3.0.1' + implementation 'com.google.code.gson:gson:2.8.5' + implementation 'com.google.guava:guava:23.0' + compileOnly 'org.slf4j:slf4j-api:1.7.25' + + testImplementation 'ch.qos.logback:logback-classic:1.2.3' + testImplementation 'org.testcontainers:testcontainers' +} diff --git a/examples/singleton-container/src/main/java/com/example/cache/Cache.java b/examples/singleton-container/src/main/java/com/example/cache/Cache.java index 5c5ba253d6e..986838d20f9 100644 --- a/examples/singleton-container/src/main/java/com/example/cache/Cache.java +++ b/examples/singleton-container/src/main/java/com/example/cache/Cache.java @@ -4,7 +4,7 @@ public interface Cache { - void put(String key, Object value); + void put(String key, Object value); - Optional get(String key, Class expectedClass); + Optional get(String key, Class expectedClass); } \ No newline at end of file diff --git a/examples/singleton-container/src/main/java/com/example/cache/RedisBackedCache.java b/examples/singleton-container/src/main/java/com/example/cache/RedisBackedCache.java index 12585cafe16..34553dde1f4 100644 --- a/examples/singleton-container/src/main/java/com/example/cache/RedisBackedCache.java +++ b/examples/singleton-container/src/main/java/com/example/cache/RedisBackedCache.java @@ -7,28 +7,28 @@ public class RedisBackedCache implements Cache { - private final Jedis jedis; - private final String cacheName; - private final Gson gson; - - public RedisBackedCache(Jedis jedis, String cacheName) { - this.jedis = jedis; - this.cacheName = cacheName; - this.gson = new Gson(); - } - - public void put(String key, Object value) { - String jsonValue = gson.toJson(value); - this.jedis.hset(this.cacheName, key, jsonValue); - } - - public Optional get(String key, Class expectedClass) { - String foundJson = this.jedis.hget(this.cacheName, key); - - if (foundJson == null) { - return Optional.empty(); - } - - return Optional.of(gson.fromJson(foundJson, expectedClass)); - } + private final Jedis jedis; + private final String cacheName; + private final Gson gson; + + public RedisBackedCache(Jedis jedis, String cacheName) { + this.jedis = jedis; + this.cacheName = cacheName; + this.gson = new Gson(); + } + + public void put(String key, Object value) { + String jsonValue = gson.toJson(value); + this.jedis.hset(this.cacheName, key, jsonValue); + } + + public Optional get(String key, Class expectedClass) { + String foundJson = this.jedis.hget(this.cacheName, key); + + if (foundJson == null) { + return Optional.empty(); + } + + return Optional.of(gson.fromJson(foundJson, expectedClass)); + } } diff --git a/examples/singleton-container/src/test/java/com/example/AbstractIntegrationTest.java b/examples/singleton-container/src/test/java/com/example/AbstractIntegrationTest.java index 6daff319753..d779adc5630 100644 --- a/examples/singleton-container/src/test/java/com/example/AbstractIntegrationTest.java +++ b/examples/singleton-container/src/test/java/com/example/AbstractIntegrationTest.java @@ -4,10 +4,10 @@ public abstract class AbstractIntegrationTest { - public static final GenericContainer redis = new GenericContainer("redis:3.0.6") - .withExposedPorts(6379); - - static { - redis.start(); - } + public static final GenericContainer redis = new GenericContainer("redis:3.0.6") + .withExposedPorts(6379); + + static { + redis.start(); + } } diff --git a/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java b/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java index 08a5ebfe2f5..9923b08f1e0 100644 --- a/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java +++ b/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java @@ -13,22 +13,22 @@ public class BarConcreteTestClass extends AbstractIntegrationTest { - private Cache cache; + private Cache cache; - @Before - public void setUp() { - Jedis jedis = new Jedis(redis.getContainerIpAddress(), redis.getMappedPort(6379)); + @Before + public void setUp() { + Jedis jedis = new Jedis(redis.getContainerIpAddress(), redis.getMappedPort(6379)); - cache = new RedisBackedCache(jedis, "bar"); - } + cache = new RedisBackedCache(jedis, "bar"); + } - @Test - public void testInsertValue() { - cache.put("bar", "BAR"); - Optional foundObject = cache.get("bar", String.class); + @Test + public void testInsertValue() { + cache.put("bar", "BAR"); + Optional foundObject = cache.get("bar", String.class); - assertTrue("When inserting an object into the cache, it can be retrieved", foundObject.isPresent()); - assertEquals("When accessing the value of a retrieved object, the value must be the same", "BAR", foundObject.get()); - } + assertTrue("When inserting an object into the cache, it can be retrieved", foundObject.isPresent()); + assertEquals("When accessing the value of a retrieved object, the value must be the same", "BAR", foundObject.get()); + } } diff --git a/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java b/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java index 1196348b258..a57b0d5d4a0 100644 --- a/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java +++ b/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java @@ -22,12 +22,12 @@ public void setUp() throws Exception { cache = new RedisBackedCache(jedis, "foo"); } - @Test - public void testInsertValue() { - cache.put("foo", "FOO"); - Optional foundObject = cache.get("foo", String.class); - - assertTrue("When inserting an object into the cache, it can be retrieved", foundObject.isPresent()); - assertEquals("When accessing the value of a retrieved object, the value must be the same", "FOO", foundObject.get()); - } + @Test + public void testInsertValue() { + cache.put("foo", "FOO"); + Optional foundObject = cache.get("foo", String.class); + + assertTrue("When inserting an object into the cache, it can be retrieved", foundObject.isPresent()); + assertEquals("When accessing the value of a retrieved object, the value must be the same", "FOO", foundObject.get()); + } } diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle new file mode 100644 index 00000000000..1dc5ec7c857 --- /dev/null +++ b/examples/spring-boot/build.gradle @@ -0,0 +1,19 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '2.1.2.RELEASE' +} +apply plugin: 'io.spring.dependency-management' + +repositories { + jcenter() +} + +dependencies { + compileOnly 'org.projectlombok:lombok:1.18.4' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-data-redis' + implementation 'org.springframework.boot:spring-boot-starter-web' + runtimeOnly 'org.postgresql:postgresql' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.testcontainers:postgresql' +} diff --git a/examples/spring-boot/pom.xml b/examples/spring-boot/pom.xml deleted file mode 100644 index d534782a1af..00000000000 --- a/examples/spring-boot/pom.xml +++ /dev/null @@ -1,65 +0,0 @@ - - - - - testcontainers-java-examples - org.testcontainers.examples - NOVERSION - - 4.0.0 - - spring-boot - - - UTF-8 - 1.8 - - - - - - org.springframework.boot - spring-boot-dependencies - 2.1.2.RELEASE - pom - import - - - - - - - org.springframework.boot - spring-boot-starter-data-redis - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-data-jpa - - - org.springframework.boot - spring-boot-starter-test - test - - - ${testcontainers.group} - postgresql - ${testcontainers.version} - - - - - - - org.springframework.boot - spring-boot-maven-plugin - 2.1.2.RELEASE - - - - diff --git a/modules/build.gradle b/modules/build.gradle new file mode 100644 index 00000000000..e69de29bb2d diff --git a/settings.gradle b/settings.gradle index dbbd0b6a25c..e68987a50b9 100644 --- a/settings.gradle +++ b/settings.gradle @@ -38,4 +38,4 @@ file('modules').eachDir { dir -> } include "docs-examples" -project(":docs-examples").projectDir = file("docs/examples") +project(":docs-examples").projectDir = file("docs/examples") \ No newline at end of file From 630d138f42e201ed3b57a7159cbfdcb04d1a9dc7 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Tue, 19 Feb 2019 21:56:02 +0100 Subject: [PATCH 0610/5615] Add empty build.gradle to examples projects Fixes #1258 --- examples/build.gradle | 1 + 1 file changed, 1 insertion(+) create mode 100644 examples/build.gradle diff --git a/examples/build.gradle b/examples/build.gradle new file mode 100644 index 00000000000..3130de8492e --- /dev/null +++ b/examples/build.gradle @@ -0,0 +1 @@ +// empty build.gradle for dependabot \ No newline at end of file From b42b1cda61d65b238c9fcc8323927df36d05cad2 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Wed, 20 Feb 2019 19:55:18 +0100 Subject: [PATCH 0611/5615] Single item includes of examples subprojects for Dependabot (#1266) --- examples/settings.gradle | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/examples/settings.gradle b/examples/settings.gradle index 05070116149..52617937c04 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -2,13 +2,12 @@ rootProject.name = 'testcontainers-examples' includeBuild '..' -include( - 'disque-job-queue', - 'linked-container', - 'redis-backed-cache', - 'redis-backed-cache-testng', - 'selenium-container', - 'singleton-container', - 'spring-boot' -) +// explicit include to allow Dependabot to autodiscover subprojects +include 'disque-job-queue' +include 'linked-container' +include 'redis-backed-cache' +include 'redis-backed-cache-testng' +include 'selenium-container' +include 'singleton-container' +include 'spring-boot' From f9184506ef8f569848d0be96834ddcc0c886c8c3 Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Sat, 23 Feb 2019 08:13:04 +0100 Subject: [PATCH 0612/5615] Add additional convenience methods for Neo4j containers (#1246) * Add additional convenience methods for Neo4j containers. * Make format method private, adapt test. --- docs/modules/databases/neo4j.md | 70 ++++++++++++++++ modules/neo4j/build.gradle | 26 ++++++ .../neo4j/demos/plugins/HelloWorld.java | 15 ++++ .../containers/Neo4jContainer.java | 67 ++++++++++++++++ .../containers/Neo4jContainerTest.java | 75 +++++++++++++++++- .../src/test/resources/test-graph.db/neostore | Bin 0 -> 8192 bytes .../test-graph.db/neostore.counts.db.a | Bin 0 -> 96 bytes .../test-graph.db/neostore.counts.db.b | Bin 0 -> 160 bytes .../test/resources/test-graph.db/neostore.id | Bin 0 -> 9 bytes .../test-graph.db/neostore.labelscanstore.db | Bin 0 -> 49152 bytes .../test-graph.db/neostore.labeltokenstore.db | Bin 0 -> 8190 bytes .../neostore.labeltokenstore.db.id | Bin 0 -> 9 bytes .../neostore.labeltokenstore.db.names | Bin 0 -> 8192 bytes .../neostore.labeltokenstore.db.names.id | Bin 0 -> 9 bytes .../test-graph.db/neostore.nodestore.db | Bin 0 -> 8190 bytes .../test-graph.db/neostore.nodestore.db.id | Bin 0 -> 137 bytes .../neostore.nodestore.db.labels | Bin 0 -> 8192 bytes .../neostore.nodestore.db.labels.id | Bin 0 -> 9 bytes .../test-graph.db/neostore.propertystore.db | Bin 0 -> 8159 bytes .../neostore.propertystore.db.arrays | Bin 0 -> 8192 bytes .../neostore.propertystore.db.arrays.id | Bin 0 -> 161 bytes .../neostore.propertystore.db.id | Bin 0 -> 137 bytes .../neostore.propertystore.db.index | Bin 0 -> 8190 bytes .../neostore.propertystore.db.index.id | Bin 0 -> 9 bytes .../neostore.propertystore.db.index.keys | Bin 0 -> 8192 bytes .../neostore.propertystore.db.index.keys.id | Bin 0 -> 9 bytes .../neostore.propertystore.db.strings | Bin 0 -> 8192 bytes .../neostore.propertystore.db.strings.id | Bin 0 -> 9 bytes .../neostore.relationshipgroupstore.db | Bin 0 -> 8192 bytes .../neostore.relationshipgroupstore.db.id | Bin 0 -> 9 bytes .../neostore.relationshipstore.db | 0 .../neostore.relationshipstore.db.id | Bin 0 -> 9 bytes .../neostore.relationshiptypestore.db | 0 .../neostore.relationshiptypestore.db.id | Bin 0 -> 9 bytes .../neostore.relationshiptypestore.db.names | Bin 0 -> 8192 bytes ...neostore.relationshiptypestore.db.names.id | Bin 0 -> 9 bytes .../test-graph.db/neostore.schemastore.db | Bin 0 -> 8192 bytes .../test-graph.db/neostore.schemastore.db.id | Bin 0 -> 9 bytes .../test-graph.db/neostore.transaction.db.0 | Bin 0 -> 1150 bytes 39 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 modules/neo4j/src/custom-neo4j-plugin/java/ac/simons/neo4j/demos/plugins/HelloWorld.java create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.counts.db.a create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.counts.db.b create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.id create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.labelscanstore.db create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.labeltokenstore.db create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.labeltokenstore.db.id create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.labeltokenstore.db.names create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.labeltokenstore.db.names.id create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.nodestore.db create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.nodestore.db.id create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.nodestore.db.labels create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.nodestore.db.labels.id create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.propertystore.db create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.propertystore.db.arrays create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.propertystore.db.arrays.id create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.propertystore.db.id create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.propertystore.db.index create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.propertystore.db.index.id create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.propertystore.db.index.keys create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.propertystore.db.index.keys.id create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.propertystore.db.strings create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.propertystore.db.strings.id create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.relationshipgroupstore.db create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.relationshipgroupstore.db.id create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.relationshipstore.db create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.relationshipstore.db.id create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.relationshiptypestore.db create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.relationshiptypestore.db.id create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.relationshiptypestore.db.names create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.relationshiptypestore.db.names.id create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.schemastore.db create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.schemastore.db.id create mode 100644 modules/neo4j/src/test/resources/test-graph.db/neostore.transaction.db.0 diff --git a/docs/modules/databases/neo4j.md b/docs/modules/databases/neo4j.md index 6762d01a6a8..aaa18d225c9 100644 --- a/docs/modules/databases/neo4j.md +++ b/docs/modules/databases/neo4j.md @@ -68,6 +68,76 @@ public class ExampleTest { You are not limited to Unit tests and can of course use an instance of the Neo4j Testcontainer in vanilla Java code as well. +## Additional features + +### Disable authentication + +Authentication can be disabled: + +```java +@Testcontainers +public class ExampleTest { + + @Container + Neo4jContainer neo4jContainer = new Neo4jContainer() + .withoutAuthentication(); +} +``` + +### Neo4j-Configuration + +Neo4j's Docker image needs Neo4j configuration options in a dedicated format. +The container takes care of that and you can configure the database with standard options like the following: + +```java +@Testcontainers +public class ExampleTest { + + @Container + Neo4jContainer neo4jContainer = new Neo4jContainer() + .withNeo4jConfig("dbms.security.procedures.unrestricted", "apoc.*,algo.*"); +} +``` + +### Add custom plugins + +Custom plugins, like APOC, can be copied over to the container from any classpath or host resource like this: + +```java +@Testcontainers +public class ExampleTest { + + @Container + Neo4jContainer neo4jContainer = new Neo4jContainer() + .withPlugins(MountableFile.forClasspathResource("/apoc-3.5.0.1-all.jar")); +} +``` + +Whole directories work as well: + +```java +@Testcontainers +public class ExampleTest { + + @Container + Neo4jContainer neo4jContainer = new Neo4jContainer() + .withPlugins(MountableFile.forClasspathResource("/my-plugins")); +} +``` + +### Start the container with a predefined database + +If you have an existing database (`graph.db`) you want to work with, copy it over to the container like this: + +```java +@Testcontainers +public class ExampleTest { + + @Container + Neo4jContainer neo4jContainer = new Neo4jContainer() + .withDatabase(MountableFile.forClasspathResource("/test-graph.db")); +} +``` ## Choose your Neo4j license diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 74860e014a1..18f0ff0b718 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -1,6 +1,32 @@ description = "TestContainers :: Neo4j" +def generatedResourcesDir = new File(project.buildDir, "generated-resources") +def customNeo4jPluginDestinationDir = new File(generatedResourcesDir, "custom-plugins") + +sourceSets { + customNeo4jPlugin { + java { + srcDir 'src/custom-neo4j-plugin' + } + } + test { + resources { + srcDir generatedResourcesDir + } + } +} + +task customNeo4jPluginJar(type: Jar) { + from sourceSets.customNeo4jPlugin.output + archiveName = "hello-world.jar" + destinationDir = customNeo4jPluginDestinationDir +} + +test.dependsOn customNeo4jPluginJar + dependencies { + customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.3" + compile project(":testcontainers") testCompile "org.neo4j.driver:neo4j-java-driver:1.7.2" diff --git a/modules/neo4j/src/custom-neo4j-plugin/java/ac/simons/neo4j/demos/plugins/HelloWorld.java b/modules/neo4j/src/custom-neo4j-plugin/java/ac/simons/neo4j/demos/plugins/HelloWorld.java new file mode 100644 index 00000000000..cbfcdc03a55 --- /dev/null +++ b/modules/neo4j/src/custom-neo4j-plugin/java/ac/simons/neo4j/demos/plugins/HelloWorld.java @@ -0,0 +1,15 @@ +package ac.simons.neo4j.demos.plugins; + +import org.neo4j.procedure.Description; +import org.neo4j.procedure.Name; +import org.neo4j.procedure.UserFunction; + +public class HelloWorld { + + @UserFunction("ac.simons.helloWorld") + @Description("Simple Hello World") + public String helloWorld(@Name("name") String name) { + + return "Hello, " + name; + } +} diff --git a/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java index a8072d350ba..fc545122219 100644 --- a/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java +++ b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java @@ -12,6 +12,7 @@ import org.testcontainers.containers.wait.strategy.WaitAllStrategy; import org.testcontainers.containers.wait.strategy.WaitStrategy; import org.testcontainers.utility.LicenseAcceptance; +import org.testcontainers.utility.MountableFile; /** * Testcontainer for Neo4j. @@ -164,10 +165,76 @@ public S withAdminPassword(final String adminPassword) { return self(); } + /** + * Disables authentication. + * + * @return This container. + */ + public S withoutAuthentication() { + return withAdminPassword(null); + } + + /** + * Copies an existing {@code graph.db} folder into the container. This can either be a classpath resource or a + * host resource. Please have a look at the factory methods in {@link MountableFile}. + *
+ * If you want to map your database into the container instead of copying them, please use {@code #withClasspathResourceMapping}, + * but this will only work when your test does not run in a container itself. + *
+ * Mapping would work like this: + *
+     *      @Container
+     *      private static final Neo4jContainer databaseServer = new Neo4jContainer<>()
+     *          .withClasspathResourceMapping("/test-graph.db", "/data/databases/graph.db", BindMode.READ_WRITE);
+     * 
+ * + * @param graphDb The graph.db folder to copy into the container + * @return This container. + */ + public S withDatabase(MountableFile graphDb) { + return withCopyFileToContainer(graphDb, "/data/databases/graph.db"); + } + + /** + * Adds plugins to the given directory to the container. If {@code plugins} denotes a directory, than all of that + * directory is mapped to Neo4j's plugins. Otherwise, single resources are copied over. + *
+ * If you want to map your plugins into the container instead of copying them, please use {@code #withClasspathResourceMapping}, + * but this will only work when your test does not run in a container itself. + * + * @param plugins + * @return This container. + */ + public S withPlugins(MountableFile plugins) { + return withCopyFileToContainer(plugins, "/var/lib/neo4j/plugins/"); + } + + /** + * Adds Neo4j configuration properties to the container. The properties can be added as in the official Neo4j + * configuration, the method automatically translate them into the format required by the Neo4j container. + * + * @param key The key to configure, i.e. {@code dbms.security.procedures.unrestricted} + * @param value The value to set + * @return This container. + */ + public S withNeo4jConfig(String key, String value) { + + addEnv(formatConfigurationKey(key), value); + return self(); + } + /** * @return The admin password for the neo4j account or literal null if auth is disabled. */ public String getAdminPassword() { return adminPassword; } + + private static String formatConfigurationKey(String plainConfigKey) { + final String prefix = "NEO4J_"; + + return String.format("%s%s", prefix, plainConfigKey + .replaceAll("_", "__") + .replaceAll("\\.", "_")); + } } diff --git a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java index 5908ffbd80d..cbbe5cb5729 100644 --- a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java +++ b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java @@ -10,7 +10,10 @@ import org.neo4j.driver.v1.AuthTokens; import org.neo4j.driver.v1.Driver; import org.neo4j.driver.v1.GraphDatabase; +import org.neo4j.driver.v1.Record; import org.neo4j.driver.v1.Session; +import org.neo4j.driver.v1.StatementResult; +import org.testcontainers.utility.MountableFile; /** * Tests of functionality special to the Neo4jContainer. @@ -26,7 +29,7 @@ public class Neo4jContainerTest { public void shouldDisableAuthentication() { try ( - Neo4jContainer neo4jContainer = new Neo4jContainer().withAdminPassword(null); + Neo4jContainer neo4jContainer = new Neo4jContainer().withoutAuthentication(); ) { neo4jContainer.start(); try (Driver driver = getDriver(neo4jContainer); @@ -38,6 +41,63 @@ public void shouldDisableAuthentication() { } } + @Test + public void shouldCopyDatabase() { + try ( + Neo4jContainer neo4jContainer = new Neo4jContainer() + .withDatabase(MountableFile.forClasspathResource("/test-graph.db")); + ) { + neo4jContainer.start(); + try ( + Driver driver = getDriver(neo4jContainer); + Session session = driver.session() + ) { + StatementResult result = session.run("MATCH (t:Thing) RETURN t"); + assertThat(result.list().stream().map(r -> r.get("t").get("name").asString())) + .containsExactlyInAnyOrder("Thing", "Thing 2", "Thing 3", "A box"); + } + } + } + + @Test + public void shouldCopyPlugins() { + try ( + Neo4jContainer neo4jContainer = new Neo4jContainer() + .withPlugins(MountableFile.forClasspathResource("/custom-plugins")); + ) { + neo4jContainer.start(); + try ( + Driver driver = getDriver(neo4jContainer); + Session session = driver.session() + ) { + assertThatCustomPluginWasCopied(session); + } + } + } + + @Test + public void shouldCopyPlugin() { + try ( + Neo4jContainer neo4jContainer = new Neo4jContainer() + .withPlugins(MountableFile.forClasspathResource("/custom-plugins/hello-world.jar")); + ) { + neo4jContainer.start(); + try ( + Driver driver = getDriver(neo4jContainer); + Session session = driver.session() + ) { + assertThatCustomPluginWasCopied(session); + } + } + } + + private static void assertThatCustomPluginWasCopied(Session session) { + StatementResult result = session.run("RETURN ac.simons.helloWorld('Testcontainers') AS greeting"); + Record singleRecord = result.single(); + assertThat(singleRecord).isNotNull(); + assertThat(singleRecord.get("greeting").asString()).isEqualTo("Hello, Testcontainers"); + } + @Test public void shouldCheckEnterpriseLicense() { assumeThat(Neo4jContainerTest.class.getResource(ACCEPTANCE_FILE_LOCATION)).isNull(); @@ -71,6 +131,19 @@ public void shouldRunEnterprise() { } } + @Test + public void shouldAddConfigToEnvironment() { + + Neo4jContainer neo4jContainer = new Neo4jContainer() + .withNeo4jConfig("dbms.security.procedures.unrestricted", "apoc.*,algo.*") + .withNeo4jConfig("dbms.tx_log.rotation.size", "42M"); + + assertThat(neo4jContainer.getEnvMap()) + .containsEntry("NEO4J_dbms_security_procedures_unrestricted", "apoc.*,algo.*"); + assertThat(neo4jContainer.getEnvMap()) + .containsEntry("NEO4J_dbms_tx__log_rotation_size", "42M"); + } + private static Driver getDriver(Neo4jContainer container) { AuthToken authToken = AuthTokens.none(); diff --git a/modules/neo4j/src/test/resources/test-graph.db/neostore b/modules/neo4j/src/test/resources/test-graph.db/neostore new file mode 100644 index 0000000000000000000000000000000000000000..cbd1bec5bd7d13e71951f0b0feadbfba88594e22 GIT binary patch literal 8192 zcmeIup$&vU5QX8H1So+rHdrJ@fIy%Tr>_LSp>XJ{fdZ}s6c#}ldnC8B1)%>i^OAYl zO+FRTz8$Yl*XI0sKWr*tETk%>t6cHU)Q7Q?SkOC!ckaqDf0o)#v11{5{mWCn$BzBU sMgRc>5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|00D%VrKgC5I9smFU literal 0 HcmV?d00001 diff --git a/modules/neo4j/src/test/resources/test-graph.db/neostore.counts.db.a b/modules/neo4j/src/test/resources/test-graph.db/neostore.counts.db.a new file mode 100644 index 0000000000000000000000000000000000000000..42f972e9874f08f0892f314608912e70ce393999 GIT binary patch literal 96 lcmZQzKm~rO`Of*Jc_qOm`9-M=Okt?9a2_L^g@f@Q3IMy=4T=B& literal 0 HcmV?d00001 diff --git a/modules/neo4j/src/test/resources/test-graph.db/neostore.counts.db.b b/modules/neo4j/src/test/resources/test-graph.db/neostore.counts.db.b new file mode 100644 index 0000000000000000000000000000000000000000..92ac40b0b5cd5d65d552f487b43782af1cf57704 GIT binary patch literal 160 zcmZQzKm~rO`Of*Jc_qOm`9-M=Okt?9a2_j+#Ry|yr&*BY{{sP|3;-3zrVDf{$|x%%}s5FkK+009C72oNAZ cfB*pk1PBlyK!5-N0t5&UAV7cs0RnFYemjl^G5`Po literal 0 HcmV?d00001 diff --git a/modules/neo4j/src/test/resources/test-graph.db/neostore.labeltokenstore.db.names.id b/modules/neo4j/src/test/resources/test-graph.db/neostore.labeltokenstore.db.names.id new file mode 100644 index 0000000000000000000000000000000000000000..bdf13bc6a20a3e9db459fad0db82279367256087 GIT binary patch literal 9 LcmZQzfB+@{01E&E literal 0 HcmV?d00001 diff --git a/modules/neo4j/src/test/resources/test-graph.db/neostore.nodestore.db b/modules/neo4j/src/test/resources/test-graph.db/neostore.nodestore.db new file mode 100644 index 0000000000000000000000000000000000000000..901a05100580e3bb88900c0132947c0c3c1565dc GIT binary patch literal 8190 zcmeIuu?YYm3`Ies3%LKXYETd-u(@9ZUdoHHR?d2flaZgB_06_3uMr?XfB*pk1PBly dK!5-N0t5&UAV7cs0RjXF5FkK+009EO1t!HQ5IX<> literal 0 HcmV?d00001 diff --git a/modules/neo4j/src/test/resources/test-graph.db/neostore.nodestore.db.id b/modules/neo4j/src/test/resources/test-graph.db/neostore.nodestore.db.id new file mode 100644 index 0000000000000000000000000000000000000000..9a0b5a4425b423bdb3b35b096cce4a75ec617632 GIT binary patch literal 137 tcmXZNw*i14001xsQ9+vE|F<%jED7QF`Ec~<%eRxW9~W0QcMrdwUK=ab0L=gZ literal 0 HcmV?d00001 diff --git a/modules/neo4j/src/test/resources/test-graph.db/neostore.nodestore.db.labels b/modules/neo4j/src/test/resources/test-graph.db/neostore.nodestore.db.labels new file mode 100644 index 0000000000000000000000000000000000000000..df8ce2a58c7337a0be82febc80d3e0e21c414f96 GIT binary patch literal 8192 zcmeIu0Sy2E48x#GyMI;Ju>>SgT^KN6z<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK JfB^&30}mg#06_o% literal 0 HcmV?d00001 diff --git a/modules/neo4j/src/test/resources/test-graph.db/neostore.nodestore.db.labels.id b/modules/neo4j/src/test/resources/test-graph.db/neostore.nodestore.db.labels.id new file mode 100644 index 0000000000000000000000000000000000000000..102ef2455533af2377ffa5fb7481ab9dc0684c79 GIT binary patch literal 9 LcmZQzfB;4S015yC literal 0 HcmV?d00001 diff --git a/modules/neo4j/src/test/resources/test-graph.db/neostore.propertystore.db b/modules/neo4j/src/test/resources/test-graph.db/neostore.propertystore.db new file mode 100644 index 0000000000000000000000000000000000000000..a2583744d0fc7704cc84b2d73f300f05832a49df GIT binary patch literal 8159 zcmeIuu?c`c3_P*aF#V3Im$DMVo>p60|yrtz) zY)WzaTHN1@QyWXU7{m3kkEqgTlK=q%1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 I2>e{&1U8{4&;S4c literal 0 HcmV?d00001 diff --git a/modules/neo4j/src/test/resources/test-graph.db/neostore.propertystore.db.arrays b/modules/neo4j/src/test/resources/test-graph.db/neostore.propertystore.db.arrays new file mode 100644 index 0000000000000000000000000000000000000000..5595acd18d12f2329c08828628c4e7bd42baec59 GIT binary patch literal 8192 zcmeIuu?c`s3NkP|*y$jy;WlorHfcWgg1l?O1p?tEIrCxWx6G-lo&Q9%;JAD-b1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ P009C72oNAZ;EBKhzrg?o literal 0 HcmV?d00001 diff --git a/modules/neo4j/src/test/resources/test-graph.db/neostore.propertystore.db.index.id b/modules/neo4j/src/test/resources/test-graph.db/neostore.propertystore.db.index.id new file mode 100644 index 0000000000000000000000000000000000000000..bdf13bc6a20a3e9db459fad0db82279367256087 GIT binary patch literal 9 LcmZQzfB+@{01E&E literal 0 HcmV?d00001 diff --git a/modules/neo4j/src/test/resources/test-graph.db/neostore.propertystore.db.index.keys b/modules/neo4j/src/test/resources/test-graph.db/neostore.propertystore.db.index.keys new file mode 100644 index 0000000000000000000000000000000000000000..34c25065f4c2c1d6b76478898e266d7ab4ff5dcc GIT binary patch literal 8192 zcmeIup$&jA07TIf2qaUm7otHG5`tlRC<8PY t^SJt@hX4Tr1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oU&F-~poQ4OjpG literal 0 HcmV?d00001 diff --git a/modules/neo4j/src/test/resources/test-graph.db/neostore.propertystore.db.index.keys.id b/modules/neo4j/src/test/resources/test-graph.db/neostore.propertystore.db.index.keys.id new file mode 100644 index 0000000000000000000000000000000000000000..df3610a3fa6bb3c111b158edfa7feddad03373d7 GIT binary patch literal 9 LcmZQzfBYwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK IfPv|O2On?%fdBvi literal 0 HcmV?d00001 diff --git a/modules/neo4j/src/test/resources/test-graph.db/neostore.propertystore.db.strings.id b/modules/neo4j/src/test/resources/test-graph.db/neostore.propertystore.db.strings.id new file mode 100644 index 0000000000000000000000000000000000000000..102ef2455533af2377ffa5fb7481ab9dc0684c79 GIT binary patch literal 9 LcmZQzfB;4S015yC literal 0 HcmV?d00001 diff --git a/modules/neo4j/src/test/resources/test-graph.db/neostore.relationshipgroupstore.db b/modules/neo4j/src/test/resources/test-graph.db/neostore.relationshipgroupstore.db new file mode 100644 index 0000000000000000000000000000000000000000..b72840ef50b6a432f92ecd6edaaf539fae606949 GIT binary patch literal 8192 zcmeIu0Sy2E48x#mx_@!ku>>SgT^KN6z<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK JfB^&30}o%s05bpp literal 0 HcmV?d00001 diff --git a/modules/neo4j/src/test/resources/test-graph.db/neostore.relationshipgroupstore.db.id b/modules/neo4j/src/test/resources/test-graph.db/neostore.relationshipgroupstore.db.id new file mode 100644 index 0000000000000000000000000000000000000000..102ef2455533af2377ffa5fb7481ab9dc0684c79 GIT binary patch literal 9 LcmZQzfB;4S015yC literal 0 HcmV?d00001 diff --git a/modules/neo4j/src/test/resources/test-graph.db/neostore.relationshipstore.db b/modules/neo4j/src/test/resources/test-graph.db/neostore.relationshipstore.db new file mode 100644 index 00000000000..e69de29bb2d diff --git a/modules/neo4j/src/test/resources/test-graph.db/neostore.relationshipstore.db.id b/modules/neo4j/src/test/resources/test-graph.db/neostore.relationshipstore.db.id new file mode 100644 index 0000000000000000000000000000000000000000..bc8840b22b1bcf5594717cc39cf105953e00c24d GIT binary patch literal 9 KcmZQzfC2yj2><~A literal 0 HcmV?d00001 diff --git a/modules/neo4j/src/test/resources/test-graph.db/neostore.relationshiptypestore.db b/modules/neo4j/src/test/resources/test-graph.db/neostore.relationshiptypestore.db new file mode 100644 index 00000000000..e69de29bb2d diff --git a/modules/neo4j/src/test/resources/test-graph.db/neostore.relationshiptypestore.db.id b/modules/neo4j/src/test/resources/test-graph.db/neostore.relationshiptypestore.db.id new file mode 100644 index 0000000000000000000000000000000000000000..bc8840b22b1bcf5594717cc39cf105953e00c24d GIT binary patch literal 9 KcmZQzfC2yj2><~A literal 0 HcmV?d00001 diff --git a/modules/neo4j/src/test/resources/test-graph.db/neostore.relationshiptypestore.db.names b/modules/neo4j/src/test/resources/test-graph.db/neostore.relationshiptypestore.db.names new file mode 100644 index 0000000000000000000000000000000000000000..ce6529796feeb9afb9f2b4f981f75295e5542784 GIT binary patch literal 8192 zcmeIu0Sy2E48x$Rxqpe*u>>SgT^KN6z<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK JfB^&30}tQI04D$d literal 0 HcmV?d00001 diff --git a/modules/neo4j/src/test/resources/test-graph.db/neostore.relationshiptypestore.db.names.id b/modules/neo4j/src/test/resources/test-graph.db/neostore.relationshiptypestore.db.names.id new file mode 100644 index 0000000000000000000000000000000000000000..102ef2455533af2377ffa5fb7481ab9dc0684c79 GIT binary patch literal 9 LcmZQzfB;4S015yC literal 0 HcmV?d00001 diff --git a/modules/neo4j/src/test/resources/test-graph.db/neostore.schemastore.db b/modules/neo4j/src/test/resources/test-graph.db/neostore.schemastore.db new file mode 100644 index 0000000000000000000000000000000000000000..df8ce2a58c7337a0be82febc80d3e0e21c414f96 GIT binary patch literal 8192 zcmeIu0Sy2E48x#GyMI;Ju>>SgT^KN6z<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK JfB^&30}mg#06_o% literal 0 HcmV?d00001 diff --git a/modules/neo4j/src/test/resources/test-graph.db/neostore.schemastore.db.id b/modules/neo4j/src/test/resources/test-graph.db/neostore.schemastore.db.id new file mode 100644 index 0000000000000000000000000000000000000000..102ef2455533af2377ffa5fb7481ab9dc0684c79 GIT binary patch literal 9 LcmZQzfB;4S015yC literal 0 HcmV?d00001 diff --git a/modules/neo4j/src/test/resources/test-graph.db/neostore.transaction.db.0 b/modules/neo4j/src/test/resources/test-graph.db/neostore.transaction.db.0 new file mode 100644 index 0000000000000000000000000000000000000000..c7e1d7a4f9c66d16935f63ce4e87cd67ad57d621 GIT binary patch literal 1150 zcmZ`&Jxc>Y5Z#YUTmuOg1O<(VsAcdkogmg) zs;yj=IHvnt-khGtTsLk zC;}Au?rbcszkZ(RU(%4WiRgbbfZ3C0@(exBXx>_{oSd{pP&)$zySyt1|rDtJSXfD8v$nDYx`@$(`^qh88R1 zmY%zja{m-?t1@m{tc+XDb59Gnbs0C_z};kaqs&%Xg}qSRr*n%I;Qj$t=>hmf;cH+! zWUjhYsSpG;gXG8W{=5Ef_ih(5aCDsf1DLo#yZ`_I literal 0 HcmV?d00001 From 6cee57b144bf801bb5bc4fb82dc649ba95643f03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 24 Feb 2019 16:56:44 +0100 Subject: [PATCH 0613/5615] Bump okhttp from 3.12.1 to 3.13.1 in /examples (#1268) Bumps [okhttp](https://github.com/square/okhttp) from 3.12.1 to 3.13.1. - [Release notes](https://github.com/square/okhttp/releases) - [Changelog](https://github.com/square/okhttp/blob/master/CHANGELOG.md) - [Commits](https://github.com/square/okhttp/compare/parent-3.12.1...parent-3.13.1) Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 79c2403fdfb..8be38d67c95 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.25' - implementation 'com.squareup.okhttp3:okhttp:3.12.1' + implementation 'com.squareup.okhttp3:okhttp:3.13.1' implementation 'org.json:json:20180813' testImplementation 'org.postgresql:postgresql:42.2.5' testImplementation 'ch.qos.logback:logback-classic:1.2.3' From 2d0dfc1bd69b799843767b7a4145783e2bed8736 Mon Sep 17 00:00:00 2001 From: Enrico Costanzi Date: Sun, 24 Feb 2019 18:02:19 +0100 Subject: [PATCH 0614/5615] Allow to change password in MSSQLServerContainer (#885) --- .../junit/CustomPasswordMSSQLServerTest.java | 79 +++++++++++++++++++ .../junit/CustomizableMSSQLServerTest.java | 44 +++++++++++ .../containers/MSSQLServerContainer.java | 48 +++++++++++ 3 files changed, 171 insertions(+) create mode 100644 modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomPasswordMSSQLServerTest.java create mode 100644 modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomizableMSSQLServerTest.java diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomPasswordMSSQLServerTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomPasswordMSSQLServerTest.java new file mode 100644 index 00000000000..2f77dad44fc --- /dev/null +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomPasswordMSSQLServerTest.java @@ -0,0 +1,79 @@ +package org.testcontainers.junit; + +import org.apache.commons.lang.RandomStringUtils; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.testcontainers.containers.MSSQLServerContainer; + +import java.util.Arrays; +import java.util.Collection; + +import static org.rnorth.visibleassertions.VisibleAssertions.fail; + +/** + * Tests if the password passed to the container satisfied the password policy described at + * https://docs.microsoft.com/en-us/sql/relational-databases/security/password-policy?view=sql-server-2017 + * + * @author Enrico Costanzi + */ +@RunWith(Parameterized.class) +public class CustomPasswordMSSQLServerTest { + + private static String UPPER_CASE_LETTERS = "ABCDE"; + private static String LOWER_CASE_LETTERS = "abcde"; + private static String NUMBERS = "12345"; + private static String SPECIAL_CHARS = "_(!)_"; + + private String password; + private Boolean valid; + + public CustomPasswordMSSQLServerTest(String password, Boolean valid) { + this.password = password; + this.valid = valid; + } + + @Parameterized.Parameters + public static Collection data() { + return Arrays.asList(new Object[][]{ + new Object[]{null, false}, + // too short + {"abc123", false}, + + // too long + {RandomStringUtils.randomAlphabetic(129), false}, + + // only 2 categories + {UPPER_CASE_LETTERS + NUMBERS, false}, + {UPPER_CASE_LETTERS + SPECIAL_CHARS, false}, + {LOWER_CASE_LETTERS + NUMBERS, false}, + {LOWER_CASE_LETTERS + SPECIAL_CHARS, false}, + {NUMBERS + SPECIAL_CHARS, false}, + + // 3 categories + {UPPER_CASE_LETTERS + LOWER_CASE_LETTERS + NUMBERS, true}, + {UPPER_CASE_LETTERS + LOWER_CASE_LETTERS + SPECIAL_CHARS, true}, + {UPPER_CASE_LETTERS + NUMBERS + SPECIAL_CHARS, true}, + {LOWER_CASE_LETTERS + NUMBERS + SPECIAL_CHARS, true}, + + // 4 categories + {UPPER_CASE_LETTERS + LOWER_CASE_LETTERS + NUMBERS + SPECIAL_CHARS, true}, + + + }); + } + + @Test + public void runPasswordTests() { + try { + new MSSQLServerContainer().withPassword(this.password); + if (!valid) + fail("Password " + this.password + " is not valid. Expected exception"); + } catch (IllegalArgumentException e) { + if (valid) + fail("Password " + this.password + " should have been validated"); + } + } + + +} diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomizableMSSQLServerTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomizableMSSQLServerTest.java new file mode 100644 index 00000000000..2f6e43a771f --- /dev/null +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomizableMSSQLServerTest.java @@ -0,0 +1,44 @@ +package org.testcontainers.junit; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import org.junit.Rule; +import org.junit.Test; +import org.testcontainers.containers.MSSQLServerContainer; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.fail; + +public class CustomizableMSSQLServerTest { + + private static final String STRONG_PASSWORD = "myStrong(!)Password"; + + @Rule + public MSSQLServerContainer mssqlServerContainer = new MSSQLServerContainer() + .withPassword(STRONG_PASSWORD); + + @Test + public void testSqlServerConnection() throws SQLException { + HikariConfig hikariConfig = new HikariConfig(); + hikariConfig.setJdbcUrl("jdbc:sqlserver://localhost:" + + mssqlServerContainer.getMappedPort(MSSQLServerContainer.MS_SQL_SERVER_PORT)); + hikariConfig.setUsername("SA"); + hikariConfig.setPassword(STRONG_PASSWORD); + + HikariDataSource ds = new HikariDataSource(hikariConfig); + Statement statement = ds.getConnection().createStatement(); + statement.execute(mssqlServerContainer.getTestQueryString()); + ResultSet resultSet = statement.getResultSet(); + if (resultSet.next()) { + int resultSetInt = resultSet.getInt(1); + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + } else { + fail("No results returned from query"); + } + + } +} diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java index ec657b90bad..ad1a7d19090 100644 --- a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java @@ -2,6 +2,9 @@ import org.testcontainers.utility.LicenseAcceptance; +import java.util.regex.Pattern; +import java.util.stream.Stream; + /** * @author Stefan Hufschmidt */ @@ -18,6 +21,13 @@ public class MSSQLServerContainer> exten private static final int DEFAULT_STARTUP_TIMEOUT_SECONDS = 240; private static final int DEFAULT_CONNECT_TIMEOUT_SECONDS = 240; + private static final Pattern[] PASSWORD_CATEGORY_VALIDATION_PATTERNS = new Pattern[]{ + Pattern.compile("[A-Z]+"), + Pattern.compile("[a-z]+"), + Pattern.compile("[0-9]+"), + Pattern.compile("[^a-zA-Z0-9]+", Pattern.CASE_INSENSITIVE) + }; + public MSSQLServerContainer() { this(IMAGE + ":" + DEFAULT_TAG); } @@ -67,4 +77,42 @@ public String getPassword() { public String getTestQueryString() { return "SELECT 1"; } + + @Override + public SELF withPassword(final String password) { + checkPasswordStrength(password); + this.password = password; + return self(); + } + + private void checkPasswordStrength(String password) { + + if (password == null) { + throw new IllegalArgumentException("Null password is not allowed"); + } + + if (password.length() < 8) { + throw new IllegalArgumentException("Password should be at least 8 characters long"); + } + + if (password.length() > 128) { + throw new IllegalArgumentException("Password can be up to 128 characters long"); + } + + long satisfiedCategories = Stream.of(PASSWORD_CATEGORY_VALIDATION_PATTERNS) + .filter(p -> p.matcher(password).find()) + .count(); + + if (satisfiedCategories < 3) { + throw new IllegalArgumentException( + "Password must contain characters from three of the following four categories:\n" + + " - Latin uppercase letters (A through Z)\n" + + " - Latin lowercase letters (a through z)\n" + + " - Base 10 digits (0 through 9)\n" + + " - Non-alphanumeric characters such as: exclamation point (!), dollar sign ($), number sign (#), " + + "or percent (%)." + ); + } + + } } From c58a6f10670a43c31f50586502a0722a449ad2e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 25 Feb 2019 08:25:14 +0100 Subject: [PATCH 0615/5615] Bump influxdb-java from 2.14 to 2.15 in /modules/influxdb (#1271) Bumps [influxdb-java](https://github.com/influxdata/influxdb-java) from 2.14 to 2.15. - [Release notes](https://github.com/influxdata/influxdb-java/releases) - [Changelog](https://github.com/influxdata/influxdb-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/influxdata/influxdb-java/compare/influxdb-java-2.14...influxdb-java-2.15) Signed-off-by: dependabot[bot] --- modules/influxdb/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/influxdb/build.gradle b/modules/influxdb/build.gradle index b0ce14b562e..d122f786591 100644 --- a/modules/influxdb/build.gradle +++ b/modules/influxdb/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: InfluxDB" dependencies { compile project(':testcontainers') - compileOnly 'org.influxdb:influxdb-java:2.14' - testCompile 'org.influxdb:influxdb-java:2.10' + compileOnly 'org.influxdb:influxdb-java:2.15' + testCompile 'org.influxdb:influxdb-java:2.15' } From d9c24a4c962e41260417045aa771ea249af2f6d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 3 Mar 2019 11:20:27 +0000 Subject: [PATCH 0616/5615] Bump assertj-core from 3.12.0 to 3.12.1 in /modules/pulsar (#1287) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.12.0 to 3.12.1. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.12.0...assertj-core-3.12.1) Signed-off-by: dependabot[bot] --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 2a0d0bfa345..f5e4ffe29cf 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -4,5 +4,5 @@ dependencies { compile project(':testcontainers') testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.2.1' - testCompile group: 'org.assertj', name: 'assertj-core', version: '3.12.0' + testCompile group: 'org.assertj', name: 'assertj-core', version: '3.12.1' } From a7eebda7feb776dbf2d2c94fb4b424b986a2b628 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 3 Mar 2019 11:20:50 +0000 Subject: [PATCH 0617/5615] Bump assertj-core from 3.12.0 to 3.12.1 in /modules/neo4j (#1286) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.12.0 to 3.12.1.
Commits - [`8cc755f`](https://github.com/joel-costigliola/assertj-core/commit/8cc755f605ca3722c7851108ef0e952d77a43842) [maven-release-plugin] prepare release assertj-core-3.12.1 - [`14384ec`](https://github.com/joel-costigliola/assertj-core/commit/14384ec07c09d510685f88c95814daa068fdba33) Avoid generating a module-info class. Fixes [#1448](https://github-redirect.dependabot.com/joel-costigliola/assertj-core/issues/1448) - [`fb75625`](https://github.com/joel-costigliola/assertj-core/commit/fb756253cf71564e5bd04867e28cdca2885ecf47) anySatisfy: do not continue evaluating elements once we found a match - [`c8a6798`](https://github.com/joel-costigliola/assertj-core/commit/c8a67982e552da22633db93260c5ca233e941368) fix a minor typo ([#1449](https://github-redirect.dependabot.com/joel-costigliola/assertj-core/issues/1449)) - [`4e32776`](https://github.com/joel-costigliola/assertj-core/commit/4e32776f3318a63ef2ee4736a0fece99a988928d) Fail with error message if the given expectedStringTemplate is null ([#1450](https://github-redirect.dependabot.com/joel-costigliola/assertj-core/issues/1450)) - [`2cba38f`](https://github.com/joel-costigliola/assertj-core/commit/2cba38fe2de5a398afde3f634d6c75c128edb275) fix some more javadoc errors ([#1447](https://github-redirect.dependabot.com/joel-costigliola/assertj-core/issues/1447)) - [`50e2e3a`](https://github.com/joel-costigliola/assertj-core/commit/50e2e3ac62f729b95c205c06fae76628461bf68c) Use String#join where possible - [`fa94bf2`](https://github.com/joel-costigliola/assertj-core/commit/fa94bf2bf67a0c8c0f510ec3a4743adb99841dca) Javadoc fixes ([#1444](https://github-redirect.dependabot.com/joel-costigliola/assertj-core/issues/1444)) - [`e3c2a57`](https://github.com/joel-costigliola/assertj-core/commit/e3c2a574618793ec67cb0e573e8c07a7b9480d53) Update Memory File System to 2.1.0 - [`2c57b2b`](https://github.com/joel-costigliola/assertj-core/commit/2c57b2b4e470a0a7ff50ee64cbc7d27c02303893) Fix javadoc and reenable Javadoc doclint verification ([#1438](https://github-redirect.dependabot.com/joel-costigliola/assertj-core/issues/1438)) - Additional commits viewable in [compare view](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.12.0...assertj-core-3.12.1)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=org.assertj:assertj-core&package-manager=gradle&previous-version=3.12.0&new-version=3.12.1)](https://dependabot.com/compatibility-score.html?dependency-name=org.assertj:assertj-core&package-manager=gradle&previous-version=3.12.0&new-version=3.12.1) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot ignore this [patch|minor|major] version` will close this PR and stop Dependabot creating any more for this minor/major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 18f0ff0b718..77535d1e3df 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -30,5 +30,5 @@ dependencies { compile project(":testcontainers") testCompile "org.neo4j.driver:neo4j-java-driver:1.7.2" - testCompile 'org.assertj:assertj-core:3.12.0' + testCompile 'org.assertj:assertj-core:3.12.1' } From abbe5c39d965a26df1d6c05927e60eff196e019f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 3 Mar 2019 11:21:02 +0000 Subject: [PATCH 0618/5615] Bump assertj-core from 3.12.0 to 3.12.1 in /core (#1284) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.12.0 to 3.12.1. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.12.0...assertj-core-3.12.1) Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 556d3cddef9..820f478b920 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -129,6 +129,6 @@ dependencies { jarFileTestCompileOnly "org.projectlombok:lombok:${lombok.version}" jarFileTestCompile 'junit:junit:4.12' - jarFileTestCompile 'org.assertj:assertj-core:3.12.0' + jarFileTestCompile 'org.assertj:assertj-core:3.12.1' jarFileTestCompile 'org.ow2.asm:asm-debug-all:5.2' } From 95dab1c599ad161cd51b413e96af55dff3cd5b38 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 3 Mar 2019 11:21:14 +0000 Subject: [PATCH 0619/5615] Bump assertj-core from 3.12.0 to 3.12.1 in /modules/kafka (#1285) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.12.0 to 3.12.1. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.12.0...assertj-core-3.12.1) Signed-off-by: dependabot[bot] --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index b2b767a2dcc..99dea31d8b8 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -4,6 +4,6 @@ dependencies { compile project(':testcontainers') testCompile 'org.apache.kafka:kafka-clients:2.1.0' - testCompile 'org.assertj:assertj-core:3.12.0' + testCompile 'org.assertj:assertj-core:3.12.1' testCompile 'com.google.guava:guava:23.0' } From 82dde4619e835bd53413967cf044ca2161b1f6a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 3 Mar 2019 11:22:26 +0000 Subject: [PATCH 0620/5615] Bump pulsar-client from 2.2.1 to 2.3.0 in /modules/pulsar (#1267) Bumps [pulsar-client](https://github.com/apache/pulsar) from 2.2.1 to 2.3.0. - [Release notes](https://github.com/apache/pulsar/releases) - [Commits](https://github.com/apache/pulsar/compare/v2.2.1...v2.3.0) Signed-off-by: dependabot[bot] --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index f5e4ffe29cf..eaddd4a3862 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Pulsar" dependencies { compile project(':testcontainers') - testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.2.1' + testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.3.0' testCompile group: 'org.assertj', name: 'assertj-core', version: '3.12.1' } From 5b840e348ddb9697bd89a027bfefac01caf9bdf8 Mon Sep 17 00:00:00 2001 From: Zoran Regvart Date: Sun, 3 Mar 2019 12:25:21 +0100 Subject: [PATCH 0621/5615] refactor: robust version parsing (#1273) Tries to parse given version in `ComparableVersion` disregarding non-numeric characters. This allows parsing Docker CLI versions like `18.06.0-dev`. --- .../utility/ComparableVersion.java | 44 ++++++++++++++++--- .../utility/ComparableVersionTest.java | 40 +++++++++++++++++ 2 files changed, 79 insertions(+), 5 deletions(-) create mode 100644 core/src/test/java/org/testcontainers/utility/ComparableVersionTest.java diff --git a/core/src/main/java/org/testcontainers/utility/ComparableVersion.java b/core/src/main/java/org/testcontainers/utility/ComparableVersion.java index 71418711391..1ffa1642568 100644 --- a/core/src/main/java/org/testcontainers/utility/ComparableVersion.java +++ b/core/src/main/java/org/testcontainers/utility/ComparableVersion.java @@ -1,22 +1,27 @@ package org.testcontainers.utility; +import java.util.ArrayList; +import java.util.List; + +import com.google.common.annotations.VisibleForTesting; + import org.jetbrains.annotations.NotNull; -public class ComparableVersion implements Comparable { +public final class ComparableVersion implements Comparable { - private final String[] parts; + private final int[] parts; public static final ComparableVersion OS_VERSION = new ComparableVersion(System.getProperty("os.version")); public ComparableVersion(String version) { - this.parts = version.split("\\."); + this.parts = parseVersion(version); } @Override public int compareTo(@NotNull ComparableVersion other) { for (int i=0; i otherPart) { return 1; } else if (thisPart < otherPart) { @@ -34,4 +39,33 @@ public boolean isLessThan(String other) { public boolean isGreaterThanOrEqualTo(String other) { return this.compareTo(new ComparableVersion(other)) >= 0; } + + @VisibleForTesting + static int[] parseVersion(final String version) { + final List parts = new ArrayList<>(5); + + int acc = 0; + for (final char c : version.toCharArray()) { + if (c == '.') { + parts.add(acc); + acc = 0; + } + + if (Character.isDigit(c)) { + acc = 10 * acc + Character.digit(c, 10); + } + } + + if (acc != 0) { + parts.add(acc); + } + + final int[] ret = new int[parts.size()]; + for (int i = 0; i < ret.length; i++) { + ret[i] = parts.get(i); + } + + return ret; + } + } diff --git a/core/src/test/java/org/testcontainers/utility/ComparableVersionTest.java b/core/src/test/java/org/testcontainers/utility/ComparableVersionTest.java new file mode 100644 index 00000000000..d5a2c63a5b8 --- /dev/null +++ b/core/src/test/java/org/testcontainers/utility/ComparableVersionTest.java @@ -0,0 +1,40 @@ +package org.testcontainers.utility; + +import java.util.Arrays; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import static org.junit.Assert.assertArrayEquals; + +@RunWith(Parameterized.class) +public class ComparableVersionTest { + + private final int[] expected; + private final String given; + + public ComparableVersionTest(final String given, final int[] expected) { + this.given = given; + this.expected = expected; + } + + @Test + public void shouldParseVersions() { + assertArrayEquals(expected, ComparableVersion.parseVersion(given)); + } + + @Parameters(name = "Parsed version: {0}={1}") + public static Iterable data() { + return Arrays.asList(new Object[][] { + {"1.2.3", new int[] {1, 2, 3}}, + {"", new int[0]}, + {"1", new int[] {1}}, + {"1.2.3.4.5.6.7", new int[] {1, 2, 3, 4, 5, 6, 7}}, + {"1.2-dev", new int[] {1, 2}}, + {"18.06.0-dev", new int[] {18, 6}}, + }); + } + +} From 208258fa7bd16f462b5f3be82a90d933e0f62c95 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 3 Mar 2019 14:30:01 +0000 Subject: [PATCH 0622/5615] Bump lombok from 1.18.4 to 1.18.6 in /examples (#1261) Bumps [lombok](https://github.com/rzwitserloot/lombok) from 1.18.4 to 1.18.6. - [Release notes](https://github.com/rzwitserloot/lombok/releases) - [Changelog](https://github.com/rzwitserloot/lombok/blob/master/doc/changelog.markdown) - [Commits](https://github.com/rzwitserloot/lombok/compare/v1.18.4...v1.18.6) Signed-off-by: dependabot[bot] --- examples/disque-job-queue/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index 000b94b9d98..77b6022a968 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.projectlombok:lombok:1.18.4' + compileOnly 'org.projectlombok:lombok:1.18.6' implementation 'biz.paluch.redis:spinach:0.3' implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.google.guava:guava:23.0' diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index 1dc5ec7c857..5a5ad6d893a 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -9,7 +9,7 @@ repositories { } dependencies { - compileOnly 'org.projectlombok:lombok:1.18.4' + compileOnly 'org.projectlombok:lombok:1.18.6' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-data-redis' implementation 'org.springframework.boot:spring-boot-starter-web' From b4e4f11a939b84c1c3a2d7ac9df321f2da8178fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 3 Mar 2019 14:30:17 +0000 Subject: [PATCH 0623/5615] Bump kafka-clients from 2.1.0 to 2.1.1 in /modules/kafka (#1251) Bumps kafka-clients from 2.1.0 to 2.1.1. Signed-off-by: dependabot[bot] --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index 99dea31d8b8..ad781298deb 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Kafka" dependencies { compile project(':testcontainers') - testCompile 'org.apache.kafka:kafka-clients:2.1.0' + testCompile 'org.apache.kafka:kafka-clients:2.1.1' testCompile 'org.assertj:assertj-core:3.12.1' testCompile 'com.google.guava:guava:23.0' } From 411e3d024e45649fc428ade6f42cfb443e1dd653 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 3 Mar 2019 14:30:36 +0000 Subject: [PATCH 0624/5615] Bump slf4j-api from 1.7.25 to 1.7.26 in /examples (#1262) Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 1.7.25 to 1.7.26.
Commits - [`7a35058`](https://github.com/qos-ch/slf4j/commit/7a35058597b49f220b751ad56ac0b45e570c2205) rework on site creation, keep Nexus/Central happy - [`3cd3f3e`](https://github.com/qos-ch/slf4j/commit/3cd3f3e5ccdebdc499356fc5644d96e393c2b5f3) enable source jar creation only for select modules to keep Nexus happy - [`918a055`](https://github.com/qos-ch/slf4j/commit/918a055bdf87867f69693cf82727fa435c489e25) fix CVE-2018-8088, prepare 1.7.26 release - See full diff in [compare view](https://github.com/qos-ch/slf4j/compare/v_1.7.25...v_1.7.26)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=org.slf4j:slf4j-api&package-manager=gradle&previous-version=1.7.25&new-version=1.7.26)](https://dependabot.com/compatibility-score.html?dependency-name=org.slf4j:slf4j-api&package-manager=gradle&previous-version=1.7.25&new-version=1.7.26) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) If all status checks pass Dependabot will automatically merge this pull request. [//]: # (dependabot-automerge-end) --- **Note:** This repo was added to Dependabot recently, so you'll receive a maximum of 5 PRs for your first few update runs. Once an update run creates fewer than 5 PRs we'll remove that limit. You can always request more updates by clicking `Bump now` in your [Dependabot dashboard](https://app.dependabot.com).
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot ignore this [patch|minor|major] version` will close this PR and stop Dependabot creating any more for this minor/major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- examples/linked-container/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 8be38d67c95..565ed7c5353 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -6,7 +6,7 @@ repositories { jcenter() } dependencies { - compileOnly 'org.slf4j:slf4j-api:1.7.25' + compileOnly 'org.slf4j:slf4j-api:1.7.26' implementation 'com.squareup.okhttp3:okhttp:3.13.1' implementation 'org.json:json:20180813' testImplementation 'org.postgresql:postgresql:42.2.5' diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index c213af0b205..5075c10e7df 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:1.7.25' + compileOnly 'org.slf4j:slf4j-api:1.7.26' implementation 'redis.clients:jedis:3.0.1' implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.google.guava:guava:23.0' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index c6370fceac9..692841ae7d7 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:1.7.25' + compileOnly 'org.slf4j:slf4j-api:1.7.26' implementation 'redis.clients:jedis:3.0.1' implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.google.guava:guava:23.0' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 3c3e3b043eb..0a1789c944a 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -11,7 +11,7 @@ dependencies { implementation 'redis.clients:jedis:3.0.1' implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.google.guava:guava:23.0' - compileOnly 'org.slf4j:slf4j-api:1.7.25' + compileOnly 'org.slf4j:slf4j-api:1.7.26' testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.testcontainers:testcontainers' From 5e0a75e90e9cf827c29c6b3e76264f492b8e5c2a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 3 Mar 2019 15:01:03 +0000 Subject: [PATCH 0625/5615] Bump mockito-core from 2.24.0 to 2.24.5 in /core (#1255) Bumps [mockito-core](https://github.com/mockito/mockito) from 2.24.0 to 2.24.5.
Commits - [`41c5606`](https://github.com/mockito/mockito/commit/41c5606349e13a1e3e060ab8348b412662219d62) 2.24.5 release (previous 2.24.4) + release notes updated by CI build 3887 - [`e2e7dc8`](https://github.com/mockito/mockito/commit/e2e7dc8bd8c4244ba864dd8687a0dc30cace967f) [ci maven-central-release] Release 2.24.5 - [`1ba9276`](https://github.com/mockito/mockito/commit/1ba92767c19c2dd3d3bf3ebf697f32851b2b7af9) [ci maven-central-release] Release 2.24.4 - [`4c25405`](https://github.com/mockito/mockito/commit/4c254058f1f1606a5de4c4fdc9f1a0b3c10ad15e) 2.24.4 release (previous 2.24.3) + release notes updated by CI build 3883 - [`d9cbe3e`](https://github.com/mockito/mockito/commit/d9cbe3e8ec9cafa521e61371d9e9600b7262cda7) Fixes [#1618](https://github-redirect.dependabot.com/mockito/mockito/issues/1618) : Fix strict stubbing profile serialization support. ([#1620](https://github-redirect.dependabot.com/mockito/mockito/issues/1620)) - [`34ebca1`](https://github.com/mockito/mockito/commit/34ebca1a8cd88465dd6e8ba395f78a6593522958) 2.24.3 release (previous 2.24.2) + release notes updated by CI build 3877 - [`cbedbe5`](https://github.com/mockito/mockito/commit/cbedbe567d5bf902b13e8cebc4a2a2ccfef7758f) Return null instead of causing a CCE ([#1612](https://github-redirect.dependabot.com/mockito/mockito/issues/1612)) - [`ac9195f`](https://github.com/mockito/mockito/commit/ac9195f740280f1964382acb469b14955a08d359) Automatic dependency update with Dependabot ([#1600](https://github-redirect.dependabot.com/mockito/mockito/issues/1600)) - [`a9d998f`](https://github.com/mockito/mockito/commit/a9d998fff27bc5019ba1ad78fc068bb8a2a424b0) 2.24.2 release (previous 2.24.1) + release notes updated by CI build 3874 - [`faeae6a`](https://github.com/mockito/mockito/commit/faeae6ac604e0103229a2ef23e35f88e10e026dd) Fix issue with mocking of java.util.* classes ([#1617](https://github-redirect.dependabot.com/mockito/mockito/issues/1617)) - Additional commits viewable in [compare view](https://github.com/mockito/mockito/compare/v2.24.0...v2.24.5)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=org.mockito:mockito-core&package-manager=gradle&previous-version=2.24.0&new-version=2.24.5)](https://dependabot.com/compatibility-score.html?dependency-name=org.mockito:mockito-core&package-manager=gradle&previous-version=2.24.0&new-version=2.24.5) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot ignore this [patch|minor|major] version` will close this PR and stop Dependabot creating any more for this minor/major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 820f478b920..41238e1ff6a 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -120,7 +120,7 @@ dependencies { testCompile 'redis.clients:jedis:3.0.1' testCompile 'com.rabbitmq:amqp-client:5.6.0' testCompile 'org.mongodb:mongo-java-driver:3.9.1' - testCompile ('org.mockito:mockito-core:2.24.0') { + testCompile ('org.mockito:mockito-core:2.24.5') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From 162e7840d849d29c8ea0ec0f8fe6331d8a85b538 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 3 Mar 2019 15:01:22 +0000 Subject: [PATCH 0626/5615] Bump elasticsearch-rest-client from 6.6.0 to 6.6.1 in /modules/elasticsearch (#1259) Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 6.6.0 to 6.6.1.
Commits - See full diff in [compare view](https://github.com/elastic/elasticsearch/commits)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=org.elasticsearch.client:elasticsearch-rest-client&package-manager=gradle&previous-version=6.6.0&new-version=6.6.1)](https://dependabot.com/compatibility-score.html?dependency-name=org.elasticsearch.client:elasticsearch-rest-client&package-manager=gradle&previous-version=6.6.0&new-version=6.6.1) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot ignore this [patch|minor|major] version` will close this PR and stop Dependabot creating any more for this minor/major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 8405a9000c0..54db119daf8 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,6 +2,6 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') - testCompile "org.elasticsearch.client:elasticsearch-rest-client:6.6.0" + testCompile "org.elasticsearch.client:elasticsearch-rest-client:6.6.1" testCompile "org.elasticsearch.client:transport:6.6.0" } From 063f9bb0c166bd207b242ac6d553386ceed231d0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 3 Mar 2019 15:02:03 +0000 Subject: [PATCH 0627/5615] Bump transport from 6.6.0 to 6.6.1 in /modules/elasticsearch (#1260) Bumps [transport](https://github.com/elastic/elasticsearch) from 6.6.0 to 6.6.1. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/commits) Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 54db119daf8..a41455f76aa 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,5 +3,5 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') testCompile "org.elasticsearch.client:elasticsearch-rest-client:6.6.1" - testCompile "org.elasticsearch.client:transport:6.6.0" + testCompile "org.elasticsearch.client:transport:6.6.1" } From 49c3f7265b3d4dec3d96f5bb16b6823ab1ed3e81 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 3 Mar 2019 15:02:23 +0000 Subject: [PATCH 0628/5615] Bump selenium-firefox-driver from 3.14.0 to 3.141.59 in /examples (#1263) Bumps [selenium-firefox-driver](https://github.com/SeleniumHQ/selenium) from 3.14.0 to 3.141.59. - [Release notes](https://github.com/SeleniumHQ/selenium/releases) - [Commits](https://github.com/SeleniumHQ/selenium/compare/selenium-3.14.0...selenium-3.141.59) Signed-off-by: dependabot[bot] --- examples/selenium-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index 17e7e21c776..9b8fe1d661f 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -10,7 +10,7 @@ repositories { dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.14.0' - implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.14.0' + implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation 'org.springframework.boot:spring-boot-starter-test' From 7e751be6111dcc69236dc9b0019265f3410899a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 3 Mar 2019 15:03:21 +0000 Subject: [PATCH 0629/5615] Bump mongo-java-driver from 3.9.1 to 3.10.1 in /core (#1234) Bumps [mongo-java-driver](https://github.com/mongodb/mongo-java-driver) from 3.9.1 to 3.10.1.
Release notes *Sourced from [mongo-java-driver's releases](https://github.com/mongodb/mongo-java-driver/releases).* > ## 3.10.1 > ## Java Driver 3.10.1 (February 11, 2019) > > The 3.10.1 MongoDB Java Driver is a patch to the 3.10.0 release. > > ### Reference documentation > > The [documentation hub](http://mongodb.github.io/mongo-java-driver/) includes extensive documentation of the 3.10 driver, including > - [What's New](http://mongodb.github.io/mongo-java-driver/3.10/whats-new/): what's new in the 3.10 driver > - [Upgrading](http://mongodb.github.io/mongo-java-driver/3.10/upgrading/): upgrading from the 3.9.x driver > - [Installation](http://mongodb.github.io/mongo-java-driver/3.10/driver/getting-started/installation/): how to get the driver > > and much more. > > ### Bug fixes > > You can find a full list of bug fixes [here](https://jira.mongodb.org/issues/?jql=project%20%3D%20JAVA%20AND%20issuetype%20%3D%20Bug%20AND%20fixVersion%20%3D%203.10.1%20ORDER%20BY%20priority%20DESC). > > ### API Documentation > > http://mongodb.github.io/mongo-java-driver/3.10/javadoc/ > > ## 3.10.0 > ## Java Driver 3.10.0 (February 5, 2019) > > The 3.10.0 MongoDB Java Driver includes bug fixes, new features, and improvements. It supports MongoDB server versions from the 2.6 release through the [4.0 release](https://docs.mongodb.com/master/release-notes/4.0/). > > ### New Features > > See the [What's New](http://mongodb.github.io/mongo-java-driver/3.10/whats-new/) section of the reference documentation. > > ### Upgrade Considerations > > See the [Upgrade Considerations](http://mongodb.github.io/mongo-java-driver/3.10/upgrading/) section of the reference documentation. > > ### Downloads > > On [maven central](https://repo.maven.apache.org/maven2/org/mongodb/mongodb-driver-sync/3.10.0/). > > ### API Documentation > > http://mongodb.github.io/mongo-java-driver/3.10/javadoc/ > > ### New Features > > You can find a full list of new features [here](https://jira.mongodb.org/issues/?jql=project%20%3D%20JAVA%20AND%20issuetype%20%3D%20%22New%20Feature%22%20AND%20status%20%3D%20Closed%20AND%20fixVersion%20%3D%203.10.0%20ORDER%20BY%20component%20ASC). > > ### Bug fixes > > You can find a full list of bug fixes [here](https://jira.mongodb.org/issues/?jql=project%20%3D%20JAVA%20AND%20issuetype%20%3D%20Bug%20AND%20status%20%3D%20Closed%20AND%20fixVersion%20%3D%203.10.0%20ORDER%20BY%20component%20ASC). > ... (truncated)
Commits - [`00ae57e`](https://github.com/mongodb/mongo-java-driver/commit/00ae57eebf361c92519ccbf4b72064e7e126954b) Bump to 3.10.1 - [`66dcabe`](https://github.com/mongodb/mongo-java-driver/commit/66dcabe7bfd926791583afd04b5a9cf8ee6872ae) Fix NullPointerException when using mongodb+srv scheme - [`d76c796`](https://github.com/mongodb/mongo-java-driver/commit/d76c796291a8eed25c46ac1791b2ccb4a7eb1b72) Update async SSL reference documentation to document native TLS support - [`0d508d0`](https://github.com/mongodb/mongo-java-driver/commit/0d508d023c84e56d02d950d573ef3eae7caadd88) Bump to 3.10.1-SNAPSHOT - [`31d51c1`](https://github.com/mongodb/mongo-java-driver/commit/31d51c1fc6598b47f89ef0a42613c784d990d279) Bump to 3.10.0 - [`5683525`](https://github.com/mongodb/mongo-java-driver/commit/56835250ac6596248c309cac472848967b841ee9) Added IdGenerator to the POJOs docs. - [`3321d3c`](https://github.com/mongodb/mongo-java-driver/commit/3321d3c76fdb08c2c095c6493ede41a9bf66dde7) Docs: Remove links in tour example code - [`b73f9e1`](https://github.com/mongodb/mongo-java-driver/commit/b73f9e116699306ab45d80a74372bbb219eb342a) Fixed typo in reference docs - [`a68b86f`](https://github.com/mongodb/mongo-java-driver/commit/a68b86fddfd3367021e1b70c5b477b1bb759fae7) Fix checkstyle error - [`b10f9f5`](https://github.com/mongodb/mongo-java-driver/commit/b10f9f5bf6f8c31db410521b43f1ec31c5d3987d) Log retryable writes at DEBUG level - Additional commits viewable in [compare view](https://github.com/mongodb/mongo-java-driver/compare/r3.9.1...r3.10.1)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=org.mongodb:mongo-java-driver&package-manager=gradle&previous-version=3.9.1&new-version=3.10.1)](https://dependabot.com/compatibility-score.html?dependency-name=org.mongodb:mongo-java-driver&package-manager=gradle&previous-version=3.9.1&new-version=3.10.1) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot ignore this [patch|minor|major] version` will close this PR and stop Dependabot creating any more for this minor/major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 41238e1ff6a..ca09860f6e4 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -119,7 +119,7 @@ dependencies { testCompile 'redis.clients:jedis:3.0.1' testCompile 'com.rabbitmq:amqp-client:5.6.0' - testCompile 'org.mongodb:mongo-java-driver:3.9.1' + testCompile 'org.mongodb:mongo-java-driver:3.10.1' testCompile ('org.mockito:mockito-core:2.24.5') { exclude(module: 'hamcrest-core') } From 0be37bcab370f0a4e14dbf567c029c6680add2e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 3 Mar 2019 15:04:14 +0000 Subject: [PATCH 0630/5615] Bump selenium-chrome-driver from 3.14.0 to 3.141.59 in /examples (#1264) Bumps [selenium-chrome-driver](https://github.com/SeleniumHQ/selenium) from 3.14.0 to 3.141.59. - [Release notes](https://github.com/SeleniumHQ/selenium/releases) - [Commits](https://github.com/SeleniumHQ/selenium/compare/selenium-3.14.0...selenium-3.141.59) Signed-off-by: dependabot[bot] --- examples/selenium-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index 9b8fe1d661f..0592a0fffd6 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -11,7 +11,7 @@ repositories { dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' - implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' + implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.testcontainers:selenium' From 12a9f4ebc0d13bd4b239187858ed73ceece3a762 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 3 Mar 2019 15:04:40 +0000 Subject: [PATCH 0631/5615] Bump selenium-remote-driver from 3.14.0 to 3.141.59 in /examples (#1265) Bumps [selenium-remote-driver](https://github.com/SeleniumHQ/selenium) from 3.14.0 to 3.141.59. - [Release notes](https://github.com/SeleniumHQ/selenium/releases) - [Commits](https://github.com/SeleniumHQ/selenium/compare/selenium-3.14.0...selenium-3.141.59) Signed-off-by: dependabot[bot] --- examples/selenium-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index 0592a0fffd6..207020cd636 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -9,7 +9,7 @@ repositories { } dependencies { - implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.14.0' + implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' implementation 'org.springframework.boot:spring-boot-starter-web' From 6035930c47a71b0ca427ad903eea07d3ddd76be1 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 3 Mar 2019 16:22:28 +0000 Subject: [PATCH 0632/5615] Fix for hanging Gradle build scan step --- examples/build.gradle | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/build.gradle b/examples/build.gradle index 3130de8492e..f3f08d812b4 100644 --- a/examples/build.gradle +++ b/examples/build.gradle @@ -1 +1,4 @@ -// empty build.gradle for dependabot \ No newline at end of file +buildScan { + termsOfServiceUrl = 'https://gradle.com/terms-of-service' + termsOfServiceAgree = 'yes' +} From cf6626618902b0c22b483e83bffbb91a70bdc775 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 3 Mar 2019 16:40:10 +0000 Subject: [PATCH 0633/5615] Only build master branch using Travis --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index e5a282092d3..0409d2a5a6a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,10 @@ language: java jdk: - oraclejdk8 +branches: + only: + - master + sudo: required services: - docker From 2ec768ef1c47e1c302a0b7995e20d352ae41b48b Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 3 Mar 2019 17:04:45 +0000 Subject: [PATCH 0634/5615] Disable Gradle build scan for examples project --- .travis.yml | 2 +- examples/build.gradle | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0409d2a5a6a..924b31b4f8d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,7 +36,7 @@ jobs: script: ./gradlew check -x testcontainers:check -x selenium:check --scan --no-daemon - env: [ NAME=examples ] - script: cd examples && ../gradlew check --scan --no-daemon + script: cd examples && ../gradlew check --no-daemon # Run Docker-in-Docker tests inside Alpine - env: [ NAME="docker-in-alpine-docker" ] diff --git a/examples/build.gradle b/examples/build.gradle index f3f08d812b4..b2ecf1b0be3 100644 --- a/examples/build.gradle +++ b/examples/build.gradle @@ -1,4 +1 @@ -buildScan { - termsOfServiceUrl = 'https://gradle.com/terms-of-service' - termsOfServiceAgree = 'yes' -} +// empty build.gradle for dependabot From b27f4419f0c81d3cb1bdcdee46986b71cdbe0dd6 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 3 Mar 2019 20:10:42 +0000 Subject: [PATCH 0635/5615] Remove hardcoded Selenium version and use Spring Boot dependency-managed version instead --- examples/selenium-container/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index 207020cd636..884657194cb 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -9,9 +9,9 @@ repositories { } dependencies { - implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.141.59' - implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' - implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' + implementation 'org.seleniumhq.selenium:selenium-remote-driver' + implementation 'org.seleniumhq.selenium:selenium-firefox-driver' + implementation 'org.seleniumhq.selenium:selenium-chrome-driver' implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.testcontainers:selenium' From 585fa90f958032af6634e7f2811f7e50ccb41d41 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 3 Mar 2019 22:05:26 +0000 Subject: [PATCH 0636/5615] Enable automerge of dependabot updates (minor and patch updates only) --- .dependabot/config.yml | 79 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 2 deletions(-) diff --git a/.dependabot/config.yml b/.dependabot/config.yml index 45dc11c034b..7acddda566c 100644 --- a/.dependabot/config.yml +++ b/.dependabot/config.yml @@ -4,85 +4,160 @@ update_configs: - package_manager: "java:gradle" directory: "/core" update_schedule: "daily" + automerged_updates: + - match: { dependency_type: "development", update_type: "semver:minor" } + - match: { dependency_type: "production", update_type: "semver:minor" } # Explicit entry for each module - package_manager: "java:gradle" directory: "/modules/cassandra" update_schedule: "daily" + automerged_updates: + - match: { dependency_type: "development", update_type: "semver:minor" } + - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/clickhouse" update_schedule: "daily" + automerged_updates: + - match: { dependency_type: "development", update_type: "semver:minor" } + - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/couchbase" update_schedule: "daily" + automerged_updates: + - match: { dependency_type: "development", update_type: "semver:minor" } + - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/database-commons" update_schedule: "daily" + automerged_updates: + - match: { dependency_type: "development", update_type: "semver:minor" } + - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/dynalite" update_schedule: "daily" + automerged_updates: + - match: { dependency_type: "development", update_type: "semver:minor" } + - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/elasticsearch" update_schedule: "daily" + automerged_updates: + - match: { dependency_type: "development", update_type: "semver:minor" } + - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/influxdb" update_schedule: "daily" + automerged_updates: + - match: { dependency_type: "development", update_type: "semver:minor" } + - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/jdbc" update_schedule: "daily" + automerged_updates: + - match: { dependency_type: "development", update_type: "semver:minor" } + - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/jdbc-test" update_schedule: "daily" + automerged_updates: + - match: { dependency_type: "development", update_type: "semver:minor" } + - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/junit-jupiter" update_schedule: "daily" + automerged_updates: + - match: { dependency_type: "development", update_type: "semver:minor" } + - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/kafka" update_schedule: "daily" + automerged_updates: + - match: { dependency_type: "development", update_type: "semver:minor" } + - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/localstack" update_schedule: "daily" + automerged_updates: + - match: { dependency_type: "development", update_type: "semver:minor" } + - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/mariadb" update_schedule: "daily" + automerged_updates: + - match: { dependency_type: "development", update_type: "semver:minor" } + - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/mockserver" update_schedule: "daily" + automerged_updates: + - match: { dependency_type: "development", update_type: "semver:minor" } + - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/mssqlserver" update_schedule: "daily" + automerged_updates: + - match: { dependency_type: "development", update_type: "semver:minor" } + - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/mysql" update_schedule: "daily" + automerged_updates: + - match: { dependency_type: "development", update_type: "semver:minor" } + - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/neo4j" update_schedule: "daily" + automerged_updates: + - match: { dependency_type: "development", update_type: "semver:minor" } + - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/nginx" update_schedule: "daily" + automerged_updates: + - match: { dependency_type: "development", update_type: "semver:minor" } + - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/oracle-xe" update_schedule: "daily" + automerged_updates: + - match: { dependency_type: "development", update_type: "semver:minor" } + - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/postgresql" update_schedule: "daily" + automerged_updates: + - match: { dependency_type: "development", update_type: "semver:minor" } + - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/pulsar" update_schedule: "daily" + automerged_updates: + - match: { dependency_type: "development", update_type: "semver:minor" } + - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/selenium" update_schedule: "daily" + automerged_updates: + - match: { dependency_type: "development", update_type: "semver:minor" } + - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/spock" update_schedule: "daily" + automerged_updates: + - match: { dependency_type: "development", update_type: "semver:minor" } + - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/vault" update_schedule: "daily" + automerged_updates: + - match: { dependency_type: "development", update_type: "semver:minor" } + - match: { dependency_type: "production", update_type: "semver:minor" } # Examples - package_manager: "java:gradle" directory: "/examples" update_schedule: "daily" automerged_updates: - - match: { dependency_type: "development", update_type: "all" } - - match: { dependency_type: "production", update_type: "all" } + - match: { dependency_type: "development", update_type: "semver:minor" } + - match: { dependency_type: "production", update_type: "semver:minor" } From fe5952c3af9b84fe043de1441a3de11e149ad71a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 4 Mar 2019 12:55:45 +0000 Subject: [PATCH 0637/5615] Bump commons-codec from 1.11 to 1.12 in /core (#1238) Bumps [commons-codec](https://github.com/apache/commons-codec) from 1.11 to 1.12. - [Release notes](https://github.com/apache/commons-codec/releases) - [Changelog](https://github.com/apache/commons-codec/blob/master/RELEASE-NOTES.txt) - [Commits](https://github.com/apache/commons-codec/commits) Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index ca09860f6e4..899dfaffdbe 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -110,7 +110,7 @@ dependencies { shaded 'org.zeroturnaround:zt-exec:1.10' shaded 'commons-lang:commons-lang:2.6' shaded 'commons-io:commons-io:2.6' - shaded 'commons-codec:commons-codec:1.11' + shaded 'commons-codec:commons-codec:1.12' // docker-java uses SslConfigurator from jersey-common for TLS shaded ('org.glassfish.jersey.core:jersey-common:2.27') { // SslConfigurator doesn't use classes from dependencies From 6007f7169265d8cf8b7294bdb9ce7d0c22f9e543 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 4 Mar 2019 12:55:58 +0000 Subject: [PATCH 0638/5615] Bump cassandra-driver-core from 3.6.0 to 3.7.1 in /modules/cassandra (#1290) Bumps [cassandra-driver-core](https://github.com/datastax/java-driver) from 3.6.0 to 3.7.1. - [Release notes](https://github.com/datastax/java-driver/releases) - [Commits](https://github.com/datastax/java-driver/compare/3.6.0...3.7.1) Signed-off-by: dependabot[bot] --- modules/cassandra/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cassandra/build.gradle b/modules/cassandra/build.gradle index 0ec1cb8f2af..9704a05b69d 100644 --- a/modules/cassandra/build.gradle +++ b/modules/cassandra/build.gradle @@ -2,5 +2,5 @@ description = "TestContainers :: Cassandra" dependencies { compile project(":database-commons") - compile "com.datastax.cassandra:cassandra-driver-core:3.6.0" + compile "com.datastax.cassandra:cassandra-driver-core:3.7.1" } From 77597e3241e27838329462739cd4b938457cbf93 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 4 Mar 2019 12:58:26 +0000 Subject: [PATCH 0639/5615] Bump java-client from 2.7.3 to 2.7.4 in /modules/couchbase (#1275) Bumps [java-client](https://github.com/couchbase/couchbase-java-client) from 2.7.3 to 2.7.4.
Commits - [`e759d32`](https://github.com/couchbase/couchbase-java-client/commit/e759d326b7fce9133127858e477d839ce099c3fd) Prepare 2.7.4 release - [`588dde8`](https://github.com/couchbase/couchbase-java-client/commit/588dde8ec9fbf101e25ada0755413a1995a10d66) JCBC-1292 Fix OSGi to export only java-client package - [`8392859`](https://github.com/couchbase/couchbase-java-client/commit/83928592d78eccbcdaa09e7f9535f3f10b540800) Start 2.7.4 development - See full diff in [compare view](https://github.com/couchbase/couchbase-java-client/compare/2.7.3...2.7.4)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=com.couchbase.client:java-client&package-manager=gradle&previous-version=2.7.3&new-version=2.7.4)](https://dependabot.com/compatibility-score.html?dependency-name=com.couchbase.client:java-client&package-manager=gradle&previous-version=2.7.3&new-version=2.7.4) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot ignore this [patch|minor|major] version` will close this PR and stop Dependabot creating any more for this minor/major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index bbbea2b036d..2bf1055038b 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -2,5 +2,5 @@ description = "Testcontainers :: Couchbase" dependencies { compile project(':testcontainers') - compile 'com.couchbase.client:java-client:2.7.3' + compile 'com.couchbase.client:java-client:2.7.4' } From 4583015b668a314ab271e9ac25a9612c6329fe80 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 4 Mar 2019 12:58:34 +0000 Subject: [PATCH 0640/5615] Bump slf4j-api from 1.7.25 to 1.7.26 in /core (#1254) Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 1.7.25 to 1.7.26. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_1.7.25...v_1.7.26) Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 899dfaffdbe..bea5b94da63 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -73,7 +73,7 @@ project.tasks.check.dependsOn(jarFileTest) dependencies { compile 'junit:junit:4.12' - compile 'org.slf4j:slf4j-api:1.7.25' + compile 'org.slf4j:slf4j-api:1.7.26' compile 'org.jetbrains:annotations:17.0.0' compile 'javax.annotation:javax.annotation-api:1.3.2' compile 'org.apache.commons:commons-compress:1.18' From 2033706bf6e5305d8031fb9efd1d7d09ec98689b Mon Sep 17 00:00:00 2001 From: stsefanenko Date: Sat, 9 Mar 2019 22:54:35 +0300 Subject: [PATCH 0641/5615] Example cucumber test (#1180) * cucumber example * remove spring boot dependency * integrate with TestLifecycleAware interface and make it record video after a test failure * fix parameter after review * fixes after review * move dependency to module * update to gradle * Update examples/cucumber/src/test/java/org/testcontainsers/examples/Stepdefs.java Co-Authored-By: stsefanenko --- examples/cucumber/build.gradle | 16 +++++ .../examples/CucumberTest.java | 12 ++++ .../testcontainsers/examples/Stepdefs.java | 70 +++++++++++++++++++ .../examples/is_search_possible.feature | 12 ++++ examples/settings.gradle | 1 + 5 files changed, 111 insertions(+) create mode 100644 examples/cucumber/build.gradle create mode 100644 examples/cucumber/src/test/java/org/testcontainsers/examples/CucumberTest.java create mode 100644 examples/cucumber/src/test/java/org/testcontainsers/examples/Stepdefs.java create mode 100644 examples/cucumber/src/test/resources/org/testcontainsers/examples/is_search_possible.feature diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle new file mode 100644 index 00000000000..f8744b9d0e8 --- /dev/null +++ b/examples/cucumber/build.gradle @@ -0,0 +1,16 @@ +plugins { + id 'java' +} + +repositories { + jcenter() +} + +dependencies { + implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.14.0' + implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.14.0' + implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' + implementation 'io.cucumber:cucumber-java:2.3.1' + testImplementation 'io.cucumber:cucumber-junit:2.3.1' + testImplementation 'org.testcontainers:selenium' +} diff --git a/examples/cucumber/src/test/java/org/testcontainsers/examples/CucumberTest.java b/examples/cucumber/src/test/java/org/testcontainsers/examples/CucumberTest.java new file mode 100644 index 00000000000..df1a449f1a9 --- /dev/null +++ b/examples/cucumber/src/test/java/org/testcontainsers/examples/CucumberTest.java @@ -0,0 +1,12 @@ +package org.testcontainsers.examples; + +import cucumber.api.CucumberOptions; +import cucumber.api.junit.Cucumber; +import org.junit.runner.RunWith; + +@RunWith(Cucumber.class) +@CucumberOptions(plugin = {"pretty"}) +public class CucumberTest { + + +} diff --git a/examples/cucumber/src/test/java/org/testcontainsers/examples/Stepdefs.java b/examples/cucumber/src/test/java/org/testcontainsers/examples/Stepdefs.java new file mode 100644 index 00000000000..ee3c05d4457 --- /dev/null +++ b/examples/cucumber/src/test/java/org/testcontainsers/examples/Stepdefs.java @@ -0,0 +1,70 @@ +package org.testcontainsers.examples; + +import cucumber.api.Scenario; +import cucumber.api.java.After; +import cucumber.api.java.Before; +import cucumber.api.java.en.Given; +import cucumber.api.java.en.Then; +import cucumber.api.java.en.When; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.chrome.ChromeOptions; +import org.openqa.selenium.remote.RemoteWebDriver; +import org.testcontainers.containers.BrowserWebDriverContainer; +import org.testcontainers.lifecycle.TestDescription; + +import java.io.File; +import java.util.List; +import java.util.Optional; + +import static junit.framework.TestCase.assertEquals; +import static org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode.RECORD_ALL; + +public class Stepdefs { + + private BrowserWebDriverContainer container = new BrowserWebDriverContainer() + .withCapabilities(new ChromeOptions()) + .withRecordingMode(RECORD_ALL, new File("build")); + + private String location; + private String answer; + + @Before + public void beforeScenario() { + container.start(); + } + + @After + public void afterScenario(Scenario scenario) { + container.afterTest(new TestDescription() { + @Override + public String getTestId() { + return scenario.getId(); + } + + @Override + public String getFilesystemFriendlyName() { + return scenario.getName(); + } + }, Optional.of(scenario).filter(Scenario::isFailed).map(__ -> new RuntimeException())); + } + + @Given("^location is \"([^\"]*)\"$") + public void locationIs(String location) throws Exception { + this.location = location; + } + + @When("^I ask is it possible to search here$") + public void iAskIsItPossibleToSearchHere() throws Exception { + RemoteWebDriver driver = container.getWebDriver(); + driver.get(location); + List searchInputs = driver.findElementsByTagName("input"); + answer = searchInputs != null && searchInputs.size() > 0 ? "YES" : "NOPE"; + } + + @Then("^I should be told \"([^\"]*)\"$") + public void iShouldBeTold(String expected) throws Exception { + assertEquals(expected, answer); + } + + +} diff --git a/examples/cucumber/src/test/resources/org/testcontainsers/examples/is_search_possible.feature b/examples/cucumber/src/test/resources/org/testcontainsers/examples/is_search_possible.feature new file mode 100644 index 00000000000..48ab4a7d5dc --- /dev/null +++ b/examples/cucumber/src/test/resources/org/testcontainsers/examples/is_search_possible.feature @@ -0,0 +1,12 @@ +Feature: Is it possible to search here? + Everybody wants to search something + + Scenario Outline: Is search possible here + Given location is "" + When I ask is it possible to search here + Then I should be told "" + + Examples: + | location | answer | + | https://www.google.com/ | YES | + | https://www.google.com/appsstatus | NOPE | \ No newline at end of file diff --git a/examples/settings.gradle b/examples/settings.gradle index 52617937c04..671646b76d6 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -10,4 +10,5 @@ include 'redis-backed-cache-testng' include 'selenium-container' include 'singleton-container' include 'spring-boot' +include 'cucumber' From fcd32d63fd733e429aa854fdffcb093aa28302fd Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 10 Mar 2019 13:36:30 +0000 Subject: [PATCH 0642/5615] Update to CircleCI 2.0 (#1289) --- .circleci/config.yml | 121 +++++++++++++++++++++++++++++++++++++++++++ circle.yml | 112 --------------------------------------- 2 files changed, 121 insertions(+), 112 deletions(-) create mode 100644 .circleci/config.yml delete mode 100644 circle.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000000..12192fa16ae --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,121 @@ +version: 2 +jobs: + core: + machine: + enabled: true + steps: + - checkout + - run: + command: ./gradlew testcontainers:build -x test -x check + - run: + command: ./gradlew testcontainers:check --scan + - run: + name: Save test results + command: | + mkdir -p ~/junit/ + find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; + when: always + - store_test_results: + path: ~/junit + - store_artifacts: + path: ~/junit + okhttp: + machine: + enabled: true + steps: + - checkout + - run: + command: | + echo "transport.type=okhttp" >> core/src/test/resources/testcontainers.properties + ./gradlew testcontainers:check --scan + - run: + name: Save test results + command: | + mkdir -p ~/junit/ + find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; + when: always + - store_test_results: + path: ~/junit + modules-no-jdbc-test-no-selenium: + machine: + enabled: true + steps: + - checkout + - run: + command: ./gradlew check -x testcontainers:check -x selenium:check -x jdbc-test:check + --scan + environment: + TZ: /usr/share/zoneinfo/ETC/UTC + - run: + name: Save test results + command: | + mkdir -p ~/junit/ + find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; + when: always + - store_test_results: + path: ~/junit + - store_artifacts: + path: ~/junit + modules-jdbc-test: + machine: + enabled: true + steps: + - checkout + - run: + command: ./gradlew jdbc-test:check --scan + environment: + TZ: /usr/share/zoneinfo/ETC/UTC + - run: + name: Save test results + command: | + mkdir -p ~/junit/ + find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; + when: always + - store_test_results: + path: ~/junit + - store_artifacts: + path: ~/junit + selenium: + machine: + enabled: true + steps: + - checkout + - run: + command: ./gradlew selenium:check --scan + - run: + name: Save test results + command: | + mkdir -p ~/junit/ + find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; + when: always + - store_test_results: + path: ~/junit + - store_artifacts: + path: ~/junit + examples: + machine: + enabled: true + steps: + - checkout + - run: + command: cd examples && ../gradlew check + - run: + name: Save test results + command: | + mkdir -p ~/junit/ + find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; + when: always + - store_test_results: + path: ~/junit + - store_artifacts: + path: ~/junit +workflows: + version: 2 + test_all: + jobs: + - core + - okhttp + - modules-no-jdbc-test-no-selenium + - modules-jdbc-test + - selenium + - examples diff --git a/circle.yml b/circle.yml deleted file mode 100644 index eeafac5237f..00000000000 --- a/circle.yml +++ /dev/null @@ -1,112 +0,0 @@ -version: 2 -executorType: machine -jobs: - core: - steps: - - checkout - - run: - command: ./gradlew testcontainers:build -x test -x check - - run: - command: ./gradlew testcontainers:check --scan - - run: - name: Save test results - command: | - mkdir -p ~/junit/ - find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; - when: always - - store_test_results: - path: ~/junit - - store_artifacts: - path: ~/junit - okhttp: - steps: - - checkout - - run: - command: | - echo "transport.type=okhttp" >> core/src/test/resources/testcontainers.properties - ./gradlew testcontainers:check --scan - - run: - name: Save test results - command: | - mkdir -p ~/junit/ - find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; - when: always - - store_test_results: - path: ~/junit - modules-no-jdbc-test-no-selenium: - steps: - - checkout - - run: - command: ./gradlew check -x testcontainers:check -x selenium:check -x jdbc-test:check --scan - environment: - # Oracle JDBC drivers require a timezone to be set - TZ: "/usr/share/zoneinfo/ETC/UTC" - - run: - name: Save test results - command: | - mkdir -p ~/junit/ - find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; - when: always - - store_test_results: - path: ~/junit - - store_artifacts: - path: ~/junit - modules-jdbc-test: - steps: - - checkout - - run: - command: ./gradlew jdbc-test:check --scan - environment: - # Oracle JDBC drivers require a timezone to be set - TZ: "/usr/share/zoneinfo/ETC/UTC" - - run: - name: Save test results - command: | - mkdir -p ~/junit/ - find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; - when: always - - store_test_results: - path: ~/junit - - store_artifacts: - path: ~/junit - selenium: - steps: - - checkout - - run: - command: ./gradlew selenium:check --scan - - run: - name: Save test results - command: | - mkdir -p ~/junit/ - find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; - when: always - - store_test_results: - path: ~/junit - - store_artifacts: - path: ~/junit - examples: - steps: - - checkout - - run: - command: cd examples && ../gradlew check - - run: - name: Save test results - command: | - mkdir -p ~/junit/ - find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; - when: always - - store_test_results: - path: ~/junit - - store_artifacts: - path: ~/junit - -workflows: - version: 2 - test_all: - jobs: - - core - - okhttp - - modules-no-jdbc-test-no-selenium - - modules-jdbc-test - - selenium - - examples From bb9c10ad743a8ab3c3652c7277535d83850468c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 10 Mar 2019 13:49:33 +0000 Subject: [PATCH 0643/5615] Bump neo4j-java-driver from 1.7.2 to 1.7.3 in /modules/neo4j (#1297) Bumps [neo4j-java-driver](https://github.com/neo4j/neo4j-java-driver) from 1.7.2 to 1.7.3.
Commits - [`742d280`](https://github.com/neo4j/neo4j-java-driver/commit/742d2806be92a17e35fca2aeea85f3fc90ed7700) Merge pull request [#567](https://github-redirect.dependabot.com/neo4j/neo4j-java-driver/issues/567) from ali-ince/1.7-pass-access-mode - [`49ef4db`](https://github.com/neo4j/neo4j-java-driver/commit/49ef4db3cdecae30c00f67ce052293b5b6cefb85) Fixing wrong file header - [`f66e194`](https://github.com/neo4j/neo4j-java-driver/commit/f66e194d6a5386d027804710db0ad89d4c39a596) Merge pull request [#570](https://github-redirect.dependabot.com/neo4j/neo4j-java-driver/issues/570) from zhenlineo/1.7-flaky-test - [`5b58956`](https://github.com/neo4j/neo4j-java-driver/commit/5b58956f5cf9192b4c3e9e85d44164a211f9273f) Fixing the flaky test that is caused by certificate file changes. - [`f9a6fca`](https://github.com/neo4j/neo4j-java-driver/commit/f9a6fca67d6fad483fef13f4c646d65ef17a60a5) Merge pull request [#569](https://github-redirect.dependabot.com/neo4j/neo4j-java-driver/issues/569) from michael-simons/fix-java-doc-trust-strategy - [`bc68f0f`](https://github.com/neo4j/neo4j-java-driver/commit/bc68f0fed8f317c69ec2cfed0f2e41c4b008c2b3) Merge pull request [#566](https://github-redirect.dependabot.com/neo4j/neo4j-java-driver/issues/566) from zhenlineo/1.7-hostname-for-sni - [`41949fc`](https://github.com/neo4j/neo4j-java-driver/commit/41949fce54b516d0ec4b797179c08afe43a830b0) Fix JavaDoc of withTrustStrategy. - [`227c0fc`](https://github.com/neo4j/neo4j-java-driver/commit/227c0fcdd21020f5bf3d36fb30e3f93ba735fbb9) Fix test failure - [`11345c1`](https://github.com/neo4j/neo4j-java-driver/commit/11345c186c9e8b44093ec4cfca1d1160c13e8cb2) Pass access mode as part of statement metadata - [`e9e5a93`](https://github.com/neo4j/neo4j-java-driver/commit/e9e5a9396baf153804c6b2e18c4d669cf13d965b) Fix some test failures due to changes to routing table procedure on read-repl... - Additional commits viewable in [compare view](https://github.com/neo4j/neo4j-java-driver/compare/1.7.2...1.7.3)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=org.neo4j.driver:neo4j-java-driver&package-manager=gradle&previous-version=1.7.2&new-version=1.7.3)](https://dependabot.com/compatibility-score.html?dependency-name=org.neo4j.driver:neo4j-java-driver&package-manager=gradle&previous-version=1.7.2&new-version=1.7.3) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) If all status checks pass Dependabot will automatically merge this pull request. [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot ignore this [patch|minor|major] version` will close this PR and stop Dependabot creating any more for this minor/major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 77535d1e3df..abdd5044cdc 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -29,6 +29,6 @@ dependencies { compile project(":testcontainers") - testCompile "org.neo4j.driver:neo4j-java-driver:1.7.2" + testCompile "org.neo4j.driver:neo4j-java-driver:1.7.3" testCompile 'org.assertj:assertj-core:3.12.1' } From 8319d464b738ac899d76f267f793398098229e1a Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 12 Mar 2019 19:41:03 +0000 Subject: [PATCH 0644/5615] Fix failing docs example test (#1300) For some reason this test is now failing, probably due to insufficient memory being allocated for the process. Was failing, now fixed, for Docker for Mac Version 2.0.0.3 (31259) --- docs/examples/src/test/java/generic/CmdModifierTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/examples/src/test/java/generic/CmdModifierTest.java b/docs/examples/src/test/java/generic/CmdModifierTest.java index 9f87a4da21c..6d075c0bccc 100644 --- a/docs/examples/src/test/java/generic/CmdModifierTest.java +++ b/docs/examples/src/test/java/generic/CmdModifierTest.java @@ -20,8 +20,8 @@ public class CmdModifierTest { // memory { @Rule public GenericContainer memoryLimitedRedis = new GenericContainer<>("redis:3.0.2") - .withCreateContainerCmdModifier(cmd -> cmd.withMemory((long) 4 * 1024 * 1024)) - .withCreateContainerCmdModifier(cmd -> cmd.withMemorySwap((long) 4 * 1024 * 1024)); + .withCreateContainerCmdModifier(cmd -> cmd.withMemory((long) 8 * 1024 * 1024)) + .withCreateContainerCmdModifier(cmd -> cmd.withMemorySwap((long) 12 * 1024 * 1024)); // } @@ -34,6 +34,6 @@ public void testHostnameModified() throws IOException, InterruptedException { @Test public void testMemoryLimitModified() throws IOException, InterruptedException { final Container.ExecResult execResult = memoryLimitedRedis.execInContainer("cat", "/sys/fs/cgroup/memory/memory.limit_in_bytes"); - assertEquals("4194304", execResult.getStdout().trim()); + assertEquals("8388608", execResult.getStdout().trim()); } } From e16f2422df71a263d57f9f97e43690ef37a36d79 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 14 Mar 2019 21:22:14 +0100 Subject: [PATCH 0645/5615] Remove `okhttp` stage from CircleCI --- .circleci/config.yml | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 12192fa16ae..3ea6d3901ec 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -19,23 +19,6 @@ jobs: path: ~/junit - store_artifacts: path: ~/junit - okhttp: - machine: - enabled: true - steps: - - checkout - - run: - command: | - echo "transport.type=okhttp" >> core/src/test/resources/testcontainers.properties - ./gradlew testcontainers:check --scan - - run: - name: Save test results - command: | - mkdir -p ~/junit/ - find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; - when: always - - store_test_results: - path: ~/junit modules-no-jdbc-test-no-selenium: machine: enabled: true From 91ca6800758a4dc5fc536fad0a62e5a7d8ad5678 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 14 Mar 2019 21:26:05 +0100 Subject: [PATCH 0646/5615] remove `okhttp` from the workflows --- .circleci/config.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3ea6d3901ec..08c0f4aabab 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -97,7 +97,6 @@ workflows: test_all: jobs: - core - - okhttp - modules-no-jdbc-test-no-selenium - modules-jdbc-test - selenium From 1fe2804714887c0a43077901d5446daeb2c75ab1 Mon Sep 17 00:00:00 2001 From: Manik Magar Date: Sun, 17 Mar 2019 11:54:11 -0400 Subject: [PATCH 0647/5615] Add JDBC custom credential support for postgres (#1293) --- .../testcontainers/jdbc/JDBCDriverTest.java | 23 +++++++++++++----- .../JdbcDatabaseContainerProvider.java | 22 +++++++++++++++++ .../containers/MariaDBContainerProvider.java | 24 ++++--------------- .../containers/MySQLContainerProvider.java | 18 +------------- .../containers/PostgisContainerProvider.java | 12 ++++++++++ .../PostgreSQLContainerProvider.java | 14 +++++++++++ 6 files changed, 70 insertions(+), 43 deletions(-) diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java index f61f6276c5a..a94f0b0897b 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java @@ -50,9 +50,9 @@ public static Iterable data() { {"jdbc:tc:mysql:5.5.43://hostname/databasename?TC_INITSCRIPT=somepath/init_unicode_mysql.sql&useUnicode=yes&characterEncoding=utf8", EnumSet.of(Options.CharacterSet)}, {"jdbc:tc:mysql:5.5.43://hostname/databasename", EnumSet.noneOf(Options.class)}, {"jdbc:tc:mysql:5.5.43://hostname/databasename?useSSL=false", EnumSet.noneOf(Options.class)}, - {"jdbc:tc:postgresql:9.6.8://hostname/databasename", EnumSet.noneOf(Options.class)}, - {"jdbc:tc:postgis://hostname/databasename", EnumSet.noneOf(Options.class)}, - {"jdbc:tc:postgis:9.6://hostname/databasename", EnumSet.noneOf(Options.class)}, + {"jdbc:tc:postgresql:9.6.8://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, + {"jdbc:tc:postgis://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, + {"jdbc:tc:postgis:9.6://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, {"jdbc:tc:mysql:5.6://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override", EnumSet.of(Options.CustomIniFile)}, {"jdbc:tc:mariadb://hostname/databasename", EnumSet.noneOf(Options.class)}, {"jdbc:tc:mariadb://hostname/databasename?user=someuser&TC_INITSCRIPT=somepath/init_mariadb.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, @@ -135,16 +135,27 @@ private void performTestForScriptedSchema(String jdbcUrl) throws SQLException { private void performTestForJDBCParamUsage(String jdbcUrl) throws SQLException { try (HikariDataSource dataSource = getDataSource(jdbcUrl, 1)) { - boolean result = new QueryRunner(dataSource).query("select CURRENT_USER()", rs -> { + boolean result = new QueryRunner(dataSource).query("select CURRENT_USER", rs -> { rs.next(); String resultUser = rs.getString(1); - assertEquals("User from query param is created.", "someuser@%", resultUser); + // Not all databases (eg. Postgres) return @% at the end of user name. We just need to make sure the user name matches. + if (resultUser.endsWith("@%")) { + resultUser = resultUser.substring(0, resultUser.length() - 2); + } + assertEquals("User from query param is created.", "someuser", resultUser); return true; }); assertTrue("The database returned a record as expected", result); - result = new QueryRunner(dataSource).query("SELECT DATABASE()", rs -> { + String databaseQuery = "SELECT DATABASE()"; + // Postgres does not have Database() as a function + String databaseType = ConnectionUrl.newInstance(jdbcUrl).getDatabaseType(); + if (databaseType.equalsIgnoreCase("postgresql") || databaseType.equalsIgnoreCase("postgis")) { + databaseQuery = "SELECT CURRENT_DATABASE()"; + } + + result = new QueryRunner(dataSource).query(databaseQuery, rs -> { rs.next(); String resultDB = rs.getString(1); assertEquals("Database name from URL String is used.", "databasename", resultDB); diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainerProvider.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainerProvider.java index 2a73879a572..f2ebde857c5 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainerProvider.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainerProvider.java @@ -4,6 +4,8 @@ import lombok.extern.slf4j.Slf4j; +import java.util.Objects; + /** * Base class for classes that can provide a JDBC container. */ @@ -49,4 +51,24 @@ public JdbcDatabaseContainer newInstance(ConnectionUrl url) { return newInstance(); } } + + protected JdbcDatabaseContainer newInstanceFromConnectionUrl(ConnectionUrl connectionUrl, final String userParamName, final String pwdParamName) { + Objects.requireNonNull(connectionUrl, "Connection URL cannot be null"); + + final String databaseName = connectionUrl.getDatabaseName().orElse("test"); + final String user = connectionUrl.getQueryParameters().getOrDefault(userParamName, "test"); + final String password = connectionUrl.getQueryParameters().getOrDefault(pwdParamName, "test"); + + final JdbcDatabaseContainer instance; + if (connectionUrl.getImageTag().isPresent()) { + instance = newInstance(connectionUrl.getImageTag().get()); + } else { + instance = newInstance(); + } + + return instance + .withDatabaseName(databaseName) + .withUsername(user) + .withPassword(password); + } } diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java index deb1b2c5c5e..86f30ee59e5 100644 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java @@ -8,7 +8,7 @@ * Factory for MariaDB org.testcontainers.containers. */ public class MariaDBContainerProvider extends JdbcDatabaseContainerProvider { - + private static final String USER_PARAM = "user"; private static final String PASSWORD_PARAM = "password"; @@ -27,26 +27,10 @@ public JdbcDatabaseContainer newInstance() { public JdbcDatabaseContainer newInstance(String tag) { return new MariaDBContainer(MariaDBContainer.IMAGE + ":" + tag); } - + @Override public JdbcDatabaseContainer newInstance(ConnectionUrl connectionUrl) { - Objects.requireNonNull(connectionUrl, "Connection URL cannot be null"); - - final String databaseName = connectionUrl.getDatabaseName().orElse("test"); - final String user = connectionUrl.getQueryParameters().getOrDefault(USER_PARAM, "test"); - final String password = connectionUrl.getQueryParameters().getOrDefault(PASSWORD_PARAM, "test"); - - final JdbcDatabaseContainer instance; - if (connectionUrl.getImageTag().isPresent()) { - instance = newInstance(connectionUrl.getImageTag().get()); - } else { - instance = newInstance(); - } - - return instance - .withDatabaseName(databaseName) - .withUsername(user) - .withPassword(password); + return newInstanceFromConnectionUrl(connectionUrl, USER_PARAM, PASSWORD_PARAM); } - + } diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainerProvider.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainerProvider.java index cfc072707da..7d4919f4b26 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainerProvider.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainerProvider.java @@ -34,23 +34,7 @@ public JdbcDatabaseContainer newInstance(String tag) { @Override public JdbcDatabaseContainer newInstance(ConnectionUrl connectionUrl) { - Objects.requireNonNull(connectionUrl, "Connection URL cannot be null"); - - final String databaseName = connectionUrl.getDatabaseName().orElse("test"); - final String user = connectionUrl.getQueryParameters().getOrDefault(USER_PARAM, "test"); - final String password = connectionUrl.getQueryParameters().getOrDefault(PASSWORD_PARAM, "test"); - - final JdbcDatabaseContainer instance; - if (connectionUrl.getImageTag().isPresent()) { - instance = newInstance(connectionUrl.getImageTag().get()); - } else { - instance = newInstance(); - } - - return instance - .withDatabaseName(databaseName) - .withUsername(user) - .withPassword(password); + return newInstanceFromConnectionUrl(connectionUrl, USER_PARAM, PASSWORD_PARAM); } } diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java index 644a5ef8c84..83f15e3fd6f 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java @@ -1,5 +1,9 @@ package org.testcontainers.containers; +import org.testcontainers.jdbc.ConnectionUrl; + +import java.util.Objects; + /** * Factory for PostGIS containers, which are a special flavour of PostgreSQL. */ @@ -8,6 +12,9 @@ public class PostgisContainerProvider extends JdbcDatabaseContainerProvider { private static final String NAME = "postgis"; private static final String DEFAULT_TAG = "10"; private static final String DEFAULT_IMAGE = "mdillon/postgis"; + public static final String USER_PARAM = "user"; + public static final String PASSWORD_PARAM = "password"; + @Override public boolean supports(String databaseType) { @@ -23,4 +30,9 @@ public JdbcDatabaseContainer newInstance() { public JdbcDatabaseContainer newInstance(String tag) { return new PostgreSQLContainer(DEFAULT_IMAGE + ":" + tag); } + + @Override + public JdbcDatabaseContainer newInstance(ConnectionUrl connectionUrl) { + return newInstanceFromConnectionUrl(connectionUrl, USER_PARAM, PASSWORD_PARAM); + } } diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainerProvider.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainerProvider.java index 394fd5d49b8..77a0329c22c 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainerProvider.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainerProvider.java @@ -1,9 +1,17 @@ package org.testcontainers.containers; +import org.testcontainers.jdbc.ConnectionUrl; + +import java.util.Objects; + /** * Factory for PostgreSQL containers. */ public class PostgreSQLContainerProvider extends JdbcDatabaseContainerProvider { + + public static final String USER_PARAM = "user"; + public static final String PASSWORD_PARAM = "password"; + @Override public boolean supports(String databaseType) { return databaseType.equals(PostgreSQLContainer.NAME); @@ -18,4 +26,10 @@ public JdbcDatabaseContainer newInstance() { public JdbcDatabaseContainer newInstance(String tag) { return new PostgreSQLContainer(PostgreSQLContainer.IMAGE + ":" + tag); } + + @Override + public JdbcDatabaseContainer newInstance(ConnectionUrl connectionUrl) { + return newInstanceFromConnectionUrl(connectionUrl, USER_PARAM, PASSWORD_PARAM); + } + } From e326e3ec42052bb6424cb0193c178b85fd1d1457 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 17 Mar 2019 15:55:54 +0000 Subject: [PATCH 0648/5615] Bump mockito-core from 2.24.5 to 2.25.0 in /core (#1294) Bumps [mockito-core](https://github.com/mockito/mockito) from 2.24.5 to 2.25.0.
Release notes *Sourced from [mockito-core's releases](https://github.com/mockito/mockito/releases).* > ## v2.25.0 > *Release notes were automatically generated by [Shipkit](http://shipkit.org/)* > > #### 2.25.0 > - 2019-03-05 - [11 commits](https://github.com/mockito/mockito/compare/v2.24.10...v2.25.0) by [Szczepan Faber](https://github.com/mockitoguy) (10), Garfield Tan (1) - published to [![Bintray](https://img.shields.io/badge/Bintray-2.25.0-green.svg)](https://bintray.com/mockito/maven/mockito/2.25.0) > - New API to clean up all inline mocks after test [([#1619](https://github-redirect.dependabot.com/mockito/mockito/issues/1619))](https://github-redirect.dependabot.com/mockito/mockito/pull/1619) > - Memory leak in mockito-inline calling method on mock with at least a mock as parameter [([#1614](https://github-redirect.dependabot.com/mockito/mockito/issues/1614))](https://github-redirect.dependabot.com/mockito/mockito/issues/1614) > - Cross-references and a single spy cause memory leak [([#1533](https://github-redirect.dependabot.com/mockito/mockito/issues/1533))](https://github-redirect.dependabot.com/mockito/mockito/issues/1533) > - Nested spies cause memory leaks [([#1532](https://github-redirect.dependabot.com/mockito/mockito/issues/1532))](https://github-redirect.dependabot.com/mockito/mockito/issues/1532) > > ## v2.24.10 > *Release notes were automatically generated by [Shipkit](http://shipkit.org/)* > > #### 2.24.10 > - 2019-03-05 - [4 commits](https://github.com/mockito/mockito/compare/v2.24.9...v2.24.10) by [Tim van der Lippe](https://github.com/TimvdLippe) - published to [![Bintray](https://img.shields.io/badge/Bintray-2.24.10-green.svg)](https://bintray.com/mockito/maven/mockito-development/2.24.10) > - Prevent NPE in findTypeFromGenericInArguments [([#1648](https://github-redirect.dependabot.com/mockito/mockito/issues/1648))](https://github-redirect.dependabot.com/mockito/mockito/pull/1648) > > ## v2.24.9 > *Release notes were automatically generated by [Shipkit](http://shipkit.org/)* > > #### 2.24.9 > - 2019-03-04 - [12 commits](https://github.com/mockito/mockito/compare/v2.24.7...v2.24.9) by 6 authors - published to [![Bintray](https://img.shields.io/badge/Bintray-2.24.9-green.svg)](https://bintray.com/mockito/maven/mockito-development/2.24.9) > - Commits: [Brice Dutheil](https://github.com/bric3) (5), [Tim van der Lippe](https://github.com/TimvdLippe) (3), [epeee](https://github.com/epeee) (1), [Fr Jeremy Krieg](https://github.com/kriegfrj) (1), [PaweB PamuBa](https://github.com/PawelPamula) (1), shipkit-org (1) > - [Java 9 support] ClassCastExceptions with JDK9 javac [([#357](https://github-redirect.dependabot.com/mockito/mockito/issues/357))](https://github-redirect.dependabot.com/mockito/mockito/issues/357) > - [Bugfixes] RETURNS_DEEP_STUBS causes "Raw extraction not supported for : 'null'" in some cases [([#1621](https://github-redirect.dependabot.com/mockito/mockito/issues/1621))](https://github-redirect.dependabot.com/mockito/mockito/issues/1621) > - Update shipkit plugin (v2.1.6) [([#1647](https://github-redirect.dependabot.com/mockito/mockito/issues/1647))](https://github-redirect.dependabot.com/mockito/mockito/pull/1647) > - VerificationCollector to handle non-matching args and other assertions [([#1644](https://github-redirect.dependabot.com/mockito/mockito/issues/1644))](https://github-redirect.dependabot.com/mockito/mockito/pull/1644) > - VerificationCollector doesn't work for invocations with non-matching args [([#1642](https://github-redirect.dependabot.com/mockito/mockito/issues/1642))](https://github-redirect.dependabot.com/mockito/mockito/issues/1642) > - Fix returns mocks for final classes [([#1641](https://github-redirect.dependabot.com/mockito/mockito/issues/1641))](https://github-redirect.dependabot.com/mockito/mockito/pull/1641) > - Removes inaccessible links from javadocs in Mockito.java [([#1639](https://github-redirect.dependabot.com/mockito/mockito/issues/1639))](https://github-redirect.dependabot.com/mockito/mockito/pull/1639) > - Mockito.java contains inaccessible links to articles. [([#1638](https://github-redirect.dependabot.com/mockito/mockito/issues/1638))](https://github-redirect.dependabot.com/mockito/mockito/issues/1638) > - Handle terminal type var with bounds [([#1624](https://github-redirect.dependabot.com/mockito/mockito/issues/1624))](https://github-redirect.dependabot.com/mockito/mockito/pull/1624) > - Return null instead of causing a CCE [([#1612](https://github-redirect.dependabot.com/mockito/mockito/issues/1612))](https://github-redirect.dependabot.com/mockito/mockito/pull/1612) > > ## v2.24.8 > *Release notes were automatically generated by [Shipkit](http://shipkit.org/)* > > #### 2.24.8 > - 2019-03-01 - [2 commits](https://github.com/mockito/mockito/compare/v2.24.7...v2.24.8) by [PaweB PamuBa](https://github.com/PawelPamula) (1), [shipkit-org](https://github.com/shipkit-org) (1) - published to [![Bintray](https://img.shields.io/badge/Bintray-2.24.8-green.svg)](https://bintray.com/mockito/maven/mockito-development/2.24.8) > - Removes inaccessible links from javadocs in Mockito.java [([#1639](https://github-redirect.dependabot.com/mockito/mockito/issues/1639))](https://github-redirect.dependabot.com/mockito/mockito/pull/1639) > - Mockito.java contains inaccessible links to articles. [([#1638](https://github-redirect.dependabot.com/mockito/mockito/issues/1638))](https://github-redirect.dependabot.com/mockito/mockito/issues/1638) > > ## v2.24.7 > *Release notes were automatically generated by [Shipkit](http://shipkit.org/)* > > #### 2.24.7 > - 2019-02-28 - [2 commits](https://github.com/mockito/mockito/compare/v2.24.6...v2.24.7) by [shipkit-org](https://github.com/shipkit-org) (1), [Tim van der Lippe](https://github.com/TimvdLippe) (1) - published to [![Bintray](https://img.shields.io/badge/Bintray-2.24.7-green.svg)](https://bintray.com/mockito/maven/mockito-development/2.24.7) > - Fix handling of generics in ReturnsMocks [([#1635](https://github-redirect.dependabot.com/mockito/mockito/issues/1635))](https://github-redirect.dependabot.com/mockito/mockito/pull/1635) > > ## v2.24.6 > ... (truncated)
Commits - [`70c6fa4`](https://github.com/mockito/mockito/commit/70c6fa4a056e5bf6ce1c464d5e4c5bd6431a2828) 2.25.0 release (previous 2.24.10) + release notes updated by CI build 3956 - [`a7ee084`](https://github.com/mockito/mockito/commit/a7ee084e8501b29d74466213c869f9acbd26635c) Merge pull request [#1619](https://github-redirect.dependabot.com/mockito/mockito/issues/1619) from ttanxu/release/2.x - [`959cba4`](https://github.com/mockito/mockito/commit/959cba41910f7bf215fe66d2932bf3b348d83014) Merge branch 'release/2.x' into release/2.x - [`1641868`](https://github.com/mockito/mockito/commit/1641868a4651bf5c020f7afeb50fc728c3e38d27) Javadoc - [`d61a8be`](https://github.com/mockito/mockito/commit/d61a8beb9a4bb8234e3685c9c55dff05de8d2673) Added missing coverage - [`af2d33b`](https://github.com/mockito/mockito/commit/af2d33be7fbfecad172cef4da2127b8d9a6fbe31) 2.24.10 release (previous 2.24.9) + release notes updated by CI build 3953 - [`1183d1f`](https://github.com/mockito/mockito/commit/1183d1fa72311ef82998cd91ee5f4bb5182c96d5) Merge pull request [#1648](https://github-redirect.dependabot.com/mockito/mockito/issues/1648) from mockito/TimvdLippe-patch-1 - [`2894895`](https://github.com/mockito/mockito/commit/2894895746950be644af81055bf7543ff4e9f832) Fix checkstyle warnings - [`2d21a5a`](https://github.com/mockito/mockito/commit/2d21a5abc744084646d9939d5ed9d1825695af9a) Add regression test - [`b5e9400`](https://github.com/mockito/mockito/commit/b5e9400b01f8bfbda436c79ce5f857b3dc613657) Prevent NPE in findTypeFromGenericInArguments - Additional commits viewable in [compare view](https://github.com/mockito/mockito/compare/v2.24.5...v2.25.0)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=org.mockito:mockito-core&package-manager=gradle&previous-version=2.24.5&new-version=2.25.0)](https://dependabot.com/compatibility-score.html?dependency-name=org.mockito:mockito-core&package-manager=gradle&previous-version=2.24.5&new-version=2.25.0) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) If all status checks pass Dependabot will automatically merge this pull request. [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot ignore this [patch|minor|major] version` will close this PR and stop Dependabot creating any more for this minor/major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index bea5b94da63..19edabfb813 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -120,7 +120,7 @@ dependencies { testCompile 'redis.clients:jedis:3.0.1' testCompile 'com.rabbitmq:amqp-client:5.6.0' testCompile 'org.mongodb:mongo-java-driver:3.10.1' - testCompile ('org.mockito:mockito-core:2.24.5') { + testCompile ('org.mockito:mockito-core:2.25.0') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From b1b8cf354a0fc15af23d1bd082f817039b89bfab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 17 Mar 2019 15:56:08 +0000 Subject: [PATCH 0649/5615] Bump transport from 6.6.1 to 6.6.2 in /modules/elasticsearch (#1302) Bumps [transport](https://github.com/elastic/elasticsearch) from 6.6.1 to 6.6.2. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v6.6.1...v6.6.2) Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index a41455f76aa..7d8f0f5eb1a 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,5 +3,5 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') testCompile "org.elasticsearch.client:elasticsearch-rest-client:6.6.1" - testCompile "org.elasticsearch.client:transport:6.6.1" + testCompile "org.elasticsearch.client:transport:6.6.2" } From 5a3c9f395043ef2c5d3ff123dfaeb7771b0234fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 17 Mar 2019 15:56:43 +0000 Subject: [PATCH 0650/5615] Bump elasticsearch-rest-client in /modules/elasticsearch (#1301) Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 6.6.1 to 6.6.2. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v6.6.1...v6.6.2) Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 7d8f0f5eb1a..78d8c96a4be 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,6 +2,6 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') - testCompile "org.elasticsearch.client:elasticsearch-rest-client:6.6.1" + testCompile "org.elasticsearch.client:elasticsearch-rest-client:6.6.2" testCompile "org.elasticsearch.client:transport:6.6.2" } From 604fd0ee0e8aba424c8ea63e887b4b941bb533d5 Mon Sep 17 00:00:00 2001 From: Gerasimov Gennady Date: Sun, 17 Mar 2019 23:14:51 +0300 Subject: [PATCH 0651/5615] #1256 Added fsync=off option by default (#1280) --- .../org/testcontainers/containers/PostgreSQLContainer.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index 232daa89284..13c5b49efc7 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -22,6 +22,8 @@ public class PostgreSQLContainer> extends private String username = "test"; private String password = "test"; + private static final String FSYNC_OFF_OPTION = "fsync=off"; + public PostgreSQLContainer() { this(IMAGE + ":" + DEFAULT_TAG); } @@ -47,7 +49,7 @@ protected void configure() { addEnv("POSTGRES_DB", databaseName); addEnv("POSTGRES_USER", username); addEnv("POSTGRES_PASSWORD", password); - setCommand("postgres"); + setCommand("postgres", "-c", FSYNC_OFF_OPTION); } @Override From 43e971f43a87f25a75c4410d3199cf576d157c92 Mon Sep 17 00:00:00 2001 From: Dan Markhasin Date: Mon, 18 Mar 2019 17:53:47 +0200 Subject: [PATCH 0652/5615] get exitcode from inspectExecCmd (#1318) * get exitcode from inspectExecCmd * Update core/src/main/java/org/testcontainers/containers/Container.java Co-Authored-By: dmarkhas * Update core/src/main/java/org/testcontainers/containers/Container.java Co-Authored-By: dmarkhas * Update core/src/main/java/org/testcontainers/containers/Container.java Co-Authored-By: dmarkhas * Use Lombok's `@Value` in `ExecResult` * Update Container.java --- .../testcontainers/containers/Container.java | 30 +++++++++---------- .../containers/ExecInContainerPattern.java | 2 ++ .../InternalCommandPortListeningCheck.java | 10 +++---- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/Container.java b/core/src/main/java/org/testcontainers/containers/Container.java index 6c5e547f7fa..ca327149ab5 100644 --- a/core/src/main/java/org/testcontainers/containers/Container.java +++ b/core/src/main/java/org/testcontainers/containers/Container.java @@ -3,7 +3,10 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.model.Bind; import com.github.dockerjava.api.model.Info; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.NonNull; +import lombok.Value; import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.startupcheck.StartupCheckStrategy; @@ -36,24 +39,21 @@ default SELF self() { } /** - * Class to hold results from a "docker exec" command. Note that, due to the limitations of the - * docker API, there's no easy way to get the result code from the process we ran. + * Class to hold results from a "docker exec" command */ + @Value + @AllArgsConstructor(access = AccessLevel.MODULE) class ExecResult { - private final String stdout; - private final String stderr; - + int exitCode; + String stdout; + String stderr; + + /** + * @deprecated should not be instantiated outside of the library, please migrate + */ + @Deprecated public ExecResult(String stdout, String stderr) { - this.stdout = stdout; - this.stderr = stderr; - } - - public String getStdout() { - return stdout; - } - - public String getStderr() { - return stderr; + this(-1, stdout, stderr); } } diff --git a/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java b/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java index af1e7e8d503..3d0f4c2c360 100644 --- a/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java +++ b/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java @@ -82,8 +82,10 @@ public Container.ExecResult execInContainer(InspectContainerResponse containerIn callback.addConsumer(OutputFrame.OutputType.STDERR, stderrConsumer); dockerClient.execStartCmd(execCreateCmdResponse.getId()).exec(callback).awaitCompletion(); + Integer exitCode = dockerClient.inspectExecCmd(execCreateCmdResponse.getId()).exec().getExitCode(); final Container.ExecResult result = new Container.ExecResult( + exitCode, stdoutConsumer.toString(outputCharset), stderrConsumer.toString(outputCharset)); diff --git a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java index a4ba013b9d7..bad30fef653 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java +++ b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java @@ -14,8 +14,6 @@ @RequiredArgsConstructor public class InternalCommandPortListeningCheck implements java.util.concurrent.Callable { - private static final String SUCCESS_MARKER = "TESTCONTAINERS_SUCCESS"; - private final WaitStrategyTarget waitStrategyTarget; private final Set internalPorts; @@ -30,14 +28,14 @@ public Boolean call() { private void tryPort(Integer internalPort) { String[][] commands = { - {"/bin/sh", "-c", format("cat /proc/net/tcp{,6} | awk '{print $2}' | grep -i :%x && echo %s", internalPort, SUCCESS_MARKER)}, - {"/bin/sh", "-c", format("nc -vz -w 1 localhost %d && echo %s", internalPort, SUCCESS_MARKER)}, - {"/bin/bash", "-c", format(" Date: Mon, 18 Mar 2019 21:18:21 +0100 Subject: [PATCH 0653/5615] CredsStore validation for RegistryAuthLocator (#1303) ## Fixes `RegistryAuthLocator` : Add additional validation for existing key `credsStore` but empty value ## Purpose Define behaviour for RegistryAuthLocator on empty values in config Offer: - fail on empty value - ignore field with empty value & log -> return null; ## Background Context Currently experienced problems using testcontainers, I found existing issue for my problem and decided to help. Potentially, put checks for all existing `keys` with empty strings ## References ref: #921 --- .../testcontainers/utility/RegistryAuthLocator.java | 4 ++++ .../utility/RegistryAuthLocatorTest.java | 10 ++++++++++ .../resources/auth-config/config-with-store-empty.json | 9 +++++++++ 3 files changed, 23 insertions(+) create mode 100644 core/src/test/resources/auth-config/config-with-store-empty.json diff --git a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java index efe7303d581..402a15d58b4 100644 --- a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java +++ b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java @@ -183,6 +183,10 @@ private AuthConfig authConfigUsingStore(final JsonNode config, final String repo final JsonNode credsStoreNode = config.get("credsStore"); if (credsStoreNode != null && !credsStoreNode.isMissingNode() && credsStoreNode.isTextual()) { final String credsStore = credsStoreNode.asText(); + if (isBlank(credsStore)) { + log.warn("Docker auth config credsStore field will be ignored, because value is blank"); + return null; + } return runCredentialProvider(reposName, credsStore); } return null; diff --git a/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java b/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java index fa1770df4f1..938505c0212 100644 --- a/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java +++ b/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java @@ -101,6 +101,16 @@ public void lookupAuthConfigWithCredentialsNotFound() throws URISyntaxException discoveredMessage); } + @Test + public void lookupAuthConfigWithCredStoreEmpty() throws URISyntaxException { + final RegistryAuthLocator authLocator = createTestAuthLocator("config-with-store-empty.json"); + + DockerImageName dockerImageName = new DockerImageName("registry2.example.com/org/repo"); + final AuthConfig authConfig = authLocator.lookupAuthConfig(dockerImageName, new AuthConfig()); + + assertNull("CredStore field will be ignored, because value is blank", authConfig.getAuth()); + } + @NotNull private RegistryAuthLocator createTestAuthLocator(String configName) throws URISyntaxException { return createTestAuthLocator(configName, new HashMap<>()); diff --git a/core/src/test/resources/auth-config/config-with-store-empty.json b/core/src/test/resources/auth-config/config-with-store-empty.json new file mode 100644 index 00000000000..4106187ff1e --- /dev/null +++ b/core/src/test/resources/auth-config/config-with-store-empty.json @@ -0,0 +1,9 @@ +{ + "auths": { + "registry.example.com": {} + }, + "HttpHeaders": { + "User-Agent": "Docker-Client/18.03.0-ce (darwin)" + }, + "credsStore": "" +} From 21f64ea8accd77f669711d09d0cfc8798cd366b7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 18 Mar 2019 20:19:24 +0000 Subject: [PATCH 0654/5615] Bump junit-jupiter-api from 5.4.0 to 5.4.1 in /modules/junit-jupiter (#1311) Bumps [junit-jupiter-api](https://github.com/junit-team/junit5) from 5.4.0 to 5.4.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.4.0...r5.4.1) Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index fff8099606d..17f8fbc419b 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -2,7 +2,7 @@ description = "Testcontainers :: JUnit Jupiter Extension" dependencies { compile project(':testcontainers') - compile 'org.junit.jupiter:junit-jupiter-api:5.4.0' + compile 'org.junit.jupiter:junit-jupiter-api:5.4.1' testCompile project(':mysql') testCompile project(':postgresql') From 42a25cba65aa09045dbbe6ae47d80ea2678cc1c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 18 Mar 2019 20:28:22 +0000 Subject: [PATCH 0655/5615] Bump assertj-core from 3.12.1 to 3.12.2 in /modules/pulsar (#1312) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.12.1 to 3.12.2.
Commits - [`5dd7fac`](https://github.com/joel-costigliola/assertj-core/commit/5dd7facdfb3e3a17e5dbbc213ab9ac6656987186) [maven-release-plugin] prepare release assertj-core-3.12.2 - [`d427863`](https://github.com/joel-costigliola/assertj-core/commit/d42786341730e51ee9203d5eacfddb2ea59f5592) Update Guava test dependency to version 27.1-jre. - [`cb5b285`](https://github.com/joel-costigliola/assertj-core/commit/cb5b285ac7e0e74fc6059cf8606fc0a82a8deb12) anySatisfy (Maps): do not continue evaluating elements once we found a match ... - [`8d83c41`](https://github.com/joel-costigliola/assertj-core/commit/8d83c419f34f7dab982cd84d438480cf74e3b17a) Ignore static methods when finding property accessors. Fixes [#1458](https://github-redirect.dependabot.com/joel-costigliola/assertj-core/issues/1458) - [`b600f5b`](https://github.com/joel-costigliola/assertj-core/commit/b600f5be7e49efff47199bc02e2bca3d02466354) Use URI#create (which does not throw a checked Exception) in tests. - [`91af5c9`](https://github.com/joel-costigliola/assertj-core/commit/91af5c9838a05b8b3d7dc7c2e0fdaf0d25712533) [maven-release-plugin] prepare for next development iteration - See full diff in [compare view](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.12.1...assertj-core-3.12.2)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=org.assertj:assertj-core&package-manager=gradle&previous-version=3.12.1&new-version=3.12.2)](https://dependabot.com/compatibility-score.html?dependency-name=org.assertj:assertj-core&package-manager=gradle&previous-version=3.12.1&new-version=3.12.2) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) If all status checks pass Dependabot will automatically merge this pull request. [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot ignore this [patch|minor|major] version` will close this PR and stop Dependabot creating any more for this minor/major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index eaddd4a3862..a67c83cc923 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -4,5 +4,5 @@ dependencies { compile project(':testcontainers') testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.3.0' - testCompile group: 'org.assertj', name: 'assertj-core', version: '3.12.1' + testCompile group: 'org.assertj', name: 'assertj-core', version: '3.12.2' } From 4c6518fa8c974996d2801aec45d01c591d0f10eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 18 Mar 2019 20:28:37 +0000 Subject: [PATCH 0656/5615] Bump mockito-core from 2.25.0 to 2.25.1 in /core (#1313) Bumps [mockito-core](https://github.com/mockito/mockito) from 2.25.0 to 2.25.1. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v2.25.0...v2.25.1) Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 19edabfb813..be95690bc1e 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -120,7 +120,7 @@ dependencies { testCompile 'redis.clients:jedis:3.0.1' testCompile 'com.rabbitmq:amqp-client:5.6.0' testCompile 'org.mongodb:mongo-java-driver:3.10.1' - testCompile ('org.mockito:mockito-core:2.25.0') { + testCompile ('org.mockito:mockito-core:2.25.1') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From 5a7924ac3093d31452b1a84e28375fe7d7bcc0c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 18 Mar 2019 20:28:49 +0000 Subject: [PATCH 0657/5615] Bump junit-jupiter-engine from 5.4.0 to 5.4.1 in /modules/junit-jupiter (#1310) Bumps [junit-jupiter-engine](https://github.com/junit-team/junit5) from 5.4.0 to 5.4.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.4.0...r5.4.1) Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 17f8fbc419b..9ab71fe7c43 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -11,7 +11,7 @@ dependencies { testRuntime 'org.postgresql:postgresql:42.2.5' testRuntime 'mysql:mysql-connector-java:8.0.15' - testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.4.0' + testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.4.1' } test { From a69662d1cb7667cc001e6a6704cb8e44509b4387 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 18 Mar 2019 20:29:01 +0000 Subject: [PATCH 0658/5615] Bump assertj-core from 3.12.1 to 3.12.2 in /modules/kafka (#1316) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.12.1 to 3.12.2. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.12.1...assertj-core-3.12.2) Signed-off-by: dependabot[bot] --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index ad781298deb..6985f7a89a6 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -4,6 +4,6 @@ dependencies { compile project(':testcontainers') testCompile 'org.apache.kafka:kafka-clients:2.1.1' - testCompile 'org.assertj:assertj-core:3.12.1' + testCompile 'org.assertj:assertj-core:3.12.2' testCompile 'com.google.guava:guava:23.0' } From f23dbdf37493a77f80c09dddd57007ee31897845 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 18 Mar 2019 20:29:34 +0000 Subject: [PATCH 0659/5615] Bump assertj-core from 3.12.1 to 3.12.2 in /core (#1314) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [//]: # (dependabot-start) ⚠️ **Dependabot is rebasing this PR** ⚠️ If you make any changes to it yourself then they will take precedence over the rebase. --- [//]: # (dependabot-end) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.12.1 to 3.12.2.
Commits - [`5dd7fac`](https://github.com/joel-costigliola/assertj-core/commit/5dd7facdfb3e3a17e5dbbc213ab9ac6656987186) [maven-release-plugin] prepare release assertj-core-3.12.2 - [`d427863`](https://github.com/joel-costigliola/assertj-core/commit/d42786341730e51ee9203d5eacfddb2ea59f5592) Update Guava test dependency to version 27.1-jre. - [`cb5b285`](https://github.com/joel-costigliola/assertj-core/commit/cb5b285ac7e0e74fc6059cf8606fc0a82a8deb12) anySatisfy (Maps): do not continue evaluating elements once we found a match ... - [`8d83c41`](https://github.com/joel-costigliola/assertj-core/commit/8d83c419f34f7dab982cd84d438480cf74e3b17a) Ignore static methods when finding property accessors. Fixes [#1458](https://github-redirect.dependabot.com/joel-costigliola/assertj-core/issues/1458) - [`b600f5b`](https://github.com/joel-costigliola/assertj-core/commit/b600f5be7e49efff47199bc02e2bca3d02466354) Use URI#create (which does not throw a checked Exception) in tests. - [`91af5c9`](https://github.com/joel-costigliola/assertj-core/commit/91af5c9838a05b8b3d7dc7c2e0fdaf0d25712533) [maven-release-plugin] prepare for next development iteration - See full diff in [compare view](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.12.1...assertj-core-3.12.2)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=org.assertj:assertj-core&package-manager=gradle&previous-version=3.12.1&new-version=3.12.2)](https://dependabot.com/compatibility-score.html?dependency-name=org.assertj:assertj-core&package-manager=gradle&previous-version=3.12.1&new-version=3.12.2) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) If all status checks pass Dependabot will automatically merge this pull request. [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot ignore this [patch|minor|major] version` will close this PR and stop Dependabot creating any more for this minor/major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index be95690bc1e..4f0a0a7392d 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -129,6 +129,6 @@ dependencies { jarFileTestCompileOnly "org.projectlombok:lombok:${lombok.version}" jarFileTestCompile 'junit:junit:4.12' - jarFileTestCompile 'org.assertj:assertj-core:3.12.1' + jarFileTestCompile 'org.assertj:assertj-core:3.12.2' jarFileTestCompile 'org.ow2.asm:asm-debug-all:5.2' } From b12c9470b18beca6c072a7afa38fd9c59f0f983f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 18 Mar 2019 20:29:46 +0000 Subject: [PATCH 0660/5615] Bump assertj-core from 3.12.1 to 3.12.2 in /modules/neo4j (#1315) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.12.1 to 3.12.2. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.12.1...assertj-core-3.12.2) Signed-off-by: dependabot[bot] --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index abdd5044cdc..092cb3a6889 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -30,5 +30,5 @@ dependencies { compile project(":testcontainers") testCompile "org.neo4j.driver:neo4j-java-driver:1.7.3" - testCompile 'org.assertj:assertj-core:3.12.1' + testCompile 'org.assertj:assertj-core:3.12.2' } From 34a2347055f232a98cce15dc7cd148690e699106 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 18 Mar 2019 20:29:58 +0000 Subject: [PATCH 0661/5615] Bump mariadb-java-client from 2.4.0 to 2.4.1 in /modules/mariadb (#1317) Bumps [mariadb-java-client](https://github.com/MariaDB/mariadb-connector-j) from 2.4.0 to 2.4.1. - [Release notes](https://github.com/MariaDB/mariadb-connector-j/releases) - [Commits](https://github.com/MariaDB/mariadb-connector-j/commits) Signed-off-by: dependabot[bot] --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 3cebd55943d..110e63f2444 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: JDBC :: MariaDB" dependencies { compile project(':jdbc') - testCompile 'org.mariadb.jdbc:mariadb-java-client:2.4.0' + testCompile 'org.mariadb.jdbc:mariadb-java-client:2.4.1' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.16' From 9c651fe3052cb2f68d6faca887d1efe9e5e5e7b9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 19 Mar 2019 09:28:33 +0000 Subject: [PATCH 0662/5615] Bump tomcat-jdbc from 9.0.16 to 9.0.17 in /modules/mysql (#1321) Bumps tomcat-jdbc from 9.0.16 to 9.0.17. [![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=org.apache.tomcat:tomcat-jdbc&package-manager=gradle&previous-version=9.0.16&new-version=9.0.17)](https://dependabot.com/compatibility-score.html?dependency-name=org.apache.tomcat:tomcat-jdbc&package-manager=gradle&previous-version=9.0.16&new-version=9.0.17) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) If all status checks pass Dependabot will automatically merge this pull request. [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot ignore this [patch|minor|major] version` will close this PR and stop Dependabot creating any more for this minor/major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index deaf441f633..0703b29ad3a 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -5,7 +5,7 @@ dependencies { testCompile 'mysql:mysql-connector-java:8.0.14' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.16' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.17' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.vibur:vibur-dbcp:22.2' } From 50db30a9d36a21d83b81314216cc2419325140ca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 19 Mar 2019 09:28:52 +0000 Subject: [PATCH 0663/5615] Bump tomcat-jdbc from 9.0.16 to 9.0.17 in /modules/mariadb (#1322) Bumps tomcat-jdbc from 9.0.16 to 9.0.17. Signed-off-by: dependabot[bot] --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 110e63f2444..1f679b7a592 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -6,6 +6,6 @@ dependencies { testCompile 'org.mariadb.jdbc:mariadb-java-client:2.4.1' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.16' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.17' testCompile 'org.vibur:vibur-dbcp:22.2' } From 5109f4c1f4f04460fe5f1b965d974125ec4c0b57 Mon Sep 17 00:00:00 2001 From: Dan Markhasin Date: Tue, 19 Mar 2019 11:53:19 +0200 Subject: [PATCH 0664/5615] updated docs with exitCode (#1319) --- docs/examples/src/test/java/generic/ExecTest.java | 2 ++ docs/features/commands.md | 4 +--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/examples/src/test/java/generic/ExecTest.java b/docs/examples/src/test/java/generic/ExecTest.java index 3ff67956d39..e160390c293 100644 --- a/docs/examples/src/test/java/generic/ExecTest.java +++ b/docs/examples/src/test/java/generic/ExecTest.java @@ -24,7 +24,9 @@ public void testSimpleExec() throws IOException, InterruptedException { // execReadingStdout { Container.ExecResult lsResult = container.execInContainer("ls", "-al", "/"); String stdout = lsResult.getStdout(); + int exitCode = lsResult.getExitCode(); assertTrue(stdout.contains("somefile.txt")); + assertTrue(exitCode == 0); // } } } diff --git a/docs/features/commands.md b/docs/features/commands.md index 238c140765e..197871ebe02 100644 --- a/docs/features/commands.md +++ b/docs/features/commands.md @@ -16,14 +16,12 @@ Your test can execute a command inside a running container, similar to a `docker [Executing a command inside a running container](../examples/src/test/java/generic/ExecTest.java) inside_block:standaloneExec -This can be useful for software that has a command line administration tool. You can also get the output (stdout/stderr) from the command - for example: +This can be useful for software that has a command line administration tool. You can also get the output (stdout/stderr) and exit code from the command - for example: [Executing a command inside a running container and reading the result](../examples/src/test/java/generic/ExecTest.java) inside_block:execReadingStdout -Note that there is no way to get the return code of the executed command, only stdout/stderr. - ## Environment variables To add environment variables to the container, use `withEnv`: From 99e2c1ce00b00c4758cfa4247fc7104475ecae26 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 19 Mar 2019 11:00:07 +0100 Subject: [PATCH 0665/5615] Remove netty transport, rework shading (#1307) * Remove Netty transport from docker-java * Remove Netty transport from docker-java, rework the shading config * exclude public API dependencies from the shaded ones * encode filter's key, remove `okhttp` stage * fix the jupiter tests * fix the spock tests --- build.gradle | 33 +- core/build.gradle | 46 +-- .../testcontainers/JarFileShadingTest.java | 8 +- .../dockerjava/core/util/FiltersEncoder.java | 27 ++ .../DockerClientProviderStrategy.java | 4 - .../TestcontainersDockerCmdExecFactory.java | 317 ------------------ .../utility/ResourceReaper.java | 19 +- .../core/util/FiltersEncoderTest.java | 28 ++ modules/junit-jupiter/build.gradle | 1 + modules/spock/build.gradle | 3 +- 10 files changed, 94 insertions(+), 392 deletions(-) create mode 100644 core/src/main/java/com/github/dockerjava/core/util/FiltersEncoder.java delete mode 100644 core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java create mode 100644 core/src/test/java/com/github/dockerjava/core/util/FiltersEncoderTest.java diff --git a/build.gradle b/build.gradle index 318562a8936..75977bd540f 100644 --- a/build.gradle +++ b/build.gradle @@ -56,32 +56,17 @@ subprojects { configurations = [] classifier = null - relocate('META-INF/native/libnetty', 'META-INF/native/liborg-testcontainers-shaded-netty') - - [ - "org.apache.http", - "org.apache.commons.lang", - "org.apache.commons.io", - "org.apache.commons.codec", - "org.glassfish", - "org.aopalliance", - "org.jvnet", - "javax.annotation", - "javax.inject", - "javax.ws.rs", - "com.fasterxml.jackson", - "jersey.repackaged", - "com.google", - "io.netty", - "org.bouncycastle", - "org.zeroturnaround", - "okhttp3", - "okio", - "org.scalasbt.ipcsocket", - "com.trilead.ssh2", - ].each { relocate(it, "org.testcontainers.shaded.$it") } + // Keep docker-java's package inside the final jar + relocate("com.github.dockerjava", "com.github.dockerjava") } + task relocateShadowJar(type: com.github.jengelman.gradle.plugins.shadow.tasks.ConfigureShadowRelocation) { + target = tasks.shadowJar + prefix = "org.testcontainers.shaded" + } + + tasks.shadowJar.dependsOn tasks.relocateShadowJar + publishing { publications { mavenJava(MavenPublication) { publication -> diff --git a/core/build.gradle b/core/build.gradle index 4f0a0a7392d..1e3083c4542 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -21,7 +21,6 @@ shadowJar { exclude 'org/newsclub/**' [ - 'META-INF/io.netty.versions.properties', 'META-INF/NOTICE', 'META-INF/NOTICE.txt', 'META-INF/LICENSE', @@ -33,31 +32,16 @@ shadowJar { 'META-INF/services/com.fasterxml.jackson.core.*', 'META-INF/services/com.github.dockerjava.api.command.*', 'META-INF/services/javax.ws.rs.ext.*', - 'META-INF/services/org.glassfish.hk2.extension.*', - 'META-INF/services/org.jvnet.hk2.external.generator.*', - 'META-INF/services/org.glassfish.jersey.internal.spi.*', 'META-INF/services/java.security.Provider', 'mozilla/public-suffix-list.txt', ].each { exclude(it) } - dependencies { - include(dependency('org.apache.httpcomponents:.*')) - include(dependency('org.glassfish.*:.*')) - include(dependency('org.aopalliance.*:.*')) - include(dependency('javax.ws.rs:.*')) - include(dependency('com.fasterxml.*:.*')) - include(dependency('com.github.docker-java:.*')) - include(dependency('com.google.guava:.*')) - include(dependency('io.netty:.*')) - include(dependency('org.bouncycastle:.*')) - include(dependency('org.zeroturnaround:zt-exec')) - include(dependency('commons-lang:commons-lang')) - include(dependency('commons-io:commons-io')) - include(dependency('commons-codec:commons-codec')) - include(dependency('com.squareup.okhttp3:.*')) - include(dependency('com.squareup.okio:.*')) - include(dependency('org.scala-sbt.ipcsocket:ipcsocket')) - include(dependency('org.glassfish.main.external:trilead-ssh2-repackaged')) + project.afterEvaluate { + dependencies { + for (id in project.configurations.compile.resolvedConfiguration.resolvedArtifacts*.moduleVersion*.id) { + exclude(dependency("${id.group}:${id.name}")) + } + } } } @@ -98,25 +82,25 @@ dependencies { shaded ('com.github.docker-java:docker-java:3.1.0-rc-4') { exclude(group: 'org.glassfish.jersey.core') exclude(group: 'org.glassfish.jersey.connectors') + exclude(group: 'org.glassfish.jersey.inject') exclude(group: 'log4j') exclude(group: 'com.google.code.findbug') exclude(group: 'com.kohlschutter.junixsocket') + exclude(group: 'io.netty') + exclude(group: 'org.apache.httpcomponents') + exclude(group: 'org.slf4j') + exclude(group: 'org.apache.commons', module: 'commons-compress') } + shaded 'com.squareup.okhttp3:okhttp:3.12.1' shaded 'org.glassfish.main.external:trilead-ssh2-repackaged:4.1.2' - shaded 'javax.ws.rs:javax.ws.rs-api:2.0.1' - shaded 'org.zeroturnaround:zt-exec:1.10' - shaded 'commons-lang:commons-lang:2.6' - shaded 'commons-io:commons-io:2.6' - shaded 'commons-codec:commons-codec:1.12' - // docker-java uses SslConfigurator from jersey-common for TLS - shaded ('org.glassfish.jersey.core:jersey-common:2.27') { - // SslConfigurator doesn't use classes from dependencies - exclude(module: '*') + shaded 'org.zeroturnaround:zt-exec:1.10', { + exclude(group: 'org.slf4j') } + testCompile 'org.apache.httpcomponents:httpclient:4.5.7' testCompile 'redis.clients:jedis:3.0.1' testCompile 'com.rabbitmq:amqp-client:5.6.0' testCompile 'org.mongodb:mongo-java-driver:3.10.1' diff --git a/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java b/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java index ee950f4698c..cf807087d55 100644 --- a/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java +++ b/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java @@ -37,13 +37,7 @@ public void testPackages() throws Exception { public void testMetaInf() throws Exception { assertThatFileList(root.resolve("META-INF")).containsOnly( "MANIFEST.MF", - "services", - "native" - ); - - assertThatFileList(root.resolve("META-INF").resolve("native")).containsOnly( - "liborg-testcontainers-shaded-netty_transport_native_epoll_x86_64.so", - "liborg-testcontainers-shaded-netty_transport_native_kqueue_x86_64.jnilib" + "services" ); } diff --git a/core/src/main/java/com/github/dockerjava/core/util/FiltersEncoder.java b/core/src/main/java/com/github/dockerjava/core/util/FiltersEncoder.java new file mode 100644 index 00000000000..2a6f185462e --- /dev/null +++ b/core/src/main/java/com/github/dockerjava/core/util/FiltersEncoder.java @@ -0,0 +1,27 @@ +package com.github.dockerjava.core.util; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.util.List; +import java.util.Map; + +/** + * Monkey patched {@link FiltersEncoder} to avoid a use of jaxrs. + * See https://github.com/docker-java/docker-java/issues/1176 + */ +public class FiltersEncoder { + + private FiltersEncoder() { + } + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + public static String jsonEncode(Map> filters) { + try { + return OBJECT_MAPPER.writeValueAsString(filters); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } +} diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index 186fd194131..a9321276fd6 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -13,7 +13,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testcontainers.dockerclient.auth.AuthDelegatingDockerClientConfig; -import org.testcontainers.dockerclient.transport.TestcontainersDockerCmdExecFactory; import org.testcontainers.dockerclient.transport.okhttp.OkHttpDockerCmdExecFactory; import org.testcontainers.utility.TestcontainersConfiguration; @@ -173,9 +172,6 @@ protected DockerClient getClientForConfig(DockerClientConfig config) { if ("okhttp".equals(transportType)) { clientBuilder .withDockerCmdExecFactory(new OkHttpDockerCmdExecFactory()); - } else if ("netty".equals(transportType)) { - clientBuilder - .withDockerCmdExecFactory(new TestcontainersDockerCmdExecFactory()); } else { throw new IllegalArgumentException("Unknown transport type: " + transportType); } diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java b/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java deleted file mode 100644 index 1ec43e2b251..00000000000 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/TestcontainersDockerCmdExecFactory.java +++ /dev/null @@ -1,317 +0,0 @@ -package org.testcontainers.dockerclient.transport; - -import com.github.dockerjava.api.command.DockerCmdExecFactory; -import com.github.dockerjava.core.AbstractDockerCmdExecFactory; -import com.github.dockerjava.core.DockerClientConfig; -import com.github.dockerjava.core.SSLConfig; -import com.github.dockerjava.core.WebTarget; -import com.github.dockerjava.netty.NettyWebTarget; -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.*; -import io.netty.channel.epoll.EpollDomainSocketChannel; -import io.netty.channel.epoll.EpollEventLoopGroup; -import io.netty.channel.kqueue.KQueueDomainSocketChannel; -import io.netty.channel.kqueue.KQueueEventLoopGroup; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.DuplexChannel; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.channel.unix.DomainSocketAddress; -import io.netty.channel.unix.UnixChannel; -import io.netty.handler.codec.http.HttpClientCodec; -import io.netty.handler.codec.http.HttpContentDecompressor; -import io.netty.handler.ssl.SslHandler; -import io.netty.handler.timeout.IdleState; -import io.netty.handler.timeout.IdleStateEvent; -import io.netty.handler.timeout.IdleStateHandler; -import io.netty.util.concurrent.DefaultThreadFactory; -import org.apache.commons.lang.SystemUtils; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.testcontainers.DockerClientFactory; - -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLParameters; -import java.io.IOException; -import java.net.SocketTimeoutException; -import java.security.Security; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; - -/** - * This class is a modified version of docker-java's NettyDockerCmdExecFactory v3.1.0-rc-2 - * Changes: - * - daemonized thread factory - * - thread group - * - the logging handler removed - * - - */ -public class TestcontainersDockerCmdExecFactory extends AbstractDockerCmdExecFactory implements DockerCmdExecFactory { - - private static final String THREAD_PREFIX = "testcontainers-netty"; - - /* - * useful links: - * - * http://stackoverflow.com/questions/33296749/netty-connect-to-unix-domain-socket-failed - * http://netty.io/wiki/native-transports.html - * https://github.com/netty/netty/blob/master/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClient.java - * https://github.com/slandelle/netty-request-chunking/blob/master/src/test/java/slandelle/ChunkingTest.java - */ - - private Bootstrap bootstrap; - - private EventLoopGroup eventLoopGroup; - - private NettyInitializer nettyInitializer; - - private WebTarget baseResource; - - private Integer connectTimeout = null; - - private Integer readTimeout = null; - - @Override - public void init(DockerClientConfig dockerClientConfig) { - super.init(dockerClientConfig); - - bootstrap = new Bootstrap(); - - String scheme = dockerClientConfig.getDockerHost().getScheme(); - String host = ""; - - if ("unix".equals(scheme)) { - nettyInitializer = new UnixDomainSocketInitializer(); - host = "DUMMY"; - } else if ("tcp".equals(scheme)) { - nettyInitializer = new InetSocketInitializer(); - host = dockerClientConfig.getDockerHost().getHost() + ":" - + Integer.toString(dockerClientConfig.getDockerHost().getPort()); - } - - eventLoopGroup = nettyInitializer.init(bootstrap, dockerClientConfig); - - baseResource = new NettyWebTarget(this::connect, host) - .path(dockerClientConfig.getApiVersion().asWebPathPart()); - } - - private DuplexChannel connect() { - checkState(!eventLoopGroup.isShuttingDown(), "EventLoop is shutting down"); - - try { - return connect(bootstrap); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - - private DuplexChannel connect(final Bootstrap bootstrap) throws InterruptedException { - return nettyInitializer.connect(bootstrap); - } - - private interface NettyInitializer { - EventLoopGroup init(final Bootstrap bootstrap, DockerClientConfig dockerClientConfig); - - DuplexChannel connect(final Bootstrap bootstrap) throws InterruptedException; - } - - private ThreadFactory createThreadFactory() { - return new DefaultThreadFactory(THREAD_PREFIX, true, Thread.NORM_PRIORITY, DockerClientFactory.TESTCONTAINERS_THREAD_GROUP); - } - - private class UnixDomainSocketInitializer implements NettyInitializer { - @Override - public EventLoopGroup init(Bootstrap bootstrap, DockerClientConfig dockerClientConfig) { - if (SystemUtils.IS_OS_LINUX) { - return epollGroup(); - } else if (SystemUtils.IS_OS_MAC_OSX) { - return kqueueGroup(); - } - throw new RuntimeException("Unsupported OS"); - } - - public EventLoopGroup epollGroup() { - EventLoopGroup epollEventLoopGroup = new EpollEventLoopGroup(0, createThreadFactory()); - - ChannelFactory factory = () -> configure(new EpollDomainSocketChannel()); - - bootstrap.group(epollEventLoopGroup).channelFactory(factory).handler(new ChannelInitializer() { - @Override - protected void initChannel(final UnixChannel channel) throws Exception { - channel.pipeline().addLast(new HttpClientCodec()); - channel.pipeline().addLast(new HttpContentDecompressor()); - } - }); - return epollEventLoopGroup; - } - - public EventLoopGroup kqueueGroup() { - EventLoopGroup nioEventLoopGroup = new KQueueEventLoopGroup(0, createThreadFactory()); - - bootstrap.group(nioEventLoopGroup).channel(KQueueDomainSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(final KQueueDomainSocketChannel channel) throws Exception { - channel.pipeline().addLast(new HttpClientCodec()); - channel.pipeline().addLast(new HttpContentDecompressor()); - } - }); - - return nioEventLoopGroup; - } - - @Override - public DuplexChannel connect(Bootstrap bootstrap) throws InterruptedException { - String socketPath = getDockerClientConfig().getDockerHost().getPath(); - return (DuplexChannel) bootstrap.connect(new DomainSocketAddress(socketPath)).sync().channel(); - } - } - - private class InetSocketInitializer implements NettyInitializer { - @Override - public EventLoopGroup init(Bootstrap bootstrap, final DockerClientConfig dockerClientConfig) { - EventLoopGroup nioEventLoopGroup = new NioEventLoopGroup(0, createThreadFactory()); - - // TODO do we really need BouncyCastle? - Security.addProvider(new BouncyCastleProvider()); - - ChannelFactory factory = () -> configure(new NioSocketChannel()); - - bootstrap.group(nioEventLoopGroup).channelFactory(factory) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(final SocketChannel channel) throws Exception { - channel.pipeline().addLast(new HttpClientCodec()); - channel.pipeline().addLast(new HttpContentDecompressor()); - } - }); - - return nioEventLoopGroup; - } - - @Override - public DuplexChannel connect(Bootstrap bootstrap) throws InterruptedException { - DockerClientConfig dockerClientConfig = getDockerClientConfig(); - String host = dockerClientConfig.getDockerHost().getHost(); - int port = dockerClientConfig.getDockerHost().getPort(); - - if (port == -1) { - throw new RuntimeException("no port configured for " + host); - } - - DuplexChannel channel = (DuplexChannel) bootstrap.connect(host, port).sync().channel(); - - final SslHandler ssl = initSsl(dockerClientConfig); - - if (ssl != null) { - channel.pipeline().addFirst(ssl); - } - - return channel; - } - - private SslHandler initSsl(DockerClientConfig dockerClientConfig) { - SslHandler ssl = null; - - try { - String host = dockerClientConfig.getDockerHost().getHost(); - int port = dockerClientConfig.getDockerHost().getPort(); - - final SSLConfig sslConfig = dockerClientConfig.getSSLConfig(); - - if (sslConfig != null && sslConfig.getSSLContext() != null) { - - SSLEngine engine = sslConfig.getSSLContext().createSSLEngine(host, port); - engine.setUseClientMode(true); - engine.setSSLParameters(enableHostNameVerification(engine.getSSLParameters())); - - // in the future we may use HostnameVerifier like here: - // https://github.com/AsyncHttpClient/async-http-client/blob/1.8.x/src/main/java/com/ning/http/client/providers/netty/NettyConnectListener.java#L76 - - ssl = new SslHandler(engine); - } - - } catch (Exception e) { - throw new RuntimeException(e); - } - - return ssl; - } - } - - public SSLParameters enableHostNameVerification(SSLParameters sslParameters) { - sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); - return sslParameters; - } - - @Override - public void close() throws IOException { - checkNotNull(eventLoopGroup, "Factory not initialized. You probably forgot to call init()!"); - - eventLoopGroup.shutdownGracefully(); - } - - /** - * Configure connection timeout in milliseconds - */ - public TestcontainersDockerCmdExecFactory withConnectTimeout(Integer connectTimeout) { - this.connectTimeout = connectTimeout; - return this; - } - - /** - * Configure read timeout in milliseconds - */ - public TestcontainersDockerCmdExecFactory withReadTimeout(Integer readTimeout) { - this.readTimeout = readTimeout; - return this; - } - - private T configure(T channel) { - ChannelConfig channelConfig = channel.config(); - - if (connectTimeout != null) { - channelConfig.setConnectTimeoutMillis(connectTimeout); - } - if (readTimeout != null) { - channel.pipeline().addLast("readTimeoutHandler", new ReadTimeoutHandler()); - } - - return channel; - } - - private final class ReadTimeoutHandler extends IdleStateHandler { - private boolean alreadyTimedOut; - - ReadTimeoutHandler() { - super(readTimeout, 0, 0, TimeUnit.MILLISECONDS); - } - - /** - * Called when a read timeout was detected. - */ - @Override - protected synchronized void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception { - assert evt.state() == IdleState.READER_IDLE; - final Channel channel = ctx.channel(); - if (channel == null || !channel.isActive() || alreadyTimedOut) { - return; - } - DockerClientConfig dockerClientConfig = getDockerClientConfig(); - final Object dockerAPIEndpoint = dockerClientConfig.getDockerHost(); - final String msg = "Read timed out: No data received within " + readTimeout - + "ms. Perhaps the docker API (" + dockerAPIEndpoint - + ") is not responding normally, or perhaps you need to increase the readTimeout value."; - final Exception ex = new SocketTimeoutException(msg); - ctx.fireExceptionCaught(ex); - alreadyTimedOut = true; - } - } - - protected WebTarget getBaseResource() { - checkNotNull(baseResource, "Factory not initialized, baseResource not set. You probably forgot to call init()!"); - return baseResource; - } -} diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index 717ba2635a8..46550dc0af7 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -14,8 +14,6 @@ import com.google.common.annotations.VisibleForTesting; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import org.apache.http.client.utils.URLEncodedUtils; -import org.apache.http.message.BasicNameValuePair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testcontainers.DockerClientFactory; @@ -25,7 +23,9 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; +import java.io.UnsupportedEncodingException; import java.net.Socket; +import java.net.URLEncoder; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Collections; @@ -365,12 +365,15 @@ static class FilterRegistry { * @throws IOException if communication with Ryuk fails */ protected boolean register(List> filters) throws IOException { - String query = URLEncodedUtils.format( - filters.stream() - .map(it -> new BasicNameValuePair(it.getKey(), it.getValue())) - .collect(Collectors.toList()), - (String) null - ); + String query = filters.stream() + .map(it -> { + try { + return URLEncoder.encode(it.getKey(), "UTF-8") + "=" + URLEncoder.encode(it.getValue(), "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + }) + .collect(Collectors.joining("&")); log.debug("Sending '{}' to Ryuk", query); out.write(query.getBytes()); diff --git a/core/src/test/java/com/github/dockerjava/core/util/FiltersEncoderTest.java b/core/src/test/java/com/github/dockerjava/core/util/FiltersEncoderTest.java new file mode 100644 index 00000000000..353133ae148 --- /dev/null +++ b/core/src/test/java/com/github/dockerjava/core/util/FiltersEncoderTest.java @@ -0,0 +1,28 @@ +package com.github.dockerjava.core.util; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.model.Image; +import org.junit.Test; +import org.rnorth.visibleassertions.VisibleAssertions; +import org.testcontainers.DockerClientFactory; + +import java.util.List; +import java.util.UUID; + +/** + * This test checks that our custom monkey-patched version works + * and does not throw {@link ClassNotFoundException}. + */ +public class FiltersEncoderTest { + + @Test + public void filtersShouldWork() throws Exception { + DockerClient client = DockerClientFactory.instance().client(); + + List images = client.listImagesCmd() + .withLabelFilter("com.example=" + UUID.randomUUID().toString()) + .exec(); + + VisibleAssertions.assertTrue("List is empty", images.isEmpty()); + } +} diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 9ab71fe7c43..ba9a2db096e 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -8,6 +8,7 @@ dependencies { testCompile project(':postgresql') testCompile 'com.zaxxer:HikariCP:3.3.1' testCompile 'redis.clients:jedis:3.0.1' + testCompile 'org.apache.httpcomponents:httpclient:4.5.7' testRuntime 'org.postgresql:postgresql:42.2.5' testRuntime 'mysql:mysql-connector-java:8.0.15' diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index d891e179a94..1280fb4a818 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -11,7 +11,8 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') testCompile 'com.zaxxer:HikariCP:3.3.1' + testCompile 'org.apache.httpcomponents:httpclient:4.5.7' testRuntime 'org.postgresql:postgresql:42.2.5' testRuntime 'mysql:mysql-connector-java:8.0.15' -} \ No newline at end of file +} From 541dbf4dbb885af866bcb729d8a599c095b114cb Mon Sep 17 00:00:00 2001 From: Alexander Sparkowsky Date: Tue, 19 Mar 2019 14:07:59 +0100 Subject: [PATCH 0666/5615] Update BrowserWebDriverContainer to honor existing no_proxy setting (#929) Fixes #928 --- .../containers/BrowserWebDriverContainer.java | 8 +++- .../junit/BrowserWebDriverContainerTest.java | 42 +++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index 94374235cbb..b92a4877cd2 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -48,6 +48,8 @@ public class BrowserWebDriverContainer Date: Tue, 19 Mar 2019 22:08:07 +0100 Subject: [PATCH 0667/5615] Cassandra: Add support for creating Cluster objects without JMX reporting (#1199) --- .../containers/CassandraContainer.java | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java b/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java index dbb3316158f..a78d23e4b4b 100644 --- a/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java +++ b/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java @@ -32,6 +32,7 @@ public class CassandraContainer> extends G private String configLocation; private String initScriptPath; + private boolean enableJmxReporting; public CassandraContainer() { this(IMAGE + ":3.11.2"); @@ -41,6 +42,7 @@ public CassandraContainer(String dockerImageName) { super(dockerImageName); addExposedPort(CQL_PORT); setStartupAttempts(3); + this.enableJmxReporting = false; } @Override @@ -116,6 +118,14 @@ public SELF withInitScript(String initScriptPath) { return self(); } + /** + * Initialize Cassandra client with JMX reporting enabled or disabled + */ + public SELF withJmxReporting(boolean enableJmxReporting) { + this.enableJmxReporting = enableJmxReporting; + return self(); + } + /** * Get username * @@ -146,14 +156,21 @@ public String getPassword() { * Can be used to obtain connections to Cassandra in the container */ public Cluster getCluster() { - return getCluster(this); + return getCluster(this, enableJmxReporting); } - public static Cluster getCluster(ContainerState containerState) { - return Cluster.builder() + public static Cluster getCluster(ContainerState containerState, boolean enableJmxReporting) { + final Cluster.Builder builder = Cluster.builder() .addContactPoint(containerState.getContainerIpAddress()) - .withPort(containerState.getMappedPort(CQL_PORT)) - .build(); + .withPort(containerState.getMappedPort(CQL_PORT)); + if (!enableJmxReporting) { + builder.withoutJMXReporting(); + } + return builder.build(); + } + + public static Cluster getCluster(ContainerState containerState) { + return getCluster(containerState, false); } private DatabaseDelegate getDatabaseDelegate() { From f3ae35ef8379bdc248e71768956b73048216e456 Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 20 Mar 2019 22:00:47 +0000 Subject: [PATCH 0668/5615] Allow TC_INITSCRIPT to support absolute/relative path via file: URI (#1329) * TC_INITSCRIPT support absolute/relative path * TC_INITSCRIPT remove absolute path,relative project path use file: prefix * into a constant and use FILE_PATH_PREFIX * Simplify implementation by using URL directly * Update docs * Update modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java Co-Authored-By: rnorth --- docs/modules/databases/index.md | 8 +++++++- modules/jdbc-test/sql/init_mysql.sql | 5 +++++ .../java/org/testcontainers/jdbc/JDBCDriverTest.java | 1 + .../testcontainers/jdbc/ContainerDatabaseDriver.java | 11 +++++++++-- 4 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 modules/jdbc-test/sql/init_mysql.sql diff --git a/docs/modules/databases/index.md b/docs/modules/databases/index.md index 4fd498b7248..aaef47a44ef 100644 --- a/docs/modules/databases/index.md +++ b/docs/modules/databases/index.md @@ -64,7 +64,7 @@ Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database `jdbc:tc:postgis:9.6://hostname/databasename` -## Using an init script +## Using a classpath init script Testcontainers can run an initscript after the database container is started, but before your code is given a connection to it. The script must be on the classpath, and is referenced as follows: @@ -72,6 +72,12 @@ Testcontainers can run an initscript after the database container is started, bu This is useful if you have a fixed script for setting up database schema, etc. +## Using an init script from a file + +If the init script path is prefixed `file:`, it will be loaded from a file (relative to the working directory, which will usually be the project root). + +`jdbc:tc:mysql:5.7.22://hostname/databasename?TC_INITSCRIPT=file:src/main/resources/init_mysql.sql` + #### Using an init function Instead of running a fixed script for DB setup, it may be useful to call a Java function that you define. This is intended to allow you to trigger database schema migration tools. To do this, add TC_INITFUNCTION to the URL as follows, passing a full path to the class name and method: diff --git a/modules/jdbc-test/sql/init_mysql.sql b/modules/jdbc-test/sql/init_mysql.sql new file mode 100644 index 00000000000..2b00ee968b0 --- /dev/null +++ b/modules/jdbc-test/sql/init_mysql.sql @@ -0,0 +1,5 @@ +CREATE TABLE bar ( + foo VARCHAR(255) +); + +INSERT INTO bar (foo) VALUES ('hello world'); \ No newline at end of file diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java index a94f0b0897b..4eb55b38e24 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java @@ -46,6 +46,7 @@ public static Iterable data() { {"jdbc:tc:mysql://hostname/databasename?user=someuser&TC_INITSCRIPT=somepath/init_mysql.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, {"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, {"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=somepath/init_mysql.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, + {"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=file:sql/init_mysql.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, {"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&password=somepwd&TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, {"jdbc:tc:mysql:5.5.43://hostname/databasename?TC_INITSCRIPT=somepath/init_unicode_mysql.sql&useUnicode=yes&characterEncoding=utf8", EnumSet.of(Options.CharacterSet)}, {"jdbc:tc:mysql:5.5.43://hostname/databasename", EnumSet.noneOf(Options.class)}, diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java index 163c67f486d..0f470e36979 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java @@ -41,6 +41,7 @@ public class ContainerDatabaseDriver implements Driver { private static final Map> containerConnections = new HashMap<>(); private static final Map jdbcUrlContainerCache = new HashMap<>(); private static final Set initializedContainers = new HashSet<>(); + private static final String FILE_PATH_PREFIX = "file:"; static { load(); @@ -177,8 +178,14 @@ private void runInitScriptIfRequired(final ConnectionUrl connectionUrl, Database if (connectionUrl.getInitScriptPath().isPresent()) { String initScriptPath = connectionUrl.getInitScriptPath().get(); try { - URL resource = Thread.currentThread().getContextClassLoader().getResource(initScriptPath); - + URL resource; + if (initScriptPath.startsWith(FILE_PATH_PREFIX)) { + //relative workdir path + resource = new URL(initScriptPath); + } else { + //classpath resource + resource = Thread.currentThread().getContextClassLoader().getResource(initScriptPath); + } if (resource == null) { LOGGER.warn("Could not load classpath init script: {}", initScriptPath); throw new SQLException("Could not load classpath init script: " + initScriptPath + ". Resource not found."); From 2fc7c21772b57ee9afcf92a028a20c35baca2062 Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 20 Mar 2019 22:14:13 +0000 Subject: [PATCH 0669/5615] Update latest version number --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 0aad56f0314..aee5d80e340 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -76,4 +76,4 @@ nav: - contributing.md - contributing_docs.md extra: - latest_version: 1.10.6 + latest_version: 1.10.7 From 31b361084257e8c751bbeef3bded0ffe41fe7e00 Mon Sep 17 00:00:00 2001 From: Frederic Tardif Date: Thu, 21 Mar 2019 13:32:20 -0400 Subject: [PATCH 0670/5615] start ambassador only if mapping configured (#1328) --- .../org/testcontainers/containers/DockerComposeContainer.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index d2958129e90..ae10c3096fa 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -239,7 +239,9 @@ private List listChildContainers() { } private void startAmbassadorContainers() { - ambassadorContainer.start(); + if (!ambassadorPortMappings.isEmpty()) { + ambassadorContainer.start(); + } } private Logger logger() { From 41987efdc1c465c79b075e77ff79501c709ad5a7 Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 22 Mar 2019 20:25:30 +0000 Subject: [PATCH 0671/5615] Add Toxiproxy module (#1330) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on https://gist.github.com/rnorth/4c3666d62fa93bf0daa813b282e4ebff, in turn based on [hotels.com’s blog post](https://medium.com/hotels-com-technology/i-dont-know-about-resilience-testing-and-so-can-you-b3c59d80012d) where they show use of Testcontainers with Toxiproxy. Having seen Toxiproxy and experimented with it, I'm inclined to go with this and suggest that we not move forward with #283 (which is a great PR, but we've unfortunately failed to land after quite a long time). While Pumba may be nice and transparent, there are a lot of moving parts and complexity under the covers, vs Toxiproxy, which is a bit less magical but easier to understand. --- .travis.yml | 4 - docs/modules/toxiproxy.md | 76 ++++++++++ mkdocs.yml | 1 + modules/toxiproxy/build.gradle | 9 ++ .../containers/ToxiproxyContainer.java | 130 ++++++++++++++++++ .../containers/ToxiproxyTest.java | 102 ++++++++++++++ 6 files changed, 318 insertions(+), 4 deletions(-) create mode 100644 docs/modules/toxiproxy.md create mode 100644 modules/toxiproxy/build.gradle create mode 100644 modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java create mode 100644 modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java diff --git a/.travis.yml b/.travis.yml index 924b31b4f8d..571c078730d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,10 +2,6 @@ language: java jdk: - oraclejdk8 -branches: - only: - - master - sudo: required services: - docker diff --git a/docs/modules/toxiproxy.md b/docs/modules/toxiproxy.md new file mode 100644 index 00000000000..5df65e232ff --- /dev/null +++ b/docs/modules/toxiproxy.md @@ -0,0 +1,76 @@ +# Toxiproxy Module + +Testcontainers module for Shopify's [Toxiproxy](https://github.com/Shopify/toxiproxy). +This TCP proxy can be used to simulate network failure conditions in between tests and containers. + +[toxiproxy-java](https://github.com/trekawek/toxiproxy-java) is used as a client. + +## Usage example + +A Toxiproxy container can be placed in between test code and a container, or in between containers. +In either scenario, it is necessary to create a `ToxiproxyContainer` instance on the same Docker network, as follows: + + +[Creating a Toxiproxy container](../../modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java) inside_block:creatingProxy + + +Next, it is necessary to instruct Toxiproxy to start proxying connections. +Each `ToxiproxyContainer` can proxy to many target containers if necessary. + +We do this as follows: + + +[Starting proxying connections to a target container](../../modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java) inside_block:obtainProxyObject + + +Then, to establish a connection via Toxiproxy, we obtain **Toxiproxy's** proxy host IP and port: + + +[Obtaining proxied host and port](../../modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java) inside_block:obtainProxiedHostAndPort + + +Code under test, or other containers, should connect to this proxied host IP and port. + +Having done this, it is possible to trigger failure conditions ('Toxics') through the `proxy.toxics()` object: + +* `bandwidth` - Limit a connection to a maximum number of kilobytes per second. +* `latency` - Add a delay to all data going through the proxy. The delay is equal to `latency +/- jitter`. +* `slicer` - Slices TCP data up into small bits, optionally adding a delay between each sliced "packet". +* `slowClose` - Delay the TCP socket from closing until `delay` milliseconds has elapsed. +* `timeout` - Stops all data from getting through, and closes the connection after `timeout`. If `timeout` is `0`, the connection won't close, and data will be delayed until the toxic is removed. +* `limitData` - Closes connection when transmitted data exceeded limit. + +Please see the [Toxiproxy documentation](https://github.com/Shopify/toxiproxy#toxics) for full details on the available Toxics. + +As one example, we can introduce latency and random jitter to proxied connections as follows: + + +[Adding latency to a connection](../../modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java) inside_block:addingLatency + + +Additionally we can disable the proxy to simulate a complete interruption to the network connection: + + +[Cutting a connection](../../modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java) inside_block:disableProxy + + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:toxiproxy:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + toxiproxy + {{latest_version}} + test + +``` + +## Acknowledgements + +This module was inspired by a [hotels.com blog post](https://medium.com/hotels-com-technology/i-dont-know-about-resilience-testing-and-so-can-you-b3c59d80012d). diff --git a/mkdocs.yml b/mkdocs.yml index aee5d80e340..1b7d3f1852e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -56,6 +56,7 @@ nav: - modules/pulsar.md - modules/localstack.md - modules/mockserver.md + - modules/toxiproxy.md - modules/nginx.md - modules/vault.md - Test framework integration: diff --git a/modules/toxiproxy/build.gradle b/modules/toxiproxy/build.gradle new file mode 100644 index 00000000000..0d004fd32bf --- /dev/null +++ b/modules/toxiproxy/build.gradle @@ -0,0 +1,9 @@ +description = "Testcontainers :: Toxiproxy" + +dependencies { + compile project(':testcontainers') + compile 'eu.rekawek.toxiproxy:toxiproxy-java:2.1.3' + + testCompile 'redis.clients:jedis:3.0.1' + testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' +} diff --git a/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java b/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java new file mode 100644 index 00000000000..cce6a0dca22 --- /dev/null +++ b/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java @@ -0,0 +1,130 @@ +package org.testcontainers.containers; + +import com.github.dockerjava.api.command.InspectContainerResponse; +import eu.rekawek.toxiproxy.Proxy; +import eu.rekawek.toxiproxy.ToxiproxyClient; +import eu.rekawek.toxiproxy.model.ToxicDirection; +import eu.rekawek.toxiproxy.model.ToxicList; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Container for resiliency testing using Toxiproxy. + */ +public class ToxiproxyContainer extends GenericContainer { + + private static final String IMAGE_NAME = "shopify/toxiproxy:2.1.0"; + private static final int TOXIPROXY_CONTROL_PORT = 8474; + private static final int FIRST_PROXIED_PORT = 8666; + private static final int LAST_PROXIED_PORT = 8666 + 31; + + private ToxiproxyClient client; + private final Map proxies = new HashMap<>(); + private final AtomicInteger nextPort = new AtomicInteger(FIRST_PROXIED_PORT); + + public ToxiproxyContainer() { + this(IMAGE_NAME); + } + + public ToxiproxyContainer(String imageName) { + super(imageName); + addExposedPorts(TOXIPROXY_CONTROL_PORT); + setWaitStrategy(new HttpWaitStrategy().forPath("/version").forPort(TOXIPROXY_CONTROL_PORT)); + + // allow up to 32 ports to be proxied (arbitrary value). Here we make the ports exposed; whether or not + // Toxiproxy will listen is controlled at runtime using getProxy(...) + for (int i = FIRST_PROXIED_PORT; i <= LAST_PROXIED_PORT; i++) { + addExposedPort(i); + } + } + + @Override + protected void containerIsStarted(InspectContainerResponse containerInfo) { + client = new ToxiproxyClient(getContainerIpAddress(), getMappedPort(TOXIPROXY_CONTROL_PORT)); + } + + /** + * Obtain a {@link ContainerProxy} instance for target container that is managed by Testcontainers. The target + * container should be routable from this from this {@link ToxiproxyContainer} instance (e.g. on the same + * Docker {@link Network}). + * + * @param container target container + * @param port port number on the target service that should be proxied + * @return a {@link ContainerProxy} instance + */ + public ContainerProxy getProxy(GenericContainer container, int port) { + return this.getProxy(container.getNetworkAliases().get(0), port); + } + + /** + * Obtain a {@link ContainerProxy} instance for a specific hostname and port, which can be for any host + * that is routable from this {@link ToxiproxyContainer} instance (e.g. on the same + * Docker {@link Network} or on routable from the Docker host). + * + *

It is expected that {@link ToxiproxyContainer#getProxy(GenericContainer, int)} will be more + * useful in most scenarios, but this method is present to allow use of Toxiproxy in front of containers + * or external servers that are not managed by Testcontainers.

+ * + * @param hostname hostname of target server to be proxied + * @param port port number on the target server that should be proxied + * @return a {@link ContainerProxy} instance + */ + public ContainerProxy getProxy(String hostname, int port) { + String upstream = hostname + ":" + port; + + return proxies.computeIfAbsent(upstream, __ -> { + try { + final int toxiPort = nextPort.getAndIncrement(); + if (toxiPort > LAST_PROXIED_PORT) { + throw new IllegalStateException("Maximum number of proxies exceeded"); + } + + final Proxy proxy = client.createProxy("name", "0.0.0.0:" + toxiPort, upstream); + return new ContainerProxy(proxy, getContainerIpAddress(), getMappedPort(toxiPort)); + } catch (IOException e) { + throw new RuntimeException("Proxy could not be created", e); + } + }); + } + + @RequiredArgsConstructor(access = AccessLevel.PROTECTED) + public static class ContainerProxy { + private static final String CUT_CONNECTION_DOWNSTREAM = "CUT_CONNECTION_DOWNSTREAM"; + private static final String CUT_CONNECTION_UPSTREAM = "CUT_CONNECTION_UPSTREAM"; + private final Proxy toxi; + @Getter private final String containerIpAddress; + @Getter private final int proxyPort; + private boolean isCurrentlyCut; + + public ToxicList toxics() { + return toxi.toxics(); + } + + /** + * Cuts the connection by setting bandwidth in both directions to zero. + * @param shouldCutConnection true if the connection should be cut, or false if it should be re-enabled + */ + public void setConnectionCut(boolean shouldCutConnection) { + try { + if (shouldCutConnection) { + toxics().bandwidth(CUT_CONNECTION_DOWNSTREAM, ToxicDirection.DOWNSTREAM, 0); + toxics().bandwidth(CUT_CONNECTION_UPSTREAM, ToxicDirection.UPSTREAM, 0); + isCurrentlyCut = true; + } else if (isCurrentlyCut) { + toxics().get(CUT_CONNECTION_DOWNSTREAM).remove(); + toxics().get(CUT_CONNECTION_UPSTREAM).remove(); + isCurrentlyCut = false; + } + } catch (IOException e) { + throw new RuntimeException("Could not control proxy", e); + } + } + } +} diff --git a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java new file mode 100644 index 00000000000..fbcfa124f80 --- /dev/null +++ b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java @@ -0,0 +1,102 @@ +package org.testcontainers.containers; + +import eu.rekawek.toxiproxy.model.ToxicDirection; +import org.junit.Rule; +import org.junit.Test; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.exceptions.JedisConnectionException; + +import java.io.IOException; + +import static java.lang.String.format; +import static org.rnorth.visibleassertions.VisibleAssertions.*; + +public class ToxiproxyTest { + + // creatingProxy { + // Create a common docker network so that containers can communicate + @Rule + public Network network = Network.newNetwork(); + + // the target container - this could be anything + @Rule + public GenericContainer redis = new GenericContainer("redis:5.0.4") + .withExposedPorts(6379) + .withNetwork(network); + + // Toxiproxy container, which will be used as a TCP proxy + @Rule + public ToxiproxyContainer toxiproxy = new ToxiproxyContainer() + .withNetwork(network); + // } + + @Test + public void testDirect() { + final Jedis jedis = new Jedis(redis.getContainerIpAddress(), redis.getFirstMappedPort()); + jedis.set("somekey", "somevalue"); + + final String s = jedis.get("somekey"); + assertEquals("direct access to the container works OK", "somevalue", s); + } + + @Test + public void testLatencyViaProxy() throws IOException { + // obtainProxyObject { + final ToxiproxyContainer.ContainerProxy proxy = toxiproxy.getProxy(redis, 6379); + // } + + // obtainProxiedHostAndPort { + final String ipAddressViaToxiproxy = proxy.getContainerIpAddress(); + final int portViaToxiproxy = proxy.getProxyPort(); + // } + + final Jedis jedis = new Jedis(ipAddressViaToxiproxy, portViaToxiproxy); + jedis.set("somekey", "somevalue"); + + checkCallWithLatency(jedis, "without interference", 0, 250); + + // addingLatency { + proxy.toxics() + .latency("latency", ToxicDirection.DOWNSTREAM, 1_100) + .setJitter(100); + // from now on the connection latency should be from 1000-1200 ms. + // } + + checkCallWithLatency(jedis, "with interference", 1_000, 1_500); + } + + @Test + public void testConnectionCut() { + final ToxiproxyContainer.ContainerProxy proxy = toxiproxy.getProxy(redis, 6379); + final Jedis jedis = new Jedis(proxy.getContainerIpAddress(), proxy.getProxyPort()); + jedis.set("somekey", "somevalue"); + + assertEquals("access to the container works OK before cutting the connection", "somevalue", jedis.get("somekey")); + + // disableProxy { + proxy.setConnectionCut(true); + + // for example, expect failure when the connection is cut + assertThrows("calls fail when the connection is cut", + JedisConnectionException.class, () -> { + jedis.get("somekey"); + }); + + proxy.setConnectionCut(false); + + // and with the connection re-established, expect success + assertEquals("access to the container works OK after re-establishing the connection", "somevalue", jedis.get("somekey")); + // } + } + + private void checkCallWithLatency(Jedis jedis, final String description, int expectedMinLatency, long expectedMaxLatency) { + final long start = System.currentTimeMillis(); + String s = jedis.get("somekey"); + final long end = System.currentTimeMillis(); + final long duration = end - start; + + assertEquals(format("access to the container %s works OK", description), "somevalue", s); + assertTrue(format("%s there is at least %dms latency", description, expectedMinLatency), duration >= expectedMinLatency); + assertTrue(format("%s there is no more than %dms latency", description, expectedMaxLatency), duration < expectedMaxLatency); + } +} From cb27c41d535f2550bb94cba98b83495fee67f948 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 23 Mar 2019 08:44:31 +0000 Subject: [PATCH 0672/5615] Update latest version number --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 1b7d3f1852e..31938bb99f7 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -77,4 +77,4 @@ nav: - contributing.md - contributing_docs.md extra: - latest_version: 1.10.7 + latest_version: 1.11.0 From bb5c6fc2fe926228b1dae33f7e2457f9b5c9a753 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 23 Mar 2019 09:12:19 +0000 Subject: [PATCH 0673/5615] Add extra background to toxiproxy module docs --- docs/modules/toxiproxy.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/docs/modules/toxiproxy.md b/docs/modules/toxiproxy.md index 5df65e232ff..9e09d8b387b 100644 --- a/docs/modules/toxiproxy.md +++ b/docs/modules/toxiproxy.md @@ -1,10 +1,16 @@ # Toxiproxy Module Testcontainers module for Shopify's [Toxiproxy](https://github.com/Shopify/toxiproxy). -This TCP proxy can be used to simulate network failure conditions in between tests and containers. - -[toxiproxy-java](https://github.com/trekawek/toxiproxy-java) is used as a client. +This TCP proxy can be used to simulate network failure conditions. + +You can simulate network failures: + +* between Java code and containers, ideal for testing resilience features of client code +* between containers, for testing resilience and emergent behaviour of multi-container systems +* if desired, between Java code/containers and external resources (non-Dockerized!), for scenarios where not all dependencies can be/have been dockerized +Testcontainers Toxiproxy support allows resilience features to be easily verified as part of isolated dev/CI testing. This allows earlier testing of resilience features, and broader sets of failure conditions to be covered. + ## Usage example A Toxiproxy container can be placed in between test code and a container, or in between containers. @@ -74,3 +80,6 @@ testCompile "org.testcontainers:toxiproxy:{{latest_version}}" ## Acknowledgements This module was inspired by a [hotels.com blog post](https://medium.com/hotels-com-technology/i-dont-know-about-resilience-testing-and-so-can-you-b3c59d80012d). + +[toxiproxy-java](https://github.com/trekawek/toxiproxy-java) is used to help control failure conditions. + From f2fc02c49afc4c183bd22a09f340c0a23fb3cf03 Mon Sep 17 00:00:00 2001 From: worldtiki Date: Sun, 24 Mar 2019 08:19:53 +0000 Subject: [PATCH 0674/5615] Toxiproxy: Fix multi-proxy use case (remove static "name" argument) (#1335) * Use hostname instead of hardcoded string to allow for multiple proxies to be created * Update modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java Co-Authored-By: worldtiki * Add a test covering creation of multiple proxies --- .../containers/ToxiproxyContainer.java | 2 +- .../containers/ToxiproxyTest.java | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java b/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java index cce6a0dca22..df4bf6e688d 100644 --- a/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java +++ b/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java @@ -86,7 +86,7 @@ public ContainerProxy getProxy(String hostname, int port) { throw new IllegalStateException("Maximum number of proxies exceeded"); } - final Proxy proxy = client.createProxy("name", "0.0.0.0:" + toxiPort, upstream); + final Proxy proxy = client.createProxy(upstream, "0.0.0.0:" + toxiPort, upstream); return new ContainerProxy(proxy, getContainerIpAddress(), getMappedPort(toxiPort)); } catch (IOException e) { throw new RuntimeException("Proxy could not be created", e); diff --git a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java index fbcfa124f80..34861f68f7a 100644 --- a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java +++ b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java @@ -89,6 +89,34 @@ public void testConnectionCut() { // } } + @Test + public void testMultipleProxiesCanBeCreated() { + try (GenericContainer secondRedis = new GenericContainer("redis:5.0.4") + .withExposedPorts(6379) + .withNetwork(network)) { + + secondRedis.start(); + + final ToxiproxyContainer.ContainerProxy firstProxy = toxiproxy.getProxy(redis, 6379); + final ToxiproxyContainer.ContainerProxy secondProxy = toxiproxy.getProxy(secondRedis, 6379); + + final Jedis firstJedis = new Jedis(firstProxy.getContainerIpAddress(), firstProxy.getProxyPort()); + final Jedis secondJedis = new Jedis(secondProxy.getContainerIpAddress(), secondProxy.getProxyPort()); + + firstJedis.set("somekey", "somevalue"); + secondJedis.set("somekey", "somevalue"); + + firstProxy.setConnectionCut(true); + + assertThrows("calls fail when the connection is cut, for only the relevant proxy", + JedisConnectionException.class, () -> { + firstJedis.get("somekey"); + }); + + assertEquals("access via a different proxy is OK", "somevalue", secondJedis.get("somekey")); + } + } + private void checkCallWithLatency(Jedis jedis, final String description, int expectedMinLatency, long expectedMaxLatency) { final long start = System.currentTimeMillis(); String s = jedis.get("somekey"); From 672523057bd2f2a7ad716e9648dde7a526dd64ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sun, 24 Mar 2019 09:21:28 +0100 Subject: [PATCH 0675/5615] Bump spock-core from 1.0-groovy-2.4 to 1.3-groovy-2.5 in /modules/spock (#1291) Bumps spock-core from 1.0-groovy-2.4 to 1.3-groovy-2.5. Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 1280fb4a818..2e8b175e303 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -6,7 +6,7 @@ description = "Testcontainers :: Spock-Extension" dependencies { compile project(':testcontainers') - compile 'org.spockframework:spock-core:1.0-groovy-2.4' + compile 'org.spockframework:spock-core:1.3-groovy-2.5' testCompile project(':mysql') testCompile project(':postgresql') From 2c5f274d73317257efa99d359357856c076c24b7 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 24 Mar 2019 21:16:19 +0000 Subject: [PATCH 0676/5615] Add getLogs() to GenericContainer, allowing all logs to be retrieved (#1206) Fixes #1205 --- .travis.yml | 2 +- .../containers/ContainerState.java | 18 +++++ .../containers/GenericContainer.java | 29 +++---- .../containers/output/WaitingConsumer.java | 9 ++- .../org/testcontainers/utility/LogUtils.java | 81 ++++++++++++++++--- .../containers/output/ContainerLogsTest.java | 72 +++++++++++++++++ .../junit/WorkingDirectoryTest.java | 14 +--- .../utility/DirectoryTarResourceTest.java | 43 +++------- docs/features/container_logs.md | 34 ++++++-- 9 files changed, 216 insertions(+), 86 deletions(-) create mode 100644 core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java diff --git a/.travis.yml b/.travis.yml index 571c078730d..575aa50b208 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,7 +22,7 @@ jobs: - stage: test env: [ NAME=core ] - jdk: openjdk-ea + jdk: openjdk12 script: ./gradlew testcontainers:check --scan --no-daemon - env: [ NAME=selenium ] diff --git a/core/src/main/java/org/testcontainers/containers/ContainerState.java b/core/src/main/java/org/testcontainers/containers/ContainerState.java index 26eec8c4ebe..2066fe72157 100644 --- a/core/src/main/java/org/testcontainers/containers/ContainerState.java +++ b/core/src/main/java/org/testcontainers/containers/ContainerState.java @@ -7,6 +7,8 @@ import com.github.dockerjava.api.model.Ports; import com.google.common.base.Preconditions; import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.output.OutputFrame; +import org.testcontainers.utility.LogUtils; import java.util.ArrayList; import java.util.List; @@ -149,6 +151,22 @@ default List getBoundPortNumbers() { .collect(Collectors.toList()); } + + /** + * @return all log output from the container from start until the current instant (both stdout and stderr) + */ + default String getLogs() { + return LogUtils.getOutput(DockerClientFactory.instance().client(), getContainerId()); + } + + /** + * @param types log types to return + * @return all log output from the container from start until the current instant + */ + default String getLogs(OutputFrame.OutputType... types) { + return LogUtils.getOutput(DockerClientFactory.instance().client(), getContainerId(), types); + } + /** * @return the id of the container */ diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index b00061b3d97..936d880d1a8 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -20,9 +20,7 @@ import org.rnorth.visibleassertions.VisibleAssertions; import org.slf4j.Logger; import org.testcontainers.DockerClientFactory; -import org.testcontainers.containers.output.FrameConsumerResultCallback; import org.testcontainers.containers.output.OutputFrame; -import org.testcontainers.containers.output.Slf4jLogConsumer; import org.testcontainers.containers.startupcheck.IsRunningStartupCheckStrategy; import org.testcontainers.containers.startupcheck.MinimumDurationRunningStartupCheckStrategy; import org.testcontainers.containers.startupcheck.StartupCheckStrategy; @@ -50,8 +48,6 @@ import java.util.stream.Stream; import static com.google.common.collect.Lists.newArrayList; -import static org.testcontainers.containers.output.OutputFrame.OutputType.STDERR; -import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; import static org.testcontainers.utility.CommandLine.runShellCommand; /** @@ -242,24 +238,24 @@ private void tryStart() { logger().info("Starting container with ID: {}", containerId); dockerClient.startContainerCmd(containerId).exec(); + logger().info("Container {} is starting: {}", dockerImageName, containerId); + // For all registered output consumers, start following as close to container startup as possible this.logConsumers.forEach(this::followOutput); - logger().info("Container {} is starting: {}", dockerImageName, containerId); - // Tell subclasses that we're starting containerInfo = dockerClient.inspectContainerCmd(containerId).exec(); containerName = containerInfo.getName(); containerIsStarting(containerInfo); - // Wait until the container is running (may not be fully started) - + // Wait until the container has reached the desired running state if (!this.startupCheckStrategy.waitUntilStartupSuccessful(dockerClient, containerId)) { // Bail out, don't wait for the port to start listening. // (Exception thrown here will be caught below and wrapped) throw new IllegalStateException("Container did not start correctly."); } + // Wait until the process within the container has become ready for use (e.g. listening on network, log message emitted, etc). waitUntilContainerStarted(); logger().info("Container {} started", dockerImageName); @@ -269,17 +265,12 @@ private void tryStart() { if (containerId != null) { // Log output if startup failed, either due to a container failure or exception (including timeout) - logger().error("Container log output (if any) will follow:"); - FrameConsumerResultCallback resultCallback = new FrameConsumerResultCallback(); - resultCallback.addConsumer(STDOUT, new Slf4jLogConsumer(logger())); - resultCallback.addConsumer(STDERR, new Slf4jLogConsumer(logger())); - dockerClient.logContainerCmd(containerId).withStdOut(true).withStdErr(true).exec(resultCallback); - - // Try to ensure that container log output is shown before proceeding - try { - resultCallback.getCompletionLatch().await(1, TimeUnit.MINUTES); - } catch (InterruptedException ignored) { - // Cannot do anything at this point + final String containerLogs = getLogs(); + + if (containerLogs.length() > 0) { + logger().error("Log output from the failed container:\n{}", getLogs()); + } else { + logger().error("There are no stdout/stderr logs available for the failed container"); } } diff --git a/core/src/main/java/org/testcontainers/containers/output/WaitingConsumer.java b/core/src/main/java/org/testcontainers/containers/output/WaitingConsumer.java index 367581528c2..858f5a4b487 100644 --- a/core/src/main/java/org/testcontainers/containers/output/WaitingConsumer.java +++ b/core/src/main/java/org/testcontainers/containers/output/WaitingConsumer.java @@ -113,8 +113,13 @@ private void waitUntil(Predicate predicate, long expiry, int times) /** * Wait until Docker closes the stream of output. */ - public void waitUntilEnd() throws TimeoutException { - waitUntilEnd(Long.MAX_VALUE); + public void waitUntilEnd() { + try { + waitUntilEnd(Long.MAX_VALUE); + } catch (TimeoutException e) { + // timeout condition can never occur in a realistic timeframe + throw new IllegalStateException(e); + } } /** diff --git a/core/src/main/java/org/testcontainers/utility/LogUtils.java b/core/src/main/java/org/testcontainers/utility/LogUtils.java index 100a79a8395..b51325cdda7 100644 --- a/core/src/main/java/org/testcontainers/utility/LogUtils.java +++ b/core/src/main/java/org/testcontainers/utility/LogUtils.java @@ -2,15 +2,14 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.LogContainerCmd; -import com.github.dockerjava.api.model.AuthConfig; -import com.google.common.base.MoreObjects; import lombok.experimental.UtilityClass; import org.testcontainers.containers.output.FrameConsumerResultCallback; import org.testcontainers.containers.output.OutputFrame; +import org.testcontainers.containers.output.ToStringConsumer; +import org.testcontainers.containers.output.WaitingConsumer; import java.util.function.Consumer; -import static com.google.common.base.Strings.isNullOrEmpty; import static org.testcontainers.containers.output.OutputFrame.OutputType.STDERR; import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; @@ -19,15 +18,76 @@ */ @UtilityClass public class LogUtils { + + /** + * Attach a log consumer to a container's log outputs in follow mode. The consumer will receive all previous + * and all future log frames of the specified type(s). + * + * @param dockerClient a Docker client + * @param containerId container ID to attach to + * @param consumer a consumer of {@link OutputFrame}s + * @param types types of {@link OutputFrame} to receive + */ + public void followOutput(DockerClient dockerClient, + String containerId, + Consumer consumer, + OutputFrame.OutputType... types) { + + attachConsumer(dockerClient, containerId, consumer, true, types); + } + + /** + * Attach a log consumer to a container's log outputs in follow mode. The consumer will receive all previous + * and all future log frames (both stdout and stderr). + * + * @param dockerClient a Docker client + * @param containerId container ID to attach to + * @param consumer a consumer of {@link OutputFrame}s + */ + public void followOutput(DockerClient dockerClient, + String containerId, + Consumer consumer) { + + followOutput(dockerClient, containerId, consumer, STDOUT, STDERR); + } + /** - * {@inheritDoc} + * Retrieve all previous log outputs for a container of the specified type(s). + * + * @param dockerClient a Docker client + * @param containerId container ID to attach to + * @param types types of {@link OutputFrame} to receive + * @return all previous output frames (stdout/stderr being separated by newline characters) */ - public void followOutput(DockerClient dockerClient, String containerId, - Consumer consumer, OutputFrame.OutputType... types) { + public String getOutput(DockerClient dockerClient, + String containerId, + OutputFrame.OutputType... types) { + + if (containerId == null) { + return ""; + } + + if (types.length == 0) { + types = new OutputFrame.OutputType[] { STDOUT, STDERR }; + } + + final ToStringConsumer consumer = new ToStringConsumer(); + final WaitingConsumer wait = new WaitingConsumer(); + attachConsumer(dockerClient, containerId, consumer.andThen(wait), false, types); + + wait.waitUntilEnd(); + return consumer.toUtf8String(); + } + + private static void attachConsumer(DockerClient dockerClient, + String containerId, + Consumer consumer, + boolean followStream, + OutputFrame.OutputType... types) { final LogContainerCmd cmd = dockerClient.logContainerCmd(containerId) - .withFollowStream(true) - .withSince(0); + .withFollowStream(followStream) + .withSince(0); final FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); for (OutputFrame.OutputType type : types) { @@ -38,9 +98,4 @@ public void followOutput(DockerClient dockerClient, String containerId, cmd.exec(callback); } - - public void followOutput(DockerClient dockerClient, String containerId, Consumer consumer) { - followOutput(dockerClient, containerId, consumer, STDOUT, STDERR); - } - } diff --git a/core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java b/core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java new file mode 100644 index 00000000000..f95d5a06850 --- /dev/null +++ b/core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java @@ -0,0 +1,72 @@ +package org.testcontainers.containers.output; + +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.testcontainers.containers.output.OutputFrame.OutputType.STDERR; +import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; + +public class ContainerLogsTest { + + @Test + public void getLogsReturnsAllLogsToDate() { + try (GenericContainer container = shortLivedContainer()) { + container.start(); + + // docsGetAllLogs { + final String logs = container.getLogs(); + // } + assertEquals("stdout and stderr are reflected in the returned logs", "stdout\nstderr", logs); + } + } + + @Test + public void getLogsReturnsStdOutToDate() { + try (GenericContainer container = shortLivedContainer()) { + container.start(); + + // docsGetStdOut { + final String logs = container.getLogs(STDOUT); + // } + assertEquals("stdout and stderr are reflected in the returned logs", "stdout", logs); + } + } + + @Test + public void getLogsReturnsStdErrToDate() { + try (GenericContainer container = shortLivedContainer()) { + container.start(); + + // docsGetStdErr { + final String logs = container.getLogs(STDERR); + // } + assertEquals("stdout and stderr are reflected in the returned logs", "stderr", logs); + } + } + + @Test + public void getLogsForLongRunningContainer() throws InterruptedException { + try (GenericContainer container = longRunningContainer()) { + container.start(); + + Thread.sleep(1000L); + + final String logs = container.getLogs(STDOUT); + assertTrue("stdout is reflected in the returned logs for a running container", logs.contains("seq=0")); + } + } + + private static GenericContainer shortLivedContainer() { + return new GenericContainer("alpine:3.3") + .withCommand("/bin/sh", "-c", "echo -n 'stdout' && echo -n 'stderr' 1>&2") + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()); + } + + private static GenericContainer longRunningContainer() { + return new GenericContainer("alpine:3.3") + .withCommand("ping -c 100 127.0.0.1"); + } +} diff --git a/core/src/test/java/org/testcontainers/junit/WorkingDirectoryTest.java b/core/src/test/java/org/testcontainers/junit/WorkingDirectoryTest.java index 1118302f701..30230cc12ff 100644 --- a/core/src/test/java/org/testcontainers/junit/WorkingDirectoryTest.java +++ b/core/src/test/java/org/testcontainers/junit/WorkingDirectoryTest.java @@ -3,13 +3,8 @@ import org.junit.ClassRule; import org.junit.Test; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.output.OutputFrame; -import org.testcontainers.containers.output.ToStringConsumer; -import org.testcontainers.containers.output.WaitingConsumer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; -import java.util.function.Consumer; - import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; /** @@ -17,20 +12,15 @@ */ public class WorkingDirectoryTest { - private static WaitingConsumer waitingConsumer = new WaitingConsumer(); - private static ToStringConsumer toStringConsumer = new ToStringConsumer(); - private static Consumer compositeConsumer = waitingConsumer.andThen(toStringConsumer); - @ClassRule public static GenericContainer container = new GenericContainer("alpine:3.2") .withWorkingDirectory("/etc") .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) - .withCommand("ls", "-al") - .withLogConsumer(compositeConsumer); + .withCommand("ls", "-al"); @Test public void checkOutput() { - String listing = toStringConsumer.toUtf8String(); + String listing = container.getLogs(); assertTrue("Directory listing contains expected /etc content", listing.contains("hostname")); assertTrue("Directory listing contains expected /etc content", listing.contains("init.d")); diff --git a/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java b/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java index b73bfeed46e..3789a654c20 100644 --- a/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java +++ b/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java @@ -5,15 +5,11 @@ import org.hamcrest.core.IsCollectionContaining; import org.junit.Test; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.output.ToStringConsumer; -import org.testcontainers.containers.output.WaitingConsumer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import org.testcontainers.images.builder.ImageFromDockerfile; import java.io.File; import java.util.Arrays; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.CoreMatchers.containsString; @@ -23,12 +19,7 @@ public class DirectoryTarResourceTest { @Test - public void simpleRecursiveFileTest() throws TimeoutException { - - WaitingConsumer wait = new WaitingConsumer(); - - final ToStringConsumer toString = new ToStringConsumer(); - + public void simpleRecursiveFileTest() { // 'src' is expected to be the project base directory, so all source code/resources should be copied in File directory = new File("src"); @@ -40,24 +31,17 @@ public void simpleRecursiveFileTest() throws TimeoutException { .cmd("cat /foo/test/resources/test-recursive-file.txt") .build() ).withFileFromFile("/tmp/foo", directory)) - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) - .withLogConsumer(wait.andThen(toString)); + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()); container.start(); - wait.waitUntilEnd(60, TimeUnit.SECONDS); - final String results = toString.toUtf8String(); + final String results = container.getLogs(); assertTrue("The container has a file that was copied in via a recursive copy", results.contains("Used for DirectoryTarResourceTest")); } @Test - public void simpleRecursiveFileWithPermissionTest() throws TimeoutException { - - WaitingConsumer wait = new WaitingConsumer(); - - final ToStringConsumer toString = new ToStringConsumer(); - + public void simpleRecursiveFileWithPermissionTest() { GenericContainer container = new GenericContainer( new ImageFromDockerfile() .withDockerfileFromBuilder(builder -> @@ -67,13 +51,10 @@ public void simpleRecursiveFileWithPermissionTest() throws TimeoutException { .build() ).withFileFromFile("/tmp/foo", new File("/mappable-resource/test-resource.txt"), 0754)) - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) - .withLogConsumer(wait.andThen(toString)); + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()); container.start(); - wait.waitUntilEnd(60, TimeUnit.SECONDS); - - String listing = toString.toUtf8String(); + String listing = container.getLogs(); assertThat("Listing shows that file is copied with mode requested.", Arrays.asList(listing.split("\\n")), @@ -81,13 +62,9 @@ public void simpleRecursiveFileWithPermissionTest() throws TimeoutException { } @Test - public void simpleRecursiveClasspathResourceTest() throws TimeoutException { + public void simpleRecursiveClasspathResourceTest() { // This test combines the copying of classpath resources from JAR files with the recursive TAR approach, to allow JARed classpath resources to be copied in to an image - WaitingConsumer wait = new WaitingConsumer(); - - final ToStringConsumer toString = new ToStringConsumer(); - GenericContainer container = new GenericContainer( new ImageFromDockerfile() .withDockerfileFromBuilder(builder -> @@ -96,13 +73,11 @@ public void simpleRecursiveClasspathResourceTest() throws TimeoutException { .cmd("ls -lRt /foo") .build() ).withFileFromClasspath("/tmp/foo", "/recursive/dir")) // here we use /org/junit as a directory that really should exist on the classpath - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) - .withLogConsumer(wait.andThen(toString)); + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()); container.start(); - wait.waitUntilEnd(60, TimeUnit.SECONDS); - final String results = toString.toUtf8String(); + final String results = container.getLogs(); // ExternalResource.class is known to exist in a subdirectory of /org/junit so should be successfully copied in assertTrue("The container has a file that was copied in via a recursive copy from a JAR resource", results.contains("content.txt")); diff --git a/docs/features/container_logs.md b/docs/features/container_logs.md index 389dc57c631..a9e2832eb70 100644 --- a/docs/features/container_logs.md +++ b/docs/features/container_logs.md @@ -1,13 +1,34 @@ # Accessing container logs -It is possible to capture container output using the `followOutput()` method. This method accepts a Consumer and (optionally) +It is possible to capture container output using: + + * the `getLogs()` method, which simply returns a `String` snapshot of a container's entire log output + * the `followOutput()` method. This method accepts a Consumer and (optionally) a varargs list stating which of STDOUT, STDERR, or both, should be followed. If not specified, both will be followed. At present, container output will always begin from the time of container creation. -Testcontainers includes some out-of-the-box Consumer implementations that can be used; examples follow. +## Reading all logs (from startup time to present) -## Streaming container output to an SLF4J logger +`getLogs()` is the simplest mechanism for accessing container logs, and can be used as follows: + + +[Accessing all output (stdout and stderr)](../../core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java) inside_block:docsGetAllLogs + + + +[Accessing just stdout](../../core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java) inside_block:docsGetStdOut + + + +[Accessing just stderr](../../core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java) inside_block:docsGetStdErr + + +## Streaming logs + +Testcontainers includes some out-of-the-box Consumer implementations that can be used with the streaming `followOutput()` model; examples follow. + +### Streaming container output to an SLF4J logger Given an existing SLF4J logger instance named LOGGER: ```java @@ -15,7 +36,10 @@ Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(LOGGER); container.followOutput(logConsumer); ``` -## Capturing container output as a String +### Capturing container output as a String + +To stream logs live or customize the decoding, `ToStringConsumer` may be used: + ```java ToStringConsumer toStringConsumer = new ToStringConsumer(); container.followOutput(toStringConsumer, OutputType.STDOUT); @@ -26,7 +50,7 @@ String utf8String = toStringConsumer.toUtf8String(); String otherString = toStringConsumer.toString(CharSet.forName("ISO-8859-1")); ``` -## Waiting for container output to contain expected content +### Waiting for container output to contain expected content `WaitingConsumer` will block until a frame of container output (usually a line) matches a provided predicate. From 1021e0e5c232b0f1a3392cffacf074d9ccfdeb36 Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 25 Mar 2019 15:23:46 +0000 Subject: [PATCH 0677/5615] Update latest version number --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 31938bb99f7..fc37c0be80a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -77,4 +77,4 @@ nav: - contributing.md - contributing_docs.md extra: - latest_version: 1.11.0 + latest_version: 1.11.1 From d6e0e8c7b46a7a88d1b399db3e0109ce85172e0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Thu, 28 Mar 2019 17:37:55 +0000 Subject: [PATCH 0678/5615] Bump transport from 6.6.2 to 6.7.0 in /modules/elasticsearch (#1344) Bumps [transport](https://github.com/elastic/elasticsearch) from 6.6.2 to 6.7.0.
Commits - [`8453f77`](https://github.com/elastic/elasticsearch/commit/8453f7701de25c467ce08088ddddac185cb8028c) SQL: Preserve original source for cast/convert function ([#40271](https://github-redirect.dependabot.com/elastic/elasticsearch/issues/40271)) - [`8b9fd09`](https://github.com/elastic/elasticsearch/commit/8b9fd0937d1e1a4ef95dc0db6b111ff80955104a) Reduce retention lease sync intervals ([#40302](https://github-redirect.dependabot.com/elastic/elasticsearch/issues/40302)) - [`c1999c7`](https://github.com/elastic/elasticsearch/commit/c1999c7b604793bc808b3685f11610def576a154) SQL: fix LIKE function equality by considering its pattern as well ([#40260](https://github-redirect.dependabot.com/elastic/elasticsearch/issues/40260)) - [`3522b00`](https://github.com/elastic/elasticsearch/commit/3522b00ae6c46cb3154fb660d998ee4ddd7ceb4c) Moving tests in locale-aware test file ([#40254](https://github-redirect.dependabot.com/elastic/elasticsearch/issues/40254)) - [`145e10b`](https://github.com/elastic/elasticsearch/commit/145e10b52e45818a84b7afaa6c627faba9e82769) Fix so non super users can also create API keys ([#40028](https://github-redirect.dependabot.com/elastic/elasticsearch/issues/40028)) ([#40288](https://github-redirect.dependabot.com/elastic/elasticsearch/issues/40288)) - [`e9ab087`](https://github.com/elastic/elasticsearch/commit/e9ab0879077df9f07c783005a4da0b37cd8a1d90) SQL: Fix issue with getting DATE type in JDBC ([#40207](https://github-redirect.dependabot.com/elastic/elasticsearch/issues/40207)) - [`3c00866`](https://github.com/elastic/elasticsearch/commit/3c008663a2749ff38804700bf0163814369b703f) SQL: Fix issue with optimization on queries with ORDER BY/LIMIT ([#40256](https://github-redirect.dependabot.com/elastic/elasticsearch/issues/40256)) - [`6998ea7`](https://github.com/elastic/elasticsearch/commit/6998ea7280772816b503d72ce9a5621328848c82) SQL: Fix issue with date columns returned always in UTC ([#40163](https://github-redirect.dependabot.com/elastic/elasticsearch/issues/40163)) - [`df1883d`](https://github.com/elastic/elasticsearch/commit/df1883d745f074d4d0c3de41726bddbf80c16243) Fix snapshot restore logging on fresh restore ([#40252](https://github-redirect.dependabot.com/elastic/elasticsearch/issues/40252)) - [`d5b26a4`](https://github.com/elastic/elasticsearch/commit/d5b26a46cd2e2b6d75442385b59427ad102c0419) Cascading primary failure lead to MSU too low ([#40249](https://github-redirect.dependabot.com/elastic/elasticsearch/issues/40249)) - Additional commits viewable in [compare view](https://github.com/elastic/elasticsearch/compare/v6.6.2...v6.7.0)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=org.elasticsearch.client:transport&package-manager=gradle&previous-version=6.6.2&new-version=6.7.0)](https://dependabot.com/compatibility-score.html?dependency-name=org.elasticsearch.client:transport&package-manager=gradle&previous-version=6.6.2&new-version=6.7.0) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) If all status checks pass Dependabot will automatically merge this pull request. [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot ignore this [patch|minor|major] version` will close this PR and stop Dependabot creating any more for this minor/major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 78d8c96a4be..f7119b02ef6 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,5 +3,5 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') testCompile "org.elasticsearch.client:elasticsearch-rest-client:6.6.2" - testCompile "org.elasticsearch.client:transport:6.6.2" + testCompile "org.elasticsearch.client:transport:6.7.0" } From bf1e5b22dbb090d2e49e6232dbf885b5a941a69e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Thu, 28 Mar 2019 17:38:29 +0000 Subject: [PATCH 0679/5615] Bump elasticsearch-rest-client in /modules/elasticsearch (#1343) Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 6.6.2 to 6.7.0. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v6.6.2...v6.7.0) Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index f7119b02ef6..af6efc2a436 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,6 +2,6 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') - testCompile "org.elasticsearch.client:elasticsearch-rest-client:6.6.2" + testCompile "org.elasticsearch.client:elasticsearch-rest-client:6.7.0" testCompile "org.elasticsearch.client:transport:6.7.0" } From 94b1a3c9b85311143aa60dd210af6c6ccb05ca91 Mon Sep 17 00:00:00 2001 From: bopi- Date: Fri, 29 Mar 2019 17:47:09 +0100 Subject: [PATCH 0680/5615] hostPort != containerPort (#1334) * hostPort != containerPort * hostPort != containerPort * hostPort != containerPort * hostPort != containerPort * hostPort != containerPort * hostPort != containerPort * hostPort != containerPort * Update ExposedHostTest.java * Do not use the two-keys Map because of the key conflicts --- .../org/testcontainers/Testcontainers.java | 10 ++++++++++ .../containers/PortForwardingContainer.java | 13 ++++++++++--- .../containers/ExposedHostTest.java | 18 ++++++++++++++---- 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/testcontainers/Testcontainers.java b/core/src/main/java/org/testcontainers/Testcontainers.java index 4d7f5af4753..0f83725c55b 100644 --- a/core/src/main/java/org/testcontainers/Testcontainers.java +++ b/core/src/main/java/org/testcontainers/Testcontainers.java @@ -1,6 +1,10 @@ package org.testcontainers; +import java.util.Map; +import java.util.Map.Entry; + import lombok.experimental.UtilityClass; + import org.testcontainers.containers.PortForwardingContainer; @UtilityClass @@ -11,4 +15,10 @@ public void exposeHostPorts(int... ports) { PortForwardingContainer.INSTANCE.exposeHostPort(port); } } + + public void exposeHostPorts(Map ports) { + for(Entry entry : ports.entrySet()) { + PortForwardingContainer.INSTANCE.exposeHostPort(entry.getKey(), entry.getValue()); + } + } } diff --git a/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java b/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java index 576ac33d148..852a986b58f 100644 --- a/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java +++ b/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java @@ -8,10 +8,12 @@ import org.testcontainers.utility.TestcontainersConfiguration; import java.time.Duration; +import java.util.AbstractMap; import java.util.Collections; import java.util.Optional; import java.util.Set; import java.util.UUID; +import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; public enum PortForwardingContainer { @@ -19,7 +21,7 @@ public enum PortForwardingContainer { private GenericContainer container; - private final Set exposedPorts = Collections.newSetFromMap(new ConcurrentHashMap<>()); + private final Set> exposedPorts = Collections.newSetFromMap(new ConcurrentHashMap<>()); @Getter(value = AccessLevel.PRIVATE, lazy = true) private final Connection sshConnection = createSSHSession(); @@ -56,8 +58,13 @@ private Connection createSSHSession() { @SneakyThrows public void exposeHostPort(int port) { - if (exposedPorts.add(port)) { - getSshConnection().requestRemotePortForwarding("", port, "localhost", port); + exposeHostPort(port, port); + } + + @SneakyThrows + public void exposeHostPort(int hostPort, int containerPort) { + if (exposedPorts.add(new AbstractMap.SimpleEntry<>(hostPort, containerPort))) { + getSshConnection().requestRemotePortForwarding("", containerPort, "localhost", hostPort); } } diff --git a/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java b/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java index 30bb6573533..8c98b0730b0 100644 --- a/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java +++ b/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java @@ -1,5 +1,6 @@ package org.testcontainers.containers; +import com.google.common.collect.ImmutableMap; import com.sun.net.httpserver.HttpServer; import lombok.SneakyThrows; import org.junit.AfterClass; @@ -30,6 +31,9 @@ public static void setUpClass() throws Exception { server.start(); Testcontainers.exposeHostPorts(server.getAddress().getPort()); + + Testcontainers.exposeHostPorts(ImmutableMap.of(server.getAddress().getPort(), 80)); + Testcontainers.exposeHostPorts(ImmutableMap.of(server.getAddress().getPort(), 81)); } @AfterClass @@ -39,22 +43,28 @@ public static void tearDownClass() throws Exception { @Test public void testExposedHost() throws Exception { - assertResponse(new GenericContainer().withCommand("top")); + assertResponse(new GenericContainer().withCommand("top"), server.getAddress().getPort()); } @Test public void testExposedHostWithNetwork() throws Exception { try (Network network = Network.newNetwork()) { - assertResponse(new GenericContainer().withNetwork(network).withCommand("top")); + assertResponse(new GenericContainer().withNetwork(network).withCommand("top"), server.getAddress().getPort()); } } + + @Test + public void testExposedHostPortOnFixedInternalPorts() throws Exception { + assertResponse(new GenericContainer().withCommand("top"), 80); + assertResponse(new GenericContainer().withCommand("top"), 81); + } @SneakyThrows - protected void assertResponse(GenericContainer container) { + protected void assertResponse(GenericContainer container, int port) { try { container.start(); - String response = container.execInContainer("wget", "-O", "-", "http://host.testcontainers.internal:" + server.getAddress().getPort()).getStdout(); + String response = container.execInContainer("wget", "-O", "-", "http://host.testcontainers.internal:" + port).getStdout(); assertEquals("received response", "Hello World!", response); } finally { From bb7c9bd1a665432d1fbb139ee7d6d60f1eb7aba2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 8 Apr 2019 16:50:45 +0100 Subject: [PATCH 0681/5615] Bump neo4j from 3.5.3 to 3.5.4 in /modules/neo4j (#1368) Bumps [neo4j](https://github.com/neo4j/neo4j) from 3.5.3 to 3.5.4.
Release notes *Sourced from [neo4j's releases](https://github.com/neo4j/neo4j/releases).* > ## Neo4j 1.9.0 stable release > The 1.9 release of Neo4j builds upon the previous 1.8 releases and brings extensive improvements in stability and performance. In addition, _Neo4j 1.9 Enterprise_ introduces auto-clustering support, with dramatically simplified configuration and deployment for high demand production use. > - Support for [auto-clustering in _Neo4j Enterprise_](http://docs.neo4j.org/chunked/1.9/ha.html), which replaces the previous Zookeeper based HA clustering, provides a streamlined deployment process and is much safer and more resilient in failure scenarios. > - New [neo4j-arbiter](http://docs.neo4j.org/chunked/1.9/arbiter-instances.html) tool, which acts as a voting instance in a Neo4j cluster but does not keep a data store or handle transactions. > - Addition of [string manipulation, timestamp and other functions](http://docs.neo4j.org/chunked/1.9/query-function.html) to the Cypher query language. > - Improvements to memory utilization, which reduces working memory requirements and improves overall performance. > - New [REST endpoints for inspecting cluster status information](http://docs.neo4j.org/chunked/1.9/ha-rest-info.html) (master, slave, etc). > - Correction of several concurrency and performance issues. > - Improvements to [Online Backup](http://docs.neo4j.org/chunked/1.9/operations-backup.html) in _Neo4j Enterprise_, including auto-detection of full vs incremental backup based on existing content at the backup location. > - Updates to Neo4j server webadmin, including welcome pages for new users. > > **It is recommended to upgrade all previous Neo4j installations to this release.** Neo4j 1.9 does not require any explicit upgrade to persistent stores created using Neo4j 1.7 and 1.8 installations, however _current users of Neo4j Enterprise must consult the documentation and update the cluster configuration_. Please see the [deployment](http://docs.neo4j.org/chunked/1.9/deployment.html) and [cluster upgrade](http://docs.neo4j.org/chunked/1.9/upgrade-guide.html) sections of the Neo4j manual for more detail. Also note that Neo4j 1.9 is tested and supported with [Oracle JRE 7](http://www.oracle.com/technetwork/java/javase/downloads/index.html). _Users of previous Java versions should upgrade to [Oracle JRE 7](http://www.oracle.com/technetwork/java/javase/downloads/index.html) when deploying Neo4j 1.9._ > > As part of the Neo4j 1.9 release, multiple behaviours and features of Neo4j have been declared as deprecated, in order to help users prepare for the removal of these in the next major Neo4j release. A [list of deprecations](http://docs.neo4j.org/chunked/1.9.3/deprecations.html) can be found in the Neo4j manual. > > For a full summary of changes in this release, please review the CHANGES.TXT file contained within the distribution. > > ## Neo4j 1.8.0 stable release > The 1.8 release of Neo4j provides extensive improvements in stability and performance. As an incremental service release, Neo4j 1.8 builds upon the previous 1.6 and 1.7 releases and provides full backward compatibility. > > This release includes the following corrections and improvements: > - Correction of several concurrency issues affecting performance in multi- > threaded configurations. > - Correction of several concurrency issues that infrequently affect consistency of persisted data. > - Optimization of high-availability (HA) protocols to increase write performance. > - Support for zero-downtime rolling upgrades in HA clusters. > - Improvements to transaction distribution in HA clusters, providing greater resistance to branching. > - Configurable rotation of logical logs. > - Support for streamed responses to REST API requests. > - Support for bi-directional traversals, branch state and path expanders in the traversal framework. > - Performance improvements to several graph algorithms. > - Support in the Cypher language for writing graph data and updating auto-indexes (manual indexes are not supported). > - Extensions to the Cypher language to allow the use of patterns as expressions and predicates. > - Correction of several issues in the Cypher parser when using collections and when aggregating. > - Improved error reporting in Cypher and more reliable statement parsing. > - Support for explicit transactions in neo4j-shell. > > **It is recommended to upgrade all previous Neo4j installations to this release.** Neo4j 1.8 does not require any explicit upgrade to persistent stores created using Neo4j 1.6 and 1.7 installations. Please see the [deployment section of the Neo4j manual](http://docs.neo4j.org/chunked/1.8/deployment.html) for more detail. > > For a full summary of changes in this release, please review the CHANGES.TXT file contained within the distribution.
Commits - [`a41a4c3`](https://github.com/neo4j/neo4j/commit/a41a4c33edcacd51f2ad1305895d47db588d83bb) Ignored revapi for default method and made guard installer thread safe - [`58a2d96`](https://github.com/neo4j/neo4j/commit/58a2d9695d17a1f936dbae65231e9c689b008a23) Address reviewer feedback for 1603 - [`1515a6b`](https://github.com/neo4j/neo4j/commit/1515a6bbed4b21c4a2e5da21990d92dcb5c766a7) Test fixes - [`214c116`](https://github.com/neo4j/neo4j/commit/214c116f659b11c6eb8b11429d602586ad7bf128) Added AvailabilityGuardInstaller to LifecycleManagingDatabase - [`8d133d5`](https://github.com/neo4j/neo4j/commit/8d133d590b7cb54d2b949acd67243ebe2f43d685) Merge branch '3.4' into 3.5 - [`c60c090`](https://github.com/neo4j/neo4j/commit/c60c090b64ee00b6c8c2598e42cd5fa92b4849a4) Merge branch '3.3' into 3.4 - [`dadb6a9`](https://github.com/neo4j/neo4j/commit/dadb6a992e7e02c3f16a71ef0f93152f393bcf0a) Backport a data-race fix that could prevent profiles from being captured. - [`e4db673`](https://github.com/neo4j/neo4j/commit/e4db673dcf926f7c779bf62d5d1feadf1abc611b) Merge remote-tracking branch 'origin/3.4' into 3.5 - [`2041c79`](https://github.com/neo4j/neo4j/commit/2041c79183c8ab1f136ea0eb70b920e0a949d1d5) Merge remote-tracking branch 'origin/3.3' into 3.4 - [`f9c012a`](https://github.com/neo4j/neo4j/commit/f9c012ad1e0501865a0f7940f3e3e8e472f36eb7) Fix possible NPE in dbms.setTXMetaData - Additional commits viewable in [compare view](https://github.com/neo4j/neo4j/compare/3.5.3...3.5.4)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=org.neo4j:neo4j&package-manager=gradle&previous-version=3.5.3&new-version=3.5.4)](https://dependabot.com/compatibility-score.html?dependency-name=org.neo4j:neo4j&package-manager=gradle&previous-version=3.5.3&new-version=3.5.4) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) If all status checks pass Dependabot will automatically merge this pull request. [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot ignore this [patch|minor|major] version` will close this PR and stop Dependabot creating any more for this minor/major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 092cb3a6889..9b0f3a48c3b 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -25,7 +25,7 @@ task customNeo4jPluginJar(type: Jar) { test.dependsOn customNeo4jPluginJar dependencies { - customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.3" + customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.4" compile project(":testcontainers") From 81ca4f196436c56c423cea0c663312e049635ab6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 8 Apr 2019 16:51:12 +0100 Subject: [PATCH 0682/5615] Bump elasticsearch-rest-client in /modules/elasticsearch (#1369) Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 6.7.0 to 6.7.1. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v6.7.0...v6.7.1) Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index af6efc2a436..68e9028c43b 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,6 +2,6 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') - testCompile "org.elasticsearch.client:elasticsearch-rest-client:6.7.0" + testCompile "org.elasticsearch.client:elasticsearch-rest-client:6.7.1" testCompile "org.elasticsearch.client:transport:6.7.0" } From dc61742b47c1a20edf8b1057c2db0fc079bf7a89 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 8 Apr 2019 16:51:27 +0100 Subject: [PATCH 0683/5615] Bump junit-jupiter-engine from 5.4.1 to 5.4.2 in /modules/junit-jupiter (#1364) Bumps [junit-jupiter-engine](https://github.com/junit-team/junit5) from 5.4.1 to 5.4.2. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.4.1...r5.4.2) Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index ba9a2db096e..bd2f38e5737 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -12,7 +12,7 @@ dependencies { testRuntime 'org.postgresql:postgresql:42.2.5' testRuntime 'mysql:mysql-connector-java:8.0.15' - testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.4.1' + testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.4.2' } test { From 12c1fd4a39ad5f371bca2f896bcd2bcefb8fc205 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 8 Apr 2019 16:56:51 +0100 Subject: [PATCH 0684/5615] Bump httpclient from 4.5.7 to 4.5.8 in /modules/spock (#1353) Bumps httpclient from 4.5.7 to 4.5.8. [![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=org.apache.httpcomponents:httpclient&package-manager=gradle&previous-version=4.5.7&new-version=4.5.8)](https://dependabot.com/compatibility-score.html?dependency-name=org.apache.httpcomponents:httpclient&package-manager=gradle&previous-version=4.5.7&new-version=4.5.8) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) If all status checks pass Dependabot will automatically merge this pull request. [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot ignore this [patch|minor|major] version` will close this PR and stop Dependabot creating any more for this minor/major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 2e8b175e303..d9c482b0c7b 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -11,7 +11,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') testCompile 'com.zaxxer:HikariCP:3.3.1' - testCompile 'org.apache.httpcomponents:httpclient:4.5.7' + testCompile 'org.apache.httpcomponents:httpclient:4.5.8' testRuntime 'org.postgresql:postgresql:42.2.5' testRuntime 'mysql:mysql-connector-java:8.0.15' From 9f23e022fca06e1903ebb811b194ca8fd0c75ada Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 8 Apr 2019 16:57:16 +0100 Subject: [PATCH 0685/5615] Bump httpclient from 4.5.7 to 4.5.8 in /modules/junit-jupiter (#1352) Bumps httpclient from 4.5.7 to 4.5.8. Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index bd2f38e5737..36b0f217cb4 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -8,7 +8,7 @@ dependencies { testCompile project(':postgresql') testCompile 'com.zaxxer:HikariCP:3.3.1' testCompile 'redis.clients:jedis:3.0.1' - testCompile 'org.apache.httpcomponents:httpclient:4.5.7' + testCompile 'org.apache.httpcomponents:httpclient:4.5.8' testRuntime 'org.postgresql:postgresql:42.2.5' testRuntime 'mysql:mysql-connector-java:8.0.15' From 738c90a8f08b45ed7f7cb3bb49a9ad18cafacab5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 8 Apr 2019 16:57:31 +0100 Subject: [PATCH 0686/5615] Bump httpclient from 4.5.7 to 4.5.8 in /core (#1351) Bumps httpclient from 4.5.7 to 4.5.8. Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 1e3083c4542..57984f513f6 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -100,7 +100,7 @@ dependencies { exclude(group: 'org.slf4j') } - testCompile 'org.apache.httpcomponents:httpclient:4.5.7' + testCompile 'org.apache.httpcomponents:httpclient:4.5.8' testCompile 'redis.clients:jedis:3.0.1' testCompile 'com.rabbitmq:amqp-client:5.6.0' testCompile 'org.mongodb:mongo-java-driver:3.10.1' From 935b12f3798265c588f28212877719721b4f39cf Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 9 Apr 2019 15:54:04 +0100 Subject: [PATCH 0687/5615] Update mssqlserver docs to link to EULA description --- docs/modules/databases/mssqlserver.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/modules/databases/mssqlserver.md b/docs/modules/databases/mssqlserver.md index 9d63cee9494..9e0bec25dad 100644 --- a/docs/modules/databases/mssqlserver.md +++ b/docs/modules/databases/mssqlserver.md @@ -20,7 +20,9 @@ public class SomeTest { ``` !!! warning "EULA Acceptance" - Due to licencing restrictions you are required to accept an EULA for this container image. To indicate that you accept the MS SQL Server image EULA, Please place a file at the root of the classpath named `container-license-acceptance.txt`, e.g. at `src/test/resources/container-license-acceptance.txt`. This file should contain the line: `mcr.microsoft.com/mssql/server:latest` + Due to licencing restrictions you are required to accept an EULA for this container image. To indicate that you accept the MS SQL Server image EULA, Please place a file at the root of the classpath named `container-license-acceptance.txt`, e.g. at `src/test/resources/container-license-acceptance.txt`. This file should contain the line: `mcr.microsoft.com/mssql/server:2017-CU12` (or, if you are overriding the docker image name/tag, update accordingly). + + Please see the [`microsoft-mssql-server` image documentation](https://hub.docker.com/_/microsoft-mssql-server#environment-variables) for a link to the EULA document. ## Adding this module to your project dependencies From 1938b32868751af6b28304292bf5b6fa9a4bf18e Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 9 Apr 2019 20:18:55 +0100 Subject: [PATCH 0688/5615] Add build args and Dockerfile path override to ImageFromDockerfile (#1372) --- .../images/builder/ImageFromDockerfile.java | 20 ++++++ .../images/builder/DockerfileBuildTest.java | 67 +++++++++++++++++++ .../dockerfile-build-test/Dockerfile | 2 + .../dockerfile-build-test/Dockerfile-alt | 2 + .../dockerfile-build-test/Dockerfile-buildarg | 3 + .../dockerfile-build-test/localfile.txt | 1 + docs/features/creating_images.md | 33 +++++++-- 7 files changed, 122 insertions(+), 6 deletions(-) create mode 100644 core/src/test/java/org/testcontainers/images/builder/DockerfileBuildTest.java create mode 100644 core/src/test/resources/dockerfile-build-test/Dockerfile create mode 100644 core/src/test/resources/dockerfile-build-test/Dockerfile-alt create mode 100644 core/src/test/resources/dockerfile-build-test/Dockerfile-buildarg create mode 100644 core/src/test/resources/dockerfile-build-test/localfile.txt diff --git a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java index ed109acc4c3..c7d409c0660 100644 --- a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java +++ b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java @@ -23,6 +23,7 @@ import java.io.PipedOutputStream; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.zip.GZIPOutputStream; @@ -60,6 +61,8 @@ public class ImageFromDockerfile extends LazyFuture implements private boolean deleteOnExit = true; private final Map transferables = new HashMap<>(); + private final Map buildArgs = new HashMap<>(); + private Optional dockerFilePath = Optional.empty(); public ImageFromDockerfile() { this("testcontainers/" + Base58.randomString(16).toLowerCase()); @@ -139,5 +142,22 @@ public void onNext(BuildResponseItem item) { protected void configure(BuildImageCmd buildImageCmd) { buildImageCmd.withTag(this.getDockerImageName()); + this.dockerFilePath.ifPresent(buildImageCmd::withDockerfilePath); + this.buildArgs.forEach(buildImageCmd::withBuildArg); + } + + public ImageFromDockerfile withBuildArg(final String key, final String value) { + this.buildArgs.put(key, value); + return this; + } + + public ImageFromDockerfile withBuildArgs(final Map args) { + this.buildArgs.putAll(args); + return this; + } + + public ImageFromDockerfile withDockerfilePath(String relativePathFromBuildRoot) { + this.dockerFilePath = Optional.of(relativePathFromBuildRoot); + return this; } } diff --git a/core/src/test/java/org/testcontainers/images/builder/DockerfileBuildTest.java b/core/src/test/java/org/testcontainers/images/builder/DockerfileBuildTest.java new file mode 100644 index 00000000000..4c33fe9118b --- /dev/null +++ b/core/src/test/java/org/testcontainers/images/builder/DockerfileBuildTest.java @@ -0,0 +1,67 @@ +package org.testcontainers.images.builder; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; + +@RunWith(Parameterized.class) +public class DockerfileBuildTest { + + private static final Path RESOURCE_PATH = Paths.get("src/test/resources/dockerfile-build-test"); + + public String expectedFileContent; + public ImageFromDockerfile image; + + @Parameterized.Parameters + public static Object[][] parameters() { + return new Object[][]{ + // Dockerfile build without explicit per-file inclusion + new Object[]{"test1234", + // docsShowRecursiveFileInclusion { + new ImageFromDockerfile() + .withFileFromPath(".", RESOURCE_PATH) + // } + }, + + // Dockerfile build using a non-standard Dockerfile + new Object[]{"test4567", + new ImageFromDockerfile() + .withFileFromPath(".", RESOURCE_PATH) + .withDockerfilePath("./Dockerfile-alt") + }, + + // Dockerfile build using build args + new Object[]{"test7890", + new ImageFromDockerfile() + .withFileFromPath(".", RESOURCE_PATH) + .withDockerfilePath("./Dockerfile-buildarg") + .withBuildArg("CUSTOM_ARG", "test7890") + }, + }; + } + + public DockerfileBuildTest(String expectedFileContent, ImageFromDockerfile image) { + this.expectedFileContent = expectedFileContent; + this.image = image; + } + + @Test + public void performTest() { + try (final GenericContainer container = new GenericContainer(image) + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .withCommand("cat", "/test.txt")) { + + container.start(); + + final String logs = container.getLogs(); + assertTrue("expected file content indicates that dockerfile build steps have been run", logs.contains(expectedFileContent)); + } + } +} diff --git a/core/src/test/resources/dockerfile-build-test/Dockerfile b/core/src/test/resources/dockerfile-build-test/Dockerfile new file mode 100644 index 00000000000..eee182f21c1 --- /dev/null +++ b/core/src/test/resources/dockerfile-build-test/Dockerfile @@ -0,0 +1,2 @@ +FROM alpine:3.2 +COPY localfile.txt /test.txt diff --git a/core/src/test/resources/dockerfile-build-test/Dockerfile-alt b/core/src/test/resources/dockerfile-build-test/Dockerfile-alt new file mode 100644 index 00000000000..f6938e5fc8d --- /dev/null +++ b/core/src/test/resources/dockerfile-build-test/Dockerfile-alt @@ -0,0 +1,2 @@ +FROM alpine:3.2 +RUN echo "test4567" > /test.txt diff --git a/core/src/test/resources/dockerfile-build-test/Dockerfile-buildarg b/core/src/test/resources/dockerfile-build-test/Dockerfile-buildarg new file mode 100644 index 00000000000..4ae26f50f77 --- /dev/null +++ b/core/src/test/resources/dockerfile-build-test/Dockerfile-buildarg @@ -0,0 +1,3 @@ +FROM alpine:3.2 +ARG CUSTOM_ARG +RUN echo "${CUSTOM_ARG}" > /test.txt diff --git a/core/src/test/resources/dockerfile-build-test/localfile.txt b/core/src/test/resources/dockerfile-build-test/localfile.txt new file mode 100644 index 00000000000..5586d4f8333 --- /dev/null +++ b/core/src/test/resources/dockerfile-build-test/localfile.txt @@ -0,0 +1 @@ +test1234 diff --git a/docs/features/creating_images.md b/docs/features/creating_images.md index 0437e6d8a8e..0539642e159 100644 --- a/docs/features/creating_images.md +++ b/docs/features/creating_images.md @@ -9,10 +9,7 @@ image needs to be customized for specific test(s). Simply pass a configured instance of `ImageFromDockerfile` as a constructor parameter to `GenericContainer`. Testcontainers will `docker build` a temporary container image, and will use it when creating the container. - -## Examples - -### Dockerfile from String, file or classpath resource +## Dockerfile from String, file or classpath resource `ImageFromDockerfile` accepts arbitrary files, strings or classpath resources to be used as files in the build context. At least one of these needs to be a `Dockerfile`. @@ -25,14 +22,27 @@ public GenericContainer dslContainer = new GenericContainer( .withFileFromClasspath("Dockerfile", "mappable-dockerfile/Dockerfile")) ``` -The following methods may be used to provide the `Dockerfile` and any other required files: +The following methods may be used to provide the `Dockerfile` and any other required build context files: * `withFileFromString(buildContextPath, content)` * `withFileFromClasspath(buildContextPath, classpathPath)` * `withFileFromPath(buildContextPath, filesystemPath)` * `withFileFromFile(buildContextPath, filesystemFile)` -### Dockerfile DSL +!!! info + In older versions of Testcontainers (before 1.3.0) it was necessary to explicitly declare each file that needed to + be present in the Docker build context. + This can be replaced with the following syntax: + + [Passing an entire directory of files to the Dockerfile build context](../../core/src/test/java/org/testcontainers/images/builder/DockerfileBuildTest.java) inside_block:docsShowRecursiveFileInclusion + + + Where `RESOURCE_PATH` is the path to a directory containing a `Dockerfile` and any files that it needs to refer to. + Doing this is equivalent to `docker build RESOURCE_PATH` on the command line. + + To mimic `docker build .`, `RESOURCE_PATH` would simply be set to `.` as well. + +## Dockerfile DSL If a static Dockerfile is not sufficient (e.g. your test needs to cover many variations that are best generated programmatically), there is a DSL available to allow Dockerfiles to be defined in code. e.g.: @@ -64,3 +74,14 @@ By default the no-args constructor will use an image name of the form `testconta * `public ImageFromDockerfile()` * `public ImageFromDockerfile(String dockerImageName)` * `public ImageFromDockerfile(String dockerImageName, boolean deleteOnExit)` + +## Alternative Dockerfiles + +Normally Docker will automatically build an image from any `/Dockerfile` that it finds in the root of the build context. +To override this behaviour, use `.withDockerfilePath("./Name-Of-Other-Dockerfile")`. + +## Build Args + +[Build Args](https://docs.docker.com/engine/reference/builder/#arg) may be used to allow lightweight parameterization. + +To specify build args, use `.withBuildArg("varname", "value")` or provide a `Map` of args using `.withBuildArgs(map)`. From 30d5f8518f86dc2861213fb6f59432defcffcb82 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 9 Apr 2019 20:21:15 +0100 Subject: [PATCH 0689/5615] Bump transport from 6.7.0 to 6.7.1 in /modules/elasticsearch (#1370) Bumps [transport](https://github.com/elastic/elasticsearch) from 6.7.0 to 6.7.1. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v6.7.0...v6.7.1) Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 68e9028c43b..699bc54bd9e 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,5 +3,5 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') testCompile "org.elasticsearch.client:elasticsearch-rest-client:6.7.1" - testCompile "org.elasticsearch.client:transport:6.7.0" + testCompile "org.elasticsearch.client:transport:6.7.1" } From 2da17b9855f855b3ae6ea5b9f07f8cb6080dc2fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 9 Apr 2019 20:21:33 +0100 Subject: [PATCH 0690/5615] Bump mockito-core from 2.25.1 to 2.26.0 in /core (#1366) Bumps [mockito-core](https://github.com/mockito/mockito) from 2.25.1 to 2.26.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v2.25.1...v2.26.0) Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 57984f513f6..cbac38e88ff 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -104,7 +104,7 @@ dependencies { testCompile 'redis.clients:jedis:3.0.1' testCompile 'com.rabbitmq:amqp-client:5.6.0' testCompile 'org.mongodb:mongo-java-driver:3.10.1' - testCompile ('org.mockito:mockito-core:2.25.1') { + testCompile ('org.mockito:mockito-core:2.26.0') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From 3fff9741183013666a0e5672fc1359e03f431681 Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Wed, 10 Apr 2019 12:37:21 +0200 Subject: [PATCH 0691/5615] Improve WaitAllStrategy (#1272) --- .../containers/DockerComposeContainer.java | 2 +- .../wait/strategy/WaitAllStrategy.java | 73 +++++- .../containers/wait/WaitAllStrategyTest.java | 69 ------ .../wait/strategy/WaitAllStrategyTest.java | 224 ++++++++++++++++++ 4 files changed, 290 insertions(+), 78 deletions(-) delete mode 100644 core/src/test/java/org/testcontainers/containers/wait/WaitAllStrategyTest.java create mode 100644 core/src/test/java/org/testcontainers/containers/wait/strategy/WaitAllStrategyTest.java diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index ae10c3096fa..0b6db19ca28 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -335,7 +335,7 @@ private String getServiceInstanceName(String serviceName) { */ private void addWaitStrategy(String serviceInstanceName, @NonNull WaitStrategy waitStrategy) { final WaitAllStrategy waitAllStrategy = waitStrategyMap.computeIfAbsent(serviceInstanceName, __ -> - (WaitAllStrategy) new WaitAllStrategy().withStartupTimeout(Duration.ofMinutes(30))); + new WaitAllStrategy(WaitAllStrategy.Mode.WITH_MAXIMUM_OUTER_TIMEOUT).withStartupTimeout(Duration.ofMinutes(30))); waitAllStrategy.withStrategy(waitStrategy); } diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitAllStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitAllStrategy.java index 1cc76c0747c..cebacb375b6 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitAllStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitAllStrategy.java @@ -1,34 +1,91 @@ package org.testcontainers.containers.wait.strategy; -import org.rnorth.ducttape.timeouts.Timeouts; - import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; +import org.rnorth.ducttape.timeouts.Timeouts; + public class WaitAllStrategy implements WaitStrategy { + public enum Mode { + + /** + * This is the default mode: The timeout of the {@link WaitAllStrategy strategy} is applied to each individual + * strategy, so that the container waits maximum for + * {@link org.testcontainers.containers.wait.strategy.WaitAllStrategy#timeout}. + */ + WITH_OUTER_TIMEOUT, + + /** + * Using this mode triggers the following behaviour: The outer timeout is disabled and the outer enclosing + * strategy waits for all inner strategies according to their timeout. Once set, it disables + * {@link org.testcontainers.containers.wait.strategy.WaitAllStrategy#withStartupTimeout(java.time.Duration)} method, + * as it would overwrite inner timeouts. + */ + WITH_INDIVIDUAL_TIMEOUTS_ONLY, + + /** + * This is the original mode of this strategy: The inner strategies wait with their preconfigured timeout + * individually and the wait all strategy kills them, if the outer limit is reached. + */ + WITH_MAXIMUM_OUTER_TIMEOUT + } + + private final Mode mode; private final List strategies = new ArrayList<>(); private Duration timeout = Duration.ofSeconds(30); + public WaitAllStrategy() { + this(Mode.WITH_OUTER_TIMEOUT); + } + + public WaitAllStrategy(Mode mode) { + this.mode = mode; + } + @Override public void waitUntilReady(WaitStrategyTarget waitStrategyTarget) { - Timeouts.doWithTimeout((int) timeout.toMillis(), TimeUnit.MILLISECONDS, () -> { - for (WaitStrategy strategy : strategies) { - strategy.waitUntilReady(waitStrategyTarget); - } - }); + if (mode == Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY) { + waitUntilNestedStrategiesAreReady(waitStrategyTarget); + } else { + Timeouts.doWithTimeout((int) timeout.toMillis(), TimeUnit.MILLISECONDS, () -> { + waitUntilNestedStrategiesAreReady(waitStrategyTarget); + }); + } + } + + private void waitUntilNestedStrategiesAreReady(WaitStrategyTarget waitStrategyTarget) { + for (WaitStrategy strategy : strategies) { + strategy.waitUntilReady(waitStrategyTarget); + } } public WaitAllStrategy withStrategy(WaitStrategy strategy) { + + if (mode == Mode.WITH_OUTER_TIMEOUT) { + applyStartupTimeout(strategy); + } + this.strategies.add(strategy); return this; } @Override - public WaitStrategy withStartupTimeout(Duration startupTimeout) { + public WaitAllStrategy withStartupTimeout(Duration startupTimeout) { + + if (mode == Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY) { + throw new IllegalStateException(String.format( + "Changing startup timeout is not supported with mode %s", Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY)); + } + this.timeout = startupTimeout; + strategies.forEach(this::applyStartupTimeout); return this; } + + private void applyStartupTimeout(WaitStrategy childStrategy) { + childStrategy.withStartupTimeout(this.timeout); + } } diff --git a/core/src/test/java/org/testcontainers/containers/wait/WaitAllStrategyTest.java b/core/src/test/java/org/testcontainers/containers/wait/WaitAllStrategyTest.java deleted file mode 100644 index f47a7212a5c..00000000000 --- a/core/src/test/java/org/testcontainers/containers/wait/WaitAllStrategyTest.java +++ /dev/null @@ -1,69 +0,0 @@ -package org.testcontainers.containers.wait; - -import com.google.common.util.concurrent.Uninterruptibles; -import org.junit.Before; -import org.junit.Test; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.rnorth.ducttape.TimeoutException; -import org.testcontainers.containers.GenericContainer; - -import java.time.Duration; -import java.util.concurrent.TimeUnit; - -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.inOrder; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; - -public class WaitAllStrategyTest { - - @Mock - private GenericContainer container; - @Mock - private WaitStrategy strategy1; - @Mock - private WaitStrategy strategy2; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - } - - @Test - public void simpleTest() { - - final WaitStrategy underTest = new WaitAllStrategy() - .withStrategy(strategy1) - .withStrategy(strategy2); - - doNothing().when(strategy1).waitUntilReady(eq(container)); - doNothing().when(strategy2).waitUntilReady(eq(container)); - - underTest.waitUntilReady(container); - - InOrder inOrder = inOrder(strategy1, strategy2); - inOrder.verify(strategy1).waitUntilReady(any()); - inOrder.verify(strategy2).waitUntilReady(any()); - } - - @Test - public void appliesOuterTimeout() { - - final WaitStrategy underTest = new WaitAllStrategy() - .withStrategy(strategy1) - .withStartupTimeout(Duration.ofMillis(10)); - - doAnswer(invocation -> { - Uninterruptibles.sleepUninterruptibly(10, TimeUnit.SECONDS); - return null; - }).when(strategy1).waitUntilReady(eq(container)); - - assertThrows("The outer strategy timeout applies", TimeoutException.class, () -> { - underTest.waitUntilReady(container); - }); - } -} diff --git a/core/src/test/java/org/testcontainers/containers/wait/strategy/WaitAllStrategyTest.java b/core/src/test/java/org/testcontainers/containers/wait/strategy/WaitAllStrategyTest.java new file mode 100644 index 00000000000..3b2172d5bcf --- /dev/null +++ b/core/src/test/java/org/testcontainers/containers/wait/strategy/WaitAllStrategyTest.java @@ -0,0 +1,224 @@ +package org.testcontainers.containers.wait.strategy; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.rnorth.visibleassertions.VisibleAssertions.*; + +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.rnorth.ducttape.TimeoutException; +import org.testcontainers.containers.GenericContainer; + +import com.google.common.util.concurrent.Uninterruptibles; + +public class WaitAllStrategyTest { + + @Mock + private GenericContainer container; + @Mock + private WaitStrategy strategy1; + @Mock + private WaitStrategy strategy2; + @Mock + private WaitStrategy strategy3; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void simpleTest() { + + final WaitStrategy underTest = new WaitAllStrategy() + .withStrategy(strategy1) + .withStrategy(strategy2); + + doNothing().when(strategy1).waitUntilReady(eq(container)); + doNothing().when(strategy2).waitUntilReady(eq(container)); + + underTest.waitUntilReady(container); + + InOrder inOrder = inOrder(strategy1, strategy2); + inOrder.verify(strategy1).waitUntilReady(any()); + inOrder.verify(strategy2).waitUntilReady(any()); + } + + @Test + public void maxTimeOutApplies() { + + WaitStrategy child1 = new SleepingStrategy(Duration.ofMinutes(30)) + .withStartupTimeout(Duration.ofHours(1)); + + final WaitStrategy underTest = new WaitAllStrategy() + .withStrategy(child1) + .withStartupTimeout(Duration.ofMillis(10)); + + assertThrows("The outer strategy timeout applies", TimeoutException.class, () -> { + underTest.waitUntilReady(container); + }); + } + + @Test + public void appliesOuterTimeoutTooLittleTime() { + + Duration defaultInnerWait = Duration.ofMillis(2); + Duration outerWait = Duration.ofMillis(6); + + WaitStrategy child1 = new SleepingStrategy(defaultInnerWait); + WaitStrategy child2 = new SleepingStrategy(defaultInnerWait); + + final WaitStrategy underTest = new WaitAllStrategy() + .withStrategy(child1) + .withStrategy(child2) + .withStartupTimeout(outerWait); + + assertThrows("The outer strategy timeout applies", TimeoutException.class, () -> { + underTest.waitUntilReady(container); + }); + } + + @Test + public void appliesOuterTimeoutEnoughTime() { + + Duration defaultInnerWait = Duration.ofMillis(2); + Duration outerWait = Duration.ofMillis(20); + + WaitStrategy child1 = new SleepingStrategy(defaultInnerWait); + WaitStrategy child2 = new SleepingStrategy(defaultInnerWait); + + final WaitStrategy underTest = new WaitAllStrategy() + .withStrategy(child1) + .withStrategy(child2) + .withStartupTimeout(outerWait); + + try { + underTest.waitUntilReady(container); + } catch (Exception e) { + if (e.getCause() instanceof CaughtTimeoutException) + fail("The timeout wasn't applied to inner strategies."); + else { + fail(e.getMessage()); + } + } + } + + @Test + public void withoutOuterTimeoutShouldRelyOnInnerStrategies() { + + final WaitStrategy underTest = new WaitAllStrategy(WaitAllStrategy.Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY) + .withStrategy(strategy1) + .withStrategy(strategy2) + .withStrategy(strategy3); + + doNothing().when(strategy1).waitUntilReady(eq(container)); + doThrow(TimeoutException.class).when(strategy2).waitUntilReady(eq(container)); + + assertThrows("The outer strategy timeout applies", TimeoutException.class, () -> { + underTest.waitUntilReady(container); + }); + + InOrder inOrder = inOrder(strategy1, strategy2, strategy3); + inOrder.verify(strategy1).waitUntilReady(any()); + inOrder.verify(strategy2).waitUntilReady(any()); + inOrder.verify(strategy3, never()).waitUntilReady(any()); + } + + @Test + public void timeoutChangeShouldNotBePossibleWithIndividualTimeoutMode() { + + final WaitStrategy underTest = new WaitAllStrategy(WaitAllStrategy.Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY); + + assertThrows("Cannot change timeout for individual timeouts", IllegalStateException.class, () -> { + underTest.withStartupTimeout(Duration.ofSeconds(42)); + }); + } + + @Test + public void shouldNotMessWithIndividualTimeouts() { + + final WaitStrategy underTest = new WaitAllStrategy(WaitAllStrategy.Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY) + .withStrategy(strategy1) + .withStrategy(strategy2); + + verify(strategy1, never()).withStartupTimeout(any()); + verify(strategy1, never()).withStartupTimeout(any()); + } + + @Test + public void shouldOverwriteIndividualTimeouts() { + + Duration someSeconds = Duration.ofSeconds(23); + final WaitStrategy underTest = new WaitAllStrategy() + .withStartupTimeout(someSeconds) + .withStrategy(strategy1) + .withStrategy(strategy2); + + verify(strategy1).withStartupTimeout(someSeconds); + verify(strategy1).withStartupTimeout(someSeconds); + } + + @Test + public void appliesOuterTimeoutOnAdditionalChildren() { + + Duration defaultInnerWait = Duration.ofMillis(2); + Duration outerWait = Duration.ofMillis(20); + + WaitStrategy child1 = new SleepingStrategy(defaultInnerWait); + WaitStrategy child2 = new SleepingStrategy(defaultInnerWait); + + final WaitStrategy underTest = new WaitAllStrategy() + .withStrategy(child1) + .withStartupTimeout(outerWait) + .withStrategy(child2); + + try { + underTest.waitUntilReady(container); + } catch (Exception e) { + if (e.getCause() instanceof CaughtTimeoutException) + fail("The timeout wasn't applied to inner strategies."); + else { + fail(e.getMessage()); + } + } + } + + static class CaughtTimeoutException extends RuntimeException { + + CaughtTimeoutException(String message) { + super(message); + } + } + + static class SleepingStrategy extends AbstractWaitStrategy { + + private final long sleepingDuration; + + SleepingStrategy(Duration defaultInnerWait) { + super.startupTimeout = defaultInnerWait; + // Always oversleep by default + this.sleepingDuration = defaultInnerWait.multipliedBy(2).toMillis(); + } + + @Override + protected void waitUntilReady() { + try { + // Don't use ducttape, make sure we don't catch the wrong TimeoutException later on. + CompletableFuture.runAsync( + () -> Uninterruptibles.sleepUninterruptibly(this.sleepingDuration, TimeUnit.MILLISECONDS) + ).get((int) startupTimeout.toMillis(), TimeUnit.MILLISECONDS); + } catch (InterruptedException | ExecutionException e) { + } catch (java.util.concurrent.TimeoutException e) { + throw new CaughtTimeoutException("Inner wait timed out, outer strategy didn't apply."); + } + } + } +} From 1628b659cf991576902457845319602ea2c8fc23 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Wed, 10 Apr 2019 17:58:43 +0100 Subject: [PATCH 0692/5615] Bump amqp-client from 5.6.0 to 5.7.0 in /core (#1367) Bumps [amqp-client](https://github.com/rabbitmq/rabbitmq-java-client) from 5.6.0 to 5.7.0.
Release notes *Sourced from [amqp-client's releases](https://github.com/rabbitmq/rabbitmq-java-client/releases).* > ## 5.7.0 > # Changes between 5.6.0 and 5.7.0 > > This is a minor release with usability improvements and dependency upgrades. All users of the 5.x.x series are encouraged to upgrade to this version. > > ## Improve logging for TLS connections > > GitHub issue: [#441](https://github-redirect.dependabot.com/rabbitmq/rabbitmq-java-client/issues/441) > > ## Don't panic when cancelling an unknown consumer > > GitHub issue: [#525](https://github-redirect.dependabot.com/rabbitmq/rabbitmq-java-client/issues/525) > > ## Bump dependencies > > GitHub issue: [#607](https://github-redirect.dependabot.com/rabbitmq/rabbitmq-java-client/issues/607) > > ## 5.7.0.RC1 > # Changes between 5.6.0 and 5.7.0.RC1 > > This is a pre-release for 5.7.0, a maintenance release with usability improvements and dependency upgrades. All users of the 5.x.x series are encouraged to test this version. > > ## Improve logging for TLS connections > > GitHub issue: [#441](https://github-redirect.dependabot.com/rabbitmq/rabbitmq-java-client/issues/441) > > ## Don't panic when cancelling an unknown consumer > > GitHub issue: [#525](https://github-redirect.dependabot.com/rabbitmq/rabbitmq-java-client/issues/525) > > ## Bump dependencies > > GitHub issue: [#607](https://github-redirect.dependabot.com/rabbitmq/rabbitmq-java-client/issues/607)
Changelog *Sourced from [amqp-client's changelog](https://github.com/rabbitmq/rabbitmq-java-client/blob/v5.7.0/release-versions.txt).* > RELEASE_VERSION="5.7.0" > DEVELOPMENT_VERSION="5.8.0-SNAPSHOT"
Commits - [`57aa724`](https://github.com/rabbitmq/rabbitmq-java-client/commit/57aa724ddd15ac376576894bd869d04b49e62451) [maven-release-plugin] prepare release v5.7.0 - [`1928ce8`](https://github.com/rabbitmq/rabbitmq-java-client/commit/1928ce86a7c7da761003af4ee8fed89237b18938) Set release version to 5.7.0 - [`9f2dffe`](https://github.com/rabbitmq/rabbitmq-java-client/commit/9f2dffec12bcebcc41dc0d2a6d996df04661143a) [maven-release-plugin] prepare for next development iteration - [`e79ed4d`](https://github.com/rabbitmq/rabbitmq-java-client/commit/e79ed4d7491bc541e15c3e548ed77181778ff0b8) [maven-release-plugin] prepare release v5.7.0.RC1 - [`bb8f6ed`](https://github.com/rabbitmq/rabbitmq-java-client/commit/bb8f6edf2f4c7fc3d9c805b91c32e59a58782e0a) Bump test dependencies - [`953d255`](https://github.com/rabbitmq/rabbitmq-java-client/commit/953d255e4728b222e3f3fbc88b8db8a541d82933) Bump dependencies - [`e49f66d`](https://github.com/rabbitmq/rabbitmq-java-client/commit/e49f66d6f0fad816feb33012a95d059cfdab2c20) Add comment to explain decision tree - [`365dd22`](https://github.com/rabbitmq/rabbitmq-java-client/commit/365dd22a51a79ed40e7f45742a4eb71ecbfd44e5) Add test for Channel#basicCancel with unknown consumer tag - [`6081628`](https://github.com/rabbitmq/rabbitmq-java-client/commit/6081628163669bac39456c9fcf1dbc3c6692786d) Log warning when receiving basic.cancel for unknown consumer - [`be57b26`](https://github.com/rabbitmq/rabbitmq-java-client/commit/be57b2620a5d148af3a0857fe798bd1f1bbb26a6) Merge pull request [#548](https://github-redirect.dependabot.com/rabbitmq/rabbitmq-java-client/issues/548) from spring-operator/polish-urls-apache-license-5.x.x... - Additional commits viewable in [compare view](https://github.com/rabbitmq/rabbitmq-java-client/compare/v5.6.0...v5.7.0)

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=com.rabbitmq:amqp-client&package-manager=gradle&previous-version=5.6.0&new-version=5.7.0)](https://dependabot.com/compatibility-score.html?dependency-name=com.rabbitmq:amqp-client&package-manager=gradle&previous-version=5.6.0&new-version=5.7.0) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) If all status checks pass Dependabot will automatically merge this pull request. [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot ignore this [patch|minor|major] version` will close this PR and stop Dependabot creating any more for this minor/major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index cbac38e88ff..112bd096193 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -102,7 +102,7 @@ dependencies { testCompile 'org.apache.httpcomponents:httpclient:4.5.8' testCompile 'redis.clients:jedis:3.0.1' - testCompile 'com.rabbitmq:amqp-client:5.6.0' + testCompile 'com.rabbitmq:amqp-client:5.7.0' testCompile 'org.mongodb:mongo-java-driver:3.10.1' testCompile ('org.mockito:mockito-core:2.26.0') { exclude(module: 'hamcrest-core') From a6c676bc43541100f036ac368d242b25c5a16069 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Wed, 10 Apr 2019 17:58:56 +0100 Subject: [PATCH 0693/5615] Bump kafka-clients from 2.1.1 to 2.2.0 in /modules/kafka (#1338) Bumps kafka-clients from 2.1.1 to 2.2.0. Signed-off-by: dependabot[bot] --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index 6985f7a89a6..cefb6368445 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Kafka" dependencies { compile project(':testcontainers') - testCompile 'org.apache.kafka:kafka-clients:2.1.1' + testCompile 'org.apache.kafka:kafka-clients:2.2.0' testCompile 'org.assertj:assertj-core:3.12.2' testCompile 'com.google.guava:guava:23.0' } From 8a8548affca2dab87478677215283817ce3bbd46 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Wed, 10 Apr 2019 18:00:14 +0100 Subject: [PATCH 0694/5615] Bump mongo-java-driver from 3.10.1 to 3.10.2 in /core (#1374) Bumps [mongo-java-driver](https://github.com/mongodb/mongo-java-driver) from 3.10.1 to 3.10.2. - [Release notes](https://github.com/mongodb/mongo-java-driver/releases) - [Commits](https://github.com/mongodb/mongo-java-driver/compare/r3.10.1...r3.10.2) Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 112bd096193..1f08c0159b2 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -103,7 +103,7 @@ dependencies { testCompile 'org.apache.httpcomponents:httpclient:4.5.8' testCompile 'redis.clients:jedis:3.0.1' testCompile 'com.rabbitmq:amqp-client:5.7.0' - testCompile 'org.mongodb:mongo-java-driver:3.10.1' + testCompile 'org.mongodb:mongo-java-driver:3.10.2' testCompile ('org.mockito:mockito-core:2.26.0') { exclude(module: 'hamcrest-core') } From 3b9e61e2b69db264db4e3f18dce8194d4debd2cf Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 11 Apr 2019 17:51:28 +0200 Subject: [PATCH 0695/5615] Fix shading of modules (#1342, #1390) --- build.gradle | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index 75977bd540f..27f1d8859f1 100644 --- a/build.gradle +++ b/build.gradle @@ -56,16 +56,34 @@ subprojects { configurations = [] classifier = null - // Keep docker-java's package inside the final jar - relocate("com.github.dockerjava", "com.github.dockerjava") - } + doFirst { + // See https://github.com/johnrengelman/shadow/blob/5.0.0/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ConfigureShadowRelocation.groovy + Set packages = [] + // Always read from core's configurations + for (configuration in tasks.getByPath(":testcontainers:shadowJar").configurations) { + for (jar in configuration.files) { + def jf = new java.util.jar.JarFile(jar) + for (entry in jf.entries()) { + def name = entry.name + if (name.endsWith(".class")) { + packages.add(name.substring(0, name.lastIndexOf('/'))) + } + } + jf.close() + } + } + for (pkg in packages) { + pkg = pkg.replaceAll('/', '.') - task relocateShadowJar(type: com.github.jengelman.gradle.plugins.shadow.tasks.ConfigureShadowRelocation) { - target = tasks.shadowJar - prefix = "org.testcontainers.shaded" - } + if (pkg.startsWith("com.github.dockerjava.")) { + // Keep docker-java's package inside the final jar + continue; + } - tasks.shadowJar.dependsOn tasks.relocateShadowJar + tasks.shadowJar.relocate(pkg, "org.testcontainers.shaded.${pkg}") + } + } + } publishing { publications { From 7501244d0ee19038b5e9c3c10d8c6844b0ce0d89 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sat, 13 Apr 2019 21:47:45 +0100 Subject: [PATCH 0696/5615] Bump cucumber-java from 2.3.1 to 4.3.0 in /examples (#1394) * Bump cucumber-java from 2.3.1 to 4.3.0 in /examples Bumps [cucumber-java](https://github.com/cucumber/cucumber-jvm) from 2.3.1 to 4.3.0. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/master/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v2.3.1...v4.3.0) Signed-off-by: dependabot[bot] * Bump cucumber libs together for compatibility --- examples/cucumber/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index f8744b9d0e8..e9b052050d5 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' - implementation 'io.cucumber:cucumber-java:2.3.1' - testImplementation 'io.cucumber:cucumber-junit:2.3.1' + implementation 'io.cucumber:cucumber-java:4.3.0' + testImplementation 'io.cucumber:cucumber-junit:4.3.0' testImplementation 'org.testcontainers:selenium' } From e5b629eb668c103701b3fc4751b929c9b211f250 Mon Sep 17 00:00:00 2001 From: Gerasimov Gennady Date: Sat, 13 Apr 2019 23:50:05 +0300 Subject: [PATCH 0697/5615] Remove repeated call to toLowerCase() in ScriptUtils (#1397) Fixes #1396 --- .../java/org/testcontainers/ext/ScriptUtils.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java b/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java index 0f917a1561f..f27b3610f15 100644 --- a/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java +++ b/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java @@ -124,6 +124,7 @@ public static void splitSqlScript(String resource, String script, String separat Character currentLiteralDelimiter = null; int compoundStatementDepth = 0; + final String lowerCaseScriptContent = script.toLowerCase(); char[] content = script.toCharArray(); for (int i = 0; i < script.length(); i++) { char c = content[i]; @@ -148,24 +149,24 @@ public static void splitSqlScript(String resource, String script, String separat } final boolean inLiteral = currentLiteralDelimiter != null; - if (!inLiteral && containsSubstringAtOffset(script, commentPrefix, i)) { + if (!inLiteral && containsSubstringAtOffset(lowerCaseScriptContent, commentPrefix, i)) { inLineComment = true; } if (inLineComment && c == '\n') { inLineComment = false; } - if (!inLiteral && containsSubstringAtOffset(script, blockCommentStartDelimiter, i)) { + if (!inLiteral && containsSubstringAtOffset(lowerCaseScriptContent, blockCommentStartDelimiter, i)) { inBlockComment = true; } - if (!inLiteral && inBlockComment && containsSubstringAtOffset(script, blockCommentEndDelimiter, i)) { + if (!inLiteral && inBlockComment && containsSubstringAtOffset(lowerCaseScriptContent, blockCommentEndDelimiter, i)) { inBlockComment = false; } final boolean inComment = inLineComment || inBlockComment; - if (!inLiteral && !inComment && containsSubstringAtOffset(script, "BEGIN", i)) { + if (!inLiteral && !inComment && containsSubstringAtOffset(lowerCaseScriptContent, "BEGIN", i)) { compoundStatementDepth++; } - if (!inLiteral && !inComment && containsSubstringAtOffset(script, "END", i)) { + if (!inLiteral && !inComment && containsSubstringAtOffset(lowerCaseScriptContent, "END", i)) { compoundStatementDepth--; } final boolean inCompoundStatement = compoundStatementDepth != 0; @@ -222,8 +223,7 @@ else if (c == ' ' || c == '\n' || c == '\t') { } } - private static boolean containsSubstringAtOffset(String string, String substring, int offset) { - String lowercaseString = string.toLowerCase(); + private static boolean containsSubstringAtOffset(String lowercaseString, String substring, int offset) { String lowercaseSubstring = substring.toLowerCase(); return lowercaseString.startsWith(lowercaseSubstring, offset); From 92fc7fa78fc8eee94badd2d0df3e8a45809b9dfa Mon Sep 17 00:00:00 2001 From: sullis Date: Sun, 14 Apr 2019 06:46:00 -0400 Subject: [PATCH 0698/5615] Postgresql 9.6.8 -> 9.6.12 (#1350) --- .../java/org/testcontainers/containers/PostgreSQLContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index 13c5b49efc7..eb7b78b1864 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -15,7 +15,7 @@ public class PostgreSQLContainer> extends JdbcDatabaseContainer { public static final String NAME = "postgresql"; public static final String IMAGE = "postgres"; - public static final String DEFAULT_TAG = "9.6.8"; + public static final String DEFAULT_TAG = "9.6.12"; public static final Integer POSTGRESQL_PORT = 5432; private String databaseName = "test"; From 6f6fb8299aaa6e01b93a2d4ba89e50e1a16927d5 Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 15 Apr 2019 10:55:07 +0100 Subject: [PATCH 0699/5615] Attempt to make WaitAllStrategyTests more stable (#1400) @kiview WDYT? I was finding that these tests were failing randomly ~30% of the time, which is likely to be caused by the specific timing-sensitive aspects of the code. To be honest I also found the existing tests (the ones which exercised the timeouts behaviour) to be hard to follow, and therefore very hard to be sure that it would be both correct and stable. So, instead, I've updated the test: * to keep the existing mock-based tests to check _what the strategy does_ (agnostic of timeout durations). * to replace the timing-sensitive tests with some simpler tests that just check that the `startupTimeout`s get propagated properly. These tests _don't_ fire the `WaitAllStrategy`, i.e. don't actually wait at all. My thinking is that the combination of these two types of tests really should be enough. 'Integration testing' this would be nice, but is complicated and error prone, so testing the execution flow and the data flow, essentially, is a compromise that I'm happier with. --- .../wait/strategy/WaitAllStrategyTest.java | 161 ++++++------------ 1 file changed, 55 insertions(+), 106 deletions(-) diff --git a/core/src/test/java/org/testcontainers/containers/wait/strategy/WaitAllStrategyTest.java b/core/src/test/java/org/testcontainers/containers/wait/strategy/WaitAllStrategyTest.java index 3b2172d5bcf..897293282bd 100644 --- a/core/src/test/java/org/testcontainers/containers/wait/strategy/WaitAllStrategyTest.java +++ b/core/src/test/java/org/testcontainers/containers/wait/strategy/WaitAllStrategyTest.java @@ -1,14 +1,5 @@ package org.testcontainers.containers.wait.strategy; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; -import static org.rnorth.visibleassertions.VisibleAssertions.*; - -import java.time.Duration; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; - import org.junit.Before; import org.junit.Test; import org.mockito.InOrder; @@ -17,7 +8,12 @@ import org.rnorth.ducttape.TimeoutException; import org.testcontainers.containers.GenericContainer; -import com.google.common.util.concurrent.Uninterruptibles; +import java.time.Duration; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; +import static org.rnorth.visibleassertions.VisibleAssertions.*; public class WaitAllStrategyTest { @@ -35,80 +31,78 @@ public void setUp() { MockitoAnnotations.initMocks(this); } + /* + * Dummy-based tests, to check that timeout values are propagated correctly, without involving actual timing-sensitive code + */ @Test - public void simpleTest() { - - final WaitStrategy underTest = new WaitAllStrategy() - .withStrategy(strategy1) - .withStrategy(strategy2); - - doNothing().when(strategy1).waitUntilReady(eq(container)); - doNothing().when(strategy2).waitUntilReady(eq(container)); - - underTest.waitUntilReady(container); - - InOrder inOrder = inOrder(strategy1, strategy2); - inOrder.verify(strategy1).waitUntilReady(any()); - inOrder.verify(strategy2).waitUntilReady(any()); - } + public void parentTimeoutApplies() { - @Test - public void maxTimeOutApplies() { + DummyStrategy child1 = new DummyStrategy(Duration.ofMillis(10)); + child1.withStartupTimeout(Duration.ofMillis(20)); - WaitStrategy child1 = new SleepingStrategy(Duration.ofMinutes(30)) - .withStartupTimeout(Duration.ofHours(1)); + assertEquals("withStartupTimeout directly sets the timeout", 20L, child1.startupTimeout.toMillis()); - final WaitStrategy underTest = new WaitAllStrategy() + new WaitAllStrategy() .withStrategy(child1) - .withStartupTimeout(Duration.ofMillis(10)); + .withStartupTimeout(Duration.ofMillis(30)); - assertThrows("The outer strategy timeout applies", TimeoutException.class, () -> { - underTest.waitUntilReady(container); - }); + assertEquals("WaitAllStrategy overrides a child's timeout", 30L, child1.startupTimeout.toMillis()); } @Test - public void appliesOuterTimeoutTooLittleTime() { + public void parentTimeoutAppliesToMultipleChildren() { Duration defaultInnerWait = Duration.ofMillis(2); Duration outerWait = Duration.ofMillis(6); - WaitStrategy child1 = new SleepingStrategy(defaultInnerWait); - WaitStrategy child2 = new SleepingStrategy(defaultInnerWait); + DummyStrategy child1 = new DummyStrategy(defaultInnerWait); + DummyStrategy child2 = new DummyStrategy(defaultInnerWait); - final WaitStrategy underTest = new WaitAllStrategy() + new WaitAllStrategy() .withStrategy(child1) .withStrategy(child2) .withStartupTimeout(outerWait); - assertThrows("The outer strategy timeout applies", TimeoutException.class, () -> { - underTest.waitUntilReady(container); - }); + assertEquals("WaitAllStrategy overrides a child's timeout (1st)", 6L, child1.startupTimeout.toMillis()); + assertEquals("WaitAllStrategy overrides a child's timeout (2nd)", 6L, child2.startupTimeout.toMillis()); } @Test - public void appliesOuterTimeoutEnoughTime() { + public void parentTimeoutAppliesToAdditionalChildren() { Duration defaultInnerWait = Duration.ofMillis(2); Duration outerWait = Duration.ofMillis(20); - WaitStrategy child1 = new SleepingStrategy(defaultInnerWait); - WaitStrategy child2 = new SleepingStrategy(defaultInnerWait); + DummyStrategy child1 = new DummyStrategy(defaultInnerWait); + DummyStrategy child2 = new DummyStrategy(defaultInnerWait); - final WaitStrategy underTest = new WaitAllStrategy() + new WaitAllStrategy() .withStrategy(child1) - .withStrategy(child2) - .withStartupTimeout(outerWait); + .withStartupTimeout(outerWait) + .withStrategy(child2); - try { - underTest.waitUntilReady(container); - } catch (Exception e) { - if (e.getCause() instanceof CaughtTimeoutException) - fail("The timeout wasn't applied to inner strategies."); - else { - fail(e.getMessage()); - } - } + assertEquals("WaitAllStrategy overrides a child's timeout (1st)", 20L, child1.startupTimeout.toMillis()); + assertEquals("WaitAllStrategy overrides a child's timeout (2nd, additional)", 20L, child2.startupTimeout.toMillis()); + } + + /* + * Mock-based tests to check overall behaviour, without involving timing-sensitive code + */ + @Test + public void childExecutionTest() { + + final WaitStrategy underTest = new WaitAllStrategy() + .withStrategy(strategy1) + .withStrategy(strategy2); + + doNothing().when(strategy1).waitUntilReady(eq(container)); + doNothing().when(strategy2).waitUntilReady(eq(container)); + + underTest.waitUntilReady(container); + + InOrder inOrder = inOrder(strategy1, strategy2); + inOrder.verify(strategy1).waitUntilReady(any()); + inOrder.verify(strategy2).waitUntilReady(any()); } @Test @@ -145,7 +139,7 @@ public void timeoutChangeShouldNotBePossibleWithIndividualTimeoutMode() { @Test public void shouldNotMessWithIndividualTimeouts() { - final WaitStrategy underTest = new WaitAllStrategy(WaitAllStrategy.Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY) + new WaitAllStrategy(WaitAllStrategy.Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY) .withStrategy(strategy1) .withStrategy(strategy2); @@ -157,7 +151,7 @@ public void shouldNotMessWithIndividualTimeouts() { public void shouldOverwriteIndividualTimeouts() { Duration someSeconds = Duration.ofSeconds(23); - final WaitStrategy underTest = new WaitAllStrategy() + new WaitAllStrategy() .withStartupTimeout(someSeconds) .withStrategy(strategy1) .withStrategy(strategy2); @@ -166,59 +160,14 @@ public void shouldOverwriteIndividualTimeouts() { verify(strategy1).withStartupTimeout(someSeconds); } - @Test - public void appliesOuterTimeoutOnAdditionalChildren() { - - Duration defaultInnerWait = Duration.ofMillis(2); - Duration outerWait = Duration.ofMillis(20); - - WaitStrategy child1 = new SleepingStrategy(defaultInnerWait); - WaitStrategy child2 = new SleepingStrategy(defaultInnerWait); - - final WaitStrategy underTest = new WaitAllStrategy() - .withStrategy(child1) - .withStartupTimeout(outerWait) - .withStrategy(child2); - - try { - underTest.waitUntilReady(container); - } catch (Exception e) { - if (e.getCause() instanceof CaughtTimeoutException) - fail("The timeout wasn't applied to inner strategies."); - else { - fail(e.getMessage()); - } - } - } - - static class CaughtTimeoutException extends RuntimeException { - - CaughtTimeoutException(String message) { - super(message); - } - } - - static class SleepingStrategy extends AbstractWaitStrategy { - - private final long sleepingDuration; - - SleepingStrategy(Duration defaultInnerWait) { + static class DummyStrategy extends AbstractWaitStrategy { + DummyStrategy(Duration defaultInnerWait) { super.startupTimeout = defaultInnerWait; - // Always oversleep by default - this.sleepingDuration = defaultInnerWait.multipliedBy(2).toMillis(); } @Override protected void waitUntilReady() { - try { - // Don't use ducttape, make sure we don't catch the wrong TimeoutException later on. - CompletableFuture.runAsync( - () -> Uninterruptibles.sleepUninterruptibly(this.sleepingDuration, TimeUnit.MILLISECONDS) - ).get((int) startupTimeout.toMillis(), TimeUnit.MILLISECONDS); - } catch (InterruptedException | ExecutionException e) { - } catch (java.util.concurrent.TimeoutException e) { - throw new CaughtTimeoutException("Inner wait timed out, outer strategy didn't apply."); - } + // no-op } } } From 978d374ceb38e6fa1ea677db27b4ac0e976463ce Mon Sep 17 00:00:00 2001 From: Gerasimov Gennady Date: Mon, 15 Apr 2019 13:17:06 +0300 Subject: [PATCH 0700/5615] #1389 Override close method without throws statement (#1392) --- core/src/main/java/org/testcontainers/containers/Network.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/main/java/org/testcontainers/containers/Network.java b/core/src/main/java/org/testcontainers/containers/Network.java index b71a74a455c..411459b62eb 100644 --- a/core/src/main/java/org/testcontainers/containers/Network.java +++ b/core/src/main/java/org/testcontainers/containers/Network.java @@ -28,6 +28,9 @@ public void close() { String getId(); + @Override + void close(); + static Network newNetwork() { return builder().build(); } From ed0db162c445169431373989ff3bd0cf62f9f02f Mon Sep 17 00:00:00 2001 From: Jeremy Comte Date: Mon, 15 Apr 2019 13:29:29 +0200 Subject: [PATCH 0701/5615] Make StartupCheckStrategy timeout value configurable (#1308) in case of big docker images (Elasticsearch 6.6.0 is 808MB for example) the hardcoded 30s timeout makes a lot of our builds fail the first time they run. We fixed this locally, but it would be nice to have it as a parameter on the parent class. --- .../containers/startupcheck/StartupCheckStrategy.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/startupcheck/StartupCheckStrategy.java b/core/src/main/java/org/testcontainers/containers/startupcheck/StartupCheckStrategy.java index f592411b4fb..1425123473b 100644 --- a/core/src/main/java/org/testcontainers/containers/startupcheck/StartupCheckStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/startupcheck/StartupCheckStrategy.java @@ -6,6 +6,7 @@ import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; import org.rnorth.ducttape.unreliables.Unreliables; +import java.time.Duration; import java.util.concurrent.TimeUnit; import static org.testcontainers.containers.GenericContainer.CONTAINER_RUNNING_TIMEOUT_SEC; @@ -21,9 +22,17 @@ public abstract class StartupCheckStrategy { .withConstantThroughput() .build(); + private Duration timeout = Duration.ofSeconds(CONTAINER_RUNNING_TIMEOUT_SEC); + + @SuppressWarnings("unchecked") + public SELF withTimeout(Duration timeout) { + this.timeout = timeout; + return (SELF) this; + } + public boolean waitUntilStartupSuccessful(DockerClient dockerClient, String containerId) { final Boolean[] startedOK = {null}; - Unreliables.retryUntilTrue(CONTAINER_RUNNING_TIMEOUT_SEC, TimeUnit.SECONDS, () -> { + Unreliables.retryUntilTrue((int) timeout.toMillis(), TimeUnit.MILLISECONDS, () -> { //noinspection CodeBlock2Expr return DOCKER_CLIENT_RATE_LIMITER.getWhenReady(() -> { StartupStatus state = checkStartupState(dockerClient, containerId); From 999d4f7d5ea56f05d132178ba9a989dcd9965a8d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 15 Apr 2019 12:49:34 +0100 Subject: [PATCH 0702/5615] Bump tomcat-jdbc from 9.0.17 to 9.0.19 in /modules/mysql (#1401) Bumps tomcat-jdbc from 9.0.17 to 9.0.19. [![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=org.apache.tomcat:tomcat-jdbc&package-manager=gradle&previous-version=9.0.17&new-version=9.0.19)](https://dependabot.com/compatibility-score.html?dependency-name=org.apache.tomcat:tomcat-jdbc&package-manager=gradle&previous-version=9.0.17&new-version=9.0.19) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) If all status checks pass Dependabot will automatically merge this pull request. [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot ignore this [patch|minor|major] version` will close this PR and stop Dependabot creating any more for this minor/major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) - `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme Additionally, you can set the following in the `.dependabot/config.yml` file in this repo: - Update frequency (including time of day and day of week) - Automerge options (never/patch/minor, and dev/runtime dependencies) - Pull request limits (per update run and/or open at any time) - Out-of-range updates (receive only lockfile updates, if desired) - Security updates (receive only security updates, if desired) Finally, you can contact us by mentioning @dependabot.
--- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 0703b29ad3a..00217f4ff41 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -5,7 +5,7 @@ dependencies { testCompile 'mysql:mysql-connector-java:8.0.14' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.17' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.19' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.vibur:vibur-dbcp:22.2' } From cc09697878dd11e21148972a54c8c287a99d2045 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 15 Apr 2019 12:49:58 +0100 Subject: [PATCH 0703/5615] Bump tomcat-jdbc from 9.0.17 to 9.0.19 in /modules/mariadb (#1403) Bumps tomcat-jdbc from 9.0.17 to 9.0.19. Signed-off-by: dependabot[bot] --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 1f679b7a592..84c5ce667d4 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -6,6 +6,6 @@ dependencies { testCompile 'org.mariadb.jdbc:mariadb-java-client:2.4.1' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.17' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.19' testCompile 'org.vibur:vibur-dbcp:22.2' } From 40724a0abbaa04b60e0231c7fa2616564f058430 Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 15 Apr 2019 21:00:07 +0100 Subject: [PATCH 0704/5615] Fix #1391 by using a tagged version of `sshd` (#1407) Workaround for #1406 --- .../org/testcontainers/utility/TestcontainersConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 2d478b4cc0a..f87b02a17d3 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -55,7 +55,7 @@ public String getRyukImage() { } public String getSSHdImage() { - return (String) properties.getOrDefault("sshd.container.image", "quay.io/testcontainers/sshd@sha256:18aa929c653284189fc9cefa45b731021857b6047a0a1757e909f958f258f088"); + return (String) properties.getOrDefault("sshd.container.image", "quay.io/testcontainers/sshd:1.0.0"); } public Integer getRyukTimeout() { From 57a154b46ac3a2804341272ed06e89aebfce8f2e Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 16 Apr 2019 07:01:45 +0100 Subject: [PATCH 0705/5615] Update latest version in docs --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index fc37c0be80a..3bdf1466e47 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -77,4 +77,4 @@ nav: - contributing.md - contributing_docs.md extra: - latest_version: 1.11.1 + latest_version: 1.11.2 From 2c3b36ad43f20a1e43015ac270b5318f2d45ca41 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 16 Apr 2019 07:34:17 +0100 Subject: [PATCH 0706/5615] Configure boomper probot (#1409) Configure [boomper](https://github.com/toolmantim/boomper) probot to keep latest version number in docs in sync with releases --- .github/boomper.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .github/boomper.yml diff --git a/.github/boomper.yml b/.github/boomper.yml new file mode 100644 index 00000000000..bb9bca7d29a --- /dev/null +++ b/.github/boomper.yml @@ -0,0 +1,3 @@ +updates: +- path: mkdocs.yml + pattern: 'latest_version: (.*)' From d9362fcbfdbf302fb41454132b65d54fc288c36a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Fri, 26 Apr 2019 09:28:40 +0200 Subject: [PATCH 0707/5615] Bump mysql-connector-java from 8.0.15 to 8.0.16 in /modules/spock (#1429) Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.15 to 8.0.16. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.15...8.0.16) Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index d9c482b0c7b..6076788dbc4 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -14,5 +14,5 @@ dependencies { testCompile 'org.apache.httpcomponents:httpclient:4.5.8' testRuntime 'org.postgresql:postgresql:42.2.5' - testRuntime 'mysql:mysql-connector-java:8.0.15' + testRuntime 'mysql:mysql-connector-java:8.0.16' } From f604a2f9c97b3fb28dda109c7a9493150faceba2 Mon Sep 17 00:00:00 2001 From: Gerasimov Gennady Date: Sat, 27 Apr 2019 23:09:58 +0300 Subject: [PATCH 0708/5615] Support SELF typing for OracleContainer (#1425) * PR #1292 Added waitUntilContainerStarted and adjusted OracleContainer class declaration * PR #1292 Do not override waitUntilContainerStarted method. Changed class declaration according. --- .../java/org/testcontainers/containers/OracleContainer.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java index d7410373fd9..ea0eee1a2ff 100644 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java @@ -7,7 +7,7 @@ /** * @author gusohal */ -public class OracleContainer extends JdbcDatabaseContainer { +public class OracleContainer extends JdbcDatabaseContainer { public static final String NAME = "oracle"; @@ -81,13 +81,13 @@ public String getPassword() { @Override public OracleContainer withUsername(String username) { this.username = username; - return this; + return self(); } @Override public OracleContainer withPassword(String password) { this.password = password; - return this; + return self(); } @SuppressWarnings("SameReturnValue") From c02ec79e9ad077bd03ec85f108e625531ac2ef40 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sat, 27 Apr 2019 21:37:34 +0100 Subject: [PATCH 0709/5615] Bump mysql-connector-java from 8.0.15 to 8.0.16 in /modules/junit-jupiter (#1428) Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.15 to 8.0.16. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.15...8.0.16) Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 36b0f217cb4..5d3ff61d46d 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -11,7 +11,7 @@ dependencies { testCompile 'org.apache.httpcomponents:httpclient:4.5.8' testRuntime 'org.postgresql:postgresql:42.2.5' - testRuntime 'mysql:mysql-connector-java:8.0.15' + testRuntime 'mysql:mysql-connector-java:8.0.16' testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.4.2' } From 3d2a7ed936cf467fc07189753746fd0ea7bcf31e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sat, 27 Apr 2019 21:37:56 +0100 Subject: [PATCH 0710/5615] Bump mysql-connector-java from 8.0.14 to 8.0.16 in /modules/mysql (#1427) Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.14 to 8.0.16. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.14...8.0.16) Signed-off-by: dependabot[bot] --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 00217f4ff41..4bc4640b0fc 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: JDBC :: MySQL" dependencies { compile project(':jdbc') - testCompile 'mysql:mysql-connector-java:8.0.14' + testCompile 'mysql:mysql-connector-java:8.0.16' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.19' testCompile 'commons-dbutils:commons-dbutils:1.7' From a727b0bfb98472418bc251d76ddc532095427c1c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sat, 27 Apr 2019 21:38:37 +0100 Subject: [PATCH 0711/5615] Bump neo4j from 3.5.4 to 3.5.5 in /modules/neo4j (#1426) Bumps [neo4j](https://github.com/neo4j/neo4j) from 3.5.4 to 3.5.5. - [Release notes](https://github.com/neo4j/neo4j/releases) - [Commits](https://github.com/neo4j/neo4j/commits) Signed-off-by: dependabot[bot] --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 9b0f3a48c3b..2a6f5ea1e1e 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -25,7 +25,7 @@ task customNeo4jPluginJar(type: Jar) { test.dependsOn customNeo4jPluginJar dependencies { - customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.4" + customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.5" compile project(":testcontainers") From e83136f53ea0835e462ed60b21a10e9aec0c5b89 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sat, 27 Apr 2019 21:38:51 +0100 Subject: [PATCH 0712/5615] Bump mockito-core from 2.26.0 to 2.27.0 in /core (#1382) Bumps [mockito-core](https://github.com/mockito/mockito) from 2.26.0 to 2.27.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v2.26.0...v2.27.0) Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 1f08c0159b2..d9dee98ee6b 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -104,7 +104,7 @@ dependencies { testCompile 'redis.clients:jedis:3.0.1' testCompile 'com.rabbitmq:amqp-client:5.7.0' testCompile 'org.mongodb:mongo-java-driver:3.10.2' - testCompile ('org.mockito:mockito-core:2.26.0') { + testCompile ('org.mockito:mockito-core:2.27.0') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From 7d299acd0087ba8f756d56c44e53aad1e3db3722 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sat, 27 Apr 2019 21:39:15 +0100 Subject: [PATCH 0713/5615] Bump pulsar-client from 2.3.0 to 2.3.1 in /modules/pulsar (#1402) Bumps [pulsar-client](https://github.com/apache/pulsar) from 2.3.0 to 2.3.1. - [Release notes](https://github.com/apache/pulsar/releases) - [Commits](https://github.com/apache/pulsar/compare/v2.3.0...v2.3.1) Signed-off-by: dependabot[bot] --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index a67c83cc923..c3505dedf65 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Pulsar" dependencies { compile project(':testcontainers') - testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.3.0' + testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.3.1' testCompile group: 'org.assertj', name: 'assertj-core', version: '3.12.2' } From ff9f0c443e8cb5c6bd37319cc5e5142549eb2355 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sat, 27 Apr 2019 21:39:59 +0100 Subject: [PATCH 0714/5615] Bump junit-jupiter-api from 5.4.1 to 5.4.2 in /modules/junit-jupiter (#1365) Bumps [junit-jupiter-api](https://github.com/junit-team/junit5) from 5.4.1 to 5.4.2. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.4.1...r5.4.2) Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 5d3ff61d46d..591347ca699 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -2,7 +2,7 @@ description = "Testcontainers :: JUnit Jupiter Extension" dependencies { compile project(':testcontainers') - compile 'org.junit.jupiter:junit-jupiter-api:5.4.1' + compile 'org.junit.jupiter:junit-jupiter-api:5.4.2' testCompile project(':mysql') testCompile project(':postgresql') From ea2c25c4c407582f4a0553a3f46bbcfb36cbc929 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Wed, 1 May 2019 10:50:51 +0200 Subject: [PATCH 0715/5615] Bump org.springframework.boot in /examples (#1443) Bumps org.springframework.boot from 2.1.2.RELEASE to 2.1.4.RELEASE. Signed-off-by: dependabot[bot] --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index 884657194cb..c978a96082d 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.1.2.RELEASE' + id 'org.springframework.boot' version '2.1.4.RELEASE' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index 5a5ad6d893a..926d1cd8eb6 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.1.2.RELEASE' + id 'org.springframework.boot' version '2.1.4.RELEASE' } apply plugin: 'io.spring.dependency-management' From 51333a64b596260845c230e15ef038a367833cd2 Mon Sep 17 00:00:00 2001 From: mschnitzler Date: Tue, 7 May 2019 14:24:09 +0200 Subject: [PATCH 0716/5615] Add documentation for Bitbucket Pipelines (#1439) --- .../bitbucket_pipelines.md | 26 +++++++++++++++++++ mkdocs.yml | 1 + 2 files changed, 27 insertions(+) create mode 100644 docs/supported_docker_environment/continuous_integration/bitbucket_pipelines.md diff --git a/docs/supported_docker_environment/continuous_integration/bitbucket_pipelines.md b/docs/supported_docker_environment/continuous_integration/bitbucket_pipelines.md new file mode 100644 index 00000000000..0fd76448ffc --- /dev/null +++ b/docs/supported_docker_environment/continuous_integration/bitbucket_pipelines.md @@ -0,0 +1,26 @@ +# Bitbucket Pipelines + +To enable access to Docker in Bitbucket Pipelines, you need to add `docker` as a service on the step. + +Furthermore, Ryuk needs to be turned off since Bitbucket Pipelines does not allow starting privileged containers (see [Disabling Ryuk](../../features/configuration.md#disabling-ryuk)). This can either be done by setting a repository variable in Bitbucket's project settings or by explicitly exporting the variable on a step. + +In some cases the memory available to Docker needs to be increased. + +Here is a sample Bitbucket Pipeline configuration that does a checkout of a project and runs maven: + +```yml +image: maven:3.6.1 + +pipelines: + default: + - step: + script: + - export TESTCONTAINERS_RYUK_DISABLED=true + - mvn clean install + services: + - docker +definitions: + services: + docker: + memory: 2048 +``` diff --git a/mkdocs.yml b/mkdocs.yml index 3bdf1466e47..2238c7573a8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -70,6 +70,7 @@ nav: - supported_docker_environment/continuous_integration/dind_patterns.md - supported_docker_environment/continuous_integration/circle_ci.md - supported_docker_environment/continuous_integration/gitlab_ci.md + - supported_docker_environment/continuous_integration/bitbucket_pipelines.md - supported_docker_environment/windows.md - supported_docker_environment/logging_config.md - Getting help: getting_help.md From 55f10a704802b1fe8288d911d85ef272d44580e9 Mon Sep 17 00:00:00 2001 From: Enno <458526+ennru@users.noreply.github.com> Date: Tue, 14 May 2019 12:04:09 +0200 Subject: [PATCH 0717/5615] Add Lightbend to the Who is using list (#1464) --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index dc038d374be..cbee344e637 100644 --- a/docs/index.md +++ b/docs/index.md @@ -70,6 +70,7 @@ You can also [check the latest version available on Maven Central](https://searc * [eBay Marketing](https://www.ebay.com) - Testing for MySQL, Cassandra, Redis, Couchbase, Kafka, etc. * [Skyscanner](https://www.skyscanner.net/) - Integration testing against HTTP service mocks and various data stores * [Neo4j-OGM](https://neo4j.com/developer/neo4j-ogm/) - Testing new, reactive client implementations +* [Lightbend](https://www.lightbend.com/) - Testing [Alpakka Kafka](https://doc.akka.io/docs/alpakka-kafka/current/) and support in [Alpakka Kafka Testkit](https://doc.akka.io/docs/alpakka-kafka/current/testing.html#testing-with-kafka-in-docker) ## License From 4947ec4a9b12daf3709cb9dc4d1447fe3be3caa9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Wed, 15 May 2019 09:34:10 +0100 Subject: [PATCH 0718/5615] Bump cucumber-java from 4.3.0 to 4.3.1 in /examples (#1463) Bumps [cucumber-java](https://github.com/cucumber/cucumber-jvm) from 4.3.0 to 4.3.1. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/master/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v4.3.0...v4.3.1) Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index e9b052050d5..97d56deeb05 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' - implementation 'io.cucumber:cucumber-java:4.3.0' + implementation 'io.cucumber:cucumber-java:4.3.1' testImplementation 'io.cucumber:cucumber-junit:4.3.0' testImplementation 'org.testcontainers:selenium' } From e930b59a2ac28caa6a82890781a2487eea6e9beb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Wed, 15 May 2019 09:34:26 +0100 Subject: [PATCH 0719/5615] Bump lombok from 1.18.6 to 1.18.8 in /examples (#1462) Bumps [lombok](https://github.com/rzwitserloot/lombok) from 1.18.6 to 1.18.8. - [Release notes](https://github.com/rzwitserloot/lombok/releases) - [Changelog](https://github.com/rzwitserloot/lombok/blob/master/doc/changelog.markdown) - [Commits](https://github.com/rzwitserloot/lombok/compare/v1.18.6...v1.18.8) Signed-off-by: dependabot[bot] --- examples/disque-job-queue/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index 77b6022a968..78a38d2ba08 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.projectlombok:lombok:1.18.6' + compileOnly 'org.projectlombok:lombok:1.18.8' implementation 'biz.paluch.redis:spinach:0.3' implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.google.guava:guava:23.0' diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index 926d1cd8eb6..ee2b0d032d6 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -9,7 +9,7 @@ repositories { } dependencies { - compileOnly 'org.projectlombok:lombok:1.18.6' + compileOnly 'org.projectlombok:lombok:1.18.8' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-data-redis' implementation 'org.springframework.boot:spring-boot-starter-web' From b7bbbfdf504f74e0c43b296ec4beb9e45e79ac97 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Wed, 15 May 2019 09:34:56 +0100 Subject: [PATCH 0720/5615] Bump rest-assured from 3.3.0 to 4.0.0 in /modules/vault (#1460) Bumps [rest-assured](https://github.com/jayway/rest-assured) from 3.3.0 to 4.0.0. - [Release notes](https://github.com/jayway/rest-assured/releases) - [Changelog](https://github.com/rest-assured/rest-assured/blob/master/changelog.txt) - [Commits](https://github.com/jayway/rest-assured/compare/rest-assured-3.3.0...rest-assured-4.0.0) Signed-off-by: dependabot[bot] --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index d5c791acd32..3dd346ab559 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: Vault" dependencies { compile project(':testcontainers') - testCompile 'io.rest-assured:rest-assured:3.3.0' + testCompile 'io.rest-assured:rest-assured:4.0.0' } From 5acb599797d809d72836499140fe79214958cf99 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Wed, 15 May 2019 10:35:27 +0100 Subject: [PATCH 0721/5615] Bump neo4j-java-driver from 1.7.3 to 1.7.4 in /modules/neo4j (#1451) Bumps [neo4j-java-driver](https://github.com/neo4j/neo4j-java-driver) from 1.7.3 to 1.7.4. - [Release notes](https://github.com/neo4j/neo4j-java-driver/releases) - [Commits](https://github.com/neo4j/neo4j-java-driver/compare/1.7.3...1.7.4) Signed-off-by: dependabot[bot] --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 2a6f5ea1e1e..c467cc7381f 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -29,6 +29,6 @@ dependencies { compile project(":testcontainers") - testCompile "org.neo4j.driver:neo4j-java-driver:1.7.3" + testCompile "org.neo4j.driver:neo4j-java-driver:1.7.4" testCompile 'org.assertj:assertj-core:3.12.2' } From 2f1bce93926cc7827a3bb811167cfc4a7eade699 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Wed, 15 May 2019 10:35:49 +0100 Subject: [PATCH 0722/5615] Bump tomcat-jdbc from 9.0.19 to 9.0.20 in /modules/mysql (#1468) Bumps tomcat-jdbc from 9.0.19 to 9.0.20. Signed-off-by: dependabot[bot] --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 4bc4640b0fc..e48c71734ae 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -5,7 +5,7 @@ dependencies { testCompile 'mysql:mysql-connector-java:8.0.16' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.19' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.20' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.vibur:vibur-dbcp:22.2' } From 88014b3d46fd65c13ca366635f62fb6fb679cb0d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Wed, 15 May 2019 21:21:36 +0100 Subject: [PATCH 0723/5615] Bump cucumber-junit from 4.3.0 to 4.3.1 in /examples (#1461) Bumps [cucumber-junit](https://github.com/cucumber/cucumber-jvm) from 4.3.0 to 4.3.1. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/master/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v4.3.0...v4.3.1) Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 97d56deeb05..bab77700eff 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' implementation 'io.cucumber:cucumber-java:4.3.1' - testImplementation 'io.cucumber:cucumber-junit:4.3.0' + testImplementation 'io.cucumber:cucumber-junit:4.3.1' testImplementation 'org.testcontainers:selenium' } From 0a66f3cde6aaea232f9ece858caca2e89f5581fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Thu, 16 May 2019 09:05:39 +0200 Subject: [PATCH 0724/5615] Bump org.springframework.boot in /examples (#1471) Bumps org.springframework.boot from 2.1.4.RELEASE to 2.1.5.RELEASE. Signed-off-by: dependabot[bot] --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index c978a96082d..0aa55093dca 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.1.4.RELEASE' + id 'org.springframework.boot' version '2.1.5.RELEASE' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index ee2b0d032d6..f45b0e51c11 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.1.4.RELEASE' + id 'org.springframework.boot' version '2.1.5.RELEASE' } apply plugin: 'io.spring.dependency-management' From 03f8caa3a9dabc6dedd7caa4541a235b1f73a0a3 Mon Sep 17 00:00:00 2001 From: Stefan Ludwig Date: Thu, 16 May 2019 10:01:59 +0200 Subject: [PATCH 0725/5615] Add example for MongoDB containers (#1288) --- examples/mongodb-container/build.gradle | 12 +++++ .../containers/MongoDbContainer.java | 53 +++++++++++++++++++ .../containers/MongoDbContainerTest.java | 27 ++++++++++ examples/settings.gradle | 1 + 4 files changed, 93 insertions(+) create mode 100644 examples/mongodb-container/build.gradle create mode 100644 examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainer.java create mode 100644 examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java diff --git a/examples/mongodb-container/build.gradle b/examples/mongodb-container/build.gradle new file mode 100644 index 00000000000..11c5f2bbe24 --- /dev/null +++ b/examples/mongodb-container/build.gradle @@ -0,0 +1,12 @@ +plugins { + id 'java' +} + +repositories { + jcenter() +} + +dependencies { + testImplementation 'org.testcontainers:testcontainers' + testImplementation 'junit:junit:4.12' +} diff --git a/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainer.java b/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainer.java new file mode 100644 index 00000000000..00fd9c05b52 --- /dev/null +++ b/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainer.java @@ -0,0 +1,53 @@ +package org.testcontainers.containers; + +import org.jetbrains.annotations.NotNull; + + +/** + * This {@link Container} is based on the official MongoDb ({@code mongo}) image from + * DockerHub. If you need to use a custom MongoDB + * image, you can provide the full image name as well. + * + * @author Stefan Ludwig + */ +public class MongoDbContainer extends GenericContainer { + + /** + * This is the internal port on which MongoDB is running inside the container. + *

+ * You can use this constant in case you want to map an explicit public port to it + * instead of the default random port. This can be done using methods like + * {@link #setPortBindings(java.util.List)}. + */ + public static final int MONGODB_PORT = 27017; + public static final String DEFAULT_IMAGE_AND_TAG = "mongo:4.0"; + + /** + * Creates a new {@link MongoDbContainer} with the {@value DEFAULT_IMAGE_AND_TAG} image. + */ + public MongoDbContainer() { + this(DEFAULT_IMAGE_AND_TAG); + } + + /** + * Creates a new {@link MongoDbContainer} with the given {@code 'image'}. + * + * @param image the image (e.g. {@value DEFAULT_IMAGE_AND_TAG}) to use + */ + public MongoDbContainer(@NotNull String image) { + super(image); + addExposedPort(MONGODB_PORT); + } + + /** + * Returns the actual public port of the internal MongoDB port ({@value MONGODB_PORT}). + * + * @return the public port of this container + * @see #getMappedPort(int) + */ + @NotNull + public Integer getPort() { + return getMappedPort(MONGODB_PORT); + } + +} diff --git a/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java b/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java new file mode 100644 index 00000000000..96096d51c1a --- /dev/null +++ b/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java @@ -0,0 +1,27 @@ +package org.testcontainers.containers; + +import java.io.IOException; +import java.net.Socket; + +import org.junit.Test; + + +public class MongoDbContainerTest { + + @Test + public void containerStartsAndPublicPortIsAvailable() { + try (MongoDbContainer container = new MongoDbContainer()) { + container.start(); + assertThatPortIsAvailable(container); + } + } + + private void assertThatPortIsAvailable(MongoDbContainer container) { + try { + new Socket(container.getContainerIpAddress(), container.getPort()); + } catch (IOException e) { + throw new AssertionError("The expected port " + container.getPort() + " is not available!"); + } + } + +} diff --git a/examples/settings.gradle b/examples/settings.gradle index 671646b76d6..06252afa580 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -5,6 +5,7 @@ includeBuild '..' // explicit include to allow Dependabot to autodiscover subprojects include 'disque-job-queue' include 'linked-container' +include 'mongodb-container' include 'redis-backed-cache' include 'redis-backed-cache-testng' include 'selenium-container' From e4ce8f578d09a29c3dbddc9be47a92ee6f06d9cd Mon Sep 17 00:00:00 2001 From: Patrick Pfeiler Date: Sat, 18 May 2019 23:07:47 +0200 Subject: [PATCH 0726/5615] Fix ScriptUtils splitting with Windows EOF (#1467, #1465) --- .../org/testcontainers/ext/ScriptUtils.java | 2 +- .../org/testcontainers/ext/ScriptUtilsTest.java | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java b/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java index f27b3610f15..eb041708fb6 100644 --- a/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java +++ b/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java @@ -206,7 +206,7 @@ else if (script.startsWith(blockCommentStartDelimiter, i)) { blockCommentEndDelimiter), resource); } } - else if (c == ' ' || c == '\n' || c == '\t') { + else if (c == ' ' || c == '\n' || c == '\t' || c == '\r') { // avoid multiple adjacent whitespace characters if (sb.length() > 0 && sb.charAt(sb.length() - 1) != ' ') { c = ' '; diff --git a/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptUtilsTest.java b/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptUtilsTest.java index 7a50026cd93..c9b1218321b 100644 --- a/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptUtilsTest.java +++ b/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptUtilsTest.java @@ -28,4 +28,21 @@ public void testSplit() throws IOException { assertEquals("SELECT * from `bar`", statements.get(4)); assertEquals("INSERT INTO bar (foo) VALUES ('hello world')", statements.get(6)); } + + /* + * Test ScriptUtils script splitting with some ugly/hard-to-split cases and linux line endings + */ + @Test + public void testSplitWithWidnwosLineEnding() throws IOException { + final String script = Resources.toString(Resources.getResource("splittable.sql"), Charsets.UTF_8); + final String scriptWithWindowsLineEndings = script.replaceAll("\n", "\r\n"); + final List statements = new ArrayList<>(); + ScriptUtils.splitSqlScript("resourcename", scriptWithWindowsLineEndings, ";", "--", "/*", "*/", statements); + + assertEquals(7, statements.size()); + assertEquals("SELECT \"a /* string literal containing comment characters like -- here\"", statements.get(2)); + assertEquals("SELECT \"a 'quoting' \\\"scenario ` involving BEGIN keyword\\\" here\"", statements.get(3)); + assertEquals("SELECT * from `bar`", statements.get(4)); + assertEquals("INSERT INTO bar (foo) VALUES ('hello world')", statements.get(6)); + } } From 2dffb2648e2d9fbfcb7892e60cf6dbe8f1151d46 Mon Sep 17 00:00:00 2001 From: Johnny Lim Date: Tue, 21 May 2019 17:00:23 +0900 Subject: [PATCH 0727/5615] Fix NB in the "Database containers" docs (#1484) --- docs/modules/databases/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/modules/databases/index.md b/docs/modules/databases/index.md index aaef47a44ef..c7253cdddd7 100644 --- a/docs/modules/databases/index.md +++ b/docs/modules/databases/index.md @@ -43,6 +43,7 @@ Examples/Tests: As long as you have Testcontainers and the appropriate JDBC driver on your classpath, you can simply modify regular JDBC connection URLs to get a fresh containerized instance of the database each time your application starts up. _N.B:_ + * _TC needs to be on your application's classpath at runtime for this to work_ * _For Spring Boot you need to specify the driver manually `spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver`_ From 24e6f1154f371ebe1623e9621726ce2d019b23d5 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Tue, 21 May 2019 17:53:44 +0200 Subject: [PATCH 0728/5615] Redirect Docker-Compose stderr to INFO logger (#1485) Docker-Compose logs pull information to stderr, but we don't want those logs in our ERROR logger. --- .../org/testcontainers/containers/DockerComposeContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 0b6db19ca28..67674796cdd 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -638,7 +638,7 @@ public void invoke() { try { new ProcessExecutor().command(command) .redirectOutput(Slf4jStream.of(logger()).asInfo()) - .redirectError(Slf4jStream.of(logger()).asError()) + .redirectError(Slf4jStream.of(logger()).asInfo()) // docker-compose will log pull information to stderr .environment(environment) .directory(pwd) .exitValueNormal() From b75f52c2412b39cc166358622d1fe4c330c26812 Mon Sep 17 00:00:00 2001 From: Anand Date: Tue, 21 May 2019 21:10:09 +0100 Subject: [PATCH 0729/5615] Adding field name to ExtensionConfigurationException message (#1488) --- CONTRIBUTING.md | 8 ++++++-- .../junit/jupiter/TestcontainersExtension.java | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cd3822df3a2..2914c874e1a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,5 +8,9 @@ * discuss with the authors on an issue ticket prior to doing anything big * follow the style, naming and structure conventions of the rest of the project * make commits atomic and easy to merge - * verify all tests are passing. Build the project with `./gradlew check` to do this. - **N.B.** Gradle's Build Cache is enabled by default, but you can add `--no-build-cache` flag to disable it. \ No newline at end of file + * verify all tests are passing. Build the project with `./gradlew check` to do this + + **PS:** Make sure Docker is running locally and you have the lombok plugin installed in your IDE + + **N.B.** Gradle's Build Cache is enabled by default, but you can add `--no-build-cache` flag to disable it. + diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java index 7c5f8b205ef..7f6183cc74d 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java @@ -95,7 +95,7 @@ private static Predicate isContainer() { boolean isStartable = Startable.class.isAssignableFrom(field.getType()); if (!isStartable) { - throw new ExtensionConfigurationException("Annotation is only supported for Startable types"); + throw new ExtensionConfigurationException(String.format("FieldName: %s does not implement Startable", field.getName())); } return true; } From 581b80b184e2d0a0f65e4f12f8493565adb2f60a Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 27 May 2019 20:55:43 +0100 Subject: [PATCH 0730/5615] Support MySQL 8 (#1470) Amend default MySQL connection parameters for MySQL 5 - 8 compatibility Fixes #736 --- modules/jdbc-test/build.gradle | 2 +- .../testcontainers/jdbc/JDBCDriverTest.java | 4 +- .../junit/AbstractContainerDatabaseTest.java | 32 +++++++++++ .../junit/MultiVersionMySQLTest.java | 37 ++++++++++++ .../junit/SimpleClickhouseTest.java | 15 +---- .../junit/SimpleMSSQLServerTest.java | 23 ++------ .../junit/SimpleMariaDBTest.java | 25 +------- .../testcontainers/junit/SimpleMySQLTest.java | 57 +++---------------- .../junit/SimpleOracleTest.java | 16 +----- .../junit/SimplePostgreSQLTest.java | 20 +------ .../containers/MySQLContainer.java | 10 +++- 11 files changed, 99 insertions(+), 142 deletions(-) create mode 100644 modules/jdbc-test/src/test/java/org/testcontainers/junit/AbstractContainerDatabaseTest.java create mode 100644 modules/jdbc-test/src/test/java/org/testcontainers/junit/MultiVersionMySQLTest.java diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index e631d2fa1a3..4c7ab8b0974 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -14,7 +14,7 @@ dependencies { testCompile 'com.google.guava:guava:18.0' testCompile 'org.postgresql:postgresql:42.0.0' - testCompile 'mysql:mysql-connector-java:5.1.45' + testCompile 'mysql:mysql-connector-java:8.0.14' testCompile 'org.mariadb.jdbc:mariadb-java-client:1.4.6' testCompile 'com.oracle:ojdbc6:12.1.0.1-atlassian-hosted' testCompile 'com.microsoft.sqlserver:mssql-jdbc:6.1.0.jre8' diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java index 4eb55b38e24..40cb707a2d0 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java @@ -198,8 +198,8 @@ private HikariDataSource verifyCharacterSet(String jdbcUrl) throws SQLException HikariDataSource dataSource = getDataSource(jdbcUrl, 1); boolean result = new QueryRunner(dataSource).query("SHOW VARIABLES LIKE 'character\\_set\\_connection'", rs -> { rs.next(); - String resultSetInt = rs.getString(2); - assertEquals("Passing query parameters to set DB connection encoding is successful", "utf8", resultSetInt); + String resultSetString = rs.getString(2); + assertTrue("Passing query parameters to set DB connection encoding is successful", resultSetString.startsWith("utf8")); return true; }); diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/AbstractContainerDatabaseTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/AbstractContainerDatabaseTest.java new file mode 100644 index 00000000000..a01146fe7a3 --- /dev/null +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/AbstractContainerDatabaseTest.java @@ -0,0 +1,32 @@ +package org.testcontainers.junit; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import org.testcontainers.containers.JdbcDatabaseContainer; + +import javax.sql.DataSource; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +abstract class AbstractContainerDatabaseTest { + + ResultSet performQuery(JdbcDatabaseContainer container, String sql) throws SQLException { + DataSource ds = getDataSource(container); + Statement statement = ds.getConnection().createStatement(); + statement.execute(sql); + ResultSet resultSet = statement.getResultSet(); + + resultSet.next(); + return resultSet; + } + + DataSource getDataSource(JdbcDatabaseContainer container) { + HikariConfig hikariConfig = new HikariConfig(); + hikariConfig.setJdbcUrl(container.getJdbcUrl()); + hikariConfig.setUsername(container.getUsername()); + hikariConfig.setPassword(container.getPassword()); + + return new HikariDataSource(hikariConfig); + } +} diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/MultiVersionMySQLTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/MultiVersionMySQLTest.java new file mode 100644 index 00000000000..b685d6dad80 --- /dev/null +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/MultiVersionMySQLTest.java @@ -0,0 +1,37 @@ +package org.testcontainers.junit; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.testcontainers.containers.MySQLContainer; + +import java.sql.ResultSet; +import java.sql.SQLException; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; + +@RunWith(Parameterized.class) +public class MultiVersionMySQLTest extends AbstractContainerDatabaseTest { + + @Parameterized.Parameters(name = "{0}") + public static String[] params() { + return new String[]{"5.5.62", "5.6.42", "5.7.26", "8.0.16"}; + } + + private final String version; + + public MultiVersionMySQLTest(String version) { + this.version = version; + } + + @Test + public void versionCheckTest() throws SQLException { + try (final MySQLContainer container = new MySQLContainer<>("mysql:" + version)) { + container.start(); + final ResultSet resultSet = performQuery(container, "SELECT VERSION()"); + final String resultSetString = resultSet.getString(1); + + assertEquals("The database version can be set using a container rule parameter", version, resultSetString); + } + } +} diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleClickhouseTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleClickhouseTest.java index 4e123617b8d..b765208e212 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleClickhouseTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleClickhouseTest.java @@ -1,33 +1,22 @@ package org.testcontainers.junit; -import com.zaxxer.hikari.HikariConfig; -import com.zaxxer.hikari.HikariDataSource; import org.junit.Rule; import org.junit.Test; import org.testcontainers.containers.ClickHouseContainer; import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Statement; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -public class SimpleClickhouseTest { +public class SimpleClickhouseTest extends AbstractContainerDatabaseTest { @Rule public ClickHouseContainer clickhouse = new ClickHouseContainer(); @Test public void testSimple() throws SQLException { - HikariConfig hikariConfig = new HikariConfig(); - hikariConfig.setJdbcUrl(clickhouse.getJdbcUrl()); - hikariConfig.setUsername(clickhouse.getUsername()); - hikariConfig.setPassword(clickhouse.getPassword()); - - HikariDataSource ds = new HikariDataSource(hikariConfig); - Statement statement = ds.getConnection().createStatement(); - statement.execute("SELECT 1"); - ResultSet resultSet = statement.getResultSet(); + ResultSet resultSet = performQuery(clickhouse, "SELECT 1"); resultSet.next(); int resultSetInt = resultSet.getInt(1); diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMSSQLServerTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMSSQLServerTest.java index 93904cfb94a..2791efa0780 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMSSQLServerTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMSSQLServerTest.java @@ -1,11 +1,10 @@ package org.testcontainers.junit; -import com.zaxxer.hikari.HikariConfig; -import com.zaxxer.hikari.HikariDataSource; import org.junit.Rule; import org.junit.Test; import org.testcontainers.containers.MSSQLServerContainer; +import javax.sql.DataSource; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; @@ -15,36 +14,22 @@ /** * @author Stefan Hufschmidt */ -public class SimpleMSSQLServerTest { +public class SimpleMSSQLServerTest extends AbstractContainerDatabaseTest { @Rule public MSSQLServerContainer mssqlServer = new MSSQLServerContainer(); @Test public void testSimple() throws SQLException { - HikariConfig hikariConfig = new HikariConfig(); - hikariConfig.setJdbcUrl(mssqlServer.getJdbcUrl()); - hikariConfig.setUsername(mssqlServer.getUsername()); - hikariConfig.setPassword(mssqlServer.getPassword()); + ResultSet resultSet = performQuery(mssqlServer, "SELECT 1"); - HikariDataSource ds = new HikariDataSource(hikariConfig); - Statement statement = ds.getConnection().createStatement(); - statement.execute("SELECT 1"); - ResultSet resultSet = statement.getResultSet(); - - resultSet.next(); int resultSetInt = resultSet.getInt(1); assertEquals("A basic SELECT query succeeds", 1, resultSetInt); } @Test public void testSetupDatabase() throws SQLException { - HikariConfig hikariConfig = new HikariConfig(); - hikariConfig.setJdbcUrl(mssqlServer.getJdbcUrl()); - hikariConfig.setUsername(mssqlServer.getUsername()); - hikariConfig.setPassword(mssqlServer.getPassword()); - - HikariDataSource ds = new HikariDataSource(hikariConfig); + DataSource ds = getDataSource(mssqlServer); Statement statement = ds.getConnection().createStatement(); statement.executeUpdate("CREATE DATABASE [test];"); statement = ds.getConnection().createStatement(); diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMariaDBTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMariaDBTest.java index 042582ed890..21badefb9fb 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMariaDBTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMariaDBTest.java @@ -1,26 +1,21 @@ package org.testcontainers.junit; -import com.zaxxer.hikari.HikariConfig; -import com.zaxxer.hikari.HikariDataSource; -import lombok.NonNull; - import org.apache.commons.lang.SystemUtils; import org.junit.Test; import org.testcontainers.containers.MariaDBContainer; import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Statement; +import static org.junit.Assume.assumeFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.junit.Assume.assumeFalse; /** * @author Miguel Gonzalez Sanchez */ -public class SimpleMariaDBTest { +public class SimpleMariaDBTest extends AbstractContainerDatabaseTest { @Test public void testSimple() throws SQLException { @@ -85,20 +80,4 @@ public void testMariaDBWithCommandOverride() throws SQLException { mariadbCustomConfig.stop(); } } - - @NonNull - protected ResultSet performQuery(MariaDBContainer containerRule, String sql) throws SQLException { - HikariConfig hikariConfig = new HikariConfig(); - hikariConfig.setJdbcUrl(containerRule.getJdbcUrl()); - hikariConfig.setUsername(containerRule.getUsername()); - hikariConfig.setPassword(containerRule.getPassword()); - - HikariDataSource ds = new HikariDataSource(hikariConfig); - Statement statement = ds.getConnection().createStatement(); - statement.execute(sql); - ResultSet resultSet = statement.getResultSet(); - - resultSet.next(); - return resultSet; - } } diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java index 5c8e4133a22..9570659c4dc 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java @@ -1,17 +1,5 @@ package org.testcontainers.junit; -import static org.junit.Assert.fail; -import static org.junit.Assume.assumeFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -import com.zaxxer.hikari.HikariConfig; -import com.zaxxer.hikari.HikariDataSource; - import org.apache.commons.lang.SystemUtils; import org.junit.Test; import org.slf4j.Logger; @@ -20,13 +8,19 @@ import org.testcontainers.containers.MySQLContainer; import org.testcontainers.containers.output.Slf4jLogConsumer; -import lombok.NonNull; +import java.sql.ResultSet; +import java.sql.SQLException; + +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeFalse; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; /** * @author richardnorth */ -public class SimpleMySQLTest { +public class SimpleMySQLTest extends AbstractContainerDatabaseTest { private static final Logger logger = LoggerFactory.getLogger(SimpleMySQLTest.class); @@ -114,24 +108,6 @@ public void testCommandOverride() throws SQLException { } - @Test - public void testMySQL8() throws SQLException { - assumeFalse(SystemUtils.IS_OS_WINDOWS); - MySQLContainer container = new MySQLContainer<>("mysql:8.0.11") - .withCommand("mysqld --default-authentication-plugin=mysql_native_password"); - container.start(); - - try { - ResultSet resultSet = performQuery(container, "SELECT VERSION()"); - String resultSetString = resultSet.getString(1); - - assertTrue("The database version can be set using a container rule parameter", "8.0.11".equals(resultSetString)); - } - finally { - container.stop(); - } - } - @Test public void testExplicitInitScript() throws SQLException { try (MySQLContainer container = (MySQLContainer) new MySQLContainer() @@ -160,21 +136,4 @@ public void testEmptyPasswordWithNonRootUser() { container.stop(); } } - - @NonNull - protected ResultSet performQuery(MySQLContainer containerRule, String sql) throws SQLException { - HikariConfig hikariConfig = new HikariConfig(); - hikariConfig.setDriverClassName(containerRule.getDriverClassName()); - hikariConfig.setJdbcUrl(containerRule.getJdbcUrl()); - hikariConfig.setUsername(containerRule.getUsername()); - hikariConfig.setPassword(containerRule.getPassword()); - - HikariDataSource ds = new HikariDataSource(hikariConfig); - Statement statement = ds.getConnection().createStatement(); - statement.execute(sql); - ResultSet resultSet = statement.getResultSet(); - - resultSet.next(); - return resultSet; - } } diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleOracleTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleOracleTest.java index 2fedcaf08e1..0855fd4860e 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleOracleTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleOracleTest.java @@ -1,7 +1,5 @@ package org.testcontainers.junit; -import com.zaxxer.hikari.HikariConfig; -import com.zaxxer.hikari.HikariDataSource; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; @@ -9,7 +7,6 @@ import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Statement; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; @@ -17,25 +14,18 @@ * @author gusohal */ @Ignore -public class SimpleOracleTest { +public class SimpleOracleTest extends AbstractContainerDatabaseTest { @Rule public OracleContainer oracle = new OracleContainer(); @Test public void testSimple() throws SQLException { - HikariConfig hikariConfig = new HikariConfig(); - hikariConfig.setJdbcUrl(oracle.getJdbcUrl()); - hikariConfig.setUsername(oracle.getUsername()); - hikariConfig.setPassword(oracle.getPassword()); - - HikariDataSource ds = new HikariDataSource(hikariConfig); - Statement statement = ds.getConnection().createStatement(); - statement.execute("SELECT 1 FROM dual"); - ResultSet resultSet = statement.getResultSet(); + ResultSet resultSet = performQuery(oracle, "SELECT 1 FROM dual"); resultSet.next(); int resultSetInt = resultSet.getInt(1); + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); } } diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimplePostgreSQLTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimplePostgreSQLTest.java index 2581240cd84..c6685343c29 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimplePostgreSQLTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimplePostgreSQLTest.java @@ -1,21 +1,17 @@ package org.testcontainers.junit; -import com.zaxxer.hikari.HikariConfig; -import com.zaxxer.hikari.HikariDataSource; import org.junit.Test; -import org.testcontainers.containers.JdbcDatabaseContainer; import org.testcontainers.containers.PostgreSQLContainer; import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Statement; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; /** * @author richardnorth */ -public class SimplePostgreSQLTest { +public class SimplePostgreSQLTest extends AbstractContainerDatabaseTest { @Test public void testSimple() throws SQLException { @@ -41,18 +37,4 @@ public void testExplicitInitScript() throws SQLException { assertEquals("Value from init script should equal real value", "hello world", firstColumnValue); } } - - private ResultSet performQuery(JdbcDatabaseContainer container, String sql) throws SQLException { - HikariConfig hikariConfig = new HikariConfig(); - hikariConfig.setJdbcUrl(container.getJdbcUrl()); - hikariConfig.setUsername(container.getUsername()); - hikariConfig.setPassword(container.getPassword()); - - HikariDataSource ds = new HikariDataSource(hikariConfig); - Statement statement = ds.getConnection().createStatement(); - statement.execute(sql); - ResultSet resultSet = statement.getResultSet(); - resultSet.next(); - return resultSet; - } } diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java index 6d9139fa971..172ead4856a 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java @@ -70,10 +70,14 @@ protected String constructUrlForConnection(String queryString) { if (! url.contains("useSSL=")) { String separator = url.contains("?") ? "&" : "?"; - return url + separator + "useSSL=false"; - } else { - return url; + url = url + separator + "useSSL=false"; + } + + if (! url.contains("allowPublicKeyRetrieval=")) { + url = url + "&allowPublicKeyRetrieval=true"; } + + return url; } @Override From 9fd199d39183798c046ae0f8de2d8340cc5f8584 Mon Sep 17 00:00:00 2001 From: Johnny Lim Date: Tue, 28 May 2019 06:20:12 +0900 Subject: [PATCH 0731/5615] Fix initscript typo in documentation (#1491) --- docs/modules/databases/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/databases/index.md b/docs/modules/databases/index.md index c7253cdddd7..71d65af7e20 100644 --- a/docs/modules/databases/index.md +++ b/docs/modules/databases/index.md @@ -67,7 +67,7 @@ Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database ## Using a classpath init script -Testcontainers can run an initscript after the database container is started, but before your code is given a connection to it. The script must be on the classpath, and is referenced as follows: +Testcontainers can run an init script after the database container is started, but before your code is given a connection to it. The script must be on the classpath, and is referenced as follows: `jdbc:tc:mysql:5.7.22://hostname/databasename?TC_INITSCRIPT=somepath/init_mysql.sql` From b0b3f9a1205037906838f6643594f27a79c8a734 Mon Sep 17 00:00:00 2001 From: Frederik Hahne Date: Mon, 27 May 2019 23:30:08 +0200 Subject: [PATCH 0732/5615] Fix docker registry auth parsing of GCR json key format credentials (#1492) closes #1466 --- .../utility/RegistryAuthLocator.java | 2 +- .../utility/RegistryAuthLocatorTest.java | 14 ++++++++++++-- .../auth-config/config-with-json-key.json | 7 +++++++ 3 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 core/src/test/resources/auth-config/config-with-json-key.json diff --git a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java index 402a15d58b4..a1be23eb82e 100644 --- a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java +++ b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java @@ -154,7 +154,7 @@ private AuthConfig findExistingAuthConfig(final JsonNode config, final String re !isBlank(deserializedAuth.getAuth())) { final String rawAuth = new String(Base64.getDecoder().decode(deserializedAuth.getAuth())); - final String[] splitRawAuth = rawAuth.split(":"); + final String[] splitRawAuth = rawAuth.split(":", 2); if (splitRawAuth.length == 2) { deserializedAuth.withUsername(splitRawAuth[0]); diff --git a/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java b/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java index 938505c0212..fe930cf78b7 100644 --- a/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java +++ b/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java @@ -11,8 +11,7 @@ import java.util.HashMap; import java.util.Map; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNull; +import static org.rnorth.visibleassertions.VisibleAssertions.*; public class RegistryAuthLocatorTest { @Test @@ -37,6 +36,17 @@ public void lookupAuthConfigWithBasicAuthCredentials() throws URISyntaxException assertEquals("Password is set", "pass", authConfig.getPassword()); } + @Test + public void lookupAuthConfigWithJsonKeyCredentials() throws URISyntaxException { + final RegistryAuthLocator authLocator = createTestAuthLocator("config-with-json-key.json"); + + final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("registry.example.com/org/repo"), new AuthConfig()); + + assertEquals("Default docker registry URL is set on auth config", "https://registry.example.com", authConfig.getRegistryAddress()); + assertEquals("Username is set", "_json_key", authConfig.getUsername()); + assertNotNull("Password is set", authConfig.getPassword()); + } + @Test public void lookupAuthConfigUsingStore() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-with-store.json"); diff --git a/core/src/test/resources/auth-config/config-with-json-key.json b/core/src/test/resources/auth-config/config-with-json-key.json new file mode 100644 index 00000000000..3584963d645 --- /dev/null +++ b/core/src/test/resources/auth-config/config-with-json-key.json @@ -0,0 +1,7 @@ +{ + "auths": { + "https://registry.example.com": { + "auth": "X2pzb25fa2V5OnsKInR5cGUiOiAic2VydmljZV9hY2NvdW50IiwKInByb2plY3RfaWQiOiAiZXhhbXBsZS1wcm9qZWN0IiwKInByaXZhdGVfa2V5X2lkIjogImV4YW1wbGUta2V5LWlkIiwKInByaXZhdGVfa2V5IjogImV4YW1wbGUtcHJpdmF0ZS1rZXkiLAoiY2xpZW50X2VtYWlsIjogInVzZXJAZXhhbXBsZS5jb20iLAoiY2xpZW50X2lkIjogInVzZXJAZXhhbXBsZS5jb20iLAoiYXV0aF91cmkiOiAiaHR0cHM6Ly9yZWdpc3RyeS5leGFtcGxlLmNvbS9vL29hdXRoMi9hdXRoIiwKInRva2VuX3VyaSI6ICJodHRwczovL3JlZ2lzdHJ5LmV4YW1wbGUuY29tL3Rva2VuIiwKImF1dGhfcHJvdmlkZXJfeDUwOV9jZXJ0X3VybCI6ICJodHRwczovL3JlZ2lzdHJ5LmV4YW1wbGUuY29tIiwKImNsaWVudF94NTA5X2NlcnRfdXJsIjogImh0dHBzOi8vcmVnaXN0cnkuZXhhbXBsZS5jb20iCn0=" + } + } +} From d54e119eaf31ec13a3d2bcfbf6d44f7e46c604e8 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Mon, 27 May 2019 22:31:18 +0100 Subject: [PATCH 0733/5615] Bump neo4j-java-driver from 1.7.4 to 1.7.5 in /modules/neo4j (#1477) Bumps [neo4j-java-driver](https://github.com/neo4j/neo4j-java-driver) from 1.7.4 to 1.7.5. - [Release notes](https://github.com/neo4j/neo4j-java-driver/releases) - [Commits](https://github.com/neo4j/neo4j-java-driver/compare/1.7.4...1.7.5) Signed-off-by: dependabot[bot] --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index c467cc7381f..cc0e246396f 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -29,6 +29,6 @@ dependencies { compile project(":testcontainers") - testCompile "org.neo4j.driver:neo4j-java-driver:1.7.4" + testCompile "org.neo4j.driver:neo4j-java-driver:1.7.5" testCompile 'org.assertj:assertj-core:3.12.2' } From 6feb6c9e0fc5c4e5ef2ff94e538ee173b5eca2e0 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Mon, 27 May 2019 22:32:02 +0100 Subject: [PATCH 0734/5615] Bump java-client from 2.7.4 to 2.7.6 in /modules/couchbase (#1493) Bumps [java-client](https://github.com/couchbase/couchbase-java-client) from 2.7.4 to 2.7.6. - [Release notes](https://github.com/couchbase/couchbase-java-client/releases) - [Commits](https://github.com/couchbase/couchbase-java-client/compare/2.7.4...2.7.6) Signed-off-by: dependabot[bot] --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 2bf1055038b..8cf0f879215 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -2,5 +2,5 @@ description = "Testcontainers :: Couchbase" dependencies { compile project(':testcontainers') - compile 'com.couchbase.client:java-client:2.7.4' + compile 'com.couchbase.client:java-client:2.7.6' } From e5de96aa39bee8e774f46912cd47eb115204c38c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Tue, 28 May 2019 09:03:23 +0100 Subject: [PATCH 0735/5615] Bump neo4j from 3.5.5 to 3.5.6 in /modules/neo4j (#1498) Bumps [neo4j](https://github.com/neo4j/neo4j) from 3.5.5 to 3.5.6. - [Release notes](https://github.com/neo4j/neo4j/releases) - [Commits](https://github.com/neo4j/neo4j/compare/3.5.5...3.5.6) --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index cc0e246396f..68a52d115cd 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -25,7 +25,7 @@ task customNeo4jPluginJar(type: Jar) { test.dependsOn customNeo4jPluginJar dependencies { - customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.5" + customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.6" compile project(":testcontainers") From 31f33aa96a7345e1e5eb22fc54f4422a4cfa2475 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Tue, 28 May 2019 09:03:34 +0100 Subject: [PATCH 0736/5615] Bump jna-platform from 5.2.0 to 5.3.1 in /core (#1437) Bumps [jna-platform](https://github.com/java-native-access/jna) from 5.2.0 to 5.3.1. - [Release notes](https://github.com/java-native-access/jna/releases) - [Changelog](https://github.com/java-native-access/jna/blob/master/CHANGES.md) - [Commits](https://github.com/java-native-access/jna/compare/5.2.0...5.3.1) Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index d9dee98ee6b..9786e51a774 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -73,7 +73,7 @@ dependencies { exclude(group: "log4j", module: "log4j") } - compile "net.java.dev.jna:jna-platform:5.2.0" + compile "net.java.dev.jna:jna-platform:5.3.1" shaded ('org.scala-sbt.ipcsocket:ipcsocket:1.0.0') { exclude(group: "net.java.dev.jna") From 1b3a3bd45211ad2b4a5c1b24d89cd1e092d64647 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Tue, 28 May 2019 09:03:59 +0100 Subject: [PATCH 0737/5615] Bump amqp-client from 5.7.0 to 5.7.1 in /core (#1501) Bumps [amqp-client](https://github.com/rabbitmq/rabbitmq-java-client) from 5.7.0 to 5.7.1. - [Release notes](https://github.com/rabbitmq/rabbitmq-java-client/releases) - [Commits](https://github.com/rabbitmq/rabbitmq-java-client/compare/v5.7.0...v5.7.1) --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 9786e51a774..853d3a199b9 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -102,7 +102,7 @@ dependencies { testCompile 'org.apache.httpcomponents:httpclient:4.5.8' testCompile 'redis.clients:jedis:3.0.1' - testCompile 'com.rabbitmq:amqp-client:5.7.0' + testCompile 'com.rabbitmq:amqp-client:5.7.1' testCompile 'org.mongodb:mongo-java-driver:3.10.2' testCompile ('org.mockito:mockito-core:2.27.0') { exclude(module: 'hamcrest-core') From 6f6942ef68236eb57a792a6b387c9efdb2fa7396 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Tue, 28 May 2019 09:04:21 +0100 Subject: [PATCH 0738/5615] Bump tomcat-jdbc from 9.0.19 to 9.0.20 in /modules/mariadb (#1469) Bumps tomcat-jdbc from 9.0.19 to 9.0.20. Signed-off-by: dependabot[bot] --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 84c5ce667d4..371ec52641d 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -6,6 +6,6 @@ dependencies { testCompile 'org.mariadb.jdbc:mariadb-java-client:2.4.1' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.19' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.20' testCompile 'org.vibur:vibur-dbcp:22.2' } From 4325c2ad7da78a3057710a2af0950bc80140af57 Mon Sep 17 00:00:00 2001 From: Johnny Lim Date: Tue, 28 May 2019 17:07:54 +0900 Subject: [PATCH 0739/5615] Fix typo in docs (describe use of stop() method rather than close()) (#1479) --- docs/test_framework_integration/junit_4.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/test_framework_integration/junit_4.md b/docs/test_framework_integration/junit_4.md index f2f46636442..8ab753e611f 100644 --- a/docs/test_framework_integration/junit_4.md +++ b/docs/test_framework_integration/junit_4.md @@ -18,7 +18,7 @@ public class SimpleMySQLTest { ## Manually controlling container lifecycle -As an alternative, you can manually start the container in a `@BeforeClass`/`@Before` annotated method in your tests. Tear down will be done automatically on JVM exit, but you can of course also use an `@AfterClass`/`@After` annotated method to manually call the `close()` method on your container. +As an alternative, you can manually start the container in a `@BeforeClass`/`@Before` annotated method in your tests. Tear down will be done automatically on JVM exit, but you can of course also use an `@AfterClass`/`@After` annotated method to manually call the `stop()` method on your container. *Example of starting a container in a `@Before` annotated method:* From 83a7accfebae7671294bd0193caba7c5fff4563a Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 28 May 2019 13:02:12 +0100 Subject: [PATCH 0740/5615] Remove internet connectivity dependency from test (#1502) * Remove internet connectivity dependency from test * Add test timeout * Remove redundant throw --- .../org/testcontainers/junit/OutputStreamTest.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/core/src/test/java/org/testcontainers/junit/OutputStreamTest.java b/core/src/test/java/org/testcontainers/junit/OutputStreamTest.java index a14e1e32237..83ba05be86e 100644 --- a/core/src/test/java/org/testcontainers/junit/OutputStreamTest.java +++ b/core/src/test/java/org/testcontainers/junit/OutputStreamTest.java @@ -24,11 +24,11 @@ public class OutputStreamTest { @Rule public GenericContainer container = new GenericContainer("alpine:3.2") - .withCommand("ping -c 5 www.google.com"); + .withCommand("ping -c 5 127.0.0.1"); private static final Logger LOGGER = LoggerFactory.getLogger(OutputStreamTest.class); - @Test + @Test(timeout = 60_000L) public void testFetchStdout() throws TimeoutException { WaitingConsumer consumer = new WaitingConsumer(); @@ -39,8 +39,8 @@ public void testFetchStdout() throws TimeoutException { 30, TimeUnit.SECONDS); } - @Test - public void testFetchStdoutWithTimeout() throws TimeoutException { + @Test(timeout = 60_000L) + public void testFetchStdoutWithTimeout() { WaitingConsumer consumer = new WaitingConsumer(); @@ -53,7 +53,7 @@ public void testFetchStdoutWithTimeout() throws TimeoutException { }); } - @Test + @Test(timeout = 60_000L) public void testFetchStdoutWithNoLimit() throws TimeoutException { WaitingConsumer consumer = new WaitingConsumer(); @@ -63,7 +63,7 @@ public void testFetchStdoutWithNoLimit() throws TimeoutException { consumer.waitUntil(frame -> frame.getType() == STDOUT && frame.getUtf8String().contains("seq=2")); } - @Test + @Test(timeout = 60_000L) public void testLogConsumer() throws TimeoutException { WaitingConsumer waitingConsumer = new WaitingConsumer(); @@ -75,7 +75,7 @@ public void testLogConsumer() throws TimeoutException { waitingConsumer.waitUntil(frame -> frame.getType() == STDOUT && frame.getUtf8String().contains("seq=2")); } - @Test + @Test(timeout = 60_000L) public void testToStringConsumer() throws TimeoutException { WaitingConsumer waitingConsumer = new WaitingConsumer(); From 3b0a7e652b5913cae2e560c748ba75e2535e9ca3 Mon Sep 17 00:00:00 2001 From: Johnny Lim Date: Wed, 29 May 2019 04:38:54 +0900 Subject: [PATCH 0741/5615] Replace deprecated RestClient.performRequest() (#1494) --- docs/modules/elasticsearch.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/elasticsearch.md b/docs/modules/elasticsearch.md index 3f683e570f9..02eeb61dfbe 100644 --- a/docs/modules/elasticsearch.md +++ b/docs/modules/elasticsearch.md @@ -22,7 +22,7 @@ credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredential RestClient restClient = RestClient.builder(HttpHost.create(container.getHttpHostAddress())) .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)) .build(); -Response response = restClient.performRequest("GET", "/"); +Response response = restClient.performRequest(new Request("GET", "/")); // ... or the transport client TransportAddress transportAddress = new TransportAddress(container.getTcpHost()); From 833f4170fb862ab5b0976926986ec9508bd55798 Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 29 May 2019 16:25:45 +0100 Subject: [PATCH 0742/5615] Rename boomper bot configuration file to match correct name --- .github/{boomper.yml => bumper.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/{boomper.yml => bumper.yml} (100%) diff --git a/.github/boomper.yml b/.github/bumper.yml similarity index 100% rename from .github/boomper.yml rename to .github/bumper.yml From 94a14f0bbb8f4b57a19f6ff69aa0298d81a8132c Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 29 May 2019 16:27:16 +0100 Subject: [PATCH 0743/5615] Fix latest version (manual, due to misconfigured boomper bot) --- mkdocs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index 2238c7573a8..9539df4fad8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,4 +1,4 @@ -site_name: Testcontainers +'site_name: Testcontainers plugins: - search - codeinclude @@ -78,4 +78,4 @@ nav: - contributing.md - contributing_docs.md extra: - latest_version: 1.11.2 + latest_version: 1.11.3 From 261982768ceecd9aec26dff9457c8ec7953d791b Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 29 May 2019 17:16:45 +0100 Subject: [PATCH 0744/5615] Fix broken mkdocs.yml --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 9539df4fad8..762116a062c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,4 +1,4 @@ -'site_name: Testcontainers +site_name: Testcontainers plugins: - search - codeinclude From 16ed2379ff6e8622adddfd271032a28477cdcd0c Mon Sep 17 00:00:00 2001 From: Stuart Davidson Date: Thu, 30 May 2019 13:54:19 +0100 Subject: [PATCH 0745/5615] Remove broken link to roadmap (#1511) The docs/ROADMAP.md link doesn't work and I don't see a replacement, so either remove the link or update it. --- CONTRIBUTING.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2914c874e1a..9a9f1e43b55 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,6 @@ * Star the project on [Github](https://github.com/testcontainers/testcontainers-java) and help spread the word :) * Join our Slack: http://slack.testcontainers.org -* See [ROADMAP](docs/ROADMAP.md) to understand the approach behind the project and what may/may not be in store for the future. * [Post an issue](https://github.com/testcontainers/testcontainers-java/issues) if you find any bugs * Contribute improvements or fixes using a [Pull Request](https://github.com/testcontainers/testcontainers-java/pulls). If you're going to contribute, thank you! Please just be sure to: * discuss with the authors on an issue ticket prior to doing anything big From 5c5bfe83308791c578351e99bff13c9b3eaeede4 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Thu, 30 May 2019 13:55:02 +0100 Subject: [PATCH 0746/5615] Bump mockito-core from 2.27.0 to 2.28.2 in /core (#1512) Bumps [mockito-core](https://github.com/mockito/mockito) from 2.27.0 to 2.28.2. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v2.27.0...v2.28.2) --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 853d3a199b9..a0c35863328 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -104,7 +104,7 @@ dependencies { testCompile 'redis.clients:jedis:3.0.1' testCompile 'com.rabbitmq:amqp-client:5.7.1' testCompile 'org.mongodb:mongo-java-driver:3.10.2' - testCompile ('org.mockito:mockito-core:2.27.0') { + testCompile ('org.mockito:mockito-core:2.28.2') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From 1e49e7dcaaaa54ce6e5c27b18aed63a5ed3545b0 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Fri, 31 May 2019 01:31:23 +0000 Subject: [PATCH 0747/5615] Bump okhttp from 3.12.1 to 3.14.2 in /core (#1475) --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index a0c35863328..a77720d66df 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -92,7 +92,7 @@ dependencies { exclude(group: 'org.apache.commons', module: 'commons-compress') } - shaded 'com.squareup.okhttp3:okhttp:3.12.1' + shaded 'com.squareup.okhttp3:okhttp:3.14.2' shaded 'org.glassfish.main.external:trilead-ssh2-repackaged:4.1.2' From 3a9a7a2ac36b0a0fe808c032300906fe45c2381e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Fri, 31 May 2019 02:08:45 +0000 Subject: [PATCH 0748/5615] Bump okhttp from 3.13.1 to 3.14.2 in /examples (#1478) --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 565ed7c5353..8522878f5ec 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.26' - implementation 'com.squareup.okhttp3:okhttp:3.13.1' + implementation 'com.squareup.okhttp3:okhttp:3.14.2' implementation 'org.json:json:20180813' testImplementation 'org.postgresql:postgresql:42.2.5' testImplementation 'ch.qos.logback:logback-classic:1.2.3' From bded5611667a0a9c6b779927643db9fea8405d79 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Fri, 31 May 2019 07:58:29 +0000 Subject: [PATCH 0749/5615] Bump pulsar-client from 2.3.1 to 2.3.2 in /modules/pulsar (#1517) --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index c3505dedf65..301d6f81da2 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Pulsar" dependencies { compile project(':testcontainers') - testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.3.1' + testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.3.2' testCompile group: 'org.assertj', name: 'assertj-core', version: '3.12.2' } From 481ff4873f6a80138c9ce84bf5eb1a87749f1e8d Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 31 May 2019 12:03:42 +0100 Subject: [PATCH 0750/5615] Disable dependabot automerge for core module (#1518) Disabling for core only, due to higher risk of accidental changes if automerge is used for this library --- .dependabot/config.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.dependabot/config.yml b/.dependabot/config.yml index 7acddda566c..7fe8921a36b 100644 --- a/.dependabot/config.yml +++ b/.dependabot/config.yml @@ -4,9 +4,6 @@ update_configs: - package_manager: "java:gradle" directory: "/core" update_schedule: "daily" - automerged_updates: - - match: { dependency_type: "development", update_type: "semver:minor" } - - match: { dependency_type: "production", update_type: "semver:minor" } # Explicit entry for each module - package_manager: "java:gradle" From 3d57220a0a557def6bf717ca26604f59293bcfab Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Mon, 3 Jun 2019 08:36:54 +0000 Subject: [PATCH 0751/5615] Bump kafka-clients from 2.2.0 to 2.2.1 in /modules/kafka (#1522) --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index cefb6368445..29cef0b4713 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Kafka" dependencies { compile project(':testcontainers') - testCompile 'org.apache.kafka:kafka-clients:2.2.0' + testCompile 'org.apache.kafka:kafka-clients:2.2.1' testCompile 'org.assertj:assertj-core:3.12.2' testCompile 'com.google.guava:guava:23.0' } From 369cfe1cac3d4ab3bf1c8590199cd4786c11f4d6 Mon Sep 17 00:00:00 2001 From: RSchlenker Date: Mon, 3 Jun 2019 11:08:15 +0200 Subject: [PATCH 0752/5615] Add spock quickstart documentation (#1499) --- docs/quickstart/spock_quickstart.md | 87 +++++++++++++++++++ examples/settings.gradle | 2 +- examples/spock/build.gradle | 15 ++++ .../java/quickstart/RedisBackedCache.java | 22 +++++ .../quickstart/RedisBackedCacheIntTest.groovy | 36 ++++++++ .../RedisBackedCacheIntTestStep0.groovy | 25 ++++++ .../spock/src/test/resources/logback-test.xml | 27 ++++++ mkdocs.yml | 1 + 8 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 docs/quickstart/spock_quickstart.md create mode 100644 examples/spock/build.gradle create mode 100644 examples/spock/src/main/java/quickstart/RedisBackedCache.java create mode 100644 examples/spock/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy create mode 100644 examples/spock/src/test/groovy/quickstart/RedisBackedCacheIntTestStep0.groovy create mode 100644 examples/spock/src/test/resources/logback-test.xml diff --git a/docs/quickstart/spock_quickstart.md b/docs/quickstart/spock_quickstart.md new file mode 100644 index 00000000000..921956c875a --- /dev/null +++ b/docs/quickstart/spock_quickstart.md @@ -0,0 +1,87 @@ +# Spock Quickstart + +It's easy to add Testcontainers to your project - let's walk through a quick example to see how. + +Let's imagine we have a simple program that has a dependency on Redis, and we want to add some tests for it. +In our imaginary program, there is a `RedisBackedCache` class which stores data in Redis. + +You can see an example test that could have been written for it (without using Testcontainers): + + +[Pre-Testcontainers test code](../../examples/spock/src/test/groovy/quickstart/RedisBackedCacheIntTestStep0.groovy) block:RedisBackedCacheIntTestStep0 + + +Notice that the existing test has a problem - it's relying on a local installation of Redis, which is a red flag for test reliability. +This may work if we were sure that every developer and CI machine had Redis installed, but would fail otherwise. +We might also have problems if we attempted to run tests in parallel, such as state bleeding between tests, or port clashes. + +Let's start from here, and see how to improve the test with Testcontainers: + +## 1. Add Testcontainers as a test-scoped dependency + +First, add Testcontainers as a dependency as follows: + +```groovy tab='Gradle' +testCompile "org.testcontainers:spock:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + spock + {{latest_version}} + test + +``` + +## 2. Get Testcontainers to run a Redis container during our tests + +Annotate the Spock specification class with the Testcontainers extension: + +```groovy tab='Spock Testcontainers annotation' +@org.testcontainers.spock.Testcontainers +class RedisBackedCacheIntTest extends Specification { +``` + +And add the following field to the body of our test class: + + +[Spock Testcontainers init](../../examples/spock/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy) inside_block:init + + +This tells Spock to start a Testcontainers `GenericContainer`, configured to use a specific Redis image from Docker Hub, and configured to expose a port. + +If we run our test as-is, then regardless of the actual test outcome, we'll see logs showing us that Testcontainers: + +* was activated before our test method ran +* discovered and quickly tested our local Docker setup +* pulled the image if necessary +* started a new container and waited for it to be ready +* shut down and deleted the container after the test + +## 3. Make sure our code can talk to the container + +Before Testcontainers, we might have hardcoded an address like `localhost:6379` into our tests. + +Testcontainers uses *randomized ports* for each container it starts, but makes it easy to obtain the actual port at runtime. +We can do this in our test `setup` method, to set up our component under test: + + +[Obtaining a mapped port](../../examples/spock/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy) inside_block:setup + + +!!! tip + Notice that we also ask Testcontainers for the container's actual address with `redis.containerIpAddress`, + rather than hard-coding `localhost`. `localhost` may work in some environments but not others - for example it may + not work on your current or future CI environment. As such, **avoid hard-coding** the address, and use + `containerIpAddress` instead. + +## 4. Run the tests! + +That's it! + +Let's look at our complete test class to see how little we had to add to get up and running with Testcontainers: + + +[RedisBackedCacheIntTest](../../examples/spock/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy) block:complete + diff --git a/examples/settings.gradle b/examples/settings.gradle index 06252afa580..3d57c2a18df 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -12,4 +12,4 @@ include 'selenium-container' include 'singleton-container' include 'spring-boot' include 'cucumber' - +include 'spock' diff --git a/examples/spock/build.gradle b/examples/spock/build.gradle new file mode 100644 index 00000000000..0b727fbd18a --- /dev/null +++ b/examples/spock/build.gradle @@ -0,0 +1,15 @@ +plugins { + id 'java' + id 'groovy' +} + +repositories { + jcenter() +} + +dependencies { + compile "io.lettuce:lettuce-core:5.1.1.RELEASE" + testImplementation 'org.spockframework:spock-core:1.2-groovy-2.5' + testImplementation 'org.testcontainers:spock' + testImplementation 'ch.qos.logback:logback-classic:1.2.3' +} diff --git a/examples/spock/src/main/java/quickstart/RedisBackedCache.java b/examples/spock/src/main/java/quickstart/RedisBackedCache.java new file mode 100644 index 00000000000..1123858ca17 --- /dev/null +++ b/examples/spock/src/main/java/quickstart/RedisBackedCache.java @@ -0,0 +1,22 @@ +package quickstart; + +import io.lettuce.core.RedisClient; +import io.lettuce.core.api.StatefulRedisConnection; + +public class RedisBackedCache { + + private final StatefulRedisConnection connection; + + public RedisBackedCache(String hostname, Integer port) { + RedisClient client = RedisClient.create(String.format("redis://%s:%d/0", hostname, port)); + connection = client.connect(); + } + + public String get(String key) { + return connection.sync().get(key); + } + + public void put(String key, String value) { + connection.sync().set(key, value); + } +} diff --git a/examples/spock/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy b/examples/spock/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy new file mode 100644 index 00000000000..d8c056b5070 --- /dev/null +++ b/examples/spock/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy @@ -0,0 +1,36 @@ +package quickstart + +import org.testcontainers.containers.GenericContainer +import spock.lang.Specification + +// complete { +@org.testcontainers.spock.Testcontainers +class RedisBackedCacheIntTest extends Specification { + + private RedisBackedCache underTest + + // init { + public GenericContainer redis = new GenericContainer<>("redis:5.0.3-alpine") + .withExposedPorts(6379) + // } + + void setup() { + String address = redis.containerIpAddress + Integer port = redis.firstMappedPort + + // Now we have an address and port for Redis, no matter where it is running + underTest = new RedisBackedCache(address, port) + } + + void testSimplePutAndGet() { + setup: + underTest.put("test", "example") + + when: + String retrieved = underTest.get("test") + + then: + retrieved == "example" + } +} +// } diff --git a/examples/spock/src/test/groovy/quickstart/RedisBackedCacheIntTestStep0.groovy b/examples/spock/src/test/groovy/quickstart/RedisBackedCacheIntTestStep0.groovy new file mode 100644 index 00000000000..52897c30097 --- /dev/null +++ b/examples/spock/src/test/groovy/quickstart/RedisBackedCacheIntTestStep0.groovy @@ -0,0 +1,25 @@ +package quickstart + +import spock.lang.Ignore +import spock.lang.Specification + +@Ignore("This test class is deliberately invalid, as it relies on a non-existent local Redis") +class RedisBackedCacheIntTestStep0 extends Specification { + private RedisBackedCache underTest + + void setup() { + // Assume that we have Redis running locally? + underTest = new RedisBackedCache("localhost", 6379) + } + + void testSimplePutAndGet() { + setup: + underTest.put("test", "example") + + when: + String retrieved = underTest.get("test") + + then: + retrieved == "example" + } +} diff --git a/examples/spock/src/test/resources/logback-test.xml b/examples/spock/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..6e7cfd092de --- /dev/null +++ b/examples/spock/src/test/resources/logback-test.xml @@ -0,0 +1,27 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + + + + + + + + + PROFILER + DENY + + diff --git a/mkdocs.yml b/mkdocs.yml index 762116a062c..869d0d4b551 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -25,6 +25,7 @@ nav: - Home: index.md - Quickstart: - quickstart/junit_4_quickstart.md + - quickstart/spock_quickstart.md - Features: - features/creating_container.md - features/networking.md From 4bb2dea90c61759c75745c99052c33175f8a2a53 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Tue, 4 Jun 2019 08:28:09 +0000 Subject: [PATCH 0753/5615] Bump lettuce-core from 5.1.1.RELEASE to 5.1.6.RELEASE in /examples (#1526) --- examples/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spock/build.gradle b/examples/spock/build.gradle index 0b727fbd18a..9ac238b54f4 100644 --- a/examples/spock/build.gradle +++ b/examples/spock/build.gradle @@ -8,7 +8,7 @@ repositories { } dependencies { - compile "io.lettuce:lettuce-core:5.1.1.RELEASE" + compile "io.lettuce:lettuce-core:5.1.6.RELEASE" testImplementation 'org.spockframework:spock-core:1.2-groovy-2.5' testImplementation 'org.testcontainers:spock' testImplementation 'ch.qos.logback:logback-classic:1.2.3' From a44f357956e116bcbc0062d22096671150fdf376 Mon Sep 17 00:00:00 2001 From: Sandra Parsick Date: Tue, 4 Jun 2019 13:00:16 +0200 Subject: [PATCH 0754/5615] Add support for tmpfs to ContainerDatabaseDriver (#1500) --- docs/modules/databases/index.md | 15 ++++- .../jdbc/DatabaseDriverTmpfsTest.java | 35 +++++++++++ .../testcontainers/jdbc/ConnectionUrl.java | 59 ++++++++++++++----- .../jdbc/ContainerDatabaseDriver.java | 1 + .../jdbc/ConnectionUrlTest.java | 15 +++++ 5 files changed, 110 insertions(+), 15 deletions(-) create mode 100644 modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java diff --git a/docs/modules/databases/index.md b/docs/modules/databases/index.md index 71d65af7e20..22292875307 100644 --- a/docs/modules/databases/index.md +++ b/docs/modules/databases/index.md @@ -7,7 +7,7 @@ You might want to use Testcontainers' database support: * **Instead of H2 database for DAO unit tests that depend on database features that H2 doesn't emulate.** Testcontainers is not as performant as H2, but does give you the benefit of 100% database compatibility (since it runs a real DB inside of a container). * **Instead of a database running on the local machine or in a VM** for DAO unit tests or end-to-end integration tests that need a database to be present. In this context, the benefit of Testcontainers is that the database always starts in a known state, without any contamination between test runs or on developers' local machines. -!!! note +!!! note Of course, it's still important to have as few tests that hit the database as possible, and make good use of mocks for components higher up the stack. You can obtain a temporary database in one of two ways: @@ -101,3 +101,16 @@ By default database container is being stopped as soon as last connection is clo `jdbc:tc:mysql:5.7.22://hostname/databasename?TC_DAEMON=true` With this parameter database container will keep running even when there're no open connections. + + +#### Running container with tmpfs options + +Container can have `tmpfs` mounts for storing data in host memory. This is useful if you want to speed up your database tests. Be aware that the data will be lost when the container stops. + +To pass this option to the container, add `TC_TMPFS` paramater to the URL as follows: + + `jdbc:tc:postgresql:9.6.8://hostname/databasename?TC_TMPFS=/testtmpfs:rw` + +If you need more than one option, seperate them by comma (e.g. `TC_TMPFS=key:value,key1:value1&other_parameters=foo`). + +For more information about `tmpfs` mount, see [the official Docker documentation](https://docs.docker.com/storage/tmpfs/). diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java new file mode 100644 index 00000000000..07b70020733 --- /dev/null +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java @@ -0,0 +1,35 @@ +package org.testcontainers.jdbc; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import org.junit.Test; +import org.testcontainers.containers.Container; +import org.testcontainers.containers.JdbcDatabaseContainer; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; + +public class DatabaseDriverTmpfsTest { + + @Test + public void tmpfs() throws IOException, InterruptedException, SQLException { + final String jdbcUrl = "jdbc:tc:postgresql:9.6.8://hostname/databasename?TC_TMPFS=/testtmpfs:rw"; + try (Connection ignored = DriverManager.getConnection(jdbcUrl)) { + + JdbcDatabaseContainer container = ContainerDatabaseDriver.getContainer(jdbcUrl); + // check file doesn't exist + String path = "/testtmpfs/test.file"; + Container.ExecResult execResult = container.execInContainer("ls", path); + assertEquals("tmpfs inside container works fine", execResult.getStderr(), + "ls: cannot access '/testtmpfs/test.file': No such file or directory\n"); + // touch && check file does exist + container.execInContainer("touch", path); + execResult = container.execInContainer("ls", path); + assertEquals("tmpfs inside container works fine", execResult.getStdout(), path + "\n"); + } + + } + + +} diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java index 7c5a53ede65..36206b89d82 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java @@ -1,13 +1,20 @@ package org.testcontainers.jdbc; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collector; +import java.util.stream.Collectors; +import java.util.stream.Stream; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; +import static java.util.stream.Collectors.toMap; /** * This is an Immutable class holding JDBC Connection Url and its parsed components, used by {@link ContainerDatabaseDriver}. @@ -16,7 +23,8 @@ * * @author manikmagar */ -@EqualsAndHashCode(of = "url") @Getter +@EqualsAndHashCode(of = "url") +@Getter public class ConnectionUrl { private String url; @@ -48,8 +56,9 @@ public class ConnectionUrl { private Map containerParameters; private Map queryParameters; + private Map tmpfsOptions = new HashMap<>(); - public static ConnectionUrl newInstance(final String url){ + public static ConnectionUrl newInstance(final String url) { ConnectionUrl connectionUrl = new ConnectionUrl(url); connectionUrl.parseUrl(); return connectionUrl; @@ -65,7 +74,8 @@ public static boolean accepts(final String url) { /** * This method applies various REGEX Patterns to parse the URL associated with this instance. - * This is called from a @{@link ConnectionUrl#newInstance(String)} static factory method to create immutable instance of {@link ConnectionUrl}. + * This is called from a @{@link ConnectionUrl#newInstance(String)} static factory method to create immutable instance of + * {@link ConnectionUrl}. * To avoid mutation after class is instantiated, this method should not be publicly accessible. */ private void parseUrl() { @@ -100,14 +110,14 @@ private void parseUrl() { } queryParameters = Collections.unmodifiableMap( - parseQueryParameters( - Optional.ofNullable(urlMatcher.group(5)).orElse(""))); + parseQueryParameters( + Optional.ofNullable(urlMatcher.group(5)).orElse(""))); String query = queryParameters - .entrySet() - .stream() - .map(e -> e.getKey() + "=" + e.getValue()) - .collect(Collectors.joining("&")); + .entrySet() + .stream() + .map(e -> e.getKey() + "=" + e.getValue()) + .collect(Collectors.joining("&")); if (query == null || query.trim().length() == 0) { queryString = Optional.empty(); @@ -117,6 +127,9 @@ private void parseUrl() { containerParameters = Collections.unmodifiableMap(parseContainerParameters()); + tmpfsOptions = parseTmpfsOptions(containerParameters); + + initScriptPath = Optional.ofNullable(containerParameters.get("TC_INITSCRIPT")); Matcher funcMatcher = Patterns.INITFUNCTION_MATCHING_PATTERN.matcher(this.getUrl()); @@ -129,6 +142,19 @@ private void parseUrl() { } + private Map parseTmpfsOptions(Map containerParameters) { + if(!containerParameters.containsKey("TC_TMPFS")){ + return Collections.emptyMap(); + } + + String tmpfsOptions = containerParameters.get("TC_TMPFS"); + + return Stream.of(tmpfsOptions.split(",")) + .collect(toMap( + string -> string.split(":")[0], + string -> string.split(":")[1])); + } + /** * Get the TestContainers Parameters such as Init Function, Init Script path etc. * @@ -160,12 +186,17 @@ private Map parseQueryParameters(final String queryString) { while (matcher.find()) { String key = matcher.group(1); String value = matcher.group(2); - if(!key.matches(Patterns.TC_PARAM_NAME_PATTERN)) results.put(key, value); + if (!key.matches(Patterns.TC_PARAM_NAME_PATTERN)) + results.put(key, value); } return results; } + public Map getTmpfsOptions() { + return Collections.unmodifiableMap(tmpfsOptions); + } + /** * This interface defines the Regex Patterns used by {@link ConnectionUrl}. * diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java index 0f470e36979..6d3df6c7cc5 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java @@ -93,6 +93,7 @@ public synchronized Connection connect(String url, final Properties info) throws for (JdbcDatabaseContainerProvider candidateContainerType : databaseContainers) { if (candidateContainerType.supports(connectionUrl.getDatabaseType())) { container = candidateContainerType.newInstance(connectionUrl); + container.withTmpFs(connectionUrl.getTmpfsOptions()); delegate = container.getJdbcDriverInstance(); } } diff --git a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java index a5c36d8f065..53524eb32b2 100644 --- a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java +++ b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java @@ -49,6 +49,21 @@ public void testConnectionUrl2() { } + @Test + public void testTmpfsOption() { + String urlString = "jdbc:tc:mysql://somehostname/databasename?TC_TMPFS=key:value,key1:value1"; + ConnectionUrl url = ConnectionUrl.newInstance(urlString); + + assertTrue("Connection Parameters set is empty", url.getQueryParameters().isEmpty()); + assertFalse("Container Parameters set is not empty", url.getContainerParameters().isEmpty()); + assertEquals("Container Parameter TC_TMPFS is true", "key:value,key1:value1", url.getContainerParameters().get("TC_TMPFS")); + assertTrue("tmpfs option key exists", url.getTmpfsOptions().containsKey("key")); + assertEquals("tmpfs option key has correct value", "value" , url.getTmpfsOptions().get("key")); + assertTrue("tmpfs option key1 exists", url.getTmpfsOptions().containsKey("key1")); + assertEquals("tmpfs option key1 has correct value", "value1" , url.getTmpfsOptions().get("key1")); + } + + @Test public void testInitScriptPathCapture() { String urlString = "jdbc:tc:mysql:5.6.23://somehostname:3306/databasename?a=b&c=d&TC_INITSCRIPT=somepath/init_mysql.sql"; From e6693bc9f0f88f1ad5ae5b0368b42daf8a22972a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Fri, 7 Jun 2019 07:58:41 +0000 Subject: [PATCH 0755/5615] Bump java-client from 2.7.6 to 2.7.7 in /modules/couchbase (#1536) --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 8cf0f879215..6635462818f 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -2,5 +2,5 @@ description = "Testcontainers :: Couchbase" dependencies { compile project(':testcontainers') - compile 'com.couchbase.client:java-client:2.7.6' + compile 'com.couchbase.client:java-client:2.7.7' } From cdf3501c2be5c5c6b891e30c6642719bb22ba4ba Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Mon, 10 Jun 2019 08:20:49 +0000 Subject: [PATCH 0756/5615] Bump tomcat-jdbc from 9.0.20 to 9.0.21 in /modules/mariadb (#1538) --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 371ec52641d..e1b21c58d04 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -6,6 +6,6 @@ dependencies { testCompile 'org.mariadb.jdbc:mariadb-java-client:2.4.1' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.20' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.21' testCompile 'org.vibur:vibur-dbcp:22.2' } From 287f7512113dfe1531cc54fd674ef9a3ec331ef9 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Mon, 10 Jun 2019 08:58:20 +0000 Subject: [PATCH 0757/5615] Bump tomcat-jdbc from 9.0.20 to 9.0.21 in /modules/mysql (#1539) --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index e48c71734ae..e2b1b416400 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -5,7 +5,7 @@ dependencies { testCompile 'mysql:mysql-connector-java:8.0.16' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.20' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.21' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.vibur:vibur-dbcp:22.2' } From 7de41161a3e3b0dfb737ff8964f44dc70db04b16 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Wed, 12 Jun 2019 08:02:12 +0000 Subject: [PATCH 0758/5615] Bump httpclient from 4.5.8 to 4.5.9 in /modules/junit-jupiter (#1543) --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 591347ca699..59d72d82a21 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -8,7 +8,7 @@ dependencies { testCompile project(':postgresql') testCompile 'com.zaxxer:HikariCP:3.3.1' testCompile 'redis.clients:jedis:3.0.1' - testCompile 'org.apache.httpcomponents:httpclient:4.5.8' + testCompile 'org.apache.httpcomponents:httpclient:4.5.9' testRuntime 'org.postgresql:postgresql:42.2.5' testRuntime 'mysql:mysql-connector-java:8.0.16' From 57ed84a58a0b0a9b93b3175321e89eec0d922a0d Mon Sep 17 00:00:00 2001 From: Dmitry Yakubovsky Date: Wed, 12 Jun 2019 15:14:07 +0200 Subject: [PATCH 0759/5615] Add Zalando SE as one of the users (#1542) --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index cbee344e637..d202ef67cbb 100644 --- a/docs/index.md +++ b/docs/index.md @@ -71,6 +71,7 @@ You can also [check the latest version available on Maven Central](https://searc * [Skyscanner](https://www.skyscanner.net/) - Integration testing against HTTP service mocks and various data stores * [Neo4j-OGM](https://neo4j.com/developer/neo4j-ogm/) - Testing new, reactive client implementations * [Lightbend](https://www.lightbend.com/) - Testing [Alpakka Kafka](https://doc.akka.io/docs/alpakka-kafka/current/) and support in [Alpakka Kafka Testkit](https://doc.akka.io/docs/alpakka-kafka/current/testing.html#testing-with-kafka-in-docker) +* [Zalando SE](https://corporate.zalando.com/en) - Testing core business services ## License From 8d242a385ea472655b77b4ada498cc67f6666aec Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 12 Jun 2019 20:17:05 +0100 Subject: [PATCH 0760/5615] Add minimal docs describing Drone CI usage (#1505) --- .../continuous_integration/dind_patterns.md | 7 +++++++ .../continuous_integration/drone.md | 5 ++++- mkdocs.yml | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/supported_docker_environment/continuous_integration/dind_patterns.md b/docs/supported_docker_environment/continuous_integration/dind_patterns.md index 38bcd82c752..1534949e1bd 100644 --- a/docs/supported_docker_environment/continuous_integration/dind_patterns.md +++ b/docs/supported_docker_environment/continuous_integration/dind_patterns.md @@ -47,3 +47,10 @@ tests: - ~/.m2:/root/.m2 command: mvn test ``` + +## Docker-in-Docker + +While Docker-in-Docker (DinD) is generally considered an instrument of last resort, it is necessary for some CI environments. + +[Drone CI](./drone.md) is one such example. Testcontainers has a Docker-in-Docker plugin (build image) for use with Drone, +which could be used as inspiration for setting up other similar testing using DinD. diff --git a/docs/supported_docker_environment/continuous_integration/drone.md b/docs/supported_docker_environment/continuous_integration/drone.md index 2549bdf4f48..f4a1dd2bc6a 100644 --- a/docs/supported_docker_environment/continuous_integration/drone.md +++ b/docs/supported_docker_environment/continuous_integration/drone.md @@ -1,3 +1,6 @@ # Drone CI -> TODO +Drone CI 0.8 is supported via the use of a general purpose Docker-in-Docker plugin. + +Please see [testcontainers/dind-drone-plugin](https://github.com/testcontainers/dind-drone-plugin) for further details and usage instructions. + diff --git a/mkdocs.yml b/mkdocs.yml index 869d0d4b551..6b38d82eff4 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -70,6 +70,7 @@ nav: - Continuous Integration: - supported_docker_environment/continuous_integration/dind_patterns.md - supported_docker_environment/continuous_integration/circle_ci.md + - supported_docker_environment/continuous_integration/drone.md - supported_docker_environment/continuous_integration/gitlab_ci.md - supported_docker_environment/continuous_integration/bitbucket_pipelines.md - supported_docker_environment/windows.md From 271835edd0f0afa875ad79146faa9f207d4c0a3c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Tue, 18 Jun 2019 08:07:52 +0000 Subject: [PATCH 0761/5615] Bump mariadb-java-client from 2.4.1 to 2.4.2 in /modules/mariadb (#1551) --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index e1b21c58d04..0068368b8a3 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: JDBC :: MariaDB" dependencies { compile project(':jdbc') - testCompile 'org.mariadb.jdbc:mariadb-java-client:2.4.1' + testCompile 'org.mariadb.jdbc:mariadb-java-client:2.4.2' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.21' From 4f56c79a9175b24934eb759f51742baa3925ed7f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Tue, 18 Jun 2019 08:58:56 +0000 Subject: [PATCH 0762/5615] Bump httpclient from 4.5.8 to 4.5.9 in /modules/spock (#1545) --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 6076788dbc4..c11850eacc6 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -11,7 +11,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') testCompile 'com.zaxxer:HikariCP:3.3.1' - testCompile 'org.apache.httpcomponents:httpclient:4.5.8' + testCompile 'org.apache.httpcomponents:httpclient:4.5.9' testRuntime 'org.postgresql:postgresql:42.2.5' testRuntime 'mysql:mysql-connector-java:8.0.16' From b6f42a343960ba0542e7b48c0148ef8557db6e9a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Wed, 19 Jun 2019 06:01:29 +0000 Subject: [PATCH 0763/5615] Bump lettuce-core from 5.1.6.RELEASE to 5.1.7.RELEASE in /examples (#1554) --- examples/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spock/build.gradle b/examples/spock/build.gradle index 9ac238b54f4..cc04f74c90d 100644 --- a/examples/spock/build.gradle +++ b/examples/spock/build.gradle @@ -8,7 +8,7 @@ repositories { } dependencies { - compile "io.lettuce:lettuce-core:5.1.6.RELEASE" + compile "io.lettuce:lettuce-core:5.1.7.RELEASE" testImplementation 'org.spockframework:spock-core:1.2-groovy-2.5' testImplementation 'org.testcontainers:spock' testImplementation 'ch.qos.logback:logback-classic:1.2.3' From b8126ac70345c1d6eda32a7375e1ebf11081911c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Wed, 19 Jun 2019 06:35:58 +0000 Subject: [PATCH 0764/5615] Bump org.springframework.boot in /examples (#1556) --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index 0aa55093dca..d4f3c132896 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.1.5.RELEASE' + id 'org.springframework.boot' version '2.1.6.RELEASE' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index f45b0e51c11..52767c7a9ca 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.1.5.RELEASE' + id 'org.springframework.boot' version '2.1.6.RELEASE' } apply plugin: 'io.spring.dependency-management' From 1862873051d0ea12d6acc80e3ac98f40c7253ee1 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Wed, 19 Jun 2019 07:22:38 +0000 Subject: [PATCH 0765/5615] Bump cucumber-junit from 4.3.1 to 4.4.0 in /examples (#1555) --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index bab77700eff..3381ecab567 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' implementation 'io.cucumber:cucumber-java:4.3.1' - testImplementation 'io.cucumber:cucumber-junit:4.3.1' + testImplementation 'io.cucumber:cucumber-junit:4.4.0' testImplementation 'org.testcontainers:selenium' } From d44c83289ede050b1f0a87bb0d765047e5fd7d6f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Wed, 19 Jun 2019 08:19:01 +0000 Subject: [PATCH 0766/5615] Bump cucumber-java from 4.3.1 to 4.4.0 in /examples (#1553) --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 3381ecab567..6d6e6385a60 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' - implementation 'io.cucumber:cucumber-java:4.3.1' + implementation 'io.cucumber:cucumber-java:4.4.0' testImplementation 'io.cucumber:cucumber-junit:4.4.0' testImplementation 'org.testcontainers:selenium' } From ef91a5c126bfa989ee5a962fc87904b548d3a7f8 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Thu, 20 Jun 2019 06:11:00 +0000 Subject: [PATCH 0767/5615] Bump postgresql from 42.2.5 to 42.2.6 in /modules/spock (#1558) --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index c11850eacc6..74c6b6fd5db 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -13,6 +13,6 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.3.1' testCompile 'org.apache.httpcomponents:httpclient:4.5.9' - testRuntime 'org.postgresql:postgresql:42.2.5' + testRuntime 'org.postgresql:postgresql:42.2.6' testRuntime 'mysql:mysql-connector-java:8.0.16' } From b880ef0c4ce7d94136edd1dacd82bebc88367aa9 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Thu, 20 Jun 2019 07:10:46 +0000 Subject: [PATCH 0768/5615] Bump postgresql from 42.2.5 to 42.2.6 in /modules/postgresql (#1560) --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 0ef6df998e3..1a5980dee55 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: JDBC :: PostgreSQL" dependencies { compile project(':jdbc') - testCompile 'org.postgresql:postgresql:42.2.5' + testCompile 'org.postgresql:postgresql:42.2.6' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' } From 10c71d8188d1fc95416745d6f0469ee2b3452d75 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Thu, 20 Jun 2019 07:45:47 +0000 Subject: [PATCH 0769/5615] Bump postgresql from 42.2.5 to 42.2.6 in /examples (#1561) --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 8522878f5ec..ce0eff58a28 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.26' implementation 'com.squareup.okhttp3:okhttp:3.14.2' implementation 'org.json:json:20180813' - testImplementation 'org.postgresql:postgresql:42.2.5' + testImplementation 'org.postgresql:postgresql:42.2.6' testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.testcontainers:postgresql' } From e690b938f4180782a5003f83840e90d642b91569 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2019 09:47:57 +0200 Subject: [PATCH 0770/5615] Bump mockserver-client-java from 5.5.1 to 5.5.4 in /modules/mockserver (#1438) Also increase the used image tag accordingly. --- modules/mockserver/build.gradle | 2 +- .../org/testcontainers/containers/MockServerContainer.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/mockserver/build.gradle b/modules/mockserver/build.gradle index 8855121360c..b463997fed5 100644 --- a/modules/mockserver/build.gradle +++ b/modules/mockserver/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: MockServer" dependencies { compile project(':testcontainers') - testCompile 'org.mock-server:mockserver-client-java:5.5.1' + testCompile 'org.mock-server:mockserver-client-java:5.5.4' } diff --git a/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java b/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java index f191adea06d..f1f9dc42d19 100644 --- a/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java +++ b/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java @@ -5,7 +5,7 @@ @Slf4j public class MockServerContainer extends GenericContainer { - public static final String VERSION = "5.5.1"; + public static final String VERSION = "5.5.4"; public static final int PORT = 1080; @@ -15,7 +15,7 @@ public MockServerContainer() { public MockServerContainer(String version) { super("jamesdbloom/mockserver:mockserver-" + version); - withCommand("/opt/mockserver/run_mockserver.sh -logLevel INFO -serverPort " + PORT); + withCommand("-logLevel INFO -serverPort " + PORT); addExposedPorts(PORT); } From c5e4e643382022543244d988cff89a9d5656bdb6 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2019 08:27:41 +0000 Subject: [PATCH 0771/5615] Bump postgresql from 42.2.5 to 42.2.6 in /modules/junit-jupiter (#1559) --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 59d72d82a21..9860686eed7 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -10,7 +10,7 @@ dependencies { testCompile 'redis.clients:jedis:3.0.1' testCompile 'org.apache.httpcomponents:httpclient:4.5.9' - testRuntime 'org.postgresql:postgresql:42.2.5' + testRuntime 'org.postgresql:postgresql:42.2.6' testRuntime 'mysql:mysql-connector-java:8.0.16' testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.4.2' } From 61595018f232278cf8aa95ad2363e3a2bdf974ab Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 25 Jun 2019 05:54:46 +0000 Subject: [PATCH 0772/5615] Bump kafka-clients from 2.2.1 to 2.3.0 in /modules/kafka (#1565) --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index 29cef0b4713..10013a6e11e 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Kafka" dependencies { compile project(':testcontainers') - testCompile 'org.apache.kafka:kafka-clients:2.2.1' + testCompile 'org.apache.kafka:kafka-clients:2.3.0' testCompile 'org.assertj:assertj-core:3.12.2' testCompile 'com.google.guava:guava:23.0' } From 67ca611a4a3509130663b0ccd54195b8d1014117 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2019 06:26:01 +0000 Subject: [PATCH 0773/5615] Bump junit-jupiter-engine from 5.4.2 to 5.5.0 in /modules/junit-jupiter (#1577) --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 9860686eed7..56a11f60ffe 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -12,7 +12,7 @@ dependencies { testRuntime 'org.postgresql:postgresql:42.2.6' testRuntime 'mysql:mysql-connector-java:8.0.16' - testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.4.2' + testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.5.0' } test { From 909f0092bb01781579b04a37d5fadcebabb9c9ec Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2019 07:38:46 +0000 Subject: [PATCH 0774/5615] Bump junit-jupiter-api from 5.4.2 to 5.5.0 in /modules/junit-jupiter (#1578) --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 56a11f60ffe..baab3b68bb8 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -2,7 +2,7 @@ description = "Testcontainers :: JUnit Jupiter Extension" dependencies { compile project(':testcontainers') - compile 'org.junit.jupiter:junit-jupiter-api:5.4.2' + compile 'org.junit.jupiter:junit-jupiter-api:5.5.0' testCompile project(':mysql') testCompile project(':postgresql') From d5c0dcae4083038bb02c7248d09c672c3b5814b3 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2019 08:29:27 +0000 Subject: [PATCH 0775/5615] Bump cucumber-junit from 4.4.0 to 4.5.0 in /examples (#1579) --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 6d6e6385a60..8e6c52f451e 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' implementation 'io.cucumber:cucumber-java:4.4.0' - testImplementation 'io.cucumber:cucumber-junit:4.4.0' + testImplementation 'io.cucumber:cucumber-junit:4.5.0' testImplementation 'org.testcontainers:selenium' } From a1f1d242b196a086f8660b260c4e3c969228de4c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2019 09:50:47 +0000 Subject: [PATCH 0776/5615] Bump cucumber-java from 4.4.0 to 4.5.0 in /examples (#1580) --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 8e6c52f451e..c590d68875b 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' - implementation 'io.cucumber:cucumber-java:4.4.0' + implementation 'io.cucumber:cucumber-java:4.5.0' testImplementation 'io.cucumber:cucumber-junit:4.5.0' testImplementation 'org.testcontainers:selenium' } From 67ca5e717cc2ea88d1fda79286bfef619aca4e25 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2019 05:50:48 +0000 Subject: [PATCH 0777/5615] Bump cucumber-java from 4.5.0 to 4.5.1 in /examples (#1583) --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index c590d68875b..48a016a30c8 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' - implementation 'io.cucumber:cucumber-java:4.5.0' + implementation 'io.cucumber:cucumber-java:4.5.1' testImplementation 'io.cucumber:cucumber-junit:4.5.0' testImplementation 'org.testcontainers:selenium' } From d5efe5be77e8f868faaf2cc8e044d740b9c65b9a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2019 07:08:13 +0000 Subject: [PATCH 0778/5615] Bump cucumber-junit from 4.5.0 to 4.5.1 in /examples (#1584) --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 48a016a30c8..505aad82ae4 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' implementation 'io.cucumber:cucumber-java:4.5.1' - testImplementation 'io.cucumber:cucumber-junit:4.5.0' + testImplementation 'io.cucumber:cucumber-junit:4.5.1' testImplementation 'org.testcontainers:selenium' } From ace97aaa4ede14bc559c786552651acd4c553a48 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2019 05:44:27 +0000 Subject: [PATCH 0779/5615] Bump neo4j from 3.5.6 to 3.5.7 in /modules/neo4j (#1586) --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 68a52d115cd..9c5410aa179 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -25,7 +25,7 @@ task customNeo4jPluginJar(type: Jar) { test.dependsOn customNeo4jPluginJar dependencies { - customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.6" + customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.7" compile project(":testcontainers") From efabf3d1300da7b735453e916df40da6d5fc9337 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2019 06:16:51 +0000 Subject: [PATCH 0780/5615] Bump pulsar-client from 2.3.2 to 2.4.0 in /modules/pulsar (#1587) --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 301d6f81da2..e8e589cda72 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Pulsar" dependencies { compile project(':testcontainers') - testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.3.2' + testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.4.0' testCompile group: 'org.assertj', name: 'assertj-core', version: '3.12.2' } From 52f090acd9d6c97fb534071456164f76868b749d Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 6 Jul 2019 17:23:19 +0100 Subject: [PATCH 0781/5615] =?UTF-8?q?Revert=20"Bump=20pulsar-client=20from?= =?UTF-8?q?=202.3.2=20to=202.4.0=20in=20/modules/puls=E2=80=A6=20(#1589)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit efabf3d1300da7b735453e916df40da6d5fc9337. --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index e8e589cda72..301d6f81da2 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Pulsar" dependencies { compile project(':testcontainers') - testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.4.0' + testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.3.2' testCompile group: 'org.assertj', name: 'assertj-core', version: '3.12.2' } From 06ea95be1e288622aedb3a35ea585575654e5a9a Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sat, 6 Jul 2019 23:18:18 +0200 Subject: [PATCH 0782/5615] Deprecate fields that are derived from `containerInfo` (#1585) --- .../containers/DockerComposeContainer.java | 2 +- .../containers/GenericContainer.java | 16 +++++++++++++++- .../containers/VncRecordingContainer.java | 6 +++--- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 67674796cdd..ff92289431e 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -559,7 +559,7 @@ public void invoke() { AuditLogger.doComposeLog(this.getCommandParts(), this.getEnv()); - final Integer exitCode = this.dockerClient.inspectContainerCmd(containerId) + final Integer exitCode = this.dockerClient.inspectContainerCmd(getContainerId()) .exec() .getState() .getExitCode(); diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 936d880d1a8..8045d47c720 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -143,13 +143,23 @@ public class GenericContainer> @Setter(AccessLevel.NONE) protected Info dockerDaemonInfo = null; - /* + /** * Set during container startup + * // TODO make it private + * + * @deprecated use {@link ContainerState#getContainerId()} */ @Setter(AccessLevel.NONE) + @Deprecated protected String containerId; + /** + * Set during container startup + * + * @deprecated use {@link GenericContainer#getContainerInfo()} + */ @Setter(AccessLevel.NONE) + @Deprecated protected String containerName; @Setter(AccessLevel.NONE) @@ -188,6 +198,10 @@ public GenericContainer(@NonNull final Future image) { this.image = image; } + public String getContainerId() { + return containerId; + } + /** * Starts the container using docker, pulling an image if necessary. */ diff --git a/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java b/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java index b8a26bfe65b..1b06215b706 100644 --- a/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java +++ b/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java @@ -78,7 +78,7 @@ public void onNext(Frame frame) { }; try ( - Closeable __ = dockerClient.logContainerCmd(containerId) + Closeable __ = dockerClient.logContainerCmd(getContainerId()) .withFollowStream(true) .withSince(0) .withStdErr(true) @@ -122,7 +122,7 @@ protected void configure() { @SneakyThrows public InputStream streamRecording() { TarArchiveInputStream archiveInputStream = new TarArchiveInputStream( - dockerClient.copyArchiveFromContainerCmd(containerId, RECORDING_FILE_NAME).exec() + dockerClient.copyArchiveFromContainerCmd(getContainerId(), RECORDING_FILE_NAME).exec() ); archiveInputStream.getNextEntry(); return archiveInputStream; @@ -134,4 +134,4 @@ public void saveRecordingToFile(File file) { Files.copy(inputStream, file.toPath(), StandardCopyOption.REPLACE_EXISTING); } } -} \ No newline at end of file +} From 7fae384751da6101a451aa7d0a753ebd6dbf13ce Mon Sep 17 00:00:00 2001 From: HaMatthias <38075918+HaMatthias@users.noreply.github.com> Date: Sat, 6 Jul 2019 23:19:45 +0200 Subject: [PATCH 0783/5615] Docker Compose: allow specific services to be started (#1528) --- .../containers/DockerComposeContainer.java | 12 ++++++- .../junit/DockerComposeServiceTest.java | 33 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 core/src/test/java/org/testcontainers/junit/DockerComposeServiceTest.java diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index ff92289431e..46f4c561e5d 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -75,6 +75,8 @@ public class DockerComposeContainer> e private static final Object MUTEX = new Object(); + private List services = new ArrayList<>(); + /** * Properties that should be passed through to all Compose and ambassador containers (not * necessarily to containers that are spawned by Compose itself) @@ -159,10 +161,18 @@ private void pullImages() { runWithCompose("pull"); } + public SELF withServices(@NonNull String... services) { + this.services = Arrays.asList(services); + return self(); + } private void createServices() { // Run the docker-compose container, which starts up the services - runWithCompose("up -d"); + if(services.isEmpty()) { + runWithCompose("up -d"); + } else { + runWithCompose("up -d " + String.join(" ", services)); + } } private void waitUntilServiceStarted() { diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeServiceTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeServiceTest.java new file mode 100644 index 00000000000..c0360050d27 --- /dev/null +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeServiceTest.java @@ -0,0 +1,33 @@ +package org.testcontainers.junit; + +import org.junit.Rule; +import org.junit.Test; +import org.testcontainers.containers.DockerComposeContainer; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; + +import java.io.File; + +public class DockerComposeServiceTest extends BaseDockerComposeTest { + + @Rule + public DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/compose-test.yml")) + .withServices("redis") + .withExposedService("redis_1", REDIS_PORT); + + + @Override + protected DockerComposeContainer getEnvironment() { + return environment; + } + + @Test(expected = NullPointerException.class) + public void testDbIsNotStarting() { + environment.getServicePort("db_1", 10001); + } + + @Test + public void testRedisIsStarting() { + assertNotNull("Redis server started", environment.getServicePort("redis_1", REDIS_PORT)); + } +} From c7945503d99bb573dc05e769a924aebcf8d0a5ee Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 6 Jul 2019 22:32:24 +0100 Subject: [PATCH 0784/5615] Bump httpclient from 4.5.8 to 4.5.9 in /core (#1544) Bumps httpclient from 4.5.8 to 4.5.9. Signed-off-by: dependabot-preview[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index a77720d66df..30fd4d6167e 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -100,7 +100,7 @@ dependencies { exclude(group: 'org.slf4j') } - testCompile 'org.apache.httpcomponents:httpclient:4.5.8' + testCompile 'org.apache.httpcomponents:httpclient:4.5.9' testCompile 'redis.clients:jedis:3.0.1' testCompile 'com.rabbitmq:amqp-client:5.7.1' testCompile 'org.mongodb:mongo-java-driver:3.10.2' From 64f3906080961fb11e2023a4fe31adb2a5090a22 Mon Sep 17 00:00:00 2001 From: Tobias Gesellchen Date: Mon, 8 Jul 2019 14:00:59 +0200 Subject: [PATCH 0785/5615] Add Europace AG as user (#1591) --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index d202ef67cbb..89e70611f37 100644 --- a/docs/index.md +++ b/docs/index.md @@ -72,6 +72,7 @@ You can also [check the latest version available on Maven Central](https://searc * [Neo4j-OGM](https://neo4j.com/developer/neo4j-ogm/) - Testing new, reactive client implementations * [Lightbend](https://www.lightbend.com/) - Testing [Alpakka Kafka](https://doc.akka.io/docs/alpakka-kafka/current/) and support in [Alpakka Kafka Testkit](https://doc.akka.io/docs/alpakka-kafka/current/testing.html#testing-with-kafka-in-docker) * [Zalando SE](https://corporate.zalando.com/en) - Testing core business services +* [Europace AG](https://tech.europace.de/) - Integration testing for databases and micro services ## License From ed0143333b386d551ae0fbdf00e11e707e2cf0a1 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 8 Jul 2019 16:51:53 +0200 Subject: [PATCH 0786/5615] Azure pipelines (#1363) --- azure-pipelines.yml | 21 +++ build.gradle | 1 + .../junit/DockerNetworkModeTest.java | 85 ++++------ .../testcontainers/jdbc/JDBCDriverTest.java | 156 ++++++++---------- .../jdbc/JDBCDriverWithPoolTest.java | 3 +- 5 files changed, 127 insertions(+), 139 deletions(-) create mode 100644 azure-pipelines.yml diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 00000000000..abcd155010d --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,21 @@ +steps: +- task: Gradle@2 + displayName: Build core + inputs: + gradleWrapperFile: 'gradlew' + jdkVersionOption: '1.11' + options: '--no-daemon --continue' + tasks: 'testcontainers:check' + publishJUnitResults: true + testResultsFiles: '**/TEST-*.xml' +- task: Gradle@2 + displayName: Build modules + inputs: + gradleWrapperFile: 'gradlew' + jdkVersionOption: '1.11' + options: '--no-daemon --continue -x testcontainers:check' + tasks: 'check' + publishJUnitResults: true + testResultsFiles: '**/TEST-*.xml' + + diff --git a/build.gradle b/build.gradle index 27f1d8859f1..235c1ae22f1 100644 --- a/build.gradle +++ b/build.gradle @@ -20,6 +20,7 @@ subprojects { sourceCompatibility = 1.8 targetCompatibility = 1.8 compileJava.options.encoding = 'UTF-8' + compileTestJava.options.encoding = 'UTF-8' lombok { version = '1.18.6' diff --git a/core/src/test/java/org/testcontainers/junit/DockerNetworkModeTest.java b/core/src/test/java/org/testcontainers/junit/DockerNetworkModeTest.java index 24215094e08..873160bdbc9 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerNetworkModeTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerNetworkModeTest.java @@ -1,73 +1,50 @@ package org.testcontainers.junit; +import com.github.dockerjava.api.model.NetworkSettings; +import lombok.extern.slf4j.Slf4j; import org.junit.*; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.output.OutputFrame; -import org.testcontainers.containers.output.ToStringConsumer; -import org.testcontainers.containers.output.WaitingConsumer; -import org.testcontainers.utility.TestEnvironment; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.function.Consumer; +import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; /** * Simple tests of named network modes - more may be possible, but may not be reproducible * without other setup steps. */ +@Slf4j public class DockerNetworkModeTest { - @BeforeClass - public static void checkVersion() { - Assume.assumeTrue(TestEnvironment.dockerApiAtLeast("1.22")); - } - - @ClassRule - public static GenericContainer noNetwork = new GenericContainer("alpine:3.2") - .withNetworkMode("none") - .withCommand("ping -c 5 www.google.com"); - - @ClassRule - public static GenericContainer hostNetwork = new GenericContainer("alpine:3.2") - .withNetworkMode("host") - .withCommand("ping -c 5 www.google.com"); - - @ClassRule - public static GenericContainer bridgedNetwork = new GenericContainer("alpine:3.2") - .withNetworkMode("bridge") - .withCommand("ping -c 5 www.google.com"); - - @Test - public void testNoNetworkContainer() throws TimeoutException { - String output = getContainerOutput(noNetwork); - - assertTrue("'none' network causes a network access error", output.contains("bad address")); - } - @Test - public void testHostNetworkContainer() throws TimeoutException { - String output = getContainerOutput(hostNetwork); - - assertTrue("'host' network can access the internet", output.contains("seq=1")); + public void testNoNetworkContainer() { + try ( + GenericContainer container = new GenericContainer() + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .withCommand("true") + .withNetworkMode("none") + ) { + container.start(); + NetworkSettings networkSettings = container.getContainerInfo().getNetworkSettings(); + + assertEquals("only one network is set", 1, networkSettings.getNetworks().size()); + assertTrue("network is 'none'", networkSettings.getNetworks().containsKey("none")); + } } @Test - public void testBridgedNetworkContainer() throws TimeoutException { - String output = getContainerOutput(bridgedNetwork); - - assertTrue("'bridge' network can access the internet", output.contains("seq=1")); - } - - private String getContainerOutput(GenericContainer container) throws TimeoutException { - WaitingConsumer waitingConsumer = new WaitingConsumer(); - ToStringConsumer toStringConsumer = new ToStringConsumer(); - Consumer composedConsumer = waitingConsumer.andThen(toStringConsumer); - - container.followOutput(composedConsumer); - waitingConsumer.waitUntilEnd(10, TimeUnit.SECONDS); - - return toStringConsumer.toUtf8String(); + public void testHostNetworkContainer() { + try ( + GenericContainer container = new GenericContainer() + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .withCommand("true") + .withNetworkMode("host") + ) { + container.start(); + NetworkSettings networkSettings = container.getContainerInfo().getNetworkSettings(); + + assertEquals("only one network is set", 1, networkSettings.getNetworks().size()); + assertTrue("network is 'host'", networkSettings.getNetworks().containsKey("host")); + } } } diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java index 40cb707a2d0..30b224ab6a6 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java @@ -1,6 +1,5 @@ package org.testcontainers.jdbc; -import com.googlecode.junittoolbox.ParallelParameterized; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import org.apache.commons.dbutils.QueryRunner; @@ -22,7 +21,7 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -@RunWith(ParallelParameterized.class) +@RunWith(Parameterized.class) public class JDBCDriverTest { private enum Options { @@ -86,98 +85,91 @@ public static void testCleanup() { @Test public void test() throws SQLException { - performSimpleTest(jdbcUrl); + try (HikariDataSource dataSource = getDataSource(jdbcUrl, 1)) { + performSimpleTest(dataSource); - if (options.contains(Options.ScriptedSchema)) { - performTestForScriptedSchema(jdbcUrl); - } + if (options.contains(Options.ScriptedSchema)) { + performTestForScriptedSchema(dataSource); + } - if (options.contains(Options.JDBCParams)) { - performTestForJDBCParamUsage(jdbcUrl); - } + if (options.contains(Options.JDBCParams)) { + performTestForJDBCParamUsage(dataSource); + } - if (options.contains(Options.CharacterSet)) { - //Called twice to ensure that the query string parameters are used when - //connections are created from cached containers. - performSimpleTestWithCharacterSet(jdbcUrl); - performSimpleTestWithCharacterSet(jdbcUrl); + if (options.contains(Options.CharacterSet)) { + performSimpleTestWithCharacterSet(jdbcUrl); - performTestForCharacterEncodingForInitialScriptConnection(jdbcUrl); - } + performTestForCharacterEncodingForInitialScriptConnection(dataSource); + } - if (options.contains(Options.CustomIniFile)) { - performTestForCustomIniFile(jdbcUrl); + if (options.contains(Options.CustomIniFile)) { + performTestForCustomIniFile(dataSource); + } } } - private void performSimpleTest(String jdbcUrl) throws SQLException { - try (HikariDataSource dataSource = getDataSource(jdbcUrl, 1)) { - boolean result = new QueryRunner(dataSource, options.contains(Options.PmdKnownBroken)).query("SELECT 1", rs -> { - rs.next(); - int resultSetInt = rs.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); - return true; - }); - - assertTrue("The database returned a record as expected", result); - } - } + private void performSimpleTest(HikariDataSource dataSource) throws SQLException { + boolean result = new QueryRunner(dataSource, options.contains(Options.PmdKnownBroken)).query("SELECT 1", rs -> { + rs.next(); + int resultSetInt = rs.getInt(1); + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + return true; + }); - private void performTestForScriptedSchema(String jdbcUrl) throws SQLException { - try (HikariDataSource dataSource = getDataSource(jdbcUrl, 1)) { - boolean result = new QueryRunner(dataSource).query("SELECT foo FROM bar WHERE foo LIKE '%world'", rs -> { - rs.next(); - String resultSetString = rs.getString(1); - assertEquals("A basic SELECT query succeeds where the schema has been applied from a script", "hello world", resultSetString); - return true; - }); - } + assertTrue("The database returned a record as expected", result); } - private void performTestForJDBCParamUsage(String jdbcUrl) throws SQLException { - try (HikariDataSource dataSource = getDataSource(jdbcUrl, 1)) { - boolean result = new QueryRunner(dataSource).query("select CURRENT_USER", rs -> { - rs.next(); - String resultUser = rs.getString(1); - // Not all databases (eg. Postgres) return @% at the end of user name. We just need to make sure the user name matches. - if (resultUser.endsWith("@%")) { - resultUser = resultUser.substring(0, resultUser.length() - 2); - } - assertEquals("User from query param is created.", "someuser", resultUser); - return true; - }); + private void performTestForScriptedSchema(HikariDataSource dataSource) throws SQLException { + boolean result = new QueryRunner(dataSource).query("SELECT foo FROM bar WHERE foo LIKE '%world'", rs -> { + rs.next(); + String resultSetString = rs.getString(1); + assertEquals("A basic SELECT query succeeds where the schema has been applied from a script", "hello world", resultSetString); + return true; + }); - assertTrue("The database returned a record as expected", result); + assertTrue("The database returned a record as expected", result); + } - String databaseQuery = "SELECT DATABASE()"; - // Postgres does not have Database() as a function - String databaseType = ConnectionUrl.newInstance(jdbcUrl).getDatabaseType(); - if (databaseType.equalsIgnoreCase("postgresql") || databaseType.equalsIgnoreCase("postgis")) { - databaseQuery = "SELECT CURRENT_DATABASE()"; + private void performTestForJDBCParamUsage(HikariDataSource dataSource) throws SQLException { + boolean result = new QueryRunner(dataSource).query("select CURRENT_USER", rs -> { + rs.next(); + String resultUser = rs.getString(1); + // Not all databases (eg. Postgres) return @% at the end of user name. We just need to make sure the user name matches. + if (resultUser.endsWith("@%")) { + resultUser = resultUser.substring(0, resultUser.length() - 2); } + assertEquals("User from query param is created.", "someuser", resultUser); + return true; + }); - result = new QueryRunner(dataSource).query(databaseQuery, rs -> { - rs.next(); - String resultDB = rs.getString(1); - assertEquals("Database name from URL String is used.", "databasename", resultDB); - return true; - }); + assertTrue("The database returned a record as expected", result); - assertTrue("The database returned a record as expected", result); + String databaseQuery = "SELECT DATABASE()"; + // Postgres does not have Database() as a function + String databaseType = ConnectionUrl.newInstance(jdbcUrl).getDatabaseType(); + if (databaseType.equalsIgnoreCase("postgresql") || databaseType.equalsIgnoreCase("postgis")) { + databaseQuery = "SELECT CURRENT_DATABASE()"; } + + result = new QueryRunner(dataSource).query(databaseQuery, rs -> { + rs.next(); + String resultDB = rs.getString(1); + assertEquals("Database name from URL String is used.", "databasename", resultDB); + return true; + }); + + assertTrue("The database returned a record as expected", result); } - private void performTestForCharacterEncodingForInitialScriptConnection(String jdbcUrl) throws SQLException { - try (HikariDataSource dataSource = getDataSource(jdbcUrl, 1)) { - boolean result = new QueryRunner(dataSource).query("SELECT foo FROM bar WHERE foo LIKE '%мир'", rs -> { - rs.next(); - String resultSetString = rs.getString(1); - assertEquals("A SELECT query succeed and the correct charset has been applied for the init script", "привет мир", resultSetString); - return true; - }); + private void performTestForCharacterEncodingForInitialScriptConnection(HikariDataSource dataSource) throws SQLException { + boolean result = new QueryRunner(dataSource).query("SELECT foo FROM bar WHERE foo LIKE '%мир'", rs -> { + rs.next(); + String resultSetString = rs.getString(1); + assertEquals("A SELECT query succeed and the correct charset has been applied for the init script", "привет мир", resultSetString); + return true; + }); - assertTrue("The database returned a record as expected", result); - } + assertTrue("The database returned a record as expected", result); } /** @@ -207,18 +199,16 @@ private HikariDataSource verifyCharacterSet(String jdbcUrl) throws SQLException return dataSource; } - private void performTestForCustomIniFile(final String jdbcUrl) throws SQLException { + private void performTestForCustomIniFile(HikariDataSource dataSource) throws SQLException { assumeFalse(SystemUtils.IS_OS_WINDOWS); - try (HikariDataSource ds = getDataSource(jdbcUrl, 1)) { - Statement statement = ds.getConnection().createStatement(); - statement.execute("SELECT @@GLOBAL.innodb_file_format"); - ResultSet resultSet = statement.getResultSet(); + Statement statement = dataSource.getConnection().createStatement(); + statement.execute("SELECT @@GLOBAL.innodb_file_format"); + ResultSet resultSet = statement.getResultSet(); - assertTrue("The query returns a result", resultSet.next()); - String result = resultSet.getString(1); + assertTrue("The query returns a result", resultSet.next()); + String result = resultSet.getString(1); - assertEquals("The InnoDB file format has been set by the ini file content", "Barracuda", result); - } + assertEquals("The InnoDB file format has been set by the ini file content", "Barracuda", result); } private HikariDataSource getDataSource(String jdbcUrl, int poolSize) { diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverWithPoolTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverWithPoolTest.java index c40721223b0..53daa1316f5 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverWithPoolTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverWithPoolTest.java @@ -1,6 +1,5 @@ package org.testcontainers.jdbc; -import com.googlecode.junittoolbox.ParallelParameterized; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import org.apache.commons.dbutils.QueryRunner; @@ -25,7 +24,7 @@ /** * */ -@RunWith(ParallelParameterized.class) +@RunWith(Parameterized.class) public class JDBCDriverWithPoolTest { public static final String URL = "jdbc:tc:mysql:5://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverWithPoolTest::sampleInitFunction"; From 68d538937ff9d329cb55ca8f7ea2d6390ae80abf Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 9 Jul 2019 10:39:13 +0200 Subject: [PATCH 0787/5615] Allocate lower shared memory in tests (#1597) --- .../testcontainers/junit/GenericContainerRuleTest.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java index a991ebcf60a..6bbd71961a6 100644 --- a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java +++ b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java @@ -403,15 +403,13 @@ public void addExposedPortAfterWithExposedPortsTest() { @Test public void sharedMemorySetTest() { - try (GenericContainer containerWithSharedMemory = new GenericContainer("busybox:1.29") - .withSharedMemorySize(1024L * FileUtils.ONE_MB)) { + try (GenericContainer containerWithSharedMemory = new GenericContainer() + .withSharedMemorySize(42L * FileUtils.ONE_MB)) { containerWithSharedMemory.start(); - HostConfig hostConfig = - containerWithSharedMemory.getDockerClient().inspectContainerCmd(containerWithSharedMemory.getContainerId()) - .exec().getHostConfig(); - assertEquals("Shared memory not set on container", hostConfig.getShmSize(), 1024 * FileUtils.ONE_MB); + HostConfig hostConfig = containerWithSharedMemory.getContainerInfo().getHostConfig(); + assertEquals("Shared memory not set on container", hostConfig.getShmSize(), 42L * FileUtils.ONE_MB); } } } From 30574ef7a0aec19223c63d52702c4d5b0a478dfc Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 9 Jul 2019 10:44:33 +0100 Subject: [PATCH 0788/5615] Module contribution policy (#1576) --- CONTRIBUTING.md | 16 ++------- docs/contributing.md | 80 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 13 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9a9f1e43b55..ffa95a477db 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,15 +1,5 @@ # Contributing -* Star the project on [Github](https://github.com/testcontainers/testcontainers-java) and help spread the word :) -* Join our Slack: http://slack.testcontainers.org -* [Post an issue](https://github.com/testcontainers/testcontainers-java/issues) if you find any bugs -* Contribute improvements or fixes using a [Pull Request](https://github.com/testcontainers/testcontainers-java/pulls). If you're going to contribute, thank you! Please just be sure to: - * discuss with the authors on an issue ticket prior to doing anything big - * follow the style, naming and structure conventions of the rest of the project - * make commits atomic and easy to merge - * verify all tests are passing. Build the project with `./gradlew check` to do this - - **PS:** Make sure Docker is running locally and you have the lombok plugin installed in your IDE - - **N.B.** Gradle's Build Cache is enabled by default, but you can add `--no-build-cache` flag to disable it. - +Please see the [main contributing guidelines](./docs/contributing.md). + +There are additional docs describing [contributing documentation changes](./docs/contributing_docs.md). diff --git a/docs/contributing.md b/docs/contributing.md index 96ba5975aeb..9d2c63b030c 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -10,3 +10,83 @@ * when updating documentation, please see [our guidance for documentation contributions](contributing_docs.md). * verify all tests are passing. Build the project with `./gradlew check` to do this. **N.B.** Gradle's Build Cache is enabled by default, but you can add `--no-build-cache` flag to disable it. + +## Contributing new modules + +We often receive proposals (or fully formed PRs) for new modules. +We're very happy to have contributions, but new modules require specific extra care. We want to balance: + +* Usefulness of the module. +* Our ability to support the module in the future, potentially after contributors have moved on. +* Contributors time, so that nobody puts in wasted effort. + +### Does it need to be a module? + +*N.B. this is not a perfect list - please always reach out to us before starting on a module contribution!* + +* Does the module enable use of Testcontainers with a popular or rapidly growing technology? +* Does the module 'add value' beyond a `GenericContainer` code snippet/example? e.g. + * does it neatly encapsulate a difficult problem of running the program in a container? + * does it add technology-specific [wait strategies](features/startup_and_waits.md)? + * does it enable straightforward usage of client libraries? + +If the answers to the above are all yes, then a new module may be a good approach. + +Otherwise, it is entirely possible for you to: + +* publish a code snippet +* contribute an [example](../examples/README.md) to the Testcontainers repo +* publish your own third party library + +In any case, please contact us to help validate your proposal! + +### Checklist + +*Suggestion: copy and paste this list into PRs for new modules.* + +Every item on this list will require judgement by the Testcontainers core maintainers. Exceptions will sometimes be possible; items with `should` are more likely to be negotiable than those items with `must`. + +#### Default docker image + +- [ ] Should be a Docker Hub official image, or published by a reputable source (ideally the company or organisation that officially supports the technology) +- [ ] Should have a verifiable open source Dockerfile and a way to view the history of changes +- [ ] MUST show general good practices regarding container image tagging - e.g. we do not use `latest` tags, and we do not use tags that may be mutated in the future +- [ ] MUST be legal for Testcontainers developers and Testcontainers users to pull and use. Mechanisms exist to allow EULA acceptance to be signalled, but images that can be used without a licence are greatly preferred. + +#### Module dependencies + +- [ ] The module should use as few dependencies as possible, +- [ ] Regarding libraries, either: + - they should be `compileOnly` if they are likely to already be on the classpath for users' tests (e.g. client libraries or drivers) + - they can be `implementation` (and thus transitive dependencies) if they are very unlikely to conflict with users' dependencies. +- [ ] If client libraries are used to test or use the module, these MUST be legal for Testcontainers developers and Testcontainers users to download and use. + +#### API (e.g. `MyModuleContainer` class) + +- [ ] Favour doing the right thing, and least surprising thing, by default +- [ ] Ensure that method and parameter names are easy to understand. Many users will ignore documentation, so IDE-based substitutes (autocompletion and Javadocs) should be intuitive. +- [ ] The module's public API should only handle standard JDK data types and MUST not expose data types that come from `compileOnly` dependencies. This is to reduce the risk of compatibility problems with future versions of third party libraries. + +#### Documentation + +- [ ] Every module MUST have a dedicated documentation page containing: + - [ ] A high level overview + - [ ] A usage example + - [ ] If appropriate, basic API documentation or further usage guidelines + - [ ] Dependency information + - [ ] Acknowledgements, if appropriate +- [ ] Consider that many users will not read the documentation pages - even if the first person to add it to a project does, people reading/updating the code in the future may not. Try and avoid the need for critical knowledge that is only present in documentation. + + + +### Incubating modules + +We have a policy of marking new modules as 'incubating' so that we can evaluate its maintainability and usability traits over a longer period of time. +We currently believe 3 months is a fair period of time, but may change this. + +New modules should have the following warning at the top of their documentation pages: + +!!! warning + This module is INCUBATING. While it is ready for use and operational in the current version of Testcontainers, it is possible that it may receive breaking changes in the future. + +We will evaluate incubating modules periodically, and remove the label when appropriate. From 3a0618511293a9e781a2eeb2bb42255a27f8295c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 10 Jul 2019 06:28:12 +0000 Subject: [PATCH 0789/5615] Bump tomcat-jdbc from 9.0.21 to 9.0.22 in /modules/mariadb (#1600) --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 0068368b8a3..a501b44f04d 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -6,6 +6,6 @@ dependencies { testCompile 'org.mariadb.jdbc:mariadb-java-client:2.4.2' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.21' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.22' testCompile 'org.vibur:vibur-dbcp:22.2' } From d681aeef6f0d2987b6126dbc7382d6eb83fb7f3b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 10 Jul 2019 07:29:15 +0000 Subject: [PATCH 0790/5615] Bump tomcat-jdbc from 9.0.21 to 9.0.22 in /modules/mysql (#1599) --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index e2b1b416400..2540df73c3f 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -5,7 +5,7 @@ dependencies { testCompile 'mysql:mysql-connector-java:8.0.16' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.21' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.22' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.vibur:vibur-dbcp:22.2' } From 8f1421ad5310f75f371804c3a3a1402b0eaab6d4 Mon Sep 17 00:00:00 2001 From: "Roman S.A" Date: Mon, 15 Jul 2019 11:38:23 +0300 Subject: [PATCH 0791/5615] Kotlin example (#1521) * add example using spring-boot + kotlin + kafka * add project to settings.gradle * using Gradle DSL instead Kotlin DSL * using redis container instead kafka * cleanups --- examples/settings.gradle | 3 +- .../spring-boot-kotlin-redis/build.gradle | 28 ++++++++++++++ .../com/example/redis/ExampleController.kt | 27 ++++++++++++++ .../com/example/redis/RedisApplication.kt | 11 ++++++ .../example/redis/AbstractIntegrationTest.kt | 37 +++++++++++++++++++ .../example/redis/RedisApplicationTests.kt | 11 ++++++ .../kotlin/com/example/redis/RedisTest.kt | 29 +++++++++++++++ 7 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 examples/spring-boot-kotlin-redis/build.gradle create mode 100644 examples/spring-boot-kotlin-redis/src/main/kotlin/com/example/redis/ExampleController.kt create mode 100644 examples/spring-boot-kotlin-redis/src/main/kotlin/com/example/redis/RedisApplication.kt create mode 100644 examples/spring-boot-kotlin-redis/src/test/kotlin/com/example/redis/AbstractIntegrationTest.kt create mode 100644 examples/spring-boot-kotlin-redis/src/test/kotlin/com/example/redis/RedisApplicationTests.kt create mode 100644 examples/spring-boot-kotlin-redis/src/test/kotlin/com/example/redis/RedisTest.kt diff --git a/examples/settings.gradle b/examples/settings.gradle index 3d57c2a18df..9efbf097344 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -12,4 +12,5 @@ include 'selenium-container' include 'singleton-container' include 'spring-boot' include 'cucumber' -include 'spock' +include 'spring-boot-kotlin-redis' +include 'spock' \ No newline at end of file diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle new file mode 100644 index 00000000000..f2d9d8d97ec --- /dev/null +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -0,0 +1,28 @@ +plugins { + id("org.springframework.boot") version "2.1.5.RELEASE" + id("org.jetbrains.kotlin.jvm") version "1.3.31" + id("org.jetbrains.kotlin.plugin.spring") version "1.3.31" +} + +apply plugin: 'io.spring.dependency-management' + +repositories { + mavenCentral() +} + +dependencies { + implementation("org.springframework.boot:spring-boot-starter") + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + implementation("org.springframework.boot:spring-boot-starter-data-redis") + implementation("org.springframework.boot:spring-boot-starter-web") + + testImplementation("org.springframework.boot:spring-boot-starter-test") + testImplementation("org.testcontainers:testcontainers") +} + +compileKotlin { + kotlinOptions { + jvmTarget = "1.8" + freeCompilerArgs = ["-Xjsr305=strict"] + } +} \ No newline at end of file diff --git a/examples/spring-boot-kotlin-redis/src/main/kotlin/com/example/redis/ExampleController.kt b/examples/spring-boot-kotlin-redis/src/main/kotlin/com/example/redis/ExampleController.kt new file mode 100644 index 00000000000..bbe3083935e --- /dev/null +++ b/examples/spring-boot-kotlin-redis/src/main/kotlin/com/example/redis/ExampleController.kt @@ -0,0 +1,27 @@ +package com.example.redis + +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.data.redis.core.RedisTemplate +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RestController + +@RestController +class ExampleController { + + private val key = "testcontainers" + + @Autowired + private lateinit var redisTemplate: RedisTemplate + + @PostMapping("/set-foo") + fun setFoo(@RequestBody value: String) { + redisTemplate.opsForValue().set(key, value) + } + + @GetMapping("/get-foo") + fun getFoo(): String? { + return redisTemplate.opsForValue().get(key) + } +} diff --git a/examples/spring-boot-kotlin-redis/src/main/kotlin/com/example/redis/RedisApplication.kt b/examples/spring-boot-kotlin-redis/src/main/kotlin/com/example/redis/RedisApplication.kt new file mode 100644 index 00000000000..dc06c8004bf --- /dev/null +++ b/examples/spring-boot-kotlin-redis/src/main/kotlin/com/example/redis/RedisApplication.kt @@ -0,0 +1,11 @@ +package com.example.redis + +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.runApplication + +@SpringBootApplication +class RedisApplication + +fun main(args: Array) { + runApplication(*args) +} diff --git a/examples/spring-boot-kotlin-redis/src/test/kotlin/com/example/redis/AbstractIntegrationTest.kt b/examples/spring-boot-kotlin-redis/src/test/kotlin/com/example/redis/AbstractIntegrationTest.kt new file mode 100644 index 00000000000..dc8b0bf9e68 --- /dev/null +++ b/examples/spring-boot-kotlin-redis/src/test/kotlin/com/example/redis/AbstractIntegrationTest.kt @@ -0,0 +1,37 @@ +package com.example.redis + +import org.junit.runner.RunWith +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.util.TestPropertyValues +import org.springframework.context.ApplicationContextInitializer +import org.springframework.context.ConfigurableApplicationContext +import org.springframework.test.context.ContextConfiguration +import org.springframework.test.context.junit4.SpringRunner +import org.testcontainers.containers.GenericContainer + +@RunWith(SpringRunner::class) +@SpringBootTest +@ContextConfiguration(initializers = [AbstractIntegrationTest.Initializer::class]) +@AutoConfigureMockMvc +abstract class AbstractIntegrationTest { + + companion object { + val redisContainer = object : GenericContainer("redis:3-alpine") { + init { + withExposedPorts(6379) + } + } + } + + internal class Initializer : ApplicationContextInitializer { + override fun initialize(configurableApplicationContext: ConfigurableApplicationContext) { + redisContainer.start() + + TestPropertyValues.of( + "spring.redis.host=${redisContainer.containerIpAddress}", + "spring.redis.port=${redisContainer.firstMappedPort}" + ).applyTo(configurableApplicationContext.environment) + } + } +} diff --git a/examples/spring-boot-kotlin-redis/src/test/kotlin/com/example/redis/RedisApplicationTests.kt b/examples/spring-boot-kotlin-redis/src/test/kotlin/com/example/redis/RedisApplicationTests.kt new file mode 100644 index 00000000000..aec5a486120 --- /dev/null +++ b/examples/spring-boot-kotlin-redis/src/test/kotlin/com/example/redis/RedisApplicationTests.kt @@ -0,0 +1,11 @@ +package com.example.redis + +import org.junit.Test + +class RedisApplicationTests : AbstractIntegrationTest() { + + @Test + fun contextLoads() { + } + +} diff --git a/examples/spring-boot-kotlin-redis/src/test/kotlin/com/example/redis/RedisTest.kt b/examples/spring-boot-kotlin-redis/src/test/kotlin/com/example/redis/RedisTest.kt new file mode 100644 index 00000000000..317916bd3e6 --- /dev/null +++ b/examples/spring-boot-kotlin-redis/src/test/kotlin/com/example/redis/RedisTest.kt @@ -0,0 +1,29 @@ +package com.example.redis + +import org.hamcrest.CoreMatchers.containsString +import org.junit.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.content +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import org.springframework.test.web.servlet.setup.MockMvcBuilders + + +class RedisTest : AbstractIntegrationTest() { + + @Autowired + private lateinit var mockMvc: MockMvc + + @Test + fun testRedisFunctionality() { + val greeting = "Hello Testcontainers with Kotlin" + mockMvc.perform(post("/set-foo").content(greeting)) + .andExpect(status().isOk) + + mockMvc.perform(get("/get-foo")) + .andExpect(status().isOk) + .andExpect(content().string(containsString(greeting))) + } +} From 0ccadb75402b6f4e994c90293ced152331bcd46c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 16 Jul 2019 06:30:38 +0000 Subject: [PATCH 0792/5615] Bump cucumber-junit from 4.5.1 to 4.5.4 in /examples (#1614) --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 505aad82ae4..733a2e6a8b7 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' implementation 'io.cucumber:cucumber-java:4.5.1' - testImplementation 'io.cucumber:cucumber-junit:4.5.1' + testImplementation 'io.cucumber:cucumber-junit:4.5.4' testImplementation 'org.testcontainers:selenium' } From fb1e91c71d44d6ffbd0f395b3eac0547e39eb581 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 16 Jul 2019 07:58:06 +0000 Subject: [PATCH 0793/5615] Bump cucumber-java from 4.5.1 to 4.5.4 in /examples (#1615) --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 733a2e6a8b7..fb3a1ae875c 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' - implementation 'io.cucumber:cucumber-java:4.5.1' + implementation 'io.cucumber:cucumber-java:4.5.4' testImplementation 'io.cucumber:cucumber-junit:4.5.4' testImplementation 'org.testcontainers:selenium' } From 3dc9d9dc3a1d03b87707d6bff6a95e0fbf0a4510 Mon Sep 17 00:00:00 2001 From: Andrew Guibert Date: Tue, 16 Jul 2019 04:48:56 -0500 Subject: [PATCH 0794/5615] Add DB2 module (#1611) * Add db2 jdbc module * Update Db2Container to use ibmcom/db2 image * Add doc page on DB2 module * Resolve review comments --- docs/modules/databases/db2.md | 47 +++++++ modules/db2/build.gradle | 5 + .../containers/Db2Container.java | 129 ++++++++++++++++++ .../containers/Db2ContainerProvider.java | 18 +++ ...s.containers.JdbcDatabaseContainerProvider | 1 + modules/jdbc-test/build.gradle | 2 + .../testcontainers/jdbc/JDBCDriverTest.java | 8 +- .../testcontainers/junit/SimpleDb2Test.java | 39 ++++++ .../container-license-acceptance.txt | 1 + .../testcontainers/jdbc/ConnectionUrl.java | 2 +- 10 files changed, 250 insertions(+), 2 deletions(-) create mode 100644 docs/modules/databases/db2.md create mode 100644 modules/db2/build.gradle create mode 100644 modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java create mode 100644 modules/db2/src/main/java/org/testcontainers/containers/Db2ContainerProvider.java create mode 100644 modules/db2/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider create mode 100644 modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleDb2Test.java diff --git a/docs/modules/databases/db2.md b/docs/modules/databases/db2.md new file mode 100644 index 00000000000..b7871e636d0 --- /dev/null +++ b/docs/modules/databases/db2.md @@ -0,0 +1,47 @@ +# DB2 Module + +See [Database containers](./index.md) for documentation and usage that is common to all relational database container types. + +## Usage example + +Running DB2 as a stand-in for in a test: + +```java +public class SomeTest { + + @ClassRule + public Db2Container db2 = new Db2Container() + .acceptLicense(); + + @Test + public void someTestMethod() { + String url = db2.getJdbcUrl(); + + ... create a connection and run test as normal + } +``` + +!!! warning "EULA Acceptance" + Due to licencing restrictions you are required to accept an EULA for this container image. To indicate that you accept the DB2 image EULA, call the `acceptLicense()` method, or place a file at the root of the classpath named `container-license-acceptance.txt`, e.g. at `src/test/resources/container-license-acceptance.txt`. This file should contain the line: `ibmcom/db2:11.5.0.0a` (or, if you are overriding the docker image name/tag, update accordingly). + + Please see the [`ibmcom/db2` image documentation](https://hub.docker.com/r/ibmcom/db2) for a link to the EULA document. + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:db2:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + db2 + {{latest_version}} + test + +``` + +!!! hint + Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency. diff --git a/modules/db2/build.gradle b/modules/db2/build.gradle new file mode 100644 index 00000000000..a92631df264 --- /dev/null +++ b/modules/db2/build.gradle @@ -0,0 +1,5 @@ +description = "Testcontainers :: JDBC :: DB2" + +dependencies { + compile project(':jdbc') +} diff --git a/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java b/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java new file mode 100644 index 00000000000..6e45b0f7f12 --- /dev/null +++ b/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java @@ -0,0 +1,129 @@ +package org.testcontainers.containers; + +import com.github.dockerjava.api.command.InspectContainerResponse; +import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.output.OutputFrame; +import org.testcontainers.containers.output.WaitingConsumer; +import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; +import org.testcontainers.utility.LicenseAcceptance; +import org.testcontainers.utility.LogUtils; + +import java.io.IOException; +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.TimeoutException; +import java.util.function.Predicate; + +public class Db2Container extends JdbcDatabaseContainer { + + public static final String NAME = "db2"; + public static final String DEFAULT_DB2_IMAGE_NAME = "ibmcom/db2"; + public static final String DEFAULT_TAG = "11.5.0.0a"; + public static final int DB2_PORT = 50000; + + private String databaseName = "test"; + private String username = "db2inst1"; + private String password = "foobar1234"; + + public Db2Container() { + this(DEFAULT_DB2_IMAGE_NAME + ":" + DEFAULT_TAG); + } + + public Db2Container(String imageName) { + super(imageName); + withPrivilegedMode(true); + this.waitStrategy = new LogMessageWaitStrategy() + .withRegEx(".*Setup has completed\\..*") + .withStartupTimeout(Duration.of(3, ChronoUnit.MINUTES)); + } + + @Override + protected Set getLivenessCheckPorts() { + return new HashSet<>(getMappedPort(DB2_PORT)); + } + + @Override + protected void configure() { + // If license was not accepted programatically, check if it was accepted via resource file + if (!getEnvMap().containsKey("LICENSE")) { + LicenseAcceptance.assertLicenseAccepted(this.getDockerImageName()); + acceptLicense(); + } + + addExposedPort(DB2_PORT); + + addEnv("DBNAME", databaseName); + addEnv("DB2INSTANCE", username); + addEnv("DB2INST1_PASSWORD", password); + + // These settings help the DB2 container start faster + if (!getEnvMap().containsKey("AUTOCONFIG")) + addEnv("AUTOCONFIG", "false"); + if (!getEnvMap().containsKey("ARCHIVE_LOGS")) + addEnv("ARCHIVE_LOGS", "false"); + } + + /** + * Accepts the license for the DB2 container by setting the LICENSE=accept + * variable as described at https://hub.docker.com/r/ibmcom/db2 + */ + public Db2Container acceptLicense() { + addEnv("LICENSE", "accept"); + return this; + } + + @Override + public String getDriverClassName() { + return "com.ibm.db2.jcc.DB2Driver"; + } + + @Override + public String getJdbcUrl() { + return "jdbc:db2://" + getContainerIpAddress() + ":" + getMappedPort(DB2_PORT) + "/" + databaseName; + } + + @Override + public String getUsername() { + return username; + } + + @Override + public String getPassword() { + return password; + } + + @Override + public String getDatabaseName() { + return databaseName; + } + + @Override + public Db2Container withUsername(String username) { + this.username = username; + return this; + } + + @Override + public Db2Container withPassword(String password) { + this.password = password; + return this; + } + + @Override + public Db2Container withDatabaseName(String dbName) { + this.databaseName = dbName; + return this; + } + + @Override + protected void waitUntilContainerStarted() { + getWaitStrategy().waitUntilReady(this); + } + + @Override + protected String getTestQueryString() { + return "SELECT 1 FROM SYSIBM.SYSDUMMY1"; + } +} diff --git a/modules/db2/src/main/java/org/testcontainers/containers/Db2ContainerProvider.java b/modules/db2/src/main/java/org/testcontainers/containers/Db2ContainerProvider.java new file mode 100644 index 00000000000..5c78fdc0505 --- /dev/null +++ b/modules/db2/src/main/java/org/testcontainers/containers/Db2ContainerProvider.java @@ -0,0 +1,18 @@ +package org.testcontainers.containers; + +public class Db2ContainerProvider extends JdbcDatabaseContainerProvider { + @Override + public boolean supports(String databaseType) { + return databaseType.equals(Db2Container.NAME); + } + + @Override + public JdbcDatabaseContainer newInstance() { + return newInstance(Db2Container.DEFAULT_TAG); + } + + @Override + public JdbcDatabaseContainer newInstance(String tag) { + return new Db2Container(Db2Container.DEFAULT_DB2_IMAGE_NAME + ":" + tag); + } +} diff --git a/modules/db2/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider b/modules/db2/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider new file mode 100644 index 00000000000..d433ae9fc82 --- /dev/null +++ b/modules/db2/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider @@ -0,0 +1 @@ +org.testcontainers.containers.Db2ContainerProvider diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 4c7ab8b0974..5874787e9eb 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -11,6 +11,7 @@ dependencies { compile project(':oracle-xe') compile project(':mssqlserver') compile project(':clickhouse') + compile project(':db2') testCompile 'com.google.guava:guava:18.0' testCompile 'org.postgresql:postgresql:42.0.0' @@ -19,6 +20,7 @@ dependencies { testCompile 'com.oracle:ojdbc6:12.1.0.1-atlassian-hosted' testCompile 'com.microsoft.sqlserver:mssql-jdbc:6.1.0.jre8' testCompile 'ru.yandex.clickhouse:clickhouse-jdbc:0.1.41' + testCompile 'com.ibm.db2:jcc:11.1.4.4' testCompile 'com.zaxxer:HikariCP-java6:2.3.8' testCompile 'org.apache.tomcat:tomcat-jdbc:8.5.4' diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java index 30b224ab6a6..0508a54e13d 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java @@ -65,6 +65,7 @@ public static Iterable data() { {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_MY_CNF=somepath/mariadb_conf_override", EnumSet.of(Options.CustomIniFile)}, {"jdbc:tc:clickhouse://hostname/databasename", EnumSet.of(Options.PmdKnownBroken)}, {"jdbc:tc:sqlserver:2017-CU12://hostname:hostport;databaseName=databasename", EnumSet.noneOf(Options.class)}, + {"jdbc:tc:db2://hostname/databasename", EnumSet.noneOf(Options.class)}, }); } @@ -109,7 +110,12 @@ public void test() throws SQLException { } private void performSimpleTest(HikariDataSource dataSource) throws SQLException { - boolean result = new QueryRunner(dataSource, options.contains(Options.PmdKnownBroken)).query("SELECT 1", rs -> { + String query = "SELECT 1"; + if (jdbcUrl.startsWith("jdbc:tc:db2:")) { + query = "SELECT 1 FROM SYSIBM.SYSDUMMY1"; + } + + boolean result = new QueryRunner(dataSource, options.contains(Options.PmdKnownBroken)).query(query, rs -> { rs.next(); int resultSetInt = rs.getInt(1); assertEquals("A basic SELECT query succeeds", 1, resultSetInt); diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleDb2Test.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleDb2Test.java new file mode 100644 index 00000000000..c686f979b79 --- /dev/null +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleDb2Test.java @@ -0,0 +1,39 @@ +package org.testcontainers.junit; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import org.junit.Rule; +import org.junit.Test; +import org.testcontainers.containers.Db2Container; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; + + +public class SimpleDb2Test { + + @Rule + public Db2Container db2 = new Db2Container() + .acceptLicense(); + + @Test + public void testSimple() throws SQLException { + HikariConfig hikariConfig = new HikariConfig(); + hikariConfig.setJdbcUrl(db2.getJdbcUrl()); + hikariConfig.setUsername(db2.getUsername()); + hikariConfig.setPassword(db2.getPassword()); + + HikariDataSource ds = new HikariDataSource(hikariConfig); + Statement statement = ds.getConnection().createStatement(); + statement.execute("SELECT 1 FROM SYSIBM.SYSDUMMY1"); + ResultSet resultSet = statement.getResultSet(); + + resultSet.next(); + int resultSetInt = resultSet.getInt(1); + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + } + +} diff --git a/modules/jdbc-test/src/test/resources/container-license-acceptance.txt b/modules/jdbc-test/src/test/resources/container-license-acceptance.txt index 8a704b9c066..4af979f0f18 100644 --- a/modules/jdbc-test/src/test/resources/container-license-acceptance.txt +++ b/modules/jdbc-test/src/test/resources/container-license-acceptance.txt @@ -1 +1,2 @@ mcr.microsoft.com/mssql/server:2017-CU12 +ibmcom/db2:11.5.0.0a diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java index 36206b89d82..e236d723c82 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java @@ -203,7 +203,7 @@ public Map getTmpfsOptions() { * @author manikmagar */ public interface Patterns { - Pattern URL_MATCHING_PATTERN = Pattern.compile("jdbc:tc:([a-z]+)(:([^:]+))?://([^\\?]+)(\\?.*)?"); + Pattern URL_MATCHING_PATTERN = Pattern.compile("jdbc:tc:([a-z0-9]+)(:([^:]+))?://([^\\?]+)(\\?.*)?"); Pattern ORACLE_URL_MATCHING_PATTERN = Pattern.compile("jdbc:tc:([a-z]+)(:([^(thin:)]+))?:thin:@([^\\?]+)(\\?.*)?"); From 371b32566ddb76e8779c802ae1ffa9afafdf5b59 Mon Sep 17 00:00:00 2001 From: Chung Nguyen Date: Tue, 16 Jul 2019 17:13:44 +0700 Subject: [PATCH 0795/5615] catch exception when try to kill dead container (#1420) Fixes #1379 --- .../java/org/testcontainers/utility/ResourceReaper.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index 46550dc0af7..086cac83206 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -2,6 +2,7 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.exception.ConflictException; import com.github.dockerjava.api.exception.DockerException; import com.github.dockerjava.api.exception.InternalServerErrorException; import com.github.dockerjava.api.exception.NotFoundException; @@ -231,6 +232,12 @@ private void stopContainer(String containerId, String imageName) { LOGGER.trace("Stopping container: {}", containerId); dockerClient.killContainerCmd(containerId).exec(); LOGGER.trace("Stopped container: {}", imageName); + } catch (ConflictException e) { + if ( e.getMessage().startsWith("Cannot kill container: ") && e.getMessage().endsWith(containerId + " is not running")) { + LOGGER.trace("Was going to kill the container but it apparently already dead : {}", e.getMessage()); + } else { + throw e; + } } catch (DockerException e) { LOGGER.trace("Error encountered shutting down container (ID: {}) - it may not have been stopped, or may already be stopped: {}", containerId, e.getMessage()); } From 041d5084c407d859f8740e052e1e9a36b080d2ea Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 16 Jul 2019 12:10:21 +0100 Subject: [PATCH 0796/5615] Upgrade localstack and enable new services (#1504) * Add new services to the enum Note: Some services need a newer Localstack version * Upgrade Localstack and alter endpoint URL generation to be IP-only Also extend basic tests to cover SQS * Describe rationale for adding new Localstack tests (or not) (#1509) * Update localstack to 0.9.4 --- modules/localstack/build.gradle | 1 + .../localstack/LocalStackContainer.java | 49 +++++----- .../localstack/SimpleLocalstackS3Test.java | 52 ----------- .../localstack/SimpleLocalstackTest.java | 93 +++++++++++++++++++ 4 files changed, 117 insertions(+), 78 deletions(-) delete mode 100644 modules/localstack/src/test/java/org/testcontainers/containers/localstack/SimpleLocalstackS3Test.java create mode 100644 modules/localstack/src/test/java/org/testcontainers/containers/localstack/SimpleLocalstackTest.java diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 18a410ef1a8..e5e3efb5de3 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,4 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.479' } diff --git a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java index 319581e9f31..1bf0e18627d 100644 --- a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java +++ b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java @@ -28,7 +28,7 @@ */ public class LocalStackContainer extends GenericContainer { - public static final String VERSION = "0.8.6"; + public static final String VERSION = "0.9.4"; private final List services = new ArrayList<>(); @@ -82,20 +82,11 @@ public AwsClientBuilder.EndpointConfiguration getEndpointConfiguration(Service s final String address = getContainerIpAddress(); String ipAddress = address; try { + // resolve IP address and use that as the endpoint so that path-style access is automatically used for S3 ipAddress = InetAddress.getByName(address).getHostAddress(); } catch (UnknownHostException ignored) { } - ipAddress = ipAddress + ".nip.io"; - while (true) { - try { - //noinspection ResultOfMethodCallIgnored - InetAddress.getAllByName(ipAddress); - break; - } catch (UnknownHostException ignored) { - - } - } return new AwsClientBuilder.EndpointConfiguration( "http://" + @@ -123,23 +114,29 @@ public AWSCredentialsProvider getDefaultCredentialsProvider() { @Getter @FieldDefaults(makeFinal = true) public enum Service { - API_GATEWAY("apigateway", 4567), - KINESIS("kinesis", 4568), - DYNAMODB("dynamodb", 4569), - DYNAMODB_STREAMS("dynamodbstreams", 4570), + API_GATEWAY("apigateway", 4567), + KINESIS("kinesis", 4568), + DYNAMODB("dynamodb", 4569), + DYNAMODB_STREAMS("dynamodbstreams", 4570), // TODO: Clarify usage for ELASTICSEARCH and ELASTICSEARCH_SERVICE // ELASTICSEARCH("es", 4571), - S3("s3", 4572), - FIREHOSE("firehose", 4573), - LAMBDA("lambda", 4574), - SNS("sns", 4575), - SQS("sqs", 4576), - REDSHIFT("redshift", 4577), -// ELASTICSEARCH_SERVICE("", 4578), - SES("ses", 4579), - ROUTE53("route53", 4580), - CLOUDFORMATION("cloudformation", 4581), - CLOUDWATCH("cloudwatch", 4582); + S3("s3", 4572), + FIREHOSE("firehose", 4573), + LAMBDA("lambda", 4574), + SNS("sns", 4575), + SQS("sqs", 4576), + REDSHIFT("redshift", 4577), + // ELASTICSEARCH_SERVICE("", 4578), + SES("ses", 4579), + ROUTE53("route53", 4580), + CLOUDFORMATION("cloudformation", 4581), + CLOUDWATCH("cloudwatch", 4582), + SSM("ssm", 4583), + SECRETSMANAGER("secretsmanager", 4584), + STEPFUNCTIONS("stepsfunctions", 4585), + CLOUDWATCHLOGS("cloudwatchlogs", 4586), + STS("sts", 4592), + IAM("iam", 4593); String localStackName; diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/SimpleLocalstackS3Test.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/SimpleLocalstackS3Test.java deleted file mode 100644 index f00423d31b7..00000000000 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/SimpleLocalstackS3Test.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.testcontainers.containers.localstack; - - -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.AmazonS3ClientBuilder; -import com.amazonaws.services.s3.model.Bucket; -import com.amazonaws.services.s3.model.ObjectListing; -import com.amazonaws.services.s3.model.S3Object; -import org.apache.commons.io.IOUtils; -import org.junit.Rule; -import org.junit.Test; - -import java.io.IOException; -import java.nio.charset.Charset; -import java.util.List; - -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.testcontainers.containers.localstack.LocalStackContainer.Service.S3; - -public class SimpleLocalstackS3Test { - - @Rule - public LocalStackContainer localstack = new LocalStackContainer() - .withServices(S3); - - @Test - public void s3Test() throws IOException { - AmazonS3 s3 = AmazonS3ClientBuilder - .standard() - .withEndpointConfiguration(localstack.getEndpointConfiguration(S3)) - .withCredentials(localstack.getDefaultCredentialsProvider()) - .build(); - - s3.createBucket("foo"); - s3.putObject("foo", "bar", "baz"); - - final List buckets = s3.listBuckets(); - assertEquals("The created bucket is present", 1, buckets.size()); - final Bucket bucket = buckets.get(0); - - assertEquals("The created bucket has the right name", "foo", bucket.getName()); - assertEquals("The created bucket has the right name", "foo", bucket.getName()); - - final ObjectListing objectListing = s3.listObjects("foo"); - assertEquals("The created bucket has 1 item in it", 1, objectListing.getObjectSummaries().size()); - - final S3Object object = s3.getObject("foo", "bar"); - final String content = IOUtils.toString(object.getObjectContent(), Charset.forName("UTF-8")); - assertEquals("The object can be retrieved", "baz", content); - - } -} diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/SimpleLocalstackTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/SimpleLocalstackTest.java new file mode 100644 index 00000000000..f3495d2d8b6 --- /dev/null +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/SimpleLocalstackTest.java @@ -0,0 +1,93 @@ +package org.testcontainers.containers.localstack; + + +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import com.amazonaws.services.s3.model.Bucket; +import com.amazonaws.services.s3.model.ObjectListing; +import com.amazonaws.services.s3.model.S3Object; +import com.amazonaws.services.sqs.AmazonSQS; +import com.amazonaws.services.sqs.AmazonSQSClientBuilder; +import com.amazonaws.services.sqs.model.CreateQueueResult; +import com.amazonaws.services.sqs.model.Message; +import org.apache.commons.io.IOUtils; +import org.junit.ClassRule; +import org.junit.Test; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Set; + +import static java.util.stream.Collectors.toSet; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.testcontainers.containers.localstack.LocalStackContainer.Service.S3; +import static org.testcontainers.containers.localstack.LocalStackContainer.Service.SQS; + +/** + * The only tests that should go here are for those Localstack services that require special testcontainers code to work with them. + * For example, S3 needs a test to ensure special HTTP routing to the S3 container works. + */ +public class SimpleLocalstackTest { + + private static final String BUCKET_1 = "bucket1"; + private static final String BUCKET_2 = "bucket2"; + private static final String ITEM_KEY = "bar"; + private static final String DUMMY_CONTENT = "baz"; + + @ClassRule + public static LocalStackContainer localstack = new LocalStackContainer() + .withServices(S3, SQS); + + @Test + public void simpleS3Test() throws IOException { + AmazonS3 s3 = AmazonS3ClientBuilder + .standard() + .withEndpointConfiguration(localstack.getEndpointConfiguration(S3)) + .withCredentials(localstack.getDefaultCredentialsProvider()) + .build(); + + s3.createBucket(BUCKET_1); + s3.putObject(BUCKET_1, ITEM_KEY, DUMMY_CONTENT); + + s3.createBucket(BUCKET_2); + s3.putObject(BUCKET_2, ITEM_KEY, DUMMY_CONTENT); + + final Set bucketNames = s3.listBuckets().stream() + .map(Bucket::getName) + .collect(toSet()); + assertTrue("The created buckets have the right name", + bucketNames.contains(BUCKET_1) && bucketNames.contains(BUCKET_2)); + + assertBucketContentsCorrect(s3, BUCKET_1); + assertBucketContentsCorrect(s3, BUCKET_2); + } + + private void assertBucketContentsCorrect(AmazonS3 s3, String bucketName) throws IOException { + final ObjectListing objectListing1 = s3.listObjects(bucketName); + assertEquals("The created bucket has 1 item in it", 1, objectListing1.getObjectSummaries().size()); + + final S3Object object = s3.getObject(bucketName, ITEM_KEY); + final String content = IOUtils.toString(object.getObjectContent(), StandardCharsets.UTF_8); + assertEquals("The object can be retrieved", DUMMY_CONTENT, content); + } + + @Test + public void simpleSQSTest() { + AmazonSQS sqs = AmazonSQSClientBuilder.standard() + .withEndpointConfiguration(localstack.getEndpointConfiguration(SQS)) + .withCredentials(localstack.getDefaultCredentialsProvider()) + .build(); + + CreateQueueResult queueResult = sqs.createQueue("baz"); + String queueUrl = queueResult.getQueueUrl(); + + sqs.sendMessage(queueUrl, "ping"); + + final List messages = sqs.receiveMessage(queueUrl).getMessages(); + + assertTrue("the message queue contains a message", messages.size() > 0); + assertEquals("the first message is the one we sent", "ping", messages.get(0).getBody()); + } +} From 60b91b0b5b9331c5adfaddcf030d984033131c40 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 16 Jul 2019 13:12:45 +0200 Subject: [PATCH 0797/5615] Split AZP config into jobs rather than steps (#1613) * Split AZP config into jobs rather than steps * fix the modules task --- azure-pipelines.yml | 53 +++++++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index abcd155010d..d6efa593dcb 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,21 +1,36 @@ -steps: -- task: Gradle@2 - displayName: Build core - inputs: - gradleWrapperFile: 'gradlew' - jdkVersionOption: '1.11' - options: '--no-daemon --continue' - tasks: 'testcontainers:check' - publishJUnitResults: true - testResultsFiles: '**/TEST-*.xml' -- task: Gradle@2 - displayName: Build modules - inputs: - gradleWrapperFile: 'gradlew' - jdkVersionOption: '1.11' - options: '--no-daemon --continue -x testcontainers:check' - tasks: 'check' - publishJUnitResults: true - testResultsFiles: '**/TEST-*.xml' +jobs: +- job: core + steps: + - task: Gradle@2 + displayName: Build & test + inputs: + gradleWrapperFile: 'gradlew' + jdkVersionOption: '1.11' + options: '--no-daemon --continue' + tasks: 'testcontainers:check' + publishJUnitResults: true + testResultsFiles: '**/TEST-*.xml' +- job: jdbc + steps: + - task: Gradle@2 + displayName: Build & test + inputs: + gradleWrapperFile: 'gradlew' + jdkVersionOption: '1.11' + options: '--no-daemon --continue' + tasks: 'jdbc-test:check' + publishJUnitResults: true + testResultsFiles: '**/TEST-*.xml' +- job: modules + steps: + - task: Gradle@2 + displayName: Build & test + inputs: + gradleWrapperFile: 'gradlew' + jdkVersionOption: '1.11' + options: '--continue -x testcontainers:check -x jdbc-test:check' + tasks: 'check' + publishJUnitResults: true + testResultsFiles: '**/TEST-*.xml' From d7852496526c0043a2a5e2c247d2da771bce0313 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 16 Jul 2019 12:56:59 +0100 Subject: [PATCH 0798/5615] Upgrade duct-tape library (#1433) --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 30fd4d6167e..ee876f595c1 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -63,7 +63,7 @@ dependencies { compile 'org.apache.commons:commons-compress:1.18' // Added for JDK9 compatibility since it's missing this package compile 'javax.xml.bind:jaxb-api:2.3.1' - compile ('org.rnorth.duct-tape:duct-tape:1.0.7') { + compile ('org.rnorth.duct-tape:duct-tape:1.0.8') { exclude(group: 'org.jetbrains', module: 'annotations') } From 1d6ab93f5881f34c5e9cec75954410fe2ac38e18 Mon Sep 17 00:00:00 2001 From: Wladimir Schmidt Date: Tue, 16 Jul 2019 14:00:41 +0200 Subject: [PATCH 0799/5615] Bumped up default version of Confluent Platform to the current 5.2.1 (Kafka container) (#1446) * Bumped up default version of Confluent Platform to the current 5.2.1 * Update KafkaContainerTest.java --- .../main/java/org/testcontainers/containers/KafkaContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java index 6396d6bcd7f..b1be94f1e67 100644 --- a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java +++ b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java @@ -20,7 +20,7 @@ public class KafkaContainer extends GenericContainer { protected SocatContainer proxy; public KafkaContainer() { - this("4.0.0"); + this("5.2.1"); } public KafkaContainer(String confluentPlatformVersion) { From 03ba4c8f1efbb9308c11ee4340dc5681a9d1fa30 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 16 Jul 2019 13:52:29 +0100 Subject: [PATCH 0800/5615] Make test resilient to timing issues (#1375) * Make test resilient to timing issues * Update ContainerLogsTest.java * Update ContainerLogsTest.java * ignore the failing test * Update ContainerLogsTest.java --- .../containers/output/ContainerLogsTest.java | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java b/core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java index f95d5a06850..ad0e4c76ae2 100644 --- a/core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java +++ b/core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java @@ -1,5 +1,6 @@ package org.testcontainers.containers.output; +import org.junit.Ignore; import org.junit.Test; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; @@ -12,14 +13,26 @@ public class ContainerLogsTest { @Test + @Ignore("fails due to the timing of the shell's decision to flush") public void getLogsReturnsAllLogsToDate() { + try (GenericContainer container = shortLivedContainer()) { + container.start(); + + final String logs = container.getLogs(); + assertEquals("stdout and stderr are reflected in the returned logs", "stdout\nstderr", logs); + } + } + + @Test + public void getLogsContainsBothOutputTypes() { try (GenericContainer container = shortLivedContainer()) { container.start(); // docsGetAllLogs { final String logs = container.getLogs(); // } - assertEquals("stdout and stderr are reflected in the returned logs", "stdout\nstderr", logs); + assertTrue("stdout is reflected in the returned logs", logs.contains("stdout")); + assertTrue("stderr is reflected in the returned logs", logs.contains("stderr")); } } @@ -31,7 +44,7 @@ public void getLogsReturnsStdOutToDate() { // docsGetStdOut { final String logs = container.getLogs(STDOUT); // } - assertEquals("stdout and stderr are reflected in the returned logs", "stdout", logs); + assertTrue("stdout is reflected in the returned logs", logs.contains("stdout")); } } @@ -43,7 +56,7 @@ public void getLogsReturnsStdErrToDate() { // docsGetStdErr { final String logs = container.getLogs(STDERR); // } - assertEquals("stdout and stderr are reflected in the returned logs", "stderr", logs); + assertTrue("stderr is reflected in the returned logs", logs.contains("stderr")); } } From 30af86b8293d8200a50e9a7f1b7ecd873cfdbd45 Mon Sep 17 00:00:00 2001 From: martingreber <31406507+martingreber@users.noreply.github.com> Date: Tue, 16 Jul 2019 23:04:48 +1000 Subject: [PATCH 0801/5615] Add rabbitmq (#1449) * Added RabbitMQ module Added RabbitMQ SSL option * Added SSL option for RabbitMQ * Update modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java Co-Authored-By: martingreber <31406507+martingreber@users.noreply.github.com> * Update modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java Co-Authored-By: martingreber <31406507+martingreber@users.noreply.github.com> * Fixed bug in enabling RabbitMQ plugin * Changed builder methods to return RabbitMQContainer * Replaced org.json with jackson * Removed option to disable plugins. It shouldn't be needed * Removed unused code * Simplified wait condition * Removed unused exception class * Use try with resources for tests * Fixed copy-paste comment bug from the Neo4JContainer. Cheers to the Neo4JContainer author, Michael J. Simons :) * Removed getLivenessCheckPortNumbers() as the super implementation is sufficient * Removed unnecessary wait from test * Refactored methods returning URLs and json string generation * Fixed bug with missing verify_peer option * Restructured code for readability * Add Lightbend to the Who is using list (#1464) * accept image, not tag * Update RabbitMQContainerTest.java --- modules/rabbitmq/build.gradle | 7 + .../containers/RabbitMQContainer.java | 367 ++++++++++++++++++ ...RabbitMQContainerJUnitIntegrationTest.java | 22 ++ .../containers/RabbitMQContainerTest.java | 233 +++++++++++ .../test/resources/certs/ca_certificate.pem | 20 + .../src/test/resources/certs/ca_key.pem | 28 ++ .../resources/certs/client_certificate.pem | 19 + .../src/test/resources/certs/client_key.p12 | Bin 0 -> 2413 bytes .../src/test/resources/certs/client_key.pem | 27 ++ .../resources/certs/server_certificate.pem | 21 + .../src/test/resources/certs/server_key.p12 | Bin 0 -> 2477 bytes .../src/test/resources/certs/server_key.pem | 27 ++ .../src/test/resources/certs/truststore.jks | Bin 0 -> 1130 bytes .../src/test/resources/rabbitmq-custom.conf | 2 + 14 files changed, 773 insertions(+) create mode 100644 modules/rabbitmq/build.gradle create mode 100644 modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java create mode 100644 modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerJUnitIntegrationTest.java create mode 100644 modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java create mode 100644 modules/rabbitmq/src/test/resources/certs/ca_certificate.pem create mode 100644 modules/rabbitmq/src/test/resources/certs/ca_key.pem create mode 100644 modules/rabbitmq/src/test/resources/certs/client_certificate.pem create mode 100644 modules/rabbitmq/src/test/resources/certs/client_key.p12 create mode 100644 modules/rabbitmq/src/test/resources/certs/client_key.pem create mode 100644 modules/rabbitmq/src/test/resources/certs/server_certificate.pem create mode 100644 modules/rabbitmq/src/test/resources/certs/server_key.p12 create mode 100644 modules/rabbitmq/src/test/resources/certs/server_key.pem create mode 100644 modules/rabbitmq/src/test/resources/certs/truststore.jks create mode 100644 modules/rabbitmq/src/test/resources/rabbitmq-custom.conf diff --git a/modules/rabbitmq/build.gradle b/modules/rabbitmq/build.gradle new file mode 100644 index 00000000000..7b9a5274a7e --- /dev/null +++ b/modules/rabbitmq/build.gradle @@ -0,0 +1,7 @@ +description = "TestContainers :: RabbitMQ" + +dependencies { + compile project(":testcontainers") + testCompile "com.rabbitmq:amqp-client:5.7.0" + testCompile 'org.assertj:assertj-core:3.12.2' +} diff --git a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java new file mode 100644 index 00000000000..98c2497c2dc --- /dev/null +++ b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java @@ -0,0 +1,367 @@ +package org.testcontainers.containers; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dockerjava.api.command.InspectContainerResponse; +import org.jetbrains.annotations.NotNull; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.utility.MountableFile; + +import java.io.IOException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static java.lang.String.join; +import static java.util.Arrays.asList; + +/** + * Testcontainer for RabbitMQ. + * + * @author Martin Greber + */ +public class RabbitMQContainer extends GenericContainer { + + /** + * The image defaults to the official RabbitmQ image: RabbitMQ. + */ + private static final String DEFAULT_IMAGE_NAME = "rabbitmq"; + private static final String DEFAULT_TAG = "3.7-management-alpine"; + + private static final int DEFAULT_AMQP_PORT = 5672; + private static final int DEFAULT_AMQPS_PORT = 5671; + private static final int DEFAULT_HTTPS_PORT = 15671; + private static final int DEFAULT_HTTP_PORT = 15672; + + private String adminPassword = "guest"; + private String adminUsername = "guest"; + private final List> values = new ArrayList<>(); + + /** + * Creates a Testcontainer using the official RabbitMQ docker image. + */ + public RabbitMQContainer() { + this(DEFAULT_IMAGE_NAME + ":" + DEFAULT_TAG); + } + + /** + * Creates a Testcontainer using a specific docker image. + * + * @param tag The docker image to use. + */ + public RabbitMQContainer(String image) { + super(image); + + addExposedPorts(DEFAULT_AMQP_PORT, DEFAULT_AMQPS_PORT, DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT); + + this.waitStrategy = Wait. + forLogMessage(".*Server startup complete.*", 1) + .withStartupTimeout(Duration.ofSeconds(60)); + } + + @Override + protected void configure() { + if (adminPassword != null) { + addEnv("RABBITMQ_DEFAULT_PASS", adminPassword); + } + } + + @Override + protected void containerIsStarted(InspectContainerResponse containerInfo) { + + values.forEach(command -> { + try { + ExecResult execResult = execInContainer(command.toArray(new String[0])); + if (execResult.getExitCode() != 0) { + logger().error("Could not execute command {}: {}", command, execResult.getStderr()); + } + } catch (IOException | InterruptedException e) { + logger().error("Could not execute command {}: {}", command, e.getMessage()); + } + }); + } + + /** + * @return The admin password for the admin account + */ + public String getAdminPassword() { + return adminPassword; + } + + public String getAdminUsername() { + return adminUsername; + } + + public Integer getAmqpPort() { + return getMappedPort(DEFAULT_AMQP_PORT); + } + + public Integer getAmqpsPort() { + return getMappedPort(DEFAULT_AMQPS_PORT); + } + + public Integer getHttpsPort() { + return getMappedPort(DEFAULT_HTTPS_PORT); + } + + public Integer getHttpPort() { + return getMappedPort(DEFAULT_HTTP_PORT); + } + + /** + * @return AMQP URL for use with AMQP clients. + */ + public String getAmqpUrl() { + return "amqp://" + getContainerIpAddress() + ":" + getAmqpPort(); + } + + /** + * @return AMQPS URL for use with AMQPS clients. + */ + public String getAmqpsUrl() { + return "amqps://" + getContainerIpAddress() + ":" + getAmqpsPort(); + } + + /** + * @return URL of the HTTP management endpoint. + */ + public String getHttpUrl() { + return "http://" + getContainerIpAddress() + ":" + getHttpPort(); + } + + /** + * @return URL of the HTTPS management endpoint. + */ + public String getHttpsUrl() { + return "https://" + getContainerIpAddress() + ":" + getHttpsPort(); + } + + /** + * Sets the password for the admin (default is

guest
) + * + * @param adminPassword The admin password. + * @return This container. + */ + public RabbitMQContainer withAdminPassword(final String adminPassword) { + this.adminPassword = adminPassword; + return this; + } + + public enum SslVerification { + VERIFY_NONE("verify_none"), VERIFY_PEER("verify_peer"); + + SslVerification(String value) { + this.value = value; + } + private final String value; + + } + + public RabbitMQContainer withSSL( + final MountableFile keyFile, + final MountableFile certFile, + final MountableFile caFile, + final SslVerification verify, + boolean failIfNoCert, + int verificationDepth) { + + return withSSL(keyFile, certFile, caFile, verify, failIfNoCert) + .withEnv("RABBITMQ_SSL_DEPTH", String.valueOf(verificationDepth)); + } + + public RabbitMQContainer withSSL( + final MountableFile keyFile, + final MountableFile certFile, + final MountableFile caFile, + final SslVerification verify, + boolean failIfNoCert) { + + return withSSL(keyFile, certFile, caFile, verify) + .withEnv("RABBITMQ_SSL_FAIL_IF_NO_PEER_CERT", String.valueOf(failIfNoCert)); + } + + public RabbitMQContainer withSSL( + final MountableFile keyFile, + final MountableFile certFile, + final MountableFile caFile, + final SslVerification verify) { + + return withEnv("RABBITMQ_SSL_CACERTFILE", "/etc/rabbitmq/ca_cert.pem") + .withEnv("RABBITMQ_SSL_CERTFILE", "/etc/rabbitmq/rabbitmq_cert.pem") + .withEnv("RABBITMQ_SSL_KEYFILE", "/etc/rabbitmq/rabbitmq_key.pem") + .withEnv("RABBITMQ_SSL_VERIFY", verify.value) + .withCopyFileToContainer(certFile, "/etc/rabbitmq/rabbitmq_cert.pem") + .withCopyFileToContainer(caFile, "/etc/rabbitmq/ca_cert.pem") + .withCopyFileToContainer(keyFile, "/etc/rabbitmq/rabbitmq_key.pem"); + } + + public RabbitMQContainer withPluginsEnabled(String... pluginNames) { + List command = new ArrayList<>(asList("rabbitmq-plugins", "enable")); + command.addAll(asList(pluginNames)); + values.add(command); + return self(); + } + + public RabbitMQContainer withBinding(String source, String destination) { + values.add(asList("rabbitmqadmin", "declare", "binding", + "source=" + source, + "destination=" + destination)); + return self(); + } + + public RabbitMQContainer withBinding(String source, String destination, Map arguments, String routingKey, String destinationType) { + values.add(asList("rabbitmqadmin", "declare", "binding", + "source=" + source, + "destination=" + destination, + "routing-key=" + routingKey, + "destination-type=" + destinationType, + "arguments=" + toJson(arguments))); + return self(); + } + + public RabbitMQContainer withParameter(String component, String name, String value) { + values.add(asList("rabbitmqadmin", "declare", "parameter", + "component=" + component, + "name=" + name, + "value=" + value)); + return self(); + } + + public RabbitMQContainer withPermission(String vhost, String user, String configure, String write, String read) { + values.add(asList("rabbitmqadmin", "declare", "permission", + "vhost=" + vhost, + "user=" + user, + "configure=" + configure, + "write=" + write, + "read=" + read)); + return self(); + } + + public RabbitMQContainer withUser(String name, String password) { + values.add(asList("rabbitmqadmin", "declare", "user", + "name=" + name, + "password=" + password, + "tags=")); + return self(); + } + + public RabbitMQContainer withUser(String name, String password, Set tags) { + values.add(asList("rabbitmqadmin", "declare", "user", + "name=" + name, + "password=" + password, + "tags=" + join(",", tags))); + return self(); + } + + public RabbitMQContainer withPolicy(String name, String pattern, Map definition) { + values.add(asList("rabbitmqadmin", "declare", "policy", + "name=" + name, + "pattern=" + pattern, + "definition=" + toJson(definition))); + return self(); + } + + public RabbitMQContainer withPolicy(String name, String pattern, Map definition, int priority, String applyTo) { + values.add(asList("rabbitmqadmin", "declare", "policy", + "name=" + name, + "pattern=" + pattern, + "priority=" + priority, + "apply-to=" + applyTo, + "definition=" + toJson(definition))); + return self(); + } + + public RabbitMQContainer withOperatorPolicy(String name, String pattern, Map definition) { + values.add(new ArrayList<>(asList("rabbitmqadmin", "declare", "operator_policy", + "name=" + name, + "pattern=" + pattern, + "definition=" + toJson(definition)))); + return self(); + } + + public RabbitMQContainer withOperatorPolicy(String name, String pattern, Map definition, int priority, String applyTo) { + values.add(asList("rabbitmqadmin", "declare", "operator_policy", + "name=" + name, + "pattern=" + pattern, + "priority=" + priority, + "apply-to=" + applyTo, + "definition=" + toJson(definition))); + return self(); + } + + public RabbitMQContainer withVhost(String name) { + values.add(asList("rabbitmqadmin", "declare", "vhost", + "name=" + name)); + return self(); + } + + public RabbitMQContainer withVhost(String name, boolean tracing) { + values.add(asList("rabbitmqadmin", "declare", "vhost", + "name=" + name, + "tracing=" + tracing)); + return self(); + } + + public RabbitMQContainer withVhostLimit(String vhost, String name, int value) { + values.add(asList("rabbitmqadmin", "declare", "vhost_limit", + "vhost=" + vhost, + "name=" + name, + "value=" + value)); + return self(); + } + + public RabbitMQContainer withQueue(String name) { + values.add(asList("rabbitmqadmin", "declare", "queue", + "name=" + name)); + return self(); + } + + public RabbitMQContainer withQueue(String name, boolean autoDelete, boolean durable, Map arguments) { + values.add(asList("rabbitmqadmin", "declare", "queue", + "name=" + name, + "auto_delete=" + autoDelete, + "durable=" + durable, + "arguments=" + toJson(arguments))); + return self(); + } + + public RabbitMQContainer withExchange(String name, String type) { + values.add(asList("rabbitmqadmin", "declare", "exchange", + "name=" + name, + "type=" + type)); + return self(); + } + + public RabbitMQContainer withExchange(String name, String type, boolean autoDelete, boolean internal, boolean durable, Map arguments) { + values.add(asList("rabbitmqadmin", "declare", "exchange", + "name=" + name, + "type=" + type, + "auto_delete=" + autoDelete, + "internal=" + internal, + "durable=" + durable, + "arguments=" + toJson(arguments))); + return self(); + } + + /** + * Overwrites the default RabbitMQ configuration file with the supplied one + * + * @param rabbitMQConf The rabbitmq.conf file to use + * @return This container. + */ + public RabbitMQContainer withRabbitMQConfig(MountableFile rabbitMQConf) { + withEnv("RABBITMQ_CONFIG_FILE", "/etc/rabbitmq/rabbitmq-custom.conf"); + return withCopyFileToContainer(rabbitMQConf, "/etc/rabbitmq/rabbitmq-custom.conf"); + } + + @NotNull + private String toJson(Map arguments) { + try { + return new ObjectMapper().writeValueAsString(arguments); + } catch (JsonProcessingException e) { + throw new RuntimeException("Failed to convert arguments into json: " + e.getMessage(), e); + } + } +} diff --git a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerJUnitIntegrationTest.java b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerJUnitIntegrationTest.java new file mode 100644 index 00000000000..ff71db44348 --- /dev/null +++ b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerJUnitIntegrationTest.java @@ -0,0 +1,22 @@ +package org.testcontainers.containers; + +import org.junit.ClassRule; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for basic functionality when used as a @ClassRule. + * + * @author Mrtin Greber + */ +public class RabbitMQContainerJUnitIntegrationTest { + + @ClassRule + public static RabbitMQContainer rabbitMQContainer = new RabbitMQContainer(); + + @Test + public void shouldStart() { + assertThat(rabbitMQContainer.isRunning()).isTrue(); + } +} diff --git a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java new file mode 100644 index 00000000000..4f549a6f5ed --- /dev/null +++ b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java @@ -0,0 +1,233 @@ +package org.testcontainers.containers; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; +import org.junit.Test; +import org.testcontainers.utility.MountableFile; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.security.*; +import java.security.cert.CertificateException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.testcontainers.containers.RabbitMQContainer.SslVerification.VERIFY_PEER; +import static org.testcontainers.utility.MountableFile.forClasspathResource; + +/** + * @author Martin Greber + */ +public class RabbitMQContainerTest { + + public static final String DEFAULT_IMAGE = "rabbitmq:3.7-management-alpine"; + public static final int DEFAULT_AMQPS_PORT = 5671; + public static final int DEFAULT_AMQP_PORT = 5672; + public static final int DEFAULT_HTTPS_PORT = 15671; + public static final int DEFAULT_HTTP_PORT = 15672; + + @Test + public void shouldCreateRabbitMQContainer() { + try (RabbitMQContainer container = new RabbitMQContainer()) { + + assertThat(container.getDockerImageName()).isEqualTo(DEFAULT_IMAGE); + assertThat(container.getAdminPassword()).isEqualTo("guest"); + assertThat(container.getAdminUsername()).isEqualTo("guest"); + + container.start(); + + assertThat(container.getAmqpsUrl()).isEqualTo( + String.format("amqps://%s:%d", container.getContainerIpAddress(), container.getMappedPort(DEFAULT_AMQPS_PORT))); + assertThat(container.getAmqpUrl()).isEqualTo( + String.format("amqp://%s:%d", container.getContainerIpAddress(), container.getMappedPort(DEFAULT_AMQP_PORT))); + assertThat(container.getHttpsUrl()).isEqualTo( + String.format("https://%s:%d", container.getContainerIpAddress(), container.getMappedPort(DEFAULT_HTTPS_PORT))); + assertThat(container.getHttpUrl()).isEqualTo( + String.format("http://%s:%d", container.getContainerIpAddress(), container.getMappedPort(DEFAULT_HTTP_PORT))); + + assertThat(container.getHttpsPort()).isEqualTo(container.getMappedPort(DEFAULT_HTTPS_PORT)); + assertThat(container.getHttpPort()).isEqualTo(container.getMappedPort(DEFAULT_HTTP_PORT)); + assertThat(container.getAmqpsPort()).isEqualTo(container.getMappedPort(DEFAULT_AMQPS_PORT)); + assertThat(container.getAmqpPort()).isEqualTo(container.getMappedPort(DEFAULT_AMQP_PORT)); + + assertThat(container.getLivenessCheckPortNumbers()).containsExactlyInAnyOrder( + container.getMappedPort(DEFAULT_AMQP_PORT), + container.getMappedPort(DEFAULT_AMQPS_PORT), + container.getMappedPort(DEFAULT_HTTP_PORT), + container.getMappedPort(DEFAULT_HTTPS_PORT) + ); + } + } + + @Test + public void shouldCreateRabbitMQContainerWithTag() { + try (RabbitMQContainer container = new RabbitMQContainer(DEFAULT_IMAGE)) { + assertThat(container.getDockerImageName()).isEqualTo(DEFAULT_IMAGE); + } + } + + @Test + public void shouldCreateRabbitMQContainerWithExchange() throws IOException, InterruptedException { + try (RabbitMQContainer container = new RabbitMQContainer()) { + container.withExchange("test-exchange", "direct"); + + container.start(); + + assertThat(container.execInContainer("rabbitmqctl", "list_exchanges").getStdout()) + .containsPattern("test-exchange\\s+direct"); + } + } + + @Test + public void shouldCreateRabbitMQContainerWithQueues() throws IOException, InterruptedException { + try (RabbitMQContainer container = new RabbitMQContainer()) { + + container.withQueue("queue-one") + .withQueue("queue-two", false, true, ImmutableMap.of("x-message-ttl", 1000)); + + container.start(); + + assertThat(container.execInContainer("rabbitmqctl", "list_queues", "name", "arguments").getStdout()) + .containsPattern("queue-one"); + assertThat(container.execInContainer("rabbitmqctl", "list_queues", "name", "arguments").getStdout()) + .containsPattern("queue-two\\s.*x-message-ttl"); + } + } + + @Test + public void shouldMountConfigurationFile() { + try (RabbitMQContainer container = new RabbitMQContainer()) { + + container.withRabbitMQConfig(MountableFile.forClasspathResource("/rabbitmq-custom.conf")); + container.start(); + + assertThat(container.getLogs().contains("/etc/rabbitmq/rabbitmq-custom.conf")).isTrue(); + } + } + + @Test + public void shouldStartTheWholeEnchilada() throws IOException, InterruptedException { + try (RabbitMQContainer container = new RabbitMQContainer()) { + container + .withVhost("vhost1") + .withVhostLimit("vhost1", "max-connections", 1) + .withVhost("vhost2", true) + .withExchange("direct-exchange", "direct") + .withExchange("topic-exchange", "topic") + .withQueue("queue1") + .withQueue("queue2", true, false, ImmutableMap.of("x-message-ttl", 1000)) + .withBinding("direct-exchange", "queue1") + .withUser("user1", "password1") + .withUser("user2", "password2", ImmutableSet.of("administrator")) + .withPermission("vhost1", "user1", ".*", ".*", ".*") + .withPolicy("max length policy", "^dog", ImmutableMap.of("max-length", 1), 1, "queues") + .withPolicy("alternate exchange policy", "^direct-exchange", ImmutableMap.of("alternate-exchange", "amq.direct")) + .withOperatorPolicy("operator policy 1", "^queue1", ImmutableMap.of("message-ttl", 1000), 1, "queues") + .withPluginsEnabled("rabbitmq_shovel", "rabbitmq_random_exchange"); + + container.start(); + + assertThat(container.execInContainer("rabbitmqadmin", "list", "queues") + .getStdout()) + .contains("queue1", "queue2"); + + assertThat(container.execInContainer("rabbitmqadmin", "list", "exchanges") + .getStdout()) + .contains("direct-exchange", "topic-exchange"); + + assertThat(container.execInContainer("rabbitmqadmin", "list", "bindings") + .getStdout()) + .contains("direct-exchange"); + + assertThat(container.execInContainer("rabbitmqadmin", "list", "users") + .getStdout()) + .contains("user1", "user2"); + + assertThat(container.execInContainer("rabbitmqadmin", "list", "policies") + .getStdout()) + .contains("max length policy", "alternate exchange policy"); + + assertThat(container.execInContainer("rabbitmqadmin", "list", "operator_policies") + .getStdout()) + .contains("operator policy 1"); + + assertThat(container.execInContainer("rabbitmq-plugins", "is_enabled", "rabbitmq_shovel", "--quiet") + .getStdout()) + .contains("rabbitmq_shovel is enabled"); + + assertThat(container.execInContainer("rabbitmq-plugins", "is_enabled", "rabbitmq_random_exchange", "--quiet") + .getStdout()) + .contains("rabbitmq_random_exchange is enabled"); + } + } + + @Test + public void shouldThrowExceptionForDodgyJson() { + try (RabbitMQContainer container = new RabbitMQContainer()) { + + assertThatCode(() -> + container.withQueue( + "queue2", + true, + false, + ImmutableMap.of("x-message-ttl", container)) + ).hasMessageStartingWith("Failed to convert arguments into json"); + + } + } + + @Test + public void shouldWorkWithSSL() { + try (RabbitMQContainer container = new RabbitMQContainer()) { + container.withSSL( + forClasspathResource("/certs/server_key.pem", 0644), + forClasspathResource("/certs/server_certificate.pem", 0644), + forClasspathResource("/certs/ca_certificate.pem", 0644), + VERIFY_PEER, + true + ); + + container.start(); + + assertThatCode(() -> { + ConnectionFactory connectionFactory = new ConnectionFactory(); + connectionFactory.useSslProtocol(createSslContext( + "certs/client_key.p12", "password", + "certs/truststore.jks", "password")); + connectionFactory.enableHostnameVerification(); + connectionFactory.setUri(container.getAmqpsUrl()); + connectionFactory.setPassword(container.getAdminPassword()); + Connection connection = connectionFactory.newConnection(); + Channel channel = connection.openChannel().orElseThrow(() -> new RuntimeException("Failed to Open channel")); + channel.close(); + connection.close(); + }).doesNotThrowAnyException(); + } + } + + private SSLContext createSslContext(String keystoreFile, String keystorePassword, String truststoreFile, String truststorePassword) + throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException, KeyManagementException { + ClassLoader classLoader = getClass().getClassLoader(); + + KeyStore ks = KeyStore.getInstance("PKCS12"); + ks.load(new FileInputStream(new File(classLoader.getResource(keystoreFile).getFile())), keystorePassword.toCharArray()); + KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + kmf.init(ks, "password".toCharArray()); + + KeyStore trustStore = KeyStore.getInstance("PKCS12"); + trustStore.load(new FileInputStream(new File(classLoader.getResource(truststoreFile).getFile())), truststorePassword.toCharArray()); + TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); + tmf.init(trustStore); + + SSLContext c = SSLContext.getInstance("TLSv1.2"); + c.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + return c; + } +} diff --git a/modules/rabbitmq/src/test/resources/certs/ca_certificate.pem b/modules/rabbitmq/src/test/resources/certs/ca_certificate.pem new file mode 100644 index 00000000000..f22df829095 --- /dev/null +++ b/modules/rabbitmq/src/test/resources/certs/ca_certificate.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDRzCCAi+gAwIBAgIJAJJIMzvZuRzlMA0GCSqGSIb3DQEBCwUAMDExIDAeBgNV +BAMMF1RMU0dlblNlbGZTaWduZWR0Um9vdENBMQ0wCwYDVQQHDAQkJCQkMCAXDTE5 +MDUwMjA3MjI0OVoYDzIxMTkwNDA4MDcyMjQ5WjAxMSAwHgYDVQQDDBdUTFNHZW5T +ZWxmU2lnbmVkdFJvb3RDQTENMAsGA1UEBwwEJCQkJDCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAKko8FmfzrLHyZckvdR1oiSZf80m0t66TMqtLat1Oxjh +CjsxvswwJ/m2I5dM48hwZ+0b2ufkvaudLPq/8jDGyONVfjMGlbe1YlmQMDC7YWdI +XM1nCWAZIKaOHwIkfswuVBAdBVYV4Polu6wjVt5edEpl/IWEpPicXjLOY1Fw3q67 +5tP2Mmo6TJg5YqgB4fH4SmajtP3j+H4puQ8ZPIs26mInEgfCyrMWey/oQX8qqMph +pKMEJYE7DHawriFraOooJadJYojbY5H27nmJe8yXURb3wSQSaKnFZL25cmVm2kue +/lw+n+a2wLdHdU4cmghCURalhcXUNZe7UbdRZ9e9r2cCAwEAAaNgMF4wCwYDVR0P +BAQDAgEGMB0GA1UdDgQWBBSZiNur/XHsqSfdWnB1NPi/ql5+tzAfBgNVHSMEGDAW +gBSZiNur/XHsqSfdWnB1NPi/ql5+tzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQAar/db/T7izD4pyh2titl7Dkgp2iTditfgqRlU0yVGiiB6rLmY +sYE2QAuFhgqyRLPcjVV8F39iRJHQ17SGT8e2iAaUTnbQj0AiskKjonF9+quKuVbr +TpYHk+guS0Jn2rU6HK8WQeYZOh3WdLTu4ArXkxywgwVssQQ9JmpTd9YEYePWfs7i +WZB6AQyL9CD3z1j4i1G4ft6pB1Ps5XjznqMZ2//7AUpoRTrettWqorPWwudQ9yna +B4S6KtvpnxUQSeHJW6Q4NvTrOsvHEOCa6OtwYbWmLf+qbpPb8oHt9UF3ze2PJopB +QzsQop1+gPudG0DX0SgyuQT+SsFjYlDazZdZ +-----END CERTIFICATE----- diff --git a/modules/rabbitmq/src/test/resources/certs/ca_key.pem b/modules/rabbitmq/src/test/resources/certs/ca_key.pem new file mode 100644 index 00000000000..29ed148fb01 --- /dev/null +++ b/modules/rabbitmq/src/test/resources/certs/ca_key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCpKPBZn86yx8mX +JL3UdaIkmX/NJtLeukzKrS2rdTsY4Qo7Mb7MMCf5tiOXTOPIcGftG9rn5L2rnSz6 +v/IwxsjjVX4zBpW3tWJZkDAwu2FnSFzNZwlgGSCmjh8CJH7MLlQQHQVWFeD6Jbus +I1beXnRKZfyFhKT4nF4yzmNRcN6uu+bT9jJqOkyYOWKoAeHx+Epmo7T94/h+KbkP +GTyLNupiJxIHwsqzFnsv6EF/KqjKYaSjBCWBOwx2sK4ha2jqKCWnSWKI22OR9u55 +iXvMl1EW98EkEmipxWS9uXJlZtpLnv5cPp/mtsC3R3VOHJoIQlEWpYXF1DWXu1G3 +UWfXva9nAgMBAAECggEAVHUVM5o/aDGp6+WzRa2Jy/47ueEFbaDUkGjQoYeBfxV0 +t0NKAMaWXu3abUM9gyjkKpU6wYcKT/HEsFk/gazuRdq8jJtgCv3r4c3E0b/sjNWr +R/6Gxs0k6SOSRc6U5DrJS9ZBgM6hqiNGxVZLm/DK3Q54eu1UNLBVs8Yp/lJ9S/3C +oM6lhex6SoCTiZyFb3ZvVTmBAZe/ROFXZAtmVac1KORBFN8FpkXQcB3EPTkvZmNJ +sSp8dVPBLABm6gAbdJRLVqqndkHDPsopFGHLGzo4LtR7AlEklfxBdC/XlYmudZeY +peWba+ZdLH3sCybE55Xcm7nTfRRSQSLDv++r73GCCQKBgQDe/7ltr8N+gWpb1fDC +nEyBU4WHoTRp09sD+MZe3B06PX3WNRrf+3wHj9oB9UkhZ1baCq84mRzBiUKpAYrw +GrC4iL4kqbWeIZzcJqLZ7zk1zzHzW0+zzNJSjzE8dq3W26+SnuLqKDFpkxtAyIMQ +Rp9AZJIW8q9ay6ZCsZk/NNBoKwKBgQDCMYh53dXKiYtr6tAXXManWWpQ24af6WKl +3faXCw5eXG7ic5bNZD7ql+DWJzRylkI4yeLDvWAsHprtyIBYmkRki3lBAnpLeHY8 +7XWjZeOVeIpiSbG+1Fo7LrOuWi3wYGOZ61QUTjqJhvZJpzZBMEgo7cx+WQhlrioI +9BbXqPabtQKBgAZJv7jQE/slOxKL3dYfAilDaaiBazDwwGRER5O1MT3LLhk0NiXK +uZyc+dDEUeOXPmO3mWlHKABtFmwdlwVeO014zaPLBUwINpwemsj6beqOhSIPmRfA +1s1tLD5AOnasiy7fPBbOO1Z2x3X0MX3r/+GY9GWhQkCVLYMD7wZRPu8xAoGBAJMr +PNW2s+ZJtPq1Orzp17dOAU+D/xPDqLoxbEbt3xbOEE7X8Mp5lWDudzt0/L92dntZ +LNzQ8UiebSWVlQcQ6pIUTXFiMlJt2ZW1FDkf54kIkD+KwATyI+vEKfIRb81DD1i/ +yrmUy7IcMRyCd5CRya4TAa4jRUTh6ANfEMyhxTsdAoGBAIFub7nikxPQ8J4ItWq8 +EKOptyziJ38KnL2nZnFyVDu6uZDtSk9Tf1dHqWy8D4yP9K3OLshipEp7rnNG25dr +e7fkB58K77eSoaEpCZmwzPZstwkXbeBl+DHerDyC2UBXwIUWWFq+FuCdRQqCoYmU +OGiaT92l+UInN7VchZjJS7Oj +-----END PRIVATE KEY----- diff --git a/modules/rabbitmq/src/test/resources/certs/client_certificate.pem b/modules/rabbitmq/src/test/resources/certs/client_certificate.pem new file mode 100644 index 00000000000..f6d75c8c272 --- /dev/null +++ b/modules/rabbitmq/src/test/resources/certs/client_certificate.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDKDCCAhCgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAxMSAwHgYDVQQDDBdUTFNH +ZW5TZWxmU2lnbmVkdFJvb3RDQTENMAsGA1UEBwwEJCQkJDAgFw0xOTA1MDIwNzIy +NDlaGA8yMTE5MDQwODA3MjI0OVowIzEQMA4GA1UEAwwHQzY1U1RUMjEPMA0GA1UE +CgwGY2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtcAse1RO +mRngQ4NNFFgyPgbq2eZc3c/ERKdg2xKi2XESMIbOreVkoBRq1IXoxkJHgQZJvPP8 +JbH7hmY+DFZcreszlEk5FI7bvxFNQM3CKsy2LeEcN9BTchSEsWgmSbwiZJ31kEB8 +u/i+/GpgTTxtwPWi/zTep+7fKM4dyzYX9WcMu8wif3jKIzqXVUqHpWopFUK9zLQf +JRnyfiD2uYbfcZYopsIG+wdL2Ebgfr8fpc+YsWn0aA0zBiEFAKYh9bewPqDIZKyg +qd4FKzeUaqGHynttNxKDJ9z0oD84PKFEOuryf7V8ODU8+kI1+y/+3C5Hlq6n92y6 +eSPn81W44+WZeQIDAQABo1cwVTAJBgNVHRMEAjAAMAsGA1UdDwQEAwIFoDATBgNV +HSUEDDAKBggrBgEFBQcDAjAmBgNVHREEHzAdggdDNjVTVFQyggdDNjVTVFQyggls +b2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggEBAH3oqsmr+hIfMEpZUolPNuAl43TF +KUu14SeFyS+P8fEIQVGkHdpEpHzC0G+fvREAy4YSw0WE8upOq6tkbbjen222SP0J +C2vj0B9cCPLZUQ4kW6Z7JJD/1qryML0XB9uMPHqDeV/yBETJZW80TV0f7p8GWKXe ++spu1W13rJ0jvQQd9anNEx3VhcKTwBIcTAhTHothxFLLhX1zb/oVZcl1riBopBFm +WtrZRTCHs31o5GBnIs540uH0f2iCk7wUf8M/evxv5F1hms+NV7kfOz7fyCEXc9La +P9pDjuxTNOUeOZH815BR9Lj71txY9AdjXqEXj7JdecACLsjHc+LMEP36cls= +-----END CERTIFICATE----- diff --git a/modules/rabbitmq/src/test/resources/certs/client_key.p12 b/modules/rabbitmq/src/test/resources/certs/client_key.p12 new file mode 100644 index 0000000000000000000000000000000000000000..15ce61136e2e1239cbaa457929f35f112bcbe72f GIT binary patch literal 2413 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 literal 0 HcmV?d00001 diff --git a/modules/rabbitmq/src/test/resources/certs/server_key.pem b/modules/rabbitmq/src/test/resources/certs/server_key.pem new file mode 100644 index 00000000000..dfbfb6db7e3 --- /dev/null +++ b/modules/rabbitmq/src/test/resources/certs/server_key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAqR0QXKtbKVeEuCmZGcZAlAlTBC8E/G3UuX6qKwTR1xEOvUWe +BH1n0WeXXGd/p/y6P4lRBeWNBZ9KcvIlNDeDMy05NfxnO1vnJk9E8/0xwMiY1LJd +MHzIzhmrrqXo0u3DT8MmoNR67CTcnG21gi1GrjW8a747yFF0xfukEc6FkyVqLsjt +CkHPwrc/sBHVS3aivNWGkJzAeBXBdWJAg3ZC6T9U+Y8cndWQrpYMJvek1IewlyDS +spHZDFmM1OwVwypnMt4fGgaX5IlUMnNgKmisOSuI529rxLF+mvYIQLRl5bP+1/c9 +JD5MZ5krA3SrjdwRFS3sQXC3nuHqJofFXNkbXQIDAQABAoIBAA0dxvYZCEIFmrKZ +71jzanDQ5FJvvyhA8H3OmC4r+oZ+uTDu5FmezF2OdkvhbyI9VMi2wsT9T9m+yAxw +QXhyUce3WzeXsv4Em8H55fQykBhOtqPQja/EDeMGVK2ACrXJYRufnDBfKoWEOmQb +kjddgZzjaBDHOWXJA5CTet8ysGOAJBTxyzU69k5Vj9B5abG9CofNzGOFF+Uleff5 +ip3sz7JpDXCex3oEs98veco6+8i/MZNo3BnwB5J+P+2MFFKONfPwuNyKAWBza2/X +66Lk3xXBjLJJ+Ww16jkqueTXEq6GCFXavNfdL9aonth5V5YYR/cj+2u2LM1oj9cJ +bp0xbvUCgYEA2Svq1DyR9cfTwrbc/0J2JfrjavClzDYU2oeO2fSU85WEEjJguaja +17Vdo/UsJtiUiSq4UhI1n0haaIpTBCeF2tHGXVEYZ7ZBi1zzdWbWlDxFmi+rcE57 +ytx5w+iLE366tQEMa/Jn3bly54pG5JZAr9TXkpg9sMbzWZri2ocyU/cCgYEAx1l/ +9X9C/OruDp/MhhmVwKfw/X2+RhZRuv0pPcpJu7/gIoLgaxNj41XSeLqLYMlisaRk +GFU17GFXtfRGE1a3z+jj8UPTP2sHk3w8m0yI+pgWgsvG0TJ0B+XsRfpVxFiIoaEs +3AsBaGR+hrRY1dpaJ9Cu3J9mEeToTpbCzPzVDksCgYEAzwSvWNvYY4u2UFHSvz2S +tMfBzCpUUiNno50/ToN5De4ENPhy/eh5nNDVz7qh+PHSPiNMC2gyV4E4NZlOY5Jt +Zdc8ma35brvtJTVZGxwKBsqhqsYwTeFy3kFnjZn6IX5X6r1yIuCzpEfowdEtnS+h +wDtLuAGKJR6x0UP1Zk0ka6cCgYBGE6I1rJzhx7wTi/0bjtbjuKWwlolSnfnxH5ll +zTyKMXMa7qLxQQm2Gq84HWtthJ2bEMzW+O1RwQ5SOiKAHdXT0mx+nXcfLgKlx+CO +PyNP5DLVm8iyNWgwdpTOLKgFs5GkL8JTP9Mo3VrVA4TO+EkFAgjWKXp6A9vd9IVa +Be7nbQKBgAVtFKuf9nbCMfN+W1gN0vlW2lwxCTa4w0KHgIlGIIvnYVuixSgu9fGt +uylQcQirEjqrdzdVF9L2BQ37ZcLaGh1LoCmx8XVCX/HhbwW2RP798P3Z1P7htm16 +ha5OfuPjHvoZklbYJo6EORJZQehS2VP63pjdnmUeMHPFzrPUevI5 +-----END RSA PRIVATE KEY----- diff --git a/modules/rabbitmq/src/test/resources/certs/truststore.jks b/modules/rabbitmq/src/test/resources/certs/truststore.jks new file mode 100644 index 0000000000000000000000000000000000000000..84fce84266ba154fc2278b4ef033811f2b292af7 GIT binary patch literal 1130 zcmV-w1eNWumexf~gMGVwZ001T`xi))j-ZQ$EOubqh*D$*)ZW)X?JjRo#e&H>7ZbPkK?aM3 zxgT?kuIy=K6Pi=M$2Ru7E->F5Tv$NkFFj+L|3XLVnctX2*VbI^To?RrMJtBfAG_UN zX8pY${t+sgfs-Rh=LR7d6O391Xce>OClLOoi60u)%NqM*1)S=b@V)Oi1wU*3PWAL1 z_tO_teeLE0uWZe^vilB$a~iOfEt@x9WQbK3b1RCl;!4S~>L{`x{lKB*3RVVO@I9ca zWLnX10d34+24&a)|`Ubk62WhcJcL=P8kSCH?h0pM3Fs>et!_yv&^^&Fr zsjc>(rm0!s2<72ij@Xa9=d-`Wd7DqCeor6h1`2%a8VljL66Vx=eL^>EZ!&HVH`aGO zr!D6UPjx1DpyL~flTF1TkY8!L;Ba*pCts5Q2C0e*QuY?~Ooqc_h1_ru`&_W}9a!3V zC^gOmiYX9vxc92-yWL875hvPW&E$#42@d@%ga0X8N0mBMiN?{L2OlPLkIugriPF*7 zO2?`!76&`E+Q@eoO~)2fP64ks9cD6#LGv;5*Jx{0aT?xYG6VbP*mm_{Zh*disBhjT zq+iL4)tSQ%Qwtolmqa{^qALv+#|PM@+v2^v#l*zWhk5SkH*52{@{Xx=ykh(x#qaU4 zj|~vV$2S0}9i{iYMRP_xMq2?m{o4R5#9lP)TUO%GfAR1g@7G000kvzD`j~BTjUDJr`nDNcD5{Lhyi*Pe62;2`fjeDhD^wXmsPnQ>EZCOF*0B Date: Tue, 16 Jul 2019 16:32:12 +0200 Subject: [PATCH 0802/5615] increase db2 timeout (#1617) --- .../containers/Db2Container.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java b/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java index 6e45b0f7f12..429cc060094 100644 --- a/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java +++ b/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java @@ -36,14 +36,14 @@ public Db2Container(String imageName) { withPrivilegedMode(true); this.waitStrategy = new LogMessageWaitStrategy() .withRegEx(".*Setup has completed\\..*") - .withStartupTimeout(Duration.of(3, ChronoUnit.MINUTES)); + .withStartupTimeout(Duration.of(10, ChronoUnit.MINUTES)); } - + @Override protected Set getLivenessCheckPorts() { return new HashSet<>(getMappedPort(DB2_PORT)); } - + @Override protected void configure() { // If license was not accepted programatically, check if it was accepted via resource file @@ -53,11 +53,11 @@ protected void configure() { } addExposedPort(DB2_PORT); - + addEnv("DBNAME", databaseName); addEnv("DB2INSTANCE", username); addEnv("DB2INST1_PASSWORD", password); - + // These settings help the DB2 container start faster if (!getEnvMap().containsKey("AUTOCONFIG")) addEnv("AUTOCONFIG", "false"); @@ -93,30 +93,30 @@ public String getUsername() { public String getPassword() { return password; } - + @Override public String getDatabaseName() { return databaseName; } - + @Override public Db2Container withUsername(String username) { this.username = username; return this; } - + @Override public Db2Container withPassword(String password) { this.password = password; return this; } - + @Override public Db2Container withDatabaseName(String dbName) { this.databaseName = dbName; return this; } - + @Override protected void waitUntilContainerStarted() { getWaitStrategy().waitUntilReady(this); From fbb4d8af1489d9e2c5a463e44ab25ed4dfde7eb1 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 17 Jul 2019 07:42:13 +0200 Subject: [PATCH 0803/5615] fix RabbitMQ's Javadoc --- .../java/org/testcontainers/containers/RabbitMQContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java index 98c2497c2dc..f16f005df5e 100644 --- a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java +++ b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java @@ -49,7 +49,7 @@ public RabbitMQContainer() { /** * Creates a Testcontainer using a specific docker image. * - * @param tag The docker image to use. + * @param image The docker image to use. */ public RabbitMQContainer(String image) { super(image); From b840912d9df616e551a77f4b58502166df32c8cb Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 17 Jul 2019 15:19:48 +0200 Subject: [PATCH 0804/5615] Simplify Kafka container by deferring the Kafka command (#1458, Fixes #1450) * Simplify Kafka container by deferring the Kafka command * extract `startZookeeper` method, add `PORT_NOT_ASSIGNED` constant * keep network&alias for backward compatibility --- .../containers/KafkaContainer.java | 78 +++++++++++++------ 1 file changed, 53 insertions(+), 25 deletions(-) diff --git a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java index b1be94f1e67..b08d6611e6d 100644 --- a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java +++ b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java @@ -1,9 +1,13 @@ package org.testcontainers.containers; +import com.github.dockerjava.api.command.ExecCreateCmdResponse; +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.core.command.ExecStartResultCallback; +import lombok.SneakyThrows; import org.testcontainers.utility.Base58; import org.testcontainers.utility.TestcontainersConfiguration; -import java.util.stream.Stream; +import java.util.concurrent.TimeUnit; /** * This container wraps Confluent Kafka and Zookeeper (optionally) @@ -15,9 +19,11 @@ public class KafkaContainer extends GenericContainer { public static final int ZOOKEEPER_PORT = 2181; + private static final int PORT_NOT_ASSIGNED = -1; + protected String externalZookeeperConnect = null; - protected SocatContainer proxy; + private int port = PORT_NOT_ASSIGNED; public KafkaContainer() { this("5.2.1"); @@ -26,6 +32,7 @@ public KafkaContainer() { public KafkaContainer(String confluentPlatformVersion) { super(TestcontainersConfiguration.getInstance().getKafkaImage() + ":" + confluentPlatformVersion); + // TODO Only for backward compatibility withNetwork(Network.newNetwork()); withNetworkAliases("kafka-" + Base58.randomString(6)); withExposedPorts(KAFKA_PORT); @@ -54,40 +61,61 @@ public KafkaContainer withExternalZookeeper(String connectString) { } public String getBootstrapServers() { - return String.format("PLAINTEXT://%s:%s", proxy.getContainerIpAddress(), proxy.getFirstMappedPort()); + if (port == PORT_NOT_ASSIGNED) { + throw new IllegalStateException("You should start Kafka container first"); + } + return String.format("PLAINTEXT://%s:%s", getContainerIpAddress(), port); } @Override protected void doStart() { - String networkAlias = getNetworkAliases().get(0); - proxy = new SocatContainer() - .withNetwork(getNetwork()) - .withTarget(KAFKA_PORT, networkAlias) - .withTarget(ZOOKEEPER_PORT, networkAlias); - - proxy.start(); - withEnv("KAFKA_ADVERTISED_LISTENERS", "BROKER://" + networkAlias + ":9092" + "," + getBootstrapServers()); + withCommand("sleep infinity"); - if (externalZookeeperConnect != null) { - withEnv("KAFKA_ZOOKEEPER_CONNECT", externalZookeeperConnect); - } else { + if (externalZookeeperConnect == null) { addExposedPort(ZOOKEEPER_PORT); - withEnv("KAFKA_ZOOKEEPER_CONNECT", "localhost:2181"); - withCommand( - "sh", - "-c", - // Use command to create the file to avoid file mounting (useful when you run your tests against a remote Docker daemon) - "printf 'clientPort=2181\ndataDir=/var/lib/zookeeper/data\ndataLogDir=/var/lib/zookeeper/log' > /zookeeper.properties" + - " && zookeeper-server-start /zookeeper.properties" + - " & /etc/confluent/docker/run" - ); } super.doStart(); } @Override - public void stop() { - Stream.of(super::stop, proxy::stop).parallel().forEach(Runnable::run); + @SneakyThrows + protected void containerIsStarting(InspectContainerResponse containerInfo) { + super.containerIsStarting(containerInfo); + + port = getMappedPort(KAFKA_PORT); + + final String zookeeperConnect; + if (externalZookeeperConnect != null) { + zookeeperConnect = externalZookeeperConnect; + } else { + zookeeperConnect = startZookeeper(); + } + + String internalIp = containerInfo.getNetworkSettings().getIpAddress(); + + ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(getContainerId()) + .withCmd("sh", "-c", "" + + "export KAFKA_ZOOKEEPER_CONNECT=" + zookeeperConnect + "\n" + + "export KAFKA_ADVERTISED_LISTENERS=" + getBootstrapServers() + "," + String.format("BROKER://%s:9092", internalIp) + "\n" + + "/etc/confluent/docker/run" + ) + .exec(); + + dockerClient.execStartCmd(execCreateCmdResponse.getId()).exec(new ExecStartResultCallback()).awaitStarted(10, TimeUnit.SECONDS); + } + + @SneakyThrows(InterruptedException.class) + private String startZookeeper() { + ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(getContainerId()) + .withCmd("sh", "-c", "" + + "printf 'clientPort=" + ZOOKEEPER_PORT + "\ndataDir=/var/lib/zookeeper/data\ndataLogDir=/var/lib/zookeeper/log' > /zookeeper.properties\n" + + "zookeeper-server-start /zookeeper.properties\n" + ) + .exec(); + + dockerClient.execStartCmd(execCreateCmdResponse.getId()).exec(new ExecStartResultCallback()).awaitStarted(10, TimeUnit.SECONDS); + + return "localhost:" + ZOOKEEPER_PORT; } } From 7ca3f9f47bee462be2481b0601a491d156f8108a Mon Sep 17 00:00:00 2001 From: Andrew Guibert Date: Wed, 17 Jul 2019 14:42:49 -0500 Subject: [PATCH 0805/5615] Add new withDockerfile methods to ImageFromDockerfile (#1535) * Add new withDockerfile methods to ImageFromDockerfile These methods are preferable to the existing withDockerfilePath methods because the docker-java API will honor .dockerignore files if withDockerfile(File) is used. * Review comments for .dockerignore support PR * Update core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java Co-Authored-By: Kevin Wittek --- .../images/builder/ImageFromDockerfile.java | 30 ++++++++++ .../images/builder/DockerfileBuildTest.java | 12 +++- .../images/builder/DockerignoreTest.java | 56 +++++++++++++++++++ .../dockerfile-build-invalid/.dockerignore | 3 + .../dockerfile-build-invalid/Dockerfile | 1 + .../dockerfile-build-test/.dockerignore | 1 + .../Dockerfile-currentdir | 2 + .../should_be_ignored.txt | 1 + .../should_not_be_ignored.txt | 1 + 9 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 core/src/test/java/org/testcontainers/images/builder/DockerignoreTest.java create mode 100644 core/src/test/resources/dockerfile-build-invalid/.dockerignore create mode 100644 core/src/test/resources/dockerfile-build-invalid/Dockerfile create mode 100644 core/src/test/resources/dockerfile-build-test/.dockerignore create mode 100644 core/src/test/resources/dockerfile-build-test/Dockerfile-currentdir create mode 100644 core/src/test/resources/dockerfile-build-test/should_be_ignored.txt create mode 100644 core/src/test/resources/dockerfile-build-test/should_not_be_ignored.txt diff --git a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java index c7d409c0660..f4968c11ff0 100644 --- a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java +++ b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java @@ -10,6 +10,7 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; +import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.testcontainers.DockerClientFactory; @@ -18,9 +19,11 @@ import org.testcontainers.utility.DockerLoggerFactory; import org.testcontainers.utility.LazyFuture; +import java.io.File; import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; +import java.nio.file.Path; import java.util.HashMap; import java.util.Map; import java.util.Optional; @@ -63,6 +66,7 @@ public class ImageFromDockerfile extends LazyFuture implements private final Map transferables = new HashMap<>(); private final Map buildArgs = new HashMap<>(); private Optional dockerFilePath = Optional.empty(); + private Optional dockerfile = Optional.empty(); public ImageFromDockerfile() { this("testcontainers/" + Base58.randomString(16).toLowerCase()); @@ -119,6 +123,8 @@ public void onNext(BuildResponseItem item) { configure(buildImageCmd); BuildImageResultCallback exec = buildImageCmd.exec(resultCallback); + + long bytesToDockerDaemon = 0; // To build an image, we have to send the context to Docker in TAR archive format try (TarArchiveOutputStream tarArchive = new TarArchiveOutputStream(new GZIPOutputStream(out))) { @@ -128,9 +134,15 @@ public void onNext(BuildResponseItem item) { Transferable transferable = entry.getValue(); final String destination = entry.getKey(); transferable.transferTo(tarArchive, destination); + bytesToDockerDaemon += transferable.getSize(); } tarArchive.finish(); } + + log.info("Transferred {} KB to Docker daemon", FileUtils.byteCountToDisplaySize(bytesToDockerDaemon)); + if (bytesToDockerDaemon > FileUtils.ONE_MB * 50) // warn if >50MB sent to docker daemon + log.warn("A large amount of data was sent to the Docker daemon ({}). Consider using a .dockerignore file for better performance.", + FileUtils.byteCountToDisplaySize(bytesToDockerDaemon)); exec.awaitImageId(); @@ -143,6 +155,7 @@ public void onNext(BuildResponseItem item) { protected void configure(BuildImageCmd buildImageCmd) { buildImageCmd.withTag(this.getDockerImageName()); this.dockerFilePath.ifPresent(buildImageCmd::withDockerfilePath); + this.dockerfile.ifPresent(p -> buildImageCmd.withDockerfile(p.toFile())); this.buildArgs.forEach(buildImageCmd::withBuildArg); } @@ -156,8 +169,25 @@ public ImageFromDockerfile withBuildArgs(final Map args) { return this; } + /** + * Sets the Dockerfile to be used for this image. + * @deprecated It is recommended to use {@link #withDockerfile} instead because it honors + * .dockerignore files and therefore will be more efficient + * @param relativePathFromBuildRoot + */ + @Deprecated public ImageFromDockerfile withDockerfilePath(String relativePathFromBuildRoot) { this.dockerFilePath = Optional.of(relativePathFromBuildRoot); return this; } + + /** + * Sets the Dockerfile to be used for this image. + * @param dockerfile + */ + public ImageFromDockerfile withDockerfile(Path dockerfile) { + this.dockerfile = Optional.of(dockerfile); + return this; + } + } diff --git a/core/src/test/java/org/testcontainers/images/builder/DockerfileBuildTest.java b/core/src/test/java/org/testcontainers/images/builder/DockerfileBuildTest.java index 4c33fe9118b..9a9d647d71b 100644 --- a/core/src/test/java/org/testcontainers/images/builder/DockerfileBuildTest.java +++ b/core/src/test/java/org/testcontainers/images/builder/DockerfileBuildTest.java @@ -6,6 +6,7 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; +import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; @@ -14,7 +15,7 @@ @RunWith(Parameterized.class) public class DockerfileBuildTest { - private static final Path RESOURCE_PATH = Paths.get("src/test/resources/dockerfile-build-test"); + static final Path RESOURCE_PATH = Paths.get("src/test/resources/dockerfile-build-test"); public String expectedFileContent; public ImageFromDockerfile image; @@ -44,6 +45,13 @@ public static Object[][] parameters() { .withDockerfilePath("./Dockerfile-buildarg") .withBuildArg("CUSTOM_ARG", "test7890") }, + + // Dockerfile build using withDockerfile(File) + new Object[]{"test4567", + new ImageFromDockerfile() + .withFileFromPath(".", RESOURCE_PATH) + .withDockerfile(RESOURCE_PATH.resolve("Dockerfile-alt")) + }, }; } @@ -57,11 +65,11 @@ public void performTest() { try (final GenericContainer container = new GenericContainer(image) .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) .withCommand("cat", "/test.txt")) { - container.start(); final String logs = container.getLogs(); assertTrue("expected file content indicates that dockerfile build steps have been run", logs.contains(expectedFileContent)); } } + } diff --git a/core/src/test/java/org/testcontainers/images/builder/DockerignoreTest.java b/core/src/test/java/org/testcontainers/images/builder/DockerignoreTest.java new file mode 100644 index 00000000000..faf5fb950b8 --- /dev/null +++ b/core/src/test/java/org/testcontainers/images/builder/DockerignoreTest.java @@ -0,0 +1,56 @@ +package org.testcontainers.images.builder; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.rnorth.visibleassertions.VisibleAssertions.fail; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; + +import com.github.dockerjava.api.exception.DockerClientException; + +public class DockerignoreTest { + + private static final Path INVALID_DOCKERIGNORE_PATH = Paths.get("src/test/resources/dockerfile-build-invalid"); + + @Test + public void testInvalidDockerignore() throws Exception { + try { + new ImageFromDockerfile() + .withFileFromPath(".", INVALID_DOCKERIGNORE_PATH) + .withDockerfile(INVALID_DOCKERIGNORE_PATH.resolve("Dockerfile")) + .get(); + fail("Should not be able to build an image with an invalid .dockerignore file"); + } + catch (DockerClientException e) { + if (!e.getMessage().contains("Invalid pattern")) + throw e; + } + } + + @SuppressWarnings("resource") + @Test + public void testValidDockerignore() throws Exception { + ImageFromDockerfile img = new ImageFromDockerfile() + .withFileFromPath(".", DockerfileBuildTest.RESOURCE_PATH) + .withDockerfile(DockerfileBuildTest.RESOURCE_PATH.resolve("Dockerfile-currentdir")); + try( + final GenericContainer container = new GenericContainer<>(img.get()) + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .withCommand("ls", "/") + ) { + + container.start(); + + final String logs = container.getLogs(); + assertTrue("Files in the container indicated the .dockerignore was not applied. Output was: " + logs, + logs.contains("should_not_be_ignored.txt")); + assertTrue("Files in the container indicated the .dockerignore was not applied. Output was: " + logs, + !logs.contains("should_be_ignored.txt")); + } + } + +} diff --git a/core/src/test/resources/dockerfile-build-invalid/.dockerignore b/core/src/test/resources/dockerfile-build-invalid/.dockerignore new file mode 100644 index 00000000000..5f06037fe04 --- /dev/null +++ b/core/src/test/resources/dockerfile-build-invalid/.dockerignore @@ -0,0 +1,3 @@ +# This is an invalid dockerignore file +*~ +[a-b-c] \ No newline at end of file diff --git a/core/src/test/resources/dockerfile-build-invalid/Dockerfile b/core/src/test/resources/dockerfile-build-invalid/Dockerfile new file mode 100644 index 00000000000..6140f575ef7 --- /dev/null +++ b/core/src/test/resources/dockerfile-build-invalid/Dockerfile @@ -0,0 +1 @@ +FROM alpine:3.2 diff --git a/core/src/test/resources/dockerfile-build-test/.dockerignore b/core/src/test/resources/dockerfile-build-test/.dockerignore new file mode 100644 index 00000000000..a6bd49d7d81 --- /dev/null +++ b/core/src/test/resources/dockerfile-build-test/.dockerignore @@ -0,0 +1 @@ +should_be_ignored.txt diff --git a/core/src/test/resources/dockerfile-build-test/Dockerfile-currentdir b/core/src/test/resources/dockerfile-build-test/Dockerfile-currentdir new file mode 100644 index 00000000000..97f80bdfa65 --- /dev/null +++ b/core/src/test/resources/dockerfile-build-test/Dockerfile-currentdir @@ -0,0 +1,2 @@ +FROM alpine:3.2 +COPY . / diff --git a/core/src/test/resources/dockerfile-build-test/should_be_ignored.txt b/core/src/test/resources/dockerfile-build-test/should_be_ignored.txt new file mode 100644 index 00000000000..6b04bcc9cec --- /dev/null +++ b/core/src/test/resources/dockerfile-build-test/should_be_ignored.txt @@ -0,0 +1 @@ +this file should be ignored by a .dockerignore file \ No newline at end of file diff --git a/core/src/test/resources/dockerfile-build-test/should_not_be_ignored.txt b/core/src/test/resources/dockerfile-build-test/should_not_be_ignored.txt new file mode 100644 index 00000000000..675ce985900 --- /dev/null +++ b/core/src/test/resources/dockerfile-build-test/should_not_be_ignored.txt @@ -0,0 +1 @@ +this file should not be ignored by a .dockerignore file \ No newline at end of file From 60269891bce2c32a51c8deee1356a5437fb45add Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 19 Jul 2019 05:59:46 +0000 Subject: [PATCH 0806/5615] Bump aws-java-sdk-sqs from 1.11.479 to 1.11.594 in /modules/localstack (#1626) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index e5e3efb5de3..3bcfcb91714 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.479' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.594' } From a0e479ad7b26f73025e4e7926908dd39b369c619 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 19 Jul 2019 07:38:30 +0000 Subject: [PATCH 0807/5615] Bump cucumber-java from 4.5.4 to 4.6.0 in /examples (#1620) --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index fb3a1ae875c..792968df7c4 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' - implementation 'io.cucumber:cucumber-java:4.5.4' + implementation 'io.cucumber:cucumber-java:4.6.0' testImplementation 'io.cucumber:cucumber-junit:4.5.4' testImplementation 'org.testcontainers:selenium' } From bf764260b8d74e59f9e19bd8d42df02916a4a048 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 19 Jul 2019 09:37:48 +0000 Subject: [PATCH 0808/5615] Bump cucumber-junit from 4.5.4 to 4.6.0 in /examples (#1619) --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 792968df7c4..9907da86262 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' implementation 'io.cucumber:cucumber-java:4.6.0' - testImplementation 'io.cucumber:cucumber-junit:4.5.4' + testImplementation 'io.cucumber:cucumber-junit:4.6.0' testImplementation 'org.testcontainers:selenium' } From 6dd9c44140ed19fd3f46dd3aace0fd6799c37b9a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 19 Jul 2019 15:21:59 +0200 Subject: [PATCH 0809/5615] Bump amqp-client from 5.7.1 to 5.7.2 in /core (#1588) Bumps [amqp-client](https://github.com/rabbitmq/rabbitmq-java-client) from 5.7.1 to 5.7.2. - [Release notes](https://github.com/rabbitmq/rabbitmq-java-client/releases) - [Commits](https://github.com/rabbitmq/rabbitmq-java-client/compare/v5.7.1...v5.7.2) Signed-off-by: dependabot-preview[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index ee876f595c1..77620f6e98e 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -102,7 +102,7 @@ dependencies { testCompile 'org.apache.httpcomponents:httpclient:4.5.9' testCompile 'redis.clients:jedis:3.0.1' - testCompile 'com.rabbitmq:amqp-client:5.7.1' + testCompile 'com.rabbitmq:amqp-client:5.7.2' testCompile 'org.mongodb:mongo-java-driver:3.10.2' testCompile ('org.mockito:mockito-core:2.28.2') { exclude(module: 'hamcrest-core') From 4373d1fb3540aea5be684daf32ab4b0eec7cb0cb Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 19 Jul 2019 15:22:33 +0200 Subject: [PATCH 0810/5615] Fail fast if `container_name` is set in Docker Compose file (#1581) * Fail fast if `container_name` is set in Docker Compose file * Update DockerComposeContainer.java --- core/build.gradle | 4 + .../containers/DockerComposeContainer.java | 86 +++++++++++++++---- .../DockerComposeFileValidationTest.java | 70 +++++++++++++++ .../docker-compose-container-name-v1.yml | 3 + 4 files changed, 147 insertions(+), 16 deletions(-) create mode 100644 core/src/test/java/org/testcontainers/containers/DockerComposeFileValidationTest.java create mode 100644 core/src/test/resources/docker-compose-container-name-v1.yml diff --git a/core/build.gradle b/core/build.gradle index 77620f6e98e..f400b5e3c1c 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -92,6 +92,8 @@ dependencies { exclude(group: 'org.apache.commons', module: 'commons-compress') } + shaded "org.yaml:snakeyaml:1.21" + shaded 'com.squareup.okhttp3:okhttp:3.14.2' shaded 'org.glassfish.main.external:trilead-ssh2-repackaged:4.1.2' @@ -110,6 +112,8 @@ dependencies { // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest testCompile files('testlib/repo/fakejar/fakejar/0/fakejar-0.jar') + testCompile 'org.assertj:assertj-core:3.12.2' + jarFileTestCompileOnly "org.projectlombok:lombok:${lombok.version}" jarFileTestCompile 'junit:junit:4.12' diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 46f4c561e5d..06a8bd9d597 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -2,17 +2,19 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.model.Container; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.base.Splitter; import com.google.common.collect.Maps; import com.google.common.util.concurrent.Uninterruptibles; import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.SystemUtils; import org.junit.runner.Description; import org.junit.runners.model.Statement; import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.output.Slf4jLogConsumer; @@ -22,11 +24,14 @@ import org.testcontainers.containers.wait.strategy.WaitStrategy; import org.testcontainers.lifecycle.Startable; import org.testcontainers.utility.*; +import org.yaml.snakeyaml.Yaml; import org.zeroturnaround.exec.InvalidExitValueException; import org.zeroturnaround.exec.ProcessExecutor; import org.zeroturnaround.exec.stream.slf4j.Slf4jStream; import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -49,6 +54,7 @@ /** * Container which launches Docker Compose, for the purposes of launching a defined set of containers. */ +@Slf4j public class DockerComposeContainer> extends FailureDetectingExternalResource implements Startable { /** @@ -147,7 +153,7 @@ public void start() { try { pullImages(); } catch (ContainerLaunchException e) { - logger().warn("Exception while pulling images, using local images if available", e); + log.warn("Exception while pulling images, using local images if available", e); } } applyScaling(); // scale before up, so that all scaled instances are available first for linking @@ -187,7 +193,7 @@ private void createServiceInstance(Container container) { String containerId = containerInstance.getContainerId(); if (tailChildContainers) { - followLogs(containerId, new Slf4jLogConsumer(logger()).withPrefix(container.getNames()[0])); + followLogs(containerId, new Slf4jLogConsumer(log).withPrefix(container.getNames()[0])); } //follow logs using registered consumers for this service logConsumers.getOrDefault(serviceName, Collections.emptyList()).forEach(consumer -> followLogs(containerId, consumer)); @@ -208,6 +214,13 @@ private String getServiceNameFromContainer(Container container) { } private void runWithCompose(String cmd) { + checkNotNull(composeFiles); + checkArgument(!composeFiles.isEmpty(), "No docker compose file have been provided"); + + for (File composeFile : composeFiles) { + validate(composeFile); + } + final DockerCompose dockerCompose; if (localCompose) { dockerCompose = new LocalDockerCompose(composeFiles, project); @@ -221,6 +234,60 @@ private void runWithCompose(String cmd) { .invoke(); } + @SuppressWarnings("unchecked") + private static void validate(File composeFile) { + Yaml yaml = new Yaml(); + try (FileInputStream fileInputStream = FileUtils.openInputStream(composeFile)) { + Object template = yaml.load(fileInputStream); + validate(template, composeFile.getAbsolutePath()); + } catch (IOException e) { + log.warn("Failed to read YAML from {}", composeFile.getAbsolutePath(), e); + } + } + + @VisibleForTesting + static void validate(Object template, String identifier) { + if (!(template instanceof Map)) { + return; + } + + Map map = (Map) template; + + final Map servicesMap; + if (map.containsKey("version")) { + if (!map.containsKey("services")) { + log.debug("Compose file {} has an unknown format: 'version' is set but 'services' is not defined", identifier); + return; + } + Object services = map.get("services"); + if (!(services instanceof Map)) { + log.debug("Compose file {} has an unknown format: 'services' is not Map", identifier); + return; + } + + servicesMap = (Map) services; + } else { + servicesMap = map; + } + + for (Map.Entry entry : servicesMap.entrySet()) { + String serviceName = entry.getKey(); + Object serviceDefinition = entry.getValue(); + if (!(serviceDefinition instanceof Map)) { + log.debug("Compose file {} has an unknown format: service '{}' is not Map", identifier, serviceName); + break; + } + + if (((Map) serviceDefinition).containsKey("container_name")) { + throw new IllegalStateException(String.format( + "Compose file %s has 'container_name' property set for service '%s' but this property is not supported by Testcontainers, consider removing it", + identifier, + serviceName + )); + } + } + } + private void applyScaling() { // Apply scaling if (!scalingPreferences.isEmpty()) { @@ -254,10 +321,6 @@ private void startAmbassadorContainers() { } } - private Logger logger() { - return LoggerFactory.getLogger(DockerComposeContainer.class); - } - @Override public void stop() { synchronized (MUTEX) { @@ -476,11 +539,6 @@ interface DockerCompose { DockerCompose withEnv(Map env); void invoke(); - - default void validateFileList(List composeFiles) { - checkNotNull(composeFiles); - checkArgument(!composeFiles.isEmpty(), "No docker compose file have been provided"); - } } /** @@ -497,8 +555,6 @@ class ContainerisedDockerCompose extends GenericContainer composeFiles, String identifier) { super(TestcontainersConfiguration.getInstance().getDockerComposeContainerImage()); - validateFileList(composeFiles); - addEnv(ENV_PROJECT_NAME, identifier); // Map the docker compose file into the container @@ -599,8 +655,6 @@ class LocalDockerCompose implements DockerCompose { private Map env = new HashMap<>(); public LocalDockerCompose(List composeFiles, String identifier) { - validateFileList(composeFiles); - this.composeFiles = composeFiles; this.identifier = identifier; } diff --git a/core/src/test/java/org/testcontainers/containers/DockerComposeFileValidationTest.java b/core/src/test/java/org/testcontainers/containers/DockerComposeFileValidationTest.java new file mode 100644 index 00000000000..7a3121dcd32 --- /dev/null +++ b/core/src/test/java/org/testcontainers/containers/DockerComposeFileValidationTest.java @@ -0,0 +1,70 @@ +package org.testcontainers.containers; + +import com.google.common.collect.ImmutableMap; +import org.assertj.core.api.Assertions; +import org.junit.Test; + +import java.io.File; + +import static java.util.Collections.emptyList; + +public class DockerComposeFileValidationTest { + + @Test + public void shouldValidate() { + File file = new File("src/test/resources/docker-compose-container-name-v1.yml"); + try (DockerComposeContainer container = new DockerComposeContainer<>(file)) { + Assertions.assertThatThrownBy(container::start) + .hasMessageContaining(file.getAbsolutePath()) + .hasMessageContaining("'container_name' property set for service 'redis'"); + } + } + + @Test + public void shouldRejectContainerNameV1() { + Assertions + .assertThatThrownBy(() -> { + DockerComposeContainer.validate(ImmutableMap.of( + "redis", ImmutableMap.of("container_name", "redis") + ), ""); + }) + .hasMessageContaining("'container_name' property set for service 'redis'"); + } + + @Test + public void shouldRejectContainerNameV2() { + Assertions + .assertThatThrownBy(() -> { + DockerComposeContainer.validate(ImmutableMap.of( + "version", "2", + "services", ImmutableMap.of( + "redis", ImmutableMap.of("container_name", "redis") + ) + ), ""); + }) + .hasMessageContaining("'container_name' property set for service 'redis'"); + } + + @Test + public void shouldIgnoreUnknownStructure() { + // Everything is a list + DockerComposeContainer.validate(emptyList(), ""); + + // services is not a map but List + DockerComposeContainer.validate(ImmutableMap.of( + "version", "2", + "services", emptyList() + ), ""); + + // services is not a collection + DockerComposeContainer.validate(ImmutableMap.of( + "version", "2", + "services", true + ), ""); + + // no services while version is defined + DockerComposeContainer.validate(ImmutableMap.of( + "version", "9000" + ), ""); + } +} diff --git a/core/src/test/resources/docker-compose-container-name-v1.yml b/core/src/test/resources/docker-compose-container-name-v1.yml new file mode 100644 index 00000000000..96af67248a0 --- /dev/null +++ b/core/src/test/resources/docker-compose-container-name-v1.yml @@ -0,0 +1,3 @@ +redis: + image: redis + container_name: redis From 151b93ac065a420d0a803d2ebb8ba6d152c783a4 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Sun, 21 Jul 2019 08:38:12 +0100 Subject: [PATCH 0811/5615] Allow tests to be disabled when Docker is unavailable (#1530) This commit introduces a new attribute, disabledWithoutDocker, on @Testcontainers that defaults to false. When set to true the class's tests are disabled when Docker in unavailable. --- modules/junit-jupiter/build.gradle | 3 + .../junit/jupiter/Testcontainers.java | 5 ++ .../jupiter/TestcontainersExtension.java | 41 ++++++++++- .../jupiter/TestcontainersExtensionTests.java | 72 +++++++++++++++++++ 4 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersExtensionTests.java diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index baab3b68bb8..08bdedc6620 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,6 +9,9 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.3.1' testCompile 'redis.clients:jedis:3.0.1' testCompile 'org.apache.httpcomponents:httpclient:4.5.9' + testCompile ('org.mockito:mockito-core:2.28.2') { + exclude(module: 'hamcrest-core') + } testRuntime 'org.postgresql:postgresql:42.2.6' testRuntime 'mysql:mysql-connector-java:8.0.16' diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Testcontainers.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Testcontainers.java index ecf59ff1399..6a364cd19e6 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Testcontainers.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Testcontainers.java @@ -53,4 +53,9 @@ @Retention(RetentionPolicy.RUNTIME) @ExtendWith(TestcontainersExtension.class) public @interface Testcontainers { + + /** + * Whether tests should be disabled (rather than failing) when Docker is not available. + */ + boolean disabledWithoutDocker() default false; } diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java index 7f6183cc74d..e6ce10f8541 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java @@ -5,8 +5,10 @@ import org.junit.jupiter.api.extension.ExtensionContext.Namespace; import org.junit.jupiter.api.extension.ExtensionContext.Store.CloseableResource; import org.junit.platform.commons.support.AnnotationSupport; +import org.junit.platform.commons.util.AnnotationUtils; import org.junit.platform.commons.util.Preconditions; import org.junit.platform.commons.util.ReflectionUtils; +import org.testcontainers.DockerClientFactory; import org.testcontainers.lifecycle.Startable; import java.lang.reflect.Field; @@ -16,7 +18,7 @@ import java.util.function.Predicate; import java.util.stream.Stream; -class TestcontainersExtension implements BeforeEachCallback, BeforeAllCallback, TestInstancePostProcessor { +class TestcontainersExtension implements BeforeEachCallback, BeforeAllCallback, ExecutionCondition, TestInstancePostProcessor { private static final Namespace NAMESPACE = Namespace.create(TestcontainersExtension.class); @@ -48,6 +50,43 @@ public void beforeEach(final ExtensionContext context) { .getOrComputeIfAbsent(adapter.getKey(), k -> adapter.start())); } + @Override + public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { + return findTestcontainers(context).map(this::evaluate) + .orElseThrow(() -> new ExtensionConfigurationException("@Testcontainers not found")); + } + + private Optional findTestcontainers(ExtensionContext context) { + Optional current = Optional.of(context); + while (current.isPresent()) { + Optional testcontainers = AnnotationUtils.findAnnotation(current.get().getRequiredTestClass(), Testcontainers.class); + if (testcontainers.isPresent()) { + return testcontainers; + } + current = current.get().getParent(); + } + return Optional.empty(); + } + + private ConditionEvaluationResult evaluate(Testcontainers testcontainers) { + if (testcontainers.disabledWithoutDocker()) { + if (isDockerAvailable()) { + return ConditionEvaluationResult.enabled("Docker is available"); + } + return ConditionEvaluationResult.disabled("disabledWithoutDocker is true and Docker is not available"); + } + return ConditionEvaluationResult.enabled("disabledWithoutDocker is false"); + } + + boolean isDockerAvailable() { + try { + DockerClientFactory.instance().client(); + return true; + } catch (Throwable ex) { + return false; + } + } + private Set collectParentTestInstances(final ExtensionContext context) { Set testInstances = new LinkedHashSet<>(); Optional current = Optional.of(context); diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersExtensionTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersExtensionTests.java new file mode 100644 index 00000000000..f3aea9bb850 --- /dev/null +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersExtensionTests.java @@ -0,0 +1,72 @@ +package org.testcontainers.junit.jupiter; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ConditionEvaluationResult; +import org.junit.jupiter.api.extension.ExtensionContext; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class TestcontainersExtensionTests { + + @Test + void whenDisabledWithoutDockerAndDockerIsAvailableTestsAreEnabled() { + ConditionEvaluationResult result = new TestTestcontainersExtension(true) + .evaluateExecutionCondition(extensionContext(DisabledWithoutDocker.class)); + assertFalse(result.isDisabled()); + } + + @Test + void whenDisabledWithoutDockerAndDockerIsUnavailableTestsAreDisabled() { + ConditionEvaluationResult result = new TestTestcontainersExtension(false) + .evaluateExecutionCondition(extensionContext(DisabledWithoutDocker.class)); + assertTrue(result.isDisabled()); + } + + @Test + void whenEnabledWithoutDockerAndDockerIsAvailableTestsAreEnabled() { + ConditionEvaluationResult result = new TestTestcontainersExtension(true) + .evaluateExecutionCondition(extensionContext(EnabledWithoutDocker.class)); + assertFalse(result.isDisabled()); + } + + @Test + void whenEnabledWithoutDockerAndDockerIsUnavailableTestsAreEnabled() { + ConditionEvaluationResult result = new TestTestcontainersExtension(false) + .evaluateExecutionCondition(extensionContext(EnabledWithoutDocker.class)); + assertFalse(result.isDisabled()); + } + + private ExtensionContext extensionContext(Class clazz) { + ExtensionContext extensionContext = mock(ExtensionContext.class); + when(extensionContext.getRequiredTestClass()).thenReturn(clazz); + return extensionContext; + } + + @Testcontainers(disabledWithoutDocker = true) + static final class DisabledWithoutDocker { + + } + + @Testcontainers + static final class EnabledWithoutDocker { + + } + + static final class TestTestcontainersExtension extends TestcontainersExtension { + + private final boolean dockerAvailable; + + private TestTestcontainersExtension(boolean dockerAvailable) { + this.dockerAvailable = dockerAvailable; + } + + boolean isDockerAvailable() { + return dockerAvailable; + } + + } + +} From f1565f1ba842ccfa9d8ed9ee00276f38b7a2546f Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 21 Jul 2019 10:01:38 +0100 Subject: [PATCH 0812/5615] Fail gracefully if no JDBC driver found (#1434) * WIP * Simplify code * Ensure that HikariCP uses the correct driver (Auto-selected driver on classpath seems to be MariaDB; seemingly not compatible with 8.0.16 of MySQL) * Cleanup old Hikari pools after use to avoid reconnection attempts to dead containers after test has already completed * Update following review comments --- .../containers/ContainerLaunchException.java | 4 +- core/src/test/resources/logback-test.xml | 2 - .../src/test/resources/logback-test.xml | 14 --- .../src/test/resources/logback-test.xml | 10 +-- .../src/test/resources/logback-test.xml | 14 ++- .../src/test/resources/logback-test.xml | 10 +-- .../src/test/resources/logback-test.xml | 10 +-- .../src/test/resources/logback-test.xml | 10 +-- .../spock/src/test/resources/logback-test.xml | 13 +-- .../src/test/resources/logback-test.xml | 7 +- .../src/test/resources/logback-test.xml | 3 +- .../src/test/resources/logback-test.xml | 13 +-- .../src/test/resources/logback-test.xml | 14 +-- .../src/test/resources/logback-test.xml | 5 +- .../src/test/resources/logback-test.xml | 12 +-- .../junit/AbstractContainerDatabaseTest.java | 16 +++- .../src/test/resources/logback-test.xml | 13 +-- .../containers/JdbcDatabaseContainer.java | 89 ++++++++++++------- .../jdbc/MissingJdbcDriverTest.java | 86 ++++++++++++++++++ .../jdbc/src/test/resources/logback-test.xml | 16 ++++ .../src/test/resources/logback-test.xml | 5 +- .../kafka/src/test/resources/logback-test.xml | 4 +- .../src/test/resources/logback-test.xml | 17 +--- .../neo4j/src/test/resources/logback-test.xml | 4 +- .../nginx/src/test/resources/logback-test.xml | 13 +-- .../src/test/resources/logback-test.xml | 13 +-- .../containers/PostgreSQLContainer.java | 3 +- .../src/test/resources/logback-test.xml | 4 +- .../src/test/resources/logback-test.xml | 13 +-- .../spock/src/test/resources/logback-test.xml | 5 +- .../vault/src/test/resources/logback-test.xml | 14 +-- 31 files changed, 229 insertions(+), 227 deletions(-) create mode 100644 modules/jdbc/src/test/java/org/testcontainers/jdbc/MissingJdbcDriverTest.java create mode 100644 modules/jdbc/src/test/resources/logback-test.xml diff --git a/core/src/main/java/org/testcontainers/containers/ContainerLaunchException.java b/core/src/main/java/org/testcontainers/containers/ContainerLaunchException.java index 9943d9f612b..bf407c16808 100644 --- a/core/src/main/java/org/testcontainers/containers/ContainerLaunchException.java +++ b/core/src/main/java/org/testcontainers/containers/ContainerLaunchException.java @@ -9,7 +9,7 @@ public ContainerLaunchException(String message) { super(message); } - public ContainerLaunchException(String message, Exception exception) { - super(message, exception); + public ContainerLaunchException(String message, Throwable cause) { + super(message, cause); } } diff --git a/core/src/test/resources/logback-test.xml b/core/src/test/resources/logback-test.xml index 57a53cdd3aa..5e96a70b244 100644 --- a/core/src/test/resources/logback-test.xml +++ b/core/src/test/resources/logback-test.xml @@ -14,10 +14,8 @@ - - diff --git a/docs/examples/src/test/resources/logback-test.xml b/docs/examples/src/test/resources/logback-test.xml index 49da0be9709..62bc2c43d3a 100644 --- a/docs/examples/src/test/resources/logback-test.xml +++ b/docs/examples/src/test/resources/logback-test.xml @@ -1,5 +1,4 @@ - @@ -11,17 +10,4 @@ - - - - - - - - - - - PROFILER - DENY - diff --git a/examples/disque-job-queue/src/test/resources/logback-test.xml b/examples/disque-job-queue/src/test/resources/logback-test.xml index c53f381fca5..be4ebaed2c8 100644 --- a/examples/disque-job-queue/src/test/resources/logback-test.xml +++ b/examples/disque-job-queue/src/test/resources/logback-test.xml @@ -4,15 +4,11 @@ - %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n - + - - - - - \ No newline at end of file + diff --git a/examples/linked-container/src/test/resources/logback-test.xml b/examples/linked-container/src/test/resources/logback-test.xml index 23f40bbec32..be4ebaed2c8 100644 --- a/examples/linked-container/src/test/resources/logback-test.xml +++ b/examples/linked-container/src/test/resources/logback-test.xml @@ -1,16 +1,14 @@ + + - %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n - + - - - - - - \ No newline at end of file + diff --git a/examples/redis-backed-cache-testng/src/test/resources/logback-test.xml b/examples/redis-backed-cache-testng/src/test/resources/logback-test.xml index c53f381fca5..be4ebaed2c8 100644 --- a/examples/redis-backed-cache-testng/src/test/resources/logback-test.xml +++ b/examples/redis-backed-cache-testng/src/test/resources/logback-test.xml @@ -4,15 +4,11 @@ - %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n - + - - - - - \ No newline at end of file + diff --git a/examples/redis-backed-cache/src/test/resources/logback-test.xml b/examples/redis-backed-cache/src/test/resources/logback-test.xml index c53f381fca5..be4ebaed2c8 100644 --- a/examples/redis-backed-cache/src/test/resources/logback-test.xml +++ b/examples/redis-backed-cache/src/test/resources/logback-test.xml @@ -4,15 +4,11 @@ - %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n - + - - - - - \ No newline at end of file + diff --git a/examples/selenium-container/src/test/resources/logback-test.xml b/examples/selenium-container/src/test/resources/logback-test.xml index c53f381fca5..be4ebaed2c8 100644 --- a/examples/selenium-container/src/test/resources/logback-test.xml +++ b/examples/selenium-container/src/test/resources/logback-test.xml @@ -4,15 +4,11 @@ - %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n - + - - - - - \ No newline at end of file + diff --git a/examples/spock/src/test/resources/logback-test.xml b/examples/spock/src/test/resources/logback-test.xml index 6e7cfd092de..535e406fc13 100644 --- a/examples/spock/src/test/resources/logback-test.xml +++ b/examples/spock/src/test/resources/logback-test.xml @@ -12,16 +12,5 @@ - - - - - - - - - - PROFILER - DENY - + diff --git a/examples/spring-boot/src/test/resources/logback-test.xml b/examples/spring-boot/src/test/resources/logback-test.xml index 86c4c861a78..5431cb6200b 100644 --- a/examples/spring-boot/src/test/resources/logback-test.xml +++ b/examples/spring-boot/src/test/resources/logback-test.xml @@ -1,8 +1,3 @@ - - - - - - \ No newline at end of file + diff --git a/modules/cassandra/src/test/resources/logback-test.xml b/modules/cassandra/src/test/resources/logback-test.xml index fc48b14c7de..535e406fc13 100644 --- a/modules/cassandra/src/test/resources/logback-test.xml +++ b/modules/cassandra/src/test/resources/logback-test.xml @@ -13,5 +13,4 @@ - - \ No newline at end of file + diff --git a/modules/couchbase/src/test/resources/logback-test.xml b/modules/couchbase/src/test/resources/logback-test.xml index d163f3ececb..535e406fc13 100644 --- a/modules/couchbase/src/test/resources/logback-test.xml +++ b/modules/couchbase/src/test/resources/logback-test.xml @@ -8,18 +8,9 @@ - + - - - - - - - - - - + diff --git a/modules/dynalite/src/test/resources/logback-test.xml b/modules/dynalite/src/test/resources/logback-test.xml index 5d27573a3cb..535e406fc13 100644 --- a/modules/dynalite/src/test/resources/logback-test.xml +++ b/modules/dynalite/src/test/resources/logback-test.xml @@ -8,17 +8,9 @@ - + - - - - - - - - - - \ No newline at end of file + + diff --git a/modules/elasticsearch/src/test/resources/logback-test.xml b/modules/elasticsearch/src/test/resources/logback-test.xml index 6d3eb183989..535e406fc13 100644 --- a/modules/elasticsearch/src/test/resources/logback-test.xml +++ b/modules/elasticsearch/src/test/resources/logback-test.xml @@ -8,10 +8,9 @@ - + - - + diff --git a/modules/influxdb/src/test/resources/logback-test.xml b/modules/influxdb/src/test/resources/logback-test.xml index e8f35ff1c79..535e406fc13 100644 --- a/modules/influxdb/src/test/resources/logback-test.xml +++ b/modules/influxdb/src/test/resources/logback-test.xml @@ -8,17 +8,9 @@ - + - - - - - - - - - + diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/AbstractContainerDatabaseTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/AbstractContainerDatabaseTest.java index a01146fe7a3..7230759382d 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/AbstractContainerDatabaseTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/AbstractContainerDatabaseTest.java @@ -2,15 +2,20 @@ import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; +import org.junit.After; import org.testcontainers.containers.JdbcDatabaseContainer; import javax.sql.DataSource; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.util.HashSet; +import java.util.Set; abstract class AbstractContainerDatabaseTest { + private final Set datasourcesForCleanup = new HashSet<>(); + ResultSet performQuery(JdbcDatabaseContainer container, String sql) throws SQLException { DataSource ds = getDataSource(container); Statement statement = ds.getConnection().createStatement(); @@ -26,7 +31,16 @@ DataSource getDataSource(JdbcDatabaseContainer container) { hikariConfig.setJdbcUrl(container.getJdbcUrl()); hikariConfig.setUsername(container.getUsername()); hikariConfig.setPassword(container.getPassword()); + hikariConfig.setDriverClassName(container.getDriverClassName()); + + final HikariDataSource dataSource = new HikariDataSource(hikariConfig); + datasourcesForCleanup.add(dataSource); + + return dataSource; + } - return new HikariDataSource(hikariConfig); + @After + public void teardown() { + datasourcesForCleanup.forEach(HikariDataSource::close); } } diff --git a/modules/jdbc-test/src/test/resources/logback-test.xml b/modules/jdbc-test/src/test/resources/logback-test.xml index b0f6b00e3e0..535e406fc13 100644 --- a/modules/jdbc-test/src/test/resources/logback-test.xml +++ b/modules/jdbc-test/src/test/resources/logback-test.xml @@ -8,18 +8,9 @@ - + - - - - - - - - - - + diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index e480070168f..36a6461aa74 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -1,11 +1,8 @@ package org.testcontainers.containers; -import lombok.NonNull; import com.github.dockerjava.api.command.InspectContainerResponse; +import lombok.NonNull; import org.jetbrains.annotations.NotNull; -import org.rnorth.ducttape.ratelimits.RateLimiter; -import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; -import org.rnorth.ducttape.unreliables.Unreliables; import org.testcontainers.containers.traits.LinkableContainer; import org.testcontainers.delegate.DatabaseDelegate; import org.testcontainers.ext.ScriptUtils; @@ -19,7 +16,6 @@ import java.util.Map; import java.util.Properties; import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; /** * Base class for containers that expose a JDBC connection @@ -33,11 +29,6 @@ public abstract class JdbcDatabaseContainer parameters = new HashMap<>(); - private static final RateLimiter DB_CONNECT_RATE_LIMIT = RateLimiterBuilder.newBuilder() - .withRate(10, TimeUnit.SECONDS) - .withConstantThroughput() - .build(); - private int startupTimeoutSeconds = 120; private int connectTimeoutSeconds = 120; @@ -123,26 +114,39 @@ public SELF withInitScript(String initScriptPath) { @Override protected void waitUntilContainerStarted() { - // Repeatedly try and open a connection to the DB and execute a test query - logger().info("Waiting for database connection to become available at {} using query '{}'", getJdbcUrl(), getTestQueryString()); - Unreliables.retryUntilSuccess(getStartupTimeoutSeconds(), TimeUnit.SECONDS, () -> { - if (!isRunning()) { - throw new ContainerLaunchException("Container failed to start"); - } - - try (Connection connection = createConnection("")) { - boolean success = connection.createStatement().execute(JdbcDatabaseContainer.this.getTestQueryString()); - - if (success) { - logger().info("Obtained a connection to container ({})", JdbcDatabaseContainer.this.getJdbcUrl()); - return null; - } else { - throw new SQLException("Failed to execute test query"); + // Repeatedly try and open a connection to the DB and execute a test query + long start = System.currentTimeMillis(); + try { + while (System.currentTimeMillis() < start + (1000 * startupTimeoutSeconds)) { + try { + if (!isRunning()) { + Thread.sleep(100L); + continue; // Don't attempt to connect yet + } + + try (Connection connection = createConnection("")) { + boolean testQuerySucceeded = connection.createStatement().execute(this.getTestQueryString()); + if (testQuerySucceeded) { + break; + } + } + } catch (NoDriverFoundException e) { + // we explicitly want this exception to fail fast without retries + throw e; + } catch (Exception e) { + // ignore so that we can try again + logger().debug("Failure when trying test query", e); + Thread.sleep(100L); } } - }); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new ContainerLaunchException("Container startup wait was interrupted", e); + } + + logger().info("Container is started (JDBC URL: {})", JdbcDatabaseContainer.this.getJdbcUrl()); } @Override @@ -155,14 +159,14 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { * * @return a JDBC Driver */ - public Driver getJdbcDriverInstance() { + public Driver getJdbcDriverInstance() throws NoDriverFoundException { synchronized (DRIVER_LOAD_MUTEX) { if (driver == null) { try { driver = (Driver) Class.forName(this.getDriverClassName()).newInstance(); } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { - throw new RuntimeException("Could not get Driver", e); + throw new NoDriverFoundException("Could not get Driver", e); } } } @@ -178,7 +182,7 @@ public Driver getJdbcDriverInstance() { * @return a Connection * @throws SQLException if there is a repeated failure to create the connection */ - public Connection createConnection(String queryString) throws SQLException { + public Connection createConnection(String queryString) throws SQLException, NoDriverFoundException { final Properties info = new Properties(); info.put("user", this.getUsername()); info.put("password", this.getPassword()); @@ -186,13 +190,24 @@ public Connection createConnection(String queryString) throws SQLException { final Driver jdbcDriverInstance = getJdbcDriverInstance(); + SQLException lastException = null; try { - return Unreliables.retryUntilSuccess(getConnectTimeoutSeconds(), TimeUnit.SECONDS, () -> - DB_CONNECT_RATE_LIMIT.getWhenReady(() -> - jdbcDriverInstance.connect(url, info))); - } catch (Exception e) { - throw new SQLException("Could not create new connection", e); + long start = System.currentTimeMillis(); + // give up if we hit the time limit or the container stops running for some reason + while (System.currentTimeMillis() < start + (1000 * connectTimeoutSeconds) && isRunning()) { + try { + logger().debug("Trying to create JDBC connection using {} to {} with properties: {}", driver.getClass().getName(), url, info); + + return jdbcDriverInstance.connect(url, info); + } catch (SQLException e) { + lastException = e; + Thread.sleep(100L); + } + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); } + throw new SQLException("Could not create new connection", lastException); } /** @@ -256,4 +271,10 @@ protected int getConnectTimeoutSeconds() { protected DatabaseDelegate getDatabaseDelegate() { return new JdbcDatabaseDelegate(this, ""); } + + public static class NoDriverFoundException extends RuntimeException { + public NoDriverFoundException(String message, Throwable e) { + super(message, e); + } + } } diff --git a/modules/jdbc/src/test/java/org/testcontainers/jdbc/MissingJdbcDriverTest.java b/modules/jdbc/src/test/java/org/testcontainers/jdbc/MissingJdbcDriverTest.java new file mode 100644 index 00000000000..38ae38095eb --- /dev/null +++ b/modules/jdbc/src/test/java/org/testcontainers/jdbc/MissingJdbcDriverTest.java @@ -0,0 +1,86 @@ +package org.testcontainers.jdbc; + +import com.google.common.base.Throwables; +import org.junit.Test; +import org.testcontainers.containers.JdbcDatabaseContainer; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.rnorth.visibleassertions.VisibleAssertions.fail; + +public class MissingJdbcDriverTest { + + @Test + public void shouldFailFastIfNoDriverFound() { + + final MissingDriverContainer container = new MissingDriverContainer(); + + try { + container.start(); + fail("The container is expected to fail to start"); + } catch (Exception e) { + final Throwable rootCause = Throwables.getRootCause(e); + assertTrue("ClassNotFoundException is the root cause", rootCause instanceof ClassNotFoundException); + } finally { + container.stop(); + } + + assertEquals("only one connection attempt should have been made", 1, container.getConnectionAttempts()); + } + + /** + * Container class for the purposes of testing, with a known non-existent driver + */ + static class MissingDriverContainer extends JdbcDatabaseContainer { + private final AtomicInteger connectionAttempts = new AtomicInteger(); + + MissingDriverContainer() { + super("mysql:5.7.22"); + withEnv("MYSQL_ROOT_PASSWORD", "test"); + withExposedPorts(3306); + } + + @Override + public String getDriverClassName() { + return "nonexistent.ClassName"; + } + + @Override + public String getJdbcUrl() { + return ""; + } + + @Override + public String getUsername() { + return "root"; + } + + @Override + public String getPassword() { + return "test"; + } + + @Override + protected String getTestQueryString() { + return ""; + } + + @Override + public Connection createConnection(String queryString) throws SQLException, NoDriverFoundException { + connectionAttempts.incrementAndGet(); // + return super.createConnection(queryString); + } + + /** + * test window + * @return how many times a connection was attempted + */ + int getConnectionAttempts() { + return connectionAttempts.get(); + } + } +} diff --git a/modules/jdbc/src/test/resources/logback-test.xml b/modules/jdbc/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..535e406fc13 --- /dev/null +++ b/modules/jdbc/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + diff --git a/modules/junit-jupiter/src/test/resources/logback-test.xml b/modules/junit-jupiter/src/test/resources/logback-test.xml index 1d1a7695915..535e406fc13 100644 --- a/modules/junit-jupiter/src/test/resources/logback-test.xml +++ b/modules/junit-jupiter/src/test/resources/logback-test.xml @@ -8,8 +8,9 @@ - + - \ No newline at end of file + + diff --git a/modules/kafka/src/test/resources/logback-test.xml b/modules/kafka/src/test/resources/logback-test.xml index 7bd6a94d827..535e406fc13 100644 --- a/modules/kafka/src/test/resources/logback-test.xml +++ b/modules/kafka/src/test/resources/logback-test.xml @@ -13,6 +13,4 @@ - - - \ No newline at end of file + diff --git a/modules/localstack/src/test/resources/logback-test.xml b/modules/localstack/src/test/resources/logback-test.xml index cc69fdf0ce0..535e406fc13 100644 --- a/modules/localstack/src/test/resources/logback-test.xml +++ b/modules/localstack/src/test/resources/logback-test.xml @@ -8,20 +8,9 @@ - + - - - - - - - - - - - - - \ No newline at end of file + + diff --git a/modules/neo4j/src/test/resources/logback-test.xml b/modules/neo4j/src/test/resources/logback-test.xml index 7bd6a94d827..535e406fc13 100644 --- a/modules/neo4j/src/test/resources/logback-test.xml +++ b/modules/neo4j/src/test/resources/logback-test.xml @@ -13,6 +13,4 @@ - - - \ No newline at end of file + diff --git a/modules/nginx/src/test/resources/logback-test.xml b/modules/nginx/src/test/resources/logback-test.xml index b0f6b00e3e0..535e406fc13 100644 --- a/modules/nginx/src/test/resources/logback-test.xml +++ b/modules/nginx/src/test/resources/logback-test.xml @@ -8,18 +8,9 @@ - + - - - - - - - - - - + diff --git a/modules/oracle-xe/src/test/resources/logback-test.xml b/modules/oracle-xe/src/test/resources/logback-test.xml index b0f6b00e3e0..535e406fc13 100644 --- a/modules/oracle-xe/src/test/resources/logback-test.xml +++ b/modules/oracle-xe/src/test/resources/logback-test.xml @@ -8,18 +8,9 @@ - + - - - - - - - - - - + diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index eb7b78b1864..a9429210261 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -59,7 +59,8 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return "jdbc:postgresql://" + getContainerIpAddress() + ":" + getMappedPort(POSTGRESQL_PORT) + "/" + databaseName; + // Disable Postgres driver use of java.util.logging to reduce noise at startup time + return "jdbc:postgresql://" + getContainerIpAddress() + ":" + getMappedPort(POSTGRESQL_PORT) + "/" + databaseName + "?loggerLevel=OFF"; } @Override diff --git a/modules/pulsar/src/test/resources/logback-test.xml b/modules/pulsar/src/test/resources/logback-test.xml index 7bd6a94d827..535e406fc13 100644 --- a/modules/pulsar/src/test/resources/logback-test.xml +++ b/modules/pulsar/src/test/resources/logback-test.xml @@ -13,6 +13,4 @@ - - - \ No newline at end of file + diff --git a/modules/selenium/src/test/resources/logback-test.xml b/modules/selenium/src/test/resources/logback-test.xml index b0f6b00e3e0..535e406fc13 100644 --- a/modules/selenium/src/test/resources/logback-test.xml +++ b/modules/selenium/src/test/resources/logback-test.xml @@ -8,18 +8,9 @@ - + - - - - - - - - - - + diff --git a/modules/spock/src/test/resources/logback-test.xml b/modules/spock/src/test/resources/logback-test.xml index 1d1a7695915..535e406fc13 100644 --- a/modules/spock/src/test/resources/logback-test.xml +++ b/modules/spock/src/test/resources/logback-test.xml @@ -8,8 +8,9 @@ - + - \ No newline at end of file + + diff --git a/modules/vault/src/test/resources/logback-test.xml b/modules/vault/src/test/resources/logback-test.xml index 5d27573a3cb..535e406fc13 100644 --- a/modules/vault/src/test/resources/logback-test.xml +++ b/modules/vault/src/test/resources/logback-test.xml @@ -8,17 +8,9 @@ - + - - - - - - - - - - \ No newline at end of file + + From 67501e83ffb759256d4f1752683e77e17d1fbf70 Mon Sep 17 00:00:00 2001 From: Jameel Al-Aziz <247849+jalaziz@users.noreply.github.com> Date: Sun, 21 Jul 2019 02:42:35 -0700 Subject: [PATCH 0813/5615] Add container stopping and stopped hooks (#1610) Add pre-stop and post-stop hooks to the GenericContainer. This provides symmetry to the equavilent starting/started hooks and allows custom behaviour to be defined in sub-classes. --- .../containers/GenericContainer.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 8045d47c720..7e37839979b 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -333,7 +333,9 @@ public void stop() { imageName = ""; } + containerIsStopping(containerInfo); ResourceReaper.instance().stopAndRemoveContainer(containerId, imageName); + containerIsStopped(containerInfo); } finally { containerId = null; containerInfo = null; @@ -382,6 +384,24 @@ protected void containerIsStarting(InspectContainerResponse containerInfo) { protected void containerIsStarted(InspectContainerResponse containerInfo) { } + /** + * A hook that is executed before the container is stopped with {@link #stop()}. + * Warning! This hook won't be executed if the container is terminated during + * the JVM's shutdown hook or by Ryuk. + */ + @SuppressWarnings({"EmptyMethod", "UnusedParameters"}) + protected void containerIsStopping(InspectContainerResponse containerInfo) { + } + + /** + * A hook that is executed after the container is stopped with {@link #stop()}. + * Warning! This hook won't be executed if the container is terminated during + * the JVM's shutdown hook or by Ryuk. + */ + @SuppressWarnings({"EmptyMethod", "UnusedParameters"}) + protected void containerIsStopped(InspectContainerResponse containerInfo) { + } + /** * @return the port on which to check if the container is ready * @deprecated see {@link GenericContainer#getLivenessCheckPorts()} for replacement From 908a4ecebddf7cc2b053f64f7bf0550192c041a4 Mon Sep 17 00:00:00 2001 From: kellen Date: Mon, 22 Jul 2019 05:06:53 -0400 Subject: [PATCH 0814/5615] Enable command override for PostgreSQLContainer (#938) --- .../junit/SimplePostgreSQLTest.java | 30 +++++++++++++++---- .../containers/PostgreSQLContainer.java | 3 +- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimplePostgreSQLTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimplePostgreSQLTest.java index c6685343c29..4624268cb2b 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimplePostgreSQLTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimplePostgreSQLTest.java @@ -7,10 +7,8 @@ import java.sql.SQLException; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; -/** - * @author richardnorth - */ public class SimplePostgreSQLTest extends AbstractContainerDatabaseTest { @Test @@ -19,16 +17,36 @@ public void testSimple() throws SQLException { postgres.start(); ResultSet resultSet = performQuery(postgres, "SELECT 1"); - int resultSetInt = resultSet.getInt(1); assertEquals("A basic SELECT query succeeds", 1, resultSetInt); } } + @Test + public void testCommandOverride() throws SQLException { + try (PostgreSQLContainer postgres = new PostgreSQLContainer<>().withCommand("postgres -c max_connections=42")) { + postgres.start(); + + ResultSet resultSet = performQuery(postgres, "SELECT current_setting('max_connections')"); + String result = resultSet.getString(1); + assertEquals("max_connections should be overriden", "42", result); + } + } + + @Test + public void testUnsetCommand() throws SQLException { + try (PostgreSQLContainer postgres = new PostgreSQLContainer<>().withCommand("postgres -c max_connections=42").withCommand()) { + postgres.start(); + + ResultSet resultSet = performQuery(postgres, "SELECT current_setting('max_connections')"); + String result = resultSet.getString(1); + assertNotEquals("max_connections should not be overriden", "42", result); + } + } + @Test public void testExplicitInitScript() throws SQLException { - try (PostgreSQLContainer postgres = new PostgreSQLContainer<>() - .withInitScript("somepath/init_postgresql.sql")) { + try (PostgreSQLContainer postgres = new PostgreSQLContainer<>().withInitScript("somepath/init_postgresql.sql")) { postgres.start(); ResultSet resultSet = performQuery(postgres, "SELECT foo FROM bar"); diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index a9429210261..85ad910aa17 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -34,6 +34,7 @@ public PostgreSQLContainer(final String dockerImageName) { .withRegEx(".*database system is ready to accept connections.*\\s") .withTimes(2) .withStartupTimeout(Duration.of(60, SECONDS)); + this.setCommand("postgres", "-c", FSYNC_OFF_OPTION); } @NotNull @@ -44,12 +45,10 @@ protected Set getLivenessCheckPorts() { @Override protected void configure() { - addExposedPort(POSTGRESQL_PORT); addEnv("POSTGRES_DB", databaseName); addEnv("POSTGRES_USER", username); addEnv("POSTGRES_PASSWORD", password); - setCommand("postgres", "-c", FSYNC_OFF_OPTION); } @Override From 4df20c2d1d090d8f2d3129ec24b0988faecbe896 Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 22 Jul 2019 17:47:59 +0100 Subject: [PATCH 0815/5615] Improve image pull handling, with better logging and timeout behaviour (#1320) --- .../testcontainers/DockerClientFactory.java | 15 ++- .../containers/DockerComposeContainer.java | 8 +- .../containers/GenericContainer.java | 46 ++++++- .../images/LoggedPullImageResultCallback.java | 121 ++++++++++++++++++ .../images/RemoteDockerImage.java | 98 +++++++------- ...eLimitedLoggedPullImageResultCallback.java | 117 +++++++++++++++++ .../images/builder/ImageFromDockerfile.java | 23 ++-- .../utility/RegistryAuthLocator.java | 8 +- .../utility/TestcontainersConfiguration.java | 4 + .../dockerclient/ImagePullTest.java | 1 + docs/features/configuration.md | 4 + 11 files changed, 362 insertions(+), 83 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/images/LoggedPullImageResultCallback.java create mode 100644 core/src/main/java/org/testcontainers/images/TimeLimitedLoggedPullImageResultCallback.java diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 72cff0217cd..928b4cad291 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -4,12 +4,17 @@ import com.github.dockerjava.api.command.CreateContainerCmd; import com.github.dockerjava.api.exception.InternalServerErrorException; import com.github.dockerjava.api.exception.NotFoundException; -import com.github.dockerjava.api.model.*; +import com.github.dockerjava.api.model.AccessMode; +import com.github.dockerjava.api.model.Bind; +import com.github.dockerjava.api.model.Image; +import com.github.dockerjava.api.model.Info; +import com.github.dockerjava.api.model.Version; +import com.github.dockerjava.api.model.Volume; import com.github.dockerjava.core.command.ExecStartResultCallback; -import com.github.dockerjava.core.command.PullImageResultCallback; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableMap; import lombok.Getter; +import lombok.SneakyThrows; import lombok.Synchronized; import lombok.extern.slf4j.Slf4j; import org.hamcrest.BaseMatcher; @@ -17,6 +22,7 @@ import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.dockerclient.DockerClientProviderStrategy; import org.testcontainers.dockerclient.DockerMachineClientProviderStrategy; +import org.testcontainers.images.TimeLimitedLoggedPullImageResultCallback; import org.testcontainers.utility.ComparableVersion; import org.testcontainers.utility.MountableFile; import org.testcontainers.utility.ResourceReaper; @@ -125,7 +131,7 @@ public DockerClient client() { ryukContainerId = ResourceReaper.start(hostIpAddress, client); log.info("Ryuk started - will monitor and terminate Testcontainers containers on JVM exit"); } - + boolean checksEnabled = !TestcontainersConfiguration.getInstance().isDisableChecks(); if (checksEnabled) { VisibleAssertions.info("Checking the system..."); @@ -215,10 +221,11 @@ private boolean checkMountableFile() { /** * Check whether the image is available locally and pull it otherwise */ + @SneakyThrows public void checkAndPullImage(DockerClient client, String image) { List images = client.listImagesCmd().withImageNameFilter(image).exec(); if (images.isEmpty()) { - client.pullImageCmd(image).exec(new PullImageResultCallback()).awaitSuccess(); + client.pullImageCmd(image).exec(new TimeLimitedLoggedPullImageResultCallback(log)).awaitCompletion(); } } diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 06a8bd9d597..b21e160d892 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -19,9 +19,7 @@ import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.output.Slf4jLogConsumer; import org.testcontainers.containers.startupcheck.IndefiniteWaitOneShotStartupCheckStrategy; -import org.testcontainers.containers.wait.strategy.Wait; -import org.testcontainers.containers.wait.strategy.WaitAllStrategy; -import org.testcontainers.containers.wait.strategy.WaitStrategy; +import org.testcontainers.containers.wait.strategy.*; import org.testcontainers.lifecycle.Startable; import org.testcontainers.utility.*; import org.yaml.snakeyaml.Yaml; @@ -32,9 +30,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; +import java.nio.file.*; import java.time.Duration; import java.util.AbstractMap.SimpleEntry; import java.util.*; diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 7e37839979b..c2701c2f87e 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -3,9 +3,22 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.CreateContainerCmd; import com.github.dockerjava.api.command.InspectContainerResponse; -import com.github.dockerjava.api.model.*; +import com.github.dockerjava.api.model.Bind; +import com.github.dockerjava.api.model.ContainerNetwork; +import com.github.dockerjava.api.model.ExposedPort; +import com.github.dockerjava.api.model.HostConfig; +import com.github.dockerjava.api.model.Info; +import com.github.dockerjava.api.model.Link; +import com.github.dockerjava.api.model.PortBinding; +import com.github.dockerjava.api.model.Volume; +import com.github.dockerjava.api.model.VolumesFrom; import com.google.common.base.Strings; -import lombok.*; +import lombok.AccessLevel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NonNull; +import lombok.Setter; +import lombok.SneakyThrows; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.apache.commons.compress.utils.IOUtils; @@ -33,13 +46,34 @@ import org.testcontainers.lifecycle.Startable; import org.testcontainers.lifecycle.TestDescription; import org.testcontainers.lifecycle.TestLifecycleAware; -import org.testcontainers.utility.*; - -import java.io.*; +import org.testcontainers.utility.Base58; +import org.testcontainers.utility.DockerLoggerFactory; +import org.testcontainers.utility.DockerMachineClient; +import org.testcontainers.utility.MountableFile; +import org.testcontainers.utility.PathUtils; +import org.testcontainers.utility.ResourceReaper; +import org.testcontainers.utility.TestcontainersConfiguration; +import org.testcontainers.utility.ThrowingFunction; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; import java.nio.charset.Charset; import java.nio.file.Path; import java.time.Duration; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; diff --git a/core/src/main/java/org/testcontainers/images/LoggedPullImageResultCallback.java b/core/src/main/java/org/testcontainers/images/LoggedPullImageResultCallback.java new file mode 100644 index 00000000000..71b797298aa --- /dev/null +++ b/core/src/main/java/org/testcontainers/images/LoggedPullImageResultCallback.java @@ -0,0 +1,121 @@ +package org.testcontainers.images; + +import com.github.dockerjava.api.model.PullResponseItem; +import com.github.dockerjava.core.command.PullImageResultCallback; +import org.slf4j.Logger; + +import java.io.Closeable; +import java.time.Duration; +import java.time.Instant; +import java.util.*; + +import static java.lang.String.format; +import static org.apache.commons.io.FileUtils.byteCountToDisplaySize; + +/** + * {@link PullImageResultCallback} with improved logging of pull progress. + */ +class LoggedPullImageResultCallback extends PullImageResultCallback { + private final Logger logger; + + private final Set allLayers = new HashSet<>(); + private final Set downloadedLayers = new HashSet<>(); + private final Set pulledLayers = new HashSet<>(); + private final Map totalSizes = new HashMap<>(); + private final Map currentSizes = new HashMap<>(); + private boolean completed; + private Instant start; + + LoggedPullImageResultCallback(final Logger logger) { + this.logger = logger; + } + + @Override + public void onStart(final Closeable stream) { + super.onStart(stream); + start = Instant.now(); + + logger.info("Starting to pull image"); + } + + @Override + public void onNext(final PullResponseItem item) { + super.onNext(item); + + final String statusLowercase = item.getStatus() != null ? item.getStatus().toLowerCase() : ""; + final String id = item.getId(); + + if (item.getProgressDetail() != null) { + allLayers.add(id); + } + + if (statusLowercase.equalsIgnoreCase("download complete")) { + downloadedLayers.add(id); + } + + if (statusLowercase.equalsIgnoreCase("pull complete")) { + pulledLayers.add(id); + } + + if (item.getProgressDetail() != null) { + Long total = item.getProgressDetail().getTotal(); + Long current = item.getProgressDetail().getCurrent(); + + if (total != null && total > totalSizes.getOrDefault(id, 0L)) { + totalSizes.put(id, total); + } + if (current != null && current > currentSizes.getOrDefault(id, 0L)) { + currentSizes.put(id, current); + } + } + + if (statusLowercase.startsWith("pulling from" ) || statusLowercase.contains("complete" )) { + + long totalSize = totalLayerSize(); + long currentSize = downloadedLayerSize(); + + int pendingCount = allLayers.size() - downloadedLayers.size(); + String friendlyTotalSize; + if (pendingCount > 0) { + friendlyTotalSize = "? MB"; + } else { + friendlyTotalSize = byteCountToDisplaySize(totalSize); + } + + logger.info("Pulling image layers: {} pending, {} downloaded, {} extracted, ({}/{})", + format("%2d", pendingCount), + format("%2d", downloadedLayers.size()), + format("%2d", pulledLayers.size()), + byteCountToDisplaySize(currentSize), + friendlyTotalSize); + } + + if (statusLowercase.contains("complete")) { + completed = true; + } + } + + @Override + public void onComplete() { + super.onComplete(); + + final long downloadedLayerSize = downloadedLayerSize(); + final long duration = Duration.between(start, Instant.now()).getSeconds(); + + if (completed) { + logger.info("Pull complete. {} layers, pulled in {}s (downloaded {} at {}/s)", + allLayers.size(), + duration, + byteCountToDisplaySize(downloadedLayerSize), + byteCountToDisplaySize(downloadedLayerSize / duration)); + } + } + + private long downloadedLayerSize() { + return currentSizes.values().stream().filter(Objects::nonNull).mapToLong(it -> it).sum(); + } + + private long totalLayerSize() { + return totalSizes.values().stream().filter(Objects::nonNull).mapToLong(it -> it).sum(); + } +} diff --git a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java index d0bc47d6b12..7aff7918f6d 100644 --- a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java +++ b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java @@ -24,6 +24,10 @@ @ToString public class RemoteDockerImage extends LazyFuture { + /** + * @deprecated this field will become private in a later release + */ + @Deprecated public static final Set AVAILABLE_IMAGE_NAME_CACHE = new HashSet<>(); private DockerImageName imageName; @@ -42,59 +46,47 @@ protected final String resolve() { DockerClient dockerClient = DockerClientFactory.instance().client(); try { - int attempts = 0; - Exception lastException = null; - while (true) { - // Does our cache already know the image? - if (AVAILABLE_IMAGE_NAME_CACHE.contains(imageName)) { - logger.trace("{} is already in image name cache", imageName); - break; - } - - // Update the cache - ListImagesCmd listImagesCmd = dockerClient.listImagesCmd(); - - if (Boolean.parseBoolean(System.getProperty("useFilter"))) { - listImagesCmd = listImagesCmd.withImageNameFilter(imageName.toString()); - } - - List updatedImages = listImagesCmd.exec(); - updatedImages.stream() - .map(Image::getRepoTags) - .filter(Objects::nonNull) - .flatMap(Stream::of) - .map(DockerImageName::new) - .collect(Collectors.toCollection(() -> AVAILABLE_IMAGE_NAME_CACHE)); - - // And now? - if (AVAILABLE_IMAGE_NAME_CACHE.contains(imageName)) { - logger.trace("{} is in image name cache following listing of images", imageName); - break; - } - - // Log only on first attempt - if (attempts == 0) { - logger.info("Pulling docker image: {}. Please be patient; this may take some time but only needs to be done once.", imageName); - } - - if (attempts++ >= 3) { - logger.error("Retry limit reached while trying to pull image: {}. Please check output of `docker pull {}`", imageName, imageName); - throw new ContainerFetchException("Retry limit reached while trying to pull image: " + imageName, lastException); - } - - // The image is not available locally - pull it - try { - final PullImageResultCallback callback = new PullImageResultCallback(); - dockerClient - .pullImageCmd(imageName.getUnversionedPart()) - .withTag(imageName.getVersionPart()) - .exec(callback); - callback.awaitCompletion(); - AVAILABLE_IMAGE_NAME_CACHE.add(imageName); - break; - } catch (Exception e) { - lastException = e; - } + // Does our cache already know the image? + if (AVAILABLE_IMAGE_NAME_CACHE.contains(imageName)) { + logger.trace("{} is already in image name cache", imageName); + return imageName.toString(); + } + + // Update the cache + ListImagesCmd listImagesCmd = dockerClient.listImagesCmd(); + + if (Boolean.parseBoolean(System.getProperty("useFilter"))) { + listImagesCmd = listImagesCmd.withImageNameFilter(imageName.toString()); + } + + List updatedImages = listImagesCmd.exec(); + updatedImages.stream() + .map(Image::getRepoTags) + .filter(Objects::nonNull) + .flatMap(Stream::of) + .map(DockerImageName::new) + .collect(Collectors.toCollection(() -> AVAILABLE_IMAGE_NAME_CACHE)); + + // And now? + if (AVAILABLE_IMAGE_NAME_CACHE.contains(imageName)) { + logger.trace("{} is in image name cache following listing of images", imageName); + return imageName.toString(); + } + + logger.info("Pulling docker image: {}. Please be patient; this may take some time but only needs to be done once.", imageName); + + // The image is not available locally - pull it + try { + final PullImageResultCallback callback = new TimeLimitedLoggedPullImageResultCallback(logger); + dockerClient + .pullImageCmd(imageName.getUnversionedPart()) + .withTag(imageName.getVersionPart()) + .exec(callback); + callback.awaitCompletion(); + AVAILABLE_IMAGE_NAME_CACHE.add(imageName); + } catch (Exception e) { + logger.error("Failed to pull image: {}. Please check output of `docker pull {}`", imageName, imageName); + throw new ContainerFetchException("Failed to pull image: " + imageName, e); } return imageName.toString(); diff --git a/core/src/main/java/org/testcontainers/images/TimeLimitedLoggedPullImageResultCallback.java b/core/src/main/java/org/testcontainers/images/TimeLimitedLoggedPullImageResultCallback.java new file mode 100644 index 00000000000..20b7de6c666 --- /dev/null +++ b/core/src/main/java/org/testcontainers/images/TimeLimitedLoggedPullImageResultCallback.java @@ -0,0 +1,117 @@ +package org.testcontainers.images; + +import com.github.dockerjava.api.model.PullResponseItem; +import com.github.dockerjava.core.command.PullImageResultCallback; +import org.slf4j.Logger; +import org.testcontainers.utility.TestcontainersConfiguration; + +import java.io.Closeable; +import java.io.IOException; +import java.time.Duration; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.testcontainers.DockerClientFactory.TESTCONTAINERS_THREAD_GROUP; + +/** + * {@link PullImageResultCallback} with improved logging of pull progress and a 'watchdog' which will abort the pull + * if progress is not being made, to prevent a hanging test + */ +public class TimeLimitedLoggedPullImageResultCallback extends LoggedPullImageResultCallback { + + private static final AtomicInteger THREAD_ID = new AtomicInteger(0); + private static final ScheduledExecutorService PROGRESS_WATCHDOG_EXECUTOR = + Executors.newScheduledThreadPool(0, runnable -> { + Thread t = new Thread(TESTCONTAINERS_THREAD_GROUP, runnable); + t.setDaemon(true); + t.setName("testcontainers-pull-watchdog-" + THREAD_ID.incrementAndGet()); + return t; + }); + private static final Duration PULL_PAUSE_TOLERANCE = Duration.ofSeconds(TestcontainersConfiguration.getInstance().getImagePullPauseTimeout()); + private final Logger logger; + + // A future which, if it ever fires, will kill the pull + private ScheduledFuture nextCheckForProgress; + + // All threads that are 'awaiting' this pull + private final Set waitingThreads = new HashSet<>(); + + public TimeLimitedLoggedPullImageResultCallback(Logger logger) { + super(logger); + this.logger = logger; + } + + @Override + public PullImageResultCallback awaitCompletion() throws InterruptedException { + waitingThreads.add(Thread.currentThread()); + return super.awaitCompletion(); + } + + @Override + public boolean awaitCompletion(long timeout, TimeUnit timeUnit) throws InterruptedException { + waitingThreads.add(Thread.currentThread()); + return super.awaitCompletion(timeout, timeUnit); + } + + @Override + public void onNext(PullResponseItem item) { + if (item.getProgressDetail() != null) { + resetProgressWatchdog(false); + } + super.onNext(item); + } + + @Override + public void onStart(Closeable stream) { + resetProgressWatchdog(false); + super.onStart(stream); + } + + @Override + public void onError(Throwable throwable) { + resetProgressWatchdog(true); + super.onError(throwable); + } + + @Override + public void onComplete() { + resetProgressWatchdog(true); + super.onComplete(); + } + + + /* + * This method schedules a future task which will interrupt the waiting waiting threads if ever fired. + * Every time this method is called (from onStart or onNext), the task is cancelled and recreated 30s in the future, + * ensuring that it will only fire if the method stops being called regularly (e.g. if the pull has hung). + */ + private void resetProgressWatchdog(boolean isFinished) { + if (nextCheckForProgress != null && ! nextCheckForProgress.isCancelled()) { + nextCheckForProgress.cancel(false); + } + if (!isFinished) { + nextCheckForProgress = PROGRESS_WATCHDOG_EXECUTOR.schedule( + this::abortPull, + PULL_PAUSE_TOLERANCE.getSeconds(), + TimeUnit.SECONDS + ); + } + } + + private void abortPull() { + logger.error("Docker image pull has not made progress in {}s - aborting pull", PULL_PAUSE_TOLERANCE.getSeconds()); + // Interrupt any threads that are waiting, before closing streams, because the stream can take + // an indeterminate amount of time to close + waitingThreads.forEach(Thread::interrupt); + try { + close(); + } catch (IOException ignored) { + // no action + } + } +} diff --git a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java index f4968c11ff0..8c653718a69 100644 --- a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java +++ b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java @@ -14,12 +14,15 @@ import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.testcontainers.DockerClientFactory; -import org.testcontainers.images.builder.traits.*; +import org.testcontainers.images.builder.traits.BuildContextBuilderTrait; +import org.testcontainers.images.builder.traits.ClasspathTrait; +import org.testcontainers.images.builder.traits.DockerfileTrait; +import org.testcontainers.images.builder.traits.FilesTrait; +import org.testcontainers.images.builder.traits.StringsTrait; import org.testcontainers.utility.Base58; import org.testcontainers.utility.DockerLoggerFactory; import org.testcontainers.utility.LazyFuture; -import java.io.File; import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; @@ -123,7 +126,7 @@ public void onNext(BuildResponseItem item) { configure(buildImageCmd); BuildImageResultCallback exec = buildImageCmd.exec(resultCallback); - + long bytesToDockerDaemon = 0; // To build an image, we have to send the context to Docker in TAR archive format @@ -138,10 +141,10 @@ public void onNext(BuildResponseItem item) { } tarArchive.finish(); } - + log.info("Transferred {} KB to Docker daemon", FileUtils.byteCountToDisplaySize(bytesToDockerDaemon)); if (bytesToDockerDaemon > FileUtils.ONE_MB * 50) // warn if >50MB sent to docker daemon - log.warn("A large amount of data was sent to the Docker daemon ({}). Consider using a .dockerignore file for better performance.", + log.warn("A large amount of data was sent to the Docker daemon ({}). Consider using a .dockerignore file for better performance.", FileUtils.byteCountToDisplaySize(bytesToDockerDaemon)); exec.awaitImageId(); @@ -170,8 +173,8 @@ public ImageFromDockerfile withBuildArgs(final Map args) { } /** - * Sets the Dockerfile to be used for this image. - * @deprecated It is recommended to use {@link #withDockerfile} instead because it honors + * Sets the Dockerfile to be used for this image. + * @deprecated It is recommended to use {@link #withDockerfile} instead because it honors * .dockerignore files and therefore will be more efficient * @param relativePathFromBuildRoot */ @@ -180,14 +183,14 @@ public ImageFromDockerfile withDockerfilePath(String relativePathFromBuildRoot) this.dockerFilePath = Optional.of(relativePathFromBuildRoot); return this; } - + /** - * Sets the Dockerfile to be used for this image. + * Sets the Dockerfile to be used for this image. * @param dockerfile */ public ImageFromDockerfile withDockerfile(Path dockerfile) { this.dockerfile = Optional.of(dockerfile); return this; } - + } diff --git a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java index a1be23eb82e..5b248aea04e 100644 --- a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java +++ b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java @@ -209,7 +209,7 @@ private Map.Entry findAuthNode(final JsonNode config, final St private AuthConfig runCredentialProvider(String hostName, String helperOrStoreName) throws Exception { if (isBlank(hostName)) { - log.debug("There is no point to locate AuthConfig for blank hostName. Return NULL to allow fallback"); + log.debug("There is no point in locating AuthConfig for blank hostName. Returning NULL to allow fallback"); return null; } @@ -228,9 +228,9 @@ private AuthConfig runCredentialProvider(String hostName, String helperOrStoreNa if (!isBlank(responseErrorMsg)) { String credentialsNotFoundMsg = getGenericCredentialsNotFoundMsg(credentialProgramName); if (credentialsNotFoundMsg != null && credentialsNotFoundMsg.equals(responseErrorMsg)) { - log.info("Credentials not found for host ({}) when using credential helper/store ({})", - hostName, - credentialProgramName); + log.info("Credential helper/store ({}) does not have credentials for {}", + credentialProgramName, + hostName); return null; } diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index f87b02a17d3..608d02716d0 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -82,6 +82,10 @@ public String getTransportType() { return properties.getProperty("transport.type", "okhttp"); } + public Integer getImagePullPauseTimeout() { + return Integer.parseInt((String) properties.getOrDefault("pull.pause.timeout", "30")); + } + @Synchronized public boolean updateGlobalConfig(@NonNull String prop, @NonNull String value) { try { diff --git a/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java b/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java index 47ad456dc26..6e319838888 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java @@ -24,6 +24,7 @@ public static String[] parameters() { "quay.io/testcontainers/ryuk:latest", "quay.io/testcontainers/ryuk:0.2.3", "quay.io/testcontainers/ryuk@sha256:bb5a635cac4bd96c93cc476969ce11dc56436238ec7cd028d0524462f4739dd9", +// "ibmcom/db2express-c", // Big image for testing with slow networks }; } diff --git a/docs/features/configuration.md b/docs/features/configuration.md index 72b5cac65f7..2ef7e4997a3 100644 --- a/docs/features/configuration.md +++ b/docs/features/configuration.md @@ -59,3 +59,7 @@ but does not allow starting privileged containers, you can turn off the Ryuk con !!!tip Note that Testcontainers will continue doing the cleanup at JVM's shutdown, unless you `kill -9` your JVM process. +## Customizing image pull behaviour + +> **pull.pause.timeout = 30** +> By default Testcontainers will abort the pull of an image if the pull appears stalled (no data transferred) for longer than this duration (in seconds). From 1d686a1d99050a213572891702e531cacb372ad6 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 23 Jul 2019 10:15:36 +0200 Subject: [PATCH 0816/5615] Implement `dependsOn` for cross-container dependencies (#1404) * Implement `dependsOn` for cross-container dependencies * Add transitive test case * fix the compilation on JDK 11 * avoid a recursive update in `computeIfAbsent` * javadocs, add a test case for the diamond graph --- .../containers/GenericContainer.java | 37 ++++- .../testcontainers/lifecycle/Startable.java | 7 + .../testcontainers/lifecycle/Startables.java | 76 ++++++++++ .../junit/DependenciesTest.java | 142 ++++++++++++++++++ 4 files changed, 261 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/org/testcontainers/lifecycle/Startables.java create mode 100644 core/src/test/java/org/testcontainers/junit/DependenciesTest.java diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index c2701c2f87e..14ce129c9a5 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -44,6 +44,7 @@ import org.testcontainers.images.RemoteDockerImage; import org.testcontainers.images.builder.Transferable; import org.testcontainers.lifecycle.Startable; +import org.testcontainers.lifecycle.Startables; import org.testcontainers.lifecycle.TestDescription; import org.testcontainers.lifecycle.TestLifecycleAware; import org.testcontainers.utility.Base58; @@ -74,6 +75,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -88,7 +90,6 @@ * Base class for that allows a container to be launched and controlled. */ @Data -@EqualsAndHashCode(callSuper = false) public class GenericContainer> extends FailureDetectingExternalResource implements Container, AutoCloseable, WaitStrategyTarget, Startable { @@ -165,6 +166,8 @@ public class GenericContainer> private Map copyToFileContainerPathMap = new HashMap<>(); + protected final Set dependencies = new HashSet<>(); + /* * Unique instance of DockerClient for use by this container object. */ @@ -232,6 +235,26 @@ public GenericContainer(@NonNull final Future image) { this.image = image; } + /** + * @see #dependsOn(List) + */ + public SELF dependsOn(Startable... startables) { + Collections.addAll(dependencies, startables); + return self(); + } + + /** + * Delays this container's creation and start until provided {@link Startable}s start first. + * Note that the circular dependencies are not supported. + * + * @param startables a list of {@link Startable} to depend on + * @see Startables#deepStart(Collection) + */ + public SELF dependsOn(List startables) { + dependencies.addAll(startables); + return self(); + } + public String getContainerId() { return containerId; } @@ -240,10 +263,12 @@ public String getContainerId() { * Starts the container using docker, pulling an image if necessary. */ @Override + @SneakyThrows({InterruptedException.class, ExecutionException.class}) public void start() { if (containerId != null) { return; } + Startables.deepStart(dependencies).get(); doStart(); } @@ -1221,6 +1246,16 @@ public SELF withTmpFs(Map mapping) { return self(); } + @Override + public boolean equals(Object o) { + return this == o; + } + + @Override + public int hashCode() { + return System.identityHashCode(this); + } + /** * Convenience class with access to non-public members of GenericContainer. * diff --git a/core/src/main/java/org/testcontainers/lifecycle/Startable.java b/core/src/main/java/org/testcontainers/lifecycle/Startable.java index fa2bfd9dfc5..3e73968d06e 100644 --- a/core/src/main/java/org/testcontainers/lifecycle/Startable.java +++ b/core/src/main/java/org/testcontainers/lifecycle/Startable.java @@ -1,7 +1,14 @@ package org.testcontainers.lifecycle; +import java.util.Collections; +import java.util.Set; + public interface Startable extends AutoCloseable { + default Set getDependencies() { + return Collections.emptySet(); + } + void start(); void stop(); diff --git a/core/src/main/java/org/testcontainers/lifecycle/Startables.java b/core/src/main/java/org/testcontainers/lifecycle/Startables.java new file mode 100644 index 00000000000..0e6452c1914 --- /dev/null +++ b/core/src/main/java/org/testcontainers/lifecycle/Startables.java @@ -0,0 +1,76 @@ +package org.testcontainers.lifecycle; + +import lombok.experimental.UtilityClass; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Stream; + +@UtilityClass +public class Startables { + + private static final Executor EXECUTOR = Executors.newCachedThreadPool(new ThreadFactory() { + + private final AtomicLong COUNTER = new AtomicLong(0); + + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r, "testcontainers-lifecycle-" + COUNTER.getAndIncrement()); + thread.setDaemon(true); + return thread; + } + }); + + /** + * @see #deepStart(Stream) + */ + public CompletableFuture deepStart(Collection startables) { + return deepStart(startables.stream()); + } + + /** + * Start every {@link Startable} recursively and asynchronously and join on the result. + * + * Performance note: + * The method uses and returns {@link CompletableFuture}s to resolve as many {@link Startable}s at once as possible. + * This way, for the following graph: + * / b \ + * a e + * c / + * d / + * "a", "c" and "d" will resolve in parallel, then "b". + * + * If we would call blocking {@link Startable#start()}, "e" would wait for "b", "b" for "a", and only then "c", and then "d". + * But, since "c" and "d" are independent from "a", there is no point in waiting for "a" to be resolved first. + * + * @param startables a {@link Stream} of {@link Startable}s to start and scan for transitive dependencies. + * @return a {@link CompletableFuture} that resolves once all {@link Startable}s have started. + */ + public CompletableFuture deepStart(Stream startables) { + return deepStart(new HashMap<>(), startables); + } + + /** + * + * @param started an intermediate storage for already started {@link Startable}s to prevent multiple starts. + * @param startables a {@link Stream} of {@link Startable}s to start and scan for transitive dependencies. + */ + private CompletableFuture deepStart(Map> started, Stream startables) { + CompletableFuture[] futures = startables + .map(it -> { + // avoid a recursive update in `computeIfAbsent` + Map> subStarted = new HashMap<>(started); + CompletableFuture future = started.computeIfAbsent(it, startable -> { + return deepStart(subStarted, startable.getDependencies().stream()).thenRunAsync(startable::start, EXECUTOR); + }); + started.putAll(subStarted); + return future; + }) + .toArray(CompletableFuture[]::new); + + return CompletableFuture.allOf(futures); + } +} diff --git a/core/src/test/java/org/testcontainers/junit/DependenciesTest.java b/core/src/test/java/org/testcontainers/junit/DependenciesTest.java new file mode 100644 index 00000000000..df7e85a31f8 --- /dev/null +++ b/core/src/test/java/org/testcontainers/junit/DependenciesTest.java @@ -0,0 +1,142 @@ +package org.testcontainers.junit; + +import lombok.Getter; +import org.junit.Test; +import org.rnorth.visibleassertions.VisibleAssertions; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; +import org.testcontainers.lifecycle.Startable; +import org.testcontainers.lifecycle.Startables; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Stream; + +public class DependenciesTest { + + @Test + public void shouldWorkWithSimpleDependency() { + InvocationCountingStartable startable = new InvocationCountingStartable(); + + try ( + GenericContainer container = new GenericContainer() + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .dependsOn(startable) + ) { + container.start(); + } + + VisibleAssertions.assertEquals("Started once", 1, startable.getStartInvocationCount().intValue()); + VisibleAssertions.assertEquals("Does not trigger .stop()", 0, startable.getStopInvocationCount().intValue()); + } + + @Test + public void shouldWorkWithMutlipleDependencies() { + InvocationCountingStartable startable1 = new InvocationCountingStartable(); + InvocationCountingStartable startable2 = new InvocationCountingStartable(); + + try ( + GenericContainer container = new GenericContainer() + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .dependsOn(startable1, startable2) + ) { + container.start(); + } + + VisibleAssertions.assertEquals("Startable1 started once", 1, startable1.getStartInvocationCount().intValue()); + VisibleAssertions.assertEquals("Startable2 started once", 1, startable2.getStartInvocationCount().intValue()); + } + + @Test + public void shouldStartEveryTime() { + InvocationCountingStartable startable = new InvocationCountingStartable(); + + try ( + GenericContainer container = new GenericContainer() + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .dependsOn(startable) + ) { + container.start(); + container.stop(); + + container.start(); + container.stop(); + + container.start(); + } + + VisibleAssertions.assertEquals("Started multiple times", 3, startable.getStartInvocationCount().intValue()); + VisibleAssertions.assertEquals("Does not trigger .stop()", 0, startable.getStopInvocationCount().intValue()); + } + + @Test + public void shouldStartTransitiveDependencies() { + InvocationCountingStartable transitiveOfTransitiveStartable = new InvocationCountingStartable(); + InvocationCountingStartable transitiveStartable = new InvocationCountingStartable(); + transitiveStartable.getDependencies().add(transitiveOfTransitiveStartable); + + InvocationCountingStartable startable = new InvocationCountingStartable(); + startable.getDependencies().add(transitiveStartable); + + try ( + GenericContainer container = new GenericContainer() + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .dependsOn(startable) + ) { + container.start(); + container.stop(); + } + + VisibleAssertions.assertEquals("Root started", 1, startable.getStartInvocationCount().intValue()); + VisibleAssertions.assertEquals("Transitive started", 1, transitiveStartable.getStartInvocationCount().intValue()); + VisibleAssertions.assertEquals("Transitive of transitive started", 1, transitiveOfTransitiveStartable.getStartInvocationCount().intValue()); + } + + @Test + public void shouldHandleDiamondDependencies() throws Exception { + InvocationCountingStartable a = new InvocationCountingStartable(); + InvocationCountingStartable b = new InvocationCountingStartable(); + InvocationCountingStartable c = new InvocationCountingStartable(); + InvocationCountingStartable d = new InvocationCountingStartable(); + // / b \ + // a d + // \ c / + b.getDependencies().add(a); + c.getDependencies().add(a); + + d.getDependencies().add(b); + d.getDependencies().add(c); + + Startables.deepStart(Stream.of(d)).get(1, TimeUnit.SECONDS); + + VisibleAssertions.assertEquals("A started", 1, a.getStartInvocationCount().intValue()); + VisibleAssertions.assertEquals("B started", 1, b.getStartInvocationCount().intValue()); + VisibleAssertions.assertEquals("C started", 1, c.getStartInvocationCount().intValue()); + VisibleAssertions.assertEquals("D started", 1, d.getStartInvocationCount().intValue()); + } + + private static class InvocationCountingStartable implements Startable { + + @Getter + Set dependencies = new HashSet<>(); + + @Getter + AtomicLong startInvocationCount = new AtomicLong(0); + + @Getter + AtomicLong stopInvocationCount = new AtomicLong(0); + + @Override + public void start() { + startInvocationCount.getAndIncrement(); + + } + + @Override + public void stop() { + stopInvocationCount.getAndIncrement(); + } + } +} From 64dcf7aa632b388cc5d20da2286b3eea99b003a1 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 23 Jul 2019 10:56:04 +0200 Subject: [PATCH 0817/5615] use DigitalOcean for the build cache --- settings.gradle | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/settings.gradle b/settings.gradle index e68987a50b9..272daa5699d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,7 +5,7 @@ buildscript { } } dependencies { - classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.4.1" + classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.9.0" } } @@ -19,8 +19,10 @@ buildCache { } remote(ch.myniva.gradle.caching.s3.AwsS3BuildCache) { - region = 'us-east-1' - bucket = 'testcontainers-build-cache' + endpoint = 'fra1.digitaloceanspaces.com' + region = 'fra1' + bucket = 'testcontainers' + path = 'cache' push = isMasterBuild } } @@ -38,4 +40,4 @@ file('modules').eachDir { dir -> } include "docs-examples" -project(":docs-examples").projectDir = file("docs/examples") \ No newline at end of file +project(":docs-examples").projectDir = file("docs/examples") From fcfff1a675d3e211b969584e842ba07f603b12ea Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 23 Jul 2019 11:53:35 +0100 Subject: [PATCH 0818/5615] Improve splitting of SQL scripts into statements (#1627) Fixes #1547 --- modules/database-commons/build.gradle | 2 + .../org/testcontainers/ext/ScriptUtils.java | 51 ++- .../ext/ScriptSplittingTest.java | 294 ++++++++++++++++++ .../testcontainers/ext/ScriptUtilsTest.java | 48 --- .../src/test/resources/splittable.sql | 45 --- 5 files changed, 337 insertions(+), 103 deletions(-) create mode 100644 modules/database-commons/src/test/java/org/testcontainers/ext/ScriptSplittingTest.java delete mode 100644 modules/database-commons/src/test/java/org/testcontainers/ext/ScriptUtilsTest.java delete mode 100644 modules/database-commons/src/test/resources/splittable.sql diff --git a/modules/database-commons/build.gradle b/modules/database-commons/build.gradle index 8b9c288b9d4..27d1aeeb5e6 100644 --- a/modules/database-commons/build.gradle +++ b/modules/database-commons/build.gradle @@ -2,4 +2,6 @@ description = "Testcontainers :: Database-Commons" dependencies { compile project(':testcontainers') + + testCompile 'org.assertj:assertj-core:3.12.2' } diff --git a/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java b/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java index eb041708fb6..d30064af28e 100644 --- a/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java +++ b/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java @@ -163,10 +163,10 @@ public static void splitSqlScript(String resource, String script, String separat } final boolean inComment = inLineComment || inBlockComment; - if (!inLiteral && !inComment && containsSubstringAtOffset(lowerCaseScriptContent, "BEGIN", i)) { + if (!inLiteral && !inComment && containsKeywordsAtOffset(lowerCaseScriptContent, "BEGIN", i, separator, commentPrefix, blockCommentStartDelimiter)) { compoundStatementDepth++; } - if (!inLiteral && !inComment && containsSubstringAtOffset(lowerCaseScriptContent, "END", i)) { + if (!inLiteral && !inComment && containsKeywordsAtOffset(lowerCaseScriptContent, "END", i, separator, commentPrefix, blockCommentStartDelimiter)) { compoundStatementDepth--; } final boolean inCompoundStatement = compoundStatementDepth != 0; @@ -174,10 +174,7 @@ public static void splitSqlScript(String resource, String script, String separat if (!inLiteral && !inCompoundStatement) { if (script.startsWith(separator, i)) { // we've reached the end of the current statement - if (sb.length() > 0) { - statements.add(sb.toString()); - sb = new StringBuilder(); - } + sb = flushStringBuilder(sb, statements); i += separator.length() - 1; continue; } @@ -199,6 +196,7 @@ else if (script.startsWith(blockCommentStartDelimiter, i)) { int indexOfCommentEnd = script.indexOf(blockCommentEndDelimiter, i); if (indexOfCommentEnd > i) { i = indexOfCommentEnd + blockCommentEndDelimiter.length() - 1; + inBlockComment = false; continue; } else { @@ -218,24 +216,57 @@ else if (c == ' ' || c == '\n' || c == '\t' || c == '\r') { } sb.append(c); } - if (StringUtils.isNotEmpty(sb.toString())) { - statements.add(sb.toString()); + flushStringBuilder(sb, statements); + } + + private static StringBuilder flushStringBuilder(StringBuilder sb, List statements) { + if (sb.length() == 0) { + return sb; } + + final String s = sb.toString().trim(); + if (StringUtils.isNotEmpty(s)) { + statements.add(s); + } + + return new StringBuilder(); } + private static boolean isSeperator(char c, String separator, String commentPrefix, + String blockCommentStartDelimiter) { + return c == ' ' || c == '\r' || c == '\n' || c == '\t' || + c == separator.charAt(0) || c == separator.charAt(separator.length() - 1) || + c == commentPrefix.charAt(0) || c == blockCommentStartDelimiter.charAt(0) || + c == blockCommentStartDelimiter.charAt(blockCommentStartDelimiter.length() - 1); + } + private static boolean containsSubstringAtOffset(String lowercaseString, String substring, int offset) { String lowercaseSubstring = substring.toLowerCase(); return lowercaseString.startsWith(lowercaseSubstring, offset); } + private static boolean containsKeywordsAtOffset(String lowercaseString, String keywords, int offset, + String separator, String commentPrefix, + String blockCommentStartDelimiter) { + String lowercaseKeywords = keywords.toLowerCase(); + + boolean backSeperated = (offset == 0) || isSeperator(lowercaseString.charAt(offset - 1), + separator, commentPrefix, blockCommentStartDelimiter); + boolean frontSeperated = (offset >= (lowercaseString.length() - keywords.length())) || + isSeperator(lowercaseString.charAt(offset + keywords.length()), + separator, commentPrefix, blockCommentStartDelimiter); + + return backSeperated && frontSeperated && lowercaseString.startsWith(lowercaseKeywords, offset); + } + private static void checkArgument(boolean expression, String errorMessage) { if (!expression) { throw new IllegalArgumentException(errorMessage); } } - /** + /** * Does the provided SQL script contain the specified delimiter? * @param script the SQL script * @param delim String delimiting each statement - typically a ';' character @@ -356,7 +387,7 @@ public ScriptLoadException(String message, Throwable cause) { } } - private static class ScriptParseException extends RuntimeException { + public static class ScriptParseException extends RuntimeException { public ScriptParseException(String format, String scriptPath) { super(String.format(format, scriptPath)); } diff --git a/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptSplittingTest.java b/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptSplittingTest.java new file mode 100644 index 00000000000..0836592b91e --- /dev/null +++ b/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptSplittingTest.java @@ -0,0 +1,294 @@ +package org.testcontainers.ext; + +import org.assertj.core.api.Assertions; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static java.util.Arrays.asList; +import static org.junit.Assert.fail; +import static org.testcontainers.ext.ScriptUtils.*; + +public class ScriptSplittingTest { + + @Test + public void testStringDemarcation() { + String script = "SELECT 'foo `bar`'; SELECT 'foo -- `bar`'; SELECT 'foo /* `bar`';"; + + List expected = asList( + "SELECT 'foo `bar`'", + "SELECT 'foo -- `bar`'", + "SELECT 'foo /* `bar`'" + ); + + splitAndCompare(script, expected); + } + + @Test + public void testIssue1547Case1() { + String script = "create database if not exists ttt;\n" + + "\n" + + "use ttt;\n" + + "\n" + + "create table aaa\n" + + "(\n" + + " id bigint auto_increment primary key,\n" + + " end_time datetime null COMMENT 'end_time',\n" + + " data_status varchar(16) not null\n" + + ") comment 'aaa';\n" + + "\n" + + "create table bbb\n" + + "(\n" + + " id bigint auto_increment primary key\n" + + ") comment 'bbb';"; + + List expected = asList( + "create database if not exists ttt", + "use ttt", + "create table aaa ( id bigint auto_increment primary key, end_time datetime null COMMENT 'end_time', data_status varchar(16) not null ) comment 'aaa'", + "create table bbb ( id bigint auto_increment primary key ) comment 'bbb'" + ); + + splitAndCompare(script, expected); + } + + @Test + public void testIssue1547Case2() { + String script = "CREATE TABLE bar (\n" + + " end_time VARCHAR(255)\n" + + ");\n" + + "CREATE TABLE bar (\n" + + " end_time VARCHAR(255)\n" + + ");"; + + List expected = asList( + "CREATE TABLE bar ( end_time VARCHAR(255) )", + "CREATE TABLE bar ( end_time VARCHAR(255) )" + ); + + splitAndCompare(script, expected); + } + + @Test + public void testUnusualSemicolonPlacement() { + String script = "SELECT 1;;;;;SELECT 2;\n;SELECT 3\n; SELECT 4;\n SELECT 5"; + + List expected = asList( + "SELECT 1", + "SELECT 2", + "SELECT 3", + "SELECT 4", + "SELECT 5" + ); + + splitAndCompare(script, expected); + } + + @Test + public void testCommentedSemicolon() { + String script = "CREATE TABLE bar (\n" + + " foo VARCHAR(255)\n" + + "); \nDROP PROCEDURE IF EXISTS -- ;\n" + + " count_foo"; + + List expected = asList( + "CREATE TABLE bar ( foo VARCHAR(255) )", + "DROP PROCEDURE IF EXISTS count_foo" + ); + + splitAndCompare(script, expected); + } + + @Test + public void testStringEscaping() { + String script = "SELECT \"a /* string literal containing comment characters like -- here\";\n" + + "SELECT \"a 'quoting' \\\"scenario ` involving BEGIN keyword\\\" here\";\n" + + "SELECT * from `bar`;"; + + List expected = asList( + "SELECT \"a /* string literal containing comment characters like -- here\"", + "SELECT \"a 'quoting' \\\"scenario ` involving BEGIN keyword\\\" here\"", + "SELECT * from `bar`" + ); + + splitAndCompare(script, expected); + } + + @Test + public void testBlockCommentExclusion() { + String script = "INSERT INTO bar (foo) /* ; */ VALUES ('hello world');"; + + List expected = asList( + "INSERT INTO bar (foo) VALUES ('hello world')" + ); + + splitAndCompare(script, expected); + } + + @Test + public void testBeginEndKeywordCorrectDetection() { + String script = "INSERT INTO something_end (begin_with_the_token, another_field) /*end*/ VALUES /* end */ (' begin ', `end`)-- begin\n;"; + + List expected = asList( + "INSERT INTO something_end (begin_with_the_token, another_field) VALUES (' begin ', `end`)" + ); + + splitAndCompare(script, expected); + } + + @Test + public void testCommentInStrings() { + String script = "CREATE TABLE bar (foo VARCHAR(255));\n" + + "\n" + + "/* Insert Values */\n" + + "INSERT INTO bar (foo) values ('--1');\n" + + "INSERT INTO bar (foo) values ('--2');\n" + + "INSERT INTO bar (foo) values ('/* something */');\n" + + "/* INSERT INTO bar (foo) values (' */'); -- '*/;\n" + // purposefully broken, to see if it breaks our splitting + "INSERT INTO bar (foo) values ('foo');"; + + List expected = asList( + "CREATE TABLE bar (foo VARCHAR(255))", + "INSERT INTO bar (foo) values ('--1')", + "INSERT INTO bar (foo) values ('--2')", + "INSERT INTO bar (foo) values ('/* something */')", + "'); -- '*/", + "INSERT INTO bar (foo) values ('foo')" + ); + + splitAndCompare(script, expected); + } + + @Test + public void testMultipleBeginEndDetection() { + String script = "CREATE TABLE bar (foo VARCHAR(255));\n" + + "\n" + + "CREATE TABLE gender (gender VARCHAR(255));\n" + + "CREATE TABLE ending (ending VARCHAR(255));\n" + + "CREATE TABLE end2 (end2 VARCHAR(255));\n" + + "CREATE TABLE end_2 (end2 VARCHAR(255));\n" + + "\n" + + "BEGIN\n" + + " INSERT INTO ending values ('ending');\n" + + "END;\n" + + "\n" + + "BEGIN\n" + + " INSERT INTO ending values ('ending');\n" + + "END/*hello*/;\n" + + "\n" + + "BEGIN--Hello\n" + + " INSERT INTO ending values ('ending');\n" + + "END;\n" + + "\n" + + "/*Hello*/BEGIN\n" + + " INSERT INTO ending values ('ending');\n" + + "END;\n" + + "\n" + + "CREATE TABLE foo (bar VARCHAR(255));"; + + List expected = asList( + "CREATE TABLE bar (foo VARCHAR(255))", + "CREATE TABLE gender (gender VARCHAR(255))", + "CREATE TABLE ending (ending VARCHAR(255))", + "CREATE TABLE end2 (end2 VARCHAR(255))", + "CREATE TABLE end_2 (end2 VARCHAR(255))", + "BEGIN\n" + + " INSERT INTO ending values ('ending');\n" + + "END", + "BEGIN\n" + + " INSERT INTO ending values ('ending');\n" + + "END", + "BEGIN--Hello\n" + + " INSERT INTO ending values ('ending');\n" + + "END", + "BEGIN\n" + + " INSERT INTO ending values ('ending');\n" + + "END", + "CREATE TABLE foo (bar VARCHAR(255))" + ); + + splitAndCompare(script, expected); + } + + @Test + public void testProcedureBlock() { + String script = "CREATE PROCEDURE count_foo()\n" + + " BEGIN\n" + + "\n" + + " BEGIN\n" + + " SELECT *\n" + + " FROM bar;\n" + + " SELECT 1\n" + + " FROM dual;\n" + + " END;\n" + + "\n" + + " BEGIN\n" + + " select * from bar;\n" + + " END;\n" + + "\n" + + " -- we can do comments\n" + + "\n" + + " /* including block\n" + + " comments\n" + + " */\n" + + "\n" + + " /* what if BEGIN appears inside a comment? */\n" + + "\n" + + " select \"or what if BEGIN appears inside a literal?\";\n" + + "\n" + + " END /*; */;"; + + List expected = asList( + "CREATE PROCEDURE count_foo() BEGIN\n" + + "\n" + + " BEGIN\n" + + " SELECT *\n" + + " FROM bar;\n" + + " SELECT 1\n" + + " FROM dual;\n" + + " END;\n" + + "\n" + + " BEGIN\n" + + " select * from bar;\n" + + " END;\n" + + "\n" + + " -- we can do comments\n" + + "\n" + + " /* including block\n" + + " comments\n" + + " */\n" + + "\n" + + " /* what if BEGIN appears inside a comment? */\n" + + "\n" + + " select \"or what if BEGIN appears inside a literal?\";\n" + + "\n" + + " END" + ); + + splitAndCompare(script, expected); + } + + @Test + public void testUnclosedBlockComment() { + String script = "SELECT 'foo `bar`'; /*"; + + try { + doSplit(script); + fail("Should have thrown!"); + } catch (ScriptUtils.ScriptParseException expected) { + // ignore expected exception + } + } + + private void splitAndCompare(String script, List expected) { + final List statements = doSplit(script); + Assertions.assertThat(statements).isEqualTo(expected); + } + + private List doSplit(String script) { + final List statements = new ArrayList<>(); + ScriptUtils.splitSqlScript("ignored", script, DEFAULT_STATEMENT_SEPARATOR, DEFAULT_COMMENT_PREFIX, DEFAULT_BLOCK_COMMENT_START_DELIMITER, DEFAULT_BLOCK_COMMENT_END_DELIMITER, statements); + return statements; + } +} diff --git a/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptUtilsTest.java b/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptUtilsTest.java deleted file mode 100644 index c9b1218321b..00000000000 --- a/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptUtilsTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.testcontainers.ext; - -import com.google.common.base.Charsets; -import com.google.common.io.Resources; -import org.junit.Test; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import static org.junit.Assert.assertEquals; - -public class ScriptUtilsTest { - - /* - * Test ScriptUtils script splitting with some ugly/hard-to-split cases - */ - @Test - public void testSplit() throws IOException { - final String script = Resources.toString(Resources.getResource("splittable.sql"), Charsets.UTF_8); - - final List statements = new ArrayList<>(); - ScriptUtils.splitSqlScript("resourcename", script, ";", "--", "/*", "*/", statements); - - assertEquals(7, statements.size()); - assertEquals("SELECT \"a /* string literal containing comment characters like -- here\"", statements.get(2)); - assertEquals("SELECT \"a 'quoting' \\\"scenario ` involving BEGIN keyword\\\" here\"", statements.get(3)); - assertEquals("SELECT * from `bar`", statements.get(4)); - assertEquals("INSERT INTO bar (foo) VALUES ('hello world')", statements.get(6)); - } - - /* - * Test ScriptUtils script splitting with some ugly/hard-to-split cases and linux line endings - */ - @Test - public void testSplitWithWidnwosLineEnding() throws IOException { - final String script = Resources.toString(Resources.getResource("splittable.sql"), Charsets.UTF_8); - final String scriptWithWindowsLineEndings = script.replaceAll("\n", "\r\n"); - final List statements = new ArrayList<>(); - ScriptUtils.splitSqlScript("resourcename", scriptWithWindowsLineEndings, ";", "--", "/*", "*/", statements); - - assertEquals(7, statements.size()); - assertEquals("SELECT \"a /* string literal containing comment characters like -- here\"", statements.get(2)); - assertEquals("SELECT \"a 'quoting' \\\"scenario ` involving BEGIN keyword\\\" here\"", statements.get(3)); - assertEquals("SELECT * from `bar`", statements.get(4)); - assertEquals("INSERT INTO bar (foo) VALUES ('hello world')", statements.get(6)); - } -} diff --git a/modules/database-commons/src/test/resources/splittable.sql b/modules/database-commons/src/test/resources/splittable.sql deleted file mode 100644 index addede19dd7..00000000000 --- a/modules/database-commons/src/test/resources/splittable.sql +++ /dev/null @@ -1,45 +0,0 @@ -CREATE TABLE bar ( - foo VARCHAR(255) -); - -DROP PROCEDURE IF EXISTS -- ; - count_foo; - -SELECT "a /* string literal containing comment characters like -- here"; -SELECT "a 'quoting' \"scenario ` involving BEGIN keyword\" here"; -SELECT * from `bar`; - --- What about a line comment containing imbalanced string delimiters? " - -CREATE PROCEDURE count_foo() - BEGIN - - BEGIN - SELECT * - FROM bar; - SELECT 1 - FROM dual; - END; - - BEGIN - select * from bar; - END; - - -- we can do comments - - /* including block - comments - */ - - /* what if BEGIN appears inside a comment? */ - - select "or what if BEGIN appears inside a literal?"; - - END /*; */; - -/* or a block comment - containing imbalanced string delimiters? - ' " - */ - -INSERT INTO bar (foo) /* ; */ VALUES ('hello world'); From 195339b127abd8036b3a995ff21c18a0b388fecf Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 23 Jul 2019 21:39:36 +0200 Subject: [PATCH 0819/5615] Bump docker-java from 3.1.0-rc-4 to 3.1.5 (#1649) * Bump docker-java from 3.1.0-rc-4 to 3.1.5 in /core Bumps [docker-java](https://github.com/docker-java/docker-java) from 3.1.0-rc-4 to 3.1.5. - [Release notes](https://github.com/docker-java/docker-java/releases) - [Changelog](https://github.com/docker-java/docker-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/docker-java/docker-java/compare/3.1.0-rc-4...3.1.5) Signed-off-by: dependabot-preview[bot] * remove `FiltersEncoder` (backported to docker-java) * run jdbc checks separately on Travis * fix jdbc -> jdbc-test fixes #1548 --- .travis.yml | 5 +++- core/build.gradle | 2 +- .../dockerjava/core/util/FiltersEncoder.java | 27 ------------------ .../core/util/FiltersEncoderTest.java | 28 ------------------- 4 files changed, 5 insertions(+), 57 deletions(-) delete mode 100644 core/src/main/java/com/github/dockerjava/core/util/FiltersEncoder.java delete mode 100644 core/src/test/java/com/github/dockerjava/core/util/FiltersEncoderTest.java diff --git a/.travis.yml b/.travis.yml index 575aa50b208..0c03c4931bb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,7 +29,10 @@ jobs: script: ./gradlew selenium:check --scan --no-daemon - env: [ NAME=modules ] - script: ./gradlew check -x testcontainers:check -x selenium:check --scan --no-daemon + script: ./gradlew check -x testcontainers:check -x selenium:check -x jdbc-test:check --scan --no-daemon + + - env: [ NAME=jdbc ] + script: ./gradlew jdbc-test:check --scan --no-daemon - env: [ NAME=examples ] script: cd examples && ../gradlew check --no-daemon diff --git a/core/build.gradle b/core/build.gradle index f400b5e3c1c..545780c0bc7 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -79,7 +79,7 @@ dependencies { exclude(group: "net.java.dev.jna") } - shaded ('com.github.docker-java:docker-java:3.1.0-rc-4') { + shaded ('com.github.docker-java:docker-java:3.1.5') { exclude(group: 'org.glassfish.jersey.core') exclude(group: 'org.glassfish.jersey.connectors') exclude(group: 'org.glassfish.jersey.inject') diff --git a/core/src/main/java/com/github/dockerjava/core/util/FiltersEncoder.java b/core/src/main/java/com/github/dockerjava/core/util/FiltersEncoder.java deleted file mode 100644 index 2a6f185462e..00000000000 --- a/core/src/main/java/com/github/dockerjava/core/util/FiltersEncoder.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.github.dockerjava.core.util; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; - -import java.util.List; -import java.util.Map; - -/** - * Monkey patched {@link FiltersEncoder} to avoid a use of jaxrs. - * See https://github.com/docker-java/docker-java/issues/1176 - */ -public class FiltersEncoder { - - private FiltersEncoder() { - } - - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - - public static String jsonEncode(Map> filters) { - try { - return OBJECT_MAPPER.writeValueAsString(filters); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } -} diff --git a/core/src/test/java/com/github/dockerjava/core/util/FiltersEncoderTest.java b/core/src/test/java/com/github/dockerjava/core/util/FiltersEncoderTest.java deleted file mode 100644 index 353133ae148..00000000000 --- a/core/src/test/java/com/github/dockerjava/core/util/FiltersEncoderTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.github.dockerjava.core.util; - -import com.github.dockerjava.api.DockerClient; -import com.github.dockerjava.api.model.Image; -import org.junit.Test; -import org.rnorth.visibleassertions.VisibleAssertions; -import org.testcontainers.DockerClientFactory; - -import java.util.List; -import java.util.UUID; - -/** - * This test checks that our custom monkey-patched version works - * and does not throw {@link ClassNotFoundException}. - */ -public class FiltersEncoderTest { - - @Test - public void filtersShouldWork() throws Exception { - DockerClient client = DockerClientFactory.instance().client(); - - List images = client.listImagesCmd() - .withLabelFilter("com.example=" + UUID.randomUUID().toString()) - .exec(); - - VisibleAssertions.assertTrue("List is empty", images.isEmpty()); - } -} From 365856d2b23f6ca2529ec557329719e3276c3166 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 23 Jul 2019 22:25:15 +0000 Subject: [PATCH 0820/5615] Bump neo4j from 3.5.7 to 3.5.8 in /modules/neo4j (#1640) --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 9c5410aa179..1ea0ab3fb23 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -25,7 +25,7 @@ task customNeo4jPluginJar(type: Jar) { test.dependsOn customNeo4jPluginJar dependencies { - customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.7" + customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.8" compile project(":testcontainers") From 1d0f022aa367c91ab0ce6217964805445bd814cd Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 24 Jul 2019 00:57:58 +0000 Subject: [PATCH 0821/5615] Bump junit-jupiter-engine from 5.5.0 to 5.5.1 in /modules/junit-jupiter (#1642) --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 08bdedc6620..53eec4bece1 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -15,7 +15,7 @@ dependencies { testRuntime 'org.postgresql:postgresql:42.2.6' testRuntime 'mysql:mysql-connector-java:8.0.16' - testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.5.0' + testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.5.1' } test { From 505731a854e3c2df583416e3acc7012ea08ab1bc Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 24 Jul 2019 03:35:08 +0000 Subject: [PATCH 0822/5615] Bump junit-jupiter-api from 5.5.0 to 5.5.1 in /modules/junit-jupiter (#1638) --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 53eec4bece1..198d67764c7 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -2,7 +2,7 @@ description = "Testcontainers :: JUnit Jupiter Extension" dependencies { compile project(':testcontainers') - compile 'org.junit.jupiter:junit-jupiter-api:5.5.0' + compile 'org.junit.jupiter:junit-jupiter-api:5.5.1' testCompile project(':mysql') testCompile project(':postgresql') From 65f4ee7ea65ca5039943675131075a6c95facd8e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 24 Jul 2019 05:08:34 +0000 Subject: [PATCH 0823/5615] Bump aws-java-sdk-sqs from 1.11.594 to 1.11.597 in /modules/localstack (#1654) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 3bcfcb91714..fd51e51a9c8 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.594' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.597' } From eb7d96b6c78afb463c746dddfe7775cbc16842e5 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 24 Jul 2019 06:37:37 +0000 Subject: [PATCH 0824/5615] Bump mysql-connector-java from 8.0.16 to 8.0.17 in /modules/mysql (#1633) --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 2540df73c3f..3647fe11cc6 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: JDBC :: MySQL" dependencies { compile project(':jdbc') - testCompile 'mysql:mysql-connector-java:8.0.16' + testCompile 'mysql:mysql-connector-java:8.0.17' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.22' testCompile 'commons-dbutils:commons-dbutils:1.7' From 1010b8955f36a31c2c5d05e7a650d5ba1cbf9ba8 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 24 Jul 2019 10:19:38 +0200 Subject: [PATCH 0825/5615] Add "housekeeping" category to the release drafter config (#1652) --- .github/release-drafter.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 1bfa59ca589..ed8939a3117 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -11,5 +11,9 @@ categories: label: type/bug - title: 📖 Documentation label: type/docs + - title: 🧹 Housekeeping + labels: + - type/housekeeping + - type/test-improvement - title: 📦 Dependency updates label: dependencies From 46041742b048cd93da5e94e13807053e70466a67 Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 24 Jul 2019 10:20:13 +0100 Subject: [PATCH 0826/5615] Add link to DB2 module docs, and add incubating note to page (#1660) --- docs/contributing.md | 4 ++-- docs/modules/databases/db2.md | 3 +++ mkdocs.yml | 13 +++++++------ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/docs/contributing.md b/docs/contributing.md index 9d2c63b030c..70cd452b143 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -86,7 +86,7 @@ We currently believe 3 months is a fair period of time, but may change this. New modules should have the following warning at the top of their documentation pages: -!!! warning - This module is INCUBATING. While it is ready for use and operational in the current version of Testcontainers, it is possible that it may receive breaking changes in the future. +!!! note + This module is INCUBATING. While it is ready for use and operational in the current version of Testcontainers, it is possible that it may receive breaking changes in the future. See [our contributing guidelines](/contributing/#incubating-modules) for more information on our incubating modules policy. We will evaluate incubating modules periodically, and remove the label when appropriate. diff --git a/docs/modules/databases/db2.md b/docs/modules/databases/db2.md index b7871e636d0..bc25b343072 100644 --- a/docs/modules/databases/db2.md +++ b/docs/modules/databases/db2.md @@ -1,5 +1,8 @@ # DB2 Module +!!! note + This module is INCUBATING. While it is ready for use and operational in the current version of Testcontainers, it is possible that it may receive breaking changes in the future. See [our contributing guidelines](/contributing/#incubating-modules) for more information on our incubating modules policy. + See [Database containers](./index.md) for documentation and usage that is common to all relational database container types. ## Usage example diff --git a/mkdocs.yml b/mkdocs.yml index 6b38d82eff4..de9819f102f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -39,17 +39,18 @@ nav: - Modules: - Databases: - modules/databases/index.md - - modules/databases/mysql.md - - modules/databases/mariadb.md - - modules/databases/postgres.md - - modules/databases/oraclexe.md - - modules/databases/mssqlserver.md - modules/databases/cassandra.md - modules/databases/couchbase.md - modules/databases/clickhouse.md - - modules/databases/influxdb.md + - modules/databases/db2.md - modules/databases/dynalite.md + - modules/databases/influxdb.md + - modules/databases/mariadb.md + - modules/databases/mssqlserver.md + - modules/databases/mysql.md - modules/databases/neo4j.md + - modules/databases/oraclexe.md + - modules/databases/postgres.md - modules/webdriver_containers.md - modules/docker_compose.md - modules/elasticsearch.md From e95bdd5a3e503b2120ecc4a01ea4501ca72c3fc7 Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 24 Jul 2019 17:51:10 +0100 Subject: [PATCH 0827/5615] Add a basic documentation page for RabbitMQ module (#1661) --- docs/modules/rabbitmq.md | 21 +++++++++++++++++++++ mkdocs.yml | 7 ++++--- 2 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 docs/modules/rabbitmq.md diff --git a/docs/modules/rabbitmq.md b/docs/modules/rabbitmq.md new file mode 100644 index 00000000000..92275ad765b --- /dev/null +++ b/docs/modules/rabbitmq.md @@ -0,0 +1,21 @@ +# RabbitMQ Module + +!!! note + This module is INCUBATING. While it is ready for use and operational in the current version of Testcontainers, it is possible that it may receive breaking changes in the future. See [our contributing guidelines](/contributing/#incubating-modules) for more information on our incubating modules policy. + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:rabbitmq:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + rabbitmq + {{latest_version}} + test + +``` diff --git a/mkdocs.yml b/mkdocs.yml index de9819f102f..044489d1e50 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -51,16 +51,17 @@ nav: - modules/databases/neo4j.md - modules/databases/oraclexe.md - modules/databases/postgres.md - - modules/webdriver_containers.md - modules/docker_compose.md - modules/elasticsearch.md - modules/kafka.md - - modules/pulsar.md - modules/localstack.md - modules/mockserver.md - - modules/toxiproxy.md - modules/nginx.md + - modules/pulsar.md + - modules/rabbitmq.md + - modules/toxiproxy.md - modules/vault.md + - modules/webdriver_containers.md - Test framework integration: - test_framework_integration/junit_4.md - test_framework_integration/junit_5.md From bf84ace9e4841bba56555e26c7d4b041080ff994 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 25 Jul 2019 05:59:58 +0000 Subject: [PATCH 0828/5615] Bump aws-java-sdk-sqs from 1.11.597 to 1.11.598 in /modules/localstack (#1664) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index fd51e51a9c8..46b9b059a6a 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.597' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.598' } From cde3d464a63845868d43ea20c9e5448432437524 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 26 Jul 2019 05:59:26 +0000 Subject: [PATCH 0829/5615] Bump aws-java-sdk-sqs from 1.11.598 to 1.11.599 in /modules/localstack (#1667) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 46b9b059a6a..5481ea1696c 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.598' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.599' } From f9453ddee1a5b15c43f3290e9136eee4a1a251fc Mon Sep 17 00:00:00 2001 From: Michael Irwin Date: Fri, 26 Jul 2019 05:43:17 -0400 Subject: [PATCH 0830/5615] Add networking documentation (#1651) * Add networking documentation Resolves #1159 * Use code snippet from NetworkTest in networking documentation Issue #1159 * Removed example no longer being used Issue #1159 --- .../org/testcontainers/containers/NetworkTest.java | 5 ++++- docs/features/networking.md | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/core/src/test/java/org/testcontainers/containers/NetworkTest.java b/core/src/test/java/org/testcontainers/containers/NetworkTest.java index 04edf42c6d1..b717b8dc81a 100644 --- a/core/src/test/java/org/testcontainers/containers/NetworkTest.java +++ b/core/src/test/java/org/testcontainers/containers/NetworkTest.java @@ -5,6 +5,7 @@ import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.Network; import static org.rnorth.visibleassertions.VisibleAssertions.*; import static org.testcontainers.containers.Network.newNetwork; @@ -39,8 +40,9 @@ public static class WithoutRules { @Test public void testNetworkSupport() throws Exception { + // useCustomNetwork { try ( - Network network = newNetwork(); + Network network = Network.newNetwork(); GenericContainer foo = new GenericContainer() .withNetwork(network) @@ -57,6 +59,7 @@ public void testNetworkSupport() throws Exception { String response = bar.execInContainer("wget", "-O", "-", "http://foo:8080").getStdout(); assertEquals("received response", "yay", response); } + // } } @Test diff --git a/docs/features/networking.md b/docs/features/networking.md index d0007df878b..75dd924d6bc 100644 --- a/docs/features/networking.md +++ b/docs/features/networking.md @@ -75,3 +75,15 @@ For example, here we construct an HTTP URL for our local web application and tel [Accessing the exposed host port from a container](../examples/src/test/java/generic/HostPortExposedTest.java) inside_block:useHostExposedPort + + +## Advanced networking + +Docker provides the ability for you to create custom networks and place containers on one or more networks. Then, communication can occur between networked containers without the need of exposing ports through the host. With Testcontainers, you can do this as well. + +!!! warning + Note that Testcontainers currently only allows a container to be on a single network. + + +[Creating custom networks](../../core/src/test/java/org/testcontainers/containers/NetworkTest.java) inside_block:useCustomNetwork + \ No newline at end of file From c094a034f733215bfd88d41538089699caab1616 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2019 07:02:56 +0000 Subject: [PATCH 0831/5615] Bump cucumber-java from 4.6.0 to 4.7.1 in /examples (#1677) --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 9907da86262..08626f9e257 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' - implementation 'io.cucumber:cucumber-java:4.6.0' + implementation 'io.cucumber:cucumber-java:4.7.1' testImplementation 'io.cucumber:cucumber-junit:4.6.0' testImplementation 'org.testcontainers:selenium' } From b12f6526fef4b05bbcc3ff8e267b9ceda437b820 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2019 09:30:40 +0000 Subject: [PATCH 0832/5615] Bump mysql-connector-java from 8.0.16 to 8.0.17 in /modules/spock (#1634) --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 74c6b6fd5db..f9353314c1b 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -14,5 +14,5 @@ dependencies { testCompile 'org.apache.httpcomponents:httpclient:4.5.9' testRuntime 'org.postgresql:postgresql:42.2.6' - testRuntime 'mysql:mysql-connector-java:8.0.16' + testRuntime 'mysql:mysql-connector-java:8.0.17' } From 2d11d80034c47f6d6538e036e77f50c0ede98018 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2019 10:56:46 +0000 Subject: [PATCH 0833/5615] Bump mysql-connector-java in /modules/junit-jupiter (#1658) --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 198d67764c7..9c668a208a1 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -14,7 +14,7 @@ dependencies { } testRuntime 'org.postgresql:postgresql:42.2.6' - testRuntime 'mysql:mysql-connector-java:8.0.16' + testRuntime 'mysql:mysql-connector-java:8.0.17' testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.5.1' } From 5cbf6d556166b56ef9c59c859f748444023b236a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 30 Jul 2019 06:05:06 +0000 Subject: [PATCH 0834/5615] Bump assertj-core from 3.12.2 to 3.13.1 in /modules/kafka (#1683) --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index 10013a6e11e..f9b94304b98 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -4,6 +4,6 @@ dependencies { compile project(':testcontainers') testCompile 'org.apache.kafka:kafka-clients:2.3.0' - testCompile 'org.assertj:assertj-core:3.12.2' + testCompile 'org.assertj:assertj-core:3.13.1' testCompile 'com.google.guava:guava:23.0' } From 56499c7291ca7dcdc3d7f4e64bfd77463f570747 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 30 Jul 2019 07:57:40 +0000 Subject: [PATCH 0835/5615] Bump aws-java-sdk-sqs from 1.11.599 to 1.11.601 in /modules/localstack (#1682) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 5481ea1696c..ecb7ecd4345 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.599' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.601' } From aeb1ebf4e37dc519890279c53739695801cad8e5 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 30 Jul 2019 10:37:48 +0000 Subject: [PATCH 0836/5615] Bump assertj-core from 3.12.2 to 3.13.1 in /modules/neo4j (#1681) --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 1ea0ab3fb23..5eaae90325f 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -30,5 +30,5 @@ dependencies { compile project(":testcontainers") testCompile "org.neo4j.driver:neo4j-java-driver:1.7.5" - testCompile 'org.assertj:assertj-core:3.12.2' + testCompile 'org.assertj:assertj-core:3.13.1' } From fa324f5072485e899f9eff7cf2d241aea8465976 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 30 Jul 2019 11:47:25 +0000 Subject: [PATCH 0837/5615] Bump cucumber-junit from 4.6.0 to 4.7.1 in /examples (#1676) --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 08626f9e257..069e0a8881a 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' implementation 'io.cucumber:cucumber-java:4.7.1' - testImplementation 'io.cucumber:cucumber-junit:4.6.0' + testImplementation 'io.cucumber:cucumber-junit:4.7.1' testImplementation 'org.testcontainers:selenium' } From 0605014353a680f1efed7f6d663feab2cf65ad6d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 30 Jul 2019 12:36:33 +0000 Subject: [PATCH 0838/5615] Bump assertj-core from 3.12.2 to 3.13.1 in /modules/pulsar (#1684) --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 301d6f81da2..b493976c9d6 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -4,5 +4,5 @@ dependencies { compile project(':testcontainers') testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.3.2' - testCompile group: 'org.assertj', name: 'assertj-core', version: '3.12.2' + testCompile group: 'org.assertj', name: 'assertj-core', version: '3.13.1' } From ba96952e1a53cdfbf58e9781463adbbccf951f30 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 30 Jul 2019 13:22:19 +0000 Subject: [PATCH 0839/5615] Bump jedis from 3.0.1 to 3.1.0 in /modules/junit-jupiter (#1657) --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 9c668a208a1..5710b48fd15 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -7,7 +7,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') testCompile 'com.zaxxer:HikariCP:3.3.1' - testCompile 'redis.clients:jedis:3.0.1' + testCompile 'redis.clients:jedis:3.1.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.9' testCompile ('org.mockito:mockito-core:2.28.2') { exclude(module: 'hamcrest-core') From ec00eaf93ac63cd574ab69331d1621470624f8b8 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 30 Jul 2019 15:20:04 +0000 Subject: [PATCH 0840/5615] Bump assertj-core from 3.12.2 to 3.13.1 in /modules/database-commons (#1685) --- modules/database-commons/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/database-commons/build.gradle b/modules/database-commons/build.gradle index 27d1aeeb5e6..a7ccab63fa9 100644 --- a/modules/database-commons/build.gradle +++ b/modules/database-commons/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: Database-Commons" dependencies { compile project(':testcontainers') - testCompile 'org.assertj:assertj-core:3.12.2' + testCompile 'org.assertj:assertj-core:3.13.1' } From f7e0eedd64843ada2afe61c67117b3557c190fd9 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 30 Jul 2019 16:55:09 +0000 Subject: [PATCH 0841/5615] Bump jedis from 3.0.1 to 3.1.0 in /examples (#1686) --- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 5075c10e7df..6446bc43497 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.26' - implementation 'redis.clients:jedis:3.0.1' + implementation 'redis.clients:jedis:3.1.0' implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 692841ae7d7..f9239984a9a 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.26' - implementation 'redis.clients:jedis:3.0.1' + implementation 'redis.clients:jedis:3.1.0' implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 0a1789c944a..3c73816f578 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { - implementation 'redis.clients:jedis:3.0.1' + implementation 'redis.clients:jedis:3.1.0' implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.26' From cf7a0499dec246c854387694da8c74e3e81e1855 Mon Sep 17 00:00:00 2001 From: William Delanoue Date: Fri, 2 Aug 2019 21:22:50 +0200 Subject: [PATCH 0842/5615] Update copyright dates (#1691) --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8f18b9edd6d..317bc0c749d 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,10 @@ See [LICENSE](LICENSE). ## Copyright -Copyright (c) 2015 - 2018 Richard North and other authors. +Copyright (c) 2015 - 2019 Richard North and other authors. -MS SQL Server module is (c) 2017 - 2018 G DATA Software AG and other authors. +MS SQL Server module is (c) 2017 - 2019 G DATA Software AG and other authors. -Hashicorp Vault module is (c) 2017 Capital One Services, LLC and other authors. +Hashicorp Vault module is (c) 2017 - 2019 Capital One Services, LLC and other authors. See [contributors](https://github.com/testcontainers/testcontainers-java/graphs/contributors) for all contributors. From 9ce2d4a94397fe4ec1940073dd5721deca644713 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 6 Aug 2019 06:03:01 +0000 Subject: [PATCH 0843/5615] Bump aws-java-sdk-sqs from 1.11.601 to 1.11.604 in /modules/localstack (#1709) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index ecb7ecd4345..8d91198c27b 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.601' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.604' } From 42fd8c7edcf80a8ce7284b5876994967739aa996 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 6 Aug 2019 08:42:23 +0000 Subject: [PATCH 0844/5615] Bump assertj-core from 3.13.1 to 3.13.2 in /modules/pulsar (#1703) --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index b493976c9d6..d0d89d3666c 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -4,5 +4,5 @@ dependencies { compile project(':testcontainers') testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.3.2' - testCompile group: 'org.assertj', name: 'assertj-core', version: '3.13.1' + testCompile group: 'org.assertj', name: 'assertj-core', version: '3.13.2' } From 8042f9c40b89b66bce2b50f50386d6cf60d015d8 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 6 Aug 2019 11:22:46 +0100 Subject: [PATCH 0845/5615] Add Maven Central as a fallback for dependency download (#1710) To reduce flaky CI builds when JCenter is experiencing a blip --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index 235c1ae22f1..988844f4c90 100644 --- a/build.gradle +++ b/build.gradle @@ -51,6 +51,7 @@ subprojects { repositories { jcenter() + mavenCentral() } shadowJar { From bdb82b76a86b57a9789731430cddf3858f41388a Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 6 Aug 2019 12:38:37 +0100 Subject: [PATCH 0846/5615] JUnit rule for flaky test retry (#1680) --- build.gradle | 40 ++-- core/build.gradle | 2 +- .../junit/GenericContainerRuleTest.java | 27 +-- modules/couchbase/build.gradle | 2 + .../couchbase/BaseCouchbaseContainerTest.java | 9 + modules/jdbc-test/build.gradle | 1 + .../jdbc/DatabaseDriverTmpfsTest.java | 14 +- settings.gradle | 2 + test-support/build.gradle | 4 + .../org/testcontainers/testsupport/Flaky.java | 34 ++++ .../testsupport/FlakyTestJUnit4RetryRule.java | 99 +++++++++ .../testsupport/FlakyRuleTest.java | 190 ++++++++++++++++++ .../src/test/resources/logback-test.xml | 16 ++ 13 files changed, 394 insertions(+), 46 deletions(-) create mode 100644 test-support/build.gradle create mode 100644 test-support/src/main/java/org/testcontainers/testsupport/Flaky.java create mode 100644 test-support/src/main/java/org/testcontainers/testsupport/FlakyTestJUnit4RetryRule.java create mode 100644 test-support/src/test/java/org/testcontainers/testsupport/FlakyRuleTest.java create mode 100644 test-support/src/test/resources/logback-test.xml diff --git a/build.gradle b/build.gradle index 988844f4c90..b3e96b265b5 100644 --- a/build.gradle +++ b/build.gradle @@ -12,8 +12,6 @@ subprojects { apply plugin: 'idea' apply plugin: 'io.franzbecker.gradle-lombok' apply plugin: 'com.github.johnrengelman.shadow' - apply from: "$rootDir/gradle/publishing.gradle" - apply from: "$rootDir/gradle/bintray.gradle" group = "org.testcontainers" @@ -35,9 +33,29 @@ subprojects { } } - project.tasks.sourceJar.from(delombok) + repositories { + jcenter() + mavenCentral() + } + + // specific modules should be excluded from publication + if ( ! ["test-support", "jdbc-test", "docs-examples"].contains(it.name) ) { + apply from: "$rootDir/gradle/publishing.gradle" + apply from: "$rootDir/gradle/bintray.gradle" + + project.tasks.sourceJar.from(delombok) + + publishing { + publications { + mavenJava(MavenPublication) { publication -> + artifacts.removeAll { it.classifier == null } + artifact project.tasks.shadowJar + } + } + } - task release(dependsOn: bintrayUpload) + task release(dependsOn: bintrayUpload) + } test { defaultCharacterEncoding = "UTF-8" @@ -49,11 +67,6 @@ subprojects { } } - repositories { - jcenter() - mavenCentral() - } - shadowJar { configurations = [] classifier = null @@ -87,15 +100,6 @@ subprojects { } } - publishing { - publications { - mavenJava(MavenPublication) { publication -> - artifacts.removeAll { it.classifier == null } - artifact project.tasks.shadowJar - } - } - } - dependencies { testCompile 'ch.qos.logback:logback-classic:1.2.3' } diff --git a/core/build.gradle b/core/build.gradle index 545780c0bc7..42c2dcc5794 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -113,7 +113,7 @@ dependencies { testCompile files('testlib/repo/fakejar/fakejar/0/fakejar-0.jar') testCompile 'org.assertj:assertj-core:3.12.2' - + testCompile project(':test-support') jarFileTestCompileOnly "org.projectlombok:lombok:${lombok.version}" jarFileTestCompile 'junit:junit:4.12' diff --git a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java index 6bbd71961a6..6c51713b6b1 100644 --- a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java +++ b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java @@ -14,6 +14,7 @@ import org.rnorth.ducttape.unreliables.Unreliables; import org.testcontainers.containers.Container; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import org.testcontainers.utility.Base58; import org.testcontainers.utility.TestEnvironment; @@ -128,29 +129,6 @@ public static void setupContent() throws FileNotFoundException { .withExtraHost("somehost", "192.168.1.10") .withCommand("/bin/sh", "-c", "while true; do cat /etc/hosts | nc -l -p 80; done"); -// @Test -// public void simpleRedisTest() { -// String ipAddress = redis.getContainerIpAddress(); -// Integer port = redis.getMappedPort(REDIS_PORT); -// -// // Use Redisson to obtain a List that is backed by Redis -// Config redisConfig = new Config(); -// redisConfig.useSingleServer().setAddress(ipAddress + ":" + port); -// -// Redisson redisson = Redisson.create(redisConfig); -// -// List testList = redisson.getList("test"); -// testList.add("foo"); -// testList.add("bar"); -// testList.add("baz"); -// -// List testList2 = redisson.getList("test"); -// assertEquals("The list contains the expected number of items (redis is working!)", 3, testList2.size()); -// assertTrue("The list contains an item that was put in (redis is working!)", testList2.contains("foo")); -// assertTrue("The list contains an item that was put in (redis is working!)", testList2.contains("bar")); -// assertTrue("The list contains an item that was put in (redis is working!)", testList2.contains("baz")); -// } - @Test public void testIsRunning() { try (GenericContainer container = new GenericContainer().withCommand("top")) { @@ -404,7 +382,8 @@ public void addExposedPortAfterWithExposedPortsTest() { @Test public void sharedMemorySetTest() { try (GenericContainer containerWithSharedMemory = new GenericContainer() - .withSharedMemorySize(42L * FileUtils.ONE_MB)) { + .withSharedMemorySize(42L * FileUtils.ONE_MB) + .withStartupCheckStrategy(new OneShotStartupCheckStrategy())) { containerWithSharedMemory.start(); diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 6635462818f..753e93adb75 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -3,4 +3,6 @@ description = "Testcontainers :: Couchbase" dependencies { compile project(':testcontainers') compile 'com.couchbase.client:java-client:2.7.7' + + testCompile project(':test-support') } diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java index 845d9bfaf5e..1c5e702727a 100644 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java @@ -9,7 +9,10 @@ import com.couchbase.client.java.view.View; import com.google.common.collect.Lists; import org.junit.Assert; +import org.junit.Rule; import org.junit.Test; +import org.testcontainers.testsupport.Flaky; +import org.testcontainers.testsupport.FlakyTestJUnit4RetryRule; import java.util.List; @@ -25,7 +28,11 @@ public abstract class BaseCouchbaseContainerTest extends AbstractCouchbaseTest { private static final String DOCUMENT = "{\"name\":\"toto\"}"; + @Rule + public FlakyTestJUnit4RetryRule retry = new FlakyTestJUnit4RetryRule(); + @Test + @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1453", reviewDate = "2019-10-01") public void shouldInsertDocument() { RawJsonDocument expected = RawJsonDocument.create(ID, DOCUMENT); getBucket().upsert(expected); @@ -34,6 +41,7 @@ public void shouldInsertDocument() { } @Test + @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1453", reviewDate = "2019-10-01") public void shouldExecuteN1ql() { getBucket().query(N1qlQuery.simple("INSERT INTO " + TEST_BUCKET + " (KEY, VALUE) VALUES ('" + ID + "', " + DOCUMENT + ")")); @@ -46,6 +54,7 @@ public void shouldExecuteN1ql() { } @Test + @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1453", reviewDate = "2019-10-01") public void shouldCreateView() { View view = DefaultView.create(VIEW_NAME, VIEW_FUNCTION); DesignDocument document = DesignDocument.create(VIEW_NAME, Lists.newArrayList(view)); diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 5874787e9eb..9300ba49a4d 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -28,4 +28,5 @@ dependencies { testCompile 'commons-dbutils:commons-dbutils:1.6' testCompile 'com.googlecode.junit-toolbox:junit-toolbox:2.4' + testCompile project(':test-support') } diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java index 07b70020733..38c97686679 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java @@ -1,18 +1,26 @@ package org.testcontainers.jdbc; +import org.junit.Rule; +import org.junit.Test; +import org.testcontainers.containers.Container; +import org.testcontainers.containers.JdbcDatabaseContainer; +import org.testcontainers.testsupport.Flaky; +import org.testcontainers.testsupport.FlakyTestJUnit4RetryRule; + import java.io.IOException; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; -import org.junit.Test; -import org.testcontainers.containers.Container; -import org.testcontainers.containers.JdbcDatabaseContainer; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; public class DatabaseDriverTmpfsTest { + @Rule + public FlakyTestJUnit4RetryRule retry = new FlakyTestJUnit4RetryRule(); + @Test + @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1687", reviewDate = "2019-10-01") public void tmpfs() throws IOException, InterruptedException, SQLException { final String jdbcUrl = "jdbc:tc:postgresql:9.6.8://hostname/databasename?TC_TMPFS=/testtmpfs:rw"; try (Connection ignored = DriverManager.getConnection(jdbcUrl)) { diff --git a/settings.gradle b/settings.gradle index 272daa5699d..72e7d312555 100644 --- a/settings.gradle +++ b/settings.gradle @@ -41,3 +41,5 @@ file('modules').eachDir { dir -> include "docs-examples" project(":docs-examples").projectDir = file("docs/examples") +include 'test-support' + diff --git a/test-support/build.gradle b/test-support/build.gradle new file mode 100644 index 00000000000..7cc1bb6d1d4 --- /dev/null +++ b/test-support/build.gradle @@ -0,0 +1,4 @@ +dependencies { + implementation 'junit:junit:4.12' + implementation 'org.slf4j:slf4j-api:1.7.26' +} diff --git a/test-support/src/main/java/org/testcontainers/testsupport/Flaky.java b/test-support/src/main/java/org/testcontainers/testsupport/Flaky.java new file mode 100644 index 00000000000..cc5f6f947fa --- /dev/null +++ b/test-support/src/main/java/org/testcontainers/testsupport/Flaky.java @@ -0,0 +1,34 @@ +package org.testcontainers.testsupport; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Annotation for test methods that should be retried in the event of failure. See {@link FlakyTestJUnit4RetryRule} for + * more details. + */ +@Retention(RUNTIME) +@Target({METHOD}) +public @interface Flaky { + + /** + * @return a URL for a GitHub issue where this flaky test can be discussed, and where actions to resolve it can be + * coordinated. + */ + String githubIssueUrl(); + + /** + * @return a date at which this should be reviewed, in {@link java.time.format.DateTimeFormatter#ISO_LOCAL_DATE} + * format (e.g. {@code 2020-12-03}). Now + 3 months is suggested. Once this date has passed, retries will no longer + * be applied. + */ + String reviewDate(); + + /** + * @return the total number of times to try running this test (default 3) + */ + int maxTries() default 3; +} diff --git a/test-support/src/main/java/org/testcontainers/testsupport/FlakyTestJUnit4RetryRule.java b/test-support/src/main/java/org/testcontainers/testsupport/FlakyTestJUnit4RetryRule.java new file mode 100644 index 00000000000..aa1a3ed3c5f --- /dev/null +++ b/test-support/src/main/java/org/testcontainers/testsupport/FlakyTestJUnit4RetryRule.java @@ -0,0 +1,99 @@ +package org.testcontainers.testsupport; + +import lombok.extern.slf4j.Slf4j; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.MultipleFailureException; +import org.junit.runners.model.Statement; + +import java.time.LocalDate; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.List; + +/** + *

+ * JUnit 4 @Rule that implements retry for flaky tests (tests that suffer from sporadic random failures). + *

+ *

+ * This rule should be used in conjunction with the @{@link Flaky} annotation. When this Rule is applied to a test + * class, any test method with this annotation will be invoked up to 3 times or until it succeeds. + *

+ *

+ * Tests should not be marked @{@link Flaky} for a long period of time. Every usage should be + * accompanied by a GitHub issue URL, and should be subject to review at a suitable point in the (near) future. + * Should the review date pass without the test's instability being fixed, the retry behaviour will cease to have an + * effect and the test will be allowed to sporadically fail again. + *

+ */ +@Slf4j +public class FlakyTestJUnit4RetryRule implements TestRule { + + @Override + public Statement apply(Statement base, Description description) { + + final Flaky annotation = description.getAnnotation(Flaky.class); + + if (annotation == null) { + // leave the statement as-is + return base; + } + + if (annotation.githubIssueUrl().trim().length() == 0) { + throw new IllegalArgumentException("A GitHub issue URL must be set for usages of the @Flaky annotation"); + } + + final int maxTries = annotation.maxTries(); + + if (maxTries < 1) { + throw new IllegalArgumentException("@Flaky annotation maxTries must be at least one"); + } + + final LocalDate reviewDate; + try { + reviewDate = LocalDate.parse(annotation.reviewDate()); + } catch (DateTimeParseException e) { + throw new IllegalArgumentException("@Flaky reviewDate could not be parsed. Please provide a date in yyyy-mm-dd format"); + } + + // the annotation should only have an effect before the review date, to encourage review and resolution + if ( LocalDate.now().isBefore(reviewDate) ) { + return new RetryingStatement(base, description, maxTries); + } else { + return base; + } + } + + private static class RetryingStatement extends Statement { + private final Statement base; + private final Description description; + private final int maxTries; + + RetryingStatement(Statement base, Description description, int maxTries) { + this.base = base; + this.description = description; + this.maxTries = maxTries; + } + + @Override + public void evaluate() { + + int attempts = 0; + final List causes = new ArrayList<>(); + + while (++attempts <= maxTries) { + try { + base.evaluate(); + return; + } catch (Throwable throwable) { + log.warn("Retrying @Flaky-annotated test: {}", description.getDisplayName()); + causes.add(throwable); + } + } + + throw new IllegalStateException( + "@Flaky-annotated test failed despite retries.", + new MultipleFailureException(causes)); + } + } +} diff --git a/test-support/src/test/java/org/testcontainers/testsupport/FlakyRuleTest.java b/test-support/src/test/java/org/testcontainers/testsupport/FlakyRuleTest.java new file mode 100644 index 00000000000..07b0ffe1fa8 --- /dev/null +++ b/test-support/src/test/java/org/testcontainers/testsupport/FlakyRuleTest.java @@ -0,0 +1,190 @@ +package org.testcontainers.testsupport; + +import org.junit.Test; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +import java.lang.annotation.Annotation; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class FlakyRuleTest { + + private static final String VALID_URL = "http://some.url/here"; + private static final String INVALID_URL = ""; + + private static final String VALID_DATE_IN_FAR_FUTURE = "2063-04-05"; + private static final String VALID_DATE_IN_PAST = "1991-08-16"; + private static final String INVALID_DATE = "91-01-45"; + private static final int DEFAULT_TRIES = 3; + + private FlakyTestJUnit4RetryRule rule = new FlakyTestJUnit4RetryRule(); + + @Test + public void testIgnoresMethodWithoutAnnotation() throws Throwable { + final Description description = newDescriptionWithoutAnnotation(); + final DummyStatement statement = newStatement(3); + try { + rule.apply(statement, description).evaluate(); + fail("Should not reach here"); + } catch (Exception ignored) { + + } + assertEquals("The statement should only be invoked once, even if it throws", 1, statement.invocationCount); + } + + @Test + public void testRetriesMethodWithAnnotationUntilFailure() throws Throwable { + final Description description = newDescriptionWithAnnotation(); + + final DummyStatement statement = newStatement(3); + try { + rule.apply(statement, description).evaluate(); + fail("Should not reach here"); + } catch (Exception ignored) { + + } + assertEquals("The statement should be invoked three times", 3, statement.invocationCount); + } + + @Test + public void testCustomRetryCount() throws Throwable { + final Description description = newDescriptionWithAnnotationAndCustomTries(10); + + final DummyStatement statement = newStatement(10); + try { + rule.apply(statement, description).evaluate(); + fail("Should not reach here"); + } catch (Exception ignored) { + + } + assertEquals("The statement should be invoked ten times", 10, statement.invocationCount); + } + + @Test + public void testRetriesMethodWithAnnotationUntilSuccess() throws Throwable { + final Description description = newDescriptionWithAnnotation(); + + final DummyStatement statement = newStatement(2); + + rule.apply(statement, description).evaluate(); + + assertEquals("The statement should be invoked three times, and succeed the third time", 3, statement.invocationCount); + } + + @Test + public void testDoesNotRetryMethodWithAnnotationIfNotThrowing() throws Throwable { + final Description description = newDescriptionWithAnnotation(); + + final DummyStatement statement = newStatement(0); + + rule.apply(statement, description).evaluate(); + + assertEquals("The statement should be invoked once", 1, statement.invocationCount); + } + + @Test + public void testTreatsExpiredAnnotationAsNoAnnotation() throws Throwable { + final Description description = newDescriptionWithExpiredAnnotation(); + final DummyStatement statement = newStatement(3); + try { + rule.apply(statement, description).evaluate(); + fail("Should not reach here"); + } catch (Exception ignored) { + + } + assertEquals("The statement should only be invoked once, even if it throws", 1, statement.invocationCount); + } + + @Test + public void testThrowsOnInvalidDateFormat() throws Throwable { + final Description description = newDescriptionWithAnnotation(INVALID_DATE, VALID_URL); + final DummyStatement statement = newStatement(3); + try { + rule.apply(statement, description).evaluate(); + fail("Should not reach here"); + } catch (IllegalArgumentException ignored) { + + } + assertEquals("The statement should not be invoked if the annotation is invalid", 0, statement.invocationCount); + } + + @Test + public void testThrowsOnInvalidGitHubUrl() throws Throwable { + final Description description = newDescriptionWithAnnotation(VALID_DATE_IN_FAR_FUTURE, INVALID_URL); + final DummyStatement statement = newStatement(3); + try { + rule.apply(statement, description).evaluate(); + fail("Should not reach here"); + } catch (IllegalArgumentException ignored) { + + } + assertEquals("The statement should not be invoked if the annotation is invalid", 0, statement.invocationCount); + } + + private Description newDescriptionWithAnnotation(String reviewDate, String gitHubUrl) { + return Description.createTestDescription("SomeTestClass", "someMethod", newAnnotation(reviewDate, gitHubUrl, DEFAULT_TRIES)); + } + + private Description newDescriptionWithoutAnnotation() { + return Description.createTestDescription("SomeTestClass", "someMethod"); + } + + private Description newDescriptionWithAnnotation() { + return Description.createTestDescription("SomeTestClass", "someMethod", newAnnotation(VALID_DATE_IN_FAR_FUTURE, VALID_URL, DEFAULT_TRIES)); + } + + private Description newDescriptionWithAnnotationAndCustomTries(int maxTries) { + return Description.createTestDescription("SomeTestClass", "someMethod", newAnnotation(VALID_DATE_IN_FAR_FUTURE, VALID_URL, maxTries)); + } + + private Description newDescriptionWithExpiredAnnotation() { + return Description.createTestDescription("SomeTestClass", "someMethod", newAnnotation(VALID_DATE_IN_PAST, VALID_URL, DEFAULT_TRIES)); + } + + private Flaky newAnnotation(final String reviewDate, String gitHubUrl, int tries) { + return new Flaky() { + @Override + public Class annotationType() { + return Flaky.class; + } + + @Override + public String githubIssueUrl() { + return gitHubUrl; + } + + @Override + public String reviewDate() { + return reviewDate; + } + + @Override + public int maxTries() { + return tries; + } + }; + } + + private DummyStatement newStatement(int timesToThrow) { + final DummyStatement statement = new DummyStatement(); + statement.shouldThrowTimes = timesToThrow; + return statement; + } + + private static class DummyStatement extends Statement { + int shouldThrowTimes = -1; + int invocationCount = 0; + + @Override + public void evaluate() { + invocationCount++; + + if (shouldThrowTimes > 0) { + shouldThrowTimes--; + throw new RuntimeException(); + } + } + } +} diff --git a/test-support/src/test/resources/logback-test.xml b/test-support/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..535e406fc13 --- /dev/null +++ b/test-support/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + From fbf50922adf98716835b6bb5c8ad9804788c2394 Mon Sep 17 00:00:00 2001 From: Michael Irwin Date: Tue, 6 Aug 2019 11:08:13 -0400 Subject: [PATCH 0847/5615] Add docker-compose.yml to simplify local doc development (#1653) Co-Authored-By: Richard North --- docker-compose.yml | 11 +++++++++++ docs/contributing_docs.md | 6 +++++- 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 docker-compose.yml diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000000..822956457f7 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,11 @@ +version: "3.7" + +services: + docs: + image: python:3.6 + command: sh -c "pip install -r requirements.txt && mkdocs serve -a 0.0.0.0:8000" + working_dir: /docs + volumes: + - ./:/docs + ports: + - 8000:8000 diff --git a/docs/contributing_docs.md b/docs/contributing_docs.md index 294ae54e0de..9ed4b08169c 100644 --- a/docs/contributing_docs.md +++ b/docs/contributing_docs.md @@ -9,7 +9,11 @@ We publish our documentation using Netlify. ## Previewing rendered content -### Previewing docs locally +### Using Docker locally + +The root of the project contains a `docker-compose.yml` file. Simply run `docker-compose up` and then access the docs at [http://localhost:8000](http://localhost:8000). + +### Using Python locally * Ensure that you have Python 3.6.0 or higher. * Set up a virtualenv and run `pip install -r requirements.txt` in the `testcontainers-java` root directory. From 28b2032afa97c9e8e2787d3d771a4342574c97a0 Mon Sep 17 00:00:00 2001 From: Graeme Rocher Date: Wed, 7 Aug 2019 22:02:20 +0200 Subject: [PATCH 0848/5615] Add Micronaut Data to who is using (#1717) --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index 89e70611f37..657c2446a55 100644 --- a/docs/index.md +++ b/docs/index.md @@ -73,6 +73,7 @@ You can also [check the latest version available on Maven Central](https://searc * [Lightbend](https://www.lightbend.com/) - Testing [Alpakka Kafka](https://doc.akka.io/docs/alpakka-kafka/current/) and support in [Alpakka Kafka Testkit](https://doc.akka.io/docs/alpakka-kafka/current/testing.html#testing-with-kafka-in-docker) * [Zalando SE](https://corporate.zalando.com/en) - Testing core business services * [Europace AG](https://tech.europace.de/) - Integration testing for databases and micro services +* [Micronaut Data](https://github.com/micronaut-projects/micronaut-data/) - Testing of Micronaut Data JDBC, a database access toolkit ## License From 4d1ba9e7095483c0636138aff7831935a8b8e5d9 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 10 Aug 2019 20:33:33 +0100 Subject: [PATCH 0849/5615] =?UTF-8?q?Amend=20container=20shutdown=20to=20c?= =?UTF-8?q?atch=20and=20log=20for=20all=20Exception=20cla=E2=80=A6=20(#166?= =?UTF-8?q?3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Amend container shutdown to catch and log for all Exception classes Replacement for #1420 to fix incomplete error handling Refs #1379 * Log root cause exception message rather than immediate exception's message --- .../utility/ResourceReaper.java | 40 ++++++++----------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index 086cac83206..53f7693149b 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -2,9 +2,6 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.InspectContainerResponse; -import com.github.dockerjava.api.exception.ConflictException; -import com.github.dockerjava.api.exception.DockerException; -import com.github.dockerjava.api.exception.InternalServerErrorException; import com.github.dockerjava.api.exception.NotFoundException; import com.github.dockerjava.api.model.Bind; import com.github.dockerjava.api.model.ExposedPort; @@ -13,6 +10,7 @@ import com.github.dockerjava.api.model.Ports; import com.github.dockerjava.api.model.Volume; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Throwables; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; @@ -218,12 +216,14 @@ private void stopContainer(String containerId, String imageName) { boolean running; try { InspectContainerResponse containerInfo = dockerClient.inspectContainerCmd(containerId).exec(); - running = containerInfo.getState().getRunning(); + running = containerInfo.getState() != null && Boolean.TRUE.equals(containerInfo.getState().getRunning()); } catch (NotFoundException e) { LOGGER.trace("Was going to stop container but it apparently no longer exists: {}", containerId); return; - } catch (DockerException e) { - LOGGER.trace("Error encountered when checking container for shutdown (ID: {}) - it may not have been stopped, or may already be stopped: {}", containerId, e.getMessage()); + } catch (Exception e) { + LOGGER.trace("Error encountered when checking container for shutdown (ID: {}) - it may not have been stopped, or may already be stopped. Root cause: {}", + containerId, + Throwables.getRootCause(e).getMessage()); return; } @@ -232,34 +232,28 @@ private void stopContainer(String containerId, String imageName) { LOGGER.trace("Stopping container: {}", containerId); dockerClient.killContainerCmd(containerId).exec(); LOGGER.trace("Stopped container: {}", imageName); - } catch (ConflictException e) { - if ( e.getMessage().startsWith("Cannot kill container: ") && e.getMessage().endsWith(containerId + " is not running")) { - LOGGER.trace("Was going to kill the container but it apparently already dead : {}", e.getMessage()); - } else { - throw e; - } - } catch (DockerException e) { - LOGGER.trace("Error encountered shutting down container (ID: {}) - it may not have been stopped, or may already be stopped: {}", containerId, e.getMessage()); + } catch (Exception e) { + LOGGER.trace("Error encountered shutting down container (ID: {}) - it may not have been stopped, or may already be stopped. Root cause: {}", + containerId, + Throwables.getRootCause(e).getMessage()); } } try { dockerClient.inspectContainerCmd(containerId).exec(); - } catch (NotFoundException e) { + } catch (Exception e) { LOGGER.trace("Was going to remove container but it apparently no longer exists: {}", containerId); return; } try { LOGGER.trace("Removing container: {}", containerId); - try { - dockerClient.removeContainerCmd(containerId).withRemoveVolumes(true).withForce(true).exec(); - LOGGER.debug("Removed container and associated volume(s): {}", imageName); - } catch (InternalServerErrorException e) { - LOGGER.trace("Exception when removing container with associated volume(s): {} (due to {})", imageName, e.getMessage()); - } - } catch (DockerException e) { - LOGGER.trace("Error encountered shutting down container (ID: {}) - it may not have been stopped, or may already be stopped: {}", containerId, e.getMessage()); + dockerClient.removeContainerCmd(containerId).withRemoveVolumes(true).withForce(true).exec(); + LOGGER.debug("Removed container and associated volume(s): {}", imageName); + } catch (Exception e) { + LOGGER.trace("Error encountered shutting down container (ID: {}) - it may not have been stopped, or may already be stopped. Root cause: {}", + containerId, + Throwables.getRootCause(e).getMessage()); } } From e3dffc2a88adceb3a954978b3e6b61db5b9cd103 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2019 06:42:25 +0000 Subject: [PATCH 0850/5615] Bump aws-java-sdk-sqs from 1.11.604 to 1.11.608 in /modules/localstack (#1728) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 8d91198c27b..a943a47d4d0 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.604' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.608' } From 032495ad49ff55d1b1b0546f420e2909e4fd26d5 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2019 08:56:02 +0000 Subject: [PATCH 0851/5615] Bump assertj-core from 3.13.1 to 3.13.2 in /modules/database-commons (#1705) --- modules/database-commons/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/database-commons/build.gradle b/modules/database-commons/build.gradle index a7ccab63fa9..7c820909d61 100644 --- a/modules/database-commons/build.gradle +++ b/modules/database-commons/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: Database-Commons" dependencies { compile project(':testcontainers') - testCompile 'org.assertj:assertj-core:3.13.1' + testCompile 'org.assertj:assertj-core:3.13.2' } From 6a90c2f8bfef66585eca0240bcd6f6bb67c666a6 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2019 10:25:15 +0000 Subject: [PATCH 0852/5615] Bump assertj-core from 3.13.1 to 3.13.2 in /modules/neo4j (#1708) --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 5eaae90325f..c7227a2d9ac 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -30,5 +30,5 @@ dependencies { compile project(":testcontainers") testCompile "org.neo4j.driver:neo4j-java-driver:1.7.5" - testCompile 'org.assertj:assertj-core:3.13.1' + testCompile 'org.assertj:assertj-core:3.13.2' } From 7534938bdb0bb5e9d314c0da6aa64a692b7709fc Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2019 12:30:17 +0000 Subject: [PATCH 0853/5615] Bump pulsar-client from 2.3.2 to 2.4.0 in /modules/pulsar (#1729) --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index d0d89d3666c..34cde96a33e 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Pulsar" dependencies { compile project(':testcontainers') - testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.3.2' + testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.4.0' testCompile group: 'org.assertj', name: 'assertj-core', version: '3.13.2' } From 5f154008ad80f3f109241b3232d511ec69b5c5a5 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2019 14:52:55 +0000 Subject: [PATCH 0854/5615] Bump slf4j-api from 1.7.26 to 1.7.28 in /examples (#1730) --- examples/linked-container/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index ce0eff58a28..6bf099030ec 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -6,7 +6,7 @@ repositories { jcenter() } dependencies { - compileOnly 'org.slf4j:slf4j-api:1.7.26' + compileOnly 'org.slf4j:slf4j-api:1.7.28' implementation 'com.squareup.okhttp3:okhttp:3.14.2' implementation 'org.json:json:20180813' testImplementation 'org.postgresql:postgresql:42.2.6' diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 6446bc43497..777ca2d9511 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:1.7.26' + compileOnly 'org.slf4j:slf4j-api:1.7.28' implementation 'redis.clients:jedis:3.1.0' implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.google.guava:guava:23.0' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index f9239984a9a..8b1a5638243 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:1.7.26' + compileOnly 'org.slf4j:slf4j-api:1.7.28' implementation 'redis.clients:jedis:3.1.0' implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.google.guava:guava:23.0' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 3c73816f578..26a437909fe 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -11,7 +11,7 @@ dependencies { implementation 'redis.clients:jedis:3.1.0' implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.google.guava:guava:23.0' - compileOnly 'org.slf4j:slf4j-api:1.7.26' + compileOnly 'org.slf4j:slf4j-api:1.7.28' testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.testcontainers:testcontainers' From 67672a1b1144beae0254040ea49cb045bc6c77ed Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2019 16:30:24 +0000 Subject: [PATCH 0855/5615] Bump mariadb-java-client from 2.4.2 to 2.4.3 in /modules/mariadb (#1704) --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index a501b44f04d..aa85a5d1f5e 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: JDBC :: MariaDB" dependencies { compile project(':jdbc') - testCompile 'org.mariadb.jdbc:mariadb-java-client:2.4.2' + testCompile 'org.mariadb.jdbc:mariadb-java-client:2.4.3' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.22' From 1693d796fa8976b9112bc775873d542152909e73 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2019 17:41:52 +0000 Subject: [PATCH 0856/5615] Bump assertj-core from 3.13.1 to 3.13.2 in /modules/kafka (#1707) --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index f9b94304b98..9d86482cf2f 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -4,6 +4,6 @@ dependencies { compile project(':testcontainers') testCompile 'org.apache.kafka:kafka-clients:2.3.0' - testCompile 'org.assertj:assertj-core:3.13.1' + testCompile 'org.assertj:assertj-core:3.13.2' testCompile 'com.google.guava:guava:23.0' } From 7a7c2fcd64a4a241241594870c378630465b3158 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2019 06:03:22 +0000 Subject: [PATCH 0857/5615] Bump aws-java-sdk-sqs from 1.11.608 to 1.11.609 in /modules/localstack (#1735) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index a943a47d4d0..fd5ef14c600 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.608' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.609' } From e9e45bcb4cc8dca1e93a63b112e5c9588feddde3 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2019 06:06:21 +0000 Subject: [PATCH 0858/5615] Bump aws-java-sdk-sqs from 1.11.609 to 1.11.610 in /modules/localstack (#1741) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index fd5ef14c600..46b805f2f69 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.609' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.610' } From 983d7eba8ba46b7d9c692d2d28fed17790d0c00a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2019 07:21:09 +0000 Subject: [PATCH 0859/5615] Bump lettuce-core from 5.1.7.RELEASE to 5.1.8.RELEASE in /examples (#1696) --- examples/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spock/build.gradle b/examples/spock/build.gradle index cc04f74c90d..147e1eaf079 100644 --- a/examples/spock/build.gradle +++ b/examples/spock/build.gradle @@ -8,7 +8,7 @@ repositories { } dependencies { - compile "io.lettuce:lettuce-core:5.1.7.RELEASE" + compile "io.lettuce:lettuce-core:5.1.8.RELEASE" testImplementation 'org.spockframework:spock-core:1.2-groovy-2.5' testImplementation 'org.testcontainers:spock' testImplementation 'ch.qos.logback:logback-classic:1.2.3' From d9ca8ef90e6c98f912ab4a099a208b939dcc2497 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2019 09:34:25 +0000 Subject: [PATCH 0860/5615] Bump java-client from 2.7.7 to 2.7.9 in /modules/couchbase (#1714) --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 753e93adb75..063b12c9d8e 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -2,7 +2,7 @@ description = "Testcontainers :: Couchbase" dependencies { compile project(':testcontainers') - compile 'com.couchbase.client:java-client:2.7.7' + compile 'com.couchbase.client:java-client:2.7.9' testCompile project(':test-support') } From 49b526cfd172d43a8c7164cfa3d882d0e5aa4a01 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2019 11:10:41 +0000 Subject: [PATCH 0861/5615] Bump org.springframework.boot in /examples (#1736) --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index d4f3c132896..22198984e33 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.1.6.RELEASE' + id 'org.springframework.boot' version '2.1.7.RELEASE' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index 52767c7a9ca..7af6b9d26da 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.1.6.RELEASE' + id 'org.springframework.boot' version '2.1.7.RELEASE' } apply plugin: 'io.spring.dependency-management' From 9a9684106dafe06af47c85bd076d62f9074ae38e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 15 Aug 2019 06:03:53 +0000 Subject: [PATCH 0862/5615] Bump aws-java-sdk-sqs from 1.11.610 to 1.11.611 in /modules/localstack (#1747) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 46b805f2f69..2acbdff6541 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.610' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.611' } From 828a14b77aef6fcaa05dc6bd5abed07404363205 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 16 Aug 2019 06:00:18 +0000 Subject: [PATCH 0863/5615] Bump aws-java-sdk-sqs from 1.11.611 to 1.11.612 in /modules/localstack (#1750) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 2acbdff6541..3e9eadf36e5 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.611' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.612' } From f65a5902b2d3a123caef497faf7f09867e436d0c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2019 07:17:08 +0000 Subject: [PATCH 0864/5615] Bump tomcat-jdbc from 9.0.22 to 9.0.24 in /modules/mariadb (#1753) --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index aa85a5d1f5e..eb778c6c827 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -6,6 +6,6 @@ dependencies { testCompile 'org.mariadb.jdbc:mariadb-java-client:2.4.3' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.22' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.24' testCompile 'org.vibur:vibur-dbcp:22.2' } From 07154353019f88eee81fa67864bd141ba4956d3b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2019 09:06:53 +0000 Subject: [PATCH 0865/5615] Bump aws-java-sdk-sqs from 1.11.612 to 1.11.613 in /modules/localstack (#1754) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 3e9eadf36e5..6384d7e4d50 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.612' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.613' } From 8689f849209e4808681955488763419e4346f804 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2019 10:53:35 +0000 Subject: [PATCH 0866/5615] Bump tomcat-jdbc from 9.0.22 to 9.0.24 in /modules/mysql (#1755) --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 3647fe11cc6..d5a366825b9 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -5,7 +5,7 @@ dependencies { testCompile 'mysql:mysql-connector-java:8.0.17' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.22' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.24' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.vibur:vibur-dbcp:22.2' } From 22030eace3f4bafc735ccb0e402c1202329a95d1 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 21 Aug 2019 06:18:59 +0000 Subject: [PATCH 0867/5615] Bump aws-java-sdk-sqs from 1.11.613 to 1.11.615 in /modules/localstack (#1762) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 6384d7e4d50..9e7824ef7ce 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.613' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.615' } From 246332d347475fa1f1d3665f9c267e7bb4dd4524 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2019 06:06:34 +0000 Subject: [PATCH 0868/5615] Bump aws-java-sdk-sqs from 1.11.615 to 1.11.616 in /modules/localstack (#1768) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 9e7824ef7ce..d950467e502 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.615' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.616' } From 62d646afbd3aa28da11ce572146b2ebafe71d942 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 22 Aug 2019 14:59:49 +0100 Subject: [PATCH 0869/5615] Reinstate retries for image pulls (#1712) * Reinstate retries for image pulls * Adopt time limit for retries instead of a fixed number of retries * Only retry image pull failures that could potentially succeed next time * Qualify usages of `Instant` and `Duration` --- .../images/RemoteDockerImage.java | 41 +++++++++++++------ 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java index 7aff7918f6d..257bacc9961 100644 --- a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java +++ b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java @@ -3,6 +3,7 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.ListImagesCmd; import com.github.dockerjava.api.exception.DockerClientException; +import com.github.dockerjava.api.exception.InternalServerErrorException; import com.github.dockerjava.api.model.Image; import com.github.dockerjava.core.command.PullImageResultCallback; import lombok.NonNull; @@ -14,6 +15,8 @@ import org.testcontainers.utility.DockerLoggerFactory; import org.testcontainers.utility.LazyFuture; +import java.time.Duration; +import java.time.Instant; import java.util.HashSet; import java.util.List; import java.util.Objects; @@ -29,6 +32,7 @@ public class RemoteDockerImage extends LazyFuture { */ @Deprecated public static final Set AVAILABLE_IMAGE_NAME_CACHE = new HashSet<>(); + private static final Duration PULL_RETRY_TIME_LIMIT = Duration.ofMinutes(2); private DockerImageName imageName; @@ -73,23 +77,34 @@ protected final String resolve() { return imageName.toString(); } + // The image is not available locally - pull it logger.info("Pulling docker image: {}. Please be patient; this may take some time but only needs to be done once.", imageName); - // The image is not available locally - pull it - try { - final PullImageResultCallback callback = new TimeLimitedLoggedPullImageResultCallback(logger); - dockerClient - .pullImageCmd(imageName.getUnversionedPart()) - .withTag(imageName.getVersionPart()) - .exec(callback); - callback.awaitCompletion(); - AVAILABLE_IMAGE_NAME_CACHE.add(imageName); - } catch (Exception e) { - logger.error("Failed to pull image: {}. Please check output of `docker pull {}`", imageName, imageName); - throw new ContainerFetchException("Failed to pull image: " + imageName, e); + Exception lastFailure = null; + final Instant lastRetryAllowed = Instant.now().plus(PULL_RETRY_TIME_LIMIT); + + while (Instant.now().isBefore(lastRetryAllowed)) { + try { + final PullImageResultCallback callback = new TimeLimitedLoggedPullImageResultCallback(logger); + dockerClient + .pullImageCmd(imageName.getUnversionedPart()) + .withTag(imageName.getVersionPart()) + .exec(callback); + callback.awaitCompletion(); + AVAILABLE_IMAGE_NAME_CACHE.add(imageName); + + return imageName.toString(); + } catch (InterruptedException | InternalServerErrorException e) { + // these classes of exception often relate to timeout/connection errors so should be retried + lastFailure = e; + logger.warn("Retrying pull for image: {} ({}s remaining)", + imageName, + Duration.between(Instant.now(), lastRetryAllowed).getSeconds()); + } } + logger.error("Failed to pull image: {}. Please check output of `docker pull {}`", imageName, imageName, lastFailure); - return imageName.toString(); + throw new ContainerFetchException("Failed to pull image: " + imageName, lastFailure); } catch (DockerClientException e) { throw new ContainerFetchException("Failed to get Docker client for " + imageName, e); } From 2d1df1648c41c7dcb3508fae130bbbdf5faf47dd Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 23 Aug 2019 08:43:33 +0100 Subject: [PATCH 0870/5615] Refs #1769: mark toxiproxy tests as flaky pending further analysis (#1772) --- modules/toxiproxy/build.gradle | 2 ++ .../java/org/testcontainers/containers/ToxiproxyTest.java | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/modules/toxiproxy/build.gradle b/modules/toxiproxy/build.gradle index 0d004fd32bf..293b8f8f190 100644 --- a/modules/toxiproxy/build.gradle +++ b/modules/toxiproxy/build.gradle @@ -6,4 +6,6 @@ dependencies { testCompile 'redis.clients:jedis:3.0.1' testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + + testCompile project(':test-support') } diff --git a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java index 34861f68f7a..8fa28431ab5 100644 --- a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java +++ b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java @@ -3,6 +3,8 @@ import eu.rekawek.toxiproxy.model.ToxicDirection; import org.junit.Rule; import org.junit.Test; +import org.testcontainers.testsupport.Flaky; +import org.testcontainers.testsupport.FlakyTestJUnit4RetryRule; import redis.clients.jedis.Jedis; import redis.clients.jedis.exceptions.JedisConnectionException; @@ -30,6 +32,9 @@ public class ToxiproxyTest { .withNetwork(network); // } + @Rule + public FlakyTestJUnit4RetryRule retry = new FlakyTestJUnit4RetryRule(); + @Test public void testDirect() { final Jedis jedis = new Jedis(redis.getContainerIpAddress(), redis.getFirstMappedPort()); @@ -40,6 +45,7 @@ public void testDirect() { } @Test + @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1769", reviewDate = "2019-10-01") public void testLatencyViaProxy() throws IOException { // obtainProxyObject { final ToxiproxyContainer.ContainerProxy proxy = toxiproxy.getProxy(redis, 6379); @@ -66,6 +72,7 @@ public void testLatencyViaProxy() throws IOException { } @Test + @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1769", reviewDate = "2019-10-01") public void testConnectionCut() { final ToxiproxyContainer.ContainerProxy proxy = toxiproxy.getProxy(redis, 6379); final Jedis jedis = new Jedis(proxy.getContainerIpAddress(), proxy.getProxyPort()); @@ -90,6 +97,7 @@ public void testConnectionCut() { } @Test + @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1769", reviewDate = "2019-10-01") public void testMultipleProxiesCanBeCreated() { try (GenericContainer secondRedis = new GenericContainer("redis:5.0.4") .withExposedPorts(6379) From ad5f1f63719c19b01c78746a4efd07eab7ca01c0 Mon Sep 17 00:00:00 2001 From: Evan Gilbert Date: Sun, 25 Aug 2019 10:30:12 -0500 Subject: [PATCH 0871/5615] Exclude jna transitive dependency from visible-assertions to reconcile versions (#1744) --- core/build.gradle | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 42c2dcc5794..c39bb183e82 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -67,7 +67,10 @@ dependencies { exclude(group: 'org.jetbrains', module: 'annotations') } - compile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + compile ('org.rnorth.visible-assertions:visible-assertions:2.1.2') { + // Excluded in favor of jna-platform below + exclude(group: "net.java.dev.jna", module: "jna") + } compile ('org.rnorth:tcp-unix-socket-proxy:1.0.2') { exclude(group: "log4j", module: "log4j") From d5f15774187eac4e83346cb59fb00197f07ac5c4 Mon Sep 17 00:00:00 2001 From: Rob Szewczyk Date: Sun, 25 Aug 2019 11:33:15 -0400 Subject: [PATCH 0872/5615] :bug: Fix credentials when using a helper that returns a token (#1734) --- .../utility/RegistryAuthLocator.java | 14 +++++++---- .../utility/RegistryAuthLocatorTest.java | 9 ++++++++ .../config-with-helper-using-token.json | 11 +++++++++ .../auth-config/docker-credential-fake | 23 +++++++++++++++---- .../win/docker-credential-fake.bat | 23 +++++++++++++++---- 5 files changed, 66 insertions(+), 14 deletions(-) create mode 100644 core/src/test/resources/auth-config/config-with-helper-using-token.json diff --git a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java index 5b248aea04e..e8651167221 100644 --- a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java +++ b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java @@ -251,10 +251,16 @@ private AuthConfig runCredentialProvider(String hostName, String helperOrStoreNa final JsonNode helperResponse = OBJECT_MAPPER.readTree(data); log.debug("Credential helper/store provided auth config for: {}", hostName); - return new AuthConfig() - .withRegistryAddress(helperResponse.at("/ServerURL").asText()) - .withUsername(helperResponse.at("/Username").asText()) - .withPassword(helperResponse.at("/Secret").asText()); + final String username = helperResponse.at("/Username").asText(); + final String password = helperResponse.at("/Secret").asText(); + if ("".equals(username)) { + return new AuthConfig().withIdentityToken(password); + } else { + return new AuthConfig() + .withRegistryAddress(helperResponse.at("/ServerURL").asText()) + .withUsername(username) + .withPassword(password); + } } private String getCredentialProgramName(String credHelper) { diff --git a/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java b/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java index fe930cf78b7..e1cb595e903 100644 --- a/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java +++ b/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java @@ -69,6 +69,15 @@ public void lookupAuthConfigUsingHelper() throws URISyntaxException { assertEquals("Correct secret is obtained from a credential store", "secret", authConfig.getPassword()); } + @Test + public void lookupAuthConfigUsingHelperWithToken() throws URISyntaxException { + final RegistryAuthLocator authLocator = createTestAuthLocator("config-with-helper-using-token.json"); + + final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("registrytoken.example.com/org/repo"), new AuthConfig()); + + assertEquals("Correct identitytoken is obtained from a credential store", "secret", authConfig.getIdentitytoken()); + } + @Test public void lookupUsingHelperEmptyAuth() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-empty-auth-with-helper.json"); diff --git a/core/src/test/resources/auth-config/config-with-helper-using-token.json b/core/src/test/resources/auth-config/config-with-helper-using-token.json new file mode 100644 index 00000000000..63b10a1ef5e --- /dev/null +++ b/core/src/test/resources/auth-config/config-with-helper-using-token.json @@ -0,0 +1,11 @@ +{ + "auths": { + "registrytoken.example.com": {} + }, + "HttpHeaders": { + "User-Agent": "Docker-Client/18.03.0-ce (darwin)" + }, + "credHelpers": { + "registrytoken.example.com": "fake" + } +} diff --git a/core/src/test/resources/auth-config/docker-credential-fake b/core/src/test/resources/auth-config/docker-credential-fake index 0fc0d44621e..880009f2c2a 100755 --- a/core/src/test/resources/auth-config/docker-credential-fake +++ b/core/src/test/resources/auth-config/docker-credential-fake @@ -15,8 +15,21 @@ if [[ $inputLine == "https://not.a.real.registry/url" ]]; then exit 1 fi -echo '{' \ - ' "ServerURL": "url",' \ - ' "Username": "username",' \ - ' "Secret": "secret"' \ - '}' +if [[ $inputLine == "registry.example.com" ]]; then + echo '{' \ + ' "ServerURL": "url",' \ + ' "Username": "username",' \ + ' "Secret": "secret"' \ + '}' + exit 0 +fi +if [[ $inputLine == "registrytoken.example.com" ]]; then + echo '{' \ + ' "ServerURL": "url",' \ + ' "Username": "",' \ + ' "Secret": "secret"' \ + '}' + exit 0 +fi + +exit 1 diff --git a/core/src/test/resources/auth-config/win/docker-credential-fake.bat b/core/src/test/resources/auth-config/win/docker-credential-fake.bat index 7b6b27df5a6..4b248973b48 100644 --- a/core/src/test/resources/auth-config/win/docker-credential-fake.bat +++ b/core/src/test/resources/auth-config/win/docker-credential-fake.bat @@ -14,8 +14,21 @@ if "%inputLine%" == "https://not.a.real.registry/url" ( exit 1 ) -echo { -echo "ServerURL": "url", -echo "Username": "username", -echo "Secret": "secret" -echo } +if "%inputLine%" == "registry.example.com" ( + echo { + echo "ServerURL": "url", + echo "Username": "username", + echo "Secret": "secret" + echo } + exit 0 +) +if "%inputLine%" == "registrytoken.example.com" ( + echo { + echo "ServerURL": "url", + echo "Username": "", + echo "Secret": "secret" + echo } + exit 0 +) + +exit 1 From da97377ff459247156cc39814f6a4a6e9009ce65 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 25 Aug 2019 18:41:56 +0200 Subject: [PATCH 0873/5615] Bump elasticsearch-rest-client in /modules/elasticsearch (#1779) Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 6.7.1 to 7.3.1. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v6.7.1...v7.3.1) Signed-off-by: dependabot-preview[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 699bc54bd9e..6ae9ab00dab 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,6 +2,6 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') - testCompile "org.elasticsearch.client:elasticsearch-rest-client:6.7.1" + testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.3.1" testCompile "org.elasticsearch.client:transport:6.7.1" } From aab9aa06a8cfa2f04d87f08c2098d7bd5b6f0435 Mon Sep 17 00:00:00 2001 From: Andrew Guibert Date: Sun, 25 Aug 2019 14:14:57 -0500 Subject: [PATCH 0874/5615] Coordinate image removals after stopping containers (#1745) --- .../images/builder/ImageFromDockerfile.java | 23 ++----------------- .../utility/ResourceReaper.java | 20 +++++++++++++++- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java index 8c653718a69..98d0dda5e5c 100644 --- a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java +++ b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java @@ -22,6 +22,7 @@ import org.testcontainers.utility.Base58; import org.testcontainers.utility.DockerLoggerFactory; import org.testcontainers.utility.LazyFuture; +import org.testcontainers.utility.ResourceReaper; import java.io.IOException; import java.io.PipedInputStream; @@ -42,26 +43,6 @@ public class ImageFromDockerfile extends LazyFuture implements StringsTrait, DockerfileTrait { - private static final Set imagesToDelete = Sets.newConcurrentHashSet(); - - static { - Runtime.getRuntime().addShutdownHook(new Thread(DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, () -> { - DockerClient dockerClientForCleaning = DockerClientFactory.instance().client(); - try { - for (String dockerImageName : imagesToDelete) { - log.info("Removing image tagged {}", dockerImageName); - try { - dockerClientForCleaning.removeImageCmd(dockerImageName).withForce(true).exec(); - } catch (Throwable e) { - log.warn("Unable to delete image " + dockerImageName, e); - } - } - } catch (DockerClientException e) { - throw new RuntimeException(e); - } - })); - } - private final String dockerImageName; private boolean deleteOnExit = true; @@ -102,7 +83,7 @@ protected final String resolve() { DockerClient dockerClient = DockerClientFactory.instance().client(); try { if (deleteOnExit) { - imagesToDelete.add(dockerImageName); + ResourceReaper.instance().registerImageForCleanup(dockerImageName); } BuildImageResultCallback resultCallback = new BuildImageResultCallback() { diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index 53f7693149b..f46db952c37 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -11,6 +11,8 @@ import com.github.dockerjava.api.model.Volume; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Throwables; +import com.google.common.collect.Sets; + import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; @@ -51,7 +53,8 @@ public final class ResourceReaper { private static ResourceReaper instance; private final DockerClient dockerClient; private Map registeredContainers = new ConcurrentHashMap<>(); - private Set registeredNetworks = Collections.newSetFromMap(new ConcurrentHashMap<>()); + private Set registeredNetworks = Sets.newConcurrentHashSet(); + private Set registeredImages = Sets.newConcurrentHashSet(); private AtomicBoolean hookIsSet = new AtomicBoolean(false); private ResourceReaper() { @@ -164,6 +167,7 @@ public synchronized static ResourceReaper instance() { public synchronized void performCleanup() { registeredContainers.forEach(this::stopContainer); registeredNetworks.forEach(this::removeNetwork); + registeredImages.forEach(this::removeImage); } /** @@ -337,6 +341,20 @@ public void unregisterNetwork(String identifier) { public void unregisterContainer(String identifier) { registeredContainers.remove(identifier); } + + public void registerImageForCleanup(String dockerImageName) { + setHook(); + registeredImages.add(dockerImageName); + } + + private void removeImage(String dockerImageName) { + LOGGER.trace("Removing image tagged {}", dockerImageName); + try { + dockerClient.removeImageCmd(dockerImageName).withForce(true).exec(); + } catch (Throwable e) { + LOGGER.warn("Unable to delete image " + dockerImageName, e); + } + } private void setHook() { if (hookIsSet.compareAndSet(false, true)) { From a4b70413e71b7ea65675de0475e296a2ce102184 Mon Sep 17 00:00:00 2001 From: glebsts Date: Sun, 25 Aug 2019 22:16:28 +0300 Subject: [PATCH 0875/5615] Fix fixed host port test (#1355) * 'fix fixed host port test' - rewrote test using simple echo http container, followed path of original test; * 'fix fixed host port test' - rewrote assertions to hamcrest, failure in response getting now fails test directly with io exc * 'fix fixed host port test' - as suggested by reviewer, rewrote tests to alpine, also copied some routines from generic container tests * 'fix fixed host port test' - no assertions on basic container start, code style updates based on PR --- .../junit/FixedHostPortContainerTest.java | 93 +++++++++++++++---- 1 file changed, 74 insertions(+), 19 deletions(-) diff --git a/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java b/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java index 516fcb8516a..dba48da4db1 100644 --- a/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java @@ -1,10 +1,19 @@ package org.testcontainers.junit; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; + +import com.google.common.util.concurrent.Uninterruptibles; import org.junit.Test; +import org.rnorth.ducttape.unreliables.Unreliables; import org.testcontainers.containers.FixedHostPortGenericContainer; import org.testcontainers.containers.GenericContainer; +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStreamReader; +import java.net.Socket; +import java.util.concurrent.TimeUnit; /** * Test of {@link FixedHostPortGenericContainer}. Note that this is not an example of typical use (usually, a container @@ -14,28 +23,74 @@ */ public class FixedHostPortContainerTest { - private static final int REDIS_PORT = 6379; + private static final String TEST_IMAGE = "alpine:3.2"; + + /** + * Default http server port (just something different from default) + */ + private static final int TEST_PORT = 5678; + + /** + * test response + */ + private static final String TEST_RESPONSE = "test-response"; + + /** + * *nix pipe to fire test response on test port + */ + private static final String HTTP_ECHO_CMD = + String.format("while true; do echo \"%s\" | nc -l -p %d; done", TEST_RESPONSE, TEST_PORT); @Test public void testFixedHostPortMapping() throws IOException { // first find a free port on the docker host that will work for testing - GenericContainer portDiscoveryRedis = new GenericContainer("redis:3.0.2").withExposedPorts(REDIS_PORT); - portDiscoveryRedis.start(); - Integer freePort = portDiscoveryRedis.getMappedPort(REDIS_PORT); - portDiscoveryRedis.stop(); - - - // Set up a FixedHostPortGenericContainer as if this were a @Rule - FixedHostPortGenericContainer redis = new FixedHostPortGenericContainer("redis:3.0.2").withFixedExposedPort(freePort, REDIS_PORT); - redis.start(); - -// Config redisConfig = new Config(); -// redisConfig.useSingleServer().setAddress(redis.getContainerIpAddress() + ":" + freePort); -// Redisson redisson = Redisson.create(redisConfig); -// -// redisson.getBucket("test").set("foo"); -// -// assertEquals("The bucket content was successfully set", "foo", redisson.getBucket("test").get()); -// assertEquals("The container returns the fixed port from getMappedPort(...)", freePort, redis.getMappedPort(REDIS_PORT)); + final Integer unusedHostPort; + try ( + final GenericContainer echoServer = new GenericContainer<>(TEST_IMAGE) + .withExposedPorts(TEST_PORT) + .withCommand("/bin/sh", "-c", HTTP_ECHO_CMD) + ) { + echoServer.start(); + unusedHostPort = echoServer.getMappedPort(TEST_PORT); + } + + // now starting echo server container mapped to known-as-free host port + try ( + final GenericContainer echoServer = new FixedHostPortGenericContainer(TEST_IMAGE) + // using workaround for port bind+expose + .withFixedExposedPort(unusedHostPort, TEST_PORT) + .withExposedPorts(TEST_PORT) + .withCommand("/bin/sh", "-c", HTTP_ECHO_CMD) + ) { + echoServer.start(); + + assertThat("Port mapping does not seem to match given fixed port", + echoServer.getMappedPort(TEST_PORT), equalTo(unusedHostPort)); + + final String content = this.readResponse(echoServer, unusedHostPort); + assertThat("Returned echo from fixed port does not match expected", content, equalTo(TEST_RESPONSE)); + } + } + + /** + * Simple socket content reader from given container:port + * + * @param container to query + * @param port to send request to + * @return socket reader content + * @throws IOException if any + */ + private String readResponse(GenericContainer container, Integer port) throws IOException { + try ( + final BufferedReader reader = Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, + () -> { + Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); + final Socket socket = new Socket(container.getContainerIpAddress(), port); + return new BufferedReader(new InputStreamReader(socket.getInputStream())); + } + ) + ) { + return reader.readLine(); + } } } From 79adf4e5f7f2c511a7ce3f0583326ace4448cd1e Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 25 Aug 2019 20:17:01 +0100 Subject: [PATCH 0876/5615] Disable Travis for all but master branch builds (#1720) --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 0c03c4931bb..f07d50123e2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,5 @@ +if: branch = master + language: java jdk: - oraclejdk8 From 8894daec097aa1e91e434475d4d7edb2f81fdfcd Mon Sep 17 00:00:00 2001 From: Joseph Petersen Date: Sun, 25 Aug 2019 21:34:14 +0200 Subject: [PATCH 0877/5615] add check for compatible Docker OS type (#1780) * add container OSType check for Windows strategies as Windows containers are not supported at the moment * fix typo * generic check for OS Type, as we currently only support Linux containers * use debug logging when checking OS type * words --- .../dockerclient/DockerClientProviderStrategy.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index a9321276fd6..57cccc8b540 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -5,6 +5,7 @@ import com.github.dockerjava.core.DockerClientConfig; import com.google.common.base.Throwables; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.Nullable; import org.rnorth.ducttape.TimeoutException; import org.rnorth.ducttape.ratelimits.RateLimiter; @@ -111,6 +112,7 @@ public static DockerClientProviderStrategy getFirstValidStrategy(List Date: Sun, 25 Aug 2019 22:51:24 +0300 Subject: [PATCH 0878/5615] Fix Deprecated MySQL Class Name (#1760) * Fix #1655 * MySQL driver discovery in MySQLContainer instead of JdbcDatabaseContainer --- .../java/org/testcontainers/containers/MySQLContainer.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java index 172ead4856a..fa2e8626b0d 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java @@ -56,7 +56,12 @@ protected void configure() { @Override public String getDriverClassName() { - return "com.mysql.jdbc.Driver"; + try { + Class.forName("com.mysql.cj.jdbc.Driver"); + return "com.mysql.cj.jdbc.Driver"; + } catch (ClassNotFoundException e) { + return "com.mysql.jdbc.Driver"; + } } @Override From 764ab17217b2fdb67898f9ea00bdfdc7e6b4798b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 25 Aug 2019 22:40:53 +0200 Subject: [PATCH 0879/5615] Bump mockito-core from 2.28.2 to 3.0.0 in /core (#1635) Bumps [mockito-core](https://github.com/mockito/mockito) from 2.28.2 to 3.0.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v2.28.2...v3.0.0) Signed-off-by: dependabot-preview[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index c39bb183e82..9698160c033 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -109,7 +109,7 @@ dependencies { testCompile 'redis.clients:jedis:3.0.1' testCompile 'com.rabbitmq:amqp-client:5.7.2' testCompile 'org.mongodb:mongo-java-driver:3.10.2' - testCompile ('org.mockito:mockito-core:2.28.2') { + testCompile ('org.mockito:mockito-core:3.0.0') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From 7228a2df3cd7a02ad644c072525d3276fd8a25fb Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 25 Aug 2019 22:34:24 +0000 Subject: [PATCH 0880/5615] Bump aws-java-sdk-sqs from 1.11.616 to 1.11.618 in /modules/localstack (#1785) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index d950467e502..897e337ddb5 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.616' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.618' } From e6cd4f51523a1b4e4c5f5821a6fe9bd1081702c3 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 25 Aug 2019 23:53:33 +0000 Subject: [PATCH 0881/5615] Bump selenium-firefox-driver from 3.14.0 to 3.141.59 in /examples (#1748) --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 069e0a8881a..5467783ef92 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.14.0' - implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.14.0' + implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' implementation 'io.cucumber:cucumber-java:4.7.1' testImplementation 'io.cucumber:cucumber-junit:4.7.1' From 6fda0556732087659b24ffa81710bcef3726867f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2019 06:33:51 +0000 Subject: [PATCH 0882/5615] Bump assertj-core from 3.12.2 to 3.13.2 in /core (#1789) --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 9698160c033..2b4c76ee4b1 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -115,7 +115,7 @@ dependencies { // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest testCompile files('testlib/repo/fakejar/fakejar/0/fakejar-0.jar') - testCompile 'org.assertj:assertj-core:3.12.2' + testCompile 'org.assertj:assertj-core:3.13.2' testCompile project(':test-support') jarFileTestCompileOnly "org.projectlombok:lombok:${lombok.version}" From f9fc67147ea68cbaab4c9fcd128c6b2c57588626 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2019 08:36:43 +0100 Subject: [PATCH 0883/5615] Bump snakeyaml from 1.21 to 1.25 in /core (#1742) Bumps [snakeyaml](https://bitbucket.org/asomov/snakeyaml) from 1.21 to 1.25. - [Commits](https://bitbucket.org/asomov/snakeyaml/commits) Signed-off-by: dependabot-preview[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 2b4c76ee4b1..78b60a41de0 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -95,7 +95,7 @@ dependencies { exclude(group: 'org.apache.commons', module: 'commons-compress') } - shaded "org.yaml:snakeyaml:1.21" + shaded "org.yaml:snakeyaml:1.25" shaded 'com.squareup.okhttp3:okhttp:3.14.2' From 7d93069b1f427ed530a2cd465fca58554af81051 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2019 08:37:03 +0100 Subject: [PATCH 0884/5615] Bump jedis from 3.0.1 to 3.1.0 in /core (#1656) Bumps [jedis](https://github.com/xetorthio/jedis) from 3.0.1 to 3.1.0. - [Release notes](https://github.com/xetorthio/jedis/releases) - [Commits](https://github.com/xetorthio/jedis/compare/jedis-3.0.1...jedis-3.1.0) Signed-off-by: dependabot-preview[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 78b60a41de0..2235b53742e 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -106,7 +106,7 @@ dependencies { } testCompile 'org.apache.httpcomponents:httpclient:4.5.9' - testCompile 'redis.clients:jedis:3.0.1' + testCompile 'redis.clients:jedis:3.1.0' testCompile 'com.rabbitmq:amqp-client:5.7.2' testCompile 'org.mongodb:mongo-java-driver:3.10.2' testCompile ('org.mockito:mockito-core:3.0.0') { From 827c4c567a30a1c43483a85c326883d051ea539d Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 26 Aug 2019 10:45:12 +0200 Subject: [PATCH 0885/5615] Separate "environment" and "classpath" properties (for global things) (#1784) * Separate "environment" and "classpath" properties (for global things) * Update TestcontainersConfiguration.java --- .../DockerClientProviderStrategy.java | 18 +--- .../utility/TestcontainersConfiguration.java | 97 ++++++++++--------- .../TestcontainersConfigurationTest.java | 43 ++++++++ 3 files changed, 99 insertions(+), 59 deletions(-) create mode 100644 core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index 57cccc8b540..7101b23b21a 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -167,20 +167,10 @@ public DockerClient getClient() { } protected DockerClient getClientForConfig(DockerClientConfig config) { - DockerClientBuilder clientBuilder = DockerClientBuilder - .getInstance(new AuthDelegatingDockerClientConfig(config)); - - String transportType = TestcontainersConfiguration.getInstance().getTransportType(); - if ("okhttp".equals(transportType)) { - clientBuilder - .withDockerCmdExecFactory(new OkHttpDockerCmdExecFactory()); - } else { - throw new IllegalArgumentException("Unknown transport type: " + transportType); - } - - LOGGER.info("Will use '{}' transport", transportType); - - return clientBuilder.build(); + return DockerClientBuilder + .getInstance(new AuthDelegatingDockerClientConfig(config)) + .withDockerCmdExecFactory(new OkHttpDockerCmdExecFactory()) + .build(); } protected void ping(DockerClient client, int timeoutInSeconds) { diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 608d02716d0..b7ef772fcf5 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -5,6 +5,7 @@ import java.io.*; import java.net.MalformedURLException; +import java.net.URL; import java.util.Objects; import java.util.Properties; import java.util.stream.Stream; @@ -19,12 +20,22 @@ public class TestcontainersConfiguration { private static String PROPERTIES_FILE_NAME = "testcontainers.properties"; - private static File GLOBAL_CONFIG_FILE = new File(System.getProperty("user.home"), "." + PROPERTIES_FILE_NAME); + private static File ENVIRONMENT_CONFIG_FILE = new File(System.getProperty("user.home"), "." + PROPERTIES_FILE_NAME); @Getter(lazy = true) private static final TestcontainersConfiguration instance = loadConfiguration(); - private final Properties properties; + @Getter(AccessLevel.NONE) + private final Properties environmentProperties; + + private final Properties properties = new Properties(); + + TestcontainersConfiguration(Properties environmentProperties, Properties classpathProperties) { + this.environmentProperties = environmentProperties; + + this.properties.putAll(classpathProperties); + this.properties.putAll(environmentProperties); + } public String getAmbassadorContainerImage() { return (String) properties.getOrDefault("ambassador.container.image", "richnorth/ambassador:latest"); @@ -71,13 +82,18 @@ public String getPulsarImage() { } public boolean isDisableChecks() { - return Boolean.parseBoolean((String) properties.getOrDefault("checks.disable", "false")); + return Boolean.parseBoolean((String) environmentProperties.getOrDefault("checks.disable", "false")); } public String getDockerClientStrategyClassName() { - return (String) properties.get("docker.client.strategy"); + return (String) environmentProperties.get("docker.client.strategy"); } + /** + * + * @deprecated we no longer have different transport types + */ + @Deprecated public String getTransportType() { return properties.getProperty("transport.type", "okhttp"); } @@ -89,64 +105,55 @@ public Integer getImagePullPauseTimeout() { @Synchronized public boolean updateGlobalConfig(@NonNull String prop, @NonNull String value) { try { - Properties globalProperties = new Properties(); - GLOBAL_CONFIG_FILE.createNewFile(); - try (InputStream inputStream = new FileInputStream(GLOBAL_CONFIG_FILE)) { - globalProperties.load(inputStream); - } - - if (value.equals(globalProperties.get(prop))) { + if (value.equals(environmentProperties.get(prop))) { return false; } - globalProperties.setProperty(prop, value); + environmentProperties.setProperty(prop, value); - try (OutputStream outputStream = new FileOutputStream(GLOBAL_CONFIG_FILE)) { - globalProperties.store(outputStream, "Modified by Testcontainers"); + ENVIRONMENT_CONFIG_FILE.createNewFile(); + try (OutputStream outputStream = new FileOutputStream(ENVIRONMENT_CONFIG_FILE)) { + environmentProperties.store(outputStream, "Modified by Testcontainers"); } - // Update internal state only if global config was successfully updated + // Update internal state only if environment config was successfully updated properties.setProperty(prop, value); return true; } catch (Exception e) { - log.debug("Can't store global property {} in {}", prop, GLOBAL_CONFIG_FILE); + log.debug("Can't store environment property {} in {}", prop, ENVIRONMENT_CONFIG_FILE); return false; } } @SneakyThrows(MalformedURLException.class) private static TestcontainersConfiguration loadConfiguration() { - final TestcontainersConfiguration config = new TestcontainersConfiguration( - Stream - .of( - TestcontainersConfiguration.class.getClassLoader().getResource(PROPERTIES_FILE_NAME), - Thread.currentThread().getContextClassLoader().getResource(PROPERTIES_FILE_NAME), - GLOBAL_CONFIG_FILE.toURI().toURL() - ) - .filter(Objects::nonNull) - .map(it -> { - log.debug("Testcontainers configuration overrides will be loaded from {}", it); - - final Properties subProperties = new Properties(); - try (final InputStream inputStream = it.openStream()) { - subProperties.load(inputStream); - } catch (FileNotFoundException e) { - log.trace("Testcontainers config override was found on " + it + " but the file was not found", e); - } catch (IOException e) { - log.warn("Testcontainers config override was found on " + it + " but could not be loaded", e); - } - return subProperties; - }) - .reduce(new Properties(), (a, b) -> { - a.putAll(b); - return a; - }) + return new TestcontainersConfiguration( + readProperties(ENVIRONMENT_CONFIG_FILE.toURI().toURL()), + Stream + .of( + TestcontainersConfiguration.class.getClassLoader(), + Thread.currentThread().getContextClassLoader() + ) + .map(it -> it.getResource(PROPERTIES_FILE_NAME)) + .filter(Objects::nonNull) + .map(TestcontainersConfiguration::readProperties) + .reduce(new Properties(), (a, b) -> { + a.putAll(b); + return a; + }) ); + } - if (!config.getProperties().isEmpty()) { - log.debug("Testcontainers configuration overrides loaded from {}", config); + private static Properties readProperties(URL url) { + log.debug("Testcontainers configuration overrides will be loaded from {}", url); + Properties properties = new Properties(); + try (InputStream inputStream = url.openStream()) { + properties.load(inputStream); + } catch (FileNotFoundException e) { + log.trace("Testcontainers config override was found on {} but the file was not found", url, e); + } catch (IOException e) { + log.warn("Testcontainers config override was found on {} but could not be loaded", url, e); } - - return config; + return properties; } } diff --git a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java new file mode 100644 index 00000000000..ad2a0be6cd3 --- /dev/null +++ b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java @@ -0,0 +1,43 @@ +package org.testcontainers.utility; + +import org.junit.Test; + +import java.util.Properties; +import java.util.UUID; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; + +public class TestcontainersConfigurationTest { + + final Properties environmentProperties = new Properties(); + + final Properties classpathProperties = new Properties(); + + @Test + public void shouldReadChecksFromEnvironmentOnly() { + assertFalse("checks enabled by default", newConfig().isDisableChecks()); + + classpathProperties.setProperty("checks.disable", "true"); + assertFalse("checks are not affected by classpath properties", newConfig().isDisableChecks()); + + environmentProperties.setProperty("checks.disable", "true"); + assertTrue("checks disabled", newConfig().isDisableChecks()); + } + + @Test + public void shouldReadDockerClientStrategyFromEnvironmentOnly() { + String currentValue = newConfig().getDockerClientStrategyClassName(); + + classpathProperties.setProperty("docker.client.strategy", UUID.randomUUID().toString()); + assertEquals("Docker client strategy is not affected by classpath properties", currentValue, newConfig().getDockerClientStrategyClassName()); + + environmentProperties.setProperty("docker.client.strategy", "foo"); + assertEquals("Docker client strategy is changed", "foo", newConfig().getDockerClientStrategyClassName()); + } + + private TestcontainersConfiguration newConfig() { + return new TestcontainersConfiguration(environmentProperties, classpathProperties); + } +} From 137c510f96e89fdf359c5a7e36af3f82e06e6430 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 26 Aug 2019 10:47:13 +0200 Subject: [PATCH 0886/5615] speed up port detection by running the checks as a single command (#1782) --- .../internal/ExternalPortListeningCheck.java | 4 +- .../InternalCommandPortListeningCheck.java | 45 ++++++++++--------- .../wait/strategy/HostPortWaitStrategy.java | 4 +- ...InternalCommandPortListeningCheckTest.java | 10 ++--- 4 files changed, 33 insertions(+), 30 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java b/core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java index 3283880b569..2aae12bab8d 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java +++ b/core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java @@ -20,13 +20,13 @@ public class ExternalPortListeningCheck implements Callable { public Boolean call() { String address = containerState.getContainerIpAddress(); - for (Integer externalPort : externalLivenessCheckPorts) { + externalLivenessCheckPorts.parallelStream().forEach(externalPort -> { try { new Socket(address, externalPort).close(); } catch (IOException e) { throw new IllegalStateException("Socket not listening yet: " + externalPort); } - } + }); return true; } } diff --git a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java index bad30fef653..43ed94bc84d 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java +++ b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java @@ -1,9 +1,13 @@ package org.testcontainers.containers.wait.internal; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.testcontainers.containers.Container.ExecResult; import org.testcontainers.containers.ExecInContainerPattern; import org.testcontainers.containers.wait.strategy.WaitStrategyTarget; +import java.time.Duration; +import java.time.Instant; import java.util.Set; import static java.lang.String.format; @@ -12,6 +16,7 @@ * Mechanism for testing that a socket is listening when run from the container being checked. */ @RequiredArgsConstructor +@Slf4j public class InternalCommandPortListeningCheck implements java.util.concurrent.Callable { private final WaitStrategyTarget waitStrategyTarget; @@ -19,30 +24,26 @@ public class InternalCommandPortListeningCheck implements java.util.concurrent.C @Override public Boolean call() { - for (Integer internalPort : internalPorts) { - tryPort(internalPort); + String command = "true"; + + for (int internalPort : internalPorts) { + command += " && "; + command += " ("; + command += format("cat /proc/net/tcp{,6} | awk '{print $2}' | grep -i :%x", internalPort); + command += " || "; + command += format("nc -vz -w 1 localhost %d", internalPort); + command += " || "; + command += format("/bin/bash -c ' externalLivenessCheckPorts = getLivenessCheckPorts(); if (externalLivenessCheckPorts.isEmpty()) { - log.debug("Liveness check ports of {} is empty. Not waiting.", waitStrategyTarget.getContainerInfo().getName()); + if (log.isDebugEnabled()) { + log.debug("Liveness check ports of {} is empty. Not waiting.", waitStrategyTarget.getContainerInfo().getName()); + } return; } diff --git a/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java b/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java index c1cd341943e..71a14c3c79e 100644 --- a/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java +++ b/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java @@ -6,7 +6,7 @@ import org.testcontainers.containers.BindMode; import org.testcontainers.containers.GenericContainer; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; +import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; public class InternalCommandPortListeningCheckTest { @@ -30,8 +30,8 @@ public void singleListening() { public void nonListening() { final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(nginx, ImmutableSet.of(8080, 1234)); - assertThrows("InternalCommandPortListeningCheck detects a non-listening port among many", - IllegalStateException.class, - (Runnable) check::call); + final Boolean result = check.call(); + + assertFalse("InternalCommandPortListeningCheck detects a non-listening port among many", result); } -} \ No newline at end of file +} From a0f5d0008f5a89929dba388557a49718a3e0af20 Mon Sep 17 00:00:00 2001 From: William Delanoue Date: Mon, 26 Aug 2019 20:58:50 +0200 Subject: [PATCH 0887/5615] Support legacy RabbitMQ configuration format (#1692) see https://github.com/testcontainers/testcontainers-java/issues/1690 --- .../containers/RabbitMQContainer.java | 32 ++- .../containers/RabbitMQContainerTest.java | 195 +++++++++++------- .../src/test/resources/rabbitmq-custom.conf | 2 +- .../src/test/resources/rabbitmq-custom.config | 13 ++ 4 files changed, 161 insertions(+), 81 deletions(-) create mode 100644 modules/rabbitmq/src/test/resources/rabbitmq-custom.config diff --git a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java index f16f005df5e..4faaf3a5dea 100644 --- a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java +++ b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java @@ -346,16 +346,42 @@ public RabbitMQContainer withExchange(String name, String type, boolean autoDele } /** - * Overwrites the default RabbitMQ configuration file with the supplied one + * Overwrites the default RabbitMQ configuration file with the supplied one. * - * @param rabbitMQConf The rabbitmq.conf file to use + * @param rabbitMQConf The rabbitmq.conf file to use (in sysctl format, don't forget empty line in the end of file) * @return This container. */ public RabbitMQContainer withRabbitMQConfig(MountableFile rabbitMQConf) { - withEnv("RABBITMQ_CONFIG_FILE", "/etc/rabbitmq/rabbitmq-custom.conf"); + + return withRabbitMQConfigSysctl(rabbitMQConf); + } + + /** + * Overwrites the default RabbitMQ configuration file with the supplied one. + * + * This function doesn't work with RabbitMQ < 3.7. + * + * This function and the Sysctl format is recommended for RabbitMQ >= 3.7 + * + * @param rabbitMQConf The rabbitmq.config file to use (in sysctl format, don't forget empty line in the end of file) + * @return This container. + */ + public RabbitMQContainer withRabbitMQConfigSysctl(MountableFile rabbitMQConf) { + withEnv("RABBITMQ_CONFIG_FILE", "/etc/rabbitmq/rabbitmq-custom"); return withCopyFileToContainer(rabbitMQConf, "/etc/rabbitmq/rabbitmq-custom.conf"); } + /** + * Overwrites the default RabbitMQ configuration file with the supplied one. + * + * @param rabbitMQConf The rabbitmq.config file to use (in erlang format) + * @return This container. + */ + public RabbitMQContainer withRabbitMQConfigErlang(MountableFile rabbitMQConf) { + withEnv("RABBITMQ_CONFIG_FILE", "/etc/rabbitmq/rabbitmq-custom.config"); + return withCopyFileToContainer(rabbitMQConf, "/etc/rabbitmq/rabbitmq-custom.config"); + } + @NotNull private String toJson(Map arguments) { try { diff --git a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java index 4f549a6f5ed..bdda594818f 100644 --- a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java +++ b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java @@ -1,26 +1,29 @@ package org.testcontainers.containers; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.testcontainers.containers.RabbitMQContainer.SslVerification.VERIFY_PEER; +import static org.testcontainers.utility.MountableFile.forClasspathResource; + import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; -import org.junit.Test; -import org.testcontainers.utility.MountableFile; - -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManagerFactory; import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.security.*; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.testcontainers.containers.RabbitMQContainer.SslVerification.VERIFY_PEER; -import static org.testcontainers.utility.MountableFile.forClasspathResource; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; +import org.junit.Test; +import org.testcontainers.utility.MountableFile; /** * @author Martin Greber @@ -34,7 +37,8 @@ public class RabbitMQContainerTest { public static final int DEFAULT_HTTP_PORT = 15672; @Test - public void shouldCreateRabbitMQContainer() { + public void shouldCreateRabbitMQContainer() + { try (RabbitMQContainer container = new RabbitMQContainer()) { assertThat(container.getDockerImageName()).isEqualTo(DEFAULT_IMAGE); @@ -44,13 +48,13 @@ public void shouldCreateRabbitMQContainer() { container.start(); assertThat(container.getAmqpsUrl()).isEqualTo( - String.format("amqps://%s:%d", container.getContainerIpAddress(), container.getMappedPort(DEFAULT_AMQPS_PORT))); + String.format("amqps://%s:%d", container.getContainerIpAddress(), container.getMappedPort(DEFAULT_AMQPS_PORT))); assertThat(container.getAmqpUrl()).isEqualTo( - String.format("amqp://%s:%d", container.getContainerIpAddress(), container.getMappedPort(DEFAULT_AMQP_PORT))); + String.format("amqp://%s:%d", container.getContainerIpAddress(), container.getMappedPort(DEFAULT_AMQP_PORT))); assertThat(container.getHttpsUrl()).isEqualTo( - String.format("https://%s:%d", container.getContainerIpAddress(), container.getMappedPort(DEFAULT_HTTPS_PORT))); + String.format("https://%s:%d", container.getContainerIpAddress(), container.getMappedPort(DEFAULT_HTTPS_PORT))); assertThat(container.getHttpUrl()).isEqualTo( - String.format("http://%s:%d", container.getContainerIpAddress(), container.getMappedPort(DEFAULT_HTTP_PORT))); + String.format("http://%s:%d", container.getContainerIpAddress(), container.getMappedPort(DEFAULT_HTTP_PORT))); assertThat(container.getHttpsPort()).isEqualTo(container.getMappedPort(DEFAULT_HTTPS_PORT)); assertThat(container.getHttpPort()).isEqualTo(container.getMappedPort(DEFAULT_HTTP_PORT)); @@ -58,140 +62,176 @@ public void shouldCreateRabbitMQContainer() { assertThat(container.getAmqpPort()).isEqualTo(container.getMappedPort(DEFAULT_AMQP_PORT)); assertThat(container.getLivenessCheckPortNumbers()).containsExactlyInAnyOrder( - container.getMappedPort(DEFAULT_AMQP_PORT), - container.getMappedPort(DEFAULT_AMQPS_PORT), - container.getMappedPort(DEFAULT_HTTP_PORT), - container.getMappedPort(DEFAULT_HTTPS_PORT) + container.getMappedPort(DEFAULT_AMQP_PORT), + container.getMappedPort(DEFAULT_AMQPS_PORT), + container.getMappedPort(DEFAULT_HTTP_PORT), + container.getMappedPort(DEFAULT_HTTPS_PORT) ); } } @Test - public void shouldCreateRabbitMQContainerWithTag() { + public void shouldCreateRabbitMQContainerWithTag() + { try (RabbitMQContainer container = new RabbitMQContainer(DEFAULT_IMAGE)) { assertThat(container.getDockerImageName()).isEqualTo(DEFAULT_IMAGE); } } @Test - public void shouldCreateRabbitMQContainerWithExchange() throws IOException, InterruptedException { + public void shouldCreateRabbitMQContainerWithExchange() throws IOException, InterruptedException + { try (RabbitMQContainer container = new RabbitMQContainer()) { container.withExchange("test-exchange", "direct"); container.start(); assertThat(container.execInContainer("rabbitmqctl", "list_exchanges").getStdout()) - .containsPattern("test-exchange\\s+direct"); + .containsPattern("test-exchange\\s+direct"); } } @Test - public void shouldCreateRabbitMQContainerWithQueues() throws IOException, InterruptedException { + public void shouldCreateRabbitMQContainerWithQueues() throws IOException, InterruptedException + { try (RabbitMQContainer container = new RabbitMQContainer()) { container.withQueue("queue-one") - .withQueue("queue-two", false, true, ImmutableMap.of("x-message-ttl", 1000)); + .withQueue("queue-two", false, true, ImmutableMap.of("x-message-ttl", 1000)); container.start(); assertThat(container.execInContainer("rabbitmqctl", "list_queues", "name", "arguments").getStdout()) - .containsPattern("queue-one"); + .containsPattern("queue-one"); assertThat(container.execInContainer("rabbitmqctl", "list_queues", "name", "arguments").getStdout()) - .containsPattern("queue-two\\s.*x-message-ttl"); + .containsPattern("queue-two\\s.*x-message-ttl"); + } + } + + @Test + public void shouldMountConfigurationFile() + { + try (RabbitMQContainer container = new RabbitMQContainer()) { + + container.withRabbitMQConfig(MountableFile.forClasspathResource("/rabbitmq-custom.conf")); + container.start(); + + assertThat(container.getLogs()).contains("config file(s) : /etc/rabbitmq/rabbitmq-custom.conf"); + assertThat(container.getLogs()).doesNotContain(" (not found)"); } } + + @Test + public void shouldMountConfigurationFileErlang() + { + try (RabbitMQContainer container = new RabbitMQContainer()) { + + container.withRabbitMQConfigErlang(MountableFile.forClasspathResource("/rabbitmq-custom.config")); + container.start(); + + assertThat(container.getLogs()).contains("config file(s) : /etc/rabbitmq/rabbitmq-custom.config"); + assertThat(container.getLogs()).doesNotContain(" (not found)"); + } + } + + @Test - public void shouldMountConfigurationFile() { + public void shouldMountConfigurationFileSysctl() + { try (RabbitMQContainer container = new RabbitMQContainer()) { container.withRabbitMQConfig(MountableFile.forClasspathResource("/rabbitmq-custom.conf")); container.start(); - assertThat(container.getLogs().contains("/etc/rabbitmq/rabbitmq-custom.conf")).isTrue(); + assertThat(container.getLogs()).contains("config file(s) : /etc/rabbitmq/rabbitmq-custom.conf"); + assertThat(container.getLogs()).doesNotContain(" (not found)"); } } @Test - public void shouldStartTheWholeEnchilada() throws IOException, InterruptedException { + public void shouldStartTheWholeEnchilada() throws IOException, InterruptedException + { try (RabbitMQContainer container = new RabbitMQContainer()) { container - .withVhost("vhost1") - .withVhostLimit("vhost1", "max-connections", 1) - .withVhost("vhost2", true) - .withExchange("direct-exchange", "direct") - .withExchange("topic-exchange", "topic") - .withQueue("queue1") - .withQueue("queue2", true, false, ImmutableMap.of("x-message-ttl", 1000)) - .withBinding("direct-exchange", "queue1") - .withUser("user1", "password1") - .withUser("user2", "password2", ImmutableSet.of("administrator")) - .withPermission("vhost1", "user1", ".*", ".*", ".*") - .withPolicy("max length policy", "^dog", ImmutableMap.of("max-length", 1), 1, "queues") - .withPolicy("alternate exchange policy", "^direct-exchange", ImmutableMap.of("alternate-exchange", "amq.direct")) - .withOperatorPolicy("operator policy 1", "^queue1", ImmutableMap.of("message-ttl", 1000), 1, "queues") - .withPluginsEnabled("rabbitmq_shovel", "rabbitmq_random_exchange"); + .withVhost("vhost1") + .withVhostLimit("vhost1", "max-connections", 1) + .withVhost("vhost2", true) + .withExchange("direct-exchange", "direct") + .withExchange("topic-exchange", "topic") + .withQueue("queue1") + .withQueue("queue2", true, false, ImmutableMap.of("x-message-ttl", 1000)) + .withBinding("direct-exchange", "queue1") + .withUser("user1", "password1") + .withUser("user2", "password2", ImmutableSet.of("administrator")) + .withPermission("vhost1", "user1", ".*", ".*", ".*") + .withPolicy("max length policy", "^dog", ImmutableMap.of("max-length", 1), 1, "queues") + .withPolicy("alternate exchange policy", "^direct-exchange", ImmutableMap.of("alternate-exchange", "amq.direct")) + .withOperatorPolicy("operator policy 1", "^queue1", ImmutableMap.of("message-ttl", 1000), 1, "queues") + .withPluginsEnabled("rabbitmq_shovel", "rabbitmq_random_exchange"); container.start(); assertThat(container.execInContainer("rabbitmqadmin", "list", "queues") - .getStdout()) - .contains("queue1", "queue2"); + .getStdout()) + .contains("queue1", "queue2"); assertThat(container.execInContainer("rabbitmqadmin", "list", "exchanges") - .getStdout()) - .contains("direct-exchange", "topic-exchange"); + .getStdout()) + .contains("direct-exchange", "topic-exchange"); assertThat(container.execInContainer("rabbitmqadmin", "list", "bindings") - .getStdout()) - .contains("direct-exchange"); + .getStdout()) + .contains("direct-exchange"); assertThat(container.execInContainer("rabbitmqadmin", "list", "users") - .getStdout()) - .contains("user1", "user2"); + .getStdout()) + .contains("user1", "user2"); assertThat(container.execInContainer("rabbitmqadmin", "list", "policies") - .getStdout()) - .contains("max length policy", "alternate exchange policy"); + .getStdout()) + .contains("max length policy", "alternate exchange policy"); assertThat(container.execInContainer("rabbitmqadmin", "list", "operator_policies") - .getStdout()) - .contains("operator policy 1"); + .getStdout()) + .contains("operator policy 1"); assertThat(container.execInContainer("rabbitmq-plugins", "is_enabled", "rabbitmq_shovel", "--quiet") - .getStdout()) - .contains("rabbitmq_shovel is enabled"); + .getStdout()) + .contains("rabbitmq_shovel is enabled"); assertThat(container.execInContainer("rabbitmq-plugins", "is_enabled", "rabbitmq_random_exchange", "--quiet") - .getStdout()) - .contains("rabbitmq_random_exchange is enabled"); + .getStdout()) + .contains("rabbitmq_random_exchange is enabled"); } } @Test - public void shouldThrowExceptionForDodgyJson() { + public void shouldThrowExceptionForDodgyJson() + { try (RabbitMQContainer container = new RabbitMQContainer()) { assertThatCode(() -> - container.withQueue( - "queue2", - true, - false, - ImmutableMap.of("x-message-ttl", container)) + container.withQueue( + "queue2", + true, + false, + ImmutableMap.of("x-message-ttl", container)) ).hasMessageStartingWith("Failed to convert arguments into json"); } } @Test - public void shouldWorkWithSSL() { + public void shouldWorkWithSSL() + { try (RabbitMQContainer container = new RabbitMQContainer()) { container.withSSL( - forClasspathResource("/certs/server_key.pem", 0644), - forClasspathResource("/certs/server_certificate.pem", 0644), - forClasspathResource("/certs/ca_certificate.pem", 0644), - VERIFY_PEER, - true + forClasspathResource("/certs/server_key.pem", 0644), + forClasspathResource("/certs/server_certificate.pem", 0644), + forClasspathResource("/certs/ca_certificate.pem", 0644), + VERIFY_PEER, + true ); container.start(); @@ -199,8 +239,8 @@ public void shouldWorkWithSSL() { assertThatCode(() -> { ConnectionFactory connectionFactory = new ConnectionFactory(); connectionFactory.useSslProtocol(createSslContext( - "certs/client_key.p12", "password", - "certs/truststore.jks", "password")); + "certs/client_key.p12", "password", + "certs/truststore.jks", "password")); connectionFactory.enableHostnameVerification(); connectionFactory.setUri(container.getAmqpsUrl()); connectionFactory.setPassword(container.getAdminPassword()); @@ -213,7 +253,8 @@ public void shouldWorkWithSSL() { } private SSLContext createSslContext(String keystoreFile, String keystorePassword, String truststoreFile, String truststorePassword) - throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException, KeyManagementException { + throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException, KeyManagementException + { ClassLoader classLoader = getClass().getClassLoader(); KeyStore ks = KeyStore.getInstance("PKCS12"); diff --git a/modules/rabbitmq/src/test/resources/rabbitmq-custom.conf b/modules/rabbitmq/src/test/resources/rabbitmq-custom.conf index 2e93b89bb13..293829a353b 100644 --- a/modules/rabbitmq/src/test/resources/rabbitmq-custom.conf +++ b/modules/rabbitmq/src/test/resources/rabbitmq-custom.conf @@ -1,2 +1,2 @@ loopback_users.guest = false -listeners.tcp.default = 5555 \ No newline at end of file +listeners.tcp.default = 5555 diff --git a/modules/rabbitmq/src/test/resources/rabbitmq-custom.config b/modules/rabbitmq/src/test/resources/rabbitmq-custom.config new file mode 100644 index 00000000000..a1c581542ce --- /dev/null +++ b/modules/rabbitmq/src/test/resources/rabbitmq-custom.config @@ -0,0 +1,13 @@ +[ +{ rabbit, [ +{ loopback_users, [ ] }, + { tcp_listeners, [ 5555 ] }, + { ssl_listeners, [ ] }, + { default_vhost, <<"vhost">> }, + { hipe_compile, false } + ] }, + { rabbitmq_management, [ { listener, [ + { port, 15672 }, + { ssl, false } + ] } ] } +]. From b5fda2acf41d67f800b92a86285ee9e60ebd3078 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2019 19:59:34 +0100 Subject: [PATCH 0888/5615] Bump mockito-core from 2.28.2 to 3.0.0 in /modules/junit-jupiter (#1636) Bumps [mockito-core](https://github.com/mockito/mockito) from 2.28.2 to 3.0.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v2.28.2...v3.0.0) Signed-off-by: dependabot-preview[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 5710b48fd15..2a0937a85d7 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,7 +9,7 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.3.1' testCompile 'redis.clients:jedis:3.1.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.9' - testCompile ('org.mockito:mockito-core:2.28.2') { + testCompile ('org.mockito:mockito-core:3.0.0') { exclude(module: 'hamcrest-core') } From 2c21181520086a966714de4bd57bde360d6133f7 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2019 05:57:08 +0000 Subject: [PATCH 0889/5615] Bump aws-java-sdk-sqs from 1.11.618 to 1.11.619 in /modules/localstack (#1793) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 897e337ddb5..24474e5b697 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.618' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.619' } From 91acbb4f4934eae5b2b3c8a0c2f9355c8f489675 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2019 16:12:00 +0100 Subject: [PATCH 0890/5615] Bump mongo-java-driver from 3.10.2 to 3.11.0 in /core (#1795) Bumps [mongo-java-driver](https://github.com/mongodb/mongo-java-driver) from 3.10.2 to 3.11.0. - [Release notes](https://github.com/mongodb/mongo-java-driver/releases) - [Commits](https://github.com/mongodb/mongo-java-driver/compare/r3.10.2...r3.11.0) Signed-off-by: dependabot-preview[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 2235b53742e..dee1acb4d69 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -108,7 +108,7 @@ dependencies { testCompile 'org.apache.httpcomponents:httpclient:4.5.9' testCompile 'redis.clients:jedis:3.1.0' testCompile 'com.rabbitmq:amqp-client:5.7.2' - testCompile 'org.mongodb:mongo-java-driver:3.10.2' + testCompile 'org.mongodb:mongo-java-driver:3.11.0' testCompile ('org.mockito:mockito-core:3.0.0') { exclude(module: 'hamcrest-core') } From 0044218c722c174d1968c5a5e933edb5efb5ceca Mon Sep 17 00:00:00 2001 From: Yoonyoul Yoo Date: Wed, 28 Aug 2019 00:14:20 +0900 Subject: [PATCH 0891/5615] update latest version with 1.12.0 (#1800) --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 044489d1e50..f0d953ecd8a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -82,4 +82,4 @@ nav: - contributing.md - contributing_docs.md extra: - latest_version: 1.11.3 + latest_version: 1.12.0 From 8405607b9da8fc324adf1b5b3162f8fb3c2e257d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 28 Aug 2019 09:31:09 +0000 Subject: [PATCH 0892/5615] Bump aws-java-sdk-sqs from 1.11.619 to 1.11.620 in /modules/localstack (#1803) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 24474e5b697..d808e44d594 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.619' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.620' } From 2c72803f3dc4acea93ab31988d2ad3d4890a269a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 29 Aug 2019 06:03:39 +0000 Subject: [PATCH 0893/5615] Bump aws-java-sdk-sqs from 1.11.620 to 1.11.621 in /modules/localstack (#1808) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index d808e44d594..bfb94e0a16b 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.620' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.621' } From 040b35dd2b7163b1687ca9f5d39f7de7e67f1df9 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 29 Aug 2019 07:37:08 +0000 Subject: [PATCH 0894/5615] Bump cucumber-junit from 4.7.1 to 4.7.2 in /examples (#1809) --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 5467783ef92..062fbf84612 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' implementation 'io.cucumber:cucumber-java:4.7.1' - testImplementation 'io.cucumber:cucumber-junit:4.7.1' + testImplementation 'io.cucumber:cucumber-junit:4.7.2' testImplementation 'org.testcontainers:selenium' } From 6e7546ac3bf923f1f00fe4178be5ff2655403400 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2019 05:55:27 +0000 Subject: [PATCH 0895/5615] Bump aws-java-sdk-sqs from 1.11.621 to 1.11.622 in /modules/localstack (#1811) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index bfb94e0a16b..371891ce27a 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.621' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.622' } From a3190044dd940c8ec61bfc5b5441fb07d5f161bf Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2019 06:44:56 +0000 Subject: [PATCH 0896/5615] Bump cucumber-java from 4.7.1 to 4.7.2 in /examples (#1812) --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 062fbf84612..7e73852ca1d 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' - implementation 'io.cucumber:cucumber-java:4.7.1' + implementation 'io.cucumber:cucumber-java:4.7.2' testImplementation 'io.cucumber:cucumber-junit:4.7.2' testImplementation 'org.testcontainers:selenium' } From d9f0d73307c27de6883602ca13257bb914da03b5 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 30 Aug 2019 09:57:08 +0200 Subject: [PATCH 0897/5615] Add more labels to the Stale bot's config (#1810) --- .github/stale.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/stale.yml b/.github/stale.yml index a071ce18a5f..5f903fb0189 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -7,6 +7,8 @@ exemptLabels: - pinned - security - "resolution/acknowledged" + - "help wanted" + - "good first issue" # Label to use when marking an issue as stale staleLabel: stale # Comment to post when marking an issue as stale. Set to `false` to disable From b97254c3bfa208011d484ac3073674aa6dd11954 Mon Sep 17 00:00:00 2001 From: Guillaume Le Floch Date: Sun, 1 Sep 2019 22:52:08 +0200 Subject: [PATCH 0898/5615] RabbitMQ: fix typo in binding command (#1792) --- .../java/org/testcontainers/containers/RabbitMQContainer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java index 4faaf3a5dea..8f0ef0ebcc1 100644 --- a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java +++ b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java @@ -215,8 +215,8 @@ public RabbitMQContainer withBinding(String source, String destination, Map Date: Sun, 1 Sep 2019 21:59:59 +0100 Subject: [PATCH 0899/5615] Bump commons-compress from 1.18 to 1.19 in /core (#1804) Bumps commons-compress from 1.18 to 1.19. Signed-off-by: dependabot-preview[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index dee1acb4d69..51f61fcb976 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -60,7 +60,7 @@ dependencies { compile 'org.slf4j:slf4j-api:1.7.26' compile 'org.jetbrains:annotations:17.0.0' compile 'javax.annotation:javax.annotation-api:1.3.2' - compile 'org.apache.commons:commons-compress:1.18' + compile 'org.apache.commons:commons-compress:1.19' // Added for JDK9 compatibility since it's missing this package compile 'javax.xml.bind:jaxb-api:2.3.1' compile ('org.rnorth.duct-tape:duct-tape:1.0.8') { From 3509316dda8b1cefbc5201ea3248dab611bc2f08 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2019 06:06:14 +0000 Subject: [PATCH 0900/5615] Bump rest-assured from 4.0.0 to 4.1.0 in /modules/vault (#1820) --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index 3dd346ab559..a05c77f8158 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: Vault" dependencies { compile project(':testcontainers') - testCompile 'io.rest-assured:rest-assured:4.0.0' + testCompile 'io.rest-assured:rest-assured:4.1.0' } From 2c6315270bd388138181d529719230e2261ea34e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2019 08:35:09 +0100 Subject: [PATCH 0901/5615] Bump slf4j-api from 1.7.26 to 1.7.28 in /core (#1805) Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 1.7.26 to 1.7.28. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_1.7.26...v_1.7.28) Signed-off-by: dependabot-preview[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 51f61fcb976..6b6ad2a6c28 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -57,7 +57,7 @@ project.tasks.check.dependsOn(jarFileTest) dependencies { compile 'junit:junit:4.12' - compile 'org.slf4j:slf4j-api:1.7.26' + compile 'org.slf4j:slf4j-api:1.7.28' compile 'org.jetbrains:annotations:17.0.0' compile 'javax.annotation:javax.annotation-api:1.3.2' compile 'org.apache.commons:commons-compress:1.19' From 7122b33750ebce829959622a58b5f760b6e8cfb2 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2019 08:36:52 +0000 Subject: [PATCH 0902/5615] Bump aws-java-sdk-sqs from 1.11.622 to 1.11.623 in /modules/localstack (#1819) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 371891ce27a..15eac5ebb44 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.622' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.623' } From 88a4eaf6f0353af172b2b952fc8d02c2bbea026e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2019 11:27:06 +0100 Subject: [PATCH 0903/5615] Bump amqp-client from 5.7.2 to 5.7.3 in /core (#1818) Bumps [amqp-client](https://github.com/rabbitmq/rabbitmq-java-client) from 5.7.2 to 5.7.3. - [Release notes](https://github.com/rabbitmq/rabbitmq-java-client/releases) - [Commits](https://github.com/rabbitmq/rabbitmq-java-client/compare/v5.7.2...v5.7.3) Signed-off-by: dependabot-preview[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 6b6ad2a6c28..f08e7763b18 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -107,7 +107,7 @@ dependencies { testCompile 'org.apache.httpcomponents:httpclient:4.5.9' testCompile 'redis.clients:jedis:3.1.0' - testCompile 'com.rabbitmq:amqp-client:5.7.2' + testCompile 'com.rabbitmq:amqp-client:5.7.3' testCompile 'org.mongodb:mongo-java-driver:3.11.0' testCompile ('org.mockito:mockito-core:3.0.0') { exclude(module: 'hamcrest-core') From c9b66eb3e610bfb76024000ae062bec889cdab34 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 3 Sep 2019 06:56:39 +0200 Subject: [PATCH 0904/5615] Use "copy" strategy in `withClasspathResourceMapping` where appropriate (#1814) --- .../containers/GenericContainer.java | 6 ++- .../junit/CopyFileToContainerTest.java | 49 ++++++++++++++++--- 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 14ce129c9a5..baf79474aaa 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -967,7 +967,11 @@ public SELF withClasspathResourceMapping(final String resourcePath, final String public SELF withClasspathResourceMapping(final String resourcePath, final String containerPath, final BindMode mode, final SelinuxContext selinuxContext) { final MountableFile mountableFile = MountableFile.forClasspathResource(resourcePath); - this.addFileSystemBind(mountableFile.getResolvedPath(), containerPath, mode, selinuxContext); + if (mode == BindMode.READ_ONLY && selinuxContext == SelinuxContext.NONE) { + withCopyFileToContainer(mountableFile, containerPath); + } else { + addFileSystemBind(mountableFile.getResolvedPath(), containerPath, mode, selinuxContext); + } return self(); } diff --git a/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java b/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java index 5164ea6c6e1..0641bc7d50d 100644 --- a/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java @@ -1,10 +1,16 @@ package org.testcontainers.junit; -import org.junit.Assert; import org.junit.Test; +import org.testcontainers.containers.BindMode; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.SelinuxContext; import org.testcontainers.utility.MountableFile; + import java.io.IOException; +import java.util.Map; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; public class CopyFileToContainerTest { private static String containerPath = "/tmp/mappable-resource/"; @@ -12,14 +18,45 @@ public class CopyFileToContainerTest { @Test public void checkFileCopied() throws IOException, InterruptedException { - try( - GenericContainer container = new GenericContainer("alpine:latest") - .withCommand("sleep","3000") + try ( + GenericContainer container = new GenericContainer() + .withCommand("sleep", "3000") .withCopyFileToContainer(MountableFile.forClasspathResource("/mappable-resource/"), containerPath) ) { container.start(); - String filesList = container.execInContainer("ls","/tmp/mappable-resource").getStdout(); - Assert.assertTrue(filesList.contains(fileName)); + String filesList = container.execInContainer("ls", "/tmp/mappable-resource").getStdout(); + assertTrue("file list contains the file", filesList.contains(fileName)); + } + } + + @Test + public void shouldUseCopyForReadOnlyClasspathResources() throws Exception { + try ( + GenericContainer container = new GenericContainer() + .withCommand("sleep", "3000") + .withClasspathResourceMapping("/mappable-resource/", containerPath, BindMode.READ_ONLY) + ) { + container.start(); + String filesList = container.execInContainer("ls", "/tmp/mappable-resource").getStdout(); + assertTrue("file list contains the file", filesList.contains(fileName)); } } + + @Test + public void shouldUseCopyOnlyWithReadOnlyClasspathResources() { + String resource = "/test_copy_to_container.txt"; + GenericContainer container = new GenericContainer<>() + .withClasspathResourceMapping(resource, "/readOnly", BindMode.READ_ONLY) + .withClasspathResourceMapping(resource, "/readOnlyNoSelinux", BindMode.READ_ONLY) + + .withClasspathResourceMapping(resource, "/readOnlyShared", BindMode.READ_ONLY, SelinuxContext.SHARED) + .withClasspathResourceMapping(resource, "/readWrite", BindMode.READ_WRITE); + + Map copyMap = container.getCopyToFileContainerPathMap(); + assertTrue("uses copy for read-only", copyMap.containsValue("/readOnly")); + assertTrue("uses copy for read-only and no Selinux", copyMap.containsValue("/readOnlyNoSelinux")); + + assertFalse("uses mount for read-only with Selinux", copyMap.containsValue("/readOnlyShared")); + assertFalse("uses mount for read-write", copyMap.containsValue("/readWrite")); + } } From 0ce63054628fa1ed1a1a3fa5f58d3931f6e23df3 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2019 05:55:49 +0000 Subject: [PATCH 0905/5615] Bump neo4j from 3.5.8 to 3.5.9 in /modules/neo4j (#1823) --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index c7227a2d9ac..4889196ccef 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -25,7 +25,7 @@ task customNeo4jPluginJar(type: Jar) { test.dependsOn customNeo4jPluginJar dependencies { - customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.8" + customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.9" compile project(":testcontainers") From 33beb8d4bd97dd1f106d77124ddd2a1eb95ecf97 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2019 05:55:25 +0000 Subject: [PATCH 0906/5615] Bump aws-java-sdk-sqs from 1.11.623 to 1.11.624 in /modules/localstack (#1826) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 15eac5ebb44..ce2982204b7 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.623' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.624' } From 0d8b42b47fcfd5f7cf41c9c7324dca32f73f1d8d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2019 06:44:24 +0000 Subject: [PATCH 0907/5615] Bump pulsar-client from 2.4.0 to 2.4.1 in /modules/pulsar (#1825) --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 34cde96a33e..05d9a711420 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Pulsar" dependencies { compile project(':testcontainers') - testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.4.0' + testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.4.1' testCompile group: 'org.assertj', name: 'assertj-core', version: '3.13.2' } From acd8bdc782dfd2e8456427bb0677e94d0d5a0c8b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2019 05:56:26 +0000 Subject: [PATCH 0908/5615] Bump aws-java-sdk-sqs from 1.11.624 to 1.11.625 in /modules/localstack (#1829) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index ce2982204b7..9bc7d4fe545 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.624' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.625' } From 1e6ac9b9510f2227c6e54694ae048e47de469d12 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 5 Sep 2019 08:47:16 +0100 Subject: [PATCH 0909/5615] Fix error in Javadocs and run javadoc generation during 'check' (#1813) * Fix Javadoc error * Ensure that Javadoc generation is tested for all `check` builds * Generate Javadocs from delomboked sources --- build.gradle | 8 ++++++++ .../org/testcontainers/containers/RabbitMQContainer.java | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index b3e96b265b5..c1c031f2757 100644 --- a/build.gradle +++ b/build.gradle @@ -67,6 +67,14 @@ subprojects { } } + // Ensure that Javadoc generation is always tested + check.dependsOn(javadoc) + + javadoc { + dependsOn delombok + source = delombok.outputs + } + shadowJar { configurations = [] classifier = null diff --git a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java index 8f0ef0ebcc1..ea25bf629e6 100644 --- a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java +++ b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java @@ -359,9 +359,9 @@ public RabbitMQContainer withRabbitMQConfig(MountableFile rabbitMQConf) { /** * Overwrites the default RabbitMQ configuration file with the supplied one. * - * This function doesn't work with RabbitMQ < 3.7. + * This function doesn't work with RabbitMQ < 3.7. * - * This function and the Sysctl format is recommended for RabbitMQ >= 3.7 + * This function and the Sysctl format is recommended for RabbitMQ >= 3.7 * * @param rabbitMQConf The rabbitmq.config file to use (in sysctl format, don't forget empty line in the end of file) * @return This container. From 1161b1b843928706dcb4223d0ff87497249306c7 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2019 05:58:27 +0000 Subject: [PATCH 0910/5615] Bump aws-java-sdk-sqs from 1.11.625 to 1.11.626 in /modules/localstack (#1830) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 9bc7d4fe545..4bc50a6a1f1 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.625' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.626' } From 386c545b01751447890139aaf6475e9fb73e0529 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2019 07:00:32 +0000 Subject: [PATCH 0911/5615] Bump org.springframework.boot in /examples (#1831) --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index 22198984e33..cf3b6567982 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.1.7.RELEASE' + id 'org.springframework.boot' version '2.1.8.RELEASE' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index 7af6b9d26da..46e587bc39a 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.1.7.RELEASE' + id 'org.springframework.boot' version '2.1.8.RELEASE' } apply plugin: 'io.spring.dependency-management' From 4fbef4b66b2af7658792402db4523c4eba398fd9 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 7 Sep 2019 19:36:24 +0100 Subject: [PATCH 0912/5615] Fix javadoc error (#1832) * Update Lombok for fix to [JDK12 delombok issue](https://github.com/rzwitserloot/lombok/issues/2082) Also upgrade Gradle Lombok plugin --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index c1c031f2757..ecdf48cd3f7 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id 'com.gradle.build-scan' version '1.16' - id 'io.franzbecker.gradle-lombok' version '1.14' + id 'io.franzbecker.gradle-lombok' version '3.1.0' id 'nebula.provided-base' version '3.0.3' id 'com.github.johnrengelman.shadow' version '2.0.2' id "com.jfrog.bintray" version "1.8.4" apply false @@ -21,7 +21,7 @@ subprojects { compileTestJava.options.encoding = 'UTF-8' lombok { - version = '1.18.6' + version = '1.18.8' } task delombok(type: io.franzbecker.gradle.lombok.task.DelombokTask) { From b1cb06d7524ce44c56c7050f06e4aafb222c17fd Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 7 Sep 2019 19:36:50 +0100 Subject: [PATCH 0913/5615] =?UTF-8?q?Add=20test=20to=20demonstrate=20fix?= =?UTF-8?q?=20for=20#1452=20(loss=20of=20whitespace=20in=20sp=E2=80=A6=20(?= =?UTF-8?q?#1702)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (probably fixed by #1627) --- .../testcontainers/ext/ScriptSplittingTest.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptSplittingTest.java b/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptSplittingTest.java index 0836592b91e..202a3b995db 100644 --- a/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptSplittingTest.java +++ b/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptSplittingTest.java @@ -281,6 +281,21 @@ public void testUnclosedBlockComment() { } } + @Test + public void testIssue1452Case() { + String script = "create table test (text VARCHAR(255));\n" + + "\n" + + "/* some comment */\n" + + "insert into `test` (`text`) values ('a b');"; + + List expected = asList( + "create table test (text VARCHAR(255))", + "insert into `test` (`text`) values ('a b')" + ); + + splitAndCompare(script, expected); + } + private void splitAndCompare(String script, List expected) { final List statements = doSplit(script); Assertions.assertThat(statements).isEqualTo(expected); From b35f2ca12ce1a30c4dd932b9f5f6299ac8917c50 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 8 Sep 2019 09:00:20 +0100 Subject: [PATCH 0914/5615] Update .travis.yml to ensure that tag builds are performed --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f07d50123e2..d7d5f94270d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -if: branch = master +if: branch = master OR tag is present language: java jdk: From 271cb1a37356f322bd4f34b73e91d29d02e71283 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 8 Sep 2019 13:29:50 +0100 Subject: [PATCH 0915/5615] Update latest version in docs --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index f0d953ecd8a..39d2c8d8164 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -82,4 +82,4 @@ nav: - contributing.md - contributing_docs.md extra: - latest_version: 1.12.0 + latest_version: 1.12.1 From 5a2257454d46adc9404886f48fb4bac45c708457 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 8 Sep 2019 17:33:55 +0200 Subject: [PATCH 0916/5615] Bump testng from 6.14.3 to 7.0.0 in /examples (#1756) Bumps testng from 6.14.3 to 7.0.0. Signed-off-by: dependabot-preview[bot] --- examples/redis-backed-cache-testng/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 777ca2d9511..e68ccfea24f 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -13,7 +13,7 @@ dependencies { implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'ch.qos.logback:logback-classic:1.2.3' - testImplementation 'org.testng:testng:6.14.3' + testImplementation 'org.testng:testng:7.0.0' } test { From 097d1572851334dea84c54c053d6065c7f8f1db4 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2019 06:07:07 +0000 Subject: [PATCH 0917/5615] Bump rest-assured from 4.1.0 to 4.1.1 in /modules/vault (#1839) --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index a05c77f8158..41807d2573b 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: Vault" dependencies { compile project(':testcontainers') - testCompile 'io.rest-assured:rest-assured:4.1.0' + testCompile 'io.rest-assured:rest-assured:4.1.1' } From fc5f971711ef74f14434c0115664d26bd0299c1c Mon Sep 17 00:00:00 2001 From: Frederik Hahne Date: Mon, 9 Sep 2019 08:36:02 +0200 Subject: [PATCH 0918/5615] add jhipster as user (#1838) --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index 657c2446a55..f62cba3af45 100644 --- a/docs/index.md +++ b/docs/index.md @@ -74,6 +74,7 @@ You can also [check the latest version available on Maven Central](https://searc * [Zalando SE](https://corporate.zalando.com/en) - Testing core business services * [Europace AG](https://tech.europace.de/) - Integration testing for databases and micro services * [Micronaut Data](https://github.com/micronaut-projects/micronaut-data/) - Testing of Micronaut Data JDBC, a database access toolkit +* [JHipster](https://www.jhipster.tech/) - Couchbase and Cassandra integration testing ## License From 21e397be4e9e3a7c36863c352785e86a6ea0e2e2 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2019 05:59:32 +0000 Subject: [PATCH 0919/5615] Bump httpclient from 4.5.9 to 4.5.10 in /modules/spock (#1850) --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index f9353314c1b..69058b53a92 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -11,7 +11,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') testCompile 'com.zaxxer:HikariCP:3.3.1' - testCompile 'org.apache.httpcomponents:httpclient:4.5.9' + testCompile 'org.apache.httpcomponents:httpclient:4.5.10' testRuntime 'org.postgresql:postgresql:42.2.6' testRuntime 'mysql:mysql-connector-java:8.0.17' From 896e30634df1dc1b27ec8e80c8612f2f87c3b96c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2019 06:41:00 +0000 Subject: [PATCH 0920/5615] Bump junit-jupiter-api from 5.5.1 to 5.5.2 in /modules/junit-jupiter (#1842) --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 2a0937a85d7..3aa2abd08ba 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -2,7 +2,7 @@ description = "Testcontainers :: JUnit Jupiter Extension" dependencies { compile project(':testcontainers') - compile 'org.junit.jupiter:junit-jupiter-api:5.5.1' + compile 'org.junit.jupiter:junit-jupiter-api:5.5.2' testCompile project(':mysql') testCompile project(':postgresql') From d1a41d44a9a90438775c39d4d937ba86467a3897 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2019 07:32:17 +0000 Subject: [PATCH 0921/5615] Bump httpclient from 4.5.9 to 4.5.10 in /modules/junit-jupiter (#1851) --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 3aa2abd08ba..6997c270052 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -8,7 +8,7 @@ dependencies { testCompile project(':postgresql') testCompile 'com.zaxxer:HikariCP:3.3.1' testCompile 'redis.clients:jedis:3.1.0' - testCompile 'org.apache.httpcomponents:httpclient:4.5.9' + testCompile 'org.apache.httpcomponents:httpclient:4.5.10' testCompile ('org.mockito:mockito-core:3.0.0') { exclude(module: 'hamcrest-core') } From 09d70c4b9d942c1c71b8bc01d406245966ed8649 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2019 08:11:30 +0000 Subject: [PATCH 0922/5615] Bump aws-java-sdk-sqs from 1.11.626 to 1.11.628 in /modules/localstack (#1852) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 4bc50a6a1f1..3e065497bf2 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.626' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.628' } From aa4a99f795fb21cb9b790dc088be95fdcca3ae96 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2019 08:50:54 +0000 Subject: [PATCH 0923/5615] Bump junit-jupiter-engine from 5.5.1 to 5.5.2 in /modules/junit-jupiter (#1840) --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 6997c270052..74cb75cec0c 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -15,7 +15,7 @@ dependencies { testRuntime 'org.postgresql:postgresql:42.2.6' testRuntime 'mysql:mysql-connector-java:8.0.17' - testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.5.1' + testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.5.2' } test { From c370fe19546671bb349222b95213315353d6ee6c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 11 Sep 2019 05:57:00 +0000 Subject: [PATCH 0924/5615] Bump postgresql from 42.2.6 to 42.2.7 in /modules/junit-jupiter (#1856) --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 74cb75cec0c..83559949614 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -13,7 +13,7 @@ dependencies { exclude(module: 'hamcrest-core') } - testRuntime 'org.postgresql:postgresql:42.2.6' + testRuntime 'org.postgresql:postgresql:42.2.7' testRuntime 'mysql:mysql-connector-java:8.0.17' testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.5.2' } From 33ace195c63bc52baec050f1cf3a31de1f215d10 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 11 Sep 2019 06:34:54 +0000 Subject: [PATCH 0925/5615] Bump aws-java-sdk-sqs from 1.11.628 to 1.11.629 in /modules/localstack (#1858) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 3e065497bf2..0b4faf4aabe 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.628' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.629' } From 185498dfa209b05ba02c01227dfd66d5970d8658 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 11 Sep 2019 07:40:29 +0000 Subject: [PATCH 0926/5615] Bump postgresql from 42.2.6 to 42.2.7 in /modules/spock (#1860) --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 69058b53a92..a51b7edaf32 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -13,6 +13,6 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.3.1' testCompile 'org.apache.httpcomponents:httpclient:4.5.10' - testRuntime 'org.postgresql:postgresql:42.2.6' + testRuntime 'org.postgresql:postgresql:42.2.7' testRuntime 'mysql:mysql-connector-java:8.0.17' } From 4bb2cc231f23b62a67471e5c8408a30aa89325fd Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 11 Sep 2019 08:56:07 +0000 Subject: [PATCH 0927/5615] Bump postgresql from 42.2.6 to 42.2.7 in /examples (#1862) --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 6bf099030ec..bc531b1b2c5 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.28' implementation 'com.squareup.okhttp3:okhttp:3.14.2' implementation 'org.json:json:20180813' - testImplementation 'org.postgresql:postgresql:42.2.6' + testImplementation 'org.postgresql:postgresql:42.2.7' testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.testcontainers:postgresql' } From 5705b0db5a61ad4150a60880036827830f8bda8f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 11 Sep 2019 10:03:53 +0000 Subject: [PATCH 0928/5615] Bump okhttp from 3.14.2 to 3.14.3 in /examples (#1861) --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index bc531b1b2c5..b0247f837cf 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.28' - implementation 'com.squareup.okhttp3:okhttp:3.14.2' + implementation 'com.squareup.okhttp3:okhttp:3.14.3' implementation 'org.json:json:20180813' testImplementation 'org.postgresql:postgresql:42.2.7' testImplementation 'ch.qos.logback:logback-classic:1.2.3' From ea54415727bf819f01fff3ce539b0d21771f4be9 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 11 Sep 2019 10:55:43 +0000 Subject: [PATCH 0929/5615] Bump postgresql from 42.2.6 to 42.2.7 in /modules/postgresql (#1857) --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 1a5980dee55..df75d333ca1 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: JDBC :: PostgreSQL" dependencies { compile project(':jdbc') - testCompile 'org.postgresql:postgresql:42.2.6' + testCompile 'org.postgresql:postgresql:42.2.7' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' } From ee51a486f643feba25932f7158376de46af97f24 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 12 Sep 2019 05:57:35 +0000 Subject: [PATCH 0930/5615] Bump aws-java-sdk-sqs from 1.11.629 to 1.11.630 in /modules/localstack (#1866) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 0b4faf4aabe..d19da1299da 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.629' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.630' } From 520f86cf767b5f4ebd3aac7e510aa61a85aa30d7 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 12 Sep 2019 07:01:45 +0000 Subject: [PATCH 0931/5615] Bump lombok from 1.18.8 to 1.18.10 in /examples (#1867) --- examples/disque-job-queue/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index 78a38d2ba08..bb4aa6edaaa 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.projectlombok:lombok:1.18.8' + compileOnly 'org.projectlombok:lombok:1.18.10' implementation 'biz.paluch.redis:spinach:0.3' implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.google.guava:guava:23.0' diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index 46e587bc39a..487c60e51dd 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -9,7 +9,7 @@ repositories { } dependencies { - compileOnly 'org.projectlombok:lombok:1.18.8' + compileOnly 'org.projectlombok:lombok:1.18.10' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-data-redis' implementation 'org.springframework.boot:spring-boot-starter-web' From 5ecf798b5dbf8d95f48be1b5633f53f8db18676f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2019 05:59:07 +0000 Subject: [PATCH 0932/5615] Bump elasticsearch-rest-client in /modules/elasticsearch (#1870) --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 6ae9ab00dab..e8431190e8b 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,6 +2,6 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') - testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.3.1" + testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.3.2" testCompile "org.elasticsearch.client:transport:6.7.1" } From 34ba4f763cfb4f6a70fcd4de6ed191e9d9fac07d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2019 06:37:45 +0000 Subject: [PATCH 0933/5615] Bump aws-java-sdk-sqs from 1.11.630 to 1.11.631 in /modules/localstack (#1869) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index d19da1299da..f8ff744ed47 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.630' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.631' } From e5ab9ecfa047efdfd1db5805ea4e488956c1154c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2019 05:59:14 +0000 Subject: [PATCH 0934/5615] Bump postgresql from 42.2.7 to 42.2.8 in /modules/junit-jupiter (#1877) --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 83559949614..41e011c07b9 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -13,7 +13,7 @@ dependencies { exclude(module: 'hamcrest-core') } - testRuntime 'org.postgresql:postgresql:42.2.7' + testRuntime 'org.postgresql:postgresql:42.2.8' testRuntime 'mysql:mysql-connector-java:8.0.17' testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.5.2' } From 901724719be11d52e3777a5e21eddf5d7d993429 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2019 06:36:59 +0000 Subject: [PATCH 0935/5615] Bump mariadb-java-client from 2.4.3 to 2.4.4 in /modules/mariadb (#1875) --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index eb778c6c827..692a1636e15 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: JDBC :: MariaDB" dependencies { compile project(':jdbc') - testCompile 'org.mariadb.jdbc:mariadb-java-client:2.4.3' + testCompile 'org.mariadb.jdbc:mariadb-java-client:2.4.4' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.24' From 2b3a49f7c38b729ea594551d20455d8d047f58a5 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2019 07:16:44 +0000 Subject: [PATCH 0936/5615] Bump postgresql from 42.2.7 to 42.2.8 in /examples (#1879) --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index b0247f837cf..3a721e4702b 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.28' implementation 'com.squareup.okhttp3:okhttp:3.14.3' implementation 'org.json:json:20180813' - testImplementation 'org.postgresql:postgresql:42.2.7' + testImplementation 'org.postgresql:postgresql:42.2.8' testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.testcontainers:postgresql' } From d62a9a10377d9a7a81525657b774e7208bd40b5a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2019 08:21:06 +0000 Subject: [PATCH 0937/5615] Bump postgresql from 42.2.7 to 42.2.8 in /modules/spock (#1878) --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index a51b7edaf32..df61a9fefa2 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -13,6 +13,6 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.3.1' testCompile 'org.apache.httpcomponents:httpclient:4.5.10' - testRuntime 'org.postgresql:postgresql:42.2.7' + testRuntime 'org.postgresql:postgresql:42.2.8' testRuntime 'mysql:mysql-connector-java:8.0.17' } From 3ad6adefefe025e267ec5ec15318f87ebe5d5bad Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2019 09:23:29 +0000 Subject: [PATCH 0938/5615] Bump postgresql from 42.2.7 to 42.2.8 in /modules/postgresql (#1876) --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index df75d333ca1..c070ce9ba13 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: JDBC :: PostgreSQL" dependencies { compile project(':jdbc') - testCompile 'org.postgresql:postgresql:42.2.7' + testCompile 'org.postgresql:postgresql:42.2.8' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' } From 158e42f3d4d493fd7ab16c268e88fc6002e12c28 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 17 Sep 2019 06:00:45 +0000 Subject: [PATCH 0939/5615] Bump HikariCP from 3.3.1 to 3.4.0 in /modules/spock (#1882) --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index df61a9fefa2..285618ab116 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -10,7 +10,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') - testCompile 'com.zaxxer:HikariCP:3.3.1' + testCompile 'com.zaxxer:HikariCP:3.4.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.10' testRuntime 'org.postgresql:postgresql:42.2.8' From 7bd756d2a279eabecf55e54ca0454a9b975ff184 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 17 Sep 2019 07:07:43 +0000 Subject: [PATCH 0940/5615] Bump aws-java-sdk-sqs from 1.11.631 to 1.11.632 in /modules/localstack (#1884) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index f8ff744ed47..b96f4ad0048 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.631' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.632' } From 8ce740d2b7b73e56e5855c8ff6208c9c10687fe1 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 17 Sep 2019 08:12:14 +0000 Subject: [PATCH 0941/5615] Bump HikariCP from 3.3.1 to 3.4.0 in /modules/junit-jupiter (#1883) --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 41e011c07b9..291bc27152c 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -6,7 +6,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') - testCompile 'com.zaxxer:HikariCP:3.3.1' + testCompile 'com.zaxxer:HikariCP:3.4.0' testCompile 'redis.clients:jedis:3.1.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.10' testCompile ('org.mockito:mockito-core:3.0.0') { From b84b1b04d27ffd379452740d655381040622c88c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2019 05:57:56 +0000 Subject: [PATCH 0942/5615] Bump aws-java-sdk-sqs from 1.11.632 to 1.11.633 in /modules/localstack (#1885) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index b96f4ad0048..c5f09772048 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.632' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.633' } From 0fcaca7cd3ebc8b3e168617257c26b4e1c842cb7 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2019 05:58:22 +0000 Subject: [PATCH 0943/5615] Bump HikariCP from 3.4.0 to 3.4.1 in /modules/junit-jupiter (#1889) --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 291bc27152c..75367d57720 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -6,7 +6,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') - testCompile 'com.zaxxer:HikariCP:3.4.0' + testCompile 'com.zaxxer:HikariCP:3.4.1' testCompile 'redis.clients:jedis:3.1.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.10' testCompile ('org.mockito:mockito-core:3.0.0') { From 78e78fcd08b873e2ffbd0b9fa46bba364335c298 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 19 Sep 2019 21:46:54 +0100 Subject: [PATCH 0944/5615] Create _headers for docs, to allow cross-origin search --- docs/_headers | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 docs/_headers diff --git a/docs/_headers b/docs/_headers new file mode 100644 index 00000000000..e59f34a29e2 --- /dev/null +++ b/docs/_headers @@ -0,0 +1,2 @@ +/search/search_index.json + Access-Control-Allow-Origin: * From 826b9583cf26fc1ef06fc6003cda554b463b1374 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 20 Sep 2019 05:57:39 +0000 Subject: [PATCH 0945/5615] Bump tomcat-jdbc from 9.0.24 to 9.0.26 in /modules/mariadb (#1895) --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 692a1636e15..9393f6c65c7 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -6,6 +6,6 @@ dependencies { testCompile 'org.mariadb.jdbc:mariadb-java-client:2.4.4' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.24' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.26' testCompile 'org.vibur:vibur-dbcp:22.2' } From 7c07bf989f7f2b3d18c0632b1be63f152807d42a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 20 Sep 2019 06:39:51 +0000 Subject: [PATCH 0946/5615] Bump HikariCP from 3.4.0 to 3.4.1 in /modules/spock (#1888) --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 285618ab116..552fd3883cd 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -10,7 +10,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') - testCompile 'com.zaxxer:HikariCP:3.4.0' + testCompile 'com.zaxxer:HikariCP:3.4.1' testCompile 'org.apache.httpcomponents:httpclient:4.5.10' testRuntime 'org.postgresql:postgresql:42.2.8' From 1b9ab1a0975f8b88360e6177f913dccfd6e66389 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 20 Sep 2019 07:45:10 +0000 Subject: [PATCH 0947/5615] Bump tomcat-jdbc from 9.0.24 to 9.0.26 in /modules/mysql (#1894) --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index d5a366825b9..dbb6747e80f 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -5,7 +5,7 @@ dependencies { testCompile 'mysql:mysql-connector-java:8.0.17' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.24' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.26' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.vibur:vibur-dbcp:22.2' } From 2e0cdb0381c1d348c3edae9ca120a2ac2563806a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 20 Sep 2019 08:47:31 +0000 Subject: [PATCH 0948/5615] Bump aws-java-sdk-sqs from 1.11.633 to 1.11.635 in /modules/localstack (#1896) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index c5f09772048..662e754976e 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.633' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.635' } From 8e871d03c0082474b3a7e304362e97aca4de0740 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2019 06:12:17 +0000 Subject: [PATCH 0949/5615] Bump aws-java-sdk-sqs from 1.11.635 to 1.11.636 in /modules/localstack (#1901) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 662e754976e..eacbc7bb5ae 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,5 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.635' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.636' } From b063082db75918b81158b26a70b34f09c9591182 Mon Sep 17 00:00:00 2001 From: Kai Winter Date: Mon, 23 Sep 2019 22:18:23 +0200 Subject: [PATCH 0950/5615] Obtain official Oracle JDBC driver (#73) (#1899) Also removed two unnecessary dependencies. --- modules/oracle-xe/build.gradle | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/modules/oracle-xe/build.gradle b/modules/oracle-xe/build.gradle index 6b4ab1af763..de1160fa84d 100644 --- a/modules/oracle-xe/build.gradle +++ b/modules/oracle-xe/build.gradle @@ -1,17 +1,9 @@ description = "Testcontainers :: JDBC :: Oracle XE" -repositories { - maven { - url "https://maven.atlassian.com/3rdparty/" - } -} - dependencies { compile project(':jdbc') - testCompile 'com.oracle:ojdbc6:12.1.0.1-atlassian-hosted' + testCompile 'com.oracle.ojdbc:ojdbc8:19.3.0.0' testCompile 'com.zaxxer:HikariCP-java6:2.3.8' testCompile 'commons-dbutils:commons-dbutils:1.6' - testCompile 'org.apache.tomcat:tomcat-jdbc:8.5.4' - testCompile 'org.vibur:vibur-dbcp:9.0' } From 41a009dde90941c587920f3fec685660e948649b Mon Sep 17 00:00:00 2001 From: aosagie Date: Mon, 23 Sep 2019 16:24:19 -0400 Subject: [PATCH 0951/5615] Make LocalStack image configurable (#1873) --- .../testcontainers/utility/TestcontainersConfiguration.java | 4 ++++ docs/features/configuration.md | 3 +++ .../containers/localstack/LocalStackContainer.java | 3 ++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index b7ef772fcf5..21f9bdfb333 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -81,6 +81,10 @@ public String getPulsarImage() { return (String) properties.getOrDefault("pulsar.container.image", "apachepulsar/pulsar"); } + public String getLocalStackImage() { + return (String) properties.getOrDefault("localstack.container.image", "localstack/localstack"); + } + public boolean isDisableChecks() { return Boolean.parseBoolean((String) environmentProperties.getOrDefault("checks.disable", "false")); } diff --git a/docs/features/configuration.md b/docs/features/configuration.md index 2ef7e4997a3..61500cb9cb5 100644 --- a/docs/features/configuration.md +++ b/docs/features/configuration.md @@ -42,6 +42,9 @@ Some companies disallow the usage of Docker Hub, but you can override `*.image` > **kafka.container.image = confluentinc/cp-kafka** > Used by KafkaContainer +> **localstack.container.image = localstack/localstack** +> Used by LocalStack + ## Customizing Ryuk resource reaper > **ryuk.container.image = quay.io/testcontainers/ryuk:0.2.3** diff --git a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java index 1bf0e18627d..d16babb51e8 100644 --- a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java +++ b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java @@ -10,6 +10,7 @@ import org.rnorth.ducttape.Preconditions; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.utility.TestcontainersConfiguration; import java.net.InetAddress; import java.net.UnknownHostException; @@ -37,7 +38,7 @@ public LocalStackContainer() { } public LocalStackContainer(String version) { - super("localstack/localstack:" + version); + super(TestcontainersConfiguration.getInstance().getLocalStackImage() + ":" + version); withFileSystemBind("//var/run/docker.sock", "/var/run/docker.sock"); waitingFor(Wait.forLogMessage(".*Ready\\.\n", 1)); From b6a5248365acb240cd6f6c7d3c1ae38c6c5b3ed7 Mon Sep 17 00:00:00 2001 From: Frederik Hahne Date: Mon, 23 Sep 2019 22:26:43 +0200 Subject: [PATCH 0952/5615] add wescale as happy user (#1887) --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index f62cba3af45..ba99eb6e555 100644 --- a/docs/index.md +++ b/docs/index.md @@ -75,6 +75,7 @@ You can also [check the latest version available on Maven Central](https://searc * [Europace AG](https://tech.europace.de/) - Integration testing for databases and micro services * [Micronaut Data](https://github.com/micronaut-projects/micronaut-data/) - Testing of Micronaut Data JDBC, a database access toolkit * [JHipster](https://www.jhipster.tech/) - Couchbase and Cassandra integration testing +* [wescale](https://www.wescale.com) - Integration testing against HTTP service mocks and various data stores ## License From ec0ab6f1e0c2a826ee21bb06a88508081484892d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 24 Sep 2019 06:04:35 +0000 Subject: [PATCH 0953/5615] Bump commons-dbutils from 1.6 to 1.7 in /modules/oracle-xe (#1905) --- modules/oracle-xe/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/oracle-xe/build.gradle b/modules/oracle-xe/build.gradle index de1160fa84d..1ab61e997af 100644 --- a/modules/oracle-xe/build.gradle +++ b/modules/oracle-xe/build.gradle @@ -5,5 +5,5 @@ dependencies { testCompile 'com.oracle.ojdbc:ojdbc8:19.3.0.0' testCompile 'com.zaxxer:HikariCP-java6:2.3.8' - testCompile 'commons-dbutils:commons-dbutils:1.6' + testCompile 'commons-dbutils:commons-dbutils:1.7' } From d3ba8428d7494a522b49792bc04d528fb70fea4f Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 26 Sep 2019 08:06:31 +0100 Subject: [PATCH 0954/5615] =?UTF-8?q?Reduce=20logs=20in=20EnvironmentAndSy?= =?UTF-8?q?stemPropertyClientProviderStrate=E2=80=A6=20(#1913)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #1912 --- ...dSystemPropertyClientProviderStrategy.java | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java index 195a07a4024..4b02eded72b 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java @@ -1,7 +1,6 @@ package org.testcontainers.dockerclient; import com.github.dockerjava.core.DefaultDockerClientConfig; -import com.github.dockerjava.core.DockerClientConfig; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.SystemUtils; @@ -50,21 +49,7 @@ public void test() throws InvalidConfigurationException { @Override public String getDescription() { - return "Environment variables, system properties and defaults. Resolved: \n" + stringRepresentation(config); - } - - private String stringRepresentation(DockerClientConfig config) { - - if (config == null) { - return ""; - } - - return " dockerHost=" + config.getDockerHost() + "\n" + - " apiVersion='" + config.getApiVersion() + "'\n" + - " registryUrl='" + config.getRegistryUrl() + "'\n" + - " registryUsername='" + config.getRegistryUsername() + "'\n" + - " registryPassword='" + config.getRegistryPassword() + "'\n" + - " registryEmail='" + config.getRegistryEmail() + "'\n" + - " dockerConfig='" + config.toString() + "'\n"; + return "Environment variables, system properties and defaults. Resolved dockerHost=" + + (config != null ? config.getDockerHost() : ""); } } From e717e8375c13fe416fa5860fe06a0caef48d8590 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 27 Sep 2019 05:58:17 +0000 Subject: [PATCH 0955/5615] Bump lettuce-core from 5.1.8.RELEASE to 5.2.0.RELEASE in /examples (#1919) --- examples/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spock/build.gradle b/examples/spock/build.gradle index 147e1eaf079..486107f9b21 100644 --- a/examples/spock/build.gradle +++ b/examples/spock/build.gradle @@ -8,7 +8,7 @@ repositories { } dependencies { - compile "io.lettuce:lettuce-core:5.1.8.RELEASE" + compile "io.lettuce:lettuce-core:5.2.0.RELEASE" testImplementation 'org.spockframework:spock-core:1.2-groovy-2.5' testImplementation 'org.testcontainers:spock' testImplementation 'ch.qos.logback:logback-classic:1.2.3' From a90b120bdd8e4133e10aea862e30f02a22dfca40 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2019 06:25:16 +0000 Subject: [PATCH 0956/5615] Bump okhttp from 3.14.3 to 3.14.4 in /examples (#1925) --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 3a721e4702b..1680aa3b963 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.28' - implementation 'com.squareup.okhttp3:okhttp:3.14.3' + implementation 'com.squareup.okhttp3:okhttp:3.14.4' implementation 'org.json:json:20180813' testImplementation 'org.postgresql:postgresql:42.2.8' testImplementation 'ch.qos.logback:logback-classic:1.2.3' From 9ab6909eda47ad65f6155737b1b869d88ad15850 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 1 Oct 2019 21:57:58 +0200 Subject: [PATCH 0957/5615] =?UTF-8?q?Remove=20`hostname`=20and=20`port`=20?= =?UTF-8?q?from=20JDBC=20examples=20to=20avoid=20confus=E2=80=A6=20(#1786)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remove `hostname` and `port` from JDBC examples to avoid confusion * restore original URL * Explain the host-less URIs --- docs/modules/databases/index.md | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/docs/modules/databases/index.md b/docs/modules/databases/index.md index 22292875307..b0e5335b7e8 100644 --- a/docs/modules/databases/index.md +++ b/docs/modules/databases/index.md @@ -47,29 +47,33 @@ _N.B:_ * _TC needs to be on your application's classpath at runtime for this to work_ * _For Spring Boot you need to specify the driver manually `spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver`_ -**Original URL**: `jdbc:mysql:5.7.22://somehostname:someport/databasename` +**Original URL**: `jdbc:mysql:5.7.22://localhost:3306/databasename` -Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database name will be ignored; you can leave these as-is or set them to any value. +Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database name will be ignored; you can leave these as-is or set them to any value. + +!!! note + We will use `///` (host-less URIs) from now on to emphasis the unimportance of the `host:port` pair. + From Testcontainers' perspective, `jdbc:mysql:5.7.22://localhost:3306/databasename` and `jdbc:mysql:5.7.22:///databasename` is the same URI. ### JDBC URL examples #### Using Testcontainers with a fixed version -`jdbc:tc:mysql:5.6.23://somehostname:someport/databasename` +`jdbc:tc:mysql:5.6.23:///databasename` #### Using PostgreSQL -`jdbc:tc:postgresql:9.6.8://hostname/databasename` +`jdbc:tc:postgresql:9.6.8:///databasename` ### Using PostGIS -`jdbc:tc:postgis:9.6://hostname/databasename` +`jdbc:tc:postgis:9.6:///databasename` ## Using a classpath init script Testcontainers can run an init script after the database container is started, but before your code is given a connection to it. The script must be on the classpath, and is referenced as follows: -`jdbc:tc:mysql:5.7.22://hostname/databasename?TC_INITSCRIPT=somepath/init_mysql.sql` +`jdbc:tc:mysql:5.7.22:///databasename?TC_INITSCRIPT=somepath/init_mysql.sql` This is useful if you have a fixed script for setting up database schema, etc. @@ -77,13 +81,13 @@ This is useful if you have a fixed script for setting up database schema, etc. If the init script path is prefixed `file:`, it will be loaded from a file (relative to the working directory, which will usually be the project root). -`jdbc:tc:mysql:5.7.22://hostname/databasename?TC_INITSCRIPT=file:src/main/resources/init_mysql.sql` +`jdbc:tc:mysql:5.7.22:///databasename?TC_INITSCRIPT=file:src/main/resources/init_mysql.sql` #### Using an init function Instead of running a fixed script for DB setup, it may be useful to call a Java function that you define. This is intended to allow you to trigger database schema migration tools. To do this, add TC_INITFUNCTION to the URL as follows, passing a full path to the class name and method: - `jdbc:tc:mysql:5.7.22://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction` + `jdbc:tc:mysql:5.7.22:///databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction` The init function must be a public static method which takes a `java.sql.Connection` as its only parameter, e.g. ```java @@ -98,7 +102,7 @@ public class JDBCDriverTest { By default database container is being stopped as soon as last connection is closed. There are cases when you might need to start container and keep it running till you stop it explicitly or JVM is shutdown. To do this, add `TC_DAEMON` parameter to the URL as follows: - `jdbc:tc:mysql:5.7.22://hostname/databasename?TC_DAEMON=true` + `jdbc:tc:mysql:5.7.22:///databasename?TC_DAEMON=true` With this parameter database container will keep running even when there're no open connections. @@ -109,7 +113,7 @@ Container can have `tmpfs` mounts for storing data in host memory. This is usefu To pass this option to the container, add `TC_TMPFS` paramater to the URL as follows: - `jdbc:tc:postgresql:9.6.8://hostname/databasename?TC_TMPFS=/testtmpfs:rw` + `jdbc:tc:postgresql:9.6.8:///databasename?TC_TMPFS=/testtmpfs:rw` If you need more than one option, seperate them by comma (e.g. `TC_TMPFS=key:value,key1:value1&other_parameters=foo`). From 9f8a73e18bcf3e648a6c1af4b126051cdd60b837 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 1 Oct 2019 20:58:30 +0100 Subject: [PATCH 0958/5615] Update to latest version in docs --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 39d2c8d8164..9d857ad21a9 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -82,4 +82,4 @@ nav: - contributing.md - contributing_docs.md extra: - latest_version: 1.12.1 + latest_version: 1.12.2 From dbb8472a334bdd76009db95632989a18c78794c0 Mon Sep 17 00:00:00 2001 From: Mitchell Just Date: Wed, 2 Oct 2019 06:25:37 +1000 Subject: [PATCH 0959/5615] Add support for DockerClient event streaming (#1886) Probably fixes #1473 Co-authored-by: Sergei Egorov --- .../okhttp/OkHttpInvocationBuilder.java | 24 ++++++- .../transport/okhttp/UnixSocketFactory.java | 8 +++ .../dockerclient/EventStreamTest.java | 64 +++++++++++++++++++ 3 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 core/src/test/java/org/testcontainers/dockerclient/EventStreamTest.java diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java index a03914d5f07..c7f679eb22b 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java @@ -42,6 +42,8 @@ @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) class OkHttpInvocationBuilder implements InvocationBuilder { + static final ThreadLocal CLOSING = ThreadLocal.withInitial(() -> false); + ObjectMapper objectMapper; OkHttpClient okHttpClient; @@ -101,8 +103,15 @@ public T get(TypeReference typeReference) { @Override public void get(TypeReference typeReference, ResultCallback resultCallback) { - // FIXME - throw new IllegalStateException("doesn't seem to be used in docker-java"); + Request request = requestBuilder + .get() + .build(); + + executeAndStream( + request, + resultCallback, + new JsonSink<>(objectMapper, typeReference, resultCallback) + ); } @Override @@ -292,7 +301,16 @@ protected void executeAndStream(OkHttpClient okHttpClient, Request request, Response response = execute(okHttpClient, request.newBuilder().tag("streaming").build()); BufferedSource source = response.body().source(); ) { - callback.onStart(response); + callback.onStart(() -> { + boolean previous = CLOSING.get(); + CLOSING.set(true); + try { + response.close(); + } finally { + CLOSING.set(previous); + } + }); + sourceConsumer.accept(source); callback.onComplete(); } catch (Exception e) { diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/UnixSocketFactory.java b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/UnixSocketFactory.java index a9eb9f30086..2fbdb936447 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/UnixSocketFactory.java +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/UnixSocketFactory.java @@ -37,6 +37,14 @@ public InputStream getInputStream() { public void close() throws IOException { shutdownInput(); } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (OkHttpInvocationBuilder.CLOSING.get()) { + return 0; + } + return super.read(b, off, len); + } }; } diff --git a/core/src/test/java/org/testcontainers/dockerclient/EventStreamTest.java b/core/src/test/java/org/testcontainers/dockerclient/EventStreamTest.java new file mode 100644 index 00000000000..4d7a4e21133 --- /dev/null +++ b/core/src/test/java/org/testcontainers/dockerclient/EventStreamTest.java @@ -0,0 +1,64 @@ +package org.testcontainers.dockerclient; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.model.Event; +import com.github.dockerjava.core.command.EventsResultCallback; +import org.jetbrains.annotations.NotNull; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.Timeout; +import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; + +import java.io.IOException; +import java.time.Instant; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * Test that event streaming from the {@link DockerClient} works correctly + */ +public class EventStreamTest { + + @Rule + public Timeout timeout = new Timeout(10, TimeUnit.SECONDS); + + /** + * Test that docker events can be streamed from the client. + */ + @Test + public void test() throws IOException, InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + + try ( + GenericContainer container = new GenericContainer<>() + .withCommand("true") + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + ) { + container.start(); + String createdAt = container.getContainerInfo().getCreated(); + + // Request all events between startTime and endTime for the container + try ( + EventsResultCallback response = DockerClientFactory.instance().client().eventsCmd() + .withContainerFilter(container.getContainerId()) + .withEventFilter("create") + .withSince(Instant.parse(createdAt).getEpochSecond() + "") + .exec(new EventsResultCallback() { + @Override + public void onNext(@NotNull Event event) { + // Check that a create event for the container is received + if (event.getId().equals(container.getContainerId()) && event.getStatus().equals("create")) { + latch.countDown(); + } + } + }) + ) { + response.awaitStarted(5, TimeUnit.SECONDS); + latch.await(5, TimeUnit.SECONDS); + } + } + } + +} From 232e9b03f199299f8e01cc60e8fa3e5ca0c549ed Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2019 07:03:58 +0000 Subject: [PATCH 0960/5615] Bump mockito-core from 3.0.0 to 3.1.0 in /modules/junit-jupiter (#1931) --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 75367d57720..e286af1e149 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,7 +9,7 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.4.1' testCompile 'redis.clients:jedis:3.1.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.10' - testCompile ('org.mockito:mockito-core:3.0.0') { + testCompile ('org.mockito:mockito-core:3.1.0') { exclude(module: 'hamcrest-core') } From ca8a35dc5ccf31142919c5fb5635c664d4f89302 Mon Sep 17 00:00:00 2001 From: MariuszCwikla <47976407+MariuszCwikla@users.noreply.github.com> Date: Thu, 3 Oct 2019 08:16:06 +0200 Subject: [PATCH 0961/5615] ConnectionWrapper#rollback(Savepoint) delegates to wrong method #1909 (#1937) * ConnectionWrapper#rollback(Savepoint) delegates to wrong method #1909 * fix whitespaces and use @AllArgsConstructor * Update ConnectionDelegate.java --- .../jdbc/ConnectionDelegate.java | 13 + .../jdbc/ConnectionWrapper.java | 290 +----------------- 2 files changed, 23 insertions(+), 280 deletions(-) create mode 100644 modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionDelegate.java diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionDelegate.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionDelegate.java new file mode 100644 index 00000000000..e7f59b2df4f --- /dev/null +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionDelegate.java @@ -0,0 +1,13 @@ +package org.testcontainers.jdbc; + +import lombok.RequiredArgsConstructor; +import lombok.Value; +import lombok.experimental.Delegate; + +import java.sql.Connection; + +@RequiredArgsConstructor +class ConnectionDelegate implements Connection { + @Delegate + private final Connection delegate; +} diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionWrapper.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionWrapper.java index 5a783c96947..3e1f67c5b86 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionWrapper.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionWrapper.java @@ -1,294 +1,24 @@ package org.testcontainers.jdbc; -import java.sql.*; -import java.util.Map; -import java.util.Properties; -import java.util.concurrent.Executor; +import java.sql.Connection; +import java.sql.SQLException; -/** - * @author richardnorth - */ -class ConnectionWrapper implements Connection { - private final Connection delegate; - private final Runnable closeCallback; - - public ConnectionWrapper(Connection delegate, Runnable closeCallback) { - this.delegate = delegate; - this.closeCallback = closeCallback; - } - - @Override - public Statement createStatement() throws SQLException { - return delegate.createStatement(); - } - - @Override - public PreparedStatement prepareStatement(String sql) throws SQLException { - return delegate.prepareStatement(sql); - } - - @Override - public CallableStatement prepareCall(String sql) throws SQLException { - return delegate.prepareCall(sql); - } - - @Override - public String nativeSQL(String sql) throws SQLException { - return delegate.nativeSQL(sql); - } - - @Override - public void setAutoCommit(boolean autoCommit) throws SQLException { - delegate.setAutoCommit(autoCommit); - } - - @Override - public boolean getAutoCommit() throws SQLException { - return delegate.getAutoCommit(); - } +public class ConnectionWrapper extends ConnectionDelegate { - @Override - public void commit() throws SQLException { - delegate.commit(); - } + private final Runnable closeCallback; - @Override - public void rollback() throws SQLException { - delegate.rollback(); + public ConnectionWrapper(Connection connection, Runnable runnable) { + super(connection); + this.closeCallback = runnable; } @Override public void close() throws SQLException { - delegate.close(); + super.close(); try { - this.closeCallback.run(); - } catch (Exception e) { + closeCallback.run(); + } catch(Exception e) { e.printStackTrace(); } } - - @Override - public boolean isClosed() throws SQLException { - return delegate.isClosed(); - } - - @Override - public DatabaseMetaData getMetaData() throws SQLException { - return delegate.getMetaData(); - } - - @Override - public void setReadOnly(boolean readOnly) throws SQLException { - delegate.setReadOnly(readOnly); - } - - @Override - public boolean isReadOnly() throws SQLException { - return delegate.isReadOnly(); - } - - @Override - public void setCatalog(String catalog) throws SQLException { - delegate.setCatalog(catalog); - } - - @Override - public String getCatalog() throws SQLException { - return delegate.getCatalog(); - } - - @Override - public void setTransactionIsolation(int level) throws SQLException { - delegate.setTransactionIsolation(level); - } - - @Override - public int getTransactionIsolation() throws SQLException { - return delegate.getTransactionIsolation(); - } - - @Override - public SQLWarning getWarnings() throws SQLException { - return delegate.getWarnings(); - } - - @Override - public void clearWarnings() throws SQLException { - delegate.clearWarnings(); - } - - @Override - public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { - return delegate.createStatement(resultSetType, resultSetConcurrency); - } - - @Override - public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { - return delegate.prepareStatement(sql, resultSetType, resultSetConcurrency); - } - - @Override - public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { - return delegate.prepareCall(sql, resultSetType, resultSetConcurrency); - } - - @Override - public Map> getTypeMap() throws SQLException { - return delegate.getTypeMap(); - } - - @Override - public void setTypeMap(Map> map) throws SQLException { - delegate.setTypeMap(map); - } - - @Override - public void setHoldability(int holdability) throws SQLException { - delegate.setHoldability(holdability); - } - - @Override - public int getHoldability() throws SQLException { - return delegate.getHoldability(); - } - - @Override - public Savepoint setSavepoint() throws SQLException { - return delegate.setSavepoint(); - } - - @Override - public Savepoint setSavepoint(String name) throws SQLException { - return delegate.setSavepoint(name); - } - - @Override - public void rollback(Savepoint savepoint) throws SQLException { - delegate.rollback(); - } - - @Override - public void releaseSavepoint(Savepoint savepoint) throws SQLException { - delegate.releaseSavepoint(savepoint); - } - - @Override - public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { - return delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); - } - - @Override - public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { - return delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); - } - - @Override - public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { - return delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); - } - - @Override - public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { - return delegate.prepareStatement(sql, autoGeneratedKeys); - } - - @Override - public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { - return delegate.prepareStatement(sql, columnIndexes); - } - - @Override - public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { - return delegate.prepareStatement(sql, columnNames); - } - - @Override - public Clob createClob() throws SQLException { - return delegate.createClob(); - } - - @Override - public Blob createBlob() throws SQLException { - return delegate.createBlob(); - } - - @Override - public NClob createNClob() throws SQLException { - return delegate.createNClob(); - } - - @Override - public SQLXML createSQLXML() throws SQLException { - return delegate.createSQLXML(); - } - - @Override - public boolean isValid(int timeout) throws SQLException { - return delegate.isValid(timeout); - } - - @Override - public void setClientInfo(String name, String value) throws SQLClientInfoException { - delegate.setClientInfo(name, value); - } - - @Override - public void setClientInfo(Properties properties) throws SQLClientInfoException { - delegate.setClientInfo(properties); - } - - @Override - public String getClientInfo(String name) throws SQLException { - return delegate.getClientInfo(name); - } - - @Override - public Properties getClientInfo() throws SQLException { - return delegate.getClientInfo(); - } - - @Override - public Array createArrayOf(String typeName, Object[] elements) throws SQLException { - return delegate.createArrayOf(typeName, elements); - } - - @Override - public Struct createStruct(String typeName, Object[] attributes) throws SQLException { - return delegate.createStruct(typeName, attributes); - } - - @Override - public void setSchema(String schema) throws SQLException { - delegate.setSchema(schema); - } - - @Override - public String getSchema() throws SQLException { - return delegate.getSchema(); - } - - @Override - public void abort(Executor executor) throws SQLException { - delegate.abort(executor); - } - - @Override - public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { - delegate.setNetworkTimeout(executor, milliseconds); - } - - @Override - public int getNetworkTimeout() throws SQLException { - return delegate.getNetworkTimeout(); - } - - @Override - public T unwrap(Class iface) throws SQLException { - return delegate.unwrap(iface); - } - - @Override - public boolean isWrapperFor(Class iface) throws SQLException { - return delegate.isWrapperFor(iface); - } } From 1beff7ca8c19f7d62e68fecc0019aacddd90d8fe Mon Sep 17 00:00:00 2001 From: Guillaume Le Floch Date: Thu, 3 Oct 2019 15:17:44 +0200 Subject: [PATCH 0962/5615] Localstack: fix typo in stepfunctions service name (#1930) --- .../containers/localstack/LocalStackContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java index d16babb51e8..a5005abe239 100644 --- a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java +++ b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java @@ -134,7 +134,7 @@ public enum Service { CLOUDWATCH("cloudwatch", 4582), SSM("ssm", 4583), SECRETSMANAGER("secretsmanager", 4584), - STEPFUNCTIONS("stepsfunctions", 4585), + STEPFUNCTIONS("stepfunctions", 4585), CLOUDWATCHLOGS("cloudwatchlogs", 4586), STS("sts", 4592), IAM("iam", 4593); From 15140c5320ba726447c303aa51bbda467e9f9bb9 Mon Sep 17 00:00:00 2001 From: Pavel Popov Date: Thu, 3 Oct 2019 18:57:41 +0300 Subject: [PATCH 0963/5615] Add cockroach db support (#499) Co-authored-by: Kevin Wittek Co-authored-by: Richard North --- docs/modules/databases/cockroachdb.md | 26 +++++++ mkdocs.yml | 1 + modules/cockroachdb/build.gradle | 5 ++ .../containers/CockroachContainer.java | 78 +++++++++++++++++++ .../CockroachContainerProvider.java | 18 +++++ ...s.containers.JdbcDatabaseContainerProvider | 1 + modules/jdbc-test/build.gradle | 1 + .../testcontainers/jdbc/JDBCDriverTest.java | 3 +- .../junit/SimpleCockroachDBTest.java | 44 +++++++++++ .../junit/SimplePostgreSQLTest.java | 7 ++ 10 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 docs/modules/databases/cockroachdb.md create mode 100644 modules/cockroachdb/build.gradle create mode 100644 modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java create mode 100644 modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainerProvider.java create mode 100644 modules/cockroachdb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider create mode 100644 modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleCockroachDBTest.java diff --git a/docs/modules/databases/cockroachdb.md b/docs/modules/databases/cockroachdb.md new file mode 100644 index 00000000000..d9e6def4e48 --- /dev/null +++ b/docs/modules/databases/cockroachdb.md @@ -0,0 +1,26 @@ +# CockroachDB Module + +!!! note + This module is INCUBATING. While it is ready for use and operational in the current version of Testcontainers, it is possible that it may receive breaking changes in the future. See [our contributing guidelines](/contributing/#incubating-modules) for more information on our incubating modules policy. + +See [Database containers](./index.md) for documentation and usage that is common to all relational database container types. + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:cockroachdb:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + cockroachdb + {{latest_version}} + test + +``` + +!!! hint + Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency. diff --git a/mkdocs.yml b/mkdocs.yml index 9d857ad21a9..976c1b7c95f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -40,6 +40,7 @@ nav: - Databases: - modules/databases/index.md - modules/databases/cassandra.md + - modules/databases/cockroachdb.md - modules/databases/couchbase.md - modules/databases/clickhouse.md - modules/databases/db2.md diff --git a/modules/cockroachdb/build.gradle b/modules/cockroachdb/build.gradle new file mode 100644 index 00000000000..e52f9f3729c --- /dev/null +++ b/modules/cockroachdb/build.gradle @@ -0,0 +1,5 @@ +description = "Testcontainers :: JDBC :: CockroachDB" + +dependencies { + compile project(':jdbc') +} diff --git a/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java b/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java new file mode 100644 index 00000000000..fb13ced60d7 --- /dev/null +++ b/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java @@ -0,0 +1,78 @@ +package org.testcontainers.containers; + +import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; + +import java.time.Duration; + +public class CockroachContainer extends JdbcDatabaseContainer { + public static final String NAME = "cockroach"; + public static final String IMAGE = "cockroachdb/cockroach"; + public static final String IMAGE_TAG = "v19.1.1"; + private static final String JDBC_DRIVER_CLASS_NAME = "org.postgresql.Driver"; + private static final String JDBC_URL_PREFIX = "jdbc:postgresql"; + private static final String TEST_QUERY_STRING = "SELECT 1"; + private static final int REST_API_PORT = 8080; + private static final int DB_PORT = 26257; + + private String databaseName = "postgres"; + private String username = "root"; + private String password = ""; + + public CockroachContainer() { + this(IMAGE + ":" + IMAGE_TAG); + } + + public CockroachContainer(final String dockerImageName) { + super(dockerImageName); + + withExposedPorts(REST_API_PORT, DB_PORT); + waitingFor( + new HttpWaitStrategy() + .forPath("/health") + .forPort(REST_API_PORT) + .forStatusCode(200) + .withStartupTimeout(Duration.ofMinutes(1)) + ); + withCommand("start --insecure"); + } + + @Override + public String getDriverClassName() { + return JDBC_DRIVER_CLASS_NAME; + } + + @Override + public String getJdbcUrl() { + return JDBC_URL_PREFIX + "://" + getContainerIpAddress() + ":" + getMappedPort(DB_PORT) + "/" + databaseName; + } + + @Override + public String getUsername() { + return username; + } + + @Override + public String getPassword() { + return password; + } + + @Override + public String getTestQueryString() { + return TEST_QUERY_STRING; + } + + @Override + public CockroachContainer withUsername(String username) { + throw new UnsupportedOperationException("The CockroachDB docker image does not currently support this - please see https://github.com/cockroachdb/cockroach/issues/19826"); + } + + @Override + public CockroachContainer withPassword(String password) { + throw new UnsupportedOperationException("The CockroachDB docker image does not currently support this - please see https://github.com/cockroachdb/cockroach/issues/19826"); + } + + @Override + public CockroachContainer withDatabaseName(final String databaseName) { + throw new UnsupportedOperationException("The CockroachDB docker image does not currently support this - please see https://github.com/cockroachdb/cockroach/issues/19826"); + } +} diff --git a/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainerProvider.java b/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainerProvider.java new file mode 100644 index 00000000000..96cafd54f31 --- /dev/null +++ b/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainerProvider.java @@ -0,0 +1,18 @@ +package org.testcontainers.containers; + +public class CockroachContainerProvider extends JdbcDatabaseContainerProvider { + @Override + public boolean supports(String databaseType) { + return databaseType.equals(CockroachContainer.NAME); + } + + @Override + public JdbcDatabaseContainer newInstance() { + return newInstance(CockroachContainer.IMAGE_TAG); + } + + @Override + public JdbcDatabaseContainer newInstance(String tag) { + return new CockroachContainer(CockroachContainer.IMAGE + ":" + tag); + } +} diff --git a/modules/cockroachdb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider b/modules/cockroachdb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider new file mode 100644 index 00000000000..3b58b4c6799 --- /dev/null +++ b/modules/cockroachdb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider @@ -0,0 +1 @@ +org.testcontainers.containers.CockroachContainerProvider \ No newline at end of file diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 9300ba49a4d..842f2a761eb 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -7,6 +7,7 @@ repositories { dependencies { compile project(':mysql') compile project(':postgresql') + compile project(':cockroachdb') compile project(':mariadb') compile project(':oracle-xe') compile project(':mssqlserver') diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java index 0508a54e13d..2089513df06 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java @@ -65,7 +65,8 @@ public static Iterable data() { {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_MY_CNF=somepath/mariadb_conf_override", EnumSet.of(Options.CustomIniFile)}, {"jdbc:tc:clickhouse://hostname/databasename", EnumSet.of(Options.PmdKnownBroken)}, {"jdbc:tc:sqlserver:2017-CU12://hostname:hostport;databaseName=databasename", EnumSet.noneOf(Options.class)}, - {"jdbc:tc:db2://hostname/databasename", EnumSet.noneOf(Options.class)}, + {"jdbc:tc:cockroach://hostname/databasename", EnumSet.noneOf(Options.class)}, + {"jdbc:tc:db2://hostname/databasename", EnumSet.noneOf(Options.class)} }); } diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleCockroachDBTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleCockroachDBTest.java new file mode 100644 index 00000000000..c21a5ddfaf1 --- /dev/null +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleCockroachDBTest.java @@ -0,0 +1,44 @@ +package org.testcontainers.junit; + +import org.junit.Test; +import org.testcontainers.containers.CockroachContainer; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.logging.Level; +import java.util.logging.LogManager; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; + +public class SimpleCockroachDBTest extends AbstractContainerDatabaseTest { + + static { + // Postgres JDBC driver uses JUL; disable it to avoid annoying, irrelevant, stderr logs during connection testing + LogManager.getLogManager().getLogger("").setLevel(Level.OFF); + } + + @Test + public void testSimple() throws SQLException { + try (CockroachContainer cockroach = new CockroachContainer()) { + cockroach.start(); + + ResultSet resultSet = performQuery(cockroach, "SELECT 1"); + + int resultSetInt = resultSet.getInt(1); + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + } + } + + @Test + public void testExplicitInitScript() throws SQLException { + try (CockroachContainer cockroach = new CockroachContainer() + .withInitScript("somepath/init_postgresql.sql")) { // CockroachDB is expected to be compatible with Postgres + cockroach.start(); + + ResultSet resultSet = performQuery(cockroach, "SELECT foo FROM bar"); + + String firstColumnValue = resultSet.getString(1); + assertEquals("Value from init script should equal real value", "hello world", firstColumnValue); + } + } +} diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimplePostgreSQLTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimplePostgreSQLTest.java index 4624268cb2b..7189e05fb16 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimplePostgreSQLTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimplePostgreSQLTest.java @@ -5,12 +5,19 @@ import java.sql.ResultSet; import java.sql.SQLException; +import java.util.logging.Level; +import java.util.logging.LogManager; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; public class SimplePostgreSQLTest extends AbstractContainerDatabaseTest { + static { + // Postgres JDBC driver uses JUL; disable it to avoid annoying, irrelevant, stderr logs during connection testing + LogManager.getLogManager().getLogger("").setLevel(Level.OFF); + } + @Test public void testSimple() throws SQLException { try (PostgreSQLContainer postgres = new PostgreSQLContainer<>()) { From cc1b86d72482d2f3122f86427eacd10d805e409f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2019 06:03:09 +0000 Subject: [PATCH 0964/5615] Bump mariadb-java-client from 2.4.4 to 2.5.0 in /modules/mariadb (#1942) --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 9393f6c65c7..89449edc9ec 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: JDBC :: MariaDB" dependencies { compile project(':jdbc') - testCompile 'org.mariadb.jdbc:mariadb-java-client:2.4.4' + testCompile 'org.mariadb.jdbc:mariadb-java-client:2.5.0' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.26' From 58e62a26c58fb5d93674de30726d4b4cd8ef547c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2019 07:05:34 +0000 Subject: [PATCH 0965/5615] Bump rest-assured from 4.1.1 to 4.1.2 in /modules/vault (#1943) --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index 41807d2573b..b37a74602ff 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: Vault" dependencies { compile project(':testcontainers') - testCompile 'io.rest-assured:rest-assured:4.1.1' + testCompile 'io.rest-assured:rest-assured:4.1.2' } From cd0b7cb4e1014ea89d6eb5479ba95102c2b9e30a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2019 08:14:17 +0000 Subject: [PATCH 0966/5615] Bump elasticsearch-rest-client in /modules/elasticsearch (#1934) --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index e8431190e8b..11c8b57f07a 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,6 +2,6 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') - testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.3.2" + testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.4.0" testCompile "org.elasticsearch.client:transport:6.7.1" } From 43aa38af157126fac47185fec5262a361fcea863 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2019 09:18:14 +0000 Subject: [PATCH 0967/5615] Bump org.springframework.boot in /examples (#1944) --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index cf3b6567982..9f3aed047b3 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.1.8.RELEASE' + id 'org.springframework.boot' version '2.1.9.RELEASE' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index 487c60e51dd..f6ef68f9866 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.1.8.RELEASE' + id 'org.springframework.boot' version '2.1.9.RELEASE' } apply plugin: 'io.spring.dependency-management' From bc082a84f24ec9fdb0dc06eed7204d61ea45c2db Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 4 Oct 2019 15:20:04 +0100 Subject: [PATCH 0968/5615] =?UTF-8?q?Fix=20for=20streaming=20readBytes=20t?= =?UTF-8?q?imeout=20on=20Windows=20with=20npipe=20strat=E2=80=A6=20(#1947)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../transport/okhttp/NamedPipeSocketFactory.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/NamedPipeSocketFactory.java b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/NamedPipeSocketFactory.java index ce272d6e1a6..d150eedb6a5 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/NamedPipeSocketFactory.java +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/NamedPipeSocketFactory.java @@ -55,16 +55,25 @@ public void connect(SocketAddress endpoint, int timeout) { is = new InputStream() { @Override public int read(byte[] bytes, int off, int len) throws IOException { + if (OkHttpInvocationBuilder.CLOSING.get()) { + return 0; + } return file.read(bytes, off, len); } @Override public int read() throws IOException { + if (OkHttpInvocationBuilder.CLOSING.get()) { + return 0; + } return file.read(); } @Override public int read(byte[] bytes) throws IOException { + if (OkHttpInvocationBuilder.CLOSING.get()) { + return 0; + } return file.read(bytes); } }; From 5aa389af939102f767d3a8f58f3d451239915549 Mon Sep 17 00:00:00 2001 From: Guillaume Le Floch Date: Fri, 4 Oct 2019 16:21:14 +0200 Subject: [PATCH 0969/5615] Automatically set SHM for selenium container, and allow SHM size to be configured (#1751) --- .../containers/BrowserWebDriverContainer.java | 4 ++ .../junit/BrowserWebDriverContainerTest.java | 42 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index b92a4877cd2..32245dee3b0 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -165,6 +165,10 @@ protected void configure() { setCommand("/opt/bin/entry_point.sh"); + if (getShmSize() == null) { + addFileSystemBind("/dev/shm", "/dev/shm", BindMode.READ_WRITE); + } + /* * Some unreliability of the selenium browser containers has been observed, so allow multiple attempts to start. */ diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java index 036262a6bd1..74f2d430383 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java @@ -1,10 +1,16 @@ package org.testcontainers.junit; +import com.github.dockerjava.api.command.InspectContainerResponse; +import org.apache.commons.io.FileUtils; import org.junit.Test; import org.openqa.selenium.chrome.ChromeOptions; import org.testcontainers.containers.BrowserWebDriverContainer; +import java.util.List; +import java.util.stream.Collectors; + import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; public class BrowserWebDriverContainerTest { @@ -39,4 +45,40 @@ public void provideDefaultNoProxyEnvironmentIfNotSet() { assertEquals("no_proxy should be set to default if not already present", "localhost", noProxy); } } + + + @Test + public void createContainerWithShmVolume() { + try ( + BrowserWebDriverContainer webDriverContainer = new BrowserWebDriverContainer() + ) { + webDriverContainer.start(); + + final List mounts = webDriverContainer.getContainerInfo().getMounts(); + assertEquals("Shm mounts present", mounts.size(), 1); + + final InspectContainerResponse.Mount shmMount = mounts.get(0); + assertEquals("Shm mount source is correct", "/dev/shm", shmMount.getSource()); + assertEquals("Shm mount destination is correct", "/dev/shm", shmMount.getDestination().getPath()); + assertEquals("Shm mount mode is correct", shmMount.getMode(), "rw"); + } + } + + @Test + public void createContainerWithoutShmVolume() { + try ( + BrowserWebDriverContainer webDriverContainer = new BrowserWebDriverContainer<>() + .withSharedMemorySize(512 * FileUtils.ONE_MB) + ) { + webDriverContainer.start(); + assertEquals("Shared memory size is configured", 512 * FileUtils.ONE_MB, webDriverContainer.getShmSize()); + + final long shmMountCount = webDriverContainer.getContainerInfo().getMounts() + .stream() + .filter(m -> "/dev/shm".equals(m.getSource())) + .count(); + assertEquals("No shm mounts present", shmMountCount, 0L); + } + } + } From a9aa07cde52c80d37328d0e01eb2da4cd7db8d6c Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Fri, 4 Oct 2019 16:22:03 +0200 Subject: [PATCH 0970/5615] Update Gradle to 5.6.2 (#1510) Co-authored-by: Kevin Wittek Co-authored-by: Richard North --- build.gradle | 7 +++++-- core/build.gradle | 1 + examples/disque-job-queue/build.gradle | 3 ++- examples/spring-boot/build.gradle | 3 ++- gradle/wrapper/gradle-wrapper.jar | Bin 56177 -> 55616 bytes gradle/wrapper/gradle-wrapper.properties | 3 +-- gradlew | 22 +++++++++++++++++++--- gradlew.bat | 18 +++++++++++++++++- 8 files changed, 47 insertions(+), 10 deletions(-) diff --git a/build.gradle b/build.gradle index ecdf48cd3f7..7bbf71af9f9 100644 --- a/build.gradle +++ b/build.gradle @@ -1,8 +1,8 @@ plugins { - id 'com.gradle.build-scan' version '1.16' + id 'com.gradle.build-scan' version '2.4.2' id 'io.franzbecker.gradle-lombok' version '3.1.0' id 'nebula.provided-base' version '3.0.3' - id 'com.github.johnrengelman.shadow' version '2.0.2' + id 'com.github.johnrengelman.shadow' version '5.1.0' id "com.jfrog.bintray" version "1.8.4" apply false } @@ -32,6 +32,9 @@ subprojects { args(srcDir, "-d", outputDir) } } + delombok.onlyIf { + project.sourceSets.main.java.srcDirs.find { it.exists() } + } repositories { jcenter() diff --git a/core/build.gradle b/core/build.gradle index f08e7763b18..f7c4f727069 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -119,6 +119,7 @@ dependencies { testCompile project(':test-support') jarFileTestCompileOnly "org.projectlombok:lombok:${lombok.version}" + jarFileTestAnnotationProcessor "org.projectlombok:lombok:${lombok.version}" jarFileTestCompile 'junit:junit:4.12' jarFileTestCompile 'org.assertj:assertj-core:3.12.2' jarFileTestCompile 'org.ow2.asm:asm-debug-all:5.2' diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index bb4aa6edaaa..fbebb1d8e9f 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -7,7 +7,8 @@ repositories { } dependencies { - compileOnly 'org.projectlombok:lombok:1.18.10' + compileOnly "org.projectlombok:lombok:1.18.10" + annotationProcessor "org.projectlombok:lombok:1.18.10" implementation 'biz.paluch.redis:spinach:0.3' implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.google.guava:guava:23.0' diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index f6ef68f9866..9d2d2db76ad 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -9,7 +9,8 @@ repositories { } dependencies { - compileOnly 'org.projectlombok:lombok:1.18.10' + compileOnly "org.projectlombok:lombok:1.18.10" + annotationProcessor "org.projectlombok:lombok:1.18.10" implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-data-redis' implementation 'org.springframework.boot:spring-boot-starter-web' diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 29953ea141f55e3b8fc691d31b5ca8816d89fa87..5c2d1cf016b3885f6930543d57b744ea8c220a1a 100644 GIT binary patch delta 47360 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 delta 47987 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 P{tQ5B1&<&s7}); Date: Sat, 5 Oct 2019 09:03:22 +0100 Subject: [PATCH 0971/5615] Bump mockito-core from 3.0.0 to 3.1.0 in /core (#1932) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.0.0 to 3.1.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.0.0...v3.1.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index f7c4f727069..0bd3a019931 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -109,7 +109,7 @@ dependencies { testCompile 'redis.clients:jedis:3.1.0' testCompile 'com.rabbitmq:amqp-client:5.7.3' testCompile 'org.mongodb:mongo-java-driver:3.11.0' - testCompile ('org.mockito:mockito-core:3.0.0') { + testCompile ('org.mockito:mockito-core:3.1.0') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From 52420d872c1a2832ab1785719b856e67faed7b5a Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 5 Oct 2019 09:18:22 +0100 Subject: [PATCH 0972/5615] Bump flaky test review dates (#1952) as we've not had time to look at these properly --- .../couchbase/BaseCouchbaseContainerTest.java | 6 +++--- .../org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java | 2 +- .../java/org/testcontainers/containers/ToxiproxyTest.java | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java index 1c5e702727a..19eead1b23a 100644 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java @@ -32,7 +32,7 @@ public abstract class BaseCouchbaseContainerTest extends AbstractCouchbaseTest { public FlakyTestJUnit4RetryRule retry = new FlakyTestJUnit4RetryRule(); @Test - @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1453", reviewDate = "2019-10-01") + @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1453", reviewDate = "2019-12-01") public void shouldInsertDocument() { RawJsonDocument expected = RawJsonDocument.create(ID, DOCUMENT); getBucket().upsert(expected); @@ -41,7 +41,7 @@ public void shouldInsertDocument() { } @Test - @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1453", reviewDate = "2019-10-01") + @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1453", reviewDate = "2019-12-01") public void shouldExecuteN1ql() { getBucket().query(N1qlQuery.simple("INSERT INTO " + TEST_BUCKET + " (KEY, VALUE) VALUES ('" + ID + "', " + DOCUMENT + ")")); @@ -54,7 +54,7 @@ public void shouldExecuteN1ql() { } @Test - @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1453", reviewDate = "2019-10-01") + @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1453", reviewDate = "2019-12-01") public void shouldCreateView() { View view = DefaultView.create(VIEW_NAME, VIEW_FUNCTION); DesignDocument document = DesignDocument.create(VIEW_NAME, Lists.newArrayList(view)); diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java index 38c97686679..fb9794e29a8 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java @@ -20,7 +20,7 @@ public class DatabaseDriverTmpfsTest { public FlakyTestJUnit4RetryRule retry = new FlakyTestJUnit4RetryRule(); @Test - @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1687", reviewDate = "2019-10-01") + @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1687", reviewDate = "2019-12-01") public void tmpfs() throws IOException, InterruptedException, SQLException { final String jdbcUrl = "jdbc:tc:postgresql:9.6.8://hostname/databasename?TC_TMPFS=/testtmpfs:rw"; try (Connection ignored = DriverManager.getConnection(jdbcUrl)) { diff --git a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java index 8fa28431ab5..840165fb8ee 100644 --- a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java +++ b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java @@ -45,7 +45,7 @@ public void testDirect() { } @Test - @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1769", reviewDate = "2019-10-01") + @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1769", reviewDate = "2019-12-01") public void testLatencyViaProxy() throws IOException { // obtainProxyObject { final ToxiproxyContainer.ContainerProxy proxy = toxiproxy.getProxy(redis, 6379); @@ -72,7 +72,7 @@ public void testLatencyViaProxy() throws IOException { } @Test - @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1769", reviewDate = "2019-10-01") + @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1769", reviewDate = "2019-12-01") public void testConnectionCut() { final ToxiproxyContainer.ContainerProxy proxy = toxiproxy.getProxy(redis, 6379); final Jedis jedis = new Jedis(proxy.getContainerIpAddress(), proxy.getProxyPort()); @@ -97,7 +97,7 @@ public void testConnectionCut() { } @Test - @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1769", reviewDate = "2019-10-01") + @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1769", reviewDate = "2019-12-01") public void testMultipleProxiesCanBeCreated() { try (GenericContainer secondRedis = new GenericContainer("redis:5.0.4") .withExposedPorts(6379) From 94c903d755c89340aecb7eeb8f77834c6c88e095 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 6 Oct 2019 08:16:36 +0100 Subject: [PATCH 0973/5615] Fix use of SHM for Selenium containers on Windows (#1948) * Change assertion used for SHM testing to support Windows * Make test resilient to future changes in default mounts by only checking counts of SHM volumes * WIP - experimental * Tweak assertion to cope with /dev/shm/ * Switch to directly adding SHM bind rather than using addFileSystemBind * Simplify assertion code --- .../testcontainers/utility/MountableFile.java | 2 + .../containers/BrowserWebDriverContainer.java | 5 ++- .../junit/BrowserWebDriverContainerTest.java | 39 +++++++++++-------- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java index cd83a81781f..56d5bda3ffa 100644 --- a/core/src/main/java/org/testcontainers/utility/MountableFile.java +++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java @@ -167,7 +167,9 @@ private static String unencodeResourceURIToFilePath(@NotNull final String resour private String resolvePath() { String result = getResourcePath(); + // Special case for Windows if (SystemUtils.IS_OS_WINDOWS && result.startsWith("/")) { + // Remove leading / result = result.substring(1); } diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index 32245dee3b0..9e1a2733a2e 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -1,5 +1,8 @@ package org.testcontainers.containers; +import com.github.dockerjava.api.model.AccessMode; +import com.github.dockerjava.api.model.Bind; +import com.github.dockerjava.api.model.Volume; import com.google.common.collect.ImmutableSet; import com.github.dockerjava.api.command.InspectContainerResponse; @@ -166,7 +169,7 @@ protected void configure() { setCommand("/opt/bin/entry_point.sh"); if (getShmSize() == null) { - addFileSystemBind("/dev/shm", "/dev/shm", BindMode.READ_WRITE); + this.getBinds().add(new Bind("/dev/shm", new Volume("/dev/shm"), AccessMode.rw)); } /* diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java index 74f2d430383..7b7d3a46733 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java @@ -4,13 +4,14 @@ import org.apache.commons.io.FileUtils; import org.junit.Test; import org.openqa.selenium.chrome.ChromeOptions; +import org.openqa.selenium.firefox.FirefoxOptions; import org.testcontainers.containers.BrowserWebDriverContainer; import java.util.List; -import java.util.stream.Collectors; +import static java.util.Collections.emptyList; +import static java.util.stream.Collectors.toList; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; public class BrowserWebDriverContainerTest { @@ -51,34 +52,40 @@ public void provideDefaultNoProxyEnvironmentIfNotSet() { public void createContainerWithShmVolume() { try ( BrowserWebDriverContainer webDriverContainer = new BrowserWebDriverContainer() + .withCapabilities(new FirefoxOptions()) ) { webDriverContainer.start(); - final List mounts = webDriverContainer.getContainerInfo().getMounts(); - assertEquals("Shm mounts present", mounts.size(), 1); + final List shmVolumes = shmVolumes(webDriverContainer); - final InspectContainerResponse.Mount shmMount = mounts.get(0); - assertEquals("Shm mount source is correct", "/dev/shm", shmMount.getSource()); - assertEquals("Shm mount destination is correct", "/dev/shm", shmMount.getDestination().getPath()); - assertEquals("Shm mount mode is correct", shmMount.getMode(), "rw"); + assertEquals("Only one shm mount present", 1, shmVolumes.size()); + assertEquals("Shm mount source is correct", "/dev/shm", shmVolumes.get(0).getSource()); + assertEquals("Shm mount mode is correct", "rw", shmVolumes.get(0).getMode()); } } @Test public void createContainerWithoutShmVolume() { try ( - BrowserWebDriverContainer webDriverContainer = new BrowserWebDriverContainer<>() - .withSharedMemorySize(512 * FileUtils.ONE_MB) + BrowserWebDriverContainer webDriverContainer = new BrowserWebDriverContainer<>() + .withSharedMemorySize(512 * FileUtils.ONE_MB) + .withCapabilities(new FirefoxOptions()) ) { webDriverContainer.start(); - assertEquals("Shared memory size is configured", 512 * FileUtils.ONE_MB, webDriverContainer.getShmSize()); - final long shmMountCount = webDriverContainer.getContainerInfo().getMounts() - .stream() - .filter(m -> "/dev/shm".equals(m.getSource())) - .count(); - assertEquals("No shm mounts present", shmMountCount, 0L); + assertEquals("Shared memory size is configured", + 512 * FileUtils.ONE_MB, + webDriverContainer.getShmSize()); + + assertEquals("No shm mounts present", emptyList(), shmVolumes(webDriverContainer)); } } + private List shmVolumes(final BrowserWebDriverContainer container) { + return container.getContainerInfo().getMounts() + .stream() + // destination path is always /dev/shm + .filter(m -> m.getDestination().getPath().equals("/dev/shm")) + .collect(toList()); + } } From 343bcd2f554b31b5ba04acd0212675192eb3fbef Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 6 Oct 2019 17:06:25 +0200 Subject: [PATCH 0974/5615] Lock the version of AWS S3 SDK in the build script. (#1955) see https://github.com/myniva/gradle-s3-build-cache/issues/20 --- settings.gradle | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/settings.gradle b/settings.gradle index 72e7d312555..e8fb1efe910 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,12 +1,18 @@ buildscript { - repositories { - maven { - url "https://plugins.gradle.org/m2/" + repositories { + maven { + url "https://plugins.gradle.org/m2/" + } + } + dependencies { + classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.9.0" + } + + configurations.classpath { + resolutionStrategy { + force 'com.amazonaws:aws-java-sdk-s3:1.11.582' + } } - } - dependencies { - classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.9.0" - } } apply plugin: 'ch.myniva.s3-build-cache' From 9a50a771894fe8e966fd4b95eb3ba8b9a781473b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2019 06:38:59 +0000 Subject: [PATCH 0975/5615] Bump cucumber-java from 4.7.2 to 4.7.4 in /examples (#1958) --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 7e73852ca1d..61dfe948d4c 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' - implementation 'io.cucumber:cucumber-java:4.7.2' + implementation 'io.cucumber:cucumber-java:4.7.4' testImplementation 'io.cucumber:cucumber-junit:4.7.2' testImplementation 'org.testcontainers:selenium' } From a46f3021135cb5a69bcd3948141d10bfe4b56f5d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2019 07:30:50 +0000 Subject: [PATCH 0976/5615] Bump cucumber-junit from 4.7.2 to 4.7.4 in /examples (#1957) --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 61dfe948d4c..7e536842bf0 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' implementation 'io.cucumber:cucumber-java:4.7.4' - testImplementation 'io.cucumber:cucumber-junit:4.7.2' + testImplementation 'io.cucumber:cucumber-junit:4.7.4' testImplementation 'org.testcontainers:selenium' } From b08308f46f5d0403d779f785cef5a84e1569658b Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 8 Oct 2019 14:57:34 -0500 Subject: [PATCH 0977/5615] Always copy to "/" (root). Fixes #1954 (#1956) --- .../containers/GenericContainer.java | 7 ++----- .../junit/CopyFileToContainerTest.java | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index baf79474aaa..e3a16c2dacb 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -1154,16 +1154,13 @@ public void copyFileToContainer(Transferable transferable, String containerPath) ) { tarArchive.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); - int lastSlashIndex = StringUtils.removeEnd(containerPath, "/").lastIndexOf("/"); - String extractArchiveTo = containerPath.substring(0, lastSlashIndex + 1); - String pathInArchive = containerPath.substring(lastSlashIndex + 1); - transferable.transferTo(tarArchive, pathInArchive); + transferable.transferTo(tarArchive, containerPath); tarArchive.finish(); dockerClient .copyArchiveToContainerCmd(containerId) .withTarInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray())) - .withRemotePath(extractArchiveTo) + .withRemotePath("/") .exec(); } } diff --git a/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java b/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java index 0641bc7d50d..ed6ee561505 100644 --- a/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java @@ -59,4 +59,18 @@ public void shouldUseCopyOnlyWithReadOnlyClasspathResources() { assertFalse("uses mount for read-only with Selinux", copyMap.containsValue("/readOnlyShared")); assertFalse("uses mount for read-write", copyMap.containsValue("/readWrite")); } + + @Test + public void shouldCreateFoldersStructureWithCopy() throws Exception { + String resource = "/test_copy_to_container.txt"; + try ( + GenericContainer container = new GenericContainer<>() + .withCommand("sleep", "3000") + .withClasspathResourceMapping(resource, "/a/b/c/file", BindMode.READ_ONLY) + ) { + container.start(); + String filesList = container.execInContainer("ls", "/a/b/c/").getStdout(); + assertTrue("file list contains the file", filesList.contains("file")); + } + } } From e2272021b6ffaf329211349f0daa2b6b3c4cd584 Mon Sep 17 00:00:00 2001 From: worldtiki Date: Tue, 8 Oct 2019 20:59:12 +0100 Subject: [PATCH 0978/5615] Add a simple example with how to use the mock server container (#1950) * Add a simple example with how to use the mock server container * Move examples for mockserver to unit tests --- docs/modules/mockserver.md | 17 +++++ .../containers/MockServerContainerTest.java | 63 +++++++++++-------- 2 files changed, 55 insertions(+), 25 deletions(-) diff --git a/docs/modules/mockserver.md b/docs/modules/mockserver.md index b6c0cd92457..051cecf076e 100644 --- a/docs/modules/mockserver.md +++ b/docs/modules/mockserver.md @@ -1,5 +1,21 @@ # Mockserver Module +Mock Server can be used to mock HTTP services by matching requests against user-defined expectations. + +## Usage example + +The following example shows how to start Mockserver. + + +[Creating a MockServer container](../../modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java) inside_block:creatingProxy + + +And how to set a simple expectation using the Java MockServerClient. + + +[Setting a simple expectation](../../modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java) inside_block:testSimpleExpectation + + ## Adding this module to your project dependencies Add the following dependency to your `pom.xml`/`build.gradle` file: @@ -16,3 +32,4 @@ testCompile "org.testcontainers:mockserver:{{latest_version}}" test ``` + diff --git a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java index 8cb8ac88448..be1495a04c7 100644 --- a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java +++ b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java @@ -1,6 +1,7 @@ package org.testcontainers.containers; import lombok.Cleanup; +import org.junit.Rule; import org.junit.Test; import org.mockserver.client.MockServerClient; @@ -11,12 +12,43 @@ import java.net.URLConnection; import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.equalTo; import static org.mockserver.model.HttpRequest.request; import static org.mockserver.model.HttpResponse.response; import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; public class MockServerContainerTest { + // creatingProxy { + @Rule + public MockServerContainer mockServer = new MockServerContainer(); + // } + + private static String responseFromMockserver(MockServerContainer mockServer, String path) throws IOException { + URLConnection urlConnection = new URL(mockServer.getEndpoint() + path).openConnection(); + @Cleanup BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); + return reader.readLine(); + } + + @Test + public void shouldReturnExpectation() throws Exception { + // testSimpleExpectation { + new MockServerClient(mockServer.getContainerIpAddress(), mockServer.getServerPort()) + .when(request() + .withPath("/person") + .withQueryStringParameter("name", "peter")) + .respond(response() + .withBody("Peter the person!")); + + // ...a GET request to '/person?name=peter' returns "Peter the person!" + // } + + assertThat("Expectation returns expected response body", + responseFromMockserver(mockServer, "/person?name=peter"), + containsString("Peter the person") + ); + } + @Test public void shouldCallActualMockserverVersion() throws Exception { String actualVersion = MockServerClient.class.getPackage().getImplementationVersion(); @@ -25,33 +57,14 @@ public void shouldCallActualMockserverVersion() throws Exception { String expectedBody = "Hello World!"; - assertThat("MockServer returns correct result", - responseFromMockserver(mockServer, expectedBody, "/hello"), - containsString(expectedBody) - ); - } - } + new MockServerClient(mockServer.getContainerIpAddress(), mockServer.getServerPort()) + .when(request().withPath("/hello")) + .respond(response().withBody(expectedBody)); - @Test - public void shouldCallDefaultMockserverVersion() throws Exception { - try (MockServerContainer mockServerDefault = new MockServerContainer()) { - mockServerDefault.start(); - String expectedBody = "Hello Default World!"; - - assertThat("MockServer returns correct result for default constructor", - responseFromMockserver(mockServerDefault, expectedBody, "/hellodefault"), - containsString(expectedBody) + assertThat("MockServer returns correct result", + responseFromMockserver(mockServer, "/hello"), + equalTo(expectedBody) ); } } - - private static String responseFromMockserver(MockServerContainer mockServer, String expectedBody, String path) throws IOException { - new MockServerClient(mockServer.getContainerIpAddress(), mockServer.getServerPort()) - .when(request(path)) - .respond(response(expectedBody)); - - URLConnection urlConnection = new URL(mockServer.getEndpoint() + path).openConnection(); - @Cleanup BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); - return reader.readLine(); - } } From ce759bd869de75e53e6db61b4d0a36cf881aab9e Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 8 Oct 2019 21:18:17 +0100 Subject: [PATCH 0979/5615] Switch travis build JDK to openjdk8 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d7d5f94270d..85bef8cff9c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ if: branch = master OR tag is present language: java jdk: - - oraclejdk8 + - openjdk8 sudo: required services: From 11fa0f9d8907811aa62847274f5fc1ad472e0a8e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2019 08:26:19 +0100 Subject: [PATCH 0980/5615] Bump gson from 2.8.5 to 2.8.6 in /examples (#1960) Bumps [gson](https://github.com/google/gson) from 2.8.5 to 2.8.6. - [Release notes](https://github.com/google/gson/releases) - [Changelog](https://github.com/google/gson/blob/master/CHANGELOG.md) - [Commits](https://github.com/google/gson/compare/gson-parent-2.8.5...gson-parent-2.8.6) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> Co-authored-by: Richard North --- examples/disque-job-queue/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index fbebb1d8e9f..1f4e257cad1 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly "org.projectlombok:lombok:1.18.10" annotationProcessor "org.projectlombok:lombok:1.18.10" implementation 'biz.paluch.redis:spinach:0.3' - implementation 'com.google.code.gson:gson:2.8.5' + implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.guava:guava:23.0' testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.mockito:mockito-all:1.10.19' diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index e68ccfea24f..e339878813c 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -9,7 +9,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.28' implementation 'redis.clients:jedis:3.1.0' - implementation 'com.google.code.gson:gson:2.8.5' + implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'ch.qos.logback:logback-classic:1.2.3' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 8b1a5638243..727c1649b07 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -9,7 +9,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.28' implementation 'redis.clients:jedis:3.1.0' - implementation 'com.google.code.gson:gson:2.8.5' + implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'junit:junit:4.12' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 26a437909fe..250eb755632 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -9,7 +9,7 @@ repositories { dependencies { implementation 'redis.clients:jedis:3.1.0' - implementation 'com.google.code.gson:gson:2.8.5' + implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.28' From 8104c0c5c92d4f24003fc6a7f8dcc18835488c6e Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 10 Oct 2019 21:54:22 +0100 Subject: [PATCH 0981/5615] Update .travis.yml Add info level logging --- .travis.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 85bef8cff9c..810b5827757 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,29 +15,29 @@ jobs: include: - stage: test env: [ NAME=core ] - script: ./gradlew testcontainers:check javadoc --scan --no-daemon + script: ./gradlew testcontainers:check javadoc --scan --no-daemon -i - stage: test env: [ NAME=core ] jdk: openjdk11 - script: ./gradlew testcontainers:check --scan --no-daemon + script: ./gradlew testcontainers:check --scan --no-daemon -i - stage: test env: [ NAME=core ] jdk: openjdk12 - script: ./gradlew testcontainers:check --scan --no-daemon + script: ./gradlew testcontainers:check --scan --no-daemon -i - env: [ NAME=selenium ] - script: ./gradlew selenium:check --scan --no-daemon + script: ./gradlew selenium:check --scan --no-daemon -i - env: [ NAME=modules ] - script: ./gradlew check -x testcontainers:check -x selenium:check -x jdbc-test:check --scan --no-daemon + script: ./gradlew check -x testcontainers:check -x selenium:check -x jdbc-test:check --scan --no-daemon -i - env: [ NAME=jdbc ] - script: ./gradlew jdbc-test:check --scan --no-daemon + script: ./gradlew jdbc-test:check --scan --no-daemon -i - env: [ NAME=examples ] - script: cd examples && ../gradlew check --no-daemon + script: cd examples && ../gradlew check --no-daemon -i # Run Docker-in-Docker tests inside Alpine - env: [ NAME="docker-in-alpine-docker" ] @@ -49,7 +49,7 @@ jobs: -v "$(pwd)":"$(pwd)" \ -w "$(pwd)" \ openjdk:8-jdk-alpine \ - ./gradlew --no-daemon testcontainers:test --tests '*GenericContainerRuleTest' --scan + ./gradlew --no-daemon testcontainers:test --tests '*GenericContainerRuleTest' --scan -i - stage: deploy sudo: false @@ -58,7 +58,7 @@ jobs: script: skip deploy: provider: script - script: ./gradlew -Pversion=$TRAVIS_TAG release --scan --no-daemon + script: ./gradlew -Pversion=$TRAVIS_TAG release --scan --no-daemon -i on: tags: true branch: master From 32e8c53cf0d8bebf6d0056d5e6c12dbce499e599 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 12 Oct 2019 16:07:05 +0100 Subject: [PATCH 0982/5615] Fix docker-compose issue in Travis build --- .travis.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.travis.yml b/.travis.yml index 810b5827757..02196e895fe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,16 @@ jdk: sudo: required services: - docker + +env: + - DOCKER_COMPOSE_VERSION=1.24.1 + +# Fix for https://travis-ci.community/t/problematic-docker-compose-version-1-23-1/5362/3 +before_install: + - sudo rm /usr/local/bin/docker-compose + - curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose + - chmod +x docker-compose + - sudo mv docker-compose /usr/local/bin install: - ./gradlew build -x check --scan --no-daemon From 92b751d11e40a8ca7f54ec92ebcc8831dff79d04 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 12 Oct 2019 16:38:17 +0100 Subject: [PATCH 0983/5615] Update .travis.yml --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 02196e895fe..e069ed68bc6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,12 +9,12 @@ services: - docker env: - - DOCKER_COMPOSE_VERSION=1.24.1 + - DOCKER_COMPOSE_VERSION= # Fix for https://travis-ci.community/t/problematic-docker-compose-version-1-23-1/5362/3 before_install: - sudo rm /usr/local/bin/docker-compose - - curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose + - curl -L https://github.com/docker/compose/releases/download/1.24.1/docker-compose-Linux-x86_64 > docker-compose - chmod +x docker-compose - sudo mv docker-compose /usr/local/bin From 1a5cd16421c9747bc303ccbfcb704420b78a5d47 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 12 Oct 2019 16:38:28 +0100 Subject: [PATCH 0984/5615] Update .travis.yml --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index e069ed68bc6..d21033b942a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,9 +7,6 @@ jdk: sudo: required services: - docker - -env: - - DOCKER_COMPOSE_VERSION= # Fix for https://travis-ci.community/t/problematic-docker-compose-version-1-23-1/5362/3 before_install: From a502c8c3b21769c3dfa483bd7967339cd083eb6f Mon Sep 17 00:00:00 2001 From: Steve Hill Date: Sat, 12 Oct 2019 09:53:32 -0700 Subject: [PATCH 0985/5615] Update example dependency specified in junit_5.md (#1817) `testCompile` is deprecated and has been superseded by `testImplementation` --- docs/test_framework_integration/junit_5.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/test_framework_integration/junit_5.md b/docs/test_framework_integration/junit_5.md index 442fb485185..371240a8d54 100644 --- a/docs/test_framework_integration/junit_5.md +++ b/docs/test_framework_integration/junit_5.md @@ -126,7 +126,7 @@ This extension has only be tested with sequential test execution. Using it with Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:junit-jupiter:{{latest_version}}" +testImplementation "org.testcontainers:junit-jupiter:{{latest_version}}" ``` ```xml tab='Maven' From fcf3b0f642d0a80cf752ce2d4ac624baf449bc64 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 12 Oct 2019 17:59:19 +0100 Subject: [PATCH 0986/5615] Bump jna-platform from 5.3.1 to 5.4.0 in /core (#1796) Bumps [jna-platform](https://github.com/java-native-access/jna) from 5.3.1 to 5.4.0. - [Release notes](https://github.com/java-native-access/jna/releases) - [Changelog](https://github.com/java-native-access/jna/blob/master/CHANGES.md) - [Commits](https://github.com/java-native-access/jna/compare/5.3.1...5.4.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> Co-authored-by: Richard North --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 0bd3a019931..45fdf5d1b0b 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -76,7 +76,7 @@ dependencies { exclude(group: "log4j", module: "log4j") } - compile "net.java.dev.jna:jna-platform:5.3.1" + compile "net.java.dev.jna:jna-platform:5.4.0" shaded ('org.scala-sbt.ipcsocket:ipcsocket:1.0.0') { exclude(group: "net.java.dev.jna") From ce7a57200f1f889c18d384ab1dad438edfa14efc Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 13 Oct 2019 18:12:03 +0100 Subject: [PATCH 0987/5615] Bump okhttp from 3.14.2 to 3.14.4 in /core (#1924) Bumps [okhttp](https://github.com/square/okhttp) from 3.14.2 to 3.14.4. - [Release notes](https://github.com/square/okhttp/releases) - [Changelog](https://github.com/square/okhttp/blob/master/docs/changelog_3x.md) - [Commits](https://github.com/square/okhttp/compare/parent-3.14.2...parent-3.14.4) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> Co-authored-by: Richard North --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 45fdf5d1b0b..1d3ca56f89f 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -97,7 +97,7 @@ dependencies { shaded "org.yaml:snakeyaml:1.25" - shaded 'com.squareup.okhttp3:okhttp:3.14.2' + shaded 'com.squareup.okhttp3:okhttp:3.14.4' shaded 'org.glassfish.main.external:trilead-ssh2-repackaged:4.1.2' From fbdfeddeed79d818ab9dd2d0b1ba18ba0453bbb0 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2019 18:55:42 +0000 Subject: [PATCH 0988/5615] Bump mongo-java-driver from 3.11.0 to 3.11.1 in /core (#1970) --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 1d3ca56f89f..2b631f46bce 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -108,7 +108,7 @@ dependencies { testCompile 'org.apache.httpcomponents:httpclient:4.5.9' testCompile 'redis.clients:jedis:3.1.0' testCompile 'com.rabbitmq:amqp-client:5.7.3' - testCompile 'org.mongodb:mongo-java-driver:3.11.0' + testCompile 'org.mongodb:mongo-java-driver:3.11.1' testCompile ('org.mockito:mockito-core:3.1.0') { exclude(module: 'hamcrest-core') } From cea37a8219176db8d568d2049d9cb27e38451ebb Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2019 19:55:54 +0100 Subject: [PATCH 0989/5615] Bump tomcat-jdbc from 9.0.26 to 9.0.27 in /modules/mariadb (#1967) Bumps tomcat-jdbc from 9.0.26 to 9.0.27. Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 89449edc9ec..9c2150a51cd 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -6,6 +6,6 @@ dependencies { testCompile 'org.mariadb.jdbc:mariadb-java-client:2.5.0' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.26' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.27' testCompile 'org.vibur:vibur-dbcp:22.2' } From 69cd9ebc9780925f2fc2124a925faddb6ccc015c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2019 20:16:48 +0100 Subject: [PATCH 0990/5615] Bump mariadb-java-client from 2.5.0 to 2.5.1 in /modules/mariadb (#1983) Bumps [mariadb-java-client](https://github.com/MariaDB/mariadb-connector-j) from 2.5.0 to 2.5.1. - [Release notes](https://github.com/MariaDB/mariadb-connector-j/releases) - [Commits](https://github.com/MariaDB/mariadb-connector-j/compare/2.5.0...2.5.1) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 9c2150a51cd..f6d65f4a5f0 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: JDBC :: MariaDB" dependencies { compile project(':jdbc') - testCompile 'org.mariadb.jdbc:mariadb-java-client:2.5.0' + testCompile 'org.mariadb.jdbc:mariadb-java-client:2.5.1' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.27' From c6e76b4e00b895a7ecaeee08efd9b4365178132e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2019 20:17:11 +0100 Subject: [PATCH 0991/5615] Bump cucumber-junit from 4.7.4 to 4.8.0 in /examples (#1973) Bumps [cucumber-junit](https://github.com/cucumber/cucumber-jvm) from 4.7.4 to 4.8.0. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/master/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v4.7.4...v4.8.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 7e536842bf0..7b489671506 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' implementation 'io.cucumber:cucumber-java:4.7.4' - testImplementation 'io.cucumber:cucumber-junit:4.7.4' + testImplementation 'io.cucumber:cucumber-junit:4.8.0' testImplementation 'org.testcontainers:selenium' } From 33e26deb6c524cff82078e2f21ba11540cd4f51d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2019 20:17:28 +0100 Subject: [PATCH 0992/5615] =?UTF-8?q?Bump=20mysql-connector-java=20from=20?= =?UTF-8?q?8.0.17=20to=208.0.18=20in=20/modules/mys=E2=80=A6=20(#1968)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.17 to 8.0.18. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/commits) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index dbb6747e80f..3c856aca872 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: JDBC :: MySQL" dependencies { compile project(':jdbc') - testCompile 'mysql:mysql-connector-java:8.0.17' + testCompile 'mysql:mysql-connector-java:8.0.18' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.26' testCompile 'commons-dbutils:commons-dbutils:1.7' From 0f55de13309ad25266dd981d37e5bcda19d010fe Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2019 20:17:36 +0100 Subject: [PATCH 0993/5615] Bump tomcat-jdbc from 9.0.26 to 9.0.27 in /modules/mysql (#1969) Bumps tomcat-jdbc from 9.0.26 to 9.0.27. Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 3c856aca872..7704f56c54f 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -5,7 +5,7 @@ dependencies { testCompile 'mysql:mysql-connector-java:8.0.18' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.26' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.27' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.vibur:vibur-dbcp:22.2' } From 3bff1d0b614b23b8a56292aa5de77637f5860198 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2019 20:18:11 +0100 Subject: [PATCH 0994/5615] =?UTF-8?q?Bump=20mysql-connector-java=20from=20?= =?UTF-8?q?8.0.17=20to=208.0.18=20in=20/modules/spo=E2=80=A6=20(#1972)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.17 to 8.0.18. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/commits) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 552fd3883cd..d84e8776af3 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -14,5 +14,5 @@ dependencies { testCompile 'org.apache.httpcomponents:httpclient:4.5.10' testRuntime 'org.postgresql:postgresql:42.2.8' - testRuntime 'mysql:mysql-connector-java:8.0.17' + testRuntime 'mysql:mysql-connector-java:8.0.18' } From e36ec2ba8dfc5b7b786dafa81b3d419498923bef Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2019 20:18:22 +0100 Subject: [PATCH 0995/5615] Bump cucumber-java from 4.7.4 to 4.8.0 in /examples (#1974) Bumps [cucumber-java](https://github.com/cucumber/cucumber-jvm) from 4.7.4 to 4.8.0. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/master/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v4.7.4...v4.8.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 7b489671506..5d2caed8c2a 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' - implementation 'io.cucumber:cucumber-java:4.7.4' + implementation 'io.cucumber:cucumber-java:4.8.0' testImplementation 'io.cucumber:cucumber-junit:4.8.0' testImplementation 'org.testcontainers:selenium' } From 8cf76c09f065d6fb8fdb03a236cc270412b9afa0 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2019 20:18:34 +0100 Subject: [PATCH 0996/5615] =?UTF-8?q?Bump=20mysql-connector-java=20from=20?= =?UTF-8?q?8.0.17=20to=208.0.18=20in=20/modules/jun=E2=80=A6=20(#1971)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.17 to 8.0.18. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/commits) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index e286af1e149..f6f23aa8959 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -14,7 +14,7 @@ dependencies { } testRuntime 'org.postgresql:postgresql:42.2.8' - testRuntime 'mysql:mysql-connector-java:8.0.17' + testRuntime 'mysql:mysql-connector-java:8.0.18' testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.5.2' } From c27f4016c791d811dfe1cf392606ae0f65d4e6e7 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 20 Oct 2019 13:05:28 +0000 Subject: [PATCH 0997/5615] Bump org.springframework.boot in /examples (#1985) --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index 9f3aed047b3..2c15c3038ba 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.1.9.RELEASE' + id 'org.springframework.boot' version '2.2.0.RELEASE' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index 9d2d2db76ad..df18e6a5f03 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.1.9.RELEASE' + id 'org.springframework.boot' version '2.2.0.RELEASE' } apply plugin: 'io.spring.dependency-management' From 38062356bf955b177f8890155da5e182c7caf151 Mon Sep 17 00:00:00 2001 From: Jonathan Vila Date: Sun, 20 Oct 2019 15:16:39 +0200 Subject: [PATCH 0998/5615] Added documentation example of using local Docker Compose (#1987) Co-authored-by: Jonathan Vila --- docs/modules/docker_compose.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/modules/docker_compose.md b/docs/modules/docker_compose.md index 965b6c7084a..fa584d7f631 100644 --- a/docs/modules/docker_compose.md +++ b/docs/modules/docker_compose.md @@ -112,7 +112,13 @@ public static DockerComposeContainer environment = You can override Testcontainers' default behaviour and make it use a `docker-compose` binary installed on the local machine. This will generally yield an experience that is closer to running docker-compose locally, with the caveat that Docker Compose needs to be present on dev and CI machines. - +```java +public static DockerComposeContainer environment = + new DockerComposeContainer(new File("src/test/resources/compose-test.yml")) + .withExposedService("redis_1", REDIS_PORT, Wait.forListeningPort()) + .waitingFor("db_1", Wait.forLogMessage("started", 1)) + .withLocalCompose(true); +``` ## Using private repositories in Docker compose When Docker Compose is used in container mode (not local), it's needs to be made aware of Docker settings for private repositories. By default, those setting are located in `$HOME/.docker/config.json`. From 43a76abb5d27f322a6a7a30ffc148beb31cd5736 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sat, 26 Oct 2019 11:34:22 +0300 Subject: [PATCH 0999/5615] Reusable containers (stage1, alpha) (#1781) * Simplify Kafka container by deferring the Kafka command * WIP: reusable containers based on labels * do not call `newInstance` from `newInstanceFromConnectionUrl` * disable the port forwarding in `ReusabilityUnitTests` * Simplify the startup sequence * speed up port detection by running the checks as a single command * Separate "environment" and "classpath" properties (for global things) * Update TestcontainersConfiguration.java * Add test for the reuse env property * Add `@UnstableAPI` annotation * Add `VisibleForTesting` on `findContainerForReuse` --- .../java/org/testcontainers/UnstableAPI.java | 21 ++ .../containers/GenericContainer.java | 126 +++++++++-- .../containers/LazyDockerClient.java | 15 ++ .../utility/TestcontainersConfiguration.java | 8 +- .../containers/ReusabilityUnitTests.java | 200 ++++++++++++++++++ .../TestcontainersConfigurationTest.java | 11 + .../JdbcDatabaseContainerProvider.java | 10 +- .../testcontainers/jdbc/ConnectionUrl.java | 6 + .../jdbc/ContainerDatabaseDriver.java | 2 +- 9 files changed, 380 insertions(+), 19 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/UnstableAPI.java create mode 100644 core/src/main/java/org/testcontainers/containers/LazyDockerClient.java create mode 100644 core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java diff --git a/core/src/main/java/org/testcontainers/UnstableAPI.java b/core/src/main/java/org/testcontainers/UnstableAPI.java new file mode 100644 index 00000000000..21fd2c3176c --- /dev/null +++ b/core/src/main/java/org/testcontainers/UnstableAPI.java @@ -0,0 +1,21 @@ +package org.testcontainers; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Marks that the annotated API is a subject to change and SHOULD NOT be considered + * a stable API. + */ +@Retention(RetentionPolicy.SOURCE) +@Target({ + ElementType.TYPE, + ElementType.METHOD, + ElementType.FIELD, +}) +@Documented +public @interface UnstableAPI { +} diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index e3a16c2dacb..f17e8dfd0b2 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -1,5 +1,9 @@ package org.testcontainers.containers; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.CreateContainerCmd; import com.github.dockerjava.api.command.InspectContainerResponse; @@ -12,13 +16,15 @@ import com.github.dockerjava.api.model.PortBinding; import com.github.dockerjava.api.model.Volume; import com.github.dockerjava.api.model.VolumesFrom; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; +import com.google.common.collect.ImmutableMap; import lombok.AccessLevel; import lombok.Data; -import lombok.EqualsAndHashCode; import lombok.NonNull; import lombok.Setter; import lombok.SneakyThrows; +import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.apache.commons.compress.utils.IOUtils; @@ -33,6 +39,7 @@ import org.rnorth.visibleassertions.VisibleAssertions; import org.slf4j.Logger; import org.testcontainers.DockerClientFactory; +import org.testcontainers.UnstableAPI; import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.startupcheck.IsRunningStartupCheckStrategy; import org.testcontainers.containers.startupcheck.MinimumDurationRunningStartupCheckStrategy; @@ -62,11 +69,14 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.Method; import java.nio.charset.Charset; import java.nio.file.Path; import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -100,6 +110,8 @@ public class GenericContainer> public static final String INTERNAL_HOST_HOSTNAME = "host.testcontainers.internal"; + static final String HASH_LABEL = "org.testcontainers.hash"; + /* * Default settings */ @@ -168,11 +180,12 @@ public class GenericContainer> protected final Set dependencies = new HashSet<>(); - /* + /** * Unique instance of DockerClient for use by this container object. + * We use {@link LazyDockerClient} here to avoid eager client creation */ @Setter(AccessLevel.NONE) - protected DockerClient dockerClient = DockerClientFactory.instance().client(); + protected DockerClient dockerClient = LazyDockerClient.INSTANCE; /* * Info about the Docker server; lazily fetched. @@ -222,6 +235,8 @@ public class GenericContainer> @Nullable private Map tmpFsMapping; + @Setter(AccessLevel.NONE) + private boolean shouldBeReused = false; public GenericContainer() { this(TestcontainersConfiguration.getInstance().getTinyImage()); @@ -276,13 +291,15 @@ protected void doStart() { try { configure(); + Instant startedAt = Instant.now(); + logger().debug("Starting container: {}", getDockerImageName()); logger().debug("Trying to start container: {}", image.get()); AtomicInteger attempt = new AtomicInteger(0); Unreliables.retryUntilSuccess(startupAttempts, () -> { logger().debug("Trying to start container: {} (attempt {}/{})", image.get(), attempt.incrementAndGet(), startupAttempts); - tryStart(); + tryStart(startedAt); return true; }); @@ -291,7 +308,25 @@ protected void doStart() { } } - private void tryStart() { + @UnstableAPI + @SneakyThrows + protected boolean canBeReused() { + for (Class type = getClass(); type != GenericContainer.class; type = type.getSuperclass()) { + try { + Method method = type.getDeclaredMethod("containerIsCreated", String.class); + if (method.getDeclaringClass() != GenericContainer.class) { + logger().warn("{} can't be reused because it overrides {}", getClass(), method.getName()); + return false; + } + } catch (NoSuchMethodException e) { + // ignore + } + } + + return true; + } + + private void tryStart(Instant startedAt) { try { String dockerImageName = image.get(); logger().debug("Starting container: {}", dockerImageName); @@ -300,16 +335,49 @@ private void tryStart() { CreateContainerCmd createCommand = dockerClient.createContainerCmd(dockerImageName); applyConfiguration(createCommand); - containerId = createCommand.exec().getId(); + createCommand.getLabels().put(DockerClientFactory.TESTCONTAINERS_LABEL, "true"); - connectToPortForwardingNetwork(createCommand.getNetworkMode()); + boolean reused = false; + if (shouldBeReused) { + if (!canBeReused()) { + throw new IllegalStateException("This container does not support reuse"); + } - copyToFileContainerPathMap.forEach(this::copyFileToContainer); + if (TestcontainersConfiguration.getInstance().environmentSupportsReuse()) { + String hash = hash(createCommand); - containerIsCreated(containerId); + containerId = findContainerForReuse(hash).orElse(null); - logger().info("Starting container with ID: {}", containerId); - dockerClient.startContainerCmd(containerId).exec(); + if (containerId != null) { + logger().info("Reusing container with ID: {} and hash: {}", containerId, hash); + reused = true; + } else { + logger().debug("Can't find a reusable running container with hash: {}", hash); + + createCommand.getLabels().put(HASH_LABEL, hash); + } + } else { + logger().info("Reuse was requested but the environment does not support the reuse of containers"); + } + } else { + createCommand.getLabels().put(DockerClientFactory.TESTCONTAINERS_SESSION_ID_LABEL, DockerClientFactory.SESSION_ID); + } + + if (!reused) { + containerId = createCommand.exec().getId(); + + // TODO add to the hash + copyToFileContainerPathMap.forEach(this::copyFileToContainer); + } + + connectToPortForwardingNetwork(createCommand.getNetworkMode()); + + if (!reused) { + containerIsCreated(containerId); + + logger().info("Starting container with ID: {}", containerId); + dockerClient.startContainerCmd(containerId).exec(); + } logger().info("Container {} is starting: {}", dockerImageName, containerId); @@ -331,7 +399,7 @@ private void tryStart() { // Wait until the process within the container has become ready for use (e.g. listening on network, log message emitted, etc). waitUntilContainerStarted(); - logger().info("Container {} started", dockerImageName); + logger().info("Container {} started in {}", dockerImageName, Duration.between(startedAt, Instant.now())); containerIsStarted(containerInfo); } catch (Exception e) { logger().error("Could not start container", e); @@ -351,6 +419,31 @@ private void tryStart() { } } + @UnstableAPI + @SneakyThrows(JsonProcessingException.class) + final String hash(CreateContainerCmd createCommand) { + // TODO add Testcontainers' version to the hash + String commandJson = new ObjectMapper() + .enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY) + .enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS) + .writeValueAsString(createCommand); + + return DigestUtils.sha1Hex(commandJson); + } + + @VisibleForTesting + Optional findContainerForReuse(String hash) { + // TODO locking + return dockerClient.listContainersCmd() + .withLabelFilter(ImmutableMap.of(HASH_LABEL, hash)) + .withLimit(1) + .withStatusFilter(Arrays.asList("running")) + .exec() + .stream() + .findAny() + .map(it -> it.getId()); + } + /** * Set any custom settings for the create command such as shared memory size. */ @@ -613,7 +706,6 @@ private void applyConfiguration(CreateContainerCmd createCommand) { if (createCommand.getLabels() != null) { combinedLabels.putAll(createCommand.getLabels()); } - combinedLabels.putAll(DockerClientFactory.DEFAULT_LABELS); createCommand.withLabels(combinedLabels); } @@ -1215,7 +1307,7 @@ public SELF withStartupAttempts(int attempts) { } /** - * Allow low level modifications of {@link CreateContainerCmd} after it was pre-configured in {@link #tryStart()}. + * Allow low level modifications of {@link CreateContainerCmd} after it was pre-configured in {@link #tryStart(Instant)}. * Invocation happens eagerly on a moment when container is created. * Warning: this does expose the underlying docker-java API so might change outside of our control. * @@ -1247,6 +1339,12 @@ public SELF withTmpFs(Map mapping) { return self(); } + @UnstableAPI + public SELF withReuse(boolean reusable) { + this.shouldBeReused = reusable; + return self(); + } + @Override public boolean equals(Object o) { return this == o; diff --git a/core/src/main/java/org/testcontainers/containers/LazyDockerClient.java b/core/src/main/java/org/testcontainers/containers/LazyDockerClient.java new file mode 100644 index 00000000000..b1341b52755 --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/LazyDockerClient.java @@ -0,0 +1,15 @@ +package org.testcontainers.containers; + +import com.github.dockerjava.api.DockerClient; +import lombok.experimental.Delegate; +import org.testcontainers.DockerClientFactory; + +enum LazyDockerClient implements DockerClient { + + INSTANCE; + + @Delegate + final DockerClient getDockerClient() { + return DockerClientFactory.instance().client(); + } +} diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 21f9bdfb333..e5a45a05b64 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -2,6 +2,7 @@ import lombok.*; import lombok.extern.slf4j.Slf4j; +import org.testcontainers.UnstableAPI; import java.io.*; import java.net.MalformedURLException; @@ -89,12 +90,17 @@ public boolean isDisableChecks() { return Boolean.parseBoolean((String) environmentProperties.getOrDefault("checks.disable", "false")); } + @UnstableAPI + public boolean environmentSupportsReuse() { + return Boolean.parseBoolean((String) environmentProperties.getOrDefault("testcontainers.reuse.enable", "false")); + } + public String getDockerClientStrategyClassName() { return (String) environmentProperties.get("docker.client.strategy"); } /** - * + * * @deprecated we no longer have different transport types */ @Deprecated diff --git a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java new file mode 100644 index 00000000000..b05db5b5077 --- /dev/null +++ b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java @@ -0,0 +1,200 @@ +package org.testcontainers.containers; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.command.*; +import com.github.dockerjava.api.model.Container; +import com.github.dockerjava.core.command.CreateContainerCmdImpl; +import com.github.dockerjava.core.command.InspectContainerCmdImpl; +import com.github.dockerjava.core.command.ListContainersCmdImpl; +import com.github.dockerjava.core.command.StartContainerCmdImpl; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; +import org.junit.Assume; +import org.junit.Test; +import org.junit.experimental.runners.Enclosed; +import org.junit.runner.RunWith; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.Parameterized; +import org.mockito.Mockito; +import org.mockito.stubbing.Answer; +import org.rnorth.visibleassertions.VisibleAssertions; +import org.testcontainers.containers.startupcheck.StartupCheckStrategy; +import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy; +import org.testcontainers.containers.wait.strategy.WaitStrategy; +import org.testcontainers.utility.TestcontainersConfiguration; + +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +@RunWith(Enclosed.class) +public class ReusabilityUnitTests { + + static final CompletableFuture IMAGE_FUTURE = CompletableFuture.completedFuture( + TestcontainersConfiguration.getInstance().getTinyImage() + ); + + @RunWith(Parameterized.class) + @RequiredArgsConstructor + @FieldDefaults(makeFinal = true) + public static class CanBeReusedTest { + + @Parameterized.Parameters(name = "{0}") + public static Object[][] data() { + return new Object[][] { + { "generic", new GenericContainer(IMAGE_FUTURE), true }, + { "anonymous generic", new GenericContainer(IMAGE_FUTURE) {}, true }, + { "custom", new CustomContainer(), true }, + { "anonymous custom", new CustomContainer() {}, true }, + { "custom with containerIsCreated", new CustomContainerWithContainerIsCreated(), false }, + }; + } + + String name; + + GenericContainer container; + + boolean reusable; + + @Test + public void shouldBeReusable() { + if (reusable) { + VisibleAssertions.assertTrue("Is reusable", container.canBeReused()); + } else { + VisibleAssertions.assertFalse("Is not reusable", container.canBeReused()); + } + } + + static class CustomContainer extends GenericContainer { + CustomContainer() { + super(IMAGE_FUTURE); + } + } + + static class CustomContainerWithContainerIsCreated extends GenericContainer { + + CustomContainerWithContainerIsCreated() { + super(IMAGE_FUTURE); + } + + @Override + protected void containerIsCreated(String containerId) { + super.containerIsCreated(containerId); + } + } + } + + @RunWith(BlockJUnit4ClassRunner.class) + @FieldDefaults(makeFinal = true) + public static class HashTest { + + StartupCheckStrategy startupCheckStrategy = new StartupCheckStrategy() { + @Override + public StartupStatus checkStartupState(DockerClient dockerClient, String containerId) { + return StartupStatus.SUCCESSFUL; + } + }; + + WaitStrategy waitStrategy = new AbstractWaitStrategy() { + @Override + protected void waitUntilReady() { + + } + }; + + DockerClient client = Mockito.mock(DockerClient.class); + + GenericContainer container = new GenericContainer(IMAGE_FUTURE) + .withNetworkMode("none") // to disable the port forwarding + .withStartupCheckStrategy(startupCheckStrategy) + .waitingFor(waitStrategy) + .withReuse(true); + + { + container.dockerClient = client; + } + + @Test + public void shouldStartIfListReturnsEmpty() { + String containerId = randomContainerId(); + when(client.createContainerCmd(any())).then(createContainerAnswer(containerId)); + when(client.listContainersCmd()).then(listContainersAnswer()); + when(client.startContainerCmd(containerId)).then(startContainerAnswer()); + when(client.inspectContainerCmd(containerId)).then(inspectContainerAnswer()); + + container.start(); + + Mockito.verify(client, Mockito.atLeastOnce()).startContainerCmd(containerId); + } + + @Test + public void shouldReuseIfListReturnsID() { + // TODO mock TestcontainersConfiguration + Assume.assumeTrue("supports reuse", TestcontainersConfiguration.getInstance().environmentSupportsReuse()); + when(client.createContainerCmd(any())).then(createContainerAnswer(randomContainerId())); + String existingContainerId = randomContainerId(); + when(client.listContainersCmd()).then(listContainersAnswer(existingContainerId)); + when(client.inspectContainerCmd(existingContainerId)).then(inspectContainerAnswer()); + + container.start(); + + Mockito.verify(client, Mockito.never()).startContainerCmd(existingContainerId); + } + + private String randomContainerId() { + return UUID.randomUUID().toString(); + } + + private Answer listContainersAnswer(String... ids) { + return invocation -> { + ListContainersCmd.Exec exec = command -> { + return new ObjectMapper().convertValue( + Stream.of(ids) + .map(id -> Collections.singletonMap("Id", id)) + .collect(Collectors.toList()), + new TypeReference>() {} + ); + }; + return new ListContainersCmdImpl(exec); + }; + } + + private Answer createContainerAnswer(String containerId) { + return invocation -> { + CreateContainerCmd.Exec exec = command -> { + CreateContainerResponse response = new CreateContainerResponse(); + response.setId(containerId); + return response; + }; + return new CreateContainerCmdImpl(exec, null, "image:latest"); + }; + } + + private Answer startContainerAnswer() { + return invocation -> { + StartContainerCmd.Exec exec = command -> { + return null; + }; + return new StartContainerCmdImpl(exec, invocation.getArgument(0)); + }; + } + + private Answer inspectContainerAnswer() { + return invocation -> { + InspectContainerCmd.Exec exec = command -> { + return new InspectContainerResponse(); + }; + return new InspectContainerCmdImpl(exec, invocation.getArgument(0)); + }; + } + + } +} diff --git a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java index ad2a0be6cd3..49a378554f1 100644 --- a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java +++ b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java @@ -37,6 +37,17 @@ public void shouldReadDockerClientStrategyFromEnvironmentOnly() { assertEquals("Docker client strategy is changed", "foo", newConfig().getDockerClientStrategyClassName()); } + @Test + public void shouldReadReuseFromEnvironmentOnly() { + assertFalse("no reuse by default", newConfig().environmentSupportsReuse()); + + classpathProperties.setProperty("testcontainers.reuse.enable", "true"); + assertFalse("reuse is not affected by classpath properties", newConfig().environmentSupportsReuse()); + + environmentProperties.setProperty("testcontainers.reuse.enable", "true"); + assertTrue("reuse enabled", newConfig().environmentSupportsReuse()); + } + private TestcontainersConfiguration newConfig() { return new TestcontainersConfiguration(environmentProperties, classpathProperties); } diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainerProvider.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainerProvider.java index f2ebde857c5..f8694306384 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainerProvider.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainerProvider.java @@ -45,11 +45,14 @@ public JdbcDatabaseContainer newInstance() { * @return Instance of {@link JdbcDatabaseContainer} */ public JdbcDatabaseContainer newInstance(ConnectionUrl url) { + final JdbcDatabaseContainer result; if (url.getImageTag().isPresent()) { - return newInstance(url.getImageTag().get()); + result = newInstance(url.getImageTag().get()); } else { - return newInstance(); + result = newInstance(); } + result.withReuse(url.isReusable()); + return result; } protected JdbcDatabaseContainer newInstanceFromConnectionUrl(ConnectionUrl connectionUrl, final String userParamName, final String pwdParamName) { @@ -59,7 +62,7 @@ protected JdbcDatabaseContainer newInstanceFromConnectionUrl(ConnectionUrl conne final String user = connectionUrl.getQueryParameters().getOrDefault(userParamName, "test"); final String password = connectionUrl.getQueryParameters().getOrDefault(pwdParamName, "test"); - final JdbcDatabaseContainer instance; + final JdbcDatabaseContainer instance; if (connectionUrl.getImageTag().isPresent()) { instance = newInstance(connectionUrl.getImageTag().get()); } else { @@ -67,6 +70,7 @@ protected JdbcDatabaseContainer newInstanceFromConnectionUrl(ConnectionUrl conne } return instance + .withReuse(connectionUrl.isReusable()) .withDatabaseName(databaseName) .withUsername(user) .withPassword(password); diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java index e236d723c82..4c590474bf7 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java @@ -13,6 +13,7 @@ import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; +import org.testcontainers.UnstableAPI; import static java.util.stream.Collectors.toMap; @@ -49,6 +50,9 @@ public class ConnectionUrl { private Optional initScriptPath = Optional.empty(); + @UnstableAPI + private boolean reusable = false; + private Optional initFunction = Optional.empty(); private Optional queryString; @@ -132,6 +136,8 @@ private void parseUrl() { initScriptPath = Optional.ofNullable(containerParameters.get("TC_INITSCRIPT")); + reusable = Boolean.parseBoolean(containerParameters.get("TC_REUSABLE")); + Matcher funcMatcher = Patterns.INITFUNCTION_MATCHING_PATTERN.matcher(this.getUrl()); if (funcMatcher.matches()) { initFunction = Optional.of(new InitFunctionDef(funcMatcher.group(2), funcMatcher.group(4))); diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java index 6d3df6c7cc5..c5413fe7efb 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java @@ -151,7 +151,7 @@ public synchronized Connection connect(String url, final Properties info) throws */ private Connection wrapConnection(final Connection connection, final JdbcDatabaseContainer container, final ConnectionUrl connectionUrl) { - final boolean isDaemon = connectionUrl.isInDaemonMode(); + final boolean isDaemon = connectionUrl.isInDaemonMode() || connectionUrl.isReusable(); Set connections = containerConnections.computeIfAbsent(container.getContainerId(), k -> new HashSet<>()); From 92f0a4f7ffb9d46023ec77e6588e74831ee8e29f Mon Sep 17 00:00:00 2001 From: Benjamin Muskalla Date: Sat, 26 Oct 2019 10:35:38 +0200 Subject: [PATCH 1000/5615] Allow @Testcontainers to be inherited from superclass (#1843) (#2001) * Updated existing test with now expected behaviour * @Testcontainers is now @Inherited --- .../org/testcontainers/junit/jupiter/Testcontainers.java | 6 ++++++ .../junit/jupiter/inheritance/AbstractTestBase.java | 2 -- .../junit/jupiter/inheritance/InheritedTests.java | 1 - 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Testcontainers.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Testcontainers.java index 6a364cd19e6..8e05dfdc576 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Testcontainers.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Testcontainers.java @@ -3,6 +3,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @@ -18,6 +19,10 @@ * last test method has executed. Containers declared as instance fields will * be started and stopped for every test method.

* + *

The annotation {@code @Testcontainers} can be used on a superclass in + * the test hierarchy as well. All subclasses will automatically inherit + * support for the extension.

+ * *

Note: This extension has only be tested with sequential * test execution. Using it with parallel test execution is unsupported and * may have unintended side effects.

@@ -52,6 +57,7 @@ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @ExtendWith(TestcontainersExtension.class) +@Inherited public @interface Testcontainers { /** diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/AbstractTestBase.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/AbstractTestBase.java index 930f16f035d..3766c3f7a60 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/AbstractTestBase.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/AbstractTestBase.java @@ -3,8 +3,6 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; -import java.util.concurrent.atomic.AtomicLong; - @Testcontainers abstract class AbstractTestBase { diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/InheritedTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/InheritedTests.java index 37e535d25e7..f050e428692 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/InheritedTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/InheritedTests.java @@ -6,7 +6,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -@Testcontainers class InheritedTests extends AbstractTestBase { @Container From 12cb54baa4f702c8d5dd9f05a0194114e061c6ed Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sat, 26 Oct 2019 12:44:40 +0300 Subject: [PATCH 1001/5615] Update mkdocs.yml to 1.12.3 --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 976c1b7c95f..5c78e884662 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -83,4 +83,4 @@ nav: - contributing.md - contributing_docs.md extra: - latest_version: 1.12.2 + latest_version: 1.12.3 From 71e3d75d4897399c788c276f9f86d0023bbeb862 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 30 Oct 2019 09:28:17 +0000 Subject: [PATCH 1002/5615] Bump influxdb-java from 2.15 to 2.16 in /modules/influxdb (#2010) --- modules/influxdb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/influxdb/build.gradle b/modules/influxdb/build.gradle index d122f786591..186174c242f 100644 --- a/modules/influxdb/build.gradle +++ b/modules/influxdb/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: InfluxDB" dependencies { compile project(':testcontainers') - compileOnly 'org.influxdb:influxdb-java:2.15' + compileOnly 'org.influxdb:influxdb-java:2.16' testCompile 'org.influxdb:influxdb-java:2.15' } From 8fbd1f8a9c0966781db939718141daa50e8e1bb3 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 30 Oct 2019 09:40:24 +0000 Subject: [PATCH 1003/5615] Bump assertj-core from 3.13.2 to 3.14.0 in /modules/pulsar (#2009) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.13.2 to 3.14.0. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.13.2...assertj-core-3.14.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 05d9a711420..046debc2b9b 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -4,5 +4,5 @@ dependencies { compile project(':testcontainers') testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.4.1' - testCompile group: 'org.assertj', name: 'assertj-core', version: '3.13.2' + testCompile group: 'org.assertj', name: 'assertj-core', version: '3.14.0' } From 63099ca0c8e2f44ff62558307a1398a3b9cbb074 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 30 Oct 2019 09:40:37 +0000 Subject: [PATCH 1004/5615] Bump assertj-core from 3.13.2 to 3.14.0 in /modules/neo4j (#2006) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.13.2 to 3.14.0. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.13.2...assertj-core-3.14.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 4889196ccef..00e1e36f8b7 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -30,5 +30,5 @@ dependencies { compile project(":testcontainers") testCompile "org.neo4j.driver:neo4j-java-driver:1.7.5" - testCompile 'org.assertj:assertj-core:3.13.2' + testCompile 'org.assertj:assertj-core:3.14.0' } From eb69320384a8c2988f17907b297760a0d4cf0167 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 30 Oct 2019 09:48:44 +0000 Subject: [PATCH 1005/5615] =?UTF-8?q?Bump=20assertj-core=20from=203.13.2?= =?UTF-8?q?=20to=203.14.0=20in=20/modules/database-co=E2=80=A6=20(#2007)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.13.2 to 3.14.0. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.13.2...assertj-core-3.14.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/database-commons/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/database-commons/build.gradle b/modules/database-commons/build.gradle index 7c820909d61..ea3e0b07360 100644 --- a/modules/database-commons/build.gradle +++ b/modules/database-commons/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: Database-Commons" dependencies { compile project(':testcontainers') - testCompile 'org.assertj:assertj-core:3.13.2' + testCompile 'org.assertj:assertj-core:3.14.0' } From 0940e0caa58a44e902c2d0345fc72eb99ba4f997 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 30 Oct 2019 09:49:13 +0000 Subject: [PATCH 1006/5615] Bump kafka-clients from 2.3.0 to 2.3.1 in /modules/kafka (#2002) Bumps kafka-clients from 2.3.0 to 2.3.1. Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index 9d86482cf2f..f53c428886f 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Kafka" dependencies { compile project(':testcontainers') - testCompile 'org.apache.kafka:kafka-clients:2.3.0' + testCompile 'org.apache.kafka:kafka-clients:2.3.1' testCompile 'org.assertj:assertj-core:3.13.2' testCompile 'com.google.guava:guava:23.0' } From 5b24a298fad5cc3f432c7a7627d1c3d43a0c01c6 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 30 Oct 2019 09:49:40 +0000 Subject: [PATCH 1007/5615] Bump assertj-core from 3.13.2 to 3.14.0 in /core (#2005) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.13.2 to 3.14.0. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.13.2...assertj-core-3.14.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- core/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index 2b631f46bce..a580d73c340 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -115,12 +115,12 @@ dependencies { // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest testCompile files('testlib/repo/fakejar/fakejar/0/fakejar-0.jar') - testCompile 'org.assertj:assertj-core:3.13.2' + testCompile 'org.assertj:assertj-core:3.14.0' testCompile project(':test-support') jarFileTestCompileOnly "org.projectlombok:lombok:${lombok.version}" jarFileTestAnnotationProcessor "org.projectlombok:lombok:${lombok.version}" jarFileTestCompile 'junit:junit:4.12' - jarFileTestCompile 'org.assertj:assertj-core:3.12.2' + jarFileTestCompile 'org.assertj:assertj-core:3.14.0' jarFileTestCompile 'org.ow2.asm:asm-debug-all:5.2' } From 3725b791d11d0251e3e3b900b94a5a2b4003c2be Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 30 Oct 2019 11:12:47 +0000 Subject: [PATCH 1008/5615] Bump assertj-core from 3.13.2 to 3.14.0 in /modules/kafka (#2008) --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index f53c428886f..d9d7c20ca4a 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -4,6 +4,6 @@ dependencies { compile project(':testcontainers') testCompile 'org.apache.kafka:kafka-clients:2.3.1' - testCompile 'org.assertj:assertj-core:3.13.2' + testCompile 'org.assertj:assertj-core:3.14.0' testCompile 'com.google.guava:guava:23.0' } From c5c24a55e62e4610c23adbd7a30138b0ee4045b9 Mon Sep 17 00:00:00 2001 From: David Harris Date: Wed, 30 Oct 2019 08:44:58 -0600 Subject: [PATCH 1009/5615] Slf4jLogConsumer improvements (#1986) * Adds support for MDC in Slf4jLogConsumer * Uses SLF4J levels instead of including the level as part of the message * make separation of output streams to different log levels configurable * use `with` prefix on method --- .../containers/output/Slf4jLogConsumer.java | 65 ++++++++++++++++--- 1 file changed, 56 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/output/Slf4jLogConsumer.java b/core/src/main/java/org/testcontainers/containers/output/Slf4jLogConsumer.java index 26b017d4a36..a274cd63004 100644 --- a/core/src/main/java/org/testcontainers/containers/output/Slf4jLogConsumer.java +++ b/core/src/main/java/org/testcontainers/containers/output/Slf4jLogConsumer.java @@ -1,16 +1,27 @@ package org.testcontainers.containers.output; +import java.util.HashMap; +import java.util.Map; + import org.slf4j.Logger; +import org.slf4j.MDC; /** * A consumer for container output that logs output to an SLF4J logger. */ public class Slf4jLogConsumer extends BaseConsumer { private final Logger logger; + private final Map mdc = new HashMap<>(); + private boolean separateOutputStreams; private String prefix = ""; public Slf4jLogConsumer(Logger logger) { + this(logger, false); + } + + public Slf4jLogConsumer(Logger logger, boolean separateOutputStreams) { this.logger = logger; + this.separateOutputStreams = separateOutputStreams; } public Slf4jLogConsumer withPrefix(String prefix) { @@ -18,21 +29,57 @@ public Slf4jLogConsumer withPrefix(String prefix) { return this; } + public Slf4jLogConsumer withMdc(String key, String value) { + mdc.put(key, value); + return this; + } + + public Slf4jLogConsumer withMdc(Map mdc) { + this.mdc.putAll(mdc); + return this; + } + + public Slf4jLogConsumer withSeparateOutputStreams() { + this.separateOutputStreams = true; + return this; + } + @Override public void accept(OutputFrame outputFrame) { OutputFrame.OutputType outputType = outputFrame.getType(); String utf8String = outputFrame.getUtf8String(); utf8String = utf8String.replaceAll(FrameConsumerResultCallback.LINE_BREAK_AT_END_REGEX, ""); - switch (outputType) { - case END: - break; - case STDOUT: - case STDERR: - logger.info("{}{}: {}", prefix, outputType, utf8String); - break; - default: - throw new IllegalArgumentException("Unexpected outputType " + outputType); + + Map originalMdc = MDC.getCopyOfContextMap(); + MDC.setContextMap(mdc); + try { + switch (outputType) { + case END: + break; + case STDOUT: + if (separateOutputStreams) { + logger.info("{}{}", prefix.isEmpty() ? "" : (prefix + ": "), utf8String); + } else { + logger.info("{}{}: {}", prefix, outputType, utf8String); + } + break; + case STDERR: + if (separateOutputStreams) { + logger.error("{}{}", prefix.isEmpty() ? "" : (prefix + ": "), utf8String); + } else { + logger.info("{}{}: {}", prefix, outputType, utf8String); + } + break; + default: + throw new IllegalArgumentException("Unexpected outputType " + outputType); + } + } finally { + if (originalMdc == null) { + MDC.clear(); + } else { + MDC.setContextMap(originalMdc); + } } } } From a2ecd4b813aba905eaa56159a17e2ec0820d93d9 Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 30 Oct 2019 14:47:04 +0000 Subject: [PATCH 1010/5615] Slf4j log consumer improvements - add docs (#2016) --- docs/features/container_logs.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/features/container_logs.md b/docs/features/container_logs.md index a9e2832eb70..d603981059a 100644 --- a/docs/features/container_logs.md +++ b/docs/features/container_logs.md @@ -36,6 +36,25 @@ Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(LOGGER); container.followOutput(logConsumer); ``` +By default both standard out and standard error will both be emitted at INFO level. +Standard error may be emitted at ERROR level, if desired: + +```java +Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(log).withSeparateOutputStreams() +``` + +The [Mapped Diagnostic Context (MDC)](http://logback.qos.ch/manual/mdc.html) for emitted messages may be configured using the `withMdc(...)` option: + +```java +Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(log).withMdc("key", "value") +``` + +or using an existing map of key-value pairs: + +```java +Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(log).withMdc(map) +``` + ### Capturing container output as a String To stream logs live or customize the decoding, `ToStringConsumer` may be used: From a753bb1bd1aa132ac89463393a1b9c037197ec77 Mon Sep 17 00:00:00 2001 From: Yuri Orlov Date: Wed, 30 Oct 2019 17:47:37 +0300 Subject: [PATCH 1011/5615] Small code style fix (#2004) Co-authored-by: Richard North --- .../java/org/testcontainers/utility/CommandLine.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/CommandLine.java b/core/src/main/java/org/testcontainers/utility/CommandLine.java index 65788357898..a217823c40f 100644 --- a/core/src/main/java/org/testcontainers/utility/CommandLine.java +++ b/core/src/main/java/org/testcontainers/utility/CommandLine.java @@ -35,12 +35,11 @@ public static String runShellCommand(String... command) { LOGGER.debug("Executing shell command: `{}`", joinedCommand); try { - ProcessResult result; - result = new ProcessExecutor() - .command(command) - .readOutput(true) - .exitValueNormal() - .execute(); + ProcessResult result = new ProcessExecutor() + .command(command) + .readOutput(true) + .exitValueNormal() + .execute(); return result.outputUTF8().trim(); } catch (IOException | InterruptedException | TimeoutException | InvalidExitValueException e) { From 9a31ab351b3697dd29369f82b504c3fc43339de8 Mon Sep 17 00:00:00 2001 From: Joseph Petersen Date: Wed, 30 Oct 2019 15:50:22 +0100 Subject: [PATCH 1012/5615] Fix vault module, add new test with java client v2 (#1791) * fix vault module, add new test with java client (cherry picked from commit 8931f8643bdda8edc5318b76ea5f27f9cf98d271) * adds test for try with resource adds tests for default constructor sets default image to vault:latest * Update VaultContainer.java * Update VaultContainer.java * Update VaultClientTest.java * Update VaultContainer.java * Update VaultContainerTest.java * fix vault cli test * Update modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java Co-Authored-By: Richard North * * Use health endpoint for discovering vault readiness * Move customizations to constructor to allow overrides * Use a single startup attempt, as 1 should be sufficient Co-authored-by: Roberto Franchini Co-authored-by: Sergei Egorov Co-authored-by: Richard North --- modules/vault/build.gradle | 3 + .../testcontainers/vault/VaultContainer.java | 66 ++++++++----------- .../testcontainers/vault/VaultClientTest.java | 58 ++++++++++++++++ .../vault/VaultContainerTest.java | 63 +++++++++--------- 4 files changed, 118 insertions(+), 72 deletions(-) create mode 100644 modules/vault/src/test/java/org/testcontainers/vault/VaultClientTest.java diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index b37a74602ff..052446bef6f 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -3,5 +3,8 @@ description = "Testcontainers :: Vault" dependencies { compile project(':testcontainers') + testCompile 'com.bettercloud:vault-java-driver:4.0.0' testCompile 'io.rest-assured:rest-assured:4.1.2' + testCompile 'org.assertj:assertj-core:3.12.0' + } diff --git a/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java b/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java index 3f0f7684e82..ac4e730adb3 100644 --- a/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java +++ b/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java @@ -2,11 +2,10 @@ import com.github.dockerjava.api.command.InspectContainerResponse; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.traits.LinkableContainer; +import org.testcontainers.containers.wait.strategy.Wait; import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -18,33 +17,30 @@ * GenericContainer subclass for Vault specific configuration and features. The main feature is the * withSecretInVault method, where users can specify which secrets to be pre-loaded into Vault for * their specific test scenario. - * + *

* Other helpful features include the withVaultPort, and withVaultToken methods for convenience. */ -public class VaultContainer> extends GenericContainer - implements LinkableContainer { +public class VaultContainer> extends GenericContainer { - private static final String VAULT_PORT = "8200"; - - private boolean vaultPortRequested = false; + private static final int VAULT_PORT = 8200; private Map> secretsMap = new HashMap<>(); + private int port = VAULT_PORT; + public VaultContainer() { - this("vault:0.7.0"); + this("vault:1.1.3"); } public VaultContainer(String dockerImageName) { super(dockerImageName); - } - @Override - protected void configure() { - setStartupAttempts(3); + // Use the vault healthcheck endpoint to check for readiness, per https://www.vaultproject.io/api/system/health.html + setWaitStrategy(Wait.forHttp("/v1/sys/health").forStatusCode(200)); + withCreateContainerCmdModifier(cmd -> cmd.withCapAdd(IPC_LOCK)); - if(!isVaultPortRequested()){ - withEnv("VAULT_ADDR", "http://0.0.0.0:" + VAULT_PORT); - } + withEnv("VAULT_ADDR", "http://0.0.0.0:" + port); + withExposedPorts(port); } @Override @@ -53,11 +49,10 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { } private void addSecrets() { - if(!secretsMap.isEmpty()){ + if (!secretsMap.isEmpty()) { try { this.execInContainer(buildExecCommand(secretsMap)).getStdout().contains("Success"); - } - catch (IOException | InterruptedException e) { + } catch (IOException | InterruptedException e) { logger().error("Failed to add these secrets {} into Vault via exec command. Exception message: {}", secretsMap, e.getMessage()); } } @@ -66,10 +61,10 @@ private void addSecrets() { private String[] buildExecCommand(Map> map) { StringBuilder stringBuilder = new StringBuilder(); map.forEach((path, secrets) -> { - stringBuilder.append(" && vault write " + path); + stringBuilder.append(" && vault kv put " + path); secrets.forEach(item -> stringBuilder.append(" " + item)); }); - return new String[] { "/bin/sh", "-c", stringBuilder.toString().substring(4)}; + return new String[]{"/bin/sh", "-c", stringBuilder.toString().substring(4)}; } /** @@ -89,45 +84,36 @@ public SELF withVaultToken(String token) { * * @param port the port number you want to have the Vault container listen on for tests. * @return this + * @deprecated the exposed port will be randomized automatically. As calling this method provides no additional value, you are recommended to remove the call. getFirstMappedPort() may be used to obtain the listening vault port. */ - public SELF withVaultPort(int port){ - setVaultPortRequested(true); - String vaultPort = String.valueOf(port); - withEnv("VAULT_ADDR", "http://0.0.0.0:" + VAULT_PORT); - setPortBindings(Arrays.asList(vaultPort + ":" + VAULT_PORT)); + @Deprecated + public SELF withVaultPort(int port) { + this.port = port; return self(); } /** * Pre-loads secrets into Vault container. User may specify one or more secrets and all will be added to each path * that is specified. Thus this can be called more than once for multiple paths to be added to Vault. - * + *

* The secrets are added to vault directly after the container is up via the * {@link #addSecrets() addSecrets}, called from {@link #containerIsStarted(InspectContainerResponse) containerIsStarted} * - * @param path specific Vault path to store specified secrets - * @param firstSecret first secret to add to specifed path + * @param path specific Vault path to store specified secrets + * @param firstSecret first secret to add to specifed path * @param remainingSecrets var args list of secrets to add to specified path * @return this */ public SELF withSecretInVault(String path, String firstSecret, String... remainingSecrets) { List list = new ArrayList<>(); list.add(firstSecret); - for(String secret : remainingSecrets) { + for (String secret : remainingSecrets) { list.add(secret); } if (secretsMap.containsKey(path)) { list.addAll(list); } - secretsMap.putIfAbsent(path,list); + secretsMap.putIfAbsent(path, list); return self(); } - - private void setVaultPortRequested(boolean vaultPortRequested) { - this.vaultPortRequested = vaultPortRequested; - } - - private boolean isVaultPortRequested() { - return vaultPortRequested; - } -} \ No newline at end of file +} diff --git a/modules/vault/src/test/java/org/testcontainers/vault/VaultClientTest.java b/modules/vault/src/test/java/org/testcontainers/vault/VaultClientTest.java new file mode 100644 index 00000000000..8091980d234 --- /dev/null +++ b/modules/vault/src/test/java/org/testcontainers/vault/VaultClientTest.java @@ -0,0 +1,58 @@ +package org.testcontainers.vault; + +import com.bettercloud.vault.Vault; +import com.bettercloud.vault.VaultConfig; +import com.bettercloud.vault.VaultException; +import com.bettercloud.vault.response.LogicalResponse; +import org.junit.Test; +import org.testcontainers.containers.wait.strategy.Wait; + +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +public class VaultClientTest { + + private static final String VAULT_TOKEN = "my-root-token"; + + @Test + public void writeAndReadMultipleValues() throws VaultException { + try ( + VaultContainer vaultContainer = new VaultContainer<>() + .withVaultToken(VAULT_TOKEN) + ) { + + vaultContainer.start(); + + final VaultConfig config = new VaultConfig() + .address("http://" + vaultContainer.getContainerIpAddress() + ":" + vaultContainer.getFirstMappedPort()) + .token(VAULT_TOKEN) + .build(); + + final Vault vault = new Vault(config); + + final Map secrets = new HashMap<>(); + secrets.put("value", "world"); + secrets.put("other_value", "another world"); + + // Write operation + final LogicalResponse writeResponse = vault.logical() + .write("secret/hello", secrets); + + assertThat(writeResponse.getRestResponse().getStatus()).isEqualTo(200); + + // Read operation + final Map value = vault.logical() + .read("secret/hello") + .getData(); + + + assertThat(value) + .containsEntry("value", "world") + .containsEntry("other_value", "another world"); + + } + + } +} diff --git a/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java b/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java index 5e1c55b33ac..fae9209c8e7 100644 --- a/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java +++ b/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java @@ -3,13 +3,11 @@ import org.junit.ClassRule; import org.junit.Test; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.wait.Wait; import java.io.IOException; import static io.restassured.RestAssured.given; -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.assertThat; /** @@ -18,32 +16,31 @@ */ public class VaultContainerTest { - private static final int VAULT_PORT = 8201; //using non-default port to show other ports can be passed besides 8200 - private static final String VAULT_TOKEN = "my-root-token"; @ClassRule public static VaultContainer vaultContainer = new VaultContainer<>() - .withVaultToken(VAULT_TOKEN) - .withVaultPort(VAULT_PORT) - .withSecretInVault("secret/testing1", "top_secret=password123") - .withSecretInVault("secret/testing2", "secret_one=password1", - "secret_two=password2", "secret_three=password3", "secret_three=password3", - "secret_four=password4") - .waitingFor(Wait.forHttp("/v1/secret/testing1").forStatusCode(400)); + .withVaultToken(VAULT_TOKEN) + .withSecretInVault("secret/testing1", "top_secret=password123") + .withSecretInVault("secret/testing2", + "secret_one=password1", + "secret_two=password2", + "secret_three=password3", + "secret_three=password3", + "secret_four=password4"); @Test public void readFirstSecretPathWithCli() throws IOException, InterruptedException { - GenericContainer.ExecResult result = vaultContainer.execInContainer("vault", - "read", "-field=top_secret", "secret/testing1"); - assertThat(result.getStdout(), containsString("password123")); + GenericContainer.ExecResult result = vaultContainer.execInContainer("vault", "kv", "get", "-format=json", "secret/testing1"); + final String output = result.getStdout().replaceAll("\\r?\\n", ""); + assertThat(output, containsString("password123")); } @Test public void readSecondSecretPathWithCli() throws IOException, InterruptedException { - GenericContainer.ExecResult result = vaultContainer.execInContainer("vault", - "read", "secret/testing2"); - String output = result.getStdout(); + GenericContainer.ExecResult result = vaultContainer.execInContainer("vault", "kv", "get", "-format=json", "secret/testing2"); + final String output = result.getStdout().replaceAll("\\r?\\n", ""); + System.out.println("output = " + output); assertThat(output, containsString("password1")); assertThat(output, containsString("password2")); assertThat(output, containsString("password3")); @@ -54,26 +51,28 @@ public void readSecondSecretPathWithCli() throws IOException, InterruptedExcepti public void readFirstSecretPathOverHttpApi() throws InterruptedException { given(). header("X-Vault-Token", VAULT_TOKEN). - when(). - get("http://"+getHostAndPort()+"/v1/secret/testing1"). - then(). - assertThat().body("data.top_secret", equalTo("password123")); + when(). + get("http://" + getHostAndPort() + "/v1/secret/data/testing1"). + then(). + assertThat().body("data.data.top_secret", equalTo("password123")); } @Test - public void readSecondecretPathOverHttpApi() throws InterruptedException { + public void readSecondSecretPathOverHttpApi() throws InterruptedException { given(). header("X-Vault-Token", VAULT_TOKEN). - when(). - get("http://"+getHostAndPort()+"/v1/secret/testing2"). - then(). - assertThat().body("data.secret_one", containsString("password1")). - assertThat().body("data.secret_two", containsString("password2")). - assertThat().body("data.secret_three", containsString("password3")). - assertThat().body("data.secret_four", containsString("password4")); + when(). + get("http://" + getHostAndPort() + "/v1/secret/data/testing2"). + then(). + assertThat().body("data.data.secret_one", containsString("password1")). + assertThat().body("data.data.secret_two", containsString("password2")). + assertThat().body("data.data.secret_three", hasItem("password3")). + assertThat().body("data.data.secret_four", containsString("password4")); } - private String getHostAndPort(){ - return vaultContainer.getContainerIpAddress()+":"+vaultContainer.getMappedPort(8200); + private String getHostAndPort() { + return vaultContainer.getContainerIpAddress() + ":" + vaultContainer.getMappedPort(8200); } + + } From 6568a5a47d9de9c3480b0e5455045b7fa8af7091 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2019 09:16:07 +0000 Subject: [PATCH 1013/5615] Bump assertj-core from 3.12.0 to 3.14.0 in /modules/vault (#2018) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.12.0 to 3.14.0. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.12.0...assertj-core-3.14.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index 052446bef6f..145cb86bdf2 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -5,6 +5,6 @@ dependencies { testCompile 'com.bettercloud:vault-java-driver:4.0.0' testCompile 'io.rest-assured:rest-assured:4.1.2' - testCompile 'org.assertj:assertj-core:3.12.0' + testCompile 'org.assertj:assertj-core:3.14.0' } From 6d99da8f87c12cf455af9a1f7cad399f71268b21 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2019 09:16:24 +0000 Subject: [PATCH 1014/5615] Bump vault-java-driver from 4.0.0 to 5.0.0 in /modules/vault (#2019) Bumps [vault-java-driver](https://github.com/BetterCloud/vault-java-driver) from 4.0.0 to 5.0.0. - [Release notes](https://github.com/BetterCloud/vault-java-driver/releases) - [Commits](https://github.com/BetterCloud/vault-java-driver/compare/v4.0.0...v5.0.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index 145cb86bdf2..55249f74fb0 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Vault" dependencies { compile project(':testcontainers') - testCompile 'com.bettercloud:vault-java-driver:4.0.0' + testCompile 'com.bettercloud:vault-java-driver:5.0.0' testCompile 'io.rest-assured:rest-assured:4.1.2' testCompile 'org.assertj:assertj-core:3.14.0' From 7ab24309e6821c22f90e3516ad4bcd8dd5c1a187 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2019 11:28:57 +0000 Subject: [PATCH 1015/5615] Bump jna-platform from 5.4.0 to 5.5.0 in /core (#2017) Bumps [jna-platform](https://github.com/java-native-access/jna) from 5.4.0 to 5.5.0. - [Release notes](https://github.com/java-native-access/jna/releases) - [Changelog](https://github.com/java-native-access/jna/blob/master/CHANGES.md) - [Commits](https://github.com/java-native-access/jna/compare/5.4.0...5.5.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index a580d73c340..d6b862852e3 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -76,7 +76,7 @@ dependencies { exclude(group: "log4j", module: "log4j") } - compile "net.java.dev.jna:jna-platform:5.4.0" + compile "net.java.dev.jna:jna-platform:5.5.0" shaded ('org.scala-sbt.ipcsocket:ipcsocket:1.0.0') { exclude(group: "net.java.dev.jna") From 87bae54f236971f89b05a16e0e7d747eff0aa28d Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 2 Nov 2019 19:35:06 +0000 Subject: [PATCH 1016/5615] Fix docker image name parsing bugs (#2022) * Fix DockerImageName secondary constructor It was cutting down last char of registry name * Add comprehensive unit tests for parsing of image names * Fix bug * Reduce assumptions repeated in test * Switch to using parameterized tests --- .../utility/DockerImageName.java | 4 +- .../utility/DockerImageNameTest.java | 133 ++++++++++++++---- 2 files changed, 105 insertions(+), 32 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/DockerImageName.java b/core/src/main/java/org/testcontainers/utility/DockerImageName.java index bd1c713e26a..d57f1044e80 100644 --- a/core/src/main/java/org/testcontainers/utility/DockerImageName.java +++ b/core/src/main/java/org/testcontainers/utility/DockerImageName.java @@ -61,13 +61,13 @@ public DockerImageName(String name, String tag) { registry = ""; remoteName = name; } else { - registry = name.substring(0, slashIndex - 1); + registry = name.substring(0, slashIndex); remoteName = name.substring(slashIndex + 1); } if (tag.startsWith("sha256:")) { repo = remoteName; - versioning = new Sha256Versioning(tag); + versioning = new Sha256Versioning(tag.replace("sha256:", "")); } else { repo = remoteName; versioning = new TagVersioning(tag); diff --git a/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java b/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java index caef1bcf9ec..37d2472949f 100644 --- a/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java +++ b/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java @@ -1,46 +1,119 @@ package org.testcontainers.utility; import org.junit.Test; +import org.junit.experimental.runners.Enclosed; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.rnorth.visibleassertions.VisibleAssertions; -import static org.junit.Assert.fail; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +@RunWith(Enclosed.class) public class DockerImageNameTest { - @Test - public void validNames() { - testValid("myname:latest"); - testValid("myname:latest"); - testValid("repo/my-name:1.0"); - testValid("repo.foo.com:1234/my-name:1.0"); - testValid("repo.foo.com/my-name:1.0"); - testValid("repo.foo.com:1234/repo_here/my-name:1.0"); - testValid("repo.foo.com:1234/repo-here/my-name@sha256:1234abcd1234abcd1234abcd1234abcd"); - testValid("repo.foo.com:1234/my-name@sha256:1234abcd1234abcd1234abcd1234abcd"); - testValid("1.2.3.4/my-name:1.0"); - testValid("1.2.3.4:1234/my-name:1.0"); - testValid("1.2.3.4/repo-here/my-name:1.0"); - testValid("1.2.3.4:1234/repo-here/my-name:1.0"); - } + @RunWith(Parameterized.class) + public static class ValidNames { + + @Parameterized.Parameters(name = "{0}") + public static String[] getNames() { + return new String[]{"myname:latest", + "myname:latest", + "repo/my-name:1.0", + "registry.foo.com:1234/my-name:1.0", + "registry.foo.com/my-name:1.0", + "registry.foo.com:1234/repo_here/my-name:1.0", + "registry.foo.com:1234/repo-here/my-name@sha256:1234abcd1234abcd1234abcd1234abcd", + "registry.foo.com:1234/my-name@sha256:1234abcd1234abcd1234abcd1234abcd", + "1.2.3.4/my-name:1.0", + "1.2.3.4:1234/my-name:1.0", + "1.2.3.4/repo-here/my-name:1.0", + "1.2.3.4:1234/repo-here/my-name:1.0"}; + } - @Test - public void invalidNames() { - testInvalid(":latest"); - testInvalid("/myname:latest"); - testInvalid("/myname@sha256:latest"); - testInvalid("/myname@sha256:gggggggggggggggggggggggggggggggg"); - testInvalid("repo:notaport/myname:latest"); + @Parameterized.Parameter + public String imageName; + + @Test + public void testValidNameAccepted() { + new DockerImageName(imageName).assertValid(); + } } - private void testValid(String s) { - new DockerImageName(s).assertValid(); + @RunWith(Parameterized.class) + public static class InvalidNames { + + @Parameterized.Parameters(name = "{0}") + public static String[] getNames() { + return new String[]{":latest", + "/myname:latest", + "/myname@sha256:latest", + "/myname@sha256:gggggggggggggggggggggggggggggggg", + "repo:notaport/myname:latest"}; + } + + @Parameterized.Parameter + public String imageName; + + @Test(expected = IllegalArgumentException.class) + public void testInvalidNameRejected() { + new DockerImageName(imageName).assertValid(); + } } - private void testInvalid(String myname) { - try { - new DockerImageName(myname).assertValid(); - fail(); - } catch (IllegalArgumentException expected) { + @RunWith(Parameterized.class) + public static class Parsing { + + @Parameterized.Parameters(name = "{0}{1}{2}{3}{4}") + public static String[][] getNames() { + return new String[][]{ + {"", "", "myname", ":", "latest"}, + {"", "", "repo/myname", ":", "latest"}, + {"registry.foo.com:1234", "/", "my-name", ":", "1.0"}, + {"registry.foo.com", "/", "my-name", ":", "1.0"}, + {"registry.foo.com:1234", "/", "repo_here/my-name", ":", "1.0"}, + {"1.2.3.4:1234", "/", "repo_here/my-name", ":", "1.0"}, + {"1.2.3.4:1234", "/", "my-name", ":", "1.0"}, + {"", "", "myname", "@", "sha256:1234abcd1234abcd1234abcd1234abcd"}, + {"", "", "repo/myname", "@", "sha256:1234abcd1234abcd1234abcd1234abcd"}, + {"registry.foo.com:1234", "/", "repo-here/my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd"}, + {"registry.foo.com:1234", "/", "my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd"}, + {"1.2.3.4", "/", "my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd"}, + {"1.2.3.4:1234", "/", "my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd"}, + {"1.2.3.4", "/", "my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd"}, + {"1.2.3.4:1234", "/", "my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd"} + }; + } + + @Parameterized.Parameter(0) + public String registry; + @Parameterized.Parameter(1) + public String registrySeparator; + @Parameterized.Parameter(2) + public String repo; + @Parameterized.Parameter(3) + public String versionSeparator; + @Parameterized.Parameter(4) + public String version; + + @Test + public void testParsing() { + final String unversionedPart = registry + registrySeparator + repo; + final String combined = unversionedPart + versionSeparator + version; + + VisibleAssertions.context("For " + combined); + VisibleAssertions.context("Using single-arg constructor:", 2); + + final DockerImageName imageName = new DockerImageName(combined); + assertEquals(combined + " has registry address: " + registry, registry, imageName.getRegistry()); + assertEquals(combined + " has unversioned part: " + unversionedPart, unversionedPart, imageName.getUnversionedPart()); + assertEquals(combined + " has version part: " + version, version, imageName.getVersionPart()); + + VisibleAssertions.context("Using two-arg constructor:", 2); + final DockerImageName imageNameFromSecondaryConstructor = new DockerImageName(unversionedPart, version); + assertEquals(combined + " has registry address: " + registry, registry, imageNameFromSecondaryConstructor.getRegistry()); + assertEquals(combined + " has unversioned part: " + unversionedPart, unversionedPart, imageNameFromSecondaryConstructor.getUnversionedPart()); + assertEquals(combined + " has version part: " + version, version, imageNameFromSecondaryConstructor.getVersionPart()); } } } From 033f51d531dcb2410d3f560b1430ad01cd4c93b0 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2019 19:32:39 +0100 Subject: [PATCH 1017/5615] Bump slf4j-api from 1.7.28 to 1.7.29 in /examples (#2031) Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 1.7.28 to 1.7.29. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/commits) Signed-off-by: dependabot-preview[bot] --- examples/linked-container/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 1680aa3b963..eb10aed7159 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -6,7 +6,7 @@ repositories { jcenter() } dependencies { - compileOnly 'org.slf4j:slf4j-api:1.7.28' + compileOnly 'org.slf4j:slf4j-api:1.7.29' implementation 'com.squareup.okhttp3:okhttp:3.14.4' implementation 'org.json:json:20180813' testImplementation 'org.postgresql:postgresql:42.2.8' diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index e339878813c..8b133f0cc1b 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:1.7.28' + compileOnly 'org.slf4j:slf4j-api:1.7.29' implementation 'redis.clients:jedis:3.1.0' implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.guava:guava:23.0' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 727c1649b07..a95000e8d71 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:1.7.28' + compileOnly 'org.slf4j:slf4j-api:1.7.29' implementation 'redis.clients:jedis:3.1.0' implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.guava:guava:23.0' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 250eb755632..6cb2e6d77c5 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -11,7 +11,7 @@ dependencies { implementation 'redis.clients:jedis:3.1.0' implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.guava:guava:23.0' - compileOnly 'org.slf4j:slf4j-api:1.7.28' + compileOnly 'org.slf4j:slf4j-api:1.7.29' testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.testcontainers:testcontainers' From 9b1077bb86ee6bad919915aeb8e70504101c77ab Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Wed, 6 Nov 2019 12:56:29 +0100 Subject: [PATCH 1018/5615] Compare to `0` instead of expecting `1` in `TestEnvironmentTest` (#2036) Future-proofing `TestEnvironmentTest` by making sure we rely on `Comparable#compareTo` contract and not on `ComparableVersion`'s implementation details. --- .../java/org/testcontainers/utility/TestEnvironmentTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/test/java/org/testcontainers/utility/TestEnvironmentTest.java b/core/src/test/java/org/testcontainers/utility/TestEnvironmentTest.java index f34df95137c..de97d100a39 100644 --- a/core/src/test/java/org/testcontainers/utility/TestEnvironmentTest.java +++ b/core/src/test/java/org/testcontainers/utility/TestEnvironmentTest.java @@ -11,7 +11,7 @@ public class TestEnvironmentTest { @Test public void testCompareVersionGreaterThanSameMajor() { - assertTrue("1.22 > 1.20", new ComparableVersion("1.22").compareTo(new ComparableVersion("1.20")) == 1); + assertTrue("1.22 > 1.20", new ComparableVersion("1.22").compareTo(new ComparableVersion("1.20")) > 0); } @Test @@ -21,7 +21,7 @@ public void testCompareVersionEqual() { @Test public void testCompareVersionGreaterThan() { - assertTrue("2.10 > 1.20", new ComparableVersion("2.10").compareTo(new ComparableVersion("1.20")) == 1); + assertTrue("2.10 > 1.20", new ComparableVersion("2.10").compareTo(new ComparableVersion("1.20")) > 0); } @Test From 3b308b862fde3afafb6a78b51106e96011ae541d Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 9 Nov 2019 08:26:16 +0000 Subject: [PATCH 1019/5615] Localstack support for HOSTNAME_EXTERNAL (#1891) * Infer sensible default for HOSTNAME_EXTERNAL Co-authored-by: CauchyPeano <431522+CauchyPeano@users.noreply.github.com> * Restore import style * Update modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java Co-Authored-By: Sergei Egorov * Use nested containers for Localstack tests --- .editorconfig | 8 + .../containers/GenericContainer.java | 1 + docs/modules/localstack.md | 49 ++++-- modules/localstack/build.gradle | 4 +- .../localstack/LocalStackContainer.java | 19 +++ .../localstack/LocalstackContainerTest.java | 152 ++++++++++++++++++ .../localstack/SimpleLocalstackTest.java | 93 ----------- 7 files changed, 217 insertions(+), 109 deletions(-) create mode 100644 modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java delete mode 100644 modules/localstack/src/test/java/org/testcontainers/containers/localstack/SimpleLocalstackTest.java diff --git a/.editorconfig b/.editorconfig index ce20a2be466..4d023ed18d5 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,3 +14,11 @@ trim_trailing_whitespace = true [*.md] trim_trailing_whitespace = false + +[*.java] +indent_style = space +indent_size = 4 +# Never use star imports +ij_java_names_count_to_use_import_on_demand = 99 +ij_java_class_count_to_use_import_on_demand = 99 +ij_java_layout_static_imports_separately = true \ No newline at end of file diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index f17e8dfd0b2..c5df0d4fea1 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -616,6 +616,7 @@ private void applyConfiguration(CreateContainerCmd createCommand) { } String[] envArray = env.entrySet().stream() + .filter(it -> it.getValue() != null) .map(it -> it.getKey() + "=" + it.getValue()) .toArray(String[]::new); createCommand.withEnv(envArray); diff --git a/docs/modules/localstack.md b/docs/modules/localstack.md index 7062b684931..bf520b1fac6 100644 --- a/docs/modules/localstack.md +++ b/docs/modules/localstack.md @@ -7,24 +7,45 @@ Testcontainers module for the Atlassian's [LocalStack](https://github.com/locals Running LocalStack as a stand-in for AWS S3 during a test: ```java -public class SomeTest { +@Rule +public LocalStackContainer localstack = new LocalStackContainer() + .withServices(S3); - @Rule - public LocalStackContainer localstack = new LocalStackContainer() - .withServices(S3); +@Test +public void someTestMethod() { + AmazonS3 s3 = AmazonS3ClientBuilder + .standard() + .withEndpointConfiguration(localstack.getEndpointConfiguration(S3)) + .withCredentials(localstack.getDefaultCredentialsProvider()) + .build(); - @Test - public void someTestMethod() { - AmazonS3 s3 = AmazonS3ClientBuilder - .standard() - .withEndpointConfiguration(localstack.getEndpointConfiguration(S3)) - .withCredentials(localstack.getDefaultCredentialsProvider()) - .build(); - - s3.createBucket("foo"); - s3.putObject("foo", "bar", "baz"); + s3.createBucket("foo"); + s3.putObject("foo", "bar", "baz"); ``` +Environment variables listed in [Localstack's README](https://github.com/localstack/localstack#configurations) may be used to customize Localstack's configuration. +Use the `.withEnv(key, value)` method on `LocalStackContainer` to apply configuration settings. + +## `HOSTNAME_EXTERNAL` and hostname-sensitive services + +Some Localstack APIs, such as SQS, require the container to be aware of the hostname that it is accessible on - for example, for construction of queue URLs in responses. + +Testcontainers will inform Localstack of the best hostname automatically, using the `HOSTNAME_EXTERNAL` environment variable: + +* when running the Localstack container directly without a custom network defined, it is expected that all calls to the container will be from the test host. As such, the container address will be used (typically localhost or the address where the Docker daemon is running). + + + [Localstack container running without a custom network](../../modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java) inside_block:without_network + + +* when running the Localstack container [with a custom network defined](/features/networking/#advanced-networking), it is expected that all calls to the container will be **from other containers on that network**. `HOSTNAME_EXTERNAL` will be set to the *last* network alias that has been configured for the Localstack container. + + + [Localstack container running without a custom network](../../modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java) inside_block:with_network + + +* Other usage scenarios, such as where the Localstack container is used from both the test host and containers on a custom network are not automatically supported. If you have this use case, you should set `HOSTNAME_EXTERNAL` manually. + ## Adding this module to your project dependencies Add the following dependency to your `pom.xml`/`build.gradle` file: diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index eacbc7bb5ae..1ee679abf98 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.479' - testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.479' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.636' + testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.636' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.636' } diff --git a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java index a5005abe239..36eaacba89b 100644 --- a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java +++ b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java @@ -30,6 +30,7 @@ public class LocalStackContainer extends GenericContainer { public static final String VERSION = "0.9.4"; + private static final String HOSTNAME_EXTERNAL_ENV_VAR = "HOSTNAME_EXTERNAL"; private final List services = new ArrayList<>(); @@ -52,6 +53,19 @@ protected void configure() { withEnv("SERVICES", services.stream().map(Service::getLocalStackName).collect(Collectors.joining(","))); + String hostnameExternalReason; + if (getEnvMap().containsKey(HOSTNAME_EXTERNAL_ENV_VAR)) { + // do nothing + hostnameExternalReason = "explicitly as environment variable"; + } else if (getNetwork() != null && getNetworkAliases() != null && getNetworkAliases().size() >= 1) { + withEnv(HOSTNAME_EXTERNAL_ENV_VAR, getNetworkAliases().get(getNetworkAliases().size() - 1)); // use the last network alias set + hostnameExternalReason = "to match last network alias on container with non-default network"; + } else { + withEnv(HOSTNAME_EXTERNAL_ENV_VAR, getContainerIpAddress()); + hostnameExternalReason = "to match host-routable address for container"; + } + logger().info("{} environment variable set to {} ({})", HOSTNAME_EXTERNAL_ENV_VAR, getEnvMap().get(HOSTNAME_EXTERNAL_ENV_VAR), hostnameExternalReason); + for (Service service : services) { addExposedPort(service.getPort()); } @@ -76,6 +90,11 @@ public LocalStackContainer withServices(Service... services) { .withCredentials(localstack.getDefaultCredentialsProvider()) .build(); + * + *

Please note that this method is only intended to be used for configuring AWS SDK clients + * that are running on the test host. If other containers need to call this one, they should be configured + * specifically to do so using a Docker network and appropriate addressing.

+ * * @param service the service that is to be accessed * @return an {@link AwsClientBuilder.EndpointConfiguration} */ diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java new file mode 100644 index 00000000000..1b222c0fbc7 --- /dev/null +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java @@ -0,0 +1,152 @@ +package org.testcontainers.containers.localstack; + + +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import com.amazonaws.services.s3.model.Bucket; +import com.amazonaws.services.s3.model.ObjectListing; +import com.amazonaws.services.s3.model.S3Object; +import com.amazonaws.services.sqs.AmazonSQS; +import com.amazonaws.services.sqs.AmazonSQSClientBuilder; +import com.amazonaws.services.sqs.model.CreateQueueResult; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.IOUtils; +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.runners.Enclosed; +import org.junit.runner.RunWith; +import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.Container; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.Network; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.List; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.testcontainers.containers.localstack.LocalStackContainer.Service.S3; +import static org.testcontainers.containers.localstack.LocalStackContainer.Service.SQS; + +/** + * Tests for Localstack Container, used both in bridge network (exposed to host) and docker network modes. + *

+ * These tests attempt simple interactions with the container to verify behaviour. The bridge network tests use the + * Java AWS SDK, whereas the docker network tests use an AWS CLI container within the network, to simulate usage of + * Localstack from within a Docker network. + */ +@Slf4j +@RunWith(Enclosed.class) +public class LocalstackContainerTest { + + public static class WithoutNetwork { + + // without_network { + @ClassRule + public static LocalStackContainer localstack = new LocalStackContainer() + .withServices(S3, SQS); + // } + + @Test + public void s3TestOverBridgeNetwork() throws IOException { + AmazonS3 s3 = AmazonS3ClientBuilder + .standard() + .withEndpointConfiguration(localstack.getEndpointConfiguration(S3)) + .withCredentials(localstack.getDefaultCredentialsProvider()) + .build(); + + s3.createBucket("foo"); + s3.putObject("foo", "bar", "baz"); + + final List buckets = s3.listBuckets(); + assertEquals("The created bucket is present", 1, buckets.size()); + final Bucket bucket = buckets.get(0); + + assertEquals("The created bucket has the right name", "foo", bucket.getName()); + assertEquals("The created bucket has the right name", "foo", bucket.getName()); + + final ObjectListing objectListing = s3.listObjects("foo"); + assertEquals("The created bucket has 1 item in it", 1, objectListing.getObjectSummaries().size()); + + final S3Object object = s3.getObject("foo", "bar"); + final String content = IOUtils.toString(object.getObjectContent(), Charset.forName("UTF-8")); + assertEquals("The object can be retrieved", "baz", content); + } + + @Test + public void sqsTestOverBridgeNetwork() { + AmazonSQS sqs = AmazonSQSClientBuilder.standard() + .withEndpointConfiguration(localstack.getEndpointConfiguration(SQS)) + .withCredentials(localstack.getDefaultCredentialsProvider()) + .build(); + + CreateQueueResult queueResult = sqs.createQueue("baz"); + String fooQueueUrl = queueResult.getQueueUrl(); + assertThat("Created queue has external hostname URL", fooQueueUrl, + containsString("http://" + DockerClientFactory.instance().dockerHostIpAddress() + ":" + localstack.getMappedPort(SQS.getPort()))); + + sqs.sendMessage(fooQueueUrl, "test"); + final long messageCount = sqs.receiveMessage(fooQueueUrl).getMessages().stream() + .filter(message -> message.getBody().equals("test")) + .count(); + assertEquals("the sent message can be received", 1L, messageCount); + } + } + + public static class WithNetwork { + // with_network { + private static Network network = Network.newNetwork(); + + @ClassRule + public static LocalStackContainer localstackInDockerNetwork = new LocalStackContainer() + .withNetwork(network) + .withNetworkAliases("notthis", "localstack") // the last alias is used for HOSTNAME_EXTERNAL + .withServices(S3, SQS); + // } + + @ClassRule + public static GenericContainer awsCliInDockerNetwork = new GenericContainer<>("atlassian/pipelines-awscli") + .withNetwork(network) + .withCreateContainerCmdModifier(cmd -> cmd.withEntrypoint("top")) + .withEnv("AWS_ACCESS_KEY_ID", "accesskey") + .withEnv("AWS_SECRET_ACCESS_KEY", "secretkey") + .withEnv("AWS_REGION", "eu-west-1"); + + + @Test + public void s3TestOverDockerNetwork() throws Exception { + runAwsCliAgainstDockerNetworkContainer("s3api create-bucket --bucket foo", S3.getPort()); + runAwsCliAgainstDockerNetworkContainer("s3api list-buckets", S3.getPort()); + runAwsCliAgainstDockerNetworkContainer("s3 ls s3://foo", S3.getPort()); + } + + @Test + public void sqsTestOverDockerNetwork() throws Exception { + final String queueCreationResponse = runAwsCliAgainstDockerNetworkContainer("sqs create-queue --queue-name baz", SQS.getPort()); + + assertThat("Created queue has external hostname URL", queueCreationResponse, + containsString("http://localstack:" + SQS.getPort())); + + runAwsCliAgainstDockerNetworkContainer( + String.format("sqs send-message --endpoint http://localstack:%d --queue-url http://localstack:%d/queue/baz --message-body test", SQS.getPort(), SQS.getPort()), SQS.getPort()); + final String message = runAwsCliAgainstDockerNetworkContainer( + String.format("sqs receive-message --endpoint http://localstack:%d --queue-url http://localstack:%d/queue/baz", SQS.getPort(), SQS.getPort()), SQS.getPort()); + + assertTrue("the sent message can be received", message.contains("\"Body\": \"test\"")); + } + + private String runAwsCliAgainstDockerNetworkContainer(String command, final int port) throws Exception { + final String[] commandParts = String.format("/usr/bin/aws --region eu-west-1 %s --endpoint-url http://localstack:%d --no-verify-ssl", command, port).split(" "); + final Container.ExecResult execResult = awsCliInDockerNetwork.execInContainer(commandParts); + Assert.assertEquals(0, execResult.getExitCode()); + + final String logs = execResult.getStdout() + execResult.getStderr(); + log.info(logs); + return logs; + } + } +} diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/SimpleLocalstackTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/SimpleLocalstackTest.java deleted file mode 100644 index f3495d2d8b6..00000000000 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/SimpleLocalstackTest.java +++ /dev/null @@ -1,93 +0,0 @@ -package org.testcontainers.containers.localstack; - - -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.AmazonS3ClientBuilder; -import com.amazonaws.services.s3.model.Bucket; -import com.amazonaws.services.s3.model.ObjectListing; -import com.amazonaws.services.s3.model.S3Object; -import com.amazonaws.services.sqs.AmazonSQS; -import com.amazonaws.services.sqs.AmazonSQSClientBuilder; -import com.amazonaws.services.sqs.model.CreateQueueResult; -import com.amazonaws.services.sqs.model.Message; -import org.apache.commons.io.IOUtils; -import org.junit.ClassRule; -import org.junit.Test; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.Set; - -import static java.util.stream.Collectors.toSet; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.containers.localstack.LocalStackContainer.Service.S3; -import static org.testcontainers.containers.localstack.LocalStackContainer.Service.SQS; - -/** - * The only tests that should go here are for those Localstack services that require special testcontainers code to work with them. - * For example, S3 needs a test to ensure special HTTP routing to the S3 container works. - */ -public class SimpleLocalstackTest { - - private static final String BUCKET_1 = "bucket1"; - private static final String BUCKET_2 = "bucket2"; - private static final String ITEM_KEY = "bar"; - private static final String DUMMY_CONTENT = "baz"; - - @ClassRule - public static LocalStackContainer localstack = new LocalStackContainer() - .withServices(S3, SQS); - - @Test - public void simpleS3Test() throws IOException { - AmazonS3 s3 = AmazonS3ClientBuilder - .standard() - .withEndpointConfiguration(localstack.getEndpointConfiguration(S3)) - .withCredentials(localstack.getDefaultCredentialsProvider()) - .build(); - - s3.createBucket(BUCKET_1); - s3.putObject(BUCKET_1, ITEM_KEY, DUMMY_CONTENT); - - s3.createBucket(BUCKET_2); - s3.putObject(BUCKET_2, ITEM_KEY, DUMMY_CONTENT); - - final Set bucketNames = s3.listBuckets().stream() - .map(Bucket::getName) - .collect(toSet()); - assertTrue("The created buckets have the right name", - bucketNames.contains(BUCKET_1) && bucketNames.contains(BUCKET_2)); - - assertBucketContentsCorrect(s3, BUCKET_1); - assertBucketContentsCorrect(s3, BUCKET_2); - } - - private void assertBucketContentsCorrect(AmazonS3 s3, String bucketName) throws IOException { - final ObjectListing objectListing1 = s3.listObjects(bucketName); - assertEquals("The created bucket has 1 item in it", 1, objectListing1.getObjectSummaries().size()); - - final S3Object object = s3.getObject(bucketName, ITEM_KEY); - final String content = IOUtils.toString(object.getObjectContent(), StandardCharsets.UTF_8); - assertEquals("The object can be retrieved", DUMMY_CONTENT, content); - } - - @Test - public void simpleSQSTest() { - AmazonSQS sqs = AmazonSQSClientBuilder.standard() - .withEndpointConfiguration(localstack.getEndpointConfiguration(SQS)) - .withCredentials(localstack.getDefaultCredentialsProvider()) - .build(); - - CreateQueueResult queueResult = sqs.createQueue("baz"); - String queueUrl = queueResult.getQueueUrl(); - - sqs.sendMessage(queueUrl, "ping"); - - final List messages = sqs.receiveMessage(queueUrl).getMessages(); - - assertTrue("the message queue contains a message", messages.size() > 0); - assertEquals("the first message is the one we sent", "ping", messages.get(0).getBody()); - } -} From d62705a898d95d71c36d61f371fb8120f326323c Mon Sep 17 00:00:00 2001 From: Florian LOPES Date: Sat, 9 Nov 2019 09:27:04 +0100 Subject: [PATCH 1020/5615] Fix typo in index.md (#2043) --- docs/modules/databases/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/modules/databases/index.md b/docs/modules/databases/index.md index b0e5335b7e8..2511502f650 100644 --- a/docs/modules/databases/index.md +++ b/docs/modules/databases/index.md @@ -111,10 +111,10 @@ With this parameter database container will keep running even when there're no o Container can have `tmpfs` mounts for storing data in host memory. This is useful if you want to speed up your database tests. Be aware that the data will be lost when the container stops. -To pass this option to the container, add `TC_TMPFS` paramater to the URL as follows: +To pass this option to the container, add `TC_TMPFS` parameter to the URL as follows: `jdbc:tc:postgresql:9.6.8:///databasename?TC_TMPFS=/testtmpfs:rw` -If you need more than one option, seperate them by comma (e.g. `TC_TMPFS=key:value,key1:value1&other_parameters=foo`). +If you need more than one option, separate them by comma (e.g. `TC_TMPFS=key:value,key1:value1&other_parameters=foo`). For more information about `tmpfs` mount, see [the official Docker documentation](https://docs.docker.com/storage/tmpfs/). From 055f78323f584a9f8b3248196d5ca99fd3cc454f Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 9 Nov 2019 08:28:03 +0000 Subject: [PATCH 1021/5615] Add docker compose support for --build and --rmi options (#1625) * 924: Add docker compose support for --build and --rmi options * Fix bug with merge * Use standard enum style * Refactor test to verify removal of images * Fix param name * Switch to using parameterized tests Co-authored-by: Soner Dastan --- .../containers/DockerComposeContainer.java | 64 ++++++++++++- .../DockerComposeContainerWithBuildTest.java | 92 +++++++++++++++++++ .../resources/compose-build-test/Dockerfile | 3 + .../compose-build-test/docker-compose.yml | 6 ++ 4 files changed, 160 insertions(+), 5 deletions(-) create mode 100644 core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithBuildTest.java create mode 100644 core/src/test/resources/compose-build-test/Dockerfile create mode 100644 core/src/test/resources/compose-build-test/docker-compose.yml diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index b21e160d892..73628a19ad7 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -64,6 +64,7 @@ public class DockerComposeContainer> e private DockerClient dockerClient; private boolean localCompose; private boolean pull = true; + private boolean build = false; private boolean tailChildContainers; private String project; @@ -84,6 +85,7 @@ public class DockerComposeContainer> e * necessarily to containers that are spawned by Compose itself) */ private Map env = new HashMap<>(); + private RemoveImages removeImages; @Deprecated public DockerComposeContainer(File composeFile, String identifier) { @@ -170,11 +172,17 @@ public SELF withServices(@NonNull String... services) { private void createServices() { // Run the docker-compose container, which starts up the services - if(services.isEmpty()) { - runWithCompose("up -d"); - } else { - runWithCompose("up -d " + String.join(" ", services)); + String command = "up -d"; + + if (build) { + command += " --build"; + } + + if (!services.isEmpty()) { + command += " " + String.join(" ", services); } + + runWithCompose(command); } private void waitUntilServiceStarted() { @@ -326,7 +334,11 @@ public void stop() { // Kill the services using docker-compose try { - runWithCompose("down -v"); + String cmd = "down -v"; + if (removeImages != null) { + cmd += " --rmi " + removeImages.dockerRemoveImagesType(); + } + runWithCompose(cmd); // If we reach here then docker-compose down has cleared networks and containers; // we can unregister from ResourceReaper @@ -513,6 +525,26 @@ public SELF withLogConsumer(String serviceName, Consumer consumer) return self(); } + /** + * Whether to always build images before starting containers. + * + * @return this instance, for chaining + */ + public SELF withBuild(boolean build) { + this.build = build; + return self(); + } + + /** + * Remove images after containers shutdown. + * + * @return this instance, for chaining + */ + public SELF withRemoveImages(RemoveImages removeImages) { + this.removeImages = removeImages; + return self(); + } + private void followLogs(String containerId, Consumer consumer) { LogUtils.followOutput(DockerClientFactory.instance().client(), containerId, consumer); } @@ -524,6 +556,28 @@ private SELF self() { private String randomProjectId() { return identifier + Base58.randomString(6).toLowerCase(); } + + public enum RemoveImages { + /** + * Remove all images used by any service. + */ + ALL("all"), + + /** + * Remove only images that don't have a custom tag set by the `image` field. + */ + LOCAL("local"); + + private final String dockerRemoveImagesType; + + RemoveImages(final String dockerRemoveImagesType) { + this.dockerRemoveImagesType = dockerRemoveImagesType; + } + + public String dockerRemoveImagesType() { + return dockerRemoveImagesType; + } + } } interface DockerCompose { diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithBuildTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithBuildTest.java new file mode 100644 index 00000000000..1b1efc878af --- /dev/null +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithBuildTest.java @@ -0,0 +1,92 @@ +package org.testcontainers.junit; + +import com.github.dockerjava.api.model.Container; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.rnorth.ducttape.unreliables.Unreliables; +import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.DockerComposeContainer; + +import java.io.File; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Stream; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.testcontainers.containers.DockerComposeContainer.RemoveImages.ALL; +import static org.testcontainers.containers.DockerComposeContainer.RemoveImages.LOCAL; + +@RunWith(Parameterized.class) +public class DockerComposeContainerWithBuildTest { + + public DockerComposeContainerWithBuildTest(final DockerComposeContainer.RemoveImages removeMode, + final boolean shouldBuiltImageBePresentAfterRunning, + final boolean shouldPulledImageBePresentAfterRunning) { + this.removeMode = removeMode; + this.shouldBuiltImageBePresentAfterRunning = shouldBuiltImageBePresentAfterRunning; + this.shouldPulledImageBePresentAfterRunning = shouldPulledImageBePresentAfterRunning; + } + + public final DockerComposeContainer.RemoveImages removeMode; + public final boolean shouldBuiltImageBePresentAfterRunning; + public final boolean shouldPulledImageBePresentAfterRunning; + + @Parameterized.Parameters(name = "removeMode = {0}") + public static Object[][] params() { + return new Object[][]{ + {null, true, true}, + {LOCAL, false, true}, + {ALL, false, false}, + }; + } + + @Test + public void performTest() { + final File composeFile = new File("src/test/resources/compose-build-test/docker-compose.yml"); + + final AtomicReference builtImageName = new AtomicReference<>(""); + final AtomicReference pulledImageName = new AtomicReference<>(""); + try (DockerComposeContainer environment = new DockerComposeContainer<>(composeFile) + .withExposedService("customredis", 6379) + .withBuild(true) + .withRemoveImages(removeMode)) { + + environment.start(); + + builtImageName.set(imageNameForRunningContainer("_customredis_1")); + final boolean isBuiltImagePresentWhileRunning = isImagePresent(builtImageName.get()); + assertEquals("the built image is present while running", true, isBuiltImagePresentWhileRunning); + + pulledImageName.set(imageNameForRunningContainer("_normalredis_1")); + final boolean isPulledImagePresentWhileRunning = isImagePresent(pulledImageName.get()); + assertEquals("the pulled image is present while running", true, isPulledImagePresentWhileRunning); + } + + Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, () -> { + final boolean isBuiltImagePresentAfterRunning = isImagePresent(builtImageName.get()); + assertEquals("the built image is not present after running", shouldBuiltImageBePresentAfterRunning, isBuiltImagePresentAfterRunning); + return null; + }); + + Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, () -> { + final boolean isPulledImagePresentAfterRunning = isImagePresent(pulledImageName.get()); + assertEquals("the pulled image is present after running", shouldPulledImageBePresentAfterRunning, isPulledImagePresentAfterRunning); + return null; + }); + } + + private String imageNameForRunningContainer(final String containerNameSuffix) { + return DockerClientFactory.instance().client().listContainersCmd().exec().stream() + .filter(it -> Stream.of(it.getNames()).anyMatch(name -> name.endsWith(containerNameSuffix))) + .findFirst() + .map(Container::getImage) + .orElseThrow(IllegalStateException::new); + } + + private boolean isImagePresent(final String imageName) { + return DockerClientFactory.instance().client().listImagesCmd().withImageNameFilter(imageName).exec().stream() + .findFirst() + .isPresent(); + } +} diff --git a/core/src/test/resources/compose-build-test/Dockerfile b/core/src/test/resources/compose-build-test/Dockerfile new file mode 100644 index 00000000000..521e2d23b80 --- /dev/null +++ b/core/src/test/resources/compose-build-test/Dockerfile @@ -0,0 +1,3 @@ +FROM redis + +CMD ["redis-server"] diff --git a/core/src/test/resources/compose-build-test/docker-compose.yml b/core/src/test/resources/compose-build-test/docker-compose.yml new file mode 100644 index 00000000000..333b2fa46f1 --- /dev/null +++ b/core/src/test/resources/compose-build-test/docker-compose.yml @@ -0,0 +1,6 @@ +version: '2.0' +services: + customredis: + build: . + normalredis: + image: redis:latest From fb9e7ef1c9aefd18a334708e33c2771760ab8c24 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 9 Nov 2019 08:28:24 +0000 Subject: [PATCH 1022/5615] =?UTF-8?q?Bump=20org.springframework.boot=20fro?= =?UTF-8?q?m=202.2.0.RELEASE=20to=202.2.1.RELEA=E2=80=A6=20(#2042)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [org.springframework.boot](https://github.com/spring-projects/spring-boot) from 2.2.0.RELEASE to 2.2.1.RELEASE. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.2.0.RELEASE...v2.2.1.RELEASE) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index 2c15c3038ba..5386234dccb 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.2.0.RELEASE' + id 'org.springframework.boot' version '2.2.1.RELEASE' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index df18e6a5f03..2a67123fa03 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.2.0.RELEASE' + id 'org.springframework.boot' version '2.2.1.RELEASE' } apply plugin: 'io.spring.dependency-management' From e86fb093d80e7c9576f8063415861eb2dc30fa91 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 9 Nov 2019 08:28:35 +0000 Subject: [PATCH 1023/5615] Bump mongo-java-driver from 3.11.1 to 3.11.2 in /core (#2035) Bumps [mongo-java-driver](https://github.com/mongodb/mongo-java-driver) from 3.11.1 to 3.11.2. - [Release notes](https://github.com/mongodb/mongo-java-driver/releases) - [Commits](https://github.com/mongodb/mongo-java-driver/compare/r3.11.1...r3.11.2) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index d6b862852e3..1b037390956 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -108,7 +108,7 @@ dependencies { testCompile 'org.apache.httpcomponents:httpclient:4.5.9' testCompile 'redis.clients:jedis:3.1.0' testCompile 'com.rabbitmq:amqp-client:5.7.3' - testCompile 'org.mongodb:mongo-java-driver:3.11.1' + testCompile 'org.mongodb:mongo-java-driver:3.11.2' testCompile ('org.mockito:mockito-core:3.1.0') { exclude(module: 'hamcrest-core') } From 84b86aff56efe8b1ff20dc29ad455b4116cd1092 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 9 Nov 2019 08:28:44 +0000 Subject: [PATCH 1024/5615] Bump slf4j-api from 1.7.28 to 1.7.29 in /core (#2030) Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 1.7.28 to 1.7.29. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/commits) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> Co-authored-by: Kevin Wittek --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 1b037390956..9da3288d0c3 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -57,7 +57,7 @@ project.tasks.check.dependsOn(jarFileTest) dependencies { compile 'junit:junit:4.12' - compile 'org.slf4j:slf4j-api:1.7.28' + compile 'org.slf4j:slf4j-api:1.7.29' compile 'org.jetbrains:annotations:17.0.0' compile 'javax.annotation:javax.annotation-api:1.3.2' compile 'org.apache.commons:commons-compress:1.19' From f3011b6cfaf058fef26583d259bad00f9103a2ea Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sat, 9 Nov 2019 11:09:54 +0100 Subject: [PATCH 1025/5615] Check container's status if `WaitStrategy` fails. (#1837) * Check container's status if `WaitStrategy` fails. Fixes #1834 * Try "128Gb" * Update core/src/main/java/org/testcontainers/containers/GenericContainer.java Co-Authored-By: Richard North * Use `/dev/null` as dd's target, ignore startup check strategy * Achieve OOMKill in a way that works reliably on CI On CircleCI there were two problems: * `dd: /dev/urandom: Bad address` when `/dev/urandom` was used, which would cause exit code 1, not the desired OOMKill * Once worked-around, `dd` was only processing a partial block: ``` 0+1 records in 0+1 records out ``` I suspect that `dd` might internally be respecting, or suffering limited buffers due to the cgroup memory limit, though I'm not clear why this behaviour only appeared on CircleCI and Azure pipelines. * Limit loop * Allocate more memory to trigger the OOM killer * increase the memory even more --- .../containers/GenericContainer.java | 45 ++++++++++- .../containers/GenericContainerTest.java | 75 +++++++++++++++++++ 2 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 core/src/test/java/org/testcontainers/containers/GenericContainerTest.java diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index c5df0d4fea1..18c53f8b80f 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -7,6 +7,7 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.CreateContainerCmd; import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.exception.NotFoundException; import com.github.dockerjava.api.model.Bind; import com.github.dockerjava.api.model.ContainerNetwork; import com.github.dockerjava.api.model.ExposedPort; @@ -69,7 +70,9 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.UndeclaredThrowableException; import java.nio.charset.Charset; import java.nio.file.Path; import java.time.Duration; @@ -397,11 +400,51 @@ private void tryStart(Instant startedAt) { } // Wait until the process within the container has become ready for use (e.g. listening on network, log message emitted, etc). - waitUntilContainerStarted(); + try { + waitUntilContainerStarted(); + } catch (Exception e) { + logger().debug("Wait strategy threw an exception", e); + InspectContainerResponse inspectContainerResponse = null; + try { + inspectContainerResponse = dockerClient.inspectContainerCmd(containerId).exec(); + } catch (NotFoundException notFoundException) { + logger().debug("Container {} not found", containerId, notFoundException); + } + + if (inspectContainerResponse == null) { + throw new IllegalStateException("Container is removed"); + } + + InspectContainerResponse.ContainerState state = inspectContainerResponse.getState(); + if (Boolean.TRUE.equals(state.getDead())) { + throw new IllegalStateException("Container is dead"); + } + + if (Boolean.TRUE.equals(state.getOOMKilled())) { + throw new IllegalStateException("Container crashed with out-of-memory (OOMKilled)"); + } + + String error = state.getError(); + if (!StringUtils.isBlank(error)) { + throw new IllegalStateException("Container crashed: " + error); + } + + if (!Boolean.TRUE.equals(state.getRunning())) { + throw new IllegalStateException("Container exited with code " + state.getExitCode()); + } + + throw e; + } logger().info("Container {} started in {}", dockerImageName, Duration.between(startedAt, Instant.now())); containerIsStarted(containerInfo); } catch (Exception e) { + if (e instanceof UndeclaredThrowableException && e.getCause() instanceof Exception) { + e = (Exception) e.getCause(); + } + if (e instanceof InvocationTargetException && e.getCause() instanceof Exception) { + e = (Exception) e.getCause(); + } logger().error("Could not start container", e); if (containerId != null) { diff --git a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java new file mode 100644 index 00000000000..c69a3b69594 --- /dev/null +++ b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java @@ -0,0 +1,75 @@ +package org.testcontainers.containers; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.command.InspectContainerResponse.ContainerState; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.experimental.FieldDefaults; +import org.apache.commons.io.FileUtils; +import org.junit.Test; +import org.rnorth.ducttape.unreliables.Unreliables; +import org.testcontainers.containers.startupcheck.StartupCheckStrategy; +import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy; + +import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class GenericContainerTest { + + @Test + public void shouldReportOOMAfterWait() { + try ( + GenericContainer container = new GenericContainer<>() + .withStartupCheckStrategy(new NoopStartupCheckStrategy()) + .waitingFor(new WaitForState(ContainerState::getOOMKilled)) + .withCreateContainerCmdModifier(it -> { + it.getHostConfig().withMemory(4 * FileUtils.ONE_MB); + }) + .withCommand("sh", "-c", "A='0123456789'; for i in $(seq 0 32); do A=$A$A; done") + ) { + assertThatThrownBy(container::start) + .hasStackTraceContaining("Container crashed with out-of-memory"); + } + } + + @Test + public void shouldReportErrorAfterWait() { + try ( + GenericContainer container = new GenericContainer<>() + .withStartupCheckStrategy(new NoopStartupCheckStrategy()) + .waitingFor(new WaitForState(state -> state.getExitCode() > 0)) + .withCommand("sh", "-c", "usleep 100; exit 123") + ) { + assertThatThrownBy(container::start) + .hasStackTraceContaining("Container exited with code 123"); + } + } + + static class NoopStartupCheckStrategy extends StartupCheckStrategy { + + @Override + public StartupStatus checkStartupState(DockerClient dockerClient, String containerId) { + return StartupStatus.SUCCESSFUL; + } + } + + @RequiredArgsConstructor + @FieldDefaults(makeFinal = true) + static class WaitForState extends AbstractWaitStrategy { + + Predicate predicate; + + @Override + @SneakyThrows + protected void waitUntilReady() { + Unreliables.retryUntilTrue(5, TimeUnit.SECONDS, () -> { + ContainerState state = waitStrategyTarget.getCurrentContainerInfo().getState(); + return predicate.test(state); + }); + + throw new IllegalStateException("Nope!"); + } + } +} From 6aced9f4954053b57d6f72623711099bb643a1e4 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 10 Nov 2019 09:33:54 +0100 Subject: [PATCH 1026/5615] debug `GenericContainerTest` --- .../containers/GenericContainerTest.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java index c69a3b69594..a5fbd9541e8 100644 --- a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java +++ b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java @@ -5,6 +5,7 @@ import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.experimental.FieldDefaults; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; import org.junit.Test; import org.rnorth.ducttape.unreliables.Unreliables; @@ -23,7 +24,7 @@ public void shouldReportOOMAfterWait() { try ( GenericContainer container = new GenericContainer<>() .withStartupCheckStrategy(new NoopStartupCheckStrategy()) - .waitingFor(new WaitForState(ContainerState::getOOMKilled)) + .waitingFor(new WaitForExitedState(ContainerState::getOOMKilled)) .withCreateContainerCmdModifier(it -> { it.getHostConfig().withMemory(4 * FileUtils.ONE_MB); }) @@ -39,7 +40,7 @@ public void shouldReportErrorAfterWait() { try ( GenericContainer container = new GenericContainer<>() .withStartupCheckStrategy(new NoopStartupCheckStrategy()) - .waitingFor(new WaitForState(state -> state.getExitCode() > 0)) + .waitingFor(new WaitForExitedState(state -> state.getExitCode() > 0)) .withCommand("sh", "-c", "usleep 100; exit 123") ) { assertThatThrownBy(container::start) @@ -57,7 +58,8 @@ public StartupStatus checkStartupState(DockerClient dockerClient, String contain @RequiredArgsConstructor @FieldDefaults(makeFinal = true) - static class WaitForState extends AbstractWaitStrategy { + @Slf4j + static class WaitForExitedState extends AbstractWaitStrategy { Predicate predicate; @@ -66,6 +68,12 @@ static class WaitForState extends AbstractWaitStrategy { protected void waitUntilReady() { Unreliables.retryUntilTrue(5, TimeUnit.SECONDS, () -> { ContainerState state = waitStrategyTarget.getCurrentContainerInfo().getState(); + + log.debug("Current state: {}", state); + if (!"exited".equalsIgnoreCase(state.getStatus())) { + Thread.sleep(100); + return false; + } return predicate.test(state); }); From 7562e05b9771b48113b7602deebb2c9971594566 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 10 Nov 2019 10:45:19 +0100 Subject: [PATCH 1027/5615] Add sleep to GenericContainerTest --- .../org/testcontainers/containers/GenericContainerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java index a5fbd9541e8..f805ee1aa9f 100644 --- a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java +++ b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java @@ -28,7 +28,7 @@ public void shouldReportOOMAfterWait() { .withCreateContainerCmdModifier(it -> { it.getHostConfig().withMemory(4 * FileUtils.ONE_MB); }) - .withCommand("sh", "-c", "A='0123456789'; for i in $(seq 0 32); do A=$A$A; done") + .withCommand("sh", "-c", "A='0123456789'; for i in $(seq 0 32); do A=$A$A; sleep 0.01; done; sleep 10m") ) { assertThatThrownBy(container::start) .hasStackTraceContaining("Container crashed with out-of-memory"); From 2fe206c4cc337e1ab38c401c89ceca56c672cf1f Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 10 Nov 2019 20:43:55 +0100 Subject: [PATCH 1028/5615] Fix session label when reuse is not supported but requested (#2051) --- .../containers/GenericContainer.java | 7 +++++ .../containers/ReusabilityUnitTests.java | 29 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 18c53f8b80f..6ce8a2f5b09 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -341,6 +341,7 @@ private void tryStart(Instant startedAt) { createCommand.getLabels().put(DockerClientFactory.TESTCONTAINERS_LABEL, "true"); boolean reused = false; + final boolean reusable; if (shouldBeReused) { if (!canBeReused()) { throw new IllegalStateException("This container does not support reuse"); @@ -359,10 +360,16 @@ private void tryStart(Instant startedAt) { createCommand.getLabels().put(HASH_LABEL, hash); } + reusable = true; } else { logger().info("Reuse was requested but the environment does not support the reuse of containers"); + reusable = false; } } else { + reusable = false; + } + + if (!reusable) { createCommand.getLabels().put(DockerClientFactory.TESTCONTAINERS_SESSION_ID_LABEL, DockerClientFactory.SESSION_ID); } diff --git a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java index b05db5b5077..32adbaa837f 100644 --- a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java +++ b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java @@ -20,6 +20,7 @@ import org.mockito.Mockito; import org.mockito.stubbing.Answer; import org.rnorth.visibleassertions.VisibleAssertions; +import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.startupcheck.StartupCheckStrategy; import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy; import org.testcontainers.containers.wait.strategy.WaitStrategy; @@ -29,9 +30,12 @@ import java.util.List; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; @@ -149,6 +153,26 @@ public void shouldReuseIfListReturnsID() { Mockito.verify(client, Mockito.never()).startContainerCmd(existingContainerId); } + @Test + public void shouldSetLabelsIfEnvironmentDoesNotSupportReuse() { + // TODO mock TestcontainersConfiguration + Assume.assumeFalse("does not support reuse", TestcontainersConfiguration.getInstance().environmentSupportsReuse()); + AtomicReference commandRef = new AtomicReference<>(); + String containerId = randomContainerId(); + when(client.createContainerCmd(any())).then(createContainerAnswer(containerId, commandRef::set)); + when(client.startContainerCmd(containerId)).then(startContainerAnswer()); + when(client.inspectContainerCmd(containerId)).then(inspectContainerAnswer()); + + container.start(); + + assertThat(commandRef) + .isNotNull() + .satisfies(command -> { + assertThat(command.get().getLabels()) + .containsKeys(DockerClientFactory.TESTCONTAINERS_SESSION_ID_LABEL); + }); + } + private String randomContainerId() { return UUID.randomUUID().toString(); } @@ -168,8 +192,13 @@ private Answer listContainersAnswer(String... ids) { } private Answer createContainerAnswer(String containerId) { + return createContainerAnswer(containerId, command -> {}); + } + + private Answer createContainerAnswer(String containerId, Consumer cmdConsumer) { return invocation -> { CreateContainerCmd.Exec exec = command -> { + cmdConsumer.accept(command); CreateContainerResponse response = new CreateContainerResponse(); response.setId(containerId); return response; From f59b123f7ee116776b9ae38e7eb030d2be5bbfd4 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 10 Nov 2019 21:02:33 +0100 Subject: [PATCH 1029/5615] Try increasing the limit --- .../org/testcontainers/containers/GenericContainerTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java index f805ee1aa9f..dfc4a955713 100644 --- a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java +++ b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java @@ -26,9 +26,9 @@ public void shouldReportOOMAfterWait() { .withStartupCheckStrategy(new NoopStartupCheckStrategy()) .waitingFor(new WaitForExitedState(ContainerState::getOOMKilled)) .withCreateContainerCmdModifier(it -> { - it.getHostConfig().withMemory(4 * FileUtils.ONE_MB); + it.getHostConfig().withMemory(32 * FileUtils.ONE_MB); }) - .withCommand("sh", "-c", "A='0123456789'; for i in $(seq 0 32); do A=$A$A; sleep 0.01; done; sleep 10m") + .withCommand("sh", "-c", "A='0123456789'; for i in $(seq 0 32); do A=$A$A; done; sleep 10m") ) { assertThatThrownBy(container::start) .hasStackTraceContaining("Container crashed with out-of-memory"); From 86bfd78e86cd84207e086921f1d2f3bd2d2bb6b1 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 10 Nov 2019 21:23:27 +0100 Subject: [PATCH 1030/5615] tune some memory params --- .../testcontainers/containers/GenericContainerTest.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java index dfc4a955713..c29f6f37c36 100644 --- a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java +++ b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java @@ -2,6 +2,7 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.InspectContainerResponse.ContainerState; +import com.github.dockerjava.api.model.HostConfig; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.experimental.FieldDefaults; @@ -26,7 +27,12 @@ public void shouldReportOOMAfterWait() { .withStartupCheckStrategy(new NoopStartupCheckStrategy()) .waitingFor(new WaitForExitedState(ContainerState::getOOMKilled)) .withCreateContainerCmdModifier(it -> { - it.getHostConfig().withMemory(32 * FileUtils.ONE_MB); + it.getHostConfig() + .withMemory(20 * FileUtils.ONE_MB) + .withMemorySwappiness(0L) + .withMemorySwap(0L) + .withMemoryReservation(0L) + .withKernelMemory(16 * FileUtils.ONE_MB); }) .withCommand("sh", "-c", "A='0123456789'; for i in $(seq 0 32); do A=$A$A; done; sleep 10m") ) { From c0277514327097abadaed58dadaa375c32fcdb5c Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Mon, 11 Nov 2019 16:09:25 +0100 Subject: [PATCH 1031/5615] Make `Startables#deepStart` always iterate sequentially (#2053) * Reproduce the issue with passing parallel Stream into Startables#deepStart * Fix the issue by sanitizing the input stream --- .../org/testcontainers/lifecycle/Startables.java | 1 + .../testcontainers/junit/DependenciesTest.java | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/lifecycle/Startables.java b/core/src/main/java/org/testcontainers/lifecycle/Startables.java index 0e6452c1914..6cf57cc6f6e 100644 --- a/core/src/main/java/org/testcontainers/lifecycle/Startables.java +++ b/core/src/main/java/org/testcontainers/lifecycle/Startables.java @@ -60,6 +60,7 @@ public CompletableFuture deepStart(Stream startables) { */ private CompletableFuture deepStart(Map> started, Stream startables) { CompletableFuture[] futures = startables + .sequential() .map(it -> { // avoid a recursive update in `computeIfAbsent` Map> subStarted = new HashMap<>(started); diff --git a/core/src/test/java/org/testcontainers/junit/DependenciesTest.java b/core/src/test/java/org/testcontainers/junit/DependenciesTest.java index df7e85a31f8..4f2ee01bc6d 100644 --- a/core/src/test/java/org/testcontainers/junit/DependenciesTest.java +++ b/core/src/test/java/org/testcontainers/junit/DependenciesTest.java @@ -9,9 +9,11 @@ import org.testcontainers.lifecycle.Startables; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; import java.util.stream.Stream; public class DependenciesTest { @@ -117,6 +119,19 @@ public void shouldHandleDiamondDependencies() throws Exception { VisibleAssertions.assertEquals("D started", 1, d.getStartInvocationCount().intValue()); } + @Test + public void shouldHandleParallelStream() throws Exception { + List startables = Stream.generate(InvocationCountingStartable::new) + .limit(10) + .collect(Collectors.toList()); + + for (int i = 1; i < startables.size(); i++) { + startables.get(0).getDependencies().add(startables.get(i)); + } + + Startables.deepStart(startables.parallelStream()).get(1, TimeUnit.SECONDS); + } + private static class InvocationCountingStartable implements Startable { @Getter @@ -131,7 +146,6 @@ private static class InvocationCountingStartable implements Startable { @Override public void start() { startInvocationCount.getAndIncrement(); - } @Override From 7e5adade117e94ac0ed4e825b35ac0bdaf45c8fa Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 11 Nov 2019 18:11:11 +0100 Subject: [PATCH 1032/5615] Add `reused` parameter to the hooks (#2052) * Add `containerIsReused` hook * Add `reused` parameter to `containerIs{Starting,Started}` * post-merge fixes --- .../containers/GenericContainer.java | 16 ++- .../containers/ReusabilityUnitTests.java | 134 ++++++++++++++---- .../containers/KafkaContainer.java | 8 +- 3 files changed, 130 insertions(+), 28 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 6ce8a2f5b09..47d08b57528 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -397,7 +397,7 @@ private void tryStart(Instant startedAt) { // Tell subclasses that we're starting containerInfo = dockerClient.inspectContainerCmd(containerId).exec(); containerName = containerInfo.getName(); - containerIsStarting(containerInfo); + containerIsStarting(containerInfo, reused); // Wait until the container has reached the desired running state if (!this.startupCheckStrategy.waitUntilStartupSuccessful(dockerClient, containerId)) { @@ -444,7 +444,7 @@ private void tryStart(Instant startedAt) { } logger().info("Container {} started in {}", dockerImageName, Duration.between(startedAt, Instant.now())); - containerIsStarted(containerInfo); + containerIsStarted(containerInfo, reused); } catch (Exception e) { if (e instanceof UndeclaredThrowableException && e.getCause() instanceof Exception) { e = (Exception) e.getCause(); @@ -582,10 +582,22 @@ protected void containerIsCreated(String containerId) { protected void containerIsStarting(InspectContainerResponse containerInfo) { } + @SuppressWarnings({"EmptyMethod", "UnusedParameters"}) + @UnstableAPI + protected void containerIsStarting(InspectContainerResponse containerInfo, boolean reused) { + containerIsStarting(containerInfo); + } + @SuppressWarnings({"EmptyMethod", "UnusedParameters"}) protected void containerIsStarted(InspectContainerResponse containerInfo) { } + @SuppressWarnings({"EmptyMethod", "UnusedParameters"}) + @UnstableAPI + protected void containerIsStarted(InspectContainerResponse containerInfo, boolean reused) { + containerIsStarted(containerInfo); + } + /** * A hook that is executed before the container is stopped with {@link #stop()}. * Warning! This hook won't be executed if the container is terminated during diff --git a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java index 32adbaa837f..2d0f7a8133a 100644 --- a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java +++ b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java @@ -23,9 +23,9 @@ import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.startupcheck.StartupCheckStrategy; import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy; -import org.testcontainers.containers.wait.strategy.WaitStrategy; import org.testcontainers.utility.TestcontainersConfiguration; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.UUID; @@ -98,34 +98,93 @@ protected void containerIsCreated(String containerId) { @RunWith(BlockJUnit4ClassRunner.class) @FieldDefaults(makeFinal = true) - public static class HashTest { + public static class HooksTest extends AbstractReusabilityTest { + + List script = new ArrayList<>(); + + GenericContainer container = makeReusable(new GenericContainer(IMAGE_FUTURE) { - StartupCheckStrategy startupCheckStrategy = new StartupCheckStrategy() { @Override - public StartupStatus checkStartupState(DockerClient dockerClient, String containerId) { - return StartupStatus.SUCCESSFUL; + protected boolean canBeReused() { + // Because we override "containerIsCreated" + return true; } - }; - WaitStrategy waitStrategy = new AbstractWaitStrategy() { @Override - protected void waitUntilReady() { + protected void containerIsCreated(String containerId) { + script.add("containerIsCreated"); + } + @Override + protected void containerIsStarting(InspectContainerResponse containerInfo, boolean reused) { + script.add("containerIsStarting(reused=" + reused + ")"); } - }; - DockerClient client = Mockito.mock(DockerClient.class); + @Override + protected void containerIsStarted(InspectContainerResponse containerInfo, boolean reused) { + script.add("containerIsStarted(reused=" + reused + ")"); + } + }); - GenericContainer container = new GenericContainer(IMAGE_FUTURE) - .withNetworkMode("none") // to disable the port forwarding - .withStartupCheckStrategy(startupCheckStrategy) - .waitingFor(waitStrategy) - .withReuse(true); + @Test + public void shouldSetLabelsIfEnvironmentDoesNotSupportReuse() { + // TODO mock TestcontainersConfiguration + Assume.assumeFalse("does not support reuse", TestcontainersConfiguration.getInstance().environmentSupportsReuse()); + AtomicReference commandRef = new AtomicReference<>(); + String containerId = randomContainerId(); + when(client.createContainerCmd(any())).then(createContainerAnswer(containerId)); + when(client.listContainersCmd()).then(listContainersAnswer()); + when(client.startContainerCmd(containerId)).then(startContainerAnswer()); + when(client.inspectContainerCmd(containerId)).then(inspectContainerAnswer()); - { - container.dockerClient = client; + container.start(); + assertThat(script).containsExactly( + "containerIsCreated", + "containerIsStarting(reused=false)", + "containerIsStarted(reused=false)" + ); + } + + @Test + public void shouldCallHookIfReused() { + // TODO mock TestcontainersConfiguration + Assume.assumeTrue("supports reuse", TestcontainersConfiguration.getInstance().environmentSupportsReuse()); + String containerId = randomContainerId(); + when(client.createContainerCmd(any())).then(createContainerAnswer(containerId)); + String existingContainerId = randomContainerId(); + when(client.listContainersCmd()).then(listContainersAnswer(existingContainerId)); + when(client.inspectContainerCmd(existingContainerId)).then(inspectContainerAnswer()); + + container.start(); + assertThat(script).containsExactly( + "containerIsStarting(reused=true)", + "containerIsStarted(reused=true)" + ); } + @Test + public void shouldNotCallHookIfNotReused() { + String containerId = randomContainerId(); + when(client.createContainerCmd(any())).then(createContainerAnswer(containerId)); + when(client.listContainersCmd()).then(listContainersAnswer()); + when(client.startContainerCmd(containerId)).then(startContainerAnswer()); + when(client.inspectContainerCmd(containerId)).then(inspectContainerAnswer()); + + container.start(); + assertThat(script).containsExactly( + "containerIsCreated", + "containerIsStarting(reused=false)", + "containerIsStarted(reused=false)" + ); + } + } + + @RunWith(BlockJUnit4ClassRunner.class) + @FieldDefaults(makeFinal = true) + public static class HashTest extends AbstractReusabilityTest { + + protected GenericContainer container = makeReusable(new GenericContainer<>(IMAGE_FUTURE)); + @Test public void shouldStartIfListReturnsEmpty() { String containerId = randomContainerId(); @@ -143,13 +202,15 @@ public void shouldStartIfListReturnsEmpty() { public void shouldReuseIfListReturnsID() { // TODO mock TestcontainersConfiguration Assume.assumeTrue("supports reuse", TestcontainersConfiguration.getInstance().environmentSupportsReuse()); - when(client.createContainerCmd(any())).then(createContainerAnswer(randomContainerId())); + String containerId = randomContainerId(); + when(client.createContainerCmd(any())).then(createContainerAnswer(containerId)); String existingContainerId = randomContainerId(); when(client.listContainersCmd()).then(listContainersAnswer(existingContainerId)); when(client.inspectContainerCmd(existingContainerId)).then(inspectContainerAnswer()); container.start(); + Mockito.verify(client, Mockito.never()).startContainerCmd(containerId); Mockito.verify(client, Mockito.never()).startContainerCmd(existingContainerId); } @@ -172,12 +233,37 @@ public void shouldSetLabelsIfEnvironmentDoesNotSupportReuse() { .containsKeys(DockerClientFactory.TESTCONTAINERS_SESSION_ID_LABEL); }); } + } + + @FieldDefaults(makeFinal = true) + static abstract class AbstractReusabilityTest { + + protected DockerClient client = Mockito.mock(DockerClient.class); + + protected > T makeReusable(T container) { + container.dockerClient = client; + container.withNetworkMode("none"); // to disable the port forwarding + container.withStartupCheckStrategy(new StartupCheckStrategy() { + @Override + public StartupStatus checkStartupState(DockerClient dockerClient, String containerId) { + return StartupStatus.SUCCESSFUL; + } + }); + container.waitingFor(new AbstractWaitStrategy() { + @Override + protected void waitUntilReady() { + + } + }); + container.withReuse(true); + return container; + } - private String randomContainerId() { + protected String randomContainerId() { return UUID.randomUUID().toString(); } - private Answer listContainersAnswer(String... ids) { + protected Answer listContainersAnswer(String... ids) { return invocation -> { ListContainersCmd.Exec exec = command -> { return new ObjectMapper().convertValue( @@ -191,11 +277,11 @@ private Answer listContainersAnswer(String... ids) { }; } - private Answer createContainerAnswer(String containerId) { + protected Answer createContainerAnswer(String containerId) { return createContainerAnswer(containerId, command -> {}); } - private Answer createContainerAnswer(String containerId, Consumer cmdConsumer) { + protected Answer createContainerAnswer(String containerId, Consumer cmdConsumer) { return invocation -> { CreateContainerCmd.Exec exec = command -> { cmdConsumer.accept(command); @@ -207,7 +293,7 @@ private Answer createContainerAnswer(String containerId, Con }; } - private Answer startContainerAnswer() { + protected Answer startContainerAnswer() { return invocation -> { StartContainerCmd.Exec exec = command -> { return null; @@ -216,7 +302,7 @@ private Answer startContainerAnswer() { }; } - private Answer inspectContainerAnswer() { + protected Answer inspectContainerAnswer() { return invocation -> { InspectContainerCmd.Exec exec = command -> { return new InspectContainerResponse(); diff --git a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java index b08d6611e6d..ede8cb5d086 100644 --- a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java +++ b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java @@ -80,8 +80,12 @@ protected void doStart() { @Override @SneakyThrows - protected void containerIsStarting(InspectContainerResponse containerInfo) { - super.containerIsStarting(containerInfo); + protected void containerIsStarting(InspectContainerResponse containerInfo, boolean reused) { + super.containerIsStarting(containerInfo, reused); + + if (reused) { + return; + } port = getMappedPort(KAFKA_PORT); From 3142e58b9d38205d94aabc9fefef7651c502b95f Mon Sep 17 00:00:00 2001 From: Dominik Weidenfeld Date: Mon, 11 Nov 2019 23:57:10 +0100 Subject: [PATCH 1033/5615] added read timeout to http wait strategy (#2058) This commit adds the possibility to add a read timeout to the HTTP call for the HTTP wait strategy. This is necessary, because with the default Docker settings for Mac can lead to a blocking call to `HttpURLConnection.connect()`. The follow up issue is, that the container will never get healthy and due to automated integration tests the tests are not able to run. The main cause of the problem is the lazy connection establishment during setting up the container. So the time between starting the container and starting the wait strategy checks. The read timeout lets the HTTP call fail, so the `retryUntilSuccess()` is able to send another HTTP call in order to reestablish the connection and not block the whole thread. --- .../wait/strategy/HttpWaitStrategy.java | 19 ++++++++++++++++++- .../wait/strategy/HttpWaitStrategyTest.java | 19 +++++++++++++++---- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java index 6096e745e36..a44fc46e4b0 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java @@ -2,7 +2,6 @@ import com.google.common.base.Strings; import com.google.common.io.BaseEncoding; -import java.util.Optional; import lombok.extern.slf4j.Slf4j; import org.rnorth.ducttape.TimeoutException; import org.testcontainers.containers.ContainerLaunchException; @@ -13,7 +12,9 @@ import java.net.HttpURLConnection; import java.net.URI; import java.net.URL; +import java.time.Duration; import java.util.HashSet; +import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; @@ -41,6 +42,7 @@ public class HttpWaitStrategy extends AbstractWaitStrategy { private Predicate responsePredicate; private Predicate statusCodePredicate = null; private Optional livenessPort = Optional.empty(); + private Duration readTimeout = Duration.ofSeconds(1); /** * Waits for the given status code. @@ -108,6 +110,20 @@ public HttpWaitStrategy withBasicCredentials(String username, String password) { return this; } + /** + * Set the HTTP connections read timeout. + * + * @param timeout the timeout (minimum 1 millisecond) + * @return this + */ + public HttpWaitStrategy withReadTimeout(Duration timeout) { + if (timeout.toMillis() < 1) { + throw new IllegalArgumentException("you cannot specify a value smaller than 1 ms"); + } + this.readTimeout = timeout; + return this; + } + /** * Waits for the response to pass the given predicate * @param responsePredicate The predicate to test the response against @@ -143,6 +159,7 @@ protected void waitUntilReady() { getRateLimiter().doWhenReady(() -> { try { final HttpURLConnection connection = (HttpURLConnection) new URL(uri).openConnection(); + connection.setReadTimeout(Math.toIntExact(readTimeout.toMillis())); // authenticate if (!Strings.isNullOrEmpty(username)) { diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java index 5820333245a..e048f4bccb8 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java @@ -5,6 +5,7 @@ import org.rnorth.ducttape.RetryCountExceededException; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; +import java.time.Duration; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; @@ -131,6 +132,15 @@ public void testWaitUntilReadyWithSpecificPort() { )); } + @Test + public void testWaitUntilReadyWithTimoutCausedByReadTimeout() { + waitUntilReadyAndTimeout( + startContainerWithCommand(createShellCommand("0 Connection Refused", GOOD_RESPONSE_BODY, 9090), + createHttpWaitStrategy(ready).forPort(9090).withReadTimeout(Duration.ofMillis(1)), + 9090 + )); + } + /** * @param ready the AtomicBoolean on which to indicate success * @return the WaitStrategy under test @@ -143,6 +153,7 @@ protected HttpWaitStrategy buildWaitStrategy(final AtomicBoolean ready) { /** * Create a HttpWaitStrategy instance with a waitUntilReady implementation + * * @param ready Indicates that the WaitStrategy has completed waiting successfully. * @return the HttpWaitStrategy instance */ @@ -163,9 +174,9 @@ private String createShellCommand(String header, String responseBody) { private String createShellCommand(String header, String responseBody, int port) { int length = responseBody.getBytes().length; - return "while true; do { echo -e \"HTTP/1.1 "+header+NEWLINE+ - "Content-Type: text/html"+NEWLINE+ - "Content-Length: "+length +NEWLINE+ "\";" - +" echo \""+responseBody+"\";} | nc -lp " + port + "; done"; + return "while true; do { echo -e \"HTTP/1.1 " + header + NEWLINE + + "Content-Type: text/html" + NEWLINE + + "Content-Length: " + length + NEWLINE + "\";" + + " echo \"" + responseBody + "\";} | nc -lp " + port + "; done"; } } From e26788a0af57ee23151b8594375049bcf7e8968f Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 12 Nov 2019 08:08:17 +0100 Subject: [PATCH 1034/5615] [hotfix] always obtain port in Kafka --- .../java/org/testcontainers/containers/KafkaContainer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java index ede8cb5d086..25cfba9d322 100644 --- a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java +++ b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java @@ -83,12 +83,12 @@ protected void doStart() { protected void containerIsStarting(InspectContainerResponse containerInfo, boolean reused) { super.containerIsStarting(containerInfo, reused); + port = getMappedPort(KAFKA_PORT); + if (reused) { return; } - port = getMappedPort(KAFKA_PORT); - final String zookeeperConnect; if (externalZookeeperConnect != null) { zookeeperConnect = externalZookeeperConnect; From 92cbc2f9af019461cdc0431b318944ce72a704a1 Mon Sep 17 00:00:00 2001 From: Willy Lulciuc Date: Tue, 12 Nov 2019 11:05:03 -0800 Subject: [PATCH 1035/5615] Add Marquez as user (#2060) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add Marquez as user This PR adds [Marquez](https://marquezproject.github.io/marquez/) to the list of users. Keep up the great work 💯 * Link to website --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index ba99eb6e555..8f95ad5399a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -76,6 +76,7 @@ You can also [check the latest version available on Maven Central](https://searc * [Micronaut Data](https://github.com/micronaut-projects/micronaut-data/) - Testing of Micronaut Data JDBC, a database access toolkit * [JHipster](https://www.jhipster.tech/) - Couchbase and Cassandra integration testing * [wescale](https://www.wescale.com) - Integration testing against HTTP service mocks and various data stores +* [Marquez](https://marquezproject.github.io/marquez) - PostgreSQL integration testing ## License From 8dee638fc4c5ad3c11f0156abf97cb148b58db27 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 18 Nov 2019 14:16:42 +0100 Subject: [PATCH 1036/5615] Stabilize CouchbaseContainer by merging the Socat command (#2081) Fixes #1453 --- .../couchbase/CouchbaseContainer.java | 38 ++++++++++++------- .../couchbase/BaseCouchbaseContainerTest.java | 9 ----- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index ad6fe3d18a9..9e7ace2af04 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -37,7 +37,6 @@ import org.testcontainers.containers.SocatContainer; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.images.builder.Transferable; -import org.testcontainers.utility.Base58; import org.testcontainers.utility.ThrowingFunction; import java.io.DataOutputStream; @@ -47,6 +46,7 @@ import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.*; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -107,11 +107,10 @@ public CouchbaseContainer() { this(DOCKER_IMAGE_NAME + VERSION); } - public CouchbaseContainer(String containerName) { - super(containerName); + public CouchbaseContainer(String imageName) { + super(imageName); withNetwork(Network.SHARED); - withNetworkAliases("couchbase-" + Base58.randomString(6)); setWaitStrategy(new HttpWaitStrategy().forPath("/ui/index.html")); } @@ -131,15 +130,16 @@ protected void configure() { @Override @SneakyThrows protected void doStart() { - String networkAlias = getNetworkAliases().get(0); - startProxy(networkAlias); - - for (CouchbasePort port : CouchbasePort.values()) { - exposePortThroughProxy(networkAlias, port.getOriginalPort(), getMappedPort(port)); + startProxy(getNetworkAliases().get(0)); + try { + super.doStart(); + } catch (Throwable e) { + proxy.stop(); + throw e; } - super.doStart(); } + @SneakyThrows private void startProxy(String networkAlias) { proxy = new SocatContainer().withNetwork(getNetwork()); @@ -150,18 +150,28 @@ private void startProxy(String networkAlias) { proxy.addExposedPort(port.getOriginalPort()); } } + proxy.setWaitStrategy(null); proxy.start(); - } - private void exposePortThroughProxy(String networkAlias, int originalPort, int mappedPort) { ExecCreateCmdResponse createCmdResponse = dockerClient .execCreateCmd(proxy.getContainerId()) - .withCmd("/usr/bin/socat", "TCP-LISTEN:" + originalPort + ",fork,reuseaddr", "TCP:" + networkAlias + ":" + mappedPort) + .withCmd( + "sh", + "-c", + Stream.of(CouchbasePort.values()) + .map(port -> { + return "/usr/bin/socat " + + "TCP-LISTEN:" + port.getOriginalPort() + ",fork,reuseaddr " + + "TCP:" + networkAlias + ":" + getMappedPort(port); + }) + .collect(Collectors.joining(" & ", "true", "")) + ) .exec(); dockerClient.execStartCmd(createCmdResponse.getId()) - .exec(new ExecStartResultCallback()); + .exec(new ExecStartResultCallback()) + .awaitCompletion(10, TimeUnit.SECONDS); } @Override diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java index 19eead1b23a..845d9bfaf5e 100644 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java @@ -9,10 +9,7 @@ import com.couchbase.client.java.view.View; import com.google.common.collect.Lists; import org.junit.Assert; -import org.junit.Rule; import org.junit.Test; -import org.testcontainers.testsupport.Flaky; -import org.testcontainers.testsupport.FlakyTestJUnit4RetryRule; import java.util.List; @@ -28,11 +25,7 @@ public abstract class BaseCouchbaseContainerTest extends AbstractCouchbaseTest { private static final String DOCUMENT = "{\"name\":\"toto\"}"; - @Rule - public FlakyTestJUnit4RetryRule retry = new FlakyTestJUnit4RetryRule(); - @Test - @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1453", reviewDate = "2019-12-01") public void shouldInsertDocument() { RawJsonDocument expected = RawJsonDocument.create(ID, DOCUMENT); getBucket().upsert(expected); @@ -41,7 +34,6 @@ public void shouldInsertDocument() { } @Test - @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1453", reviewDate = "2019-12-01") public void shouldExecuteN1ql() { getBucket().query(N1qlQuery.simple("INSERT INTO " + TEST_BUCKET + " (KEY, VALUE) VALUES ('" + ID + "', " + DOCUMENT + ")")); @@ -54,7 +46,6 @@ public void shouldExecuteN1ql() { } @Test - @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1453", reviewDate = "2019-12-01") public void shouldCreateView() { View view = DefaultView.create(VIEW_NAME, VIEW_FUNCTION); DesignDocument document = DesignDocument.create(VIEW_NAME, Lists.newArrayList(view)); From 839ce65fde0367ccb3eb80d2b24df9c9e3fc9390 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 22 Nov 2019 13:23:39 +0100 Subject: [PATCH 1037/5615] Lazily create/set Network in KafkaContainer (#2079) To make KafkaContainer work with #1781 without having to nullify the network, we should start removed the old, deprecated behaviour where the network was implicitly created in the constructor. --- .../containers/KafkaContainer.java | 36 ++++++++- .../containers/KafkaContainerTest.java | 74 ++++++++++--------- 2 files changed, 71 insertions(+), 39 deletions(-) diff --git a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java index 25cfba9d322..4b5e66a7bf5 100644 --- a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java +++ b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java @@ -4,7 +4,6 @@ import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.core.command.ExecStartResultCallback; import lombok.SneakyThrows; -import org.testcontainers.utility.Base58; import org.testcontainers.utility.TestcontainersConfiguration; import java.util.concurrent.TimeUnit; @@ -25,6 +24,8 @@ public class KafkaContainer extends GenericContainer { private int port = PORT_NOT_ASSIGNED; + private boolean useImplicitNetwork = true; + public KafkaContainer() { this("5.2.1"); } @@ -32,9 +33,6 @@ public KafkaContainer() { public KafkaContainer(String confluentPlatformVersion) { super(TestcontainersConfiguration.getInstance().getKafkaImage() + ":" + confluentPlatformVersion); - // TODO Only for backward compatibility - withNetwork(Network.newNetwork()); - withNetworkAliases("kafka-" + Base58.randomString(6)); withExposedPorts(KAFKA_PORT); // Use two listeners with different names, it will force Kafka to communicate with itself via internal @@ -50,6 +48,36 @@ public KafkaContainer(String confluentPlatformVersion) { withEnv("KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS", "0"); } + @Override + public KafkaContainer withNetwork(Network network) { + useImplicitNetwork = false; + return super.withNetwork(network); + } + + @Override + public Network getNetwork() { + if (useImplicitNetwork) { + // TODO Only for backward compatibility, to be removed soon + logger().warn( + "Deprecation warning! " + + "KafkaContainer#getNetwork without an explicitly set network. " + + "Consider using KafkaContainer#withNetwork", + new Exception("Deprecated method") + ); + Network network = Network.SHARED; + super.withNetwork(network); + + if (getContainerId() != null) { + dockerClient.connectToNetworkCmd() + .withContainerId(getContainerId()) + .withNetworkId(network.getId()) + .exec(); + } + return network; + } + return super.getNetwork(); + } + public KafkaContainer withEmbeddedZookeeper() { externalZookeeperConnect = null; return self(); diff --git a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java index fc36a3f4356..0b29ee0e6ef 100644 --- a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java +++ b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java @@ -31,16 +31,20 @@ public void testUsage() throws Exception { } } + /** + * @deprecated the {@link Network} should be set explicitly with {@link KafkaContainer#withNetwork(Network)}. + */ @Test + @Deprecated public void testExternalZookeeperWithKafkaNetwork() throws Exception { try ( - KafkaContainer kafka = new KafkaContainer() - .withExternalZookeeper("zookeeper:2181"); + KafkaContainer kafka = new KafkaContainer() + .withExternalZookeeper("zookeeper:2181"); - GenericContainer zookeeper = new GenericContainer("confluentinc/cp-zookeeper:4.0.0") - .withNetwork(kafka.getNetwork()) - .withNetworkAliases("zookeeper") - .withEnv("ZOOKEEPER_CLIENT_PORT", "2181"); + GenericContainer zookeeper = new GenericContainer("confluentinc/cp-zookeeper:4.0.0") + .withNetwork(kafka.getNetwork()) + .withNetworkAliases("zookeeper") + .withEnv("ZOOKEEPER_CLIENT_PORT", "2181"); ) { Stream.of(kafka, zookeeper).parallel().forEach(GenericContainer::start); @@ -51,16 +55,16 @@ public void testExternalZookeeperWithKafkaNetwork() throws Exception { @Test public void testExternalZookeeperWithExternalNetwork() throws Exception { try ( - Network network = Network.newNetwork(); + Network network = Network.newNetwork(); - KafkaContainer kafka = new KafkaContainer() - .withNetwork(network) - .withExternalZookeeper("zookeeper:2181"); + KafkaContainer kafka = new KafkaContainer() + .withNetwork(network) + .withExternalZookeeper("zookeeper:2181"); - GenericContainer zookeeper = new GenericContainer("confluentinc/cp-zookeeper:4.0.0") - .withNetwork(network) - .withNetworkAliases("zookeeper") - .withEnv("ZOOKEEPER_CLIENT_PORT", "2181"); + GenericContainer zookeeper = new GenericContainer("confluentinc/cp-zookeeper:4.0.0") + .withNetwork(network) + .withNetworkAliases("zookeeper") + .withEnv("ZOOKEEPER_CLIENT_PORT", "2181"); ) { Stream.of(kafka, zookeeper).parallel().forEach(GenericContainer::start); @@ -70,24 +74,24 @@ public void testExternalZookeeperWithExternalNetwork() throws Exception { protected void testKafkaFunctionality(String bootstrapServers) throws Exception { try ( - KafkaProducer producer = new KafkaProducer<>( - ImmutableMap.of( - ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers, - ProducerConfig.CLIENT_ID_CONFIG, UUID.randomUUID().toString() - ), - new StringSerializer(), - new StringSerializer() - ); - - KafkaConsumer consumer = new KafkaConsumer<>( - ImmutableMap.of( - ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers, - ConsumerConfig.GROUP_ID_CONFIG, "tc-" + UUID.randomUUID(), - ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest" - ), - new StringDeserializer(), - new StringDeserializer() - ); + KafkaProducer producer = new KafkaProducer<>( + ImmutableMap.of( + ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers, + ProducerConfig.CLIENT_ID_CONFIG, UUID.randomUUID().toString() + ), + new StringSerializer(), + new StringSerializer() + ); + + KafkaConsumer consumer = new KafkaConsumer<>( + ImmutableMap.of( + ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers, + ConsumerConfig.GROUP_ID_CONFIG, "tc-" + UUID.randomUUID(), + ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest" + ), + new StringDeserializer(), + new StringDeserializer() + ); ) { String topicName = "messages"; consumer.subscribe(Arrays.asList(topicName)); @@ -102,9 +106,9 @@ protected void testKafkaFunctionality(String bootstrapServers) throws Exception } assertThat(records) - .hasSize(1) - .extracting(ConsumerRecord::topic, ConsumerRecord::key, ConsumerRecord::value) - .containsExactly(tuple(topicName, "testcontainers", "rulezzz")); + .hasSize(1) + .extracting(ConsumerRecord::topic, ConsumerRecord::key, ConsumerRecord::value) + .containsExactly(tuple(topicName, "testcontainers", "rulezzz")); return true; }); From 154e9339aba4a7f9e53f942ecb1b02362f479776 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 22 Nov 2019 14:48:35 +0100 Subject: [PATCH 1038/5615] Add Gradle wrapper to examples/ that points to the parent's wrapper (#2077) * Add Gradle wrapper to examples/ that points to the parent's wrapper * Update CI scripts --- .circleci/config.yml | 2 +- .travis.yml | 2 +- examples/gradlew | 188 +++++++++++++++++++++++++++++++++++++++++++ examples/gradlew.bat | 100 +++++++++++++++++++++++ 4 files changed, 290 insertions(+), 2 deletions(-) create mode 100755 examples/gradlew create mode 100644 examples/gradlew.bat diff --git a/.circleci/config.yml b/.circleci/config.yml index 08c0f4aabab..5ee4097934a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -81,7 +81,7 @@ jobs: steps: - checkout - run: - command: cd examples && ../gradlew check + command: cd examples && ./gradlew check - run: name: Save test results command: | diff --git a/.travis.yml b/.travis.yml index d21033b942a..eda26dc69af 100644 --- a/.travis.yml +++ b/.travis.yml @@ -44,7 +44,7 @@ jobs: script: ./gradlew jdbc-test:check --scan --no-daemon -i - env: [ NAME=examples ] - script: cd examples && ../gradlew check --no-daemon -i + script: cd examples && ./gradlew check --no-daemon -i # Run Docker-in-Docker tests inside Alpine - env: [ NAME="docker-in-alpine-docker" ] diff --git a/examples/gradlew b/examples/gradlew new file mode 100755 index 00000000000..56f7b0fc7bf --- /dev/null +++ b/examples/gradlew @@ -0,0 +1,188 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/../gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/examples/gradlew.bat b/examples/gradlew.bat new file mode 100644 index 00000000000..f3a074d99d4 --- /dev/null +++ b/examples/gradlew.bat @@ -0,0 +1,100 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\..\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega From 003ef139a9bdfa07a1fc57c90694f61fcfc61dd8 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 22 Nov 2019 20:56:42 +0000 Subject: [PATCH 1039/5615] Bump tomcat-jdbc from 9.0.27 to 9.0.29 in /modules/mariadb (#2096) Bumps tomcat-jdbc from 9.0.27 to 9.0.29. Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index f6d65f4a5f0..87462ce158d 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -6,6 +6,6 @@ dependencies { testCompile 'org.mariadb.jdbc:mariadb-java-client:2.5.1' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.27' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.29' testCompile 'org.vibur:vibur-dbcp:22.2' } From 69c54aac43b8e4eed3e35d34424867a3e2c65b60 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 22 Nov 2019 20:57:44 +0000 Subject: [PATCH 1040/5615] =?UTF-8?q?Bump=20aws-java-sdk-s3=20from=201.11.?= =?UTF-8?q?636=20to=201.11.680=20in=20/modules/loca=E2=80=A6=20(#2094)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.11.636 to 1.11.680. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.636...1.11.680) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 1ee679abf98..770294621aa 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.636' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.680' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.636' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.636' } From 53e16333477e261460f170eddae78662c140774b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 22 Nov 2019 20:57:56 +0000 Subject: [PATCH 1041/5615] Bump tomcat-jdbc from 9.0.27 to 9.0.29 in /modules/mysql (#2095) Bumps tomcat-jdbc from 9.0.27 to 9.0.29. Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 7704f56c54f..2a71e8f9d04 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -5,7 +5,7 @@ dependencies { testCompile 'mysql:mysql-connector-java:8.0.18' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.27' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.29' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.vibur:vibur-dbcp:22.2' } From f1e9883bcf50fdeebcbd93cbc0f55f721b2e4330 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 23 Nov 2019 15:22:00 +0000 Subject: [PATCH 1042/5615] Bump java-client from 2.7.9 to 2.7.10 in /modules/couchbase (#2057) Bumps [java-client](https://github.com/couchbase/couchbase-java-client) from 2.7.9 to 2.7.10. - [Release notes](https://github.com/couchbase/couchbase-java-client/releases) - [Commits](https://github.com/couchbase/couchbase-java-client/compare/2.7.9...2.7.10) Signed-off-by: dependabot-preview[bot] --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 063b12c9d8e..7642bb2fc5b 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -2,7 +2,7 @@ description = "Testcontainers :: Couchbase" dependencies { compile project(':testcontainers') - compile 'com.couchbase.client:java-client:2.7.9' + compile 'com.couchbase.client:java-client:2.7.10' testCompile project(':test-support') } From b7565275eba1009b0db1d1cde0979420459555b6 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 23 Nov 2019 16:40:15 +0000 Subject: [PATCH 1043/5615] =?UTF-8?q?Upgrade=20docker-compose=20image=20to?= =?UTF-8?q?=20latest=20version=20and=20perform=20dire=E2=80=A6=20(#1847)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Upgrade docker-compose image to latest version and perform direct image pull Together with using Compose file 2.1 syntax, this is a solution to network cleanup issue described in: * #1767 * #739 * https://github.com/testcontainers/moby-ryuk/issues/2 * https://github.com/docker/compose/issues/6636 Solution to general credential helper authenticated pull issues in: * https://github.com/docker/compose/issues/5854 Tangentially should add support for v3 syntax (not yet tested) re #531 --- .../containers/DockerComposeContainer.java | 170 ++++-------------- .../containers/ParsedDockerComposeFile.java | 95 ++++++++++ .../utility/TestcontainersConfiguration.java | 2 +- .../DockerComposeFileValidationTest.java | 70 -------- ...ParsedDockerComposeFileValidationTest.java | 88 +++++++++ .../junit/DockerComposeErrorHandlingTest.java | 15 +- core/src/test/resources/compose-test.yml | 2 +- .../test/resources/docker-compose-base.yml | 4 +- .../docker-compose-imagename-parsing-v1.yml | 6 + .../docker-compose-imagename-parsing-v2.yml | 10 ++ .../docker-compose-non-default-override.yml | 4 +- .../src/test/resources/local-compose-test.yml | 2 +- .../resources/v2-compose-test-passthrough.yml | 2 +- .../v2-compose-test-with-network.yml | 2 +- core/src/test/resources/v2-compose-test.yml | 2 +- .../src/test/resources/docker-compose.yml | 2 +- .../src/test/resources/docker-compose.yml | 2 +- 17 files changed, 250 insertions(+), 228 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/containers/ParsedDockerComposeFile.java delete mode 100644 core/src/test/java/org/testcontainers/containers/DockerComposeFileValidationTest.java create mode 100644 core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java create mode 100644 core/src/test/resources/docker-compose-imagename-parsing-v1.yml create mode 100644 core/src/test/resources/docker-compose-imagename-parsing-v2.yml diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 73628a19ad7..31d84284584 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -2,14 +2,13 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.model.Container; -import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.base.Splitter; +import com.google.common.base.Strings; import com.google.common.collect.Maps; import com.google.common.util.concurrent.Uninterruptibles; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.SystemUtils; import org.junit.runner.Description; @@ -19,18 +18,16 @@ import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.output.Slf4jLogConsumer; import org.testcontainers.containers.startupcheck.IndefiniteWaitOneShotStartupCheckStrategy; -import org.testcontainers.containers.wait.strategy.*; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.containers.wait.strategy.WaitAllStrategy; +import org.testcontainers.containers.wait.strategy.WaitStrategy; import org.testcontainers.lifecycle.Startable; import org.testcontainers.utility.*; -import org.yaml.snakeyaml.Yaml; import org.zeroturnaround.exec.InvalidExitValueException; import org.zeroturnaround.exec.ProcessExecutor; import org.zeroturnaround.exec.stream.slf4j.Slf4jStream; import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.nio.file.*; import java.time.Duration; import java.util.AbstractMap.SimpleEntry; import java.util.*; @@ -38,6 +35,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; +import java.util.stream.Collectors; import java.util.stream.Stream; import static com.google.common.base.Preconditions.checkArgument; @@ -58,8 +56,7 @@ public class DockerComposeContainer> e */ private final String identifier; private final List composeFiles; - private final Set spawnedContainerIds = new HashSet<>(); - private final Set spawnedNetworkIds = new HashSet<>(); + private Set parsedComposeFiles; private final Map scalingPreferences = new HashMap<>(); private DockerClient dockerClient; private boolean localCompose; @@ -107,10 +104,11 @@ public DockerComposeContainer(String identifier, File... composeFiles) { public DockerComposeContainer(String identifier, List composeFiles) { this.composeFiles = composeFiles; + this.parsedComposeFiles = composeFiles.stream().map(ParsedDockerComposeFile::new).collect(Collectors.toSet()); // Use a unique identifier so that containers created for this compose environment can be identified this.identifier = identifier; - project = randomProjectId(); + this.project = randomProjectId(); this.dockerClient = DockerClientFactory.instance().client(); } @@ -154,7 +152,6 @@ public void start() { log.warn("Exception while pulling images, using local images if available", e); } } - applyScaling(); // scale before up, so that all scaled instances are available first for linking createServices(); startAmbassadorContainers(); waitUntilServiceStarted(); @@ -162,7 +159,18 @@ public void start() { } private void pullImages() { - runWithCompose("pull"); + // Pull images using our docker client rather than compose itself, + // (a) as a workaround for https://github.com/docker/compose/issues/5854, which prevents authenticated image pulls being possible when credential helpers are in use + // (b) so that credential helper-based auth still works when compose is running from within a container + parsedComposeFiles.stream() + .flatMap(it -> it.getServiceImageNames().stream()) + .forEach(imageName -> { + try { + DockerClientFactory.instance().checkAndPullImage(dockerClient, imageName); + } catch (Exception e) { + log.warn("Failed to pull image '{}'. Exception message was {}", imageName, e.getMessage()); + } + }); } public SELF withServices(@NonNull String... services) { @@ -171,18 +179,23 @@ public SELF withServices(@NonNull String... services) { } private void createServices() { - // Run the docker-compose container, which starts up the services - String command = "up -d"; + // Apply scaling + final String servicesWithScalingSettings = Stream.concat(services.stream(), scalingPreferences.keySet().stream()) + .map(service -> "--scale " + service + "=" + scalingPreferences.getOrDefault(service, 1)) + .collect(joining(" ")); + + String flags = "-d"; if (build) { - command += " --build"; + flags += " --build"; } - if (!services.isEmpty()) { - command += " " + String.join(" ", services); + // Run the docker-compose container, which starts up the services + if(Strings.isNullOrEmpty(servicesWithScalingSettings)) { + runWithCompose("up " + flags); + } else { + runWithCompose("up " + flags + " " + servicesWithScalingSettings); } - - runWithCompose(command); } private void waitUntilServiceStarted() { @@ -221,10 +234,6 @@ private void runWithCompose(String cmd) { checkNotNull(composeFiles); checkArgument(!composeFiles.isEmpty(), "No docker compose file have been provided"); - for (File composeFile : composeFiles) { - validate(composeFile); - } - final DockerCompose dockerCompose; if (localCompose) { dockerCompose = new LocalDockerCompose(composeFiles, project); @@ -238,72 +247,6 @@ private void runWithCompose(String cmd) { .invoke(); } - @SuppressWarnings("unchecked") - private static void validate(File composeFile) { - Yaml yaml = new Yaml(); - try (FileInputStream fileInputStream = FileUtils.openInputStream(composeFile)) { - Object template = yaml.load(fileInputStream); - validate(template, composeFile.getAbsolutePath()); - } catch (IOException e) { - log.warn("Failed to read YAML from {}", composeFile.getAbsolutePath(), e); - } - } - - @VisibleForTesting - static void validate(Object template, String identifier) { - if (!(template instanceof Map)) { - return; - } - - Map map = (Map) template; - - final Map servicesMap; - if (map.containsKey("version")) { - if (!map.containsKey("services")) { - log.debug("Compose file {} has an unknown format: 'version' is set but 'services' is not defined", identifier); - return; - } - Object services = map.get("services"); - if (!(services instanceof Map)) { - log.debug("Compose file {} has an unknown format: 'services' is not Map", identifier); - return; - } - - servicesMap = (Map) services; - } else { - servicesMap = map; - } - - for (Map.Entry entry : servicesMap.entrySet()) { - String serviceName = entry.getKey(); - Object serviceDefinition = entry.getValue(); - if (!(serviceDefinition instanceof Map)) { - log.debug("Compose file {} has an unknown format: service '{}' is not Map", identifier, serviceName); - break; - } - - if (((Map) serviceDefinition).containsKey("container_name")) { - throw new IllegalStateException(String.format( - "Compose file %s has 'container_name' property set for service '%s' but this property is not supported by Testcontainers, consider removing it", - identifier, - serviceName - )); - } - } - } - - private void applyScaling() { - // Apply scaling - if (!scalingPreferences.isEmpty()) { - StringBuilder sb = new StringBuilder("scale"); - for (Map.Entry scale : scalingPreferences.entrySet()) { - sb.append(" ").append(scale.getKey()).append("=").append(scale.getValue()); - } - - runWithCompose(sb.toString()); - } - } - private void registerContainersForShutdown() { ResourceReaper.instance().registerFilterForCleanup(Arrays.asList( new SimpleEntry<>("label", "com.docker.compose.project=" + project) @@ -333,29 +276,12 @@ public void stop() { ambassadorContainer.stop(); // Kill the services using docker-compose - try { - String cmd = "down -v"; - if (removeImages != null) { - cmd += " --rmi " + removeImages.dockerRemoveImagesType(); - } - runWithCompose(cmd); - - // If we reach here then docker-compose down has cleared networks and containers; - // we can unregister from ResourceReaper - spawnedContainerIds.forEach(ResourceReaper.instance()::unregisterContainer); - spawnedNetworkIds.forEach(ResourceReaper.instance()::unregisterNetwork); - } catch (Exception e) { - // docker-compose down failed; use ResourceReaper to ensure cleanup - - // kill the spawned service containers - spawnedContainerIds.forEach(ResourceReaper.instance()::stopAndRemoveContainer); - - // remove the networks after removing the containers - spawnedNetworkIds.forEach(ResourceReaper.instance()::removeNetworkById); + String cmd = "down -v"; + if (removeImages != null) { + cmd += " --rmi " + removeImages.dockerRemoveImagesType(); } + runWithCompose(cmd); - spawnedContainerIds.clear(); - spawnedNetworkIds.clear(); } finally { project = randomProjectId(); } @@ -597,9 +523,6 @@ interface DockerCompose { class ContainerisedDockerCompose extends GenericContainer implements DockerCompose { private static final String DOCKER_SOCKET_PATH = "/var/run/docker.sock"; - private static final String DOCKER_CONFIG_FILE = "/root/.docker/config.json"; - private static final String DOCKER_CONFIG_ENV = "DOCKER_CONFIG_FILE"; - private static final String DOCKER_CONFIG_PROPERTY = "dockerConfigFile"; public static final char UNIX_PATH_SEPERATOR = ':'; public ContainerisedDockerCompose(List composeFiles, String identifier) { @@ -630,27 +553,6 @@ public ContainerisedDockerCompose(List composeFiles, String identifier) { addEnv("DOCKER_HOST", "unix:///docker.sock"); setStartupCheckStrategy(new IndefiniteWaitOneShotStartupCheckStrategy()); setWorkingDirectory(containerPwd); - - String dockerConfigPath = determineDockerConfigPath(); - if (dockerConfigPath != null && !dockerConfigPath.isEmpty()) { - addFileSystemBind(dockerConfigPath, DOCKER_CONFIG_FILE, READ_ONLY); - } - } - - private String determineDockerConfigPath() { - String dockerConfigEnv = System.getenv(DOCKER_CONFIG_ENV); - String dockerConfigProperty = System.getProperty(DOCKER_CONFIG_PROPERTY); - Path dockerConfig = Paths.get(System.getProperty("user.home"), ".docker", "config.json"); - - if (dockerConfigEnv != null && !dockerConfigEnv.trim().isEmpty() && Files.exists(Paths.get(dockerConfigEnv))) { - return dockerConfigEnv; - } else if (dockerConfigProperty != null && !dockerConfigProperty.trim().isEmpty() && Files.exists(Paths.get(dockerConfigProperty))) { - return dockerConfigProperty; - } else if (Files.exists(dockerConfig)) { - return dockerConfig.toString(); - } else { - return null; - } } private String getDockerSocketHostPath() { diff --git a/core/src/main/java/org/testcontainers/containers/ParsedDockerComposeFile.java b/core/src/main/java/org/testcontainers/containers/ParsedDockerComposeFile.java new file mode 100644 index 00000000000..838607d8337 --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/ParsedDockerComposeFile.java @@ -0,0 +1,95 @@ +package org.testcontainers.containers; + +import com.google.common.annotations.VisibleForTesting; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FileUtils; +import org.yaml.snakeyaml.Yaml; + +import java.io.File; +import java.io.FileInputStream; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * Representation of a docker-compose file, with partial parsing for validation and extraction of a minimal set of + * data. + */ +@Slf4j +@EqualsAndHashCode +class ParsedDockerComposeFile { + + private final Map composeFileContent; + private final String composeFileName; + + @Getter + private Set serviceImageNames = new HashSet<>(); + + ParsedDockerComposeFile(File composeFile) { + Yaml yaml = new Yaml(); + try (FileInputStream fileInputStream = FileUtils.openInputStream(composeFile)) { + composeFileContent = yaml.load(fileInputStream); + } catch (Exception e) { + throw new IllegalArgumentException("Unable to parse YAML file from " + composeFile.getAbsolutePath(), e); + } + this.composeFileName = composeFile.getAbsolutePath(); + + parseAndValidate(); + } + + @VisibleForTesting + ParsedDockerComposeFile(Map testContent) { + this.composeFileContent = testContent; + this.composeFileName = ""; + + parseAndValidate(); + } + + private void parseAndValidate() { + final Map servicesMap; + if (composeFileContent.containsKey("version")) { + if ("2.0".equals(composeFileContent.get("version"))) { + log.warn("Testcontainers may not be able to clean up networks spawned using Docker Compose v2.0 files. " + + "Please see https://github.com/testcontainers/moby-ryuk/issues/2, and specify 'version: \"2.1\"' or " + + "higher in {}", composeFileName); + } + + final Object servicesElement = composeFileContent.get("services"); + if (servicesElement == null) { + log.debug("Compose file {} has an unknown format: 'version' is set but 'services' is not defined", composeFileName); + return; + } + if (!(servicesElement instanceof Map)) { + log.debug("Compose file {} has an unknown format: 'services' is not Map", composeFileName); + return; + } + + servicesMap = (Map) servicesElement; + } else { + servicesMap = composeFileContent; + } + + for (Map.Entry entry : servicesMap.entrySet()) { + String serviceName = entry.getKey(); + Object serviceDefinition = entry.getValue(); + if (!(serviceDefinition instanceof Map)) { + log.debug("Compose file {} has an unknown format: service '{}' is not Map", composeFileName, serviceName); + break; + } + + final Map serviceDefinitionMap = (Map) serviceDefinition; + if (serviceDefinitionMap.containsKey("container_name")) { + throw new IllegalStateException(String.format( + "Compose file %s has 'container_name' property set for service '%s' but this property is not supported by Testcontainers, consider removing it", + composeFileName, + serviceName + )); + } + if (serviceDefinitionMap.containsKey("image") && serviceDefinitionMap.get("image") instanceof String) { + serviceImageNames.add((String) serviceDefinitionMap.get("image")); + } + } + } +} diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index e5a45a05b64..1387dc6b9cd 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -51,7 +51,7 @@ public String getVncRecordedContainerImage() { } public String getDockerComposeContainerImage() { - return (String) properties.getOrDefault("compose.container.image", "docker/compose:1.8.0"); + return (String) properties.getOrDefault("compose.container.image", "docker/compose:1.24.1"); } public String getTinyImage() { diff --git a/core/src/test/java/org/testcontainers/containers/DockerComposeFileValidationTest.java b/core/src/test/java/org/testcontainers/containers/DockerComposeFileValidationTest.java deleted file mode 100644 index 7a3121dcd32..00000000000 --- a/core/src/test/java/org/testcontainers/containers/DockerComposeFileValidationTest.java +++ /dev/null @@ -1,70 +0,0 @@ -package org.testcontainers.containers; - -import com.google.common.collect.ImmutableMap; -import org.assertj.core.api.Assertions; -import org.junit.Test; - -import java.io.File; - -import static java.util.Collections.emptyList; - -public class DockerComposeFileValidationTest { - - @Test - public void shouldValidate() { - File file = new File("src/test/resources/docker-compose-container-name-v1.yml"); - try (DockerComposeContainer container = new DockerComposeContainer<>(file)) { - Assertions.assertThatThrownBy(container::start) - .hasMessageContaining(file.getAbsolutePath()) - .hasMessageContaining("'container_name' property set for service 'redis'"); - } - } - - @Test - public void shouldRejectContainerNameV1() { - Assertions - .assertThatThrownBy(() -> { - DockerComposeContainer.validate(ImmutableMap.of( - "redis", ImmutableMap.of("container_name", "redis") - ), ""); - }) - .hasMessageContaining("'container_name' property set for service 'redis'"); - } - - @Test - public void shouldRejectContainerNameV2() { - Assertions - .assertThatThrownBy(() -> { - DockerComposeContainer.validate(ImmutableMap.of( - "version", "2", - "services", ImmutableMap.of( - "redis", ImmutableMap.of("container_name", "redis") - ) - ), ""); - }) - .hasMessageContaining("'container_name' property set for service 'redis'"); - } - - @Test - public void shouldIgnoreUnknownStructure() { - // Everything is a list - DockerComposeContainer.validate(emptyList(), ""); - - // services is not a map but List - DockerComposeContainer.validate(ImmutableMap.of( - "version", "2", - "services", emptyList() - ), ""); - - // services is not a collection - DockerComposeContainer.validate(ImmutableMap.of( - "version", "2", - "services", true - ), ""); - - // no services while version is defined - DockerComposeContainer.validate(ImmutableMap.of( - "version", "9000" - ), ""); - } -} diff --git a/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java b/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java new file mode 100644 index 00000000000..c553d250ec9 --- /dev/null +++ b/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java @@ -0,0 +1,88 @@ +package org.testcontainers.containers; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Sets; +import org.assertj.core.api.Assertions; +import org.junit.Test; + +import java.io.File; + +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; + +public class ParsedDockerComposeFileValidationTest { + + @Test + public void shouldValidate() { + File file = new File("src/test/resources/docker-compose-container-name-v1.yml"); + Assertions + .assertThatThrownBy(() -> { + new ParsedDockerComposeFile(file); + }) + .hasMessageContaining(file.getAbsolutePath()) + .hasMessageContaining("'container_name' property set for service 'redis'"); + } + + @Test + public void shouldRejectContainerNameV1() { + Assertions + .assertThatThrownBy(() -> { + new ParsedDockerComposeFile(ImmutableMap.of( + "redis", ImmutableMap.of("container_name", "redis") + )); + }) + .hasMessageContaining("'container_name' property set for service 'redis'"); + } + + @Test + public void shouldRejectContainerNameV2() { + Assertions + .assertThatThrownBy(() -> { + new ParsedDockerComposeFile(ImmutableMap.of( + "version", "2", + "services", ImmutableMap.of( + "redis", ImmutableMap.of("container_name", "redis") + ) + )); + }) + .hasMessageContaining("'container_name' property set for service 'redis'"); + } + + @Test + public void shouldIgnoreUnknownStructure() { + // Everything is a list + new ParsedDockerComposeFile(emptyMap()); + + // services is not a map but List + new ParsedDockerComposeFile(ImmutableMap.of( + "version", "2", + "services", emptyList() + )); + + // services is not a collection + new ParsedDockerComposeFile(ImmutableMap.of( + "version", "2", + "services", true + )); + + // no services while version is defined + new ParsedDockerComposeFile(ImmutableMap.of( + "version", "9000" + )); + } + + @Test + public void shouldObtainImageNamesV1() { + File file = new File("src/test/resources/docker-compose-imagename-parsing-v1.yml"); + ParsedDockerComposeFile parsedFile = new ParsedDockerComposeFile(file); + assertEquals("all defined service names are found", Sets.newHashSet("redis", "mysql"), parsedFile.getServiceImageNames()); + } + + @Test + public void shouldObtainImageNamesV2() { + File file = new File("src/test/resources/docker-compose-imagename-parsing-v2.yml"); + ParsedDockerComposeFile parsedFile = new ParsedDockerComposeFile(file); + assertEquals("all defined service names are found", Sets.newHashSet("redis", "mysql"), parsedFile.getServiceImageNames()); + } +} diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeErrorHandlingTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeErrorHandlingTest.java index c48cff1ff14..b8e26ffd3f1 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeErrorHandlingTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeErrorHandlingTest.java @@ -1,29 +1,20 @@ package org.testcontainers.junit; import org.junit.Test; -import org.junit.runner.Description; import org.rnorth.visibleassertions.VisibleAssertions; -import org.testcontainers.containers.ContainerLaunchException; import org.testcontainers.containers.DockerComposeContainer; import java.io.File; -/** - * Created by rnorth on 08/08/2015. - */ public class DockerComposeErrorHandlingTest { - @Test public void simpleTest() { - - DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/invalid-compose.yml")) - .withExposedService("something", 123); - VisibleAssertions.assertThrows("starting with an invalid docker-compose file throws an exception", - ContainerLaunchException.class, + IllegalArgumentException.class, () -> { - environment.starting(Description.createTestDescription(Object.class, "name")); + DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/invalid-compose.yml")) + .withExposedService("something", 123); }); } } diff --git a/core/src/test/resources/compose-test.yml b/core/src/test/resources/compose-test.yml index 2e54e9f30b1..7436db7d211 100644 --- a/core/src/test/resources/compose-test.yml +++ b/core/src/test/resources/compose-test.yml @@ -3,4 +3,4 @@ redis: db: image: orchardup/mysql environment: - MYSQL_DATABASE: wordpress \ No newline at end of file + MYSQL_DATABASE: wordpress diff --git a/core/src/test/resources/docker-compose-base.yml b/core/src/test/resources/docker-compose-base.yml index c2dda256277..585d3dead39 100644 --- a/core/src/test/resources/docker-compose-base.yml +++ b/core/src/test/resources/docker-compose-base.yml @@ -1,6 +1,6 @@ -version: '2' +version: '2.1' services: alpine: build: compose-dockerfile environment: - bar: base \ No newline at end of file + bar: base diff --git a/core/src/test/resources/docker-compose-imagename-parsing-v1.yml b/core/src/test/resources/docker-compose-imagename-parsing-v1.yml new file mode 100644 index 00000000000..c3ae53fa3a8 --- /dev/null +++ b/core/src/test/resources/docker-compose-imagename-parsing-v1.yml @@ -0,0 +1,6 @@ +redis: + image: redis +mysql: + image: mysql +custom: + build: . diff --git a/core/src/test/resources/docker-compose-imagename-parsing-v2.yml b/core/src/test/resources/docker-compose-imagename-parsing-v2.yml new file mode 100644 index 00000000000..71ca8409f27 --- /dev/null +++ b/core/src/test/resources/docker-compose-imagename-parsing-v2.yml @@ -0,0 +1,10 @@ +version: "2.1" +services: + redis: + image: redis + mysql: + image: mysql + custom: + build: . +networks: + custom_network: {} diff --git a/core/src/test/resources/docker-compose-non-default-override.yml b/core/src/test/resources/docker-compose-non-default-override.yml index fa7acb70dfd..19f80a31c83 100644 --- a/core/src/test/resources/docker-compose-non-default-override.yml +++ b/core/src/test/resources/docker-compose-non-default-override.yml @@ -1,5 +1,5 @@ -version: '2' +version: '2.1' services: alpine: environment: - bar: overwritten \ No newline at end of file + bar: overwritten diff --git a/core/src/test/resources/local-compose-test.yml b/core/src/test/resources/local-compose-test.yml index 8d4f20e6924..b5f61be041b 100644 --- a/core/src/test/resources/local-compose-test.yml +++ b/core/src/test/resources/local-compose-test.yml @@ -1,4 +1,4 @@ -version: '2' +version: '2.1' services: redis: image: redis-local:latest diff --git a/core/src/test/resources/v2-compose-test-passthrough.yml b/core/src/test/resources/v2-compose-test-passthrough.yml index b52f2d28b0e..f96aff26f75 100644 --- a/core/src/test/resources/v2-compose-test-passthrough.yml +++ b/core/src/test/resources/v2-compose-test-passthrough.yml @@ -1,4 +1,4 @@ -version: '2' +version: '2.1' services: alpine: build: compose-dockerfile diff --git a/core/src/test/resources/v2-compose-test-with-network.yml b/core/src/test/resources/v2-compose-test-with-network.yml index 82b54a85823..89301c7918a 100644 --- a/core/src/test/resources/v2-compose-test-with-network.yml +++ b/core/src/test/resources/v2-compose-test-with-network.yml @@ -1,4 +1,4 @@ -version: '2' +version: '2.1' services: redis: image: redis diff --git a/core/src/test/resources/v2-compose-test.yml b/core/src/test/resources/v2-compose-test.yml index d439945d236..a9090c55671 100644 --- a/core/src/test/resources/v2-compose-test.yml +++ b/core/src/test/resources/v2-compose-test.yml @@ -1,4 +1,4 @@ -version: '2' +version: '2.1' services: redis: image: redis diff --git a/modules/junit-jupiter/src/test/resources/docker-compose.yml b/modules/junit-jupiter/src/test/resources/docker-compose.yml index ec1015f106a..810c7c178b5 100644 --- a/modules/junit-jupiter/src/test/resources/docker-compose.yml +++ b/modules/junit-jupiter/src/test/resources/docker-compose.yml @@ -1,4 +1,4 @@ -version: '2' +version: '2.1' services: whoami: image: emilevauge/whoami diff --git a/modules/spock/src/test/resources/docker-compose.yml b/modules/spock/src/test/resources/docker-compose.yml index ec1015f106a..810c7c178b5 100644 --- a/modules/spock/src/test/resources/docker-compose.yml +++ b/modules/spock/src/test/resources/docker-compose.yml @@ -1,4 +1,4 @@ -version: '2' +version: '2.1' services: whoami: image: emilevauge/whoami From 8fb199ff32386f5f83961a60471ef10e7a379a72 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 27 Nov 2019 09:38:05 +0100 Subject: [PATCH 1044/5615] Mock TestcontainersConfiguration in ReusabilityUnitTests (#2080) --- .../utility/TestcontainersConfiguration.java | 11 +++++- .../containers/ReusabilityUnitTests.java | 39 +++++++++++-------- .../MockTestcontainersConfigurationRule.java | 36 +++++++++++++++++ 3 files changed, 69 insertions(+), 17 deletions(-) create mode 100644 core/src/test/java/org/testcontainers/utility/MockTestcontainersConfigurationRule.java diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 1387dc6b9cd..64ee03efb06 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -1,5 +1,6 @@ package org.testcontainers.utility; +import com.google.common.annotations.VisibleForTesting; import lombok.*; import lombok.extern.slf4j.Slf4j; import org.testcontainers.UnstableAPI; @@ -9,6 +10,7 @@ import java.net.URL; import java.util.Objects; import java.util.Properties; +import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Stream; /** @@ -24,7 +26,14 @@ public class TestcontainersConfiguration { private static File ENVIRONMENT_CONFIG_FILE = new File(System.getProperty("user.home"), "." + PROPERTIES_FILE_NAME); @Getter(lazy = true) - private static final TestcontainersConfiguration instance = loadConfiguration(); + private static final TestcontainersConfiguration instance = loadConfiguration();; + + @SuppressWarnings({"ConstantConditions", "unchecked", "rawtypes"}) + @VisibleForTesting + static AtomicReference getInstanceField() { + // Lazy Getter from Lombok changes the field's type to AtomicReference + return (AtomicReference) (Object) instance; + } @Getter(AccessLevel.NONE) private final Properties environmentProperties; diff --git a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java index 2d0f7a8133a..3f7c7d75b45 100644 --- a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java +++ b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java @@ -11,7 +11,7 @@ import com.github.dockerjava.core.command.StartContainerCmdImpl; import lombok.RequiredArgsConstructor; import lombok.experimental.FieldDefaults; -import org.junit.Assume; +import org.junit.Rule; import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; @@ -23,6 +23,7 @@ import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.startupcheck.StartupCheckStrategy; import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy; +import org.testcontainers.utility.MockTestcontainersConfigurationRule; import org.testcontainers.utility.TestcontainersConfiguration; import java.util.ArrayList; @@ -128,9 +129,8 @@ protected void containerIsStarted(InspectContainerResponse containerInfo, boolea @Test public void shouldSetLabelsIfEnvironmentDoesNotSupportReuse() { - // TODO mock TestcontainersConfiguration - Assume.assumeFalse("does not support reuse", TestcontainersConfiguration.getInstance().environmentSupportsReuse()); - AtomicReference commandRef = new AtomicReference<>(); + Mockito.doReturn(false).when(TestcontainersConfiguration.getInstance()).environmentSupportsReuse(); + String containerId = randomContainerId(); when(client.createContainerCmd(any())).then(createContainerAnswer(containerId)); when(client.listContainersCmd()).then(listContainersAnswer()); @@ -147,8 +147,7 @@ public void shouldSetLabelsIfEnvironmentDoesNotSupportReuse() { @Test public void shouldCallHookIfReused() { - // TODO mock TestcontainersConfiguration - Assume.assumeTrue("supports reuse", TestcontainersConfiguration.getInstance().environmentSupportsReuse()); + Mockito.doReturn(true).when(TestcontainersConfiguration.getInstance()).environmentSupportsReuse(); String containerId = randomContainerId(); when(client.createContainerCmd(any())).then(createContainerAnswer(containerId)); String existingContainerId = randomContainerId(); @@ -200,8 +199,7 @@ public void shouldStartIfListReturnsEmpty() { @Test public void shouldReuseIfListReturnsID() { - // TODO mock TestcontainersConfiguration - Assume.assumeTrue("supports reuse", TestcontainersConfiguration.getInstance().environmentSupportsReuse()); + Mockito.doReturn(true).when(TestcontainersConfiguration.getInstance()).environmentSupportsReuse(); String containerId = randomContainerId(); when(client.createContainerCmd(any())).then(createContainerAnswer(containerId)); String existingContainerId = randomContainerId(); @@ -216,8 +214,7 @@ public void shouldReuseIfListReturnsID() { @Test public void shouldSetLabelsIfEnvironmentDoesNotSupportReuse() { - // TODO mock TestcontainersConfiguration - Assume.assumeFalse("does not support reuse", TestcontainersConfiguration.getInstance().environmentSupportsReuse()); + Mockito.doReturn(false).when(TestcontainersConfiguration.getInstance()).environmentSupportsReuse(); AtomicReference commandRef = new AtomicReference<>(); String containerId = randomContainerId(); when(client.createContainerCmd(any())).then(createContainerAnswer(containerId, commandRef::set)); @@ -236,7 +233,10 @@ public void shouldSetLabelsIfEnvironmentDoesNotSupportReuse() { } @FieldDefaults(makeFinal = true) - static abstract class AbstractReusabilityTest { + public static abstract class AbstractReusabilityTest { + + @Rule + public MockTestcontainersConfigurationRule configurationMock = new MockTestcontainersConfigurationRule(); protected DockerClient client = Mockito.mock(DockerClient.class); @@ -244,11 +244,18 @@ protected > T makeReusable(T container) { container.dockerClient = client; container.withNetworkMode("none"); // to disable the port forwarding container.withStartupCheckStrategy(new StartupCheckStrategy() { - @Override - public StartupStatus checkStartupState(DockerClient dockerClient, String containerId) { - return StartupStatus.SUCCESSFUL; - } - }); + + @Override + public boolean waitUntilStartupSuccessful(DockerClient dockerClient, String containerId) { + // Skip DockerClient rate limiter + return true; + } + + @Override + public StartupStatus checkStartupState(DockerClient dockerClient, String containerId) { + return StartupStatus.SUCCESSFUL; + } + }); container.waitingFor(new AbstractWaitStrategy() { @Override protected void waitUntilReady() { diff --git a/core/src/test/java/org/testcontainers/utility/MockTestcontainersConfigurationRule.java b/core/src/test/java/org/testcontainers/utility/MockTestcontainersConfigurationRule.java new file mode 100644 index 00000000000..4d156fd752e --- /dev/null +++ b/core/src/test/java/org/testcontainers/utility/MockTestcontainersConfigurationRule.java @@ -0,0 +1,36 @@ +package org.testcontainers.utility; + +import org.jetbrains.annotations.NotNull; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; +import org.mockito.Mockito; + +import java.util.concurrent.atomic.AtomicReference; + +/** + * This {@link TestRule} applies a spy on {@link TestcontainersConfiguration} + * for testing features that depend on the global configuration. + */ +public class MockTestcontainersConfigurationRule implements TestRule { + + static AtomicReference REF = TestcontainersConfiguration.getInstanceField(); + + @NotNull + @Override + public Statement apply(@NotNull Statement base, @NotNull Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + TestcontainersConfiguration previous = REF.get(); + REF.set(Mockito.spy(previous)); + + try { + base.evaluate(); + } finally { + REF.set(previous); + } + } + }; + } +} From 17d86c94dae3ac21c4c8c1949b72fe7a770eb3a9 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 27 Nov 2019 16:56:15 +0100 Subject: [PATCH 1045/5615] Image pull policy (#2024) See #1345 by @dmarkhas Fixes #1324 --- .../testcontainers/DockerClientFactory.java | 4 + .../{containers => }/LazyDockerClient.java | 5 +- .../testcontainers/containers/Container.java | 7 + .../containers/GenericContainer.java | 31 +-- .../AuditLoggingDockerClient.java | 4 + .../images/AbstractImagePullPolicy.java | 49 +++++ .../images/AgeBasedPullPolicy.java | 28 +++ .../images/AlwaysPullPolicy.java | 20 ++ .../images/DefaultPullPolicy.java | 18 ++ .../org/testcontainers/images/ImageData.java | 29 +++ .../images/ImagePullPolicy.java | 9 + .../images/LocalImagesCache.java | 89 +++++++++ .../org/testcontainers/images/PullPolicy.java | 38 ++++ .../images/RemoteDockerImage.java | 80 ++++---- .../images/AgeBasedPullPolicyTest.java | 30 +++ .../images/ImagePullPolicyTest.java | 178 ++++++++++++++++++ .../junit/NonExistentImagePullTest.java | 2 +- docs/features/advanced_options.md | 16 ++ 18 files changed, 575 insertions(+), 62 deletions(-) rename core/src/main/java/org/testcontainers/{containers => }/LazyDockerClient.java (75%) create mode 100644 core/src/main/java/org/testcontainers/images/AbstractImagePullPolicy.java create mode 100644 core/src/main/java/org/testcontainers/images/AgeBasedPullPolicy.java create mode 100644 core/src/main/java/org/testcontainers/images/AlwaysPullPolicy.java create mode 100644 core/src/main/java/org/testcontainers/images/DefaultPullPolicy.java create mode 100644 core/src/main/java/org/testcontainers/images/ImageData.java create mode 100644 core/src/main/java/org/testcontainers/images/ImagePullPolicy.java create mode 100644 core/src/main/java/org/testcontainers/images/LocalImagesCache.java create mode 100644 core/src/main/java/org/testcontainers/images/PullPolicy.java create mode 100644 core/src/test/java/org/testcontainers/images/AgeBasedPullPolicyTest.java create mode 100644 core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 928b4cad291..54f44dbed3c 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -81,6 +81,10 @@ private DockerClientFactory() { } + public static DockerClient lazyClient() { + return LazyDockerClient.INSTANCE; + } + /** * Obtain an instance of the DockerClientFactory. * diff --git a/core/src/main/java/org/testcontainers/containers/LazyDockerClient.java b/core/src/main/java/org/testcontainers/LazyDockerClient.java similarity index 75% rename from core/src/main/java/org/testcontainers/containers/LazyDockerClient.java rename to core/src/main/java/org/testcontainers/LazyDockerClient.java index b1341b52755..49ebf9330a7 100644 --- a/core/src/main/java/org/testcontainers/containers/LazyDockerClient.java +++ b/core/src/main/java/org/testcontainers/LazyDockerClient.java @@ -1,9 +1,10 @@ -package org.testcontainers.containers; +package org.testcontainers; import com.github.dockerjava.api.DockerClient; +import lombok.ToString; import lombok.experimental.Delegate; -import org.testcontainers.DockerClientFactory; +@ToString enum LazyDockerClient implements DockerClient { INSTANCE; diff --git a/core/src/main/java/org/testcontainers/containers/Container.java b/core/src/main/java/org/testcontainers/containers/Container.java index ca327149ab5..ef27a95a463 100644 --- a/core/src/main/java/org/testcontainers/containers/Container.java +++ b/core/src/main/java/org/testcontainers/containers/Container.java @@ -8,6 +8,7 @@ import lombok.NonNull; import lombok.Value; import org.testcontainers.DockerClientFactory; +import org.testcontainers.images.ImagePullPolicy; import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.startupcheck.StartupCheckStrategy; import org.testcontainers.containers.traits.LinkableContainer; @@ -283,6 +284,12 @@ default SELF withEnv(String key, Function, String> mapper) { */ SELF withNetworkAliases(String... aliases); + /** + * Set the image pull policy of the container + * @return + */ + SELF withImagePullPolicy(ImagePullPolicy policy); + /** * Map a resource (file or directory) on the classpath to a path inside the container. * This will only work if you are running your tests outside a Docker container. diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 47d08b57528..94ce6f64518 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -41,6 +41,7 @@ import org.slf4j.Logger; import org.testcontainers.DockerClientFactory; import org.testcontainers.UnstableAPI; +import org.testcontainers.images.ImagePullPolicy; import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.startupcheck.IsRunningStartupCheckStrategy; import org.testcontainers.containers.startupcheck.MinimumDurationRunningStartupCheckStrategy; @@ -139,7 +140,7 @@ public class GenericContainer> )); @NonNull - private Future image; + private RemoteDockerImage image; @NonNull private Map env = new HashMap<>(); @@ -185,10 +186,10 @@ public class GenericContainer> /** * Unique instance of DockerClient for use by this container object. - * We use {@link LazyDockerClient} here to avoid eager client creation + * We use {@link DockerClientFactory#lazyClient()} here to avoid eager client creation */ @Setter(AccessLevel.NONE) - protected DockerClient dockerClient = LazyDockerClient.INSTANCE; + protected DockerClient dockerClient = DockerClientFactory.lazyClient(); /* * Info about the Docker server; lazily fetched. @@ -250,7 +251,11 @@ public GenericContainer(@NonNull final String dockerImageName) { } public GenericContainer(@NonNull final Future image) { - this.image = image; + setImage(image); + } + + public void setImage(Future image) { + this.image = new RemoteDockerImage(image); } /** @@ -297,11 +302,11 @@ protected void doStart() { Instant startedAt = Instant.now(); logger().debug("Starting container: {}", getDockerImageName()); - logger().debug("Trying to start container: {}", image.get()); + logger().debug("Trying to start container: {}", getDockerImageName()); AtomicInteger attempt = new AtomicInteger(0); Unreliables.retryUntilSuccess(startupAttempts, () -> { - logger().debug("Trying to start container: {} (attempt {}/{})", image.get(), attempt.incrementAndGet(), startupAttempts); + logger().debug("Trying to start container: {} (attempt {}/{})", getDockerImageName(), attempt.incrementAndGet(), startupAttempts); tryStart(startedAt); return true; }); @@ -331,7 +336,7 @@ protected boolean canBeReused() { private void tryStart(Instant startedAt) { try { - String dockerImageName = image.get(); + String dockerImageName = getDockerImageName(); logger().debug("Starting container: {}", dockerImageName); logger().info("Creating container for image: {}", dockerImageName); @@ -530,7 +535,7 @@ public void stop() { String imageName; try { - imageName = image.get(); + imageName = getDockerImageName(); } catch (Exception e) { imageName = ""; } @@ -571,7 +576,6 @@ protected Path createVolumeDirectory(boolean temporary) { } protected void configure() { - } @SuppressWarnings({"EmptyMethod", "UnusedParameters"}) @@ -1107,6 +1111,12 @@ public SELF withNetworkAliases(String... aliases) { return self(); } + @Override + public SELF withImagePullPolicy(ImagePullPolicy imagePullPolicy) { + this.image = this.image.withImagePullPolicy(imagePullPolicy); + return self(); + } + /** * {@inheritDoc} */ @@ -1199,9 +1209,6 @@ public String getIpAddress() { @Override public void setDockerImageName(@NonNull String dockerImageName) { this.image = new RemoteDockerImage(dockerImageName); - - // Mimic old behavior where we resolve image once it's set - getDockerImageName(); } /** diff --git a/core/src/main/java/org/testcontainers/dockerclient/AuditLoggingDockerClient.java b/core/src/main/java/org/testcontainers/dockerclient/AuditLoggingDockerClient.java index 4250b884a74..f982945f9d4 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/AuditLoggingDockerClient.java +++ b/core/src/main/java/org/testcontainers/dockerclient/AuditLoggingDockerClient.java @@ -6,6 +6,7 @@ import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Proxy; import java.util.function.BiConsumer; @@ -99,6 +100,9 @@ private , R> T wrappedCommand(Class clazz, successConsumer.accept(cmd, r); return r; } catch (Exception e) { + if (e instanceof InvocationTargetException && e.getCause() instanceof Exception) { + e = (Exception) e.getCause(); + } failureConsumer.accept(cmd, e); throw e; } diff --git a/core/src/main/java/org/testcontainers/images/AbstractImagePullPolicy.java b/core/src/main/java/org/testcontainers/images/AbstractImagePullPolicy.java new file mode 100644 index 00000000000..11c621eb08b --- /dev/null +++ b/core/src/main/java/org/testcontainers/images/AbstractImagePullPolicy.java @@ -0,0 +1,49 @@ +package org.testcontainers.images; + +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.DockerLoggerFactory; + +@Slf4j +public abstract class AbstractImagePullPolicy implements ImagePullPolicy { + + private static final LocalImagesCache LOCAL_IMAGES_CACHE = LocalImagesCache.INSTANCE; + + @Override + public boolean shouldPull(DockerImageName imageName) { + Logger logger = DockerLoggerFactory.getLogger(imageName.toString()); + + // Does our cache already know the image? + ImageData cachedImageData = LOCAL_IMAGES_CACHE.get(imageName); + if (cachedImageData != null) { + logger.trace("{} is already in image name cache", imageName); + } else { + logger.debug("{} is not in image name cache, updating...", imageName); + // Was not in cache, inspecting... + cachedImageData = LOCAL_IMAGES_CACHE.refreshCache(imageName).orElse(null); + + if (cachedImageData == null) { + log.debug("Not available locally, should pull image: {}", imageName); + return true; + } + } + + if (shouldPullCached(imageName, cachedImageData)) { + log.debug("Should pull locally available image: {}", imageName); + return true; + } else { + log.debug("Using locally available and not pulling image: {}", imageName); + return false; + } + + } + + /** + * Implement this method to decide whether a locally available image should be pulled + * (e.g. to always pull images, or to pull them after some duration of time) + * + * @return {@code true} to update the locally available image, {@code false} to use local instead + */ + abstract protected boolean shouldPullCached(DockerImageName imageName, ImageData localImageData); +} diff --git a/core/src/main/java/org/testcontainers/images/AgeBasedPullPolicy.java b/core/src/main/java/org/testcontainers/images/AgeBasedPullPolicy.java new file mode 100644 index 00000000000..39028d6c52b --- /dev/null +++ b/core/src/main/java/org/testcontainers/images/AgeBasedPullPolicy.java @@ -0,0 +1,28 @@ +package org.testcontainers.images; + +import lombok.Value; +import lombok.extern.slf4j.Slf4j; +import org.testcontainers.utility.DockerImageName; + +import java.time.Duration; +import java.time.Instant; + +/** + * An ImagePullPolicy which pulls the image from a remote repository only if its created date is older than maxAge + */ +@Slf4j +@Value +class AgeBasedPullPolicy extends AbstractImagePullPolicy { + + Duration maxAge; + + @Override + protected boolean shouldPullCached(DockerImageName imageName, ImageData localImageData) { + Duration imageAge = Duration.between(localImageData.getCreatedAt(), Instant.now()); + boolean result = imageAge.compareTo(maxAge) > 0; + if (result) { + log.trace("Should pull image: {}", imageName); + } + return result; + } +} diff --git a/core/src/main/java/org/testcontainers/images/AlwaysPullPolicy.java b/core/src/main/java/org/testcontainers/images/AlwaysPullPolicy.java new file mode 100644 index 00000000000..92f94f67124 --- /dev/null +++ b/core/src/main/java/org/testcontainers/images/AlwaysPullPolicy.java @@ -0,0 +1,20 @@ +package org.testcontainers.images; + +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; +import org.testcontainers.utility.DockerImageName; + +/*** + * An ImagePullPolicy which pulls the image even if it exists locally. + * Useful for obtaining the latest version of an image with a static tag, i.e. 'latest' + */ +@Slf4j +@ToString +class AlwaysPullPolicy implements ImagePullPolicy { + + @Override + public boolean shouldPull(DockerImageName imageName) { + log.trace("Unconditionally pulling an image: {}", imageName); + return true; + } +} diff --git a/core/src/main/java/org/testcontainers/images/DefaultPullPolicy.java b/core/src/main/java/org/testcontainers/images/DefaultPullPolicy.java new file mode 100644 index 00000000000..40984b2940e --- /dev/null +++ b/core/src/main/java/org/testcontainers/images/DefaultPullPolicy.java @@ -0,0 +1,18 @@ +package org.testcontainers.images; + +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; +import org.testcontainers.utility.DockerImageName; + +/** + * The default imagePullPolicy, which pulls the image from a remote repository only if it does not exist locally + */ +@Slf4j +@ToString +class DefaultPullPolicy extends AbstractImagePullPolicy { + + @Override + protected boolean shouldPullCached(DockerImageName imageName, ImageData localImageData) { + return false; + } +} diff --git a/core/src/main/java/org/testcontainers/images/ImageData.java b/core/src/main/java/org/testcontainers/images/ImageData.java new file mode 100644 index 00000000000..bef0c56c245 --- /dev/null +++ b/core/src/main/java/org/testcontainers/images/ImageData.java @@ -0,0 +1,29 @@ +package org.testcontainers.images; + +import com.github.dockerjava.api.command.InspectImageResponse; +import com.github.dockerjava.api.model.Image; +import lombok.Builder; +import lombok.NonNull; +import lombok.Value; + +import java.time.Instant; + +@Value +@Builder +class ImageData { + + @NonNull + Instant createdAt; + + static ImageData from(InspectImageResponse inspectImageResponse) { + return ImageData.builder() + .createdAt(Instant.parse(inspectImageResponse.getCreated())) + .build(); + } + + static ImageData from(Image image) { + return ImageData.builder() + .createdAt(Instant.ofEpochMilli(image.getCreated())) + .build(); + } +} diff --git a/core/src/main/java/org/testcontainers/images/ImagePullPolicy.java b/core/src/main/java/org/testcontainers/images/ImagePullPolicy.java new file mode 100644 index 00000000000..e821e511795 --- /dev/null +++ b/core/src/main/java/org/testcontainers/images/ImagePullPolicy.java @@ -0,0 +1,9 @@ +package org.testcontainers.images; + +import org.testcontainers.utility.DockerImageName; + +public interface ImagePullPolicy { + + boolean shouldPull(DockerImageName imageName); + +} diff --git a/core/src/main/java/org/testcontainers/images/LocalImagesCache.java b/core/src/main/java/org/testcontainers/images/LocalImagesCache.java new file mode 100644 index 00000000000..29bc8ef1408 --- /dev/null +++ b/core/src/main/java/org/testcontainers/images/LocalImagesCache.java @@ -0,0 +1,89 @@ +package org.testcontainers.images; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.command.InspectImageResponse; +import com.github.dockerjava.api.exception.NotFoundException; +import com.github.dockerjava.api.model.Image; +import com.google.common.annotations.VisibleForTesting; +import lombok.extern.slf4j.Slf4j; +import org.testcontainers.DockerClientFactory; +import org.testcontainers.utility.DockerImageName; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Slf4j +enum LocalImagesCache { + INSTANCE; + + private final AtomicBoolean initialized = new AtomicBoolean(false); + + @VisibleForTesting + final Map cache = new ConcurrentHashMap<>(); + + DockerClient dockerClient = DockerClientFactory.lazyClient(); + + public ImageData get(DockerImageName imageName) { + maybeInitCache(); + return cache.get(imageName); + } + + public Optional refreshCache(DockerImageName imageName) { + if (!maybeInitCache()) { + // Cache may be stale, trying inspectImageCmd... + + InspectImageResponse response = null; + try { + response = dockerClient.inspectImageCmd(imageName.toString()).exec(); + } catch (NotFoundException e) { + log.trace("Image {} not found", imageName, e); + } + if (response != null) { + ImageData imageData = ImageData.from(response); + cache.put(imageName, imageData); + return Optional.of(imageData); + } else { + cache.remove(imageName); + return Optional.empty(); + } + } + + return Optional.ofNullable(cache.get(imageName)); + } + + private synchronized boolean maybeInitCache() { + if (!initialized.compareAndSet(false, true)) { + return false; + } + + if (Boolean.parseBoolean(System.getProperty("useFilter"))) { + return false; + } + + populateFromList(dockerClient.listImagesCmd().exec()); + + return true; + } + + private void populateFromList(List images) { + for (Image image : images) { + String[] repoTags = image.getRepoTags(); + if (repoTags == null) { + log.debug("repoTags is null, skipping image: {}", image); + continue; + } + + cache.putAll( + Stream.of(repoTags).collect(Collectors.toMap( + DockerImageName::new, + it -> ImageData.from(image) + )) + ); + } + } +} diff --git a/core/src/main/java/org/testcontainers/images/PullPolicy.java b/core/src/main/java/org/testcontainers/images/PullPolicy.java new file mode 100644 index 00000000000..3dc2e452b8d --- /dev/null +++ b/core/src/main/java/org/testcontainers/images/PullPolicy.java @@ -0,0 +1,38 @@ +package org.testcontainers.images; + +import java.time.Duration; + +import lombok.experimental.UtilityClass; + +/** + * Convenience class with logic for building common {@link ImagePullPolicy} instances. + * + */ +@UtilityClass +public class PullPolicy { + + /** + * Convenience method for returning the {@link DefaultPullPolicy} default image pull policy + * @return {@link ImagePullPolicy} + */ + public static ImagePullPolicy defaultPolicy() { + return new DefaultPullPolicy(); + } + + /** + * Convenience method for returning the {@link AlwaysPullPolicy} alwaysPull image pull policy + * @return {@link ImagePullPolicy} + */ + public static ImagePullPolicy alwaysPull() { + return new AlwaysPullPolicy(); + } + + /** + * Convenience method for returning an {@link AgeBasedPullPolicy} Age based image pull policy, + * @return {@link ImagePullPolicy} + */ + public static ImagePullPolicy ageBased(Duration maxAge) { + return new AgeBasedPullPolicy(maxAge); + } + +} diff --git a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java index 257bacc9961..4975f536384 100644 --- a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java +++ b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java @@ -1,13 +1,14 @@ package org.testcontainers.images; import com.github.dockerjava.api.DockerClient; -import com.github.dockerjava.api.command.ListImagesCmd; import com.github.dockerjava.api.exception.DockerClientException; import com.github.dockerjava.api.exception.InternalServerErrorException; -import com.github.dockerjava.api.model.Image; -import com.github.dockerjava.core.command.PullImageResultCallback; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.NonNull; +import lombok.SneakyThrows; import lombok.ToString; +import lombok.experimental.Wither; import org.slf4j.Logger; import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.ContainerFetchException; @@ -17,63 +18,48 @@ import java.time.Duration; import java.time.Instant; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; @ToString +@AllArgsConstructor(access = AccessLevel.PACKAGE) public class RemoteDockerImage extends LazyFuture { - /** - * @deprecated this field will become private in a later release - */ - @Deprecated - public static final Set AVAILABLE_IMAGE_NAME_CACHE = new HashSet<>(); private static final Duration PULL_RETRY_TIME_LIMIT = Duration.ofMinutes(2); - private DockerImageName imageName; + private Future imageNameFuture; + + @Wither + private ImagePullPolicy imagePullPolicy = PullPolicy.defaultPolicy(); + + private DockerClient dockerClient = DockerClientFactory.lazyClient(); public RemoteDockerImage(String dockerImageName) { - imageName = new DockerImageName(dockerImageName); + this.imageNameFuture = CompletableFuture.completedFuture(new DockerImageName(dockerImageName)); } public RemoteDockerImage(@NonNull String repository, @NonNull String tag) { - imageName = new DockerImageName(repository, tag); + this.imageNameFuture = CompletableFuture.completedFuture(new DockerImageName(repository, tag)); + } + + public RemoteDockerImage(@NonNull Future imageFuture) { + this.imageNameFuture = new LazyFuture() { + @Override + @SneakyThrows({InterruptedException.class, ExecutionException.class}) + protected DockerImageName resolve() { + return new DockerImageName(imageFuture.get()); + } + }; } @Override + @SneakyThrows({InterruptedException.class, ExecutionException.class}) protected final String resolve() { + final DockerImageName imageName = imageNameFuture.get(); Logger logger = DockerLoggerFactory.getLogger(imageName.toString()); - - DockerClient dockerClient = DockerClientFactory.instance().client(); try { - // Does our cache already know the image? - if (AVAILABLE_IMAGE_NAME_CACHE.contains(imageName)) { - logger.trace("{} is already in image name cache", imageName); - return imageName.toString(); - } - - // Update the cache - ListImagesCmd listImagesCmd = dockerClient.listImagesCmd(); - - if (Boolean.parseBoolean(System.getProperty("useFilter"))) { - listImagesCmd = listImagesCmd.withImageNameFilter(imageName.toString()); - } - - List updatedImages = listImagesCmd.exec(); - updatedImages.stream() - .map(Image::getRepoTags) - .filter(Objects::nonNull) - .flatMap(Stream::of) - .map(DockerImageName::new) - .collect(Collectors.toCollection(() -> AVAILABLE_IMAGE_NAME_CACHE)); - - // And now? - if (AVAILABLE_IMAGE_NAME_CACHE.contains(imageName)) { - logger.trace("{} is in image name cache following listing of images", imageName); + if (!imagePullPolicy.shouldPull(imageName)) { return imageName.toString(); } @@ -85,13 +71,13 @@ protected final String resolve() { while (Instant.now().isBefore(lastRetryAllowed)) { try { - final PullImageResultCallback callback = new TimeLimitedLoggedPullImageResultCallback(logger); dockerClient .pullImageCmd(imageName.getUnversionedPart()) .withTag(imageName.getVersionPart()) - .exec(callback); - callback.awaitCompletion(); - AVAILABLE_IMAGE_NAME_CACHE.add(imageName); + .exec(new TimeLimitedLoggedPullImageResultCallback(logger)) + .awaitCompletion(); + + LocalImagesCache.INSTANCE.refreshCache(imageName); return imageName.toString(); } catch (InterruptedException | InternalServerErrorException e) { diff --git a/core/src/test/java/org/testcontainers/images/AgeBasedPullPolicyTest.java b/core/src/test/java/org/testcontainers/images/AgeBasedPullPolicyTest.java new file mode 100644 index 00000000000..20b52798d40 --- /dev/null +++ b/core/src/test/java/org/testcontainers/images/AgeBasedPullPolicyTest.java @@ -0,0 +1,30 @@ +package org.testcontainers.images; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.time.Duration; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.UUID; + +import org.junit.Test; +import org.testcontainers.utility.DockerImageName; + +public class AgeBasedPullPolicyTest { + + final DockerImageName imageName = new DockerImageName(UUID.randomUUID().toString()); + + @Test + public void shouldPull() { + ImageData imageData = ImageData.builder() + .createdAt(Instant.now().minus(2, ChronoUnit.HOURS)) + .build(); + + AgeBasedPullPolicy oneHour = new AgeBasedPullPolicy(Duration.of(1L, ChronoUnit.HOURS)); + assertTrue(oneHour.shouldPullCached(imageName, imageData)); + + AgeBasedPullPolicy fiveHours = new AgeBasedPullPolicy(Duration.of(5L, ChronoUnit.HOURS)); + assertFalse(fiveHours.shouldPullCached(imageName, imageData)); + } +} diff --git a/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java b/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java new file mode 100644 index 00000000000..59b7486c75e --- /dev/null +++ b/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java @@ -0,0 +1,178 @@ +package org.testcontainers.images; + +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.exception.NotFoundException; +import com.github.dockerjava.core.command.PullImageResultCallback; +import com.github.dockerjava.core.command.PushImageResultCallback; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.mockito.Mockito; +import org.rnorth.visibleassertions.VisibleAssertions; +import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.ContainerLaunchException; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; +import org.testcontainers.utility.DockerImageName; + +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +public class ImagePullPolicyTest { + + @ClassRule + public static GenericContainer registry = new GenericContainer<>("registry:2") + .withExposedPorts(5000); + + private static String imageName; + + @BeforeClass + public static void beforeClass() throws Exception { + String testRegistryAddress = registry.getContainerIpAddress() + ":" + registry.getFirstMappedPort(); + String testImageName = testRegistryAddress + "/image-pull-policy-test"; + String tag = UUID.randomUUID().toString(); + imageName = testImageName + ":" + tag; + + DockerClient client = DockerClientFactory.instance().client(); + String dummySourceImage = "hello-world:latest"; + client.pullImageCmd(dummySourceImage).exec(new PullImageResultCallback()).awaitCompletion(); + + String dummyImageId = client.inspectImageCmd(dummySourceImage).exec().getId(); + + // push the image to the registry + client.tagImageCmd(dummyImageId, testImageName, tag).exec(); + + client.pushImageCmd(imageName) + .exec(new PushImageResultCallback()) + .awaitCompletion(1, TimeUnit.MINUTES); + } + + @AfterClass + public static void afterClass() { + try { + DockerClientFactory.instance().client().removeImageCmd(imageName).withForce(true).exec(); + } catch (NotFoundException ignored) { + } + } + + @Before + public void setUp() { + // Clean up local cache + try { + DockerClientFactory.instance().client().removeImageCmd(imageName).withForce(true).exec(); + } catch (NotFoundException ignored) { + } + + LocalImagesCache.INSTANCE.cache.remove(new DockerImageName(imageName)); + } + + @Test + public void pullsByDefault() { + try ( + GenericContainer container = new GenericContainer<>(imageName) + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + ) { + container.start(); + } + } + + @Test + public void shouldAlwaysPull() { + try ( + GenericContainer container = new GenericContainer<>(imageName) + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + ) { + container.start(); + } + + DockerClientFactory.instance().client().removeImageCmd(imageName).withForce(true).exec(); + + try ( + GenericContainer container = new GenericContainer<>(imageName) + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + ) { + expectToFailWithNotFoundException(container); + } + + try ( + // built_in_image_pull_policy { + GenericContainer container = new GenericContainer<>(imageName) + .withImagePullPolicy(PullPolicy.alwaysPull()) + // } + ) { + container.withStartupCheckStrategy(new OneShotStartupCheckStrategy()); + container.start(); + } + } + + @Test + public void shouldSupportCustomPolicies() { + try ( + // custom_image_pull_policy { + GenericContainer container = new GenericContainer<>(imageName) + .withImagePullPolicy(new AbstractImagePullPolicy() { + @Override + protected boolean shouldPullCached(DockerImageName imageName, ImageData localImageData) { + return System.getenv("ALWAYS_PULL_IMAGE") != null; + } + }) + // } + ) { + container.withStartupCheckStrategy(new OneShotStartupCheckStrategy()); + container.start(); + } + } + + @Test + public void shouldCheckPolicy() { + ImagePullPolicy policy = Mockito.spy(new AbstractImagePullPolicy() { + @Override + protected boolean shouldPullCached(DockerImageName imageName, ImageData localImageData) { + return false; + } + }); + try ( + GenericContainer container = new GenericContainer<>(imageName) + .withImagePullPolicy(policy) + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + ) { + container.start(); + + Mockito.verify(policy).shouldPull(any()); + } + } + + @Test + public void shouldNotForcePulling() { + try ( + GenericContainer container = new GenericContainer<>(imageName) + .withImagePullPolicy(__ -> false) + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + ) { + expectToFailWithNotFoundException(container); + } + } + + private void expectToFailWithNotFoundException(GenericContainer container) { + try { + container.start(); + fail("Should fail"); + } catch (ContainerLaunchException e) { + Throwable throwable = e; + while (throwable.getCause() != null) { + throwable = throwable.getCause(); + if (throwable.getCause() instanceof NotFoundException) { + VisibleAssertions.pass("Caused by NotFoundException"); + return; + } + } + VisibleAssertions.fail("Caused by NotFoundException"); + } + } + +} diff --git a/core/src/test/java/org/testcontainers/junit/NonExistentImagePullTest.java b/core/src/test/java/org/testcontainers/junit/NonExistentImagePullTest.java index 22c7b4e9d9c..d842829b4b6 100644 --- a/core/src/test/java/org/testcontainers/junit/NonExistentImagePullTest.java +++ b/core/src/test/java/org/testcontainers/junit/NonExistentImagePullTest.java @@ -16,7 +16,7 @@ public void pullingNonExistentImageFailsGracefully() { assertThrows("Pulling a nonexistent container will cause an exception to be thrown", ContainerFetchException.class, () -> { - return new GenericContainer("richnorth/nonexistent:latest"); + new GenericContainer("richnorth/nonexistent:latest").getDockerImageName(); }); } } diff --git a/docs/features/advanced_options.md b/docs/features/advanced_options.md index 385cdc3a4c8..bedb7aee6b8 100644 --- a/docs/features/advanced_options.md +++ b/docs/features/advanced_options.md @@ -14,6 +14,22 @@ Additionally, multiple labels may be applied together from a map: [Adding multiple labels](../examples/src/test/java/generic/ContainerLabelTest.java) inside_block:multiple_labels +## Image Pull Policy + +By default, the container image is retrieved from the local Docker images cache. +This works well when running against a specific version, but for images with a static tag (i.e. 'latest') this may lead to a newer version not being pulled. + +It is possible to specify an Image Pull Policy to determine at runtime whether an image should be pulled or not: + + +[Setting image pull policy](../../core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java) inside_block:built_in_image_pull_policy + + +... or providing a function: + + +[Custom image pull policy](../../core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java) inside_block:custom_image_pull_policy + ## Customizing the container From 22a5e92fc26d27023bcbde8da3750451250bef58 Mon Sep 17 00:00:00 2001 From: Erhun Baycelik Date: Wed, 27 Nov 2019 17:59:42 +0100 Subject: [PATCH 1046/5615] Update Who Is Using Testcontainers (#2105) As a Transferwise engineer I want to list our company in this list. --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index 8f95ad5399a..c26ed164067 100644 --- a/docs/index.md +++ b/docs/index.md @@ -77,6 +77,7 @@ You can also [check the latest version available on Maven Central](https://searc * [JHipster](https://www.jhipster.tech/) - Couchbase and Cassandra integration testing * [wescale](https://www.wescale.com) - Integration testing against HTTP service mocks and various data stores * [Marquez](https://marquezproject.github.io/marquez) - PostgreSQL integration testing +* [Transferwise](https://transferwise.com/) - Integration testing for different RDBMS, kafka and micro services ## License From 4f6f2c7cdf65f16952806ac91758ba0e3cf7e8d8 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 27 Nov 2019 19:08:04 +0100 Subject: [PATCH 1047/5615] Restore Flaky on couchbase tests (#2106) * Restore `@Flaky` in Couchbase tests (partially revert #2081) * Manually start/stop container, do not reuse environment/cluster --- .../couchbase/AbstractCouchbaseTest.java | 21 ++++++++++------ .../couchbase/CouchbaseContainer.java | 11 +++++++-- .../couchbase/BaseCouchbaseContainerTest.java | 24 +++++++++++++++++++ .../couchbase/Couchbase4_6Test.java | 7 +++--- .../couchbase/Couchbase5_1Test.java | 7 +++--- .../couchbase/Couchbase5_5Test.java | 7 +++--- .../couchbase/CouchbaseCommunity5_1Test.java | 7 +++--- 7 files changed, 63 insertions(+), 21 deletions(-) diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java index e1aec0ba424..7fd99ca9fbc 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java @@ -10,6 +10,8 @@ import com.couchbase.client.java.query.consistency.ScanConsistency; import org.junit.After; +import java.util.concurrent.TimeUnit; + /** * Basic class that can be used for couchbase tests. It will clear the database after every test. */ @@ -19,16 +21,21 @@ public abstract class AbstractCouchbaseTest { public static final String DEFAULT_PASSWORD = "password"; - private Bucket bucket; + Bucket bucket; @After public void clear() { - if (getCouchbaseContainer().isIndex() && getCouchbaseContainer().isQuery() && getCouchbaseContainer().isPrimaryIndex()) { - getBucket().query( - N1qlQuery.simple(String.format("DELETE FROM `%s`", getBucket().name()), - N1qlParams.build().consistency(ScanConsistency.STATEMENT_PLUS))); - } else { - getBucket().bucketManager().flush(); + try { + if (getCouchbaseContainer().isIndex() && getCouchbaseContainer().isQuery() && getCouchbaseContainer().isPrimaryIndex()) { + getBucket().query( + N1qlQuery.simple(String.format("DELETE FROM `%s`", getBucket().name()), + N1qlParams.build().consistency(ScanConsistency.STATEMENT_PLUS))); + } else { + getBucket().bucketManager().flush(); + } + } finally { + bucket.close(60, TimeUnit.SECONDS); + bucket = null; } } diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index 9e7ace2af04..19583d5a0e4 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -47,6 +47,7 @@ import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -199,9 +200,15 @@ public List getBoundPortNumbers() { } @Override + @SuppressWarnings({"unchecked", "ConstantConditions"}) public void stop() { - stopCluster(); - Stream.of(super::stop, proxy::stop).parallel().forEach(Runnable::run); + try { + stopCluster(); + ((AtomicReference) (Object) couchbaseEnvironment).set(null); + ((AtomicReference) (Object) couchbaseCluster).set(null); + } finally { + Stream.of(super::stop, proxy::stop).parallel().forEach(Runnable::run); + } } private void stopCluster() { diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java index 845d9bfaf5e..6deba2c4c5e 100644 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java @@ -9,7 +9,13 @@ import com.couchbase.client.java.view.View; import com.google.common.collect.Lists; import org.junit.Assert; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.RuleChain; +import org.junit.runner.Description; +import org.testcontainers.containers.FailureDetectingExternalResource; +import org.testcontainers.testsupport.Flaky; +import org.testcontainers.testsupport.FlakyTestJUnit4RetryRule; import java.util.List; @@ -25,7 +31,23 @@ public abstract class BaseCouchbaseContainerTest extends AbstractCouchbaseTest { private static final String DOCUMENT = "{\"name\":\"toto\"}"; + @Rule + public RuleChain ruleChain = RuleChain.emptyRuleChain() + .around(new FlakyTestJUnit4RetryRule()) + .around(new FailureDetectingExternalResource() { + @Override + protected void starting(Description description) { + getCouchbaseContainer().start(); + } + + @Override + protected void failed(Throwable e, Description description) { + getCouchbaseContainer().stop(); + } + }); + @Test + @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1453", reviewDate = "2019-12-01") public void shouldInsertDocument() { RawJsonDocument expected = RawJsonDocument.create(ID, DOCUMENT); getBucket().upsert(expected); @@ -34,6 +56,7 @@ public void shouldInsertDocument() { } @Test + @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1453", reviewDate = "2019-12-01") public void shouldExecuteN1ql() { getBucket().query(N1qlQuery.simple("INSERT INTO " + TEST_BUCKET + " (KEY, VALUE) VALUES ('" + ID + "', " + DOCUMENT + ")")); @@ -46,6 +69,7 @@ public void shouldExecuteN1ql() { } @Test + @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1453", reviewDate = "2019-12-01") public void shouldCreateView() { View view = DefaultView.create(VIEW_NAME, VIEW_FUNCTION); DesignDocument document = DesignDocument.create(VIEW_NAME, Lists.newArrayList(view)); diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase4_6Test.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase4_6Test.java index 7781d705d41..c43d3c981ba 100644 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase4_6Test.java +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase4_6Test.java @@ -1,10 +1,11 @@ package org.testcontainers.couchbase; -import org.junit.ClassRule; +import org.junit.Rule; public class Couchbase4_6Test extends BaseCouchbaseContainerTest { - @ClassRule - public static CouchbaseContainer container = initCouchbaseContainer("couchbase/server:4.6.5"); + + @Rule + public CouchbaseContainer container = initCouchbaseContainer("couchbase/server:4.6.5"); @Override public CouchbaseContainer getCouchbaseContainer() { diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_1Test.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_1Test.java index 8334088fdb3..2c3dbf3c901 100644 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_1Test.java +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_1Test.java @@ -1,10 +1,11 @@ package org.testcontainers.couchbase; -import org.junit.ClassRule; +import org.junit.Rule; public class Couchbase5_1Test extends BaseCouchbaseContainerTest { - @ClassRule - public static CouchbaseContainer container = initCouchbaseContainer("couchbase/server:5.1.1"); + + @Rule + public CouchbaseContainer container = initCouchbaseContainer("couchbase/server:5.1.1"); @Override public CouchbaseContainer getCouchbaseContainer() { diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_5Test.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_5Test.java index 40a44235f20..47bc020ef03 100644 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_5Test.java +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_5Test.java @@ -1,10 +1,11 @@ package org.testcontainers.couchbase; -import org.junit.ClassRule; +import org.junit.Rule; public class Couchbase5_5Test extends BaseCouchbaseContainerTest { - @ClassRule - public static CouchbaseContainer container = initCouchbaseContainer("couchbase/server:5.5.1"); + + @Rule + public CouchbaseContainer container = initCouchbaseContainer("couchbase/server:5.5.1"); @Override public CouchbaseContainer getCouchbaseContainer() { diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseCommunity5_1Test.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseCommunity5_1Test.java index 74298c990cf..30be62bc2bd 100644 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseCommunity5_1Test.java +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseCommunity5_1Test.java @@ -6,10 +6,12 @@ import com.couchbase.client.java.cluster.UserRole; import com.couchbase.client.java.cluster.UserSettings; +import org.junit.Rule; public class CouchbaseCommunity5_1Test extends BaseCouchbaseContainerTest { - @ClassRule - public static CouchbaseContainer container = initCouchbaseContainer(); + + @Rule + public CouchbaseContainer container = initCouchbaseContainer(); private static CouchbaseContainer initCouchbaseContainer() { CouchbaseContainer couchbaseContainer = new CouchbaseContainer("couchbase/server:community-5.1.1"); @@ -20,7 +22,6 @@ private static CouchbaseContainer initCouchbaseContainer() { .roles(Collections.singletonList(new UserRole("admin"))); couchbaseContainer.withNewBucket(getDefaultBucketSettings(), communityAdminUserSettings); - couchbaseContainer.start(); return couchbaseContainer; } From 23235833edfb1ae6c6c1147309aad4c067394728 Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 27 Nov 2019 20:07:03 +0000 Subject: [PATCH 1048/5615] =?UTF-8?q?Break=20up=20quickstart=20example=20p?= =?UTF-8?q?rojects=20and=20introduce=20JUnit=205=20refe=E2=80=A6=20(#2097)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Break up quickstart example projects and introduce JUnit 5 refe… (#1416) * Break up quickstart example projects and introduce JUnit 5 reference * Use for dependency declarations Dependabot needs to be able to discover those and propose a change. * Relocate Spock example code Co-authored-by: Benjamin Muschko --- .../{ => junit4/generic}/build.gradle | 2 - .../test/java/generic/CmdModifierTest.java | 0 .../src/test/java/generic/CommandsTest.java | 0 .../java/generic/ContainerCreationTest.java | 0 .../test/java/generic/ContainerLabelTest.java | 0 .../src/test/java/generic/ExecTest.java | 0 .../java/generic/HostPortExposedTest.java | 0 .../generic/MultiplePortsExposedTest.java | 0 .../test/java/generic/WaitStrategiesTest.java | 0 docs/examples/junit4/redis/build.gradle | 8 ++ .../java/quickstart/RedisBackedCache.java | 0 .../quickstart/RedisBackedCacheIntTest.java | 0 .../RedisBackedCacheIntTestStep0.java | 0 .../src/test/resources/logback-test.xml | 0 docs/examples/junit5/redis/build.gradle | 11 ++ .../java/quickstart/RedisBackedCache.java | 0 .../quickstart/RedisBackedCacheIntTest.java | 42 +++++++ .../RedisBackedCacheIntTestStep0.java | 27 +++++ .../redis/src/test/resources/logback-test.xml | 27 +++++ .../examples/spock/redis}/build.gradle | 6 +- .../java/quickstart/RedisBackedCache.java | 22 ++++ .../quickstart/RedisBackedCacheIntTest.groovy | 0 .../RedisBackedCacheIntTestStep0.groovy | 0 .../src/test/resources/logback-test.xml | 0 docs/features/advanced_options.md | 8 +- docs/features/commands.md | 6 +- docs/features/creating_container.md | 4 +- docs/features/networking.md | 14 +-- docs/features/startup_and_waits.md | 14 +-- docs/quickstart/junit_4_quickstart.md | 8 +- docs/quickstart/junit_5_quickstart.md | 110 ++++++++++++++++++ docs/quickstart/spock_quickstart.md | 8 +- mkdocs.yml | 1 + settings.gradle | 7 +- 34 files changed, 285 insertions(+), 40 deletions(-) rename docs/examples/{ => junit4/generic}/build.gradle (81%) rename docs/examples/{ => junit4/generic}/src/test/java/generic/CmdModifierTest.java (100%) rename docs/examples/{ => junit4/generic}/src/test/java/generic/CommandsTest.java (100%) rename docs/examples/{ => junit4/generic}/src/test/java/generic/ContainerCreationTest.java (100%) rename docs/examples/{ => junit4/generic}/src/test/java/generic/ContainerLabelTest.java (100%) rename docs/examples/{ => junit4/generic}/src/test/java/generic/ExecTest.java (100%) rename docs/examples/{ => junit4/generic}/src/test/java/generic/HostPortExposedTest.java (100%) rename docs/examples/{ => junit4/generic}/src/test/java/generic/MultiplePortsExposedTest.java (100%) rename docs/examples/{ => junit4/generic}/src/test/java/generic/WaitStrategiesTest.java (100%) create mode 100644 docs/examples/junit4/redis/build.gradle rename docs/examples/{ => junit4/redis}/src/main/java/quickstart/RedisBackedCache.java (100%) rename docs/examples/{ => junit4/redis}/src/test/java/quickstart/RedisBackedCacheIntTest.java (100%) rename docs/examples/{ => junit4/redis}/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java (100%) rename docs/examples/{ => junit4/redis}/src/test/resources/logback-test.xml (100%) create mode 100644 docs/examples/junit5/redis/build.gradle rename {examples/spock => docs/examples/junit5/redis}/src/main/java/quickstart/RedisBackedCache.java (100%) create mode 100644 docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java create mode 100644 docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java create mode 100644 docs/examples/junit5/redis/src/test/resources/logback-test.xml rename {examples/spock => docs/examples/spock/redis}/build.gradle (74%) create mode 100644 docs/examples/spock/redis/src/main/java/quickstart/RedisBackedCache.java rename {examples/spock => docs/examples/spock/redis}/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy (100%) rename {examples/spock => docs/examples/spock/redis}/src/test/groovy/quickstart/RedisBackedCacheIntTestStep0.groovy (100%) rename {examples/spock => docs/examples/spock/redis}/src/test/resources/logback-test.xml (100%) create mode 100644 docs/quickstart/junit_5_quickstart.md diff --git a/docs/examples/build.gradle b/docs/examples/junit4/generic/build.gradle similarity index 81% rename from docs/examples/build.gradle rename to docs/examples/junit4/generic/build.gradle index e5247a6e9d8..97902d5d9ca 100644 --- a/docs/examples/build.gradle +++ b/docs/examples/junit4/generic/build.gradle @@ -1,8 +1,6 @@ description = "Examples for docs" dependencies { - compile "io.lettuce:lettuce-core:5.1.1.RELEASE" - testCompile "junit:junit:4.12" testCompile project(":testcontainers") testCompile project(":selenium") diff --git a/docs/examples/src/test/java/generic/CmdModifierTest.java b/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java similarity index 100% rename from docs/examples/src/test/java/generic/CmdModifierTest.java rename to docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java diff --git a/docs/examples/src/test/java/generic/CommandsTest.java b/docs/examples/junit4/generic/src/test/java/generic/CommandsTest.java similarity index 100% rename from docs/examples/src/test/java/generic/CommandsTest.java rename to docs/examples/junit4/generic/src/test/java/generic/CommandsTest.java diff --git a/docs/examples/src/test/java/generic/ContainerCreationTest.java b/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java similarity index 100% rename from docs/examples/src/test/java/generic/ContainerCreationTest.java rename to docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java diff --git a/docs/examples/src/test/java/generic/ContainerLabelTest.java b/docs/examples/junit4/generic/src/test/java/generic/ContainerLabelTest.java similarity index 100% rename from docs/examples/src/test/java/generic/ContainerLabelTest.java rename to docs/examples/junit4/generic/src/test/java/generic/ContainerLabelTest.java diff --git a/docs/examples/src/test/java/generic/ExecTest.java b/docs/examples/junit4/generic/src/test/java/generic/ExecTest.java similarity index 100% rename from docs/examples/src/test/java/generic/ExecTest.java rename to docs/examples/junit4/generic/src/test/java/generic/ExecTest.java diff --git a/docs/examples/src/test/java/generic/HostPortExposedTest.java b/docs/examples/junit4/generic/src/test/java/generic/HostPortExposedTest.java similarity index 100% rename from docs/examples/src/test/java/generic/HostPortExposedTest.java rename to docs/examples/junit4/generic/src/test/java/generic/HostPortExposedTest.java diff --git a/docs/examples/src/test/java/generic/MultiplePortsExposedTest.java b/docs/examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java similarity index 100% rename from docs/examples/src/test/java/generic/MultiplePortsExposedTest.java rename to docs/examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java diff --git a/docs/examples/src/test/java/generic/WaitStrategiesTest.java b/docs/examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java similarity index 100% rename from docs/examples/src/test/java/generic/WaitStrategiesTest.java rename to docs/examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java diff --git a/docs/examples/junit4/redis/build.gradle b/docs/examples/junit4/redis/build.gradle new file mode 100644 index 00000000000..76d32e5b4d0 --- /dev/null +++ b/docs/examples/junit4/redis/build.gradle @@ -0,0 +1,8 @@ +description = "Examples for docs" + +dependencies { + compile "io.lettuce:lettuce-core:5.1.1.RELEASE" + + testCompile "junit:junit:4.12" + testCompile project(":testcontainers") +} diff --git a/docs/examples/src/main/java/quickstart/RedisBackedCache.java b/docs/examples/junit4/redis/src/main/java/quickstart/RedisBackedCache.java similarity index 100% rename from docs/examples/src/main/java/quickstart/RedisBackedCache.java rename to docs/examples/junit4/redis/src/main/java/quickstart/RedisBackedCache.java diff --git a/docs/examples/src/test/java/quickstart/RedisBackedCacheIntTest.java b/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java similarity index 100% rename from docs/examples/src/test/java/quickstart/RedisBackedCacheIntTest.java rename to docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java diff --git a/docs/examples/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java b/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java similarity index 100% rename from docs/examples/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java rename to docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java diff --git a/docs/examples/src/test/resources/logback-test.xml b/docs/examples/junit4/redis/src/test/resources/logback-test.xml similarity index 100% rename from docs/examples/src/test/resources/logback-test.xml rename to docs/examples/junit4/redis/src/test/resources/logback-test.xml diff --git a/docs/examples/junit5/redis/build.gradle b/docs/examples/junit5/redis/build.gradle new file mode 100644 index 00000000000..56442b66af3 --- /dev/null +++ b/docs/examples/junit5/redis/build.gradle @@ -0,0 +1,11 @@ +description = "Examples for docs" + +dependencies { + compile "io.lettuce:lettuce-core:5.1.1.RELEASE" + + testCompile "org.junit.jupiter:junit-jupiter-api:5.4.2" + testCompile "org.junit.jupiter:junit-jupiter-params:5.4.2" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.4.2" + testCompile project(":testcontainers") + testCompile project(":junit-jupiter") +} diff --git a/examples/spock/src/main/java/quickstart/RedisBackedCache.java b/docs/examples/junit5/redis/src/main/java/quickstart/RedisBackedCache.java similarity index 100% rename from examples/spock/src/main/java/quickstart/RedisBackedCache.java rename to docs/examples/junit5/redis/src/main/java/quickstart/RedisBackedCache.java diff --git a/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java b/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java new file mode 100644 index 00000000000..7c3968a724e --- /dev/null +++ b/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java @@ -0,0 +1,42 @@ +package quickstart; + + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +// class { +@Testcontainers +public class RedisBackedCacheIntTest { + + private RedisBackedCache underTest; + + // container { + @Container + public GenericContainer redis = new GenericContainer<>("redis:5.0.3-alpine") + .withExposedPorts(6379); + // } + + + @BeforeEach + public void setUp() { + String address = redis.getContainerIpAddress(); + Integer port = redis.getFirstMappedPort(); + + // Now we have an address and port for Redis, no matter where it is running + underTest = new RedisBackedCache(address, port); + } + + @Test + public void testSimplePutAndGet() { + underTest.put("test", "example"); + + String retrieved = underTest.get("test"); + assertEquals("example", retrieved); + } +} +// } diff --git a/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java b/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java new file mode 100644 index 00000000000..c81a185a188 --- /dev/null +++ b/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java @@ -0,0 +1,27 @@ +package quickstart; + + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@Disabled("This test class is deliberately invalid, as it relies on a non-existent local Redis") +public class RedisBackedCacheIntTestStep0 { + private RedisBackedCache underTest; + + @BeforeEach + public void setUp() { + // Assume that we have Redis running locally? + underTest = new RedisBackedCache("localhost", 6379); + } + + @Test + public void testSimplePutAndGet() { + underTest.put("test", "example"); + + String retrieved = underTest.get("test"); + assertEquals("example", retrieved); + } +} diff --git a/docs/examples/junit5/redis/src/test/resources/logback-test.xml b/docs/examples/junit5/redis/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..49da0be9709 --- /dev/null +++ b/docs/examples/junit5/redis/src/test/resources/logback-test.xml @@ -0,0 +1,27 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + + + + + + + + + PROFILER + DENY + + diff --git a/examples/spock/build.gradle b/docs/examples/spock/redis/build.gradle similarity index 74% rename from examples/spock/build.gradle rename to docs/examples/spock/redis/build.gradle index 486107f9b21..398a6d3fa1c 100644 --- a/examples/spock/build.gradle +++ b/docs/examples/spock/redis/build.gradle @@ -3,13 +3,9 @@ plugins { id 'groovy' } -repositories { - jcenter() -} - dependencies { compile "io.lettuce:lettuce-core:5.2.0.RELEASE" testImplementation 'org.spockframework:spock-core:1.2-groovy-2.5' - testImplementation 'org.testcontainers:spock' + testImplementation project(":spock") testImplementation 'ch.qos.logback:logback-classic:1.2.3' } diff --git a/docs/examples/spock/redis/src/main/java/quickstart/RedisBackedCache.java b/docs/examples/spock/redis/src/main/java/quickstart/RedisBackedCache.java new file mode 100644 index 00000000000..1123858ca17 --- /dev/null +++ b/docs/examples/spock/redis/src/main/java/quickstart/RedisBackedCache.java @@ -0,0 +1,22 @@ +package quickstart; + +import io.lettuce.core.RedisClient; +import io.lettuce.core.api.StatefulRedisConnection; + +public class RedisBackedCache { + + private final StatefulRedisConnection connection; + + public RedisBackedCache(String hostname, Integer port) { + RedisClient client = RedisClient.create(String.format("redis://%s:%d/0", hostname, port)); + connection = client.connect(); + } + + public String get(String key) { + return connection.sync().get(key); + } + + public void put(String key, String value) { + connection.sync().set(key, value); + } +} diff --git a/examples/spock/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy b/docs/examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy similarity index 100% rename from examples/spock/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy rename to docs/examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy diff --git a/examples/spock/src/test/groovy/quickstart/RedisBackedCacheIntTestStep0.groovy b/docs/examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTestStep0.groovy similarity index 100% rename from examples/spock/src/test/groovy/quickstart/RedisBackedCacheIntTestStep0.groovy rename to docs/examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTestStep0.groovy diff --git a/examples/spock/src/test/resources/logback-test.xml b/docs/examples/spock/redis/src/test/resources/logback-test.xml similarity index 100% rename from examples/spock/src/test/resources/logback-test.xml rename to docs/examples/spock/redis/src/test/resources/logback-test.xml diff --git a/docs/features/advanced_options.md b/docs/features/advanced_options.md index bedb7aee6b8..a774dbddc06 100644 --- a/docs/features/advanced_options.md +++ b/docs/features/advanced_options.md @@ -5,13 +5,13 @@ To add a custom label to the container, use `withLabel`: -[Adding a single label](../examples/src/test/java/generic/ContainerLabelTest.java) inside_block:single_label +[Adding a single label](../examples/junit4/generic/src/test/java/generic/ContainerLabelTest.java) inside_block:single_label Additionally, multiple labels may be applied together from a map: -[Adding multiple labels](../examples/src/test/java/generic/ContainerLabelTest.java) inside_block:multiple_labels +[Adding multiple labels](../examples/junit4/generic/src/test/java/generic/ContainerLabelTest.java) inside_block:multiple_labels ## Image Pull Policy @@ -38,13 +38,13 @@ It is possible to use the [`docker-java`](https://github.com/docker-java/docker- For example, this can be used to change the container hostname: -[Using modifier to change hostname](../examples/src/test/java/generic/CmdModifierTest.java) inside_block:hostname +[Using modifier to change hostname](../examples/junit4/generic/src/test/java/generic/CmdModifierTest.java) inside_block:hostname ... or modify container memory (see [this](https://fabiokung.com/2014/03/13/memory-inside-linux-containers/) if it does not appear to work): -[Using modifier to change memory limits](../examples/src/test/java/generic/CmdModifierTest.java) inside_block:memory +[Using modifier to change memory limits](../examples/junit4/generic/src/test/java/generic/CmdModifierTest.java) inside_block:memory !!! note diff --git a/docs/features/commands.md b/docs/features/commands.md index 197871ebe02..40bcce5d1ab 100644 --- a/docs/features/commands.md +++ b/docs/features/commands.md @@ -5,7 +5,7 @@ By default the container will execute whatever command is specified in the image's Dockerfile. To override this, and specify a different command, use `withCommand`. For example: -[Specifying a startup command](../examples/src/test/java/generic/CommandsTest.java) inside_block:startupCommand +[Specifying a startup command](../examples/junit4/generic/src/test/java/generic/CommandsTest.java) inside_block:startupCommand ## Executing a command @@ -13,13 +13,13 @@ By default the container will execute whatever command is specified in the image Your test can execute a command inside a running container, similar to a `docker exec` call: -[Executing a command inside a running container](../examples/src/test/java/generic/ExecTest.java) inside_block:standaloneExec +[Executing a command inside a running container](../examples/junit4/generic/src/test/java/generic/ExecTest.java) inside_block:standaloneExec This can be useful for software that has a command line administration tool. You can also get the output (stdout/stderr) and exit code from the command - for example: -[Executing a command inside a running container and reading the result](../examples/src/test/java/generic/ExecTest.java) inside_block:execReadingStdout +[Executing a command inside a running container and reading the result](../examples/junit4/generic/src/test/java/generic/ExecTest.java) inside_block:execReadingStdout ## Environment variables diff --git a/docs/features/creating_container.md b/docs/features/creating_container.md index 16413924197..e92f20e71cd 100644 --- a/docs/features/creating_container.md +++ b/docs/features/creating_container.md @@ -20,13 +20,13 @@ new GenericContainer("jboss/wildfly:9.0.1.Final") A generic container rule can be used with any public docker image; for example: -[Creating a Redis container (JUnit 4)](../examples/src/test/java/generic/ContainerCreationTest.java) inside_block:simple +[Creating a Redis container (JUnit 4)](../examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java) inside_block:simple Further options may be specified: -[Creating a container with more options (JUnit 4)](../examples/src/test/java/generic/ContainerCreationTest.java) inside_block:withOptions +[Creating a container with more options (JUnit 4)](../examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java) inside_block:withOptions These containers, as `@ClassRule`s, will be started before any tests in the class run, and will be destroyed after all diff --git a/docs/features/networking.md b/docs/features/networking.md index 75dd924d6bc..5de0c9dfa96 100644 --- a/docs/features/networking.md +++ b/docs/features/networking.md @@ -8,7 +8,7 @@ For example, you may be testing a class that needs to connect to a backend or da Generally, each required port needs to be explicitly exposed. For example, we can specify one or more ports as follows: -[Exposing ports](../examples/src/test/java/generic/MultiplePortsExposedTest.java) inside_block:rule +[Exposing ports](../examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java) inside_block:rule Note that this exposed port number is from the *perspective of the container*. @@ -20,7 +20,7 @@ Because there is this layer of indirection, it is necessary to ask Testcontainer This can be done using the `getMappedPort` method, which takes the original (container) port as an argument: -[Retrieving actual ports at runtime](../examples/src/test/java/generic/MultiplePortsExposedTest.java) inside_block:fetchPortsByNumber +[Retrieving actual ports at runtime](../examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java) inside_block:fetchPortsByNumber !!! warning @@ -30,7 +30,7 @@ This can be done using the `getMappedPort` method, which takes the original (con There is also a `getFirstMappedPort` method for convenience, for the fairly common scenario of a container that only exposes one port: -[Retrieving the first mapped port](../examples/src/test/java/generic/MultiplePortsExposedTest.java) inside_block:fetchFirstMappedPort +[Retrieving the first mapped port](../examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java) inside_block:fetchFirstMappedPort ## Getting the container IP address @@ -41,13 +41,13 @@ However, in some CI environments they may instead be reachable on a different ho As such, Testcontainers provides a convenience method to obtain an IP address on which the container should be reachable from the host machine. -[Getting the container IP address](../examples/src/test/java/generic/MultiplePortsExposedTest.java) inside_block:getContainerIpAddressOnly +[Getting the container IP address](../examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java) inside_block:getContainerIpAddressOnly It is normally advisable to use `getContainerIpAddress` and `getMappedPort` together when constructing addresses - for example: -[Getting the container IP address and mapped port](../examples/src/test/java/generic/MultiplePortsExposedTest.java) inside_block:getContainerIpAddressAndMappedPort +[Getting the container IP address and mapped port](../examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java) inside_block:getContainerIpAddressAndMappedPort ## Exposing host ports to the container @@ -61,7 +61,7 @@ In this example, assume that `localServerPort` is a port on our test host machin We need to tell Testcontainers to prepare to expose this port to containers: -[Exposing the host port](../examples/src/test/java/generic/HostPortExposedTest.java) inside_block:exposePort +[Exposing the host port](../examples/junit4/generic/src/test/java/generic/HostPortExposedTest.java) inside_block:exposePort !!! warning @@ -73,7 +73,7 @@ From a container's perspective, the hostname will be `host.testcontainers.intern For example, here we construct an HTTP URL for our local web application and tell a Selenium container to get a page from it: -[Accessing the exposed host port from a container](../examples/src/test/java/generic/HostPortExposedTest.java) inside_block:useHostExposedPort +[Accessing the exposed host port from a container](../examples/junit4/generic/src/test/java/generic/HostPortExposedTest.java) inside_block:useHostExposedPort diff --git a/docs/features/startup_and_waits.md b/docs/features/startup_and_waits.md index a7e277b9892..e1d30457e5c 100644 --- a/docs/features/startup_and_waits.md +++ b/docs/features/startup_and_waits.md @@ -14,7 +14,7 @@ Ordinarily Testcontainers will wait for up to 60 seconds for the container's fir This simple measure provides a basic check whether a container is ready for use. -[Waiting for the first exposed port to start listening](../examples/src/test/java/generic/WaitStrategiesTest.java) inside_block:waitForNetworkListening +[Waiting for the first exposed port to start listening](../examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java) inside_block:waitForNetworkListening If the default 60s timeout is not sufficient, it can be altered with the `withStartupTimeout()` method. @@ -28,24 +28,24 @@ You can choose to wait for an HTTP(S) endpoint to return a particular status cod #### Waiting for 200 OK -[](../examples/src/test/java/generic/WaitStrategiesTest.java) inside_block:waitForSimpleHttp +[](../examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java) inside_block:waitForSimpleHttp Variations on the HTTP wait strategy are supported, including: #### Waiting for multiple possible status codes -[](../examples/src/test/java/generic/WaitStrategiesTest.java) inside_block:waitForHttpWithMultipleStatusCodes +[](../examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java) inside_block:waitForHttpWithMultipleStatusCodes #### Waiting for a status code that matches a predicate -[Waiting for a status code that matches a predicate](../examples/src/test/java/generic/WaitStrategiesTest.java) inside_block:waitForHttpWithStatusCodePredicate +[Waiting for a status code that matches a predicate](../examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java) inside_block:waitForHttpWithStatusCodePredicate #### Using TLS -[](../examples/src/test/java/generic/WaitStrategiesTest.java) inside_block:waitForHttpWithTls +[](../examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java) inside_block:waitForHttpWithTls ### Healthcheck Wait strategy examples @@ -53,7 +53,7 @@ Variations on the HTTP wait strategy are supported, including: If the used image supports Docker's [Healthcheck](https://docs.docker.com/engine/reference/builder/#healthcheck) feature, you can directly leverage the `healthy` state of the container as your wait condition: -[](../examples/src/test/java/generic/WaitStrategiesTest.java) inside_block:healthcheckWait +[](../examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java) inside_block:healthcheckWait ### Log output Wait Strategy @@ -62,7 +62,7 @@ In some situations a container's log output is a simple way to determine if it i For example, we can wait for a `Ready' message in the container's logs as follows: -[](../examples/src/test/java/generic/WaitStrategiesTest.java) inside_block:logMessageWait +[](../examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java) inside_block:logMessageWait ### Other Wait Strategies diff --git a/docs/quickstart/junit_4_quickstart.md b/docs/quickstart/junit_4_quickstart.md index 93ca7e9a024..86cd720942b 100644 --- a/docs/quickstart/junit_4_quickstart.md +++ b/docs/quickstart/junit_4_quickstart.md @@ -8,7 +8,7 @@ In our imaginary program, there is a `RedisBackedCache` class which stores data You can see an example test that could have been written for it (without using Testcontainers): -[Pre-Testcontainers test code](../examples/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java) block:RedisBackedCacheIntTestStep0 +[Pre-Testcontainers test code](../examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java) block:RedisBackedCacheIntTestStep0 Notice that the existing test has a problem - it's relying on a local installation of Redis, which is a red flag for test reliability. @@ -39,7 +39,7 @@ testCompile "org.testcontainers:testcontainers:{{latest_version}}" Simply add the following to the body of our test class: -[JUnit 4 Rule](../examples/src/test/java/quickstart/RedisBackedCacheIntTest.java) inside_block:rule +[JUnit 4 Rule](../examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java) inside_block:rule The `@Rule` annotation tells JUnit to notify this field about various events in the test lifecycle. @@ -61,7 +61,7 @@ Testcontainers uses *randomized ports* for each container it starts, but makes i We can do this in our test `setUp` method, to set up our component under test: -[Obtaining a mapped port](../examples/src/test/java/quickstart/RedisBackedCacheIntTest.java) inside_block:setUp +[Obtaining a mapped port](../examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java) inside_block:setUp !!! tip @@ -77,6 +77,6 @@ That's it! Let's look at our complete test class to see how little we had to add to get up and running with Testcontainers: -[RedisBackedCacheIntTest](../examples/src/test/java/quickstart/RedisBackedCacheIntTest.java) block:RedisBackedCacheIntTest +[RedisBackedCacheIntTest](../examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java) block:RedisBackedCacheIntTest diff --git a/docs/quickstart/junit_5_quickstart.md b/docs/quickstart/junit_5_quickstart.md new file mode 100644 index 00000000000..5a07af24731 --- /dev/null +++ b/docs/quickstart/junit_5_quickstart.md @@ -0,0 +1,110 @@ +# JUnit 5 Quickstart + +It's easy to add Testcontainers to your project - let's walk through a quick example to see how. + +Let's imagine we have a simple program that has a dependency on Redis, and we want to add some tests for it. +In our imaginary program, there is a `RedisBackedCache` class which stores data in Redis. + +You can see an example test that could have been written for it (without using Testcontainers): + + +[Pre-Testcontainers test code](../examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java) block:RedisBackedCacheIntTestStep0 + + +Notice that the existing test has a problem - it's relying on a local installation of Redis, which is a red flag for test reliability. +This may work if we were sure that every developer and CI machine had Redis installed, but would fail otherwise. +We might also have problems if we attempted to run tests in parallel, such as state bleeding between tests, or port clashes. + +Let's start from here, and see how to improve the test with Testcontainers: + +## 1. Add Testcontainers as a test-scoped dependency + +First, add Testcontainers as a dependency as follows: + +```groovy tab='Gradle' +def junitJupiterVersion = '5.4.2' +testCompile "org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion" +testCompile "org.junit.jupiter:junit-jupiter-params:$junitJupiterVersion" +testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion" +testCompile "org.testcontainers:testcontainers:{{latest_version}}" +``` + +```xml tab='Maven' + + org.junit.jupiter + junit-jupiter-api + 5.4.2 + test + + + org.junit.jupiter + junit-jupiter-params + 5.4.2 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.4.2 + test + + + org.testcontainers + testcontainers + {{latest_version}} + test + + + org.testcontainers + junit-jupiter + {{latest_version}} + test + +``` + +## 2. Get Testcontainers to run a Redis container during our tests + +First, you'll need to annotate the test class with `@Testcontainers`. Furthermore, add the following to the body of our test class: + + +[JUnit 5 Rule](../examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java) inside_block:container + + +The `@Container` annotation tells JUnit to notify this field about various events in the test lifecycle. +In this case, our rule object is a Testcontainers `GenericContainer`, configured to use a specific Redis image from Docker Hub, and configured to expose a port. + +If we run our test as-is, then regardless of the actual test outcome, we'll see logs showing us that Testcontainers: + +* was activated before our test method ran +* discovered and quickly tested our local Docker setup +* pulled the image if necessary +* started a new container and waited for it to be ready +* shut down and deleted the container after the test + +## 3. Make sure our code can talk to the container + +Before Testcontainers, we might have hardcoded an address like `localhost:6379` into our tests. + +Testcontainers uses *randomized ports* for each container it starts, but makes it easy to obtain the actual port at runtime. +We can do this in our test `setUp` method, to set up our component under test: + + +[Obtaining a mapped port](../examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java) inside_block:setUp + + +!!! tip + Notice that we also ask Testcontainers for the container's actual address with `redis.getContainerIpAddress();`, + rather than hard-coding `localhost`. `localhost` may work in some environments but not others - for example it may + not work on your current or future CI environment. As such, **avoid hard-coding** the address, and use + `getContainerIpAddress()` instead. + +## 4. Run the tests! + +That's it! + +Let's look at our complete test class to see how little we had to add to get up and running with Testcontainers: + + +[RedisBackedCacheIntTest](../examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java) inside_block:class + + diff --git a/docs/quickstart/spock_quickstart.md b/docs/quickstart/spock_quickstart.md index 921956c875a..902f1a78c09 100644 --- a/docs/quickstart/spock_quickstart.md +++ b/docs/quickstart/spock_quickstart.md @@ -8,7 +8,7 @@ In our imaginary program, there is a `RedisBackedCache` class which stores data You can see an example test that could have been written for it (without using Testcontainers): -[Pre-Testcontainers test code](../../examples/spock/src/test/groovy/quickstart/RedisBackedCacheIntTestStep0.groovy) block:RedisBackedCacheIntTestStep0 +[Pre-Testcontainers test code](../examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTestStep0.groovy) block:RedisBackedCacheIntTestStep0 Notice that the existing test has a problem - it's relying on a local installation of Redis, which is a red flag for test reliability. @@ -46,7 +46,7 @@ class RedisBackedCacheIntTest extends Specification { And add the following field to the body of our test class: -[Spock Testcontainers init](../../examples/spock/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy) inside_block:init +[Spock Testcontainers init](../examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy) inside_block:init This tells Spock to start a Testcontainers `GenericContainer`, configured to use a specific Redis image from Docker Hub, and configured to expose a port. @@ -67,7 +67,7 @@ Testcontainers uses *randomized ports* for each container it starts, but makes i We can do this in our test `setup` method, to set up our component under test: -[Obtaining a mapped port](../../examples/spock/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy) inside_block:setup +[Obtaining a mapped port](../examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy) inside_block:setup !!! tip @@ -83,5 +83,5 @@ That's it! Let's look at our complete test class to see how little we had to add to get up and running with Testcontainers: -[RedisBackedCacheIntTest](../../examples/spock/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy) block:complete +[RedisBackedCacheIntTest](../examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy) block:complete diff --git a/mkdocs.yml b/mkdocs.yml index 5c78e884662..eaec3ae13a1 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -25,6 +25,7 @@ nav: - Home: index.md - Quickstart: - quickstart/junit_4_quickstart.md + - quickstart/junit_5_quickstart.md - quickstart/spock_quickstart.md - Features: - features/creating_container.md diff --git a/settings.gradle b/settings.gradle index e8fb1efe910..7e01821a058 100644 --- a/settings.gradle +++ b/settings.gradle @@ -45,7 +45,10 @@ file('modules').eachDir { dir -> project(":${dir.name}").projectDir = dir } -include "docs-examples" -project(":docs-examples").projectDir = file("docs/examples") +include ':docs:examples:junit4:generic' +include ':docs:examples:junit4:redis' +include ':docs:examples:junit5:redis' +include ':docs:examples:spock:redis' + include 'test-support' From 6b158463141e70ce7b9cf05c0998401fd6660adb Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 27 Nov 2019 20:08:23 +0000 Subject: [PATCH 1049/5615] Switch to publishing non-delomboked sources (#1788) * Fixes #762, #1966: Switch to publishing non-delomboked sources Delomboked sources are available under the 'sources-delomboked' classifier * Remove publication of delomboked sources JAR --- build.gradle | 12 +++++------- gradle/publishing.gradle | 1 + 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 7bbf71af9f9..67b3a3a6635 100644 --- a/build.gradle +++ b/build.gradle @@ -20,6 +20,11 @@ subprojects { compileJava.options.encoding = 'UTF-8' compileTestJava.options.encoding = 'UTF-8' + repositories { + jcenter() + mavenCentral() + } + lombok { version = '1.18.8' } @@ -36,18 +41,11 @@ subprojects { project.sourceSets.main.java.srcDirs.find { it.exists() } } - repositories { - jcenter() - mavenCentral() - } - // specific modules should be excluded from publication if ( ! ["test-support", "jdbc-test", "docs-examples"].contains(it.name) ) { apply from: "$rootDir/gradle/publishing.gradle" apply from: "$rootDir/gradle/bintray.gradle" - project.tasks.sourceJar.from(delombok) - publishing { publications { mavenJava(MavenPublication) { publication -> diff --git a/gradle/publishing.gradle b/gradle/publishing.gradle index 14c385144ba..3acdeb10c88 100644 --- a/gradle/publishing.gradle +++ b/gradle/publishing.gradle @@ -2,6 +2,7 @@ apply plugin: 'maven-publish' task sourceJar(type: Jar) { classifier = 'sources' + from sourceSets.main.allJava } task javadocJar(type: Jar, dependsOn: javadoc) { From 7b38847bc2de5ab7bc112ac1a89d63dff135af45 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 27 Nov 2019 20:10:49 +0000 Subject: [PATCH 1050/5615] =?UTF-8?q?Bump=20aws-java-sdk-s3=20from=201.11.?= =?UTF-8?q?680=20to=201.11.683=20in=20/modules/loca=E2=80=A6=20(#2103)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.11.680 to 1.11.683. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.680...1.11.683) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 770294621aa..819419c57ee 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.680' - testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.636' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.683' + testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.683' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.636' } From 96c8e051ba5630603cb772d4e5774e811b3ec936 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 27 Nov 2019 20:11:09 +0000 Subject: [PATCH 1051/5615] Bump mariadb-java-client from 2.5.1 to 2.5.2 in /modules/mariadb (#2098) Bumps [mariadb-java-client](https://github.com/MariaDB/mariadb-connector-j) from 2.5.1 to 2.5.2. - [Release notes](https://github.com/MariaDB/mariadb-connector-j/releases) - [Changelog](https://github.com/MariaDB/mariadb-connector-j/blob/master/CHANGELOG.md) - [Commits](https://github.com/MariaDB/mariadb-connector-j/compare/2.5.1...2.5.2) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 87462ce158d..541d3967680 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: JDBC :: MariaDB" dependencies { compile project(':jdbc') - testCompile 'org.mariadb.jdbc:mariadb-java-client:2.5.1' + testCompile 'org.mariadb.jdbc:mariadb-java-client:2.5.2' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.29' From 3880dd4a0c0c347cd47aaf612597893f2ca9f6c6 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 27 Nov 2019 21:11:27 +0100 Subject: [PATCH 1052/5615] Rework KafkaContainer (do not run a sub-process) (#2078) * Rework KafkaContainer (do not run a sub-process) * revert unrelated change * Escape environment variables * Start ZK before Kafka --- .../containers/KafkaContainer.java | 33 +++++++++++++------ .../containers/KafkaContainerTest.java | 7 ++-- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java index 4b5e66a7bf5..60037acfd45 100644 --- a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java +++ b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java @@ -4,9 +4,13 @@ import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.core.command.ExecStartResultCallback; import lombok.SneakyThrows; +import org.testcontainers.images.builder.Transferable; import org.testcontainers.utility.TestcontainersConfiguration; +import java.nio.charset.StandardCharsets; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * This container wraps Confluent Kafka and Zookeeper (optionally) @@ -14,6 +18,8 @@ */ public class KafkaContainer extends GenericContainer { + private static final String STARTER_SCRIPT = "/testcontainers_start.sh"; + public static final int KAFKA_PORT = 9093; public static final int ZOOKEEPER_PORT = 2181; @@ -97,7 +103,7 @@ public String getBootstrapServers() { @Override protected void doStart() { - withCommand("sleep infinity"); + withCommand("sh", "-c", "while [ ! -f " + STARTER_SCRIPT + " ]; do sleep 0.1; done; " + STARTER_SCRIPT); if (externalZookeeperConnect == null) { addExposedPort(ZOOKEEPER_PORT); @@ -124,17 +130,24 @@ protected void containerIsStarting(InspectContainerResponse containerInfo, boole zookeeperConnect = startZookeeper(); } - String internalIp = containerInfo.getNetworkSettings().getIpAddress(); - - ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(getContainerId()) - .withCmd("sh", "-c", "" + - "export KAFKA_ZOOKEEPER_CONNECT=" + zookeeperConnect + "\n" + - "export KAFKA_ADVERTISED_LISTENERS=" + getBootstrapServers() + "," + String.format("BROKER://%s:9092", internalIp) + "\n" + - "/etc/confluent/docker/run" + String command = "#!/bin/bash \n"; + command += "export KAFKA_ZOOKEEPER_CONNECT='" + zookeeperConnect + "'\n"; + command += "export KAFKA_ADVERTISED_LISTENERS='" + Stream + .concat( + Stream.of(getBootstrapServers()), + containerInfo.getNetworkSettings().getNetworks().values().stream() + .map(it -> "BROKER://" + it.getIpAddress() + ":9092") ) - .exec(); + .collect(Collectors.joining(",")) + "'\n"; - dockerClient.execStartCmd(execCreateCmdResponse.getId()).exec(new ExecStartResultCallback()).awaitStarted(10, TimeUnit.SECONDS); + command += ". /etc/confluent/docker/bash-config \n"; + command += "/etc/confluent/docker/configure \n"; + command += "/etc/confluent/docker/launch \n"; + + copyFileToContainer( + Transferable.of(command.getBytes(StandardCharsets.UTF_8), 700), + STARTER_SCRIPT + ); } @SneakyThrows(InterruptedException.class) diff --git a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java index 0b29ee0e6ef..add8b2b171d 100644 --- a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java +++ b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java @@ -16,7 +16,6 @@ import java.util.Arrays; import java.util.UUID; import java.util.concurrent.TimeUnit; -import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; @@ -46,7 +45,8 @@ public void testExternalZookeeperWithKafkaNetwork() throws Exception { .withNetworkAliases("zookeeper") .withEnv("ZOOKEEPER_CLIENT_PORT", "2181"); ) { - Stream.of(kafka, zookeeper).parallel().forEach(GenericContainer::start); + zookeeper.start(); + kafka.start(); testKafkaFunctionality(kafka.getBootstrapServers()); } @@ -66,7 +66,8 @@ public void testExternalZookeeperWithExternalNetwork() throws Exception { .withNetworkAliases("zookeeper") .withEnv("ZOOKEEPER_CLIENT_PORT", "2181"); ) { - Stream.of(kafka, zookeeper).parallel().forEach(GenericContainer::start); + zookeeper.start(); + kafka.start(); testKafkaFunctionality(kafka.getBootstrapServers()); } From 3cb33792328e49b5ab8d78f747a256d65593dd64 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 27 Nov 2019 21:48:04 +0100 Subject: [PATCH 1053/5615] Hash copied files (#2055) * Fix session label when reuse is not supported but requested * Hash copied files * restore import * add file mode to the checksum * Mock TestcontainersConfiguration * checksum folders too --- .../containers/GenericContainer.java | 46 +++- .../testcontainers/utility/MountableFile.java | 7 +- .../containers/ReusabilityUnitTests.java | 228 +++++++++++++++++- 3 files changed, 272 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 94ce6f64518..0062612c10d 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -29,6 +29,7 @@ import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.apache.commons.compress.utils.IOUtils; +import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -75,6 +76,7 @@ import java.lang.reflect.Method; import java.lang.reflect.UndeclaredThrowableException; import java.nio.charset.Charset; +import java.nio.file.Files; import java.nio.file.Path; import java.time.Duration; import java.time.Instant; @@ -87,6 +89,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Optional; import java.util.Set; import java.util.concurrent.ExecutionException; @@ -96,6 +99,8 @@ import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; +import java.util.zip.Adler32; +import java.util.zip.Checksum; import static com.google.common.collect.Lists.newArrayList; import static org.testcontainers.utility.CommandLine.runShellCommand; @@ -116,6 +121,8 @@ public class GenericContainer> static final String HASH_LABEL = "org.testcontainers.hash"; + static final String COPIED_FILES_HASH_LABEL = "org.testcontainers.copied_files.hash"; + /* * Default settings */ @@ -353,6 +360,11 @@ private void tryStart(Instant startedAt) { } if (TestcontainersConfiguration.getInstance().environmentSupportsReuse()) { + createCommand.getLabels().put( + COPIED_FILES_HASH_LABEL, + Long.toHexString(hashCopiedFiles().getValue()) + ); + String hash = hash(createCommand); containerId = findContainerForReuse(hash).orElse(null); @@ -381,7 +393,7 @@ private void tryStart(Instant startedAt) { if (!reused) { containerId = createCommand.exec().getId(); - // TODO add to the hash + // TODO use single "copy" invocation (and calculate an hash of the resulting tar archive) copyToFileContainerPathMap.forEach(this::copyFileToContainer); } @@ -474,6 +486,36 @@ private void tryStart(Instant startedAt) { } } + @VisibleForTesting + Checksum hashCopiedFiles() { + Checksum checksum = new Adler32(); + copyToFileContainerPathMap.entrySet().stream().sorted(Entry.comparingByValue()).forEach(entry -> { + byte[] pathBytes = entry.getValue().getBytes(); + // Add path to the hash + checksum.update(pathBytes, 0, pathBytes.length); + + File file = new File(entry.getKey().getResolvedPath()); + checksumFile(file, checksum); + }); + return checksum; + } + + @VisibleForTesting + @SneakyThrows(IOException.class) + void checksumFile(File file, Checksum checksum) { + Path path = file.toPath(); + checksum.update(MountableFile.getUnixFileMode(path)); + if (file.isDirectory()) { + try (Stream stream = Files.walk(path)) { + stream.filter(it -> it != path).forEach(it -> { + checksumFile(it.toFile(), checksum); + }); + } + } else { + FileUtils.checksum(file, checksum); + } + } + @UnstableAPI @SneakyThrows(JsonProcessingException.class) final String hash(CreateContainerCmd createCommand) { @@ -708,7 +750,7 @@ private void applyConfiguration(CreateContainerCmd createCommand) { Set allLinks = new HashSet<>(); Set allLinkedContainerNetworks = new HashSet<>(); - for (Map.Entry linkEntries : linkedContainers.entrySet()) { + for (Entry linkEntries : linkedContainers.entrySet()) { String alias = linkEntries.getKey(); LinkableContainer linkableContainer = linkEntries.getValue(); diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java index 56d5bda3ffa..37312116734 100644 --- a/core/src/main/java/org/testcontainers/utility/MountableFile.java +++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java @@ -9,6 +9,7 @@ import org.apache.commons.lang.SystemUtils; import org.jetbrains.annotations.NotNull; import org.testcontainers.DockerClientFactory; +import org.testcontainers.UnstableAPI; import org.testcontainers.images.builder.Transferable; import java.io.File; @@ -361,9 +362,13 @@ private int getUnixFileMode(final String pathAsString) { if (this.forcedFileMode != null) { return this.getModeValue(path); } + return getUnixFileMode(path); + } + @UnstableAPI + public static int getUnixFileMode(final Path path) { try { - return (int) Files.getAttribute(path, "unix:mode"); + return (int) Files.readAttributes(path, "unix:mode").get("mode"); } catch (IOException | UnsupportedOperationException e) { // fallback for non-posix environments int mode = DEFAULT_FILE_MODE; diff --git a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java index 3f7c7d75b45..096f202b3d7 100644 --- a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java +++ b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java @@ -9,6 +9,7 @@ import com.github.dockerjava.core.command.InspectContainerCmdImpl; import com.github.dockerjava.core.command.ListContainersCmdImpl; import com.github.dockerjava.core.command.StartContainerCmdImpl; +import com.google.common.collect.Sets; import lombok.RequiredArgsConstructor; import lombok.experimental.FieldDefaults; import org.junit.Rule; @@ -24,11 +25,20 @@ import org.testcontainers.containers.startupcheck.StartupCheckStrategy; import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy; import org.testcontainers.utility.MockTestcontainersConfigurationRule; +import org.testcontainers.utility.MountableFile; import org.testcontainers.utility.TestcontainersConfiguration; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.PosixFilePermissions; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; @@ -182,7 +192,12 @@ public void shouldNotCallHookIfNotReused() { @FieldDefaults(makeFinal = true) public static class HashTest extends AbstractReusabilityTest { - protected GenericContainer container = makeReusable(new GenericContainer<>(IMAGE_FUTURE)); + protected GenericContainer container = makeReusable(new GenericContainer(IMAGE_FUTURE) { + @Override + public void copyFileToContainer(MountableFile mountableFile, String containerPath) { + // NOOP + } + }); @Test public void shouldStartIfListReturnsEmpty() { @@ -230,6 +245,208 @@ public void shouldSetLabelsIfEnvironmentDoesNotSupportReuse() { .containsKeys(DockerClientFactory.TESTCONTAINERS_SESSION_ID_LABEL); }); } + + @Test + public void shouldSetCopiedFilesHashLabel() { + Mockito.doReturn(true).when(TestcontainersConfiguration.getInstance()).environmentSupportsReuse(); + AtomicReference commandRef = new AtomicReference<>(); + String containerId = randomContainerId(); + when(client.createContainerCmd(any())).then(createContainerAnswer(containerId, commandRef::set)); + when(client.listContainersCmd()).then(listContainersAnswer()); + when(client.startContainerCmd(containerId)).then(startContainerAnswer()); + when(client.inspectContainerCmd(containerId)).then(inspectContainerAnswer()); + + container.start(); + + assertThat(commandRef).isNotNull(); + assertThat(commandRef.get().getLabels()) + .containsKeys(GenericContainer.COPIED_FILES_HASH_LABEL); + } + + @Test + public void shouldHashCopiedFiles() { + Mockito.doReturn(true).when(TestcontainersConfiguration.getInstance()).environmentSupportsReuse(); + AtomicReference commandRef = new AtomicReference<>(); + String containerId = randomContainerId(); + when(client.createContainerCmd(any())).then(createContainerAnswer(containerId, commandRef::set)); + when(client.listContainersCmd()).then(listContainersAnswer()); + when(client.startContainerCmd(containerId)).then(startContainerAnswer()); + when(client.inspectContainerCmd(containerId)).then(inspectContainerAnswer()); + + container.start(); + + assertThat(commandRef).isNotNull(); + + Map labels = commandRef.get().getLabels(); + assertThat(labels).containsKeys(GenericContainer.COPIED_FILES_HASH_LABEL); + + String oldHash = labels.get(GenericContainer.COPIED_FILES_HASH_LABEL); + + // Simulate stop + container.containerId = null; + + container.withCopyFileToContainer( + MountableFile.forClasspathResource("test_copy_to_container.txt"), + "/foo/bar" + ); + container.start(); + + assertThat(commandRef.get().getLabels()).hasEntrySatisfying(GenericContainer.COPIED_FILES_HASH_LABEL, newHash -> { + assertThat(newHash).as("new hash").isNotEqualTo(oldHash); + }); + } + } + + @RunWith(BlockJUnit4ClassRunner.class) + @FieldDefaults(makeFinal = true) + public static class CopyFilesHashTest { + GenericContainer container = new GenericContainer(IMAGE_FUTURE); + + @Test + public void empty() { + assertThat(container.hashCopiedFiles()).isNotNull(); + } + + @Test + public void oneFile() { + long emptyHash = container.hashCopiedFiles().getValue(); + + container.withCopyFileToContainer( + MountableFile.forClasspathResource("test_copy_to_container.txt"), + "/foo/bar" + ); + + assertThat(container.hashCopiedFiles().getValue()).isNotEqualTo(emptyHash); + } + + @Test + public void differentPath() { + MountableFile mountableFile = MountableFile.forClasspathResource("test_copy_to_container.txt"); + container.withCopyFileToContainer(mountableFile, "/foo/bar"); + + long hash1 = container.hashCopiedFiles().getValue(); + + container.getCopyToFileContainerPathMap().clear(); + + container.withCopyFileToContainer(mountableFile, "/foo/baz"); + + assertThat(container.hashCopiedFiles().getValue()).isNotEqualTo(hash1); + } + + @Test + public void detectsChangesInFile() throws Exception { + Path path = File.createTempFile("reusable_test", ".txt").toPath(); + MountableFile mountableFile = MountableFile.forHostPath(path); + container.withCopyFileToContainer(mountableFile, "/foo/bar"); + + long hash1 = container.hashCopiedFiles().getValue(); + + Files.write(path, UUID.randomUUID().toString().getBytes()); + + assertThat(container.hashCopiedFiles().getValue()).isNotEqualTo(hash1); + } + + @Test + public void multipleFiles() { + container.withCopyFileToContainer( + MountableFile.forClasspathResource("test_copy_to_container.txt"), + "/foo/bar" + ); + long hash1 = container.hashCopiedFiles().getValue(); + + container.withCopyFileToContainer( + MountableFile.forClasspathResource("mappable-resource/test-resource.txt"), + "/foo/baz" + ); + + assertThat(container.hashCopiedFiles().getValue()).isNotEqualTo(hash1); + } + + @Test + public void folder() throws Exception { + long emptyHash = container.hashCopiedFiles().getValue(); + + Path tempDirectory = Files.createTempDirectory("reusable_test"); + MountableFile mountableFile = MountableFile.forHostPath(tempDirectory); + container.withCopyFileToContainer(mountableFile, "/foo/bar/"); + + assertThat(container.hashCopiedFiles().getValue()).isNotEqualTo(emptyHash); + } + + @Test + public void changesInFolder() throws Exception { + Path tempDirectory = Files.createTempDirectory("reusable_test"); + MountableFile mountableFile = MountableFile.forHostPath(tempDirectory); + assertThat(new File(mountableFile.getResolvedPath())).isDirectory(); + container.withCopyFileToContainer(mountableFile, "/foo/bar/"); + + long hash1 = container.hashCopiedFiles().getValue(); + + Path fileInFolder = Files.createFile( + // Create file in the sub-folder + Files.createDirectory(tempDirectory.resolve("sub")).resolve("test.txt") + ); + assertThat(fileInFolder).exists(); + Files.write(fileInFolder, UUID.randomUUID().toString().getBytes()); + + assertThat(container.hashCopiedFiles().getValue()).isNotEqualTo(hash1); + } + + @Test + public void folderAndFile() throws Exception { + Path tempDirectory = Files.createTempDirectory("reusable_test"); + MountableFile mountableFile = MountableFile.forHostPath(tempDirectory); + assertThat(new File(mountableFile.getResolvedPath())).isDirectory(); + container.withCopyFileToContainer(mountableFile, "/foo/bar/"); + + long hash1 = container.hashCopiedFiles().getValue(); + + container.withCopyFileToContainer( + MountableFile.forClasspathResource("test_copy_to_container.txt"), + "/foo/baz" + ); + + assertThat(container.hashCopiedFiles().getValue()).isNotEqualTo(hash1); + } + + @Test + public void filePermissions() throws Exception { + Path path = File.createTempFile("reusable_test", ".txt").toPath(); + MountableFile mountableFile = MountableFile.forHostPath(path); + container.withCopyFileToContainer(mountableFile, "/foo/bar"); + + long hash1 = container.hashCopiedFiles().getValue(); + + Set filePermissions = new HashSet<>(Files.getPosixFilePermissions(path)); + assertThat(filePermissions).doesNotContain(PosixFilePermission.OWNER_EXECUTE); + filePermissions.add(PosixFilePermission.OWNER_EXECUTE); + Files.setPosixFilePermissions(path, filePermissions); + + assertThat(container.hashCopiedFiles().getValue()).isNotEqualTo(hash1); + } + + @Test + public void folderPermissions() throws Exception { + Path tempDirectory = Files.createTempDirectory("reusable_test"); + MountableFile mountableFile = MountableFile.forHostPath(tempDirectory); + assertThat(new File(mountableFile.getResolvedPath())).isDirectory(); + Path subDir = Files.createDirectory( + tempDirectory.resolve("sub"), + PosixFilePermissions.asFileAttribute( + Sets.newHashSet(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE) + ) + ); + container.withCopyFileToContainer(mountableFile, "/foo/bar/"); + + long hash1 = container.hashCopiedFiles().getValue(); + + Set permissions = new HashSet<>(Files.getPosixFilePermissions(subDir)); + assertThat(permissions).doesNotContain(PosixFilePermission.OWNER_EXECUTE); + permissions.add(PosixFilePermission.OWNER_EXECUTE); + Files.setPosixFilePermissions(subDir, permissions); + + assertThat(container.hashCopiedFiles().getValue()).isNotEqualTo(hash1); + } } @FieldDefaults(makeFinal = true) @@ -257,11 +474,11 @@ public StartupStatus checkStartupState(DockerClient dockerClient, String contain } }); container.waitingFor(new AbstractWaitStrategy() { - @Override - protected void waitUntilReady() { + @Override + protected void waitUntilReady() { - } - }); + } + }); container.withReuse(true); return container; } @@ -317,6 +534,5 @@ protected Answer inspectContainerAnswer() { return new InspectContainerCmdImpl(exec, invocation.getArgument(0)); }; } - } } From af0a6803666cfbeb15fd1db0b50243c658969860 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 28 Nov 2019 09:14:13 +0100 Subject: [PATCH 1054/5615] Use `File#setExecutable` --- .../containers/ReusabilityUnitTests.java | 24 ++++--------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java index 096f202b3d7..5ecee409ead 100644 --- a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java +++ b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java @@ -9,7 +9,6 @@ import com.github.dockerjava.core.command.InspectContainerCmdImpl; import com.github.dockerjava.core.command.ListContainersCmdImpl; import com.github.dockerjava.core.command.StartContainerCmdImpl; -import com.google.common.collect.Sets; import lombok.RequiredArgsConstructor; import lombok.experimental.FieldDefaults; import org.junit.Rule; @@ -31,14 +30,10 @@ import java.io.File; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.attribute.PosixFilePermission; -import java.nio.file.attribute.PosixFilePermissions; import java.util.ArrayList; import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; @@ -417,10 +412,8 @@ public void filePermissions() throws Exception { long hash1 = container.hashCopiedFiles().getValue(); - Set filePermissions = new HashSet<>(Files.getPosixFilePermissions(path)); - assertThat(filePermissions).doesNotContain(PosixFilePermission.OWNER_EXECUTE); - filePermissions.add(PosixFilePermission.OWNER_EXECUTE); - Files.setPosixFilePermissions(path, filePermissions); + assertThat(path.toFile().canExecute()).isFalse(); + path.toFile().setExecutable(true); assertThat(container.hashCopiedFiles().getValue()).isNotEqualTo(hash1); } @@ -430,20 +423,13 @@ public void folderPermissions() throws Exception { Path tempDirectory = Files.createTempDirectory("reusable_test"); MountableFile mountableFile = MountableFile.forHostPath(tempDirectory); assertThat(new File(mountableFile.getResolvedPath())).isDirectory(); - Path subDir = Files.createDirectory( - tempDirectory.resolve("sub"), - PosixFilePermissions.asFileAttribute( - Sets.newHashSet(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE) - ) - ); + Path subDir = Files.createDirectory(tempDirectory.resolve("sub")); + subDir.toFile().setExecutable(false); container.withCopyFileToContainer(mountableFile, "/foo/bar/"); long hash1 = container.hashCopiedFiles().getValue(); - Set permissions = new HashSet<>(Files.getPosixFilePermissions(subDir)); - assertThat(permissions).doesNotContain(PosixFilePermission.OWNER_EXECUTE); - permissions.add(PosixFilePermission.OWNER_EXECUTE); - Files.setPosixFilePermissions(subDir, permissions); + subDir.toFile().setExecutable(true); assertThat(container.hashCopiedFiles().getValue()).isNotEqualTo(hash1); } From f9ed1416a279ad2aa3c119f682621e2f931785bb Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 28 Nov 2019 09:29:36 +0100 Subject: [PATCH 1055/5615] Use `setWritable` for folder, `setExecutable(false)` in `filePermissions` --- .../org/testcontainers/containers/ReusabilityUnitTests.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java index 5ecee409ead..3414ae1a412 100644 --- a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java +++ b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java @@ -407,6 +407,7 @@ public void folderAndFile() throws Exception { @Test public void filePermissions() throws Exception { Path path = File.createTempFile("reusable_test", ".txt").toPath(); + path.toFile().setExecutable(false); MountableFile mountableFile = MountableFile.forHostPath(path); container.withCopyFileToContainer(mountableFile, "/foo/bar"); @@ -424,12 +425,12 @@ public void folderPermissions() throws Exception { MountableFile mountableFile = MountableFile.forHostPath(tempDirectory); assertThat(new File(mountableFile.getResolvedPath())).isDirectory(); Path subDir = Files.createDirectory(tempDirectory.resolve("sub")); - subDir.toFile().setExecutable(false); + subDir.toFile().setWritable(false); container.withCopyFileToContainer(mountableFile, "/foo/bar/"); long hash1 = container.hashCopiedFiles().getValue(); - subDir.toFile().setExecutable(true); + subDir.toFile().setWritable(true); assertThat(container.hashCopiedFiles().getValue()).isNotEqualTo(hash1); } From 3826190f2ac57117bd6b36d807f7006076de5f5a Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 28 Nov 2019 09:41:17 +0100 Subject: [PATCH 1056/5615] assume file permissions --- .../org/testcontainers/containers/ReusabilityUnitTests.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java index 3414ae1a412..35c4a88e68a 100644 --- a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java +++ b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java @@ -42,6 +42,7 @@ import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assumptions.assumeThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; @@ -413,7 +414,7 @@ public void filePermissions() throws Exception { long hash1 = container.hashCopiedFiles().getValue(); - assertThat(path.toFile().canExecute()).isFalse(); + assumeThat(path.toFile().canExecute()).isFalse(); path.toFile().setExecutable(true); assertThat(container.hashCopiedFiles().getValue()).isNotEqualTo(hash1); @@ -423,7 +424,7 @@ public void filePermissions() throws Exception { public void folderPermissions() throws Exception { Path tempDirectory = Files.createTempDirectory("reusable_test"); MountableFile mountableFile = MountableFile.forHostPath(tempDirectory); - assertThat(new File(mountableFile.getResolvedPath())).isDirectory(); + assumeThat(new File(mountableFile.getResolvedPath())).isDirectory(); Path subDir = Files.createDirectory(tempDirectory.resolve("sub")); subDir.toFile().setWritable(false); container.withCopyFileToContainer(mountableFile, "/foo/bar/"); From d44debdaf173571be35928b2d5d22c713a68925e Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 28 Nov 2019 10:46:44 +0100 Subject: [PATCH 1057/5615] fix the assumptions --- .../org/testcontainers/containers/ReusabilityUnitTests.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java index 35c4a88e68a..b2d4d327ba2 100644 --- a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java +++ b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java @@ -424,14 +424,16 @@ public void filePermissions() throws Exception { public void folderPermissions() throws Exception { Path tempDirectory = Files.createTempDirectory("reusable_test"); MountableFile mountableFile = MountableFile.forHostPath(tempDirectory); - assumeThat(new File(mountableFile.getResolvedPath())).isDirectory(); + assertThat(new File(mountableFile.getResolvedPath())).isDirectory(); Path subDir = Files.createDirectory(tempDirectory.resolve("sub")); subDir.toFile().setWritable(false); + assumeThat(subDir.toFile().canWrite()).isFalse(); container.withCopyFileToContainer(mountableFile, "/foo/bar/"); long hash1 = container.hashCopiedFiles().getValue(); subDir.toFile().setWritable(true); + assumeThat(subDir.toFile()).canWrite(); assertThat(container.hashCopiedFiles().getValue()).isNotEqualTo(hash1); } From cd7fce7e85b06d75eb675f9cbb8f5ffb615e043e Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 28 Nov 2019 13:30:42 +0100 Subject: [PATCH 1058/5615] Add GHA for Release Drafter (#2109) Currently we have Release Drafter integrated as a GitHub App. If we change it to GHA (GitHub Actions), we will be able to re-trigger it and overall have better control of it (e.g. version) --- .github/workflows/release-drafter.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/workflows/release-drafter.yml diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 00000000000..6f4e849bd5b --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,14 @@ +name: Release Drafter + +on: + push: + branches: + - master + +jobs: + update_release_draft: + runs-on: ubuntu-latest + steps: + - uses: release-drafter/release-drafter@v5.2.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 50ff2c514e460290af4ac5c5da89f97f001d9e80 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 28 Nov 2019 18:04:41 +0000 Subject: [PATCH 1059/5615] Basic GitHub Action for release (#1991) * Basic GitHub Action for release * Update following review * Update .github/workflows/gradle.yml Co-Authored-By: Sergei Egorov * Remove explicit check stage (release runs without testing - should be manually verified that master branch is stable) * Add simple shell check for whether master branch is passing CI checks * Strip quotes * Update .github/workflows/gradle-release.yml Co-Authored-By: Sergei Egorov --- .github/workflows/gradle-release.yml | 22 ++++++++++++++++++++++ .travis.yml | 11 ----------- 2 files changed, 22 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/gradle-release.yml diff --git a/.github/workflows/gradle-release.yml b/.github/workflows/gradle-release.yml new file mode 100644 index 00000000000..ca823536fcb --- /dev/null +++ b/.github/workflows/gradle-release.yml @@ -0,0 +1,22 @@ +name: Gradle Release + +on: + release: + types: [published] + +jobs: + build: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v1 + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + - name: Verify CI check status + run: [ $(curl -s https://api.github.com/repos/testcontainers/testcontainers-java/commits/$GITHUB_SHA/status | jq -r '.state') == 'success' ] + - name: Release with Gradle + run: ./gradlew -Pversion=$GITHUB_REF release --scan --no-daemon -i + env: + BINTRAY_USER: ${{ secrets.BINTRAY_USER }} + BINTRAY_API_KEY: ${{ secrets.BINTRAY_API_KEY }} diff --git a/.travis.yml b/.travis.yml index eda26dc69af..6e07925db3f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,14 +58,3 @@ jobs: openjdk:8-jdk-alpine \ ./gradlew --no-daemon testcontainers:test --tests '*GenericContainerRuleTest' --scan -i - - stage: deploy - sudo: false - services: [] - install: skip - script: skip - deploy: - provider: script - script: ./gradlew -Pversion=$TRAVIS_TAG release --scan --no-daemon -i - on: - tags: true - branch: master From 8ed2cfadf1fa0ddc3f806f701c1c5e747e3a2356 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 28 Nov 2019 20:51:14 +0000 Subject: [PATCH 1060/5615] Escape verify command --- .github/workflows/gradle-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gradle-release.yml b/.github/workflows/gradle-release.yml index ca823536fcb..f8feb0cccde 100644 --- a/.github/workflows/gradle-release.yml +++ b/.github/workflows/gradle-release.yml @@ -14,7 +14,7 @@ jobs: with: java-version: 1.8 - name: Verify CI check status - run: [ $(curl -s https://api.github.com/repos/testcontainers/testcontainers-java/commits/$GITHUB_SHA/status | jq -r '.state') == 'success' ] + run: "[ $(curl -s https://api.github.com/repos/testcontainers/testcontainers-java/commits/$GITHUB_SHA/status | jq -r '.state') == 'success' ]" - name: Release with Gradle run: ./gradlew -Pversion=$GITHUB_REF release --scan --no-daemon -i env: From 7336e4a3b44c4def9967da775e739d52374e9e40 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 29 Nov 2019 09:48:20 +0100 Subject: [PATCH 1061/5615] Extract the CI status check into a script (#2113) --- .github/workflows/gradle-release.yml | 2 +- .github/workflows/scripts/check_ci_status.sh | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100755 .github/workflows/scripts/check_ci_status.sh diff --git a/.github/workflows/gradle-release.yml b/.github/workflows/gradle-release.yml index f8feb0cccde..a1bce2d9278 100644 --- a/.github/workflows/gradle-release.yml +++ b/.github/workflows/gradle-release.yml @@ -14,7 +14,7 @@ jobs: with: java-version: 1.8 - name: Verify CI check status - run: "[ $(curl -s https://api.github.com/repos/testcontainers/testcontainers-java/commits/$GITHUB_SHA/status | jq -r '.state') == 'success' ]" + run: .github/workflows/scripts/check_ci_status.sh - name: Release with Gradle run: ./gradlew -Pversion=$GITHUB_REF release --scan --no-daemon -i env: diff --git a/.github/workflows/scripts/check_ci_status.sh b/.github/workflows/scripts/check_ci_status.sh new file mode 100755 index 00000000000..ee2719cbb49 --- /dev/null +++ b/.github/workflows/scripts/check_ci_status.sh @@ -0,0 +1,3 @@ +#/bin/bash + +[ $(curl -s https://api.github.com/repos/testcontainers/testcontainers-java/commits/$1/status | jq -r '.state') == 'success' ] From ad58df04eea922d0e3e4126fb2a45483c5d9ebba Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 29 Nov 2019 10:30:57 +0100 Subject: [PATCH 1062/5615] add missing argument --- .github/workflows/gradle-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gradle-release.yml b/.github/workflows/gradle-release.yml index a1bce2d9278..8c261064d33 100644 --- a/.github/workflows/gradle-release.yml +++ b/.github/workflows/gradle-release.yml @@ -14,7 +14,7 @@ jobs: with: java-version: 1.8 - name: Verify CI check status - run: .github/workflows/scripts/check_ci_status.sh + run: .github/workflows/scripts/check_ci_status.sh $GITHUB_SHA - name: Release with Gradle run: ./gradlew -Pversion=$GITHUB_REF release --scan --no-daemon -i env: From 7f9ff7951d89c8a2ac50fdef867f6d3f345a53b7 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 29 Nov 2019 10:40:26 +0100 Subject: [PATCH 1063/5615] future proof check_ci_status.sh --- .github/workflows/scripts/check_ci_status.sh | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/scripts/check_ci_status.sh b/.github/workflows/scripts/check_ci_status.sh index ee2719cbb49..bc5c0b138a2 100755 --- a/.github/workflows/scripts/check_ci_status.sh +++ b/.github/workflows/scripts/check_ci_status.sh @@ -1,3 +1,13 @@ #/bin/bash +set -o errexit +set -o nounset +set -o pipefail +set -o xtrace -[ $(curl -s https://api.github.com/repos/testcontainers/testcontainers-java/commits/$1/status | jq -r '.state') == 'success' ] +if [ -z $1 ] ; then + echo "First parameter (commit SHA) is required!" && exit 1; +fi + +STATUS=$(curl -s https://api.github.com/repos/testcontainers/testcontainers-java/commits/$1/status | jq -r '.state') + +[ "$STATUS" == 'success' ] From 5c4ccedb03589a8fd6484440d75695d7e765b5b1 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 29 Nov 2019 11:42:09 +0100 Subject: [PATCH 1064/5615] Update gradle-release.yml strip `/ref/tags/` from `$GITHUB_REF` temporary remove the master check --- .github/workflows/gradle-release.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/gradle-release.yml b/.github/workflows/gradle-release.yml index 8c261064d33..1391561284e 100644 --- a/.github/workflows/gradle-release.yml +++ b/.github/workflows/gradle-release.yml @@ -13,10 +13,8 @@ jobs: uses: actions/setup-java@v1 with: java-version: 1.8 - - name: Verify CI check status - run: .github/workflows/scripts/check_ci_status.sh $GITHUB_SHA - name: Release with Gradle - run: ./gradlew -Pversion=$GITHUB_REF release --scan --no-daemon -i + run: ./gradlew -Pversion=${GITHUB_REF##*/} release --scan --no-daemon -i env: BINTRAY_USER: ${{ secrets.BINTRAY_USER }} BINTRAY_API_KEY: ${{ secrets.BINTRAY_API_KEY }} From 31519e3c1f1a65f6e47eebb27f0b97521535a5ea Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 29 Nov 2019 11:59:47 +0100 Subject: [PATCH 1065/5615] Exclude `:docs:` from publication and BOM --- bom/build.gradle | 2 +- build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bom/build.gradle b/bom/build.gradle index 32805571109..1a1b3c2d9f5 100644 --- a/bom/build.gradle +++ b/bom/build.gradle @@ -9,7 +9,7 @@ publishing { pom.withXml { def dependencyManagementNode = asNode().appendNode('dependencyManagement').appendNode('dependencies') - rootProject.subprojects.findAll { it != project }.each { subProject -> + rootProject.subprojects.findAll { it != project && !it.path.startsWith(":docs:") && it != project(":docs") }.each { subProject -> dependencyManagementNode.appendNode('dependency').with { appendNode('groupId', subProject.group) appendNode('artifactId',subProject.name) diff --git a/build.gradle b/build.gradle index 67b3a3a6635..bac06d64052 100644 --- a/build.gradle +++ b/build.gradle @@ -42,7 +42,7 @@ subprojects { } // specific modules should be excluded from publication - if ( ! ["test-support", "jdbc-test", "docs-examples"].contains(it.name) ) { + if ( ! ["test-support", "jdbc-test"].contains(it.name) && !it.path.startsWith(":docs:") && it != project(":docs") ) { apply from: "$rootDir/gradle/publishing.gradle" apply from: "$rootDir/gradle/bintray.gradle" From d23e2d58f2db7b8ffee15fa741b181f63e4a44ad Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 29 Nov 2019 14:05:23 +0100 Subject: [PATCH 1066/5615] Update gradle-release.yml restore the CI check --- .github/workflows/gradle-release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/gradle-release.yml b/.github/workflows/gradle-release.yml index 1391561284e..3034ce9cdfb 100644 --- a/.github/workflows/gradle-release.yml +++ b/.github/workflows/gradle-release.yml @@ -13,6 +13,8 @@ jobs: uses: actions/setup-java@v1 with: java-version: 1.8 + - name: Verify CI check status + run: .github/workflows/scripts/check_ci_status.sh $GITHUB_SHA - name: Release with Gradle run: ./gradlew -Pversion=${GITHUB_REF##*/} release --scan --no-daemon -i env: From d3650665ebfd9a6db11231d217f6b5898a13d382 Mon Sep 17 00:00:00 2001 From: Vincent Massol Date: Fri, 29 Nov 2019 17:47:23 +0100 Subject: [PATCH 1067/5615] Add XWiki to "Who is using Testcontainers" (#2117) --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index c26ed164067..9caecd2261e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -78,6 +78,7 @@ You can also [check the latest version available on Maven Central](https://searc * [wescale](https://www.wescale.com) - Integration testing against HTTP service mocks and various data stores * [Marquez](https://marquezproject.github.io/marquez) - PostgreSQL integration testing * [Transferwise](https://transferwise.com/) - Integration testing for different RDBMS, kafka and micro services +* [XWiki](https://xwiki.org) - [Testing XWiki](https://dev.xwiki.org/xwiki/bin/view/Community/Testing/DockerTesting/) under all [supported configurations](https://dev.xwiki.org/xwiki/bin/view/Community/SupportStrategy/) ## License From 9c320eeb221a036ef3779f2e2a9bffd13c1cf8e9 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 1 Dec 2019 16:11:04 +0100 Subject: [PATCH 1068/5615] Add `x-amz-acl` header to allow public reads of the cache files --- settings.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/settings.gradle b/settings.gradle index 7e01821a058..e2ddf080972 100644 --- a/settings.gradle +++ b/settings.gradle @@ -30,6 +30,9 @@ buildCache { bucket = 'testcontainers' path = 'cache' push = isMasterBuild + headers = [ + 'x-amz-acl': 'public-read' + ] } } From bfb44a0067cf4ac7ea21b2ae3fb569fb790b831d Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 1 Dec 2019 22:45:46 +0100 Subject: [PATCH 1069/5615] include only published projects into BOM (#2118) --- bom/build.gradle | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/bom/build.gradle b/bom/build.gradle index 1a1b3c2d9f5..6826879ce18 100644 --- a/bom/build.gradle +++ b/bom/build.gradle @@ -9,11 +9,14 @@ publishing { pom.withXml { def dependencyManagementNode = asNode().appendNode('dependencyManagement').appendNode('dependencies') - rootProject.subprojects.findAll { it != project && !it.path.startsWith(":docs:") && it != project(":docs") }.each { subProject -> - dependencyManagementNode.appendNode('dependency').with { - appendNode('groupId', subProject.group) - appendNode('artifactId',subProject.name) - appendNode('version', subProject.version) + def bomProject = project + rootProject.subprojects.each { subProject -> + if (subProject != bomProject && subProject.plugins.findPlugin("maven-publish")) { + dependencyManagementNode.appendNode('dependency').with { + appendNode('groupId', subProject.group) + appendNode('artifactId',subProject.name) + appendNode('version', subProject.version) + } } } } From 650e5b0a854d400982d0df1a187d4bbec4850caf Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 1 Dec 2019 22:59:43 +0100 Subject: [PATCH 1070/5615] Push to the build cache from AZP too --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index e2ddf080972..e5432e08061 100644 --- a/settings.gradle +++ b/settings.gradle @@ -17,7 +17,7 @@ buildscript { apply plugin: 'ch.myniva.s3-build-cache' -ext.isMasterBuild = System.getenv("CIRCLE_BRANCH") == "master" +ext.isMasterBuild = (System.getenv("CIRCLE_BRANCH") ?: System.getenv("BUILD_SOURCEBRANCHNAME")) == "master" buildCache { local { From ef96100721f32b8e435cdbde444cb0454acda4f5 Mon Sep 17 00:00:00 2001 From: Tristan Tarrant Date: Mon, 2 Dec 2019 14:12:02 +0100 Subject: [PATCH 1071/5615] Add Infinispan to "Who is (#2136) --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index 9caecd2261e..1ab42e286c7 100644 --- a/docs/index.md +++ b/docs/index.md @@ -66,6 +66,7 @@ You can also [check the latest version available on Maven Central](https://searc * [Streamlio](https://streaml.io/) - Integration and Chaos Testing of our fast data platform based on Apache Puslar, Apache Bookeeper and Apache Heron. * [Spring Session](https://projects.spring.io/spring-session/) - Redis, PostgreSQL, MySQL and MariaDB integration testing * [Apache Camel](https://camel.apache.org) - Testing Camel against native services such as Consul, Etcd and so on +* [Infinispan](https://infinispan.org) - Testing the Infinispan Server as well as integration tests with databases, LDAP and KeyCloak * [Instana](https://www.instana.com) - Testing agents and stream processing backends * [eBay Marketing](https://www.ebay.com) - Testing for MySQL, Cassandra, Redis, Couchbase, Kafka, etc. * [Skyscanner](https://www.skyscanner.net/) - Integration testing against HTTP service mocks and various data stores From 18c2e2244e675b97362cffa1d1e11e10f9d3792e Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 3 Dec 2019 09:11:08 +0100 Subject: [PATCH 1072/5615] GHA-based in-docker testing (#2121) * GHA-based in-docker testing * Add missing triggers * Damn it, YAML! * simplify * use `:test` instead of `:check` * fix volume mappings * set workdir * Force run's working directory * *correctly* force run's working directory :D * use `cd` * try to mount `/home/runner/` * try `working-directory` again * try `cd` again * try `github.repository` * debug * try another volume mounting * debug * use `docker run` * remove TTY * add regular tests * fix syntax * temporary remove `include` * remove "normal" tests for now * test on `push` only when pushed to `master` * auto-format with IDEA --- .editorconfig | 6 +++- .github/workflows/ci.yml | 21 ++++++++++++++ .travis.yml | 60 ---------------------------------------- 3 files changed, 26 insertions(+), 61 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .travis.yml diff --git a/.editorconfig b/.editorconfig index 4d023ed18d5..b4910e47661 100644 --- a/.editorconfig +++ b/.editorconfig @@ -21,4 +21,8 @@ indent_size = 4 # Never use star imports ij_java_names_count_to_use_import_on_demand = 99 ij_java_class_count_to_use_import_on_demand = 99 -ij_java_layout_static_imports_separately = true \ No newline at end of file +ij_java_layout_static_imports_separately = true + +[*.{yml, yaml}] +indent_size = 2 +ij_yaml_keep_indents_on_empty_lines = false diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000000..7013e66bea6 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,21 @@ +name: CI + +on: + pull_request: {} + push: { branches: [ master ] } + +jobs: + in-docker_test: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v1 + - name: Build with Gradle + run: | + docker run -i --rm \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v "$HOME:$HOME" \ + -v "$PWD:$PWD" \ + -w "$PWD" \ + openjdk:8-jdk-alpine \ + ./gradlew --no-daemon --continue --scan testcontainers:test --tests '*GenericContainerRuleTest' + diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 6e07925db3f..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,60 +0,0 @@ -if: branch = master OR tag is present - -language: java -jdk: - - openjdk8 - -sudo: required -services: - - docker - -# Fix for https://travis-ci.community/t/problematic-docker-compose-version-1-23-1/5362/3 -before_install: - - sudo rm /usr/local/bin/docker-compose - - curl -L https://github.com/docker/compose/releases/download/1.24.1/docker-compose-Linux-x86_64 > docker-compose - - chmod +x docker-compose - - sudo mv docker-compose /usr/local/bin - -install: - - ./gradlew build -x check --scan --no-daemon - -jobs: - include: - - stage: test - env: [ NAME=core ] - script: ./gradlew testcontainers:check javadoc --scan --no-daemon -i - - - stage: test - env: [ NAME=core ] - jdk: openjdk11 - script: ./gradlew testcontainers:check --scan --no-daemon -i - - - stage: test - env: [ NAME=core ] - jdk: openjdk12 - script: ./gradlew testcontainers:check --scan --no-daemon -i - - - env: [ NAME=selenium ] - script: ./gradlew selenium:check --scan --no-daemon -i - - - env: [ NAME=modules ] - script: ./gradlew check -x testcontainers:check -x selenium:check -x jdbc-test:check --scan --no-daemon -i - - - env: [ NAME=jdbc ] - script: ./gradlew jdbc-test:check --scan --no-daemon -i - - - env: [ NAME=examples ] - script: cd examples && ./gradlew check --no-daemon -i - - # Run Docker-in-Docker tests inside Alpine - - env: [ NAME="docker-in-alpine-docker" ] - script: - - | - DOCKER_HOST=unix:///var/run/docker.sock DOCKER_TLS_VERIFY= docker run -t --rm \ - -v "$HOME/.gradle":/root/.gradle/ \ - -v /var/run/docker.sock:/var/run/docker.sock \ - -v "$(pwd)":"$(pwd)" \ - -w "$(pwd)" \ - openjdk:8-jdk-alpine \ - ./gradlew --no-daemon testcontainers:test --tests '*GenericContainerRuleTest' --scan -i - From 796c08a8c6f421adec2c91e9a2fa93b4d448aeb3 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 3 Dec 2019 11:22:43 +0100 Subject: [PATCH 1073/5615] fix `DockerClientFactory#dockerHostIpAddress` (#2119) * fix `DockerClientFactory#dockerHostIpAddress` * Remove conflicting `DockerClientFactoryTest#dockerClient` test --- .../testcontainers/DockerClientFactory.java | 134 +++++++++--------- .../DockerClientFactoryTest.java | 11 +- .../DockerClientConfigUtilsTest.java | 10 +- 3 files changed, 86 insertions(+), 69 deletions(-) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 54f44dbed3c..0ca7d41a209 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -62,8 +62,12 @@ public class DockerClientFactory { private static DockerClientFactory instance; // Cached client configuration - private DockerClientProviderStrategy strategy; - private boolean initialized = false; + @VisibleForTesting + DockerClientProviderStrategy strategy; + + @VisibleForTesting + DockerClient dockerClient; + private String activeApiVersion; private String activeExecutionDriver; @@ -74,10 +78,8 @@ public class DockerClientFactory { System.setProperty("org.testcontainers.shaded.io.netty.packagePrefix", "org.testcontainers.shaded."); } - /** - * Private constructor - */ - private DockerClientFactory() { + @VisibleForTesting + DockerClientFactory() { } @@ -98,6 +100,19 @@ public synchronized static DockerClientFactory instance() { return instance; } + @Synchronized + private DockerClientProviderStrategy getOrInitializeStrategy() { + if (strategy != null) { + return strategy; + } + + List configurationStrategies = new ArrayList<>(); + ServiceLoader.load(DockerClientProviderStrategy.class).forEach(configurationStrategies::add); + + strategy = DockerClientProviderStrategy.getFirstValidStrategy(configurationStrategies); + return strategy; + } + /** * * @return a new initialized Docker client @@ -105,63 +120,57 @@ public synchronized static DockerClientFactory instance() { @Synchronized public DockerClient client() { - if (strategy != null) { - return strategy.getClient(); + if (dockerClient != null) { + return dockerClient; } - List configurationStrategies = new ArrayList(); - ServiceLoader.load(DockerClientProviderStrategy.class).forEach( cs -> configurationStrategies.add( cs ) ); - - strategy = DockerClientProviderStrategy.getFirstValidStrategy(configurationStrategies); + final DockerClientProviderStrategy strategy = getOrInitializeStrategy(); String hostIpAddress = strategy.getDockerHostIpAddress(); log.info("Docker host IP address is {}", hostIpAddress); - DockerClient client = strategy.getClient(); - - if (!initialized) { - Info dockerInfo = client.infoCmd().exec(); - Version version = client.versionCmd().exec(); - activeApiVersion = version.getApiVersion(); - activeExecutionDriver = dockerInfo.getExecutionDriver(); - log.info("Connected to docker: \n" + - " Server Version: " + dockerInfo.getServerVersion() + "\n" + - " API Version: " + activeApiVersion + "\n" + - " Operating System: " + dockerInfo.getOperatingSystem() + "\n" + - " Total Memory: " + dockerInfo.getMemTotal() / (1024 * 1024) + " MB"); - - String ryukContainerId = null; - boolean useRyuk = !Boolean.parseBoolean(System.getenv("TESTCONTAINERS_RYUK_DISABLED")); - if (useRyuk) { - ryukContainerId = ResourceReaper.start(hostIpAddress, client); - log.info("Ryuk started - will monitor and terminate Testcontainers containers on JVM exit"); - } + final DockerClient client = strategy.getClient(); + + Info dockerInfo = client.infoCmd().exec(); + Version version = client.versionCmd().exec(); + activeApiVersion = version.getApiVersion(); + activeExecutionDriver = dockerInfo.getExecutionDriver(); + log.info("Connected to docker: \n" + + " Server Version: " + dockerInfo.getServerVersion() + "\n" + + " API Version: " + activeApiVersion + "\n" + + " Operating System: " + dockerInfo.getOperatingSystem() + "\n" + + " Total Memory: " + dockerInfo.getMemTotal() / (1024 * 1024) + " MB"); + + String ryukContainerId = null; + boolean useRyuk = !Boolean.parseBoolean(System.getenv("TESTCONTAINERS_RYUK_DISABLED")); + if (useRyuk) { + ryukContainerId = ResourceReaper.start(hostIpAddress, client); + log.info("Ryuk started - will monitor and terminate Testcontainers containers on JVM exit"); + } - boolean checksEnabled = !TestcontainersConfiguration.getInstance().isDisableChecks(); - if (checksEnabled) { - VisibleAssertions.info("Checking the system..."); - checkDockerVersion(version.getVersion()); - if (ryukContainerId != null) { - checkDiskSpace(client, ryukContainerId); - } else { - runInsideDocker( - client, - createContainerCmd -> { - createContainerCmd.withName("testcontainers-checks-" + SESSION_ID); - createContainerCmd.getHostConfig().withAutoRemove(true); - createContainerCmd.withCmd("tail", "-f", "/dev/null"); - }, - (__, containerId) -> { - checkDiskSpace(client, containerId); - return ""; - } - ); - } + boolean checksEnabled = !TestcontainersConfiguration.getInstance().isDisableChecks(); + if (checksEnabled) { + VisibleAssertions.info("Checking the system..."); + checkDockerVersion(version.getVersion()); + if (ryukContainerId != null) { + checkDiskSpace(client, ryukContainerId); + } else { + runInsideDocker( + client, + createContainerCmd -> { + createContainerCmd.withName("testcontainers-checks-" + SESSION_ID); + createContainerCmd.getHostConfig().withAutoRemove(true); + createContainerCmd.withCmd("tail", "-f", "/dev/null"); + }, + (__, containerId) -> { + checkDiskSpace(client, containerId); + return ""; + } + ); } - - initialized = true; } - return client; + dockerClient = client; + return dockerClient; } private void checkDockerVersion(String dockerVersion) { @@ -237,15 +246,12 @@ public void checkAndPullImage(DockerClient client, String image) { * @return the IP address of the host running Docker */ public String dockerHostIpAddress() { - return strategy.getDockerHostIpAddress(); + return getOrInitializeStrategy().getDockerHostIpAddress(); } public T runInsideDocker(Consumer createContainerCmdConsumer, BiFunction block) { - if (strategy == null) { - client(); - } // We can't use client() here because it might create an infinite loop - return runInsideDocker(strategy.getClient(), createContainerCmdConsumer, block); + return runInsideDocker(getOrInitializeStrategy().getClient(), createContainerCmdConsumer, block); } private T runInsideDocker(DockerClient client, Consumer createContainerCmdConsumer, BiFunction block) { @@ -293,9 +299,7 @@ DiskSpaceUsage parseAvailableDiskSpace(String dfOutput) { * @return the docker API version of the daemon that we have connected to */ public String getActiveApiVersion() { - if (!initialized) { - client(); - } + client(); return activeApiVersion; } @@ -303,9 +307,7 @@ public String getActiveApiVersion() { * @return the docker execution driver of the daemon that we have connected to */ public String getActiveExecutionDriver() { - if (!initialized) { - client(); - } + client(); return activeExecutionDriver; } @@ -314,7 +316,7 @@ public String getActiveExecutionDriver() { * @return whether or not the currently active strategy is of the provided type */ public boolean isUsing(Class providerStrategyClass) { - return providerStrategyClass.isAssignableFrom(this.strategy.getClass()); + return strategy != null && providerStrategyClass.isAssignableFrom(this.strategy.getClass()); } private static class NotEnoughDiskSpaceException extends RuntimeException { diff --git a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java index e13c3875f31..19a591be919 100644 --- a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java +++ b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java @@ -7,6 +7,8 @@ import org.testcontainers.dockerclient.LogToStringContainerCallback; import org.testcontainers.utility.TestcontainersConfiguration; +import static org.assertj.core.api.Assertions.assertThat; + /** * Test for {@link DockerClientFactory}. */ @@ -43,4 +45,11 @@ public void shouldHandleBigDiskSize() throws Exception { VisibleAssertions.assertEquals("Available MB is correct", 2982480572L / 1024L, usage.availableMB.orElse(0L)); VisibleAssertions.assertEquals("Available percentage is correct", 31, usage.usedPercent.orElse(0)); } -} \ No newline at end of file + + @Test + public void dockerHostIpAddress() { + DockerClientFactory instance = new DockerClientFactory(); + instance.strategy = null; + assertThat(instance.dockerHostIpAddress()).isNotNull(); + } +} diff --git a/core/src/test/java/org/testcontainers/dockerclient/DockerClientConfigUtilsTest.java b/core/src/test/java/org/testcontainers/dockerclient/DockerClientConfigUtilsTest.java index e4e29550b9c..2e88982a7a8 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/DockerClientConfigUtilsTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/DockerClientConfigUtilsTest.java @@ -6,6 +6,7 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; public class DockerClientConfigUtilsTest { @@ -33,7 +34,7 @@ public void getDockerHostIpAddressShouldReturnDockerHostIpWhenHttpsUri() { String actual = DockerClientConfigUtils.getDockerHostIpAddress(configuration); assertEquals("12.23.34.45", actual); } - + @Test public void getDockerHostIpAddressShouldReturnDockerHostIpWhenTcpUri() { DockerClientConfig configuration = DefaultDockerClientConfig.createDefaultConfigBuilder() @@ -43,11 +44,16 @@ public void getDockerHostIpAddressShouldReturnDockerHostIpWhenTcpUri() { String actual = DockerClientConfigUtils.getDockerHostIpAddress(configuration); assertEquals("12.23.34.45", actual); } - + @Test @Ignore public void getDockerHostIpAddressShouldReturnNullWhenUnsupportedUriScheme() { DockerClientConfig configuration = DefaultDockerClientConfig.createDefaultConfigBuilder().withDockerHost("gopher://12.23.34.45").build(); String actual = DockerClientConfigUtils.getDockerHostIpAddress(configuration); assertNull(actual); } + + @Test(timeout = 5_000) + public void getDefaultGateway() { + assertNotNull(DockerClientConfigUtils.getDefaultGateway()); + } } From 82876e489f19796229d96bb116ab7d2415c96cdb Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 3 Dec 2019 13:24:38 +0000 Subject: [PATCH 1074/5615] Use Python 3.7 for netlify doc generation (#2143) --- .envrc | 2 +- runtime.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.envrc b/.envrc index 3e6a147e52b..dad2eb2cfc6 100644 --- a/.envrc +++ b/.envrc @@ -1 +1 @@ -use python 3.6.0 +use python 3.7.5 diff --git a/runtime.txt b/runtime.txt index d70c8f8d89f..475ba515c04 100644 --- a/runtime.txt +++ b/runtime.txt @@ -1 +1 @@ -3.6 +3.7 From 77189eff82a5a033ae116c38a6e29a43cea985bf Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 5 Dec 2019 18:33:26 +0200 Subject: [PATCH 1075/5615] Set AWS credentials for the build cache in AZP (#2138) --- azure-pipelines.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index d6efa593dcb..4b060c5fcea 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -3,6 +3,9 @@ jobs: steps: - task: Gradle@2 displayName: Build & test + env: + AWS_ACCESS_KEY_ID: $(aws.accessKeyId) + AWS_SECRET_ACCESS_KEY: $(aws.secretAccessKey) inputs: gradleWrapperFile: 'gradlew' jdkVersionOption: '1.11' @@ -14,6 +17,9 @@ jobs: steps: - task: Gradle@2 displayName: Build & test + env: + AWS_ACCESS_KEY_ID: $(aws.accessKeyId) + AWS_SECRET_ACCESS_KEY: $(aws.secretAccessKey) inputs: gradleWrapperFile: 'gradlew' jdkVersionOption: '1.11' @@ -25,6 +31,9 @@ jobs: steps: - task: Gradle@2 displayName: Build & test + env: + AWS_ACCESS_KEY_ID: $(aws.accessKeyId) + AWS_SECRET_ACCESS_KEY: $(aws.secretAccessKey) inputs: gradleWrapperFile: 'gradlew' jdkVersionOption: '1.11' From 3f6c38be0c3b2a228be69db61607d9991a03b998 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2019 20:49:39 +0000 Subject: [PATCH 1076/5615] Bump java-client from 2.7.10 to 2.7.11 in /modules/couchbase (#2150) Bumps [java-client](https://github.com/couchbase/couchbase-java-client) from 2.7.10 to 2.7.11. - [Release notes](https://github.com/couchbase/couchbase-java-client/releases) - [Commits](https://github.com/couchbase/couchbase-java-client/compare/2.7.10...2.7.11) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 7642bb2fc5b..27074f1bff7 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -2,7 +2,7 @@ description = "Testcontainers :: Couchbase" dependencies { compile project(':testcontainers') - compile 'com.couchbase.client:java-client:2.7.10' + compile 'com.couchbase.client:java-client:2.7.11' testCompile project(':test-support') } From 3f6d62601961cf400fa6bc597fc99ea25be5356a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2019 21:25:16 +0000 Subject: [PATCH 1077/5615] Bump mockito-core from 3.1.0 to 3.2.0 in /modules/junit-jupiter (#2131) --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index f6f23aa8959..b5c974ecd89 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,7 +9,7 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.4.1' testCompile 'redis.clients:jedis:3.1.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.10' - testCompile ('org.mockito:mockito-core:3.1.0') { + testCompile ('org.mockito:mockito-core:3.2.0') { exclude(module: 'hamcrest-core') } From 0adacdef409e76f79e3e3dc2ee988b44541bf33f Mon Sep 17 00:00:00 2001 From: Andrew Guibert Date: Thu, 5 Dec 2019 15:31:17 -0600 Subject: [PATCH 1078/5615] Truncate mode bits for z/OS file permissions (#2023) --- .../org/testcontainers/utility/MountableFile.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java index 37312116734..22939b0e1fd 100644 --- a/core/src/main/java/org/testcontainers/utility/MountableFile.java +++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java @@ -6,6 +6,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; +import org.apache.commons.compress.archivers.tar.TarConstants; import org.apache.commons.lang.SystemUtils; import org.jetbrains.annotations.NotNull; import org.testcontainers.DockerClientFactory; @@ -368,7 +369,15 @@ private int getUnixFileMode(final String pathAsString) { @UnstableAPI public static int getUnixFileMode(final Path path) { try { - return (int) Files.readAttributes(path, "unix:mode").get("mode"); + int unixMode = (int) Files.readAttributes(path, "unix:mode").get("mode"); + // Truncate mode bits for z/OS + if ("OS/390".equals(SystemUtils.OS_NAME) || + "z/OS".equals(SystemUtils.OS_NAME) || + "zOS".equals(SystemUtils.OS_NAME) ) { + unixMode &= TarConstants.MAXID; + unixMode |= Files.isDirectory(path) ? 040000 : 0100000; + } + return unixMode; } catch (IOException | UnsupportedOperationException e) { // fallback for non-posix environments int mode = DEFAULT_FILE_MODE; From bb0554b626dd29031ee01efc14f21972a0015fba Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 6 Dec 2019 11:35:33 +0200 Subject: [PATCH 1079/5615] do not implicitly connect to Kafka network (#2139) Apparently, there is a bug(?) in Docker that sometimes(!) re-assigns random ports when container is connect to network. It means that we cannot lazily connect KafkaContainer to network in case it wasn't set. --- .../org/testcontainers/containers/KafkaContainer.java | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java index 60037acfd45..b0ce047f5d4 100644 --- a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java +++ b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java @@ -39,6 +39,7 @@ public KafkaContainer() { public KafkaContainer(String confluentPlatformVersion) { super(TestcontainersConfiguration.getInstance().getKafkaImage() + ":" + confluentPlatformVersion); + super.withNetwork(Network.SHARED); withExposedPorts(KAFKA_PORT); // Use two listeners with different names, it will force Kafka to communicate with itself via internal @@ -70,16 +71,6 @@ public Network getNetwork() { "Consider using KafkaContainer#withNetwork", new Exception("Deprecated method") ); - Network network = Network.SHARED; - super.withNetwork(network); - - if (getContainerId() != null) { - dockerClient.connectToNetworkCmd() - .withContainerId(getContainerId()) - .withNetworkId(network.getId()) - .exec(); - } - return network; } return super.getNetwork(); } From 500fd7404c07712c1f8af2d5f6ba51a048b9f6da Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 6 Dec 2019 12:07:41 +0000 Subject: [PATCH 1080/5615] Improve Docker Compose error messaging (#2154) Co-Authored-By: Richard North Co-authored-by: Matthias Hahn Co-authored-by: null <38075918+HaMatthias@users.noreply.github.com> --- .../containers/DockerComposeContainer.java | 10 +++++++++- .../testcontainers/junit/DockerComposeServiceTest.java | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 31d84284584..7dec6399b8f 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -385,7 +385,15 @@ public String getServiceHost(String serviceName, Integer servicePort) { * @return a port that can be used for accessing the service container. */ public Integer getServicePort(String serviceName, Integer servicePort) { - return ambassadorContainer.getMappedPort(ambassadorPortMappings.get(getServiceInstanceName(serviceName)).get(servicePort)); + Map portMap = ambassadorPortMappings.get(getServiceInstanceName(serviceName)); + + if (portMap == null) { + throw new IllegalArgumentException("Could not get a port for '" + serviceName + "'. " + + "Testcontainers does not have an exposed port configured for '" + serviceName + "'. "+ + "To fix, please ensure that the service '" + serviceName + "' has ports exposed using .withExposedService(...)"); + } else { + return ambassadorContainer.getMappedPort(portMap.get(servicePort)); + } } public SELF withScaledService(String serviceBaseName, int numInstances) { diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeServiceTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeServiceTest.java index c0360050d27..ec8c50d42ab 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeServiceTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeServiceTest.java @@ -21,7 +21,7 @@ protected DockerComposeContainer getEnvironment() { return environment; } - @Test(expected = NullPointerException.class) + @Test(expected = IllegalArgumentException.class) public void testDbIsNotStarting() { environment.getServicePort("db_1", 10001); } From b51789896d404bee9f439099e5cbb4d79f62ef99 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 6 Dec 2019 12:48:08 +0000 Subject: [PATCH 1081/5615] Bump vibur-dbcp from 22.2 to 25.0 in /modules/mariadb (#2128) --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 541d3967680..a881b63cb99 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -7,5 +7,5 @@ dependencies { testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.29' - testCompile 'org.vibur:vibur-dbcp:22.2' + testCompile 'org.vibur:vibur-dbcp:25.0' } From 968a56e45e2700700926cf4fe4128be6cab933e7 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 6 Dec 2019 13:07:42 +0000 Subject: [PATCH 1082/5615] Bump mockito-core from 3.1.0 to 3.2.0 in /core (#2129) --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 9da3288d0c3..461c5696347 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -109,7 +109,7 @@ dependencies { testCompile 'redis.clients:jedis:3.1.0' testCompile 'com.rabbitmq:amqp-client:5.7.3' testCompile 'org.mongodb:mongo-java-driver:3.11.2' - testCompile ('org.mockito:mockito-core:3.1.0') { + testCompile ('org.mockito:mockito-core:3.2.0') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From b03d77fb4a4f75b95b674b53b41725e09cd61fda Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 6 Dec 2019 13:36:08 +0000 Subject: [PATCH 1083/5615] Bump vibur-dbcp from 22.2 to 25.0 in /modules/mysql (#2130) --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 2a71e8f9d04..d9961293d86 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -7,5 +7,5 @@ dependencies { testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.29' testCompile 'commons-dbutils:commons-dbutils:1.7' - testCompile 'org.vibur:vibur-dbcp:22.2' + testCompile 'org.vibur:vibur-dbcp:25.0' } From 35fd71d57b6b0ea862f19e7eb43001f1fb219098 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 6 Dec 2019 14:02:47 +0000 Subject: [PATCH 1084/5615] Bump elasticsearch-rest-client in /modules/elasticsearch (#2142) --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 11c8b57f07a..9e7ac832916 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,6 +2,6 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') - testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.4.0" + testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.5.0" testCompile "org.elasticsearch.client:transport:6.7.1" } From 9a45baa2f8d9e7fedb17fb2378b5da012bfb7103 Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Sat, 7 Dec 2019 21:32:10 +0700 Subject: [PATCH 1085/5615] Refactor deprecated Mockito API usages (#2039) --- .../containers/FailureDetectingExternalResourceTest.java | 2 +- .../testcontainers/utility/AuthenticatedImagePullTest.java | 4 ++-- .../org/testcontainers/dynamodb/DynaliteContainerTest.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/test/java/org/testcontainers/containers/FailureDetectingExternalResourceTest.java b/core/src/test/java/org/testcontainers/containers/FailureDetectingExternalResourceTest.java index 47d594c4c02..15a9eba7d17 100644 --- a/core/src/test/java/org/testcontainers/containers/FailureDetectingExternalResourceTest.java +++ b/core/src/test/java/org/testcontainers/containers/FailureDetectingExternalResourceTest.java @@ -3,7 +3,7 @@ import org.junit.Test; import org.junit.runner.Description; import org.junit.runners.model.Statement; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; diff --git a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java index 19b933d174c..78b54798cd1 100644 --- a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java +++ b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java @@ -16,8 +16,8 @@ import java.util.concurrent.TimeUnit; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; diff --git a/modules/dynalite/src/test/java/org/testcontainers/dynamodb/DynaliteContainerTest.java b/modules/dynalite/src/test/java/org/testcontainers/dynamodb/DynaliteContainerTest.java index 2d414aecb3b..524d3f6c34c 100644 --- a/modules/dynalite/src/test/java/org/testcontainers/dynamodb/DynaliteContainerTest.java +++ b/modules/dynalite/src/test/java/org/testcontainers/dynamodb/DynaliteContainerTest.java @@ -37,7 +37,7 @@ private void runTest(AmazonDynamoDB client) { "Name", ScalarAttributeType.S)) .withKeySchema(new KeySchemaElement("Name", KeyType.HASH)) .withProvisionedThroughput(new ProvisionedThroughput( - new Long(10), new Long(10))) + 10L, 10L)) .withTableName("foo"); @@ -49,4 +49,4 @@ private void runTest(AmazonDynamoDB client) { assertEquals("the table has the right name", "foo", tableDescription.getTableName()); assertEquals("the name has the right primary key", "Name", tableDescription.getKeySchema().get(0).getAttributeName()); } -} \ No newline at end of file +} From 2af09196b55cd36d18cf64970c8a2504dd629f84 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 13 Dec 2019 15:55:56 +0100 Subject: [PATCH 1086/5615] Bump amqp-client from 5.7.3 to 5.8.0 in /core (#2173) Bumps [amqp-client](https://github.com/rabbitmq/rabbitmq-java-client) from 5.7.3 to 5.8.0. - [Release notes](https://github.com/rabbitmq/rabbitmq-java-client/releases) - [Commits](https://github.com/rabbitmq/rabbitmq-java-client/compare/v5.7.3...v5.8.0) Signed-off-by: dependabot-preview[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 461c5696347..dcb4165d08d 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -107,7 +107,7 @@ dependencies { testCompile 'org.apache.httpcomponents:httpclient:4.5.9' testCompile 'redis.clients:jedis:3.1.0' - testCompile 'com.rabbitmq:amqp-client:5.7.3' + testCompile 'com.rabbitmq:amqp-client:5.8.0' testCompile 'org.mongodb:mongo-java-driver:3.11.2' testCompile ('org.mockito:mockito-core:3.2.0') { exclude(module: 'hamcrest-core') From 309aad0662c748819f82101087f78082fd2806eb Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 13 Dec 2019 15:56:41 +0100 Subject: [PATCH 1087/5615] Bump tomcat-jdbc from 9.0.29 to 9.0.30 in /modules/mysql (#2174) Bumps tomcat-jdbc from 9.0.29 to 9.0.30. Signed-off-by: dependabot-preview[bot] --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index d9961293d86..0b70ea3af6a 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -5,7 +5,7 @@ dependencies { testCompile 'mysql:mysql-connector-java:8.0.18' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.29' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.30' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.vibur:vibur-dbcp:25.0' } From 666781cef6ea70de7eff236fe5e9ef9ce2a4e4d9 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 13 Dec 2019 15:57:14 +0100 Subject: [PATCH 1088/5615] Bump tomcat-jdbc from 9.0.29 to 9.0.30 in /modules/mariadb (#2172) Bumps tomcat-jdbc from 9.0.29 to 9.0.30. Signed-off-by: dependabot-preview[bot] --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index a881b63cb99..041b0a146da 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -6,6 +6,6 @@ dependencies { testCompile 'org.mariadb.jdbc:mariadb-java-client:2.5.2' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.29' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.30' testCompile 'org.vibur:vibur-dbcp:25.0' } From b4fe1eddc48b23488eb5d1c6c65071169913424a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 13 Dec 2019 15:57:51 +0100 Subject: [PATCH 1089/5615] Bump mongo-java-driver from 3.11.2 to 3.12.0 in /core (#2168) Bumps [mongo-java-driver](https://github.com/mongodb/mongo-java-driver) from 3.11.2 to 3.12.0. - [Release notes](https://github.com/mongodb/mongo-java-driver/releases) - [Commits](https://github.com/mongodb/mongo-java-driver/compare/r3.11.2...r3.12.0) Signed-off-by: dependabot-preview[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index dcb4165d08d..618c31c7176 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -108,7 +108,7 @@ dependencies { testCompile 'org.apache.httpcomponents:httpclient:4.5.9' testCompile 'redis.clients:jedis:3.1.0' testCompile 'com.rabbitmq:amqp-client:5.8.0' - testCompile 'org.mongodb:mongo-java-driver:3.11.2' + testCompile 'org.mongodb:mongo-java-driver:3.12.0' testCompile ('org.mockito:mockito-core:3.2.0') { exclude(module: 'hamcrest-core') } From ae6d150eaf95cec56cb94fe7d1cdc33b9c9f111e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 13 Dec 2019 15:58:48 +0100 Subject: [PATCH 1090/5615] Bump postgresql from 42.2.8 to 42.2.9 in /modules/junit-jupiter (#2160) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.8 to 42.2.9. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.8...REL42.2.9) Signed-off-by: dependabot-preview[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index b5c974ecd89..8ff335251a3 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -13,7 +13,7 @@ dependencies { exclude(module: 'hamcrest-core') } - testRuntime 'org.postgresql:postgresql:42.2.8' + testRuntime 'org.postgresql:postgresql:42.2.9' testRuntime 'mysql:mysql-connector-java:8.0.18' testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.5.2' } From 98a201aa91344484d2c8cb4290361cc9b9c512a2 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 13 Dec 2019 16:00:30 +0100 Subject: [PATCH 1091/5615] Bump postgresql from 42.2.8 to 42.2.9 in /modules/postgresql (#2159) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.8 to 42.2.9. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.8...REL42.2.9) Signed-off-by: dependabot-preview[bot] --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index c070ce9ba13..a4bf697a3ad 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: JDBC :: PostgreSQL" dependencies { compile project(':jdbc') - testCompile 'org.postgresql:postgresql:42.2.8' + testCompile 'org.postgresql:postgresql:42.2.9' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' } From 0ba7a0e5644f7a23fc90f5988fde46ad5d6748ec Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 13 Dec 2019 16:42:21 +0100 Subject: [PATCH 1092/5615] Bump postgresql from 42.2.8 to 42.2.9 in /examples (#2162) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.8 to 42.2.9. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.8...REL42.2.9) Signed-off-by: dependabot-preview[bot] --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index eb10aed7159..dc9f7158e29 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.29' implementation 'com.squareup.okhttp3:okhttp:3.14.4' implementation 'org.json:json:20180813' - testImplementation 'org.postgresql:postgresql:42.2.8' + testImplementation 'org.postgresql:postgresql:42.2.9' testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.testcontainers:postgresql' } From 9a8ada41c564cff615df98031ab7c0ccfa40101d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 13 Dec 2019 16:43:11 +0100 Subject: [PATCH 1093/5615] Bump postgresql from 42.2.8 to 42.2.9 in /modules/spock (#2158) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.8 to 42.2.9. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.8...REL42.2.9) Signed-off-by: dependabot-preview[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index d84e8776af3..2974784d466 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -13,6 +13,6 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.4.1' testCompile 'org.apache.httpcomponents:httpclient:4.5.10' - testRuntime 'org.postgresql:postgresql:42.2.8' + testRuntime 'org.postgresql:postgresql:42.2.9' testRuntime 'mysql:mysql-connector-java:8.0.18' } From 9bd7a61f5b847596c595ea75e69fe9faea486b2f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 13 Dec 2019 16:51:25 +0100 Subject: [PATCH 1094/5615] Bump org.springframework.boot in /examples (#2161) Bumps [org.springframework.boot](https://github.com/spring-projects/spring-boot) from 2.2.1.RELEASE to 2.2.2.RELEASE. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.2.1.RELEASE...v2.2.2.RELEASE) Signed-off-by: dependabot-preview[bot] --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index 5386234dccb..5b6d71934b9 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.2.1.RELEASE' + id 'org.springframework.boot' version '2.2.2.RELEASE' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index 2a67123fa03..bea11bf0909 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.2.1.RELEASE' + id 'org.springframework.boot' version '2.2.2.RELEASE' } apply plugin: 'io.spring.dependency-management' From e0cfea8f5848508b3e108259865942b9719142bb Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 14 Dec 2019 13:20:49 +0100 Subject: [PATCH 1095/5615] Bump influxdb-java from 2.16 to 2.17 in /modules/influxdb (#2157) Bumps [influxdb-java](https://github.com/influxdata/influxdb-java) from 2.16 to 2.17. - [Release notes](https://github.com/influxdata/influxdb-java/releases) - [Changelog](https://github.com/influxdata/influxdb-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/influxdata/influxdb-java/compare/influxdb-java-2.16...influxdb-java-2.17) Signed-off-by: dependabot-preview[bot] --- modules/influxdb/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/influxdb/build.gradle b/modules/influxdb/build.gradle index 186174c242f..d349e06da12 100644 --- a/modules/influxdb/build.gradle +++ b/modules/influxdb/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: InfluxDB" dependencies { compile project(':testcontainers') - compileOnly 'org.influxdb:influxdb-java:2.16' - testCompile 'org.influxdb:influxdb-java:2.15' + compileOnly 'org.influxdb:influxdb-java:2.17' + testCompile 'org.influxdb:influxdb-java:2.17' } From dbffb52b426a2021484ff2abc765080288c29e8c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 14 Dec 2019 12:37:23 +0000 Subject: [PATCH 1096/5615] Bump testng from 7.0.0 to 7.1.0 in /examples (#2164) --- examples/redis-backed-cache-testng/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 8b133f0cc1b..98d0f4e06aa 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -13,7 +13,7 @@ dependencies { implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'ch.qos.logback:logback-classic:1.2.3' - testImplementation 'org.testng:testng:7.0.0' + testImplementation 'org.testng:testng:7.1.0' } test { From d6889e289bad8a3d7bdfd59c1fe4ac02111e7516 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 14 Dec 2019 13:39:28 +0100 Subject: [PATCH 1097/5615] Add GitHub action to automatically update docs version (#2122) * Add GitHub action to automatically update docs version * Fix syntax error * Update update-docs-version.yml * Update update-docs-version.yml * Use ad-m/github-push-action * Updates following review --- .github/workflows/update-docs-version.yml | 27 +++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/workflows/update-docs-version.yml diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml new file mode 100644 index 00000000000..ad8773db2db --- /dev/null +++ b/.github/workflows/update-docs-version.yml @@ -0,0 +1,27 @@ +name: Update docs version + +on: + release: + types: [published] + +jobs: + build: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v1 + - name: Checkout master + run: git checkout master + - name: Update latest_version property in mkdocs.yml + run: | + sed -i "s/latest_version: .*/latest_version: ${GITHUB_REF##*/}/g" mkdocs.yml + git diff + - name: Commit changes + run: | + git config user.email "nobody@testcontainers.org" + git config user.name "GitHub Action - Update docs version" + git add mkdocs.yml + git commit -m "Update documentation version to ${GITHUB_REF##*/}" -m "skip-checks: true" || echo "not changed" + - name: Push changes + uses: ad-m/github-push-action@68af9897f2b021035ca3952bf354bbb4675c1762 # v0.5.0 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} From 6e1d211f9be44e564d47fddedad809215813d7e0 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sat, 14 Dec 2019 14:52:59 +0100 Subject: [PATCH 1098/5615] Fix framing in OkHttp transport (#2155) we were skipping first 8 bytes in case of a RAW stream --- .../okhttp/OkHttpInvocationBuilder.java | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java index c7f679eb22b..cada10328ec 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java @@ -35,6 +35,8 @@ import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; +import java.nio.ByteBuffer; +import java.util.Arrays; import java.util.Objects; import java.util.function.Consumer; @@ -362,23 +364,34 @@ public void accept(BufferedSource source) { try { while (!source.exhausted()) { // See https://docs.docker.com/engine/api/v1.37/#operation/ContainerAttach - if(!source.request(HEADER_SIZE)) { + // [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4}[]byte{OUTPUT} + + if (!source.request(HEADER_SIZE)) { return; } - StreamType streamType = streamType(source.readByte()); - source.skip(3); - int payloadSize = source.readInt(); + byte[] bytes = source.readByteArray(HEADER_SIZE); + + StreamType streamType = streamType(bytes[0]); - if (streamType != StreamType.RAW) { - if (!source.request(payloadSize)) { - return; + if (streamType == StreamType.RAW) { + resultCallback.onNext(new Frame(StreamType.RAW, bytes)); + byte[] buffer = new byte[1024]; + while (!source.exhausted()) { + int readBytes = source.read(buffer); + if (readBytes != -1) { + resultCallback.onNext(new Frame(StreamType.RAW, Arrays.copyOf(buffer, readBytes))); + } } - byte[] payload = source.readByteArray(payloadSize); + return; + } - resultCallback.onNext(new Frame(streamType, payload)); - } else { - resultCallback.onNext(new Frame(streamType, source.readByteArray())); + int payloadSize = ByteBuffer.wrap(bytes, 4, 4).getInt(); + if (!source.request(payloadSize)) { + return; } + byte[] payload = source.readByteArray(payloadSize); + + resultCallback.onNext(new Frame(streamType, payload)); } } catch (Exception e) { resultCallback.onError(e); From 464cba86b6aa0e17dceec13f7bee3fcd5baaf7f1 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2019 17:29:58 +0000 Subject: [PATCH 1099/5615] Bump jedis from 3.1.0 to 3.2.0 in /examples (#2184) --- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 98d0f4e06aa..12e78729a28 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.29' - implementation 'redis.clients:jedis:3.1.0' + implementation 'redis.clients:jedis:3.2.0' implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index a95000e8d71..664a0fac876 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.29' - implementation 'redis.clients:jedis:3.1.0' + implementation 'redis.clients:jedis:3.2.0' implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 6cb2e6d77c5..3b4af328379 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { - implementation 'redis.clients:jedis:3.1.0' + implementation 'redis.clients:jedis:3.2.0' implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.29' From e6f92c695025e9f243f8f94572143f33eceebf5d Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Mon, 16 Dec 2019 18:31:26 +0100 Subject: [PATCH 1100/5615] Remove duplicated DockerImageNameTest test case (#2040) --- .../java/org/testcontainers/utility/DockerImageNameTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java b/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java index 37d2472949f..54488773652 100644 --- a/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java +++ b/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java @@ -16,7 +16,7 @@ public static class ValidNames { @Parameterized.Parameters(name = "{0}") public static String[] getNames() { - return new String[]{"myname:latest", + return new String[]{ "myname:latest", "repo/my-name:1.0", "registry.foo.com:1234/my-name:1.0", From 95ae65170d11461b5dbd75aee202ce2c70b9a930 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2019 08:58:38 +0000 Subject: [PATCH 1101/5615] Bump kafka-clients from 2.3.1 to 2.4.0 in /modules/kafka (#2182) Bumps kafka-clients from 2.3.1 to 2.4.0. Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index d9d7c20ca4a..d064e46c1dc 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Kafka" dependencies { compile project(':testcontainers') - testCompile 'org.apache.kafka:kafka-clients:2.3.1' + testCompile 'org.apache.kafka:kafka-clients:2.4.0' testCompile 'org.assertj:assertj-core:3.14.0' testCompile 'com.google.guava:guava:23.0' } From 4170e72e3e2d32064a71af117026356cb92a3139 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2019 08:58:47 +0000 Subject: [PATCH 1102/5615] Bump jedis from 3.1.0 to 3.2.0 in /modules/junit-jupiter (#2181) Bumps [jedis](https://github.com/xetorthio/jedis) from 3.1.0 to 3.2.0. - [Release notes](https://github.com/xetorthio/jedis/releases) - [Commits](https://github.com/xetorthio/jedis/compare/jedis-3.1.0...jedis-3.2.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 8ff335251a3..6cae8d48a67 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -7,7 +7,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') testCompile 'com.zaxxer:HikariCP:3.4.1' - testCompile 'redis.clients:jedis:3.1.0' + testCompile 'redis.clients:jedis:3.2.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.10' testCompile ('org.mockito:mockito-core:3.2.0') { exclude(module: 'hamcrest-core') From 6958fb2c9af5ab953787086d5ddc110e326d4a00 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2019 08:58:58 +0000 Subject: [PATCH 1103/5615] Bump jedis from 3.1.0 to 3.2.0 in /core (#2183) Bumps [jedis](https://github.com/xetorthio/jedis) from 3.1.0 to 3.2.0. - [Release notes](https://github.com/xetorthio/jedis/releases) - [Commits](https://github.com/xetorthio/jedis/compare/jedis-3.1.0...jedis-3.2.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 618c31c7176..8517033e51b 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -106,7 +106,7 @@ dependencies { } testCompile 'org.apache.httpcomponents:httpclient:4.5.9' - testCompile 'redis.clients:jedis:3.1.0' + testCompile 'redis.clients:jedis:3.2.0' testCompile 'com.rabbitmq:amqp-client:5.8.0' testCompile 'org.mongodb:mongo-java-driver:3.12.0' testCompile ('org.mockito:mockito-core:3.2.0') { From 639cacde7e0bb825da1ace99c7b70ca0d8233802 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2019 09:29:52 +0000 Subject: [PATCH 1104/5615] Bump elasticsearch-rest-client in /modules/elasticsearch (#2197) --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 9e7ac832916..2201efff48a 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,6 +2,6 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') - testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.5.0" + testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.5.1" testCompile "org.elasticsearch.client:transport:6.7.1" } From b0d1c9be21ee0bd9255920ef7b7299807053df7c Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 19 Dec 2019 10:48:33 +0100 Subject: [PATCH 1105/5615] Use `inspectNetworkCmd` to get the gateway's IP (#2151) --- .../dockerclient/DockerClientConfigUtils.java | 6 ++- .../DockerClientProviderStrategy.java | 39 ++++++++++++++++++- .../DockerClientConfigUtilsTest.java | 38 ++++++------------ 3 files changed, 53 insertions(+), 30 deletions(-) diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java index bfeabc63f8a..d88dc76331f 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java @@ -43,8 +43,10 @@ public class DockerClientConfigUtils { .map(StringUtils::trimToEmpty) .filter(StringUtils::isNotBlank); - - + /** + * Use {@link DockerClientFactory#dockerHostIpAddress()} + */ + @Deprecated public static String getDockerHostIpAddress(DockerClientConfig config) { switch (config.getDockerHost().getScheme()) { case "http": diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index 7101b23b21a..aa607964ae5 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -1,8 +1,10 @@ package org.testcontainers.dockerclient; import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.model.Network; import com.github.dockerjava.core.DockerClientBuilder; import com.github.dockerjava.core.DockerClientConfig; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Throwables; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; @@ -17,6 +19,7 @@ import org.testcontainers.dockerclient.transport.okhttp.OkHttpDockerCmdExecFactory; import org.testcontainers.utility.TestcontainersConfiguration; +import java.net.URI; import java.util.ArrayList; import java.util.Comparator; import java.util.List; @@ -33,6 +36,8 @@ public abstract class DockerClientProviderStrategy { protected DockerClient client; protected DockerClientConfig config; + private String dockerHostIpAddress; + private static final RateLimiter PING_RATE_LIMITER = RateLimiterBuilder.newBuilder() .withRate(2, TimeUnit.SECONDS) .withConstantThroughput() @@ -188,8 +193,38 @@ protected void ping(DockerClient client, int timeoutInSeconds) { } } - public String getDockerHostIpAddress() { - return DockerClientConfigUtils.getDockerHostIpAddress(this.config); + public synchronized String getDockerHostIpAddress() { + if (dockerHostIpAddress == null) { + dockerHostIpAddress = resolveDockerHostIpAddress(client, config.getDockerHost()); + } + return dockerHostIpAddress; + } + + @VisibleForTesting + static String resolveDockerHostIpAddress(DockerClient client, URI dockerHost) { + switch (dockerHost.getScheme()) { + case "http": + case "https": + case "tcp": + return dockerHost.getHost(); + case "unix": + case "npipe": + if (DockerClientConfigUtils.IN_A_CONTAINER) { + return client.inspectNetworkCmd() + .withNetworkId("bridge") + .exec() + .getIpam() + .getConfig() + .stream() + .filter(it -> it.getGateway() != null) + .findAny() + .map(Network.Ipam.Config::getGateway) + .orElse("localhost"); + } + return "localhost"; + default: + return null; + } } protected void checkOSType() { diff --git a/core/src/test/java/org/testcontainers/dockerclient/DockerClientConfigUtilsTest.java b/core/src/test/java/org/testcontainers/dockerclient/DockerClientConfigUtilsTest.java index 2e88982a7a8..353d6ea94e7 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/DockerClientConfigUtilsTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/DockerClientConfigUtilsTest.java @@ -1,9 +1,10 @@ package org.testcontainers.dockerclient; -import com.github.dockerjava.core.DefaultDockerClientConfig; -import com.github.dockerjava.core.DockerClientConfig; -import org.junit.Ignore; +import com.github.dockerjava.api.DockerClient; import org.junit.Test; +import org.testcontainers.DockerClientFactory; + +import java.net.URI; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -11,44 +12,29 @@ public class DockerClientConfigUtilsTest { + DockerClient client = DockerClientFactory.lazyClient(); + @Test public void getDockerHostIpAddressShouldReturnLocalhostWhenUnixSocket() { - DockerClientConfig configuration = DefaultDockerClientConfig.createDefaultConfigBuilder() - .withDockerHost("unix:///var/run/docker.sock") - .withDockerTlsVerify(false) // TODO - check wrt. https://github.com/docker-java/docker-java/issues/588 - .build(); - String actual = DockerClientConfigUtils.getDockerHostIpAddress(configuration); + String actual = DockerClientProviderStrategy.resolveDockerHostIpAddress(client, URI.create("unix:///var/run/docker.sock")); assertEquals("localhost", actual); } - @Test @Ignore - public void getDockerHostIpAddressShouldReturnDockerHostIpWhenHttpUri() { - DockerClientConfig configuration = DefaultDockerClientConfig.createDefaultConfigBuilder().withDockerHost("http://12.23.34.45").build(); - String actual = DockerClientConfigUtils.getDockerHostIpAddress(configuration); - assertEquals("12.23.34.45", actual); - } - - @Test @Ignore + @Test public void getDockerHostIpAddressShouldReturnDockerHostIpWhenHttpsUri() { - DockerClientConfig configuration = DefaultDockerClientConfig.createDefaultConfigBuilder().withDockerHost("https://12.23.34.45").build(); - String actual = DockerClientConfigUtils.getDockerHostIpAddress(configuration); + String actual = DockerClientProviderStrategy.resolveDockerHostIpAddress(client, URI.create("http://12.23.34.45")); assertEquals("12.23.34.45", actual); } @Test public void getDockerHostIpAddressShouldReturnDockerHostIpWhenTcpUri() { - DockerClientConfig configuration = DefaultDockerClientConfig.createDefaultConfigBuilder() - .withDockerHost("tcp://12.23.34.45") - .withDockerTlsVerify(false) // TODO - check wrt. https://github.com/docker-java/docker-java/issues/588 - .build(); - String actual = DockerClientConfigUtils.getDockerHostIpAddress(configuration); + String actual = DockerClientProviderStrategy.resolveDockerHostIpAddress(client, URI.create("tcp://12.23.34.45")); assertEquals("12.23.34.45", actual); } - @Test @Ignore + @Test public void getDockerHostIpAddressShouldReturnNullWhenUnsupportedUriScheme() { - DockerClientConfig configuration = DefaultDockerClientConfig.createDefaultConfigBuilder().withDockerHost("gopher://12.23.34.45").build(); - String actual = DockerClientConfigUtils.getDockerHostIpAddress(configuration); + String actual = DockerClientProviderStrategy.resolveDockerHostIpAddress(client, URI.create("gopher://12.23.34.45")); assertNull(actual); } From e9b2783a3fcbd6e73f168a4d55c1b7acd229511b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2019 10:25:37 +0000 Subject: [PATCH 1106/5615] Bump mockito-core from 3.2.0 to 3.2.4 in /modules/junit-jupiter (#2189) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.2.0 to 3.2.4. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.2.0...v3.2.4) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 6cae8d48a67..18cc5ddfe96 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,7 +9,7 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.4.1' testCompile 'redis.clients:jedis:3.2.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.10' - testCompile ('org.mockito:mockito-core:3.2.0') { + testCompile ('org.mockito:mockito-core:3.2.4') { exclude(module: 'hamcrest-core') } From 76d4668a40e2b2105fcc877158e520e93ca96137 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2019 10:47:47 +0000 Subject: [PATCH 1107/5615] Bump vault-java-driver from 5.0.0 to 5.1.0 in /modules/vault (#2187) Bumps [vault-java-driver](https://github.com/BetterCloud/vault-java-driver) from 5.0.0 to 5.1.0. - [Release notes](https://github.com/BetterCloud/vault-java-driver/releases) - [Commits](https://github.com/BetterCloud/vault-java-driver/commits) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index 55249f74fb0..0a68c9310e0 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Vault" dependencies { compile project(':testcontainers') - testCompile 'com.bettercloud:vault-java-driver:5.0.0' + testCompile 'com.bettercloud:vault-java-driver:5.1.0' testCompile 'io.rest-assured:rest-assured:4.1.2' testCompile 'org.assertj:assertj-core:3.14.0' From 2065330f6690c02931f69e1f6ab7717e466e4e55 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2019 10:47:57 +0000 Subject: [PATCH 1108/5615] Bump slf4j-api from 1.7.29 to 1.7.30 in /core (#2188) Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 1.7.29 to 1.7.30. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_1.7.29...v_1.7.30) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 8517033e51b..585faedfaa2 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -57,7 +57,7 @@ project.tasks.check.dependsOn(jarFileTest) dependencies { compile 'junit:junit:4.12' - compile 'org.slf4j:slf4j-api:1.7.29' + compile 'org.slf4j:slf4j-api:1.7.30' compile 'org.jetbrains:annotations:17.0.0' compile 'javax.annotation:javax.annotation-api:1.3.2' compile 'org.apache.commons:commons-compress:1.19' From 10114683dfe212c48f1301c96af929363b65dbd8 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2019 10:48:24 +0000 Subject: [PATCH 1109/5615] Bump mockito-core from 3.2.0 to 3.2.4 in /core (#2186) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.2.0 to 3.2.4. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.2.0...v3.2.4) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 585faedfaa2..2d76c38c0de 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -109,7 +109,7 @@ dependencies { testCompile 'redis.clients:jedis:3.2.0' testCompile 'com.rabbitmq:amqp-client:5.8.0' testCompile 'org.mongodb:mongo-java-driver:3.12.0' - testCompile ('org.mockito:mockito-core:3.2.0') { + testCompile ('org.mockito:mockito-core:3.2.4') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From e7e05d29a3b733315a2b0e620741e942f3c56fc4 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2019 10:48:54 +0000 Subject: [PATCH 1110/5615] Bump slf4j-api from 1.7.29 to 1.7.30 in /examples (#2190) Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 1.7.29 to 1.7.30. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_1.7.29...v_1.7.30) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- examples/linked-container/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index dc9f7158e29..dea159a89b5 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -6,7 +6,7 @@ repositories { jcenter() } dependencies { - compileOnly 'org.slf4j:slf4j-api:1.7.29' + compileOnly 'org.slf4j:slf4j-api:1.7.30' implementation 'com.squareup.okhttp3:okhttp:3.14.4' implementation 'org.json:json:20180813' testImplementation 'org.postgresql:postgresql:42.2.9' diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 12e78729a28..cf22e89c528 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:1.7.29' + compileOnly 'org.slf4j:slf4j-api:1.7.30' implementation 'redis.clients:jedis:3.2.0' implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.guava:guava:23.0' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 664a0fac876..b665b9a24a4 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:1.7.29' + compileOnly 'org.slf4j:slf4j-api:1.7.30' implementation 'redis.clients:jedis:3.2.0' implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.guava:guava:23.0' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 3b4af328379..b06d4e7cb30 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -11,7 +11,7 @@ dependencies { implementation 'redis.clients:jedis:3.2.0' implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.guava:guava:23.0' - compileOnly 'org.slf4j:slf4j-api:1.7.29' + compileOnly 'org.slf4j:slf4j-api:1.7.30' testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.testcontainers:testcontainers' From 08234f88a3c3f460db4684da683378d8bf4d026c Mon Sep 17 00:00:00 2001 From: William Delanoue Date: Mon, 23 Dec 2019 03:39:08 +0100 Subject: [PATCH 1111/5615] closes #1697: add vhost parameter for policies (#1701) --- .../containers/RabbitMQContainer.java | 9 +++++ .../containers/RabbitMQContainerTest.java | 35 +++++++++++-------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java index ea25bf629e6..ede111ed2d8 100644 --- a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java +++ b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java @@ -263,6 +263,15 @@ public RabbitMQContainer withPolicy(String name, String pattern, Map definition) { + values.add(asList("rabbitmqadmin", "declare", "policy", + "--vhost=" + vhost, + "name=" + name, + "pattern=" + pattern, + "definition=" + toJson(definition))); + return self(); + } + public RabbitMQContainer withPolicy(String name, String pattern, Map definition, int priority, String applyTo) { values.add(asList("rabbitmqadmin", "declare", "policy", "name=" + name, diff --git a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java index bdda594818f..5822e213a82 100644 --- a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java +++ b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java @@ -154,21 +154,22 @@ public void shouldStartTheWholeEnchilada() throws IOException, InterruptedExcept { try (RabbitMQContainer container = new RabbitMQContainer()) { container - .withVhost("vhost1") - .withVhostLimit("vhost1", "max-connections", 1) - .withVhost("vhost2", true) - .withExchange("direct-exchange", "direct") - .withExchange("topic-exchange", "topic") - .withQueue("queue1") - .withQueue("queue2", true, false, ImmutableMap.of("x-message-ttl", 1000)) - .withBinding("direct-exchange", "queue1") - .withUser("user1", "password1") - .withUser("user2", "password2", ImmutableSet.of("administrator")) - .withPermission("vhost1", "user1", ".*", ".*", ".*") - .withPolicy("max length policy", "^dog", ImmutableMap.of("max-length", 1), 1, "queues") - .withPolicy("alternate exchange policy", "^direct-exchange", ImmutableMap.of("alternate-exchange", "amq.direct")) - .withOperatorPolicy("operator policy 1", "^queue1", ImmutableMap.of("message-ttl", 1000), 1, "queues") - .withPluginsEnabled("rabbitmq_shovel", "rabbitmq_random_exchange"); + .withVhost("vhost1") + .withVhostLimit("vhost1", "max-connections", 1) + .withVhost("vhost2", true) + .withExchange("direct-exchange", "direct") + .withExchange("topic-exchange", "topic") + .withQueue("queue1") + .withQueue("queue2", true, false, ImmutableMap.of("x-message-ttl", 1000)) + .withBinding("direct-exchange", "queue1") + .withUser("user1", "password1") + .withUser("user2", "password2", ImmutableSet.of("administrator")) + .withPermission("vhost1", "user1", ".*", ".*", ".*") + .withPolicy("max length policy", "^dog", ImmutableMap.of("max-length", 1), 1, "queues") + .withPolicy("alternate exchange policy", "^direct-exchange", ImmutableMap.of("alternate-exchange", "amq.direct")) + .withPolicy("vhost2", "ha-all", ".*", ImmutableMap.of("ha-mode", "all", "ha-sync-mode", "automatic")) + .withOperatorPolicy("operator policy 1", "^queue1", ImmutableMap.of("message-ttl", 1000), 1, "queues") + .withPluginsEnabled("rabbitmq_shovel", "rabbitmq_random_exchange"); container.start(); @@ -192,6 +193,10 @@ public void shouldStartTheWholeEnchilada() throws IOException, InterruptedExcept .getStdout()) .contains("max length policy", "alternate exchange policy"); + assertThat(container.execInContainer("rabbitmqadmin", "list", "policies", "--vhost=vhost2") + .getStdout()) + .contains("ha-all", "ha-sync-mode"); + assertThat(container.execInContainer("rabbitmqadmin", "list", "operator_policies") .getStdout()) .contains("operator policy 1"); From 938c09a766f56fb09918abd7e352b9bdbedf1a4f Mon Sep 17 00:00:00 2001 From: Billy Yuan Date: Thu, 26 Dec 2019 21:08:59 +0800 Subject: [PATCH 1112/5615] Add Vert.x SQL Client to Userlist (#2211) Signed-off-by: Billy Yuan --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index 1ab42e286c7..f214bf0f73f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -75,6 +75,7 @@ You can also [check the latest version available on Maven Central](https://searc * [Zalando SE](https://corporate.zalando.com/en) - Testing core business services * [Europace AG](https://tech.europace.de/) - Integration testing for databases and micro services * [Micronaut Data](https://github.com/micronaut-projects/micronaut-data/) - Testing of Micronaut Data JDBC, a database access toolkit +* [Vert.x SQL Client](https://github.com/eclipse-vertx/vertx-sql-client) - Testing with PostgreSQL, MySQL, MariaDB, SQL Server, etc. * [JHipster](https://www.jhipster.tech/) - Couchbase and Cassandra integration testing * [wescale](https://www.wescale.com) - Integration testing against HTTP service mocks and various data stores * [Marquez](https://marquezproject.github.io/marquez) - PostgreSQL integration testing From 2cfd34935b07480750a879362ab10e04c210365e Mon Sep 17 00:00:00 2001 From: sullis Date: Thu, 26 Dec 2019 05:27:09 -0800 Subject: [PATCH 1113/5615] GitHub Actions checkout v2 (#2212) --- .github/workflows/ci.yml | 3 +-- .github/workflows/gradle-release.yml | 4 ++-- .github/workflows/update-docs-version.yml | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7013e66bea6..5618be1230d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ jobs: in-docker_test: runs-on: ubuntu-18.04 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Build with Gradle run: | docker run -i --rm \ @@ -18,4 +18,3 @@ jobs: -w "$PWD" \ openjdk:8-jdk-alpine \ ./gradlew --no-daemon --continue --scan testcontainers:test --tests '*GenericContainerRuleTest' - diff --git a/.github/workflows/gradle-release.yml b/.github/workflows/gradle-release.yml index 3034ce9cdfb..d35c0aa73f8 100644 --- a/.github/workflows/gradle-release.yml +++ b/.github/workflows/gradle-release.yml @@ -8,12 +8,12 @@ jobs: build: runs-on: ubuntu-18.04 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Set up JDK 1.8 uses: actions/setup-java@v1 with: java-version: 1.8 - - name: Verify CI check status + - name: Verify CI check status run: .github/workflows/scripts/check_ci_status.sh $GITHUB_SHA - name: Release with Gradle run: ./gradlew -Pversion=${GITHUB_REF##*/} release --scan --no-daemon -i diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index ad8773db2db..7268f72f961 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -8,7 +8,7 @@ jobs: build: runs-on: ubuntu-18.04 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Checkout master run: git checkout master - name: Update latest_version property in mkdocs.yml From 13b3e6bc4294c7fa6dc2c897f59b2672dd6cfcdc Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 26 Dec 2019 22:33:21 +0900 Subject: [PATCH 1114/5615] Bump testng from 7.1.0 to 7.1.1 in /examples (#2200) Bumps [testng](https://github.com/cbeust/testng) from 7.1.0 to 7.1.1. - [Release notes](https://github.com/cbeust/testng/releases) - [Changelog](https://github.com/cbeust/testng/blob/master/CHANGES.txt) - [Commits](https://github.com/cbeust/testng/commits/7.1.1) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- examples/redis-backed-cache-testng/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index cf22e89c528..efe713d5c0e 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -13,7 +13,7 @@ dependencies { implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'ch.qos.logback:logback-classic:1.2.3' - testImplementation 'org.testng:testng:7.1.0' + testImplementation 'org.testng:testng:7.1.1' } test { From 4266fda1f35be31ca0a4a6101a2afae8fba09195 Mon Sep 17 00:00:00 2001 From: Piotr Findeisen Date: Fri, 27 Dec 2019 10:49:40 +0100 Subject: [PATCH 1115/5615] JDBC: Fix parsing empty connection parameters (#2207) --- .../main/java/org/testcontainers/jdbc/ConnectionUrl.java | 2 +- .../java/org/testcontainers/jdbc/ConnectionUrlTest.java | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java index 4c590474bf7..4f339b43fe5 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java @@ -228,7 +228,7 @@ public interface Patterns { Pattern TC_PARAM_MATCHING_PATTERN = Pattern.compile(TC_PARAM_NAME_PATTERN + "=([^\\?&]+)"); - Pattern QUERY_PARAM_MATCHING_PATTERN = Pattern.compile("([^\\?&=]+)=([^\\?&]+)"); + Pattern QUERY_PARAM_MATCHING_PATTERN = Pattern.compile("([^\\?&=]+)=([^\\?&]*)"); } diff --git a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java index 53524eb32b2..312d5cf5146 100644 --- a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java +++ b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java @@ -49,6 +49,12 @@ public void testConnectionUrl2() { } + @Test + public void testEmptyQueryParameter() { + ConnectionUrl url = ConnectionUrl.newInstance("jdbc:tc:mysql://somehostname/databasename?key="); + assertEquals("'key' property value", "", url.getQueryParameters().get("key")); + } + @Test public void testTmpfsOption() { String urlString = "jdbc:tc:mysql://somehostname/databasename?TC_TMPFS=key:value,key1:value1"; From f8cd923403a0b7e9cfa9e021b5b0ea6b1ec8a687 Mon Sep 17 00:00:00 2001 From: "Roman S.A" Date: Fri, 27 Dec 2019 12:54:21 +0300 Subject: [PATCH 1116/5615] =?UTF-8?q?Mention=20`testcontainers-bom`=20in?= =?UTF-8?q?=20"Maven=20dependencies"=20section=20of=E2=80=A6=20(#2178)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Mention testcontainers-bom in the Maven dependencies section of the docs #2134 * Update docs/index.md Co-Authored-By: Richard North * Update docs/index.md Co-Authored-By: Richard North * Update docs/index.md Co-Authored-By: Richard North * Update docs/index.md Co-Authored-By: Richard North Co-authored-by: Richard North --- docs/index.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/docs/index.md b/docs/index.md index f214bf0f73f..af2bf79f0c9 100644 --- a/docs/index.md +++ b/docs/index.md @@ -46,6 +46,43 @@ testCompile "org.testcontainers:testcontainers:{{latest_version}}" You can also [check the latest version available on Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.testcontainers%22). +### Managing versions for multiple Testcontainers dependencies + +To avoid specifying the version of each dependency, you can use a `BOM` or `Bill Of Materials`. + +Using Maven you can add the following to `dependencyManagement` section in your `pom.xml`: + +```xml tab='Maven' + + + + org.testcontainers + testcontainers-bom + {{latest_version}} + pom + import + + + +``` +and then use dependencies without specifying a version: + +```xml tab='Maven' + + org.testcontainers + mysql + test + +``` + +Using Gradle 5.0 or higher, you can add the following to the `dependencies` section in your `build.gradle`: + +```groovy tab='Gradle' +implementation platform('org.testcontainers:testcontainers-bom:{{latest_version}}') //import bom +testImplementation('org.testcontainers:mysql') //no version specified +``` + + [JitPack](jitpack_dependencies.md) builds are available for pre-release versions. !!! warning "Shaded dependencies" From 3d79c03aa793a89bb124b92982c54469ad384978 Mon Sep 17 00:00:00 2001 From: worldtiki Date: Fri, 27 Dec 2019 10:13:43 +0000 Subject: [PATCH 1117/5615] Add example for nginx module (#2027) --- docs/modules/nginx.md | 22 +++++++++ .../testcontainers/junit/SimpleNginxTest.java | 48 +++++++++++-------- 2 files changed, 49 insertions(+), 21 deletions(-) diff --git a/docs/modules/nginx.md b/docs/modules/nginx.md index 32d668e150a..d195b5451a2 100644 --- a/docs/modules/nginx.md +++ b/docs/modules/nginx.md @@ -1,5 +1,27 @@ # Nginx Module +Nginx is a web server, reverse proxy and mail proxy and http cache. + +## Usage example + +The following example shows how to start Nginx. + + +[Creating a Nginx container](../../modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java) inside_block:creatingContainer + + +How to add custom content to the Nginx server. + + +[Creating the static content to serve](../../modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java) inside_block:addCustomContent + + +And how to query the Nginx server for the custom content added. + + +[Creating the static content to serve](../../modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java) inside_block:getFromNginxServer + + ## Adding this module to your project dependencies Add the following dependency to your `pom.xml`/`build.gradle` file: diff --git a/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java b/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java index ad9026b6e4c..b41a0d8361e 100644 --- a/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java +++ b/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java @@ -8,52 +8,58 @@ import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import java.io.*; +import java.net.URL; import java.net.URLConnection; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.rnorth.visibleassertions.VisibleAssertions.info; - +import static org.hamcrest.CoreMatchers.containsString; +import static org.rnorth.visibleassertions.VisibleAssertions.*; /** * @author richardnorth */ public class SimpleNginxTest { - private static File contentFolder = new File(System.getProperty("user.home") + "/.tmp-test-container"); + private static String tmpDirectory = System.getProperty("user.home") + "/.tmp-test-container"; + // creatingContainer { @Rule public NginxContainer nginx = new NginxContainer<>() - .withCustomContent(contentFolder.toString()) + .withCustomContent(tmpDirectory) .waitingFor(new HttpWaitStrategy()); + // } @SuppressWarnings({"Duplicates", "ResultOfMethodCallIgnored"}) @BeforeClass - public static void setupContent() throws FileNotFoundException { + public static void setupContent() throws Exception { + // addCustomContent { + // Create a temporary dir + File contentFolder = new File(tmpDirectory); contentFolder.mkdir(); - contentFolder.setReadable(true, false); - contentFolder.setWritable(true, false); - contentFolder.setExecutable(true, false); - + contentFolder.deleteOnExit(); + // And "hello world" HTTP file File indexFile = new File(contentFolder, "index.html"); - indexFile.setReadable(true, false); - indexFile.setWritable(true, false); - indexFile.setExecutable(true, false); - + indexFile.deleteOnExit(); @Cleanup PrintStream printStream = new PrintStream(new FileOutputStream(indexFile)); - printStream.println("This worked"); + printStream.println("Hello World!"); + // } } @Test public void testSimple() throws Exception { + // getFromNginxServer { + URL baseUrl = nginx.getBaseUrl("http", 80); - info("Base URL is " + nginx.getBaseUrl("http", 80)); + assertThat("An HTTP GET from the Nginx server returns the index.html from the custom content directory", + responseFromNginx(baseUrl), + containsString("Hello World!") + ); + // } + } - URLConnection urlConnection = nginx.getBaseUrl("http", 80).openConnection(); + private static String responseFromNginx(URL baseUrl) throws IOException { + URLConnection urlConnection = baseUrl.openConnection(); @Cleanup BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); - String line = reader.readLine(); - System.out.println(line); - - assertTrue("Using URLConnection, an HTTP GET from the nginx server returns the index.html from the custom content directory", line.contains("This worked")); + return reader.readLine(); } } From aa1c65b6efcc6d052fe71043d9985d69df70771f Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 2 Jan 2020 13:26:28 +0000 Subject: [PATCH 1118/5615] =?UTF-8?q?Remove=20stray=20'KB'=20suffix=20from?= =?UTF-8?q?=20docker=20build=20context=20size=20log=20mes=E2=80=A6=20(#221?= =?UTF-8?q?4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/testcontainers/images/builder/ImageFromDockerfile.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java index 98d0dda5e5c..ded64439a64 100644 --- a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java +++ b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java @@ -123,7 +123,7 @@ public void onNext(BuildResponseItem item) { tarArchive.finish(); } - log.info("Transferred {} KB to Docker daemon", FileUtils.byteCountToDisplaySize(bytesToDockerDaemon)); + log.info("Transferred {} to Docker daemon", FileUtils.byteCountToDisplaySize(bytesToDockerDaemon)); if (bytesToDockerDaemon > FileUtils.ONE_MB * 50) // warn if >50MB sent to docker daemon log.warn("A large amount of data was sent to the Docker daemon ({}). Consider using a .dockerignore file for better performance.", FileUtils.byteCountToDisplaySize(bytesToDockerDaemon)); From f9646ec48f99327a48e27a89d51c13fef5f9343d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 11 Jan 2020 15:54:07 +0000 Subject: [PATCH 1119/5615] Bump HikariCP from 3.4.1 to 3.4.2 in /modules/spock (#2236) Bumps [HikariCP](https://github.com/brettwooldridge/HikariCP) from 3.4.1 to 3.4.2. - [Release notes](https://github.com/brettwooldridge/HikariCP/releases) - [Changelog](https://github.com/brettwooldridge/HikariCP/blob/dev/CHANGES) - [Commits](https://github.com/brettwooldridge/HikariCP/compare/HikariCP-3.4.1...HikariCP-3.4.2) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 2974784d466..465547f1d6e 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -10,7 +10,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') - testCompile 'com.zaxxer:HikariCP:3.4.1' + testCompile 'com.zaxxer:HikariCP:3.4.2' testCompile 'org.apache.httpcomponents:httpclient:4.5.10' testRuntime 'org.postgresql:postgresql:42.2.9' From 46e95756e603df7054f0aec74e81c652a3c6a97e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 11 Jan 2020 15:54:29 +0000 Subject: [PATCH 1120/5615] Bump HikariCP from 3.4.1 to 3.4.2 in /modules/junit-jupiter (#2235) Bumps [HikariCP](https://github.com/brettwooldridge/HikariCP) from 3.4.1 to 3.4.2. - [Release notes](https://github.com/brettwooldridge/HikariCP/releases) - [Changelog](https://github.com/brettwooldridge/HikariCP/blob/dev/CHANGES) - [Commits](https://github.com/brettwooldridge/HikariCP/compare/HikariCP-3.4.1...HikariCP-3.4.2) Signed-off-by: dependabot-preview[bot] Co-authored-by: Richard North --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 18cc5ddfe96..0ea792ec8b3 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -6,7 +6,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') - testCompile 'com.zaxxer:HikariCP:3.4.1' + testCompile 'com.zaxxer:HikariCP:3.4.2' testCompile 'redis.clients:jedis:3.2.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.10' testCompile ('org.mockito:mockito-core:3.2.4') { From 62ce00d167924cca453a8d989a224b2eca339d35 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 11 Jan 2020 15:54:39 +0000 Subject: [PATCH 1121/5615] Bump mariadb-java-client from 2.5.2 to 2.5.3 in /modules/mariadb (#2233) Bumps [mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 2.5.2 to 2.5.3. - [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases) - [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md) - [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/2.5.2...2.5.3) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 041b0a146da..fac747aa68e 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: JDBC :: MariaDB" dependencies { compile project(':jdbc') - testCompile 'org.mariadb.jdbc:mariadb-java-client:2.5.2' + testCompile 'org.mariadb.jdbc:mariadb-java-client:2.5.3' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.30' From dab1a9481ec9ca43c298abd9de060e5ecad3e9ba Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 11 Jan 2020 15:54:51 +0000 Subject: [PATCH 1122/5615] Bump okhttp from 3.14.4 to 3.14.5 in /examples (#2230) Bumps [okhttp](https://github.com/square/okhttp) from 3.14.4 to 3.14.5. - [Release notes](https://github.com/square/okhttp/releases) - [Changelog](https://github.com/square/okhttp/blob/master/docs/changelog_3x.md) - [Commits](https://github.com/square/okhttp/compare/parent-3.14.4...parent-3.14.5) Signed-off-by: dependabot-preview[bot] Co-authored-by: null <27856297+dependabot-preview[bot]@users.noreply.github.com> --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index dea159a89b5..3e603dec908 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' - implementation 'com.squareup.okhttp3:okhttp:3.14.4' + implementation 'com.squareup.okhttp3:okhttp:3.14.5' implementation 'org.json:json:20180813' testImplementation 'org.postgresql:postgresql:42.2.9' testImplementation 'ch.qos.logback:logback-classic:1.2.3' From 6622578bed5e66bfb979b9bfeeb0e22321e21266 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 12 Jan 2020 13:01:42 -0800 Subject: [PATCH 1123/5615] Move `execInContainer` and `copy*` to `ContainerState` (#2176) * Move `execInContainer` and `copy*` to `ContainerState` * fix backward compatibility * Fix javadoc --- .../testcontainers/containers/Container.java | 55 -------- .../containers/ContainerState.java | 129 +++++++++++++++++- .../containers/GenericContainer.java | 82 +---------- 3 files changed, 130 insertions(+), 136 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/Container.java b/core/src/main/java/org/testcontainers/containers/Container.java index ef27a95a463..631e2653ceb 100644 --- a/core/src/main/java/org/testcontainers/containers/Container.java +++ b/core/src/main/java/org/testcontainers/containers/Container.java @@ -420,61 +420,6 @@ default void followOutput(Consumer consumer, OutputFrame.OutputType @Deprecated Info fetchDockerDaemonInfo() throws IOException; - /** - * Run a command inside a running container, as though using "docker exec", and interpreting - * the output as UTF8. - *

- * @see ExecInContainerPattern#execInContainer(com.github.dockerjava.api.command.InspectContainerResponse, String...) - */ - ExecResult execInContainer(String... command) - throws UnsupportedOperationException, IOException, InterruptedException; - - /** - * Run a command inside a running container, as though using "docker exec". - *

- * @see ExecInContainerPattern#execInContainer(com.github.dockerjava.api.command.InspectContainerResponse, Charset, String...) - */ - ExecResult execInContainer(Charset outputCharset, String... command) - throws UnsupportedOperationException, IOException, InterruptedException; - - /** - * - * Copies a file or directory to the container. - * - * @param mountableFile file or directory which is copied into the container - * @param containerPath destination path inside the container - * @throws IOException if there's an issue communicating with Docker - * @throws InterruptedException if the thread waiting for the response is interrupted - */ - void copyFileToContainer(MountableFile mountableFile, String containerPath) throws IOException, InterruptedException; - - /** - * - * Copies a file to the container. - * - * @param transferable file which is copied into the container - * @param containerPath destination path inside the container - */ - void copyFileToContainer(Transferable transferable, String containerPath); - - /** - * Copies a file which resides inside the container to user defined directory - * - * @param containerPath path to file which is copied from container - * @param destinationPath destination path to which file is copied with file name - * @throws IOException if there's an issue communicating with Docker or receiving entry from TarArchiveInputStream - * @throws InterruptedException if the thread waiting for the response is interrupted - */ - void copyFileFromContainer(String containerPath, String destinationPath) throws IOException, InterruptedException; - - /** - * Streams a file which resides inside the container - * - * @param containerPath path to file which is copied from container - * @param function function that takes InputStream of the copied file - */ - T copyFileFromContainer(String containerPath, ThrowingFunction function); - List getPortBindings(); List getExtraHosts(); diff --git a/core/src/main/java/org/testcontainers/containers/ContainerState.java b/core/src/main/java/org/testcontainers/containers/ContainerState.java index 2066fe72157..043bae1d52f 100644 --- a/core/src/main/java/org/testcontainers/containers/ContainerState.java +++ b/core/src/main/java/org/testcontainers/containers/ContainerState.java @@ -1,15 +1,33 @@ package org.testcontainers.containers; +import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.exception.DockerException; import com.github.dockerjava.api.model.ExposedPort; import com.github.dockerjava.api.model.PortBinding; import com.github.dockerjava.api.model.Ports; import com.google.common.base.Preconditions; +import lombok.SneakyThrows; +import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; +import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; +import org.apache.commons.compress.utils.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.output.OutputFrame; +import org.testcontainers.images.builder.Transferable; import org.testcontainers.utility.LogUtils; +import org.testcontainers.utility.MountableFile; +import org.testcontainers.utility.ThrowingFunction; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -170,10 +188,119 @@ default String getLogs(OutputFrame.OutputType... types) { /** * @return the id of the container */ - String getContainerId(); + default String getContainerId() { + return getContainerInfo().getId(); + } /** * @return the container info */ InspectContainerResponse getContainerInfo(); + + /** + * Run a command inside a running container, as though using "docker exec", and interpreting + * the output as UTF8. + *

+ * @see ExecInContainerPattern#execInContainer(com.github.dockerjava.api.command.InspectContainerResponse, String...) + */ + default Container.ExecResult execInContainer(String... command) throws UnsupportedOperationException, IOException, InterruptedException { + return execInContainer(StandardCharsets.UTF_8, command); + } + + /** + * Run a command inside a running container, as though using "docker exec". + *

+ * @see ExecInContainerPattern#execInContainer(com.github.dockerjava.api.command.InspectContainerResponse, Charset, String...) + */ + default Container.ExecResult execInContainer(Charset outputCharset, String... command) throws UnsupportedOperationException, IOException, InterruptedException { + return ExecInContainerPattern.execInContainer(getContainerInfo(), outputCharset, command); + } + + /** + * + * Copies a file or directory to the container. + * + * @param mountableFile file or directory which is copied into the container + * @param containerPath destination path inside the container + */ + default void copyFileToContainer(MountableFile mountableFile, String containerPath) { + File sourceFile = new File(mountableFile.getResolvedPath()); + + if (containerPath.endsWith("/") && sourceFile.isFile()) { + final Logger logger = LoggerFactory.getLogger(GenericContainer.class); + logger.warn("folder-like containerPath in copyFileToContainer is deprecated, please explicitly specify a file path"); + copyFileToContainer((Transferable) mountableFile, containerPath + sourceFile.getName()); + } else { + copyFileToContainer((Transferable) mountableFile, containerPath); + } + } + + /** + * + * Copies a file to the container. + * + * @param transferable file which is copied into the container + * @param containerPath destination path inside the container + */ + @SneakyThrows(IOException.class) + default void copyFileToContainer(Transferable transferable, String containerPath) { + if (!isCreated()) { + throw new IllegalStateException("copyFileToContainer can only be used with created / running container"); + } + + try ( + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + TarArchiveOutputStream tarArchive = new TarArchiveOutputStream(byteArrayOutputStream) + ) { + tarArchive.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); + + transferable.transferTo(tarArchive, containerPath); + tarArchive.finish(); + + DockerClientFactory.instance().client() + .copyArchiveToContainerCmd(getContainerId()) + .withTarInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray())) + .withRemotePath("/") + .exec(); + } + } + + /** + * Copies a file which resides inside the container to user defined directory + * + * @param containerPath path to file which is copied from container + * @param destinationPath destination path to which file is copied with file name + * @throws IOException if there's an issue communicating with Docker or receiving entry from TarArchiveInputStream + * @throws InterruptedException if the thread waiting for the response is interrupted + */ + default void copyFileFromContainer(String containerPath, String destinationPath) throws IOException, InterruptedException { + copyFileFromContainer(containerPath, inputStream -> { + try(FileOutputStream output = new FileOutputStream(destinationPath)) { + IOUtils.copy(inputStream, output); + return null; + } + }); + } + + /** + * Streams a file which resides inside the container + * + * @param containerPath path to file which is copied from container + * @param function function that takes InputStream of the copied file + */ + @SneakyThrows + default T copyFileFromContainer(String containerPath, ThrowingFunction function) { + if (!isCreated()) { + throw new IllegalStateException("copyFileFromContainer can only be used when the Container is created."); + } + + try ( + DockerClient dockerClient = DockerClientFactory.instance().client(); + InputStream inputStream = dockerClient.copyArchiveFromContainerCmd(getContainerId(), containerPath).exec(); + TarArchiveInputStream tarInputStream = new TarArchiveInputStream(inputStream) + ) { + tarInputStream.getNextTarEntry(); + return function.apply(tarInputStream); + } + } } diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 0062612c10d..ee458339d0a 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -1324,87 +1324,9 @@ public synchronized Info fetchDockerDaemonInfo() throws IOException { * {@inheritDoc} */ @Override - public ExecResult execInContainer(String... command) - throws UnsupportedOperationException, IOException, InterruptedException { - - return execInContainer(UTF8, command); - } - - /** - * {@inheritDoc} - */ - @Override - public void copyFileToContainer(MountableFile mountableFile, String containerPath) { - File sourceFile = new File(mountableFile.getResolvedPath()); - - if (containerPath.endsWith("/") && sourceFile.isFile()) { - logger().warn("folder-like containerPath in copyFileToContainer is deprecated, please explicitly specify a file path"); - copyFileToContainer((Transferable) mountableFile, containerPath + sourceFile.getName()); - } else { - copyFileToContainer((Transferable) mountableFile, containerPath); - } - } - - @Override - @SneakyThrows(IOException.class) - public void copyFileToContainer(Transferable transferable, String containerPath) { - if (!isCreated()) { - throw new IllegalStateException("copyFileToContainer can only be used with created / running container"); - } - - try ( - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - TarArchiveOutputStream tarArchive = new TarArchiveOutputStream(byteArrayOutputStream) - ) { - tarArchive.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); - - transferable.transferTo(tarArchive, containerPath); - tarArchive.finish(); - - dockerClient - .copyArchiveToContainerCmd(containerId) - .withTarInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray())) - .withRemotePath("/") - .exec(); - } - } - - /** - * {@inheritDoc} - */ - @Override + @SneakyThrows public void copyFileFromContainer(String containerPath, String destinationPath) { - copyFileFromContainer(containerPath, inputStream -> { - try(FileOutputStream output = new FileOutputStream(destinationPath)) { - IOUtils.copy(inputStream, output); - return null; - } - }); - } - - @Override - @SneakyThrows(Exception.class) - public T copyFileFromContainer(String containerPath, ThrowingFunction consumer) { - if (!isCreated()) { - throw new IllegalStateException("copyFileFromContainer can only be used when the Container is created."); - } - - try ( - InputStream inputStream = dockerClient.copyArchiveFromContainerCmd(containerId, containerPath).exec(); - TarArchiveInputStream tarInputStream = new TarArchiveInputStream(inputStream) - ) { - tarInputStream.getNextTarEntry(); - return consumer.apply(tarInputStream); - } - } - - /** - * {@inheritDoc} - */ - @Override - public ExecResult execInContainer(Charset outputCharset, String... command) - throws UnsupportedOperationException, IOException, InterruptedException { - return ExecInContainerPattern.execInContainer(getContainerInfo(), outputCharset, command); + Container.super.copyFileFromContainer(containerPath, destinationPath); } /** From 728261c7c9cdb5c02d736f31461d11717f97618a Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 13 Jan 2020 20:02:12 +0000 Subject: [PATCH 1124/5615] Bump flaky annotation review dates (#2248) --- .../couchbase/BaseCouchbaseContainerTest.java | 6 +++--- .../org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java | 2 +- .../java/org/testcontainers/containers/ToxiproxyTest.java | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java index 6deba2c4c5e..ef2f0925c48 100644 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java @@ -47,7 +47,7 @@ protected void failed(Throwable e, Description description) { }); @Test - @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1453", reviewDate = "2019-12-01") + @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1453", reviewDate = "2020-03-01") public void shouldInsertDocument() { RawJsonDocument expected = RawJsonDocument.create(ID, DOCUMENT); getBucket().upsert(expected); @@ -56,7 +56,7 @@ public void shouldInsertDocument() { } @Test - @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1453", reviewDate = "2019-12-01") + @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1453", reviewDate = "2020-03-01") public void shouldExecuteN1ql() { getBucket().query(N1qlQuery.simple("INSERT INTO " + TEST_BUCKET + " (KEY, VALUE) VALUES ('" + ID + "', " + DOCUMENT + ")")); @@ -69,7 +69,7 @@ public void shouldExecuteN1ql() { } @Test - @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1453", reviewDate = "2019-12-01") + @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1453", reviewDate = "2020-03-01") public void shouldCreateView() { View view = DefaultView.create(VIEW_NAME, VIEW_FUNCTION); DesignDocument document = DesignDocument.create(VIEW_NAME, Lists.newArrayList(view)); diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java index fb9794e29a8..07a17f68ade 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java @@ -20,7 +20,7 @@ public class DatabaseDriverTmpfsTest { public FlakyTestJUnit4RetryRule retry = new FlakyTestJUnit4RetryRule(); @Test - @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1687", reviewDate = "2019-12-01") + @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1687", reviewDate = "2020-03-01") public void tmpfs() throws IOException, InterruptedException, SQLException { final String jdbcUrl = "jdbc:tc:postgresql:9.6.8://hostname/databasename?TC_TMPFS=/testtmpfs:rw"; try (Connection ignored = DriverManager.getConnection(jdbcUrl)) { diff --git a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java index 840165fb8ee..c96e2efd19e 100644 --- a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java +++ b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java @@ -45,7 +45,7 @@ public void testDirect() { } @Test - @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1769", reviewDate = "2019-12-01") + @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1769", reviewDate = "2020-03-01") public void testLatencyViaProxy() throws IOException { // obtainProxyObject { final ToxiproxyContainer.ContainerProxy proxy = toxiproxy.getProxy(redis, 6379); @@ -72,7 +72,7 @@ public void testLatencyViaProxy() throws IOException { } @Test - @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1769", reviewDate = "2019-12-01") + @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1769", reviewDate = "2020-03-01") public void testConnectionCut() { final ToxiproxyContainer.ContainerProxy proxy = toxiproxy.getProxy(redis, 6379); final Jedis jedis = new Jedis(proxy.getContainerIpAddress(), proxy.getProxyPort()); @@ -97,7 +97,7 @@ public void testConnectionCut() { } @Test - @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1769", reviewDate = "2019-12-01") + @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1769", reviewDate = "2020-03-01") public void testMultipleProxiesCanBeCreated() { try (GenericContainer secondRedis = new GenericContainer("redis:5.0.4") .withExposedPorts(6379) From d4f6d13b4b730cbb24a82939cd6d3472e818e6d2 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 18 Jan 2020 10:15:54 +0000 Subject: [PATCH 1125/5615] Bump pulsar-client from 2.4.1 to 2.5.0 in /modules/pulsar (#2252) Bumps [pulsar-client](https://github.com/apache/pulsar) from 2.4.1 to 2.5.0. - [Release notes](https://github.com/apache/pulsar/releases) - [Commits](https://github.com/apache/pulsar/compare/v2.4.1...v2.5.0) Signed-off-by: dependabot-preview[bot] --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 046debc2b9b..81be68579d7 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Pulsar" dependencies { compile project(':testcontainers') - testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.4.1' + testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.5.0' testCompile group: 'org.assertj', name: 'assertj-core', version: '3.14.0' } From 5b48d4dbb3092a4919bcea9aa2aa034b08dbfa91 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 18 Jan 2020 10:16:04 +0000 Subject: [PATCH 1126/5615] Bump mysql-connector-java from 8.0.18 to 8.0.19 in /modules/spock (#2242) Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.18 to 8.0.19. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/commits) Signed-off-by: dependabot-preview[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 465547f1d6e..d964e67c5cf 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -14,5 +14,5 @@ dependencies { testCompile 'org.apache.httpcomponents:httpclient:4.5.10' testRuntime 'org.postgresql:postgresql:42.2.9' - testRuntime 'mysql:mysql-connector-java:8.0.18' + testRuntime 'mysql:mysql-connector-java:8.0.19' } From 2be3b44a348646d34f0e8737e825b7f32bac9220 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 18 Jan 2020 10:16:22 +0000 Subject: [PATCH 1127/5615] Bump mysql-connector-java from 8.0.18 to 8.0.19 in /modules/mysql (#2241) Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.18 to 8.0.19. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/commits) Signed-off-by: dependabot-preview[bot] Co-authored-by: Richard North --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 0b70ea3af6a..43867df521a 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: JDBC :: MySQL" dependencies { compile project(':jdbc') - testCompile 'mysql:mysql-connector-java:8.0.18' + testCompile 'mysql:mysql-connector-java:8.0.19' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.30' testCompile 'commons-dbutils:commons-dbutils:1.7' From dd5fe11e6bdae0657cc6a2551e3890f64973ce3c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 18 Jan 2020 10:17:28 +0000 Subject: [PATCH 1128/5615] Bump junit from 4.12 to 4.13 in /examples (#2220) Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/master/doc/ReleaseNotes4.12.md) - [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13) Signed-off-by: dependabot-preview[bot] --- examples/mongodb-container/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/mongodb-container/build.gradle b/examples/mongodb-container/build.gradle index 11c5f2bbe24..d3b85856b8a 100644 --- a/examples/mongodb-container/build.gradle +++ b/examples/mongodb-container/build.gradle @@ -8,5 +8,5 @@ repositories { dependencies { testImplementation 'org.testcontainers:testcontainers' - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13' } diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index b665b9a24a4..9dea8531b5e 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -12,6 +12,6 @@ dependencies { implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13' testImplementation 'ch.qos.logback:logback-classic:1.2.3' } From 9f748ab1fd1a63e5ad1509e0681be6cfe2210e2c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 18 Jan 2020 10:17:44 +0000 Subject: [PATCH 1129/5615] =?UTF-8?q?Bump=20mysql-connector-java=20from=20?= =?UTF-8?q?8.0.18=20to=208.0.19=20in=20/modules/jun=E2=80=A6=20(#2244)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.18 to 8.0.19. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/commits) Signed-off-by: dependabot-preview[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 0ea792ec8b3..a2698da90d9 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -14,7 +14,7 @@ dependencies { } testRuntime 'org.postgresql:postgresql:42.2.9' - testRuntime 'mysql:mysql-connector-java:8.0.18' + testRuntime 'mysql:mysql-connector-java:8.0.19' testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.5.2' } From 4671f3a007ab38e16fa1c3b0dd80ad899fafc2aa Mon Sep 17 00:00:00 2001 From: Michael DePouw Date: Sat, 18 Jan 2020 05:23:35 -0500 Subject: [PATCH 1130/5615] fix anchor link for link to feature description (#2257) --- docs/supported_docker_environment/windows.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/supported_docker_environment/windows.md b/docs/supported_docker_environment/windows.md index 122965ef133..b35ee1aa2ee 100644 --- a/docs/supported_docker_environment/windows.md +++ b/docs/supported_docker_environment/windows.md @@ -17,7 +17,7 @@ If you wish to use Testcontainers on Windows, please confirm that it works corre effort. ### MySQL containers -* MySQL server prevents custom configuration file (ini-script) from being loaded due to security measures ([link to feature description](../modules/databases/index.md#using-an-init-script)) +* MySQL server prevents custom configuration file (ini-script) from being loaded due to security measures ([link to feature description](../modules/databases/index.md#using-an-init-script-from-a-file)) ### Windows Container on Windows (WCOW) From 23e9718a56eeac3ca2ec87c8a2525fa8e4bf76df Mon Sep 17 00:00:00 2001 From: Per Lundberg Date: Sat, 18 Jan 2020 12:24:44 +0200 Subject: [PATCH 1131/5615] =?UTF-8?q?Fix=20/proc/net/tcp*=20check=20in=20I?= =?UTF-8?q?nternalCommandPortListeningCheck=20t=E2=80=A6=20(#2195)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wait/internal/InternalCommandPortListeningCheck.java | 2 +- .../wait/internal/InternalCommandPortListeningCheckTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java index 43ed94bc84d..2013ddc4d9c 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java +++ b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java @@ -29,7 +29,7 @@ public Boolean call() { for (int internalPort : internalPorts) { command += " && "; command += " ("; - command += format("cat /proc/net/tcp{,6} | awk '{print $2}' | grep -i :%x", internalPort); + command += format("cat /proc/net/tcp* | awk '{print $2}' | grep -i :%x", internalPort); command += " || "; command += format("nc -vz -w 1 localhost %d", internalPort); command += " || "; diff --git a/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java b/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java index 71a14c3c79e..bee4f0a231a 100644 --- a/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java +++ b/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java @@ -11,8 +11,8 @@ public class InternalCommandPortListeningCheckTest { - // Linking a custom configuration into the container so that nginx is listening on port 8080. This is necessary to proof - // that the command formatting uses the correct casing for hexadecimal numberd (i.e. 1F90 and not 1f90). + // Linking a custom configuration into the container so that nginx is listening on port 8080. This is necessary to prove + // that the command formatting uses the correct casing for hexadecimal numbers (i.e. 1F90 and not 1f90). @Rule public GenericContainer nginx = new GenericContainer<>("nginx:1.9.4") .withClasspathResourceMapping("nginx_on_8080.conf", "/etc/nginx/conf.d/default.conf", BindMode.READ_ONLY); From 7143939ed97df0d14b0d44e99bdfa7561a2dd1cb Mon Sep 17 00:00:00 2001 From: Piotr Findeisen Date: Sat, 18 Jan 2020 11:33:05 +0100 Subject: [PATCH 1132/5615] Accept List in deepStart (#2221) --- .../main/java/org/testcontainers/lifecycle/Startables.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/testcontainers/lifecycle/Startables.java b/core/src/main/java/org/testcontainers/lifecycle/Startables.java index 6cf57cc6f6e..b20c5c91b54 100644 --- a/core/src/main/java/org/testcontainers/lifecycle/Startables.java +++ b/core/src/main/java/org/testcontainers/lifecycle/Startables.java @@ -27,7 +27,7 @@ public Thread newThread(Runnable r) { /** * @see #deepStart(Stream) */ - public CompletableFuture deepStart(Collection startables) { + public CompletableFuture deepStart(Collection startables) { return deepStart(startables.stream()); } @@ -49,7 +49,7 @@ public CompletableFuture deepStart(Collection startables) { * @param startables a {@link Stream} of {@link Startable}s to start and scan for transitive dependencies. * @return a {@link CompletableFuture} that resolves once all {@link Startable}s have started. */ - public CompletableFuture deepStart(Stream startables) { + public CompletableFuture deepStart(Stream startables) { return deepStart(new HashMap<>(), startables); } @@ -58,7 +58,7 @@ public CompletableFuture deepStart(Stream startables) { * @param started an intermediate storage for already started {@link Startable}s to prevent multiple starts. * @param startables a {@link Stream} of {@link Startable}s to start and scan for transitive dependencies. */ - private CompletableFuture deepStart(Map> started, Stream startables) { + private CompletableFuture deepStart(Map> started, Stream startables) { CompletableFuture[] futures = startables .sequential() .map(it -> { From fb869c06db062abc86b4bd88400d9b2ccfb2bb18 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 19 Jan 2020 13:04:13 +0000 Subject: [PATCH 1133/5615] Prevent duplicate attempts to start Ryuk container (#2245) Prevent duplicate attempts to start Ryuk container If for example, checks fail once but the Docker client is otherwise initialized, an error would occur due to an attempt to start a Ryuk container twice with the same name. This changed is aimed at avoiding that situation. Additionally, if checks fail once they should fail on every subsequent attempt to start a client. Therefore, we cache the failure and rethrow it. Co-authored-by: Sergei Egorov --- .../testcontainers/DockerClientFactory.java | 99 +++++++++++-------- .../DockerClientFactoryTest.java | 36 +++++++ .../MockTestcontainersConfigurationRule.java | 3 + 3 files changed, 96 insertions(+), 42 deletions(-) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 0ca7d41a209..2edcdf38e24 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -17,9 +17,6 @@ import lombok.SneakyThrows; import lombok.Synchronized; import lombok.extern.slf4j.Slf4j; -import org.hamcrest.BaseMatcher; -import org.hamcrest.Description; -import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.dockerclient.DockerClientProviderStrategy; import org.testcontainers.dockerclient.DockerMachineClientProviderStrategy; import org.testcontainers.images.TimeLimitedLoggedPullImageResultCallback; @@ -68,12 +65,16 @@ public class DockerClientFactory { @VisibleForTesting DockerClient dockerClient; + @VisibleForTesting + RuntimeException cachedChecksFailure; + private String activeApiVersion; private String activeExecutionDriver; @Getter(lazy = true) private final boolean fileMountingSupported = checkMountableFile(); + static { System.setProperty("org.testcontainers.shaded.io.netty.packagePrefix", "org.testcontainers.shaded."); } @@ -124,6 +125,12 @@ public DockerClient client() { return dockerClient; } + // fail-fast if checks have failed previously + if (cachedChecksFailure != null) { + log.debug("There is a cached checks failure - throwing", cachedChecksFailure); + throw cachedChecksFailure; + } + final DockerClientProviderStrategy strategy = getOrInitializeStrategy(); String hostIpAddress = strategy.getDockerHostIpAddress(); @@ -140,33 +147,47 @@ public DockerClient client() { " Operating System: " + dockerInfo.getOperatingSystem() + "\n" + " Total Memory: " + dockerInfo.getMemTotal() / (1024 * 1024) + " MB"); - String ryukContainerId = null; + final String ryukContainerId; + boolean useRyuk = !Boolean.parseBoolean(System.getenv("TESTCONTAINERS_RYUK_DISABLED")); if (useRyuk) { + log.debug("Ryuk is enabled"); ryukContainerId = ResourceReaper.start(hostIpAddress, client); log.info("Ryuk started - will monitor and terminate Testcontainers containers on JVM exit"); + } else { + log.debug("Ryuk is disabled"); + ryukContainerId = null; } boolean checksEnabled = !TestcontainersConfiguration.getInstance().isDisableChecks(); if (checksEnabled) { - VisibleAssertions.info("Checking the system..."); - checkDockerVersion(version.getVersion()); - if (ryukContainerId != null) { - checkDiskSpace(client, ryukContainerId); - } else { - runInsideDocker( - client, - createContainerCmd -> { - createContainerCmd.withName("testcontainers-checks-" + SESSION_ID); - createContainerCmd.getHostConfig().withAutoRemove(true); - createContainerCmd.withCmd("tail", "-f", "/dev/null"); - }, - (__, containerId) -> { - checkDiskSpace(client, containerId); - return ""; - } - ); + log.debug("Checks are enabled"); + + try { + log.info("Checking the system..."); + checkDockerVersion(version.getVersion()); + if (ryukContainerId != null) { + checkDiskSpace(client, ryukContainerId); + } else { + runInsideDocker( + client, + createContainerCmd -> { + createContainerCmd.withName("testcontainers-checks-" + SESSION_ID); + createContainerCmd.getHostConfig().withAutoRemove(true); + createContainerCmd.withCmd("tail", "-f", "/dev/null"); + }, + (__, containerId) -> { + checkDiskSpace(client, containerId); + return ""; + } + ); + } + } catch (RuntimeException e) { + cachedChecksFailure = e; + throw e; } + } else { + log.debug("Checks are disabled"); } dockerClient = client; @@ -174,17 +195,8 @@ public DockerClient client() { } private void checkDockerVersion(String dockerVersion) { - VisibleAssertions.assertThat("Docker version", dockerVersion, new BaseMatcher() { - @Override - public boolean matches(Object o) { - return new ComparableVersion(o.toString()).compareTo(new ComparableVersion("1.6.0")) >= 0; - } - - @Override - public void describeTo(Description description) { - description.appendText("should be at least 1.6.0"); - } - }); + boolean versionIsSufficient = new ComparableVersion(dockerVersion).compareTo(new ComparableVersion("1.6.0")) >= 0; + check("Docker server version should be at least 1.6.0", versionIsSufficient); } private void checkDiskSpace(DockerClient dockerClient, String id) { @@ -201,12 +213,21 @@ private void checkDiskSpace(DockerClient dockerClient, String id) { DiskSpaceUsage df = parseAvailableDiskSpace(outputStream.toString()); - VisibleAssertions.assertTrue( + check( "Docker environment should have more than 2GB free disk space", df.availableMB.map(it -> it >= 2048).orElse(true) ); } + private void check(String message, boolean isSuccessful) { + if (isSuccessful) { + log.info("✔︎ {}", message); + } else { + log.error("❌ {}", message); + throw new IllegalStateException("Check failed: " + message); + } + } + private boolean checkMountableFile() { DockerClient dockerClient = client(); @@ -267,8 +288,8 @@ private T runInsideDocker(DockerClient client, Consumer } finally { try { client.removeContainerCmd(id).withRemoveVolumes(true).withForce(true).exec(); - } catch (NotFoundException | InternalServerErrorException ignored) { - log.debug("", ignored); + } catch (NotFoundException | InternalServerErrorException e) { + log.debug("Swallowed exception while removing container", e); } } } @@ -286,7 +307,7 @@ DiskSpaceUsage parseAvailableDiskSpace(String dfOutput) { for (String line : lines) { String[] fields = line.split("\\s+"); if (fields.length > 5 && fields[5].equals("/")) { - long availableKB = Long.valueOf(fields[3]); + long availableKB = Long.parseLong(fields[3]); df.availableMB = Optional.of(availableKB / 1024L); df.usedPercent = Optional.of(Integer.valueOf(fields[4].replace("%", ""))); break; @@ -318,10 +339,4 @@ public String getActiveExecutionDriver() { public boolean isUsing(Class providerStrategyClass) { return strategy != null && providerStrategyClass.isAssignableFrom(this.strategy.getClass()); } - - private static class NotEnoughDiskSpaceException extends RuntimeException { - NotEnoughDiskSpaceException(String message) { - super(message); - } - } } diff --git a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java index 19a591be919..60fd69e9fd3 100644 --- a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java +++ b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java @@ -1,19 +1,28 @@ package org.testcontainers; +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.exception.DockerException; import com.github.dockerjava.api.exception.NotFoundException; +import org.junit.Rule; import org.junit.Test; +import org.mockito.Mockito; import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.DockerClientFactory.DiskSpaceUsage; import org.testcontainers.dockerclient.LogToStringContainerCallback; +import org.testcontainers.utility.MockTestcontainersConfigurationRule; import org.testcontainers.utility.TestcontainersConfiguration; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; /** * Test for {@link DockerClientFactory}. */ public class DockerClientFactoryTest { + @Rule + public MockTestcontainersConfigurationRule configurationMock = new MockTestcontainersConfigurationRule(); + @Test public void runCommandInsideDockerShouldNotFailIfImageDoesNotExistsLocally() { @@ -52,4 +61,31 @@ public void dockerHostIpAddress() { instance.strategy = null; assertThat(instance.dockerHostIpAddress()).isNotNull(); } + + @Test + public void failedChecksFailFast() { + Mockito.doReturn(false).when(TestcontainersConfiguration.getInstance()).isDisableChecks(); + + // Make sure that Ryuk is started + assertThat(DockerClientFactory.instance().client()).isNotNull(); + + DockerClientFactory instance = new DockerClientFactory(); + DockerClient dockerClient = instance.dockerClient; + assertThat(instance.cachedChecksFailure).isNull(); + try { + // Remove cached client to force the initialization logic + instance.dockerClient = null; + + // Ryuk should fail to start twice due to the name conflict (equal to the session id) + assertThatThrownBy(instance::client).isInstanceOf(DockerException.class); + + RuntimeException failure = new IllegalStateException("Boom!"); + instance.cachedChecksFailure = failure; + // Fail fast + assertThatThrownBy(instance::client).isEqualTo(failure); + } finally { + instance.dockerClient = dockerClient; + instance.cachedChecksFailure = null; + } + } } diff --git a/core/src/test/java/org/testcontainers/utility/MockTestcontainersConfigurationRule.java b/core/src/test/java/org/testcontainers/utility/MockTestcontainersConfigurationRule.java index 4d156fd752e..ba467f13897 100644 --- a/core/src/test/java/org/testcontainers/utility/MockTestcontainersConfigurationRule.java +++ b/core/src/test/java/org/testcontainers/utility/MockTestcontainersConfigurationRule.java @@ -23,6 +23,9 @@ public Statement apply(@NotNull Statement base, @NotNull Description description @Override public void evaluate() throws Throwable { TestcontainersConfiguration previous = REF.get(); + if (previous == null) { + previous = TestcontainersConfiguration.getInstance(); + } REF.set(Mockito.spy(previous)); try { From a1dac13a4a0a2ae9f4acd97a7e9a246093ead5f3 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 19 Jan 2020 14:25:37 +0000 Subject: [PATCH 1134/5615] =?UTF-8?q?Replace=20unicode=20characters=20in?= =?UTF-8?q?=20strings=20with=20unicode=20escape=20seque=E2=80=A6=20(#2261)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/org/testcontainers/DockerClientFactory.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 2edcdf38e24..8d9da8825d2 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -221,9 +221,9 @@ private void checkDiskSpace(DockerClient dockerClient, String id) { private void check(String message, boolean isSuccessful) { if (isSuccessful) { - log.info("✔︎ {}", message); + log.info("\u2714︎ {}", message); } else { - log.error("❌ {}", message); + log.error("\u274c {}", message); throw new IllegalStateException("Check failed: " + message); } } From ad703f0503634fa6a53a915b875e145fbb1757e3 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 19 Jan 2020 20:32:00 +0000 Subject: [PATCH 1135/5615] Allow bintray upload to overwrite existing publications So that a re-run of a partially failed publication is permitted --- gradle/bintray.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/gradle/bintray.gradle b/gradle/bintray.gradle index 6990393401e..5ddfefd8e49 100644 --- a/gradle/bintray.gradle +++ b/gradle/bintray.gradle @@ -12,6 +12,7 @@ bintray { name = 'testcontainers' publish = true + override = true version { name = project.version From 92a0d8c2248e96fa589e40fbb26847744bf37ca5 Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 22 Jan 2020 21:22:48 +0000 Subject: [PATCH 1136/5615] Restore Travis Release automation (#2279) * Restore Travis Release automation Temporary, for duration of [this apparent issue](https://github.community/t5/GitHub-Actions/Read-timeout-issues-uploading-large-number-of-files-to-Bintray/m-p/42760#M5104) * Remove gradle release script temporarily * Empty commit to trigger build (Travis webhook was reinstated) --- .github/workflows/gradle-release.yml | 22 ---------------------- .travis.yml | 26 ++++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 22 deletions(-) delete mode 100644 .github/workflows/gradle-release.yml create mode 100644 .travis.yml diff --git a/.github/workflows/gradle-release.yml b/.github/workflows/gradle-release.yml deleted file mode 100644 index d35c0aa73f8..00000000000 --- a/.github/workflows/gradle-release.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Gradle Release - -on: - release: - types: [published] - -jobs: - build: - runs-on: ubuntu-18.04 - steps: - - uses: actions/checkout@v2 - - name: Set up JDK 1.8 - uses: actions/setup-java@v1 - with: - java-version: 1.8 - - name: Verify CI check status - run: .github/workflows/scripts/check_ci_status.sh $GITHUB_SHA - - name: Release with Gradle - run: ./gradlew -Pversion=${GITHUB_REF##*/} release --scan --no-daemon -i - env: - BINTRAY_USER: ${{ secrets.BINTRAY_USER }} - BINTRAY_API_KEY: ${{ secrets.BINTRAY_API_KEY }} diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000000..daf52bf1aa5 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,26 @@ +if: branch = master AND tag is present + +language: java +jdk: + - openjdk8 + +sudo: required +services: + - docker + +jobs: + include: + - stage: before_deploy + script: .github/workflows/scripts/check_ci_status.sh $TRAVIS_COMMIT + + - stage: deploy + sudo: false + services: [] + install: skip + script: skip + deploy: + provider: script + script: ./gradlew -Pversion=$TRAVIS_TAG release --scan --no-daemon -i + on: + tags: true + branch: master From 97d2bf1a305a9b2ae701cfe645254327be2657b0 Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 22 Jan 2020 21:55:08 +0000 Subject: [PATCH 1137/5615] Simplify conditional travis execution --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index daf52bf1aa5..d05af36ac15 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -if: branch = master AND tag is present +if: tag is present language: java jdk: From 2a56996fe688bdd191299847a9286a5faa66830e Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 22 Jan 2020 22:02:44 +0000 Subject: [PATCH 1138/5615] Remove CI status check --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index d05af36ac15..ec7a5d343d9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,8 +10,6 @@ services: jobs: include: - - stage: before_deploy - script: .github/workflows/scripts/check_ci_status.sh $TRAVIS_COMMIT - stage: deploy sudo: false From 63f60423fa31fbd3b1d7f17221f17f5d2b6daf33 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2020 19:22:51 +0000 Subject: [PATCH 1139/5615] Bump mongo-java-driver from 3.12.0 to 3.12.1 in /core (#2264) Bumps [mongo-java-driver](https://github.com/mongodb/mongo-java-driver) from 3.12.0 to 3.12.1. - [Release notes](https://github.com/mongodb/mongo-java-driver/releases) - [Commits](https://github.com/mongodb/mongo-java-driver/compare/r3.12.0...r3.12.1) Signed-off-by: dependabot-preview[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 2d76c38c0de..d7d9c581c1f 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -108,7 +108,7 @@ dependencies { testCompile 'org.apache.httpcomponents:httpclient:4.5.9' testCompile 'redis.clients:jedis:3.2.0' testCompile 'com.rabbitmq:amqp-client:5.8.0' - testCompile 'org.mongodb:mongo-java-driver:3.12.0' + testCompile 'org.mongodb:mongo-java-driver:3.12.1' testCompile ('org.mockito:mockito-core:3.2.4') { exclude(module: 'hamcrest-core') } From 1e645e129d17eed31d1989aee4099f7b8b8f82af Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2020 19:23:37 +0000 Subject: [PATCH 1140/5615] Bump org.springframework.boot in /examples (#2273) Bumps [org.springframework.boot](https://github.com/spring-projects/spring-boot) from 2.2.2.RELEASE to 2.2.4.RELEASE. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.2.2.RELEASE...v2.2.4.RELEASE) Signed-off-by: dependabot-preview[bot] --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index 5b6d71934b9..db570bf67ac 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.2.2.RELEASE' + id 'org.springframework.boot' version '2.2.4.RELEASE' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index bea11bf0909..e74e04a95e5 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.2.2.RELEASE' + id 'org.springframework.boot' version '2.2.4.RELEASE' } apply plugin: 'io.spring.dependency-management' From 8a2b77cf5d7e6e7fdf6073eb93aadf2c06c1e9ce Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2020 19:23:51 +0000 Subject: [PATCH 1141/5615] Bump okhttp from 3.14.5 to 3.14.6 in /examples (#2268) Bumps [okhttp](https://github.com/square/okhttp) from 3.14.5 to 3.14.6. - [Release notes](https://github.com/square/okhttp/releases) - [Changelog](https://github.com/square/okhttp/blob/master/docs/changelog_3x.md) - [Commits](https://github.com/square/okhttp/compare/parent-3.14.5...parent-3.14.6) Signed-off-by: dependabot-preview[bot] --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 3e603dec908..f1977ea2c0b 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' - implementation 'com.squareup.okhttp3:okhttp:3.14.5' + implementation 'com.squareup.okhttp3:okhttp:3.14.6' implementation 'org.json:json:20180813' testImplementation 'org.postgresql:postgresql:42.2.9' testImplementation 'ch.qos.logback:logback-classic:1.2.3' From 9b27f6bb52ed55a5a14552dfa04f71ed41c7a5fc Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2020 19:24:08 +0000 Subject: [PATCH 1142/5615] Bump rest-assured from 4.1.2 to 4.2.0 in /modules/vault (#2266) Bumps [rest-assured](https://github.com/jayway/rest-assured) from 4.1.2 to 4.2.0. - [Release notes](https://github.com/jayway/rest-assured/releases) - [Changelog](https://github.com/rest-assured/rest-assured/blob/master/changelog.txt) - [Commits](https://github.com/jayway/rest-assured/compare/rest-assured-4.1.2...rest-assured-4.2.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: Richard North --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index 0a68c9310e0..ac72d306a5c 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -4,7 +4,7 @@ dependencies { compile project(':testcontainers') testCompile 'com.bettercloud:vault-java-driver:5.1.0' - testCompile 'io.rest-assured:rest-assured:4.1.2' + testCompile 'io.rest-assured:rest-assured:4.2.0' testCompile 'org.assertj:assertj-core:3.14.0' } From a9006ae80e61f78bcadd970cdba9117727536b2d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2020 19:24:19 +0000 Subject: [PATCH 1143/5615] Bump httpclient from 4.5.10 to 4.5.11 in /modules/junit-jupiter (#2265) Bumps httpclient from 4.5.10 to 4.5.11. Signed-off-by: dependabot-preview[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index a2698da90d9..7b29e36b4dd 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -8,7 +8,7 @@ dependencies { testCompile project(':postgresql') testCompile 'com.zaxxer:HikariCP:3.4.2' testCompile 'redis.clients:jedis:3.2.0' - testCompile 'org.apache.httpcomponents:httpclient:4.5.10' + testCompile 'org.apache.httpcomponents:httpclient:4.5.11' testCompile ('org.mockito:mockito-core:3.2.4') { exclude(module: 'hamcrest-core') } From fa1f8ce05365ac0c6cd4893dc476c5b43279b607 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2020 19:24:28 +0000 Subject: [PATCH 1144/5615] Bump httpclient from 4.5.10 to 4.5.11 in /modules/spock (#2263) Bumps httpclient from 4.5.10 to 4.5.11. Signed-off-by: dependabot-preview[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index d964e67c5cf..f26858947ce 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -11,7 +11,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') testCompile 'com.zaxxer:HikariCP:3.4.2' - testCompile 'org.apache.httpcomponents:httpclient:4.5.10' + testCompile 'org.apache.httpcomponents:httpclient:4.5.11' testRuntime 'org.postgresql:postgresql:42.2.9' testRuntime 'mysql:mysql-connector-java:8.0.19' From bd148a9291e9e840fe5ed2c3f8ae8de2490eff8f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2020 19:24:36 +0000 Subject: [PATCH 1145/5615] Bump elasticsearch-rest-client in /modules/elasticsearch (#2277) Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 7.5.1 to 7.5.2. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.5.1...v7.5.2) Signed-off-by: dependabot-preview[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 2201efff48a..41d5b0f27ab 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,6 +2,6 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') - testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.5.1" + testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.5.2" testCompile "org.elasticsearch.client:transport:6.7.1" } From 3b83ce9017e58255fb3087e5f6fde31db0bb9ba8 Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 24 Jan 2020 09:57:53 +0000 Subject: [PATCH 1146/5615] Fix CI Docker Compose test failure (#2284) ``` org.testcontainers.junit.DockerComposeContainerWithBuildTest > performTest[removeMode = ALL] FAILED java.lang.RuntimeException: java.lang.AssertionError: the pulled image is present after running: 'true' does not equal expected 'false' at org.rnorth.ducttape.timeouts.Timeouts.callFuture(Timeouts.java:68) at org.rnorth.ducttape.timeouts.Timeouts.getWithTimeout(Timeouts.java:43) at org.rnorth.ducttape.unreliables.Unreliables.retryUntilSuccess(Unreliables.java:40) at org.testcontainers.junit.DockerComposeContainerWithBuildTest.performTest(DockerComposeContainerWithBuildTest.java:72) Caused by: java.lang.AssertionError: the pulled image is present after running: 'true' does not equal expected 'false' at org.rnorth.visibleassertions.VisibleAssertions.fail(VisibleAssertions.java:440) at org.rnorth.visibleassertions.VisibleAssertions.assertEquals(VisibleAssertions.java:174) at org.testcontainers.junit.DockerComposeContainerWithBuildTest.lambda$performTest$1(DockerComposeContainerWithBuildTest.java:74) ``` Hypothesis: docker-compose cannot remove the `redis` (latest) image because there is some other running container that relies on that image. This may be a CI-specific problem depending on the Azure Pipelines Docker setup. To work around (without publishing our own single-use image): use an old version of the Redis image that is unlikely to be in use by concurrent builds. --- core/src/test/resources/compose-build-test/Dockerfile | 2 +- core/src/test/resources/compose-build-test/docker-compose.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/test/resources/compose-build-test/Dockerfile b/core/src/test/resources/compose-build-test/Dockerfile index 521e2d23b80..f5df49b0152 100644 --- a/core/src/test/resources/compose-build-test/Dockerfile +++ b/core/src/test/resources/compose-build-test/Dockerfile @@ -1,3 +1,3 @@ -FROM redis +FROM redis:2.6.17 CMD ["redis-server"] diff --git a/core/src/test/resources/compose-build-test/docker-compose.yml b/core/src/test/resources/compose-build-test/docker-compose.yml index 333b2fa46f1..5b9f3e2c21b 100644 --- a/core/src/test/resources/compose-build-test/docker-compose.yml +++ b/core/src/test/resources/compose-build-test/docker-compose.yml @@ -3,4 +3,4 @@ services: customredis: build: . normalredis: - image: redis:latest + image: redis:2.6.17 From 32bd7a449732dee460787d1a7b92aab3e5a26167 Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 24 Jan 2020 19:31:53 +0000 Subject: [PATCH 1147/5615] Update docs version to match latest release --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index eaec3ae13a1..189bc3b6788 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -84,4 +84,4 @@ nav: - contributing.md - contributing_docs.md extra: - latest_version: 1.12.3 + latest_version: 1.12.5 From 142e585b567f7bac6b9dc97418634362e7ff9d70 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 24 Jan 2020 20:36:51 +0100 Subject: [PATCH 1148/5615] Mention JDBC URL change in Spring Boot 2.3.0 (#2286) See https://github.com/spring-projects/spring-boot/pull/19044 --- docs/modules/databases/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/databases/index.md b/docs/modules/databases/index.md index 2511502f650..32026953802 100644 --- a/docs/modules/databases/index.md +++ b/docs/modules/databases/index.md @@ -45,7 +45,7 @@ As long as you have Testcontainers and the appropriate JDBC driver on your class _N.B:_ * _TC needs to be on your application's classpath at runtime for this to work_ -* _For Spring Boot you need to specify the driver manually `spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver`_ +* _For Spring Boot (Before version `2.3.0`) you need to specify the driver manually `spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver`_ **Original URL**: `jdbc:mysql:5.7.22://localhost:3306/databasename` From a1b6641d6de0b1542421e79f2de6c42d08f26870 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 29 Jan 2020 17:34:35 +0000 Subject: [PATCH 1149/5615] Bump assertj-core from 3.14.0 to 3.15.0 in /modules/database-commons (#2307) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.14.0 to 3.15.0. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.14.0...assertj-core-3.15.0) Signed-off-by: dependabot-preview[bot] --- modules/database-commons/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/database-commons/build.gradle b/modules/database-commons/build.gradle index ea3e0b07360..d8738a6cb69 100644 --- a/modules/database-commons/build.gradle +++ b/modules/database-commons/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: Database-Commons" dependencies { compile project(':testcontainers') - testCompile 'org.assertj:assertj-core:3.14.0' + testCompile 'org.assertj:assertj-core:3.15.0' } From 3854933cfe94eca9976a0e7086fb8bc066b39c0a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 29 Jan 2020 17:34:57 +0000 Subject: [PATCH 1150/5615] Bump assertj-core from 3.14.0 to 3.15.0 in /modules/neo4j (#2306) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.14.0 to 3.15.0. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.14.0...assertj-core-3.15.0) Signed-off-by: dependabot-preview[bot] --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 00e1e36f8b7..9361824fdd6 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -30,5 +30,5 @@ dependencies { compile project(":testcontainers") testCompile "org.neo4j.driver:neo4j-java-driver:1.7.5" - testCompile 'org.assertj:assertj-core:3.14.0' + testCompile 'org.assertj:assertj-core:3.15.0' } From 110d700950a7ac7f651d3ae2c4c3bed2d40ab5fa Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 29 Jan 2020 17:35:38 +0000 Subject: [PATCH 1151/5615] Bump assertj-core from 3.14.0 to 3.15.0 in /modules/pulsar (#2305) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.14.0 to 3.15.0. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.14.0...assertj-core-3.15.0) Signed-off-by: dependabot-preview[bot] --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 81be68579d7..88d783fc221 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -4,5 +4,5 @@ dependencies { compile project(':testcontainers') testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.5.0' - testCompile group: 'org.assertj', name: 'assertj-core', version: '3.14.0' + testCompile group: 'org.assertj', name: 'assertj-core', version: '3.15.0' } From 3dbc728ac1ae757b497e338fdefa4633e6546ce8 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 29 Jan 2020 17:36:18 +0000 Subject: [PATCH 1152/5615] Bump assertj-core from 3.14.0 to 3.15.0 in /modules/vault (#2304) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.14.0 to 3.15.0. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.14.0...assertj-core-3.15.0) Signed-off-by: dependabot-preview[bot] --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index ac72d306a5c..1c8a4083b9c 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -5,6 +5,6 @@ dependencies { testCompile 'com.bettercloud:vault-java-driver:5.1.0' testCompile 'io.rest-assured:rest-assured:4.2.0' - testCompile 'org.assertj:assertj-core:3.14.0' + testCompile 'org.assertj:assertj-core:3.15.0' } From bbc82dd14a9032909e7c1c4a4aaf3c3e499611c3 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 29 Jan 2020 17:36:43 +0000 Subject: [PATCH 1153/5615] Bump assertj-core from 3.14.0 to 3.15.0 in /modules/kafka (#2303) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.14.0 to 3.15.0. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.14.0...assertj-core-3.15.0) Signed-off-by: dependabot-preview[bot] --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index d064e46c1dc..3960cd96b69 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -4,6 +4,6 @@ dependencies { compile project(':testcontainers') testCompile 'org.apache.kafka:kafka-clients:2.4.0' - testCompile 'org.assertj:assertj-core:3.14.0' + testCompile 'org.assertj:assertj-core:3.15.0' testCompile 'com.google.guava:guava:23.0' } From 8f3f4af5b1219d22b4b3367e020d41984b2bf3f1 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 29 Jan 2020 17:36:51 +0000 Subject: [PATCH 1154/5615] Bump assertj-core from 3.14.0 to 3.15.0 in /core (#2308) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.14.0 to 3.15.0. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.14.0...assertj-core-3.15.0) Signed-off-by: dependabot-preview[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index d7d9c581c1f..25aaa396a60 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -115,7 +115,7 @@ dependencies { // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest testCompile files('testlib/repo/fakejar/fakejar/0/fakejar-0.jar') - testCompile 'org.assertj:assertj-core:3.14.0' + testCompile 'org.assertj:assertj-core:3.15.0' testCompile project(':test-support') jarFileTestCompileOnly "org.projectlombok:lombok:${lombok.version}" From 7a4d4c668ffefa642e80750b7e53eba069e99af8 Mon Sep 17 00:00:00 2001 From: dark Date: Thu, 30 Jan 2020 02:37:42 +0900 Subject: [PATCH 1155/5615] Add junit-jupiter dependency in junit 5 quickstart docs (#2301) Co-authored-by: dark --- docs/quickstart/junit_5_quickstart.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/quickstart/junit_5_quickstart.md b/docs/quickstart/junit_5_quickstart.md index 5a07af24731..179a87a5448 100644 --- a/docs/quickstart/junit_5_quickstart.md +++ b/docs/quickstart/junit_5_quickstart.md @@ -27,6 +27,7 @@ testCompile "org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion" testCompile "org.junit.jupiter:junit-jupiter-params:$junitJupiterVersion" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion" testCompile "org.testcontainers:testcontainers:{{latest_version}}" +testCompile "org.testcontainers:junit-jupiter:{{latest_version}}" ``` ```xml tab='Maven' From 85f1b5978803283d723247ed025c3118bbaa4352 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 29 Jan 2020 17:38:00 +0000 Subject: [PATCH 1156/5615] Bump mariadb-java-client from 2.5.3 to 2.5.4 in /modules/mariadb (#2298) Bumps [mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 2.5.3 to 2.5.4. - [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases) - [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md) - [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/2.5.3...2.5.4) Signed-off-by: dependabot-preview[bot] --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index fac747aa68e..c7d1dc96676 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: JDBC :: MariaDB" dependencies { compile project(':jdbc') - testCompile 'org.mariadb.jdbc:mariadb-java-client:2.5.3' + testCompile 'org.mariadb.jdbc:mariadb-java-client:2.5.4' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.30' From ae3cbc4620aeda338a853efaa8f9590f4f06e31f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 29 Jan 2020 17:38:50 +0000 Subject: [PATCH 1157/5615] =?UTF-8?q?Bump=20junit-jupiter-api=20from=205.5?= =?UTF-8?q?.2=20to=205.6.0=20in=20/modules/junit-ju=E2=80=A6=20(#2271)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Bump junit-jupiter-api from 5.5.2 to 5.6.0 in /modules/junit-jupiter Bumps [junit-jupiter-api](https://github.com/junit-team/junit5) from 5.5.2 to 5.6.0. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.5.2...r5.6.0) Signed-off-by: dependabot-preview[bot] * Match engine and API versions to fix compile error Co-authored-by: Richard North --- modules/junit-jupiter/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 7b29e36b4dd..7bb6393a586 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -2,7 +2,7 @@ description = "Testcontainers :: JUnit Jupiter Extension" dependencies { compile project(':testcontainers') - compile 'org.junit.jupiter:junit-jupiter-api:5.5.2' + compile 'org.junit.jupiter:junit-jupiter-api:5.6.0' testCompile project(':mysql') testCompile project(':postgresql') @@ -15,7 +15,7 @@ dependencies { testRuntime 'org.postgresql:postgresql:42.2.9' testRuntime 'mysql:mysql-connector-java:8.0.19' - testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.5.2' + testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.6.0' } test { From 797c39c81bc125489ccc728d0c4f576002016919 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 5 Feb 2020 08:30:47 +0000 Subject: [PATCH 1158/5615] Bump java-client from 2.7.11 to 2.7.12 in /modules/couchbase (#2323) Bumps [java-client](https://github.com/couchbase/couchbase-java-client) from 2.7.11 to 2.7.12. - [Release notes](https://github.com/couchbase/couchbase-java-client/releases) - [Commits](https://github.com/couchbase/couchbase-java-client/compare/2.7.11...2.7.12) Signed-off-by: dependabot-preview[bot] --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 27074f1bff7..d6a7e51681e 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -2,7 +2,7 @@ description = "Testcontainers :: Couchbase" dependencies { compile project(':testcontainers') - compile 'com.couchbase.client:java-client:2.7.11' + compile 'com.couchbase.client:java-client:2.7.12' testCompile project(':test-support') } From a2995478699597d6a61fc2b475ff6f8ff8937275 Mon Sep 17 00:00:00 2001 From: worldtiki Date: Wed, 5 Feb 2020 08:38:40 +0000 Subject: [PATCH 1159/5615] =?UTF-8?q?Add=20small=20delay=20in=20while=20lo?= =?UTF-8?q?op=20to=20avoid=20spamming=20logs=20(ResourceR=E2=80=A6=20(#228?= =?UTF-8?q?7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add small delay in while loop to avoid spamming logs (ResourceReaper) * Add small delay in while loop to avoid spamming logs (ResourceReaper) - Replaced Thread.sleep with RateLimiter --- .../utility/ResourceReaper.java | 62 +++++++++++-------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index f46db952c37..f74905e60ef 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -15,10 +15,11 @@ import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import org.rnorth.ducttape.ratelimits.RateLimiter; +import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testcontainers.DockerClientFactory; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -49,6 +50,11 @@ public final class ResourceReaper { private static final Logger LOGGER = LoggerFactory.getLogger(ResourceReaper.class); private static final List>> DEATH_NOTE = new ArrayList<>(); + private static final RateLimiter RYUK_ACK_RATE_LIMITER = RateLimiterBuilder + .newBuilder() + .withRate(4, TimeUnit.SECONDS) + .withConstantThroughput() + .build(); private static ResourceReaper instance; private final DockerClient dockerClient; @@ -110,34 +116,36 @@ public static String start(String hostIpAddress, DockerClient client) { DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, () -> { while (true) { - int index = 0; - try(Socket clientSocket = new Socket(hostIpAddress, ryukPort)) { - FilterRegistry registry = new FilterRegistry(clientSocket.getInputStream(), clientSocket.getOutputStream()); - - synchronized (DEATH_NOTE) { - while (true) { - if (DEATH_NOTE.size() <= index) { - try { - DEATH_NOTE.wait(1_000); - continue; - } catch (InterruptedException e) { - throw new RuntimeException(e); + RYUK_ACK_RATE_LIMITER.doWhenReady(() -> { + int index = 0; + try(Socket clientSocket = new Socket(hostIpAddress, ryukPort)) { + FilterRegistry registry = new FilterRegistry(clientSocket.getInputStream(), clientSocket.getOutputStream()); + + synchronized (DEATH_NOTE) { + while (true) { + if (DEATH_NOTE.size() <= index) { + try { + DEATH_NOTE.wait(1_000); + continue; + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + List> filters = DEATH_NOTE.get(index); + boolean isAcknowledged = registry.register(filters); + if (isAcknowledged) { + log.debug("Received 'ACK' from Ryuk"); + ryukScheduledLatch.countDown(); + index++; + } else { + log.debug("Didn't receive 'ACK' from Ryuk. Will retry to send filters."); } - } - List> filters = DEATH_NOTE.get(index); - boolean isAcknowledged = registry.register(filters); - if (isAcknowledged) { - log.debug("Received 'ACK' from Ryuk"); - ryukScheduledLatch.countDown(); - index++; - } else { - log.debug("Didn't receive 'ACK' from Ryuk. Will retry to send filters."); } } + } catch (IOException e) { + log.warn("Can not connect to Ryuk at {}:{}", hostIpAddress, ryukPort, e); } - } catch (IOException e) { - log.warn("Can not connect to Ryuk at {}:{}", hostIpAddress, ryukPort, e); - } + }); } }, "testcontainers-ryuk" @@ -341,12 +349,12 @@ public void unregisterNetwork(String identifier) { public void unregisterContainer(String identifier) { registeredContainers.remove(identifier); } - + public void registerImageForCleanup(String dockerImageName) { setHook(); registeredImages.add(dockerImageName); } - + private void removeImage(String dockerImageName) { LOGGER.trace("Removing image tagged {}", dockerImageName); try { From d4d9bea7c80fadf2e70cb90b54d2bea8dd8d6d48 Mon Sep 17 00:00:00 2001 From: MariuszCwikla <47976407+MariuszCwikla@users.noreply.github.com> Date: Wed, 5 Feb 2020 09:43:31 +0100 Subject: [PATCH 1160/5615] Fix localstack cloudwatchlogs service (#2316) --- modules/localstack/build.gradle | 1 + .../localstack/LocalStackContainer.java | 2 +- .../localstack/LocalstackContainerTest.java | 31 +++++++++++++++++-- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 819419c57ee..6d87d282d90 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,4 +6,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.683' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.683' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.636' + testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.636' } diff --git a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java index 36eaacba89b..e21cc735447 100644 --- a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java +++ b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java @@ -154,7 +154,7 @@ public enum Service { SSM("ssm", 4583), SECRETSMANAGER("secretsmanager", 4584), STEPFUNCTIONS("stepfunctions", 4585), - CLOUDWATCHLOGS("cloudwatchlogs", 4586), + CLOUDWATCHLOGS("logs", 4586), STS("sts", 4592), IAM("iam", 4593); diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java index 1b222c0fbc7..7ad31f6340f 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java @@ -1,6 +1,12 @@ package org.testcontainers.containers.localstack; +import com.amazonaws.services.logs.AWSLogs; +import com.amazonaws.services.logs.AWSLogsClientBuilder; +import com.amazonaws.services.logs.model.CreateLogGroupRequest; +import com.amazonaws.services.logs.model.CreateLogGroupResult; +import com.amazonaws.services.logs.model.DescribeLogGroupsRequest; +import com.amazonaws.services.logs.model.LogGroup; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3ClientBuilder; import com.amazonaws.services.s3.model.Bucket; @@ -13,6 +19,7 @@ import org.apache.commons.io.IOUtils; import org.junit.Assert; import org.junit.ClassRule; +import org.junit.Ignore; import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; @@ -31,6 +38,7 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; import static org.testcontainers.containers.localstack.LocalStackContainer.Service.S3; import static org.testcontainers.containers.localstack.LocalStackContainer.Service.SQS; +import static org.testcontainers.containers.localstack.LocalStackContainer.Service.CLOUDWATCHLOGS; /** * Tests for Localstack Container, used both in bridge network (exposed to host) and docker network modes. @@ -48,7 +56,7 @@ public static class WithoutNetwork { // without_network { @ClassRule public static LocalStackContainer localstack = new LocalStackContainer() - .withServices(S3, SQS); + .withServices(S3, SQS, CLOUDWATCHLOGS); // } @Test @@ -95,6 +103,20 @@ public void sqsTestOverBridgeNetwork() { .count(); assertEquals("the sent message can be received", 1L, messageCount); } + + @Test + @Ignore("Fails due to https://github.com/localstack/localstack/issues/1434") + public void cloudWatchLogsTestOverBridgeNetwork() { + AWSLogs logs = AWSLogsClientBuilder.standard() + .withEndpointConfiguration(localstack.getEndpointConfiguration(CLOUDWATCHLOGS)) + .withCredentials(localstack.getDefaultCredentialsProvider()).build(); + + logs.createLogGroup(new CreateLogGroupRequest("foo")); + + List groups = logs.describeLogGroups().getLogGroups(); + assertEquals("One log group should be created", 1, groups.size()); + assertEquals("Name of created log group is [foo]", "foo", groups.get(0).getLogGroupName()); + } } public static class WithNetwork { @@ -105,7 +127,7 @@ public static class WithNetwork { public static LocalStackContainer localstackInDockerNetwork = new LocalStackContainer() .withNetwork(network) .withNetworkAliases("notthis", "localstack") // the last alias is used for HOSTNAME_EXTERNAL - .withServices(S3, SQS); + .withServices(S3, SQS, CLOUDWATCHLOGS); // } @ClassRule @@ -139,6 +161,11 @@ public void sqsTestOverDockerNetwork() throws Exception { assertTrue("the sent message can be received", message.contains("\"Body\": \"test\"")); } + @Test + public void cloudWatchLogsTestOverDockerNetwork() throws Exception { + runAwsCliAgainstDockerNetworkContainer("logs create-log-group --log-group-name foo", CLOUDWATCHLOGS.getPort()); + } + private String runAwsCliAgainstDockerNetworkContainer(String command, final int port) throws Exception { final String[] commandParts = String.format("/usr/bin/aws --region eu-west-1 %s --endpoint-url http://localstack:%d --no-verify-ssl", command, port).split(" "); final Container.ExecResult execResult = awsCliInDockerNetwork.execInContainer(commandParts); From 7cb8ef5850533717b78d78381d40fd7af0b8bf1a Mon Sep 17 00:00:00 2001 From: Roberto Franchini Date: Wed, 5 Feb 2020 10:18:02 +0100 Subject: [PATCH 1161/5615] adds OrientDB 3.0.x module (#1414) Co-authored-by: Richard North Co-authored-by: Sergei Egorov --- docs/modules/databases/orientdb.md | 78 +++++++++ modules/orientdb/build.gradle | 11 ++ .../containers/OrientDBContainer.java | 153 ++++++++++++++++++ .../containers/OrientDBContainerTest.java | 73 +++++++++ .../src/test/resources/initscript.osql | 6 + .../src/test/resources/logback-test.xml | 18 +++ 6 files changed, 339 insertions(+) create mode 100644 docs/modules/databases/orientdb.md create mode 100644 modules/orientdb/build.gradle create mode 100644 modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java create mode 100644 modules/orientdb/src/test/java/org/testcontainers/containers/OrientDBContainerTest.java create mode 100644 modules/orientdb/src/test/resources/initscript.osql create mode 100644 modules/orientdb/src/test/resources/logback-test.xml diff --git a/docs/modules/databases/orientdb.md b/docs/modules/databases/orientdb.md new file mode 100644 index 00000000000..80ed96f9548 --- /dev/null +++ b/docs/modules/databases/orientdb.md @@ -0,0 +1,78 @@ +# OrientDB Module + +!!! note + This module is INCUBATING. While it is ready for use and operational in the current version of Testcontainers, it is possible that it may receive breaking changes in the future. See [our contributing guidelines](/contributing/#incubating-modules) for more information on our incubating modules policy. + + +This module helps running [OrientDB](https://orientdb.org/download) using Testcontainers. + +Note that it's based on the [official Docker image](https://hub.docker.com/_/orientdb/) provided by OrientDB. + +## Usage example + +Declare your Testcontainer as a `@ClassRule` or `@Rule` in a JUnit 4 test or as static or member attribute of a JUnit 5 test annotated with `@Container` as you would with other Testcontainers. +You can call `getDbUrl()` OrientDB container and build the `ODatabaseSession` by your own, but a more useful `getSession()` method is provided. +On the JVM you would most likely use the [Java driver](https://github.com/). + +The following example uses the JUnit 5 extension `@Testcontainers` and demonstrates both the usage of the Java Client: + +```java tab="JUnit 5 example" +@Testcontainers +public class ExampleTest { + + @Container + private static OrientDBContainer container = new OrientDBContainer(); + + @Test + void testDbCreation() { + + final ODatabaseSession session = container.getSession(); + + session.command("CREATE CLASS Person EXTENDS V"); + session.command("INSERT INTO Person set name='john'"); + session.command("INSERT INTO Person set name='jane'"); + + assertThat(session.query("SELECT FROM Person").stream()).hasSize(2); + } + +} +``` + +You are not limited to Unit tests and can of course use an instance of the OrientDB Testcontainer in vanilla Java code as well. + + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:orientdb:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + orientdb + {{latest_version}} + test + +``` + +!!! hint + Add the OrientDB Java client if you plan to access the Testcontainer: + + ```groovy tab='Gradle' + compile "com.orientechnologies:orientdb-client:3.0.24" + ``` + + ```xml tab='Maven' + + com.orientechnologies + orientdb-client + 3.0.24 + + ``` + + + + diff --git a/modules/orientdb/build.gradle b/modules/orientdb/build.gradle new file mode 100644 index 00000000000..ffa1350172a --- /dev/null +++ b/modules/orientdb/build.gradle @@ -0,0 +1,11 @@ +description = "TestContainers :: Orientdb" + +dependencies { + compile project(":testcontainers") + + compile "com.orientechnologies:orientdb-client:3.0.24" + + testCompile 'org.assertj:assertj-core:3.12.0' + testCompile 'org.apache.tinkerpop:gremlin-driver:3.3.4' + testCompile "com.orientechnologies:orientdb-gremlin:3.0.18" +} diff --git a/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java b/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java new file mode 100644 index 00000000000..b368d96bf8c --- /dev/null +++ b/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java @@ -0,0 +1,153 @@ +package org.testcontainers.containers; + +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.orientechnologies.orient.core.db.ODatabaseSession; +import com.orientechnologies.orient.core.db.ODatabaseType; +import com.orientechnologies.orient.core.db.OrientDB; +import com.orientechnologies.orient.core.db.OrientDBConfig; +import lombok.NonNull; +import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.containers.wait.strategy.WaitAllStrategy; +import org.testcontainers.containers.wait.strategy.WaitStrategy; + +import java.io.IOException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.Optional; + +import static java.net.HttpURLConnection.HTTP_OK; + +/** + * @author robfrank + */ +public class OrientDBContainer extends GenericContainer { + private static final Logger LOGGER = LoggerFactory.getLogger(OrientDBContainer.class); + + private static final String DEFAULT_IMAGE_NAME = "orientdb"; + private static final String DEFAULT_TAG = "3.0.24-tp3"; + private static final String DOCKER_IMAGE_NAME = DEFAULT_IMAGE_NAME + ":" + DEFAULT_TAG; + + private static final String DEFAULT_USERNAME = "admin"; + private static final String DEFAULT_PASSWORD = "admin"; + private static final String DEFAULT_SERVER_PASSWORD = "root"; + + private static final String DEFAULT_DATABASE_NAME = "testcontainers"; + + private static final int DEFAULT_BINARY_PORT = 2424; + private static final int DEFAULT_HTTP_PORT = 2480; + + private String databaseName; + private String serverPassword; + private Optional scriptPath = Optional.empty(); + + private OrientDB orientDB; + private ODatabaseSession session; + + public OrientDBContainer() { + this(DOCKER_IMAGE_NAME); + } + + public OrientDBContainer(@NonNull String dockerImageName) { + super(dockerImageName); + + serverPassword = DEFAULT_SERVER_PASSWORD; + databaseName = DEFAULT_DATABASE_NAME; + + WaitStrategy waitForHttp = new HttpWaitStrategy() + .forPort(DEFAULT_HTTP_PORT) + .forStatusCodeMatching(response -> response == HTTP_OK); + + waitStrategy = new WaitAllStrategy() + .withStrategy(Wait.forListeningPort()) + .withStrategy(waitForHttp) + .withStartupTimeout(Duration.ofMinutes(2)); + } + + @Override + protected void configure() { + addExposedPorts(DEFAULT_BINARY_PORT, DEFAULT_HTTP_PORT); + addEnv("ORIENTDB_ROOT_PASSWORD", serverPassword); + } + + public String getDatabaseName() { + return databaseName; + } + + public String getTestQueryString() { + return "SELECT FROM V"; + } + + public OrientDBContainer withDatabaseName(final String databaseName) { + this.databaseName = databaseName; + return self(); + } + + public OrientDBContainer withServerPassword(final String serverPassword) { + this.serverPassword = serverPassword; + return self(); + } + + public OrientDBContainer withScriptPath(String scriptPath) { + this.scriptPath = Optional.of(scriptPath); + return self(); + } + + @Override + protected void containerIsStarted(InspectContainerResponse containerInfo) { + orientDB = new OrientDB(getServerUrl(), "root", serverPassword, OrientDBConfig.defaultConfig()); + } + + public OrientDB getOrientDB() { + return orientDB; + } + + public String getServerUrl() { + return "remote:" + getContainerIpAddress() + ":" + getMappedPort(2424); + } + + public String getDbUrl() { + return getServerUrl() + "/" + databaseName; + } + + public ODatabaseSession getSession() { + return getSession(DEFAULT_USERNAME, DEFAULT_PASSWORD); + } + + public synchronized ODatabaseSession getSession(String username, String password) { + orientDB.createIfNotExists(databaseName, ODatabaseType.PLOCAL); + + if (session == null) { + session = orientDB.open(databaseName, username, password); + + scriptPath.ifPresent(path -> loadScript(path, session)); + } + return session; + } + + private void loadScript(String path, ODatabaseSession session) { + try { + URL resource = getClass().getClassLoader().getResource(path); + + if (resource == null) { + LOGGER.warn("Could not load classpath init script: {}", scriptPath); + throw new RuntimeException("Could not load classpath init script: " + scriptPath + ". Resource not found."); + } + + String script = IOUtils.toString(resource, StandardCharsets.UTF_8); + + session.execute("sql", script); + } catch (IOException e) { + LOGGER.warn("Could not load classpath init script: {}", scriptPath); + throw new RuntimeException("Could not load classpath init script: " + scriptPath, e); + } catch (UnsupportedOperationException e) { + LOGGER.error("Error while executing init script: {}", scriptPath, e); + throw new RuntimeException("Error while executing init script: " + scriptPath, e); + } + } + +} diff --git a/modules/orientdb/src/test/java/org/testcontainers/containers/OrientDBContainerTest.java b/modules/orientdb/src/test/java/org/testcontainers/containers/OrientDBContainerTest.java new file mode 100644 index 00000000000..0a067b375b6 --- /dev/null +++ b/modules/orientdb/src/test/java/org/testcontainers/containers/OrientDBContainerTest.java @@ -0,0 +1,73 @@ +package org.testcontainers.containers; + +import com.orientechnologies.orient.core.db.ODatabaseSession; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.*; + +/** + * @author robfrank + */ +public class OrientDBContainerTest { + + @Test + public void shouldReturnTheSameSession() { + try (OrientDBContainer container = new OrientDBContainer()) { + container.start(); + + final ODatabaseSession session = container.getSession(); + final ODatabaseSession session2 = container.getSession(); + + assertThat(session).isSameAs(session2); + } + } + + @Test + public void shouldInitializeWithCommands() { + try (OrientDBContainer container = new OrientDBContainer()) { + container.start(); + + final ODatabaseSession session = container.getSession(); + + session.command("CREATE CLASS Person EXTENDS V"); + session.command("INSERT INTO Person set name='john'"); + session.command("INSERT INTO Person set name='jane'"); + + assertThat(session.query("SELECT FROM Person").stream()).hasSize(2); + } + } + + @Test + public void shouldQueryWithGremlin() { + + try (OrientDBContainer container = new OrientDBContainer()) { + container.start(); + + final ODatabaseSession session = container.getSession("admin", "admin"); + + session.command("CREATE CLASS Person EXTENDS V"); + session.command("INSERT INTO Person set name='john'"); + session.command("INSERT INTO Person set name='jane'"); + + assertThat(session.execute("gremlin", + "g.V().hasLabel('Person')").stream()).hasSize(2); + } + } + + @Test + public void shouldInitializeDatabaseFromScript() { + try (OrientDBContainer container = new OrientDBContainer() + .withScriptPath("initscript.osql") + .withDatabaseName("persons")) { + + container.start(); + + assertThat(container.getDbUrl()) + .isEqualTo("remote:" + container.getContainerIpAddress() + ":" + container.getMappedPort(2424) + "/persons"); + + final ODatabaseSession session = container.getSession(); + + assertThat(session.query("SELECT FROM Person").stream()).hasSize(4); + } + } +} diff --git a/modules/orientdb/src/test/resources/initscript.osql b/modules/orientdb/src/test/resources/initscript.osql new file mode 100644 index 00000000000..e1ba22bbb3e --- /dev/null +++ b/modules/orientdb/src/test/resources/initscript.osql @@ -0,0 +1,6 @@ +CREATE CLASS Person EXTENDS V; + +INSERT INTO Person set name="john"; +INSERT INTO Person set name="paul"; +INSERT INTO Person set name="luke"; +INSERT INTO Person set name="albert"; diff --git a/modules/orientdb/src/test/resources/logback-test.xml b/modules/orientdb/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..7bd6a94d827 --- /dev/null +++ b/modules/orientdb/src/test/resources/logback-test.xml @@ -0,0 +1,18 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + + + \ No newline at end of file From 142558426edcc8c993612b1a9e229259106b22bf Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 7 Feb 2020 20:56:15 +0000 Subject: [PATCH 1162/5615] Pin mkdocs dependencies to avoid version incompatibility (#2335) See https://github.com/squidfunk/mkdocs-material/issues/1450 for more background --- requirements.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index a936d8a0a95..dd80510a4b4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ -mkdocs +mkdocs==1.0.4 -e git+https://github.com/rnorth/mkdocs-codeinclude-plugin#egg=mkdocs_codeinclude_plugin -mkdocs-material -mkdocs-markdownextradata-plugin +mkdocs-material==4.6.0 +mkdocs-markdownextradata-plugin==0.1.1 +markdown>=3.1,<3.2 From 61ce025d2affb602384af6312235f895bb104983 Mon Sep 17 00:00:00 2001 From: Piotr Findeisen Date: Sat, 8 Feb 2020 21:07:45 +0100 Subject: [PATCH 1163/5615] Add Presto container (#2196) * Add Presto container * Add incubating note to module docs Co-authored-by: Richard North --- docs/modules/databases/index.md | 4 + docs/modules/databases/presto.md | 88 +++++++++++++ mkdocs.yml | 1 + modules/jdbc-test/build.gradle | 2 + .../testcontainers/jdbc/JDBCDriverTest.java | 1 + modules/presto/build.gradle | 8 ++ .../containers/PrestoContainer.java | 104 +++++++++++++++ .../containers/PrestoContainerProvider.java | 35 +++++ ...s.containers.JdbcDatabaseContainerProvider | 1 + .../containers/PrestoContainerTest.java | 121 ++++++++++++++++++ modules/presto/src/test/resources/initial.sql | 2 + 11 files changed, 367 insertions(+) create mode 100644 docs/modules/databases/presto.md create mode 100644 modules/presto/build.gradle create mode 100644 modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java create mode 100644 modules/presto/src/main/java/org/testcontainers/containers/PrestoContainerProvider.java create mode 100644 modules/presto/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider create mode 100644 modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java create mode 100644 modules/presto/src/test/resources/initial.sql diff --git a/docs/modules/databases/index.md b/docs/modules/databases/index.md index 32026953802..d7654cc6a33 100644 --- a/docs/modules/databases/index.md +++ b/docs/modules/databases/index.md @@ -69,6 +69,10 @@ Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database `jdbc:tc:postgis:9.6:///databasename` +### Using Presto + +`jdbc:tc:presto:329://localhost/memory/default` + ## Using a classpath init script Testcontainers can run an init script after the database container is started, but before your code is given a connection to it. The script must be on the classpath, and is referenced as follows: diff --git a/docs/modules/databases/presto.md b/docs/modules/databases/presto.md new file mode 100644 index 00000000000..52776d06daf --- /dev/null +++ b/docs/modules/databases/presto.md @@ -0,0 +1,88 @@ +# Presto Module + +!!! note + This module is INCUBATING. While it is ready for use and operational in the current version of Testcontainers, it is possible that it may receive breaking changes in the future. See [our contributing guidelines](/contributing/#incubating-modules) for more information on our incubating modules policy. + +See [Database containers](./index.md) for documentation and usage that is common to all database container types. + +## Usage example + +Running Presto as a stand-in for in a test: + +```java +public class SomeTest { + + @Rule + public PrestoContainer presto = new PrestoContainer(); + + @Test + public void someTestMethod() { + String url = presto.getJdbcUrl(); + + ... create a connection and run test as normal +``` + +Presto comes with several catalogs preconfigured. Most useful ones for testing are + +* `tpch` catalog using the [Presto TPCH Connector](https://prestosql.io/docs/current/connector/tpch.html). + This is a read-only catalog that defines standard TPCH schema, so is available for querying without a need + to create any tables. +* `memory` catalog using the [Presto Memory Connector](https://prestosql.io/docs/current/connector/memory.html). + This catalog can be used for creating schemas and tables and does not require any storage, as everything + is stored fully in-memory. + +Example test using the `tpch` and `memory` catalogs: + +```java +public class SomeTest { + @Rule + public PrestoContainer prestoSql = new PrestoContainer(); + + @Test + public void queryMemoryAndTpchConnectors() throws SQLException { + try (Connection connection = prestoSql.createConnection(); + Statement statement = connection.createStatement()) { + // Prepare data + statement.execute("CREATE TABLE memory.default.table_with_array AS SELECT 1 id, ARRAY[1, 42, 2, 42, 4, 42] my_array"); + + // Query Presto using newly created table and a builtin connector + try (ResultSet resultSet = statement.executeQuery("" + + "SELECT nationkey, element " + + "FROM tpch.tiny.nation " + + "JOIN memory.default.table_with_array twa ON nationkey = twa.id " + + "LEFT JOIN UNNEST(my_array) a(element) ON true " + + "ORDER BY element OFFSET 1 FETCH NEXT 3 ROWS WITH TIES ")) { + List actualElements = new ArrayList<>(); + while (resultSet.next()) { + actualElements.add(resultSet.getInt("element")); + } + Assert.assertEquals(Arrays.asList(2, 4, 42, 42, 42), actualElements); + } + } + } +} +``` + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:presto:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + presto + {{latest_version}} + test + +``` + +!!! hint + Adding this Testcontainers library JAR will not automatically add the Presto JDBC driver JAR to your project. + You should ensure that your project has the Presto JDBC driver as a dependency, if you plan on using it. + Refer to [Presto project download page](https://prestosql.io/download.html) for instructions. + + diff --git a/mkdocs.yml b/mkdocs.yml index 189bc3b6788..f66f382ccb0 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -53,6 +53,7 @@ nav: - modules/databases/neo4j.md - modules/databases/oraclexe.md - modules/databases/postgres.md + - modules/databases/presto.md - modules/docker_compose.md - modules/elasticsearch.md - modules/kafka.md diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 842f2a761eb..c7c909782bb 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -7,6 +7,7 @@ repositories { dependencies { compile project(':mysql') compile project(':postgresql') + compile project(':presto') compile project(':cockroachdb') compile project(':mariadb') compile project(':oracle-xe') @@ -16,6 +17,7 @@ dependencies { testCompile 'com.google.guava:guava:18.0' testCompile 'org.postgresql:postgresql:42.0.0' + testCompile 'io.prestosql:presto-jdbc:329' testCompile 'mysql:mysql-connector-java:8.0.14' testCompile 'org.mariadb.jdbc:mariadb-java-client:1.4.6' testCompile 'com.oracle:ojdbc6:12.1.0.1-atlassian-hosted' diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java index 2089513df06..ca55a1d1841 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java @@ -53,6 +53,7 @@ public static Iterable data() { {"jdbc:tc:postgresql:9.6.8://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, {"jdbc:tc:postgis://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, {"jdbc:tc:postgis:9.6://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, + {"jdbc:tc:presto:329://hostname/", EnumSet.of(Options.PmdKnownBroken)}, {"jdbc:tc:mysql:5.6://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override", EnumSet.of(Options.CustomIniFile)}, {"jdbc:tc:mariadb://hostname/databasename", EnumSet.noneOf(Options.class)}, {"jdbc:tc:mariadb://hostname/databasename?user=someuser&TC_INITSCRIPT=somepath/init_mariadb.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, diff --git a/modules/presto/build.gradle b/modules/presto/build.gradle new file mode 100644 index 00000000000..07aad11168e --- /dev/null +++ b/modules/presto/build.gradle @@ -0,0 +1,8 @@ +description = "Testcontainers :: JDBC :: Presto" + +dependencies { + compile project(':jdbc') + + testCompile 'io.prestosql:presto-jdbc:329' + testCompile 'commons-dbutils:commons-dbutils:1.7' +} diff --git a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java new file mode 100644 index 00000000000..186acb18af6 --- /dev/null +++ b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java @@ -0,0 +1,104 @@ +package org.testcontainers.containers; + +import org.jetbrains.annotations.NotNull; +import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; + +import java.sql.Connection; +import java.sql.SQLException; +import java.time.Duration; +import java.util.HashSet; +import java.util.Set; + +import static com.google.common.base.Strings.nullToEmpty; +import static java.lang.String.format; +import static java.time.temporal.ChronoUnit.SECONDS; + +public class PrestoContainer> extends JdbcDatabaseContainer { + public static final String NAME = "presto"; + public static final String IMAGE = "prestosql/presto"; + public static final String DEFAULT_TAG = "329"; + + public static final Integer PRESTO_PORT = 8080; + + private String username = "test"; + private String catalog = null; + + public PrestoContainer() { + this(IMAGE + ":" + DEFAULT_TAG); + } + + public PrestoContainer(final String dockerImageName) { + super(dockerImageName); + this.waitStrategy = new LogMessageWaitStrategy() + .withRegEx(".*io.prestosql.server.PrestoServer\\s+======== SERVER STARTED ========.*") + .withStartupTimeout(Duration.of(60, SECONDS)); + } + + @NotNull + @Override + protected Set getLivenessCheckPorts() { + return new HashSet<>(getMappedPort(PRESTO_PORT)); + } + + @Override + protected void configure() { + addExposedPort(PRESTO_PORT); + } + + @Override + public String getDriverClassName() { + return "io.prestosql.jdbc.PrestoDriver"; + } + + @Override + public String getJdbcUrl() { + return format("jdbc:presto://%s:%s/%s", getContainerIpAddress(), getMappedPort(PRESTO_PORT), nullToEmpty(catalog)); + } + + @Override + public String getUsername() { + return username; + } + + @Override + public String getPassword() { + return ""; + } + + @Override + public String getDatabaseName() { + return catalog; + } + + @Override + public String getTestQueryString() { + return "SELECT count(*) FROM tpch.tiny.nation"; + } + + @Override + public SELF withUsername(final String username) { + this.username = username; + return self(); + } + + /** + * @deprecated This operation is not supported. + */ + @Override + @Deprecated + public SELF withPassword(final String password) { + // ignored, Presto does not support password authentication without TLS + // TODO: make JDBCDriverTest not pass a password unconditionally and remove this method + return self(); + } + + @Override + public SELF withDatabaseName(String dbName) { + this.catalog = dbName; + return self(); + } + + public Connection createConnection() throws SQLException, NoDriverFoundException { + return createConnection(""); + } +} diff --git a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainerProvider.java b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainerProvider.java new file mode 100644 index 00000000000..663cf9be4a9 --- /dev/null +++ b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainerProvider.java @@ -0,0 +1,35 @@ +package org.testcontainers.containers; + +import org.testcontainers.jdbc.ConnectionUrl; + +import java.util.Objects; + +/** + * Factory for Presto containers. + */ +public class PrestoContainerProvider extends JdbcDatabaseContainerProvider { + + public static final String USER_PARAM = "user"; + public static final String PASSWORD_PARAM = "password"; + + @Override + public boolean supports(String databaseType) { + return databaseType.equals(PrestoContainer.NAME); + } + + @Override + public JdbcDatabaseContainer newInstance() { + return newInstance(PrestoContainer.DEFAULT_TAG); + } + + @Override + public JdbcDatabaseContainer newInstance(String tag) { + return new PrestoContainer(PrestoContainer.IMAGE + ":" + tag); + } + + @Override + public JdbcDatabaseContainer newInstance(ConnectionUrl connectionUrl) { + return newInstanceFromConnectionUrl(connectionUrl, USER_PARAM, PASSWORD_PARAM); + } + +} diff --git a/modules/presto/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider b/modules/presto/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider new file mode 100644 index 00000000000..ad36a36a117 --- /dev/null +++ b/modules/presto/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider @@ -0,0 +1 @@ +org.testcontainers.containers.PrestoContainerProvider diff --git a/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java b/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java new file mode 100644 index 00000000000..07fb6fa30ed --- /dev/null +++ b/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java @@ -0,0 +1,121 @@ +package org.testcontainers.containers; + +import org.junit.Assert; +import org.junit.Test; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static java.lang.Integer.parseInt; +import static java.lang.String.format; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * @author findepi + */ +public class PrestoContainerTest { + @Test + public void testSimple() throws Exception { + try (PrestoContainer prestoSql = new PrestoContainer<>()) { + prestoSql.start(); + try (Connection connection = prestoSql.createConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes")) { + assertTrue("No result", resultSet.next()); + assertEquals("Presto version", PrestoContainer.DEFAULT_TAG, resultSet.getString("node_version")); + } + } + } + + @Test + public void testSpecificVersion() throws Exception { + String prestoVersion = Integer.toString(parseInt(PrestoContainer.DEFAULT_TAG) - 1); + try (PrestoContainer prestoSql = new PrestoContainer<>("prestosql/presto:" + prestoVersion)) { + prestoSql.start(); + try (Connection connection = prestoSql.createConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes")) { + assertTrue("No result", resultSet.next()); + assertEquals("Presto version", prestoVersion, resultSet.getString("node_version")); + } + } + } + + @Test + public void testQueryMemoryAndTpch() throws SQLException { + try (PrestoContainer prestoSql = new PrestoContainer<>()) { + prestoSql.start(); + try (Connection connection = prestoSql.createConnection(); + Statement statement = connection.createStatement()) { + // Prepare data + statement.execute("CREATE TABLE memory.default.table_with_array AS SELECT 1 id, ARRAY[1, 42, 2, 42, 4, 42] my_array"); + + // Query Presto using newly created table and a builtin connector + try (ResultSet resultSet = statement.executeQuery("" + + "SELECT nationkey, element " + + "FROM tpch.tiny.nation " + + "JOIN memory.default.table_with_array twa ON nationkey = twa.id " + + "LEFT JOIN UNNEST(my_array) a(element) ON true " + + "ORDER BY element OFFSET 1 FETCH NEXT 3 ROWS WITH TIES ")) { + List actualElements = new ArrayList<>(); + while (resultSet.next()) { + actualElements.add(resultSet.getInt("element")); + } + Assert.assertEquals(Arrays.asList(2, 4, 42, 42, 42), actualElements); + } + } + } + } + + @Test + public void testInitScript() throws Exception { + try (PrestoContainer prestoSql = new PrestoContainer<>()) { + prestoSql.withInitScript("initial.sql"); + prestoSql.start(); + try (Connection connection = prestoSql.createConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT a FROM memory.default.test_table")) { + assertTrue("No result", resultSet.next()); + assertEquals("Value", 12345678909324L, resultSet.getObject("a")); + assertFalse("Too many result", resultSet.next()); + } + } + } + + @Test + public void testTcJdbcUri() throws Exception { + try (Connection connection = DriverManager.getConnection(format("jdbc:tc:presto:%s://hostname/", PrestoContainer.DEFAULT_TAG))) { + // Verify metadata with tc: JDBC connection URI + assertEquals(connection.getMetaData().getDatabaseMajorVersion(), parseInt(PrestoContainer.DEFAULT_TAG)); + + // Verify transactions with tc: JDBC connection URI + assertTrue("Is autocommit", connection.getAutoCommit()); + connection.setAutoCommit(false); + assertFalse("Is autocommit", connection.getAutoCommit()); + assertEquals("Transaction isolation", Connection.TRANSACTION_READ_UNCOMMITTED, connection.getTransactionIsolation()); + + try (Statement statement = connection.createStatement()) { + assertEquals("Update result", 0, statement.executeUpdate("CREATE TABLE memory.default.test_tc(a bigint)")); + try (ResultSet resultSet = statement.executeQuery("SELECT sum(cast(node_version AS bigint)) AS v FROM system.runtime.nodes")) { + assertTrue(resultSet.next()); + assertEquals(PrestoContainer.DEFAULT_TAG, resultSet.getString("v")); + assertFalse(resultSet.next()); + } + connection.commit(); + } finally { + connection.rollback(); + } + connection.setAutoCommit(true); + assertTrue("Is autocommit", connection.getAutoCommit()); + assertEquals("Transaction isolation should be retained", Connection.TRANSACTION_READ_UNCOMMITTED, connection.getTransactionIsolation()); + } + } +} diff --git a/modules/presto/src/test/resources/initial.sql b/modules/presto/src/test/resources/initial.sql new file mode 100644 index 00000000000..1a3ecdab4f9 --- /dev/null +++ b/modules/presto/src/test/resources/initial.sql @@ -0,0 +1,2 @@ +CREATE TABLE memory.default.test_table(a bigint); +INSERT INTO memory.default.test_table(a) VALUES (12345678909324); From 0e13f273a96e59432ac0f0244e61accfe2467e27 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 8 Feb 2020 20:08:35 +0000 Subject: [PATCH 1164/5615] =?UTF-8?q?Bump=20aws-java-sdk-logs=20from=201.1?= =?UTF-8?q?1.636=20to=201.11.717=20in=20/modules/lo=E2=80=A6=20(#2327)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.11.636 to 1.11.717. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.636...1.11.717) Signed-off-by: dependabot-preview[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 6d87d282d90..81490d2daef 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,5 +6,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.683' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.683' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.636' - testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.636' + testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.717' } From d4b2369e30a55ebe34d79062bf018d2576223904 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 8 Feb 2020 20:08:53 +0000 Subject: [PATCH 1165/5615] Bump lombok from 1.18.10 to 1.18.12 in /examples (#2333) Bumps [lombok](https://github.com/rzwitserloot/lombok) from 1.18.10 to 1.18.12. - [Release notes](https://github.com/rzwitserloot/lombok/releases) - [Changelog](https://github.com/rzwitserloot/lombok/blob/master/doc/changelog.markdown) - [Commits](https://github.com/rzwitserloot/lombok/compare/v1.18.10...v1.18.12) Signed-off-by: dependabot-preview[bot] --- examples/disque-job-queue/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index 1f4e257cad1..fb8413bdec8 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly "org.projectlombok:lombok:1.18.10" + compileOnly "org.projectlombok:lombok:1.18.12" annotationProcessor "org.projectlombok:lombok:1.18.10" implementation 'biz.paluch.redis:spinach:0.3' implementation 'com.google.code.gson:gson:2.8.6' diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index e74e04a95e5..49052847de1 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -9,7 +9,7 @@ repositories { } dependencies { - compileOnly "org.projectlombok:lombok:1.18.10" + compileOnly "org.projectlombok:lombok:1.18.12" annotationProcessor "org.projectlombok:lombok:1.18.10" implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-data-redis' From 3ba7a7936b5f9e44ef2624edc9e275a39d25add4 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 8 Feb 2020 20:09:08 +0000 Subject: [PATCH 1166/5615] Bump postgresql from 42.2.9 to 42.2.10 in /modules/spock (#2329) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.9 to 42.2.10. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.9...REL42.2.10) Signed-off-by: dependabot-preview[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index f26858947ce..c7b51cc9c5f 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -13,6 +13,6 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.4.2' testCompile 'org.apache.httpcomponents:httpclient:4.5.11' - testRuntime 'org.postgresql:postgresql:42.2.9' + testRuntime 'org.postgresql:postgresql:42.2.10' testRuntime 'mysql:mysql-connector-java:8.0.19' } From 0d25192210d18ee77b34ad80496d169b9b159512 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 8 Feb 2020 20:09:19 +0000 Subject: [PATCH 1167/5615] Bump postgresql from 42.2.9 to 42.2.10 in /modules/postgresql (#2328) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.9 to 42.2.10. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.9...REL42.2.10) Signed-off-by: dependabot-preview[bot] --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index a4bf697a3ad..9acf9ead2aa 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: JDBC :: PostgreSQL" dependencies { compile project(':jdbc') - testCompile 'org.postgresql:postgresql:42.2.9' + testCompile 'org.postgresql:postgresql:42.2.10' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' } From 39f1267a5733c27b57477fe7f11321f8d3cde95a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 8 Feb 2020 20:09:30 +0000 Subject: [PATCH 1168/5615] Bump postgresql from 42.2.9 to 42.2.10 in /modules/junit-jupiter (#2330) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.9 to 42.2.10. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.9...REL42.2.10) Signed-off-by: dependabot-preview[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 7bb6393a586..b01333b5ec9 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -13,7 +13,7 @@ dependencies { exclude(module: 'hamcrest-core') } - testRuntime 'org.postgresql:postgresql:42.2.9' + testRuntime 'org.postgresql:postgresql:42.2.10' testRuntime 'mysql:mysql-connector-java:8.0.19' testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.6.0' } From b1117888694f21dfa2f31a30af7acda6cb34fa2c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 8 Feb 2020 20:23:14 +0000 Subject: [PATCH 1169/5615] =?UTF-8?q?Bump=20aws-java-sdk-logs=20from=201.1?= =?UTF-8?q?1.636=20to=201.11.718=20in=20/modules/lo=E2=80=A6=20(#2336)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.11.636 to 1.11.718. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.636...1.11.718) Signed-off-by: dependabot-preview[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 81490d2daef..c1c12257baa 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,5 +6,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.683' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.683' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.636' - testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.717' + testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.718' } From d4cf9d78314e5a6edbb4c0c2601a16304a865e85 Mon Sep 17 00:00:00 2001 From: Artjom Kalita Date: Sun, 9 Feb 2020 17:34:44 +0200 Subject: [PATCH 1170/5615] Add functionality to retrieve container by its name (#2314) Since `DockerComposeContainer#project` is private, there is currently no way of accessing containers started by Docker Compose. This change adds a new API to the DockerComposeContainer to obtain a `ContainerState` reference by the service's name. Fixes #2302. --- .../containers/DockerComposeContainer.java | 23 ++++++++++++++-- .../junit/DockerComposeContainerTest.java | 26 ++++++++++++++++--- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 7dec6399b8f..839db7c89db 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -22,7 +22,14 @@ import org.testcontainers.containers.wait.strategy.WaitAllStrategy; import org.testcontainers.containers.wait.strategy.WaitStrategy; import org.testcontainers.lifecycle.Startable; -import org.testcontainers.utility.*; +import org.testcontainers.utility.AuditLogger; +import org.testcontainers.utility.Base58; +import org.testcontainers.utility.CommandLine; +import org.testcontainers.utility.DockerLoggerFactory; +import org.testcontainers.utility.LogUtils; +import org.testcontainers.utility.MountableFile; +import org.testcontainers.utility.ResourceReaper; +import org.testcontainers.utility.TestcontainersConfiguration; import org.zeroturnaround.exec.InvalidExitValueException; import org.zeroturnaround.exec.ProcessExecutor; import org.zeroturnaround.exec.stream.slf4j.Slf4jStream; @@ -30,7 +37,15 @@ import java.io.File; import java.time.Duration; import java.util.AbstractMap.SimpleEntry; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -479,6 +494,10 @@ public SELF withRemoveImages(RemoveImages removeImages) { return self(); } + public Optional getContainerByServiceName(String serviceName) { + return Optional.ofNullable(serviceInstanceMap.get(serviceName)); + } + private void followLogs(String containerId, Consumer consumer) { LogUtils.followOutput(DockerClientFactory.instance().client(), containerId, consumer); } diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java index bf69656ac04..53d90872332 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java @@ -2,12 +2,18 @@ import org.junit.Rule; import org.junit.Test; +import org.testcontainers.containers.ContainerState; import org.testcontainers.containers.DockerComposeContainer; import java.io.File; +import java.util.Optional; +import static java.lang.String.format; +import static java.util.Collections.singletonList; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; /** * Created by rnorth on 08/08/2015. @@ -16,9 +22,8 @@ public class DockerComposeContainerTest extends BaseDockerComposeTest { @Rule public DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/compose-test.yml")) - .withExposedService("redis_1", REDIS_PORT) - .withExposedService("db_1", 3306) - ; + .withExposedService("redis_1", REDIS_PORT) + .withExposedService("db_1", 3306); @Override protected DockerComposeContainer getEnvironment() { @@ -33,4 +38,19 @@ public void testGetServicePort() { assertNotNull("Port is set for service with instance number", serviceWithoutInstancePort); assertEquals("Service ports are the same", serviceWithInstancePort, serviceWithoutInstancePort); } + + @Test + public void shouldRetrieveContainerByServiceName() { + String existingServiceName = "db_1"; + Optional result = environment.getContainerByServiceName(existingServiceName); + assertTrue(format("Container should be found by service name %s", existingServiceName), result.isPresent()); + assertEquals("Mapped port for result container was wrong, probably wrong container found", result.get().getExposedPorts(), singletonList(3306)); + } + + @Test + public void shouldReturnEmptyResultOnNoneExistingService() { + String notExistingServiceName = "db_256"; + Optional result = environment.getContainerByServiceName(notExistingServiceName); + assertFalse(format("No container should be found under service name %s", notExistingServiceName), result.isPresent()); + } } From 2e9adb94ec09c074c43984e14e710daf9d1d6c20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivo=20=C5=A0m=C3=ADd?= Date: Sun, 9 Feb 2020 20:34:02 +0100 Subject: [PATCH 1171/5615] checkMountableFile on Windows (#2296) --- .../java/org/testcontainers/utility/MountableFile.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java index 22939b0e1fd..7b7367b9290 100644 --- a/core/src/main/java/org/testcontainers/utility/MountableFile.java +++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java @@ -242,7 +242,11 @@ private String extractClassPathResourceToTempLocation(final String hostPath) { // Mark temporary files/dirs for deletion at JVM shutdown deleteOnExit(tmpLocation.toPath()); - return tmpLocation.getAbsolutePath(); + try { + return tmpLocation.getCanonicalPath(); + } catch (IOException e) { + throw new IllegalStateException(e); + } } private File createTempDirectory() { @@ -371,8 +375,8 @@ public static int getUnixFileMode(final Path path) { try { int unixMode = (int) Files.readAttributes(path, "unix:mode").get("mode"); // Truncate mode bits for z/OS - if ("OS/390".equals(SystemUtils.OS_NAME) || - "z/OS".equals(SystemUtils.OS_NAME) || + if ("OS/390".equals(SystemUtils.OS_NAME) || + "z/OS".equals(SystemUtils.OS_NAME) || "zOS".equals(SystemUtils.OS_NAME) ) { unixMode &= TarConstants.MAXID; unixMode |= Files.isDirectory(path) ? 040000 : 0100000; From f99de58845c77e8ca1982ef555b598215c24e217 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 15 Feb 2020 16:20:29 +0000 Subject: [PATCH 1172/5615] =?UTF-8?q?Bump=20aws-java-sdk-logs=20from=201.1?= =?UTF-8?q?1.718=20to=201.11.722=20in=20/modules/lo=E2=80=A6=20(#2353)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.11.718 to 1.11.722. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.718...1.11.722) Signed-off-by: dependabot-preview[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index c1c12257baa..2bdf076a68c 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,5 +6,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.683' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.683' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.636' - testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.718' + testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.722' } From 4d28d29ffddccc72aa939e6780358832f32bc1bf Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 15 Feb 2020 16:20:34 +0000 Subject: [PATCH 1173/5615] Bump tomcat-jdbc from 9.0.30 to 9.0.31 in /modules/mysql (#2344) Bumps tomcat-jdbc from 9.0.30 to 9.0.31. Signed-off-by: dependabot-preview[bot] --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 43867df521a..11c522197b7 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -5,7 +5,7 @@ dependencies { testCompile 'mysql:mysql-connector-java:8.0.19' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.30' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.31' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.vibur:vibur-dbcp:25.0' } From 5364d699adedcfdf98668319a51affa6aa677c1d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 15 Feb 2020 16:20:42 +0000 Subject: [PATCH 1174/5615] Bump tomcat-jdbc from 9.0.30 to 9.0.31 in /modules/mariadb (#2343) Bumps tomcat-jdbc from 9.0.30 to 9.0.31. Signed-off-by: dependabot-preview[bot] --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index c7d1dc96676..c479824ae47 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -6,6 +6,6 @@ dependencies { testCompile 'org.mariadb.jdbc:mariadb-java-client:2.5.4' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.30' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.31' testCompile 'org.vibur:vibur-dbcp:25.0' } From f242e290b65ced9ef67dc056540fa55077e3bd24 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 15 Feb 2020 16:20:47 +0000 Subject: [PATCH 1175/5615] Bump postgresql from 42.2.9 to 42.2.10 in /examples (#2338) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.9 to 42.2.10. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.9...REL42.2.10) Signed-off-by: dependabot-preview[bot] --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index f1977ea2c0b..081b2ac04fa 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' implementation 'com.squareup.okhttp3:okhttp:3.14.6' implementation 'org.json:json:20180813' - testImplementation 'org.postgresql:postgresql:42.2.9' + testImplementation 'org.postgresql:postgresql:42.2.10' testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.testcontainers:postgresql' } From 7c8e0ea62ce648b7b75dbdf05263c251284e800b Mon Sep 17 00:00:00 2001 From: nshipyakov <58605486+nshipyakov@users.noreply.github.com> Date: Sun, 16 Feb 2020 23:48:23 +0300 Subject: [PATCH 1176/5615] Kafka: replace deprecated poll method millis param with Duration (#2318) --- .../java/org/testcontainers/containers/KafkaContainerTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java index add8b2b171d..1490caa3328 100644 --- a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java +++ b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java @@ -13,6 +13,7 @@ import org.junit.Test; import org.rnorth.ducttape.unreliables.Unreliables; +import java.time.Duration; import java.util.Arrays; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -100,7 +101,7 @@ protected void testKafkaFunctionality(String bootstrapServers) throws Exception producer.send(new ProducerRecord<>(topicName, "testcontainers", "rulezzz")).get(); Unreliables.retryUntilTrue(10, TimeUnit.SECONDS, () -> { - ConsumerRecords records = consumer.poll(100); + ConsumerRecords records = consumer.poll(Duration.ofMillis(100)); if (records.isEmpty()) { return false; From c7642e581f449fb5bf466a3a74e030be6f57c666 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 16 Feb 2020 20:49:06 +0000 Subject: [PATCH 1177/5615] =?UTF-8?q?Bump=20elasticsearch-rest-client=20fr?= =?UTF-8?q?om=207.5.2=20to=207.6.0=20in=20/modules/=E2=80=A6=20(#2347)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 7.5.2 to 7.6.0. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.5.2...v7.6.0) Signed-off-by: dependabot-preview[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 41d5b0f27ab..17c34818a82 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,6 +2,6 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') - testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.5.2" + testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.6.0" testCompile "org.elasticsearch.client:transport:6.7.1" } From edb54a6bd9fa85fbba748dfe11837ab9c7e31ccb Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 16 Feb 2020 20:49:39 +0000 Subject: [PATCH 1178/5615] Bump commons-compress from 1.19 to 1.20 in /core (#2337) Bumps commons-compress from 1.19 to 1.20. Signed-off-by: dependabot-preview[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 25aaa396a60..ab9b92115e9 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -60,7 +60,7 @@ dependencies { compile 'org.slf4j:slf4j-api:1.7.30' compile 'org.jetbrains:annotations:17.0.0' compile 'javax.annotation:javax.annotation-api:1.3.2' - compile 'org.apache.commons:commons-compress:1.19' + compile 'org.apache.commons:commons-compress:1.20' // Added for JDK9 compatibility since it's missing this package compile 'javax.xml.bind:jaxb-api:2.3.1' compile ('org.rnorth.duct-tape:duct-tape:1.0.8') { From 573a6222d4a82b24c2d18883a21631d2108d9252 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 16 Feb 2020 20:51:17 +0000 Subject: [PATCH 1179/5615] Bump okhttp from 3.14.4 to 3.14.6 in /core (#2243) Bumps [okhttp](https://github.com/square/okhttp) from 3.14.4 to 3.14.6. - [Release notes](https://github.com/square/okhttp/releases) - [Changelog](https://github.com/square/okhttp/blob/master/docs/changelog_3x.md) - [Commits](https://github.com/square/okhttp/compare/parent-3.14.4...parent-3.14.6) Signed-off-by: dependabot-preview[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index ab9b92115e9..1f76589a64f 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -97,7 +97,7 @@ dependencies { shaded "org.yaml:snakeyaml:1.25" - shaded 'com.squareup.okhttp3:okhttp:3.14.4' + shaded 'com.squareup.okhttp3:okhttp:3.14.6' shaded 'org.glassfish.main.external:trilead-ssh2-repackaged:4.1.2' From 6f7b17a9717bb600eccd839b2d84566392e7d999 Mon Sep 17 00:00:00 2001 From: pertu Date: Sun, 16 Feb 2020 20:53:09 +0000 Subject: [PATCH 1180/5615] Set sourceJar.from=allSource for modules/spock (#2281) Fixes issue #2280 By default, sourceJar.from = allJava (see gradle/publishing.gradle) But Spock module is in Groovy => sourceJar.from should be allGroovy/allSource Co-authored-by: Richard North --- modules/spock/build.gradle | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index c7b51cc9c5f..074711d8bda 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -16,3 +16,9 @@ dependencies { testRuntime 'org.postgresql:postgresql:42.2.10' testRuntime 'mysql:mysql-connector-java:8.0.19' } + +sourceJar { + /* allJava is default (see gradle/publishing.gradle:sourceJar) + allSource contains both .java and .groovy files */ + from sourceSets.main.allSource +} From 6ec63f863f760e4e4b31965695834622dbcde505 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 24 Feb 2020 14:19:35 +0100 Subject: [PATCH 1181/5615] Prune images between module checks (#2376) --- azure-pipelines.yml | 4 +--- build.gradle | 11 +++++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4b060c5fcea..68330550ee1 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -37,9 +37,7 @@ jobs: inputs: gradleWrapperFile: 'gradlew' jdkVersionOption: '1.11' - options: '--continue -x testcontainers:check -x jdbc-test:check' + options: '--continue -x testcontainers:check -x jdbc-test:check -PpostCheckCommand="docker image prune -af"' tasks: 'check' publishJUnitResults: true testResultsFiles: '**/TEST-*.xml' - - diff --git a/build.gradle b/build.gradle index bac06d64052..c8b2267a049 100644 --- a/build.gradle +++ b/build.gradle @@ -71,6 +71,17 @@ subprojects { // Ensure that Javadoc generation is always tested check.dependsOn(javadoc) + def postCheckCommand = properties["postCheckCommand"] + if (postCheckCommand) { + check.finalizedBy(tasks.create("postCheckExec", Exec) { + if (org.gradle.internal.os.OperatingSystem.current().isWindows()) { + commandLine('cmd', '/c', postCheckCommand) + } else { + commandLine('sh', '-c', postCheckCommand) + } + }) + } + javadoc { dependsOn delombok source = delombok.outputs From b262a20c5c71ec56d8f05f0269bbfec054754606 Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 28 Feb 2020 09:53:31 +0000 Subject: [PATCH 1182/5615] =?UTF-8?q?Adding=20Windows=20Subsystem=20for=20?= =?UTF-8?q?Linux=20section=20to=20the=20windows=20docum=E2=80=A6=20(#2282)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Luis Miguel Mejía Suárez Co-authored-by: Richard North Co-authored-by: Luis Miguel Mejía Suárez Co-authored-by: Kevin Wittek --- docs/contributing.md | 6 +++--- docs/supported_docker_environment/index.md | 1 + docs/supported_docker_environment/windows.md | 19 +++++++++++++++++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/docs/contributing.md b/docs/contributing.md index 70cd452b143..7d87945c704 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -4,9 +4,9 @@ * Join our [Slack group](http://slack.testcontainers.org) * [Post an issue](https://github.com/testcontainers/testcontainers-java/issues) if you find any bugs * Contribute improvements or fixes using a [Pull Request](https://github.com/testcontainers/testcontainers-java/pulls). If you're going to contribute, thank you! Please just be sure to: - * discuss with the authors on an issue ticket prior to doing anything big - * follow the style, naming and structure conventions of the rest of the project - * make commits atomic and easy to merge + * discuss with the authors on an issue ticket prior to doing anything big. + * follow the style, naming and structure conventions of the rest of the project. + * make commits atomic and easy to merge. * when updating documentation, please see [our guidance for documentation contributions](contributing_docs.md). * verify all tests are passing. Build the project with `./gradlew check` to do this. **N.B.** Gradle's Build Cache is enabled by default, but you can add `--no-build-cache` flag to disable it. diff --git a/docs/supported_docker_environment/index.md b/docs/supported_docker_environment/index.md index 3477275c2f2..7622a5051cd 100644 --- a/docs/supported_docker_environment/index.md +++ b/docs/supported_docker_environment/index.md @@ -12,6 +12,7 @@ | Mac OS X - Docker for Mac | 1.12.0 | *Support is best-efforts at present*. `getTestHostIpAddress()` is [not currently supported](https://github.com/testcontainers/testcontainers-java/issues/166) due to limitations in Docker for Mac. | | Windows - Docker Toolbox | | *Support is limited at present and this is not currently tested on a regular basis*. | | Windows - Docker for Windows | | *Support is best-efforts at present.* Only Linux Containers (LCOW) are supported at the moment. See [Windows Support](windows.md) | +| Windows - Windows Subsystem for Linux (WSL) | Docker v17.06 | *Support is best-efforts at present.* Only Linux Containers (LCOW) are supported at the moment. See [Windows Support](windows.md). | ## Docker environment discovery diff --git a/docs/supported_docker_environment/windows.md b/docs/supported_docker_environment/windows.md index b35ee1aa2ee..6365456b095 100644 --- a/docs/supported_docker_environment/windows.md +++ b/docs/supported_docker_environment/windows.md @@ -23,6 +23,25 @@ effort. * WCOW is currently not supported, since Testcontainers uses auxiliary Linux containers for certain tasks and Docker for Windows does not support hybrid engine mode at the time of writing. +## Windows Subsystem for Linux + +Testcontainers supports communicating with Docker for Windows within the Windows Subsystem for Linux *([**WSL**](https://docs.microsoft.com/en-us/windows/wsl/about))*. +The following additional configurations steps are required: + ++ Expose the Docker for Windows daemon on tcp port `2375` without **TLS**. + *(Right-click the Docker for Windows icon on the task bar, click setting and go to `General`)*. + ++ Set the `DOCKER_HOST` environment variable inside the **WSL** shell to `tcp://localhost:2375`. + It is recommended to add this to your `~/.bashrc` file, so it’s available every time you open your terminal. + ++ **Optional** - Only if volumes are required: + Inside the **WSL** shell, modify the `/ect/wsl.conf` file to mount the Windows drives on `/` instead of on `/mnt/`. + *(Reboot required after this step)*. + Remember to share the drives, on which you will store your volumes, with Docker for Windows. + *(Right-click the Docker for Windows icon on the task bar, click setting and go to `Shared Drives`)*. + +More information about running Docker within the **WSL** can be found [here](https://nickjanetakis.com/blog/setting-up-docker-for-windows-and-wsl-to-work-flawlessly). + ## Reporting issues Please report any issues with the Windows build of Testcontainers [here](https://github.com/testcontainers/testcontainers-java/issues) From df48901ed2783ff582ddabd6eb94279e21fd74ab Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Wed, 4 Mar 2020 22:24:55 +0100 Subject: [PATCH 1183/5615] Idiomatize testcontainer object creation in kotlin example (#1964) Co-authored-by: Kevin Wittek --- .../kotlin/com/example/redis/AbstractIntegrationTest.kt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/examples/spring-boot-kotlin-redis/src/test/kotlin/com/example/redis/AbstractIntegrationTest.kt b/examples/spring-boot-kotlin-redis/src/test/kotlin/com/example/redis/AbstractIntegrationTest.kt index dc8b0bf9e68..6350ace9bd9 100644 --- a/examples/spring-boot-kotlin-redis/src/test/kotlin/com/example/redis/AbstractIntegrationTest.kt +++ b/examples/spring-boot-kotlin-redis/src/test/kotlin/com/example/redis/AbstractIntegrationTest.kt @@ -17,11 +17,8 @@ import org.testcontainers.containers.GenericContainer abstract class AbstractIntegrationTest { companion object { - val redisContainer = object : GenericContainer("redis:3-alpine") { - init { - withExposedPorts(6379) - } - } + val redisContainer = GenericContainer("redis:3-alpine") + .apply { withExposedPorts(6379) } } internal class Initializer : ApplicationContextInitializer { From 0981d275c10769f237aa0a5f75386851ee971bf4 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 5 Mar 2020 15:07:39 +0000 Subject: [PATCH 1184/5615] Bump mockito-core from 3.2.4 to 3.3.0 in /modules/junit-jupiter (#2372) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.2.4 to 3.3.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.2.4...v3.3.0) Signed-off-by: dependabot-preview[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index b01333b5ec9..7f6d4533509 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,7 +9,7 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.4.2' testCompile 'redis.clients:jedis:3.2.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.11' - testCompile ('org.mockito:mockito-core:3.2.4') { + testCompile ('org.mockito:mockito-core:3.3.0') { exclude(module: 'hamcrest-core') } From 7b4f3810d0e5b80123fa2c81fe79b64161232be5 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 5 Mar 2020 15:08:13 +0000 Subject: [PATCH 1185/5615] =?UTF-8?q?Bump=20org.springframework.boot=20fro?= =?UTF-8?q?m=202.2.4.RELEASE=20to=202.2.5.RELEA=E2=80=A6=20(#2387)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [org.springframework.boot](https://github.com/spring-projects/spring-boot) from 2.2.4.RELEASE to 2.2.5.RELEASE. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.2.4.RELEASE...v2.2.5.RELEASE) Signed-off-by: dependabot-preview[bot] --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index db570bf67ac..c9bed8d1467 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.2.4.RELEASE' + id 'org.springframework.boot' version '2.2.5.RELEASE' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index 49052847de1..bd9cc9e95f2 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.2.4.RELEASE' + id 'org.springframework.boot' version '2.2.5.RELEASE' } apply plugin: 'io.spring.dependency-management' From f8a6fa0c818987e4ab2656417eeac4c9982f773c Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 5 Mar 2020 15:08:22 +0000 Subject: [PATCH 1186/5615] Bump java-client from 2.7.12 to 2.7.13 in /modules/couchbase (#2397) Bumps [java-client](https://github.com/couchbase/couchbase-java-client) from 2.7.12 to 2.7.13. - [Release notes](https://github.com/couchbase/couchbase-java-client/releases) - [Commits](https://github.com/couchbase/couchbase-java-client/compare/2.7.12...2.7.13) Signed-off-by: dependabot-preview[bot] --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index d6a7e51681e..66f29fc4718 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -2,7 +2,7 @@ description = "Testcontainers :: Couchbase" dependencies { compile project(':testcontainers') - compile 'com.couchbase.client:java-client:2.7.12' + compile 'com.couchbase.client:java-client:2.7.13' testCompile project(':test-support') } From e6ee290f39f552c91edbc5c4cdc28c1bb44a18c4 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 5 Mar 2020 15:08:38 +0000 Subject: [PATCH 1187/5615] =?UTF-8?q?Bump=20aws-java-sdk-logs=20from=201.1?= =?UTF-8?q?1.722=20to=201.11.737=20in=20/modules/lo=E2=80=A6=20(#2399)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.11.722 to 1.11.737. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.722...1.11.737) Signed-off-by: dependabot-preview[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 2bdf076a68c..49e586b19f7 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,5 +6,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.683' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.683' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.636' - testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.722' + testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.737' } From 832c6e0ecf861d22a87bdd1760d22e9c74f64993 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 5 Mar 2020 15:08:51 +0000 Subject: [PATCH 1188/5615] =?UTF-8?q?Bump=20elasticsearch-rest-client=20fr?= =?UTF-8?q?om=207.6.0=20to=207.6.1=20in=20/modules/=E2=80=A6=20(#2401)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 7.6.0 to 7.6.1. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.6.0...v7.6.1) Signed-off-by: dependabot-preview[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 17c34818a82..75697344874 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,6 +2,6 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') - testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.6.0" + testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.6.1" testCompile "org.elasticsearch.client:transport:6.7.1" } From f43e799cea8d6ae4aa22927f773b8d0ae5926fc8 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 5 Mar 2020 15:09:56 +0000 Subject: [PATCH 1189/5615] Bump mockito-core from 3.2.4 to 3.3.0 in /core (#2370) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.2.4 to 3.3.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.2.4...v3.3.0) Signed-off-by: dependabot-preview[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 1f76589a64f..5040a487f50 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -109,7 +109,7 @@ dependencies { testCompile 'redis.clients:jedis:3.2.0' testCompile 'com.rabbitmq:amqp-client:5.8.0' testCompile 'org.mongodb:mongo-java-driver:3.12.1' - testCompile ('org.mockito:mockito-core:3.2.4') { + testCompile ('org.mockito:mockito-core:3.3.0') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From 16e9be99861da40edaab78f45b524102af6049f2 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 5 Mar 2020 21:04:30 +0000 Subject: [PATCH 1190/5615] #1975: add example for Cassandra module (#2210) --- docs/modules/databases/cassandra.md | 33 +++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/docs/modules/databases/cassandra.md b/docs/modules/databases/cassandra.md index 0d9a52a0166..d15304afa81 100644 --- a/docs/modules/databases/cassandra.md +++ b/docs/modules/databases/cassandra.md @@ -1,5 +1,38 @@ # Cassandra Module +## Usage example + +This example connects to the Cassandra Cluster, creates a keyspaces and asserts that is has been created. + +```java tab="JUnit 4 example" +public class SomeTest { + + @Rule + public CassandraContainer cassandra = new CassandraContainer(); + + + @Test + public void test(){ + Cluster cluster = cassandra.getCluster(); + + try(Session session = cluster.connect()) { + + session.execute("CREATE KEYSPACE IF NOT EXISTS test WITH replication = \n" + + "{'class':'SimpleStrategy','replication_factor':'1'};"); + + List keyspaces = session.getCluster().getMetadata().getKeyspaces(); + List filteredKeyspaces = keyspaces + .stream() + .filter(km -> km.getName().equals("test")) + .collect(Collectors.toList()); + + assertEquals(1, filteredKeyspaces.size()); + } + } + +} +``` + ## Adding this module to your project dependencies Add the following dependency to your `pom.xml`/`build.gradle` file: From bf187bb83cb76df9c65f66bc335080d18d5256fe Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 5 Mar 2020 22:58:18 +0000 Subject: [PATCH 1191/5615] Fix missing link to OrientDB in docs and update version --- mkdocs.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index f66f382ccb0..49876885dfd 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -52,6 +52,7 @@ nav: - modules/databases/mysql.md - modules/databases/neo4j.md - modules/databases/oraclexe.md + - modules/databases/orientdb.md - modules/databases/postgres.md - modules/databases/presto.md - modules/docker_compose.md @@ -85,4 +86,4 @@ nav: - contributing.md - contributing_docs.md extra: - latest_version: 1.12.5 + latest_version: 1.13.0 From b437227a065e0bc1ff5f58e6bd88fbaef9b58343 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 10 Mar 2020 07:32:17 +0100 Subject: [PATCH 1192/5615] Fix internal port check when other ports are opened as well on the target container (#2363) * Remove redundant execCreateCmd call This one is never used and probably causes some garbage on the docker side. * Allow leading zeros in /proc/net/tcp* when doing internal port listen check When at least one process is listening for a port that is significantly higher then the port we're checking, this check will fail as the checked port will contain leading zeros in the output of "cat /proc/net/tcp". This commit fixes this by changing the grep to ":0*XXX". * Surround grep parameter with ' Otherwise unnecessary shell expansion is tried * Refactor/Rewrite InternalCommandPortListeningCheckTest to test all possible checks This commit refactors InternalCommandPortListeningCheckTest to work with 3 different nginx based containers, each being able to only succeed one of the 3 tests performed in InternalCommandPortListeningCheck. Each test is now executed agains all 3 containers to ensure that all tests work as expected. * Add test for low+high ports This test should fail without the leading zeros fix found a few commits before this one. * Check for bash existence instead of only mentioning it in a comment * Rename nginx_on_8080.conf to nginx.conf It's not only port 8080 anymore, so better not have it in the name. * Make container a @Rule again by moving initialization into contructor * Wait a few seconds for InternalCommandPortListeningCheck to pass --- .../containers/ExecInContainerPattern.java | 4 -- .../InternalCommandPortListeningCheck.java | 2 +- ...InternalCommandPortListeningCheckTest.java | 55 ++++++++++++++++--- .../Dockerfile-bash | 11 ++++ .../Dockerfile-nc | 9 +++ .../Dockerfile-tcp | 9 +++ .../internal-port-check-dockerfile/nginx.conf | 8 +++ core/src/test/resources/nginx_on_8080.conf | 5 -- 8 files changed, 86 insertions(+), 17 deletions(-) create mode 100644 core/src/test/resources/internal-port-check-dockerfile/Dockerfile-bash create mode 100644 core/src/test/resources/internal-port-check-dockerfile/Dockerfile-nc create mode 100644 core/src/test/resources/internal-port-check-dockerfile/Dockerfile-tcp create mode 100644 core/src/test/resources/internal-port-check-dockerfile/nginx.conf delete mode 100644 core/src/test/resources/nginx_on_8080.conf diff --git a/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java b/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java index 3d0f4c2c360..359065d732b 100644 --- a/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java +++ b/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java @@ -66,10 +66,6 @@ public Container.ExecResult execInContainer(InspectContainerResponse containerIn DockerClient dockerClient = DockerClientFactory.instance().client(); - dockerClient - .execCreateCmd(containerId) - .withCmd(command); - log.debug("{}: Running \"exec\" command: {}", containerName, String.join(" ", command)); final ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(containerId) .withAttachStdout(true).withAttachStderr(true).withCmd(command).exec(); diff --git a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java index 2013ddc4d9c..9b4be81b4c4 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java +++ b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java @@ -29,7 +29,7 @@ public Boolean call() { for (int internalPort : internalPorts) { command += " && "; command += " ("; - command += format("cat /proc/net/tcp* | awk '{print $2}' | grep -i :%x", internalPort); + command += format("cat /proc/net/tcp* | awk '{print $2}' | grep -i ':0*%x'", internalPort); command += " || "; command += format("nc -vz -w 1 localhost %d", internalPort); command += " || "; diff --git a/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java b/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java index bee4f0a231a..00c0bc9d1ef 100644 --- a/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java +++ b/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java @@ -3,23 +3,47 @@ import com.google.common.collect.ImmutableSet; import org.junit.Rule; import org.junit.Test; -import org.testcontainers.containers.BindMode; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.rnorth.ducttape.TimeoutException; +import org.rnorth.ducttape.unreliables.Unreliables; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.images.builder.ImageFromDockerfile; +import java.util.concurrent.TimeUnit; + +import static java.util.Arrays.asList; import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +@RunWith(Parameterized.class) public class InternalCommandPortListeningCheckTest { - // Linking a custom configuration into the container so that nginx is listening on port 8080. This is necessary to prove - // that the command formatting uses the correct casing for hexadecimal numbers (i.e. 1F90 and not 1f90). + @Parameterized.Parameters(name = "{index} - {0}") + public static Iterable data() { + return asList( + new Object[][]{ + {"internal-port-check-dockerfile/Dockerfile-tcp"}, + {"internal-port-check-dockerfile/Dockerfile-nc"}, + {"internal-port-check-dockerfile/Dockerfile-bash"}, + }); + } + @Rule - public GenericContainer nginx = new GenericContainer<>("nginx:1.9.4") - .withClasspathResourceMapping("nginx_on_8080.conf", "/etc/nginx/conf.d/default.conf", BindMode.READ_ONLY); + public GenericContainer container; + + public InternalCommandPortListeningCheckTest(String dockerfile) { + container = new GenericContainer(new ImageFromDockerfile() + .withFileFromClasspath("Dockerfile", dockerfile) + .withFileFromClasspath("nginx.conf", "internal-port-check-dockerfile/nginx.conf") + ); + } @Test public void singleListening() { - final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(nginx, ImmutableSet.of(8080)); + final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(container, ImmutableSet.of(8080)); + + Unreliables.retryUntilTrue(5, TimeUnit.SECONDS, check); final Boolean result = check.call(); @@ -28,10 +52,27 @@ public void singleListening() { @Test public void nonListening() { - final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(nginx, ImmutableSet.of(8080, 1234)); + final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(container, ImmutableSet.of(8080, 1234)); + + try { + Unreliables.retryUntilTrue(5, TimeUnit.SECONDS, check); + } catch (TimeoutException e) { + // we expect it to timeout + } final Boolean result = check.call(); assertFalse("InternalCommandPortListeningCheck detects a non-listening port among many", result); } + + @Test + public void lowAndHighPortListening() { + final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(container, ImmutableSet.of(100, 8080)); + + Unreliables.retryUntilTrue(5, TimeUnit.SECONDS, check); + + final Boolean result = check.call(); + + assertTrue("InternalCommandPortListeningCheck identifies a low and a high port", result); + } } diff --git a/core/src/test/resources/internal-port-check-dockerfile/Dockerfile-bash b/core/src/test/resources/internal-port-check-dockerfile/Dockerfile-bash new file mode 100644 index 00000000000..02373da980e --- /dev/null +++ b/core/src/test/resources/internal-port-check-dockerfile/Dockerfile-bash @@ -0,0 +1,11 @@ +FROM nginx:1.17-alpine + +RUN apk add bash + +# Make sure the /proc/net/tcp* check fails in this container +RUN rm /usr/bin/awk + +# Make sure the nc check fails in this container +RUN rm /usr/bin/nc + +ADD nginx.conf /etc/nginx/conf.d/default.conf diff --git a/core/src/test/resources/internal-port-check-dockerfile/Dockerfile-nc b/core/src/test/resources/internal-port-check-dockerfile/Dockerfile-nc new file mode 100644 index 00000000000..f69b74a8754 --- /dev/null +++ b/core/src/test/resources/internal-port-check-dockerfile/Dockerfile-nc @@ -0,0 +1,9 @@ +FROM nginx:1.17-alpine + +# If this fails, you ended up using a base image with bash installed. Consider removing /bin/bash in this case +RUN if bash -c true &> /dev/null; then exit 1; fi + +# Make sure the /proc/net/tcp* check fails in this container +RUN rm /usr/bin/awk + +ADD nginx.conf /etc/nginx/conf.d/default.conf diff --git a/core/src/test/resources/internal-port-check-dockerfile/Dockerfile-tcp b/core/src/test/resources/internal-port-check-dockerfile/Dockerfile-tcp new file mode 100644 index 00000000000..8699db537aa --- /dev/null +++ b/core/src/test/resources/internal-port-check-dockerfile/Dockerfile-tcp @@ -0,0 +1,9 @@ +FROM nginx:1.17-alpine + +# If this fails, you ended up using a base image with bash installed. Consider removing /bin/bash in this case +RUN if bash -c true &> /dev/null; then exit 1; fi + +# Make sure the nc check fails in this container +RUN rm /usr/bin/nc + +ADD nginx.conf /etc/nginx/conf.d/default.conf diff --git a/core/src/test/resources/internal-port-check-dockerfile/nginx.conf b/core/src/test/resources/internal-port-check-dockerfile/nginx.conf new file mode 100644 index 00000000000..f69ddf345cf --- /dev/null +++ b/core/src/test/resources/internal-port-check-dockerfile/nginx.conf @@ -0,0 +1,8 @@ +# This configuration makes Nginx listen on port port 8080 + +server { + # Port 8080 is necessary to prove that the command formatting in the /proc/net/tcp* check uses the correct casing for hexadecimal numbers (i.e. 1F90 and not 1f90) + listen 8080; + # Port 100 is necessary to ensure that the /proc/net/tcp* check also succeeds with trailing zeros in the hexadecimal port + listen 100; +} diff --git a/core/src/test/resources/nginx_on_8080.conf b/core/src/test/resources/nginx_on_8080.conf deleted file mode 100644 index c7927d46265..00000000000 --- a/core/src/test/resources/nginx_on_8080.conf +++ /dev/null @@ -1,5 +0,0 @@ -# This configuration makes Nginx listen on port port 8080 - -server { - listen 8080; -} \ No newline at end of file From 16fda4216018d94f2bf9c20379d3a3573050a06c Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Tue, 10 Mar 2020 07:35:28 +0100 Subject: [PATCH 1193/5615] Fix invalid connection URLs returned by PostgreSQLContainer (fixes #1976) (#2049) * Reproduce #1976 in tests * Override constructUrlForConnection for PostgreSQLContainer * Lighten PostgreSQLConnectionURLTest * Lighten PostgreSQLConnectionURLTest * Cater for base URLs without query strings --- .../containers/PostgreSQLContainer.java | 22 +++++- .../PostgreSQLConnectionURLTest.java | 68 +++++++++++++++++++ 2 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index 85ad910aa17..cef66d0c492 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -7,6 +7,7 @@ import java.util.HashSet; import java.util.Set; +import static java.lang.String.format; import static java.time.temporal.ChronoUnit.SECONDS; /** @@ -24,6 +25,8 @@ public class PostgreSQLContainer> extends private static final String FSYNC_OFF_OPTION = "fsync=off"; + private static final String QUERY_PARAM_SEPARATOR = "&"; + public PostgreSQLContainer() { this(IMAGE + ":" + DEFAULT_TAG); } @@ -59,7 +62,24 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { // Disable Postgres driver use of java.util.logging to reduce noise at startup time - return "jdbc:postgresql://" + getContainerIpAddress() + ":" + getMappedPort(POSTGRESQL_PORT) + "/" + databaseName + "?loggerLevel=OFF"; + return format("jdbc:postgresql://%s:%d/%s?loggerLevel=OFF", getContainerIpAddress(), getMappedPort(POSTGRESQL_PORT), databaseName); + } + + @Override + protected String constructUrlForConnection(String queryString) { + String baseUrl = getJdbcUrl(); + + if ("".equals(queryString)) { + return baseUrl; + } + + if (!queryString.startsWith("?")) { + throw new IllegalArgumentException("The '?' character must be included"); + } + + return baseUrl.contains("?") + ? baseUrl + QUERY_PARAM_SEPARATOR + queryString.substring(1) + : baseUrl + queryString; } @Override diff --git a/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java new file mode 100644 index 00000000000..ec6a8756cab --- /dev/null +++ b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java @@ -0,0 +1,68 @@ +package org.testcontainers.containers; + +import org.junit.Test; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; +import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; + +public class PostgreSQLConnectionURLTest { + + @Test + public void shouldCorrectlyAppendQueryString() { + PostgreSQLContainer postgres = new FixedJdbcUrlPostgreSQLContainer(); + String connectionUrl = postgres.constructUrlForConnection("?stringtype=unspecified&stringtype=unspecified"); + String queryString = connectionUrl.substring(connectionUrl.indexOf('?')); + + assertTrue("Query String contains expected params", queryString.contains("&stringtype=unspecified&stringtype=unspecified")); + assertEquals("Query String starts with '?'", 0, queryString.indexOf('?')); + assertFalse("Query String does not contain extra '?'", queryString.substring(1).contains("?")); + } + + @Test + public void shouldCorrectlyAppendQueryStringWhenNoBaseParams() { + PostgreSQLContainer postgres = new NoParamsUrlPostgreSQLContainer(); + String connectionUrl = postgres.constructUrlForConnection("?stringtype=unspecified&stringtype=unspecified"); + String queryString = connectionUrl.substring(connectionUrl.indexOf('?')); + + assertTrue("Query String contains expected params", queryString.contains("?stringtype=unspecified&stringtype=unspecified")); + assertEquals("Query String starts with '?'", 0, queryString.indexOf('?')); + assertFalse("Query String does not contain extra '?'", queryString.substring(1).contains("?")); + } + + @Test + public void shouldReturnOriginalURLWhenEmptyQueryString() { + PostgreSQLContainer postgres = new FixedJdbcUrlPostgreSQLContainer(); + String connectionUrl = postgres.constructUrlForConnection(""); + + assertTrue("Query String remains unchanged", postgres.getJdbcUrl().equals(connectionUrl)); + } + + @Test + public void shouldRejectInvalidQueryString() { + assertThrows("Fails when invalid query string provided", IllegalArgumentException.class, + () -> new NoParamsUrlPostgreSQLContainer().constructUrlForConnection("stringtype=unspecified")); + } + + static class FixedJdbcUrlPostgreSQLContainer extends PostgreSQLContainer { + + @Override + public String getContainerIpAddress() { + return "localhost"; + } + + @Override + public Integer getMappedPort(int originalPort) { + return 34532; + } + } + + static class NoParamsUrlPostgreSQLContainer extends PostgreSQLContainer { + + @Override + public String getJdbcUrl() { + return "jdbc:postgresql://host:port/database"; + } + } +} From 7d5f4c9e35b5d671f24125395aed3f741f6c3d9e Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 12 Mar 2020 21:14:19 +0100 Subject: [PATCH 1194/5615] Update to docker-java 3.2.0 (#2297) * Test new `docker-java` module * global Jitpack repo * update to the current master of docker-java * try docker-java-core * use 3.2.0-rc1 * update to docker-java 3.2.0-rc2 * remove unnecessary dependency * Update to docker-java 3.2.0-rc3, use their okhttp cmd factory * post-merge fixes * Update to docker-java 3.2.0-rc4 * fix JarFileShadingTest * Add japicmp task * Update to 3.2.0-rc5 * postpone shading changes * reduce the changeset * do not run japicmp on Windows * try running modules' tests without the checks * split module runs * fix the tasks list * fix the tasks list (2) * do not disable the checks * try `postCheckCommand` * make `postCheckCommand` optional * Update azure-pipelines.yml * Update to docker-java 3.2.0 * restore `postCheckCommand` --- core/build.gradle | 66 ++++++++++++++++--- .../testcontainers/JarFileShadingTest.java | 1 - .../testcontainers/PublicBinaryAPITest.java | 12 ++++ .../containers/GenericContainer.java | 8 +-- .../containers/VncRecordingContainer.java | 5 +- .../AuditLoggingDockerClient.java | 3 + .../dockerclient/DockerClientConfigUtils.java | 9 ++- .../DockerClientProviderStrategy.java | 9 ++- .../NpipeSocketClientProviderStrategy.java | 13 +--- .../AuthDelegatingDockerClientConfig.java | 3 + .../okhttp/NamedPipeSocketFactory.java | 4 ++ .../okhttp/OkHttpDockerCmdExecFactory.java | 12 +++- .../transport/okhttp/OkHttpWebTarget.java | 8 +-- .../transport/okhttp/UnixSocketFactory.java | 4 ++ ...eLimitedLoggedPullImageResultCallback.java | 5 +- .../couchbase/CouchbaseContainer.java | 3 +- .../containers/KafkaContainer.java | 3 +- 17 files changed, 123 insertions(+), 45 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index 5040a487f50..0d452f5cc1d 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -1,3 +1,18 @@ +buildscript { + repositories { + jcenter() + } + + dependencies { + // https://github.com/melix/japicmp-gradle-plugin/issues/36 + classpath 'com.google.guava:guava:28.2-jre' + } +} + +plugins { + id 'me.champeau.gradle.japicmp' version '0.2.8' +} + apply plugin: 'com.github.johnrengelman.shadow' description = "Testcontainers Core" @@ -9,6 +24,7 @@ sourceSets { idea.module.testSourceDirs += sourceSets.jarFileTest.allSource.srcDirs configurations { + baseline shaded [runtime, compileOnly, testCompile]*.extendsFrom shaded } @@ -55,7 +71,47 @@ task jarFileTest(type: Test) { } project.tasks.check.dependsOn(jarFileTest) +task japicmp(type: me.champeau.gradle.japicmp.JapicmpTask) { + dependsOn(tasks.shadowJar) + + oldClasspath = configurations.baseline + newClasspath = shadowJar.outputs.files + ignoreMissingClasses = true + + accessModifier = "protected" + failOnModification = true + failOnSourceIncompatibility = true + + // TODO cleanup excludes after we release the version based on docker-java 3.2.0 + + packageExcludes = [ + "com.github.dockerjava.*", + "org.testcontainers.shaded.*", + ] + + classExcludes = [ + // 'METHOD_REMOVED_IN_SUPERCLASS' + "org.testcontainers.containers.output.FrameConsumerResultCallback", + "org.testcontainers.dockerclient.LogToStringContainerCallback", + + // 'METHOD_RETURN_TYPE_CHANGED', exposed `com.github.dockerjava.core.command.PullImageResultCallback` to the public API + "org.testcontainers.images.TimeLimitedLoggedPullImageResultCallback", + ] + + onlyBinaryIncompatibleModified = true + htmlOutputFile = file("$buildDir/reports/japi.html") +} +// do not run on Windows by default +// TODO investigate zip issue on Windows +if (!org.gradle.internal.os.OperatingSystem.current().isWindows()) { + project.tasks.check.dependsOn(japicmp) +} + dependencies { + baseline 'org.testcontainers:testcontainers:1.12.5', { + exclude group: "*", module: "*" + } + compile 'junit:junit:4.12' compile 'org.slf4j:slf4j-api:1.7.30' compile 'org.jetbrains:annotations:17.0.0' @@ -82,15 +138,9 @@ dependencies { exclude(group: "net.java.dev.jna") } - shaded ('com.github.docker-java:docker-java:3.1.5') { - exclude(group: 'org.glassfish.jersey.core') - exclude(group: 'org.glassfish.jersey.connectors') - exclude(group: 'org.glassfish.jersey.inject') - exclude(group: 'log4j') + shaded ('com.github.docker-java:docker-java-transport-okhttp:3.2.0') { + exclude(group: 'net.java.dev.jna') exclude(group: 'com.google.code.findbug') - exclude(group: 'com.kohlschutter.junixsocket') - exclude(group: 'io.netty') - exclude(group: 'org.apache.httpcomponents') exclude(group: 'org.slf4j') exclude(group: 'org.apache.commons', module: 'commons-compress') } diff --git a/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java b/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java index cf807087d55..587ab6277f6 100644 --- a/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java +++ b/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java @@ -14,7 +14,6 @@ public class JarFileShadingTest extends AbstractJarFileTest { @Test public void testPackages() throws Exception { assertThatFileList(root).containsOnly( - "docker-java.properties", "org", "META-INF", "com" diff --git a/core/src/jarFileTest/java/org/testcontainers/PublicBinaryAPITest.java b/core/src/jarFileTest/java/org/testcontainers/PublicBinaryAPITest.java index 6d995e7e0be..93d1a759096 100644 --- a/core/src/jarFileTest/java/org/testcontainers/PublicBinaryAPITest.java +++ b/core/src/jarFileTest/java/org/testcontainers/PublicBinaryAPITest.java @@ -2,6 +2,8 @@ import lombok.RequiredArgsConstructor; import org.assertj.core.api.Assertions; +import org.junit.Assume; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -84,6 +86,16 @@ public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IO private final ClassNode classNode; + @Before + public void setUp() { + Assume.assumeFalse(classNode.name.startsWith("com.github.dockerjava.")); + switch (classNode.name) { + // TODO should go to docker-java project + case "org/testcontainers/dockerclient/auth/AuthDelegatingDockerClientConfig": + Assume.assumeTrue(false); + } + } + @Test public void testSuperClass() { assertThat(classNode.superName) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index ee458339d0a..03d03759506 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -20,12 +20,12 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; +import com.google.common.hash.Hashing; import lombok.AccessLevel; import lombok.Data; import lombok.NonNull; import lombok.Setter; import lombok.SneakyThrows; -import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.apache.commons.compress.utils.IOUtils; @@ -520,12 +520,12 @@ void checksumFile(File file, Checksum checksum) { @SneakyThrows(JsonProcessingException.class) final String hash(CreateContainerCmd createCommand) { // TODO add Testcontainers' version to the hash - String commandJson = new ObjectMapper() + byte[] commandJson = new ObjectMapper() .enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY) .enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS) - .writeValueAsString(createCommand); + .writeValueAsBytes(createCommand); - return DigestUtils.sha1Hex(commandJson); + return Hashing.sha1().hashBytes(commandJson).toString(); } @VisibleForTesting diff --git a/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java b/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java index 1b06215b706..298593a116b 100644 --- a/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java +++ b/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java @@ -5,7 +5,6 @@ import lombok.NonNull; import lombok.SneakyThrows; import lombok.ToString; -import org.apache.commons.codec.binary.Base64; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.rnorth.ducttape.TimeoutException; import org.rnorth.ducttape.unreliables.Unreliables; @@ -17,6 +16,7 @@ import java.io.InputStream; import java.nio.file.Files; import java.nio.file.StandardCopyOption; +import java.util.Base64; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -112,9 +112,10 @@ public VncRecordingContainer withFrameRate(int frameRate) { @Override protected void configure() { withCreateContainerCmdModifier(it -> it.withEntrypoint("/bin/sh")); + String encodedPassword = Base64.getEncoder().encodeToString(vncPassword.getBytes()); setCommand( "-c", - "echo '" + Base64.encodeBase64String(vncPassword.getBytes()) + "' | base64 -d > /vnc_password && " + + "echo '" + encodedPassword + "' | base64 -d > /vnc_password && " + "flvrec.py -o " + RECORDING_FILE_NAME + " -d -r " + frameRate + " -P /vnc_password " + targetNetworkAlias + " " + vncPort ); } diff --git a/core/src/main/java/org/testcontainers/dockerclient/AuditLoggingDockerClient.java b/core/src/main/java/org/testcontainers/dockerclient/AuditLoggingDockerClient.java index f982945f9d4..891709e3a0c 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/AuditLoggingDockerClient.java +++ b/core/src/main/java/org/testcontainers/dockerclient/AuditLoggingDockerClient.java @@ -15,9 +15,12 @@ /** * Wrapper for {@link DockerClient} to facilitate 'audit logging' of potentially destruction actions using * {@link org.testcontainers.utility.AuditLogger}. + * + * @deprecated should not be used publicly */ @Slf4j @SuppressWarnings("unchecked") +@Deprecated public class AuditLoggingDockerClient implements DockerClient { @Delegate(excludes = InterceptedMethods.class) diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java index d88dc76331f..1060d663d65 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java @@ -7,6 +7,7 @@ import org.testcontainers.DockerClientFactory; import java.io.File; +import java.net.URI; import java.util.Optional; import static java.util.concurrent.TimeUnit.SECONDS; @@ -48,11 +49,15 @@ public class DockerClientConfigUtils { */ @Deprecated public static String getDockerHostIpAddress(DockerClientConfig config) { - switch (config.getDockerHost().getScheme()) { + return getDockerHostIpAddress(config.getDockerHost()); + } + + public static String getDockerHostIpAddress(URI dockerHost) { + switch (dockerHost.getScheme()) { case "http": case "https": case "tcp": - return config.getDockerHost().getHost(); + return dockerHost.getHost(); case "unix": case "npipe": if (IN_A_CONTAINER) { diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index aa607964ae5..1c5aea87723 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -2,8 +2,9 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.model.Network; -import com.github.dockerjava.core.DockerClientBuilder; import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.core.DockerClientImpl; +import com.github.dockerjava.okhttp.OkHttpDockerCmdExecFactory; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Throwables; import org.apache.commons.io.IOUtils; @@ -16,7 +17,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testcontainers.dockerclient.auth.AuthDelegatingDockerClientConfig; -import org.testcontainers.dockerclient.transport.okhttp.OkHttpDockerCmdExecFactory; import org.testcontainers.utility.TestcontainersConfiguration; import java.net.URI; @@ -172,10 +172,9 @@ public DockerClient getClient() { } protected DockerClient getClientForConfig(DockerClientConfig config) { - return DockerClientBuilder + return DockerClientImpl .getInstance(new AuthDelegatingDockerClientConfig(config)) - .withDockerCmdExecFactory(new OkHttpDockerCmdExecFactory()) - .build(); + .withDockerCmdExecFactory(new OkHttpDockerCmdExecFactory()); } protected void ping(DockerClient client, int timeoutInSeconds) { diff --git a/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java index b8d85f11623..329bde36433 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java @@ -8,8 +8,6 @@ import org.apache.commons.lang.SystemUtils; import org.jetbrains.annotations.NotNull; -import java.net.URI; - @Slf4j public class NpipeSocketClientProviderStrategy extends DockerClientProviderStrategy { @@ -38,19 +36,12 @@ public void test() throws InvalidConfigurationException { @NotNull private DockerClientConfig tryConfiguration() { - URI dockerHost = URI.create(SOCKET_LOCATION); - config = new DelegatingDockerClientConfig( DefaultDockerClientConfig.createDefaultConfigBuilder() - .withDockerHost("tcp://localhost:0") + .withDockerHost(SOCKET_LOCATION) .withDockerTlsVerify(false) .build() - ) { - @Override - public URI getDockerHost() { - return dockerHost; - } - }; + ); client = getClientForConfig(config); final int timeout = Integer.parseInt(System.getProperty(PING_TIMEOUT_PROPERTY_NAME, PING_TIMEOUT_DEFAULT)); diff --git a/core/src/main/java/org/testcontainers/dockerclient/auth/AuthDelegatingDockerClientConfig.java b/core/src/main/java/org/testcontainers/dockerclient/auth/AuthDelegatingDockerClientConfig.java index d75f3ce6bc1..a68605fdfda 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/auth/AuthDelegatingDockerClientConfig.java +++ b/core/src/main/java/org/testcontainers/dockerclient/auth/AuthDelegatingDockerClientConfig.java @@ -14,8 +14,11 @@ * configuration is obtained. A delegate {@link DockerClientConfig} will be called first * to try and obtain auth credentials, but after that {@link RegistryAuthLocator} will be * used to try and improve the auth resolution (e.g. using credential helpers). + * + * @deprecated should not be used publicly, to be moved to docker-java */ @Slf4j +@Deprecated public class AuthDelegatingDockerClientConfig implements DockerClientConfig { @Delegate(excludes = DelegateExclusions.class) diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/NamedPipeSocketFactory.java b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/NamedPipeSocketFactory.java index d150eedb6a5..d26e2977318 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/NamedPipeSocketFactory.java +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/NamedPipeSocketFactory.java @@ -13,6 +13,10 @@ import java.net.SocketAddress; import java.util.concurrent.TimeUnit; +/** + * @deprecated use com.github.docker-java:docker-java-transport-okhttp + */ +@Deprecated @Value @EqualsAndHashCode(callSuper = false) public class NamedPipeSocketFactory extends SocketFactory { diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpDockerCmdExecFactory.java b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpDockerCmdExecFactory.java index 841d012e2b3..4da568834d5 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpDockerCmdExecFactory.java +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpDockerCmdExecFactory.java @@ -1,5 +1,6 @@ package org.testcontainers.dockerclient.transport.okhttp; +import com.fasterxml.jackson.databind.ObjectMapper; import com.github.dockerjava.api.command.PingCmd; import com.github.dockerjava.core.AbstractDockerCmdExecFactory; import com.github.dockerjava.core.DockerClientConfig; @@ -25,11 +26,17 @@ import java.util.Collections; import java.util.concurrent.TimeUnit; +/** + * @deprecated use com.github.docker-java:docker-java-transport-okhttp + */ +@Deprecated @Slf4j public class OkHttpDockerCmdExecFactory extends AbstractDockerCmdExecFactory { private static final String SOCKET_SUFFIX = ".socket"; + private ObjectMapper objectMapper; + private OkHttpClient okHttpClient; private HttpUrl baseUrl; @@ -100,11 +107,14 @@ public void init(DockerClientConfig dockerClientConfig) { baseUrlBuilder = HttpUrl.get(dockerHost.toString()).newBuilder(); } baseUrl = baseUrlBuilder.build(); + + objectMapper = dockerClientConfig.getObjectMapper(); } @Override - protected WebTarget getBaseResource() { + protected OkHttpWebTarget getBaseResource() { return new OkHttpWebTarget( + objectMapper, okHttpClient, baseUrl, ImmutableList.of(), diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpWebTarget.java b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpWebTarget.java index 7e12b0b4541..9c360522d20 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpWebTarget.java +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpWebTarget.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; import com.github.dockerjava.core.InvocationBuilder; import com.github.dockerjava.core.WebTarget; import com.google.common.collect.HashMultimap; @@ -25,8 +24,7 @@ @Value class OkHttpWebTarget implements WebTarget { - private static final ObjectMapper MAPPER = new ObjectMapper() - .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); + ObjectMapper objectMapper; OkHttpClient okHttpClient; @@ -56,7 +54,7 @@ public InvocationBuilder request() { } return new OkHttpInvocationBuilder( - MAPPER, + objectMapper, okHttpClient, baseUrlBuilder.build() ); @@ -110,6 +108,6 @@ public OkHttpWebTarget queryParamsJsonMap(String name, Map value } // when param value is JSON string - return queryParam(name, MAPPER.writeValueAsString(values)); + return queryParam(name, objectMapper.writeValueAsString(values)); } } diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/UnixSocketFactory.java b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/UnixSocketFactory.java index 2fbdb936447..d1e55f3e647 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/UnixSocketFactory.java +++ b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/UnixSocketFactory.java @@ -15,6 +15,10 @@ import java.net.Socket; import java.net.SocketAddress; +/** + * @deprecated use com.github.docker-java:docker-java-transport-okhttp + */ +@Deprecated @Value @EqualsAndHashCode(callSuper = false) public class UnixSocketFactory extends SocketFactory { diff --git a/core/src/main/java/org/testcontainers/images/TimeLimitedLoggedPullImageResultCallback.java b/core/src/main/java/org/testcontainers/images/TimeLimitedLoggedPullImageResultCallback.java index 20b7de6c666..274eedefded 100644 --- a/core/src/main/java/org/testcontainers/images/TimeLimitedLoggedPullImageResultCallback.java +++ b/core/src/main/java/org/testcontainers/images/TimeLimitedLoggedPullImageResultCallback.java @@ -47,9 +47,10 @@ public TimeLimitedLoggedPullImageResultCallback(Logger logger) { } @Override - public PullImageResultCallback awaitCompletion() throws InterruptedException { + public TimeLimitedLoggedPullImageResultCallback awaitCompletion() throws InterruptedException { waitingThreads.add(Thread.currentThread()); - return super.awaitCompletion(); + super.awaitCompletion(); + return this; } @Override diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index 19583d5a0e4..e43cf8f652d 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -26,7 +26,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.github.dockerjava.api.command.ExecCreateCmdResponse; import com.github.dockerjava.api.command.InspectContainerResponse; -import com.github.dockerjava.core.command.ExecStartResultCallback; import com.google.common.collect.Lists; import lombok.*; import org.apache.commons.compress.utils.Sets; @@ -171,7 +170,7 @@ private void startProxy(String networkAlias) { .exec(); dockerClient.execStartCmd(createCmdResponse.getId()) - .exec(new ExecStartResultCallback()) + .start() .awaitCompletion(10, TimeUnit.SECONDS); } diff --git a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java index b0ce047f5d4..5aab0f52ffa 100644 --- a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java +++ b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java @@ -2,7 +2,6 @@ import com.github.dockerjava.api.command.ExecCreateCmdResponse; import com.github.dockerjava.api.command.InspectContainerResponse; -import com.github.dockerjava.core.command.ExecStartResultCallback; import lombok.SneakyThrows; import org.testcontainers.images.builder.Transferable; import org.testcontainers.utility.TestcontainersConfiguration; @@ -150,7 +149,7 @@ private String startZookeeper() { ) .exec(); - dockerClient.execStartCmd(execCreateCmdResponse.getId()).exec(new ExecStartResultCallback()).awaitStarted(10, TimeUnit.SECONDS); + dockerClient.execStartCmd(execCreateCmdResponse.getId()).start().awaitStarted(10, TimeUnit.SECONDS); return "localhost:" + ZOOKEEPER_PORT; } From 3f9216ebda2087b3311de979a96918e3ccaefc2d Mon Sep 17 00:00:00 2001 From: MariuszCwikla <47976407+MariuszCwikla@users.noreply.github.com> Date: Fri, 13 Mar 2020 10:53:36 +0100 Subject: [PATCH 1195/5615] Upgrade localstack to 0.10.8 (#2413) --- .../containers/localstack/LocalStackContainer.java | 2 +- .../containers/localstack/LocalstackContainerTest.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java index e21cc735447..0d8c3627f6c 100644 --- a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java +++ b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java @@ -29,7 +29,7 @@ */ public class LocalStackContainer extends GenericContainer { - public static final String VERSION = "0.9.4"; + public static final String VERSION = "0.10.8"; private static final String HOSTNAME_EXTERNAL_ENV_VAR = "HOSTNAME_EXTERNAL"; private final List services = new ArrayList<>(); diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java index 7ad31f6340f..034ff2f0914 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java @@ -105,7 +105,6 @@ public void sqsTestOverBridgeNetwork() { } @Test - @Ignore("Fails due to https://github.com/localstack/localstack/issues/1434") public void cloudWatchLogsTestOverBridgeNetwork() { AWSLogs logs = AWSLogsClientBuilder.standard() .withEndpointConfiguration(localstack.getEndpointConfiguration(CLOUDWATCHLOGS)) From 4f8f8b143d6d462257500959be9fa0eb37f2fa12 Mon Sep 17 00:00:00 2001 From: Sandra Parsick Date: Wed, 18 Mar 2020 14:02:57 +0100 Subject: [PATCH 1196/5615] Extend docs with registry mirror as workaround for custom images (#2452) Add a link to registry mirror documentation in docker as a possibility when direct usage of docker hub is not allowed Co-Authored-By: Kevin Wittek --- docs/features/configuration.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/features/configuration.md b/docs/features/configuration.md index 61500cb9cb5..cd01b99b1c7 100644 --- a/docs/features/configuration.md +++ b/docs/features/configuration.md @@ -45,6 +45,9 @@ Some companies disallow the usage of Docker Hub, but you can override `*.image` > **localstack.container.image = localstack/localstack** > Used by LocalStack +Another possibility is to set up a registry mirror in your environment so that all images are pulled from there and not directly from Docker Hub. +For more information, see the [official Docker documentation about "Registry as a pull through cache"](https://docs.docker.com/registry/recipes/mirror/). + ## Customizing Ryuk resource reaper > **ryuk.container.image = quay.io/testcontainers/ryuk:0.2.3** From d4f894f081838de96f0053312b08dc2f9a365a64 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 25 Mar 2020 11:44:09 +0100 Subject: [PATCH 1197/5615] Fallback to `getDefaultGateway()` if "bridge" is unavailable. (#2429) Fixes #2239 --- .../dockerclient/DockerClientProviderStrategy.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index 1c5aea87723..c305d56389c 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -218,7 +218,9 @@ static String resolveDockerHostIpAddress(DockerClient client, URI dockerHost) { .filter(it -> it.getGateway() != null) .findAny() .map(Network.Ipam.Config::getGateway) - .orElse("localhost"); + .orElseGet(() -> { + return DockerClientConfigUtils.getDefaultGateway().orElse("localhost"); + }); } return "localhost"; default: From 7acb08d6c5c99f94a40998e00fb1db18cc66c061 Mon Sep 17 00:00:00 2001 From: Anton Deriabin Date: Wed, 25 Mar 2020 14:00:27 +0300 Subject: [PATCH 1198/5615] Bump Cucucmber example version to 5.5.0 (#2476) * Bump Cucucmber example version to 5.5.0 * Fixed wrong imports --- examples/cucumber/build.gradle | 4 ++-- .../org/testcontainsers/examples/CucumberTest.java | 4 ++-- .../java/org/testcontainsers/examples/Stepdefs.java | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 5d2caed8c2a..52c59229350 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' - implementation 'io.cucumber:cucumber-java:4.8.0' - testImplementation 'io.cucumber:cucumber-junit:4.8.0' + testImplementation 'io.cucumber:cucumber-junit:5.5.0' + testImplementation 'io.cucumber:cucumber-java:5.5.0' testImplementation 'org.testcontainers:selenium' } diff --git a/examples/cucumber/src/test/java/org/testcontainsers/examples/CucumberTest.java b/examples/cucumber/src/test/java/org/testcontainsers/examples/CucumberTest.java index df1a449f1a9..0551b4dbf2e 100644 --- a/examples/cucumber/src/test/java/org/testcontainsers/examples/CucumberTest.java +++ b/examples/cucumber/src/test/java/org/testcontainsers/examples/CucumberTest.java @@ -1,7 +1,7 @@ package org.testcontainsers.examples; -import cucumber.api.CucumberOptions; -import cucumber.api.junit.Cucumber; +import io.cucumber.junit.Cucumber; +import io.cucumber.junit.CucumberOptions; import org.junit.runner.RunWith; @RunWith(Cucumber.class) diff --git a/examples/cucumber/src/test/java/org/testcontainsers/examples/Stepdefs.java b/examples/cucumber/src/test/java/org/testcontainsers/examples/Stepdefs.java index ee3c05d4457..ca70173ae92 100644 --- a/examples/cucumber/src/test/java/org/testcontainsers/examples/Stepdefs.java +++ b/examples/cucumber/src/test/java/org/testcontainsers/examples/Stepdefs.java @@ -1,11 +1,11 @@ package org.testcontainsers.examples; -import cucumber.api.Scenario; -import cucumber.api.java.After; -import cucumber.api.java.Before; -import cucumber.api.java.en.Given; -import cucumber.api.java.en.Then; -import cucumber.api.java.en.When; +import io.cucumber.java.After; +import io.cucumber.java.Before; +import io.cucumber.java.Scenario; +import io.cucumber.java.en.Given; +import io.cucumber.java.en.Then; +import io.cucumber.java.en.When; import org.openqa.selenium.WebElement; import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.remote.RemoteWebDriver; From a51b1e0d457f652dcd236320b3e089bc2ee0228c Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Thu, 26 Mar 2020 17:36:24 +0100 Subject: [PATCH 1199/5615] Add custom exception message when HealthState not present in response (#2206) See ##2205 --- .../org/testcontainers/containers/ContainerState.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/ContainerState.java b/core/src/main/java/org/testcontainers/containers/ContainerState.java index 043bae1d52f..ad006aa3b43 100644 --- a/core/src/main/java/org/testcontainers/containers/ContainerState.java +++ b/core/src/main/java/org/testcontainers/containers/ContainerState.java @@ -1,6 +1,7 @@ package org.testcontainers.containers; import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.command.HealthState; import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.exception.DockerException; import com.github.dockerjava.api.model.ExposedPort; @@ -89,9 +90,12 @@ default boolean isHealthy() { try { InspectContainerResponse inspectContainerResponse = getCurrentContainerInfo(); - String healthStatus = inspectContainerResponse.getState().getHealth().getStatus(); + HealthState health = inspectContainerResponse.getState().getHealth(); + if (health == null) { + throw new RuntimeException("This container's image does not have a healthcheck declared, so health cannot be determined. Either amend the image or use another approach to determine whether containers are healthy."); + } - return healthStatus.equals(STATE_HEALTHY); + return STATE_HEALTHY.equals(health.getStatus()); } catch (DockerException e) { return false; } From 682bfac4e45b5a900f9ce9d6ca687af692ed6e8c Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 31 Mar 2020 14:41:08 +0200 Subject: [PATCH 1200/5615] Use `@DynamicPropertySource` in the Spring Boot example (#2495) --- examples/spring-boot/build.gradle | 2 +- .../com/example/AbstractIntegrationTest.java | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index bd9cc9e95f2..6cd1db0172a 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.2.5.RELEASE' + id 'org.springframework.boot' version '2.2.6.RELEASE' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java b/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java index 3b092a05ca0..29a1f51ffcc 100644 --- a/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java +++ b/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java @@ -4,6 +4,8 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.testcontainers.containers.GenericContainer; @@ -15,13 +17,13 @@ @ActiveProfiles("test") public abstract class AbstractIntegrationTest { - static { - GenericContainer redis = new GenericContainer("redis:3-alpine") - .withExposedPorts(6379); - redis.start(); - - System.setProperty("spring.redis.host", redis.getContainerIpAddress()); - System.setProperty("spring.redis.port", redis.getFirstMappedPort() + ""); + static GenericContainer redis = new GenericContainer("redis:3-alpine") + .withExposedPorts(6379); + @DynamicPropertySource + static void redisProperties(DynamicPropertyRegistry registry) { + redis.start(); + registry.add("spring.redis.host", redis::getContainerIpAddress); + registry.add("spring.redis.port", redis::getFirstMappedPort); } } From 7c635e0c86adf3c88bad081d124ea651736c0738 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 4 Apr 2020 20:02:26 +0100 Subject: [PATCH 1201/5615] Bump mockito-core from 3.3.0 to 3.3.3 in /core (#2439) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.3.0 to 3.3.3. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.3.0...v3.3.3) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 0d452f5cc1d..6b9a1994ebd 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -159,7 +159,7 @@ dependencies { testCompile 'redis.clients:jedis:3.2.0' testCompile 'com.rabbitmq:amqp-client:5.8.0' testCompile 'org.mongodb:mongo-java-driver:3.12.1' - testCompile ('org.mockito:mockito-core:3.3.0') { + testCompile ('org.mockito:mockito-core:3.3.3') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From 0c02e71a507cdeb106aaf692ccc33bccd5729a6c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 4 Apr 2020 20:02:35 +0100 Subject: [PATCH 1202/5615] Bump httpclient from 4.5.11 to 4.5.12 in /modules/spock (#2411) Bumps httpclient from 4.5.11 to 4.5.12. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 074711d8bda..19a2a78f1d5 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -11,7 +11,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') testCompile 'com.zaxxer:HikariCP:3.4.2' - testCompile 'org.apache.httpcomponents:httpclient:4.5.11' + testCompile 'org.apache.httpcomponents:httpclient:4.5.12' testRuntime 'org.postgresql:postgresql:42.2.10' testRuntime 'mysql:mysql-connector-java:8.0.19' From ab72e1b07759a292bdd256aa53f274f083aa3339 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 4 Apr 2020 20:02:44 +0100 Subject: [PATCH 1203/5615] Bump postgresql from 42.2.10 to 42.2.12 in /modules/spock (#2502) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.10 to 42.2.12. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.10...REL42.2.12) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 19a2a78f1d5..2bcf7431808 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -13,7 +13,7 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.4.2' testCompile 'org.apache.httpcomponents:httpclient:4.5.12' - testRuntime 'org.postgresql:postgresql:42.2.10' + testRuntime 'org.postgresql:postgresql:42.2.12' testRuntime 'mysql:mysql-connector-java:8.0.19' } From 7025e3fbdb934962454070b00b6444936c1f9ecd Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 4 Apr 2020 20:03:01 +0100 Subject: [PATCH 1204/5615] =?UTF-8?q?Bump=20elasticsearch-rest-client=20fr?= =?UTF-8?q?om=207.6.1=20to=207.6.2=20in=20/modules/=E2=80=A6=20(#2501)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 7.6.1 to 7.6.2. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.6.1...v7.6.2) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 75697344874..6deaf802112 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,6 +2,6 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') - testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.6.1" + testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.6.2" testCompile "org.elasticsearch.client:transport:6.7.1" } From b3ec8a38a6da5c88b1989ba0d9b3765dcdb44675 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 4 Apr 2020 20:03:09 +0100 Subject: [PATCH 1205/5615] Bump postgresql from 42.2.10 to 42.2.12 in /examples (#2500) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.10 to 42.2.12. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.10...REL42.2.12) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 081b2ac04fa..4f9a699504d 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' implementation 'com.squareup.okhttp3:okhttp:3.14.6' implementation 'org.json:json:20180813' - testImplementation 'org.postgresql:postgresql:42.2.10' + testImplementation 'org.postgresql:postgresql:42.2.12' testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.testcontainers:postgresql' } From b96c84bc2ae71adea293a50015ccd26f520ddb81 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 4 Apr 2020 20:03:24 +0100 Subject: [PATCH 1206/5615] =?UTF-8?q?Bump=20postgresql=20from=2042.2.10=20?= =?UTF-8?q?to=2042.2.12=20in=20/modules/junit-jupit=E2=80=A6=20(#2498)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.10 to 42.2.12. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.10...REL42.2.12) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 7f6d4533509..445bdb3586d 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -13,7 +13,7 @@ dependencies { exclude(module: 'hamcrest-core') } - testRuntime 'org.postgresql:postgresql:42.2.10' + testRuntime 'org.postgresql:postgresql:42.2.12' testRuntime 'mysql:mysql-connector-java:8.0.19' testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.6.0' } From 2ef0ef28d39d327d351ad96623191fbcc921b238 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 4 Apr 2020 20:03:39 +0100 Subject: [PATCH 1207/5615] Bump tomcat-jdbc from 9.0.31 to 9.0.33 in /modules/mysql (#2445) Bumps tomcat-jdbc from 9.0.31 to 9.0.33. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 11c522197b7..da609a1b535 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -5,7 +5,7 @@ dependencies { testCompile 'mysql:mysql-connector-java:8.0.19' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.31' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.33' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.vibur:vibur-dbcp:25.0' } From e4633d827d057e260d2324d7e9103cd1bcc894cd Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 4 Apr 2020 20:03:51 +0100 Subject: [PATCH 1208/5615] Bump mariadb-java-client from 2.5.4 to 2.6.0 in /modules/mariadb (#2461) Bumps [mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 2.5.4 to 2.6.0. - [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases) - [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md) - [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/commits) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index c479824ae47..505b67ba9e6 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: JDBC :: MariaDB" dependencies { compile project(':jdbc') - testCompile 'org.mariadb.jdbc:mariadb-java-client:2.5.4' + testCompile 'org.mariadb.jdbc:mariadb-java-client:2.6.0' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.31' From d64e5790b51e1fdccba86f5c13a8c40b9f3af2a0 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 4 Apr 2020 20:03:57 +0100 Subject: [PATCH 1209/5615] Bump kafka-clients from 2.4.0 to 2.4.1 in /modules/kafka (#2424) Bumps kafka-clients from 2.4.0 to 2.4.1. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index 3960cd96b69..cd6745e1eae 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Kafka" dependencies { compile project(':testcontainers') - testCompile 'org.apache.kafka:kafka-clients:2.4.0' + testCompile 'org.apache.kafka:kafka-clients:2.4.1' testCompile 'org.assertj:assertj-core:3.15.0' testCompile 'com.google.guava:guava:23.0' } From 19d954c2ce08f9e762a52e55491bb4108e5a7c17 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 4 Apr 2020 20:04:09 +0100 Subject: [PATCH 1210/5615] Bump testng from 7.1.1 to 7.2.0 in /examples (#2441) Bumps [testng](https://github.com/cbeust/testng) from 7.1.1 to 7.2.0. - [Release notes](https://github.com/cbeust/testng/releases) - [Changelog](https://github.com/cbeust/testng/blob/master/CHANGES.txt) - [Commits](https://github.com/cbeust/testng/commits) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- examples/redis-backed-cache-testng/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index efe713d5c0e..1779966c692 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -13,7 +13,7 @@ dependencies { implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'ch.qos.logback:logback-classic:1.2.3' - testImplementation 'org.testng:testng:7.1.1' + testImplementation 'org.testng:testng:7.2.0' } test { From b6866be6c29795c9499e73c91b3a8b40b5c9966e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 4 Apr 2020 20:04:23 +0100 Subject: [PATCH 1211/5615] Bump postgresql from 42.2.10 to 42.2.12 in /modules/postgresql (#2499) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.10 to 42.2.12. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.10...REL42.2.12) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 9acf9ead2aa..89d40fe6923 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: JDBC :: PostgreSQL" dependencies { compile project(':jdbc') - testCompile 'org.postgresql:postgresql:42.2.10' + testCompile 'org.postgresql:postgresql:42.2.12' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' } From 5c2714f55c665bcd9f266b7039d39613f448ae65 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 4 Apr 2020 20:04:45 +0100 Subject: [PATCH 1212/5615] Bump tomcat-jdbc from 9.0.31 to 9.0.33 in /modules/mariadb (#2444) Bumps tomcat-jdbc from 9.0.31 to 9.0.33. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 505b67ba9e6..be42121f3d8 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -6,6 +6,6 @@ dependencies { testCompile 'org.mariadb.jdbc:mariadb-java-client:2.6.0' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.31' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.33' testCompile 'org.vibur:vibur-dbcp:25.0' } From a65789df7b77582f10e5255cc4002f6e5ce4c82e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 4 Apr 2020 20:04:55 +0100 Subject: [PATCH 1213/5615] Bump okhttp from 3.14.6 to 3.14.7 in /examples (#2481) Bumps [okhttp](https://github.com/square/okhttp) from 3.14.6 to 3.14.7. - [Release notes](https://github.com/square/okhttp/releases) - [Changelog](https://github.com/square/okhttp/blob/master/docs/changelog_3x.md) - [Commits](https://github.com/square/okhttp/compare/parent-3.14.6...parent-3.14.7) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 4f9a699504d..e7bed23509d 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' - implementation 'com.squareup.okhttp3:okhttp:3.14.6' + implementation 'com.squareup.okhttp3:okhttp:3.14.7' implementation 'org.json:json:20180813' testImplementation 'org.postgresql:postgresql:42.2.12' testImplementation 'ch.qos.logback:logback-classic:1.2.3' From fad1667d53c1832cac79fe9f94a688a05d98a933 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 4 Apr 2020 20:05:15 +0100 Subject: [PATCH 1214/5615] Bump rest-assured from 4.2.0 to 4.3.0 in /modules/vault (#2440) Bumps [rest-assured](https://github.com/jayway/rest-assured) from 4.2.0 to 4.3.0. - [Release notes](https://github.com/jayway/rest-assured/releases) - [Changelog](https://github.com/rest-assured/rest-assured/blob/master/changelog.txt) - [Commits](https://github.com/jayway/rest-assured/compare/rest-assured-4.2.0...rest-assured-4.3.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index 1c8a4083b9c..bf85047ddd7 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -4,7 +4,7 @@ dependencies { compile project(':testcontainers') testCompile 'com.bettercloud:vault-java-driver:5.1.0' - testCompile 'io.rest-assured:rest-assured:4.2.0' + testCompile 'io.rest-assured:rest-assured:4.3.0' testCompile 'org.assertj:assertj-core:3.15.0' } From 08e13bf2273ef6e2da004644ae7de6051e39ccda Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Sat, 4 Apr 2020 21:37:14 +0200 Subject: [PATCH 1215/5615] Bump Copyrights to 2020 (#2488) --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 317bc0c749d..ec5ab97ca12 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,10 @@ See [LICENSE](LICENSE). ## Copyright -Copyright (c) 2015 - 2019 Richard North and other authors. +Copyright (c) 2015 - 2020 Richard North and other authors. -MS SQL Server module is (c) 2017 - 2019 G DATA Software AG and other authors. +MS SQL Server module is (c) 2017 - 2020 G DATA Software AG and other authors. -Hashicorp Vault module is (c) 2017 - 2019 Capital One Services, LLC and other authors. +Hashicorp Vault module is (c) 2017 - 2020 Capital One Services, LLC and other authors. See [contributors](https://github.com/testcontainers/testcontainers-java/graphs/contributors) for all contributors. From 7eced8b44de8bda25c6312e4a08461d9514d9686 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 4 Apr 2020 20:45:58 +0100 Subject: [PATCH 1216/5615] Cache results of auth lookups (#2213) --- .../utility/RegistryAuthLocator.java | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java index e8651167221..32b49bd8172 100644 --- a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java +++ b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java @@ -16,6 +16,8 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -38,6 +40,8 @@ public class RegistryAuthLocator { private final String commandExtension; private final File configFile; + private final Map> cache = new ConcurrentHashMap<>(); + /** * key - credential helper's name * value - helper's response for "credentials not found" use case @@ -97,9 +101,22 @@ static void setInstance(RegistryAuthLocator overrideInstance) { * @return an AuthConfig that is applicable to this specific image OR the defaultAuthConfig. */ public AuthConfig lookupAuthConfig(DockerImageName dockerImageName, AuthConfig defaultAuthConfig) { + final String registryName = effectiveRegistryName(dockerImageName); + log.debug("Looking up auth config for image: {} at registry: {}", dockerImageName, registryName); + + final Optional cachedAuth = cache.computeIfAbsent(registryName, __ -> lookupUncachedAuthConfig(registryName, dockerImageName)); - log.debug("Looking up auth config for image: {}", dockerImageName); + if (cachedAuth.isPresent()) { + log.debug("Cached auth found: [{}]", toSafeString(cachedAuth.get())); + return cachedAuth.get(); + } else { + log.debug("No matching Auth Configs - falling back to defaultAuthConfig [{}]", toSafeString(defaultAuthConfig)); + // otherwise, defaultAuthConfig should already contain any credentials available + return defaultAuthConfig; + } + } + private Optional lookupUncachedAuthConfig(String registryName, DockerImageName dockerImageName) { log.debug("RegistryAuthLocator has configFile: {} ({}) and commandPathPrefix: {}", configFile, configFile.exists() ? "exists" : "does not exist", @@ -107,37 +124,33 @@ public AuthConfig lookupAuthConfig(DockerImageName dockerImageName, AuthConfig d try { final JsonNode config = OBJECT_MAPPER.readTree(configFile); - final String registryName = effectiveRegistryName(dockerImageName); log.debug("registryName [{}] for dockerImageName [{}]", registryName, dockerImageName); // use helper preferentially (per https://docs.docker.com/engine/reference/commandline/cli/) final AuthConfig helperAuthConfig = authConfigUsingHelper(config, registryName); if (helperAuthConfig != null) { log.debug("found helper auth config [{}]", toSafeString(helperAuthConfig)); - return helperAuthConfig; + return Optional.of(helperAuthConfig); } // no credsHelper to use, using credsStore: final AuthConfig storeAuthConfig = authConfigUsingStore(config, registryName); if (storeAuthConfig != null) { log.debug("found creds store auth config [{}]", toSafeString(storeAuthConfig)); - return storeAuthConfig; + return Optional.of(storeAuthConfig); } // fall back to base64 encoded auth hardcoded in config file final AuthConfig existingAuthConfig = findExistingAuthConfig(config, registryName); if (existingAuthConfig != null) { log.debug("found existing auth config [{}]", toSafeString(existingAuthConfig)); - return existingAuthConfig; + return Optional.of(existingAuthConfig); } - - log.debug("no matching Auth Configs - falling back to defaultAuthConfig [{}]", toSafeString(defaultAuthConfig)); - // otherwise, defaultAuthConfig should already contain any credentials available } catch (Exception e) { log.warn("Failure when attempting to lookup auth config (dockerImageName: {}, configFile: {}. Falling back to docker-java default behaviour. Exception message: {}", dockerImageName, configFile, e.getMessage()); } - return defaultAuthConfig; + return Optional.empty(); } private AuthConfig findExistingAuthConfig(final JsonNode config, final String reposName) throws Exception { From 4c3bbd6c721ac45d36c73e77a232044de26badc3 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 5 Apr 2020 07:11:41 +0100 Subject: [PATCH 1217/5615] Pre-pull images required for Dockerfile/Compose image builds (#2201) * Pre-pull images required for Dockerfile/Compose image builds So that ImageFromDockerfile and Docker Compose can use images from authenticated registries when building temporary images Fixes #1799 * Restore wider test scope --- .../containers/DockerComposeContainer.java | 5 +- .../containers/ParsedDockerComposeFile.java | 71 ++++++++++++++++--- .../images/ParsedDockerfile.java | 67 +++++++++++++++++ .../images/builder/ImageFromDockerfile.java | 22 +++++- ...ParsedDockerComposeFileValidationTest.java | 18 ++++- .../images/ParsedDockerfileTest.java | 54 ++++++++++++++ core/src/test/resources/Dockerfile | 1 + ...gename-parsing-dockerfile-with-context.yml | 12 ++++ ...r-compose-imagename-parsing-dockerfile.yml | 10 +++ 9 files changed, 243 insertions(+), 17 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/images/ParsedDockerfile.java create mode 100644 core/src/test/java/org/testcontainers/images/ParsedDockerfileTest.java create mode 100644 core/src/test/resources/Dockerfile create mode 100644 core/src/test/resources/docker-compose-imagename-parsing-dockerfile-with-context.yml create mode 100644 core/src/test/resources/docker-compose-imagename-parsing-dockerfile.yml diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 839db7c89db..61036f690cd 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -178,12 +178,13 @@ private void pullImages() { // (a) as a workaround for https://github.com/docker/compose/issues/5854, which prevents authenticated image pulls being possible when credential helpers are in use // (b) so that credential helper-based auth still works when compose is running from within a container parsedComposeFiles.stream() - .flatMap(it -> it.getServiceImageNames().stream()) + .flatMap(it -> it.getDependencyImageNames().stream()) .forEach(imageName -> { try { + log.info("Preemptively checking local images for '{}', referenced via a compose file or transitive Dockerfile. If not available, it will be pulled.", imageName); DockerClientFactory.instance().checkAndPullImage(dockerClient, imageName); } catch (Exception e) { - log.warn("Failed to pull image '{}'. Exception message was {}", imageName, e.getMessage()); + log.warn("Unable to pre-fetch an image ({}) depended upon by Docker Compose build - startup will continue but may fail. Exception message was: {}", imageName, e.getMessage()); } }); } diff --git a/core/src/main/java/org/testcontainers/containers/ParsedDockerComposeFile.java b/core/src/main/java/org/testcontainers/containers/ParsedDockerComposeFile.java index 838607d8337..8f88724647f 100644 --- a/core/src/main/java/org/testcontainers/containers/ParsedDockerComposeFile.java +++ b/core/src/main/java/org/testcontainers/containers/ParsedDockerComposeFile.java @@ -5,10 +5,13 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; +import org.testcontainers.images.ParsedDockerfile; import org.yaml.snakeyaml.Yaml; import java.io.File; import java.io.FileInputStream; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -23,9 +26,10 @@ class ParsedDockerComposeFile { private final Map composeFileContent; private final String composeFileName; + private final File composeFile; @Getter - private Set serviceImageNames = new HashSet<>(); + private Set dependencyImageNames = new HashSet<>(); ParsedDockerComposeFile(File composeFile) { Yaml yaml = new Yaml(); @@ -35,7 +39,7 @@ class ParsedDockerComposeFile { throw new IllegalArgumentException("Unable to parse YAML file from " + composeFile.getAbsolutePath(), e); } this.composeFileName = composeFile.getAbsolutePath(); - + this.composeFile = composeFile; parseAndValidate(); } @@ -43,6 +47,7 @@ class ParsedDockerComposeFile { ParsedDockerComposeFile(Map testContent) { this.composeFileContent = testContent; this.composeFileName = ""; + this.composeFile = new File("."); parseAndValidate(); } @@ -80,15 +85,61 @@ private void parseAndValidate() { } final Map serviceDefinitionMap = (Map) serviceDefinition; - if (serviceDefinitionMap.containsKey("container_name")) { - throw new IllegalStateException(String.format( - "Compose file %s has 'container_name' property set for service '%s' but this property is not supported by Testcontainers, consider removing it", - composeFileName, - serviceName - )); + + validateNoContainerNameSpecified(serviceName, serviceDefinitionMap); + findServiceImageName(serviceDefinitionMap); + findImageNamesInDockerfile(serviceDefinitionMap); + } + } + + private void validateNoContainerNameSpecified(String serviceName, Map serviceDefinitionMap) { + if (serviceDefinitionMap.containsKey("container_name")) { + throw new IllegalStateException(String.format( + "Compose file %s has 'container_name' property set for service '%s' but this property is not supported by Testcontainers, consider removing it", + composeFileName, + serviceName + )); + } + } + + private void findServiceImageName(Map serviceDefinitionMap) { + if (serviceDefinitionMap.containsKey("image") && serviceDefinitionMap.get("image") instanceof String) { + final String imageName = (String) serviceDefinitionMap.get("image"); + log.debug("Resolved dependency image for Docker Compose in {}: {}", composeFileName, imageName); + dependencyImageNames.add(imageName); + } + } + + private void findImageNamesInDockerfile(Map serviceDefinitionMap) { + final Object buildNode = serviceDefinitionMap.get("build"); + Path dockerfilePath = null; + + if (buildNode instanceof Map) { + final Map buildElement = (Map) buildNode; + final Object dockerfileRelativePath = buildElement.get("dockerfile"); + final Object contextRelativePath = buildElement.get("context"); + if (dockerfileRelativePath instanceof String && contextRelativePath instanceof String) { + dockerfilePath = composeFile + .getParentFile() + .toPath() + .resolve((String) contextRelativePath) + .resolve((String) dockerfileRelativePath) + .normalize(); } - if (serviceDefinitionMap.containsKey("image") && serviceDefinitionMap.get("image") instanceof String) { - serviceImageNames.add((String) serviceDefinitionMap.get("image")); + } else if (buildNode instanceof String) { + dockerfilePath = composeFile + .getParentFile() + .toPath() + .resolve((String) buildNode) + .resolve("./Dockerfile") + .normalize(); + } + + if (dockerfilePath != null && Files.exists(dockerfilePath)) { + Set resolvedImageNames = new ParsedDockerfile(dockerfilePath).getDependencyImageNames(); + if (!resolvedImageNames.isEmpty()) { + log.debug("Resolved Dockerfile dependency images for Docker Compose in {} -> {}: {}", composeFileName, dockerfilePath, resolvedImageNames); + this.dependencyImageNames.addAll(resolvedImageNames); } } } diff --git a/core/src/main/java/org/testcontainers/images/ParsedDockerfile.java b/core/src/main/java/org/testcontainers/images/ParsedDockerfile.java new file mode 100644 index 00000000000..1ad43813757 --- /dev/null +++ b/core/src/main/java/org/testcontainers/images/ParsedDockerfile.java @@ -0,0 +1,67 @@ +package org.testcontainers.images; + +import com.google.common.annotations.VisibleForTesting; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * Representation of a Dockerfile, with partial parsing for extraction of a minimal set of data. + */ +@Slf4j +public class ParsedDockerfile { + + private static final Pattern FROM_LINE_PATTERN = Pattern.compile("FROM ([^\\s]+).*"); + + private final Path dockerFilePath; + + @Getter + private Set dependencyImageNames = Collections.emptySet(); + + public ParsedDockerfile(Path dockerFilePath) { + this.dockerFilePath = dockerFilePath; + parse(read()); + } + + @VisibleForTesting + ParsedDockerfile(List lines) { + this.dockerFilePath = Paths.get("dummy.Dockerfile"); + parse(lines); + } + + private List read() { + if (!Files.exists(dockerFilePath)) { + log.warn("Tried to parse Dockerfile at path {} but none was found", dockerFilePath); + return Collections.emptyList(); + } + + try { + return Files.readAllLines(dockerFilePath); + } catch (IOException e) { + log.warn("Unable to read Dockerfile at path {}", dockerFilePath, e); + return Collections.emptyList(); + } + } + + private void parse(List lines) { + dependencyImageNames = lines.stream() + .map(FROM_LINE_PATTERN::matcher) + .filter(Matcher::matches) + .map(matcher -> matcher.group(1)) + .collect(Collectors.toSet()); + + if (!dependencyImageNames.isEmpty()) { + log.debug("Found dependency images in Dockerfile {}: {}", dockerFilePath, dependencyImageNames); + } + } +} diff --git a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java index ded64439a64..1140247960a 100644 --- a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java +++ b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java @@ -2,10 +2,8 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.BuildImageCmd; -import com.github.dockerjava.api.exception.DockerClientException; import com.github.dockerjava.api.model.BuildResponseItem; import com.github.dockerjava.core.command.BuildImageResultCallback; -import com.google.common.collect.Sets; import lombok.Cleanup; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -14,6 +12,7 @@ import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.testcontainers.DockerClientFactory; +import org.testcontainers.images.ParsedDockerfile; import org.testcontainers.images.builder.traits.BuildContextBuilderTrait; import org.testcontainers.images.builder.traits.ClasspathTrait; import org.testcontainers.images.builder.traits.DockerfileTrait; @@ -28,6 +27,7 @@ import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.nio.file.Path; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Optional; @@ -51,6 +51,7 @@ public class ImageFromDockerfile extends LazyFuture implements private final Map buildArgs = new HashMap<>(); private Optional dockerFilePath = Optional.empty(); private Optional dockerfile = Optional.empty(); + private Set dependencyImageNames = Collections.emptySet(); public ImageFromDockerfile() { this("testcontainers/" + Base58.randomString(16).toLowerCase()); @@ -81,6 +82,17 @@ protected final String resolve() { Logger logger = DockerLoggerFactory.getLogger(dockerImageName); DockerClient dockerClient = DockerClientFactory.instance().client(); + + dependencyImageNames.forEach(imageName -> { + try { + log.info("Pre-emptively checking local images for '{}', referenced via a Dockerfile. If not available, it will be pulled.", imageName); + DockerClientFactory.instance().checkAndPullImage(dockerClient, imageName); + } catch (Exception e) { + log.warn("Unable to pre-fetch an image ({}) depended upon by Dockerfile - image build will continue but may fail. Exception message was: {}", imageName, e.getMessage()); + } + }); + + try { if (deleteOnExit) { ResourceReaper.instance().registerImageForCleanup(dockerImageName); @@ -139,7 +151,11 @@ public void onNext(BuildResponseItem item) { protected void configure(BuildImageCmd buildImageCmd) { buildImageCmd.withTag(this.getDockerImageName()); this.dockerFilePath.ifPresent(buildImageCmd::withDockerfilePath); - this.dockerfile.ifPresent(p -> buildImageCmd.withDockerfile(p.toFile())); + this.dockerfile.ifPresent(p -> { + buildImageCmd.withDockerfile(p.toFile()); + dependencyImageNames = new ParsedDockerfile(p).getDependencyImageNames(); + }); + this.buildArgs.forEach(buildImageCmd::withBuildArg); } diff --git a/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java b/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java index c553d250ec9..9a8421086f1 100644 --- a/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java +++ b/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java @@ -76,13 +76,27 @@ public void shouldIgnoreUnknownStructure() { public void shouldObtainImageNamesV1() { File file = new File("src/test/resources/docker-compose-imagename-parsing-v1.yml"); ParsedDockerComposeFile parsedFile = new ParsedDockerComposeFile(file); - assertEquals("all defined service names are found", Sets.newHashSet("redis", "mysql"), parsedFile.getServiceImageNames()); + assertEquals("all defined images are found", Sets.newHashSet("redis", "mysql", "postgres"), parsedFile.getDependencyImageNames()); // redis, mysql from compose file, postgres from Dockerfile build } @Test public void shouldObtainImageNamesV2() { File file = new File("src/test/resources/docker-compose-imagename-parsing-v2.yml"); ParsedDockerComposeFile parsedFile = new ParsedDockerComposeFile(file); - assertEquals("all defined service names are found", Sets.newHashSet("redis", "mysql"), parsedFile.getServiceImageNames()); + assertEquals("all defined images are found", Sets.newHashSet("redis", "mysql", "postgres"), parsedFile.getDependencyImageNames()); // redis, mysql from compose file, postgres from Dockerfile build + } + + @Test + public void shouldObtainImageFromDockerfileBuild() { + File file = new File("src/test/resources/docker-compose-imagename-parsing-dockerfile.yml"); + ParsedDockerComposeFile parsedFile = new ParsedDockerComposeFile(file); + assertEquals("all defined images are found", Sets.newHashSet("redis", "mysql", "alpine:3.2"), parsedFile.getDependencyImageNames()); // redis, mysql from compose file, alpine:3.2 from Dockerfile build + } + + @Test + public void shouldObtainImageFromDockerfileBuildWithContext() { + File file = new File("src/test/resources/docker-compose-imagename-parsing-dockerfile-with-context.yml"); + ParsedDockerComposeFile parsedFile = new ParsedDockerComposeFile(file); + assertEquals("all defined images are found", Sets.newHashSet("redis", "mysql", "alpine:3.2"), parsedFile.getDependencyImageNames()); // redis, mysql from compose file, alpine:3.2 from Dockerfile build } } diff --git a/core/src/test/java/org/testcontainers/images/ParsedDockerfileTest.java b/core/src/test/java/org/testcontainers/images/ParsedDockerfileTest.java new file mode 100644 index 00000000000..e2b97f9fab7 --- /dev/null +++ b/core/src/test/java/org/testcontainers/images/ParsedDockerfileTest.java @@ -0,0 +1,54 @@ +package org.testcontainers.images; + +import com.google.common.collect.Sets; +import org.junit.Test; + +import java.nio.file.Paths; + +import static java.util.Arrays.asList; +import static org.junit.Assert.assertEquals; + +public class ParsedDockerfileTest { + + @Test + public void doesSimpleParsing() { + final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("FROM someimage", "RUN something")); + assertEquals("extracts a single image name", Sets.newHashSet("someimage"), parsedDockerfile.getDependencyImageNames()); + } + + @Test + public void handlesTags() { + final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("FROM someimage:tag", "RUN something")); + assertEquals("retains tags in image names", Sets.newHashSet("someimage:tag"), parsedDockerfile.getDependencyImageNames()); + } + + @Test + public void handlesDigests() { + final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("FROM someimage@sha256:abc123", "RUN something")); + assertEquals("retains digests in image names", Sets.newHashSet("someimage@sha256:abc123"), parsedDockerfile.getDependencyImageNames()); + } + + @Test + public void ignoringCommentedFromLines() { + final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("FROM someimage", "#FROM somethingelse")); + assertEquals("ignores commented from lines", Sets.newHashSet("someimage"), parsedDockerfile.getDependencyImageNames()); + } + + @Test + public void ignoringBuildStageNames() { + final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("FROM someimage --as=base", "RUN something", "FROM nextimage", "RUN something")); + assertEquals("ignores build stage names and allows multiple images to be extracted", Sets.newHashSet("someimage", "nextimage"), parsedDockerfile.getDependencyImageNames()); + } + + @Test + public void handlesGracefullyIfNoFromLine() { + final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("RUN something", "# is this even a valid Dockerfile?")); + assertEquals("handles invalid Dockerfiles gracefully", Sets.newHashSet(), parsedDockerfile.getDependencyImageNames()); + } + + @Test + public void handlesGracefullyIfDockerfileNotFound() { + final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(Paths.get("nonexistent.Dockerfile")); + assertEquals("handles missing Dockerfiles gracefully", Sets.newHashSet(), parsedDockerfile.getDependencyImageNames()); + } +} diff --git a/core/src/test/resources/Dockerfile b/core/src/test/resources/Dockerfile new file mode 100644 index 00000000000..d3f2fcb60f7 --- /dev/null +++ b/core/src/test/resources/Dockerfile @@ -0,0 +1 @@ +FROM postgres diff --git a/core/src/test/resources/docker-compose-imagename-parsing-dockerfile-with-context.yml b/core/src/test/resources/docker-compose-imagename-parsing-dockerfile-with-context.yml new file mode 100644 index 00000000000..4e86b83720b --- /dev/null +++ b/core/src/test/resources/docker-compose-imagename-parsing-dockerfile-with-context.yml @@ -0,0 +1,12 @@ +version: "2.1" +services: + redis: + image: redis + mysql: + image: mysql + custom: + build: + context: compose-dockerfile + dockerfile: Dockerfile +networks: + custom_network: {} diff --git a/core/src/test/resources/docker-compose-imagename-parsing-dockerfile.yml b/core/src/test/resources/docker-compose-imagename-parsing-dockerfile.yml new file mode 100644 index 00000000000..46d3da26f4c --- /dev/null +++ b/core/src/test/resources/docker-compose-imagename-parsing-dockerfile.yml @@ -0,0 +1,10 @@ +version: "2.1" +services: + redis: + image: redis + mysql: + image: mysql + custom: + build: compose-dockerfile +networks: + custom_network: {} From 5b4cda1b5d5bc0316237d802c4c4b745121fe9ea Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 5 Apr 2020 07:12:31 +0100 Subject: [PATCH 1218/5615] Bump mockito-core from 3.3.0 to 3.3.3 in /modules/junit-jupiter (#2437) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.3.0 to 3.3.3. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.3.0...v3.3.3) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 445bdb3586d..d0a573adcc8 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,7 +9,7 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.4.2' testCompile 'redis.clients:jedis:3.2.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.11' - testCompile ('org.mockito:mockito-core:3.3.0') { + testCompile ('org.mockito:mockito-core:3.3.3') { exclude(module: 'hamcrest-core') } From 92fb4e2be425f43685569754f2a572c822e050d9 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 5 Apr 2020 07:12:40 +0100 Subject: [PATCH 1219/5615] =?UTF-8?q?Bump=20aws-java-sdk-logs=20from=201.1?= =?UTF-8?q?1.737=20to=201.11.759=20in=20/modules/lo=E2=80=A6=20(#2518)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.11.737 to 1.11.759. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.737...1.11.759) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 49e586b19f7..1e9e3cd9121 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,5 +6,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.683' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.683' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.636' - testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.737' + testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.759' } From 08bafcfdae0caab213de28b8b3adedb7b4e35924 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 5 Apr 2020 18:58:30 +0100 Subject: [PATCH 1220/5615] Fix `update-docs-version.yml` script (#2519) Using checkout action to check out master branch --- .github/workflows/update-docs-version.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index 7268f72f961..cce6e5b9bf1 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -9,8 +9,8 @@ jobs: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - - name: Checkout master - run: git checkout master + with: + ref: master - name: Update latest_version property in mkdocs.yml run: | sed -i "s/latest_version: .*/latest_version: ${GITHUB_REF##*/}/g" mkdocs.yml From a09f4d50487bf9b9e9807af0dbf59b802f7490ee Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 5 Apr 2020 18:59:39 +0100 Subject: [PATCH 1221/5615] Expand Container `dependsOn` parameters to accept `Iterable` (#2259) * Expand `dependsOn` parameters to accept `Iterable` * Reinstate a `dependsOn(List)` method, for binary backwards compatibility * Reinstate `deepStart(Collection)` method --- .../containers/GenericContainer.java | 27 +++++++++---------- .../testcontainers/lifecycle/Startables.java | 15 +++++++++-- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 03d03759506..945d1e1aff2 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -26,9 +26,6 @@ import lombok.NonNull; import lombok.Setter; import lombok.SneakyThrows; -import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; -import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; -import org.apache.commons.compress.utils.IOUtils; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; @@ -42,7 +39,6 @@ import org.slf4j.Logger; import org.testcontainers.DockerClientFactory; import org.testcontainers.UnstableAPI; -import org.testcontainers.images.ImagePullPolicy; import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.startupcheck.IsRunningStartupCheckStrategy; import org.testcontainers.containers.startupcheck.MinimumDurationRunningStartupCheckStrategy; @@ -51,8 +47,8 @@ import org.testcontainers.containers.wait.Wait; import org.testcontainers.containers.wait.WaitStrategy; import org.testcontainers.containers.wait.strategy.WaitStrategyTarget; +import org.testcontainers.images.ImagePullPolicy; import org.testcontainers.images.RemoteDockerImage; -import org.testcontainers.images.builder.Transferable; import org.testcontainers.lifecycle.Startable; import org.testcontainers.lifecycle.Startables; import org.testcontainers.lifecycle.TestDescription; @@ -64,14 +60,9 @@ import org.testcontainers.utility.PathUtils; import org.testcontainers.utility.ResourceReaper; import org.testcontainers.utility.TestcontainersConfiguration; -import org.testcontainers.utility.ThrowingFunction; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.UndeclaredThrowableException; @@ -82,7 +73,6 @@ import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -266,22 +256,29 @@ public void setImage(Future image) { } /** - * @see #dependsOn(List) + * @see #dependsOn(Iterable) */ public SELF dependsOn(Startable... startables) { Collections.addAll(dependencies, startables); return self(); } + /** + * @see #dependsOn(Iterable) + */ + public SELF dependsOn(List startables) { + return this.dependsOn((Iterable) startables); + } + /** * Delays this container's creation and start until provided {@link Startable}s start first. * Note that the circular dependencies are not supported. * * @param startables a list of {@link Startable} to depend on - * @see Startables#deepStart(Collection) + * @see Startables#deepStart(Iterable) */ - public SELF dependsOn(List startables) { - dependencies.addAll(startables); + public SELF dependsOn(Iterable startables) { + startables.forEach(dependencies::add); return self(); } diff --git a/core/src/main/java/org/testcontainers/lifecycle/Startables.java b/core/src/main/java/org/testcontainers/lifecycle/Startables.java index b20c5c91b54..292e1ab17b3 100644 --- a/core/src/main/java/org/testcontainers/lifecycle/Startables.java +++ b/core/src/main/java/org/testcontainers/lifecycle/Startables.java @@ -5,9 +5,13 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Stream; +import java.util.stream.StreamSupport; @UtilityClass public class Startables { @@ -28,7 +32,14 @@ public Thread newThread(Runnable r) { * @see #deepStart(Stream) */ public CompletableFuture deepStart(Collection startables) { - return deepStart(startables.stream()); + return deepStart((Iterable) startables); + } + + /** + * @see #deepStart(Stream) + */ + public CompletableFuture deepStart(Iterable startables) { + return deepStart(StreamSupport.stream(startables.spliterator(), false)); } /** From f5948a9085a75e875472c7b6be8a55f4861271a2 Mon Sep 17 00:00:00 2001 From: Michael Kutz Date: Sat, 11 Apr 2020 13:33:16 +0200 Subject: [PATCH 1222/5615] Remove unidiomatic public modifier in Groovy example code (#2238) The `public` access modifier is quite unidiomatic in Groovy code. Co-authored-by: Kevin Wittek --- .../src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy b/docs/examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy index d8c056b5070..c2537a948d5 100644 --- a/docs/examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy +++ b/docs/examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy @@ -10,7 +10,7 @@ class RedisBackedCacheIntTest extends Specification { private RedisBackedCache underTest // init { - public GenericContainer redis = new GenericContainer<>("redis:5.0.3-alpine") + GenericContainer redis = new GenericContainer<>("redis:5.0.3-alpine") .withExposedPorts(6379) // } From 17b9ffc092629b86b301580fa3a8fba18271daa2 Mon Sep 17 00:00:00 2001 From: Mitchell Just Date: Sat, 11 Apr 2020 21:40:14 +1000 Subject: [PATCH 1223/5615] Move static config from `configure()` to constructors (#2473) --- .../containers/ClickHouseContainer.java | 3 --- .../testcontainers/containers/Db2Container.java | 4 ++-- .../containers/InfluxDBContainer.java | 4 ++-- .../containers/MariaDBContainer.java | 4 ++-- .../containers/MSSQLServerContainer.java | 4 +--- .../testcontainers/containers/MySQLContainer.java | 2 +- .../testcontainers/containers/Neo4jContainer.java | 4 ++-- .../testcontainers/containers/NginxContainer.java | 15 ++++++++------- .../containers/OracleContainer.java | 6 +----- .../containers/OrientDBContainer.java | 3 ++- .../containers/PostgreSQLContainer.java | 3 ++- .../containers/PrestoContainer.java | 7 ++----- 12 files changed, 25 insertions(+), 34 deletions(-) diff --git a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java index 0fd6174bf3b..9364ecc4d14 100644 --- a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java +++ b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java @@ -26,10 +26,7 @@ public ClickHouseContainer() { public ClickHouseContainer(String dockerImageName) { super(dockerImageName); - } - @Override - protected void configure() { withExposedPorts(HTTP_PORT, NATIVE_PORT); waitingFor( new HttpWaitStrategy() diff --git a/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java b/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java index 429cc060094..13ef72c328e 100644 --- a/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java +++ b/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java @@ -37,6 +37,8 @@ public Db2Container(String imageName) { this.waitStrategy = new LogMessageWaitStrategy() .withRegEx(".*Setup has completed\\..*") .withStartupTimeout(Duration.of(10, ChronoUnit.MINUTES)); + + addExposedPort(DB2_PORT); } @Override @@ -52,8 +54,6 @@ protected void configure() { acceptLicense(); } - addExposedPort(DB2_PORT); - addEnv("DBNAME", databaseName); addEnv("DB2INSTANCE", username); addEnv("DB2INST1_PASSWORD", password); diff --git a/modules/influxdb/src/main/java/org/testcontainers/containers/InfluxDBContainer.java b/modules/influxdb/src/main/java/org/testcontainers/containers/InfluxDBContainer.java index ee5b449f961..c3ac909f2d4 100644 --- a/modules/influxdb/src/main/java/org/testcontainers/containers/InfluxDBContainer.java +++ b/modules/influxdb/src/main/java/org/testcontainers/containers/InfluxDBContainer.java @@ -36,12 +36,12 @@ public InfluxDBContainer(final String version) { waitStrategy = new WaitAllStrategy() .withStrategy(Wait.forHttp("/ping").withBasicCredentials(username, password).forStatusCode(204)) .withStrategy(Wait.forListeningPort()); + + addExposedPort(INFLUXDB_PORT); } @Override protected void configure() { - addExposedPort(INFLUXDB_PORT); - addEnv("INFLUXDB_ADMIN_USER", admin); addEnv("INFLUXDB_ADMIN_PASSWORD", adminPassword); diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java index c41136c0bf2..be1b3d3d44d 100644 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java @@ -24,6 +24,7 @@ public MariaDBContainer() { public MariaDBContainer(String dockerImageName) { super(dockerImageName); + addExposedPort(MARIADB_PORT); } @Override @@ -35,7 +36,6 @@ protected Integer getLivenessCheckPort() { protected void configure() { optionallyMapResourceParameterAsVolume(MY_CNF_CONFIG_OVERRIDE_PARAM_NAME, "/etc/mysql/conf.d", "mariadb-default-conf"); - addExposedPort(MARIADB_PORT); addEnv("MYSQL_DATABASE", databaseName); addEnv("MYSQL_USER", username); if (password != null && !password.isEmpty()) { @@ -83,7 +83,7 @@ public SELF withConfigurationOverride(String s) { parameters.put(MY_CNF_CONFIG_OVERRIDE_PARAM_NAME, s); return self(); } - + @Override public SELF withDatabaseName(final String databaseName) { this.databaseName = databaseName; diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java index ad1a7d19090..afe3f5b8651 100644 --- a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java @@ -36,6 +36,7 @@ public MSSQLServerContainer(final String dockerImageName) { super(dockerImageName); withStartupTimeoutSeconds(DEFAULT_STARTUP_TIMEOUT_SECONDS); withConnectTimeoutSeconds(DEFAULT_CONNECT_TIMEOUT_SECONDS); + addExposedPort(MS_SQL_SERVER_PORT); } @Override @@ -45,11 +46,8 @@ protected Integer getLivenessCheckPort() { @Override protected void configure() { - addExposedPort(MS_SQL_SERVER_PORT); - LicenseAcceptance.assertLicenseAccepted(this.getDockerImageName()); addEnv("ACCEPT_EULA", "Y"); - addEnv("SA_PASSWORD", password); } diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java index fa2e8626b0d..a8549672ff3 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java @@ -27,6 +27,7 @@ public MySQLContainer() { public MySQLContainer(String dockerImageName) { super(dockerImageName); + addExposedPort(MYSQL_PORT); } @NotNull @@ -40,7 +41,6 @@ protected void configure() { optionallyMapResourceParameterAsVolume(MY_CNF_CONFIG_OVERRIDE_PARAM_NAME, "/etc/mysql/conf.d", "mysql-default-conf"); - addExposedPort(MYSQL_PORT); addEnv("MYSQL_DATABASE", databaseName); addEnv("MYSQL_USER", username); if (password != null && !password.isEmpty()) { diff --git a/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java index fc545122219..0c16927dba8 100644 --- a/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java +++ b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java @@ -87,6 +87,8 @@ public Neo4jContainer(String dockerImageName) { .withStrategy(waitForBolt) .withStrategy(waitForHttp) .withStartupTimeout(Duration.ofMinutes(2)); + + addExposedPorts(DEFAULT_BOLT_PORT, DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT); } @Override @@ -100,8 +102,6 @@ public Set getLivenessCheckPortNumbers() { @Override protected void configure() { - addExposedPorts(DEFAULT_BOLT_PORT, DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT); - boolean emptyAdminPassword = this.adminPassword == null || this.adminPassword.isEmpty(); String neo4jAuth = emptyAdminPassword ? "none" : String.format(AUTH_FORMAT, this.adminPassword); addEnv("NEO4J_AUTH", neo4jAuth); diff --git a/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java b/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java index 1badffa1cee..bedf040869f 100644 --- a/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java +++ b/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java @@ -16,7 +16,14 @@ public class NginxContainer> extends GenericCo private static final int NGINX_DEFAULT_PORT = 80; public NginxContainer() { - super("nginx:1.9.4"); + this("nginx:1.9.4"); + } + + public NginxContainer(String dockerImageName) { + super(dockerImageName); + + addExposedPort(NGINX_DEFAULT_PORT); + setCommand("nginx", "-g", "daemon off;"); } @NotNull @@ -25,12 +32,6 @@ protected Set getLivenessCheckPorts() { return Collections.singleton(getMappedPort(80)); } - @Override - protected void configure() { - addExposedPort(NGINX_DEFAULT_PORT); - setCommand("nginx", "-g", "daemon off;"); - } - public URL getBaseUrl(String scheme, int port) throws MalformedURLException { return new URL(scheme + "://" + getContainerIpAddress() + ":" + getMappedPort(port)); } diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java index ea0eee1a2ff..bad22af2511 100644 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java @@ -46,6 +46,7 @@ public OracleContainer(Future dockerImageName) { super(dockerImageName); withStartupTimeoutSeconds(DEFAULT_STARTUP_TIMEOUT_SECONDS); withConnectTimeoutSeconds(DEFAULT_CONNECT_TIMEOUT_SECONDS); + addExposedPorts(ORACLE_PORT, APEX_HTTP_PORT); } @Override @@ -53,11 +54,6 @@ protected Integer getLivenessCheckPort() { return getMappedPort(ORACLE_PORT); } - @Override - protected void configure() { - addExposedPorts(ORACLE_PORT, APEX_HTTP_PORT); - } - @Override public String getDriverClassName() { return "oracle.jdbc.OracleDriver"; diff --git a/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java b/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java index b368d96bf8c..ca0180640ce 100644 --- a/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java +++ b/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java @@ -66,11 +66,12 @@ public OrientDBContainer(@NonNull String dockerImageName) { .withStrategy(Wait.forListeningPort()) .withStrategy(waitForHttp) .withStartupTimeout(Duration.ofMinutes(2)); + + addExposedPorts(DEFAULT_BINARY_PORT, DEFAULT_HTTP_PORT); } @Override protected void configure() { - addExposedPorts(DEFAULT_BINARY_PORT, DEFAULT_HTTP_PORT); addEnv("ORIENTDB_ROOT_PASSWORD", serverPassword); } diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index cef66d0c492..0fae3a16f4a 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -38,6 +38,8 @@ public PostgreSQLContainer(final String dockerImageName) { .withTimes(2) .withStartupTimeout(Duration.of(60, SECONDS)); this.setCommand("postgres", "-c", FSYNC_OFF_OPTION); + + addExposedPort(POSTGRESQL_PORT); } @NotNull @@ -48,7 +50,6 @@ protected Set getLivenessCheckPorts() { @Override protected void configure() { - addExposedPort(POSTGRESQL_PORT); addEnv("POSTGRES_DB", databaseName); addEnv("POSTGRES_USER", username); addEnv("POSTGRES_PASSWORD", password); diff --git a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java index 186acb18af6..0f2a82b9b9d 100644 --- a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java +++ b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java @@ -32,6 +32,8 @@ public PrestoContainer(final String dockerImageName) { this.waitStrategy = new LogMessageWaitStrategy() .withRegEx(".*io.prestosql.server.PrestoServer\\s+======== SERVER STARTED ========.*") .withStartupTimeout(Duration.of(60, SECONDS)); + + addExposedPort(PRESTO_PORT); } @NotNull @@ -40,11 +42,6 @@ protected Set getLivenessCheckPorts() { return new HashSet<>(getMappedPort(PRESTO_PORT)); } - @Override - protected void configure() { - addExposedPort(PRESTO_PORT); - } - @Override public String getDriverClassName() { return "io.prestosql.jdbc.PrestoDriver"; From ecdbc11c2d637d3dec50b32655958a9a8ef1e807 Mon Sep 17 00:00:00 2001 From: Sandra Parsick Date: Sat, 11 Apr 2020 13:41:55 +0200 Subject: [PATCH 1224/5615] add more information for using registry mirror (#2477) --- docs/features/configuration.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/features/configuration.md b/docs/features/configuration.md index cd01b99b1c7..ad039528b5d 100644 --- a/docs/features/configuration.md +++ b/docs/features/configuration.md @@ -48,6 +48,11 @@ Some companies disallow the usage of Docker Hub, but you can override `*.image` Another possibility is to set up a registry mirror in your environment so that all images are pulled from there and not directly from Docker Hub. For more information, see the [official Docker documentation about "Registry as a pull through cache"](https://docs.docker.com/registry/recipes/mirror/). +!!!tip + Registry mirror currently only works for Docker images with image name that has no registry specified (for example, for Docker image `mariadb:10.3.6`, it works, for Docker image `quay.io/testcontainers/ryuk:0.2.3`, not). + Workaround is to to configure the affected Docker image in `.testcontainers.properties`. + For example: `ryuk.container.image = testcontainers/ryuk:0.2.3` or `ryuk.container.image = /testcontainers/ryuk:0.2.3` + ## Customizing Ryuk resource reaper > **ryuk.container.image = quay.io/testcontainers/ryuk:0.2.3** From 21ba44813164aff012993be643ee3ed12ffac107 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sat, 11 Apr 2020 13:49:35 +0200 Subject: [PATCH 1225/5615] Add ciMate (#2550) --- .circleci/config.yml | 11 +++++++++++ .github/workflows/ci.yml | 8 ++++++++ azure-pipelines.yml | 6 ++++++ 3 files changed, 25 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5ee4097934a..c4302911f40 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,6 +9,13 @@ jobs: command: ./gradlew testcontainers:build -x test -x check - run: command: ./gradlew testcontainers:check --scan + - run: &ciMate + name: aggregate test reports with ciMate + when: always + command: | + wget -q https://get.cimate.io/release/linux/cimate + chmod +x cimate + ./cimate "**/TEST-*.xml" - run: name: Save test results command: | @@ -29,6 +36,7 @@ jobs: --scan environment: TZ: /usr/share/zoneinfo/ETC/UTC + - run: *ciMate - run: name: Save test results command: | @@ -48,6 +56,7 @@ jobs: command: ./gradlew jdbc-test:check --scan environment: TZ: /usr/share/zoneinfo/ETC/UTC + - run: *ciMate - run: name: Save test results command: | @@ -65,6 +74,7 @@ jobs: - checkout - run: command: ./gradlew selenium:check --scan + - run: *ciMate - run: name: Save test results command: | @@ -82,6 +92,7 @@ jobs: - checkout - run: command: cd examples && ./gradlew check + - run: *ciMate - run: name: Save test results command: | diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5618be1230d..1a0a97d05f7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,3 +18,11 @@ jobs: -w "$PWD" \ openjdk:8-jdk-alpine \ ./gradlew --no-daemon --continue --scan testcontainers:test --tests '*GenericContainerRuleTest' + - name: aggregate test reports with ciMate + if: always() + env: + CIMATE_PROJECT_ID: 2348n4vl + run: | + wget -q https://get.cimate.io/release/linux/cimate + chmod +x cimate + ./cimate "**/TEST-*.xml" diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 68330550ee1..8ab7bc98db5 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -13,6 +13,8 @@ jobs: tasks: 'testcontainers:check' publishJUnitResults: true testResultsFiles: '**/TEST-*.xml' + - script: wget -q https://get.cimate.io/release/linux/cimate && chmod +x cimate && ./cimate "**/TEST-*.xml" + condition: succeededOrFailed() - job: jdbc steps: - task: Gradle@2 @@ -27,6 +29,8 @@ jobs: tasks: 'jdbc-test:check' publishJUnitResults: true testResultsFiles: '**/TEST-*.xml' + - script: wget -q https://get.cimate.io/release/linux/cimate && chmod +x cimate && ./cimate "**/TEST-*.xml" + condition: succeededOrFailed() - job: modules steps: - task: Gradle@2 @@ -41,3 +45,5 @@ jobs: tasks: 'check' publishJUnitResults: true testResultsFiles: '**/TEST-*.xml' + - script: wget -q https://get.cimate.io/release/linux/cimate && chmod +x cimate && ./cimate "**/TEST-*.xml" + condition: succeededOrFailed() From 7f3c4d2e0b24105a39e9b9c00117a9cf1de4838b Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sat, 11 Apr 2020 20:06:11 +0200 Subject: [PATCH 1226/5615] set `outputPerTestCase` --- build.gradle | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/build.gradle b/build.gradle index c8b2267a049..a55753aa7f5 100644 --- a/build.gradle +++ b/build.gradle @@ -68,6 +68,12 @@ subprojects { } } + tasks.withType(Test).all { + reports { + junitXml.outputPerTestCase = true + } + } + // Ensure that Javadoc generation is always tested check.dependsOn(javadoc) From 09cc191c137f91c22b55796ab1067898286bde52 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sat, 11 Apr 2020 20:46:15 +0200 Subject: [PATCH 1227/5615] disable ciMate on non-Linux AZP builds --- azure-pipelines.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 8ab7bc98db5..5572a4096a3 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -14,7 +14,7 @@ jobs: publishJUnitResults: true testResultsFiles: '**/TEST-*.xml' - script: wget -q https://get.cimate.io/release/linux/cimate && chmod +x cimate && ./cimate "**/TEST-*.xml" - condition: succeededOrFailed() + condition: and(succeededOrFailed(),eq(variables.os, 'Linux')) - job: jdbc steps: - task: Gradle@2 @@ -30,7 +30,7 @@ jobs: publishJUnitResults: true testResultsFiles: '**/TEST-*.xml' - script: wget -q https://get.cimate.io/release/linux/cimate && chmod +x cimate && ./cimate "**/TEST-*.xml" - condition: succeededOrFailed() + condition: and(succeededOrFailed(), eq(variables.os, 'Linux')) - job: modules steps: - task: Gradle@2 @@ -46,4 +46,4 @@ jobs: publishJUnitResults: true testResultsFiles: '**/TEST-*.xml' - script: wget -q https://get.cimate.io/release/linux/cimate && chmod +x cimate && ./cimate "**/TEST-*.xml" - condition: succeededOrFailed() + condition: and(succeededOrFailed(), eq(variables.os, 'Linux')) From dd662ce494c9600c210a7e85006f90729d4163fe Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 12 Apr 2020 13:26:08 +0200 Subject: [PATCH 1228/5615] fix the OS variable in AZP --- azure-pipelines.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 5572a4096a3..c56ae9fde36 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -14,7 +14,7 @@ jobs: publishJUnitResults: true testResultsFiles: '**/TEST-*.xml' - script: wget -q https://get.cimate.io/release/linux/cimate && chmod +x cimate && ./cimate "**/TEST-*.xml" - condition: and(succeededOrFailed(),eq(variables.os, 'Linux')) + condition: and(succeededOrFailed(),eq(variables['Agent.OS'], 'Linux')) - job: jdbc steps: - task: Gradle@2 @@ -30,7 +30,7 @@ jobs: publishJUnitResults: true testResultsFiles: '**/TEST-*.xml' - script: wget -q https://get.cimate.io/release/linux/cimate && chmod +x cimate && ./cimate "**/TEST-*.xml" - condition: and(succeededOrFailed(), eq(variables.os, 'Linux')) + condition: and(succeededOrFailed(), eq(variables['Agent.OS'], 'Linux')) - job: modules steps: - task: Gradle@2 @@ -46,4 +46,4 @@ jobs: publishJUnitResults: true testResultsFiles: '**/TEST-*.xml' - script: wget -q https://get.cimate.io/release/linux/cimate && chmod +x cimate && ./cimate "**/TEST-*.xml" - condition: and(succeededOrFailed(), eq(variables.os, 'Linux')) + condition: and(succeededOrFailed(), eq(variables['Agent.OS'], 'Linux')) From c90a9f996bafbb7f6f26cd875fc936dd84f08a55 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Sun, 12 Apr 2020 15:18:20 +0200 Subject: [PATCH 1229/5615] Temporarily ignore DB2 test - unrelated CI disk space issues (#2557) * Temporary ignore DB2 test in JDBCDriverTest.data We have some problems with file space running out on CI, so the jdbc-test module, pulling a lot of database images, can potentially break the build. * Also ignore SimpleDb2Test --- .../src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java | 2 +- .../src/test/java/org/testcontainers/junit/SimpleDb2Test.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java index ca55a1d1841..c96ce251a63 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java @@ -67,7 +67,7 @@ public static Iterable data() { {"jdbc:tc:clickhouse://hostname/databasename", EnumSet.of(Options.PmdKnownBroken)}, {"jdbc:tc:sqlserver:2017-CU12://hostname:hostport;databaseName=databasename", EnumSet.noneOf(Options.class)}, {"jdbc:tc:cockroach://hostname/databasename", EnumSet.noneOf(Options.class)}, - {"jdbc:tc:db2://hostname/databasename", EnumSet.noneOf(Options.class)} +// {"jdbc:tc:db2://hostname/databasename", EnumSet.noneOf(Options.class)} }); } diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleDb2Test.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleDb2Test.java index c686f979b79..60ad9a23a27 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleDb2Test.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleDb2Test.java @@ -2,6 +2,7 @@ import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.testcontainers.containers.Db2Container; @@ -13,6 +14,7 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +@Ignore("Disabled since we had problems with CI disk space filling up from images in jdbc-test module") public class SimpleDb2Test { @Rule From ee05f4ab6780a7490112d26de5f375d70f4949e6 Mon Sep 17 00:00:00 2001 From: Alex Stockinger Date: Sun, 12 Apr 2020 15:24:23 +0200 Subject: [PATCH 1230/5615] Remove unused JEE deps (#2505) Two compile dependencies referencing JEE artifacts don't seem to be required for compilation. This PR removes them from the build. --- core/build.gradle | 3 --- 1 file changed, 3 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index 6b9a1994ebd..f2d7efcc1a9 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -115,10 +115,7 @@ dependencies { compile 'junit:junit:4.12' compile 'org.slf4j:slf4j-api:1.7.30' compile 'org.jetbrains:annotations:17.0.0' - compile 'javax.annotation:javax.annotation-api:1.3.2' compile 'org.apache.commons:commons-compress:1.20' - // Added for JDK9 compatibility since it's missing this package - compile 'javax.xml.bind:jaxb-api:2.3.1' compile ('org.rnorth.duct-tape:duct-tape:1.0.8') { exclude(group: 'org.jetbrains', module: 'annotations') } From 0b7e72feeade14168cc52e8c8ac3100f9b1f4066 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 12 Apr 2020 16:01:20 +0200 Subject: [PATCH 1231/5615] Update to docker-java 3.2.1 (#2435) --- build.gradle | 5 ++++- core/build.gradle | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index a55753aa7f5..4fbaf42c160 100644 --- a/build.gradle +++ b/build.gradle @@ -107,7 +107,10 @@ subprojects { for (entry in jf.entries()) { def name = entry.name if (name.endsWith(".class")) { - packages.add(name.substring(0, name.lastIndexOf('/'))) + def index = name.lastIndexOf('/') + if (index != -1) { + packages.add(name.substring(0, index)) + } } } jf.close() diff --git a/core/build.gradle b/core/build.gradle index f2d7efcc1a9..01575f0d351 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -135,7 +135,7 @@ dependencies { exclude(group: "net.java.dev.jna") } - shaded ('com.github.docker-java:docker-java-transport-okhttp:3.2.0') { + shaded ('com.github.docker-java:docker-java-transport-okhttp:3.2.1') { exclude(group: 'net.java.dev.jna') exclude(group: 'com.google.code.findbug') exclude(group: 'org.slf4j') From 3833ce483aad18ccb1809453da638b22f04c5ad8 Mon Sep 17 00:00:00 2001 From: Andrew Guibert Date: Sun, 12 Apr 2020 09:01:57 -0500 Subject: [PATCH 1232/5615] Add more clear warning message instructing users how to enable reusable containers (#2422) * Add more clear warning message instructing users how to enable reusable containers * Update GenericContainer.java Co-authored-by: Sergei Egorov --- .../org/testcontainers/containers/GenericContainer.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 945d1e1aff2..211ad2f59cb 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -69,6 +69,7 @@ import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.time.Duration; import java.time.Instant; import java.util.ArrayList; @@ -376,7 +377,12 @@ private void tryStart(Instant startedAt) { } reusable = true; } else { - logger().info("Reuse was requested but the environment does not support the reuse of containers"); + logger().warn( + "" + + "Reuse was requested but the environment does not support the reuse of containers\n" + + "To enable reuse of containers, you must set 'testcontainers.reuse.enable=true' in a file located at {}", + Paths.get(System.getProperty("user.home"), ".testcontainers.properties") + ); reusable = false; } } else { From cbd3220c555e7ece09163f245cd4114b7860c150 Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Sun, 12 Apr 2020 16:02:39 +0200 Subject: [PATCH 1233/5615] Protect `NetworkImpl#close` from concurrency issues (#2203) * Increase thread safety of NetworkImpl * un-volatile `id` Co-authored-by: Sergei Egorov --- core/src/main/java/org/testcontainers/containers/Network.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/Network.java b/core/src/main/java/org/testcontainers/containers/Network.java index 411459b62eb..1c00fa00fac 100644 --- a/core/src/main/java/org/testcontainers/containers/Network.java +++ b/core/src/main/java/org/testcontainers/containers/Network.java @@ -97,7 +97,7 @@ protected void after() { } @Override - public void close() { + public synchronized void close() { if (initialized.getAndSet(false)) { ResourceReaper.instance().removeNetworkById(id); } From 84c8d1e290b6f2c5be00477cc67d85dc5ac3b26c Mon Sep 17 00:00:00 2001 From: Alexander Sparkowsky Date: Sun, 12 Apr 2020 16:07:22 +0200 Subject: [PATCH 1234/5615] Support TestLifecycleAware-ness of containers started by the JUnit Jupiter integration (#1326) * Update BrowserWebDriverContainer to honor existing no_proxy setting * Update test to only start one container per test * Use constant for no_proxy key * Cleanup test implementation (#929) * Add signalling of TestLifecycleAware containers. Allow containers like WebBrowserContainers to initialize and/or finalize before/after tests. * #1326 Add early draft to test signalling of TestLifecycleAware containers * Add test for post condition when signalling lifecycleaware containers This kind of test is a bit tricky since the post condition occurs after the original test has been finished. Also it's not nice to pass data between two tests. * Update test for lifecycle aware containers to cover shared case (#1326) In order to check that the afterAll callback has signalled lifecycleaware containers correctly a second extension is used. The order of the extension annotation ensures that the assertion is run after the extension under test. * Update test for lifecycle aware containers to cover shared case (#1326) To test the beforeAll() case the assertion has to be called from within the test class since it's called after all extensions. * Fix formatting (#1326) * Use lighter container for testing (#1326) * Separate store and collect of shared lifecycle-aware-containers (#1326) * Add tests for ordering and capturing test exceptions (#1326) * Make lifecycle tests independent of timing (#1326) Calls to the lifecycle methods are now recorded in an ordered list that is then used to test the correct number and order of calls. This makes the test independent of timing. Unfortunately it's still required to execute tests in a deterministic order. For a better separation of test concerns tests for the lifecycle methods and exception capturing have been moved into separate test classes. * Make mock now implements Startable (#1326) There is no need to start a container since only the TestLifecycleAware is important. * Add AssertJ dependency (#1326) We want to use AssertJ for some tests. * Migrate assertions of TestLifecycleAwareMethodTest to AssertJ (#1326) * Update generation of filesystem friendly description (#1326) * Separated tests for filesystem friendly filename (#1326) * Use lombok to improve readability (#1326) * Generate filesystem friendly name from display name (#1326) Generating a filesystem friendly name in a Junit Jupiter test is a bit tricky. Since tests can be generated dynamically class and/or test method may not be available. The display name provided by the ExtensionContext on the other hand may use characters that are not filesystem safe. This approach removes all characters from the display name that are not in a restricted set of allowed characters. However this may lead to name clashes if two tests have a display name that only differs in characters that are removed from the display name. * Generate filesystem friendly name from URLEncoded unique id (#1326) Co-authored-by: Richard North Co-authored-by: Kevin Wittek --- modules/junit-jupiter/build.gradle | 2 + .../FilesystemFriendlyNameGenerator.java | 24 +++++ .../jupiter/TestcontainerTestDescription.java | 10 ++ .../jupiter/TestcontainersExtension.java | 93 ++++++++++++++++--- .../FilesystemFriendlyNameGeneratorTest.java | 45 +++++++++ .../TestLifecycleAwareContainerMock.java | 54 +++++++++++ ...tLifecycleAwareExceptionCapturingTest.java | 37 ++++++++ .../jupiter/TestLifecycleAwareMethodTest.java | 73 +++++++++++++++ 8 files changed, 326 insertions(+), 12 deletions(-) create mode 100644 modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGenerator.java create mode 100644 modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainerTestDescription.java create mode 100644 modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGeneratorTest.java create mode 100644 modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java create mode 100644 modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareExceptionCapturingTest.java create mode 100644 modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index d0a573adcc8..bf52b25913c 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -12,6 +12,8 @@ dependencies { testCompile ('org.mockito:mockito-core:3.3.3') { exclude(module: 'hamcrest-core') } + testCompile 'org.junit.jupiter:junit-jupiter-params:5.6.0' + testCompile 'org.assertj:assertj-core:3.14.0' testRuntime 'org.postgresql:postgresql:42.2.12' testRuntime 'mysql:mysql-connector-java:8.0.19' diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGenerator.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGenerator.java new file mode 100644 index 00000000000..ef8560b3f19 --- /dev/null +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGenerator.java @@ -0,0 +1,24 @@ +package org.testcontainers.junit.jupiter; + +import org.junit.jupiter.api.extension.ExtensionContext; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.platform.commons.util.StringUtils.isBlank; + +class FilesystemFriendlyNameGenerator { + private static final String UNKNOWN_NAME = "unknown"; + + static String filesystemFriendlyNameOf(ExtensionContext context) { + String contextId = context.getUniqueId(); + try { + return (isBlank(contextId)) + ? UNKNOWN_NAME + : URLEncoder.encode(contextId, UTF_8.toString()); + } catch (UnsupportedEncodingException e) { + return UNKNOWN_NAME; + } + } +} diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainerTestDescription.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainerTestDescription.java new file mode 100644 index 00000000000..568d228e08c --- /dev/null +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainerTestDescription.java @@ -0,0 +1,10 @@ +package org.testcontainers.junit.jupiter; + +import lombok.Value; +import org.testcontainers.lifecycle.TestDescription; + +@Value +class TestcontainersTestDescription implements TestDescription { + String testId; + String filesystemFriendlyName; +} diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java index e6ce10f8541..1ed5e954e44 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java @@ -1,32 +1,50 @@ package org.testcontainers.junit.jupiter; import lombok.Getter; -import org.junit.jupiter.api.extension.*; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ConditionEvaluationResult; +import org.junit.jupiter.api.extension.ExecutionCondition; +import org.junit.jupiter.api.extension.ExtensionConfigurationException; +import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ExtensionContext.Namespace; +import org.junit.jupiter.api.extension.ExtensionContext.Store; import org.junit.jupiter.api.extension.ExtensionContext.Store.CloseableResource; +import org.junit.jupiter.api.extension.TestInstancePostProcessor; import org.junit.platform.commons.support.AnnotationSupport; import org.junit.platform.commons.util.AnnotationUtils; import org.junit.platform.commons.util.Preconditions; import org.junit.platform.commons.util.ReflectionUtils; import org.testcontainers.DockerClientFactory; import org.testcontainers.lifecycle.Startable; +import org.testcontainers.lifecycle.TestDescription; +import org.testcontainers.lifecycle.TestLifecycleAware; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.LinkedHashSet; +import java.util.List; import java.util.Optional; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Stream; -class TestcontainersExtension implements BeforeEachCallback, BeforeAllCallback, ExecutionCondition, TestInstancePostProcessor { +import static java.util.stream.Collectors.toList; +import static org.testcontainers.junit.jupiter.FilesystemFriendlyNameGenerator.filesystemFriendlyNameOf; + +class TestcontainersExtension implements BeforeEachCallback, BeforeAllCallback, AfterEachCallback, AfterAllCallback, ExecutionCondition, TestInstancePostProcessor { private static final Namespace NAMESPACE = Namespace.create(TestcontainersExtension.class); private static final String TEST_INSTANCE = "testInstance"; + private static final String SHARED_LIFECYCLE_AWARE_CONTAINERS = "sharedLifecycleAwareContainers"; + private static final String LOCAL_LIFECYCLE_AWARE_CONTAINERS = "localLifecycleAwareContainers"; @Override public void postProcessTestInstance(final Object testInstance, final ExtensionContext context) { - ExtensionContext.Store store = context.getStore(NAMESPACE); + Store store = context.getStore(NAMESPACE); store.put(TEST_INSTANCE, testInstance); } @@ -35,19 +53,69 @@ public void beforeAll(ExtensionContext context) { Class testClass = context.getTestClass() .orElseThrow(() -> new ExtensionConfigurationException("TestcontainersExtension is only supported for classes.")); - ExtensionContext.Store store = context.getStore(NAMESPACE); + Store store = context.getStore(NAMESPACE); + List sharedContainersStoreAdapters = findSharedContainers(testClass); + + sharedContainersStoreAdapters.forEach(adapter -> store.getOrComputeIfAbsent(adapter.getKey(), k -> adapter.start())); + + List lifecycleAwareContainers = sharedContainersStoreAdapters + .stream() + .filter(this::isTestLifecycleAware) + .map(lifecycleAwareAdapter -> (TestLifecycleAware) lifecycleAwareAdapter.container) + .collect(toList()); - findSharedContainers(testClass) - .forEach(adapter -> store.getOrComputeIfAbsent(adapter.getKey(), k -> adapter.start())); + store.put(SHARED_LIFECYCLE_AWARE_CONTAINERS, lifecycleAwareContainers); + signalBeforeTestToContainers(lifecycleAwareContainers, testDescriptionFrom(context)); + } + + @Override + public void afterAll(ExtensionContext context) { + signalAfterTestToContainersFor(SHARED_LIFECYCLE_AWARE_CONTAINERS, context); } @Override public void beforeEach(final ExtensionContext context) { - collectParentTestInstances(context) - .parallelStream() + Store store = context.getStore(NAMESPACE); + + List lifecycleAwareContainers = collectParentTestInstances(context).parallelStream() .flatMap(this::findRestartContainers) - .forEach(adapter -> context.getStore(NAMESPACE) - .getOrComputeIfAbsent(adapter.getKey(), k -> adapter.start())); + .peek(adapter -> store.getOrComputeIfAbsent(adapter.getKey(), k -> adapter.start())) + .filter(this::isTestLifecycleAware) + .map(lifecycleAwareAdapter -> (TestLifecycleAware) lifecycleAwareAdapter.container) + .collect(toList()); + + store.put(LOCAL_LIFECYCLE_AWARE_CONTAINERS, lifecycleAwareContainers); + signalBeforeTestToContainers(lifecycleAwareContainers, testDescriptionFrom(context)); + } + + @Override + public void afterEach(ExtensionContext context) { + signalAfterTestToContainersFor(LOCAL_LIFECYCLE_AWARE_CONTAINERS, context); + } + + private void signalBeforeTestToContainers(List lifecycleAwareContainers, TestDescription testDescription) { + lifecycleAwareContainers.forEach(container -> container.beforeTest(testDescription)); + } + + private void signalAfterTestToContainersFor(String storeKey, ExtensionContext context) { + List lifecycleAwareContainers = + (List) context.getStore(NAMESPACE).get(storeKey); + if (lifecycleAwareContainers != null) { + TestDescription description = testDescriptionFrom(context); + Optional throwable = context.getExecutionException(); + lifecycleAwareContainers.forEach(container -> container.afterTest(description, throwable)); + } + } + + private TestDescription testDescriptionFrom(ExtensionContext context) { + return new TestcontainersTestDescription( + context.getUniqueId(), + filesystemFriendlyNameOf(context) + ); + } + + private boolean isTestLifecycleAware(StoreAdapter adapter) { + return adapter.container instanceof TestLifecycleAware; } @Override @@ -101,13 +169,14 @@ private Set collectParentTestInstances(final ExtensionContext context) { return testInstances; } - private Stream findSharedContainers(Class testClass) { + private List findSharedContainers(Class testClass) { return ReflectionUtils.findFields( testClass, isSharedContainer(), ReflectionUtils.HierarchyTraversalMode.TOP_DOWN) .stream() - .map(f -> getContainerInstance(null, f)); + .map(f -> getContainerInstance(null, f)) + .collect(toList()); } private Predicate isSharedContainer() { diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGeneratorTest.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGeneratorTest.java new file mode 100644 index 00000000000..96617b0e063 --- /dev/null +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGeneratorTest.java @@ -0,0 +1,45 @@ +package org.testcontainers.junit.jupiter; + +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.testcontainers.junit.jupiter.FilesystemFriendlyNameGenerator.filesystemFriendlyNameOf; + +class FilesystemFriendlyNameGeneratorTest { + + @ParameterizedTest + @MethodSource("provideDisplayNamesAndFilesystemFriendlyNames") + void should_generate_filesystem_friendly_name(String displayName, String expectedName) { + ExtensionContext context = mock(ExtensionContext.class); + doReturn(displayName) + .when(context).getUniqueId(); + + String filesystemFriendlyName = filesystemFriendlyNameOf(context); + + assertThat(filesystemFriendlyName).isEqualTo(expectedName); + } + + private static Stream provideDisplayNamesAndFilesystemFriendlyNames() { + return Stream.of( + Arguments.of("", "unknown"), + Arguments.of(" ", "unknown"), + Arguments.of("not blank", "not+blank"), + Arguments.of("abc ABC 1234567890", "abc+ABC+1234567890"), + Arguments.of( + "no_umlauts_äöüÄÖÜéáíó", + "no_umlauts_%C3%A4%C3%B6%C3%BC%C3%84%C3%96%C3%9C%C3%A9%C3%A1%C3%AD%C3%B3" + ), + Arguments.of( + "[engine:junit-jupiter]/[class:com.example.MyTest]/[test-factory:parameterizedTest()]/[dynamic-test:#3]", + "%5Bengine%3Ajunit-jupiter%5D%2F%5Bclass%3Acom.example.MyTest%5D%2F%5Btest-factory%3AparameterizedTest%28%29%5D%2F%5Bdynamic-test%3A%233%5D" + ) + ); + } +} diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java new file mode 100644 index 00000000000..38751c8789a --- /dev/null +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java @@ -0,0 +1,54 @@ +package org.testcontainers.junit.jupiter; + +import org.testcontainers.lifecycle.Startable; +import org.testcontainers.lifecycle.TestDescription; +import org.testcontainers.lifecycle.TestLifecycleAware; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class TestLifecycleAwareContainerMock implements Startable, TestLifecycleAware { + + static final String BEFORE_TEST = "beforeTest"; + static final String AFTER_TEST = "afterTest"; + + private final List lifecycleMethodCalls = new ArrayList<>(); + private final List lifecycleFilesystemFriendlyNames = new ArrayList<>(); + + private Throwable capturedThrowable; + + @Override + public void beforeTest(TestDescription description) { + lifecycleMethodCalls.add(BEFORE_TEST); + lifecycleFilesystemFriendlyNames.add(description.getFilesystemFriendlyName()); + } + + @Override + public void afterTest(TestDescription description, Optional throwable) { + lifecycleMethodCalls.add(AFTER_TEST); + throwable.ifPresent(capturedThrowable -> this.capturedThrowable = capturedThrowable); + } + + List getLifecycleMethodCalls() { + return lifecycleMethodCalls; + } + + Throwable getCapturedThrowable() { + return capturedThrowable; + } + + public List getLifecycleFilesystemFriendlyNames() { + return lifecycleFilesystemFriendlyNames; + } + + @Override + public void start() { + + } + + @Override + public void stop() { + + } +} diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareExceptionCapturingTest.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareExceptionCapturingTest.java new file mode 100644 index 00000000000..85d837a7880 --- /dev/null +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareExceptionCapturingTest.java @@ -0,0 +1,37 @@ +package org.testcontainers.junit.jupiter; + +import org.junit.AssumptionViolatedException; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; + +// The order of @ExtendsWith and @Testcontainers is crucial in order for the tests +@Testcontainers +@TestMethodOrder(OrderAnnotation.class) +class TestLifecycleAwareExceptionCapturingTest { + @Container + private final TestLifecycleAwareContainerMock testContainer = new TestLifecycleAwareContainerMock(); + + private static TestLifecycleAwareContainerMock startedTestContainer; + + @Test + @Order(1) + void failing_test_should_pass_throwable_to_testContainer() { + startedTestContainer = testContainer; + // Force an exception that is captured by the test container without failing the test itself + assumeTrue(false); + } + + @Test + @Order(2) + void should_have_captured_thrownException() { + Throwable capturedThrowable = startedTestContainer.getCapturedThrowable(); + assertTrue(capturedThrowable instanceof AssumptionViolatedException); + assertEquals("got: , expected: is ", capturedThrowable.getMessage()); + } +} diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java new file mode 100644 index 00000000000..cecdff490ef --- /dev/null +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java @@ -0,0 +1,73 @@ +package org.testcontainers.junit.jupiter; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.testcontainers.junit.jupiter.TestLifecycleAwareContainerMock.AFTER_TEST; +import static org.testcontainers.junit.jupiter.TestLifecycleAwareContainerMock.BEFORE_TEST; + +// The order of @ExtendsWith and @Testcontainers is crucial for the tests +@ExtendWith({TestLifecycleAwareMethodTest.SharedContainerAfterAllTestExtension.class}) +@Testcontainers +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +class TestLifecycleAwareMethodTest { + @Container + private final TestLifecycleAwareContainerMock testContainer = new TestLifecycleAwareContainerMock(); + + @Container + private static final TestLifecycleAwareContainerMock SHARED_CONTAINER = new TestLifecycleAwareContainerMock(); + + private static TestLifecycleAwareContainerMock startedTestContainer; + + @BeforeAll + static void beforeAll() { + assertThat(SHARED_CONTAINER.getLifecycleMethodCalls()).containsExactly(BEFORE_TEST); + + } + + @Test + @Order(1) + void should_prepare_before_and_after_test() { + // we can only test for a call to afterTest() after this test has been finished. + startedTestContainer = testContainer; + } + + @Test + @Order(2) + void should_call_beforeTest_first_afterTest_later_with_filesystem_friendly_name() { + assertThat(startedTestContainer.getLifecycleMethodCalls()) + .containsExactly(BEFORE_TEST, AFTER_TEST); + } + + @Test + void should_have_a_filesystem_friendly_name_container_has_started() { + assertThat(startedTestContainer.getLifecycleFilesystemFriendlyNames()) + .containsExactly( + "%5Bengine%3Ajunit-jupiter%5D%2F%5Bclass%3Aorg.testcontainers.junit.jupiter.TestLifecycleAwareMethodTest%5D%2F%5Bmethod%3Ashould_prepare_before_and_after_test%28%29%5D" + ); + } + + @Test + void static_container_should_have_a_filesystem_friendly_name_after_container_has_started() { + assertThat(SHARED_CONTAINER.getLifecycleFilesystemFriendlyNames()) + .containsExactly( + "%5Bengine%3Ajunit-jupiter%5D%2F%5Bclass%3Aorg.testcontainers.junit.jupiter.TestLifecycleAwareMethodTest%5D" + ); + } + + static class SharedContainerAfterAllTestExtension implements AfterAllCallback { + // Unfortunately it's not possible to write a @Test that is run after all tests + @Override + public void afterAll(ExtensionContext context) { + assertThat(SHARED_CONTAINER.getLifecycleMethodCalls()) + .containsExactly(BEFORE_TEST, AFTER_TEST); + } + } +} From 6c45a20892d837ac736dd293abfe458f96be74fc Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 12 Apr 2020 19:27:49 +0200 Subject: [PATCH 1235/5615] Add R2DBC support (#2545) This PR adds support for [R2DBC](https://r2dbc.io). ## URL support The URL support is similar to the JDBC's, but: 1. Does not reuse containers by URL - it is unclear yet how to "hash" `ConnectionFactoryOptions` 1. The image tag must be explicitly provided via `TC_IMAGE_TAG` r2dbc option 1. TODO: daemon mode 1. TODO: alias support (see #4) ## Example usage The user **must add the `org.testcontainers:r2dbc` module** - unlike JDBC, we use `compileOnly` dependency on R2DBC, since it is a 3rd party dependency. Once both the `r2dbc` and database's module are added, one can use: ```java String url = "r2dbc:tc:postgresql:///db?TC_IMAGE_TAG=10-alpine"; ConnectionFactory connectionFactory = ConnectionFactories.get(url); ``` ## Programmatic access For those who start containers manually and want to obtain `ConnectionFactoryOptions` for already started containers, we provide a helper static method: ```java try (PostgreSQLContainer container = new PostgreSQLContainer<>()) { container.start(); ConnectionFactory connectionFactory = ConnectionFactories.get( PostgreSQLR2DBCDatabaseContainer.getOptions(container) ); } ``` --- docs/modules/databases/index.md | 113 +----------- docs/modules/databases/jdbc.md | 119 +++++++++++++ docs/modules/databases/r2dbc.md | 57 +++++++ mkdocs.yml | 2 + modules/mariadb/build.gradle | 9 + .../containers/MariaDBContainer.java | 2 +- .../MariaDBR2DBCDatabaseContainer.java | 33 ++++ ...MariaDBR2DBCDatabaseContainerProvider.java | 30 ++++ .../MariaDBR2DBCDatabaseContainerTest.java | 23 +++ .../src/test/resources/logback-test.xml | 16 ++ modules/mssqlserver/build.gradle | 13 ++ .../MSSQLR2DBCDatabaseContainer.java | 34 ++++ .../MSSQLR2DBCDatabaseContainerProvider.java | 29 ++++ .../MSSQLR2DBCDatabaseContainerTest.java | 22 +++ .../container-license-acceptance.txt | 1 + .../src/test/resources/logback-test.xml | 16 ++ modules/mysql/build.gradle | 9 + .../MySQLR2DBCDatabaseContainer.java | 33 ++++ .../MySQLR2DBCDatabaseContainerProvider.java | 30 ++++ .../MySQLR2DBCDatabaseContainerTest.java | 23 +++ .../mysql/src/test/resources/logback-test.xml | 16 ++ modules/postgresql/build.gradle | 8 + .../PostgreSQLR2DBCDatabaseContainer.java | 33 ++++ ...tgreSQLR2DBCDatabaseContainerProvider.java | 30 ++++ .../PostgreSQLR2DBCDatabaseContainerTest.java | 27 +++ .../src/test/resources/logback-test.xml | 16 ++ modules/r2dbc/build.gradle | 20 +++ .../r2dbc/CancellableSubscription.java | 23 +++ .../r2dbc/ConnectionPublisher.java | 161 ++++++++++++++++++ .../r2dbc/EmptySubscription.java | 17 ++ .../java/org/testcontainers/r2dbc/Hidden.java | 73 ++++++++ .../r2dbc/R2DBCDatabaseContainer.java | 9 + .../r2dbc/R2DBCDatabaseContainerProvider.java | 15 ++ .../TestcontainersR2DBCConnectionFactory.java | 104 +++++++++++ ...tcontainersR2DBCConnectionFactoryTest.java | 79 +++++++++ .../r2dbc/src/test/resources/logback-test.xml | 16 ++ .../AbstractR2DBCDatabaseContainerTest.java | 64 +++++++ 37 files changed, 1212 insertions(+), 113 deletions(-) create mode 100644 docs/modules/databases/jdbc.md create mode 100644 docs/modules/databases/r2dbc.md create mode 100644 modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainer.java create mode 100644 modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainerProvider.java create mode 100644 modules/mariadb/src/test/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainerTest.java create mode 100644 modules/mariadb/src/test/resources/logback-test.xml create mode 100644 modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainer.java create mode 100644 modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainerProvider.java create mode 100644 modules/mssqlserver/src/test/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainerTest.java create mode 100644 modules/mssqlserver/src/test/resources/container-license-acceptance.txt create mode 100644 modules/mssqlserver/src/test/resources/logback-test.xml create mode 100644 modules/mysql/src/main/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainer.java create mode 100644 modules/mysql/src/main/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerProvider.java create mode 100644 modules/mysql/src/test/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerTest.java create mode 100644 modules/mysql/src/test/resources/logback-test.xml create mode 100644 modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainer.java create mode 100644 modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainerProvider.java create mode 100644 modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainerTest.java create mode 100644 modules/postgresql/src/test/resources/logback-test.xml create mode 100644 modules/r2dbc/build.gradle create mode 100644 modules/r2dbc/src/main/java/org/testcontainers/r2dbc/CancellableSubscription.java create mode 100644 modules/r2dbc/src/main/java/org/testcontainers/r2dbc/ConnectionPublisher.java create mode 100644 modules/r2dbc/src/main/java/org/testcontainers/r2dbc/EmptySubscription.java create mode 100644 modules/r2dbc/src/main/java/org/testcontainers/r2dbc/Hidden.java create mode 100644 modules/r2dbc/src/main/java/org/testcontainers/r2dbc/R2DBCDatabaseContainer.java create mode 100644 modules/r2dbc/src/main/java/org/testcontainers/r2dbc/R2DBCDatabaseContainerProvider.java create mode 100644 modules/r2dbc/src/main/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactory.java create mode 100644 modules/r2dbc/src/test/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactoryTest.java create mode 100644 modules/r2dbc/src/test/resources/logback-test.xml create mode 100644 modules/r2dbc/src/testFixtures/java/org/testcontainers/r2dbc/AbstractR2DBCDatabaseContainerTest.java diff --git a/docs/modules/databases/index.md b/docs/modules/databases/index.md index d7654cc6a33..6685ade55c8 100644 --- a/docs/modules/databases/index.md +++ b/docs/modules/databases/index.md @@ -10,115 +10,4 @@ You might want to use Testcontainers' database support: !!! note Of course, it's still important to have as few tests that hit the database as possible, and make good use of mocks for components higher up the stack. -You can obtain a temporary database in one of two ways: - - * **JUnit @Rule/@ClassRule**: this mode starts a database inside a container before your tests and tears it down afterwards. - * **Using a specially modified JDBC URL**: after making a very simple modification to your system's JDBC URL string, Testcontainers will provide a disposable stand-in database that can be used without requiring modification to your application code. - -## Database container objects - -Add a @Rule or @ClassRule to your test class, e.g.: - -```java -public class SimpleMySQLTest { - @Rule - public MySQLContainer mysql = new MySQLContainer(); -``` - -Now, in your test code (or a suitable setup method), you can obtain details necessary to connect to this database: - - * `mysql.getJdbcUrl()` provides a JDBC URL your code can connect to - * `mysql.getUsername()` provides the username your code should pass to the driver - * `mysql.getPassword()` provides the password your code should pass to the driver - -Note that if you use `@Rule`, you will be given an isolated container for each test method. If you use `@ClassRule`, you will get on isolated container for all the methods in the test class. - -Examples/Tests: - - * [MySQL](https://github.com/testcontainers/testcontainers-java/blob/master/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java) - * [PostgreSQL](https://github.com/testcontainers/testcontainers-java/blob/master/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimplePostgreSQLTest.java) - -## Database containers launched via JDBC URL scheme - -As long as you have Testcontainers and the appropriate JDBC driver on your classpath, you can simply modify regular JDBC connection URLs to get a fresh containerized instance of the database each time your application starts up. - -_N.B:_ - -* _TC needs to be on your application's classpath at runtime for this to work_ -* _For Spring Boot (Before version `2.3.0`) you need to specify the driver manually `spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver`_ - -**Original URL**: `jdbc:mysql:5.7.22://localhost:3306/databasename` - -Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database name will be ignored; you can leave these as-is or set them to any value. - -!!! note - We will use `///` (host-less URIs) from now on to emphasis the unimportance of the `host:port` pair. - From Testcontainers' perspective, `jdbc:mysql:5.7.22://localhost:3306/databasename` and `jdbc:mysql:5.7.22:///databasename` is the same URI. - -### JDBC URL examples - -#### Using Testcontainers with a fixed version - -`jdbc:tc:mysql:5.6.23:///databasename` - -#### Using PostgreSQL - -`jdbc:tc:postgresql:9.6.8:///databasename` - -### Using PostGIS - -`jdbc:tc:postgis:9.6:///databasename` - -### Using Presto - -`jdbc:tc:presto:329://localhost/memory/default` - -## Using a classpath init script - -Testcontainers can run an init script after the database container is started, but before your code is given a connection to it. The script must be on the classpath, and is referenced as follows: - -`jdbc:tc:mysql:5.7.22:///databasename?TC_INITSCRIPT=somepath/init_mysql.sql` - -This is useful if you have a fixed script for setting up database schema, etc. - -## Using an init script from a file - -If the init script path is prefixed `file:`, it will be loaded from a file (relative to the working directory, which will usually be the project root). - -`jdbc:tc:mysql:5.7.22:///databasename?TC_INITSCRIPT=file:src/main/resources/init_mysql.sql` - -#### Using an init function - -Instead of running a fixed script for DB setup, it may be useful to call a Java function that you define. This is intended to allow you to trigger database schema migration tools. To do this, add TC_INITFUNCTION to the URL as follows, passing a full path to the class name and method: - - `jdbc:tc:mysql:5.7.22:///databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction` - -The init function must be a public static method which takes a `java.sql.Connection` as its only parameter, e.g. -```java -public class JDBCDriverTest { - public static void sampleInitFunction(Connection connection) throws SQLException { - // e.g. run schema setup or Flyway/liquibase/etc DB migrations here... - } - ... -``` - -#### Running container in daemon mode - -By default database container is being stopped as soon as last connection is closed. There are cases when you might need to start container and keep it running till you stop it explicitly or JVM is shutdown. To do this, add `TC_DAEMON` parameter to the URL as follows: - - `jdbc:tc:mysql:5.7.22:///databasename?TC_DAEMON=true` - -With this parameter database container will keep running even when there're no open connections. - - -#### Running container with tmpfs options - -Container can have `tmpfs` mounts for storing data in host memory. This is useful if you want to speed up your database tests. Be aware that the data will be lost when the container stops. - -To pass this option to the container, add `TC_TMPFS` parameter to the URL as follows: - - `jdbc:tc:postgresql:9.6.8:///databasename?TC_TMPFS=/testtmpfs:rw` - -If you need more than one option, separate them by comma (e.g. `TC_TMPFS=key:value,key1:value1&other_parameters=foo`). - -For more information about `tmpfs` mount, see [the official Docker documentation](https://docs.docker.com/storage/tmpfs/). +See [JDBC](./jdbc.md) and [R2DBC](./r2dbc.md) for information on how to use Testcontainers with SQL-like databases. \ No newline at end of file diff --git a/docs/modules/databases/jdbc.md b/docs/modules/databases/jdbc.md new file mode 100644 index 00000000000..5f83d7fcdeb --- /dev/null +++ b/docs/modules/databases/jdbc.md @@ -0,0 +1,119 @@ +# JDBC support + +You can obtain a temporary database in one of two ways: + + * **Using a specially modified JDBC URL**: after making a very simple modification to your system's JDBC URL string, Testcontainers will provide a disposable stand-in database that can be used without requiring modification to your application code. + * **JUnit @Rule/@ClassRule**: this mode starts a database inside a container before your tests and tears it down afterwards. + +## Database containers launched via JDBC URL scheme + +As long as you have Testcontainers and the appropriate JDBC driver on your classpath, you can simply modify regular JDBC connection URLs to get a fresh containerized instance of the database each time your application starts up. + +_N.B:_ + +* _TC needs to be on your application's classpath at runtime for this to work_ +* _For Spring Boot (Before version `2.3.0`) you need to specify the driver manually `spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver`_ + +**Original URL**: `jdbc:mysql://localhost:3306/databasename` + +Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database name will be ignored; you can leave these as-is or set them to any value. + +!!! note + We will use `///` (host-less URIs) from now on to emphasis the unimportance of the `host:port` pair. + From Testcontainers' perspective, `jdbc:mysql:5.7.22://localhost:3306/databasename` and `jdbc:mysql:5.7.22:///databasename` is the same URI. + +!!! warning + If you're using the JDBC URL support, there is no need to instantiate an instance of the container - Testcontainers will do it automagically. + +### JDBC URL examples + +#### Using Testcontainers with a fixed version + +`jdbc:tc:mysql:5.6.23:///databasename` + +#### Using PostgreSQL + +`jdbc:tc:postgresql:9.6.8:///databasename` + +#### Using PostGIS + +`jdbc:tc:postgis:9.6:///databasename` + +#### Using Presto + +`jdbc:tc:presto:329://localhost/memory/default` + +### Using a classpath init script + +Testcontainers can run an init script after the database container is started, but before your code is given a connection to it. The script must be on the classpath, and is referenced as follows: + +`jdbc:tc:mysql:5.7.22:///databasename?TC_INITSCRIPT=somepath/init_mysql.sql` + +This is useful if you have a fixed script for setting up database schema, etc. + +### Using an init script from a file + +If the init script path is prefixed `file:`, it will be loaded from a file (relative to the working directory, which will usually be the project root). + +`jdbc:tc:mysql:5.7.22:///databasename?TC_INITSCRIPT=file:src/main/resources/init_mysql.sql` + +### Using an init function + +Instead of running a fixed script for DB setup, it may be useful to call a Java function that you define. This is intended to allow you to trigger database schema migration tools. To do this, add TC_INITFUNCTION to the URL as follows, passing a full path to the class name and method: + + `jdbc:tc:mysql:5.7.22:///databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction` + +The init function must be a public static method which takes a `java.sql.Connection` as its only parameter, e.g. +```java +public class JDBCDriverTest { + public static void sampleInitFunction(Connection connection) throws SQLException { + // e.g. run schema setup or Flyway/liquibase/etc DB migrations here... + } + ... +``` + +### Running container in daemon mode + +By default database container is being stopped as soon as last connection is closed. There are cases when you might need to start container and keep it running till you stop it explicitly or JVM is shutdown. To do this, add `TC_DAEMON` parameter to the URL as follows: + + `jdbc:tc:mysql:5.7.22:///databasename?TC_DAEMON=true` + +With this parameter database container will keep running even when there're no open connections. + + +### Running container with tmpfs options + +Container can have `tmpfs` mounts for storing data in host memory. This is useful if you want to speed up your database tests. Be aware that the data will be lost when the container stops. + +To pass this option to the container, add `TC_TMPFS` parameter to the URL as follows: + + `jdbc:tc:postgresql:9.6.8:///databasename?TC_TMPFS=/testtmpfs:rw` + +If you need more than one option, separate them by comma (e.g. `TC_TMPFS=key:value,key1:value1&other_parameters=foo`). + +For more information about `tmpfs` mount, see [the official Docker documentation](https://docs.docker.com/storage/tmpfs/). + +## Database container objects + +In case you can't use the URL support, or need to fine-tune the container, you can instantiate it yourself. + +Add a @Rule or @ClassRule to your test class, e.g.: + +```java +public class SimpleMySQLTest { + @Rule + public MySQLContainer mysql = new MySQLContainer(); +``` + +Now, in your test code (or a suitable setup method), you can obtain details necessary to connect to this database: + + * `mysql.getJdbcUrl()` provides a JDBC URL your code can connect to + * `mysql.getUsername()` provides the username your code should pass to the driver + * `mysql.getPassword()` provides the password your code should pass to the driver + +Note that if you use `@Rule`, you will be given an isolated container for each test method. If you use `@ClassRule`, you will get on isolated container for all the methods in the test class. + +Examples/Tests: + + * [MySQL](https://github.com/testcontainers/testcontainers-java/blob/master/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java) + * [PostgreSQL](https://github.com/testcontainers/testcontainers-java/blob/master/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimplePostgreSQLTest.java) \ No newline at end of file diff --git a/docs/modules/databases/r2dbc.md b/docs/modules/databases/r2dbc.md new file mode 100644 index 00000000000..6ba3f401a08 --- /dev/null +++ b/docs/modules/databases/r2dbc.md @@ -0,0 +1,57 @@ +# R2DBC support + +You can obtain a temporary database in one of two ways: + + * **Using a specially modified R2DBC URL**: after making a very simple modification to your system's R2DBC URL string, Testcontainers will provide a disposable stand-in database that can be used without requiring modification to your application code. + * **JUnit @Rule/@ClassRule**: this mode starts a database inside a container before your tests and tears it down afterwards. + +## Database containers launched via R2DBC URL scheme + +As long as you have Testcontainers and the appropriate R2DBC driver on your classpath, you can simply modify regular R2DBC connection URLs to get a fresh containerized instance of the database each time your application starts up. + +The started container will be terminated when the `ConnectionFactory` is closed. + +!!! warning + Both the database module (e.g. `org.testcontainers:mysql`) **and** `org.testcontainers:r2dbc` need to be on your application's classpath at runtime. + +**Original URL**: `r2dbc:mysql://localhost:3306/databasename` + +1. Insert `tc:` after `r2dbc:` as follows. Note that the hostname, port and database name will be ignored; you can leave these as-is or set them to any value. +1. Specify the mandatory Docker tag of the database's official image that you want using a `TC_IMAGE_TAG` query parameter. + +**Note that, unlike Testcontainers' JDBC URL support, it is not possible to specify an image tag in the 'scheme' part of the URL, and it is always necessary to specify a tag using `TC_IMAGE_TAG`.** + +So that the URL becomes: +`r2dbc:tc:mysql:///databasename?TC_IMAGE_TAG=5.7.22` + +!!! note + We will use `///` (host-less URIs) from now on to emphasis the unimportance of the `host:port` pair. + From Testcontainers' perspective, `r2dbc:mysql://localhost:3306/databasename` and `r2dbc:mysql:///databasename` is the same URI. + +!!! warning + If you're using the R2DBC URL support, there is no need to instantiate an instance of the container - Testcontainers will do it automagically. + +### R2DBC URL examples + +#### Using MySQL + +`r2dbc:tc:mysql:///databasename?TC_IMAGE_TAG=5.6.23` + +#### Using MariaDB + +`r2dbc:tc:mariadb:///databasename?TC_IMAGE_TAG=10.3.6` + +#### Using PostgreSQL + +`r2dbc:tc:postgresql:///databasename?TC_IMAGE_TAG=9.6.8` + +#### Using MSSQL: + +`r2dbc:tc:sqlserver:///?TC_IMAGE_TAG=2017-CU12` + +## Obtaining `ConnectionFactoryOptions` from database container objects + +If you already have an instance of the database container, you can get an instance of `ConnectionFactoryOptions` from it: + +[Creating `ConnectionFactoryOptions` from an instance)](../../../modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainerTest.java) inside_block:get_options + diff --git a/mkdocs.yml b/mkdocs.yml index 49876885dfd..c2d39bc9d2c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -40,6 +40,8 @@ nav: - Modules: - Databases: - modules/databases/index.md + - modules/databases/jdbc.md + - modules/databases/r2dbc.md - modules/databases/cassandra.md - modules/databases/cockroachdb.md - modules/databases/couchbase.md diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index be42121f3d8..93b2de2a716 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -1,11 +1,20 @@ description = "Testcontainers :: JDBC :: MariaDB" dependencies { + annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6' + compileOnly 'com.google.auto.service:auto-service:1.0-rc6' + compile project(':jdbc') + compileOnly project(':r2dbc') + compileOnly 'org.mariadb:r2dbc-mariadb:0.8.1-alpha1' + testCompile 'org.mariadb.jdbc:mariadb-java-client:2.6.0' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.33' testCompile 'org.vibur:vibur-dbcp:25.0' + + testCompile testFixtures(project(':r2dbc')) + testCompile 'org.mariadb:r2dbc-mariadb:0.8.1-alpha1' } diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java index be1b3d3d44d..e402bc24e0e 100644 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java @@ -11,7 +11,7 @@ public class MariaDBContainer> extends JdbcD public static final String IMAGE = "mariadb"; public static final String DEFAULT_TAG = "10.3.6"; - private static final Integer MARIADB_PORT = 3306; + static final Integer MARIADB_PORT = 3306; private String databaseName = "test"; private String username = "test"; private String password = "test"; diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainer.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainer.java new file mode 100644 index 00000000000..4ddbed997d4 --- /dev/null +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainer.java @@ -0,0 +1,33 @@ +package org.testcontainers.containers; + +import io.r2dbc.spi.ConnectionFactoryOptions; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Delegate; +import org.testcontainers.lifecycle.Startable; +import org.testcontainers.r2dbc.R2DBCDatabaseContainer; + +@RequiredArgsConstructor +public class MariaDBR2DBCDatabaseContainer implements R2DBCDatabaseContainer { + + @Delegate(types = Startable.class) + private final MariaDBContainer container; + + public static ConnectionFactoryOptions getOptions(MariaDBContainer container) { + ConnectionFactoryOptions options = ConnectionFactoryOptions.builder() + .option(ConnectionFactoryOptions.DRIVER, MariaDBR2DBCDatabaseContainerProvider.DRIVER) + .build(); + + return new MariaDBR2DBCDatabaseContainer(container).configure(options); + } + + @Override + public ConnectionFactoryOptions configure(ConnectionFactoryOptions options) { + return options.mutate() + .option(ConnectionFactoryOptions.HOST, container.getContainerIpAddress()) + .option(ConnectionFactoryOptions.PORT, container.getMappedPort(MariaDBContainer.MARIADB_PORT)) + .option(ConnectionFactoryOptions.DATABASE, container.getDatabaseName()) + .option(ConnectionFactoryOptions.USER, container.getUsername()) + .option(ConnectionFactoryOptions.PASSWORD, container.getPassword()) + .build(); + } +} diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainerProvider.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainerProvider.java new file mode 100644 index 00000000000..79cb627f8a9 --- /dev/null +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainerProvider.java @@ -0,0 +1,30 @@ +package org.testcontainers.containers; + +import com.google.auto.service.AutoService; +import io.r2dbc.spi.ConnectionFactoryOptions; +import org.mariadb.r2dbc.MariadbConnectionFactoryProvider; +import org.testcontainers.r2dbc.R2DBCDatabaseContainer; +import org.testcontainers.r2dbc.R2DBCDatabaseContainerProvider; + +@AutoService(R2DBCDatabaseContainerProvider.class) +public class MariaDBR2DBCDatabaseContainerProvider implements R2DBCDatabaseContainerProvider { + + static final String DRIVER = MariadbConnectionFactoryProvider.MARIADB_DRIVER; + + @Override + public boolean supports(ConnectionFactoryOptions options) { + return DRIVER.equals(options.getRequiredValue(ConnectionFactoryOptions.DRIVER)); + } + + @Override + public R2DBCDatabaseContainer createContainer(ConnectionFactoryOptions options) { + String image = MariaDBContainer.IMAGE + ":" + options.getRequiredValue(IMAGE_TAG_OPTION); + MariaDBContainer container = new MariaDBContainer<>(image) + .withDatabaseName(options.getRequiredValue(ConnectionFactoryOptions.DATABASE)); + + if (Boolean.TRUE.equals(options.getValue(REUSABLE_OPTION))) { + container.withReuse(true); + } + return new MariaDBR2DBCDatabaseContainer(container); + } +} diff --git a/modules/mariadb/src/test/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainerTest.java b/modules/mariadb/src/test/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainerTest.java new file mode 100644 index 00000000000..30e8cad9c32 --- /dev/null +++ b/modules/mariadb/src/test/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainerTest.java @@ -0,0 +1,23 @@ +package org.testcontainers.containers; + +import io.r2dbc.spi.ConnectionFactoryOptions; +import org.testcontainers.r2dbc.AbstractR2DBCDatabaseContainerTest; + +public class MariaDBR2DBCDatabaseContainerTest extends AbstractR2DBCDatabaseContainerTest> { + + @Override + protected ConnectionFactoryOptions getOptions(MariaDBContainer container) { + return MariaDBR2DBCDatabaseContainer.getOptions(container); + } + + @Override + protected String createR2DBCUrl() { + return "r2dbc:tc:mariadb:///db?TC_IMAGE_TAG=10.3.6"; + } + + @Override + protected MariaDBContainer createContainer() { + return new MariaDBContainer<>(); + } + +} diff --git a/modules/mariadb/src/test/resources/logback-test.xml b/modules/mariadb/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..535e406fc13 --- /dev/null +++ b/modules/mariadb/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index 62434722d63..3c44df64eda 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -1,5 +1,18 @@ description = "Testcontainers :: MS SQL Server" dependencies { + annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6' + compileOnly 'com.google.auto.service:auto-service:1.0-rc6' + compile project(':jdbc') + + compileOnly project(':r2dbc') + compileOnly 'io.r2dbc:r2dbc-mssql:0.8.1.RELEASE' + + testCompile project(':r2dbc') + testCompile 'io.r2dbc:r2dbc-mssql:0.8.1.RELEASE' + + // MSSQL's wait strategy requires the JDBC driver + testCompile testFixtures(project(':r2dbc')) + testCompile 'com.microsoft.sqlserver:mssql-jdbc:6.1.0.jre8' } diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainer.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainer.java new file mode 100644 index 00000000000..27c486c218c --- /dev/null +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainer.java @@ -0,0 +1,34 @@ +package org.testcontainers.containers; + +import io.r2dbc.spi.ConnectionFactoryOptions; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Delegate; +import org.testcontainers.lifecycle.Startable; +import org.testcontainers.r2dbc.R2DBCDatabaseContainer; + +@RequiredArgsConstructor +public class MSSQLR2DBCDatabaseContainer implements R2DBCDatabaseContainer { + + @Delegate(types = Startable.class) + private final MSSQLServerContainer container; + + public static ConnectionFactoryOptions getOptions(MSSQLServerContainer container) { + ConnectionFactoryOptions options = ConnectionFactoryOptions.builder() + .option(ConnectionFactoryOptions.DRIVER, MSSQLR2DBCDatabaseContainerProvider.DRIVER) + .build(); + + return new MSSQLR2DBCDatabaseContainer(container).configure(options); + } + + @Override + public ConnectionFactoryOptions configure(ConnectionFactoryOptions options) { + return options.mutate() + .option(ConnectionFactoryOptions.HOST, container.getContainerIpAddress()) + .option(ConnectionFactoryOptions.PORT, container.getMappedPort(MSSQLServerContainer.MS_SQL_SERVER_PORT)) + // TODO enable if/when MSSQLServerContainer adds support for customizing the DB name + // .option(ConnectionFactoryOptions.DATABASE, container.getDatabasseName()) + .option(ConnectionFactoryOptions.USER, container.getUsername()) + .option(ConnectionFactoryOptions.PASSWORD, container.getPassword()) + .build(); + } +} diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainerProvider.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainerProvider.java new file mode 100644 index 00000000000..1638901226c --- /dev/null +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainerProvider.java @@ -0,0 +1,29 @@ +package org.testcontainers.containers; + +import com.google.auto.service.AutoService; +import io.r2dbc.mssql.MssqlConnectionFactoryProvider; +import io.r2dbc.spi.ConnectionFactoryOptions; +import org.testcontainers.r2dbc.R2DBCDatabaseContainer; +import org.testcontainers.r2dbc.R2DBCDatabaseContainerProvider; + +@AutoService(R2DBCDatabaseContainerProvider.class) +public class MSSQLR2DBCDatabaseContainerProvider implements R2DBCDatabaseContainerProvider { + + static final String DRIVER = MssqlConnectionFactoryProvider.MSSQL_DRIVER; + + @Override + public boolean supports(ConnectionFactoryOptions options) { + return DRIVER.equals(options.getRequiredValue(ConnectionFactoryOptions.DRIVER)); + } + + @Override + public R2DBCDatabaseContainer createContainer(ConnectionFactoryOptions options) { + String image = MSSQLServerContainer.IMAGE + ":" + options.getRequiredValue(IMAGE_TAG_OPTION); + MSSQLServerContainer container = new MSSQLServerContainer<>(image); + + if (Boolean.TRUE.equals(options.getValue(REUSABLE_OPTION))) { + container.withReuse(true); + } + return new MSSQLR2DBCDatabaseContainer(container); + } +} diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainerTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainerTest.java new file mode 100644 index 00000000000..1fcf2dd293b --- /dev/null +++ b/modules/mssqlserver/src/test/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainerTest.java @@ -0,0 +1,22 @@ +package org.testcontainers.containers; + +import io.r2dbc.spi.ConnectionFactoryOptions; +import org.testcontainers.r2dbc.AbstractR2DBCDatabaseContainerTest; + +public class MSSQLR2DBCDatabaseContainerTest extends AbstractR2DBCDatabaseContainerTest> { + + @Override + protected ConnectionFactoryOptions getOptions(MSSQLServerContainer container) { + return MSSQLR2DBCDatabaseContainer.getOptions(container); + } + + @Override + protected String createR2DBCUrl() { + return "r2dbc:tc:sqlserver:///?TC_IMAGE_TAG=2017-CU12"; + } + + @Override + protected MSSQLServerContainer createContainer() { + return new MSSQLServerContainer<>(); + } +} diff --git a/modules/mssqlserver/src/test/resources/container-license-acceptance.txt b/modules/mssqlserver/src/test/resources/container-license-acceptance.txt new file mode 100644 index 00000000000..8a704b9c066 --- /dev/null +++ b/modules/mssqlserver/src/test/resources/container-license-acceptance.txt @@ -0,0 +1 @@ +mcr.microsoft.com/mssql/server:2017-CU12 diff --git a/modules/mssqlserver/src/test/resources/logback-test.xml b/modules/mssqlserver/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..535e406fc13 --- /dev/null +++ b/modules/mssqlserver/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index da609a1b535..10fd90b3c38 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -1,11 +1,20 @@ description = "Testcontainers :: JDBC :: MySQL" dependencies { + annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6' + compileOnly 'com.google.auto.service:auto-service:1.0-rc6' + compile project(':jdbc') + compileOnly project(':r2dbc') + compileOnly 'dev.miku:r2dbc-mysql:0.8.1.RELEASE' + testCompile 'mysql:mysql-connector-java:8.0.19' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.33' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.vibur:vibur-dbcp:25.0' + + testCompile testFixtures(project(':r2dbc')) + testCompile 'dev.miku:r2dbc-mysql:0.8.1.RELEASE' } diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainer.java new file mode 100644 index 00000000000..c43ffcca1b2 --- /dev/null +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainer.java @@ -0,0 +1,33 @@ +package org.testcontainers.containers; + +import io.r2dbc.spi.ConnectionFactoryOptions; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Delegate; +import org.testcontainers.lifecycle.Startable; +import org.testcontainers.r2dbc.R2DBCDatabaseContainer; + +@RequiredArgsConstructor +public class MySQLR2DBCDatabaseContainer implements R2DBCDatabaseContainer { + + @Delegate(types = Startable.class) + private final MySQLContainer container; + + public static ConnectionFactoryOptions getOptions(MySQLContainer container) { + ConnectionFactoryOptions options = ConnectionFactoryOptions.builder() + .option(ConnectionFactoryOptions.DRIVER, MySQLR2DBCDatabaseContainerProvider.DRIVER) + .build(); + + return new MySQLR2DBCDatabaseContainer(container).configure(options); + } + + @Override + public ConnectionFactoryOptions configure(ConnectionFactoryOptions options) { + return options.mutate() + .option(ConnectionFactoryOptions.HOST, container.getContainerIpAddress()) + .option(ConnectionFactoryOptions.PORT, container.getMappedPort(MySQLContainer.MYSQL_PORT)) + .option(ConnectionFactoryOptions.DATABASE, container.getDatabaseName()) + .option(ConnectionFactoryOptions.USER, container.getUsername()) + .option(ConnectionFactoryOptions.PASSWORD, container.getPassword()) + .build(); + } +} diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerProvider.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerProvider.java new file mode 100644 index 00000000000..3a01616caba --- /dev/null +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerProvider.java @@ -0,0 +1,30 @@ +package org.testcontainers.containers; + +import com.google.auto.service.AutoService; +import dev.miku.r2dbc.mysql.MySqlConnectionFactoryProvider; +import io.r2dbc.spi.ConnectionFactoryOptions; +import org.testcontainers.r2dbc.R2DBCDatabaseContainer; +import org.testcontainers.r2dbc.R2DBCDatabaseContainerProvider; + +@AutoService(R2DBCDatabaseContainerProvider.class) +public class MySQLR2DBCDatabaseContainerProvider implements R2DBCDatabaseContainerProvider { + + static final String DRIVER = MySqlConnectionFactoryProvider.MYSQL_DRIVER; + + @Override + public boolean supports(ConnectionFactoryOptions options) { + return DRIVER.equals(options.getRequiredValue(ConnectionFactoryOptions.DRIVER)); + } + + @Override + public R2DBCDatabaseContainer createContainer(ConnectionFactoryOptions options) { + String image = MySQLContainer.IMAGE + ":" + options.getRequiredValue(IMAGE_TAG_OPTION); + MySQLContainer container = new MySQLContainer<>(image) + .withDatabaseName(options.getRequiredValue(ConnectionFactoryOptions.DATABASE)); + + if (Boolean.TRUE.equals(options.getValue(REUSABLE_OPTION))) { + container.withReuse(true); + } + return new MySQLR2DBCDatabaseContainer(container); + } +} diff --git a/modules/mysql/src/test/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerTest.java b/modules/mysql/src/test/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerTest.java new file mode 100644 index 00000000000..19edc3dbca4 --- /dev/null +++ b/modules/mysql/src/test/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerTest.java @@ -0,0 +1,23 @@ +package org.testcontainers.containers; + +import io.r2dbc.spi.ConnectionFactoryOptions; +import org.testcontainers.r2dbc.AbstractR2DBCDatabaseContainerTest; + +public class MySQLR2DBCDatabaseContainerTest extends AbstractR2DBCDatabaseContainerTest> { + + @Override + protected ConnectionFactoryOptions getOptions(MySQLContainer container) { + return MySQLR2DBCDatabaseContainer.getOptions(container); + } + + @Override + protected String createR2DBCUrl() { + return "r2dbc:tc:mysql:///db?TC_IMAGE_TAG=5.7.22"; + } + + @Override + protected MySQLContainer createContainer() { + return new MySQLContainer<>(); + } + +} diff --git a/modules/mysql/src/test/resources/logback-test.xml b/modules/mysql/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..535e406fc13 --- /dev/null +++ b/modules/mysql/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 89d40fe6923..c629c1c5a13 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -1,9 +1,17 @@ description = "Testcontainers :: JDBC :: PostgreSQL" dependencies { + annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6' + compileOnly 'com.google.auto.service:auto-service:1.0-rc6' + compile project(':jdbc') + compileOnly project(':r2dbc') + compileOnly 'io.r2dbc:r2dbc-postgresql:0.8.1.RELEASE' testCompile 'org.postgresql:postgresql:42.2.12' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' + + testCompile testFixtures(project(':r2dbc')) + testCompile 'io.r2dbc:r2dbc-postgresql:0.8.1.RELEASE' } diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainer.java new file mode 100644 index 00000000000..9bfb9fdee05 --- /dev/null +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainer.java @@ -0,0 +1,33 @@ +package org.testcontainers.containers; + +import io.r2dbc.spi.ConnectionFactoryOptions; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Delegate; +import org.testcontainers.lifecycle.Startable; +import org.testcontainers.r2dbc.R2DBCDatabaseContainer; + +@RequiredArgsConstructor +public final class PostgreSQLR2DBCDatabaseContainer implements R2DBCDatabaseContainer { + + @Delegate(types = Startable.class) + private final PostgreSQLContainer container; + + public static ConnectionFactoryOptions getOptions(PostgreSQLContainer container) { + ConnectionFactoryOptions options = ConnectionFactoryOptions.builder() + .option(ConnectionFactoryOptions.DRIVER, PostgreSQLR2DBCDatabaseContainerProvider.DRIVER) + .build(); + + return new PostgreSQLR2DBCDatabaseContainer(container).configure(options); + } + + @Override + public ConnectionFactoryOptions configure(ConnectionFactoryOptions options) { + return options.mutate() + .option(ConnectionFactoryOptions.HOST, container.getContainerIpAddress()) + .option(ConnectionFactoryOptions.PORT, container.getMappedPort(PostgreSQLContainer.POSTGRESQL_PORT)) + .option(ConnectionFactoryOptions.DATABASE, container.getDatabaseName()) + .option(ConnectionFactoryOptions.USER, container.getUsername()) + .option(ConnectionFactoryOptions.PASSWORD, container.getPassword()) + .build(); + } +} diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainerProvider.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainerProvider.java new file mode 100644 index 00000000000..5e9da980b8e --- /dev/null +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainerProvider.java @@ -0,0 +1,30 @@ +package org.testcontainers.containers; + +import com.google.auto.service.AutoService; +import io.r2dbc.postgresql.PostgresqlConnectionFactoryProvider; +import io.r2dbc.spi.ConnectionFactoryOptions; +import org.testcontainers.r2dbc.R2DBCDatabaseContainer; +import org.testcontainers.r2dbc.R2DBCDatabaseContainerProvider; + +@AutoService(R2DBCDatabaseContainerProvider.class) +public final class PostgreSQLR2DBCDatabaseContainerProvider implements R2DBCDatabaseContainerProvider { + + static final String DRIVER = PostgresqlConnectionFactoryProvider.POSTGRESQL_DRIVER; + + @Override + public boolean supports(ConnectionFactoryOptions options) { + return DRIVER.equals(options.getRequiredValue(ConnectionFactoryOptions.DRIVER)); + } + + @Override + public R2DBCDatabaseContainer createContainer(ConnectionFactoryOptions options) { + String image = PostgreSQLContainer.IMAGE + ":" + options.getRequiredValue(IMAGE_TAG_OPTION); + PostgreSQLContainer container = new PostgreSQLContainer<>(image) + .withDatabaseName(options.getRequiredValue(ConnectionFactoryOptions.DATABASE)); + + if (Boolean.TRUE.equals(options.getValue(REUSABLE_OPTION))) { + container.withReuse(true); + } + return new PostgreSQLR2DBCDatabaseContainer(container); + } +} diff --git a/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainerTest.java b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainerTest.java new file mode 100644 index 00000000000..505b37859ba --- /dev/null +++ b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainerTest.java @@ -0,0 +1,27 @@ +package org.testcontainers.containers; + +import io.r2dbc.spi.ConnectionFactoryOptions; +import org.testcontainers.r2dbc.AbstractR2DBCDatabaseContainerTest; + +public class PostgreSQLR2DBCDatabaseContainerTest extends AbstractR2DBCDatabaseContainerTest> { + + @Override + protected PostgreSQLContainer createContainer() { + return new PostgreSQLContainer<>(); + } + + @Override + protected ConnectionFactoryOptions getOptions(PostgreSQLContainer container) { + // get_options { + ConnectionFactoryOptions options = PostgreSQLR2DBCDatabaseContainer.getOptions( + container + ); + // } + + return options; + } + + protected String createR2DBCUrl() { + return "r2dbc:tc:postgresql:///db?TC_IMAGE_TAG=10-alpine"; + } +} diff --git a/modules/postgresql/src/test/resources/logback-test.xml b/modules/postgresql/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..535e406fc13 --- /dev/null +++ b/modules/postgresql/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle new file mode 100644 index 00000000000..a7388ebc6d2 --- /dev/null +++ b/modules/r2dbc/build.gradle @@ -0,0 +1,20 @@ +plugins { + id "java-test-fixtures" +} + +description = "Testcontainers :: R2DBC" + +dependencies { + annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6' + compileOnly 'com.google.auto.service:auto-service:1.0-rc6' + + compile project(':testcontainers') + compile 'io.r2dbc:r2dbc-spi:0.8.1.RELEASE' + + testCompile 'org.assertj:assertj-core:3.14.0' + testCompile 'io.r2dbc:r2dbc-postgresql:0.8.1.RELEASE' + testCompile project(':postgresql') + + testFixturesCompile 'io.projectreactor:reactor-core:3.3.4.RELEASE' + testFixturesCompile 'org.assertj:assertj-core:3.14.0' +} diff --git a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/CancellableSubscription.java b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/CancellableSubscription.java new file mode 100644 index 00000000000..e64d19a9f49 --- /dev/null +++ b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/CancellableSubscription.java @@ -0,0 +1,23 @@ +package org.testcontainers.r2dbc; + +import org.reactivestreams.Subscription; + +import java.util.concurrent.atomic.AtomicBoolean; + +class CancellableSubscription implements Subscription { + + private final AtomicBoolean cancelled = new AtomicBoolean(); + + @Override + public void request(long n) { + } + + @Override + public void cancel() { + cancelled.set(true); + } + + public boolean isCancelled() { + return cancelled.get(); + } +} diff --git a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/ConnectionPublisher.java b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/ConnectionPublisher.java new file mode 100644 index 00000000000..489d545429d --- /dev/null +++ b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/ConnectionPublisher.java @@ -0,0 +1,161 @@ +package org.testcontainers.r2dbc; + +import io.r2dbc.spi.Connection; +import io.r2dbc.spi.ConnectionFactory; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; + +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +/** + * Design notes: + * - ConnectionPublisher is Mono-like (0..1), the request amount is ignored + * - given the testing nature, the performance requirements are less strict + * - "synchronized" is used to avoid races + * - Reactive Streams spec violations are not checked (e.g. non-positive request) + */ +class ConnectionPublisher implements Publisher { + + private final Supplier> futureSupplier; + + ConnectionPublisher(Supplier> futureSupplier) { + this.futureSupplier = futureSupplier; + } + + @Override + public void subscribe(Subscriber actual) { + actual.onSubscribe(new StateMachineSubscription(actual)); + } + + private class StateMachineSubscription implements Subscription { + + private final Subscriber actual; + + Subscription subscriptionState; + + StateMachineSubscription(Subscriber actual) { + this.actual = actual; + subscriptionState = new WaitRequestSubscriptionState(); + } + + @Override + public synchronized void request(long n) { + subscriptionState.request(n); + } + + @Override + public synchronized void cancel() { + subscriptionState.cancel(); + } + + synchronized void transitionTo(SubscriptionState newState) { + subscriptionState = newState; + newState.enter(); + } + + abstract class SubscriptionState implements Subscription { + void enter() { + } + } + + class WaitRequestSubscriptionState extends SubscriptionState { + + @Override + public void request(long n) { + transitionTo(new WaitFutureCompletionSubscriptionState()); + } + + @Override + public void cancel() { + } + } + + class WaitFutureCompletionSubscriptionState extends SubscriptionState { + + private CompletableFuture future; + + @Override + void enter() { + this.future = futureSupplier.get(); + + future.whenComplete((connectionFactory, e) -> { + if (e != null) { + actual.onSubscribe(EmptySubscription.INSTANCE); + actual.onError(e); + return; + } + + Publisher publisher = connectionFactory.create(); + transitionTo(new ProxySubscriptionState(publisher)); + }); + } + + @Override + public void request(long n) { + } + + @Override + public void cancel() { + future.cancel(true); + } + } + + class ProxySubscriptionState extends SubscriptionState implements Subscriber { + + private final Publisher publisher; + + private Subscription s; + + private boolean cancelled = false; + + ProxySubscriptionState(Publisher publisher) { + this.publisher = publisher; + } + + @Override + void enter() { + publisher.subscribe(this); + } + + @Override + public void request(long n) { + // Ignore + } + + @Override + public synchronized void cancel() { + cancelled = true; + if (s != null) { + s.cancel(); + } + } + + @Override + public synchronized void onSubscribe(Subscription s) { + this.s = s; + if (!cancelled) { + s.request(1); + } else { + s.cancel(); + } + } + + @Override + public void onNext(Connection connection) { + actual.onNext(connection); + } + + @Override + public void onError(Throwable t) { + actual.onError(t); + } + + @Override + public void onComplete() { + actual.onComplete(); + } + } + } +} diff --git a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/EmptySubscription.java b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/EmptySubscription.java new file mode 100644 index 00000000000..7828d121c6f --- /dev/null +++ b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/EmptySubscription.java @@ -0,0 +1,17 @@ +package org.testcontainers.r2dbc; + +import org.reactivestreams.Subscription; + +enum EmptySubscription implements Subscription { + INSTANCE; + + @Override + public void request(long n) { + + } + + @Override + public void cancel() { + + } +} diff --git a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/Hidden.java b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/Hidden.java new file mode 100644 index 00000000000..f642a60d67c --- /dev/null +++ b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/Hidden.java @@ -0,0 +1,73 @@ +package org.testcontainers.r2dbc; + +import com.google.auto.service.AutoService; +import io.r2dbc.spi.ConnectionFactory; +import io.r2dbc.spi.ConnectionFactoryOptions; +import io.r2dbc.spi.ConnectionFactoryProvider; + +/** + * Hide inner classes that must be public due to the way {@link java.util.ServiceLoader} works + */ +class Hidden { + + @AutoService(ConnectionFactoryProvider.class) + public static final class TestcontainersR2DBCConnectionFactoryProvider implements ConnectionFactoryProvider { + + public static final String DRIVER = "tc"; + + @Override + public ConnectionFactory create(ConnectionFactoryOptions options) { + options = sanitize(options); + options = removeProxying(options); + + return new TestcontainersR2DBCConnectionFactory(options); + } + + private ConnectionFactoryOptions sanitize(ConnectionFactoryOptions options) { + ConnectionFactoryOptions.Builder builder = options.mutate(); + + Object reusable = options.getValue(R2DBCDatabaseContainerProvider.REUSABLE_OPTION); + if (reusable instanceof String) { + builder.option(R2DBCDatabaseContainerProvider.REUSABLE_OPTION, Boolean.valueOf((String) reusable)); + } + return builder.build(); + } + + private ConnectionFactoryOptions removeProxying(ConnectionFactoryOptions options) { + // To delegate to the next factory provider, inspect the PROTOCOL and convert it to the next DRIVER and PROTOCOL values. + // + // example: + // | Property | Input | Output | + // |----------|-----------------|--------------| + // | DRIVER | tc | postgres | + // | PROTOCOL | postgres | | + + String protocol = options.getRequiredValue(ConnectionFactoryOptions.PROTOCOL); + if (protocol.trim().length() == 0) { + throw new IllegalArgumentException("Invalid protocol: " + protocol); + } + String[] protocols = protocol.split(":", 2); + String driverDelegate = protocols[0]; + + // when protocol does NOT contain COLON, the length becomes 1 + String protocolDelegate = protocols.length == 2 ? protocols[1] : ""; + + return ConnectionFactoryOptions.builder() + .from(options) + .option(ConnectionFactoryOptions.DRIVER, driverDelegate) + .option(ConnectionFactoryOptions.PROTOCOL, protocolDelegate) + .build(); + } + + @Override + public boolean supports(ConnectionFactoryOptions options) { + return DRIVER.equals(options.getValue(ConnectionFactoryOptions.DRIVER)); + } + + @Override + public String getDriver() { + return DRIVER; + } + + } +} diff --git a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/R2DBCDatabaseContainer.java b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/R2DBCDatabaseContainer.java new file mode 100644 index 00000000000..a303cbafff8 --- /dev/null +++ b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/R2DBCDatabaseContainer.java @@ -0,0 +1,9 @@ +package org.testcontainers.r2dbc; + +import io.r2dbc.spi.ConnectionFactoryOptions; +import org.testcontainers.lifecycle.Startable; + +public interface R2DBCDatabaseContainer extends Startable { + + ConnectionFactoryOptions configure(ConnectionFactoryOptions options); +} diff --git a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/R2DBCDatabaseContainerProvider.java b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/R2DBCDatabaseContainerProvider.java new file mode 100644 index 00000000000..2eabc0d521b --- /dev/null +++ b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/R2DBCDatabaseContainerProvider.java @@ -0,0 +1,15 @@ +package org.testcontainers.r2dbc; + +import io.r2dbc.spi.ConnectionFactoryOptions; +import io.r2dbc.spi.Option; + +public interface R2DBCDatabaseContainerProvider { + + Option REUSABLE_OPTION = Option.valueOf("TC_REUSABLE"); + + Option IMAGE_TAG_OPTION = Option.valueOf("TC_IMAGE_TAG"); + + boolean supports(ConnectionFactoryOptions options); + + R2DBCDatabaseContainer createContainer(ConnectionFactoryOptions options); +} diff --git a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactory.java b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactory.java new file mode 100644 index 00000000000..1a0fc57ad8c --- /dev/null +++ b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactory.java @@ -0,0 +1,104 @@ +package org.testcontainers.r2dbc; + +import io.r2dbc.spi.Closeable; +import io.r2dbc.spi.Connection; +import io.r2dbc.spi.ConnectionFactories; +import io.r2dbc.spi.ConnectionFactory; +import io.r2dbc.spi.ConnectionFactoryMetadata; +import io.r2dbc.spi.ConnectionFactoryOptions; +import org.reactivestreams.Publisher; +import org.testcontainers.lifecycle.Startable; + +import java.util.ServiceLoader; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.StreamSupport; + +class TestcontainersR2DBCConnectionFactory implements ConnectionFactory, Closeable { + + private static final AtomicLong THREAD_COUNT = new AtomicLong(); + + private static final Executor EXECUTOR = Executors.newCachedThreadPool(r -> { + Thread thread = new Thread(r); + thread.setName("testcontainers-r2dbc-" + THREAD_COUNT.getAndIncrement()); + thread.setDaemon(true); + return thread; + }); + + private final ConnectionFactoryOptions options; + + private final R2DBCDatabaseContainerProvider containerProvider; + + private CompletableFuture future; + + TestcontainersR2DBCConnectionFactory(ConnectionFactoryOptions options) { + this.options = options; + + containerProvider = StreamSupport.stream(ServiceLoader.load(R2DBCDatabaseContainerProvider.class).spliterator(), false) + .filter(it -> it.supports(options)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("Missing provider for " + options)); + } + + @Override + public Publisher create() { + return new ConnectionPublisher( + () -> { + if (future == null) { + synchronized (this) { + if (future == null) { + future = CompletableFuture.supplyAsync(() -> { + R2DBCDatabaseContainer container = containerProvider.createContainer(options); + container.start(); + return container; + }, EXECUTOR); + } + } + } + return future.thenApply(it -> { + return ConnectionFactories.find( + it.configure(options) + ); + }); + } + ); + } + + @Override + public ConnectionFactoryMetadata getMetadata() { + // TODO check whether it is fine to call it without setting the host/port and other values + return ConnectionFactories.find(options).getMetadata(); + } + + @Override + public Publisher close() { + return s -> { + CompletableFuture futureRef; + synchronized (this) { + futureRef = this.future; + this.future = null; + } + + CancellableSubscription subscription = new CancellableSubscription(); + s.onSubscribe(subscription); + + if (futureRef == null) { + if (!subscription.isCancelled()) { + s.onComplete(); + } + } else { + futureRef.thenAcceptAsync(Startable::stop, EXECUTOR); + + EXECUTOR.execute(() -> { + futureRef.cancel(true); + if (!subscription.isCancelled()) { + s.onComplete(); + } + }); + } + }; + } + +} diff --git a/modules/r2dbc/src/test/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactoryTest.java b/modules/r2dbc/src/test/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactoryTest.java new file mode 100644 index 00000000000..6fad93f0e2a --- /dev/null +++ b/modules/r2dbc/src/test/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactoryTest.java @@ -0,0 +1,79 @@ +package org.testcontainers.r2dbc; + +import io.r2dbc.postgresql.api.PostgresqlException; +import io.r2dbc.spi.Closeable; +import io.r2dbc.spi.Connection; +import io.r2dbc.spi.ConnectionFactories; +import io.r2dbc.spi.ConnectionFactory; +import io.r2dbc.spi.Result; +import org.assertj.core.api.Assertions; +import org.junit.Test; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TestcontainersR2DBCConnectionFactoryTest { + + @Test + public void failsOnUnknownProvider() { + String nonExistingProvider = UUID.randomUUID().toString(); + Assertions + .assertThatThrownBy(() -> { + ConnectionFactories.get(String.format("r2dbc:tc:%s:///db", nonExistingProvider)); + }) + .hasMessageContaining("Missing provider") + .hasMessageContaining(nonExistingProvider); + } + + @Test + public void reusesUntilConnectionFactoryIsClosed() { + String url = "r2dbc:tc:postgresql:///db?TC_IMAGE_TAG=10-alpine"; + ConnectionFactory connectionFactory = ConnectionFactories.get(url); + + Integer updated = Flux + .usingWhen( + connectionFactory.create(), + connection -> { + return Mono + .from(connection.createStatement("CREATE TABLE test(id integer PRIMARY KEY)").execute()) + .thenMany(connection.createStatement("INSERT INTO test(id) VALUES(123)").execute()) + .flatMap(Result::getRowsUpdated); + }, + Connection::close + ) + .blockFirst(); + + assertThat(updated).isEqualTo(1); + + Flux select = Flux + .usingWhen( + Flux.defer(connectionFactory::create), + connection -> { + return Flux + .from(connection.createStatement("SELECT COUNT(*) FROM test").execute()) + .flatMap(it -> it.map((row, meta) -> (Long) row.get(0))); + }, + Connection::close + ); + + Long rows = select.blockFirst(); + + assertThat(rows).isEqualTo(1); + + close(connectionFactory); + + Assertions + .assertThatThrownBy(select::blockFirst) + .isInstanceOf(PostgresqlException.class) + // relation "X" does not exists + // https://github.com/postgres/postgres/blob/REL_10_0/src/backend/utils/errcodes.txt#L349 + .returns("42P01", e -> ((PostgresqlException) e).getErrorDetails().getCode()); + } + + private static void close(ConnectionFactory connectionFactory) { + Mono.from(((Closeable) connectionFactory).close()).block(); + } +} diff --git a/modules/r2dbc/src/test/resources/logback-test.xml b/modules/r2dbc/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..535e406fc13 --- /dev/null +++ b/modules/r2dbc/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + diff --git a/modules/r2dbc/src/testFixtures/java/org/testcontainers/r2dbc/AbstractR2DBCDatabaseContainerTest.java b/modules/r2dbc/src/testFixtures/java/org/testcontainers/r2dbc/AbstractR2DBCDatabaseContainerTest.java new file mode 100644 index 00000000000..7f14ecca2a7 --- /dev/null +++ b/modules/r2dbc/src/testFixtures/java/org/testcontainers/r2dbc/AbstractR2DBCDatabaseContainerTest.java @@ -0,0 +1,64 @@ +package org.testcontainers.r2dbc; + +import io.r2dbc.spi.Closeable; +import io.r2dbc.spi.Connection; +import io.r2dbc.spi.ConnectionFactories; +import io.r2dbc.spi.ConnectionFactory; +import io.r2dbc.spi.ConnectionFactoryOptions; +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import static org.assertj.core.api.Assertions.assertThat; + +public abstract class AbstractR2DBCDatabaseContainerTest> { + + protected abstract ConnectionFactoryOptions getOptions(T container); + + protected abstract String createR2DBCUrl(); + + protected String createTestQuery(int result) { + return String.format("SELECT %d", result); + } + + @Test + public final void testGetOptions() { + try (T container = createContainer()) { + container.start(); + + ConnectionFactory connectionFactory = ConnectionFactories.get(getOptions(container)); + runTestQuery(connectionFactory); + } + } + + @Test + public final void testUrlSupport() { + ConnectionFactory connectionFactory = ConnectionFactories.get(createR2DBCUrl()); + runTestQuery(connectionFactory); + } + + protected abstract T createContainer(); + + protected void runTestQuery(ConnectionFactory connectionFactory) { + try { + int expected = 42; + Number result = Flux + .usingWhen( + connectionFactory.create(), + connection -> connection.createStatement(createTestQuery(expected)).execute(), + Connection::close + ) + .flatMap(it -> it.map((row, meta) -> (Number) row.get(0))) + .blockFirst(); + + assertThat(result) + .isNotNull() + .returns(expected, Number::intValue); + } finally { + if (connectionFactory instanceof Closeable) { + Mono.from(((Closeable) connectionFactory).close()).block(); + } + } + } +} From 8dede0455827f89c03e753ee75dc70326303de68 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 12 Apr 2020 19:58:19 +0200 Subject: [PATCH 1236/5615] fix the shading exclusion list --- core/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/core/build.gradle b/core/build.gradle index 01575f0d351..84a20425cc5 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -50,6 +50,7 @@ shadowJar { 'META-INF/services/javax.ws.rs.ext.*', 'META-INF/services/java.security.Provider', 'mozilla/public-suffix-list.txt', + 'module-info.class', ].each { exclude(it) } project.afterEvaluate { From 631a3f46971156e65f3f9843624acd4188768b71 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Sun, 12 Apr 2020 20:08:18 +0200 Subject: [PATCH 1237/5615] Support VNC recording with BrowserWebdriverContainer in Spock-Extension (#2548) Initially done as part of the Hackergarten at Gr8ConfEU 2018. Thanks to Tamer Shahin, Marcin Erdmann and Dawid Kublik for working on this! Fixes #631. --- modules/spock/build.gradle | 2 + .../spock/SpockTestDescription.groovy | 36 ++++++++ .../spock/TestcontainersExtension.groovy | 17 +++- .../TestcontainersMethodInterceptor.groovy | 25 +++++- .../spock/BrowserVncRecordingIT.groovy | 88 +++++++++++++++++++ 5 files changed, 164 insertions(+), 4 deletions(-) create mode 100644 modules/spock/src/main/groovy/org/testcontainers/spock/SpockTestDescription.groovy create mode 100644 modules/spock/src/test/groovy/org/testcontainers/spock/BrowserVncRecordingIT.groovy diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 2bcf7431808..d091ce068dc 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -8,8 +8,10 @@ dependencies { compile project(':testcontainers') compile 'org.spockframework:spock-core:1.3-groovy-2.5' + testCompile project(':selenium') testCompile project(':mysql') testCompile project(':postgresql') + testCompile 'com.zaxxer:HikariCP:3.4.2' testCompile 'org.apache.httpcomponents:httpclient:4.5.12' diff --git a/modules/spock/src/main/groovy/org/testcontainers/spock/SpockTestDescription.groovy b/modules/spock/src/main/groovy/org/testcontainers/spock/SpockTestDescription.groovy new file mode 100644 index 00000000000..85b58724000 --- /dev/null +++ b/modules/spock/src/main/groovy/org/testcontainers/spock/SpockTestDescription.groovy @@ -0,0 +1,36 @@ +package org.testcontainers.spock + +import groovy.transform.PackageScope +import org.spockframework.runtime.extension.IMethodInvocation +import org.testcontainers.lifecycle.TestDescription + +/** + * Spock specific implementation of a Testcontainers TestDescription. + * + * Filesystem friendly name is based on Specification and Feature. + */ +@PackageScope +class SpockTestDescription implements TestDescription { + + String specName + String featureName + + static SpockTestDescription fromTestDescription(IMethodInvocation invocation) { + return new SpockTestDescription([ + specName: invocation.spec.name, + featureName: invocation.feature.name + ]) + } + + @Override + String getTestId() { + return getFilesystemFriendlyName() + } + + @Override + String getFilesystemFriendlyName() { + return [specName, featureName].collect { + URLEncoder.encode(it, 'UTF-8') + }.join('-') + } +} diff --git a/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersExtension.groovy b/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersExtension.groovy index 9f26cf95451..1f66fd8f596 100644 --- a/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersExtension.groovy +++ b/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersExtension.groovy @@ -1,17 +1,32 @@ package org.testcontainers.spock +import org.spockframework.runtime.AbstractRunListener import org.spockframework.runtime.extension.AbstractAnnotationDrivenExtension +import org.spockframework.runtime.model.ErrorInfo import org.spockframework.runtime.model.SpecInfo class TestcontainersExtension extends AbstractAnnotationDrivenExtension { @Override void visitSpecAnnotation(Testcontainers annotation, SpecInfo spec) { - def interceptor = new TestcontainersMethodInterceptor(spec) + def listener = new ErrorListener() + def interceptor = new TestcontainersMethodInterceptor(spec, listener) spec.addSetupSpecInterceptor(interceptor) spec.addCleanupSpecInterceptor(interceptor) spec.addSetupInterceptor(interceptor) spec.addCleanupInterceptor(interceptor) + + spec.addListener(listener) + + } + + private class ErrorListener extends AbstractRunListener { + List errors = [] + + @Override + void error(ErrorInfo error) { + errors.add(error) + } } } diff --git a/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersMethodInterceptor.groovy b/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersMethodInterceptor.groovy index d4b6f3ccc9d..a70ec0d6e53 100644 --- a/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersMethodInterceptor.groovy +++ b/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersMethodInterceptor.groovy @@ -6,13 +6,17 @@ import org.spockframework.runtime.model.FieldInfo import org.spockframework.runtime.model.SpecInfo import org.testcontainers.containers.DockerComposeContainer import org.testcontainers.containers.GenericContainer +import org.testcontainers.lifecycle.TestLifecycleAware +import org.testcontainers.spock.TestcontainersExtension.ErrorListener class TestcontainersMethodInterceptor extends AbstractMethodInterceptor { private final SpecInfo spec + private final ErrorListener errorListener - TestcontainersMethodInterceptor(SpecInfo spec) { + TestcontainersMethodInterceptor(SpecInfo spec, ErrorListener errorListener) { this.spec = spec + this.errorListener = errorListener } @Override @@ -50,6 +54,13 @@ class TestcontainersMethodInterceptor extends AbstractMethodInterceptor { @Override void interceptCleanupMethod(IMethodInvocation invocation) throws Throwable { + findAllTestLifecycleAwareContainers(invocation).each { + // we assume first error is the one we want + def maybeException = Optional.ofNullable(errorListener.errors[0]?.exception) + def testDescription = SpockTestDescription.fromTestDescription(invocation) + it.afterTest(testDescription, maybeException) + } + def containers = findAllContainers(false) stopContainers(containers, invocation) @@ -65,6 +76,14 @@ class TestcontainersMethodInterceptor extends AbstractMethodInterceptor { } } + private List findAllTestLifecycleAwareContainers(IMethodInvocation invocation) { + spec.allFields.findAll { FieldInfo f -> + TestLifecycleAware.isAssignableFrom(f.type) + }.collect { + it.readValue(invocation.instance) as TestLifecycleAware + } + } + private List findAllComposeContainers(boolean shared) { spec.allFields.findAll { FieldInfo f -> DockerComposeContainer.isAssignableFrom(f.type) && f.shared == shared @@ -90,14 +109,14 @@ class TestcontainersMethodInterceptor extends AbstractMethodInterceptor { private static void startComposeContainers(List compose, IMethodInvocation invocation) { compose.each { FieldInfo f -> DockerComposeContainer c = f.readValue(invocation.instance) as DockerComposeContainer - c.starting(null) + c.start() } } private static void stopComposeContainers(List compose, IMethodInvocation invocation) { compose.each { FieldInfo f -> DockerComposeContainer c = f.readValue(invocation.instance) as DockerComposeContainer - c.finished(null) + c.stop() } } diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/BrowserVncRecordingIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/BrowserVncRecordingIT.groovy new file mode 100644 index 00000000000..794032a58c4 --- /dev/null +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/BrowserVncRecordingIT.groovy @@ -0,0 +1,88 @@ +package org.testcontainers.spock + +import org.intellij.lang.annotations.Language +import org.junit.Rule +import org.junit.rules.TemporaryFolder +import spock.lang.Specification +import spock.lang.Unroll +import spock.util.EmbeddedSpecRunner + +import static org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode.RECORD_ALL +import static org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode.RECORD_FAILING + +class BrowserVncRecordingIT extends Specification { + + @Rule + TemporaryFolder temp + + String recordingDir + + def setup() { + recordingDir = temp.getRoot().getAbsolutePath() + } + + @Unroll("For recording mode #recordingMode and failing test is #fails records video file named '#videoFileName'") + def "retains all recordings for RECORD_ALL if successful"() { + given: + + //noinspection GrPackage + @Language("groovy") + String myTest = """ +package org.testcontainers.spock + +import org.openqa.selenium.chrome.ChromeOptions +import org.openqa.selenium.remote.RemoteWebDriver +import org.testcontainers.containers.BrowserWebDriverContainer +import spock.lang.Specification + +import java.util.concurrent.TimeUnit + +import static org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode + +@Testcontainers +class BrowserWebdriverContainerIT extends Specification { + + BrowserWebDriverContainer browserContainer = new BrowserWebDriverContainer() + .withCapabilities(new ChromeOptions()) + .withRecordingMode("$recordingMode" as VncRecordingMode, new File("$recordingDir")) + + RemoteWebDriver driver + + def setup() { + driver = browserContainer.getWebDriver() + driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS) + } + + def "should record"() { + when: + driver.get("http://en.wikipedia.org/wiki/Randomness") + + then: + driver.findElementByPartialLinkText("pattern").isDisplayed() == !$fails + } + +} + + +""" + + when: + EmbeddedSpecRunner runner = new EmbeddedSpecRunner(throwFailure: false) + runner.run(myTest) + + then: + def videoDir = temp.getRoot().list() + if (videoFileName.isEmpty()) { + videoDir.length == 0 + } else { + videoDir.find { it.contains(videoFileName) } + } + + where: + recordingMode | fails | videoFileName + RECORD_ALL | false | 'BrowserWebdriverContainerIT-should+record' + RECORD_FAILING | false | '' + RECORD_FAILING | true | 'BrowserWebdriverContainerIT-should+record' + } + +} From c6e0cd9faf83a160bf1cb4b3f0b6b7b9535daad2 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 12 Apr 2020 20:23:51 +0200 Subject: [PATCH 1238/5615] Rewrite Couchbase module. closes #2447 (#2491) * Rewrite couchbase module. closes #2447 This changeset completely reworks the couchbase module and hopefully greatly improve the out-of-the-box experience. Note that this is a breaking change over the previous code because by intention it does NOT depend on SDK 2 so you can test SDK 2 and 3 with it at the same time. Highlights: - Removed the need for a SDK, so both 2 and 3 can be used with it. - Updated to 6.5.0 baseline and using alternate addresses for "proper" port exposure without having to rely on the socat proxy container like the previous version had to. - Allows to define which services should be exposed and handles states automatically (i.e. will not try to create the primary index if the query service is not enabled). Note that a bunch of tests have been removed since they are not adequate anymore. A side effect of the alternate address change is that older servers cannot be used. 6.5.0 is available in both CE and EE, and Couchbase in general allows EE versions to be used in development and testing so we should use it if we can. * Wait until query respsonds with 200 * fixes * add `getBootstrap*DirectPort()` methods, review fixes * Restore `getConnectionString()` * Apply suggestions from code review Co-Authored-By: Kevin Wittek * Update docs * Update docs (2) Co-authored-by: Michael Nitschinger Co-authored-by: Kevin Wittek --- docs/modules/databases/couchbase.md | 78 +- modules/couchbase/build.gradle | 3 +- .../couchbase/AbstractCouchbaseTest.java | 72 -- .../couchbase/BucketDefinition.java | 69 ++ .../couchbase/CouchbaseContainer.java | 683 +++++++++--------- .../CouchbaseQueryServiceWaitStrategy.java | 55 -- .../couchbase/CouchbaseService.java | 44 ++ .../couchbase/BaseCouchbaseContainerTest.java | 82 --- .../couchbase/Couchbase4_6Test.java | 14 - .../couchbase/Couchbase5_1Test.java | 14 - .../couchbase/Couchbase5_5Test.java | 14 - .../couchbase/CouchbaseCommunity5_1Test.java | 32 - .../couchbase/CouchbaseContainerTest.java | 78 +- 13 files changed, 530 insertions(+), 708 deletions(-) delete mode 100644 modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java create mode 100644 modules/couchbase/src/main/java/org/testcontainers/couchbase/BucketDefinition.java delete mode 100644 modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseQueryServiceWaitStrategy.java create mode 100644 modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseService.java delete mode 100644 modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java delete mode 100644 modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase4_6Test.java delete mode 100644 modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_1Test.java delete mode 100644 modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_5Test.java delete mode 100644 modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseCommunity5_1Test.java diff --git a/docs/modules/databases/couchbase.md b/docs/modules/databases/couchbase.md index f6d014a42d4..b71ee5de327 100644 --- a/docs/modules/databases/couchbase.md +++ b/docs/modules/databases/couchbase.md @@ -8,65 +8,25 @@ Testcontainers module for Couchbase. [Couchbase](https://www.couchbase.com/) is Running Couchbase as a stand-in in a test: -### Create your own bucket - -```java -public class SomeTest { - - @Rule - public CouchbaseContainer couchbase = new CouchbaseContainer() - .withClusterAdmin("admin", "secret") - .withNewBucket(DefaultBucketSettings.builder() - .enableFlush(true) - .name("bucket-name") - .password("secret") - .quota(100) - .type(BucketType.COUCHBASE) - .build()); - - @Test - public void someTestMethod() { - Bucket bucket = couchbase.getCouchbaseCluster().openBucket("bucket-name"); - - // ... interact with client as if using Couchbase normally - } -} -``` - -### Use preconfigured default bucket - -Bucket is cleared after each test - -```java -public class SomeTest extends AbstractCouchbaseTest { - - @Test - public void someTestMethod() { - Bucket bucket = getBucket(); - - // ... interact with client as if using Couchbase normally - } -} -``` - -### Special consideration - -Couchbase container is configured to use random available [ports](https://developer.couchbase.com/documentation/server/current/install/install-ports.html) for some ports only, as [Couchbase Java SDK](https://developer.couchbase.com/documentation/server/current/sdk/java/start-using-sdk.html) permit to configure only some ports: - -- **8091** : REST/HTTP traffic ([bootstrapHttpDirectPort](http://docs.couchbase.com/sdk-api/couchbase-java-client-2.4.6/com/couchbase/client/java/env/DefaultCouchbaseEnvironment.Builder.html#bootstrapCarrierDirectPort-int-)) -- **18091** : REST/HTTP traffic with SSL ([bootstrapHttpSslPort](http://docs.couchbase.com/sdk-api/couchbase-java-client-2.4.6/com/couchbase/client/java/env/DefaultCouchbaseEnvironment.Builder.html#bootstrapCarrierSslPort-int-)) -- **11210** : memcached ([bootstrapCarrierDirectPort](http://docs.couchbase.com/sdk-api/couchbase-java-client-2.4.6/com/couchbase/client/java/env/DefaultCouchbaseEnvironment.Builder.html#bootstrapCarrierDirectPort-int-)) -- **11207** : memcached SSL ([bootstrapCarrierSslPort](http://docs.couchbase.com/sdk-api/couchbase-java-client-2.4.6/com/couchbase/client/java/env/DefaultCouchbaseEnvironment.Builder.html#bootstrapCarrierSslPort-int-)) - -All other ports cannot be changed by Java SDK, there are sadly fixed: - -- **8092** : Queries, views, XDCR -- **8093** : REST/HTTP Query service -- **8094** : REST/HTTP Search Service -- **8095** : REST/HTTP Analytic service - -So if you disable Query, Search and Analytic service, you can run multiple instance of this container, otherwise, you're stuck with one instance, for now. - +1. Define a bucket: + + [Bucket Definition](../../../modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java) inside_block:bucket_definition + + +2. define a container: + + [Container definition](../../../modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java) inside_block:container_definition + + +3. create an environment & cluster: + + [Cluster creation](../../../modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java) inside_block:cluster_creation + + +4. authenticate: + + [Authentication](../../../modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java) inside_block:auth + ## Adding this module to your project dependencies diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 66f29fc4718..095f8828aac 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -2,7 +2,6 @@ description = "Testcontainers :: Couchbase" dependencies { compile project(':testcontainers') - compile 'com.couchbase.client:java-client:2.7.13' - testCompile project(':test-support') + testCompile 'com.couchbase.client:java-client:2.7.13' } diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java deleted file mode 100644 index 7fd99ca9fbc..00000000000 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.testcontainers.couchbase; - -import com.couchbase.client.java.Bucket; -import com.couchbase.client.java.CouchbaseCluster; -import com.couchbase.client.java.bucket.BucketType; -import com.couchbase.client.java.cluster.BucketSettings; -import com.couchbase.client.java.cluster.DefaultBucketSettings; -import com.couchbase.client.java.query.N1qlParams; -import com.couchbase.client.java.query.N1qlQuery; -import com.couchbase.client.java.query.consistency.ScanConsistency; -import org.junit.After; - -import java.util.concurrent.TimeUnit; - -/** - * Basic class that can be used for couchbase tests. It will clear the database after every test. - */ -public abstract class AbstractCouchbaseTest { - - public static final String TEST_BUCKET = "test"; - - public static final String DEFAULT_PASSWORD = "password"; - - Bucket bucket; - - @After - public void clear() { - try { - if (getCouchbaseContainer().isIndex() && getCouchbaseContainer().isQuery() && getCouchbaseContainer().isPrimaryIndex()) { - getBucket().query( - N1qlQuery.simple(String.format("DELETE FROM `%s`", getBucket().name()), - N1qlParams.build().consistency(ScanConsistency.STATEMENT_PLUS))); - } else { - getBucket().bucketManager().flush(); - } - } finally { - bucket.close(60, TimeUnit.SECONDS); - bucket = null; - } - } - - protected abstract CouchbaseContainer getCouchbaseContainer(); - - protected static CouchbaseContainer initCouchbaseContainer(String imageName) { - CouchbaseContainer couchbaseContainer = (imageName == null) ? new CouchbaseContainer() : new CouchbaseContainer(imageName); - couchbaseContainer.withNewBucket(getDefaultBucketSettings()); - return couchbaseContainer; - } - - protected static BucketSettings getDefaultBucketSettings() { - return DefaultBucketSettings.builder() - .enableFlush(true) - .name(TEST_BUCKET) - .password(DEFAULT_PASSWORD) - .quota(100) - .replicas(0) - .type(BucketType.COUCHBASE) - .build(); - } - - protected synchronized Bucket getBucket() { - if (bucket == null) { - bucket = openBucket(TEST_BUCKET, DEFAULT_PASSWORD); - } - return bucket; - } - - private Bucket openBucket(String bucketName, String password) { - CouchbaseCluster cluster = getCouchbaseContainer().getCouchbaseCluster(); - return cluster.openBucket(bucketName, password); - } -} diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/BucketDefinition.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/BucketDefinition.java new file mode 100644 index 00000000000..8c2b11403ad --- /dev/null +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/BucketDefinition.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2020 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.testcontainers.couchbase; + +/** + * Allows to configure the properties of a bucket that should be created. + */ +public class BucketDefinition { + + private final String name; + private boolean queryPrimaryIndex = true; + private int quota = 100; + + public BucketDefinition(final String name) { + this.name = name; + } + + /** + * Sets a custom bucket quota (100MB by default). + * + * @param quota the quota to set for the bucket. + * @return this {@link BucketDefinition} for chaining purposes. + */ + public BucketDefinition withQuota(final int quota) { + if (quota < 100) { + throw new IllegalArgumentException("Bucket quota cannot be less than 100MB!"); + } + this.quota = quota; + return this; + } + + /** + * Allows to disable creating a primary index for this bucket (enabled by default). + * + * @param create if false, a primary index will not be created. + * @return this {@link BucketDefinition} for chaining purposes. + */ + public BucketDefinition withPrimaryIndex(final boolean create) { + this.queryPrimaryIndex = create; + return this; + } + + public String getName() { + return name; + } + + public boolean hasPrimaryIndex() { + return queryPrimaryIndex; + } + + public int getQuota() { + return quota; + } + +} diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index e43cf8f652d..14e5744bb6d 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Couchbase, Inc. + * Copyright (c) 2020 Couchbase, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,447 +15,424 @@ */ package org.testcontainers.couchbase; -import com.couchbase.client.core.utils.Base64; -import com.couchbase.client.java.Bucket; -import com.couchbase.client.java.CouchbaseCluster; -import com.couchbase.client.java.cluster.*; -import com.couchbase.client.java.env.CouchbaseEnvironment; -import com.couchbase.client.java.env.DefaultCouchbaseEnvironment; -import com.couchbase.client.java.query.Index; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.dockerjava.api.command.ExecCreateCmdResponse; import com.github.dockerjava.api.command.InspectContainerResponse; -import com.google.common.collect.Lists; -import lombok.*; -import org.apache.commons.compress.utils.Sets; -import org.apache.commons.io.IOUtils; -import org.jetbrains.annotations.NotNull; +import com.github.dockerjava.api.model.ContainerNetwork; +import okhttp3.Credentials; +import okhttp3.FormBody; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.Network; -import org.testcontainers.containers.SocatContainer; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; -import org.testcontainers.images.builder.Transferable; -import org.testcontainers.utility.ThrowingFunction; +import org.testcontainers.containers.wait.strategy.WaitAllStrategy; -import java.io.DataOutputStream; import java.io.IOException; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static java.net.HttpURLConnection.HTTP_OK; -import static org.testcontainers.couchbase.CouchbaseContainer.CouchbasePort.*; /** - * Based on Laurent Doguin version, + * The couchbase container initializes and configures a Couchbase Server single node cluster. *

- * optimized by Tayeb Chlyah + * Note that it does not depend on a specific couchbase SDK, so it can be used with both the Java SDK 2 and 3 as well + * as the Scala SDK 1 or newer. We recommend using the latest and greatest SDKs for the best experience. */ -@AllArgsConstructor public class CouchbaseContainer extends GenericContainer { - public static final String VERSION = "5.5.1"; - public static final String DOCKER_IMAGE_NAME = "couchbase/server:"; - public static final ObjectMapper MAPPER = new ObjectMapper(); - public static final String STATIC_CONFIG = "/opt/couchbase/etc/couchbase/static_config"; - public static final String CAPI_CONFIG = "/opt/couchbase/etc/couchdb/default.d/capi.ini"; + private static final int MGMT_PORT = 8091; + + private static final int MGMT_SSL_PORT = 18091; + + private static final int VIEW_PORT = 8092; - private static final int REQUIRED_DEFAULT_PASSWORD_LENGTH = 6; + private static final int VIEW_SSL_PORT = 18092; - private String memoryQuota = "300"; + private static final int QUERY_PORT = 8093; - private String indexMemoryQuota = "300"; + private static final int QUERY_SSL_PORT = 18093; - private String clusterUsername = "Administrator"; + private static final int SEARCH_PORT = 8094; - private String clusterPassword = "password"; + private static final int SEARCH_SSL_PORT = 18094; - private boolean keyValue = true; + private static final int KV_PORT = 11210; - @Getter - private boolean query = true; + private static final int KV_SSL_PORT = 11207; - @Getter - private boolean index = true; + private static final String DOCKER_IMAGE_NAME = "couchbase/server"; - @Getter - private boolean primaryIndex = true; + private static final String VERSION = "6.5.0"; - @Getter - private boolean fts = false; + private static final ObjectMapper MAPPER = new ObjectMapper(); - @Getter(lazy = true) - private final CouchbaseEnvironment couchbaseEnvironment = createCouchbaseEnvironment(); + private static final OkHttpClient HTTP_CLIENT = new OkHttpClient(); - @Getter(lazy = true) - private final CouchbaseCluster couchbaseCluster = createCouchbaseCluster(); + private String username = "Administrator"; - private List newBuckets = new ArrayList<>(); + private String password = "password"; - private String urlBase; + private Set enabledServices = EnumSet.allOf(CouchbaseService.class); - private SocatContainer proxy; + private final List buckets = new ArrayList<>(); + /** + * Creates a new couchbase container with the default image and version. + */ public CouchbaseContainer() { - this(DOCKER_IMAGE_NAME + VERSION); + this(DOCKER_IMAGE_NAME + ":" + VERSION); } - public CouchbaseContainer(String imageName) { + /** + * Creates a new couchbase container with a custom image name. + * + * @param imageName the image name that should be used. + */ + public CouchbaseContainer(final String imageName) { super(imageName); - - withNetwork(Network.SHARED); - setWaitStrategy(new HttpWaitStrategy().forPath("/ui/index.html")); } - @Override - public Set getLivenessCheckPortNumbers() { - return Sets.newHashSet(getMappedPort(REST)); + /** + * Set custom username and password for the admin user. + * + * @param username the admin username to use. + * @param password the password for the admin user. + * @return this {@link CouchbaseContainer} for chaining purposes. + */ + public CouchbaseContainer withCredentials(final String username, final String password) { + checkNotRunning(); + this.username = username; + this.password = password; + return this; } - @Override - protected void configure() { - if (clusterPassword.length() < REQUIRED_DEFAULT_PASSWORD_LENGTH) { - logger().warn("The provided cluster admin password length is less then the default password policy length. " + - "Cluster start will fail if configured password requirements are not met."); - } + public CouchbaseContainer withBucket(final BucketDefinition bucketDefinition) { + checkNotRunning(); + this.buckets.add(bucketDefinition); + return this; } - @Override - @SneakyThrows - protected void doStart() { - startProxy(getNetworkAliases().get(0)); - try { - super.doStart(); - } catch (Throwable e) { - proxy.stop(); - throw e; - } + public CouchbaseContainer withEnabledServices(final CouchbaseService... enabled) { + checkNotRunning(); + this.enabledServices = EnumSet.copyOf(Arrays.asList(enabled)); + return this; } - @SneakyThrows - private void startProxy(String networkAlias) { - proxy = new SocatContainer().withNetwork(getNetwork()); - - for (CouchbasePort port : CouchbasePort.values()) { - if (port.isDynamic()) { - proxy.withTarget(port.getOriginalPort(), networkAlias); - } else { - proxy.addExposedPort(port.getOriginalPort()); - } - } - - proxy.setWaitStrategy(null); - proxy.start(); - - ExecCreateCmdResponse createCmdResponse = dockerClient - .execCreateCmd(proxy.getContainerId()) - .withCmd( - "sh", - "-c", - Stream.of(CouchbasePort.values()) - .map(port -> { - return "/usr/bin/socat " + - "TCP-LISTEN:" + port.getOriginalPort() + ",fork,reuseaddr " + - "TCP:" + networkAlias + ":" + getMappedPort(port); - }) - .collect(Collectors.joining(" & ", "true", "")) - ) - .exec(); - - dockerClient.execStartCmd(createCmdResponse.getId()) - .start() - .awaitCompletion(10, TimeUnit.SECONDS); + public final String getUsername() { + return username; } - @Override - public List getExposedPorts() { - return proxy.getExposedPorts(); + public final String getPassword() { + return password; } - @Override - public String getContainerIpAddress() { - return proxy.getContainerIpAddress(); + public int getBootstrapCarrierDirectPort() { + return getMappedPort(KV_PORT); } - @Override - public Integer getMappedPort(int originalPort) { - return proxy.getMappedPort(originalPort); + public int getBootstrapHttpDirectPort() { + return getMappedPort(MGMT_PORT); } - protected Integer getMappedPort(CouchbasePort port) { - return getMappedPort(port.getOriginalPort()); + public String getConnectionString() { + return String.format("couchbase://%s:%d", getContainerIpAddress(), getBootstrapCarrierDirectPort()); } @Override - public List getBoundPortNumbers() { - return proxy.getBoundPortNumbers(); - } - - @Override - @SuppressWarnings({"unchecked", "ConstantConditions"}) - public void stop() { - try { - stopCluster(); - ((AtomicReference) (Object) couchbaseEnvironment).set(null); - ((AtomicReference) (Object) couchbaseCluster).set(null); - } finally { - Stream.of(super::stop, proxy::stop).parallel().forEach(Runnable::run); + protected void configure() { + super.configure(); + + WaitAllStrategy waitStrategy = new WaitAllStrategy(); + + // Makes sure that all nodes in the cluster are healthy. + waitStrategy = waitStrategy.withStrategy( + new HttpWaitStrategy() + .forPath("/pools/default") + .forPort(MGMT_PORT) + .withBasicCredentials(username, password) + .forStatusCode(200) + .forResponsePredicate(response -> { + try { + return Optional.of(MAPPER.readTree(response)) + .map(n -> n.at("/nodes/0/status")) + .map(JsonNode::asText) + .map("healthy"::equals) + .orElse(false); + } catch (IOException e) { + logger().error("Unable to parse response {}", response); + return false; + } + }) + ); + + if (enabledServices.contains(CouchbaseService.QUERY)) { + waitStrategy = waitStrategy.withStrategy( + new HttpWaitStrategy() + .forPath("/admin/ping") + .forPort(QUERY_PORT) + .withBasicCredentials(username, password) + .forStatusCode(200) + ); } - } - - private void stopCluster() { - getCouchbaseCluster().disconnect(); - getCouchbaseEnvironment().shutdown(); - } - - public CouchbaseContainer withNewBucket(BucketSettings bucketSettings) { - newBuckets.add(new BucketAndUserSettings(bucketSettings)); - return self(); - } - public CouchbaseContainer withNewBucket(BucketSettings bucketSettings, UserSettings userSettings) { - newBuckets.add(new BucketAndUserSettings(bucketSettings, userSettings)); - return self(); + waitingFor(waitStrategy); } - @SneakyThrows - public void initCluster() { - urlBase = String.format("http://%s:%s", getContainerIpAddress(), getMappedPort(REST)); - String poolURL = "/pools/default"; - String poolPayload = "memoryQuota=" + URLEncoder.encode(memoryQuota, "UTF-8") + "&indexMemoryQuota=" + URLEncoder.encode(indexMemoryQuota, "UTF-8"); - - String setupServicesURL = "/node/controller/setupServices"; - StringBuilder servicePayloadBuilder = new StringBuilder(); - if (keyValue) { - servicePayloadBuilder.append("kv,"); - } - if (query) { - servicePayloadBuilder.append("n1ql,"); - } - if (index) { - servicePayloadBuilder.append("index,"); - } - if (fts) { - servicePayloadBuilder.append("fts,"); + @Override + protected void containerIsStarting(final InspectContainerResponse containerInfo) { + logger().debug("Couchbase container is starting, performing configuration."); + + waitUntilNodeIsOnline(); + renameNode(); + initializeServices(); + configureAdminUser(); + configureExternalPorts(); + if (enabledServices.contains(CouchbaseService.INDEX)) { + configureIndexer(); } - String setupServiceContent = "services=" + URLEncoder.encode(servicePayloadBuilder.toString(), "UTF-8"); - - String webSettingsURL = "/settings/web"; - String webSettingsContent = "username=" + URLEncoder.encode(clusterUsername, "UTF-8") + "&password=" + URLEncoder.encode(clusterPassword, "UTF-8") + "&port=8091"; - - callCouchbaseRestAPI(poolURL, poolPayload); - callCouchbaseRestAPI(setupServicesURL, setupServiceContent); - callCouchbaseRestAPI(webSettingsURL, webSettingsContent); - - createNodeWaitStrategy().waitUntilReady(this); - callCouchbaseRestAPI("/settings/indexes", "indexerThreads=0&logLevel=info&maxRollbackPoints=5&storageMode=memory_optimized"); } - @NotNull - private HttpWaitStrategy createNodeWaitStrategy() { - return new HttpWaitStrategy() - .forPath("/pools/default/") - .withBasicCredentials(clusterUsername, clusterPassword) - .forStatusCode(HTTP_OK) - .forResponsePredicate(response -> { - try { - return Optional.of(MAPPER.readTree(response)) - .map(n -> n.at("/nodes/0/status")) - .map(JsonNode::asText) - .map("healthy"::equals) - .orElse(false); - } catch (IOException e) { - logger().error("Unable to parse response {}", response); - return false; - } - }); - } - - public void createBucket(BucketSettings bucketSetting, UserSettings userSettings, boolean primaryIndex) { - ClusterManager clusterManager = getCouchbaseCluster().clusterManager(clusterUsername, clusterPassword); - // Insert Bucket - BucketSettings bucketSettings = clusterManager.insertBucket(bucketSetting); - try { - // Insert Bucket user - clusterManager.upsertUser(AuthDomain.LOCAL, bucketSetting.name(), userSettings); - } catch (Exception e) { - logger().warn("Unable to insert user '" + bucketSetting.name() + "', maybe you are using older version"); - } - if (index) { - Bucket bucket = getCouchbaseCluster().openBucket(bucketSettings.name(), bucketSettings.password()); - new CouchbaseQueryServiceWaitStrategy(bucket).waitUntilReady(this); - if (primaryIndex) { - bucket.query(Index.createPrimaryIndex().on(bucketSetting.name())); + @Override + protected void containerIsStarted(InspectContainerResponse containerInfo) { + createBuckets(); + logger().info("Couchbase container is ready! UI available at http://{}:{}", getContainerIpAddress(), getMappedPort(MGMT_PORT)); + } + + /** + * Before we can start configuring the host, we need to wait until the cluster manager is listening. + */ + private void waitUntilNodeIsOnline() { + new HttpWaitStrategy() + .forPort(MGMT_PORT) + .forPath("/pools") + .forStatusCode(200) + .waitUntilReady(this); + } + + /** + * Rebinds/renames the internal hostname. + *

+ * To make sure the internal hostname is different from the external (alternate) address and the SDK can pick it + * up automatically, we bind the internal hostname to the internal IP address. + */ + private void renameNode() { + logger().debug("Renaming Couchbase Node from localhost to {}", getContainerIpAddress()); + + Response response = doHttpRequest(MGMT_PORT, "/node/controller/rename", "POST", new FormBody.Builder() + .add("hostname", getInternalIpAddress()) + .build(), false + ); + + checkSuccessfulResponse(response, "Could not rename couchbase node"); + } + + /** + * Initializes services based on the configured enabled services. + */ + private void initializeServices() { + logger().debug("Initializing couchbase services on host: {}", enabledServices); + + final String services = enabledServices.stream().map(s -> { + switch (s) { + case KV: return "kv"; + case QUERY: return "n1ql"; + case INDEX: return "index"; + case SEARCH: return "fts"; + default: throw new IllegalStateException("Unknown service!"); } + }).collect(Collectors.joining(",")); + + Response response = doHttpRequest(MGMT_PORT, "/node/controller/setupServices", "POST", new FormBody.Builder() + .add("services", services) + .build(), false + ); + + checkSuccessfulResponse(response, "Could not enable couchbase services"); + } + + /** + * Configures the admin user on the couchbase node. + *

+ * After this stage, all subsequent API calls need to have the basic auth header set. + */ + private void configureAdminUser() { + logger().debug("Configuring couchbase admin user with username: \"{}\"", username); + + Response response = doHttpRequest(MGMT_PORT, "/settings/web", "POST", new FormBody.Builder() + .add("username", username) + .add("password", password) + .add("port", Integer.toString(MGMT_PORT)) + .build(), false); + + checkSuccessfulResponse(response, "Could not configure couchbase admin user"); + } + + /** + * Configures the external ports for SDK access. + *

+ * Since the internal ports are not accessible from outside the container, this code configures the "external" + * hostname and services to align with the mapped ports. The SDK will pick it up and then automatically connect + * to those ports. Note that for all services non-ssl and ssl ports are configured. + */ + private void configureExternalPorts() { + logger().debug("Mapping external ports to the alternate address configuration"); + + final FormBody.Builder builder = new FormBody.Builder(); + builder.add("hostname", getContainerIpAddress()); + builder.add("mgmt", Integer.toString(getMappedPort(MGMT_PORT))); + builder.add("mgmtSSL", Integer.toString(getMappedPort(MGMT_SSL_PORT))); + + if (enabledServices.contains(CouchbaseService.KV)) { + builder.add("kv", Integer.toString(getMappedPort(KV_PORT))); + builder.add("kvSSL", Integer.toString(getMappedPort(KV_SSL_PORT))); + builder.add("capi", Integer.toString(getMappedPort(VIEW_PORT))); + builder.add("capiSSL", Integer.toString(getMappedPort(VIEW_SSL_PORT))); } - } - public void callCouchbaseRestAPI(String url, String payload) throws IOException { - String fullUrl = urlBase + url; - @Cleanup("disconnect") - HttpURLConnection httpConnection = (HttpURLConnection) ((new URL(fullUrl).openConnection())); - httpConnection.setDoOutput(true); - httpConnection.setRequestMethod("POST"); - httpConnection.setRequestProperty("Content-Type", - "application/x-www-form-urlencoded"); - String encoded = Base64.encode((clusterUsername + ":" + clusterPassword).getBytes("UTF-8")); - httpConnection.setRequestProperty("Authorization", "Basic " + encoded); - @Cleanup - DataOutputStream out = new DataOutputStream(httpConnection.getOutputStream()); - out.writeBytes(payload); - out.flush(); - httpConnection.getResponseCode(); - } + if (enabledServices.contains(CouchbaseService.QUERY)) { + builder.add("n1ql", Integer.toString(getMappedPort(QUERY_PORT))); + builder.add("n1qlSSL", Integer.toString(getMappedPort(QUERY_SSL_PORT))); + } - @Override - protected void containerIsCreated(String containerId) { - patchConfig(STATIC_CONFIG, this::addMappedPorts); - // capi needs a special configuration, see https://developer.couchbase.com/documentation/server/current/install/install-ports.html - patchConfig(CAPI_CONFIG, this::replaceCapiPort); - } + if (enabledServices.contains(CouchbaseService.SEARCH)) { + builder.add("fts", Integer.toString(getMappedPort(SEARCH_PORT))); + builder.add("ftsSSL", Integer.toString(getMappedPort(SEARCH_SSL_PORT))); + } - private void patchConfig(String configLocation, ThrowingFunction patchFunction) { - String patchedConfig = copyFileFromContainer(configLocation, - inputStream -> patchFunction.apply(IOUtils.toString(inputStream, StandardCharsets.UTF_8))); - copyFileToContainer(Transferable.of(patchedConfig.getBytes(StandardCharsets.UTF_8)), configLocation); - } + final Response response = doHttpRequest( + MGMT_PORT, + "/node/controller/setupAlternateAddresses/external", + "PUT", + builder.build(), + true + ); - private String addMappedPorts(String originalConfig) { - String portConfig = Stream.of(CouchbasePort.values()) - .filter(port -> !port.isDynamic()) - .map(port -> String.format("{%s, %d}.", port.name, getMappedPort(port))) - .collect(Collectors.joining("\n")); - return String.format("%s\n%s", originalConfig, portConfig); + checkSuccessfulResponse(response, "Could not configure external ports"); } - private String replaceCapiPort(String originalConfig) { - return Arrays.stream(originalConfig.split("\n")) - .map(s -> (s.matches("port\\s*=\\s*" + CAPI.getOriginalPort())) ? "port = " + getMappedPort(CAPI) : s) - .collect(Collectors.joining("\n")); - } + /** + * Configures the indexer service so that indexes can be created later on the bucket. + */ + private void configureIndexer() { + logger().debug("Configuring the indexer service"); - @Override - protected void containerIsStarted(InspectContainerResponse containerInfo) { - if (!newBuckets.isEmpty()) { - for (BucketAndUserSettings bucket : newBuckets) { - createBucket(bucket.getBucketSettings(), bucket.getUserSettings(), primaryIndex); - } - } - } + Response response = doHttpRequest(MGMT_PORT, "/settings/indexes", "POST", new FormBody.Builder() + .add("storageMode", "memory_optimized") + .build(), true + ); - private CouchbaseCluster createCouchbaseCluster() { - return CouchbaseCluster.create(getCouchbaseEnvironment(), getContainerIpAddress()); + checkSuccessfulResponse(response, "Could not configure the indexing service"); } - private DefaultCouchbaseEnvironment createCouchbaseEnvironment() { - initCluster(); - return DefaultCouchbaseEnvironment.builder() - .kvTimeout(10000) - .bootstrapCarrierDirectPort(getMappedPort(MEMCACHED)) - .bootstrapCarrierSslPort(getMappedPort(MEMCACHED_SSL)) - .bootstrapHttpDirectPort(getMappedPort(REST)) - .bootstrapHttpSslPort(getMappedPort(REST_SSL)) - .build(); - } + /** + * Based on the user-configured bucket definitions, creating buckets and corresponding indexes if needed. + */ + private void createBuckets() { + logger().debug("Creating " + buckets.size() + " buckets (and corresponding indexes)."); - public CouchbaseContainer withMemoryQuota(String memoryQuota) { - this.memoryQuota = memoryQuota; - return self(); - } + for (BucketDefinition bucket : buckets) { + logger().debug("Creating bucket \"" + bucket.getName() + "\""); - public CouchbaseContainer withIndexMemoryQuota(String indexMemoryQuota) { - this.indexMemoryQuota = indexMemoryQuota; - return self(); - } + Response response = doHttpRequest(MGMT_PORT, "/pools/default/buckets", "POST", new FormBody.Builder() + .add("name", bucket.getName()) + .add("ramQuotaMB", Integer.toString(bucket.getQuota())) + .build(), true); - public CouchbaseContainer withClusterAdmin(String username, String password) { - this.clusterUsername = username; - this.clusterPassword = password; - return self(); - } + checkSuccessfulResponse(response, "Could not create bucket " + bucket.getName()); - public CouchbaseContainer withKeyValue(boolean keyValue) { - this.keyValue = keyValue; - return self(); - } + new HttpWaitStrategy() + .forPath("/pools/default/buckets/" + bucket.getName()) + .forPort(MGMT_PORT) + .withBasicCredentials(username, password) + .forStatusCode(200) + .waitUntilReady(this); - public CouchbaseContainer withQuery(boolean query) { - this.query = query; - return self(); - } + if (bucket.hasPrimaryIndex()) { + if (enabledServices.contains(CouchbaseService.QUERY)) { + Response queryResponse = doHttpRequest(QUERY_PORT, "/query/service", "POST", new FormBody.Builder() + .add("statement", "CREATE PRIMARY INDEX on `" + bucket.getName() + "`") + .build(), true); - public CouchbaseContainer withIndex(boolean index) { - this.index = index; - return self(); + checkSuccessfulResponse(queryResponse, "Could not create primary index for bucket " + bucket.getName()); + } else { + logger().info("Primary index creation for bucket " + bucket.getName() + " ignored, since QUERY service is not present."); + } + } + } } - public CouchbaseContainer withPrimaryIndex(boolean primaryIndex) { - this.primaryIndex = primaryIndex; - return self(); + /** + * Helper method to extract the internal IP address based on the network configuration. + */ + private String getInternalIpAddress() { + return getContainerInfo().getNetworkSettings().getNetworks().values().stream() + .findFirst() + .map(ContainerNetwork::getIpAddress) + .orElseThrow(() -> new IllegalStateException("No network available to extract the internal IP from!")); } - public CouchbaseContainer withFts(boolean fts) { - this.fts = fts; - return self(); + /** + * Helper method to check if the response is successful and release the body if needed. + * + * @param response the response to check. + * @param message the message that should be part of the exception of not successful. + */ + private void checkSuccessfulResponse(final Response response, final String message) { + try { + if (!response.isSuccessful()) { + throw new IllegalStateException(message + ": " + response.toString()); + } + } finally { + if (response.body() != null) { + response.body().close(); + } + } } - @Getter - @RequiredArgsConstructor - protected enum CouchbasePort { - REST("rest_port", 8091, true), - CAPI("capi_port", 8092, false), - QUERY("query_port", 8093, false), - FTS("fts_http_port", 8094, false), - CBAS("cbas_http_port", 8095, false), - EVENTING("eventing_http_port", 8096, false), - MEMCACHED_SSL("memcached_ssl_port", 11207, false), - MEMCACHED("memcached_port", 11210, false), - REST_SSL("ssl_rest_port", 18091, true), - CAPI_SSL("ssl_capi_port", 18092, false), - QUERY_SSL("ssl_query_port", 18093, false), - FTS_SSL("fts_ssl_port", 18094, false), - CBAS_SSL("cbas_ssl_port", 18095, false), - EVENTING_SSL("eventing_ssl_port", 18096, false); - - final String name; - - final int originalPort; - - final boolean dynamic; + /** + * Checks if already running and if so raises an exception to prevent too-late setters. + */ + private void checkNotRunning() { + if (isRunning()) { + throw new IllegalStateException("Setter can only be called before the container is running"); + } } - @Value - @AllArgsConstructor - private class BucketAndUserSettings { + /** + * Helper method to perform a request against a couchbase server HTTP endpoint. + * + * @param port the (unmapped) original port that should be used. + * @param path the relative http path. + * @param method the http method to use. + * @param body if present, will be part of the payload. + * @param auth if authentication with the admin user and password should be used. + * @return the response of the request. + */ + private Response doHttpRequest(final int port, final String path, final String method, final RequestBody body, + final boolean auth) { + try { + Request.Builder requestBuilder = new Request.Builder() + .url("http://" + getContainerIpAddress() + ":" + getMappedPort(port) + path); - private final BucketSettings bucketSettings; - private final UserSettings userSettings; + if (auth) { + requestBuilder = requestBuilder.header("Authorization", Credentials.basic(username, password)); + } - public BucketAndUserSettings(final BucketSettings bucketSettings) { - this.bucketSettings = bucketSettings; - this.userSettings = UserSettings.build() - .password(bucketSettings.password()) - .roles(getDefaultAdminRoles(bucketSettings.name())); - } + if (body == null) { + requestBuilder = requestBuilder.get(); + } else { + requestBuilder = requestBuilder.method(method, body); + } - private List getDefaultAdminRoles(String bucketName) { - return Lists.newArrayList( - new UserRole("bucket_admin", bucketName), - new UserRole("views_admin", bucketName), - new UserRole("query_manage_index", bucketName), - new UserRole("query_update", bucketName), - new UserRole("query_select", bucketName), - new UserRole("query_insert", bucketName), - new UserRole("query_delete", bucketName) - ); + return HTTP_CLIENT.newCall(requestBuilder.build()).execute(); + } catch (Exception ex) { + throw new RuntimeException("Could not perform request against couchbase HTTP endpoint ", ex); } - } } diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseQueryServiceWaitStrategy.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseQueryServiceWaitStrategy.java deleted file mode 100644 index 853dd7d8eb7..00000000000 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseQueryServiceWaitStrategy.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.testcontainers.couchbase; - -import com.couchbase.client.core.message.cluster.GetClusterConfigRequest; -import com.couchbase.client.core.message.cluster.GetClusterConfigResponse; -import com.couchbase.client.core.service.ServiceType; -import com.couchbase.client.java.Bucket; -import lombok.extern.slf4j.Slf4j; -import org.rnorth.ducttape.TimeoutException; -import org.testcontainers.containers.ContainerLaunchException; -import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy; - -import java.time.Duration; -import java.util.concurrent.TimeUnit; - -import static org.rnorth.ducttape.unreliables.Unreliables.retryUntilSuccess; - -/** - * @author ctayeb - * Created on 06/06/2017 - */ -@Slf4j -public class CouchbaseQueryServiceWaitStrategy extends AbstractWaitStrategy { - - private final Bucket bucket; - - public CouchbaseQueryServiceWaitStrategy(Bucket bucket) { - this.bucket = bucket; - startupTimeout = Duration.ofSeconds(120); - } - - @Override - protected void waitUntilReady() { - log.info("Waiting for {} seconds for QUERY service", startupTimeout.getSeconds()); - - // try to connect to the URL - try { - retryUntilSuccess((int) startupTimeout.getSeconds(), TimeUnit.SECONDS, () -> { - getRateLimiter().doWhenReady(() -> { - GetClusterConfigResponse clusterConfig = bucket.core() - .send(new GetClusterConfigRequest()) - .toBlocking().single(); - boolean queryServiceEnabled = clusterConfig.config() - .bucketConfig(bucket.name()) - .serviceEnabled(ServiceType.QUERY); - if (!queryServiceEnabled) { - throw new ContainerLaunchException("Query service not ready yet"); - } - }); - return true; - }); - } catch (TimeoutException e) { - throw new ContainerLaunchException("Timed out waiting for QUERY service"); - } - } -} diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseService.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseService.java new file mode 100644 index 00000000000..d284c660709 --- /dev/null +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseService.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.testcontainers.couchbase; + +/** + * Describes the different services that should be exposed on the container. + */ +public enum CouchbaseService { + + /** + * Key-Value service. + */ + KV, + + /** + * Query (N1QL) service. + */ + QUERY, + + /** + * Search (FTS) service. + */ + SEARCH, + + /** + * Indexing service (needed if QUERY is also used!). + */ + INDEX + +} diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java deleted file mode 100644 index ef2f0925c48..00000000000 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/BaseCouchbaseContainerTest.java +++ /dev/null @@ -1,82 +0,0 @@ -package org.testcontainers.couchbase; - -import com.couchbase.client.java.document.RawJsonDocument; -import com.couchbase.client.java.query.N1qlQuery; -import com.couchbase.client.java.query.N1qlQueryResult; -import com.couchbase.client.java.query.N1qlQueryRow; -import com.couchbase.client.java.view.DefaultView; -import com.couchbase.client.java.view.DesignDocument; -import com.couchbase.client.java.view.View; -import com.google.common.collect.Lists; -import org.junit.Assert; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.RuleChain; -import org.junit.runner.Description; -import org.testcontainers.containers.FailureDetectingExternalResource; -import org.testcontainers.testsupport.Flaky; -import org.testcontainers.testsupport.FlakyTestJUnit4RetryRule; - -import java.util.List; - -public abstract class BaseCouchbaseContainerTest extends AbstractCouchbaseTest { - - private static final String VIEW_NAME = "testview"; - private static final String VIEW_FUNCTION = "function (doc, meta) {\n" + - " emit(meta.id, null);\n" + - "}"; - - - private static final String ID = "toto"; - - private static final String DOCUMENT = "{\"name\":\"toto\"}"; - - @Rule - public RuleChain ruleChain = RuleChain.emptyRuleChain() - .around(new FlakyTestJUnit4RetryRule()) - .around(new FailureDetectingExternalResource() { - @Override - protected void starting(Description description) { - getCouchbaseContainer().start(); - } - - @Override - protected void failed(Throwable e, Description description) { - getCouchbaseContainer().stop(); - } - }); - - @Test - @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1453", reviewDate = "2020-03-01") - public void shouldInsertDocument() { - RawJsonDocument expected = RawJsonDocument.create(ID, DOCUMENT); - getBucket().upsert(expected); - RawJsonDocument result = getBucket().get(ID, RawJsonDocument.class); - Assert.assertEquals(expected.content(), result.content()); - } - - @Test - @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1453", reviewDate = "2020-03-01") - public void shouldExecuteN1ql() { - getBucket().query(N1qlQuery.simple("INSERT INTO " + TEST_BUCKET + " (KEY, VALUE) VALUES ('" + ID + "', " + DOCUMENT + ")")); - - N1qlQueryResult query = getBucket().query(N1qlQuery.simple("SELECT * FROM " + TEST_BUCKET + " USE KEYS '" + ID + "'")); - Assert.assertTrue(query.parseSuccess()); - Assert.assertTrue(query.finalSuccess()); - List n1qlQueryRows = query.allRows(); - Assert.assertEquals(1, n1qlQueryRows.size()); - Assert.assertEquals(DOCUMENT, n1qlQueryRows.get(0).value().get(TEST_BUCKET).toString()); - } - - @Test - @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1453", reviewDate = "2020-03-01") - public void shouldCreateView() { - View view = DefaultView.create(VIEW_NAME, VIEW_FUNCTION); - DesignDocument document = DesignDocument.create(VIEW_NAME, Lists.newArrayList(view)); - getBucket().bucketManager().insertDesignDocument(document); - DesignDocument result = getBucket().bucketManager().getDesignDocument(VIEW_NAME); - Assert.assertEquals(1, result.views().size()); - View resultView = result.views().get(0); - Assert.assertEquals(VIEW_NAME, resultView.name()); - } -} diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase4_6Test.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase4_6Test.java deleted file mode 100644 index c43d3c981ba..00000000000 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase4_6Test.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.testcontainers.couchbase; - -import org.junit.Rule; - -public class Couchbase4_6Test extends BaseCouchbaseContainerTest { - - @Rule - public CouchbaseContainer container = initCouchbaseContainer("couchbase/server:4.6.5"); - - @Override - public CouchbaseContainer getCouchbaseContainer() { - return container; - } -} diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_1Test.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_1Test.java deleted file mode 100644 index 2c3dbf3c901..00000000000 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_1Test.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.testcontainers.couchbase; - -import org.junit.Rule; - -public class Couchbase5_1Test extends BaseCouchbaseContainerTest { - - @Rule - public CouchbaseContainer container = initCouchbaseContainer("couchbase/server:5.1.1"); - - @Override - public CouchbaseContainer getCouchbaseContainer() { - return container; - } -} diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_5Test.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_5Test.java deleted file mode 100644 index 47bc020ef03..00000000000 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/Couchbase5_5Test.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.testcontainers.couchbase; - -import org.junit.Rule; - -public class Couchbase5_5Test extends BaseCouchbaseContainerTest { - - @Rule - public CouchbaseContainer container = initCouchbaseContainer("couchbase/server:5.5.1"); - - @Override - public CouchbaseContainer getCouchbaseContainer() { - return container; - } -} diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseCommunity5_1Test.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseCommunity5_1Test.java deleted file mode 100644 index 30be62bc2bd..00000000000 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseCommunity5_1Test.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.testcontainers.couchbase; - -import java.util.Collections; - -import org.junit.ClassRule; - -import com.couchbase.client.java.cluster.UserRole; -import com.couchbase.client.java.cluster.UserSettings; -import org.junit.Rule; - -public class CouchbaseCommunity5_1Test extends BaseCouchbaseContainerTest { - - @Rule - public CouchbaseContainer container = initCouchbaseContainer(); - - private static CouchbaseContainer initCouchbaseContainer() { - CouchbaseContainer couchbaseContainer = new CouchbaseContainer("couchbase/server:community-5.1.1"); - - final UserSettings communityAdminUserSettings = - UserSettings.build() - .password(getDefaultBucketSettings().password()) - .roles(Collections.singletonList(new UserRole("admin"))); - - couchbaseContainer.withNewBucket(getDefaultBucketSettings(), communityAdminUserSettings); - return couchbaseContainer; - } - - @Override - public CouchbaseContainer getCouchbaseContainer() { - return container; - } -} diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java index 6e9afe96865..27ad0f6265a 100644 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java @@ -1,22 +1,78 @@ +/* + * Copyright (c) 2020 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.testcontainers.couchbase; -import org.junit.Assert; +import com.couchbase.client.java.Bucket; +import com.couchbase.client.java.Cluster; +import com.couchbase.client.java.CouchbaseCluster; +import com.couchbase.client.java.document.JsonDocument; +import com.couchbase.client.java.document.json.JsonObject; +import com.couchbase.client.java.env.CouchbaseEnvironment; +import com.couchbase.client.java.env.DefaultCouchbaseEnvironment; import org.junit.Test; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + public class CouchbaseContainerTest { @Test - public void shouldUseCorrectDockerImage() { - CouchbaseContainer couchbaseContainer = new CouchbaseContainer().withClusterAdmin("admin", "foobar"); + public void testBasicContainerUsage() { + // bucket_definition { + BucketDefinition bucketDefinition = new BucketDefinition("mybucket"); + // } - Assert.assertEquals(CouchbaseContainer.DOCKER_IMAGE_NAME + CouchbaseContainer.VERSION, - couchbaseContainer.getDockerImageName()); - } + try ( + // container_definition { + CouchbaseContainer container = new CouchbaseContainer() + .withBucket(bucketDefinition) + // } + ) { + container.start(); - @Test - public void shouldStopWithoutThrowingException() { - CouchbaseContainer couchbaseContainer = new CouchbaseContainer(); - couchbaseContainer.start(); - couchbaseContainer.stop(); + // cluster_creation { + CouchbaseEnvironment environment = DefaultCouchbaseEnvironment + .builder() + .bootstrapCarrierDirectPort(container.getBootstrapCarrierDirectPort()) + .bootstrapHttpDirectPort(container.getBootstrapHttpDirectPort()) + .build(); + + Cluster cluster = CouchbaseCluster.create( + environment, + container.getContainerIpAddress() + ); + // } + + try { + // auth { + cluster.authenticate(container.getUsername(), container.getPassword()); + // } + + Bucket bucket = cluster.openBucket(bucketDefinition.getName()); + + bucket.upsert(JsonDocument.create("foo", JsonObject.empty())); + + assertTrue(bucket.exists("foo")); + assertNotNull(cluster.clusterManager().getBucket(bucketDefinition.getName())); + } finally { + cluster.disconnect(); + environment.shutdown(); + } + } } + } From 34b8d7e03de1fafb50a4c3b37388ae4d40059736 Mon Sep 17 00:00:00 2001 From: Stefan Rempfer Date: Sun, 12 Apr 2020 20:32:41 +0200 Subject: [PATCH 1239/5615] Using LogMessageWaitStrategy for VncRecordingContainer (#2063) (#2547) Co-authored-by: Stefan Rempfer Co-authored-by: Richard North Co-authored-by: Sergei Egorov --- .../containers/VncRecordingContainer.java | 47 +++---------------- 1 file changed, 7 insertions(+), 40 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java b/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java index 298593a116b..fd48dd01f92 100644 --- a/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java +++ b/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java @@ -1,24 +1,21 @@ package org.testcontainers.containers; -import com.github.dockerjava.api.model.Frame; import lombok.Getter; import lombok.NonNull; import lombok.SneakyThrows; import lombok.ToString; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; -import org.rnorth.ducttape.TimeoutException; -import org.rnorth.ducttape.unreliables.Unreliables; -import org.testcontainers.containers.output.FrameConsumerResultCallback; +import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.utility.TestcontainersConfiguration; -import java.io.Closeable; import java.io.File; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.StandardCopyOption; +import java.time.Duration; import java.util.Base64; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; + +import static java.time.temporal.ChronoUnit.SECONDS; /** * 'Sidekick container' with the sole purpose of recording the VNC screen output from another container. @@ -59,39 +56,9 @@ public VncRecordingContainer(@NonNull Network network, @NonNull String targetNet this.targetNetworkAlias = targetNetworkAlias; withNetwork(network); - - waitingFor(new AbstractWaitStrategy() { - - @Override - protected void waitUntilReady() { - try { - Unreliables.retryUntilTrue((int) startupTimeout.toMillis(), TimeUnit.MILLISECONDS, () -> { - CountDownLatch latch = new CountDownLatch(1); - - FrameConsumerResultCallback callback = new FrameConsumerResultCallback() { - @Override - public void onNext(Frame frame) { - if (frame != null && new String(frame.getPayload()).contains("Connected")) { - latch.countDown(); - } - } - }; - - try ( - Closeable __ = dockerClient.logContainerCmd(getContainerId()) - .withFollowStream(true) - .withSince(0) - .withStdErr(true) - .exec(callback) - ) { - return latch.await(1, TimeUnit.SECONDS); - } - }); - } catch (TimeoutException e) { - throw new ContainerLaunchException("Timed out waiting for log output", e); - } - } - }); + waitingFor(new LogMessageWaitStrategy() + .withRegEx(".*Connected.*") + .withStartupTimeout(Duration.of(15, SECONDS))); } public VncRecordingContainer withVncPassword(@NonNull String vncPassword) { From 95c828d01eaa8532a4878a89587d0116e413ebd4 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 12 Apr 2020 20:41:30 +0200 Subject: [PATCH 1240/5615] Include image name in RemoteDockerImage#toString (#2558) * clean up imports in GenericContainer * include image name in RemoteDockerImage.toString() to fix #2443 * test RemoteDockerImage directly * handle failures getting the image name * use completeExceptionally instead of mocking * tweak assertions * more assertion tweaks * add isDone check to RemoteDockerImage.toString -- still figuring out why test fails * go back to using lombok @ToString, exclude dockerClient while we're at it * use `Futures.lazyTransform` that also proxies `isDone` * `imageNameToString` should not be public * Include the message on error Co-authored-by: Byron David Co-authored-by: David Byron --- .../images/RemoteDockerImage.java | 30 ++++++++---- .../containers/GenericContainerTest.java | 1 - .../images/RemoteDockerImageTest.java | 47 +++++++++++++++++++ 3 files changed, 69 insertions(+), 9 deletions(-) create mode 100644 core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java diff --git a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java index 4975f536384..d0e1313edf6 100644 --- a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java +++ b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java @@ -3,6 +3,7 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.exception.DockerClientException; import com.github.dockerjava.api.exception.InternalServerErrorException; +import com.google.common.util.concurrent.Futures; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.NonNull; @@ -28,11 +29,13 @@ public class RemoteDockerImage extends LazyFuture { private static final Duration PULL_RETRY_TIME_LIMIT = Duration.ofMinutes(2); + @ToString.Exclude private Future imageNameFuture; @Wither private ImagePullPolicy imagePullPolicy = PullPolicy.defaultPolicy(); + @ToString.Exclude private DockerClient dockerClient = DockerClientFactory.lazyClient(); public RemoteDockerImage(String dockerImageName) { @@ -44,19 +47,13 @@ public RemoteDockerImage(@NonNull String repository, @NonNull String tag) { } public RemoteDockerImage(@NonNull Future imageFuture) { - this.imageNameFuture = new LazyFuture() { - @Override - @SneakyThrows({InterruptedException.class, ExecutionException.class}) - protected DockerImageName resolve() { - return new DockerImageName(imageFuture.get()); - } - }; + this.imageNameFuture = Futures.lazyTransform(imageFuture, DockerImageName::new); } @Override @SneakyThrows({InterruptedException.class, ExecutionException.class}) protected final String resolve() { - final DockerImageName imageName = imageNameFuture.get(); + final DockerImageName imageName = getImageName(); Logger logger = DockerLoggerFactory.getLogger(imageName.toString()); try { if (!imagePullPolicy.shouldPull(imageName)) { @@ -95,4 +92,21 @@ protected final String resolve() { throw new ContainerFetchException("Failed to get Docker client for " + imageName, e); } } + + private DockerImageName getImageName() throws InterruptedException, ExecutionException { + return imageNameFuture.get(); + } + + @ToString.Include(name = "imageName", rank = 1) + private String imageNameToString() { + if (!imageNameFuture.isDone()) { + return ""; + } + + try { + return getImageName().toString(); + } catch (InterruptedException | ExecutionException e) { + return e.getMessage(); + } + } } diff --git a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java index c29f6f37c36..78a7a3b1bef 100644 --- a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java +++ b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java @@ -2,7 +2,6 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.InspectContainerResponse.ContainerState; -import com.github.dockerjava.api.model.HostConfig; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.experimental.FieldDefaults; diff --git a/core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java b/core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java new file mode 100644 index 00000000000..c62f15653b3 --- /dev/null +++ b/core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java @@ -0,0 +1,47 @@ +package org.testcontainers.images; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; + +import org.junit.Test; +import org.testcontainers.utility.Base58; + +import java.util.concurrent.CompletableFuture; + +public class RemoteDockerImageTest { + + @Test + public void toStringContainsOnlyImageName() { + String imageName = Base58.randomString(8).toLowerCase(); + RemoteDockerImage remoteDockerImage = new RemoteDockerImage(imageName); + assertThat(remoteDockerImage.toString(), containsString("imageName=" + imageName)); + } + + @Test + public void toStringWithExceptionContainsOnlyImageNameFuture() { + CompletableFuture imageNameFuture = new CompletableFuture<>(); + imageNameFuture.completeExceptionally(new RuntimeException("arbitrary")); + + RemoteDockerImage remoteDockerImage = new RemoteDockerImage(imageNameFuture); + assertThat(remoteDockerImage.toString(), containsString("imageName=java.lang.RuntimeException: arbitrary")); + } + + @Test(timeout=5000L) + public void toStringDoesntResolveImageNameFuture() { + CompletableFuture imageNameFuture = new CompletableFuture<>(); + + // verify that we've set up the test properly + assertFalse(imageNameFuture.isDone()); + + RemoteDockerImage remoteDockerImage = new RemoteDockerImage(imageNameFuture); + assertThat(remoteDockerImage.toString(), containsString("imageName=")); + + // Make sure the act of calling toString doesn't resolve the imageNameFuture + assertFalse(imageNameFuture.isDone()); + + String imageName = Base58.randomString(8).toLowerCase(); + imageNameFuture.complete(imageName); + assertThat(remoteDockerImage.toString(), containsString("imageName=" + imageName)); + } +} From 6adb23b5ab860280fc6d3ffbc3186709d4c848cc Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 13 Apr 2020 16:54:30 +0200 Subject: [PATCH 1241/5615] Fix Spock's `TestLifecycleAware` integration (#2563) * Fix Spock's `TestLifecycleAware` integration * Small cleanups Co-authored-by: Kevin Wittek --- .../TestcontainersMethodInterceptor.groovy | 31 ++++--- .../spock/BrowserVncRecordingIT.groovy | 88 ------------------- .../TestLifecycleAwareContainerMock.java | 42 +++++++++ .../spock/TestLifecycleAwareIT.groovy | 55 ++++++++++++ 4 files changed, 112 insertions(+), 104 deletions(-) delete mode 100644 modules/spock/src/test/groovy/org/testcontainers/spock/BrowserVncRecordingIT.groovy create mode 100644 modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareContainerMock.java create mode 100644 modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareIT.groovy diff --git a/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersMethodInterceptor.groovy b/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersMethodInterceptor.groovy index a70ec0d6e53..16be002aeb1 100644 --- a/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersMethodInterceptor.groovy +++ b/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersMethodInterceptor.groovy @@ -30,6 +30,7 @@ class TestcontainersMethodInterceptor extends AbstractMethodInterceptor { invocation.proceed() } + @Override void interceptCleanupSpecMethod(IMethodInvocation invocation) throws Throwable { def containers = findAllContainers(true) stopContainers(containers, invocation) @@ -54,13 +55,6 @@ class TestcontainersMethodInterceptor extends AbstractMethodInterceptor { @Override void interceptCleanupMethod(IMethodInvocation invocation) throws Throwable { - findAllTestLifecycleAwareContainers(invocation).each { - // we assume first error is the one we want - def maybeException = Optional.ofNullable(errorListener.errors[0]?.exception) - def testDescription = SpockTestDescription.fromTestDescription(invocation) - it.afterTest(testDescription, maybeException) - } - def containers = findAllContainers(false) stopContainers(containers, invocation) @@ -76,14 +70,6 @@ class TestcontainersMethodInterceptor extends AbstractMethodInterceptor { } } - private List findAllTestLifecycleAwareContainers(IMethodInvocation invocation) { - spec.allFields.findAll { FieldInfo f -> - TestLifecycleAware.isAssignableFrom(f.type) - }.collect { - it.readValue(invocation.instance) as TestLifecycleAware - } - } - private List findAllComposeContainers(boolean shared) { spec.allFields.findAll { FieldInfo f -> DockerComposeContainer.isAssignableFrom(f.type) && f.shared == shared @@ -96,12 +82,25 @@ class TestcontainersMethodInterceptor extends AbstractMethodInterceptor { if(!container.isRunning()){ container.start() } + + if (container instanceof TestLifecycleAware) { + def testDescription = SpockTestDescription.fromTestDescription(invocation) + (container as TestLifecycleAware).beforeTest(testDescription) + } } } - private static void stopContainers(List containers, IMethodInvocation invocation) { + private void stopContainers(List containers, IMethodInvocation invocation) { containers.each { FieldInfo f -> GenericContainer container = readContainerFromField(f, invocation) + + if (container instanceof TestLifecycleAware) { + // we assume first error is the one we want + def maybeException = Optional.ofNullable(errorListener.errors[0]?.exception) + def testDescription = SpockTestDescription.fromTestDescription(invocation) + (container as TestLifecycleAware).afterTest(testDescription, maybeException) + } + container.stop() } } diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/BrowserVncRecordingIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/BrowserVncRecordingIT.groovy deleted file mode 100644 index 794032a58c4..00000000000 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/BrowserVncRecordingIT.groovy +++ /dev/null @@ -1,88 +0,0 @@ -package org.testcontainers.spock - -import org.intellij.lang.annotations.Language -import org.junit.Rule -import org.junit.rules.TemporaryFolder -import spock.lang.Specification -import spock.lang.Unroll -import spock.util.EmbeddedSpecRunner - -import static org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode.RECORD_ALL -import static org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode.RECORD_FAILING - -class BrowserVncRecordingIT extends Specification { - - @Rule - TemporaryFolder temp - - String recordingDir - - def setup() { - recordingDir = temp.getRoot().getAbsolutePath() - } - - @Unroll("For recording mode #recordingMode and failing test is #fails records video file named '#videoFileName'") - def "retains all recordings for RECORD_ALL if successful"() { - given: - - //noinspection GrPackage - @Language("groovy") - String myTest = """ -package org.testcontainers.spock - -import org.openqa.selenium.chrome.ChromeOptions -import org.openqa.selenium.remote.RemoteWebDriver -import org.testcontainers.containers.BrowserWebDriverContainer -import spock.lang.Specification - -import java.util.concurrent.TimeUnit - -import static org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode - -@Testcontainers -class BrowserWebdriverContainerIT extends Specification { - - BrowserWebDriverContainer browserContainer = new BrowserWebDriverContainer() - .withCapabilities(new ChromeOptions()) - .withRecordingMode("$recordingMode" as VncRecordingMode, new File("$recordingDir")) - - RemoteWebDriver driver - - def setup() { - driver = browserContainer.getWebDriver() - driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS) - } - - def "should record"() { - when: - driver.get("http://en.wikipedia.org/wiki/Randomness") - - then: - driver.findElementByPartialLinkText("pattern").isDisplayed() == !$fails - } - -} - - -""" - - when: - EmbeddedSpecRunner runner = new EmbeddedSpecRunner(throwFailure: false) - runner.run(myTest) - - then: - def videoDir = temp.getRoot().list() - if (videoFileName.isEmpty()) { - videoDir.length == 0 - } else { - videoDir.find { it.contains(videoFileName) } - } - - where: - recordingMode | fails | videoFileName - RECORD_ALL | false | 'BrowserWebdriverContainerIT-should+record' - RECORD_FAILING | false | '' - RECORD_FAILING | true | 'BrowserWebdriverContainerIT-should+record' - } - -} diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareContainerMock.java b/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareContainerMock.java new file mode 100644 index 00000000000..43be6d32ca4 --- /dev/null +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareContainerMock.java @@ -0,0 +1,42 @@ +package org.testcontainers.spock; + +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.lifecycle.TestDescription; +import org.testcontainers.lifecycle.TestLifecycleAware; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class TestLifecycleAwareContainerMock extends GenericContainer implements TestLifecycleAware { + + static final String BEFORE_TEST = "beforeTest"; + static final String AFTER_TEST = "afterTest"; + + final List lifecycleMethodCalls = new ArrayList<>(); + final List lifecycleFilesystemFriendlyNames = new ArrayList<>(); + + Throwable capturedThrowable; + + @Override + public void beforeTest(TestDescription description) { + lifecycleMethodCalls.add(BEFORE_TEST); + lifecycleFilesystemFriendlyNames.add(description.getFilesystemFriendlyName()); + } + + @Override + public void afterTest(TestDescription description, Optional throwable) { + lifecycleMethodCalls.add(AFTER_TEST); + throwable.ifPresent(capturedThrowable -> this.capturedThrowable = capturedThrowable); + } + + @Override + public void start() { + // Do nothing + } + + @Override + public void stop() { + // Do nothing + } +} diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareIT.groovy new file mode 100644 index 00000000000..18f7517ea3f --- /dev/null +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareIT.groovy @@ -0,0 +1,55 @@ +package org.testcontainers.spock + +import org.intellij.lang.annotations.Language +import spock.lang.Specification +import spock.lang.Unroll +import spock.util.EmbeddedSpecRunner + +class TestLifecycleAwareIT extends Specification { + + @Unroll("When failing test is #fails, afterTest receives '#filesystemFriendlyNames' and throwable starting with message '#errorMessageStartsWith'") + def "lifecycle awareness"() { + given: + + @Language("groovy") + String myTest = """ +import org.testcontainers.spock.Testcontainers +import org.testcontainers.containers.GenericContainer +import spock.lang.Specification + +@Testcontainers +class TestLifecycleAwareIT extends Specification { + + GenericContainer container = System.properties["org.testcontainers.container"] as GenericContainer + + def "perform test"() { + expect: + !System.properties["org.testcontainers.shouldFail"] + } +} +""" + and: + def container = new TestLifecycleAwareContainerMock() + System.properties["org.testcontainers.container"] = container + System.properties["org.testcontainers.shouldFail"] = fails + + when: "executing the test" + def runner = new EmbeddedSpecRunner(throwFailure: false) + runner.run(myTest) + + then: "mock container received lifecycle calls as expected" + container.lifecycleMethodCalls == ["beforeTest", "afterTest"] + container.lifecycleFilesystemFriendlyNames.join(",") == filesystemFriendlyNames + if (errorMessageStartsWith) { + assert container.capturedThrowable.message.startsWith(errorMessageStartsWith) + } else { + assert container.capturedThrowable == null + } + + where: + fails | filesystemFriendlyNames | errorMessageStartsWith + false | 'TestLifecycleAwareIT-perform+test' | null + true | 'TestLifecycleAwareIT-perform+test' | "Condition not satisfied:" + } + +} From d33f6475a9792b62c772d8f38a8c7a78ff1edf69 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Mon, 13 Apr 2020 17:39:01 +0200 Subject: [PATCH 1242/5615] Make default file recording directory of BrowserWebDriverContainer platform independent (#2562) Co-authored-by: Sergei Egorov --- .../containers/BrowserWebDriverContainer.java | 13 +++++++++++- ...ChromeRecordingWebDriverContainerTest.java | 20 ++++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index 9e1a2733a2e..22a4b9d47a5 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -28,8 +28,10 @@ import org.testcontainers.lifecycle.TestLifecycleAware; import java.io.File; +import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; +import java.nio.file.Files; import java.time.Duration; import java.util.Optional; import java.util.Set; @@ -52,6 +54,7 @@ public class BrowserWebDriverContainer Date: Mon, 13 Apr 2020 20:51:04 +0100 Subject: [PATCH 1243/5615] Bump tomcat-jdbc from 9.0.33 to 9.0.34 in /modules/mysql (#2542) Bumps tomcat-jdbc from 9.0.33 to 9.0.34. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 10fd90b3c38..606aeefe813 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -11,7 +11,7 @@ dependencies { testCompile 'mysql:mysql-connector-java:8.0.19' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.33' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.34' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.vibur:vibur-dbcp:25.0' From fbda419042d5de2b223b33a018269c714eefa78e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2020 20:51:20 +0100 Subject: [PATCH 1244/5615] =?UTF-8?q?Bump=20junit-jupiter-engine=20from=20?= =?UTF-8?q?5.6.0=20to=205.6.2=20in=20/modules/junit=E2=80=A6=20(#2553)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [junit-jupiter-engine](https://github.com/junit-team/junit5) from 5.6.0 to 5.6.2. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.6.0...r5.6.2) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index bf52b25913c..f7cbb476260 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -17,7 +17,7 @@ dependencies { testRuntime 'org.postgresql:postgresql:42.2.12' testRuntime 'mysql:mysql-connector-java:8.0.19' - testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.6.0' + testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.6.2' } test { From f18775801e71f32f1c962dab0e2435ca4727f9fc Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2020 20:51:31 +0100 Subject: [PATCH 1245/5615] =?UTF-8?q?Bump=20aws-java-sdk-logs=20from=201.1?= =?UTF-8?q?1.759=20to=201.11.762=20in=20/modules/lo=E2=80=A6=20(#2541)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.11.759 to 1.11.762. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.759...1.11.762) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 1e9e3cd9121..c8bf561b6d1 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,5 +6,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.683' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.683' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.636' - testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.759' + testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.762' } From f822865e5738535f9ce6ed502dac679337a141ca Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2020 20:51:38 +0100 Subject: [PATCH 1246/5615] =?UTF-8?q?Bump=20r2dbc-postgresql=20from=200.8.?= =?UTF-8?q?1.RELEASE=20to=200.8.2.RELEASE=20in=20/m=E2=80=A6=20(#2561)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [r2dbc-postgresql](https://github.com/r2dbc/r2dbc-postgresql) from 0.8.1.RELEASE to 0.8.2.RELEASE. - [Release notes](https://github.com/r2dbc/r2dbc-postgresql/releases) - [Changelog](https://github.com/r2dbc/r2dbc-postgresql/blob/v0.8.2.RELEASE/CHANGELOG) - [Commits](https://github.com/r2dbc/r2dbc-postgresql/compare/v0.8.1.RELEASE...v0.8.2.RELEASE) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index c629c1c5a13..9893ec28bd1 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -6,7 +6,7 @@ dependencies { compile project(':jdbc') compileOnly project(':r2dbc') - compileOnly 'io.r2dbc:r2dbc-postgresql:0.8.1.RELEASE' + compileOnly 'io.r2dbc:r2dbc-postgresql:0.8.2.RELEASE' testCompile 'org.postgresql:postgresql:42.2.12' testCompile 'commons-dbutils:commons-dbutils:1.7' From a988cf8400408273d617c9c3bf259ff18b5746d4 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2020 20:51:49 +0100 Subject: [PATCH 1247/5615] Bump tomcat-jdbc from 9.0.33 to 9.0.34 in /modules/mariadb (#2543) Bumps tomcat-jdbc from 9.0.33 to 9.0.34. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 93b2de2a716..730dae7c794 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -12,7 +12,7 @@ dependencies { testCompile 'org.mariadb.jdbc:mariadb-java-client:2.6.0' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.33' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.34' testCompile 'org.vibur:vibur-dbcp:25.0' testCompile testFixtures(project(':r2dbc')) From 3bf78155ce7006dce40123df2d610016e34d0c2a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2020 20:51:54 +0100 Subject: [PATCH 1248/5615] Bump cucumber-java from 5.5.0 to 5.6.0 in /examples (#2527) Bumps [cucumber-java](https://github.com/cucumber/cucumber-jvm) from 5.5.0 to 5.6.0. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/master/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v5.5.0...v5.6.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 52c59229350..6827bb7c683 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' testImplementation 'io.cucumber:cucumber-junit:5.5.0' - testImplementation 'io.cucumber:cucumber-java:5.5.0' + testImplementation 'io.cucumber:cucumber-java:5.6.0' testImplementation 'org.testcontainers:selenium' } From ba21115757507d6347548065afa52226f8eb9c0e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2020 20:52:25 +0100 Subject: [PATCH 1249/5615] =?UTF-8?q?Bump=20junit-jupiter-api=20from=205.6?= =?UTF-8?q?.0=20to=205.6.2=20in=20/modules/junit-ju=E2=80=A6=20(#2554)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [junit-jupiter-api](https://github.com/junit-team/junit5) from 5.6.0 to 5.6.2. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.6.0...r5.6.2) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index f7cbb476260..950302715b5 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -2,7 +2,7 @@ description = "Testcontainers :: JUnit Jupiter Extension" dependencies { compile project(':testcontainers') - compile 'org.junit.jupiter:junit-jupiter-api:5.6.0' + compile 'org.junit.jupiter:junit-jupiter-api:5.6.2' testCompile project(':mysql') testCompile project(':postgresql') From 83da2e25a9348a1ed7b321ac82d931bafdf90b8a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2020 20:52:42 +0100 Subject: [PATCH 1250/5615] =?UTF-8?q?Bump=20r2dbc-mssql=20from=200.8.1.REL?= =?UTF-8?q?EASE=20to=200.8.2.RELEASE=20in=20/module=E2=80=A6=20(#2559)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [r2dbc-mssql](https://github.com/r2dbc/r2dbc-mssql) from 0.8.1.RELEASE to 0.8.2.RELEASE. - [Release notes](https://github.com/r2dbc/r2dbc-mssql/releases) - [Changelog](https://github.com/r2dbc/r2dbc-mssql/blob/v0.8.2.RELEASE/CHANGELOG) - [Commits](https://github.com/r2dbc/r2dbc-mssql/compare/v0.8.1.RELEASE...v0.8.2.RELEASE) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/mssqlserver/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index 3c44df64eda..4a496e9eb35 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -7,7 +7,7 @@ dependencies { compile project(':jdbc') compileOnly project(':r2dbc') - compileOnly 'io.r2dbc:r2dbc-mssql:0.8.1.RELEASE' + compileOnly 'io.r2dbc:r2dbc-mssql:0.8.2.RELEASE' testCompile project(':r2dbc') testCompile 'io.r2dbc:r2dbc-mssql:0.8.1.RELEASE' From 558988c1cf86976795cd28eacc9c6417bba6ea99 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2020 20:52:54 +0100 Subject: [PATCH 1251/5615] Bump java-client from 2.7.13 to 2.7.14 in /modules/couchbase (#2540) Bumps [java-client](https://github.com/couchbase/couchbase-java-client) from 2.7.13 to 2.7.14. - [Release notes](https://github.com/couchbase/couchbase-java-client/releases) - [Commits](https://github.com/couchbase/couchbase-java-client/compare/2.7.13...2.7.14) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 095f8828aac..9dd0856fd51 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: Couchbase" dependencies { compile project(':testcontainers') - testCompile 'com.couchbase.client:java-client:2.7.13' + testCompile 'com.couchbase.client:java-client:2.7.14' } From 3645252cf7de3021abdf7bc1c7e86d22fad69551 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2020 20:53:13 +0100 Subject: [PATCH 1252/5615] =?UTF-8?q?Bump=20org.springframework.boot=20fro?= =?UTF-8?q?m=202.2.5.RELEASE=20to=202.2.6.RELEA=E2=80=A6=20(#2530)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [org.springframework.boot](https://github.com/spring-projects/spring-boot) from 2.2.5.RELEASE to 2.2.6.RELEASE. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.2.5.RELEASE...v2.2.6.RELEASE) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> Co-authored-by: Richard North --- examples/selenium-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index c9bed8d1467..edbe7d1fb68 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.2.5.RELEASE' + id 'org.springframework.boot' version '2.2.6.RELEASE' } apply plugin: 'io.spring.dependency-management' From 780359762c05c7b4153337686a3a6805bef3c864 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2020 20:53:45 +0100 Subject: [PATCH 1253/5615] Bump cucumber-junit from 5.5.0 to 5.6.0 in /examples (#2529) Bumps [cucumber-junit](https://github.com/cucumber/cucumber-jvm) from 5.5.0 to 5.6.0. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/master/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v5.5.0...v5.6.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> Co-authored-by: Richard North --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 6827bb7c683..53a50c374f5 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' - testImplementation 'io.cucumber:cucumber-junit:5.5.0' + testImplementation 'io.cucumber:cucumber-junit:5.6.0' testImplementation 'io.cucumber:cucumber-java:5.6.0' testImplementation 'org.testcontainers:selenium' } From b5da9140f3c910e38c474b8614d23ea9308afb32 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 14 Apr 2020 11:00:53 +0100 Subject: [PATCH 1254/5615] Upgrade Gradle to 6.3 (#2551) For compatibility with recent JDKs --- build.gradle | 6 ----- gradle/wrapper/gradle-wrapper.properties | 5 ++-- gradlew | 29 ++++++++++-------------- settings.gradle | 8 +++++++ 4 files changed, 23 insertions(+), 25 deletions(-) diff --git a/build.gradle b/build.gradle index 4fbaf42c160..d9d374cb511 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,4 @@ plugins { - id 'com.gradle.build-scan' version '2.4.2' id 'io.franzbecker.gradle-lombok' version '3.1.0' id 'nebula.provided-base' version '3.0.3' id 'com.github.johnrengelman.shadow' version '5.1.0' @@ -133,8 +132,3 @@ subprojects { testCompile 'ch.qos.logback:logback-classic:1.2.3' } } - -buildScan { - termsOfServiceUrl = 'https://gradle.com/terms-of-service' - termsOfServiceAgree = 'yes' -} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ca9d62814a2..6fd47cc3de5 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Sun Apr 05 19:28:23 BST 2020 +distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip -zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 83f2acfdc31..2fe81a7d95e 100755 --- a/gradlew +++ b/gradlew @@ -154,19 +154,19 @@ if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -175,14 +175,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@" diff --git a/settings.gradle b/settings.gradle index e5432e08061..5ec35718ef6 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,6 +6,7 @@ buildscript { } dependencies { classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.9.0" + classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.2.1" } configurations.classpath { @@ -16,6 +17,7 @@ buildscript { } apply plugin: 'ch.myniva.s3-build-cache' +apply plugin: 'com.gradle.enterprise' ext.isMasterBuild = (System.getenv("CIRCLE_BRANCH") ?: System.getenv("BUILD_SOURCEBRANCHNAME")) == "master" @@ -55,3 +57,9 @@ include ':docs:examples:spock:redis' include 'test-support' +gradleEnterprise { + buildScan { + termsOfServiceUrl = 'https://gradle.com/terms-of-service' + termsOfServiceAgree = 'yes' + } +} From 34c7aef429d0debf9c5b7268c210536089c71a53 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 14 Apr 2020 11:01:46 +0100 Subject: [PATCH 1255/5615] =?UTF-8?q?Bump=20junit-jupiter-params=20from=20?= =?UTF-8?q?5.6.0=20to=205.6.2=20in=20/modules/junit=E2=80=A6=20(#2571)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [junit-jupiter-params](https://github.com/junit-team/junit5) from 5.6.0 to 5.6.2. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.6.0...r5.6.2) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 950302715b5..c1b713cdd86 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -12,7 +12,7 @@ dependencies { testCompile ('org.mockito:mockito-core:3.3.3') { exclude(module: 'hamcrest-core') } - testCompile 'org.junit.jupiter:junit-jupiter-params:5.6.0' + testCompile 'org.junit.jupiter:junit-jupiter-params:5.6.2' testCompile 'org.assertj:assertj-core:3.14.0' testRuntime 'org.postgresql:postgresql:42.2.12' From c9a0b1f0b1d7f389bacdf8b89407968f45a73b65 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 14 Apr 2020 11:02:27 +0100 Subject: [PATCH 1256/5615] =?UTF-8?q?Bump=20assertj-core=20from=203.14.0?= =?UTF-8?q?=20to=203.15.0=20in=20/modules/junit-jupit=E2=80=A6=20(#2570)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.14.0 to 3.15.0. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.14.0...assertj-core-3.15.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index c1b713cdd86..86d9b30930e 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -12,8 +12,8 @@ dependencies { testCompile ('org.mockito:mockito-core:3.3.3') { exclude(module: 'hamcrest-core') } + testCompile 'org.assertj:assertj-core:3.15.0' testCompile 'org.junit.jupiter:junit-jupiter-params:5.6.2' - testCompile 'org.assertj:assertj-core:3.14.0' testRuntime 'org.postgresql:postgresql:42.2.12' testRuntime 'mysql:mysql-connector-java:8.0.19' From 207d62df010109b93f632408c7b6b0f7fb5057fa Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 15 Apr 2020 15:03:55 +0200 Subject: [PATCH 1257/5615] Explicitly set ciMate's projectId in CircleCI (#2572) --- .circleci/config.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index c4302911f40..67e6546e220 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -12,6 +12,8 @@ jobs: - run: &ciMate name: aggregate test reports with ciMate when: always + environment: + CIMATE_PROJECT_ID: 2348n4vl command: | wget -q https://get.cimate.io/release/linux/cimate chmod +x cimate From c3d37e4195470b7bace41d1556df7a50a6c56ea8 Mon Sep 17 00:00:00 2001 From: Stefan Rempfer Date: Thu, 16 Apr 2020 15:04:21 +0200 Subject: [PATCH 1258/5615] Regard specified VNC recording directory for BrowserWebDriverContainer again (#2574) Co-authored-by: Stefan Rempfer --- .../containers/BrowserWebDriverContainer.java | 15 ++++---- ...ChromeRecordingWebDriverContainerTest.java | 36 +++++++++++++++---- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index 22a4b9d47a5..49218dfb990 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -143,12 +143,15 @@ protected void configure() { } if (recordingMode != VncRecordingMode.SKIP) { - try { - vncRecordingDirectory = Files.createTempDirectory(TC_TEMP_DIR_PREFIX).toFile(); - } catch (IOException e) { - // should never happen as per javadoc, since we use valid prefix - logger().error("Exception while trying to create temp directory " + vncRecordingDirectory.getAbsolutePath(), e); - throw new ContainerLaunchException("Exception while trying to create temp directory", e); + + if (vncRecordingDirectory == null) { + try { + vncRecordingDirectory = Files.createTempDirectory(TC_TEMP_DIR_PREFIX).toFile(); + } catch (IOException e) { + // should never happen as per javadoc, since we use valid prefix + logger().error("Exception while trying to create temp directory " + vncRecordingDirectory.getAbsolutePath(), e); + throw new ContainerLaunchException("Exception while trying to create temp directory", e); + } } if (getNetwork() == null) { diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java index 7e77f63a051..3872e3d8dd2 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java @@ -1,17 +1,19 @@ package org.testcontainers.junit; + +import com.google.common.io.PatternFilenameFilter; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.runners.Enclosed; +import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.openqa.selenium.chrome.ChromeOptions; import org.testcontainers.containers.BrowserWebDriverContainer; import org.testcontainers.containers.DefaultRecordingFileFactory; import org.testcontainers.lifecycle.TestDescription; -import java.io.File; import java.util.Optional; - +import static org.junit.Assert.assertTrue; import static org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode.RECORD_ALL; @RunWith(Enclosed.class) @@ -20,14 +22,34 @@ public class ChromeRecordingWebDriverContainerTest extends BaseWebDriverContaine public static class ChromeThatRecordsAllTests { @Rule - public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() - .withCapabilities(new ChromeOptions()) - .withRecordingMode(RECORD_ALL, new File("./build/")) - .withRecordingFileFactory(new DefaultRecordingFileFactory()); + public TemporaryFolder vncRecordingDirectory = new TemporaryFolder(); @Test public void recordingTestThatShouldBeRecordedAndRetained() { - doSimpleExplore(chrome); + try ( + BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() + .withCapabilities(new ChromeOptions()) + .withRecordingMode(RECORD_ALL, vncRecordingDirectory.getRoot()) + .withRecordingFileFactory(new DefaultRecordingFileFactory()) + ) { + chrome.start(); + + doSimpleExplore(chrome); + chrome.afterTest(new TestDescription() { + @Override + public String getTestId() { + return getFilesystemFriendlyName(); + } + + @Override + public String getFilesystemFriendlyName() { + return "ChromeThatRecordsAllTests-recordingTestThatShouldBeRecordedAndRetained"; + } + }, Optional.empty()); + + String[] files = vncRecordingDirectory.getRoot().list(new PatternFilenameFilter("PASSED-.*\\.flv")); + assertTrue("Recorded file not found", files.length == 1); + } } } From a6e6079bbd61383e68bcf464ba49eccb1262e844 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 16 Apr 2020 22:02:15 +0200 Subject: [PATCH 1259/5615] Add `type/enhancement` to the Release Drafter (#2580) --- .github/release-drafter.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index ed8939a3117..af82504717a 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -5,8 +5,10 @@ template: | $CHANGES categories: - - title: 🚀 Features - label: type/feature + - title: 🚀 Features & Enhancements + labels: + - type/feature + - type/enhancement - title: 🐛 Bug Fixes label: type/bug - title: 📖 Documentation From 4b5bfaef9345f9e4e9f4607e5c7fd185124e7915 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 17 Apr 2020 14:25:16 +0200 Subject: [PATCH 1260/5615] Update Gradle Shadow Plugin to 5.2.0 (#2582) Gradle Shadow Plugin supports Gradle Caching from 5.2.0 and should help avoiding this heavyweight task when not necessarily. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d9d374cb511..78958851963 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ plugins { id 'io.franzbecker.gradle-lombok' version '3.1.0' id 'nebula.provided-base' version '3.0.3' - id 'com.github.johnrengelman.shadow' version '5.1.0' + id 'com.github.johnrengelman.shadow' version '5.2.0' id "com.jfrog.bintray" version "1.8.4" apply false } From b7cda9d81479c66f957435bccde72a4e66ae648e Mon Sep 17 00:00:00 2001 From: Lokesh Pant <7022317+KraTos5589@users.noreply.github.com> Date: Fri, 17 Apr 2020 18:17:36 +0530 Subject: [PATCH 1261/5615] Update CreateContainerCmd link to the latest release (#2583) * CreateContainerCmd Link was outdated. Replaced working link * Replaced with version 3.0.0 link * Updated to 3.2.1 --- docs/features/advanced_options.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/advanced_options.md b/docs/features/advanced_options.md index a774dbddc06..8689ce46021 100644 --- a/docs/features/advanced_options.md +++ b/docs/features/advanced_options.md @@ -51,4 +51,4 @@ For example, this can be used to change the container hostname: It is recommended to use this sparingly, and follow changes to the `docker-java` API if you choose to use this. It is typically quite stable, though. -For what is possible, consult the [`docker-java CreateContainerCmd` source code](https://github.com/docker-java/docker-java/blob/master/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java). +For what is possible, consult the [`docker-java CreateContainerCmd` source code](https://github.com/docker-java/docker-java/blob/3.2.1/docker-java-api/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java). From e354a7f51c62c40e26aa761be93eafcc2cd82bfe Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 18 Apr 2020 10:13:09 +0100 Subject: [PATCH 1262/5615] Reduce CircleCI build to a minimal subset of core (#2586) --- .circleci/config.yml | 89 ++------------------------------------------ 1 file changed, 3 insertions(+), 86 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 67e6546e220..8f700dba2b5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,15 +1,13 @@ version: 2 jobs: - core: + minimal_core: machine: enabled: true steps: - checkout - run: - command: ./gradlew testcontainers:build -x test -x check + command: ./gradlew --no-daemon --continue --scan testcontainers:test --tests '*GenericContainerRuleTest' - run: - command: ./gradlew testcontainers:check --scan - - run: &ciMate name: aggregate test reports with ciMate when: always environment: @@ -28,89 +26,8 @@ jobs: path: ~/junit - store_artifacts: path: ~/junit - modules-no-jdbc-test-no-selenium: - machine: - enabled: true - steps: - - checkout - - run: - command: ./gradlew check -x testcontainers:check -x selenium:check -x jdbc-test:check - --scan - environment: - TZ: /usr/share/zoneinfo/ETC/UTC - - run: *ciMate - - run: - name: Save test results - command: | - mkdir -p ~/junit/ - find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; - when: always - - store_test_results: - path: ~/junit - - store_artifacts: - path: ~/junit - modules-jdbc-test: - machine: - enabled: true - steps: - - checkout - - run: - command: ./gradlew jdbc-test:check --scan - environment: - TZ: /usr/share/zoneinfo/ETC/UTC - - run: *ciMate - - run: - name: Save test results - command: | - mkdir -p ~/junit/ - find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; - when: always - - store_test_results: - path: ~/junit - - store_artifacts: - path: ~/junit - selenium: - machine: - enabled: true - steps: - - checkout - - run: - command: ./gradlew selenium:check --scan - - run: *ciMate - - run: - name: Save test results - command: | - mkdir -p ~/junit/ - find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; - when: always - - store_test_results: - path: ~/junit - - store_artifacts: - path: ~/junit - examples: - machine: - enabled: true - steps: - - checkout - - run: - command: cd examples && ./gradlew check - - run: *ciMate - - run: - name: Save test results - command: | - mkdir -p ~/junit/ - find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; - when: always - - store_test_results: - path: ~/junit - - store_artifacts: - path: ~/junit workflows: version: 2 test_all: jobs: - - core - - modules-no-jdbc-test-no-selenium - - modules-jdbc-test - - selenium - - examples + - minimal_core From 1ef6fac59c11c2898466c53f6ed63d5095b67f5a Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 18 Apr 2020 10:31:15 +0100 Subject: [PATCH 1263/5615] Generate GitHub Actions CI Jobs automatically (#2520) Co-Authored-By: Sergei Egorov Co-Authored-By: Richard North --- .github/workflows/ci-dind.yml | 28 ++++++++ .github/workflows/ci.yml | 68 +++++++++++++++++--- .github/workflows/scripts/check_ci_status.sh | 2 +- build.gradle | 6 +- gradle/ci-support.gradle | 37 +++++++++++ 5 files changed, 129 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/ci-dind.yml create mode 100644 gradle/ci-support.gradle diff --git a/.github/workflows/ci-dind.yml b/.github/workflows/ci-dind.yml new file mode 100644 index 00000000000..15bd5ec76ee --- /dev/null +++ b/.github/workflows/ci-dind.yml @@ -0,0 +1,28 @@ +name: in-container + +on: + pull_request: {} + push: { branches: [ master ] } + +jobs: + in-docker_test: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Build with Gradle + run: | + docker run -i --rm \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v "$HOME:$HOME" \ + -v "$PWD:$PWD" \ + -w "$PWD" \ + openjdk:8-jdk-alpine \ + ./gradlew --no-daemon --continue --scan testcontainers:test --tests '*GenericContainerRuleTest' + - name: aggregate test reports with ciMate + if: always() + env: + CIMATE_PROJECT_ID: 2348n4vl + run: | + wget -q https://get.cimate.io/release/linux/cimate + chmod +x cimate + ./cimate "**/TEST-*.xml" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1a0a97d05f7..5f7dfb2c3dc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,20 +5,68 @@ on: push: { branches: [ master ] } jobs: - in-docker_test: + find_gradle_jobs: runs-on: ubuntu-18.04 + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - uses: actions/checkout@v2 - - name: Build with Gradle + - id: set-matrix run: | - docker run -i --rm \ - -v /var/run/docker.sock:/var/run/docker.sock \ - -v "$HOME:$HOME" \ - -v "$PWD:$PWD" \ - -w "$PWD" \ - openjdk:8-jdk-alpine \ - ./gradlew --no-daemon --continue --scan testcontainers:test --tests '*GenericContainerRuleTest' - - name: aggregate test reports with ciMate + TASKS=$(./gradlew --no-daemon --parallel -q testMatrix) + echo $TASKS + echo "::set-output name=matrix::{\"gradle_args\":$TASKS}" + check: + needs: find_gradle_jobs + strategy: + matrix: ${{ fromJson(needs.find_gradle_jobs.outputs.matrix) }} + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-java@v1 + with: + java-version: '1.8' + - name: Cache Gradle Home files + uses: actions/cache@v1 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-home-${{matrix.gradle_args}}_check-${{ hashFiles('**/*.gradle') }} + restore-keys: | + ${{ runner.os }}-gradle-home-${{matrix.gradle_args}}_check- + ${{ runner.os }}-gradle-home-${{matrix.gradle_args}}_check- + ${{ runner.os }}-gradle-home- + - name: Clear existing docker image cache + run: docker image prune -af + - name: Build and test with Gradle (${{matrix.gradle_args}}) + run: | + ./gradlew --no-daemon --continue --scan --info ${{matrix.gradle_args}} + - name: Aggregate test reports with ciMate + if: always() + env: + CIMATE_PROJECT_ID: 2348n4vl + run: | + wget -q https://get.cimate.io/release/linux/cimate + chmod +x cimate + ./cimate "**/TEST-*.xml" + examples: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-java@v1 + with: + java-version: '1.8' + - name: Cache Gradle Home files + uses: actions/cache@v1 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-home-examples-${{ hashFiles('**/*.gradle*') }} + restore-keys: | + ${{ runner.os }}-gradle-home-examples- + - name: Build and test Examples with Gradle + working-directory: ./examples/ + run: | + ./gradlew --no-daemon --scan --info check + - name: Aggregate test reports with ciMate if: always() env: CIMATE_PROJECT_ID: 2348n4vl diff --git a/.github/workflows/scripts/check_ci_status.sh b/.github/workflows/scripts/check_ci_status.sh index bc5c0b138a2..29ef1d939a8 100755 --- a/.github/workflows/scripts/check_ci_status.sh +++ b/.github/workflows/scripts/check_ci_status.sh @@ -1,4 +1,4 @@ -#/bin/bash +#!/bin/bash set -o errexit set -o nounset set -o pipefail diff --git a/build.gradle b/build.gradle index 78958851963..382ecdf161e 100644 --- a/build.gradle +++ b/build.gradle @@ -5,6 +5,8 @@ plugins { id "com.jfrog.bintray" version "1.8.4" apply false } +apply from: "$rootDir/gradle/ci-support.gradle" + subprojects { apply plugin: 'nebula.provided-base' apply plugin: 'java' @@ -45,10 +47,12 @@ subprojects { apply from: "$rootDir/gradle/publishing.gradle" apply from: "$rootDir/gradle/bintray.gradle" + jar.archiveClassifier.set("original") + publishing { publications { mavenJava(MavenPublication) { publication -> - artifacts.removeAll { it.classifier == null } + artifacts.removeAll { it.classifier == jar.archiveClassifier.get() } artifact project.tasks.shadowJar } } diff --git a/gradle/ci-support.gradle b/gradle/ci-support.gradle new file mode 100644 index 00000000000..7c3408068b3 --- /dev/null +++ b/gradle/ci-support.gradle @@ -0,0 +1,37 @@ +import groovy.json.JsonOutput + +// Emit a JSON-formatted list of check tasks to be run in CI +task testMatrix { + project.afterEvaluate { + def checkTasks = subprojects.collect { + it.tasks.findByName("check") + }.findAll { it != null } + + dependsOn(checkTasks) + doLast { + // Obtain a list of check tasks that are not up-to-date, i.e. + // the ones which Gradle cannot find a cached output for. + def checkTaskPaths = checkTasks + .findAll { !it.state.upToDate } + .collect { it.path } + + println(JsonOutput.toJson(checkTaskPaths)) + } + } +} + +// If we're executing the `taskMatrix` task, disable tests and other slow tasks +// so that we can get a result quickly. +gradle.taskGraph.whenReady { + if (it.hasTask(tasks.testMatrix)) { + for (subproject in subprojects) { + subproject.tasks.withType(Test).all { + testExecuter([execute: { spec, processor -> }, stopNow:{}] as org.gradle.api.internal.tasks.testing.TestExecuter) + } + subproject.tasks.findByName("shadowJar").enabled = false + subproject.tasks.findByName("javadoc").enabled = false + subproject.tasks.findByName("delombok").enabled = false + subproject.tasks.findByName("japicmp")?.enabled = false + } + } +} From e7230c282e76ac54ea1d0d941e71876ba6e45d49 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 18 Apr 2020 14:22:57 +0100 Subject: [PATCH 1264/5615] Exclude multi-module `module-info.class` from shaded JAR (#2588) --- core/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/core/build.gradle b/core/build.gradle index 84a20425cc5..b116a1f85f2 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -44,6 +44,7 @@ shadowJar { 'META-INF/DEPENDENCIES', 'META-INF/maven/', 'META-INF/proguard/', + 'META-INF/versions/*/module-info.class', 'META-INF/services/README.md', 'META-INF/services/com.fasterxml.jackson.core.*', 'META-INF/services/com.github.dockerjava.api.command.*', From 374c559d9bca054f06fdd69cd0f95b024af425bc Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 18 Apr 2020 16:07:28 +0100 Subject: [PATCH 1265/5615] Add gradle caching to find_gradle_jobs build (#2589) --- .github/workflows/ci.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5f7dfb2c3dc..84b3d9a5197 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,6 +11,17 @@ jobs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - uses: actions/checkout@v2 + - uses: actions/setup-java@v1 + with: + java-version: '1.8' + - name: Cache Gradle Home files + uses: actions/cache@v1 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-home-testmatrix-${{ hashFiles('**/*.gradle') }} + restore-keys: | + ${{ runner.os }}-gradle-home-testmatrix- + ${{ runner.os }}-gradle-home- - id: set-matrix run: | TASKS=$(./gradlew --no-daemon --parallel -q testMatrix) From f6728616872ec7011ef3b3c75fab9c4bc1982fe5 Mon Sep 17 00:00:00 2001 From: Sudharaka Palamakumbura Date: Sat, 18 Apr 2020 08:17:35 -0700 Subject: [PATCH 1266/5615] Update Copyright for 2020 in Documentation (#2566) --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index af2bf79f0c9..8b3ffda05d2 100644 --- a/docs/index.md +++ b/docs/index.md @@ -131,6 +131,6 @@ This project was initially inspired by a [gist](https://gist.github.com/mosheesh ## Copyright -Copyright (c) 2015-2019 Richard North and other authors. +Copyright (c) 2015-2020 Richard North and other authors. See [AUTHORS](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/AUTHORS) for contributors. From d2fe97bc47c65cd87a28cd11576bee944c07a848 Mon Sep 17 00:00:00 2001 From: Sudharaka Palamakumbura Date: Sat, 18 Apr 2020 08:18:24 -0700 Subject: [PATCH 1267/5615] Update latest _version tag in Documentation Code Samples (#2568) --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index c2d39bc9d2c..e3cae4c5da8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -88,4 +88,4 @@ nav: - contributing.md - contributing_docs.md extra: - latest_version: 1.13.0 + latest_version: 1.14.0 From 3530e63d1d33801b4851acbe606d1282ebbd4298 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 19 Apr 2020 11:11:35 +0200 Subject: [PATCH 1268/5615] delegate `getMetadata()` to `R2DBCDatabaseContainerProvider` (#2577) Apparently, R2DBC ConnectionFactories perform early validation of options, and it fails because the original options (when specified as `r2dbc://mysql:///db`) miss some required options like host/port/username/password. After talking to Mark from R2DBC, it seems that the checks will stay, so we need to workaround it somehow. I ended up with the delegation to `R2DBCDatabaseContainerProvider`, because these provides should know which options they add and which are required. --- .../containers/MariaDBContainer.java | 8 ++++++-- ...MariaDBR2DBCDatabaseContainerProvider.java | 16 ++++++++++++++++ .../MSSQLR2DBCDatabaseContainerProvider.java | 16 ++++++++++++++++ .../containers/MSSQLServerContainer.java | 10 +++++++--- .../containers/MySQLContainer.java | 8 ++++++-- .../MySQLR2DBCDatabaseContainerProvider.java | 16 ++++++++++++++++ .../containers/PostgreSQLContainer.java | 5 +++++ ...tgreSQLR2DBCDatabaseContainerProvider.java | 16 ++++++++++++++++ .../r2dbc/R2DBCDatabaseContainerProvider.java | 19 +++++++++++++++++++ .../TestcontainersR2DBCConnectionFactory.java | 3 +-- .../AbstractR2DBCDatabaseContainerTest.java | 8 ++++++++ 11 files changed, 116 insertions(+), 9 deletions(-) diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java index e402bc24e0e..fc876713dda 100644 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java @@ -11,10 +11,14 @@ public class MariaDBContainer> extends JdbcD public static final String IMAGE = "mariadb"; public static final String DEFAULT_TAG = "10.3.6"; + static final String DEFAULT_USER = "test"; + + static final String DEFAULT_PASSWORD = "test"; + static final Integer MARIADB_PORT = 3306; private String databaseName = "test"; - private String username = "test"; - private String password = "test"; + private String username = DEFAULT_USER; + private String password = DEFAULT_PASSWORD; private static final String MARIADB_ROOT_USER = "root"; private static final String MY_CNF_CONFIG_OVERRIDE_PARAM_NAME = "TC_MY_CNF"; diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainerProvider.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainerProvider.java index 79cb627f8a9..36a201f85bb 100644 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainerProvider.java +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainerProvider.java @@ -1,11 +1,14 @@ package org.testcontainers.containers; import com.google.auto.service.AutoService; +import io.r2dbc.spi.ConnectionFactoryMetadata; import io.r2dbc.spi.ConnectionFactoryOptions; import org.mariadb.r2dbc.MariadbConnectionFactoryProvider; import org.testcontainers.r2dbc.R2DBCDatabaseContainer; import org.testcontainers.r2dbc.R2DBCDatabaseContainerProvider; +import javax.annotation.Nullable; + @AutoService(R2DBCDatabaseContainerProvider.class) public class MariaDBR2DBCDatabaseContainerProvider implements R2DBCDatabaseContainerProvider { @@ -27,4 +30,17 @@ public R2DBCDatabaseContainer createContainer(ConnectionFactoryOptions options) } return new MariaDBR2DBCDatabaseContainer(container); } + + @Nullable + @Override + public ConnectionFactoryMetadata getMetadata(ConnectionFactoryOptions options) { + ConnectionFactoryOptions.Builder builder = options.mutate(); + if (!options.hasOption(ConnectionFactoryOptions.USER)) { + builder.option(ConnectionFactoryOptions.USER, MariaDBContainer.DEFAULT_USER); + } + if (!options.hasOption(ConnectionFactoryOptions.PASSWORD)) { + builder.option(ConnectionFactoryOptions.PASSWORD, MariaDBContainer.DEFAULT_PASSWORD); + } + return R2DBCDatabaseContainerProvider.super.getMetadata(builder.build()); + } } diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainerProvider.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainerProvider.java index 1638901226c..d72ab8e8419 100644 --- a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainerProvider.java +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainerProvider.java @@ -2,10 +2,13 @@ import com.google.auto.service.AutoService; import io.r2dbc.mssql.MssqlConnectionFactoryProvider; +import io.r2dbc.spi.ConnectionFactoryMetadata; import io.r2dbc.spi.ConnectionFactoryOptions; import org.testcontainers.r2dbc.R2DBCDatabaseContainer; import org.testcontainers.r2dbc.R2DBCDatabaseContainerProvider; +import javax.annotation.Nullable; + @AutoService(R2DBCDatabaseContainerProvider.class) public class MSSQLR2DBCDatabaseContainerProvider implements R2DBCDatabaseContainerProvider { @@ -26,4 +29,17 @@ public R2DBCDatabaseContainer createContainer(ConnectionFactoryOptions options) } return new MSSQLR2DBCDatabaseContainer(container); } + + @Nullable + @Override + public ConnectionFactoryMetadata getMetadata(ConnectionFactoryOptions options) { + ConnectionFactoryOptions.Builder builder = options.mutate(); + if (!options.hasOption(ConnectionFactoryOptions.USER)) { + builder.option(ConnectionFactoryOptions.USER, MSSQLServerContainer.DEFAULT_USER); + } + if (!options.hasOption(ConnectionFactoryOptions.PASSWORD)) { + builder.option(ConnectionFactoryOptions.PASSWORD, MSSQLServerContainer.DEFAULT_PASSWORD); + } + return R2DBCDatabaseContainerProvider.super.getMetadata(builder.build()); + } } diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java index afe3f5b8651..2219a5aaf61 100644 --- a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java @@ -15,8 +15,12 @@ public class MSSQLServerContainer> exten public static final String DEFAULT_TAG = "2017-CU12"; public static final Integer MS_SQL_SERVER_PORT = 1433; - private String username = "SA"; - private String password = "A_Str0ng_Required_Password"; + + static final String DEFAULT_USER = "SA"; + + static final String DEFAULT_PASSWORD = "A_Str0ng_Required_Password"; + + private String password = DEFAULT_PASSWORD; private static final int DEFAULT_STARTUP_TIMEOUT_SECONDS = 240; private static final int DEFAULT_CONNECT_TIMEOUT_SECONDS = 240; @@ -63,7 +67,7 @@ public String getJdbcUrl() { @Override public String getUsername() { - return username; + return DEFAULT_USER; } @Override diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java index a8549672ff3..54f3ab08524 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java @@ -14,11 +14,15 @@ public class MySQLContainer> extends JdbcDatab public static final String IMAGE = "mysql"; public static final String DEFAULT_TAG = "5.7.22"; + static final String DEFAULT_USER = "test"; + + static final String DEFAULT_PASSWORD = "test"; + private static final String MY_CNF_CONFIG_OVERRIDE_PARAM_NAME = "TC_MY_CNF"; public static final Integer MYSQL_PORT = 3306; private String databaseName = "test"; - private String username = "test"; - private String password = "test"; + private String username = DEFAULT_USER; + private String password = DEFAULT_PASSWORD; private static final String MYSQL_ROOT_USER = "root"; public MySQLContainer() { diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerProvider.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerProvider.java index 3a01616caba..ec3b6a4e41f 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerProvider.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerProvider.java @@ -2,10 +2,13 @@ import com.google.auto.service.AutoService; import dev.miku.r2dbc.mysql.MySqlConnectionFactoryProvider; +import io.r2dbc.spi.ConnectionFactoryMetadata; import io.r2dbc.spi.ConnectionFactoryOptions; import org.testcontainers.r2dbc.R2DBCDatabaseContainer; import org.testcontainers.r2dbc.R2DBCDatabaseContainerProvider; +import javax.annotation.Nullable; + @AutoService(R2DBCDatabaseContainerProvider.class) public class MySQLR2DBCDatabaseContainerProvider implements R2DBCDatabaseContainerProvider { @@ -27,4 +30,17 @@ public R2DBCDatabaseContainer createContainer(ConnectionFactoryOptions options) } return new MySQLR2DBCDatabaseContainer(container); } + + @Nullable + @Override + public ConnectionFactoryMetadata getMetadata(ConnectionFactoryOptions options) { + ConnectionFactoryOptions.Builder builder = options.mutate(); + if (!options.hasOption(ConnectionFactoryOptions.USER)) { + builder.option(ConnectionFactoryOptions.USER, MySQLContainer.DEFAULT_USER); + } + if (!options.hasOption(ConnectionFactoryOptions.PASSWORD)) { + builder.option(ConnectionFactoryOptions.PASSWORD, MySQLContainer.DEFAULT_PASSWORD); + } + return R2DBCDatabaseContainerProvider.super.getMetadata(builder.build()); + } } diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index 0fae3a16f4a..06cdd7ebc8d 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -19,6 +19,11 @@ public class PostgreSQLContainer> extends public static final String DEFAULT_TAG = "9.6.12"; public static final Integer POSTGRESQL_PORT = 5432; + + static final String DEFAULT_USER = "test"; + + static final String DEFAULT_PASSWORD = "test"; + private String databaseName = "test"; private String username = "test"; private String password = "test"; diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainerProvider.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainerProvider.java index 5e9da980b8e..971b4cd3758 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainerProvider.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainerProvider.java @@ -2,10 +2,13 @@ import com.google.auto.service.AutoService; import io.r2dbc.postgresql.PostgresqlConnectionFactoryProvider; +import io.r2dbc.spi.ConnectionFactoryMetadata; import io.r2dbc.spi.ConnectionFactoryOptions; import org.testcontainers.r2dbc.R2DBCDatabaseContainer; import org.testcontainers.r2dbc.R2DBCDatabaseContainerProvider; +import javax.annotation.Nullable; + @AutoService(R2DBCDatabaseContainerProvider.class) public final class PostgreSQLR2DBCDatabaseContainerProvider implements R2DBCDatabaseContainerProvider { @@ -27,4 +30,17 @@ public R2DBCDatabaseContainer createContainer(ConnectionFactoryOptions options) } return new PostgreSQLR2DBCDatabaseContainer(container); } + + @Nullable + @Override + public ConnectionFactoryMetadata getMetadata(ConnectionFactoryOptions options) { + ConnectionFactoryOptions.Builder builder = options.mutate(); + if (!options.hasOption(ConnectionFactoryOptions.USER)) { + builder.option(ConnectionFactoryOptions.USER, PostgreSQLContainer.DEFAULT_USER); + } + if (!options.hasOption(ConnectionFactoryOptions.PASSWORD)) { + builder.option(ConnectionFactoryOptions.PASSWORD, PostgreSQLContainer.DEFAULT_PASSWORD); + } + return R2DBCDatabaseContainerProvider.super.getMetadata(builder.build()); + } } diff --git a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/R2DBCDatabaseContainerProvider.java b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/R2DBCDatabaseContainerProvider.java index 2eabc0d521b..287407dc15e 100644 --- a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/R2DBCDatabaseContainerProvider.java +++ b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/R2DBCDatabaseContainerProvider.java @@ -1,8 +1,13 @@ package org.testcontainers.r2dbc; +import io.r2dbc.spi.ConnectionFactories; +import io.r2dbc.spi.ConnectionFactory; +import io.r2dbc.spi.ConnectionFactoryMetadata; import io.r2dbc.spi.ConnectionFactoryOptions; import io.r2dbc.spi.Option; +import javax.annotation.Nullable; + public interface R2DBCDatabaseContainerProvider { Option REUSABLE_OPTION = Option.valueOf("TC_REUSABLE"); @@ -12,4 +17,18 @@ public interface R2DBCDatabaseContainerProvider { boolean supports(ConnectionFactoryOptions options); R2DBCDatabaseContainer createContainer(ConnectionFactoryOptions options); + + @Nullable + default ConnectionFactoryMetadata getMetadata(ConnectionFactoryOptions options) { + ConnectionFactoryOptions.Builder builder = options.mutate(); + if (!options.hasOption(ConnectionFactoryOptions.HOST)) { + builder.option(ConnectionFactoryOptions.HOST, "localhost"); + } + if (!options.hasOption(ConnectionFactoryOptions.PORT)) { + builder.option(ConnectionFactoryOptions.PORT, 65535); + } + + ConnectionFactory connectionFactory = ConnectionFactories.find(builder.build()); + return connectionFactory != null ? connectionFactory.getMetadata() : null; + } } diff --git a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactory.java b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactory.java index 1a0fc57ad8c..691775a94f8 100644 --- a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactory.java +++ b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactory.java @@ -68,8 +68,7 @@ public Publisher create() { @Override public ConnectionFactoryMetadata getMetadata() { - // TODO check whether it is fine to call it without setting the host/port and other values - return ConnectionFactories.find(options).getMetadata(); + return containerProvider.getMetadata(options); } @Override diff --git a/modules/r2dbc/src/testFixtures/java/org/testcontainers/r2dbc/AbstractR2DBCDatabaseContainerTest.java b/modules/r2dbc/src/testFixtures/java/org/testcontainers/r2dbc/AbstractR2DBCDatabaseContainerTest.java index 7f14ecca2a7..c21c7078410 100644 --- a/modules/r2dbc/src/testFixtures/java/org/testcontainers/r2dbc/AbstractR2DBCDatabaseContainerTest.java +++ b/modules/r2dbc/src/testFixtures/java/org/testcontainers/r2dbc/AbstractR2DBCDatabaseContainerTest.java @@ -4,6 +4,7 @@ import io.r2dbc.spi.Connection; import io.r2dbc.spi.ConnectionFactories; import io.r2dbc.spi.ConnectionFactory; +import io.r2dbc.spi.ConnectionFactoryMetadata; import io.r2dbc.spi.ConnectionFactoryOptions; import org.junit.Test; import org.testcontainers.containers.GenericContainer; @@ -38,6 +39,13 @@ public final void testUrlSupport() { runTestQuery(connectionFactory); } + @Test + public final void testGetMetadata() { + ConnectionFactory connectionFactory = ConnectionFactories.get(createR2DBCUrl()); + ConnectionFactoryMetadata metadata = connectionFactory.getMetadata(); + assertThat(metadata).isNotNull(); + } + protected abstract T createContainer(); protected void runTestQuery(ConnectionFactory connectionFactory) { From add6eb431525c3fd51c8709441c502f1657529ad Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 19 Apr 2020 11:12:12 +0200 Subject: [PATCH 1269/5615] Bump testcontainers from 1.12.5 to 1.14.0 in /core (#2569) Bumps [testcontainers](https://github.com/testcontainers/testcontainers-java) from 1.12.5 to 1.14.0. - [Release notes](https://github.com/testcontainers/testcontainers-java/releases) - [Changelog](https://github.com/testcontainers/testcontainers-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-java/compare/1.12.5...1.14.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index b116a1f85f2..09a6c590242 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -110,7 +110,7 @@ if (!org.gradle.internal.os.OperatingSystem.current().isWindows()) { } dependencies { - baseline 'org.testcontainers:testcontainers:1.12.5', { + baseline 'org.testcontainers:testcontainers:1.14.0', { exclude group: "*", module: "*" } From 84f422b5c6e23c39698b32ff3cbc3a5d64f70177 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 19 Apr 2020 16:19:40 +0200 Subject: [PATCH 1270/5615] GHA: disable fail-fast matrix strategy (#2591) See https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstrategyfail-fast --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 84b3d9a5197..3dbcc4c41fd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,6 +30,7 @@ jobs: check: needs: find_gradle_jobs strategy: + fail-fast: false matrix: ${{ fromJson(needs.find_gradle_jobs.outputs.matrix) }} runs-on: ubuntu-18.04 steps: From b6cd9c80eddca65c9e2718b66c91b6b76f181371 Mon Sep 17 00:00:00 2001 From: Chason Choate Date: Sun, 19 Apr 2020 09:20:17 -0500 Subject: [PATCH 1271/5615] Make ImageData class public for custom image pull policies (#2585) * Making imagedata public so it can be used to create custom image pull policies. Adding test to ensure it works as expected. * Ensuring we don't do unnecessary pulls and using the default generic container image for the out of package pull policy test. Co-authored-by: Chason Choate Co-authored-by: Richard North --- .../org/testcontainers/images/ImageData.java | 2 +- .../test/java/alt/testcontainers/README.md | 1 + .../OutOfPackageImagePullPolicyTest.java | 27 +++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 core/src/test/java/alt/testcontainers/README.md create mode 100644 core/src/test/java/alt/testcontainers/images/OutOfPackageImagePullPolicyTest.java diff --git a/core/src/main/java/org/testcontainers/images/ImageData.java b/core/src/main/java/org/testcontainers/images/ImageData.java index bef0c56c245..54b07d5acf2 100644 --- a/core/src/main/java/org/testcontainers/images/ImageData.java +++ b/core/src/main/java/org/testcontainers/images/ImageData.java @@ -10,7 +10,7 @@ @Value @Builder -class ImageData { +public class ImageData { @NonNull Instant createdAt; diff --git a/core/src/test/java/alt/testcontainers/README.md b/core/src/test/java/alt/testcontainers/README.md new file mode 100644 index 00000000000..0a08ff91ba5 --- /dev/null +++ b/core/src/test/java/alt/testcontainers/README.md @@ -0,0 +1 @@ +Useful place for running tests that need to be outside of the `org.testcontainers` package. diff --git a/core/src/test/java/alt/testcontainers/images/OutOfPackageImagePullPolicyTest.java b/core/src/test/java/alt/testcontainers/images/OutOfPackageImagePullPolicyTest.java new file mode 100644 index 00000000000..2da41a8344e --- /dev/null +++ b/core/src/test/java/alt/testcontainers/images/OutOfPackageImagePullPolicyTest.java @@ -0,0 +1,27 @@ +package alt.testcontainers.images; + +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; +import org.testcontainers.images.AbstractImagePullPolicy; +import org.testcontainers.images.ImageData; +import org.testcontainers.utility.DockerImageName; + +public class OutOfPackageImagePullPolicyTest { + @Test + public void shouldSupportCustomPoliciesOutOfTestContainersPackage() { + try ( + GenericContainer container = new GenericContainer<>() + .withImagePullPolicy(new AbstractImagePullPolicy() { + @Override + protected boolean shouldPullCached(DockerImageName imageName, ImageData localImageData) { + return false; + } + }) + ) { + container.withStartupCheckStrategy(new OneShotStartupCheckStrategy()); + container.start(); + } + } + +} From c9692006f1e774eda0981e9820f7331cab662196 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 19 Apr 2020 15:22:17 +0100 Subject: [PATCH 1272/5615] Reduce AZP build to a minimal subset of core, except when running on Windows (#2587) --- azure-pipelines.yml | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c56ae9fde36..5780a01bb83 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,24 +1,29 @@ jobs: -- job: core + +- job: azure_pipeline_tests steps: + + # Run a minimal set of tests in normal Linux pipeline builds - task: Gradle@2 - displayName: Build & test + condition: eq(variables['Agent.OS'], 'Linux') + displayName: Build & test (Linux - minimal core) env: AWS_ACCESS_KEY_ID: $(aws.accessKeyId) AWS_SECRET_ACCESS_KEY: $(aws.secretAccessKey) inputs: gradleWrapperFile: 'gradlew' jdkVersionOption: '1.11' - options: '--no-daemon --continue' - tasks: 'testcontainers:check' + options: "--no-daemon --continue" + tasks: "testcontainers:test --tests GenericContainerRuleTest" publishJUnitResults: true testResultsFiles: '**/TEST-*.xml' - script: wget -q https://get.cimate.io/release/linux/cimate && chmod +x cimate && ./cimate "**/TEST-*.xml" condition: and(succeededOrFailed(),eq(variables['Agent.OS'], 'Linux')) -- job: jdbc - steps: + + # Run all tests when running the Windows CI tests - task: Gradle@2 - displayName: Build & test + condition: eq(variables['Agent.OS'], 'Windows_NT') + displayName: Build & test (Windows - all modules) env: AWS_ACCESS_KEY_ID: $(aws.accessKeyId) AWS_SECRET_ACCESS_KEY: $(aws.secretAccessKey) @@ -26,24 +31,6 @@ jobs: gradleWrapperFile: 'gradlew' jdkVersionOption: '1.11' options: '--no-daemon --continue' - tasks: 'jdbc-test:check' - publishJUnitResults: true - testResultsFiles: '**/TEST-*.xml' - - script: wget -q https://get.cimate.io/release/linux/cimate && chmod +x cimate && ./cimate "**/TEST-*.xml" - condition: and(succeededOrFailed(), eq(variables['Agent.OS'], 'Linux')) -- job: modules - steps: - - task: Gradle@2 - displayName: Build & test - env: - AWS_ACCESS_KEY_ID: $(aws.accessKeyId) - AWS_SECRET_ACCESS_KEY: $(aws.secretAccessKey) - inputs: - gradleWrapperFile: 'gradlew' - jdkVersionOption: '1.11' - options: '--continue -x testcontainers:check -x jdbc-test:check -PpostCheckCommand="docker image prune -af"' tasks: 'check' publishJUnitResults: true testResultsFiles: '**/TEST-*.xml' - - script: wget -q https://get.cimate.io/release/linux/cimate && chmod +x cimate && ./cimate "**/TEST-*.xml" - condition: and(succeededOrFailed(), eq(variables['Agent.OS'], 'Linux')) From f631023576288234e7aacdce867520200b2dcfda Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 19 Apr 2020 19:57:42 +0100 Subject: [PATCH 1273/5615] Support ImageFromDockerfile authenticated image pulls (#2573) Co-Authored-By: David Byron --- .../images/builder/ImageFromDockerfile.java | 49 ++++++--- .../utility/AuthenticatedImagePullTest.java | 103 ++++++++++++++---- 2 files changed, 112 insertions(+), 40 deletions(-) diff --git a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java index 1140247960a..11a7e2f4cb6 100644 --- a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java +++ b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java @@ -83,16 +83,6 @@ protected final String resolve() { DockerClient dockerClient = DockerClientFactory.instance().client(); - dependencyImageNames.forEach(imageName -> { - try { - log.info("Pre-emptively checking local images for '{}', referenced via a Dockerfile. If not available, it will be pulled.", imageName); - DockerClientFactory.instance().checkAndPullImage(dockerClient, imageName); - } catch (Exception e) { - log.warn("Unable to pre-fetch an image ({}) depended upon by Dockerfile - image build will continue but may fail. Exception message was: {}", imageName, e.getMessage()); - } - }); - - try { if (deleteOnExit) { ResourceReaper.instance().registerImageForCleanup(dockerImageName); @@ -118,6 +108,8 @@ public void onNext(BuildResponseItem item) { BuildImageCmd buildImageCmd = dockerClient.buildImageCmd(in); configure(buildImageCmd); + prePullDependencyImages(dependencyImageNames); + BuildImageResultCallback exec = buildImageCmd.exec(resultCallback); long bytesToDockerDaemon = 0; @@ -154,11 +146,30 @@ protected void configure(BuildImageCmd buildImageCmd) { this.dockerfile.ifPresent(p -> { buildImageCmd.withDockerfile(p.toFile()); dependencyImageNames = new ParsedDockerfile(p).getDependencyImageNames(); + + if (dependencyImageNames.size() > 0) { + // if we'll be pre-pulling images, disable the built-in pull as it is not necessary and will fail for + // authenticated registries + buildImageCmd.withPull(false); + } }); this.buildArgs.forEach(buildImageCmd::withBuildArg); } + private void prePullDependencyImages(Set imagesToPull) { + final DockerClient dockerClient = DockerClientFactory.instance().client(); + + imagesToPull.forEach(imageName -> { + try { + log.info("Pre-emptively checking local images for '{}', referenced via a Dockerfile. If not available, it will be pulled.", imageName); + DockerClientFactory.instance().checkAndPullImage(dockerClient, imageName); + } catch (Exception e) { + log.warn("Unable to pre-fetch an image ({}) depended upon by Dockerfile - image build will continue but may fail. Exception message was: {}", imageName, e.getMessage()); + } + }); + } + public ImageFromDockerfile withBuildArg(final String key, final String value) { this.buildArgs.put(key, value); return this; @@ -171,19 +182,23 @@ public ImageFromDockerfile withBuildArgs(final Map args) { /** * Sets the Dockerfile to be used for this image. - * @deprecated It is recommended to use {@link #withDockerfile} instead because it honors - * .dockerignore files and therefore will be more efficient - * @param relativePathFromBuildRoot + * + * @param relativePathFromBuildContextDirectory relative path to the Dockerfile, relative to the image build context directory + * @deprecated It is recommended to use {@link #withDockerfile} instead because it honors .dockerignore files and + * will therefore be more efficient. Additionally, using {@link #withDockerfile} supports Dockerfiles that depend + * upon images from authenticated private registries. */ @Deprecated - public ImageFromDockerfile withDockerfilePath(String relativePathFromBuildRoot) { - this.dockerFilePath = Optional.of(relativePathFromBuildRoot); + public ImageFromDockerfile withDockerfilePath(String relativePathFromBuildContextDirectory) { + this.dockerFilePath = Optional.of(relativePathFromBuildContextDirectory); return this; } /** - * Sets the Dockerfile to be used for this image. - * @param dockerfile + * Sets the Dockerfile to be used for this image. Honors .dockerignore files for efficiency. + * Additionally, supports Dockerfiles that depend upon images from authenticated private registries. + * + * @param dockerfile path to Dockerfile on the test host. */ public ImageFromDockerfile withDockerfile(Path dockerfile) { this.dockerfile = Optional.of(dockerfile); diff --git a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java index 78b54798cd1..56945643a88 100644 --- a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java +++ b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java @@ -1,19 +1,27 @@ package org.testcontainers.utility; import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.async.ResultCallback; +import com.github.dockerjava.api.command.PullImageResultCallback; import com.github.dockerjava.api.model.AuthConfig; -import com.github.dockerjava.core.command.PullImageResultCallback; -import com.github.dockerjava.core.command.PushImageResultCallback; +import org.intellij.lang.annotations.Language; import org.junit.AfterClass; +import org.junit.Before; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; import org.mockito.Mockito; import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.ContainerState; +import org.testcontainers.containers.DockerComposeContainer; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.images.builder.ImageFromDockerfile; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.concurrent.TimeUnit; import static org.mockito.ArgumentMatchers.any; @@ -25,14 +33,17 @@ * This test checks the integration between Testcontainers and an authenticated registry, but uses * a mock instance of {@link RegistryAuthLocator} - the purpose of the test is solely to ensure that * the auth locator is utilised, and that the credentials it provides flow through to the registry. - * + *

* {@link RegistryAuthLocatorTest} covers actual credential scenarios at a lower level, which are * impractical to test end-to-end. */ public class AuthenticatedImagePullTest { + /** + * Containerised docker image registry, with simple hardcoded credentials + */ @ClassRule - public static GenericContainer authenticatedRegistry = new GenericContainer(new ImageFromDockerfile() + public static GenericContainer authenticatedRegistry = new GenericContainer<>(new ImageFromDockerfile() .withDockerfileFromBuilder(builder -> { builder.from("registry:2") .run("htpasswd -Bbn testuser notasecret > /htpasswd") @@ -46,28 +57,17 @@ public class AuthenticatedImagePullTest { private static RegistryAuthLocator originalAuthLocatorSingleton; private static DockerClient client; - private static String testRegistryAddress; private static String testImageName; private static String testImageNameWithTag; @BeforeClass - public static void setUp() { + public static void setUp() throws InterruptedException { originalAuthLocatorSingleton = RegistryAuthLocator.instance(); client = DockerClientFactory.instance().client(); - testRegistryAddress = authenticatedRegistry.getContainerIpAddress() + ":" + authenticatedRegistry.getFirstMappedPort(); + String testRegistryAddress = authenticatedRegistry.getContainerIpAddress() + ":" + authenticatedRegistry.getFirstMappedPort(); testImageName = testRegistryAddress + "/alpine"; testImageNameWithTag = testImageName + ":latest"; - } - - @AfterClass - public static void tearDown() { - RegistryAuthLocator.setInstance(originalAuthLocatorSingleton); - client.removeImageCmd(testImageNameWithTag).withForce(true).exec(); - } - - @Test - public void testThatAuthLocatorIsUsed() throws Exception { final DockerImageName expectedName = new DockerImageName(testImageNameWithTag); final AuthConfig authConfig = new AuthConfig() @@ -83,9 +83,23 @@ public void testThatAuthLocatorIsUsed() throws Exception { // a push will use the auth locator for authentication, although that isn't the goal of this test putImageInRegistry(); + } + + @Before + public void removeImageFromLocalDocker() { + // remove the image tag from local docker so that it must be pulled before use + client.removeImageCmd(testImageNameWithTag).withForce(true).exec(); + } + @AfterClass + public static void tearDown() { + RegistryAuthLocator.setInstance(originalAuthLocatorSingleton); + } + + @Test + public void testThatAuthLocatorIsUsedForContainerCreation() { // actually start a container, which will require an authenticated pull - try (final GenericContainer container = new GenericContainer<>(testImageNameWithTag) + try (final GenericContainer container = new GenericContainer<>(testImageNameWithTag) .withCommand("/bin/sh", "-c", "sleep 10")) { container.start(); @@ -93,7 +107,53 @@ public void testThatAuthLocatorIsUsed() throws Exception { } } - private void putImageInRegistry() throws InterruptedException { + @Test + public void testThatAuthLocatorIsUsedForDockerfileBuild() throws IOException { + // Prepare a simple temporary Dockerfile which requires our custom private image + Path tempContext = Files.createTempDirectory(Paths.get("."), this.getClass().getSimpleName() + "-test-"); + Path tempFile = Files.createTempFile(tempContext, "test", ".Dockerfile"); + String dockerFileContent = "FROM " + testImageNameWithTag; + Files.write(tempFile, dockerFileContent.getBytes()); + + // Start a container built from a derived image, which will require an authenticated pull + try (final GenericContainer container = new GenericContainer<>( + new ImageFromDockerfile() + .withDockerfile(tempFile) + ) + .withCommand("/bin/sh", "-c", "sleep 10")) { + container.start(); + + assertTrue("container started following an authenticated pull", container.isRunning()); + } + } + + @Test + public void testThatAuthLocatorIsUsedForDockerComposePull() throws IOException { + // Prepare a simple temporary Docker Compose manifest which requires our custom private image + Path tempContext = Files.createTempDirectory(Paths.get("."), this.getClass().getSimpleName() + "-test-"); + Path tempFile = Files.createTempFile(tempContext, "test", ".docker-compose.yml"); + @Language("yaml") String composeFileContent = + "version: '2.0'\n" + + "services:\n" + + " privateservice:\n" + + " command: /bin/sh -c 'sleep 60'\n" + + " image: " + testImageNameWithTag; + Files.write(tempFile, composeFileContent.getBytes()); + + // Start the docker compose project, which will require an authenticated pull + try (final DockerComposeContainer compose = new DockerComposeContainer<>(tempFile.toFile())) { + compose.start(); + + assertTrue("container started following an authenticated pull", + compose + .getContainerByServiceName("privateservice_1") + .map(ContainerState::isRunning) + .orElse(false) + ); + } + } + + private static void putImageInRegistry() throws InterruptedException { // It doesn't matter which image we use for this test, but use one that's likely to have been pulled already final String dummySourceImage = TestcontainersConfiguration.getInstance().getRyukImage(); @@ -109,10 +169,7 @@ private void putImageInRegistry() throws InterruptedException { client.tagImageCmd(id, testImageName, "latest").exec(); client.pushImageCmd(testImageNameWithTag) - .exec(new PushImageResultCallback()) + .exec(new ResultCallback.Adapter<>()) .awaitCompletion(1, TimeUnit.MINUTES); - - // remove the image tag from local docker so that it must be pulled before use - client.removeImageCmd(testImageNameWithTag).withForce(true).exec(); } } From d604f3ec45c9c61574b1728b801b70dba35eaa18 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 20 Apr 2020 10:18:14 +0200 Subject: [PATCH 1274/5615] Fix regression introduced in #2473 (missing `this` constructor call) (#2596) --- .../java/org/testcontainers/containers/MariaDBContainer.java | 2 +- .../main/java/org/testcontainers/containers/MySQLContainer.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java index fc876713dda..fb21dd9a1a9 100644 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java @@ -23,7 +23,7 @@ public class MariaDBContainer> extends JdbcD private static final String MY_CNF_CONFIG_OVERRIDE_PARAM_NAME = "TC_MY_CNF"; public MariaDBContainer() { - super(IMAGE + ":" + DEFAULT_TAG); + this(IMAGE + ":" + DEFAULT_TAG); } public MariaDBContainer(String dockerImageName) { diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java index 54f3ab08524..b8bbe225617 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java @@ -26,7 +26,7 @@ public class MySQLContainer> extends JdbcDatab private static final String MYSQL_ROOT_USER = "root"; public MySQLContainer() { - super(IMAGE + ":" + DEFAULT_TAG); + this(IMAGE + ":" + DEFAULT_TAG); } public MySQLContainer(String dockerImageName) { From 9c4756f73dde77b3df1e4b77aeccaa3de818e935 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 21 Apr 2020 10:52:14 +0200 Subject: [PATCH 1275/5615] Push Gradle Cache from GHA (#2597) * Push Gradle Cache from GHA * Update ci.yml --- .github/workflows/ci.yml | 4 ++++ settings.gradle | 14 +++++--------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3dbcc4c41fd..d488542d8a2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,6 +23,10 @@ jobs: ${{ runner.os }}-gradle-home-testmatrix- ${{ runner.os }}-gradle-home- - id: set-matrix + env: + # Since we override the tests executor, + # we should not push empty results to the cache + READ_ONLY_REMOTE_GRADLE_CACHE: true run: | TASKS=$(./gradlew --no-daemon --parallel -q testMatrix) echo $TASKS diff --git a/settings.gradle b/settings.gradle index 5ec35718ef6..4321c4ac4a4 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,21 +5,17 @@ buildscript { } } dependencies { - classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.9.0" + classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.10.0" classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.2.1" } - - configurations.classpath { - resolutionStrategy { - force 'com.amazonaws:aws-java-sdk-s3:1.11.582' - } - } } apply plugin: 'ch.myniva.s3-build-cache' apply plugin: 'com.gradle.enterprise' -ext.isMasterBuild = (System.getenv("CIRCLE_BRANCH") ?: System.getenv("BUILD_SOURCEBRANCHNAME")) == "master" +ext.isMasterBuild = false || + (System.getenv("GITHUB_REF") == "refs/heads/master") || + (System.getenv("BUILD_SOURCEBRANCHNAME") == "master") buildCache { local { @@ -31,7 +27,7 @@ buildCache { region = 'fra1' bucket = 'testcontainers' path = 'cache' - push = isMasterBuild + push = !(System.getenv("READ_ONLY_REMOTE_GRADLE_CACHE") || !isMasterBuild) headers = [ 'x-amz-acl': 'public-read' ] From 3f3f4b25381713ccd76459ab329d3c28b740dd5e Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 22 Apr 2020 17:17:54 +0200 Subject: [PATCH 1276/5615] Set custom ciMate key (#2601) --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d488542d8a2..df6fc96ea44 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,6 +60,7 @@ jobs: if: always() env: CIMATE_PROJECT_ID: 2348n4vl + CIMATE_CI_KEY: "CI / ${{ runner.os }} / ${{matrix.gradle_args}}" run: | wget -q https://get.cimate.io/release/linux/cimate chmod +x cimate @@ -86,6 +87,7 @@ jobs: if: always() env: CIMATE_PROJECT_ID: 2348n4vl + CIMATE_CI_KEY: examples run: | wget -q https://get.cimate.io/release/linux/cimate chmod +x cimate From 4d17308fda95947eaac1449551e47b619df0396b Mon Sep 17 00:00:00 2001 From: Fernando Mouco Date: Thu, 23 Apr 2020 13:02:27 +0100 Subject: [PATCH 1277/5615] CircleCI docs: fix invalid executor key (#2607) executorType is no longer a valid executor definition. The simplest form to designate using machine executor is `machine: true` --- .../continuous_integration/circle_ci.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/supported_docker_environment/continuous_integration/circle_ci.md b/docs/supported_docker_environment/continuous_integration/circle_ci.md index 1ec2a32b4cd..64e7ea131f2 100644 --- a/docs/supported_docker_environment/continuous_integration/circle_ci.md +++ b/docs/supported_docker_environment/continuous_integration/circle_ci.md @@ -9,7 +9,7 @@ Here is a sample CircleCI configuration that does a checkout of a project and ru # Check https://circleci.com/docs/2.0/language-java/ for more details # version: 2 -executorType: machine +machine: true jobs: build: steps: From 2c20417ac07b11e3255d7ce13dd5d83f3908df10 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 23 Apr 2020 13:03:06 +0100 Subject: [PATCH 1278/5615] Bump mongo-java-driver from 3.12.1 to 3.12.3 in /core (#2594) Bumps [mongo-java-driver](https://github.com/mongodb/mongo-java-driver) from 3.12.1 to 3.12.3. - [Release notes](https://github.com/mongodb/mongo-java-driver/releases) - [Commits](https://github.com/mongodb/mongo-java-driver/compare/r3.12.1...r3.12.3) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 09a6c590242..d99ca8c3676 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -157,7 +157,7 @@ dependencies { testCompile 'org.apache.httpcomponents:httpclient:4.5.9' testCompile 'redis.clients:jedis:3.2.0' testCompile 'com.rabbitmq:amqp-client:5.8.0' - testCompile 'org.mongodb:mongo-java-driver:3.12.1' + testCompile 'org.mongodb:mongo-java-driver:3.12.3' testCompile ('org.mockito:mockito-core:3.3.3') { exclude(module: 'hamcrest-core') } From 12de2b407238835bdc06169a6ac4b7bdcc98e028 Mon Sep 17 00:00:00 2001 From: Vladimir Orany Date: Thu, 23 Apr 2020 14:43:45 +0200 Subject: [PATCH 1279/5615] Allow using LocalStackContainer with AWS SDK v2 #1442 (#2579) Closes #1442 --- docs/modules/localstack.md | 14 +++ modules/localstack/build.gradle | 1 + .../localstack/LocalStackContainer.java | 118 ++++++++++++++++-- .../localstack/LocalstackContainerTest.java | 41 ++++-- 4 files changed, 152 insertions(+), 22 deletions(-) diff --git a/docs/modules/localstack.md b/docs/modules/localstack.md index bf520b1fac6..4ff46233b05 100644 --- a/docs/modules/localstack.md +++ b/docs/modules/localstack.md @@ -13,6 +13,7 @@ public LocalStackContainer localstack = new LocalStackContainer() @Test public void someTestMethod() { + // AWS SDK v1 AmazonS3 s3 = AmazonS3ClientBuilder .standard() .withEndpointConfiguration(localstack.getEndpointConfiguration(S3)) @@ -21,6 +22,19 @@ public void someTestMethod() { s3.createBucket("foo"); s3.putObject("foo", "bar", "baz"); + + // AWS SDK v2 + S3Client s3 = S3Client + .builder() + .endpointOverride(localstack.getEndpointOverride(LocalStackContainer.Service.S3)) + .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create( + localstack.getAccessKey(), localstack.getSecretKey() + ))) + .region(Region.of(localstack.getRegion())) + .build(); + + s3.createBucket(b -> b.bucket("foo")); + s3.putObject(b -> b.bucket("foo").key("bar"), RequestBody.fromBytes("baz".getBytes())); ``` Environment variables listed in [Localstack's README](https://github.com/localstack/localstack#configurations) may be used to customize Localstack's configuration. diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index c8bf561b6d1..cd6f3d9f86c 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,4 +7,5 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.683' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.636' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.762' + testCompile 'software.amazon.awssdk:s3:2.11.10' } diff --git a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java index 0d8c3627f6c..c649d322ead 100644 --- a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java +++ b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java @@ -13,6 +13,8 @@ import org.testcontainers.utility.TestcontainersConfiguration; import java.net.InetAddress; +import java.net.URI; +import java.net.URISyntaxException; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; @@ -90,7 +92,16 @@ public LocalStackContainer withServices(Service... services) { .withCredentials(localstack.getDefaultCredentialsProvider()) .build(); - * + * or for AWS SDK v2 + *

S3Client s3 = S3Client
+             .builder()
+             .endpointOverride(localstack.getEndpointOverride(LocalStackContainer.Service.S3))
+             .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create(
+                localstack.getAccessKey(), localstack.getSecretKey()
+             )))
+             .region(Region.of(localstack.getRegion()))
+             .build()
+     
*

Please note that this method is only intended to be used for configuring AWS SDK clients * that are running on the test host. If other containers need to call this one, they should be configured * specifically to do so using a Docker network and appropriate addressing.

@@ -99,20 +110,41 @@ public LocalStackContainer withServices(Service... services) { * @return an {@link AwsClientBuilder.EndpointConfiguration} */ public AwsClientBuilder.EndpointConfiguration getEndpointConfiguration(Service service) { - final String address = getContainerIpAddress(); - String ipAddress = address; + return new AwsClientBuilder.EndpointConfiguration(getEndpointOverride(service).toString(), getRegion()); + } + + /** + * Provides an endpoint override that is preconfigured to communicate with a given simulated service. + * The provided endpoint override should be set in the AWS Java SDK v2 when building a client, e.g.: + *
S3Client s3 = S3Client
+             .builder()
+             .endpointOverride(localstack.getEndpointOverride(LocalStackContainer.Service.S3))
+             .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create(
+             localstack.getAccessKey(), localstack.getSecretKey()
+             )))
+             .region(Region.of(localstack.getRegion()))
+             .build()
+             
+ *

Please note that this method is only intended to be used for configuring AWS SDK clients + * that are running on the test host. If other containers need to call this one, they should be configured + * specifically to do so using a Docker network and appropriate addressing.

+ * + * @param service the service that is to be accessed + * @return an {@link URI} endpoint override + */ + public URI getEndpointOverride(Service service) { try { + final String address = getContainerIpAddress(); + String ipAddress = address; // resolve IP address and use that as the endpoint so that path-style access is automatically used for S3 ipAddress = InetAddress.getByName(address).getHostAddress(); - } catch (UnknownHostException ignored) { - - } - - return new AwsClientBuilder.EndpointConfiguration( - "http://" + + return new URI("http://" + ipAddress + ":" + - getMappedPort(service.getPort()), "us-east-1"); + getMappedPort(service.getPort())); + } catch (UnknownHostException | URISyntaxException e) { + throw new IllegalStateException("Cannot obtain endpoint URL", e); + } } /** @@ -124,10 +156,74 @@ public AwsClientBuilder.EndpointConfiguration getEndpointConfiguration(Service s .withCredentials(localstack.getDefaultCredentialsProvider()) .build(); + * or for AWS SDK v2 you can use {@link #getAccessKey()}, {@link #getSecretKey()} directly: + *
S3Client s3 = S3Client
+             .builder()
+             .endpointOverride(localstack.getEndpointOverride(LocalStackContainer.Service.S3))
+             .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create(
+             localstack.getAccessKey(), localstack.getSecretKey()
+             )))
+             .region(Region.of(localstack.getRegion()))
+             .build()
+     
* @return an {@link AWSCredentialsProvider} */ public AWSCredentialsProvider getDefaultCredentialsProvider() { - return new AWSStaticCredentialsProvider(new BasicAWSCredentials("accesskey", "secretkey")); + return new AWSStaticCredentialsProvider(new BasicAWSCredentials(getAccessKey(), getSecretKey())); + } + + /** + * Provides a default access key that is preconfigured to communicate with a given simulated service. + * The access key can be used to construct AWS SDK v2 clients: + *
S3Client s3 = S3Client
+             .builder()
+             .endpointOverride(localstack.getEndpointOverride(LocalStackContainer.Service.S3))
+             .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create(
+             localstack.getAccessKey(), localstack.getSecretKey()
+             )))
+             .region(Region.of(localstack.getRegion()))
+             .build()
+     
+ * @return a default access key + */ + public String getAccessKey() { + return "accesskey"; + } + + /** + * Provides a default secret key that is preconfigured to communicate with a given simulated service. + * The secret key can be used to construct AWS SDK v2 clients: + *
S3Client s3 = S3Client
+             .builder()
+             .endpointOverride(localstack.getEndpointOverride(LocalStackContainer.Service.S3))
+             .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create(
+             localstack.getAccessKey(), localstack.getSecretKey()
+             )))
+             .region(Region.of(localstack.getRegion()))
+             .build()
+     
+ * @return a default secret key + */ + public String getSecretKey() { + return "secretkey"; + } + + /** + * Provides a default region that is preconfigured to communicate with a given simulated service. + * The region can be used to construct AWS SDK v2 clients: + *
S3Client s3 = S3Client
+             .builder()
+             .endpointOverride(localstack.getEndpointOverride(LocalStackContainer.Service.S3))
+             .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create(
+             localstack.getAccessKey(), localstack.getSecretKey()
+             )))
+             .region(Region.of(localstack.getRegion()))
+             .build()
+     
+ * @return a default region + */ + public String getRegion() { + return "us-east-1"; } @RequiredArgsConstructor diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java index 034ff2f0914..ee5945d0aff 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java @@ -4,8 +4,6 @@ import com.amazonaws.services.logs.AWSLogs; import com.amazonaws.services.logs.AWSLogsClientBuilder; import com.amazonaws.services.logs.model.CreateLogGroupRequest; -import com.amazonaws.services.logs.model.CreateLogGroupResult; -import com.amazonaws.services.logs.model.DescribeLogGroupsRequest; import com.amazonaws.services.logs.model.LogGroup; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3ClientBuilder; @@ -19,7 +17,6 @@ import org.apache.commons.io.IOUtils; import org.junit.Assert; import org.junit.ClassRule; -import org.junit.Ignore; import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; @@ -27,10 +24,15 @@ import org.testcontainers.containers.Container; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; import java.io.IOException; import java.nio.charset.Charset; import java.util.List; +import java.util.Optional; import static org.hamcrest.CoreMatchers.containsString; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; @@ -67,24 +69,41 @@ public void s3TestOverBridgeNetwork() throws IOException { .withCredentials(localstack.getDefaultCredentialsProvider()) .build(); - s3.createBucket("foo"); - s3.putObject("foo", "bar", "baz"); + final String bucketName = "foo"; + s3.createBucket(bucketName); + s3.putObject(bucketName, "bar", "baz"); final List buckets = s3.listBuckets(); - assertEquals("The created bucket is present", 1, buckets.size()); - final Bucket bucket = buckets.get(0); + final Optional maybeBucket = buckets.stream().filter(b -> b.getName().equals(bucketName)).findFirst(); + assertTrue("The created bucket is present", maybeBucket.isPresent()); + final Bucket bucket = maybeBucket.get(); - assertEquals("The created bucket has the right name", "foo", bucket.getName()); - assertEquals("The created bucket has the right name", "foo", bucket.getName()); + assertEquals("The created bucket has the right name", bucketName, bucket.getName()); - final ObjectListing objectListing = s3.listObjects("foo"); + final ObjectListing objectListing = s3.listObjects(bucketName); assertEquals("The created bucket has 1 item in it", 1, objectListing.getObjectSummaries().size()); - final S3Object object = s3.getObject("foo", "bar"); + final S3Object object = s3.getObject(bucketName, "bar"); final String content = IOUtils.toString(object.getObjectContent(), Charset.forName("UTF-8")); assertEquals("The object can be retrieved", "baz", content); } + @Test + public void s3TestUsingAwsSdkV2() { + S3Client s3 = S3Client + .builder() + .endpointOverride(localstack.getEndpointOverride(LocalStackContainer.Service.S3)) + .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create( + localstack.getAccessKey(), localstack.getSecretKey() + ))) + .region(Region.of(localstack.getRegion())) + .build(); + + final String bucketName = "foov2"; + s3.createBucket(b -> b.bucket(bucketName)); + assertTrue("New bucket was created", s3.listBuckets().buckets().stream().anyMatch(b -> b.name().equals(bucketName))); + } + @Test public void sqsTestOverBridgeNetwork() { AmazonSQS sqs = AmazonSQSClientBuilder.standard() From 1e803b8f96272f463715f8560736cc4596b23a52 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 23 Apr 2020 13:45:07 +0100 Subject: [PATCH 1280/5615] Use relative path for temp dirs in test (#2604) --- .../utility/AuthenticatedImagePullTest.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java index 56945643a88..5f4417f218b 100644 --- a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java +++ b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java @@ -110,8 +110,7 @@ public void testThatAuthLocatorIsUsedForContainerCreation() { @Test public void testThatAuthLocatorIsUsedForDockerfileBuild() throws IOException { // Prepare a simple temporary Dockerfile which requires our custom private image - Path tempContext = Files.createTempDirectory(Paths.get("."), this.getClass().getSimpleName() + "-test-"); - Path tempFile = Files.createTempFile(tempContext, "test", ".Dockerfile"); + Path tempFile = getLocalTempFile(".Dockerfile"); String dockerFileContent = "FROM " + testImageNameWithTag; Files.write(tempFile, dockerFileContent.getBytes()); @@ -130,8 +129,7 @@ public void testThatAuthLocatorIsUsedForDockerfileBuild() throws IOException { @Test public void testThatAuthLocatorIsUsedForDockerComposePull() throws IOException { // Prepare a simple temporary Docker Compose manifest which requires our custom private image - Path tempContext = Files.createTempDirectory(Paths.get("."), this.getClass().getSimpleName() + "-test-"); - Path tempFile = Files.createTempFile(tempContext, "test", ".docker-compose.yml"); + Path tempFile = getLocalTempFile(".docker-compose.yml"); @Language("yaml") String composeFileContent = "version: '2.0'\n" + "services:\n" + @@ -153,6 +151,18 @@ public void testThatAuthLocatorIsUsedForDockerComposePull() throws IOException { } } + private Path getLocalTempFile(String s) throws IOException { + Path projectRoot = Paths.get("."); + Path tempDirectory = Files.createTempDirectory(projectRoot, this.getClass().getSimpleName() + "-test-"); + Path relativeTempDirectory = projectRoot.relativize(tempDirectory); + Path tempFile = Files.createTempFile(relativeTempDirectory, "test", s); + + tempDirectory.toFile().deleteOnExit(); + tempFile.toFile().deleteOnExit(); + + return tempFile; + } + private static void putImageInRegistry() throws InterruptedException { // It doesn't matter which image we use for this test, but use one that's likely to have been pulled already final String dummySourceImage = TestcontainersConfiguration.getInstance().getRyukImage(); From 5fbf102a6532cb9e14c87cb15d4577c30fac026f Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 23 Apr 2020 16:19:52 +0100 Subject: [PATCH 1281/5615] Update version --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index e3cae4c5da8..708fbe06883 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -88,4 +88,4 @@ nav: - contributing.md - contributing_docs.md extra: - latest_version: 1.14.0 + latest_version: 1.14.1 From 2281f8ff697b8e74f558412f814b5247d13d6759 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 24 Apr 2020 05:23:36 +0000 Subject: [PATCH 1282/5615] Bump influxdb-java from 2.17 to 2.18 in /modules/influxdb (#2592) --- modules/influxdb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/influxdb/build.gradle b/modules/influxdb/build.gradle index d349e06da12..02af2e924a9 100644 --- a/modules/influxdb/build.gradle +++ b/modules/influxdb/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: InfluxDB" dependencies { compile project(':testcontainers') - compileOnly 'org.influxdb:influxdb-java:2.17' + compileOnly 'org.influxdb:influxdb-java:2.18' testCompile 'org.influxdb:influxdb-java:2.17' } From fdad5ce673bfd11a35f627bbd08fbc9af08e090c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 25 Apr 2020 01:31:52 +0000 Subject: [PATCH 1283/5615] Bump s3 from 2.11.10 to 2.13.2 in /modules/localstack (#2610) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index cd6f3d9f86c..4e882514e81 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,5 +7,5 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.683' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.636' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.762' - testCompile 'software.amazon.awssdk:s3:2.11.10' + testCompile 'software.amazon.awssdk:s3:2.13.2' } From d5a79f6f777ebc134559046b5f16dbf500ab6884 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 26 Apr 2020 14:58:35 +0100 Subject: [PATCH 1284/5615] Bump ipcsocket from 1.0.0 to 1.0.1 in /core (#2525) --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index d99ca8c3676..78e7b553606 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -133,7 +133,7 @@ dependencies { compile "net.java.dev.jna:jna-platform:5.5.0" - shaded ('org.scala-sbt.ipcsocket:ipcsocket:1.0.0') { + shaded ('org.scala-sbt.ipcsocket:ipcsocket:1.0.1') { exclude(group: "net.java.dev.jna") } From 4740cbe4b32667cec9b8d3028bb10319c718b062 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 26 Apr 2020 15:07:39 +0100 Subject: [PATCH 1285/5615] Bump pulsar-client from 2.5.0 to 2.5.1 in /modules/pulsar (#2609) --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 88d783fc221..4f459d9a97e 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Pulsar" dependencies { compile project(':testcontainers') - testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.5.0' + testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.5.1' testCompile group: 'org.assertj', name: 'assertj-core', version: '3.15.0' } From be41d15f5b9bcd133be58bd8ddd6a9a2b99e8a66 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 26 Apr 2020 15:08:17 +0100 Subject: [PATCH 1286/5615] Bump aws-java-sdk-logs from 1.11.762 to 1.11.768 in /modules/localstack (#2611) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 4e882514e81..cd7678ccbf5 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,6 +6,6 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.683' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.683' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.636' - testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.762' + testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.768' testCompile 'software.amazon.awssdk:s3:2.13.2' } From 8e096cc0d27cdab6f1b07c6a2488a26fea559a35 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 26 Apr 2020 15:08:32 +0100 Subject: [PATCH 1287/5615] Replace legacy orchardup/mysql with mysql:5.7.22 in docker compose test (#2602) --- core/src/test/resources/compose-test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/test/resources/compose-test.yml b/core/src/test/resources/compose-test.yml index 7436db7d211..a79a4c0b25b 100644 --- a/core/src/test/resources/compose-test.yml +++ b/core/src/test/resources/compose-test.yml @@ -1,6 +1,6 @@ redis: image: redis db: - image: orchardup/mysql + image: mysql:5.7.22 environment: - MYSQL_DATABASE: wordpress + MYSQL_RANDOM_ROOT_PASSWORD: "true" From dbe4b0f3d56bebbb09089633b65772a4aa54842b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 26 Apr 2020 15:10:20 +0100 Subject: [PATCH 1288/5615] Bump amqp-client from 5.8.0 to 5.9.0 in /core (#2593) --- core/build.gradle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 78e7b553606..9df3a024192 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -156,8 +156,9 @@ dependencies { testCompile 'org.apache.httpcomponents:httpclient:4.5.9' testCompile 'redis.clients:jedis:3.2.0' - testCompile 'com.rabbitmq:amqp-client:5.8.0' + testCompile 'com.rabbitmq:amqp-client:5.9.0' testCompile 'org.mongodb:mongo-java-driver:3.12.3' + testCompile ('org.mockito:mockito-core:3.3.3') { exclude(module: 'hamcrest-core') } From b54f162a6578fa01c44c04381787b7b9103e64c1 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2020 05:23:31 +0000 Subject: [PATCH 1289/5615] Bump kafka-clients from 2.4.1 to 2.5.0 in /modules/kafka (#2576) --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index cd6745e1eae..355c44eab1a 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Kafka" dependencies { compile project(':testcontainers') - testCompile 'org.apache.kafka:kafka-clients:2.4.1' + testCompile 'org.apache.kafka:kafka-clients:2.5.0' testCompile 'org.assertj:assertj-core:3.15.0' testCompile 'com.google.guava:guava:23.0' } From bc3a26680886410bc0c59186092406439ab9c4c3 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2020 15:56:43 +0200 Subject: [PATCH 1290/5615] Bump mysql-connector-java from 8.0.19 to 8.0.20 in /modules/spock (#2624) Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.19 to 8.0.20. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/commits) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index d091ce068dc..40f765f255c 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -16,7 +16,7 @@ dependencies { testCompile 'org.apache.httpcomponents:httpclient:4.5.12' testRuntime 'org.postgresql:postgresql:42.2.12' - testRuntime 'mysql:mysql-connector-java:8.0.19' + testRuntime 'mysql:mysql-connector-java:8.0.20' } sourceJar { From aefb82281dca4802d9aaedb48535a313938ec373 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2020 16:02:15 +0200 Subject: [PATCH 1291/5615] Bump HikariCP from 3.4.2 to 3.4.3 in /modules/spock (#2625) Bumps [HikariCP](https://github.com/brettwooldridge/HikariCP) from 3.4.2 to 3.4.3. - [Release notes](https://github.com/brettwooldridge/HikariCP/releases) - [Changelog](https://github.com/brettwooldridge/HikariCP/blob/dev/CHANGES) - [Commits](https://github.com/brettwooldridge/HikariCP/compare/HikariCP-3.4.2...HikariCP-3.4.3) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 40f765f255c..7b1780391b5 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -12,7 +12,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') - testCompile 'com.zaxxer:HikariCP:3.4.2' + testCompile 'com.zaxxer:HikariCP:3.4.3' testCompile 'org.apache.httpcomponents:httpclient:4.5.12' testRuntime 'org.postgresql:postgresql:42.2.12' From 746ee1bc4a8a92bb1e66d9d819c69ef567ca97f0 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2020 16:03:50 +0200 Subject: [PATCH 1292/5615] Bump HikariCP from 3.4.2 to 3.4.3 in /modules/junit-jupiter (#2622) Bumps [HikariCP](https://github.com/brettwooldridge/HikariCP) from 3.4.2 to 3.4.3. - [Release notes](https://github.com/brettwooldridge/HikariCP/releases) - [Changelog](https://github.com/brettwooldridge/HikariCP/blob/dev/CHANGES) - [Commits](https://github.com/brettwooldridge/HikariCP/compare/HikariCP-3.4.2...HikariCP-3.4.3) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 86d9b30930e..efc65cf57db 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -6,7 +6,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') - testCompile 'com.zaxxer:HikariCP:3.4.2' + testCompile 'com.zaxxer:HikariCP:3.4.3' testCompile 'redis.clients:jedis:3.2.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.11' testCompile ('org.mockito:mockito-core:3.3.3') { From 89d77fb58bd90ab1879a24a268e0feac31b86c3d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 28 Apr 2020 09:39:48 +0200 Subject: [PATCH 1293/5615] Bump s3 from 2.13.2 to 2.13.4 in /modules/localstack (#2630) Bumps s3 from 2.13.2 to 2.13.4. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index cd7678ccbf5..343895fee53 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,5 +7,5 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.683' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.636' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.768' - testCompile 'software.amazon.awssdk:s3:2.13.2' + testCompile 'software.amazon.awssdk:s3:2.13.4' } From 8c3a9c932f46c292ee5995a03fb6f9c9fee82c2d Mon Sep 17 00:00:00 2001 From: Benedikt Ritter Date: Thu, 30 Apr 2020 09:53:33 +0200 Subject: [PATCH 1294/5615] Fix typo in jupiter docs (#2636) --- docs/test_framework_integration/junit_5.md | 2 +- .../java/org/testcontainers/junit/jupiter/Testcontainers.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/test_framework_integration/junit_5.md b/docs/test_framework_integration/junit_5.md index 371240a8d54..11798484b0f 100644 --- a/docs/test_framework_integration/junit_5.md +++ b/docs/test_framework_integration/junit_5.md @@ -19,7 +19,7 @@ methods (methods on the `Startable` interface). Containers declared as static fi methods. They will be started only once before any test method is executed and stopped after the last test method has executed. Containers declared as instance fields will be started and stopped for every test method. -**Note:** This extension has only be tested with sequential test execution. Using it with parallel test execution is +**Note:** This extension has only been tested with sequential test execution. Using it with parallel test execution is unsupported and may have unintended side effects. *Example:* diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Testcontainers.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Testcontainers.java index 8e05dfdc576..db8273563a7 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Testcontainers.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Testcontainers.java @@ -23,7 +23,7 @@ * the test hierarchy as well. All subclasses will automatically inherit * support for the extension.

* - *

Note: This extension has only be tested with sequential + *

Note: This extension has only been tested with sequential * test execution. Using it with parallel test execution is unsupported and * may have unintended side effects.

* From 2dac7ba4bba73b7c44d71856b20c9b1476f344ce Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 1 May 2020 18:52:44 +0100 Subject: [PATCH 1295/5615] Bump aws-java-sdk-logs from 1.11.768 to 1.11.773 in /modules/localstack (#2641) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 343895fee53..82cf5c1f841 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,6 +6,6 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.683' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.683' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.636' - testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.768' + testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.773' testCompile 'software.amazon.awssdk:s3:2.13.4' } From afa89ecf0888504673fa7c29dec80b3fd6096f23 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 1 May 2020 18:52:56 +0100 Subject: [PATCH 1296/5615] Bump mysql-connector-java from 8.0.19 to 8.0.20 in /modules/junit-jupiter (#2620) --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index efc65cf57db..5046008c7fa 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -16,7 +16,7 @@ dependencies { testCompile 'org.junit.jupiter:junit-jupiter-params:5.6.2' testRuntime 'org.postgresql:postgresql:42.2.12' - testRuntime 'mysql:mysql-connector-java:8.0.19' + testRuntime 'mysql:mysql-connector-java:8.0.20' testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.6.2' } From f0e6e55b7550665402d0b7afeb656e80910be1a9 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 1 May 2020 18:58:29 +0100 Subject: [PATCH 1297/5615] Bump okhttp from 3.14.7 to 3.14.8 in /examples (#2645) --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index e7bed23509d..96dd03c602b 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' - implementation 'com.squareup.okhttp3:okhttp:3.14.7' + implementation 'com.squareup.okhttp3:okhttp:3.14.8' implementation 'org.json:json:20180813' testImplementation 'org.postgresql:postgresql:42.2.12' testImplementation 'ch.qos.logback:logback-classic:1.2.3' From 4b385cde15aaeb0a4c74065cd424ddebf1791c55 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 1 May 2020 18:58:47 +0100 Subject: [PATCH 1298/5615] Bump testcontainers from 1.14.0 to 1.14.1 in /core (#2623) --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 9df3a024192..bbf57a34a70 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -110,7 +110,7 @@ if (!org.gradle.internal.os.OperatingSystem.current().isWindows()) { } dependencies { - baseline 'org.testcontainers:testcontainers:1.14.0', { + baseline 'org.testcontainers:testcontainers:1.14.1', { exclude group: "*", module: "*" } From 6fb689f8938518a144640e4c690ef56cbe767870 Mon Sep 17 00:00:00 2001 From: Konstantin Lutovich Date: Fri, 1 May 2020 20:22:30 +0200 Subject: [PATCH 1299/5615] Add a getter for Toxiproxy exposed port (#2513) --- docs/modules/toxiproxy.md | 15 +++-- .../containers/ToxiproxyContainer.java | 16 +++++- .../containers/ToxiproxyTest.java | 55 +++++++++++++------ 3 files changed, 65 insertions(+), 21 deletions(-) diff --git a/docs/modules/toxiproxy.md b/docs/modules/toxiproxy.md index 9e09d8b387b..8ffb910d6c1 100644 --- a/docs/modules/toxiproxy.md +++ b/docs/modules/toxiproxy.md @@ -29,13 +29,21 @@ We do this as follows: [Starting proxying connections to a target container](../../modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java) inside_block:obtainProxyObject -Then, to establish a connection via Toxiproxy, we obtain **Toxiproxy's** proxy host IP and port: +To establish a connection from the test code (on the host machine) to the target container via Toxiproxy, we obtain **Toxiproxy's** proxy host IP and port: -[Obtaining proxied host and port](../../modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java) inside_block:obtainProxiedHostAndPort +[Obtaining proxied host and port for connections from the host machine](../../modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java) inside_block:obtainProxiedHostAndPortForHostMachine -Code under test, or other containers, should connect to this proxied host IP and port. +Code under test should connect to this proxied host IP and port. + +To establish a connection from a different container on the same network to the target container via Toxiproxy, we use **Toxiproxy's** network alias and original port: + + +[Obtaining proxied host and port for connections from a different container](../../modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java) inside_block:obtainProxiedHostAndPortForDifferentContainer + + +Other containers should connect to this proxied host and port. Having done this, it is possible to trigger failure conditions ('Toxics') through the `proxy.toxics()` object: @@ -82,4 +90,3 @@ testCompile "org.testcontainers:toxiproxy:{{latest_version}}" This module was inspired by a [hotels.com blog post](https://medium.com/hotels-com-technology/i-dont-know-about-resilience-testing-and-so-can-you-b3c59d80012d). [toxiproxy-java](https://github.com/trekawek/toxiproxy-java) is used to help control failure conditions. - diff --git a/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java b/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java index df4bf6e688d..1b631fa9d6d 100644 --- a/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java +++ b/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java @@ -87,7 +87,8 @@ public ContainerProxy getProxy(String hostname, int port) { } final Proxy proxy = client.createProxy(upstream, "0.0.0.0:" + toxiPort, upstream); - return new ContainerProxy(proxy, getContainerIpAddress(), getMappedPort(toxiPort)); + final int mappedPort = getMappedPort(toxiPort); + return new ContainerProxy(proxy, getContainerIpAddress(), mappedPort, toxiPort); } catch (IOException e) { throw new RuntimeException("Proxy could not be created", e); } @@ -99,8 +100,21 @@ public static class ContainerProxy { private static final String CUT_CONNECTION_DOWNSTREAM = "CUT_CONNECTION_DOWNSTREAM"; private static final String CUT_CONNECTION_UPSTREAM = "CUT_CONNECTION_UPSTREAM"; private final Proxy toxi; + /** + * The IP address that this proxy container may be reached on from the host machine. + */ @Getter private final String containerIpAddress; + /** + * The mapped port of this proxy. This is a port of the host machine. It can be used to + * access the Toxiproxy container from the host machine. + */ @Getter private final int proxyPort; + /** + * The original (exposed) port of this proxy. This is a port of the Toxiproxy Docker + * container. It can be used to access this container from a different Docker container + * on the same network. + */ + @Getter private final int originalProxyPort; private boolean isCurrentlyCut; public ToxicList toxics() { diff --git a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java index c96e2efd19e..366f425b5b5 100644 --- a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java +++ b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java @@ -1,10 +1,9 @@ package org.testcontainers.containers; import eu.rekawek.toxiproxy.model.ToxicDirection; +import java.time.Duration; import org.junit.Rule; import org.junit.Test; -import org.testcontainers.testsupport.Flaky; -import org.testcontainers.testsupport.FlakyTestJUnit4RetryRule; import redis.clients.jedis.Jedis; import redis.clients.jedis.exceptions.JedisConnectionException; @@ -15,12 +14,18 @@ public class ToxiproxyTest { + private static final Duration JEDIS_TIMEOUT = Duration.ofSeconds(10); + // creatingProxy { + // An alias that can be used to resolve the Toxiproxy container by name in the network it is connected to. + // It can be used as a hostname of the Toxiproxy container by other containers in the same network. + private static final String TOXIPROXY_NETWORK_ALIAS = "toxiproxy"; + // Create a common docker network so that containers can communicate @Rule public Network network = Network.newNetwork(); - // the target container - this could be anything + // The target container - this could be anything @Rule public GenericContainer redis = new GenericContainer("redis:5.0.4") .withExposedPorts(6379) @@ -29,15 +34,13 @@ public class ToxiproxyTest { // Toxiproxy container, which will be used as a TCP proxy @Rule public ToxiproxyContainer toxiproxy = new ToxiproxyContainer() - .withNetwork(network); + .withNetwork(network) + .withNetworkAliases(TOXIPROXY_NETWORK_ALIAS); // } - @Rule - public FlakyTestJUnit4RetryRule retry = new FlakyTestJUnit4RetryRule(); - @Test public void testDirect() { - final Jedis jedis = new Jedis(redis.getContainerIpAddress(), redis.getFirstMappedPort()); + final Jedis jedis = createJedis(redis.getContainerIpAddress(), redis.getFirstMappedPort()); jedis.set("somekey", "somevalue"); final String s = jedis.get("somekey"); @@ -45,18 +48,17 @@ public void testDirect() { } @Test - @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1769", reviewDate = "2020-03-01") public void testLatencyViaProxy() throws IOException { // obtainProxyObject { final ToxiproxyContainer.ContainerProxy proxy = toxiproxy.getProxy(redis, 6379); // } - // obtainProxiedHostAndPort { + // obtainProxiedHostAndPortForHostMachine { final String ipAddressViaToxiproxy = proxy.getContainerIpAddress(); final int portViaToxiproxy = proxy.getProxyPort(); // } - final Jedis jedis = new Jedis(ipAddressViaToxiproxy, portViaToxiproxy); + final Jedis jedis = createJedis(ipAddressViaToxiproxy, portViaToxiproxy); jedis.set("somekey", "somevalue"); checkCallWithLatency(jedis, "without interference", 0, 250); @@ -72,10 +74,9 @@ public void testLatencyViaProxy() throws IOException { } @Test - @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1769", reviewDate = "2020-03-01") public void testConnectionCut() { final ToxiproxyContainer.ContainerProxy proxy = toxiproxy.getProxy(redis, 6379); - final Jedis jedis = new Jedis(proxy.getContainerIpAddress(), proxy.getProxyPort()); + final Jedis jedis = createJedis(proxy.getContainerIpAddress(), proxy.getProxyPort()); jedis.set("somekey", "somevalue"); assertEquals("access to the container works OK before cutting the connection", "somevalue", jedis.get("somekey")); @@ -97,7 +98,6 @@ public void testConnectionCut() { } @Test - @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1769", reviewDate = "2020-03-01") public void testMultipleProxiesCanBeCreated() { try (GenericContainer secondRedis = new GenericContainer("redis:5.0.4") .withExposedPorts(6379) @@ -108,8 +108,8 @@ public void testMultipleProxiesCanBeCreated() { final ToxiproxyContainer.ContainerProxy firstProxy = toxiproxy.getProxy(redis, 6379); final ToxiproxyContainer.ContainerProxy secondProxy = toxiproxy.getProxy(secondRedis, 6379); - final Jedis firstJedis = new Jedis(firstProxy.getContainerIpAddress(), firstProxy.getProxyPort()); - final Jedis secondJedis = new Jedis(secondProxy.getContainerIpAddress(), secondProxy.getProxyPort()); + final Jedis firstJedis = createJedis(firstProxy.getContainerIpAddress(), firstProxy.getProxyPort()); + final Jedis secondJedis = createJedis(secondProxy.getContainerIpAddress(), secondProxy.getProxyPort()); firstJedis.set("somekey", "somevalue"); secondJedis.set("somekey", "somevalue"); @@ -125,6 +125,25 @@ public void testMultipleProxiesCanBeCreated() { } } + @Test + public void testOriginalAndMappedPorts() { + final ToxiproxyContainer.ContainerProxy proxy = toxiproxy.getProxy("hostname", 7070); + // obtainProxiedHostAndPortForDifferentContainer { + final String hostViaToxiproxy = TOXIPROXY_NETWORK_ALIAS; + final int portViaToxiproxy = proxy.getOriginalProxyPort(); + // } + assertEquals("host is correct", TOXIPROXY_NETWORK_ALIAS, hostViaToxiproxy); + assertEquals("original port is correct", 8666, portViaToxiproxy); + + final ToxiproxyContainer.ContainerProxy proxy1 = toxiproxy.getProxy("hostname1", 8080); + assertEquals("original port is correct", 8667, proxy1.getOriginalProxyPort()); + assertEquals("mapped port is correct", toxiproxy.getMappedPort(proxy1.getOriginalProxyPort()), proxy1.getProxyPort()); + + final ToxiproxyContainer.ContainerProxy proxy2 = toxiproxy.getProxy("hostname2", 9090); + assertEquals("original port is correct", 8668, proxy2.getOriginalProxyPort()); + assertEquals("mapped port is correct", toxiproxy.getMappedPort(proxy2.getOriginalProxyPort()), proxy2.getProxyPort()); + } + private void checkCallWithLatency(Jedis jedis, final String description, int expectedMinLatency, long expectedMaxLatency) { final long start = System.currentTimeMillis(); String s = jedis.get("somekey"); @@ -135,4 +154,8 @@ private void checkCallWithLatency(Jedis jedis, final String description, int exp assertTrue(format("%s there is at least %dms latency", description, expectedMinLatency), duration >= expectedMinLatency); assertTrue(format("%s there is no more than %dms latency", description, expectedMaxLatency), duration < expectedMaxLatency); } + + private static Jedis createJedis(String host, int port) { + return new Jedis(host, port, Math.toIntExact(JEDIS_TIMEOUT.toMillis())); + } } From 88849acbec0d2d6511c8e112f053253eb384f4fd Mon Sep 17 00:00:00 2001 From: sumitanvekar <2293969+sumitanvekar@users.noreply.github.com> Date: Sat, 2 May 2020 00:13:32 +0530 Subject: [PATCH 1300/5615] Example and docs for ability to copy to and from container for #1164 (#1989) --- .../junit/CopyFileToContainerTest.java | 30 ++++++++++++++++--- docs/features/files.md | 16 ++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java b/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java index ed6ee561505..1e230ec605b 100644 --- a/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java @@ -1,32 +1,54 @@ package org.testcontainers.junit; +import com.google.common.io.Files; +import com.google.common.io.Resources; import org.junit.Test; import org.testcontainers.containers.BindMode; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.SelinuxContext; import org.testcontainers.utility.MountableFile; +import java.io.File; import java.io.IOException; import java.util.Map; +import static org.junit.Assert.assertArrayEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; public class CopyFileToContainerTest { - private static String containerPath = "/tmp/mappable-resource/"; + public static final String destinationOnHost = "/tmp/test-resource-in-host.txt"; + private static String directoryInContainer = "/tmp/mappable-resource/"; private static String fileName = "test-resource.txt"; @Test public void checkFileCopied() throws IOException, InterruptedException { try ( + // copyToContainer { GenericContainer container = new GenericContainer() .withCommand("sleep", "3000") - .withCopyFileToContainer(MountableFile.forClasspathResource("/mappable-resource/"), containerPath) + // withCopyFileToContainer ensure that a file or directory will be copied to the container + // before starting. In this case, we map a classpath directory to a directory inside the container + .withCopyFileToContainer(MountableFile.forClasspathResource("/mappable-resource/"), directoryInContainer) + // } ) { container.start(); - String filesList = container.execInContainer("ls", "/tmp/mappable-resource").getStdout(); + + // at this point directoryInContainer should exist, and should contain copies of file(s) + String filesList = container.execInContainer("ls", directoryInContainer).getStdout(); assertTrue("file list contains the file", filesList.contains(fileName)); + + // ... + + // copyFromContainer { + container.copyFileFromContainer(directoryInContainer + fileName, destinationOnHost); + // } } + + assertArrayEquals( + Files.toByteArray(new File(destinationOnHost)), + Resources.toByteArray(CopyFileToContainerTest.class.getResource("/mappable-resource/" + fileName)) + ); } @Test @@ -34,7 +56,7 @@ public void shouldUseCopyForReadOnlyClasspathResources() throws Exception { try ( GenericContainer container = new GenericContainer() .withCommand("sleep", "3000") - .withClasspathResourceMapping("/mappable-resource/", containerPath, BindMode.READ_ONLY) + .withClasspathResourceMapping("/mappable-resource/", directoryInContainer, BindMode.READ_ONLY) ) { container.start(); String filesList = container.execInContainer("ls", "/tmp/mappable-resource").getStdout(); diff --git a/docs/features/files.md b/docs/features/files.md index a74b1add134..47240f82cd2 100644 --- a/docs/features/files.md +++ b/docs/features/files.md @@ -9,3 +9,19 @@ new GenericContainer(...) "/etc/redis.conf", BindMode.READ_ONLY) ``` + +## Copying files to and from containers + +Files can be copied into the container before startup, or can be copied from the container after the container has started. + +### Copying to a container before startup + + +[Copying files to a container](../../core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java) inside_block:copyToContainer + + +### Copying a file from a running container + + +[Copying files from a container](../../core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java) inside_block:copyFromContainer + From cae51cd6fc97aeab8be8f645fbcc26dda9513823 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 2 May 2020 10:07:30 +0100 Subject: [PATCH 1301/5615] Tweak GHA CI configuration to enable Gradle remote cache push --- .github/workflows/ci.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index df6fc96ea44..b9c94223eb3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,6 +27,8 @@ jobs: # Since we override the tests executor, # we should not push empty results to the cache READ_ONLY_REMOTE_GRADLE_CACHE: true + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} run: | TASKS=$(./gradlew --no-daemon --parallel -q testMatrix) echo $TASKS @@ -56,6 +58,9 @@ jobs: - name: Build and test with Gradle (${{matrix.gradle_args}}) run: | ./gradlew --no-daemon --continue --scan --info ${{matrix.gradle_args}} + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - name: Aggregate test reports with ciMate if: always() env: @@ -83,6 +88,9 @@ jobs: working-directory: ./examples/ run: | ./gradlew --no-daemon --scan --info check + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - name: Aggregate test reports with ciMate if: always() env: From 3d407196be6865fef0daaf3e4077b565e0aa0a19 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 2 May 2020 10:07:59 +0100 Subject: [PATCH 1302/5615] Simplify remote build cache boolean expression --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index 4321c4ac4a4..150d79f89fe 100644 --- a/settings.gradle +++ b/settings.gradle @@ -27,7 +27,7 @@ buildCache { region = 'fra1' bucket = 'testcontainers' path = 'cache' - push = !(System.getenv("READ_ONLY_REMOTE_GRADLE_CACHE") || !isMasterBuild) + push = isMasterBuild && !System.getenv("READ_ONLY_REMOTE_GRADLE_CACHE") headers = [ 'x-amz-acl': 'public-read' ] From e10c7eab23d84827c4e09e1e92312a2295626d71 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 2 May 2020 10:22:45 +0100 Subject: [PATCH 1303/5615] Pull up `env` to top level to be available to all jobs/steps --- .github/workflows/ci.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b9c94223eb3..a6baaf7e92d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,10 @@ on: pull_request: {} push: { branches: [ master ] } +env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + jobs: find_gradle_jobs: runs-on: ubuntu-18.04 @@ -27,8 +31,6 @@ jobs: # Since we override the tests executor, # we should not push empty results to the cache READ_ONLY_REMOTE_GRADLE_CACHE: true - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} run: | TASKS=$(./gradlew --no-daemon --parallel -q testMatrix) echo $TASKS @@ -58,9 +60,6 @@ jobs: - name: Build and test with Gradle (${{matrix.gradle_args}}) run: | ./gradlew --no-daemon --continue --scan --info ${{matrix.gradle_args}} - env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - name: Aggregate test reports with ciMate if: always() env: @@ -88,9 +87,6 @@ jobs: working-directory: ./examples/ run: | ./gradlew --no-daemon --scan --info check - env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - name: Aggregate test reports with ciMate if: always() env: From fd28fcf42bde161ab42a2f8157e205de5bb0446e Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 2 May 2020 12:23:44 +0100 Subject: [PATCH 1304/5615] Fix flaky JDBC tmpfs test (#2646) --- .../jdbc/DatabaseDriverTmpfsTest.java | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java index 07a17f68ade..6f337a586b6 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java +++ b/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java @@ -1,27 +1,19 @@ package org.testcontainers.jdbc; -import org.junit.Rule; import org.junit.Test; import org.testcontainers.containers.Container; import org.testcontainers.containers.JdbcDatabaseContainer; -import org.testcontainers.testsupport.Flaky; -import org.testcontainers.testsupport.FlakyTestJUnit4RetryRule; -import java.io.IOException; import java.sql.Connection; import java.sql.DriverManager; -import java.sql.SQLException; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; public class DatabaseDriverTmpfsTest { - @Rule - public FlakyTestJUnit4RetryRule retry = new FlakyTestJUnit4RetryRule(); - @Test - @Flaky(githubIssueUrl = "https://github.com/testcontainers/testcontainers-java/issues/1687", reviewDate = "2020-03-01") - public void tmpfs() throws IOException, InterruptedException, SQLException { + public void testDatabaseHasTmpFsViaConnectionString() throws Exception { final String jdbcUrl = "jdbc:tc:postgresql:9.6.8://hostname/databasename?TC_TMPFS=/testtmpfs:rw"; try (Connection ignored = DriverManager.getConnection(jdbcUrl)) { @@ -29,15 +21,11 @@ public void tmpfs() throws IOException, InterruptedException, SQLException { // check file doesn't exist String path = "/testtmpfs/test.file"; Container.ExecResult execResult = container.execInContainer("ls", path); - assertEquals("tmpfs inside container works fine", execResult.getStderr(), - "ls: cannot access '/testtmpfs/test.file': No such file or directory\n"); + assertNotEquals("tmpfs inside container doesn't have file that doesn't exist", 0, execResult.getExitCode()); // touch && check file does exist container.execInContainer("touch", path); execResult = container.execInContainer("ls", path); - assertEquals("tmpfs inside container works fine", execResult.getStdout(), path + "\n"); + assertEquals("tmpfs inside container has file that does exist", 0, execResult.getExitCode()); } - } - - } From 12fef372c278f6b2d07700161dec5ff92da6576d Mon Sep 17 00:00:00 2001 From: mans2singh Date: Sat, 2 May 2020 10:25:24 -0400 Subject: [PATCH 1305/5615] Doc update for elasticsearch and kafka (#2615) --- docs/modules/elasticsearch.md | 2 +- docs/modules/kafka.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/modules/elasticsearch.md b/docs/modules/elasticsearch.md index 02eeb61dfbe..c7efa872df6 100644 --- a/docs/modules/elasticsearch.md +++ b/docs/modules/elasticsearch.md @@ -36,7 +36,7 @@ container.stop(); ``` Note that if you are still using the [TransportClient](https://www.elastic.co/guide/en/elasticsearch/client/java-api/6.3/transport-client.html) -(not recommended as deprecated), the default cluster name is set to `docker-cluster` so you need to change `cluster.name` setting +(not recommended as it is deprecated), the default cluster name is set to `docker-cluster` so you need to change `cluster.name` setting or set `client.transport.ignore_cluster_name` to `true`. ## Choose your Elasticsearch license diff --git a/docs/modules/kafka.md b/docs/modules/kafka.md index c0b85b67a62..a96a0a0b43a 100644 --- a/docs/modules/kafka.md +++ b/docs/modules/kafka.md @@ -42,7 +42,7 @@ new KafkaContainer().withExternalZookeeper("localhost:2181") If your test needs to run some other Docker container which needs access to the Kafka, do the following: -* Run you other container on the same network as Kafka container. E.g. as following: +* Run your other container on the same network as Kafka container, e.g.: ```java new GenericContainer("myImage").withNetwork(kafka.getNetwork()) ``` From 3e4e5f27988809a5897bc1cf0e2407002153dfec Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sat, 2 May 2020 17:51:39 +0200 Subject: [PATCH 1306/5615] Fix a regression in OracleContainer introduced in #2473 (#2612) --- .../org/testcontainers/containers/OracleContainer.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java index bad22af2511..e86768bc383 100644 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java @@ -38,12 +38,15 @@ public OracleContainer() { public OracleContainer(String dockerImageName) { super(dockerImageName); - withStartupTimeoutSeconds(DEFAULT_STARTUP_TIMEOUT_SECONDS); - withConnectTimeoutSeconds(DEFAULT_CONNECT_TIMEOUT_SECONDS); + preconfigure(); } public OracleContainer(Future dockerImageName) { super(dockerImageName); + preconfigure(); + } + + private void preconfigure() { withStartupTimeoutSeconds(DEFAULT_STARTUP_TIMEOUT_SECONDS); withConnectTimeoutSeconds(DEFAULT_CONNECT_TIMEOUT_SECONDS); addExposedPorts(ORACLE_PORT, APEX_HTTP_PORT); From 2bb9613731a0a662f63323f7bb99a399cc1e1c92 Mon Sep 17 00:00:00 2001 From: Janusz Date: Sat, 2 May 2020 17:56:10 +0200 Subject: [PATCH 1307/5615] Docker Compose: Verify that all services that have a wait condition defined also have a corresponding service instance (#2637) --- .../containers/DockerComposeContainer.java | 15 +++++ .../junit/DockerComposeWaitStrategyTest.java | 55 ++++++++++++++++--- 2 files changed, 63 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 61036f690cd..7bf8d3c916b 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -6,6 +6,7 @@ import com.google.common.base.Splitter; import com.google.common.base.Strings; import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import com.google.common.util.concurrent.Uninterruptibles; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; @@ -216,6 +217,20 @@ private void createServices() { private void waitUntilServiceStarted() { listChildContainers().forEach(this::createServiceInstance); + + Set servicesToWaitFor = waitStrategyMap.keySet(); + Set instantiatedServices = serviceInstanceMap.keySet(); + Sets.SetView missingServiceInstances = + Sets.difference(servicesToWaitFor, instantiatedServices); + + if (!missingServiceInstances.isEmpty()) { + throw new IllegalStateException( + "Services named " + missingServiceInstances + " " + + "do not exist, but wait conditions have been defined " + + "for them. This might mean that you misspelled " + + "the service name when defining the wait condition."); + } + serviceInstanceMap.forEach(this::waitUntilServiceStarted); } diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeWaitStrategyTest.java index 7724b048b69..32465010700 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeWaitStrategyTest.java @@ -1,21 +1,40 @@ package org.testcontainers.junit; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; + +import java.io.File; +import java.time.Duration; +import org.junit.After; +import org.junit.Before; import org.junit.Test; import org.junit.runner.Description; +import org.mockito.Mockito; import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.containers.DockerComposeContainer; import org.testcontainers.containers.wait.strategy.Wait; - -import java.io.File; -import java.time.Duration; +import org.testcontainers.containers.wait.strategy.WaitStrategy; public class DockerComposeWaitStrategyTest { private static final int REDIS_PORT = 6379; + private DockerComposeContainer environment; + + @Before + public final void setUp() { + environment = new DockerComposeContainer<>( + new File("src/test/resources/compose-test.yml")); + } + + @After + public final void cleanUp() { + environment.stop(); + } + @Test public void testWaitOnListeningPort() { - final DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/compose-test.yml")) + environment .withExposedService("redis_1", REDIS_PORT, Wait.forListeningPort()); try { @@ -28,7 +47,7 @@ public void testWaitOnListeningPort() { @Test public void testWaitOnMultipleStrategiesPassing() { - final DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/compose-test.yml")) + environment .withExposedService("redis_1", REDIS_PORT, Wait.forListeningPort()) .withExposedService("db_1", 3306, Wait.forLogMessage(".*ready for connections.*\\s", 1)) .withTailChildContainers(true); @@ -43,7 +62,7 @@ public void testWaitOnMultipleStrategiesPassing() { @Test public void testWaitingFails() { - final DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/compose-test.yml")) + environment .withExposedService("redis_1", REDIS_PORT, Wait.forHttp("/test").withStartupTimeout(Duration.ofSeconds(10))); VisibleAssertions.assertThrows("waiting on an invalid http path times out", RuntimeException.class, @@ -52,7 +71,7 @@ public void testWaitingFails() { @Test public void testWaitOnOneOfMultipleStrategiesFailing() { - final DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/compose-test.yml")) + environment .withExposedService("redis_1", REDIS_PORT, Wait.forListeningPort().withStartupTimeout(Duration.ofSeconds(10))) .waitingFor("db_1", Wait.forLogMessage(".*test test test.*\\s", 1).withStartupTimeout(Duration.ofSeconds(10))) .withTailChildContainers(true); @@ -62,4 +81,26 @@ public void testWaitOnOneOfMultipleStrategiesFailing() { () -> environment.starting(Description.createTestDescription(Object.class, "name"))); } + @Test + public void testWaitingForNonexistentServices() { + String existentServiceName = "db_1"; + String nonexistentServiceName1 = "some_nonexistent_service_1"; + String nonexistentServiceName2 = "some_nonexistent_service_2"; + WaitStrategy someWaitStrategy = Mockito.mock(WaitStrategy.class); + + environment + .waitingFor(existentServiceName, someWaitStrategy) + .waitingFor(nonexistentServiceName1, someWaitStrategy) + .waitingFor(nonexistentServiceName2, someWaitStrategy); + + Throwable thrownWhenRequestedToWaitForNonexistentService = + catchThrowable(environment::start); + + assertThat(thrownWhenRequestedToWaitForNonexistentService) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining( + nonexistentServiceName1, + nonexistentServiceName2) + .hasMessageNotContaining(existentServiceName); + } } From eada48eadcbc5ab1cb97d833b9de6b2350efa172 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 2 May 2020 19:50:26 +0100 Subject: [PATCH 1308/5615] Remove .envrc file --- .envrc | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .envrc diff --git a/.envrc b/.envrc deleted file mode 100644 index dad2eb2cfc6..00000000000 --- a/.envrc +++ /dev/null @@ -1 +0,0 @@ -use python 3.7.5 From a3b200b12d6f93d07c1c1864c2197b6a358dd7b1 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 2 May 2020 20:05:47 +0100 Subject: [PATCH 1309/5615] Bump mysql-connector-java from 8.0.19 to 8.0.20 in /modules/mysql (#2618) --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 606aeefe813..2ee2d62ce59 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly project(':r2dbc') compileOnly 'dev.miku:r2dbc-mysql:0.8.1.RELEASE' - testCompile 'mysql:mysql-connector-java:8.0.19' + testCompile 'mysql:mysql-connector-java:8.0.20' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.34' testCompile 'commons-dbutils:commons-dbutils:1.7' From f30ac94f8aa0b67e8795845b7357ae70c33ec4a0 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 2 May 2020 20:05:59 +0100 Subject: [PATCH 1310/5615] Bump s3 from 2.13.4 to 2.13.7 in /modules/localstack (#2642) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 82cf5c1f841..0f905597e26 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,5 +7,5 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.683' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.636' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.773' - testCompile 'software.amazon.awssdk:s3:2.13.4' + testCompile 'software.amazon.awssdk:s3:2.13.7' } From 7c761a9e3571609ab6aacbaecdae67f79c3526b4 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 2 May 2020 20:07:08 +0100 Subject: [PATCH 1311/5615] Bump guava from 28.2-jre to 29.0-jre in /core (#2643) --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index bbf57a34a70..9b317074909 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -5,7 +5,7 @@ buildscript { dependencies { // https://github.com/melix/japicmp-gradle-plugin/issues/36 - classpath 'com.google.guava:guava:28.2-jre' + classpath 'com.google.guava:guava:29.0-jre' } } From 0050e70e24b1cd6540bd08eb960467aa11544398 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 3 May 2020 10:43:38 +0100 Subject: [PATCH 1312/5615] Revert "Example and docs for ability to copy to and from container for #1164" (#2651) --- .../junit/CopyFileToContainerTest.java | 30 +++---------------- docs/features/files.md | 16 ---------- 2 files changed, 4 insertions(+), 42 deletions(-) diff --git a/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java b/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java index 1e230ec605b..ed6ee561505 100644 --- a/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java @@ -1,54 +1,32 @@ package org.testcontainers.junit; -import com.google.common.io.Files; -import com.google.common.io.Resources; import org.junit.Test; import org.testcontainers.containers.BindMode; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.SelinuxContext; import org.testcontainers.utility.MountableFile; -import java.io.File; import java.io.IOException; import java.util.Map; -import static org.junit.Assert.assertArrayEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; public class CopyFileToContainerTest { - public static final String destinationOnHost = "/tmp/test-resource-in-host.txt"; - private static String directoryInContainer = "/tmp/mappable-resource/"; + private static String containerPath = "/tmp/mappable-resource/"; private static String fileName = "test-resource.txt"; @Test public void checkFileCopied() throws IOException, InterruptedException { try ( - // copyToContainer { GenericContainer container = new GenericContainer() .withCommand("sleep", "3000") - // withCopyFileToContainer ensure that a file or directory will be copied to the container - // before starting. In this case, we map a classpath directory to a directory inside the container - .withCopyFileToContainer(MountableFile.forClasspathResource("/mappable-resource/"), directoryInContainer) - // } + .withCopyFileToContainer(MountableFile.forClasspathResource("/mappable-resource/"), containerPath) ) { container.start(); - - // at this point directoryInContainer should exist, and should contain copies of file(s) - String filesList = container.execInContainer("ls", directoryInContainer).getStdout(); + String filesList = container.execInContainer("ls", "/tmp/mappable-resource").getStdout(); assertTrue("file list contains the file", filesList.contains(fileName)); - - // ... - - // copyFromContainer { - container.copyFileFromContainer(directoryInContainer + fileName, destinationOnHost); - // } } - - assertArrayEquals( - Files.toByteArray(new File(destinationOnHost)), - Resources.toByteArray(CopyFileToContainerTest.class.getResource("/mappable-resource/" + fileName)) - ); } @Test @@ -56,7 +34,7 @@ public void shouldUseCopyForReadOnlyClasspathResources() throws Exception { try ( GenericContainer container = new GenericContainer() .withCommand("sleep", "3000") - .withClasspathResourceMapping("/mappable-resource/", directoryInContainer, BindMode.READ_ONLY) + .withClasspathResourceMapping("/mappable-resource/", containerPath, BindMode.READ_ONLY) ) { container.start(); String filesList = container.execInContainer("ls", "/tmp/mappable-resource").getStdout(); diff --git a/docs/features/files.md b/docs/features/files.md index 47240f82cd2..a74b1add134 100644 --- a/docs/features/files.md +++ b/docs/features/files.md @@ -9,19 +9,3 @@ new GenericContainer(...) "/etc/redis.conf", BindMode.READ_ONLY) ``` - -## Copying files to and from containers - -Files can be copied into the container before startup, or can be copied from the container after the container has started. - -### Copying to a container before startup - - -[Copying files to a container](../../core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java) inside_block:copyToContainer - - -### Copying a file from a running container - - -[Copying files from a container](../../core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java) inside_block:copyFromContainer - From 8fd4c2081d3975473d430b81b08fa25c30d74199 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 5 May 2020 20:58:50 +0100 Subject: [PATCH 1313/5615] Make neo4j module support Gradle caching (#2649) --- modules/neo4j/build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 9361824fdd6..1f119ff83bd 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -20,6 +20,10 @@ task customNeo4jPluginJar(type: Jar) { from sourceSets.customNeo4jPlugin.output archiveName = "hello-world.jar" destinationDir = customNeo4jPluginDestinationDir + + inputs.files(sourceSets.customNeo4jPlugin.java.srcDirs) + outputs.cacheIf { true } + outputs.dir(customNeo4jPluginDestinationDir) } test.dependsOn customNeo4jPluginJar From 2ea0524494075709babd3b470cadb99bf19a0e0a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 5 May 2020 20:59:58 +0100 Subject: [PATCH 1314/5615] Bump jedis from 3.2.0 to 3.3.0 in /core (#2653) --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 9b317074909..fc4a36ea0b9 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -155,7 +155,7 @@ dependencies { } testCompile 'org.apache.httpcomponents:httpclient:4.5.9' - testCompile 'redis.clients:jedis:3.2.0' + testCompile 'redis.clients:jedis:3.3.0' testCompile 'com.rabbitmq:amqp-client:5.9.0' testCompile 'org.mongodb:mongo-java-driver:3.12.3' From 6f4f808357d47907decfd650b211ac6cae9e182b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 5 May 2020 21:00:11 +0100 Subject: [PATCH 1315/5615] Bump jedis from 3.2.0 to 3.3.0 in /modules/junit-jupiter (#2652) --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 5046008c7fa..7acc7fcbf11 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -7,7 +7,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') testCompile 'com.zaxxer:HikariCP:3.4.3' - testCompile 'redis.clients:jedis:3.2.0' + testCompile 'redis.clients:jedis:3.3.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.11' testCompile ('org.mockito:mockito-core:3.3.3') { exclude(module: 'hamcrest-core') From 6b522dbc6c39d68ae1c046bbcaf2acd110a91f86 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 5 May 2020 21:00:27 +0100 Subject: [PATCH 1316/5615] Bump jedis from 3.2.0 to 3.3.0 in /examples (#2656) --- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 1779966c692..62f7de6dcf8 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' - implementation 'redis.clients:jedis:3.2.0' + implementation 'redis.clients:jedis:3.3.0' implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 9dea8531b5e..258c98fb107 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' - implementation 'redis.clients:jedis:3.2.0' + implementation 'redis.clients:jedis:3.3.0' implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index b06d4e7cb30..11e6cb2a31b 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { - implementation 'redis.clients:jedis:3.2.0' + implementation 'redis.clients:jedis:3.3.0' implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.30' From 71d0c0c48c445fe77450a44fa27f756f58bf36f4 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 5 May 2020 21:01:07 +0100 Subject: [PATCH 1317/5615] Bump cucumber-junit from 5.6.0 to 5.7.0 in /examples (#2657) --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 53a50c374f5..92724e358db 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' - testImplementation 'io.cucumber:cucumber-junit:5.6.0' + testImplementation 'io.cucumber:cucumber-junit:5.7.0' testImplementation 'io.cucumber:cucumber-java:5.6.0' testImplementation 'org.testcontainers:selenium' } From 7e92eea2f0650ed0ff19a702451cbf60ef27395f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 5 May 2020 21:14:06 +0100 Subject: [PATCH 1318/5615] Bump cucumber-java from 5.6.0 to 5.7.0 in /examples (#2658) --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 92724e358db..011b2bddecd 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.14.0' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' + testImplementation 'io.cucumber:cucumber-java:5.7.0' testImplementation 'io.cucumber:cucumber-junit:5.7.0' - testImplementation 'io.cucumber:cucumber-java:5.6.0' testImplementation 'org.testcontainers:selenium' } From 5cd771e6294e25fa186452fcd3f699246950b89d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 5 May 2020 21:15:13 +0100 Subject: [PATCH 1319/5615] Bump s3 from 2.13.7 to 2.13.9 in /modules/localstack (#2659) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 0f905597e26..98f01ea3b38 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,5 +7,5 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.683' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.636' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.773' - testCompile 'software.amazon.awssdk:s3:2.13.7' + testCompile 'software.amazon.awssdk:s3:2.13.9' } From 0adeb114b957721ff457b83e3dbd26a5be0eae36 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 5 May 2020 21:25:05 +0100 Subject: [PATCH 1320/5615] Bump aws-java-sdk-logs from 1.11.773 to 1.11.775 in /modules/localstack (#2660) --- modules/localstack/build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 98f01ea3b38..89e204515c5 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,9 +3,9 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.683' - testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.683' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.636' - testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.773' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.775' + testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.775' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.775' + testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.775' testCompile 'software.amazon.awssdk:s3:2.13.9' } From edde70525af03a2a8fdde95a9e5f6a3d6a27bf5f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 6 May 2020 09:01:01 +0200 Subject: [PATCH 1321/5615] Bump assertj-core from 3.15.0 to 3.16.0 in /core (#2671) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.15.0 to 3.16.0. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.15.0...assertj-core-3.16.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- core/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index fc4a36ea0b9..f74744255f8 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -165,12 +165,12 @@ dependencies { // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest testCompile files('testlib/repo/fakejar/fakejar/0/fakejar-0.jar') - testCompile 'org.assertj:assertj-core:3.15.0' + testCompile 'org.assertj:assertj-core:3.16.0' testCompile project(':test-support') jarFileTestCompileOnly "org.projectlombok:lombok:${lombok.version}" jarFileTestAnnotationProcessor "org.projectlombok:lombok:${lombok.version}" jarFileTestCompile 'junit:junit:4.12' - jarFileTestCompile 'org.assertj:assertj-core:3.14.0' + jarFileTestCompile 'org.assertj:assertj-core:3.16.0' jarFileTestCompile 'org.ow2.asm:asm-debug-all:5.2' } From eb58a9a023d1d52980f578af8083421216416352 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 6 May 2020 09:02:28 +0200 Subject: [PATCH 1322/5615] Bump assertj-core from 3.15.0 to 3.16.0 in /modules/vault (#2670) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.15.0 to 3.16.0. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.15.0...assertj-core-3.16.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index bf85047ddd7..b8681be150d 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -5,6 +5,6 @@ dependencies { testCompile 'com.bettercloud:vault-java-driver:5.1.0' testCompile 'io.rest-assured:rest-assured:4.3.0' - testCompile 'org.assertj:assertj-core:3.15.0' + testCompile 'org.assertj:assertj-core:3.16.0' } From ef3ac663b4bc1f43520407aa971537506a1c5dfb Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 6 May 2020 09:02:52 +0200 Subject: [PATCH 1323/5615] Bump assertj-core from 3.15.0 to 3.16.0 in /modules/junit-jupiter (#2669) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.15.0 to 3.16.0. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.15.0...assertj-core-3.16.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 7acc7fcbf11..b80f9263e9c 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -12,7 +12,7 @@ dependencies { testCompile ('org.mockito:mockito-core:3.3.3') { exclude(module: 'hamcrest-core') } - testCompile 'org.assertj:assertj-core:3.15.0' + testCompile 'org.assertj:assertj-core:3.16.0' testCompile 'org.junit.jupiter:junit-jupiter-params:5.6.2' testRuntime 'org.postgresql:postgresql:42.2.12' From 1f535b72bd4a2ff02f9b58d5c588acd0ff8eb395 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 6 May 2020 09:03:17 +0200 Subject: [PATCH 1324/5615] Bump assertj-core from 3.15.0 to 3.16.0 in /modules/database-commons (#2672) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.15.0 to 3.16.0. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.15.0...assertj-core-3.16.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/database-commons/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/database-commons/build.gradle b/modules/database-commons/build.gradle index d8738a6cb69..14dffc649ac 100644 --- a/modules/database-commons/build.gradle +++ b/modules/database-commons/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: Database-Commons" dependencies { compile project(':testcontainers') - testCompile 'org.assertj:assertj-core:3.15.0' + testCompile 'org.assertj:assertj-core:3.16.0' } From 5b2590c2eae76aeccd29a49c347591674cffead7 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 7 May 2020 11:49:38 +0100 Subject: [PATCH 1325/5615] Bump aws-java-sdk-sqs from 1.11.775 to 1.11.777 in /modules/localstack (#2675) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 89e204515c5..d2e3a1ff2cf 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.775' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.775' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.775' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.777' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.775' testCompile 'software.amazon.awssdk:s3:2.13.9' } From 76ff1118f8caf4dbd0a7c9210d44d0702ce021c0 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 7 May 2020 14:30:02 +0100 Subject: [PATCH 1326/5615] Bump assertj-core from 3.15.0 to 3.16.0 in /modules/pulsar (#2668) --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 4f459d9a97e..ecfa7c5f26a 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -4,5 +4,5 @@ dependencies { compile project(':testcontainers') testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.5.1' - testCompile group: 'org.assertj', name: 'assertj-core', version: '3.15.0' + testCompile group: 'org.assertj', name: 'assertj-core', version: '3.16.0' } From 402823c43ce2e2aa576fef11a9803add317c54fd Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 7 May 2020 14:30:22 +0100 Subject: [PATCH 1327/5615] Bump assertj-core from 3.15.0 to 3.16.0 in /modules/neo4j (#2665) --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 1f119ff83bd..77ac94a5e8d 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -34,5 +34,5 @@ dependencies { compile project(":testcontainers") testCompile "org.neo4j.driver:neo4j-java-driver:1.7.5" - testCompile 'org.assertj:assertj-core:3.15.0' + testCompile 'org.assertj:assertj-core:3.16.0' } From 81483cfc85d8b805b20ca9017394c92ab7d25fed Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 7 May 2020 14:30:30 +0100 Subject: [PATCH 1328/5615] Bump assertj-core from 3.15.0 to 3.16.0 in /modules/kafka (#2664) --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index 355c44eab1a..17d6f4b652d 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -4,6 +4,6 @@ dependencies { compile project(':testcontainers') testCompile 'org.apache.kafka:kafka-clients:2.5.0' - testCompile 'org.assertj:assertj-core:3.15.0' + testCompile 'org.assertj:assertj-core:3.16.0' testCompile 'com.google.guava:guava:23.0' } From 89ef00389218a5b89682e2ed01db3cabaca85a16 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 7 May 2020 14:31:18 +0100 Subject: [PATCH 1329/5615] Bump aws-java-sdk-logs from 1.11.775 to 1.11.777 in /modules/localstack (#2674) --- modules/localstack/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index d2e3a1ff2cf..2aa9150b32b 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,6 +6,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.775' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.775' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.777' + testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.777' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.775' testCompile 'software.amazon.awssdk:s3:2.13.9' } From bc67cb5d55fbf4595e1d9bd644280e30310a33be Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 7 May 2020 14:31:31 +0100 Subject: [PATCH 1330/5615] Bump s3 from 2.13.9 to 2.13.11 in /modules/localstack (#2673) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 2aa9150b32b..9270eb9a3ca 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -8,5 +8,5 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.777' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.777' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.775' - testCompile 'software.amazon.awssdk:s3:2.13.9' + testCompile 'software.amazon.awssdk:s3:2.13.11' } From 342f6aef60a0421495f28ce36dd42268d5553796 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 7 May 2020 14:31:43 +0100 Subject: [PATCH 1331/5615] Bump mongo-java-driver from 3.12.3 to 3.12.4 in /core (#2676) --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index f74744255f8..e7176938b43 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -157,7 +157,7 @@ dependencies { testCompile 'org.apache.httpcomponents:httpclient:4.5.9' testCompile 'redis.clients:jedis:3.3.0' testCompile 'com.rabbitmq:amqp-client:5.9.0' - testCompile 'org.mongodb:mongo-java-driver:3.12.3' + testCompile 'org.mongodb:mongo-java-driver:3.12.4' testCompile ('org.mockito:mockito-core:3.3.3') { exclude(module: 'hamcrest-core') From 886e4c3239da7b73625cd4b965a0ff5b6b74d6aa Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 7 May 2020 15:21:58 +0100 Subject: [PATCH 1332/5615] Bump annotations from 17.0.0 to 19.0.0 in /core (#2358) --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index e7176938b43..3f4faf0c709 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -116,7 +116,7 @@ dependencies { compile 'junit:junit:4.12' compile 'org.slf4j:slf4j-api:1.7.30' - compile 'org.jetbrains:annotations:17.0.0' + compile 'org.jetbrains:annotations:19.0.0' compile 'org.apache.commons:commons-compress:1.20' compile ('org.rnorth.duct-tape:duct-tape:1.0.8') { exclude(group: 'org.jetbrains', module: 'annotations') From 83a3c941ce948b5339529dd1b2b9928bee1dee19 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 7 May 2020 19:55:23 +0100 Subject: [PATCH 1333/5615] Bump httpclient from 4.5.11 to 4.5.12 in /modules/junit-jupiter (#2409) --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index b80f9263e9c..fb5548c320d 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -8,7 +8,7 @@ dependencies { testCompile project(':postgresql') testCompile 'com.zaxxer:HikariCP:3.4.3' testCompile 'redis.clients:jedis:3.3.0' - testCompile 'org.apache.httpcomponents:httpclient:4.5.11' + testCompile 'org.apache.httpcomponents:httpclient:4.5.12' testCompile ('org.mockito:mockito-core:3.3.3') { exclude(module: 'hamcrest-core') } From e5489d2dd4136d6bc39ed98e0a7db7c66d0cfcb2 Mon Sep 17 00:00:00 2001 From: Danny George Date: Thu, 7 May 2020 13:22:32 -0600 Subject: [PATCH 1334/5615] RabbitMQ: Support declaring an exchange within a vhost (#2362) --- .../containers/RabbitMQContainer.java | 11 +++++++++++ .../containers/RabbitMQContainerTest.java | 15 +++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java index ede111ed2d8..aba71e25a7e 100644 --- a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java +++ b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java @@ -345,6 +345,17 @@ public RabbitMQContainer withExchange(String name, String type) { public RabbitMQContainer withExchange(String name, String type, boolean autoDelete, boolean internal, boolean durable, Map arguments) { values.add(asList("rabbitmqadmin", "declare", "exchange", + "name=" + name, + "type=" + type, + "auto_delete=" + autoDelete, + "internal=" + internal, + "durable=" + durable, + "arguments=" + toJson(arguments))); + return self(); + } + + public RabbitMQContainer withExchange(String vhost, String name, String type, boolean autoDelete, boolean internal, boolean durable, Map arguments) { + values.add(asList("rabbitmqadmin", "--vhost=" + vhost, "declare", "exchange", "name=" + name, "type=" + type, "auto_delete=" + autoDelete, diff --git a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java index 5822e213a82..6f283c7451f 100644 --- a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java +++ b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java @@ -19,6 +19,7 @@ import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; +import java.util.Collections; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManagerFactory; @@ -91,6 +92,20 @@ public void shouldCreateRabbitMQContainerWithExchange() throws IOException, Inte } } + @Test + public void shouldCreateRabbitMQContainerWithExchangeInVhost() throws IOException, InterruptedException + { + try (RabbitMQContainer container = new RabbitMQContainer()) { + container.withVhost("test-vhost"); + container.withExchange("test-vhost", "test-exchange", "direct", false, false, false, Collections.emptyMap()); + + container.start(); + + assertThat(container.execInContainer("rabbitmqctl", "list_exchanges", "-p", "test-vhost").getStdout()) + .containsPattern("test-exchange\\s+direct"); + } + } + @Test public void shouldCreateRabbitMQContainerWithQueues() throws IOException, InterruptedException { From c88d25705942024677533f333a84734557044088 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 7 May 2020 20:23:42 +0100 Subject: [PATCH 1335/5615] Bump okhttp from 3.14.6 to 3.14.8 in /core (#2644) --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 3f4faf0c709..312298bf77d 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -146,7 +146,7 @@ dependencies { shaded "org.yaml:snakeyaml:1.25" - shaded 'com.squareup.okhttp3:okhttp:3.14.6' + shaded 'com.squareup.okhttp3:okhttp:3.14.8' shaded 'org.glassfish.main.external:trilead-ssh2-repackaged:4.1.2' From 6263cad11a6e8e466ebf12f11173238ac2915052 Mon Sep 17 00:00:00 2001 From: streppel Date: Thu, 7 May 2020 16:32:06 -0300 Subject: [PATCH 1336/5615] Remove duplicate debug log message at container start (#2522) --- .../java/org/testcontainers/containers/GenericContainer.java | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 211ad2f59cb..23e5f9bf173 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -307,7 +307,6 @@ protected void doStart() { Instant startedAt = Instant.now(); logger().debug("Starting container: {}", getDockerImageName()); - logger().debug("Trying to start container: {}", getDockerImageName()); AtomicInteger attempt = new AtomicInteger(0); Unreliables.retryUntilSuccess(startupAttempts, () -> { From 0df9f87b9af0ec58dbe04930416799ac58f562af Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 8 May 2020 08:41:41 +0100 Subject: [PATCH 1337/5615] Bump aws-java-sdk-s3 from 1.11.775 to 1.11.778 in /modules/localstack (#2682) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 9270eb9a3ca..31ce7af53b7 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.775' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.778' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.775' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.777' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.777' From 05da884d1beef61fa8b743b798ea988f3cb0dc5d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 8 May 2020 08:41:51 +0100 Subject: [PATCH 1338/5615] Bump HikariCP from 3.4.3 to 3.4.4 in /modules/spock (#2678) --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 7b1780391b5..8ea7a940212 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -12,7 +12,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') - testCompile 'com.zaxxer:HikariCP:3.4.3' + testCompile 'com.zaxxer:HikariCP:3.4.4' testCompile 'org.apache.httpcomponents:httpclient:4.5.12' testRuntime 'org.postgresql:postgresql:42.2.12' From 8f5863b4b8245af3e9dddb3d70c1ff7e1dace152 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 8 May 2020 08:41:58 +0100 Subject: [PATCH 1339/5615] Bump org.springframework.boot from 2.2.6.RELEASE to 2.2.7.RELEASE in /examples (#2683) --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index edbe7d1fb68..04888a55c65 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.2.6.RELEASE' + id 'org.springframework.boot' version '2.2.7.RELEASE' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index 6cd1db0172a..c7b0c63d7ba 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.2.6.RELEASE' + id 'org.springframework.boot' version '2.2.7.RELEASE' } apply plugin: 'io.spring.dependency-management' From d1ae700f18cf37e9954c2b9c75e30072b55ce2d5 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 8 May 2020 08:42:05 +0100 Subject: [PATCH 1340/5615] Bump s3 from 2.13.11 to 2.13.12 in /modules/localstack (#2681) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 31ce7af53b7..25365a3f8a3 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -8,5 +8,5 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.777' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.777' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.775' - testCompile 'software.amazon.awssdk:s3:2.13.11' + testCompile 'software.amazon.awssdk:s3:2.13.12' } From d3d168a3885947f58c13d6e0f9a4cf5500b96794 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 8 May 2020 08:42:12 +0100 Subject: [PATCH 1341/5615] Bump aws-java-sdk-sqs from 1.11.777 to 1.11.778 in /modules/localstack (#2680) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 25365a3f8a3..874e5fe54df 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.778' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.775' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.777' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.778' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.777' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.775' testCompile 'software.amazon.awssdk:s3:2.13.12' From 69b3bc2359dd01e025ec618a4e4ba1ec5b8fb1c2 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 8 May 2020 08:42:19 +0100 Subject: [PATCH 1342/5615] Bump HikariCP from 3.4.3 to 3.4.4 in /modules/junit-jupiter (#2679) --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index fb5548c320d..93b9b1dc23e 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -6,7 +6,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') - testCompile 'com.zaxxer:HikariCP:3.4.3' + testCompile 'com.zaxxer:HikariCP:3.4.4' testCompile 'redis.clients:jedis:3.3.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.12' testCompile ('org.mockito:mockito-core:3.3.3') { From a2cfd4502d272d1116d42e5c7339a5320a401a43 Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 8 May 2020 12:51:53 +0100 Subject: [PATCH 1343/5615] Revert "Bump HikariCP from 3.4.3 to 3.4.4 in /modules/spock" (#2686) --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 8ea7a940212..7b1780391b5 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -12,7 +12,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') - testCompile 'com.zaxxer:HikariCP:3.4.4' + testCompile 'com.zaxxer:HikariCP:3.4.3' testCompile 'org.apache.httpcomponents:httpclient:4.5.12' testRuntime 'org.postgresql:postgresql:42.2.12' From 9b12c6403eecf6ebeaa412d85c8a8aaa7ad7de49 Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 8 May 2020 12:52:05 +0100 Subject: [PATCH 1344/5615] Revert "Bump HikariCP from 3.4.3 to 3.4.4 in /modules/junit-jupiter" (#2685) --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 93b9b1dc23e..fb5548c320d 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -6,7 +6,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') - testCompile 'com.zaxxer:HikariCP:3.4.4' + testCompile 'com.zaxxer:HikariCP:3.4.3' testCompile 'redis.clients:jedis:3.3.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.12' testCompile ('org.mockito:mockito-core:3.3.3') { From 8377288a10a892d8ed830b59b860eb3e40a72491 Mon Sep 17 00:00:00 2001 From: silaev Date: Fri, 8 May 2020 17:02:29 +0500 Subject: [PATCH 1345/5615] Add MongoDB module (#1961) --- docs/modules/databases/mongodb.md | 60 +++++++++ mkdocs.yml | 1 + modules/mongodb/build.gradle | 7 + .../containers/MongoDBContainer.java | 120 ++++++++++++++++++ .../containers/MongoDBContainerTest.java | 74 +++++++++++ .../src/test/resources/logback-test.xml | 16 +++ 6 files changed, 278 insertions(+) create mode 100644 docs/modules/databases/mongodb.md create mode 100644 modules/mongodb/build.gradle create mode 100644 modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java create mode 100644 modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java create mode 100644 modules/mongodb/src/test/resources/logback-test.xml diff --git a/docs/modules/databases/mongodb.md b/docs/modules/databases/mongodb.md new file mode 100644 index 00000000000..fc2cb873f59 --- /dev/null +++ b/docs/modules/databases/mongodb.md @@ -0,0 +1,60 @@ +# MongoDB Module + +!!! note + This module is INCUBATING. While it is ready for use and operational in the current version of Testcontainers, it is possible that it may receive breaking changes in the future. See [our contributing guidelines](/contributing/#incubating-modules) for more information on our incubating modules policy. + +## Usage example + +The following example shows how to create a MongoDBContainer: + + +[Creating a MongoDB container](../../../modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java) inside_block:creatingMongoDBContainer + + +And how to start it: + + +[Starting a MongoDB container](../../../modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java) inside_block:startingMongoDBContainer + + +!!! note + To construct a multi-node MongoDB cluster, consider the [mongodb-replica-set project](https://github.com/silaev/mongodb-replica-set/) + +#### Motivation +Implement a reusable, cross-platform, simple to install solution that doesn't depend on +fixed ports to test MongoDB transactions. + +#### General info +MongoDB starting from version 4 supports multi-document transactions only for a replica set. +For instance, to initialize a single node replica set on fixed ports via Docker, one has to do the following: + +* Run a MongoDB container of version 4 and up specifying --replSet command +* Initialize a single replica set via executing a proper command +* Wait for the initialization to complete +* Provide a special url for a user to employ with a MongoDB driver without specifying replicaSet + +As we can see, there is a lot of operations to execute and we even haven't touched a non-fixed port approach. +That's where the MongoDBContainer might come in handy. + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:mongodb:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + mongodb + {{latest_version}} + test + +``` + +!!! hint +Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency + +#### Copyright +Copyright (c) 2019 Konstantin Silaev diff --git a/mkdocs.yml b/mkdocs.yml index 708fbe06883..e1b21d80811 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -50,6 +50,7 @@ nav: - modules/databases/dynalite.md - modules/databases/influxdb.md - modules/databases/mariadb.md + - modules/databases/mongodb.md - modules/databases/mssqlserver.md - modules/databases/mysql.md - modules/databases/neo4j.md diff --git a/modules/mongodb/build.gradle b/modules/mongodb/build.gradle new file mode 100644 index 00000000000..57db1be21de --- /dev/null +++ b/modules/mongodb/build.gradle @@ -0,0 +1,7 @@ +description = "Testcontainers :: MongoDB" + +dependencies { + compile project(':testcontainers') + + testCompile("org.mongodb:mongodb-driver-sync:4.0.2") +} diff --git a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java new file mode 100644 index 00000000000..a7f57d310f2 --- /dev/null +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java @@ -0,0 +1,120 @@ +package org.testcontainers.containers; + +import com.github.dockerjava.api.command.InspectContainerResponse; +import lombok.NonNull; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.testcontainers.containers.wait.strategy.Wait; + +import java.io.IOException; + +/** + * Constructs a single node MongoDB replica set for testing transactions. + *

To construct a multi-node MongoDB cluster, consider the mongodb-replica-set project on GitHub + *

Tested on a MongoDB version 4.0.10+ (that is the default version if not specified). + */ +@Slf4j +public class MongoDBContainer extends GenericContainer { + private static final int CONTAINER_EXIT_CODE_OK = 0; + private static final int MONGODB_INTERNAL_PORT = 27017; + private static final int AWAIT_INIT_REPLICA_SET_ATTEMPTS = 60; + private static final String MONGODB_VERSION_DEFAULT = "4.0.10"; + private static final String MONGODB_DATABASE_NAME_DEFAULT = "test"; + + public MongoDBContainer() { + this("mongo:" + MONGODB_VERSION_DEFAULT); + } + + public MongoDBContainer(@NonNull final String dockerImageName) { + super(dockerImageName); + withExposedPorts(MONGODB_INTERNAL_PORT); + withCommand("--replSet", "docker-rs"); + waitingFor( + Wait.forLogMessage(".*waiting for connections on port.*", 1) + ); + } + + public String getReplicaSetUrl() { + if (!isRunning()) { + throw new IllegalStateException("MongoDBContainer should be started first"); + } + return String.format( + "mongodb://%s:%d/%s", + getContainerIpAddress(), + getMappedPort(MONGODB_INTERNAL_PORT), + MONGODB_DATABASE_NAME_DEFAULT + ); + } + + @Override + protected void containerIsStarted(InspectContainerResponse containerInfo) { + initReplicaSet(); + } + + private String[] buildMongoEvalCommand(final String command) { + return new String[]{"mongo", "--eval", command}; + } + + private void checkMongoNodeExitCode(final Container.ExecResult execResult) { + if (execResult.getExitCode() != CONTAINER_EXIT_CODE_OK) { + final String errorMessage = String.format("An error occurred: %s", execResult.getStdout()); + log.error(errorMessage); + throw new ReplicaSetInitializationException(errorMessage); + } + } + + private String buildMongoWaitCommand() { + return String.format( + "var attempt = 0; " + + "while" + + "(%s) " + + "{ " + + "if (attempt > %d) {quit(1);} " + + "print('%s ' + attempt); sleep(100); attempt++; " + + " }", + "db.runCommand( { isMaster: 1 } ).ismaster==false", + AWAIT_INIT_REPLICA_SET_ATTEMPTS, + "An attempt to await for a single node replica set initialization:" + ); + } + + private void checkMongoNodeExitCodeAfterWaiting( + final Container.ExecResult execResultWaitForMaster + ) { + if (execResultWaitForMaster.getExitCode() != CONTAINER_EXIT_CODE_OK) { + final String errorMessage = String.format( + "A single node replica set was not initialized in a set timeout: %d attempts", + AWAIT_INIT_REPLICA_SET_ATTEMPTS + ); + log.error(errorMessage); + throw new ReplicaSetInitializationException(errorMessage); + } + } + + @SneakyThrows(value = {IOException.class, InterruptedException.class}) + private void initReplicaSet() { + log.debug("Initializing a single node node replica set..."); + final ExecResult execResultInitRs = execInContainer( + buildMongoEvalCommand("rs.initiate();") + ); + log.debug(execResultInitRs.getStdout()); + checkMongoNodeExitCode(execResultInitRs); + + log.debug( + "Awaiting for a single node replica set initialization up to {} attempts", + AWAIT_INIT_REPLICA_SET_ATTEMPTS + ); + final ExecResult execResultWaitForMaster = execInContainer( + buildMongoEvalCommand(buildMongoWaitCommand()) + ); + log.debug(execResultWaitForMaster.getStdout()); + + checkMongoNodeExitCodeAfterWaiting(execResultWaitForMaster); + } + + public static class ReplicaSetInitializationException extends RuntimeException { + ReplicaSetInitializationException(final String errorMessage) { + super(errorMessage); + } + } +} diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java new file mode 100644 index 00000000000..7c4f4c12408 --- /dev/null +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java @@ -0,0 +1,74 @@ +package org.testcontainers.containers; + +import com.mongodb.ReadConcern; +import com.mongodb.ReadPreference; +import com.mongodb.TransactionOptions; +import com.mongodb.WriteConcern; +import com.mongodb.client.ClientSession; +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoClients; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.TransactionBody; +import org.bson.Document; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + + +public class MongoDBContainerTest { + /** + * Taken from https://docs.mongodb.com + */ + @Test + public void shouldExecuteTransactions() { + try ( + // creatingMongoDBContainer { + final MongoDBContainer mongoDBContainer = new MongoDBContainer() + // } + ) { + + // startingMongoDBContainer { + mongoDBContainer.start(); + // } + + final String mongoRsUrl = mongoDBContainer.getReplicaSetUrl(); + assertNotNull(mongoRsUrl); + final MongoClient mongoSyncClient = MongoClients.create(mongoRsUrl); + mongoSyncClient.getDatabase("mydb1").getCollection("foo") + .withWriteConcern(WriteConcern.MAJORITY).insertOne(new Document("abc", 0)); + mongoSyncClient.getDatabase("mydb2").getCollection("bar") + .withWriteConcern(WriteConcern.MAJORITY).insertOne(new Document("xyz", 0)); + + final ClientSession clientSession = mongoSyncClient.startSession(); + final TransactionOptions txnOptions = TransactionOptions.builder() + .readPreference(ReadPreference.primary()) + .readConcern(ReadConcern.LOCAL) + .writeConcern(WriteConcern.MAJORITY) + .build(); + + final String trxResult = "Inserted into collections in different databases"; + + TransactionBody txnBody = () -> { + final MongoCollection coll1 = + mongoSyncClient.getDatabase("mydb1").getCollection("foo"); + final MongoCollection coll2 = + mongoSyncClient.getDatabase("mydb2").getCollection("bar"); + + coll1.insertOne(clientSession, new Document("abc", 1)); + coll2.insertOne(clientSession, new Document("xyz", 999)); + return trxResult; + }; + + try { + final String trxResultActual = clientSession.withTransaction(txnBody, txnOptions); + assertEquals(trxResult, trxResultActual); + } catch (RuntimeException re) { + throw new IllegalStateException(re.getMessage(), re); + } finally { + clientSession.close(); + mongoSyncClient.close(); + } + } + } +} diff --git a/modules/mongodb/src/test/resources/logback-test.xml b/modules/mongodb/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..535e406fc13 --- /dev/null +++ b/modules/mongodb/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + From e6930d6ac986db1373e29b61263f61aecbfc5155 Mon Sep 17 00:00:00 2001 From: Vivek Jaiswal Date: Wed, 13 May 2020 22:50:49 +0530 Subject: [PATCH 1346/5615] Fix typo in DinD docs (#2725) --- .../continuous_integration/dind_patterns.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/supported_docker_environment/continuous_integration/dind_patterns.md b/docs/supported_docker_environment/continuous_integration/dind_patterns.md index 1534949e1bd..e742c047afa 100644 --- a/docs/supported_docker_environment/continuous_integration/dind_patterns.md +++ b/docs/supported_docker_environment/continuous_integration/dind_patterns.md @@ -32,7 +32,7 @@ Where: * `-v /var/run/docker.sock:/var/run/docker.sock` will map the Docker socket ### Docker Compose example -The same can be achived with Docker Compose: +The same can be achieved with Docker Compose: ```yaml tests: image: maven:3 From 123f13aeec72b9c7998b8b5fa32aa1de57c3b31e Mon Sep 17 00:00:00 2001 From: Perennes Date: Wed, 13 May 2020 21:34:26 +0200 Subject: [PATCH 1347/5615] Do not fail on duplicated local image tags. Fixes #2431 (#2692) * #2431 Correction for an edge case in local image repos (duplicates repo tags) crashing the image pulling * Indentation correction * Indentation correction --- .../org/testcontainers/images/LocalImagesCache.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/testcontainers/images/LocalImagesCache.java b/core/src/main/java/org/testcontainers/images/LocalImagesCache.java index 29bc8ef1408..e6b330d4c90 100644 --- a/core/src/main/java/org/testcontainers/images/LocalImagesCache.java +++ b/core/src/main/java/org/testcontainers/images/LocalImagesCache.java @@ -79,10 +79,14 @@ private void populateFromList(List images) { } cache.putAll( - Stream.of(repoTags).collect(Collectors.toMap( - DockerImageName::new, - it -> ImageData.from(image) - )) + Stream.of(repoTags) + // Protection against some edge case where local image repository tags end up with duplicates + // making toMap crash at merge time. + .distinct() + .collect(Collectors.toMap( + DockerImageName::new, + it -> ImageData.from(image) + )) ); } } From 284c272cbf5095fd586ffebfd7d7ec6e7f634f84 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 14 May 2020 10:25:00 +0100 Subject: [PATCH 1348/5615] Bump HikariCP from 3.4.3 to 3.4.5 in /modules/junit-jupiter (#2697) --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index fb5548c320d..5e143b3b706 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -6,7 +6,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') - testCompile 'com.zaxxer:HikariCP:3.4.3' + testCompile 'com.zaxxer:HikariCP:3.4.5' testCompile 'redis.clients:jedis:3.3.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.12' testCompile ('org.mockito:mockito-core:3.3.3') { From d2b677856aa41940a106f2a9da9c15370cfb8077 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 14 May 2020 10:25:19 +0100 Subject: [PATCH 1349/5615] Bump tomcat-jdbc from 9.0.34 to 9.0.35 in /modules/mariadb (#2712) --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 730dae7c794..226e34aae90 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -12,7 +12,7 @@ dependencies { testCompile 'org.mariadb.jdbc:mariadb-java-client:2.6.0' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'commons-dbutils:commons-dbutils:1.7' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.34' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.35' testCompile 'org.vibur:vibur-dbcp:25.0' testCompile testFixtures(project(':r2dbc')) From df6816940a2970a0577933e6b9bd88719984ea30 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 14 May 2020 10:25:36 +0100 Subject: [PATCH 1350/5615] Bump assertj-core from 3.16.0 to 3.16.1 in /modules/neo4j (#2698) --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 77ac94a5e8d..4bc92e49cda 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -34,5 +34,5 @@ dependencies { compile project(":testcontainers") testCompile "org.neo4j.driver:neo4j-java-driver:1.7.5" - testCompile 'org.assertj:assertj-core:3.16.0' + testCompile 'org.assertj:assertj-core:3.16.1' } From 1a754757ebc0d580e04a2ffdc504ed53a2a1e953 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 14 May 2020 10:25:41 +0100 Subject: [PATCH 1351/5615] Bump assertj-core from 3.16.0 to 3.16.1 in /modules/junit-jupiter (#2700) --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 5e143b3b706..485e02f6089 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -12,7 +12,7 @@ dependencies { testCompile ('org.mockito:mockito-core:3.3.3') { exclude(module: 'hamcrest-core') } - testCompile 'org.assertj:assertj-core:3.16.0' + testCompile 'org.assertj:assertj-core:3.16.1' testCompile 'org.junit.jupiter:junit-jupiter-params:5.6.2' testRuntime 'org.postgresql:postgresql:42.2.12' From f60160993117e83eae612d3614a4bdb7fc5fda37 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 14 May 2020 10:25:56 +0100 Subject: [PATCH 1352/5615] Bump assertj-core from 3.16.0 to 3.16.1 in /modules/kafka (#2709) --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index 17d6f4b652d..06090c5b35e 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -4,6 +4,6 @@ dependencies { compile project(':testcontainers') testCompile 'org.apache.kafka:kafka-clients:2.5.0' - testCompile 'org.assertj:assertj-core:3.16.0' + testCompile 'org.assertj:assertj-core:3.16.1' testCompile 'com.google.guava:guava:23.0' } From 948e58dca8ab80e60251f2e7065fc2e3d05a4513 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 14 May 2020 10:26:11 +0100 Subject: [PATCH 1353/5615] Bump HikariCP from 3.4.3 to 3.4.5 in /modules/spock (#2699) --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 7b1780391b5..fea1c8baa2d 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -12,7 +12,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') - testCompile 'com.zaxxer:HikariCP:3.4.3' + testCompile 'com.zaxxer:HikariCP:3.4.5' testCompile 'org.apache.httpcomponents:httpclient:4.5.12' testRuntime 'org.postgresql:postgresql:42.2.12' From 7b3a45d1b2f607ba921f3523a9a0e1745768509d Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 14 May 2020 10:24:13 +0100 Subject: [PATCH 1354/5615] Reduce dependabot bump frequency --- .dependabot/config.yml | 52 +++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/.dependabot/config.yml b/.dependabot/config.yml index 7fe8921a36b..bf80d72ab92 100644 --- a/.dependabot/config.yml +++ b/.dependabot/config.yml @@ -3,150 +3,150 @@ version: 1 update_configs: - package_manager: "java:gradle" directory: "/core" - update_schedule: "daily" + update_schedule: "weekly" # Explicit entry for each module - package_manager: "java:gradle" directory: "/modules/cassandra" - update_schedule: "daily" + update_schedule: "weekly" automerged_updates: - match: { dependency_type: "development", update_type: "semver:minor" } - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/clickhouse" - update_schedule: "daily" + update_schedule: "weekly" automerged_updates: - match: { dependency_type: "development", update_type: "semver:minor" } - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/couchbase" - update_schedule: "daily" + update_schedule: "weekly" automerged_updates: - match: { dependency_type: "development", update_type: "semver:minor" } - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/database-commons" - update_schedule: "daily" + update_schedule: "weekly" automerged_updates: - match: { dependency_type: "development", update_type: "semver:minor" } - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/dynalite" - update_schedule: "daily" + update_schedule: "weekly" automerged_updates: - match: { dependency_type: "development", update_type: "semver:minor" } - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/elasticsearch" - update_schedule: "daily" + update_schedule: "weekly" automerged_updates: - match: { dependency_type: "development", update_type: "semver:minor" } - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/influxdb" - update_schedule: "daily" + update_schedule: "weekly" automerged_updates: - match: { dependency_type: "development", update_type: "semver:minor" } - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/jdbc" - update_schedule: "daily" + update_schedule: "weekly" automerged_updates: - match: { dependency_type: "development", update_type: "semver:minor" } - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/jdbc-test" - update_schedule: "daily" + update_schedule: "weekly" automerged_updates: - match: { dependency_type: "development", update_type: "semver:minor" } - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/junit-jupiter" - update_schedule: "daily" + update_schedule: "weekly" automerged_updates: - match: { dependency_type: "development", update_type: "semver:minor" } - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/kafka" - update_schedule: "daily" + update_schedule: "weekly" automerged_updates: - match: { dependency_type: "development", update_type: "semver:minor" } - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/localstack" - update_schedule: "daily" + update_schedule: "weekly" automerged_updates: - match: { dependency_type: "development", update_type: "semver:minor" } - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/mariadb" - update_schedule: "daily" + update_schedule: "weekly" automerged_updates: - match: { dependency_type: "development", update_type: "semver:minor" } - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/mockserver" - update_schedule: "daily" + update_schedule: "weekly" automerged_updates: - match: { dependency_type: "development", update_type: "semver:minor" } - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/mssqlserver" - update_schedule: "daily" + update_schedule: "weekly" automerged_updates: - match: { dependency_type: "development", update_type: "semver:minor" } - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/mysql" - update_schedule: "daily" + update_schedule: "weekly" automerged_updates: - match: { dependency_type: "development", update_type: "semver:minor" } - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/neo4j" - update_schedule: "daily" + update_schedule: "weekly" automerged_updates: - match: { dependency_type: "development", update_type: "semver:minor" } - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/nginx" - update_schedule: "daily" + update_schedule: "weekly" automerged_updates: - match: { dependency_type: "development", update_type: "semver:minor" } - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/oracle-xe" - update_schedule: "daily" + update_schedule: "weekly" automerged_updates: - match: { dependency_type: "development", update_type: "semver:minor" } - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/postgresql" - update_schedule: "daily" + update_schedule: "weekly" automerged_updates: - match: { dependency_type: "development", update_type: "semver:minor" } - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/pulsar" - update_schedule: "daily" + update_schedule: "weekly" automerged_updates: - match: { dependency_type: "development", update_type: "semver:minor" } - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/selenium" - update_schedule: "daily" + update_schedule: "weekly" automerged_updates: - match: { dependency_type: "development", update_type: "semver:minor" } - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/spock" - update_schedule: "daily" + update_schedule: "weekly" automerged_updates: - match: { dependency_type: "development", update_type: "semver:minor" } - match: { dependency_type: "production", update_type: "semver:minor" } - package_manager: "java:gradle" directory: "/modules/vault" - update_schedule: "daily" + update_schedule: "weekly" automerged_updates: - match: { dependency_type: "development", update_type: "semver:minor" } - match: { dependency_type: "production", update_type: "semver:minor" } @@ -154,7 +154,7 @@ update_configs: # Examples - package_manager: "java:gradle" directory: "/examples" - update_schedule: "daily" + update_schedule: "weekly" automerged_updates: - match: { dependency_type: "development", update_type: "semver:minor" } - match: { dependency_type: "production", update_type: "semver:minor" } From 64fc34a10e0bab2b1675b3878e52be72d6c65451 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 14 May 2020 10:26:39 +0100 Subject: [PATCH 1355/5615] Bump assertj-core from 3.16.0 to 3.16.1 in /modules/pulsar (#2696) --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index ecfa7c5f26a..74626006ba0 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -4,5 +4,5 @@ dependencies { compile project(':testcontainers') testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.5.1' - testCompile group: 'org.assertj', name: 'assertj-core', version: '3.16.0' + testCompile group: 'org.assertj', name: 'assertj-core', version: '3.16.1' } From 279b5b25438e07856d437d4650cc0ec37b8b72de Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 14 May 2020 10:26:45 +0100 Subject: [PATCH 1356/5615] Bump assertj-core from 3.16.0 to 3.16.1 in /modules/vault (#2695) --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index b8681be150d..2b9f702f9d9 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -5,6 +5,6 @@ dependencies { testCompile 'com.bettercloud:vault-java-driver:5.1.0' testCompile 'io.rest-assured:rest-assured:4.3.0' - testCompile 'org.assertj:assertj-core:3.16.0' + testCompile 'org.assertj:assertj-core:3.16.1' } From 82be21d7272f4e25adbd14db281f5a63e5c35489 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 14 May 2020 10:27:12 +0100 Subject: [PATCH 1357/5615] Bump assertj-core from 3.16.0 to 3.16.1 in /modules/database-commons (#2703) --- modules/database-commons/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/database-commons/build.gradle b/modules/database-commons/build.gradle index 14dffc649ac..82e318e5017 100644 --- a/modules/database-commons/build.gradle +++ b/modules/database-commons/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: Database-Commons" dependencies { compile project(':testcontainers') - testCompile 'org.assertj:assertj-core:3.16.0' + testCompile 'org.assertj:assertj-core:3.16.1' } From 024d33261ed4c4337b4b6319a991d59e2401140f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 14 May 2020 10:27:25 +0100 Subject: [PATCH 1358/5615] Bump s3 from 2.13.12 to 2.13.16 in /modules/localstack (#2729) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 874e5fe54df..566eb403468 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -8,5 +8,5 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.778' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.777' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.775' - testCompile 'software.amazon.awssdk:s3:2.13.12' + testCompile 'software.amazon.awssdk:s3:2.13.16' } From 1cefa4b9255d13a0ef20c1cec96d127e3b8d7dcd Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 14 May 2020 10:27:37 +0100 Subject: [PATCH 1359/5615] Bump aws-java-sdk-sqs from 1.11.778 to 1.11.782 in /modules/localstack (#2732) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 566eb403468..44b732d4c60 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.778' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.775' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.778' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.782' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.777' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.775' testCompile 'software.amazon.awssdk:s3:2.13.16' From 1a9e55457ab0f9b8ec5b5b3f8b27428d307118ac Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 14 May 2020 10:29:43 +0100 Subject: [PATCH 1360/5615] Bump auto-service from 1.0-rc6 to 1.0-rc7 in /modules/mssqlserver (#2734) --- modules/mssqlserver/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index 4a496e9eb35..055548d4182 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -1,7 +1,7 @@ description = "Testcontainers :: MS SQL Server" dependencies { - annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6' + annotationProcessor 'com.google.auto.service:auto-service:1.0-rc7' compileOnly 'com.google.auto.service:auto-service:1.0-rc6' compile project(':jdbc') From 4ef332644a08ee57b3e01c57d275da3a33cec31b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 14 May 2020 10:30:07 +0100 Subject: [PATCH 1361/5615] Bump auto-service from 1.0-rc6 to 1.0-rc7 in /modules/postgresql (#2735) --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 9893ec28bd1..8c07465d424 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -1,7 +1,7 @@ description = "Testcontainers :: JDBC :: PostgreSQL" dependencies { - annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6' + annotationProcessor 'com.google.auto.service:auto-service:1.0-rc7' compileOnly 'com.google.auto.service:auto-service:1.0-rc6' compile project(':jdbc') From c40d56e4d44cfe4f5d0e0487a2f4b4627d9f6454 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 14 May 2020 10:30:16 +0100 Subject: [PATCH 1362/5615] Bump auto-service from 1.0-rc6 to 1.0-rc7 in /modules/mysql (#2736) --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 2ee2d62ce59..d41609ac6aa 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -1,7 +1,7 @@ description = "Testcontainers :: JDBC :: MySQL" dependencies { - annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6' + annotationProcessor 'com.google.auto.service:auto-service:1.0-rc7' compileOnly 'com.google.auto.service:auto-service:1.0-rc6' compile project(':jdbc') From 44ab8134b286febe8a40f979372b550fd40dd9d1 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 14 May 2020 10:30:31 +0100 Subject: [PATCH 1363/5615] Bump auto-service from 1.0-rc6 to 1.0-rc7 in /modules/mariadb (#2739) --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 226e34aae90..fe8788cb06b 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -1,7 +1,7 @@ description = "Testcontainers :: JDBC :: MariaDB" dependencies { - annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6' + annotationProcessor 'com.google.auto.service:auto-service:1.0-rc7' compileOnly 'com.google.auto.service:auto-service:1.0-rc6' compile project(':jdbc') From 3f7c0ed2916cc103051fdcb713d1dabf96da3a16 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 14 May 2020 10:32:40 +0100 Subject: [PATCH 1364/5615] Bump java-client from 2.7.14 to 2.7.15 in /modules/couchbase (#2701) --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 9dd0856fd51..9cb778fd479 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: Couchbase" dependencies { compile project(':testcontainers') - testCompile 'com.couchbase.client:java-client:2.7.14' + testCompile 'com.couchbase.client:java-client:2.7.15' } From c4e2e3184afdef0b265e16498b83c520a1cfb31a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 14 May 2020 10:33:06 +0100 Subject: [PATCH 1365/5615] Bump tomcat-jdbc from 9.0.34 to 9.0.35 in /modules/mysql (#2713) --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index d41609ac6aa..04648e35f94 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -11,7 +11,7 @@ dependencies { testCompile 'mysql:mysql-connector-java:8.0.20' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.34' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.35' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.vibur:vibur-dbcp:25.0' From e508b542f6d54adad17bf104ac0e34afd8ccc7f3 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 14 May 2020 10:33:40 +0100 Subject: [PATCH 1366/5615] Bump transport from 6.7.1 to 7.7.0 in /modules/elasticsearch (#2737) --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 6deaf802112..959d17e3d02 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,5 +3,5 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.6.2" - testCompile "org.elasticsearch.client:transport:6.7.1" + testCompile "org.elasticsearch.client:transport:7.7.0" } From 79d89ce75c300d26904a75f84430a118ab36d69b Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 14 May 2020 10:42:33 +0100 Subject: [PATCH 1367/5615] Revert "Bump transport from 6.7.1 to 7.7.0 in /modules/elasticsearch" (#2740) --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 959d17e3d02..6deaf802112 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,5 +3,5 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.6.2" - testCompile "org.elasticsearch.client:transport:7.7.0" + testCompile "org.elasticsearch.client:transport:6.7.1" } From 2f8bbdd57287bcf0409b02cf85f0343a05f52e68 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 14 May 2020 11:05:41 +0100 Subject: [PATCH 1368/5615] Bump assertj-core from 3.16.0 to 3.16.1 in /core (#2706) --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 312298bf77d..4c62a2416ef 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -165,7 +165,7 @@ dependencies { // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest testCompile files('testlib/repo/fakejar/fakejar/0/fakejar-0.jar') - testCompile 'org.assertj:assertj-core:3.16.0' + testCompile 'org.assertj:assertj-core:3.16.1' testCompile project(':test-support') jarFileTestCompileOnly "org.projectlombok:lombok:${lombok.version}" From c18a203e3bf06872c0f73a2743bb2c4da0a72023 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 14 May 2020 11:37:08 +0100 Subject: [PATCH 1369/5615] Split JDBC tests (#2556) --- modules/clickhouse/build.gradle | 3 + .../clickhouse/ClickhouseJDBCDriverTest.java | 21 ++++ .../clickhouse/SimpleClickhouseTest.java | 25 +++++ modules/cockroachdb/build.gradle | 3 + .../CockroachDBJDBCDriverTest.java | 21 ++++ .../cockroachdb}/SimpleCockroachDBTest.java | 3 +- .../resources/somepath/init_postgresql.sql | 0 modules/db2/build.gradle | 3 + .../jdbc/db2/DB2JDBCDriverTest.java | 21 ++++ .../junit/db2/SimpleDb2Test.java | 27 ++++++ .../container-license-acceptance.txt | 1 + modules/jdbc-test/build.gradle | 46 ++++----- .../db}/AbstractContainerDatabaseTest.java | 23 +---- .../jdbc/AbstractJDBCDriverTest.java} | 42 +------- .../{test => main}/resources/logback-test.xml | 0 .../junit/CustomizableMSSQLServerTest.java | 44 --------- .../junit/CustomizableMysqlTest.java | 47 --------- .../junit/CustomizablePostgreSQLTest.java | 48 ---------- .../junit/EmptyPasswordMysqlTest.java | 46 --------- .../junit/SimpleClickhouseTest.java | 25 ----- .../testcontainers/junit/SimpleDb2Test.java | 41 -------- .../junit/SimpleMSSQLServerTest.java | 47 --------- .../container-license-acceptance.txt | 2 - modules/jdbc/build.gradle | 7 ++ .../jupiter/inheritance/InheritedTests.java | 1 - modules/mariadb/build.gradle | 5 +- .../jdbc/mariadb/MariaDBJDBCDriverTest.java | 29 ++++++ .../junit/mariadb}/SimpleMariaDBTest.java | 42 +++----- .../test/resources/somepath/init_mariadb.sql | 0 .../somepath/init_unicode_mariadb.sql} | 0 .../somepath/mariadb_conf_override/my.cnf | 0 modules/mssqlserver/build.gradle | 3 + .../MSSQLServerJDBCDriverTest.java | 21 ++++ .../CustomPasswordMSSQLServerTest.java | 4 +- .../CustomizableMSSQLServerTest.java | 28 ++++++ .../mssqlserver/SimpleMSSQLServerTest.java | 47 +++++++++ modules/mysql/build.gradle | 5 +- modules/mysql/sql/init_mysql.sql | 5 + .../jdbc/mysql}/JDBCDriverWithPoolTest.java | 36 ++++--- .../jdbc/mysql/MySQLJDBCDriverTest.java | 30 ++++++ .../junit/mysql/CustomizableMysqlTest.java | 34 +++++++ .../junit/mysql}/MultiVersionMySQLTest.java | 9 +- .../junit/mysql}/SimpleMySQLTest.java | 96 ++++++++++--------- .../test/resources/somepath/init_mysql.sql | 0 .../resources/somepath/init_unicode_mysql.sql | 5 + .../somepath/mysql_conf_override/my.cnf | 0 modules/oracle-xe/build.gradle | 3 +- .../{ => jdbc}/OracleJDBCDriverTest.java | 2 +- .../junit/oracle}/SimpleOracleTest.java | 19 ++-- modules/postgresql/build.gradle | 5 +- .../PostgreSQLConnectionURLTest.java | 10 +- .../jdbc/DatabaseDriverShutdownTest.java | 5 +- .../jdbc/DatabaseDriverTmpfsTest.java | 6 ++ .../jdbc/postgis/PostgisJDBCDriverTest.java | 22 +++++ .../postgresql/PostgreSQLJDBCDriverTest.java | 21 ++++ .../CustomizablePostgreSQLTest.java | 35 +++++++ .../postgresql}/SimplePostgreSQLTest.java | 11 ++- .../resources/somepath/init_postgresql.sql | 5 + modules/presto/build.gradle | 4 +- .../jdbc/presto/PrestoJDBCDriverTest.java | 21 ++++ test-support/build.gradle | 2 +- 61 files changed, 596 insertions(+), 521 deletions(-) create mode 100644 modules/clickhouse/src/test/java/org/testcontainers/jdbc/clickhouse/ClickhouseJDBCDriverTest.java create mode 100644 modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java create mode 100644 modules/cockroachdb/src/test/java/org/testcontainers/jdbc/cockroachdb/CockroachDBJDBCDriverTest.java rename modules/{jdbc-test/src/test/java/org/testcontainers/junit => cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb}/SimpleCockroachDBTest.java (93%) rename modules/{jdbc-test => cockroachdb}/src/test/resources/somepath/init_postgresql.sql (100%) create mode 100644 modules/db2/src/test/java/org/testcontainers/jdbc/db2/DB2JDBCDriverTest.java create mode 100644 modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java create mode 100644 modules/db2/src/test/resources/container-license-acceptance.txt rename modules/jdbc-test/src/{test/java/org/testcontainers/junit => main/java/org/testcontainers/db}/AbstractContainerDatabaseTest.java (55%) rename modules/jdbc-test/src/{test/java/org/testcontainers/jdbc/JDBCDriverTest.java => main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java} (63%) rename modules/jdbc-test/src/{test => main}/resources/logback-test.xml (100%) delete mode 100644 modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomizableMSSQLServerTest.java delete mode 100644 modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomizableMysqlTest.java delete mode 100644 modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomizablePostgreSQLTest.java delete mode 100644 modules/jdbc-test/src/test/java/org/testcontainers/junit/EmptyPasswordMysqlTest.java delete mode 100644 modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleClickhouseTest.java delete mode 100644 modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleDb2Test.java delete mode 100644 modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMSSQLServerTest.java delete mode 100644 modules/jdbc-test/src/test/resources/container-license-acceptance.txt create mode 100644 modules/mariadb/src/test/java/org/testcontainers/jdbc/mariadb/MariaDBJDBCDriverTest.java rename modules/{jdbc-test/src/test/java/org/testcontainers/junit => mariadb/src/test/java/org/testcontainers/junit/mariadb}/SimpleMariaDBTest.java (70%) rename modules/{jdbc-test => mariadb}/src/test/resources/somepath/init_mariadb.sql (100%) rename modules/{jdbc-test/src/test/resources/somepath/init_unicode_mysql.sql => mariadb/src/test/resources/somepath/init_unicode_mariadb.sql} (100%) rename modules/{jdbc-test => mariadb}/src/test/resources/somepath/mariadb_conf_override/my.cnf (100%) create mode 100644 modules/mssqlserver/src/test/java/org/testcontainers/jdbc/mssqlserver/MSSQLServerJDBCDriverTest.java rename modules/{jdbc-test/src/test/java/org/testcontainers/junit => mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver}/CustomPasswordMSSQLServerTest.java (95%) create mode 100644 modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomizableMSSQLServerTest.java create mode 100644 modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java create mode 100644 modules/mysql/sql/init_mysql.sql rename modules/{jdbc-test/src/test/java/org/testcontainers/jdbc => mysql/src/test/java/org/testcontainers/jdbc/mysql}/JDBCDriverWithPoolTest.java (78%) create mode 100644 modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/MySQLJDBCDriverTest.java create mode 100644 modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java rename modules/{jdbc-test/src/test/java/org/testcontainers/junit => mysql/src/test/java/org/testcontainers/junit/mysql}/MultiVersionMySQLTest.java (76%) rename modules/{jdbc-test/src/test/java/org/testcontainers/junit => mysql/src/test/java/org/testcontainers/junit/mysql}/SimpleMySQLTest.java (58%) rename modules/{jdbc-test => mysql}/src/test/resources/somepath/init_mysql.sql (100%) create mode 100644 modules/mysql/src/test/resources/somepath/init_unicode_mysql.sql rename modules/{jdbc-test => mysql}/src/test/resources/somepath/mysql_conf_override/my.cnf (100%) rename modules/oracle-xe/src/test/java/org/testcontainers/containers/{ => jdbc}/OracleJDBCDriverTest.java (97%) rename modules/{jdbc-test/src/test/java/org/testcontainers/junit => oracle-xe/src/test/java/org/testcontainers/junit/oracle}/SimpleOracleTest.java (50%) rename modules/{jdbc-test => postgresql}/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java (76%) rename modules/{jdbc-test => postgresql}/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java (71%) create mode 100644 modules/postgresql/src/test/java/org/testcontainers/jdbc/postgis/PostgisJDBCDriverTest.java create mode 100644 modules/postgresql/src/test/java/org/testcontainers/jdbc/postgresql/PostgreSQLJDBCDriverTest.java create mode 100644 modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/CustomizablePostgreSQLTest.java rename modules/{jdbc-test/src/test/java/org/testcontainers/junit => postgresql/src/test/java/org/testcontainers/junit/postgresql}/SimplePostgreSQLTest.java (78%) create mode 100644 modules/postgresql/src/test/resources/somepath/init_postgresql.sql create mode 100644 modules/presto/src/test/java/org/testcontainers/jdbc/presto/PrestoJDBCDriverTest.java diff --git a/modules/clickhouse/build.gradle b/modules/clickhouse/build.gradle index 4f0e1a1fd90..323475b1d1c 100644 --- a/modules/clickhouse/build.gradle +++ b/modules/clickhouse/build.gradle @@ -3,4 +3,7 @@ description = "Testcontainers :: JDBC :: ClickHouse" dependencies { compile project(':testcontainers') compile project(':jdbc') + + testCompile project(':jdbc-test') + testCompile 'ru.yandex.clickhouse:clickhouse-jdbc:0.2.4' } diff --git a/modules/clickhouse/src/test/java/org/testcontainers/jdbc/clickhouse/ClickhouseJDBCDriverTest.java b/modules/clickhouse/src/test/java/org/testcontainers/jdbc/clickhouse/ClickhouseJDBCDriverTest.java new file mode 100644 index 00000000000..f3946c8a317 --- /dev/null +++ b/modules/clickhouse/src/test/java/org/testcontainers/jdbc/clickhouse/ClickhouseJDBCDriverTest.java @@ -0,0 +1,21 @@ +package org.testcontainers.jdbc.clickhouse; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.testcontainers.jdbc.AbstractJDBCDriverTest; + +import java.util.EnumSet; + +import static java.util.Arrays.asList; + +@RunWith(Parameterized.class) +public class ClickhouseJDBCDriverTest extends AbstractJDBCDriverTest { + + @Parameterized.Parameters(name = "{index} - {0}") + public static Iterable data() { + return asList( + new Object[][]{ + {"jdbc:tc:clickhouse://hostname/databasename", EnumSet.of(Options.PmdKnownBroken)}, + }); + } +} diff --git a/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java b/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java new file mode 100644 index 00000000000..2dbce0b3593 --- /dev/null +++ b/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java @@ -0,0 +1,25 @@ +package org.testcontainers.junit.clickhouse; + +import org.junit.Test; +import org.testcontainers.containers.ClickHouseContainer; +import org.testcontainers.db.AbstractContainerDatabaseTest; + +import java.sql.ResultSet; +import java.sql.SQLException; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; + +public class SimpleClickhouseTest extends AbstractContainerDatabaseTest { + + @Test + public void testSimple() throws SQLException { + try (ClickHouseContainer clickhouse = new ClickHouseContainer()) { + clickhouse.start(); + + ResultSet resultSet = performQuery(clickhouse, "SELECT 1"); + + int resultSetInt = resultSet.getInt(1); + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + } + } +} diff --git a/modules/cockroachdb/build.gradle b/modules/cockroachdb/build.gradle index e52f9f3729c..a3927258679 100644 --- a/modules/cockroachdb/build.gradle +++ b/modules/cockroachdb/build.gradle @@ -2,4 +2,7 @@ description = "Testcontainers :: JDBC :: CockroachDB" dependencies { compile project(':jdbc') + + testCompile project(':jdbc-test') + testCompile 'org.postgresql:postgresql:42.2.12' } diff --git a/modules/cockroachdb/src/test/java/org/testcontainers/jdbc/cockroachdb/CockroachDBJDBCDriverTest.java b/modules/cockroachdb/src/test/java/org/testcontainers/jdbc/cockroachdb/CockroachDBJDBCDriverTest.java new file mode 100644 index 00000000000..75b1fadb47e --- /dev/null +++ b/modules/cockroachdb/src/test/java/org/testcontainers/jdbc/cockroachdb/CockroachDBJDBCDriverTest.java @@ -0,0 +1,21 @@ +package org.testcontainers.jdbc.cockroachdb; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.testcontainers.jdbc.AbstractJDBCDriverTest; + +import java.util.EnumSet; + +import static java.util.Arrays.asList; + +@RunWith(Parameterized.class) +public class CockroachDBJDBCDriverTest extends AbstractJDBCDriverTest { + + @Parameterized.Parameters(name = "{index} - {0}") + public static Iterable data() { + return asList( + new Object[][]{ + {"jdbc:tc:cockroach://hostname/databasename", EnumSet.noneOf(Options.class)}, + }); + } +} diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleCockroachDBTest.java b/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java similarity index 93% rename from modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleCockroachDBTest.java rename to modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java index c21a5ddfaf1..7c62b2f6bcf 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleCockroachDBTest.java +++ b/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java @@ -1,7 +1,8 @@ -package org.testcontainers.junit; +package org.testcontainers.junit.cockroachdb; import org.junit.Test; import org.testcontainers.containers.CockroachContainer; +import org.testcontainers.db.AbstractContainerDatabaseTest; import java.sql.ResultSet; import java.sql.SQLException; diff --git a/modules/jdbc-test/src/test/resources/somepath/init_postgresql.sql b/modules/cockroachdb/src/test/resources/somepath/init_postgresql.sql similarity index 100% rename from modules/jdbc-test/src/test/resources/somepath/init_postgresql.sql rename to modules/cockroachdb/src/test/resources/somepath/init_postgresql.sql diff --git a/modules/db2/build.gradle b/modules/db2/build.gradle index a92631df264..feaf5f790c5 100644 --- a/modules/db2/build.gradle +++ b/modules/db2/build.gradle @@ -2,4 +2,7 @@ description = "Testcontainers :: JDBC :: DB2" dependencies { compile project(':jdbc') + + testCompile project(':jdbc-test') + testCompile 'com.ibm.db2:jcc:11.5.0.0' } diff --git a/modules/db2/src/test/java/org/testcontainers/jdbc/db2/DB2JDBCDriverTest.java b/modules/db2/src/test/java/org/testcontainers/jdbc/db2/DB2JDBCDriverTest.java new file mode 100644 index 00000000000..2c87a4a1a65 --- /dev/null +++ b/modules/db2/src/test/java/org/testcontainers/jdbc/db2/DB2JDBCDriverTest.java @@ -0,0 +1,21 @@ +package org.testcontainers.jdbc.db2; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.testcontainers.jdbc.AbstractJDBCDriverTest; + +import java.util.EnumSet; + +import static java.util.Arrays.asList; + +@RunWith(Parameterized.class) +public class DB2JDBCDriverTest extends AbstractJDBCDriverTest { + + @Parameterized.Parameters(name = "{index} - {0}") + public static Iterable data() { + return asList( + new Object[][]{ + {"jdbc:tc:db2://hostname/databasename", EnumSet.noneOf(Options.class)} + }); + } +} diff --git a/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java b/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java new file mode 100644 index 00000000000..08f427cf12b --- /dev/null +++ b/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java @@ -0,0 +1,27 @@ +package org.testcontainers.junit.db2; + +import org.junit.Test; +import org.testcontainers.containers.Db2Container; +import org.testcontainers.db.AbstractContainerDatabaseTest; + +import java.sql.ResultSet; +import java.sql.SQLException; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; + +public class SimpleDb2Test extends AbstractContainerDatabaseTest { + + @Test + public void testSimple() throws SQLException { + try (Db2Container db2 = new Db2Container() + .acceptLicense()) { + + db2.start(); + + ResultSet resultSet = performQuery(db2, "SELECT 1 FROM SYSIBM.SYSDUMMY1"); + + int resultSetInt = resultSet.getInt(1); + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + } + } +} diff --git a/modules/db2/src/test/resources/container-license-acceptance.txt b/modules/db2/src/test/resources/container-license-acceptance.txt new file mode 100644 index 00000000000..c4ad0a38410 --- /dev/null +++ b/modules/db2/src/test/resources/container-license-acceptance.txt @@ -0,0 +1 @@ +ibmcom/db2:11.5.0.0a diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index c7c909782bb..8b36f126b86 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -1,35 +1,21 @@ -repositories { - maven { - url "https://maven.atlassian.com/3rdparty/" - } -} - dependencies { - compile project(':mysql') - compile project(':postgresql') - compile project(':presto') - compile project(':cockroachdb') - compile project(':mariadb') - compile project(':oracle-xe') - compile project(':mssqlserver') - compile project(':clickhouse') - compile project(':db2') + compile project(':jdbc') + compile project(':test-support') - testCompile 'com.google.guava:guava:18.0' - testCompile 'org.postgresql:postgresql:42.0.0' - testCompile 'io.prestosql:presto-jdbc:329' - testCompile 'mysql:mysql-connector-java:8.0.14' - testCompile 'org.mariadb.jdbc:mariadb-java-client:1.4.6' - testCompile 'com.oracle:ojdbc6:12.1.0.1-atlassian-hosted' - testCompile 'com.microsoft.sqlserver:mssql-jdbc:6.1.0.jre8' - testCompile 'ru.yandex.clickhouse:clickhouse-jdbc:0.1.41' - testCompile 'com.ibm.db2:jcc:11.1.4.4' + compile 'com.google.guava:guava:28.2-jre' + compile 'org.apache.commons:commons-lang3:3.10' + compile 'com.zaxxer:HikariCP-java6:2.3.13' + compile 'commons-dbutils:commons-dbutils:1.7' - testCompile 'com.zaxxer:HikariCP-java6:2.3.8' - testCompile 'org.apache.tomcat:tomcat-jdbc:8.5.4' - testCompile 'org.vibur:vibur-dbcp:9.0' - testCompile 'commons-dbutils:commons-dbutils:1.6' + compile 'com.googlecode.junit-toolbox:junit-toolbox:2.4' + + compile('org.rnorth.visible-assertions:visible-assertions:2.1.2') { + // Excluded in favor of jna-platform + exclude(group: "net.java.dev.jna", module: "jna") + } - testCompile 'com.googlecode.junit-toolbox:junit-toolbox:2.4' - testCompile project(':test-support') + compile 'commons-dbutils:commons-dbutils:1.7' + compile 'org.apache.tomcat:tomcat-jdbc:9.0.33' + compile 'org.vibur:vibur-dbcp:25.0' + compile 'mysql:mysql-connector-java:8.0.19' } diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/AbstractContainerDatabaseTest.java b/modules/jdbc-test/src/main/java/org/testcontainers/db/AbstractContainerDatabaseTest.java similarity index 55% rename from modules/jdbc-test/src/test/java/org/testcontainers/junit/AbstractContainerDatabaseTest.java rename to modules/jdbc-test/src/main/java/org/testcontainers/db/AbstractContainerDatabaseTest.java index 7230759382d..8881ed963d8 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/AbstractContainerDatabaseTest.java +++ b/modules/jdbc-test/src/main/java/org/testcontainers/db/AbstractContainerDatabaseTest.java @@ -1,22 +1,17 @@ -package org.testcontainers.junit; +package org.testcontainers.db; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; -import org.junit.After; import org.testcontainers.containers.JdbcDatabaseContainer; import javax.sql.DataSource; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import java.util.HashSet; -import java.util.Set; -abstract class AbstractContainerDatabaseTest { +public abstract class AbstractContainerDatabaseTest { - private final Set datasourcesForCleanup = new HashSet<>(); - - ResultSet performQuery(JdbcDatabaseContainer container, String sql) throws SQLException { + protected ResultSet performQuery(JdbcDatabaseContainer container, String sql) throws SQLException { DataSource ds = getDataSource(container); Statement statement = ds.getConnection().createStatement(); statement.execute(sql); @@ -26,21 +21,13 @@ ResultSet performQuery(JdbcDatabaseContainer container, String sql) throws SQLEx return resultSet; } - DataSource getDataSource(JdbcDatabaseContainer container) { + protected DataSource getDataSource(JdbcDatabaseContainer container) { HikariConfig hikariConfig = new HikariConfig(); hikariConfig.setJdbcUrl(container.getJdbcUrl()); hikariConfig.setUsername(container.getUsername()); hikariConfig.setPassword(container.getPassword()); hikariConfig.setDriverClassName(container.getDriverClassName()); - final HikariDataSource dataSource = new HikariDataSource(hikariConfig); - datasourcesForCleanup.add(dataSource); - - return dataSource; - } - - @After - public void teardown() { - datasourcesForCleanup.forEach(HikariDataSource::close); + return new HikariDataSource(hikariConfig); } } diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java b/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java similarity index 63% rename from modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java rename to modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java index c96ce251a63..38ca2237567 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverTest.java +++ b/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java @@ -6,8 +6,6 @@ import org.apache.commons.lang.SystemUtils; import org.junit.AfterClass; import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameter; import java.sql.Connection; @@ -16,15 +14,13 @@ import java.sql.Statement; import java.util.EnumSet; -import static java.util.Arrays.asList; import static org.junit.Assume.assumeFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -@RunWith(Parameterized.class) -public class JDBCDriverTest { +public class AbstractJDBCDriverTest { - private enum Options { + protected enum Options { ScriptedSchema, CharacterSet, CustomIniFile, @@ -37,40 +33,6 @@ private enum Options { @Parameter(1) public EnumSet options; - @Parameterized.Parameters(name = "{index} - {0}") - public static Iterable data() { - return asList( - new Object[][]{ - {"jdbc:tc:mysql://hostname/databasename", EnumSet.noneOf(Options.class)}, - {"jdbc:tc:mysql://hostname/databasename?user=someuser&TC_INITSCRIPT=somepath/init_mysql.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=somepath/init_mysql.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=file:sql/init_mysql.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&password=somepwd&TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mysql:5.5.43://hostname/databasename?TC_INITSCRIPT=somepath/init_unicode_mysql.sql&useUnicode=yes&characterEncoding=utf8", EnumSet.of(Options.CharacterSet)}, - {"jdbc:tc:mysql:5.5.43://hostname/databasename", EnumSet.noneOf(Options.class)}, - {"jdbc:tc:mysql:5.5.43://hostname/databasename?useSSL=false", EnumSet.noneOf(Options.class)}, - {"jdbc:tc:postgresql:9.6.8://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, - {"jdbc:tc:postgis://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, - {"jdbc:tc:postgis:9.6://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, - {"jdbc:tc:presto:329://hostname/", EnumSet.of(Options.PmdKnownBroken)}, - {"jdbc:tc:mysql:5.6://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override", EnumSet.of(Options.CustomIniFile)}, - {"jdbc:tc:mariadb://hostname/databasename", EnumSet.noneOf(Options.class)}, - {"jdbc:tc:mariadb://hostname/databasename?user=someuser&TC_INITSCRIPT=somepath/init_mariadb.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename", EnumSet.noneOf(Options.class)}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITSCRIPT=somepath/init_unicode_mysql.sql&useUnicode=yes&characterEncoding=utf8", EnumSet.of(Options.CharacterSet)}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&TC_INITSCRIPT=somepath/init_mariadb.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=somepath/init_mariadb.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&password=somepwd&TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_MY_CNF=somepath/mariadb_conf_override", EnumSet.of(Options.CustomIniFile)}, - {"jdbc:tc:clickhouse://hostname/databasename", EnumSet.of(Options.PmdKnownBroken)}, - {"jdbc:tc:sqlserver:2017-CU12://hostname:hostport;databaseName=databasename", EnumSet.noneOf(Options.class)}, - {"jdbc:tc:cockroach://hostname/databasename", EnumSet.noneOf(Options.class)}, -// {"jdbc:tc:db2://hostname/databasename", EnumSet.noneOf(Options.class)} - }); - } - public static void sampleInitFunction(Connection connection) throws SQLException { connection.createStatement().execute("CREATE TABLE bar (\n" + " foo VARCHAR(255)\n" + diff --git a/modules/jdbc-test/src/test/resources/logback-test.xml b/modules/jdbc-test/src/main/resources/logback-test.xml similarity index 100% rename from modules/jdbc-test/src/test/resources/logback-test.xml rename to modules/jdbc-test/src/main/resources/logback-test.xml diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomizableMSSQLServerTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomizableMSSQLServerTest.java deleted file mode 100644 index 2f6e43a771f..00000000000 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomizableMSSQLServerTest.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.testcontainers.junit; - -import com.zaxxer.hikari.HikariConfig; -import com.zaxxer.hikari.HikariDataSource; -import org.junit.Rule; -import org.junit.Test; -import org.testcontainers.containers.MSSQLServerContainer; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.fail; - -public class CustomizableMSSQLServerTest { - - private static final String STRONG_PASSWORD = "myStrong(!)Password"; - - @Rule - public MSSQLServerContainer mssqlServerContainer = new MSSQLServerContainer() - .withPassword(STRONG_PASSWORD); - - @Test - public void testSqlServerConnection() throws SQLException { - HikariConfig hikariConfig = new HikariConfig(); - hikariConfig.setJdbcUrl("jdbc:sqlserver://localhost:" + - mssqlServerContainer.getMappedPort(MSSQLServerContainer.MS_SQL_SERVER_PORT)); - hikariConfig.setUsername("SA"); - hikariConfig.setPassword(STRONG_PASSWORD); - - HikariDataSource ds = new HikariDataSource(hikariConfig); - Statement statement = ds.getConnection().createStatement(); - statement.execute(mssqlServerContainer.getTestQueryString()); - ResultSet resultSet = statement.getResultSet(); - if (resultSet.next()) { - int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); - } else { - fail("No results returned from query"); - } - - } -} diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomizableMysqlTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomizableMysqlTest.java deleted file mode 100644 index 11ad8f111ee..00000000000 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomizableMysqlTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.testcontainers.junit; - -import com.zaxxer.hikari.HikariConfig; -import com.zaxxer.hikari.HikariDataSource; -import org.junit.Rule; -import org.junit.Test; -import org.testcontainers.containers.MySQLContainer; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; - -public class CustomizableMysqlTest { - private static final String DB_NAME = "foo"; - private static final String USER = "bar"; - private static final String PWD = "baz"; - - // Add MYSQL_ROOT_HOST environment so that we can root login from anywhere for testing purposes - @Rule - public MySQLContainer mysql = (MySQLContainer) new MySQLContainer("mysql:5.5") - .withDatabaseName(DB_NAME) - .withUsername(USER) - .withPassword(PWD) - .withEnv("MYSQL_ROOT_HOST", "%"); - - @Test - public void testSimple() throws SQLException { - HikariConfig hikariConfig = new HikariConfig(); - hikariConfig.setJdbcUrl("jdbc:mysql://" - + mysql.getContainerIpAddress() - + ":" + mysql.getMappedPort(MySQLContainer.MYSQL_PORT) - + "/" + DB_NAME); - hikariConfig.setUsername(USER); - hikariConfig.setPassword(PWD); - - HikariDataSource ds = new HikariDataSource(hikariConfig); - Statement statement = ds.getConnection().createStatement(); - statement.execute("SELECT 1"); - ResultSet resultSet = statement.getResultSet(); - - assertEquals("There is a result", resultSet.next(), true); - int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); - } -} diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomizablePostgreSQLTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomizablePostgreSQLTest.java deleted file mode 100644 index ef1c7b212db..00000000000 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomizablePostgreSQLTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.testcontainers.junit; - -import com.zaxxer.hikari.HikariConfig; -import com.zaxxer.hikari.HikariDataSource; -import org.junit.Rule; -import org.junit.Test; -import org.testcontainers.containers.PostgreSQLContainer; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; - -/** - * @author richardnorth - */ -public class CustomizablePostgreSQLTest { - private static final String DB_NAME = "foo"; - private static final String USER = "bar"; - private static final String PWD = "baz"; - - @Rule - public PostgreSQLContainer postgres = new PostgreSQLContainer("postgres:9.6.8") - .withDatabaseName(DB_NAME) - .withUsername(USER) - .withPassword(PWD); - - @Test - public void testSimple() throws SQLException { - HikariConfig hikariConfig = new HikariConfig(); - hikariConfig.setJdbcUrl("jdbc:postgresql://" - + postgres.getContainerIpAddress() - + ":" + postgres.getMappedPort(PostgreSQLContainer.POSTGRESQL_PORT) - + "/" + DB_NAME); - hikariConfig.setUsername(USER); - hikariConfig.setPassword(PWD); - - HikariDataSource ds = new HikariDataSource(hikariConfig); - Statement statement = ds.getConnection().createStatement(); - statement.execute("SELECT 1"); - ResultSet resultSet = statement.getResultSet(); - - resultSet.next(); - int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); - } -} diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/EmptyPasswordMysqlTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/EmptyPasswordMysqlTest.java deleted file mode 100644 index 0dfc3149ef5..00000000000 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/EmptyPasswordMysqlTest.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.testcontainers.junit; - -import com.zaxxer.hikari.HikariConfig; -import com.zaxxer.hikari.HikariDataSource; -import org.junit.Rule; -import org.junit.Test; -import org.testcontainers.containers.MySQLContainer; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; - -public class EmptyPasswordMysqlTest { - private static final String DB_NAME = "foo"; - private static final String USER = "root"; // MySQL allows empty password with root only - - // Add MYSQL_ROOT_HOST environment so that we can root login from anywhere for testing purposes - @Rule - public MySQLContainer mysql = (MySQLContainer) new MySQLContainer("mysql:5.5") - .withDatabaseName(DB_NAME) - .withUsername(USER) - .withPassword("") - .withEnv("MYSQL_ROOT_HOST", "%"); - - @Test - public void testSimple() throws SQLException { - HikariConfig hikariConfig = new HikariConfig(); - hikariConfig.setJdbcUrl("jdbc:mysql://" - + mysql.getContainerIpAddress() - + ":" + mysql.getMappedPort(MySQLContainer.MYSQL_PORT) - + "/" + DB_NAME); - hikariConfig.setUsername(USER); - hikariConfig.setPassword(""); - - HikariDataSource ds = new HikariDataSource(hikariConfig); - Statement statement = ds.getConnection().createStatement(); - statement.execute("SELECT 1"); - ResultSet resultSet = statement.getResultSet(); - - assertEquals("There is a result", resultSet.next(), true); - int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); - } -} diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleClickhouseTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleClickhouseTest.java deleted file mode 100644 index b765208e212..00000000000 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleClickhouseTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.testcontainers.junit; - -import org.junit.Rule; -import org.junit.Test; -import org.testcontainers.containers.ClickHouseContainer; - -import java.sql.ResultSet; -import java.sql.SQLException; - -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; - -public class SimpleClickhouseTest extends AbstractContainerDatabaseTest { - - @Rule - public ClickHouseContainer clickhouse = new ClickHouseContainer(); - - @Test - public void testSimple() throws SQLException { - ResultSet resultSet = performQuery(clickhouse, "SELECT 1"); - - resultSet.next(); - int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); - } -} diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleDb2Test.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleDb2Test.java deleted file mode 100644 index 60ad9a23a27..00000000000 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleDb2Test.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.testcontainers.junit; - -import com.zaxxer.hikari.HikariConfig; -import com.zaxxer.hikari.HikariDataSource; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.testcontainers.containers.Db2Container; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; - - -@Ignore("Disabled since we had problems with CI disk space filling up from images in jdbc-test module") -public class SimpleDb2Test { - - @Rule - public Db2Container db2 = new Db2Container() - .acceptLicense(); - - @Test - public void testSimple() throws SQLException { - HikariConfig hikariConfig = new HikariConfig(); - hikariConfig.setJdbcUrl(db2.getJdbcUrl()); - hikariConfig.setUsername(db2.getUsername()); - hikariConfig.setPassword(db2.getPassword()); - - HikariDataSource ds = new HikariDataSource(hikariConfig); - Statement statement = ds.getConnection().createStatement(); - statement.execute("SELECT 1 FROM SYSIBM.SYSDUMMY1"); - ResultSet resultSet = statement.getResultSet(); - - resultSet.next(); - int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); - } - -} diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMSSQLServerTest.java b/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMSSQLServerTest.java deleted file mode 100644 index 2791efa0780..00000000000 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMSSQLServerTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.testcontainers.junit; - -import org.junit.Rule; -import org.junit.Test; -import org.testcontainers.containers.MSSQLServerContainer; - -import javax.sql.DataSource; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; - -/** - * @author Stefan Hufschmidt - */ -public class SimpleMSSQLServerTest extends AbstractContainerDatabaseTest { - - @Rule - public MSSQLServerContainer mssqlServer = new MSSQLServerContainer(); - - @Test - public void testSimple() throws SQLException { - ResultSet resultSet = performQuery(mssqlServer, "SELECT 1"); - - int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); - } - - @Test - public void testSetupDatabase() throws SQLException { - DataSource ds = getDataSource(mssqlServer); - Statement statement = ds.getConnection().createStatement(); - statement.executeUpdate("CREATE DATABASE [test];"); - statement = ds.getConnection().createStatement(); - statement.executeUpdate("CREATE TABLE [test].[dbo].[Foo](ID INT PRIMARY KEY);"); - statement = ds.getConnection().createStatement(); - statement.executeUpdate("INSERT INTO [test].[dbo].[Foo] (ID) VALUES (3);"); - statement = ds.getConnection().createStatement(); - statement.execute("SELECT * FROM [test].[dbo].[Foo];"); - ResultSet resultSet = statement.getResultSet(); - - resultSet.next(); - int resultSetInt = resultSet.getInt("ID"); - assertEquals("A basic SELECT query succeeds", 3, resultSetInt); - } -} diff --git a/modules/jdbc-test/src/test/resources/container-license-acceptance.txt b/modules/jdbc-test/src/test/resources/container-license-acceptance.txt deleted file mode 100644 index 4af979f0f18..00000000000 --- a/modules/jdbc-test/src/test/resources/container-license-acceptance.txt +++ /dev/null @@ -1,2 +0,0 @@ -mcr.microsoft.com/mssql/server:2017-CU12 -ibmcom/db2:11.5.0.0a diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 2278b14006d..aa21939c769 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -2,4 +2,11 @@ description = "Testcontainers :: JDBC" dependencies { compile project(':database-commons') + testCompile project(':test-support') + + testCompile 'commons-dbutils:commons-dbutils:1.7' + testCompile 'org.vibur:vibur-dbcp:25.0' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.33' + testCompile 'com.zaxxer:HikariCP-java6:2.3.13' + testCompile 'com.googlecode.junit-toolbox:junit-toolbox:2.4' } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/InheritedTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/InheritedTests.java index f050e428692..720c31cf794 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/InheritedTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/InheritedTests.java @@ -2,7 +2,6 @@ import org.junit.jupiter.api.Test; import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index fe8788cb06b..71f2417f2ad 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -9,11 +9,8 @@ dependencies { compileOnly project(':r2dbc') compileOnly 'org.mariadb:r2dbc-mariadb:0.8.1-alpha1' + testCompile project(':jdbc-test') testCompile 'org.mariadb.jdbc:mariadb-java-client:2.6.0' - testCompile 'com.zaxxer:HikariCP-java6:2.3.13' - testCompile 'commons-dbutils:commons-dbutils:1.7' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.35' - testCompile 'org.vibur:vibur-dbcp:25.0' testCompile testFixtures(project(':r2dbc')) testCompile 'org.mariadb:r2dbc-mariadb:0.8.1-alpha1' diff --git a/modules/mariadb/src/test/java/org/testcontainers/jdbc/mariadb/MariaDBJDBCDriverTest.java b/modules/mariadb/src/test/java/org/testcontainers/jdbc/mariadb/MariaDBJDBCDriverTest.java new file mode 100644 index 00000000000..ef1dfef2703 --- /dev/null +++ b/modules/mariadb/src/test/java/org/testcontainers/jdbc/mariadb/MariaDBJDBCDriverTest.java @@ -0,0 +1,29 @@ +package org.testcontainers.jdbc.mariadb; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.testcontainers.jdbc.AbstractJDBCDriverTest; + +import java.util.EnumSet; + +import static java.util.Arrays.asList; + +@RunWith(Parameterized.class) +public class MariaDBJDBCDriverTest extends AbstractJDBCDriverTest { + + @Parameterized.Parameters(name = "{index} - {0}") + public static Iterable data() { + return asList( + new Object[][]{ + {"jdbc:tc:mariadb://hostname/databasename", EnumSet.noneOf(Options.class)}, + {"jdbc:tc:mariadb://hostname/databasename?user=someuser&TC_INITSCRIPT=somepath/init_mariadb.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, + {"jdbc:tc:mariadb:10.2.14://hostname/databasename", EnumSet.noneOf(Options.class)}, + {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITSCRIPT=somepath/init_unicode_mariadb.sql&useUnicode=yes&characterEncoding=utf8", EnumSet.of(Options.CharacterSet)}, + {"jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&TC_INITSCRIPT=somepath/init_mariadb.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, + {"jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&TC_INITFUNCTION=org.testcontainers.jdbc.AbstractJDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, + {"jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=somepath/init_mariadb.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, + {"jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&password=somepwd&TC_INITFUNCTION=org.testcontainers.jdbc.AbstractJDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, + {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_MY_CNF=somepath/mariadb_conf_override", EnumSet.of(Options.CustomIniFile)}, + }); + } +} diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMariaDBTest.java b/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java similarity index 70% rename from modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMariaDBTest.java rename to modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java index 21badefb9fb..646fbb40947 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMariaDBTest.java +++ b/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java @@ -1,8 +1,9 @@ -package org.testcontainers.junit; +package org.testcontainers.junit.mariadb; import org.apache.commons.lang.SystemUtils; import org.junit.Test; import org.testcontainers.containers.MariaDBContainer; +import org.testcontainers.db.AbstractContainerDatabaseTest; import java.sql.ResultSet; import java.sql.SQLException; @@ -12,72 +13,59 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -/** - * @author Miguel Gonzalez Sanchez - */ public class SimpleMariaDBTest extends AbstractContainerDatabaseTest { @Test public void testSimple() throws SQLException { - MariaDBContainer mariadb = new MariaDBContainer(); - mariadb.start(); + try (MariaDBContainer mariadb = new MariaDBContainer<>()) { + + mariadb.start(); - try { ResultSet resultSet = performQuery(mariadb, "SELECT 1"); int resultSetInt = resultSet.getInt(1); assertEquals("A basic SELECT query succeeds", 1, resultSetInt); - } finally { - mariadb.stop(); } } @Test public void testSpecificVersion() throws SQLException { - MariaDBContainer mariadbOldVersion = new MariaDBContainer("mariadb:5.5.51"); - mariadbOldVersion.start(); + try (MariaDBContainer mariadbOldVersion = new MariaDBContainer<>("mariadb:5.5.51")) { + + mariadbOldVersion.start(); - try { ResultSet resultSet = performQuery(mariadbOldVersion, "SELECT VERSION()"); String resultSetString = resultSet.getString(1); assertTrue("The database version can be set using a container rule parameter", resultSetString.startsWith("5.5.51")); - } finally { - mariadbOldVersion.stop(); } } @Test public void testMariaDBWithCustomIniFile() throws SQLException { assumeFalse(SystemUtils.IS_OS_WINDOWS); - MariaDBContainer mariadbCustomConfig = new MariaDBContainer("mariadb:10.1.16") - .withConfigurationOverride("somepath/mariadb_conf_override"); - mariadbCustomConfig.start(); - try { + try (MariaDBContainer mariadbCustomConfig = new MariaDBContainer<>("mariadb:10.1.16") + .withConfigurationOverride("somepath/mariadb_conf_override")) { + mariadbCustomConfig.start(); + ResultSet resultSet = performQuery(mariadbCustomConfig, "SELECT @@GLOBAL.innodb_file_format"); String result = resultSet.getString(1); assertEquals("The InnoDB file format has been set by the ini file content", "Barracuda", result); - } finally { - mariadbCustomConfig.stop(); } } @Test public void testMariaDBWithCommandOverride() throws SQLException { - MariaDBContainer mariadbCustomConfig = (MariaDBContainer) new MariaDBContainer("mariadb:10.1.16") - .withCommand("mysqld --auto_increment_increment=10"); - mariadbCustomConfig.start(); - - try { + try (MariaDBContainer mariadbCustomConfig = new MariaDBContainer<>("mariadb:10.1.16") + .withCommand("mysqld --auto_increment_increment=10")) { + mariadbCustomConfig.start(); ResultSet resultSet = performQuery(mariadbCustomConfig, "show variables like 'auto_increment_increment'"); String result = resultSet.getString("Value"); assertEquals("Auto increment increment should be overriden by command line", "10", result); - } finally { - mariadbCustomConfig.stop(); } } } diff --git a/modules/jdbc-test/src/test/resources/somepath/init_mariadb.sql b/modules/mariadb/src/test/resources/somepath/init_mariadb.sql similarity index 100% rename from modules/jdbc-test/src/test/resources/somepath/init_mariadb.sql rename to modules/mariadb/src/test/resources/somepath/init_mariadb.sql diff --git a/modules/jdbc-test/src/test/resources/somepath/init_unicode_mysql.sql b/modules/mariadb/src/test/resources/somepath/init_unicode_mariadb.sql similarity index 100% rename from modules/jdbc-test/src/test/resources/somepath/init_unicode_mysql.sql rename to modules/mariadb/src/test/resources/somepath/init_unicode_mariadb.sql diff --git a/modules/jdbc-test/src/test/resources/somepath/mariadb_conf_override/my.cnf b/modules/mariadb/src/test/resources/somepath/mariadb_conf_override/my.cnf similarity index 100% rename from modules/jdbc-test/src/test/resources/somepath/mariadb_conf_override/my.cnf rename to modules/mariadb/src/test/resources/somepath/mariadb_conf_override/my.cnf diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index 055548d4182..c21bc43c869 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -9,6 +9,9 @@ dependencies { compileOnly project(':r2dbc') compileOnly 'io.r2dbc:r2dbc-mssql:0.8.2.RELEASE' + testCompile project(':jdbc-test') + testCompile 'com.microsoft.sqlserver:mssql-jdbc:8.2.2.jre8' + testCompile project(':r2dbc') testCompile 'io.r2dbc:r2dbc-mssql:0.8.1.RELEASE' diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/jdbc/mssqlserver/MSSQLServerJDBCDriverTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/jdbc/mssqlserver/MSSQLServerJDBCDriverTest.java new file mode 100644 index 00000000000..00e22199b46 --- /dev/null +++ b/modules/mssqlserver/src/test/java/org/testcontainers/jdbc/mssqlserver/MSSQLServerJDBCDriverTest.java @@ -0,0 +1,21 @@ +package org.testcontainers.jdbc.mssqlserver; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.testcontainers.jdbc.AbstractJDBCDriverTest; + +import java.util.EnumSet; + +import static java.util.Arrays.asList; + +@RunWith(Parameterized.class) +public class MSSQLServerJDBCDriverTest extends AbstractJDBCDriverTest { + + @Parameterized.Parameters(name = "{index} - {0}") + public static Iterable data() { + return asList( + new Object[][]{ + {"jdbc:tc:sqlserver:2017-CU12://hostname:hostport;databaseName=databasename", EnumSet.noneOf(Options.class)}, + }); + } +} diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomPasswordMSSQLServerTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java similarity index 95% rename from modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomPasswordMSSQLServerTest.java rename to modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java index 2f77dad44fc..0e1ea05b31d 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/CustomPasswordMSSQLServerTest.java +++ b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java @@ -1,4 +1,4 @@ -package org.testcontainers.junit; +package org.testcontainers.junit.mssqlserver; import org.apache.commons.lang.RandomStringUtils; import org.junit.Test; @@ -66,7 +66,7 @@ public static Collection data() { @Test public void runPasswordTests() { try { - new MSSQLServerContainer().withPassword(this.password); + new MSSQLServerContainer<>().withPassword(this.password); if (!valid) fail("Password " + this.password + " is not valid. Expected exception"); } catch (IllegalArgumentException e) { diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomizableMSSQLServerTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomizableMSSQLServerTest.java new file mode 100644 index 00000000000..ecbc4e31714 --- /dev/null +++ b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomizableMSSQLServerTest.java @@ -0,0 +1,28 @@ +package org.testcontainers.junit.mssqlserver; + +import org.junit.Test; +import org.testcontainers.containers.MSSQLServerContainer; +import org.testcontainers.db.AbstractContainerDatabaseTest; + +import java.sql.ResultSet; +import java.sql.SQLException; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; + +public class CustomizableMSSQLServerTest extends AbstractContainerDatabaseTest { + + private static final String STRONG_PASSWORD = "myStrong(!)Password"; + + @Test + public void testSqlServerConnection() throws SQLException { + try (MSSQLServerContainer mssqlServerContainer = new MSSQLServerContainer<>() + .withPassword(STRONG_PASSWORD)) { + + mssqlServerContainer.start(); + + ResultSet resultSet = performQuery(mssqlServerContainer, mssqlServerContainer.getTestQueryString()); + int resultSetInt = resultSet.getInt(1); + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + } + } +} diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java new file mode 100644 index 00000000000..c1699d2473a --- /dev/null +++ b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java @@ -0,0 +1,47 @@ +package org.testcontainers.junit.mssqlserver; + +import org.junit.Test; +import org.testcontainers.containers.MSSQLServerContainer; +import org.testcontainers.db.AbstractContainerDatabaseTest; + +import javax.sql.DataSource; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; + +public class SimpleMSSQLServerTest extends AbstractContainerDatabaseTest { + + @Test + public void testSimple() throws SQLException { + try (MSSQLServerContainer mssqlServer = new MSSQLServerContainer<>()) { + mssqlServer.start(); + ResultSet resultSet = performQuery(mssqlServer, "SELECT 1"); + + int resultSetInt = resultSet.getInt(1); + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + } + } + + @Test + public void testSetupDatabase() throws SQLException { + try (MSSQLServerContainer mssqlServer = new MSSQLServerContainer<>()) { + mssqlServer.start(); + DataSource ds = getDataSource(mssqlServer); + Statement statement = ds.getConnection().createStatement(); + statement.executeUpdate("CREATE DATABASE [test];"); + statement = ds.getConnection().createStatement(); + statement.executeUpdate("CREATE TABLE [test].[dbo].[Foo](ID INT PRIMARY KEY);"); + statement = ds.getConnection().createStatement(); + statement.executeUpdate("INSERT INTO [test].[dbo].[Foo] (ID) VALUES (3);"); + statement = ds.getConnection().createStatement(); + statement.execute("SELECT * FROM [test].[dbo].[Foo];"); + ResultSet resultSet = statement.getResultSet(); + + resultSet.next(); + int resultSetInt = resultSet.getInt("ID"); + assertEquals("A basic SELECT query succeeds", 3, resultSetInt); + } + } +} diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 04648e35f94..c9c165cc874 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -9,11 +9,8 @@ dependencies { compileOnly project(':r2dbc') compileOnly 'dev.miku:r2dbc-mysql:0.8.1.RELEASE' + testCompile project(':jdbc-test') testCompile 'mysql:mysql-connector-java:8.0.20' - testCompile 'com.zaxxer:HikariCP-java6:2.3.13' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.35' - testCompile 'commons-dbutils:commons-dbutils:1.7' - testCompile 'org.vibur:vibur-dbcp:25.0' testCompile testFixtures(project(':r2dbc')) testCompile 'dev.miku:r2dbc-mysql:0.8.1.RELEASE' diff --git a/modules/mysql/sql/init_mysql.sql b/modules/mysql/sql/init_mysql.sql new file mode 100644 index 00000000000..2b00ee968b0 --- /dev/null +++ b/modules/mysql/sql/init_mysql.sql @@ -0,0 +1,5 @@ +CREATE TABLE bar ( + foo VARCHAR(255) +); + +INSERT INTO bar (foo) VALUES ('hello world'); \ No newline at end of file diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverWithPoolTest.java b/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/JDBCDriverWithPoolTest.java similarity index 78% rename from modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverWithPoolTest.java rename to modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/JDBCDriverWithPoolTest.java index 53daa1316f5..1ad77264c62 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/JDBCDriverWithPoolTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/JDBCDriverWithPoolTest.java @@ -1,4 +1,4 @@ -package org.testcontainers.jdbc; +package org.testcontainers.jdbc.mysql; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; @@ -8,6 +8,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import org.testcontainers.jdbc.ContainerDatabaseDriver; import org.vibur.dbcp.ViburDBCPDataSource; import javax.sql.DataSource; @@ -22,23 +23,26 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; /** - * + * This test belongs in the jdbc module, as it is focused on testing the behaviour of {@link org.testcontainers.containers.JdbcDatabaseContainer}. + * However, the need to use the {@link org.testcontainers.containers.MySQLContainerProvider} (due to the jdbc:tc:mysql) URL forces it to live here in + * the mysql module, to avoid circular dependencies. + * TODO: Move to the jdbc module and either (a) implement a barebones {@link org.testcontainers.containers.JdbcDatabaseContainerProvider} for testing, or (b) refactor into a unit test. */ @RunWith(Parameterized.class) public class JDBCDriverWithPoolTest { - public static final String URL = "jdbc:tc:mysql:5://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverWithPoolTest::sampleInitFunction"; + public static final String URL = "jdbc:tc:mysql:5://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.mysql.JDBCDriverWithPoolTest::sampleInitFunction"; private final DataSource dataSource; @Parameterized.Parameters public static Iterable> dataSourceSuppliers() { return asList( - JDBCDriverWithPoolTest::getTomcatDataSourceWithDriverClassName, - JDBCDriverWithPoolTest::getTomcatDataSource, - JDBCDriverWithPoolTest::getHikariDataSourceWithDriverClassName, - JDBCDriverWithPoolTest::getHikariDataSource, - JDBCDriverWithPoolTest::getViburDataSourceWithDriverClassName, - JDBCDriverWithPoolTest::getViburDataSource + JDBCDriverWithPoolTest::getTomcatDataSourceWithDriverClassName, + JDBCDriverWithPoolTest::getTomcatDataSource, + JDBCDriverWithPoolTest::getHikariDataSourceWithDriverClassName, + JDBCDriverWithPoolTest::getHikariDataSource, + JDBCDriverWithPoolTest::getViburDataSourceWithDriverClassName, + JDBCDriverWithPoolTest::getViburDataSource ); } @@ -56,7 +60,7 @@ public void testMySQLWithConnectionPoolUsingSameContainer() throws SQLException, executorService.submit(() -> { try { new QueryRunner(dataSource).insert("INSERT INTO my_counter (n) VALUES (5)", - (ResultSetHandler) rs -> true); + (ResultSetHandler) rs -> true); } catch (SQLException e) { e.printStackTrace(); } @@ -130,6 +134,8 @@ private static DataSource getViburDataSourceWithDriverClassName() { ViburDBCPDataSource ds = new ViburDBCPDataSource(); ds.setJdbcUrl(URL + ";TEST=VIBUR_WITH_CLASSNAME"); + ds.setUsername("any"); // Recent versions of Vibur require a username, even though it will not be used + ds.setPassword(""); ds.setPoolInitialSize(3); ds.setPoolMaxSize(10); ds.setTestConnectionQuery("SELECT 1"); @@ -143,6 +149,8 @@ private static DataSource getViburDataSourceWithDriverClassName() { private static DataSource getViburDataSource() { ViburDBCPDataSource ds = new ViburDBCPDataSource(); ds.setJdbcUrl(URL + ";TEST=VIBUR"); + ds.setUsername("any"); // Recent versions of Vibur require a username, even though it will not be used + ds.setPassword(""); ds.setPoolInitialSize(3); ds.setPoolMaxSize(10); ds.setTestConnectionQuery("SELECT 1"); @@ -155,11 +163,11 @@ private static DataSource getViburDataSource() { @SuppressWarnings("SqlNoDataSourceInspection") public static void sampleInitFunction(Connection connection) throws SQLException { connection.createStatement().execute("CREATE TABLE bar (\n" + - " foo VARCHAR(255)\n" + - ");"); + " foo VARCHAR(255)\n" + + ");"); connection.createStatement().execute("INSERT INTO bar (foo) VALUES ('hello world');"); connection.createStatement().execute("CREATE TABLE my_counter (\n" + - " n INT\n" + - ");"); + " n INT\n" + + ");"); } } diff --git a/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/MySQLJDBCDriverTest.java b/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/MySQLJDBCDriverTest.java new file mode 100644 index 00000000000..d4b37b291d5 --- /dev/null +++ b/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/MySQLJDBCDriverTest.java @@ -0,0 +1,30 @@ +package org.testcontainers.jdbc.mysql; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.testcontainers.jdbc.AbstractJDBCDriverTest; + +import java.util.EnumSet; + +import static java.util.Arrays.asList; + +@RunWith(Parameterized.class) +public class MySQLJDBCDriverTest extends AbstractJDBCDriverTest { + + @Parameterized.Parameters(name = "{index} - {0}") + public static Iterable data() { + return asList( + new Object[][]{ + {"jdbc:tc:mysql://hostname/databasename", EnumSet.noneOf(Options.class)}, + {"jdbc:tc:mysql://hostname/databasename?user=someuser&TC_INITSCRIPT=somepath/init_mysql.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, + {"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&TC_INITFUNCTION=org.testcontainers.jdbc.AbstractJDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, + {"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=somepath/init_mysql.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, + {"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=file:sql/init_mysql.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, + {"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&password=somepwd&TC_INITFUNCTION=org.testcontainers.jdbc.AbstractJDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, + {"jdbc:tc:mysql:5.5.43://hostname/databasename?TC_INITSCRIPT=somepath/init_unicode_mysql.sql&useUnicode=yes&characterEncoding=utf8", EnumSet.of(Options.CharacterSet)}, + {"jdbc:tc:mysql:5.5.43://hostname/databasename", EnumSet.noneOf(Options.class)}, + {"jdbc:tc:mysql:5.5.43://hostname/databasename?useSSL=false", EnumSet.noneOf(Options.class)}, + {"jdbc:tc:mysql:5.6://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override", EnumSet.of(Options.CustomIniFile)}, + }); + } +} diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java new file mode 100644 index 00000000000..590d288e3fe --- /dev/null +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java @@ -0,0 +1,34 @@ +package org.testcontainers.junit.mysql; + +import org.junit.Test; +import org.testcontainers.containers.MySQLContainer; +import org.testcontainers.db.AbstractContainerDatabaseTest; + +import java.sql.ResultSet; +import java.sql.SQLException; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; + +public class CustomizableMysqlTest extends AbstractContainerDatabaseTest { + private static final String DB_NAME = "foo"; + private static final String USER = "bar"; + private static final String PWD = "baz"; + + @Test + public void testSimple() throws SQLException { + // Add MYSQL_ROOT_HOST environment so that we can root login from anywhere for testing purposes + try (MySQLContainer mysql = new MySQLContainer<>("mysql:5.5") + .withDatabaseName(DB_NAME) + .withUsername(USER) + .withPassword(PWD) + .withEnv("MYSQL_ROOT_HOST", "%")) { + + mysql.start(); + + ResultSet resultSet = performQuery(mysql, "SELECT 1"); + + int resultSetInt = resultSet.getInt(1); + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + } + } +} diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/MultiVersionMySQLTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java similarity index 76% rename from modules/jdbc-test/src/test/java/org/testcontainers/junit/MultiVersionMySQLTest.java rename to modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java index b685d6dad80..3c0c86ffafa 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/MultiVersionMySQLTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java @@ -1,9 +1,10 @@ -package org.testcontainers.junit; +package org.testcontainers.junit.mysql; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.testcontainers.containers.MySQLContainer; +import org.testcontainers.db.AbstractContainerDatabaseTest; import java.sql.ResultSet; import java.sql.SQLException; @@ -26,9 +27,9 @@ public MultiVersionMySQLTest(String version) { @Test public void versionCheckTest() throws SQLException { - try (final MySQLContainer container = new MySQLContainer<>("mysql:" + version)) { - container.start(); - final ResultSet resultSet = performQuery(container, "SELECT VERSION()"); + try (MySQLContainer mysql = new MySQLContainer<>("mysql:" + version)) { + mysql.start(); + final ResultSet resultSet = performQuery(mysql, "SELECT VERSION()"); final String resultSetString = resultSet.getString(1); assertEquals("The database version can be set using a container rule parameter", version, resultSetString); diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java similarity index 58% rename from modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java rename to modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java index 9570659c4dc..ee6c3ab6993 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java @@ -1,4 +1,4 @@ -package org.testcontainers.junit; +package org.testcontainers.junit.mysql; import org.apache.commons.lang.SystemUtils; import org.junit.Test; @@ -7,6 +7,7 @@ import org.testcontainers.containers.ContainerLaunchException; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.db.AbstractContainerDatabaseTest; import java.sql.ResultSet; import java.sql.SQLException; @@ -17,9 +18,6 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -/** - * @author richardnorth - */ public class SimpleMySQLTest extends AbstractContainerDatabaseTest { private static final Logger logger = LoggerFactory.getLogger(SimpleMySQLTest.class); @@ -31,86 +29,79 @@ public class SimpleMySQLTest extends AbstractContainerDatabaseTest { */ /* @ClassRule - public static MySQLContainer mysql = new MySQLContainer(); + public static MySQLContainer mysql = new MySQLContainer<>(); @ClassRule - public static MySQLContainer mysqlOldVersion = new MySQLContainer("mysql:5.5"); + public static MySQLContainer mysqlOldVersion = new MySQLContainer<>("mysql:5.5"); @ClassRule - public static MySQLContainer mysqlCustomConfig = new MySQLContainer("mysql:5.6") + public static MySQLContainer mysqlCustomConfig = new MySQLContainer<>("mysql:5.6") .withConfigurationOverride("somepath/mysql_conf_override"); */ @Test public void testSimple() throws SQLException { - MySQLContainer mysql = (MySQLContainer) new MySQLContainer() - .withConfigurationOverride("somepath/mysql_conf_override") - .withLogConsumer(new Slf4jLogConsumer(logger)); - mysql.start(); + try (MySQLContainer mysql = new MySQLContainer<>() + .withConfigurationOverride("somepath/mysql_conf_override") + .withLogConsumer(new Slf4jLogConsumer(logger))) { + + mysql.start(); - try { ResultSet resultSet = performQuery(mysql, "SELECT 1"); int resultSetInt = resultSet.getInt(1); assertEquals("A basic SELECT query succeeds", 1, resultSetInt); - } finally { - mysql.stop(); } } @Test public void testSpecificVersion() throws SQLException { - MySQLContainer mysqlOldVersion = (MySQLContainer) new MySQLContainer("mysql:5.5") - .withConfigurationOverride("somepath/mysql_conf_override") - .withLogConsumer(new Slf4jLogConsumer(logger)); - mysqlOldVersion.start(); + try (MySQLContainer mysqlOldVersion = new MySQLContainer<>("mysql:5.5") + .withConfigurationOverride("somepath/mysql_conf_override") + .withLogConsumer(new Slf4jLogConsumer(logger))) { + + mysqlOldVersion.start(); - try { ResultSet resultSet = performQuery(mysqlOldVersion, "SELECT VERSION()"); String resultSetString = resultSet.getString(1); assertTrue("The database version can be set using a container rule parameter", resultSetString.startsWith("5.5")); - } finally { - mysqlOldVersion.stop(); } } @Test public void testMySQLWithCustomIniFile() throws SQLException { - assumeFalse(SystemUtils.IS_OS_WINDOWS); - MySQLContainer mysqlCustomConfig = new MySQLContainer("mysql:5.6") - .withConfigurationOverride("somepath/mysql_conf_override"); - mysqlCustomConfig.start(); + assumeFalse(SystemUtils.IS_OS_WINDOWS); + + try (MySQLContainer mysqlCustomConfig = new MySQLContainer<>("mysql:5.6") + .withConfigurationOverride("somepath/mysql_conf_override")) { + + mysqlCustomConfig.start(); - try { ResultSet resultSet = performQuery(mysqlCustomConfig, "SELECT @@GLOBAL.innodb_file_format"); String result = resultSet.getString(1); assertEquals("The InnoDB file format has been set by the ini file content", "Barracuda", result); - } finally { - mysqlCustomConfig.stop(); } } @Test public void testCommandOverride() throws SQLException { - MySQLContainer mysqlCustomConfig = (MySQLContainer) new MySQLContainer().withCommand("mysqld --auto_increment_increment=42"); - mysqlCustomConfig.start(); + try (MySQLContainer mysqlCustomConfig = new MySQLContainer<>() + .withCommand("mysqld --auto_increment_increment=42")) { + + mysqlCustomConfig.start(); - try { ResultSet resultSet = performQuery(mysqlCustomConfig, "show variables like 'auto_increment_increment'"); String result = resultSet.getString("Value"); assertEquals("Auto increment increment should be overriden by command line", "42", result); - } finally { - mysqlCustomConfig.stop(); } - } @Test public void testExplicitInitScript() throws SQLException { - try (MySQLContainer container = (MySQLContainer) new MySQLContainer() + try (MySQLContainer container = new MySQLContainer<>() .withInitScript("somepath/init_mysql.sql") .withLogConsumer(new Slf4jLogConsumer(logger))) { container.start(); @@ -120,20 +111,35 @@ public void testExplicitInitScript() throws SQLException { assertEquals("Value from init script should equal real value", "hello world", firstColumnValue); } - } + } - @Test + @Test(expected = ContainerLaunchException.class) public void testEmptyPasswordWithNonRootUser() { - - MySQLContainer container = (MySQLContainer) new MySQLContainer("mysql:5.5").withDatabaseName("TEST") - .withUsername("test").withPassword("").withEnv("MYSQL_ROOT_HOST", "%"); - - try { + try (MySQLContainer container = new MySQLContainer<>("mysql:5.5") + .withDatabaseName("TEST") + .withUsername("test") + .withPassword("") + .withEnv("MYSQL_ROOT_HOST", "%")){ container.start(); fail("ContainerLaunchException expected to be thrown"); - } catch (ContainerLaunchException e) { - } finally { - container.stop(); + } + } + + @Test + public void testEmptyPasswordWithRootUser() throws SQLException { + // Add MYSQL_ROOT_HOST environment so that we can root login from anywhere for testing purposes + try (MySQLContainer mysql = new MySQLContainer<>("mysql:5.5") + .withDatabaseName("foo") + .withUsername("root") + .withPassword("") + .withEnv("MYSQL_ROOT_HOST", "%")) { + + mysql.start(); + + ResultSet resultSet = performQuery(mysql, "SELECT 1"); + int resultSetInt = resultSet.getInt(1); + + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); } } } diff --git a/modules/jdbc-test/src/test/resources/somepath/init_mysql.sql b/modules/mysql/src/test/resources/somepath/init_mysql.sql similarity index 100% rename from modules/jdbc-test/src/test/resources/somepath/init_mysql.sql rename to modules/mysql/src/test/resources/somepath/init_mysql.sql diff --git a/modules/mysql/src/test/resources/somepath/init_unicode_mysql.sql b/modules/mysql/src/test/resources/somepath/init_unicode_mysql.sql new file mode 100644 index 00000000000..f88969b0f30 --- /dev/null +++ b/modules/mysql/src/test/resources/somepath/init_unicode_mysql.sql @@ -0,0 +1,5 @@ +CREATE TABLE bar ( + foo varchar(255) character set utf8 +); + +INSERT INTO bar (foo) VALUES ('привет мир'); diff --git a/modules/jdbc-test/src/test/resources/somepath/mysql_conf_override/my.cnf b/modules/mysql/src/test/resources/somepath/mysql_conf_override/my.cnf similarity index 100% rename from modules/jdbc-test/src/test/resources/somepath/mysql_conf_override/my.cnf rename to modules/mysql/src/test/resources/somepath/mysql_conf_override/my.cnf diff --git a/modules/oracle-xe/build.gradle b/modules/oracle-xe/build.gradle index 1ab61e997af..026521e9a0c 100644 --- a/modules/oracle-xe/build.gradle +++ b/modules/oracle-xe/build.gradle @@ -3,7 +3,6 @@ description = "Testcontainers :: JDBC :: Oracle XE" dependencies { compile project(':jdbc') + testCompile project(':jdbc-test') testCompile 'com.oracle.ojdbc:ojdbc8:19.3.0.0' - testCompile 'com.zaxxer:HikariCP-java6:2.3.8' - testCompile 'commons-dbutils:commons-dbutils:1.7' } diff --git a/modules/oracle-xe/src/test/java/org/testcontainers/containers/OracleJDBCDriverTest.java b/modules/oracle-xe/src/test/java/org/testcontainers/containers/jdbc/OracleJDBCDriverTest.java similarity index 97% rename from modules/oracle-xe/src/test/java/org/testcontainers/containers/OracleJDBCDriverTest.java rename to modules/oracle-xe/src/test/java/org/testcontainers/containers/jdbc/OracleJDBCDriverTest.java index fcc09ab69a4..fbe5ce95a05 100644 --- a/modules/oracle-xe/src/test/java/org/testcontainers/containers/OracleJDBCDriverTest.java +++ b/modules/oracle-xe/src/test/java/org/testcontainers/containers/jdbc/OracleJDBCDriverTest.java @@ -1,4 +1,4 @@ -package org.testcontainers.containers; +package org.testcontainers.containers.jdbc; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleOracleTest.java b/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java similarity index 50% rename from modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleOracleTest.java rename to modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java index 0855fd4860e..2415fed11f4 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleOracleTest.java +++ b/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java @@ -1,31 +1,26 @@ -package org.testcontainers.junit; +package org.testcontainers.junit.oracle; import org.junit.Ignore; -import org.junit.Rule; import org.junit.Test; import org.testcontainers.containers.OracleContainer; +import org.testcontainers.db.AbstractContainerDatabaseTest; import java.sql.ResultSet; import java.sql.SQLException; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -/** - * @author gusohal - */ @Ignore public class SimpleOracleTest extends AbstractContainerDatabaseTest { - @Rule - public OracleContainer oracle = new OracleContainer(); - @Test public void testSimple() throws SQLException { - ResultSet resultSet = performQuery(oracle, "SELECT 1 FROM dual"); + try (OracleContainer oracle = new OracleContainer()) { + ResultSet resultSet = performQuery(oracle, "SELECT 1 FROM dual"); - resultSet.next(); - int resultSetInt = resultSet.getInt(1); + int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + } } } diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 8c07465d424..380a3f86a80 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -5,12 +5,13 @@ dependencies { compileOnly 'com.google.auto.service:auto-service:1.0-rc6' compile project(':jdbc') + compileOnly project(':r2dbc') compileOnly 'io.r2dbc:r2dbc-postgresql:0.8.2.RELEASE' + testCompile project(':jdbc-test') + testCompile project(':test-support') testCompile 'org.postgresql:postgresql:42.2.12' - testCompile 'commons-dbutils:commons-dbutils:1.7' - testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile testFixtures(project(':r2dbc')) testCompile 'io.r2dbc:r2dbc-postgresql:0.8.1.RELEASE' diff --git a/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java index ec6a8756cab..3b887cedbec 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java @@ -11,7 +11,7 @@ public class PostgreSQLConnectionURLTest { @Test public void shouldCorrectlyAppendQueryString() { - PostgreSQLContainer postgres = new FixedJdbcUrlPostgreSQLContainer(); + PostgreSQLContainer postgres = new FixedJdbcUrlPostgreSQLContainer(); String connectionUrl = postgres.constructUrlForConnection("?stringtype=unspecified&stringtype=unspecified"); String queryString = connectionUrl.substring(connectionUrl.indexOf('?')); @@ -22,7 +22,7 @@ public void shouldCorrectlyAppendQueryString() { @Test public void shouldCorrectlyAppendQueryStringWhenNoBaseParams() { - PostgreSQLContainer postgres = new NoParamsUrlPostgreSQLContainer(); + PostgreSQLContainer postgres = new NoParamsUrlPostgreSQLContainer(); String connectionUrl = postgres.constructUrlForConnection("?stringtype=unspecified&stringtype=unspecified"); String queryString = connectionUrl.substring(connectionUrl.indexOf('?')); @@ -33,7 +33,7 @@ public void shouldCorrectlyAppendQueryStringWhenNoBaseParams() { @Test public void shouldReturnOriginalURLWhenEmptyQueryString() { - PostgreSQLContainer postgres = new FixedJdbcUrlPostgreSQLContainer(); + PostgreSQLContainer postgres = new FixedJdbcUrlPostgreSQLContainer(); String connectionUrl = postgres.constructUrlForConnection(""); assertTrue("Query String remains unchanged", postgres.getJdbcUrl().equals(connectionUrl)); @@ -45,7 +45,7 @@ public void shouldRejectInvalidQueryString() { () -> new NoParamsUrlPostgreSQLContainer().constructUrlForConnection("stringtype=unspecified")); } - static class FixedJdbcUrlPostgreSQLContainer extends PostgreSQLContainer { + static class FixedJdbcUrlPostgreSQLContainer extends PostgreSQLContainer { @Override public String getContainerIpAddress() { @@ -58,7 +58,7 @@ public Integer getMappedPort(int originalPort) { } } - static class NoParamsUrlPostgreSQLContainer extends PostgreSQLContainer { + static class NoParamsUrlPostgreSQLContainer extends PostgreSQLContainer { @Override public String getJdbcUrl() { diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java b/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java similarity index 76% rename from modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java rename to modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java index 855658ada56..9e3e0b03413 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java @@ -13,7 +13,10 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; /** - * Created by inikolaev on 08/06/2017. + * This test belongs in the jdbc module, as it is focused on testing the behaviour of {@link org.testcontainers.containers.JdbcDatabaseContainer}. + * However, the need to use the {@link org.testcontainers.containers.PostgreSQLContainerProvider} (due to the jdbc:tc:postgresql) URL forces it to live here in + * the mysql module, to avoid circular dependencies. + * TODO: Move to the jdbc module and either (a) implement a barebones {@link org.testcontainers.containers.JdbcDatabaseContainerProvider} for testing, or (b) refactor into a unit test. */ public class DatabaseDriverShutdownTest { @AfterClass diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java b/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java similarity index 71% rename from modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java rename to modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java index 6f337a586b6..a2b1fd5da8b 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java @@ -10,6 +10,12 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; +/** + * This test belongs in the jdbc module, as it is focused on testing the behaviour of {@link org.testcontainers.containers.JdbcDatabaseContainer}. + * However, the need to use the {@link org.testcontainers.containers.PostgreSQLContainerProvider} (due to the jdbc:tc:postgresql) URL forces it to live here in + * the mysql module, to avoid circular dependencies. + * TODO: Move to the jdbc module and either (a) implement a barebones {@link org.testcontainers.containers.JdbcDatabaseContainerProvider} for testing, or (b) refactor into a unit test. + */ public class DatabaseDriverTmpfsTest { @Test diff --git a/modules/postgresql/src/test/java/org/testcontainers/jdbc/postgis/PostgisJDBCDriverTest.java b/modules/postgresql/src/test/java/org/testcontainers/jdbc/postgis/PostgisJDBCDriverTest.java new file mode 100644 index 00000000000..551ba86e185 --- /dev/null +++ b/modules/postgresql/src/test/java/org/testcontainers/jdbc/postgis/PostgisJDBCDriverTest.java @@ -0,0 +1,22 @@ +package org.testcontainers.jdbc.postgis; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.testcontainers.jdbc.AbstractJDBCDriverTest; + +import java.util.EnumSet; + +import static java.util.Arrays.asList; + +@RunWith(Parameterized.class) +public class PostgisJDBCDriverTest extends AbstractJDBCDriverTest { + + @Parameterized.Parameters(name = "{index} - {0}") + public static Iterable data() { + return asList( + new Object[][]{ + {"jdbc:tc:postgis://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, + {"jdbc:tc:postgis:9.6://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, + }); + } +} diff --git a/modules/postgresql/src/test/java/org/testcontainers/jdbc/postgresql/PostgreSQLJDBCDriverTest.java b/modules/postgresql/src/test/java/org/testcontainers/jdbc/postgresql/PostgreSQLJDBCDriverTest.java new file mode 100644 index 00000000000..c63eb9ffb9d --- /dev/null +++ b/modules/postgresql/src/test/java/org/testcontainers/jdbc/postgresql/PostgreSQLJDBCDriverTest.java @@ -0,0 +1,21 @@ +package org.testcontainers.jdbc.postgresql; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.testcontainers.jdbc.AbstractJDBCDriverTest; + +import java.util.EnumSet; + +import static java.util.Arrays.asList; + +@RunWith(Parameterized.class) +public class PostgreSQLJDBCDriverTest extends AbstractJDBCDriverTest { + + @Parameterized.Parameters(name = "{index} - {0}") + public static Iterable data() { + return asList( + new Object[][]{ + {"jdbc:tc:postgresql:9.6.8://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, + }); + } +} diff --git a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/CustomizablePostgreSQLTest.java b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/CustomizablePostgreSQLTest.java new file mode 100644 index 00000000000..8446440059e --- /dev/null +++ b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/CustomizablePostgreSQLTest.java @@ -0,0 +1,35 @@ +package org.testcontainers.junit.postgresql; + +import org.junit.Test; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.db.AbstractContainerDatabaseTest; + +import java.sql.ResultSet; +import java.sql.SQLException; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; + +/** + * @author richardnorth + */ +public class CustomizablePostgreSQLTest extends AbstractContainerDatabaseTest { + private static final String DB_NAME = "foo"; + private static final String USER = "bar"; + private static final String PWD = "baz"; + + @Test + public void testSimple() throws SQLException { + try (PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:9.6.8") + .withDatabaseName(DB_NAME) + .withUsername(USER) + .withPassword(PWD)) { + + postgres.start(); + + ResultSet resultSet = performQuery(postgres, "SELECT 1"); + + int resultSetInt = resultSet.getInt(1); + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + } + } +} diff --git a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimplePostgreSQLTest.java b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java similarity index 78% rename from modules/jdbc-test/src/test/java/org/testcontainers/junit/SimplePostgreSQLTest.java rename to modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java index 7189e05fb16..9c97f51e64e 100644 --- a/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimplePostgreSQLTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java @@ -1,7 +1,8 @@ -package org.testcontainers.junit; +package org.testcontainers.junit.postgresql; import org.junit.Test; import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.db.AbstractContainerDatabaseTest; import java.sql.ResultSet; import java.sql.SQLException; @@ -20,7 +21,7 @@ public class SimplePostgreSQLTest extends AbstractContainerDatabaseTest { @Test public void testSimple() throws SQLException { - try (PostgreSQLContainer postgres = new PostgreSQLContainer<>()) { + try (PostgreSQLContainer postgres = new PostgreSQLContainer<>()) { postgres.start(); ResultSet resultSet = performQuery(postgres, "SELECT 1"); @@ -31,7 +32,7 @@ public void testSimple() throws SQLException { @Test public void testCommandOverride() throws SQLException { - try (PostgreSQLContainer postgres = new PostgreSQLContainer<>().withCommand("postgres -c max_connections=42")) { + try (PostgreSQLContainer postgres = new PostgreSQLContainer<>().withCommand("postgres -c max_connections=42")) { postgres.start(); ResultSet resultSet = performQuery(postgres, "SELECT current_setting('max_connections')"); @@ -42,7 +43,7 @@ public void testCommandOverride() throws SQLException { @Test public void testUnsetCommand() throws SQLException { - try (PostgreSQLContainer postgres = new PostgreSQLContainer<>().withCommand("postgres -c max_connections=42").withCommand()) { + try (PostgreSQLContainer postgres = new PostgreSQLContainer<>().withCommand("postgres -c max_connections=42").withCommand()) { postgres.start(); ResultSet resultSet = performQuery(postgres, "SELECT current_setting('max_connections')"); @@ -53,7 +54,7 @@ public void testUnsetCommand() throws SQLException { @Test public void testExplicitInitScript() throws SQLException { - try (PostgreSQLContainer postgres = new PostgreSQLContainer<>().withInitScript("somepath/init_postgresql.sql")) { + try (PostgreSQLContainer postgres = new PostgreSQLContainer<>().withInitScript("somepath/init_postgresql.sql")) { postgres.start(); ResultSet resultSet = performQuery(postgres, "SELECT foo FROM bar"); diff --git a/modules/postgresql/src/test/resources/somepath/init_postgresql.sql b/modules/postgresql/src/test/resources/somepath/init_postgresql.sql new file mode 100644 index 00000000000..2b00ee968b0 --- /dev/null +++ b/modules/postgresql/src/test/resources/somepath/init_postgresql.sql @@ -0,0 +1,5 @@ +CREATE TABLE bar ( + foo VARCHAR(255) +); + +INSERT INTO bar (foo) VALUES ('hello world'); \ No newline at end of file diff --git a/modules/presto/build.gradle b/modules/presto/build.gradle index 07aad11168e..51c5bf4345e 100644 --- a/modules/presto/build.gradle +++ b/modules/presto/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: JDBC :: Presto" dependencies { compile project(':jdbc') - testCompile 'io.prestosql:presto-jdbc:329' - testCompile 'commons-dbutils:commons-dbutils:1.7' + testCompile project(':jdbc-test') + testCompile 'io.prestosql:presto-jdbc:331' } diff --git a/modules/presto/src/test/java/org/testcontainers/jdbc/presto/PrestoJDBCDriverTest.java b/modules/presto/src/test/java/org/testcontainers/jdbc/presto/PrestoJDBCDriverTest.java new file mode 100644 index 00000000000..88939c62939 --- /dev/null +++ b/modules/presto/src/test/java/org/testcontainers/jdbc/presto/PrestoJDBCDriverTest.java @@ -0,0 +1,21 @@ +package org.testcontainers.jdbc.presto; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.testcontainers.jdbc.AbstractJDBCDriverTest; + +import java.util.EnumSet; + +import static java.util.Arrays.asList; + +@RunWith(Parameterized.class) +public class PrestoJDBCDriverTest extends AbstractJDBCDriverTest { + + @Parameterized.Parameters(name = "{index} - {0}") + public static Iterable data() { + return asList( + new Object[][]{ + {"jdbc:tc:presto:329://hostname/", EnumSet.of(Options.PmdKnownBroken)}, + }); + } +} diff --git a/test-support/build.gradle b/test-support/build.gradle index 7cc1bb6d1d4..4c6fdd2bed1 100644 --- a/test-support/build.gradle +++ b/test-support/build.gradle @@ -1,4 +1,4 @@ dependencies { implementation 'junit:junit:4.12' - implementation 'org.slf4j:slf4j-api:1.7.26' + implementation 'org.slf4j:slf4j-api:1.7.30' } From fbda093796fec3eb6aed369b3699e426faf19ad7 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 14 May 2020 11:49:11 +0100 Subject: [PATCH 1370/5615] Bump r2dbc-mariadb from 0.8.1-alpha1 to 0.8.2-alpha2 in /modules/mariadb (#2702) --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 71f2417f2ad..38862c7f03c 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -7,7 +7,7 @@ dependencies { compile project(':jdbc') compileOnly project(':r2dbc') - compileOnly 'org.mariadb:r2dbc-mariadb:0.8.1-alpha1' + compileOnly 'org.mariadb:r2dbc-mariadb:0.8.2-alpha2' testCompile project(':jdbc-test') testCompile 'org.mariadb.jdbc:mariadb-java-client:2.6.0' From d1bf6b836d0e3e78845b9650076a8a3388a540f3 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 14 May 2020 11:54:40 +0100 Subject: [PATCH 1371/5615] Bump aws-java-sdk-logs from 1.11.777 to 1.11.782 in /modules/localstack (#2730) --- modules/localstack/build.gradle | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 44b732d4c60..3f88f41c176 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,10 +3,9 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.778' - testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.775' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.782' + testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.782' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.782' - testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.777' - testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.775' + testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.782' testCompile 'software.amazon.awssdk:s3:2.13.16' } From 1ed8d81d5eaf8293b1fe6659060e790cbe88e6ce Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 14 May 2020 11:56:40 +0100 Subject: [PATCH 1372/5615] Bump mssql-jdbc from 6.1.0.jre8 to 8.3.0.jre8-preview in /modules/mssqlserver (#2733) --- modules/mssqlserver/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index c21bc43c869..5d72209b27e 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -17,5 +17,5 @@ dependencies { // MSSQL's wait strategy requires the JDBC driver testCompile testFixtures(project(':r2dbc')) - testCompile 'com.microsoft.sqlserver:mssql-jdbc:6.1.0.jre8' + testCompile 'com.microsoft.sqlserver:mssql-jdbc:8.3.0.jre8-preview' } From f84bcd6136a33a336ecf6139b2f85b8bf19512d5 Mon Sep 17 00:00:00 2001 From: Simon Schneider <10846939+raynigon@users.noreply.github.com> Date: Thu, 14 May 2020 14:25:00 +0200 Subject: [PATCH 1373/5615] Add Apache Solr Module (#2123) --- docs/modules/solr.md | 34 +++++ examples/settings.gradle | 3 +- examples/solr-container/build.gradle | 18 +++ .../main/java/com/example/SearchEngine.java | 6 + .../main/java/com/example/SearchResult.java | 20 +++ .../java/com/example/SolrSearchEngine.java | 39 +++++ .../test/java/com/example/SolrQueryTest.java | 88 ++++++++++++ mkdocs.yml | 1 + modules/solr/build.gradle | 7 + .../containers/SolrClientUtils.java | 133 +++++++++++++++++ .../containers/SolrClientUtilsException.java | 7 + .../containers/SolrContainer.java | 135 ++++++++++++++++++ .../SolrContainerConfiguration.java | 18 +++ .../containers/SolrContainerTest.java | 76 ++++++++++ 14 files changed, 584 insertions(+), 1 deletion(-) create mode 100644 docs/modules/solr.md create mode 100644 examples/solr-container/build.gradle create mode 100644 examples/solr-container/src/main/java/com/example/SearchEngine.java create mode 100644 examples/solr-container/src/main/java/com/example/SearchResult.java create mode 100644 examples/solr-container/src/main/java/com/example/SolrSearchEngine.java create mode 100644 examples/solr-container/src/test/java/com/example/SolrQueryTest.java create mode 100644 modules/solr/build.gradle create mode 100644 modules/solr/src/main/java/org/testcontainers/containers/SolrClientUtils.java create mode 100644 modules/solr/src/main/java/org/testcontainers/containers/SolrClientUtilsException.java create mode 100644 modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java create mode 100644 modules/solr/src/main/java/org/testcontainers/containers/SolrContainerConfiguration.java create mode 100644 modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java diff --git a/docs/modules/solr.md b/docs/modules/solr.md new file mode 100644 index 00000000000..4776ffa608b --- /dev/null +++ b/docs/modules/solr.md @@ -0,0 +1,34 @@ +# Solr Container + +!!! note + This module is INCUBATING. While it is ready for use and operational in the current version of Testcontainers, it is possible that it may receive breaking changes in the future. See [our contributing guidelines](/contributing/#incubating-modules) for more information on our incubating modules policy. + + +This module helps running [solr](https://lucene.apache.org/solr/) using Testcontainers. + +Note that it's based on the [official Docker image](https://hub.docker.com/_/solr/). + +## Usage example + +You can start a solr container instance from any Java application by using: + + +[Using a Solr container](../../modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java) inside_block:solrContainerUsage + + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:solr:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + solr + {{latest_version}} + test + +``` diff --git a/examples/settings.gradle b/examples/settings.gradle index 9efbf097344..ef47a61f807 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -10,7 +10,8 @@ include 'redis-backed-cache' include 'redis-backed-cache-testng' include 'selenium-container' include 'singleton-container' +include 'solr-container' include 'spring-boot' include 'cucumber' include 'spring-boot-kotlin-redis' -include 'spock' \ No newline at end of file +include 'spock' diff --git a/examples/solr-container/build.gradle b/examples/solr-container/build.gradle new file mode 100644 index 00000000000..62ed7d1153a --- /dev/null +++ b/examples/solr-container/build.gradle @@ -0,0 +1,18 @@ +plugins { + id 'java' +} + +repositories { + jcenter() +} + +dependencies { + compileOnly "org.projectlombok:lombok:1.18.10" + annotationProcessor "org.projectlombok:lombok:1.18.10" + + implementation 'org.apache.solr:solr-solrj:8.3.0' + + testImplementation 'org.testcontainers:testcontainers' + testImplementation 'org.testcontainers:solr' + +} diff --git a/examples/solr-container/src/main/java/com/example/SearchEngine.java b/examples/solr-container/src/main/java/com/example/SearchEngine.java new file mode 100644 index 00000000000..c05bc23aad2 --- /dev/null +++ b/examples/solr-container/src/main/java/com/example/SearchEngine.java @@ -0,0 +1,6 @@ +package com.example; + +public interface SearchEngine { + + public SearchResult search(String term); +} diff --git a/examples/solr-container/src/main/java/com/example/SearchResult.java b/examples/solr-container/src/main/java/com/example/SearchResult.java new file mode 100644 index 00000000000..06544f0ba34 --- /dev/null +++ b/examples/solr-container/src/main/java/com/example/SearchResult.java @@ -0,0 +1,20 @@ +package com.example; + +import java.util.List; +import java.util.Map; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SearchResult { + + private long totalHits; + + private List> results; +} diff --git a/examples/solr-container/src/main/java/com/example/SolrSearchEngine.java b/examples/solr-container/src/main/java/com/example/SolrSearchEngine.java new file mode 100644 index 00000000000..8a7f38e455d --- /dev/null +++ b/examples/solr-container/src/main/java/com/example/SolrSearchEngine.java @@ -0,0 +1,39 @@ +package com.example; + +import java.util.stream.Collectors; + +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; + +import org.apache.solr.client.solrj.SolrClient; +import org.apache.solr.client.solrj.SolrQuery; +import org.apache.solr.client.solrj.response.QueryResponse; +import org.apache.solr.client.solrj.util.ClientUtils; +import org.apache.solr.common.SolrDocument; + +@RequiredArgsConstructor +public class SolrSearchEngine implements SearchEngine { + + public static final String COLLECTION_NAME = "products"; + + private final SolrClient client; + + @SneakyThrows + public SearchResult search(String term) { + + SolrQuery query = new SolrQuery(); + query.setQuery("title:" + ClientUtils.escapeQueryChars(term)); + QueryResponse response = client.query(COLLECTION_NAME, query); + return createResult(response); + } + + private SearchResult createResult(QueryResponse response) { + return SearchResult.builder() + .totalHits(response.getResults().getNumFound()) + .results(response.getResults() + .stream() + .map(SolrDocument::getFieldValueMap) + .collect(Collectors.toList())) + .build(); + } +} diff --git a/examples/solr-container/src/test/java/com/example/SolrQueryTest.java b/examples/solr-container/src/test/java/com/example/SolrQueryTest.java new file mode 100644 index 00000000000..413d7937853 --- /dev/null +++ b/examples/solr-container/src/test/java/com/example/SolrQueryTest.java @@ -0,0 +1,88 @@ +package com.example; + +import static com.example.SolrSearchEngine.COLLECTION_NAME; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.apache.solr.client.solrj.SolrClient; +import org.apache.solr.client.solrj.SolrServerException; +import org.apache.solr.client.solrj.impl.Http2SolrClient; +import org.apache.solr.common.SolrInputDocument; +import org.apache.solr.common.SolrInputField; +import org.junit.BeforeClass; +import org.junit.Test; +import org.testcontainers.containers.SolrContainer; + +public class SolrQueryTest { + + public static final SolrContainer solrContainer = new SolrContainer() + .withCollection(COLLECTION_NAME); + + private static SolrClient solrClient; + + @BeforeClass + public static void setUp() throws IOException, SolrServerException { + solrContainer.start(); + solrClient = new Http2SolrClient.Builder("http://" + solrContainer.getContainerIpAddress() + ":" + solrContainer.getSolrPort() + "/solr").build(); + + // Add Sample Data + solrClient.add(COLLECTION_NAME, Collections.singletonList( + new SolrInputDocument(createMap( + "id", createInputField("id", "1"), + "title", createInputField("title", "old skool - trainers - shoes") + )) + )); + + solrClient.add(COLLECTION_NAME, Collections.singletonList( + new SolrInputDocument(createMap( + "id", createInputField("id", "2"), + "title", createInputField("title", "print t-shirt") + )) + )); + + solrClient.commit(COLLECTION_NAME); + } + + @Test + public void testQueryForShoes() { + SolrSearchEngine searchEngine = new SolrSearchEngine(solrClient); + + SearchResult result = searchEngine.search("shoes"); + assertEquals("When searching for shoes we expect one result", 1L, result.getTotalHits()); + assertEquals("The result should have the id 1", "1", result.getResults().get(0).get("id")); + } + + @Test + public void testQueryForTShirt() { + SolrSearchEngine searchEngine = new SolrSearchEngine(solrClient); + + SearchResult result = searchEngine.search("t-shirt"); + assertEquals("When searching for t-shirt we expect one result", 1L, result.getTotalHits()); + assertEquals("The result should have the id 2", "2", result.getResults().get(0).get("id")); + } + + @Test + public void testQueryForAsterisk() { + SolrSearchEngine searchEngine = new SolrSearchEngine(solrClient); + + SearchResult result = searchEngine.search("*"); + assertEquals("When searching for * we expect no results", 0L, result.getTotalHits()); + } + + private static SolrInputField createInputField(String key, String value) { + SolrInputField inputField = new SolrInputField(key); + inputField.setValue(value); + return inputField; + } + + private static Map createMap(String k0, SolrInputField v0, String k1, SolrInputField v1) { + Map result = new HashMap<>(); + result.put(k0, v0); + result.put(k1, v1); + return result; + } +} diff --git a/mkdocs.yml b/mkdocs.yml index e1b21d80811..48b63c59631 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -66,6 +66,7 @@ nav: - modules/nginx.md - modules/pulsar.md - modules/rabbitmq.md + - modules/solr.md - modules/toxiproxy.md - modules/vault.md - modules/webdriver_containers.md diff --git a/modules/solr/build.gradle b/modules/solr/build.gradle new file mode 100644 index 00000000000..af3193be52a --- /dev/null +++ b/modules/solr/build.gradle @@ -0,0 +1,7 @@ +description = "Testcontainers :: Solr" + +dependencies { + compile project(':testcontainers') + testCompile 'org.apache.solr:solr-solrj:8.3.0' + +} diff --git a/modules/solr/src/main/java/org/testcontainers/containers/SolrClientUtils.java b/modules/solr/src/main/java/org/testcontainers/containers/SolrClientUtils.java new file mode 100644 index 00000000000..4492ca0befe --- /dev/null +++ b/modules/solr/src/main/java/org/testcontainers/containers/SolrClientUtils.java @@ -0,0 +1,133 @@ +package org.testcontainers.containers; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import org.apache.commons.io.IOUtils; + +import okhttp3.HttpUrl; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; + +/** + * Utils class which can create collections and configurations. + * + * @author Simon Schneider + */ +public class SolrClientUtils { + + private static OkHttpClient httpClient = new OkHttpClient(); + + /** + * Creates a new configuration and uploads the solrconfig.xml and schema.xml + * + * @param hostname the Hostname under which solr is reachable + * @param port the Port on which solr is running + * @param configurationName the name of the configuration which should be created + * @param solrConfig the url under which the solrconfig.xml can be found + * @param solrSchema the url under which the schema.xml can be found or null if the default schema should be used + */ + public static void uploadConfiguration(String hostname, int port, String configurationName, URL solrConfig, URL solrSchema) throws URISyntaxException, IOException { + Map parameters = new HashMap<>(); + parameters.put("action", "UPLOAD"); + parameters.put("name", configurationName); + HttpUrl url = generateSolrURL(hostname, port, Arrays.asList("admin", "configs"), parameters); + + byte[] configurationZipFile = generateConfigZipFile(solrConfig, solrSchema); + executePost(url, configurationZipFile); + + } + + /** + * Creates a new collection + * + * @param hostname the Hostname under which solr is reachable + * @param port The Port on which solr is running + * @param collectionName the name of the collection which should be created + * @param configurationName the name of the configuration which should used to create the collection + * or null if the default configuration should be used + */ + public static void createCollection(String hostname, int port, String collectionName, String configurationName) throws URISyntaxException, IOException { + Map parameters = new HashMap<>(); + parameters.put("action", "CREATE"); + parameters.put("name", collectionName); + parameters.put("numShards", "1"); + parameters.put("replicationFactor", "1"); + parameters.put("wt", "json"); + if (configurationName != null) { + parameters.put("collection.configName", configurationName); + } + HttpUrl url = generateSolrURL(hostname, port, Arrays.asList("admin", "collections"), parameters); + executePost(url, null); + } + + private static void executePost(HttpUrl url, byte[] data) throws IOException { + + RequestBody requestBody = data == null ? + RequestBody.create(MediaType.parse("text/plain"), "") : + RequestBody.create(MediaType.parse("application/octet-stream"), data); + ; + + Request request = new Request.Builder() + .url(url) + .post(requestBody) + .build(); + Response response = httpClient.newCall(request).execute(); + if (!response.isSuccessful()) { + String responseBody = ""; + if (response.body() != null) { + responseBody = response.body().string(); + response.close(); + } + throw new SolrClientUtilsException(response.code(), "Unable to upload binary\n" + responseBody); + } + if (response.body() != null) { + response.close(); + } + } + + private static HttpUrl generateSolrURL(String hostname, int port, List pathSegments, Map parameters) throws URISyntaxException { + HttpUrl.Builder builder = new HttpUrl.Builder(); + builder.scheme("http"); + builder.host(hostname); + builder.port(port); + // Path + builder.addPathSegment("solr"); + if (pathSegments != null) { + pathSegments.forEach(builder::addPathSegment); + } + // Query Parameters + parameters.forEach(builder::addQueryParameter); + return builder.build(); + } + + private static byte[] generateConfigZipFile(URL solrConfiguration, URL solrSchema) throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ZipOutputStream zipOutputStream = new ZipOutputStream(bos); + // SolrConfig + zipOutputStream.putNextEntry(new ZipEntry("solrconfig.xml")); + IOUtils.copy(solrConfiguration.openStream(), zipOutputStream); + zipOutputStream.closeEntry(); + + // Solr Schema + if (solrSchema != null) { + zipOutputStream.putNextEntry(new ZipEntry("schema.xml")); + IOUtils.copy(solrSchema.openStream(), zipOutputStream); + zipOutputStream.closeEntry(); + } + + zipOutputStream.close(); + return bos.toByteArray(); + } +} diff --git a/modules/solr/src/main/java/org/testcontainers/containers/SolrClientUtilsException.java b/modules/solr/src/main/java/org/testcontainers/containers/SolrClientUtilsException.java new file mode 100644 index 00000000000..5ba0865502c --- /dev/null +++ b/modules/solr/src/main/java/org/testcontainers/containers/SolrClientUtilsException.java @@ -0,0 +1,7 @@ +package org.testcontainers.containers; + +public class SolrClientUtilsException extends RuntimeException { + public SolrClientUtilsException(int statusCode, String msg) { + super("Http Call Status: " + statusCode + "\n" + msg); + } +} diff --git a/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java b/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java new file mode 100644 index 00000000000..90fd674dfda --- /dev/null +++ b/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java @@ -0,0 +1,135 @@ +package org.testcontainers.containers; + +import static java.time.temporal.ChronoUnit.SECONDS; + +import java.net.URL; +import java.time.Duration; +import java.util.HashSet; +import java.util.Set; + +import com.github.dockerjava.api.command.InspectContainerResponse; + +import lombok.SneakyThrows; + +import org.apache.commons.lang.StringUtils; +import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; + +/** + * SolrContainer allows a solr container to be launched and controlled. + */ +public class SolrContainer extends GenericContainer { + + public static final String IMAGE = "solr"; + public static final String DEFAULT_TAG = "8.3.0"; + + public static final Integer ZOOKEEPER_PORT = 9983; + public static final Integer SOLR_PORT = 8983; + + private SolrContainerConfiguration configuration; + + public SolrContainer() { + this(IMAGE + ":" + DEFAULT_TAG); + } + + public SolrContainer(final String dockerImageName) { + super(dockerImageName); + this.waitStrategy = new LogMessageWaitStrategy() + .withRegEx(".*o\\.e\\.j\\.s\\.Server Started.*") + .withStartupTimeout(Duration.of(60, SECONDS)); + this.configuration = new SolrContainerConfiguration(); + } + + public SolrContainer withZookeeper(boolean zookeeper) { + configuration.setZookeeper(zookeeper); + return self(); + } + + public SolrContainer withCollection(String collection) { + if (StringUtils.isEmpty(collection)) { + throw new IllegalArgumentException("Collection name must not be empty"); + } + configuration.setCollectionName(collection); + return self(); + } + + public SolrContainer withConfiguration(String name, URL solrConfig) { + if (StringUtils.isEmpty(name) || solrConfig == null) { + throw new IllegalArgumentException(); + } + configuration.setConfigurationName(name); + configuration.setSolrConfiguration(solrConfig); + return self(); + } + + public SolrContainer withSchema(URL schema) { + configuration.setSolrSchema(schema); + return self(); + } + + public int getSolrPort() { + return getMappedPort(SOLR_PORT); + } + + public int getZookeeperPort() { + return getMappedPort(ZOOKEEPER_PORT); + } + + @Override + @SneakyThrows + protected void configure() { + if (configuration.getSolrSchema() != null && configuration.getSolrConfiguration() == null) { + throw new IllegalStateException("Solr needs to have a configuration is you want to use a schema"); + } + // Generate Command Builder + String command = "solr -f"; + // Add Default Ports + this.addExposedPort(SOLR_PORT); + + // Configure Zookeeper + if (configuration.isZookeeper()) { + this.addExposedPort(ZOOKEEPER_PORT); + command = "-DzkRun -h localhost"; + } + + // Apply generated Command + this.setCommand(command); + } + + @Override + public Set getLivenessCheckPortNumbers() { + return new HashSet<>(getSolrPort()); + } + + @Override + protected void waitUntilContainerStarted() { + getWaitStrategy().waitUntilReady(this); + } + + @Override + @SneakyThrows + protected void containerIsStarted(InspectContainerResponse containerInfo) { + if (!configuration.isZookeeper()) { + ExecResult result = execInContainer("solr", "create_core", "-c", configuration.getCollectionName()); + if (result.getExitCode() != 0) { + throw new IllegalStateException("Unable to create solr core:\nStdout: " + result.getStdout() + "\nStderr:" + result.getStderr()); + } + return; + } + + if (StringUtils.isNotEmpty(configuration.getConfigurationName())) { + SolrClientUtils.uploadConfiguration( + getContainerIpAddress(), + getSolrPort(), + configuration.getConfigurationName(), + configuration.getSolrConfiguration(), + configuration.getSolrSchema()); + } + + SolrClientUtils.createCollection( + getContainerIpAddress(), + getSolrPort(), + configuration.getCollectionName(), + configuration.getConfigurationName()); + } +} + diff --git a/modules/solr/src/main/java/org/testcontainers/containers/SolrContainerConfiguration.java b/modules/solr/src/main/java/org/testcontainers/containers/SolrContainerConfiguration.java new file mode 100644 index 00000000000..1f35ce2a810 --- /dev/null +++ b/modules/solr/src/main/java/org/testcontainers/containers/SolrContainerConfiguration.java @@ -0,0 +1,18 @@ +package org.testcontainers.containers; + +import java.net.URL; + +import lombok.Data; + +/** + * @author Simon Schneider + */ +@Data +public class SolrContainerConfiguration { + + private boolean zookeeper = true; + private String collectionName = "dummy"; + private String configurationName; + private URL solrConfiguration; + private URL solrSchema; +} diff --git a/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java b/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java new file mode 100644 index 00000000000..48cb04b7053 --- /dev/null +++ b/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java @@ -0,0 +1,76 @@ +package org.testcontainers.containers; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +import java.io.IOException; + +import org.apache.solr.client.solrj.SolrClient; +import org.apache.solr.client.solrj.SolrServerException; +import org.apache.solr.client.solrj.impl.Http2SolrClient; +import org.apache.solr.client.solrj.response.SolrPingResponse; +import org.junit.After; +import org.junit.Test; + +/** + * @author Simon Schneider + */ +public class SolrContainerTest { + + private SolrClient client = null; + + @After + public void stopRestClient() throws IOException { + if (client != null) { + client.close(); + client = null; + } + } + + @Test + public void solrCloudTest() throws IOException, SolrServerException { + try (SolrContainer container = new SolrContainer()) { + container.start(); + SolrPingResponse response = getClient(container).ping("dummy"); + assertThat(response.getStatus(), is(0)); + assertThat(response.jsonStr(), containsString("zkConnected\":true")); + } + } + + @Test + public void solrStandaloneTest() throws IOException, SolrServerException { + try (SolrContainer container = new SolrContainer().withZookeeper(false)) { + container.start(); + SolrPingResponse response = getClient(container).ping("dummy"); + assertThat(response.getStatus(), is(0)); + assertThat(response.jsonStr(), containsString("zkConnected\":null")); + } + } + + @Test + public void solrCloudPingTest() throws IOException, SolrServerException { + // solrContainerUsage { + // Create the solr container. + SolrContainer container = new SolrContainer(); + + // Start the container. This step might take some time... + container.start(); + + // Do whatever you want with the client ... + SolrClient client = new Http2SolrClient.Builder("http://" + container.getContainerIpAddress() + ":" + container.getSolrPort() + "/solr").build(); + SolrPingResponse response = client.ping("dummy"); + + // Stop the container. + container.stop(); + // } + } + + private SolrClient getClient(SolrContainer container) { + if (client == null) { + client = new Http2SolrClient.Builder("http://" + container.getContainerIpAddress() + ":" + container.getSolrPort() + "/solr").build(); + } + return client; + } + +} From a2a59bcf9592f1a6d8a1efbaea443ac5e2714f62 Mon Sep 17 00:00:00 2001 From: Martin Vysny Date: Thu, 14 May 2020 18:34:41 +0300 Subject: [PATCH 1374/5615] Implement DockerClientFactory.isDockerAvailable() (#2605) --- .../org/testcontainers/DockerClientFactory.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 8d9da8825d2..4e16a7edf9d 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -101,6 +101,19 @@ public synchronized static DockerClientFactory instance() { return instance; } + /** + * Checks whether Docker is accessible and {@link #client()} is able to produce a client. + * @return true if Docker is available, false if not. + */ + public synchronized boolean isDockerAvailable() { + try { + client(); + return true; + } catch (IllegalStateException ex) { + return false; + } + } + @Synchronized private DockerClientProviderStrategy getOrInitializeStrategy() { if (strategy != null) { From ede19c8a4efd48ed20f32105d81fb71249e386af Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 15 May 2020 15:10:36 +0200 Subject: [PATCH 1375/5615] Update example to Spring Boot 2.3.0 (#2743) --- examples/spring-boot/build.gradle | 6 +++--- .../src/test/java/com/example/AbstractIntegrationTest.java | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index c7b0c63d7ba..ad8d8252fb6 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.2.7.RELEASE' + id 'org.springframework.boot' version '2.3.0.RELEASE' } apply plugin: 'io.spring.dependency-management' @@ -9,8 +9,8 @@ repositories { } dependencies { - compileOnly "org.projectlombok:lombok:1.18.12" - annotationProcessor "org.projectlombok:lombok:1.18.10" + compileOnly "org.projectlombok:lombok" + annotationProcessor "org.projectlombok:lombok" implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-data-redis' implementation 'org.springframework.boot:spring-boot-starter-web' diff --git a/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java b/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java index 29a1f51ffcc..79828923d7d 100644 --- a/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java +++ b/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java @@ -12,7 +12,6 @@ @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = DemoApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "spring.datasource.url=jdbc:tc:postgresql:11-alpine:///databasename", - "spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver" }) @ActiveProfiles("test") public abstract class AbstractIntegrationTest { From 672cbeebcac6310caab90383bce11a0b753b1106 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 15 May 2020 15:11:57 +0200 Subject: [PATCH 1376/5615] Add `ContainerState#getHost` replacing `getContainerIpAddress` (#2742) * Add `ContainerState#getHost` replacing `getContainerIpAddress` * post-merge fixes * Ignore `METHOD_NEW_DEFAULT` in japicmp * Update docs/features/networking.md Co-authored-by: Richard North * Add a tip about `getContainerIpAddress()` * Fix the codeincludes Co-authored-by: Richard North --- core/build.gradle | 12 +++--------- .../ComposeServiceWaitStrategyTarget.java | 4 ++-- .../containers/ContainerState.java | 12 ++++++++++++ .../containers/DockerComposeContainer.java | 2 +- .../containers/GenericContainer.java | 2 +- .../containers/PortForwardingContainer.java | 4 ++-- .../internal/ExternalPortListeningCheck.java | 2 +- .../wait/strategy/HostPortWaitStrategy.java | 2 +- .../wait/strategy/HttpWaitStrategy.java | 2 +- .../internal/ExternalPortListeningCheckTest.java | 2 +- .../images/ImagePullPolicyTest.java | 2 +- .../junit/DockerfileContainerTest.java | 2 +- .../junit/FixedHostPortContainerTest.java | 2 +- .../junit/GenericContainerRuleTest.java | 6 +++--- .../utility/AuthenticatedImagePullTest.java | 2 +- .../java/generic/MultiplePortsExposedTest.java | 8 ++++---- .../java/quickstart/RedisBackedCacheIntTest.java | 2 +- .../java/quickstart/RedisBackedCacheIntTest.java | 2 +- .../quickstart/RedisBackedCacheIntTest.groovy | 2 +- docs/features/networking.md | 16 ++++++++++------ docs/modules/vault.md | 2 +- docs/quickstart/junit_4_quickstart.md | 4 ++-- docs/quickstart/junit_5_quickstart.md | 4 ++-- .../containers/CassandraContainer.java | 2 +- .../containers/CassandraContainerTest.java | 2 +- .../containers/ClickHouseContainer.java | 2 +- .../containers/CockroachContainer.java | 2 +- .../couchbase/CouchbaseContainer.java | 10 +++++----- .../couchbase/CouchbaseContainerTest.java | 2 +- .../testcontainers/containers/Db2Container.java | 10 +--------- .../dynamodb/DynaliteContainer.java | 2 +- .../elasticsearch/ElasticsearchContainer.java | 4 ++-- .../containers/InfluxDBContainer.java | 2 +- .../jupiter/inheritance/RedisContainer.java | 2 +- .../containers/KafkaContainer.java | 2 +- .../localstack/LocalStackContainer.java | 4 ++-- .../containers/MariaDBContainer.java | 2 +- .../MariaDBR2DBCDatabaseContainer.java | 2 +- .../containers/MockServerContainer.java | 2 +- .../containers/MockServerContainerTest.java | 4 ++-- .../containers/MSSQLR2DBCDatabaseContainer.java | 2 +- .../containers/MSSQLServerContainer.java | 2 +- .../containers/MySQLContainer.java | 2 +- .../containers/MySQLR2DBCDatabaseContainer.java | 2 +- .../containers/Neo4jContainer.java | 6 +++--- .../containers/NginxContainer.java | 2 +- .../containers/OracleContainer.java | 2 +- .../containers/OrientDBContainer.java | 2 +- .../containers/OrientDBContainerTest.java | 2 +- .../containers/PostgreSQLContainer.java | 2 +- .../PostgreSQLR2DBCDatabaseContainer.java | 2 +- .../containers/PostgreSQLConnectionURLTest.java | 2 +- .../containers/PrestoContainer.java | 2 +- .../containers/PulsarContainer.java | 4 ++-- .../containers/RabbitMQContainer.java | 8 ++++---- .../containers/RabbitMQContainerTest.java | 8 ++++---- .../containers/BrowserWebDriverContainer.java | 4 ++-- .../spock/TestcontainersSharedContainerIT.groovy | 2 +- .../containers/ToxiproxyContainer.java | 4 ++-- .../testcontainers/containers/ToxiproxyTest.java | 2 +- .../testcontainers/vault/VaultClientTest.java | 3 +-- .../testcontainers/vault/VaultContainerTest.java | 2 +- 62 files changed, 111 insertions(+), 110 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index 4c62a2416ef..39495e59dce 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -84,20 +84,14 @@ task japicmp(type: me.champeau.gradle.japicmp.JapicmpTask) { failOnModification = true failOnSourceIncompatibility = true - // TODO cleanup excludes after we release the version based on docker-java 3.2.0 - packageExcludes = [ "com.github.dockerjava.*", "org.testcontainers.shaded.*", ] - classExcludes = [ - // 'METHOD_REMOVED_IN_SUPERCLASS' - "org.testcontainers.containers.output.FrameConsumerResultCallback", - "org.testcontainers.dockerclient.LogToStringContainerCallback", - - // 'METHOD_RETURN_TYPE_CHANGED', exposed `com.github.dockerjava.core.command.PullImageResultCallback` to the public API - "org.testcontainers.images.TimeLimitedLoggedPullImageResultCallback", + methodExcludes = [ + // METHOD_NEW_DEFAULT + "org.testcontainers.containers.ContainerState#getHost()" ] onlyBinaryIncompatibleModified = true diff --git a/core/src/main/java/org/testcontainers/containers/ComposeServiceWaitStrategyTarget.java b/core/src/main/java/org/testcontainers/containers/ComposeServiceWaitStrategyTarget.java index d4ad51dc1da..2fb953b4aee 100644 --- a/core/src/main/java/org/testcontainers/containers/ComposeServiceWaitStrategyTarget.java +++ b/core/src/main/java/org/testcontainers/containers/ComposeServiceWaitStrategyTarget.java @@ -54,8 +54,8 @@ public Integer getMappedPort(int originalPort) { * {@inheritDoc} */ @Override - public String getContainerIpAddress() { - return proxyContainer.getContainerIpAddress(); + public String getHost() { + return proxyContainer.getHost(); } /** diff --git a/core/src/main/java/org/testcontainers/containers/ContainerState.java b/core/src/main/java/org/testcontainers/containers/ContainerState.java index ad006aa3b43..1565b4e7e3e 100644 --- a/core/src/main/java/org/testcontainers/containers/ContainerState.java +++ b/core/src/main/java/org/testcontainers/containers/ContainerState.java @@ -43,8 +43,18 @@ public interface ContainerState { * Get the IP address that this container may be reached on (may not be the local machine). * * @return an IP address + * @see #getHost() */ default String getContainerIpAddress() { + return getHost(); + } + + /** + * Get the host that this container may be reached on (may not be the local machine). + * + * @return a host + */ + default String getHost() { return DockerClientFactory.instance().dockerHostIpAddress(); } @@ -107,6 +117,7 @@ default InspectContainerResponse getCurrentContainerInfo() { /** * Get the actual mapped port for a first port exposed by the container. + * Should be used in conjunction with {@link #getHost()}. * * @return the port that the exposed port is mapped to * @throws IllegalStateException if there are no exposed ports @@ -121,6 +132,7 @@ default Integer getFirstMappedPort() { /** * Get the actual mapped port for a given port exposed by the container. + * Should be used in conjunction with {@link #getHost()}. * * @param originalPort the original TCP port that is exposed * @return the port that the exposed port is mapped to, or null if it is not exposed diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 7bf8d3c916b..651bd9c8526 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -402,7 +402,7 @@ public SELF waitingFor(String serviceName, @NonNull WaitStrategy waitStrategy) { * @return a host IP address or hostname that can be used for accessing the service container. */ public String getServiceHost(String serviceName, Integer servicePort) { - return ambassadorContainer.getContainerIpAddress(); + return ambassadorContainer.getHost(); } /** diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 23e5f9bf173..89938cdfe56 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -1244,7 +1244,7 @@ public SELF withCopyFileToContainer(MountableFile mountableFile, String containe */ @Deprecated public String getIpAddress() { - return getContainerIpAddress(); + return getHost(); } /** diff --git a/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java b/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java index 852a986b58f..09f1d98bd2d 100644 --- a/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java +++ b/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java @@ -40,7 +40,7 @@ private Connection createSSHSession() { ); container.start(); - Connection connection = new Connection(container.getContainerIpAddress(), container.getMappedPort(22)); + Connection connection = new Connection(container.getHost(), container.getMappedPort(22)); connection.setTCPNoDelay(true); connection.connect( @@ -60,7 +60,7 @@ private Connection createSSHSession() { public void exposeHostPort(int port) { exposeHostPort(port, port); } - + @SneakyThrows public void exposeHostPort(int hostPort, int containerPort) { if (exposedPorts.add(new AbstractMap.SimpleEntry<>(hostPort, containerPort))) { diff --git a/core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java b/core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java index 2aae12bab8d..b9b63eeee5c 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java +++ b/core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java @@ -18,7 +18,7 @@ public class ExternalPortListeningCheck implements Callable { @Override public Boolean call() { - String address = containerState.getContainerIpAddress(); + String address = containerState.getHost(); externalLivenessCheckPorts.parallelStream().forEach(externalPort -> { try { diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/HostPortWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/HostPortWaitStrategy.java index 91f4e91fadf..a0ba1e9950c 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/HostPortWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/HostPortWaitStrategy.java @@ -46,7 +46,7 @@ protected void waitUntilReady() { } catch (TimeoutException e) { throw new ContainerLaunchException("Timed out waiting for container port to open (" + - waitStrategyTarget.getContainerIpAddress() + + waitStrategyTarget.getHost() + " ports: " + externalLivenessCheckPorts + " should be listening)"); diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java index a44fc46e4b0..5fd7834b12d 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java @@ -225,7 +225,7 @@ protected void waitUntilReady() { */ private URI buildLivenessUri(int livenessCheckPort) { final String scheme = (tlsEnabled ? "https" : "http") + "://"; - final String host = waitStrategyTarget.getContainerIpAddress(); + final String host = waitStrategyTarget.getHost(); final String portSuffix; if ((tlsEnabled && 443 == livenessCheckPort) || (!tlsEnabled && 80 == livenessCheckPort)) { diff --git a/core/src/test/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheckTest.java b/core/src/test/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheckTest.java index 69bf9d06ce7..19f3a772d41 100644 --- a/core/src/test/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheckTest.java +++ b/core/src/test/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheckTest.java @@ -29,7 +29,7 @@ public void setUp() throws Exception { nonListeningSocket.close(); mockContainer = mock(WaitStrategyTarget.class); - when(mockContainer.getContainerIpAddress()).thenReturn("127.0.0.1"); + when(mockContainer.getHost()).thenReturn("127.0.0.1"); } @Test diff --git a/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java b/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java index 59b7486c75e..69e68f7f225 100644 --- a/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java +++ b/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java @@ -33,7 +33,7 @@ public class ImagePullPolicyTest { @BeforeClass public static void beforeClass() throws Exception { - String testRegistryAddress = registry.getContainerIpAddress() + ":" + registry.getFirstMappedPort(); + String testRegistryAddress = registry.getHost() + ":" + registry.getFirstMappedPort(); String testImageName = testRegistryAddress + "/image-pull-policy-test"; String tag = UUID.randomUUID().toString(); imageName = testImageName + ":" + tag; diff --git a/core/src/test/java/org/testcontainers/junit/DockerfileContainerTest.java b/core/src/test/java/org/testcontainers/junit/DockerfileContainerTest.java index 8f601b2e737..6ad7c3ea26e 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerfileContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerfileContainerTest.java @@ -31,7 +31,7 @@ public class DockerfileContainerTest { @Test public void simpleDslTest() throws IOException { - String address = String.format("http://%s:%s", dslContainer.getContainerIpAddress(), dslContainer.getMappedPort(80)); + String address = String.format("http://%s:%s", dslContainer.getHost(), dslContainer.getMappedPort(80)); CloseableHttpClient httpClient = HttpClientBuilder.create().build(); HttpGet get = new HttpGet(address); diff --git a/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java b/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java index dba48da4db1..5fe49756ba1 100644 --- a/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java @@ -85,7 +85,7 @@ private String readResponse(GenericContainer container, Integer port) throws IOE final BufferedReader reader = Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, () -> { Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); - final Socket socket = new Socket(container.getContainerIpAddress(), port); + final Socket socket = new Socket(container.getHost(), port); return new BufferedReader(new InputStreamReader(socket.getInputStream())); } ) diff --git a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java index 6c51713b6b1..1f4a3ff121d 100644 --- a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java +++ b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java @@ -162,7 +162,7 @@ public void withTmpFsTest() throws Exception { @Test public void simpleRabbitMqTest() throws IOException, TimeoutException { ConnectionFactory factory = new ConnectionFactory(); - factory.setHost(rabbitMq.getContainerIpAddress()); + factory.setHost(rabbitMq.getHost()); factory.setPort(rabbitMq.getMappedPort(RABBITMQ_PORT)); Connection connection = factory.newConnection(); @@ -194,7 +194,7 @@ public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProp @Test public void simpleMongoDbTest() { - MongoClient mongoClient = new MongoClient(mongo.getContainerIpAddress(), mongo.getMappedPort(MONGO_PORT)); + MongoClient mongoClient = new MongoClient(mongo.getHost(), mongo.getMappedPort(MONGO_PORT)); MongoDatabase database = mongoClient.getDatabase("test"); MongoCollection collection = database.getCollection("testCollection"); @@ -366,7 +366,7 @@ private BufferedReader getReaderForContainerPort80(GenericContainer container) { return Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, () -> { Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); - Socket socket = new Socket(container.getContainerIpAddress(), container.getFirstMappedPort()); + Socket socket = new Socket(container.getHost(), container.getFirstMappedPort()); return new BufferedReader(new InputStreamReader(socket.getInputStream())); }); } diff --git a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java index 5f4417f218b..31f098bab16 100644 --- a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java +++ b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java @@ -65,7 +65,7 @@ public static void setUp() throws InterruptedException { originalAuthLocatorSingleton = RegistryAuthLocator.instance(); client = DockerClientFactory.instance().client(); - String testRegistryAddress = authenticatedRegistry.getContainerIpAddress() + ":" + authenticatedRegistry.getFirstMappedPort(); + String testRegistryAddress = authenticatedRegistry.getHost() + ":" + authenticatedRegistry.getFirstMappedPort(); testImageName = testRegistryAddress + "/alpine"; testImageNameWithTag = testImageName + ":latest"; diff --git a/docs/examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java b/docs/examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java index f22bc55defd..f5a92423b6c 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java @@ -31,13 +31,13 @@ public void fetchFirstMappedPort() { } @Test - public void getContainerIpAddressOnly() { - String ipAddress = container.getContainerIpAddress(); + public void getHostOnly() { + String ipAddress = container.getHost(); } @Test - public void getContainerIpAddressAndMappedPort() { + public void getHostAndMappedPort() { String address = - container.getContainerIpAddress() + ":" + container.getMappedPort(2424); + container.getHost() + ":" + container.getMappedPort(2424); } } diff --git a/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java b/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java index ac3ae7746b4..f2ef1ab0220 100644 --- a/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java +++ b/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java @@ -21,7 +21,7 @@ public class RedisBackedCacheIntTest { @Before public void setUp() { - String address = redis.getContainerIpAddress(); + String address = redis.getHost(); Integer port = redis.getFirstMappedPort(); // Now we have an address and port for Redis, no matter where it is running diff --git a/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java b/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java index 7c3968a724e..0f4f98fb669 100644 --- a/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java +++ b/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java @@ -24,7 +24,7 @@ public class RedisBackedCacheIntTest { @BeforeEach public void setUp() { - String address = redis.getContainerIpAddress(); + String address = redis.getHost(); Integer port = redis.getFirstMappedPort(); // Now we have an address and port for Redis, no matter where it is running diff --git a/docs/examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy b/docs/examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy index c2537a948d5..068c8079a7d 100644 --- a/docs/examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy +++ b/docs/examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy @@ -15,7 +15,7 @@ class RedisBackedCacheIntTest extends Specification { // } void setup() { - String address = redis.containerIpAddress + String address = redis.host Integer port = redis.firstMappedPort // Now we have an address and port for Redis, no matter where it is running diff --git a/docs/features/networking.md b/docs/features/networking.md index 5de0c9dfa96..2fe039b74ef 100644 --- a/docs/features/networking.md +++ b/docs/features/networking.md @@ -33,23 +33,27 @@ There is also a `getFirstMappedPort` method for convenience, for the fairly comm [Retrieving the first mapped port](../examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java) inside_block:fetchFirstMappedPort -## Getting the container IP address +## Getting the container host When running with a local Docker daemon, exposed ports will usually be reachable on `localhost`. However, in some CI environments they may instead be reachable on a different host. -As such, Testcontainers provides a convenience method to obtain an IP address on which the container should be reachable from the host machine. +As such, Testcontainers provides a convenience method to obtain an address on which the container should be reachable from the host machine. -[Getting the container IP address](../examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java) inside_block:getContainerIpAddressOnly +[Getting the container host](../examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java) inside_block:getHostOnly -It is normally advisable to use `getContainerIpAddress` and `getMappedPort` together when constructing addresses - for example: +It is normally advisable to use `getHost` and `getMappedPort` together when constructing addresses - for example: -[Getting the container IP address and mapped port](../examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java) inside_block:getContainerIpAddressAndMappedPort +[Getting the container host and mapped port](../examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java) inside_block:getHostAndMappedPort +!!! tip + `getHost()` is a replacement for `getContainerIpAddress()` and returns the same result. + `getContainerIpAddress()` is believed to be confusingly named, and will eventually be deprecated. + ## Exposing host ports to the container In some cases it is necessary to make a network connection from a container to a socket that is listening on the host machine. @@ -86,4 +90,4 @@ Docker provides the ability for you to create custom networks and place containe [Creating custom networks](../../core/src/test/java/org/testcontainers/containers/NetworkTest.java) inside_block:useCustomNetwork - \ No newline at end of file + diff --git a/docs/modules/vault.md b/docs/modules/vault.md index 43d8ff57468..1f58c0ab836 100644 --- a/docs/modules/vault.md +++ b/docs/modules/vault.md @@ -21,7 +21,7 @@ public class SomeTest { //There are many integration clients for Vault so let's just define a general one here: VaultClient client = new VaultClient( - vaultContainer.getContainerIpAddress(), + vaultContainer.getHost(), vaultContainer.getMappedPort(8200), "my-root-token"); diff --git a/docs/quickstart/junit_4_quickstart.md b/docs/quickstart/junit_4_quickstart.md index 86cd720942b..a3fecb1a14d 100644 --- a/docs/quickstart/junit_4_quickstart.md +++ b/docs/quickstart/junit_4_quickstart.md @@ -65,10 +65,10 @@ We can do this in our test `setUp` method, to set up our component under test: !!! tip - Notice that we also ask Testcontainers for the container's actual address with `redis.getContainerIpAddress();`, + Notice that we also ask Testcontainers for the container's actual address with `redis.getHost();`, rather than hard-coding `localhost`. `localhost` may work in some environments but not others - for example it may not work on your current or future CI environment. As such, **avoid hard-coding** the address, and use - `getContainerIpAddress()` instead. + `getHost()` instead. ## 4. Run the tests! diff --git a/docs/quickstart/junit_5_quickstart.md b/docs/quickstart/junit_5_quickstart.md index 179a87a5448..efd52b6c523 100644 --- a/docs/quickstart/junit_5_quickstart.md +++ b/docs/quickstart/junit_5_quickstart.md @@ -94,10 +94,10 @@ We can do this in our test `setUp` method, to set up our component under test: !!! tip - Notice that we also ask Testcontainers for the container's actual address with `redis.getContainerIpAddress();`, + Notice that we also ask Testcontainers for the container's actual address with `redis.getHost();`, rather than hard-coding `localhost`. `localhost` may work in some environments but not others - for example it may not work on your current or future CI environment. As such, **avoid hard-coding** the address, and use - `getContainerIpAddress()` instead. + `getHost()` instead. ## 4. Run the tests! diff --git a/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java b/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java index a78d23e4b4b..27c2f182e9e 100644 --- a/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java +++ b/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java @@ -161,7 +161,7 @@ public Cluster getCluster() { public static Cluster getCluster(ContainerState containerState, boolean enableJmxReporting) { final Cluster.Builder builder = Cluster.builder() - .addContactPoint(containerState.getContainerIpAddress()) + .addContactPoint(containerState.getHost()) .withPort(containerState.getMappedPort(CQL_PORT)); if (!enableJmxReporting) { builder.withoutJMXReporting(); diff --git a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java index 6105aa275c9..5bc68aa9972 100644 --- a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java +++ b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java @@ -116,7 +116,7 @@ private void testInitScript(CassandraContainer cassandraContainer) { private ResultSet performQuery(CassandraContainer cassandraContainer, String cql) { Cluster explicitCluster = Cluster.builder() - .addContactPoint(cassandraContainer.getContainerIpAddress()) + .addContactPoint(cassandraContainer.getHost()) .withPort(cassandraContainer.getMappedPort(CassandraContainer.CQL_PORT)) .build(); return performQuery(explicitCluster, cql); diff --git a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java index 9364ecc4d14..4d4ea2d11a9 100644 --- a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java +++ b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java @@ -48,7 +48,7 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return JDBC_URL_PREFIX + getContainerIpAddress() + ":" + getMappedPort(HTTP_PORT) + "/" + databaseName; + return JDBC_URL_PREFIX + getHost() + ":" + getMappedPort(HTTP_PORT) + "/" + databaseName; } @Override diff --git a/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java b/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java index fb13ced60d7..3768ca7a008 100644 --- a/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java +++ b/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java @@ -43,7 +43,7 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return JDBC_URL_PREFIX + "://" + getContainerIpAddress() + ":" + getMappedPort(DB_PORT) + "/" + databaseName; + return JDBC_URL_PREFIX + "://" + getHost() + ":" + getMappedPort(DB_PORT) + "/" + databaseName; } @Override diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index 14e5744bb6d..33f87359a92 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -141,7 +141,7 @@ public int getBootstrapHttpDirectPort() { } public String getConnectionString() { - return String.format("couchbase://%s:%d", getContainerIpAddress(), getBootstrapCarrierDirectPort()); + return String.format("couchbase://%s:%d", getHost(), getBootstrapCarrierDirectPort()); } @Override @@ -201,7 +201,7 @@ protected void containerIsStarting(final InspectContainerResponse containerInfo) @Override protected void containerIsStarted(InspectContainerResponse containerInfo) { createBuckets(); - logger().info("Couchbase container is ready! UI available at http://{}:{}", getContainerIpAddress(), getMappedPort(MGMT_PORT)); + logger().info("Couchbase container is ready! UI available at http://{}:{}", getHost(), getMappedPort(MGMT_PORT)); } /** @@ -222,7 +222,7 @@ private void waitUntilNodeIsOnline() { * up automatically, we bind the internal hostname to the internal IP address. */ private void renameNode() { - logger().debug("Renaming Couchbase Node from localhost to {}", getContainerIpAddress()); + logger().debug("Renaming Couchbase Node from localhost to {}", getHost()); Response response = doHttpRequest(MGMT_PORT, "/node/controller/rename", "POST", new FormBody.Builder() .add("hostname", getInternalIpAddress()) @@ -284,7 +284,7 @@ private void configureExternalPorts() { logger().debug("Mapping external ports to the alternate address configuration"); final FormBody.Builder builder = new FormBody.Builder(); - builder.add("hostname", getContainerIpAddress()); + builder.add("hostname", getHost()); builder.add("mgmt", Integer.toString(getMappedPort(MGMT_PORT))); builder.add("mgmtSSL", Integer.toString(getMappedPort(MGMT_SSL_PORT))); @@ -418,7 +418,7 @@ private Response doHttpRequest(final int port, final String path, final String m final boolean auth) { try { Request.Builder requestBuilder = new Request.Builder() - .url("http://" + getContainerIpAddress() + ":" + getMappedPort(port) + path); + .url("http://" + getHost() + ":" + getMappedPort(port) + path); if (auth) { requestBuilder = requestBuilder.header("Authorization", Credentials.basic(username, password)); diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java index 27ad0f6265a..c5c9f18a58b 100644 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java @@ -53,7 +53,7 @@ public void testBasicContainerUsage() { Cluster cluster = CouchbaseCluster.create( environment, - container.getContainerIpAddress() + container.getHost() ); // } diff --git a/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java b/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java index 13ef72c328e..b1472b21c6c 100644 --- a/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java +++ b/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java @@ -1,20 +1,12 @@ package org.testcontainers.containers; -import com.github.dockerjava.api.command.InspectContainerResponse; -import org.testcontainers.DockerClientFactory; -import org.testcontainers.containers.output.OutputFrame; -import org.testcontainers.containers.output.WaitingConsumer; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.utility.LicenseAcceptance; -import org.testcontainers.utility.LogUtils; -import java.io.IOException; import java.time.Duration; import java.time.temporal.ChronoUnit; import java.util.HashSet; import java.util.Set; -import java.util.concurrent.TimeoutException; -import java.util.function.Predicate; public class Db2Container extends JdbcDatabaseContainer { @@ -81,7 +73,7 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return "jdbc:db2://" + getContainerIpAddress() + ":" + getMappedPort(DB2_PORT) + "/" + databaseName; + return "jdbc:db2://" + getHost() + ":" + getMappedPort(DB2_PORT) + "/" + databaseName; } @Override diff --git a/modules/dynalite/src/main/java/org/testcontainers/dynamodb/DynaliteContainer.java b/modules/dynalite/src/main/java/org/testcontainers/dynamodb/DynaliteContainer.java index 66397452921..6280ddb9421 100644 --- a/modules/dynalite/src/main/java/org/testcontainers/dynamodb/DynaliteContainer.java +++ b/modules/dynalite/src/main/java/org/testcontainers/dynamodb/DynaliteContainer.java @@ -46,7 +46,7 @@ public AmazonDynamoDB getClient() { */ public AwsClientBuilder.EndpointConfiguration getEndpointConfiguration() { return new AwsClientBuilder.EndpointConfiguration("http://" + - this.getContainerIpAddress() + ":" + + this.getHost() + ":" + this.getMappedPort(MAPPED_PORT), null); } diff --git a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java index 3eb12969bed..e9a0b95d7ed 100644 --- a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java +++ b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java @@ -57,10 +57,10 @@ public ElasticsearchContainer(String dockerImageName) { } public String getHttpHostAddress() { - return getContainerIpAddress() + ":" + getMappedPort(ELASTICSEARCH_DEFAULT_PORT); + return getHost() + ":" + getMappedPort(ELASTICSEARCH_DEFAULT_PORT); } public InetSocketAddress getTcpHost() { - return new InetSocketAddress(getContainerIpAddress(), getMappedPort(ELASTICSEARCH_DEFAULT_TCP_PORT)); + return new InetSocketAddress(getHost(), getMappedPort(ELASTICSEARCH_DEFAULT_TCP_PORT)); } } diff --git a/modules/influxdb/src/main/java/org/testcontainers/containers/InfluxDBContainer.java b/modules/influxdb/src/main/java/org/testcontainers/containers/InfluxDBContainer.java index c3ac909f2d4..68efc516504 100644 --- a/modules/influxdb/src/main/java/org/testcontainers/containers/InfluxDBContainer.java +++ b/modules/influxdb/src/main/java/org/testcontainers/containers/InfluxDBContainer.java @@ -131,7 +131,7 @@ public SELF withPassword(final String password) { * @return a url to influxDb */ public String getUrl() { - return "http://" + getContainerIpAddress() + ":" + getLivenessCheckPort(); + return "http://" + getHost() + ":" + getLivenessCheckPort(); } /** diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/RedisContainer.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/RedisContainer.java index bae3885527d..177a48bb9d0 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/RedisContainer.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/RedisContainer.java @@ -15,6 +15,6 @@ public RedisContainer(String dockerImageName) { } public Jedis getJedis() { - return new Jedis(getContainerIpAddress(), getMappedPort(6379)); + return new Jedis(getHost(), getMappedPort(6379)); } } diff --git a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java index 5aab0f52ffa..bb92f85b03c 100644 --- a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java +++ b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java @@ -88,7 +88,7 @@ public String getBootstrapServers() { if (port == PORT_NOT_ASSIGNED) { throw new IllegalStateException("You should start Kafka container first"); } - return String.format("PLAINTEXT://%s:%s", getContainerIpAddress(), port); + return String.format("PLAINTEXT://%s:%s", getHost(), port); } @Override diff --git a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java index c649d322ead..96cedb37726 100644 --- a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java +++ b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java @@ -63,7 +63,7 @@ protected void configure() { withEnv(HOSTNAME_EXTERNAL_ENV_VAR, getNetworkAliases().get(getNetworkAliases().size() - 1)); // use the last network alias set hostnameExternalReason = "to match last network alias on container with non-default network"; } else { - withEnv(HOSTNAME_EXTERNAL_ENV_VAR, getContainerIpAddress()); + withEnv(HOSTNAME_EXTERNAL_ENV_VAR, getHost()); hostnameExternalReason = "to match host-routable address for container"; } logger().info("{} environment variable set to {} ({})", HOSTNAME_EXTERNAL_ENV_VAR, getEnvMap().get(HOSTNAME_EXTERNAL_ENV_VAR), hostnameExternalReason); @@ -134,7 +134,7 @@ public AwsClientBuilder.EndpointConfiguration getEndpointConfiguration(Service s */ public URI getEndpointOverride(Service service) { try { - final String address = getContainerIpAddress(); + final String address = getHost(); String ipAddress = address; // resolve IP address and use that as the endpoint so that path-style access is automatically used for S3 ipAddress = InetAddress.getByName(address).getHostAddress(); diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java index fb21dd9a1a9..1b4cb3344e4 100644 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java @@ -60,7 +60,7 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return "jdbc:mariadb://" + getContainerIpAddress() + ":" + getMappedPort(MARIADB_PORT) + "/" + databaseName; + return "jdbc:mariadb://" + getHost() + ":" + getMappedPort(MARIADB_PORT) + "/" + databaseName; } @Override diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainer.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainer.java index 4ddbed997d4..e37e9b0b6bf 100644 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainer.java +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainer.java @@ -23,7 +23,7 @@ public static ConnectionFactoryOptions getOptions(MariaDBContainer container) @Override public ConnectionFactoryOptions configure(ConnectionFactoryOptions options) { return options.mutate() - .option(ConnectionFactoryOptions.HOST, container.getContainerIpAddress()) + .option(ConnectionFactoryOptions.HOST, container.getHost()) .option(ConnectionFactoryOptions.PORT, container.getMappedPort(MariaDBContainer.MARIADB_PORT)) .option(ConnectionFactoryOptions.DATABASE, container.getDatabaseName()) .option(ConnectionFactoryOptions.USER, container.getUsername()) diff --git a/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java b/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java index f1f9dc42d19..e35370f4b27 100644 --- a/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java +++ b/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java @@ -20,7 +20,7 @@ public MockServerContainer(String version) { } public String getEndpoint() { - return String.format("http://%s:%d", getContainerIpAddress(), getMappedPort(PORT)); + return String.format("http://%s:%d", getHost(), getMappedPort(PORT)); } public Integer getServerPort() { diff --git a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java index be1495a04c7..91f740b4bd8 100644 --- a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java +++ b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java @@ -33,7 +33,7 @@ private static String responseFromMockserver(MockServerContainer mockServer, Str @Test public void shouldReturnExpectation() throws Exception { // testSimpleExpectation { - new MockServerClient(mockServer.getContainerIpAddress(), mockServer.getServerPort()) + new MockServerClient(mockServer.getHost(), mockServer.getServerPort()) .when(request() .withPath("/person") .withQueryStringParameter("name", "peter")) @@ -57,7 +57,7 @@ public void shouldCallActualMockserverVersion() throws Exception { String expectedBody = "Hello World!"; - new MockServerClient(mockServer.getContainerIpAddress(), mockServer.getServerPort()) + new MockServerClient(mockServer.getHost(), mockServer.getServerPort()) .when(request().withPath("/hello")) .respond(response().withBody(expectedBody)); diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainer.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainer.java index 27c486c218c..59c3ed0de68 100644 --- a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainer.java +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainer.java @@ -23,7 +23,7 @@ public static ConnectionFactoryOptions getOptions(MSSQLServerContainer contai @Override public ConnectionFactoryOptions configure(ConnectionFactoryOptions options) { return options.mutate() - .option(ConnectionFactoryOptions.HOST, container.getContainerIpAddress()) + .option(ConnectionFactoryOptions.HOST, container.getHost()) .option(ConnectionFactoryOptions.PORT, container.getMappedPort(MSSQLServerContainer.MS_SQL_SERVER_PORT)) // TODO enable if/when MSSQLServerContainer adds support for customizing the DB name // .option(ConnectionFactoryOptions.DATABASE, container.getDatabasseName()) diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java index 2219a5aaf61..631228aeef2 100644 --- a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java @@ -62,7 +62,7 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return "jdbc:sqlserver://" + getContainerIpAddress() + ":" + getMappedPort(MS_SQL_SERVER_PORT); + return "jdbc:sqlserver://" + getHost() + ":" + getMappedPort(MS_SQL_SERVER_PORT); } @Override diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java index b8bbe225617..79d29234b99 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java @@ -70,7 +70,7 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return "jdbc:mysql://" + getContainerIpAddress() + ":" + getMappedPort(MYSQL_PORT) + "/" + databaseName; + return "jdbc:mysql://" + getHost() + ":" + getMappedPort(MYSQL_PORT) + "/" + databaseName; } @Override diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainer.java index c43ffcca1b2..274224352a8 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainer.java @@ -23,7 +23,7 @@ public static ConnectionFactoryOptions getOptions(MySQLContainer container) { @Override public ConnectionFactoryOptions configure(ConnectionFactoryOptions options) { return options.mutate() - .option(ConnectionFactoryOptions.HOST, container.getContainerIpAddress()) + .option(ConnectionFactoryOptions.HOST, container.getHost()) .option(ConnectionFactoryOptions.PORT, container.getMappedPort(MySQLContainer.MYSQL_PORT)) .option(ConnectionFactoryOptions.DATABASE, container.getDatabaseName()) .option(ConnectionFactoryOptions.USER, container.getUsername()) diff --git a/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java index 0c16927dba8..10545da43d6 100644 --- a/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java +++ b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java @@ -111,21 +111,21 @@ protected void configure() { * @return Bolt URL for use with Neo4j's Java-Driver. */ public String getBoltUrl() { - return String.format("bolt://" + getContainerIpAddress() + ":" + getMappedPort(DEFAULT_BOLT_PORT)); + return String.format("bolt://" + getHost() + ":" + getMappedPort(DEFAULT_BOLT_PORT)); } /** * @return URL of the transactional HTTP endpoint. */ public String getHttpUrl() { - return String.format("http://" + getContainerIpAddress() + ":" + getMappedPort(DEFAULT_HTTP_PORT)); + return String.format("http://" + getHost() + ":" + getMappedPort(DEFAULT_HTTP_PORT)); } /** * @return URL of the transactional HTTPS endpoint. */ public String getHttpsUrl() { - return String.format("https://" + getContainerIpAddress() + ":" + getMappedPort(DEFAULT_HTTPS_PORT)); + return String.format("https://" + getHost() + ":" + getMappedPort(DEFAULT_HTTPS_PORT)); } /** diff --git a/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java b/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java index bedf040869f..fb931bce409 100644 --- a/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java +++ b/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java @@ -33,7 +33,7 @@ protected Set getLivenessCheckPorts() { } public URL getBaseUrl(String scheme, int port) throws MalformedURLException { - return new URL(scheme + "://" + getContainerIpAddress() + ":" + getMappedPort(port)); + return new URL(scheme + "://" + getHost() + ":" + getMappedPort(port)); } public void setCustomContent(String htmlContentPath) { diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java index e86768bc383..60603a7e222 100644 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java @@ -64,7 +64,7 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return "jdbc:oracle:thin:" + getUsername() + "/" + getPassword() + "@" + getContainerIpAddress() + ":" + getOraclePort() + ":" + getSid(); + return "jdbc:oracle:thin:" + getUsername() + "/" + getPassword() + "@" + getHost() + ":" + getOraclePort() + ":" + getSid(); } @Override diff --git a/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java b/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java index ca0180640ce..b7fcc85573a 100644 --- a/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java +++ b/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java @@ -108,7 +108,7 @@ public OrientDB getOrientDB() { } public String getServerUrl() { - return "remote:" + getContainerIpAddress() + ":" + getMappedPort(2424); + return "remote:" + getHost() + ":" + getMappedPort(2424); } public String getDbUrl() { diff --git a/modules/orientdb/src/test/java/org/testcontainers/containers/OrientDBContainerTest.java b/modules/orientdb/src/test/java/org/testcontainers/containers/OrientDBContainerTest.java index 0a067b375b6..54b0661cae2 100644 --- a/modules/orientdb/src/test/java/org/testcontainers/containers/OrientDBContainerTest.java +++ b/modules/orientdb/src/test/java/org/testcontainers/containers/OrientDBContainerTest.java @@ -63,7 +63,7 @@ public void shouldInitializeDatabaseFromScript() { container.start(); assertThat(container.getDbUrl()) - .isEqualTo("remote:" + container.getContainerIpAddress() + ":" + container.getMappedPort(2424) + "/persons"); + .isEqualTo("remote:" + container.getHost() + ":" + container.getMappedPort(2424) + "/persons"); final ODatabaseSession session = container.getSession(); diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index 06cdd7ebc8d..8634f9d70fa 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -68,7 +68,7 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { // Disable Postgres driver use of java.util.logging to reduce noise at startup time - return format("jdbc:postgresql://%s:%d/%s?loggerLevel=OFF", getContainerIpAddress(), getMappedPort(POSTGRESQL_PORT), databaseName); + return format("jdbc:postgresql://%s:%d/%s?loggerLevel=OFF", getHost(), getMappedPort(POSTGRESQL_PORT), databaseName); } @Override diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainer.java index 9bfb9fdee05..88b5497f5d3 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainer.java @@ -23,7 +23,7 @@ public static ConnectionFactoryOptions getOptions(PostgreSQLContainer contain @Override public ConnectionFactoryOptions configure(ConnectionFactoryOptions options) { return options.mutate() - .option(ConnectionFactoryOptions.HOST, container.getContainerIpAddress()) + .option(ConnectionFactoryOptions.HOST, container.getHost()) .option(ConnectionFactoryOptions.PORT, container.getMappedPort(PostgreSQLContainer.POSTGRESQL_PORT)) .option(ConnectionFactoryOptions.DATABASE, container.getDatabaseName()) .option(ConnectionFactoryOptions.USER, container.getUsername()) diff --git a/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java index 3b887cedbec..05f6ea204a8 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java @@ -48,7 +48,7 @@ public void shouldRejectInvalidQueryString() { static class FixedJdbcUrlPostgreSQLContainer extends PostgreSQLContainer { @Override - public String getContainerIpAddress() { + public String getHost() { return "localhost"; } diff --git a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java index 0f2a82b9b9d..56488525734 100644 --- a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java +++ b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java @@ -49,7 +49,7 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return format("jdbc:presto://%s:%s/%s", getContainerIpAddress(), getMappedPort(PRESTO_PORT), nullToEmpty(catalog)); + return format("jdbc:presto://%s:%s/%s", getHost(), getMappedPort(PRESTO_PORT), nullToEmpty(catalog)); } @Override diff --git a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java index 49f0437f95a..c9532979057 100644 --- a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java +++ b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java @@ -26,11 +26,11 @@ public PulsarContainer(String pulsarVersion) { } public String getPulsarBrokerUrl() { - return String.format("pulsar://%s:%s", getContainerIpAddress(), getMappedPort(BROKER_PORT)); + return String.format("pulsar://%s:%s", getHost(), getMappedPort(BROKER_PORT)); } public String getHttpServiceUrl() { - return String.format("http://%s:%s", getContainerIpAddress(), getMappedPort(BROKER_HTTP_PORT)); + return String.format("http://%s:%s", getHost(), getMappedPort(BROKER_HTTP_PORT)); } } diff --git a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java index aba71e25a7e..49423201d14 100644 --- a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java +++ b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java @@ -114,28 +114,28 @@ public Integer getHttpPort() { * @return AMQP URL for use with AMQP clients. */ public String getAmqpUrl() { - return "amqp://" + getContainerIpAddress() + ":" + getAmqpPort(); + return "amqp://" + getHost() + ":" + getAmqpPort(); } /** * @return AMQPS URL for use with AMQPS clients. */ public String getAmqpsUrl() { - return "amqps://" + getContainerIpAddress() + ":" + getAmqpsPort(); + return "amqps://" + getHost() + ":" + getAmqpsPort(); } /** * @return URL of the HTTP management endpoint. */ public String getHttpUrl() { - return "http://" + getContainerIpAddress() + ":" + getHttpPort(); + return "http://" + getHost() + ":" + getHttpPort(); } /** * @return URL of the HTTPS management endpoint. */ public String getHttpsUrl() { - return "https://" + getContainerIpAddress() + ":" + getHttpsPort(); + return "https://" + getHost() + ":" + getHttpsPort(); } /** diff --git a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java index 6f283c7451f..27ac38f55ce 100644 --- a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java +++ b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java @@ -49,13 +49,13 @@ public void shouldCreateRabbitMQContainer() container.start(); assertThat(container.getAmqpsUrl()).isEqualTo( - String.format("amqps://%s:%d", container.getContainerIpAddress(), container.getMappedPort(DEFAULT_AMQPS_PORT))); + String.format("amqps://%s:%d", container.getHost(), container.getMappedPort(DEFAULT_AMQPS_PORT))); assertThat(container.getAmqpUrl()).isEqualTo( - String.format("amqp://%s:%d", container.getContainerIpAddress(), container.getMappedPort(DEFAULT_AMQP_PORT))); + String.format("amqp://%s:%d", container.getHost(), container.getMappedPort(DEFAULT_AMQP_PORT))); assertThat(container.getHttpsUrl()).isEqualTo( - String.format("https://%s:%d", container.getContainerIpAddress(), container.getMappedPort(DEFAULT_HTTPS_PORT))); + String.format("https://%s:%d", container.getHost(), container.getMappedPort(DEFAULT_HTTPS_PORT))); assertThat(container.getHttpUrl()).isEqualTo( - String.format("http://%s:%d", container.getContainerIpAddress(), container.getMappedPort(DEFAULT_HTTP_PORT))); + String.format("http://%s:%d", container.getHost(), container.getMappedPort(DEFAULT_HTTP_PORT))); assertThat(container.getHttpsPort()).isEqualTo(container.getMappedPort(DEFAULT_HTTPS_PORT)); assertThat(container.getHttpPort()).isEqualTo(container.getMappedPort(DEFAULT_HTTP_PORT)); diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index 49218dfb990..397f6c4a2cf 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -207,7 +207,7 @@ public static String getImageForCapabilities(Capabilities capabilities, String s public URL getSeleniumAddress() { try { - return new URL("http", getContainerIpAddress(), getMappedPort(SELENIUM_PORT), "/wd/hub"); + return new URL("http", getHost(), getMappedPort(SELENIUM_PORT), "/wd/hub"); } catch (MalformedURLException e) { e.printStackTrace();// TODO return null; @@ -216,7 +216,7 @@ public URL getSeleniumAddress() { @Override public String getVncAddress() { - return "vnc://vnc:secret@" + getContainerIpAddress() + ":" + getMappedPort(VNC_PORT); + return "vnc://vnc:secret@" + getHost() + ":" + getMappedPort(VNC_PORT); } @Override diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/TestcontainersSharedContainerIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/TestcontainersSharedContainerIT.groovy index 014b25eac1f..5a12ce25b71 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/TestcontainersSharedContainerIT.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/TestcontainersSharedContainerIT.groovy @@ -38,7 +38,7 @@ class TestcontainersSharedContainerIT extends Specification { } private CloseableHttpResponse performHttpRequest(CloseableHttpClient client) { - String ip = genericContainer.containerIpAddress + String ip = genericContainer.host String port = genericContainer.getMappedPort(80) def response = client.execute(new HttpGet("http://$ip:$port")) response diff --git a/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java b/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java index 1b631fa9d6d..f2915b5af11 100644 --- a/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java +++ b/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java @@ -47,7 +47,7 @@ public ToxiproxyContainer(String imageName) { @Override protected void containerIsStarted(InspectContainerResponse containerInfo) { - client = new ToxiproxyClient(getContainerIpAddress(), getMappedPort(TOXIPROXY_CONTROL_PORT)); + client = new ToxiproxyClient(getHost(), getMappedPort(TOXIPROXY_CONTROL_PORT)); } /** @@ -88,7 +88,7 @@ public ContainerProxy getProxy(String hostname, int port) { final Proxy proxy = client.createProxy(upstream, "0.0.0.0:" + toxiPort, upstream); final int mappedPort = getMappedPort(toxiPort); - return new ContainerProxy(proxy, getContainerIpAddress(), mappedPort, toxiPort); + return new ContainerProxy(proxy, getHost(), mappedPort, toxiPort); } catch (IOException e) { throw new RuntimeException("Proxy could not be created", e); } diff --git a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java index 366f425b5b5..d76851f57b5 100644 --- a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java +++ b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java @@ -40,7 +40,7 @@ public class ToxiproxyTest { @Test public void testDirect() { - final Jedis jedis = createJedis(redis.getContainerIpAddress(), redis.getFirstMappedPort()); + final Jedis jedis = createJedis(redis.getHost(), redis.getFirstMappedPort()); jedis.set("somekey", "somevalue"); final String s = jedis.get("somekey"); diff --git a/modules/vault/src/test/java/org/testcontainers/vault/VaultClientTest.java b/modules/vault/src/test/java/org/testcontainers/vault/VaultClientTest.java index 8091980d234..dfe73cae4b9 100644 --- a/modules/vault/src/test/java/org/testcontainers/vault/VaultClientTest.java +++ b/modules/vault/src/test/java/org/testcontainers/vault/VaultClientTest.java @@ -5,7 +5,6 @@ import com.bettercloud.vault.VaultException; import com.bettercloud.vault.response.LogicalResponse; import org.junit.Test; -import org.testcontainers.containers.wait.strategy.Wait; import java.util.HashMap; import java.util.Map; @@ -26,7 +25,7 @@ public void writeAndReadMultipleValues() throws VaultException { vaultContainer.start(); final VaultConfig config = new VaultConfig() - .address("http://" + vaultContainer.getContainerIpAddress() + ":" + vaultContainer.getFirstMappedPort()) + .address("http://" + vaultContainer.getHost() + ":" + vaultContainer.getFirstMappedPort()) .token(VAULT_TOKEN) .build(); diff --git a/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java b/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java index fae9209c8e7..d7ac5ca8686 100644 --- a/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java +++ b/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java @@ -71,7 +71,7 @@ public void readSecondSecretPathOverHttpApi() throws InterruptedException { } private String getHostAndPort() { - return vaultContainer.getContainerIpAddress() + ":" + vaultContainer.getMappedPort(8200); + return vaultContainer.getHost() + ":" + vaultContainer.getMappedPort(8200); } From 1eef935529564c0f2df1384bd5d55999f849bf5c Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 15 May 2020 15:14:28 +0200 Subject: [PATCH 1377/5615] Store `GenericContainer#exposedPorts` as an order-preserving `Set` (#2613) * Store `GenericContainer#exposedPorts` as an order-preserving `Set` * Update core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java Co-authored-by: Richard North Co-authored-by: Richard North --- .../containers/GenericContainer.java | 18 +++++++++++++++--- .../junit/GenericContainerRuleTest.java | 9 +++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 89938cdfe56..d906c320bd5 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -77,6 +77,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -118,7 +119,7 @@ public class GenericContainer> * Default settings */ @NonNull - private List exposedPorts = new ArrayList<>(); + private LinkedHashSet exposedPorts = new LinkedHashSet<>(); @NonNull private List portBindings = new ArrayList<>(); @@ -256,6 +257,16 @@ public void setImage(Future image) { this.image = new RemoteDockerImage(image); } + @Override + public List getExposedPorts() { + return new ArrayList<>(exposedPorts); + } + + @Override + public void setExposedPorts(List exposedPorts) { + this.exposedPorts = new LinkedHashSet<>(exposedPorts); + } + /** * @see #dependsOn(Iterable) */ @@ -671,8 +682,9 @@ protected void containerIsStopped(InspectContainerResponse containerInfo) { @Deprecated protected Integer getLivenessCheckPort() { // legacy implementation for backwards compatibility - if (exposedPorts.size() > 0) { - return getMappedPort(exposedPorts.get(0)); + Iterator exposedPortsIterator = exposedPorts.iterator(); + if (exposedPortsIterator.hasNext()) { + return getMappedPort(exposedPortsIterator.next()); } else if (portBindings.size() > 0) { return Integer.valueOf(PortBinding.parse(portBindings.get(0)).getBinding().getHostPortSpec()); } else { diff --git a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java index 1f4a3ff121d..b450244a84d 100644 --- a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java +++ b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java @@ -379,6 +379,15 @@ public void addExposedPortAfterWithExposedPortsTest() { assertTrue("addExposedPort should be exposed", redis.getExposedPorts().contains(8987)); } + @Test + public void addingExposedPortTwiceShouldNotFail() { + redis.addExposedPort(8987); + redis.addExposedPort(8987); + assertThat("Both ports should be exposed", redis.getExposedPorts().size(), equalTo(2)); // 2 ports = de-duplicated port 8897 and original port 6379 + assertTrue("withExposedPort should be exposed", redis.getExposedPorts().contains(REDIS_PORT)); + assertTrue("addExposedPort should be exposed", redis.getExposedPorts().contains(8987)); + } + @Test public void sharedMemorySetTest() { try (GenericContainer containerWithSharedMemory = new GenericContainer() From 8c7c769cdfd3848457e0a70de8518f08ad7053b4 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 16 May 2020 20:43:00 +0100 Subject: [PATCH 1378/5615] Dynamic CI jobs for examples (#2677) --- .../{ci-dind.yml => ci-docker-wormhole.yml} | 2 +- .github/workflows/ci-examples.yml | 73 +++++++++++++++++++ .github/workflows/ci.yml | 27 ------- examples/build.gradle | 5 ++ examples/settings.gradle | 36 +++++++++ gradle/ci-support.gradle | 6 +- settings.gradle | 38 +++++----- 7 files changed, 137 insertions(+), 50 deletions(-) rename .github/workflows/{ci-dind.yml => ci-docker-wormhole.yml} (96%) create mode 100644 .github/workflows/ci-examples.yml diff --git a/.github/workflows/ci-dind.yml b/.github/workflows/ci-docker-wormhole.yml similarity index 96% rename from .github/workflows/ci-dind.yml rename to .github/workflows/ci-docker-wormhole.yml index 15bd5ec76ee..3c6da54dc1f 100644 --- a/.github/workflows/ci-dind.yml +++ b/.github/workflows/ci-docker-wormhole.yml @@ -1,4 +1,4 @@ -name: in-container +name: CI-Docker-Wormhole on: pull_request: {} diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml new file mode 100644 index 00000000000..bec794dc167 --- /dev/null +++ b/.github/workflows/ci-examples.yml @@ -0,0 +1,73 @@ +name: CI-Examples + +on: + pull_request: {} + push: { branches: [ master ] } + +env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + +jobs: + find_gradle_jobs: + runs-on: ubuntu-18.04 + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-java@v1 + with: + java-version: '1.8' + - name: Cache Gradle Home files + uses: actions/cache@v1 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-home-testmatrix-examples-${{ hashFiles('**/*.gradle') }} + restore-keys: | + ${{ runner.os }}-gradle-home-testmatrix-examples- + ${{ runner.os }}-gradle-home- + - id: set-matrix + working-directory: ./examples/ + env: + # Since we override the tests executor, + # we should not push empty results to the cache + READ_ONLY_REMOTE_GRADLE_CACHE: true + run: | + TASKS=$(./gradlew --no-daemon --parallel -q testMatrix) + echo $TASKS + echo "::set-output name=matrix::{\"gradle_args\":$TASKS}" + check: + needs: find_gradle_jobs + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.find_gradle_jobs.outputs.matrix) }} + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-java@v1 + with: + java-version: '1.8' + - name: Cache Gradle Home files + uses: actions/cache@v1 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-home-examples-${{matrix.gradle_args}}_check-${{ hashFiles('**/*.gradle') }} + restore-keys: | + ${{ runner.os }}-gradle-home-examples-${{matrix.gradle_args}}_check- + ${{ runner.os }}-gradle-home-examples-${{matrix.gradle_args}}_check- + ${{ runner.os }}-gradle-home-examples- + - name: Clear existing docker image cache + run: docker image prune -af + - name: Build and test Examples with Gradle (${{matrix.gradle_args}}) + working-directory: ./examples/ + run: | + ./gradlew --no-daemon --continue --scan --info ${{matrix.gradle_args}} + - name: Aggregate Examples test reports with ciMate + if: always() + env: + CIMATE_PROJECT_ID: 2348n4vl + CIMATE_CI_KEY: "Examples / ${{ runner.os }} / ${{matrix.gradle_args}}" + run: | + wget -q https://get.cimate.io/release/linux/cimate + chmod +x cimate + ./cimate "**/TEST-*.xml" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a6baaf7e92d..90e64dde133 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,30 +69,3 @@ jobs: wget -q https://get.cimate.io/release/linux/cimate chmod +x cimate ./cimate "**/TEST-*.xml" - examples: - runs-on: ubuntu-18.04 - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-java@v1 - with: - java-version: '1.8' - - name: Cache Gradle Home files - uses: actions/cache@v1 - with: - path: ~/.gradle/caches - key: ${{ runner.os }}-gradle-home-examples-${{ hashFiles('**/*.gradle*') }} - restore-keys: | - ${{ runner.os }}-gradle-home-examples- - - name: Build and test Examples with Gradle - working-directory: ./examples/ - run: | - ./gradlew --no-daemon --scan --info check - - name: Aggregate test reports with ciMate - if: always() - env: - CIMATE_PROJECT_ID: 2348n4vl - CIMATE_CI_KEY: examples - run: | - wget -q https://get.cimate.io/release/linux/cimate - chmod +x cimate - ./cimate "**/TEST-*.xml" diff --git a/examples/build.gradle b/examples/build.gradle index b2ecf1b0be3..25b7c1fcf29 100644 --- a/examples/build.gradle +++ b/examples/build.gradle @@ -1 +1,6 @@ // empty build.gradle for dependabot +apply from: "$rootDir/../gradle/ci-support.gradle" + +subprojects { + apply plugin:"java" +} diff --git a/examples/settings.gradle b/examples/settings.gradle index ef47a61f807..25da14a0931 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -1,3 +1,18 @@ +buildscript { + repositories { + maven { + url "https://plugins.gradle.org/m2/" + } + } + dependencies { + classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.10.0" + classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.2.1" + } +} + +apply plugin: 'ch.myniva.s3-build-cache' +apply plugin: 'com.gradle.enterprise' + rootProject.name = 'testcontainers-examples' includeBuild '..' @@ -15,3 +30,24 @@ include 'spring-boot' include 'cucumber' include 'spring-boot-kotlin-redis' include 'spock' + +ext.isMasterBuild = false || + (System.getenv("GITHUB_REF") == "refs/heads/master") || + (System.getenv("BUILD_SOURCEBRANCHNAME") == "master") + +buildCache { + local { + enabled = !isMasterBuild + } + + remote(ch.myniva.gradle.caching.s3.AwsS3BuildCache) { + endpoint = 'fra1.digitaloceanspaces.com' + region = 'fra1' + bucket = 'testcontainers' + path = 'cache' + push = isMasterBuild && !System.getenv("READ_ONLY_REMOTE_GRADLE_CACHE") + headers = [ + 'x-amz-acl': 'public-read' + ] + } +} diff --git a/gradle/ci-support.gradle b/gradle/ci-support.gradle index 7c3408068b3..e83c936f2f3 100644 --- a/gradle/ci-support.gradle +++ b/gradle/ci-support.gradle @@ -28,9 +28,9 @@ gradle.taskGraph.whenReady { subproject.tasks.withType(Test).all { testExecuter([execute: { spec, processor -> }, stopNow:{}] as org.gradle.api.internal.tasks.testing.TestExecuter) } - subproject.tasks.findByName("shadowJar").enabled = false - subproject.tasks.findByName("javadoc").enabled = false - subproject.tasks.findByName("delombok").enabled = false + subproject.tasks.findByName("shadowJar")?.enabled = false + subproject.tasks.findByName("javadoc")?.enabled = false + subproject.tasks.findByName("delombok")?.enabled = false subproject.tasks.findByName("japicmp")?.enabled = false } } diff --git a/settings.gradle b/settings.gradle index 150d79f89fe..2ea21599ceb 100644 --- a/settings.gradle +++ b/settings.gradle @@ -13,6 +13,25 @@ buildscript { apply plugin: 'ch.myniva.s3-build-cache' apply plugin: 'com.gradle.enterprise' +rootProject.name = 'testcontainers-java' + +include "bom" + +include "testcontainers" +project(':testcontainers').projectDir = "$rootDir/core" as File + +file('modules').eachDir { dir -> + include dir.name + project(":${dir.name}").projectDir = dir +} + +include ':docs:examples:junit4:generic' +include ':docs:examples:junit4:redis' +include ':docs:examples:junit5:redis' +include ':docs:examples:spock:redis' + +include 'test-support' + ext.isMasterBuild = false || (System.getenv("GITHUB_REF") == "refs/heads/master") || (System.getenv("BUILD_SOURCEBRANCHNAME") == "master") @@ -34,25 +53,6 @@ buildCache { } } -rootProject.name = 'testcontainers-java' - -include "bom" - -include "testcontainers" -project(':testcontainers').projectDir = "$rootDir/core" as File - -file('modules').eachDir { dir -> - include dir.name - project(":${dir.name}").projectDir = dir -} - -include ':docs:examples:junit4:generic' -include ':docs:examples:junit4:redis' -include ':docs:examples:junit5:redis' -include ':docs:examples:spock:redis' - -include 'test-support' - gradleEnterprise { buildScan { termsOfServiceUrl = 'https://gradle.com/terms-of-service' From bf233cd62b610491f9007c7aa94e087298f0109b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 16 May 2020 21:49:03 +0100 Subject: [PATCH 1379/5615] Bump testcontainers from 1.14.1 to 1.14.2 in /core (#2751) --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 39495e59dce..48bbf57d7e9 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -104,7 +104,7 @@ if (!org.gradle.internal.os.OperatingSystem.current().isWindows()) { } dependencies { - baseline 'org.testcontainers:testcontainers:1.14.1', { + baseline 'org.testcontainers:testcontainers:1.14.2', { exclude group: "*", module: "*" } From 1e0625cea385f8c1e1864a365a471ac7dcdd86a2 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 16 May 2020 21:49:27 +0100 Subject: [PATCH 1380/5615] Bump guava from 28.2-jre to 29.0-jre in /modules/jdbc-test (#2754) --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 8b36f126b86..df9c26fcd72 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -2,7 +2,7 @@ dependencies { compile project(':jdbc') compile project(':test-support') - compile 'com.google.guava:guava:28.2-jre' + compile 'com.google.guava:guava:29.0-jre' compile 'org.apache.commons:commons-lang3:3.10' compile 'com.zaxxer:HikariCP-java6:2.3.13' compile 'commons-dbutils:commons-dbutils:1.7' From e465f5d836922a1b3f5853a65a57d349574f62e3 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 16 May 2020 21:49:45 +0100 Subject: [PATCH 1381/5615] Bump mssql-jdbc from 8.2.2.jre8 to 8.3.0.jre8-preview in /modules/mssqlserver (#2755) --- modules/mssqlserver/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index 5d72209b27e..8cdfa35dee1 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'io.r2dbc:r2dbc-mssql:0.8.2.RELEASE' testCompile project(':jdbc-test') - testCompile 'com.microsoft.sqlserver:mssql-jdbc:8.2.2.jre8' + testCompile 'com.microsoft.sqlserver:mssql-jdbc:8.3.0.jre8-preview' testCompile project(':r2dbc') testCompile 'io.r2dbc:r2dbc-mssql:0.8.1.RELEASE' From 421f67bff6b5d814ec4b92c7c9b6086e00e4c886 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 16 May 2020 21:50:25 +0100 Subject: [PATCH 1382/5615] Bump tomcat-jdbc from 9.0.33 to 9.0.35 in /modules/jdbc (#2744) --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index aa21939c769..1cfdeab73b2 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -6,7 +6,7 @@ dependencies { testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.vibur:vibur-dbcp:25.0' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.33' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.35' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'com.googlecode.junit-toolbox:junit-toolbox:2.4' } From 6b6feeb39baf0328628925821bbb40a90e7dafbd Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 16 May 2020 21:51:05 +0100 Subject: [PATCH 1383/5615] Bump tomcat-jdbc from 9.0.33 to 9.0.35 in /modules/jdbc-test (#2752) --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index df9c26fcd72..eb3e4ed222d 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -15,7 +15,7 @@ dependencies { } compile 'commons-dbutils:commons-dbutils:1.7' - compile 'org.apache.tomcat:tomcat-jdbc:9.0.33' + compile 'org.apache.tomcat:tomcat-jdbc:9.0.35' compile 'org.vibur:vibur-dbcp:25.0' compile 'mysql:mysql-connector-java:8.0.19' } From 2a9b875fcad74251abad8c7b874714ad17e9082e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 16 May 2020 21:51:19 +0100 Subject: [PATCH 1384/5615] Bump mysql-connector-java from 8.0.19 to 8.0.20 in /modules/jdbc-test (#2750) --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index eb3e4ed222d..23564279332 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -17,5 +17,5 @@ dependencies { compile 'commons-dbutils:commons-dbutils:1.7' compile 'org.apache.tomcat:tomcat-jdbc:9.0.35' compile 'org.vibur:vibur-dbcp:25.0' - compile 'mysql:mysql-connector-java:8.0.19' + compile 'mysql:mysql-connector-java:8.0.20' } From 3bdd665048b3a7f13d6c2acf75f687a4f076f50a Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 17 May 2020 06:45:03 +0100 Subject: [PATCH 1385/5615] Add gradle.properties to examples to enable caching --- examples/gradle.properties | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 examples/gradle.properties diff --git a/examples/gradle.properties b/examples/gradle.properties new file mode 100644 index 00000000000..dad41a4425a --- /dev/null +++ b/examples/gradle.properties @@ -0,0 +1,2 @@ +org.gradle.parallel=false +org.gradle.caching=true From 4450294c6cd2a01233f1baa2ba9723c5c1c2ef54 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 17 May 2020 07:17:43 +0100 Subject: [PATCH 1386/5615] Bump aws-java-sdk-s3 from 1.11.782 to 1.11.784 in /modules/localstack (#2747) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 3f88f41c176..67b9d6e79f8 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.782' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.784' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.782' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.782' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.782' From a1a8c4c212587692fcf64dd9cd0b8336a7aff4ea Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 17 May 2020 07:35:37 +0100 Subject: [PATCH 1387/5615] Bump aws-java-sdk-sqs from 1.11.782 to 1.11.784 in /modules/localstack (#2746) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 67b9d6e79f8..9b4133c6d4d 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.784' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.782' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.782' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.784' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.782' testCompile 'software.amazon.awssdk:s3:2.13.16' } From 0fafaa909e5b2f434b5846dfc8719d0460cac779 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 17 May 2020 07:35:45 +0100 Subject: [PATCH 1388/5615] Bump solr-solrj from 8.3.0 to 8.5.1 in /examples (#2749) --- examples/solr-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/solr-container/build.gradle b/examples/solr-container/build.gradle index 62ed7d1153a..7e9b7a8c71f 100644 --- a/examples/solr-container/build.gradle +++ b/examples/solr-container/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly "org.projectlombok:lombok:1.18.10" annotationProcessor "org.projectlombok:lombok:1.18.10" - implementation 'org.apache.solr:solr-solrj:8.3.0' + implementation 'org.apache.solr:solr-solrj:8.5.1' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.testcontainers:solr' From f88eb3f4f28b5b74bdb725e143a9db89c72afbfb Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 17 May 2020 07:35:56 +0100 Subject: [PATCH 1389/5615] Bump org.springframework.boot from 2.2.7.RELEASE to 2.3.0.RELEASE in /examples (#2753) --- examples/selenium-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index 04888a55c65..49e015f1af3 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.2.7.RELEASE' + id 'org.springframework.boot' version '2.3.0.RELEASE' } apply plugin: 'io.spring.dependency-management' From eb30647d19ceb87c0303e40341a4dd6dd28f9c81 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 17 May 2020 07:36:08 +0100 Subject: [PATCH 1390/5615] Bump s3 from 2.13.16 to 2.13.18 in /modules/localstack (#2748) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 9b4133c6d4d..482b977792d 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,5 +7,5 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.782' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.784' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.782' - testCompile 'software.amazon.awssdk:s3:2.13.16' + testCompile 'software.amazon.awssdk:s3:2.13.18' } From 206c5f65482475e14e9fc1eba5edd7d20ae25aea Mon Sep 17 00:00:00 2001 From: "Roman S.A" Date: Thu, 21 May 2020 18:36:24 +0300 Subject: [PATCH 1391/5615] Added additional url params in JdbcDatabaseContainer (#1802) (#1874) --- .../containers/ClickHouseContainer.java | 4 + .../containers/CockroachContainer.java | 4 +- .../cockroachdb/SimpleCockroachDBTest.java | 20 +++++ .../containers/Db2Container.java | 4 +- .../junit/db2/SimpleDb2Test.java | 15 ++++ .../containers/JdbcDatabaseContainer.java | 23 +++++ .../containers/MariaDBContainer.java | 4 +- .../junit/mariadb/SimpleMariaDBTest.java | 20 +++++ .../containers/MSSQLServerContainer.java | 3 +- .../mssqlserver/SimpleMSSQLServerTest.java | 16 ++++ .../containers/MySQLContainer.java | 6 +- .../junit/mysql/SimpleMySQLTest.java | 83 ++++++++++++++++++- .../containers/OracleContainer.java | 5 ++ .../containers/PostgreSQLContainer.java | 24 ++---- .../PostgreSQLConnectionURLTest.java | 2 +- .../postgresql/SimplePostgreSQLTest.java | 15 ++++ 16 files changed, 222 insertions(+), 26 deletions(-) diff --git a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java index 4d4ea2d11a9..10c0e1835c2 100644 --- a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java +++ b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java @@ -66,4 +66,8 @@ public String getTestQueryString() { return TEST_QUERY; } + @Override + public ClickHouseContainer withUrlParam(String paramName, String paramValue) { + throw new UnsupportedOperationException("The ClickHouse does not support this"); + } } diff --git a/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java b/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java index 3768ca7a008..de64902c88a 100644 --- a/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java +++ b/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java @@ -43,7 +43,9 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return JDBC_URL_PREFIX + "://" + getHost() + ":" + getMappedPort(DB_PORT) + "/" + databaseName; + String additionalUrlParams = constructUrlParameters("?", "&"); + return JDBC_URL_PREFIX + "://" + getHost() + ":" + getMappedPort(DB_PORT) + + "/" + databaseName + additionalUrlParams; } @Override diff --git a/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java b/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java index 7c62b2f6bcf..14fe20222a3 100644 --- a/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java +++ b/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java @@ -9,6 +9,8 @@ import java.util.logging.Level; import java.util.logging.LogManager; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; public class SimpleCockroachDBTest extends AbstractContainerDatabaseTest { @@ -42,4 +44,22 @@ public void testExplicitInitScript() throws SQLException { assertEquals("Value from init script should equal real value", "hello world", firstColumnValue); } } + + @Test + public void testWithAdditionalUrlParamInJdbcUrl() { + CockroachContainer cockroach = new CockroachContainer() + .withUrlParam("sslmode", "disable") + .withUrlParam("application_name", "cockroach"); + + try { + cockroach.start(); + String jdbcUrl = cockroach.getJdbcUrl(); + assertThat(jdbcUrl, containsString("?")); + assertThat(jdbcUrl, containsString("&")); + assertThat(jdbcUrl, containsString("sslmode=disable")); + assertThat(jdbcUrl, containsString("application_name=cockroach")); + } finally { + cockroach.stop(); + } + } } diff --git a/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java b/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java index b1472b21c6c..05edcb2c3ed 100644 --- a/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java +++ b/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java @@ -73,7 +73,9 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return "jdbc:db2://" + getHost() + ":" + getMappedPort(DB2_PORT) + "/" + databaseName; + String additionalUrlParams = constructUrlParameters(":", ";", ";"); + return "jdbc:db2://" + getHost() + ":" + getMappedPort(DB2_PORT) + + "/" + databaseName + additionalUrlParams; } @Override diff --git a/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java b/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java index 08f427cf12b..6cc7535f785 100644 --- a/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java +++ b/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java @@ -7,6 +7,8 @@ import java.sql.ResultSet; import java.sql.SQLException; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; public class SimpleDb2Test extends AbstractContainerDatabaseTest { @@ -24,4 +26,17 @@ public void testSimple() throws SQLException { assertEquals("A basic SELECT query succeeds", 1, resultSetInt); } } + + @Test + public void testWithAdditionalUrlParamInJdbcUrl() { + try (Db2Container db2 = new Db2Container() + .withUrlParam("sslConnection", "false") + .acceptLicense()) { + + db2.start(); + + String jdbcUrl = db2.getJdbcUrl(); + assertThat(jdbcUrl, containsString(":sslConnection=false;")); + } + } } diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index 36a6461aa74..6c265bc6943 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -2,6 +2,7 @@ import com.github.dockerjava.api.command.InspectContainerResponse; import lombok.NonNull; +import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.traits.LinkableContainer; import org.testcontainers.delegate.DatabaseDelegate; @@ -17,6 +18,8 @@ import java.util.Properties; import java.util.concurrent.Future; +import static java.util.stream.Collectors.joining; + /** * Base class for containers that expose a JDBC connection * @@ -28,6 +31,7 @@ public abstract class JdbcDatabaseContainer parameters = new HashMap<>(); + protected Map urlParameters = new HashMap<>(); private int startupTimeoutSeconds = 120; private int connectTimeoutSeconds = 120; @@ -82,7 +86,11 @@ public SELF withPassword(String password) { public SELF withDatabaseName(String dbName) { throw new UnsupportedOperationException(); + } + public SELF withUrlParam(String paramName, String paramValue) { + urlParameters.put(paramName, paramValue); + return self(); } /** @@ -223,6 +231,21 @@ protected String constructUrlForConnection(String queryString) { return getJdbcUrl() + queryString; } + protected String constructUrlParameters(String startCharacter, String delimiter) { + return constructUrlParameters(startCharacter, delimiter, StringUtils.EMPTY); + } + + protected String constructUrlParameters(String startCharacter, String delimiter, String endCharacter) { + String urlParameters = ""; + if (!this.urlParameters.isEmpty()) { + String additionalParameters = this.urlParameters.entrySet().stream() + .map(Object::toString) + .collect(joining(delimiter)); + urlParameters = startCharacter + additionalParameters + endCharacter; + } + return urlParameters; + } + protected void optionallyMapResourceParameterAsVolume(@NotNull String paramName, @NotNull String pathNameInContainer, @NotNull String defaultResource) { String resourceName = parameters.getOrDefault(paramName, defaultResource); diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java index 1b4cb3344e4..04d745a9efe 100644 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java @@ -60,7 +60,9 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return "jdbc:mariadb://" + getHost() + ":" + getMappedPort(MARIADB_PORT) + "/" + databaseName; + String additionalUrlParams = constructUrlParameters("?", "&"); + return "jdbc:mariadb://" + getHost() + ":" + getMappedPort(MARIADB_PORT) + + "/" + databaseName + additionalUrlParams; } @Override diff --git a/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java b/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java index 646fbb40947..af503ce424b 100644 --- a/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java +++ b/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java @@ -8,6 +8,8 @@ import java.sql.ResultSet; import java.sql.SQLException; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; import static org.junit.Assume.assumeFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; @@ -68,4 +70,22 @@ public void testMariaDBWithCommandOverride() throws SQLException { assertEquals("Auto increment increment should be overriden by command line", "10", result); } } + + @Test + public void testWithAdditionalUrlParamInJdbcUrl() { + MariaDBContainer mariaDBContainer = (MariaDBContainer) new MariaDBContainer() + .withUrlParam("connectTimeout", "40000") + .withUrlParam("rewriteBatchedStatements", "true"); + + try { + mariaDBContainer.start(); + String jdbcUrl = mariaDBContainer.getJdbcUrl(); + assertThat(jdbcUrl, containsString("?")); + assertThat(jdbcUrl, containsString("&")); + assertThat(jdbcUrl, containsString("rewriteBatchedStatements=true")); + assertThat(jdbcUrl, containsString("connectTimeout=40000")); + } finally { + mariaDBContainer.stop(); + } + } } diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java index 631228aeef2..b500e6fddf0 100644 --- a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java @@ -62,7 +62,8 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return "jdbc:sqlserver://" + getHost() + ":" + getMappedPort(MS_SQL_SERVER_PORT); + String additionalUrlParams = constructUrlParameters(";", ";"); + return "jdbc:sqlserver://" + getHost() + ":" + getMappedPort(MS_SQL_SERVER_PORT) + additionalUrlParams; } @Override diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java index c1699d2473a..71e12a6d864 100644 --- a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java +++ b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java @@ -9,6 +9,8 @@ import java.sql.SQLException; import java.sql.Statement; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; public class SimpleMSSQLServerTest extends AbstractContainerDatabaseTest { @@ -24,6 +26,20 @@ public void testSimple() throws SQLException { } } + @Test + public void testWithAdditionalUrlParamInJdbcUrl() { + try (MSSQLServerContainer mssqlServer = new MSSQLServerContainer<>() + .withUrlParam("integratedSecurity", "false") + .withUrlParam("applicationName", "MyApp")) { + + mssqlServer.start(); + + String jdbcUrl = mssqlServer.getJdbcUrl(); + assertThat(jdbcUrl, containsString(";integratedSecurity=false;applicationName=MyApp")); + } + } + + @Test public void testSetupDatabase() throws SQLException { try (MSSQLServerContainer mssqlServer = new MSSQLServerContainer<>()) { diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java index 79d29234b99..1ef574cbd5e 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java @@ -5,6 +5,8 @@ import java.util.HashSet; import java.util.Set; +import static java.util.stream.Collectors.joining; + /** * @author richardnorth */ @@ -70,7 +72,9 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return "jdbc:mysql://" + getHost() + ":" + getMappedPort(MYSQL_PORT) + "/" + databaseName; + String additionalUrlParams = constructUrlParameters("?", "&"); + return "jdbc:mysql://" + getHost() + ":" + getMappedPort(MYSQL_PORT) + + "/" + databaseName + additionalUrlParams; } @Override diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java index ee6c3ab6993..6d6f8d26453 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java @@ -9,9 +9,17 @@ import org.testcontainers.containers.output.Slf4jLogConsumer; import org.testcontainers.db.AbstractContainerDatabaseTest; +import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; - +import java.sql.Statement; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.junit.Assume.assumeFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; @@ -142,4 +150,77 @@ public void testEmptyPasswordWithRootUser() throws SQLException { assertEquals("A basic SELECT query succeeds", 1, resultSetInt); } } + + @Test + public void testWithAdditionalUrlParamTimeZone() throws SQLException { + MySQLContainer mysql = (MySQLContainer) new MySQLContainer() + .withUrlParam("serverTimezone", "Europe/Zurich") + .withEnv("TZ", "Europe/Zurich") + .withLogConsumer(new Slf4jLogConsumer(logger)); + mysql.start(); + + try(Connection connection = mysql.createConnection("")) { + Statement statement = connection.createStatement(); + statement.execute("SELECT NOW();"); + try (ResultSet resultSet = statement.getResultSet()) { + resultSet.next(); + + // checking that the time_zone MySQL is Europe/Zurich + LocalDateTime localDateTime = resultSet.getObject(1, LocalDateTime.class); + ZonedDateTime actualDateTime = localDateTime.atZone(ZoneId.of("Europe/Zurich")) + .truncatedTo(ChronoUnit.MINUTES); + ZonedDateTime expectedDateTime = ZonedDateTime.now(ZoneId.of("Europe/Zurich")) + .truncatedTo(ChronoUnit.MINUTES); + + String message = String.format("MySQL time zone is not Europe/Zurich. MySQL date:%s, current date:%s", + actualDateTime, expectedDateTime); + assertTrue(message, actualDateTime.equals(expectedDateTime)); + } + } finally { + mysql.stop(); + } + } + + @Test + public void testWithAdditionalUrlParamMultiQueries() throws SQLException { + MySQLContainer mysql = (MySQLContainer) new MySQLContainer() + .withUrlParam("allowMultiQueries", "true") + .withLogConsumer(new Slf4jLogConsumer(logger)); + mysql.start(); + + try(Connection connection = mysql.createConnection("")) { + Statement statement = connection.createStatement(); + String multiQuery = "DROP TABLE IF EXISTS bar; " + + "CREATE TABLE bar (foo VARCHAR(20)); " + + "INSERT INTO bar (foo) VALUES ('hello world');"; + statement.execute(multiQuery); + statement.execute("SELECT foo FROM bar;"); + try(ResultSet resultSet = statement.getResultSet()) { + resultSet.next(); + String firstColumnValue = resultSet.getString(1); + assertEquals("Value from bar should equal real value", "hello world", firstColumnValue); + } + } finally { + mysql.stop(); + } + } + + @Test + public void testWithAdditionalUrlParamInJdbcUrl() { + MySQLContainer mysql = (MySQLContainer) new MySQLContainer() + .withUrlParam("allowMultiQueries", "true") + .withUrlParam("rewriteBatchedStatements", "true") + .withLogConsumer(new Slf4jLogConsumer(logger)); + + try { + mysql.start(); + String jdbcUrl = mysql.getJdbcUrl(); + assertThat(jdbcUrl, containsString("?")); + assertThat(jdbcUrl, containsString("&")); + assertThat(jdbcUrl, containsString("rewriteBatchedStatements=true")); + assertThat(jdbcUrl, containsString("allowMultiQueries=true")); + } finally { + mysql.stop(); + } + } } diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java index 60603a7e222..d263a141d4f 100644 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java @@ -89,6 +89,11 @@ public OracleContainer withPassword(String password) { return self(); } + @Override + public OracleContainer withUrlParam(String paramName, String paramValue) { + throw new UnsupportedOperationException("The OracleDb does not support this"); + } + @SuppressWarnings("SameReturnValue") public String getSid() { return "xe"; diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index 8634f9d70fa..6ba7390878e 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -55,6 +55,8 @@ protected Set getLivenessCheckPorts() { @Override protected void configure() { + // Disable Postgres driver use of java.util.logging to reduce noise at startup time + withUrlParam("loggerLevel", "OFF"); addEnv("POSTGRES_DB", databaseName); addEnv("POSTGRES_USER", username); addEnv("POSTGRES_PASSWORD", password); @@ -67,25 +69,9 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - // Disable Postgres driver use of java.util.logging to reduce noise at startup time - return format("jdbc:postgresql://%s:%d/%s?loggerLevel=OFF", getHost(), getMappedPort(POSTGRESQL_PORT), databaseName); - } - - @Override - protected String constructUrlForConnection(String queryString) { - String baseUrl = getJdbcUrl(); - - if ("".equals(queryString)) { - return baseUrl; - } - - if (!queryString.startsWith("?")) { - throw new IllegalArgumentException("The '?' character must be included"); - } - - return baseUrl.contains("?") - ? baseUrl + QUERY_PARAM_SEPARATOR + queryString.substring(1) - : baseUrl + queryString; + String additionalUrlParams = constructUrlParameters("?", "&"); + return "jdbc:postgresql://" + getContainerIpAddress() + ":" + getMappedPort(POSTGRESQL_PORT) + + "/" + databaseName + additionalUrlParams; } @Override diff --git a/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java index 05f6ea204a8..c0ba7e40d44 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java @@ -15,7 +15,7 @@ public void shouldCorrectlyAppendQueryString() { String connectionUrl = postgres.constructUrlForConnection("?stringtype=unspecified&stringtype=unspecified"); String queryString = connectionUrl.substring(connectionUrl.indexOf('?')); - assertTrue("Query String contains expected params", queryString.contains("&stringtype=unspecified&stringtype=unspecified")); + assertTrue("Query String contains expected params", queryString.contains("?stringtype=unspecified&stringtype=unspecified")); assertEquals("Query String starts with '?'", 0, queryString.indexOf('?')); assertFalse("Query String does not contain extra '?'", queryString.substring(1).contains("?")); } diff --git a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java index 9c97f51e64e..36ece03b604 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java @@ -9,6 +9,8 @@ import java.util.logging.Level; import java.util.logging.LogManager; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; @@ -63,4 +65,17 @@ public void testExplicitInitScript() throws SQLException { assertEquals("Value from init script should equal real value", "hello world", firstColumnValue); } } + + @Test + public void testWithAdditionalUrlParamInJdbcUrl() { + try (PostgreSQLContainer postgres = new PostgreSQLContainer<>() + .withUrlParam("charSet", "UNICODE")) { + + postgres.start(); + String jdbcUrl = postgres.getJdbcUrl(); + assertThat(jdbcUrl, containsString("?")); + assertThat(jdbcUrl, containsString("&")); + assertThat(jdbcUrl, containsString("charSet=UNICODE")); + } + } } From c0fc42ff60bdf639ce28ab6dc3d97117548f1e0a Mon Sep 17 00:00:00 2001 From: vcvitaly Date: Thu, 21 May 2020 20:02:23 +0300 Subject: [PATCH 1392/5615] Add docs for startup strategy information/examples (#1157) (#2565) * Add startup strategy information/examples to mkdocs #1157 * Update docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java Co-authored-by: Sergei Egorov Co-authored-by: Richard North --- docs/examples/junit4/generic/build.gradle | 1 + .../StartupCheckStrategyTest.java | 90 +++++++++++++++++++ docs/features/startup_and_waits.md | 49 ++++++++++ 3 files changed, 140 insertions(+) create mode 100644 docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java diff --git a/docs/examples/junit4/generic/build.gradle b/docs/examples/junit4/generic/build.gradle index 97902d5d9ca..5ce878c7ba2 100644 --- a/docs/examples/junit4/generic/build.gradle +++ b/docs/examples/junit4/generic/build.gradle @@ -5,4 +5,5 @@ dependencies { testCompile project(":testcontainers") testCompile project(":selenium") testCompile "org.seleniumhq.selenium:selenium-api:3.141.59" + testCompile 'org.assertj:assertj-core:3.15.0' } diff --git a/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java b/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java new file mode 100644 index 00000000000..5ab32919bb2 --- /dev/null +++ b/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java @@ -0,0 +1,90 @@ +package org.testcontainers.containers.startupcheck; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import lombok.SneakyThrows; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.WaitingConsumer; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; +import static org.testcontainers.containers.startupcheck.StartupCheckStrategyTest.IndefiniteOneShotStrategyTest; +import static org.testcontainers.containers.startupcheck.StartupCheckStrategyTest.MinimumDurationStrategyTest; +import static org.testcontainers.containers.startupcheck.StartupCheckStrategyTest.OneShotStrategyTest; + +@RunWith(Suite.class) +@Suite.SuiteClasses({OneShotStrategyTest.class, IndefiniteOneShotStrategyTest.class, MinimumDurationStrategyTest.class}) +public class StartupCheckStrategyTest { + + private static final String HELLO_TESTCONTAINERS = "Hello Testcontainers!"; + + private static void waitForHello(GenericContainer container) throws TimeoutException { + WaitingConsumer consumer = new WaitingConsumer(); + container.followOutput(consumer, STDOUT); + + consumer.waitUntil(frame -> + frame.getUtf8String().contains(HELLO_TESTCONTAINERS), 30, TimeUnit.SECONDS); + } + + public static class OneShotStrategyTest { + @Rule + // withOneShotStrategy { + public GenericContainer bboxWithOneShot = new GenericContainer("busybox:1.31.1") + .withCommand(String.format("echo %s", HELLO_TESTCONTAINERS)) + .withStartupCheckStrategy( + new OneShotStartupCheckStrategy().withTimeout(Duration.ofSeconds(3)) + ); + // } + + @SneakyThrows + @Test + public void testCommandIsExecuted() { + waitForHello(bboxWithOneShot); + + assertThat(bboxWithOneShot.isRunning()).isFalse(); + } + } + + public static class IndefiniteOneShotStrategyTest { + @Rule + // withIndefiniteOneShotStrategy { + public GenericContainer bboxWithIndefiniteOneShot = new GenericContainer("busybox:1.31.1") + .withCommand("sh", "-c", String.format("sleep 5 && echo \"%s\"", HELLO_TESTCONTAINERS)) + .withStartupCheckStrategy( + new IndefiniteWaitOneShotStartupCheckStrategy() + ); + // } + + @SneakyThrows + @Test + public void testCommandIsExecuted() { + waitForHello(bboxWithIndefiniteOneShot); + + assertThat(bboxWithIndefiniteOneShot.isRunning()).isFalse(); + } + } + + public static class MinimumDurationStrategyTest { + @Rule + // withMinimumDurationStrategy { + public GenericContainer bboxWithMinimumDuration = new GenericContainer("busybox:1.31.1") + .withCommand("sh", "-c", String.format("sleep 5 && echo \"%s\"", HELLO_TESTCONTAINERS)) + .withStartupCheckStrategy( + new MinimumDurationRunningStartupCheckStrategy(Duration.ofSeconds(5)) + ); + // } + + @SneakyThrows + @Test + public void testCommandIsExecuted() { + waitForHello(bboxWithMinimumDuration); + + assertThat(bboxWithMinimumDuration.isRunning()).isTrue(); + } + } +} diff --git a/docs/features/startup_and_waits.md b/docs/features/startup_and_waits.md index e1d30457e5c..3cbec97af0d 100644 --- a/docs/features/startup_and_waits.md +++ b/docs/features/startup_and_waits.md @@ -74,3 +74,52 @@ If none of these options meet your requirements, you can create your own subclas appropriate wait mechanism in `waitUntilReady()`. The `GenericContainer.waitingFor()` method accepts any valid [`WaitStrategy`](http://static.javadoc.io/org.testcontainers/testcontainers/{{ latest_version }}/org/testcontainers/containers/wait/strategy/WaitStrategy.html). + +## Startup check Strategies + +Ordinarily Testcontainers will check that the container has reached the running state and has not exited. +In order to do that inspect is executed against the container and state parameter is extracted. + +All logic is implemented in [`StartupCheckStrategy`](http://static.javadoc.io/org.testcontainers/testcontainers/{{ latest_version }}/org/testcontainers/containers/startupcheck/StartupCheckStrategy.html) child classes. + +### Running startup strategy example + +This is the strategy used by default. Testcontainers just checks if container is running. + +Implemented in [`IsRunningStartupCheckStrategy`](http://static.javadoc.io/org.testcontainers/testcontainers/{{ latest_version }}/org/testcontainers/containers/startupcheck/IsRunningStartupCheckStrategy.html) class. + +### One shot startup strategy example + +This strategy is intended for use with containers that only run briefly and exit of their own accord. As such, success is deemed to be when +the container has stopped with exit code 0. + + +[Using one shot startup strategy](../examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java) inside_block:withOneShotStrategy + + +### Indefinite one shot startup strategy example + +Variant of one shot strategy that does not impose a timeout. Intended for situation such as when a long running task forms part of + container startup. + +It has to be assumed that the container will stop of its own accord, either with a success or failure exit code. + + +[Using indefinite one shot startup strategy](../examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java) inside_block:withIndefiniteOneShotStrategy + + +### Minimum duration startup strategy example + +Checks that the container is running and has been running for a defined minimum period of time. + + +[Using minimum duration strategy](../examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java) inside_block:withMinimumDurationStrategy + + +### Other startup strategies + +If none of these options meet your requirements, you can create your own subclass of +[`StartupCheckStrategy`](http://static.javadoc.io/org.testcontainers/testcontainers/{{ latest_version }}/org/testcontainers/containers +/startupcheck/StartupCheckStrategy.html) with an appropriate startup check mechanism in `waitUntilStartupSuccessful()`. +Or you can leave it as is and just implement the `checkStartupState(DockerClient dockerClient, String containerId)` if you still want to check state + periodically. From 0385030aa4307c7041a0150edbbd2159f9060e16 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 21 May 2020 19:25:02 +0100 Subject: [PATCH 1393/5615] Revert "Added additional url params in JdbcDatabaseContainer (#1802)" (#2775) --- .../containers/ClickHouseContainer.java | 4 - .../containers/CockroachContainer.java | 4 +- .../cockroachdb/SimpleCockroachDBTest.java | 20 ----- .../containers/Db2Container.java | 4 +- .../junit/db2/SimpleDb2Test.java | 15 ---- .../containers/JdbcDatabaseContainer.java | 23 ----- .../containers/MariaDBContainer.java | 4 +- .../junit/mariadb/SimpleMariaDBTest.java | 20 ----- .../containers/MSSQLServerContainer.java | 3 +- .../mssqlserver/SimpleMSSQLServerTest.java | 16 ---- .../containers/MySQLContainer.java | 6 +- .../junit/mysql/SimpleMySQLTest.java | 83 +------------------ .../containers/OracleContainer.java | 5 -- .../containers/PostgreSQLContainer.java | 24 ++++-- .../PostgreSQLConnectionURLTest.java | 2 +- .../postgresql/SimplePostgreSQLTest.java | 15 ---- 16 files changed, 26 insertions(+), 222 deletions(-) diff --git a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java index 10c0e1835c2..4d4ea2d11a9 100644 --- a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java +++ b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java @@ -66,8 +66,4 @@ public String getTestQueryString() { return TEST_QUERY; } - @Override - public ClickHouseContainer withUrlParam(String paramName, String paramValue) { - throw new UnsupportedOperationException("The ClickHouse does not support this"); - } } diff --git a/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java b/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java index de64902c88a..3768ca7a008 100644 --- a/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java +++ b/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java @@ -43,9 +43,7 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - String additionalUrlParams = constructUrlParameters("?", "&"); - return JDBC_URL_PREFIX + "://" + getHost() + ":" + getMappedPort(DB_PORT) + - "/" + databaseName + additionalUrlParams; + return JDBC_URL_PREFIX + "://" + getHost() + ":" + getMappedPort(DB_PORT) + "/" + databaseName; } @Override diff --git a/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java b/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java index 14fe20222a3..7c62b2f6bcf 100644 --- a/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java +++ b/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java @@ -9,8 +9,6 @@ import java.util.logging.Level; import java.util.logging.LogManager; -import static org.hamcrest.CoreMatchers.containsString; -import static org.junit.Assert.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; public class SimpleCockroachDBTest extends AbstractContainerDatabaseTest { @@ -44,22 +42,4 @@ public void testExplicitInitScript() throws SQLException { assertEquals("Value from init script should equal real value", "hello world", firstColumnValue); } } - - @Test - public void testWithAdditionalUrlParamInJdbcUrl() { - CockroachContainer cockroach = new CockroachContainer() - .withUrlParam("sslmode", "disable") - .withUrlParam("application_name", "cockroach"); - - try { - cockroach.start(); - String jdbcUrl = cockroach.getJdbcUrl(); - assertThat(jdbcUrl, containsString("?")); - assertThat(jdbcUrl, containsString("&")); - assertThat(jdbcUrl, containsString("sslmode=disable")); - assertThat(jdbcUrl, containsString("application_name=cockroach")); - } finally { - cockroach.stop(); - } - } } diff --git a/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java b/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java index 05edcb2c3ed..b1472b21c6c 100644 --- a/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java +++ b/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java @@ -73,9 +73,7 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - String additionalUrlParams = constructUrlParameters(":", ";", ";"); - return "jdbc:db2://" + getHost() + ":" + getMappedPort(DB2_PORT) + - "/" + databaseName + additionalUrlParams; + return "jdbc:db2://" + getHost() + ":" + getMappedPort(DB2_PORT) + "/" + databaseName; } @Override diff --git a/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java b/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java index 6cc7535f785..08f427cf12b 100644 --- a/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java +++ b/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java @@ -7,8 +7,6 @@ import java.sql.ResultSet; import java.sql.SQLException; -import static org.hamcrest.CoreMatchers.containsString; -import static org.junit.Assert.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; public class SimpleDb2Test extends AbstractContainerDatabaseTest { @@ -26,17 +24,4 @@ public void testSimple() throws SQLException { assertEquals("A basic SELECT query succeeds", 1, resultSetInt); } } - - @Test - public void testWithAdditionalUrlParamInJdbcUrl() { - try (Db2Container db2 = new Db2Container() - .withUrlParam("sslConnection", "false") - .acceptLicense()) { - - db2.start(); - - String jdbcUrl = db2.getJdbcUrl(); - assertThat(jdbcUrl, containsString(":sslConnection=false;")); - } - } } diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index 6c265bc6943..36a6461aa74 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -2,7 +2,6 @@ import com.github.dockerjava.api.command.InspectContainerResponse; import lombok.NonNull; -import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.traits.LinkableContainer; import org.testcontainers.delegate.DatabaseDelegate; @@ -18,8 +17,6 @@ import java.util.Properties; import java.util.concurrent.Future; -import static java.util.stream.Collectors.joining; - /** * Base class for containers that expose a JDBC connection * @@ -31,7 +28,6 @@ public abstract class JdbcDatabaseContainer parameters = new HashMap<>(); - protected Map urlParameters = new HashMap<>(); private int startupTimeoutSeconds = 120; private int connectTimeoutSeconds = 120; @@ -86,11 +82,7 @@ public SELF withPassword(String password) { public SELF withDatabaseName(String dbName) { throw new UnsupportedOperationException(); - } - public SELF withUrlParam(String paramName, String paramValue) { - urlParameters.put(paramName, paramValue); - return self(); } /** @@ -231,21 +223,6 @@ protected String constructUrlForConnection(String queryString) { return getJdbcUrl() + queryString; } - protected String constructUrlParameters(String startCharacter, String delimiter) { - return constructUrlParameters(startCharacter, delimiter, StringUtils.EMPTY); - } - - protected String constructUrlParameters(String startCharacter, String delimiter, String endCharacter) { - String urlParameters = ""; - if (!this.urlParameters.isEmpty()) { - String additionalParameters = this.urlParameters.entrySet().stream() - .map(Object::toString) - .collect(joining(delimiter)); - urlParameters = startCharacter + additionalParameters + endCharacter; - } - return urlParameters; - } - protected void optionallyMapResourceParameterAsVolume(@NotNull String paramName, @NotNull String pathNameInContainer, @NotNull String defaultResource) { String resourceName = parameters.getOrDefault(paramName, defaultResource); diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java index 04d745a9efe..1b4cb3344e4 100644 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java @@ -60,9 +60,7 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - String additionalUrlParams = constructUrlParameters("?", "&"); - return "jdbc:mariadb://" + getHost() + ":" + getMappedPort(MARIADB_PORT) + - "/" + databaseName + additionalUrlParams; + return "jdbc:mariadb://" + getHost() + ":" + getMappedPort(MARIADB_PORT) + "/" + databaseName; } @Override diff --git a/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java b/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java index af503ce424b..646fbb40947 100644 --- a/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java +++ b/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java @@ -8,8 +8,6 @@ import java.sql.ResultSet; import java.sql.SQLException; -import static org.hamcrest.CoreMatchers.containsString; -import static org.junit.Assert.assertThat; import static org.junit.Assume.assumeFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; @@ -70,22 +68,4 @@ public void testMariaDBWithCommandOverride() throws SQLException { assertEquals("Auto increment increment should be overriden by command line", "10", result); } } - - @Test - public void testWithAdditionalUrlParamInJdbcUrl() { - MariaDBContainer mariaDBContainer = (MariaDBContainer) new MariaDBContainer() - .withUrlParam("connectTimeout", "40000") - .withUrlParam("rewriteBatchedStatements", "true"); - - try { - mariaDBContainer.start(); - String jdbcUrl = mariaDBContainer.getJdbcUrl(); - assertThat(jdbcUrl, containsString("?")); - assertThat(jdbcUrl, containsString("&")); - assertThat(jdbcUrl, containsString("rewriteBatchedStatements=true")); - assertThat(jdbcUrl, containsString("connectTimeout=40000")); - } finally { - mariaDBContainer.stop(); - } - } } diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java index b500e6fddf0..631228aeef2 100644 --- a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java @@ -62,8 +62,7 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - String additionalUrlParams = constructUrlParameters(";", ";"); - return "jdbc:sqlserver://" + getHost() + ":" + getMappedPort(MS_SQL_SERVER_PORT) + additionalUrlParams; + return "jdbc:sqlserver://" + getHost() + ":" + getMappedPort(MS_SQL_SERVER_PORT); } @Override diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java index 71e12a6d864..c1699d2473a 100644 --- a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java +++ b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java @@ -9,8 +9,6 @@ import java.sql.SQLException; import java.sql.Statement; -import static org.hamcrest.CoreMatchers.containsString; -import static org.junit.Assert.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; public class SimpleMSSQLServerTest extends AbstractContainerDatabaseTest { @@ -26,20 +24,6 @@ public void testSimple() throws SQLException { } } - @Test - public void testWithAdditionalUrlParamInJdbcUrl() { - try (MSSQLServerContainer mssqlServer = new MSSQLServerContainer<>() - .withUrlParam("integratedSecurity", "false") - .withUrlParam("applicationName", "MyApp")) { - - mssqlServer.start(); - - String jdbcUrl = mssqlServer.getJdbcUrl(); - assertThat(jdbcUrl, containsString(";integratedSecurity=false;applicationName=MyApp")); - } - } - - @Test public void testSetupDatabase() throws SQLException { try (MSSQLServerContainer mssqlServer = new MSSQLServerContainer<>()) { diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java index 1ef574cbd5e..79d29234b99 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java @@ -5,8 +5,6 @@ import java.util.HashSet; import java.util.Set; -import static java.util.stream.Collectors.joining; - /** * @author richardnorth */ @@ -72,9 +70,7 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - String additionalUrlParams = constructUrlParameters("?", "&"); - return "jdbc:mysql://" + getHost() + ":" + getMappedPort(MYSQL_PORT) + - "/" + databaseName + additionalUrlParams; + return "jdbc:mysql://" + getHost() + ":" + getMappedPort(MYSQL_PORT) + "/" + databaseName; } @Override diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java index 6d6f8d26453..ee6c3ab6993 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java @@ -9,17 +9,9 @@ import org.testcontainers.containers.output.Slf4jLogConsumer; import org.testcontainers.db.AbstractContainerDatabaseTest; -import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Statement; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.temporal.ChronoUnit; - -import static org.hamcrest.CoreMatchers.containsString; -import static org.junit.Assert.assertThat; + import static org.junit.Assert.fail; import static org.junit.Assume.assumeFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; @@ -150,77 +142,4 @@ public void testEmptyPasswordWithRootUser() throws SQLException { assertEquals("A basic SELECT query succeeds", 1, resultSetInt); } } - - @Test - public void testWithAdditionalUrlParamTimeZone() throws SQLException { - MySQLContainer mysql = (MySQLContainer) new MySQLContainer() - .withUrlParam("serverTimezone", "Europe/Zurich") - .withEnv("TZ", "Europe/Zurich") - .withLogConsumer(new Slf4jLogConsumer(logger)); - mysql.start(); - - try(Connection connection = mysql.createConnection("")) { - Statement statement = connection.createStatement(); - statement.execute("SELECT NOW();"); - try (ResultSet resultSet = statement.getResultSet()) { - resultSet.next(); - - // checking that the time_zone MySQL is Europe/Zurich - LocalDateTime localDateTime = resultSet.getObject(1, LocalDateTime.class); - ZonedDateTime actualDateTime = localDateTime.atZone(ZoneId.of("Europe/Zurich")) - .truncatedTo(ChronoUnit.MINUTES); - ZonedDateTime expectedDateTime = ZonedDateTime.now(ZoneId.of("Europe/Zurich")) - .truncatedTo(ChronoUnit.MINUTES); - - String message = String.format("MySQL time zone is not Europe/Zurich. MySQL date:%s, current date:%s", - actualDateTime, expectedDateTime); - assertTrue(message, actualDateTime.equals(expectedDateTime)); - } - } finally { - mysql.stop(); - } - } - - @Test - public void testWithAdditionalUrlParamMultiQueries() throws SQLException { - MySQLContainer mysql = (MySQLContainer) new MySQLContainer() - .withUrlParam("allowMultiQueries", "true") - .withLogConsumer(new Slf4jLogConsumer(logger)); - mysql.start(); - - try(Connection connection = mysql.createConnection("")) { - Statement statement = connection.createStatement(); - String multiQuery = "DROP TABLE IF EXISTS bar; " + - "CREATE TABLE bar (foo VARCHAR(20)); " + - "INSERT INTO bar (foo) VALUES ('hello world');"; - statement.execute(multiQuery); - statement.execute("SELECT foo FROM bar;"); - try(ResultSet resultSet = statement.getResultSet()) { - resultSet.next(); - String firstColumnValue = resultSet.getString(1); - assertEquals("Value from bar should equal real value", "hello world", firstColumnValue); - } - } finally { - mysql.stop(); - } - } - - @Test - public void testWithAdditionalUrlParamInJdbcUrl() { - MySQLContainer mysql = (MySQLContainer) new MySQLContainer() - .withUrlParam("allowMultiQueries", "true") - .withUrlParam("rewriteBatchedStatements", "true") - .withLogConsumer(new Slf4jLogConsumer(logger)); - - try { - mysql.start(); - String jdbcUrl = mysql.getJdbcUrl(); - assertThat(jdbcUrl, containsString("?")); - assertThat(jdbcUrl, containsString("&")); - assertThat(jdbcUrl, containsString("rewriteBatchedStatements=true")); - assertThat(jdbcUrl, containsString("allowMultiQueries=true")); - } finally { - mysql.stop(); - } - } } diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java index d263a141d4f..60603a7e222 100644 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java @@ -89,11 +89,6 @@ public OracleContainer withPassword(String password) { return self(); } - @Override - public OracleContainer withUrlParam(String paramName, String paramValue) { - throw new UnsupportedOperationException("The OracleDb does not support this"); - } - @SuppressWarnings("SameReturnValue") public String getSid() { return "xe"; diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index 6ba7390878e..8634f9d70fa 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -55,8 +55,6 @@ protected Set getLivenessCheckPorts() { @Override protected void configure() { - // Disable Postgres driver use of java.util.logging to reduce noise at startup time - withUrlParam("loggerLevel", "OFF"); addEnv("POSTGRES_DB", databaseName); addEnv("POSTGRES_USER", username); addEnv("POSTGRES_PASSWORD", password); @@ -69,9 +67,25 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - String additionalUrlParams = constructUrlParameters("?", "&"); - return "jdbc:postgresql://" + getContainerIpAddress() + ":" + getMappedPort(POSTGRESQL_PORT) - + "/" + databaseName + additionalUrlParams; + // Disable Postgres driver use of java.util.logging to reduce noise at startup time + return format("jdbc:postgresql://%s:%d/%s?loggerLevel=OFF", getHost(), getMappedPort(POSTGRESQL_PORT), databaseName); + } + + @Override + protected String constructUrlForConnection(String queryString) { + String baseUrl = getJdbcUrl(); + + if ("".equals(queryString)) { + return baseUrl; + } + + if (!queryString.startsWith("?")) { + throw new IllegalArgumentException("The '?' character must be included"); + } + + return baseUrl.contains("?") + ? baseUrl + QUERY_PARAM_SEPARATOR + queryString.substring(1) + : baseUrl + queryString; } @Override diff --git a/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java index c0ba7e40d44..05f6ea204a8 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java @@ -15,7 +15,7 @@ public void shouldCorrectlyAppendQueryString() { String connectionUrl = postgres.constructUrlForConnection("?stringtype=unspecified&stringtype=unspecified"); String queryString = connectionUrl.substring(connectionUrl.indexOf('?')); - assertTrue("Query String contains expected params", queryString.contains("?stringtype=unspecified&stringtype=unspecified")); + assertTrue("Query String contains expected params", queryString.contains("&stringtype=unspecified&stringtype=unspecified")); assertEquals("Query String starts with '?'", 0, queryString.indexOf('?')); assertFalse("Query String does not contain extra '?'", queryString.substring(1).contains("?")); } diff --git a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java index 36ece03b604..9c97f51e64e 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java @@ -9,8 +9,6 @@ import java.util.logging.Level; import java.util.logging.LogManager; -import static org.hamcrest.CoreMatchers.containsString; -import static org.junit.Assert.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; @@ -65,17 +63,4 @@ public void testExplicitInitScript() throws SQLException { assertEquals("Value from init script should equal real value", "hello world", firstColumnValue); } } - - @Test - public void testWithAdditionalUrlParamInJdbcUrl() { - try (PostgreSQLContainer postgres = new PostgreSQLContainer<>() - .withUrlParam("charSet", "UNICODE")) { - - postgres.start(); - String jdbcUrl = postgres.getJdbcUrl(); - assertThat(jdbcUrl, containsString("?")); - assertThat(jdbcUrl, containsString("&")); - assertThat(jdbcUrl, containsString("charSet=UNICODE")); - } - } } From 4ba01a655353dd22c8384a8d00dfc53749666109 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 21 May 2020 19:26:30 +0100 Subject: [PATCH 1394/5615] Revert "Add docs for startup strategy information/examples (#1157)" (#2776) --- docs/examples/junit4/generic/build.gradle | 1 - .../StartupCheckStrategyTest.java | 90 ------------------- docs/features/startup_and_waits.md | 49 ---------- 3 files changed, 140 deletions(-) delete mode 100644 docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java diff --git a/docs/examples/junit4/generic/build.gradle b/docs/examples/junit4/generic/build.gradle index 5ce878c7ba2..97902d5d9ca 100644 --- a/docs/examples/junit4/generic/build.gradle +++ b/docs/examples/junit4/generic/build.gradle @@ -5,5 +5,4 @@ dependencies { testCompile project(":testcontainers") testCompile project(":selenium") testCompile "org.seleniumhq.selenium:selenium-api:3.141.59" - testCompile 'org.assertj:assertj-core:3.15.0' } diff --git a/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java b/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java deleted file mode 100644 index 5ab32919bb2..00000000000 --- a/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java +++ /dev/null @@ -1,90 +0,0 @@ -package org.testcontainers.containers.startupcheck; - -import java.time.Duration; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import lombok.SneakyThrows; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.output.WaitingConsumer; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; -import static org.testcontainers.containers.startupcheck.StartupCheckStrategyTest.IndefiniteOneShotStrategyTest; -import static org.testcontainers.containers.startupcheck.StartupCheckStrategyTest.MinimumDurationStrategyTest; -import static org.testcontainers.containers.startupcheck.StartupCheckStrategyTest.OneShotStrategyTest; - -@RunWith(Suite.class) -@Suite.SuiteClasses({OneShotStrategyTest.class, IndefiniteOneShotStrategyTest.class, MinimumDurationStrategyTest.class}) -public class StartupCheckStrategyTest { - - private static final String HELLO_TESTCONTAINERS = "Hello Testcontainers!"; - - private static void waitForHello(GenericContainer container) throws TimeoutException { - WaitingConsumer consumer = new WaitingConsumer(); - container.followOutput(consumer, STDOUT); - - consumer.waitUntil(frame -> - frame.getUtf8String().contains(HELLO_TESTCONTAINERS), 30, TimeUnit.SECONDS); - } - - public static class OneShotStrategyTest { - @Rule - // withOneShotStrategy { - public GenericContainer bboxWithOneShot = new GenericContainer("busybox:1.31.1") - .withCommand(String.format("echo %s", HELLO_TESTCONTAINERS)) - .withStartupCheckStrategy( - new OneShotStartupCheckStrategy().withTimeout(Duration.ofSeconds(3)) - ); - // } - - @SneakyThrows - @Test - public void testCommandIsExecuted() { - waitForHello(bboxWithOneShot); - - assertThat(bboxWithOneShot.isRunning()).isFalse(); - } - } - - public static class IndefiniteOneShotStrategyTest { - @Rule - // withIndefiniteOneShotStrategy { - public GenericContainer bboxWithIndefiniteOneShot = new GenericContainer("busybox:1.31.1") - .withCommand("sh", "-c", String.format("sleep 5 && echo \"%s\"", HELLO_TESTCONTAINERS)) - .withStartupCheckStrategy( - new IndefiniteWaitOneShotStartupCheckStrategy() - ); - // } - - @SneakyThrows - @Test - public void testCommandIsExecuted() { - waitForHello(bboxWithIndefiniteOneShot); - - assertThat(bboxWithIndefiniteOneShot.isRunning()).isFalse(); - } - } - - public static class MinimumDurationStrategyTest { - @Rule - // withMinimumDurationStrategy { - public GenericContainer bboxWithMinimumDuration = new GenericContainer("busybox:1.31.1") - .withCommand("sh", "-c", String.format("sleep 5 && echo \"%s\"", HELLO_TESTCONTAINERS)) - .withStartupCheckStrategy( - new MinimumDurationRunningStartupCheckStrategy(Duration.ofSeconds(5)) - ); - // } - - @SneakyThrows - @Test - public void testCommandIsExecuted() { - waitForHello(bboxWithMinimumDuration); - - assertThat(bboxWithMinimumDuration.isRunning()).isTrue(); - } - } -} diff --git a/docs/features/startup_and_waits.md b/docs/features/startup_and_waits.md index 3cbec97af0d..e1d30457e5c 100644 --- a/docs/features/startup_and_waits.md +++ b/docs/features/startup_and_waits.md @@ -74,52 +74,3 @@ If none of these options meet your requirements, you can create your own subclas appropriate wait mechanism in `waitUntilReady()`. The `GenericContainer.waitingFor()` method accepts any valid [`WaitStrategy`](http://static.javadoc.io/org.testcontainers/testcontainers/{{ latest_version }}/org/testcontainers/containers/wait/strategy/WaitStrategy.html). - -## Startup check Strategies - -Ordinarily Testcontainers will check that the container has reached the running state and has not exited. -In order to do that inspect is executed against the container and state parameter is extracted. - -All logic is implemented in [`StartupCheckStrategy`](http://static.javadoc.io/org.testcontainers/testcontainers/{{ latest_version }}/org/testcontainers/containers/startupcheck/StartupCheckStrategy.html) child classes. - -### Running startup strategy example - -This is the strategy used by default. Testcontainers just checks if container is running. - -Implemented in [`IsRunningStartupCheckStrategy`](http://static.javadoc.io/org.testcontainers/testcontainers/{{ latest_version }}/org/testcontainers/containers/startupcheck/IsRunningStartupCheckStrategy.html) class. - -### One shot startup strategy example - -This strategy is intended for use with containers that only run briefly and exit of their own accord. As such, success is deemed to be when -the container has stopped with exit code 0. - - -[Using one shot startup strategy](../examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java) inside_block:withOneShotStrategy - - -### Indefinite one shot startup strategy example - -Variant of one shot strategy that does not impose a timeout. Intended for situation such as when a long running task forms part of - container startup. - -It has to be assumed that the container will stop of its own accord, either with a success or failure exit code. - - -[Using indefinite one shot startup strategy](../examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java) inside_block:withIndefiniteOneShotStrategy - - -### Minimum duration startup strategy example - -Checks that the container is running and has been running for a defined minimum period of time. - - -[Using minimum duration strategy](../examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java) inside_block:withMinimumDurationStrategy - - -### Other startup strategies - -If none of these options meet your requirements, you can create your own subclass of -[`StartupCheckStrategy`](http://static.javadoc.io/org.testcontainers/testcontainers/{{ latest_version }}/org/testcontainers/containers -/startupcheck/StartupCheckStrategy.html) with an appropriate startup check mechanism in `waitUntilStartupSuccessful()`. -Or you can leave it as is and just implement the `checkStartupState(DockerClient dockerClient, String containerId)` if you still want to check state - periodically. From 74a88ad6498b800b0e76d28ca41dc02369d870b9 Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 22 May 2020 09:36:20 +0100 Subject: [PATCH 1395/5615] Prevent Local gradle build cache collisions (#2778) --- .github/workflows/ci-examples.yml | 3 --- .github/workflows/ci.yml | 3 --- 2 files changed, 6 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index bec794dc167..8dab14e831c 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -25,7 +25,6 @@ jobs: key: ${{ runner.os }}-gradle-home-testmatrix-examples-${{ hashFiles('**/*.gradle') }} restore-keys: | ${{ runner.os }}-gradle-home-testmatrix-examples- - ${{ runner.os }}-gradle-home- - id: set-matrix working-directory: ./examples/ env: @@ -54,8 +53,6 @@ jobs: key: ${{ runner.os }}-gradle-home-examples-${{matrix.gradle_args}}_check-${{ hashFiles('**/*.gradle') }} restore-keys: | ${{ runner.os }}-gradle-home-examples-${{matrix.gradle_args}}_check- - ${{ runner.os }}-gradle-home-examples-${{matrix.gradle_args}}_check- - ${{ runner.os }}-gradle-home-examples- - name: Clear existing docker image cache run: docker image prune -af - name: Build and test Examples with Gradle (${{matrix.gradle_args}}) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 90e64dde133..6ea2319193d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,6 @@ jobs: key: ${{ runner.os }}-gradle-home-testmatrix-${{ hashFiles('**/*.gradle') }} restore-keys: | ${{ runner.os }}-gradle-home-testmatrix- - ${{ runner.os }}-gradle-home- - id: set-matrix env: # Since we override the tests executor, @@ -53,8 +52,6 @@ jobs: key: ${{ runner.os }}-gradle-home-${{matrix.gradle_args}}_check-${{ hashFiles('**/*.gradle') }} restore-keys: | ${{ runner.os }}-gradle-home-${{matrix.gradle_args}}_check- - ${{ runner.os }}-gradle-home-${{matrix.gradle_args}}_check- - ${{ runner.os }}-gradle-home- - name: Clear existing docker image cache run: docker image prune -af - name: Build and test with Gradle (${{matrix.gradle_args}}) From af077e91508cf7bbdb179053c479af7d4b068b83 Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 22 May 2020 10:08:02 +0100 Subject: [PATCH 1396/5615] Reinstate "Add docs for startup strategy information/examples" (#2779) --- docs/examples/junit4/generic/build.gradle | 1 + .../StartupCheckStrategyTest.java | 90 +++++++++++++++++++ docs/features/startup_and_waits.md | 49 ++++++++++ 3 files changed, 140 insertions(+) create mode 100644 docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java diff --git a/docs/examples/junit4/generic/build.gradle b/docs/examples/junit4/generic/build.gradle index 97902d5d9ca..5ce878c7ba2 100644 --- a/docs/examples/junit4/generic/build.gradle +++ b/docs/examples/junit4/generic/build.gradle @@ -5,4 +5,5 @@ dependencies { testCompile project(":testcontainers") testCompile project(":selenium") testCompile "org.seleniumhq.selenium:selenium-api:3.141.59" + testCompile 'org.assertj:assertj-core:3.15.0' } diff --git a/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java b/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java new file mode 100644 index 00000000000..5ab32919bb2 --- /dev/null +++ b/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java @@ -0,0 +1,90 @@ +package org.testcontainers.containers.startupcheck; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import lombok.SneakyThrows; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.WaitingConsumer; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; +import static org.testcontainers.containers.startupcheck.StartupCheckStrategyTest.IndefiniteOneShotStrategyTest; +import static org.testcontainers.containers.startupcheck.StartupCheckStrategyTest.MinimumDurationStrategyTest; +import static org.testcontainers.containers.startupcheck.StartupCheckStrategyTest.OneShotStrategyTest; + +@RunWith(Suite.class) +@Suite.SuiteClasses({OneShotStrategyTest.class, IndefiniteOneShotStrategyTest.class, MinimumDurationStrategyTest.class}) +public class StartupCheckStrategyTest { + + private static final String HELLO_TESTCONTAINERS = "Hello Testcontainers!"; + + private static void waitForHello(GenericContainer container) throws TimeoutException { + WaitingConsumer consumer = new WaitingConsumer(); + container.followOutput(consumer, STDOUT); + + consumer.waitUntil(frame -> + frame.getUtf8String().contains(HELLO_TESTCONTAINERS), 30, TimeUnit.SECONDS); + } + + public static class OneShotStrategyTest { + @Rule + // withOneShotStrategy { + public GenericContainer bboxWithOneShot = new GenericContainer("busybox:1.31.1") + .withCommand(String.format("echo %s", HELLO_TESTCONTAINERS)) + .withStartupCheckStrategy( + new OneShotStartupCheckStrategy().withTimeout(Duration.ofSeconds(3)) + ); + // } + + @SneakyThrows + @Test + public void testCommandIsExecuted() { + waitForHello(bboxWithOneShot); + + assertThat(bboxWithOneShot.isRunning()).isFalse(); + } + } + + public static class IndefiniteOneShotStrategyTest { + @Rule + // withIndefiniteOneShotStrategy { + public GenericContainer bboxWithIndefiniteOneShot = new GenericContainer("busybox:1.31.1") + .withCommand("sh", "-c", String.format("sleep 5 && echo \"%s\"", HELLO_TESTCONTAINERS)) + .withStartupCheckStrategy( + new IndefiniteWaitOneShotStartupCheckStrategy() + ); + // } + + @SneakyThrows + @Test + public void testCommandIsExecuted() { + waitForHello(bboxWithIndefiniteOneShot); + + assertThat(bboxWithIndefiniteOneShot.isRunning()).isFalse(); + } + } + + public static class MinimumDurationStrategyTest { + @Rule + // withMinimumDurationStrategy { + public GenericContainer bboxWithMinimumDuration = new GenericContainer("busybox:1.31.1") + .withCommand("sh", "-c", String.format("sleep 5 && echo \"%s\"", HELLO_TESTCONTAINERS)) + .withStartupCheckStrategy( + new MinimumDurationRunningStartupCheckStrategy(Duration.ofSeconds(5)) + ); + // } + + @SneakyThrows + @Test + public void testCommandIsExecuted() { + waitForHello(bboxWithMinimumDuration); + + assertThat(bboxWithMinimumDuration.isRunning()).isTrue(); + } + } +} diff --git a/docs/features/startup_and_waits.md b/docs/features/startup_and_waits.md index e1d30457e5c..3cbec97af0d 100644 --- a/docs/features/startup_and_waits.md +++ b/docs/features/startup_and_waits.md @@ -74,3 +74,52 @@ If none of these options meet your requirements, you can create your own subclas appropriate wait mechanism in `waitUntilReady()`. The `GenericContainer.waitingFor()` method accepts any valid [`WaitStrategy`](http://static.javadoc.io/org.testcontainers/testcontainers/{{ latest_version }}/org/testcontainers/containers/wait/strategy/WaitStrategy.html). + +## Startup check Strategies + +Ordinarily Testcontainers will check that the container has reached the running state and has not exited. +In order to do that inspect is executed against the container and state parameter is extracted. + +All logic is implemented in [`StartupCheckStrategy`](http://static.javadoc.io/org.testcontainers/testcontainers/{{ latest_version }}/org/testcontainers/containers/startupcheck/StartupCheckStrategy.html) child classes. + +### Running startup strategy example + +This is the strategy used by default. Testcontainers just checks if container is running. + +Implemented in [`IsRunningStartupCheckStrategy`](http://static.javadoc.io/org.testcontainers/testcontainers/{{ latest_version }}/org/testcontainers/containers/startupcheck/IsRunningStartupCheckStrategy.html) class. + +### One shot startup strategy example + +This strategy is intended for use with containers that only run briefly and exit of their own accord. As such, success is deemed to be when +the container has stopped with exit code 0. + + +[Using one shot startup strategy](../examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java) inside_block:withOneShotStrategy + + +### Indefinite one shot startup strategy example + +Variant of one shot strategy that does not impose a timeout. Intended for situation such as when a long running task forms part of + container startup. + +It has to be assumed that the container will stop of its own accord, either with a success or failure exit code. + + +[Using indefinite one shot startup strategy](../examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java) inside_block:withIndefiniteOneShotStrategy + + +### Minimum duration startup strategy example + +Checks that the container is running and has been running for a defined minimum period of time. + + +[Using minimum duration strategy](../examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java) inside_block:withMinimumDurationStrategy + + +### Other startup strategies + +If none of these options meet your requirements, you can create your own subclass of +[`StartupCheckStrategy`](http://static.javadoc.io/org.testcontainers/testcontainers/{{ latest_version }}/org/testcontainers/containers +/startupcheck/StartupCheckStrategy.html) with an appropriate startup check mechanism in `waitUntilStartupSuccessful()`. +Or you can leave it as is and just implement the `checkStartupState(DockerClient dockerClient, String containerId)` if you still want to check state + periodically. From a6e8be1066b837fa666ca492233b1f97cc62234f Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 22 May 2020 11:00:40 +0100 Subject: [PATCH 1397/5615] Fix flaky StartupCheckStrategyTest --- .../startupcheck/StartupCheckStrategyTest.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java b/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java index 5ab32919bb2..deef64bc59b 100644 --- a/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java +++ b/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java @@ -1,8 +1,5 @@ package org.testcontainers.containers.startupcheck; -import java.time.Duration; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import lombok.SneakyThrows; import org.junit.Rule; import org.junit.Test; @@ -11,6 +8,10 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.output.WaitingConsumer; +import java.time.Duration; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + import static org.assertj.core.api.Assertions.assertThat; import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; import static org.testcontainers.containers.startupcheck.StartupCheckStrategyTest.IndefiniteOneShotStrategyTest; @@ -75,7 +76,7 @@ public static class MinimumDurationStrategyTest { public GenericContainer bboxWithMinimumDuration = new GenericContainer("busybox:1.31.1") .withCommand("sh", "-c", String.format("sleep 5 && echo \"%s\"", HELLO_TESTCONTAINERS)) .withStartupCheckStrategy( - new MinimumDurationRunningStartupCheckStrategy(Duration.ofSeconds(5)) + new MinimumDurationRunningStartupCheckStrategy(Duration.ofSeconds(1)) ); // } From f401b1c202bd05cb2cab78c2949f3ba15b7682b4 Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 22 May 2020 12:33:29 +0100 Subject: [PATCH 1398/5615] Reinstate #1874 "Added additional url params in JdbcDatabaseContainer" (#2777) --- .../containers/ClickHouseContainer.java | 4 + .../containers/CockroachContainer.java | 4 +- .../cockroachdb/SimpleCockroachDBTest.java | 20 +++++ .../containers/Db2Container.java | 4 +- .../junit/db2/SimpleDb2Test.java | 15 ++++ .../containers/JdbcDatabaseContainer.java | 39 ++++++++- .../containers/MariaDBContainer.java | 4 +- .../junit/mariadb/SimpleMariaDBTest.java | 20 +++++ .../containers/MSSQLServerContainer.java | 3 +- .../mssqlserver/SimpleMSSQLServerTest.java | 16 ++++ .../containers/MySQLContainer.java | 6 +- .../junit/mysql/SimpleMySQLTest.java | 83 ++++++++++++++++++- .../containers/OracleContainer.java | 5 ++ .../containers/PostgreSQLContainer.java | 24 ++---- .../PostgreSQLConnectionURLTest.java | 2 +- .../postgresql/SimplePostgreSQLTest.java | 15 ++++ 16 files changed, 237 insertions(+), 27 deletions(-) diff --git a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java index 4d4ea2d11a9..10c0e1835c2 100644 --- a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java +++ b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java @@ -66,4 +66,8 @@ public String getTestQueryString() { return TEST_QUERY; } + @Override + public ClickHouseContainer withUrlParam(String paramName, String paramValue) { + throw new UnsupportedOperationException("The ClickHouse does not support this"); + } } diff --git a/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java b/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java index 3768ca7a008..de64902c88a 100644 --- a/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java +++ b/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java @@ -43,7 +43,9 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return JDBC_URL_PREFIX + "://" + getHost() + ":" + getMappedPort(DB_PORT) + "/" + databaseName; + String additionalUrlParams = constructUrlParameters("?", "&"); + return JDBC_URL_PREFIX + "://" + getHost() + ":" + getMappedPort(DB_PORT) + + "/" + databaseName + additionalUrlParams; } @Override diff --git a/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java b/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java index 7c62b2f6bcf..14fe20222a3 100644 --- a/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java +++ b/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java @@ -9,6 +9,8 @@ import java.util.logging.Level; import java.util.logging.LogManager; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; public class SimpleCockroachDBTest extends AbstractContainerDatabaseTest { @@ -42,4 +44,22 @@ public void testExplicitInitScript() throws SQLException { assertEquals("Value from init script should equal real value", "hello world", firstColumnValue); } } + + @Test + public void testWithAdditionalUrlParamInJdbcUrl() { + CockroachContainer cockroach = new CockroachContainer() + .withUrlParam("sslmode", "disable") + .withUrlParam("application_name", "cockroach"); + + try { + cockroach.start(); + String jdbcUrl = cockroach.getJdbcUrl(); + assertThat(jdbcUrl, containsString("?")); + assertThat(jdbcUrl, containsString("&")); + assertThat(jdbcUrl, containsString("sslmode=disable")); + assertThat(jdbcUrl, containsString("application_name=cockroach")); + } finally { + cockroach.stop(); + } + } } diff --git a/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java b/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java index b1472b21c6c..05edcb2c3ed 100644 --- a/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java +++ b/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java @@ -73,7 +73,9 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return "jdbc:db2://" + getHost() + ":" + getMappedPort(DB2_PORT) + "/" + databaseName; + String additionalUrlParams = constructUrlParameters(":", ";", ";"); + return "jdbc:db2://" + getHost() + ":" + getMappedPort(DB2_PORT) + + "/" + databaseName + additionalUrlParams; } @Override diff --git a/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java b/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java index 08f427cf12b..6cc7535f785 100644 --- a/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java +++ b/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java @@ -7,6 +7,8 @@ import java.sql.ResultSet; import java.sql.SQLException; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; public class SimpleDb2Test extends AbstractContainerDatabaseTest { @@ -24,4 +26,17 @@ public void testSimple() throws SQLException { assertEquals("A basic SELECT query succeeds", 1, resultSetInt); } } + + @Test + public void testWithAdditionalUrlParamInJdbcUrl() { + try (Db2Container db2 = new Db2Container() + .withUrlParam("sslConnection", "false") + .acceptLicense()) { + + db2.start(); + + String jdbcUrl = db2.getJdbcUrl(); + assertThat(jdbcUrl, containsString(":sslConnection=false;")); + } + } } diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index 36a6461aa74..62fe43e7932 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -2,6 +2,7 @@ import com.github.dockerjava.api.command.InspectContainerResponse; import lombok.NonNull; +import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.traits.LinkableContainer; import org.testcontainers.delegate.DatabaseDelegate; @@ -17,6 +18,8 @@ import java.util.Properties; import java.util.concurrent.Future; +import static java.util.stream.Collectors.joining; + /** * Base class for containers that expose a JDBC connection * @@ -28,10 +31,13 @@ public abstract class JdbcDatabaseContainer parameters = new HashMap<>(); + protected Map urlParameters = new HashMap<>(); private int startupTimeoutSeconds = 120; private int connectTimeoutSeconds = 120; + private static final String QUERY_PARAM_SEPARATOR = "&"; + public JdbcDatabaseContainer(@NonNull final String dockerImageName) { super(dockerImageName); } @@ -82,7 +88,11 @@ public SELF withPassword(String password) { public SELF withDatabaseName(String dbName) { throw new UnsupportedOperationException(); + } + public SELF withUrlParam(String paramName, String paramValue) { + urlParameters.put(paramName, paramValue); + return self(); } /** @@ -220,7 +230,34 @@ public Connection createConnection(String queryString) throws SQLException, NoDr * @return a full JDBC URL including queryString */ protected String constructUrlForConnection(String queryString) { - return getJdbcUrl() + queryString; + String baseUrl = getJdbcUrl(); + + if ("".equals(queryString)) { + return baseUrl; + } + + if (!queryString.startsWith("?")) { + throw new IllegalArgumentException("The '?' character must be included"); + } + + return baseUrl.contains("?") + ? baseUrl + QUERY_PARAM_SEPARATOR + queryString.substring(1) + : baseUrl + queryString; + } + + protected String constructUrlParameters(String startCharacter, String delimiter) { + return constructUrlParameters(startCharacter, delimiter, StringUtils.EMPTY); + } + + protected String constructUrlParameters(String startCharacter, String delimiter, String endCharacter) { + String urlParameters = ""; + if (!this.urlParameters.isEmpty()) { + String additionalParameters = this.urlParameters.entrySet().stream() + .map(Object::toString) + .collect(joining(delimiter)); + urlParameters = startCharacter + additionalParameters + endCharacter; + } + return urlParameters; } protected void optionallyMapResourceParameterAsVolume(@NotNull String paramName, @NotNull String pathNameInContainer, @NotNull String defaultResource) { diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java index 1b4cb3344e4..04d745a9efe 100644 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java @@ -60,7 +60,9 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return "jdbc:mariadb://" + getHost() + ":" + getMappedPort(MARIADB_PORT) + "/" + databaseName; + String additionalUrlParams = constructUrlParameters("?", "&"); + return "jdbc:mariadb://" + getHost() + ":" + getMappedPort(MARIADB_PORT) + + "/" + databaseName + additionalUrlParams; } @Override diff --git a/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java b/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java index 646fbb40947..af503ce424b 100644 --- a/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java +++ b/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java @@ -8,6 +8,8 @@ import java.sql.ResultSet; import java.sql.SQLException; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; import static org.junit.Assume.assumeFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; @@ -68,4 +70,22 @@ public void testMariaDBWithCommandOverride() throws SQLException { assertEquals("Auto increment increment should be overriden by command line", "10", result); } } + + @Test + public void testWithAdditionalUrlParamInJdbcUrl() { + MariaDBContainer mariaDBContainer = (MariaDBContainer) new MariaDBContainer() + .withUrlParam("connectTimeout", "40000") + .withUrlParam("rewriteBatchedStatements", "true"); + + try { + mariaDBContainer.start(); + String jdbcUrl = mariaDBContainer.getJdbcUrl(); + assertThat(jdbcUrl, containsString("?")); + assertThat(jdbcUrl, containsString("&")); + assertThat(jdbcUrl, containsString("rewriteBatchedStatements=true")); + assertThat(jdbcUrl, containsString("connectTimeout=40000")); + } finally { + mariaDBContainer.stop(); + } + } } diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java index 631228aeef2..b500e6fddf0 100644 --- a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java @@ -62,7 +62,8 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return "jdbc:sqlserver://" + getHost() + ":" + getMappedPort(MS_SQL_SERVER_PORT); + String additionalUrlParams = constructUrlParameters(";", ";"); + return "jdbc:sqlserver://" + getHost() + ":" + getMappedPort(MS_SQL_SERVER_PORT) + additionalUrlParams; } @Override diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java index c1699d2473a..71e12a6d864 100644 --- a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java +++ b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java @@ -9,6 +9,8 @@ import java.sql.SQLException; import java.sql.Statement; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; public class SimpleMSSQLServerTest extends AbstractContainerDatabaseTest { @@ -24,6 +26,20 @@ public void testSimple() throws SQLException { } } + @Test + public void testWithAdditionalUrlParamInJdbcUrl() { + try (MSSQLServerContainer mssqlServer = new MSSQLServerContainer<>() + .withUrlParam("integratedSecurity", "false") + .withUrlParam("applicationName", "MyApp")) { + + mssqlServer.start(); + + String jdbcUrl = mssqlServer.getJdbcUrl(); + assertThat(jdbcUrl, containsString(";integratedSecurity=false;applicationName=MyApp")); + } + } + + @Test public void testSetupDatabase() throws SQLException { try (MSSQLServerContainer mssqlServer = new MSSQLServerContainer<>()) { diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java index 79d29234b99..1ef574cbd5e 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java @@ -5,6 +5,8 @@ import java.util.HashSet; import java.util.Set; +import static java.util.stream.Collectors.joining; + /** * @author richardnorth */ @@ -70,7 +72,9 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return "jdbc:mysql://" + getHost() + ":" + getMappedPort(MYSQL_PORT) + "/" + databaseName; + String additionalUrlParams = constructUrlParameters("?", "&"); + return "jdbc:mysql://" + getHost() + ":" + getMappedPort(MYSQL_PORT) + + "/" + databaseName + additionalUrlParams; } @Override diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java index ee6c3ab6993..6d6f8d26453 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java @@ -9,9 +9,17 @@ import org.testcontainers.containers.output.Slf4jLogConsumer; import org.testcontainers.db.AbstractContainerDatabaseTest; +import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; - +import java.sql.Statement; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.junit.Assume.assumeFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; @@ -142,4 +150,77 @@ public void testEmptyPasswordWithRootUser() throws SQLException { assertEquals("A basic SELECT query succeeds", 1, resultSetInt); } } + + @Test + public void testWithAdditionalUrlParamTimeZone() throws SQLException { + MySQLContainer mysql = (MySQLContainer) new MySQLContainer() + .withUrlParam("serverTimezone", "Europe/Zurich") + .withEnv("TZ", "Europe/Zurich") + .withLogConsumer(new Slf4jLogConsumer(logger)); + mysql.start(); + + try(Connection connection = mysql.createConnection("")) { + Statement statement = connection.createStatement(); + statement.execute("SELECT NOW();"); + try (ResultSet resultSet = statement.getResultSet()) { + resultSet.next(); + + // checking that the time_zone MySQL is Europe/Zurich + LocalDateTime localDateTime = resultSet.getObject(1, LocalDateTime.class); + ZonedDateTime actualDateTime = localDateTime.atZone(ZoneId.of("Europe/Zurich")) + .truncatedTo(ChronoUnit.MINUTES); + ZonedDateTime expectedDateTime = ZonedDateTime.now(ZoneId.of("Europe/Zurich")) + .truncatedTo(ChronoUnit.MINUTES); + + String message = String.format("MySQL time zone is not Europe/Zurich. MySQL date:%s, current date:%s", + actualDateTime, expectedDateTime); + assertTrue(message, actualDateTime.equals(expectedDateTime)); + } + } finally { + mysql.stop(); + } + } + + @Test + public void testWithAdditionalUrlParamMultiQueries() throws SQLException { + MySQLContainer mysql = (MySQLContainer) new MySQLContainer() + .withUrlParam("allowMultiQueries", "true") + .withLogConsumer(new Slf4jLogConsumer(logger)); + mysql.start(); + + try(Connection connection = mysql.createConnection("")) { + Statement statement = connection.createStatement(); + String multiQuery = "DROP TABLE IF EXISTS bar; " + + "CREATE TABLE bar (foo VARCHAR(20)); " + + "INSERT INTO bar (foo) VALUES ('hello world');"; + statement.execute(multiQuery); + statement.execute("SELECT foo FROM bar;"); + try(ResultSet resultSet = statement.getResultSet()) { + resultSet.next(); + String firstColumnValue = resultSet.getString(1); + assertEquals("Value from bar should equal real value", "hello world", firstColumnValue); + } + } finally { + mysql.stop(); + } + } + + @Test + public void testWithAdditionalUrlParamInJdbcUrl() { + MySQLContainer mysql = (MySQLContainer) new MySQLContainer() + .withUrlParam("allowMultiQueries", "true") + .withUrlParam("rewriteBatchedStatements", "true") + .withLogConsumer(new Slf4jLogConsumer(logger)); + + try { + mysql.start(); + String jdbcUrl = mysql.getJdbcUrl(); + assertThat(jdbcUrl, containsString("?")); + assertThat(jdbcUrl, containsString("&")); + assertThat(jdbcUrl, containsString("rewriteBatchedStatements=true")); + assertThat(jdbcUrl, containsString("allowMultiQueries=true")); + } finally { + mysql.stop(); + } + } } diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java index 60603a7e222..d263a141d4f 100644 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java @@ -89,6 +89,11 @@ public OracleContainer withPassword(String password) { return self(); } + @Override + public OracleContainer withUrlParam(String paramName, String paramValue) { + throw new UnsupportedOperationException("The OracleDb does not support this"); + } + @SuppressWarnings("SameReturnValue") public String getSid() { return "xe"; diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index 8634f9d70fa..6ba7390878e 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -55,6 +55,8 @@ protected Set getLivenessCheckPorts() { @Override protected void configure() { + // Disable Postgres driver use of java.util.logging to reduce noise at startup time + withUrlParam("loggerLevel", "OFF"); addEnv("POSTGRES_DB", databaseName); addEnv("POSTGRES_USER", username); addEnv("POSTGRES_PASSWORD", password); @@ -67,25 +69,9 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - // Disable Postgres driver use of java.util.logging to reduce noise at startup time - return format("jdbc:postgresql://%s:%d/%s?loggerLevel=OFF", getHost(), getMappedPort(POSTGRESQL_PORT), databaseName); - } - - @Override - protected String constructUrlForConnection(String queryString) { - String baseUrl = getJdbcUrl(); - - if ("".equals(queryString)) { - return baseUrl; - } - - if (!queryString.startsWith("?")) { - throw new IllegalArgumentException("The '?' character must be included"); - } - - return baseUrl.contains("?") - ? baseUrl + QUERY_PARAM_SEPARATOR + queryString.substring(1) - : baseUrl + queryString; + String additionalUrlParams = constructUrlParameters("?", "&"); + return "jdbc:postgresql://" + getContainerIpAddress() + ":" + getMappedPort(POSTGRESQL_PORT) + + "/" + databaseName + additionalUrlParams; } @Override diff --git a/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java index 05f6ea204a8..c0ba7e40d44 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java @@ -15,7 +15,7 @@ public void shouldCorrectlyAppendQueryString() { String connectionUrl = postgres.constructUrlForConnection("?stringtype=unspecified&stringtype=unspecified"); String queryString = connectionUrl.substring(connectionUrl.indexOf('?')); - assertTrue("Query String contains expected params", queryString.contains("&stringtype=unspecified&stringtype=unspecified")); + assertTrue("Query String contains expected params", queryString.contains("?stringtype=unspecified&stringtype=unspecified")); assertEquals("Query String starts with '?'", 0, queryString.indexOf('?')); assertFalse("Query String does not contain extra '?'", queryString.substring(1).contains("?")); } diff --git a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java index 9c97f51e64e..36ece03b604 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java @@ -9,6 +9,8 @@ import java.util.logging.Level; import java.util.logging.LogManager; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; @@ -63,4 +65,17 @@ public void testExplicitInitScript() throws SQLException { assertEquals("Value from init script should equal real value", "hello world", firstColumnValue); } } + + @Test + public void testWithAdditionalUrlParamInJdbcUrl() { + try (PostgreSQLContainer postgres = new PostgreSQLContainer<>() + .withUrlParam("charSet", "UNICODE")) { + + postgres.start(); + String jdbcUrl = postgres.getJdbcUrl(); + assertThat(jdbcUrl, containsString("?")); + assertThat(jdbcUrl, containsString("&")); + assertThat(jdbcUrl, containsString("charSet=UNICODE")); + } + } } From 289fb50f06d1ee99c036858b8b0889e96c1396a8 Mon Sep 17 00:00:00 2001 From: AElMehdi Date: Fri, 22 May 2020 13:34:35 +0200 Subject: [PATCH 1399/5615] Update version #2757 (#2758) --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 48b63c59631..c28e0827f7c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -90,4 +90,4 @@ nav: - contributing.md - contributing_docs.md extra: - latest_version: 1.14.1 + latest_version: 1.14.2 From 647d1fe9f5766e5aeaa8a238e553976eeaf16bbb Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 23 May 2020 13:14:35 +0100 Subject: [PATCH 1400/5615] Bump influxdb-java from 2.18 to 2.19 in /modules/influxdb (#2784) --- modules/influxdb/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/influxdb/build.gradle b/modules/influxdb/build.gradle index 02af2e924a9..2deacd3a149 100644 --- a/modules/influxdb/build.gradle +++ b/modules/influxdb/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: InfluxDB" dependencies { compile project(':testcontainers') - compileOnly 'org.influxdb:influxdb-java:2.18' - testCompile 'org.influxdb:influxdb-java:2.17' + compileOnly 'org.influxdb:influxdb-java:2.19' + testCompile 'org.influxdb:influxdb-java:2.19' } From 066a1bdf4219cd0ea5e31abcdf07faab5e9c1eee Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 23 May 2020 13:14:48 +0100 Subject: [PATCH 1401/5615] Bump pulsar-client from 2.5.1 to 2.5.2 in /modules/pulsar (#2787) --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 74626006ba0..4082197b8ea 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Pulsar" dependencies { compile project(':testcontainers') - testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.5.1' + testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.5.2' testCompile group: 'org.assertj', name: 'assertj-core', version: '3.16.1' } From b3cd937da000bf92f0428cbb2ba2e92c2d95fa93 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 23 May 2020 13:14:57 +0100 Subject: [PATCH 1402/5615] Bump aws-java-sdk-sqs from 1.11.784 to 1.11.789 in /modules/localstack (#2785) --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 482b977792d..ce921607844 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.784' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.782' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.784' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.789' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.782' testCompile 'software.amazon.awssdk:s3:2.13.18' } From fe2102a99ce323a1fa00913104954264bda44a86 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 28 May 2020 22:08:35 +0100 Subject: [PATCH 1403/5615] Move away from using quay.io for default images (#2805) --- .../utility/TestcontainersConfiguration.java | 21 ++++++++++++++----- docs/features/configuration.md | 8 +++---- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 64ee03efb06..44691cfb6fb 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -1,11 +1,22 @@ package org.testcontainers.utility; import com.google.common.annotations.VisibleForTesting; -import lombok.*; +import lombok.AccessLevel; +import lombok.Data; +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.Synchronized; import lombok.extern.slf4j.Slf4j; import org.testcontainers.UnstableAPI; -import java.io.*; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.Objects; @@ -56,7 +67,7 @@ public String getSocatContainerImage() { } public String getVncRecordedContainerImage() { - return (String) properties.getOrDefault("vncrecorder.container.image", "quay.io/testcontainers/vnc-recorder:1.1.0"); + return (String) properties.getOrDefault("vncrecorder.container.image", "testcontainersofficial/vnc-recorder:1.1.0"); } public String getDockerComposeContainerImage() { @@ -72,11 +83,11 @@ public boolean isRyukPrivileged() { } public String getRyukImage() { - return (String) properties.getOrDefault("ryuk.container.image", "quay.io/testcontainers/ryuk:0.2.3"); + return (String) properties.getOrDefault("ryuk.container.image", "testcontainersofficial/ryuk:0.3.0"); } public String getSSHdImage() { - return (String) properties.getOrDefault("sshd.container.image", "quay.io/testcontainers/sshd:1.0.0"); + return (String) properties.getOrDefault("sshd.container.image", "testcontainersofficial/sshd:1.0.0"); } public Integer getRyukTimeout() { diff --git a/docs/features/configuration.md b/docs/features/configuration.md index ad039528b5d..f0172b3c978 100644 --- a/docs/features/configuration.md +++ b/docs/features/configuration.md @@ -32,7 +32,7 @@ Some companies disallow the usage of Docker Hub, but you can override `*.image` > **tinyimage.container.image = alpine:3.5** > Used by Testcontainers' core -> **vncrecorder.container.image = quay.io/testcontainers/vnc-recorder:1.1.0** +> **vncrecorder.container.image = testcontainersofficial/vnc-recorder:1.1.0** > Used by VNC recorder in Testcontainers' Seleniun integration > **ambassador.container.image = richnorth/ambassador:latest** @@ -49,13 +49,11 @@ Another possibility is to set up a registry mirror in your environment so that a For more information, see the [official Docker documentation about "Registry as a pull through cache"](https://docs.docker.com/registry/recipes/mirror/). !!!tip - Registry mirror currently only works for Docker images with image name that has no registry specified (for example, for Docker image `mariadb:10.3.6`, it works, for Docker image `quay.io/testcontainers/ryuk:0.2.3`, not). - Workaround is to to configure the affected Docker image in `.testcontainers.properties`. - For example: `ryuk.container.image = testcontainers/ryuk:0.2.3` or `ryuk.container.image = /testcontainers/ryuk:0.2.3` + Registry mirror currently only works for Docker images with image name that has no registry specified (for example, for Docker image `mariadb:10.3.6`, it works, for Docker image `quay.io/something/else`, not). ## Customizing Ryuk resource reaper -> **ryuk.container.image = quay.io/testcontainers/ryuk:0.2.3** +> **ryuk.container.image = testcontainersofficial/ryuk:0.3.0** > The resource reaper is responsible for container removal and automatic cleanup of dead containers at JVM shutdown > **ryuk.container.privileged = false** From 3a2462cbeb5fee3cafcd885eeb10f2514f6d096b Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 28 May 2020 23:13:46 +0100 Subject: [PATCH 1404/5615] Ensure that ParsedDockerfile supports platform args (#2780) --- .../images/ParsedDockerfile.java | 4 ++-- .../images/ParsedDockerfileTest.java | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/testcontainers/images/ParsedDockerfile.java b/core/src/main/java/org/testcontainers/images/ParsedDockerfile.java index 1ad43813757..d4574d17c34 100644 --- a/core/src/main/java/org/testcontainers/images/ParsedDockerfile.java +++ b/core/src/main/java/org/testcontainers/images/ParsedDockerfile.java @@ -21,7 +21,7 @@ @Slf4j public class ParsedDockerfile { - private static final Pattern FROM_LINE_PATTERN = Pattern.compile("FROM ([^\\s]+).*"); + private static final Pattern FROM_LINE_PATTERN = Pattern.compile("FROM (?--[^\\s]+\\s)*(?[^\\s]+).*", Pattern.CASE_INSENSITIVE); private final Path dockerFilePath; @@ -57,7 +57,7 @@ private void parse(List lines) { dependencyImageNames = lines.stream() .map(FROM_LINE_PATTERN::matcher) .filter(Matcher::matches) - .map(matcher -> matcher.group(1)) + .map(matcher -> matcher.group("image")) .collect(Collectors.toSet()); if (!dependencyImageNames.isEmpty()) { diff --git a/core/src/test/java/org/testcontainers/images/ParsedDockerfileTest.java b/core/src/test/java/org/testcontainers/images/ParsedDockerfileTest.java index e2b97f9fab7..52a22c83b00 100644 --- a/core/src/test/java/org/testcontainers/images/ParsedDockerfileTest.java +++ b/core/src/test/java/org/testcontainers/images/ParsedDockerfileTest.java @@ -16,6 +16,12 @@ public void doesSimpleParsing() { assertEquals("extracts a single image name", Sets.newHashSet("someimage"), parsedDockerfile.getDependencyImageNames()); } + @Test + public void isCaseInsensitive() { + final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("from someimage", "RUN something")); + assertEquals("extracts a single image name", Sets.newHashSet("someimage"), parsedDockerfile.getDependencyImageNames()); + } + @Test public void handlesTags() { final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("FROM someimage:tag", "RUN something")); @@ -40,6 +46,18 @@ public void ignoringBuildStageNames() { assertEquals("ignores build stage names and allows multiple images to be extracted", Sets.newHashSet("someimage", "nextimage"), parsedDockerfile.getDependencyImageNames()); } + @Test + public void ignoringPlatformArgs() { + final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("FROM --platform=linux/amd64 someimage", "RUN something")); + assertEquals("ignores platform args", Sets.newHashSet("someimage"), parsedDockerfile.getDependencyImageNames()); + } + + @Test + public void ignoringExtraPlatformArgs() { + final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("FROM --platform=linux/amd64 --somethingelse=value someimage", "RUN something")); + assertEquals("ignores platform args", Sets.newHashSet("someimage"), parsedDockerfile.getDependencyImageNames()); + } + @Test public void handlesGracefullyIfNoFromLine() { final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("RUN something", "# is this even a valid Dockerfile?")); From 77a662c19d524ce3d714df79bf3994368252c109 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 28 May 2020 23:16:57 +0100 Subject: [PATCH 1405/5615] Bump aws-java-sdk-logs from 1.11.782 to 1.11.789 in /modules/localstack (#2788) Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index ce921607844..2ad0552bdb3 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,6 +6,6 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.784' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.782' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.789' - testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.782' + testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.789' testCompile 'software.amazon.awssdk:s3:2.13.18' } From f2109fc33a760242942cc869b7914e88336fe5f7 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 28 May 2020 23:17:32 +0100 Subject: [PATCH 1406/5615] Bump s3 from 2.13.18 to 2.13.23 in /modules/localstack (#2786) Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 2ad0552bdb3..857b752ec1e 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,5 +7,5 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.782' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.789' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.789' - testCompile 'software.amazon.awssdk:s3:2.13.18' + testCompile 'software.amazon.awssdk:s3:2.13.23' } From 71b9bcefafbe77ec462eeca2444ca3786ce205b0 Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 29 May 2020 10:05:18 +0100 Subject: [PATCH 1407/5615] Always clean Azure pipelines builds --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 5780a01bb83..185240ae7eb 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -14,7 +14,7 @@ jobs: gradleWrapperFile: 'gradlew' jdkVersionOption: '1.11' options: "--no-daemon --continue" - tasks: "testcontainers:test --tests GenericContainerRuleTest" + tasks: "clean testcontainers:test --tests GenericContainerRuleTest" publishJUnitResults: true testResultsFiles: '**/TEST-*.xml' - script: wget -q https://get.cimate.io/release/linux/cimate && chmod +x cimate && ./cimate "**/TEST-*.xml" @@ -31,6 +31,6 @@ jobs: gradleWrapperFile: 'gradlew' jdkVersionOption: '1.11' options: '--no-daemon --continue' - tasks: 'check' + tasks: 'clean check' publishJUnitResults: true testResultsFiles: '**/TEST-*.xml' From 785bdf567f2e771de5613bbeeda419f33b4bdecf Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 29 May 2020 10:36:03 +0100 Subject: [PATCH 1408/5615] Temporarily disable use of build cache --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 185240ae7eb..1e3715bec7c 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -13,7 +13,7 @@ jobs: inputs: gradleWrapperFile: 'gradlew' jdkVersionOption: '1.11' - options: "--no-daemon --continue" + options: "--no-daemon --continue --no-build-cache" tasks: "clean testcontainers:test --tests GenericContainerRuleTest" publishJUnitResults: true testResultsFiles: '**/TEST-*.xml' @@ -30,7 +30,7 @@ jobs: inputs: gradleWrapperFile: 'gradlew' jdkVersionOption: '1.11' - options: '--no-daemon --continue' + options: '--no-daemon --continue --no-build-cache' tasks: 'clean check' publishJUnitResults: true testResultsFiles: '**/TEST-*.xml' From 892d84db66b8e99b140aecd863111cb0b0527e5d Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 29 May 2020 11:39:20 +0100 Subject: [PATCH 1409/5615] Re-enable build cache --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 1e3715bec7c..185240ae7eb 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -13,7 +13,7 @@ jobs: inputs: gradleWrapperFile: 'gradlew' jdkVersionOption: '1.11' - options: "--no-daemon --continue --no-build-cache" + options: "--no-daemon --continue" tasks: "clean testcontainers:test --tests GenericContainerRuleTest" publishJUnitResults: true testResultsFiles: '**/TEST-*.xml' @@ -30,7 +30,7 @@ jobs: inputs: gradleWrapperFile: 'gradlew' jdkVersionOption: '1.11' - options: '--no-daemon --continue --no-build-cache' + options: '--no-daemon --continue' tasks: 'clean check' publishJUnitResults: true testResultsFiles: '**/TEST-*.xml' From 6ac18b2b882f4c1a8b5d550d9ca79de339ce0265 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 29 May 2020 12:43:11 +0200 Subject: [PATCH 1410/5615] Add default labels to images created with `ImageFromDockerfile` (#2809) --- .../images/builder/ImageFromDockerfile.java | 6 +++++ .../builder/ImageFromDockerfileTest.java | 27 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 core/src/test/java/org/testcontainers/images/builder/ImageFromDockerfileTest.java diff --git a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java index 11a7e2f4cb6..638db2e351d 100644 --- a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java +++ b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java @@ -107,6 +107,12 @@ public void onNext(BuildResponseItem item) { BuildImageCmd buildImageCmd = dockerClient.buildImageCmd(in); configure(buildImageCmd); + Map labels = new HashMap<>(); + if (buildImageCmd.getLabels() != null) { + labels.putAll(buildImageCmd.getLabels()); + } + labels.putAll(DockerClientFactory.DEFAULT_LABELS); + buildImageCmd.withLabels(labels); prePullDependencyImages(dependencyImageNames); diff --git a/core/src/test/java/org/testcontainers/images/builder/ImageFromDockerfileTest.java b/core/src/test/java/org/testcontainers/images/builder/ImageFromDockerfileTest.java new file mode 100644 index 00000000000..93ec5cb5cd1 --- /dev/null +++ b/core/src/test/java/org/testcontainers/images/builder/ImageFromDockerfileTest.java @@ -0,0 +1,27 @@ +package org.testcontainers.images.builder; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.command.InspectImageResponse; +import org.junit.Test; +import org.testcontainers.DockerClientFactory; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ImageFromDockerfileTest { + + @Test + public void shouldAddDefaultLabels() { + ImageFromDockerfile image = new ImageFromDockerfile() + .withDockerfileFromBuilder(it -> it.from("scratch")); + + String imageId = image.resolve(); + + DockerClient dockerClient = DockerClientFactory.instance().client(); + + InspectImageResponse inspectImageResponse = dockerClient.inspectImageCmd(imageId).exec(); + + assertThat(inspectImageResponse.getConfig().getLabels()) + .containsAllEntriesOf(DockerClientFactory.DEFAULT_LABELS); + } + +} From 6e1f8a03699076a54e79e037f7ad34b06202f2f2 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 29 May 2020 12:43:27 +0200 Subject: [PATCH 1411/5615] Record and dump Ryuk's logs on timeout (#2810) --- .../utility/ResourceReaper.java | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index f74905e60ef..2f668d80426 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -1,10 +1,12 @@ package org.testcontainers.utility; import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.async.ResultCallback; import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.exception.NotFoundException; import com.github.dockerjava.api.model.Bind; import com.github.dockerjava.api.model.ExposedPort; +import com.github.dockerjava.api.model.Frame; import com.github.dockerjava.api.model.HostConfig; import com.github.dockerjava.api.model.Network; import com.github.dockerjava.api.model.Ports; @@ -28,6 +30,7 @@ import java.io.UnsupportedEncodingException; import java.net.Socket; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Collections; @@ -93,6 +96,20 @@ public static String start(String hostIpAddress, DockerClient client) { client.startContainerCmd(ryukContainerId).exec(); + StringBuilder ryukLog = new StringBuilder(); + + client.logContainerCmd(ryukContainerId) + .withSince(0) + .withFollowStream(true) + .withStdOut(true) + .withStdErr(true) + .exec(new ResultCallback.Adapter() { + @Override + public void onNext(Frame frame) { + ryukLog.append(new String(frame.getPayload(), StandardCharsets.UTF_8)); + } + }); + InspectContainerResponse inspectedContainer = client.inspectContainerCmd(ryukContainerId).exec(); Integer ryukPort = inspectedContainer.getNetworkSettings().getPorts().getBindings().values().stream() @@ -155,7 +172,8 @@ public static String start(String hostIpAddress, DockerClient client) { // We need to wait before we can start any containers to make sure that we delete them if (!ryukScheduledLatch.await(TestcontainersConfiguration.getInstance().getRyukTimeout(), TimeUnit.SECONDS)) { - throw new IllegalStateException("Can not connect to Ryuk"); + log.error("Timeout out waiting for Ryuk. Ryuk's log:\n{}", ryukLog); + throw new IllegalStateException(String.format("Can not connect to Ryuk at %s:%s", hostIpAddress, ryukPort)); } return ryukContainerId; From 835ac71d8ddcf206146af08b2e4efc56532d878f Mon Sep 17 00:00:00 2001 From: Michael Nitschinger Date: Fri, 29 May 2020 14:56:07 +0200 Subject: [PATCH 1412/5615] =?UTF-8?q?couchbase:=20wait=20until=20query=20e?= =?UTF-8?q?ngine=20knows=20about=20bucket=20before=20creating=E2=80=A6=20(?= =?UTF-8?q?#2662)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The motivation for this change is that in slow environments, it could be that the query engine is not aware of the just created bucket yet and so the create primary index will take longer than the 10s read timeout for the subsequent http request. As a result, this is just another precaution to minimize the chances that in slow environments the create primary index call times out with a read timeout. --- .../couchbase/CouchbaseContainer.java | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index 33f87359a92..92b35da3361 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -19,12 +19,14 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.model.ContainerNetwork; +import lombok.Cleanup; import okhttp3.Credentials; import okhttp3.FormBody; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; +import org.rnorth.ducttape.unreliables.Unreliables; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.containers.wait.strategy.WaitAllStrategy; @@ -36,6 +38,7 @@ import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** @@ -224,7 +227,7 @@ private void waitUntilNodeIsOnline() { private void renameNode() { logger().debug("Renaming Couchbase Node from localhost to {}", getHost()); - Response response = doHttpRequest(MGMT_PORT, "/node/controller/rename", "POST", new FormBody.Builder() + @Cleanup Response response = doHttpRequest(MGMT_PORT, "/node/controller/rename", "POST", new FormBody.Builder() .add("hostname", getInternalIpAddress()) .build(), false ); @@ -248,7 +251,7 @@ private void initializeServices() { } }).collect(Collectors.joining(",")); - Response response = doHttpRequest(MGMT_PORT, "/node/controller/setupServices", "POST", new FormBody.Builder() + @Cleanup Response response = doHttpRequest(MGMT_PORT, "/node/controller/setupServices", "POST", new FormBody.Builder() .add("services", services) .build(), false ); @@ -264,7 +267,7 @@ private void initializeServices() { private void configureAdminUser() { logger().debug("Configuring couchbase admin user with username: \"{}\"", username); - Response response = doHttpRequest(MGMT_PORT, "/settings/web", "POST", new FormBody.Builder() + @Cleanup Response response = doHttpRequest(MGMT_PORT, "/settings/web", "POST", new FormBody.Builder() .add("username", username) .add("password", password) .add("port", Integer.toString(MGMT_PORT)) @@ -305,7 +308,7 @@ private void configureExternalPorts() { builder.add("ftsSSL", Integer.toString(getMappedPort(SEARCH_SSL_PORT))); } - final Response response = doHttpRequest( + @Cleanup Response response = doHttpRequest( MGMT_PORT, "/node/controller/setupAlternateAddresses/external", "PUT", @@ -322,7 +325,7 @@ private void configureExternalPorts() { private void configureIndexer() { logger().debug("Configuring the indexer service"); - Response response = doHttpRequest(MGMT_PORT, "/settings/indexes", "POST", new FormBody.Builder() + @Cleanup Response response = doHttpRequest(MGMT_PORT, "/settings/indexes", "POST", new FormBody.Builder() .add("storageMode", "memory_optimized") .build(), true ); @@ -339,7 +342,7 @@ private void createBuckets() { for (BucketDefinition bucket : buckets) { logger().debug("Creating bucket \"" + bucket.getName() + "\""); - Response response = doHttpRequest(MGMT_PORT, "/pools/default/buckets", "POST", new FormBody.Builder() + @Cleanup Response response = doHttpRequest(MGMT_PORT, "/pools/default/buckets", "POST", new FormBody.Builder() .add("name", bucket.getName()) .add("ramQuotaMB", Integer.toString(bucket.getQuota())) .build(), true); @@ -353,9 +356,27 @@ private void createBuckets() { .forStatusCode(200) .waitUntilReady(this); + if (enabledServices.contains(CouchbaseService.QUERY)) { + // If the query service is enabled, make sure that we only proceed if the query engine also + // knows about the bucket in its metadata configuration. + Unreliables.retryUntilTrue(1, TimeUnit.MINUTES, () -> { + @Cleanup Response queryResponse = doHttpRequest(QUERY_PORT, "/query/service", "POST", new FormBody.Builder() + .add("statement", "SELECT COUNT(*) > 0 as present FROM system:keyspaces WHERE name = \"" + bucket.getName() + "\"") + .build(), true); + + String body = queryResponse.body() != null ? queryResponse.body().string() : null; + checkSuccessfulResponse(queryResponse, "Could not poll query service state for bucket: " + bucket.getName()); + + return Optional.of(MAPPER.readTree(body)) + .map(n -> n.at("/results/0/present")) + .map(JsonNode::asBoolean) + .orElse(false); + }); + } + if (bucket.hasPrimaryIndex()) { if (enabledServices.contains(CouchbaseService.QUERY)) { - Response queryResponse = doHttpRequest(QUERY_PORT, "/query/service", "POST", new FormBody.Builder() + @Cleanup Response queryResponse = doHttpRequest(QUERY_PORT, "/query/service", "POST", new FormBody.Builder() .add("statement", "CREATE PRIMARY INDEX on `" + bucket.getName() + "`") .build(), true); @@ -384,14 +405,8 @@ private String getInternalIpAddress() { * @param message the message that should be part of the exception of not successful. */ private void checkSuccessfulResponse(final Response response, final String message) { - try { - if (!response.isSuccessful()) { - throw new IllegalStateException(message + ": " + response.toString()); - } - } finally { - if (response.body() != null) { - response.body().close(); - } + if (!response.isSuccessful()) { + throw new IllegalStateException(message + ": " + response.toString()); } } From 48534069037e17147208e65c3c7d3d03e3c83a47 Mon Sep 17 00:00:00 2001 From: Michael Nitschinger Date: Fri, 29 May 2020 15:02:06 +0200 Subject: [PATCH 1413/5615] Bump Couchbase Server Image Version (#2804) 6.5.1 got released in april 2020 and brings bugfixes and enhancements. Let's make sure users pick that up by default. --- .../java/org/testcontainers/couchbase/CouchbaseContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index 92b35da3361..ee4b3212cac 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -71,7 +71,7 @@ public class CouchbaseContainer extends GenericContainer { private static final String DOCKER_IMAGE_NAME = "couchbase/server"; - private static final String VERSION = "6.5.0"; + private static final String VERSION = "6.5.1"; private static final ObjectMapper MAPPER = new ObjectMapper(); From c8edde492d76899abe3a4abd2faeef7d23b6801c Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 29 May 2020 15:49:51 +0100 Subject: [PATCH 1414/5615] Update mkdocs.yml --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index c28e0827f7c..795a2bdb4e2 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -90,4 +90,4 @@ nav: - contributing.md - contributing_docs.md extra: - latest_version: 1.14.2 + latest_version: 1.14.3 From ee37dfce15f1be44e93123362c791466716bcffe Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 30 May 2020 15:48:17 +0100 Subject: [PATCH 1415/5615] Bump aws-java-sdk-logs from 1.11.789 to 1.11.792 in /modules/localstack (#2822) Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.11.789 to 1.11.792. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.789...1.11.792) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 857b752ec1e..a6a73430c17 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,6 +6,6 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.784' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.782' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.789' - testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.789' + testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.792' testCompile 'software.amazon.awssdk:s3:2.13.23' } From 904337623fd75653e580faefce57dab854148b33 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 30 May 2020 15:48:51 +0100 Subject: [PATCH 1416/5615] Bump aws-java-sdk-sqs from 1.11.789 to 1.11.792 in /modules/localstack (#2820) Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index a6a73430c17..f3e093a49b8 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.784' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.782' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.789' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.792' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.792' testCompile 'software.amazon.awssdk:s3:2.13.23' } From 0a6e4159005db2b350041017422b175782ca7246 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 30 May 2020 15:57:42 +0100 Subject: [PATCH 1417/5615] Bump solr-solrj from 8.5.1 to 8.5.2 in /examples (#2819) Bumps solr-solrj from 8.5.1 to 8.5.2. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- examples/solr-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/solr-container/build.gradle b/examples/solr-container/build.gradle index 7e9b7a8c71f..5845753cb08 100644 --- a/examples/solr-container/build.gradle +++ b/examples/solr-container/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly "org.projectlombok:lombok:1.18.10" annotationProcessor "org.projectlombok:lombok:1.18.10" - implementation 'org.apache.solr:solr-solrj:8.5.1' + implementation 'org.apache.solr:solr-solrj:8.5.2' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.testcontainers:solr' From 46f085d0fe54a3a55ca5f80410911c73b98c4ea3 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 30 May 2020 15:57:51 +0100 Subject: [PATCH 1418/5615] Bump mssql-jdbc from 8.3.0.jre8-preview to 8.3.1.jre8-preview in /modules/mssqlserver (#2823) Bumps [mssql-jdbc](https://github.com/Microsoft/mssql-jdbc) from 8.3.0.jre8-preview to 8.3.1.jre8-preview. - [Release notes](https://github.com/Microsoft/mssql-jdbc/releases) - [Changelog](https://github.com/microsoft/mssql-jdbc/blob/dev/CHANGELOG.md) - [Commits](https://github.com/Microsoft/mssql-jdbc/commits) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/mssqlserver/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index 8cdfa35dee1..bfb9777ddfa 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -10,12 +10,12 @@ dependencies { compileOnly 'io.r2dbc:r2dbc-mssql:0.8.2.RELEASE' testCompile project(':jdbc-test') - testCompile 'com.microsoft.sqlserver:mssql-jdbc:8.3.0.jre8-preview' + testCompile 'com.microsoft.sqlserver:mssql-jdbc:8.3.1.jre8-preview' testCompile project(':r2dbc') testCompile 'io.r2dbc:r2dbc-mssql:0.8.1.RELEASE' // MSSQL's wait strategy requires the JDBC driver testCompile testFixtures(project(':r2dbc')) - testCompile 'com.microsoft.sqlserver:mssql-jdbc:8.3.0.jre8-preview' + testCompile 'com.microsoft.sqlserver:mssql-jdbc:8.3.1.jre8-preview' } From 289188be6d58c20b042fa7016453fe39875b6e28 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 30 May 2020 15:58:02 +0100 Subject: [PATCH 1419/5615] Bump r2dbc-postgresql from 0.8.2.RELEASE to 0.8.3.RELEASE in /modules/postgresql (#2824) Bumps [r2dbc-postgresql](https://github.com/r2dbc/r2dbc-postgresql) from 0.8.2.RELEASE to 0.8.3.RELEASE. - [Release notes](https://github.com/r2dbc/r2dbc-postgresql/releases) - [Changelog](https://github.com/r2dbc/r2dbc-postgresql/blob/v0.8.3.RELEASE/CHANGELOG) - [Commits](https://github.com/r2dbc/r2dbc-postgresql/compare/v0.8.2.RELEASE...v0.8.3.RELEASE) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/postgresql/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 380a3f86a80..1d808b1555d 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -7,12 +7,12 @@ dependencies { compile project(':jdbc') compileOnly project(':r2dbc') - compileOnly 'io.r2dbc:r2dbc-postgresql:0.8.2.RELEASE' + compileOnly 'io.r2dbc:r2dbc-postgresql:0.8.3.RELEASE' testCompile project(':jdbc-test') testCompile project(':test-support') testCompile 'org.postgresql:postgresql:42.2.12' testCompile testFixtures(project(':r2dbc')) - testCompile 'io.r2dbc:r2dbc-postgresql:0.8.1.RELEASE' + testCompile 'io.r2dbc:r2dbc-postgresql:0.8.3.RELEASE' } From f6e48a49963be968b4662cfe8493c219daf82d29 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 30 May 2020 15:59:17 +0100 Subject: [PATCH 1420/5615] Bump aws-java-sdk-s3 from 1.11.784 to 1.11.792 in /modules/localstack (#2817) Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/localstack/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index f3e093a49b8..f35d38c3530 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.784' - testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.782' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.792' + testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.792' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.792' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.792' testCompile 'software.amazon.awssdk:s3:2.13.23' From 9a4a4a971eed573f77629ec025aeea10cb9114c8 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 30 May 2020 16:10:06 +0100 Subject: [PATCH 1421/5615] Bump r2dbc-mssql from 0.8.2.RELEASE to 0.8.3.RELEASE in /modules/mssqlserver (#2821) Bumps [r2dbc-mssql](https://github.com/r2dbc/r2dbc-mssql) from 0.8.2.RELEASE to 0.8.3.RELEASE. - [Release notes](https://github.com/r2dbc/r2dbc-mssql/releases) - [Changelog](https://github.com/r2dbc/r2dbc-mssql/blob/v0.8.3.RELEASE/CHANGELOG) - [Commits](https://github.com/r2dbc/r2dbc-mssql/compare/v0.8.2.RELEASE...v0.8.3.RELEASE) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/mssqlserver/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index bfb9777ddfa..d9e01fe3a57 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -7,13 +7,13 @@ dependencies { compile project(':jdbc') compileOnly project(':r2dbc') - compileOnly 'io.r2dbc:r2dbc-mssql:0.8.2.RELEASE' + compileOnly 'io.r2dbc:r2dbc-mssql:0.8.3.RELEASE' testCompile project(':jdbc-test') testCompile 'com.microsoft.sqlserver:mssql-jdbc:8.3.1.jre8-preview' testCompile project(':r2dbc') - testCompile 'io.r2dbc:r2dbc-mssql:0.8.1.RELEASE' + testCompile 'io.r2dbc:r2dbc-mssql:0.8.3.RELEASE' // MSSQL's wait strategy requires the JDBC driver testCompile testFixtures(project(':r2dbc')) From a5b4f2c6f8d9ee778389358040464fa1ca6f156e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 30 May 2020 16:18:07 +0100 Subject: [PATCH 1422/5615] Bump testcontainers from 1.14.2 to 1.14.3 in /core (#2818) Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> Co-authored-by: Richard North --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 48bbf57d7e9..efe90a8b68d 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -104,7 +104,7 @@ if (!org.gradle.internal.os.OperatingSystem.current().isWindows()) { } dependencies { - baseline 'org.testcontainers:testcontainers:1.14.2', { + baseline 'org.testcontainers:testcontainers:1.14.3', { exclude group: "*", module: "*" } From 0eb2dea4298088e35c59f7bf947812d807f9006e Mon Sep 17 00:00:00 2001 From: Joseph Petersen Date: Mon, 1 Jun 2020 19:22:27 +0200 Subject: [PATCH 1423/5615] Switch to latest v5 release of release-drafter (#2815) Co-authored-by: Kevin Wittek --- .github/workflows/release-drafter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 6f4e849bd5b..3123c3f6fac 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -9,6 +9,6 @@ jobs: update_release_draft: runs-on: ubuntu-latest steps: - - uses: release-drafter/release-drafter@v5.2.0 + - uses: release-drafter/release-drafter@v5.9.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From c3b85deb8a7bafac64bc47a01531522bfd7ba3b5 Mon Sep 17 00:00:00 2001 From: Erhun Baycelik Date: Thu, 4 Jun 2020 15:51:05 +0300 Subject: [PATCH 1424/5615] [Documentation Update] Update Mysql and Postgres tests example URLs (#2832) Tests url was outdated. --- docs/modules/databases/jdbc.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/modules/databases/jdbc.md b/docs/modules/databases/jdbc.md index 5f83d7fcdeb..21393455a9f 100644 --- a/docs/modules/databases/jdbc.md +++ b/docs/modules/databases/jdbc.md @@ -115,5 +115,5 @@ Note that if you use `@Rule`, you will be given an isolated container for each t Examples/Tests: - * [MySQL](https://github.com/testcontainers/testcontainers-java/blob/master/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java) - * [PostgreSQL](https://github.com/testcontainers/testcontainers-java/blob/master/modules/jdbc-test/src/test/java/org/testcontainers/junit/SimplePostgreSQLTest.java) \ No newline at end of file + * [MySQL](https://github.com/testcontainers/testcontainers-java/blob/master/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java) + * [PostgreSQL](https://github.com/testcontainers/testcontainers-java/blob/master/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java) From e94e13e67a9dadcc5e0fffb32d4827a25d176d89 Mon Sep 17 00:00:00 2001 From: Alexander Zapasnik Date: Thu, 4 Jun 2020 23:19:00 +0300 Subject: [PATCH 1425/5615] Migrate default postgis image to use the new docker hub repo (#2797) Co-authored-by: Kevin Wittek --- .../testcontainers/containers/PostgisContainerProvider.java | 4 ++-- .../testcontainers/jdbc/postgis/PostgisJDBCDriverTest.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java index 83f15e3fd6f..54c87a84648 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java @@ -10,8 +10,8 @@ public class PostgisContainerProvider extends JdbcDatabaseContainerProvider { private static final String NAME = "postgis"; - private static final String DEFAULT_TAG = "10"; - private static final String DEFAULT_IMAGE = "mdillon/postgis"; + private static final String DEFAULT_TAG = "12-3.0"; + private static final String DEFAULT_IMAGE = "postgis/postgis"; public static final String USER_PARAM = "user"; public static final String PASSWORD_PARAM = "password"; diff --git a/modules/postgresql/src/test/java/org/testcontainers/jdbc/postgis/PostgisJDBCDriverTest.java b/modules/postgresql/src/test/java/org/testcontainers/jdbc/postgis/PostgisJDBCDriverTest.java index 551ba86e185..b1d01441016 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/jdbc/postgis/PostgisJDBCDriverTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/jdbc/postgis/PostgisJDBCDriverTest.java @@ -16,7 +16,7 @@ public static Iterable data() { return asList( new Object[][]{ {"jdbc:tc:postgis://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, - {"jdbc:tc:postgis:9.6://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, + {"jdbc:tc:postgis:9.6-2.5://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, }); } } From 617cb73acb05d8f1fd8d2d626313d16fb8bff7ca Mon Sep 17 00:00:00 2001 From: SahilWadhwa Date: Fri, 5 Jun 2020 20:11:48 +0530 Subject: [PATCH 1426/5615] Enable AWS KMS For LocalStack 0.10.8 (#2812) Co-authored-by: Sahil --- .../localstack/LocalStackContainer.java | 3 ++- .../localstack/LocalstackContainerTest.java | 26 ++++++++++++++++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java index 96cedb37726..72eb320c729 100644 --- a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java +++ b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java @@ -252,7 +252,8 @@ public enum Service { STEPFUNCTIONS("stepfunctions", 4585), CLOUDWATCHLOGS("logs", 4586), STS("sts", 4592), - IAM("iam", 4593); + IAM("iam", 4593), + KMS("kms", 4599); String localStackName; diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java index ee5945d0aff..d4e2f0c2864 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java @@ -1,6 +1,11 @@ package org.testcontainers.containers.localstack; +import com.amazonaws.services.kms.AWSKMS; +import com.amazonaws.services.kms.AWSKMSClientBuilder; +import com.amazonaws.services.kms.model.CreateKeyRequest; +import com.amazonaws.services.kms.model.CreateKeyResult; +import com.amazonaws.services.kms.model.Tag; import com.amazonaws.services.logs.AWSLogs; import com.amazonaws.services.logs.AWSLogsClientBuilder; import com.amazonaws.services.logs.model.CreateLogGroupRequest; @@ -38,9 +43,7 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.containers.localstack.LocalStackContainer.Service.S3; -import static org.testcontainers.containers.localstack.LocalStackContainer.Service.SQS; -import static org.testcontainers.containers.localstack.LocalStackContainer.Service.CLOUDWATCHLOGS; +import static org.testcontainers.containers.localstack.LocalStackContainer.Service.*; /** * Tests for Localstack Container, used both in bridge network (exposed to host) and docker network modes. @@ -58,7 +61,7 @@ public static class WithoutNetwork { // without_network { @ClassRule public static LocalStackContainer localstack = new LocalStackContainer() - .withServices(S3, SQS, CLOUDWATCHLOGS); + .withServices(S3, SQS, CLOUDWATCHLOGS, KMS); // } @Test @@ -135,6 +138,21 @@ public void cloudWatchLogsTestOverBridgeNetwork() { assertEquals("One log group should be created", 1, groups.size()); assertEquals("Name of created log group is [foo]", "foo", groups.get(0).getLogGroupName()); } + + @Test + public void kmsKeyCreationTest() { + AWSKMS awskms = AWSKMSClientBuilder.standard() + .withEndpointConfiguration(localstack.getEndpointConfiguration(KMS)) + .withCredentials(localstack.getDefaultCredentialsProvider()) + .build(); + + String desc = String.format("AWS CMK Description"); + Tag createdByTag = new Tag().withTagKey("CreatedBy").withTagValue("StorageService"); + CreateKeyRequest req = new CreateKeyRequest().withDescription(desc).withTags(createdByTag); + CreateKeyResult key = awskms.createKey(req); + + assertEquals("AWS KMS Customer Managed Key should be created ", key.getKeyMetadata().getDescription(), desc); + } } public static class WithNetwork { From ff73a5e355ce72097fd2d7f7e0bf3879f213a514 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 5 Jun 2020 21:31:23 +0200 Subject: [PATCH 1427/5615] Clean up the deprecated methods (#2835) * Clean up the deprecated methods * fix the examples project * add japicmp excludes --- core/build.gradle | 44 +- .../containers/AmbassadorContainer.java | 67 --- .../testcontainers/containers/Container.java | 28 -- .../containers/GenericContainer.java | 110 +---- .../containers/traits/VncService.java | 22 - .../containers/wait/HostPortWaitStrategy.java | 31 -- .../containers/wait/HttpWaitStrategy.java | 77 ---- .../wait/LogMessageWaitStrategy.java | 29 -- .../testcontainers/containers/wait/Wait.java | 67 --- .../containers/wait/WaitAllStrategy.java | 41 -- .../containers/wait/WaitStrategy.java | 38 -- .../AuditLoggingDockerClient.java | 4 +- .../AuthDelegatingDockerClientConfig.java | 7 +- .../dockerclient/DockerClientConfigUtils.java | 11 +- .../DockerClientProviderStrategy.java | 1 - .../okhttp/NamedPipeSocketFactory.java | 134 ------ .../okhttp/OkHttpDockerCmdExecFactory.java | 162 ------- .../okhttp/OkHttpInvocationBuilder.java | 414 ------------------ .../transport/okhttp/OkHttpWebTarget.java | 113 ----- .../transport/okhttp/UnixSocketFactory.java | 92 ---- .../utility/ResourceReaper.java | 5 - .../linkedcontainer/RedmineContainer.java | 2 +- .../containers/ClickHouseContainer.java | 2 +- .../containers/PostgreSQLContainer.java | 3 +- .../containers/BrowserWebDriverContainer.java | 16 +- 25 files changed, 58 insertions(+), 1462 deletions(-) delete mode 100644 core/src/main/java/org/testcontainers/containers/AmbassadorContainer.java delete mode 100644 core/src/main/java/org/testcontainers/containers/traits/VncService.java delete mode 100644 core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java delete mode 100644 core/src/main/java/org/testcontainers/containers/wait/HttpWaitStrategy.java delete mode 100644 core/src/main/java/org/testcontainers/containers/wait/LogMessageWaitStrategy.java delete mode 100644 core/src/main/java/org/testcontainers/containers/wait/Wait.java delete mode 100644 core/src/main/java/org/testcontainers/containers/wait/WaitAllStrategy.java delete mode 100644 core/src/main/java/org/testcontainers/containers/wait/WaitStrategy.java rename core/src/main/java/org/testcontainers/dockerclient/{auth => }/AuthDelegatingDockerClientConfig.java (90%) delete mode 100644 core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/NamedPipeSocketFactory.java delete mode 100644 core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpDockerCmdExecFactory.java delete mode 100644 core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java delete mode 100644 core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpWebTarget.java delete mode 100644 core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/UnixSocketFactory.java diff --git a/core/build.gradle b/core/build.gradle index efe90a8b68d..5011d24d70c 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -89,9 +89,43 @@ task japicmp(type: me.champeau.gradle.japicmp.JapicmpTask) { "org.testcontainers.shaded.*", ] + // TODO remove once 1.15.0 is released + classExcludes = [ + "org.testcontainers.containers.AmbassadorContainer", + "org.testcontainers.containers.traits.VncService", + "org.testcontainers.containers.wait.HostPortWaitStrategy", + "org.testcontainers.containers.wait.HttpWaitStrategy", + "org.testcontainers.containers.wait.LogMessageWaitStrategy", + "org.testcontainers.containers.wait.Wait", + "org.testcontainers.containers.wait.WaitAllStrategy", + "org.testcontainers.containers.wait.WaitStrategy", + "org.testcontainers.dockerclient.AuditLoggingDockerClient", + "org.testcontainers.dockerclient.auth.AuthDelegatingDockerClientConfig", + "org.testcontainers.containers.GenericContainer\$AbstractWaitStrategy", + "org.testcontainers.dockerclient.transport.okhttp.NamedPipeSocketFactory", + "org.testcontainers.dockerclient.transport.okhttp.OkHttpDockerCmdExecFactory", + "org.testcontainers.dockerclient.transport.okhttp.UnixSocketFactory", + ] + methodExcludes = [ - // METHOD_NEW_DEFAULT - "org.testcontainers.containers.ContainerState#getHost()" + "org.testcontainers.dockerclient.DockerClientConfigUtils#getDetectedDockerHostIp()", + "org.testcontainers.dockerclient.DockerClientConfigUtils#getDockerHostIpAddress(com.github.dockerjava.core.DockerClientConfig)", + + "org.testcontainers.utility.ResourceReaper#start(java.lang.String, com.github.dockerjava.api.DockerClient, boolean)", + + "org.testcontainers.containers.Container#fetchDockerDaemonInfo()", + "org.testcontainers.containers.Container#getDockerDaemonInfo()", + + "org.testcontainers.containers.GenericContainer#fetchDockerDaemonInfo()", + "org.testcontainers.containers.GenericContainer#getDockerDaemonInfo()", + + "org.testcontainers.containers.Container\$ExecResult#Container\$ExecResult(java.lang.String, java.lang.String)", + ] + + fieldExcludes = [ + "org.testcontainers.containers.GenericContainer#containerId", + "org.testcontainers.containers.GenericContainer#containerName", + "org.testcontainers.containers.GenericContainer#dockerDaemonInfo", ] onlyBinaryIncompatibleModified = true @@ -127,10 +161,6 @@ dependencies { compile "net.java.dev.jna:jna-platform:5.5.0" - shaded ('org.scala-sbt.ipcsocket:ipcsocket:1.0.1') { - exclude(group: "net.java.dev.jna") - } - shaded ('com.github.docker-java:docker-java-transport-okhttp:3.2.1') { exclude(group: 'net.java.dev.jna') exclude(group: 'com.google.code.findbug') @@ -140,8 +170,6 @@ dependencies { shaded "org.yaml:snakeyaml:1.25" - shaded 'com.squareup.okhttp3:okhttp:3.14.8' - shaded 'org.glassfish.main.external:trilead-ssh2-repackaged:4.1.2' shaded 'org.zeroturnaround:zt-exec:1.10', { diff --git a/core/src/main/java/org/testcontainers/containers/AmbassadorContainer.java b/core/src/main/java/org/testcontainers/containers/AmbassadorContainer.java deleted file mode 100644 index 0b86821d293..00000000000 --- a/core/src/main/java/org/testcontainers/containers/AmbassadorContainer.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.testcontainers.containers; - -import com.github.dockerjava.api.exception.DockerException; -import com.github.dockerjava.api.model.Container; -import lombok.Data; -import lombok.EqualsAndHashCode; -import org.testcontainers.containers.traits.LinkableContainer; -import org.testcontainers.utility.TestcontainersConfiguration; - -import java.util.List; - -import static java.util.Arrays.asList; - -/** - * An ambassador container is used as a TCP proxy, enabling any TCP port of another linked container to be exposed - * publicly, even if that container does not make the port public itself. The richnorth/ambassador:latest - * container is used (based on HAProxy). - * - * @deprecated use {@link SocatContainer} - */ -@EqualsAndHashCode(callSuper = false) -@Data -@Deprecated -public class AmbassadorContainer> extends GenericContainer { - - private final String otherContainerName; - private final String serviceName; - private final int servicePort; - - public AmbassadorContainer(LinkableContainer otherContainer, String serviceName, int servicePort) { - super(TestcontainersConfiguration.getInstance().getAmbassadorContainerImage()); - - /* - Use the unique 'identifierPrefix' (random compose project name) so that the ambassador can see - the container it's supposed to be proxying. - */ - this.otherContainerName = otherContainer.getContainerName(); - this.serviceName = serviceName; - this.servicePort = servicePort; - - // Link - addLink(otherContainer, serviceName); - - // Expose ambassador's port - addExposedPort(servicePort); - - // Tell the proxy what to connect to within the docker network - addEnv("SERVICE_NAME", serviceName); - addEnv("SERVICE_PORT", String.format("%d", servicePort)); - } - - public boolean isServiceReady() { - try { - List allContainers = dockerClient.listContainersCmd().withShowAll(true).exec(); - - for (Container container : allContainers) { - if (asList(container.getNames()).contains("/" + otherContainerName) && !container.getStatus().contains("Exited")) { - return true; - } - } - - } catch (DockerException e) { - return false; - } - return false; - } -} diff --git a/core/src/main/java/org/testcontainers/containers/Container.java b/core/src/main/java/org/testcontainers/containers/Container.java index 631e2653ceb..9c1cb7f8034 100644 --- a/core/src/main/java/org/testcontainers/containers/Container.java +++ b/core/src/main/java/org/testcontainers/containers/Container.java @@ -2,7 +2,6 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.model.Bind; -import com.github.dockerjava.api.model.Info; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.NonNull; @@ -13,14 +12,9 @@ import org.testcontainers.containers.startupcheck.StartupCheckStrategy; import org.testcontainers.containers.traits.LinkableContainer; import org.testcontainers.containers.wait.strategy.WaitStrategy; -import org.testcontainers.images.builder.Transferable; import org.testcontainers.utility.LogUtils; import org.testcontainers.utility.MountableFile; -import org.testcontainers.utility.ThrowingFunction; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.Charset; import java.time.Duration; import java.util.List; import java.util.Map; @@ -48,14 +42,6 @@ class ExecResult { int exitCode; String stdout; String stderr; - - /** - * @deprecated should not be instantiated outside of the library, please migrate - */ - @Deprecated - public ExecResult(String stdout, String stderr) { - this(-1, stdout, stderr); - } } /** @@ -413,13 +399,6 @@ default void followOutput(Consumer consumer, OutputFrame.OutputType */ SELF withLogConsumer(Consumer consumer); - /** - * - * @deprecated please use {@code org.testcontainers.DockerClientFactory.instance().client().infoCmd().exec()} - */ - @Deprecated - Info fetchDockerDaemonInfo() throws IOException; - List getPortBindings(); List getExtraHosts(); @@ -447,13 +426,6 @@ default void followOutput(Consumer consumer, OutputFrame.OutputType DockerClient getDockerClient(); - /** - * - * @deprecated please use {@code org.testcontainers.DockerClientFactory.instance().client().infoCmd().exec()} - */ - @Deprecated - Info getDockerDaemonInfo(); - void setExposedPorts(List exposedPorts); void setPortBindings(List portBindings); diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index d906c320bd5..072129de60c 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -44,8 +44,8 @@ import org.testcontainers.containers.startupcheck.MinimumDurationRunningStartupCheckStrategy; import org.testcontainers.containers.startupcheck.StartupCheckStrategy; import org.testcontainers.containers.traits.LinkableContainer; -import org.testcontainers.containers.wait.Wait; -import org.testcontainers.containers.wait.WaitStrategy; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.containers.wait.strategy.WaitStrategy; import org.testcontainers.containers.wait.strategy.WaitStrategyTarget; import org.testcontainers.images.ImagePullPolicy; import org.testcontainers.images.RemoteDockerImage; @@ -190,30 +190,13 @@ public class GenericContainer> @Setter(AccessLevel.NONE) protected DockerClient dockerClient = DockerClientFactory.lazyClient(); - /* - * Info about the Docker server; lazily fetched. - */ - @Setter(AccessLevel.NONE) - protected Info dockerDaemonInfo = null; - /** * Set during container startup - * // TODO make it private * - * @deprecated use {@link ContainerState#getContainerId()} */ @Setter(AccessLevel.NONE) - @Deprecated - protected String containerId; - - /** - * Set during container startup - * - * @deprecated use {@link GenericContainer#getContainerInfo()} - */ - @Setter(AccessLevel.NONE) - @Deprecated - protected String containerName; + @VisibleForTesting + String containerId; @Setter(AccessLevel.NONE) private InspectContainerResponse containerInfo; @@ -426,7 +409,6 @@ private void tryStart(Instant startedAt) { // Tell subclasses that we're starting containerInfo = dockerClient.inspectContainerCmd(containerId).exec(); - containerName = containerInfo.getName(); containerIsStarting(containerInfo, reused); // Wait until the container has reached the desired running state @@ -1322,18 +1304,6 @@ public SELF withLogConsumer(Consumer consumer) { return self(); } - /** - * {@inheritDoc} - */ - @Override - public synchronized Info fetchDockerDaemonInfo() throws IOException { - - if (this.dockerDaemonInfo == null) { - this.dockerDaemonInfo = this.dockerClient.infoCmd().exec(); - } - return this.dockerDaemonInfo; - } - /** * {@inheritDoc} */ @@ -1403,74 +1373,8 @@ public int hashCode() { return System.identityHashCode(this); } - /** - * Convenience class with access to non-public members of GenericContainer. - * - * @deprecated use {@link org.testcontainers.containers.wait.strategy.AbstractWaitStrategy} - */ - @Deprecated - public static abstract class AbstractWaitStrategy extends org.testcontainers.containers.wait.strategy.AbstractWaitStrategy implements WaitStrategy { - protected GenericContainer container; - - @NonNull - protected Duration startupTimeout = Duration.ofSeconds(60); - - /** - * Wait until the container has started. - * - * @param container the container for which to wait - */ - @Override - public void waitUntilReady(GenericContainer container) { - this.container = container; - waitUntilReady(); - } - - /** - * Wait until {@link #container} has started. - */ - protected abstract void waitUntilReady(); - - /** - * Set the duration of waiting time until container treated as started. - * - * @param startupTimeout timeout - * @return this - * @see WaitStrategy#waitUntilReady(GenericContainer) - */ - public WaitStrategy withStartupTimeout(Duration startupTimeout) { - this.startupTimeout = startupTimeout; - return this; - } - - /** - * @return the container's logger - */ - protected Logger logger() { - return container.logger(); - } - - /** - * @return the port on which to check if the container is ready - * @deprecated see {@link AbstractWaitStrategy#getLivenessCheckPorts()} - */ - @Deprecated - protected Integer getLivenessCheckPort() { - return container.getLivenessCheckPort(); - } - - /** - * @return the ports on which to check if the container is ready - */ - protected Set getLivenessCheckPorts() { - return container.getLivenessCheckPorts(); - } - - /** - * @return the rate limiter to use - */ - protected RateLimiter getRateLimiter() { - return DOCKER_CLIENT_RATE_LIMITER; - } + @Override + public String getContainerName() { + return getContainerInfo().getName(); } } diff --git a/core/src/main/java/org/testcontainers/containers/traits/VncService.java b/core/src/main/java/org/testcontainers/containers/traits/VncService.java deleted file mode 100644 index c0f6458c2c0..00000000000 --- a/core/src/main/java/org/testcontainers/containers/traits/VncService.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.testcontainers.containers.traits; - -/** - * A container which exposes a VNC server. - */ -@Deprecated -public interface VncService { - /** - * @return a URL which can be used to connect to the VNC server from the machine running the tests. Exposed for convenience, e.g. to aid manual debugging - */ - String getVncAddress(); - - /** - * @return the VNC password for this server - */ - String getPassword(); - - /** - * @return the port number on which the VNC server is running - */ - int getPort(); -} diff --git a/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java deleted file mode 100644 index e208b80345d..00000000000 --- a/core/src/main/java/org/testcontainers/containers/wait/HostPortWaitStrategy.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.testcontainers.containers.wait; - -import lombok.extern.slf4j.Slf4j; -import org.testcontainers.containers.GenericContainer; - -import java.time.Duration; - -/** - * Waits until a socket connection can be established on a port exposed or mapped by the container. - * - * @author richardnorth - * - * @deprecated Use {@link org.testcontainers.containers.wait.strategy.HostPortWaitStrategy} - */ -@Deprecated -@Slf4j -public class HostPortWaitStrategy extends GenericContainer.AbstractWaitStrategy { - - private org.testcontainers.containers.wait.strategy.HostPortWaitStrategy delegateStrategy = new org.testcontainers.containers.wait.strategy.HostPortWaitStrategy(); - - @Override - protected void waitUntilReady() { - delegateStrategy.waitUntilReady(this.waitStrategyTarget); - } - - @Override - public WaitStrategy withStartupTimeout(Duration startupTimeout) { - delegateStrategy.withStartupTimeout(startupTimeout); - return super.withStartupTimeout(startupTimeout); - } -} diff --git a/core/src/main/java/org/testcontainers/containers/wait/HttpWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/HttpWaitStrategy.java deleted file mode 100644 index d27133c499d..00000000000 --- a/core/src/main/java/org/testcontainers/containers/wait/HttpWaitStrategy.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.testcontainers.containers.wait; - -import org.testcontainers.containers.GenericContainer; - -import java.util.function.Predicate; - -/** - * Waits until an HTTP(S) endpoint returns a given status code. - * - * @author Pete Cornish {@literal } - * - * @deprecated Use {@link org.testcontainers.containers.wait.strategy.HttpWaitStrategy} - */ -@Deprecated -public class HttpWaitStrategy extends GenericContainer.AbstractWaitStrategy { - - private org.testcontainers.containers.wait.strategy.HttpWaitStrategy delegateStrategy = new org.testcontainers.containers.wait.strategy.HttpWaitStrategy(); - - /** - * Waits for the given status code. - * - * @param statusCode the expected status code - * @return this - */ - public HttpWaitStrategy forStatusCode(int statusCode) { - delegateStrategy.forStatusCode(statusCode); - return this; - } - - /** - * Waits for the given path. - * - * @param path the path to check - * @return this - */ - public HttpWaitStrategy forPath(String path) { - delegateStrategy.forPath(path); - return this; - } - - /** - * Indicates that the status check should use HTTPS. - * - * @return this - */ - public HttpWaitStrategy usingTls() { - delegateStrategy.usingTls(); - return this; - } - - /** - * Authenticate with HTTP Basic Authorization credentials. - * - * @param username the username - * @param password the password - * @return this - */ - public HttpWaitStrategy withBasicCredentials(String username, String password) { - delegateStrategy.withBasicCredentials(username, password); - return this; - } - - /** - * Waits for the response to pass the given predicate - * @param responsePredicate The predicate to test the response against - * @return this - */ - public HttpWaitStrategy forResponsePredicate(Predicate responsePredicate) { - delegateStrategy.forResponsePredicate(responsePredicate); - return this; - } - - @Override - protected void waitUntilReady() { - delegateStrategy.waitUntilReady(this.waitStrategyTarget); - } -} diff --git a/core/src/main/java/org/testcontainers/containers/wait/LogMessageWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/LogMessageWaitStrategy.java deleted file mode 100644 index a21b6b66a59..00000000000 --- a/core/src/main/java/org/testcontainers/containers/wait/LogMessageWaitStrategy.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.testcontainers.containers.wait; - -import org.testcontainers.containers.GenericContainer; - -/** - * Waits until containers logs expected content. - * - * @deprecated Use {@link org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy} - */ -@Deprecated -public class LogMessageWaitStrategy extends GenericContainer.AbstractWaitStrategy { - - private org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy delegateWaitStrategy = new org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy(); - - @Override - protected void waitUntilReady() { - delegateWaitStrategy.waitUntilReady(this.waitStrategyTarget); - } - - public LogMessageWaitStrategy withRegEx(String regEx) { - delegateWaitStrategy.withRegEx(regEx); - return this; - } - - public LogMessageWaitStrategy withTimes(int times) { - delegateWaitStrategy.withTimes(times); - return this; - } -} diff --git a/core/src/main/java/org/testcontainers/containers/wait/Wait.java b/core/src/main/java/org/testcontainers/containers/wait/Wait.java deleted file mode 100644 index 66d055395c5..00000000000 --- a/core/src/main/java/org/testcontainers/containers/wait/Wait.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.testcontainers.containers.wait; - -import java.net.HttpURLConnection; - -/** - * Convenience class with logic for building common {@link WaitStrategy} instances. - * - * @author Pete Cornish {@literal } - * - * @deprecated Use {@link org.testcontainers.containers.wait.strategy.Wait} - */ -@Deprecated -public class Wait { - /** - * Convenience method to return the default WaitStrategy. - * - * @return a WaitStrategy - */ - public static WaitStrategy defaultWaitStrategy() { - return forListeningPort(); - } - - /** - * Convenience method to return a WaitStrategy for an exposed or mapped port. - * - * @return the WaitStrategy - * @see HostPortWaitStrategy - */ - public static HostPortWaitStrategy forListeningPort() { - return new HostPortWaitStrategy(); - } - - /** - * Convenience method to return a WaitStrategy for an HTTP endpoint. - * - * @param path the path to check - * @return the WaitStrategy - * @see HttpWaitStrategy - */ - public static HttpWaitStrategy forHttp(String path) { - return new HttpWaitStrategy() - .forPath(path); - } - - /** - * Convenience method to return a WaitStrategy for an HTTPS endpoint. - * - * @param path the path to check - * @return the WaitStrategy - * @see HttpWaitStrategy - */ - public static HttpWaitStrategy forHttps(String path) { - return forHttp(path) - .usingTls(); - } - - /** - * Convenience method to return a WaitStrategy for log messages. - * - * @param regex the regex pattern to check for - * @param times the number of times the pattern is expected - * @return LogMessageWaitStrategy - */ - public static LogMessageWaitStrategy forLogMessage(String regex, int times) { - return new LogMessageWaitStrategy().withRegEx(regex).withTimes(times); - } -} diff --git a/core/src/main/java/org/testcontainers/containers/wait/WaitAllStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/WaitAllStrategy.java deleted file mode 100644 index 7e3032cd76d..00000000000 --- a/core/src/main/java/org/testcontainers/containers/wait/WaitAllStrategy.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.testcontainers.containers.wait; - -import org.rnorth.ducttape.timeouts.Timeouts; -import org.testcontainers.containers.GenericContainer; - -import java.time.Duration; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -/** - * Wait strategy that waits for a number of other strategies to pass in series. - * - * @deprecated Use {@link org.testcontainers.containers.wait.strategy.WaitAllStrategy} - */ -@Deprecated -public class WaitAllStrategy implements WaitStrategy { - - private final List strategies = new ArrayList<>(); - private Duration timeout = Duration.ofSeconds(30); - - @Override - public void waitUntilReady(GenericContainer container) { - Timeouts.doWithTimeout((int) timeout.toMillis(), TimeUnit.MILLISECONDS, () -> { - for (WaitStrategy strategy : strategies) { - strategy.waitUntilReady(container); - } - }); - } - - public WaitAllStrategy withStrategy(WaitStrategy strategy) { - this.strategies.add(strategy); - return this; - } - - @Override - public WaitStrategy withStartupTimeout(Duration startupTimeout) { - this.timeout = startupTimeout; - return this; - } -} diff --git a/core/src/main/java/org/testcontainers/containers/wait/WaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/WaitStrategy.java deleted file mode 100644 index ee2c14d90ae..00000000000 --- a/core/src/main/java/org/testcontainers/containers/wait/WaitStrategy.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.testcontainers.containers.wait; - -import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.wait.strategy.WaitStrategyTarget; - -import java.time.Duration; - -/** - * Approach to determine whether a container is ready. - * - * @author Pete Cornish {@literal } - * - * @deprecated Use {@link org.testcontainers.containers.wait.strategy.WaitStrategy} - */ -@Deprecated -public interface WaitStrategy extends org.testcontainers.containers.wait.strategy.WaitStrategy { - /** - * Wait until the container has started. - * - * @param container the container for which to wait - */ - default void waitUntilReady(GenericContainer container) { - this.waitUntilReady((WaitStrategyTarget)container); - } - - /** - * @param startupTimeout the duration for which to wait - * @return this - */ - WaitStrategy withStartupTimeout(Duration startupTimeout); - - /** - * {@inheritDoc} - */ - default void waitUntilReady(WaitStrategyTarget waitStrategyTarget) { - //default method for backwards compatibility - } -} diff --git a/core/src/main/java/org/testcontainers/dockerclient/AuditLoggingDockerClient.java b/core/src/main/java/org/testcontainers/dockerclient/AuditLoggingDockerClient.java index 891709e3a0c..2dc88cad313 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/AuditLoggingDockerClient.java +++ b/core/src/main/java/org/testcontainers/dockerclient/AuditLoggingDockerClient.java @@ -16,12 +16,10 @@ * Wrapper for {@link DockerClient} to facilitate 'audit logging' of potentially destruction actions using * {@link org.testcontainers.utility.AuditLogger}. * - * @deprecated should not be used publicly */ @Slf4j @SuppressWarnings("unchecked") -@Deprecated -public class AuditLoggingDockerClient implements DockerClient { +class AuditLoggingDockerClient implements DockerClient { @Delegate(excludes = InterceptedMethods.class) private final DockerClient wrappedClient; diff --git a/core/src/main/java/org/testcontainers/dockerclient/auth/AuthDelegatingDockerClientConfig.java b/core/src/main/java/org/testcontainers/dockerclient/AuthDelegatingDockerClientConfig.java similarity index 90% rename from core/src/main/java/org/testcontainers/dockerclient/auth/AuthDelegatingDockerClientConfig.java rename to core/src/main/java/org/testcontainers/dockerclient/AuthDelegatingDockerClientConfig.java index a68605fdfda..c78a13c99ac 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/auth/AuthDelegatingDockerClientConfig.java +++ b/core/src/main/java/org/testcontainers/dockerclient/AuthDelegatingDockerClientConfig.java @@ -1,4 +1,4 @@ -package org.testcontainers.dockerclient.auth; +package org.testcontainers.dockerclient; import com.github.dockerjava.api.model.AuthConfig; import com.github.dockerjava.core.DockerClientConfig; @@ -15,11 +15,10 @@ * to try and obtain auth credentials, but after that {@link RegistryAuthLocator} will be * used to try and improve the auth resolution (e.g. using credential helpers). * - * @deprecated should not be used publicly, to be moved to docker-java + * TODO move to docker-java */ @Slf4j -@Deprecated -public class AuthDelegatingDockerClientConfig implements DockerClientConfig { +class AuthDelegatingDockerClientConfig implements DockerClientConfig { @Delegate(excludes = DelegateExclusions.class) private DockerClientConfig delegate; diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java index 1060d663d65..b494eb087f6 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java @@ -1,6 +1,5 @@ package org.testcontainers.dockerclient; -import com.github.dockerjava.core.DockerClientConfig; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; @@ -18,10 +17,6 @@ public class DockerClientConfigUtils { // See https://github.com/docker/docker/blob/a9fa38b1edf30b23cae3eade0be48b3d4b1de14b/daemon/initlayer/setup_unix.go#L25 public static final boolean IN_A_CONTAINER = new File("/.dockerenv").exists(); - @Deprecated - @Getter(lazy = true) - private static final Optional detectedDockerHostIp = IN_A_CONTAINER ? getDefaultGateway() : Optional.empty(); - @Getter(lazy = true) private static final Optional defaultGateway = Optional .ofNullable(DockerClientFactory.instance().runInsideDocker( @@ -45,13 +40,9 @@ public class DockerClientConfigUtils { .filter(StringUtils::isNotBlank); /** - * Use {@link DockerClientFactory#dockerHostIpAddress()} + * @deprecated use {@link DockerClientProviderStrategy#getDockerHostIpAddress()} */ @Deprecated - public static String getDockerHostIpAddress(DockerClientConfig config) { - return getDockerHostIpAddress(config.getDockerHost()); - } - public static String getDockerHostIpAddress(URI dockerHost) { switch (dockerHost.getScheme()) { case "http": diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index c305d56389c..f0af6d51ae2 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -16,7 +16,6 @@ import org.rnorth.ducttape.unreliables.Unreliables; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.testcontainers.dockerclient.auth.AuthDelegatingDockerClientConfig; import org.testcontainers.utility.TestcontainersConfiguration; import java.net.URI; diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/NamedPipeSocketFactory.java b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/NamedPipeSocketFactory.java deleted file mode 100644 index d26e2977318..00000000000 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/NamedPipeSocketFactory.java +++ /dev/null @@ -1,134 +0,0 @@ -package org.testcontainers.dockerclient.transport.okhttp; - -import com.sun.jna.platform.win32.Kernel32; -import lombok.EqualsAndHashCode; -import lombok.SneakyThrows; -import lombok.Value; -import org.rnorth.ducttape.unreliables.Unreliables; - -import javax.net.SocketFactory; -import java.io.*; -import java.net.InetAddress; -import java.net.Socket; -import java.net.SocketAddress; -import java.util.concurrent.TimeUnit; - -/** - * @deprecated use com.github.docker-java:docker-java-transport-okhttp - */ -@Deprecated -@Value -@EqualsAndHashCode(callSuper = false) -public class NamedPipeSocketFactory extends SocketFactory { - - String socketFileName; - - @Override - @SneakyThrows - public Socket createSocket() { - return new Socket() { - - RandomAccessFile file; - InputStream is; - OutputStream os; - - @Override - public void close() throws IOException { - if (file != null) { - file.close(); - file = null; - } - } - - @Override - public void connect(SocketAddress endpoint) { - connect(endpoint, 0); - } - - @Override - public void connect(SocketAddress endpoint, int timeout) { - file = Unreliables.retryUntilSuccess(Math.max(timeout, 10_000), TimeUnit.MILLISECONDS, () -> { - try { - return new RandomAccessFile(socketFileName, "rw"); - } catch (FileNotFoundException e) { - Kernel32.INSTANCE.WaitNamedPipe(socketFileName, 100); - throw e; - } - }); - - is = new InputStream() { - @Override - public int read(byte[] bytes, int off, int len) throws IOException { - if (OkHttpInvocationBuilder.CLOSING.get()) { - return 0; - } - return file.read(bytes, off, len); - } - - @Override - public int read() throws IOException { - if (OkHttpInvocationBuilder.CLOSING.get()) { - return 0; - } - return file.read(); - } - - @Override - public int read(byte[] bytes) throws IOException { - if (OkHttpInvocationBuilder.CLOSING.get()) { - return 0; - } - return file.read(bytes); - } - }; - - os = new OutputStream() { - @Override - public void write(byte[] bytes, int off, int len) throws IOException { - file.write(bytes, off, len); - } - - @Override - public void write(int value) throws IOException { - file.write(value); - } - - @Override - public void write(byte[] bytes) throws IOException { - file.write(bytes); - } - }; - } - - @Override - public InputStream getInputStream() { - return is; - } - - @Override - public OutputStream getOutputStream() { - return os; - } - }; - } - - @Override - public Socket createSocket(String s, int i) { - throw new UnsupportedOperationException(); - } - - @Override - public Socket createSocket(String s, int i, InetAddress inetAddress, int i1) { - throw new UnsupportedOperationException(); - } - - @Override - public Socket createSocket(InetAddress inetAddress, int i) { - throw new UnsupportedOperationException(); - } - - @Override - public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddress1, int i1) { - throw new UnsupportedOperationException(); - } -} diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpDockerCmdExecFactory.java b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpDockerCmdExecFactory.java deleted file mode 100644 index 4da568834d5..00000000000 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpDockerCmdExecFactory.java +++ /dev/null @@ -1,162 +0,0 @@ -package org.testcontainers.dockerclient.transport.okhttp; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.dockerjava.api.command.PingCmd; -import com.github.dockerjava.core.AbstractDockerCmdExecFactory; -import com.github.dockerjava.core.DockerClientConfig; -import com.github.dockerjava.core.SSLConfig; -import com.github.dockerjava.core.WebTarget; -import com.github.dockerjava.core.exec.PingCmdExec; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.MultimapBuilder; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; -import okhttp3.ConnectionPool; -import okhttp3.Dns; -import okhttp3.HttpUrl; -import okhttp3.OkHttpClient; -import org.apache.commons.io.IOUtils; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.X509TrustManager; -import java.io.IOException; -import java.net.InetAddress; -import java.net.URI; -import java.security.cert.X509Certificate; -import java.util.Collections; -import java.util.concurrent.TimeUnit; - -/** - * @deprecated use com.github.docker-java:docker-java-transport-okhttp - */ -@Deprecated -@Slf4j -public class OkHttpDockerCmdExecFactory extends AbstractDockerCmdExecFactory { - - private static final String SOCKET_SUFFIX = ".socket"; - - private ObjectMapper objectMapper; - - private OkHttpClient okHttpClient; - - private HttpUrl baseUrl; - - @Override - @SneakyThrows - public void init(DockerClientConfig dockerClientConfig) { - super.init(dockerClientConfig); - - OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder() - .readTimeout(0, TimeUnit.SECONDS) - .retryOnConnectionFailure(true); - - URI dockerHost = dockerClientConfig.getDockerHost(); - switch (dockerHost.getScheme()) { - case "unix": - case "npipe": - String socketPath = dockerHost.getPath(); - - if ("unix".equals(dockerHost.getScheme())) { - clientBuilder - .socketFactory(new UnixSocketFactory(socketPath)); - } else { - clientBuilder - .socketFactory(new NamedPipeSocketFactory(socketPath)); - } - - clientBuilder - // Disable pooling - .connectionPool(new ConnectionPool(0, 1, TimeUnit.SECONDS)) - .dns(hostname -> { - if (hostname.endsWith(SOCKET_SUFFIX)) { - return Collections.singletonList(InetAddress.getByAddress(hostname, new byte[]{0, 0, 0, 0})); - } else { - return Dns.SYSTEM.lookup(hostname); - } - }); - default: - } - - SSLConfig sslConfig = dockerClientConfig.getSSLConfig(); - if (sslConfig != null) { - SSLContext sslContext = sslConfig.getSSLContext(); - if (sslContext != null) { - clientBuilder - .sslSocketFactory(sslContext.getSocketFactory(), new TrustAllX509TrustManager()); - } - } - - okHttpClient = clientBuilder.build(); - - HttpUrl.Builder baseUrlBuilder; - - switch (dockerHost.getScheme()) { - case "unix": - case "npipe": - baseUrlBuilder = new HttpUrl.Builder() - .scheme("http") - .host("docker" + SOCKET_SUFFIX); - break; - case "tcp": - baseUrlBuilder = new HttpUrl.Builder() - .scheme(sslConfig != null && sslConfig.getSSLContext() != null ? "https" : "http") - .host(dockerHost.getHost()) - .port(dockerHost.getPort()); - break; - default: - baseUrlBuilder = HttpUrl.get(dockerHost.toString()).newBuilder(); - } - baseUrl = baseUrlBuilder.build(); - - objectMapper = dockerClientConfig.getObjectMapper(); - } - - @Override - protected OkHttpWebTarget getBaseResource() { - return new OkHttpWebTarget( - objectMapper, - okHttpClient, - baseUrl, - ImmutableList.of(), - MultimapBuilder.hashKeys().hashSetValues().build() - ); - } - - @Override - public PingCmd.Exec createPingCmdExec() { - return new PingCmdExec(getBaseResource(), getDockerClientConfig()) { - - @Override - protected Void execute(PingCmd command) { - WebTarget webResource = getBaseResource().path("/_ping"); - - // TODO contribute to docker-java, make it close the stream - IOUtils.closeQuietly(webResource.request().get()); - - return null; - } - }; - } - - @Override - public void close() throws IOException { - - } - - private static class TrustAllX509TrustManager implements X509TrustManager { - @Override - public void checkClientTrusted(X509Certificate[] x509Certificates, String s) { - - } - - @Override - public void checkServerTrusted(X509Certificate[] x509Certificates, String s) { - - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - return new X509Certificate[0]; - } - } -} diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java deleted file mode 100644 index cada10328ec..00000000000 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpInvocationBuilder.java +++ /dev/null @@ -1,414 +0,0 @@ -package org.testcontainers.dockerclient.transport.okhttp; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.dockerjava.api.async.ResultCallback; -import com.github.dockerjava.api.exception.BadRequestException; -import com.github.dockerjava.api.exception.ConflictException; -import com.github.dockerjava.api.exception.DockerException; -import com.github.dockerjava.api.exception.InternalServerErrorException; -import com.github.dockerjava.api.exception.NotAcceptableException; -import com.github.dockerjava.api.exception.NotFoundException; -import com.github.dockerjava.api.exception.NotModifiedException; -import com.github.dockerjava.api.exception.UnauthorizedException; -import com.github.dockerjava.api.model.Frame; -import com.github.dockerjava.api.model.StreamType; -import com.github.dockerjava.core.InvocationBuilder; -import lombok.AccessLevel; -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; -import lombok.experimental.FieldDefaults; -import lombok.extern.slf4j.Slf4j; -import okhttp3.HttpUrl; -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import okhttp3.internal.connection.RealConnection; -import okio.*; -import org.jetbrains.annotations.Nullable; -import org.testcontainers.DockerClientFactory; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.Field; -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.Objects; -import java.util.function.Consumer; - -@Slf4j -@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) -class OkHttpInvocationBuilder implements InvocationBuilder { - - static final ThreadLocal CLOSING = ThreadLocal.withInitial(() -> false); - - ObjectMapper objectMapper; - - OkHttpClient okHttpClient; - - Request.Builder requestBuilder; - - public OkHttpInvocationBuilder(ObjectMapper objectMapper, OkHttpClient okHttpClient, HttpUrl httpUrl) { - this.objectMapper = objectMapper; - this.okHttpClient = okHttpClient; - - requestBuilder = new Request.Builder() - .url(httpUrl); - } - - @Override - public OkHttpInvocationBuilder accept(com.github.dockerjava.core.MediaType mediaType) { - return header("accept", mediaType.getMediaType()); - } - - @Override - public OkHttpInvocationBuilder header(String name, String value) { - requestBuilder.header(name, value); - return this; - } - - @Override - @SneakyThrows - public void delete() { - Request request = requestBuilder - .delete() - .build(); - - execute(request).close(); - } - - @Override - @SneakyThrows - public void get(ResultCallback resultCallback) { - Request request = requestBuilder - .get() - .build(); - - executeAndStream( - request, - resultCallback, - new FramedSink(resultCallback) - ); - } - - @Override - @SneakyThrows(IOException.class) - public T get(TypeReference typeReference) { - try (InputStream inputStream = get()) { - return objectMapper.readValue(inputStream, typeReference); - } - } - - @Override - public void get(TypeReference typeReference, ResultCallback resultCallback) { - Request request = requestBuilder - .get() - .build(); - - executeAndStream( - request, - resultCallback, - new JsonSink<>(objectMapper, typeReference, resultCallback) - ); - } - - @Override - @SneakyThrows - public InputStream post(Object entity) { - Request request = requestBuilder - .post(RequestBody.create(MediaType.parse("application/json"), objectMapper.writeValueAsBytes(entity))) - .build(); - - return execute(request).body().byteStream(); - } - - @Override - @SneakyThrows - public T post(Object entity, TypeReference typeReference) { - Request request = requestBuilder - .post(RequestBody.create(MediaType.parse("application/json"), objectMapper.writeValueAsBytes(entity))) - .build(); - - try (Response response = execute(request)) { - String inputStream = response.body().string(); - return objectMapper.readValue(inputStream, typeReference); - } - } - - @Override - @SneakyThrows(JsonProcessingException.class) - public void post(Object entity, TypeReference typeReference, ResultCallback resultCallback) { - post(typeReference, resultCallback, new ByteArrayInputStream(objectMapper.writeValueAsBytes(entity))); - } - - @Override - @SneakyThrows(IOException.class) - public T post(TypeReference typeReference, InputStream body) { - try (InputStream inputStream = post(body)) { - return objectMapper.readValue(inputStream, typeReference); - } - } - - @Override - @SneakyThrows - public void post(Object entity, InputStream stdin, ResultCallback resultCallback) { - Request request = requestBuilder - .post(RequestBody.create(MediaType.parse("application/json"), objectMapper.writeValueAsBytes(entity))) - .build(); - - OkHttpClient okHttpClient = this.okHttpClient; - - if (stdin != null) { - // FIXME there must be a better way of handling it - okHttpClient = okHttpClient.newBuilder() - .addNetworkInterceptor(chain -> { - Response response = chain.proceed(chain.request()); - if (response.isSuccessful()) { - Thread thread = new Thread() { - @Override - @SneakyThrows - public void run() { - Field sinkField = RealConnection.class.getDeclaredField("sink"); - sinkField.setAccessible(true); - - try ( - BufferedSink sink = (BufferedSink) sinkField.get(chain.connection()); - Source source = Okio.source(stdin); - ) { - sink.writeAll(source); - } - } - }; - thread.start(); - } - return response; - }) - .build(); - } - - executeAndStream( - okHttpClient, - request, - resultCallback, - new FramedSink(resultCallback) - ); - } - - @Override - public void post(TypeReference typeReference, ResultCallback resultCallback, InputStream body) { - Request request = requestBuilder - .post(toRequestBody(body, null)) - .build(); - - executeAndStream( - request, - resultCallback, - new JsonSink<>(objectMapper, typeReference, resultCallback) - ); - } - - @Override - @SneakyThrows - public void postStream(InputStream body) { - Request request = requestBuilder - .post(toRequestBody(body, null)) - .build(); - - execute(request).close(); - } - - @Override - @SneakyThrows - public InputStream get() { - Request request = requestBuilder - .get() - .build(); - - return execute(request).body().byteStream(); - } - - @Override - @SneakyThrows - public void put(InputStream body, com.github.dockerjava.core.MediaType mediaType) { - Request request = requestBuilder - .put(toRequestBody(body, mediaType.toString())) - .build(); - - execute(request).close(); - } - - protected RequestBody toRequestBody(InputStream body, @Nullable String mediaType) { - return new RequestBody() { - @Nullable - @Override - public MediaType contentType() { - if (mediaType == null) { - return null; - } - return MediaType.parse(mediaType); - } - - @Override - public void writeTo(BufferedSink sink) throws IOException { - try (Source source = Okio.source(body)) { - sink.writeAll(source); - } - } - }; - } - - protected Response execute(Request request) { - return execute(okHttpClient, request); - } - - @SneakyThrows(IOException.class) - protected Response execute(OkHttpClient okHttpClient, Request request) { - Response response = okHttpClient.newCall(request).execute(); - if (!response.isSuccessful()) { - String body = response.body().string(); - switch (response.code()) { - case 304: - throw new NotModifiedException(body); - case 400: - throw new BadRequestException(body); - case 401: - throw new UnauthorizedException(body); - case 404: - throw new NotFoundException(body); - case 406: - throw new NotAcceptableException(body); - case 409: - throw new ConflictException(body); - case 500: - throw new InternalServerErrorException(body); - default: - throw new DockerException(body, response.code()); - } - } else { - return response; - } - } - - protected void executeAndStream(Request request, ResultCallback callback, Consumer sourceConsumer) { - executeAndStream(okHttpClient, request, callback, sourceConsumer); - } - - protected void executeAndStream(OkHttpClient okHttpClient, Request request, ResultCallback callback, Consumer sourceConsumer) { - Thread thread = new Thread(DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, () -> { - try ( - Response response = execute(okHttpClient, request.newBuilder().tag("streaming").build()); - BufferedSource source = response.body().source(); - ) { - callback.onStart(() -> { - boolean previous = CLOSING.get(); - CLOSING.set(true); - try { - response.close(); - } finally { - CLOSING.set(previous); - } - }); - - sourceConsumer.accept(source); - callback.onComplete(); - } catch (Exception e) { - callback.onError(e); - } - }, "tc-okhttp-stream-" + Objects.hashCode(request)); - thread.setDaemon(true); - - thread.start(); - } - - @RequiredArgsConstructor - @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) - private static class JsonSink implements Consumer { - - ObjectMapper objectMapper; - - TypeReference typeReference; - - ResultCallback resultCallback; - - @Override - public void accept(BufferedSource source) { - try { - while (true) { - String line = source.readUtf8Line(); - if (line == null) { - break; - } - - resultCallback.onNext(objectMapper.readValue(line, typeReference)); - } - } catch (Exception e) { - resultCallback.onError(e); - } - } - } - - @RequiredArgsConstructor - @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) - private static class FramedSink implements Consumer { - - private static final int HEADER_SIZE = 8; - - ResultCallback resultCallback; - - @Override - public void accept(BufferedSource source) { - try { - while (!source.exhausted()) { - // See https://docs.docker.com/engine/api/v1.37/#operation/ContainerAttach - // [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4}[]byte{OUTPUT} - - if (!source.request(HEADER_SIZE)) { - return; - } - byte[] bytes = source.readByteArray(HEADER_SIZE); - - StreamType streamType = streamType(bytes[0]); - - if (streamType == StreamType.RAW) { - resultCallback.onNext(new Frame(StreamType.RAW, bytes)); - byte[] buffer = new byte[1024]; - while (!source.exhausted()) { - int readBytes = source.read(buffer); - if (readBytes != -1) { - resultCallback.onNext(new Frame(StreamType.RAW, Arrays.copyOf(buffer, readBytes))); - } - } - return; - } - - int payloadSize = ByteBuffer.wrap(bytes, 4, 4).getInt(); - if (!source.request(payloadSize)) { - return; - } - byte[] payload = source.readByteArray(payloadSize); - - resultCallback.onNext(new Frame(streamType, payload)); - } - } catch (Exception e) { - resultCallback.onError(e); - } - } - - private static StreamType streamType(byte streamType) { - switch (streamType) { - case 0: - return StreamType.STDIN; - case 1: - return StreamType.STDOUT; - case 2: - return StreamType.STDERR; - default: - return StreamType.RAW; - } - } - } -} diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpWebTarget.java b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpWebTarget.java deleted file mode 100644 index 9c360522d20..00000000000 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/OkHttpWebTarget.java +++ /dev/null @@ -1,113 +0,0 @@ -package org.testcontainers.dockerclient.transport.okhttp; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.dockerjava.core.InvocationBuilder; -import com.github.dockerjava.core.WebTarget; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.SetMultimap; -import lombok.SneakyThrows; -import lombok.Value; -import lombok.experimental.Wither; -import okhttp3.HttpUrl; -import okhttp3.OkHttpClient; -import org.apache.commons.lang.StringUtils; - -import java.util.Collection; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; - -@Wither -@Value -class OkHttpWebTarget implements WebTarget { - - ObjectMapper objectMapper; - - OkHttpClient okHttpClient; - - HttpUrl baseUrl; - - ImmutableList path; - - SetMultimap queryParams; - - @Override - @SneakyThrows - public InvocationBuilder request() { - String resource = StringUtils.join(path, "/"); - - if (!resource.startsWith("/")) { - resource = "/" + resource; - } - - HttpUrl.Builder baseUrlBuilder = baseUrl.newBuilder() - .encodedPath(resource); - - for (Map.Entry> queryParamEntry : queryParams.asMap().entrySet()) { - String key = queryParamEntry.getKey(); - for (String paramValue : queryParamEntry.getValue()) { - baseUrlBuilder.addQueryParameter(key, paramValue); - } - } - - return new OkHttpInvocationBuilder( - objectMapper, - okHttpClient, - baseUrlBuilder.build() - ); - } - - @Override - public OkHttpWebTarget path(String... components) { - return this.withPath( - ImmutableList.builder() - .addAll(path) - .add(components) - .build() - ); - } - - @Override - public OkHttpWebTarget resolveTemplate(String name, Object value) { - ImmutableList.Builder newPath = ImmutableList.builder(); - for (String component : path) { - component = component.replaceAll("\\{" + name + "\\}", value.toString()); - newPath.add(component); - } - return this.withPath(newPath.build()); - } - - @Override - public OkHttpWebTarget queryParam(String name, Object value) { - if (value == null) { - return this; - } - - SetMultimap newQueryParams = HashMultimap.create(queryParams); - newQueryParams.put(name, value.toString()); - - return this.withQueryParams(newQueryParams); - } - - @Override - public OkHttpWebTarget queryParamsSet(String name, Set values) { - SetMultimap newQueryParams = HashMultimap.create(queryParams); - newQueryParams.replaceValues(name, values.stream().filter(Objects::nonNull).map(Object::toString).collect(Collectors.toSet())); - - return this.withQueryParams(newQueryParams); - } - - @Override - @SneakyThrows(JsonProcessingException.class) - public OkHttpWebTarget queryParamsJsonMap(String name, Map values) { - if (values == null || values.isEmpty()) { - return this; - } - - // when param value is JSON string - return queryParam(name, objectMapper.writeValueAsString(values)); - } -} diff --git a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/UnixSocketFactory.java b/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/UnixSocketFactory.java deleted file mode 100644 index d1e55f3e647..00000000000 --- a/core/src/main/java/org/testcontainers/dockerclient/transport/okhttp/UnixSocketFactory.java +++ /dev/null @@ -1,92 +0,0 @@ -package org.testcontainers.dockerclient.transport.okhttp; - -import lombok.EqualsAndHashCode; -import lombok.SneakyThrows; -import lombok.Value; -import org.scalasbt.ipcsocket.UnixDomainSocket; - -import javax.net.SocketFactory; -import java.io.FilterInputStream; -import java.io.FilterOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.InetAddress; -import java.net.Socket; -import java.net.SocketAddress; - -/** - * @deprecated use com.github.docker-java:docker-java-transport-okhttp - */ -@Deprecated -@Value -@EqualsAndHashCode(callSuper = false) -public class UnixSocketFactory extends SocketFactory { - - String socketPath; - - @Override - @SneakyThrows - public Socket createSocket() { - return new UnixDomainSocket(socketPath) { - @Override - public void connect(SocketAddress endpoint, int timeout) throws IOException { - // Do nothing since it's not "connectable" - } - - @Override - public InputStream getInputStream() { - return new FilterInputStream(super.getInputStream()) { - @Override - public void close() throws IOException { - shutdownInput(); - } - - @Override - public int read(byte[] b, int off, int len) throws IOException { - if (OkHttpInvocationBuilder.CLOSING.get()) { - return 0; - } - return super.read(b, off, len); - } - }; - } - - @Override - public OutputStream getOutputStream() { - return new FilterOutputStream(super.getOutputStream()) { - - @Override - public void write(byte[] b, int off, int len) throws IOException { - out.write(b, off, len); - } - - @Override - public void close() throws IOException { - shutdownOutput(); - } - }; - } - }; - } - - @Override - public Socket createSocket(String s, int i) { - throw new UnsupportedOperationException(); - } - - @Override - public Socket createSocket(String s, int i, InetAddress inetAddress, int i1) { - throw new UnsupportedOperationException(); - } - - @Override - public Socket createSocket(InetAddress inetAddress, int i) { - throw new UnsupportedOperationException(); - } - - @Override - public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddress1, int i1) { - throw new UnsupportedOperationException(); - } -} diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index 2f668d80426..4d44cbc3842 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -70,11 +70,6 @@ private ResourceReaper() { dockerClient = DockerClientFactory.instance().client(); } - @Deprecated - public static String start(String hostIpAddress, DockerClient client, boolean withDummyMount) { - return start(hostIpAddress, client); - } - @SneakyThrows(InterruptedException.class) public static String start(String hostIpAddress, DockerClient client) { String ryukImage = TestcontainersConfiguration.getInstance().getRyukImage(); diff --git a/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java b/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java index a4e8cceb558..113d0a76ec0 100644 --- a/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java +++ b/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java @@ -2,7 +2,7 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.traits.LinkableContainer; -import org.testcontainers.containers.wait.Wait; +import org.testcontainers.containers.wait.strategy.Wait; /** * A Redmine container. diff --git a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java index 10c0e1835c2..dd48f32bece 100644 --- a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java +++ b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java @@ -1,6 +1,6 @@ package org.testcontainers.containers; -import org.testcontainers.containers.wait.HttpWaitStrategy; +import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import java.time.Duration; diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index 6ba7390878e..1c814044023 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -1,13 +1,12 @@ package org.testcontainers.containers; import org.jetbrains.annotations.NotNull; -import org.testcontainers.containers.wait.LogMessageWaitStrategy; +import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import java.time.Duration; import java.util.HashSet; import java.util.Set; -import static java.lang.String.format; import static java.time.temporal.ChronoUnit.SECONDS; /** diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index 397f6c4a2cf..16127171c59 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -19,11 +19,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testcontainers.containers.traits.LinkableContainer; -import org.testcontainers.containers.traits.VncService; -import org.testcontainers.containers.wait.HostPortWaitStrategy; -import org.testcontainers.containers.wait.LogMessageWaitStrategy; -import org.testcontainers.containers.wait.WaitAllStrategy; -import org.testcontainers.containers.wait.WaitStrategy; +import org.testcontainers.containers.wait.strategy.HostPortWaitStrategy; +import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; +import org.testcontainers.containers.wait.strategy.WaitAllStrategy; +import org.testcontainers.containers.wait.strategy.WaitStrategy; import org.testcontainers.lifecycle.TestDescription; import org.testcontainers.lifecycle.TestLifecycleAware; @@ -44,7 +43,7 @@ *

* The container should expose Selenium remote control protocol and VNC. */ -public class BrowserWebDriverContainer> extends GenericContainer implements VncService, LinkableContainer, TestLifecycleAware { +public class BrowserWebDriverContainer> extends GenericContainer implements LinkableContainer, TestLifecycleAware { private static final String CHROME_IMAGE = "selenium/standalone-chrome-debug:%s"; private static final String FIREFOX_IMAGE = "selenium/standalone-firefox-debug:%s"; @@ -214,17 +213,16 @@ public URL getSeleniumAddress() { } } - @Override public String getVncAddress() { return "vnc://vnc:secret@" + getHost() + ":" + getMappedPort(VNC_PORT); } - @Override + @Deprecated public String getPassword() { return DEFAULT_PASSWORD; } - @Override + @Deprecated public int getPort() { return VNC_PORT; } From ddae79c2c0ae65d3cf2e898c5aeea9fcf3a55ecd Mon Sep 17 00:00:00 2001 From: Kyle Aure Date: Fri, 5 Jun 2020 16:22:34 -0500 Subject: [PATCH 1428/5615] Add acceptLicense method to MSSQLServerContainer (#2085) Co-authored-by: Richard North --- docs/modules/databases/mssqlserver.md | 5 +++-- .../containers/MSSQLServerContainer.java | 18 +++++++++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/docs/modules/databases/mssqlserver.md b/docs/modules/databases/mssqlserver.md index 9e0bec25dad..29c5435be32 100644 --- a/docs/modules/databases/mssqlserver.md +++ b/docs/modules/databases/mssqlserver.md @@ -10,7 +10,8 @@ Running MS SQL Server as a stand-in for in a test: public class SomeTest { @Rule - public MSSQLServerContainer mssqlserver = new MSSQLServerContainer(); + public MSSQLServerContainer mssqlserver = new MSSQLServerContainer() + .acceptLicense(); @Test public void someTestMethod() { @@ -20,7 +21,7 @@ public class SomeTest { ``` !!! warning "EULA Acceptance" - Due to licencing restrictions you are required to accept an EULA for this container image. To indicate that you accept the MS SQL Server image EULA, Please place a file at the root of the classpath named `container-license-acceptance.txt`, e.g. at `src/test/resources/container-license-acceptance.txt`. This file should contain the line: `mcr.microsoft.com/mssql/server:2017-CU12` (or, if you are overriding the docker image name/tag, update accordingly). + Due to licencing restrictions you are required to accept an EULA for this container image. To indicate that you accept the MS SQL Server image EULA, call the `acceptLicense()` method, or place a file at the root of the classpath named `container-license-acceptance.txt`, e.g. at `src/test/resources/container-license-acceptance.txt`. This file should contain the line: `mcr.microsoft.com/mssql/server:2017-CU12` (or, if you are overriding the docker image name/tag, update accordingly). Please see the [`microsoft-mssql-server` image documentation](https://hub.docker.com/_/microsoft-mssql-server#environment-variables) for a link to the EULA document. diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java index b500e6fddf0..7550f820d51 100644 --- a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java @@ -50,11 +50,24 @@ protected Integer getLivenessCheckPort() { @Override protected void configure() { - LicenseAcceptance.assertLicenseAccepted(this.getDockerImageName()); - addEnv("ACCEPT_EULA", "Y"); + // If license was not accepted programatically, check if it was accepted via resource file + if (!getEnvMap().containsKey("ACCEPT_EULA")) { + LicenseAcceptance.assertLicenseAccepted(this.getDockerImageName()); + acceptLicense(); + } + addEnv("SA_PASSWORD", password); } + /** + * Accepts the license for the SQLServer container by setting the ACCEPT_EULA=Y + * variable as described at https://hub.docker.com/_/microsoft-mssql-server + */ + public SELF acceptLicense() { + addEnv("ACCEPT_EULA", "Y"); + return self(); + } + @Override public String getDriverClassName() { return "com.microsoft.sqlserver.jdbc.SQLServerDriver"; @@ -116,6 +129,5 @@ private void checkPasswordStrength(String password) { "or percent (%)." ); } - } } From 0035c311b8cd3dc342a092992efdf39d158264b6 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sat, 6 Jun 2020 09:35:01 +0200 Subject: [PATCH 1429/5615] Avoid connection leaks in `LogUtils` (#2834) --- .../wait/strategy/LogMessageWaitStrategy.java | 37 ++++++++++++++----- .../org/testcontainers/utility/LogUtils.java | 26 ++++++++----- 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java index bd164096300..9f7a0078f00 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java @@ -1,15 +1,21 @@ package org.testcontainers.containers.wait.strategy; +import com.github.dockerjava.api.command.LogContainerCmd; +import lombok.SneakyThrows; import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.ContainerLaunchException; +import org.testcontainers.containers.output.FrameConsumerResultCallback; import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.output.WaitingConsumer; -import org.testcontainers.utility.LogUtils; +import java.io.IOException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.Predicate; +import static org.testcontainers.containers.output.OutputFrame.OutputType.STDERR; +import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; + public class LogMessageWaitStrategy extends AbstractWaitStrategy { private String regEx; @@ -17,18 +23,31 @@ public class LogMessageWaitStrategy extends AbstractWaitStrategy { private int times = 1; @Override + @SneakyThrows(IOException.class) protected void waitUntilReady() { WaitingConsumer waitingConsumer = new WaitingConsumer(); - LogUtils.followOutput(DockerClientFactory.instance().client(), waitStrategyTarget.getContainerId(), waitingConsumer); - Predicate waitPredicate = outputFrame -> - // (?s) enables line terminator matching (equivalent to Pattern.DOTALL) - outputFrame.getUtf8String().matches("(?s)" + regEx); + LogContainerCmd cmd = DockerClientFactory.instance().client().logContainerCmd(waitStrategyTarget.getContainerId()) + .withFollowStream(true) + .withSince(0) + .withStdOut(true) + .withStdErr(true); + + try (FrameConsumerResultCallback callback = new FrameConsumerResultCallback()) { + callback.addConsumer(STDOUT, waitingConsumer); + callback.addConsumer(STDERR, waitingConsumer); + + cmd.exec(callback); + + Predicate waitPredicate = outputFrame -> + // (?s) enables line terminator matching (equivalent to Pattern.DOTALL) + outputFrame.getUtf8String().matches("(?s)" + regEx); - try { - waitingConsumer.waitUntil(waitPredicate, startupTimeout.getSeconds(), TimeUnit.SECONDS, times); - } catch (TimeoutException e) { - throw new ContainerLaunchException("Timed out waiting for log output matching '" + regEx + "'"); + try { + waitingConsumer.waitUntil(waitPredicate, startupTimeout.getSeconds(), TimeUnit.SECONDS, times); + } catch (TimeoutException e) { + throw new ContainerLaunchException("Timed out waiting for log output matching '" + regEx + "'"); + } } } diff --git a/core/src/main/java/org/testcontainers/utility/LogUtils.java b/core/src/main/java/org/testcontainers/utility/LogUtils.java index b51325cdda7..ce27387490f 100644 --- a/core/src/main/java/org/testcontainers/utility/LogUtils.java +++ b/core/src/main/java/org/testcontainers/utility/LogUtils.java @@ -2,12 +2,15 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.LogContainerCmd; +import lombok.SneakyThrows; import lombok.experimental.UtilityClass; import org.testcontainers.containers.output.FrameConsumerResultCallback; import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.output.ToStringConsumer; import org.testcontainers.containers.output.WaitingConsumer; +import java.io.Closeable; +import java.io.IOException; import java.util.function.Consumer; import static org.testcontainers.containers.output.OutputFrame.OutputType.STDERR; @@ -59,6 +62,7 @@ public void followOutput(DockerClient dockerClient, * @param types types of {@link OutputFrame} to receive * @return all previous output frames (stdout/stderr being separated by newline characters) */ + @SneakyThrows(IOException.class) public String getOutput(DockerClient dockerClient, String containerId, OutputFrame.OutputType... types) { @@ -73,17 +77,19 @@ public String getOutput(DockerClient dockerClient, final ToStringConsumer consumer = new ToStringConsumer(); final WaitingConsumer wait = new WaitingConsumer(); - attachConsumer(dockerClient, containerId, consumer.andThen(wait), false, types); - - wait.waitUntilEnd(); - return consumer.toUtf8String(); + try (Closeable closeable = attachConsumer(dockerClient, containerId, consumer.andThen(wait), false, types)) { + wait.waitUntilEnd(); + return consumer.toUtf8String(); + } } - private static void attachConsumer(DockerClient dockerClient, - String containerId, - Consumer consumer, - boolean followStream, - OutputFrame.OutputType... types) { + private static Closeable attachConsumer( + DockerClient dockerClient, + String containerId, + Consumer consumer, + boolean followStream, + OutputFrame.OutputType... types + ) { final LogContainerCmd cmd = dockerClient.logContainerCmd(containerId) .withFollowStream(followStream) @@ -96,6 +102,6 @@ private static void attachConsumer(DockerClient dockerClient, if (type == STDERR) cmd.withStdErr(true); } - cmd.exec(callback); + return cmd.exec(callback); } } From 54b3a1869468866a2f2e0b0a003f5a0abcc3f2b4 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 6 Jun 2020 08:46:11 +0100 Subject: [PATCH 1430/5615] Bump mongo-java-driver from 3.12.4 to 3.12.5 in /core (#2844) Bumps [mongo-java-driver](https://github.com/mongodb/mongo-java-driver) from 3.12.4 to 3.12.5. - [Release notes](https://github.com/mongodb/mongo-java-driver/releases) - [Commits](https://github.com/mongodb/mongo-java-driver/compare/r3.12.4...r3.12.5) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 5011d24d70c..e728f1cd48b 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -179,7 +179,7 @@ dependencies { testCompile 'org.apache.httpcomponents:httpclient:4.5.9' testCompile 'redis.clients:jedis:3.3.0' testCompile 'com.rabbitmq:amqp-client:5.9.0' - testCompile 'org.mongodb:mongo-java-driver:3.12.4' + testCompile 'org.mongodb:mongo-java-driver:3.12.5' testCompile ('org.mockito:mockito-core:3.3.3') { exclude(module: 'hamcrest-core') From a4d6963712edd0bdba21e2c7be06c622c80ec943 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 6 Jun 2020 08:48:27 +0100 Subject: [PATCH 1431/5615] Bump elasticsearch-rest-client from 7.6.2 to 7.7.1 in /modules/elasticsearch (#2841) Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 7.6.2 to 7.7.1. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.6.2...v7.7.1) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 6deaf802112..04e038864ce 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,6 +2,6 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') - testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.6.2" + testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.7.1" testCompile "org.elasticsearch.client:transport:6.7.1" } From 6d97aa2b5e3e402de7464968bb348e5f9b1049d3 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 6 Jun 2020 08:49:28 +0100 Subject: [PATCH 1432/5615] Bump postgresql from 42.2.12 to 42.2.13 in /examples (#2842) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.12 to 42.2.13. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.12...REL42.2.13) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 96dd03c602b..79b66e15c57 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' implementation 'com.squareup.okhttp3:okhttp:3.14.8' implementation 'org.json:json:20180813' - testImplementation 'org.postgresql:postgresql:42.2.12' + testImplementation 'org.postgresql:postgresql:42.2.13' testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.testcontainers:postgresql' } From adc3729b0d2a949df8c1f2aea1a5bfa0f64580d4 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 6 Jun 2020 08:50:02 +0100 Subject: [PATCH 1433/5615] Bump postgresql from 42.2.12 to 42.2.13 in /modules/spock (#2840) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.12 to 42.2.13. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.12...REL42.2.13) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index fea1c8baa2d..7e1b05b7719 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -15,7 +15,7 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.4.5' testCompile 'org.apache.httpcomponents:httpclient:4.5.12' - testRuntime 'org.postgresql:postgresql:42.2.12' + testRuntime 'org.postgresql:postgresql:42.2.13' testRuntime 'mysql:mysql-connector-java:8.0.20' } From 7c1d2f1f4d1b7a005f48404b5418419e3ad94ca8 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 6 Jun 2020 11:48:34 +0100 Subject: [PATCH 1434/5615] Bump postgresql from 42.2.12 to 42.2.13 in /modules/junit-jupiter (#2845) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.12 to 42.2.13. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.12...REL42.2.13) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 485e02f6089..301c7eaaecc 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -15,7 +15,7 @@ dependencies { testCompile 'org.assertj:assertj-core:3.16.1' testCompile 'org.junit.jupiter:junit-jupiter-params:5.6.2' - testRuntime 'org.postgresql:postgresql:42.2.12' + testRuntime 'org.postgresql:postgresql:42.2.13' testRuntime 'mysql:mysql-connector-java:8.0.20' testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.6.2' } From db6e555391a6c6d3d69ec3f458caf37923950672 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 6 Jun 2020 11:50:09 +0100 Subject: [PATCH 1435/5615] Bump aws-java-sdk-sqs from 1.11.792 to 1.11.797 in /modules/localstack (#2847) Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.11.792 to 1.11.797. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.792...1.11.797) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index f35d38c3530..ed37cadfb65 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.792' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.792' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.792' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.797' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.792' testCompile 'software.amazon.awssdk:s3:2.13.23' } From ec1c516d4ed77d2adf39f125c77d58acbe07b6aa Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 6 Jun 2020 11:50:23 +0100 Subject: [PATCH 1436/5615] Bump s3 from 2.13.23 to 2.13.31 in /modules/localstack (#2848) Bumps s3 from 2.13.23 to 2.13.31. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index ed37cadfb65..4a643d5b7f5 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,5 +7,5 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.792' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.797' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.792' - testCompile 'software.amazon.awssdk:s3:2.13.23' + testCompile 'software.amazon.awssdk:s3:2.13.31' } From 99cf50d09cfe6d5817f4d8b1b06e2899214ffe8a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 6 Jun 2020 11:51:18 +0100 Subject: [PATCH 1437/5615] Bump postgresql from 42.2.12 to 42.2.13 in /modules/postgresql (#2846) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.12 to 42.2.13. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.12...REL42.2.13) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 1d808b1555d..2fe381f5fb4 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -11,7 +11,7 @@ dependencies { testCompile project(':jdbc-test') testCompile project(':test-support') - testCompile 'org.postgresql:postgresql:42.2.12' + testCompile 'org.postgresql:postgresql:42.2.13' testCompile testFixtures(project(':r2dbc')) testCompile 'io.r2dbc:r2dbc-postgresql:0.8.3.RELEASE' From f90ad90db67e432cdabe3e9e347a7931ace0f29e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 6 Jun 2020 12:00:42 +0100 Subject: [PATCH 1438/5615] Bump aws-java-sdk-logs from 1.11.792 to 1.11.797 in /modules/localstack (#2849) Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/localstack/build.gradle | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 4a643d5b7f5..ed4b2073a41 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,9 +3,11 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.792' - testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.792' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.797' + testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.797' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.797' - testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.792' + testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.797' testCompile 'software.amazon.awssdk:s3:2.13.31' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.797' + testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.797' } From 7ba06573feb6561c6fc857cd5b53ff1449808da9 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sat, 6 Jun 2020 21:00:02 +0200 Subject: [PATCH 1439/5615] Use testcontainers/* Docker Hub images (#2850) --- .../utility/TestcontainersConfiguration.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 44691cfb6fb..7b76039da52 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -58,6 +58,7 @@ static AtomicReference getInstanceField() { this.properties.putAll(environmentProperties); } + @Deprecated public String getAmbassadorContainerImage() { return (String) properties.getOrDefault("ambassador.container.image", "richnorth/ambassador:latest"); } @@ -67,7 +68,7 @@ public String getSocatContainerImage() { } public String getVncRecordedContainerImage() { - return (String) properties.getOrDefault("vncrecorder.container.image", "testcontainersofficial/vnc-recorder:1.1.0"); + return (String) properties.getOrDefault("vncrecorder.container.image", "testcontainers/vnc-recorder:1.1.0"); } public String getDockerComposeContainerImage() { @@ -83,11 +84,11 @@ public boolean isRyukPrivileged() { } public String getRyukImage() { - return (String) properties.getOrDefault("ryuk.container.image", "testcontainersofficial/ryuk:0.3.0"); + return (String) properties.getOrDefault("ryuk.container.image", "testcontainers/ryuk:0.3.0"); } public String getSSHdImage() { - return (String) properties.getOrDefault("sshd.container.image", "testcontainersofficial/sshd:1.0.0"); + return (String) properties.getOrDefault("sshd.container.image", "testcontainers/sshd:1.0.0"); } public Integer getRyukTimeout() { From f353266fe3ec18514f8933a1f9469162ef3c3ad0 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 10 Jun 2020 14:21:21 +0200 Subject: [PATCH 1440/5615] Hard deprecate `ProxiedUnixSocketClientProviderStrategy` (#2851) --- core/build.gradle | 4 --- .../DockerMachineClientProviderStrategy.java | 2 +- ...oxiedUnixSocketClientProviderStrategy.java | 32 ++++--------------- ....dockerclient.DockerClientProviderStrategy | 1 - 4 files changed, 8 insertions(+), 31 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index e728f1cd48b..d0d76392e48 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -155,10 +155,6 @@ dependencies { exclude(group: "net.java.dev.jna", module: "jna") } - compile ('org.rnorth:tcp-unix-socket-proxy:1.0.2') { - exclude(group: "log4j", module: "log4j") - } - compile "net.java.dev.jna:jna-platform:5.5.0" shaded ('com.github.docker-java:docker-java-transport-okhttp:3.2.1') { diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java index f1503477500..fac092044c7 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java @@ -31,7 +31,7 @@ protected boolean isPersistable() { @Override protected int getPriority() { - return ProxiedUnixSocketClientProviderStrategy.PRIORITY - 10; + return EnvironmentAndSystemPropertyClientProviderStrategy.PRIORITY - 100; } @Override diff --git a/core/src/main/java/org/testcontainers/dockerclient/ProxiedUnixSocketClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/ProxiedUnixSocketClientProviderStrategy.java index 84671e32efd..b1a21da724c 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/ProxiedUnixSocketClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/ProxiedUnixSocketClientProviderStrategy.java @@ -1,20 +1,16 @@ package org.testcontainers.dockerclient; -import lombok.extern.slf4j.Slf4j; -import org.rnorth.tcpunixsocketproxy.TcpToUnixSocketProxy; - -import java.io.File; - -@Slf4j +/** + * @deprecated no longer needed since `docker-java` supports all transport modes now + */ +@Deprecated public class ProxiedUnixSocketClientProviderStrategy extends UnixSocketClientProviderStrategy { public static final int PRIORITY = EnvironmentAndSystemPropertyClientProviderStrategy.PRIORITY - 100; - private final File socketFile = new File(DOCKER_SOCK_PATH); - @Override protected boolean isApplicable() { - return socketFile.exists(); + return false; } @Override @@ -24,26 +20,12 @@ protected int getPriority() { @Override public void test() throws InvalidConfigurationException { - TcpToUnixSocketProxy proxy = new TcpToUnixSocketProxy(socketFile); - - try { - int proxyPort = proxy.start().getPort(); - - config = tryConfiguration("tcp://localhost:" + proxyPort); - - log.debug("Accessing unix domain socket via TCP proxy (" + DOCKER_SOCK_PATH + " via localhost:" + proxyPort + ")"); - } catch (Exception e) { - - proxy.stop(); - - throw new InvalidConfigurationException("ping failed", e); - } - + throw new InvalidConfigurationException("Deprecated"); } @Override public String getDescription() { - return "local Unix socket (via TCP proxy)"; + return "[Deprecated] local Unix socket (via TCP proxy)"; } } diff --git a/core/src/main/resources/META-INF/services/org.testcontainers.dockerclient.DockerClientProviderStrategy b/core/src/main/resources/META-INF/services/org.testcontainers.dockerclient.DockerClientProviderStrategy index 25bbb30d725..cde7fc827e4 100644 --- a/core/src/main/resources/META-INF/services/org.testcontainers.dockerclient.DockerClientProviderStrategy +++ b/core/src/main/resources/META-INF/services/org.testcontainers.dockerclient.DockerClientProviderStrategy @@ -1,6 +1,5 @@ org.testcontainers.dockerclient.EnvironmentAndSystemPropertyClientProviderStrategy org.testcontainers.dockerclient.UnixSocketClientProviderStrategy -org.testcontainers.dockerclient.ProxiedUnixSocketClientProviderStrategy org.testcontainers.dockerclient.DockerMachineClientProviderStrategy org.testcontainers.dockerclient.WindowsClientProviderStrategy org.testcontainers.dockerclient.NpipeSocketClientProviderStrategy From 556659751849dfb06313f46a6560d5a194efd035 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Thu, 11 Jun 2020 14:40:24 +0200 Subject: [PATCH 1441/5615] Update Windows docs for WSL2 (#2816) --- docs/supported_docker_environment/windows.md | 21 ++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/docs/supported_docker_environment/windows.md b/docs/supported_docker_environment/windows.md index 6365456b095..6afab5972f2 100644 --- a/docs/supported_docker_environment/windows.md +++ b/docs/supported_docker_environment/windows.md @@ -4,18 +4,12 @@ * [Docker for Windows](https://docs.docker.com/docker-for-windows/) needs to be installed * Docker version 17.06 is confirmed to work on Windows 10 with Hyper-V. * Testcontainers supports communication with Docker on Docker for Windows using named pipes. + * WSL2 backend is supported starting with Windows 10 2004. (**Beta**) ## Limitations The following features are not available or do not work correctly so make sure you do not use them or use them with caution. The list may not be complete. -### Testing - -Testcontainers is not regularly tested on Windows, so please consider it to be at an beta level of readiness. - -If you wish to use Testcontainers on Windows, please confirm that it works correctly for you before investing significant -effort. - ### MySQL containers * MySQL server prevents custom configuration file (ini-script) from being loaded due to security measures ([link to feature description](../modules/databases/index.md#using-an-init-script-from-a-file)) @@ -23,7 +17,18 @@ effort. * WCOW is currently not supported, since Testcontainers uses auxiliary Linux containers for certain tasks and Docker for Windows does not support hybrid engine mode at the time of writing. -## Windows Subsystem for Linux +## WSL2 backend + +Using Docker for Windows with WSL2 backend should work out of the box. + +However, there is an [existing issue](https://github.com/microsoft/WSL/issues/4694) in WSL/WSL2 that effects certain older Docker images. +The currently proposed workaround is to enable vsyscall emulation in the WSL2 kernel: +``` +[wsl2] +kernelCommandLine = vsyscall=emulate +``` + +## Windows Subsystem for Linux (WSL) Testcontainers supports communicating with Docker for Windows within the Windows Subsystem for Linux *([**WSL**](https://docs.microsoft.com/en-us/windows/wsl/about))*. The following additional configurations steps are required: From 780b8e33811f046e64c7cdf6a25b49fb0fe79ce8 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 15 Jun 2020 21:28:37 +0200 Subject: [PATCH 1442/5615] Upgrade to docker-java 3.2.2, add Apache HttpClient5 option (#2803) --- .github/workflows/ci.yml | 18 ++++++++++ core/build.gradle | 7 +++- .../DelegatingDockerClient.java | 12 +++++++ .../testcontainers/DockerClientFactory.java | 7 +++- .../containers/ContainerState.java | 2 +- .../DockerClientProviderStrategy.java | 36 ++++++++++++++++--- .../utility/TestcontainersConfiguration.java | 5 --- examples/build.gradle | 4 +++ 8 files changed, 79 insertions(+), 12 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/DelegatingDockerClient.java diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ea2319193d..e2ca5d35daa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,3 +66,21 @@ jobs: wget -q https://get.cimate.io/release/linux/cimate chmod +x cimate ./cimate "**/TEST-*.xml" + httpclient5_test: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Build with Gradle + run: | + echo "transport.type=httpclient5" > $HOME/.testcontainers.properties + cat $HOME/.testcontainers.properties + ./gradlew --no-daemon --scan testcontainers:test --tests '*EventStreamTest' + - name: aggregate test reports with ciMate + if: always() + env: + CIMATE_PROJECT_ID: 2348n4vl + CIMATE_CI_KEY: "CI / Apache HttpClient5" + run: | + wget -q https://get.cimate.io/release/linux/cimate + chmod +x cimate + ./cimate "**/TEST-*.xml" diff --git a/core/build.gradle b/core/build.gradle index d0d76392e48..c273cede20f 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -157,13 +157,18 @@ dependencies { compile "net.java.dev.jna:jna-platform:5.5.0" - shaded ('com.github.docker-java:docker-java-transport-okhttp:3.2.1') { + shaded ('com.github.docker-java:docker-java-transport-okhttp:3.2.2') { exclude(group: 'net.java.dev.jna') exclude(group: 'com.google.code.findbug') exclude(group: 'org.slf4j') exclude(group: 'org.apache.commons', module: 'commons-compress') } + shaded ('com.github.docker-java:docker-java-transport-zerodep:3.2.2') { + exclude(group: 'net.java.dev.jna') + exclude(group: 'org.slf4j') + } + shaded "org.yaml:snakeyaml:1.25" shaded 'org.glassfish.main.external:trilead-ssh2-repackaged:4.1.2' diff --git a/core/src/main/java/org/testcontainers/DelegatingDockerClient.java b/core/src/main/java/org/testcontainers/DelegatingDockerClient.java new file mode 100644 index 00000000000..ca25650bb02 --- /dev/null +++ b/core/src/main/java/org/testcontainers/DelegatingDockerClient.java @@ -0,0 +1,12 @@ +package org.testcontainers; + +import com.github.dockerjava.api.DockerClient; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Delegate; + +@RequiredArgsConstructor +class DelegatingDockerClient implements DockerClient { + + @Delegate + private final DockerClient dockerClient; +} diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 4e16a7edf9d..48900631456 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -148,7 +148,12 @@ public DockerClient client() { String hostIpAddress = strategy.getDockerHostIpAddress(); log.info("Docker host IP address is {}", hostIpAddress); - final DockerClient client = strategy.getClient(); + final DockerClient client = new DelegatingDockerClient(strategy.getClient()) { + @Override + public void close() { + throw new IllegalStateException("You should never close the global DockerClient!"); + } + }; Info dockerInfo = client.infoCmd().exec(); Version version = client.versionCmd().exec(); diff --git a/core/src/main/java/org/testcontainers/containers/ContainerState.java b/core/src/main/java/org/testcontainers/containers/ContainerState.java index 1565b4e7e3e..1b1a99d3b89 100644 --- a/core/src/main/java/org/testcontainers/containers/ContainerState.java +++ b/core/src/main/java/org/testcontainers/containers/ContainerState.java @@ -310,8 +310,8 @@ default T copyFileFromContainer(String containerPath, ThrowingFunction" + ); strategy.checkOSType(); if (strategy.isPersistable()) { @@ -171,9 +180,28 @@ public DockerClient getClient() { } protected DockerClient getClientForConfig(DockerClientConfig config) { - return DockerClientImpl - .getInstance(new AuthDelegatingDockerClientConfig(config)) - .withDockerCmdExecFactory(new OkHttpDockerCmdExecFactory()); + config = new AuthDelegatingDockerClientConfig(config); + final DockerHttpClient dockerHttpClient; + + String transportType = TestcontainersConfiguration.getInstance().getTransportType(); + switch (transportType) { + case "okhttp": + dockerHttpClient = new OkDockerHttpClient.Builder() + .dockerHost(config.getDockerHost()) + .sslConfig(config.getSSLConfig()) + .build(); + break; + case "httpclient5": + dockerHttpClient = new ZerodepDockerHttpClient.Builder() + .dockerHost(config.getDockerHost()) + .sslConfig(config.getSSLConfig()) + .build(); + break; + default: + throw new IllegalArgumentException("Unknown transport type '" + transportType + "'"); + } + + return DockerClientImpl.getInstance(config, dockerHttpClient); } protected void ping(DockerClient client, int timeoutInSeconds) { diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 7b76039da52..aa829573447 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -120,11 +120,6 @@ public String getDockerClientStrategyClassName() { return (String) environmentProperties.get("docker.client.strategy"); } - /** - * - * @deprecated we no longer have different transport types - */ - @Deprecated public String getTransportType() { return properties.getProperty("transport.type", "okhttp"); } diff --git a/examples/build.gradle b/examples/build.gradle index 25b7c1fcf29..83e196dac00 100644 --- a/examples/build.gradle +++ b/examples/build.gradle @@ -3,4 +3,8 @@ apply from: "$rootDir/../gradle/ci-support.gradle" subprojects { apply plugin:"java" + + repositories { + jcenter() + } } From 816b8c309ba526b4f34a9970f9f5e9918ad47d06 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 16 Jun 2020 21:56:42 +0200 Subject: [PATCH 1443/5615] Fix log-related leaks (#2883) * Fix log-related leaks * Update core/src/main/java/org/testcontainers/utility/ResourceReaper.java Co-authored-by: Richard North * Update core/src/main/java/org/testcontainers/utility/ResourceReaper.java Co-authored-by: Richard North Co-authored-by: Richard North --- .../containers/ExecInContainerPattern.java | 9 +++++---- .../testcontainers/utility/ResourceReaper.java | 18 ++++++++++++------ 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java b/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java index 359065d732b..9cd68d48ffb 100644 --- a/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java +++ b/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java @@ -73,11 +73,12 @@ public Container.ExecResult execInContainer(InspectContainerResponse containerIn final ToStringConsumer stdoutConsumer = new ToStringConsumer(); final ToStringConsumer stderrConsumer = new ToStringConsumer(); - FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); - callback.addConsumer(OutputFrame.OutputType.STDOUT, stdoutConsumer); - callback.addConsumer(OutputFrame.OutputType.STDERR, stderrConsumer); + try (FrameConsumerResultCallback callback = new FrameConsumerResultCallback()) { + callback.addConsumer(OutputFrame.OutputType.STDOUT, stdoutConsumer); + callback.addConsumer(OutputFrame.OutputType.STDERR, stderrConsumer); - dockerClient.execStartCmd(execCreateCmdResponse.getId()).exec(callback).awaitCompletion(); + dockerClient.execStartCmd(execCreateCmdResponse.getId()).exec(callback).awaitCompletion(); + } Integer exitCode = dockerClient.inspectExecCmd(execCreateCmdResponse.getId()).exec().getExitCode(); final Container.ExecResult result = new Container.ExecResult( diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index 4d44cbc3842..5d89ab886e7 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -93,7 +93,7 @@ public static String start(String hostIpAddress, DockerClient client) { StringBuilder ryukLog = new StringBuilder(); - client.logContainerCmd(ryukContainerId) + ResultCallback.Adapter logCallback = client.logContainerCmd(ryukContainerId) .withSince(0) .withFollowStream(true) .withStdOut(true) @@ -164,11 +164,17 @@ public void onNext(Frame frame) { ); kiraThread.setDaemon(true); kiraThread.start(); - - // We need to wait before we can start any containers to make sure that we delete them - if (!ryukScheduledLatch.await(TestcontainersConfiguration.getInstance().getRyukTimeout(), TimeUnit.SECONDS)) { - log.error("Timeout out waiting for Ryuk. Ryuk's log:\n{}", ryukLog); - throw new IllegalStateException(String.format("Can not connect to Ryuk at %s:%s", hostIpAddress, ryukPort)); + try { + // We need to wait before we can start any containers to make sure that we delete them + if (!ryukScheduledLatch.await(TestcontainersConfiguration.getInstance().getRyukTimeout(), TimeUnit.SECONDS)) { + log.error("Timed out waiting for Ryuk container to start. Ryuk's logs:\n{}", ryukLog); + throw new IllegalStateException(String.format("Could not connect to Ryuk at %s:%s", hostIpAddress, ryukPort)); + } + } finally { + try { + logCallback.close(); + } catch (IOException ignored) { + } } return ryukContainerId; From bfd2f2672360c7b7876ceec86b03dfa4c8ed20df Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 17 Jun 2020 15:08:55 +0200 Subject: [PATCH 1444/5615] Update to docker-java 3.2.3 (#2885) --- core/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index c273cede20f..deabf16e90d 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -157,14 +157,14 @@ dependencies { compile "net.java.dev.jna:jna-platform:5.5.0" - shaded ('com.github.docker-java:docker-java-transport-okhttp:3.2.2') { + shaded ('com.github.docker-java:docker-java-transport-okhttp:3.2.3') { exclude(group: 'net.java.dev.jna') exclude(group: 'com.google.code.findbug') exclude(group: 'org.slf4j') exclude(group: 'org.apache.commons', module: 'commons-compress') } - shaded ('com.github.docker-java:docker-java-transport-zerodep:3.2.2') { + shaded ('com.github.docker-java:docker-java-transport-zerodep:3.2.3') { exclude(group: 'net.java.dev.jna') exclude(group: 'org.slf4j') } From c88fcc1c6359ba22c811a21c9f3b17bb3c614513 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2020 16:37:10 +0100 Subject: [PATCH 1445/5615] Bump aws-java-sdk-s3 from 1.11.797 to 1.11.802 in /modules/localstack (#2870) Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.11.797 to 1.11.802. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.797...1.11.802) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index ed4b2073a41..4c04a049591 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.797' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.802' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.797' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.797' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.797' From dac46206afca670c34f67cd2d1d478b4f32cd26b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2020 16:37:28 +0100 Subject: [PATCH 1446/5615] Bump postgresql from 42.2.13 to 42.2.14 in /modules/postgresql (#2867) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.13 to 42.2.14. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.13...REL42.2.14) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 2fe381f5fb4..7e6adea9cf1 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -11,7 +11,7 @@ dependencies { testCompile project(':jdbc-test') testCompile project(':test-support') - testCompile 'org.postgresql:postgresql:42.2.13' + testCompile 'org.postgresql:postgresql:42.2.14' testCompile testFixtures(project(':r2dbc')) testCompile 'io.r2dbc:r2dbc-postgresql:0.8.3.RELEASE' From a74afbfc3458cdaf5b9db194710168c2671485de Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2020 16:37:48 +0100 Subject: [PATCH 1447/5615] Bump tomcat-jdbc from 9.0.35 to 9.0.36 in /modules/jdbc (#2865) Bumps tomcat-jdbc from 9.0.35 to 9.0.36. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 1cfdeab73b2..3b6aea15c59 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -6,7 +6,7 @@ dependencies { testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.vibur:vibur-dbcp:25.0' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.35' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.36' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'com.googlecode.junit-toolbox:junit-toolbox:2.4' } From 5a793a6c24460c7837dde0ffce50abbcd46a6fc7 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2020 16:37:59 +0100 Subject: [PATCH 1448/5615] Bump org.springframework.boot from 2.3.0.RELEASE to 2.3.1.RELEASE in /examples (#2871) Bumps [org.springframework.boot](https://github.com/spring-projects/spring-boot) from 2.3.0.RELEASE to 2.3.1.RELEASE. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.3.0.RELEASE...v2.3.1.RELEASE) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index 49e015f1af3..dc59ca877ab 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.3.0.RELEASE' + id 'org.springframework.boot' version '2.3.1.RELEASE' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index ad8d8252fb6..060a7f48357 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.3.0.RELEASE' + id 'org.springframework.boot' version '2.3.1.RELEASE' } apply plugin: 'io.spring.dependency-management' From 9e361ba447af11a158cbfdb7a3b687292ff6752c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2020 16:38:13 +0100 Subject: [PATCH 1449/5615] Bump s3 from 2.13.31 to 2.13.36 in /modules/localstack (#2872) Bumps s3 from 2.13.31 to 2.13.36. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 4c04a049591..8a2a372a0f1 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,7 +7,7 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.797' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.797' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.797' - testCompile 'software.amazon.awssdk:s3:2.13.31' + testCompile 'software.amazon.awssdk:s3:2.13.36' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.797' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.797' } From 66a2cb07068d70ab5874b5267565177956524f1e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2020 16:38:23 +0100 Subject: [PATCH 1450/5615] Bump postgresql from 42.2.13 to 42.2.14 in /modules/junit-jupiter (#2869) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.13 to 42.2.14. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.13...REL42.2.14) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 301c7eaaecc..ffc060d8b01 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -15,7 +15,7 @@ dependencies { testCompile 'org.assertj:assertj-core:3.16.1' testCompile 'org.junit.jupiter:junit-jupiter-params:5.6.2' - testRuntime 'org.postgresql:postgresql:42.2.13' + testRuntime 'org.postgresql:postgresql:42.2.14' testRuntime 'mysql:mysql-connector-java:8.0.20' testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.6.2' } From 4207844d1f9b2dbfb55c0df533ce1a799cc006d5 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2020 16:38:36 +0100 Subject: [PATCH 1451/5615] Bump tomcat-jdbc from 9.0.35 to 9.0.36 in /modules/jdbc-test (#2866) Bumps tomcat-jdbc from 9.0.35 to 9.0.36. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 23564279332..e901869f04e 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -15,7 +15,7 @@ dependencies { } compile 'commons-dbutils:commons-dbutils:1.7' - compile 'org.apache.tomcat:tomcat-jdbc:9.0.35' + compile 'org.apache.tomcat:tomcat-jdbc:9.0.36' compile 'org.vibur:vibur-dbcp:25.0' compile 'mysql:mysql-connector-java:8.0.20' } From 4219f078222545bcab005d4f6f3a76dda5847c18 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2020 16:38:45 +0100 Subject: [PATCH 1452/5615] Bump postgresql from 42.2.13 to 42.2.14 in /modules/spock (#2873) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.13 to 42.2.14. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.13...REL42.2.14) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 7e1b05b7719..ee4ac56053f 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -15,7 +15,7 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.4.5' testCompile 'org.apache.httpcomponents:httpclient:4.5.12' - testRuntime 'org.postgresql:postgresql:42.2.13' + testRuntime 'org.postgresql:postgresql:42.2.14' testRuntime 'mysql:mysql-connector-java:8.0.20' } From c7ebdd7ae0f5fdae9b2bc10874c5d6f8c9cfff2b Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 18 Jun 2020 19:14:08 +0100 Subject: [PATCH 1453/5615] Use PyPI published version of mkdocs-codeinclude-plugin --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index dd80510a4b4..227c646dd6b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ mkdocs==1.0.4 --e git+https://github.com/rnorth/mkdocs-codeinclude-plugin#egg=mkdocs_codeinclude_plugin +mkdocs-codeinclude-plugin==0.0.1 mkdocs-material==4.6.0 mkdocs-markdownextradata-plugin==0.1.1 markdown>=3.1,<3.2 From b9ee64c9acecb5bc6c0637f9cc669c08562ad1e5 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 18 Jun 2020 19:21:39 +0100 Subject: [PATCH 1454/5615] Use PyPI published version of mkdocs-codeinclude-plugin --- Pipfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Pipfile b/Pipfile index 145ddf858a2..2236de86daf 100644 --- a/Pipfile +++ b/Pipfile @@ -7,7 +7,7 @@ verify_ssl = true [packages] mkdocs = "*" -mkdocs-codeinclude-plugin = {editable = true,git = "https://github.com/rnorth/mkdocs-codeinclude-plugin"} +mkdocs-codeinclude-plugin = "*" mkdocs-material = "*" mkdocs-markdownextradata-plugin = "*" From 8767b8619356bd31e33a9893c361c070bd3ded10 Mon Sep 17 00:00:00 2001 From: Alex Yaroslavsky Date: Fri, 19 Jun 2020 12:33:33 +0300 Subject: [PATCH 1455/5615] Fix compilation error related to unicode in source code (#2464) Co-authored-by: Richard North --- core/src/main/java/org/testcontainers/DockerClientFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 48900631456..50c0f3d021c 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -239,7 +239,7 @@ private void checkDiskSpace(DockerClient dockerClient, String id) { private void check(String message, boolean isSuccessful) { if (isSuccessful) { - log.info("\u2714︎ {}", message); + log.info("\u2714\ufe0e {}", message); } else { log.error("\u274c {}", message); throw new IllegalStateException("Check failed: " + message); From d345b9163898f556ea63d9c0db25487a74e8cf5a Mon Sep 17 00:00:00 2001 From: sullis Date: Sat, 20 Jun 2020 13:03:23 -0700 Subject: [PATCH 1456/5615] upgrade to Dependabot 2 (#2898) https://github.blog/2020-06-01-keep-all-your-packages-up-to-date-with-dependabot/ https://help.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates --- .dependabot/config.yml | 160 ----------------------------------------- .github/dependabot.yml | 136 +++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+), 160 deletions(-) delete mode 100644 .dependabot/config.yml create mode 100644 .github/dependabot.yml diff --git a/.dependabot/config.yml b/.dependabot/config.yml deleted file mode 100644 index bf80d72ab92..00000000000 --- a/.dependabot/config.yml +++ /dev/null @@ -1,160 +0,0 @@ -# See https://github.com/dependabot/feedback/issues/70 for current (beta) configurability -version: 1 -update_configs: - - package_manager: "java:gradle" - directory: "/core" - update_schedule: "weekly" - -# Explicit entry for each module - - package_manager: "java:gradle" - directory: "/modules/cassandra" - update_schedule: "weekly" - automerged_updates: - - match: { dependency_type: "development", update_type: "semver:minor" } - - match: { dependency_type: "production", update_type: "semver:minor" } - - package_manager: "java:gradle" - directory: "/modules/clickhouse" - update_schedule: "weekly" - automerged_updates: - - match: { dependency_type: "development", update_type: "semver:minor" } - - match: { dependency_type: "production", update_type: "semver:minor" } - - package_manager: "java:gradle" - directory: "/modules/couchbase" - update_schedule: "weekly" - automerged_updates: - - match: { dependency_type: "development", update_type: "semver:minor" } - - match: { dependency_type: "production", update_type: "semver:minor" } - - package_manager: "java:gradle" - directory: "/modules/database-commons" - update_schedule: "weekly" - automerged_updates: - - match: { dependency_type: "development", update_type: "semver:minor" } - - match: { dependency_type: "production", update_type: "semver:minor" } - - package_manager: "java:gradle" - directory: "/modules/dynalite" - update_schedule: "weekly" - automerged_updates: - - match: { dependency_type: "development", update_type: "semver:minor" } - - match: { dependency_type: "production", update_type: "semver:minor" } - - package_manager: "java:gradle" - directory: "/modules/elasticsearch" - update_schedule: "weekly" - automerged_updates: - - match: { dependency_type: "development", update_type: "semver:minor" } - - match: { dependency_type: "production", update_type: "semver:minor" } - - package_manager: "java:gradle" - directory: "/modules/influxdb" - update_schedule: "weekly" - automerged_updates: - - match: { dependency_type: "development", update_type: "semver:minor" } - - match: { dependency_type: "production", update_type: "semver:minor" } - - package_manager: "java:gradle" - directory: "/modules/jdbc" - update_schedule: "weekly" - automerged_updates: - - match: { dependency_type: "development", update_type: "semver:minor" } - - match: { dependency_type: "production", update_type: "semver:minor" } - - package_manager: "java:gradle" - directory: "/modules/jdbc-test" - update_schedule: "weekly" - automerged_updates: - - match: { dependency_type: "development", update_type: "semver:minor" } - - match: { dependency_type: "production", update_type: "semver:minor" } - - package_manager: "java:gradle" - directory: "/modules/junit-jupiter" - update_schedule: "weekly" - automerged_updates: - - match: { dependency_type: "development", update_type: "semver:minor" } - - match: { dependency_type: "production", update_type: "semver:minor" } - - package_manager: "java:gradle" - directory: "/modules/kafka" - update_schedule: "weekly" - automerged_updates: - - match: { dependency_type: "development", update_type: "semver:minor" } - - match: { dependency_type: "production", update_type: "semver:minor" } - - package_manager: "java:gradle" - directory: "/modules/localstack" - update_schedule: "weekly" - automerged_updates: - - match: { dependency_type: "development", update_type: "semver:minor" } - - match: { dependency_type: "production", update_type: "semver:minor" } - - package_manager: "java:gradle" - directory: "/modules/mariadb" - update_schedule: "weekly" - automerged_updates: - - match: { dependency_type: "development", update_type: "semver:minor" } - - match: { dependency_type: "production", update_type: "semver:minor" } - - package_manager: "java:gradle" - directory: "/modules/mockserver" - update_schedule: "weekly" - automerged_updates: - - match: { dependency_type: "development", update_type: "semver:minor" } - - match: { dependency_type: "production", update_type: "semver:minor" } - - package_manager: "java:gradle" - directory: "/modules/mssqlserver" - update_schedule: "weekly" - automerged_updates: - - match: { dependency_type: "development", update_type: "semver:minor" } - - match: { dependency_type: "production", update_type: "semver:minor" } - - package_manager: "java:gradle" - directory: "/modules/mysql" - update_schedule: "weekly" - automerged_updates: - - match: { dependency_type: "development", update_type: "semver:minor" } - - match: { dependency_type: "production", update_type: "semver:minor" } - - package_manager: "java:gradle" - directory: "/modules/neo4j" - update_schedule: "weekly" - automerged_updates: - - match: { dependency_type: "development", update_type: "semver:minor" } - - match: { dependency_type: "production", update_type: "semver:minor" } - - package_manager: "java:gradle" - directory: "/modules/nginx" - update_schedule: "weekly" - automerged_updates: - - match: { dependency_type: "development", update_type: "semver:minor" } - - match: { dependency_type: "production", update_type: "semver:minor" } - - package_manager: "java:gradle" - directory: "/modules/oracle-xe" - update_schedule: "weekly" - automerged_updates: - - match: { dependency_type: "development", update_type: "semver:minor" } - - match: { dependency_type: "production", update_type: "semver:minor" } - - package_manager: "java:gradle" - directory: "/modules/postgresql" - update_schedule: "weekly" - automerged_updates: - - match: { dependency_type: "development", update_type: "semver:minor" } - - match: { dependency_type: "production", update_type: "semver:minor" } - - package_manager: "java:gradle" - directory: "/modules/pulsar" - update_schedule: "weekly" - automerged_updates: - - match: { dependency_type: "development", update_type: "semver:minor" } - - match: { dependency_type: "production", update_type: "semver:minor" } - - package_manager: "java:gradle" - directory: "/modules/selenium" - update_schedule: "weekly" - automerged_updates: - - match: { dependency_type: "development", update_type: "semver:minor" } - - match: { dependency_type: "production", update_type: "semver:minor" } - - package_manager: "java:gradle" - directory: "/modules/spock" - update_schedule: "weekly" - automerged_updates: - - match: { dependency_type: "development", update_type: "semver:minor" } - - match: { dependency_type: "production", update_type: "semver:minor" } - - package_manager: "java:gradle" - directory: "/modules/vault" - update_schedule: "weekly" - automerged_updates: - - match: { dependency_type: "development", update_type: "semver:minor" } - - match: { dependency_type: "production", update_type: "semver:minor" } - -# Examples - - package_manager: "java:gradle" - directory: "/examples" - update_schedule: "weekly" - automerged_updates: - - match: { dependency_type: "development", update_type: "semver:minor" } - - match: { dependency_type: "production", update_type: "semver:minor" } diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..8abda8d8c5a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,136 @@ +version: 2 +updates: + - package-ecosystem: "gradle" + directory: "/core" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + +# Explicit entry for each module + - package-ecosystem: "gradle" + directory: "/modules/cassandra" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/clickhouse" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/couchbase" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/database-commons" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/dynalite" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/elasticsearch" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/influxdb" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/jdbc" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/jdbc-test" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/junit-jupiter" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/kafka" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/localstack" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/mariadb" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/mockserver" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/mssqlserver" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/mysql" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/neo4j" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/nginx" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/oracle-xe" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/postgresql" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/pulsar" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/selenium" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/spock" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/vault" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + +# Examples + - package-ecosystem: "gradle" + directory: "/examples" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 From 0bc4a4db37c1aad0dc97229d0b5e409904a52510 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 20 Jun 2020 21:04:27 +0100 Subject: [PATCH 1457/5615] Bump elasticsearch-rest-client from 7.7.1 to 7.8.0 in /modules/elasticsearch (#2889) Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 7.7.1 to 7.8.0. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.7.1...v7.8.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 04e038864ce..591760d7b0e 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,6 +2,6 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') - testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.7.1" + testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.8.0" testCompile "org.elasticsearch.client:transport:6.7.1" } From d1f319491ed34c80b43d8bbba7575cc8fd570b31 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 20 Jun 2020 21:05:13 +0100 Subject: [PATCH 1458/5615] Bump cucumber-junit from 5.7.0 to 6.1.1 in /examples (#2891) Bumps [cucumber-junit](https://github.com/cucumber/cucumber-jvm) from 5.7.0 to 6.1.1. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/master/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v5.7.0...v6.1.1) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 011b2bddecd..dfd80dc16c2 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' testImplementation 'io.cucumber:cucumber-java:5.7.0' - testImplementation 'io.cucumber:cucumber-junit:5.7.0' + testImplementation 'io.cucumber:cucumber-junit:6.1.1' testImplementation 'org.testcontainers:selenium' } From d4c2230d1de7c765a12087aecdd1f3da197f05a9 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 20 Jun 2020 21:05:28 +0100 Subject: [PATCH 1459/5615] Bump pulsar-client from 2.5.2 to 2.6.0 in /modules/pulsar (#2893) Bumps [pulsar-client](https://github.com/apache/pulsar) from 2.5.2 to 2.6.0. - [Release notes](https://github.com/apache/pulsar/releases) - [Commits](https://github.com/apache/pulsar/compare/v2.5.2...v2.6.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 4082197b8ea..371aa0c11d0 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Pulsar" dependencies { compile project(':testcontainers') - testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.5.2' + testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.6.0' testCompile group: 'org.assertj', name: 'assertj-core', version: '3.16.1' } From 0e6e14725418a99a38c87b05f3e13d87d428bda3 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 20 Jun 2020 21:05:47 +0100 Subject: [PATCH 1460/5615] Bump postgresql from 42.2.13 to 42.2.14 in /examples (#2892) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.13 to 42.2.14. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.13...REL42.2.14) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 79b66e15c57..52d2d58b6e7 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' implementation 'com.squareup.okhttp3:okhttp:3.14.8' implementation 'org.json:json:20180813' - testImplementation 'org.postgresql:postgresql:42.2.13' + testImplementation 'org.postgresql:postgresql:42.2.14' testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.testcontainers:postgresql' } From 1955de455c36008048b3756b5a9623fd0382b13f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 20 Jun 2020 21:06:02 +0100 Subject: [PATCH 1461/5615] Bump aws-java-sdk-s3 from 1.11.802 to 1.11.807 in /modules/localstack (#2894) Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.11.802 to 1.11.807. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.802...1.11.807) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 8a2a372a0f1..12f7dbe7e63 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.802' - testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.797' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.807' + testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.807' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.797' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.797' testCompile 'software.amazon.awssdk:s3:2.13.36' From 7278c496f4a6ad2cfdf79c187efae36fa35324f0 Mon Sep 17 00:00:00 2001 From: Simon Schneider <10846939+raynigon@users.noreply.github.com> Date: Sat, 20 Jun 2020 22:07:37 +0200 Subject: [PATCH 1462/5615] Use codeinclude for Elasticsearch Module (#2828) Co-authored-by: Simon Schneider --- docs/modules/elasticsearch.md | 39 +++-------- .../ElasticsearchContainerTest.java | 69 +++++++++++++++---- 2 files changed, 66 insertions(+), 42 deletions(-) diff --git a/docs/modules/elasticsearch.md b/docs/modules/elasticsearch.md index c7efa872df6..9959d0b7926 100644 --- a/docs/modules/elasticsearch.md +++ b/docs/modules/elasticsearch.md @@ -9,31 +9,11 @@ Note that it's based on the [official Docker image](https://www.elastic.co/guide You can start an elasticsearch container instance from any Java application by using: -```java -// Create the elasticsearch container. -ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:6.4.1"); + +[HttpClient](../../modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java) inside_block:httpClientContainer +[TransportClient](../../modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java) inside_block:transportClientContainer + -// Start the container. This step might take some time... -container.start(); - -// Do whatever you want with the rest client ... -final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); -credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("elastic", "changeme")); -RestClient restClient = RestClient.builder(HttpHost.create(container.getHttpHostAddress())) - .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)) - .build(); -Response response = restClient.performRequest(new Request("GET", "/")); - -// ... or the transport client -TransportAddress transportAddress = new TransportAddress(container.getTcpHost()); -Settings settings = Settings.builder().put("cluster.name", "docker-cluster").build(); -TransportClient transportClient = new PreBuiltTransportClient(settings) - .addTransportAddress(transportAddress); -ClusterHealthResponse healths = transportClient.admin().cluster().prepareHealth().get(); - -// Stop the container. -container.stop(); -``` Note that if you are still using the [TransportClient](https://www.elastic.co/guide/en/elasticsearch/client/java-api/6.3/transport-client.html) (not recommended as it is deprecated), the default cluster name is set to `docker-cluster` so you need to change `cluster.name` setting @@ -41,13 +21,12 @@ or set `client.transport.ignore_cluster_name` to `true`. ## Choose your Elasticsearch license -If you prefer to start a Docker image with the pure OSS version (which means with no security or -other advanced features), you can use this instead: +If you prefer to start a Docker image with the pure OSS version (which means with no security in older versions or +other new and advanced features), you can use this instead: -```java -// Create the elasticsearch container. -ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch-oss:6.4.1"); -``` + +[Elasticsearch OSS](../../modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java) inside_block:oosContainer + ## Adding this module to your project dependencies diff --git a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java index 7aaf49d3af7..5da759872c9 100644 --- a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java +++ b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java @@ -1,5 +1,12 @@ package org.testcontainers.elasticsearch; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; +import static org.testcontainers.elasticsearch.ElasticsearchContainer.ELASTICSEARCH_DEFAULT_VERSION; + +import java.io.IOException; import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; @@ -7,6 +14,7 @@ import org.apache.http.client.CredentialsProvider; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.util.EntityUtils; +import org.elasticsearch.Version; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; import org.elasticsearch.client.Request; import org.elasticsearch.client.Response; @@ -19,16 +27,13 @@ import org.junit.After; import org.junit.Test; -import java.io.IOException; - -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; -import static org.testcontainers.elasticsearch.ElasticsearchContainer.ELASTICSEARCH_DEFAULT_VERSION; - public class ElasticsearchContainerTest { + /** + * Elasticsearch version which should be used for the Tests + */ + private static final String ELASTICSEARCH_VERSION = Version.CURRENT.toString(); + /** * Elasticsearch default username, when secured with a license > basic */ @@ -51,10 +56,14 @@ public void stopRestClient() throws IOException { @Test public void elasticsearchDefaultTest() throws IOException { + // Create the elasticsearch container. try (ElasticsearchContainer container = new ElasticsearchContainer() .withEnv("foo", "bar") // dummy env for compiler checking correct generics usage ) { + // Start the container. This step might take some time... container.start(); + + // Do whatever you want with the rest client ... Response response = getClient(container).performRequest(new Request("GET", "/")); assertThat(response.getStatusLine().getStatusCode(), is(200)); assertThat(EntityUtils.toString(response.getEntity()), containsString(ELASTICSEARCH_DEFAULT_VERSION)); @@ -69,19 +78,22 @@ public void elasticsearchDefaultTest() throws IOException { @Test public void elasticsearchVersion() throws IOException { - try (ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:5.6.12")) { + try (ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:" + ELASTICSEARCH_VERSION)) { container.start(); Response response = getClient(container).performRequest(new Request("GET", "/")); assertThat(response.getStatusLine().getStatusCode(), is(200)); String responseAsString = EntityUtils.toString(response.getEntity()); - assertThat(responseAsString, containsString("5.6.12")); + assertThat(responseAsString, containsString(ELASTICSEARCH_VERSION)); } } @Test public void elasticsearchOssImage() throws IOException { - try (ElasticsearchContainer container = - new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch-oss:" + ELASTICSEARCH_DEFAULT_VERSION)) { + try ( + // oosContainer { + ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch-oss:" + ELASTICSEARCH_VERSION) + // } + ) { container.start(); Response response = getClient(container).performRequest(new Request("GET", "/")); assertThat(response.getStatusLine().getStatusCode(), is(200)); @@ -92,11 +104,41 @@ public void elasticsearchOssImage() throws IOException { } } + @Test + public void restClientClusterHealth() throws IOException { + // httpClientContainer { + // Create the elasticsearch container. + try (ElasticsearchContainer container = new ElasticsearchContainer()) { + // Start the container. This step might take some time... + container.start(); + + // Do whatever you want with the rest client ... + final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials(AuthScope.ANY, + new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ELASTICSEARCH_PASSWORD)); + + client = RestClient.builder(HttpHost.create(container.getHttpHostAddress())) + .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)) + .build(); + + Response response = client.performRequest(new Request("GET", "/_cluster/health")); + // }} + assertThat(response.getStatusLine().getStatusCode(), is(200)); + assertThat(EntityUtils.toString(response.getEntity()), containsString("cluster_name")); + // httpClientContainer {{ + } + // } + } + @Test public void transportClientClusterHealth() { + // transportClientContainer { + // Create the elasticsearch container. try (ElasticsearchContainer container = new ElasticsearchContainer()) { + // Start the container. This step might take some time... container.start(); + // Do whatever you want with the transport client TransportAddress transportAddress = new TransportAddress(container.getTcpHost()); String expectedClusterName = "docker-cluster"; Settings settings = Settings.builder().put("cluster.name", expectedClusterName).build(); @@ -104,9 +146,12 @@ public void transportClientClusterHealth() { .addTransportAddress(transportAddress)) { ClusterHealthResponse healths = transportClient.admin().cluster().prepareHealth().get(); String clusterName = healths.getClusterName(); + // }}} assertThat(clusterName, is(expectedClusterName)); + // transportClientContainer {{{ } } + // } } private RestClient getClient(ElasticsearchContainer container) { From 0053dfe559a90c4d8abb2a296d046d513abf0a08 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 20 Jun 2020 21:12:21 +0100 Subject: [PATCH 1463/5615] Bump aws-java-sdk-dynamodb from 1.11.479 to 1.11.807 in /modules/dynalite (#2905) Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.11.479 to 1.11.807. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.479...1.11.807) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/dynalite/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 32d522aa964..7101f40552f 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Dynalite" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.479' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.807' testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.479' } From 924d36f9773fefc3321f6d3e23d97fea8852ff8e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 20 Jun 2020 21:13:44 +0100 Subject: [PATCH 1464/5615] Bump me.champeau.gradle.japicmp from 0.2.8 to 0.2.9 in /core (#2900) Bumps me.champeau.gradle.japicmp from 0.2.8 to 0.2.9. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index deabf16e90d..69788d1f0de 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -10,7 +10,7 @@ buildscript { } plugins { - id 'me.champeau.gradle.japicmp' version '0.2.8' + id 'me.champeau.gradle.japicmp' version '0.2.9' } apply plugin: 'com.github.johnrengelman.shadow' From e61bff8675847eec9db1d626ece77b588c70ad80 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 21 Jun 2020 09:50:05 +0100 Subject: [PATCH 1465/5615] Bump aws-java-sdk-sqs from 1.11.797 to 1.11.807 in /modules/localstack (#2896) Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/localstack/build.gradle | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 12f7dbe7e63..d90e8e1611a 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,9 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.807' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.807' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.797' - testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.797' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.807' + testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' testCompile 'software.amazon.awssdk:s3:2.13.36' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.797' - testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.797' } From f358ebfcfa36c84ef50707767d4beaa644e7a070 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Jun 2020 09:51:51 +0100 Subject: [PATCH 1466/5615] Bump selenium-remote-driver from 3.14.0 to 3.141.59 in /examples (#2913) Bumps [selenium-remote-driver](https://github.com/SeleniumHQ/selenium) from 3.14.0 to 3.141.59. - [Release notes](https://github.com/SeleniumHQ/selenium/releases) - [Commits](https://github.com/SeleniumHQ/selenium/compare/selenium-3.14.0...selenium-3.141.59) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index dfd80dc16c2..2131c5766e8 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.14.0' + implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' testImplementation 'io.cucumber:cucumber-java:5.7.0' From 0cfa031297e38fd818af8add256dc08f7bd0928d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Jun 2020 09:52:05 +0100 Subject: [PATCH 1467/5615] Bump selenium-chrome-driver from 3.14.0 to 3.141.59 in /examples (#2914) Bumps [selenium-chrome-driver](https://github.com/SeleniumHQ/selenium) from 3.14.0 to 3.141.59. - [Release notes](https://github.com/SeleniumHQ/selenium/releases) - [Commits](https://github.com/SeleniumHQ/selenium/compare/selenium-3.14.0...selenium-3.141.59) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 2131c5766e8..e4c44c0e063 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -9,7 +9,7 @@ repositories { dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' - implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' + implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' testImplementation 'io.cucumber:cucumber-java:5.7.0' testImplementation 'io.cucumber:cucumber-junit:6.1.1' testImplementation 'org.testcontainers:selenium' From 043d9ea2e2add78a767c241cbb04f6494071dee5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Jun 2020 09:52:25 +0100 Subject: [PATCH 1468/5615] Bump okhttp from 3.14.8 to 4.7.2 in /examples (#2915) Bumps [okhttp](https://github.com/square/okhttp) from 3.14.8 to 4.7.2. - [Release notes](https://github.com/square/okhttp/releases) - [Changelog](https://github.com/square/okhttp/blob/master/CHANGELOG.md) - [Commits](https://github.com/square/okhttp/compare/parent-3.14.8...parent-4.7.2) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 52d2d58b6e7..f56b8bc46d8 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' - implementation 'com.squareup.okhttp3:okhttp:3.14.8' + implementation 'com.squareup.okhttp3:okhttp:4.7.2' implementation 'org.json:json:20180813' testImplementation 'org.postgresql:postgresql:42.2.14' testImplementation 'ch.qos.logback:logback-classic:1.2.3' From 98f5e9bb88de667b5b7ad5d548d0f4901776821e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Jun 2020 19:43:37 +0100 Subject: [PATCH 1469/5615] Bump s3 from 2.13.36 to 2.13.41 in /modules/localstack (#2907) Bumps s3 from 2.13.36 to 2.13.41. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index d90e8e1611a..a04c04a4751 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,5 +7,5 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.807' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.807' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' - testCompile 'software.amazon.awssdk:s3:2.13.36' + testCompile 'software.amazon.awssdk:s3:2.13.41' } From 859f29d205f88404d7ec290cdaf6e4fb7b8b6ca2 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 23 Jun 2020 15:11:09 +0100 Subject: [PATCH 1470/5615] Upgrade docker-java to 3.2.4 (#2924) Primarily to incorporate https://github.com/docker-java/docker-java/pull/1420 and improve build stability issues seen recently. --- core/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index 69788d1f0de..0e0a41f4914 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -157,14 +157,14 @@ dependencies { compile "net.java.dev.jna:jna-platform:5.5.0" - shaded ('com.github.docker-java:docker-java-transport-okhttp:3.2.3') { + shaded ('com.github.docker-java:docker-java-transport-okhttp:3.2.4') { exclude(group: 'net.java.dev.jna') exclude(group: 'com.google.code.findbug') exclude(group: 'org.slf4j') exclude(group: 'org.apache.commons', module: 'commons-compress') } - shaded ('com.github.docker-java:docker-java-transport-zerodep:3.2.3') { + shaded ('com.github.docker-java:docker-java-transport-zerodep:3.2.4') { exclude(group: 'net.java.dev.jna') exclude(group: 'org.slf4j') } From fc03c217b9ebfbc7e6db82bcdd3b54a159580cd9 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 23 Jun 2020 17:59:12 +0100 Subject: [PATCH 1471/5615] Pin registry image used for testing (#2926) As a workaround for https://github.com/docker/distribution-library-image/issues/107 --- .../org/testcontainers/utility/AuthenticatedImagePullTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java index 31f098bab16..3986255f248 100644 --- a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java +++ b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java @@ -45,7 +45,7 @@ public class AuthenticatedImagePullTest { @ClassRule public static GenericContainer authenticatedRegistry = new GenericContainer<>(new ImageFromDockerfile() .withDockerfileFromBuilder(builder -> { - builder.from("registry:2") + builder.from("registry:2.7.0") .run("htpasswd -Bbn testuser notasecret > /htpasswd") .env("REGISTRY_AUTH", "htpasswd") .env("REGISTRY_AUTH_HTPASSWD_PATH", "/htpasswd") From 3f64d0c9eac4b500730074fb40a20b9a689c6b65 Mon Sep 17 00:00:00 2001 From: Simon Schneider <10846939+raynigon@users.noreply.github.com> Date: Tue, 23 Jun 2020 19:53:54 +0200 Subject: [PATCH 1472/5615] Convert Docs for the Spock Testframework (#2928) Convert inline example code to use codeinclude macro for the Spock Testframework documentation See #1158 for more details. --- docs/test_framework_integration/spock.md | 34 ++----------------- .../spock/PostgresContainerIT.groovy | 2 ++ 2 files changed, 5 insertions(+), 31 deletions(-) diff --git a/docs/test_framework_integration/spock.md b/docs/test_framework_integration/spock.md index bbe06a43e80..8528faf4490 100644 --- a/docs/test_framework_integration/spock.md +++ b/docs/test_framework_integration/spock.md @@ -9,37 +9,9 @@ Specifying the `@Testcontainers` annotation will instruct Spock to start and stop all testcontainers accordingly. This annotation can be mixed with Spock's `@Shared` annotation to indicate, that containers shouldn't be restarted between tests. -```groovy -@Testcontainers -class DatabaseTest extends Specification { - - @Shared - PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer() - .withDatabaseName("foo") - .withUsername("foo") - .withPassword("secret") - - def "database is accessible"() { - - given: "a jdbc connection" - HikariConfig hikariConfig = new HikariConfig() - hikariConfig.setJdbcUrl(postgreSQLContainer.jdbcUrl) - hikariConfig.setUsername("foo") - hikariConfig.setPassword("secret") - HikariDataSource ds = new HikariDataSource(hikariConfig) - - when: "querying the database" - Statement statement = ds.getConnection().createStatement() - statement.execute("SELECT 1") - ResultSet resultSet = statement.getResultSet() - resultSet.next() - - then: "result is returned" - int resultSetInt = resultSet.getInt(1) - resultSetInt == 1 - } -} -``` + +[PostgresContainerIT](../../modules/spock/src/test/groovy/org/testcontainers/spock/PostgresContainerIT.groovy) inside_block:PostgresContainerIT + ## Adding Testcontainers Spock support to your project dependencies diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/PostgresContainerIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/PostgresContainerIT.groovy index 6e090e77c33..4ac095bed6f 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/PostgresContainerIT.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/PostgresContainerIT.groovy @@ -9,6 +9,7 @@ import spock.lang.Specification import java.sql.ResultSet import java.sql.Statement +// PostgresContainerIT { @Testcontainers class PostgresContainerIT extends Specification { @@ -42,3 +43,4 @@ class PostgresContainerIT extends Specification { } } +// } From 53e02a5f5ff1d8db7d9ab04890e62ae25e639066 Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 24 Jun 2020 08:32:00 +0100 Subject: [PATCH 1473/5615] Run correct subset of docker compose containers when `withServices`/`withScaledService` used (#2922) Co-authored-by: Afshin --- .../containers/DockerComposeContainer.java | 115 ++++++++++-------- ...ockerComposeContainerWithServicesTest.java | 107 ++++++++++++++++ .../compose-scaling-multiple-containers.yml | 7 ++ .../compose-with-inline-scale-test.yml | 5 + 4 files changed, 184 insertions(+), 50 deletions(-) create mode 100644 core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java create mode 100644 core/src/test/resources/compose-scaling-multiple-containers.yml create mode 100644 core/src/test/resources/compose-with-inline-scale-test.yml diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 651bd9c8526..c3fe9ab17f9 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -2,9 +2,9 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.model.Container; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.base.Splitter; -import com.google.common.base.Strings; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.util.concurrent.Uninterruptibles; @@ -56,6 +56,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Strings.isNullOrEmpty; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; import static org.testcontainers.containers.BindMode.READ_ONLY; @@ -196,23 +197,36 @@ public SELF withServices(@NonNull String... services) { } private void createServices() { - // Apply scaling - final String servicesWithScalingSettings = Stream.concat(services.stream(), scalingPreferences.keySet().stream()) - .map(service -> "--scale " + service + "=" + scalingPreferences.getOrDefault(service, 1)) + // services that have been explicitly requested to be started. If empty, all services should be started. + final String serviceNameArgs = Stream.concat( + services.stream(), // services that have been specified with `withServices` + scalingPreferences.keySet().stream() // services that are implicitly needed via `withScaledService` + ) + .distinct() .collect(joining(" ")); - String flags = "-d"; + // Apply scaling for the services specified using `withScaledService` + final String scalingOptions = scalingPreferences.entrySet().stream() + .map(entry -> "--scale " + entry.getKey() + "=" + entry.getValue()) + .distinct() + .collect(joining(" ")); + + String command = "up -d"; if (build) { - flags += " --build"; + command += " --build"; } - // Run the docker-compose container, which starts up the services - if(Strings.isNullOrEmpty(servicesWithScalingSettings)) { - runWithCompose("up " + flags); - } else { - runWithCompose("up " + flags + " " + servicesWithScalingSettings); + if (!isNullOrEmpty(scalingOptions)) { + command += " " + scalingOptions; + } + + if (!isNullOrEmpty(serviceNameArgs)) { + command += " " + serviceNameArgs; } + + // Run the docker-compose container, which starts up the services + runWithCompose(command); } private void waitUntilServiceStarted() { @@ -250,7 +264,7 @@ private void createServiceInstance(Container container) { private void waitUntilServiceStarted(String serviceName, ComposeServiceWaitStrategyTarget serviceInstance) { final WaitAllStrategy waitAllStrategy = waitStrategyMap.get(serviceName); - if(waitAllStrategy != null) { + if (waitAllStrategy != null) { waitAllStrategy.waitUntilReady(serviceInstance); } } @@ -273,24 +287,25 @@ private void runWithCompose(String cmd) { } dockerCompose - .withCommand(cmd) - .withEnv(env) - .invoke(); + .withCommand(cmd) + .withEnv(env) + .invoke(); } private void registerContainersForShutdown() { ResourceReaper.instance().registerFilterForCleanup(Arrays.asList( - new SimpleEntry<>("label", "com.docker.compose.project=" + project) + new SimpleEntry<>("label", "com.docker.compose.project=" + project) )); } - private List listChildContainers() { + @VisibleForTesting + List listChildContainers() { return dockerClient.listContainersCmd() - .withShowAll(true) - .exec().stream() - .filter(container -> Arrays.stream(container.getNames()).anyMatch(name -> - name.startsWith("/" + project))) - .collect(toList()); + .withShowAll(true) + .exec().stream() + .filter(container -> Arrays.stream(container.getNames()).anyMatch(name -> + name.startsWith("/" + project))) + .collect(toList()); } private void startAmbassadorContainers() { @@ -378,12 +393,12 @@ private void addWaitStrategy(String serviceInstanceName, @NonNull WaitStrategy w } /** - Specify the {@link WaitStrategy} to use to determine if the container is ready. + * Specify the {@link WaitStrategy} to use to determine if the container is ready. * - * @see org.testcontainers.containers.wait.strategy.Wait#defaultWaitStrategy() - * @param serviceName the name of the service to wait for + * @param serviceName the name of the service to wait for * @param waitStrategy the WaitStrategy to use * @return this + * @see org.testcontainers.containers.wait.strategy.Wait#defaultWaitStrategy() */ public SELF waitingFor(String serviceName, @NonNull WaitStrategy waitStrategy) { String serviceInstanceName = getServiceInstanceName(serviceName); @@ -420,8 +435,8 @@ public Integer getServicePort(String serviceName, Integer servicePort) { if (portMap == null) { throw new IllegalArgumentException("Could not get a port for '" + serviceName + "'. " + - "Testcontainers does not have an exposed port configured for '" + serviceName + "'. "+ - "To fix, please ensure that the service '" + serviceName + "' has ports exposed using .withExposedService(...)"); + "Testcontainers does not have an exposed port configured for '" + serviceName + "'. " + + "To fix, please ensure that the service '" + serviceName + "' has ports exposed using .withExposedService(...)"); } else { return ambassadorContainer.getMappedPort(portMap.get(servicePort)); } @@ -479,7 +494,7 @@ public SELF withTailChildContainers(boolean tailChildContainers) { * More than one consumer may be registered. * * @param serviceName the name of the service as set in the docker-compose.yml file - * @param consumer consumer that output frames should be sent to + * @param consumer consumer that output frames should be sent to * @return this instance, for chaining */ public SELF withLogConsumer(String serviceName, Consumer consumer) { @@ -579,10 +594,10 @@ public ContainerisedDockerCompose(List composeFiles, String identifier) { final String containerPwd = MountableFile.forHostPath(pwd).getFilesystemPath(); final List absoluteDockerComposeFiles = composeFiles.stream() - .map(File::getAbsolutePath) - .map(MountableFile::forHostPath) - .map(MountableFile::getFilesystemPath) - .collect(toList()); + .map(File::getAbsolutePath) + .map(MountableFile::forHostPath) + .map(MountableFile::getFilesystemPath) + .collect(toList()); final String composeFileEnvVariableValue = Joiner.on(UNIX_PATH_SEPERATOR).join(absoluteDockerComposeFiles); // we always need the UNIX path separator logger().debug("Set env COMPOSE_FILE={}", composeFileEnvVariableValue); addEnv(ENV_COMPOSE_FILE, composeFileEnvVariableValue); @@ -600,8 +615,8 @@ public ContainerisedDockerCompose(List composeFiles, String identifier) { private String getDockerSocketHostPath() { return SystemUtils.IS_OS_WINDOWS - ? "/" + DOCKER_SOCKET_PATH - : DOCKER_SOCKET_PATH; + ? "/" + DOCKER_SOCKET_PATH + : DOCKER_SOCKET_PATH; } @Override @@ -621,16 +636,16 @@ public void invoke() { AuditLogger.doComposeLog(this.getCommandParts(), this.getEnv()); final Integer exitCode = this.dockerClient.inspectContainerCmd(getContainerId()) - .exec() - .getState() - .getExitCode(); + .exec() + .getState() + .getExitCode(); if (exitCode == null || exitCode != 0) { throw new ContainerLaunchException( - "Containerised Docker Compose exited abnormally with code " + - exitCode + - " whilst running command: " + - StringUtils.join(this.getCommandParts(), ' ')); + "Containerised Docker Compose exited abnormally with code " + + exitCode + + " whilst running command: " + + StringUtils.join(this.getCommandParts(), ' ')); } } } @@ -691,23 +706,23 @@ public void invoke() { logger().info("Local Docker Compose is running command: {}", cmd); final List command = Splitter.onPattern(" ") - .omitEmptyStrings() - .splitToList(COMPOSE_EXECUTABLE + " " + cmd); + .omitEmptyStrings() + .splitToList(COMPOSE_EXECUTABLE + " " + cmd); try { new ProcessExecutor().command(command) - .redirectOutput(Slf4jStream.of(logger()).asInfo()) - .redirectError(Slf4jStream.of(logger()).asInfo()) // docker-compose will log pull information to stderr - .environment(environment) - .directory(pwd) - .exitValueNormal() - .executeNoTimeout(); + .redirectOutput(Slf4jStream.of(logger()).asInfo()) + .redirectError(Slf4jStream.of(logger()).asInfo()) // docker-compose will log pull information to stderr + .environment(environment) + .directory(pwd) + .exitValueNormal() + .executeNoTimeout(); logger().info("Docker Compose has finished running"); } catch (InvalidExitValueException e) { throw new ContainerLaunchException("Local Docker Compose exited abnormally with code " + - e.getExitValue() + " whilst running command: " + cmd); + e.getExitValue() + " whilst running command: " + cmd); } catch (Exception e) { throw new ContainerLaunchException("Error running local Docker Compose command: " + cmd, e); diff --git a/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java b/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java new file mode 100644 index 00000000000..8927972e642 --- /dev/null +++ b/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java @@ -0,0 +1,107 @@ +package org.testcontainers.containers; + +import org.junit.Test; + +import java.io.File; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; + + +public class DockerComposeContainerWithServicesTest { + + public static final File SIMPLE_COMPOSE_FILE = new File("src/test/resources/compose-scaling-multiple-containers.yml"); + public static final File COMPOSE_FILE_WITH_INLINE_SCALE = new File("src/test/resources/compose-with-inline-scale-test.yml"); + + @Test + public void testDesiredSubsetOfServicesAreStarted() { + try ( + DockerComposeContainer compose = new DockerComposeContainer<>(SIMPLE_COMPOSE_FILE) + .withServices("redis") + ) { + compose.start(); + + verifyStartedContainers(compose, "redis_1"); + } + } + + @Test + public void testDesiredSubsetOfScaledServicesAreStarted() { + try ( + DockerComposeContainer compose = new DockerComposeContainer<>(SIMPLE_COMPOSE_FILE) + .withScaledService("redis", 2) + ) { + compose.start(); + + verifyStartedContainers(compose, "redis_1", "redis_2"); + } + } + + @Test + public void testDesiredSubsetOfSpecifiedAndScaledServicesAreStarted() { + try ( + DockerComposeContainer compose = new DockerComposeContainer<>(SIMPLE_COMPOSE_FILE) + .withServices("redis") + .withScaledService("redis", 2) + ) { + compose.start(); + + verifyStartedContainers(compose, "redis_1", "redis_2"); + } + } + + @Test + public void testDesiredSubsetOfSpecifiedOrScaledServicesAreStarted() { + try ( + DockerComposeContainer compose = new DockerComposeContainer<>(SIMPLE_COMPOSE_FILE) + .withServices("other") + .withScaledService("redis", 2) + ) { + compose.start(); + + verifyStartedContainers(compose, "redis_1", "redis_2", "other_1"); + } + } + + @Test + public void testAllServicesAreStartedIfNotSpecified() { + try ( + DockerComposeContainer compose = new DockerComposeContainer<>(SIMPLE_COMPOSE_FILE) + ) { + compose.start(); + + verifyStartedContainers(compose, "redis_1", "other_1"); + } + } + + @Test + public void testScaleInComposeFileIsRespected() { + try ( + DockerComposeContainer compose = new DockerComposeContainer<>(COMPOSE_FILE_WITH_INLINE_SCALE) + ) { + compose.start(); + + // the compose file includes `scale: 3` for the redis container + verifyStartedContainers(compose, "redis_1", "redis_2", "redis_3"); + } + } + + private void verifyStartedContainers(final DockerComposeContainer compose, final String... names) { + final List containerNames = compose.listChildContainers().stream() + .flatMap(container -> Stream.of(container.getNames())) + .collect(Collectors.toList()); + + assertEquals("number of running services of docker-compose is the same as length of listOfServices", + names.length, containerNames.size()); + + for (final String expectedName : names) { + final long matches = containerNames.stream() + .filter(foundName -> foundName.endsWith(expectedName)) + .count(); + + assertEquals("container with name starting '" + expectedName + "' should be running", 1L, matches); + } + } +} diff --git a/core/src/test/resources/compose-scaling-multiple-containers.yml b/core/src/test/resources/compose-scaling-multiple-containers.yml new file mode 100644 index 00000000000..da5ac6f3a07 --- /dev/null +++ b/core/src/test/resources/compose-scaling-multiple-containers.yml @@ -0,0 +1,7 @@ +version: '2.4' +services: + redis: + image: redis + other: + image: alpine:3.5 + command: sleep 10000 diff --git a/core/src/test/resources/compose-with-inline-scale-test.yml b/core/src/test/resources/compose-with-inline-scale-test.yml new file mode 100644 index 00000000000..f2e7e8c1a79 --- /dev/null +++ b/core/src/test/resources/compose-with-inline-scale-test.yml @@ -0,0 +1,5 @@ +version: '2.4' +services: + redis: + image: redis + scale: 3 # legacy mechanism to specify scale From f07f31ec49fdfe02d0fc1a6c2674b62b08510736 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 25 Jun 2020 12:11:08 +0200 Subject: [PATCH 1474/5615] Continue on ciMate's error (#2933) --- .github/workflows/ci-docker-wormhole.yml | 1 + .github/workflows/ci-examples.yml | 1 + .github/workflows/ci.yml | 2 ++ 3 files changed, 4 insertions(+) diff --git a/.github/workflows/ci-docker-wormhole.yml b/.github/workflows/ci-docker-wormhole.yml index 3c6da54dc1f..f9e106d56b7 100644 --- a/.github/workflows/ci-docker-wormhole.yml +++ b/.github/workflows/ci-docker-wormhole.yml @@ -20,6 +20,7 @@ jobs: ./gradlew --no-daemon --continue --scan testcontainers:test --tests '*GenericContainerRuleTest' - name: aggregate test reports with ciMate if: always() + continue-on-error: true env: CIMATE_PROJECT_ID: 2348n4vl run: | diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index 8dab14e831c..d81a4ffb509 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -61,6 +61,7 @@ jobs: ./gradlew --no-daemon --continue --scan --info ${{matrix.gradle_args}} - name: Aggregate Examples test reports with ciMate if: always() + continue-on-error: true env: CIMATE_PROJECT_ID: 2348n4vl CIMATE_CI_KEY: "Examples / ${{ runner.os }} / ${{matrix.gradle_args}}" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e2ca5d35daa..b965eb77dc9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,6 +59,7 @@ jobs: ./gradlew --no-daemon --continue --scan --info ${{matrix.gradle_args}} - name: Aggregate test reports with ciMate if: always() + continue-on-error: true env: CIMATE_PROJECT_ID: 2348n4vl CIMATE_CI_KEY: "CI / ${{ runner.os }} / ${{matrix.gradle_args}}" @@ -77,6 +78,7 @@ jobs: ./gradlew --no-daemon --scan testcontainers:test --tests '*EventStreamTest' - name: aggregate test reports with ciMate if: always() + continue-on-error: true env: CIMATE_PROJECT_ID: 2348n4vl CIMATE_CI_KEY: "CI / Apache HttpClient5" From d09491bdc2fa1dd50c12c66650a2928642d0a037 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 25 Jun 2020 12:17:51 +0200 Subject: [PATCH 1475/5615] Update to `docker-java` 3.2.5 (#2938) --- core/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index 0e0a41f4914..e1fb724597e 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -157,14 +157,14 @@ dependencies { compile "net.java.dev.jna:jna-platform:5.5.0" - shaded ('com.github.docker-java:docker-java-transport-okhttp:3.2.4') { + shaded ('com.github.docker-java:docker-java-transport-okhttp:3.2.5') { exclude(group: 'net.java.dev.jna') exclude(group: 'com.google.code.findbug') exclude(group: 'org.slf4j') exclude(group: 'org.apache.commons', module: 'commons-compress') } - shaded ('com.github.docker-java:docker-java-transport-zerodep:3.2.4') { + shaded ('com.github.docker-java:docker-java-transport-zerodep:3.2.5') { exclude(group: 'net.java.dev.jna') exclude(group: 'org.slf4j') } From 60537db11726a278d0259dd4b08771910278814d Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 25 Jun 2020 11:58:20 +0100 Subject: [PATCH 1476/5615] Pin version of RabbitMQ image to `3.7.25-management-alpine` (#2941) --- .../java/org/testcontainers/containers/RabbitMQContainer.java | 2 +- .../org/testcontainers/containers/RabbitMQContainerTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java index 49423201d14..d8d46f51b1c 100644 --- a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java +++ b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java @@ -28,7 +28,7 @@ public class RabbitMQContainer extends GenericContainer { * The image defaults to the official RabbitmQ image: RabbitMQ. */ private static final String DEFAULT_IMAGE_NAME = "rabbitmq"; - private static final String DEFAULT_TAG = "3.7-management-alpine"; + private static final String DEFAULT_TAG = "3.7.25-management-alpine"; private static final int DEFAULT_AMQP_PORT = 5672; private static final int DEFAULT_AMQPS_PORT = 5671; diff --git a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java index 27ac38f55ce..d8e9c7658c0 100644 --- a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java +++ b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java @@ -31,7 +31,7 @@ */ public class RabbitMQContainerTest { - public static final String DEFAULT_IMAGE = "rabbitmq:3.7-management-alpine"; + public static final String DEFAULT_IMAGE = "rabbitmq:3.7.25-management-alpine"; public static final int DEFAULT_AMQPS_PORT = 5671; public static final int DEFAULT_AMQP_PORT = 5672; public static final int DEFAULT_HTTPS_PORT = 15671; From a4d67a68d37b1fbd804daa513c9b93fb4d3cb05c Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 25 Jun 2020 12:58:49 +0200 Subject: [PATCH 1477/5615] Delete pom.xml (#2937) --- examples/singleton-container/pom.xml | 43 ---------------------------- 1 file changed, 43 deletions(-) delete mode 100644 examples/singleton-container/pom.xml diff --git a/examples/singleton-container/pom.xml b/examples/singleton-container/pom.xml deleted file mode 100644 index 710c32d6b80..00000000000 --- a/examples/singleton-container/pom.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - testcontainers-java-examples - org.testcontainers.examples - NOVERSION - - 4.0.0 - - singleton-container - - - - redis.clients - jedis - 3.0.1 - - - com.google.code.gson - gson - 2.8.5 - - - com.google.guava - guava - 23.0 - - - org.slf4j - slf4j-api - 1.7.25 - provided - - - ch.qos.logback - logback-classic - 1.2.3 - test - - - From 22c64030140f9c67023be498faa795709c00ebcd Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 25 Jun 2020 22:38:30 +0200 Subject: [PATCH 1478/5615] Cache Ryuk's failure to prevent the double start (#2935) see #2395 --- .../org/testcontainers/DockerClientFactory.java | 17 +++++++++++------ .../testcontainers/DockerClientFactoryTest.java | 6 +++--- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 50c0f3d021c..95e0c9838ba 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -66,7 +66,7 @@ public class DockerClientFactory { DockerClient dockerClient; @VisibleForTesting - RuntimeException cachedChecksFailure; + RuntimeException cachedClientFailure; private String activeApiVersion; private String activeExecutionDriver; @@ -139,9 +139,9 @@ public DockerClient client() { } // fail-fast if checks have failed previously - if (cachedChecksFailure != null) { - log.debug("There is a cached checks failure - throwing", cachedChecksFailure); - throw cachedChecksFailure; + if (cachedClientFailure != null) { + log.debug("There is a cached checks failure - throwing", cachedClientFailure); + throw cachedClientFailure; } final DockerClientProviderStrategy strategy = getOrInitializeStrategy(); @@ -170,7 +170,12 @@ public void close() { boolean useRyuk = !Boolean.parseBoolean(System.getenv("TESTCONTAINERS_RYUK_DISABLED")); if (useRyuk) { log.debug("Ryuk is enabled"); - ryukContainerId = ResourceReaper.start(hostIpAddress, client); + try { + ryukContainerId = ResourceReaper.start(hostIpAddress, client); + } catch (RuntimeException e) { + cachedClientFailure = e; + throw e; + } log.info("Ryuk started - will monitor and terminate Testcontainers containers on JVM exit"); } else { log.debug("Ryuk is disabled"); @@ -201,7 +206,7 @@ public void close() { ); } } catch (RuntimeException e) { - cachedChecksFailure = e; + cachedClientFailure = e; throw e; } } else { diff --git a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java index 60fd69e9fd3..04edd407959 100644 --- a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java +++ b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java @@ -71,7 +71,7 @@ public void failedChecksFailFast() { DockerClientFactory instance = new DockerClientFactory(); DockerClient dockerClient = instance.dockerClient; - assertThat(instance.cachedChecksFailure).isNull(); + assertThat(instance.cachedClientFailure).isNull(); try { // Remove cached client to force the initialization logic instance.dockerClient = null; @@ -80,12 +80,12 @@ public void failedChecksFailFast() { assertThatThrownBy(instance::client).isInstanceOf(DockerException.class); RuntimeException failure = new IllegalStateException("Boom!"); - instance.cachedChecksFailure = failure; + instance.cachedClientFailure = failure; // Fail fast assertThatThrownBy(instance::client).isEqualTo(failure); } finally { instance.dockerClient = dockerClient; - instance.cachedChecksFailure = null; + instance.cachedClientFailure = null; } } } From f4400a09ef3fef2d98e1fa0085508a8989ab8662 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 25 Jun 2020 22:39:00 +0200 Subject: [PATCH 1479/5615] Do not resolve `LazyFuture` from `RemoteDockerImage#toString` (#2930) --- .../testcontainers/utility/LazyFuture.java | 30 +++++++++++-------- .../images/RemoteDockerImageTest.java | 30 +++++++++++++++++++ 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/LazyFuture.java b/core/src/main/java/org/testcontainers/utility/LazyFuture.java index b0aa4346cbb..758082f5185 100644 --- a/core/src/main/java/org/testcontainers/utility/LazyFuture.java +++ b/core/src/main/java/org/testcontainers/utility/LazyFuture.java @@ -2,10 +2,10 @@ import lombok.AccessLevel; import lombok.Getter; -import lombok.experimental.Delegate; import org.rnorth.ducttape.timeouts.Timeouts; import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicReference; /** * Future implementation with lazy result evaluation in the same Thread as caller. @@ -14,31 +14,37 @@ */ public abstract class LazyFuture implements Future { - @Delegate(excludes = Excludes.class) - private final Future delegate = CompletableFuture.completedFuture(null); - @Getter(value = AccessLevel.MODULE, lazy = true) private final T resolvedValue = resolve(); abstract protected T resolve(); @Override - public T get() throws InterruptedException, ExecutionException { + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isDone() { + return ((AtomicReference) resolvedValue).get() != null; + } + + @Override + public T get() { return getResolvedValue(); } @Override - public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + public T get(long timeout, TimeUnit unit) throws TimeoutException { try { return Timeouts.getWithTimeout((int) timeout, unit, this::get); } catch (org.rnorth.ducttape.TimeoutException e) { throw new TimeoutException(e.getMessage()); } } - - private interface Excludes { - T get(); - - T get(long timeout, TimeUnit unit); - } } diff --git a/core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java b/core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java index c62f15653b3..6b6a82559bf 100644 --- a/core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java +++ b/core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java @@ -6,8 +6,11 @@ import org.junit.Test; import org.testcontainers.utility.Base58; +import org.testcontainers.utility.LazyFuture; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicBoolean; public class RemoteDockerImageTest { @@ -44,4 +47,31 @@ public void toStringDoesntResolveImageNameFuture() { imageNameFuture.complete(imageName); assertThat(remoteDockerImage.toString(), containsString("imageName=" + imageName)); } + + @Test(timeout=5000L) + public void toStringDoesntResolveLazyFuture() throws Exception { + String imageName = Base58.randomString(8).toLowerCase(); + AtomicBoolean resolved = new AtomicBoolean(false); + Future imageNameFuture = new LazyFuture() { + @Override + protected String resolve() { + resolved.set(true); + return imageName; + } + }; + + // verify that we've set up the test properly + assertFalse(imageNameFuture.isDone()); + + RemoteDockerImage remoteDockerImage = new RemoteDockerImage(imageNameFuture); + assertThat(remoteDockerImage.toString(), containsString("imageName=")); + + // Make sure the act of calling toString doesn't resolve the imageNameFuture + assertFalse(imageNameFuture.isDone()); + assertFalse(resolved.get()); + + // Trigger resolve + imageNameFuture.get(); + assertThat(remoteDockerImage.toString(), containsString("imageName=" + imageName)); + } } From 6a005390d670632d63cef2c6bf8939cf76725341 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 26 Jun 2020 10:17:51 +0200 Subject: [PATCH 1480/5615] Remove implicit network in `KafkaContainer` (#2932) --- .../containers/KafkaContainer.java | 23 ------------------- .../containers/KafkaContainerTest.java | 22 ------------------ 2 files changed, 45 deletions(-) diff --git a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java index bb92f85b03c..9746849be29 100644 --- a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java +++ b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java @@ -29,8 +29,6 @@ public class KafkaContainer extends GenericContainer { private int port = PORT_NOT_ASSIGNED; - private boolean useImplicitNetwork = true; - public KafkaContainer() { this("5.2.1"); } @@ -38,7 +36,6 @@ public KafkaContainer() { public KafkaContainer(String confluentPlatformVersion) { super(TestcontainersConfiguration.getInstance().getKafkaImage() + ":" + confluentPlatformVersion); - super.withNetwork(Network.SHARED); withExposedPorts(KAFKA_PORT); // Use two listeners with different names, it will force Kafka to communicate with itself via internal @@ -54,26 +51,6 @@ public KafkaContainer(String confluentPlatformVersion) { withEnv("KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS", "0"); } - @Override - public KafkaContainer withNetwork(Network network) { - useImplicitNetwork = false; - return super.withNetwork(network); - } - - @Override - public Network getNetwork() { - if (useImplicitNetwork) { - // TODO Only for backward compatibility, to be removed soon - logger().warn( - "Deprecation warning! " + - "KafkaContainer#getNetwork without an explicitly set network. " + - "Consider using KafkaContainer#withNetwork", - new Exception("Deprecated method") - ); - } - return super.getNetwork(); - } - public KafkaContainer withEmbeddedZookeeper() { externalZookeeperConnect = null; return self(); diff --git a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java index 1490caa3328..c5d719295ed 100644 --- a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java +++ b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java @@ -31,28 +31,6 @@ public void testUsage() throws Exception { } } - /** - * @deprecated the {@link Network} should be set explicitly with {@link KafkaContainer#withNetwork(Network)}. - */ - @Test - @Deprecated - public void testExternalZookeeperWithKafkaNetwork() throws Exception { - try ( - KafkaContainer kafka = new KafkaContainer() - .withExternalZookeeper("zookeeper:2181"); - - GenericContainer zookeeper = new GenericContainer("confluentinc/cp-zookeeper:4.0.0") - .withNetwork(kafka.getNetwork()) - .withNetworkAliases("zookeeper") - .withEnv("ZOOKEEPER_CLIENT_PORT", "2181"); - ) { - zookeeper.start(); - kafka.start(); - - testKafkaFunctionality(kafka.getBootstrapServers()); - } - } - @Test public void testExternalZookeeperWithExternalNetwork() throws Exception { try ( From bde786cd0b22ef03ca99bbd557264310f9da4ef3 Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 29 Jun 2020 21:36:48 +0100 Subject: [PATCH 1481/5615] Add initial bounty policy documentation. (#2954) --- CONTRIBUTING.md | 8 ++++++++ docs/bounty.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ mkdocs.yml | 1 + 3 files changed, 60 insertions(+) create mode 100644 docs/bounty.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ffa95a477db..83afa8a9ea8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,3 +3,11 @@ Please see the [main contributing guidelines](./docs/contributing.md). There are additional docs describing [contributing documentation changes](./docs/contributing_docs.md). + +### GitHub Sponsorship + +Testcontainers is [in the GitHub Sponsors program](https://github.com/sponsors/testcontainers)! + +This repository is supported by our sponsors, meaning that issues are eligible to have a 'bounty' attached to them by sponsors. + +Please see [the bounty policy page](https://testcontainers.org/bounty) if you are interested, either as a sponsor or as a contributor. \ No newline at end of file diff --git a/docs/bounty.md b/docs/bounty.md new file mode 100644 index 00000000000..56ee501062c --- /dev/null +++ b/docs/bounty.md @@ -0,0 +1,51 @@ +# Testcontainers issue bounty policy + +## General + +We want to use issue bounties to encourage contributions in areas that are important to our sponsors, or tricky to solve. +This includes bug fixes and new features. +We hope that this will provide incentives to tackle issues, and gives sponsors a way to influence where development time is expended. +We also want to reward our contributors, some of whom make huge efforts to improve Testcontainers and help their fellow developers! + +!!! note + It's early days for our use of sponsorship, so we expect to evolve this policy over time, possibly without notice. In the event of any ambiguity or dispute, the [Testcontainers org core maintainers](#organisation-core-maintainers) have the final say. + + If you'd like to suggest an improvement to this policy, we'd be grateful for your input - please raise a pull request! + +## For Sponsors + +Sponsors will be able to create a number of 'bounties' per month, varying according to sponsorship tier. + +As a sponsor, the process for creating a bounty is as follows: + +* Raise an issue, or find an existing issue that describes the bug or feature. +* Start a discussion with the [Testcontainers org core maintainers](#organisation-core-maintainers) to agree that the issue is suitable for a bounty, and how much the reward amount should be. +* Once agreed, we will assign a label to the issue so that interested developers can find it. + +Sponsors can create up to 1 or 3 bounties (according to tier) _per calendar month_ - i.e. the counter resets on the 1st of each month. +If a sponsor does not use their full quota of bounty credits in a calendar month, they cannot be rolled over to the next month. + +Bounties will expire 90 days after creation - after this time, if they have not been resolved we will close them. + +## For Contributors + +As a contributor, the process for working on an issue with a bounty attached is: + +* Find an issue with a bounty attached to it and no assignee, clarify the requirements if necessary, and consider how you would approach working on it. +* Start a discussion with the [Testcontainers org core maintainers](#organisation-core-maintainers) and the bounty owner. To avoid unpleasant surprises at review time, we'll try to confirm that we're happy with your proposed solution. +* If we're happy with your proposed solution, we will assign the ticket to you. +* Once work is complete, we will go through the PR process as usual and merge the work when finished. +* To receive the bounty reward, [raise an invoice](https://opencollective.com/testcontainers/expenses/new) on Open Collective, following the expenses policy on that page. + +Note that a 20% cut of the bounty amount will normally be assigned to project maintainers for PR review work. +We believe this reflects that PR review can often be a significant amount of work for some issues - and also gives maintainers an incentive to complete the review and unlock the bounty reward! +Some pull requests are so well done that very little review is necessary. If that happens, the maintainers may choose not to take a cut of the bounty, and instead release the full amount to the contributor. + +## Organisation core maintainers + +The organisation core maintainers are: + +* Richard North (@rnorth) +* Sergei Egorov (@bsideup) +* Kevin Wittek (@kiview) + diff --git a/mkdocs.yml b/mkdocs.yml index 795a2bdb4e2..b8372e81ed8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -89,5 +89,6 @@ nav: - Contributing: - contributing.md - contributing_docs.md + - bounty.md extra: latest_version: 1.14.3 From 96de42be8fc635196c6fc55bb39c02125897e732 Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 29 Jun 2020 21:38:28 +0100 Subject: [PATCH 1482/5615] Create FUNDING.yml --- .github/FUNDING.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000000..9889f256efc --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +github: [testcontainers] From 4ab556ce4527dd0574a377751e82f94832707f83 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jun 2020 21:45:07 +0100 Subject: [PATCH 1483/5615] Bump aws-java-sdk-s3 from 1.11.807 to 1.11.812 in /modules/localstack (#2948) Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.11.807 to 1.11.812. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.807...1.11.812) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index a04c04a4751..ee726826b54 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.807' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.812' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.807' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.807' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' From 0cc880eb6c4b93feea3ae50175625fd7062e5df3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jun 2020 21:45:20 +0100 Subject: [PATCH 1484/5615] Bump aws-java-sdk-sqs from 1.11.807 to 1.11.812 in /modules/localstack (#2949) Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.11.807 to 1.11.812. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.807...1.11.812) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index ee726826b54..04a18626bad 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.812' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.807' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.807' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.812' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' testCompile 'software.amazon.awssdk:s3:2.13.41' } From c48f7077415e0e87d27ffd2247c00d70492f2513 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jun 2020 21:45:36 +0100 Subject: [PATCH 1485/5615] Bump mariadb-java-client from 2.6.0 to 2.6.1 in /modules/mariadb (#2950) Bumps [mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 2.6.0 to 2.6.1. - [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases) - [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md) - [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/2.6.0...2.6.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 38862c7f03c..a4f2f951233 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'org.mariadb:r2dbc-mariadb:0.8.2-alpha2' testCompile project(':jdbc-test') - testCompile 'org.mariadb.jdbc:mariadb-java-client:2.6.0' + testCompile 'org.mariadb.jdbc:mariadb-java-client:2.6.1' testCompile testFixtures(project(':r2dbc')) testCompile 'org.mariadb:r2dbc-mariadb:0.8.1-alpha1' From 1ad7ffa3bffc71c1768816c8fb86dbbe72186f24 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jun 2020 21:46:00 +0100 Subject: [PATCH 1486/5615] Bump aws-java-sdk-dynamodb from 1.11.807 to 1.11.812 in /modules/dynalite (#2953) Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.11.807 to 1.11.812. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.807...1.11.812) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/dynalite/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 7101f40552f..8045c273496 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Dynalite" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.807' - testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.479' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.812' + testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.812' } From b0dbd3190e20a09ed3d0030d3cf23174190c82f1 Mon Sep 17 00:00:00 2001 From: Simon Schneider <10846939+raynigon@users.noreply.github.com> Date: Mon, 29 Jun 2020 23:02:21 +0200 Subject: [PATCH 1487/5615] Convert Docs for Webdriver Containers (#2927) Co-authored-by: Richard North --- docs/modules/webdriver_containers.md | 62 ++++++---------- ...ChromeRecordingWebDriverContainerTest.java | 73 ++++++++++++++----- .../junit/ChromeWebDriverContainerTest.java | 2 + .../junit/FirefoxWebDriverContainerTest.java | 2 + .../LocalServerWebDriverContainerTest.java | 4 + 5 files changed, 86 insertions(+), 57 deletions(-) diff --git a/docs/modules/webdriver_containers.md b/docs/modules/webdriver_containers.md index 70c06d9dbdd..40b27a5207f 100644 --- a/docs/modules/webdriver_containers.md +++ b/docs/modules/webdriver_containers.md @@ -22,70 +22,54 @@ every test. ## Example The following field in your JUnit UI test class will prepare a container running Chrome: -```java -@Rule -public BrowserWebDriverContainer chrome = - new BrowserWebDriverContainer() - .withCapabilities(new ChromeOptions()); -``` + +[Chrome](../../modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java) inside_block:junitRule + + Now, instead of instantiating an instance of WebDriver directly, use the following to obtain an instance inside your test methods: -```java -RemoteWebDriver driver = chrome.getWebDriver(); -``` + +[RemoteWebDriver](../../modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java) inside_block:getWebDriver + You can then use this driver instance like a regular WebDriver. Note that, if you want to test a **web application running on the host machine** (the machine the JUnit tests are running on - which is quite likely), you'll need to replace any references to `localhost` with an IP address that the Docker container can reach. Use the `getTestHostIpAddress()` method, e.g.: -```java -driver.get("http://" + chrome.getTestHostIpAddress() + ":8080/"); -``` + +[Open Web Page](../../modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java) inside_block:getPage + + ## Options ### Other browsers At the moment, Chrome and Firefox are supported. To switch, simply change the first parameter to the rule constructor: -```java -new BrowserWebDriverContainer() - .withCapabilities(new ChromeOptions()); -``` - -or -```java -new BrowserWebDriverContainer() - .withCapabilities(new FirefoxOptions()); -``` + +[Chrome](../../modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java) inside_block:junitRule +[Firefox](../../modules/selenium/src/test/java/org/testcontainers/junit/FirefoxWebDriverContainerTest.java) inside_block:junitRule + ### Recording videos By default, no videos will be recorded. However, you can instruct Testcontainers to capture videos for all tests or just for failing tests. -To do this, simply add extra parameters to the rule constructor: -```java -new BrowserWebDriverContainer() - .withCapabilities(new ChromeOptions()) - .withRecordingMode(VncRecordingMode.RECORD_ALL, new File("./target/")) -``` + +[Record all Tests](../../modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java) inside_block:recordAll +[Record failing Tests](../../modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java) inside_block:recordFailing + -or if you only want videos for test failures: -```java -new BrowserWebDriverContainer() - .withCapabilities(new ChromeOptions()) - .withRecordingMode(VncRecordingMode.RECORD_FAILING, new File("./target/")) -``` Note that the seconds parameter to `withRecordingMode` should be a directory where recordings can be saved. If you would like to customise the file name of the recording, or provide a different directory at runtime based on the description of the test and/or its success or failure, you may provide a custom recording file factory as follows: -```java -new BrowserWebDriverContainer() - //... - .withRecordingFileFactory(new CustomRecordingFileFactory()) -``` + +[CustomRecordingFileFactory](../../modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java) inside_block:withRecordingFileFactory + + Note the factory must implement `org.testcontainers.containers.RecordingFileFactory`. diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java index 3872e3d8dd2..ead9a21ae4f 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java @@ -12,9 +12,12 @@ import org.testcontainers.containers.DefaultRecordingFileFactory; import org.testcontainers.lifecycle.TestDescription; +import java.io.File; import java.util.Optional; -import static org.junit.Assert.assertTrue; + +import static org.junit.Assert.assertEquals; import static org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode.RECORD_ALL; +import static org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode.RECORD_FAILING; @RunWith(Enclosed.class) public class ChromeRecordingWebDriverContainerTest extends BaseWebDriverContainerTest { @@ -26,10 +29,14 @@ public static class ChromeThatRecordsAllTests { @Test public void recordingTestThatShouldBeRecordedAndRetained() { + File target = vncRecordingDirectory.getRoot(); try ( + // recordAll { + // To do this, simply add extra parameters to the rule constructor: BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() .withCapabilities(new ChromeOptions()) - .withRecordingMode(RECORD_ALL, vncRecordingDirectory.getRoot()) + .withRecordingMode(RECORD_ALL, target) + // } .withRecordingFileFactory(new DefaultRecordingFileFactory()) ) { chrome.start(); @@ -48,7 +55,7 @@ public String getFilesystemFriendlyName() { }, Optional.empty()); String[] files = vncRecordingDirectory.getRoot().list(new PatternFilenameFilter("PASSED-.*\\.flv")); - assertTrue("Recorded file not found", files.length == 1); + assertEquals("Recorded file not found", 1, files.length); } } } @@ -56,28 +63,58 @@ public String getFilesystemFriendlyName() { public static class ChromeThatRecordsFailingTests { @Rule - public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() - .withCapabilities(new ChromeOptions()); + public TemporaryFolder vncRecordingDirectory = new TemporaryFolder(); @Test public void recordingTestThatShouldBeRecordedButNotPersisted() { - doSimpleExplore(chrome); + try ( + // withRecordingFileFactory { + BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() + // } + .withCapabilities(new ChromeOptions()) + // withRecordingFileFactory { + .withRecordingFileFactory(new CustomRecordingFileFactory()) + // } + ) { + chrome.start(); + + doSimpleExplore(chrome); + } } @Test public void recordingTestThatShouldBeRecordedAndRetained() { - doSimpleExplore(chrome); - chrome.afterTest(new TestDescription() { - @Override - public String getTestId() { - return getFilesystemFriendlyName(); - } - - @Override - public String getFilesystemFriendlyName() { - return "ChromeThatRecordsFailingTests-recordingTestThatShouldBeRecordedAndRetained"; - } - }, Optional.of(new RuntimeException("Force writing of video file."))); + File target = vncRecordingDirectory.getRoot(); + try ( + // recordFailing { + // or if you only want videos for test failures: + BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() + .withCapabilities(new ChromeOptions()) + .withRecordingMode(RECORD_FAILING, target) + // } + .withRecordingFileFactory(new DefaultRecordingFileFactory()) + ) { + chrome.start(); + + doSimpleExplore(chrome); + chrome.afterTest(new TestDescription() { + @Override + public String getTestId() { + return getFilesystemFriendlyName(); + } + + @Override + public String getFilesystemFriendlyName() { + return "ChromeThatRecordsFailingTests-recordingTestThatShouldBeRecordedAndRetained"; + } + }, Optional.of(new RuntimeException("Force writing of video file."))); + + String[] files = vncRecordingDirectory.getRoot().list(new PatternFilenameFilter("FAILED-.*\\.flv")); + assertEquals("Recorded file not found", 1, files.length); + } + } + + private static class CustomRecordingFileFactory extends DefaultRecordingFileFactory { } } } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java index 7914e1bd810..92ba4e2cd37 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java @@ -11,9 +11,11 @@ */ public class ChromeWebDriverContainerTest extends BaseWebDriverContainerTest { + // junitRule { @Rule public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() .withCapabilities(new ChromeOptions()); + // } @Before public void checkBrowserIsIndeedChrome() { diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/FirefoxWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/FirefoxWebDriverContainerTest.java index 21b03164102..1f687aab762 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/FirefoxWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/FirefoxWebDriverContainerTest.java @@ -11,9 +11,11 @@ */ public class FirefoxWebDriverContainerTest extends BaseWebDriverContainerTest { + // junitRule { @Rule public BrowserWebDriverContainer firefox = new BrowserWebDriverContainer() .withCapabilities(new FirefoxOptions()); + // } @Before public void checkBrowserIsIndeedFirefox() { diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java index 0cee0cf49e2..f7d626c15b5 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java @@ -49,11 +49,15 @@ public void setupLocalServer() throws Exception { @Test public void testConnection() { + // getWebDriver { RemoteWebDriver driver = chrome.getWebDriver(); + // } // Construct a URL that the browser container can access + // getPage { String hostIpAddress = chrome.getTestHostIpAddress(); driver.get("http://" + hostIpAddress + ":" + localPort); + // } String headingText = driver.findElement(By.cssSelector("h1")).getText().trim(); From f0da2f3365a20b8a7f7d62ebc0b55098e95e5665 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jun 2020 22:03:08 +0100 Subject: [PATCH 1488/5615] Bump s3 from 2.13.41 to 2.13.46 in /modules/localstack (#2947) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 04a18626bad..44a7e3fb6a8 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,5 +7,5 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.807' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.812' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' - testCompile 'software.amazon.awssdk:s3:2.13.41' + testCompile 'software.amazon.awssdk:s3:2.13.46' } From eecaa9a0461740d08e6393753cdd63e45b356088 Mon Sep 17 00:00:00 2001 From: JeanBaptisteWATENBERG Date: Mon, 29 Jun 2020 23:45:55 +0200 Subject: [PATCH 1489/5615] RabbitMQ: correct `shouldMountConfigurationFileSysctl` test so that it exercises the `withRabbitMQConfigSysctl` method (#2945) --- .../org/testcontainers/containers/RabbitMQContainerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java index d8e9c7658c0..4c2787bf5ab 100644 --- a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java +++ b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java @@ -156,7 +156,7 @@ public void shouldMountConfigurationFileSysctl() { try (RabbitMQContainer container = new RabbitMQContainer()) { - container.withRabbitMQConfig(MountableFile.forClasspathResource("/rabbitmq-custom.conf")); + container.withRabbitMQConfigSysctl(MountableFile.forClasspathResource("/rabbitmq-custom.conf")); container.start(); assertThat(container.getLogs()).contains("config file(s) : /etc/rabbitmq/rabbitmq-custom.conf"); From d03768bdf6c5c39d4f0382a98f3defbd0e2144d1 Mon Sep 17 00:00:00 2001 From: Simon Schneider <10846939+raynigon@users.noreply.github.com> Date: Mon, 29 Jun 2020 23:47:00 +0200 Subject: [PATCH 1490/5615] Convert Docs for Kafka Module #1158 (#2925) --- docs/modules/kafka.md | 36 ++++++++++--------- .../containers/KafkaContainerTest.java | 32 +++++++++++++++++ 2 files changed, 52 insertions(+), 16 deletions(-) diff --git a/docs/modules/kafka.md b/docs/modules/kafka.md index a96a0a0b43a..35ac37b6906 100644 --- a/docs/modules/kafka.md +++ b/docs/modules/kafka.md @@ -11,41 +11,45 @@ More precisely Testcontainers uses the official Docker images for [Confluent OSS ## Example The following field in your JUnit test class will prepare a container running Kafka: -```java -@Rule -public KafkaContainer kafka = new KafkaContainer(); -``` + +[JUnit Rule](../../modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java) inside_block:junitRule + Now your tests or any other process running on your machine can get access to running Kafka broker by using the following bootstrap server location: -```java -kafka.getBootstrapServers() -``` + + +[Bootstrap Servers](../../modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java) inside_block:getBootstrapServers + + ## Options ### Selecting Kafka version You can select a version of Confluent Platform by passing it to the container's constructor: -```java -new KafkaContainer("4.1.2") -``` + +[Version Constructor](../../modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java) inside_block:constructorWithVersion + + + The correspondence between Confluent Platform versions and Kafka versions can be seen [in Confluent documentation](https://docs.confluent.io/current/installation/versions-interoperability.html#cp-and-apache-kafka-compatibility) ### Using external Zookeeper If for some reason you want to use an externally running Zookeeper, then just pass its location during construction: -```java -new KafkaContainer().withExternalZookeeper("localhost:2181") -``` + +[External Zookeeper](../../modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java) inside_block:withExternalZookeeper + + ## Multi-container usage If your test needs to run some other Docker container which needs access to the Kafka, do the following: * Run your other container on the same network as Kafka container, e.g.: -```java -new GenericContainer("myImage").withNetwork(kafka.getNetwork()) -``` + +[Network](../../modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java) inside_block:withKafkaNetwork + * Use `kafka.getNetworkAliases().get(0)+":9092"` as bootstrap server location. Or just give your Kafka container a network alias of your liking. diff --git a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java index c5d719295ed..25bc1b5022c 100644 --- a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java +++ b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java @@ -10,6 +10,7 @@ import org.apache.kafka.clients.producer.ProducerRecord; import org.apache.kafka.common.serialization.StringDeserializer; import org.apache.kafka.common.serialization.StringSerializer; +import org.junit.Rule; import org.junit.Test; import org.rnorth.ducttape.unreliables.Unreliables; @@ -23,6 +24,11 @@ public class KafkaContainerTest { + // junitRule { + @Rule + public KafkaContainer kafka = new KafkaContainer(); + // } + @Test public void testUsage() throws Exception { try (KafkaContainer kafka = new KafkaContainer()) { @@ -31,22 +37,48 @@ public void testUsage() throws Exception { } } + + @Test + public void testUsageWithVersion() throws Exception { + try ( + // constructorWithVersion { + KafkaContainer kafka = new KafkaContainer("4.1.2") + // } + ) { + kafka.start(); + testKafkaFunctionality( + // getBootstrapServers { + kafka.getBootstrapServers() + // } + ); + } + } + @Test public void testExternalZookeeperWithExternalNetwork() throws Exception { try ( Network network = Network.newNetwork(); + // withExternalZookeeper { KafkaContainer kafka = new KafkaContainer() .withNetwork(network) .withExternalZookeeper("zookeeper:2181"); + // } GenericContainer zookeeper = new GenericContainer("confluentinc/cp-zookeeper:4.0.0") .withNetwork(network) .withNetworkAliases("zookeeper") .withEnv("ZOOKEEPER_CLIENT_PORT", "2181"); + + // withKafkaNetwork { + GenericContainer application = new GenericContainer("alpine").withNetwork(kafka.getNetwork()) + // } + .withNetworkAliases("dummy") + .withCommand("sleep 10000") ) { zookeeper.start(); kafka.start(); + application.start(); testKafkaFunctionality(kafka.getBootstrapServers()); } From 6186598c9b518e71fcc55ff313a5ac1460ff3df9 Mon Sep 17 00:00:00 2001 From: Kirill Merkushev Date: Tue, 30 Jun 2020 16:49:03 +0200 Subject: [PATCH 1491/5615] Make it possible to enable functions worker in PulsarContainer (#2711) * Method to enable functions worker in pulsar container This change should simplify a bit pulsar functions development experience Tried here: https://github.com/lanwen/pulsar-functions-example Without waiter pulsar could return random 503, depending on how fast was a call. * treat functions worker method as a flag to enable in configure real functions upload test * upd pulsar to 2.5.1 * don't store jar * expect error for functions without worker * reduce the changeset Co-authored-by: Sergei Egorov --- modules/pulsar/build.gradle | 1 + .../containers/PulsarContainer.java | 23 +++++++++++++- .../containers/PulsarContainerTest.java | 30 +++++++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 371aa0c11d0..b0a76d673ad 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -5,4 +5,5 @@ dependencies { testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.6.0' testCompile group: 'org.assertj', name: 'assertj-core', version: '3.16.1' + testCompile group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.5.1' } diff --git a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java index c9532979057..936759efb1a 100644 --- a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java +++ b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java @@ -1,6 +1,7 @@ package org.testcontainers.containers; import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.containers.wait.strategy.WaitAllStrategy; import org.testcontainers.utility.TestcontainersConfiguration; /** @@ -14,6 +15,8 @@ public class PulsarContainer extends GenericContainer { private static final String PULSAR_VERSION = "2.2.0"; + private boolean functionsWorkerEnabled = false; + public PulsarContainer() { this(PULSAR_VERSION); } @@ -25,6 +28,25 @@ public PulsarContainer(String pulsarVersion) { waitingFor(Wait.forHttp(METRICS_ENDPOINT).forStatusCode(200).forPort(BROKER_HTTP_PORT)); } + @Override + protected void configure() { + super.configure(); + + if (functionsWorkerEnabled) { + withCommand("/pulsar/bin/pulsar", "standalone"); + waitingFor( + new WaitAllStrategy() + .withStrategy(waitStrategy) + .withStrategy(Wait.forLogMessage(".*Function worker service started.*", 1)) + ); + } + } + + public PulsarContainer withFunctionsWorker() { + functionsWorkerEnabled = true; + return this; + } + public String getPulsarBrokerUrl() { return String.format("pulsar://%s:%s", getHost(), getMappedPort(BROKER_PORT)); } @@ -33,4 +55,3 @@ public String getHttpServiceUrl() { return String.format("http://%s:%s", getHost(), getMappedPort(BROKER_HTTP_PORT)); } } - diff --git a/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java b/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java index cacf79729e0..e9d41516670 100644 --- a/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java +++ b/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java @@ -1,5 +1,7 @@ package org.testcontainers.containers; +import org.apache.pulsar.client.admin.PulsarAdmin; +import org.apache.pulsar.client.admin.PulsarAdminException; import org.apache.pulsar.client.api.Consumer; import org.apache.pulsar.client.api.Message; import org.apache.pulsar.client.api.Producer; @@ -10,6 +12,7 @@ import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; public class PulsarContainerTest { @@ -23,6 +26,33 @@ public void testUsage() throws Exception { } } + @Test + public void shouldNotEnableFunctionsWorkerByDefault() throws Exception { + try (PulsarContainer pulsar = new PulsarContainer("2.5.1")) { + pulsar.start(); + + PulsarAdmin pulsarAdmin = PulsarAdmin.builder() + .serviceHttpUrl(pulsar.getHttpServiceUrl()) + .build(); + + assertThatThrownBy(() -> pulsarAdmin.functions().getFunctions("public", "default")) + .isInstanceOf(PulsarAdminException.class); + } + } + + @Test + public void shouldWaitForFunctionsWorkerStarted() throws Exception { + try (PulsarContainer pulsar = new PulsarContainer("2.5.1").withFunctionsWorker()) { + pulsar.start(); + + PulsarAdmin pulsarAdmin = PulsarAdmin.builder() + .serviceHttpUrl(pulsar.getHttpServiceUrl()) + .build(); + + assertThat(pulsarAdmin.functions().getFunctions("public", "default")).hasSize(0); + } + } + protected void testPulsarFunctionality(String pulsarBrokerUrl) throws Exception { try ( From 25a9aa51a11d3c050da8b6b532dac50ef8e227ba Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 30 Jun 2020 21:02:45 +0100 Subject: [PATCH 1492/5615] Add backers (#2958) Co-authored-by: Kevin Wittek --- docs/index.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/index.md b/docs/index.md index 8b3ffda05d2..d665557cff1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -119,6 +119,21 @@ testImplementation('org.testcontainers:mysql') //no version specified * [Transferwise](https://transferwise.com/) - Integration testing for different RDBMS, kafka and micro services * [XWiki](https://xwiki.org) - [Testing XWiki](https://dev.xwiki.org/xwiki/bin/view/Community/Testing/DockerTesting/) under all [supported configurations](https://dev.xwiki.org/xwiki/bin/view/Community/SupportStrategy/) +## Sponsors + +Testcontainers is on Github Sponsors! Please consider sponsoring us to sustain the project and help us improve it further. + + + +### Backers + +A huge thank you to our backers: + +* [Philip Riecks (@rieckpil)](https://github.com/rieckpil) +* [Karl Heinz Marbaise (@khmarbaise)](https://github.com/khmarbaise) +* [Sascha Frinken (@sascha-frinken)](https://github.com/sascha-frinken) + + ## License See [LICENSE](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/LICENSE). From 923472ff114d99b2ac0e199a86829879c24c8471 Mon Sep 17 00:00:00 2001 From: kezhenxu94 Date: Fri, 3 Jul 2020 23:36:34 +0800 Subject: [PATCH 1493/5615] Add Apache SkyWalking to the list of users (#2967) --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index d665557cff1..6c8bf49f08f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -118,6 +118,7 @@ testImplementation('org.testcontainers:mysql') //no version specified * [Marquez](https://marquezproject.github.io/marquez) - PostgreSQL integration testing * [Transferwise](https://transferwise.com/) - Integration testing for different RDBMS, kafka and micro services * [XWiki](https://xwiki.org) - [Testing XWiki](https://dev.xwiki.org/xwiki/bin/view/Community/Testing/DockerTesting/) under all [supported configurations](https://dev.xwiki.org/xwiki/bin/view/Community/SupportStrategy/) +* [Apache SkyWalking](http://github.com/apache/skywalking) - End-to-end testing of the Apache SkyWalking, and plugin tests of its subproject, [Apache SkyWalking Python](http://github.com/apache/skywalking-python), and of its eco-system built by the community, like [SkyAPM NodeJS Agent](http://github.com/SkyAPM/nodejs) ## Sponsors From 7eb504a411337b4fb3c0772cf5938a2272f6bf3a Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Sun, 5 Jul 2020 17:04:36 +0200 Subject: [PATCH 1494/5615] Add Windows Server 2019 as unsupported to docs (#2963) --- docs/supported_docker_environment/windows.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/supported_docker_environment/windows.md b/docs/supported_docker_environment/windows.md index 6afab5972f2..9a50a844b04 100644 --- a/docs/supported_docker_environment/windows.md +++ b/docs/supported_docker_environment/windows.md @@ -5,6 +5,7 @@ * Docker version 17.06 is confirmed to work on Windows 10 with Hyper-V. * Testcontainers supports communication with Docker on Docker for Windows using named pipes. * WSL2 backend is supported starting with Windows 10 2004. (**Beta**) + * Docker on Windows Server 2019 is currently **not supported** (also note [this issue](https://github.com/testcontainers/testcontainers-java/issues/2960)). ## Limitations The following features are not available or do not work correctly so make sure you do not use them or use them with From 66e6a2fb171f9102d699dbd863d4401d5fbfcb6c Mon Sep 17 00:00:00 2001 From: Thomas Schmidt Date: Sun, 5 Jul 2020 18:06:13 +0200 Subject: [PATCH 1495/5615] Adapt docs to non deprecated way of setting --memory and --memory-swap (#2511) Co-authored-by: Thomas Schmidt Co-authored-by: Richard North --- .../generic/src/test/java/generic/CmdModifierTest.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java b/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java index 6d075c0bccc..172a821b178 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java @@ -18,10 +18,15 @@ public class CmdModifierTest { // } // memory { + private long memoryInBytes = 8 * 1024 * 1024; + private long memorySwapInBytes = 12 * 1024 * 1024; + @Rule public GenericContainer memoryLimitedRedis = new GenericContainer<>("redis:3.0.2") - .withCreateContainerCmdModifier(cmd -> cmd.withMemory((long) 8 * 1024 * 1024)) - .withCreateContainerCmdModifier(cmd -> cmd.withMemorySwap((long) 12 * 1024 * 1024)); + .withCreateContainerCmdModifier(cmd -> cmd.getHostConfig() + .withMemory(memoryInBytes) + .withMemorySwap(memorySwapInBytes) + ); // } From 965fcf552dcc27e1fbffd01a50e9a33ea496120c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jul 2020 08:33:29 +0100 Subject: [PATCH 1496/5615] Bump tomcat-jdbc from 9.0.36 to 9.0.37 in /modules/jdbc-test (#2969) Bumps tomcat-jdbc from 9.0.36 to 9.0.37. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index e901869f04e..0c06ad083e0 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -15,7 +15,7 @@ dependencies { } compile 'commons-dbutils:commons-dbutils:1.7' - compile 'org.apache.tomcat:tomcat-jdbc:9.0.36' + compile 'org.apache.tomcat:tomcat-jdbc:9.0.37' compile 'org.vibur:vibur-dbcp:25.0' compile 'mysql:mysql-connector-java:8.0.20' } From f7c027f20779dbfd30eb8592994ae495097f263e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jul 2020 08:33:37 +0100 Subject: [PATCH 1497/5615] Bump rest-assured from 4.3.0 to 4.3.1 in /modules/vault (#2970) Bumps [rest-assured](https://github.com/jayway/rest-assured) from 4.3.0 to 4.3.1. - [Release notes](https://github.com/jayway/rest-assured/releases) - [Changelog](https://github.com/rest-assured/rest-assured/blob/master/changelog.txt) - [Commits](https://github.com/jayway/rest-assured/compare/rest-assured-4.3.0...rest-assured-4.3.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index 2b9f702f9d9..0d094905198 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -4,7 +4,7 @@ dependencies { compile project(':testcontainers') testCompile 'com.bettercloud:vault-java-driver:5.1.0' - testCompile 'io.rest-assured:rest-assured:4.3.0' + testCompile 'io.rest-assured:rest-assured:4.3.1' testCompile 'org.assertj:assertj-core:3.16.1' } From 8c689fa4e1f32dd0f8db7d79b2b6e2137975de9c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jul 2020 08:33:47 +0100 Subject: [PATCH 1498/5615] Bump tomcat-jdbc from 9.0.36 to 9.0.37 in /modules/jdbc (#2971) Bumps tomcat-jdbc from 9.0.36 to 9.0.37. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 3b6aea15c59..cd10c7a98c3 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -6,7 +6,7 @@ dependencies { testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.vibur:vibur-dbcp:25.0' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.36' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.37' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'com.googlecode.junit-toolbox:junit-toolbox:2.4' } From d41e04ca46a4adfeee9dc311acb31c18566dc821 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jul 2020 08:33:57 +0100 Subject: [PATCH 1499/5615] Bump s3 from 2.13.46 to 2.13.50 in /modules/localstack (#2972) Bumps s3 from 2.13.46 to 2.13.50. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 44a7e3fb6a8..8945c082685 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,5 +7,5 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.807' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.812' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' - testCompile 'software.amazon.awssdk:s3:2.13.46' + testCompile 'software.amazon.awssdk:s3:2.13.50' } From ddfa60a1932de7b23a73c8ff85a2fb6febbcfb3c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jul 2020 08:34:06 +0100 Subject: [PATCH 1500/5615] Bump aws-java-sdk-s3 from 1.11.812 to 1.11.816 in /modules/localstack (#2973) Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.11.812 to 1.11.816. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.812...1.11.816) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 8945c082685..6e9e44af320 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.812' - testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.807' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.816' + testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.816' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.812' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' testCompile 'software.amazon.awssdk:s3:2.13.50' From af4b1a312d6523e08f21bbcb69dcafd74597b733 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jul 2020 08:34:25 +0100 Subject: [PATCH 1501/5615] Bump pulsar-client-admin from 2.5.1 to 2.6.0 in /modules/pulsar (#2975) Bumps [pulsar-client-admin](https://github.com/apache/pulsar) from 2.5.1 to 2.6.0. - [Release notes](https://github.com/apache/pulsar/releases) - [Commits](https://github.com/apache/pulsar/compare/v2.5.1...v2.6.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index b0a76d673ad..a8f55696a65 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -5,5 +5,5 @@ dependencies { testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.6.0' testCompile group: 'org.assertj', name: 'assertj-core', version: '3.16.1' - testCompile group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.5.1' + testCompile group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.6.0' } From c13aea3f02989f3c9294ec5ca23ed0a59a4b1eaf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jul 2020 08:34:34 +0100 Subject: [PATCH 1502/5615] Bump aws-java-sdk-dynamodb from 1.11.812 to 1.11.816 in /modules/dynalite (#2976) Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.11.812 to 1.11.816. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.812...1.11.816) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/dynalite/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 8045c273496..b3919a3eaf3 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Dynalite" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.812' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.816' testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.812' } From 448d05fc7da5fd5d1bd7744201202d8da52abb4c Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 6 Jul 2020 20:01:16 +0100 Subject: [PATCH 1503/5615] Add breaking change and deprecation sections to release notes (#2978) --- .github/release-drafter.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index af82504717a..4da341055c2 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -5,10 +5,14 @@ template: | $CHANGES categories: + - title: ⚠️ Breaking API changes + label: type/breaking-api-change - title: 🚀 Features & Enhancements labels: - type/feature - type/enhancement + - title: ☠️ Deprecations + label: type/deprecation - title: 🐛 Bug Fixes label: type/bug - title: 📖 Documentation From ddbaf94a21aabe2b2e98d16fd735e0917e88a7d0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Jul 2020 08:53:47 +0100 Subject: [PATCH 1504/5615] Bump cucumber-java from 5.7.0 to 6.1.1 in /examples (#2919) Bumps [cucumber-java](https://github.com/cucumber/cucumber-jvm) from 5.7.0 to 6.1.1. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/master/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v5.7.0...v6.1.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index e4c44c0e063..d17837f1518 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' - testImplementation 'io.cucumber:cucumber-java:5.7.0' + testImplementation 'io.cucumber:cucumber-java:6.1.1' testImplementation 'io.cucumber:cucumber-junit:6.1.1' testImplementation 'org.testcontainers:selenium' } From 1c97b978ac6ccfba0119986c22e4ac6d9ac955fa Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 9 Jul 2020 17:52:06 +0200 Subject: [PATCH 1505/5615] Un-shade docker-java-api (#2882) This PR makes Testcontainers depend on `docker-java-api` instead of including it into our JAR unshaded. Note that `docker-java-core` and `docker-java-transport-okhttp` remain shaded due to their transitive dependencies like Guava or now-in-Kotlin OkHttp. The transition requires a breaking change in the `DockerClientProviderStrategy` API and includes a rework. The rework also includes an improvement that removes one Docker API call (`/_ping`) and instead reuses the `/_info` one. --- build.gradle | 5 - core/build.gradle | 38 ++++- .../testcontainers/JarFileShadingTest.java | 11 +- .../testcontainers/PublicBinaryAPITest.java | 7 +- .../testcontainers/DockerClientFactory.java | 27 ++- .../containers/GenericContainer.java | 8 +- .../output/FrameConsumerResultCallback.java | 2 +- .../DockerClientProviderStrategy.java | 154 ++++++++++-------- .../DockerMachineClientProviderStrategy.java | 87 +++++----- ...dSystemPropertyClientProviderStrategy.java | 44 ++--- .../LogToStringContainerCallback.java | 10 +- .../NpipeSocketClientProviderStrategy.java | 57 ++----- ...oxiedUnixSocketClientProviderStrategy.java | 31 ---- .../dockerclient/TransportConfig.java | 18 ++ .../UnixSocketClientProviderStrategy.java | 53 ++---- .../WindowsClientProviderStrategy.java | 41 ----- .../images/LoggedPullImageResultCallback.java | 2 +- ...eLimitedLoggedPullImageResultCallback.java | 2 +- .../images/builder/ImageFromDockerfile.java | 2 +- .../resources/META-INF/services/README.md | 1 - ....dockerclient.DockerClientProviderStrategy | 1 - ...ded.com.fasterxml.jackson.core.JsonFactory | 1 - ...ded.com.fasterxml.jackson.core.ObjectCodec | 1 - ...ockerjava.api.command.DockerCmdExecFactory | 1 - ...ers.shaded.javax.ws.rs.ext.RuntimeDelegate | 1 - ...fish.hk2.extension.ServiceLocatorGenerator | 1 - ...external.generator.ServiceLocatorGenerator | 1 - 27 files changed, 268 insertions(+), 339 deletions(-) delete mode 100644 core/src/main/java/org/testcontainers/dockerclient/ProxiedUnixSocketClientProviderStrategy.java create mode 100644 core/src/main/java/org/testcontainers/dockerclient/TransportConfig.java delete mode 100644 core/src/main/java/org/testcontainers/dockerclient/WindowsClientProviderStrategy.java delete mode 100644 core/src/main/resources/META-INF/services/README.md delete mode 100755 core/src/main/resources/META-INF/services/org.testcontainers.shaded.com.fasterxml.jackson.core.JsonFactory delete mode 100755 core/src/main/resources/META-INF/services/org.testcontainers.shaded.com.fasterxml.jackson.core.ObjectCodec delete mode 100644 core/src/main/resources/META-INF/services/org.testcontainers.shaded.com.github.dockerjava.api.command.DockerCmdExecFactory delete mode 100644 core/src/main/resources/META-INF/services/org.testcontainers.shaded.javax.ws.rs.ext.RuntimeDelegate delete mode 100644 core/src/main/resources/META-INF/services/org.testcontainers.shaded.org.glassfish.hk2.extension.ServiceLocatorGenerator delete mode 100644 core/src/main/resources/META-INF/services/org.testcontainers.shaded.org.jvnet.hk2.external.generator.ServiceLocatorGenerator diff --git a/build.gradle b/build.gradle index 382ecdf161e..b7830992b07 100644 --- a/build.gradle +++ b/build.gradle @@ -122,11 +122,6 @@ subprojects { for (pkg in packages) { pkg = pkg.replaceAll('/', '.') - if (pkg.startsWith("com.github.dockerjava.")) { - // Keep docker-java's package inside the final jar - continue; - } - tasks.shadowJar.relocate(pkg, "org.testcontainers.shaded.${pkg}") } } diff --git a/core/build.gradle b/core/build.gradle index e1fb724597e..98452944bd0 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -100,17 +100,33 @@ task japicmp(type: me.champeau.gradle.japicmp.JapicmpTask) { "org.testcontainers.containers.wait.WaitAllStrategy", "org.testcontainers.containers.wait.WaitStrategy", "org.testcontainers.dockerclient.AuditLoggingDockerClient", + "org.testcontainers.dockerclient.LogToStringContainerCallback", + "org.testcontainers.dockerclient.DockerMachineClientProviderStrategy", + "org.testcontainers.dockerclient.EnvironmentAndSystemPropertyClientProviderStrategy", + "org.testcontainers.dockerclient.NpipeSocketClientProviderStrategy", + "org.testcontainers.dockerclient.ProxiedUnixSocketClientProviderStrategy", + "org.testcontainers.dockerclient.WindowsClientProviderStrategy", + "org.testcontainers.dockerclient.UnixSocketClientProviderStrategy", "org.testcontainers.dockerclient.auth.AuthDelegatingDockerClientConfig", "org.testcontainers.containers.GenericContainer\$AbstractWaitStrategy", "org.testcontainers.dockerclient.transport.okhttp.NamedPipeSocketFactory", "org.testcontainers.dockerclient.transport.okhttp.OkHttpDockerCmdExecFactory", "org.testcontainers.dockerclient.transport.okhttp.UnixSocketFactory", + "org.testcontainers.containers.output.FrameConsumerResultCallback", + "org.testcontainers.images.TimeLimitedLoggedPullImageResultCallback", ] methodExcludes = [ "org.testcontainers.dockerclient.DockerClientConfigUtils#getDetectedDockerHostIp()", "org.testcontainers.dockerclient.DockerClientConfigUtils#getDockerHostIpAddress(com.github.dockerjava.core.DockerClientConfig)", + "org.testcontainers.dockerclient.DockerClientProviderStrategy#checkOSType()", + "org.testcontainers.dockerclient.DockerClientProviderStrategy#getClientForConfig(com.github.dockerjava.core.DockerClientConfig)", + "org.testcontainers.dockerclient.DockerClientProviderStrategy#ping(com.github.dockerjava.api.DockerClient, int)", + "org.testcontainers.dockerclient.DockerClientProviderStrategy#test()", + + "org.testcontainers.dockerclient.UnixSocketClientProviderStrategy#tryConfiguration(java.lang.String)", + "org.testcontainers.utility.ResourceReaper#start(java.lang.String, com.github.dockerjava.api.DockerClient, boolean)", "org.testcontainers.containers.Container#fetchDockerDaemonInfo()", @@ -126,6 +142,10 @@ task japicmp(type: me.champeau.gradle.japicmp.JapicmpTask) { "org.testcontainers.containers.GenericContainer#containerId", "org.testcontainers.containers.GenericContainer#containerName", "org.testcontainers.containers.GenericContainer#dockerDaemonInfo", + + "org.testcontainers.dockerclient.DockerClientProviderStrategy#LOGGER", + "org.testcontainers.dockerclient.DockerClientProviderStrategy#client", + "org.testcontainers.dockerclient.DockerClientProviderStrategy#config", ] onlyBinaryIncompatibleModified = true @@ -150,25 +170,27 @@ dependencies { exclude(group: 'org.jetbrains', module: 'annotations') } - compile ('org.rnorth.visible-assertions:visible-assertions:2.1.2') { - // Excluded in favor of jna-platform below - exclude(group: "net.java.dev.jna", module: "jna") - } + compile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' - compile "net.java.dev.jna:jna-platform:5.5.0" + compile "com.github.docker-java:docker-java-api:3.2.5" - shaded ('com.github.docker-java:docker-java-transport-okhttp:3.2.5') { - exclude(group: 'net.java.dev.jna') + shaded ('com.github.docker-java:docker-java-core:3.2.5') { + exclude(group: 'com.github.docker-java', module: 'docker-java-api') + exclude(group: 'com.github.docker-java', module: 'docker-java-transport') + exclude(group: 'com.fasterxml.jackson.core', module: 'jackson-annotations') exclude(group: 'com.google.code.findbug') exclude(group: 'org.slf4j') exclude(group: 'org.apache.commons', module: 'commons-compress') } - shaded ('com.github.docker-java:docker-java-transport-zerodep:3.2.5') { + shaded ('com.github.docker-java:docker-java-transport-okhttp:3.2.5') { + exclude(group: 'com.github.docker-java', module: 'docker-java-core') exclude(group: 'net.java.dev.jna') exclude(group: 'org.slf4j') } + compile 'com.github.docker-java:docker-java-transport-zerodep:3.2.5' + shaded "org.yaml:snakeyaml:1.25" shaded 'org.glassfish.main.external:trilead-ssh2-repackaged:4.1.2' diff --git a/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java b/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java index 587ab6277f6..4cf7b3a9e9f 100644 --- a/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java +++ b/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java @@ -15,21 +15,12 @@ public class JarFileShadingTest extends AbstractJarFileTest { public void testPackages() throws Exception { assertThatFileList(root).containsOnly( "org", - "META-INF", - "com" + "META-INF" ); assertThatFileList(root.resolve("org")).containsOnly( "testcontainers" ); - - assertThatFileList(root.resolve("com")).containsOnly( - "github" - ); - - assertThatFileList(root.resolve("com").resolve("github")).containsOnly( - "dockerjava" - ); } @Test diff --git a/core/src/jarFileTest/java/org/testcontainers/PublicBinaryAPITest.java b/core/src/jarFileTest/java/org/testcontainers/PublicBinaryAPITest.java index 93d1a759096..6a5a766d24e 100644 --- a/core/src/jarFileTest/java/org/testcontainers/PublicBinaryAPITest.java +++ b/core/src/jarFileTest/java/org/testcontainers/PublicBinaryAPITest.java @@ -88,10 +88,11 @@ public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IO @Before public void setUp() { - Assume.assumeFalse(classNode.name.startsWith("com.github.dockerjava.")); switch (classNode.name) { - // TODO should go to docker-java project - case "org/testcontainers/dockerclient/auth/AuthDelegatingDockerClientConfig": + // Necessary evil + case "org/testcontainers/dockerclient/UnixSocketClientProviderStrategy": + case "org/testcontainers/dockerclient/DockerClientProviderStrategy": + case "org/testcontainers/dockerclient/WindowsClientProviderStrategy": Assume.assumeTrue(false); } } diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 95e0c9838ba..baebe164c48 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -1,16 +1,17 @@ package org.testcontainers; import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.async.ResultCallback; import com.github.dockerjava.api.command.CreateContainerCmd; import com.github.dockerjava.api.exception.InternalServerErrorException; import com.github.dockerjava.api.exception.NotFoundException; import com.github.dockerjava.api.model.AccessMode; import com.github.dockerjava.api.model.Bind; +import com.github.dockerjava.api.model.Frame; import com.github.dockerjava.api.model.Image; import com.github.dockerjava.api.model.Info; import com.github.dockerjava.api.model.Version; import com.github.dockerjava.api.model.Volume; -import com.github.dockerjava.core.command.ExecStartResultCallback; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableMap; import lombok.Getter; @@ -26,6 +27,7 @@ import org.testcontainers.utility.TestcontainersConfiguration; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; @@ -148,7 +150,7 @@ public DockerClient client() { String hostIpAddress = strategy.getDockerHostIpAddress(); log.info("Docker host IP address is {}", hostIpAddress); - final DockerClient client = new DelegatingDockerClient(strategy.getClient()) { + final DockerClient client = new DelegatingDockerClient(strategy.getDockerClient()) { @Override public void close() { throw new IllegalStateException("You should never close the global DockerClient!"); @@ -228,7 +230,24 @@ private void checkDiskSpace(DockerClient dockerClient, String id) { try { dockerClient .execStartCmd(dockerClient.execCreateCmd(id).withAttachStdout(true).withCmd("df", "-P").exec().getId()) - .exec(new ExecStartResultCallback(outputStream, null)) + .exec(new ResultCallback.Adapter() { + @Override + public void onNext(Frame frame) { + if (frame == null) { + return; + } + switch (frame.getStreamType()) { + case RAW: + case STDOUT: + try { + outputStream.write(frame.getPayload()); + outputStream.flush(); + } catch (IOException e) { + onError(e); + } + } + } + }) .awaitCompletion(); } catch (Exception e) { log.debug("Can't exec disk checking command", e); @@ -295,7 +314,7 @@ public String dockerHostIpAddress() { public T runInsideDocker(Consumer createContainerCmdConsumer, BiFunction block) { // We can't use client() here because it might create an infinite loop - return runInsideDocker(getOrInitializeStrategy().getClient(), createContainerCmdConsumer, block); + return runInsideDocker(getOrInitializeStrategy().getDockerClient(), createContainerCmdConsumer, block); } private T runInsideDocker(DockerClient client, Consumer createContainerCmdConsumer, BiFunction block) { diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 072129de60c..417826e83b5 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -17,6 +17,7 @@ import com.github.dockerjava.api.model.PortBinding; import com.github.dockerjava.api.model.Volume; import com.github.dockerjava.api.model.VolumesFrom; +import com.github.dockerjava.core.DefaultDockerClientConfig; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; @@ -514,12 +515,15 @@ void checksumFile(File file, Checksum checksum) { @UnstableAPI @SneakyThrows(JsonProcessingException.class) final String hash(CreateContainerCmd createCommand) { - // TODO add Testcontainers' version to the hash - byte[] commandJson = new ObjectMapper() + DefaultDockerClientConfig dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder().build(); + + byte[] commandJson = dockerClientConfig.getObjectMapper() + .copy() .enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY) .enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS) .writeValueAsBytes(createCommand); + // TODO add Testcontainers' version to the hash return Hashing.sha1().hashBytes(commandJson).toString(); } diff --git a/core/src/main/java/org/testcontainers/containers/output/FrameConsumerResultCallback.java b/core/src/main/java/org/testcontainers/containers/output/FrameConsumerResultCallback.java index 6894b9ec4b8..8c7480320a8 100644 --- a/core/src/main/java/org/testcontainers/containers/output/FrameConsumerResultCallback.java +++ b/core/src/main/java/org/testcontainers/containers/output/FrameConsumerResultCallback.java @@ -1,9 +1,9 @@ package org.testcontainers.containers.output; +import com.github.dockerjava.api.async.ResultCallbackTemplate; import com.github.dockerjava.api.model.Frame; import com.github.dockerjava.api.model.StreamType; -import com.github.dockerjava.core.async.ResultCallbackTemplate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index 9573613c613..3f288d015d0 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -1,14 +1,17 @@ package org.testcontainers.dockerclient; import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.model.Info; import com.github.dockerjava.api.model.Network; -import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.core.DockerClientImpl; import com.github.dockerjava.okhttp.OkDockerHttpClient; import com.github.dockerjava.transport.DockerHttpClient; import com.github.dockerjava.zerodep.ZerodepDockerHttpClient; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Throwables; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.Nullable; @@ -16,8 +19,6 @@ import org.rnorth.ducttape.ratelimits.RateLimiter; import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; import org.rnorth.ducttape.unreliables.Unreliables; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.testcontainers.utility.TestcontainersConfiguration; import java.net.URI; @@ -32,27 +33,21 @@ /** * Mechanism to find a viable Docker client configuration according to the host system environment. */ +@Slf4j public abstract class DockerClientProviderStrategy { - protected DockerClient client; - protected DockerClientConfig config; + @Getter(lazy = true) + private final DockerClient dockerClient = getClientForConfig(getTransportConfig()); private String dockerHostIpAddress; - private static final RateLimiter PING_RATE_LIMITER = RateLimiterBuilder.newBuilder() - .withRate(2, TimeUnit.SECONDS) + private final RateLimiter PING_RATE_LIMITER = RateLimiterBuilder.newBuilder() + .withRate(10, TimeUnit.SECONDS) .withConstantThroughput() .build(); private static final AtomicBoolean FAIL_FAST_ALWAYS = new AtomicBoolean(false); - protected static final Logger LOGGER = LoggerFactory.getLogger(DockerClientProviderStrategy.class); - - /** - * @throws InvalidConfigurationException if this strategy fails - */ - public abstract void test() throws InvalidConfigurationException; - /** * @return a short textual description of the strategy */ @@ -73,6 +68,35 @@ protected int getPriority() { return 0; } + /** + * @throws InvalidConfigurationException if this strategy fails + */ + public abstract TransportConfig getTransportConfig() throws InvalidConfigurationException; + + /** + * @return a usable, tested, Docker client configuration for the host system environment + * + * @deprecated use {@link #getDockerClient()} + */ + @Deprecated + public DockerClient getClient() { + DockerClient dockerClient = getDockerClient(); + try { + Unreliables.retryUntilSuccess(30, TimeUnit.SECONDS, () -> { + return PING_RATE_LIMITER.getWhenReady(() -> { + log.debug("Pinging docker daemon..."); + dockerClient.pingCmd().exec(); + log.debug("Pinged"); + return true; + }); + }); + } catch (TimeoutException e) { + IOUtils.closeQuietly(dockerClient); + throw e; + } + return dockerClient; + } + /** * Determine the right DockerClientConfig to use for building clients by trial-and-error. * @@ -96,19 +120,19 @@ public static DockerClientProviderStrategy getFirstValidStrategy(List strategyClass = (Class) Thread.currentThread().getContextClassLoader().loadClass(it); return Stream.of(strategyClass.newInstance()); } catch (ClassNotFoundException e) { - LOGGER.warn("Can't instantiate a strategy from {} (ClassNotFoundException). " + + log.warn("Can't instantiate a strategy from {} (ClassNotFoundException). " + "This probably means that cached configuration refers to a client provider " + "class that is not available in this version of Testcontainers. Other " + "strategies will be tried instead.", it); return Stream.empty(); } catch (InstantiationException | IllegalAccessException e) { - LOGGER.warn("Can't instantiate a strategy from {}", it, e); + log.warn("Can't instantiate a strategy from {}", it, e); return Stream.empty(); } }) // Ignore persisted strategy if it's not persistable anymore .filter(DockerClientProviderStrategy::isPersistable) - .peek(strategy -> LOGGER.info("Loaded {} from ~/.testcontainers.properties, will try it first", strategy.getClass().getName())), + .peek(strategy -> log.info("Loaded {} from ~/.testcontainers.properties, will try it first", strategy.getClass().getName())), strategies .stream() .filter(DockerClientProviderStrategy::isApplicable) @@ -116,16 +140,35 @@ public static DockerClientProviderStrategy getFirstValidStrategy(List { try { - strategy.test(); - LOGGER.info("Found Docker environment with {}", strategy.getDescription()); - LOGGER.debug( + DockerClient dockerClient = strategy.getDockerClient(); + + Info info; + try { + info = Unreliables.retryUntilSuccess(30, TimeUnit.SECONDS, () -> { + return strategy.PING_RATE_LIMITER.getWhenReady(() -> { + log.debug("Pinging docker daemon..."); + return dockerClient.infoCmd().exec(); + }); + }); + } catch (TimeoutException e) { + IOUtils.closeQuietly(dockerClient); + throw e; + } + log.info("Found Docker environment with {}", strategy.getDescription()); + log.debug( "Transport type: '{}', Docker host: '{}'", TestcontainersConfiguration.getInstance().getTransportType(), - strategy.config != null - ? strategy.config.getDockerHost() - : "" + strategy.getTransportConfig().getDockerHost() ); - strategy.checkOSType(); + + log.debug("Checking Docker OS type for {}", strategy.getDescription()); + String osType = info.getOsType(); + if (StringUtils.isBlank(osType)) { + log.warn("Could not determine Docker OS type"); + } else if (!osType.equals("linux")) { + log.warn("{} is currently not supported", osType); + throw new InvalidConfigurationException(osType + " containers are currently not supported"); + } if (strategy.isPersistable()) { TestcontainersConfiguration.getInstance().updateGlobalConfig("docker.client.strategy", strategy.getClass().getName()); @@ -155,73 +198,57 @@ public static DockerClientProviderStrategy getFirstValidStrategy(List { - LOGGER.error("Could not find a valid Docker environment. Please check configuration. Attempted configurations were:"); + log.error("Could not find a valid Docker environment. Please check configuration. Attempted configurations were:"); for (String failureMessage : configurationFailures) { - LOGGER.error(" " + failureMessage); + log.error(" " + failureMessage); } - LOGGER.error("As no valid configuration was found, execution cannot continue"); + log.error("As no valid configuration was found, execution cannot continue"); FAIL_FAST_ALWAYS.set(true); return new IllegalStateException("Could not find a valid Docker environment. Please see logs and check configuration"); }); } - /** - * @return a usable, tested, Docker client configuration for the host system environment - */ - public DockerClient getClient() { - return new AuditLoggingDockerClient(client); - } - - protected DockerClient getClientForConfig(DockerClientConfig config) { - config = new AuthDelegatingDockerClientConfig(config); + public static DockerClient getClientForConfig(TransportConfig transportConfig) { final DockerHttpClient dockerHttpClient; String transportType = TestcontainersConfiguration.getInstance().getTransportType(); switch (transportType) { case "okhttp": dockerHttpClient = new OkDockerHttpClient.Builder() - .dockerHost(config.getDockerHost()) - .sslConfig(config.getSSLConfig()) + .dockerHost(transportConfig.getDockerHost()) + .sslConfig(transportConfig.getSslConfig()) .build(); break; case "httpclient5": dockerHttpClient = new ZerodepDockerHttpClient.Builder() - .dockerHost(config.getDockerHost()) - .sslConfig(config.getSSLConfig()) + .dockerHost(transportConfig.getDockerHost()) + .sslConfig(transportConfig.getSslConfig()) .build(); break; default: throw new IllegalArgumentException("Unknown transport type '" + transportType + "'"); } - return DockerClientImpl.getInstance(config, dockerHttpClient); - } - - protected void ping(DockerClient client, int timeoutInSeconds) { - try { - Unreliables.retryUntilSuccess(timeoutInSeconds, TimeUnit.SECONDS, () -> { - return PING_RATE_LIMITER.getWhenReady(() -> { - LOGGER.debug("Pinging docker daemon..."); - client.pingCmd().exec(); - return true; - }); - }); - } catch (TimeoutException e) { - IOUtils.closeQuietly(client); - throw e; - } + return DockerClientImpl.getInstance( + new AuthDelegatingDockerClientConfig( + DefaultDockerClientConfig.createDefaultConfigBuilder() + .withDockerHost(transportConfig.getDockerHost().toString()) + .build() + ), + dockerHttpClient + ); } public synchronized String getDockerHostIpAddress() { if (dockerHostIpAddress == null) { - dockerHostIpAddress = resolveDockerHostIpAddress(client, config.getDockerHost()); + dockerHostIpAddress = resolveDockerHostIpAddress(getDockerClient(), getTransportConfig().getDockerHost()); } return dockerHostIpAddress; } @@ -254,15 +281,4 @@ static String resolveDockerHostIpAddress(DockerClient client, URI dockerHost) { return null; } } - - protected void checkOSType() { - LOGGER.debug("Checking Docker OS type for {}", this.getDescription()); - String osType = client.infoCmd().exec().getOsType(); - if (StringUtils.isBlank(osType)) { - LOGGER.warn("Could not determine Docker OS type"); - } else if (!osType.equals("linux")) { - LOGGER.warn("{} is currently not supported", osType); - throw new InvalidConfigurationException(osType + " containers are currently not supported"); - } - } } diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java index fac092044c7..2b0454c1ee6 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java @@ -1,10 +1,12 @@ package org.testcontainers.dockerclient; -import com.github.dockerjava.core.DefaultDockerClientConfig; +import com.github.dockerjava.core.LocalDirectorySSLConfig; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.testcontainers.utility.CommandLine; import org.testcontainers.utility.DockerMachineClient; +import java.net.URI; import java.nio.file.Paths; import java.util.Arrays; import java.util.Optional; @@ -13,15 +15,56 @@ /** * Use Docker machine (if available on the PATH) to locate a Docker environment. + * + * @deprecated this class is used by the SPI and should not be used directly */ @Slf4j -public class DockerMachineClientProviderStrategy extends DockerClientProviderStrategy { - private static final String PING_TIMEOUT_DEFAULT = "30"; - private static final String PING_TIMEOUT_PROPERTY_NAME = "testcontainers.dockermachineprovider.timeout"; +@Deprecated +public final class DockerMachineClientProviderStrategy extends DockerClientProviderStrategy { + + @Getter(lazy = true) + private final TransportConfig transportConfig = resolveTransportConfig(); + + private TransportConfig resolveTransportConfig() throws InvalidConfigurationException { + boolean installed = DockerMachineClient.instance().isInstalled(); + checkArgument(installed, "docker-machine executable was not found on PATH (" + Arrays.toString(CommandLine.getSystemPath()) + ")"); + + Optional machineNameOptional = DockerMachineClient.instance().getDefaultMachine(); + checkArgument(machineNameOptional.isPresent(), "docker-machine is installed but no default machine could be found"); + String machineName = machineNameOptional.get(); + + log.info("Found docker-machine, and will use machine named {}", machineName); + + DockerMachineClient.instance().ensureMachineRunning(machineName); + + String dockerDaemonIpAddress = DockerMachineClient.instance().getDockerDaemonIpAddress(machineName); + + log.info("Docker daemon IP address for docker machine {} is {}", machineName, dockerDaemonIpAddress); + + return TransportConfig.builder() + .dockerHost(URI.create("tcp://" + dockerDaemonIpAddress + ":2376")) + .sslConfig( + new LocalDirectorySSLConfig( + Paths.get(System.getProperty("user.home") + "/.docker/machine/certs/").toString() + ) + ) + .build(); + } @Override protected boolean isApplicable() { - return DockerMachineClient.instance().isInstalled(); + boolean installed = DockerMachineClient.instance().isInstalled(); + if (!installed) { + log.info("docker-machine executable was not found on PATH ({})", Arrays.toString(CommandLine.getSystemPath())); + return false; + } + + Optional machineNameOptional = DockerMachineClient.instance().getDefaultMachine(); + if (!machineNameOptional.isPresent()) { + log.info("docker-machine is installed but no default machine could be found"); + } + + return true; } @Override @@ -34,40 +77,6 @@ protected int getPriority() { return EnvironmentAndSystemPropertyClientProviderStrategy.PRIORITY - 100; } - @Override - public void test() throws InvalidConfigurationException { - - try { - boolean installed = DockerMachineClient.instance().isInstalled(); - checkArgument(installed, "docker-machine executable was not found on PATH (" + Arrays.toString(CommandLine.getSystemPath()) + ")"); - - Optional machineNameOptional = DockerMachineClient.instance().getDefaultMachine(); - checkArgument(machineNameOptional.isPresent(), "docker-machine is installed but no default machine could be found"); - String machineName = machineNameOptional.get(); - - log.info("Found docker-machine, and will use machine named {}", machineName); - - DockerMachineClient.instance().ensureMachineRunning(machineName); - - String dockerDaemonIpAddress = DockerMachineClient.instance().getDockerDaemonIpAddress(machineName); - - log.info("Docker daemon IP address for docker machine {} is {}", machineName, dockerDaemonIpAddress); - - config = DefaultDockerClientConfig.createDefaultConfigBuilder() - .withDockerHost("tcp://" + dockerDaemonIpAddress + ":2376") - .withDockerTlsVerify(true) - .withDockerCertPath(Paths.get(System.getProperty("user.home") + "/.docker/machine/certs/").toString()) - .build(); - client = getClientForConfig(config); - } catch (Exception e) { - throw new InvalidConfigurationException(e.getMessage()); - } - - // If the docker-machine VM has started, the docker daemon may still not be ready. Retry pinging until it works. - final int timeout = Integer.parseInt(System.getProperty(PING_TIMEOUT_PROPERTY_NAME, PING_TIMEOUT_DEFAULT)); - ping(client, timeout); - } - @Override public String getDescription() { return "docker-machine"; diff --git a/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java index 4b02eded72b..c95c9242a07 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java @@ -1,29 +1,28 @@ package org.testcontainers.dockerclient; import com.github.dockerjava.core.DefaultDockerClientConfig; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang.SystemUtils; +import com.github.dockerjava.core.DockerClientConfig; /** * Use environment variables and system properties (as supported by the underlying DockerClient DefaultConfigBuilder) * to try and locate a docker environment. + * + * @deprecated this class is used by the SPI and should not be used directly */ -@Slf4j -public class EnvironmentAndSystemPropertyClientProviderStrategy extends DockerClientProviderStrategy { +@Deprecated +public final class EnvironmentAndSystemPropertyClientProviderStrategy extends DockerClientProviderStrategy { public static final int PRIORITY = 100; - private static final String PING_TIMEOUT_DEFAULT = "10"; - private static final String PING_TIMEOUT_PROPERTY_NAME = "testcontainers.environmentprovider.timeout"; - - public EnvironmentAndSystemPropertyClientProviderStrategy() { - // Try using environment variables - config = DefaultDockerClientConfig.createDefaultConfigBuilder().build(); - } + // Try using environment variables + private final DockerClientConfig dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder().build(); @Override - protected boolean isApplicable() { - return "tcp".equalsIgnoreCase(config.getDockerHost().getScheme()) || SystemUtils.IS_OS_LINUX; + public TransportConfig getTransportConfig() { + return TransportConfig.builder() + .dockerHost(dockerClientConfig.getDockerHost()) + .sslConfig(dockerClientConfig.getSSLConfig()) + .build(); } @Override @@ -31,25 +30,8 @@ protected int getPriority() { return PRIORITY; } - @Override - public void test() throws InvalidConfigurationException { - - try { - client = getClientForConfig(config); - - final int timeout = Integer.parseInt(System.getProperty(PING_TIMEOUT_PROPERTY_NAME, PING_TIMEOUT_DEFAULT)); - ping(client, timeout); - } catch (Exception | UnsatisfiedLinkError e) { - log.error("ping failed with configuration {} due to {}", getDescription(), e.toString(), e); - throw new InvalidConfigurationException("ping failed"); - } - - log.info("Found docker client settings from environment"); - } - @Override public String getDescription() { - return "Environment variables, system properties and defaults. Resolved dockerHost=" + - (config != null ? config.getDockerHost() : ""); + return "Environment variables, system properties and defaults. Resolved dockerHost=" + dockerClientConfig.getDockerHost(); } } diff --git a/core/src/main/java/org/testcontainers/dockerclient/LogToStringContainerCallback.java b/core/src/main/java/org/testcontainers/dockerclient/LogToStringContainerCallback.java index e0ffce7c3a1..d376b685a91 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/LogToStringContainerCallback.java +++ b/core/src/main/java/org/testcontainers/dockerclient/LogToStringContainerCallback.java @@ -1,15 +1,19 @@ package org.testcontainers.dockerclient; +import com.github.dockerjava.api.async.ResultCallback; import com.github.dockerjava.api.model.Frame; -import com.github.dockerjava.core.command.LogContainerResultCallback; -public class LogToStringContainerCallback extends LogContainerResultCallback { +/** + * + * @deprecated use {@link ResultCallback.Adapter} + */ +@Deprecated +public class LogToStringContainerCallback extends ResultCallback.Adapter { private final StringBuffer log = new StringBuffer(); @Override public void onNext(Frame frame) { log.append(new String(frame.getPayload())); - super.onNext(frame); } @Override diff --git a/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java index 329bde36433..fc3c65fd3dd 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java @@ -1,53 +1,31 @@ package org.testcontainers.dockerclient; -import com.github.dockerjava.core.DefaultDockerClientConfig; -import com.github.dockerjava.core.DockerClientConfig; -import lombok.RequiredArgsConstructor; -import lombok.experimental.Delegate; -import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.SystemUtils; -import org.jetbrains.annotations.NotNull; -@Slf4j -public class NpipeSocketClientProviderStrategy extends DockerClientProviderStrategy { +import java.net.URI; + +/** + * + * @deprecated this class is used by the SPI and should not be used directly + */ +@Deprecated +public final class NpipeSocketClientProviderStrategy extends DockerClientProviderStrategy { protected static final String DOCKER_SOCK_PATH = "//./pipe/docker_engine"; private static final String SOCKET_LOCATION = "npipe://" + DOCKER_SOCK_PATH; - private static final String PING_TIMEOUT_DEFAULT = "10"; - private static final String PING_TIMEOUT_PROPERTY_NAME = "testcontainers.npipesocketprovider.timeout"; - public static final int PRIORITY = EnvironmentAndSystemPropertyClientProviderStrategy.PRIORITY - 20; @Override - protected boolean isApplicable() { - return SystemUtils.IS_OS_WINDOWS; + public TransportConfig getTransportConfig() { + return TransportConfig.builder() + .dockerHost(URI.create(SOCKET_LOCATION)) + .build(); } @Override - public void test() throws InvalidConfigurationException { - try { - config = tryConfiguration(); - log.info("Accessing docker with {}", getDescription()); - } catch (Exception | UnsatisfiedLinkError e) { - throw new InvalidConfigurationException("ping failed", e); - } - } - - @NotNull - private DockerClientConfig tryConfiguration() { - config = new DelegatingDockerClientConfig( - DefaultDockerClientConfig.createDefaultConfigBuilder() - .withDockerHost(SOCKET_LOCATION) - .withDockerTlsVerify(false) - .build() - ); - client = getClientForConfig(config); - - final int timeout = Integer.parseInt(System.getProperty(PING_TIMEOUT_PROPERTY_NAME, PING_TIMEOUT_DEFAULT)); - ping(client, timeout); - - return config; + protected boolean isApplicable() { + return SystemUtils.IS_OS_WINDOWS; } @Override @@ -59,11 +37,4 @@ public String getDescription() { protected int getPriority() { return PRIORITY; } - - @RequiredArgsConstructor - private static class DelegatingDockerClientConfig implements DockerClientConfig { - - @Delegate - final DockerClientConfig dockerClientConfig; - } } diff --git a/core/src/main/java/org/testcontainers/dockerclient/ProxiedUnixSocketClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/ProxiedUnixSocketClientProviderStrategy.java deleted file mode 100644 index b1a21da724c..00000000000 --- a/core/src/main/java/org/testcontainers/dockerclient/ProxiedUnixSocketClientProviderStrategy.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.testcontainers.dockerclient; - -/** - * @deprecated no longer needed since `docker-java` supports all transport modes now - */ -@Deprecated -public class ProxiedUnixSocketClientProviderStrategy extends UnixSocketClientProviderStrategy { - - public static final int PRIORITY = EnvironmentAndSystemPropertyClientProviderStrategy.PRIORITY - 100; - - @Override - protected boolean isApplicable() { - return false; - } - - @Override - protected int getPriority() { - return PRIORITY; - } - - @Override - public void test() throws InvalidConfigurationException { - throw new InvalidConfigurationException("Deprecated"); - } - - @Override - public String getDescription() { - return "[Deprecated] local Unix socket (via TCP proxy)"; - } - -} diff --git a/core/src/main/java/org/testcontainers/dockerclient/TransportConfig.java b/core/src/main/java/org/testcontainers/dockerclient/TransportConfig.java new file mode 100644 index 00000000000..cbd1278b179 --- /dev/null +++ b/core/src/main/java/org/testcontainers/dockerclient/TransportConfig.java @@ -0,0 +1,18 @@ +package org.testcontainers.dockerclient; + +import com.github.dockerjava.transport.SSLConfig; +import lombok.Builder; +import lombok.Value; +import org.jetbrains.annotations.Nullable; + +import java.net.URI; + +@Builder +@Value +public class TransportConfig { + + URI dockerHost; + + @Nullable + SSLConfig sslConfig; +} diff --git a/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java index ea192f9b557..fd4ef4f9e6e 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java @@ -1,48 +1,27 @@ package org.testcontainers.dockerclient; -import com.github.dockerjava.core.DefaultDockerClientConfig; -import com.github.dockerjava.core.DockerClientConfig; -import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.SystemUtils; -import org.jetbrains.annotations.NotNull; -import org.testcontainers.utility.ComparableVersion; import java.io.IOException; +import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -@Slf4j -public class UnixSocketClientProviderStrategy extends DockerClientProviderStrategy { +/** + * + * @deprecated this class is used by the SPI and should not be used directly + */ +@Deprecated +public final class UnixSocketClientProviderStrategy extends DockerClientProviderStrategy { protected static final String DOCKER_SOCK_PATH = "/var/run/docker.sock"; private static final String SOCKET_LOCATION = "unix://" + DOCKER_SOCK_PATH; private static final int SOCKET_FILE_MODE_MASK = 0xc000; - private static final String PING_TIMEOUT_DEFAULT = "10"; - private static final String PING_TIMEOUT_PROPERTY_NAME = "testcontainers.unixsocketprovider.timeout"; public static final int PRIORITY = EnvironmentAndSystemPropertyClientProviderStrategy.PRIORITY - 20; @Override - protected boolean isApplicable() { - final boolean nettyDoesSupportMacUnixSockets = SystemUtils.IS_OS_MAC_OSX && - ComparableVersion.OS_VERSION.isGreaterThanOrEqualTo("10.12"); - - return SystemUtils.IS_OS_LINUX || nettyDoesSupportMacUnixSockets; - } - - @Override - public void test() throws InvalidConfigurationException { - try { - config = tryConfiguration(SOCKET_LOCATION); - log.info("Accessing docker with local Unix socket"); - } catch (Exception | UnsatisfiedLinkError e) { - throw new InvalidConfigurationException("ping failed", e); - } - } - - @NotNull - protected DockerClientConfig tryConfiguration(String dockerHost) { - + public TransportConfig getTransportConfig() throws InvalidConfigurationException { Path dockerSocketFile = Paths.get(DOCKER_SOCK_PATH); Integer mode; try { @@ -55,16 +34,14 @@ protected DockerClientConfig tryConfiguration(String dockerHost) { throw new InvalidConfigurationException("Found docker unix domain socket but file mode was not as expected (expected: srwxr-xr-x). This problem is possibly due to occurrence of this issue in the past: https://github.com/docker/docker/issues/13121"); } - config = DefaultDockerClientConfig.createDefaultConfigBuilder() - .withDockerHost(dockerHost) - .withDockerTlsVerify(false) - .build(); - client = getClientForConfig(config); - - final int timeout = Integer.parseInt(System.getProperty(PING_TIMEOUT_PROPERTY_NAME, PING_TIMEOUT_DEFAULT)); - ping(client, timeout); + return TransportConfig.builder() + .dockerHost(URI.create(SOCKET_LOCATION)) + .build(); + } - return config; + @Override + protected boolean isApplicable() { + return SystemUtils.IS_OS_LINUX; } @Override diff --git a/core/src/main/java/org/testcontainers/dockerclient/WindowsClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/WindowsClientProviderStrategy.java deleted file mode 100644 index f53ac10d044..00000000000 --- a/core/src/main/java/org/testcontainers/dockerclient/WindowsClientProviderStrategy.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.testcontainers.dockerclient; - -import com.github.dockerjava.core.DefaultDockerClientConfig; -import com.github.dockerjava.core.DockerClientConfig; -import org.apache.commons.lang.SystemUtils; -import org.jetbrains.annotations.NotNull; - -public class WindowsClientProviderStrategy extends DockerClientProviderStrategy { - - private static final int PING_TIMEOUT_DEFAULT = 5; - private static final String PING_TIMEOUT_PROPERTY_NAME = "testcontainers.windowsprovider.timeout"; - - @Override - protected boolean isApplicable() { - return SystemUtils.IS_OS_WINDOWS; - } - - @Override - public void test() throws InvalidConfigurationException { - config = tryConfiguration("tcp://localhost:2375"); - } - - @Override - public String getDescription() { - return "Docker for Windows (via TCP port 2375)"; - } - - @NotNull - protected DockerClientConfig tryConfiguration(String dockerHost) { - config = DefaultDockerClientConfig.createDefaultConfigBuilder() - .withDockerHost(dockerHost) - .withDockerTlsVerify(false) - .build(); - client = getClientForConfig(config); - - final int timeout = Integer.getInteger(PING_TIMEOUT_PROPERTY_NAME, PING_TIMEOUT_DEFAULT); - ping(client, timeout); - - return config; - } -} diff --git a/core/src/main/java/org/testcontainers/images/LoggedPullImageResultCallback.java b/core/src/main/java/org/testcontainers/images/LoggedPullImageResultCallback.java index 71b797298aa..2ba8ba6df11 100644 --- a/core/src/main/java/org/testcontainers/images/LoggedPullImageResultCallback.java +++ b/core/src/main/java/org/testcontainers/images/LoggedPullImageResultCallback.java @@ -1,7 +1,7 @@ package org.testcontainers.images; +import com.github.dockerjava.api.command.PullImageResultCallback; import com.github.dockerjava.api.model.PullResponseItem; -import com.github.dockerjava.core.command.PullImageResultCallback; import org.slf4j.Logger; import java.io.Closeable; diff --git a/core/src/main/java/org/testcontainers/images/TimeLimitedLoggedPullImageResultCallback.java b/core/src/main/java/org/testcontainers/images/TimeLimitedLoggedPullImageResultCallback.java index 274eedefded..b6d8539c3e8 100644 --- a/core/src/main/java/org/testcontainers/images/TimeLimitedLoggedPullImageResultCallback.java +++ b/core/src/main/java/org/testcontainers/images/TimeLimitedLoggedPullImageResultCallback.java @@ -1,7 +1,7 @@ package org.testcontainers.images; +import com.github.dockerjava.api.command.PullImageResultCallback; import com.github.dockerjava.api.model.PullResponseItem; -import com.github.dockerjava.core.command.PullImageResultCallback; import org.slf4j.Logger; import org.testcontainers.utility.TestcontainersConfiguration; diff --git a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java index 638db2e351d..133b6e1c4b0 100644 --- a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java +++ b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java @@ -2,8 +2,8 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.BuildImageCmd; +import com.github.dockerjava.api.command.BuildImageResultCallback; import com.github.dockerjava.api.model.BuildResponseItem; -import com.github.dockerjava.core.command.BuildImageResultCallback; import lombok.Cleanup; import lombok.Getter; import lombok.extern.slf4j.Slf4j; diff --git a/core/src/main/resources/META-INF/services/README.md b/core/src/main/resources/META-INF/services/README.md deleted file mode 100644 index fd11f50456e..00000000000 --- a/core/src/main/resources/META-INF/services/README.md +++ /dev/null @@ -1 +0,0 @@ -The contents of this folder is a workaround for issues described here: https://github.com/KostyaSha/docker-java-shade/issues/1 \ No newline at end of file diff --git a/core/src/main/resources/META-INF/services/org.testcontainers.dockerclient.DockerClientProviderStrategy b/core/src/main/resources/META-INF/services/org.testcontainers.dockerclient.DockerClientProviderStrategy index cde7fc827e4..006c3ec7f82 100644 --- a/core/src/main/resources/META-INF/services/org.testcontainers.dockerclient.DockerClientProviderStrategy +++ b/core/src/main/resources/META-INF/services/org.testcontainers.dockerclient.DockerClientProviderStrategy @@ -1,5 +1,4 @@ org.testcontainers.dockerclient.EnvironmentAndSystemPropertyClientProviderStrategy org.testcontainers.dockerclient.UnixSocketClientProviderStrategy org.testcontainers.dockerclient.DockerMachineClientProviderStrategy -org.testcontainers.dockerclient.WindowsClientProviderStrategy org.testcontainers.dockerclient.NpipeSocketClientProviderStrategy diff --git a/core/src/main/resources/META-INF/services/org.testcontainers.shaded.com.fasterxml.jackson.core.JsonFactory b/core/src/main/resources/META-INF/services/org.testcontainers.shaded.com.fasterxml.jackson.core.JsonFactory deleted file mode 100755 index 239a78a0a82..00000000000 --- a/core/src/main/resources/META-INF/services/org.testcontainers.shaded.com.fasterxml.jackson.core.JsonFactory +++ /dev/null @@ -1 +0,0 @@ -com.fasterxml.jackson.core.JsonFactory diff --git a/core/src/main/resources/META-INF/services/org.testcontainers.shaded.com.fasterxml.jackson.core.ObjectCodec b/core/src/main/resources/META-INF/services/org.testcontainers.shaded.com.fasterxml.jackson.core.ObjectCodec deleted file mode 100755 index f126bb401c2..00000000000 --- a/core/src/main/resources/META-INF/services/org.testcontainers.shaded.com.fasterxml.jackson.core.ObjectCodec +++ /dev/null @@ -1 +0,0 @@ -com.fasterxml.jackson.databind.ObjectMapper diff --git a/core/src/main/resources/META-INF/services/org.testcontainers.shaded.com.github.dockerjava.api.command.DockerCmdExecFactory b/core/src/main/resources/META-INF/services/org.testcontainers.shaded.com.github.dockerjava.api.command.DockerCmdExecFactory deleted file mode 100644 index f0686bc9f31..00000000000 --- a/core/src/main/resources/META-INF/services/org.testcontainers.shaded.com.github.dockerjava.api.command.DockerCmdExecFactory +++ /dev/null @@ -1 +0,0 @@ -com.github.dockerjava.jaxrs.DockerCmdExecFactoryImpl \ No newline at end of file diff --git a/core/src/main/resources/META-INF/services/org.testcontainers.shaded.javax.ws.rs.ext.RuntimeDelegate b/core/src/main/resources/META-INF/services/org.testcontainers.shaded.javax.ws.rs.ext.RuntimeDelegate deleted file mode 100644 index 550198cfdf7..00000000000 --- a/core/src/main/resources/META-INF/services/org.testcontainers.shaded.javax.ws.rs.ext.RuntimeDelegate +++ /dev/null @@ -1 +0,0 @@ -org.glassfish.jersey.internal.RuntimeDelegateImpl \ No newline at end of file diff --git a/core/src/main/resources/META-INF/services/org.testcontainers.shaded.org.glassfish.hk2.extension.ServiceLocatorGenerator b/core/src/main/resources/META-INF/services/org.testcontainers.shaded.org.glassfish.hk2.extension.ServiceLocatorGenerator deleted file mode 100644 index 7834b52dd6d..00000000000 --- a/core/src/main/resources/META-INF/services/org.testcontainers.shaded.org.glassfish.hk2.extension.ServiceLocatorGenerator +++ /dev/null @@ -1 +0,0 @@ -org.jvnet.hk2.external.generator.ServiceLocatorGeneratorImpl diff --git a/core/src/main/resources/META-INF/services/org.testcontainers.shaded.org.jvnet.hk2.external.generator.ServiceLocatorGenerator b/core/src/main/resources/META-INF/services/org.testcontainers.shaded.org.jvnet.hk2.external.generator.ServiceLocatorGenerator deleted file mode 100644 index 25f6b0c3fc9..00000000000 --- a/core/src/main/resources/META-INF/services/org.testcontainers.shaded.org.jvnet.hk2.external.generator.ServiceLocatorGenerator +++ /dev/null @@ -1 +0,0 @@ -org.testcontainers.shaded.org.jvnet.hk2.external.generator.ServiceLocatorGeneratorImpl From 8e57ae60565f24c01ec7ef964dc37130a022eba4 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sat, 11 Jul 2020 20:14:44 +0200 Subject: [PATCH 1506/5615] Use `EnvAndSysPropClientProviderStrategy` only if `DOCKER_HOST` is set (#2987) --- .../EnvironmentAndSystemPropertyClientProviderStrategy.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java index c95c9242a07..e0afba9af93 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java @@ -17,6 +17,11 @@ public final class EnvironmentAndSystemPropertyClientProviderStrategy extends Do // Try using environment variables private final DockerClientConfig dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder().build(); + @Override + protected boolean isApplicable() { + return System.getenv("DOCKER_HOST") != null; + } + @Override public TransportConfig getTransportConfig() { return TransportConfig.builder() From 74a9240473ef2404ff9b8bd76f53f33607401cae Mon Sep 17 00:00:00 2001 From: Simon Schneider <10846939+raynigon@users.noreply.github.com> Date: Sun, 12 Jul 2020 13:58:10 +0200 Subject: [PATCH 1507/5615] Convert Docs for the Junit5 (#2977) Co-authored-by: Richard North --- docs/test_framework_integration/junit_5.md | 75 +++---------------- .../junit/jupiter/MixedLifecycleTests.java | 38 ++++++++++ ...ntainersNestedRestartedContainerTests.java | 13 +++- 3 files changed, 59 insertions(+), 67 deletions(-) create mode 100644 modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/MixedLifecycleTests.java diff --git a/docs/test_framework_integration/junit_5.md b/docs/test_framework_integration/junit_5.md index 11798484b0f..7f3689c0301 100644 --- a/docs/test_framework_integration/junit_5.md +++ b/docs/test_framework_integration/junit_5.md @@ -23,28 +23,9 @@ executed. Containers declared as instance fields will be started and stopped for unsupported and may have unintended side effects. *Example:* -```java -@Testcontainers -class MyTestcontainersTests { - - // will be shared between test methods - @Container - private static final MySQLContainer MY_SQL_CONTAINER = new MySQLContainer(); - - // will be started before and stopped after each test method - @Container - private PostgreSQLContainer postgresqlContainer = new PostgreSQLContainer() - .withDatabaseName("foo") - .withUsername("foo") - .withPassword("secret"); - @Test - void test() { - assertTrue(MY_SQL_CONTAINER.isRunning()); - assertTrue(postgresqlContainer.isRunning()); - } -} -``` - + +[Mixed Lifecycle](../../modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/MixedLifecycleTests.java) inside_block:testClass + ## Examples @@ -55,36 +36,10 @@ To use the Testcontainers extension annotate your test class with `@Testcontaine To define a restarted container, define an instance field inside your test class and annotate it with the `@Container` annotation. -```java -@Testcontainers -class SomeTest { - - @Container - private MySQLContainer mySQLContainer = new MySQLContainer(); - - @Test - void someTestMethod() { - String url = mySQLContainer.getJdbcUrl(); - - // create a connection and run test as normal - } - - @Nested - class NestedTests { + +[Restarted Containers](../../modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java) inside_block:testClass + - @Container - private final PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer(); - - void nestedTestMethod() { - // top level container is restarted for nested methods - String mySqlUrl = mySQLContainer.getJdbcUrl(); - - // nested containers are only available inside their nested class - String postgresUrl = postgreSQLContainer.getJdbcUrl(); - } - } -} -``` ### Shared containers @@ -92,21 +47,9 @@ Shared containers are defined as static fields in a top level test class and hav Note that shared containers can't be declared inside nested test classes. This is because nested test classes have to be defined non-static and can't therefore have static fields. -```java -@Testcontainers -class SomeTest { - - @Container - private static final MySQLContainer MY_SQL_CONTAINER = new MySQLContainer(); - - @Test - void someTestMethod() { - String url = MY_SQL_CONTAINER.getJdbcUrl(); - - // create a connection and run test as normal - } -} -``` + +[Shared Container](../../modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/MixedLifecycleTests.java) lines:18-23,32-33,35-36 + ## Singleton containers diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/MixedLifecycleTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/MixedLifecycleTests.java new file mode 100644 index 00000000000..791c887cc19 --- /dev/null +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/MixedLifecycleTests.java @@ -0,0 +1,38 @@ +package org.testcontainers.junit.jupiter; + +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.HttpClientBuilder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.DockerComposeContainer; +import org.testcontainers.containers.MySQLContainer; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.containers.wait.strategy.Wait; + +import static org.junit.jupiter.api.Assertions.assertTrue; + + +// testClass { +@Testcontainers +class MixedLifecycleTests { + + // will be shared between test methods + @Container + private static final MySQLContainer MY_SQL_CONTAINER = new MySQLContainer(); + + // will be started before and stopped after each test method + @Container + private PostgreSQLContainer postgresqlContainer = new PostgreSQLContainer() + .withDatabaseName("foo") + .withUsername("foo") + .withPassword("secret"); + + @Test + void test() { + assertTrue(MY_SQL_CONTAINER.isRunning()); + assertTrue(postgresqlContainer.isRunning()); + } +} +// } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java index 4d27c410979..fb753744425 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java @@ -8,21 +8,26 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +// testClass { @Testcontainers class TestcontainersNestedRestartedContainerTests { @Container private final GenericContainer topLevelContainer = new GenericContainer("httpd:2.4-alpine") .withExposedPorts(80); + // }} private static String topLevelContainerId; private static String nestedContainerId; + // testClass { @Test void top_level_container_should_be_running() { assertTrue(topLevelContainer.isRunning()); +// }} topLevelContainerId = topLevelContainer.getContainerId(); +// testClass {{ } @Nested @@ -34,16 +39,20 @@ class NestedTestCase { @Test void both_containers_should_be_running() { + // top level container is restarted for nested methods assertTrue(topLevelContainer.isRunning()); + // nested containers are only available inside their nested class assertTrue(nestedContainer.isRunning()); - +// }}} if (nestedContainerId == null) { nestedContainerId = nestedContainer.getContainerId(); } else { assertNotEquals(nestedContainerId, nestedContainer.getContainerId()); } +// testClass {{ } + // } @Test void containers_should_not_be_the_same() { assertNotEquals(topLevelContainer.getContainerId(), nestedContainer.getContainerId()); @@ -65,5 +74,7 @@ void ids_should_not_change() { assertNotEquals(nestedContainerId, nestedContainer.getContainerId()); } } +// testClass {{{ } } +// } From c137996b2932b223d22cdd81215e96b098a28255 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 12 Jul 2020 18:22:54 +0200 Subject: [PATCH 1508/5615] Allow overriding detected host (#2991) --- .../dockerclient/DockerClientProviderStrategy.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index 3f288d015d0..e21c75a581d 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -255,6 +255,11 @@ public synchronized String getDockerHostIpAddress() { @VisibleForTesting static String resolveDockerHostIpAddress(DockerClient client, URI dockerHost) { + String hostOverride = System.getenv("TESTCONTAINERS_HOST_OVERRIDE"); + if (!StringUtils.isBlank(hostOverride)) { + return hostOverride; + } + switch (dockerHost.getScheme()) { case "http": case "https": From 50c778fa39c8adab5b0ee078ee47b074482112ac Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 15 Jul 2020 14:14:07 +0200 Subject: [PATCH 1509/5615] Add a rootless Docker strategy (#2985) Closes #2943, #1770 --- .github/workflows/ci-rootless.yml | 31 ++++++++++ .github/workflows/ci.yml | 2 +- .../testcontainers/DockerClientFactory.java | 15 +++++ .../containers/DockerComposeContainer.java | 24 ++++---- .../RootlessDockerClientProviderStrategy.java | 58 +++++++++++++++++++ .../UnixSocketClientProviderStrategy.java | 2 +- .../utility/ResourceReaper.java | 2 +- ....dockerclient.DockerClientProviderStrategy | 1 + .../DockerComposeOverridesTest.java | 13 ++++- .../containers/GenericContainerTest.java | 6 ++ .../DockerClientConfigUtilsTest.java | 5 ++ .../auth-config/docker-credential-fake | 12 ++-- docs/features/configuration.md | 17 ++++++ 13 files changed, 167 insertions(+), 21 deletions(-) create mode 100644 .github/workflows/ci-rootless.yml create mode 100644 core/src/main/java/org/testcontainers/dockerclient/RootlessDockerClientProviderStrategy.java rename core/src/test/java/org/testcontainers/{junit => containers}/DockerComposeOverridesTest.java (90%) diff --git a/.github/workflows/ci-rootless.yml b/.github/workflows/ci-rootless.yml new file mode 100644 index 00000000000..e752747b804 --- /dev/null +++ b/.github/workflows/ci-rootless.yml @@ -0,0 +1,31 @@ +name: CI-Docker-Rootless + +on: + pull_request: {} + push: { branches: [ master ] } + +jobs: + test: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: debug + run: id -u; whoami + - name: uninstall rootful Docker + run: sudo apt-get -q -y --purge remove moby-engine moby-buildx && sudo rm -rf /var/run/docker.sock + - name: install rootless Docker + run: curl -fsSL https://get.docker.com/rootless | sh + - name: start rootless Docker + run: PATH=$HOME/bin:$PATH XDG_RUNTIME_DIR=/tmp/docker-$(id -u) dockerd-rootless.sh --experimental --storage-driver vfs & + - name: Build with Gradle + run: XDG_RUNTIME_DIR=/tmp/docker-$(id -u) ./gradlew --no-daemon --scan testcontainers:test + - name: aggregate test reports with ciMate + if: always() + continue-on-error: true + env: + CIMATE_PROJECT_ID: 2348n4vl + CIMATE_CI_KEY: "CI / rootless Docker" + run: | + wget -q https://get.cimate.io/release/linux/cimate + chmod +x cimate + ./cimate "**/TEST-*.xml" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b965eb77dc9..668dde2b28c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,7 +56,7 @@ jobs: run: docker image prune -af - name: Build and test with Gradle (${{matrix.gradle_args}}) run: | - ./gradlew --no-daemon --continue --scan --info ${{matrix.gradle_args}} + ./gradlew --no-daemon --continue --scan ${{matrix.gradle_args}} - name: Aggregate test reports with ciMate if: always() continue-on-error: true diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index baebe164c48..b9c4d508f25 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -18,6 +18,7 @@ import lombok.SneakyThrows; import lombok.Synchronized; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.StringUtils; import org.testcontainers.dockerclient.DockerClientProviderStrategy; import org.testcontainers.dockerclient.DockerMachineClientProviderStrategy; import org.testcontainers.images.TimeLimitedLoggedPullImageResultCallback; @@ -29,6 +30,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -129,6 +131,19 @@ private DockerClientProviderStrategy getOrInitializeStrategy() { return strategy; } + @UnstableAPI + public String getDockerUnixSocketPath() { + String dockerSocketOverride = System.getenv("TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE"); + if (!StringUtils.isBlank(dockerSocketOverride)) { + return dockerSocketOverride; + } + + URI dockerHost = getOrInitializeStrategy().getTransportConfig().getDockerHost(); + return "unix".equals(dockerHost.getScheme()) + ? dockerHost.getRawPath() + : "/var/run/docker.sock"; + } + /** * * @return a new initialized Docker client diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index c3fe9ab17f9..8c07c036d11 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -59,7 +59,6 @@ import static com.google.common.base.Strings.isNullOrEmpty; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; -import static org.testcontainers.containers.BindMode.READ_ONLY; import static org.testcontainers.containers.BindMode.READ_WRITE; /** @@ -580,7 +579,6 @@ interface DockerCompose { */ class ContainerisedDockerCompose extends GenericContainer implements DockerCompose { - private static final String DOCKER_SOCKET_PATH = "/var/run/docker.sock"; public static final char UNIX_PATH_SEPERATOR = ':'; public ContainerisedDockerCompose(List composeFiles, String identifier) { @@ -601,24 +599,18 @@ public ContainerisedDockerCompose(List composeFiles, String identifier) { final String composeFileEnvVariableValue = Joiner.on(UNIX_PATH_SEPERATOR).join(absoluteDockerComposeFiles); // we always need the UNIX path separator logger().debug("Set env COMPOSE_FILE={}", composeFileEnvVariableValue); addEnv(ENV_COMPOSE_FILE, composeFileEnvVariableValue); - addFileSystemBind(pwd, containerPwd, READ_ONLY); + addFileSystemBind(pwd, containerPwd, READ_WRITE); // Ensure that compose can access docker. Since the container is assumed to be running on the same machine // as the docker daemon, just mapping the docker control socket is OK. // As there seems to be a problem with mapping to the /var/run directory in certain environments (e.g. CircleCI) // we map the socket file outside of /var/run, as just /docker.sock - addFileSystemBind(getDockerSocketHostPath(), "/docker.sock", READ_WRITE); + addFileSystemBind("/" + DockerClientFactory.instance().getDockerUnixSocketPath(), "/docker.sock", READ_WRITE); addEnv("DOCKER_HOST", "unix:///docker.sock"); setStartupCheckStrategy(new IndefiniteWaitOneShotStartupCheckStrategy()); setWorkingDirectory(containerPwd); } - private String getDockerSocketHostPath() { - return SystemUtils.IS_OS_WINDOWS - ? "/" + DOCKER_SOCKET_PATH - : DOCKER_SOCKET_PATH; - } - @Override public void invoke() { super.start(); @@ -681,16 +673,26 @@ public DockerCompose withEnv(Map env) { return this; } + @VisibleForTesting + static boolean executableExists() { + return CommandLine.executableExists(COMPOSE_EXECUTABLE); + } + @Override public void invoke() { // bail out early - if (!CommandLine.executableExists(COMPOSE_EXECUTABLE)) { + if (!executableExists()) { throw new ContainerLaunchException("Local Docker Compose not found. Is " + COMPOSE_EXECUTABLE + " on the PATH?"); } final Map environment = Maps.newHashMap(env); environment.put(ENV_PROJECT_NAME, identifier); + String dockerHost = System.getenv("DOCKER_HOST"); + if (dockerHost == null) { + dockerHost = "unix://" + DockerClientFactory.instance().getDockerUnixSocketPath(); + } + environment.put("DOCKER_HOST", dockerHost); final Stream absoluteDockerComposeFilePaths = composeFiles.stream() .map(File::getAbsolutePath) diff --git a/core/src/main/java/org/testcontainers/dockerclient/RootlessDockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/RootlessDockerClientProviderStrategy.java new file mode 100644 index 00000000000..cc6b23e506d --- /dev/null +++ b/core/src/main/java/org/testcontainers/dockerclient/RootlessDockerClientProviderStrategy.java @@ -0,0 +1,58 @@ +package org.testcontainers.dockerclient; + +import com.sun.jna.Library; +import com.sun.jna.Native; +import org.apache.commons.lang.SystemUtils; + +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * + * @deprecated this class is used by the SPI and should not be used directly + */ +@Deprecated +public final class RootlessDockerClientProviderStrategy extends DockerClientProviderStrategy { + + public static final int PRIORITY = UnixSocketClientProviderStrategy.PRIORITY + 1; + + private Path getSocketPath() { + String xdgRuntimeDir = System.getenv("XDG_RUNTIME_DIR"); + if (xdgRuntimeDir == null) { + xdgRuntimeDir = "/run/user/" + LibC.INSTANCE.getuid(); + } + return Paths.get(xdgRuntimeDir).resolve("docker.sock"); + } + + @Override + public TransportConfig getTransportConfig() throws InvalidConfigurationException { + return TransportConfig.builder() + .dockerHost(URI.create("unix://" + getSocketPath().toString())) + .build(); + } + + @Override + protected boolean isApplicable() { + return SystemUtils.IS_OS_LINUX && Files.exists(getSocketPath()); + } + + @Override + public String getDescription() { + return "Rootless Docker accessed via Unix socket (" + getSocketPath() + ")"; + } + + @Override + protected int getPriority() { + return PRIORITY; + } + + private interface LibC extends Library { + + LibC INSTANCE = Native.loadLibrary("c", LibC.class); + + int getuid(); + } + +} diff --git a/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java index fd4ef4f9e6e..6f030ada279 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java @@ -41,7 +41,7 @@ public TransportConfig getTransportConfig() throws InvalidConfigurationException @Override protected boolean isApplicable() { - return SystemUtils.IS_OS_LINUX; + return SystemUtils.IS_OS_LINUX || SystemUtils.IS_OS_MAC; } @Override diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index 5d89ab886e7..e9c0f68a82b 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -76,7 +76,7 @@ public static String start(String hostIpAddress, DockerClient client) { DockerClientFactory.instance().checkAndPullImage(client, ryukImage); List binds = new ArrayList<>(); - binds.add(new Bind("//var/run/docker.sock", new Volume("/var/run/docker.sock"))); + binds.add(new Bind("/" + DockerClientFactory.instance().getDockerUnixSocketPath(), new Volume("/var/run/docker.sock"))); String ryukContainerId = client.createContainerCmd(ryukImage) .withHostConfig(new HostConfig().withAutoRemove(true)) diff --git a/core/src/main/resources/META-INF/services/org.testcontainers.dockerclient.DockerClientProviderStrategy b/core/src/main/resources/META-INF/services/org.testcontainers.dockerclient.DockerClientProviderStrategy index 006c3ec7f82..d9cbad0ebff 100644 --- a/core/src/main/resources/META-INF/services/org.testcontainers.dockerclient.DockerClientProviderStrategy +++ b/core/src/main/resources/META-INF/services/org.testcontainers.dockerclient.DockerClientProviderStrategy @@ -2,3 +2,4 @@ org.testcontainers.dockerclient.EnvironmentAndSystemPropertyClientProviderStrate org.testcontainers.dockerclient.UnixSocketClientProviderStrategy org.testcontainers.dockerclient.DockerMachineClientProviderStrategy org.testcontainers.dockerclient.NpipeSocketClientProviderStrategy +org.testcontainers.dockerclient.RootlessDockerClientProviderStrategy diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeOverridesTest.java b/core/src/test/java/org/testcontainers/containers/DockerComposeOverridesTest.java similarity index 90% rename from core/src/test/java/org/testcontainers/junit/DockerComposeOverridesTest.java rename to core/src/test/java/org/testcontainers/containers/DockerComposeOverridesTest.java index 6413694d1c5..9e9eb27b509 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeOverridesTest.java +++ b/core/src/test/java/org/testcontainers/containers/DockerComposeOverridesTest.java @@ -1,6 +1,8 @@ -package org.testcontainers.junit; +package org.testcontainers.containers; import com.google.common.util.concurrent.Uninterruptibles; +import org.assertj.core.api.Assumptions; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -48,6 +50,15 @@ public static Iterable data() { }); } + @Before + public void setUp() { + if (localMode) { + Assumptions.assumeThat(LocalDockerCompose.executableExists()) + .as("docker-compose executable exists") + .isTrue(); + } + } + @Test public void test() { try (DockerComposeContainer compose = diff --git a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java index 78a7a3b1bef..134bdf18f30 100644 --- a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java +++ b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java @@ -2,13 +2,16 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.InspectContainerResponse.ContainerState; +import com.github.dockerjava.api.model.Info; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.experimental.FieldDefaults; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; +import org.assertj.core.api.Assumptions; import org.junit.Test; import org.rnorth.ducttape.unreliables.Unreliables; +import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.startupcheck.StartupCheckStrategy; import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy; @@ -21,6 +24,9 @@ public class GenericContainerTest { @Test public void shouldReportOOMAfterWait() { + Info info = DockerClientFactory.instance().client().infoCmd().exec(); + // Poor man's rootless Docker detection :D + Assumptions.assumeThat(info.getDriver()).doesNotContain("vfs"); try ( GenericContainer container = new GenericContainer<>() .withStartupCheckStrategy(new NoopStartupCheckStrategy()) diff --git a/core/src/test/java/org/testcontainers/dockerclient/DockerClientConfigUtilsTest.java b/core/src/test/java/org/testcontainers/dockerclient/DockerClientConfigUtilsTest.java index 353d6ea94e7..442c2efd950 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/DockerClientConfigUtilsTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/DockerClientConfigUtilsTest.java @@ -1,6 +1,7 @@ package org.testcontainers.dockerclient; import com.github.dockerjava.api.DockerClient; +import org.assertj.core.api.Assumptions; import org.junit.Test; import org.testcontainers.DockerClientFactory; @@ -16,6 +17,10 @@ public class DockerClientConfigUtilsTest { @Test public void getDockerHostIpAddressShouldReturnLocalhostWhenUnixSocket() { + Assumptions.assumeThat(DockerClientConfigUtils.IN_A_CONTAINER) + .as("in a container") + .isFalse(); + String actual = DockerClientProviderStrategy.resolveDockerHostIpAddress(client, URI.create("unix:///var/run/docker.sock")); assertEquals("localhost", actual); } diff --git a/core/src/test/resources/auth-config/docker-credential-fake b/core/src/test/resources/auth-config/docker-credential-fake index 880009f2c2a..753e62ca347 100755 --- a/core/src/test/resources/auth-config/docker-credential-fake +++ b/core/src/test/resources/auth-config/docker-credential-fake @@ -1,21 +1,21 @@ -#!/bin/bash +#!/bin/sh -if [[ $1 != "get" ]]; then +if [ $1 != "get" ]; then exit 1 fi read inputLine -if [[ $inputLine == "registry2.example.com" ]]; then +if [ "$inputLine" = "registry2.example.com" ]; then echo Fake credentials not found on credentials store \'$inputLine\' 1>&2 exit 1 fi -if [[ $inputLine == "https://not.a.real.registry/url" ]]; then +if [ "$inputLine" = "https://not.a.real.registry/url" ]; then echo Fake credentials not found on credentials store \'$inputLine\' 1>&2 exit 1 fi -if [[ $inputLine == "registry.example.com" ]]; then +if [ "$inputLine" = "registry.example.com" ]; then echo '{' \ ' "ServerURL": "url",' \ ' "Username": "username",' \ @@ -23,7 +23,7 @@ if [[ $inputLine == "registry.example.com" ]]; then '}' exit 0 fi -if [[ $inputLine == "registrytoken.example.com" ]]; then +if [ "$inputLine" = "registrytoken.example.com" ]; then echo '{' \ ' "ServerURL": "url",' \ ' "Username": "",' \ diff --git a/docs/features/configuration.md b/docs/features/configuration.md index f0172b3c978..8b12f8e8d54 100644 --- a/docs/features/configuration.md +++ b/docs/features/configuration.md @@ -72,3 +72,20 @@ but does not allow starting privileged containers, you can turn off the Ryuk con > **pull.pause.timeout = 30** > By default Testcontainers will abort the pull of an image if the pull appears stalled (no data transferred) for longer than this duration (in seconds). + +## Customizing Docker host detection + +Testcontainers will attempt to detect the Docker environment and configure everything. + +However, sometimes a customization is required. For that, you can provide the following environment variables: + +> **DOCKER_HOST** = unix:///var/run/docker.sock +> See [Docker environment variables](https://docs.docker.com/engine/reference/commandline/cli/#environment-variables) +> +> **TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE** +> Path to Docker's socket. Used by Ryuk, Docker Compose, and a few other containers that need to perform Docker actions. +> Example: `/var/run/docker-alt.sock` +> +> **TESTCONTAINERS_HOST_OVERRIDE** +> Docker's host on which ports are exposed. +> Example: `docker.svc.local` From cce8a7f7db57278eb81c878c4879a50479917a4d Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Wed, 15 Jul 2020 16:48:47 +0200 Subject: [PATCH 1510/5615] Added jOOQ to the who's using section, and me to the backers section (#2996) --- docs/index.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/index.md b/docs/index.md index 6c8bf49f08f..7619146c8b5 100644 --- a/docs/index.md +++ b/docs/index.md @@ -119,6 +119,7 @@ testImplementation('org.testcontainers:mysql') //no version specified * [Transferwise](https://transferwise.com/) - Integration testing for different RDBMS, kafka and micro services * [XWiki](https://xwiki.org) - [Testing XWiki](https://dev.xwiki.org/xwiki/bin/view/Community/Testing/DockerTesting/) under all [supported configurations](https://dev.xwiki.org/xwiki/bin/view/Community/SupportStrategy/) * [Apache SkyWalking](http://github.com/apache/skywalking) - End-to-end testing of the Apache SkyWalking, and plugin tests of its subproject, [Apache SkyWalking Python](http://github.com/apache/skywalking-python), and of its eco-system built by the community, like [SkyAPM NodeJS Agent](http://github.com/SkyAPM/nodejs) +* [jOOQ](https://www.jooq.org) - Integration testing all of jOOQ with a variety of RDBMS ## Sponsors @@ -133,6 +134,7 @@ A huge thank you to our backers: * [Philip Riecks (@rieckpil)](https://github.com/rieckpil) * [Karl Heinz Marbaise (@khmarbaise)](https://github.com/khmarbaise) * [Sascha Frinken (@sascha-frinken)](https://github.com/sascha-frinken) +* [Lukas Eder (@lukaseder)](https://github.com/lukaseder) ## License From f8954ff7c0cdd3e0ff3c13e484cab4f221c6f7ea Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 15 Jul 2020 16:21:18 +0100 Subject: [PATCH 1511/5615] Add fkorotkov to backers (#2997) --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index 7619146c8b5..94224c3c0db 100644 --- a/docs/index.md +++ b/docs/index.md @@ -135,6 +135,7 @@ A huge thank you to our backers: * [Karl Heinz Marbaise (@khmarbaise)](https://github.com/khmarbaise) * [Sascha Frinken (@sascha-frinken)](https://github.com/sascha-frinken) * [Lukas Eder (@lukaseder)](https://github.com/lukaseder) +* [Fedor Korotkov (@fedor)](https://github.com/fkorotkov) ## License From 977058da8e63a06f08e91aed2afd0c22df59641f Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 15 Jul 2020 16:25:21 +0100 Subject: [PATCH 1512/5615] Add dreis2211 to backers And use github username, not twitter handle, for Fedor --- docs/index.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 94224c3c0db..4894297f034 100644 --- a/docs/index.md +++ b/docs/index.md @@ -135,7 +135,8 @@ A huge thank you to our backers: * [Karl Heinz Marbaise (@khmarbaise)](https://github.com/khmarbaise) * [Sascha Frinken (@sascha-frinken)](https://github.com/sascha-frinken) * [Lukas Eder (@lukaseder)](https://github.com/lukaseder) -* [Fedor Korotkov (@fedor)](https://github.com/fkorotkov) +* [Fedor Korotkov (@fkorotkov)](https://github.com/fkorotkov) +* [Christoph Dreis (@dreis2211)](https://github.com/dreis2211) ## License From a5c28943992e491c688603982a517c59c7a02d8f Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 16 Jul 2020 11:34:49 +0200 Subject: [PATCH 1513/5615] Fix local Docker Compose (#2998) * Fix local Docker Compose * set `DOCKER_TLS_VERIFY` as well --- .../org/testcontainers/DockerClientFactory.java | 10 ++++++++-- .../containers/DockerComposeContainer.java | 17 +++++++++++++++-- .../testcontainers/utility/ResourceReaper.java | 2 +- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index b9c4d508f25..f91b075cdb2 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -21,6 +21,7 @@ import org.apache.commons.lang.StringUtils; import org.testcontainers.dockerclient.DockerClientProviderStrategy; import org.testcontainers.dockerclient.DockerMachineClientProviderStrategy; +import org.testcontainers.dockerclient.TransportConfig; import org.testcontainers.images.TimeLimitedLoggedPullImageResultCallback; import org.testcontainers.utility.ComparableVersion; import org.testcontainers.utility.MountableFile; @@ -132,13 +133,18 @@ private DockerClientProviderStrategy getOrInitializeStrategy() { } @UnstableAPI - public String getDockerUnixSocketPath() { + public TransportConfig getTransportConfig() { + return getOrInitializeStrategy().getTransportConfig(); + } + + @UnstableAPI + public String getRemoteDockerUnixSocketPath() { String dockerSocketOverride = System.getenv("TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE"); if (!StringUtils.isBlank(dockerSocketOverride)) { return dockerSocketOverride; } - URI dockerHost = getOrInitializeStrategy().getTransportConfig().getDockerHost(); + URI dockerHost = getTransportConfig().getDockerHost(); return "unix".equals(dockerHost.getScheme()) ? dockerHost.getRawPath() : "/var/run/docker.sock"; diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 8c07c036d11..50a58ba8c87 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -2,6 +2,8 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.model.Container; +import com.github.dockerjava.core.LocalDirectorySSLConfig; +import com.github.dockerjava.transport.SSLConfig; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.base.Splitter; @@ -22,6 +24,7 @@ import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.containers.wait.strategy.WaitAllStrategy; import org.testcontainers.containers.wait.strategy.WaitStrategy; +import org.testcontainers.dockerclient.TransportConfig; import org.testcontainers.lifecycle.Startable; import org.testcontainers.utility.AuditLogger; import org.testcontainers.utility.Base58; @@ -605,7 +608,7 @@ public ContainerisedDockerCompose(List composeFiles, String identifier) { // as the docker daemon, just mapping the docker control socket is OK. // As there seems to be a problem with mapping to the /var/run directory in certain environments (e.g. CircleCI) // we map the socket file outside of /var/run, as just /docker.sock - addFileSystemBind("/" + DockerClientFactory.instance().getDockerUnixSocketPath(), "/docker.sock", READ_WRITE); + addFileSystemBind("/" + DockerClientFactory.instance().getRemoteDockerUnixSocketPath(), "/docker.sock", READ_WRITE); addEnv("DOCKER_HOST", "unix:///docker.sock"); setStartupCheckStrategy(new IndefiniteWaitOneShotStartupCheckStrategy()); setWorkingDirectory(containerPwd); @@ -690,7 +693,17 @@ public void invoke() { String dockerHost = System.getenv("DOCKER_HOST"); if (dockerHost == null) { - dockerHost = "unix://" + DockerClientFactory.instance().getDockerUnixSocketPath(); + TransportConfig transportConfig = DockerClientFactory.instance().getTransportConfig(); + SSLConfig sslConfig = transportConfig.getSslConfig(); + if (sslConfig != null) { + if (sslConfig instanceof LocalDirectorySSLConfig) { + environment.put("DOCKER_CERT_PATH", ((LocalDirectorySSLConfig) sslConfig).getDockerCertPath()); + environment.put("DOCKER_TLS_VERIFY", "true"); + } else { + logger().warn("Couldn't set DOCKER_CERT_PATH. `sslConfig` is present but it's not LocalDirectorySSLConfig."); + } + } + dockerHost = transportConfig.getDockerHost().toString(); } environment.put("DOCKER_HOST", dockerHost); diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index e9c0f68a82b..0a0728e9306 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -76,7 +76,7 @@ public static String start(String hostIpAddress, DockerClient client) { DockerClientFactory.instance().checkAndPullImage(client, ryukImage); List binds = new ArrayList<>(); - binds.add(new Bind("/" + DockerClientFactory.instance().getDockerUnixSocketPath(), new Volume("/var/run/docker.sock"))); + binds.add(new Bind("/" + DockerClientFactory.instance().getRemoteDockerUnixSocketPath(), new Volume("/var/run/docker.sock"))); String ryukContainerId = client.createContainerCmd(ryukImage) .withHostConfig(new HostConfig().withAutoRemove(true)) From 6e8c18e1a3d9a75c86e86577b684bfdb8ce61160 Mon Sep 17 00:00:00 2001 From: Aaron Whiteside Date: Sun, 19 Jul 2020 20:44:41 +1000 Subject: [PATCH 1514/5615] couchbase: include error response from couchbase in checkSuccessfulResponse() (#3006) Also minor logging cleanup, and prevented an exception from being swallowed. fixes #2799 Co-authored-by: Aaron Whiteside --- .../couchbase/CouchbaseContainer.java | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index ee4b3212cac..4a95334752a 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -168,7 +168,7 @@ protected void configure() { .map("healthy"::equals) .orElse(false); } catch (IOException e) { - logger().error("Unable to parse response {}", response); + logger().error("Unable to parse response {}", response, e); return false; } }) @@ -337,10 +337,10 @@ private void configureIndexer() { * Based on the user-configured bucket definitions, creating buckets and corresponding indexes if needed. */ private void createBuckets() { - logger().debug("Creating " + buckets.size() + " buckets (and corresponding indexes)."); + logger().debug("Creating {} buckets (and corresponding indexes).", buckets.size()); for (BucketDefinition bucket : buckets) { - logger().debug("Creating bucket \"" + bucket.getName() + "\""); + logger().debug("Creating bucket \"{}\"", bucket.getName()); @Cleanup Response response = doHttpRequest(MGMT_PORT, "/pools/default/buckets", "POST", new FormBody.Builder() .add("name", bucket.getName()) @@ -382,7 +382,7 @@ private void createBuckets() { checkSuccessfulResponse(queryResponse, "Could not create primary index for bucket " + bucket.getName()); } else { - logger().info("Primary index creation for bucket " + bucket.getName() + " ignored, since QUERY service is not present."); + logger().info("Primary index creation for bucket {} ignored, since QUERY service is not present.", bucket.getName()); } } } @@ -406,7 +406,16 @@ private String getInternalIpAddress() { */ private void checkSuccessfulResponse(final Response response, final String message) { if (!response.isSuccessful()) { - throw new IllegalStateException(message + ": " + response.toString()); + String body = null; + if (response.body() != null) { + try { + body = response.body().string(); + } catch (IOException e) { + logger().debug("Unable to read body of response: {}", response, e); + } + } + + throw new IllegalStateException(message + ": " + response.toString() + ", body=" + (body == null ? "" : body)); } } From 3c391bf51db8c80156771e8366e30d5ccb44410c Mon Sep 17 00:00:00 2001 From: Piotr Findeisen Date: Sun, 19 Jul 2020 12:47:21 +0200 Subject: [PATCH 1515/5615] Maintain deterministic order in withCopyFileToContainer (#2897) --- .../java/org/testcontainers/containers/GenericContainer.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 417826e83b5..b3dcf469cfc 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -79,6 +79,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -180,7 +181,8 @@ public class GenericContainer> @Nullable private Long shmSize; - private Map copyToFileContainerPathMap = new HashMap<>(); + // Maintain order in which entries are added, as earlier target location may be a prefix of a later location. + private Map copyToFileContainerPathMap = new LinkedHashMap<>(); protected final Set dependencies = new HashSet<>(); From fd823a14f8d791def4b04b10e2f8f47b76c02edf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20B=C3=B6ckling?= Date: Sun, 19 Jul 2020 13:20:55 +0200 Subject: [PATCH 1516/5615] Make WaitStrategy's regex in PrestoContainer work with newer image (#2992) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michael Böckling --- .../java/org/testcontainers/containers/PrestoContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java index 56488525734..c3f25c40ac8 100644 --- a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java +++ b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java @@ -30,7 +30,7 @@ public PrestoContainer() { public PrestoContainer(final String dockerImageName) { super(dockerImageName); this.waitStrategy = new LogMessageWaitStrategy() - .withRegEx(".*io.prestosql.server.PrestoServer\\s+======== SERVER STARTED ========.*") + .withRegEx(".*======== SERVER STARTED ========.*") .withStartupTimeout(Duration.of(60, SECONDS)); addExposedPort(PRESTO_PORT); From c3f53b3a63e6b0bc800a7f0fbce91ce95a8986b3 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 19 Jul 2020 13:32:40 +0100 Subject: [PATCH 1517/5615] Deprecate ambiguous constructors (#2839) Our constructors for `GenericContainer` and all the classes that inherit from it tend to be a bit inconsistent. In some places, a no-args constructor is favoured, in other places a String parameter is favoured - which can be either a full image name or a tag. This raises confusion, and in the case of an image tag being the parameter, it restricts which images people can easily use. Another problem this raises is when the container class specifies a default tag version: this frequently falls out of date. We're stuck for options: some users (and image maintainers) would like to see newer image versions being used, but this could be a breaking change for existing users. This is especially risky when a major version bump is proposed. This change aims to: - Deprecate 'ambiguous' constructors for containers - any no-args or String-arg constructor gets this treatment. - Introduce a new constructor which accepts a `DockerImageName` object. This class is preferred to a String because its meaning is unambiguous. - Enhance `DockerImageName` with a static factory method and a convenience `withTag` method so that users can easily create and derive new instances. - Update all modules to follow suit --- .../testcontainers/DockerClientFactory.java | 2 +- .../containers/DockerComposeContainer.java | 2 +- .../FixedHostPortGenericContainer.java | 5 + .../containers/GenericContainer.java | 19 +++- .../containers/PortForwardingContainer.java | 4 +- .../containers/SocatContainer.java | 9 +- .../containers/VncRecordingContainer.java | 2 +- .../AuthDelegatingDockerClientConfig.java | 2 +- .../images/AbstractImagePullPolicy.java | 2 +- .../images/LocalImagesCache.java | 2 +- .../images/RemoteDockerImage.java | 16 ++- .../dockerfile/traits/FromStatementTrait.java | 2 +- .../utility/DockerImageName.java | 104 ++++++++++++------ .../utility/DockerMachineClient.java | 1 - .../utility/ResourceReaper.java | 4 +- .../utility/TestcontainersConfiguration.java | 45 ++++++++ .../OutOfPackageImagePullPolicyTest.java | 4 +- .../java/org/testcontainers/DaemonTest.java | 5 +- .../DockerClientFactoryTest.java | 2 +- .../java/org/testcontainers/TestImages.java | 13 +++ .../containers/ExposedHostTest.java | 28 +++-- .../FailureDetectingExternalResourceTest.java | 1 + .../containers/GenericContainerTest.java | 5 +- .../containers/NetworkTest.java | 24 ++-- .../containers/ReusabilityUnitTests.java | 31 +++--- .../containers/output/ContainerLogsTest.java | 19 ++-- .../dockerclient/EventStreamTest.java | 4 +- .../dockerclient/ImagePullTest.java | 5 +- .../images/AgeBasedPullPolicyTest.java | 2 +- .../images/ImagePullPolicyTest.java | 44 ++++---- .../images/RemoteDockerImageTest.java | 11 +- .../images/builder/DockerignoreTest.java | 30 ++--- .../junit/CopyFileToContainerTest.java | 9 +- .../junit/DependenciesTest.java | 10 +- .../junit/DockerNetworkModeTest.java | 7 +- .../junit/FileOperationsTest.java | 11 +- .../junit/FixedHostPortContainerTest.java | 9 +- .../junit/GenericContainerRuleTest.java | 71 ++++++++---- .../junit/NonExistentImagePullTest.java | 3 +- .../junit/OutputStreamTest.java | 7 +- .../junit/OutputStreamWithTTYTest.java | 7 +- .../junit/WorkingDirectoryTest.java | 3 +- .../strategy/AbstractWaitStrategyTest.java | 20 ++-- .../strategy/HostPortWaitStrategyTest.java | 5 +- .../utility/AuthenticatedImagePullTest.java | 9 +- .../utility/DockerImageNameTest.java | 46 ++++++-- .../utility/RegistryAuthLocatorTest.java | 20 ++-- .../test/java/generic/CmdModifierTest.java | 5 +- .../src/test/java/generic/CommandsTest.java | 3 +- .../java/generic/ContainerCreationTest.java | 21 ++-- .../test/java/generic/ContainerLabelTest.java | 5 +- .../src/test/java/generic/ExecTest.java | 3 +- .../generic/MultiplePortsExposedTest.java | 3 +- .../test/java/generic/WaitStrategiesTest.java | 7 +- .../StartupCheckStrategyTest.java | 7 +- .../quickstart/RedisBackedCacheIntTest.java | 3 +- .../quickstart/RedisBackedCacheIntTest.java | 3 +- docs/features/creating_container.md | 16 ++- .../test/java/DisqueBackedMailSenderTest.java | 13 ++- .../test/java/SingleDisqueInstanceTest.java | 8 +- examples/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 58910 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + examples/gradlew | 33 +++--- examples/gradlew.bat | 6 +- .../LinkedContainerTestImages.java | 8 ++ .../linkedcontainer/RedmineClientTest.java | 4 +- .../linkedcontainer/RedmineContainer.java | 3 +- .../containers/MongoDbContainer.java | 14 ++- .../containers/MongoDbContainerTest.java | 7 +- .../src/test/java/RedisBackedCacheTest.java | 7 +- .../src/test/java/RedisBackedCacheTest.java | 7 +- .../com/example/AbstractIntegrationTest.java | 3 +- .../test/java/com/example/SolrQueryTest.java | 21 ++-- .../com/example/AbstractIntegrationTest.java | 3 +- .../containers/CassandraContainer.java | 17 ++- .../containers/CassandraContainerTest.java | 29 +++-- .../containers/ClickHouseContainer.java | 14 +++ .../containers/ClickHouseProvider.java | 4 +- .../testcontainers/ClickhouseTestImages.java | 7 ++ .../clickhouse/SimpleClickhouseTest.java | 3 +- .../containers/CockroachContainer.java | 13 +++ .../CockroachContainerProvider.java | 4 +- .../testcontainers/CockroachDBTestImages.java | 7 ++ .../cockroachdb/SimpleCockroachDBTest.java | 7 +- .../couchbase/CouchbaseContainer.java | 19 +++- .../couchbase/CouchbaseContainerTest.java | 5 +- .../containers/Db2Container.java | 17 ++- .../containers/Db2ContainerProvider.java | 4 +- .../org/testcontainers/Db2TestImages.java | 7 ++ .../junit/db2/SimpleDb2Test.java | 5 +- .../dynamodb/DynaliteContainer.java | 18 ++- .../dynamodb/DynaliteContainerTest.java | 13 ++- .../elasticsearch/ElasticsearchContainer.java | 11 ++ .../ElasticsearchContainerTest.java | 47 +++++--- .../containers/InfluxDBContainer.java | 16 ++- .../containers/InfluxDBContainerTest.java | 2 +- .../InfluxDBContainerWithUserTest.java | 4 +- .../containers/InfluxDBTestImages.java | 7 ++ .../containers/JdbcDatabaseContainer.java | 11 +- .../jdbc/MissingJdbcDriverTest.java | 3 +- .../junit/jupiter/JUnitJupiterTestImages.java | 8 ++ .../junit/jupiter/PostgresContainerTests.java | 3 +- ...ntainersNestedRestartedContainerTests.java | 6 +- ...tcontainersNestedSharedContainerTests.java | 3 +- .../TestcontainersRestartBetweenTests.java | 3 +- .../TestcontainersSharedContainerTests.java | 3 +- .../jupiter/inheritance/RedisContainer.java | 7 +- .../containers/KafkaContainer.java | 15 ++- .../containers/KafkaContainerTest.java | 12 +- .../localstack/LocalStackContainer.java | 15 ++- .../localstack/LocalstackContainerTest.java | 15 ++- .../containers/MariaDBContainer.java | 14 +++ .../containers/MariaDBContainerProvider.java | 5 +- .../org/testcontainers/MariaDBTestImages.java | 7 ++ .../MariaDBR2DBCDatabaseContainerTest.java | 3 +- .../junit/mariadb/SimpleMariaDBTest.java | 11 +- .../containers/MockServerContainer.java | 15 ++- .../containers/MockServerContainerTest.java | 7 +- .../containers/MongoDBContainer.java | 13 +++ .../containers/MongoDBContainerTest.java | 4 +- .../containers/MSSQLServerContainer.java | 13 +++ .../MSSQLServerContainerProvider.java | 4 +- .../testcontainers/MSSQLServerTestImages.java | 7 ++ .../MSSQLR2DBCDatabaseContainerTest.java | 3 +- .../CustomPasswordMSSQLServerTest.java | 3 +- .../CustomizableMSSQLServerTest.java | 3 +- .../mssqlserver/SimpleMSSQLServerTest.java | 8 +- .../containers/MySQLContainer.java | 16 ++- .../containers/MySQLContainerProvider.java | 5 +- .../org/testcontainers/MySQLTestImages.java | 9 ++ .../MySQLR2DBCDatabaseContainerTest.java | 3 +- .../junit/mysql/CustomizableMysqlTest.java | 3 +- .../junit/mysql/MultiVersionMySQLTest.java | 7 +- .../junit/mysql/SimpleMySQLTest.java | 31 +++--- .../containers/Neo4jContainer.java | 33 ++++-- .../Neo4jContainerJUnitIntegrationTest.java | 11 +- .../containers/Neo4jContainerTest.java | 33 +++--- .../containers/Neo4jTestImages.java | 7 ++ .../containers/NginxContainer.java | 15 ++- .../testcontainers/junit/SimpleNginxTest.java | 5 +- .../containers/OracleContainer.java | 13 +++ .../containers/OrientDBContainer.java | 13 +++ .../containers/OrientDBContainerTest.java | 11 +- .../containers/PostgisContainerProvider.java | 5 +- .../containers/PostgreSQLContainer.java | 13 +++ .../PostgreSQLContainerProvider.java | 5 +- .../testcontainers/PostgreSQLTestImages.java | 7 ++ .../PostgreSQLConnectionURLTest.java | 7 ++ .../PostgreSQLR2DBCDatabaseContainerTest.java | 3 +- .../CustomizablePostgreSQLTest.java | 3 +- .../postgresql/SimplePostgreSQLTest.java | 11 +- .../containers/PrestoContainer.java | 13 +++ .../containers/PrestoContainerProvider.java | 5 +- .../org/testcontainers/PrestoTestImages.java | 8 ++ .../containers/PrestoContainerTest.java | 14 ++- .../containers/PulsarContainer.java | 20 +++- .../containers/PulsarContainerTest.java | 4 +- .../containers/RabbitMQContainer.java | 15 ++- ...RabbitMQContainerJUnitIntegrationTest.java | 2 +- .../containers/RabbitMQContainerTest.java | 51 ++++----- .../containers/RabbitMQTestImages.java | 7 ++ .../containers/BrowserWebDriverContainer.java | 34 ++++-- .../testcontainers/SeleniumTestImages.java | 7 ++ .../junit/LinkedContainerTest.java | 12 +- .../testcontainers/junit/Selenium3xTest.java | 4 +- ...ecificImageNameWebDriverContainerTest.java | 8 +- .../containers/SolrContainer.java | 25 +++-- .../containers/SolrContainerTest.java | 20 ++-- .../spock/MySqlContainerIT.groovy | 2 +- .../spock/PostgresContainerIT.groovy | 2 +- .../spock/SpockTestImages.groovy | 11 ++ .../spock/TestHierarchyIT.groovy | 2 +- .../TestLifecycleAwareContainerMock.java | 6 + ...TestcontainersRestartBetweenTestsIT.groovy | 2 +- .../TestcontainersSharedContainerIT.groovy | 2 +- .../containers/ToxiproxyContainer.java | 17 ++- .../containers/ToxiproxyTest.java | 15 ++- .../testcontainers/vault/VaultContainer.java | 13 +++ .../testcontainers/vault/VaultClientTest.java | 3 +- .../vault/VaultContainerTest.java | 2 +- .../testcontainers/vault/VaultTestImages.java | 7 ++ 181 files changed, 1445 insertions(+), 565 deletions(-) create mode 100644 core/src/test/java/org/testcontainers/TestImages.java create mode 100644 examples/gradle/wrapper/gradle-wrapper.jar create mode 100644 examples/gradle/wrapper/gradle-wrapper.properties create mode 100644 examples/linked-container/src/test/java/com/example/linkedcontainer/LinkedContainerTestImages.java create mode 100644 modules/clickhouse/src/test/java/org/testcontainers/ClickhouseTestImages.java create mode 100644 modules/cockroachdb/src/test/java/org/testcontainers/CockroachDBTestImages.java create mode 100644 modules/db2/src/test/java/org/testcontainers/Db2TestImages.java create mode 100644 modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBTestImages.java create mode 100644 modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/JUnitJupiterTestImages.java create mode 100644 modules/mariadb/src/test/java/org/testcontainers/MariaDBTestImages.java create mode 100644 modules/mssqlserver/src/test/java/org/testcontainers/MSSQLServerTestImages.java create mode 100644 modules/mysql/src/test/java/org/testcontainers/MySQLTestImages.java create mode 100644 modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jTestImages.java create mode 100644 modules/postgresql/src/test/java/org/testcontainers/PostgreSQLTestImages.java create mode 100644 modules/presto/src/test/java/org/testcontainers/PrestoTestImages.java create mode 100644 modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQTestImages.java create mode 100644 modules/selenium/src/test/java/org/testcontainers/SeleniumTestImages.java create mode 100644 modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy create mode 100644 modules/vault/src/test/java/org/testcontainers/vault/VaultTestImages.java diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index f91b075cdb2..1905fc7c098 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -60,7 +60,7 @@ public class DockerClientFactory { TESTCONTAINERS_SESSION_ID_LABEL, SESSION_ID ); - private static final String TINY_IMAGE = TestcontainersConfiguration.getInstance().getTinyImage(); + private static final String TINY_IMAGE = TestcontainersConfiguration.getInstance().getTinyDockerImageName().asCanonicalNameString(); private static DockerClientFactory instance; // Cached client configuration diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 50a58ba8c87..af1da29442f 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -586,7 +586,7 @@ class ContainerisedDockerCompose extends GenericContainer composeFiles, String identifier) { - super(TestcontainersConfiguration.getInstance().getDockerComposeContainerImage()); + super(TestcontainersConfiguration.getInstance().getDockerComposeDockerImageName()); addEnv(ENV_PROJECT_NAME, identifier); // Map the docker compose file into the container diff --git a/core/src/main/java/org/testcontainers/containers/FixedHostPortGenericContainer.java b/core/src/main/java/org/testcontainers/containers/FixedHostPortGenericContainer.java index 33c1c8e89bd..6477c28042e 100644 --- a/core/src/main/java/org/testcontainers/containers/FixedHostPortGenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/FixedHostPortGenericContainer.java @@ -12,6 +12,11 @@ * not available - which could manifest as flaky or unstable tests.

*/ public class FixedHostPortGenericContainer> extends GenericContainer { + + /** + * @deprecated it is highly recommended that {@link FixedHostPortGenericContainer} not be used, as it risks port conflicts. + */ + @Deprecated public FixedHostPortGenericContainer(@NotNull String dockerImageName) { super(dockerImageName); } diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index b3dcf469cfc..22a6c2408d6 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -12,7 +12,6 @@ import com.github.dockerjava.api.model.ContainerNetwork; import com.github.dockerjava.api.model.ExposedPort; import com.github.dockerjava.api.model.HostConfig; -import com.github.dockerjava.api.model.Info; import com.github.dockerjava.api.model.Link; import com.github.dockerjava.api.model.PortBinding; import com.github.dockerjava.api.model.Volume; @@ -55,6 +54,7 @@ import org.testcontainers.lifecycle.TestDescription; import org.testcontainers.lifecycle.TestLifecycleAware; import org.testcontainers.utility.Base58; +import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.DockerLoggerFactory; import org.testcontainers.utility.DockerMachineClient; import org.testcontainers.utility.MountableFile; @@ -227,10 +227,27 @@ public class GenericContainer> @Setter(AccessLevel.NONE) private boolean shouldBeReused = false; + + public GenericContainer(@NonNull final DockerImageName dockerImageName) { + this.image = new RemoteDockerImage(dockerImageName); + } + + public GenericContainer(@NonNull final RemoteDockerImage image) { + this.image = image; + } + + /** + * @deprecated use {@link GenericContainer(DockerImageName)} instead + */ + @Deprecated public GenericContainer() { this(TestcontainersConfiguration.getInstance().getTinyImage()); } + /** + * @deprecated use {@link GenericContainer(DockerImageName)} instead + */ + @Deprecated public GenericContainer(@NonNull final String dockerImageName) { this.setDockerImageName(dockerImageName); } diff --git a/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java b/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java index 09f1d98bd2d..e42f2681675 100644 --- a/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java +++ b/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java @@ -19,7 +19,7 @@ public enum PortForwardingContainer { INSTANCE; - private GenericContainer container; + private GenericContainer container; private final Set> exposedPorts = Collections.newSetFromMap(new ConcurrentHashMap<>()); @@ -29,7 +29,7 @@ public enum PortForwardingContainer { @SneakyThrows private Connection createSSHSession() { String password = UUID.randomUUID().toString(); - container = new GenericContainer<>(TestcontainersConfiguration.getInstance().getSSHdImage()) + container = new GenericContainer<>(TestcontainersConfiguration.getInstance().getSSHdDockerImageName()) .withExposedPorts(22) .withEnv("PASSWORD", password) .withCommand( diff --git a/core/src/main/java/org/testcontainers/containers/SocatContainer.java b/core/src/main/java/org/testcontainers/containers/SocatContainer.java index 5949a14490d..fbca73eb29d 100644 --- a/core/src/main/java/org/testcontainers/containers/SocatContainer.java +++ b/core/src/main/java/org/testcontainers/containers/SocatContainer.java @@ -1,6 +1,7 @@ package org.testcontainers.containers; import org.testcontainers.utility.Base58; +import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.TestcontainersConfiguration; import java.util.HashMap; @@ -16,7 +17,11 @@ public class SocatContainer extends GenericContainer { private final Map targets = new HashMap<>(); public SocatContainer() { - super(TestcontainersConfiguration.getInstance().getSocatContainerImage()); + this(TestcontainersConfiguration.getInstance().getSocatDockerImageName()); + } + + public SocatContainer(final DockerImageName dockerImageName) { + super(dockerImageName); withCreateContainerCmdModifier(it -> it.withEntrypoint("/bin/sh")); withCreateContainerCmdModifier(it -> it.withName("testcontainers-socat-" + Base58.randomString(8))); } @@ -39,4 +44,4 @@ protected void configure() { .collect(Collectors.joining(" & ")) ); } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java b/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java index fd48dd01f92..8ac060c2af6 100644 --- a/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java +++ b/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java @@ -52,7 +52,7 @@ public VncRecordingContainer(@NonNull GenericContainer targetContainer) { * Create a sidekick container and attach it to another container. The VNC output of that container will be recorded. */ public VncRecordingContainer(@NonNull Network network, @NonNull String targetNetworkAlias) throws IllegalStateException { - super(TestcontainersConfiguration.getInstance().getVncRecordedContainerImage()); + super(TestcontainersConfiguration.getInstance().getVncDockerImageName()); this.targetNetworkAlias = targetNetworkAlias; withNetwork(network); diff --git a/core/src/main/java/org/testcontainers/dockerclient/AuthDelegatingDockerClientConfig.java b/core/src/main/java/org/testcontainers/dockerclient/AuthDelegatingDockerClientConfig.java index c78a13c99ac..5acb7ab8835 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/AuthDelegatingDockerClientConfig.java +++ b/core/src/main/java/org/testcontainers/dockerclient/AuthDelegatingDockerClientConfig.java @@ -40,7 +40,7 @@ public AuthConfig effectiveAuthConfig(String imageName) { } // try and obtain more accurate auth config using our resolution - final DockerImageName parsed = new DockerImageName(imageName); + final DockerImageName parsed = DockerImageName.parse(imageName); final AuthConfig effectiveAuthConfig = RegistryAuthLocator.instance() .lookupAuthConfig(parsed, fallbackAuthConfig); diff --git a/core/src/main/java/org/testcontainers/images/AbstractImagePullPolicy.java b/core/src/main/java/org/testcontainers/images/AbstractImagePullPolicy.java index 11c621eb08b..f5d75ad9617 100644 --- a/core/src/main/java/org/testcontainers/images/AbstractImagePullPolicy.java +++ b/core/src/main/java/org/testcontainers/images/AbstractImagePullPolicy.java @@ -12,7 +12,7 @@ public abstract class AbstractImagePullPolicy implements ImagePullPolicy { @Override public boolean shouldPull(DockerImageName imageName) { - Logger logger = DockerLoggerFactory.getLogger(imageName.toString()); + Logger logger = DockerLoggerFactory.getLogger(imageName.asCanonicalNameString()); // Does our cache already know the image? ImageData cachedImageData = LOCAL_IMAGES_CACHE.get(imageName); diff --git a/core/src/main/java/org/testcontainers/images/LocalImagesCache.java b/core/src/main/java/org/testcontainers/images/LocalImagesCache.java index e6b330d4c90..ec371ef830e 100644 --- a/core/src/main/java/org/testcontainers/images/LocalImagesCache.java +++ b/core/src/main/java/org/testcontainers/images/LocalImagesCache.java @@ -39,7 +39,7 @@ public Optional refreshCache(DockerImageName imageName) { InspectImageResponse response = null; try { - response = dockerClient.inspectImageCmd(imageName.toString()).exec(); + response = dockerClient.inspectImageCmd(imageName.asCanonicalNameString()).exec(); } catch (NotFoundException e) { log.trace("Image {} not found", imageName, e); } diff --git a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java index d0e1313edf6..8d3a77c5680 100644 --- a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java +++ b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java @@ -38,12 +38,18 @@ public class RemoteDockerImage extends LazyFuture { @ToString.Exclude private DockerClient dockerClient = DockerClientFactory.lazyClient(); + public RemoteDockerImage(DockerImageName dockerImageName) { + this.imageNameFuture = CompletableFuture.completedFuture(dockerImageName); + } + + @Deprecated public RemoteDockerImage(String dockerImageName) { - this.imageNameFuture = CompletableFuture.completedFuture(new DockerImageName(dockerImageName)); + this.imageNameFuture = CompletableFuture.completedFuture(DockerImageName.parse(dockerImageName)); } + @Deprecated public RemoteDockerImage(@NonNull String repository, @NonNull String tag) { - this.imageNameFuture = CompletableFuture.completedFuture(new DockerImageName(repository, tag)); + this.imageNameFuture = CompletableFuture.completedFuture(DockerImageName.parse(repository).withTag(tag)); } public RemoteDockerImage(@NonNull Future imageFuture) { @@ -57,7 +63,7 @@ protected final String resolve() { Logger logger = DockerLoggerFactory.getLogger(imageName.toString()); try { if (!imagePullPolicy.shouldPull(imageName)) { - return imageName.toString(); + return imageName.asCanonicalNameString(); } // The image is not available locally - pull it @@ -76,7 +82,7 @@ protected final String resolve() { LocalImagesCache.INSTANCE.refreshCache(imageName); - return imageName.toString(); + return imageName.asCanonicalNameString(); } catch (InterruptedException | InternalServerErrorException e) { // these classes of exception often relate to timeout/connection errors so should be retried lastFailure = e; @@ -104,7 +110,7 @@ private String imageNameToString() { } try { - return getImageName().toString(); + return getImageName().asCanonicalNameString(); } catch (InterruptedException | ExecutionException e) { return e.getMessage(); } diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/FromStatementTrait.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/FromStatementTrait.java index 2a87cb08223..07077259df8 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/FromStatementTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/FromStatementTrait.java @@ -6,7 +6,7 @@ public interface FromStatementTrait & DockerfileBuilderTrait> { default SELF from(String dockerImageName) { - new DockerImageName(dockerImageName).assertValid(); + DockerImageName.parse(dockerImageName).assertValid(); return ((SELF) this).withStatement(new SingleArgumentStatement("FROM", dockerImageName)); } diff --git a/core/src/main/java/org/testcontainers/utility/DockerImageName.java b/core/src/main/java/org/testcontainers/utility/DockerImageName.java index d57f1044e80..b4a40be2f16 100644 --- a/core/src/main/java/org/testcontainers/utility/DockerImageName.java +++ b/core/src/main/java/org/testcontainers/utility/DockerImageName.java @@ -2,12 +2,16 @@ import com.google.common.net.HostAndPort; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; +import org.jetbrains.annotations.NotNull; import java.util.regex.Pattern; @EqualsAndHashCode(exclude = "rawName") +@AllArgsConstructor(access = AccessLevel.PRIVATE) public final class DockerImageName { /* Regex patterns used for validation */ @@ -19,22 +23,42 @@ public final class DockerImageName { private final String rawName; private final String registry; private final String repo; - private final Versioning versioning; + @NotNull private final Versioning versioning; - public DockerImageName(String name) { - this.rawName = name; - final int slashIndex = name.indexOf('/'); + /** + * Parses a docker image name from a provided string. + * + * @param fullImageName in standard Docker format, e.g. name:tag, + * some.registry/path/name:tag, + * some.registry/path/name@sha256:abcdef..., etc. + */ + public static DockerImageName parse(String fullImageName) { + return new DockerImageName(fullImageName); + } + + /** + * Parses a docker image name from a provided string. + * + * @param fullImageName in standard Docker format, e.g. name:tag, + * some.registry/path/name:tag, + * some.registry/path/name@sha256:abcdef..., etc. + * @deprecated use {@link DockerImageName#parse(String)} instead + */ + @Deprecated + public DockerImageName(String fullImageName) { + this.rawName = fullImageName; + final int slashIndex = fullImageName.indexOf('/'); String remoteName; if (slashIndex == -1 || - (!name.substring(0, slashIndex).contains(".") && - !name.substring(0, slashIndex).contains(":") && - !name.substring(0, slashIndex).equals("localhost"))) { + (!fullImageName.substring(0, slashIndex).contains(".") && + !fullImageName.substring(0, slashIndex).contains(":") && + !fullImageName.substring(0, slashIndex).equals("localhost"))) { registry = ""; - remoteName = name; + remoteName = fullImageName; } else { - registry = name.substring(0, slashIndex); - remoteName = name.substring(slashIndex + 1); + registry = fullImageName.substring(0, slashIndex); + remoteName = fullImageName.substring(slashIndex + 1); } if (remoteName.contains("@sha256:")) { @@ -49,28 +73,40 @@ public DockerImageName(String name) { } } - public DockerImageName(String name, String tag) { - this.rawName = name; - final int slashIndex = name.indexOf('/'); + /** + * Parses a docker image name from a provided string, and uses a separate provided version. + * + * @param nameWithoutTag in standard Docker format, e.g. name, + * some.registry/path/name, + * some.registry/path/name, etc. + * @param version a docker image version identifier, either as a tag or sha256 checksum, e.g. + * tag, + * sha256:abcdef.... + * @deprecated use {@link DockerImageName#parse(String)}.{@link DockerImageName#withTag(String)} instead + */ + @Deprecated + public DockerImageName(String nameWithoutTag, @NotNull String version) { + this.rawName = nameWithoutTag; + final int slashIndex = nameWithoutTag.indexOf('/'); String remoteName; if (slashIndex == -1 || - (!name.substring(0, slashIndex).contains(".") && - !name.substring(0, slashIndex).contains(":") && - !name.substring(0, slashIndex).equals("localhost"))) { + (!nameWithoutTag.substring(0, slashIndex).contains(".") && + !nameWithoutTag.substring(0, slashIndex).contains(":") && + !nameWithoutTag.substring(0, slashIndex).equals("localhost"))) { registry = ""; - remoteName = name; + remoteName = nameWithoutTag; } else { - registry = name.substring(0, slashIndex); - remoteName = name.substring(slashIndex + 1); + registry = nameWithoutTag.substring(0, slashIndex); + remoteName = nameWithoutTag.substring(slashIndex + 1); } - if (tag.startsWith("sha256:")) { + if (version.startsWith("sha256:")) { repo = remoteName; - versioning = new Sha256Versioning(tag.replace("sha256:", "")); + versioning = new Sha256Versioning(version.replace("sha256:", "")); } else { repo = remoteName; - versioning = new TagVersioning(tag); + versioning = new TagVersioning(version); } } @@ -92,13 +128,16 @@ public String getVersionPart() { return versioning.toString(); } + /** + * @return canonical name for the image + */ + public String asCanonicalNameString() { + return getUnversionedPart() + versioning.getSeparator() + versioning.toString(); + } + @Override public String toString() { - if (versioning == null) { - return getUnversionedPart(); - } else { - return getUnversionedPart() + versioning.getSeparator() + versioning.toString(); - } + return asCanonicalNameString(); } /** @@ -111,10 +150,6 @@ public void assertValid() { if (!REPO_NAME.matcher(repo).matches()) { throw new IllegalArgumentException(repo + " is not a valid Docker image name (in " + rawName + ")"); } - if (versioning == null) { - throw new IllegalArgumentException("No image tag was specified in docker image name " + - "(" + rawName + "). Please provide a tag; this may be 'latest' or a specific version"); - } if (!versioning.isValid()) { throw new IllegalArgumentException(versioning + " is not a valid image versioning identifier (in " + rawName + ")"); } @@ -124,8 +159,13 @@ public String getRegistry() { return registry; } + public DockerImageName withTag(final String newTag) { + return new DockerImageName(rawName, registry, repo, new TagVersioning(newTag)); + } + private interface Versioning { boolean isValid(); + String getSeparator(); } @@ -155,7 +195,7 @@ public String toString() { } @Data - private class Sha256Versioning implements Versioning { + private static class Sha256Versioning implements Versioning { public static final String HASH_REGEX = "[0-9a-fA-F]{32,}"; private final String hash; diff --git a/core/src/main/java/org/testcontainers/utility/DockerMachineClient.java b/core/src/main/java/org/testcontainers/utility/DockerMachineClient.java index 240a6d5afb4..fe12e9f541d 100644 --- a/core/src/main/java/org/testcontainers/utility/DockerMachineClient.java +++ b/core/src/main/java/org/testcontainers/utility/DockerMachineClient.java @@ -1,7 +1,6 @@ package org.testcontainers.utility; import lombok.NonNull; - import org.apache.commons.lang.SystemUtils; import org.slf4j.Logger; diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index 0a0728e9306..3448e73dce3 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -14,7 +14,6 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Throwables; import com.google.common.collect.Sets; - import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.rnorth.ducttape.ratelimits.RateLimiter; @@ -22,6 +21,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testcontainers.DockerClientFactory; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -72,7 +72,7 @@ private ResourceReaper() { @SneakyThrows(InterruptedException.class) public static String start(String hostIpAddress, DockerClient client) { - String ryukImage = TestcontainersConfiguration.getInstance().getRyukImage(); + String ryukImage = TestcontainersConfiguration.getInstance().getRyukDockerImageName().asCanonicalNameString(); DockerClientFactory.instance().checkAndPullImage(client, ryukImage); List binds = new ArrayList<>(); diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index aa829573447..1939e8e21b3 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -63,50 +63,95 @@ public String getAmbassadorContainerImage() { return (String) properties.getOrDefault("ambassador.container.image", "richnorth/ambassador:latest"); } + @Deprecated public String getSocatContainerImage() { return (String) properties.getOrDefault("socat.container.image", "alpine/socat:latest"); } + public DockerImageName getSocatDockerImageName() { + return DockerImageName.parse(getSocatContainerImage()); + } + + @Deprecated public String getVncRecordedContainerImage() { return (String) properties.getOrDefault("vncrecorder.container.image", "testcontainers/vnc-recorder:1.1.0"); } + public DockerImageName getVncDockerImageName() { + return DockerImageName.parse(getVncRecordedContainerImage()); + } + + @Deprecated public String getDockerComposeContainerImage() { return (String) properties.getOrDefault("compose.container.image", "docker/compose:1.24.1"); } + public DockerImageName getDockerComposeDockerImageName() { + return DockerImageName.parse(getDockerComposeContainerImage()); + } + + @Deprecated public String getTinyImage() { return (String) properties.getOrDefault("tinyimage.container.image", "alpine:3.5"); } + public DockerImageName getTinyDockerImageName() { + return DockerImageName.parse(getTinyImage()); + } + public boolean isRyukPrivileged() { return Boolean.parseBoolean((String) properties.getOrDefault("ryuk.container.privileged", "false")); } + @Deprecated public String getRyukImage() { return (String) properties.getOrDefault("ryuk.container.image", "testcontainers/ryuk:0.3.0"); } + public DockerImageName getRyukDockerImageName() { + return DockerImageName.parse(getRyukImage()); + } + + @Deprecated public String getSSHdImage() { return (String) properties.getOrDefault("sshd.container.image", "testcontainers/sshd:1.0.0"); } + public DockerImageName getSSHdDockerImageName() { + return DockerImageName.parse(getSSHdImage()); + } + public Integer getRyukTimeout() { return Integer.parseInt((String) properties.getOrDefault("ryuk.container.timeout", "30")); } + @Deprecated public String getKafkaImage() { return (String) properties.getOrDefault("kafka.container.image", "confluentinc/cp-kafka"); } + public DockerImageName getKafkaDockerImageName() { + return DockerImageName.parse(getKafkaImage()); + } + + @Deprecated public String getPulsarImage() { return (String) properties.getOrDefault("pulsar.container.image", "apachepulsar/pulsar"); } + public DockerImageName getPulsarDockerImageName() { + return DockerImageName.parse(getPulsarImage()); + } + + @Deprecated public String getLocalStackImage() { return (String) properties.getOrDefault("localstack.container.image", "localstack/localstack"); } + public DockerImageName getLocalstackDockerImageName() { + return DockerImageName.parse(getLocalStackImage()); + } + public boolean isDisableChecks() { return Boolean.parseBoolean((String) environmentProperties.getOrDefault("checks.disable", "false")); } diff --git a/core/src/test/java/alt/testcontainers/images/OutOfPackageImagePullPolicyTest.java b/core/src/test/java/alt/testcontainers/images/OutOfPackageImagePullPolicyTest.java index 2da41a8344e..e4197c7c1f6 100644 --- a/core/src/test/java/alt/testcontainers/images/OutOfPackageImagePullPolicyTest.java +++ b/core/src/test/java/alt/testcontainers/images/OutOfPackageImagePullPolicyTest.java @@ -7,11 +7,13 @@ import org.testcontainers.images.ImageData; import org.testcontainers.utility.DockerImageName; +import static org.testcontainers.TestImages.TINY_IMAGE; + public class OutOfPackageImagePullPolicyTest { @Test public void shouldSupportCustomPoliciesOutOfTestContainersPackage() { try ( - GenericContainer container = new GenericContainer<>() + GenericContainer container = new GenericContainer<>(TINY_IMAGE) .withImagePullPolicy(new AbstractImagePullPolicy() { @Override protected boolean shouldPullCached(DockerImageName imageName, ImageData localImageData) { diff --git a/core/src/test/java/org/testcontainers/DaemonTest.java b/core/src/test/java/org/testcontainers/DaemonTest.java index e339f30acc9..f08614c8cdc 100644 --- a/core/src/test/java/org/testcontainers/DaemonTest.java +++ b/core/src/test/java/org/testcontainers/DaemonTest.java @@ -10,6 +10,7 @@ import java.util.stream.Collectors; import static org.junit.Assert.assertEquals; +import static org.testcontainers.TestImages.TINY_IMAGE; /** * This test forks a new JVM, otherwise it's not possible to reliably diff the threads @@ -19,10 +20,10 @@ public class DaemonTest { public static void main(String[] args) { Thread mainThread = Thread.currentThread(); - GenericContainer genericContainer = null; + GenericContainer genericContainer = null; try { - genericContainer = new GenericContainer().withCommand("top"); + genericContainer = new GenericContainer<>(TINY_IMAGE).withCommand("top"); genericContainer.start(); Set threads = new HashSet<>(Thread.getAllStackTraces().keySet()); diff --git a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java index 04edd407959..030e4164f00 100644 --- a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java +++ b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java @@ -30,7 +30,7 @@ public void runCommandInsideDockerShouldNotFailIfImageDoesNotExistsLocally() { try { //remove tiny image, so it will be pulled during next command run dockFactory.client() - .removeImageCmd(TestcontainersConfiguration.getInstance().getTinyImage()) + .removeImageCmd(TestcontainersConfiguration.getInstance().getTinyDockerImageName().asCanonicalNameString()) .withForce(true).exec(); } catch (NotFoundException ignored) { // Do not fail if it's not pulled yet diff --git a/core/src/test/java/org/testcontainers/TestImages.java b/core/src/test/java/org/testcontainers/TestImages.java new file mode 100644 index 00000000000..3f16e88d882 --- /dev/null +++ b/core/src/test/java/org/testcontainers/TestImages.java @@ -0,0 +1,13 @@ +package org.testcontainers; + +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.TestcontainersConfiguration; + +public interface TestImages { + DockerImageName REDIS_IMAGE = DockerImageName.parse("redis:3.0.2"); + DockerImageName RABBITMQ_IMAGE = DockerImageName.parse("rabbitmq:3.5.3"); + DockerImageName MONGODB_IMAGE = DockerImageName.parse("mongo:3.1.5"); + DockerImageName ALPINE_IMAGE = DockerImageName.parse("alpine:3.2"); + DockerImageName DOCKER_REGISTRY_IMAGE = DockerImageName.parse("registry:2.7.0"); + DockerImageName TINY_IMAGE = TestcontainersConfiguration.getInstance().getTinyDockerImageName(); +} diff --git a/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java b/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java index 8c98b0730b0..d17b0709dfa 100644 --- a/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java +++ b/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java @@ -12,6 +12,7 @@ import java.net.InetSocketAddress; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.testcontainers.TestImages.TINY_IMAGE; public class ExposedHostTest { @@ -31,9 +32,9 @@ public static void setUpClass() throws Exception { server.start(); Testcontainers.exposeHostPorts(server.getAddress().getPort()); - + Testcontainers.exposeHostPorts(ImmutableMap.of(server.getAddress().getPort(), 80)); - Testcontainers.exposeHostPorts(ImmutableMap.of(server.getAddress().getPort(), 81)); + Testcontainers.exposeHostPorts(ImmutableMap.of(server.getAddress().getPort(), 81)); } @AfterClass @@ -43,24 +44,33 @@ public static void tearDownClass() throws Exception { @Test public void testExposedHost() throws Exception { - assertResponse(new GenericContainer().withCommand("top"), server.getAddress().getPort()); + assertResponse(new GenericContainer<>(TINY_IMAGE) + .withCommand("top"), + server.getAddress().getPort()); } @Test public void testExposedHostWithNetwork() throws Exception { try (Network network = Network.newNetwork()) { - assertResponse(new GenericContainer().withNetwork(network).withCommand("top"), server.getAddress().getPort()); + assertResponse(new GenericContainer<>(TINY_IMAGE) + .withNetwork(network) + .withCommand("top"), + server.getAddress().getPort()); } } - + @Test public void testExposedHostPortOnFixedInternalPorts() throws Exception { - assertResponse(new GenericContainer().withCommand("top"), 80); - assertResponse(new GenericContainer().withCommand("top"), 81); - } + assertResponse(new GenericContainer<>(TINY_IMAGE) + .withCommand("top"), + 80); + assertResponse(new GenericContainer<>(TINY_IMAGE) + .withCommand("top"), + 81); + } @SneakyThrows - protected void assertResponse(GenericContainer container, int port) { + protected void assertResponse(GenericContainer container, int port) { try { container.start(); diff --git a/core/src/test/java/org/testcontainers/containers/FailureDetectingExternalResourceTest.java b/core/src/test/java/org/testcontainers/containers/FailureDetectingExternalResourceTest.java index 15a9eba7d17..63993bc24ff 100644 --- a/core/src/test/java/org/testcontainers/containers/FailureDetectingExternalResourceTest.java +++ b/core/src/test/java/org/testcontainers/containers/FailureDetectingExternalResourceTest.java @@ -3,6 +3,7 @@ import org.junit.Test; import org.junit.runner.Description; import org.junit.runners.model.Statement; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; diff --git a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java index 134bdf18f30..5008ba5baca 100644 --- a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java +++ b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java @@ -11,6 +11,7 @@ import org.assertj.core.api.Assumptions; import org.junit.Test; import org.rnorth.ducttape.unreliables.Unreliables; +import org.testcontainers.TestImages; import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.startupcheck.StartupCheckStrategy; import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy; @@ -28,7 +29,7 @@ public void shouldReportOOMAfterWait() { // Poor man's rootless Docker detection :D Assumptions.assumeThat(info.getDriver()).doesNotContain("vfs"); try ( - GenericContainer container = new GenericContainer<>() + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withStartupCheckStrategy(new NoopStartupCheckStrategy()) .waitingFor(new WaitForExitedState(ContainerState::getOOMKilled)) .withCreateContainerCmdModifier(it -> { @@ -49,7 +50,7 @@ public void shouldReportOOMAfterWait() { @Test public void shouldReportErrorAfterWait() { try ( - GenericContainer container = new GenericContainer<>() + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withStartupCheckStrategy(new NoopStartupCheckStrategy()) .waitingFor(new WaitForExitedState(state -> state.getExitCode() > 0)) .withCommand("sh", "-c", "usleep 100; exit 123") diff --git a/core/src/test/java/org/testcontainers/containers/NetworkTest.java b/core/src/test/java/org/testcontainers/containers/NetworkTest.java index b717b8dc81a..a39f0552a5a 100644 --- a/core/src/test/java/org/testcontainers/containers/NetworkTest.java +++ b/core/src/test/java/org/testcontainers/containers/NetworkTest.java @@ -5,9 +5,11 @@ import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; import org.testcontainers.DockerClientFactory; -import org.testcontainers.containers.Network; +import org.testcontainers.TestImages; -import static org.rnorth.visibleassertions.VisibleAssertions.*; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; import static org.testcontainers.containers.Network.newNetwork; @RunWith(Enclosed.class) @@ -19,13 +21,13 @@ public static class WithRules { public Network network = newNetwork(); @Rule - public GenericContainer foo = new GenericContainer() + public GenericContainer foo = new GenericContainer<>(TestImages.TINY_IMAGE) .withNetwork(network) .withNetworkAliases("foo") .withCommand("/bin/sh", "-c", "while true ; do printf 'HTTP/1.1 200 OK\\n\\nyay' | nc -l -p 8080; done"); @Rule - public GenericContainer bar = new GenericContainer() + public GenericContainer bar = new GenericContainer<>(TestImages.TINY_IMAGE) .withNetwork(network) .withCommand("top"); @@ -44,12 +46,12 @@ public void testNetworkSupport() throws Exception { try ( Network network = Network.newNetwork(); - GenericContainer foo = new GenericContainer() + GenericContainer foo = new GenericContainer<>(TestImages.TINY_IMAGE) .withNetwork(network) .withNetworkAliases("foo") .withCommand("/bin/sh", "-c", "while true ; do printf 'HTTP/1.1 200 OK\\n\\nyay' | nc -l -p 8080; done"); - GenericContainer bar = new GenericContainer() + GenericContainer bar = new GenericContainer<>(TestImages.TINY_IMAGE) .withNetwork(network) .withCommand("top") ) { @@ -63,11 +65,11 @@ public void testNetworkSupport() throws Exception { } @Test - public void testBuilder() throws Exception { + public void testBuilder() { try ( Network network = Network.builder() .driver("macvlan") - .build(); + .build() ) { String id = network.getId(); assertEquals( @@ -79,11 +81,11 @@ public void testBuilder() throws Exception { } @Test - public void testModifiers() throws Exception { + public void testModifiers() { try ( Network network = Network.builder() .createNetworkCmdModifier(cmd -> cmd.withDriver("macvlan")) - .build(); + .build() ) { String id = network.getId(); assertEquals( @@ -95,7 +97,7 @@ public void testModifiers() throws Exception { } @Test - public void testReusability() throws Exception { + public void testReusability() { try (Network network = Network.newNetwork()) { String firstId = network.getId(); assertNotNull( diff --git a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java index b2d4d327ba2..c212d04c33a 100644 --- a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java +++ b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java @@ -3,7 +3,12 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.dockerjava.api.DockerClient; -import com.github.dockerjava.api.command.*; +import com.github.dockerjava.api.command.CreateContainerCmd; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.InspectContainerCmd; +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.command.ListContainersCmd; +import com.github.dockerjava.api.command.StartContainerCmd; import com.github.dockerjava.api.model.Container; import com.github.dockerjava.core.command.CreateContainerCmdImpl; import com.github.dockerjava.core.command.InspectContainerCmdImpl; @@ -35,7 +40,6 @@ import java.util.List; import java.util.Map; import java.util.UUID; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -45,14 +49,11 @@ import static org.assertj.core.api.Assumptions.assumeThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; +import static org.testcontainers.TestImages.TINY_IMAGE; @RunWith(Enclosed.class) public class ReusabilityUnitTests { - static final CompletableFuture IMAGE_FUTURE = CompletableFuture.completedFuture( - TestcontainersConfiguration.getInstance().getTinyImage() - ); - @RunWith(Parameterized.class) @RequiredArgsConstructor @FieldDefaults(makeFinal = true) @@ -61,8 +62,8 @@ public static class CanBeReusedTest { @Parameterized.Parameters(name = "{0}") public static Object[][] data() { return new Object[][] { - { "generic", new GenericContainer(IMAGE_FUTURE), true }, - { "anonymous generic", new GenericContainer(IMAGE_FUTURE) {}, true }, + { "generic", new GenericContainer<>(TINY_IMAGE), true }, + { "anonymous generic", new GenericContainer(TINY_IMAGE) {}, true }, { "custom", new CustomContainer(), true }, { "anonymous custom", new CustomContainer() {}, true }, { "custom with containerIsCreated", new CustomContainerWithContainerIsCreated(), false }, @@ -84,16 +85,16 @@ public void shouldBeReusable() { } } - static class CustomContainer extends GenericContainer { + static class CustomContainer extends GenericContainer { CustomContainer() { - super(IMAGE_FUTURE); + super(TINY_IMAGE); } } - static class CustomContainerWithContainerIsCreated extends GenericContainer { + static class CustomContainerWithContainerIsCreated extends GenericContainer { CustomContainerWithContainerIsCreated() { - super(IMAGE_FUTURE); + super(TINY_IMAGE); } @Override @@ -109,7 +110,7 @@ public static class HooksTest extends AbstractReusabilityTest { List script = new ArrayList<>(); - GenericContainer container = makeReusable(new GenericContainer(IMAGE_FUTURE) { + GenericContainer container = makeReusable(new GenericContainer(TINY_IMAGE) { @Override protected boolean canBeReused() { @@ -188,7 +189,7 @@ public void shouldNotCallHookIfNotReused() { @FieldDefaults(makeFinal = true) public static class HashTest extends AbstractReusabilityTest { - protected GenericContainer container = makeReusable(new GenericContainer(IMAGE_FUTURE) { + protected GenericContainer container = makeReusable(new GenericContainer(TINY_IMAGE) { @Override public void copyFileToContainer(MountableFile mountableFile, String containerPath) { // NOOP @@ -296,7 +297,7 @@ public void shouldHashCopiedFiles() { @RunWith(BlockJUnit4ClassRunner.class) @FieldDefaults(makeFinal = true) public static class CopyFilesHashTest { - GenericContainer container = new GenericContainer(IMAGE_FUTURE); + GenericContainer container = new GenericContainer<>(TINY_IMAGE); @Test public void empty() { diff --git a/core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java b/core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java index ad0e4c76ae2..05ef7373293 100644 --- a/core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java +++ b/core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java @@ -7,6 +7,7 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.testcontainers.TestImages.ALPINE_IMAGE; import static org.testcontainers.containers.output.OutputFrame.OutputType.STDERR; import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; @@ -15,7 +16,7 @@ public class ContainerLogsTest { @Test @Ignore("fails due to the timing of the shell's decision to flush") public void getLogsReturnsAllLogsToDate() { - try (GenericContainer container = shortLivedContainer()) { + try (GenericContainer container = shortLivedContainer()) { container.start(); final String logs = container.getLogs(); @@ -25,7 +26,7 @@ public void getLogsReturnsAllLogsToDate() { @Test public void getLogsContainsBothOutputTypes() { - try (GenericContainer container = shortLivedContainer()) { + try (GenericContainer container = shortLivedContainer()) { container.start(); // docsGetAllLogs { @@ -38,7 +39,7 @@ public void getLogsContainsBothOutputTypes() { @Test public void getLogsReturnsStdOutToDate() { - try (GenericContainer container = shortLivedContainer()) { + try (GenericContainer container = shortLivedContainer()) { container.start(); // docsGetStdOut { @@ -50,7 +51,7 @@ public void getLogsReturnsStdOutToDate() { @Test public void getLogsReturnsStdErrToDate() { - try (GenericContainer container = shortLivedContainer()) { + try (GenericContainer container = shortLivedContainer()) { container.start(); // docsGetStdErr { @@ -62,7 +63,7 @@ public void getLogsReturnsStdErrToDate() { @Test public void getLogsForLongRunningContainer() throws InterruptedException { - try (GenericContainer container = longRunningContainer()) { + try (GenericContainer container = longRunningContainer()) { container.start(); Thread.sleep(1000L); @@ -72,14 +73,14 @@ public void getLogsForLongRunningContainer() throws InterruptedException { } } - private static GenericContainer shortLivedContainer() { - return new GenericContainer("alpine:3.3") + private static GenericContainer shortLivedContainer() { + return new GenericContainer<>(ALPINE_IMAGE) .withCommand("/bin/sh", "-c", "echo -n 'stdout' && echo -n 'stderr' 1>&2") .withStartupCheckStrategy(new OneShotStartupCheckStrategy()); } - private static GenericContainer longRunningContainer() { - return new GenericContainer("alpine:3.3") + private static GenericContainer longRunningContainer() { + return new GenericContainer<>(ALPINE_IMAGE) .withCommand("ping -c 100 127.0.0.1"); } } diff --git a/core/src/test/java/org/testcontainers/dockerclient/EventStreamTest.java b/core/src/test/java/org/testcontainers/dockerclient/EventStreamTest.java index 4d7a4e21133..a43a001ca5b 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/EventStreamTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/EventStreamTest.java @@ -16,6 +16,8 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import static org.testcontainers.TestImages.TINY_IMAGE; + /** * Test that event streaming from the {@link DockerClient} works correctly */ @@ -32,7 +34,7 @@ public void test() throws IOException, InterruptedException { CountDownLatch latch = new CountDownLatch(1); try ( - GenericContainer container = new GenericContainer<>() + GenericContainer container = new GenericContainer<>(TINY_IMAGE) .withCommand("true") .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) ) { diff --git a/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java b/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java index 6e319838888..fd936d26c19 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java @@ -5,11 +5,12 @@ import org.junit.runners.Parameterized; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; +import org.testcontainers.utility.DockerImageName; @RunWith(Parameterized.class) public class ImagePullTest { - private String image; + private final String image; @Parameterized.Parameters(name = "{0}") public static String[] parameters() { @@ -34,7 +35,7 @@ public ImagePullTest(String image) { @Test public void test() { - try (final GenericContainer container = new GenericContainer<>(image) + try (final GenericContainer container = new GenericContainer<>(DockerImageName.parse(image)) .withCommand("/bin/sh", "-c", "sleep 0") .withStartupCheckStrategy(new OneShotStartupCheckStrategy())) { container.start(); diff --git a/core/src/test/java/org/testcontainers/images/AgeBasedPullPolicyTest.java b/core/src/test/java/org/testcontainers/images/AgeBasedPullPolicyTest.java index 20b52798d40..e073fa0522a 100644 --- a/core/src/test/java/org/testcontainers/images/AgeBasedPullPolicyTest.java +++ b/core/src/test/java/org/testcontainers/images/AgeBasedPullPolicyTest.java @@ -13,7 +13,7 @@ public class AgeBasedPullPolicyTest { - final DockerImageName imageName = new DockerImageName(UUID.randomUUID().toString()); + final DockerImageName imageName = DockerImageName.parse(UUID.randomUUID().toString()); @Test public void shouldPull() { diff --git a/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java b/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java index 69e68f7f225..9b22f5e6166 100644 --- a/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java +++ b/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java @@ -1,12 +1,9 @@ package org.testcontainers.images; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; - import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.async.ResultCallback; +import com.github.dockerjava.api.command.PullImageResultCallback; import com.github.dockerjava.api.exception.NotFoundException; -import com.github.dockerjava.core.command.PullImageResultCallback; -import com.github.dockerjava.core.command.PushImageResultCallback; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; @@ -23,20 +20,24 @@ import java.util.UUID; import java.util.concurrent.TimeUnit; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.testcontainers.TestImages.DOCKER_REGISTRY_IMAGE; + public class ImagePullPolicyTest { @ClassRule - public static GenericContainer registry = new GenericContainer<>("registry:2") + public static GenericContainer registry = new GenericContainer<>(DOCKER_REGISTRY_IMAGE) .withExposedPorts(5000); - private static String imageName; + private static DockerImageName imageName; @BeforeClass public static void beforeClass() throws Exception { String testRegistryAddress = registry.getHost() + ":" + registry.getFirstMappedPort(); String testImageName = testRegistryAddress + "/image-pull-policy-test"; String tag = UUID.randomUUID().toString(); - imageName = testImageName + ":" + tag; + imageName = DockerImageName.parse(testImageName).withTag(tag); DockerClient client = DockerClientFactory.instance().client(); String dummySourceImage = "hello-world:latest"; @@ -47,28 +48,22 @@ public static void beforeClass() throws Exception { // push the image to the registry client.tagImageCmd(dummyImageId, testImageName, tag).exec(); - client.pushImageCmd(imageName) - .exec(new PushImageResultCallback()) + client.pushImageCmd(imageName.asCanonicalNameString()) + .exec(new ResultCallback.Adapter<>()) .awaitCompletion(1, TimeUnit.MINUTES); } @AfterClass public static void afterClass() { - try { - DockerClientFactory.instance().client().removeImageCmd(imageName).withForce(true).exec(); - } catch (NotFoundException ignored) { - } + removeImage(); } @Before public void setUp() { // Clean up local cache - try { - DockerClientFactory.instance().client().removeImageCmd(imageName).withForce(true).exec(); - } catch (NotFoundException ignored) { - } + removeImage(); - LocalImagesCache.INSTANCE.cache.remove(new DockerImageName(imageName)); + LocalImagesCache.INSTANCE.cache.remove(imageName); } @Test @@ -90,7 +85,7 @@ public void shouldAlwaysPull() { container.start(); } - DockerClientFactory.instance().client().removeImageCmd(imageName).withForce(true).exec(); + removeImage(); try ( GenericContainer container = new GenericContainer<>(imageName) @@ -175,4 +170,13 @@ private void expectToFailWithNotFoundException(GenericContainer container) { } } + private static void removeImage() { + try { + DockerClientFactory.instance().client() + .removeImageCmd(imageName.asCanonicalNameString()) + .withForce(true) + .exec(); + } catch (NotFoundException ignored) { + } + } } diff --git a/core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java b/core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java index 6b6a82559bf..d2ea3e7188a 100644 --- a/core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java +++ b/core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java @@ -1,23 +1,24 @@ package org.testcontainers.images; -import static org.hamcrest.CoreMatchers.containsString; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; - import org.junit.Test; import org.testcontainers.utility.Base58; +import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.LazyFuture; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; + public class RemoteDockerImageTest { @Test public void toStringContainsOnlyImageName() { String imageName = Base58.randomString(8).toLowerCase(); - RemoteDockerImage remoteDockerImage = new RemoteDockerImage(imageName); + RemoteDockerImage remoteDockerImage = new RemoteDockerImage(DockerImageName.parse(imageName)); assertThat(remoteDockerImage.toString(), containsString("imageName=" + imageName)); } diff --git a/core/src/test/java/org/testcontainers/images/builder/DockerignoreTest.java b/core/src/test/java/org/testcontainers/images/builder/DockerignoreTest.java index faf5fb950b8..b99a70a7650 100644 --- a/core/src/test/java/org/testcontainers/images/builder/DockerignoreTest.java +++ b/core/src/test/java/org/testcontainers/images/builder/DockerignoreTest.java @@ -1,21 +1,21 @@ package org.testcontainers.images.builder; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.rnorth.visibleassertions.VisibleAssertions.fail; - -import java.nio.file.Path; -import java.nio.file.Paths; - +import com.github.dockerjava.api.exception.DockerClientException; import org.junit.Test; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; +import org.testcontainers.utility.DockerImageName; -import com.github.dockerjava.api.exception.DockerClientException; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.rnorth.visibleassertions.VisibleAssertions.fail; public class DockerignoreTest { - + private static final Path INVALID_DOCKERIGNORE_PATH = Paths.get("src/test/resources/dockerfile-build-invalid"); - + @Test public void testInvalidDockerignore() throws Exception { try { @@ -24,13 +24,13 @@ public void testInvalidDockerignore() throws Exception { .withDockerfile(INVALID_DOCKERIGNORE_PATH.resolve("Dockerfile")) .get(); fail("Should not be able to build an image with an invalid .dockerignore file"); - } + } catch (DockerClientException e) { if (!e.getMessage().contains("Invalid pattern")) throw e; } } - + @SuppressWarnings("resource") @Test public void testValidDockerignore() throws Exception { @@ -38,7 +38,7 @@ public void testValidDockerignore() throws Exception { .withFileFromPath(".", DockerfileBuildTest.RESOURCE_PATH) .withDockerfile(DockerfileBuildTest.RESOURCE_PATH.resolve("Dockerfile-currentdir")); try( - final GenericContainer container = new GenericContainer<>(img.get()) + final GenericContainer container = new GenericContainer(DockerImageName.parse(img.get())) .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) .withCommand("ls", "/") ) { @@ -46,11 +46,11 @@ public void testValidDockerignore() throws Exception { container.start(); final String logs = container.getLogs(); - assertTrue("Files in the container indicated the .dockerignore was not applied. Output was: " + logs, + assertTrue("Files in the container indicated the .dockerignore was not applied. Output was: " + logs, logs.contains("should_not_be_ignored.txt")); - assertTrue("Files in the container indicated the .dockerignore was not applied. Output was: " + logs, + assertTrue("Files in the container indicated the .dockerignore was not applied. Output was: " + logs, !logs.contains("should_be_ignored.txt")); } } - + } diff --git a/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java b/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java index ed6ee561505..b0ed90d9c9e 100644 --- a/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java @@ -11,6 +11,7 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.testcontainers.TestImages.TINY_IMAGE; public class CopyFileToContainerTest { private static String containerPath = "/tmp/mappable-resource/"; @@ -19,7 +20,7 @@ public class CopyFileToContainerTest { @Test public void checkFileCopied() throws IOException, InterruptedException { try ( - GenericContainer container = new GenericContainer() + GenericContainer container = new GenericContainer<>(TINY_IMAGE) .withCommand("sleep", "3000") .withCopyFileToContainer(MountableFile.forClasspathResource("/mappable-resource/"), containerPath) ) { @@ -32,7 +33,7 @@ public void checkFileCopied() throws IOException, InterruptedException { @Test public void shouldUseCopyForReadOnlyClasspathResources() throws Exception { try ( - GenericContainer container = new GenericContainer() + GenericContainer container = new GenericContainer<>(TINY_IMAGE) .withCommand("sleep", "3000") .withClasspathResourceMapping("/mappable-resource/", containerPath, BindMode.READ_ONLY) ) { @@ -45,7 +46,7 @@ public void shouldUseCopyForReadOnlyClasspathResources() throws Exception { @Test public void shouldUseCopyOnlyWithReadOnlyClasspathResources() { String resource = "/test_copy_to_container.txt"; - GenericContainer container = new GenericContainer<>() + GenericContainer container = new GenericContainer<>(TINY_IMAGE) .withClasspathResourceMapping(resource, "/readOnly", BindMode.READ_ONLY) .withClasspathResourceMapping(resource, "/readOnlyNoSelinux", BindMode.READ_ONLY) @@ -64,7 +65,7 @@ public void shouldUseCopyOnlyWithReadOnlyClasspathResources() { public void shouldCreateFoldersStructureWithCopy() throws Exception { String resource = "/test_copy_to_container.txt"; try ( - GenericContainer container = new GenericContainer<>() + GenericContainer container = new GenericContainer<>(TINY_IMAGE) .withCommand("sleep", "3000") .withClasspathResourceMapping(resource, "/a/b/c/file", BindMode.READ_ONLY) ) { diff --git a/core/src/test/java/org/testcontainers/junit/DependenciesTest.java b/core/src/test/java/org/testcontainers/junit/DependenciesTest.java index 4f2ee01bc6d..bd82c58a7f7 100644 --- a/core/src/test/java/org/testcontainers/junit/DependenciesTest.java +++ b/core/src/test/java/org/testcontainers/junit/DependenciesTest.java @@ -16,6 +16,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.testcontainers.TestImages.TINY_IMAGE; + public class DependenciesTest { @Test @@ -23,7 +25,7 @@ public void shouldWorkWithSimpleDependency() { InvocationCountingStartable startable = new InvocationCountingStartable(); try ( - GenericContainer container = new GenericContainer() + GenericContainer container = new GenericContainer<>(TINY_IMAGE) .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) .dependsOn(startable) ) { @@ -40,7 +42,7 @@ public void shouldWorkWithMutlipleDependencies() { InvocationCountingStartable startable2 = new InvocationCountingStartable(); try ( - GenericContainer container = new GenericContainer() + GenericContainer container = new GenericContainer<>(TINY_IMAGE) .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) .dependsOn(startable1, startable2) ) { @@ -56,7 +58,7 @@ public void shouldStartEveryTime() { InvocationCountingStartable startable = new InvocationCountingStartable(); try ( - GenericContainer container = new GenericContainer() + GenericContainer container = new GenericContainer<>(TINY_IMAGE) .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) .dependsOn(startable) ) { @@ -83,7 +85,7 @@ public void shouldStartTransitiveDependencies() { startable.getDependencies().add(transitiveStartable); try ( - GenericContainer container = new GenericContainer() + GenericContainer container = new GenericContainer<>(TINY_IMAGE) .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) .dependsOn(startable) ) { diff --git a/core/src/test/java/org/testcontainers/junit/DockerNetworkModeTest.java b/core/src/test/java/org/testcontainers/junit/DockerNetworkModeTest.java index 873160bdbc9..59c2a63127c 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerNetworkModeTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerNetworkModeTest.java @@ -2,12 +2,13 @@ import com.github.dockerjava.api.model.NetworkSettings; import lombok.extern.slf4j.Slf4j; -import org.junit.*; +import org.junit.Test; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.testcontainers.TestImages.TINY_IMAGE; /** * Simple tests of named network modes - more may be possible, but may not be reproducible @@ -19,7 +20,7 @@ public class DockerNetworkModeTest { @Test public void testNoNetworkContainer() { try ( - GenericContainer container = new GenericContainer() + GenericContainer container = new GenericContainer<>(TINY_IMAGE) .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) .withCommand("true") .withNetworkMode("none") @@ -35,7 +36,7 @@ public void testNoNetworkContainer() { @Test public void testHostNetworkContainer() { try ( - GenericContainer container = new GenericContainer() + GenericContainer container = new GenericContainer<>(TINY_IMAGE) .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) .withCommand("true") .withNetworkMode("host") diff --git a/core/src/test/java/org/testcontainers/junit/FileOperationsTest.java b/core/src/test/java/org/testcontainers/junit/FileOperationsTest.java index 95de9ef1b85..5e12fafc064 100644 --- a/core/src/test/java/org/testcontainers/junit/FileOperationsTest.java +++ b/core/src/test/java/org/testcontainers/junit/FileOperationsTest.java @@ -12,6 +12,7 @@ import java.io.IOException; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.testcontainers.TestImages.ALPINE_IMAGE; public class FileOperationsTest { @@ -21,7 +22,7 @@ public class FileOperationsTest { @Test public void copyFileToContainerFileTest() throws Exception { try ( - GenericContainer alpineCopyToContainer = new GenericContainer("alpine:3.2") + GenericContainer alpineCopyToContainer = new GenericContainer(ALPINE_IMAGE) .withCommand("top") ) { alpineCopyToContainer.start(); @@ -39,7 +40,7 @@ public void copyFileToContainerFileTest() throws Exception { @Test public void copyFileToContainerFolderTest() throws Exception { try ( - GenericContainer alpineCopyToContainer = new GenericContainer("alpine:3.2") + GenericContainer alpineCopyToContainer = new GenericContainer(ALPINE_IMAGE) .withCommand("top") ) { alpineCopyToContainer.start(); @@ -57,7 +58,7 @@ public void copyFileToContainerFolderTest() throws Exception { @Test public void copyFolderToContainerFolderTest() throws Exception { try ( - GenericContainer alpineCopyToContainer = new GenericContainer("alpine:3.2") + GenericContainer alpineCopyToContainer = new GenericContainer(ALPINE_IMAGE) .withCommand("top") ) { @@ -76,7 +77,7 @@ public void copyFolderToContainerFolderTest() throws Exception { @Test(expected = NotFoundException.class) public void copyFromContainerShouldFailBecauseNoFileTest() throws NotFoundException { try ( - GenericContainer alpineCopyToContainer = new GenericContainer("alpine:3.2") + GenericContainer alpineCopyToContainer = new GenericContainer(ALPINE_IMAGE) .withCommand("top") ) { alpineCopyToContainer.start(); @@ -87,7 +88,7 @@ public void copyFromContainerShouldFailBecauseNoFileTest() throws NotFoundExcept @Test public void shouldCopyFileFromContainerTest() throws IOException { try ( - GenericContainer alpineCopyToContainer = new GenericContainer("alpine:3.2") + GenericContainer alpineCopyToContainer = new GenericContainer(ALPINE_IMAGE) .withCommand("top") ) { diff --git a/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java b/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java index 5fe49756ba1..34c039b4fd3 100644 --- a/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java @@ -1,8 +1,5 @@ package org.testcontainers.junit; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.MatcherAssert.assertThat; - import com.google.common.util.concurrent.Uninterruptibles; import org.junit.Test; import org.rnorth.ducttape.unreliables.Unreliables; @@ -15,6 +12,10 @@ import java.net.Socket; import java.util.concurrent.TimeUnit; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.testcontainers.TestImages.TINY_IMAGE; + /** * Test of {@link FixedHostPortGenericContainer}. Note that this is not an example of typical use (usually, a container * should be a field on the test class annotated with @Rule or @TestRule). Instead, here, the lifecycle of the container @@ -46,7 +47,7 @@ public void testFixedHostPortMapping() throws IOException { // first find a free port on the docker host that will work for testing final Integer unusedHostPort; try ( - final GenericContainer echoServer = new GenericContainer<>(TEST_IMAGE) + final GenericContainer echoServer = new GenericContainer(TINY_IMAGE) .withExposedPorts(TEST_PORT) .withCommand("/bin/sh", "-c", HTTP_ECHO_CMD) ) { diff --git a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java index b450244a84d..c13421029e0 100644 --- a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java +++ b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java @@ -6,10 +6,19 @@ import com.mongodb.MongoClient; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; -import com.rabbitmq.client.*; +import com.rabbitmq.client.AMQP; +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; +import com.rabbitmq.client.DefaultConsumer; +import com.rabbitmq.client.Envelope; import org.apache.commons.io.FileUtils; import org.bson.Document; -import org.junit.*; +import org.junit.Assume; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Ignore; +import org.junit.Test; import org.rnorth.ducttape.RetryCountExceededException; import org.rnorth.ducttape.unreliables.Unreliables; import org.testcontainers.containers.Container; @@ -18,7 +27,13 @@ import org.testcontainers.utility.Base58; import org.testcontainers.utility.TestEnvironment; -import java.io.*; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintStream; import java.net.Socket; import java.time.Duration; import java.util.Arrays; @@ -30,7 +45,16 @@ import static java.util.Collections.singletonMap; import static org.hamcrest.CoreMatchers.equalTo; -import static org.rnorth.visibleassertions.VisibleAssertions.*; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; +import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; +import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.testcontainers.TestImages.ALPINE_IMAGE; +import static org.testcontainers.TestImages.MONGODB_IMAGE; +import static org.testcontainers.TestImages.RABBITMQ_IMAGE; +import static org.testcontainers.TestImages.REDIS_IMAGE; +import static org.testcontainers.TestImages.TINY_IMAGE; import static org.testcontainers.containers.BindMode.READ_ONLY; import static org.testcontainers.containers.BindMode.READ_WRITE; import static org.testcontainers.containers.SelinuxContext.SHARED; @@ -47,6 +71,7 @@ public class GenericContainerRuleTest { private static final int RABBITMQ_PORT = 5672; private static final int MONGO_PORT = 27017; + /* * Test data setup */ @@ -61,29 +86,27 @@ public static void setupContent() throws FileNotFoundException { * Redis */ @ClassRule - public static GenericContainer redis = new GenericContainer("redis:3.0.2") + public static GenericContainer redis = new GenericContainer<>(REDIS_IMAGE) .withExposedPorts(REDIS_PORT); /** * RabbitMQ */ @ClassRule - public static GenericContainer rabbitMq = new GenericContainer("rabbitmq:3.5.3") + public static GenericContainer rabbitMq = new GenericContainer<>(RABBITMQ_IMAGE) .withExposedPorts(RABBITMQ_PORT); - /** * MongoDB */ @ClassRule - public static GenericContainer mongo = new GenericContainer("mongo:3.1.5") + public static GenericContainer mongo = new GenericContainer<>(MONGODB_IMAGE) .withExposedPorts(MONGO_PORT); - /** * Pass an environment variable to the container, then run a shell script that exposes the variable in a quick and * dirty way for testing. */ @ClassRule - public static GenericContainer alpineEnvVar = new GenericContainer<>("alpine:3.2") + public static GenericContainer alpineEnvVar = new GenericContainer<>(ALPINE_IMAGE) .withExposedPorts(80) .withEnv("MAGIC_NUMBER", "4") .withEnv("MAGIC_NUMBER", oldValue -> oldValue.orElse("") + "2") @@ -94,7 +117,7 @@ public static void setupContent() throws FileNotFoundException { * dirty way for testing. */ @ClassRule - public static GenericContainer alpineEnvVarFromMap = new GenericContainer("alpine:3.2") + public static GenericContainer alpineEnvVarFromMap = new GenericContainer<>(ALPINE_IMAGE) .withExposedPorts(80) .withEnv(ImmutableMap.of( "FIRST", "42", @@ -106,7 +129,7 @@ public static void setupContent() throws FileNotFoundException { * Map a file on the classpath to a file in the container, and then expose the content for testing. */ @ClassRule - public static GenericContainer alpineClasspathResource = new GenericContainer("alpine:3.2") + public static GenericContainer alpineClasspathResource = new GenericContainer<>(ALPINE_IMAGE) .withExposedPorts(80) .withClasspathResourceMapping("mappable-resource/test-resource.txt", "/content.txt", READ_ONLY) .withCommand("/bin/sh", "-c", "while true; do cat /content.txt | nc -l -p 80; done"); @@ -115,7 +138,7 @@ public static void setupContent() throws FileNotFoundException { * Map a file on the classpath to a file in the container, and then expose the content for testing. */ @ClassRule - public static GenericContainer alpineClasspathResourceSelinux = new GenericContainer("alpine:3.2") + public static GenericContainer alpineClasspathResourceSelinux = new GenericContainer<>(ALPINE_IMAGE) .withExposedPorts(80) .withClasspathResourceMapping("mappable-resource/test-resource.txt", "/content.txt", READ_WRITE, SHARED) .withCommand("/bin/sh", "-c", "while true; do cat /content.txt | nc -l -p 80; done"); @@ -124,14 +147,16 @@ public static void setupContent() throws FileNotFoundException { * Create a container with an extra host entry and expose the content of /etc/hosts for testing. */ @ClassRule - public static GenericContainer alpineExtrahost = new GenericContainer("alpine:3.2") + public static GenericContainer alpineExtrahost = new GenericContainer<>(ALPINE_IMAGE) .withExposedPorts(80) .withExtraHost("somehost", "192.168.1.10") .withCommand("/bin/sh", "-c", "while true; do cat /etc/hosts | nc -l -p 80; done"); @Test public void testIsRunning() { - try (GenericContainer container = new GenericContainer().withCommand("top")) { + try ( + GenericContainer container = new GenericContainer<>(TINY_IMAGE) + .withCommand("top")) { assertFalse("Container is not started and not running", container.isRunning()); container.start(); assertTrue("Container is started and running", container.isRunning()); @@ -141,7 +166,7 @@ public void testIsRunning() { @Test public void withTmpFsTest() throws Exception { try ( - GenericContainer container = new GenericContainer() + GenericContainer container = new GenericContainer<>(TINY_IMAGE) .withCommand("top") .withTmpFs(singletonMap("/testtmpfs", "rw")) ) { @@ -222,7 +247,7 @@ public void environmentFromMapTest() throws IOException { @Test public void customLabelTest() { - try (final GenericContainer alpineCustomLabel = new GenericContainer("alpine:3.2") + try (final GenericContainer alpineCustomLabel = new GenericContainer<>(ALPINE_IMAGE) .withLabel("our.custom", "label") .withCommand("top")) { @@ -240,7 +265,7 @@ public void exceptionThrownWhenTryingToOverrideTestcontainersLabels() { assertThrows("When trying to overwrite an 'org.testcontainers' label, withLabel() throws an exception", IllegalArgumentException.class, () -> { - new GenericContainer("alpine:3.2") + new GenericContainer<>(ALPINE_IMAGE) .withLabel("org.testcontainers.foo", "false"); } ); @@ -262,7 +287,7 @@ public void customClasspathResourceMappingWithSelinuxTest() throws IOException { } @Test - public void exceptionThrownWhenMappedPortNotFound() throws IOException { + public void exceptionThrownWhenMappedPortNotFound() { assertThrows("When the requested port is not mapped, getMappedPort() throws an exception", IllegalArgumentException.class, () -> { @@ -279,10 +304,10 @@ protected static void writeStringToFile(File contentFolder, String filename, Str } @Test @Ignore //TODO investigate intermittent failures - public void failFastWhenContainerHaltsImmediately() throws Exception { + public void failFastWhenContainerHaltsImmediately() { long startingTimeMs = System.currentTimeMillis(); - final GenericContainer failsImmediately = new GenericContainer("alpine:3.2") + final GenericContainer failsImmediately = new GenericContainer<>(ALPINE_IMAGE) .withCommand("/bin/sh", "-c", "return false") .withMinimumRunningDuration(Duration.ofMillis(100)); @@ -346,7 +371,7 @@ public void createContainerCmdHookTest() { // Use random name to avoid the conflicts between the tests String randomName = Base58.randomString(5); try( - GenericContainer container = new GenericContainer<>("redis:3.0.2") + GenericContainer container = new GenericContainer<>(REDIS_IMAGE) .withCommand("redis-server", "--help") .withCreateContainerCmdModifier(cmd -> cmd.withName("overrideMe")) // Preserves the order @@ -390,7 +415,7 @@ public void addingExposedPortTwiceShouldNotFail() { @Test public void sharedMemorySetTest() { - try (GenericContainer containerWithSharedMemory = new GenericContainer() + try (GenericContainer containerWithSharedMemory = new GenericContainer<>(TINY_IMAGE) .withSharedMemorySize(42L * FileUtils.ONE_MB) .withStartupCheckStrategy(new OneShotStartupCheckStrategy())) { diff --git a/core/src/test/java/org/testcontainers/junit/NonExistentImagePullTest.java b/core/src/test/java/org/testcontainers/junit/NonExistentImagePullTest.java index d842829b4b6..95047779551 100644 --- a/core/src/test/java/org/testcontainers/junit/NonExistentImagePullTest.java +++ b/core/src/test/java/org/testcontainers/junit/NonExistentImagePullTest.java @@ -3,6 +3,7 @@ import org.junit.Test; import org.testcontainers.containers.ContainerFetchException; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.utility.DockerImageName; import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; @@ -16,7 +17,7 @@ public void pullingNonExistentImageFailsGracefully() { assertThrows("Pulling a nonexistent container will cause an exception to be thrown", ContainerFetchException.class, () -> { - new GenericContainer("richnorth/nonexistent:latest").getDockerImageName(); + new GenericContainer<>(DockerImageName.parse("testcontainers/nonexistent:latest")).getDockerImageName(); }); } } diff --git a/core/src/test/java/org/testcontainers/junit/OutputStreamTest.java b/core/src/test/java/org/testcontainers/junit/OutputStreamTest.java index 83ba05be86e..649a0b56fc4 100644 --- a/core/src/test/java/org/testcontainers/junit/OutputStreamTest.java +++ b/core/src/test/java/org/testcontainers/junit/OutputStreamTest.java @@ -14,7 +14,10 @@ import java.util.concurrent.TimeoutException; import java.util.function.Consumer; -import static org.rnorth.visibleassertions.VisibleAssertions.*; +import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; +import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.testcontainers.TestImages.ALPINE_IMAGE; import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; /** @@ -23,7 +26,7 @@ public class OutputStreamTest { @Rule - public GenericContainer container = new GenericContainer("alpine:3.2") + public GenericContainer container = new GenericContainer(ALPINE_IMAGE) .withCommand("ping -c 5 127.0.0.1"); private static final Logger LOGGER = LoggerFactory.getLogger(OutputStreamTest.class); diff --git a/core/src/test/java/org/testcontainers/junit/OutputStreamWithTTYTest.java b/core/src/test/java/org/testcontainers/junit/OutputStreamWithTTYTest.java index 0e7b66534b7..b15f63f4158 100644 --- a/core/src/test/java/org/testcontainers/junit/OutputStreamWithTTYTest.java +++ b/core/src/test/java/org/testcontainers/junit/OutputStreamWithTTYTest.java @@ -15,14 +15,17 @@ import java.util.concurrent.TimeoutException; import java.util.function.Consumer; -import static org.rnorth.visibleassertions.VisibleAssertions.*; +import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; +import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.testcontainers.TestImages.ALPINE_IMAGE; import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; @Slf4j public class OutputStreamWithTTYTest { @Rule - public GenericContainer container = new GenericContainer<>("alpine:3.2") + public GenericContainer container = new GenericContainer<>(ALPINE_IMAGE) .withCommand("ls -1") .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) .withCreateContainerCmdModifier(command -> command.withTty(true)); diff --git a/core/src/test/java/org/testcontainers/junit/WorkingDirectoryTest.java b/core/src/test/java/org/testcontainers/junit/WorkingDirectoryTest.java index 30230cc12ff..b23ddf2ca1c 100644 --- a/core/src/test/java/org/testcontainers/junit/WorkingDirectoryTest.java +++ b/core/src/test/java/org/testcontainers/junit/WorkingDirectoryTest.java @@ -6,6 +6,7 @@ import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.testcontainers.TestImages.ALPINE_IMAGE; /** * Created by rnorth on 26/07/2016. @@ -13,7 +14,7 @@ public class WorkingDirectoryTest { @ClassRule - public static GenericContainer container = new GenericContainer("alpine:3.2") + public static GenericContainer container = new GenericContainer(ALPINE_IMAGE) .withWorkingDirectory("/etc") .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) .withCommand("ls", "-al"); diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java index 3d3c2f0606d..480536a54ef 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java @@ -4,6 +4,7 @@ import org.junit.Before; import org.rnorth.ducttape.RetryCountExceededException; import org.rnorth.visibleassertions.VisibleAssertions; +import org.testcontainers.TestImages; import org.testcontainers.containers.ContainerLaunchException; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.WaitStrategy; @@ -20,7 +21,6 @@ */ public abstract class AbstractWaitStrategyTest { static final long WAIT_TIMEOUT_MILLIS = 3000; - static final String IMAGE_NAME = "alpine:3.7"; /** * Indicates that the WaitStrategy has completed waiting successfully. @@ -38,36 +38,36 @@ public abstract class AbstractWaitStrategyTest { protected abstract W buildWaitStrategy(final AtomicBoolean ready); @Before - public void setUp() throws Exception { + public void setUp() { ready = new AtomicBoolean(false); } /** - * Starts a GenericContainer with the {@link #IMAGE_NAME} image, passing the given {@code shellCommand} as + * Starts a GenericContainer with the Alpine image, passing the given {@code shellCommand} as * a parameter to {@literal sh -c} (the container CMD). * * @param shellCommand the shell command to execute * @return the (unstarted) container */ - private GenericContainer startContainerWithCommand(String shellCommand) { + private GenericContainer startContainerWithCommand(String shellCommand) { return startContainerWithCommand(shellCommand, buildWaitStrategy(ready)); } /** - * Starts a GenericContainer with the {@link #IMAGE_NAME} image, passing the given {@code shellCommand} as + * Starts a GenericContainer with the Alpine image, passing the given {@code shellCommand} as * a parameter to {@literal sh -c} (the container CMD) and apply a give wait strategy. * Note that the timeout will be overwritten if any with {@link #WAIT_TIMEOUT_MILLIS}. * @param shellCommand the shell command to execute * @param waitStrategy The wait strategy to apply * @return the (unstarted) container */ - protected GenericContainer startContainerWithCommand(String shellCommand, WaitStrategy waitStrategy) { + protected GenericContainer startContainerWithCommand(String shellCommand, WaitStrategy waitStrategy) { return startContainerWithCommand(shellCommand, waitStrategy, 8080); } - protected GenericContainer startContainerWithCommand(String shellCommand, WaitStrategy waitStrategy, Integer... ports) { + protected GenericContainer startContainerWithCommand(String shellCommand, WaitStrategy waitStrategy, Integer... ports) { // apply WaitStrategy to container - return new GenericContainer(IMAGE_NAME) + return new GenericContainer<>(TestImages.ALPINE_IMAGE) .withExposedPorts(ports) .withCommand("sh", "-c", shellCommand) .waitingFor(waitStrategy.withStartupTimeout(Duration.ofMillis(WAIT_TIMEOUT_MILLIS))); @@ -98,7 +98,7 @@ protected void waitUntilReadyAndTimeout(String shellCommand) { * * @param container the container to start */ - protected void waitUntilReadyAndTimeout(GenericContainer container) { + protected void waitUntilReadyAndTimeout(GenericContainer container) { // start() blocks until successful or timeout VisibleAssertions.assertThrows("an exception is thrown when timeout occurs (" + WAIT_TIMEOUT_MILLIS + "ms)", ContainerLaunchException.class, @@ -111,7 +111,7 @@ protected void waitUntilReadyAndTimeout(GenericContainer container) { * * @param container the container to start */ - protected void waitUntilReadyAndSucceed(GenericContainer container) { + protected void waitUntilReadyAndSucceed(GenericContainer container) { // start() blocks until successful or timeout container.start(); diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/HostPortWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/HostPortWaitStrategyTest.java index d715b25aaf6..5c7d37a7da3 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/HostPortWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/HostPortWaitStrategyTest.java @@ -2,6 +2,7 @@ import org.junit.ClassRule; import org.junit.Test; +import org.testcontainers.TestImages; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; @@ -15,10 +16,8 @@ */ public class HostPortWaitStrategyTest { - private static final String IMAGE_NAME = "alpine:3.7"; - @ClassRule - public static GenericContainer container = new GenericContainer(IMAGE_NAME).withExposedPorts() + public static GenericContainer container = new GenericContainer<>(TestImages.ALPINE_IMAGE).withExposedPorts() .withCommand("sh", "-c", "while true; do nc -lp 8080; done") .withExposedPorts(8080) .waitingFor(Wait.forListeningPort().withStartupTimeout(Duration.ofSeconds(10))); diff --git a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java index 3986255f248..0afb4d956a1 100644 --- a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java +++ b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java @@ -28,6 +28,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.testcontainers.TestImages.DOCKER_REGISTRY_IMAGE; /** * This test checks the integration between Testcontainers and an authenticated registry, but uses @@ -45,7 +46,7 @@ public class AuthenticatedImagePullTest { @ClassRule public static GenericContainer authenticatedRegistry = new GenericContainer<>(new ImageFromDockerfile() .withDockerfileFromBuilder(builder -> { - builder.from("registry:2.7.0") + builder.from(DOCKER_REGISTRY_IMAGE.asCanonicalNameString()) .run("htpasswd -Bbn testuser notasecret > /htpasswd") .env("REGISTRY_AUTH", "htpasswd") .env("REGISTRY_AUTH_HTPASSWD_PATH", "/htpasswd") @@ -69,7 +70,7 @@ public static void setUp() throws InterruptedException { testImageName = testRegistryAddress + "/alpine"; testImageNameWithTag = testImageName + ":latest"; - final DockerImageName expectedName = new DockerImageName(testImageNameWithTag); + final DockerImageName expectedName = DockerImageName.parse(testImageNameWithTag); final AuthConfig authConfig = new AuthConfig() .withUsername("testuser") .withPassword("notasecret") @@ -99,7 +100,7 @@ public static void tearDown() { @Test public void testThatAuthLocatorIsUsedForContainerCreation() { // actually start a container, which will require an authenticated pull - try (final GenericContainer container = new GenericContainer<>(testImageNameWithTag) + try (final GenericContainer container = new GenericContainer<>(DockerImageName.parse(testImageNameWithTag)) .withCommand("/bin/sh", "-c", "sleep 10")) { container.start(); @@ -165,7 +166,7 @@ private Path getLocalTempFile(String s) throws IOException { private static void putImageInRegistry() throws InterruptedException { // It doesn't matter which image we use for this test, but use one that's likely to have been pulled already - final String dummySourceImage = TestcontainersConfiguration.getInstance().getRyukImage(); + final String dummySourceImage = TestcontainersConfiguration.getInstance().getRyukDockerImageName().asCanonicalNameString(); client.pullImageCmd(dummySourceImage) .exec(new PullImageResultCallback()) diff --git a/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java b/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java index 54488773652..b0ed9428a5d 100644 --- a/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java +++ b/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java @@ -35,7 +35,7 @@ public static String[] getNames() { @Test public void testValidNameAccepted() { - new DockerImageName(imageName).assertValid(); + DockerImageName.parse(imageName).assertValid(); } } @@ -56,7 +56,7 @@ public static String[] getNames() { @Test(expected = IllegalArgumentException.class) public void testInvalidNameRejected() { - new DockerImageName(imageName).assertValid(); + DockerImageName.parse(imageName).assertValid(); } } @@ -66,12 +66,18 @@ public static class Parsing { @Parameterized.Parameters(name = "{0}{1}{2}{3}{4}") public static String[][] getNames() { return new String[][]{ + {"", "", "myname", ":", null}, {"", "", "myname", ":", "latest"}, + {"", "", "repo/myname", ":", null}, {"", "", "repo/myname", ":", "latest"}, + {"registry.foo.com:1234", "/", "my-name", ":", null}, {"registry.foo.com:1234", "/", "my-name", ":", "1.0"}, {"registry.foo.com", "/", "my-name", ":", "1.0"}, + {"registry.foo.com:1234", "/", "repo_here/my-name", ":", null}, {"registry.foo.com:1234", "/", "repo_here/my-name", ":", "1.0"}, + {"1.2.3.4:1234", "/", "repo_here/my-name", ":", null}, {"1.2.3.4:1234", "/", "repo_here/my-name", ":", "1.0"}, + {"1.2.3.4:1234", "/", "my-name", ":", null}, {"1.2.3.4:1234", "/", "my-name", ":", "1.0"}, {"", "", "myname", "@", "sha256:1234abcd1234abcd1234abcd1234abcd"}, {"", "", "repo/myname", "@", "sha256:1234abcd1234abcd1234abcd1234abcd"}, @@ -98,22 +104,38 @@ public static String[][] getNames() { @Test public void testParsing() { final String unversionedPart = registry + registrySeparator + repo; - final String combined = unversionedPart + versionSeparator + version; + + String combined; + String canonicalName; + if (version != null) { + combined = unversionedPart + versionSeparator + version; + canonicalName = unversionedPart + versionSeparator + version; + } else { + combined = unversionedPart; + canonicalName = unversionedPart + ":latest"; + } VisibleAssertions.context("For " + combined); VisibleAssertions.context("Using single-arg constructor:", 2); - final DockerImageName imageName = new DockerImageName(combined); + final DockerImageName imageName = DockerImageName.parse(combined); assertEquals(combined + " has registry address: " + registry, registry, imageName.getRegistry()); assertEquals(combined + " has unversioned part: " + unversionedPart, unversionedPart, imageName.getUnversionedPart()); - assertEquals(combined + " has version part: " + version, version, imageName.getVersionPart()); - - VisibleAssertions.context("Using two-arg constructor:", 2); - - final DockerImageName imageNameFromSecondaryConstructor = new DockerImageName(unversionedPart, version); - assertEquals(combined + " has registry address: " + registry, registry, imageNameFromSecondaryConstructor.getRegistry()); - assertEquals(combined + " has unversioned part: " + unversionedPart, unversionedPart, imageNameFromSecondaryConstructor.getUnversionedPart()); - assertEquals(combined + " has version part: " + version, version, imageNameFromSecondaryConstructor.getVersionPart()); + if (version != null) { + assertEquals(combined + " has version part: " + version, version, imageName.getVersionPart()); + } else { + assertEquals(combined + " has implicit version: latest", "latest", imageName.getVersionPart()); + } + assertEquals(combined + " has canonical name: " + canonicalName, canonicalName, imageName.asCanonicalNameString()); + + if (version != null) { + VisibleAssertions.context("Using two-arg constructor:", 2); + + final DockerImageName imageNameFromSecondaryConstructor = new DockerImageName(unversionedPart, version); + assertEquals(combined + " has registry address: " + registry, registry, imageNameFromSecondaryConstructor.getRegistry()); + assertEquals(combined + " has unversioned part: " + unversionedPart, unversionedPart, imageNameFromSecondaryConstructor.getUnversionedPart()); + assertEquals(combined + " has version part: " + version, version, imageNameFromSecondaryConstructor.getVersionPart()); + } } } } diff --git a/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java b/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java index e1cb595e903..a5e5ab4260d 100644 --- a/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java +++ b/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java @@ -18,7 +18,7 @@ public class RegistryAuthLocatorTest { public void lookupAuthConfigWithoutCredentials() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-empty.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("unauthenticated.registry.org/org/repo"), new AuthConfig()); + final AuthConfig authConfig = authLocator.lookupAuthConfig(DockerImageName.parse("unauthenticated.registry.org/org/repo"), new AuthConfig()); assertEquals("Default docker registry URL is set on auth config", "https://index.docker.io/v1/", authConfig.getRegistryAddress()); assertNull("No username is set", authConfig.getUsername()); @@ -29,7 +29,7 @@ public void lookupAuthConfigWithoutCredentials() throws URISyntaxException { public void lookupAuthConfigWithBasicAuthCredentials() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-basic-auth.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("registry.example.com/org/repo"), new AuthConfig()); + final AuthConfig authConfig = authLocator.lookupAuthConfig(DockerImageName.parse("registry.example.com/org/repo"), new AuthConfig()); assertEquals("Default docker registry URL is set on auth config", "https://registry.example.com", authConfig.getRegistryAddress()); assertEquals("Username is set", "user", authConfig.getUsername()); @@ -40,7 +40,7 @@ public void lookupAuthConfigWithBasicAuthCredentials() throws URISyntaxException public void lookupAuthConfigWithJsonKeyCredentials() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-with-json-key.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("registry.example.com/org/repo"), new AuthConfig()); + final AuthConfig authConfig = authLocator.lookupAuthConfig(DockerImageName.parse("registry.example.com/org/repo"), new AuthConfig()); assertEquals("Default docker registry URL is set on auth config", "https://registry.example.com", authConfig.getRegistryAddress()); assertEquals("Username is set", "_json_key", authConfig.getUsername()); @@ -51,7 +51,7 @@ public void lookupAuthConfigWithJsonKeyCredentials() throws URISyntaxException { public void lookupAuthConfigUsingStore() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-with-store.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("registry.example.com/org/repo"), new AuthConfig()); + final AuthConfig authConfig = authLocator.lookupAuthConfig(DockerImageName.parse("registry.example.com/org/repo"), new AuthConfig()); assertEquals("Correct server URL is obtained from a credential store", "url", authConfig.getRegistryAddress()); assertEquals("Correct username is obtained from a credential store", "username", authConfig.getUsername()); @@ -62,7 +62,7 @@ public void lookupAuthConfigUsingStore() throws URISyntaxException { public void lookupAuthConfigUsingHelper() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-with-helper.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("registry.example.com/org/repo"), new AuthConfig()); + final AuthConfig authConfig = authLocator.lookupAuthConfig(DockerImageName.parse("registry.example.com/org/repo"), new AuthConfig()); assertEquals("Correct server URL is obtained from a credential store", "url", authConfig.getRegistryAddress()); assertEquals("Correct username is obtained from a credential store", "username", authConfig.getUsername()); @@ -73,7 +73,7 @@ public void lookupAuthConfigUsingHelper() throws URISyntaxException { public void lookupAuthConfigUsingHelperWithToken() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-with-helper-using-token.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("registrytoken.example.com/org/repo"), new AuthConfig()); + final AuthConfig authConfig = authLocator.lookupAuthConfig(DockerImageName.parse("registrytoken.example.com/org/repo"), new AuthConfig()); assertEquals("Correct identitytoken is obtained from a credential store", "secret", authConfig.getIdentitytoken()); } @@ -82,7 +82,7 @@ public void lookupAuthConfigUsingHelperWithToken() throws URISyntaxException { public void lookupUsingHelperEmptyAuth() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-empty-auth-with-helper.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("registry.example.com/org/repo"), new AuthConfig()); + final AuthConfig authConfig = authLocator.lookupAuthConfig(DockerImageName.parse("registry.example.com/org/repo"), new AuthConfig()); assertEquals("Correct server URL is obtained from a credential store", "url", authConfig.getRegistryAddress()); assertEquals("Correct username is obtained from a credential store", "username", authConfig.getUsername()); @@ -93,7 +93,7 @@ public void lookupUsingHelperEmptyAuth() throws URISyntaxException { public void lookupNonEmptyAuthWithHelper() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-existing-auth-with-helper.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("registry.example.com/org/repo"), new AuthConfig()); + final AuthConfig authConfig = authLocator.lookupAuthConfig(DockerImageName.parse("registry.example.com/org/repo"), new AuthConfig()); assertEquals("Correct server URL is obtained from a credential helper", "url", authConfig.getRegistryAddress()); assertEquals("Correct username is obtained from a credential helper", "username", authConfig.getUsername()); @@ -105,7 +105,7 @@ public void lookupAuthConfigWithCredentialsNotFound() throws URISyntaxException Map notFoundMessagesReference = new HashMap<>(); final RegistryAuthLocator authLocator = createTestAuthLocator("config-with-store.json", notFoundMessagesReference); - DockerImageName dockerImageName = new DockerImageName("registry2.example.com/org/repo"); + DockerImageName dockerImageName = DockerImageName.parse("registry2.example.com/org/repo"); final AuthConfig authConfig = authLocator.lookupAuthConfig(dockerImageName, new AuthConfig()); assertNull("No username should have been obtained from a credential store", authConfig.getUsername()); @@ -124,7 +124,7 @@ public void lookupAuthConfigWithCredentialsNotFound() throws URISyntaxException public void lookupAuthConfigWithCredStoreEmpty() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-with-store-empty.json"); - DockerImageName dockerImageName = new DockerImageName("registry2.example.com/org/repo"); + DockerImageName dockerImageName = DockerImageName.parse("registry2.example.com/org/repo"); final AuthConfig authConfig = authLocator.lookupAuthConfig(dockerImageName, new AuthConfig()); assertNull("CredStore field will be ignored, because value is blank", authConfig.getAuth()); diff --git a/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java b/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java index 172a821b178..f49e6a26565 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java @@ -4,6 +4,7 @@ import org.junit.Test; import org.testcontainers.containers.Container; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.utility.DockerImageName; import java.io.IOException; @@ -13,7 +14,7 @@ public class CmdModifierTest { // hostname { @Rule - public GenericContainer theCache = new GenericContainer<>("redis:3.0.2") + public GenericContainer theCache = new GenericContainer<>(DockerImageName.parse("redis:3.0.2")) .withCreateContainerCmdModifier(cmd -> cmd.withHostName("the-cache")); // } @@ -22,7 +23,7 @@ public class CmdModifierTest { private long memorySwapInBytes = 12 * 1024 * 1024; @Rule - public GenericContainer memoryLimitedRedis = new GenericContainer<>("redis:3.0.2") + public GenericContainer memoryLimitedRedis = new GenericContainer<>(DockerImageName.parse("redis:3.0.2")) .withCreateContainerCmdModifier(cmd -> cmd.getHostConfig() .withMemory(memoryInBytes) .withMemorySwap(memorySwapInBytes) diff --git a/docs/examples/junit4/generic/src/test/java/generic/CommandsTest.java b/docs/examples/junit4/generic/src/test/java/generic/CommandsTest.java index 486464c7dda..23b265c26fa 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/CommandsTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/CommandsTest.java @@ -3,13 +3,14 @@ import org.junit.Rule; import org.junit.Test; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.utility.DockerImageName; import static org.junit.Assert.assertTrue; public class CommandsTest { @Rule // startupCommand { - public GenericContainer redisWithCustomPort = new GenericContainer("redis:5.0") + public GenericContainer redisWithCustomPort = new GenericContainer(DockerImageName.parse("redis:5.0")) .withCommand("redis-server --port 7777") // } .withExposedPorts(7777); diff --git a/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java b/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java index 19a40a7c43a..b0e8c98a0e4 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java @@ -3,28 +3,35 @@ import org.junit.ClassRule; import org.junit.Test; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.utility.DockerImageName; import static org.junit.Assert.assertTrue; public class ContainerCreationTest { + // simple { + public static final DockerImageName REDIS_IMAGE = DockerImageName.parse("redis:3.0.2"); + // } + // simple { @ClassRule - public static GenericContainer redis = - new GenericContainer("redis:3.0.2") + public static GenericContainer redis = + new GenericContainer<>(REDIS_IMAGE) .withExposedPorts(6379); // } + public static final DockerImageName ALPINE_IMAGE = DockerImageName.parse("alpine:3.2"); + // withOptions { - // Set up a plain OS container and customize environment, - // command and exposed ports. This just listens on port 80 + // Set up a plain OS container and customize environment, + // command and exposed ports. This just listens on port 80 // and always returns '42' @ClassRule - public static GenericContainer alpine = - new GenericContainer("alpine:3.2") + public static GenericContainer alpine = + new GenericContainer<>(ALPINE_IMAGE) .withExposedPorts(80) .withEnv("MAGIC_NUMBER", "42") - .withCommand("/bin/sh", "-c", + .withCommand("/bin/sh", "-c", "while true; do echo \"$MAGIC_NUMBER\" | nc -l -p 80; done"); // } diff --git a/docs/examples/junit4/generic/src/test/java/generic/ContainerLabelTest.java b/docs/examples/junit4/generic/src/test/java/generic/ContainerLabelTest.java index f8f26eaa5fe..ca0878e89d4 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/ContainerLabelTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/ContainerLabelTest.java @@ -1,13 +1,14 @@ package generic; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.utility.DockerImageName; import java.util.HashMap; import java.util.Map; public class ContainerLabelTest { // single_label { - public GenericContainer containerWithLabel = new GenericContainer("alpine:3.6") + public GenericContainer containerWithLabel = new GenericContainer(DockerImageName.parse("alpine:3.6")) .withLabel("key", "value"); // } @@ -16,7 +17,7 @@ public class ContainerLabelTest { private Map mapOfLabels = new HashMap<>(); // populate map, e.g. mapOfLabels.put("key1", "value1"); - public GenericContainer containerWithMultipleLabels = new GenericContainer("alpine:3.6") + public GenericContainer containerWithMultipleLabels = new GenericContainer(DockerImageName.parse("alpine:3.6")) .withLabels(mapOfLabels); // } } diff --git a/docs/examples/junit4/generic/src/test/java/generic/ExecTest.java b/docs/examples/junit4/generic/src/test/java/generic/ExecTest.java index e160390c293..e35ae18af70 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/ExecTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/ExecTest.java @@ -4,6 +4,7 @@ import org.junit.Test; import org.testcontainers.containers.Container; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.utility.DockerImageName; import java.io.IOException; @@ -12,7 +13,7 @@ public class ExecTest { @Rule - public GenericContainer container = new GenericContainer("alpine:3.6") + public GenericContainer container = new GenericContainer<>(DockerImageName.parse("alpine:3.6")) .withCommand("top"); @Test diff --git a/docs/examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java b/docs/examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java index f5a92423b6c..329fd8d8e19 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java @@ -5,6 +5,7 @@ import org.slf4j.Logger; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.utility.DockerImageName; import static org.slf4j.LoggerFactory.getLogger; @@ -14,7 +15,7 @@ public class MultiplePortsExposedTest { @Rule // rule { - public GenericContainer container = new GenericContainer("orientdb:3.0.13") + public GenericContainer container = new GenericContainer(DockerImageName.parse("orientdb:3.0.13")) .withExposedPorts(2424, 2480) .withLogConsumer(new Slf4jLogConsumer(log)); // } diff --git a/docs/examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java b/docs/examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java index 0bb5eb40bc1..d45b1e28c47 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java @@ -6,6 +6,7 @@ import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.containers.wait.strategy.WaitStrategy; +import org.testcontainers.utility.DockerImageName; import static org.junit.Assert.assertTrue; @@ -13,20 +14,20 @@ public class WaitStrategiesTest { @Rule // waitForNetworkListening { - public GenericContainer nginx = new GenericContainer("nginx:1.9.4") + public GenericContainer nginx = new GenericContainer(DockerImageName.parse("nginx:1.9.4")) .withExposedPorts(80); // } @Rule // waitForSimpleHttp { - public GenericContainer nginxWithHttpWait = new GenericContainer("nginx:1.9.4") + public GenericContainer nginxWithHttpWait = new GenericContainer(DockerImageName.parse("nginx:1.9.4")) .withExposedPorts(80) .waitingFor(Wait.forHttp("/")); // } @Rule // logMessageWait { - public GenericContainer containerWithLogWait = new GenericContainer("redis:5.0.3") + public GenericContainer containerWithLogWait = new GenericContainer(DockerImageName.parse("redis:5.0.3")) .withExposedPorts(6379) .waitingFor( Wait.forLogMessage(".*Ready to accept connections.*\\n", 1) diff --git a/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java b/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java index deef64bc59b..0e72efb5723 100644 --- a/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java +++ b/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java @@ -7,6 +7,7 @@ import org.junit.runners.Suite; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.output.WaitingConsumer; +import org.testcontainers.utility.DockerImageName; import java.time.Duration; import java.util.concurrent.TimeUnit; @@ -35,7 +36,7 @@ private static void waitForHello(GenericContainer container) throws TimeoutExcep public static class OneShotStrategyTest { @Rule // withOneShotStrategy { - public GenericContainer bboxWithOneShot = new GenericContainer("busybox:1.31.1") + public GenericContainer bboxWithOneShot = new GenericContainer<>(DockerImageName.parse("busybox:1.31.1")) .withCommand(String.format("echo %s", HELLO_TESTCONTAINERS)) .withStartupCheckStrategy( new OneShotStartupCheckStrategy().withTimeout(Duration.ofSeconds(3)) @@ -54,7 +55,7 @@ public void testCommandIsExecuted() { public static class IndefiniteOneShotStrategyTest { @Rule // withIndefiniteOneShotStrategy { - public GenericContainer bboxWithIndefiniteOneShot = new GenericContainer("busybox:1.31.1") + public GenericContainer bboxWithIndefiniteOneShot = new GenericContainer<>(DockerImageName.parse("busybox:1.31.1")) .withCommand("sh", "-c", String.format("sleep 5 && echo \"%s\"", HELLO_TESTCONTAINERS)) .withStartupCheckStrategy( new IndefiniteWaitOneShotStartupCheckStrategy() @@ -73,7 +74,7 @@ public void testCommandIsExecuted() { public static class MinimumDurationStrategyTest { @Rule // withMinimumDurationStrategy { - public GenericContainer bboxWithMinimumDuration = new GenericContainer("busybox:1.31.1") + public GenericContainer bboxWithMinimumDuration = new GenericContainer<>(DockerImageName.parse("busybox:1.31.1")) .withCommand("sh", "-c", String.format("sleep 5 && echo \"%s\"", HELLO_TESTCONTAINERS)) .withStartupCheckStrategy( new MinimumDurationRunningStartupCheckStrategy(Duration.ofSeconds(1)) diff --git a/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java b/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java index f2ef1ab0220..cbb49b5fdb3 100644 --- a/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java +++ b/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java @@ -5,6 +5,7 @@ import org.junit.Rule; import org.junit.Test; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.utility.DockerImageName; import static org.junit.Assert.assertEquals; @@ -14,7 +15,7 @@ public class RedisBackedCacheIntTest { // rule { @Rule - public GenericContainer redis = new GenericContainer<>("redis:5.0.3-alpine") + public GenericContainer redis = new GenericContainer(DockerImageName.parse("redis:5.0.3-alpine")) .withExposedPorts(6379); // } diff --git a/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java b/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java index 0f4f98fb669..9a3855ba23c 100644 --- a/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java +++ b/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java @@ -6,6 +6,7 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -17,7 +18,7 @@ public class RedisBackedCacheIntTest { // container { @Container - public GenericContainer redis = new GenericContainer<>("redis:5.0.3-alpine") + public GenericContainer redis = new GenericContainer(DockerImageName.parse("redis:5.0.3-alpine")) .withExposedPorts(6379); // } diff --git a/docs/features/creating_container.md b/docs/features/creating_container.md index e92f20e71cd..443e04f1a91 100644 --- a/docs/features/creating_container.md +++ b/docs/features/creating_container.md @@ -12,9 +12,23 @@ images as temporary test dependencies. For example, if you might use it to test With a generic container, you set the container image using a parameter to the rule constructor, e.g.: ```java -new GenericContainer("jboss/wildfly:9.0.1.Final") +new GenericContainer(DockerImageName.parse("jboss/wildfly:9.0.1.Final")) ``` +### Specifying an image + +Many Container classes in Testcontainers have historically supported: + +* a no-args constructor - for example `new GenericContainer()` and `new ElasticsearchContainer()`. With these constructors, Testcontainers has traditionally used a default image name (including a fixed image tag/version). This has caused a conflict between the need to keep the defaults sane (i.e. up to date) and the need to avoid silently upgrading these dependencies along with new versions of Testcontainers. +* a single string-argument constructor, which has taken either a version or an image name as a String. This has caused some ambiguity and confusion. + +Since v1.15.0, both of these constructor types have been deprecated, for the reasons given above. + +Instead, it is highly recommended that _all containers_ be constructed using a constructor that accepts a `DockerImageName` object. +The `DockerImageName` class is an unambiguous reference to a docker image. + +It is suggested that developers treat `DockerImageName`s as you would any other potentially-constant value - consider defining a constant in your test codebase that matches the production version of the dependency you are using. + ### Examples A generic container rule can be used with any public docker image; for example: diff --git a/examples/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java b/examples/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java index 5c78b3fdbd0..add59fd15ba 100644 --- a/examples/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java +++ b/examples/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java @@ -4,10 +4,17 @@ import org.junit.Rule; import org.junit.Test; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.utility.DockerImageName; import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.*; -import static org.rnorth.visibleassertions.VisibleAssertions.*; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.context; +import static org.rnorth.visibleassertions.VisibleAssertions.info; /** * Created by rnorth on 03/01/2016. @@ -15,7 +22,7 @@ public class DisqueBackedMailSenderTest { @Rule - public GenericContainer container = new GenericContainer("richnorth/disque:1.0-rc1") + public GenericContainer container = new GenericContainer<>(DockerImageName.parse("richnorth/disque:1.0-rc1")) .withExposedPorts(7711); private DisqueClient disqueClient; diff --git a/examples/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java b/examples/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java index 8137f8fae90..3fa115bea20 100644 --- a/examples/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java +++ b/examples/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java @@ -7,10 +7,14 @@ import org.junit.Rule; import org.junit.Test; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.utility.DockerImageName; import java.util.concurrent.TimeUnit; -import static org.rnorth.visibleassertions.VisibleAssertions.*; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; +import static org.rnorth.visibleassertions.VisibleAssertions.context; +import static org.rnorth.visibleassertions.VisibleAssertions.info; /** * Created by rnorth on 03/01/2016. @@ -18,7 +22,7 @@ public class SingleDisqueInstanceTest { @Rule - public GenericContainer container = new GenericContainer("richnorth/disque:1.0-rc1") + public GenericContainer container = new GenericContainer<>(DockerImageName.parse("richnorth/disque:1.0-rc1")) .withExposedPorts(7711); private DisqueCommands connection; private AddJobArgs retryAfter1Second; diff --git a/examples/gradle/wrapper/gradle-wrapper.jar b/examples/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..62d4c053550b91381bbd28b1afc82d634bf73a8a GIT binary patch literal 58910 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 literal 0 HcmV?d00001 diff --git a/examples/gradle/wrapper/gradle-wrapper.properties b/examples/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000000..622ab64a3cb --- /dev/null +++ b/examples/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/examples/gradlew b/examples/gradlew index 56f7b0fc7bf..fbd7c515832 100755 --- a/examples/gradlew +++ b/examples/gradlew @@ -80,7 +80,8 @@ case "`uname`" in ;; esac -CLASSPATH=$APP_HOME/../gradle/wrapper/gradle-wrapper.jar +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then @@ -129,6 +130,7 @@ fi if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath @@ -154,19 +156,19 @@ if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -175,14 +177,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@" diff --git a/examples/gradlew.bat b/examples/gradlew.bat index f3a074d99d4..5093609d512 100644 --- a/examples/gradlew.bat +++ b/examples/gradlew.bat @@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @@ -79,7 +82,8 @@ set CMD_LINE_ARGS=%* :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\..\gradle\wrapper\gradle-wrapper.jar +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% diff --git a/examples/linked-container/src/test/java/com/example/linkedcontainer/LinkedContainerTestImages.java b/examples/linked-container/src/test/java/com/example/linkedcontainer/LinkedContainerTestImages.java new file mode 100644 index 00000000000..8bbbafd189f --- /dev/null +++ b/examples/linked-container/src/test/java/com/example/linkedcontainer/LinkedContainerTestImages.java @@ -0,0 +1,8 @@ +package com.example.linkedcontainer; + +import org.testcontainers.utility.DockerImageName; + +public interface LinkedContainerTestImages { + DockerImageName POSTGRES_TEST_IMAGE = DockerImageName.parse("postgres:9.6.12"); + DockerImageName REDMINE_TEST_IMAGE = DockerImageName.parse("redmine:3.3.2"); +} diff --git a/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java b/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java index 82275dcc71c..b84b2a9e2c9 100644 --- a/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java +++ b/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java @@ -15,11 +15,11 @@ public class RedmineClientTest { private static final String POSTGRES_USERNAME = "redmine"; private static final String POSTGRES_PASSWORD = "secret"; - private PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer("postgres:9.6.2") + private PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer<>(LinkedContainerTestImages.POSTGRES_TEST_IMAGE) .withUsername(POSTGRES_USERNAME) .withPassword(POSTGRES_PASSWORD); - private RedmineContainer redmineContainer = new RedmineContainer("redmine:3.3.2") + private RedmineContainer redmineContainer = new RedmineContainer(LinkedContainerTestImages.REDMINE_TEST_IMAGE) .withLinkToContainer(postgreSQLContainer, "postgres") .withEnv("POSTGRES_ENV_POSTGRES_USER", POSTGRES_USERNAME) .withEnv("POSTGRES_ENV_POSTGRES_PASSWORD", POSTGRES_PASSWORD); diff --git a/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java b/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java index 113d0a76ec0..a75867205ab 100644 --- a/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java +++ b/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java @@ -3,6 +3,7 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.traits.LinkableContainer; import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.utility.DockerImageName; /** * A Redmine container. @@ -11,7 +12,7 @@ public class RedmineContainer extends GenericContainer { private static final int REDMINE_PORT = 3000; - public RedmineContainer(String dockerImageName) { + public RedmineContainer(DockerImageName dockerImageName) { super(dockerImageName); } diff --git a/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainer.java b/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainer.java index 00fd9c05b52..e44f2bb982e 100644 --- a/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainer.java +++ b/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainer.java @@ -1,6 +1,7 @@ package org.testcontainers.containers; import org.jetbrains.annotations.NotNull; +import org.testcontainers.utility.DockerImageName; /** @@ -24,7 +25,9 @@ public class MongoDbContainer extends GenericContainer { /** * Creates a new {@link MongoDbContainer} with the {@value DEFAULT_IMAGE_AND_TAG} image. + * @deprecated use {@link MongoDbContainer(DockerImageName)} instead */ + @Deprecated public MongoDbContainer() { this(DEFAULT_IMAGE_AND_TAG); } @@ -33,9 +36,18 @@ public MongoDbContainer() { * Creates a new {@link MongoDbContainer} with the given {@code 'image'}. * * @param image the image (e.g. {@value DEFAULT_IMAGE_AND_TAG}) to use + * @deprecated use {@link MongoDbContainer(DockerImageName)} instead */ + @Deprecated public MongoDbContainer(@NotNull String image) { - super(image); + this(DockerImageName.parse(image)); + } + + /** + * Creates a new {@link MongoDbContainer} with the specified image. + */ + public MongoDbContainer(final DockerImageName dockerImageName) { + super(dockerImageName); addExposedPort(MONGODB_PORT); } diff --git a/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java b/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java index 96096d51c1a..7c87e46bccd 100644 --- a/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java +++ b/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java @@ -1,16 +1,17 @@ package org.testcontainers.containers; +import org.junit.Test; +import org.testcontainers.utility.DockerImageName; + import java.io.IOException; import java.net.Socket; -import org.junit.Test; - public class MongoDbContainerTest { @Test public void containerStartsAndPublicPortIsAvailable() { - try (MongoDbContainer container = new MongoDbContainer()) { + try (MongoDbContainer container = new MongoDbContainer(DockerImageName.parse("mongo:4.0"))) { container.start(); assertThatPortIsAvailable(container); } diff --git a/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java b/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java index ad2d62a2f04..5d204cf99c4 100644 --- a/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java +++ b/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java @@ -1,6 +1,7 @@ import com.mycompany.cache.Cache; import com.mycompany.cache.RedisBackedCache; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.utility.DockerImageName; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; @@ -9,14 +10,16 @@ import java.util.Optional; -import static org.rnorth.visibleassertions.VisibleAssertions.*; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; /** * Integration test for Redis-backed cache implementation. */ public class RedisBackedCacheTest { - private static GenericContainer redis = new GenericContainer("redis:3.0.6").withExposedPorts(6379); + private static GenericContainer redis = new GenericContainer<>(DockerImageName.parse("redis:3.0.6")).withExposedPorts(6379); private Cache cache; diff --git a/examples/redis-backed-cache/src/test/java/RedisBackedCacheTest.java b/examples/redis-backed-cache/src/test/java/RedisBackedCacheTest.java index eaff293e518..d9d93b8255e 100644 --- a/examples/redis-backed-cache/src/test/java/RedisBackedCacheTest.java +++ b/examples/redis-backed-cache/src/test/java/RedisBackedCacheTest.java @@ -4,11 +4,14 @@ import org.junit.Rule; import org.junit.Test; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.utility.DockerImageName; import redis.clients.jedis.Jedis; import java.util.Optional; -import static org.rnorth.visibleassertions.VisibleAssertions.*; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; /** * Integration test for Redis-backed cache implementation. @@ -16,7 +19,7 @@ public class RedisBackedCacheTest { @Rule - public GenericContainer redis = new GenericContainer("redis:3.0.6") + public GenericContainer redis = new GenericContainer<>(DockerImageName.parse("redis:3.0.6")) .withExposedPorts(6379); private Cache cache; diff --git a/examples/singleton-container/src/test/java/com/example/AbstractIntegrationTest.java b/examples/singleton-container/src/test/java/com/example/AbstractIntegrationTest.java index d779adc5630..d152540fb1c 100644 --- a/examples/singleton-container/src/test/java/com/example/AbstractIntegrationTest.java +++ b/examples/singleton-container/src/test/java/com/example/AbstractIntegrationTest.java @@ -1,10 +1,11 @@ package com.example; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.utility.DockerImageName; public abstract class AbstractIntegrationTest { - public static final GenericContainer redis = new GenericContainer("redis:3.0.6") + public static final GenericContainer redis = new GenericContainer<>(DockerImageName.parse("redis:3.0.6")) .withExposedPorts(6379); static { diff --git a/examples/solr-container/src/test/java/com/example/SolrQueryTest.java b/examples/solr-container/src/test/java/com/example/SolrQueryTest.java index 413d7937853..63ac67ac1cd 100644 --- a/examples/solr-container/src/test/java/com/example/SolrQueryTest.java +++ b/examples/solr-container/src/test/java/com/example/SolrQueryTest.java @@ -1,13 +1,5 @@ package com.example; -import static com.example.SolrSearchEngine.COLLECTION_NAME; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; - -import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.impl.Http2SolrClient; @@ -16,10 +8,21 @@ import org.junit.BeforeClass; import org.junit.Test; import org.testcontainers.containers.SolrContainer; +import org.testcontainers.utility.DockerImageName; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static com.example.SolrSearchEngine.COLLECTION_NAME; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; public class SolrQueryTest { - public static final SolrContainer solrContainer = new SolrContainer() + private static final DockerImageName SOLR_IMAGE = DockerImageName.parse("solr:8.3.0"); + + public static final SolrContainer solrContainer = new SolrContainer(SOLR_IMAGE) .withCollection(COLLECTION_NAME); private static SolrClient solrClient; diff --git a/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java b/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java index 79828923d7d..b6859563c69 100644 --- a/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java +++ b/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java @@ -8,6 +8,7 @@ import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.utility.DockerImageName; @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = DemoApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT, properties = { @@ -16,7 +17,7 @@ @ActiveProfiles("test") public abstract class AbstractIntegrationTest { - static GenericContainer redis = new GenericContainer("redis:3-alpine") + static GenericContainer redis = new GenericContainer<>(DockerImageName.parse("redis:3-alpine")) .withExposedPorts(6379); @DynamicPropertySource diff --git a/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java b/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java index 27c2f182e9e..681c58f4efb 100644 --- a/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java +++ b/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java @@ -7,6 +7,7 @@ import org.testcontainers.delegate.DatabaseDelegate; import org.testcontainers.ext.ScriptUtils; import org.testcontainers.ext.ScriptUtils.ScriptLoadException; +import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MountableFile; import javax.script.ScriptException; @@ -24,7 +25,9 @@ */ public class CassandraContainer> extends GenericContainer { + @Deprecated public static final String IMAGE = "cassandra"; + public static final Integer CQL_PORT = 9042; private static final String CONTAINER_CONFIG_LOCATION = "/etc/cassandra"; private static final String USERNAME = "cassandra"; @@ -34,11 +37,23 @@ public class CassandraContainer> extends G private String initScriptPath; private boolean enableJmxReporting; + /** + * @deprecated use {@link #CassandraContainer(DockerImageName)} instead + */ + @Deprecated public CassandraContainer() { - this(IMAGE + ":3.11.2"); + this("cassandra:3.11.2"); } + /** + * @deprecated use {@link #CassandraContainer(DockerImageName)} instead + */ + @Deprecated public CassandraContainer(String dockerImageName) { + this(DockerImageName.parse(dockerImageName)); + } + + public CassandraContainer(DockerImageName dockerImageName) { super(dockerImageName); addExposedPort(CQL_PORT); setStartupAttempts(3); diff --git a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java index 5bc68aa9972..76c8c8438d7 100644 --- a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java +++ b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java @@ -7,8 +7,11 @@ import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.testcontainers.containers.wait.CassandraQueryWaitStrategy; +import org.testcontainers.utility.DockerImageName; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; /** * @author Eugeny Karpov @@ -16,11 +19,13 @@ @Slf4j public class CassandraContainerTest { + private static final DockerImageName CASSANDRA_IMAGE = DockerImageName.parse("cassandra:3.11.2"); + private static final String TEST_CLUSTER_NAME_IN_CONF = "Test Cluster Integration Test"; @Test public void testSimple() { - try (CassandraContainer cassandraContainer = new CassandraContainer<>()) { + try (CassandraContainer cassandraContainer = new CassandraContainer<>(CASSANDRA_IMAGE)) { cassandraContainer.start(); ResultSet resultSet = performQuery(cassandraContainer, "SELECT release_version FROM system.local"); assertTrue("Query was not applied", resultSet.wasApplied()); @@ -31,7 +36,7 @@ public void testSimple() { @Test public void testSpecificVersion() { String cassandraVersion = "3.0.15"; - try (CassandraContainer cassandraContainer = new CassandraContainer<>("cassandra:" + cassandraVersion)) { + try (CassandraContainer cassandraContainer = new CassandraContainer<>(CASSANDRA_IMAGE.withTag(cassandraVersion))) { cassandraContainer.start(); ResultSet resultSet = performQuery(cassandraContainer, "SELECT release_version FROM system.local"); assertTrue("Query was not applied", resultSet.wasApplied()); @@ -42,7 +47,7 @@ public void testSpecificVersion() { @Test public void testConfigurationOverride() { try ( - CassandraContainer cassandraContainer = new CassandraContainer<>() + CassandraContainer cassandraContainer = new CassandraContainer<>(CASSANDRA_IMAGE) .withConfigurationOverride("cassandra-test-configuration-example") ) { cassandraContainer.start(); @@ -55,7 +60,7 @@ public void testConfigurationOverride() { @Test(expected = ContainerLaunchException.class) public void testEmptyConfigurationOverride() { try ( - CassandraContainer cassandraContainer = new CassandraContainer<>() + CassandraContainer cassandraContainer = new CassandraContainer<>(CASSANDRA_IMAGE) .withConfigurationOverride("cassandra-empty-configuration") ) { cassandraContainer.start(); @@ -65,7 +70,7 @@ public void testEmptyConfigurationOverride() { @Test public void testInitScript() { try ( - CassandraContainer cassandraContainer = new CassandraContainer<>() + CassandraContainer cassandraContainer = new CassandraContainer<>(CASSANDRA_IMAGE) .withInitScript("initial.cql") ) { cassandraContainer.start(); @@ -76,7 +81,7 @@ public void testInitScript() { @Test public void testInitScriptWithLegacyCassandra() { try ( - CassandraContainer cassandraContainer = new CassandraContainer<>("cassandra:2.2.11") + CassandraContainer cassandraContainer = new CassandraContainer<>(DockerImageName.parse("cassandra:2.2.11")) .withInitScript("initial.cql") ) { cassandraContainer.start(); @@ -84,10 +89,11 @@ public void testInitScriptWithLegacyCassandra() { } } + @SuppressWarnings("deprecation") // Using deprecated constructor for verification of backwards compatibility @Test public void testCassandraQueryWaitStrategy() { try ( - CassandraContainer cassandraContainer = new CassandraContainer<>() + CassandraContainer cassandraContainer = new CassandraContainer<>() .waitingFor(new CassandraQueryWaitStrategy()) ) { cassandraContainer.start(); @@ -96,9 +102,10 @@ public void testCassandraQueryWaitStrategy() { } } + @SuppressWarnings("deprecation") // Using deprecated constructor for verification of backwards compatibility @Test public void testCassandraGetCluster() { - try (CassandraContainer cassandraContainer = new CassandraContainer<>()) { + try (CassandraContainer cassandraContainer = new CassandraContainer<>()) { cassandraContainer.start(); ResultSet resultSet = performQuery(cassandraContainer.getCluster(), "SELECT release_version FROM system.local"); assertTrue("Query was not applied", resultSet.wasApplied()); @@ -106,7 +113,7 @@ public void testCassandraGetCluster() { } } - private void testInitScript(CassandraContainer cassandraContainer) { + private void testInitScript(CassandraContainer cassandraContainer) { ResultSet resultSet = performQuery(cassandraContainer, "SELECT * FROM keySpaceTest.catalog_category"); assertTrue("Query was not applied", resultSet.wasApplied()); Row row = resultSet.one(); @@ -114,7 +121,7 @@ private void testInitScript(CassandraContainer cassandraContainer) { assertEquals("Inserted row is not in expected state", "test_category", row.getString(1)); } - private ResultSet performQuery(CassandraContainer cassandraContainer, String cql) { + private ResultSet performQuery(CassandraContainer cassandraContainer, String cql) { Cluster explicitCluster = Cluster.builder() .addContactPoint(cassandraContainer.getHost()) .withPort(cassandraContainer.getMappedPort(CassandraContainer.CQL_PORT)) diff --git a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java index dd48f32bece..6bdcff7b3f2 100644 --- a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java +++ b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java @@ -1,12 +1,14 @@ package org.testcontainers.containers; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; +import org.testcontainers.utility.DockerImageName; import java.time.Duration; public class ClickHouseContainer extends JdbcDatabaseContainer { public static final String NAME = "clickhouse"; public static final String IMAGE = "yandex/clickhouse-server"; + @Deprecated public static final String DEFAULT_TAG = "18.10.3"; public static final Integer HTTP_PORT = 8123; @@ -20,11 +22,23 @@ public class ClickHouseContainer extends JdbcDatabaseContainer { private String username = "default"; private String password = ""; + /** + * @deprecated use {@link ClickHouseContainer(DockerImageName)} instead + */ + @Deprecated public ClickHouseContainer() { super(IMAGE + ":" + DEFAULT_TAG); } + /** + * @deprecated use {@link ClickHouseContainer(DockerImageName)} instead + */ + @Deprecated public ClickHouseContainer(String dockerImageName) { + this(DockerImageName.parse(dockerImageName)); + } + + public ClickHouseContainer(final DockerImageName dockerImageName) { super(dockerImageName); withExposedPorts(HTTP_PORT, NATIVE_PORT); diff --git a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseProvider.java b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseProvider.java index be5a01657f0..cd2453b8349 100644 --- a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseProvider.java +++ b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseProvider.java @@ -1,5 +1,7 @@ package org.testcontainers.containers; +import org.testcontainers.utility.DockerImageName; + public class ClickHouseProvider extends JdbcDatabaseContainerProvider { @Override public boolean supports(String databaseType) { @@ -8,6 +10,6 @@ public boolean supports(String databaseType) { @Override public JdbcDatabaseContainer newInstance(String tag) { - return new ClickHouseContainer(ClickHouseContainer.IMAGE + ":" + tag); + return new ClickHouseContainer(DockerImageName.parse(ClickHouseContainer.IMAGE).withTag(tag)); } } diff --git a/modules/clickhouse/src/test/java/org/testcontainers/ClickhouseTestImages.java b/modules/clickhouse/src/test/java/org/testcontainers/ClickhouseTestImages.java new file mode 100644 index 00000000000..961df045bd1 --- /dev/null +++ b/modules/clickhouse/src/test/java/org/testcontainers/ClickhouseTestImages.java @@ -0,0 +1,7 @@ +package org.testcontainers; + +import org.testcontainers.utility.DockerImageName; + +public interface ClickhouseTestImages { + DockerImageName CLICKHOUSE_IMAGE = DockerImageName.parse("yandex/clickhouse-server:18.10.3"); +} diff --git a/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java b/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java index 2dbce0b3593..8f3879673a0 100644 --- a/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java +++ b/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java @@ -8,12 +8,13 @@ import java.sql.SQLException; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.testcontainers.ClickhouseTestImages.CLICKHOUSE_IMAGE; public class SimpleClickhouseTest extends AbstractContainerDatabaseTest { @Test public void testSimple() throws SQLException { - try (ClickHouseContainer clickhouse = new ClickHouseContainer()) { + try (ClickHouseContainer clickhouse = new ClickHouseContainer(CLICKHOUSE_IMAGE)) { clickhouse.start(); ResultSet resultSet = performQuery(clickhouse, "SELECT 1"); diff --git a/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java b/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java index de64902c88a..54420155f05 100644 --- a/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java +++ b/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java @@ -1,6 +1,7 @@ package org.testcontainers.containers; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; +import org.testcontainers.utility.DockerImageName; import java.time.Duration; @@ -18,11 +19,23 @@ public class CockroachContainer extends JdbcDatabaseContainer { /** * Creates a new couchbase container with the default image and version. + * @deprecated use {@link CouchbaseContainer(DockerImageName)} instead */ + @Deprecated public CouchbaseContainer() { this(DOCKER_IMAGE_NAME + ":" + VERSION); } @@ -95,10 +98,20 @@ public CouchbaseContainer() { /** * Creates a new couchbase container with a custom image name. * - * @param imageName the image name that should be used. + * @param dockerImageName the image name that should be used. + * @deprecated use {@link CouchbaseContainer(DockerImageName)} instead */ - public CouchbaseContainer(final String imageName) { - super(imageName); + @Deprecated + public CouchbaseContainer(final String dockerImageName) { + this(DockerImageName.parse(dockerImageName)); + } + + /** + * Create a new couchbase container with the specified image name. + * @param dockerImageName + */ + public CouchbaseContainer(final DockerImageName dockerImageName) { + super(dockerImageName); } /** diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java index c5c9f18a58b..3a4438c1fc6 100644 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java @@ -24,12 +24,15 @@ import com.couchbase.client.java.env.CouchbaseEnvironment; import com.couchbase.client.java.env.DefaultCouchbaseEnvironment; import org.junit.Test; +import org.testcontainers.utility.DockerImageName; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; public class CouchbaseContainerTest { + private static final DockerImageName COUCHBASE_IMAGE = DockerImageName.parse("couchbase/server:6.5.1"); + @Test public void testBasicContainerUsage() { // bucket_definition { @@ -38,7 +41,7 @@ public void testBasicContainerUsage() { try ( // container_definition { - CouchbaseContainer container = new CouchbaseContainer() + CouchbaseContainer container = new CouchbaseContainer(COUCHBASE_IMAGE) .withBucket(bucketDefinition) // } ) { diff --git a/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java b/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java index 05edcb2c3ed..aa6d136aaec 100644 --- a/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java +++ b/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java @@ -1,6 +1,7 @@ package org.testcontainers.containers; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; +import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.LicenseAcceptance; import java.time.Duration; @@ -19,12 +20,24 @@ public class Db2Container extends JdbcDatabaseContainer { private String username = "db2inst1"; private String password = "foobar1234"; + /** + * @deprecated use {@link Db2Container(DockerImageName)} instead + */ + @Deprecated public Db2Container() { this(DEFAULT_DB2_IMAGE_NAME + ":" + DEFAULT_TAG); } - public Db2Container(String imageName) { - super(imageName); + /** + * @deprecated use {@link Db2Container(DockerImageName)} instead + */ + @Deprecated + public Db2Container(String dockerImageName) { + this(DockerImageName.parse(dockerImageName)); + } + + public Db2Container(final DockerImageName dockerImageName) { + super(dockerImageName); withPrivilegedMode(true); this.waitStrategy = new LogMessageWaitStrategy() .withRegEx(".*Setup has completed\\..*") diff --git a/modules/db2/src/main/java/org/testcontainers/containers/Db2ContainerProvider.java b/modules/db2/src/main/java/org/testcontainers/containers/Db2ContainerProvider.java index 5c78fdc0505..618b97c3ca6 100644 --- a/modules/db2/src/main/java/org/testcontainers/containers/Db2ContainerProvider.java +++ b/modules/db2/src/main/java/org/testcontainers/containers/Db2ContainerProvider.java @@ -1,5 +1,7 @@ package org.testcontainers.containers; +import org.testcontainers.utility.DockerImageName; + public class Db2ContainerProvider extends JdbcDatabaseContainerProvider { @Override public boolean supports(String databaseType) { @@ -13,6 +15,6 @@ public JdbcDatabaseContainer newInstance() { @Override public JdbcDatabaseContainer newInstance(String tag) { - return new Db2Container(Db2Container.DEFAULT_DB2_IMAGE_NAME + ":" + tag); + return new Db2Container(DockerImageName.parse(Db2Container.DEFAULT_DB2_IMAGE_NAME).withTag(tag)); } } diff --git a/modules/db2/src/test/java/org/testcontainers/Db2TestImages.java b/modules/db2/src/test/java/org/testcontainers/Db2TestImages.java new file mode 100644 index 00000000000..b0bfc869272 --- /dev/null +++ b/modules/db2/src/test/java/org/testcontainers/Db2TestImages.java @@ -0,0 +1,7 @@ +package org.testcontainers; + +import org.testcontainers.utility.DockerImageName; + +public interface Db2TestImages { + DockerImageName DB2_IMAGE = DockerImageName.parse("ibmcom/db2:11.5.0.0a"); +} diff --git a/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java b/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java index 6cc7535f785..b7a1d5478e8 100644 --- a/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java +++ b/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java @@ -10,12 +10,13 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.testcontainers.Db2TestImages.DB2_IMAGE; public class SimpleDb2Test extends AbstractContainerDatabaseTest { @Test public void testSimple() throws SQLException { - try (Db2Container db2 = new Db2Container() + try (Db2Container db2 = new Db2Container(DB2_IMAGE) .acceptLicense()) { db2.start(); @@ -29,7 +30,7 @@ public void testSimple() throws SQLException { @Test public void testWithAdditionalUrlParamInJdbcUrl() { - try (Db2Container db2 = new Db2Container() + try (Db2Container db2 = new Db2Container(DB2_IMAGE) .withUrlParam("sslConnection", "false") .acceptLicense()) { diff --git a/modules/dynalite/src/main/java/org/testcontainers/dynamodb/DynaliteContainer.java b/modules/dynalite/src/main/java/org/testcontainers/dynamodb/DynaliteContainer.java index 6280ddb9421..6063c1a5dbe 100644 --- a/modules/dynalite/src/main/java/org/testcontainers/dynamodb/DynaliteContainer.java +++ b/modules/dynalite/src/main/java/org/testcontainers/dynamodb/DynaliteContainer.java @@ -7,6 +7,7 @@ import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.utility.DockerImageName; /** * Container for Dynalite, a DynamoDB clone. @@ -16,15 +17,28 @@ public class DynaliteContainer extends GenericContainer { private static final String IMAGE_NAME = "quay.io/testcontainers/dynalite:v1.2.1-1"; private static final int MAPPED_PORT = 4567; + /** + * @deprecated use {@link DynaliteContainer(DockerImageName)} instead + */ + @Deprecated public DynaliteContainer() { this(IMAGE_NAME); withExposedPorts(MAPPED_PORT); } - public DynaliteContainer(String imageName) { - super(imageName); + /** + * @deprecated use {@link DynaliteContainer(DockerImageName)} instead + */ + @Deprecated + public DynaliteContainer(String dockerImageName) { + this(DockerImageName.parse(dockerImageName)); } + public DynaliteContainer(final DockerImageName dockerImageName) { + super(dockerImageName); + } + + /** * Gets a preconfigured {@link AmazonDynamoDB} client object for connecting to this * container. diff --git a/modules/dynalite/src/test/java/org/testcontainers/dynamodb/DynaliteContainerTest.java b/modules/dynalite/src/test/java/org/testcontainers/dynamodb/DynaliteContainerTest.java index 524d3f6c34c..0b440939139 100644 --- a/modules/dynalite/src/test/java/org/testcontainers/dynamodb/DynaliteContainerTest.java +++ b/modules/dynalite/src/test/java/org/testcontainers/dynamodb/DynaliteContainerTest.java @@ -2,17 +2,26 @@ import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder; -import com.amazonaws.services.dynamodbv2.model.*; +import com.amazonaws.services.dynamodbv2.model.AttributeDefinition; +import com.amazonaws.services.dynamodbv2.model.CreateTableRequest; +import com.amazonaws.services.dynamodbv2.model.KeySchemaElement; +import com.amazonaws.services.dynamodbv2.model.KeyType; +import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput; +import com.amazonaws.services.dynamodbv2.model.ScalarAttributeType; +import com.amazonaws.services.dynamodbv2.model.TableDescription; import org.junit.Rule; import org.junit.Test; +import org.testcontainers.utility.DockerImageName; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; public class DynaliteContainerTest { + private static final DockerImageName DYNALITE_IMAGE = DockerImageName.parse("quay.io/testcontainers/dynalite:v1.2.1-1"); + @Rule - public DynaliteContainer dynamoDB = new DynaliteContainer(); + public DynaliteContainer dynamoDB = new DynaliteContainer(DYNALITE_IMAGE); @Test public void simpleTestWithManualClientCreation() { diff --git a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java index e9a0b95d7ed..6ad0172269d 100644 --- a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java +++ b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java @@ -3,6 +3,7 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.utility.Base58; +import org.testcontainers.utility.DockerImageName; import java.net.InetSocketAddress; import java.time.Duration; @@ -36,6 +37,10 @@ public class ElasticsearchContainer extends GenericContainer basic @@ -54,6 +59,7 @@ public void stopRestClient() throws IOException { } } + @SuppressWarnings("deprecation") // Using deprecated constructor for verification of backwards compatibility @Test public void elasticsearchDefaultTest() throws IOException { // Create the elasticsearch container. @@ -78,7 +84,7 @@ public void elasticsearchDefaultTest() throws IOException { @Test public void elasticsearchVersion() throws IOException { - try (ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:" + ELASTICSEARCH_VERSION)) { + try (ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE)) { container.start(); Response response = getClient(container).performRequest(new Request("GET", "/")); assertThat(response.getStatusLine().getStatusCode(), is(200)); @@ -89,10 +95,14 @@ public void elasticsearchVersion() throws IOException { @Test public void elasticsearchOssImage() throws IOException { - try ( - // oosContainer { - ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch-oss:" + ELASTICSEARCH_VERSION) - // } + try (ElasticsearchContainer container = + // oosContainer { + new ElasticsearchContainer( + DockerImageName + .parse("docker.elastic.co/elasticsearch/elasticsearch-oss") + .withTag(ELASTICSEARCH_VERSION) + ) + // } ) { container.start(); Response response = getClient(container).performRequest(new Request("GET", "/")); @@ -104,6 +114,7 @@ public void elasticsearchOssImage() throws IOException { } } + @SuppressWarnings("deprecation") // Using deprecated constructor for verification of backwards compatibility @Test public void restClientClusterHealth() throws IOException { // httpClientContainer { @@ -122,10 +133,10 @@ public void restClientClusterHealth() throws IOException { .build(); Response response = client.performRequest(new Request("GET", "/_cluster/health")); - // }} + // }} assertThat(response.getStatusLine().getStatusCode(), is(200)); assertThat(EntityUtils.toString(response.getEntity()), containsString("cluster_name")); - // httpClientContainer {{ + // httpClientContainer {{ } // } } @@ -134,7 +145,11 @@ public void restClientClusterHealth() throws IOException { public void transportClientClusterHealth() { // transportClientContainer { // Create the elasticsearch container. - try (ElasticsearchContainer container = new ElasticsearchContainer()) { + try (ElasticsearchContainer container = new ElasticsearchContainer( + DockerImageName + .parse("docker.elastic.co/elasticsearch/elasticsearch") + .withTag("6.4.1") + )){ // Start the container. This step might take some time... container.start(); diff --git a/modules/influxdb/src/main/java/org/testcontainers/containers/InfluxDBContainer.java b/modules/influxdb/src/main/java/org/testcontainers/containers/InfluxDBContainer.java index 68efc516504..5d883fea9a7 100644 --- a/modules/influxdb/src/main/java/org/testcontainers/containers/InfluxDBContainer.java +++ b/modules/influxdb/src/main/java/org/testcontainers/containers/InfluxDBContainer.java @@ -4,6 +4,7 @@ import org.influxdb.InfluxDBFactory; import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.containers.wait.strategy.WaitAllStrategy; +import org.testcontainers.utility.DockerImageName; import java.util.Collections; import java.util.Set; @@ -26,13 +27,24 @@ public class InfluxDBContainer> extends Gen private String username = "any"; private String password = "any"; - + /** + * @deprecated use {@link InfluxDBContainer(DockerImageName)} instead + */ + @Deprecated public InfluxDBContainer() { this(VERSION); } + /** + * @deprecated use {@link InfluxDBContainer(DockerImageName)} instead + */ + @Deprecated public InfluxDBContainer(final String version) { - super(IMAGE_NAME + ":" + version); + this(DockerImageName.parse(IMAGE_NAME + ":" + version)); + } + + public InfluxDBContainer(final DockerImageName dockerImageName) { + super(dockerImageName); waitStrategy = new WaitAllStrategy() .withStrategy(Wait.forHttp("/ping").withBasicCredentials(username, password).forStatusCode(204)) .withStrategy(Wait.forListeningPort()); diff --git a/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerTest.java b/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerTest.java index fa2e54fd0c9..7acf030b5e4 100644 --- a/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerTest.java +++ b/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerTest.java @@ -11,7 +11,7 @@ public class InfluxDBContainerTest { @ClassRule - public static InfluxDBContainer influxDBContainer = new InfluxDBContainer(); + public static InfluxDBContainer influxDBContainer = new InfluxDBContainer<>(InfluxDBTestImages.INFLUXDB_TEST_IMAGE); @Test public void getUrl() { diff --git a/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerWithUserTest.java b/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerWithUserTest.java index 81ad62a7b47..b045ca297c1 100644 --- a/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerWithUserTest.java +++ b/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerWithUserTest.java @@ -17,13 +17,13 @@ public class InfluxDBContainerWithUserTest { - private static final String TEST_VERSION = "1.4.3"; + private static final String TEST_VERSION = InfluxDBTestImages.INFLUXDB_TEST_IMAGE.getVersionPart(); private static final String DATABASE = "test"; private static final String USER = "test-user"; private static final String PASSWORD = "test-password"; @Rule - public InfluxDBContainer influxDBContainer = new InfluxDBContainer(TEST_VERSION) + public InfluxDBContainer influxDBContainer = new InfluxDBContainer<>(InfluxDBTestImages.INFLUXDB_TEST_IMAGE) .withDatabase(DATABASE) .withUsername(USER) .withPassword(PASSWORD); diff --git a/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBTestImages.java b/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBTestImages.java new file mode 100644 index 00000000000..a0e2d31bbdf --- /dev/null +++ b/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBTestImages.java @@ -0,0 +1,7 @@ +package org.testcontainers.containers; + +import org.testcontainers.utility.DockerImageName; + +public interface InfluxDBTestImages { + DockerImageName INFLUXDB_TEST_IMAGE = DockerImageName.parse("influxdb:1.4.3"); +} diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index 62fe43e7932..4d1c4ece67b 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -8,6 +8,7 @@ import org.testcontainers.delegate.DatabaseDelegate; import org.testcontainers.ext.ScriptUtils; import org.testcontainers.jdbc.JdbcDatabaseDelegate; +import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MountableFile; import java.sql.Connection; @@ -38,14 +39,22 @@ public abstract class JdbcDatabaseContainer image) { super(image); } + public JdbcDatabaseContainer(final DockerImageName dockerImageName) { + super(dockerImageName); + } + /** * @return the name of the actual JDBC driver to use */ diff --git a/modules/jdbc/src/test/java/org/testcontainers/jdbc/MissingJdbcDriverTest.java b/modules/jdbc/src/test/java/org/testcontainers/jdbc/MissingJdbcDriverTest.java index 38ae38095eb..88398cebf85 100644 --- a/modules/jdbc/src/test/java/org/testcontainers/jdbc/MissingJdbcDriverTest.java +++ b/modules/jdbc/src/test/java/org/testcontainers/jdbc/MissingJdbcDriverTest.java @@ -3,6 +3,7 @@ import com.google.common.base.Throwables; import org.junit.Test; import org.testcontainers.containers.JdbcDatabaseContainer; +import org.testcontainers.utility.DockerImageName; import java.sql.Connection; import java.sql.SQLException; @@ -39,7 +40,7 @@ static class MissingDriverContainer extends JdbcDatabaseContainer { private final AtomicInteger connectionAttempts = new AtomicInteger(); MissingDriverContainer() { - super("mysql:5.7.22"); + super(DockerImageName.parse("mysql:5.7.22")); withEnv("MYSQL_ROOT_PASSWORD", "test"); withExposedPorts(3306); } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/JUnitJupiterTestImages.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/JUnitJupiterTestImages.java new file mode 100644 index 00000000000..e99d1ec720c --- /dev/null +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/JUnitJupiterTestImages.java @@ -0,0 +1,8 @@ +package org.testcontainers.junit.jupiter; + +import org.testcontainers.utility.DockerImageName; + +public interface JUnitJupiterTestImages { + DockerImageName POSTGRES_IMAGE = DockerImageName.parse("postgres:9.6.12"); + DockerImageName HTTPD_IMAGE = DockerImageName.parse("httpd:2.4-alpine"); +} diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/PostgresContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/PostgresContainerTests.java index 765a9f6ff6b..4cbc331e64b 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/PostgresContainerTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/PostgresContainerTests.java @@ -9,12 +9,13 @@ import java.sql.Statement; import static org.junit.Assert.assertEquals; +import static org.testcontainers.junit.jupiter.JUnitJupiterTestImages.POSTGRES_IMAGE; @Testcontainers class PostgresContainerTests { @Container - private static final PostgreSQLContainer POSTGRE_SQL_CONTAINER = new PostgreSQLContainer() + private static final PostgreSQLContainer POSTGRE_SQL_CONTAINER = new PostgreSQLContainer<>(POSTGRES_IMAGE) .withDatabaseName("foo") .withUsername("foo") .withPassword("secret"); diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java index fb753744425..6e02e31b774 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java @@ -4,16 +4,16 @@ import org.junit.jupiter.api.Test; import org.testcontainers.containers.GenericContainer; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.testcontainers.junit.jupiter.JUnitJupiterTestImages.HTTPD_IMAGE; // testClass { @Testcontainers class TestcontainersNestedRestartedContainerTests { @Container - private final GenericContainer topLevelContainer = new GenericContainer("httpd:2.4-alpine") + private final GenericContainer topLevelContainer = new GenericContainer<>(HTTPD_IMAGE) .withExposedPorts(80); // }} @@ -34,7 +34,7 @@ void top_level_container_should_be_running() { class NestedTestCase { @Container - private final GenericContainer nestedContainer = new GenericContainer("httpd:2.4-alpine") + private final GenericContainer nestedContainer = new GenericContainer<>(HTTPD_IMAGE) .withExposedPorts(80); @Test diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedSharedContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedSharedContainerTests.java index 371a5bae178..cf59e0338a2 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedSharedContainerTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedSharedContainerTests.java @@ -6,12 +6,13 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.testcontainers.junit.jupiter.JUnitJupiterTestImages.HTTPD_IMAGE; @Testcontainers class TestcontainersNestedSharedContainerTests { @Container - private static final GenericContainer TOP_LEVEL_CONTAINER = new GenericContainer("httpd:2.4-alpine") + private static final GenericContainer TOP_LEVEL_CONTAINER = new GenericContainer<>(HTTPD_IMAGE) .withExposedPorts(80); private static String topLevelContainerId; diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersRestartBetweenTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersRestartBetweenTests.java index 2d6959ae065..eeb63b72380 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersRestartBetweenTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersRestartBetweenTests.java @@ -4,12 +4,13 @@ import org.testcontainers.containers.GenericContainer; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.testcontainers.junit.jupiter.JUnitJupiterTestImages.HTTPD_IMAGE; @Testcontainers class TestcontainersRestartBetweenTests { @Container - private GenericContainer genericContainer = new GenericContainer("httpd:2.4-alpine") + private GenericContainer genericContainer = new GenericContainer<>(HTTPD_IMAGE) .withExposedPorts(80); private static String lastContainerId; diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersSharedContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersSharedContainerTests.java index 321c0d6282f..60094994f69 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersSharedContainerTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersSharedContainerTests.java @@ -6,12 +6,13 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.testcontainers.junit.jupiter.JUnitJupiterTestImages.HTTPD_IMAGE; @Testcontainers class TestcontainersSharedContainerTests { @Container - private static final GenericContainer GENERIC_CONTAINER = new GenericContainer("httpd:2.4-alpine") + private static final GenericContainer GENERIC_CONTAINER = new GenericContainer<>(HTTPD_IMAGE) .withExposedPorts(80); private static String lastContainerId; diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/RedisContainer.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/RedisContainer.java index 177a48bb9d0..0a9ec62fca2 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/RedisContainer.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/RedisContainer.java @@ -1,16 +1,13 @@ package org.testcontainers.junit.jupiter.inheritance; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.utility.DockerImageName; import redis.clients.jedis.Jedis; public class RedisContainer extends GenericContainer { public RedisContainer() { - this("redis:3.2.11"); - } - - public RedisContainer(String dockerImageName) { - super(dockerImageName); + super(DockerImageName.parse("redis:3.2.11")); withExposedPorts(6379); } diff --git a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java index 9746849be29..2451278b1c2 100644 --- a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java +++ b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java @@ -4,6 +4,7 @@ import com.github.dockerjava.api.command.InspectContainerResponse; import lombok.SneakyThrows; import org.testcontainers.images.builder.Transferable; +import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.TestcontainersConfiguration; import java.nio.charset.StandardCharsets; @@ -29,12 +30,24 @@ public class KafkaContainer extends GenericContainer { private int port = PORT_NOT_ASSIGNED; + /** + * @deprecated use {@link KafkaContainer(DockerImageName)} instead + */ + @Deprecated public KafkaContainer() { this("5.2.1"); } + /** + * @deprecated use {@link KafkaContainer(DockerImageName)} instead + */ + @Deprecated public KafkaContainer(String confluentPlatformVersion) { - super(TestcontainersConfiguration.getInstance().getKafkaImage() + ":" + confluentPlatformVersion); + this(DockerImageName.parse(TestcontainersConfiguration.getInstance().getKafkaImage() + ":" + confluentPlatformVersion)); + } + + public KafkaContainer(final DockerImageName dockerImageName) { + super(dockerImageName); withExposedPorts(KAFKA_PORT); diff --git a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java index 25bc1b5022c..b8cd5c9be9c 100644 --- a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java +++ b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java @@ -13,6 +13,7 @@ import org.junit.Rule; import org.junit.Test; import org.rnorth.ducttape.unreliables.Unreliables; +import org.testcontainers.utility.DockerImageName; import java.time.Duration; import java.util.Arrays; @@ -24,6 +25,9 @@ public class KafkaContainerTest { + private static final DockerImageName KAFKA_TEST_IMAGE = DockerImageName.parse("confluentinc/cp-kafka:5.2.1"); + private static final DockerImageName ZOOKEEPER_TEST_IMAGE = DockerImageName.parse("confluentinc/cp-zookeeper:4.0.0"); + // junitRule { @Rule public KafkaContainer kafka = new KafkaContainer(); @@ -31,7 +35,7 @@ public class KafkaContainerTest { @Test public void testUsage() throws Exception { - try (KafkaContainer kafka = new KafkaContainer()) { + try (KafkaContainer kafka = new KafkaContainer(KAFKA_TEST_IMAGE)) { kafka.start(); testKafkaFunctionality(kafka.getBootstrapServers()); } @@ -42,7 +46,7 @@ public void testUsage() throws Exception { public void testUsageWithVersion() throws Exception { try ( // constructorWithVersion { - KafkaContainer kafka = new KafkaContainer("4.1.2") + KafkaContainer kafka = new KafkaContainer(KAFKA_TEST_IMAGE) // } ) { kafka.start(); @@ -60,12 +64,12 @@ public void testExternalZookeeperWithExternalNetwork() throws Exception { Network network = Network.newNetwork(); // withExternalZookeeper { - KafkaContainer kafka = new KafkaContainer() + KafkaContainer kafka = new KafkaContainer(KAFKA_TEST_IMAGE) .withNetwork(network) .withExternalZookeeper("zookeeper:2181"); // } - GenericContainer zookeeper = new GenericContainer("confluentinc/cp-zookeeper:4.0.0") + GenericContainer zookeeper = new GenericContainer<>(ZOOKEEPER_TEST_IMAGE) .withNetwork(network) .withNetworkAliases("zookeeper") .withEnv("ZOOKEEPER_CLIENT_PORT", "2181"); diff --git a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java index 72eb320c729..0136d94e3b2 100644 --- a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java +++ b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java @@ -10,6 +10,7 @@ import org.rnorth.ducttape.Preconditions; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.TestcontainersConfiguration; import java.net.InetAddress; @@ -36,12 +37,24 @@ public class LocalStackContainer extends GenericContainer { private final List services = new ArrayList<>(); + /** + * @deprecated use {@link LocalStackContainer(DockerImageName)} instead + */ + @Deprecated public LocalStackContainer() { this(VERSION); } + /** + * @deprecated use {@link LocalStackContainer(DockerImageName)} instead + */ + @Deprecated public LocalStackContainer(String version) { - super(TestcontainersConfiguration.getInstance().getLocalStackImage() + ":" + version); + this(DockerImageName.parse(TestcontainersConfiguration.getInstance().getLocalStackImage() + ":" + version)); + } + + public LocalStackContainer(final DockerImageName dockerImageName) { + super(dockerImageName); withFileSystemBind("//var/run/docker.sock", "/var/run/docker.sock"); waitingFor(Wait.forLogMessage(".*Ready\\.\n", 1)); diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java index d4e2f0c2864..63be9d66df7 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java @@ -29,6 +29,7 @@ import org.testcontainers.containers.Container; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; +import org.testcontainers.utility.DockerImageName; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; import software.amazon.awssdk.regions.Region; @@ -43,7 +44,10 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.containers.localstack.LocalStackContainer.Service.*; +import static org.testcontainers.containers.localstack.LocalStackContainer.Service.CLOUDWATCHLOGS; +import static org.testcontainers.containers.localstack.LocalStackContainer.Service.KMS; +import static org.testcontainers.containers.localstack.LocalStackContainer.Service.S3; +import static org.testcontainers.containers.localstack.LocalStackContainer.Service.SQS; /** * Tests for Localstack Container, used both in bridge network (exposed to host) and docker network modes. @@ -56,11 +60,14 @@ @RunWith(Enclosed.class) public class LocalstackContainerTest { + private static final DockerImageName LOCALSTACK_IMAGE = DockerImageName.parse("localstack/localstack:0.10.8"); + private static final DockerImageName AWS_CLI_IMAGE = DockerImageName.parse("atlassian/pipelines-awscli:1.16.302"); + public static class WithoutNetwork { // without_network { @ClassRule - public static LocalStackContainer localstack = new LocalStackContainer() + public static LocalStackContainer localstack = new LocalStackContainer(LOCALSTACK_IMAGE) .withServices(S3, SQS, CLOUDWATCHLOGS, KMS); // } @@ -160,14 +167,14 @@ public static class WithNetwork { private static Network network = Network.newNetwork(); @ClassRule - public static LocalStackContainer localstackInDockerNetwork = new LocalStackContainer() + public static LocalStackContainer localstackInDockerNetwork = new LocalStackContainer(LOCALSTACK_IMAGE) .withNetwork(network) .withNetworkAliases("notthis", "localstack") // the last alias is used for HOSTNAME_EXTERNAL .withServices(S3, SQS, CLOUDWATCHLOGS); // } @ClassRule - public static GenericContainer awsCliInDockerNetwork = new GenericContainer<>("atlassian/pipelines-awscli") + public static GenericContainer awsCliInDockerNetwork = new GenericContainer<>(AWS_CLI_IMAGE) .withNetwork(network) .withCreateContainerCmdModifier(cmd -> cmd.withEntrypoint("top")) .withEnv("AWS_ACCESS_KEY_ID", "accesskey") diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java index 04d745a9efe..e137c186209 100644 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java @@ -1,5 +1,7 @@ package org.testcontainers.containers; +import org.testcontainers.utility.DockerImageName; + /** * Container implementation for the MariaDB project. * @@ -22,11 +24,23 @@ public class MariaDBContainer> extends JdbcD private static final String MARIADB_ROOT_USER = "root"; private static final String MY_CNF_CONFIG_OVERRIDE_PARAM_NAME = "TC_MY_CNF"; + /** + * @deprecated use {@link MariaDBContainer(DockerImageName)} instead + */ + @Deprecated public MariaDBContainer() { this(IMAGE + ":" + DEFAULT_TAG); } + /** + * @deprecated use {@link MariaDBContainer(DockerImageName)} instead + */ + @Deprecated public MariaDBContainer(String dockerImageName) { + this(DockerImageName.parse(dockerImageName)); + } + + public MariaDBContainer(final DockerImageName dockerImageName) { super(dockerImageName); addExposedPort(MARIADB_PORT); } diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java index 86f30ee59e5..4330465a9c3 100644 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java @@ -1,8 +1,7 @@ package org.testcontainers.containers; import org.testcontainers.jdbc.ConnectionUrl; - -import java.util.Objects; +import org.testcontainers.utility.DockerImageName; /** * Factory for MariaDB org.testcontainers.containers. @@ -25,7 +24,7 @@ public JdbcDatabaseContainer newInstance() { @Override public JdbcDatabaseContainer newInstance(String tag) { - return new MariaDBContainer(MariaDBContainer.IMAGE + ":" + tag); + return new MariaDBContainer(DockerImageName.parse(MariaDBContainer.IMAGE).withTag(tag)); } @Override diff --git a/modules/mariadb/src/test/java/org/testcontainers/MariaDBTestImages.java b/modules/mariadb/src/test/java/org/testcontainers/MariaDBTestImages.java new file mode 100644 index 00000000000..7d18f2f8365 --- /dev/null +++ b/modules/mariadb/src/test/java/org/testcontainers/MariaDBTestImages.java @@ -0,0 +1,7 @@ +package org.testcontainers; + +import org.testcontainers.utility.DockerImageName; + +public interface MariaDBTestImages { + DockerImageName MARIADB_IMAGE = DockerImageName.parse("mariadb:10.3.6"); +} diff --git a/modules/mariadb/src/test/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainerTest.java b/modules/mariadb/src/test/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainerTest.java index 30e8cad9c32..b6fb4dd659e 100644 --- a/modules/mariadb/src/test/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainerTest.java +++ b/modules/mariadb/src/test/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainerTest.java @@ -2,6 +2,7 @@ import io.r2dbc.spi.ConnectionFactoryOptions; import org.testcontainers.r2dbc.AbstractR2DBCDatabaseContainerTest; +import org.testcontainers.utility.DockerImageName; public class MariaDBR2DBCDatabaseContainerTest extends AbstractR2DBCDatabaseContainerTest> { @@ -17,7 +18,7 @@ protected String createR2DBCUrl() { @Override protected MariaDBContainer createContainer() { - return new MariaDBContainer<>(); + return new MariaDBContainer<>(DockerImageName.parse("mariadb:10.3.6")); } } diff --git a/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java b/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java index af503ce424b..4ea98bbe1fe 100644 --- a/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java +++ b/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java @@ -13,13 +13,14 @@ import static org.junit.Assume.assumeFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.testcontainers.MariaDBTestImages.MARIADB_IMAGE; public class SimpleMariaDBTest extends AbstractContainerDatabaseTest { @Test public void testSimple() throws SQLException { - try (MariaDBContainer mariadb = new MariaDBContainer<>()) { + try (MariaDBContainer mariadb = new MariaDBContainer<>(MARIADB_IMAGE)) { mariadb.start(); @@ -32,7 +33,7 @@ public void testSimple() throws SQLException { @Test public void testSpecificVersion() throws SQLException { - try (MariaDBContainer mariadbOldVersion = new MariaDBContainer<>("mariadb:5.5.51")) { + try (MariaDBContainer mariadbOldVersion = new MariaDBContainer<>(MARIADB_IMAGE.withTag("5.5.51"))) { mariadbOldVersion.start(); @@ -47,7 +48,7 @@ public void testSpecificVersion() throws SQLException { public void testMariaDBWithCustomIniFile() throws SQLException { assumeFalse(SystemUtils.IS_OS_WINDOWS); - try (MariaDBContainer mariadbCustomConfig = new MariaDBContainer<>("mariadb:10.1.16") + try (MariaDBContainer mariadbCustomConfig = new MariaDBContainer<>(MARIADB_IMAGE.withTag("10.1.16")) .withConfigurationOverride("somepath/mariadb_conf_override")) { mariadbCustomConfig.start(); @@ -61,7 +62,7 @@ public void testMariaDBWithCustomIniFile() throws SQLException { @Test public void testMariaDBWithCommandOverride() throws SQLException { - try (MariaDBContainer mariadbCustomConfig = new MariaDBContainer<>("mariadb:10.1.16") + try (MariaDBContainer mariadbCustomConfig = new MariaDBContainer<>(MARIADB_IMAGE) .withCommand("mysqld --auto_increment_increment=10")) { mariadbCustomConfig.start(); ResultSet resultSet = performQuery(mariadbCustomConfig, "show variables like 'auto_increment_increment'"); @@ -73,7 +74,7 @@ public void testMariaDBWithCommandOverride() throws SQLException { @Test public void testWithAdditionalUrlParamInJdbcUrl() { - MariaDBContainer mariaDBContainer = (MariaDBContainer) new MariaDBContainer() + MariaDBContainer mariaDBContainer = new MariaDBContainer<>(MARIADB_IMAGE) .withUrlParam("connectTimeout", "40000") .withUrlParam("rewriteBatchedStatements", "true"); diff --git a/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java b/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java index e35370f4b27..2aa33931d07 100644 --- a/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java +++ b/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java @@ -1,6 +1,7 @@ package org.testcontainers.containers; import lombok.extern.slf4j.Slf4j; +import org.testcontainers.utility.DockerImageName; @Slf4j public class MockServerContainer extends GenericContainer { @@ -9,12 +10,24 @@ public class MockServerContainer extends GenericContainer { public static final int PORT = 1080; + /** + * @deprecated use {@link MockServerContainer(DockerImageName)} instead + */ + @Deprecated public MockServerContainer() { this(VERSION); } + /** + * @deprecated use {@link MockServerContainer(DockerImageName)} instead + */ + @Deprecated public MockServerContainer(String version) { - super("jamesdbloom/mockserver:mockserver-" + version); + this(DockerImageName.parse("jamesdbloom/mockserver:mockserver-" + version)); + } + + public MockServerContainer(DockerImageName dockerImageName) { + super(dockerImageName); withCommand("-logLevel INFO -serverPort " + PORT); addExposedPorts(PORT); } diff --git a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java index 91f740b4bd8..e4d11ed3f39 100644 --- a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java +++ b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java @@ -4,6 +4,7 @@ import org.junit.Rule; import org.junit.Test; import org.mockserver.client.MockServerClient; +import org.testcontainers.utility.DockerImageName; import java.io.BufferedReader; import java.io.IOException; @@ -19,9 +20,11 @@ public class MockServerContainerTest { + public static final DockerImageName MOCKSERVER_IMAGE = DockerImageName.parse("jamesdbloom/mockserver:mockserver-5.5.4"); + // creatingProxy { @Rule - public MockServerContainer mockServer = new MockServerContainer(); + public MockServerContainer mockServer = new MockServerContainer(MOCKSERVER_IMAGE); // } private static String responseFromMockserver(MockServerContainer mockServer, String path) throws IOException { @@ -52,7 +55,7 @@ public void shouldReturnExpectation() throws Exception { @Test public void shouldCallActualMockserverVersion() throws Exception { String actualVersion = MockServerClient.class.getPackage().getImplementationVersion(); - try (MockServerContainer mockServer = new MockServerContainer(actualVersion)) { + try (MockServerContainer mockServer = new MockServerContainer(MOCKSERVER_IMAGE.withTag("mockserver-" + actualVersion))) { mockServer.start(); String expectedBody = "Hello World!"; diff --git a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java index a7f57d310f2..1527b3924db 100644 --- a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java @@ -5,6 +5,7 @@ import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.utility.DockerImageName; import java.io.IOException; @@ -21,11 +22,23 @@ public class MongoDBContainer extends GenericContainer { private static final String MONGODB_VERSION_DEFAULT = "4.0.10"; private static final String MONGODB_DATABASE_NAME_DEFAULT = "test"; + /** + * @deprecated use {@link MongoDBContainer(DockerImageName)} instead + */ + @Deprecated public MongoDBContainer() { this("mongo:" + MONGODB_VERSION_DEFAULT); } + /** + * @deprecated use {@link MongoDBContainer(DockerImageName)} instead + */ + @Deprecated public MongoDBContainer(@NonNull final String dockerImageName) { + this(DockerImageName.parse(dockerImageName)); + } + + public MongoDBContainer(final DockerImageName dockerImageName) { super(dockerImageName); withExposedPorts(MONGODB_INTERNAL_PORT); withCommand("--replSet", "docker-rs"); diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java index 7c4f4c12408..d637cb07f2d 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java @@ -11,12 +11,14 @@ import com.mongodb.client.TransactionBody; import org.bson.Document; import org.junit.Test; +import org.testcontainers.utility.DockerImageName; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; public class MongoDBContainerTest { + /** * Taken from https://docs.mongodb.com */ @@ -24,7 +26,7 @@ public class MongoDBContainerTest { public void shouldExecuteTransactions() { try ( // creatingMongoDBContainer { - final MongoDBContainer mongoDBContainer = new MongoDBContainer() + final MongoDBContainer mongoDBContainer = new MongoDBContainer(DockerImageName.parse("mongo:4.0.10")) // } ) { diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java index 7550f820d51..0a6d8149e34 100644 --- a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java @@ -1,5 +1,6 @@ package org.testcontainers.containers; +import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.LicenseAcceptance; import java.util.regex.Pattern; @@ -32,11 +33,23 @@ public class MSSQLServerContainer> exten Pattern.compile("[^a-zA-Z0-9]+", Pattern.CASE_INSENSITIVE) }; + /** + * @deprecated use {@link MSSQLServerContainer(DockerImageName)} instead + */ + @Deprecated public MSSQLServerContainer() { this(IMAGE + ":" + DEFAULT_TAG); } + /** + * @deprecated use {@link MSSQLServerContainer(DockerImageName)} instead + */ + @Deprecated public MSSQLServerContainer(final String dockerImageName) { + this(DockerImageName.parse(dockerImageName)); + } + + public MSSQLServerContainer(final DockerImageName dockerImageName) { super(dockerImageName); withStartupTimeoutSeconds(DEFAULT_STARTUP_TIMEOUT_SECONDS); withConnectTimeoutSeconds(DEFAULT_CONNECT_TIMEOUT_SECONDS); diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainerProvider.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainerProvider.java index daaa82ee771..c3495c8a3a6 100644 --- a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainerProvider.java +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainerProvider.java @@ -1,5 +1,7 @@ package org.testcontainers.containers; +import org.testcontainers.utility.DockerImageName; + /** * Factory for MS SQL Server containers. */ @@ -16,6 +18,6 @@ public JdbcDatabaseContainer newInstance() { @Override public JdbcDatabaseContainer newInstance(String tag) { - return new MSSQLServerContainer(MSSQLServerContainer.IMAGE + ":" + tag); + return new MSSQLServerContainer(DockerImageName.parse(MSSQLServerContainer.IMAGE).withTag(tag)); } } diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/MSSQLServerTestImages.java b/modules/mssqlserver/src/test/java/org/testcontainers/MSSQLServerTestImages.java new file mode 100644 index 00000000000..0cec042e19f --- /dev/null +++ b/modules/mssqlserver/src/test/java/org/testcontainers/MSSQLServerTestImages.java @@ -0,0 +1,7 @@ +package org.testcontainers; + +import org.testcontainers.utility.DockerImageName; + +public interface MSSQLServerTestImages { + DockerImageName MSSQL_SERVER_IMAGE = DockerImageName.parse("mcr.microsoft.com/mssql/server:2017-CU12"); +} diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainerTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainerTest.java index 1fcf2dd293b..8c0c9ab1c2b 100644 --- a/modules/mssqlserver/src/test/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainerTest.java +++ b/modules/mssqlserver/src/test/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainerTest.java @@ -1,6 +1,7 @@ package org.testcontainers.containers; import io.r2dbc.spi.ConnectionFactoryOptions; +import org.testcontainers.MSSQLServerTestImages; import org.testcontainers.r2dbc.AbstractR2DBCDatabaseContainerTest; public class MSSQLR2DBCDatabaseContainerTest extends AbstractR2DBCDatabaseContainerTest> { @@ -17,6 +18,6 @@ protected String createR2DBCUrl() { @Override protected MSSQLServerContainer createContainer() { - return new MSSQLServerContainer<>(); + return new MSSQLServerContainer<>(MSSQLServerTestImages.MSSQL_SERVER_IMAGE); } } diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java index 0e1ea05b31d..3df8ff0fa3d 100644 --- a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java +++ b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java @@ -4,6 +4,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import org.testcontainers.MSSQLServerTestImages; import org.testcontainers.containers.MSSQLServerContainer; import java.util.Arrays; @@ -66,7 +67,7 @@ public static Collection data() { @Test public void runPasswordTests() { try { - new MSSQLServerContainer<>().withPassword(this.password); + new MSSQLServerContainer<>(MSSQLServerTestImages.MSSQL_SERVER_IMAGE).withPassword(this.password); if (!valid) fail("Password " + this.password + " is not valid. Expected exception"); } catch (IllegalArgumentException e) { diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomizableMSSQLServerTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomizableMSSQLServerTest.java index ecbc4e31714..d3c387e7b4a 100644 --- a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomizableMSSQLServerTest.java +++ b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomizableMSSQLServerTest.java @@ -3,6 +3,7 @@ import org.junit.Test; import org.testcontainers.containers.MSSQLServerContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; +import org.testcontainers.utility.DockerImageName; import java.sql.ResultSet; import java.sql.SQLException; @@ -15,7 +16,7 @@ public class CustomizableMSSQLServerTest extends AbstractContainerDatabaseTest { @Test public void testSqlServerConnection() throws SQLException { - try (MSSQLServerContainer mssqlServerContainer = new MSSQLServerContainer<>() + try (MSSQLServerContainer mssqlServerContainer = new MSSQLServerContainer<>(DockerImageName.parse("mcr.microsoft.com/mssql/server:2017-CU12")) .withPassword(STRONG_PASSWORD)) { mssqlServerContainer.start(); diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java index 71e12a6d864..100b29b34f7 100644 --- a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java +++ b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java @@ -1,6 +1,7 @@ package org.testcontainers.junit.mssqlserver; import org.junit.Test; +import org.testcontainers.MSSQLServerTestImages; import org.testcontainers.containers.MSSQLServerContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; @@ -12,12 +13,13 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.testcontainers.MSSQLServerTestImages.MSSQL_SERVER_IMAGE; public class SimpleMSSQLServerTest extends AbstractContainerDatabaseTest { @Test public void testSimple() throws SQLException { - try (MSSQLServerContainer mssqlServer = new MSSQLServerContainer<>()) { + try (MSSQLServerContainer mssqlServer = new MSSQLServerContainer<>(MSSQL_SERVER_IMAGE)) { mssqlServer.start(); ResultSet resultSet = performQuery(mssqlServer, "SELECT 1"); @@ -28,7 +30,7 @@ public void testSimple() throws SQLException { @Test public void testWithAdditionalUrlParamInJdbcUrl() { - try (MSSQLServerContainer mssqlServer = new MSSQLServerContainer<>() + try (MSSQLServerContainer mssqlServer = new MSSQLServerContainer<>(MSSQL_SERVER_IMAGE) .withUrlParam("integratedSecurity", "false") .withUrlParam("applicationName", "MyApp")) { @@ -42,7 +44,7 @@ public void testWithAdditionalUrlParamInJdbcUrl() { @Test public void testSetupDatabase() throws SQLException { - try (MSSQLServerContainer mssqlServer = new MSSQLServerContainer<>()) { + try (MSSQLServerContainer mssqlServer = new MSSQLServerContainer<>(MSSQL_SERVER_IMAGE)) { mssqlServer.start(); DataSource ds = getDataSource(mssqlServer); Statement statement = ds.getConnection().createStatement(); diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java index 1ef574cbd5e..35321db5d72 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java @@ -1,12 +1,11 @@ package org.testcontainers.containers; import org.jetbrains.annotations.NotNull; +import org.testcontainers.utility.DockerImageName; import java.util.HashSet; import java.util.Set; -import static java.util.stream.Collectors.joining; - /** * @author richardnorth */ @@ -27,15 +26,28 @@ public class MySQLContainer> extends JdbcDatab private String password = DEFAULT_PASSWORD; private static final String MYSQL_ROOT_USER = "root"; + /** + * @deprecated use {@link MySQLContainer(DockerImageName)} instead + */ + @Deprecated public MySQLContainer() { this(IMAGE + ":" + DEFAULT_TAG); } + /** + * @deprecated use {@link MySQLContainer(DockerImageName)} instead + */ + @Deprecated public MySQLContainer(String dockerImageName) { + this(DockerImageName.parse(dockerImageName)); + } + + public MySQLContainer(final DockerImageName dockerImageName) { super(dockerImageName); addExposedPort(MYSQL_PORT); } + @NotNull @Override protected Set getLivenessCheckPorts() { diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainerProvider.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainerProvider.java index 7d4919f4b26..18d126635b1 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainerProvider.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainerProvider.java @@ -1,8 +1,7 @@ package org.testcontainers.containers; import org.testcontainers.jdbc.ConnectionUrl; - -import java.util.Objects; +import org.testcontainers.utility.DockerImageName; /** * Factory for MySQL containers. @@ -26,7 +25,7 @@ public JdbcDatabaseContainer newInstance() { @Override public JdbcDatabaseContainer newInstance(String tag) { if (tag != null) { - return new MySQLContainer(MySQLContainer.IMAGE + ":" + tag); + return new MySQLContainer(DockerImageName.parse(MySQLContainer.IMAGE).withTag(tag)); } else { return newInstance(); } diff --git a/modules/mysql/src/test/java/org/testcontainers/MySQLTestImages.java b/modules/mysql/src/test/java/org/testcontainers/MySQLTestImages.java new file mode 100644 index 00000000000..6c8c6690d1f --- /dev/null +++ b/modules/mysql/src/test/java/org/testcontainers/MySQLTestImages.java @@ -0,0 +1,9 @@ +package org.testcontainers; + +import org.testcontainers.utility.DockerImageName; + +public class MySQLTestImages { + public static final DockerImageName MYSQL_IMAGE = DockerImageName.parse("mysql:5.7.22"); + public static final DockerImageName MYSQL_55_IMAGE = DockerImageName.parse("mysql:5.5"); + public static final DockerImageName MYSQL_56_IMAGE = DockerImageName.parse("mysql:5.6"); +} diff --git a/modules/mysql/src/test/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerTest.java b/modules/mysql/src/test/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerTest.java index 19edc3dbca4..38abf637c3a 100644 --- a/modules/mysql/src/test/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerTest.java @@ -1,6 +1,7 @@ package org.testcontainers.containers; import io.r2dbc.spi.ConnectionFactoryOptions; +import org.testcontainers.MySQLTestImages; import org.testcontainers.r2dbc.AbstractR2DBCDatabaseContainerTest; public class MySQLR2DBCDatabaseContainerTest extends AbstractR2DBCDatabaseContainerTest> { @@ -17,7 +18,7 @@ protected String createR2DBCUrl() { @Override protected MySQLContainer createContainer() { - return new MySQLContainer<>(); + return new MySQLContainer<>(MySQLTestImages.MYSQL_IMAGE); } } diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java index 590d288e3fe..708b2b987cd 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java @@ -1,6 +1,7 @@ package org.testcontainers.junit.mysql; import org.junit.Test; +import org.testcontainers.MySQLTestImages; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; @@ -17,7 +18,7 @@ public class CustomizableMysqlTest extends AbstractContainerDatabaseTest { @Test public void testSimple() throws SQLException { // Add MYSQL_ROOT_HOST environment so that we can root login from anywhere for testing purposes - try (MySQLContainer mysql = new MySQLContainer<>("mysql:5.5") + try (MySQLContainer mysql = new MySQLContainer<>(MySQLTestImages.MYSQL_IMAGE) .withDatabaseName(DB_NAME) .withUsername(USER) .withPassword(PWD) diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java index 3c0c86ffafa..b8f1a136d9a 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java @@ -3,8 +3,10 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import org.testcontainers.MySQLTestImages; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; +import org.testcontainers.utility.DockerImageName; import java.sql.ResultSet; import java.sql.SQLException; @@ -27,7 +29,10 @@ public MultiVersionMySQLTest(String version) { @Test public void versionCheckTest() throws SQLException { - try (MySQLContainer mysql = new MySQLContainer<>("mysql:" + version)) { + + final DockerImageName dockerImageName = MySQLTestImages.MYSQL_IMAGE.withTag(version); + + try (MySQLContainer mysql = new MySQLContainer<>(dockerImageName)) { mysql.start(); final ResultSet resultSet = performQuery(mysql, "SELECT VERSION()"); final String resultSetString = resultSet.getString(1); diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java index 6d6f8d26453..f0ceeb92a48 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java @@ -20,10 +20,13 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; import static org.junit.Assume.assumeFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.rnorth.visibleassertions.VisibleAssertions.fail; +import static org.testcontainers.MySQLTestImages.MYSQL_55_IMAGE; +import static org.testcontainers.MySQLTestImages.MYSQL_56_IMAGE; +import static org.testcontainers.MySQLTestImages.MYSQL_IMAGE; public class SimpleMySQLTest extends AbstractContainerDatabaseTest { @@ -37,19 +40,19 @@ public class SimpleMySQLTest extends AbstractContainerDatabaseTest { */ /* @ClassRule - public static MySQLContainer mysql = new MySQLContainer<>(); + public static MySQLContainer mysql = new MySQLContainer<>(MYSQL_IMAGE); @ClassRule - public static MySQLContainer mysqlOldVersion = new MySQLContainer<>("mysql:5.5"); + public static MySQLContainer mysqlOldVersion = new MySQLContainer<>(DockerImageName.parse("mysql:5.5");) @ClassRule - public static MySQLContainer mysqlCustomConfig = new MySQLContainer<>("mysql:5.6") + public static MySQLContainer mysqlCustomConfig = new MySQLContainer<>(DockerImageName.parse("mysql:5.6")) .withConfigurationOverride("somepath/mysql_conf_override"); */ @Test public void testSimple() throws SQLException { - try (MySQLContainer mysql = new MySQLContainer<>() + try (MySQLContainer mysql = new MySQLContainer<>(MYSQL_IMAGE) .withConfigurationOverride("somepath/mysql_conf_override") .withLogConsumer(new Slf4jLogConsumer(logger))) { @@ -64,7 +67,7 @@ public void testSimple() throws SQLException { @Test public void testSpecificVersion() throws SQLException { - try (MySQLContainer mysqlOldVersion = new MySQLContainer<>("mysql:5.5") + try (MySQLContainer mysqlOldVersion = new MySQLContainer<>(MYSQL_55_IMAGE) .withConfigurationOverride("somepath/mysql_conf_override") .withLogConsumer(new Slf4jLogConsumer(logger))) { @@ -81,7 +84,7 @@ public void testSpecificVersion() throws SQLException { public void testMySQLWithCustomIniFile() throws SQLException { assumeFalse(SystemUtils.IS_OS_WINDOWS); - try (MySQLContainer mysqlCustomConfig = new MySQLContainer<>("mysql:5.6") + try (MySQLContainer mysqlCustomConfig = new MySQLContainer<>(MYSQL_56_IMAGE) .withConfigurationOverride("somepath/mysql_conf_override")) { mysqlCustomConfig.start(); @@ -95,7 +98,7 @@ public void testMySQLWithCustomIniFile() throws SQLException { @Test public void testCommandOverride() throws SQLException { - try (MySQLContainer mysqlCustomConfig = new MySQLContainer<>() + try (MySQLContainer mysqlCustomConfig = new MySQLContainer<>(MYSQL_IMAGE) .withCommand("mysqld --auto_increment_increment=42")) { mysqlCustomConfig.start(); @@ -109,7 +112,7 @@ public void testCommandOverride() throws SQLException { @Test public void testExplicitInitScript() throws SQLException { - try (MySQLContainer container = new MySQLContainer<>() + try (MySQLContainer container = new MySQLContainer<>(MYSQL_IMAGE) .withInitScript("somepath/init_mysql.sql") .withLogConsumer(new Slf4jLogConsumer(logger))) { container.start(); @@ -123,7 +126,7 @@ public void testExplicitInitScript() throws SQLException { @Test(expected = ContainerLaunchException.class) public void testEmptyPasswordWithNonRootUser() { - try (MySQLContainer container = new MySQLContainer<>("mysql:5.5") + try (MySQLContainer container = new MySQLContainer<>(MYSQL_55_IMAGE) .withDatabaseName("TEST") .withUsername("test") .withPassword("") @@ -136,7 +139,7 @@ public void testEmptyPasswordWithNonRootUser() { @Test public void testEmptyPasswordWithRootUser() throws SQLException { // Add MYSQL_ROOT_HOST environment so that we can root login from anywhere for testing purposes - try (MySQLContainer mysql = new MySQLContainer<>("mysql:5.5") + try (MySQLContainer mysql = new MySQLContainer<>(MYSQL_55_IMAGE) .withDatabaseName("foo") .withUsername("root") .withPassword("") @@ -153,7 +156,7 @@ public void testEmptyPasswordWithRootUser() throws SQLException { @Test public void testWithAdditionalUrlParamTimeZone() throws SQLException { - MySQLContainer mysql = (MySQLContainer) new MySQLContainer() + MySQLContainer mysql = new MySQLContainer<>(MYSQL_IMAGE) .withUrlParam("serverTimezone", "Europe/Zurich") .withEnv("TZ", "Europe/Zurich") .withLogConsumer(new Slf4jLogConsumer(logger)); @@ -183,7 +186,7 @@ public void testWithAdditionalUrlParamTimeZone() throws SQLException { @Test public void testWithAdditionalUrlParamMultiQueries() throws SQLException { - MySQLContainer mysql = (MySQLContainer) new MySQLContainer() + MySQLContainer mysql = new MySQLContainer<>(MYSQL_IMAGE) .withUrlParam("allowMultiQueries", "true") .withLogConsumer(new Slf4jLogConsumer(logger)); mysql.start(); @@ -207,7 +210,7 @@ public void testWithAdditionalUrlParamMultiQueries() throws SQLException { @Test public void testWithAdditionalUrlParamInJdbcUrl() { - MySQLContainer mysql = (MySQLContainer) new MySQLContainer() + MySQLContainer mysql = new MySQLContainer<>(MYSQL_IMAGE) .withUrlParam("allowMultiQueries", "true") .withUrlParam("rewriteBatchedStatements", "true") .withLogConsumer(new Slf4jLogConsumer(logger)); diff --git a/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java index 10545da43d6..5d4212ccacd 100644 --- a/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java +++ b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java @@ -1,19 +1,20 @@ package org.testcontainers.containers; -import static java.net.HttpURLConnection.*; -import static java.util.stream.Collectors.*; - -import java.time.Duration; -import java.util.Set; -import java.util.stream.Stream; - import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.containers.wait.strategy.WaitAllStrategy; import org.testcontainers.containers.wait.strategy.WaitStrategy; +import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.LicenseAcceptance; import org.testcontainers.utility.MountableFile; +import java.time.Duration; +import java.util.Set; +import java.util.stream.Stream; + +import static java.net.HttpURLConnection.HTTP_OK; +import static java.util.stream.Collectors.toSet; + /** * Testcontainer for Neo4j. * @@ -58,23 +59,29 @@ public class Neo4jContainer> extends GenericContaine private String adminPassword = DEFAULT_ADMIN_PASSWORD; - private boolean defaultImage = false; + private boolean standardImage = false; /** * Creates a Testcontainer using the official Neo4j docker image. + * @deprecated use {@link Neo4jContainer(DockerImageName)} instead */ + @Deprecated public Neo4jContainer() { this(DOCKER_IMAGE_NAME); - - this.defaultImage = true; } /** * Creates a Testcontainer using a specific docker image. * * @param dockerImageName The docker image to use. + * @deprecated use {@link Neo4jContainer(DockerImageName)} instead */ + @Deprecated public Neo4jContainer(String dockerImageName) { + this(DockerImageName.parse(dockerImageName)); + } + + public Neo4jContainer(final DockerImageName dockerImageName) { super(dockerImageName); WaitStrategy waitForBolt = new LogMessageWaitStrategy() @@ -89,6 +96,10 @@ public Neo4jContainer(String dockerImageName) { .withStartupTimeout(Duration.ofMinutes(2)); addExposedPorts(DEFAULT_BOLT_PORT, DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT); + + if (dockerImageName.getUnversionedPart().equals(DEFAULT_IMAGE_NAME)) { + this.standardImage = true; + } } @Override @@ -139,7 +150,7 @@ public String getHttpsUrl() { */ public S withEnterpriseEdition() { - if (!defaultImage) { + if (!standardImage) { throw new IllegalStateException( String.format("Cannot use enterprise version with alternative image %s.", getDockerImageName())); } diff --git a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerJUnitIntegrationTest.java b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerJUnitIntegrationTest.java index 96b7a48b96f..5a67674cbe8 100644 --- a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerJUnitIntegrationTest.java +++ b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerJUnitIntegrationTest.java @@ -1,9 +1,5 @@ package org.testcontainers.containers; -import static org.assertj.core.api.Assertions.*; - -import java.util.Collections; - import org.junit.ClassRule; import org.junit.Test; import org.neo4j.driver.v1.AuthTokens; @@ -11,6 +7,11 @@ import org.neo4j.driver.v1.GraphDatabase; import org.neo4j.driver.v1.Session; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; + /** * Test for basic functionality when used as a @ClassRule. * @@ -19,7 +20,7 @@ public class Neo4jContainerJUnitIntegrationTest { @ClassRule - public static Neo4jContainer neo4jContainer = new Neo4jContainer(); + public static Neo4jContainer neo4jContainer = new Neo4jContainer<>(Neo4jTestImages.NEO4J_TEST_IMAGE); @Test public void shouldStart() { diff --git a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java index cbbe5cb5729..6a32dac0af0 100644 --- a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java +++ b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java @@ -1,10 +1,5 @@ package org.testcontainers.containers; -import static org.assertj.core.api.Assertions.*; -import static org.assertj.core.api.Assumptions.*; - -import java.util.Collections; - import org.junit.Test; import org.neo4j.driver.v1.AuthToken; import org.neo4j.driver.v1.AuthTokens; @@ -15,6 +10,12 @@ import org.neo4j.driver.v1.StatementResult; import org.testcontainers.utility.MountableFile; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assumptions.assumeThat; + /** * Tests of functionality special to the Neo4jContainer. * @@ -29,7 +30,7 @@ public class Neo4jContainerTest { public void shouldDisableAuthentication() { try ( - Neo4jContainer neo4jContainer = new Neo4jContainer().withoutAuthentication(); + Neo4jContainer neo4jContainer = new Neo4jContainer<>(Neo4jTestImages.NEO4J_TEST_IMAGE).withoutAuthentication() ) { neo4jContainer.start(); try (Driver driver = getDriver(neo4jContainer); @@ -44,8 +45,8 @@ public void shouldDisableAuthentication() { @Test public void shouldCopyDatabase() { try ( - Neo4jContainer neo4jContainer = new Neo4jContainer() - .withDatabase(MountableFile.forClasspathResource("/test-graph.db")); + Neo4jContainer neo4jContainer = new Neo4jContainer<>(Neo4jTestImages.NEO4J_TEST_IMAGE) + .withDatabase(MountableFile.forClasspathResource("/test-graph.db")) ) { neo4jContainer.start(); try ( @@ -62,8 +63,8 @@ public void shouldCopyDatabase() { @Test public void shouldCopyPlugins() { try ( - Neo4jContainer neo4jContainer = new Neo4jContainer() - .withPlugins(MountableFile.forClasspathResource("/custom-plugins")); + Neo4jContainer neo4jContainer = new Neo4jContainer<>(Neo4jTestImages.NEO4J_TEST_IMAGE) + .withPlugins(MountableFile.forClasspathResource("/custom-plugins")) ) { neo4jContainer.start(); try ( @@ -78,8 +79,8 @@ public void shouldCopyPlugins() { @Test public void shouldCopyPlugin() { try ( - Neo4jContainer neo4jContainer = new Neo4jContainer() - .withPlugins(MountableFile.forClasspathResource("/custom-plugins/hello-world.jar")); + Neo4jContainer neo4jContainer = new Neo4jContainer<>(Neo4jTestImages.NEO4J_TEST_IMAGE) + .withPlugins(MountableFile.forClasspathResource("/custom-plugins/hello-world.jar")) ) { neo4jContainer.start(); try ( @@ -105,7 +106,7 @@ public void shouldCheckEnterpriseLicense() { String expectedImageName = "neo4j:3.5.0-enterprise"; assertThatExceptionOfType(IllegalStateException.class) - .isThrownBy(() -> new Neo4jContainer().withEnterpriseEdition()) + .isThrownBy(() -> new Neo4jContainer<>(Neo4jTestImages.NEO4J_TEST_IMAGE).withEnterpriseEdition()) .withMessageContaining("The image " + expectedImageName + " requires you to accept a license agreement."); } @@ -114,7 +115,7 @@ public void shouldRunEnterprise() { assumeThat(Neo4jContainerTest.class.getResource(ACCEPTANCE_FILE_LOCATION)).isNotNull(); try ( - Neo4jContainer neo4jContainer = new Neo4jContainer() + Neo4jContainer neo4jContainer = new Neo4jContainer<>(Neo4jTestImages.NEO4J_TEST_IMAGE) .withEnterpriseEdition() .withAdminPassword("Picard123") ) { @@ -134,7 +135,7 @@ public void shouldRunEnterprise() { @Test public void shouldAddConfigToEnvironment() { - Neo4jContainer neo4jContainer = new Neo4jContainer() + Neo4jContainer neo4jContainer = new Neo4jContainer<>(Neo4jTestImages.NEO4J_TEST_IMAGE) .withNeo4jConfig("dbms.security.procedures.unrestricted", "apoc.*,algo.*") .withNeo4jConfig("dbms.tx_log.rotation.size", "42M"); @@ -144,7 +145,7 @@ public void shouldAddConfigToEnvironment() { .containsEntry("NEO4J_dbms_tx__log_rotation_size", "42M"); } - private static Driver getDriver(Neo4jContainer container) { + private static Driver getDriver(Neo4jContainer container) { AuthToken authToken = AuthTokens.none(); if (container.getAdminPassword() != null) { diff --git a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jTestImages.java b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jTestImages.java new file mode 100644 index 00000000000..2cab24baf2b --- /dev/null +++ b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jTestImages.java @@ -0,0 +1,7 @@ +package org.testcontainers.containers; + +import org.testcontainers.utility.DockerImageName; + +public interface Neo4jTestImages { + DockerImageName NEO4J_TEST_IMAGE = DockerImageName.parse("neo4j:3.5.0"); +} diff --git a/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java b/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java index fb931bce409..75a4571b68e 100644 --- a/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java +++ b/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java @@ -2,6 +2,7 @@ import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.traits.LinkableContainer; +import org.testcontainers.utility.DockerImageName; import java.net.MalformedURLException; import java.net.URL; @@ -15,17 +16,29 @@ public class NginxContainer> extends GenericCo private static final int NGINX_DEFAULT_PORT = 80; + /** + * @deprecated use {@link NginxContainer(DockerImageName)} instead + */ + @Deprecated public NginxContainer() { this("nginx:1.9.4"); } + /** + * @deprecated use {@link NginxContainer(DockerImageName)} instead + */ + @Deprecated public NginxContainer(String dockerImageName) { - super(dockerImageName); + this(DockerImageName.parse(dockerImageName)); addExposedPort(NGINX_DEFAULT_PORT); setCommand("nginx", "-g", "daemon off;"); } + public NginxContainer(final DockerImageName dockerImageName) { + super(dockerImageName); + } + @NotNull @Override protected Set getLivenessCheckPorts() { diff --git a/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java b/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java index b41a0d8361e..e322804bf75 100644 --- a/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java +++ b/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java @@ -6,6 +6,7 @@ import org.junit.Test; import org.testcontainers.containers.NginxContainer; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; +import org.testcontainers.utility.DockerImageName; import java.io.*; import java.net.URL; @@ -19,11 +20,13 @@ */ public class SimpleNginxTest { + private static final DockerImageName NGINX_IMAGE = DockerImageName.parse("nginx:1.9.4"); + private static String tmpDirectory = System.getProperty("user.home") + "/.tmp-test-container"; // creatingContainer { @Rule - public NginxContainer nginx = new NginxContainer<>() + public NginxContainer nginx = new NginxContainer<>(NGINX_IMAGE) .withCustomContent(tmpDirectory) .waitingFor(new HttpWaitStrategy()); // } diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java index d263a141d4f..8406d8275fb 100644 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java @@ -1,5 +1,6 @@ package org.testcontainers.containers; +import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.TestcontainersConfiguration; import java.util.concurrent.Future; @@ -32,11 +33,23 @@ private static String resolveImageName() { return image; } + /** + * @deprecated use {@link OracleContainer(DockerImageName)} instead + */ + @Deprecated public OracleContainer() { this(resolveImageName()); } + /** + * @deprecated use {@link OracleContainer(DockerImageName)} instead + */ + @Deprecated public OracleContainer(String dockerImageName) { + this(DockerImageName.parse(dockerImageName)); + } + + public OracleContainer(final DockerImageName dockerImageName) { super(dockerImageName); preconfigure(); } diff --git a/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java b/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java index b7fcc85573a..797f73f7159 100644 --- a/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java +++ b/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java @@ -13,6 +13,7 @@ import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.containers.wait.strategy.WaitAllStrategy; import org.testcontainers.containers.wait.strategy.WaitStrategy; +import org.testcontainers.utility.DockerImageName; import java.io.IOException; import java.net.URL; @@ -48,11 +49,23 @@ public class OrientDBContainer extends GenericContainer { private OrientDB orientDB; private ODatabaseSession session; + /** + * @deprecated use {@link OrientDBContainer(DockerImageName)} instead + */ + @Deprecated public OrientDBContainer() { this(DOCKER_IMAGE_NAME); } + /** + * @deprecated use {@link OrientDBContainer(DockerImageName)} instead + */ + @Deprecated public OrientDBContainer(@NonNull String dockerImageName) { + this(DockerImageName.parse(dockerImageName)); + } + + public OrientDBContainer(final DockerImageName dockerImageName) { super(dockerImageName); serverPassword = DEFAULT_SERVER_PASSWORD; diff --git a/modules/orientdb/src/test/java/org/testcontainers/containers/OrientDBContainerTest.java b/modules/orientdb/src/test/java/org/testcontainers/containers/OrientDBContainerTest.java index 54b0661cae2..60d7b172436 100644 --- a/modules/orientdb/src/test/java/org/testcontainers/containers/OrientDBContainerTest.java +++ b/modules/orientdb/src/test/java/org/testcontainers/containers/OrientDBContainerTest.java @@ -2,6 +2,7 @@ import com.orientechnologies.orient.core.db.ODatabaseSession; import org.junit.Test; +import org.testcontainers.utility.DockerImageName; import static org.assertj.core.api.Assertions.*; @@ -10,9 +11,11 @@ */ public class OrientDBContainerTest { + private static final DockerImageName ORIENTDB_IMAGE = DockerImageName.parse("orientdb:3.0.24-tp3"); + @Test public void shouldReturnTheSameSession() { - try (OrientDBContainer container = new OrientDBContainer()) { + try (OrientDBContainer container = new OrientDBContainer(ORIENTDB_IMAGE)) { container.start(); final ODatabaseSession session = container.getSession(); @@ -24,7 +27,7 @@ public void shouldReturnTheSameSession() { @Test public void shouldInitializeWithCommands() { - try (OrientDBContainer container = new OrientDBContainer()) { + try (OrientDBContainer container = new OrientDBContainer(ORIENTDB_IMAGE)) { container.start(); final ODatabaseSession session = container.getSession(); @@ -40,7 +43,7 @@ public void shouldInitializeWithCommands() { @Test public void shouldQueryWithGremlin() { - try (OrientDBContainer container = new OrientDBContainer()) { + try (OrientDBContainer container = new OrientDBContainer(ORIENTDB_IMAGE)) { container.start(); final ODatabaseSession session = container.getSession("admin", "admin"); @@ -56,7 +59,7 @@ public void shouldQueryWithGremlin() { @Test public void shouldInitializeDatabaseFromScript() { - try (OrientDBContainer container = new OrientDBContainer() + try (OrientDBContainer container = new OrientDBContainer(ORIENTDB_IMAGE) .withScriptPath("initscript.osql") .withDatabaseName("persons")) { diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java index 54c87a84648..9e59787837b 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java @@ -1,8 +1,7 @@ package org.testcontainers.containers; import org.testcontainers.jdbc.ConnectionUrl; - -import java.util.Objects; +import org.testcontainers.utility.DockerImageName; /** * Factory for PostGIS containers, which are a special flavour of PostgreSQL. @@ -28,7 +27,7 @@ public JdbcDatabaseContainer newInstance() { @Override public JdbcDatabaseContainer newInstance(String tag) { - return new PostgreSQLContainer(DEFAULT_IMAGE + ":" + tag); + return new PostgreSQLContainer(DockerImageName.parse(DEFAULT_IMAGE).withTag(DEFAULT_TAG)); } @Override diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index 1c814044023..b1ddb2a26ee 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -2,6 +2,7 @@ import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; +import org.testcontainers.utility.DockerImageName; import java.time.Duration; import java.util.HashSet; @@ -31,11 +32,23 @@ public class PostgreSQLContainer> extends private static final String QUERY_PARAM_SEPARATOR = "&"; + /** + * @deprecated use {@link PostgreSQLContainer(DockerImageName)} instead + */ + @Deprecated public PostgreSQLContainer() { this(IMAGE + ":" + DEFAULT_TAG); } + /** + * @deprecated use {@link PostgreSQLContainer(DockerImageName)} instead + */ + @Deprecated public PostgreSQLContainer(final String dockerImageName) { + this(DockerImageName.parse(dockerImageName)); + } + + public PostgreSQLContainer(final DockerImageName dockerImageName) { super(dockerImageName); this.waitStrategy = new LogMessageWaitStrategy() .withRegEx(".*database system is ready to accept connections.*\\s") diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainerProvider.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainerProvider.java index 77a0329c22c..5e7580c75eb 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainerProvider.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainerProvider.java @@ -1,8 +1,7 @@ package org.testcontainers.containers; import org.testcontainers.jdbc.ConnectionUrl; - -import java.util.Objects; +import org.testcontainers.utility.DockerImageName; /** * Factory for PostgreSQL containers. @@ -24,7 +23,7 @@ public JdbcDatabaseContainer newInstance() { @Override public JdbcDatabaseContainer newInstance(String tag) { - return new PostgreSQLContainer(PostgreSQLContainer.IMAGE + ":" + tag); + return new PostgreSQLContainer(DockerImageName.parse(PostgreSQLContainer.IMAGE).withTag(tag)); } @Override diff --git a/modules/postgresql/src/test/java/org/testcontainers/PostgreSQLTestImages.java b/modules/postgresql/src/test/java/org/testcontainers/PostgreSQLTestImages.java new file mode 100644 index 00000000000..8be01053570 --- /dev/null +++ b/modules/postgresql/src/test/java/org/testcontainers/PostgreSQLTestImages.java @@ -0,0 +1,7 @@ +package org.testcontainers; + +import org.testcontainers.utility.DockerImageName; + +public interface PostgreSQLTestImages { + DockerImageName POSTGRES_TEST_IMAGE = DockerImageName.parse("postgres:9.6.12"); +} diff --git a/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java index c0ba7e40d44..c18f19a109e 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java @@ -1,6 +1,7 @@ package org.testcontainers.containers; import org.junit.Test; +import org.testcontainers.PostgreSQLTestImages; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; @@ -46,6 +47,9 @@ public void shouldRejectInvalidQueryString() { } static class FixedJdbcUrlPostgreSQLContainer extends PostgreSQLContainer { + public FixedJdbcUrlPostgreSQLContainer() { + super(PostgreSQLTestImages.POSTGRES_TEST_IMAGE); + } @Override public String getHost() { @@ -59,6 +63,9 @@ public Integer getMappedPort(int originalPort) { } static class NoParamsUrlPostgreSQLContainer extends PostgreSQLContainer { + public NoParamsUrlPostgreSQLContainer() { + super(PostgreSQLTestImages.POSTGRES_TEST_IMAGE); + } @Override public String getJdbcUrl() { diff --git a/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainerTest.java b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainerTest.java index 505b37859ba..46e439aaf66 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainerTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainerTest.java @@ -1,13 +1,14 @@ package org.testcontainers.containers; import io.r2dbc.spi.ConnectionFactoryOptions; +import org.testcontainers.PostgreSQLTestImages; import org.testcontainers.r2dbc.AbstractR2DBCDatabaseContainerTest; public class PostgreSQLR2DBCDatabaseContainerTest extends AbstractR2DBCDatabaseContainerTest> { @Override protected PostgreSQLContainer createContainer() { - return new PostgreSQLContainer<>(); + return new PostgreSQLContainer<>(PostgreSQLTestImages.POSTGRES_TEST_IMAGE); } @Override diff --git a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/CustomizablePostgreSQLTest.java b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/CustomizablePostgreSQLTest.java index 8446440059e..8fd523434dd 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/CustomizablePostgreSQLTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/CustomizablePostgreSQLTest.java @@ -1,6 +1,7 @@ package org.testcontainers.junit.postgresql; import org.junit.Test; +import org.testcontainers.PostgreSQLTestImages; import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; @@ -19,7 +20,7 @@ public class CustomizablePostgreSQLTest extends AbstractContainerDatabaseTest { @Test public void testSimple() throws SQLException { - try (PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:9.6.8") + try (PostgreSQLContainer postgres = new PostgreSQLContainer<>(PostgreSQLTestImages.POSTGRES_TEST_IMAGE) .withDatabaseName(DB_NAME) .withUsername(USER) .withPassword(PWD)) { diff --git a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java index 36ece03b604..95e1fdf10fd 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java @@ -13,6 +13,7 @@ import static org.junit.Assert.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; +import static org.testcontainers.PostgreSQLTestImages.POSTGRES_TEST_IMAGE; public class SimplePostgreSQLTest extends AbstractContainerDatabaseTest { @@ -23,7 +24,7 @@ public class SimplePostgreSQLTest extends AbstractContainerDatabaseTest { @Test public void testSimple() throws SQLException { - try (PostgreSQLContainer postgres = new PostgreSQLContainer<>()) { + try (PostgreSQLContainer postgres = new PostgreSQLContainer<>(POSTGRES_TEST_IMAGE)) { postgres.start(); ResultSet resultSet = performQuery(postgres, "SELECT 1"); @@ -34,7 +35,7 @@ public void testSimple() throws SQLException { @Test public void testCommandOverride() throws SQLException { - try (PostgreSQLContainer postgres = new PostgreSQLContainer<>().withCommand("postgres -c max_connections=42")) { + try (PostgreSQLContainer postgres = new PostgreSQLContainer<>(POSTGRES_TEST_IMAGE).withCommand("postgres -c max_connections=42")) { postgres.start(); ResultSet resultSet = performQuery(postgres, "SELECT current_setting('max_connections')"); @@ -45,7 +46,7 @@ public void testCommandOverride() throws SQLException { @Test public void testUnsetCommand() throws SQLException { - try (PostgreSQLContainer postgres = new PostgreSQLContainer<>().withCommand("postgres -c max_connections=42").withCommand()) { + try (PostgreSQLContainer postgres = new PostgreSQLContainer<>(POSTGRES_TEST_IMAGE).withCommand("postgres -c max_connections=42").withCommand()) { postgres.start(); ResultSet resultSet = performQuery(postgres, "SELECT current_setting('max_connections')"); @@ -56,7 +57,7 @@ public void testUnsetCommand() throws SQLException { @Test public void testExplicitInitScript() throws SQLException { - try (PostgreSQLContainer postgres = new PostgreSQLContainer<>().withInitScript("somepath/init_postgresql.sql")) { + try (PostgreSQLContainer postgres = new PostgreSQLContainer<>(POSTGRES_TEST_IMAGE).withInitScript("somepath/init_postgresql.sql")) { postgres.start(); ResultSet resultSet = performQuery(postgres, "SELECT foo FROM bar"); @@ -68,7 +69,7 @@ public void testExplicitInitScript() throws SQLException { @Test public void testWithAdditionalUrlParamInJdbcUrl() { - try (PostgreSQLContainer postgres = new PostgreSQLContainer<>() + try (PostgreSQLContainer postgres = new PostgreSQLContainer<>(POSTGRES_TEST_IMAGE) .withUrlParam("charSet", "UNICODE")) { postgres.start(); diff --git a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java index c3f25c40ac8..1c8c03ce4de 100644 --- a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java +++ b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java @@ -2,6 +2,7 @@ import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; +import org.testcontainers.utility.DockerImageName; import java.sql.Connection; import java.sql.SQLException; @@ -23,11 +24,23 @@ public class PrestoContainer> extends JdbcDat private String username = "test"; private String catalog = null; + /** + * @deprecated use {@link PrestoContainer(DockerImageName)} instead + */ + @Deprecated public PrestoContainer() { this(IMAGE + ":" + DEFAULT_TAG); } + /** + * @deprecated use {@link PrestoContainer(DockerImageName)} instead + */ + @Deprecated public PrestoContainer(final String dockerImageName) { + this(DockerImageName.parse(dockerImageName)); + } + + public PrestoContainer(final DockerImageName dockerImageName) { super(dockerImageName); this.waitStrategy = new LogMessageWaitStrategy() .withRegEx(".*======== SERVER STARTED ========.*") diff --git a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainerProvider.java b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainerProvider.java index 663cf9be4a9..8408aab4517 100644 --- a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainerProvider.java +++ b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainerProvider.java @@ -1,8 +1,7 @@ package org.testcontainers.containers; import org.testcontainers.jdbc.ConnectionUrl; - -import java.util.Objects; +import org.testcontainers.utility.DockerImageName; /** * Factory for Presto containers. @@ -24,7 +23,7 @@ public JdbcDatabaseContainer newInstance() { @Override public JdbcDatabaseContainer newInstance(String tag) { - return new PrestoContainer(PrestoContainer.IMAGE + ":" + tag); + return new PrestoContainer(DockerImageName.parse(PrestoContainer.IMAGE).withTag(tag)); } @Override diff --git a/modules/presto/src/test/java/org/testcontainers/PrestoTestImages.java b/modules/presto/src/test/java/org/testcontainers/PrestoTestImages.java new file mode 100644 index 00000000000..1b4e449c99f --- /dev/null +++ b/modules/presto/src/test/java/org/testcontainers/PrestoTestImages.java @@ -0,0 +1,8 @@ +package org.testcontainers; + +import org.testcontainers.utility.DockerImageName; + +public interface PrestoTestImages { + DockerImageName PRESTO_TEST_IMAGE = DockerImageName.parse("prestosql/presto:329"); + DockerImageName PRESTO_PREVIOUS_VERSION_TEST_IMAGE = DockerImageName.parse("prestosql/presto:328"); +} diff --git a/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java b/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java index 07fb6fa30ed..d5a4d9587a6 100644 --- a/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java +++ b/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java @@ -2,6 +2,8 @@ import org.junit.Assert; import org.junit.Test; +import org.testcontainers.PrestoTestImages; +import org.testcontainers.utility.DockerImageName; import java.sql.Connection; import java.sql.DriverManager; @@ -22,9 +24,10 @@ * @author findepi */ public class PrestoContainerTest { + @Test public void testSimple() throws Exception { - try (PrestoContainer prestoSql = new PrestoContainer<>()) { + try (PrestoContainer prestoSql = new PrestoContainer<>(PrestoTestImages.PRESTO_TEST_IMAGE)) { prestoSql.start(); try (Connection connection = prestoSql.createConnection(); Statement statement = connection.createStatement(); @@ -37,21 +40,20 @@ public void testSimple() throws Exception { @Test public void testSpecificVersion() throws Exception { - String prestoVersion = Integer.toString(parseInt(PrestoContainer.DEFAULT_TAG) - 1); - try (PrestoContainer prestoSql = new PrestoContainer<>("prestosql/presto:" + prestoVersion)) { + try (PrestoContainer prestoSql = new PrestoContainer<>(PrestoTestImages.PRESTO_PREVIOUS_VERSION_TEST_IMAGE)) { prestoSql.start(); try (Connection connection = prestoSql.createConnection(); Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes")) { assertTrue("No result", resultSet.next()); - assertEquals("Presto version", prestoVersion, resultSet.getString("node_version")); + assertEquals("Presto version", PrestoTestImages.PRESTO_PREVIOUS_VERSION_TEST_IMAGE.getVersionPart(), resultSet.getString("node_version")); } } } @Test public void testQueryMemoryAndTpch() throws SQLException { - try (PrestoContainer prestoSql = new PrestoContainer<>()) { + try (PrestoContainer prestoSql = new PrestoContainer<>(PrestoTestImages.PRESTO_TEST_IMAGE)) { prestoSql.start(); try (Connection connection = prestoSql.createConnection(); Statement statement = connection.createStatement()) { @@ -77,7 +79,7 @@ public void testQueryMemoryAndTpch() throws SQLException { @Test public void testInitScript() throws Exception { - try (PrestoContainer prestoSql = new PrestoContainer<>()) { + try (PrestoContainer prestoSql = new PrestoContainer<>(PrestoTestImages.PRESTO_TEST_IMAGE)) { prestoSql.withInitScript("initial.sql"); prestoSql.start(); try (Connection connection = prestoSql.createConnection(); diff --git a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java index 936759efb1a..4d30d947113 100644 --- a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java +++ b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java @@ -2,6 +2,7 @@ import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.containers.wait.strategy.WaitAllStrategy; +import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.TestcontainersConfiguration; /** @@ -13,16 +14,29 @@ public class PulsarContainer extends GenericContainer { public static final int BROKER_HTTP_PORT = 8080; public static final String METRICS_ENDPOINT = "/metrics"; - private static final String PULSAR_VERSION = "2.2.0"; + @Deprecated + private static final String DEFAULT_PULSAR_VERSION = "2.2.0"; private boolean functionsWorkerEnabled = false; + /** + * @deprecated use {@link PulsarContainer(DockerImageName)} instead + */ + @Deprecated public PulsarContainer() { - this(PULSAR_VERSION); + this(DEFAULT_PULSAR_VERSION); } + /** + * @deprecated use {@link PulsarContainer(DockerImageName)} instead + */ + @Deprecated public PulsarContainer(String pulsarVersion) { - super(TestcontainersConfiguration.getInstance().getPulsarImage() + ":" + pulsarVersion); + this(TestcontainersConfiguration.getInstance().getPulsarDockerImageName().withTag(pulsarVersion)); + } + + public PulsarContainer(final DockerImageName dockerImageName) { + super(dockerImageName); withExposedPorts(BROKER_PORT, BROKER_HTTP_PORT); withCommand("/pulsar/bin/pulsar", "standalone", "--no-functions-worker", "-nss"); waitingFor(Wait.forHttp(METRICS_ENDPOINT).forStatusCode(200).forPort(BROKER_HTTP_PORT)); diff --git a/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java b/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java index e9d41516670..f2354d98ef7 100644 --- a/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java +++ b/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java @@ -7,6 +7,7 @@ import org.apache.pulsar.client.api.Producer; import org.apache.pulsar.client.api.PulsarClient; import org.junit.Test; +import org.testcontainers.utility.DockerImageName; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; @@ -17,10 +18,11 @@ public class PulsarContainerTest { public static final String TEST_TOPIC = "test_topic"; + private static final DockerImageName PULSAR_IMAGE = DockerImageName.parse("apachepulsar/pulsar:2.2.0"); @Test public void testUsage() throws Exception { - try (PulsarContainer pulsar = new PulsarContainer()) { + try (PulsarContainer pulsar = new PulsarContainer(PULSAR_IMAGE)) { pulsar.start(); testPulsarFunctionality(pulsar.getPulsarBrokerUrl()); } diff --git a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java index d8d46f51b1c..a3b6134b29c 100644 --- a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java +++ b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java @@ -5,6 +5,7 @@ import com.github.dockerjava.api.command.InspectContainerResponse; import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MountableFile; import java.io.IOException; @@ -41,7 +42,9 @@ public class RabbitMQContainer extends GenericContainer { /** * Creates a Testcontainer using the official RabbitMQ docker image. + * @deprecated use {@link RabbitMQContainer(DockerImageName)} instead */ + @Deprecated public RabbitMQContainer() { this(DEFAULT_IMAGE_NAME + ":" + DEFAULT_TAG); } @@ -49,10 +52,16 @@ public RabbitMQContainer() { /** * Creates a Testcontainer using a specific docker image. * - * @param image The docker image to use. + * @param dockerImageName The docker image to use. + * @deprecated use {@link RabbitMQContainer(DockerImageName)} instead */ - public RabbitMQContainer(String image) { - super(image); + @Deprecated + public RabbitMQContainer(String dockerImageName) { + this(DockerImageName.parse(dockerImageName)); + } + + public RabbitMQContainer(final DockerImageName dockerImageName) { + super(dockerImageName); addExposedPorts(DEFAULT_AMQP_PORT, DEFAULT_AMQPS_PORT, DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT); diff --git a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerJUnitIntegrationTest.java b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerJUnitIntegrationTest.java index ff71db44348..7473f470f6f 100644 --- a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerJUnitIntegrationTest.java +++ b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerJUnitIntegrationTest.java @@ -13,7 +13,7 @@ public class RabbitMQContainerJUnitIntegrationTest { @ClassRule - public static RabbitMQContainer rabbitMQContainer = new RabbitMQContainer(); + public static RabbitMQContainer rabbitMQContainer = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE); @Test public void shouldStart() { diff --git a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java index 4c2787bf5ab..8be97deb710 100644 --- a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java +++ b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java @@ -1,15 +1,16 @@ package org.testcontainers.containers; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.testcontainers.containers.RabbitMQContainer.SslVerification.VERIFY_PEER; -import static org.testcontainers.utility.MountableFile.forClasspathResource; - import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; +import org.junit.Test; +import org.testcontainers.utility.MountableFile; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -20,18 +21,17 @@ import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.util.Collections; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManagerFactory; -import org.junit.Test; -import org.testcontainers.utility.MountableFile; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.testcontainers.containers.RabbitMQContainer.SslVerification.VERIFY_PEER; +import static org.testcontainers.utility.MountableFile.forClasspathResource; /** * @author Martin Greber */ public class RabbitMQContainerTest { - public static final String DEFAULT_IMAGE = "rabbitmq:3.7.25-management-alpine"; public static final int DEFAULT_AMQPS_PORT = 5671; public static final int DEFAULT_AMQP_PORT = 5672; public static final int DEFAULT_HTTPS_PORT = 15671; @@ -40,9 +40,8 @@ public class RabbitMQContainerTest { @Test public void shouldCreateRabbitMQContainer() { - try (RabbitMQContainer container = new RabbitMQContainer()) { + try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { - assertThat(container.getDockerImageName()).isEqualTo(DEFAULT_IMAGE); assertThat(container.getAdminPassword()).isEqualTo("guest"); assertThat(container.getAdminUsername()).isEqualTo("guest"); @@ -71,18 +70,10 @@ public void shouldCreateRabbitMQContainer() } } - @Test - public void shouldCreateRabbitMQContainerWithTag() - { - try (RabbitMQContainer container = new RabbitMQContainer(DEFAULT_IMAGE)) { - assertThat(container.getDockerImageName()).isEqualTo(DEFAULT_IMAGE); - } - } - @Test public void shouldCreateRabbitMQContainerWithExchange() throws IOException, InterruptedException { - try (RabbitMQContainer container = new RabbitMQContainer()) { + try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { container.withExchange("test-exchange", "direct"); container.start(); @@ -95,7 +86,7 @@ public void shouldCreateRabbitMQContainerWithExchange() throws IOException, Inte @Test public void shouldCreateRabbitMQContainerWithExchangeInVhost() throws IOException, InterruptedException { - try (RabbitMQContainer container = new RabbitMQContainer()) { + try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { container.withVhost("test-vhost"); container.withExchange("test-vhost", "test-exchange", "direct", false, false, false, Collections.emptyMap()); @@ -109,7 +100,7 @@ public void shouldCreateRabbitMQContainerWithExchangeInVhost() throws IOExceptio @Test public void shouldCreateRabbitMQContainerWithQueues() throws IOException, InterruptedException { - try (RabbitMQContainer container = new RabbitMQContainer()) { + try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { container.withQueue("queue-one") .withQueue("queue-two", false, true, ImmutableMap.of("x-message-ttl", 1000)); @@ -126,7 +117,7 @@ public void shouldCreateRabbitMQContainerWithQueues() throws IOException, Interr @Test public void shouldMountConfigurationFile() { - try (RabbitMQContainer container = new RabbitMQContainer()) { + try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { container.withRabbitMQConfig(MountableFile.forClasspathResource("/rabbitmq-custom.conf")); container.start(); @@ -140,7 +131,7 @@ public void shouldMountConfigurationFile() @Test public void shouldMountConfigurationFileErlang() { - try (RabbitMQContainer container = new RabbitMQContainer()) { + try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { container.withRabbitMQConfigErlang(MountableFile.forClasspathResource("/rabbitmq-custom.config")); container.start(); @@ -154,7 +145,7 @@ public void shouldMountConfigurationFileErlang() @Test public void shouldMountConfigurationFileSysctl() { - try (RabbitMQContainer container = new RabbitMQContainer()) { + try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { container.withRabbitMQConfigSysctl(MountableFile.forClasspathResource("/rabbitmq-custom.conf")); container.start(); @@ -167,7 +158,7 @@ public void shouldMountConfigurationFileSysctl() @Test public void shouldStartTheWholeEnchilada() throws IOException, InterruptedException { - try (RabbitMQContainer container = new RabbitMQContainer()) { + try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { container .withVhost("vhost1") .withVhostLimit("vhost1", "max-connections", 1) @@ -229,7 +220,7 @@ public void shouldStartTheWholeEnchilada() throws IOException, InterruptedExcept @Test public void shouldThrowExceptionForDodgyJson() { - try (RabbitMQContainer container = new RabbitMQContainer()) { + try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { assertThatCode(() -> container.withQueue( @@ -245,7 +236,7 @@ public void shouldThrowExceptionForDodgyJson() @Test public void shouldWorkWithSSL() { - try (RabbitMQContainer container = new RabbitMQContainer()) { + try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { container.withSSL( forClasspathResource("/certs/server_key.pem", 0644), forClasspathResource("/certs/server_certificate.pem", 0644), diff --git a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQTestImages.java b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQTestImages.java new file mode 100644 index 00000000000..0898cbdb350 --- /dev/null +++ b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQTestImages.java @@ -0,0 +1,7 @@ +package org.testcontainers.containers; + +import org.testcontainers.utility.DockerImageName; + +public interface RabbitMQTestImages { + DockerImageName RABBITMQ_IMAGE = DockerImageName.parse("rabbitmq:3.7.25-management-alpine"); +} diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index 16127171c59..f1cedab76ad 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -1,12 +1,10 @@ package org.testcontainers.containers; +import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.model.AccessMode; import com.github.dockerjava.api.model.Bind; import com.github.dockerjava.api.model.Volume; import com.google.common.collect.ImmutableSet; - -import com.github.dockerjava.api.command.InspectContainerResponse; - import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.openqa.selenium.Capabilities; @@ -25,6 +23,7 @@ import org.testcontainers.containers.wait.strategy.WaitStrategy; import org.testcontainers.lifecycle.TestDescription; import org.testcontainers.lifecycle.TestLifecycleAware; +import org.testcontainers.utility.DockerImageName; import java.io.File; import java.io.IOException; @@ -70,8 +69,10 @@ public class BrowserWebDriverContainer() + public NginxContainer nginx = new NginxContainer<>(SeleniumTestImages.NGINX_TEST_IMAGE) .withNetwork(network) .withNetworkAliases("nginx") .withCustomContent(contentFolder.toString()); @Rule - public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() + public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() .withNetwork(network) .withCapabilities(new ChromeOptions()); diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/Selenium3xTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/Selenium3xTest.java index f7415471bc8..bbcd8c15166 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/Selenium3xTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/Selenium3xTest.java @@ -5,6 +5,7 @@ import org.junit.runners.Parameterized; import org.openqa.selenium.chrome.ChromeOptions; import org.testcontainers.containers.BrowserWebDriverContainer; +import org.testcontainers.utility.DockerImageName; /** * Simple test to check that readiness detection works correctly across major versions of the containers. @@ -22,7 +23,8 @@ public static String[] data() { @Test public void testAdditionalStartupString() { - try (BrowserWebDriverContainer chrome = new BrowserWebDriverContainer("selenium/standalone-chrome-debug:" + tag) + final DockerImageName imageName = DockerImageName.parse("selenium/standalone-chrome-debug").withTag(tag); + try (BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>(imageName) .withCapabilities(new ChromeOptions())) { chrome.start(); } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java index 01edd70c102..6ecf0899ede 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java @@ -4,14 +4,14 @@ import org.junit.Test; import org.openqa.selenium.firefox.FirefoxOptions; import org.testcontainers.containers.BrowserWebDriverContainer; +import org.testcontainers.utility.DockerImageName; -/** - * - */ public class SpecificImageNameWebDriverContainerTest extends BaseWebDriverContainerTest { + private static final DockerImageName FIREFOX_IMAGE = DockerImageName.parse("selenium/standalone-firefox:2.53.1-beryllium"); + @Rule - public BrowserWebDriverContainer firefox = new BrowserWebDriverContainer("selenium/standalone-firefox:2.53.1-beryllium") + public BrowserWebDriverContainer firefox = new BrowserWebDriverContainer<>(FIREFOX_IMAGE) .withCapabilities(new FirefoxOptions()); @Test diff --git a/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java b/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java index 90fd674dfda..aeadb664089 100644 --- a/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java +++ b/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java @@ -1,18 +1,17 @@ package org.testcontainers.containers; -import static java.time.temporal.ChronoUnit.SECONDS; +import com.github.dockerjava.api.command.InspectContainerResponse; +import lombok.SneakyThrows; +import org.apache.commons.lang.StringUtils; +import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; +import org.testcontainers.utility.DockerImageName; import java.net.URL; import java.time.Duration; import java.util.HashSet; import java.util.Set; -import com.github.dockerjava.api.command.InspectContainerResponse; - -import lombok.SneakyThrows; - -import org.apache.commons.lang.StringUtils; -import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; +import static java.time.temporal.ChronoUnit.SECONDS; /** * SolrContainer allows a solr container to be launched and controlled. @@ -27,11 +26,23 @@ public class SolrContainer extends GenericContainer { private SolrContainerConfiguration configuration; + /** + * @deprecated use {@link SolrContainer(DockerImageName)} instead + */ + @Deprecated public SolrContainer() { this(IMAGE + ":" + DEFAULT_TAG); } + /** + * @deprecated use {@link SolrContainer(DockerImageName)} instead + */ + @Deprecated public SolrContainer(final String dockerImageName) { + this(DockerImageName.parse(dockerImageName)); + } + + public SolrContainer(final DockerImageName dockerImageName) { super(dockerImageName); this.waitStrategy = new LogMessageWaitStrategy() .withRegEx(".*o\\.e\\.j\\.s\\.Server Started.*") diff --git a/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java b/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java index 48cb04b7053..c2d19a3e61c 100644 --- a/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java +++ b/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java @@ -1,23 +1,25 @@ package org.testcontainers.containers; -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; - -import java.io.IOException; - import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.impl.Http2SolrClient; import org.apache.solr.client.solrj.response.SolrPingResponse; import org.junit.After; import org.junit.Test; +import org.testcontainers.utility.DockerImageName; + +import java.io.IOException; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; /** * @author Simon Schneider */ public class SolrContainerTest { + private static final DockerImageName SOLR_IMAGE = DockerImageName.parse("solr:8.3.0"); private SolrClient client = null; @After @@ -30,7 +32,7 @@ public void stopRestClient() throws IOException { @Test public void solrCloudTest() throws IOException, SolrServerException { - try (SolrContainer container = new SolrContainer()) { + try (SolrContainer container = new SolrContainer(SOLR_IMAGE)) { container.start(); SolrPingResponse response = getClient(container).ping("dummy"); assertThat(response.getStatus(), is(0)); @@ -40,7 +42,7 @@ public void solrCloudTest() throws IOException, SolrServerException { @Test public void solrStandaloneTest() throws IOException, SolrServerException { - try (SolrContainer container = new SolrContainer().withZookeeper(false)) { + try (SolrContainer container = new SolrContainer(SOLR_IMAGE).withZookeeper(false)) { container.start(); SolrPingResponse response = getClient(container).ping("dummy"); assertThat(response.getStatus(), is(0)); @@ -52,7 +54,7 @@ public void solrStandaloneTest() throws IOException, SolrServerException { public void solrCloudPingTest() throws IOException, SolrServerException { // solrContainerUsage { // Create the solr container. - SolrContainer container = new SolrContainer(); + SolrContainer container = new SolrContainer(SOLR_IMAGE); // Start the container. This step might take some time... container.start(); diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/MySqlContainerIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/MySqlContainerIT.groovy index 7a68302289f..639b46f5886 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/MySqlContainerIT.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/MySqlContainerIT.groovy @@ -15,7 +15,7 @@ import spock.lang.Specification class MySqlContainerIT extends Specification { @Shared - MySQLContainer mySQLContainer = new MySQLContainer() + MySQLContainer mySQLContainer = new MySQLContainer(SpockTestImages.MYSQL_IMAGE) def "dummy test"() { expect: diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/PostgresContainerIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/PostgresContainerIT.groovy index 4ac095bed6f..a886d552732 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/PostgresContainerIT.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/PostgresContainerIT.groovy @@ -14,7 +14,7 @@ import java.sql.Statement class PostgresContainerIT extends Specification { @Shared - PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer() + PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer(SpockTestImages.POSTGRES_TEST_IMAGE) .withDatabaseName("foo") .withUsername("foo") .withPassword("secret") diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy new file mode 100644 index 00000000000..526c1fe5c42 --- /dev/null +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy @@ -0,0 +1,11 @@ +package org.testcontainers.spock; + +import org.testcontainers.utility.DockerImageName +import org.testcontainers.utility.TestcontainersConfiguration; + +interface SpockTestImages { + DockerImageName MYSQL_IMAGE = DockerImageName.parse("mysql:5.7.22") + DockerImageName POSTGRES_TEST_IMAGE = DockerImageName.parse("postgres:9.6.12") + DockerImageName HTTPD_IMAGE = DockerImageName.parse("httpd:2.4-alpine") + DockerImageName TINY_IMAGE = TestcontainersConfiguration.getInstance().getTinyDockerImageName() +} diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/TestHierarchyIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/TestHierarchyIT.groovy index 123828327bc..7fb37446ca5 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/TestHierarchyIT.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/TestHierarchyIT.groovy @@ -10,7 +10,7 @@ import spock.lang.Shared class TestHierarchyIT extends MySqlContainerIT { @Shared - PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer() + PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer(SpockTestImages.POSTGRES_TEST_IMAGE) .withDatabaseName("foo") .withUsername("foo") .withPassword("secret") diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareContainerMock.java b/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareContainerMock.java index 43be6d32ca4..eb628fd2f33 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareContainerMock.java +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareContainerMock.java @@ -8,6 +8,8 @@ import java.util.List; import java.util.Optional; +import static org.testcontainers.spock.SpockTestImages.TINY_IMAGE; + public class TestLifecycleAwareContainerMock extends GenericContainer implements TestLifecycleAware { static final String BEFORE_TEST = "beforeTest"; @@ -18,6 +20,10 @@ public class TestLifecycleAwareContainerMock extends GenericContainer { private final Map proxies = new HashMap<>(); private final AtomicInteger nextPort = new AtomicInteger(FIRST_PROXIED_PORT); + /** + * @deprecated use {@link ToxiproxyContainer(DockerImageName)} instead + */ + @Deprecated public ToxiproxyContainer() { this(IMAGE_NAME); } - public ToxiproxyContainer(String imageName) { - super(imageName); + /** + * @deprecated use {@link ToxiproxyContainer(DockerImageName)} instead + */ + @Deprecated + public ToxiproxyContainer(String dockerImageName) { + this(DockerImageName.parse(dockerImageName)); + } + + public ToxiproxyContainer(final DockerImageName dockerImageName) { + super(dockerImageName); addExposedPorts(TOXIPROXY_CONTROL_PORT); setWaitStrategy(new HttpWaitStrategy().forPath("/version").forPort(TOXIPROXY_CONTROL_PORT)); diff --git a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java index d76851f57b5..87f9758ebf1 100644 --- a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java +++ b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java @@ -1,16 +1,19 @@ package org.testcontainers.containers; import eu.rekawek.toxiproxy.model.ToxicDirection; -import java.time.Duration; import org.junit.Rule; import org.junit.Test; +import org.testcontainers.utility.DockerImageName; import redis.clients.jedis.Jedis; import redis.clients.jedis.exceptions.JedisConnectionException; import java.io.IOException; +import java.time.Duration; import static java.lang.String.format; -import static org.rnorth.visibleassertions.VisibleAssertions.*; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; public class ToxiproxyTest { @@ -25,15 +28,17 @@ public class ToxiproxyTest { @Rule public Network network = Network.newNetwork(); + private static final DockerImageName REDIS_IMAGE = DockerImageName.parse("redis:5.0.4"); // The target container - this could be anything @Rule - public GenericContainer redis = new GenericContainer("redis:5.0.4") + public GenericContainer redis = new GenericContainer<>(REDIS_IMAGE) .withExposedPorts(6379) .withNetwork(network); + private static final DockerImageName TOXIPROXY_IMAGE = DockerImageName.parse("shopify/toxiproxy:2.1.0"); // Toxiproxy container, which will be used as a TCP proxy @Rule - public ToxiproxyContainer toxiproxy = new ToxiproxyContainer() + public ToxiproxyContainer toxiproxy = new ToxiproxyContainer(TOXIPROXY_IMAGE) .withNetwork(network) .withNetworkAliases(TOXIPROXY_NETWORK_ALIAS); // } @@ -99,7 +104,7 @@ public void testConnectionCut() { @Test public void testMultipleProxiesCanBeCreated() { - try (GenericContainer secondRedis = new GenericContainer("redis:5.0.4") + try (GenericContainer secondRedis = new GenericContainer<>(REDIS_IMAGE) .withExposedPorts(6379) .withNetwork(network)) { diff --git a/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java b/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java index ac4e730adb3..1532f463f37 100644 --- a/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java +++ b/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java @@ -3,6 +3,7 @@ import com.github.dockerjava.api.command.InspectContainerResponse; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.utility.DockerImageName; import java.io.IOException; import java.util.ArrayList; @@ -28,11 +29,23 @@ public class VaultContainer> extends GenericCo private int port = VAULT_PORT; + /** + * @deprecated use {@link VaultContainer(DockerImageName)} instead + */ + @Deprecated public VaultContainer() { this("vault:1.1.3"); } + /** + * @deprecated use {@link VaultContainer(DockerImageName)} instead + */ + @Deprecated public VaultContainer(String dockerImageName) { + this(DockerImageName.parse(dockerImageName)); + } + + public VaultContainer(final DockerImageName dockerImageName) { super(dockerImageName); // Use the vault healthcheck endpoint to check for readiness, per https://www.vaultproject.io/api/system/health.html diff --git a/modules/vault/src/test/java/org/testcontainers/vault/VaultClientTest.java b/modules/vault/src/test/java/org/testcontainers/vault/VaultClientTest.java index dfe73cae4b9..4a1a800da17 100644 --- a/modules/vault/src/test/java/org/testcontainers/vault/VaultClientTest.java +++ b/modules/vault/src/test/java/org/testcontainers/vault/VaultClientTest.java @@ -10,6 +10,7 @@ import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; +import static org.testcontainers.vault.VaultTestImages.VAULT_IMAGE; public class VaultClientTest { @@ -18,7 +19,7 @@ public class VaultClientTest { @Test public void writeAndReadMultipleValues() throws VaultException { try ( - VaultContainer vaultContainer = new VaultContainer<>() + VaultContainer vaultContainer = new VaultContainer<>(VAULT_IMAGE) .withVaultToken(VAULT_TOKEN) ) { diff --git a/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java b/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java index d7ac5ca8686..b3dd788f053 100644 --- a/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java +++ b/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java @@ -19,7 +19,7 @@ public class VaultContainerTest { private static final String VAULT_TOKEN = "my-root-token"; @ClassRule - public static VaultContainer vaultContainer = new VaultContainer<>() + public static VaultContainer vaultContainer = new VaultContainer<>(VaultTestImages.VAULT_IMAGE) .withVaultToken(VAULT_TOKEN) .withSecretInVault("secret/testing1", "top_secret=password123") .withSecretInVault("secret/testing2", diff --git a/modules/vault/src/test/java/org/testcontainers/vault/VaultTestImages.java b/modules/vault/src/test/java/org/testcontainers/vault/VaultTestImages.java new file mode 100644 index 00000000000..9afecdcffb9 --- /dev/null +++ b/modules/vault/src/test/java/org/testcontainers/vault/VaultTestImages.java @@ -0,0 +1,7 @@ +package org.testcontainers.vault; + +import org.testcontainers.utility.DockerImageName; + +public interface VaultTestImages { + DockerImageName VAULT_IMAGE = DockerImageName.parse("vault:1.1.3"); +} From 39c4208ed4edfede2e8721b7aef4d1cb79a9073c Mon Sep 17 00:00:00 2001 From: Kirill Artamonov Date: Sun, 19 Jul 2020 20:35:48 +0200 Subject: [PATCH 1518/5615] Update to latest LocalStack container and support unified port mode (#2825) Co-authored-by: Richard North --- .../utility/ComparableVersion.java | 4 + .../localstack/LocalStackContainer.java | 91 +++++++++++-- .../containers/localstack/LegacyModeTest.java | 128 ++++++++++++++++++ .../localstack/LocalstackContainerTest.java | 80 ++++++----- .../localstack/LocalstackTestImages.java | 8 ++ 5 files changed, 263 insertions(+), 48 deletions(-) create mode 100644 modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java create mode 100644 modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackTestImages.java diff --git a/core/src/main/java/org/testcontainers/utility/ComparableVersion.java b/core/src/main/java/org/testcontainers/utility/ComparableVersion.java index 1ffa1642568..d3e3d9eb958 100644 --- a/core/src/main/java/org/testcontainers/utility/ComparableVersion.java +++ b/core/src/main/java/org/testcontainers/utility/ComparableVersion.java @@ -32,6 +32,10 @@ public int compareTo(@NotNull ComparableVersion other) { return 0; } + public boolean isSemanticVersion() { + return parts.length > 0; + } + public boolean isLessThan(String other) { return this.compareTo(new ComparableVersion(other)) < 0; } diff --git a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java index 0136d94e3b2..3a9fab26ae4 100644 --- a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java +++ b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java @@ -4,15 +4,6 @@ import com.amazonaws.auth.AWSStaticCredentialsProvider; import com.amazonaws.auth.BasicAWSCredentials; import com.amazonaws.client.builder.AwsClientBuilder; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.experimental.FieldDefaults; -import org.rnorth.ducttape.Preconditions; -import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.wait.strategy.Wait; -import org.testcontainers.utility.DockerImageName; -import org.testcontainers.utility.TestcontainersConfiguration; - import java.net.InetAddress; import java.net.URI; import java.net.URISyntaxException; @@ -21,6 +12,16 @@ import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; +import lombok.extern.slf4j.Slf4j; +import org.rnorth.ducttape.Preconditions; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.utility.ComparableVersion; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.TestcontainersConfiguration; /** *

Container for Atlassian Labs Localstack, 'A fully functional local AWS cloud stack'.

@@ -30,13 +31,28 @@ * {@link LocalStackContainer#getDefaultCredentialsProvider()} * be used to obtain compatible endpoint configuration and credentials, respectively.

*/ +@Slf4j public class LocalStackContainer extends GenericContainer { - public static final String VERSION = "0.10.8"; + public static final String VERSION = "0.11.2"; + static final int PORT = 4566; private static final String HOSTNAME_EXTERNAL_ENV_VAR = "HOSTNAME_EXTERNAL"; - private final List services = new ArrayList<>(); + /** + * Whether or to assume that all APIs run on different ports (when true) or are + * exposed on a single port (false). From the Localstack README: + * + *
Note: Starting with version 0.11.0, all APIs are exposed via a single edge + * service [...] The API-specific endpoints below are still left for backward-compatibility but + * may get removed in a future release - please reconfigure your client SDKs to start using the + * single edge endpoint URL!
+ *

+ * Testcontainers will use the tag of the docker image to infer whether or not the used version + * of Localstack supports this feature. + */ + private final boolean legacyMode; + /** * @deprecated use {@link LocalStackContainer(DockerImageName)} instead */ @@ -53,13 +69,41 @@ public LocalStackContainer(String version) { this(DockerImageName.parse(TestcontainersConfiguration.getInstance().getLocalStackImage() + ":" + version)); } + /** + * @param dockerImageName image name to use for Localstack + */ public LocalStackContainer(final DockerImageName dockerImageName) { + this(dockerImageName, shouldRunInLegacyMode(dockerImageName.getVersionPart())); + } + + /** + * @param dockerImageName image name to use for Localstack + * @param useLegacyMode if true, each AWS service is exposed on a different port + */ + public LocalStackContainer(final DockerImageName dockerImageName, boolean useLegacyMode) { super(dockerImageName); + this.legacyMode = useLegacyMode; withFileSystemBind("//var/run/docker.sock", "/var/run/docker.sock"); waitingFor(Wait.forLogMessage(".*Ready\\.\n", 1)); } + private static boolean shouldRunInLegacyMode(String version) { + if (version.equals("latest")) { + return false; + } + + ComparableVersion comparableVersion = new ComparableVersion(version); + if (comparableVersion.isSemanticVersion()) { + boolean versionRequiresLegacyMode = comparableVersion.isLessThan("0.11"); + return versionRequiresLegacyMode; + } + + log.warn("Version {} is not a semantic version, LocalStack will run in legacy mode.", version); + log.warn("Consider using \"LocalStackContainer(DockerImageName dockerImageName, boolean legacyMode)\" constructor if you want to disable legacy mode."); + return true; + } + @Override protected void configure() { super.configure(); @@ -81,9 +125,14 @@ protected void configure() { } logger().info("{} environment variable set to {} ({})", HOSTNAME_EXTERNAL_ENV_VAR, getEnvMap().get(HOSTNAME_EXTERNAL_ENV_VAR), hostnameExternalReason); - for (Service service : services) { - addExposedPort(service.getPort()); - } + exposePorts(); + } + + private void exposePorts() { + services.stream() + .map(this::getServicePort) + .distinct() + .forEach(this::addExposedPort); } /** @@ -154,12 +203,16 @@ public URI getEndpointOverride(Service service) { return new URI("http://" + ipAddress + ":" + - getMappedPort(service.getPort())); + getMappedPort(getServicePort(service))); } catch (UnknownHostException | URISyntaxException e) { throw new IllegalStateException("Cannot obtain endpoint URL", e); } } + private int getServicePort(Service service) { + return legacyMode ? service.port : PORT; + } + /** * Provides a {@link AWSCredentialsProvider} that is preconfigured to communicate with a given simulated service. * The credentials provider should be set in the AWS Java SDK when building a client, e.g.: @@ -271,5 +324,13 @@ public enum Service { String localStackName; int port; + + @Deprecated + /* + Since version 0.11, LocalStack exposes all services on a single (4566) port. + */ + public int getPort() { + return port; + } } } diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java new file mode 100644 index 00000000000..f50846b091f --- /dev/null +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java @@ -0,0 +1,128 @@ +package org.testcontainers.containers.localstack; + +import lombok.AllArgsConstructor; +import lombok.SneakyThrows; +import org.junit.After; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.runners.Enclosed; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Arrays; +import java.util.function.Consumer; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.testcontainers.containers.localstack.LocalStackContainer.Service.S3; +import static org.testcontainers.containers.localstack.LocalStackContainer.Service.SQS; +import static org.testcontainers.containers.localstack.LocalstackTestImages.LOCALSTACK_IMAGE; + +@RunWith(Enclosed.class) +public class LegacyModeTest { + + @RunWith(Parameterized.class) + @AllArgsConstructor + public static class Off { + private final String description; + private final LocalStackContainer localstack; + + @Parameterized.Parameters(name = "{0}") + public static Iterable constructors() { + return Arrays.asList(new Object[][]{ + {"default constructor", new LocalStackContainer(LOCALSTACK_IMAGE)}, + {"latest", new LocalStackContainer(LOCALSTACK_IMAGE.withTag("latest"))}, + {"0.11.1", new LocalStackContainer(LOCALSTACK_IMAGE.withTag("0.11.1"))}, + {"0.7.0 with legacy = off", new LocalStackContainer(LOCALSTACK_IMAGE.withTag("0.7.0"), false)} + }); + } + + @Test + public void samePortIsExposedForAllServices() { + localstack.withServices(S3, SQS); + localstack.start(); + + assertTrue("A single port is exposed", localstack.getExposedPorts().size() == 1); + assertEquals( + "Endpoint overrides are different", + localstack.getEndpointOverride(S3).toString(), + localstack.getEndpointOverride(SQS).toString()); + assertEquals( + "Endpoint configuration have different endpoints", + localstack.getEndpointConfiguration(S3).getServiceEndpoint(), + localstack.getEndpointConfiguration(SQS).getServiceEndpoint()); + } + + @After + public void cleanup() { + if (localstack != null) localstack.stop(); + } + } + + @RunWith(Parameterized.class) + @AllArgsConstructor + public static class On { + private final String description; + private final LocalStackContainer localstack; + + @BeforeClass + public static void createCustomTag() { + run("docker pull localstack/localstack:latest"); + run("docker tag localstack/localstack:latest localstack/localstack:custom"); + } + + @Parameterized.Parameters(name = "{0}") + public static Iterable constructors() { + return Arrays.asList(new Object[][]{ + {"0.10.7", new LocalStackContainer(LOCALSTACK_IMAGE.withTag("0.10.7"))}, + {"custom", new LocalStackContainer(LOCALSTACK_IMAGE.withTag("custom"))}, + {"0.11.1 with legacy = on", new LocalStackContainer(LOCALSTACK_IMAGE.withTag("0.11.1"), true)} + }); + } + + @Test + public void differentPortsAreExposed() { + localstack.withServices(S3, SQS); + localstack.start(); + + assertTrue("Multiple ports are exposed", localstack.getExposedPorts().size() > 1); + assertNotEquals( + "Endpoint overrides are different", + localstack.getEndpointOverride(S3).toString(), + localstack.getEndpointOverride(SQS).toString()); + assertNotEquals( + "Endpoint configuration have different endpoints", + localstack.getEndpointConfiguration(S3).getServiceEndpoint(), + localstack.getEndpointConfiguration(SQS).getServiceEndpoint()); + } + + @After + public void cleanup() { + if (localstack != null) localstack.stop(); + } + } + + @SneakyThrows + private static void run(String command) { + Process process = Runtime.getRuntime().exec(command); + join(process.getInputStream(), System.out::println); + join(process.getErrorStream(), System.err::println); + process.waitFor(); + if (process.exitValue() != 0) + throw new RuntimeException("Failed to execute " + command); + } + + private static void join(InputStream stream, Consumer logger) throws IOException { + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(stream)); + String line; + while ((line = bufferedReader.readLine()) != null) { + logger.accept(line); + } + } + +} diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java index 63be9d66df7..fad4060341c 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java @@ -1,6 +1,16 @@ package org.testcontainers.containers.localstack; +import static org.hamcrest.CoreMatchers.containsString; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.testcontainers.containers.localstack.LocalStackContainer.PORT; +import static org.testcontainers.containers.localstack.LocalStackContainer.Service.CLOUDWATCHLOGS; +import static org.testcontainers.containers.localstack.LocalStackContainer.Service.KMS; +import static org.testcontainers.containers.localstack.LocalStackContainer.Service.S3; +import static org.testcontainers.containers.localstack.LocalStackContainer.Service.SQS; + import com.amazonaws.services.kms.AWSKMS; import com.amazonaws.services.kms.AWSKMSClientBuilder; import com.amazonaws.services.kms.model.CreateKeyRequest; @@ -18,6 +28,10 @@ import com.amazonaws.services.sqs.AmazonSQS; import com.amazonaws.services.sqs.AmazonSQSClientBuilder; import com.amazonaws.services.sqs.model.CreateQueueResult; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.List; +import java.util.Optional; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.junit.Assert; @@ -35,20 +49,6 @@ import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3Client; -import java.io.IOException; -import java.nio.charset.Charset; -import java.util.List; -import java.util.Optional; - -import static org.hamcrest.CoreMatchers.containsString; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.containers.localstack.LocalStackContainer.Service.CLOUDWATCHLOGS; -import static org.testcontainers.containers.localstack.LocalStackContainer.Service.KMS; -import static org.testcontainers.containers.localstack.LocalStackContainer.Service.S3; -import static org.testcontainers.containers.localstack.LocalStackContainer.Service.SQS; - /** * Tests for Localstack Container, used both in bridge network (exposed to host) and docker network modes. *

@@ -60,14 +60,13 @@ @RunWith(Enclosed.class) public class LocalstackContainerTest { - private static final DockerImageName LOCALSTACK_IMAGE = DockerImageName.parse("localstack/localstack:0.10.8"); - private static final DockerImageName AWS_CLI_IMAGE = DockerImageName.parse("atlassian/pipelines-awscli:1.16.302"); - public static class WithoutNetwork { // without_network { @ClassRule - public static LocalStackContainer localstack = new LocalStackContainer(LOCALSTACK_IMAGE) + public static LocalStackContainer localstack = new LocalStackContainer( + DockerImageName.parse("localstack/localstack:0.11.0") + ) .withServices(S3, SQS, CLOUDWATCHLOGS, KMS); // } @@ -124,7 +123,7 @@ public void sqsTestOverBridgeNetwork() { CreateQueueResult queueResult = sqs.createQueue("baz"); String fooQueueUrl = queueResult.getQueueUrl(); assertThat("Created queue has external hostname URL", fooQueueUrl, - containsString("http://" + DockerClientFactory.instance().dockerHostIpAddress() + ":" + localstack.getMappedPort(SQS.getPort()))); + containsString("http://" + DockerClientFactory.instance().dockerHostIpAddress() + ":" + localstack.getMappedPort(PORT))); sqs.sendMessage(fooQueueUrl, "test"); final long messageCount = sqs.receiveMessage(fooQueueUrl).getMessages().stream() @@ -136,8 +135,8 @@ public void sqsTestOverBridgeNetwork() { @Test public void cloudWatchLogsTestOverBridgeNetwork() { AWSLogs logs = AWSLogsClientBuilder.standard() - .withEndpointConfiguration(localstack.getEndpointConfiguration(CLOUDWATCHLOGS)) - .withCredentials(localstack.getDefaultCredentialsProvider()).build(); + .withEndpointConfiguration(localstack.getEndpointConfiguration(CLOUDWATCHLOGS)) + .withCredentials(localstack.getDefaultCredentialsProvider()).build(); logs.createLogGroup(new CreateLogGroupRequest("foo")); @@ -160,6 +159,19 @@ public void kmsKeyCreationTest() { assertEquals("AWS KMS Customer Managed Key should be created ", key.getKeyMetadata().getDescription(), desc); } + + @Test + public void samePortIsExposedForAllServices() { + assertTrue("A single port is exposed", localstack.getExposedPorts().size() == 1); + assertEquals( + "Endpoint overrides are different", + localstack.getEndpointOverride(S3).toString(), + localstack.getEndpointOverride(SQS).toString()); + assertEquals( + "Endpoint configuration have different endpoints", + localstack.getEndpointConfiguration(S3).getServiceEndpoint(), + localstack.getEndpointConfiguration(SQS).getServiceEndpoint()); + } } public static class WithNetwork { @@ -167,14 +179,16 @@ public static class WithNetwork { private static Network network = Network.newNetwork(); @ClassRule - public static LocalStackContainer localstackInDockerNetwork = new LocalStackContainer(LOCALSTACK_IMAGE) + public static LocalStackContainer localstackInDockerNetwork = new LocalStackContainer( + DockerImageName.parse("localstack/localstack:0.11.0") + ) .withNetwork(network) .withNetworkAliases("notthis", "localstack") // the last alias is used for HOSTNAME_EXTERNAL .withServices(S3, SQS, CLOUDWATCHLOGS); // } @ClassRule - public static GenericContainer awsCliInDockerNetwork = new GenericContainer<>(AWS_CLI_IMAGE) + public static GenericContainer awsCliInDockerNetwork = new GenericContainer<>(LocalstackTestImages.AWS_CLI_IMAGE) .withNetwork(network) .withCreateContainerCmdModifier(cmd -> cmd.withEntrypoint("top")) .withEnv("AWS_ACCESS_KEY_ID", "accesskey") @@ -184,33 +198,33 @@ public static class WithNetwork { @Test public void s3TestOverDockerNetwork() throws Exception { - runAwsCliAgainstDockerNetworkContainer("s3api create-bucket --bucket foo", S3.getPort()); - runAwsCliAgainstDockerNetworkContainer("s3api list-buckets", S3.getPort()); - runAwsCliAgainstDockerNetworkContainer("s3 ls s3://foo", S3.getPort()); + runAwsCliAgainstDockerNetworkContainer("s3api create-bucket --bucket foo"); + runAwsCliAgainstDockerNetworkContainer("s3api list-buckets"); + runAwsCliAgainstDockerNetworkContainer("s3 ls s3://foo"); } @Test public void sqsTestOverDockerNetwork() throws Exception { - final String queueCreationResponse = runAwsCliAgainstDockerNetworkContainer("sqs create-queue --queue-name baz", SQS.getPort()); + final String queueCreationResponse = runAwsCliAgainstDockerNetworkContainer("sqs create-queue --queue-name baz"); assertThat("Created queue has external hostname URL", queueCreationResponse, - containsString("http://localstack:" + SQS.getPort())); + containsString("http://localstack:" + PORT)); runAwsCliAgainstDockerNetworkContainer( - String.format("sqs send-message --endpoint http://localstack:%d --queue-url http://localstack:%d/queue/baz --message-body test", SQS.getPort(), SQS.getPort()), SQS.getPort()); + String.format("sqs send-message --endpoint http://localstack:%d --queue-url http://localstack:%d/queue/baz --message-body test", PORT, PORT)); final String message = runAwsCliAgainstDockerNetworkContainer( - String.format("sqs receive-message --endpoint http://localstack:%d --queue-url http://localstack:%d/queue/baz", SQS.getPort(), SQS.getPort()), SQS.getPort()); + String.format("sqs receive-message --endpoint http://localstack:%d --queue-url http://localstack:%d/queue/baz", PORT, PORT)); assertTrue("the sent message can be received", message.contains("\"Body\": \"test\"")); } @Test public void cloudWatchLogsTestOverDockerNetwork() throws Exception { - runAwsCliAgainstDockerNetworkContainer("logs create-log-group --log-group-name foo", CLOUDWATCHLOGS.getPort()); + runAwsCliAgainstDockerNetworkContainer("logs create-log-group --log-group-name foo"); } - private String runAwsCliAgainstDockerNetworkContainer(String command, final int port) throws Exception { - final String[] commandParts = String.format("/usr/bin/aws --region eu-west-1 %s --endpoint-url http://localstack:%d --no-verify-ssl", command, port).split(" "); + private String runAwsCliAgainstDockerNetworkContainer(String command) throws Exception { + final String[] commandParts = String.format("/usr/bin/aws --region eu-west-1 %s --endpoint-url http://localstack:%d --no-verify-ssl", command, PORT).split(" "); final Container.ExecResult execResult = awsCliInDockerNetwork.execInContainer(commandParts); Assert.assertEquals(0, execResult.getExitCode()); diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackTestImages.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackTestImages.java new file mode 100644 index 00000000000..60e5498daf3 --- /dev/null +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackTestImages.java @@ -0,0 +1,8 @@ +package org.testcontainers.containers.localstack; + +import org.testcontainers.utility.DockerImageName; + +public interface LocalstackTestImages { + DockerImageName LOCALSTACK_IMAGE = DockerImageName.parse("localstack/localstack:0.11.0"); + DockerImageName AWS_CLI_IMAGE = DockerImageName.parse("atlassian/pipelines-awscli:1.16.302"); +} From dd0d21837e7062762969dac3c25207f445b70e0d Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 19 Jul 2020 19:43:10 +0100 Subject: [PATCH 1519/5615] Add latest backers --- docs/index.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/index.md b/docs/index.md index 4894297f034..131a9074bfd 100644 --- a/docs/index.md +++ b/docs/index.md @@ -137,6 +137,8 @@ A huge thank you to our backers: * [Lukas Eder (@lukaseder)](https://github.com/lukaseder) * [Fedor Korotkov (@fkorotkov)](https://github.com/fkorotkov) * [Christoph Dreis (@dreis2211)](https://github.com/dreis2211) +* [Pascal Zwick (@pas2al)](https://github.com/pas2al) +* [Nikita Zhevnitskiy (@zhenik)](https://github.com/zhenik) ## License From 706a6697408ef3c9ca98e5d7ab559e4ccfdf8cff Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 27 Jul 2020 18:30:22 +0200 Subject: [PATCH 1520/5615] Fix the Rootless Docker CI job (#3050) --- .github/workflows/ci-rootless.yml | 23 ++++++-- .../RootlessDockerClientProviderStrategy.java | 54 +++++++++++++++++-- 2 files changed, 67 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci-rootless.yml b/.github/workflows/ci-rootless.yml index e752747b804..7dacc2767a5 100644 --- a/.github/workflows/ci-rootless.yml +++ b/.github/workflows/ci-rootless.yml @@ -7,6 +7,14 @@ on: jobs: test: runs-on: ubuntu-18.04 + strategy: + fail-fast: false + matrix: + include: + - { XDG_RUNTIME_DIR: "" } + - { XDG_RUNTIME_DIR: "/tmp/docker-testcontainers/" } + env: + XDG_RUNTIME_DIR: ${{ matrix.XDG_RUNTIME_DIR }} steps: - uses: actions/checkout@v2 - name: debug @@ -14,17 +22,22 @@ jobs: - name: uninstall rootful Docker run: sudo apt-get -q -y --purge remove moby-engine moby-buildx && sudo rm -rf /var/run/docker.sock - name: install rootless Docker - run: curl -fsSL https://get.docker.com/rootless | sh - - name: start rootless Docker - run: PATH=$HOME/bin:$PATH XDG_RUNTIME_DIR=/tmp/docker-$(id -u) dockerd-rootless.sh --experimental --storage-driver vfs & + run: | + mkdir -p $XDG_RUNTIME_DIR || true + curl -fsSL https://get.docker.com/rootless | sh > init.sh + cat init.sh + source <(grep '^export' init.sh) + PATH=$HOME/bin:$PATH dockerd-rootless.sh --experimental --storage-driver vfs & + sleep 1 + DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock docker info || ls -la $XDG_RUNTIME_DIR - name: Build with Gradle - run: XDG_RUNTIME_DIR=/tmp/docker-$(id -u) ./gradlew --no-daemon --scan testcontainers:test + run: ./gradlew --no-daemon --scan testcontainers:test - name: aggregate test reports with ciMate if: always() continue-on-error: true env: CIMATE_PROJECT_ID: 2348n4vl - CIMATE_CI_KEY: "CI / rootless Docker" + CIMATE_CI_KEY: "CI / rootless Docker (${{ matrix.XDG_RUNTIME_DIR }})" run: | wget -q https://get.cimate.io/release/linux/cimate chmod +x cimate diff --git a/core/src/main/java/org/testcontainers/dockerclient/RootlessDockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/RootlessDockerClientProviderStrategy.java index cc6b23e506d..3ee58f4910a 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/RootlessDockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/RootlessDockerClientProviderStrategy.java @@ -2,28 +2,72 @@ import com.sun.jna.Library; import com.sun.jna.Native; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.SystemUtils; +import org.jetbrains.annotations.Nullable; import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Optional; /** * * @deprecated this class is used by the SPI and should not be used directly */ @Deprecated +@Slf4j public final class RootlessDockerClientProviderStrategy extends DockerClientProviderStrategy { public static final int PRIORITY = UnixSocketClientProviderStrategy.PRIORITY + 1; - private Path getSocketPath() { + @Getter(lazy = true) + @Nullable + private final Path socketPath = resolveSocketPath(); + + private Path resolveSocketPath() { + return tryEnv().orElseGet(() -> { + Path homePath = Paths.get(System.getProperty("user.home")).resolve(".docker").resolve("run"); + return tryFolder(homePath).orElseGet(() -> { + Path implicitPath = Paths.get("/run/user/" + LibC.INSTANCE.getuid()); + return tryFolder(implicitPath).orElse(null); + }); + }); + } + + private Optional tryEnv() { String xdgRuntimeDir = System.getenv("XDG_RUNTIME_DIR"); - if (xdgRuntimeDir == null) { - xdgRuntimeDir = "/run/user/" + LibC.INSTANCE.getuid(); + if (StringUtils.isBlank(xdgRuntimeDir)) { + log.debug("$XDG_RUNTIME_DIR is not set."); + return Optional.empty(); + } + Path path = Paths.get(xdgRuntimeDir); + if (!Files.exists(path)) { + log.debug("$XDG_RUNTIME_DIR is set to '{}' but the folder does not exist.", path); + return Optional.empty(); + } + Path socketPath = path.resolve("docker.sock"); + if (!Files.exists(socketPath)) { + log.debug("$XDG_RUNTIME_DIR is set but '{}' does not exist.", socketPath); + return Optional.empty(); + } + return Optional.of(socketPath); + } + + private Optional tryFolder(Path path) { + if (!Files.exists(path)) { + log.debug("'{}' does not exist.", path); + return Optional.empty(); + } + Path socketPath = path.resolve("docker.sock"); + if (!Files.exists(socketPath)) { + log.debug("'{}' does not exist.", socketPath); + return Optional.empty(); } - return Paths.get(xdgRuntimeDir).resolve("docker.sock"); + return Optional.of(socketPath); } @Override @@ -35,7 +79,7 @@ public TransportConfig getTransportConfig() throws InvalidConfigurationException @Override protected boolean isApplicable() { - return SystemUtils.IS_OS_LINUX && Files.exists(getSocketPath()); + return SystemUtils.IS_OS_LINUX && getSocketPath() != null && Files.exists(getSocketPath()); } @Override From 32c05bd88f120d1b9225f01ed683febd82df0b50 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 28 Jul 2020 13:44:05 +0200 Subject: [PATCH 1521/5615] limit to a subset of rootless tests --- .github/workflows/ci-rootless.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-rootless.yml b/.github/workflows/ci-rootless.yml index 7dacc2767a5..bf1fe08bbd2 100644 --- a/.github/workflows/ci-rootless.yml +++ b/.github/workflows/ci-rootless.yml @@ -31,7 +31,7 @@ jobs: sleep 1 DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock docker info || ls -la $XDG_RUNTIME_DIR - name: Build with Gradle - run: ./gradlew --no-daemon --scan testcontainers:test + run: ./gradlew --no-daemon --scan testcontainers:test --tests '*GenericContainerRuleTest' - name: aggregate test reports with ciMate if: always() continue-on-error: true From d50d01bd3a86c5540ea44515b0b1e2722e1283c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2020 20:25:10 +0100 Subject: [PATCH 1522/5615] Bump cucumber-java from 6.1.1 to 6.2.2 in /examples (#3040) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index d17837f1518..0ea976800a6 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' - testImplementation 'io.cucumber:cucumber-java:6.1.1' + testImplementation 'io.cucumber:cucumber-java:6.2.2' testImplementation 'io.cucumber:cucumber-junit:6.1.1' testImplementation 'org.testcontainers:selenium' } From 23f310c0aa41f2f5d836d44f4dab892c9e1508d8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2020 20:05:06 +0100 Subject: [PATCH 1523/5615] Bump cucumber-junit from 6.1.1 to 6.2.2 in /examples (#3041) Bumps [cucumber-junit](https://github.com/cucumber/cucumber-jvm) from 6.1.1 to 6.2.2. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v6.1.1...v6.2.2) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 0ea976800a6..ae211cedc04 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' testImplementation 'io.cucumber:cucumber-java:6.2.2' - testImplementation 'io.cucumber:cucumber-junit:6.1.1' + testImplementation 'io.cucumber:cucumber-junit:6.2.2' testImplementation 'org.testcontainers:selenium' } From 6aa2c567229c05ffaeedb478e13019442cbd8c55 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2020 20:06:38 +0100 Subject: [PATCH 1524/5615] Bump commons-lang3 from 3.10 to 3.11 in /modules/jdbc-test (#3043) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 0c06ad083e0..f19970c9380 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -3,7 +3,7 @@ dependencies { compile project(':test-support') compile 'com.google.guava:guava:29.0-jre' - compile 'org.apache.commons:commons-lang3:3.10' + compile 'org.apache.commons:commons-lang3:3.11' compile 'com.zaxxer:HikariCP-java6:2.3.13' compile 'commons-dbutils:commons-dbutils:1.7' From 95ac56c15524c17c4103ceee44b4b23849d08366 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2020 20:07:19 +0100 Subject: [PATCH 1525/5615] Bump s3 from 2.13.50 to 2.13.61 in /modules/localstack (#3028) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 6e9e44af320..3ce019b0549 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,5 +7,5 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.816' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.812' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' - testCompile 'software.amazon.awssdk:s3:2.13.50' + testCompile 'software.amazon.awssdk:s3:2.13.61' } From a0ac5449efaeed0bc62143d0a52ef472e2c6b0ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2020 20:13:00 +0100 Subject: [PATCH 1526/5615] Bump okhttp from 4.7.2 to 4.8.0 in /examples (#3042) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index f56b8bc46d8..75bc7f3b0e8 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' - implementation 'com.squareup.okhttp3:okhttp:4.7.2' + implementation 'com.squareup.okhttp3:okhttp:4.8.0' implementation 'org.json:json:20180813' testImplementation 'org.postgresql:postgresql:42.2.14' testImplementation 'ch.qos.logback:logback-classic:1.2.3' From 10dbdd98bed6c67989de277e4093eb219396169a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2020 20:38:56 +0100 Subject: [PATCH 1527/5615] Bump r2dbc-mssql from 0.8.3.RELEASE to 0.8.4.RELEASE in /modules/mssqlserver (#3033) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/mssqlserver/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index d9e01fe3a57..508de84a2f9 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -7,7 +7,7 @@ dependencies { compile project(':jdbc') compileOnly project(':r2dbc') - compileOnly 'io.r2dbc:r2dbc-mssql:0.8.3.RELEASE' + compileOnly 'io.r2dbc:r2dbc-mssql:0.8.4.RELEASE' testCompile project(':jdbc-test') testCompile 'com.microsoft.sqlserver:mssql-jdbc:8.3.1.jre8-preview' From 2056268178db93a66e16449f2afd62e4e0cd104b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2020 20:46:18 +0100 Subject: [PATCH 1528/5615] Bump mysql-connector-java from 8.0.20 to 8.0.21 in /modules/spock (#3032) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index ee4ac56053f..1a9eddf4ffc 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -16,7 +16,7 @@ dependencies { testCompile 'org.apache.httpcomponents:httpclient:4.5.12' testRuntime 'org.postgresql:postgresql:42.2.14' - testRuntime 'mysql:mysql-connector-java:8.0.20' + testRuntime 'mysql:mysql-connector-java:8.0.21' } sourceJar { From ed6b1175e0c3780ed4acaccc22bf7f71800a735c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2020 20:46:34 +0100 Subject: [PATCH 1529/5615] Bump r2dbc-postgresql from 0.8.3.RELEASE to 0.8.4.RELEASE in /modules/postgresql (#3035) Bumps [r2dbc-postgresql](https://github.com/r2dbc/r2dbc-postgresql) from 0.8.3.RELEASE to 0.8.4.RELEASE. - [Release notes](https://github.com/r2dbc/r2dbc-postgresql/releases) - [Changelog](https://github.com/pgjdbc/r2dbc-postgresql/blob/v0.8.4.RELEASE/CHANGELOG) - [Commits](https://github.com/r2dbc/r2dbc-postgresql/compare/v0.8.3.RELEASE...v0.8.4.RELEASE) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 7e6adea9cf1..8f30490eeb8 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -7,7 +7,7 @@ dependencies { compile project(':jdbc') compileOnly project(':r2dbc') - compileOnly 'io.r2dbc:r2dbc-postgresql:0.8.3.RELEASE' + compileOnly 'io.r2dbc:r2dbc-postgresql:0.8.4.RELEASE' testCompile project(':jdbc-test') testCompile project(':test-support') From d12d337e64e4dc4a76da5d04a0fe94547837155b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2020 20:46:46 +0100 Subject: [PATCH 1530/5615] Bump mariadb-java-client from 2.6.1 to 2.6.2 in /modules/mariadb (#3034) Bumps [mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 2.6.1 to 2.6.2. - [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases) - [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md) - [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/2.6.1...2.6.2) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index a4f2f951233..775854748dc 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'org.mariadb:r2dbc-mariadb:0.8.2-alpha2' testCompile project(':jdbc-test') - testCompile 'org.mariadb.jdbc:mariadb-java-client:2.6.1' + testCompile 'org.mariadb.jdbc:mariadb-java-client:2.6.2' testCompile testFixtures(project(':r2dbc')) testCompile 'org.mariadb:r2dbc-mariadb:0.8.1-alpha1' From 0f0eaee72b5ae6971e441549bbdb117f91be9d3b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2020 20:47:04 +0100 Subject: [PATCH 1531/5615] Bump r2dbc-mariadb from 0.8.2-alpha2 to 0.8.3-beta1 in /modules/mariadb (#3036) Bumps [r2dbc-mariadb](https://github.com/mariadb-corporation/mariadb-connector-r2dbc) from 0.8.2-alpha2 to 0.8.3-beta1. - [Release notes](https://github.com/mariadb-corporation/mariadb-connector-r2dbc/releases) - [Changelog](https://github.com/mariadb-corporation/mariadb-connector-r2dbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/mariadb-corporation/mariadb-connector-r2dbc/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/mariadb/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 775854748dc..94164468855 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -7,11 +7,11 @@ dependencies { compile project(':jdbc') compileOnly project(':r2dbc') - compileOnly 'org.mariadb:r2dbc-mariadb:0.8.2-alpha2' + compileOnly 'org.mariadb:r2dbc-mariadb:0.8.3-beta1' testCompile project(':jdbc-test') testCompile 'org.mariadb.jdbc:mariadb-java-client:2.6.2' testCompile testFixtures(project(':r2dbc')) - testCompile 'org.mariadb:r2dbc-mariadb:0.8.1-alpha1' + testCompile 'org.mariadb:r2dbc-mariadb:0.8.3-beta1' } From 495a7309ba2f20d76e3265176dfe9d6696238558 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2020 20:47:16 +0100 Subject: [PATCH 1532/5615] Bump mysql-connector-java from 8.0.20 to 8.0.21 in /modules/junit-jupiter (#3037) Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.20 to 8.0.21. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.20...8.0.21) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index ffc060d8b01..aedffacfc58 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -16,7 +16,7 @@ dependencies { testCompile 'org.junit.jupiter:junit-jupiter-params:5.6.2' testRuntime 'org.postgresql:postgresql:42.2.14' - testRuntime 'mysql:mysql-connector-java:8.0.20' + testRuntime 'mysql:mysql-connector-java:8.0.21' testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.6.2' } From b87f9930d83738880eda5674237ae88d44ffbf5f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2020 20:47:46 +0100 Subject: [PATCH 1533/5615] Bump org.springframework.boot from 2.3.1.RELEASE to 2.3.2.RELEASE in /examples (#3039) Bumps [org.springframework.boot](https://github.com/spring-projects/spring-boot) from 2.3.1.RELEASE to 2.3.2.RELEASE. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.3.1.RELEASE...v2.3.2.RELEASE) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index dc59ca877ab..e8a7861a7d9 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.3.1.RELEASE' + id 'org.springframework.boot' version '2.3.2.RELEASE' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index 060a7f48357..4821f0921e3 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.3.1.RELEASE' + id 'org.springframework.boot' version '2.3.2.RELEASE' } apply plugin: 'io.spring.dependency-management' From eb7d15e2ff665f062333e28d839d6ce412f0c735 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2020 20:47:55 +0100 Subject: [PATCH 1534/5615] Bump mockito-core from 3.3.3 to 3.4.4 in /modules/junit-jupiter (#3038) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index aedffacfc58..05ad7d34930 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,7 +9,7 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.4.5' testCompile 'redis.clients:jedis:3.3.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.12' - testCompile ('org.mockito:mockito-core:3.3.3') { + testCompile ('org.mockito:mockito-core:3.4.4') { exclude(module: 'hamcrest-core') } testCompile 'org.assertj:assertj-core:3.16.1' From 97f738ee3b85b43f7d3d3639140df9f18e9f0cdb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2020 20:48:14 +0100 Subject: [PATCH 1535/5615] Bump mysql-connector-java from 8.0.20 to 8.0.21 in /modules/mysql (#3045) Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.20 to 8.0.21. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.20...8.0.21) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index c9c165cc874..bf96ab89536 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'dev.miku:r2dbc-mysql:0.8.1.RELEASE' testCompile project(':jdbc-test') - testCompile 'mysql:mysql-connector-java:8.0.20' + testCompile 'mysql:mysql-connector-java:8.0.21' testCompile testFixtures(project(':r2dbc')) testCompile 'dev.miku:r2dbc-mysql:0.8.1.RELEASE' From e37b63278b3f62f33104849ce2b5e19d9197772f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2020 20:55:36 +0100 Subject: [PATCH 1536/5615] Bump mysql-connector-java from 8.0.20 to 8.0.21 in /modules/jdbc-test (#3044) Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.20 to 8.0.21. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.20...8.0.21) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index f19970c9380..d8df69b094f 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -17,5 +17,5 @@ dependencies { compile 'commons-dbutils:commons-dbutils:1.7' compile 'org.apache.tomcat:tomcat-jdbc:9.0.37' compile 'org.vibur:vibur-dbcp:25.0' - compile 'mysql:mysql-connector-java:8.0.20' + compile 'mysql:mysql-connector-java:8.0.21' } From b20eca8e2cc65d7aa98fe58d55a6af3d2893fd16 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2020 21:18:47 +0100 Subject: [PATCH 1537/5615] Bump aws-java-sdk-sqs from 1.11.812 to 1.11.831 in /modules/localstack (#3063) Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.11.812 to 1.11.831. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.812...1.11.831) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 3ce019b0549..3deb07dd139 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.816' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.816' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.812' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.831' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' testCompile 'software.amazon.awssdk:s3:2.13.61' } From 2b37cab5cfd7f9949fdc0eb64060cc4d8cfd250e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2020 21:34:00 +0100 Subject: [PATCH 1538/5615] Bump aws-java-sdk-s3 from 1.11.816 to 1.11.831 in /modules/localstack (#3064) Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.11.816 to 1.11.831. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.816...1.11.831) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 3deb07dd139..ad5808a97b2 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.816' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.831' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.816' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.831' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' From 65d35cea4bcb49736c3476d709f434a731c0a0de Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2020 21:44:25 +0100 Subject: [PATCH 1539/5615] Bump r2dbc-mysql from 0.8.1.RELEASE to 0.8.2.RELEASE in /modules/mysql (#3046) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index bf96ab89536..3badd2e18e1 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -7,7 +7,7 @@ dependencies { compile project(':jdbc') compileOnly project(':r2dbc') - compileOnly 'dev.miku:r2dbc-mysql:0.8.1.RELEASE' + compileOnly 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' testCompile project(':jdbc-test') testCompile 'mysql:mysql-connector-java:8.0.21' From aebc512d5659e98035290ba14df1f18ca4d94350 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2020 21:44:44 +0100 Subject: [PATCH 1540/5615] Bump mockito-core from 3.3.3 to 3.4.6 in /core (#3065) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.3.3 to 3.4.6. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.3.3...v3.4.6) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 98452944bd0..07cee319f5a 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -204,7 +204,7 @@ dependencies { testCompile 'com.rabbitmq:amqp-client:5.9.0' testCompile 'org.mongodb:mongo-java-driver:3.12.5' - testCompile ('org.mockito:mockito-core:3.3.3') { + testCompile ('org.mockito:mockito-core:3.4.6') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From 003c741e40da037f1b9247ab7e37cbac87e6f371 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Aug 2020 09:15:04 +0100 Subject: [PATCH 1541/5615] Bump mongo-java-driver from 3.12.5 to 3.12.6 in /core (#3024) Bumps [mongo-java-driver](https://github.com/mongodb/mongo-java-driver) from 3.12.5 to 3.12.6. - [Release notes](https://github.com/mongodb/mongo-java-driver/releases) - [Commits](https://github.com/mongodb/mongo-java-driver/compare/r3.12.5...r3.12.6) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 07cee319f5a..894990fe9a0 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -202,7 +202,7 @@ dependencies { testCompile 'org.apache.httpcomponents:httpclient:4.5.9' testCompile 'redis.clients:jedis:3.3.0' testCompile 'com.rabbitmq:amqp-client:5.9.0' - testCompile 'org.mongodb:mongo-java-driver:3.12.5' + testCompile 'org.mongodb:mongo-java-driver:3.12.6' testCompile ('org.mockito:mockito-core:3.4.6') { exclude(module: 'hamcrest-core') From 082b094d7edd0733c536f23208e55346d53abfb4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Aug 2020 09:16:05 +0100 Subject: [PATCH 1542/5615] Bump aws-java-sdk-dynamodb from 1.11.816 to 1.11.827 in /modules/dynalite (#3027) Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.11.816 to 1.11.827. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.816...1.11.827) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/dynalite/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index b3919a3eaf3..d04705cb4d4 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Dynalite" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.816' - testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.812' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.827' + testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.827' } From 0c69b448252ebd34e354289bcfb938767c7f7dd1 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Sat, 1 Aug 2020 16:44:34 +0200 Subject: [PATCH 1543/5615] Add some more logs to bash script used for DockerHealthcheckWaitStratgyTest (#2988) Co-authored-by: Sergei Egorov Co-authored-by: Richard North --- .../health-wait-strategy-dockerfile/write_file_and_loop.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/test/resources/health-wait-strategy-dockerfile/write_file_and_loop.sh b/core/src/test/resources/health-wait-strategy-dockerfile/write_file_and_loop.sh index a102129394b..3a688ee3f4f 100755 --- a/core/src/test/resources/health-wait-strategy-dockerfile/write_file_and_loop.sh +++ b/core/src/test/resources/health-wait-strategy-dockerfile/write_file_and_loop.sh @@ -1,8 +1,8 @@ #!/bin/ash -echo sleeping +set -ex + sleep 2 -echo writing file touch /testfile while true; do sleep 1; done From 135a61eb2b1fc5b36c1e0eea57bbe6efca641930 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Aug 2020 16:45:27 +0100 Subject: [PATCH 1544/5615] Bump kafka-clients from 2.5.0 to 2.6.0 in /modules/kafka (#3085) Bumps kafka-clients from 2.5.0 to 2.6.0. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index 06090c5b35e..3d8a663a0e9 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Kafka" dependencies { compile project(':testcontainers') - testCompile 'org.apache.kafka:kafka-clients:2.5.0' + testCompile 'org.apache.kafka:kafka-clients:2.6.0' testCompile 'org.assertj:assertj-core:3.16.1' testCompile 'com.google.guava:guava:23.0' } From 22eb3f6e126d71e402ae47b5cad980780f8137e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Aug 2020 16:45:47 +0100 Subject: [PATCH 1545/5615] Bump aws-java-sdk-s3 from 1.11.831 to 1.11.837 in /modules/localstack (#3088) Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.11.831 to 1.11.837. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.831...1.11.837) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index ad5808a97b2..372407baa41 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.831' - testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.816' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.837' + testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.837' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.831' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' testCompile 'software.amazon.awssdk:s3:2.13.61' From c8adfb5ad6829196fea3c8fd9eff148c23b83b98 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2020 08:17:22 +0100 Subject: [PATCH 1546/5615] Bump mongo-java-driver from 3.12.6 to 3.12.7 in /core (#3104) Bumps [mongo-java-driver](https://github.com/mongodb/mongo-java-driver) from 3.12.6 to 3.12.7. - [Release notes](https://github.com/mongodb/mongo-java-driver/releases) - [Commits](https://github.com/mongodb/mongo-java-driver/compare/r3.12.6...r3.12.7) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 894990fe9a0..81c0c20608c 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -202,7 +202,7 @@ dependencies { testCompile 'org.apache.httpcomponents:httpclient:4.5.9' testCompile 'redis.clients:jedis:3.3.0' testCompile 'com.rabbitmq:amqp-client:5.9.0' - testCompile 'org.mongodb:mongo-java-driver:3.12.6' + testCompile 'org.mongodb:mongo-java-driver:3.12.7' testCompile ('org.mockito:mockito-core:3.4.6') { exclude(module: 'hamcrest-core') From e99822c1b18bbde6c5aeeabab1477e8a3fc8d18b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2020 08:17:38 +0100 Subject: [PATCH 1547/5615] Bump mockito-core from 3.4.6 to 3.5.0 in /core (#3105) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.4.6 to 3.5.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.4.6...v3.5.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 81c0c20608c..4c1a47255ce 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -204,7 +204,7 @@ dependencies { testCompile 'com.rabbitmq:amqp-client:5.9.0' testCompile 'org.mongodb:mongo-java-driver:3.12.7' - testCompile ('org.mockito:mockito-core:3.4.6') { + testCompile ('org.mockito:mockito-core:3.5.0') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From 36f9e2be29641391df373566e904010522d41213 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2020 08:17:54 +0100 Subject: [PATCH 1548/5615] Bump postgresql from 42.2.14 to 42.2.15 in /examples (#3108) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.14 to 42.2.15. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.14...REL42.2.15) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 75bc7f3b0e8..9af26701f34 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' implementation 'com.squareup.okhttp3:okhttp:4.8.0' implementation 'org.json:json:20180813' - testImplementation 'org.postgresql:postgresql:42.2.14' + testImplementation 'org.postgresql:postgresql:42.2.15' testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.testcontainers:postgresql' } From 91c718d7951435f35e541fb54ba709591783e5c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2020 08:18:19 +0100 Subject: [PATCH 1549/5615] Bump cucumber-junit from 6.2.2 to 6.4.0 in /examples (#3109) Bumps [cucumber-junit](https://github.com/cucumber/cucumber-jvm) from 6.2.2 to 6.4.0. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v6.2.2...v6.4.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index ae211cedc04..6d56e68473e 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' testImplementation 'io.cucumber:cucumber-java:6.2.2' - testImplementation 'io.cucumber:cucumber-junit:6.2.2' + testImplementation 'io.cucumber:cucumber-junit:6.4.0' testImplementation 'org.testcontainers:selenium' } From 5653f0e80d0ed9e943a73ebdbd94ad005ea43c83 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2020 08:18:48 +0100 Subject: [PATCH 1550/5615] Bump okhttp from 4.8.0 to 4.8.1 in /examples (#3106) Bumps [okhttp](https://github.com/square/okhttp) from 4.8.0 to 4.8.1. - [Release notes](https://github.com/square/okhttp/releases) - [Changelog](https://github.com/square/okhttp/blob/master/CHANGELOG.md) - [Commits](https://github.com/square/okhttp/compare/parent-4.8.0...parent-4.8.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 9af26701f34..a62854e9b35 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' - implementation 'com.squareup.okhttp3:okhttp:4.8.0' + implementation 'com.squareup.okhttp3:okhttp:4.8.1' implementation 'org.json:json:20180813' testImplementation 'org.postgresql:postgresql:42.2.15' testImplementation 'ch.qos.logback:logback-classic:1.2.3' From 82bc1658c55ff3828687cd2e9aa1e3d48e395535 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2020 08:19:12 +0100 Subject: [PATCH 1551/5615] Bump aws-java-sdk-dynamodb from 1.11.827 to 1.11.837 in /modules/dynalite (#3090) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/dynalite/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index d04705cb4d4..fce2e17bd88 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Dynalite" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.827' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.837' testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.827' } From 36f31d6a759eda6b4177d2c0695b65d090702a4f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2020 08:21:33 +0100 Subject: [PATCH 1552/5615] Bump solr-solrj from 8.5.2 to 8.6.1 in /examples (#3107) Bumps solr-solrj from 8.5.2 to 8.6.1. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/solr-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/solr-container/build.gradle b/examples/solr-container/build.gradle index 5845753cb08..89444aeb378 100644 --- a/examples/solr-container/build.gradle +++ b/examples/solr-container/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly "org.projectlombok:lombok:1.18.10" annotationProcessor "org.projectlombok:lombok:1.18.10" - implementation 'org.apache.solr:solr-solrj:8.5.2' + implementation 'org.apache.solr:solr-solrj:8.6.1' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.testcontainers:solr' From 8de8a0a2e323a5962410194ad3b49ecf10cc2521 Mon Sep 17 00:00:00 2001 From: Artjom Kalita Date: Thu, 20 Aug 2020 10:23:42 +0300 Subject: [PATCH 1553/5615] Whitespace configuration fix (#3127, fixes #3053) Co-authored-by: artjom.kalita --- .../utility/TestcontainersConfiguration.java | 24 ++++++++++--------- .../TestcontainersConfigurationTest.java | 4 ++++ 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 1939e8e21b3..9f283ba2685 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -37,7 +37,7 @@ public class TestcontainersConfiguration { private static File ENVIRONMENT_CONFIG_FILE = new File(System.getProperty("user.home"), "." + PROPERTIES_FILE_NAME); @Getter(lazy = true) - private static final TestcontainersConfiguration instance = loadConfiguration();; + private static final TestcontainersConfiguration instance = loadConfiguration(); @SuppressWarnings({"ConstantConditions", "unchecked", "rawtypes"}) @VisibleForTesting @@ -56,16 +56,18 @@ static AtomicReference getInstanceField() { this.properties.putAll(classpathProperties); this.properties.putAll(environmentProperties); + properties.keySet() + .forEach(key -> properties.replace(key, properties.getProperty(String.valueOf(key)).trim())); } @Deprecated public String getAmbassadorContainerImage() { - return (String) properties.getOrDefault("ambassador.container.image", "richnorth/ambassador:latest"); + return String.valueOf(properties.getOrDefault("ambassador.container.image", "richnorth/ambassador:latest")); } @Deprecated public String getSocatContainerImage() { - return (String) properties.getOrDefault("socat.container.image", "alpine/socat:latest"); + return String.valueOf(properties.getOrDefault("socat.container.image", "alpine/socat:latest")); } public DockerImageName getSocatDockerImageName() { @@ -74,7 +76,7 @@ public DockerImageName getSocatDockerImageName() { @Deprecated public String getVncRecordedContainerImage() { - return (String) properties.getOrDefault("vncrecorder.container.image", "testcontainers/vnc-recorder:1.1.0"); + return String.valueOf(properties.getOrDefault("vncrecorder.container.image", "testcontainers/vnc-recorder:1.1.0")); } public DockerImageName getVncDockerImageName() { @@ -83,7 +85,7 @@ public DockerImageName getVncDockerImageName() { @Deprecated public String getDockerComposeContainerImage() { - return (String) properties.getOrDefault("compose.container.image", "docker/compose:1.24.1"); + return String.valueOf(properties.getOrDefault("compose.container.image", "docker/compose:1.24.1")); } public DockerImageName getDockerComposeDockerImageName() { @@ -92,7 +94,7 @@ public DockerImageName getDockerComposeDockerImageName() { @Deprecated public String getTinyImage() { - return (String) properties.getOrDefault("tinyimage.container.image", "alpine:3.5"); + return String.valueOf(properties.getOrDefault("tinyimage.container.image", "alpine:3.5")); } public DockerImageName getTinyDockerImageName() { @@ -105,7 +107,7 @@ public boolean isRyukPrivileged() { @Deprecated public String getRyukImage() { - return (String) properties.getOrDefault("ryuk.container.image", "testcontainers/ryuk:0.3.0"); + return String.valueOf(properties.getOrDefault("ryuk.container.image", "testcontainers/ryuk:0.3.0")); } public DockerImageName getRyukDockerImageName() { @@ -114,7 +116,7 @@ public DockerImageName getRyukDockerImageName() { @Deprecated public String getSSHdImage() { - return (String) properties.getOrDefault("sshd.container.image", "testcontainers/sshd:1.0.0"); + return String.valueOf(properties.getOrDefault("sshd.container.image", "testcontainers/sshd:1.0.0")); } public DockerImageName getSSHdDockerImageName() { @@ -127,7 +129,7 @@ public Integer getRyukTimeout() { @Deprecated public String getKafkaImage() { - return (String) properties.getOrDefault("kafka.container.image", "confluentinc/cp-kafka"); + return String.valueOf(properties.getOrDefault("kafka.container.image", "confluentinc/cp-kafka")); } public DockerImageName getKafkaDockerImageName() { @@ -136,7 +138,7 @@ public DockerImageName getKafkaDockerImageName() { @Deprecated public String getPulsarImage() { - return (String) properties.getOrDefault("pulsar.container.image", "apachepulsar/pulsar"); + return String.valueOf(properties.getOrDefault("pulsar.container.image", "apachepulsar/pulsar")); } public DockerImageName getPulsarDockerImageName() { @@ -145,7 +147,7 @@ public DockerImageName getPulsarDockerImageName() { @Deprecated public String getLocalStackImage() { - return (String) properties.getOrDefault("localstack.container.image", "localstack/localstack"); + return String.valueOf(properties.getOrDefault("localstack.container.image", "localstack/localstack")); } public DockerImageName getLocalstackDockerImageName() { diff --git a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java index 49a378554f1..f0b8fb36a37 100644 --- a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java +++ b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java @@ -46,6 +46,10 @@ public void shouldReadReuseFromEnvironmentOnly() { environmentProperties.setProperty("testcontainers.reuse.enable", "true"); assertTrue("reuse enabled", newConfig().environmentSupportsReuse()); + + environmentProperties.setProperty("ryuk.container.image", " testcontainersofficial/ryuk:0.3.0 "); + assertEquals("trailing whitespace was not removed from image name property", "testcontainersofficial/ryuk:0.3.0",newConfig().getRyukImage()); + } private TestcontainersConfiguration newConfig() { From 14a6a09eb7893e8e356ffa4ced29a9d098b523e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2020 08:52:41 +0100 Subject: [PATCH 1554/5615] Bump s3 from 2.13.61 to 2.13.76 in /modules/localstack (#3114) Bumps s3 from 2.13.61 to 2.13.76. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 372407baa41..f77d286bfca 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,5 +7,5 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.837' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.831' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' - testCompile 'software.amazon.awssdk:s3:2.13.61' + testCompile 'software.amazon.awssdk:s3:2.13.76' } From 37df788493c470d7994aea463d777a5de9559027 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 20 Aug 2020 09:05:53 +0100 Subject: [PATCH 1555/5615] Add continue-on-error for cache step, and upgrade GH cache action (#3133) --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 668dde2b28c..2b5a37a3405 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,7 +46,8 @@ jobs: with: java-version: '1.8' - name: Cache Gradle Home files - uses: actions/cache@v1 + uses: actions/cache@v2 + continue-on-error: true with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-home-${{matrix.gradle_args}}_check-${{ hashFiles('**/*.gradle') }} From 2543ea885427d61cec16ae1e0c72a964a928f691 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2020 09:06:19 +0100 Subject: [PATCH 1556/5615] Bump aws-java-sdk-s3 from 1.11.837 to 1.11.846 in /modules/localstack (#3131) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index f77d286bfca..d018846c1aa 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.837' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.846' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.837' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.831' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' From d9dd5a7692ffcbbf0ce1d3eccb175c5b1dead2ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2020 09:06:38 +0100 Subject: [PATCH 1557/5615] Bump aws-java-sdk-sqs from 1.11.831 to 1.11.846 in /modules/localstack (#3132) Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.11.831 to 1.11.846. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.831...1.11.846) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index d018846c1aa..9891879fc1a 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.846' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.837' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.831' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.846' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' testCompile 'software.amazon.awssdk:s3:2.13.76' } From 09479d1df577c408f61bdea40c6808f30db02348 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2020 09:06:54 +0100 Subject: [PATCH 1558/5615] Bump cucumber-java from 6.2.2 to 6.4.0 in /examples (#3112) Bumps [cucumber-java](https://github.com/cucumber/cucumber-jvm) from 6.2.2 to 6.4.0. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v6.2.2...v6.4.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 6d56e68473e..90cc99e7627 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' - testImplementation 'io.cucumber:cucumber-java:6.2.2' + testImplementation 'io.cucumber:cucumber-java:6.4.0' testImplementation 'io.cucumber:cucumber-junit:6.4.0' testImplementation 'org.testcontainers:selenium' } From 58c4cf2d6e8b33046c3ca0ec206acef8f4070065 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2020 09:07:10 +0100 Subject: [PATCH 1559/5615] Bump postgresql from 42.2.14 to 42.2.15 in /modules/postgresql (#3113) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.14 to 42.2.15. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.14...REL42.2.15) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 8f30490eeb8..894930bc258 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -11,7 +11,7 @@ dependencies { testCompile project(':jdbc-test') testCompile project(':test-support') - testCompile 'org.postgresql:postgresql:42.2.14' + testCompile 'org.postgresql:postgresql:42.2.15' testCompile testFixtures(project(':r2dbc')) testCompile 'io.r2dbc:r2dbc-postgresql:0.8.3.RELEASE' From 9790538a45f8104dc8e24f26fc5ad5028dd0a498 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2020 09:07:25 +0100 Subject: [PATCH 1560/5615] Bump postgresql from 42.2.14 to 42.2.15 in /modules/junit-jupiter (#3115) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.14 to 42.2.15. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.14...REL42.2.15) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 05ad7d34930..ebc03695d0d 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -15,7 +15,7 @@ dependencies { testCompile 'org.assertj:assertj-core:3.16.1' testCompile 'org.junit.jupiter:junit-jupiter-params:5.6.2' - testRuntime 'org.postgresql:postgresql:42.2.14' + testRuntime 'org.postgresql:postgresql:42.2.15' testRuntime 'mysql:mysql-connector-java:8.0.21' testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.6.2' } From 47f3fb71833fc388796ec9fa287908192045b29a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2020 09:09:12 +0100 Subject: [PATCH 1561/5615] Bump mockito-core from 3.4.4 to 3.5.2 in /modules/junit-jupiter (#3130) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.4.4 to 3.5.2. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.4.4...v3.5.2) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index ebc03695d0d..6e37336545d 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,7 +9,7 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.4.5' testCompile 'redis.clients:jedis:3.3.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.12' - testCompile ('org.mockito:mockito-core:3.4.4') { + testCompile ('org.mockito:mockito-core:3.5.2') { exclude(module: 'hamcrest-core') } testCompile 'org.assertj:assertj-core:3.16.1' From 0b6007956ca3f794aad0cfe43be4dfaca6b0c111 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2020 09:09:23 +0100 Subject: [PATCH 1562/5615] Bump org.springframework.boot from 2.3.2.RELEASE to 2.3.3.RELEASE in /examples (#3111) Bumps [org.springframework.boot](https://github.com/spring-projects/spring-boot) from 2.3.2.RELEASE to 2.3.3.RELEASE. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.3.2.RELEASE...v2.3.3.RELEASE) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index e8a7861a7d9..eb9c2d9ce3a 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.3.2.RELEASE' + id 'org.springframework.boot' version '2.3.3.RELEASE' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index 4821f0921e3..d74851d8eac 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.3.2.RELEASE' + id 'org.springframework.boot' version '2.3.3.RELEASE' } apply plugin: 'io.spring.dependency-management' From b1a1dd1c422b2c1f445df81283bf0d1364a72cdc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2020 10:04:27 +0100 Subject: [PATCH 1563/5615] Bump influxdb-java from 2.19 to 2.20 in /modules/influxdb (#3120) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/influxdb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/influxdb/build.gradle b/modules/influxdb/build.gradle index 2deacd3a149..40fa6e456ce 100644 --- a/modules/influxdb/build.gradle +++ b/modules/influxdb/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: InfluxDB" dependencies { compile project(':testcontainers') - compileOnly 'org.influxdb:influxdb-java:2.19' + compileOnly 'org.influxdb:influxdb-java:2.20' testCompile 'org.influxdb:influxdb-java:2.19' } From 6bdbc8ffe257fc879791eb7fddcf3b5ecccdd52d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2020 10:04:35 +0100 Subject: [PATCH 1564/5615] Bump postgresql from 42.2.14 to 42.2.15 in /modules/spock (#3119) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.14 to 42.2.15. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.14...REL42.2.15) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 1a9eddf4ffc..eb2914ae3ac 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -15,7 +15,7 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.4.5' testCompile 'org.apache.httpcomponents:httpclient:4.5.12' - testRuntime 'org.postgresql:postgresql:42.2.14' + testRuntime 'org.postgresql:postgresql:42.2.15' testRuntime 'mysql:mysql-connector-java:8.0.21' } From 93504a5ca04f5d86c604242124fe5e96ae553bf8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Aug 2020 11:57:58 +0100 Subject: [PATCH 1565/5615] Bump assertj-core from 3.16.1 to 3.17.0 in /modules/pulsar (#3153) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.16.1 to 3.17.0. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.16.1...assertj-core-3.17.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index a8f55696a65..493c5fa4c32 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -4,6 +4,6 @@ dependencies { compile project(':testcontainers') testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.6.0' - testCompile group: 'org.assertj', name: 'assertj-core', version: '3.16.1' + testCompile group: 'org.assertj', name: 'assertj-core', version: '3.17.0' testCompile group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.6.0' } From 2459cab02a914db9a1f554104a13b5180d441dc6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Aug 2020 11:58:27 +0100 Subject: [PATCH 1566/5615] Bump aws-java-sdk-dynamodb from 1.11.837 to 1.11.847 in /modules/dynalite (#3150) Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.11.837 to 1.11.847. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.837...1.11.847) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/dynalite/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index fce2e17bd88..762e189173b 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Dynalite" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.837' - testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.827' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.847' + testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.847' } From 4f056827712a475e0db0d45380f1d0d10e45870a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Aug 2020 11:58:39 +0100 Subject: [PATCH 1567/5615] Bump aws-java-sdk-s3 from 1.11.846 to 1.11.847 in /modules/localstack (#3148) Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.11.846 to 1.11.847. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.846...1.11.847) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 9891879fc1a..acf42539c88 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.846' - testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.837' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.847' + testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.847' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.846' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' testCompile 'software.amazon.awssdk:s3:2.13.76' From b236eacd54f6362a138f2582f04501cdb8bab5e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Aug 2020 12:00:11 +0100 Subject: [PATCH 1568/5615] Bump postgresql from 42.2.15 to 42.2.16 in /modules/junit-jupiter (#3142) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.15 to 42.2.16. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.15...REL42.2.16) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 6e37336545d..28f48cf94bb 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -15,7 +15,7 @@ dependencies { testCompile 'org.assertj:assertj-core:3.16.1' testCompile 'org.junit.jupiter:junit-jupiter-params:5.6.2' - testRuntime 'org.postgresql:postgresql:42.2.15' + testRuntime 'org.postgresql:postgresql:42.2.16' testRuntime 'mysql:mysql-connector-java:8.0.21' testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.6.2' } From c42c8eebad51206be675ad330d5c8aefe07f230d Mon Sep 17 00:00:00 2001 From: Waldemar Panas <8766331+wpanas@users.noreply.github.com> Date: Fri, 28 Aug 2020 13:01:35 +0200 Subject: [PATCH 1569/5615] #3129 Fix dead link for CircleCI configuration (#3156) Co-authored-by: wpanas --- docs/supported_docker_environment/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/supported_docker_environment/index.md b/docs/supported_docker_environment/index.md index 7622a5051cd..04cc0c9ee2b 100644 --- a/docs/supported_docker_environment/index.md +++ b/docs/supported_docker_environment/index.md @@ -6,7 +6,7 @@ |-------------------------------------|-----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Linux - general | Docker v1.10 | | | Linux - Travis CI | Docker v1.10 | See [example .travis.yml](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/.travis.yml) for baseline Travis CI configuration | -| Linux - Circle CI (LXC driver) | Docker v1.9.1 | The `exec` feature is not compatible with Circle CI. See [example circle.yml](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/circle.yml) for baseline CircleCI configuration | +| Linux - CircleCI (LXC driver) | Docker v1.9.1 | The `exec` feature is not compatible with CircleCI. See CircleCI configuration [example](./continuous_integration/circle_ci.md) | | Linux - within a Docker container | Docker v1.12 | See [Running inside Docker](continuous_integration/dind_patterns.md) for Docker-in-Docker and Docker wormhole patterns | | Mac OS X - Docker Toolbox | Docker Machine v0.8.0 | | | Mac OS X - Docker for Mac | 1.12.0 | *Support is best-efforts at present*. `getTestHostIpAddress()` is [not currently supported](https://github.com/testcontainers/testcontainers-java/issues/166) due to limitations in Docker for Mac. | From 21af1b9045f2998588375a90d975ac8aca95535c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Aug 2020 12:01:54 +0100 Subject: [PATCH 1570/5615] Bump elasticsearch-rest-client from 7.8.0 to 7.9.0 in /modules/elasticsearch (#3152) Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 7.8.0 to 7.9.0. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.8.0...v7.9.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 591760d7b0e..20097bd19b4 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,6 +2,6 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') - testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.8.0" + testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.9.0" testCompile "org.elasticsearch.client:transport:6.7.1" } From 1c442c9063474f1c4b722a768dec1f4f2a737346 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Aug 2020 12:24:52 +0100 Subject: [PATCH 1571/5615] Bump pulsar-client-admin from 2.6.0 to 2.6.1 in /modules/pulsar (#3154) Bumps [pulsar-client-admin](https://github.com/apache/pulsar) from 2.6.0 to 2.6.1. - [Release notes](https://github.com/apache/pulsar/releases) - [Commits](https://github.com/apache/pulsar/compare/v2.6.0...v2.6.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 493c5fa4c32..9aaed8e23fd 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -5,5 +5,5 @@ dependencies { testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.6.0' testCompile group: 'org.assertj', name: 'assertj-core', version: '3.17.0' - testCompile group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.6.0' + testCompile group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.6.1' } From 698738cacf662ef2f626f8e59b78b559ef5a14ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Aug 2020 12:25:04 +0100 Subject: [PATCH 1572/5615] Bump assertj-core from 3.16.1 to 3.17.0 in /modules/vault (#3146) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.16.1 to 3.17.0. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.16.1...assertj-core-3.17.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index 0d094905198..5c83cbbb56f 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -5,6 +5,6 @@ dependencies { testCompile 'com.bettercloud:vault-java-driver:5.1.0' testCompile 'io.rest-assured:rest-assured:4.3.1' - testCompile 'org.assertj:assertj-core:3.16.1' + testCompile 'org.assertj:assertj-core:3.17.0' } From b5bf40cc58a13ea18c02a36764e40b1528869609 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Aug 2020 12:25:11 +0100 Subject: [PATCH 1573/5615] Bump postgresql from 42.2.15 to 42.2.16 in /modules/postgresql (#3145) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.15 to 42.2.16. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.15...REL42.2.16) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 894930bc258..720c1d493a5 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -11,7 +11,7 @@ dependencies { testCompile project(':jdbc-test') testCompile project(':test-support') - testCompile 'org.postgresql:postgresql:42.2.15' + testCompile 'org.postgresql:postgresql:42.2.16' testCompile testFixtures(project(':r2dbc')) testCompile 'io.r2dbc:r2dbc-postgresql:0.8.3.RELEASE' From a0521e8e8e9a61d63e0fea81fd9dfabdc0df64fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Aug 2020 12:25:20 +0100 Subject: [PATCH 1574/5615] Bump assertj-core from 3.16.1 to 3.17.0 in /modules/junit-jupiter (#3144) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.16.1 to 3.17.0. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.16.1...assertj-core-3.17.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 28f48cf94bb..3aa19ddbfce 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -12,7 +12,7 @@ dependencies { testCompile ('org.mockito:mockito-core:3.5.2') { exclude(module: 'hamcrest-core') } - testCompile 'org.assertj:assertj-core:3.16.1' + testCompile 'org.assertj:assertj-core:3.17.0' testCompile 'org.junit.jupiter:junit-jupiter-params:5.6.2' testRuntime 'org.postgresql:postgresql:42.2.16' From 41cfc6ef1c6695065d1e84a90b873361ae0c99e5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Aug 2020 12:25:34 +0100 Subject: [PATCH 1575/5615] Bump mockito-core from 3.5.2 to 3.5.5 in /modules/junit-jupiter (#3143) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.5.2 to 3.5.5. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.5.2...v3.5.5) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 3aa19ddbfce..d0c53a63f39 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,7 +9,7 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.4.5' testCompile 'redis.clients:jedis:3.3.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.12' - testCompile ('org.mockito:mockito-core:3.5.2') { + testCompile ('org.mockito:mockito-core:3.5.5') { exclude(module: 'hamcrest-core') } testCompile 'org.assertj:assertj-core:3.17.0' From 6bca821edffef1680e045982bcf74e40560b3631 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Aug 2020 12:25:42 +0100 Subject: [PATCH 1576/5615] Bump pulsar-client from 2.6.0 to 2.6.1 in /modules/pulsar (#3155) Bumps [pulsar-client](https://github.com/apache/pulsar) from 2.6.0 to 2.6.1. - [Release notes](https://github.com/apache/pulsar/releases) - [Commits](https://github.com/apache/pulsar/compare/v2.6.0...v2.6.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 9aaed8e23fd..a8f0d9f48ed 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Pulsar" dependencies { compile project(':testcontainers') - testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.6.0' + testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.6.1' testCompile group: 'org.assertj', name: 'assertj-core', version: '3.17.0' testCompile group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.6.1' } From 86fa31b815717d98a1e7eda7cfd1b5a6e05a2a86 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Aug 2020 13:27:35 +0100 Subject: [PATCH 1577/5615] Bump mockito-core from 3.5.0 to 3.5.7 in /core (#3164) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.5.0 to 3.5.7. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.5.0...v3.5.7) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 4c1a47255ce..68bc339593a 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -204,7 +204,7 @@ dependencies { testCompile 'com.rabbitmq:amqp-client:5.9.0' testCompile 'org.mongodb:mongo-java-driver:3.12.7' - testCompile ('org.mockito:mockito-core:3.5.0') { + testCompile ('org.mockito:mockito-core:3.5.7') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From fbc2b4ddd9521661981d98d9f88c317978dffd7c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Aug 2020 13:27:44 +0100 Subject: [PATCH 1578/5615] Bump postgresql from 42.2.15 to 42.2.16 in /modules/spock (#3138) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.15 to 42.2.16. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.15...REL42.2.16) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index eb2914ae3ac..04a6682ee2f 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -15,7 +15,7 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.4.5' testCompile 'org.apache.httpcomponents:httpclient:4.5.12' - testRuntime 'org.postgresql:postgresql:42.2.15' + testRuntime 'org.postgresql:postgresql:42.2.16' testRuntime 'mysql:mysql-connector-java:8.0.21' } From 7bcfc6feb0c3741def110118a11974a69ba4a932 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Aug 2020 13:27:56 +0100 Subject: [PATCH 1579/5615] Bump mockito-core from 3.5.5 to 3.5.7 in /modules/junit-jupiter (#3163) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.5.5 to 3.5.7. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.5.5...v3.5.7) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index d0c53a63f39..d1368c2389a 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,7 +9,7 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.4.5' testCompile 'redis.clients:jedis:3.3.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.12' - testCompile ('org.mockito:mockito-core:3.5.5') { + testCompile ('org.mockito:mockito-core:3.5.7') { exclude(module: 'hamcrest-core') } testCompile 'org.assertj:assertj-core:3.17.0' From 0f9ca21f11ddce2ff75a88b973dd0f71da632e00 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Aug 2020 13:28:04 +0100 Subject: [PATCH 1580/5615] Bump assertj-core from 3.16.1 to 3.17.0 in /modules/kafka (#3139) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.16.1 to 3.17.0. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.16.1...assertj-core-3.17.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index 3d8a663a0e9..a2b73ff4281 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -4,6 +4,6 @@ dependencies { compile project(':testcontainers') testCompile 'org.apache.kafka:kafka-clients:2.6.0' - testCompile 'org.assertj:assertj-core:3.16.1' + testCompile 'org.assertj:assertj-core:3.17.0' testCompile 'com.google.guava:guava:23.0' } From f42d3df752ba67e5f8a21438291286023f9abc65 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Aug 2020 13:28:20 +0100 Subject: [PATCH 1581/5615] Bump s3 from 2.13.76 to 2.14.6 in /modules/localstack (#3162) Bumps s3 from 2.13.76 to 2.14.6. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index acf42539c88..e28117319f5 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,5 +7,5 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.847' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.846' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' - testCompile 'software.amazon.awssdk:s3:2.13.76' + testCompile 'software.amazon.awssdk:s3:2.14.6' } From 5397ddb51e3fcf3930910ba7f9adbbc4c97e6bb2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Aug 2020 13:28:30 +0100 Subject: [PATCH 1582/5615] Bump assertj-core from 3.16.1 to 3.17.0 in /modules/neo4j (#3137) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.16.1 to 3.17.0. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.16.1...assertj-core-3.17.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 4bc92e49cda..2f5e6b05d9f 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -34,5 +34,5 @@ dependencies { compile project(":testcontainers") testCompile "org.neo4j.driver:neo4j-java-driver:1.7.5" - testCompile 'org.assertj:assertj-core:3.16.1' + testCompile 'org.assertj:assertj-core:3.17.0' } From 87571b6ad844705e9c622144595917faebe82bf3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Aug 2020 15:13:09 +0100 Subject: [PATCH 1583/5615] Bump assertj-core from 3.16.1 to 3.17.0 in /modules/database-commons (#3141) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.16.1 to 3.17.0. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.16.1...assertj-core-3.17.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/database-commons/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/database-commons/build.gradle b/modules/database-commons/build.gradle index 82e318e5017..1ab8d67cf02 100644 --- a/modules/database-commons/build.gradle +++ b/modules/database-commons/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: Database-Commons" dependencies { compile project(':testcontainers') - testCompile 'org.assertj:assertj-core:3.16.1' + testCompile 'org.assertj:assertj-core:3.17.0' } From 8bc3bf89e0551b0d5ef602433a76b5fd76d83f91 Mon Sep 17 00:00:00 2001 From: Tobias Gesellchen Date: Sat, 29 Aug 2020 09:52:14 +0200 Subject: [PATCH 1584/5615] Fix bind mounts for ResourceReaper/ryuk and ContainerisedDockerCompose on macOS (#3159) Relates to #545 Relates to #2998 --- .../main/java/org/testcontainers/DockerClientFactory.java | 6 +++++- .../testcontainers/containers/DockerComposeContainer.java | 2 +- .../java/org/testcontainers/utility/ResourceReaper.java | 2 +- .../containers/localstack/LocalStackContainer.java | 3 ++- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 1905fc7c098..571b18fbcce 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -19,6 +19,7 @@ import lombok.Synchronized; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.SystemUtils; import org.testcontainers.dockerclient.DockerClientProviderStrategy; import org.testcontainers.dockerclient.DockerMachineClientProviderStrategy; import org.testcontainers.dockerclient.TransportConfig; @@ -145,9 +146,12 @@ public String getRemoteDockerUnixSocketPath() { } URI dockerHost = getTransportConfig().getDockerHost(); - return "unix".equals(dockerHost.getScheme()) + String path = "unix".equals(dockerHost.getScheme()) ? dockerHost.getRawPath() : "/var/run/docker.sock"; + return SystemUtils.IS_OS_WINDOWS + ? "/" + path + : path; } /** diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index af1da29442f..d2702b3db5b 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -608,7 +608,7 @@ public ContainerisedDockerCompose(List composeFiles, String identifier) { // as the docker daemon, just mapping the docker control socket is OK. // As there seems to be a problem with mapping to the /var/run directory in certain environments (e.g. CircleCI) // we map the socket file outside of /var/run, as just /docker.sock - addFileSystemBind("/" + DockerClientFactory.instance().getRemoteDockerUnixSocketPath(), "/docker.sock", READ_WRITE); + addFileSystemBind(DockerClientFactory.instance().getRemoteDockerUnixSocketPath(), "/docker.sock", READ_WRITE); addEnv("DOCKER_HOST", "unix:///docker.sock"); setStartupCheckStrategy(new IndefiniteWaitOneShotStartupCheckStrategy()); setWorkingDirectory(containerPwd); diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index 3448e73dce3..c37e6660654 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -76,7 +76,7 @@ public static String start(String hostIpAddress, DockerClient client) { DockerClientFactory.instance().checkAndPullImage(client, ryukImage); List binds = new ArrayList<>(); - binds.add(new Bind("/" + DockerClientFactory.instance().getRemoteDockerUnixSocketPath(), new Volume("/var/run/docker.sock"))); + binds.add(new Bind(DockerClientFactory.instance().getRemoteDockerUnixSocketPath(), new Volume("/var/run/docker.sock"))); String ryukContainerId = client.createContainerCmd(ryukImage) .withHostConfig(new HostConfig().withAutoRemove(true)) diff --git a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java index 3a9fab26ae4..a9547eec94d 100644 --- a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java +++ b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java @@ -17,6 +17,7 @@ import lombok.experimental.FieldDefaults; import lombok.extern.slf4j.Slf4j; import org.rnorth.ducttape.Preconditions; +import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.utility.ComparableVersion; @@ -84,7 +85,7 @@ public LocalStackContainer(final DockerImageName dockerImageName, boolean useLeg super(dockerImageName); this.legacyMode = useLegacyMode; - withFileSystemBind("//var/run/docker.sock", "/var/run/docker.sock"); + withFileSystemBind(DockerClientFactory.instance().getRemoteDockerUnixSocketPath(), "/var/run/docker.sock"); waitingFor(Wait.forLogMessage(".*Ready\\.\n", 1)); } From 6fff500b1618759f64dd4d0882e52932ef06652b Mon Sep 17 00:00:00 2001 From: Waldemar Panas <8766331+wpanas@users.noreply.github.com> Date: Tue, 1 Sep 2020 12:51:16 +0200 Subject: [PATCH 1585/5615] #3057 Fix jetbrains annotations configuration scope (#3157) Co-authored-by: wpanas --- core/build.gradle | 3 ++- examples/mongodb-container/build.gradle | 1 + modules/jdbc/build.gradle | 1 + modules/mysql/build.gradle | 2 ++ modules/nginx/build.gradle | 1 + modules/postgresql/build.gradle | 2 ++ modules/presto/build.gradle | 1 + modules/rabbitmq/build.gradle | 3 ++- modules/selenium/build.gradle | 3 +++ modules/spock/build.gradle | 2 ++ 10 files changed, 17 insertions(+), 2 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index 68bc339593a..bb27f4378e4 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -164,7 +164,8 @@ dependencies { compile 'junit:junit:4.12' compile 'org.slf4j:slf4j-api:1.7.30' - compile 'org.jetbrains:annotations:19.0.0' + compileOnly 'org.jetbrains:annotations:20.0.0' + testCompileOnly 'org.jetbrains:annotations:20.0.0' compile 'org.apache.commons:commons-compress:1.20' compile ('org.rnorth.duct-tape:duct-tape:1.0.8') { exclude(group: 'org.jetbrains', module: 'annotations') diff --git a/examples/mongodb-container/build.gradle b/examples/mongodb-container/build.gradle index d3b85856b8a..2c6b0bbcc13 100644 --- a/examples/mongodb-container/build.gradle +++ b/examples/mongodb-container/build.gradle @@ -9,4 +9,5 @@ repositories { dependencies { testImplementation 'org.testcontainers:testcontainers' testImplementation 'junit:junit:4.13' + testCompileOnly 'org.jetbrains:annotations:20.0.0' } diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index cd10c7a98c3..33ff3e8339f 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -4,6 +4,7 @@ dependencies { compile project(':database-commons') testCompile project(':test-support') + compileOnly 'org.jetbrains:annotations:20.0.0' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.vibur:vibur-dbcp:25.0' testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.37' diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 3badd2e18e1..545037d2dff 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -14,4 +14,6 @@ dependencies { testCompile testFixtures(project(':r2dbc')) testCompile 'dev.miku:r2dbc-mysql:0.8.1.RELEASE' + + compileOnly 'org.jetbrains:annotations:20.0.0' } diff --git a/modules/nginx/build.gradle b/modules/nginx/build.gradle index e5003b4e103..b56d7f2b4e8 100644 --- a/modules/nginx/build.gradle +++ b/modules/nginx/build.gradle @@ -2,4 +2,5 @@ description = "Testcontainers :: Nginx" dependencies { compile project(':testcontainers') + compileOnly 'org.jetbrains:annotations:20.0.0' } diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 720c1d493a5..6437bd9c2f8 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -15,4 +15,6 @@ dependencies { testCompile testFixtures(project(':r2dbc')) testCompile 'io.r2dbc:r2dbc-postgresql:0.8.3.RELEASE' + + compileOnly 'org.jetbrains:annotations:20.0.0' } diff --git a/modules/presto/build.gradle b/modules/presto/build.gradle index 51c5bf4345e..4130fb74891 100644 --- a/modules/presto/build.gradle +++ b/modules/presto/build.gradle @@ -5,4 +5,5 @@ dependencies { testCompile project(':jdbc-test') testCompile 'io.prestosql:presto-jdbc:331' + compileOnly 'org.jetbrains:annotations:20.0.0' } diff --git a/modules/rabbitmq/build.gradle b/modules/rabbitmq/build.gradle index 7b9a5274a7e..73524c2da18 100644 --- a/modules/rabbitmq/build.gradle +++ b/modules/rabbitmq/build.gradle @@ -2,6 +2,7 @@ description = "TestContainers :: RabbitMQ" dependencies { compile project(":testcontainers") - testCompile "com.rabbitmq:amqp-client:5.7.0" + testCompile 'com.rabbitmq:amqp-client:5.7.0' testCompile 'org.assertj:assertj-core:3.12.2' + compileOnly 'org.jetbrains:annotations:20.0.0' } diff --git a/modules/selenium/build.gradle b/modules/selenium/build.gradle index f7fcb75107d..a51735ae78a 100644 --- a/modules/selenium/build.gradle +++ b/modules/selenium/build.gradle @@ -10,4 +10,7 @@ dependencies { testCompile 'org.mortbay.jetty:jetty:6.1.26' testCompile project(':nginx') + + compileOnly 'org.jetbrains:annotations:20.0.0' + testCompileOnly 'org.jetbrains:annotations:20.0.0' } diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 04a6682ee2f..c559b623b55 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -17,6 +17,8 @@ dependencies { testRuntime 'org.postgresql:postgresql:42.2.16' testRuntime 'mysql:mysql-connector-java:8.0.21' + + testCompileOnly 'org.jetbrains:annotations:20.0.0' } sourceJar { From fb4edf618c6e9a4de32a24446d5eff330d226846 Mon Sep 17 00:00:00 2001 From: iaintatchbbc Date: Tue, 1 Sep 2020 11:52:13 +0100 Subject: [PATCH 1586/5615] Remove use of implicit network for multi-container Kafka (#3128) --- docs/modules/kafka.md | 2 ++ .../java/org/testcontainers/containers/KafkaContainerTest.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/modules/kafka.md b/docs/modules/kafka.md index 35ac37b6906..83bb8af0628 100644 --- a/docs/modules/kafka.md +++ b/docs/modules/kafka.md @@ -53,6 +53,8 @@ If your test needs to run some other Docker container which needs access to the * Use `kafka.getNetworkAliases().get(0)+":9092"` as bootstrap server location. Or just give your Kafka container a network alias of your liking. +You will need to explicitly create a network and set it on the Kafka container as well as on your other containers that need to communicate with Kafka. + ## Adding this module to your project dependencies Add the following dependency to your `pom.xml`/`build.gradle` file: diff --git a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java index b8cd5c9be9c..71702ce8e62 100644 --- a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java +++ b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java @@ -75,7 +75,7 @@ public void testExternalZookeeperWithExternalNetwork() throws Exception { .withEnv("ZOOKEEPER_CLIENT_PORT", "2181"); // withKafkaNetwork { - GenericContainer application = new GenericContainer("alpine").withNetwork(kafka.getNetwork()) + GenericContainer application = new GenericContainer("alpine").withNetwork(network) // } .withNetworkAliases("dummy") .withCommand("sleep 10000") From c9f756831008ae145a7e7f54e00f9e59e77e1642 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2020 12:30:24 +0100 Subject: [PATCH 1587/5615] Bump mssql-jdbc from 8.3.1.jre8-preview to 8.4.1.jre8 in /modules/mssqlserver (#3174) Bumps [mssql-jdbc](https://github.com/Microsoft/mssql-jdbc) from 8.3.1.jre8-preview to 8.4.1.jre8. - [Release notes](https://github.com/Microsoft/mssql-jdbc/releases) - [Changelog](https://github.com/microsoft/mssql-jdbc/blob/dev/CHANGELOG.md) - [Commits](https://github.com/Microsoft/mssql-jdbc/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/mssqlserver/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index 508de84a2f9..f0170500e7d 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -10,12 +10,12 @@ dependencies { compileOnly 'io.r2dbc:r2dbc-mssql:0.8.4.RELEASE' testCompile project(':jdbc-test') - testCompile 'com.microsoft.sqlserver:mssql-jdbc:8.3.1.jre8-preview' + testCompile 'com.microsoft.sqlserver:mssql-jdbc:8.4.1.jre8' testCompile project(':r2dbc') testCompile 'io.r2dbc:r2dbc-mssql:0.8.3.RELEASE' // MSSQL's wait strategy requires the JDBC driver testCompile testFixtures(project(':r2dbc')) - testCompile 'com.microsoft.sqlserver:mssql-jdbc:8.3.1.jre8-preview' + testCompile 'com.microsoft.sqlserver:mssql-jdbc:8.4.1.jre8' } From 2ad9fb37458092cf2aeb7a8ba2eb383a9a4626c4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2020 12:31:08 +0100 Subject: [PATCH 1588/5615] Bump aws-java-sdk-dynamodb from 1.11.847 to 1.11.851 in /modules/dynalite (#3172) Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.11.847 to 1.11.851. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.847...1.11.851) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/dynalite/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 762e189173b..7ebe948ee66 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Dynalite" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.847' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.851' testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.847' } From 6402f14156981ef09b4a195567040af9c7d2b43d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2020 12:32:07 +0100 Subject: [PATCH 1589/5615] Bump aws-java-sdk-sqs from 1.11.846 to 1.11.851 in /modules/localstack (#3171) Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.11.846 to 1.11.851. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.846...1.11.851) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index e28117319f5..37f505374e6 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.847' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.847' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.846' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.851' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' testCompile 'software.amazon.awssdk:s3:2.14.6' } From 38587306998d79b8e6625c77e3baad5f570cf8e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2020 12:36:46 +0100 Subject: [PATCH 1590/5615] Bump assertj-core from 3.17.0 to 3.17.1 in /modules/neo4j (#3167) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.17.0 to 3.17.1. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.17.0...assertj-core-3.17.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 2f5e6b05d9f..6a5bab19284 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -34,5 +34,5 @@ dependencies { compile project(":testcontainers") testCompile "org.neo4j.driver:neo4j-java-driver:1.7.5" - testCompile 'org.assertj:assertj-core:3.17.0' + testCompile 'org.assertj:assertj-core:3.17.1' } From 77528c9c5fe46ba9618497cdbeb4e5e14f00ee25 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2020 12:36:56 +0100 Subject: [PATCH 1591/5615] Bump assertj-core from 3.17.0 to 3.17.1 in /modules/database-commons (#3168) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.17.0 to 3.17.1. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.17.0...assertj-core-3.17.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/database-commons/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/database-commons/build.gradle b/modules/database-commons/build.gradle index 1ab8d67cf02..ac27b84a662 100644 --- a/modules/database-commons/build.gradle +++ b/modules/database-commons/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: Database-Commons" dependencies { compile project(':testcontainers') - testCompile 'org.assertj:assertj-core:3.17.0' + testCompile 'org.assertj:assertj-core:3.17.1' } From b7892600f39e9e089b4560f3fa8312d60cc42e8d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2020 12:37:11 +0100 Subject: [PATCH 1592/5615] Bump assertj-core from 3.17.0 to 3.17.1 in /modules/vault (#3173) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.17.0 to 3.17.1. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.17.0...assertj-core-3.17.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index 5c83cbbb56f..7c8cac68d05 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -5,6 +5,6 @@ dependencies { testCompile 'com.bettercloud:vault-java-driver:5.1.0' testCompile 'io.rest-assured:rest-assured:4.3.1' - testCompile 'org.assertj:assertj-core:3.17.0' + testCompile 'org.assertj:assertj-core:3.17.1' } From 28125c1aa47dcb98d4f93557cda8f429b435962c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2020 12:37:21 +0100 Subject: [PATCH 1593/5615] Bump assertj-core from 3.17.0 to 3.17.1 in /modules/junit-jupiter (#3177) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.17.0 to 3.17.1. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.17.0...assertj-core-3.17.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index d1368c2389a..7b4d924c9f9 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -12,7 +12,7 @@ dependencies { testCompile ('org.mockito:mockito-core:3.5.7') { exclude(module: 'hamcrest-core') } - testCompile 'org.assertj:assertj-core:3.17.0' + testCompile 'org.assertj:assertj-core:3.17.1' testCompile 'org.junit.jupiter:junit-jupiter-params:5.6.2' testRuntime 'org.postgresql:postgresql:42.2.16' From 0286de04a8843305969633cca5c03bcc2e3dba1e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2020 12:37:29 +0100 Subject: [PATCH 1594/5615] Bump assertj-core from 3.17.0 to 3.17.1 in /modules/pulsar (#3176) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.17.0 to 3.17.1. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.17.0...assertj-core-3.17.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index a8f0d9f48ed..5dbbb0d6489 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -4,6 +4,6 @@ dependencies { compile project(':testcontainers') testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.6.1' - testCompile group: 'org.assertj', name: 'assertj-core', version: '3.17.0' + testCompile group: 'org.assertj', name: 'assertj-core', version: '3.17.1' testCompile group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.6.1' } From 5f02839ce55c81bd538e83ecb28e7c9f8d6fdc12 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2020 12:37:40 +0100 Subject: [PATCH 1595/5615] Bump assertj-core from 3.17.0 to 3.17.1 in /modules/kafka (#3175) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.17.0 to 3.17.1. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.17.0...assertj-core-3.17.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index a2b73ff4281..64c0ee4ce1a 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -4,6 +4,6 @@ dependencies { compile project(':testcontainers') testCompile 'org.apache.kafka:kafka-clients:2.6.0' - testCompile 'org.assertj:assertj-core:3.17.0' + testCompile 'org.assertj:assertj-core:3.17.1' testCompile 'com.google.guava:guava:23.0' } From a2146f35b834e112cc5572b5bca757d3c53c7657 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2020 13:02:33 +0100 Subject: [PATCH 1596/5615] Bump s3 from 2.14.6 to 2.14.8 in /modules/localstack (#3181) Bumps s3 from 2.14.6 to 2.14.8. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 37f505374e6..fc9ebe3064b 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,5 +7,5 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.847' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.851' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' - testCompile 'software.amazon.awssdk:s3:2.14.6' + testCompile 'software.amazon.awssdk:s3:2.14.8' } From 63692d3d84d2c5e46788d0dea28acc33f4dc1d92 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2020 13:02:43 +0100 Subject: [PATCH 1597/5615] Bump aws-java-sdk-s3 from 1.11.847 to 1.11.852 in /modules/localstack (#3182) Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.11.847 to 1.11.852. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.847...1.11.852) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index fc9ebe3064b..eec730235ef 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.847' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.852' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.847' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.851' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' From d796707df2f9a3ced54f34924e0dbfbf0cf181f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Coll?= Date: Sat, 12 Sep 2020 15:18:28 -0300 Subject: [PATCH 1598/5615] Use testCompileClasspath instead of testCompileOnly (#3219) --- core/build.gradle | 2 +- examples/mongodb-container/build.gradle | 2 +- modules/selenium/build.gradle | 2 +- modules/spock/build.gradle | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index bb27f4378e4..65bb8477ed9 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -165,7 +165,7 @@ dependencies { compile 'junit:junit:4.12' compile 'org.slf4j:slf4j-api:1.7.30' compileOnly 'org.jetbrains:annotations:20.0.0' - testCompileOnly 'org.jetbrains:annotations:20.0.0' + testCompileClasspath 'org.jetbrains:annotations:20.0.0' compile 'org.apache.commons:commons-compress:1.20' compile ('org.rnorth.duct-tape:duct-tape:1.0.8') { exclude(group: 'org.jetbrains', module: 'annotations') diff --git a/examples/mongodb-container/build.gradle b/examples/mongodb-container/build.gradle index 2c6b0bbcc13..285c0f96315 100644 --- a/examples/mongodb-container/build.gradle +++ b/examples/mongodb-container/build.gradle @@ -9,5 +9,5 @@ repositories { dependencies { testImplementation 'org.testcontainers:testcontainers' testImplementation 'junit:junit:4.13' - testCompileOnly 'org.jetbrains:annotations:20.0.0' + testCompileClasspath 'org.jetbrains:annotations:20.0.0' } diff --git a/modules/selenium/build.gradle b/modules/selenium/build.gradle index a51735ae78a..d745baefa75 100644 --- a/modules/selenium/build.gradle +++ b/modules/selenium/build.gradle @@ -12,5 +12,5 @@ dependencies { testCompile project(':nginx') compileOnly 'org.jetbrains:annotations:20.0.0' - testCompileOnly 'org.jetbrains:annotations:20.0.0' + testCompileClasspath 'org.jetbrains:annotations:20.0.0' } diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index c559b623b55..7640188fa54 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -18,7 +18,7 @@ dependencies { testRuntime 'org.postgresql:postgresql:42.2.16' testRuntime 'mysql:mysql-connector-java:8.0.21' - testCompileOnly 'org.jetbrains:annotations:20.0.0' + testCompileClasspath 'org.jetbrains:annotations:20.0.0' } sourceJar { From 03304804c7fa87c05efcf0250a53011c01196120 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 12 Sep 2020 19:25:45 +0100 Subject: [PATCH 1599/5615] Bump assertj-core from 3.17.1 to 3.17.2 in /modules/neo4j (#3208) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.17.1 to 3.17.2. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.17.1...assertj-core-3.17.2) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 6a5bab19284..da90a493a01 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -34,5 +34,5 @@ dependencies { compile project(":testcontainers") testCompile "org.neo4j.driver:neo4j-java-driver:1.7.5" - testCompile 'org.assertj:assertj-core:3.17.1' + testCompile 'org.assertj:assertj-core:3.17.2' } From 29ff3423261ec8e4942678daed6091e2bad509d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 12 Sep 2020 19:25:56 +0100 Subject: [PATCH 1600/5615] Bump elasticsearch-rest-client from 7.9.0 to 7.9.1 in /modules/elasticsearch (#3212) Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 7.9.0 to 7.9.1. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.9.0...v7.9.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 20097bd19b4..44a5cca5963 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,6 +2,6 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') - testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.9.0" + testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.9.1" testCompile "org.elasticsearch.client:transport:6.7.1" } From 95455f2d06c30da0406c58d94551a8e9447e3a37 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 12 Sep 2020 19:26:39 +0100 Subject: [PATCH 1601/5615] Bump assertj-core from 3.16.1 to 3.17.1 in /core (#3210) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.16.1 to 3.17.1. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.16.1...assertj-core-3.17.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- core/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index 65bb8477ed9..b7a029cf5ef 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -211,12 +211,12 @@ dependencies { // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest testCompile files('testlib/repo/fakejar/fakejar/0/fakejar-0.jar') - testCompile 'org.assertj:assertj-core:3.16.1' + testCompile 'org.assertj:assertj-core:3.17.1' testCompile project(':test-support') jarFileTestCompileOnly "org.projectlombok:lombok:${lombok.version}" jarFileTestAnnotationProcessor "org.projectlombok:lombok:${lombok.version}" jarFileTestCompile 'junit:junit:4.12' - jarFileTestCompile 'org.assertj:assertj-core:3.16.0' + jarFileTestCompile 'org.assertj:assertj-core:3.17.1' jarFileTestCompile 'org.ow2.asm:asm-debug-all:5.2' } From 8cdec4434fd4c0c526e1330498047c08982a958c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 12 Sep 2020 19:27:37 +0100 Subject: [PATCH 1602/5615] Bump annotations from 20.0.0 to 20.1.0 in /core (#3214) Bumps [annotations](https://github.com/JetBrains/java-annotations) from 20.0.0 to 20.1.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/20.0.0...20.1.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index b7a029cf5ef..e233ef7b159 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -164,7 +164,7 @@ dependencies { compile 'junit:junit:4.12' compile 'org.slf4j:slf4j-api:1.7.30' - compileOnly 'org.jetbrains:annotations:20.0.0' + compileOnly 'org.jetbrains:annotations:20.1.0' testCompileClasspath 'org.jetbrains:annotations:20.0.0' compile 'org.apache.commons:commons-compress:1.20' compile ('org.rnorth.duct-tape:duct-tape:1.0.8') { From 842863441bd4f0c21f8deafa9da466f59188fe2b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 12 Sep 2020 19:27:46 +0100 Subject: [PATCH 1603/5615] Bump annotations from 20.0.0 to 20.1.0 in /modules/spock (#3204) Bumps [annotations](https://github.com/JetBrains/java-annotations) from 20.0.0 to 20.1.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/20.0.0...20.1.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 7640188fa54..3d0948e9e83 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -18,7 +18,7 @@ dependencies { testRuntime 'org.postgresql:postgresql:42.2.16' testRuntime 'mysql:mysql-connector-java:8.0.21' - testCompileClasspath 'org.jetbrains:annotations:20.0.0' + testCompileClasspath 'org.jetbrains:annotations:20.1.0' } sourceJar { From 8c7cd3823bc29a3e8ec204982923865b75c10a05 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 12 Sep 2020 19:27:55 +0100 Subject: [PATCH 1604/5615] Bump annotations from 20.0.0 to 20.1.0 in /modules/nginx (#3206) Bumps [annotations](https://github.com/JetBrains/java-annotations) from 20.0.0 to 20.1.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/20.0.0...20.1.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/nginx/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/nginx/build.gradle b/modules/nginx/build.gradle index b56d7f2b4e8..6c4b97876c4 100644 --- a/modules/nginx/build.gradle +++ b/modules/nginx/build.gradle @@ -2,5 +2,5 @@ description = "Testcontainers :: Nginx" dependencies { compile project(':testcontainers') - compileOnly 'org.jetbrains:annotations:20.0.0' + compileOnly 'org.jetbrains:annotations:20.1.0' } From 273b22d484bea0a94aec08cc8bbcfce50e1c22cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 12 Sep 2020 19:28:21 +0100 Subject: [PATCH 1605/5615] Bump annotations from 20.0.0 to 20.1.0 in /modules/postgresql (#3207) Bumps [annotations](https://github.com/JetBrains/java-annotations) from 20.0.0 to 20.1.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/20.0.0...20.1.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 6437bd9c2f8..793cf89406e 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -16,5 +16,5 @@ dependencies { testCompile testFixtures(project(':r2dbc')) testCompile 'io.r2dbc:r2dbc-postgresql:0.8.3.RELEASE' - compileOnly 'org.jetbrains:annotations:20.0.0' + compileOnly 'org.jetbrains:annotations:20.1.0' } From e14eb8e3055e8db3de0489fed11a735aa69488b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 12 Sep 2020 19:28:33 +0100 Subject: [PATCH 1606/5615] Bump annotations from 20.0.0 to 20.1.0 in /examples (#3217) Bumps [annotations](https://github.com/JetBrains/java-annotations) from 20.0.0 to 20.1.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/20.0.0...20.1.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/mongodb-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/mongodb-container/build.gradle b/examples/mongodb-container/build.gradle index 285c0f96315..c09cb033489 100644 --- a/examples/mongodb-container/build.gradle +++ b/examples/mongodb-container/build.gradle @@ -9,5 +9,5 @@ repositories { dependencies { testImplementation 'org.testcontainers:testcontainers' testImplementation 'junit:junit:4.13' - testCompileClasspath 'org.jetbrains:annotations:20.0.0' + testCompileClasspath 'org.jetbrains:annotations:20.1.0' } From aa5c2dc58282586721b574b76c3ceed02862463d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 12 Sep 2020 19:28:55 +0100 Subject: [PATCH 1607/5615] Bump annotations from 20.0.0 to 20.1.0 in /modules/jdbc (#3196) Bumps [annotations](https://github.com/JetBrains/java-annotations) from 20.0.0 to 20.1.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/20.0.0...20.1.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 33ff3e8339f..6453b49b052 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -4,7 +4,7 @@ dependencies { compile project(':database-commons') testCompile project(':test-support') - compileOnly 'org.jetbrains:annotations:20.0.0' + compileOnly 'org.jetbrains:annotations:20.1.0' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.vibur:vibur-dbcp:25.0' testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.37' From 07f035f8ba0b54729de754a35b1f16cd9c565f9b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 12 Sep 2020 19:29:10 +0100 Subject: [PATCH 1608/5615] Bump annotations from 20.0.0 to 20.1.0 in /modules/selenium (#3195) Bumps [annotations](https://github.com/JetBrains/java-annotations) from 20.0.0 to 20.1.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/20.0.0...20.1.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/selenium/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/selenium/build.gradle b/modules/selenium/build.gradle index d745baefa75..7c6d81ca1bf 100644 --- a/modules/selenium/build.gradle +++ b/modules/selenium/build.gradle @@ -11,6 +11,6 @@ dependencies { testCompile 'org.mortbay.jetty:jetty:6.1.26' testCompile project(':nginx') - compileOnly 'org.jetbrains:annotations:20.0.0' + compileOnly 'org.jetbrains:annotations:20.1.0' testCompileClasspath 'org.jetbrains:annotations:20.0.0' } From 7e1d1b9982334d52e684188378a4c379818824d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 12 Sep 2020 19:29:44 +0100 Subject: [PATCH 1609/5615] Bump annotations from 20.0.0 to 20.1.0 in /modules/mysql (#3198) Bumps [annotations](https://github.com/JetBrains/java-annotations) from 20.0.0 to 20.1.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/20.0.0...20.1.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 545037d2dff..9df2e177d3f 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -15,5 +15,5 @@ dependencies { testCompile testFixtures(project(':r2dbc')) testCompile 'dev.miku:r2dbc-mysql:0.8.1.RELEASE' - compileOnly 'org.jetbrains:annotations:20.0.0' + compileOnly 'org.jetbrains:annotations:20.1.0' } From 4331b63acd7bc034fabacb7494ac1fb1414505ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 12 Sep 2020 19:30:15 +0100 Subject: [PATCH 1610/5615] Bump aws-java-sdk-dynamodb from 1.11.851 to 1.11.856 in /modules/dynalite (#3194) Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.11.851 to 1.11.856. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.851...1.11.856) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/dynalite/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 7ebe948ee66..e850b616f2f 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Dynalite" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.851' - testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.847' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.856' + testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.856' } From 1f62cad8d86712d8b2253d536dfcedb1aa90b1e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 12 Sep 2020 19:30:29 +0100 Subject: [PATCH 1611/5615] Bump s3 from 2.14.8 to 2.14.12 in /modules/localstack (#3202) Bumps s3 from 2.14.8 to 2.14.12. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index eec730235ef..1f9816ae2d8 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,5 +7,5 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.847' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.851' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' - testCompile 'software.amazon.awssdk:s3:2.14.8' + testCompile 'software.amazon.awssdk:s3:2.14.12' } From 2279aa2df0814592066860d933ea0ab0e63eb27f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 12 Sep 2020 19:30:35 +0100 Subject: [PATCH 1612/5615] Bump aws-java-sdk-s3 from 1.11.852 to 1.11.856 in /modules/localstack (#3203) Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.11.852 to 1.11.856. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.852...1.11.856) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 1f9816ae2d8..3aea0db0acd 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.852' - testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.847' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.856' + testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.856' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.851' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' testCompile 'software.amazon.awssdk:s3:2.14.12' From d84db2dc76e64d1213b5652927a9062795c53c10 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 12 Sep 2020 19:30:50 +0100 Subject: [PATCH 1613/5615] Bump mockito-core from 3.5.7 to 3.5.10 in /modules/junit-jupiter (#3201) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.5.7 to 3.5.10. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.5.7...v3.5.10) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 7b4d924c9f9..d649278349b 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,7 +9,7 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.4.5' testCompile 'redis.clients:jedis:3.3.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.12' - testCompile ('org.mockito:mockito-core:3.5.7') { + testCompile ('org.mockito:mockito-core:3.5.10') { exclude(module: 'hamcrest-core') } testCompile 'org.assertj:assertj-core:3.17.1' From 37ff402daa646ab676082b92a10e726abc4efc8f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 12 Sep 2020 19:32:07 +0100 Subject: [PATCH 1614/5615] Bump assertj-core from 3.17.1 to 3.17.2 in /modules/vault (#3193) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.17.1 to 3.17.2. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.17.1...assertj-core-3.17.2) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index 7c8cac68d05..6d862eeba2c 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -5,6 +5,6 @@ dependencies { testCompile 'com.bettercloud:vault-java-driver:5.1.0' testCompile 'io.rest-assured:rest-assured:4.3.1' - testCompile 'org.assertj:assertj-core:3.17.1' + testCompile 'org.assertj:assertj-core:3.17.2' } From 84024b1ca0cb6cd1d570b35675f25238e9f3f05f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 12 Sep 2020 19:32:29 +0100 Subject: [PATCH 1615/5615] Bump aws-java-sdk-sqs from 1.11.851 to 1.11.860 in /modules/localstack (#3227) Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.11.851 to 1.11.860. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.851...1.11.860) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 3aea0db0acd..6de26e324b4 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.856' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.856' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.851' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.860' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' testCompile 'software.amazon.awssdk:s3:2.14.12' } From 9b3c87eb2f350c6211dfa5eadea61f8eb00e2097 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 12 Sep 2020 19:57:40 +0100 Subject: [PATCH 1616/5615] Bump cucumber-junit from 6.4.0 to 6.6.0 in /examples (#3215) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 90cc99e7627..fc4e4aaf3b1 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' testImplementation 'io.cucumber:cucumber-java:6.4.0' - testImplementation 'io.cucumber:cucumber-junit:6.4.0' + testImplementation 'io.cucumber:cucumber-junit:6.6.0' testImplementation 'org.testcontainers:selenium' } From 2be4c2a927abbed84e2a6cfcea974f821bfe3628 Mon Sep 17 00:00:00 2001 From: Quincy Bowers Date: Mon, 14 Sep 2020 02:58:28 -0600 Subject: [PATCH 1617/5615] Exclude id from NetworkImpl.NetworkImplBuilder (testcontainers#8125) (#2807) * Exclude id from NetworkImpl.NetworkImplBuilder (testcontainers#8125) * Deprecate Network.id field The field is deprecated in order to mark the NetworkBuildImpl#id method deprecated so that the method can later be removed. We'll undeprecate the field at that time. Co-authored-by: Richard North --- core/src/main/java/org/testcontainers/containers/Network.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/org/testcontainers/containers/Network.java b/core/src/main/java/org/testcontainers/containers/Network.java index 1c00fa00fac..2fda5ad1a8d 100644 --- a/core/src/main/java/org/testcontainers/containers/Network.java +++ b/core/src/main/java/org/testcontainers/containers/Network.java @@ -52,6 +52,7 @@ class NetworkImpl extends ExternalResource implements Network { @Singular private Set> createNetworkCmdModifiers; + @Deprecated private String id; private final AtomicBoolean initialized = new AtomicBoolean(); From ec16e7a347242a22aa604005667142f867637a7a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2020 19:18:57 +0100 Subject: [PATCH 1618/5615] Bump junit-jupiter-engine from 5.6.2 to 5.7.0 in /modules/junit-jupiter (#3234) Bumps [junit-jupiter-engine](https://github.com/junit-team/junit5) from 5.6.2 to 5.7.0. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.6.2...r5.7.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index d649278349b..d57835f4cfc 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -17,7 +17,7 @@ dependencies { testRuntime 'org.postgresql:postgresql:42.2.16' testRuntime 'mysql:mysql-connector-java:8.0.21' - testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.6.2' + testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.7.0' } test { From 2854194424504af8ada973b2c623da066bf8559f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2020 19:31:06 +0100 Subject: [PATCH 1619/5615] Bump postgresql from 42.2.15 to 42.2.16 in /examples (#3216) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.15 to 42.2.16. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.15...REL42.2.16) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index a62854e9b35..6877a35fec5 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' implementation 'com.squareup.okhttp3:okhttp:4.8.1' implementation 'org.json:json:20180813' - testImplementation 'org.postgresql:postgresql:42.2.15' + testImplementation 'org.postgresql:postgresql:42.2.16' testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.testcontainers:postgresql' } From fb66c0cabd1ed5e7d01b6cf9fc895880a9d7e3e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2020 19:31:19 +0100 Subject: [PATCH 1620/5615] Bump aws-java-sdk-dynamodb from 1.11.856 to 1.11.860 in /modules/dynalite (#3235) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/dynalite/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index e850b616f2f..7d9fa4d9ea5 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Dynalite" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.856' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.860' testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.856' } From 9327bba287a2e8ebe5be41ae07cb1c85038c6766 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2020 19:31:37 +0100 Subject: [PATCH 1621/5615] Bump junit-jupiter-params from 5.6.2 to 5.7.0 in /modules/junit-jupiter (#3232) Bumps [junit-jupiter-params](https://github.com/junit-team/junit5) from 5.6.2 to 5.7.0. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.6.2...r5.7.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index d57835f4cfc..56f9fc3070e 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -13,7 +13,7 @@ dependencies { exclude(module: 'hamcrest-core') } testCompile 'org.assertj:assertj-core:3.17.1' - testCompile 'org.junit.jupiter:junit-jupiter-params:5.6.2' + testCompile 'org.junit.jupiter:junit-jupiter-params:5.7.0' testRuntime 'org.postgresql:postgresql:42.2.16' testRuntime 'mysql:mysql-connector-java:8.0.21' From 730e98e570ab4ba695db60bf5e967daac9ed6696 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2020 19:31:50 +0100 Subject: [PATCH 1622/5615] Bump junit-jupiter-api from 5.6.2 to 5.7.0 in /modules/junit-jupiter (#3233) Bumps [junit-jupiter-api](https://github.com/junit-team/junit5) from 5.6.2 to 5.7.0. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.6.2...r5.7.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 56f9fc3070e..303d4b20d56 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -2,7 +2,7 @@ description = "Testcontainers :: JUnit Jupiter Extension" dependencies { compile project(':testcontainers') - compile 'org.junit.jupiter:junit-jupiter-api:5.6.2' + compile 'org.junit.jupiter:junit-jupiter-api:5.7.0' testCompile project(':mysql') testCompile project(':postgresql') From a011235b0e2152601387f16a32a6e0b03fb8a7aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2020 19:32:01 +0100 Subject: [PATCH 1623/5615] Bump s3 from 2.14.12 to 2.14.16 in /modules/localstack (#3231) Bumps s3 from 2.14.12 to 2.14.16. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 6de26e324b4..5c069167eb1 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,5 +7,5 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.856' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.860' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' - testCompile 'software.amazon.awssdk:s3:2.14.12' + testCompile 'software.amazon.awssdk:s3:2.14.16' } From a4f7880ada6597a67631b5eb8b9f48422cc8b286 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2020 19:32:11 +0100 Subject: [PATCH 1624/5615] Bump r2dbc-postgresql from 0.8.4.RELEASE to 0.8.5.RELEASE in /modules/postgresql (#3230) Bumps [r2dbc-postgresql](https://github.com/r2dbc/r2dbc-postgresql) from 0.8.4.RELEASE to 0.8.5.RELEASE. - [Release notes](https://github.com/r2dbc/r2dbc-postgresql/releases) - [Changelog](https://github.com/pgjdbc/r2dbc-postgresql/blob/v0.8.5.RELEASE/CHANGELOG) - [Commits](https://github.com/r2dbc/r2dbc-postgresql/compare/v0.8.4.RELEASE...v0.8.5.RELEASE) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/postgresql/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 793cf89406e..ea6e4a87226 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -7,14 +7,14 @@ dependencies { compile project(':jdbc') compileOnly project(':r2dbc') - compileOnly 'io.r2dbc:r2dbc-postgresql:0.8.4.RELEASE' + compileOnly 'io.r2dbc:r2dbc-postgresql:0.8.5.RELEASE' testCompile project(':jdbc-test') testCompile project(':test-support') testCompile 'org.postgresql:postgresql:42.2.16' testCompile testFixtures(project(':r2dbc')) - testCompile 'io.r2dbc:r2dbc-postgresql:0.8.3.RELEASE' + testCompile 'io.r2dbc:r2dbc-postgresql:0.8.5.RELEASE' compileOnly 'org.jetbrains:annotations:20.1.0' } From 0af261eb363b75b5e6ee1d9ff2f3d9145a9fd81f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2020 19:32:20 +0100 Subject: [PATCH 1625/5615] Bump cucumber-java from 6.4.0 to 6.6.0 in /examples (#3213) Bumps [cucumber-java](https://github.com/cucumber/cucumber-jvm) from 6.4.0 to 6.6.0. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v6.4.0...v6.6.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index fc4e4aaf3b1..1362b3f257a 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' - testImplementation 'io.cucumber:cucumber-java:6.4.0' + testImplementation 'io.cucumber:cucumber-java:6.6.0' testImplementation 'io.cucumber:cucumber-junit:6.6.0' testImplementation 'org.testcontainers:selenium' } From 3f5a8a2f0c7b16ff19ae581cc30cbca974f35636 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2020 19:51:11 +0100 Subject: [PATCH 1626/5615] Bump assertj-core from 3.17.1 to 3.17.2 in /modules/kafka (#3200) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.17.1 to 3.17.2. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.17.1...assertj-core-3.17.2) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index 64c0ee4ce1a..95d844c7a41 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -4,6 +4,6 @@ dependencies { compile project(':testcontainers') testCompile 'org.apache.kafka:kafka-clients:2.6.0' - testCompile 'org.assertj:assertj-core:3.17.1' + testCompile 'org.assertj:assertj-core:3.17.2' testCompile 'com.google.guava:guava:23.0' } From 46788e1e980b7d73152cfd15f3949b067c07fa4c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2020 19:51:24 +0100 Subject: [PATCH 1627/5615] Bump assertj-core from 3.17.1 to 3.17.2 in /modules/junit-jupiter (#3199) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.17.1 to 3.17.2. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.17.1...assertj-core-3.17.2) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 303d4b20d56..8293060b9b4 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -12,7 +12,7 @@ dependencies { testCompile ('org.mockito:mockito-core:3.5.10') { exclude(module: 'hamcrest-core') } - testCompile 'org.assertj:assertj-core:3.17.1' + testCompile 'org.assertj:assertj-core:3.17.2' testCompile 'org.junit.jupiter:junit-jupiter-params:5.7.0' testRuntime 'org.postgresql:postgresql:42.2.16' From dafc8841ea5c0cba88ffdf79452925bd83fbdcd5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2020 19:51:35 +0100 Subject: [PATCH 1628/5615] Bump assertj-core from 3.17.1 to 3.17.2 in /modules/database-commons (#3197) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.17.1 to 3.17.2. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.17.1...assertj-core-3.17.2) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/database-commons/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/database-commons/build.gradle b/modules/database-commons/build.gradle index ac27b84a662..e6d6a1812da 100644 --- a/modules/database-commons/build.gradle +++ b/modules/database-commons/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: Database-Commons" dependencies { compile project(':testcontainers') - testCompile 'org.assertj:assertj-core:3.17.1' + testCompile 'org.assertj:assertj-core:3.17.2' } From e01495d168e951e644b06eb1b1b66dd98dae78ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2020 19:51:42 +0100 Subject: [PATCH 1629/5615] Bump aws-java-sdk-s3 from 1.11.856 to 1.11.860 in /modules/localstack (#3226) Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.11.856 to 1.11.860. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.856...1.11.860) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 5c069167eb1..3eb02882546 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.856' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.860' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.856' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.860' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' From 77251a17dad3ee3aa6017229f856d46a4a8689b8 Mon Sep 17 00:00:00 2001 From: Gapmeister66 Date: Mon, 14 Sep 2020 20:30:02 +0100 Subject: [PATCH 1630/5615] Add support for Docker compose `withOptions(...)` (#2827) Co-authored-by: Peter Lewis Co-authored-by: Richard North --- .../containers/DockerComposeContainer.java | 27 +++++++- ...DockerComposeContainerWithOptionsTest.java | 62 +++++++++++++++++++ .../with-deploy-block.yml | 8 +++ 3 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithOptionsTest.java create mode 100644 core/src/test/resources/compose-options-test/with-deploy-block.yml diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index d2702b3db5b..bd06083644d 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -45,6 +45,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -81,6 +82,7 @@ public class DockerComposeContainer> e private boolean localCompose; private boolean pull = true; private boolean build = false; + private Set options = new HashSet<>(); private boolean tailChildContainers; private String project; @@ -213,7 +215,7 @@ private void createServices() { .distinct() .collect(joining(" ")); - String command = "up -d"; + String command = optionsAsString() + "up -d"; if (build) { command += " --build"; @@ -231,6 +233,19 @@ private void createServices() { runWithCompose(command); } + private String optionsAsString() { + String optionsString = options + .stream() + .collect(joining(" ")); + if (optionsString.length() !=0 ) { + // ensures that there is a space between the options and 'up' if options are passed. + return optionsString + " "; + } else { + // otherwise two spaces would appear between 'docker-compose' and 'up' + return StringUtils.EMPTY; + } + } + private void waitUntilServiceStarted() { listChildContainers().forEach(this::createServiceInstance); @@ -517,6 +532,16 @@ public SELF withBuild(boolean build) { return self(); } + /** + * Adds options to the docker-compose command, e.g. docker-compose --compatibility. + * + * @return this instance, for chaining + */ + public SELF withOptions(String... options) { + this.options = new HashSet<>(Arrays.asList(options)); + return self(); + } + /** * Remove images after containers shutdown. * diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithOptionsTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithOptionsTest.java new file mode 100644 index 00000000000..375c9baa070 --- /dev/null +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithOptionsTest.java @@ -0,0 +1,62 @@ +package org.testcontainers.junit; + +import com.google.common.collect.ImmutableSet; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.testcontainers.containers.DockerComposeContainer; + +import java.io.File; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests the options associated with the docker-compose command. + */ +@RunWith(Parameterized.class) +public class DockerComposeContainerWithOptionsTest { + + public DockerComposeContainerWithOptionsTest(final File composeFile, final boolean local, final Set options, final boolean expectError) { + this.composeFile = composeFile; + this.local = local; + this.options = options; + this.expectError = expectError; + } + + private final File composeFile; + private final boolean local; + + private final Set options; + private final boolean expectError; + + @Parameterized.Parameters(name = "docker-compose test [compose file: {0}, local: {1}, options: {2}, expected result: {3}]") + + public static Object[][] params() { + return new Object[][]{ + // Test the happy day case. THe compatibility option should be accepted by docker-compose. + {new File("src/test/resources/compose-options-test/with-deploy-block.yml"), false, ImmutableSet.of("--compatibility"), false}, + // Test with flags absent. Docker compose will warn but continue, ignoring the deploy block. + {new File("src/test/resources/compose-options-test/with-deploy-block.yml"), false, ImmutableSet.of(""), false}, + // Test with a bad option. Compose will complain. + {new File("src/test/resources/compose-options-test/with-deploy-block.yml"), false, ImmutableSet.of("--bad-option"), true}, + // Local compose + {new File("src/test/resources/compose-options-test/with-deploy-block.yml"), true, ImmutableSet.of("--compatibility"), false}, + }; + } + + @Test + public void performTest() { + + try (DockerComposeContainer environment = new DockerComposeContainer<>(composeFile) + .withOptions(options.stream().toArray(String[]::new)) + .withLocalCompose(local)) { + environment.start(); + assertThat(expectError).isEqualTo(false); + } catch (Exception e) { + assertThat(expectError).isEqualTo(true); + } + + } + +} diff --git a/core/src/test/resources/compose-options-test/with-deploy-block.yml b/core/src/test/resources/compose-options-test/with-deploy-block.yml new file mode 100644 index 00000000000..f52f6747122 --- /dev/null +++ b/core/src/test/resources/compose-options-test/with-deploy-block.yml @@ -0,0 +1,8 @@ +version: '3.7' +services: + redis: + image: redis:2.6.17 + deploy: + resources: + limits: + memory: 150M From b0cdb60e5565ceabe527165143216eecb4532413 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Sep 2020 09:49:55 +0100 Subject: [PATCH 1631/5615] Bump assertj-core from 3.17.1 to 3.17.2 in /modules/pulsar (#3192) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.17.1 to 3.17.2. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.17.1...assertj-core-3.17.2) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 5dbbb0d6489..2d2ded0c765 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -4,6 +4,6 @@ dependencies { compile project(':testcontainers') testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.6.1' - testCompile group: 'org.assertj', name: 'assertj-core', version: '3.17.1' + testCompile group: 'org.assertj', name: 'assertj-core', version: '3.17.2' testCompile group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.6.1' } From 7c994846904d7fd7b4ae82b95b964f66199926bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Sep 2020 09:50:14 +0100 Subject: [PATCH 1632/5615] Bump aws-java-sdk-dynamodb from 1.11.860 to 1.11.865 in /modules/dynalite (#3242) Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.11.860 to 1.11.865. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.860...1.11.865) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/dynalite/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 7d9fa4d9ea5..0b2e334c633 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Dynalite" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.860' - testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.856' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.865' + testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.865' } From 625fddb5900496723e18bf7af4e58a688c7d16af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Sep 2020 09:50:26 +0100 Subject: [PATCH 1633/5615] Bump mockito-core from 3.5.10 to 3.5.11 in /modules/junit-jupiter (#3243) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.5.10 to 3.5.11. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.5.10...v3.5.11) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 8293060b9b4..dbf54b8d291 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,7 +9,7 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.4.5' testCompile 'redis.clients:jedis:3.3.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.12' - testCompile ('org.mockito:mockito-core:3.5.10') { + testCompile ('org.mockito:mockito-core:3.5.11') { exclude(module: 'hamcrest-core') } testCompile 'org.assertj:assertj-core:3.17.2' From 1e6665094fe8d3f30a9af0242f9bd204fea0699b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Sep 2020 09:50:34 +0100 Subject: [PATCH 1634/5615] Bump solr-solrj from 8.6.1 to 8.6.2 in /examples (#3244) Bumps solr-solrj from 8.6.1 to 8.6.2. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/solr-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/solr-container/build.gradle b/examples/solr-container/build.gradle index 89444aeb378..52fa54f1fc5 100644 --- a/examples/solr-container/build.gradle +++ b/examples/solr-container/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly "org.projectlombok:lombok:1.18.10" annotationProcessor "org.projectlombok:lombok:1.18.10" - implementation 'org.apache.solr:solr-solrj:8.6.1' + implementation 'org.apache.solr:solr-solrj:8.6.2' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.testcontainers:solr' From a55fc35345ecd332e2a84a4c451785586d6c6321 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Sep 2020 09:50:51 +0100 Subject: [PATCH 1635/5615] Bump okhttp from 4.8.1 to 4.9.0 in /examples (#3245) Bumps [okhttp](https://github.com/square/okhttp) from 4.8.1 to 4.9.0. - [Release notes](https://github.com/square/okhttp/releases) - [Changelog](https://github.com/square/okhttp/blob/master/CHANGELOG.md) - [Commits](https://github.com/square/okhttp/compare/parent-4.8.1...parent-4.9.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 6877a35fec5..eac2c59df4a 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' - implementation 'com.squareup.okhttp3:okhttp:4.8.1' + implementation 'com.squareup.okhttp3:okhttp:4.9.0' implementation 'org.json:json:20180813' testImplementation 'org.postgresql:postgresql:42.2.16' testImplementation 'ch.qos.logback:logback-classic:1.2.3' From e90150d32b6e1ffc82ae687c85b17ab7e94b2c3f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Sep 2020 09:51:01 +0100 Subject: [PATCH 1636/5615] Bump cucumber-junit from 6.6.0 to 6.7.0 in /examples (#3246) Bumps [cucumber-junit](https://github.com/cucumber/cucumber-jvm) from 6.6.0 to 6.7.0. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v6.6.0...v6.7.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 1362b3f257a..318ed51f24c 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' testImplementation 'io.cucumber:cucumber-java:6.6.0' - testImplementation 'io.cucumber:cucumber-junit:6.6.0' + testImplementation 'io.cucumber:cucumber-junit:6.7.0' testImplementation 'org.testcontainers:selenium' } From c56dcd8d8ee7a90a70dadddc7805836d07dd3a35 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Sep 2020 09:51:37 +0100 Subject: [PATCH 1637/5615] Bump mockito-core from 3.5.7 to 3.5.11 in /core (#3250) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.5.7 to 3.5.11. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.5.7...v3.5.11) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index e233ef7b159..37595c18e7c 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -205,7 +205,7 @@ dependencies { testCompile 'com.rabbitmq:amqp-client:5.9.0' testCompile 'org.mongodb:mongo-java-driver:3.12.7' - testCompile ('org.mockito:mockito-core:3.5.7') { + testCompile ('org.mockito:mockito-core:3.5.11') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From d81f236de640ae0325c7ffd94080aac9dddae358 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Sep 2020 09:51:45 +0100 Subject: [PATCH 1638/5615] Bump s3 from 2.14.16 to 2.14.21 in /modules/localstack (#3254) Bumps s3 from 2.14.16 to 2.14.21. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 3eb02882546..7fa112b3cd2 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,5 +7,5 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.856' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.860' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' - testCompile 'software.amazon.awssdk:s3:2.14.16' + testCompile 'software.amazon.awssdk:s3:2.14.21' } From 53932d9988e8e09db45c8ccea0f39d0da7a24dc5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Sep 2020 09:51:55 +0100 Subject: [PATCH 1639/5615] Bump aws-java-sdk-s3 from 1.11.860 to 1.11.865 in /modules/localstack (#3255) Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.11.860 to 1.11.865. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.860...1.11.865) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 7fa112b3cd2..436f03ab236 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.860' - testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.856' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.865' + testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.865' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.860' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' testCompile 'software.amazon.awssdk:s3:2.14.21' From 27428a0ba798ca909db5b2f3202c15a74f34037e Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 28 Sep 2020 17:12:37 +0100 Subject: [PATCH 1640/5615] Remove dependency upon internet URLs for selenium tests (#3271) --- .../junit/BaseWebDriverContainerTest.java | 50 +++++++------- ...ChromeRecordingWebDriverContainerTest.java | 14 ++-- .../junit/ChromeWebDriverContainerTest.java | 10 +-- .../ContainerWithoutCapabilitiesTest.java | 3 +- ...stomWaitTimeoutWebDriverContainerTest.java | 11 +-- .../junit/FirefoxWebDriverContainerTest.java | 10 +-- .../junit/FlakyContainerCreationTest.java | 31 --------- .../junit/LinkedContainerTest.java | 69 ------------------- ...ecificImageNameWebDriverContainerTest.java | 8 +-- 9 files changed, 50 insertions(+), 156 deletions(-) delete mode 100644 modules/selenium/src/test/java/org/testcontainers/junit/FlakyContainerCreationTest.java delete mode 100644 modules/selenium/src/test/java/org/testcontainers/junit/LinkedContainerTest.java diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java index 0f41b2ce1d2..cf5c762f2ad 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java @@ -1,17 +1,20 @@ package org.testcontainers.junit; import org.jetbrains.annotations.NotNull; +import org.junit.ClassRule; import org.openqa.selenium.By; import org.openqa.selenium.WebElement; import org.openqa.selenium.remote.RemoteWebDriver; -import org.openqa.selenium.support.ui.ExpectedConditions; -import org.openqa.selenium.support.ui.WebDriverWait; import org.testcontainers.containers.BrowserWebDriverContainer; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.Network; +import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; +import org.testcontainers.utility.DockerImageName; -import java.util.List; import java.util.concurrent.TimeUnit; import static java.lang.String.format; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; /** @@ -19,25 +22,31 @@ */ public class BaseWebDriverContainerTest { - protected void doSimpleWebdriverTest(BrowserWebDriverContainer rule) { + @ClassRule + public static Network NETWORK = Network.newNetwork(); + + @ClassRule + public static GenericContainer HELLO_WORLD = new GenericContainer<>(DockerImageName.parse("testcontainers/helloworld:1.0.0")) + .withNetwork(NETWORK) + .withNetworkAliases("helloworld") + .withExposedPorts(8080, 8081) + .waitingFor(new HttpWaitStrategy()); + + protected static void doSimpleExplore(BrowserWebDriverContainer rule) { RemoteWebDriver driver = setupDriverFromRule(rule); System.out.println("Selenium remote URL is: " + rule.getSeleniumAddress()); System.out.println("VNC URL is: " + rule.getVncAddress()); - driver.get("http://www.google.com"); - WebElement search = driver.findElement(By.name("q")); - search.sendKeys("testcontainers"); - search.submit(); + driver.get("http://helloworld:8080"); + WebElement title = driver.findElement(By.tagName("h1")); - List results = new WebDriverWait(driver, 15) - .until(ExpectedConditions.visibilityOfAllElementsLocatedBy(By.cssSelector("#search h3"))); - - assertTrue("the word 'testcontainers' appears in search results", - results.stream() - .anyMatch(el -> el.getText().contains("testcontainers"))); + assertEquals("the index page contains the title 'Hello world'", + "Hello world", + title.getText().trim() + ); } - protected void assertBrowserNameIs(BrowserWebDriverContainer rule, String expectedName) { + protected void assertBrowserNameIs(BrowserWebDriverContainer rule, String expectedName) { RemoteWebDriver driver = setupDriverFromRule(rule); String actual = driver.getCapabilities().getBrowserName(); assertTrue(format("actual browser name is %s", actual), @@ -45,18 +54,9 @@ protected void assertBrowserNameIs(BrowserWebDriverContainer rule, String expect } @NotNull - private static RemoteWebDriver setupDriverFromRule(BrowserWebDriverContainer rule) { + private static RemoteWebDriver setupDriverFromRule(BrowserWebDriverContainer rule) { RemoteWebDriver driver = rule.getWebDriver(); driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS); return driver; } - - protected static void doSimpleExplore(BrowserWebDriverContainer rule) { - RemoteWebDriver driver = setupDriverFromRule(rule); - driver.get("http://en.wikipedia.org/wiki/Randomness"); - - // Oh! The irony! - assertTrue("Randomness' description has the word 'pattern'", driver.findElementByPartialLinkText("pattern").isDisplayed()); - } - } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java index ead9a21ae4f..cf8a6227ec1 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java @@ -33,11 +33,12 @@ public void recordingTestThatShouldBeRecordedAndRetained() { try ( // recordAll { // To do this, simply add extra parameters to the rule constructor: - BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() + BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() .withCapabilities(new ChromeOptions()) .withRecordingMode(RECORD_ALL, target) // } .withRecordingFileFactory(new DefaultRecordingFileFactory()) + .withNetwork(NETWORK) ) { chrome.start(); @@ -69,12 +70,13 @@ public static class ChromeThatRecordsFailingTests { public void recordingTestThatShouldBeRecordedButNotPersisted() { try ( // withRecordingFileFactory { - BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() + BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() // } .withCapabilities(new ChromeOptions()) // withRecordingFileFactory { .withRecordingFileFactory(new CustomRecordingFileFactory()) - // } + // } + .withNetwork(NETWORK) ) { chrome.start(); @@ -88,11 +90,12 @@ public void recordingTestThatShouldBeRecordedAndRetained() { try ( // recordFailing { // or if you only want videos for test failures: - BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() + BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() .withCapabilities(new ChromeOptions()) .withRecordingMode(RECORD_FAILING, target) // } .withRecordingFileFactory(new DefaultRecordingFileFactory()) + .withNetwork(NETWORK) ) { chrome.start(); @@ -115,6 +118,7 @@ public String getFilesystemFriendlyName() { } - private static class CustomRecordingFileFactory extends DefaultRecordingFileFactory { } + private static class CustomRecordingFileFactory extends DefaultRecordingFileFactory { + } } } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java index 92ba4e2cd37..bea1909a145 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java @@ -13,20 +13,16 @@ public class ChromeWebDriverContainerTest extends BaseWebDriverContainerTest { // junitRule { @Rule - public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() - .withCapabilities(new ChromeOptions()); + public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() + .withCapabilities(new ChromeOptions()) // } + .withNetwork(NETWORK); @Before public void checkBrowserIsIndeedChrome() { assertBrowserNameIs(chrome, "chrome"); } - @Test - public void simpleTest() { - doSimpleWebdriverTest(chrome); - } - @Test public void simpleExploreTest() { doSimpleExplore(chrome); diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/ContainerWithoutCapabilitiesTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/ContainerWithoutCapabilitiesTest.java index c319395211c..b1214d18b60 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/ContainerWithoutCapabilitiesTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/ContainerWithoutCapabilitiesTest.java @@ -7,7 +7,8 @@ public class ContainerWithoutCapabilitiesTest extends BaseWebDriverContainerTest{ @Rule - public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer(); + public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() + .withNetwork(NETWORK); @Test public void chromeIsStartedIfNoCapabilitiesProvided() { diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/CustomWaitTimeoutWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/CustomWaitTimeoutWebDriverContainerTest.java index 1d0f895b682..8448dc40d53 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/CustomWaitTimeoutWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/CustomWaitTimeoutWebDriverContainerTest.java @@ -15,12 +15,13 @@ public class CustomWaitTimeoutWebDriverContainerTest extends BaseWebDriverContainerTest { @Rule - public BrowserWebDriverContainer chromeWithCustomTimeout = new BrowserWebDriverContainer<>() - .withCapabilities(new ChromeOptions()) - .withStartupTimeout(Duration.of(30, SECONDS)); + public BrowserWebDriverContainer chromeWithCustomTimeout = new BrowserWebDriverContainer<>() + .withCapabilities(new ChromeOptions()) + .withStartupTimeout(Duration.of(30, SECONDS)) + .withNetwork(NETWORK); @Test - public void simpleTest() { - doSimpleWebdriverTest(chromeWithCustomTimeout); + public void simpleExploreTest() { + doSimpleExplore(chromeWithCustomTimeout); } } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/FirefoxWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/FirefoxWebDriverContainerTest.java index 1f687aab762..1cffec16bc6 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/FirefoxWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/FirefoxWebDriverContainerTest.java @@ -13,20 +13,16 @@ public class FirefoxWebDriverContainerTest extends BaseWebDriverContainerTest { // junitRule { @Rule - public BrowserWebDriverContainer firefox = new BrowserWebDriverContainer() - .withCapabilities(new FirefoxOptions()); + public BrowserWebDriverContainer firefox = new BrowserWebDriverContainer<>() + .withCapabilities(new FirefoxOptions()) // } + .withNetwork(NETWORK); @Before public void checkBrowserIsIndeedFirefox() { assertBrowserNameIs(firefox, "firefox"); } - @Test - public void simpleTest() { - doSimpleWebdriverTest(firefox); - } - @Test public void simpleExploreTest() { doSimpleExplore(firefox); diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/FlakyContainerCreationTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/FlakyContainerCreationTest.java deleted file mode 100644 index d33a4c9c07e..00000000000 --- a/modules/selenium/src/test/java/org/testcontainers/junit/FlakyContainerCreationTest.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.testcontainers.junit; - -import org.junit.Ignore; -import org.junit.Test; -import org.openqa.selenium.chrome.ChromeOptions; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.testcontainers.containers.BrowserWebDriverContainer; - -import java.io.File; - -/** - * Created by rnorth on 30/01/2016. - */ -public class FlakyContainerCreationTest { - - @Test @Ignore - public void testCreationOfManyContainers() { - for (int i = 0; i < 50; i++) { - BrowserWebDriverContainer container = new BrowserWebDriverContainer() - .withCapabilities(new ChromeOptions()) - .withRecordingMode(BrowserWebDriverContainer.VncRecordingMode.RECORD_FAILING, new File("build")); - - container.start(); - RemoteWebDriver driver = container.getWebDriver(); - - driver.get("http://www.google.com"); - - container.stop(); - } - } -} diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/LinkedContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/LinkedContainerTest.java deleted file mode 100644 index d3e458e07ec..00000000000 --- a/modules/selenium/src/test/java/org/testcontainers/junit/LinkedContainerTest.java +++ /dev/null @@ -1,69 +0,0 @@ -package org.testcontainers.junit; - -import lombok.Cleanup; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.openqa.selenium.By; -import org.openqa.selenium.chrome.ChromeOptions; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.testcontainers.SeleniumTestImages; -import org.testcontainers.containers.BrowserWebDriverContainer; -import org.testcontainers.containers.Network; -import org.testcontainers.containers.NginxContainer; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.PrintStream; - -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; - - -/** - * @author richardnorth - */ -public class LinkedContainerTest { - - private static File contentFolder = new File(System.getProperty("user.home") + "/.tmp-test-container"); - - @Rule - public Network network = Network.newNetwork(); - - @Rule - public NginxContainer nginx = new NginxContainer<>(SeleniumTestImages.NGINX_TEST_IMAGE) - .withNetwork(network) - .withNetworkAliases("nginx") - .withCustomContent(contentFolder.toString()); - - @Rule - public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() - .withNetwork(network) - .withCapabilities(new ChromeOptions()); - - @BeforeClass - public static void setupContent() throws FileNotFoundException { - contentFolder.mkdir(); - contentFolder.setReadable(true, false); - contentFolder.setWritable(true, false); - contentFolder.setExecutable(true, false); - - - File indexFile = new File(contentFolder, "index.html"); - indexFile.setReadable(true, false); - indexFile.setWritable(true, false); - indexFile.setExecutable(true, false); - - @Cleanup PrintStream printStream = new PrintStream(new FileOutputStream(indexFile)); - printStream.println("This worked"); - } - - @Test - public void testWebDriverToNginxContainerAccessViaContainerLink() { - RemoteWebDriver driver = chrome.getWebDriver(); - - driver.get("http://nginx/"); - - assertEquals("Using selenium, an HTTP GET from the nginx server returns the index.html from the custom content directory", "This worked", driver.findElement(By.tagName("body")).getText()); - } -} diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java index 6ecf0899ede..bbc3b4db7a0 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java @@ -12,12 +12,8 @@ public class SpecificImageNameWebDriverContainerTest extends BaseWebDriverContai @Rule public BrowserWebDriverContainer firefox = new BrowserWebDriverContainer<>(FIREFOX_IMAGE) - .withCapabilities(new FirefoxOptions()); - - @Test - public void simpleTest() { - doSimpleWebdriverTest(firefox); - } + .withCapabilities(new FirefoxOptions()) + .withNetwork(NETWORK); @Test public void simpleExploreTest() { From 149badfb729504b91b9127d7e30c014741942918 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Sep 2020 19:11:46 +0100 Subject: [PATCH 1641/5615] Bump aws-java-sdk-s3 from 1.11.865 to 1.11.870 in /modules/localstack (#3285) Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.11.865 to 1.11.870. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.865...1.11.870) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 436f03ab236..f48b3cf71bb 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.865' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.870' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.865' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.860' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' From 63e2c49bbf88d9d5b4972051c5a127a34ee8a616 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 29 Sep 2020 09:45:39 +0100 Subject: [PATCH 1642/5615] Make testMatrix task emit all check tasks, rather than filtering out up-to-date tasks (#3287) --- gradle/ci-support.gradle | 3 --- 1 file changed, 3 deletions(-) diff --git a/gradle/ci-support.gradle b/gradle/ci-support.gradle index e83c936f2f3..b878c107796 100644 --- a/gradle/ci-support.gradle +++ b/gradle/ci-support.gradle @@ -9,10 +9,7 @@ task testMatrix { dependsOn(checkTasks) doLast { - // Obtain a list of check tasks that are not up-to-date, i.e. - // the ones which Gradle cannot find a cached output for. def checkTaskPaths = checkTasks - .findAll { !it.state.upToDate } .collect { it.path } println(JsonOutput.toJson(checkTaskPaths)) From 1e597ccd4fa1a7113d3077599e0b13237c1fef51 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 29 Sep 2020 11:02:01 +0100 Subject: [PATCH 1643/5615] Add image compatibility checks (#3021) Co-authored-by: Kevin Wittek --- build.gradle | 2 +- .../containers/GenericContainer.java | 76 +++++---- .../utility/DockerImageName.java | 152 ++++++++++++------ .../utility/TestcontainersConfiguration.java | 51 +++--- .../testcontainers/utility/Versioning.java | 95 +++++++++++ .../utility/AuthenticatedImagePullTest.java | 17 +- .../DockerImageNameCompatibilityTest.java | 93 +++++++++++ .../utility/DockerImageNameTest.java | 4 +- .../TestcontainersConfigurationTest.java | 11 +- docs/modules/kafka.md | 4 +- .../containers/MongoDbContainer.java | 1 - .../containers/CassandraContainer.java | 14 +- .../containers/ClickHouseContainer.java | 15 +- .../containers/CockroachContainer.java | 21 ++- .../couchbase/CouchbaseContainer.java | 14 +- .../containers/Db2Container.java | 17 +- .../dynamodb/DynaliteContainer.java | 14 +- .../elasticsearch/ElasticsearchContainer.java | 31 ++-- .../ElasticsearchContainerTest.java | 17 +- .../containers/InfluxDBContainer.java | 14 +- .../containers/JdbcDatabaseContainer.java | 1 - .../containers/KafkaContainer.java | 9 +- .../containers/KafkaContainerTest.java | 43 +++-- .../localstack/LocalStackContainer.java | 31 ++-- .../containers/MariaDBContainer.java | 19 ++- .../containers/MockServerContainer.java | 13 +- .../containers/MongoDBContainer.java | 13 +- .../MSSQLR2DBCDatabaseContainerProvider.java | 1 + .../containers/MSSQLServerContainer.java | 17 +- .../containers/MySQLContainer.java | 17 +- .../containers/Neo4jContainer.java | 50 +++--- .../containers/NginxContainer.java | 16 +- .../containers/OracleContainer.java | 4 - .../containers/OrientDBContainer.java | 11 +- .../containers/PostgisContainerProvider.java | 4 +- .../containers/PostgreSQLContainer.java | 11 +- .../containers/PrestoContainer.java | 10 +- .../containers/PulsarContainer.java | 8 +- .../containers/RabbitMQContainer.java | 12 +- .../containers/BrowserWebDriverContainer.java | 60 +++---- .../testcontainers/junit/Selenium3xTest.java | 4 +- ...ecificImageNameWebDriverContainerTest.java | 3 +- .../containers/SolrContainer.java | 13 +- .../containers/ToxiproxyContainer.java | 12 +- .../testcontainers/vault/VaultContainer.java | 11 +- 45 files changed, 692 insertions(+), 364 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/utility/Versioning.java create mode 100644 core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java diff --git a/build.gradle b/build.gradle index b7830992b07..c51b24909cb 100644 --- a/build.gradle +++ b/build.gradle @@ -27,7 +27,7 @@ subprojects { } lombok { - version = '1.18.8' + version = '1.18.12' } task delombok(type: io.franzbecker.gradle.lombok.task.DelombokTask) { diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 22a6c2408d6..95050d8f8ee 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -1,8 +1,10 @@ package org.testcontainers.containers; +import static com.google.common.collect.Lists.newArrayList; +import static org.testcontainers.utility.CommandLine.runShellCommand; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.MapperFeature; -import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.CreateContainerCmd; @@ -21,6 +23,39 @@ import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; import com.google.common.hash.Hashing; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.UndeclaredThrowableException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.zip.Adler32; +import java.util.zip.Checksum; import lombok.AccessLevel; import lombok.Data; import lombok.NonNull; @@ -62,43 +97,6 @@ import org.testcontainers.utility.ResourceReaper; import org.testcontainers.utility.TestcontainersConfiguration; -import java.io.File; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.UndeclaredThrowableException; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.time.Duration; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import java.util.zip.Adler32; -import java.util.zip.Checksum; - -import static com.google.common.collect.Lists.newArrayList; -import static org.testcontainers.utility.CommandLine.runShellCommand; - /** * Base class for that allows a container to be launched and controlled. */ @@ -241,7 +239,7 @@ public GenericContainer(@NonNull final RemoteDockerImage image) { */ @Deprecated public GenericContainer() { - this(TestcontainersConfiguration.getInstance().getTinyImage()); + this(TestcontainersConfiguration.getInstance().getTinyDockerImageName().asCanonicalNameString()); } /** diff --git a/core/src/main/java/org/testcontainers/utility/DockerImageName.java b/core/src/main/java/org/testcontainers/utility/DockerImageName.java index b4a40be2f16..af8e5300863 100644 --- a/core/src/main/java/org/testcontainers/utility/DockerImageName.java +++ b/core/src/main/java/org/testcontainers/utility/DockerImageName.java @@ -4,26 +4,32 @@ import com.google.common.net.HostAndPort; import lombok.AccessLevel; import lombok.AllArgsConstructor; -import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.With; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.testcontainers.utility.Versioning.Sha256Versioning; +import org.testcontainers.utility.Versioning.TagVersioning; import java.util.regex.Pattern; -@EqualsAndHashCode(exclude = "rawName") +@EqualsAndHashCode(exclude = { "rawName", "compatibleSubstituteFor" }) @AllArgsConstructor(access = AccessLevel.PRIVATE) public final class DockerImageName { /* Regex patterns used for validation */ private static final String ALPHA_NUMERIC = "[a-z0-9]+"; - private static final String SEPARATOR = "([\\.]{1}|_{1,2}|-+)"; + private static final String SEPARATOR = "([.]|_{1,2}|-+)"; private static final String REPO_NAME_PART = ALPHA_NUMERIC + "(" + SEPARATOR + ALPHA_NUMERIC + ")*"; private static final Pattern REPO_NAME = Pattern.compile(REPO_NAME_PART + "(/" + REPO_NAME_PART + ")*"); private final String rawName; private final String registry; private final String repo; - @NotNull private final Versioning versioning; + @NotNull @With(AccessLevel.PRIVATE) + private final Versioning versioning; + @Nullable @With(AccessLevel.PRIVATE) + private final DockerImageName compatibleSubstituteFor; /** * Parses a docker image name from a provided string. @@ -52,8 +58,8 @@ public DockerImageName(String fullImageName) { String remoteName; if (slashIndex == -1 || (!fullImageName.substring(0, slashIndex).contains(".") && - !fullImageName.substring(0, slashIndex).contains(":") && - !fullImageName.substring(0, slashIndex).equals("localhost"))) { + !fullImageName.substring(0, slashIndex).contains(":") && + !fullImageName.substring(0, slashIndex).equals("localhost"))) { registry = ""; remoteName = fullImageName; } else { @@ -69,8 +75,10 @@ public DockerImageName(String fullImageName) { versioning = new TagVersioning(remoteName.split(":")[1]); } else { repo = remoteName; - versioning = new TagVersioning("latest"); + versioning = Versioning.ANY; } + + compatibleSubstituteFor = null; } /** @@ -92,8 +100,8 @@ public DockerImageName(String nameWithoutTag, @NotNull String version) { String remoteName; if (slashIndex == -1 || (!nameWithoutTag.substring(0, slashIndex).contains(".") && - !nameWithoutTag.substring(0, slashIndex).contains(":") && - !nameWithoutTag.substring(0, slashIndex).equals("localhost"))) { + !nameWithoutTag.substring(0, slashIndex).contains(":") && + !nameWithoutTag.substring(0, slashIndex).equals("localhost"))) { registry = ""; remoteName = nameWithoutTag; } else { @@ -108,6 +116,8 @@ public DockerImageName(String nameWithoutTag, @NotNull String version) { repo = remoteName; versioning = new TagVersioning(version); } + + compatibleSubstituteFor = null; } /** @@ -132,7 +142,7 @@ public String getVersionPart() { * @return canonical name for the image */ public String asCanonicalNameString() { - return getUnversionedPart() + versioning.getSeparator() + versioning.toString(); + return getUnversionedPart() + versioning.getSeparator() + getVersionPart(); } @Override @@ -146,7 +156,8 @@ public String toString() { * @throws IllegalArgumentException if not valid */ public void assertValid() { - HostAndPort.fromString(registry); + //noinspection UnstableApiUsage + HostAndPort.fromString(registry); // return value ignored - this throws if registry is not a valid host:port string if (!REPO_NAME.matcher(repo).matches()) { throw new IllegalArgumentException(repo + " is not a valid Docker image name (in " + rawName + ")"); } @@ -159,63 +170,98 @@ public String getRegistry() { return registry; } + /** + * @param newTag version tag for the copy to use + * @return an immutable copy of this {@link DockerImageName} with the new version tag + */ public DockerImageName withTag(final String newTag) { - return new DockerImageName(rawName, registry, repo, new TagVersioning(newTag)); + return withVersioning(new TagVersioning(newTag)); } - private interface Versioning { - boolean isValid(); - - String getSeparator(); + /** + * Declare that this {@link DockerImageName} is a compatible substitute for another image - i.e. that this image + * behaves as the other does, and is compatible with Testcontainers' assumptions about the other image. + * + * @param otherImageName the image name of the other image + * @return an immutable copy of this {@link DockerImageName} with the compatibility declaration attached. + */ + public DockerImageName asCompatibleSubstituteFor(String otherImageName) { + return withCompatibleSubstituteFor(DockerImageName.parse(otherImageName)); } - @Data - private static class TagVersioning implements Versioning { - public static final String TAG_REGEX = "[\\w][\\w\\.\\-]{0,127}"; - private final String tag; - - TagVersioning(String tag) { - this.tag = tag; - } + /** + * Declare that this {@link DockerImageName} is a compatible substitute for another image - i.e. that this image + * behaves as the other does, and is compatible with Testcontainers' assumptions about the other image. + * + * @param otherImageName the image name of the other image + * @return an immutable copy of this {@link DockerImageName} with the compatibility declaration attached. + */ + public DockerImageName asCompatibleSubstituteFor(DockerImageName otherImageName) { + return withCompatibleSubstituteFor(otherImageName); + } - @Override - public boolean isValid() { - return tag.matches(TAG_REGEX); + /** + * Test whether this {@link DockerImageName} has declared compatibility with another image (set using + * {@link DockerImageName#asCompatibleSubstituteFor(String)} or + * {@link DockerImageName#asCompatibleSubstituteFor(DockerImageName)}. + *

+ * If a version tag part is present in the other image name, the tags must exactly match, unless it + * is 'latest'. If a version part is not present in the other image name, the tag contents are ignored. + * + * @param other the other image that we are trying to test compatibility with + * @return whether this image has declared compatibility. + */ + public boolean isCompatibleWith(DockerImageName other) { + // is this image already the same or equivalent? + if (other.equals(this)) { + return true; } - @Override - public String getSeparator() { - return ":"; + if (this.compatibleSubstituteFor == null) { + return false; } - @Override - public String toString() { - return tag; - } + return this.compatibleSubstituteFor.isCompatibleWith(other); } - @Data - private static class Sha256Versioning implements Versioning { - public static final String HASH_REGEX = "[0-9a-fA-F]{32,}"; - private final String hash; - - Sha256Versioning(String hash) { - this.hash = hash; - } - - @Override - public boolean isValid() { - return hash.matches(HASH_REGEX); + /** + * Behaves as {@link DockerImageName#isCompatibleWith(DockerImageName)} but throws an exception + * rather than returning false if a mismatch is detected. + * + * @param anyOthers the other image(s) that we are trying to check compatibility with. If more + * than one is provided, this method will check compatibility with at least one + * of them. + * @throws IllegalStateException if {@link DockerImageName#isCompatibleWith(DockerImageName)} + * returns false + */ + public void assertCompatibleWith(DockerImageName... anyOthers) { + if (anyOthers.length == 0) { + throw new IllegalArgumentException("anyOthers parameter must be non-empty"); } - @Override - public String getSeparator() { - return "@"; + for (DockerImageName anyOther : anyOthers) { + if (this.isCompatibleWith(anyOther)) { + return; + } } - @Override - public String toString() { - return "sha256:" + hash; - } + final DockerImageName exampleOther = anyOthers[0]; + + throw new IllegalStateException( + String.format( + "Failed to verify that image '%s' is a compatible substitute for '%s'. This generally means that " + + + "you are trying to use an image that Testcontainers has not been designed to use. If this is " + + + "deliberate, and if you are confident that the image is compatible, you should declare " + + + "compatibility in code using the `asCompatibleSubstituteFor` method. For example:\n" + + + " DockerImageName myImage = DockerImageName.parse(\"%s\").asCompatibleSubstituteFor(\"%s\");\n" + + + "and then use `myImage` instead.", + this.rawName, exampleOther.rawName, this.rawName, exampleOther.rawName + ) + ); } } diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 9f283ba2685..521a7519c2a 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -56,49 +56,58 @@ static AtomicReference getInstanceField() { this.properties.putAll(classpathProperties); this.properties.putAll(environmentProperties); - properties.keySet() - .forEach(key -> properties.replace(key, properties.getProperty(String.valueOf(key)).trim())); + } + + private DockerImageName getImage(final String key, final String defaultValue) { + return DockerImageName + .parse(properties.getProperty(key, defaultValue).trim()) + .asCompatibleSubstituteFor(defaultValue); } @Deprecated public String getAmbassadorContainerImage() { - return String.valueOf(properties.getOrDefault("ambassador.container.image", "richnorth/ambassador:latest")); + return getAmbassadorContainerDockerImageName().asCanonicalNameString(); + } + + @Deprecated + public DockerImageName getAmbassadorContainerDockerImageName() { + return getImage("ambassador.container.image", "richnorth/ambassador:latest"); } @Deprecated public String getSocatContainerImage() { - return String.valueOf(properties.getOrDefault("socat.container.image", "alpine/socat:latest")); + return getSocatDockerImageName().asCanonicalNameString(); } public DockerImageName getSocatDockerImageName() { - return DockerImageName.parse(getSocatContainerImage()); + return getImage("socat.container.image", "alpine/socat:latest"); } @Deprecated public String getVncRecordedContainerImage() { - return String.valueOf(properties.getOrDefault("vncrecorder.container.image", "testcontainers/vnc-recorder:1.1.0")); + return getVncDockerImageName().asCanonicalNameString(); } public DockerImageName getVncDockerImageName() { - return DockerImageName.parse(getVncRecordedContainerImage()); + return getImage("vncrecorder.container.image", "testcontainers/vnc-recorder:1.1.0"); } @Deprecated public String getDockerComposeContainerImage() { - return String.valueOf(properties.getOrDefault("compose.container.image", "docker/compose:1.24.1")); + return getDockerComposeDockerImageName().asCanonicalNameString(); } public DockerImageName getDockerComposeDockerImageName() { - return DockerImageName.parse(getDockerComposeContainerImage()); + return getImage("compose.container.image", "docker/compose:1.24.1"); } @Deprecated public String getTinyImage() { - return String.valueOf(properties.getOrDefault("tinyimage.container.image", "alpine:3.5")); + return getTinyDockerImageName().asCanonicalNameString(); } public DockerImageName getTinyDockerImageName() { - return DockerImageName.parse(getTinyImage()); + return getImage("tinyimage.container.image", "alpine:3.5"); } public boolean isRyukPrivileged() { @@ -107,20 +116,20 @@ public boolean isRyukPrivileged() { @Deprecated public String getRyukImage() { - return String.valueOf(properties.getOrDefault("ryuk.container.image", "testcontainers/ryuk:0.3.0")); + return getRyukDockerImageName().asCanonicalNameString(); } public DockerImageName getRyukDockerImageName() { - return DockerImageName.parse(getRyukImage()); + return getImage("ryuk.container.image", "testcontainers/ryuk:0.3.0"); } @Deprecated public String getSSHdImage() { - return String.valueOf(properties.getOrDefault("sshd.container.image", "testcontainers/sshd:1.0.0")); + return getSSHdDockerImageName().asCanonicalNameString(); } public DockerImageName getSSHdDockerImageName() { - return DockerImageName.parse(getSSHdImage()); + return getImage("sshd.container.image", "testcontainers/sshd:1.0.0"); } public Integer getRyukTimeout() { @@ -129,29 +138,29 @@ public Integer getRyukTimeout() { @Deprecated public String getKafkaImage() { - return String.valueOf(properties.getOrDefault("kafka.container.image", "confluentinc/cp-kafka")); + return getKafkaDockerImageName().asCanonicalNameString(); } public DockerImageName getKafkaDockerImageName() { - return DockerImageName.parse(getKafkaImage()); + return getImage("kafka.container.image", "confluentinc/cp-kafka"); } @Deprecated public String getPulsarImage() { - return String.valueOf(properties.getOrDefault("pulsar.container.image", "apachepulsar/pulsar")); + return getPulsarDockerImageName().asCanonicalNameString(); } public DockerImageName getPulsarDockerImageName() { - return DockerImageName.parse(getPulsarImage()); + return getImage("pulsar.container.image", "apachepulsar/pulsar"); } @Deprecated public String getLocalStackImage() { - return String.valueOf(properties.getOrDefault("localstack.container.image", "localstack/localstack")); + return getLocalstackDockerImageName().asCanonicalNameString(); } public DockerImageName getLocalstackDockerImageName() { - return DockerImageName.parse(getLocalStackImage()); + return getImage("localstack.container.image", "localstack/localstack"); } public boolean isDisableChecks() { diff --git a/core/src/main/java/org/testcontainers/utility/Versioning.java b/core/src/main/java/org/testcontainers/utility/Versioning.java new file mode 100644 index 00000000000..8944b3f0ba6 --- /dev/null +++ b/core/src/main/java/org/testcontainers/utility/Versioning.java @@ -0,0 +1,95 @@ +package org.testcontainers.utility; + +import lombok.AccessLevel; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * Represents mechanisms for versioning docker images. + */ +interface Versioning { + AnyVersion ANY = new AnyVersion(); + + boolean isValid(); + + String getSeparator(); + + @NoArgsConstructor(access = AccessLevel.PRIVATE) + class AnyVersion implements Versioning { + + @Override + public boolean isValid() { + return true; + } + + @Override + public String getSeparator() { + return ""; + } + + @Override + public String toString() { + return ""; + } + + @Override + public boolean equals(final Object obj) { + return obj instanceof Versioning; + } + + @Override + public int hashCode() { + return super.hashCode(); + } + } + + @EqualsAndHashCode + class TagVersioning implements Versioning { + public static final String TAG_REGEX = "[\\w][\\w.\\-]{0,127}"; + private final String tag; + + TagVersioning(String tag) { + this.tag = tag; + } + + @Override + public boolean isValid() { + return tag.matches(TAG_REGEX); + } + + @Override + public String getSeparator() { + return ":"; + } + + @Override + public String toString() { + return tag; + } + } + + @EqualsAndHashCode + class Sha256Versioning implements Versioning { + public static final String HASH_REGEX = "[0-9a-fA-F]{32,}"; + private final String hash; + + Sha256Versioning(String hash) { + this.hash = hash; + } + + @Override + public boolean isValid() { + return hash.matches(HASH_REGEX); + } + + @Override + public String getSeparator() { + return "@"; + } + + @Override + public String toString() { + return "sha256:" + hash; + } + } +} diff --git a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java index 0afb4d956a1..e49ff2880df 100644 --- a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java +++ b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java @@ -59,7 +59,7 @@ public class AuthenticatedImagePullTest { private static DockerClient client; private static String testImageName; - private static String testImageNameWithTag; + private static RegistryAuthLocator mockAuthLocator; @BeforeClass public static void setUp() throws InterruptedException { @@ -68,9 +68,8 @@ public static void setUp() throws InterruptedException { String testRegistryAddress = authenticatedRegistry.getHost() + ":" + authenticatedRegistry.getFirstMappedPort(); testImageName = testRegistryAddress + "/alpine"; - testImageNameWithTag = testImageName + ":latest"; - final DockerImageName expectedName = DockerImageName.parse(testImageNameWithTag); + final DockerImageName expectedName = DockerImageName.parse(testImageName); final AuthConfig authConfig = new AuthConfig() .withUsername("testuser") .withPassword("notasecret") @@ -89,7 +88,7 @@ public static void setUp() throws InterruptedException { @Before public void removeImageFromLocalDocker() { // remove the image tag from local docker so that it must be pulled before use - client.removeImageCmd(testImageNameWithTag).withForce(true).exec(); + client.removeImageCmd(testImageName).withForce(true).exec(); } @AfterClass @@ -100,7 +99,7 @@ public static void tearDown() { @Test public void testThatAuthLocatorIsUsedForContainerCreation() { // actually start a container, which will require an authenticated pull - try (final GenericContainer container = new GenericContainer<>(DockerImageName.parse(testImageNameWithTag)) + try (final GenericContainer container = new GenericContainer<>(DockerImageName.parse(testImageName)) .withCommand("/bin/sh", "-c", "sleep 10")) { container.start(); @@ -112,7 +111,7 @@ public void testThatAuthLocatorIsUsedForContainerCreation() { public void testThatAuthLocatorIsUsedForDockerfileBuild() throws IOException { // Prepare a simple temporary Dockerfile which requires our custom private image Path tempFile = getLocalTempFile(".Dockerfile"); - String dockerFileContent = "FROM " + testImageNameWithTag; + String dockerFileContent = "FROM " + testImageName; Files.write(tempFile, dockerFileContent.getBytes()); // Start a container built from a derived image, which will require an authenticated pull @@ -136,7 +135,7 @@ public void testThatAuthLocatorIsUsedForDockerComposePull() throws IOException { "services:\n" + " privateservice:\n" + " command: /bin/sh -c 'sleep 60'\n" + - " image: " + testImageNameWithTag; + " image: " + testImageName; Files.write(tempFile, composeFileContent.getBytes()); // Start the docker compose project, which will require an authenticated pull @@ -177,9 +176,9 @@ private static void putImageInRegistry() throws InterruptedException { .getId(); // push the image to the registry - client.tagImageCmd(id, testImageName, "latest").exec(); + client.tagImageCmd(id, testImageName, "").exec(); - client.pushImageCmd(testImageNameWithTag) + client.pushImageCmd(testImageName) .exec(new ResultCallback.Adapter<>()) .awaitCompletion(1, TimeUnit.MINUTES); } diff --git a/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java b/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java new file mode 100644 index 00000000000..9e7136ea6d3 --- /dev/null +++ b/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java @@ -0,0 +1,93 @@ +package org.testcontainers.utility; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import static org.hamcrest.core.StringContains.containsString; +import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; + + +public class DockerImageNameCompatibilityTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void testPlainImage() { + DockerImageName subject = DockerImageName.parse("foo"); + + assertFalse("image name foo != bar", subject.isCompatibleWith(DockerImageName.parse("bar"))); + } + @Test + public void testNoTagTreatedAsWildcard() { + final DockerImageName subject = DockerImageName.parse("foo:4.5.6"); + /* + foo:1.2.3 != foo:4.5.6 + foo:1.2.3 ~= foo + foo:1.2.3 ~= foo:latest + + The test is effectively making sure that no tag and `latest` tag are equivalent + */ + assertFalse("foo:4.5.6 != foo:1.2.3", subject.isCompatibleWith(DockerImageName.parse("foo:1.2.3"))); + assertTrue("foo:4.5.6 ~= foo", subject.isCompatibleWith(DockerImageName.parse("foo"))); + } + + @Test + public void testImageWithAutomaticCompatibilityForFullPath() { + DockerImageName subject = DockerImageName.parse("repo/foo:1.2.3"); + + assertTrue("repo/foo:1.2.3 ~= repo/foo", subject.isCompatibleWith(DockerImageName.parse("repo/foo"))); + } + + @Test + public void testImageWithClaimedCompatibility() { + DockerImageName subject = DockerImageName.parse("foo").asCompatibleSubstituteFor("bar"); + + assertTrue("foo(bar) ~= bar", subject.isCompatibleWith(DockerImageName.parse("bar"))); + assertFalse("foo(bar) != fizz", subject.isCompatibleWith(DockerImageName.parse("fizz"))); + } + + @Test + public void testImageWithClaimedCompatibilityAndVersion() { + DockerImageName subject = DockerImageName.parse("foo:1.2.3").asCompatibleSubstituteFor("bar"); + + assertTrue("foo:1.2.3(bar) ~= bar", subject.isCompatibleWith(DockerImageName.parse("bar"))); + } + + @Test + public void testImageWithClaimedCompatibilityForFullPath() { + DockerImageName subject = DockerImageName.parse("foo").asCompatibleSubstituteFor("registry/repo/bar"); + + assertTrue("foo(registry/repo/bar) ~= registry/repo/bar", subject.isCompatibleWith(DockerImageName.parse("registry/repo/bar"))); + assertFalse("foo(registry/repo/bar) != repo/bar", subject.isCompatibleWith(DockerImageName.parse("repo/bar"))); + assertFalse("foo(registry/repo/bar) != bar", subject.isCompatibleWith(DockerImageName.parse("bar"))); + } + + @Test + public void testImageWithClaimedCompatibilityForVersion() { + DockerImageName subject = DockerImageName.parse("foo").asCompatibleSubstituteFor("bar:1.2.3"); + + assertTrue("foo(bar:1.2.3) ~= bar", subject.isCompatibleWith(DockerImageName.parse("bar"))); + assertTrue("foo(bar:1.2.3) ~= bar:1.2.3", subject.isCompatibleWith(DockerImageName.parse("bar:1.2.3"))); + assertFalse("foo(bar:1.2.3) != bar:0.0.1", subject.isCompatibleWith(DockerImageName.parse("bar:0.0.1"))); + assertFalse("foo(bar:1.2.3) != bar:2.0.0", subject.isCompatibleWith(DockerImageName.parse("bar:2.0.0"))); + assertFalse("foo(bar:1.2.3) != bar:1.2.4", subject.isCompatibleWith(DockerImageName.parse("bar:1.2.4"))); + } + + @Test + public void testAssertMethodAcceptsCompatible() { + DockerImageName subject = DockerImageName.parse("foo").asCompatibleSubstituteFor("bar"); + subject.assertCompatibleWith(DockerImageName.parse("bar")); + } + + @Test + public void testAssertMethodRejectsIncompatible() { + thrown.expect(IllegalStateException.class); + thrown.expectMessage(containsString("Failed to verify that image 'foo' is a compatible substitute for 'bar'")); + + DockerImageName subject = DockerImageName.parse("foo"); + subject.assertCompatibleWith(DockerImageName.parse("bar")); + } +} diff --git a/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java b/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java index b0ed9428a5d..34575f2b4b9 100644 --- a/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java +++ b/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java @@ -112,7 +112,7 @@ public void testParsing() { canonicalName = unversionedPart + versionSeparator + version; } else { combined = unversionedPart; - canonicalName = unversionedPart + ":latest"; + canonicalName = unversionedPart; } VisibleAssertions.context("For " + combined); @@ -124,7 +124,7 @@ public void testParsing() { if (version != null) { assertEquals(combined + " has version part: " + version, version, imageName.getVersionPart()); } else { - assertEquals(combined + " has implicit version: latest", "latest", imageName.getVersionPart()); + assertEquals(combined + " has no version specified", "", imageName.getVersionPart()); } assertEquals(combined + " has canonical name: " + canonicalName, canonicalName, imageName.asCanonicalNameString()); diff --git a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java index f0b8fb36a37..ccd68fbd984 100644 --- a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java +++ b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java @@ -1,14 +1,13 @@ package org.testcontainers.utility; -import org.junit.Test; - -import java.util.Properties; -import java.util.UUID; - import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import java.util.Properties; +import java.util.UUID; +import org.junit.Test; + public class TestcontainersConfigurationTest { final Properties environmentProperties = new Properties(); @@ -48,7 +47,7 @@ public void shouldReadReuseFromEnvironmentOnly() { assertTrue("reuse enabled", newConfig().environmentSupportsReuse()); environmentProperties.setProperty("ryuk.container.image", " testcontainersofficial/ryuk:0.3.0 "); - assertEquals("trailing whitespace was not removed from image name property", "testcontainersofficial/ryuk:0.3.0",newConfig().getRyukImage()); + assertEquals("trailing whitespace was not removed from image name property", "testcontainersofficial/ryuk:0.3.0",newConfig().getRyukDockerImageName().asCanonicalNameString()); } diff --git a/docs/modules/kafka.md b/docs/modules/kafka.md index 83bb8af0628..d36273ac8e1 100644 --- a/docs/modules/kafka.md +++ b/docs/modules/kafka.md @@ -26,7 +26,7 @@ Now your tests or any other process running on your machine can get access to ru ### Selecting Kafka version -You can select a version of Confluent Platform by passing it to the container's constructor: +You can select a specific Confluent Platform Kafka docker image by passing it to the container's constructor: [Version Constructor](../../modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java) inside_block:constructorWithVersion @@ -44,7 +44,7 @@ If for some reason you want to use an externally running Zookeeper, then just pa ## Multi-container usage -If your test needs to run some other Docker container which needs access to the Kafka, do the following: +If your test needs to run some other Docker container which needs access to Kafka, do the following: * Run your other container on the same network as Kafka container, e.g.: diff --git a/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainer.java b/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainer.java index e44f2bb982e..c4c07c1f601 100644 --- a/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainer.java +++ b/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainer.java @@ -38,7 +38,6 @@ public MongoDbContainer() { * @param image the image (e.g. {@value DEFAULT_IMAGE_AND_TAG}) to use * @deprecated use {@link MongoDbContainer(DockerImageName)} instead */ - @Deprecated public MongoDbContainer(@NotNull String image) { this(DockerImageName.parse(image)); } diff --git a/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java b/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java index 681c58f4efb..597b1c9b017 100644 --- a/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java +++ b/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java @@ -25,8 +25,11 @@ */ public class CassandraContainer> extends GenericContainer { + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("cassandra"); + private static final String DEFAULT_TAG = "3.11.2"; + @Deprecated - public static final String IMAGE = "cassandra"; + public static final String IMAGE = DEFAULT_IMAGE_NAME.getUnversionedPart(); public static final Integer CQL_PORT = 9042; private static final String CONTAINER_CONFIG_LOCATION = "/etc/cassandra"; @@ -42,19 +45,18 @@ public class CassandraContainer> extends G */ @Deprecated public CassandraContainer() { - this("cassandra:3.11.2"); + this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); } - /** - * @deprecated use {@link #CassandraContainer(DockerImageName)} instead - */ - @Deprecated public CassandraContainer(String dockerImageName) { this(DockerImageName.parse(dockerImageName)); } public CassandraContainer(DockerImageName dockerImageName) { super(dockerImageName); + + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + addExposedPort(CQL_PORT); setStartupAttempts(3); this.enableJmxReporting = false; diff --git a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java index 6bdcff7b3f2..e26ca8c8416 100644 --- a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java +++ b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java @@ -7,7 +7,12 @@ public class ClickHouseContainer extends JdbcDatabaseContainer { public static final String NAME = "clickhouse"; - public static final String IMAGE = "yandex/clickhouse-server"; + + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("yandex/clickhouse-server"); + + @Deprecated + public static final String IMAGE = DEFAULT_IMAGE_NAME.getUnversionedPart(); + @Deprecated public static final String DEFAULT_TAG = "18.10.3"; @@ -27,13 +32,9 @@ public class ClickHouseContainer extends JdbcDatabaseContainer { */ @Deprecated public ClickHouseContainer() { - super(IMAGE + ":" + DEFAULT_TAG); + this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); } - /** - * @deprecated use {@link ClickHouseContainer(DockerImageName)} instead - */ - @Deprecated public ClickHouseContainer(String dockerImageName) { this(DockerImageName.parse(dockerImageName)); } @@ -41,6 +42,8 @@ public ClickHouseContainer(String dockerImageName) { public ClickHouseContainer(final DockerImageName dockerImageName) { super(dockerImageName); + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + withExposedPorts(HTTP_PORT, NATIVE_PORT); waitingFor( new HttpWaitStrategy() diff --git a/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java b/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java index 54420155f05..2a7990413e4 100644 --- a/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java +++ b/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java @@ -6,9 +6,18 @@ import java.time.Duration; public class CockroachContainer extends JdbcDatabaseContainer { + + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("cockroachdb/cockroach"); + private static final String DEFAULT_TAG = "v19.1.1"; + public static final String NAME = "cockroach"; - public static final String IMAGE = "cockroachdb/cockroach"; - public static final String IMAGE_TAG = "v19.1.1"; + + @Deprecated + public static final String IMAGE = DEFAULT_IMAGE_NAME.getUnversionedPart(); + + @Deprecated + public static final String IMAGE_TAG = DEFAULT_TAG; + private static final String JDBC_DRIVER_CLASS_NAME = "org.postgresql.Driver"; private static final String JDBC_URL_PREFIX = "jdbc:postgresql"; private static final String TEST_QUERY_STRING = "SELECT 1"; @@ -24,13 +33,9 @@ public class CockroachContainer extends JdbcDatabaseContainer { private static final int KV_SSL_PORT = 11207; - private static final String DOCKER_IMAGE_NAME = "couchbase/server"; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("couchbase/server"); - private static final String VERSION = "6.5.1"; + private static final String DEFAULT_TAG = "6.5.1"; private static final ObjectMapper MAPPER = new ObjectMapper(); @@ -92,26 +92,26 @@ public class CouchbaseContainer extends GenericContainer { */ @Deprecated public CouchbaseContainer() { - this(DOCKER_IMAGE_NAME + ":" + VERSION); + this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); } /** - * Creates a new couchbase container with a custom image name. + * Creates a new couchbase container with the specified image name. * * @param dockerImageName the image name that should be used. - * @deprecated use {@link CouchbaseContainer(DockerImageName)} instead */ - @Deprecated public CouchbaseContainer(final String dockerImageName) { this(DockerImageName.parse(dockerImageName)); } /** * Create a new couchbase container with the specified image name. - * @param dockerImageName + * @param dockerImageName the image name that should be used. */ public CouchbaseContainer(final DockerImageName dockerImageName) { super(dockerImageName); + + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); } /** diff --git a/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java b/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java index aa6d136aaec..c1a252936ea 100644 --- a/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java +++ b/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java @@ -12,7 +12,13 @@ public class Db2Container extends JdbcDatabaseContainer { public static final String NAME = "db2"; - public static final String DEFAULT_DB2_IMAGE_NAME = "ibmcom/db2"; + + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("ibmcom/db2"); + + @Deprecated + public static final String DEFAULT_DB2_IMAGE_NAME = DEFAULT_IMAGE_NAME.getUnversionedPart(); + + @Deprecated public static final String DEFAULT_TAG = "11.5.0.0a"; public static final int DB2_PORT = 50000; @@ -25,19 +31,18 @@ public class Db2Container extends JdbcDatabaseContainer { */ @Deprecated public Db2Container() { - this(DEFAULT_DB2_IMAGE_NAME + ":" + DEFAULT_TAG); + this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); } - /** - * @deprecated use {@link Db2Container(DockerImageName)} instead - */ - @Deprecated public Db2Container(String dockerImageName) { this(DockerImageName.parse(dockerImageName)); } public Db2Container(final DockerImageName dockerImageName) { super(dockerImageName); + + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + withPrivilegedMode(true); this.waitStrategy = new LogMessageWaitStrategy() .withRegEx(".*Setup has completed\\..*") diff --git a/modules/dynalite/src/main/java/org/testcontainers/dynamodb/DynaliteContainer.java b/modules/dynalite/src/main/java/org/testcontainers/dynamodb/DynaliteContainer.java index 6063c1a5dbe..8900964a8bb 100644 --- a/modules/dynalite/src/main/java/org/testcontainers/dynamodb/DynaliteContainer.java +++ b/modules/dynalite/src/main/java/org/testcontainers/dynamodb/DynaliteContainer.java @@ -14,7 +14,8 @@ */ public class DynaliteContainer extends GenericContainer { - private static final String IMAGE_NAME = "quay.io/testcontainers/dynalite:v1.2.1-1"; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("quay.io/testcontainers/dynalite"); + private static final String DEFAULT_TAG = "v1.2.1-1"; private static final int MAPPED_PORT = 4567; /** @@ -22,20 +23,19 @@ public class DynaliteContainer extends GenericContainer { */ @Deprecated public DynaliteContainer() { - this(IMAGE_NAME); - withExposedPorts(MAPPED_PORT); + this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); } - /** - * @deprecated use {@link DynaliteContainer(DockerImageName)} instead - */ - @Deprecated public DynaliteContainer(String dockerImageName) { this(DockerImageName.parse(dockerImageName)); } public DynaliteContainer(final DockerImageName dockerImageName) { super(dockerImageName); + + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + + withExposedPorts(MAPPED_PORT); } diff --git a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java index 6ad0172269d..7ed660d38c6 100644 --- a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java +++ b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java @@ -1,16 +1,15 @@ package org.testcontainers.elasticsearch; +import static java.net.HttpURLConnection.HTTP_OK; +import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED; + +import java.net.InetSocketAddress; +import java.time.Duration; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.utility.Base58; import org.testcontainers.utility.DockerImageName; -import java.net.InetSocketAddress; -import java.time.Duration; - -import static java.net.HttpURLConnection.HTTP_OK; -import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED; - /** * Represents an elasticsearch docker instance which exposes by default port 9200 and 9300 (transport.tcp.port) * The docker image is by default fetched from docker.elastic.co/elasticsearch/elasticsearch @@ -28,35 +27,41 @@ public class ElasticsearchContainer extends GenericContainer> extends GenericContainer { - public static final String VERSION = "1.4.3"; public static final Integer INFLUXDB_PORT = 8086; - private static final String IMAGE_NAME = "influxdb"; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("influxdb"); + private static final String DEFAULT_TAG = "1.4.3"; + + @Deprecated + public static final String VERSION = DEFAULT_TAG; private boolean authEnabled = true; private String admin = "admin"; @@ -32,7 +35,7 @@ public class InfluxDBContainer> extends Gen */ @Deprecated public InfluxDBContainer() { - this(VERSION); + this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); } /** @@ -40,11 +43,14 @@ public InfluxDBContainer() { */ @Deprecated public InfluxDBContainer(final String version) { - this(DockerImageName.parse(IMAGE_NAME + ":" + version)); + this(DEFAULT_IMAGE_NAME.withTag(version)); } public InfluxDBContainer(final DockerImageName dockerImageName) { super(dockerImageName); + + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + waitStrategy = new WaitAllStrategy() .withStrategy(Wait.forHttp("/ping").withBasicCredentials(username, password).forStatusCode(204)) .withStrategy(Wait.forListeningPort()); diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index 4d1c4ece67b..26d699a1813 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -42,7 +42,6 @@ public abstract class JdbcDatabaseContainer { + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("confluentinc/cp-kafka"); + private static final String DEFAULT_TAG = "5.2.1"; + private static final String STARTER_SCRIPT = "/testcontainers_start.sh"; public static final int KAFKA_PORT = 9093; @@ -35,7 +38,7 @@ public class KafkaContainer extends GenericContainer { */ @Deprecated public KafkaContainer() { - this("5.2.1"); + this(TestcontainersConfiguration.getInstance().getKafkaDockerImageName().withTag(DEFAULT_TAG)); } /** @@ -43,12 +46,14 @@ public KafkaContainer() { */ @Deprecated public KafkaContainer(String confluentPlatformVersion) { - this(DockerImageName.parse(TestcontainersConfiguration.getInstance().getKafkaImage() + ":" + confluentPlatformVersion)); + this(TestcontainersConfiguration.getInstance().getKafkaDockerImageName().withTag(confluentPlatformVersion)); } public KafkaContainer(final DockerImageName dockerImageName) { super(dockerImageName); + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + withExposedPorts(KAFKA_PORT); // Use two listeners with different names, it will force Kafka to communicate with itself via internal diff --git a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java index 71702ce8e62..6a669b2f60d 100644 --- a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java +++ b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java @@ -1,6 +1,13 @@ package org.testcontainers.containers; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + import com.google.common.collect.ImmutableMap; +import java.time.Duration; +import java.util.UUID; +import java.util.concurrent.TimeUnit; import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; @@ -10,27 +17,19 @@ import org.apache.kafka.clients.producer.ProducerRecord; import org.apache.kafka.common.serialization.StringDeserializer; import org.apache.kafka.common.serialization.StringSerializer; -import org.junit.Rule; +import org.junit.ClassRule; import org.junit.Test; import org.rnorth.ducttape.unreliables.Unreliables; import org.testcontainers.utility.DockerImageName; -import java.time.Duration; -import java.util.Arrays; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.tuple; - public class KafkaContainerTest { private static final DockerImageName KAFKA_TEST_IMAGE = DockerImageName.parse("confluentinc/cp-kafka:5.2.1"); private static final DockerImageName ZOOKEEPER_TEST_IMAGE = DockerImageName.parse("confluentinc/cp-zookeeper:4.0.0"); // junitRule { - @Rule - public KafkaContainer kafka = new KafkaContainer(); + @ClassRule + public static KafkaContainer kafka = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:5.2.1")); // } @Test @@ -43,10 +42,10 @@ public void testUsage() throws Exception { @Test - public void testUsageWithVersion() throws Exception { + public void testUsageWithSpecificImage() throws Exception { try ( // constructorWithVersion { - KafkaContainer kafka = new KafkaContainer(KAFKA_TEST_IMAGE) + KafkaContainer kafka = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:5.2.1")) // } ) { kafka.start(); @@ -58,6 +57,17 @@ public void testUsageWithVersion() throws Exception { } } + + @Test + public void testUsageWithVersion() throws Exception { + try ( + KafkaContainer kafka = new KafkaContainer("5.2.1") + ) { + kafka.start(); + testKafkaFunctionality(kafka.getBootstrapServers()); + } + } + @Test public void testExternalZookeeperWithExternalNetwork() throws Exception { try ( @@ -75,7 +85,8 @@ public void testExternalZookeeperWithExternalNetwork() throws Exception { .withEnv("ZOOKEEPER_CLIENT_PORT", "2181"); // withKafkaNetwork { - GenericContainer application = new GenericContainer("alpine").withNetwork(network) + GenericContainer application = new GenericContainer<>(DockerImageName.parse("alpine")) + .withNetwork(network) // } .withNetworkAliases("dummy") .withCommand("sleep 10000") @@ -109,8 +120,8 @@ protected void testKafkaFunctionality(String bootstrapServers) throws Exception new StringDeserializer() ); ) { - String topicName = "messages"; - consumer.subscribe(Arrays.asList(topicName)); + String topicName = "messages-" + UUID.randomUUID(); + consumer.subscribe(singletonList(topicName)); producer.send(new ProducerRecord<>(topicName, "testcontainers", "rulezzz")).get(); diff --git a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java index a9547eec94d..7532ebe0b3d 100644 --- a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java +++ b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java @@ -4,14 +4,6 @@ import com.amazonaws.auth.AWSStaticCredentialsProvider; import com.amazonaws.auth.BasicAWSCredentials; import com.amazonaws.client.builder.AwsClientBuilder; -import java.net.InetAddress; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.experimental.FieldDefaults; @@ -24,6 +16,15 @@ import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.TestcontainersConfiguration; +import java.net.InetAddress; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + /** *

Container for Atlassian Labs Localstack, 'A fully functional local AWS cloud stack'.

*

{@link LocalStackContainer#withServices(Service...)} should be used to select which services @@ -35,11 +36,16 @@ @Slf4j public class LocalStackContainer extends GenericContainer { - public static final String VERSION = "0.11.2"; static final int PORT = 4566; private static final String HOSTNAME_EXTERNAL_ENV_VAR = "HOSTNAME_EXTERNAL"; private final List services = new ArrayList<>(); + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("localstack/localstack"); + private static final String DEFAULT_TAG = "0.11.2"; + + @Deprecated + public static final String VERSION = DEFAULT_TAG; + /** * Whether or to assume that all APIs run on different ports (when true) or are * exposed on a single port (false). From the Localstack README: @@ -59,7 +65,7 @@ public class LocalStackContainer extends GenericContainer { */ @Deprecated public LocalStackContainer() { - this(VERSION); + this(TestcontainersConfiguration.getInstance().getLocalstackDockerImageName().withTag(DEFAULT_TAG)); } /** @@ -67,7 +73,7 @@ public LocalStackContainer() { */ @Deprecated public LocalStackContainer(String version) { - this(DockerImageName.parse(TestcontainersConfiguration.getInstance().getLocalStackImage() + ":" + version)); + this(TestcontainersConfiguration.getInstance().getLocalstackDockerImageName().withTag(version)); } /** @@ -83,6 +89,9 @@ public LocalStackContainer(final DockerImageName dockerImageName) { */ public LocalStackContainer(final DockerImageName dockerImageName, boolean useLegacyMode) { super(dockerImageName); + + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + this.legacyMode = useLegacyMode; withFileSystemBind(DockerClientFactory.instance().getRemoteDockerUnixSocketPath(), "/var/run/docker.sock"); diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java index e137c186209..b43a5f237e5 100644 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java @@ -9,10 +9,16 @@ */ public class MariaDBContainer> extends JdbcDatabaseContainer { - public static final String NAME = "mariadb"; - public static final String IMAGE = "mariadb"; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("mariadb"); + + @Deprecated public static final String DEFAULT_TAG = "10.3.6"; + public static final String NAME = "mariadb"; + + @Deprecated + public static final String IMAGE = DEFAULT_IMAGE_NAME.getUnversionedPart(); + static final String DEFAULT_USER = "test"; static final String DEFAULT_PASSWORD = "test"; @@ -29,19 +35,18 @@ public class MariaDBContainer> extends JdbcD */ @Deprecated public MariaDBContainer() { - this(IMAGE + ":" + DEFAULT_TAG); + this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); } - /** - * @deprecated use {@link MariaDBContainer(DockerImageName)} instead - */ - @Deprecated public MariaDBContainer(String dockerImageName) { this(DockerImageName.parse(dockerImageName)); } public MariaDBContainer(final DockerImageName dockerImageName) { super(dockerImageName); + + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + addExposedPort(MARIADB_PORT); } diff --git a/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java b/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java index 2aa33931d07..451a4710b20 100644 --- a/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java +++ b/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java @@ -6,7 +6,11 @@ @Slf4j public class MockServerContainer extends GenericContainer { - public static final String VERSION = "5.5.4"; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("jamesdbloom/mockserver"); + private static final String DEFAULT_TAG = "mockserver-5.5.4"; + + @Deprecated + public static final String VERSION = DEFAULT_TAG; public static final int PORT = 1080; @@ -15,7 +19,7 @@ public class MockServerContainer extends GenericContainer { */ @Deprecated public MockServerContainer() { - this(VERSION); + this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); } /** @@ -23,11 +27,14 @@ public MockServerContainer() { */ @Deprecated public MockServerContainer(String version) { - this(DockerImageName.parse("jamesdbloom/mockserver:mockserver-" + version)); + this(DEFAULT_IMAGE_NAME.withTag("mockserver-" + version)); } public MockServerContainer(DockerImageName dockerImageName) { super(dockerImageName); + + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + withCommand("-logLevel INFO -serverPort " + PORT); addExposedPorts(PORT); } diff --git a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java index 1527b3924db..200b3aa0820 100644 --- a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java @@ -16,10 +16,12 @@ */ @Slf4j public class MongoDBContainer extends GenericContainer { + + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("mongo"); + private static final String DEFAULT_TAG = "4.0.10"; private static final int CONTAINER_EXIT_CODE_OK = 0; private static final int MONGODB_INTERNAL_PORT = 27017; private static final int AWAIT_INIT_REPLICA_SET_ATTEMPTS = 60; - private static final String MONGODB_VERSION_DEFAULT = "4.0.10"; private static final String MONGODB_DATABASE_NAME_DEFAULT = "test"; /** @@ -27,19 +29,18 @@ public class MongoDBContainer extends GenericContainer { */ @Deprecated public MongoDBContainer() { - this("mongo:" + MONGODB_VERSION_DEFAULT); + this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); } - /** - * @deprecated use {@link MongoDBContainer(DockerImageName)} instead - */ - @Deprecated public MongoDBContainer(@NonNull final String dockerImageName) { this(DockerImageName.parse(dockerImageName)); } public MongoDBContainer(final DockerImageName dockerImageName) { super(dockerImageName); + + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + withExposedPorts(MONGODB_INTERNAL_PORT); withCommand("--replSet", "docker-rs"); waitingFor( diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainerProvider.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainerProvider.java index d72ab8e8419..fe7ada669bd 100644 --- a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainerProvider.java +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainerProvider.java @@ -21,6 +21,7 @@ public boolean supports(ConnectionFactoryOptions options) { @Override public R2DBCDatabaseContainer createContainer(ConnectionFactoryOptions options) { + // TODO work out how best to do this if these constants become private String image = MSSQLServerContainer.IMAGE + ":" + options.getRequiredValue(IMAGE_TAG_OPTION); MSSQLServerContainer container = new MSSQLServerContainer<>(image); diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java index 0a6d8149e34..a20c60fd2dc 100644 --- a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java @@ -11,10 +11,14 @@ */ public class MSSQLServerContainer> extends JdbcDatabaseContainer { - public static final String NAME = "sqlserver"; - public static final String IMAGE = "mcr.microsoft.com/mssql/server"; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("mcr.microsoft.com/mssql/server"); + @Deprecated public static final String DEFAULT_TAG = "2017-CU12"; + public static final String NAME = "sqlserver"; + + public static final String IMAGE = DEFAULT_IMAGE_NAME.getUnversionedPart(); + public static final Integer MS_SQL_SERVER_PORT = 1433; static final String DEFAULT_USER = "SA"; @@ -38,19 +42,18 @@ public class MSSQLServerContainer> exten */ @Deprecated public MSSQLServerContainer() { - this(IMAGE + ":" + DEFAULT_TAG); + this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); } - /** - * @deprecated use {@link MSSQLServerContainer(DockerImageName)} instead - */ - @Deprecated public MSSQLServerContainer(final String dockerImageName) { this(DockerImageName.parse(dockerImageName)); } public MSSQLServerContainer(final DockerImageName dockerImageName) { super(dockerImageName); + + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + withStartupTimeoutSeconds(DEFAULT_STARTUP_TIMEOUT_SECONDS); withConnectTimeoutSeconds(DEFAULT_CONNECT_TIMEOUT_SECONDS); addExposedPort(MS_SQL_SERVER_PORT); diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java index 35321db5d72..1dc235e6536 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java @@ -12,9 +12,15 @@ public class MySQLContainer> extends JdbcDatabaseContainer { public static final String NAME = "mysql"; - public static final String IMAGE = "mysql"; + + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("mysql"); + + @Deprecated public static final String DEFAULT_TAG = "5.7.22"; + @Deprecated + public static final String IMAGE = DEFAULT_IMAGE_NAME.getUnversionedPart(); + static final String DEFAULT_USER = "test"; static final String DEFAULT_PASSWORD = "test"; @@ -31,19 +37,18 @@ public class MySQLContainer> extends JdbcDatab */ @Deprecated public MySQLContainer() { - this(IMAGE + ":" + DEFAULT_TAG); + this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); } - /** - * @deprecated use {@link MySQLContainer(DockerImageName)} instead - */ - @Deprecated public MySQLContainer(String dockerImageName) { this(DockerImageName.parse(dockerImageName)); } public MySQLContainer(final DockerImageName dockerImageName) { super(dockerImageName); + + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + addExposedPort(MYSQL_PORT); } diff --git a/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java index 5d4212ccacd..e93a840302a 100644 --- a/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java +++ b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java @@ -1,5 +1,11 @@ package org.testcontainers.containers; +import static java.net.HttpURLConnection.HTTP_OK; +import static java.util.stream.Collectors.toSet; + +import java.time.Duration; +import java.util.Set; +import java.util.stream.Stream; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.containers.wait.strategy.WaitAllStrategy; @@ -8,13 +14,6 @@ import org.testcontainers.utility.LicenseAcceptance; import org.testcontainers.utility.MountableFile; -import java.time.Duration; -import java.util.Set; -import java.util.stream.Stream; - -import static java.net.HttpURLConnection.HTTP_OK; -import static java.util.stream.Collectors.toSet; - /** * Testcontainer for Neo4j. * @@ -26,14 +25,13 @@ public class Neo4jContainer> extends GenericContaine /** * The image defaults to the official Neo4j image: Neo4j. */ - private static final String DEFAULT_IMAGE_NAME = "neo4j"; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("neo4j"); /** * The default tag (version) to use. */ private static final String DEFAULT_TAG = "3.5.0"; - - private static final String DOCKER_IMAGE_NAME = DEFAULT_IMAGE_NAME + ":" + DEFAULT_TAG; + private static final String ENTERPRISE_TAG = DEFAULT_TAG + "-enterprise"; /** * Default port for the binary Bolt protocol. @@ -57,33 +55,41 @@ public class Neo4jContainer> extends GenericContaine private static final String AUTH_FORMAT = "neo4j/%s"; - private String adminPassword = DEFAULT_ADMIN_PASSWORD; + private final boolean standardImage; - private boolean standardImage = false; + private String adminPassword = DEFAULT_ADMIN_PASSWORD; /** - * Creates a Testcontainer using the official Neo4j docker image. + * Creates a Neo4jContainer using the official Neo4j docker image. * @deprecated use {@link Neo4jContainer(DockerImageName)} instead */ @Deprecated public Neo4jContainer() { - this(DOCKER_IMAGE_NAME); + this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); } /** - * Creates a Testcontainer using a specific docker image. + * Creates a Neo4jContainer using a specific docker image. * * @param dockerImageName The docker image to use. - * @deprecated use {@link Neo4jContainer(DockerImageName)} instead */ - @Deprecated public Neo4jContainer(String dockerImageName) { this(DockerImageName.parse(dockerImageName)); } + /** + * Creates a Neo4jContainer using a specific docker image. + * + * @param dockerImageName The docker image to use. + */ public Neo4jContainer(final DockerImageName dockerImageName) { super(dockerImageName); + this.standardImage = dockerImageName.getUnversionedPart() + .equals(DEFAULT_IMAGE_NAME.getUnversionedPart()); + + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + WaitStrategy waitForBolt = new LogMessageWaitStrategy() .withRegEx(String.format(".*Bolt enabled on 0\\.0\\.0\\.0:%d\\.\n", DEFAULT_BOLT_PORT)); WaitStrategy waitForHttp = new HttpWaitStrategy() @@ -96,10 +102,6 @@ public Neo4jContainer(final DockerImageName dockerImageName) { .withStartupTimeout(Duration.ofMinutes(2)); addExposedPorts(DEFAULT_BOLT_PORT, DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT); - - if (dockerImageName.getUnversionedPart().equals(DEFAULT_IMAGE_NAME)) { - this.standardImage = true; - } } @Override @@ -149,13 +151,13 @@ public String getHttpsUrl() { * @return This container. */ public S withEnterpriseEdition() { - if (!standardImage) { throw new IllegalStateException( - String.format("Cannot use enterprise version with alternative image %s.", getDockerImageName())); + String.format("Cannot use enterprise version with alternative image %s.", + getDockerImageName())); } - setDockerImageName(DOCKER_IMAGE_NAME + "-enterprise"); + setDockerImageName(DEFAULT_IMAGE_NAME.withTag(ENTERPRISE_TAG).asCanonicalNameString()); LicenseAcceptance.assertLicenseAccepted(getDockerImageName()); addEnv("NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes"); diff --git a/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java b/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java index 75a4571b68e..296d82b7997 100644 --- a/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java +++ b/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java @@ -15,28 +15,28 @@ public class NginxContainer> extends GenericContainer implements LinkableContainer { private static final int NGINX_DEFAULT_PORT = 80; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("nginx"); + private static final String DEFAULT_TAG = "1.9.4"; /** * @deprecated use {@link NginxContainer(DockerImageName)} instead */ @Deprecated public NginxContainer() { - this("nginx:1.9.4"); + this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); } - /** - * @deprecated use {@link NginxContainer(DockerImageName)} instead - */ - @Deprecated public NginxContainer(String dockerImageName) { this(DockerImageName.parse(dockerImageName)); - - addExposedPort(NGINX_DEFAULT_PORT); - setCommand("nginx", "-g", "daemon off;"); } public NginxContainer(final DockerImageName dockerImageName) { super(dockerImageName); + + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + + addExposedPort(NGINX_DEFAULT_PORT); + setCommand("nginx", "-g", "daemon off;"); } @NotNull diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java index 8406d8275fb..b5700c1e84e 100644 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java @@ -41,10 +41,6 @@ public OracleContainer() { this(resolveImageName()); } - /** - * @deprecated use {@link OracleContainer(DockerImageName)} instead - */ - @Deprecated public OracleContainer(String dockerImageName) { this(DockerImageName.parse(dockerImageName)); } diff --git a/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java b/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java index 797f73f7159..f6ce060b6ec 100644 --- a/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java +++ b/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java @@ -29,9 +29,8 @@ public class OrientDBContainer extends GenericContainer { private static final Logger LOGGER = LoggerFactory.getLogger(OrientDBContainer.class); - private static final String DEFAULT_IMAGE_NAME = "orientdb"; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("orientdb"); private static final String DEFAULT_TAG = "3.0.24-tp3"; - private static final String DOCKER_IMAGE_NAME = DEFAULT_IMAGE_NAME + ":" + DEFAULT_TAG; private static final String DEFAULT_USERNAME = "admin"; private static final String DEFAULT_PASSWORD = "admin"; @@ -54,13 +53,9 @@ public class OrientDBContainer extends GenericContainer { */ @Deprecated public OrientDBContainer() { - this(DOCKER_IMAGE_NAME); + this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); } - /** - * @deprecated use {@link OrientDBContainer(DockerImageName)} instead - */ - @Deprecated public OrientDBContainer(@NonNull String dockerImageName) { this(DockerImageName.parse(dockerImageName)); } @@ -68,6 +63,8 @@ public OrientDBContainer(@NonNull String dockerImageName) { public OrientDBContainer(final DockerImageName dockerImageName) { super(dockerImageName); + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + serverPassword = DEFAULT_SERVER_PASSWORD; databaseName = DEFAULT_DATABASE_NAME; diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java index 9e59787837b..76c213f0931 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java @@ -9,8 +9,8 @@ public class PostgisContainerProvider extends JdbcDatabaseContainerProvider { private static final String NAME = "postgis"; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("postgis/postgis").asCompatibleSubstituteFor("postgres"); private static final String DEFAULT_TAG = "12-3.0"; - private static final String DEFAULT_IMAGE = "postgis/postgis"; public static final String USER_PARAM = "user"; public static final String PASSWORD_PARAM = "password"; @@ -27,7 +27,7 @@ public JdbcDatabaseContainer newInstance() { @Override public JdbcDatabaseContainer newInstance(String tag) { - return new PostgreSQLContainer(DockerImageName.parse(DEFAULT_IMAGE).withTag(DEFAULT_TAG)); + return new PostgreSQLContainer<>(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); } @Override diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index b1ddb2a26ee..5a4abe6d0ee 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -16,7 +16,9 @@ public class PostgreSQLContainer> extends JdbcDatabaseContainer { public static final String NAME = "postgresql"; public static final String IMAGE = "postgres"; + public static final String DEFAULT_TAG = "9.6.12"; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("postgres"); public static final Integer POSTGRESQL_PORT = 5432; @@ -37,19 +39,18 @@ public class PostgreSQLContainer> extends */ @Deprecated public PostgreSQLContainer() { - this(IMAGE + ":" + DEFAULT_TAG); + this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); } - /** - * @deprecated use {@link PostgreSQLContainer(DockerImageName)} instead - */ - @Deprecated public PostgreSQLContainer(final String dockerImageName) { this(DockerImageName.parse(dockerImageName)); } public PostgreSQLContainer(final DockerImageName dockerImageName) { super(dockerImageName); + + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + this.waitStrategy = new LogMessageWaitStrategy() .withRegEx(".*database system is ready to accept connections.*\\s") .withTimes(2) diff --git a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java index 1c8c03ce4de..e4934693d14 100644 --- a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java +++ b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java @@ -16,6 +16,7 @@ public class PrestoContainer> extends JdbcDatabaseContainer { public static final String NAME = "presto"; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("prestosql/presto"); public static final String IMAGE = "prestosql/presto"; public static final String DEFAULT_TAG = "329"; @@ -29,19 +30,18 @@ public class PrestoContainer> extends JdbcDat */ @Deprecated public PrestoContainer() { - this(IMAGE + ":" + DEFAULT_TAG); + this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); } - /** - * @deprecated use {@link PrestoContainer(DockerImageName)} instead - */ - @Deprecated public PrestoContainer(final String dockerImageName) { this(DockerImageName.parse(dockerImageName)); } public PrestoContainer(final DockerImageName dockerImageName) { super(dockerImageName); + + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + this.waitStrategy = new LogMessageWaitStrategy() .withRegEx(".*======== SERVER STARTED ========.*") .withStartupTimeout(Duration.of(60, SECONDS)); diff --git a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java index 4d30d947113..bceff8927d7 100644 --- a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java +++ b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java @@ -14,8 +14,9 @@ public class PulsarContainer extends GenericContainer { public static final int BROKER_HTTP_PORT = 8080; public static final String METRICS_ENDPOINT = "/metrics"; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("apachepulsar/pulsar"); @Deprecated - private static final String DEFAULT_PULSAR_VERSION = "2.2.0"; + private static final String DEFAULT_TAG = "2.2.0"; private boolean functionsWorkerEnabled = false; @@ -24,7 +25,7 @@ public class PulsarContainer extends GenericContainer { */ @Deprecated public PulsarContainer() { - this(DEFAULT_PULSAR_VERSION); + this(TestcontainersConfiguration.getInstance().getPulsarDockerImageName().withTag(DEFAULT_TAG)); } /** @@ -37,6 +38,9 @@ public PulsarContainer(String pulsarVersion) { public PulsarContainer(final DockerImageName dockerImageName) { super(dockerImageName); + + dockerImageName.assertCompatibleWith(DockerImageName.parse("apachepulsar/pulsar")); + withExposedPorts(BROKER_PORT, BROKER_HTTP_PORT); withCommand("/pulsar/bin/pulsar", "standalone", "--no-functions-worker", "-nss"); waitingFor(Wait.forHttp(METRICS_ENDPOINT).forStatusCode(200).forPort(BROKER_HTTP_PORT)); diff --git a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java index a3b6134b29c..edbffcf3174 100644 --- a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java +++ b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java @@ -28,7 +28,7 @@ public class RabbitMQContainer extends GenericContainer { /** * The image defaults to the official RabbitmQ image: RabbitMQ. */ - private static final String DEFAULT_IMAGE_NAME = "rabbitmq"; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("rabbitmq"); private static final String DEFAULT_TAG = "3.7.25-management-alpine"; private static final int DEFAULT_AMQP_PORT = 5672; @@ -41,21 +41,19 @@ public class RabbitMQContainer extends GenericContainer { private final List> values = new ArrayList<>(); /** - * Creates a Testcontainer using the official RabbitMQ docker image. + * Creates a RabbitMQ container using the official RabbitMQ docker image. * @deprecated use {@link RabbitMQContainer(DockerImageName)} instead */ @Deprecated public RabbitMQContainer() { - this(DEFAULT_IMAGE_NAME + ":" + DEFAULT_TAG); + this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); } /** - * Creates a Testcontainer using a specific docker image. + * Creates a RabbitMQ container using a specific docker image. * * @param dockerImageName The docker image to use. - * @deprecated use {@link RabbitMQContainer(DockerImageName)} instead */ - @Deprecated public RabbitMQContainer(String dockerImageName) { this(DockerImageName.parse(dockerImageName)); } @@ -63,6 +61,8 @@ public RabbitMQContainer(String dockerImageName) { public RabbitMQContainer(final DockerImageName dockerImageName) { super(dockerImageName); + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + addExposedPorts(DEFAULT_AMQP_PORT, DEFAULT_AMQPS_PORT, DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT); this.waitStrategy = Wait. diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index f1cedab76ad..d60d9191748 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -1,10 +1,21 @@ package org.testcontainers.containers; +import static java.time.temporal.ChronoUnit.SECONDS; + import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.model.AccessMode; import com.github.dockerjava.api.model.Bind; import com.github.dockerjava.api.model.Volume; import com.google.common.collect.ImmutableSet; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Files; +import java.time.Duration; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.TimeUnit; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.openqa.selenium.Capabilities; @@ -25,18 +36,6 @@ import org.testcontainers.lifecycle.TestLifecycleAware; import org.testcontainers.utility.DockerImageName; -import java.io.File; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.file.Files; -import java.time.Duration; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -import static java.time.temporal.ChronoUnit.SECONDS; - /** * A chrome/firefox/custom container based on SeleniumHQ's standalone container sets. *

@@ -44,8 +43,14 @@ */ public class BrowserWebDriverContainer> extends GenericContainer implements LinkableContainer, TestLifecycleAware { - private static final String CHROME_IMAGE = "selenium/standalone-chrome-debug:%s"; - private static final String FIREFOX_IMAGE = "selenium/standalone-firefox-debug:%s"; + private static final DockerImageName CHROME_IMAGE = DockerImageName.parse("selenium/standalone-chrome-debug"); + private static final DockerImageName FIREFOX_IMAGE = DockerImageName.parse("selenium/standalone-firefox-debug"); + private static final DockerImageName[] COMPATIBLE_IMAGES = new DockerImageName[] { + CHROME_IMAGE, + FIREFOX_IMAGE, + DockerImageName.parse("selenium/standalone-chrome"), + DockerImageName.parse("selenium/standalone-firefox") + }; private static final String DEFAULT_PASSWORD = "secret"; private static final int SELENIUM_PORT = 4444; @@ -56,7 +61,7 @@ public class BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>(imageName) .withCapabilities(new ChromeOptions())) { chrome.start(); diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java index bbc3b4db7a0..9a15c6b466d 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java @@ -8,7 +8,8 @@ public class SpecificImageNameWebDriverContainerTest extends BaseWebDriverContainerTest { - private static final DockerImageName FIREFOX_IMAGE = DockerImageName.parse("selenium/standalone-firefox:2.53.1-beryllium"); + private static final DockerImageName FIREFOX_IMAGE = DockerImageName + .parse("selenium/standalone-firefox:2.53.1-beryllium"); @Rule public BrowserWebDriverContainer firefox = new BrowserWebDriverContainer<>(FIREFOX_IMAGE) diff --git a/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java b/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java index aeadb664089..4bc2f19ec86 100644 --- a/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java +++ b/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java @@ -18,7 +18,12 @@ */ public class SolrContainer extends GenericContainer { - public static final String IMAGE = "solr"; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("solr"); + + @Deprecated + public static final String IMAGE = DEFAULT_IMAGE_NAME.getUnversionedPart(); + + @Deprecated public static final String DEFAULT_TAG = "8.3.0"; public static final Integer ZOOKEEPER_PORT = 9983; @@ -31,19 +36,21 @@ public class SolrContainer extends GenericContainer { */ @Deprecated public SolrContainer() { - this(IMAGE + ":" + DEFAULT_TAG); + this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); } /** * @deprecated use {@link SolrContainer(DockerImageName)} instead */ - @Deprecated public SolrContainer(final String dockerImageName) { this(DockerImageName.parse(dockerImageName)); } public SolrContainer(final DockerImageName dockerImageName) { super(dockerImageName); + + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + this.waitStrategy = new LogMessageWaitStrategy() .withRegEx(".*o\\.e\\.j\\.s\\.Server Started.*") .withStartupTimeout(Duration.of(60, SECONDS)); diff --git a/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java b/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java index b01d031cc5b..e6c143fa358 100644 --- a/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java +++ b/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java @@ -21,7 +21,8 @@ */ public class ToxiproxyContainer extends GenericContainer { - private static final String IMAGE_NAME = "shopify/toxiproxy:2.1.0"; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("shopify/toxiproxy"); + private static final String DEFAULT_TAG = "2.1.0"; private static final int TOXIPROXY_CONTROL_PORT = 8474; private static final int FIRST_PROXIED_PORT = 8666; private static final int LAST_PROXIED_PORT = 8666 + 31; @@ -35,19 +36,18 @@ public class ToxiproxyContainer extends GenericContainer { */ @Deprecated public ToxiproxyContainer() { - this(IMAGE_NAME); + this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); } - /** - * @deprecated use {@link ToxiproxyContainer(DockerImageName)} instead - */ - @Deprecated public ToxiproxyContainer(String dockerImageName) { this(DockerImageName.parse(dockerImageName)); } public ToxiproxyContainer(final DockerImageName dockerImageName) { super(dockerImageName); + + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + addExposedPorts(TOXIPROXY_CONTROL_PORT); setWaitStrategy(new HttpWaitStrategy().forPath("/version").forPort(TOXIPROXY_CONTROL_PORT)); diff --git a/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java b/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java index 1532f463f37..b0c8a2a40e3 100644 --- a/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java +++ b/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java @@ -23,6 +23,9 @@ */ public class VaultContainer> extends GenericContainer { + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("vault"); + private static final String DEFAULT_TAG = "1.1.3"; + private static final int VAULT_PORT = 8200; private Map> secretsMap = new HashMap<>(); @@ -34,13 +37,9 @@ public class VaultContainer> extends GenericCo */ @Deprecated public VaultContainer() { - this("vault:1.1.3"); + this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); } - /** - * @deprecated use {@link VaultContainer(DockerImageName)} instead - */ - @Deprecated public VaultContainer(String dockerImageName) { this(DockerImageName.parse(dockerImageName)); } @@ -48,6 +47,8 @@ public VaultContainer(String dockerImageName) { public VaultContainer(final DockerImageName dockerImageName) { super(dockerImageName); + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + // Use the vault healthcheck endpoint to check for readiness, per https://www.vaultproject.io/api/system/health.html setWaitStrategy(Wait.forHttp("/v1/sys/health").forStatusCode(200)); From 2cd32fd637d1846fba187cc11fd1ad6ef69c44dc Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Thu, 1 Oct 2020 10:53:41 +0200 Subject: [PATCH 1644/5615] Add support for MongoDB 4.4 (#3083) Make LogMessageWaitStrategy in MongoDBContainer compatible Fixes #3077 --- .../org/testcontainers/containers/MongoDBContainer.java | 2 +- .../testcontainers/containers/MongoDBContainerTest.java | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java index 200b3aa0820..5fd2451480b 100644 --- a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java @@ -44,7 +44,7 @@ public MongoDBContainer(final DockerImageName dockerImageName) { withExposedPorts(MONGODB_INTERNAL_PORT); withCommand("--replSet", "docker-rs"); waitingFor( - Wait.forLogMessage(".*waiting for connections on port.*", 1) + Wait.forLogMessage("(?i).*waiting for connections.*", 1) ); } diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java index d637cb07f2d..b18e7f15a01 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java @@ -73,4 +73,13 @@ public void shouldExecuteTransactions() { } } } + + @Test + public void supportsMongoDB_4_4() { + try ( + final MongoDBContainer mongoDBContainer = new MongoDBContainer(DockerImageName.parse("mongo:4.4")) + ) { + mongoDBContainer.start(); + } + } } From 17b4f6c136f6f2c7dc223bad407221f62a8f0088 Mon Sep 17 00:00:00 2001 From: Rishu Mehrotra Date: Sat, 3 Oct 2020 16:00:03 +0530 Subject: [PATCH 1645/5615] Don't display a stack trace in the logs when .testcontainers.properties is not found (#2293) Co-authored-by: Richard North --- .../testcontainers/utility/TestcontainersConfiguration.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 521a7519c2a..5b89140fd61 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -9,6 +9,7 @@ import lombok.SneakyThrows; import lombok.Synchronized; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.exception.ExceptionUtils; import org.testcontainers.UnstableAPI; import java.io.File; @@ -232,9 +233,9 @@ private static Properties readProperties(URL url) { try (InputStream inputStream = url.openStream()) { properties.load(inputStream); } catch (FileNotFoundException e) { - log.trace("Testcontainers config override was found on {} but the file was not found", url, e); + log.warn("Testcontainers config override was found on {} but the file was not found. Exception message: {}", url, ExceptionUtils.getRootCauseMessage(e)); } catch (IOException e) { - log.warn("Testcontainers config override was found on {} but could not be loaded", url, e); + log.warn("Testcontainers config override was found on {} but could not be loaded. Exception message: {}", url, ExceptionUtils.getRootCauseMessage(e)); } return properties; } From ae46d1049230dbc8797b077e4d475a11bfeab6ac Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 7 Oct 2020 12:58:47 +0200 Subject: [PATCH 1646/5615] Support Confluent Platform 6 in Kafka (#3293) * Support Confluent Platform 6 in Kafka * remove `KafkaContainer#startZookeeper` --- .../containers/KafkaContainer.java | 26 +++++-------------- .../containers/KafkaContainerTest.java | 10 +++++++ 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java index 4086cd4e2e4..ae44e4803a9 100644 --- a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java +++ b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java @@ -1,6 +1,5 @@ package org.testcontainers.containers; -import com.github.dockerjava.api.command.ExecCreateCmdResponse; import com.github.dockerjava.api.command.InspectContainerResponse; import lombok.SneakyThrows; import org.testcontainers.images.builder.Transferable; @@ -8,7 +7,6 @@ import org.testcontainers.utility.TestcontainersConfiguration; import java.nio.charset.StandardCharsets; -import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -108,14 +106,18 @@ protected void containerIsStarting(InspectContainerResponse containerInfo, boole return; } + String command = "#!/bin/bash\n"; final String zookeeperConnect; if (externalZookeeperConnect != null) { zookeeperConnect = externalZookeeperConnect; } else { - zookeeperConnect = startZookeeper(); + zookeeperConnect = "localhost:" + ZOOKEEPER_PORT; + command += "echo 'clientPort=" + ZOOKEEPER_PORT + "' > zookeeper.properties\n"; + command += "echo 'dataDir=/var/lib/zookeeper/data' >> zookeeper.properties\n"; + command += "echo 'dataLogDir=/var/lib/zookeeper/log' >> zookeeper.properties\n"; + command += "zookeeper-server-start zookeeper.properties &\n"; } - String command = "#!/bin/bash \n"; command += "export KAFKA_ZOOKEEPER_CONNECT='" + zookeeperConnect + "'\n"; command += "export KAFKA_ADVERTISED_LISTENERS='" + Stream .concat( @@ -130,22 +132,8 @@ protected void containerIsStarting(InspectContainerResponse containerInfo, boole command += "/etc/confluent/docker/launch \n"; copyFileToContainer( - Transferable.of(command.getBytes(StandardCharsets.UTF_8), 700), + Transferable.of(command.getBytes(StandardCharsets.UTF_8), 0777), STARTER_SCRIPT ); } - - @SneakyThrows(InterruptedException.class) - private String startZookeeper() { - ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(getContainerId()) - .withCmd("sh", "-c", "" + - "printf 'clientPort=" + ZOOKEEPER_PORT + "\ndataDir=/var/lib/zookeeper/data\ndataLogDir=/var/lib/zookeeper/log' > /zookeeper.properties\n" + - "zookeeper-server-start /zookeeper.properties\n" - ) - .exec(); - - dockerClient.execStartCmd(execCreateCmdResponse.getId()).start().awaitStarted(10, TimeUnit.SECONDS); - - return "localhost:" + ZOOKEEPER_PORT; - } } diff --git a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java index 6a669b2f60d..08b9fb6f856 100644 --- a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java +++ b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java @@ -99,6 +99,16 @@ public void testExternalZookeeperWithExternalNetwork() throws Exception { } } + @Test + public void testConfluentPlatformVersion6() throws Exception { + try ( + KafkaContainer kafka = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:6.0.0")) + ) { + kafka.start(); + testKafkaFunctionality(kafka.getBootstrapServers()); + } + } + protected void testKafkaFunctionality(String bootstrapServers) throws Exception { try ( KafkaProducer producer = new KafkaProducer<>( From 3f1cddbb33a2f75f8299a18bc794446768629f29 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 11 Oct 2020 07:05:52 +0100 Subject: [PATCH 1647/5615] When an image version is not specified, use `latest` as the default tag (#3313) --- .../testcontainers/utility/Versioning.java | 4 +-- .../dockerclient/AmbiguousImagePullTest.java | 34 +++++++++++++++++++ .../utility/DockerImageNameTest.java | 4 +-- 3 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 core/src/test/java/org/testcontainers/dockerclient/AmbiguousImagePullTest.java diff --git a/core/src/main/java/org/testcontainers/utility/Versioning.java b/core/src/main/java/org/testcontainers/utility/Versioning.java index 8944b3f0ba6..84dcf46274e 100644 --- a/core/src/main/java/org/testcontainers/utility/Versioning.java +++ b/core/src/main/java/org/testcontainers/utility/Versioning.java @@ -24,12 +24,12 @@ public boolean isValid() { @Override public String getSeparator() { - return ""; + return ":"; } @Override public String toString() { - return ""; + return "latest"; } @Override diff --git a/core/src/test/java/org/testcontainers/dockerclient/AmbiguousImagePullTest.java b/core/src/test/java/org/testcontainers/dockerclient/AmbiguousImagePullTest.java new file mode 100644 index 00000000000..5d2c42ed1b2 --- /dev/null +++ b/core/src/test/java/org/testcontainers/dockerclient/AmbiguousImagePullTest.java @@ -0,0 +1,34 @@ +package org.testcontainers.dockerclient; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.model.Image; +import org.junit.Test; +import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; +import org.testcontainers.utility.DockerImageName; + +import java.util.List; + +public class AmbiguousImagePullTest { + + @Test(timeout = 30_000) + public void testNotUsingParse() { + DockerClient client = DockerClientFactory.instance().client(); + List alpineImages = client.listImagesCmd() + .withImageNameFilter("alpine:latest") + .exec(); + for (Image alpineImage : alpineImages) { + client.removeImageCmd(alpineImage.getId()).exec(); + } + + try ( + final GenericContainer container = new GenericContainer<>(DockerImageName.parse("alpine")) + .withCommand("/bin/sh", "-c", "sleep 0") + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + ) { + container.start(); + // do nothing other than start and stop + } + } +} diff --git a/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java b/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java index 34575f2b4b9..e06407fbf56 100644 --- a/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java +++ b/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java @@ -112,7 +112,7 @@ public void testParsing() { canonicalName = unversionedPart + versionSeparator + version; } else { combined = unversionedPart; - canonicalName = unversionedPart; + canonicalName = unversionedPart + ":latest"; } VisibleAssertions.context("For " + combined); @@ -124,7 +124,7 @@ public void testParsing() { if (version != null) { assertEquals(combined + " has version part: " + version, version, imageName.getVersionPart()); } else { - assertEquals(combined + " has no version specified", "", imageName.getVersionPart()); + assertEquals(combined + " has automatic 'latest' version specified", "latest", imageName.getVersionPart()); } assertEquals(combined + " has canonical name: " + canonicalName, canonicalName, imageName.asCanonicalNameString()); From bc7aaafa75265049c700583b5230dd639b45a30e Mon Sep 17 00:00:00 2001 From: Cristian Greco Date: Sun, 11 Oct 2020 19:19:13 +0100 Subject: [PATCH 1648/5615] Add workflow for Update Gradle Wrapper Action. (#3297) Co-authored-by: Richard North --- .github/workflows/update-gradle-wrapper.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/workflows/update-gradle-wrapper.yml diff --git a/.github/workflows/update-gradle-wrapper.yml b/.github/workflows/update-gradle-wrapper.yml new file mode 100644 index 00000000000..f3a01949324 --- /dev/null +++ b/.github/workflows/update-gradle-wrapper.yml @@ -0,0 +1,20 @@ +name: Update Gradle Wrapper + +on: + schedule: + - cron: "0 0 * * *" + +jobs: + update-gradle-wrapper: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Update Gradle Wrapper + uses: gradle-update/update-gradle-wrapper-action@74a035c + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + labels: dependencies + + - uses: gradle/wrapper-validation-action@e2c57ac From edd2a1c44d3e7f9bd9d4a21dd885da394023a0f5 Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 12 Oct 2020 17:06:28 +0100 Subject: [PATCH 1649/5615] Always continue on error for examples CI (#3339) --- .github/workflows/ci-examples.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index d81a4ffb509..6f8e4868a97 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -20,6 +20,7 @@ jobs: java-version: '1.8' - name: Cache Gradle Home files uses: actions/cache@v1 + continue-on-error: true with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-home-testmatrix-examples-${{ hashFiles('**/*.gradle') }} @@ -48,6 +49,7 @@ jobs: java-version: '1.8' - name: Cache Gradle Home files uses: actions/cache@v1 + continue-on-error: true with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-home-examples-${{matrix.gradle_args}}_check-${{ hashFiles('**/*.gradle') }} From 5cc47ef9d33028477cc5f678fbeb8ef1d5ddcb74 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 17:10:23 +0100 Subject: [PATCH 1650/5615] Bump snakeyaml from 1.25 to 1.27 in /core (#3252) Bumps [snakeyaml](https://bitbucket.org/asomov/snakeyaml) from 1.25 to 1.27. - [Commits](https://bitbucket.org/asomov/snakeyaml/branches/compare/snakeyaml-1.27..snakeyaml-1.25) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 37595c18e7c..222c04384fb 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -192,7 +192,7 @@ dependencies { compile 'com.github.docker-java:docker-java-transport-zerodep:3.2.5' - shaded "org.yaml:snakeyaml:1.25" + shaded "org.yaml:snakeyaml:1.27" shaded 'org.glassfish.main.external:trilead-ssh2-repackaged:4.1.2' From 7d1734c2e3bb539870ee36524dcdec6cf2bc98e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 17:12:00 +0100 Subject: [PATCH 1651/5615] Bump lombok from 1.18.12 to 1.18.14 in /examples (#3322) Bumps [lombok](https://github.com/rzwitserloot/lombok) from 1.18.12 to 1.18.14. - [Release notes](https://github.com/rzwitserloot/lombok/releases) - [Changelog](https://github.com/rzwitserloot/lombok/blob/master/doc/changelog.markdown) - [Commits](https://github.com/rzwitserloot/lombok/compare/v1.18.12...v1.18.14) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/disque-job-queue/build.gradle | 4 ++-- examples/solr-container/build.gradle | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index fb8413bdec8..c5e191f4f94 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -7,8 +7,8 @@ repositories { } dependencies { - compileOnly "org.projectlombok:lombok:1.18.12" - annotationProcessor "org.projectlombok:lombok:1.18.10" + compileOnly "org.projectlombok:lombok:1.18.14" + annotationProcessor "org.projectlombok:lombok:1.18.14" implementation 'biz.paluch.redis:spinach:0.3' implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.guava:guava:23.0' diff --git a/examples/solr-container/build.gradle b/examples/solr-container/build.gradle index 52fa54f1fc5..f72c3376cac 100644 --- a/examples/solr-container/build.gradle +++ b/examples/solr-container/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly "org.projectlombok:lombok:1.18.10" + compileOnly "org.projectlombok:lombok:1.18.14" annotationProcessor "org.projectlombok:lombok:1.18.10" implementation 'org.apache.solr:solr-solrj:8.6.2' From aeec90a4529b7ccbe507592f732d7c8c3fba71be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 17:12:12 +0100 Subject: [PATCH 1652/5615] Bump r2dbc-mariadb from 0.8.3-beta1 to 0.8.4-rc in /modules/mariadb (#3300) Bumps [r2dbc-mariadb](https://github.com/mariadb-corporation/mariadb-connector-r2dbc) from 0.8.3-beta1 to 0.8.4-rc. - [Release notes](https://github.com/mariadb-corporation/mariadb-connector-r2dbc/releases) - [Changelog](https://github.com/mariadb-corporation/mariadb-connector-r2dbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/mariadb-corporation/mariadb-connector-r2dbc/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 94164468855..19f1adba2cc 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -7,7 +7,7 @@ dependencies { compile project(':jdbc') compileOnly project(':r2dbc') - compileOnly 'org.mariadb:r2dbc-mariadb:0.8.3-beta1' + compileOnly 'org.mariadb:r2dbc-mariadb:0.8.4-rc' testCompile project(':jdbc-test') testCompile 'org.mariadb.jdbc:mariadb-java-client:2.6.2' From 413965a9711621473137e107871eb624ed19c450 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 17:12:18 +0100 Subject: [PATCH 1653/5615] Bump testng from 7.2.0 to 7.3.0 in /examples (#3068) Bumps [testng](https://github.com/cbeust/testng) from 7.2.0 to 7.3.0. - [Release notes](https://github.com/cbeust/testng/releases) - [Changelog](https://github.com/cbeust/testng/blob/master/CHANGES.txt) - [Commits](https://github.com/cbeust/testng/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/redis-backed-cache-testng/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 62f7de6dcf8..6ef351764f5 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -13,7 +13,7 @@ dependencies { implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'ch.qos.logback:logback-classic:1.2.3' - testImplementation 'org.testng:testng:7.2.0' + testImplementation 'org.testng:testng:7.3.0' } test { From 1845cfb8ffea55e29ea3c07435367309efe8ce7c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 17:12:42 +0100 Subject: [PATCH 1654/5615] Bump assertj-core from 3.17.1 to 3.17.2 in /core (#3251) Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.17.1 to 3.17.2. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.17.1...assertj-core-3.17.2) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 222c04384fb..37c42577942 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -211,7 +211,7 @@ dependencies { // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest testCompile files('testlib/repo/fakejar/fakejar/0/fakejar-0.jar') - testCompile 'org.assertj:assertj-core:3.17.1' + testCompile 'org.assertj:assertj-core:3.17.2' testCompile project(':test-support') jarFileTestCompileOnly "org.projectlombok:lombok:${lombok.version}" From 05a30f6561509a3c354a14c0655fe939458d3da9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 17:13:35 +0100 Subject: [PATCH 1655/5615] Bump aws-java-sdk-dynamodb from 1.11.865 to 1.11.880 in /modules/dynalite (#3332) Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.11.865 to 1.11.880. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.865...1.11.880) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/dynalite/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 0b2e334c633..fe8fb883839 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Dynalite" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.865' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.880' testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.865' } From 8b17881508e57b8b807d3a7ca456d28aa6728b66 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 17:16:44 +0100 Subject: [PATCH 1656/5615] Bump mockito-core from 3.5.11 to 3.5.13 in /core (#3275) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 37c42577942..b08e54c9b97 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -205,7 +205,7 @@ dependencies { testCompile 'com.rabbitmq:amqp-client:5.9.0' testCompile 'org.mongodb:mongo-java-driver:3.12.7' - testCompile ('org.mockito:mockito-core:3.5.11') { + testCompile ('org.mockito:mockito-core:3.5.13') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From 66e5f8a6b018433e17d9389ac9f8686bf87c1c4e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 17:17:10 +0100 Subject: [PATCH 1657/5615] Bump elasticsearch-rest-client from 7.9.1 to 7.9.2 in /modules/elasticsearch (#3276) Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 7.9.1 to 7.9.2. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.9.1...v7.9.2) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 44a5cca5963..e6de1daa7ab 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,6 +2,6 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') - testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.9.1" + testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.9.2" testCompile "org.elasticsearch.client:transport:6.7.1" } From 759a441f370ab589b36099ee0a7c2952bc0d139c Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 12 Oct 2020 19:33:35 +0100 Subject: [PATCH 1658/5615] Increase memory limits used in example (#3340) For improved test stability --- .../generic/src/test/java/generic/CmdModifierTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java b/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java index f49e6a26565..73338ba38ee 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java @@ -19,8 +19,8 @@ public class CmdModifierTest { // } // memory { - private long memoryInBytes = 8 * 1024 * 1024; - private long memorySwapInBytes = 12 * 1024 * 1024; + private long memoryInBytes = 32 * 1024 * 1024; + private long memorySwapInBytes = 64 * 1024 * 1024; @Rule public GenericContainer memoryLimitedRedis = new GenericContainer<>(DockerImageName.parse("redis:3.0.2")) @@ -40,6 +40,6 @@ public void testHostnameModified() throws IOException, InterruptedException { @Test public void testMemoryLimitModified() throws IOException, InterruptedException { final Container.ExecResult execResult = memoryLimitedRedis.execInContainer("cat", "/sys/fs/cgroup/memory/memory.limit_in_bytes"); - assertEquals("8388608", execResult.getStdout().trim()); + assertEquals(String.valueOf(memoryInBytes), execResult.getStdout().trim()); } } From 831b63918e671c9bfde508c064699ca2a1dd91a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 20:18:07 +0100 Subject: [PATCH 1659/5615] Bump mockito-core from 3.5.11 to 3.5.13 in /modules/junit-jupiter (#3283) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index dbf54b8d291..7b0c0cb82f9 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,7 +9,7 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.4.5' testCompile 'redis.clients:jedis:3.3.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.12' - testCompile ('org.mockito:mockito-core:3.5.11') { + testCompile ('org.mockito:mockito-core:3.5.13') { exclude(module: 'hamcrest-core') } testCompile 'org.assertj:assertj-core:3.17.2' From 61c4119be926e4a8f19b873de10b2815b3530cbb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 20:19:11 +0100 Subject: [PATCH 1660/5615] Bump tomcat-jdbc from 9.0.37 to 9.0.39 in /modules/jdbc-test (#3338) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index d8df69b094f..ec57ddcea41 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -15,7 +15,7 @@ dependencies { } compile 'commons-dbutils:commons-dbutils:1.7' - compile 'org.apache.tomcat:tomcat-jdbc:9.0.37' + compile 'org.apache.tomcat:tomcat-jdbc:9.0.39' compile 'org.vibur:vibur-dbcp:25.0' compile 'mysql:mysql-connector-java:8.0.21' } From 0fc92bda50cff021ff4e78bb129a51cb0097c3e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 20:19:22 +0100 Subject: [PATCH 1661/5615] Bump junit from 4.13 to 4.13.1 in /examples (#3328) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- examples/mongodb-container/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/mongodb-container/build.gradle b/examples/mongodb-container/build.gradle index c09cb033489..57b578a1607 100644 --- a/examples/mongodb-container/build.gradle +++ b/examples/mongodb-container/build.gradle @@ -8,6 +8,6 @@ repositories { dependencies { testImplementation 'org.testcontainers:testcontainers' - testImplementation 'junit:junit:4.13' + testImplementation 'junit:junit:4.13.1' testCompileClasspath 'org.jetbrains:annotations:20.1.0' } diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 258c98fb107..b0b6865aea7 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -12,6 +12,6 @@ dependencies { implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' - testImplementation 'junit:junit:4.13' + testImplementation 'junit:junit:4.13.1' testImplementation 'ch.qos.logback:logback-classic:1.2.3' } From b2db18f097fcf134c4c6f2288ba0c7939ceee950 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 20:20:24 +0100 Subject: [PATCH 1662/5615] Bump mariadb-java-client from 2.6.2 to 2.7.0 in /modules/mariadb (#3278) Bumps [mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 2.6.2 to 2.7.0. - [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases) - [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md) - [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/2.6.2...2.7.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 19f1adba2cc..e5f19eed97b 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'org.mariadb:r2dbc-mariadb:0.8.4-rc' testCompile project(':jdbc-test') - testCompile 'org.mariadb.jdbc:mariadb-java-client:2.6.2' + testCompile 'org.mariadb.jdbc:mariadb-java-client:2.7.0' testCompile testFixtures(project(':r2dbc')) testCompile 'org.mariadb:r2dbc-mariadb:0.8.3-beta1' From 1261fee751b570ba231ae45854801facb9bcc1b0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 20:20:52 +0100 Subject: [PATCH 1663/5615] Bump solr-solrj from 8.6.2 to 8.6.3 in /examples (#3321) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- examples/solr-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/solr-container/build.gradle b/examples/solr-container/build.gradle index f72c3376cac..5a8973cab27 100644 --- a/examples/solr-container/build.gradle +++ b/examples/solr-container/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly "org.projectlombok:lombok:1.18.14" annotationProcessor "org.projectlombok:lombok:1.18.10" - implementation 'org.apache.solr:solr-solrj:8.6.2' + implementation 'org.apache.solr:solr-solrj:8.6.3' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.testcontainers:solr' From b6b828ee0606dd046bcb832333d5eeb1842a1fb3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 20:21:05 +0100 Subject: [PATCH 1664/5615] Bump postgresql from 42.2.16 to 42.2.17 in /examples (#3323) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index eac2c59df4a..5c4c473becb 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' implementation 'com.squareup.okhttp3:okhttp:4.9.0' implementation 'org.json:json:20180813' - testImplementation 'org.postgresql:postgresql:42.2.16' + testImplementation 'org.postgresql:postgresql:42.2.17' testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.testcontainers:postgresql' } From 0f58f36406edf917487345647996afc6cf4ba0e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 20:21:15 +0100 Subject: [PATCH 1665/5615] Bump postgresql from 42.2.16 to 42.2.17 in /modules/junit-jupiter (#3327) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 7b0c0cb82f9..a1acce9f0f3 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -15,7 +15,7 @@ dependencies { testCompile 'org.assertj:assertj-core:3.17.2' testCompile 'org.junit.jupiter:junit-jupiter-params:5.7.0' - testRuntime 'org.postgresql:postgresql:42.2.16' + testRuntime 'org.postgresql:postgresql:42.2.17' testRuntime 'mysql:mysql-connector-java:8.0.21' testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.7.0' } From 8558994aa7e66585ad6249cb24fce049b6037629 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 20:21:32 +0100 Subject: [PATCH 1666/5615] Bump postgresql from 42.2.16 to 42.2.17 in /modules/spock (#3330) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 3d0948e9e83..8d05bdf58b6 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -15,7 +15,7 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.4.5' testCompile 'org.apache.httpcomponents:httpclient:4.5.12' - testRuntime 'org.postgresql:postgresql:42.2.16' + testRuntime 'org.postgresql:postgresql:42.2.17' testRuntime 'mysql:mysql-connector-java:8.0.21' testCompileClasspath 'org.jetbrains:annotations:20.1.0' From 5b70c5860587ed3ed4073cef13d72a0d7ac4362e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 20:21:42 +0100 Subject: [PATCH 1667/5615] Bump tomcat-jdbc from 9.0.37 to 9.0.39 in /modules/jdbc (#3333) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 6453b49b052..2045461785e 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -7,7 +7,7 @@ dependencies { compileOnly 'org.jetbrains:annotations:20.1.0' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.vibur:vibur-dbcp:25.0' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.37' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.39' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'com.googlecode.junit-toolbox:junit-toolbox:2.4' } From 7681f1214c94042ae937ad010de485dd9f4229fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 20:21:51 +0100 Subject: [PATCH 1668/5615] Bump postgresql from 42.2.16 to 42.2.17 in /modules/postgresql (#3334) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index ea6e4a87226..aa5db7028b5 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -11,7 +11,7 @@ dependencies { testCompile project(':jdbc-test') testCompile project(':test-support') - testCompile 'org.postgresql:postgresql:42.2.16' + testCompile 'org.postgresql:postgresql:42.2.17' testCompile testFixtures(project(':r2dbc')) testCompile 'io.r2dbc:r2dbc-postgresql:0.8.5.RELEASE' From 304c2d702096714fba15270616a5f3c56328a560 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 20:21:59 +0100 Subject: [PATCH 1669/5615] Bump aws-java-sdk-sqs from 1.11.860 to 1.11.880 in /modules/localstack (#3337) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index f48b3cf71bb..8d4b9576cae 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.870' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.865' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.860' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.880' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' testCompile 'software.amazon.awssdk:s3:2.14.21' } From aa8e5ed0a77f48858fb3622664844d14f5068a3b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 21:47:43 +0100 Subject: [PATCH 1670/5615] Bump httpclient from 4.5.12 to 4.5.13 in /modules/junit-jupiter (#3326) Bumps httpclient from 4.5.12 to 4.5.13. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index a1acce9f0f3..28f6049faf4 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -8,7 +8,7 @@ dependencies { testCompile project(':postgresql') testCompile 'com.zaxxer:HikariCP:3.4.5' testCompile 'redis.clients:jedis:3.3.0' - testCompile 'org.apache.httpcomponents:httpclient:4.5.12' + testCompile 'org.apache.httpcomponents:httpclient:4.5.13' testCompile ('org.mockito:mockito-core:3.5.13') { exclude(module: 'hamcrest-core') } From 98a8260bf05953a823b4a526637effa50054de0e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 22:05:52 +0100 Subject: [PATCH 1671/5615] Bump httpclient from 4.5.12 to 4.5.13 in /modules/spock (#3329) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 8d05bdf58b6..86724262a29 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -13,7 +13,7 @@ dependencies { testCompile project(':postgresql') testCompile 'com.zaxxer:HikariCP:3.4.5' - testCompile 'org.apache.httpcomponents:httpclient:4.5.12' + testCompile 'org.apache.httpcomponents:httpclient:4.5.13' testRuntime 'org.postgresql:postgresql:42.2.17' testRuntime 'mysql:mysql-connector-java:8.0.21' From a0942943e03fc07bfcd03fa2fa0558f32a73d789 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 22:06:12 +0100 Subject: [PATCH 1672/5615] Bump cucumber-junit from 6.7.0 to 6.8.1 in /examples (#3325) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 318ed51f24c..5a9fd6f048f 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' testImplementation 'io.cucumber:cucumber-java:6.6.0' - testImplementation 'io.cucumber:cucumber-junit:6.7.0' + testImplementation 'io.cucumber:cucumber-junit:6.8.1' testImplementation 'org.testcontainers:selenium' } From c1e8234f737756619f6b861e94621a7546834042 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 22:15:40 +0100 Subject: [PATCH 1673/5615] Bump org.springframework.boot from 2.3.3.RELEASE to 2.3.4.RELEASE in /examples (#3247) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index eb9c2d9ce3a..2dca03f4004 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.3.3.RELEASE' + id 'org.springframework.boot' version '2.3.4.RELEASE' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index d74851d8eac..75907da4c0d 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.3.3.RELEASE' + id 'org.springframework.boot' version '2.3.4.RELEASE' } apply plugin: 'io.spring.dependency-management' From 325c8b287c7d9a6d56d15bc8c5bb5efa0c5e0d5d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Oct 2020 08:09:54 +0100 Subject: [PATCH 1674/5615] Bump aws-java-sdk-s3 from 1.11.870 to 1.11.880 in /modules/localstack (#3336) Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.11.870 to 1.11.880. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.870...1.11.880) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 8d4b9576cae..b99ec1c54fe 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.870' - testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.865' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.880' + testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.880' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.880' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' testCompile 'software.amazon.awssdk:s3:2.14.21' From 4801ed82400afd53fb991649cf1f9cf6931030ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Oct 2020 08:48:27 +0100 Subject: [PATCH 1675/5615] Bump zt-exec from 1.10 to 1.12 in /core (#3253) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index b08e54c9b97..e043313ac38 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -196,7 +196,7 @@ dependencies { shaded 'org.glassfish.main.external:trilead-ssh2-repackaged:4.1.2' - shaded 'org.zeroturnaround:zt-exec:1.10', { + shaded 'org.zeroturnaround:zt-exec:1.12', { exclude(group: 'org.slf4j') } From b2b8e62d1ffbd9f8951944bb7f44f0429fa89156 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Oct 2020 08:48:39 +0100 Subject: [PATCH 1676/5615] Bump s3 from 2.14.21 to 2.15.7 in /modules/localstack (#3335) Bumps s3 from 2.14.21 to 2.15.7. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index b99ec1c54fe..29abca90878 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,5 +7,5 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.880' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.880' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' - testCompile 'software.amazon.awssdk:s3:2.14.21' + testCompile 'software.amazon.awssdk:s3:2.15.7' } From e4c4cfebdbbb21b2b008f86bf6995c7477c59b9e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Oct 2020 08:48:52 +0100 Subject: [PATCH 1677/5615] Bump cucumber-java from 6.6.0 to 6.8.1 in /examples (#3324) Bumps [cucumber-java](https://github.com/cucumber/cucumber-jvm) from 6.6.0 to 6.8.1. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v6.6.0...v6.8.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 5a9fd6f048f..57104731c7e 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' - testImplementation 'io.cucumber:cucumber-java:6.6.0' + testImplementation 'io.cucumber:cucumber-java:6.8.1' testImplementation 'io.cucumber:cucumber-junit:6.8.1' testImplementation 'org.testcontainers:selenium' } From b0735235f0bd990665584f739726a4c4dee1b80f Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 13 Oct 2020 09:31:33 +0100 Subject: [PATCH 1678/5615] Remove GitHub Actions cache restore keys (#3342) For more specific cache matching --- .github/workflows/ci-examples.yml | 4 ---- .github/workflows/ci.yml | 4 ---- 2 files changed, 8 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index 6f8e4868a97..4f91341a4a6 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -24,8 +24,6 @@ jobs: with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-home-testmatrix-examples-${{ hashFiles('**/*.gradle') }} - restore-keys: | - ${{ runner.os }}-gradle-home-testmatrix-examples- - id: set-matrix working-directory: ./examples/ env: @@ -53,8 +51,6 @@ jobs: with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-home-examples-${{matrix.gradle_args}}_check-${{ hashFiles('**/*.gradle') }} - restore-keys: | - ${{ runner.os }}-gradle-home-examples-${{matrix.gradle_args}}_check- - name: Clear existing docker image cache run: docker image prune -af - name: Build and test Examples with Gradle (${{matrix.gradle_args}}) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b5a37a3405..df38f3b8a4f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,8 +23,6 @@ jobs: with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-home-testmatrix-${{ hashFiles('**/*.gradle') }} - restore-keys: | - ${{ runner.os }}-gradle-home-testmatrix- - id: set-matrix env: # Since we override the tests executor, @@ -51,8 +49,6 @@ jobs: with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-home-${{matrix.gradle_args}}_check-${{ hashFiles('**/*.gradle') }} - restore-keys: | - ${{ runner.os }}-gradle-home-${{matrix.gradle_args}}_check- - name: Clear existing docker image cache run: docker image prune -af - name: Build and test with Gradle (${{matrix.gradle_args}}) From 8ab49eec547103d5d3e4410e3fb0deef08f52336 Mon Sep 17 00:00:00 2001 From: silaev Date: Tue, 13 Oct 2020 15:36:05 +0300 Subject: [PATCH 1679/5615] Allow users to specify a MongoDB database name (#2980) Co-authored-by: Richard North --- .../containers/MongoDBContainer.java | 17 ++++++++++++++++- .../containers/MongoDBContainerTest.java | 13 +++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java index 5fd2451480b..662c4b575f7 100644 --- a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java @@ -48,7 +48,22 @@ public MongoDBContainer(final DockerImageName dockerImageName) { ); } + /** + * Gets a replica set url for the default {@value #MONGODB_DATABASE_NAME_DEFAULT} database. + * + * @return a replica set url. + */ public String getReplicaSetUrl() { + return getReplicaSetUrl(MONGODB_DATABASE_NAME_DEFAULT); + } + + /** + * Gets a replica set url for a provided databaseName. + * + * @param databaseName a database name. + * @return a replica set url. + */ + public String getReplicaSetUrl(final String databaseName) { if (!isRunning()) { throw new IllegalStateException("MongoDBContainer should be started first"); } @@ -56,7 +71,7 @@ public String getReplicaSetUrl() { "mongodb://%s:%d/%s", getContainerIpAddress(), getMappedPort(MONGODB_INTERNAL_PORT), - MONGODB_DATABASE_NAME_DEFAULT + databaseName ); } diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java index b18e7f15a01..ff5243acea7 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java @@ -13,8 +13,10 @@ import org.junit.Test; import org.testcontainers.utility.DockerImageName; +import static org.hamcrest.CoreMatchers.endsWith; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; public class MongoDBContainerTest { @@ -82,4 +84,15 @@ public void supportsMongoDB_4_4() { mongoDBContainer.start(); } } + + @Test + public void shouldTestDatabaseName() { + try ( + final MongoDBContainer mongoDBContainer = new MongoDBContainer(DockerImageName.parse("mongo:4.0.10")) + ) { + mongoDBContainer.start(); + final String databaseName = "my-db"; + assertThat(mongoDBContainer.getReplicaSetUrl(databaseName), endsWith(databaseName)); + } + } } From 0065801b4f13cd29af7904abd6a57d4525e710fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Tue, 13 Oct 2020 09:51:44 -0500 Subject: [PATCH 1680/5615] Add GCloud module for Google Cloud Datastore, Firestore, PubSub, and Spanner emulators (#2690) Co-authored-by: Richard North --- docs/modules/gcloud.md | 44 ++++++++ mkdocs.yml | 1 + modules/gcloud/build.gradle | 11 ++ .../DatastoreEmulatorContainer.java | 29 +++++ .../FirestoreEmulatorContainer.java | 30 ++++++ .../containers/PubSubEmulatorContainer.java | 30 ++++++ .../containers/SpannerEmulatorContainer.java | 28 +++++ .../DatastoreEmulatorContainerTest.java | 43 ++++++++ .../FirestoreEmulatorContainerTest.java | 49 +++++++++ .../PubSubEmulatorContainerTest.java | 102 ++++++++++++++++++ .../SpannerEmulatorContainerTest.java | 80 ++++++++++++++ 11 files changed, 447 insertions(+) create mode 100644 docs/modules/gcloud.md create mode 100644 modules/gcloud/build.gradle create mode 100644 modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java create mode 100644 modules/gcloud/src/main/java/org/testcontainers/containers/FirestoreEmulatorContainer.java create mode 100644 modules/gcloud/src/main/java/org/testcontainers/containers/PubSubEmulatorContainer.java create mode 100644 modules/gcloud/src/main/java/org/testcontainers/containers/SpannerEmulatorContainer.java create mode 100644 modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java create mode 100644 modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java create mode 100644 modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java create mode 100644 modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java diff --git a/docs/modules/gcloud.md b/docs/modules/gcloud.md new file mode 100644 index 00000000000..3400ad881a9 --- /dev/null +++ b/docs/modules/gcloud.md @@ -0,0 +1,44 @@ +# GCloud Module + +!!! note + This module is INCUBATING. While it is ready for use and operational in the current version of Testcontainers, it is possible that it may receive breaking changes in the future. See [our contributing guidelines](/contributing/#incubating-modules) for more information on our incubating modules policy. + +Testcontainers module for the Google's [Cloud SDK](https://cloud.google.com/sdk/). + +Currently, the module supports `Datastore`, `Firestore`, `Pub/Sub` and `Spanner` emulators. In order to use it, you should use the following classes: + +* DatastoreEmulatorContainer +* FirestoreEmulatorContainer +* PubSubEmulatorContainer +* SpannerEmulatorContainer + +## Usage example + +Running GCloud as a stand-in for Google Datastore during a test: + + +[Creating a Datastore container](../../modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java) inside_block:creatingDatastoreEmulatorContainer + + +And how to start it: + + +[Starting a Datastore container](../../modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java) inside_block:startingDatastoreEmulatorContainer + + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:gcloud:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + gcloud + {{latest_version}} + test + +``` diff --git a/mkdocs.yml b/mkdocs.yml index b8372e81ed8..dfff99bb302 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -60,6 +60,7 @@ nav: - modules/databases/presto.md - modules/docker_compose.md - modules/elasticsearch.md + - modules/gcloud.md - modules/kafka.md - modules/localstack.md - modules/mockserver.md diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle new file mode 100644 index 00000000000..a5737648a4b --- /dev/null +++ b/modules/gcloud/build.gradle @@ -0,0 +1,11 @@ +description = "Testcontainers :: GCloud" + +dependencies { + compile project(':testcontainers') + + testCompile 'com.google.cloud:google-cloud-datastore:1.102.4' + testCompile 'com.google.cloud:google-cloud-firestore:1.33.0' + testCompile 'com.google.cloud:google-cloud-pubsub:1.105.0' + testCompile 'com.google.cloud:google-cloud-spanner:1.50.0' + testCompile 'org.assertj:assertj-core:3.15.0' +} diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java new file mode 100644 index 00000000000..85b07b94591 --- /dev/null +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java @@ -0,0 +1,29 @@ +package org.testcontainers.containers; + +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.utility.DockerImageName; + +/** + * A Datastore container that relies in google cloud sdk. + * + * Default port is 8081. + * + * @author Eddú Meléndez + */ +public class DatastoreEmulatorContainer extends GenericContainer { + + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk"); + + private static final String CMD = "gcloud beta emulators datastore start --project test-project --host-port 0.0.0.0:8081"; + + public DatastoreEmulatorContainer(final DockerImageName dockerImageName) { + super(dockerImageName); + + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + + withExposedPorts(8081); + setWaitStrategy(Wait.forHttp("/").forStatusCode(200)); + withCommand("/bin/sh", "-c", CMD); + } + +} diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/FirestoreEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/FirestoreEmulatorContainer.java new file mode 100644 index 00000000000..41442f0f531 --- /dev/null +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/FirestoreEmulatorContainer.java @@ -0,0 +1,30 @@ +package org.testcontainers.containers; + +import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; +import org.testcontainers.utility.DockerImageName; + +/** + * A Firestore container that relies in google cloud sdk. + * + * Default port is 8080. + * + * @author Eddú Meléndez + */ +public class FirestoreEmulatorContainer extends GenericContainer { + + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk"); + + private static final String CMD = "gcloud beta emulators firestore start --host-port 0.0.0.0:8080"; + + public FirestoreEmulatorContainer(final DockerImageName dockerImageName) { + super(dockerImageName); + + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + + withExposedPorts(8080); + setWaitStrategy(new LogMessageWaitStrategy() + .withRegEx("(?s).*running.*$")); + withCommand("/bin/sh", "-c", CMD); + } + +} diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/PubSubEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/PubSubEmulatorContainer.java new file mode 100644 index 00000000000..c92145b3d3a --- /dev/null +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/PubSubEmulatorContainer.java @@ -0,0 +1,30 @@ +package org.testcontainers.containers; + +import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; +import org.testcontainers.utility.DockerImageName; + +/** + * A PubSub container that relies in google cloud sdk. + * + * Default port is 8085. + * + * @author Eddú Meléndez + */ +public class PubSubEmulatorContainer extends GenericContainer { + + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk"); + + private static final String CMD = "gcloud beta emulators pubsub start --host-port 0.0.0.0:8085"; + + public PubSubEmulatorContainer(final DockerImageName dockerImageName) { + super(dockerImageName); + + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + + withExposedPorts(8085); + setWaitStrategy(new LogMessageWaitStrategy() + .withRegEx("(?s).*started.*$")); + withCommand("/bin/sh", "-c", CMD); + } + +} diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/SpannerEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/SpannerEmulatorContainer.java new file mode 100644 index 00000000000..5f2a203d911 --- /dev/null +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/SpannerEmulatorContainer.java @@ -0,0 +1,28 @@ +package org.testcontainers.containers; + +import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; +import org.testcontainers.utility.DockerImageName; + +/** + * A Spanner container. Default ports: 9010 for GRPC and 9020 for HTTP. + * + * @author Eddú Meléndez + */ +public class SpannerEmulatorContainer extends GenericContainer { + + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("gcr.io/cloud-spanner-emulator/emulator"); + + private static final int GRPC_PORT = 9010; + private static final int HTTP_PORT = 9020; + + public SpannerEmulatorContainer(final DockerImageName dockerImageName) { + super(dockerImageName); + + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + + withExposedPorts(GRPC_PORT, HTTP_PORT); + setWaitStrategy(new LogMessageWaitStrategy() + .withRegEx(".*Cloud Spanner emulator running\\..*")); + } + +} diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java new file mode 100644 index 00000000000..f8285e4c691 --- /dev/null +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java @@ -0,0 +1,43 @@ +package org.testcontainers.containers; + +import com.google.cloud.NoCredentials; +import com.google.cloud.ServiceOptions; +import com.google.cloud.datastore.Datastore; +import com.google.cloud.datastore.DatastoreOptions; +import com.google.cloud.datastore.Entity; +import com.google.cloud.datastore.Key; +import org.junit.Rule; +import org.junit.Test; +import org.testcontainers.utility.DockerImageName; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DatastoreEmulatorContainerTest { + + @Rule + // creatingDatastoreEmulatorContainer { + public DatastoreEmulatorContainer emulator = new DatastoreEmulatorContainer( + DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:313.0.0") + ); + // } + + // startingDatastoreEmulatorContainer { + @Test + public void testSimple() { + DatastoreOptions options = DatastoreOptions.newBuilder() + .setHost(emulator.getContainerIpAddress() + ":" + emulator.getMappedPort(8081)) + .setCredentials(NoCredentials.getInstance()) + .setRetrySettings(ServiceOptions.getNoRetrySettings()) + .setProjectId("test-project") + .build(); + Datastore datastore = options.getService(); + + Key key = datastore.newKeyFactory().setKind("Task").newKey("sample"); + Entity entity = Entity.newBuilder(key).set("description", "my description").build(); + datastore.put(entity); + + assertThat(datastore.get(key).getString("description")).isEqualTo("my description"); + } + // } + +} diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java new file mode 100644 index 00000000000..2f23ff40483 --- /dev/null +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java @@ -0,0 +1,49 @@ +package org.testcontainers.containers; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +import com.google.api.core.ApiFuture; +import com.google.cloud.NoCredentials; +import com.google.cloud.firestore.CollectionReference; +import com.google.cloud.firestore.DocumentReference; +import com.google.cloud.firestore.Firestore; +import com.google.cloud.firestore.FirestoreOptions; +import com.google.cloud.firestore.QuerySnapshot; +import com.google.cloud.firestore.WriteResult; +import org.junit.Rule; +import org.junit.Test; +import org.testcontainers.utility.DockerImageName; + +import static org.assertj.core.api.Assertions.assertThat; + +public class FirestoreEmulatorContainerTest { + + @Rule + public FirestoreEmulatorContainer emulator = new FirestoreEmulatorContainer(DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:313.0.0")); + + @Test + public void testSimple() throws ExecutionException, InterruptedException { + FirestoreOptions options = FirestoreOptions.getDefaultInstance().toBuilder() + .setHost(emulator.getContainerIpAddress() + ":" + emulator.getMappedPort(8080)) + .setCredentials(NoCredentials.getInstance()) + .setProjectId("test-project") + .build(); + Firestore firestore = options.getService(); + + CollectionReference users = firestore.collection("users"); + DocumentReference docRef = users.document("alovelace"); + Map data = new HashMap<>(); + data.put("first", "Ada"); + data.put("last", "Lovelace"); + ApiFuture result = docRef.set(data); + result.get(); + + ApiFuture query = users.get(); + QuerySnapshot querySnapshot = query.get(); + + assertThat(querySnapshot.getDocuments().get(0).getData()).containsEntry("first", "Ada"); + } + +} diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java new file mode 100644 index 00000000000..0c5ec888de5 --- /dev/null +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java @@ -0,0 +1,102 @@ +package org.testcontainers.containers; + +import java.io.IOException; + +import com.google.api.gax.core.NoCredentialsProvider; +import com.google.api.gax.grpc.GrpcTransportChannel; +import com.google.api.gax.rpc.FixedTransportChannelProvider; +import com.google.api.gax.rpc.TransportChannelProvider; +import com.google.cloud.pubsub.v1.Publisher; +import com.google.cloud.pubsub.v1.SubscriptionAdminClient; +import com.google.cloud.pubsub.v1.SubscriptionAdminSettings; +import com.google.cloud.pubsub.v1.TopicAdminClient; +import com.google.cloud.pubsub.v1.TopicAdminSettings; +import com.google.cloud.pubsub.v1.stub.GrpcSubscriberStub; +import com.google.cloud.pubsub.v1.stub.SubscriberStub; +import com.google.cloud.pubsub.v1.stub.SubscriberStubSettings; +import com.google.protobuf.ByteString; +import com.google.pubsub.v1.ProjectSubscriptionName; +import com.google.pubsub.v1.PubsubMessage; +import com.google.pubsub.v1.PullRequest; +import com.google.pubsub.v1.PullResponse; +import com.google.pubsub.v1.PushConfig; +import com.google.pubsub.v1.TopicName; +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; +import org.junit.Rule; +import org.junit.Test; +import org.testcontainers.utility.DockerImageName; + +import static org.assertj.core.api.Assertions.assertThat; + +public class PubSubEmulatorContainerTest { + + public static final String PROJECT_ID = "my-project-id"; + + @Rule + public PubSubEmulatorContainer emulator = new PubSubEmulatorContainer(DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:313.0.0")); + + @Test + public void testSimple() throws IOException { + String hostport = emulator.getContainerIpAddress() + ":" + emulator.getMappedPort(8085); + ManagedChannel channel = ManagedChannelBuilder.forTarget(hostport).usePlaintext().build(); + try { + TransportChannelProvider channelProvider = + FixedTransportChannelProvider.create(GrpcTransportChannel.create(channel)); + NoCredentialsProvider credentialsProvider = NoCredentialsProvider.create(); + + String topicId = "my-topic-id"; + createTopic(topicId, channelProvider, credentialsProvider); + + String subscriptionId = "my-subscription-id"; + createSubscription(subscriptionId, topicId, channelProvider, credentialsProvider); + + Publisher publisher = Publisher.newBuilder(TopicName.of(PROJECT_ID, topicId)) + .setChannelProvider(channelProvider) + .setCredentialsProvider(credentialsProvider) + .build(); + PubsubMessage message = PubsubMessage.newBuilder().setData(ByteString.copyFromUtf8("test message")).build(); + publisher.publish(message); + + SubscriberStubSettings subscriberStubSettings = + SubscriberStubSettings.newBuilder() + .setTransportChannelProvider(channelProvider) + .setCredentialsProvider(credentialsProvider) + .build(); + try (SubscriberStub subscriber = GrpcSubscriberStub.create(subscriberStubSettings)) { + PullRequest pullRequest = PullRequest.newBuilder() + .setMaxMessages(1) + .setSubscription(ProjectSubscriptionName.format(PROJECT_ID, subscriptionId)) + .build(); + PullResponse pullResponse = subscriber.pullCallable().call(pullRequest); + + assertThat(pullResponse.getReceivedMessagesList()).hasSize(1); + assertThat(pullResponse.getReceivedMessages(0).getMessage().getData().toStringUtf8()).isEqualTo("test message"); + } + } finally { + channel.shutdown(); + } + } + + private void createTopic(String topicId, TransportChannelProvider channelProvider, NoCredentialsProvider credentialsProvider) throws IOException { + TopicAdminSettings topicAdminSettings = TopicAdminSettings.newBuilder() + .setTransportChannelProvider(channelProvider) + .setCredentialsProvider(credentialsProvider) + .build(); + try (TopicAdminClient topicAdminClient = TopicAdminClient.create(topicAdminSettings)) { + TopicName topicName = TopicName.of(PROJECT_ID, topicId); + topicAdminClient.createTopic(topicName); + } + } + + private void createSubscription(String subscriptionId, String topicId, TransportChannelProvider channelProvider, NoCredentialsProvider credentialsProvider) throws IOException { + SubscriptionAdminSettings subscriptionAdminSettings = SubscriptionAdminSettings.newBuilder() + .setTransportChannelProvider(channelProvider) + .setCredentialsProvider(credentialsProvider) + .build(); + SubscriptionAdminClient subscriptionAdminClient = SubscriptionAdminClient.create(subscriptionAdminSettings); + ProjectSubscriptionName subscriptionName = ProjectSubscriptionName.of(PROJECT_ID, subscriptionId); + subscriptionAdminClient.createSubscription(subscriptionName, TopicName.of(PROJECT_ID, topicId), PushConfig.getDefaultInstance(), 10); + } + +} diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java new file mode 100644 index 00000000000..9093530236a --- /dev/null +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java @@ -0,0 +1,80 @@ +package org.testcontainers.containers; + +import java.util.Arrays; +import java.util.concurrent.ExecutionException; + +import com.google.cloud.NoCredentials; +import com.google.cloud.spanner.Database; +import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.DatabaseClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.Instance; +import com.google.cloud.spanner.InstanceAdminClient; +import com.google.cloud.spanner.InstanceConfigId; +import com.google.cloud.spanner.InstanceId; +import com.google.cloud.spanner.InstanceInfo; +import com.google.cloud.spanner.ResultSet; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerOptions; +import com.google.cloud.spanner.Statement; +import org.junit.Rule; +import org.junit.Test; +import org.testcontainers.utility.DockerImageName; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SpannerEmulatorContainerTest { + + @Rule + public SpannerEmulatorContainer emulator = new SpannerEmulatorContainer(DockerImageName.parse("gcr.io/cloud-spanner-emulator/emulator:1.1.0")); + + private static final String PROJECT_NAME = "test-project"; + private static final String INSTANCE_NAME = "test-instance"; + private static final String DATABASE_NAME = "test-database"; + + @Test + public void testSimple() throws ExecutionException, InterruptedException { + SpannerOptions options = SpannerOptions.newBuilder() + .setEmulatorHost(emulator.getContainerIpAddress() + ":" + emulator.getMappedPort(9010)) + .setCredentials(NoCredentials.getInstance()) + .setProjectId(PROJECT_NAME) + .build(); + + Spanner spanner = options.getService(); + + InstanceId instanceId = createInstance(spanner); + + createDatabase(spanner); + + DatabaseId databaseId = DatabaseId.of(instanceId, DATABASE_NAME); + DatabaseClient dbClient = spanner.getDatabaseClient(databaseId); + dbClient.readWriteTransaction() + .run(tx -> { + String sql1 = "Delete from TestTable where 1=1"; + tx.executeUpdate(Statement.of(sql1)); + String sql = "INSERT INTO TestTable (Key, Value) VALUES (1, 'Java'), (2, 'Go')"; + tx.executeUpdate(Statement.of(sql)); + return null; + }); + + ResultSet resultSet = dbClient.readOnlyTransaction() + .executeQuery(Statement.of("select * from TestTable order by Key")); + resultSet.next(); + assertThat(resultSet.getLong(0)).isEqualTo(1); + assertThat(resultSet.getString(1)).isEqualTo("Java"); + } + + private void createDatabase(Spanner spanner) throws InterruptedException, ExecutionException { + DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient(); + Database database = dbAdminClient.createDatabase(INSTANCE_NAME, DATABASE_NAME, Arrays.asList("CREATE TABLE TestTable (Key INT64, Value STRING(MAX)) PRIMARY KEY (Key)")).get(); + } + + private InstanceId createInstance(Spanner spanner) throws InterruptedException, ExecutionException { + InstanceConfigId instanceConfig = InstanceConfigId.of(PROJECT_NAME, "emulator-config"); + InstanceId instanceId = InstanceId.of(PROJECT_NAME, INSTANCE_NAME); + InstanceAdminClient insAdminClient = spanner.getInstanceAdminClient(); + Instance instance = insAdminClient.createInstance(InstanceInfo.newBuilder(instanceId).setNodeCount(1).setDisplayName("Test instance").setInstanceConfigId(instanceConfig).build()).get(); + return instanceId; + } + +} From 3bf8d76683a38bdcdef6b12ab2f699e04b9c6a1c Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 13 Oct 2020 20:01:20 +0100 Subject: [PATCH 1681/5615] Use a lighter weight image for MultiplePortsExposedTest (#3343) * Use a lighter weight image for MultiplePortsExposedTest * Update helloworld container version --- .../java/generic/MultiplePortsExposedTest.java | 14 +++++++------- .../junit/BaseWebDriverContainerTest.java | 13 ++++++------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/docs/examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java b/docs/examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java index 329fd8d8e19..de2b56c6a63 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java @@ -1,5 +1,7 @@ package generic; +import static org.slf4j.LoggerFactory.getLogger; + import org.junit.Rule; import org.junit.Test; import org.slf4j.Logger; @@ -7,23 +9,21 @@ import org.testcontainers.containers.output.Slf4jLogConsumer; import org.testcontainers.utility.DockerImageName; -import static org.slf4j.LoggerFactory.getLogger; - public class MultiplePortsExposedTest { private static final Logger log = getLogger(MultiplePortsExposedTest.class); @Rule // rule { - public GenericContainer container = new GenericContainer(DockerImageName.parse("orientdb:3.0.13")) - .withExposedPorts(2424, 2480) + public GenericContainer container = new GenericContainer<>(DockerImageName.parse("testcontainers/helloworld:1.1.0")) + .withExposedPorts(8080, 8081) .withLogConsumer(new Slf4jLogConsumer(log)); // } @Test public void fetchPortsByNumber() { - Integer firstMappedPort = container.getMappedPort(2424); - Integer secondMappedPort = container.getMappedPort(2480); + Integer firstMappedPort = container.getMappedPort(8080); + Integer secondMappedPort = container.getMappedPort(8081); } @Test @@ -39,6 +39,6 @@ public void getHostOnly() { @Test public void getHostAndMappedPort() { String address = - container.getHost() + ":" + container.getMappedPort(2424); + container.getHost() + ":" + container.getMappedPort(8080); } } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java index cf5c762f2ad..db369824442 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java @@ -1,5 +1,10 @@ package org.testcontainers.junit; +import static java.lang.String.format; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; + +import java.util.concurrent.TimeUnit; import org.jetbrains.annotations.NotNull; import org.junit.ClassRule; import org.openqa.selenium.By; @@ -11,12 +16,6 @@ import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.utility.DockerImageName; -import java.util.concurrent.TimeUnit; - -import static java.lang.String.format; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; - /** * */ @@ -26,7 +25,7 @@ public class BaseWebDriverContainerTest { public static Network NETWORK = Network.newNetwork(); @ClassRule - public static GenericContainer HELLO_WORLD = new GenericContainer<>(DockerImageName.parse("testcontainers/helloworld:1.0.0")) + public static GenericContainer HELLO_WORLD = new GenericContainer<>(DockerImageName.parse("testcontainers/helloworld:1.1.0")) .withNetwork(NETWORK) .withNetworkAliases("helloworld") .withExposedPorts(8080, 8081) From ae5010aee429620acac888cef532d89135710c1d Mon Sep 17 00:00:00 2001 From: vcvitaly Date: Wed, 14 Oct 2020 19:26:16 +0300 Subject: [PATCH 1682/5615] docker-machine: get full remote daemon URL, to allow for use of custom daemon port (#2769) (#3237) Co-authored-by: Vitalii Chura --- .../dockerclient/DockerMachineClientProviderStrategy.java | 6 +++--- .../org/testcontainers/utility/DockerMachineClient.java | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java index 2b0454c1ee6..4de508ea808 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java @@ -37,12 +37,12 @@ private TransportConfig resolveTransportConfig() throws InvalidConfigurationExce DockerMachineClient.instance().ensureMachineRunning(machineName); - String dockerDaemonIpAddress = DockerMachineClient.instance().getDockerDaemonIpAddress(machineName); + String dockerDaemonUrl = DockerMachineClient.instance().getDockerDaemonUrl(machineName); - log.info("Docker daemon IP address for docker machine {} is {}", machineName, dockerDaemonIpAddress); + log.info("Docker daemon URL for docker machine {} is {}", machineName, dockerDaemonUrl); return TransportConfig.builder() - .dockerHost(URI.create("tcp://" + dockerDaemonIpAddress + ":2376")) + .dockerHost(URI.create(dockerDaemonUrl)) .sslConfig( new LocalDirectorySSLConfig( Paths.get(System.getProperty("user.home") + "/.docker/machine/certs/").toString() diff --git a/core/src/main/java/org/testcontainers/utility/DockerMachineClient.java b/core/src/main/java/org/testcontainers/utility/DockerMachineClient.java index fe12e9f541d..021baf270d9 100644 --- a/core/src/main/java/org/testcontainers/utility/DockerMachineClient.java +++ b/core/src/main/java/org/testcontainers/utility/DockerMachineClient.java @@ -79,10 +79,18 @@ public void ensureMachineRunning(@NonNull String machineName) { } } + /** + * @deprecated Use getDockerDaemonUrl(@NonNull String machineName) for connection to docker-machine + */ + @Deprecated public String getDockerDaemonIpAddress(@NonNull String machineName) { return runShellCommand(executableName, "ip", machineName); } + public String getDockerDaemonUrl(@NonNull String machineName) { + return runShellCommand(executableName, "url", machineName); + } + public boolean isMachineRunning(String machineName) { String status = runShellCommand("docker-machine", "status", machineName); return status.trim().equalsIgnoreCase("running"); From abf0f9aa65207f67e1174ad2b43b4a354f5ae998 Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 14 Oct 2020 22:46:52 +0100 Subject: [PATCH 1683/5615] Fix remote gradle cache 400 InvalidArgument error (#3346) per hint given in https://www.digitalocean.com/community/questions/node-upload-file-to-s3-error-invalidargument-null --- settings.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/settings.gradle b/settings.gradle index 2ea21599ceb..849a1ade06b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -46,6 +46,7 @@ buildCache { region = 'fra1' bucket = 'testcontainers' path = 'cache' + reducedRedundancy = false push = isMasterBuild && !System.getenv("READ_ONLY_REMOTE_GRADLE_CACHE") headers = [ 'x-amz-acl': 'public-read' From a33622f143c93ee2d771eda30bb8ab88eb450c9d Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 15 Oct 2020 08:46:41 +0100 Subject: [PATCH 1684/5615] Add gcloud endpoint accessors (#3344) --- .../containers/DatastoreEmulatorContainer.java | 11 ++++++++++- .../containers/FirestoreEmulatorContainer.java | 11 ++++++++++- .../containers/PubSubEmulatorContainer.java | 9 +++++++++ .../containers/SpannerEmulatorContainer.java | 16 ++++++++++++++++ .../DatastoreEmulatorContainerTest.java | 6 +++--- .../FirestoreEmulatorContainerTest.java | 11 +++++------ .../containers/PubSubEmulatorContainerTest.java | 7 +++---- .../containers/SpannerEmulatorContainerTest.java | 9 ++++----- .../gcloud/src/test/resources/logback-test.xml | 16 ++++++++++++++++ 9 files changed, 76 insertions(+), 20 deletions(-) create mode 100644 modules/gcloud/src/test/resources/logback-test.xml diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java index 85b07b94591..fe0a9959f2d 100644 --- a/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java @@ -15,15 +15,24 @@ public class DatastoreEmulatorContainer extends GenericContainerhost:port pair corresponding to the address on which the emulator is + * reachable from the test host machine. Directly usable as a parameter to the + * com.google.cloud.ServiceOptions.Builder#setHost(java.lang.String) method. + */ + public String getEmulatorEndpoint() { + return getContainerIpAddress() + ":" + getMappedPort(8081); + } } diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/FirestoreEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/FirestoreEmulatorContainer.java index 41442f0f531..6353a679130 100644 --- a/modules/gcloud/src/main/java/org/testcontainers/containers/FirestoreEmulatorContainer.java +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/FirestoreEmulatorContainer.java @@ -15,16 +15,25 @@ public class FirestoreEmulatorContainer extends GenericContainerhost:port pair corresponding to the address on which the emulator is + * reachable from the test host machine. Directly usable as a parameter to the + * com.google.cloud.ServiceOptions.Builder#setHost(java.lang.String) method. + */ + public String getEmulatorEndpoint() { + return getContainerIpAddress() + ":" + getMappedPort(8080); + } } diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/PubSubEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/PubSubEmulatorContainer.java index c92145b3d3a..90d77c837cf 100644 --- a/modules/gcloud/src/main/java/org/testcontainers/containers/PubSubEmulatorContainer.java +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/PubSubEmulatorContainer.java @@ -15,6 +15,7 @@ public class PubSubEmulatorContainer extends GenericContainerhost:port pair corresponding to the address on which the emulator is + * reachable from the test host machine. Directly usable as a parameter to the + * io.grpc.ManagedChannelBuilder#forTarget(java.lang.String) method. + */ + public String getEmulatorEndpoint() { + return getContainerIpAddress() + ":" + getMappedPort(PORT); + } } diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/SpannerEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/SpannerEmulatorContainer.java index 5f2a203d911..8603b1f3437 100644 --- a/modules/gcloud/src/main/java/org/testcontainers/containers/SpannerEmulatorContainer.java +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/SpannerEmulatorContainer.java @@ -25,4 +25,20 @@ public SpannerEmulatorContainer(final DockerImageName dockerImageName) { .withRegEx(".*Cloud Spanner emulator running\\..*")); } + /** + * @return a host:port pair corresponding to the address on which the emulator's + * gRPC endpoint is reachable from the test host machine. Directly usable as a parameter to the + * com.google.cloud.spanner.SpannerOptions.Builder#setEmulatorHost(java.lang.String) method. + */ + public String getEmulatorGrpcEndpoint() { + return getContainerIpAddress() + ":" + getMappedPort(GRPC_PORT); + } + + /** + * @return a host:port pair corresponding to the address on which the emulator's + * HTTP REST endpoint is reachable from the test host machine. + */ + public String getEmulatorHttpEndpoint() { + return getContainerIpAddress() + ":" + getMappedPort(HTTP_PORT); + } } diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java index f8285e4c691..4709ed1bbd6 100644 --- a/modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java @@ -1,5 +1,7 @@ package org.testcontainers.containers; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.cloud.NoCredentials; import com.google.cloud.ServiceOptions; import com.google.cloud.datastore.Datastore; @@ -10,8 +12,6 @@ import org.junit.Test; import org.testcontainers.utility.DockerImageName; -import static org.assertj.core.api.Assertions.assertThat; - public class DatastoreEmulatorContainerTest { @Rule @@ -25,7 +25,7 @@ public class DatastoreEmulatorContainerTest { @Test public void testSimple() { DatastoreOptions options = DatastoreOptions.newBuilder() - .setHost(emulator.getContainerIpAddress() + ":" + emulator.getMappedPort(8081)) + .setHost(emulator.getEmulatorEndpoint()) .setCredentials(NoCredentials.getInstance()) .setRetrySettings(ServiceOptions.getNoRetrySettings()) .setProjectId("test-project") diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java index 2f23ff40483..766ce657bf0 100644 --- a/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java @@ -1,8 +1,6 @@ package org.testcontainers.containers; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ExecutionException; +import static org.assertj.core.api.Assertions.assertThat; import com.google.api.core.ApiFuture; import com.google.cloud.NoCredentials; @@ -12,12 +10,13 @@ import com.google.cloud.firestore.FirestoreOptions; import com.google.cloud.firestore.QuerySnapshot; import com.google.cloud.firestore.WriteResult; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutionException; import org.junit.Rule; import org.junit.Test; import org.testcontainers.utility.DockerImageName; -import static org.assertj.core.api.Assertions.assertThat; - public class FirestoreEmulatorContainerTest { @Rule @@ -26,7 +25,7 @@ public class FirestoreEmulatorContainerTest { @Test public void testSimple() throws ExecutionException, InterruptedException { FirestoreOptions options = FirestoreOptions.getDefaultInstance().toBuilder() - .setHost(emulator.getContainerIpAddress() + ":" + emulator.getMappedPort(8080)) + .setHost(emulator.getEmulatorEndpoint()) .setCredentials(NoCredentials.getInstance()) .setProjectId("test-project") .build(); diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java index 0c5ec888de5..2a0acaa14e1 100644 --- a/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java @@ -1,6 +1,6 @@ package org.testcontainers.containers; -import java.io.IOException; +import static org.assertj.core.api.Assertions.assertThat; import com.google.api.gax.core.NoCredentialsProvider; import com.google.api.gax.grpc.GrpcTransportChannel; @@ -23,12 +23,11 @@ import com.google.pubsub.v1.TopicName; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; +import java.io.IOException; import org.junit.Rule; import org.junit.Test; import org.testcontainers.utility.DockerImageName; -import static org.assertj.core.api.Assertions.assertThat; - public class PubSubEmulatorContainerTest { public static final String PROJECT_ID = "my-project-id"; @@ -38,7 +37,7 @@ public class PubSubEmulatorContainerTest { @Test public void testSimple() throws IOException { - String hostport = emulator.getContainerIpAddress() + ":" + emulator.getMappedPort(8085); + String hostport = emulator.getEmulatorEndpoint(); ManagedChannel channel = ManagedChannelBuilder.forTarget(hostport).usePlaintext().build(); try { TransportChannelProvider channelProvider = diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java index 9093530236a..5a47f9429ad 100644 --- a/modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java @@ -1,7 +1,6 @@ package org.testcontainers.containers; -import java.util.Arrays; -import java.util.concurrent.ExecutionException; +import static org.assertj.core.api.Assertions.assertThat; import com.google.cloud.NoCredentials; import com.google.cloud.spanner.Database; @@ -17,12 +16,12 @@ import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.SpannerOptions; import com.google.cloud.spanner.Statement; +import java.util.Arrays; +import java.util.concurrent.ExecutionException; import org.junit.Rule; import org.junit.Test; import org.testcontainers.utility.DockerImageName; -import static org.assertj.core.api.Assertions.assertThat; - public class SpannerEmulatorContainerTest { @Rule @@ -35,7 +34,7 @@ public class SpannerEmulatorContainerTest { @Test public void testSimple() throws ExecutionException, InterruptedException { SpannerOptions options = SpannerOptions.newBuilder() - .setEmulatorHost(emulator.getContainerIpAddress() + ":" + emulator.getMappedPort(9010)) + .setEmulatorHost(emulator.getEmulatorGrpcEndpoint()) .setCredentials(NoCredentials.getInstance()) .setProjectId(PROJECT_NAME) .build(); diff --git a/modules/gcloud/src/test/resources/logback-test.xml b/modules/gcloud/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..535e406fc13 --- /dev/null +++ b/modules/gcloud/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + From 85f1e5e1352c95fb4f66b688c00e32fcf72546c1 Mon Sep 17 00:00:00 2001 From: David Pilato Date: Thu, 15 Oct 2020 21:34:44 +0200 Subject: [PATCH 1685/5615] Elasticsearch: Add test and documentation for secured cluster, bump default Elasticsearch version (deprecated constructor) from 6.4.1 to 7.9.2 (#2320) This can be now activated for free with the default basic license. Also change latest version to 7.9.2. --- docs/modules/elasticsearch.md | 13 ++- modules/elasticsearch/build.gradle | 2 +- .../elasticsearch/ElasticsearchContainer.java | 10 +- .../ElasticsearchContainerTest.java | 106 +++++++++++++++--- 4 files changed, 111 insertions(+), 20 deletions(-) diff --git a/docs/modules/elasticsearch.md b/docs/modules/elasticsearch.md index 9959d0b7926..f84a7840e4c 100644 --- a/docs/modules/elasticsearch.md +++ b/docs/modules/elasticsearch.md @@ -3,7 +3,7 @@ This module helps running [elasticsearch](https://www.elastic.co/products/elasticsearch) using Testcontainers. -Note that it's based on the [official Docker image](https://www.elastic.co/guide/en/elasticsearch/reference/6.3/docker.html) provided by elastic. +Note that it's based on the [official Docker image](https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html) provided by elastic. ## Usage example @@ -15,10 +15,19 @@ You can start an elasticsearch container instance from any Java application by u -Note that if you are still using the [TransportClient](https://www.elastic.co/guide/en/elasticsearch/client/java-api/6.3/transport-client.html) +Note that if you are still using the [TransportClient](https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/transport-client.html) (not recommended as it is deprecated), the default cluster name is set to `docker-cluster` so you need to change `cluster.name` setting or set `client.transport.ignore_cluster_name` to `true`. +## Secure your Elasticsearch cluster + +The default distribution of Elasticsearch comes with the basic license which contains security feature. +You can turn on security by providing some extra environment settings: + + +[HttpClient](../../modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java) inside_block:httpClientSecuredContainer + + ## Choose your Elasticsearch license If you prefer to start a Docker image with the pure OSS version (which means with no security in older versions or diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index e6de1daa7ab..126722759ec 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,5 +3,5 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.9.2" - testCompile "org.elasticsearch.client:transport:6.7.1" + testCompile "org.elasticsearch.client:transport:7.9.2" } diff --git a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java index 7ed660d38c6..e0880aa7849 100644 --- a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java +++ b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java @@ -23,7 +23,9 @@ public class ElasticsearchContainer extends GenericContainer basic + * Elasticsearch default username, when secured */ private static final String ELASTICSEARCH_USERNAME = "elastic"; /** - * Elasticsearch 5.x default password. In 6.x images, there's no security by default as shipped with a basic license. + * From 6.8, we can optionally activate security with a default password. */ private static final String ELASTICSEARCH_PASSWORD = "changeme"; private RestClient client = null; + private RestClient anonymousClient = null; @After public void stopRestClient() throws IOException { @@ -56,11 +55,16 @@ public void stopRestClient() throws IOException { client.close(); client = null; } + if (anonymousClient != null) { + anonymousClient.close(); + anonymousClient = null; + } } @SuppressWarnings("deprecation") // Using deprecated constructor for verification of backwards compatibility @Test - public void elasticsearchDefaultTest() throws IOException { + @Deprecated // We will remove this test in the future + public void elasticsearchDeprecatedCtorTest() throws IOException { // Create the elasticsearch container. try (ElasticsearchContainer container = new ElasticsearchContainer() .withEnv("foo", "bar") // dummy env for compiler checking correct generics usage @@ -71,7 +75,29 @@ public void elasticsearchDefaultTest() throws IOException { // Do whatever you want with the rest client ... Response response = getClient(container).performRequest(new Request("GET", "/")); assertThat(response.getStatusLine().getStatusCode(), is(200)); - assertThat(EntityUtils.toString(response.getEntity()), containsString(DEFAULT_TAG)); + assertThat(EntityUtils.toString(response.getEntity()), containsString(ELASTICSEARCH_VERSION)); + + // The default image is running with the features under Elastic License + response = getClient(container).performRequest(new Request("GET", "/_xpack/")); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + // For now we test that we have the monitoring feature available + assertThat(EntityUtils.toString(response.getEntity()), containsString("monitoring")); + } + } + + @Test + public void elasticsearchDefaultTest() throws IOException { + // Create the elasticsearch container. + try (ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE) + .withEnv("foo", "bar") // dummy env for compiler checking correct generics usage + ) { + // Start the container. This step might take some time... + container.start(); + + // Do whatever you want with the rest client ... + Response response = getClient(container).performRequest(new Request("GET", "/")); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + assertThat(EntityUtils.toString(response.getEntity()), containsString(ELASTICSEARCH_VERSION)); // The default image is running with the features under Elastic License response = getClient(container).performRequest(new Request("GET", "/_xpack/")); @@ -81,6 +107,26 @@ public void elasticsearchDefaultTest() throws IOException { } } + @Test + public void elasticsearchSecuredTest() throws IOException { + try (ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE) + .withEnv("ELASTIC_PASSWORD", ELASTICSEARCH_PASSWORD) + .withEnv("xpack.security.enabled", "true") + ) { + container.start(); + + // The cluster should be secured so it must fail when we try to access / without credentials + assertThrows("We should not be able to access / URI with an anonymous client.", + ResponseException.class, + () -> getAnonymousClient(container).performRequest(new Request("GET", "/"))); + + // But it should work when we try to access / with the proper login and password + Response response = getClient(container).performRequest(new Request("GET", "/")); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + assertThat(EntityUtils.toString(response.getEntity()), containsString(ELASTICSEARCH_VERSION)); + } + } + @Test public void elasticsearchVersion() throws IOException { try (ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE)) { @@ -113,12 +159,11 @@ public void elasticsearchOssImage() throws IOException { } } - @SuppressWarnings("deprecation") // Using deprecated constructor for verification of backwards compatibility @Test public void restClientClusterHealth() throws IOException { // httpClientContainer { // Create the elasticsearch container. - try (ElasticsearchContainer container = new ElasticsearchContainer()) { + try (ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE)) { // Start the container. This step might take some time... container.start(); @@ -140,15 +185,41 @@ public void restClientClusterHealth() throws IOException { // } } + @Test + public void restClientSecuredClusterHealth() throws IOException { + // httpClientSecuredContainer { + // Create the elasticsearch container. + try (ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE) + // With a password + .withEnv("ELASTIC_PASSWORD", ELASTICSEARCH_PASSWORD) + .withEnv("xpack.security.enabled", "true")) { + // Start the container. This step might take some time... + container.start(); + + // Create the secured client. + final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials(AuthScope.ANY, + new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ELASTICSEARCH_PASSWORD)); + + client = RestClient.builder(HttpHost.create(container.getHttpHostAddress())) + .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)) + .build(); + + Response response = client.performRequest(new Request("GET", "/_cluster/health")); + // }} + assertThat(response.getStatusLine().getStatusCode(), is(200)); + assertThat(EntityUtils.toString(response.getEntity()), containsString("cluster_name")); + // httpClientSecuredContainer {{ + } + // } + } + + @SuppressWarnings("deprecation") // The TransportClient will be removed in Elasticsearch 8. @Test public void transportClientClusterHealth() { // transportClientContainer { // Create the elasticsearch container. - try (ElasticsearchContainer container = new ElasticsearchContainer( - DockerImageName - .parse("docker.elastic.co/elasticsearch/elasticsearch") - .withTag("6.4.1") - )){ + try (ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE)){ // Start the container. This step might take some time... container.start(); @@ -182,4 +253,11 @@ private RestClient getClient(ElasticsearchContainer container) { return client; } + private RestClient getAnonymousClient(ElasticsearchContainer container) { + if (anonymousClient == null) { + anonymousClient = RestClient.builder(HttpHost.create(container.getHttpHostAddress())).build(); + } + + return anonymousClient; + } } From edb7f7a8e3233f9504f737102aa4180fe0949ec4 Mon Sep 17 00:00:00 2001 From: Kirill Artamonov Date: Thu, 15 Oct 2020 22:08:53 +0200 Subject: [PATCH 1686/5615] Update image references in Localstack module docs (#3076) Co-authored-by: Richard North --- docs/modules/localstack.md | 4 +++- .../containers/localstack/LocalstackContainerTest.java | 9 +++------ .../containers/localstack/LocalstackTestImages.java | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/modules/localstack.md b/docs/modules/localstack.md index 4ff46233b05..41fd2a50a52 100644 --- a/docs/modules/localstack.md +++ b/docs/modules/localstack.md @@ -7,8 +7,10 @@ Testcontainers module for the Atlassian's [LocalStack](https://github.com/locals Running LocalStack as a stand-in for AWS S3 during a test: ```java +DockerImageName localstackImage = DockerImageName.parse("localstack/localstack:0.11.3"); + @Rule -public LocalStackContainer localstack = new LocalStackContainer() +public LocalStackContainer localstack = new LocalStackContainer(localstackImage) .withServices(S3); @Test diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java index fad4060341c..af4d0694ab6 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java @@ -10,6 +10,7 @@ import static org.testcontainers.containers.localstack.LocalStackContainer.Service.KMS; import static org.testcontainers.containers.localstack.LocalStackContainer.Service.S3; import static org.testcontainers.containers.localstack.LocalStackContainer.Service.SQS; +import static org.testcontainers.containers.localstack.LocalstackTestImages.LOCALSTACK_IMAGE; import com.amazonaws.services.kms.AWSKMS; import com.amazonaws.services.kms.AWSKMSClientBuilder; @@ -64,9 +65,7 @@ public static class WithoutNetwork { // without_network { @ClassRule - public static LocalStackContainer localstack = new LocalStackContainer( - DockerImageName.parse("localstack/localstack:0.11.0") - ) + public static LocalStackContainer localstack = new LocalStackContainer(LOCALSTACK_IMAGE) .withServices(S3, SQS, CLOUDWATCHLOGS, KMS); // } @@ -179,9 +178,7 @@ public static class WithNetwork { private static Network network = Network.newNetwork(); @ClassRule - public static LocalStackContainer localstackInDockerNetwork = new LocalStackContainer( - DockerImageName.parse("localstack/localstack:0.11.0") - ) + public static LocalStackContainer localstackInDockerNetwork = new LocalStackContainer(LOCALSTACK_IMAGE) .withNetwork(network) .withNetworkAliases("notthis", "localstack") // the last alias is used for HOSTNAME_EXTERNAL .withServices(S3, SQS, CLOUDWATCHLOGS); diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackTestImages.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackTestImages.java index 60e5498daf3..0dd0ca9b9ac 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackTestImages.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackTestImages.java @@ -3,6 +3,6 @@ import org.testcontainers.utility.DockerImageName; public interface LocalstackTestImages { - DockerImageName LOCALSTACK_IMAGE = DockerImageName.parse("localstack/localstack:0.11.0"); + DockerImageName LOCALSTACK_IMAGE = DockerImageName.parse("localstack/localstack:0.11.3"); DockerImageName AWS_CLI_IMAGE = DockerImageName.parse("atlassian/pipelines-awscli:1.16.302"); } From 9d4b31d23d21bacf1f54791264c292e15df61d58 Mon Sep 17 00:00:00 2001 From: David Pilato Date: Fri, 16 Oct 2020 10:43:46 +0200 Subject: [PATCH 1687/5615] Fix `oos` -> `oss` in Elasticsearch's docs (#3349) --- docs/modules/elasticsearch.md | 2 +- .../elasticsearch/ElasticsearchContainerTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/modules/elasticsearch.md b/docs/modules/elasticsearch.md index f84a7840e4c..357ca782eaa 100644 --- a/docs/modules/elasticsearch.md +++ b/docs/modules/elasticsearch.md @@ -34,7 +34,7 @@ If you prefer to start a Docker image with the pure OSS version (which means wit other new and advanced features), you can use this instead: -[Elasticsearch OSS](../../modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java) inside_block:oosContainer +[Elasticsearch OSS](../../modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java) inside_block:ossContainer ## Adding this module to your project dependencies diff --git a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java index f86f1e8c104..3050ab4aedb 100644 --- a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java +++ b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java @@ -141,7 +141,7 @@ public void elasticsearchVersion() throws IOException { @Test public void elasticsearchOssImage() throws IOException { try (ElasticsearchContainer container = - // oosContainer { + // ossContainer { new ElasticsearchContainer( DockerImageName .parse("docker.elastic.co/elasticsearch/elasticsearch-oss") From 443307c21e4c4b20e7cee143b9bbd619f2319333 Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 21 Oct 2020 09:28:34 +0100 Subject: [PATCH 1688/5615] Upgrade mkdocs-codeinclude-plugin (#3354) Fixes indentation of ellipsis in within example code blocks --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 227c646dd6b..f962b36a44f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ mkdocs==1.0.4 -mkdocs-codeinclude-plugin==0.0.1 +mkdocs-codeinclude-plugin==0.1.0 mkdocs-material==4.6.0 mkdocs-markdownextradata-plugin==0.1.1 markdown>=3.1,<3.2 From ec43c31f06d2ec46014d3a2e63a2577e375008ec Mon Sep 17 00:00:00 2001 From: David Pilato Date: Wed, 21 Oct 2020 17:35:25 +0200 Subject: [PATCH 1689/5615] Elasticsearch: Add withPassword(String) method for secure access (#2321) --- docs/modules/elasticsearch.md | 2 +- .../elasticsearch/ElasticsearchContainer.java | 21 +++++++++++++++++++ .../ElasticsearchContainerTest.java | 20 +++++++++++++----- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/docs/modules/elasticsearch.md b/docs/modules/elasticsearch.md index 357ca782eaa..cd47c9c4ed3 100644 --- a/docs/modules/elasticsearch.md +++ b/docs/modules/elasticsearch.md @@ -22,7 +22,7 @@ or set `client.transport.ignore_cluster_name` to `true`. ## Secure your Elasticsearch cluster The default distribution of Elasticsearch comes with the basic license which contains security feature. -You can turn on security by providing some extra environment settings: +You can turn on security by providing a password: [HttpClient](../../modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java) inside_block:httpClientSecuredContainer diff --git a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java index e0880aa7849..acbca15aaf5 100644 --- a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java +++ b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java @@ -39,6 +39,7 @@ public class ElasticsearchContainer extends GenericContainer new ElasticsearchContainer( + DockerImageName + .parse("docker.elastic.co/elasticsearch/elasticsearch-oss") + .withTag(ELASTICSEARCH_VERSION)) + .withPassword("foo") + ); + } + private RestClient getClient(ElasticsearchContainer container) { if (client == null) { final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); From d3829ed535d3c92fa34dad49fd84ec05fd258630 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Oct 2020 19:13:09 +0100 Subject: [PATCH 1690/5615] Bump aws-java-sdk-dynamodb from 1.11.880 to 1.11.882 in /modules/dynalite (#3356) Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.11.880 to 1.11.882. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.880...1.11.882) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/dynalite/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index fe8fb883839..8aa9959ea13 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Dynalite" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.880' - testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.865' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.882' + testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.882' } From 769e202808439baa67bd47ce476144f48e29f772 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Oct 2020 19:13:29 +0100 Subject: [PATCH 1691/5615] Bump postgresql from 42.2.17 to 42.2.18 in /modules/postgresql (#3358) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.17 to 42.2.18. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/REL42.2.18/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.17...REL42.2.18) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index aa5db7028b5..b16ceaea2a8 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -11,7 +11,7 @@ dependencies { testCompile project(':jdbc-test') testCompile project(':test-support') - testCompile 'org.postgresql:postgresql:42.2.17' + testCompile 'org.postgresql:postgresql:42.2.18' testCompile testFixtures(project(':r2dbc')) testCompile 'io.r2dbc:r2dbc-postgresql:0.8.5.RELEASE' From 05691471fdb7fb61b90c2d4b286415492f2d7b57 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Oct 2020 19:14:25 +0100 Subject: [PATCH 1692/5615] Bump mysql-connector-java from 8.0.21 to 8.0.22 in /modules/mysql (#3357) Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.21 to 8.0.22. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 9df2e177d3f..da3dad9f2b3 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' testCompile project(':jdbc-test') - testCompile 'mysql:mysql-connector-java:8.0.21' + testCompile 'mysql:mysql-connector-java:8.0.22' testCompile testFixtures(project(':r2dbc')) testCompile 'dev.miku:r2dbc-mysql:0.8.1.RELEASE' From 1d80a407c0821d1c269a7ca0108f00a2703fc3a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Oct 2020 19:14:34 +0100 Subject: [PATCH 1693/5615] Bump guava from 29.0-jre to 30.0-jre in /modules/jdbc-test (#3359) Bumps [guava](https://github.com/google/guava) from 29.0-jre to 30.0-jre. - [Release notes](https://github.com/google/guava/releases) - [Commits](https://github.com/google/guava/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index ec57ddcea41..661cc1b4301 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -2,7 +2,7 @@ dependencies { compile project(':jdbc') compile project(':test-support') - compile 'com.google.guava:guava:29.0-jre' + compile 'com.google.guava:guava:30.0-jre' compile 'org.apache.commons:commons-lang3:3.11' compile 'com.zaxxer:HikariCP-java6:2.3.13' compile 'commons-dbutils:commons-dbutils:1.7' From a71887b5d86fdbd1b4e0a23e87c232395e44fb7e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Oct 2020 19:14:47 +0100 Subject: [PATCH 1694/5615] Bump mysql-connector-java from 8.0.21 to 8.0.22 in /modules/jdbc-test (#3360) Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.21 to 8.0.22. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 661cc1b4301..99841c1d62a 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -17,5 +17,5 @@ dependencies { compile 'commons-dbutils:commons-dbutils:1.7' compile 'org.apache.tomcat:tomcat-jdbc:9.0.39' compile 'org.vibur:vibur-dbcp:25.0' - compile 'mysql:mysql-connector-java:8.0.21' + compile 'mysql:mysql-connector-java:8.0.22' } From 8b10cee92c3e80ed2c833c41f037dc3cbc3a3aa3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Oct 2020 19:15:00 +0100 Subject: [PATCH 1695/5615] Bump mysql-connector-java from 8.0.21 to 8.0.22 in /modules/spock (#3363) Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.21 to 8.0.22. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 86724262a29..92e75e8cb86 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -16,7 +16,7 @@ dependencies { testCompile 'org.apache.httpcomponents:httpclient:4.5.13' testRuntime 'org.postgresql:postgresql:42.2.17' - testRuntime 'mysql:mysql-connector-java:8.0.21' + testRuntime 'mysql:mysql-connector-java:8.0.22' testCompileClasspath 'org.jetbrains:annotations:20.1.0' } From 2fb35460ba9324544e83a1990409b4dda616f6d8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Oct 2020 19:15:36 +0100 Subject: [PATCH 1696/5615] Bump mssql-jdbc from 8.4.1.jre8 to 9.1.0.jre8-preview in /modules/mssqlserver (#3364) Bumps [mssql-jdbc](https://github.com/Microsoft/mssql-jdbc) from 8.4.1.jre8 to 9.1.0.jre8-preview. - [Release notes](https://github.com/Microsoft/mssql-jdbc/releases) - [Changelog](https://github.com/microsoft/mssql-jdbc/blob/dev/CHANGELOG.md) - [Commits](https://github.com/Microsoft/mssql-jdbc/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/mssqlserver/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index f0170500e7d..9aab7844148 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -10,12 +10,12 @@ dependencies { compileOnly 'io.r2dbc:r2dbc-mssql:0.8.4.RELEASE' testCompile project(':jdbc-test') - testCompile 'com.microsoft.sqlserver:mssql-jdbc:8.4.1.jre8' + testCompile 'com.microsoft.sqlserver:mssql-jdbc:9.1.0.jre8-preview' testCompile project(':r2dbc') testCompile 'io.r2dbc:r2dbc-mssql:0.8.3.RELEASE' // MSSQL's wait strategy requires the JDBC driver testCompile testFixtures(project(':r2dbc')) - testCompile 'com.microsoft.sqlserver:mssql-jdbc:8.4.1.jre8' + testCompile 'com.microsoft.sqlserver:mssql-jdbc:9.1.0.jre8-preview' } From e7c007b03a1fd74d489d562f4c430bf3180a4af9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Oct 2020 19:15:43 +0100 Subject: [PATCH 1697/5615] Bump aws-java-sdk-s3 from 1.11.880 to 1.11.882 in /modules/localstack (#3365) Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.11.880 to 1.11.882. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.880...1.11.882) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 29abca90878..dcae987bbf4 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.880' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.882' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.880' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.880' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' From 6985c996daa85c01883cf83276ba193bd59eeb0c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Oct 2020 19:16:04 +0100 Subject: [PATCH 1698/5615] Bump lombok from 1.18.14 to 1.18.16 in /examples (#3368) Bumps [lombok](https://github.com/rzwitserloot/lombok) from 1.18.14 to 1.18.16. - [Release notes](https://github.com/rzwitserloot/lombok/releases) - [Changelog](https://github.com/rzwitserloot/lombok/blob/master/doc/changelog.markdown) - [Commits](https://github.com/rzwitserloot/lombok/compare/v1.18.14...v1.18.16) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/disque-job-queue/build.gradle | 2 +- examples/solr-container/build.gradle | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index c5e191f4f94..c31cb39b7fe 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly "org.projectlombok:lombok:1.18.14" + compileOnly "org.projectlombok:lombok:1.18.16" annotationProcessor "org.projectlombok:lombok:1.18.14" implementation 'biz.paluch.redis:spinach:0.3' implementation 'com.google.code.gson:gson:2.8.6' diff --git a/examples/solr-container/build.gradle b/examples/solr-container/build.gradle index 5a8973cab27..75524364100 100644 --- a/examples/solr-container/build.gradle +++ b/examples/solr-container/build.gradle @@ -7,8 +7,8 @@ repositories { } dependencies { - compileOnly "org.projectlombok:lombok:1.18.14" - annotationProcessor "org.projectlombok:lombok:1.18.10" + compileOnly "org.projectlombok:lombok:1.18.16" + annotationProcessor "org.projectlombok:lombok:1.18.16" implementation 'org.apache.solr:solr-solrj:8.6.3' From eeecf94f7c42b628599de13bbc5dbeaa5296f6ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Oct 2020 19:16:25 +0100 Subject: [PATCH 1699/5615] Bump s3 from 2.15.7 to 2.15.9 in /modules/localstack (#3361) Bumps s3 from 2.15.7 to 2.15.9. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index dcae987bbf4..ee02504309f 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,5 +7,5 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.880' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.880' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' - testCompile 'software.amazon.awssdk:s3:2.15.7' + testCompile 'software.amazon.awssdk:s3:2.15.9' } From 153b3baf2dce22d706c76d679ba4220cf87a1e55 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 22 Oct 2020 14:22:00 +0100 Subject: [PATCH 1700/5615] Update version to 1.15.0-rc2 --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index dfff99bb302..0fa9b2b4c91 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -92,4 +92,4 @@ nav: - contributing_docs.md - bounty.md extra: - latest_version: 1.14.3 + latest_version: 1.15.0-rc2 From c110086287f96a408c5fbfb589be72d67b033c57 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 22 Oct 2020 17:31:23 +0100 Subject: [PATCH 1701/5615] Change image used for test to avoid image name cache preventing proper pull (#3378) --- .../dockerclient/AmbiguousImagePullTest.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/core/src/test/java/org/testcontainers/dockerclient/AmbiguousImagePullTest.java b/core/src/test/java/org/testcontainers/dockerclient/AmbiguousImagePullTest.java index 5d2c42ed1b2..9e62148c3bb 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/AmbiguousImagePullTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/AmbiguousImagePullTest.java @@ -5,7 +5,6 @@ import org.junit.Test; import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import org.testcontainers.utility.DockerImageName; import java.util.List; @@ -16,16 +15,15 @@ public class AmbiguousImagePullTest { public void testNotUsingParse() { DockerClient client = DockerClientFactory.instance().client(); List alpineImages = client.listImagesCmd() - .withImageNameFilter("alpine:latest") + .withImageNameFilter("testcontainers/helloworld:latest") .exec(); for (Image alpineImage : alpineImages) { client.removeImageCmd(alpineImage.getId()).exec(); } try ( - final GenericContainer container = new GenericContainer<>(DockerImageName.parse("alpine")) - .withCommand("/bin/sh", "-c", "sleep 0") - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + final GenericContainer container = new GenericContainer<>(DockerImageName.parse("testcontainers/helloworld")) + .withExposedPorts(8080) ) { container.start(); // do nothing other than start and stop From bd86ca421482b66df6828dd29ee214bbfb9f0e67 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 24 Oct 2020 15:47:49 +0100 Subject: [PATCH 1702/5615] Bump postgresql from 42.2.17 to 42.2.18 in /modules/spock (#3362) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.17 to 42.2.18. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/REL42.2.18/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.17...REL42.2.18) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 92e75e8cb86..a095bcb8a80 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -15,7 +15,7 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.4.5' testCompile 'org.apache.httpcomponents:httpclient:4.5.13' - testRuntime 'org.postgresql:postgresql:42.2.17' + testRuntime 'org.postgresql:postgresql:42.2.18' testRuntime 'mysql:mysql-connector-java:8.0.22' testCompileClasspath 'org.jetbrains:annotations:20.1.0' From 4e1db24e195ff1c8fdf8f8cb3bf1e0d152d97b34 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 24 Oct 2020 15:48:02 +0100 Subject: [PATCH 1703/5615] Bump postgresql from 42.2.17 to 42.2.18 in /examples (#3367) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.17 to 42.2.18. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/REL42.2.18/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.17...REL42.2.18) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 5c4c473becb..f5f21a57d21 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' implementation 'com.squareup.okhttp3:okhttp:4.9.0' implementation 'org.json:json:20180813' - testImplementation 'org.postgresql:postgresql:42.2.17' + testImplementation 'org.postgresql:postgresql:42.2.18' testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.testcontainers:postgresql' } From d20619cfec8cb55a86ed2cb6b4de14dd3a0c16cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 24 Oct 2020 15:48:10 +0100 Subject: [PATCH 1704/5615] Bump mysql-connector-java from 8.0.21 to 8.0.22 in /modules/junit-jupiter (#3370) Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.21 to 8.0.22. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 28f6049faf4..d85fc9fa85c 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -16,7 +16,7 @@ dependencies { testCompile 'org.junit.jupiter:junit-jupiter-params:5.7.0' testRuntime 'org.postgresql:postgresql:42.2.17' - testRuntime 'mysql:mysql-connector-java:8.0.21' + testRuntime 'mysql:mysql-connector-java:8.0.22' testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.7.0' } From 9353569be58e8bf0d35b2838eebc68e75b07ca43 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 24 Oct 2020 15:48:21 +0100 Subject: [PATCH 1705/5615] Bump guava from 29.0-jre to 30.0-jre in /core (#3371) Bumps [guava](https://github.com/google/guava) from 29.0-jre to 30.0-jre. - [Release notes](https://github.com/google/guava/releases) - [Commits](https://github.com/google/guava/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index e043313ac38..723b14fd0e6 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -5,7 +5,7 @@ buildscript { dependencies { // https://github.com/melix/japicmp-gradle-plugin/issues/36 - classpath 'com.google.guava:guava:29.0-jre' + classpath 'com.google.guava:guava:30.0-jre' } } From b4ac0f39748c9cf45998352f404ae2a0d266d855 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 24 Oct 2020 19:14:37 +0100 Subject: [PATCH 1706/5615] Bump aws-java-sdk-sqs from 1.11.880 to 1.11.884 in /modules/localstack (#3377) Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.11.880 to 1.11.884. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.880...1.11.884) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index ee02504309f..418344b82f2 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.882' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.880' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.880' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.884' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' testCompile 'software.amazon.awssdk:s3:2.15.9' } From 1b4a2a6356cb12796b57816e9f90a23e7f5a2a57 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 24 Oct 2020 19:14:49 +0100 Subject: [PATCH 1707/5615] Bump postgresql from 42.2.17 to 42.2.18 in /modules/junit-jupiter (#3369) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.17 to 42.2.18. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/REL42.2.18/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.17...REL42.2.18) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index d85fc9fa85c..61594d9842b 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -15,7 +15,7 @@ dependencies { testCompile 'org.assertj:assertj-core:3.17.2' testCompile 'org.junit.jupiter:junit-jupiter-params:5.7.0' - testRuntime 'org.postgresql:postgresql:42.2.17' + testRuntime 'org.postgresql:postgresql:42.2.18' testRuntime 'mysql:mysql-connector-java:8.0.22' testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.7.0' } From f4fb79b002c25d71ab7a64d90860058b0f24e672 Mon Sep 17 00:00:00 2001 From: codefiddler Date: Thu, 29 Oct 2020 13:21:49 +0100 Subject: [PATCH 1708/5615] Remove potential NullPointerException when trying to log an error in VNC recorder (#3408) --- .../testcontainers/containers/BrowserWebDriverContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index d60d9191748..884cd33a9dd 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -171,7 +171,7 @@ protected void configure() { vncRecordingDirectory = Files.createTempDirectory(TC_TEMP_DIR_PREFIX).toFile(); } catch (IOException e) { // should never happen as per javadoc, since we use valid prefix - logger().error("Exception while trying to create temp directory " + vncRecordingDirectory.getAbsolutePath(), e); + logger().error("Exception while trying to create temp directory", e); throw new ContainerLaunchException("Exception while trying to create temp directory", e); } } From ed6d94f392c4beb23cfa2bd7924376ee7d80d446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Thu, 29 Oct 2020 06:23:07 -0600 Subject: [PATCH 1709/5615] Update tests to use emulators image (#3406) `Google Cloud SDK` released a new image which has just emulators installed. --- .../containers/DatastoreEmulatorContainerTest.java | 2 +- .../containers/FirestoreEmulatorContainerTest.java | 2 +- .../testcontainers/containers/PubSubEmulatorContainerTest.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java index 4709ed1bbd6..f7b35936197 100644 --- a/modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java @@ -17,7 +17,7 @@ public class DatastoreEmulatorContainerTest { @Rule // creatingDatastoreEmulatorContainer { public DatastoreEmulatorContainer emulator = new DatastoreEmulatorContainer( - DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:313.0.0") + DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:316.0.0-emulators") ); // } diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java index 766ce657bf0..4003ebdd342 100644 --- a/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java @@ -20,7 +20,7 @@ public class FirestoreEmulatorContainerTest { @Rule - public FirestoreEmulatorContainer emulator = new FirestoreEmulatorContainer(DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:313.0.0")); + public FirestoreEmulatorContainer emulator = new FirestoreEmulatorContainer(DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:316.0.0-emulators")); @Test public void testSimple() throws ExecutionException, InterruptedException { diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java index 2a0acaa14e1..3ea9f831d8e 100644 --- a/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java @@ -33,7 +33,7 @@ public class PubSubEmulatorContainerTest { public static final String PROJECT_ID = "my-project-id"; @Rule - public PubSubEmulatorContainer emulator = new PubSubEmulatorContainer(DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:313.0.0")); + public PubSubEmulatorContainer emulator = new PubSubEmulatorContainer(DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:316.0.0-emulators")); @Test public void testSimple() throws IOException { From a4ea01764c9b3792911dcf6cade3612969a9baeb Mon Sep 17 00:00:00 2001 From: Kirill Artamonov Date: Thu, 29 Oct 2020 13:26:56 +0100 Subject: [PATCH 1710/5615] Latest kafka container 5.4.3 (#3402) --- docs/modules/kafka.md | 15 +++------------ .../testcontainers/containers/KafkaContainer.java | 2 +- .../containers/KafkaContainerTest.java | 8 ++++---- 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/docs/modules/kafka.md b/docs/modules/kafka.md index d36273ac8e1..b74d4ef49bd 100644 --- a/docs/modules/kafka.md +++ b/docs/modules/kafka.md @@ -14,25 +14,16 @@ The following field in your JUnit test class will prepare a container running Ka [JUnit Rule](../../modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java) inside_block:junitRule - + +The correspondence between Confluent Platform versions and Kafka versions can be seen [in Confluent documentation](https://docs.confluent.io/current/installation/versions-interoperability.html#cp-and-apache-kafka-compatibility) + Now your tests or any other process running on your machine can get access to running Kafka broker by using the following bootstrap server location: [Bootstrap Servers](../../modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java) inside_block:getBootstrapServers - ## Options - -### Selecting Kafka version - -You can select a specific Confluent Platform Kafka docker image by passing it to the container's constructor: - -[Version Constructor](../../modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java) inside_block:constructorWithVersion - - - -The correspondence between Confluent Platform versions and Kafka versions can be seen [in Confluent documentation](https://docs.confluent.io/current/installation/versions-interoperability.html#cp-and-apache-kafka-compatibility) ### Using external Zookeeper diff --git a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java index ae44e4803a9..37f99bf970b 100644 --- a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java +++ b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java @@ -17,7 +17,7 @@ public class KafkaContainer extends GenericContainer { private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("confluentinc/cp-kafka"); - private static final String DEFAULT_TAG = "5.2.1"; + private static final String DEFAULT_TAG = "5.4.3"; private static final String STARTER_SCRIPT = "/testcontainers_start.sh"; diff --git a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java index 08b9fb6f856..4f721424bb6 100644 --- a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java +++ b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java @@ -24,12 +24,12 @@ public class KafkaContainerTest { - private static final DockerImageName KAFKA_TEST_IMAGE = DockerImageName.parse("confluentinc/cp-kafka:5.2.1"); + private static final DockerImageName KAFKA_TEST_IMAGE = DockerImageName.parse("confluentinc/cp-kafka:5.4.3"); private static final DockerImageName ZOOKEEPER_TEST_IMAGE = DockerImageName.parse("confluentinc/cp-zookeeper:4.0.0"); // junitRule { @ClassRule - public static KafkaContainer kafka = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:5.2.1")); + public static KafkaContainer kafka = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:5.4.3")); // } @Test @@ -45,7 +45,7 @@ public void testUsage() throws Exception { public void testUsageWithSpecificImage() throws Exception { try ( // constructorWithVersion { - KafkaContainer kafka = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:5.2.1")) + KafkaContainer kafka = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:5.4.3")) // } ) { kafka.start(); @@ -61,7 +61,7 @@ public void testUsageWithSpecificImage() throws Exception { @Test public void testUsageWithVersion() throws Exception { try ( - KafkaContainer kafka = new KafkaContainer("5.2.1") + KafkaContainer kafka = new KafkaContainer("5.4.3") ) { kafka.start(); testKafkaFunctionality(kafka.getBootstrapServers()); From e97fd3d0251e9f054052cd30009db6122c979210 Mon Sep 17 00:00:00 2001 From: DevilzOwn Date: Thu, 29 Oct 2020 17:58:47 +0530 Subject: [PATCH 1711/5615] Reduce severity of warning for failed auth config lookups (fixes #1399) (#3353) Co-authored-by: Prateek Bokade Co-authored-by: Richard North --- .../java/org/testcontainers/utility/RegistryAuthLocator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java index 32b49bd8172..846b62f1f25 100644 --- a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java +++ b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java @@ -145,7 +145,7 @@ private Optional lookupUncachedAuthConfig(String registryName, Docke return Optional.of(existingAuthConfig); } } catch (Exception e) { - log.warn("Failure when attempting to lookup auth config (dockerImageName: {}, configFile: {}. Falling back to docker-java default behaviour. Exception message: {}", + log.info("Failure when attempting to lookup auth config. Please ignore if you don't have images in an authenticated registry. Details: (dockerImageName: {}, configFile: {}. Falling back to docker-java default behaviour. Exception message: {}", dockerImageName, configFile, e.getMessage()); From 18fec1dcf5ba43298cdc344bc70293c91f2af28f Mon Sep 17 00:00:00 2001 From: ae-govau Date: Thu, 29 Oct 2020 23:29:42 +1100 Subject: [PATCH 1712/5615] Correct documentation for ryuk image (#3383) Commit 7ba06573feb6561c6fc857cd5b53ff1449808da9 changed the default values in code, this commit updates the documentation to match. --- docs/features/configuration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/features/configuration.md b/docs/features/configuration.md index 8b12f8e8d54..e3ac0fb6ac3 100644 --- a/docs/features/configuration.md +++ b/docs/features/configuration.md @@ -32,7 +32,7 @@ Some companies disallow the usage of Docker Hub, but you can override `*.image` > **tinyimage.container.image = alpine:3.5** > Used by Testcontainers' core -> **vncrecorder.container.image = testcontainersofficial/vnc-recorder:1.1.0** +> **vncrecorder.container.image = testcontainers/vnc-recorder:1.1.0** > Used by VNC recorder in Testcontainers' Seleniun integration > **ambassador.container.image = richnorth/ambassador:latest** @@ -53,7 +53,7 @@ For more information, see the [official Docker documentation about "Registry as ## Customizing Ryuk resource reaper -> **ryuk.container.image = testcontainersofficial/ryuk:0.3.0** +> **ryuk.container.image = testcontainers/ryuk:0.3.0** > The resource reaper is responsible for container removal and automatic cleanup of dead containers at JVM shutdown > **ryuk.container.privileged = false** From 4a529f6aaeb1ec9f59068d70f45b7e558dabcbe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20=C5=81ozi=C5=84ski?= Date: Thu, 29 Oct 2020 13:31:55 +0100 Subject: [PATCH 1713/5615] Fix use of HostPortWaitStrategy with Postgres container (fixes #3382) (#3403) --- .../org/testcontainers/containers/PostgreSQLContainer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index 5a4abe6d0ee..6c16bb8fe61 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -5,10 +5,10 @@ import org.testcontainers.utility.DockerImageName; import java.time.Duration; -import java.util.HashSet; import java.util.Set; import static java.time.temporal.ChronoUnit.SECONDS; +import static java.util.Collections.singleton; /** * @author richardnorth @@ -63,7 +63,7 @@ public PostgreSQLContainer(final DockerImageName dockerImageName) { @NotNull @Override protected Set getLivenessCheckPorts() { - return new HashSet<>(getMappedPort(POSTGRESQL_PORT)); + return singleton(getMappedPort(POSTGRESQL_PORT)); } @Override From ca74b044e222e725c72083a1d5d7d70d17a04ecf Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 29 Oct 2020 13:12:50 +0000 Subject: [PATCH 1714/5615] Refactor TestcontainersConfiguration to allow config by env var (#3411) --- .../utility/TestcontainersConfiguration.java | 252 +++++++++++++----- .../TestcontainersConfigurationTest.java | 86 ++++-- 2 files changed, 258 insertions(+), 80 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 5b89140fd61..b3c8e88a328 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -1,15 +1,17 @@ package org.testcontainers.utility; import com.google.common.annotations.VisibleForTesting; -import lombok.AccessLevel; +import com.google.common.collect.ImmutableMap; import lombok.Data; import lombok.Getter; import lombok.NonNull; -import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.Synchronized; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.exception.ExceptionUtils; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.testcontainers.UnstableAPI; import java.io.File; @@ -20,22 +22,57 @@ import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URL; +import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Properties; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Stream; /** - * Provides a mechanism for fetching configuration/defaults from the classpath. + * Provides a mechanism for fetching configuration/default settings. + *

+ * Configuration may be provided in: + *

    + *
  • A file in the user's home directory named .testcontainers.properties
  • + *
  • A file in the classpath named testcontainers.properties
  • + *
  • Environment variables
  • + *
+ *

+ * Note that, if using environment variables, property names are in upper case separated by underscores, preceded by + * TESTCONTAINERS_. */ @Data @Slf4j -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) public class TestcontainersConfiguration { private static String PROPERTIES_FILE_NAME = "testcontainers.properties"; - private static File ENVIRONMENT_CONFIG_FILE = new File(System.getProperty("user.home"), "." + PROPERTIES_FILE_NAME); + private static File USER_CONFIG_FILE = new File(System.getProperty("user.home"), "." + PROPERTIES_FILE_NAME); + + private static final String AMBASSADOR_IMAGE = "richnorth/ambassador"; + private static final String SOCAT_IMAGE = "alpine/socat"; + private static final String VNC_RECORDER_IMAGE = "testcontainers/vnc-recorder"; + private static final String COMPOSE_IMAGE = "docker/compose"; + private static final String ALPINE_IMAGE = "alpine"; + private static final String RYUK_IMAGE = "testcontainers/ryuk"; + private static final String KAFKA_IMAGE = "confluentinc/cp-kafka"; + private static final String PULSAR_IMAGE = "apachepulsar/pulsar"; + private static final String LOCALSTACK_IMAGE = "localstack/localstack"; + private static final String SSHD_IMAGE = "testcontainers/sshd"; + + private static final ImmutableMap CONTAINER_MAPPING = ImmutableMap.builder() + .put(DockerImageName.parse(AMBASSADOR_IMAGE), "ambassador.container.image") + .put(DockerImageName.parse(SOCAT_IMAGE), "socat.container.image") + .put(DockerImageName.parse(VNC_RECORDER_IMAGE), "vncrecorder.container.image") + .put(DockerImageName.parse(COMPOSE_IMAGE), "compose.container.image") + .put(DockerImageName.parse(ALPINE_IMAGE), "tinyimage.container.image") + .put(DockerImageName.parse(RYUK_IMAGE), "ryuk.container.image") + .put(DockerImageName.parse(KAFKA_IMAGE), "kafka.container.image") + .put(DockerImageName.parse(PULSAR_IMAGE), "pulsar.container.image") + .put(DockerImageName.parse(LOCALSTACK_IMAGE), "localstack.container.image") + .put(DockerImageName.parse(SSHD_IMAGE), "sshd.container.image") + .build(); @Getter(lazy = true) private static final TestcontainersConfiguration instance = loadConfiguration(); @@ -47,163 +84,234 @@ static AtomicReference getInstanceField() { return (AtomicReference) (Object) instance; } - @Getter(AccessLevel.NONE) - private final Properties environmentProperties; + private final Properties userProperties; + private final Properties classpathProperties; + private final Map environment; - private final Properties properties = new Properties(); - - TestcontainersConfiguration(Properties environmentProperties, Properties classpathProperties) { - this.environmentProperties = environmentProperties; - - this.properties.putAll(classpathProperties); - this.properties.putAll(environmentProperties); - } - - private DockerImageName getImage(final String key, final String defaultValue) { - return DockerImageName - .parse(properties.getProperty(key, defaultValue).trim()) - .asCompatibleSubstituteFor(defaultValue); + TestcontainersConfiguration(Properties userProperties, Properties classpathProperties, final Map environment) { + this.userProperties = userProperties; + this.classpathProperties = classpathProperties; + this.environment = environment; } @Deprecated public String getAmbassadorContainerImage() { - return getAmbassadorContainerDockerImageName().asCanonicalNameString(); - } - - @Deprecated - public DockerImageName getAmbassadorContainerDockerImageName() { - return getImage("ambassador.container.image", "richnorth/ambassador:latest"); + return getImage(AMBASSADOR_IMAGE).asCanonicalNameString(); } @Deprecated public String getSocatContainerImage() { - return getSocatDockerImageName().asCanonicalNameString(); + return getImage(SOCAT_IMAGE).asCanonicalNameString(); } public DockerImageName getSocatDockerImageName() { - return getImage("socat.container.image", "alpine/socat:latest"); + return getImage(SOCAT_IMAGE); } @Deprecated public String getVncRecordedContainerImage() { - return getVncDockerImageName().asCanonicalNameString(); + return getImage(VNC_RECORDER_IMAGE).asCanonicalNameString(); } public DockerImageName getVncDockerImageName() { - return getImage("vncrecorder.container.image", "testcontainers/vnc-recorder:1.1.0"); + return getImage(VNC_RECORDER_IMAGE); } @Deprecated public String getDockerComposeContainerImage() { - return getDockerComposeDockerImageName().asCanonicalNameString(); + return getImage(COMPOSE_IMAGE).asCanonicalNameString(); } public DockerImageName getDockerComposeDockerImageName() { - return getImage("compose.container.image", "docker/compose:1.24.1"); + return getImage(COMPOSE_IMAGE); } @Deprecated public String getTinyImage() { - return getTinyDockerImageName().asCanonicalNameString(); + return getImage(ALPINE_IMAGE).asCanonicalNameString(); } public DockerImageName getTinyDockerImageName() { - return getImage("tinyimage.container.image", "alpine:3.5"); + return getImage(ALPINE_IMAGE); } public boolean isRyukPrivileged() { - return Boolean.parseBoolean((String) properties.getOrDefault("ryuk.container.privileged", "false")); + return Boolean + .parseBoolean(getEnvVarOrProperty("ryuk.container.privileged", "false")); } @Deprecated public String getRyukImage() { - return getRyukDockerImageName().asCanonicalNameString(); + return getImage(RYUK_IMAGE).asCanonicalNameString(); } public DockerImageName getRyukDockerImageName() { - return getImage("ryuk.container.image", "testcontainers/ryuk:0.3.0"); + return getImage(RYUK_IMAGE); } @Deprecated public String getSSHdImage() { - return getSSHdDockerImageName().asCanonicalNameString(); + return getImage(SSHD_IMAGE).asCanonicalNameString(); } public DockerImageName getSSHdDockerImageName() { - return getImage("sshd.container.image", "testcontainers/sshd:1.0.0"); + return getImage(SSHD_IMAGE); } public Integer getRyukTimeout() { - return Integer.parseInt((String) properties.getOrDefault("ryuk.container.timeout", "30")); + return Integer.parseInt(getEnvVarOrProperty("ryuk.container.timeout", "30")); } @Deprecated public String getKafkaImage() { - return getKafkaDockerImageName().asCanonicalNameString(); + return getImage(KAFKA_IMAGE).asCanonicalNameString(); } public DockerImageName getKafkaDockerImageName() { - return getImage("kafka.container.image", "confluentinc/cp-kafka"); + return getImage(KAFKA_IMAGE); + } + + + @Deprecated + public String getOracleImage() { + return getEnvVarOrUserProperty("oracle.container.image", null); } @Deprecated public String getPulsarImage() { - return getPulsarDockerImageName().asCanonicalNameString(); + return getImage(PULSAR_IMAGE).asCanonicalNameString(); } public DockerImageName getPulsarDockerImageName() { - return getImage("pulsar.container.image", "apachepulsar/pulsar"); + return getImage(PULSAR_IMAGE); } @Deprecated public String getLocalStackImage() { - return getLocalstackDockerImageName().asCanonicalNameString(); + return getImage(LOCALSTACK_IMAGE).asCanonicalNameString(); } public DockerImageName getLocalstackDockerImageName() { - return getImage("localstack.container.image", "localstack/localstack"); + return getImage(LOCALSTACK_IMAGE); } + public boolean isDisableChecks() { - return Boolean.parseBoolean((String) environmentProperties.getOrDefault("checks.disable", "false")); + return Boolean.parseBoolean(getEnvVarOrUserProperty("checks.disable", "false")); } @UnstableAPI public boolean environmentSupportsReuse() { - return Boolean.parseBoolean((String) environmentProperties.getOrDefault("testcontainers.reuse.enable", "false")); + // specifically not supported as an environment variable or classpath property + return Boolean.parseBoolean(getEnvVarOrUserProperty("testcontainers.reuse.enable", "false")); } public String getDockerClientStrategyClassName() { - return (String) environmentProperties.get("docker.client.strategy"); + return getEnvVarOrUserProperty("docker.client.strategy", null); } public String getTransportType() { - return properties.getProperty("transport.type", "okhttp"); + return getEnvVarOrProperty("transport.type", "okhttp"); } public Integer getImagePullPauseTimeout() { - return Integer.parseInt((String) properties.getOrDefault("pull.pause.timeout", "30")); + return Integer.parseInt(getEnvVarOrProperty("pull.pause.timeout", "30")); } - @Synchronized + @Nullable + @Contract("_, !null, _ -> !null") + private String getConfigurable(@NotNull final String propertyName, @Nullable final String defaultValue, Properties... propertiesSources) { + String envVarName = propertyName.replaceAll("\\.", "_").toUpperCase(); + if (!envVarName.startsWith("TESTCONTAINERS_")) { + envVarName = "TESTCONTAINERS_" + envVarName; + } + + if (environment.containsKey(envVarName)) { + return environment.get(envVarName); + } + + for (final Properties properties : propertiesSources) { + if (properties.get(propertyName) != null) { + return (String) properties.get(propertyName); + } + } + + return defaultValue; + } + + /** + * Gets a configured setting from an environment variable (if present) or a configuration file property otherwise. + * The configuration file will be the .testcontainers.properties file in the user's home directory or + * a testcontainers.properties found on the classpath. + * + * @param propertyName name of configuration file property (dot-separated lower case) + * @return the found value, or null if not set + */ + @Nullable + @Contract("_, !null -> !null") + public String getEnvVarOrProperty(@NotNull final String propertyName, @Nullable final String defaultValue) { + return getConfigurable(propertyName, defaultValue, userProperties, classpathProperties); + } + + /** + * Gets a configured setting from an environment variable (if present) or a configuration file property otherwise. + * The configuration file will be the .testcontainers.properties file in the user's home directory. + * + * @param propertyName name of configuration file property (dot-separated lower case) + * @return the found value, or null if not set + */ + @Nullable + @Contract("_, !null -> !null") + public String getEnvVarOrUserProperty(@NotNull final String propertyName, @Nullable final String defaultValue) { + return getConfigurable(propertyName, defaultValue, userProperties); + } + + /** + * Gets a configured setting from a the user's configuration file. + * The configuration file will be the .testcontainers.properties file in the user's home directory. + * + * @param propertyName name of configuration file property (dot-separated lower case) + * @return the found value, or null if not set + */ + @Nullable + @Contract("_, !null -> !null") + public String getUserProperty(@NotNull final String propertyName, @Nullable final String defaultValue) { + return getConfigurable(propertyName, defaultValue); + } + + @Deprecated + public Properties getProperties() { + return Stream.of(userProperties, classpathProperties) + .reduce(new Properties(), (a, b) -> { + a.putAll(b); + return a; + }); + } + + @Deprecated public boolean updateGlobalConfig(@NonNull String prop, @NonNull String value) { + return updateUserConfig(prop, value); + } + + @Synchronized + public boolean updateUserConfig(@NonNull String prop, @NonNull String value) { try { - if (value.equals(environmentProperties.get(prop))) { + if (value.equals(userProperties.get(prop))) { return false; } - environmentProperties.setProperty(prop, value); + userProperties.setProperty(prop, value); - ENVIRONMENT_CONFIG_FILE.createNewFile(); - try (OutputStream outputStream = new FileOutputStream(ENVIRONMENT_CONFIG_FILE)) { - environmentProperties.store(outputStream, "Modified by Testcontainers"); + USER_CONFIG_FILE.createNewFile(); + try (OutputStream outputStream = new FileOutputStream(USER_CONFIG_FILE)) { + userProperties.store(outputStream, "Modified by Testcontainers"); } // Update internal state only if environment config was successfully updated - properties.setProperty(prop, value); + userProperties.setProperty(prop, value); return true; } catch (Exception e) { - log.debug("Can't store environment property {} in {}", prop, ENVIRONMENT_CONFIG_FILE); + log.debug("Can't store environment property {} in {}", prop, USER_CONFIG_FILE); return false; } } @@ -211,7 +319,7 @@ public boolean updateGlobalConfig(@NonNull String prop, @NonNull String value) { @SneakyThrows(MalformedURLException.class) private static TestcontainersConfiguration loadConfiguration() { return new TestcontainersConfiguration( - readProperties(ENVIRONMENT_CONFIG_FILE.toURI().toURL()), + readProperties(USER_CONFIG_FILE.toURI().toURL()), Stream .of( TestcontainersConfiguration.class.getClassLoader(), @@ -223,8 +331,8 @@ private static TestcontainersConfiguration loadConfiguration() { .reduce(new Properties(), (a, b) -> { a.putAll(b); return a; - }) - ); + }), + System.getenv()); } private static Properties readProperties(URL url) { @@ -239,4 +347,24 @@ private static Properties readProperties(URL url) { } return properties; } + + private DockerImageName getImage(final String defaultValue) { + return getConfiguredSubstituteImage(DockerImageName.parse(defaultValue)); + } + + DockerImageName getConfiguredSubstituteImage(DockerImageName original) { + for (final Map.Entry entry : CONTAINER_MAPPING.entrySet()) { + if (original.isCompatibleWith(entry.getKey())) { + return + Optional.ofNullable(entry.getValue()) + .map(propertyName -> getEnvVarOrProperty(propertyName, null)) + .map(String::valueOf) + .map(String::trim) + .map(DockerImageName::parse) + .orElse(original) + .asCompatibleSubstituteFor(original); + } + } + return original; + } } diff --git a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java index ccd68fbd984..e48a2a6fdf9 100644 --- a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java +++ b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java @@ -1,57 +1,107 @@ package org.testcontainers.utility; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import org.junit.Before; +import org.junit.Test; +import java.util.HashMap; +import java.util.Map; import java.util.Properties; import java.util.UUID; -import org.junit.Test; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; public class TestcontainersConfigurationTest { - final Properties environmentProperties = new Properties(); + private Properties userProperties; + private Properties classpathProperties; + private Map environment; - final Properties classpathProperties = new Properties(); + @Before + public void setUp() { + userProperties = new Properties(); + classpathProperties = new Properties(); + environment = new HashMap<>(); + } @Test - public void shouldReadChecksFromEnvironmentOnly() { + public void shouldNotReadChecksFromClasspathProperties() { assertFalse("checks enabled by default", newConfig().isDisableChecks()); classpathProperties.setProperty("checks.disable", "true"); assertFalse("checks are not affected by classpath properties", newConfig().isDisableChecks()); + } + + @Test + public void shouldReadChecksFromUserProperties() { + assertFalse("checks enabled by default", newConfig().isDisableChecks()); - environmentProperties.setProperty("checks.disable", "true"); - assertTrue("checks disabled", newConfig().isDisableChecks()); + userProperties.setProperty("checks.disable", "true"); + assertTrue("checks disabled via user properties", newConfig().isDisableChecks()); } @Test - public void shouldReadDockerClientStrategyFromEnvironmentOnly() { + public void shouldReadChecksFromEnvironment() { + assertFalse("checks enabled by default", newConfig().isDisableChecks()); + + userProperties.remove("checks.disable"); + environment.put("TESTCONTAINERS_CHECKS_DISABLE", "true"); + assertTrue("checks disabled via env var", newConfig().isDisableChecks()); + } + + @Test + public void shouldNotReadDockerClientStrategyFromClasspathProperties() { String currentValue = newConfig().getDockerClientStrategyClassName(); classpathProperties.setProperty("docker.client.strategy", UUID.randomUUID().toString()); assertEquals("Docker client strategy is not affected by classpath properties", currentValue, newConfig().getDockerClientStrategyClassName()); + } + + @Test + public void shouldReadDockerClientStrategyFromUserProperties() { + userProperties.setProperty("docker.client.strategy", "foo"); + assertEquals("Docker client strategy is changed by user property", "foo", newConfig().getDockerClientStrategyClassName()); + } - environmentProperties.setProperty("docker.client.strategy", "foo"); - assertEquals("Docker client strategy is changed", "foo", newConfig().getDockerClientStrategyClassName()); + @Test + public void shouldReadDockerClientStrategyFromEnvironment() { + userProperties.remove("docker.client.strategy"); + environment.put("TESTCONTAINERS_DOCKER_CLIENT_STRATEGY", "foo"); + assertEquals("Docker client strategy is changed by env var", "foo", newConfig().getDockerClientStrategyClassName()); } @Test - public void shouldReadReuseFromEnvironmentOnly() { + public void shouldNotReadReuseFromClasspathProperties() { assertFalse("no reuse by default", newConfig().environmentSupportsReuse()); classpathProperties.setProperty("testcontainers.reuse.enable", "true"); assertFalse("reuse is not affected by classpath properties", newConfig().environmentSupportsReuse()); + } - environmentProperties.setProperty("testcontainers.reuse.enable", "true"); - assertTrue("reuse enabled", newConfig().environmentSupportsReuse()); + @Test + public void shouldReadReuseFromUserProperties() { + assertFalse("no reuse by default", newConfig().environmentSupportsReuse()); + + userProperties.setProperty("testcontainers.reuse.enable", "true"); + assertTrue("reuse enabled via user property", newConfig().environmentSupportsReuse()); + } + @Test + public void shouldReadReuseFromEnvironment() { + assertFalse("no reuse by default", newConfig().environmentSupportsReuse()); - environmentProperties.setProperty("ryuk.container.image", " testcontainersofficial/ryuk:0.3.0 "); - assertEquals("trailing whitespace was not removed from image name property", "testcontainersofficial/ryuk:0.3.0",newConfig().getRyukDockerImageName().asCanonicalNameString()); + userProperties.remove("testcontainers.reuse.enable"); + environment.put("TESTCONTAINERS_REUSE_ENABLE", "true"); + assertTrue("reuse enabled via env var", newConfig().environmentSupportsReuse()); + } + @Test + public void shouldTrimImageNames() { + userProperties.setProperty("ryuk.container.image", " testcontainersofficial/ryuk:0.3.0 "); + assertEquals("trailing whitespace was not removed from image name property", "testcontainersofficial/ryuk:0.3.0",newConfig().getRyukImage()); } private TestcontainersConfiguration newConfig() { - return new TestcontainersConfiguration(environmentProperties, classpathProperties); + return new TestcontainersConfiguration(userProperties, classpathProperties, environment); } } From b94da0a425ad404a316363bce3241f07233479f2 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 29 Oct 2020 18:22:06 +0000 Subject: [PATCH 1715/5615] Update sponsors (#3410) Co-authored-by: Fedor Korotkov --- docs/index.md | 61 ++++++++++++++++++++--------- docs/sponsor_logos/cirrus_labs.jpg | Bin 0 -> 3204 bytes docs/sponsor_logos/jooq.jpg | Bin 0 -> 3559 bytes docs/sponsor_logos/vivy.png | Bin 0 -> 4170 bytes 4 files changed, 42 insertions(+), 19 deletions(-) create mode 100644 docs/sponsor_logos/cirrus_labs.jpg create mode 100644 docs/sponsor_logos/jooq.jpg create mode 100644 docs/sponsor_logos/vivy.png diff --git a/docs/index.md b/docs/index.md index 131a9074bfd..28ca2453993 100644 --- a/docs/index.md +++ b/docs/index.md @@ -90,6 +90,48 @@ testImplementation('org.testcontainers:mysql') //no version specified These libraries in particular seem to be especially prone to conflicts with test code/application under test code. As such, **these libraries are 'shaded' into the core testcontainers JAR** and relocated under `org.testcontainers.shaded` to prevent class conflicts. +## Sponsors + +Testcontainers is on Github Sponsors! Please consider sponsoring us to sustain the project and help us improve it further. + + + +A huge thank you to our sponsors: + +### Bronze sponsors + +

+ + + + + +### Backers + +* [Philip Riecks (@rieckpil)](https://github.com/rieckpil) +* [Karl Heinz Marbaise (@khmarbaise)](https://github.com/khmarbaise) +* [Sascha Frinken (@sascha-frinken)](https://github.com/sascha-frinken) +* [Christoph Dreis (@dreis2211)](https://github.com/dreis2211) +* [Pascal Zwick (@pas2al)](https://github.com/pas2al) +* [Nikita Zhevnitskiy (@zhenik)](https://github.com/zhenik) +* [Bas Stoker (@bastoker)](https://github.com/bastoker) + ## Who is using Testcontainers? * [ZeroTurnaround](https://zeroturnaround.com) - Testing of the Java Agents, micro-services, Selenium browser automation @@ -121,25 +163,6 @@ testImplementation('org.testcontainers:mysql') //no version specified * [Apache SkyWalking](http://github.com/apache/skywalking) - End-to-end testing of the Apache SkyWalking, and plugin tests of its subproject, [Apache SkyWalking Python](http://github.com/apache/skywalking-python), and of its eco-system built by the community, like [SkyAPM NodeJS Agent](http://github.com/SkyAPM/nodejs) * [jOOQ](https://www.jooq.org) - Integration testing all of jOOQ with a variety of RDBMS -## Sponsors - -Testcontainers is on Github Sponsors! Please consider sponsoring us to sustain the project and help us improve it further. - - - -### Backers - -A huge thank you to our backers: - -* [Philip Riecks (@rieckpil)](https://github.com/rieckpil) -* [Karl Heinz Marbaise (@khmarbaise)](https://github.com/khmarbaise) -* [Sascha Frinken (@sascha-frinken)](https://github.com/sascha-frinken) -* [Lukas Eder (@lukaseder)](https://github.com/lukaseder) -* [Fedor Korotkov (@fkorotkov)](https://github.com/fkorotkov) -* [Christoph Dreis (@dreis2211)](https://github.com/dreis2211) -* [Pascal Zwick (@pas2al)](https://github.com/pas2al) -* [Nikita Zhevnitskiy (@zhenik)](https://github.com/zhenik) - ## License diff --git a/docs/sponsor_logos/cirrus_labs.jpg b/docs/sponsor_logos/cirrus_labs.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3bec84f0a22693cde05118fa3c241ed9e89cf389 GIT binary patch literal 3204 zcmd6odpOkF8pqd|F&9Mc#x)I@5{hxD6GIMC$8kxaY12(ECA%U+F{o68bRo(m_Y%{% z#x9i0D4kG>klTboE|c4j#_zXh?|q(qcDw%A=RD`x&wiihyPjvgYprLk&wAIpUZfN0 z2Il}Ci^XAu@i-igK){QL$*d3)6%`{&NlVD6D5|NdC@L$j($LjfwR$~CS($9ScKwEp z6beO6Ym=#o{uW&Wiav@!Ac%>IDXdtbps%j1uK!;fQVS&Upb0dgQLBNFBnmBwLh4bO z0HB0XKO6YBp@h&FVJr?$5D{GogivU-5C$zQjKK(Ih6=_2Mp9TxeWL|d+HpT_^>G>f zh=eb2`P5|eHwr(|Yj=j7()-@R8{Qd(AC z@vyS$XQ;STEwfn?w`EqmZZn~0nS zWk8en6VlH_e+(${elgtO9UDLKlCggkG|y$xo}k08EB+f_E=+g?0J}k02MXkQFCQKnW9*w;>|ZaO;%lA z=|cvY(SJ-iNrVdCMw}7VdQ+@TPwr zD8Dt>y-Djlsq9T8JUQf|)C9}VsV>`a!#t~5m9O@{4++AWqH zg_ouRK^}Wq_!=gbcS4W$xn?vdt_NpNHvDMg-h!LP5)LOG+$L`FeYk1OpOb= z_Od@z{DfKA{4Tl|`@M)s_l^#dgGw{)AGLw71_It8U_0P#|K_h#P-T51sK~c`$?sg4 z=fCRwEN)9U**_p^jhf5kGXq%=%G=Mmcf2H-Hxn#Lb_m6uuyaE|6yp_xYaA*F_p9(N z*yx2R$ZJ^(J{#1xFhTzFM$7psUMCqGJ4I3RM*ni_{`dirCoy3_8gda_ZXMN!I6|vN zK#k9L>E~b-Z+8jtdC6xwuZub=Z%3Ul#R1f8hIsiSCZ{LA8ee=d;qg>!{u1c}TvHg5 zb7qA1Lw(y=FR9{{&TYzO-v?LDKDoh4fB>#N1$^rI)Nzt){bRUG7INL0wyU5>jx22Hj!z#6Y7-uAwBZ{Ht*m7`SdyJs3jzl(r@Ej<5a zv5-6%QvtQ&I!axdJf_p=6>yZP;BgDl_}&OOH`7ovyKyWXlIr7_tGTy%s571h5B4o7 z%-Qn^{=6($x?e7?R+XYXw5O?a4Bu0es5FxyA^PnK^JaSZ zY~Y|@NQZ5y`k~ayQ`~5*TYM|poapTG%E&{AuPt1h^CGE3pEG==xkRfI#U9^H9k8Vv z!}MN8zioy@ay~Pc@gGMPER%GWF%mlhV^Y9kO7xP~Zyf)pg#~w}brS*$=^fk4%d9nZ zrF;|^LL}_LiCTUWldgt&q?ESrky+!1j-3Mtz;mnRE4%|@$P)|83qz^!eOp0ND%}{O zs_8mzAs#%oZAPtw1R2NRaSEmA(JE9ip+% zG>#HoLn7DtLW!G(FZ#v#tPIhXwEyhc!RNOifUyFEGOf-y&Nnj`7t>)_(d=Cy0;mQE z=&LhVJJRMM6IwhDhj|IoF)F_NJ4VY31T0Lq;<{#C8q|(fAs{^&0aF866G_z`Dic-|p<8MhO1VQ*df7*KVa(QWBa zBB#O9NXu@;u>^05v!PvQ=oQ~!*hVz6*u!Yo%^>{d_`4&WflQ^Z3jDW<{$Im~y!#Px CQQ(vS literal 0 HcmV?d00001 diff --git a/docs/sponsor_logos/jooq.jpg b/docs/sponsor_logos/jooq.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7a64f773d615adaa24276c8e47bdd6da063f7b2e GIT binary patch literal 3559 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 literal 0 HcmV?d00001 From 0ac96892fbd9fa91e586658338ef2d22cb5db294 Mon Sep 17 00:00:00 2001 From: Piotr Findeisen Date: Fri, 30 Oct 2020 17:23:16 +0100 Subject: [PATCH 1716/5615] Presto: Bump default Presto version (used in deprecated constructor and unversioned JDBC URL) from 329 to 344 (#3312) --- docs/modules/databases/jdbc.md | 2 +- .../java/org/testcontainers/containers/PrestoContainer.java | 2 +- .../src/test/java/org/testcontainers/PrestoTestImages.java | 4 ++-- .../org/testcontainers/jdbc/presto/PrestoJDBCDriverTest.java | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/modules/databases/jdbc.md b/docs/modules/databases/jdbc.md index 21393455a9f..65db9aaf07f 100644 --- a/docs/modules/databases/jdbc.md +++ b/docs/modules/databases/jdbc.md @@ -41,7 +41,7 @@ Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database #### Using Presto -`jdbc:tc:presto:329://localhost/memory/default` +`jdbc:tc:presto:344://localhost/memory/default` ### Using a classpath init script diff --git a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java index e4934693d14..54cc6758562 100644 --- a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java +++ b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java @@ -18,7 +18,7 @@ public class PrestoContainer> extends JdbcDat public static final String NAME = "presto"; private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("prestosql/presto"); public static final String IMAGE = "prestosql/presto"; - public static final String DEFAULT_TAG = "329"; + public static final String DEFAULT_TAG = "344"; public static final Integer PRESTO_PORT = 8080; diff --git a/modules/presto/src/test/java/org/testcontainers/PrestoTestImages.java b/modules/presto/src/test/java/org/testcontainers/PrestoTestImages.java index 1b4e449c99f..a257500576c 100644 --- a/modules/presto/src/test/java/org/testcontainers/PrestoTestImages.java +++ b/modules/presto/src/test/java/org/testcontainers/PrestoTestImages.java @@ -3,6 +3,6 @@ import org.testcontainers.utility.DockerImageName; public interface PrestoTestImages { - DockerImageName PRESTO_TEST_IMAGE = DockerImageName.parse("prestosql/presto:329"); - DockerImageName PRESTO_PREVIOUS_VERSION_TEST_IMAGE = DockerImageName.parse("prestosql/presto:328"); + DockerImageName PRESTO_TEST_IMAGE = DockerImageName.parse("prestosql/presto:344"); + DockerImageName PRESTO_PREVIOUS_VERSION_TEST_IMAGE = DockerImageName.parse("prestosql/presto:343"); } diff --git a/modules/presto/src/test/java/org/testcontainers/jdbc/presto/PrestoJDBCDriverTest.java b/modules/presto/src/test/java/org/testcontainers/jdbc/presto/PrestoJDBCDriverTest.java index 88939c62939..d1a64c92aaa 100644 --- a/modules/presto/src/test/java/org/testcontainers/jdbc/presto/PrestoJDBCDriverTest.java +++ b/modules/presto/src/test/java/org/testcontainers/jdbc/presto/PrestoJDBCDriverTest.java @@ -15,7 +15,7 @@ public class PrestoJDBCDriverTest extends AbstractJDBCDriverTest { public static Iterable data() { return asList( new Object[][]{ - {"jdbc:tc:presto:329://hostname/", EnumSet.of(Options.PmdKnownBroken)}, + {"jdbc:tc:presto:344://hostname/", EnumSet.of(Options.PmdKnownBroken)}, }); } } From e64db24003e05a642b5f3d8ce0cb084fc6ac7adb Mon Sep 17 00:00:00 2001 From: Or Noyman Date: Fri, 30 Oct 2020 18:23:55 +0200 Subject: [PATCH 1717/5615] Vault: add a fluent API for configuring Vault's logging level (#2231) --- .../org/testcontainers/vault/VaultContainer.java | 12 ++++++++++++ .../org/testcontainers/vault/VaultLogLevel.java | 14 ++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 modules/vault/src/main/java/org/testcontainers/vault/VaultLogLevel.java diff --git a/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java b/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java index b0c8a2a40e3..f47818d8c7d 100644 --- a/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java +++ b/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java @@ -10,6 +10,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Consumer; import static com.github.dockerjava.api.model.Capability.IPC_LOCK; @@ -106,6 +107,17 @@ public SELF withVaultPort(int port) { return self(); } + /** + * Sets the logging level for the Vault server in the container. + * Logs can be consumed through {@link #withLogConsumer(Consumer)}. + * + * @param level the logging level to set for Vault. + * @return this + */ + public SELF withLogLevel(VaultLogLevel level) { + return withEnv("VAULT_LOG_LEVEL", level.config); + } + /** * Pre-loads secrets into Vault container. User may specify one or more secrets and all will be added to each path * that is specified. Thus this can be called more than once for multiple paths to be added to Vault. diff --git a/modules/vault/src/main/java/org/testcontainers/vault/VaultLogLevel.java b/modules/vault/src/main/java/org/testcontainers/vault/VaultLogLevel.java new file mode 100644 index 00000000000..58b22f975ff --- /dev/null +++ b/modules/vault/src/main/java/org/testcontainers/vault/VaultLogLevel.java @@ -0,0 +1,14 @@ +package org.testcontainers.vault; + +/** + * Vault preset of logging levels. + */ +public enum VaultLogLevel { + Trace("trace"), Debug("debug"), Info("info"), Warn("warn"), Error("err"); + + public final String config; + + VaultLogLevel(String config) { + this.config = config; + } +} From 30c29905babb9fb394be697f2c576be9756a71e5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Oct 2020 16:25:54 +0000 Subject: [PATCH 1718/5615] Bump assertj-core from 3.17.2 to 3.18.0 in /modules/vault (#3384) Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.17.2 to 3.18.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.17.2...assertj-core-3.18.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index 6d862eeba2c..caddc81d3a7 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -5,6 +5,6 @@ dependencies { testCompile 'com.bettercloud:vault-java-driver:5.1.0' testCompile 'io.rest-assured:rest-assured:4.3.1' - testCompile 'org.assertj:assertj-core:3.17.2' + testCompile 'org.assertj:assertj-core:3.18.0' } From f9f2c8369d4af951197bcbde0bf70397613f4d05 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Oct 2020 16:26:09 +0000 Subject: [PATCH 1719/5615] Bump assertj-core from 3.17.2 to 3.18.0 in /modules/neo4j (#3385) Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.17.2 to 3.18.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.17.2...assertj-core-3.18.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index da90a493a01..8d7f59e564e 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -34,5 +34,5 @@ dependencies { compile project(":testcontainers") testCompile "org.neo4j.driver:neo4j-java-driver:1.7.5" - testCompile 'org.assertj:assertj-core:3.17.2' + testCompile 'org.assertj:assertj-core:3.18.0' } From 4ec423484658eabc1b60f6b66c36005c7e36e7b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Oct 2020 16:26:31 +0000 Subject: [PATCH 1720/5615] Bump assertj-core from 3.17.2 to 3.18.0 in /modules/junit-jupiter (#3386) Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.17.2 to 3.18.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.17.2...assertj-core-3.18.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 61594d9842b..321ac8fa82e 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -12,7 +12,7 @@ dependencies { testCompile ('org.mockito:mockito-core:3.5.13') { exclude(module: 'hamcrest-core') } - testCompile 'org.assertj:assertj-core:3.17.2' + testCompile 'org.assertj:assertj-core:3.18.0' testCompile 'org.junit.jupiter:junit-jupiter-params:5.7.0' testRuntime 'org.postgresql:postgresql:42.2.18' From 6107d553d7fd74d97af2da9220d43ddee0834a8f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Oct 2020 16:26:46 +0000 Subject: [PATCH 1721/5615] Bump mockito-core from 3.5.13 to 3.5.15 in /modules/junit-jupiter (#3387) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.5.13 to 3.5.15. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.5.13...v3.5.15) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 321ac8fa82e..7986c067c89 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,7 +9,7 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.4.5' testCompile 'redis.clients:jedis:3.3.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.13' - testCompile ('org.mockito:mockito-core:3.5.13') { + testCompile ('org.mockito:mockito-core:3.5.15') { exclude(module: 'hamcrest-core') } testCompile 'org.assertj:assertj-core:3.18.0' From ef899a2ce5c43b890ab26ef32e4f7a33959ed4e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 31 Oct 2020 09:59:52 +0000 Subject: [PATCH 1722/5615] Bump s3 from 2.15.9 to 2.15.14 in /modules/localstack (#3388) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 418344b82f2..908f87de07d 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,5 +7,5 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.880' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.884' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' - testCompile 'software.amazon.awssdk:s3:2.15.9' + testCompile 'software.amazon.awssdk:s3:2.15.14' } From 8d1a723617aecda6040bcc9201fb009c2dd9fff9 Mon Sep 17 00:00:00 2001 From: Kirill Artamonov Date: Sat, 31 Oct 2020 15:39:45 +0100 Subject: [PATCH 1723/5615] Bump Python mkdocs tool dependency to address CVE-2019-10906 (#3379) Co-authored-by: Richard North --- Pipfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Pipfile.lock b/Pipfile.lock index 852b507f886..899f8c82170 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -28,7 +28,7 @@ "sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd", "sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4" ], - "version": "==2.10" + "version": "==2.11.2" }, "livereload": { "hashes": [ From 44e8e9ab279cad2cd9d0c9fc5bb6e7799c409999 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 5 Nov 2020 19:19:08 +0000 Subject: [PATCH 1724/5615] Image substitution (#3102) * Refactor Testcontainers configuration to allow config by env var * Add Image substitution mechanism Builds upon #3021 and #3411: * adds a pluggable image substitution mechanism using ServiceLoader, enabling users to perform custom substitution/auditing of images being used by their tests * provides a default implementation that behaves similarly to legacy `TestcontainersConfiguration` approach (`testcontainers.properties`) Notes: * behaviour is similar but not quite identical to `TestcontainersConfiguration`: use of a configured custom image for, e.g. Kafka/Pulsar that does not have a tag specified causes the substitution to take effect for all usages. It seems very unlikely that people would be using a mix of the config file image overrides in some places _and_ specific images specified in code in others. * Duplication of default image names in modules vs `TestcontainersConfiguration` class is intentional: specifying image overrides in `testcontainers.properties` should be removed in the future. * ~Add log deprecation warnings when `testcontainers.properties` image overrides are used.~ Defer to a future release? * Remove extraneous change * Un-ignore docs example test by implementing a 'reversing' image name substitutor * Use configuration, not service loader, to select an ImageNameSubstitutor * Add check for order of config setting precedence * Extract classpath scanner and support finding of multiple resources * Introduce deterministic merging of classpath properties files * Update docs * Update docs * Remove service loader reference * Chain substitution through default and configured implementations * Small tweaks following review * Fix test compile error * Add UnstableAPI annotation * Move TestSpecificImageNameSubstitutor back to original package and remove duplicate use of default substitutor --- .../testcontainers/DockerClientFactory.java | 11 +- .../containers/DockerComposeContainer.java | 5 +- .../containers/GenericContainer.java | 6 +- .../containers/PortForwardingContainer.java | 6 +- .../containers/SocatContainer.java | 8 +- .../containers/VncRecordingContainer.java | 4 +- .../DockerClientProviderStrategy.java | 2 +- .../images/RemoteDockerImage.java | 10 +- .../utility/ClasspathScanner.java | 59 ++++++++ ...ConfigurationFileImageNameSubstitutor.java | 44 ++++++ .../utility/DefaultImageNameSubstitutor.java | 35 +++++ .../utility/ImageNameSubstitutor.java | 130 ++++++++++++++++++ .../utility/ResourceReaper.java | 4 +- .../utility/TestcontainersConfiguration.java | 68 +++------ .../DockerClientFactoryTest.java | 9 +- .../java/org/testcontainers/TestImages.java | 3 +- .../utility/AuthenticatedImagePullTest.java | 26 ++-- .../utility/ClasspathScannerTest.java | 114 +++++++++++++++ .../DefaultImageNameSubstitutorTest.java | 38 +++++ .../DockerImageNameCompatibilityTest.java | 3 +- .../utility/FakeImageSubstitutor.java | 13 ++ .../utility/ImageNameSubstitutorTest.java | 75 ++++++++++ .../TestcontainersConfigurationTest.java | 63 +++++++++ .../test/resources/expectedClasspathFile.txt | 1 + docs/examples/junit4/generic/build.gradle | 3 + .../generic/ExampleImageNameSubstitutor.java | 24 ++++ .../generic/ImageNameSubstitutionTest.java | 48 +++++++ .../TestSpecificImageNameSubstitutor.java | 26 ++++ .../src/test/resources/logback-test.xml | 14 ++ .../test/resources/testcontainers.properties | 1 + docs/features/configuration.md | 45 ++++-- docs/features/image_name_substitution.md | 117 ++++++++++++++++ docs/features/pull_rate_limiting.md | 19 +++ .../containers/KafkaContainer.java | 5 +- .../containers/KafkaContainerTest.java | 2 +- .../localstack/LocalStackContainer.java | 5 +- .../containers/OracleContainer.java | 3 +- .../containers/PulsarContainer.java | 5 +- .../spock/SpockTestImages.groovy | 5 +- 39 files changed, 936 insertions(+), 123 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/utility/ClasspathScanner.java create mode 100644 core/src/main/java/org/testcontainers/utility/ConfigurationFileImageNameSubstitutor.java create mode 100644 core/src/main/java/org/testcontainers/utility/DefaultImageNameSubstitutor.java create mode 100644 core/src/main/java/org/testcontainers/utility/ImageNameSubstitutor.java create mode 100644 core/src/test/java/org/testcontainers/utility/ClasspathScannerTest.java create mode 100644 core/src/test/java/org/testcontainers/utility/DefaultImageNameSubstitutorTest.java create mode 100644 core/src/test/java/org/testcontainers/utility/FakeImageSubstitutor.java create mode 100644 core/src/test/java/org/testcontainers/utility/ImageNameSubstitutorTest.java create mode 100644 core/src/test/resources/expectedClasspathFile.txt create mode 100644 docs/examples/junit4/generic/src/test/java/generic/ExampleImageNameSubstitutor.java create mode 100644 docs/examples/junit4/generic/src/test/java/generic/ImageNameSubstitutionTest.java create mode 100644 docs/examples/junit4/generic/src/test/java/generic/support/TestSpecificImageNameSubstitutor.java create mode 100644 docs/examples/junit4/generic/src/test/resources/logback-test.xml create mode 100644 docs/examples/junit4/generic/src/test/resources/testcontainers.properties create mode 100644 docs/features/image_name_substitution.md create mode 100644 docs/features/pull_rate_limiting.md diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 571b18fbcce..d44cd3b5b6f 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -25,6 +25,8 @@ import org.testcontainers.dockerclient.TransportConfig; import org.testcontainers.images.TimeLimitedLoggedPullImageResultCallback; import org.testcontainers.utility.ComparableVersion; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.ImageNameSubstitutor; import org.testcontainers.utility.MountableFile; import org.testcontainers.utility.ResourceReaper; import org.testcontainers.utility.TestcontainersConfiguration; @@ -61,7 +63,7 @@ public class DockerClientFactory { TESTCONTAINERS_SESSION_ID_LABEL, SESSION_ID ); - private static final String TINY_IMAGE = TestcontainersConfiguration.getInstance().getTinyDockerImageName().asCanonicalNameString(); + private static final DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.5"); private static DockerClientFactory instance; // Cached client configuration @@ -343,8 +345,11 @@ public T runInsideDocker(Consumer createContainerCmdCons } private T runInsideDocker(DockerClient client, Consumer createContainerCmdConsumer, BiFunction block) { - checkAndPullImage(client, TINY_IMAGE); - CreateContainerCmd createContainerCmd = client.createContainerCmd(TINY_IMAGE) + + final String tinyImage = ImageNameSubstitutor.instance().apply(TINY_IMAGE).asCanonicalNameString(); + + checkAndPullImage(client, tinyImage); + CreateContainerCmd createContainerCmd = client.createContainerCmd(tinyImage) .withLabels(DEFAULT_LABELS); createContainerCmdConsumer.accept(createContainerCmd); String id = createContainerCmd.exec().getId(); diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index bd06083644d..7a67073e383 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -29,11 +29,11 @@ import org.testcontainers.utility.AuditLogger; import org.testcontainers.utility.Base58; import org.testcontainers.utility.CommandLine; +import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.DockerLoggerFactory; import org.testcontainers.utility.LogUtils; import org.testcontainers.utility.MountableFile; import org.testcontainers.utility.ResourceReaper; -import org.testcontainers.utility.TestcontainersConfiguration; import org.zeroturnaround.exec.InvalidExitValueException; import org.zeroturnaround.exec.ProcessExecutor; import org.zeroturnaround.exec.stream.slf4j.Slf4jStream; @@ -608,10 +608,11 @@ interface DockerCompose { class ContainerisedDockerCompose extends GenericContainer implements DockerCompose { public static final char UNIX_PATH_SEPERATOR = ':'; + public static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("docker/compose:1.24.1"); public ContainerisedDockerCompose(List composeFiles, String identifier) { - super(TestcontainersConfiguration.getInstance().getDockerComposeDockerImageName()); + super(DEFAULT_IMAGE_NAME); addEnv(ENV_PROJECT_NAME, identifier); // Map the docker compose file into the container diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 95050d8f8ee..641096a0779 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -239,13 +239,9 @@ public GenericContainer(@NonNull final RemoteDockerImage image) { */ @Deprecated public GenericContainer() { - this(TestcontainersConfiguration.getInstance().getTinyDockerImageName().asCanonicalNameString()); + this(TestcontainersConfiguration.getInstance().getTinyImage()); } - /** - * @deprecated use {@link GenericContainer(DockerImageName)} instead - */ - @Deprecated public GenericContainer(@NonNull final String dockerImageName) { this.setDockerImageName(dockerImageName); } diff --git a/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java b/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java index e42f2681675..b3f020500a1 100644 --- a/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java +++ b/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java @@ -5,15 +5,15 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.SneakyThrows; -import org.testcontainers.utility.TestcontainersConfiguration; +import org.testcontainers.utility.DockerImageName; import java.time.Duration; import java.util.AbstractMap; import java.util.Collections; +import java.util.Map.Entry; import java.util.Optional; import java.util.Set; import java.util.UUID; -import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; public enum PortForwardingContainer { @@ -29,7 +29,7 @@ public enum PortForwardingContainer { @SneakyThrows private Connection createSSHSession() { String password = UUID.randomUUID().toString(); - container = new GenericContainer<>(TestcontainersConfiguration.getInstance().getSSHdDockerImageName()) + container = new GenericContainer<>(DockerImageName.parse("testcontainers/sshd:1.0.0")) .withExposedPorts(22) .withEnv("PASSWORD", password) .withCommand( diff --git a/core/src/main/java/org/testcontainers/containers/SocatContainer.java b/core/src/main/java/org/testcontainers/containers/SocatContainer.java index fbca73eb29d..7592d9f4d1f 100644 --- a/core/src/main/java/org/testcontainers/containers/SocatContainer.java +++ b/core/src/main/java/org/testcontainers/containers/SocatContainer.java @@ -1,12 +1,10 @@ package org.testcontainers.containers; -import org.testcontainers.utility.Base58; -import org.testcontainers.utility.DockerImageName; -import org.testcontainers.utility.TestcontainersConfiguration; - import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; +import org.testcontainers.utility.Base58; +import org.testcontainers.utility.DockerImageName; /** * A socat container is used as a TCP proxy, enabling any TCP port of another container to be exposed @@ -17,7 +15,7 @@ public class SocatContainer extends GenericContainer { private final Map targets = new HashMap<>(); public SocatContainer() { - this(TestcontainersConfiguration.getInstance().getSocatDockerImageName()); + this(DockerImageName.parse("alpine/socat:1.7.3.4-r0")); } public SocatContainer(final DockerImageName dockerImageName) { diff --git a/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java b/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java index 8ac060c2af6..de720110a51 100644 --- a/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java +++ b/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java @@ -6,7 +6,7 @@ import lombok.ToString; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; -import org.testcontainers.utility.TestcontainersConfiguration; +import org.testcontainers.utility.DockerImageName; import java.io.File; import java.io.InputStream; @@ -52,7 +52,7 @@ public VncRecordingContainer(@NonNull GenericContainer targetContainer) { * Create a sidekick container and attach it to another container. The VNC output of that container will be recorded. */ public VncRecordingContainer(@NonNull Network network, @NonNull String targetNetworkAlias) throws IllegalStateException { - super(TestcontainersConfiguration.getInstance().getVncDockerImageName()); + super(DockerImageName.parse("testcontainers/vnc-recorder:1.1.0")); this.targetNetworkAlias = targetNetworkAlias; withNetwork(network); diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index e21c75a581d..1bd51da1263 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -171,7 +171,7 @@ public static DockerClientProviderStrategy getFirstValidStrategy(List imageFuture) { @@ -100,7 +101,10 @@ protected final String resolve() { } private DockerImageName getImageName() throws InterruptedException, ExecutionException { - return imageNameFuture.get(); + final DockerImageName specifiedImageName = imageNameFuture.get(); + + // Allow the image name to be substituted + return ImageNameSubstitutor.instance().apply(specifiedImageName); } @ToString.Include(name = "imageName", rank = 1) diff --git a/core/src/main/java/org/testcontainers/utility/ClasspathScanner.java b/core/src/main/java/org/testcontainers/utility/ClasspathScanner.java new file mode 100644 index 00000000000..76716cfdb7c --- /dev/null +++ b/core/src/main/java/org/testcontainers/utility/ClasspathScanner.java @@ -0,0 +1,59 @@ +package org.testcontainers.utility; + +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.VisibleForTesting; + +import java.net.URL; +import java.util.Collections; +import java.util.Comparator; +import java.util.Objects; +import java.util.stream.Stream; + +/** + * Utility for identifying resource files on classloaders. + */ +@Slf4j +class ClasspathScanner { + + @VisibleForTesting + static Stream scanFor(final String name, ClassLoader... classLoaders) { + return Stream + .of(classLoaders) + .flatMap(classLoader -> getAllPropertyFilesOnClassloader(classLoader, name)) + .filter(Objects::nonNull) + .sorted( + Comparator + .comparing(ClasspathScanner::filesFileSchemeFirst) // resolve 'local' files first + .thenComparing(URL::toString) // sort alphabetically for the sake of determinism + ) + .distinct(); + } + + private static Integer filesFileSchemeFirst(final URL t) { + return t.getProtocol().equals("file") ? 0 : 1; + } + + /** + * @param name the resource name to search for + * @return distinct, ordered stream of resources found by searching this class' classloader and the current thread's + * context classloader. Results are currently alphabetically sorted. + */ + static Stream scanFor(final String name) { + return scanFor( + name, + ClasspathScanner.class.getClassLoader(), + Thread.currentThread().getContextClassLoader() + ); + } + + @Nullable + private static Stream getAllPropertyFilesOnClassloader(final ClassLoader it, final String s) { + try { + return Collections.list(it.getResources(s)).stream(); + } catch (Exception e) { + log.error("Unable to read configuration from classloader {} - this is probably a bug", it, e); + return Stream.empty(); + } + } +} diff --git a/core/src/main/java/org/testcontainers/utility/ConfigurationFileImageNameSubstitutor.java b/core/src/main/java/org/testcontainers/utility/ConfigurationFileImageNameSubstitutor.java new file mode 100644 index 00000000000..530d5a76118 --- /dev/null +++ b/core/src/main/java/org/testcontainers/utility/ConfigurationFileImageNameSubstitutor.java @@ -0,0 +1,44 @@ +package org.testcontainers.utility; + +import com.google.common.annotations.VisibleForTesting; +import lombok.extern.slf4j.Slf4j; + +/** + * {@link ImageNameSubstitutor} which takes replacement image names from configuration. + * See {@link TestcontainersConfiguration} for the subset of image names which can be substituted using this mechanism. + */ +@Slf4j +final class ConfigurationFileImageNameSubstitutor extends ImageNameSubstitutor { + + private final TestcontainersConfiguration configuration; + + public ConfigurationFileImageNameSubstitutor() { + this(TestcontainersConfiguration.getInstance()); + } + + @VisibleForTesting + ConfigurationFileImageNameSubstitutor(TestcontainersConfiguration configuration) { + this.configuration = configuration; + } + + @Override + public DockerImageName apply(final DockerImageName original) { + final DockerImageName result = configuration + .getConfiguredSubstituteImage(original) + .asCompatibleSubstituteFor(original); + + if (!result.equals(original)) { + log.warn("Image name {} was substituted by configuration to {}. This approach is deprecated and will be removed in the future", + original, + result + ); + } + + return result; + } + + @Override + protected String getDescription() { + return getClass().getSimpleName(); + } +} diff --git a/core/src/main/java/org/testcontainers/utility/DefaultImageNameSubstitutor.java b/core/src/main/java/org/testcontainers/utility/DefaultImageNameSubstitutor.java new file mode 100644 index 00000000000..bcc7c96ba18 --- /dev/null +++ b/core/src/main/java/org/testcontainers/utility/DefaultImageNameSubstitutor.java @@ -0,0 +1,35 @@ +package org.testcontainers.utility; + +import com.google.common.annotations.VisibleForTesting; +import lombok.extern.slf4j.Slf4j; + +/** + * Testcontainers' default implementation of {@link ImageNameSubstitutor}. + * Delegates to {@link ConfigurationFileImageNameSubstitutor}. + */ +@Slf4j +final class DefaultImageNameSubstitutor extends ImageNameSubstitutor { + + private final ConfigurationFileImageNameSubstitutor configurationFileImageNameSubstitutor; + + public DefaultImageNameSubstitutor() { + configurationFileImageNameSubstitutor = new ConfigurationFileImageNameSubstitutor(); + } + + @VisibleForTesting + DefaultImageNameSubstitutor( + final ConfigurationFileImageNameSubstitutor configurationFileImageNameSubstitutor + ) { + this.configurationFileImageNameSubstitutor = configurationFileImageNameSubstitutor; + } + + @Override + public DockerImageName apply(final DockerImageName original) { + return configurationFileImageNameSubstitutor.apply(original); + } + + @Override + protected String getDescription() { + return "DefaultImageNameSubstitutor (" + configurationFileImageNameSubstitutor.getDescription() + ")"; + } +} diff --git a/core/src/main/java/org/testcontainers/utility/ImageNameSubstitutor.java b/core/src/main/java/org/testcontainers/utility/ImageNameSubstitutor.java new file mode 100644 index 00000000000..645b717a562 --- /dev/null +++ b/core/src/main/java/org/testcontainers/utility/ImageNameSubstitutor.java @@ -0,0 +1,130 @@ +package org.testcontainers.utility; + +import com.google.common.annotations.VisibleForTesting; +import lombok.extern.slf4j.Slf4j; +import org.testcontainers.UnstableAPI; + +import java.util.function.Function; + +/** + * An image name substitutor converts a Docker image name, as may be specified in code, to an alternative name. + * This is intended to provide a way to override image names, for example to enforce pulling of images from a private + * registry. + *

+ * This is marked as @{@link UnstableAPI} as this API is new. While we do not think major changes will be required, we + * will react to feedback if necessary. + */ +@Slf4j +@UnstableAPI +public abstract class ImageNameSubstitutor implements Function { + + @VisibleForTesting + static ImageNameSubstitutor instance; + + @VisibleForTesting + static ImageNameSubstitutor defaultImplementation = new DefaultImageNameSubstitutor(); + + public synchronized static ImageNameSubstitutor instance() { + if (instance == null) { + final String configuredClassName = TestcontainersConfiguration.getInstance().getImageSubstitutorClassName(); + + if (configuredClassName != null) { + log.debug("Attempting to instantiate an ImageNameSubstitutor with class: {}", configuredClassName); + ImageNameSubstitutor configuredInstance; + try { + configuredInstance = (ImageNameSubstitutor) Class.forName(configuredClassName).getConstructor().newInstance(); + } catch (Exception e) { + throw new IllegalArgumentException("Configured Image Substitutor could not be loaded: " + configuredClassName, e); + } + + log.info("Found configured ImageNameSubstitutor: {}", configuredInstance.getDescription()); + + instance = new ChainedImageNameSubstitutor( + wrapWithLogging(defaultImplementation), + wrapWithLogging(configuredInstance) + ); + } else { + instance = wrapWithLogging(defaultImplementation); + } + + log.info("Image name substitution will be performed by: {}", instance.getDescription()); + } + + return instance; + } + + private static ImageNameSubstitutor wrapWithLogging(final ImageNameSubstitutor wrappedInstance) { + return new LogWrappedImageNameSubstitutor(wrappedInstance); + } + + /** + * Substitute a {@link DockerImageName} for another, for example to replace a generic Docker Hub image name with a + * private registry copy of the image. + * + * @param original original name to be replaced + * @return a replacement name, or the original, as appropriate + */ + public abstract DockerImageName apply(DockerImageName original); + + /** + * @return a human-readable description of the substitutor + */ + protected abstract String getDescription(); + + /** + * Wrapper substitutor which logs which substitutions have been performed. + */ + static class LogWrappedImageNameSubstitutor extends ImageNameSubstitutor { + @VisibleForTesting + final ImageNameSubstitutor wrappedInstance; + + public LogWrappedImageNameSubstitutor(final ImageNameSubstitutor wrappedInstance) { + this.wrappedInstance = wrappedInstance; + } + + @Override + public DockerImageName apply(final DockerImageName original) { + final DockerImageName replacementImage = wrappedInstance.apply(original); + + if (!replacementImage.equals(original)) { + log.info("Using {} as a substitute image for {} (using image substitutor: {})", replacementImage.asCanonicalNameString(), original.asCanonicalNameString(), wrappedInstance.getDescription()); + return replacementImage; + } else { + log.debug("Did not find a substitute image for {} (using image substitutor: {})", original.asCanonicalNameString(), wrappedInstance.getDescription()); + return original; + } + } + + @Override + protected String getDescription() { + return wrappedInstance.getDescription(); + } + } + + /** + * Wrapper substitutor that passes the original image name through a default substitutor and then the configured one + */ + static class ChainedImageNameSubstitutor extends ImageNameSubstitutor { + private final ImageNameSubstitutor defaultInstance; + private final ImageNameSubstitutor configuredInstance; + + public ChainedImageNameSubstitutor(ImageNameSubstitutor defaultInstance, ImageNameSubstitutor configuredInstance) { + this.defaultInstance = defaultInstance; + this.configuredInstance = configuredInstance; + } + + @Override + public DockerImageName apply(DockerImageName original) { + return defaultInstance.andThen(configuredInstance).apply(original); + } + + @Override + protected String getDescription() { + return String.format( + "Chained substitutor of '%s' and then '%s'", + defaultInstance.getDescription(), + configuredInstance.getDescription() + ); + } + } +} diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index c37e6660654..3b0cfa1e289 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -72,7 +72,9 @@ private ResourceReaper() { @SneakyThrows(InterruptedException.class) public static String start(String hostIpAddress, DockerClient client) { - String ryukImage = TestcontainersConfiguration.getInstance().getRyukDockerImageName().asCanonicalNameString(); + String ryukImage = ImageNameSubstitutor.instance() + .apply(DockerImageName.parse("testcontainers/ryuk:0.3.0")) + .asCanonicalNameString(); DockerClientFactory.instance().checkAndPullImage(client, ryukImage); List binds = new ArrayList<>(); diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index b3c8e88a328..987301fbfb9 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -23,7 +23,6 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.Map; -import java.util.Objects; import java.util.Optional; import java.util.Properties; import java.util.concurrent.atomic.AtomicReference; @@ -104,37 +103,21 @@ public String getSocatContainerImage() { return getImage(SOCAT_IMAGE).asCanonicalNameString(); } - public DockerImageName getSocatDockerImageName() { - return getImage(SOCAT_IMAGE); - } - @Deprecated public String getVncRecordedContainerImage() { return getImage(VNC_RECORDER_IMAGE).asCanonicalNameString(); } - public DockerImageName getVncDockerImageName() { - return getImage(VNC_RECORDER_IMAGE); - } - @Deprecated public String getDockerComposeContainerImage() { return getImage(COMPOSE_IMAGE).asCanonicalNameString(); } - public DockerImageName getDockerComposeDockerImageName() { - return getImage(COMPOSE_IMAGE); - } - @Deprecated public String getTinyImage() { return getImage(ALPINE_IMAGE).asCanonicalNameString(); } - public DockerImageName getTinyDockerImageName() { - return getImage(ALPINE_IMAGE); - } - public boolean isRyukPrivileged() { return Boolean .parseBoolean(getEnvVarOrProperty("ryuk.container.privileged", "false")); @@ -145,19 +128,11 @@ public String getRyukImage() { return getImage(RYUK_IMAGE).asCanonicalNameString(); } - public DockerImageName getRyukDockerImageName() { - return getImage(RYUK_IMAGE); - } - @Deprecated public String getSSHdImage() { return getImage(SSHD_IMAGE).asCanonicalNameString(); } - public DockerImageName getSSHdDockerImageName() { - return getImage(SSHD_IMAGE); - } - public Integer getRyukTimeout() { return Integer.parseInt(getEnvVarOrProperty("ryuk.container.timeout", "30")); } @@ -167,11 +142,6 @@ public String getKafkaImage() { return getImage(KAFKA_IMAGE).asCanonicalNameString(); } - public DockerImageName getKafkaDockerImageName() { - return getImage(KAFKA_IMAGE); - } - - @Deprecated public String getOracleImage() { return getEnvVarOrUserProperty("oracle.container.image", null); @@ -182,20 +152,11 @@ public String getPulsarImage() { return getImage(PULSAR_IMAGE).asCanonicalNameString(); } - public DockerImageName getPulsarDockerImageName() { - return getImage(PULSAR_IMAGE); - } - @Deprecated public String getLocalStackImage() { return getImage(LOCALSTACK_IMAGE).asCanonicalNameString(); } - public DockerImageName getLocalstackDockerImageName() { - return getImage(LOCALSTACK_IMAGE); - } - - public boolean isDisableChecks() { return Boolean.parseBoolean(getEnvVarOrUserProperty("checks.disable", "false")); } @@ -218,6 +179,10 @@ public Integer getImagePullPauseTimeout() { return Integer.parseInt(getEnvVarOrProperty("pull.pause.timeout", "30")); } + public String getImageSubstitutorClassName() { + return getEnvVarOrProperty("image.substitutor", null); + } + @Nullable @Contract("_, !null, _ -> !null") private String getConfigurable(@NotNull final String propertyName, @Nullable final String defaultValue, Properties... propertiesSources) { @@ -279,6 +244,13 @@ public String getUserProperty(@NotNull final String propertyName, @Nullable fina return getConfigurable(propertyName, defaultValue); } + /** + * @return properties values available from user properties and classpath properties. Values set by environment + * variable are NOT included. + * @deprecated usages should be removed ASAP. See {@link TestcontainersConfiguration#getEnvVarOrProperty(String, String)}, + * {@link TestcontainersConfiguration#getEnvVarOrUserProperty(String, String)} or {@link TestcontainersConfiguration#getUserProperty(String, String)} + * for suitable replacements. + */ @Deprecated public Properties getProperties() { return Stream.of(userProperties, classpathProperties) @@ -320,17 +292,13 @@ public boolean updateUserConfig(@NonNull String prop, @NonNull String value) { private static TestcontainersConfiguration loadConfiguration() { return new TestcontainersConfiguration( readProperties(USER_CONFIG_FILE.toURI().toURL()), - Stream - .of( - TestcontainersConfiguration.class.getClassLoader(), - Thread.currentThread().getContextClassLoader() - ) - .map(it -> it.getResource(PROPERTIES_FILE_NAME)) - .filter(Objects::nonNull) + ClasspathScanner.scanFor(PROPERTIES_FILE_NAME) .map(TestcontainersConfiguration::readProperties) .reduce(new Properties(), (a, b) -> { - a.putAll(b); - return a; + // first-write-wins merging - URLs appearing first on the classpath alphabetically will take priority. + // Note that this means that file: URLs will always take priority over jar: URLs. + b.putAll(a); + return b; }), System.getenv()); } @@ -341,9 +309,9 @@ private static Properties readProperties(URL url) { try (InputStream inputStream = url.openStream()) { properties.load(inputStream); } catch (FileNotFoundException e) { - log.warn("Testcontainers config override was found on {} but the file was not found. Exception message: {}", url, ExceptionUtils.getRootCauseMessage(e)); + log.warn("Attempted to read Testcontainers configuration file at {} but the file was not found. Exception message: {}", url, ExceptionUtils.getRootCauseMessage(e)); } catch (IOException e) { - log.warn("Testcontainers config override was found on {} but could not be loaded. Exception message: {}", url, ExceptionUtils.getRootCauseMessage(e)); + log.warn("Attempted to read Testcontainers configuration file at {} but could it not be loaded. Exception message: {}", url, ExceptionUtils.getRootCauseMessage(e)); } return properties; } diff --git a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java index 030e4164f00..d484cc92eaf 100644 --- a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java +++ b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java @@ -1,5 +1,9 @@ package org.testcontainers; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.testcontainers.TestImages.TINY_IMAGE; + import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.exception.DockerException; import com.github.dockerjava.api.exception.NotFoundException; @@ -12,9 +16,6 @@ import org.testcontainers.utility.MockTestcontainersConfigurationRule; import org.testcontainers.utility.TestcontainersConfiguration; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - /** * Test for {@link DockerClientFactory}. */ @@ -30,7 +31,7 @@ public void runCommandInsideDockerShouldNotFailIfImageDoesNotExistsLocally() { try { //remove tiny image, so it will be pulled during next command run dockFactory.client() - .removeImageCmd(TestcontainersConfiguration.getInstance().getTinyDockerImageName().asCanonicalNameString()) + .removeImageCmd(TINY_IMAGE.asCanonicalNameString()) .withForce(true).exec(); } catch (NotFoundException ignored) { // Do not fail if it's not pulled yet diff --git a/core/src/test/java/org/testcontainers/TestImages.java b/core/src/test/java/org/testcontainers/TestImages.java index 3f16e88d882..7052f7e8f9d 100644 --- a/core/src/test/java/org/testcontainers/TestImages.java +++ b/core/src/test/java/org/testcontainers/TestImages.java @@ -1,7 +1,6 @@ package org.testcontainers; import org.testcontainers.utility.DockerImageName; -import org.testcontainers.utility.TestcontainersConfiguration; public interface TestImages { DockerImageName REDIS_IMAGE = DockerImageName.parse("redis:3.0.2"); @@ -9,5 +8,5 @@ public interface TestImages { DockerImageName MONGODB_IMAGE = DockerImageName.parse("mongo:3.1.5"); DockerImageName ALPINE_IMAGE = DockerImageName.parse("alpine:3.2"); DockerImageName DOCKER_REGISTRY_IMAGE = DockerImageName.parse("registry:2.7.0"); - DockerImageName TINY_IMAGE = TestcontainersConfiguration.getInstance().getTinyDockerImageName(); + DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.5"); } diff --git a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java index e49ff2880df..c99770c4fbf 100644 --- a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java +++ b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java @@ -1,9 +1,21 @@ package org.testcontainers.utility; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.testcontainers.TestImages.DOCKER_REGISTRY_IMAGE; +import static org.testcontainers.TestImages.TINY_IMAGE; + import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.async.ResultCallback; import com.github.dockerjava.api.command.PullImageResultCallback; import com.github.dockerjava.api.model.AuthConfig; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.concurrent.TimeUnit; import org.intellij.lang.annotations.Language; import org.junit.AfterClass; import org.junit.Before; @@ -18,18 +30,6 @@ import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.images.builder.ImageFromDockerfile; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.concurrent.TimeUnit; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.TestImages.DOCKER_REGISTRY_IMAGE; - /** * This test checks the integration between Testcontainers and an authenticated registry, but uses * a mock instance of {@link RegistryAuthLocator} - the purpose of the test is solely to ensure that @@ -165,7 +165,7 @@ private Path getLocalTempFile(String s) throws IOException { private static void putImageInRegistry() throws InterruptedException { // It doesn't matter which image we use for this test, but use one that's likely to have been pulled already - final String dummySourceImage = TestcontainersConfiguration.getInstance().getRyukDockerImageName().asCanonicalNameString(); + final String dummySourceImage = TINY_IMAGE.asCanonicalNameString(); client.pullImageCmd(dummySourceImage) .exec(new PullImageResultCallback()) diff --git a/core/src/test/java/org/testcontainers/utility/ClasspathScannerTest.java b/core/src/test/java/org/testcontainers/utility/ClasspathScannerTest.java new file mode 100644 index 00000000000..bdd71b0db9f --- /dev/null +++ b/core/src/test/java/org/testcontainers/utility/ClasspathScannerTest.java @@ -0,0 +1,114 @@ +package org.testcontainers.utility; + +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.net.URL; +import java.util.Collections; +import java.util.List; + +import static java.util.Arrays.asList; +import static java.util.stream.Collectors.toList; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; + +public class ClasspathScannerTest { + + private static URL FILE_A; + private static URL FILE_B; + private static URL JAR_A; + private static URL JAR_B; + private static URL FILE_C; + + @BeforeClass + public static void setUp() throws Exception { + FILE_A = new URL("file:///a/someName"); + FILE_B = new URL("file:///b/someName"); + FILE_C = new URL("file:///c/someName"); + JAR_A = new URL("jar:file:a!/someName"); + JAR_B = new URL("jar:file:b!/someName"); + } + + @Test + public void realClassLoaderLookupOccurs() { + // look for a resource that we know exists only once + final List foundURLs = ClasspathScanner.scanFor("expectedClasspathFile.txt").collect(toList()); + + assertEquals("Exactly one resource was found", 1, foundURLs.size()); + } + + @Test + public void multipleResultsOnOneClassLoaderAreFound() throws IOException { + final ClassLoader firstMockClassLoader = mock(ClassLoader.class); + when(firstMockClassLoader.getResources(eq("someName"))).thenReturn( + Collections.enumeration( + asList( + FILE_A, + FILE_B + ) + ) + ); + + final List foundURLs = ClasspathScanner.scanFor("someName", firstMockClassLoader).collect(toList()); + assertEquals( + "The expected URLs are found", + asList(FILE_A, FILE_B), + foundURLs + ); + } + + @Test + public void orderIsAlphabeticalForDeterminism() throws IOException { + final ClassLoader firstMockClassLoader = mock(ClassLoader.class); + when(firstMockClassLoader.getResources(eq("someName"))).thenReturn( + Collections.enumeration( + asList( + FILE_B, + JAR_A, + JAR_B, + FILE_A + ) + ) + ); + + final List foundURLs = ClasspathScanner.scanFor("someName", firstMockClassLoader).collect(toList()); + assertEquals( + "The expected URLs are found in the expected order", + asList(FILE_A, FILE_B, JAR_A, JAR_B), + foundURLs + ); + } + + @Test + public void multipleClassLoadersAreQueried() throws IOException { + final ClassLoader firstMockClassLoader = mock(ClassLoader.class); + when(firstMockClassLoader.getResources(eq("someName"))).thenReturn( + Collections.enumeration( + asList( + FILE_A, + FILE_B + ) + ) + ); + final ClassLoader secondMockClassLoader = mock(ClassLoader.class); + when(secondMockClassLoader.getResources(eq("someName"))).thenReturn( + Collections.enumeration( + asList( + FILE_B, // duplicate + FILE_C + ) + ) + ); + + final List foundURLs = ClasspathScanner.scanFor("someName", firstMockClassLoader, secondMockClassLoader).collect(toList()); + + assertEquals( + "The expected URLs are found", + asList(FILE_A, FILE_B, FILE_C), + foundURLs + ); + } +} diff --git a/core/src/test/java/org/testcontainers/utility/DefaultImageNameSubstitutorTest.java b/core/src/test/java/org/testcontainers/utility/DefaultImageNameSubstitutorTest.java new file mode 100644 index 00000000000..a6f7813c3d3 --- /dev/null +++ b/core/src/test/java/org/testcontainers/utility/DefaultImageNameSubstitutorTest.java @@ -0,0 +1,38 @@ +package org.testcontainers.utility; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mockito; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.eq; + +public class DefaultImageNameSubstitutorTest { + + public static final DockerImageName ORIGINAL_IMAGE = DockerImageName.parse("foo"); + public static final DockerImageName SUBSTITUTE_IMAGE = DockerImageName.parse("bar"); + private ConfigurationFileImageNameSubstitutor underTest; + + @Rule + public MockTestcontainersConfigurationRule config = new MockTestcontainersConfigurationRule(); + + @Before + public void setUp() { + underTest = new ConfigurationFileImageNameSubstitutor(TestcontainersConfiguration.getInstance()); + } + + @Test + public void testConfigurationLookup() { + Mockito + .doReturn(SUBSTITUTE_IMAGE) + .when(TestcontainersConfiguration.getInstance()) + .getConfiguredSubstituteImage(eq(ORIGINAL_IMAGE)); + + final DockerImageName substitute = underTest.apply(ORIGINAL_IMAGE); + + assertEquals("match is found", SUBSTITUTE_IMAGE, substitute); + assertTrue("compatibility is automatically set", substitute.isCompatibleWith(ORIGINAL_IMAGE)); + } +} diff --git a/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java b/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java index 9e7136ea6d3..7c794814faf 100644 --- a/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java +++ b/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java @@ -26,9 +26,8 @@ public void testNoTagTreatedAsWildcard() { /* foo:1.2.3 != foo:4.5.6 foo:1.2.3 ~= foo - foo:1.2.3 ~= foo:latest - The test is effectively making sure that no tag and `latest` tag are equivalent + The test is effectively making sure that 'no tag' is treated as a wildcard */ assertFalse("foo:4.5.6 != foo:1.2.3", subject.isCompatibleWith(DockerImageName.parse("foo:1.2.3"))); assertTrue("foo:4.5.6 ~= foo", subject.isCompatibleWith(DockerImageName.parse("foo"))); diff --git a/core/src/test/java/org/testcontainers/utility/FakeImageSubstitutor.java b/core/src/test/java/org/testcontainers/utility/FakeImageSubstitutor.java new file mode 100644 index 00000000000..f74a6c942b3 --- /dev/null +++ b/core/src/test/java/org/testcontainers/utility/FakeImageSubstitutor.java @@ -0,0 +1,13 @@ +package org.testcontainers.utility; + +public class FakeImageSubstitutor extends ImageNameSubstitutor { + @Override + public DockerImageName apply(final DockerImageName original) { + return DockerImageName.parse("transformed-" + original.asCanonicalNameString()); + } + + @Override + protected String getDescription() { + return "test implementation"; + } +} diff --git a/core/src/test/java/org/testcontainers/utility/ImageNameSubstitutorTest.java b/core/src/test/java/org/testcontainers/utility/ImageNameSubstitutorTest.java new file mode 100644 index 00000000000..97ee0213f21 --- /dev/null +++ b/core/src/test/java/org/testcontainers/utility/ImageNameSubstitutorTest.java @@ -0,0 +1,75 @@ +package org.testcontainers.utility; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mockito; + +import static org.mockito.ArgumentMatchers.eq; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; + +public class ImageNameSubstitutorTest { + + @Rule + public MockTestcontainersConfigurationRule config = new MockTestcontainersConfigurationRule(); + private ImageNameSubstitutor originalInstance; + private ImageNameSubstitutor originalDefaultImplementation; + + @Before + public void setUp() throws Exception { + originalInstance = ImageNameSubstitutor.instance; + originalDefaultImplementation = ImageNameSubstitutor.defaultImplementation; + ImageNameSubstitutor.instance = null; + ImageNameSubstitutor.defaultImplementation = Mockito.mock(ImageNameSubstitutor.class); + + Mockito + .doReturn(DockerImageName.parse("substituted-image")) + .when(ImageNameSubstitutor.defaultImplementation) + .apply(eq(DockerImageName.parse("original"))); + Mockito + .doReturn("default implementation") + .when(ImageNameSubstitutor.defaultImplementation) + .getDescription(); + } + + @After + public void tearDown() throws Exception { + ImageNameSubstitutor.instance = originalInstance; + ImageNameSubstitutor.defaultImplementation = originalDefaultImplementation; + } + + @Test + public void simpleConfigurationTest() { + Mockito + .doReturn(FakeImageSubstitutor.class.getCanonicalName()) + .when(TestcontainersConfiguration.getInstance()) + .getImageSubstitutorClassName(); + + final ImageNameSubstitutor imageNameSubstitutor = ImageNameSubstitutor.instance(); + + DockerImageName result = imageNameSubstitutor.apply(DockerImageName.parse("original")); + assertEquals( + "the image has been substituted by default then configured implementations", + "transformed-substituted-image:latest", + result.asCanonicalNameString() + ); + } + + @Test + public void testWorksWithoutConfiguredImplementation() { + Mockito + .doReturn(null) + .when(TestcontainersConfiguration.getInstance()) + .getImageSubstitutorClassName(); + + final ImageNameSubstitutor imageNameSubstitutor = ImageNameSubstitutor.instance(); + + DockerImageName result = imageNameSubstitutor.apply(DockerImageName.parse("original")); + assertEquals( + "the image has been substituted by default then configured implementations", + "substituted-image:latest", + result.asCanonicalNameString() + ); + } +} diff --git a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java index e48a2a6fdf9..ae07e2d9131 100644 --- a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java +++ b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java @@ -25,6 +25,69 @@ public void setUp() { environment = new HashMap<>(); } + @Test + public void shouldSubstituteImageNamesFromClasspathProperties() { + classpathProperties.setProperty("ryuk.container.image", "foo:version"); + assertEquals( + "an image name can be pulled from classpath properties", + DockerImageName.parse("foo:version"), + newConfig().getConfiguredSubstituteImage(DockerImageName.parse("testcontainers/ryuk:any")) + ); + } + + @Test + public void shouldSubstituteImageNamesFromUserProperties() { + userProperties.setProperty("ryuk.container.image", "foo:version"); + assertEquals( + "an image name can be pulled from user properties", + DockerImageName.parse("foo:version"), + newConfig().getConfiguredSubstituteImage(DockerImageName.parse("testcontainers/ryuk:any")) + ); + } + + @Test + public void shouldSubstituteImageNamesFromEnvironmentVariables() { + environment.put("TESTCONTAINERS_RYUK_CONTAINER_IMAGE", "foo:version"); + assertEquals( + "an image name can be pulled from an environment variable", + DockerImageName.parse("foo:version"), + newConfig().getConfiguredSubstituteImage(DockerImageName.parse("testcontainers/ryuk:any")) + ); + } + + @Test + public void shouldApplySettingsInOrder() { + assertEquals( + "precedence order for multiple sources of the same value is correct", + "default", + newConfig().getEnvVarOrProperty("key", "default") + ); + + classpathProperties.setProperty("key", "foo"); + + assertEquals( + "precedence order for multiple sources of the same value is correct", + "foo", + newConfig().getEnvVarOrProperty("key", "default") + ); + + userProperties.setProperty("key", "bar"); + + assertEquals( + "precedence order for multiple sources of the same value is correct", + "bar", + newConfig().getEnvVarOrProperty("key", "default") + ); + + environment.put("TESTCONTAINERS_KEY", "baz"); + + assertEquals( + "precedence order for multiple sources of the same value is correct", + "baz", + newConfig().getEnvVarOrProperty("key", "default") + ); + } + @Test public void shouldNotReadChecksFromClasspathProperties() { assertFalse("checks enabled by default", newConfig().isDisableChecks()); diff --git a/core/src/test/resources/expectedClasspathFile.txt b/core/src/test/resources/expectedClasspathFile.txt new file mode 100644 index 00000000000..e13c06f078f --- /dev/null +++ b/core/src/test/resources/expectedClasspathFile.txt @@ -0,0 +1 @@ +This file exists for org.testcontainers.utility.ClasspathScannerTest diff --git a/docs/examples/junit4/generic/build.gradle b/docs/examples/junit4/generic/build.gradle index 5ce878c7ba2..ff1b11f142e 100644 --- a/docs/examples/junit4/generic/build.gradle +++ b/docs/examples/junit4/generic/build.gradle @@ -4,6 +4,9 @@ dependencies { testCompile "junit:junit:4.12" testCompile project(":testcontainers") testCompile project(":selenium") + testCompile project(":mysql") + + testCompile 'mysql:mysql-connector-java:8.0.21' testCompile "org.seleniumhq.selenium:selenium-api:3.141.59" testCompile 'org.assertj:assertj-core:3.15.0' } diff --git a/docs/examples/junit4/generic/src/test/java/generic/ExampleImageNameSubstitutor.java b/docs/examples/junit4/generic/src/test/java/generic/ExampleImageNameSubstitutor.java new file mode 100644 index 00000000000..f6355312b3f --- /dev/null +++ b/docs/examples/junit4/generic/src/test/java/generic/ExampleImageNameSubstitutor.java @@ -0,0 +1,24 @@ +package generic; + +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.ImageNameSubstitutor; + +public class ExampleImageNameSubstitutor extends ImageNameSubstitutor { + + @Override + public DockerImageName apply(DockerImageName original) { + // convert the original name to something appropriate for + // our build environment + return DockerImageName.parse( + // your code goes here - silly example of capitalising + // the original name is shown + original.asCanonicalNameString().toUpperCase() + ); + } + + @Override + protected String getDescription() { + // used in logs + return "example image name substitutor"; + } +} diff --git a/docs/examples/junit4/generic/src/test/java/generic/ImageNameSubstitutionTest.java b/docs/examples/junit4/generic/src/test/java/generic/ImageNameSubstitutionTest.java new file mode 100644 index 00000000000..edc622a4104 --- /dev/null +++ b/docs/examples/junit4/generic/src/test/java/generic/ImageNameSubstitutionTest.java @@ -0,0 +1,48 @@ +package generic; + + +import generic.support.TestSpecificImageNameSubstitutor; +import org.junit.Test; +import org.testcontainers.containers.MySQLContainer; +import org.testcontainers.utility.DockerImageName; + +public class ImageNameSubstitutionTest { + + @Test + public void simpleExample() { + try ( + // directDockerHubReference { + // Referring directly to an image on Docker Hub (mysql:8.0.22) + final MySQLContainer mysql = new MySQLContainer<>( + DockerImageName.parse("mysql:8.0.22") + ) + + // start the container and use it for testing + // } + ) { + mysql.start(); + } + } + + /** + * Note that this test uses a fake image name, which will only work because + * {@link TestSpecificImageNameSubstitutor} steps in to override the substitution for this exact + * image name. + */ + @Test + public void substitutedExample() { + try ( + // hardcodedMirror { + // Referring directly to an image on a private registry - image name will vary + final MySQLContainer mysql = new MySQLContainer<>( + DockerImageName.parse("registry.mycompany.com/mirror/mysql:8.0.22") + .asCompatibleSubstituteFor("mysql") + ) + + // start the container and use it for testing + // } + ) { + mysql.start(); + } + } +} diff --git a/docs/examples/junit4/generic/src/test/java/generic/support/TestSpecificImageNameSubstitutor.java b/docs/examples/junit4/generic/src/test/java/generic/support/TestSpecificImageNameSubstitutor.java new file mode 100644 index 00000000000..5f9e92c0add --- /dev/null +++ b/docs/examples/junit4/generic/src/test/java/generic/support/TestSpecificImageNameSubstitutor.java @@ -0,0 +1,26 @@ +package generic.support; + +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.ImageNameSubstitutor; + +/** + * An {@link ImageNameSubstitutor} which makes it possible to use fake image names in + * {@link generic.ImageNameSubstitutionTest}. This implementation simply reverses a fake image name when presented, and + * is hardcoded to act upon the specific fake name in that test. + */ +public class TestSpecificImageNameSubstitutor extends ImageNameSubstitutor { + + @Override + public DockerImageName apply(final DockerImageName original) { + if (original.equals(DockerImageName.parse("registry.mycompany.com/mirror/mysql:8.0.22"))) { + return DockerImageName.parse("mysql"); + } else { + return original; + } + } + + @Override + protected String getDescription() { + return TestSpecificImageNameSubstitutor.class.getSimpleName(); + } +} diff --git a/docs/examples/junit4/generic/src/test/resources/logback-test.xml b/docs/examples/junit4/generic/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..1378a823a63 --- /dev/null +++ b/docs/examples/junit4/generic/src/test/resources/logback-test.xml @@ -0,0 +1,14 @@ + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + + + + + + + + + + diff --git a/docs/examples/junit4/generic/src/test/resources/testcontainers.properties b/docs/examples/junit4/generic/src/test/resources/testcontainers.properties new file mode 100644 index 00000000000..19bf8288d33 --- /dev/null +++ b/docs/examples/junit4/generic/src/test/resources/testcontainers.properties @@ -0,0 +1 @@ +image.substitutor=generic.support.TestSpecificImageNameSubstitutor diff --git a/docs/features/configuration.md b/docs/features/configuration.md index e3ac0fb6ac3..b567a8f564f 100644 --- a/docs/features/configuration.md +++ b/docs/features/configuration.md @@ -2,14 +2,26 @@ You can override some default properties if your environment requires that. -## Configuration file location +## Configuration locations The configuration will be loaded from multiple locations. Properties are considered in the following order: -1. `.testcontainers.properties` in user's home folder. Example locations: +1. Environment variables +2. `.testcontainers.properties` in user's home folder. Example locations: **Linux:** `/home/myuser/.testcontainers.properties` **Windows:** `C:/Users/myuser/.testcontainers.properties` **macOS:** `/Users/myuser/.testcontainers.properties` -2. `testcontainers.properties` on classpath +3. `testcontainers.properties` on the classpath. + +Note that when using environment variables, configuration property names should be set in upper +case with underscore separators, preceded by `TESTCONTAINERS_` - e.g. `checks.disable` becomes +`TESTCONTAINERS_CHECKS_DISABLE`. + +The classpath `testcontainers.properties` file may exist within the local codebase (e.g. within the `src/test/resources` directory) or within library dependencies that you may have. +Any such configuration files will have their contents merged. +If any keys conflict, the value will be taken on the basis of the first value found in: + +* 'local' classpath (i.e. where the URL of the file on the classpath begins with `file:`), then +* other classpath locations (i.e. JAR files) - considered in _alphabetical order of path_ to provide deterministic ordering. ## Disabling the startup checks > **checks.disable = [true|false]** @@ -26,18 +38,30 @@ It takes a couple of seconds, but if you want to speed up your tests, you can di ## Customizing images +!!! note + This approach is discouraged and deprecated, but is documented for completeness. + Overriding individual image names via configuration may be removed in 2021. + See [Image Name Substitution](./image_name_substitution.md) for other strategies for substituting image names to pull from other registries. + + Testcontainers uses public Docker images to perform different actions like startup checks, VNC recording and others. Some companies disallow the usage of Docker Hub, but you can override `*.image` properties with your own images from your private registry to workaround that. +> **ryuk.container.image = testcontainers/ryuk:0.3.0** +> Performs fail-safe cleanup of containers, and always required (unless [Ryuk is disabled](#disabling-ryuk)) + > **tinyimage.container.image = alpine:3.5** -> Used by Testcontainers' core +> Used to check whether images can be pulled at startup, and always required (unless [startup checks are disabled](#disabling-the-startup-checks)) + +> **sshd.container.image = testcontainers/sshd:1.0.0** +> Required if [exposing host ports to containers](./networking.md#exposing-host-ports-to-the-container) > **vncrecorder.container.image = testcontainers/vnc-recorder:1.1.0** -> Used by VNC recorder in Testcontainers' Seleniun integration +> Used by VNC recorder in Testcontainers' Selenium integration -> **ambassador.container.image = richnorth/ambassador:latest** +> **socat.container.image = alpine/socat** > **compose.container.image = docker/compose:1.8.0** -> Used by Docker Compose integration +> Required if using [Docker Compose](../modules/docker_compose.md) > **kafka.container.image = confluentinc/cp-kafka** > Used by KafkaContainer @@ -45,11 +69,8 @@ Some companies disallow the usage of Docker Hub, but you can override `*.image` > **localstack.container.image = localstack/localstack** > Used by LocalStack -Another possibility is to set up a registry mirror in your environment so that all images are pulled from there and not directly from Docker Hub. -For more information, see the [official Docker documentation about "Registry as a pull through cache"](https://docs.docker.com/registry/recipes/mirror/). - -!!!tip - Registry mirror currently only works for Docker images with image name that has no registry specified (for example, for Docker image `mariadb:10.3.6`, it works, for Docker image `quay.io/something/else`, not). +> **pulsar.container.image = apachepulsar/pulsar:2.2.0** +> Used by Apache Pulsar ## Customizing Ryuk resource reaper diff --git a/docs/features/image_name_substitution.md b/docs/features/image_name_substitution.md new file mode 100644 index 00000000000..0cf2d4cf8f0 --- /dev/null +++ b/docs/features/image_name_substitution.md @@ -0,0 +1,117 @@ +# Image name substitution + +Testcontainers supports automatic substitution of Docker image names. + +This allows replacement of an image name specified in test code with an alternative name - for example, to replace the +name of a Docker Hub image dependency with an alternative hosted on a private image registry. + +This is advisable to avoid [Docker Hub rate limiting](./pull_rate_limiting.md), and some companies will prefer this for policy reasons. + +This page describes four approaches for image name substitution: + +* [Manual substitution](#manual-substitution) - not relying upon an automated approach +* Using an Image Name Substitutor: + * [Developing a custom function for transforming image names on the fly](#developing-a-custom-function-for-transforming-image-names-on-the-fly) + * [Overriding image names individually in configuration](#overriding-image-names-individually-in-configuration) + +It is assumed that you have already set up a private registry hosting [all the Docker images your build requires](./pull_rate_limiting.md#which-images-are-used-by-testcontainers). + + + + +## Manual substitution + +Consider this if: + +* You use only a few images and updating code is not a chore +* All developers and CI machines in your organisation have access to a common registry server +* You also use one of the automated mechanisms to substitute [the images that Testcontainers itself requires](./pull_rate_limiting.md#which-images-are-used-by-testcontainers) + +This approach simply entails modifying test code manually, e.g. changing: + +For example, you may have a test that uses the `mysql` container image from Docker Hub: + + +[Direct Docker Hub image name](../examples/junit4/generic/src/test/java/generic/ImageNameSubstitutionTest.java) inside_block:directDockerHubReference + + +to: + + +[Private registry image name](../examples/junit4/generic/src/test/java/generic/ImageNameSubstitutionTest.java) inside_block:hardcodedMirror + + + + + + + + + + +## Developing a custom function for transforming image names on the fly + +Consider this if: + +* You have complex rules about which private registry images should be used as substitutes, e.g.: + * non-deterministic mapping of names meaning that a [name prefix](#adding-a-registry-url-prefix-to-image-names-automatically) cannot be used + * rules depending upon developer identity or location +* or you wish to add audit logging of images used in the build +* or you wish to prevent accidental usage of images that are not on an approved list + +In this case, image name references in code are **unchanged**. +i.e. you would leave as-is: + + +[Unchanged direct Docker Hub image name](../examples/junit4/generic/src/test/java/generic/ImageNameSubstitutionTest.java) inside_block:directDockerHubReference + + +You can implement a custom image name substitutor by: + +* subclassing `org.testcontainers.utility.ImageNameSubstitutor` +* configuring Testcontainers to use your custom implementation + +The following is an example image substitutor implementation: + + +[Example Image Substitutor](../examples/junit4/generic/src/test/java/generic/ExampleImageNameSubstitutor.java) block:ExampleImageNameSubstitutor + + +Testcontainers can be configured to find it at runtime via configuration. +To do this, create or modify a file on the classpath named `testcontainers.properties`. + +For example: + +```text tab="src/test/resources/testcontainers.properties" +image.substitutor=com.mycompany.testcontainers.ExampleImageNameSubstitutor +``` + +Note that it is also possible to provide this same configuration property: + +* in a `testcontainers.properties` file at the root of a library JAR file (useful if you wish to distribute a drop-in image substitutor JAR within an organization) +* in a properties file in the user's home directory (`~/.testcontainers.properties`; note the leading `.`) +* or as an environment variable (e.g. `TESTCONTAINERS_IMAGE_SUBSTITUTOR=com.mycompany.testcontainers.ExampleImageNameSubstitutor`). + +Please see [the documentation on configuration mechanisms](./configuration.md) for more information. + + +## Overriding image names individually in configuration + +!!! note + This approach is discouraged and deprecated, but is documented for completeness. + Please consider one of the other approaches outlined in this page instead. + Overriding individual image names via configuration may be removed in 2021. + +Consider this if: + +* You have many references to image names in code and changing them is impractical, and +* None of the other options are practical for you + +In this case, image name references in code are left **unchanged**. +i.e. you would leave as-is: + + +[Unchanged direct Docker Hub image name](../examples/junit4/generic/src/test/java/generic/ImageNameSubstitutionTest.java) inside_block:directDockerHubReference + + +You can force Testcontainers to substitute in a different image [using a configuration file](./configuration.md), which allows some (but not all) container names to be substituted. diff --git a/docs/features/pull_rate_limiting.md b/docs/features/pull_rate_limiting.md new file mode 100644 index 00000000000..be266545afb --- /dev/null +++ b/docs/features/pull_rate_limiting.md @@ -0,0 +1,19 @@ +# Image Registry rate limiting + +As of November 2020 Docker Hub pulls are rate limited. +As Testcontainers uses Docker Hub for standard images, some users may hit these rate limits and should mitigate accordingly. + +Suggested mitigations are noted in [this issue](https://github.com/testcontainers/testcontainers-java/issues/3099) at present. + +## Which images are used by Testcontainers? + +As of the current version of Testcontainers ({{latest_version}}): + +* every image directly used by your tests +* images pulled by Testcontainers itself to support functionality: + * [`testcontainers/ryuk`](https://hub.docker.com/r/testcontainers/ryuk) - performs fail-safe cleanup of containers, and always required (unless [Ryuk is disabled](./configuration.md#disabling-ryuk)) + * [`alpine`](https://hub.docker.com/r/_/alpine) - used to check whether images can be pulled at startup, and always required (unless [startup checks are disabled](./configuration.md#disabling-the-startup-checks)) + * [`testcontainers/sshd`](https://hub.docker.com/r/testcontainers/sshd) - required if [exposing host ports to containers](./networking.md#exposing-host-ports-to-the-container) + * [`testcontainers/vnc-recorder`](https://hub.docker.com/r/testcontainers/vnc-recorder) - required if using [Webdriver containers](../modules/webdriver_containers.md) and using the screen recording feature + * [`docker/compose`](https://hub.docker.com/r/docker/compose) - required if using [Docker Compose](../modules/docker_compose.md) + * [`alpine/socat`](https://hub.docker.com/r/alpine/socat) - required if using [Docker Compose](../modules/docker_compose.md) diff --git a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java index 37f99bf970b..12867239251 100644 --- a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java +++ b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java @@ -4,7 +4,6 @@ import lombok.SneakyThrows; import org.testcontainers.images.builder.Transferable; import org.testcontainers.utility.DockerImageName; -import org.testcontainers.utility.TestcontainersConfiguration; import java.nio.charset.StandardCharsets; import java.util.stream.Collectors; @@ -36,7 +35,7 @@ public class KafkaContainer extends GenericContainer { */ @Deprecated public KafkaContainer() { - this(TestcontainersConfiguration.getInstance().getKafkaDockerImageName().withTag(DEFAULT_TAG)); + this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); } /** @@ -44,7 +43,7 @@ public KafkaContainer() { */ @Deprecated public KafkaContainer(String confluentPlatformVersion) { - this(TestcontainersConfiguration.getInstance().getKafkaDockerImageName().withTag(confluentPlatformVersion)); + this(DEFAULT_IMAGE_NAME.withTag(confluentPlatformVersion)); } public KafkaContainer(final DockerImageName dockerImageName) { diff --git a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java index 4f721424bb6..263653822e6 100644 --- a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java +++ b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java @@ -61,7 +61,7 @@ public void testUsageWithSpecificImage() throws Exception { @Test public void testUsageWithVersion() throws Exception { try ( - KafkaContainer kafka = new KafkaContainer("5.4.3") + KafkaContainer kafka = new KafkaContainer("5.5.1") ) { kafka.start(); testKafkaFunctionality(kafka.getBootstrapServers()); diff --git a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java index 7532ebe0b3d..b8416afb1ed 100644 --- a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java +++ b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java @@ -14,7 +14,6 @@ import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.utility.ComparableVersion; import org.testcontainers.utility.DockerImageName; -import org.testcontainers.utility.TestcontainersConfiguration; import java.net.InetAddress; import java.net.URI; @@ -65,7 +64,7 @@ public class LocalStackContainer extends GenericContainer { */ @Deprecated public LocalStackContainer() { - this(TestcontainersConfiguration.getInstance().getLocalstackDockerImageName().withTag(DEFAULT_TAG)); + this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); } /** @@ -73,7 +72,7 @@ public LocalStackContainer() { */ @Deprecated public LocalStackContainer(String version) { - this(TestcontainersConfiguration.getInstance().getLocalstackDockerImageName().withTag(version)); + this(DEFAULT_IMAGE_NAME.withTag(version)); } /** diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java index b5700c1e84e..0a66ad6fd27 100644 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java @@ -22,8 +22,7 @@ public class OracleContainer extends JdbcDatabaseContainer { private String password = "oracle"; private static String resolveImageName() { - String image = TestcontainersConfiguration.getInstance() - .getProperties().getProperty("oracle.container.image"); + String image = TestcontainersConfiguration.getInstance().getOracleImage(); if (image == null) { throw new IllegalStateException("An image to use for Oracle containers must be configured. " + diff --git a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java index bceff8927d7..b3c836b56a5 100644 --- a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java +++ b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java @@ -3,7 +3,6 @@ import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.containers.wait.strategy.WaitAllStrategy; import org.testcontainers.utility.DockerImageName; -import org.testcontainers.utility.TestcontainersConfiguration; /** * This container wraps Apache Pulsar running in standalone mode @@ -25,7 +24,7 @@ public class PulsarContainer extends GenericContainer { */ @Deprecated public PulsarContainer() { - this(TestcontainersConfiguration.getInstance().getPulsarDockerImageName().withTag(DEFAULT_TAG)); + this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); } /** @@ -33,7 +32,7 @@ public PulsarContainer() { */ @Deprecated public PulsarContainer(String pulsarVersion) { - this(TestcontainersConfiguration.getInstance().getPulsarDockerImageName().withTag(pulsarVersion)); + this(DEFAULT_IMAGE_NAME.withTag(pulsarVersion)); } public PulsarContainer(final DockerImageName dockerImageName) { diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy index 526c1fe5c42..9698e7d433a 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy @@ -1,11 +1,10 @@ -package org.testcontainers.spock; +package org.testcontainers.spock import org.testcontainers.utility.DockerImageName -import org.testcontainers.utility.TestcontainersConfiguration; interface SpockTestImages { DockerImageName MYSQL_IMAGE = DockerImageName.parse("mysql:5.7.22") DockerImageName POSTGRES_TEST_IMAGE = DockerImageName.parse("postgres:9.6.12") DockerImageName HTTPD_IMAGE = DockerImageName.parse("httpd:2.4-alpine") - DockerImageName TINY_IMAGE = TestcontainersConfiguration.getInstance().getTinyDockerImageName() + DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.5") } From b28f6efafa8f60e70f95670fecfde29eafbf4e4b Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 5 Nov 2020 20:12:57 +0000 Subject: [PATCH 1725/5615] Fix missing links to new docs from site menu --- docs/features/image_name_substitution.md | 6 +++--- .../image_registry_rate_limiting.md} | 6 +++--- mkdocs.yml | 2 ++ 3 files changed, 8 insertions(+), 6 deletions(-) rename docs/{features/pull_rate_limiting.md => supported_docker_environment/image_registry_rate_limiting.md} (81%) diff --git a/docs/features/image_name_substitution.md b/docs/features/image_name_substitution.md index 0cf2d4cf8f0..45ea6038b49 100644 --- a/docs/features/image_name_substitution.md +++ b/docs/features/image_name_substitution.md @@ -5,7 +5,7 @@ Testcontainers supports automatic substitution of Docker image names. This allows replacement of an image name specified in test code with an alternative name - for example, to replace the name of a Docker Hub image dependency with an alternative hosted on a private image registry. -This is advisable to avoid [Docker Hub rate limiting](./pull_rate_limiting.md), and some companies will prefer this for policy reasons. +This is advisable to avoid [Docker Hub rate limiting](../supported_docker_environment/image_registry_rate_limiting.md), and some companies will prefer this for policy reasons. This page describes four approaches for image name substitution: @@ -14,7 +14,7 @@ This page describes four approaches for image name substitution: * [Developing a custom function for transforming image names on the fly](#developing-a-custom-function-for-transforming-image-names-on-the-fly) * [Overriding image names individually in configuration](#overriding-image-names-individually-in-configuration) -It is assumed that you have already set up a private registry hosting [all the Docker images your build requires](./pull_rate_limiting.md#which-images-are-used-by-testcontainers). +It is assumed that you have already set up a private registry hosting [all the Docker images your build requires](../supported_docker_environment/image_registry_rate_limiting.md#which-images-are-used-by-testcontainers). @@ -25,7 +25,7 @@ Consider this if: * You use only a few images and updating code is not a chore * All developers and CI machines in your organisation have access to a common registry server -* You also use one of the automated mechanisms to substitute [the images that Testcontainers itself requires](./pull_rate_limiting.md#which-images-are-used-by-testcontainers) +* You also use one of the automated mechanisms to substitute [the images that Testcontainers itself requires](../supported_docker_environment/image_registry_rate_limiting.md#which-images-are-used-by-testcontainers) This approach simply entails modifying test code manually, e.g. changing: diff --git a/docs/features/pull_rate_limiting.md b/docs/supported_docker_environment/image_registry_rate_limiting.md similarity index 81% rename from docs/features/pull_rate_limiting.md rename to docs/supported_docker_environment/image_registry_rate_limiting.md index be266545afb..a136a12b25e 100644 --- a/docs/features/pull_rate_limiting.md +++ b/docs/supported_docker_environment/image_registry_rate_limiting.md @@ -11,9 +11,9 @@ As of the current version of Testcontainers ({{latest_version}}): * every image directly used by your tests * images pulled by Testcontainers itself to support functionality: - * [`testcontainers/ryuk`](https://hub.docker.com/r/testcontainers/ryuk) - performs fail-safe cleanup of containers, and always required (unless [Ryuk is disabled](./configuration.md#disabling-ryuk)) - * [`alpine`](https://hub.docker.com/r/_/alpine) - used to check whether images can be pulled at startup, and always required (unless [startup checks are disabled](./configuration.md#disabling-the-startup-checks)) - * [`testcontainers/sshd`](https://hub.docker.com/r/testcontainers/sshd) - required if [exposing host ports to containers](./networking.md#exposing-host-ports-to-the-container) + * [`testcontainers/ryuk`](https://hub.docker.com/r/testcontainers/ryuk) - performs fail-safe cleanup of containers, and always required (unless [Ryuk is disabled](../features/configuration.md#disabling-ryuk)) + * [`alpine`](https://hub.docker.com/r/_/alpine) - used to check whether images can be pulled at startup, and always required (unless [startup checks are disabled](../features/configuration.md#disabling-the-startup-checks)) + * [`testcontainers/sshd`](https://hub.docker.com/r/testcontainers/sshd) - required if [exposing host ports to containers](../features/networking.md#exposing-host-ports-to-the-container) * [`testcontainers/vnc-recorder`](https://hub.docker.com/r/testcontainers/vnc-recorder) - required if using [Webdriver containers](../modules/webdriver_containers.md) and using the screen recording feature * [`docker/compose`](https://hub.docker.com/r/docker/compose) - required if using [Docker Compose](../modules/docker_compose.md) * [`alpine/socat`](https://hub.docker.com/r/alpine/socat) - required if using [Docker Compose](../modules/docker_compose.md) diff --git a/mkdocs.yml b/mkdocs.yml index 0fa9b2b4c91..90404a80749 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -36,6 +36,7 @@ nav: - features/container_logs.md - features/creating_images.md - features/configuration.md + - features/image_name_substitution.md - features/advanced_options.md - Modules: - Databases: @@ -86,6 +87,7 @@ nav: - supported_docker_environment/continuous_integration/bitbucket_pipelines.md - supported_docker_environment/windows.md - supported_docker_environment/logging_config.md + - supported_docker_environment/image_registry_rate_limiting.md - Getting help: getting_help.md - Contributing: - contributing.md From c9be54f7ee5a47692f3cc875452cc63bf0a92004 Mon Sep 17 00:00:00 2001 From: Piotr Findeisen Date: Fri, 6 Nov 2020 14:04:28 +0100 Subject: [PATCH 1726/5615] Add Presto project to the list of Testcontainers users (#3435) --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index 28ca2453993..22fa9c93738 100644 --- a/docs/index.md +++ b/docs/index.md @@ -162,6 +162,7 @@ A huge thank you to our sponsors: * [XWiki](https://xwiki.org) - [Testing XWiki](https://dev.xwiki.org/xwiki/bin/view/Community/Testing/DockerTesting/) under all [supported configurations](https://dev.xwiki.org/xwiki/bin/view/Community/SupportStrategy/) * [Apache SkyWalking](http://github.com/apache/skywalking) - End-to-end testing of the Apache SkyWalking, and plugin tests of its subproject, [Apache SkyWalking Python](http://github.com/apache/skywalking-python), and of its eco-system built by the community, like [SkyAPM NodeJS Agent](http://github.com/SkyAPM/nodejs) * [jOOQ](https://www.jooq.org) - Integration testing all of jOOQ with a variety of RDBMS +* [Presto](http://prestosql.io/) - Integration testing all Presto core & connectors, including tests of multi-node deployments and security configurations. ## License From d57a2bbada8c25f8f380b6258e5b259e02f34954 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 6 Nov 2020 14:05:10 +0100 Subject: [PATCH 1727/5615] Update mkdocs.yml to the latest release --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 90404a80749..618b9679291 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -94,4 +94,4 @@ nav: - contributing_docs.md - bounty.md extra: - latest_version: 1.15.0-rc2 + latest_version: 1.15.0 From e2c55a236b39b515bfe32294e875bf83cc3251bd Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 8 Nov 2020 11:42:26 +0000 Subject: [PATCH 1728/5615] Selenium: Restore getDockerImageForCapabilities method to public scope (#3441) --- .../containers/BrowserWebDriverContainer.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index 884cd33a9dd..1ceaf67ad2c 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -189,7 +189,7 @@ protected void configure() { customImageName.assertCompatibleWith(COMPATIBLE_IMAGES); super.setDockerImageName(customImageName.asCanonicalNameString()); } else { - DockerImageName standardImageForCapabilities = getImageForCapabilities(capabilities, seleniumVersion); + DockerImageName standardImageForCapabilities = getStandardImageForCapabilities(capabilities, seleniumVersion); super.setDockerImageName(standardImageForCapabilities.asCanonicalNameString()); } @@ -218,7 +218,22 @@ protected void configure() { setStartupAttempts(3); } - private static DockerImageName getImageForCapabilities(Capabilities capabilities, String seleniumVersion) { + /** + * @param capabilities a {@link Capabilities} object for either Chrome or Firefox + * @param seleniumVersion the version of selenium in use + * @return an image name for the default standalone Docker image for the appropriate browser + * + * @deprecated note that this method is deprecated and may be removed in the future. The no-args + * {@link BrowserWebDriverContainer#BrowserWebDriverContainer()} combined with the + * {@link BrowserWebDriverContainer#withCapabilities(Capabilities)} method should be considered. A decision on + * removal of this deprecated method will be taken at a future date. + */ + @Deprecated + public static String getDockerImageForCapabilities(Capabilities capabilities, String seleniumVersion) { + return getStandardImageForCapabilities(capabilities, seleniumVersion).asCanonicalNameString(); + } + + private static DockerImageName getStandardImageForCapabilities(Capabilities capabilities, String seleniumVersion) { String browserName = capabilities.getBrowserName(); switch (browserName) { case BrowserType.CHROME: From aa01d7a7f8305d068f6e60113ca1b454d0a83759 Mon Sep 17 00:00:00 2001 From: Piotr Findeisen Date: Sun, 8 Nov 2020 12:55:48 +0100 Subject: [PATCH 1729/5615] Update Presto URL to use https in 'Who is using' list (#3438) --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 22fa9c93738..2351837f8af 100644 --- a/docs/index.md +++ b/docs/index.md @@ -162,7 +162,7 @@ A huge thank you to our sponsors: * [XWiki](https://xwiki.org) - [Testing XWiki](https://dev.xwiki.org/xwiki/bin/view/Community/Testing/DockerTesting/) under all [supported configurations](https://dev.xwiki.org/xwiki/bin/view/Community/SupportStrategy/) * [Apache SkyWalking](http://github.com/apache/skywalking) - End-to-end testing of the Apache SkyWalking, and plugin tests of its subproject, [Apache SkyWalking Python](http://github.com/apache/skywalking-python), and of its eco-system built by the community, like [SkyAPM NodeJS Agent](http://github.com/SkyAPM/nodejs) * [jOOQ](https://www.jooq.org) - Integration testing all of jOOQ with a variety of RDBMS -* [Presto](http://prestosql.io/) - Integration testing all Presto core & connectors, including tests of multi-node deployments and security configurations. +* [Presto](https://prestosql.io) - Integration testing all Presto core & connectors, including tests of multi-node deployments and security configurations. ## License From dc9e9c14199caca9d3b130d058cd3de7677ea404 Mon Sep 17 00:00:00 2001 From: eyalkoren <41850454+eyalkoren@users.noreply.github.com> Date: Sat, 21 Nov 2020 18:08:26 +0200 Subject: [PATCH 1730/5615] Search Oracle image name in classpath (#3439) Not sure why the new code looks for the Oracle image name only in the user home directory and not in the config file placed in the classpath. This brakes my test. --- .../org/testcontainers/utility/TestcontainersConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 987301fbfb9..47c274c05e6 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -144,7 +144,7 @@ public String getKafkaImage() { @Deprecated public String getOracleImage() { - return getEnvVarOrUserProperty("oracle.container.image", null); + return getEnvVarOrProperty("oracle.container.image", null); } @Deprecated From 47c2988ef04c3d1ba7df8b5437d792e189b048f1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 21 Nov 2020 16:10:01 +0000 Subject: [PATCH 1731/5615] Bump aws-java-sdk-dynamodb from 1.11.882 to 1.11.901 in /modules/dynalite (#3472) Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.11.882 to 1.11.901. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.882...1.11.901) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/dynalite/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 8aa9959ea13..5f6ab14d939 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Dynalite" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.882' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.901' testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.882' } From 9ceca91d672e814e338a1ec5073b14144e188dfd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 21 Nov 2020 16:10:19 +0000 Subject: [PATCH 1732/5615] Bump elasticsearch-rest-client from 7.9.2 to 7.10.0 in /modules/elasticsearch (#3474) Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 7.9.2 to 7.10.0. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.9.2...v7.10.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 126722759ec..3b7ef456875 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,6 +2,6 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') - testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.9.2" + testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.10.0" testCompile "org.elasticsearch.client:transport:7.9.2" } From 4f053283075e21f32044286d0237c749d3733dcd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 21 Nov 2020 16:10:27 +0000 Subject: [PATCH 1733/5615] Bump assertj-core from 3.18.0 to 3.18.1 in /modules/neo4j (#3477) Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.18.0 to 3.18.1. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.18.0...assertj-core-3.18.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 8d7f59e564e..9aab6b18298 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -34,5 +34,5 @@ dependencies { compile project(":testcontainers") testCompile "org.neo4j.driver:neo4j-java-driver:1.7.5" - testCompile 'org.assertj:assertj-core:3.18.0' + testCompile 'org.assertj:assertj-core:3.18.1' } From 60018dfa586eb62d46060c0ad6403295a238df8e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 21 Nov 2020 16:10:51 +0000 Subject: [PATCH 1734/5615] Bump assertj-core from 3.17.2 to 3.18.1 in /modules/database-commons (#3479) Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.17.2 to 3.18.1. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.17.2...assertj-core-3.18.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/database-commons/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/database-commons/build.gradle b/modules/database-commons/build.gradle index e6d6a1812da..dacd8a500ac 100644 --- a/modules/database-commons/build.gradle +++ b/modules/database-commons/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: Database-Commons" dependencies { compile project(':testcontainers') - testCompile 'org.assertj:assertj-core:3.17.2' + testCompile 'org.assertj:assertj-core:3.18.1' } From c381a64a6623fe71e95cfb73173907246a21e685 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 23 Nov 2020 11:32:25 +0100 Subject: [PATCH 1735/5615] Update to docker-java 3.2.6 (#3498) --- core/build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index 723b14fd0e6..6e3df56758a 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -173,9 +173,9 @@ dependencies { compile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' - compile "com.github.docker-java:docker-java-api:3.2.5" + compile "com.github.docker-java:docker-java-api:3.2.6" - shaded ('com.github.docker-java:docker-java-core:3.2.5') { + shaded ('com.github.docker-java:docker-java-core:3.2.6') { exclude(group: 'com.github.docker-java', module: 'docker-java-api') exclude(group: 'com.github.docker-java', module: 'docker-java-transport') exclude(group: 'com.fasterxml.jackson.core', module: 'jackson-annotations') @@ -184,13 +184,13 @@ dependencies { exclude(group: 'org.apache.commons', module: 'commons-compress') } - shaded ('com.github.docker-java:docker-java-transport-okhttp:3.2.5') { + shaded ('com.github.docker-java:docker-java-transport-okhttp:3.2.6') { exclude(group: 'com.github.docker-java', module: 'docker-java-core') exclude(group: 'net.java.dev.jna') exclude(group: 'org.slf4j') } - compile 'com.github.docker-java:docker-java-transport-zerodep:3.2.5' + compile 'com.github.docker-java:docker-java-transport-zerodep:3.2.6' shaded "org.yaml:snakeyaml:1.27" From 4e27522e9348059e3ee516c3eaf021da493762a7 Mon Sep 17 00:00:00 2001 From: sullis Date: Tue, 24 Nov 2020 11:24:59 -0800 Subject: [PATCH 1736/5615] Add toxiproxy to dependabot.yml (#3502) --- .github/dependabot.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 8abda8d8c5a..d854a9aa823 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -122,6 +122,11 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/toxiproxy" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/vault" schedule: From e9a8ac9e3a9a9db6134ff07232c7e2363a5a99cf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Nov 2020 19:28:02 +0000 Subject: [PATCH 1737/5615] Bump tomcat-jdbc from 9.0.39 to 9.0.40 in /modules/jdbc-test (#3513) Bumps tomcat-jdbc from 9.0.39 to 9.0.40. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 99841c1d62a..8523ca5a3d8 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -15,7 +15,7 @@ dependencies { } compile 'commons-dbutils:commons-dbutils:1.7' - compile 'org.apache.tomcat:tomcat-jdbc:9.0.39' + compile 'org.apache.tomcat:tomcat-jdbc:9.0.40' compile 'org.vibur:vibur-dbcp:25.0' compile 'mysql:mysql-connector-java:8.0.22' } From 1a2aa0e662669b86e7c285f8ca485ab5b22f558e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Nov 2020 19:28:09 +0000 Subject: [PATCH 1738/5615] Bump tomcat-jdbc from 9.0.39 to 9.0.40 in /modules/jdbc (#3509) Bumps tomcat-jdbc from 9.0.39 to 9.0.40. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 2045461785e..12a53f7cfbc 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -7,7 +7,7 @@ dependencies { compileOnly 'org.jetbrains:annotations:20.1.0' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.vibur:vibur-dbcp:25.0' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.39' + testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.40' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'com.googlecode.junit-toolbox:junit-toolbox:2.4' } From 1a2820fa344cbb60a4b156874bcaa04536824f78 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Nov 2020 19:30:07 +0000 Subject: [PATCH 1739/5615] Bump assertj-core from 3.17.2 to 3.18.1 in /modules/pulsar (#3475) Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.17.2 to 3.18.1. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.17.2...assertj-core-3.18.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 2d2ded0c765..e09ff42f357 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -4,6 +4,6 @@ dependencies { compile project(':testcontainers') testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.6.1' - testCompile group: 'org.assertj', name: 'assertj-core', version: '3.17.2' + testCompile group: 'org.assertj', name: 'assertj-core', version: '3.18.1' testCompile group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.6.1' } From 6565fd2f5b020d341ac30bd353c2390396928ff2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Nov 2020 19:30:15 +0000 Subject: [PATCH 1740/5615] Bump assertj-core from 3.17.2 to 3.18.1 in /modules/kafka (#3469) Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.17.2 to 3.18.1. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.17.2...assertj-core-3.18.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index 95d844c7a41..4454823bda4 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -4,6 +4,6 @@ dependencies { compile project(':testcontainers') testCompile 'org.apache.kafka:kafka-clients:2.6.0' - testCompile 'org.assertj:assertj-core:3.17.2' + testCompile 'org.assertj:assertj-core:3.18.1' testCompile 'com.google.guava:guava:23.0' } From 9ccb4432f7cbb827a8a0fdd6b918369646a55258 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Nov 2020 19:30:23 +0000 Subject: [PATCH 1741/5615] Bump assertj-core from 3.18.0 to 3.18.1 in /modules/junit-jupiter (#3470) Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.18.0 to 3.18.1. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.18.0...assertj-core-3.18.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 7986c067c89..520cff37254 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -12,7 +12,7 @@ dependencies { testCompile ('org.mockito:mockito-core:3.5.15') { exclude(module: 'hamcrest-core') } - testCompile 'org.assertj:assertj-core:3.18.0' + testCompile 'org.assertj:assertj-core:3.18.1' testCompile 'org.junit.jupiter:junit-jupiter-params:5.7.0' testRuntime 'org.postgresql:postgresql:42.2.18' From a3f0a16d3825a69aa446bd6462f99a2ecda4d756 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Nov 2020 19:30:56 +0000 Subject: [PATCH 1742/5615] Bump assertj-core from 3.17.2 to 3.18.1 in /core (#3471) Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.17.2 to 3.18.1. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.17.2...assertj-core-3.18.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- core/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index 6e3df56758a..ed0fc00d4f6 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -211,12 +211,12 @@ dependencies { // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest testCompile files('testlib/repo/fakejar/fakejar/0/fakejar-0.jar') - testCompile 'org.assertj:assertj-core:3.17.2' + testCompile 'org.assertj:assertj-core:3.18.1' testCompile project(':test-support') jarFileTestCompileOnly "org.projectlombok:lombok:${lombok.version}" jarFileTestAnnotationProcessor "org.projectlombok:lombok:${lombok.version}" jarFileTestCompile 'junit:junit:4.12' - jarFileTestCompile 'org.assertj:assertj-core:3.17.1' + jarFileTestCompile 'org.assertj:assertj-core:3.18.1' jarFileTestCompile 'org.ow2.asm:asm-debug-all:5.2' } From cf8c349e43ff05f7364c645cebb9061fda90aabf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Nov 2020 19:44:04 +0000 Subject: [PATCH 1743/5615] Bump transport from 7.9.2 to 7.10.0 in /modules/elasticsearch (#3473) Bumps [transport](https://github.com/elastic/elasticsearch) from 7.9.2 to 7.10.0. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.9.2...v7.10.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 3b7ef456875..bfbfbfae9ce 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,5 +3,5 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.10.0" - testCompile "org.elasticsearch.client:transport:7.9.2" + testCompile "org.elasticsearch.client:transport:7.10.0" } From cb5e416504e74dfdf5d26640a9f0076f44df1d05 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 28 Nov 2020 09:42:59 +0000 Subject: [PATCH 1744/5615] Bump toxiproxy-java from 2.1.3 to 2.1.4 in /modules/toxiproxy (#3524) Bumps [toxiproxy-java](https://github.com/trekawek/toxiproxy-java) from 2.1.3 to 2.1.4. - [Release notes](https://github.com/trekawek/toxiproxy-java/releases) - [Changelog](https://github.com/trekawek/toxiproxy-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/trekawek/toxiproxy-java/compare/toxiproxy-java-2.1.3...toxiproxy-java-2.1.4) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/toxiproxy/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/toxiproxy/build.gradle b/modules/toxiproxy/build.gradle index 293b8f8f190..47221d68e40 100644 --- a/modules/toxiproxy/build.gradle +++ b/modules/toxiproxy/build.gradle @@ -2,7 +2,7 @@ description = "Testcontainers :: Toxiproxy" dependencies { compile project(':testcontainers') - compile 'eu.rekawek.toxiproxy:toxiproxy-java:2.1.3' + compile 'eu.rekawek.toxiproxy:toxiproxy-java:2.1.4' testCompile 'redis.clients:jedis:3.0.1' testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From 1d551b664c5b0f4f46a2ddfd22041f5afcd1e09a Mon Sep 17 00:00:00 2001 From: Ray Tsang Date: Tue, 1 Dec 2020 23:56:15 -0800 Subject: [PATCH 1745/5615] More examples in the gcloud module doc (#3463) * updated gcloud module doc * document container images to use * updated example links * fixed links --- docs/modules/gcloud.md | 110 ++++++++++++++++-- .../FirestoreEmulatorContainerTest.java | 8 +- .../PubSubEmulatorContainerTest.java | 13 ++- .../SpannerEmulatorContainerTest.java | 12 +- 4 files changed, 130 insertions(+), 13 deletions(-) diff --git a/docs/modules/gcloud.md b/docs/modules/gcloud.md index 3400ad881a9..041129919ee 100644 --- a/docs/modules/gcloud.md +++ b/docs/modules/gcloud.md @@ -3,29 +3,119 @@ !!! note This module is INCUBATING. While it is ready for use and operational in the current version of Testcontainers, it is possible that it may receive breaking changes in the future. See [our contributing guidelines](/contributing/#incubating-modules) for more information on our incubating modules policy. -Testcontainers module for the Google's [Cloud SDK](https://cloud.google.com/sdk/). +Testcontainers module for the Google Cloud Platform's [Cloud SDK](https://cloud.google.com/sdk/). -Currently, the module supports `Datastore`, `Firestore`, `Pub/Sub` and `Spanner` emulators. In order to use it, you should use the following classes: +Currently, the module supports `Datastore`, `Firestore`, `Spanner`, and `Pub/Sub` emulators. In order to use it, you should use the following classes: -* DatastoreEmulatorContainer -* FirestoreEmulatorContainer -* PubSubEmulatorContainer -* SpannerEmulatorContainer +Class | Container Image +-|- +DatastoreEmulatorContainer | [gcr.io/google.com/cloudsdktool/cloud-sdk:emulators](https://gcr.io/google.com/cloudsdktool/cloud-sdk) +FirestoreEmulatorContainer | [gcr.io/google.com/cloudsdktool/cloud-sdk:emulators](https://gcr.io/google.com/cloudsdktool/cloud-sdk) +SpannerEmulatorContainer | [gcr.io/cloud-spanner-emulator/emulator](https://gcr.io/cloud-spanner-emulator/emulator) +PubSubEmulatorContainer | [gcr.io/google.com/cloudsdktool/cloud-sdk:emulators](https://gcr.io/google.com/cloudsdktool/cloud-sdk) ## Usage example -Running GCloud as a stand-in for Google Datastore during a test: +### Datastore + +Start Datastore Emulator during a test: + + +[Starting a Datastore Emulator container](../../modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java) inside_block:creatingDatastoreEmulatorContainer + + +And test against the Emulator: + + +[Testing with a Datastore Emulator container](../../modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java) inside_block:startingDatastoreEmulatorContainer + + +See more examples: + + * [Full sample code](https://github.com/testcontainers/testcontainers-java/tree/master/modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java) + * [With Spring Boot](https://github.com/saturnism/testcontainers-gcloud-examples/tree/main/springboot/datastore-example/src/test/java/com/example/springboot/datastore) + +### Firestore + +Start Firestore Emulator during a test: + + +[Starting a Firestore Emulator container](../../modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java) inside_block:emulatorContainer + + +And test against the Emulator: + + +[Testing with a Firestore Emulator container](../../modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java) inside_block:testWithEmulatorContainer + + +See more examples: + + * [Full sample code](https://github.com/testcontainers/testcontainers-java/tree/master/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java) + * [With Spring Boot](https://github.com/saturnism/testcontainers-gcloud-examples/tree/main/springboot/firestore-example/src/test/java/com/example/springboot/firestore/FirestoreIntegrationTests.java) + +### Spanner + +Start Spanner Emulator during a test: + + +[Starting a Spanner Emulator container](../../modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java) inside_block:emulatorContainer + + +Create a test Spanner Instance in the Emulator: + + +[Create a test Spanner instance](../../modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java) inside_block:createInstance + + +Create a test Database in the Emulator: + + +[Creating a test Spanner database](../../modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java) inside_block:createDatabase + + +And test against the Emulator: + + +[Testing with a Firestore Emulator container](../../modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java) inside_block:testWithEmulatorContainer + + +See more examples: + + * [Full sample code](https://github.com/testcontainers/testcontainers-java/tree/master/modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java) + * [With Spring Boot](https://github.com/saturnism/testcontainers-gcloud-examples/tree/main/springboot/spanner-example/src/test/java/com/example/springboot/spanner/SpannerIntegrationTests.java) + +### Pub/Sub + +Start Pub/Sub Emulator during a test: + + +[Starting a Pub/Sub Emulator container](../../modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java) inside_block:emulatorContainer + + +Create a test Pub/Sub topic the Emulator: -[Creating a Datastore container](../../modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java) inside_block:creatingDatastoreEmulatorContainer +[Create a test topic](../../modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java) inside_block:createTopic -And how to start it: +Create a test Pub/Sub subscription in the Emulator: -[Starting a Datastore container](../../modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java) inside_block:startingDatastoreEmulatorContainer +[Create a test subscription](../../modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java) inside_block:createSubscription +And test against the Emulator: + + +[Testing with a Pub/Sub Emulator container](../../modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java) inside_block:testWithEmulatorContainer + + +See more examples: + + * [Full sample code](https://github.com/testcontainers/testcontainers-java/tree/master/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java) + * [With Spring Boot](https://github.com/saturnism/testcontainers-gcloud-examples/tree/main/springboot/pubsub-example/src/test/java/com/example/springboot/pubsub/PubSubIntegrationTests.java) + ## Adding this module to your project dependencies Add the following dependency to your `pom.xml`/`build.gradle` file: diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java index 4003ebdd342..138ff946f6f 100644 --- a/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java @@ -20,8 +20,13 @@ public class FirestoreEmulatorContainerTest { @Rule - public FirestoreEmulatorContainer emulator = new FirestoreEmulatorContainer(DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:316.0.0-emulators")); + // emulatorContainer { + public FirestoreEmulatorContainer emulator = new FirestoreEmulatorContainer( + DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:316.0.0-emulators") + ); + // } + // testWithEmulatorContainer { @Test public void testSimple() throws ExecutionException, InterruptedException { FirestoreOptions options = FirestoreOptions.getDefaultInstance().toBuilder() @@ -44,5 +49,6 @@ public void testSimple() throws ExecutionException, InterruptedException { assertThat(querySnapshot.getDocuments().get(0).getData()).containsEntry("first", "Ada"); } + // } } diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java index 3ea9f831d8e..2e692956418 100644 --- a/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java @@ -33,9 +33,15 @@ public class PubSubEmulatorContainerTest { public static final String PROJECT_ID = "my-project-id"; @Rule - public PubSubEmulatorContainer emulator = new PubSubEmulatorContainer(DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:316.0.0-emulators")); + // emulatorContainer { + public PubSubEmulatorContainer emulator = new PubSubEmulatorContainer( + DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:316.0.0-emulators") + ); + // } + @Test + // testWithEmulatorContainer { public void testSimple() throws IOException { String hostport = emulator.getEmulatorEndpoint(); ManagedChannel channel = ManagedChannelBuilder.forTarget(hostport).usePlaintext().build(); @@ -76,7 +82,9 @@ public void testSimple() throws IOException { channel.shutdown(); } } + // } + // createTopic { private void createTopic(String topicId, TransportChannelProvider channelProvider, NoCredentialsProvider credentialsProvider) throws IOException { TopicAdminSettings topicAdminSettings = TopicAdminSettings.newBuilder() .setTransportChannelProvider(channelProvider) @@ -87,7 +95,9 @@ private void createTopic(String topicId, TransportChannelProvider channelProvide topicAdminClient.createTopic(topicName); } } + // } + // createSubscription { private void createSubscription(String subscriptionId, String topicId, TransportChannelProvider channelProvider, NoCredentialsProvider credentialsProvider) throws IOException { SubscriptionAdminSettings subscriptionAdminSettings = SubscriptionAdminSettings.newBuilder() .setTransportChannelProvider(channelProvider) @@ -97,5 +107,6 @@ private void createSubscription(String subscriptionId, String topicId, Transport ProjectSubscriptionName subscriptionName = ProjectSubscriptionName.of(PROJECT_ID, subscriptionId); subscriptionAdminClient.createSubscription(subscriptionName, TopicName.of(PROJECT_ID, topicId), PushConfig.getDefaultInstance(), 10); } + // } } diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java index 5a47f9429ad..f801ca5b547 100644 --- a/modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java @@ -25,12 +25,17 @@ public class SpannerEmulatorContainerTest { @Rule - public SpannerEmulatorContainer emulator = new SpannerEmulatorContainer(DockerImageName.parse("gcr.io/cloud-spanner-emulator/emulator:1.1.0")); + // emulatorContainer { + public SpannerEmulatorContainer emulator = new SpannerEmulatorContainer( + DockerImageName.parse("gcr.io/cloud-spanner-emulator/emulator:1.1.0") + ); + // } private static final String PROJECT_NAME = "test-project"; private static final String INSTANCE_NAME = "test-instance"; private static final String DATABASE_NAME = "test-database"; + // testWithEmulatorContainer { @Test public void testSimple() throws ExecutionException, InterruptedException { SpannerOptions options = SpannerOptions.newBuilder() @@ -62,12 +67,16 @@ public void testSimple() throws ExecutionException, InterruptedException { assertThat(resultSet.getLong(0)).isEqualTo(1); assertThat(resultSet.getString(1)).isEqualTo("Java"); } + // } + // createDatabase { private void createDatabase(Spanner spanner) throws InterruptedException, ExecutionException { DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient(); Database database = dbAdminClient.createDatabase(INSTANCE_NAME, DATABASE_NAME, Arrays.asList("CREATE TABLE TestTable (Key INT64, Value STRING(MAX)) PRIMARY KEY (Key)")).get(); } + // } + // createInstance { private InstanceId createInstance(Spanner spanner) throws InterruptedException, ExecutionException { InstanceConfigId instanceConfig = InstanceConfigId.of(PROJECT_NAME, "emulator-config"); InstanceId instanceId = InstanceId.of(PROJECT_NAME, INSTANCE_NAME); @@ -75,5 +84,6 @@ private InstanceId createInstance(Spanner spanner) throws InterruptedException, Instance instance = insAdminClient.createInstance(InstanceInfo.newBuilder(instanceId).setNodeCount(1).setDisplayName("Test instance").setInstanceConfigId(instanceConfig).build()).get(); return instanceId; } + // } } From dc0588e8a1272193e7f2ea59d2ac97d1f4110c39 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 2 Dec 2020 18:02:37 +0100 Subject: [PATCH 1746/5615] Pre-resolve LazyDockerClient from `GenericContainer#start()` (#3530) --- .../java/org/testcontainers/containers/GenericContainer.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 641096a0779..bece4ef3378 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -305,6 +305,8 @@ public void start() { return; } Startables.deepStart(dependencies).get(); + // trigger LazyDockerClient's resolve so that we fail fast here and not in getDockerImageName() + dockerClient.authConfig(); doStart(); } From 602343a87c06c53618b666e3b774652aa16e3dda Mon Sep 17 00:00:00 2001 From: Philip Graf Date: Tue, 8 Dec 2020 15:29:09 +0100 Subject: [PATCH 1747/5615] Support timestamps with offset from "inspect image" (#3055) The command "inspect image" can return a "created" timestamp without a time zone offset (e.g. "2020-07-27T18:23:31.365190246Z") or with a time zone offset (e.g. "2020-07-27T18:23:31.365190246+02:00"). This change adds support for timestamps with a time zone offset. Co-authored-by: Sergei Egorov --- .../org/testcontainers/images/ImageData.java | 3 ++- .../testcontainers/images/ImageDataTest.java | 27 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 core/src/test/java/org/testcontainers/images/ImageDataTest.java diff --git a/core/src/main/java/org/testcontainers/images/ImageData.java b/core/src/main/java/org/testcontainers/images/ImageData.java index 54b07d5acf2..345b5939268 100644 --- a/core/src/main/java/org/testcontainers/images/ImageData.java +++ b/core/src/main/java/org/testcontainers/images/ImageData.java @@ -7,6 +7,7 @@ import lombok.Value; import java.time.Instant; +import java.time.ZonedDateTime; @Value @Builder @@ -17,7 +18,7 @@ public class ImageData { static ImageData from(InspectImageResponse inspectImageResponse) { return ImageData.builder() - .createdAt(Instant.parse(inspectImageResponse.getCreated())) + .createdAt(ZonedDateTime.parse(inspectImageResponse.getCreated()).toInstant()) .build(); } diff --git a/core/src/test/java/org/testcontainers/images/ImageDataTest.java b/core/src/test/java/org/testcontainers/images/ImageDataTest.java new file mode 100644 index 00000000000..3b473cf7ffd --- /dev/null +++ b/core/src/test/java/org/testcontainers/images/ImageDataTest.java @@ -0,0 +1,27 @@ +package org.testcontainers.images; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.Instant; + +import org.junit.Test; + +import com.github.dockerjava.api.command.InspectImageResponse; + +public class ImageDataTest { + + @Test + public void shouldReadTimestampWithoutOffsetFromInspectImageResponse() { + final String timestamp = "2020-07-27T18:23:31.365190246Z"; + final ImageData imageData = ImageData.from(new InspectImageResponse().withCreated(timestamp)); + assertThat(imageData.getCreatedAt()).isEqualTo(Instant.parse(timestamp)); + } + + @Test + public void shouldReadTimestampWithOffsetFromInspectImageResponse() { + final String timestamp = "2020-07-27T18:23:31.365190246+02:00"; + final ImageData imageData = ImageData.from(new InspectImageResponse().withCreated(timestamp)); + assertThat(imageData.getCreatedAt()).isEqualTo(Instant.parse("2020-07-27T16:23:31.365190246Z")); + } + +} From b0cc476550e12396030e5c819d5556f050e3db38 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 9 Dec 2020 23:15:56 +0100 Subject: [PATCH 1748/5615] Avoid using the image name filter. Fixes #3574 (#3575) * filter images by reference. Fixes #3574 * Use `inspectImageCmd` --- .../main/java/org/testcontainers/DockerClientFactory.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index d44cd3b5b6f..192a6b06ddb 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -3,6 +3,7 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.async.ResultCallback; import com.github.dockerjava.api.command.CreateContainerCmd; +import com.github.dockerjava.api.command.ListImagesCmd; import com.github.dockerjava.api.exception.InternalServerErrorException; import com.github.dockerjava.api.exception.NotFoundException; import com.github.dockerjava.api.model.AccessMode; @@ -36,6 +37,7 @@ import java.io.InputStream; import java.net.URI; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Optional; @@ -326,8 +328,9 @@ private boolean checkMountableFile() { */ @SneakyThrows public void checkAndPullImage(DockerClient client, String image) { - List images = client.listImagesCmd().withImageNameFilter(image).exec(); - if (images.isEmpty()) { + try { + client.inspectImageCmd(image).exec(); + } catch (NotFoundException e) { client.pullImageCmd(image).exec(new TimeLimitedLoggedPullImageResultCallback(log)).awaitCompletion(); } } From d8df52e2168398b76392fce56f99e886253f8b44 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 10 Dec 2020 09:44:24 +0100 Subject: [PATCH 1749/5615] avoid code duplication by using `ContainerState` in `ResourceReaper` (#3573) --- .../testcontainers/DockerClientFactory.java | 6 +-- .../utility/ResourceReaper.java | 49 ++++++++++++++----- 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 192a6b06ddb..98e8249e4e5 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -177,8 +177,7 @@ public DockerClient client() { final DockerClientProviderStrategy strategy = getOrInitializeStrategy(); - String hostIpAddress = strategy.getDockerHostIpAddress(); - log.info("Docker host IP address is {}", hostIpAddress); + log.info("Docker host IP address is {}", strategy.getDockerHostIpAddress()); final DockerClient client = new DelegatingDockerClient(strategy.getDockerClient()) { @Override public void close() { @@ -202,7 +201,8 @@ public void close() { if (useRyuk) { log.debug("Ryuk is enabled"); try { - ryukContainerId = ResourceReaper.start(hostIpAddress, client); + //noinspection deprecation + ryukContainerId = ResourceReaper.start(client); } catch (RuntimeException e) { cachedClientFailure = e; throw e; diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index 3b0cfa1e289..f409dbbc865 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -9,7 +9,6 @@ import com.github.dockerjava.api.model.Frame; import com.github.dockerjava.api.model.HostConfig; import com.github.dockerjava.api.model.Network; -import com.github.dockerjava.api.model.Ports; import com.github.dockerjava.api.model.Volume; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Throwables; @@ -21,6 +20,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.ContainerState; import java.io.BufferedReader; import java.io.IOException; @@ -70,8 +70,23 @@ private ResourceReaper() { dockerClient = DockerClientFactory.instance().client(); } - @SneakyThrows(InterruptedException.class) + + /** + * + * @deprecated internal API + */ + @Deprecated public static String start(String hostIpAddress, DockerClient client) { + return start(client); + } + + /** + * + * @deprecated internal API + */ + @Deprecated + @SneakyThrows(InterruptedException.class) + public static String start(DockerClient client) { String ryukImage = ImageNameSubstitutor.instance() .apply(DockerImageName.parse("testcontainers/ryuk:0.3.0")) .asCanonicalNameString(); @@ -107,14 +122,22 @@ public void onNext(Frame frame) { } }); - InspectContainerResponse inspectedContainer = client.inspectContainerCmd(ryukContainerId).exec(); + ContainerState containerState = new ContainerState() { - Integer ryukPort = inspectedContainer.getNetworkSettings().getPorts().getBindings().values().stream() - .flatMap(Stream::of) - .findFirst() - .map(Ports.Binding::getHostPortSpec) - .map(Integer::parseInt) - .get(); + final InspectContainerResponse inspectedContainer = client.inspectContainerCmd(ryukContainerId).exec(); + + @Override + public List getExposedPorts() { + return Stream.of(getContainerInfo().getConfig().getExposedPorts()) + .map(ExposedPort::getPort) + .collect(Collectors.toList()); + } + + @Override + public InspectContainerResponse getContainerInfo() { + return inspectedContainer; + } + }; CountDownLatch ryukScheduledLatch = new CountDownLatch(1); @@ -126,13 +149,15 @@ public void onNext(Frame frame) { ); } + String host = containerState.getHost(); + Integer ryukPort = containerState.getFirstMappedPort(); Thread kiraThread = new Thread( DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, () -> { while (true) { RYUK_ACK_RATE_LIMITER.doWhenReady(() -> { int index = 0; - try(Socket clientSocket = new Socket(hostIpAddress, ryukPort)) { + try(Socket clientSocket = new Socket(host, ryukPort)) { FilterRegistry registry = new FilterRegistry(clientSocket.getInputStream(), clientSocket.getOutputStream()); synchronized (DEATH_NOTE) { @@ -157,7 +182,7 @@ public void onNext(Frame frame) { } } } catch (IOException e) { - log.warn("Can not connect to Ryuk at {}:{}", hostIpAddress, ryukPort, e); + log.warn("Can not connect to Ryuk at {}:{}", host, ryukPort, e); } }); } @@ -170,7 +195,7 @@ public void onNext(Frame frame) { // We need to wait before we can start any containers to make sure that we delete them if (!ryukScheduledLatch.await(TestcontainersConfiguration.getInstance().getRyukTimeout(), TimeUnit.SECONDS)) { log.error("Timed out waiting for Ryuk container to start. Ryuk's logs:\n{}", ryukLog); - throw new IllegalStateException(String.format("Could not connect to Ryuk at %s:%s", hostIpAddress, ryukPort)); + throw new IllegalStateException(String.format("Could not connect to Ryuk at %s:%s", host, ryukPort)); } } finally { try { From 0e263f296556c2357f0d4ea99d120a3a2880ceb3 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 10 Dec 2020 10:53:33 +0000 Subject: [PATCH 1750/5615] Default prefixing image substitutor (#3413) Co-authored-by: Sergei Egorov --- .../utility/DefaultImageNameSubstitutor.java | 14 +- .../utility/DockerImageName.java | 27 ++-- .../PrefixingImageNameSubstitutor.java | 58 ++++++++ .../utility/TestcontainersConfiguration.java | 3 - .../testcontainers/utility/Versioning.java | 1 + ...estcontainers.utility.ImageNameSubstitutor | 1 + .../DockerImageNameCompatibilityTest.java | 8 +- .../PrefixingImageNameSubstitutorTest.java | 140 ++++++++++++++++++ docs/features/image_name_substitution.md | 32 +++- 9 files changed, 257 insertions(+), 27 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/utility/PrefixingImageNameSubstitutor.java create mode 100644 core/src/main/resources/META-INF/services/org.testcontainers.utility.ImageNameSubstitutor create mode 100644 core/src/test/java/org/testcontainers/utility/PrefixingImageNameSubstitutorTest.java diff --git a/core/src/main/java/org/testcontainers/utility/DefaultImageNameSubstitutor.java b/core/src/main/java/org/testcontainers/utility/DefaultImageNameSubstitutor.java index bcc7c96ba18..840b8a77b40 100644 --- a/core/src/main/java/org/testcontainers/utility/DefaultImageNameSubstitutor.java +++ b/core/src/main/java/org/testcontainers/utility/DefaultImageNameSubstitutor.java @@ -5,31 +5,37 @@ /** * Testcontainers' default implementation of {@link ImageNameSubstitutor}. - * Delegates to {@link ConfigurationFileImageNameSubstitutor}. + * Delegates to {@link ConfigurationFileImageNameSubstitutor} followed by {@link PrefixingImageNameSubstitutor}. */ @Slf4j final class DefaultImageNameSubstitutor extends ImageNameSubstitutor { private final ConfigurationFileImageNameSubstitutor configurationFileImageNameSubstitutor; + private final PrefixingImageNameSubstitutor prefixingImageNameSubstitutor; public DefaultImageNameSubstitutor() { configurationFileImageNameSubstitutor = new ConfigurationFileImageNameSubstitutor(); + prefixingImageNameSubstitutor = new PrefixingImageNameSubstitutor(); } @VisibleForTesting DefaultImageNameSubstitutor( - final ConfigurationFileImageNameSubstitutor configurationFileImageNameSubstitutor + final ConfigurationFileImageNameSubstitutor configurationFileImageNameSubstitutor, + final PrefixingImageNameSubstitutor prefixingImageNameSubstitutor ) { this.configurationFileImageNameSubstitutor = configurationFileImageNameSubstitutor; + this.prefixingImageNameSubstitutor = prefixingImageNameSubstitutor; } @Override public DockerImageName apply(final DockerImageName original) { - return configurationFileImageNameSubstitutor.apply(original); + return configurationFileImageNameSubstitutor + .andThen(prefixingImageNameSubstitutor) + .apply(original); } @Override protected String getDescription() { - return "DefaultImageNameSubstitutor (" + configurationFileImageNameSubstitutor.getDescription() + ")"; + return "DefaultImageNameSubstitutor (composite of '" + configurationFileImageNameSubstitutor.getDescription() + "' and '" + prefixingImageNameSubstitutor.getDescription() + "')"; } } diff --git a/core/src/main/java/org/testcontainers/utility/DockerImageName.java b/core/src/main/java/org/testcontainers/utility/DockerImageName.java index af8e5300863..b9d266a41ca 100644 --- a/core/src/main/java/org/testcontainers/utility/DockerImageName.java +++ b/core/src/main/java/org/testcontainers/utility/DockerImageName.java @@ -5,6 +5,7 @@ import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; +import lombok.Getter; import lombok.With; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -24,8 +25,8 @@ public final class DockerImageName { private static final Pattern REPO_NAME = Pattern.compile(REPO_NAME_PART + "(/" + REPO_NAME_PART + ")*"); private final String rawName; - private final String registry; - private final String repo; + @With @Getter private final String registry; + @With @Getter private final String repository; @NotNull @With(AccessLevel.PRIVATE) private final Versioning versioning; @Nullable @With(AccessLevel.PRIVATE) @@ -68,13 +69,13 @@ public DockerImageName(String fullImageName) { } if (remoteName.contains("@sha256:")) { - repo = remoteName.split("@sha256:")[0]; + repository = remoteName.split("@sha256:")[0]; versioning = new Sha256Versioning(remoteName.split("@sha256:")[1]); } else if (remoteName.contains(":")) { - repo = remoteName.split(":")[0]; + repository = remoteName.split(":")[0]; versioning = new TagVersioning(remoteName.split(":")[1]); } else { - repo = remoteName; + repository = remoteName; versioning = Versioning.ANY; } @@ -110,10 +111,10 @@ public DockerImageName(String nameWithoutTag, @NotNull String version) { } if (version.startsWith("sha256:")) { - repo = remoteName; + repository = remoteName; versioning = new Sha256Versioning(version.replace("sha256:", "")); } else { - repo = remoteName; + repository = remoteName; versioning = new TagVersioning(version); } @@ -125,9 +126,9 @@ public DockerImageName(String nameWithoutTag, @NotNull String version) { */ public String getUnversionedPart() { if (!"".equals(registry)) { - return registry + "/" + repo; + return registry + "/" + repository; } else { - return repo; + return repository; } } @@ -158,18 +159,14 @@ public String toString() { public void assertValid() { //noinspection UnstableApiUsage HostAndPort.fromString(registry); // return value ignored - this throws if registry is not a valid host:port string - if (!REPO_NAME.matcher(repo).matches()) { - throw new IllegalArgumentException(repo + " is not a valid Docker image name (in " + rawName + ")"); + if (!REPO_NAME.matcher(repository).matches()) { + throw new IllegalArgumentException(repository + " is not a valid Docker image name (in " + rawName + ")"); } if (!versioning.isValid()) { throw new IllegalArgumentException(versioning + " is not a valid image versioning identifier (in " + rawName + ")"); } } - public String getRegistry() { - return registry; - } - /** * @param newTag version tag for the copy to use * @return an immutable copy of this {@link DockerImageName} with the new version tag diff --git a/core/src/main/java/org/testcontainers/utility/PrefixingImageNameSubstitutor.java b/core/src/main/java/org/testcontainers/utility/PrefixingImageNameSubstitutor.java new file mode 100644 index 00000000000..c720416deac --- /dev/null +++ b/core/src/main/java/org/testcontainers/utility/PrefixingImageNameSubstitutor.java @@ -0,0 +1,58 @@ +package org.testcontainers.utility; + +import com.google.common.annotations.VisibleForTesting; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * An {@link ImageNameSubstitutor} which applies a prefix to all image names, e.g. a private registry host and path. + * The prefix may be set via an environment variable (TESTCONTAINERS_HUB_IMAGE_NAME_PREFIX) or an equivalent + * configuration file entry (see {@link TestcontainersConfiguration}). + */ +@NoArgsConstructor +@Slf4j +final class PrefixingImageNameSubstitutor extends ImageNameSubstitutor { + + @VisibleForTesting + static final String PREFIX_PROPERTY_KEY = "hub.image.name.prefix"; + + private TestcontainersConfiguration configuration = TestcontainersConfiguration.getInstance(); + + @VisibleForTesting + PrefixingImageNameSubstitutor(final TestcontainersConfiguration configuration) { + this.configuration = configuration; + } + + @Override + public DockerImageName apply(DockerImageName original) { + final String configuredPrefix = configuration.getEnvVarOrProperty(PREFIX_PROPERTY_KEY, ""); + + if (configuredPrefix.isEmpty()) { + log.debug("No prefix is configured"); + return original; + } + + boolean isAHubImage = original.getRegistry().isEmpty(); + if (!isAHubImage) { + log.debug("Image {} is not a Docker Hub image - not applying registry/repository change", original); + return original; + } + + log.debug( + "Applying changes to image name {}: applying prefix '{}'", + original, + configuredPrefix + ); + + DockerImageName prefixAsImage = DockerImageName.parse(configuredPrefix); + + return original + .withRegistry(prefixAsImage.getRegistry()) + .withRepository(prefixAsImage.getRepository() + original.getRepository()); + } + + @Override + protected String getDescription() { + return getClass().getSimpleName(); + } +} diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 47c274c05e6..3b4d9f8f70c 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -212,7 +212,6 @@ private String getConfigurable(@NotNull final String propertyName, @Nullable fin * @param propertyName name of configuration file property (dot-separated lower case) * @return the found value, or null if not set */ - @Nullable @Contract("_, !null -> !null") public String getEnvVarOrProperty(@NotNull final String propertyName, @Nullable final String defaultValue) { return getConfigurable(propertyName, defaultValue, userProperties, classpathProperties); @@ -225,7 +224,6 @@ public String getEnvVarOrProperty(@NotNull final String propertyName, @Nullable * @param propertyName name of configuration file property (dot-separated lower case) * @return the found value, or null if not set */ - @Nullable @Contract("_, !null -> !null") public String getEnvVarOrUserProperty(@NotNull final String propertyName, @Nullable final String defaultValue) { return getConfigurable(propertyName, defaultValue, userProperties); @@ -238,7 +236,6 @@ public String getEnvVarOrUserProperty(@NotNull final String propertyName, @Nulla * @param propertyName name of configuration file property (dot-separated lower case) * @return the found value, or null if not set */ - @Nullable @Contract("_, !null -> !null") public String getUserProperty(@NotNull final String propertyName, @Nullable final String defaultValue) { return getConfigurable(propertyName, defaultValue); diff --git a/core/src/main/java/org/testcontainers/utility/Versioning.java b/core/src/main/java/org/testcontainers/utility/Versioning.java index 84dcf46274e..4b1da407440 100644 --- a/core/src/main/java/org/testcontainers/utility/Versioning.java +++ b/core/src/main/java/org/testcontainers/utility/Versioning.java @@ -46,6 +46,7 @@ public int hashCode() { @EqualsAndHashCode class TagVersioning implements Versioning { public static final String TAG_REGEX = "[\\w][\\w.\\-]{0,127}"; + static final TagVersioning LATEST = new TagVersioning("latest"); private final String tag; TagVersioning(String tag) { diff --git a/core/src/main/resources/META-INF/services/org.testcontainers.utility.ImageNameSubstitutor b/core/src/main/resources/META-INF/services/org.testcontainers.utility.ImageNameSubstitutor new file mode 100644 index 00000000000..069dc7ee710 --- /dev/null +++ b/core/src/main/resources/META-INF/services/org.testcontainers.utility.ImageNameSubstitutor @@ -0,0 +1 @@ +org.testcontainers.utility.DefaultImageNameSubstitutor diff --git a/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java b/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java index 7c794814faf..22d67de5785 100644 --- a/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java +++ b/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java @@ -1,13 +1,13 @@ package org.testcontainers.utility; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - import static org.hamcrest.core.StringContains.containsString; import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + public class DockerImageNameCompatibilityTest { diff --git a/core/src/test/java/org/testcontainers/utility/PrefixingImageNameSubstitutorTest.java b/core/src/test/java/org/testcontainers/utility/PrefixingImageNameSubstitutorTest.java new file mode 100644 index 00000000000..d2c33989deb --- /dev/null +++ b/core/src/test/java/org/testcontainers/utility/PrefixingImageNameSubstitutorTest.java @@ -0,0 +1,140 @@ +package org.testcontainers.utility; + +import org.junit.Before; +import org.junit.Test; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.testcontainers.utility.PrefixingImageNameSubstitutor.PREFIX_PROPERTY_KEY; + +public class PrefixingImageNameSubstitutorTest { + + private TestcontainersConfiguration mockConfiguration; + private PrefixingImageNameSubstitutor underTest; + + @Before + public void setUp() { + mockConfiguration = mock(TestcontainersConfiguration.class); + underTest = new PrefixingImageNameSubstitutor(mockConfiguration); + } + + @Test + public void testHappyPath() { + when(mockConfiguration.getEnvVarOrProperty(eq(PREFIX_PROPERTY_KEY), any())).thenReturn("someregistry.com/our-mirror/"); + + final DockerImageName result = underTest.apply(DockerImageName.parse("some/image:tag")); + + assertEquals( + "The prefix is applied", + "someregistry.com/our-mirror/some/image:tag", + result.asCanonicalNameString() + ); + } + + @Test + public void hubIoRegistryIsNotChanged() { + when(mockConfiguration.getEnvVarOrProperty(eq(PREFIX_PROPERTY_KEY), any())).thenReturn("someregistry.com/our-mirror/"); + + final DockerImageName result = underTest.apply(DockerImageName.parse("docker.io/some/image:tag")); + + assertEquals( + "The prefix is applied", + "docker.io/some/image:tag", + result.asCanonicalNameString() + ); + } + + @Test + public void hubComRegistryIsNotChanged() { + when(mockConfiguration.getEnvVarOrProperty(eq(PREFIX_PROPERTY_KEY), any())).thenReturn("someregistry.com/our-mirror/"); + + final DockerImageName result = underTest.apply(DockerImageName.parse("registry.hub.docker.com/some/image:tag")); + + assertEquals( + "The prefix is applied", + "registry.hub.docker.com/some/image:tag", + result.asCanonicalNameString() + ); + } + + @Test + public void thirdPartyRegistriesNotAffected() { + when(mockConfiguration.getEnvVarOrProperty(eq(PREFIX_PROPERTY_KEY), any())).thenReturn("someregistry.com/our-mirror/"); + + final DockerImageName result = underTest.apply(DockerImageName.parse("gcr.io/something/image:tag")); + + assertEquals( + "The prefix is not applied if a third party registry is used", + "gcr.io/something/image:tag", + result.asCanonicalNameString() + ); + } + + @Test + public void testNoDoublePrefixing() { + when(mockConfiguration.getEnvVarOrProperty(eq(PREFIX_PROPERTY_KEY), any())).thenReturn("someregistry.com/our-mirror/"); + + final DockerImageName result = underTest.apply(DockerImageName.parse("someregistry.com/some/image:tag")); + + assertEquals( + "The prefix is not applied if already present", + "someregistry.com/some/image:tag", + result.asCanonicalNameString() + ); + } + + @Test + public void testHandlesEmptyValue() { + when(mockConfiguration.getEnvVarOrProperty(eq(PREFIX_PROPERTY_KEY), any())).thenReturn(""); + + final DockerImageName result = underTest.apply(DockerImageName.parse("some/image:tag")); + + assertEquals( + "The prefix is not applied if the env var is not set", + "some/image:tag", + result.asCanonicalNameString() + ); + } + + @Test + public void testHandlesRegistryOnlyWithTrailingSlash() { + when(mockConfiguration.getEnvVarOrProperty(eq(PREFIX_PROPERTY_KEY), any())).thenReturn("someregistry.com/"); + + final DockerImageName result = underTest.apply(DockerImageName.parse("some/image:tag")); + + assertEquals( + "The prefix is applied", + "someregistry.com/some/image:tag", + result.asCanonicalNameString() + ); + } + + @Test + public void testCombinesLiterallyForRegistryOnlyWithoutTrailingSlash() { + when(mockConfiguration.getEnvVarOrProperty(eq(PREFIX_PROPERTY_KEY), any())).thenReturn("someregistry.com"); + + final DockerImageName result = underTest.apply(DockerImageName.parse("some/image:tag")); + + assertEquals( + "The prefix is applied", + "someregistry.comsome/image:tag", // treating the prefix literally, for predictability + result.asCanonicalNameString() + ); + } + + @Test + public void testCombinesLiterallyForBothPartsWithoutTrailingSlash() { + when(mockConfiguration.getEnvVarOrProperty(eq(PREFIX_PROPERTY_KEY), any())).thenReturn("someregistry.com/our-mirror"); + + final DockerImageName result = underTest.apply(DockerImageName.parse("some/image:tag")); + + assertEquals( + "The prefix is applied", + "someregistry.com/our-mirrorsome/image:tag", // treating the prefix literally, for predictability + result.asCanonicalNameString() + ); + } +} diff --git a/docs/features/image_name_substitution.md b/docs/features/image_name_substitution.md index 45ea6038b49..37088473d72 100644 --- a/docs/features/image_name_substitution.md +++ b/docs/features/image_name_substitution.md @@ -45,7 +45,37 @@ to: +## Automatically modifying Docker Hub image names +Testcontainers can be configured to modify Docker Hub image names on the fly to apply a prefix string. + +Consider this if: + +* Developers and CI machines need to use different image names. For example, developers are able to pull images from Docker Hub, but CI machines need to pull from a private registry +* Your private registry has copies of images from Docker Hub where the names are predictable, and just adding a prefix is enough. + For example, `registry.mycompany.com/mirror/mysql:8.0.22` can be derived from the original Docker Hub image name (`mysql:8.0.22`) with a consistent prefix string: `registry.mycompany.com/mirror/` + +In this case, image name references in code are **unchanged**. +i.e. you would leave as-is: + + +[Unchanged direct Docker Hub image name](../examples/junit4/generic/src/test/java/generic/ImageNameSubstitutionTest.java) inside_block:directDockerHubReference + + +You can then configure Testcontainers to apply the prefix `registry.mycompany.com/mirror/` to every image that it tries to pull from Docker Hub. +This can be done in one of two ways: + +* Setting environment variables `TESTCONTAINERS_HUB_IMAGE_NAME_PREFIX=registry.mycompany.com/mirror/` +* Via config file, setting `hub.image.name.prefix` in either: + * the `~/.testcontainers.properties` file in your user home directory, or + * a file named `testcontainers.properties` on the classpath + +Testcontainers will automatically apply the prefix to every image that it pulls from Docker Hub - please verify that all [the required images](../supported_docker_environment/image_registry_rate_limiting.md#which-images-are-used-by-testcontainers) exist in your registry. + +Testcontainers will not apply the prefix to: + +* non-Hub image names (e.g. where another registry is set) +* Docker Hub image names where the hub registry is explicitly part of the name (i.e. anything with a `docker.io` or `registry.hub.docker.com` host part) @@ -54,7 +84,7 @@ to: Consider this if: * You have complex rules about which private registry images should be used as substitutes, e.g.: - * non-deterministic mapping of names meaning that a [name prefix](#adding-a-registry-url-prefix-to-image-names-automatically) cannot be used + * non-deterministic mapping of names meaning that a [name prefix](#automatically-modifying-docker-hub-image-names) cannot be used * rules depending upon developer identity or location * or you wish to add audit logging of images used in the build * or you wish to prevent accidental usage of images that are not on an approved list From f76970f570fd1f1dcd0a6087162de8a3e7733d29 Mon Sep 17 00:00:00 2001 From: Ray Tsang Date: Thu, 10 Dec 2020 18:59:21 +0800 Subject: [PATCH 1751/5615] Added Bigtable emulator container support (#3466) * Added Bigtable emulator container support * changed port to be consistent w/ other emulators * Update modules/gcloud/src/main/java/org/testcontainers/containers/BigtableEmulatorContainer.java Co-authored-by: Sergei Egorov * Removed `getEmulatorHost` Co-authored-by: Sergei Egorov --- modules/gcloud/build.gradle | 1 + .../containers/BigtableEmulatorContainer.java | 44 +++++++++ .../BigtableEmulatorContainerTest.java | 95 +++++++++++++++++++ 3 files changed, 140 insertions(+) create mode 100644 modules/gcloud/src/main/java/org/testcontainers/containers/BigtableEmulatorContainer.java create mode 100644 modules/gcloud/src/test/java/org/testcontainers/containers/BigtableEmulatorContainerTest.java diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index a5737648a4b..aa42d7dc560 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -7,5 +7,6 @@ dependencies { testCompile 'com.google.cloud:google-cloud-firestore:1.33.0' testCompile 'com.google.cloud:google-cloud-pubsub:1.105.0' testCompile 'com.google.cloud:google-cloud-spanner:1.50.0' + testCompile 'com.google.cloud:google-cloud-bigtable:1.17.3' testCompile 'org.assertj:assertj-core:3.15.0' } diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/BigtableEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/BigtableEmulatorContainer.java new file mode 100644 index 00000000000..4b81e3964ce --- /dev/null +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/BigtableEmulatorContainer.java @@ -0,0 +1,44 @@ +package org.testcontainers.containers; + +import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; +import org.testcontainers.utility.DockerImageName; + +/** + * A Bigtable container that relies in google cloud sdk. + * + * Default port is 9000. + * + * @author Eddú Meléndez + * @author Ray Tsang + */ +public class BigtableEmulatorContainer extends GenericContainer { + + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk"); + + private static final String CMD = "gcloud beta emulators bigtable start --host-port 0.0.0.0:9000"; + private static final int PORT = 9000; + + public BigtableEmulatorContainer(final DockerImageName dockerImageName) { + super(dockerImageName); + + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + + withExposedPorts(PORT); + setWaitStrategy(new LogMessageWaitStrategy() + .withRegEx("(?s).*running.*$")); + withCommand("/bin/sh", "-c", CMD); + } + + /** + * @return a host:port pair corresponding to the address on which the emulator is + * reachable from the test host machine. Directly usable as a parameter to the + * com.google.cloud.ServiceOptions.Builder#setHost(java.lang.String) method. + */ + public String getEmulatorEndpoint() { + return getHost() + ":" + getEmulatorPort(); + } + + public int getEmulatorPort() { + return getMappedPort(PORT); + } +} diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/BigtableEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/BigtableEmulatorContainerTest.java new file mode 100644 index 00000000000..0cc44afcc77 --- /dev/null +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/BigtableEmulatorContainerTest.java @@ -0,0 +1,95 @@ +package org.testcontainers.containers; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.api.core.ApiFuture; +import com.google.api.gax.batching.Batcher; +import com.google.api.gax.core.CredentialsProvider; +import com.google.api.gax.core.NoCredentialsProvider; +import com.google.api.gax.grpc.GrpcTransportChannel; +import com.google.api.gax.rpc.FixedTransportChannelProvider; +import com.google.api.gax.rpc.TransportChannelProvider; +import com.google.cloud.bigtable.admin.v2.BigtableInstanceAdminClient; +import com.google.cloud.bigtable.admin.v2.BigtableTableAdminClient; +import com.google.cloud.bigtable.admin.v2.models.CreateInstanceRequest; +import com.google.cloud.bigtable.admin.v2.models.CreateTableRequest; +import com.google.cloud.bigtable.admin.v2.models.Instance; +import com.google.cloud.bigtable.admin.v2.models.Table; +import com.google.cloud.bigtable.admin.v2.stub.BigtableInstanceAdminStub; +import com.google.cloud.bigtable.admin.v2.stub.BigtableInstanceAdminStubSettings; +import com.google.cloud.bigtable.admin.v2.stub.BigtableTableAdminStubSettings; +import com.google.cloud.bigtable.admin.v2.stub.EnhancedBigtableTableAdminStub; +import com.google.cloud.bigtable.data.v2.BigtableDataClient; +import com.google.cloud.bigtable.data.v2.BigtableDataSettings; +import com.google.cloud.bigtable.data.v2.models.Row; +import com.google.cloud.bigtable.data.v2.models.RowCell; +import com.google.cloud.bigtable.data.v2.models.RowMutation; +import com.google.cloud.bigtable.data.v2.models.RowMutationEntry; +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; +import java.io.IOException; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import org.junit.Rule; +import org.junit.Test; +import org.testcontainers.utility.DockerImageName; + +public class BigtableEmulatorContainerTest { + + public static final String PROJECT_ID = "test-project"; + public static final String INSTANCE_ID = "test-instance"; + + @Rule + // emulatorContainer { + public BigtableEmulatorContainer emulator = new BigtableEmulatorContainer( + DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:316.0.0-emulators") + ); + // } + + @Test + // testWithEmulatorContainer { + public void testSimple() throws IOException, InterruptedException, ExecutionException { + ManagedChannel channel = ManagedChannelBuilder.forTarget(emulator.getEmulatorEndpoint()) + .usePlaintext().build(); + + TransportChannelProvider channelProvider = FixedTransportChannelProvider.create(GrpcTransportChannel.create(channel)); + NoCredentialsProvider credentialsProvider = NoCredentialsProvider.create(); + + try { + createTable(channelProvider, credentialsProvider, "test-table" ); + + BigtableDataClient client = BigtableDataClient.create(BigtableDataSettings + .newBuilderForEmulator(emulator.getHost(), emulator.getEmulatorPort()) + .setProjectId(PROJECT_ID) + .setInstanceId(INSTANCE_ID) + .build()); + + client.mutateRow(RowMutation.create("test-table", "1") + .setCell("name", "firstName", "Ray")); + + Row row = client.readRow("test-table", "1"); + List cells = row.getCells("name", "firstName"); + + assertThat(cells).isNotNull().hasSize(1); + assertThat(cells.get(0).getValue().toStringUtf8()).isEqualTo("Ray"); + } finally { + channel.shutdown(); + } + } + // } + + // createTable { + private void createTable(TransportChannelProvider channelProvider, CredentialsProvider credentialsProvider, String tableName) throws IOException { + EnhancedBigtableTableAdminStub stub = EnhancedBigtableTableAdminStub + .createEnhanced(BigtableTableAdminStubSettings.newBuilder() + .setTransportChannelProvider(channelProvider) + .setCredentialsProvider(credentialsProvider) + .build()); + + try (BigtableTableAdminClient client = BigtableTableAdminClient.create(PROJECT_ID, INSTANCE_ID, stub)) { + Table table = client.createTable(CreateTableRequest.of(tableName).addFamily("name")); + } + } + // } +} From d60d490733a9646f8f6eff12d928909afcac86d7 Mon Sep 17 00:00:00 2001 From: Michal Hlavac Date: Thu, 10 Dec 2020 12:01:43 +0100 Subject: [PATCH 1752/5615] `PostgisContainerProvider` ignores tag parameter and uses `DEFAULT_TAG` (#3465) Co-authored-by: Michal Hlavac --- .../org/testcontainers/containers/PostgisContainerProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java index 76c213f0931..e41a349365c 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java @@ -27,7 +27,7 @@ public JdbcDatabaseContainer newInstance() { @Override public JdbcDatabaseContainer newInstance(String tag) { - return new PostgreSQLContainer<>(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); + return new PostgreSQLContainer<>(DEFAULT_IMAGE_NAME.withTag(tag)); } @Override From 1b2d228e3099488c0e5de0c1e3a5dacbf4d3f88c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavi=20Arias=20Segu=C3=AD?= <876579+xaviarias@users.noreply.github.com> Date: Thu, 10 Dec 2020 11:18:26 +0000 Subject: [PATCH 1753/5615] Add `HttpWaitStrategy#withMethod` (#3533) --- .../containers/wait/strategy/HttpWaitStrategy.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java index 5fd7834b12d..a2bf4c4899e 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java @@ -35,6 +35,7 @@ public class HttpWaitStrategy extends AbstractWaitStrategy { private static final String AUTH_BASIC = "Basic "; private String path = "/"; + private String method = "GET"; private Set statusCodes = new HashSet<>(); private boolean tlsEnabled; private String username; @@ -97,6 +98,17 @@ public HttpWaitStrategy usingTls() { return this; } + /** + * Indicates the HTTP method to use (GET by default). + * + * @param method the HTTP method. + * @return this + */ + public HttpWaitStrategy withMethod(String method) { + this.method = method; + return this; + } + /** * Authenticate with HTTP Basic Authorization credentials. * @@ -167,7 +179,7 @@ protected void waitUntilReady() { connection.setUseCaches(false); } - connection.setRequestMethod("GET"); + connection.setRequestMethod(method); connection.connect(); log.trace("Get response code {}", connection.getResponseCode()); From af678a3463e21a0b4d5118e507177d12759ae339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alan=20Artigao=20Carre=C3=B1o?= Date: Thu, 10 Dec 2020 12:28:28 +0100 Subject: [PATCH 1754/5615] Expose Toxiproxy control port and proxy name (#3454) * Expose Toxiproxy control port and proxy name In order to work with and external Toxiproxy client we need the control port to get the mapped port and the proxy names to interact with. * Accessor for Toxiproxy mapped control port Hidden the port constant again and created accessor for Toxiproxy mapped control port. --- .../containers/ToxiproxyContainer.java | 11 +++++++++++ .../testcontainers/containers/ToxiproxyTest.java | 14 ++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java b/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java index e6c143fa358..e22cc1fdd55 100644 --- a/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java +++ b/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java @@ -63,6 +63,13 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { client = new ToxiproxyClient(getHost(), getMappedPort(TOXIPROXY_CONTROL_PORT)); } + /** + * @return Publicly exposed Toxiproxy HTTP API control port. + */ + public int getControlPort() { + return getMappedPort(TOXIPROXY_CONTROL_PORT); + } + /** * Obtain a {@link ContainerProxy} instance for target container that is managed by Testcontainers. The target * container should be routable from this from this {@link ToxiproxyContainer} instance (e.g. on the same @@ -130,6 +137,10 @@ public static class ContainerProxy { @Getter private final int originalProxyPort; private boolean isCurrentlyCut; + public String getName() { + return toxi.getName(); + } + public ToxicList toxics() { return toxi.toxics(); } diff --git a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java index 87f9758ebf1..3177fb2e8e8 100644 --- a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java +++ b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java @@ -149,6 +149,20 @@ public void testOriginalAndMappedPorts() { assertEquals("mapped port is correct", toxiproxy.getMappedPort(proxy2.getOriginalProxyPort()), proxy2.getProxyPort()); } + @Test + public void testProxyName() { + final ToxiproxyContainer.ContainerProxy proxy = toxiproxy.getProxy("hostname", 7070); + + assertEquals("proxy name is hostname and port", "hostname:7070", proxy.getName()); + } + + @Test + public void testControlPort() { + final int controlPort = toxiproxy.getControlPort(); + + assertEquals("control port is mapped from port 8474", toxiproxy.getMappedPort(8474), controlPort); + } + private void checkCallWithLatency(Jedis jedis, final String description, int expectedMinLatency, long expectedMaxLatency) { final long start = System.currentTimeMillis(); String s = jedis.get("somekey"); From c3a8ca76d0ed69f71c94e5bc2f7f1e0eef1a2259 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 10 Dec 2020 20:58:26 +0100 Subject: [PATCH 1755/5615] Update to docker-java 3.2.7 (#3577) --- core/build.gradle | 8 ++++---- .../dockerclient/DockerClientProviderStrategy.java | 8 +++++++- .../testcontainers/containers/GenericContainerTest.java | 2 +- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index ed0fc00d4f6..db624e555d9 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -173,9 +173,9 @@ dependencies { compile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' - compile "com.github.docker-java:docker-java-api:3.2.6" + compile "com.github.docker-java:docker-java-api:3.2.7" - shaded ('com.github.docker-java:docker-java-core:3.2.6') { + shaded ('com.github.docker-java:docker-java-core:3.2.7') { exclude(group: 'com.github.docker-java', module: 'docker-java-api') exclude(group: 'com.github.docker-java', module: 'docker-java-transport') exclude(group: 'com.fasterxml.jackson.core', module: 'jackson-annotations') @@ -184,13 +184,13 @@ dependencies { exclude(group: 'org.apache.commons', module: 'commons-compress') } - shaded ('com.github.docker-java:docker-java-transport-okhttp:3.2.6') { + shaded ('com.github.docker-java:docker-java-transport-okhttp:3.2.7') { exclude(group: 'com.github.docker-java', module: 'docker-java-core') exclude(group: 'net.java.dev.jna') exclude(group: 'org.slf4j') } - compile 'com.github.docker-java:docker-java-transport-zerodep:3.2.6' + compile 'com.github.docker-java:docker-java-transport-zerodep:3.2.7' shaded "org.yaml:snakeyaml:1.27" diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index 1bd51da1263..35e3b6eabb8 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -5,6 +5,7 @@ import com.github.dockerjava.api.model.Network; import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.core.DockerClientImpl; +import com.github.dockerjava.core.RemoteApiVersion; import com.github.dockerjava.okhttp.OkDockerHttpClient; import com.github.dockerjava.transport.DockerHttpClient; import com.github.dockerjava.zerodep.ZerodepDockerHttpClient; @@ -236,9 +237,14 @@ public static DockerClient getClientForConfig(TransportConfig transportConfig) { throw new IllegalArgumentException("Unknown transport type '" + transportType + "'"); } + DefaultDockerClientConfig.Builder configBuilder = DefaultDockerClientConfig.createDefaultConfigBuilder(); + + if (configBuilder.build().getApiVersion() == RemoteApiVersion.UNKNOWN_VERSION) { + configBuilder.withApiVersion(RemoteApiVersion.VERSION_1_30); + } return DockerClientImpl.getInstance( new AuthDelegatingDockerClientConfig( - DefaultDockerClientConfig.createDefaultConfigBuilder() + configBuilder .withDockerHost(transportConfig.getDockerHost().toString()) .build() ), diff --git a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java index 5008ba5baca..e059a48709f 100644 --- a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java +++ b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java @@ -27,7 +27,7 @@ public class GenericContainerTest { public void shouldReportOOMAfterWait() { Info info = DockerClientFactory.instance().client().infoCmd().exec(); // Poor man's rootless Docker detection :D - Assumptions.assumeThat(info.getDriver()).doesNotContain("vfs"); + Assumptions.assumeThat(info.getSecurityOptions()).doesNotContain("rootless"); try ( GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withStartupCheckStrategy(new NoopStartupCheckStrategy()) From f033c13086d99d01f398d86187ee6a9a465b6ee2 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Fri, 11 Dec 2020 12:49:40 +0100 Subject: [PATCH 1756/5615] Always use Path for MountableFile (#3514) Also, be conscious on Windows when adding host bind for Docker socket. Make sure ContainerisedDockerCompose consciously uses Unix path when necessary. Fixes #3493 --- .../containers/DockerComposeContainer.java | 10 +++++++++- .../testcontainers/containers/GenericContainer.java | 10 ++++++++-- .../java/org/testcontainers/utility/MountableFile.java | 2 +- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 7a67073e383..3d58ad53b3b 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -33,6 +33,7 @@ import org.testcontainers.utility.DockerLoggerFactory; import org.testcontainers.utility.LogUtils; import org.testcontainers.utility.MountableFile; +import org.testcontainers.utility.PathUtils; import org.testcontainers.utility.ResourceReaper; import org.zeroturnaround.exec.InvalidExitValueException; import org.zeroturnaround.exec.ProcessExecutor; @@ -618,12 +619,13 @@ public ContainerisedDockerCompose(List composeFiles, String identifier) { // Map the docker compose file into the container final File dockerComposeBaseFile = composeFiles.get(0); final String pwd = dockerComposeBaseFile.getAbsoluteFile().getParentFile().getAbsolutePath(); - final String containerPwd = MountableFile.forHostPath(pwd).getFilesystemPath(); + final String containerPwd = convertToUnixFilesystemPath(pwd); final List absoluteDockerComposeFiles = composeFiles.stream() .map(File::getAbsolutePath) .map(MountableFile::forHostPath) .map(MountableFile::getFilesystemPath) + .map(this::convertToUnixFilesystemPath) .collect(toList()); final String composeFileEnvVariableValue = Joiner.on(UNIX_PATH_SEPERATOR).join(absoluteDockerComposeFiles); // we always need the UNIX path separator logger().debug("Set env COMPOSE_FILE={}", composeFileEnvVariableValue); @@ -669,6 +671,12 @@ public void invoke() { StringUtils.join(this.getCommandParts(), ' ')); } } + + private String convertToUnixFilesystemPath(String path) { + return SystemUtils.IS_OS_WINDOWS + ? PathUtils.createMinGWPath(path).substring(1) + : path; + } } /** diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index bece4ef3378..0a0962cd6c0 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -63,6 +63,7 @@ import lombok.SneakyThrows; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.SystemUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.junit.runner.Description; @@ -946,9 +947,14 @@ public void addEnv(String key, String value) { */ @Override public void addFileSystemBind(final String hostPath, final String containerPath, final BindMode mode, final SelinuxContext selinuxContext) { + if (SystemUtils.IS_OS_WINDOWS && hostPath.startsWith("/")) { + // e.g. Docker socket mount + binds.add(new Bind(hostPath, new Volume(containerPath), mode.accessMode, selinuxContext.selContext)); - final MountableFile mountableFile = MountableFile.forHostPath(hostPath); - binds.add(new Bind(mountableFile.getResolvedPath(), new Volume(containerPath), mode.accessMode, selinuxContext.selContext)); + } else { + final MountableFile mountableFile = MountableFile.forHostPath(hostPath); + binds.add(new Bind(mountableFile.getResolvedPath(), new Volume(containerPath), mode.accessMode, selinuxContext.selContext)); + } } /** diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java index 7b7367b9290..1982538f45d 100644 --- a/core/src/main/java/org/testcontainers/utility/MountableFile.java +++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java @@ -105,7 +105,7 @@ public static MountableFile forClasspathResource(@NotNull final String resourceN * @return a {@link MountableFile} that may be used to obtain a mountable path */ public static MountableFile forHostPath(@NotNull final String path, Integer mode) { - return new MountableFile(new File(path).toURI().toString(), mode); + return forHostPath(Paths.get(path), mode); } /** From 16c82a4ab24b2a4b9d530ccd566d3ee386a92e9c Mon Sep 17 00:00:00 2001 From: Kirill Merkushev Date: Fri, 11 Dec 2020 15:06:55 +0100 Subject: [PATCH 1757/5615] Make MockServer compatible with newer (5.11.x) versions (#3437) Co-authored-by: Sergei Egorov --- docs/modules/mockserver.md | 6 +- .../containers/MockServerContainer.java | 5 +- .../MockServerContainerRuleTest.java | 40 +++++++++++++ .../containers/MockServerContainerTest.java | 56 +++++-------------- .../containers/SimpleHttpClient.java | 17 ++++++ 5 files changed, 80 insertions(+), 44 deletions(-) create mode 100644 modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java create mode 100644 modules/mockserver/src/test/java/org/testcontainers/containers/SimpleHttpClient.java diff --git a/docs/modules/mockserver.md b/docs/modules/mockserver.md index 051cecf076e..ead9d123915 100644 --- a/docs/modules/mockserver.md +++ b/docs/modules/mockserver.md @@ -7,13 +7,13 @@ Mock Server can be used to mock HTTP services by matching requests against user- The following example shows how to start Mockserver. -[Creating a MockServer container](../../modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java) inside_block:creatingProxy +[Creating a MockServer container](../../modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java) inside_block:creatingProxy And how to set a simple expectation using the Java MockServerClient. -[Setting a simple expectation](../../modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java) inside_block:testSimpleExpectation +[Setting a simple expectation](../../modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java) inside_block:testSimpleExpectation ## Adding this module to your project dependencies @@ -33,3 +33,5 @@ testCompile "org.testcontainers:mockserver:{{latest_version}}" ``` +Additionally, don't forget to add a [client dependency `org.mock-server:mockserver-client-java`](https://search.maven.org/search?q=mockserver-client-java) +to be able to set expectations, it's not provided by the testcontainers module. Client version should match to the version in a container tag. diff --git a/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java b/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java index 451a4710b20..0502469208e 100644 --- a/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java +++ b/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java @@ -1,6 +1,7 @@ package org.testcontainers.containers; import lombok.extern.slf4j.Slf4j; +import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.utility.DockerImageName; @Slf4j @@ -33,7 +34,9 @@ public MockServerContainer(String version) { public MockServerContainer(DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME, DockerImageName.parse("mockserver/mockserver")); + + waitingFor(Wait.forHttp("/mockserver/status").withMethod("PUT").forStatusCode(200)); withCommand("-logLevel INFO -serverPort " + PORT); addExposedPorts(PORT); diff --git a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java new file mode 100644 index 00000000000..d0c290ad46a --- /dev/null +++ b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java @@ -0,0 +1,40 @@ +package org.testcontainers.containers; + +import org.junit.Rule; +import org.junit.Test; +import org.mockserver.client.MockServerClient; +import org.testcontainers.utility.DockerImageName; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; +import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; + +public class MockServerContainerRuleTest { + + public static final DockerImageName MOCKSERVER_IMAGE = DockerImageName.parse("jamesdbloom/mockserver:mockserver-5.5.4"); + + // creatingProxy { + @Rule + public MockServerContainer mockServer = new MockServerContainer(MOCKSERVER_IMAGE); + // } + + @Test + public void shouldReturnExpectation() throws Exception { + // testSimpleExpectation { + new MockServerClient(mockServer.getHost(), mockServer.getServerPort()) + .when(request() + .withPath("/person") + .withQueryStringParameter("name", "peter")) + .respond(response() + .withBody("Peter the person!")); + + // ...a GET request to '/person?name=peter' returns "Peter the person!" + // } + + assertThat("Expectation returns expected response body", + SimpleHttpClient.responseFromMockserver(mockServer, "/person?name=peter"), + containsString("Peter the person") + ); + } +} diff --git a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java index e4d11ed3f39..61a5c33c630 100644 --- a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java +++ b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java @@ -1,57 +1,19 @@ package org.testcontainers.containers; -import lombok.Cleanup; -import org.junit.Rule; import org.junit.Test; import org.mockserver.client.MockServerClient; import org.testcontainers.utility.DockerImageName; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.URL; -import java.net.URLConnection; - -import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; import static org.mockserver.model.HttpRequest.request; import static org.mockserver.model.HttpResponse.response; import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; public class MockServerContainerTest { public static final DockerImageName MOCKSERVER_IMAGE = DockerImageName.parse("jamesdbloom/mockserver:mockserver-5.5.4"); - // creatingProxy { - @Rule - public MockServerContainer mockServer = new MockServerContainer(MOCKSERVER_IMAGE); - // } - - private static String responseFromMockserver(MockServerContainer mockServer, String path) throws IOException { - URLConnection urlConnection = new URL(mockServer.getEndpoint() + path).openConnection(); - @Cleanup BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); - return reader.readLine(); - } - - @Test - public void shouldReturnExpectation() throws Exception { - // testSimpleExpectation { - new MockServerClient(mockServer.getHost(), mockServer.getServerPort()) - .when(request() - .withPath("/person") - .withQueryStringParameter("name", "peter")) - .respond(response() - .withBody("Peter the person!")); - - // ...a GET request to '/person?name=peter' returns "Peter the person!" - // } - - assertThat("Expectation returns expected response body", - responseFromMockserver(mockServer, "/person?name=peter"), - containsString("Peter the person") - ); - } - @Test public void shouldCallActualMockserverVersion() throws Exception { String actualVersion = MockServerClient.class.getPackage().getImplementationVersion(); @@ -60,14 +22,26 @@ public void shouldCallActualMockserverVersion() throws Exception { String expectedBody = "Hello World!"; - new MockServerClient(mockServer.getHost(), mockServer.getServerPort()) + MockServerClient client = new MockServerClient(mockServer.getHost(), mockServer.getServerPort()); + + assertTrue("Mockserver running", client.isRunning()); + + client .when(request().withPath("/hello")) .respond(response().withBody(expectedBody)); assertThat("MockServer returns correct result", - responseFromMockserver(mockServer, "/hello"), + SimpleHttpClient.responseFromMockserver(mockServer, "/hello"), equalTo(expectedBody) ); } } + + @Test + public void newVersionStartsWithDefaultWaitStrategy() { + DockerImageName dockerImageName = DockerImageName.parse("mockserver/mockserver:mockserver-5.11.2"); + try (MockServerContainer mockServer = new MockServerContainer(dockerImageName)) { + mockServer.start(); + } + } } diff --git a/modules/mockserver/src/test/java/org/testcontainers/containers/SimpleHttpClient.java b/modules/mockserver/src/test/java/org/testcontainers/containers/SimpleHttpClient.java new file mode 100644 index 00000000000..cd5cb25cabb --- /dev/null +++ b/modules/mockserver/src/test/java/org/testcontainers/containers/SimpleHttpClient.java @@ -0,0 +1,17 @@ +package org.testcontainers.containers; + +import lombok.Cleanup; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.net.URLConnection; + +public class SimpleHttpClient { + public static String responseFromMockserver(MockServerContainer mockServer, String path) throws IOException { + URLConnection urlConnection = new URL(mockServer.getEndpoint() + path).openConnection(); + @Cleanup BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); + return reader.readLine(); + } +} From 4fc04b64e474075bb88c1f4886a1189d89e01da4 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 11 Dec 2020 15:16:05 +0100 Subject: [PATCH 1758/5615] Use lower Jackson version to avoid conflicts. (#3579) Helps with #3433 --- core/build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/build.gradle b/core/build.gradle index db624e555d9..0dc6f07ddf9 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -175,6 +175,10 @@ dependencies { compile "com.github.docker-java:docker-java-api:3.2.7" + // use lower Jackson version + shaded ('com.fasterxml.jackson.core:jackson-databind:2.8.8') { + exclude(group: 'com.fasterxml.jackson.core', module: 'jackson-annotations') + } shaded ('com.github.docker-java:docker-java-core:3.2.7') { exclude(group: 'com.github.docker-java', module: 'docker-java-api') exclude(group: 'com.github.docker-java', module: 'docker-java-transport') From 6932c7aaf98cc17ee66ed5ab33f7a85456a5850d Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 11 Dec 2020 18:36:24 +0100 Subject: [PATCH 1759/5615] Update mkdocs.yml to the latest release --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 618b9679291..2457d06bcf0 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -94,4 +94,4 @@ nav: - contributing_docs.md - bounty.md extra: - latest_version: 1.15.0 + latest_version: 1.15.1 From 87da74e422c519cd0ac2fa124a0e47819145d5b0 Mon Sep 17 00:00:00 2001 From: jochenchrist Date: Sat, 12 Dec 2020 16:55:17 +0100 Subject: [PATCH 1760/5615] Startables#deepStart with varargs (#3261) Co-authored-by: Jochen Christ --- .../java/org/testcontainers/lifecycle/Startables.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/src/main/java/org/testcontainers/lifecycle/Startables.java b/core/src/main/java/org/testcontainers/lifecycle/Startables.java index 292e1ab17b3..5ec46f1edb9 100644 --- a/core/src/main/java/org/testcontainers/lifecycle/Startables.java +++ b/core/src/main/java/org/testcontainers/lifecycle/Startables.java @@ -1,5 +1,6 @@ package org.testcontainers.lifecycle; +import java.util.Arrays; import lombok.experimental.UtilityClass; import java.util.Collection; @@ -42,6 +43,13 @@ public CompletableFuture deepStart(Iterable startable return deepStart(StreamSupport.stream(startables.spliterator(), false)); } + /** + * @see #deepStart(Stream) + */ + public CompletableFuture deepStart(Startable... startables) { + return deepStart(Arrays.stream(startables)); + } + /** * Start every {@link Startable} recursively and asynchronously and join on the result. * From 30f399eca0e4d56c5701cc2aa02bec85f875d6ec Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 29 Dec 2020 13:00:48 +0100 Subject: [PATCH 1761/5615] Remove ciMate (#3631) --- .circleci/config.yml | 9 --------- .github/workflows/ci-docker-wormhole.yml | 9 --------- .github/workflows/ci-examples.yml | 10 ---------- .github/workflows/ci-rootless.yml | 10 ---------- .github/workflows/ci.yml | 20 -------------------- azure-pipelines.yml | 2 -- 6 files changed, 60 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8f700dba2b5..b8f75aaf4db 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,15 +7,6 @@ jobs: - checkout - run: command: ./gradlew --no-daemon --continue --scan testcontainers:test --tests '*GenericContainerRuleTest' - - run: - name: aggregate test reports with ciMate - when: always - environment: - CIMATE_PROJECT_ID: 2348n4vl - command: | - wget -q https://get.cimate.io/release/linux/cimate - chmod +x cimate - ./cimate "**/TEST-*.xml" - run: name: Save test results command: | diff --git a/.github/workflows/ci-docker-wormhole.yml b/.github/workflows/ci-docker-wormhole.yml index f9e106d56b7..d750d8b39e0 100644 --- a/.github/workflows/ci-docker-wormhole.yml +++ b/.github/workflows/ci-docker-wormhole.yml @@ -18,12 +18,3 @@ jobs: -w "$PWD" \ openjdk:8-jdk-alpine \ ./gradlew --no-daemon --continue --scan testcontainers:test --tests '*GenericContainerRuleTest' - - name: aggregate test reports with ciMate - if: always() - continue-on-error: true - env: - CIMATE_PROJECT_ID: 2348n4vl - run: | - wget -q https://get.cimate.io/release/linux/cimate - chmod +x cimate - ./cimate "**/TEST-*.xml" diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index 4f91341a4a6..d69aebcfa88 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -57,13 +57,3 @@ jobs: working-directory: ./examples/ run: | ./gradlew --no-daemon --continue --scan --info ${{matrix.gradle_args}} - - name: Aggregate Examples test reports with ciMate - if: always() - continue-on-error: true - env: - CIMATE_PROJECT_ID: 2348n4vl - CIMATE_CI_KEY: "Examples / ${{ runner.os }} / ${{matrix.gradle_args}}" - run: | - wget -q https://get.cimate.io/release/linux/cimate - chmod +x cimate - ./cimate "**/TEST-*.xml" diff --git a/.github/workflows/ci-rootless.yml b/.github/workflows/ci-rootless.yml index bf1fe08bbd2..71ded85cf53 100644 --- a/.github/workflows/ci-rootless.yml +++ b/.github/workflows/ci-rootless.yml @@ -32,13 +32,3 @@ jobs: DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock docker info || ls -la $XDG_RUNTIME_DIR - name: Build with Gradle run: ./gradlew --no-daemon --scan testcontainers:test --tests '*GenericContainerRuleTest' - - name: aggregate test reports with ciMate - if: always() - continue-on-error: true - env: - CIMATE_PROJECT_ID: 2348n4vl - CIMATE_CI_KEY: "CI / rootless Docker (${{ matrix.XDG_RUNTIME_DIR }})" - run: | - wget -q https://get.cimate.io/release/linux/cimate - chmod +x cimate - ./cimate "**/TEST-*.xml" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index df38f3b8a4f..ef0f915972b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,16 +54,6 @@ jobs: - name: Build and test with Gradle (${{matrix.gradle_args}}) run: | ./gradlew --no-daemon --continue --scan ${{matrix.gradle_args}} - - name: Aggregate test reports with ciMate - if: always() - continue-on-error: true - env: - CIMATE_PROJECT_ID: 2348n4vl - CIMATE_CI_KEY: "CI / ${{ runner.os }} / ${{matrix.gradle_args}}" - run: | - wget -q https://get.cimate.io/release/linux/cimate - chmod +x cimate - ./cimate "**/TEST-*.xml" httpclient5_test: runs-on: ubuntu-18.04 steps: @@ -73,13 +63,3 @@ jobs: echo "transport.type=httpclient5" > $HOME/.testcontainers.properties cat $HOME/.testcontainers.properties ./gradlew --no-daemon --scan testcontainers:test --tests '*EventStreamTest' - - name: aggregate test reports with ciMate - if: always() - continue-on-error: true - env: - CIMATE_PROJECT_ID: 2348n4vl - CIMATE_CI_KEY: "CI / Apache HttpClient5" - run: | - wget -q https://get.cimate.io/release/linux/cimate - chmod +x cimate - ./cimate "**/TEST-*.xml" diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 185240ae7eb..1471f9948ca 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -17,8 +17,6 @@ jobs: tasks: "clean testcontainers:test --tests GenericContainerRuleTest" publishJUnitResults: true testResultsFiles: '**/TEST-*.xml' - - script: wget -q https://get.cimate.io/release/linux/cimate && chmod +x cimate && ./cimate "**/TEST-*.xml" - condition: and(succeededOrFailed(),eq(variables['Agent.OS'], 'Linux')) # Run all tests when running the Windows CI tests - task: Gradle@2 From 184afddf981b58497823fcfae0c51c008ad9d361 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 29 Dec 2020 12:44:10 +0000 Subject: [PATCH 1762/5615] Upgrade Ryuk to 0.3.1 (#3629) Co-authored-by: Sergei Egorov --- .../main/java/org/testcontainers/utility/ResourceReaper.java | 2 +- .../utility/TestcontainersConfigurationTest.java | 4 ++-- docs/features/configuration.md | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index f409dbbc865..71a4b49d0d3 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -88,7 +88,7 @@ public static String start(String hostIpAddress, DockerClient client) { @SneakyThrows(InterruptedException.class) public static String start(DockerClient client) { String ryukImage = ImageNameSubstitutor.instance() - .apply(DockerImageName.parse("testcontainers/ryuk:0.3.0")) + .apply(DockerImageName.parse("testcontainers/ryuk:0.3.1")) .asCanonicalNameString(); DockerClientFactory.instance().checkAndPullImage(client, ryukImage); diff --git a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java index ae07e2d9131..e5300bce58d 100644 --- a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java +++ b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java @@ -160,8 +160,8 @@ public void shouldReadReuseFromEnvironment() { @Test public void shouldTrimImageNames() { - userProperties.setProperty("ryuk.container.image", " testcontainersofficial/ryuk:0.3.0 "); - assertEquals("trailing whitespace was not removed from image name property", "testcontainersofficial/ryuk:0.3.0",newConfig().getRyukImage()); + userProperties.setProperty("ryuk.container.image", " testcontainers/ryuk:0.3.1 "); + assertEquals("trailing whitespace was not removed from image name property", "testcontainers/ryuk:0.3.1",newConfig().getRyukImage()); } private TestcontainersConfiguration newConfig() { diff --git a/docs/features/configuration.md b/docs/features/configuration.md index b567a8f564f..da7bbd82d60 100644 --- a/docs/features/configuration.md +++ b/docs/features/configuration.md @@ -47,7 +47,7 @@ It takes a couple of seconds, but if you want to speed up your tests, you can di Testcontainers uses public Docker images to perform different actions like startup checks, VNC recording and others. Some companies disallow the usage of Docker Hub, but you can override `*.image` properties with your own images from your private registry to workaround that. -> **ryuk.container.image = testcontainers/ryuk:0.3.0** +> **ryuk.container.image = testcontainers/ryuk:0.3.1** > Performs fail-safe cleanup of containers, and always required (unless [Ryuk is disabled](#disabling-ryuk)) > **tinyimage.container.image = alpine:3.5** @@ -74,7 +74,7 @@ Some companies disallow the usage of Docker Hub, but you can override `*.image` ## Customizing Ryuk resource reaper -> **ryuk.container.image = testcontainers/ryuk:0.3.0** +> **ryuk.container.image = testcontainers/ryuk:0.3.1** > The resource reaper is responsible for container removal and automatic cleanup of dead containers at JVM shutdown > **ryuk.container.privileged = false** From 84f6dd815b56bf9a089a8dfe9b74622dce57fad7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Dec 2020 13:51:30 +0000 Subject: [PATCH 1763/5615] Bump assertj-core from 3.18.0 to 3.18.1 in /modules/vault (#3486) Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.18.0 to 3.18.1. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.18.0...assertj-core-3.18.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index caddc81d3a7..7db7fb2322a 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -5,6 +5,6 @@ dependencies { testCompile 'com.bettercloud:vault-java-driver:5.1.0' testCompile 'io.rest-assured:rest-assured:4.3.1' - testCompile 'org.assertj:assertj-core:3.18.0' + testCompile 'org.assertj:assertj-core:3.18.1' } From 7c4e935578861c99b0f295f5d18162f745aa9247 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Dec 2020 13:51:46 +0000 Subject: [PATCH 1764/5615] Bump influxdb-java from 2.20 to 2.21 in /modules/influxdb (#3568) Bumps [influxdb-java](https://github.com/influxdata/influxdb-java) from 2.20 to 2.21. - [Release notes](https://github.com/influxdata/influxdb-java/releases) - [Changelog](https://github.com/influxdata/influxdb-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/influxdata/influxdb-java/compare/influxdb-java-2.20...influxdb-java-2.21) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/influxdb/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/influxdb/build.gradle b/modules/influxdb/build.gradle index 40fa6e456ce..2e735e1137a 100644 --- a/modules/influxdb/build.gradle +++ b/modules/influxdb/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: InfluxDB" dependencies { compile project(':testcontainers') - compileOnly 'org.influxdb:influxdb-java:2.20' - testCompile 'org.influxdb:influxdb-java:2.19' + compileOnly 'org.influxdb:influxdb-java:2.21' + testCompile 'org.influxdb:influxdb-java:2.21' } From 5f7bb3ae4f2edcfe79dd451f29cfed3c212a2577 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Dec 2020 13:52:07 +0000 Subject: [PATCH 1765/5615] Bump json from 20180813 to 20201115 in /examples (#3567) Bumps [json](https://github.com/douglascrockford/JSON-java) from 20180813 to 20201115. - [Release notes](https://github.com/douglascrockford/JSON-java/releases) - [Commits](https://github.com/douglascrockford/JSON-java/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index f5f21a57d21..5c7279dd09a 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' implementation 'com.squareup.okhttp3:okhttp:4.9.0' - implementation 'org.json:json:20180813' + implementation 'org.json:json:20201115' testImplementation 'org.postgresql:postgresql:42.2.18' testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.testcontainers:postgresql' From 850f72eeb7dd7584ae1b46a1a5ee175c9f252bc1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Dec 2020 13:52:21 +0000 Subject: [PATCH 1766/5615] Bump pulsar-client from 2.6.1 to 2.7.0 in /modules/pulsar (#3565) Bumps [pulsar-client](https://github.com/apache/pulsar) from 2.6.1 to 2.7.0. - [Release notes](https://github.com/apache/pulsar/releases) - [Commits](https://github.com/apache/pulsar/compare/v2.6.1...v2.7.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index e09ff42f357..00455fc6602 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Pulsar" dependencies { compile project(':testcontainers') - testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.6.1' + testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.7.0' testCompile group: 'org.assertj', name: 'assertj-core', version: '3.18.1' testCompile group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.6.1' } From 7d683a1cb238d1c5c9eaf9916d010f5479182237 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Dec 2020 13:52:36 +0000 Subject: [PATCH 1767/5615] Bump pulsar-client-admin from 2.6.1 to 2.7.0 in /modules/pulsar (#3566) Bumps [pulsar-client-admin](https://github.com/apache/pulsar) from 2.6.1 to 2.7.0. - [Release notes](https://github.com/apache/pulsar/releases) - [Commits](https://github.com/apache/pulsar/compare/v2.6.1...v2.7.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 00455fc6602..b574519a939 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -5,5 +5,5 @@ dependencies { testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.7.0' testCompile group: 'org.assertj', name: 'assertj-core', version: '3.18.1' - testCompile group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.6.1' + testCompile group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.7.0' } From bf16881fb9904afebc4bf15ab3a2ed0ea3bfdb99 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Dec 2020 14:08:45 +0000 Subject: [PATCH 1768/5615] Bump mssql-jdbc from 9.1.0.jre8-preview to 9.1.1.jre8-preview in /modules/mssqlserver (#3563) Bumps [mssql-jdbc](https://github.com/Microsoft/mssql-jdbc) from 9.1.0.jre8-preview to 9.1.1.jre8-preview. - [Release notes](https://github.com/Microsoft/mssql-jdbc/releases) - [Changelog](https://github.com/microsoft/mssql-jdbc/blob/dev/CHANGELOG.md) - [Commits](https://github.com/Microsoft/mssql-jdbc/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/mssqlserver/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index 9aab7844148..765e1382180 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -10,12 +10,12 @@ dependencies { compileOnly 'io.r2dbc:r2dbc-mssql:0.8.4.RELEASE' testCompile project(':jdbc-test') - testCompile 'com.microsoft.sqlserver:mssql-jdbc:9.1.0.jre8-preview' + testCompile 'com.microsoft.sqlserver:mssql-jdbc:9.1.1.jre8-preview' testCompile project(':r2dbc') testCompile 'io.r2dbc:r2dbc-mssql:0.8.3.RELEASE' // MSSQL's wait strategy requires the JDBC driver testCompile testFixtures(project(':r2dbc')) - testCompile 'com.microsoft.sqlserver:mssql-jdbc:9.1.0.jre8-preview' + testCompile 'com.microsoft.sqlserver:mssql-jdbc:9.1.1.jre8-preview' } From d1217ab02e5994e88760e046c4ce0d74dfaeb1bd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Dec 2020 14:43:52 +0000 Subject: [PATCH 1769/5615] Bump mockito-core from 3.5.13 to 3.6.28 in /core (#3542) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.5.13 to 3.6.28. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.5.13...v3.6.28) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 0dc6f07ddf9..35a8bb92f32 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -209,7 +209,7 @@ dependencies { testCompile 'com.rabbitmq:amqp-client:5.9.0' testCompile 'org.mongodb:mongo-java-driver:3.12.7' - testCompile ('org.mockito:mockito-core:3.5.13') { + testCompile ('org.mockito:mockito-core:3.6.28') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From 96429538eb3a1f080030d79c36acf9275405d206 Mon Sep 17 00:00:00 2001 From: Martin Traverso Date: Thu, 31 Dec 2020 03:10:18 -0800 Subject: [PATCH 1770/5615] Rename Presto to Trino (#3649) PrestoSQL was rebranded as Trino: https://trino.io/blog/2020/12/27/announcing-trino.html --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 2351837f8af..bc6d1e64afe 100644 --- a/docs/index.md +++ b/docs/index.md @@ -162,7 +162,7 @@ A huge thank you to our sponsors: * [XWiki](https://xwiki.org) - [Testing XWiki](https://dev.xwiki.org/xwiki/bin/view/Community/Testing/DockerTesting/) under all [supported configurations](https://dev.xwiki.org/xwiki/bin/view/Community/SupportStrategy/) * [Apache SkyWalking](http://github.com/apache/skywalking) - End-to-end testing of the Apache SkyWalking, and plugin tests of its subproject, [Apache SkyWalking Python](http://github.com/apache/skywalking-python), and of its eco-system built by the community, like [SkyAPM NodeJS Agent](http://github.com/SkyAPM/nodejs) * [jOOQ](https://www.jooq.org) - Integration testing all of jOOQ with a variety of RDBMS -* [Presto](https://prestosql.io) - Integration testing all Presto core & connectors, including tests of multi-node deployments and security configurations. +* [Trino (formerly Presto SQL)](https://trino.io) - Integration testing all Trino core & connectors, including tests of multi-node deployments and security configurations. ## License From 54000d9574213574df7e5b211f3b15a50d1dea3d Mon Sep 17 00:00:00 2001 From: Joseph Petersen Date: Thu, 31 Dec 2020 12:19:54 +0100 Subject: [PATCH 1771/5615] add github actions to dependabot config (#3633) Co-authored-by: Richard North --- .github/dependabot.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d854a9aa823..16b59da6dfa 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -139,3 +139,10 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + +# GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 From 4e11573dcd0f9c19ceff519038c863ae807f20b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 31 Dec 2020 11:39:08 +0000 Subject: [PATCH 1772/5615] Bump mockito-core from 3.5.15 to 3.6.28 in /modules/junit-jupiter (#3539) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.5.15 to 3.6.28. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.5.15...v3.6.28) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 520cff37254..12d4321d10f 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,7 +9,7 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.4.5' testCompile 'redis.clients:jedis:3.3.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.13' - testCompile ('org.mockito:mockito-core:3.5.15') { + testCompile ('org.mockito:mockito-core:3.6.28') { exclude(module: 'hamcrest-core') } testCompile 'org.assertj:assertj-core:3.18.1' From 3a32e3fd98655e80d2f22dca4fa0944d1c84d1fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 31 Dec 2020 11:39:16 +0000 Subject: [PATCH 1773/5615] Bump r2dbc-mssql from 0.8.4.RELEASE to 0.8.5.RELEASE in /modules/mssqlserver (#3397) Bumps [r2dbc-mssql](https://github.com/r2dbc/r2dbc-mssql) from 0.8.4.RELEASE to 0.8.5.RELEASE. - [Release notes](https://github.com/r2dbc/r2dbc-mssql/releases) - [Changelog](https://github.com/r2dbc/r2dbc-mssql/blob/v0.8.5.RELEASE/CHANGELOG) - [Commits](https://github.com/r2dbc/r2dbc-mssql/compare/v0.8.4.RELEASE...v0.8.5.RELEASE) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/mssqlserver/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index 765e1382180..c36f1e6ba84 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -7,13 +7,13 @@ dependencies { compile project(':jdbc') compileOnly project(':r2dbc') - compileOnly 'io.r2dbc:r2dbc-mssql:0.8.4.RELEASE' + compileOnly 'io.r2dbc:r2dbc-mssql:0.8.5.RELEASE' testCompile project(':jdbc-test') testCompile 'com.microsoft.sqlserver:mssql-jdbc:9.1.1.jre8-preview' testCompile project(':r2dbc') - testCompile 'io.r2dbc:r2dbc-mssql:0.8.3.RELEASE' + testCompile 'io.r2dbc:r2dbc-mssql:0.8.5.RELEASE' // MSSQL's wait strategy requires the JDBC driver testCompile testFixtures(project(':r2dbc')) From a475797c9706e7f2934b6201c81002095903c5c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 31 Dec 2020 11:39:27 +0000 Subject: [PATCH 1774/5615] Bump rest-assured from 4.3.1 to 4.3.3 in /modules/vault (#3588) Bumps [rest-assured](https://github.com/rest-assured/rest-assured) from 4.3.1 to 4.3.3. - [Release notes](https://github.com/rest-assured/rest-assured/releases) - [Changelog](https://github.com/rest-assured/rest-assured/blob/master/changelog.txt) - [Commits](https://github.com/rest-assured/rest-assured/compare/rest-assured-4.3.1...rest-assured-4.3.3) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index 7db7fb2322a..e75d1121131 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -4,7 +4,7 @@ dependencies { compile project(':testcontainers') testCompile 'com.bettercloud:vault-java-driver:5.1.0' - testCompile 'io.rest-assured:rest-assured:4.3.1' + testCompile 'io.rest-assured:rest-assured:4.3.3' testCompile 'org.assertj:assertj-core:3.18.1' } From 4a03031d27d1de6f4903258df13480a21aa3fdbd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 31 Dec 2020 11:39:37 +0000 Subject: [PATCH 1775/5615] Bump jedis from 3.3.0 to 3.4.0 in /core (#3586) Bumps [jedis](https://github.com/xetorthio/jedis) from 3.3.0 to 3.4.0. - [Release notes](https://github.com/xetorthio/jedis/releases) - [Commits](https://github.com/xetorthio/jedis/compare/jedis-3.3.0...jedis-3.4.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 35a8bb92f32..ef63ce62bda 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -205,7 +205,7 @@ dependencies { } testCompile 'org.apache.httpcomponents:httpclient:4.5.9' - testCompile 'redis.clients:jedis:3.3.0' + testCompile 'redis.clients:jedis:3.4.0' testCompile 'com.rabbitmq:amqp-client:5.9.0' testCompile 'org.mongodb:mongo-java-driver:3.12.7' From cb2c31005002eef20946f519b23136ae00969751 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 31 Dec 2020 11:39:51 +0000 Subject: [PATCH 1776/5615] Bump elasticsearch-rest-client from 7.10.0 to 7.10.1 in /modules/elasticsearch (#3589) Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 7.10.0 to 7.10.1. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.10.0...v7.10.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index bfbfbfae9ce..a3442f3234b 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,6 +2,6 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') - testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.10.0" + testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.10.1" testCompile "org.elasticsearch.client:transport:7.10.0" } From 8e7fff80fe7d6e45f6170734979903f70a0e4655 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 31 Dec 2020 11:40:10 +0000 Subject: [PATCH 1777/5615] Bump tomcat-jdbc from 9.0.40 to 10.0.0 in /modules/jdbc (#3592) Bumps tomcat-jdbc from 9.0.40 to 10.0.0. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 12a53f7cfbc..910dc29ebad 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -7,7 +7,7 @@ dependencies { compileOnly 'org.jetbrains:annotations:20.1.0' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.vibur:vibur-dbcp:25.0' - testCompile 'org.apache.tomcat:tomcat-jdbc:9.0.40' + testCompile 'org.apache.tomcat:tomcat-jdbc:10.0.0' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'com.googlecode.junit-toolbox:junit-toolbox:2.4' } From 804116c18b52590b022e28b10a76e89866f3ed29 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 31 Dec 2020 11:40:18 +0000 Subject: [PATCH 1778/5615] Bump jedis from 3.3.0 to 3.4.0 in /examples (#3595) Bumps [jedis](https://github.com/xetorthio/jedis) from 3.3.0 to 3.4.0. - [Release notes](https://github.com/xetorthio/jedis/releases) - [Commits](https://github.com/xetorthio/jedis/compare/jedis-3.3.0...jedis-3.4.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 6ef351764f5..76c245d5b51 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' - implementation 'redis.clients:jedis:3.3.0' + implementation 'redis.clients:jedis:3.4.0' implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index b0b6865aea7..e63d494bcef 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' - implementation 'redis.clients:jedis:3.3.0' + implementation 'redis.clients:jedis:3.4.0' implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 11e6cb2a31b..bbd35cabc49 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { - implementation 'redis.clients:jedis:3.3.0' + implementation 'redis.clients:jedis:3.4.0' implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.30' From 463c3bb5d85be43f0950c7ae09f46e7882cfd5b3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 31 Dec 2020 11:40:30 +0000 Subject: [PATCH 1779/5615] Bump org.springframework.boot from 2.3.4.RELEASE to 2.4.1 in /examples (#3594) Bumps [org.springframework.boot](https://github.com/spring-projects/spring-boot) from 2.3.4.RELEASE to 2.4.1. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.3.4.RELEASE...v2.4.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot-kotlin-redis/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index 2dca03f4004..1f05d7b5663 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.3.4.RELEASE' + id 'org.springframework.boot' version '2.4.1' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index f2d9d8d97ec..9c622648a20 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,5 +1,5 @@ plugins { - id("org.springframework.boot") version "2.1.5.RELEASE" + id("org.springframework.boot") version "2.4.1" id("org.jetbrains.kotlin.jvm") version "1.3.31" id("org.jetbrains.kotlin.plugin.spring") version "1.3.31" } diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index 75907da4c0d..288a9226b90 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.3.4.RELEASE' + id 'org.springframework.boot' version '2.4.1' } apply plugin: 'io.spring.dependency-management' From cdd1837b7f40b749ad9576b141e3dc7f873f475b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 31 Dec 2020 11:41:08 +0000 Subject: [PATCH 1780/5615] Bump r2dbc-mariadb from 0.8.4-rc to 1.0.0 in /modules/mariadb (#3593) Bumps [r2dbc-mariadb](https://github.com/mariadb-corporation/mariadb-connector-r2dbc) from 0.8.4-rc to 1.0.0. - [Release notes](https://github.com/mariadb-corporation/mariadb-connector-r2dbc/releases) - [Changelog](https://github.com/mariadb-corporation/mariadb-connector-r2dbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/mariadb-corporation/mariadb-connector-r2dbc/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/mariadb/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index e5f19eed97b..915e14eb7ee 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -7,11 +7,11 @@ dependencies { compile project(':jdbc') compileOnly project(':r2dbc') - compileOnly 'org.mariadb:r2dbc-mariadb:0.8.4-rc' + compileOnly 'org.mariadb:r2dbc-mariadb:1.0.0' testCompile project(':jdbc-test') testCompile 'org.mariadb.jdbc:mariadb-java-client:2.7.0' testCompile testFixtures(project(':r2dbc')) - testCompile 'org.mariadb:r2dbc-mariadb:0.8.3-beta1' + testCompile 'org.mariadb:r2dbc-mariadb:1.0.0' } From 6eb362271ff94b519370f752fc3b9844ad47e2d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 31 Dec 2020 11:41:16 +0000 Subject: [PATCH 1781/5615] Bump cucumber-java from 6.8.1 to 6.9.1 in /examples (#3624) Bumps [cucumber-java](https://github.com/cucumber/cucumber-jvm) from 6.8.1 to 6.9.1. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v6.8.1...v6.9.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 57104731c7e..d3be66ddcc6 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' - testImplementation 'io.cucumber:cucumber-java:6.8.1' + testImplementation 'io.cucumber:cucumber-java:6.9.1' testImplementation 'io.cucumber:cucumber-junit:6.8.1' testImplementation 'org.testcontainers:selenium' } From 394cdce6827f5e785a233f7d67cbc72bd0e7ac84 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 31 Dec 2020 11:42:06 +0000 Subject: [PATCH 1782/5615] Bump solr-solrj from 8.6.3 to 8.7.0 in /examples (#3446) Bumps solr-solrj from 8.6.3 to 8.7.0. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/solr-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/solr-container/build.gradle b/examples/solr-container/build.gradle index 75524364100..fcdf93a9c53 100644 --- a/examples/solr-container/build.gradle +++ b/examples/solr-container/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly "org.projectlombok:lombok:1.18.16" annotationProcessor "org.projectlombok:lombok:1.18.16" - implementation 'org.apache.solr:solr-solrj:8.6.3' + implementation 'org.apache.solr:solr-solrj:8.7.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.testcontainers:solr' From 63d0290639e39084396d2eb4e4ff1be01a4a4abd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jan 2021 15:10:00 +0000 Subject: [PATCH 1783/5615] Update actions/cache requirement to v2.1.3 (#3651) Updates the requirements on [actions/cache](https://github.com/actions/cache) to permit the latest version. - [Release notes](https://github.com/actions/cache/releases) - [Commits](https://github.com/actions/cache/commits/0781355a23dac32fd3bac414512f4b903437991a) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index d69aebcfa88..cea7180566d 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -19,7 +19,7 @@ jobs: with: java-version: '1.8' - name: Cache Gradle Home files - uses: actions/cache@v1 + uses: actions/cache@v2.1.3 continue-on-error: true with: path: ~/.gradle/caches @@ -46,7 +46,7 @@ jobs: with: java-version: '1.8' - name: Cache Gradle Home files - uses: actions/cache@v1 + uses: actions/cache@v2.1.3 continue-on-error: true with: path: ~/.gradle/caches diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ef0f915972b..f49b6426a2e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: with: java-version: '1.8' - name: Cache Gradle Home files - uses: actions/cache@v1 + uses: actions/cache@v2.1.3 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-home-testmatrix-${{ hashFiles('**/*.gradle') }} @@ -44,7 +44,7 @@ jobs: with: java-version: '1.8' - name: Cache Gradle Home files - uses: actions/cache@v2 + uses: actions/cache@v2.1.3 continue-on-error: true with: path: ~/.gradle/caches From c9528eea9477696fe75f7fb5a53015e039270272 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jan 2021 15:11:20 +0000 Subject: [PATCH 1784/5615] Bump gradle/wrapper-validation-action from e2c57ac to 1.0.3 (#3657) Bumps [gradle/wrapper-validation-action](https://github.com/gradle/wrapper-validation-action) from e2c57ac to 1.0.3. This release includes the previously tagged commit. - [Release notes](https://github.com/gradle/wrapper-validation-action/releases) - [Commits](https://github.com/gradle/wrapper-validation-action/compare/e2c57ac...e2c57acffb2c9aa5a8dc6eda2bbae0b6e495bd4c) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/update-gradle-wrapper.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-gradle-wrapper.yml b/.github/workflows/update-gradle-wrapper.yml index f3a01949324..883d6dd6cbc 100644 --- a/.github/workflows/update-gradle-wrapper.yml +++ b/.github/workflows/update-gradle-wrapper.yml @@ -17,4 +17,4 @@ jobs: repo-token: ${{ secrets.GITHUB_TOKEN }} labels: dependencies - - uses: gradle/wrapper-validation-action@e2c57ac + - uses: gradle/wrapper-validation-action@e2c57acffb2c9aa5a8dc6eda2bbae0b6e495bd4c From 3b07d82c18e6f65f8b9c0a443410eb6486aa7ff1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jan 2021 15:13:30 +0000 Subject: [PATCH 1785/5615] Bump aws-java-sdk-dynamodb from 1.11.901 to 1.11.929 in /modules/dynalite (#3652) Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.11.901 to 1.11.929. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.901...1.11.929) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/dynalite/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 5f6ab14d939..a09ef417159 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Dynalite" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.901' - testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.882' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.929' + testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.929' } From 7d2e184b644b50d0ff964203708a8c82398aa836 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jan 2021 15:20:56 +0000 Subject: [PATCH 1786/5615] Bump ad-m/github-push-action from 68af9897f2b021035ca3952bf354bbb4675c1762 to 0.6.0 (#3658) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- .github/workflows/update-docs-version.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index cce6e5b9bf1..d2eb803fda2 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -22,6 +22,6 @@ jobs: git add mkdocs.yml git commit -m "Update documentation version to ${GITHUB_REF##*/}" -m "skip-checks: true" || echo "not changed" - name: Push changes - uses: ad-m/github-push-action@68af9897f2b021035ca3952bf354bbb4675c1762 # v0.5.0 + uses: ad-m/github-push-action@40bf560936a8022e68a3c00e7d2abefaf01305a6 with: github_token: ${{ secrets.GITHUB_TOKEN }} From fac5e852a0b2ad999ae5ea9991777668c64adeaf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jan 2021 16:51:22 +0000 Subject: [PATCH 1787/5615] Bump guava from 30.0-jre to 30.1-jre in /core (#3627) Bumps [guava](https://github.com/google/guava) from 30.0-jre to 30.1-jre. - [Release notes](https://github.com/google/guava/releases) - [Commits](https://github.com/google/guava/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index ef63ce62bda..4b42173cd0f 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -5,7 +5,7 @@ buildscript { dependencies { // https://github.com/melix/japicmp-gradle-plugin/issues/36 - classpath 'com.google.guava:guava:30.0-jre' + classpath 'com.google.guava:guava:30.1-jre' } } From 288af58ed0425b1edc713c33d93eb5fb66492ee0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jan 2021 16:51:31 +0000 Subject: [PATCH 1788/5615] Bump org.jetbrains.kotlin.jvm from 1.3.31 to 1.4.21-2 in /examples (#3645) Bumps org.jetbrains.kotlin.jvm from 1.3.31 to 1.4.21-2. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 9c622648a20..cc1083ee8d4 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,6 +1,6 @@ plugins { id("org.springframework.boot") version "2.4.1" - id("org.jetbrains.kotlin.jvm") version "1.3.31" + id("org.jetbrains.kotlin.jvm") version "1.4.21-2" id("org.jetbrains.kotlin.plugin.spring") version "1.3.31" } From c64a8cd04a0b33fa949e19d93f31766cab021245 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jan 2021 16:52:02 +0000 Subject: [PATCH 1789/5615] Bump s3 from 2.15.14 to 2.15.56 in /modules/localstack (#3650) Bumps s3 from 2.15.14 to 2.15.56. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 908f87de07d..dd66fb69547 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,5 +7,5 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.880' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.884' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' - testCompile 'software.amazon.awssdk:s3:2.15.14' + testCompile 'software.amazon.awssdk:s3:2.15.56' } From 52b467aa792f1c4a68f3ea79446261e4a726427b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jan 2021 16:52:12 +0000 Subject: [PATCH 1790/5615] Bump aws-java-sdk-s3 from 1.11.882 to 1.11.929 in /modules/localstack (#3654) Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.11.882 to 1.11.929. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.882...1.11.929) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index dd66fb69547..e67f6fe0632 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.882' - testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.880' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.929' + testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.929' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.884' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' testCompile 'software.amazon.awssdk:s3:2.15.56' From c5d462abad68933d3f3c9e651edfea25aef62a59 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jan 2021 16:52:45 +0000 Subject: [PATCH 1791/5615] Bump transport from 7.10.0 to 7.10.1 in /modules/elasticsearch (#3590) Bumps [transport](https://github.com/elastic/elasticsearch) from 7.10.0 to 7.10.1. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.10.0...v7.10.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index a3442f3234b..b4a64a405b7 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,5 +3,5 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.10.1" - testCompile "org.elasticsearch.client:transport:7.10.0" + testCompile "org.elasticsearch.client:transport:7.10.1" } From 4daf4dba93cc428f07395400143c6a72605497c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jan 2021 16:56:56 +0000 Subject: [PATCH 1792/5615] Bump testcontainers from 1.14.3 to 1.15.1 in /core (#3587) Bumps [testcontainers](https://github.com/testcontainers/testcontainers-java) from 1.14.3 to 1.15.1. - [Release notes](https://github.com/testcontainers/testcontainers-java/releases) - [Changelog](https://github.com/testcontainers/testcontainers-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-java/compare/1.14.3...1.15.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 4b42173cd0f..ac174e8bc93 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -158,7 +158,7 @@ if (!org.gradle.internal.os.OperatingSystem.current().isWindows()) { } dependencies { - baseline 'org.testcontainers:testcontainers:1.14.3', { + baseline 'org.testcontainers:testcontainers:1.15.1', { exclude group: "*", module: "*" } From acbb0c540dd33e903dabeffbaa9327f1873b046f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jan 2021 16:57:04 +0000 Subject: [PATCH 1793/5615] Bump cucumber-junit from 6.8.1 to 6.9.1 in /examples (#3625) Bumps [cucumber-junit](https://github.com/cucumber/cucumber-jvm) from 6.8.1 to 6.9.1. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v6.8.1...v6.9.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index d3be66ddcc6..ddffea03dfe 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' testImplementation 'io.cucumber:cucumber-java:6.9.1' - testImplementation 'io.cucumber:cucumber-junit:6.8.1' + testImplementation 'io.cucumber:cucumber-junit:6.9.1' testImplementation 'org.testcontainers:selenium' } From 2a9c4ebaf32b4f385b4ebf72ebd06f21a64e41d8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jan 2021 17:03:05 +0000 Subject: [PATCH 1794/5615] Bump gradle-update/update-gradle-wrapper-action from 74a035c to 1.0.9 (#3653) Bumps [gradle-update/update-gradle-wrapper-action](https://github.com/gradle-update/update-gradle-wrapper-action) from 74a035c to 1.0.9. This release includes the previously tagged commit. - [Release notes](https://github.com/gradle-update/update-gradle-wrapper-action/releases) - [Changelog](https://github.com/gradle-update/update-gradle-wrapper-action/blob/master/CHANGELOG.md) - [Commits](https://github.com/gradle-update/update-gradle-wrapper-action/compare/74a035c...74a035ce333f39c84811ce1167669bfa6c65f440) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/update-gradle-wrapper.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-gradle-wrapper.yml b/.github/workflows/update-gradle-wrapper.yml index 883d6dd6cbc..e8f4f130b12 100644 --- a/.github/workflows/update-gradle-wrapper.yml +++ b/.github/workflows/update-gradle-wrapper.yml @@ -12,7 +12,7 @@ jobs: - uses: actions/checkout@v2 - name: Update Gradle Wrapper - uses: gradle-update/update-gradle-wrapper-action@74a035c + uses: gradle-update/update-gradle-wrapper-action@74a035ce333f39c84811ce1167669bfa6c65f440 with: repo-token: ${{ secrets.GITHUB_TOKEN }} labels: dependencies From d9ecb9813bca5b75cdc33dc4715a0c918eaf7c5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mads=20M=C3=A6tzke=20Tandrup?= Date: Fri, 1 Jan 2021 19:54:46 +0100 Subject: [PATCH 1795/5615] Add init command parameter to Vault container (#3188) Co-authored-by: Richard North --- .../testcontainers/vault/VaultContainer.java | 37 +++++++++++++++++++ .../vault/VaultContainerTest.java | 13 ++++++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java b/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java index f47818d8c7d..37e78a14f95 100644 --- a/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java +++ b/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java @@ -7,10 +7,12 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Consumer; +import java.util.stream.Collectors; import static com.github.dockerjava.api.model.Capability.IPC_LOCK; @@ -30,6 +32,7 @@ public class VaultContainer> extends GenericCo private static final int VAULT_PORT = 8200; private Map> secretsMap = new HashMap<>(); + private List initCommands = new ArrayList<>(); private int port = VAULT_PORT; @@ -61,6 +64,7 @@ public VaultContainer(final DockerImageName dockerImageName) { @Override protected void containerIsStarted(InspectContainerResponse containerInfo) { addSecrets(); + runInitCommands(); } private void addSecrets() { @@ -82,6 +86,23 @@ private String[] buildExecCommand(Map> map) { return new String[]{"/bin/sh", "-c", stringBuilder.toString().substring(4)}; } + private void runInitCommands() { + if (!initCommands.isEmpty()) { + String commands = initCommands.stream() + .map(command -> "vault " + command) + .collect(Collectors.joining(" && ")); + try { + ExecResult execResult = this.execInContainer(new String[]{"/bin/sh", "-c", commands}); + if (execResult.getExitCode() != 0) { + logger().error("Failed to execute these init commands {}. Exit code {}. Stdout {}. Stderr {}", + initCommands, execResult.getExitCode(), execResult.getStdout(), execResult.getStderr()); + } + } catch (IOException | InterruptedException e) { + logger().error("Failed to execute these init commands {}. Exception message: {}", initCommands, e.getMessage()); + } + } + } + /** * Sets the Vault root token for the container so application tests can source secrets using the token * @@ -142,4 +163,20 @@ public SELF withSecretInVault(String path, String firstSecret, String... remaini secretsMap.putIfAbsent(path, list); return self(); } + + /** + * Run initialization commands using the vault cli. + * + * Useful for enableing more secret engines like: + *

+     *     .withInitCommand("secrets enable pki")
+     *     .withInitCommand("secrets enable transit")
+     * 
+ * @param commands The commands to send to the vault cli + * @return this + */ + public SELF withInitCommand(String... commands) { + initCommands.addAll(Arrays.asList(commands)); + return self(); + } } diff --git a/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java b/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java index b3dd788f053..9dee4c21416 100644 --- a/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java +++ b/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java @@ -27,7 +27,8 @@ public class VaultContainerTest { "secret_two=password2", "secret_three=password3", "secret_three=password3", - "secret_four=password4"); + "secret_four=password4") + .withInitCommand("secrets enable transit", "write -f transit/keys/my-key"); @Test public void readFirstSecretPathWithCli() throws IOException, InterruptedException { @@ -70,6 +71,16 @@ public void readSecondSecretPathOverHttpApi() throws InterruptedException { assertThat().body("data.data.secret_four", containsString("password4")); } + @Test + public void readTransitKeyOverHttpApi() throws InterruptedException { + given(). + header("X-Vault-Token", VAULT_TOKEN). + when(). + get("http://" + getHostAndPort() + "/v1/transit/keys/my-key"). + then(). + assertThat().body("data.name", equalTo("my-key")); + } + private String getHostAndPort() { return vaultContainer.getHost() + ":" + vaultContainer.getMappedPort(8200); } From e6089e86d605eb3120a3a282c11ae700e577c6fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jan 2021 18:55:23 +0000 Subject: [PATCH 1796/5615] Bump org.jetbrains.kotlin.plugin.spring from 1.3.31 to 1.4.21-2 in /examples (#3646) Bumps org.jetbrains.kotlin.plugin.spring from 1.3.31 to 1.4.21-2. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index cc1083ee8d4..4675f907235 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,7 +1,7 @@ plugins { id("org.springframework.boot") version "2.4.1" id("org.jetbrains.kotlin.jvm") version "1.4.21-2" - id("org.jetbrains.kotlin.plugin.spring") version "1.3.31" + id("org.jetbrains.kotlin.plugin.spring") version "1.4.21-2" } apply plugin: 'io.spring.dependency-management' From 40f96736fad1cccf34290294a1ced3cf941ba51a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jan 2021 18:55:36 +0000 Subject: [PATCH 1797/5615] Bump aws-java-sdk-s3 from 1.11.929 to 1.11.930 in /modules/localstack (#3659) Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.11.929 to 1.11.930. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.929...1.11.930) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index e67f6fe0632..a0b30cc5844 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.929' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.930' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.929' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.884' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' From b6628c5a8fa837301c01b66454eec93b2d8b46ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jan 2021 18:55:46 +0000 Subject: [PATCH 1798/5615] Bump guava from 30.0-jre to 30.1-jre in /modules/jdbc-test (#3622) Bumps [guava](https://github.com/google/guava) from 30.0-jre to 30.1-jre. - [Release notes](https://github.com/google/guava/releases) - [Commits](https://github.com/google/guava/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 8523ca5a3d8..b6581d526a5 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -2,7 +2,7 @@ dependencies { compile project(':jdbc') compile project(':test-support') - compile 'com.google.guava:guava:30.0-jre' + compile 'com.google.guava:guava:30.1-jre' compile 'org.apache.commons:commons-lang3:3.11' compile 'com.zaxxer:HikariCP-java6:2.3.13' compile 'commons-dbutils:commons-dbutils:1.7' From 0e8523db9df728a31c2b951eeee08abf7196ec6f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 3 Jan 2021 16:11:50 +0000 Subject: [PATCH 1799/5615] Bump aws-java-sdk-sqs from 1.11.884 to 1.11.930 in /modules/localstack (#3660) Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.11.884 to 1.11.930. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.884...1.11.930) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index a0b30cc5844..fdffc649a72 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.930' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.929' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.884' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.930' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' testCompile 'software.amazon.awssdk:s3:2.15.56' } From 39116c09169bcde50a9e4d62bf90c15e3df4dc02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9da=20Housni=20Alaoui?= Date: Sun, 10 Jan 2021 13:23:23 +0100 Subject: [PATCH 1800/5615] Fix handling of locally built images when used with `hub.image.name.prefix` (#3666) --- .../org/testcontainers/images/builder/ImageFromDockerfile.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java index 133b6e1c4b0..b36779f1b9e 100644 --- a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java +++ b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java @@ -54,7 +54,7 @@ public class ImageFromDockerfile extends LazyFuture implements private Set dependencyImageNames = Collections.emptySet(); public ImageFromDockerfile() { - this("testcontainers/" + Base58.randomString(16).toLowerCase()); + this("localhost/testcontainers/" + Base58.randomString(16).toLowerCase()); } public ImageFromDockerfile(String dockerImageName) { From d96dd815d2b8166b75a919c751681da3325e2014 Mon Sep 17 00:00:00 2001 From: "Junho, Kim" Date: Sun, 10 Jan 2021 21:31:39 +0900 Subject: [PATCH 1801/5615] Remove duplicated dependency in jdbc-test module (#3664) Co-authored-by: Junho, Kim --- modules/jdbc-test/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index b6581d526a5..05a96aaf333 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -14,7 +14,6 @@ dependencies { exclude(group: "net.java.dev.jna", module: "jna") } - compile 'commons-dbutils:commons-dbutils:1.7' compile 'org.apache.tomcat:tomcat-jdbc:9.0.40' compile 'org.vibur:vibur-dbcp:25.0' compile 'mysql:mysql-connector-java:8.0.22' From 97493b112f76711adc8f431f6f8e6f3245f05c47 Mon Sep 17 00:00:00 2001 From: Diego Lovison Date: Tue, 12 Jan 2021 13:58:27 -0300 Subject: [PATCH 1802/5615] Support Ryuk socket timeout (#3682) Fixes #3183 Co-authored-by: Sergei Egorov --- .../main/java/org/testcontainers/utility/ResourceReaper.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index 71a4b49d0d3..7adbc95bb4c 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -28,6 +28,7 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.io.UnsupportedEncodingException; +import java.net.InetSocketAddress; import java.net.Socket; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; @@ -157,7 +158,9 @@ public InspectContainerResponse getContainerInfo() { while (true) { RYUK_ACK_RATE_LIMITER.doWhenReady(() -> { int index = 0; - try(Socket clientSocket = new Socket(host, ryukPort)) { + // not set the read timeout, as Ryuk would not send anything unless a new filter is submitted, meaning that we would get a timeout exception pretty quick + try (Socket clientSocket = new Socket()) { + clientSocket.connect(new InetSocketAddress(host, ryukPort), 5 * 1000); FilterRegistry registry = new FilterRegistry(clientSocket.getInputStream(), clientSocket.getOutputStream()); synchronized (DEATH_NOTE) { From 864df2d50b0d5baf0ace24fba972444569d0e21e Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sat, 6 Feb 2021 15:24:18 +0100 Subject: [PATCH 1803/5615] Force Jackson version to 2.8.8 (forward compatibility) (#3602) Co-authored-by: Richard North --- core/build.gradle | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index ac174e8bc93..7613ed58ccc 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -157,6 +157,13 @@ if (!org.gradle.internal.os.OperatingSystem.current().isWindows()) { project.tasks.check.dependsOn(japicmp) } +configurations.all { + resolutionStrategy { + // use lower Jackson version + force 'com.fasterxml.jackson.core:jackson-databind:2.8.8' + } +} + dependencies { baseline 'org.testcontainers:testcontainers:1.15.1', { exclude group: "*", module: "*" @@ -175,10 +182,6 @@ dependencies { compile "com.github.docker-java:docker-java-api:3.2.7" - // use lower Jackson version - shaded ('com.fasterxml.jackson.core:jackson-databind:2.8.8') { - exclude(group: 'com.fasterxml.jackson.core', module: 'jackson-annotations') - } shaded ('com.github.docker-java:docker-java-core:3.2.7') { exclude(group: 'com.github.docker-java', module: 'docker-java-api') exclude(group: 'com.github.docker-java', module: 'docker-java-transport') From 7688d1bd808d4ce965cfd86f2053dd61a479f2ed Mon Sep 17 00:00:00 2001 From: Alexander Girke Date: Sat, 6 Feb 2021 15:49:21 +0100 Subject: [PATCH 1804/5615] Clarify usage of host port exporting (#3421) --- docs/features/networking.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/networking.md b/docs/features/networking.md index 2fe039b74ef..e7d8493483e 100644 --- a/docs/features/networking.md +++ b/docs/features/networking.md @@ -69,7 +69,7 @@ We need to tell Testcontainers to prepare to expose this port to containers: !!! warning - Note that the above command should be invoked _before_ containers are started. + Note that the above command should be invoked _before_ containers are started, but _after_ the server on the host was started. Having done so, we can now access this port from any containers that are launched. From a container's perspective, the hostname will be `host.testcontainers.internal` and the port will be the same value as `localServerPort`. From ed8386c4cc2e38cc766eea30de68f3d559639a8a Mon Sep 17 00:00:00 2001 From: Michael Nitschinger Date: Sat, 6 Feb 2021 16:23:10 +0100 Subject: [PATCH 1805/5615] couchbase: wait until all services are part of the config (#3003) This changeset adds a predicate to the wait strategy to make sure that it not only returns a 200, but also that every enabled service is actually already exposed in the config. Since we are polling the server during bootstrap here, not all of them might show up at the same time. Also, while not contributing to the fix we poll the terse bucket http config "b" instead of the verbose one "buckets" since it is a little more efficient on the server side and actually the config the client internally works with. fixes #2993 Co-authored-by: Sergei Egorov --- .../couchbase/CouchbaseContainer.java | 54 +++++++++++++++---- .../couchbase/CouchbaseService.java | 17 ++++-- 2 files changed, 56 insertions(+), 15 deletions(-) diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index 11960304ee6..3671608d324 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -36,10 +36,12 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.EnumSet; +import java.util.Iterator; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; import java.util.stream.Collectors; /** @@ -181,7 +183,7 @@ protected void configure() { .map("healthy"::equals) .orElse(false); } catch (IOException e) { - logger().error("Unable to parse response {}", response, e); + logger().error("Unable to parse response: {}", response, e); return false; } }) @@ -254,15 +256,10 @@ private void renameNode() { private void initializeServices() { logger().debug("Initializing couchbase services on host: {}", enabledServices); - final String services = enabledServices.stream().map(s -> { - switch (s) { - case KV: return "kv"; - case QUERY: return "n1ql"; - case INDEX: return "index"; - case SEARCH: return "fts"; - default: throw new IllegalStateException("Unknown service!"); - } - }).collect(Collectors.joining(",")); + final String services = enabledServices + .stream() + .map(CouchbaseService::getIdentifier) + .collect(Collectors.joining(",")); @Cleanup Response response = doHttpRequest(MGMT_PORT, "/node/controller/setupServices", "POST", new FormBody.Builder() .add("services", services) @@ -363,10 +360,11 @@ private void createBuckets() { checkSuccessfulResponse(response, "Could not create bucket " + bucket.getName()); new HttpWaitStrategy() - .forPath("/pools/default/buckets/" + bucket.getName()) + .forPath("/pools/default/b/" + bucket.getName()) .forPort(MGMT_PORT) .withBasicCredentials(username, password) .forStatusCode(200) + .forResponsePredicate(new AllServicesEnabledPredicate()) .waitUntilReady(this); if (enabledServices.contains(CouchbaseService.QUERY)) { @@ -472,4 +470,38 @@ private Response doHttpRequest(final int port, final String path, final String m throw new RuntimeException("Could not perform request against couchbase HTTP endpoint ", ex); } } + + /** + * In addition to getting a 200, we need to make sure that all services we need are enabled and available on + * the bucket. + *

+ * Fixes the issue observed in https://github.com/testcontainers/testcontainers-java/issues/2993 + */ + private class AllServicesEnabledPredicate implements Predicate { + + @Override + public boolean test(final String rawConfig) { + try { + for (JsonNode node : MAPPER.readTree(rawConfig).at("/nodesExt")) { + for (CouchbaseService enabledService : enabledServices) { + boolean found = false; + Iterator fieldNames = node.get("services").fieldNames(); + while (fieldNames.hasNext()) { + if (fieldNames.next().startsWith(enabledService.getIdentifier())) { + found = true; + } + } + if (!found) { + logger().trace("Service {} not yet part of config, retrying.", enabledService); + return false; + } + } + } + return true; + } catch (IOException ex) { + logger().error("Unable to parse response: {}", rawConfig, ex); + return false; + } + } + } } diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseService.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseService.java index d284c660709..c60fc6b71d0 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseService.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseService.java @@ -24,21 +24,30 @@ public enum CouchbaseService { /** * Key-Value service. */ - KV, + KV("kv"), /** * Query (N1QL) service. */ - QUERY, + QUERY("n1ql"), /** * Search (FTS) service. */ - SEARCH, + SEARCH("fts"), /** * Indexing service (needed if QUERY is also used!). */ - INDEX + INDEX("index"); + private final String identifier; + + CouchbaseService(String identifier) { + this.identifier = identifier; + } + + String getIdentifier() { + return identifier; + } } From bf0e4836e0136bad3844abd5db24260e2451c8e3 Mon Sep 17 00:00:00 2001 From: Artjom Kalita Date: Sat, 6 Feb 2021 17:26:55 +0200 Subject: [PATCH 1806/5615] Improve logging for port listener (#3736) * Improve logging for port listener, additional clean up for string concatenation in loop Co-authored-by: akalita Co-authored-by: Sergei Egorov --- .../InternalCommandPortListeningCheck.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java index 9b4be81b4c4..cd601e6a131 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java +++ b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java @@ -24,23 +24,23 @@ public class InternalCommandPortListeningCheck implements java.util.concurrent.C @Override public Boolean call() { - String command = "true"; + StringBuilder command = new StringBuilder("true"); for (int internalPort : internalPorts) { - command += " && "; - command += " ("; - command += format("cat /proc/net/tcp* | awk '{print $2}' | grep -i ':0*%x'", internalPort); - command += " || "; - command += format("nc -vz -w 1 localhost %d", internalPort); - command += " || "; - command += format("/bin/bash -c ' Date: Sat, 6 Feb 2021 18:27:51 +0300 Subject: [PATCH 1807/5615] Added documentation for Bigtable Emulator container (#3708) --- docs/modules/gcloud.md | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/docs/modules/gcloud.md b/docs/modules/gcloud.md index 041129919ee..33d50ee634f 100644 --- a/docs/modules/gcloud.md +++ b/docs/modules/gcloud.md @@ -5,10 +5,11 @@ Testcontainers module for the Google Cloud Platform's [Cloud SDK](https://cloud.google.com/sdk/). -Currently, the module supports `Datastore`, `Firestore`, `Spanner`, and `Pub/Sub` emulators. In order to use it, you should use the following classes: +Currently, the module supports `Bigtable`, `Datastore`, `Firestore`, `Spanner`, and `Pub/Sub` emulators. In order to use it, you should use the following classes: Class | Container Image -|- +BigtableEmulatorContainer | [gcr.io/google.com/cloudsdktool/cloud-sdk:emulators](https://gcr.io/google.com/cloudsdktool/cloud-sdk) DatastoreEmulatorContainer | [gcr.io/google.com/cloudsdktool/cloud-sdk:emulators](https://gcr.io/google.com/cloudsdktool/cloud-sdk) FirestoreEmulatorContainer | [gcr.io/google.com/cloudsdktool/cloud-sdk:emulators](https://gcr.io/google.com/cloudsdktool/cloud-sdk) SpannerEmulatorContainer | [gcr.io/cloud-spanner-emulator/emulator](https://gcr.io/cloud-spanner-emulator/emulator) @@ -16,6 +17,26 @@ PubSubEmulatorContainer | [gcr.io/google.com/cloudsdktool/cloud-sdk:emulators](h ## Usage example +### Bigtable + +Start Bigtable Emulator during a test: + + +[Starting a Bigtable Emulator container](../../modules/gcloud/src/test/java/org/testcontainers/containers/BigtableEmulatorContainerTest.java) inside_block:emulatorContainer + + +Create a test Bigtable table in the Emulator: + + +[Create a test table](../../modules/gcloud/src/test/java/org/testcontainers/containers/BigtableEmulatorContainerTest.java) inside_block:createTable + + +Test against the Emulator: + + +[Testing with a Bigtable Emulator container](../../modules/gcloud/src/test/java/org/testcontainers/containers/BigtableEmulatorContainerTest.java) inside_block:testWithEmulatorContainer + + ### Datastore Start Datastore Emulator during a test: @@ -93,7 +114,7 @@ Start Pub/Sub Emulator during a test: [Starting a Pub/Sub Emulator container](../../modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java) inside_block:emulatorContainer -Create a test Pub/Sub topic the Emulator: +Create a test Pub/Sub topic in the Emulator: [Create a test topic](../../modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java) inside_block:createTopic From fd7412fe15070a2ccf70f99c8d7edf829501f8e2 Mon Sep 17 00:00:00 2001 From: Per Lundberg Date: Sat, 6 Feb 2021 17:31:13 +0200 Subject: [PATCH 1808/5615] GenericContainer: fix typo in Javadoc (#3684) --- .../java/org/testcontainers/containers/GenericContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 0a0962cd6c0..4536a70789c 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -1339,7 +1339,7 @@ public void copyFileFromContainer(String containerPath, String destinationPath) } /** - * Allow container startup to be attempted more than once if an error occurs. To be if containers are + * Allow container startup to be attempted more than once if an error occurs. To be used if containers are * 'flaky' but this flakiness is not something that should affect test outcomes. * * @param attempts number of attempts From 73da361f47d5c144665f7baf0b45919ec63a61c7 Mon Sep 17 00:00:00 2001 From: Alar Aule Date: Sat, 6 Feb 2021 17:34:18 +0200 Subject: [PATCH 1809/5615] PostGIS JDBC url sample version update (#3606) Latest Testcontainer versions are using new postgis/postgis images, which versioning is different (containing both PostgreSQL version - PostGIS version) Sample is from older mdillion/postgis versions, with copy-paste sample fails. --- docs/modules/databases/jdbc.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/databases/jdbc.md b/docs/modules/databases/jdbc.md index 65db9aaf07f..6853cf44c5c 100644 --- a/docs/modules/databases/jdbc.md +++ b/docs/modules/databases/jdbc.md @@ -37,7 +37,7 @@ Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database #### Using PostGIS -`jdbc:tc:postgis:9.6:///databasename` +`jdbc:tc:postgis:9.6-2.5:///databasename` #### Using Presto From 625ddc374cac2447cbbcb59c9d7e07c49ac30e99 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sat, 6 Feb 2021 16:41:20 +0100 Subject: [PATCH 1810/5615] Kafka cluster example (#1984, #3758) Co-authored-by: Sean Glover --- examples/kafka-cluster/build.gradle | 17 +++ .../kafkacluster/KafkaContainerCluster.java | 102 ++++++++++++++++++ .../KafkaContainerClusterTest.java | 97 +++++++++++++++++ examples/settings.gradle | 1 + .../containers/KafkaContainer.java | 8 +- .../containers/KafkaContainerTest.java | 16 +++ 6 files changed, 239 insertions(+), 2 deletions(-) create mode 100644 examples/kafka-cluster/build.gradle create mode 100644 examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerCluster.java create mode 100644 examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerClusterTest.java diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle new file mode 100644 index 00000000000..db07c6859f3 --- /dev/null +++ b/examples/kafka-cluster/build.gradle @@ -0,0 +1,17 @@ +plugins { + id 'java' +} + +repositories { + jcenter() +} + +dependencies { + testCompileOnly "org.projectlombok:lombok:1.18.10" + testAnnotationProcessor "org.projectlombok:lombok:1.18.10" + testCompile 'org.testcontainers:kafka' + testCompile 'org.apache.kafka:kafka-clients:2.3.1' + testCompile 'org.assertj:assertj-core:3.14.0' + testCompile 'com.google.guava:guava:23.0' + testCompile 'org.slf4j:slf4j-simple:1.7.30' +} diff --git a/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerCluster.java b/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerCluster.java new file mode 100644 index 00000000000..fd5b1a0a0b6 --- /dev/null +++ b/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerCluster.java @@ -0,0 +1,102 @@ +package com.example.kafkacluster; + +import lombok.SneakyThrows; +import org.rnorth.ducttape.unreliables.Unreliables; +import org.testcontainers.containers.Container; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.Network; +import org.testcontainers.lifecycle.Startable; +import org.testcontainers.lifecycle.Startables; +import org.testcontainers.containers.KafkaContainer; +import org.testcontainers.utility.DockerImageName; + +import java.util.Collection; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import static java.util.concurrent.TimeUnit.SECONDS; + +/** + * Provides an easy way to launch a Kafka cluster with multiple brokers. + */ +public class KafkaContainerCluster implements Startable { + + private final int brokersNum; + private final Network network; + private final GenericContainer zookeeper; + private final Collection brokers; + + public KafkaContainerCluster(String confluentPlatformVersion, int brokersNum, int internalTopicsRf) { + if (brokersNum < 0) { + throw new IllegalArgumentException("brokersNum '" + brokersNum + "' must be greater than 0"); + } + if (internalTopicsRf < 0 || internalTopicsRf > brokersNum) { + throw new IllegalArgumentException("internalTopicsRf '" + internalTopicsRf + "' must be less than brokersNum and greater than 0"); + } + + this.brokersNum = brokersNum; + this.network = Network.newNetwork(); + + this.zookeeper = new GenericContainer<>(DockerImageName.parse("confluentinc/cp-zookeeper").withTag(confluentPlatformVersion)) + .withNetwork(network) + .withNetworkAliases("zookeeper") + .withEnv("ZOOKEEPER_CLIENT_PORT", String.valueOf(KafkaContainer.ZOOKEEPER_PORT)); + + this.brokers = IntStream + .range(0, this.brokersNum) + .mapToObj(brokerNum -> { + return new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka").withTag(confluentPlatformVersion)) + .withNetwork(this.network) + .withNetworkAliases("broker-" + brokerNum) + .dependsOn(this.zookeeper) + .withExternalZookeeper("zookeeper:" + KafkaContainer.ZOOKEEPER_PORT) + .withEnv("KAFKA_BROKER_ID", brokerNum + "") + .withEnv("KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR", internalTopicsRf + "") + .withEnv("KAFKA_OFFSETS_TOPIC_NUM_PARTITIONS", internalTopicsRf + "") + .withEnv("KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR", internalTopicsRf + "") + .withEnv("KAFKA_TRANSACTION_STATE_LOG_MIN_ISR", internalTopicsRf + ""); + }) + .collect(Collectors.toList()); + } + + public Collection getBrokers() { + return this.brokers; + } + + public String getBootstrapServers() { + return brokers.stream() + .map(KafkaContainer::getBootstrapServers) + .collect(Collectors.joining(",")); + } + + private Stream> allContainers() { + return Stream.concat( + this.brokers.stream(), + Stream.of(this.zookeeper) + ); + } + + @Override + @SneakyThrows + public void start() { + Stream startables = this.brokers.stream().map(Startable.class::cast); + Startables.deepStart(startables).get(60, SECONDS); + + Unreliables.retryUntilTrue(30, TimeUnit.SECONDS, () -> { + Container.ExecResult result = this.zookeeper.execInContainer( + "sh", "-c", + "zookeeper-shell zookeeper:" + KafkaContainer.ZOOKEEPER_PORT + " ls /brokers/ids | tail -n 1" + ); + String brokers = result.getStdout(); + + return brokers != null && brokers.split(",").length == this.brokersNum; + }); + } + + @Override + public void stop() { + allContainers().parallel().forEach(GenericContainer::stop); + } +} diff --git a/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerClusterTest.java b/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerClusterTest.java new file mode 100644 index 00000000000..0756b24d58a --- /dev/null +++ b/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerClusterTest.java @@ -0,0 +1,97 @@ +package com.example.kafkacluster; + +import com.google.common.collect.ImmutableMap; +import org.apache.kafka.clients.admin.AdminClient; +import org.apache.kafka.clients.admin.AdminClientConfig; +import org.apache.kafka.clients.admin.NewTopic; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.apache.kafka.common.serialization.StringSerializer; +import org.junit.Test; +import org.rnorth.ducttape.unreliables.Unreliables; + +import java.time.Duration; +import java.util.Collection; +import java.util.Collections; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + +public class KafkaContainerClusterTest { + + @Test + public void testKafkaContainerCluster() throws Exception { + try ( + KafkaContainerCluster cluster = new KafkaContainerCluster("5.2.1", 3, 2) + ) { + cluster.start(); + String bootstrapServers = cluster.getBootstrapServers(); + + assertThat(cluster.getBrokers()).hasSize(3); + + testKafkaFunctionality(bootstrapServers, 3, 2); + } + } + + protected void testKafkaFunctionality(String bootstrapServers, int partitions, int rf) throws Exception { + try ( + AdminClient adminClient = AdminClient.create(ImmutableMap.of( + AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers + )); + + KafkaProducer producer = new KafkaProducer<>( + ImmutableMap.of( + ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers, + ProducerConfig.CLIENT_ID_CONFIG, UUID.randomUUID().toString() + ), + new StringSerializer(), + new StringSerializer() + ); + + KafkaConsumer consumer = new KafkaConsumer<>( + ImmutableMap.of( + ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers, + ConsumerConfig.GROUP_ID_CONFIG, "tc-" + UUID.randomUUID(), + ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest" + ), + new StringDeserializer(), + new StringDeserializer() + ); + ) { + String topicName = "messages"; + + Collection topics = Collections.singletonList(new NewTopic(topicName, partitions, (short) rf)); + adminClient.createTopics(topics).all().get(30, TimeUnit.SECONDS); + + consumer.subscribe(Collections.singletonList(topicName)); + + producer.send(new ProducerRecord<>(topicName, "testcontainers", "rulezzz")).get(); + + Unreliables.retryUntilTrue(10, TimeUnit.SECONDS, () -> { + ConsumerRecords records = consumer.poll(Duration.ofMillis(100)); + + if (records.isEmpty()) { + return false; + } + + assertThat(records) + .hasSize(1) + .extracting(ConsumerRecord::topic, ConsumerRecord::key, ConsumerRecord::value) + .containsExactly(tuple(topicName, "testcontainers", "rulezzz")); + + return true; + }); + + consumer.unsubscribe(); + } + } + +} diff --git a/examples/settings.gradle b/examples/settings.gradle index 25da14a0931..ea988b4ada8 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -19,6 +19,7 @@ includeBuild '..' // explicit include to allow Dependabot to autodiscover subprojects include 'disque-job-queue' +include 'kafka-cluster' include 'linked-container' include 'mongodb-container' include 'redis-backed-cache' diff --git a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java index 12867239251..b1bc3d735f9 100644 --- a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java +++ b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java @@ -24,6 +24,8 @@ public class KafkaContainer extends GenericContainer { public static final int ZOOKEEPER_PORT = 2181; + private static final String DEFAULT_INTERNAL_TOPIC_RF = "1"; + private static final int PORT_NOT_ASSIGNED = -1; protected String externalZookeeperConnect = null; @@ -60,8 +62,10 @@ public KafkaContainer(final DockerImageName dockerImageName) { withEnv("KAFKA_INTER_BROKER_LISTENER_NAME", "BROKER"); withEnv("KAFKA_BROKER_ID", "1"); - withEnv("KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR", "1"); - withEnv("KAFKA_OFFSETS_TOPIC_NUM_PARTITIONS", "1"); + withEnv("KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR", DEFAULT_INTERNAL_TOPIC_RF); + withEnv("KAFKA_OFFSETS_TOPIC_NUM_PARTITIONS", DEFAULT_INTERNAL_TOPIC_RF); + withEnv("KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR", DEFAULT_INTERNAL_TOPIC_RF); + withEnv("KAFKA_TRANSACTION_STATE_LOG_MIN_ISR", DEFAULT_INTERNAL_TOPIC_RF); withEnv("KAFKA_LOG_FLUSH_INTERVAL_MESSAGES", Long.MAX_VALUE + ""); withEnv("KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS", "0"); } diff --git a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java index 263653822e6..d219aa17b13 100644 --- a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java +++ b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java @@ -6,8 +6,12 @@ import com.google.common.collect.ImmutableMap; import java.time.Duration; +import java.util.Collection; import java.util.UUID; import java.util.concurrent.TimeUnit; +import org.apache.kafka.clients.admin.AdminClient; +import org.apache.kafka.clients.admin.AdminClientConfig; +import org.apache.kafka.clients.admin.NewTopic; import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; @@ -110,7 +114,15 @@ public void testConfluentPlatformVersion6() throws Exception { } protected void testKafkaFunctionality(String bootstrapServers) throws Exception { + testKafkaFunctionality(bootstrapServers, 1, 1); + } + + protected void testKafkaFunctionality(String bootstrapServers, int partitions, int rf) throws Exception { try ( + AdminClient adminClient = AdminClient.create(ImmutableMap.of( + AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers + )); + KafkaProducer producer = new KafkaProducer<>( ImmutableMap.of( ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers, @@ -131,6 +143,10 @@ protected void testKafkaFunctionality(String bootstrapServers) throws Exception ); ) { String topicName = "messages-" + UUID.randomUUID(); + + Collection topics = singletonList(new NewTopic(topicName, partitions, (short) rf)); + adminClient.createTopics(topics).all().get(30, TimeUnit.SECONDS); + consumer.subscribe(singletonList(topicName)); producer.send(new ProducerRecord<>(topicName, "testcontainers", "rulezzz")).get(); From 140fd94b361786e777ae87a8a9643b954ed68e00 Mon Sep 17 00:00:00 2001 From: "Junho, Kim" Date: Sun, 7 Feb 2021 00:52:02 +0900 Subject: [PATCH 1811/5615] Change docker command (#3608) Closes #3571 This also includes changing default tag version (to v19.2.11) Co-authored-by: Junho, Kim Co-authored-by: Sergei Egorov --- .../org/testcontainers/containers/CockroachContainer.java | 4 ++-- .../test/java/org/testcontainers/CockroachDBTestImages.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java b/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java index 2a7990413e4..333a6ca0158 100644 --- a/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java +++ b/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java @@ -8,7 +8,7 @@ public class CockroachContainer extends JdbcDatabaseContainer { private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("cockroachdb/cockroach"); - private static final String DEFAULT_TAG = "v19.1.1"; + private static final String DEFAULT_TAG = "v19.2.11"; public static final String NAME = "cockroach"; @@ -53,7 +53,7 @@ public CockroachContainer(final DockerImageName dockerImageName) { .forStatusCode(200) .withStartupTimeout(Duration.ofMinutes(1)) ); - withCommand("start --insecure"); + withCommand("start-single-node --insecure"); } @Override diff --git a/modules/cockroachdb/src/test/java/org/testcontainers/CockroachDBTestImages.java b/modules/cockroachdb/src/test/java/org/testcontainers/CockroachDBTestImages.java index a309f84e098..ac02b2f5d81 100644 --- a/modules/cockroachdb/src/test/java/org/testcontainers/CockroachDBTestImages.java +++ b/modules/cockroachdb/src/test/java/org/testcontainers/CockroachDBTestImages.java @@ -3,5 +3,5 @@ import org.testcontainers.utility.DockerImageName; public interface CockroachDBTestImages { - DockerImageName COCKROACHDB_IMAGE = DockerImageName.parse("cockroachdb/cockroach:v19.1.1"); + DockerImageName COCKROACHDB_IMAGE = DockerImageName.parse("cockroachdb/cockroach:v19.2.11"); } From 8f13e420f4d8934ad4c39b4104df1dac571b88a7 Mon Sep 17 00:00:00 2001 From: Andrew Guibert Date: Sat, 6 Feb 2021 09:55:42 -0600 Subject: [PATCH 1812/5615] Show port mappings in HttpWaitStrategy (#2341) * Show port mappings in HttpWaitStrategy * Use stream to un-map exposed port * Catch and log failures when logging Co-authored-by: Richard North --- .../wait/strategy/HttpWaitStrategy.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java index a2bf4c4899e..85216bcedf6 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java @@ -162,8 +162,20 @@ protected void waitUntilReady() { if (null == livenessCheckPort || -1 == livenessCheckPort) { return; } - final String uri = buildLivenessUri(livenessCheckPort).toString(); - log.info("{}: Waiting for {} seconds for URL: {}", containerName, startupTimeout.getSeconds(), uri); + final URI rawUri = buildLivenessUri(livenessCheckPort); + final String uri = rawUri.toString(); + + try { + // Un-map the port for logging + int originalPort = waitStrategyTarget.getExposedPorts().stream() + .filter(exposedPort -> rawUri.getPort() == waitStrategyTarget.getMappedPort(exposedPort)) + .findFirst() + .orElseThrow(() -> new IllegalStateException("Target port " + rawUri.getPort() + " is not exposed")); + log.info("{}: Waiting for {} seconds for URL: {} (where port {} maps to container port {})", containerName, startupTimeout.getSeconds(), uri, rawUri.getPort(), originalPort); + } catch (RuntimeException e) { + // do not allow a failure in logging to prevent progress, but log for diagnosis + log.warn("Unexpected error occurred - will proceed to try to wait anyway", e); + } // try to connect to the URL try { @@ -243,7 +255,7 @@ private URI buildLivenessUri(int livenessCheckPort) { if ((tlsEnabled && 443 == livenessCheckPort) || (!tlsEnabled && 80 == livenessCheckPort)) { portSuffix = ""; } else { - portSuffix = ":" + String.valueOf(livenessCheckPort); + portSuffix = ":" + livenessCheckPort; } return URI.create(scheme + host + portSuffix + path); From 1f9b44e5b1c8785613191b925030d6be09b77826 Mon Sep 17 00:00:00 2001 From: Joseph Petersen Date: Sat, 6 Feb 2021 16:59:29 +0100 Subject: [PATCH 1813/5615] update release drafter to v5.13.0 (#3632) * update release drafter to v5.13.0 * use commit sha Co-authored-by: Richard North Co-authored-by: Richard North --- .github/workflows/release-drafter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 3123c3f6fac..8f88ac2231e 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -9,6 +9,6 @@ jobs: update_release_draft: runs-on: ubuntu-latest steps: - - uses: release-drafter/release-drafter@v5.9.0 + - uses: release-drafter/release-drafter@4d1215c #v5.13.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 51ee33bad751debcaf88a0e8b7d72818db3f6df1 Mon Sep 17 00:00:00 2001 From: Renato Mefi Date: Sat, 6 Feb 2021 17:28:35 +0100 Subject: [PATCH 1814/5615] Support HTTP headers on HttpWaitStrategy (#2549) This enables webservers that depend on headers for healthchecks to be able to correctly be queried by the HttpWaitStrategy. Co-authored-by: Sergei Egorov --- .../wait/strategy/HttpWaitStrategy.java | 26 ++++++++++++++++++ .../wait/strategy/HttpWaitStrategyTest.java | 27 +++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java index 85216bcedf6..c4751725707 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java @@ -2,6 +2,8 @@ import com.google.common.base.Strings; import com.google.common.io.BaseEncoding; +import java.util.HashMap; +import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.rnorth.ducttape.TimeoutException; import org.testcontainers.containers.ContainerLaunchException; @@ -40,6 +42,7 @@ public class HttpWaitStrategy extends AbstractWaitStrategy { private boolean tlsEnabled; private String username; private String password; + private final Map headers = new HashMap<>(); private Predicate responsePredicate; private Predicate statusCodePredicate = null; private Optional livenessPort = Optional.empty(); @@ -122,6 +125,27 @@ public HttpWaitStrategy withBasicCredentials(String username, String password) { return this; } + /** + * Add a custom HTTP Header to the call. + * @param name The HTTP Header name + * @param value The HTTP Header value + * @return this + */ + public HttpWaitStrategy withHeader(String name, String value) { + this.headers.put(name, value); + return this; + } + + /** + * Add multiple custom HTTP Headers to the call. + * @param headers Headers map of name/value + * @return this + */ + public HttpWaitStrategy withHeaders(Map headers) { + this.headers.putAll(headers); + return this; + } + /** * Set the HTTP connections read timeout. * @@ -191,6 +215,8 @@ protected void waitUntilReady() { connection.setUseCaches(false); } + // Add user configured headers + this.headers.forEach(connection::setRequestProperty); connection.setRequestMethod(method); connection.connect(); diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java index e048f4bccb8..c5a39a4538c 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java @@ -1,8 +1,13 @@ package org.testcontainers.junit.wait.strategy; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; + +import java.util.HashMap; import org.jetbrains.annotations.NotNull; import org.junit.Test; import org.rnorth.ducttape.RetryCountExceededException; +import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import java.time.Duration; @@ -30,6 +35,28 @@ public void testWaitUntilReadyWithSuccess() { waitUntilReadyAndSucceed(createShellCommand("200 OK", GOOD_RESPONSE_BODY)); } + /** + * Ensures that HTTP requests made with the HttpWaitStrategy can be enriched with user defined headers, + * although the test web server does not depend on the header to response with a 200, by checking the + * logs we can ensure the HTTP request was correctly sent. + */ + @Test + public void testWaitUntilReadyWithSuccessWithCustomHeaders() { + HashMap headers = new HashMap<>(); + headers.put("baz", "boo"); + GenericContainer container = startContainerWithCommand(createShellCommand("200 OK", GOOD_RESPONSE_BODY), + createHttpWaitStrategy(ready) + .withHeader("foo", "bar") + .withHeaders(headers) + ); + waitUntilReadyAndSucceed(container); + + String logs = container.getLogs(); + + assertThat(logs, containsString("foo: bar")); + assertThat(logs, containsString("baz: boo")); + } + /** * Expects that the WaitStrategy returns successfully after receiving an HTTP 401 response from the container. * This 401 response is checked with a lambda using {@link HttpWaitStrategy#forStatusCodeMatching(Predicate)} From a6f91e3b69a401159f88d9044cb557a12441d5f6 Mon Sep 17 00:00:00 2001 From: Oussama BADR Date: Wed, 10 Feb 2021 11:08:05 +0100 Subject: [PATCH 1815/5615] Make VNC recorded videos scrollable (#3180) This PR adds a second video recording mode, MP4, which is easier to consume and is scrollable. Fixes #512 Co-authored-by: Kevin Wittek Co-authored-by: Sergei Egorov --- .../containers/VncRecordingContainer.java | 65 ++++++++-- docs/modules/webdriver_containers.md | 9 +- .../containers/BrowserWebDriverContainer.java | 12 +- .../DefaultRecordingFileFactory.java | 13 +- .../containers/RecordingFileFactory.java | 6 + .../DefaultRecordingFileFactoryTest.java | 2 +- ...ChromeRecordingWebDriverContainerTest.java | 121 +++++++++++++++--- 7 files changed, 194 insertions(+), 34 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java b/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java index de720110a51..a171e6ba2f6 100644 --- a/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java +++ b/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java @@ -2,6 +2,7 @@ import lombok.Getter; import lombok.NonNull; +import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.ToString; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; @@ -9,6 +10,7 @@ import org.testcontainers.utility.DockerImageName; import java.io.File; +import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.StandardCopyOption; @@ -25,26 +27,30 @@ @ToString public class VncRecordingContainer extends GenericContainer { - private static final String RECORDING_FILE_NAME = "/screen.flv"; + private static final String ORIGINAL_RECORDING_FILE_NAME = "/screen.flv"; public static final String DEFAULT_VNC_PASSWORD = "secret"; public static final int DEFAULT_VNC_PORT = 5900; + static final VncRecordingFormat DEFAULT_RECORDING_FORMAT = VncRecordingFormat.FLV; + private final String targetNetworkAlias; private String vncPassword = DEFAULT_VNC_PASSWORD; + private VncRecordingFormat videoFormat = DEFAULT_RECORDING_FORMAT; + private int vncPort = 5900; private int frameRate = 30; public VncRecordingContainer(@NonNull GenericContainer targetContainer) { this( - targetContainer.getNetwork(), - targetContainer.getNetworkAliases().stream() - .findFirst() - .orElseThrow(() -> new IllegalStateException("Target container must have a network alias")) + targetContainer.getNetwork(), + targetContainer.getNetworkAliases().stream() + .findFirst() + .orElseThrow(() -> new IllegalStateException("Target container must have a network alias")) ); } @@ -52,7 +58,7 @@ public VncRecordingContainer(@NonNull GenericContainer targetContainer) { * Create a sidekick container and attach it to another container. The VNC output of that container will be recorded. */ public VncRecordingContainer(@NonNull Network network, @NonNull String targetNetworkAlias) throws IllegalStateException { - super(DockerImageName.parse("testcontainers/vnc-recorder:1.1.0")); + super(DockerImageName.parse("testcontainers/vnc-recorder:1.2.0")); this.targetNetworkAlias = targetNetworkAlias; withNetwork(network); @@ -71,6 +77,13 @@ public VncRecordingContainer withVncPort(int vncPort) { return this; } + public VncRecordingContainer withVideoFormat(VncRecordingFormat videoFormat) { + if (videoFormat != null) { + this.videoFormat = videoFormat; + } + return this; + } + public VncRecordingContainer withFrameRate(int frameRate) { this.frameRate = frameRate; return this; @@ -81,25 +94,53 @@ protected void configure() { withCreateContainerCmdModifier(it -> it.withEntrypoint("/bin/sh")); String encodedPassword = Base64.getEncoder().encodeToString(vncPassword.getBytes()); setCommand( - "-c", - "echo '" + encodedPassword + "' | base64 -d > /vnc_password && " + - "flvrec.py -o " + RECORDING_FILE_NAME + " -d -r " + frameRate + " -P /vnc_password " + targetNetworkAlias + " " + vncPort + "-c", + "echo '" + encodedPassword + "' | base64 -d > /vnc_password && " + + "flvrec.py -o " + ORIGINAL_RECORDING_FILE_NAME + " -d -r " + frameRate + " -P /vnc_password " + targetNetworkAlias + " " + vncPort ); } @SneakyThrows public InputStream streamRecording() { + String newRecordingFileName = videoFormat.reencodeRecording(this, ORIGINAL_RECORDING_FILE_NAME); + TarArchiveInputStream archiveInputStream = new TarArchiveInputStream( - dockerClient.copyArchiveFromContainerCmd(getContainerId(), RECORDING_FILE_NAME).exec() + dockerClient.copyArchiveFromContainerCmd(getContainerId(), newRecordingFileName).exec() ); archiveInputStream.getNextEntry(); return archiveInputStream; } @SneakyThrows - public void saveRecordingToFile(File file) { - try(InputStream inputStream = streamRecording()) { + public void saveRecordingToFile(@NonNull File file) { + try (InputStream inputStream = streamRecording()) { Files.copy(inputStream, file.toPath(), StandardCopyOption.REPLACE_EXISTING); } } + + @RequiredArgsConstructor + public enum VncRecordingFormat { + FLV("flv") { + @Override + String reencodeRecording(@NonNull VncRecordingContainer container, @NonNull String source) throws IOException, InterruptedException { + String newFileOutput = "/newScreen.flv"; + container.execInContainer("ffmpeg", "-i", source, "-vcodec", "libx264", newFileOutput); + return newFileOutput; + } + }, + MP4("mp4") { + @Override + String reencodeRecording(@NonNull VncRecordingContainer container, @NonNull String source) throws IOException, InterruptedException { + String newFileOutput = "/newScreen.mp4"; + container.execInContainer("ffmpeg", "-i", source, "-vcodec", "libx264", "-movflags", "faststart", newFileOutput); + return newFileOutput; + } + }; + + abstract String reencodeRecording(VncRecordingContainer container, String source) throws IOException, InterruptedException; + + @Getter + private final String filenameExtension; + } + } diff --git a/docs/modules/webdriver_containers.md b/docs/modules/webdriver_containers.md index 40b27a5207f..41b7fb6bb23 100644 --- a/docs/modules/webdriver_containers.md +++ b/docs/modules/webdriver_containers.md @@ -63,7 +63,14 @@ just for failing tests. [Record failing Tests](../../modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java) inside_block:recordFailing -Note that the seconds parameter to `withRecordingMode` should be a directory where recordings can be saved. +Note that the second parameter of `withRecordingMode` should be a directory where recordings can be saved. + +By default, the video will be recorded in [FLV](https://en.wikipedia.org/wiki/Flash_Video) format, but you can specify it explicitly or change it to [MP4](https://en.wikipedia.org/wiki/MPEG-4_Part_14) using `withRecordingMode` method with `VncRecordingFormat` option: + + +[Video Format in MP4](../../modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java) inside_block:recordMp4 +[Video Format in FLV](../../modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java) inside_block:recordFlv + If you would like to customise the file name of the recording, or provide a different directory at runtime based on the description of the test and/or its success or failure, you may provide a custom recording file factory as follows: diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index 1ceaf67ad2c..4a25ae486cd 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -27,6 +27,7 @@ import org.rnorth.ducttape.unreliables.Unreliables; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.testcontainers.containers.VncRecordingContainer.VncRecordingFormat; import org.testcontainers.containers.traits.LinkableContainer; import org.testcontainers.containers.wait.strategy.HostPortWaitStrategy; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; @@ -66,6 +67,7 @@ public class BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() .withCapabilities(new ChromeOptions()) .withRecordingMode(RECORD_ALL, target) @@ -40,25 +57,96 @@ public void recordingTestThatShouldBeRecordedAndRetained() { .withRecordingFileFactory(new DefaultRecordingFileFactory()) .withNetwork(NETWORK) ) { - chrome.start(); + File[] files = runSimpleExploreInContainer(chrome, "PASSED-.*\\.flv"); + assertEquals("Recorded file not found", 1, files.length); + } + } - doSimpleExplore(chrome); - chrome.afterTest(new TestDescription() { - @Override - public String getTestId() { - return getFilesystemFriendlyName(); - } + private File[] runSimpleExploreInContainer(BrowserWebDriverContainer container, String fileNamePattern) throws InterruptedException { + container.start(); - @Override - public String getFilesystemFriendlyName() { - return "ChromeThatRecordsAllTests-recordingTestThatShouldBeRecordedAndRetained"; - } - }, Optional.empty()); + TimeUnit.MILLISECONDS.sleep(MINIMUM_VIDEO_DURATION_MILLISECONDS); + doSimpleExplore(container); + container.afterTest(new TestDescription() { + @Override + public String getTestId() { + return getFilesystemFriendlyName(); + } + + @Override + public String getFilesystemFriendlyName() { + return "ChromeThatRecordsAllTests-recordingTestThatShouldBeRecordedAndRetained"; + } + }, Optional.empty()); + + return vncRecordingDirectory.getRoot().listFiles(new PatternFilenameFilter(fileNamePattern)); + } - String[] files = vncRecordingDirectory.getRoot().list(new PatternFilenameFilter("PASSED-.*\\.flv")); + @Test + public void recordingTestShouldHaveFlvExtension() throws InterruptedException { + File target = vncRecordingDirectory.getRoot(); + try ( + // recordFlv { + // Set (explicitly) FLV format for recorded video: + BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() + .withCapabilities(new ChromeOptions()) + .withRecordingMode(RECORD_ALL, target, VncRecordingFormat.FLV) + // } + .withRecordingFileFactory(new DefaultRecordingFileFactory()) + .withNetwork(NETWORK) + ) { + File[] files = runSimpleExploreInContainer(chrome, "PASSED-.*\\.flv"); assertEquals("Recorded file not found", 1, files.length); } } + + @Test + public void recordingTestShouldHaveMp4Extension() throws InterruptedException { + File target = vncRecordingDirectory.getRoot(); + try ( + // recordMp4 { + // Set MP4 format for recorded video: + BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() + .withCapabilities(new ChromeOptions()) + .withRecordingMode(RECORD_ALL, target, VncRecordingFormat.MP4) + // } + .withRecordingFileFactory(new DefaultRecordingFileFactory()) + .withNetwork(NETWORK) + ) { + File[] files = runSimpleExploreInContainer(chrome, "PASSED-.*\\.mp4"); + assertEquals("Recorded file not found", 1, files.length); + } + } + + @Test + public void recordingTestThatShouldHaveCorrectDuration() throws IOException, InterruptedException { + MountableFile mountableFile; + try ( + BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() + .withCapabilities(new ChromeOptions()) + .withRecordingMode(RECORD_ALL, vncRecordingDirectory.getRoot()) + .withRecordingFileFactory(new DefaultRecordingFileFactory()) + .withNetwork(NETWORK) + ) { + File[] recordedFiles = runSimpleExploreInContainer(chrome, "PASSED-.*\\.flv"); + mountableFile = MountableFile.forHostPath(recordedFiles[0].getCanonicalPath()); + } + + try (GenericContainer container = new GenericContainer<>(DockerImageName.parse("testcontainers/vnc-recorder:1.2.0"))) { + String recordFileContainerPath = "/tmp/chromeTestRecord.flv"; + container.withCopyFileToContainer(mountableFile, recordFileContainerPath) + .withCreateContainerCmdModifier(createContainerCmd -> createContainerCmd.withEntrypoint("ffmpeg")) + .withCommand("-i", recordFileContainerPath, "-f", "null", "-") + .waitingFor(new LogMessageWaitStrategy() + .withRegEx(".*Duration.*") + .withStartupTimeout(Duration.of(60, SECONDS))) + .start(); + String ffmpegOutput = container.getLogs(); + + assertTrue("Duration is incorrect in:\n " + ffmpegOutput, + ffmpegOutput.contains("Duration: 00:") && !(ffmpegOutput.contains("Duration: 00:00:00.00"))); + } + } } public static class ChromeThatRecordsFailingTests { @@ -85,7 +173,7 @@ public void recordingTestThatShouldBeRecordedButNotPersisted() { } @Test - public void recordingTestThatShouldBeRecordedAndRetained() { + public void recordingTestThatShouldBeRecordedAndRetained() throws InterruptedException { File target = vncRecordingDirectory.getRoot(); try ( // recordFailing { @@ -99,6 +187,7 @@ public void recordingTestThatShouldBeRecordedAndRetained() { ) { chrome.start(); + TimeUnit.MILLISECONDS.sleep(MINIMUM_VIDEO_DURATION_MILLISECONDS); doSimpleExplore(chrome); chrome.afterTest(new TestDescription() { @Override From b62b0ef2b7a98815f0a1937c9c1991a232e05aad Mon Sep 17 00:00:00 2001 From: Chris Roemmich Date: Wed, 10 Feb 2021 04:11:42 -0600 Subject: [PATCH 1816/5615] Implement getDatabaseName() in CockroachContainer (#3778) Co-authored-by: Sergei Egorov --- .../org/testcontainers/containers/CockroachContainer.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java b/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java index 333a6ca0158..f32e6bce385 100644 --- a/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java +++ b/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java @@ -68,6 +68,11 @@ public String getJdbcUrl() { "/" + databaseName + additionalUrlParams; } + @Override + public final String getDatabaseName() { + return databaseName; + } + @Override public String getUsername() { return username; From b0f1bbcd2eb83f939681390ca56fd929996781ab Mon Sep 17 00:00:00 2001 From: Piotr Findeisen Date: Wed, 10 Feb 2021 13:41:58 +0100 Subject: [PATCH 1817/5615] Switch to Presto image hosted on GHCR (#3667) The `prestosql` Docker Hub org has been deprecated and the current Presto image has been moved to GitHub Container Registry. --- .../java/org/testcontainers/containers/PrestoContainer.java | 4 ++-- .../src/test/java/org/testcontainers/PrestoTestImages.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java index 54cc6758562..1d9e02114ac 100644 --- a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java +++ b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java @@ -16,8 +16,8 @@ public class PrestoContainer> extends JdbcDatabaseContainer { public static final String NAME = "presto"; - private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("prestosql/presto"); - public static final String IMAGE = "prestosql/presto"; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("ghcr.io/trinodb/presto"); + public static final String IMAGE = "ghcr.io/trinodb/presto"; public static final String DEFAULT_TAG = "344"; public static final Integer PRESTO_PORT = 8080; diff --git a/modules/presto/src/test/java/org/testcontainers/PrestoTestImages.java b/modules/presto/src/test/java/org/testcontainers/PrestoTestImages.java index a257500576c..174cb7b4d4a 100644 --- a/modules/presto/src/test/java/org/testcontainers/PrestoTestImages.java +++ b/modules/presto/src/test/java/org/testcontainers/PrestoTestImages.java @@ -3,6 +3,6 @@ import org.testcontainers.utility.DockerImageName; public interface PrestoTestImages { - DockerImageName PRESTO_TEST_IMAGE = DockerImageName.parse("prestosql/presto:344"); - DockerImageName PRESTO_PREVIOUS_VERSION_TEST_IMAGE = DockerImageName.parse("prestosql/presto:343"); + DockerImageName PRESTO_TEST_IMAGE = DockerImageName.parse("ghcr.io/trinodb/presto:344"); + DockerImageName PRESTO_PREVIOUS_VERSION_TEST_IMAGE = DockerImageName.parse("ghcr.io/trinodb/presto:343"); } From 106463b2e77d45552dfbfdb23aa559de47bea8cd Mon Sep 17 00:00:00 2001 From: Per Lundberg Date: Wed, 10 Feb 2021 17:38:38 +0200 Subject: [PATCH 1818/5615] Fix extra newlines in container log / exec output (#3752) Fixes #1763 Fixes #1854 Co-authored-by: Sergei Egorov --- .../containers/output/ToStringConsumer.java | 7 -- .../output/ToStringConsumerTest.java | 89 +++++++++++++++++++ 2 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java diff --git a/core/src/main/java/org/testcontainers/containers/output/ToStringConsumer.java b/core/src/main/java/org/testcontainers/containers/output/ToStringConsumer.java index a12325865aa..40ec60fb744 100644 --- a/core/src/main/java/org/testcontainers/containers/output/ToStringConsumer.java +++ b/core/src/main/java/org/testcontainers/containers/output/ToStringConsumer.java @@ -10,21 +10,14 @@ * Created by rnorth on 26/03/2016. */ public class ToStringConsumer extends BaseConsumer { - private static final byte[] NEW_LINE = "\n".getBytes(); - - private boolean firstLine = true; private ByteArrayOutputStream stringBuffer = new ByteArrayOutputStream(); @Override public void accept(OutputFrame outputFrame) { try { if (outputFrame.getBytes() != null) { - if (!firstLine) { - stringBuffer.write(NEW_LINE); - } stringBuffer.write(outputFrame.getBytes()); stringBuffer.flush(); - firstLine = false; } } catch (IOException e) { throw new RuntimeException(e); diff --git a/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java b/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java new file mode 100644 index 00000000000..1610c688931 --- /dev/null +++ b/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java @@ -0,0 +1,89 @@ +package org.testcontainers.containers.output; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.apache.commons.lang.RandomStringUtils; +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.testcontainers.containers.Container.ExecResult; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; + +public class ToStringConsumerTest { + + private static final String LARGE_PAYLOAD; + + static { + StringBuilder builder = new StringBuilder(10_003 * 10);; + for (int i = 0; i < 10; i++) { + builder.append(' ').append(i).append(RandomStringUtils.randomAlphabetic(10000)); + } + LARGE_PAYLOAD = builder.toString(); + Assertions.assertThat(LARGE_PAYLOAD).doesNotContain("\n"); + } + + @Test + public void newlines_are_not_added_to_exec_output() throws Exception { + try (GenericContainer container = new GenericContainer<>("alpine:3.13")) { + container.withCommand("sleep", "2m"); + container.start(); + + ExecResult build = container.execInContainer("echo", "-n", LARGE_PAYLOAD); + Assertions.assertThat(build.getStdout()) + .doesNotContain("\n") + .isEqualTo(LARGE_PAYLOAD); + } + } + + @Test(timeout = 60_000L) + public void newlines_are_not_added_to_exec_output_with_tty() throws Exception { + try (GenericContainer container = new GenericContainer<>("alpine:3.13")) { + container.withCreateContainerCmdModifier(cmd -> { + cmd + .withAttachStdin(true) + .withStdinOpen(true) + .withTty(true); + }); + container.withCommand("sleep", "2m"); + container.start(); + + ExecResult build = container.execInContainer("echo", "-n", LARGE_PAYLOAD); + assertThat(build.getStdout()) + .isEqualTo(LARGE_PAYLOAD) + .doesNotContain("\n"); + } + } + + @Test + public void newlines_are_not_added_to_container_output() { + try (GenericContainer container = new GenericContainer<>("alpine:3.13")) { + container.withCommand("echo", "-n", LARGE_PAYLOAD); + container.setStartupCheckStrategy(new OneShotStartupCheckStrategy()); + container.start(); + + container.getDockerClient().waitContainerCmd(container.getContainerId()).start().awaitStatusCode(); + + assertThat(container.getLogs()) + .isEqualTo(LARGE_PAYLOAD) + .doesNotContain("\n"); + } + } + + @Test + public void newlines_are_not_added_to_container_output_with_tty() { + try (GenericContainer container = new GenericContainer<>("alpine:3.13")) { + container.withCreateContainerCmdModifier(cmd -> { + cmd.withTty(true); + }); + container.withCommand("echo", "-n", LARGE_PAYLOAD); + container.setStartupCheckStrategy(new OneShotStartupCheckStrategy()); + container.start(); + + container.getDockerClient().waitContainerCmd(container.getContainerId()).start().awaitStatusCode(); + + assertThat(container.getLogs()) + .isEqualTo(LARGE_PAYLOAD) + .doesNotContain("\n"); + } + } +} From c0eaf3412d5a6520ab973e9a693234877ae476e8 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 11 Feb 2021 16:57:46 +0100 Subject: [PATCH 1819/5615] Update mkdocs.yml --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 2457d06bcf0..82f2d291164 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -94,4 +94,4 @@ nav: - contributing_docs.md - bounty.md extra: - latest_version: 1.15.1 + latest_version: 1.15.2 From eaf9b9fe2aab4e60a4b0079e7c15afb38bb44beb Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Mon, 15 Feb 2021 21:11:59 +0100 Subject: [PATCH 1820/5615] Delegate copyFile{To,From}Container's state verification to Docker (#3805) This commit closes #3264 by letting the Docker daemon decide in which state the copy operation could be performed (we were too strict about it before). Now, only a quick check (`containerId != null`) is performed. Co-authored-by: Andrew Jefferson Co-authored-by: Sergei Egorov --- .../containers/ContainerState.java | 4 +- .../junit/FileOperationsTest.java | 50 +++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/ContainerState.java b/core/src/main/java/org/testcontainers/containers/ContainerState.java index 1b1a99d3b89..ad96aeb808c 100644 --- a/core/src/main/java/org/testcontainers/containers/ContainerState.java +++ b/core/src/main/java/org/testcontainers/containers/ContainerState.java @@ -260,7 +260,7 @@ default void copyFileToContainer(MountableFile mountableFile, String containerPa */ @SneakyThrows(IOException.class) default void copyFileToContainer(Transferable transferable, String containerPath) { - if (!isCreated()) { + if (getContainerId() == null) { throw new IllegalStateException("copyFileToContainer can only be used with created / running container"); } @@ -306,7 +306,7 @@ default void copyFileFromContainer(String containerPath, String destinationPath) */ @SneakyThrows default T copyFileFromContainer(String containerPath, ThrowingFunction function) { - if (!isCreated()) { + if (getContainerId() == null) { throw new IllegalStateException("copyFileFromContainer can only be used when the Container is created."); } diff --git a/core/src/test/java/org/testcontainers/junit/FileOperationsTest.java b/core/src/test/java/org/testcontainers/junit/FileOperationsTest.java index 5e12fafc064..beca4a8a49f 100644 --- a/core/src/test/java/org/testcontainers/junit/FileOperationsTest.java +++ b/core/src/test/java/org/testcontainers/junit/FileOperationsTest.java @@ -2,15 +2,19 @@ import com.github.dockerjava.api.exception.NotFoundException; import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.assertj.core.api.Assertions; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import org.testcontainers.utility.MountableFile; import java.io.File; import java.io.IOException; +import static org.assertj.core.api.Assertions.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; import static org.testcontainers.TestImages.ALPINE_IMAGE; @@ -103,4 +107,50 @@ public void shouldCopyFileFromContainerTest() throws IOException { assertTrue("Files aren't same ", FileUtils.contentEquals(expectedFile, actualFile)); } } + + @Test + public void copyFileOperationsShouldFailWhenNotStartedTest() { + try (GenericContainer container = new GenericContainer<>(ALPINE_IMAGE).withCommand("top")) { + Assertions + .assertThatThrownBy(() -> { + MountableFile mountableFile = MountableFile.forClasspathResource("test_copy_to_container.txt"); + container.copyFileToContainer(mountableFile, "/home/test.txt"); + }) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("can only be used with created / running container"); + + Assertions + .assertThatThrownBy(() -> { + container.copyFileFromContainer( + "/home/test_copy_to_container.txt", + IOUtils::toByteArray + ); + }) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("can only be used when the Container is created"); + } + } + + @Test + public void shouldCopyFileFromExitedContainerTest() throws IOException { + try ( + GenericContainer container = new GenericContainer<>(ALPINE_IMAGE) + .withCommand("sh", "-c", "echo -n 'Hello!' > /home/file_in_container.txt") + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + ) { + container.start(); + assertThat(container.getDockerClient().waitContainerCmd(container.getContainerId()).start().awaitStatusCode()) + .isEqualTo(0); + + container.copyFileFromContainer( + "/home/file_in_container.txt", + IOUtils::toByteArray + ); + + container.copyFileToContainer( + MountableFile.forClasspathResource("test_copy_to_container.txt"), + "/test.txt" + ); + } + } } From f588082636dc2749f4129e943b02ede62527b41c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= Date: Fri, 19 Feb 2021 16:57:23 +0100 Subject: [PATCH 1821/5615] Fix mp4 recording to be compatible with Firefox (#3812) Firefox still doesn't support yuv444 so use yuv420p https://shallowsky.com/blog/linux/videos-from-images.html --- .../org/testcontainers/containers/VncRecordingContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java b/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java index a171e6ba2f6..be8a4ed735f 100644 --- a/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java +++ b/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java @@ -132,7 +132,7 @@ String reencodeRecording(@NonNull VncRecordingContainer container, @NonNull Stri @Override String reencodeRecording(@NonNull VncRecordingContainer container, @NonNull String source) throws IOException, InterruptedException { String newFileOutput = "/newScreen.mp4"; - container.execInContainer("ffmpeg", "-i", source, "-vcodec", "libx264", "-movflags", "faststart", newFileOutput); + container.execInContainer("ffmpeg", "-i", source, "-vcodec", "libx264", "-movflags", "faststart", "-pix_fmt", "yuv420p", newFileOutput); return newFileOutput; } }; From 80a172e6fd0e82b476bf76e9ad3d9440fbd4b07b Mon Sep 17 00:00:00 2001 From: James Ward Date: Mon, 8 Mar 2021 12:48:22 -0700 Subject: [PATCH 1822/5615] Add Google (#3868) --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index bc6d1e64afe..b53ebbf4db5 100644 --- a/docs/index.md +++ b/docs/index.md @@ -163,6 +163,7 @@ A huge thank you to our sponsors: * [Apache SkyWalking](http://github.com/apache/skywalking) - End-to-end testing of the Apache SkyWalking, and plugin tests of its subproject, [Apache SkyWalking Python](http://github.com/apache/skywalking-python), and of its eco-system built by the community, like [SkyAPM NodeJS Agent](http://github.com/SkyAPM/nodejs) * [jOOQ](https://www.jooq.org) - Integration testing all of jOOQ with a variety of RDBMS * [Trino (formerly Presto SQL)](https://trino.io) - Integration testing all Trino core & connectors, including tests of multi-node deployments and security configurations. +* Google - Various open source projects: [OpenTelemetry](https://github.com/GoogleCloudPlatform/opentelemetry-operations-java), [Universal Application Tool](https://github.com/seattle-uat/universal-application-tool), [CloudBowl](https://github.com/GoogleCloudPlatform/cloudbowl-microservice-game) ## License From 5c1ac7ca518d985495afd76b89ee5325145adbfc Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 12 Mar 2021 09:58:56 +0000 Subject: [PATCH 1823/5615] Fix GitHub Actions error due to short SHA form (#3874) --- .github/workflows/release-drafter.yml | 2 +- .github/workflows/update-docs-version.yml | 2 +- .github/workflows/update-gradle-wrapper.yml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 8f88ac2231e..28f14cb0ad3 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -9,6 +9,6 @@ jobs: update_release_draft: runs-on: ubuntu-latest steps: - - uses: release-drafter/release-drafter@4d1215c #v5.13.0 + - uses: release-drafter/release-drafter@e5ccf147077e46b0225a80bbe314d795d77bb7a2 # v5.14.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index d2eb803fda2..64658c1be4a 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -22,6 +22,6 @@ jobs: git add mkdocs.yml git commit -m "Update documentation version to ${GITHUB_REF##*/}" -m "skip-checks: true" || echo "not changed" - name: Push changes - uses: ad-m/github-push-action@40bf560936a8022e68a3c00e7d2abefaf01305a6 + uses: ad-m/github-push-action@40bf560936a8022e68a3c00e7d2abefaf01305a6 # v0.6.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/update-gradle-wrapper.yml b/.github/workflows/update-gradle-wrapper.yml index e8f4f130b12..d34110f5a6d 100644 --- a/.github/workflows/update-gradle-wrapper.yml +++ b/.github/workflows/update-gradle-wrapper.yml @@ -12,9 +12,9 @@ jobs: - uses: actions/checkout@v2 - name: Update Gradle Wrapper - uses: gradle-update/update-gradle-wrapper-action@74a035ce333f39c84811ce1167669bfa6c65f440 + uses: gradle-update/update-gradle-wrapper-action@af940e8bff73d435137caae9d08cf7a1badf5497 # v1.0.13 with: repo-token: ${{ secrets.GITHUB_TOKEN }} labels: dependencies - - uses: gradle/wrapper-validation-action@e2c57acffb2c9aa5a8dc6eda2bbae0b6e495bd4c + - uses: gradle/wrapper-validation-action@e2c57acffb2c9aa5a8dc6eda2bbae0b6e495bd4c # v1.0.3 From ca1dfc499d801d2a4882d9b1e4dfe1150498cfaa Mon Sep 17 00:00:00 2001 From: Erhun Baycelik Date: Fri, 12 Mar 2021 15:59:38 +0200 Subject: [PATCH 1824/5615] Documentation TransferWise has changed its name as Wise. (#3873) Co-authored-by: Richard North --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index b53ebbf4db5..6efa8f82eb3 100644 --- a/docs/index.md +++ b/docs/index.md @@ -158,7 +158,7 @@ A huge thank you to our sponsors: * [JHipster](https://www.jhipster.tech/) - Couchbase and Cassandra integration testing * [wescale](https://www.wescale.com) - Integration testing against HTTP service mocks and various data stores * [Marquez](https://marquezproject.github.io/marquez) - PostgreSQL integration testing -* [Transferwise](https://transferwise.com/) - Integration testing for different RDBMS, kafka and micro services +* [Wise (formerly TransferWise)](https://wise.com) - Integration testing for different RDBMS, kafka and micro services * [XWiki](https://xwiki.org) - [Testing XWiki](https://dev.xwiki.org/xwiki/bin/view/Community/Testing/DockerTesting/) under all [supported configurations](https://dev.xwiki.org/xwiki/bin/view/Community/SupportStrategy/) * [Apache SkyWalking](http://github.com/apache/skywalking) - End-to-end testing of the Apache SkyWalking, and plugin tests of its subproject, [Apache SkyWalking Python](http://github.com/apache/skywalking-python), and of its eco-system built by the community, like [SkyAPM NodeJS Agent](http://github.com/SkyAPM/nodejs) * [jOOQ](https://www.jooq.org) - Integration testing all of jOOQ with a variety of RDBMS From 61dcd7ce371f84d28fcc28192223d41c20eb95bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Torres?= Date: Fri, 12 Mar 2021 16:33:44 +0000 Subject: [PATCH 1825/5615] Add Backbase to "who is using" section (#3870) --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index 6efa8f82eb3..18fd3f034b1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -164,6 +164,7 @@ A huge thank you to our sponsors: * [jOOQ](https://www.jooq.org) - Integration testing all of jOOQ with a variety of RDBMS * [Trino (formerly Presto SQL)](https://trino.io) - Integration testing all Trino core & connectors, including tests of multi-node deployments and security configurations. * Google - Various open source projects: [OpenTelemetry](https://github.com/GoogleCloudPlatform/opentelemetry-operations-java), [Universal Application Tool](https://github.com/seattle-uat/universal-application-tool), [CloudBowl](https://github.com/GoogleCloudPlatform/cloudbowl-microservice-game) +* [Backbase](https://www.backbase.com/) - Unit, Integration and Acceptance testing for different the databases supported (Oracle, SQL Server, MySQL), the different messaging systems supported (Kafka, Rabbit, AMQ) and other microservices and HTTP mocks. ## License From 60865fa43113eaa1bce6c6761fcd9027c58c05cc Mon Sep 17 00:00:00 2001 From: Viktar Patotski Date: Sat, 13 Mar 2021 11:55:34 +0100 Subject: [PATCH 1826/5615] Bump docs environment to python 3.7 (#3871) --- docker-compose.yml | 2 +- docs/contributing_docs.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 822956457f7..1ce5532d0bd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.7" services: docs: - image: python:3.6 + image: python:3.7 command: sh -c "pip install -r requirements.txt && mkdocs serve -a 0.0.0.0:8000" working_dir: /docs volumes: diff --git a/docs/contributing_docs.md b/docs/contributing_docs.md index 9ed4b08169c..f6f9304a339 100644 --- a/docs/contributing_docs.md +++ b/docs/contributing_docs.md @@ -15,7 +15,7 @@ The root of the project contains a `docker-compose.yml` file. Simply run `docker ### Using Python locally -* Ensure that you have Python 3.6.0 or higher. +* Ensure that you have Python 3.7.0 or higher. * Set up a virtualenv and run `pip install -r requirements.txt` in the `testcontainers-java` root directory. * Once Python dependencies have been installed, run `mkdocs serve` to start a local auto-updating MkDocs server. From b635eb5ac38d8c2235dd4a2af145dca21d43dcfc Mon Sep 17 00:00:00 2001 From: GooseMagnet Date: Sat, 13 Mar 2021 05:59:40 -0500 Subject: [PATCH 1827/5615] Fix NPE bug with docker-compose files in working directory (#3866) --- .../containers/ParsedDockerComposeFile.java | 2 ++ .../junit/DockerComposeContainerTest.java | 23 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/core/src/main/java/org/testcontainers/containers/ParsedDockerComposeFile.java b/core/src/main/java/org/testcontainers/containers/ParsedDockerComposeFile.java index 8f88724647f..a0972273b5b 100644 --- a/core/src/main/java/org/testcontainers/containers/ParsedDockerComposeFile.java +++ b/core/src/main/java/org/testcontainers/containers/ParsedDockerComposeFile.java @@ -120,6 +120,7 @@ private void findImageNamesInDockerfile(Map serviceDefinitionMap) { final Object contextRelativePath = buildElement.get("context"); if (dockerfileRelativePath instanceof String && contextRelativePath instanceof String) { dockerfilePath = composeFile + .getAbsoluteFile() .getParentFile() .toPath() .resolve((String) contextRelativePath) @@ -128,6 +129,7 @@ private void findImageNamesInDockerfile(Map serviceDefinitionMap) { } } else if (buildNode instanceof String) { dockerfilePath = composeFile + .getAbsoluteFile() .getParentFile() .toPath() .resolve((String) buildNode) diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java index 53d90872332..17a9b665513 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java @@ -6,6 +6,9 @@ import org.testcontainers.containers.DockerComposeContainer; import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.Optional; import static java.lang.String.format; @@ -53,4 +56,24 @@ public void shouldReturnEmptyResultOnNoneExistingService() { Optional result = environment.getContainerByServiceName(notExistingServiceName); assertFalse(format("No container should be found under service name %s", notExistingServiceName), result.isPresent()); } + + @Test + public void shouldCreateContainerWhenFileNotPrefixedWithPath() throws IOException { + String validYaml = + "version: '2.2'\n" + + "services:\n" + + " http:\n" + + " build: .\n" + + " image: python:latest\n" + + " ports:\n" + + " - 8080:8080"; + + File filePathNotStartWithDotSlash = new File("docker-compose-test.yml"); + filePathNotStartWithDotSlash.createNewFile(); + filePathNotStartWithDotSlash.deleteOnExit(); + Files.write(filePathNotStartWithDotSlash.toPath(), validYaml.getBytes(StandardCharsets.UTF_8)); + + final DockerComposeContainer dockerComposeContainer = new DockerComposeContainer<>(filePathNotStartWithDotSlash); + assertNotNull("Container could not be created using docker compose file", dockerComposeContainer); + } } From 26be859459a8596a58791f4b7101fe7026a3d5ba Mon Sep 17 00:00:00 2001 From: askfor Date: Sat, 13 Mar 2021 19:07:11 +0800 Subject: [PATCH 1828/5615] Make DockerComposeContainer only pull necessary images when multiple compose files were given (#3787) Co-authored-by: tianjie --- .../containers/DockerComposeContainer.java | 7 ++- .../containers/DockerComposeFiles.java | 43 +++++++++++++++++++ .../containers/ParsedDockerComposeFile.java | 17 ++++---- .../containers/DockerComposeFilesTest.java | 25 +++++++++++ ...ParsedDockerComposeFileValidationTest.java | 30 ++++++++++--- .../docker-compose-imagename-overriding-a.yml | 12 ++++++ .../docker-compose-imagename-overriding-b.yml | 10 +++++ 7 files changed, 127 insertions(+), 17 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/containers/DockerComposeFiles.java create mode 100644 core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java create mode 100644 core/src/test/resources/docker-compose-imagename-overriding-a.yml create mode 100644 core/src/test/resources/docker-compose-imagename-overriding-b.yml diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 3d58ad53b3b..89d6d692db7 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -77,7 +77,7 @@ public class DockerComposeContainer> e */ private final String identifier; private final List composeFiles; - private Set parsedComposeFiles; + private DockerComposeFiles dockerComposeFiles; private final Map scalingPreferences = new HashMap<>(); private DockerClient dockerClient; private boolean localCompose; @@ -126,7 +126,7 @@ public DockerComposeContainer(String identifier, File... composeFiles) { public DockerComposeContainer(String identifier, List composeFiles) { this.composeFiles = composeFiles; - this.parsedComposeFiles = composeFiles.stream().map(ParsedDockerComposeFile::new).collect(Collectors.toSet()); + this.dockerComposeFiles = new DockerComposeFiles(composeFiles); // Use a unique identifier so that containers created for this compose environment can be identified this.identifier = identifier; @@ -184,8 +184,7 @@ private void pullImages() { // Pull images using our docker client rather than compose itself, // (a) as a workaround for https://github.com/docker/compose/issues/5854, which prevents authenticated image pulls being possible when credential helpers are in use // (b) so that credential helper-based auth still works when compose is running from within a container - parsedComposeFiles.stream() - .flatMap(it -> it.getDependencyImageNames().stream()) + dockerComposeFiles.getDependencyImages() .forEach(imageName -> { try { log.info("Preemptively checking local images for '{}', referenced via a compose file or transitive Dockerfile. If not available, it will be pulled.", imageName); diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeFiles.java b/core/src/main/java/org/testcontainers/containers/DockerComposeFiles.java new file mode 100644 index 00000000000..15d035e3673 --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeFiles.java @@ -0,0 +1,43 @@ +package org.testcontainers.containers; + +import java.io.File; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.stream.Collectors; + +public class DockerComposeFiles { + + private List parsedComposeFiles; + + public DockerComposeFiles(List composeFiles) { + this.parsedComposeFiles = composeFiles.stream().map(ParsedDockerComposeFile::new).collect(Collectors.toList()); + } + + public Set getDependencyImages() { + + Map> mergedServiceNameToImageNames = mergeServiceDependencyImageNames(); + + return getImageNames(mergedServiceNameToImageNames); + } + + private Map> mergeServiceDependencyImageNames() { + Map> mergedServiceNameToImageNames = new HashMap(); + for (ParsedDockerComposeFile parsedComposeFile : parsedComposeFiles) { + for (Entry> entry : parsedComposeFile.getServiceNameToImageNames().entrySet()) { + mergedServiceNameToImageNames.put(entry.getKey(), entry.getValue()); + } + } + return mergedServiceNameToImageNames; + } + + private Set getImageNames(Map> serviceToImageNames) { + Set imageNames = new HashSet<>(); + serviceToImageNames.values().stream().forEach(imageNames::addAll); + return imageNames; + } + +} diff --git a/core/src/main/java/org/testcontainers/containers/ParsedDockerComposeFile.java b/core/src/main/java/org/testcontainers/containers/ParsedDockerComposeFile.java index a0972273b5b..e9457ba92bd 100644 --- a/core/src/main/java/org/testcontainers/containers/ParsedDockerComposeFile.java +++ b/core/src/main/java/org/testcontainers/containers/ParsedDockerComposeFile.java @@ -1,6 +1,7 @@ package org.testcontainers.containers; import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Sets; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -12,7 +13,7 @@ import java.io.FileInputStream; import java.nio.file.Files; import java.nio.file.Path; -import java.util.HashSet; +import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -29,7 +30,7 @@ class ParsedDockerComposeFile { private final File composeFile; @Getter - private Set dependencyImageNames = new HashSet<>(); + private Map> serviceNameToImageNames = new HashMap<>(); ParsedDockerComposeFile(File composeFile) { Yaml yaml = new Yaml(); @@ -87,8 +88,8 @@ private void parseAndValidate() { final Map serviceDefinitionMap = (Map) serviceDefinition; validateNoContainerNameSpecified(serviceName, serviceDefinitionMap); - findServiceImageName(serviceDefinitionMap); - findImageNamesInDockerfile(serviceDefinitionMap); + findServiceImageName(serviceName, serviceDefinitionMap); + findImageNamesInDockerfile(serviceName, serviceDefinitionMap); } } @@ -102,15 +103,15 @@ private void validateNoContainerNameSpecified(String serviceName, Map serviceDef } } - private void findServiceImageName(Map serviceDefinitionMap) { + private void findServiceImageName(String serviceName, Map serviceDefinitionMap) { if (serviceDefinitionMap.containsKey("image") && serviceDefinitionMap.get("image") instanceof String) { final String imageName = (String) serviceDefinitionMap.get("image"); log.debug("Resolved dependency image for Docker Compose in {}: {}", composeFileName, imageName); - dependencyImageNames.add(imageName); + serviceNameToImageNames.put(serviceName, Sets.newHashSet(imageName)); } } - private void findImageNamesInDockerfile(Map serviceDefinitionMap) { + private void findImageNamesInDockerfile(String serviceName, Map serviceDefinitionMap) { final Object buildNode = serviceDefinitionMap.get("build"); Path dockerfilePath = null; @@ -141,7 +142,7 @@ private void findImageNamesInDockerfile(Map serviceDefinitionMap) { Set resolvedImageNames = new ParsedDockerfile(dockerfilePath).getDependencyImageNames(); if (!resolvedImageNames.isEmpty()) { log.debug("Resolved Dockerfile dependency images for Docker Compose in {} -> {}: {}", composeFileName, dockerfilePath, resolvedImageNames); - this.dependencyImageNames.addAll(resolvedImageNames); + this.serviceNameToImageNames.put(serviceName, resolvedImageNames); } } } diff --git a/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java b/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java new file mode 100644 index 00000000000..8dbcde2181d --- /dev/null +++ b/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java @@ -0,0 +1,25 @@ +package org.testcontainers.containers; + + +import com.google.common.collect.Lists; +import java.io.File; +import org.assertj.core.api.Assertions; +import org.junit.Test; + +public class DockerComposeFilesTest { + @Test + public void shouldGetDependencyImages() { + DockerComposeFiles dockerComposeFiles = new DockerComposeFiles(Lists.newArrayList(new File("src/test/resources/docker-compose-imagename-parsing-v2.yml"))); + Assertions.assertThat(dockerComposeFiles.getDependencyImages()) + .containsExactlyInAnyOrder("postgres", "redis", "mysql"); + } + + @Test + public void shouldGetDependencyImagesWhenOverriding() { + DockerComposeFiles dockerComposeFiles = new DockerComposeFiles( + Lists.newArrayList(new File("src/test/resources/docker-compose-imagename-overriding-a.yml"), + new File("src/test/resources/docker-compose-imagename-overriding-b.yml"))); + Assertions.assertThat(dockerComposeFiles.getDependencyImages()) + .containsExactlyInAnyOrder("alpine:3.2", "redis:b", "mysql:b", "aservice"); + } +} diff --git a/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java b/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java index 9a8421086f1..bdceba343d9 100644 --- a/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java +++ b/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java @@ -9,7 +9,7 @@ import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.entry; public class ParsedDockerComposeFileValidationTest { @@ -76,27 +76,47 @@ public void shouldIgnoreUnknownStructure() { public void shouldObtainImageNamesV1() { File file = new File("src/test/resources/docker-compose-imagename-parsing-v1.yml"); ParsedDockerComposeFile parsedFile = new ParsedDockerComposeFile(file); - assertEquals("all defined images are found", Sets.newHashSet("redis", "mysql", "postgres"), parsedFile.getDependencyImageNames()); // redis, mysql from compose file, postgres from Dockerfile build + Assertions.assertThat(parsedFile.getServiceNameToImageNames()) + .contains( + entry("mysql", Sets.newHashSet("mysql")), + entry("redis", Sets.newHashSet("redis")), + entry("custom", Sets.newHashSet("postgres"))) + .as("all defined images are found"); // redis, mysql from compose file, postgres from Dockerfile build } @Test public void shouldObtainImageNamesV2() { File file = new File("src/test/resources/docker-compose-imagename-parsing-v2.yml"); ParsedDockerComposeFile parsedFile = new ParsedDockerComposeFile(file); - assertEquals("all defined images are found", Sets.newHashSet("redis", "mysql", "postgres"), parsedFile.getDependencyImageNames()); // redis, mysql from compose file, postgres from Dockerfile build + Assertions.assertThat(parsedFile.getServiceNameToImageNames()) + .contains( + entry("mysql", Sets.newHashSet("mysql")), + entry("redis", Sets.newHashSet("redis")), + entry("custom", Sets.newHashSet("postgres"))) + .as("all defined images are found"); } @Test public void shouldObtainImageFromDockerfileBuild() { File file = new File("src/test/resources/docker-compose-imagename-parsing-dockerfile.yml"); ParsedDockerComposeFile parsedFile = new ParsedDockerComposeFile(file); - assertEquals("all defined images are found", Sets.newHashSet("redis", "mysql", "alpine:3.2"), parsedFile.getDependencyImageNames()); // redis, mysql from compose file, alpine:3.2 from Dockerfile build + Assertions.assertThat(parsedFile.getServiceNameToImageNames()) + .contains( + entry("mysql", Sets.newHashSet("mysql")), + entry("redis", Sets.newHashSet("redis")), + entry("custom", Sets.newHashSet("alpine:3.2"))) + .as("all defined images are found"); // r/ redis, mysql from compose file, alpine:3.2 from Dockerfile build } @Test public void shouldObtainImageFromDockerfileBuildWithContext() { File file = new File("src/test/resources/docker-compose-imagename-parsing-dockerfile-with-context.yml"); ParsedDockerComposeFile parsedFile = new ParsedDockerComposeFile(file); - assertEquals("all defined images are found", Sets.newHashSet("redis", "mysql", "alpine:3.2"), parsedFile.getDependencyImageNames()); // redis, mysql from compose file, alpine:3.2 from Dockerfile build + Assertions.assertThat(parsedFile.getServiceNameToImageNames()) + .contains( + entry("mysql", Sets.newHashSet("mysql")), + entry("redis", Sets.newHashSet("redis")), + entry("custom", Sets.newHashSet("alpine:3.2"))) + .as("all defined images are found"); // redis, mysql from compose file, alpine:3.2 from Dockerfile build } } diff --git a/core/src/test/resources/docker-compose-imagename-overriding-a.yml b/core/src/test/resources/docker-compose-imagename-overriding-a.yml new file mode 100644 index 00000000000..50b226d32a4 --- /dev/null +++ b/core/src/test/resources/docker-compose-imagename-overriding-a.yml @@ -0,0 +1,12 @@ +version: "2.1" +services: + aservice: + image: aservice + redis: + image: redis:a + mysql: + image: mysql:a + custom: + build: . +networks: + custom_network: {} diff --git a/core/src/test/resources/docker-compose-imagename-overriding-b.yml b/core/src/test/resources/docker-compose-imagename-overriding-b.yml new file mode 100644 index 00000000000..7e6f83c6b6a --- /dev/null +++ b/core/src/test/resources/docker-compose-imagename-overriding-b.yml @@ -0,0 +1,10 @@ +version: "2.1" +services: + redis: + image: redis:b + mysql: + image: mysql:b + custom: + build: compose-dockerfile +networks: + custom_network: {} From 3c8beb2be6d94567bb7856aefc407eadb9a868f2 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 13 Mar 2021 12:19:32 +0000 Subject: [PATCH 1829/5615] Bump copyright dates and remove Java 8 reference --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ec5ab97ca12..c87138e7d1a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Testcontainers -> Testcontainers is a Java 8 library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container. +> Testcontainers is a Java library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container. [![Build Status](https://travis-ci.org/testcontainers/testcontainers-java.svg?branch=master)](https://travis-ci.org/testcontainers/testcontainers-java)[ ![Download](https://api.bintray.com/packages/testcontainers/releases/testcontainers/images/download.svg) ](https://bintray.com/testcontainers/releases/testcontainers/_latestVersion) @@ -14,10 +14,10 @@ See [LICENSE](LICENSE). ## Copyright -Copyright (c) 2015 - 2020 Richard North and other authors. +Copyright (c) 2015 - 2021 Richard North and other authors. -MS SQL Server module is (c) 2017 - 2020 G DATA Software AG and other authors. +MS SQL Server module is (c) 2017 - 2021 G DATA Software AG and other authors. -Hashicorp Vault module is (c) 2017 - 2020 Capital One Services, LLC and other authors. +Hashicorp Vault module is (c) 2017 - 2021 Capital One Services, LLC and other authors. See [contributors](https://github.com/testcontainers/testcontainers-java/graphs/contributors) for all contributors. From 57ce0c4861d83c804fc1eafb0d2239f35726553f Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 15 Mar 2021 10:45:39 +0000 Subject: [PATCH 1830/5615] Update copyright date --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 18fd3f034b1..013c18d0700 100644 --- a/docs/index.md +++ b/docs/index.md @@ -179,6 +179,6 @@ This project was initially inspired by a [gist](https://gist.github.com/mosheesh ## Copyright -Copyright (c) 2015-2020 Richard North and other authors. +Copyright (c) 2015-2021 Richard North and other authors. See [AUTHORS](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/AUTHORS) for contributors. From a406f0441d4a08431aff2bd661f9baada2e4fe19 Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 19 Mar 2021 20:16:43 +0000 Subject: [PATCH 1831/5615] Add Cloudflare Web Analytics to docs site (#3896) --- docs/theme/main.html | 5 +++++ mkdocs.yml | 1 + 2 files changed, 6 insertions(+) create mode 100644 docs/theme/main.html diff --git a/docs/theme/main.html b/docs/theme/main.html new file mode 100644 index 00000000000..f96a2669e7e --- /dev/null +++ b/docs/theme/main.html @@ -0,0 +1,5 @@ +{% extends "base.html" %} + +{% block analytics %} + +{% endblock %} \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 82f2d291164..b0608746825 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -5,6 +5,7 @@ plugins: - markdownextradata theme: name: 'material' + custom_dir: 'docs/theme' palette: primary: 'blue' accent: 'teal' From a032dc21f4991c43dd30f187136d91eadb623b46 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 20 Mar 2021 09:31:48 +0000 Subject: [PATCH 1832/5615] Docs: Bump jinja2 from 2.11.2 to 2.11.3 (#3897) Bumps [jinja2](https://github.com/pallets/jinja) from 2.11.2 to 2.11.3. - [Release notes](https://github.com/pallets/jinja/releases) - [Changelog](https://github.com/pallets/jinja/blob/master/CHANGES.rst) - [Commits](https://github.com/pallets/jinja/compare/2.11.2...2.11.3) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Pipfile.lock | 254 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 179 insertions(+), 75 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 899f8c82170..6ac609b04a9 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "c9a90b5f198797e8a6165fa7cb6c2143158bed425d0a8c5f63dc37ea4dcab632" + "sha256": "d3830267ea93391a077a2ba5b93aa8218c0a38002694b404b56df11498da3e56" }, "pipfile-spec": 6, "requires": { @@ -18,76 +18,112 @@ "default": { "click": { "hashes": [ - "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", - "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7" + "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", + "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" ], - "version": "==7.0" + "version": "==7.1.2" + }, + "importlib-metadata": { + "hashes": [ + "sha256:742add720a20d0467df2f444ae41704000f50e1234f46174b51f9c6031a1bd71", + "sha256:b74159469b464a99cb8cc3e21973e4d96e05d3024d337313fedb618a6e86e6f4" + ], + "markers": "python_version < '3.8'", + "version": "==3.7.3" }, "jinja2": { "hashes": [ - "sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd", - "sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4" + "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419", + "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6" ], - "version": "==2.11.2" + "index": "pypi", + "version": "==2.11.3" }, "livereload": { "hashes": [ - "sha256:29cadfabcedd12eed792e0131991235b9d4764d4474bed75cf525f57109ec0a2", - "sha256:e632a6cd1d349155c1d7f13a65be873b38f43ef02961804a1bba8d817fa649a7" + "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869" ], - "version": "==2.6.0" + "version": "==2.6.3" }, "markdown": { "hashes": [ - "sha256:c00429bd503a47ec88d5e30a751e147dcb4c6889663cd3e2ba0afe858e009baa", - "sha256:d02e0f9b04c500cde6637c11ad7c72671f359b87b9fe924b2383649d8841db7c" + "sha256:31b5b491868dcc87d6c24b7e3d19a0d730d59d3e46f4eea6430a321bed387a49", + "sha256:96c3ba1261de2f7547b46a00ea8463832c921d3f9d6aba3f255a6f71386db20c" ], - "version": "==3.0.1" + "version": "==3.3.4" }, "markupsafe": { "hashes": [ - "sha256:048ef924c1623740e70204aa7143ec592504045ae4429b59c30054cb31e3c432", - "sha256:130f844e7f5bdd8e9f3f42e7102ef1d49b2e6fdf0d7526df3f87281a532d8c8b", - "sha256:19f637c2ac5ae9da8bfd98cef74d64b7e1bb8a63038a3505cd182c3fac5eb4d9", - "sha256:1b8a7a87ad1b92bd887568ce54b23565f3fd7018c4180136e1cf412b405a47af", - "sha256:1c25694ca680b6919de53a4bb3bdd0602beafc63ff001fea2f2fc16ec3a11834", - "sha256:1f19ef5d3908110e1e891deefb5586aae1b49a7440db952454b4e281b41620cd", - "sha256:1fa6058938190ebe8290e5cae6c351e14e7bb44505c4a7624555ce57fbbeba0d", - "sha256:31cbb1359e8c25f9f48e156e59e2eaad51cd5242c05ed18a8de6dbe85184e4b7", - "sha256:3e835d8841ae7863f64e40e19477f7eb398674da6a47f09871673742531e6f4b", - "sha256:4e97332c9ce444b0c2c38dd22ddc61c743eb208d916e4265a2a3b575bdccb1d3", - "sha256:525396ee324ee2da82919f2ee9c9e73b012f23e7640131dd1b53a90206a0f09c", - "sha256:52b07fbc32032c21ad4ab060fec137b76eb804c4b9a1c7c7dc562549306afad2", - "sha256:52ccb45e77a1085ec5461cde794e1aa037df79f473cbc69b974e73940655c8d7", - "sha256:5c3fbebd7de20ce93103cb3183b47671f2885307df4a17a0ad56a1dd51273d36", - "sha256:5e5851969aea17660e55f6a3be00037a25b96a9b44d2083651812c99d53b14d1", - "sha256:5edfa27b2d3eefa2210fb2f5d539fbed81722b49f083b2c6566455eb7422fd7e", - "sha256:7d263e5770efddf465a9e31b78362d84d015cc894ca2c131901a4445eaa61ee1", - "sha256:83381342bfc22b3c8c06f2dd93a505413888694302de25add756254beee8449c", - "sha256:857eebb2c1dc60e4219ec8e98dfa19553dae33608237e107db9c6078b1167856", - "sha256:98e439297f78fca3a6169fd330fbe88d78b3bb72f967ad9961bcac0d7fdd1550", - "sha256:bf54103892a83c64db58125b3f2a43df6d2cb2d28889f14c78519394feb41492", - "sha256:d9ac82be533394d341b41d78aca7ed0e0f4ba5a2231602e2f05aa87f25c51672", - "sha256:e982fe07ede9fada6ff6705af70514a52beb1b2c3d25d4e873e82114cf3c5401", - "sha256:edce2ea7f3dfc981c4ddc97add8a61381d9642dc3273737e756517cc03e84dd6", - "sha256:efdc45ef1afc238db84cb4963aa689c0408912a0239b0721cb172b4016eb31d6", - "sha256:f137c02498f8b935892d5c0172560d7ab54bc45039de8805075e19079c639a9c", - "sha256:f82e347a72f955b7017a39708a3667f106e6ad4d10b25f237396a7115d8ed5fd", - "sha256:fb7c206e01ad85ce57feeaaa0bf784b97fa3cad0d4a5737bc5295785f5c613a1" - ], - "version": "==1.1.0" + "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", + "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", + "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", + "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", + "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", + "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f", + "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39", + "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", + "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", + "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014", + "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f", + "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", + "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", + "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", + "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", + "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b", + "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", + "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", + "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", + "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85", + "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1", + "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", + "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", + "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", + "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850", + "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0", + "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", + "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", + "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb", + "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", + "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", + "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", + "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1", + "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2", + "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", + "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", + "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", + "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7", + "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", + "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8", + "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", + "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193", + "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", + "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b", + "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", + "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2", + "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5", + "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c", + "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032", + "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", + "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be", + "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621" + ], + "version": "==1.1.1" }, "mkdocs": { "hashes": [ "sha256:17d34329aad75d5de604b9ed4e31df3a4d235afefdc46ce7b1964fddb2e1e939", "sha256:8cc8b38325456b9e942c981a209eaeb1e9f3f77b493ad755bfef889b9c8d356a" ], + "index": "pypi", "version": "==1.0.4" }, "mkdocs-codeinclude-plugin": { - "editable": true, - "git": "https://github.com/rnorth/mkdocs-codeinclude-plugin", - "ref": "99417fdad0634ccfeea7eaa8ba648a08c7513e7c" + "hashes": [ + "sha256:5d74bd4c1be8e64d71137c1ef1d27a0f9822d3d9ecb2af658d2af7664568a923", + "sha256:a94be142e27ef4a70d8ecc702c21d9596304e06d382d86b39a10ffe8b0375887" + ], + "index": "pypi", + "version": "==0.2.0" }, "mkdocs-markdownextradata-plugin": { "hashes": [ @@ -106,52 +142,120 @@ }, "pygments": { "hashes": [ - "sha256:5ffada19f6203563680669ee7f53b64dabbeb100eb51b61996085e99c03b284a", - "sha256:e8218dd399a61674745138520d0d4cf2621d7e032439341bc3f647bff125818d" + "sha256:2656e1a6edcdabf4275f9a3640db59fd5de107d88e8663c5d4e9a0fa62f77f94", + "sha256:534ef71d539ae97d4c3a4cf7d6f110f214b0e687e92f9cb9d2a3b0d3101289c8" ], - "version": "==2.3.1" + "version": "==2.8.1" }, "pymdown-extensions": { "hashes": [ - "sha256:25b0a7967fa697b5035e23340a48594e3e93acb10b06d74574218ace3347d1df", - "sha256:6cf0cf36b5a03b291ace22dc2f320f4789ce56fbdb6635a3be5fadbf5d7694dd" + "sha256:478b2c04513fbb2db61688d5f6e9030a92fb9be14f1f383535c43f7be9dff95b", + "sha256:632371fa3bf1b21a0e3f4063010da59b41db049f261f4c0b0872069a9b6d1735" ], - "version": "==6.0" + "version": "==8.1.1" }, "pyyaml": { "hashes": [ - "sha256:3d7da3009c0f3e783b2c873687652d83b1bbfd5c88e9813fb7e5b03c0dd3108b", - "sha256:3ef3092145e9b70e3ddd2c7ad59bdd0252a94dfe3949721633e41344de00a6bf", - "sha256:40c71b8e076d0550b2e6380bada1f1cd1017b882f7e16f09a65be98e017f211a", - "sha256:558dd60b890ba8fd982e05941927a3911dc409a63dcb8b634feaa0cda69330d3", - "sha256:a7c28b45d9f99102fa092bb213aa12e0aaf9a6a1f5e395d36166639c1f96c3a1", - "sha256:aa7dd4a6a427aed7df6fb7f08a580d68d9b118d90310374716ae90b710280af1", - "sha256:bc558586e6045763782014934bfaf39d48b8ae85a2713117d16c39864085c613", - "sha256:d46d7982b62e0729ad0175a9bc7e10a566fc07b224d2c79fafb5e032727eaa04", - "sha256:d5eef459e30b09f5a098b9cea68bebfeb268697f78d647bd255a085371ac7f3f", - "sha256:e01d3203230e1786cd91ccfdc8f8454c8069c91bee3962ad93b87a4b2860f537", - "sha256:e170a9e6fcfd19021dd29845af83bb79236068bf5fd4df3327c1be18182b2531" + "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf", + "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696", + "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393", + "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77", + "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922", + "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5", + "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8", + "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10", + "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc", + "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018", + "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e", + "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253", + "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347", + "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183", + "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541", + "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb", + "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185", + "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc", + "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db", + "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa", + "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46", + "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122", + "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b", + "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63", + "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df", + "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc", + "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247", + "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6", + "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0" ], - "version": "==3.13" + "version": "==5.4.1" }, "six": { "hashes": [ - "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", - "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" + "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", + "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" ], - "version": "==1.12.0" + "version": "==1.15.0" }, "tornado": { "hashes": [ - "sha256:0662d28b1ca9f67108c7e3b77afabfb9c7e87bde174fbda78186ecedc2499a9d", - "sha256:4e5158d97583502a7e2739951553cbd88a72076f152b4b11b64b9a10c4c49409", - "sha256:732e836008c708de2e89a31cb2fa6c0e5a70cb60492bee6f1ea1047500feaf7f", - "sha256:8154ec22c450df4e06b35f131adc4f2f3a12ec85981a203301d310abf580500f", - "sha256:8e9d728c4579682e837c92fdd98036bd5cdefa1da2aaf6acf26947e6dd0c01c5", - "sha256:d4b3e5329f572f055b587efc57d29bd051589fb5a43ec8898c77a47ec2fa2bbb", - "sha256:e5f2585afccbff22390cddac29849df463b252b711aa2ce7c5f3f342a5b3b444" + "sha256:0a00ff4561e2929a2c37ce706cb8233b7907e0cdc22eab98888aca5dd3775feb", + "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c", + "sha256:1e8225a1070cd8eec59a996c43229fe8f95689cb16e552d130b9793cb570a288", + "sha256:20241b3cb4f425e971cb0a8e4ffc9b0a861530ae3c52f2b0434e6c1b57e9fd95", + "sha256:25ad220258349a12ae87ede08a7b04aca51237721f63b1808d39bdb4b2164558", + "sha256:33892118b165401f291070100d6d09359ca74addda679b60390b09f8ef325ffe", + "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791", + "sha256:3447475585bae2e77ecb832fc0300c3695516a47d46cefa0528181a34c5b9d3d", + "sha256:34ca2dac9e4d7afb0bed4677512e36a52f09caa6fded70b4e3e1c89dbd92c326", + "sha256:3e63498f680547ed24d2c71e6497f24bca791aca2fe116dbc2bd0ac7f191691b", + "sha256:548430be2740e327b3fe0201abe471f314741efcb0067ec4f2d7dcfb4825f3e4", + "sha256:6196a5c39286cc37c024cd78834fb9345e464525d8991c21e908cc046d1cc02c", + "sha256:61b32d06ae8a036a6607805e6720ef00a3c98207038444ba7fd3d169cd998910", + "sha256:6286efab1ed6e74b7028327365cf7346b1d777d63ab30e21a0f4d5b275fc17d5", + "sha256:65d98939f1a2e74b58839f8c4dab3b6b3c1ce84972ae712be02845e65391ac7c", + "sha256:66324e4e1beede9ac79e60f88de548da58b1f8ab4b2f1354d8375774f997e6c0", + "sha256:6c77c9937962577a6a76917845d06af6ab9197702a42e1346d8ae2e76b5e3675", + "sha256:70dec29e8ac485dbf57481baee40781c63e381bebea080991893cd297742b8fd", + "sha256:7250a3fa399f08ec9cb3f7b1b987955d17e044f1ade821b32e5f435130250d7f", + "sha256:748290bf9112b581c525e6e6d3820621ff020ed95af6f17fedef416b27ed564c", + "sha256:7da13da6f985aab7f6f28debab00c67ff9cbacd588e8477034c0652ac141feea", + "sha256:8f959b26f2634a091bb42241c3ed8d3cedb506e7c27b8dd5c7b9f745318ddbb6", + "sha256:9de9e5188a782be6b1ce866e8a51bc76a0fbaa0e16613823fc38e4fc2556ad05", + "sha256:a48900ecea1cbb71b8c71c620dee15b62f85f7c14189bdeee54966fbd9a0c5bd", + "sha256:b87936fd2c317b6ee08a5741ea06b9d11a6074ef4cc42e031bc6403f82a32575", + "sha256:c77da1263aa361938476f04c4b6c8916001b90b2c2fdd92d8d535e1af48fba5a", + "sha256:cb5ec8eead331e3bb4ce8066cf06d2dfef1bfb1b2a73082dfe8a161301b76e37", + "sha256:cc0ee35043162abbf717b7df924597ade8e5395e7b66d18270116f8745ceb795", + "sha256:d14d30e7f46a0476efb0deb5b61343b1526f73ebb5ed84f23dc794bdb88f9d9f", + "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32", + "sha256:d3d20ea5782ba63ed13bc2b8c291a053c8d807a8fa927d941bd718468f7b950c", + "sha256:d3f7594930c423fd9f5d1a76bee85a2c36fd8b4b16921cae7e965f22575e9c01", + "sha256:dcef026f608f678c118779cd6591c8af6e9b4155c44e0d1bc0c87c036fb8c8c4", + "sha256:e0791ac58d91ac58f694d8d2957884df8e4e2f6687cdf367ef7eb7497f79eaa2", + "sha256:e385b637ac3acaae8022e7e47dfa7b83d3620e432e3ecb9a3f7f58f150e50921", + "sha256:e519d64089b0876c7b467274468709dadf11e41d65f63bba207e04217f47c085", + "sha256:e7229e60ac41a1202444497ddde70a48d33909e484f96eb0da9baf8dc68541df", + "sha256:ed3ad863b1b40cd1d4bd21e7498329ccaece75db5a5bf58cd3c9f130843e7102", + "sha256:f0ba29bafd8e7e22920567ce0d232c26d4d47c8b5cf4ed7b562b5db39fa199c5", + "sha256:fa2ba70284fa42c2a5ecb35e322e68823288a4251f9ba9cc77be04ae15eada68", + "sha256:fba85b6cd9c39be262fcd23865652920832b61583de2a2ca907dbd8e8a8c81e5" + ], + "version": "==6.1" + }, + "typing-extensions": { + "hashes": [ + "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918", + "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c", + "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f" + ], + "markers": "python_version < '3.8'", + "version": "==3.7.4.3" + }, + "zipp": { + "hashes": [ + "sha256:3607921face881ba3e026887d8150cca609d517579abe052ac81fc5aeffdbd76", + "sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098" ], - "version": "==5.1.1" + "version": "==3.4.1" } }, "develop": {} From a2fa424e2f2bf6053a037925b0c1a87cb1b090ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 20 Mar 2021 09:42:17 +0000 Subject: [PATCH 1833/5615] Bump testcontainers from 1.15.1 to 1.15.2 in /core (#3795) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 7613ed58ccc..3b30a64c267 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -165,7 +165,7 @@ configurations.all { } dependencies { - baseline 'org.testcontainers:testcontainers:1.15.1', { + baseline 'org.testcontainers:testcontainers:1.15.2', { exclude group: "*", module: "*" } From 8965712f239ae3c449cec579264af5e79265f314 Mon Sep 17 00:00:00 2001 From: Kevin Robayna Date: Sat, 20 Mar 2021 09:53:18 +0000 Subject: [PATCH 1834/5615] Add example of mapping from file in system (#3808) Co-authored-by: Richard North --- docs/features/files.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/features/files.md b/docs/features/files.md index a74b1add134..7e5f07b3059 100644 --- a/docs/features/files.md +++ b/docs/features/files.md @@ -1,5 +1,14 @@ # Files and volumes +## File mapping + +It is possible to map a file or directory from your FileSystem into the container as a volume using `withFileSystemBind`: +```java +String pathToFile = System.getProperty("user.home") + "/.aws"; +new GenericContainer(...) + .withFileSystemBind(pathToFile, "/home/user/.aws", BindMode.READ_ONLY) +``` + ## Volume mapping It is possible to map a file or directory **on the classpath** into the container as a volume using `withClasspathResourceMapping`: From eeb7eb2c608e6eeb27c602a6061c535add8247e3 Mon Sep 17 00:00:00 2001 From: Piotr Findeisen Date: Wed, 31 Mar 2021 10:47:32 +0200 Subject: [PATCH 1835/5615] Add TrinoContainer (#3668) --- docs/modules/databases/jdbc.md | 4 +- docs/modules/databases/presto.md | 2 +- docs/modules/databases/trino.md | 88 ++++++++++++++++++ mkdocs.yml | 1 + .../containers/PrestoContainer.java | 4 + modules/trino/build.gradle | 9 ++ .../containers/TrinoContainer.java | 89 +++++++++++++++++++ .../containers/TrinoContainerProvider.java | 23 +++++ ...s.containers.JdbcDatabaseContainerProvider | 1 + .../org/testcontainers/TrinoTestImages.java | 8 ++ .../containers/TrinoContainerTest.java | 64 +++++++++++++ .../jdbc/trino/TrinoJDBCDriverTest.java | 21 +++++ modules/trino/src/test/resources/initial.sql | 2 + 13 files changed, 313 insertions(+), 3 deletions(-) create mode 100644 docs/modules/databases/trino.md create mode 100644 modules/trino/build.gradle create mode 100644 modules/trino/src/main/java/org/testcontainers/containers/TrinoContainer.java create mode 100644 modules/trino/src/main/java/org/testcontainers/containers/TrinoContainerProvider.java create mode 100644 modules/trino/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider create mode 100644 modules/trino/src/test/java/org/testcontainers/TrinoTestImages.java create mode 100644 modules/trino/src/test/java/org/testcontainers/containers/TrinoContainerTest.java create mode 100644 modules/trino/src/test/java/org/testcontainers/jdbc/trino/TrinoJDBCDriverTest.java create mode 100644 modules/trino/src/test/resources/initial.sql diff --git a/docs/modules/databases/jdbc.md b/docs/modules/databases/jdbc.md index 6853cf44c5c..fec250ffdd9 100644 --- a/docs/modules/databases/jdbc.md +++ b/docs/modules/databases/jdbc.md @@ -39,9 +39,9 @@ Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database `jdbc:tc:postgis:9.6-2.5:///databasename` -#### Using Presto +#### Using Trino -`jdbc:tc:presto:344://localhost/memory/default` +`jdbc:tc:trino:352://localhost/memory/default` ### Using a classpath init script diff --git a/docs/modules/databases/presto.md b/docs/modules/databases/presto.md index 52776d06daf..8e8a2672355 100644 --- a/docs/modules/databases/presto.md +++ b/docs/modules/databases/presto.md @@ -1,7 +1,7 @@ # Presto Module !!! note - This module is INCUBATING. While it is ready for use and operational in the current version of Testcontainers, it is possible that it may receive breaking changes in the future. See [our contributing guidelines](/contributing/#incubating-modules) for more information on our incubating modules policy. + This module is deprecated, use Trino module. See [Database containers](./index.md) for documentation and usage that is common to all database container types. diff --git a/docs/modules/databases/trino.md b/docs/modules/databases/trino.md new file mode 100644 index 00000000000..4cd26978af1 --- /dev/null +++ b/docs/modules/databases/trino.md @@ -0,0 +1,88 @@ +# Trino Module + +!!! note + This module is INCUBATING. While it is ready for use and operational in the current version of Testcontainers, it is possible that it may receive breaking changes in the future. See [our contributing guidelines](/contributing/#incubating-modules) for more information on our incubating modules policy. + +See [Database containers](./index.md) for documentation and usage that is common to all database container types. + +## Usage example + +Running Trino as a stand-in for in a test: + +```java +public class SomeTest { + + @Rule + public TrinoContainer trino = new TrinoContainer(); + + @Test + public void someTestMethod() { + String url = trino.getJdbcUrl(); + + ... create a connection and run test as normal +``` + +Trino comes with several catalogs preconfigured. Most useful ones for testing are + +* `tpch` catalog using the [Trino TPCH Connector](https://trino.io/docs/current/connector/tpch.html). + This is a read-only catalog that defines standard TPCH schema, so is available for querying without a need + to create any tables. +* `memory` catalog using the [Trino Memory Connector](https://trino.io/docs/current/connector/memory.html). + This catalog can be used for creating schemas and tables and does not require any storage, as everything + is stored fully in-memory. + +Example test using the `tpch` and `memory` catalogs: + +```java +public class SomeTest { + @Rule + public TrinoContainer trino = new TrinoContainer(); + + @Test + public void queryMemoryAndTpchConnectors() throws SQLException { + try (Connection connection = trino.createConnection(); + Statement statement = connection.createStatement()) { + // Prepare data + statement.execute("CREATE TABLE memory.default.table_with_array AS SELECT 1 id, ARRAY[1, 42, 2, 42, 4, 42] my_array"); + + // Query Trino using newly created table and a builtin connector + try (ResultSet resultSet = statement.executeQuery("" + + "SELECT nationkey, element " + + "FROM tpch.tiny.nation " + + "JOIN memory.default.table_with_array twa ON nationkey = twa.id " + + "LEFT JOIN UNNEST(my_array) a(element) ON true " + + "ORDER BY element OFFSET 1 FETCH NEXT 3 ROWS WITH TIES ")) { + List actualElements = new ArrayList<>(); + while (resultSet.next()) { + actualElements.add(resultSet.getInt("element")); + } + Assert.assertEquals(Arrays.asList(2, 4, 42, 42, 42), actualElements); + } + } + } +} +``` + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:trino:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + trino + {{latest_version}} + test + +``` + +!!! hint + Adding this Testcontainers library JAR will not automatically add the Trino JDBC driver JAR to your project. + You should ensure that your project has the Trino JDBC driver as a dependency, if you plan on using it. + Refer to [Trino project download page](https://trino.io/download.html) for instructions. + + diff --git a/mkdocs.yml b/mkdocs.yml index b0608746825..15cc915f5b2 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -60,6 +60,7 @@ nav: - modules/databases/orientdb.md - modules/databases/postgres.md - modules/databases/presto.md + - modules/databases/trino.md - modules/docker_compose.md - modules/elasticsearch.md - modules/gcloud.md diff --git a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java index 1d9e02114ac..30d77c1a469 100644 --- a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java +++ b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java @@ -14,6 +14,10 @@ import static java.lang.String.format; import static java.time.temporal.ChronoUnit.SECONDS; +/** + * @deprecated Use {@code TrinoContainer} instead. + */ +@Deprecated public class PrestoContainer> extends JdbcDatabaseContainer { public static final String NAME = "presto"; private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("ghcr.io/trinodb/presto"); diff --git a/modules/trino/build.gradle b/modules/trino/build.gradle new file mode 100644 index 00000000000..d726cbc8e76 --- /dev/null +++ b/modules/trino/build.gradle @@ -0,0 +1,9 @@ +description = "Testcontainers :: JDBC :: Trino" + +dependencies { + compile project(':jdbc') + + testCompile project(':jdbc-test') + testCompile 'io.trino:trino-jdbc:352' + compileOnly 'org.jetbrains:annotations:20.0.0' +} diff --git a/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainer.java b/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainer.java new file mode 100644 index 00000000000..825340459f3 --- /dev/null +++ b/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainer.java @@ -0,0 +1,89 @@ +package org.testcontainers.containers; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.VisibleForTesting; +import org.testcontainers.utility.DockerImageName; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.HashSet; +import java.util.Set; + +import static com.google.common.base.Strings.nullToEmpty; +import static java.lang.String.format; + +public class TrinoContainer extends JdbcDatabaseContainer { + static final String NAME = "trino"; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("trinodb/trino"); + static final String IMAGE = "trinodb/trino"; + @VisibleForTesting + static final String DEFAULT_TAG = "352"; + + private static final int TRINO_PORT = 8080; + + private String username = "test"; + private String catalog = null; + + public TrinoContainer(final String dockerImageName) { + this(DockerImageName.parse(dockerImageName)); + } + + public TrinoContainer(final DockerImageName dockerImageName) { + super(dockerImageName); + + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + addExposedPort(TRINO_PORT); + } + + @NotNull + @Override + protected Set getLivenessCheckPorts() { + return new HashSet<>(getMappedPort(TRINO_PORT)); + } + + @Override + public String getDriverClassName() { + return "io.trino.jdbc.TrinoDriver"; + } + + @Override + public String getJdbcUrl() { + return format("jdbc:trino://%s:%s/%s", getHost(), getMappedPort(TRINO_PORT), nullToEmpty(catalog)); + } + + @Override + public String getUsername() { + return username; + } + + @Override + public String getPassword() { + return ""; + } + + @Override + public String getDatabaseName() { + return catalog; + } + + @Override + public String getTestQueryString() { + return "SELECT count(*) FROM tpch.tiny.nation"; + } + + @Override + public TrinoContainer withUsername(final String username) { + this.username = username; + return this; + } + + @Override + public TrinoContainer withDatabaseName(String dbName) { + this.catalog = dbName; + return this; + } + + public Connection createConnection() throws SQLException, NoDriverFoundException { + return createConnection(""); + } +} diff --git a/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainerProvider.java b/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainerProvider.java new file mode 100644 index 00000000000..233fc1b9b07 --- /dev/null +++ b/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainerProvider.java @@ -0,0 +1,23 @@ +package org.testcontainers.containers; + +import org.testcontainers.utility.DockerImageName; + +/** + * Factory for Trino containers. + */ +public class TrinoContainerProvider extends JdbcDatabaseContainerProvider { + @Override + public boolean supports(String databaseType) { + return databaseType.equals(TrinoContainer.NAME); + } + + @Override + public JdbcDatabaseContainer newInstance() { + return newInstance(TrinoContainer.DEFAULT_TAG); + } + + @Override + public JdbcDatabaseContainer newInstance(String tag) { + return new TrinoContainer(DockerImageName.parse(TrinoContainer.IMAGE).withTag(tag)); + } +} diff --git a/modules/trino/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider b/modules/trino/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider new file mode 100644 index 00000000000..637633627e5 --- /dev/null +++ b/modules/trino/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider @@ -0,0 +1 @@ +org.testcontainers.containers.TrinoContainerProvider diff --git a/modules/trino/src/test/java/org/testcontainers/TrinoTestImages.java b/modules/trino/src/test/java/org/testcontainers/TrinoTestImages.java new file mode 100644 index 00000000000..0ac56e54129 --- /dev/null +++ b/modules/trino/src/test/java/org/testcontainers/TrinoTestImages.java @@ -0,0 +1,8 @@ +package org.testcontainers; + +import org.testcontainers.utility.DockerImageName; + +public interface TrinoTestImages { + DockerImageName TRINO_TEST_IMAGE = DockerImageName.parse("trinodb/trino:352"); + DockerImageName TRINO_PREVIOUS_VERSION_TEST_IMAGE = DockerImageName.parse("trinodb/trino:351"); +} diff --git a/modules/trino/src/test/java/org/testcontainers/containers/TrinoContainerTest.java b/modules/trino/src/test/java/org/testcontainers/containers/TrinoContainerTest.java new file mode 100644 index 00000000000..d7f8014ca9a --- /dev/null +++ b/modules/trino/src/test/java/org/testcontainers/containers/TrinoContainerTest.java @@ -0,0 +1,64 @@ +package org.testcontainers.containers; + +import org.junit.Assert; +import org.junit.Test; +import org.testcontainers.TrinoTestImages; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static java.lang.Integer.parseInt; +import static java.lang.String.format; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class TrinoContainerTest { + + @Test + public void testSimple() throws Exception { + try (TrinoContainer trino = new TrinoContainer(TrinoTestImages.TRINO_TEST_IMAGE)) { + trino.start(); + try (Connection connection = trino.createConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes")) { + assertTrue("No result", resultSet.next()); + assertEquals("Trino version", TrinoContainer.DEFAULT_TAG, resultSet.getString("node_version")); + } + } + } + + @Test + public void testSpecificVersion() throws Exception { + try (TrinoContainer trino = new TrinoContainer(TrinoTestImages.TRINO_PREVIOUS_VERSION_TEST_IMAGE)) { + trino.start(); + try (Connection connection = trino.createConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes")) { + assertTrue("No result", resultSet.next()); + assertEquals("Trino version", TrinoTestImages.TRINO_PREVIOUS_VERSION_TEST_IMAGE.getVersionPart(), resultSet.getString("node_version")); + } + } + } + + @Test + public void testInitScript() throws Exception { + try (TrinoContainer trino = new TrinoContainer(TrinoTestImages.TRINO_TEST_IMAGE)) { + trino.withInitScript("initial.sql"); + trino.start(); + try (Connection connection = trino.createConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT a FROM memory.default.test_table")) { + assertTrue("No result", resultSet.next()); + assertEquals("Value", 12345678909324L, resultSet.getObject("a")); + assertFalse("Too many result", resultSet.next()); + } + } + } +} diff --git a/modules/trino/src/test/java/org/testcontainers/jdbc/trino/TrinoJDBCDriverTest.java b/modules/trino/src/test/java/org/testcontainers/jdbc/trino/TrinoJDBCDriverTest.java new file mode 100644 index 00000000000..8f06aef7278 --- /dev/null +++ b/modules/trino/src/test/java/org/testcontainers/jdbc/trino/TrinoJDBCDriverTest.java @@ -0,0 +1,21 @@ +package org.testcontainers.jdbc.trino; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.testcontainers.jdbc.AbstractJDBCDriverTest; + +import java.util.EnumSet; + +import static java.util.Arrays.asList; + +@RunWith(Parameterized.class) +public class TrinoJDBCDriverTest extends AbstractJDBCDriverTest { + + @Parameterized.Parameters(name = "{index} - {0}") + public static Iterable data() { + return asList( + new Object[][]{ + {"jdbc:tc:trino:352://hostname/", EnumSet.of(Options.PmdKnownBroken)}, + }); + } +} diff --git a/modules/trino/src/test/resources/initial.sql b/modules/trino/src/test/resources/initial.sql new file mode 100644 index 00000000000..1a3ecdab4f9 --- /dev/null +++ b/modules/trino/src/test/resources/initial.sql @@ -0,0 +1,2 @@ +CREATE TABLE memory.default.test_table(a bigint); +INSERT INTO memory.default.test_table(a) VALUES (12345678909324); From 8929dd72947ac102b78e8c51b19821299c2bc01e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Apr 2021 18:26:18 +0100 Subject: [PATCH 1836/5615] Bump release-drafter/release-drafter from e5ccf147077e46b0225a80bbe314d795d77bb7a2 to 5.15.0 (#3915) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- .github/workflows/release-drafter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 28f14cb0ad3..e19e08b9f32 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -9,6 +9,6 @@ jobs: update_release_draft: runs-on: ubuntu-latest steps: - - uses: release-drafter/release-drafter@e5ccf147077e46b0225a80bbe314d795d77bb7a2 # v5.14.0 + - uses: release-drafter/release-drafter@fe52e97d262833ae07d05efaf1a239df3f1b5cd4 # v5.15.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 736459168892245bd16727a6a97fd19d6ad846c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Apr 2021 18:27:15 +0100 Subject: [PATCH 1837/5615] Bump org.jetbrains.kotlin.jvm from 1.4.21-2 to 1.4.32 in /examples (#3947) Bumps org.jetbrains.kotlin.jvm from 1.4.21-2 to 1.4.32. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 4675f907235..8ffa1b46d2a 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,6 +1,6 @@ plugins { id("org.springframework.boot") version "2.4.1" - id("org.jetbrains.kotlin.jvm") version "1.4.21-2" + id("org.jetbrains.kotlin.jvm") version "1.4.32" id("org.jetbrains.kotlin.plugin.spring") version "1.4.21-2" } From 187c0cce8896dbc3ca683f787611d8fd11039a45 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Apr 2021 18:27:47 +0100 Subject: [PATCH 1838/5615] Bump elasticsearch-rest-client from 7.10.1 to 7.12.0 in /modules/elasticsearch (#3945) Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 7.10.1 to 7.12.0. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.10.1...v7.12.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index b4a64a405b7..cb553c54654 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,6 +2,6 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') - testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.10.1" + testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.12.0" testCompile "org.elasticsearch.client:transport:7.10.1" } From 79a1a0650eabfa7535607b81a752aae6739b2a5b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Apr 2021 18:28:33 +0100 Subject: [PATCH 1839/5615] Bump aws-java-sdk-sqs from 1.11.930 to 1.11.986 in /modules/localstack (#3942) Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.11.930 to 1.11.986. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.930...1.11.986) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index fdffc649a72..8a4df52b54d 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.930' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.929' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.930' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.986' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' testCompile 'software.amazon.awssdk:s3:2.15.56' } From bc48eebeefb344b0878637c021b8a1998ff4d677 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Apr 2021 18:29:12 +0100 Subject: [PATCH 1840/5615] Bump pulsar-client from 2.7.0 to 2.7.1 in /modules/pulsar (#3913) Bumps [pulsar-client](https://github.com/apache/pulsar) from 2.7.0 to 2.7.1. - [Release notes](https://github.com/apache/pulsar/releases) - [Commits](https://github.com/apache/pulsar/compare/v2.7.0...v2.7.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index b574519a939..e189e7006eb 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Pulsar" dependencies { compile project(':testcontainers') - testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.7.0' + testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.7.1' testCompile group: 'org.assertj', name: 'assertj-core', version: '3.18.1' testCompile group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.7.0' } From b3cf0624c8f251af0e5361d3a92d155d3563b372 Mon Sep 17 00:00:00 2001 From: Patrik Mihalcin <5480557+pmihalcin@users.noreply.github.com> Date: Wed, 7 Apr 2021 16:44:24 +0200 Subject: [PATCH 1841/5615] Add information for TLS disabled mode in Gitlab (#2466) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Docker daemon supports connection over TLS and it’s done by default for Docker 19.03.1 or higher. We need to instruct docker to not start over TLS with DOCKER_TLS_CERTDIR variable Co-authored-by: Richard North --- .../continuous_integration/gitlab_ci.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/supported_docker_environment/continuous_integration/gitlab_ci.md b/docs/supported_docker_environment/continuous_integration/gitlab_ci.md index 0cb5017bb87..e6daf84401f 100644 --- a/docs/supported_docker_environment/continuous_integration/gitlab_ci.md +++ b/docs/supported_docker_environment/continuous_integration/gitlab_ci.md @@ -1,7 +1,7 @@ # GitLab CI In order to use Testcontainers in a Gitlab CI pipeline, you need to run the job as a Docker container (see [Patterns for running inside Docker](dind_patterns.md)). -So edit your `.gitlab-ci.yml` to include the [Docker-In-Docker service](https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#use-docker-in-docker-executor) (`docker:dind`) and set the `DOCKER_HOST` variable to `tcp://docker:2375`. +So edit your `.gitlab-ci.yml` to include the [Docker-In-Docker service](https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#use-docker-in-docker-workflow-with-docker-executor) (`docker:dind`) and set the `DOCKER_HOST` variable to `tcp://docker:2375` and `DOCKER_TLS_CERTDIR` to empty string. Here is a sample `.gitlab-ci.yml` that executes test with gradle: @@ -13,6 +13,8 @@ services: variables: # Instruct Testcontainers to use the daemon of DinD. DOCKER_HOST: "tcp://docker:2375" + # Instruct Docker not to start over TLS. + DOCKER_TLS_CERTDIR: "" # Improve performance with overlayfs. DOCKER_DRIVER: overlay2 From 8533e9bd190d77c6df2bf7837e9ffb1786e83da8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 11 Apr 2021 15:51:57 +0100 Subject: [PATCH 1842/5615] Bump s3 from 2.15.56 to 2.16.34 in /modules/localstack (#3963) Bumps s3 from 2.15.56 to 2.16.34. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 8a4df52b54d..c007b2c649e 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,5 +7,5 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.929' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.986' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' - testCompile 'software.amazon.awssdk:s3:2.15.56' + testCompile 'software.amazon.awssdk:s3:2.16.34' } From 1e6deb167491c0f62a21dac403ea13ee07d92776 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 11 Apr 2021 19:54:20 +0100 Subject: [PATCH 1843/5615] Filter mapped ports when port numbers are not specified (replaces #3948) (#3979) Co-authored-by: Ilia Rodionov --- .../containers/ContainerState.java | 3 ++ .../containers/ContainerStateTest.java | 49 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 core/src/test/java/org/testcontainers/containers/ContainerStateTest.java diff --git a/core/src/main/java/org/testcontainers/containers/ContainerState.java b/core/src/main/java/org/testcontainers/containers/ContainerState.java index ad96aeb808c..da87dbdf724 100644 --- a/core/src/main/java/org/testcontainers/containers/ContainerState.java +++ b/core/src/main/java/org/testcontainers/containers/ContainerState.java @@ -12,6 +12,7 @@ import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.apache.commons.compress.utils.IOUtils; +import org.apache.commons.lang.math.NumberUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testcontainers.DockerClientFactory; @@ -181,7 +182,9 @@ default List getBoundPortNumbers() { .map(PortBinding::getBinding) .map(Ports.Binding::getHostPortSpec) .filter(Objects::nonNull) + .filter(NumberUtils::isNumber) .map(Integer::valueOf) + .filter(port -> port > 0) .collect(Collectors.toList()); } diff --git a/core/src/test/java/org/testcontainers/containers/ContainerStateTest.java b/core/src/test/java/org/testcontainers/containers/ContainerStateTest.java new file mode 100644 index 00000000000..52296678fd4 --- /dev/null +++ b/core/src/test/java/org/testcontainers/containers/ContainerStateTest.java @@ -0,0 +1,49 @@ +package org.testcontainers.containers; + +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.List; + +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.mockito.Mockito.doCallRealMethod; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(Parameterized.class) +public class ContainerStateTest { + + @Parameterized.Parameters(name = "{0} ({1} -> {2})") + public static Object[][] params() { + return new Object[][]{ + new Object[]{"regular mapping", "80:8080/tcp", singletonList(80),}, + new Object[]{"regular mapping with host", "127.0.0.1:80:8080/tcp", singletonList(80),}, + new Object[]{"zero port without host", ":0:8080/tcp", emptyList(),}, + new Object[]{"missing port with host", "0.0.0.0:0:8080/tcp", emptyList(),}, + new Object[]{"zero port (synthetic case)", "0:8080/tcp", emptyList(),}, + new Object[]{"missing port", ":8080/tcp", emptyList(), + } + }; + } + + @Parameterized.Parameter(0) + public String name; + @Parameterized.Parameter(1) + public String testSet; + @Parameterized.Parameter(2) + public List expectedResult; + + @Test + public void test() { + ContainerState containerState = mock(ContainerState.class); + doCallRealMethod().when(containerState).getBoundPortNumbers(); + + when(containerState.getPortBindings()).thenReturn(singletonList(testSet)); + + List result = containerState.getBoundPortNumbers(); + Assertions.assertThat(result).hasSameElementsAs(expectedResult); + } +} From 9183f8f940e12045e8790c6eb6693cf8c62bd779 Mon Sep 17 00:00:00 2001 From: Mark Rotteveel Date: Sun, 11 Apr 2021 20:57:41 +0200 Subject: [PATCH 1844/5615] Prevent ContainerDatabaseDriver from throwing NPEs when on classpath but not initialized (#3976) When ContainerDatabaseDriver hasn't been used yet to connect, the delegate is null. This causes various JDBC metadata methods of the driver to fail with a NullPointerException. Prevent this by providing reasonable defaults. --- .../jdbc/ContainerDatabaseDriver.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java index c5413fe7efb..c1b4fc29067 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java @@ -230,27 +230,30 @@ private void runInitFunctionIfRequired(final ConnectionUrl connectionUrl, Connec @Override public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException { - return delegate.getPropertyInfo(url, info); + return delegate != null ? delegate.getPropertyInfo(url, info) : new DriverPropertyInfo[0]; } @Override public int getMajorVersion() { - return delegate.getMajorVersion(); + return delegate != null ? delegate.getMajorVersion() : 1; } @Override public int getMinorVersion() { - return delegate.getMinorVersion(); + return delegate != null ? delegate.getMinorVersion() : 0; } @Override public boolean jdbcCompliant() { - return delegate.jdbcCompliant(); + return delegate != null && delegate.jdbcCompliant(); } @Override public Logger getParentLogger() throws SQLFeatureNotSupportedException { - return delegate.getParentLogger(); + if (delegate != null) { + return delegate.getParentLogger(); + } + throw new SQLFeatureNotSupportedException("getParentLogger not supported"); } /** From f3ffed66683034e6cde10d62978eb8c807736e06 Mon Sep 17 00:00:00 2001 From: Ivan Stanislavciuc <2766429+seveneves@users.noreply.github.com> Date: Sun, 11 Apr 2021 21:00:52 +0200 Subject: [PATCH 1845/5615] Fix problem of starting up mysql 5.7.33 version when user is root (#3953) Co-authored-by: Richard North Co-authored-by: Ivan Stanislavciuc --- .../containers/MySQLContainer.java | 10 ++-- .../containers/MySQLRootAccountTest.java | 53 +++++++++++++++++++ 2 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 modules/mysql/src/test/java/org/testcontainers/containers/MySQLRootAccountTest.java diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java index 1dc235e6536..a126a579489 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java @@ -62,10 +62,12 @@ protected Set getLivenessCheckPorts() { @Override protected void configure() { optionallyMapResourceParameterAsVolume(MY_CNF_CONFIG_OVERRIDE_PARAM_NAME, "/etc/mysql/conf.d", - "mysql-default-conf"); + "mysql-default-conf"); addEnv("MYSQL_DATABASE", databaseName); - addEnv("MYSQL_USER", username); + if (!MYSQL_ROOT_USER.equalsIgnoreCase(username)) { + addEnv("MYSQL_USER", username); + } if (password != null && !password.isEmpty()) { addEnv("MYSQL_PASSWORD", password); addEnv("MYSQL_ROOT_PASSWORD", password); @@ -98,12 +100,12 @@ public String getJdbcUrl() { protected String constructUrlForConnection(String queryString) { String url = super.constructUrlForConnection(queryString); - if (! url.contains("useSSL=")) { + if (!url.contains("useSSL=")) { String separator = url.contains("?") ? "&" : "?"; url = url + separator + "useSSL=false"; } - if (! url.contains("allowPublicKeyRetrieval=")) { + if (!url.contains("allowPublicKeyRetrieval=")) { url = url + "&allowPublicKeyRetrieval=true"; } diff --git a/modules/mysql/src/test/java/org/testcontainers/containers/MySQLRootAccountTest.java b/modules/mysql/src/test/java/org/testcontainers/containers/MySQLRootAccountTest.java new file mode 100644 index 00000000000..c437acac013 --- /dev/null +++ b/modules/mysql/src/test/java/org/testcontainers/containers/MySQLRootAccountTest.java @@ -0,0 +1,53 @@ +package org.testcontainers.containers; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.testcontainers.containers.output.Slf4jLogConsumer; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +@Slf4j +@RunWith(Parameterized.class) +public class MySQLRootAccountTest { + + @Parameterized.Parameters(name = "{0}") + public static String[] params() { + return new String[]{ + "mysql:8", + "mysql:5" + }; + } + + @Parameterized.Parameter() + public String image; + + @Test + public void testRootAccountUsageWithDefaultPassword() throws SQLException { + testWithDB(new MySQLContainer<>(image).withUsername("root")); + } + + @Test + public void testRootAccountUsageWithEmptyPassword() throws SQLException { + testWithDB(new MySQLContainer<>(image).withUsername("root").withPassword("")); + } + + @Test + public void testRootAccountUsageWithCustomPassword() throws SQLException { + testWithDB(new MySQLContainer<>(image).withUsername("root").withPassword("not-default")); + } + + private void testWithDB(MySQLContainer db) throws SQLException { + try { + db.withLogConsumer(new Slf4jLogConsumer(log)).start(); + Connection connection = DriverManager.getConnection(db.getJdbcUrl(), db.getUsername(), db.getPassword()); + connection.createStatement().execute("SELECT 1"); + connection.createStatement().execute("set sql_log_bin=0"); // requires root + } finally { + db.close(); + } + } +} From 3c70d0895851193ca5f3b1ec1c4b095871ed4f62 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 11 Apr 2021 21:06:40 +0200 Subject: [PATCH 1846/5615] Update docs to clarify shading approach (#3974) Make the docs section about our shading approach and motivation more up-to-date and less confusing (no JAX-RS/Jersey anymore, mention docker-java-api, etc) --- docs/index.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/index.md b/docs/index.md index 013c18d0700..176ddd16974 100644 --- a/docs/index.md +++ b/docs/index.md @@ -86,8 +86,9 @@ testImplementation('org.testcontainers:mysql') //no version specified [JitPack](jitpack_dependencies.md) builds are available for pre-release versions. !!! warning "Shaded dependencies" - Testcontainers uses the docker-java client library, which in turn depends on JAX-RS, Jersey and Jackson libraries. - These libraries in particular seem to be especially prone to conflicts with test code/application under test code. + Testcontainers depends on other libraries (like docker-java) for it to work. + Some of them (JUnit, docker-java-{api,transport} and its transitive dependencies, JNA, visible-assertions and others) are part of our public API. + But there are also "private", implementation detail dependencies (e.g. docker-java-core, Guava, OkHttp, etc etc) that are not exposed to public API but prone to conflicts with test code/application under test code. As such, **these libraries are 'shaded' into the core testcontainers JAR** and relocated under `org.testcontainers.shaded` to prevent class conflicts. ## Sponsors From 33477107ecd5610eaa25cf8c9cef449f07ac321a Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 12 Apr 2021 07:39:28 +0100 Subject: [PATCH 1847/5615] Pin localstack to 0.12.8 (#3981) --- .../containers/localstack/LegacyModeTest.java | 117 ++++++++---------- .../localstack/LocalstackTestImages.java | 6 +- 2 files changed, 59 insertions(+), 64 deletions(-) diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java index f50846b091f..b8f2ec1aae0 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java @@ -1,30 +1,31 @@ package org.testcontainers.containers.localstack; +import com.github.dockerjava.api.DockerClient; import lombok.AllArgsConstructor; -import lombok.SneakyThrows; -import org.junit.After; import org.junit.BeforeClass; import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import org.testcontainers.DockerClientFactory; +import org.testcontainers.utility.DockerImageName; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.util.Arrays; -import java.util.function.Consumer; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; import static org.testcontainers.containers.localstack.LocalStackContainer.Service.S3; import static org.testcontainers.containers.localstack.LocalStackContainer.Service.SQS; +import static org.testcontainers.containers.localstack.LocalstackTestImages.LOCALSTACK_0_10_IMAGE; +import static org.testcontainers.containers.localstack.LocalstackTestImages.LOCALSTACK_0_11_IMAGE; +import static org.testcontainers.containers.localstack.LocalstackTestImages.LOCALSTACK_0_12_IMAGE; +import static org.testcontainers.containers.localstack.LocalstackTestImages.LOCALSTACK_0_7_IMAGE; import static org.testcontainers.containers.localstack.LocalstackTestImages.LOCALSTACK_IMAGE; @RunWith(Enclosed.class) public class LegacyModeTest { + private static DockerImageName LOCALSTACK_CUSTOM_TAG = LOCALSTACK_IMAGE.withTag("custom"); @RunWith(Parameterized.class) @AllArgsConstructor @@ -35,10 +36,9 @@ public static class Off { @Parameterized.Parameters(name = "{0}") public static Iterable constructors() { return Arrays.asList(new Object[][]{ - {"default constructor", new LocalStackContainer(LOCALSTACK_IMAGE)}, - {"latest", new LocalStackContainer(LOCALSTACK_IMAGE.withTag("latest"))}, - {"0.11.1", new LocalStackContainer(LOCALSTACK_IMAGE.withTag("0.11.1"))}, - {"0.7.0 with legacy = off", new LocalStackContainer(LOCALSTACK_IMAGE.withTag("0.7.0"), false)} + {"0.12", new LocalStackContainer(LOCALSTACK_0_12_IMAGE)}, + {"0.11", new LocalStackContainer(LOCALSTACK_0_11_IMAGE)}, + {"0.7 with legacy = off", new LocalStackContainer(LOCALSTACK_0_7_IMAGE, false)} }); } @@ -47,20 +47,19 @@ public void samePortIsExposedForAllServices() { localstack.withServices(S3, SQS); localstack.start(); - assertTrue("A single port is exposed", localstack.getExposedPorts().size() == 1); - assertEquals( - "Endpoint overrides are different", - localstack.getEndpointOverride(S3).toString(), - localstack.getEndpointOverride(SQS).toString()); - assertEquals( - "Endpoint configuration have different endpoints", - localstack.getEndpointConfiguration(S3).getServiceEndpoint(), - localstack.getEndpointConfiguration(SQS).getServiceEndpoint()); - } - - @After - public void cleanup() { - if (localstack != null) localstack.stop(); + try { + assertTrue("A single port is exposed", localstack.getExposedPorts().size() == 1); + assertEquals( + "Endpoint overrides are different", + localstack.getEndpointOverride(S3).toString(), + localstack.getEndpointOverride(SQS).toString()); + assertEquals( + "Endpoint configuration have different endpoints", + localstack.getEndpointConfiguration(S3).getServiceEndpoint(), + localstack.getEndpointConfiguration(SQS).getServiceEndpoint()); + } finally { + localstack.stop(); + } } } @@ -72,16 +71,28 @@ public static class On { @BeforeClass public static void createCustomTag() { - run("docker pull localstack/localstack:latest"); - run("docker tag localstack/localstack:latest localstack/localstack:custom"); + DockerClient dockerClient = DockerClientFactory.instance().client(); + DockerClientFactory + .instance() + .checkAndPullImage( + dockerClient, + LOCALSTACK_0_12_IMAGE.asCanonicalNameString() + ); + dockerClient + .tagImageCmd( + LOCALSTACK_0_12_IMAGE.asCanonicalNameString(), + LOCALSTACK_CUSTOM_TAG.getRepository(), + LOCALSTACK_CUSTOM_TAG.getVersionPart() + ) + .exec(); } @Parameterized.Parameters(name = "{0}") public static Iterable constructors() { return Arrays.asList(new Object[][]{ - {"0.10.7", new LocalStackContainer(LOCALSTACK_IMAGE.withTag("0.10.7"))}, - {"custom", new LocalStackContainer(LOCALSTACK_IMAGE.withTag("custom"))}, - {"0.11.1 with legacy = on", new LocalStackContainer(LOCALSTACK_IMAGE.withTag("0.11.1"), true)} + {"0.10", new LocalStackContainer(LOCALSTACK_0_10_IMAGE)}, + {"custom", new LocalStackContainer(LOCALSTACK_CUSTOM_TAG)}, + {"0.11 with legacy = on", new LocalStackContainer(LOCALSTACK_0_11_IMAGE, true)} }); } @@ -90,39 +101,19 @@ public void differentPortsAreExposed() { localstack.withServices(S3, SQS); localstack.start(); - assertTrue("Multiple ports are exposed", localstack.getExposedPorts().size() > 1); - assertNotEquals( - "Endpoint overrides are different", - localstack.getEndpointOverride(S3).toString(), - localstack.getEndpointOverride(SQS).toString()); - assertNotEquals( - "Endpoint configuration have different endpoints", - localstack.getEndpointConfiguration(S3).getServiceEndpoint(), - localstack.getEndpointConfiguration(SQS).getServiceEndpoint()); - } - - @After - public void cleanup() { - if (localstack != null) localstack.stop(); - } - } - - @SneakyThrows - private static void run(String command) { - Process process = Runtime.getRuntime().exec(command); - join(process.getInputStream(), System.out::println); - join(process.getErrorStream(), System.err::println); - process.waitFor(); - if (process.exitValue() != 0) - throw new RuntimeException("Failed to execute " + command); - } - - private static void join(InputStream stream, Consumer logger) throws IOException { - BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(stream)); - String line; - while ((line = bufferedReader.readLine()) != null) { - logger.accept(line); + try { + assertTrue("Multiple ports are exposed", localstack.getExposedPorts().size() > 1); + assertNotEquals( + "Endpoint overrides are different", + localstack.getEndpointOverride(S3).toString(), + localstack.getEndpointOverride(SQS).toString()); + assertNotEquals( + "Endpoint configuration have different endpoints", + localstack.getEndpointConfiguration(S3).getServiceEndpoint(), + localstack.getEndpointConfiguration(SQS).getServiceEndpoint()); + } finally { + localstack.stop(); + } } } - } diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackTestImages.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackTestImages.java index 0dd0ca9b9ac..491394bea8a 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackTestImages.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackTestImages.java @@ -3,6 +3,10 @@ import org.testcontainers.utility.DockerImageName; public interface LocalstackTestImages { - DockerImageName LOCALSTACK_IMAGE = DockerImageName.parse("localstack/localstack:0.11.3"); + DockerImageName LOCALSTACK_IMAGE = DockerImageName.parse("localstack/localstack:0.12.8"); + DockerImageName LOCALSTACK_0_7_IMAGE = LOCALSTACK_IMAGE.withTag("0.7.0"); + DockerImageName LOCALSTACK_0_10_IMAGE = LOCALSTACK_IMAGE.withTag("0.10.7"); + DockerImageName LOCALSTACK_0_11_IMAGE = LOCALSTACK_IMAGE.withTag("0.11.3"); + DockerImageName LOCALSTACK_0_12_IMAGE = LOCALSTACK_IMAGE.withTag("0.12.8"); DockerImageName AWS_CLI_IMAGE = DockerImageName.parse("atlassian/pipelines-awscli:1.16.302"); } From 1cd04763de83b4fa523182bae55d8927d99990f8 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 12 Apr 2021 08:44:27 +0200 Subject: [PATCH 1848/5615] Update docker-java to 3.2.8 (#3980) --- core/build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index 3b30a64c267..463116dc5ee 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -180,9 +180,9 @@ dependencies { compile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' - compile "com.github.docker-java:docker-java-api:3.2.7" + compile "com.github.docker-java:docker-java-api:3.2.8" - shaded ('com.github.docker-java:docker-java-core:3.2.7') { + shaded ('com.github.docker-java:docker-java-core:3.2.8') { exclude(group: 'com.github.docker-java', module: 'docker-java-api') exclude(group: 'com.github.docker-java', module: 'docker-java-transport') exclude(group: 'com.fasterxml.jackson.core', module: 'jackson-annotations') @@ -191,13 +191,13 @@ dependencies { exclude(group: 'org.apache.commons', module: 'commons-compress') } - shaded ('com.github.docker-java:docker-java-transport-okhttp:3.2.7') { + shaded ('com.github.docker-java:docker-java-transport-okhttp:3.2.8') { exclude(group: 'com.github.docker-java', module: 'docker-java-core') exclude(group: 'net.java.dev.jna') exclude(group: 'org.slf4j') } - compile 'com.github.docker-java:docker-java-transport-zerodep:3.2.7' + compile 'com.github.docker-java:docker-java-transport-zerodep:3.2.8' shaded "org.yaml:snakeyaml:1.27" From 55838a9248a55ad628e6159fef5e4c0e6beed981 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cristopher=20Pinz=C3=B3n?= Date: Mon, 12 Apr 2021 01:45:32 -0500 Subject: [PATCH 1849/5615] ec2 added to localstack services enum (#3955) --- .../containers/localstack/LocalStackContainer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java index b8416afb1ed..4863de6e62d 100644 --- a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java +++ b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java @@ -306,6 +306,7 @@ public String getRegion() { @FieldDefaults(makeFinal = true) public enum Service { API_GATEWAY("apigateway", 4567), + EC2("ec2", 4597), KINESIS("kinesis", 4568), DYNAMODB("dynamodb", 4569), DYNAMODB_STREAMS("dynamodbstreams", 4570), From dabc99c73eb9832ed2971c42412dd56bbd4d15be Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 12 Apr 2021 13:36:29 +0200 Subject: [PATCH 1850/5615] Make it possible to provide custom LocalStack services (#3995) --- .../localstack/LocalStackContainer.java | 40 +++++++++++++++---- .../localstack/LocalstackContainerTest.java | 2 +- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java index 4863de6e62d..fe3ea6ab34f 100644 --- a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java +++ b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java @@ -37,7 +37,7 @@ public class LocalStackContainer extends GenericContainer { static final int PORT = 4566; private static final String HOSTNAME_EXTERNAL_ENV_VAR = "HOSTNAME_EXTERNAL"; - private final List services = new ArrayList<>(); + private final List services = new ArrayList<>(); private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("localstack/localstack"); private static final String DEFAULT_TAG = "0.11.2"; @@ -119,7 +119,7 @@ protected void configure() { Preconditions.check("services list must not be empty", !services.isEmpty()); - withEnv("SERVICES", services.stream().map(Service::getLocalStackName).collect(Collectors.joining(","))); + withEnv("SERVICES", services.stream().map(EnabledService::getName).collect(Collectors.joining(","))); String hostnameExternalReason; if (getEnvMap().containsKey(HOSTNAME_EXTERNAL_ENV_VAR)) { @@ -144,12 +144,17 @@ private void exposePorts() { .forEach(this::addExposedPort); } + public LocalStackContainer withServices(Service... services) { + this.services.addAll(Arrays.asList(services)); + return self(); + } + /** * Declare a set of simulated AWS services that should be launched by this container. * @param services one or more service names * @return this container object */ - public LocalStackContainer withServices(Service... services) { + public LocalStackContainer withServices(EnabledService... services) { this.services.addAll(Arrays.asList(services)); return self(); } @@ -184,6 +189,10 @@ public AwsClientBuilder.EndpointConfiguration getEndpointConfiguration(Service s return new AwsClientBuilder.EndpointConfiguration(getEndpointOverride(service).toString(), getRegion()); } + public URI getEndpointOverride(Service service) { + return getEndpointOverride((EnabledService) service); + } + /** * Provides an endpoint override that is preconfigured to communicate with a given simulated service. * The provided endpoint override should be set in the AWS Java SDK v2 when building a client, e.g.: @@ -203,7 +212,7 @@ public AwsClientBuilder.EndpointConfiguration getEndpointConfiguration(Service s * @param service the service that is to be accessed * @return an {@link URI} endpoint override */ - public URI getEndpointOverride(Service service) { + public URI getEndpointOverride(EnabledService service) { try { final String address = getHost(); String ipAddress = address; @@ -218,8 +227,8 @@ public URI getEndpointOverride(Service service) { } } - private int getServicePort(Service service) { - return legacyMode ? service.port : PORT; + private int getServicePort(EnabledService service) { + return legacyMode ? service.getPort() : PORT; } /** @@ -301,10 +310,22 @@ public String getRegion() { return "us-east-1"; } + public interface EnabledService { + static EnabledService named(String name) { + return () -> name; + } + + String getName(); + + default int getPort() { + return PORT; + } + } + @RequiredArgsConstructor @Getter @FieldDefaults(makeFinal = true) - public enum Service { + public enum Service implements EnabledService { API_GATEWAY("apigateway", 4567), EC2("ec2", 4597), KINESIS("kinesis", 4568), @@ -335,6 +356,11 @@ public enum Service { int port; + @Override + public String getName() { + return localStackName; + } + @Deprecated /* Since version 0.11, LocalStack exposes all services on a single (4566) port. diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java index af4d0694ab6..6b32ba2c102 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java @@ -66,7 +66,7 @@ public static class WithoutNetwork { // without_network { @ClassRule public static LocalStackContainer localstack = new LocalStackContainer(LOCALSTACK_IMAGE) - .withServices(S3, SQS, CLOUDWATCHLOGS, KMS); + .withServices(S3, SQS, CLOUDWATCHLOGS, KMS, LocalStackContainer.EnabledService.named("events")); // } @Test From 3a6e77e920c06ed2157d9baf0313e529f4395650 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 20 Apr 2021 16:51:15 +0200 Subject: [PATCH 1851/5615] Update mkdocs.yml to the latest release (1.15.3) --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 15cc915f5b2..f3cb11f7fe6 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -96,4 +96,4 @@ nav: - contributing_docs.md - bounty.md extra: - latest_version: 1.15.2 + latest_version: 1.15.3 From 49a103ce6272a51c29119f19951e1d3c92d2ba1d Mon Sep 17 00:00:00 2001 From: vcvitaly Date: Sat, 24 Apr 2021 21:54:33 +0300 Subject: [PATCH 1852/5615] Improve logging in InternalCommandPortListeningCheck (#3001) --- .../wait/internal/InternalCommandPortListeningCheck.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java index cd601e6a131..a3b7a5d4dc7 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java +++ b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java @@ -41,7 +41,11 @@ public Boolean call() { try { ExecResult result = ExecInContainerPattern.execInContainer(waitStrategyTarget.getContainerInfo(), "/bin/sh", "-c", command.toString()); log.trace("Check for {} took {}. Result code '{}', stdout message: '{}'", internalPorts, Duration.between(before, Instant.now()), result.getExitCode(), result.getStdout()); - return result.getExitCode() == 0; + int exitCode = result.getExitCode(); + if (exitCode != 0 && exitCode != 1) { + log.warn("An exception while executing the internal check: {}", result); + } + return exitCode == 0; } catch (Exception e) { throw new IllegalStateException(e); } From f94737f0d7509e8a6a5f39b23df7c24585bf5dd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Torres?= Date: Thu, 29 Apr 2021 13:28:21 +0100 Subject: [PATCH 1853/5615] Docs: Add Backbase as Bronze sponsor (#4039) --- docs/index.md | 7 +++++++ docs/sponsor_logos/backbase.png | Bin 0 -> 5757 bytes 2 files changed, 7 insertions(+) create mode 100644 docs/sponsor_logos/backbase.png diff --git a/docs/index.md b/docs/index.md index 176ddd16974..45213629ed0 100644 --- a/docs/index.md +++ b/docs/index.md @@ -123,6 +123,13 @@ A huge thank you to our sponsors: +

+ ### Backers * [Philip Riecks (@rieckpil)](https://github.com/rieckpil) diff --git a/docs/sponsor_logos/backbase.png b/docs/sponsor_logos/backbase.png new file mode 100644 index 0000000000000000000000000000000000000000..22acf3aac8e93f4d09f2e4c31502f85a209da4db GIT binary patch literal 5757 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 Wn%C=%XDnxTusSdUjWTtIr~d;?(<52{ literal 0 HcmV?d00001 From 3aa5fbfa17be09f4e42b2300396890f76f508c86 Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 7 May 2021 13:09:56 +0100 Subject: [PATCH 1854/5615] Use up-to-date MySQL docker images (#4059) Co-authored-by: Sergei Egorov --- core/src/test/resources/compose-test.yml | 2 +- .../generic/ImageNameSubstitutionTest.java | 6 ++--- .../TestSpecificImageNameSubstitutor.java | 2 +- docs/features/image_name_substitution.md | 2 +- docs/modules/databases/jdbc.md | 12 +++++----- docs/modules/databases/mysql.md | 2 +- docs/modules/databases/r2dbc.md | 4 ++-- .../db/AbstractContainerDatabaseTest.java | 1 - .../jdbc/ConnectionUrlDriversTests.java | 2 +- .../jdbc/ConnectionUrlTest.java | 10 ++++---- .../jdbc/MissingJdbcDriverTest.java | 2 +- modules/mysql/build.gradle | 2 +- .../containers/MySQLContainer.java | 2 +- .../org/testcontainers/MySQLTestImages.java | 6 ++--- .../MySQLR2DBCDatabaseContainerTest.java | 4 ++-- .../containers/MySQLRootAccountTest.java | 12 ++++++---- .../jdbc/mysql/JDBCDriverWithPoolTest.java | 2 +- .../jdbc/mysql/MySQLJDBCDriverTest.java | 16 ++++++------- .../junit/mysql/CustomizableMysqlTest.java | 2 +- .../junit/mysql/MultiVersionMySQLTest.java | 24 +++++++++---------- .../junit/mysql/SimpleMySQLTest.java | 23 +++++++++--------- .../spock/SpockTestImages.groovy | 2 +- 22 files changed, 70 insertions(+), 70 deletions(-) diff --git a/core/src/test/resources/compose-test.yml b/core/src/test/resources/compose-test.yml index a79a4c0b25b..9c5537e7cab 100644 --- a/core/src/test/resources/compose-test.yml +++ b/core/src/test/resources/compose-test.yml @@ -1,6 +1,6 @@ redis: image: redis db: - image: mysql:5.7.22 + image: mysql:5.7.34 environment: MYSQL_RANDOM_ROOT_PASSWORD: "true" diff --git a/docs/examples/junit4/generic/src/test/java/generic/ImageNameSubstitutionTest.java b/docs/examples/junit4/generic/src/test/java/generic/ImageNameSubstitutionTest.java index edc622a4104..65da67fd401 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/ImageNameSubstitutionTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/ImageNameSubstitutionTest.java @@ -12,9 +12,9 @@ public class ImageNameSubstitutionTest { public void simpleExample() { try ( // directDockerHubReference { - // Referring directly to an image on Docker Hub (mysql:8.0.22) + // Referring directly to an image on Docker Hub (mysql:8.0.24) final MySQLContainer mysql = new MySQLContainer<>( - DockerImageName.parse("mysql:8.0.22") + DockerImageName.parse("mysql:8.0.24") ) // start the container and use it for testing @@ -35,7 +35,7 @@ public void substitutedExample() { // hardcodedMirror { // Referring directly to an image on a private registry - image name will vary final MySQLContainer mysql = new MySQLContainer<>( - DockerImageName.parse("registry.mycompany.com/mirror/mysql:8.0.22") + DockerImageName.parse("registry.mycompany.com/mirror/mysql:8.0.24") .asCompatibleSubstituteFor("mysql") ) diff --git a/docs/examples/junit4/generic/src/test/java/generic/support/TestSpecificImageNameSubstitutor.java b/docs/examples/junit4/generic/src/test/java/generic/support/TestSpecificImageNameSubstitutor.java index 5f9e92c0add..01f6fdcb4f0 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/support/TestSpecificImageNameSubstitutor.java +++ b/docs/examples/junit4/generic/src/test/java/generic/support/TestSpecificImageNameSubstitutor.java @@ -12,7 +12,7 @@ public class TestSpecificImageNameSubstitutor extends ImageNameSubstitutor { @Override public DockerImageName apply(final DockerImageName original) { - if (original.equals(DockerImageName.parse("registry.mycompany.com/mirror/mysql:8.0.22"))) { + if (original.equals(DockerImageName.parse("registry.mycompany.com/mirror/mysql:8.0.24"))) { return DockerImageName.parse("mysql"); } else { return original; diff --git a/docs/features/image_name_substitution.md b/docs/features/image_name_substitution.md index 37088473d72..7ead811a250 100644 --- a/docs/features/image_name_substitution.md +++ b/docs/features/image_name_substitution.md @@ -53,7 +53,7 @@ Consider this if: * Developers and CI machines need to use different image names. For example, developers are able to pull images from Docker Hub, but CI machines need to pull from a private registry * Your private registry has copies of images from Docker Hub where the names are predictable, and just adding a prefix is enough. - For example, `registry.mycompany.com/mirror/mysql:8.0.22` can be derived from the original Docker Hub image name (`mysql:8.0.22`) with a consistent prefix string: `registry.mycompany.com/mirror/` + For example, `registry.mycompany.com/mirror/mysql:8.0.24` can be derived from the original Docker Hub image name (`mysql:8.0.24`) with a consistent prefix string: `registry.mycompany.com/mirror/` In this case, image name references in code are **unchanged**. i.e. you would leave as-is: diff --git a/docs/modules/databases/jdbc.md b/docs/modules/databases/jdbc.md index fec250ffdd9..7adaf2f5e70 100644 --- a/docs/modules/databases/jdbc.md +++ b/docs/modules/databases/jdbc.md @@ -20,7 +20,7 @@ Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database !!! note We will use `///` (host-less URIs) from now on to emphasis the unimportance of the `host:port` pair. - From Testcontainers' perspective, `jdbc:mysql:5.7.22://localhost:3306/databasename` and `jdbc:mysql:5.7.22:///databasename` is the same URI. + From Testcontainers' perspective, `jdbc:mysql:5.7.34://localhost:3306/databasename` and `jdbc:mysql:5.7.34:///databasename` is the same URI. !!! warning If you're using the JDBC URL support, there is no need to instantiate an instance of the container - Testcontainers will do it automagically. @@ -29,7 +29,7 @@ Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database #### Using Testcontainers with a fixed version -`jdbc:tc:mysql:5.6.23:///databasename` +`jdbc:tc:mysql:5.7.34:///databasename` #### Using PostgreSQL @@ -47,7 +47,7 @@ Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database Testcontainers can run an init script after the database container is started, but before your code is given a connection to it. The script must be on the classpath, and is referenced as follows: -`jdbc:tc:mysql:5.7.22:///databasename?TC_INITSCRIPT=somepath/init_mysql.sql` +`jdbc:tc:mysql:5.7.34:///databasename?TC_INITSCRIPT=somepath/init_mysql.sql` This is useful if you have a fixed script for setting up database schema, etc. @@ -55,13 +55,13 @@ This is useful if you have a fixed script for setting up database schema, etc. If the init script path is prefixed `file:`, it will be loaded from a file (relative to the working directory, which will usually be the project root). -`jdbc:tc:mysql:5.7.22:///databasename?TC_INITSCRIPT=file:src/main/resources/init_mysql.sql` +`jdbc:tc:mysql:5.7.34:///databasename?TC_INITSCRIPT=file:src/main/resources/init_mysql.sql` ### Using an init function Instead of running a fixed script for DB setup, it may be useful to call a Java function that you define. This is intended to allow you to trigger database schema migration tools. To do this, add TC_INITFUNCTION to the URL as follows, passing a full path to the class name and method: - `jdbc:tc:mysql:5.7.22:///databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction` + `jdbc:tc:mysql:5.7.34:///databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction` The init function must be a public static method which takes a `java.sql.Connection` as its only parameter, e.g. ```java @@ -76,7 +76,7 @@ public class JDBCDriverTest { By default database container is being stopped as soon as last connection is closed. There are cases when you might need to start container and keep it running till you stop it explicitly or JVM is shutdown. To do this, add `TC_DAEMON` parameter to the URL as follows: - `jdbc:tc:mysql:5.7.22:///databasename?TC_DAEMON=true` + `jdbc:tc:mysql:5.7.34:///databasename?TC_DAEMON=true` With this parameter database container will keep running even when there're no open connections. diff --git a/docs/modules/databases/mysql.md b/docs/modules/databases/mysql.md index b32373f3363..ac3a53fe1fe 100644 --- a/docs/modules/databases/mysql.md +++ b/docs/modules/databases/mysql.md @@ -7,7 +7,7 @@ See [Database containers](./index.md) for documentation and usage that is common For MySQL databases, it is possible to override configuration settings using resources on the classpath. Assuming `somepath/mysql_conf_override` is a directory on the classpath containing .cnf files, the following URL can be used: - `jdbc:tc:mysql:5.6://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override` + `jdbc:tc:mysql:5.7.34://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override` Any .cnf files in this classpath directory will be mapped into the database container's /etc/mysql/conf.d directory, and will be able to override server settings when the container starts. diff --git a/docs/modules/databases/r2dbc.md b/docs/modules/databases/r2dbc.md index 6ba3f401a08..dba59b2d3d9 100644 --- a/docs/modules/databases/r2dbc.md +++ b/docs/modules/databases/r2dbc.md @@ -22,7 +22,7 @@ The started container will be terminated when the `ConnectionFactory` is closed. **Note that, unlike Testcontainers' JDBC URL support, it is not possible to specify an image tag in the 'scheme' part of the URL, and it is always necessary to specify a tag using `TC_IMAGE_TAG`.** So that the URL becomes: -`r2dbc:tc:mysql:///databasename?TC_IMAGE_TAG=5.7.22` +`r2dbc:tc:mysql:///databasename?TC_IMAGE_TAG=5.7.34` !!! note We will use `///` (host-less URIs) from now on to emphasis the unimportance of the `host:port` pair. @@ -35,7 +35,7 @@ So that the URL becomes: #### Using MySQL -`r2dbc:tc:mysql:///databasename?TC_IMAGE_TAG=5.6.23` +`r2dbc:tc:mysql:///databasename?TC_IMAGE_TAG=5.7.34` #### Using MariaDB diff --git a/modules/jdbc-test/src/main/java/org/testcontainers/db/AbstractContainerDatabaseTest.java b/modules/jdbc-test/src/main/java/org/testcontainers/db/AbstractContainerDatabaseTest.java index 8881ed963d8..6a5ca5094d6 100644 --- a/modules/jdbc-test/src/main/java/org/testcontainers/db/AbstractContainerDatabaseTest.java +++ b/modules/jdbc-test/src/main/java/org/testcontainers/db/AbstractContainerDatabaseTest.java @@ -27,7 +27,6 @@ protected DataSource getDataSource(JdbcDatabaseContainer container) { hikariConfig.setUsername(container.getUsername()); hikariConfig.setPassword(container.getPassword()); hikariConfig.setDriverClassName(container.getDriverClassName()); - return new HikariDataSource(hikariConfig); } } diff --git a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java index 3f0673d834f..920fab04016 100644 --- a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java +++ b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java @@ -33,7 +33,7 @@ public class ConnectionUrlDriversTests { public static Iterable data() { return asList( new Object[][]{ - {"jdbc:tc:mysql:5.5.43://hostname/test", "mysql", Optional.of("5.5.43"), "hostname/test", "test"}, + {"jdbc:tc:mysql:5.7.34://hostname/test", "mysql", Optional.of("5.7.34"), "hostname/test", "test"}, {"jdbc:tc:mysql://hostname/test", "mysql", Optional.empty(), "hostname/test", "test"}, {"jdbc:tc:postgresql:1.2.3://hostname/test", "postgresql", Optional.of("1.2.3"), "hostname/test", "test"}, {"jdbc:tc:postgresql://hostname/test", "postgresql", Optional.empty(), "hostname/test", "test"}, diff --git a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java index 312d5cf5146..4fefd5d1e13 100644 --- a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java +++ b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java @@ -16,11 +16,11 @@ public class ConnectionUrlTest { @Test public void testConnectionUrl1() { - String urlString = "jdbc:tc:mysql:5.6.23://somehostname:3306/databasename?a=b&c=d"; + String urlString = "jdbc:tc:mysql:5.7.34://somehostname:3306/databasename?a=b&c=d"; ConnectionUrl url = ConnectionUrl.newInstance(urlString); assertEquals("Database Type value is as expected", "mysql", url.getDatabaseType()); - assertEquals("Database Image tag value is as expected", "5.6.23", url.getImageTag().get()); + assertEquals("Database Image tag value is as expected", "5.7.34", url.getImageTag().get()); assertEquals("Database Host String is as expected", "somehostname:3306/databasename", url.getDbHostString()); assertEquals("Query String value is as expected", "?a=b&c=d", url.getQueryString().get()); assertEquals("Database Host value is as expected", "somehostname", url.getDatabaseHost().get()); @@ -72,7 +72,7 @@ public void testTmpfsOption() { @Test public void testInitScriptPathCapture() { - String urlString = "jdbc:tc:mysql:5.6.23://somehostname:3306/databasename?a=b&c=d&TC_INITSCRIPT=somepath/init_mysql.sql"; + String urlString = "jdbc:tc:mysql:5.7.34://somehostname:3306/databasename?a=b&c=d&TC_INITSCRIPT=somepath/init_mysql.sql"; ConnectionUrl url = ConnectionUrl.newInstance(urlString); assertEquals("Database Type value is as expected", "somepath/init_mysql.sql", url.getInitScriptPath().get()); @@ -88,7 +88,7 @@ public void testInitScriptPathCapture() { @Test public void testInitFunctionCapture() { - String urlString = "jdbc:tc:mysql:5.6.23://somehostname:3306/databasename?a=b&c=d&TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction"; + String urlString = "jdbc:tc:mysql:5.7.34://somehostname:3306/databasename?a=b&c=d&TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction"; ConnectionUrl url = ConnectionUrl.newInstance(urlString); assertTrue("Init Function parameter exists", url.getInitFunction().isPresent()); @@ -100,7 +100,7 @@ public void testInitFunctionCapture() { @Test public void testDaemonCapture() { - String urlString = "jdbc:tc:mysql:5.6.23://somehostname:3306/databasename?a=b&c=d&TC_DAEMON=true"; + String urlString = "jdbc:tc:mysql:5.7.34://somehostname:3306/databasename?a=b&c=d&TC_DAEMON=true"; ConnectionUrl url = ConnectionUrl.newInstance(urlString); assertTrue("Daemon flag is set to true.", url.isInDaemonMode()); diff --git a/modules/jdbc/src/test/java/org/testcontainers/jdbc/MissingJdbcDriverTest.java b/modules/jdbc/src/test/java/org/testcontainers/jdbc/MissingJdbcDriverTest.java index 88398cebf85..46138d91347 100644 --- a/modules/jdbc/src/test/java/org/testcontainers/jdbc/MissingJdbcDriverTest.java +++ b/modules/jdbc/src/test/java/org/testcontainers/jdbc/MissingJdbcDriverTest.java @@ -40,7 +40,7 @@ static class MissingDriverContainer extends JdbcDatabaseContainer { private final AtomicInteger connectionAttempts = new AtomicInteger(); MissingDriverContainer() { - super(DockerImageName.parse("mysql:5.7.22")); + super(DockerImageName.parse("mysql:5.7.34")); withEnv("MYSQL_ROOT_PASSWORD", "test"); withExposedPorts(3306); } diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index da3dad9f2b3..172ca29070d 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -13,7 +13,7 @@ dependencies { testCompile 'mysql:mysql-connector-java:8.0.22' testCompile testFixtures(project(':r2dbc')) - testCompile 'dev.miku:r2dbc-mysql:0.8.1.RELEASE' + testCompile 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' compileOnly 'org.jetbrains:annotations:20.1.0' } diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java index a126a579489..20be08c2aec 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java @@ -16,7 +16,7 @@ public class MySQLContainer> extends JdbcDatab private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("mysql"); @Deprecated - public static final String DEFAULT_TAG = "5.7.22"; + public static final String DEFAULT_TAG = "5.7.34"; @Deprecated public static final String IMAGE = DEFAULT_IMAGE_NAME.getUnversionedPart(); diff --git a/modules/mysql/src/test/java/org/testcontainers/MySQLTestImages.java b/modules/mysql/src/test/java/org/testcontainers/MySQLTestImages.java index 6c8c6690d1f..833dc1448cf 100644 --- a/modules/mysql/src/test/java/org/testcontainers/MySQLTestImages.java +++ b/modules/mysql/src/test/java/org/testcontainers/MySQLTestImages.java @@ -3,7 +3,7 @@ import org.testcontainers.utility.DockerImageName; public class MySQLTestImages { - public static final DockerImageName MYSQL_IMAGE = DockerImageName.parse("mysql:5.7.22"); - public static final DockerImageName MYSQL_55_IMAGE = DockerImageName.parse("mysql:5.5"); - public static final DockerImageName MYSQL_56_IMAGE = DockerImageName.parse("mysql:5.6"); + public static final DockerImageName MYSQL_56_IMAGE = DockerImageName.parse("mysql:5.6.51"); + public static final DockerImageName MYSQL_57_IMAGE = DockerImageName.parse("mysql:5.7.34"); + public static final DockerImageName MYSQL_80_IMAGE = DockerImageName.parse("mysql:8.0.24"); } diff --git a/modules/mysql/src/test/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerTest.java b/modules/mysql/src/test/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerTest.java index 38abf637c3a..334fabb0a49 100644 --- a/modules/mysql/src/test/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerTest.java @@ -13,12 +13,12 @@ protected ConnectionFactoryOptions getOptions(MySQLContainer container) { @Override protected String createR2DBCUrl() { - return "r2dbc:tc:mysql:///db?TC_IMAGE_TAG=5.7.22"; + return "r2dbc:tc:mysql:///db?TC_IMAGE_TAG=" + MySQLTestImages.MYSQL_80_IMAGE.getVersionPart(); } @Override protected MySQLContainer createContainer() { - return new MySQLContainer<>(MySQLTestImages.MYSQL_IMAGE); + return new MySQLContainer<>(MySQLTestImages.MYSQL_80_IMAGE); } } diff --git a/modules/mysql/src/test/java/org/testcontainers/containers/MySQLRootAccountTest.java b/modules/mysql/src/test/java/org/testcontainers/containers/MySQLRootAccountTest.java index c437acac013..86b13a0d1bf 100644 --- a/modules/mysql/src/test/java/org/testcontainers/containers/MySQLRootAccountTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/containers/MySQLRootAccountTest.java @@ -4,7 +4,9 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import org.testcontainers.MySQLTestImages; import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.utility.DockerImageName; import java.sql.Connection; import java.sql.DriverManager; @@ -15,15 +17,15 @@ public class MySQLRootAccountTest { @Parameterized.Parameters(name = "{0}") - public static String[] params() { - return new String[]{ - "mysql:8", - "mysql:5" + public static DockerImageName[] params() { + return new DockerImageName[]{ + MySQLTestImages.MYSQL_80_IMAGE, + MySQLTestImages.MYSQL_57_IMAGE }; } @Parameterized.Parameter() - public String image; + public DockerImageName image; @Test public void testRootAccountUsageWithDefaultPassword() throws SQLException { diff --git a/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/JDBCDriverWithPoolTest.java b/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/JDBCDriverWithPoolTest.java index 1ad77264c62..3b9f9842095 100644 --- a/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/JDBCDriverWithPoolTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/JDBCDriverWithPoolTest.java @@ -31,7 +31,7 @@ @RunWith(Parameterized.class) public class JDBCDriverWithPoolTest { - public static final String URL = "jdbc:tc:mysql:5://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.mysql.JDBCDriverWithPoolTest::sampleInitFunction"; + public static final String URL = "jdbc:tc:mysql:5.7.34://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.mysql.JDBCDriverWithPoolTest::sampleInitFunction"; private final DataSource dataSource; @Parameterized.Parameters diff --git a/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/MySQLJDBCDriverTest.java b/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/MySQLJDBCDriverTest.java index d4b37b291d5..302621879eb 100644 --- a/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/MySQLJDBCDriverTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/MySQLJDBCDriverTest.java @@ -17,14 +17,14 @@ public static Iterable data() { new Object[][]{ {"jdbc:tc:mysql://hostname/databasename", EnumSet.noneOf(Options.class)}, {"jdbc:tc:mysql://hostname/databasename?user=someuser&TC_INITSCRIPT=somepath/init_mysql.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&TC_INITFUNCTION=org.testcontainers.jdbc.AbstractJDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=somepath/init_mysql.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=file:sql/init_mysql.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mysql:5.5.43://hostname/databasename?user=someuser&password=somepwd&TC_INITFUNCTION=org.testcontainers.jdbc.AbstractJDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mysql:5.5.43://hostname/databasename?TC_INITSCRIPT=somepath/init_unicode_mysql.sql&useUnicode=yes&characterEncoding=utf8", EnumSet.of(Options.CharacterSet)}, - {"jdbc:tc:mysql:5.5.43://hostname/databasename", EnumSet.noneOf(Options.class)}, - {"jdbc:tc:mysql:5.5.43://hostname/databasename?useSSL=false", EnumSet.noneOf(Options.class)}, - {"jdbc:tc:mysql:5.6://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override", EnumSet.of(Options.CustomIniFile)}, + {"jdbc:tc:mysql:5.7.34://hostname/databasename?user=someuser&TC_INITFUNCTION=org.testcontainers.jdbc.AbstractJDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, + {"jdbc:tc:mysql:5.7.34://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=somepath/init_mysql.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, + {"jdbc:tc:mysql:5.7.34://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=file:sql/init_mysql.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, + {"jdbc:tc:mysql:5.7.34://hostname/databasename?user=someuser&password=somepwd&TC_INITFUNCTION=org.testcontainers.jdbc.AbstractJDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, + {"jdbc:tc:mysql:5.7.34://hostname/databasename?TC_INITSCRIPT=somepath/init_unicode_mysql.sql&useUnicode=yes&characterEncoding=utf8", EnumSet.of(Options.CharacterSet)}, + {"jdbc:tc:mysql:5.7.34://hostname/databasename", EnumSet.noneOf(Options.class)}, + {"jdbc:tc:mysql:5.7.34://hostname/databasename?useSSL=false", EnumSet.noneOf(Options.class)}, + {"jdbc:tc:mysql:5.6.51://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override", EnumSet.of(Options.CustomIniFile)}, }); } } diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java index 708b2b987cd..0b72ce083d2 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java @@ -18,7 +18,7 @@ public class CustomizableMysqlTest extends AbstractContainerDatabaseTest { @Test public void testSimple() throws SQLException { // Add MYSQL_ROOT_HOST environment so that we can root login from anywhere for testing purposes - try (MySQLContainer mysql = new MySQLContainer<>(MySQLTestImages.MYSQL_IMAGE) + try (MySQLContainer mysql = new MySQLContainer<>(MySQLTestImages.MYSQL_57_IMAGE) .withDatabaseName(DB_NAME) .withUsername(USER) .withPassword(PWD) diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java index b8f1a136d9a..32dee3cd056 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java @@ -3,7 +3,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import org.testcontainers.MySQLTestImages; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; import org.testcontainers.utility.DockerImageName; @@ -12,32 +11,33 @@ import java.sql.SQLException; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.testcontainers.MySQLTestImages.MYSQL_56_IMAGE; +import static org.testcontainers.MySQLTestImages.MYSQL_57_IMAGE; +import static org.testcontainers.MySQLTestImages.MYSQL_80_IMAGE; @RunWith(Parameterized.class) public class MultiVersionMySQLTest extends AbstractContainerDatabaseTest { @Parameterized.Parameters(name = "{0}") - public static String[] params() { - return new String[]{"5.5.62", "5.6.42", "5.7.26", "8.0.16"}; + public static DockerImageName[] params() { + return new DockerImageName[]{ + MYSQL_56_IMAGE, + MYSQL_57_IMAGE, + MYSQL_80_IMAGE + }; } - private final String version; - - public MultiVersionMySQLTest(String version) { - this.version = version; - } + @Parameterized.Parameter() + public DockerImageName dockerImageName; @Test public void versionCheckTest() throws SQLException { - - final DockerImageName dockerImageName = MySQLTestImages.MYSQL_IMAGE.withTag(version); - try (MySQLContainer mysql = new MySQLContainer<>(dockerImageName)) { mysql.start(); final ResultSet resultSet = performQuery(mysql, "SELECT VERSION()"); final String resultSetString = resultSet.getString(1); - assertEquals("The database version can be set using a container rule parameter", version, resultSetString); + assertEquals("The database version can be set using a container rule parameter", dockerImageName.getVersionPart(), resultSetString); } } } diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java index f0ceeb92a48..1b6143b3b34 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java @@ -24,9 +24,8 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; import static org.rnorth.visibleassertions.VisibleAssertions.fail; -import static org.testcontainers.MySQLTestImages.MYSQL_55_IMAGE; import static org.testcontainers.MySQLTestImages.MYSQL_56_IMAGE; -import static org.testcontainers.MySQLTestImages.MYSQL_IMAGE; +import static org.testcontainers.MySQLTestImages.MYSQL_57_IMAGE; public class SimpleMySQLTest extends AbstractContainerDatabaseTest { @@ -52,7 +51,7 @@ public class SimpleMySQLTest extends AbstractContainerDatabaseTest { @Test public void testSimple() throws SQLException { - try (MySQLContainer mysql = new MySQLContainer<>(MYSQL_IMAGE) + try (MySQLContainer mysql = new MySQLContainer<>(MYSQL_57_IMAGE) .withConfigurationOverride("somepath/mysql_conf_override") .withLogConsumer(new Slf4jLogConsumer(logger))) { @@ -67,7 +66,7 @@ public void testSimple() throws SQLException { @Test public void testSpecificVersion() throws SQLException { - try (MySQLContainer mysqlOldVersion = new MySQLContainer<>(MYSQL_55_IMAGE) + try (MySQLContainer mysqlOldVersion = new MySQLContainer<>(MYSQL_56_IMAGE) .withConfigurationOverride("somepath/mysql_conf_override") .withLogConsumer(new Slf4jLogConsumer(logger))) { @@ -76,7 +75,7 @@ public void testSpecificVersion() throws SQLException { ResultSet resultSet = performQuery(mysqlOldVersion, "SELECT VERSION()"); String resultSetString = resultSet.getString(1); - assertTrue("The database version can be set using a container rule parameter", resultSetString.startsWith("5.5")); + assertTrue("The database version can be set using a container rule parameter", resultSetString.startsWith("5.6")); } } @@ -98,7 +97,7 @@ public void testMySQLWithCustomIniFile() throws SQLException { @Test public void testCommandOverride() throws SQLException { - try (MySQLContainer mysqlCustomConfig = new MySQLContainer<>(MYSQL_IMAGE) + try (MySQLContainer mysqlCustomConfig = new MySQLContainer<>(MYSQL_57_IMAGE) .withCommand("mysqld --auto_increment_increment=42")) { mysqlCustomConfig.start(); @@ -112,7 +111,7 @@ public void testCommandOverride() throws SQLException { @Test public void testExplicitInitScript() throws SQLException { - try (MySQLContainer container = new MySQLContainer<>(MYSQL_IMAGE) + try (MySQLContainer container = new MySQLContainer<>(MYSQL_57_IMAGE) .withInitScript("somepath/init_mysql.sql") .withLogConsumer(new Slf4jLogConsumer(logger))) { container.start(); @@ -126,7 +125,7 @@ public void testExplicitInitScript() throws SQLException { @Test(expected = ContainerLaunchException.class) public void testEmptyPasswordWithNonRootUser() { - try (MySQLContainer container = new MySQLContainer<>(MYSQL_55_IMAGE) + try (MySQLContainer container = new MySQLContainer<>(MYSQL_56_IMAGE) .withDatabaseName("TEST") .withUsername("test") .withPassword("") @@ -139,7 +138,7 @@ public void testEmptyPasswordWithNonRootUser() { @Test public void testEmptyPasswordWithRootUser() throws SQLException { // Add MYSQL_ROOT_HOST environment so that we can root login from anywhere for testing purposes - try (MySQLContainer mysql = new MySQLContainer<>(MYSQL_55_IMAGE) + try (MySQLContainer mysql = new MySQLContainer<>(MYSQL_56_IMAGE) .withDatabaseName("foo") .withUsername("root") .withPassword("") @@ -156,7 +155,7 @@ public void testEmptyPasswordWithRootUser() throws SQLException { @Test public void testWithAdditionalUrlParamTimeZone() throws SQLException { - MySQLContainer mysql = new MySQLContainer<>(MYSQL_IMAGE) + MySQLContainer mysql = new MySQLContainer<>(MYSQL_57_IMAGE) .withUrlParam("serverTimezone", "Europe/Zurich") .withEnv("TZ", "Europe/Zurich") .withLogConsumer(new Slf4jLogConsumer(logger)); @@ -186,7 +185,7 @@ public void testWithAdditionalUrlParamTimeZone() throws SQLException { @Test public void testWithAdditionalUrlParamMultiQueries() throws SQLException { - MySQLContainer mysql = new MySQLContainer<>(MYSQL_IMAGE) + MySQLContainer mysql = new MySQLContainer<>(MYSQL_57_IMAGE) .withUrlParam("allowMultiQueries", "true") .withLogConsumer(new Slf4jLogConsumer(logger)); mysql.start(); @@ -210,7 +209,7 @@ public void testWithAdditionalUrlParamMultiQueries() throws SQLException { @Test public void testWithAdditionalUrlParamInJdbcUrl() { - MySQLContainer mysql = new MySQLContainer<>(MYSQL_IMAGE) + MySQLContainer mysql = new MySQLContainer<>(MYSQL_57_IMAGE) .withUrlParam("allowMultiQueries", "true") .withUrlParam("rewriteBatchedStatements", "true") .withLogConsumer(new Slf4jLogConsumer(logger)); diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy index 9698e7d433a..f18bc8dc96d 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy @@ -3,7 +3,7 @@ package org.testcontainers.spock import org.testcontainers.utility.DockerImageName interface SpockTestImages { - DockerImageName MYSQL_IMAGE = DockerImageName.parse("mysql:5.7.22") + DockerImageName MYSQL_IMAGE = DockerImageName.parse("mysql:5.7.34") DockerImageName POSTGRES_TEST_IMAGE = DockerImageName.parse("postgres:9.6.12") DockerImageName HTTPD_IMAGE = DockerImageName.parse("httpd:2.4-alpine") DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.5") From 99f5fac886d857896b19b8c609161fe53608eda1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20L=C3=B6vdahl?= Date: Fri, 7 May 2021 16:58:22 +0300 Subject: [PATCH 1855/5615] Docs: Use `testImplementation` instead of deprecated `testCompile` (#4054) Co-authored-by: Richard North --- docs/examples/junit4/generic/build.gradle | 14 +++++++------- docs/examples/junit4/redis/build.gradle | 4 ++-- docs/examples/junit5/redis/build.gradle | 8 ++++---- docs/index.md | 2 +- docs/modules/databases/cassandra.md | 2 +- docs/modules/databases/clickhouse.md | 2 +- docs/modules/databases/cockroachdb.md | 2 +- docs/modules/databases/couchbase.md | 2 +- docs/modules/databases/db2.md | 2 +- docs/modules/databases/dynalite.md | 2 +- docs/modules/databases/influxdb.md | 2 +- docs/modules/databases/mariadb.md | 2 +- docs/modules/databases/mongodb.md | 2 +- docs/modules/databases/mssqlserver.md | 2 +- docs/modules/databases/mysql.md | 2 +- docs/modules/databases/neo4j.md | 2 +- docs/modules/databases/oraclexe.md | 2 +- docs/modules/databases/orientdb.md | 2 +- docs/modules/databases/postgres.md | 2 +- docs/modules/databases/presto.md | 2 +- docs/modules/databases/trino.md | 2 +- docs/modules/docker_compose.md | 2 +- docs/modules/elasticsearch.md | 2 +- docs/modules/gcloud.md | 2 +- docs/modules/kafka.md | 2 +- docs/modules/localstack.md | 2 +- docs/modules/mockserver.md | 2 +- docs/modules/nginx.md | 2 +- docs/modules/pulsar.md | 2 +- docs/modules/rabbitmq.md | 2 +- docs/modules/solr.md | 2 +- docs/modules/toxiproxy.md | 2 +- docs/modules/vault.md | 2 +- docs/modules/webdriver_containers.md | 2 +- docs/quickstart/junit_4_quickstart.md | 2 +- docs/quickstart/junit_5_quickstart.md | 8 ++++---- docs/quickstart/spock_quickstart.md | 2 +- docs/test_framework_integration/spock.md | 2 +- 38 files changed, 51 insertions(+), 51 deletions(-) diff --git a/docs/examples/junit4/generic/build.gradle b/docs/examples/junit4/generic/build.gradle index ff1b11f142e..1021bfeffc0 100644 --- a/docs/examples/junit4/generic/build.gradle +++ b/docs/examples/junit4/generic/build.gradle @@ -1,12 +1,12 @@ description = "Examples for docs" dependencies { - testCompile "junit:junit:4.12" - testCompile project(":testcontainers") - testCompile project(":selenium") - testCompile project(":mysql") + testImplementation "junit:junit:4.12" + testImplementation project(":testcontainers") + testImplementation project(":selenium") + testImplementation project(":mysql") - testCompile 'mysql:mysql-connector-java:8.0.21' - testCompile "org.seleniumhq.selenium:selenium-api:3.141.59" - testCompile 'org.assertj:assertj-core:3.15.0' + testImplementation 'mysql:mysql-connector-java:8.0.21' + testImplementation "org.seleniumhq.selenium:selenium-api:3.141.59" + testImplementation 'org.assertj:assertj-core:3.15.0' } diff --git a/docs/examples/junit4/redis/build.gradle b/docs/examples/junit4/redis/build.gradle index 76d32e5b4d0..bf203a8fefa 100644 --- a/docs/examples/junit4/redis/build.gradle +++ b/docs/examples/junit4/redis/build.gradle @@ -3,6 +3,6 @@ description = "Examples for docs" dependencies { compile "io.lettuce:lettuce-core:5.1.1.RELEASE" - testCompile "junit:junit:4.12" - testCompile project(":testcontainers") + testImplementation "junit:junit:4.12" + testImplementation project(":testcontainers") } diff --git a/docs/examples/junit5/redis/build.gradle b/docs/examples/junit5/redis/build.gradle index 56442b66af3..409b766b758 100644 --- a/docs/examples/junit5/redis/build.gradle +++ b/docs/examples/junit5/redis/build.gradle @@ -3,9 +3,9 @@ description = "Examples for docs" dependencies { compile "io.lettuce:lettuce-core:5.1.1.RELEASE" - testCompile "org.junit.jupiter:junit-jupiter-api:5.4.2" - testCompile "org.junit.jupiter:junit-jupiter-params:5.4.2" + testImplementation "org.junit.jupiter:junit-jupiter-api:5.4.2" + testImplementation "org.junit.jupiter:junit-jupiter-params:5.4.2" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.4.2" - testCompile project(":testcontainers") - testCompile project(":junit-jupiter") + testImplementation project(":testcontainers") + testImplementation project(":junit-jupiter") } diff --git a/docs/index.md b/docs/index.md index 45213629ed0..e58a23bc330 100644 --- a/docs/index.md +++ b/docs/index.md @@ -32,7 +32,7 @@ Testcontainers is distributed as separate JARs with a common version number: For the core library, the latest Maven/Gradle dependency is as follows: ```groovy tab='Gradle' -testCompile "org.testcontainers:testcontainers:{{latest_version}}" +testImplementation "org.testcontainers:testcontainers:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/databases/cassandra.md b/docs/modules/databases/cassandra.md index d15304afa81..63213d27343 100644 --- a/docs/modules/databases/cassandra.md +++ b/docs/modules/databases/cassandra.md @@ -38,7 +38,7 @@ public class SomeTest { Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:cassandra:{{latest_version}}" +testImplementation "org.testcontainers:cassandra:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/databases/clickhouse.md b/docs/modules/databases/clickhouse.md index 37f68c41e18..a52a75af51f 100644 --- a/docs/modules/databases/clickhouse.md +++ b/docs/modules/databases/clickhouse.md @@ -5,7 +5,7 @@ Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:clickhouse:{{latest_version}}" +testImplementation "org.testcontainers:clickhouse:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/databases/cockroachdb.md b/docs/modules/databases/cockroachdb.md index d9e6def4e48..540f4820a84 100644 --- a/docs/modules/databases/cockroachdb.md +++ b/docs/modules/databases/cockroachdb.md @@ -10,7 +10,7 @@ See [Database containers](./index.md) for documentation and usage that is common Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:cockroachdb:{{latest_version}}" +testImplementation "org.testcontainers:cockroachdb:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/databases/couchbase.md b/docs/modules/databases/couchbase.md index b71ee5de327..f8ed7465dd9 100644 --- a/docs/modules/databases/couchbase.md +++ b/docs/modules/databases/couchbase.md @@ -33,7 +33,7 @@ Running Couchbase as a stand-in in a test: Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:couchbase:{{latest_version}}" +testImplementation "org.testcontainers:couchbase:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/databases/db2.md b/docs/modules/databases/db2.md index bc25b343072..3872e7fa857 100644 --- a/docs/modules/databases/db2.md +++ b/docs/modules/databases/db2.md @@ -34,7 +34,7 @@ public class SomeTest { Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:db2:{{latest_version}}" +testImplementation "org.testcontainers:db2:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/databases/dynalite.md b/docs/modules/databases/dynalite.md index 2d5c7f7492d..dfc32f35d2e 100644 --- a/docs/modules/databases/dynalite.md +++ b/docs/modules/databases/dynalite.md @@ -30,7 +30,7 @@ In part, because it's light and quick to run. Also, please see the [reasons give Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:dynalite:{{latest_version}}" +testImplementation "org.testcontainers:dynalite:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/databases/influxdb.md b/docs/modules/databases/influxdb.md index 740cf69982c..5de7d36182a 100644 --- a/docs/modules/databases/influxdb.md +++ b/docs/modules/databases/influxdb.md @@ -23,7 +23,7 @@ public class SomeTest { Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:influxdb:{{latest_version}}" +testImplementation "org.testcontainers:influxdb:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/databases/mariadb.md b/docs/modules/databases/mariadb.md index 74a4c6e061e..7ef14bed3cc 100644 --- a/docs/modules/databases/mariadb.md +++ b/docs/modules/databases/mariadb.md @@ -24,7 +24,7 @@ public class SomeTest { Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:mariadb:{{latest_version}}" +testImplementation "org.testcontainers:mariadb:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/databases/mongodb.md b/docs/modules/databases/mongodb.md index fc2cb873f59..3d49ecc6809 100644 --- a/docs/modules/databases/mongodb.md +++ b/docs/modules/databases/mongodb.md @@ -41,7 +41,7 @@ That's where the MongoDBContainer might come in handy. Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:mongodb:{{latest_version}}" +testImplementation "org.testcontainers:mongodb:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/databases/mssqlserver.md b/docs/modules/databases/mssqlserver.md index 29c5435be32..6dea62def25 100644 --- a/docs/modules/databases/mssqlserver.md +++ b/docs/modules/databases/mssqlserver.md @@ -30,7 +30,7 @@ public class SomeTest { Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:mssqlserver:{{latest_version}}" +testImplementation "org.testcontainers:mssqlserver:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/databases/mysql.md b/docs/modules/databases/mysql.md index ac3a53fe1fe..f5a428e1cdc 100644 --- a/docs/modules/databases/mysql.md +++ b/docs/modules/databases/mysql.md @@ -18,7 +18,7 @@ and will be able to override server settings when the container starts. Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:mysql:{{latest_version}}" +testImplementation "org.testcontainers:mysql:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/databases/neo4j.md b/docs/modules/databases/neo4j.md index aaa18d225c9..702f9cc40d7 100644 --- a/docs/modules/databases/neo4j.md +++ b/docs/modules/databases/neo4j.md @@ -163,7 +163,7 @@ You'll find more information about licensing Neo4j here: [About Neo4j Licenses]( Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:neo4j:{{latest_version}}" +testImplementation "org.testcontainers:neo4j:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/databases/oraclexe.md b/docs/modules/databases/oraclexe.md index 86a9a5f938c..56eb57bc0e5 100644 --- a/docs/modules/databases/oraclexe.md +++ b/docs/modules/databases/oraclexe.md @@ -31,7 +31,7 @@ containing `oracle.container.image=IMAGE`, where IMAGE is a suitable image name Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:oracle-xe:{{latest_version}}" +testImplementation "org.testcontainers:oracle-xe:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/databases/orientdb.md b/docs/modules/databases/orientdb.md index 80ed96f9548..4f0b3550e44 100644 --- a/docs/modules/databases/orientdb.md +++ b/docs/modules/databases/orientdb.md @@ -46,7 +46,7 @@ You are not limited to Unit tests and can of course use an instance of the Orien Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:orientdb:{{latest_version}}" +testImplementation "org.testcontainers:orientdb:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/databases/postgres.md b/docs/modules/databases/postgres.md index 07831766d1b..0777296b0c0 100644 --- a/docs/modules/databases/postgres.md +++ b/docs/modules/databases/postgres.md @@ -7,7 +7,7 @@ See [Database containers](./index.md) for documentation and usage that is common Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:postgresql:{{latest_version}}" +testImplementation "org.testcontainers:postgresql:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/databases/presto.md b/docs/modules/databases/presto.md index 8e8a2672355..3b987796360 100644 --- a/docs/modules/databases/presto.md +++ b/docs/modules/databases/presto.md @@ -68,7 +68,7 @@ public class SomeTest { Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:presto:{{latest_version}}" +testImplementation "org.testcontainers:presto:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/databases/trino.md b/docs/modules/databases/trino.md index 4cd26978af1..5b65f45934a 100644 --- a/docs/modules/databases/trino.md +++ b/docs/modules/databases/trino.md @@ -68,7 +68,7 @@ public class SomeTest { Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:trino:{{latest_version}}" +testImplementation "org.testcontainers:trino:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/docker_compose.md b/docs/modules/docker_compose.md index fa584d7f631..b789a0030a7 100644 --- a/docs/modules/docker_compose.md +++ b/docs/modules/docker_compose.md @@ -178,7 +178,7 @@ There are 3 ways to specify location of the `config.json` for Docker Compose: Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:testcontainers:{{latest_version}}" +testImplementation "org.testcontainers:testcontainers:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/elasticsearch.md b/docs/modules/elasticsearch.md index cd47c9c4ed3..738bf427a56 100644 --- a/docs/modules/elasticsearch.md +++ b/docs/modules/elasticsearch.md @@ -42,7 +42,7 @@ other new and advanced features), you can use this instead: Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:elasticsearch:{{latest_version}}" +testImplementation "org.testcontainers:elasticsearch:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/gcloud.md b/docs/modules/gcloud.md index 33d50ee634f..aeab52fc932 100644 --- a/docs/modules/gcloud.md +++ b/docs/modules/gcloud.md @@ -142,7 +142,7 @@ See more examples: Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:gcloud:{{latest_version}}" +testImplementation "org.testcontainers:gcloud:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/kafka.md b/docs/modules/kafka.md index b74d4ef49bd..b0cd1742f6b 100644 --- a/docs/modules/kafka.md +++ b/docs/modules/kafka.md @@ -51,7 +51,7 @@ You will need to explicitly create a network and set it on the Kafka container a Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:kafka:{{latest_version}}" +testImplementation "org.testcontainers:kafka:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/localstack.md b/docs/modules/localstack.md index 41fd2a50a52..b6058508ddb 100644 --- a/docs/modules/localstack.md +++ b/docs/modules/localstack.md @@ -67,7 +67,7 @@ Testcontainers will inform Localstack of the best hostname automatically, using Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:localstack:{{latest_version}}" +testImplementation "org.testcontainers:localstack:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/mockserver.md b/docs/modules/mockserver.md index ead9d123915..bf56046deda 100644 --- a/docs/modules/mockserver.md +++ b/docs/modules/mockserver.md @@ -21,7 +21,7 @@ And how to set a simple expectation using the Java MockServerClient. Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:mockserver:{{latest_version}}" +testImplementation "org.testcontainers:mockserver:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/nginx.md b/docs/modules/nginx.md index d195b5451a2..5169d3c5625 100644 --- a/docs/modules/nginx.md +++ b/docs/modules/nginx.md @@ -27,7 +27,7 @@ And how to query the Nginx server for the custom content added. Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:nginx:{{latest_version}}" +testImplementation "org.testcontainers:nginx:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/pulsar.md b/docs/modules/pulsar.md index 8e6441586f8..92eda498f7c 100644 --- a/docs/modules/pulsar.md +++ b/docs/modules/pulsar.md @@ -5,7 +5,7 @@ Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:pulsar:{{latest_version}}" +testImplementation "org.testcontainers:pulsar:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/rabbitmq.md b/docs/modules/rabbitmq.md index 92275ad765b..dff8631eeca 100644 --- a/docs/modules/rabbitmq.md +++ b/docs/modules/rabbitmq.md @@ -8,7 +8,7 @@ Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:rabbitmq:{{latest_version}}" +testImplementation "org.testcontainers:rabbitmq:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/solr.md b/docs/modules/solr.md index 4776ffa608b..bb2d18d6431 100644 --- a/docs/modules/solr.md +++ b/docs/modules/solr.md @@ -21,7 +21,7 @@ You can start a solr container instance from any Java application by using: Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:solr:{{latest_version}}" +testImplementation "org.testcontainers:solr:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/toxiproxy.md b/docs/modules/toxiproxy.md index 8ffb910d6c1..a3403ccc377 100644 --- a/docs/modules/toxiproxy.md +++ b/docs/modules/toxiproxy.md @@ -73,7 +73,7 @@ Additionally we can disable the proxy to simulate a complete interruption to the Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:toxiproxy:{{latest_version}}" +testImplementation "org.testcontainers:toxiproxy:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/vault.md b/docs/modules/vault.md index 1f58c0ab836..3140a6258bc 100644 --- a/docs/modules/vault.md +++ b/docs/modules/vault.md @@ -42,7 +42,7 @@ test how your application behaves with Vault by writing different test scenarios Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:vault:{{latest_version}}" +testImplementation "org.testcontainers:vault:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/modules/webdriver_containers.md b/docs/modules/webdriver_containers.md index 41b7fb6bb23..34d3430830c 100644 --- a/docs/modules/webdriver_containers.md +++ b/docs/modules/webdriver_containers.md @@ -89,7 +89,7 @@ A few different examples are shown in [ChromeWebDriverContainerTest.java](https: Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:selenium:{{latest_version}}" +testImplementation "org.testcontainers:selenium:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/quickstart/junit_4_quickstart.md b/docs/quickstart/junit_4_quickstart.md index a3fecb1a14d..1a2cd84c77e 100644 --- a/docs/quickstart/junit_4_quickstart.md +++ b/docs/quickstart/junit_4_quickstart.md @@ -22,7 +22,7 @@ Let's start from here, and see how to improve the test with Testcontainers: First, add Testcontainers as a dependency as follows: ```groovy tab='Gradle' -testCompile "org.testcontainers:testcontainers:{{latest_version}}" +testImplementation "org.testcontainers:testcontainers:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/quickstart/junit_5_quickstart.md b/docs/quickstart/junit_5_quickstart.md index efd52b6c523..eb43bf05797 100644 --- a/docs/quickstart/junit_5_quickstart.md +++ b/docs/quickstart/junit_5_quickstart.md @@ -23,11 +23,11 @@ First, add Testcontainers as a dependency as follows: ```groovy tab='Gradle' def junitJupiterVersion = '5.4.2' -testCompile "org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion" -testCompile "org.junit.jupiter:junit-jupiter-params:$junitJupiterVersion" +testImplementation "org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion" +testImplementation "org.junit.jupiter:junit-jupiter-params:$junitJupiterVersion" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion" -testCompile "org.testcontainers:testcontainers:{{latest_version}}" -testCompile "org.testcontainers:junit-jupiter:{{latest_version}}" +testImplementation "org.testcontainers:testcontainers:{{latest_version}}" +testImplementation "org.testcontainers:junit-jupiter:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/quickstart/spock_quickstart.md b/docs/quickstart/spock_quickstart.md index 902f1a78c09..875c1976c93 100644 --- a/docs/quickstart/spock_quickstart.md +++ b/docs/quickstart/spock_quickstart.md @@ -22,7 +22,7 @@ Let's start from here, and see how to improve the test with Testcontainers: First, add Testcontainers as a dependency as follows: ```groovy tab='Gradle' -testCompile "org.testcontainers:spock:{{latest_version}}" +testImplementation "org.testcontainers:spock:{{latest_version}}" ``` ```xml tab='Maven' diff --git a/docs/test_framework_integration/spock.md b/docs/test_framework_integration/spock.md index 8528faf4490..11ccf058767 100644 --- a/docs/test_framework_integration/spock.md +++ b/docs/test_framework_integration/spock.md @@ -18,7 +18,7 @@ can be mixed with Spock's `@Shared` annotation to indicate, that containers shou Add the following dependency to your `pom.xml`/`build.gradle` file: ```groovy tab='Gradle' -testCompile "org.testcontainers:spock:{{latest_version}}" +testImplementation "org.testcontainers:spock:{{latest_version}}" ``` ```xml tab='Maven' From cf6d15c8b8dabb59be8be2e1000d7e4f6d16817c Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 7 May 2021 15:59:21 +0200 Subject: [PATCH 1856/5615] Remove visible assertions dependency (#4010) --- core/build.gradle | 3 +-- .../java/org/testcontainers/containers/GenericContainer.java | 3 +-- examples/disque-job-queue/build.gradle | 1 + examples/redis-backed-cache-testng/build.gradle | 1 + examples/redis-backed-cache/build.gradle | 1 + examples/selenium-container/build.gradle | 1 + examples/singleton-container/build.gradle | 1 + examples/solr-container/build.gradle | 1 + examples/spring-boot/build.gradle | 1 + modules/dynalite/build.gradle | 2 ++ modules/elasticsearch/build.gradle | 1 + modules/jdbc/build.gradle | 1 + modules/localstack/build.gradle | 1 + modules/mockserver/build.gradle | 1 + modules/nginx/build.gradle | 1 + modules/selenium/build.gradle | 1 + 16 files changed, 17 insertions(+), 4 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index 463116dc5ee..5c8e9fe875d 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -178,8 +178,6 @@ dependencies { exclude(group: 'org.jetbrains', module: 'annotations') } - compile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' - compile "com.github.docker-java:docker-java-api:3.2.8" shaded ('com.github.docker-java:docker-java-core:3.2.8') { @@ -218,6 +216,7 @@ dependencies { // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest testCompile files('testlib/repo/fakejar/fakejar/0/fakejar-0.jar') + testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' testCompile 'org.assertj:assertj-core:3.18.1' testCompile project(':test-support') diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 4536a70789c..e53af6240ab 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -71,7 +71,6 @@ import org.rnorth.ducttape.ratelimits.RateLimiter; import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; import org.rnorth.ducttape.unreliables.Unreliables; -import org.rnorth.visibleassertions.VisibleAssertions; import org.slf4j.Logger; import org.testcontainers.DockerClientFactory; import org.testcontainers.UnstableAPI; @@ -748,7 +747,7 @@ private void applyConfiguration(CreateContainerCmd createCommand) { boolean shouldCheckFileMountingSupport = binds.size() > 0 && !TestcontainersConfiguration.getInstance().isDisableChecks(); if (shouldCheckFileMountingSupport) { if (!DockerClientFactory.instance().isFileMountingSupported()) { - VisibleAssertions.warn( + logger().warn( "Unable to mount a file from test host into a running container. " + "This may be a misconfiguration or limitation of your Docker environment. " + "Some features might not work." diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index c31cb39b7fe..90bbdd6d6f9 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -15,4 +15,5 @@ dependencies { testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.mockito:mockito-all:1.10.19' testImplementation 'org.testcontainers:testcontainers' + testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 76c245d5b51..d297f026323 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -14,6 +14,7 @@ dependencies { testImplementation 'org.testcontainers:testcontainers' testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.testng:testng:7.3.0' + testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } test { diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index e63d494bcef..702ce023c6b 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -14,4 +14,5 @@ dependencies { testImplementation 'org.testcontainers:testcontainers' testImplementation 'junit:junit:4.13.1' testImplementation 'ch.qos.logback:logback-classic:1.2.3' + testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index 1f05d7b5663..a671c1832fe 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -15,4 +15,5 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.testcontainers:selenium' + testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index bbd35cabc49..54a16b25230 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -15,4 +15,5 @@ dependencies { testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.testcontainers:testcontainers' + testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/examples/solr-container/build.gradle b/examples/solr-container/build.gradle index fcdf93a9c53..8c9fc2fcc11 100644 --- a/examples/solr-container/build.gradle +++ b/examples/solr-container/build.gradle @@ -14,5 +14,6 @@ dependencies { testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.testcontainers:solr' + testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index 288a9226b90..ce2c028f83a 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -17,4 +17,5 @@ dependencies { runtimeOnly 'org.postgresql:postgresql' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.testcontainers:postgresql' + testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index a09ef417159..3ac35862e1a 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -5,4 +5,6 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.929' testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.929' + + testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index cb553c54654..f50b38bb568 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -4,4 +4,5 @@ dependencies { compile project(':testcontainers') testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.12.0" testCompile "org.elasticsearch.client:transport:7.10.1" + testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 910dc29ebad..8b509f54fdb 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -10,4 +10,5 @@ dependencies { testCompile 'org.apache.tomcat:tomcat-jdbc:10.0.0' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'com.googlecode.junit-toolbox:junit-toolbox:2.4' + testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index c007b2c649e..6fb69b1ad85 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -8,4 +8,5 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.986' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' testCompile 'software.amazon.awssdk:s3:2.16.34' + testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/modules/mockserver/build.gradle b/modules/mockserver/build.gradle index b463997fed5..e13343559f5 100644 --- a/modules/mockserver/build.gradle +++ b/modules/mockserver/build.gradle @@ -4,4 +4,5 @@ dependencies { compile project(':testcontainers') testCompile 'org.mock-server:mockserver-client-java:5.5.4' + testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/modules/nginx/build.gradle b/modules/nginx/build.gradle index 6c4b97876c4..1e601312ab4 100644 --- a/modules/nginx/build.gradle +++ b/modules/nginx/build.gradle @@ -3,4 +3,5 @@ description = "Testcontainers :: Nginx" dependencies { compile project(':testcontainers') compileOnly 'org.jetbrains:annotations:20.1.0' + testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/modules/selenium/build.gradle b/modules/selenium/build.gradle index 7c6d81ca1bf..7911b1ea991 100644 --- a/modules/selenium/build.gradle +++ b/modules/selenium/build.gradle @@ -10,6 +10,7 @@ dependencies { testCompile 'org.mortbay.jetty:jetty:6.1.26' testCompile project(':nginx') + testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' compileOnly 'org.jetbrains:annotations:20.1.0' testCompileClasspath 'org.jetbrains:annotations:20.0.0' From 67812b567285be124253442007b1fe0034c23c15 Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 14 May 2021 05:43:40 +0100 Subject: [PATCH 1857/5615] Reduce dependabot bump rate to monthly (#4083) --- .github/dependabot.yml | 56 +++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 16b59da6dfa..4caa6509af2 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,146 +3,146 @@ updates: - package-ecosystem: "gradle" directory: "/core" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 10 # Explicit entry for each module - package-ecosystem: "gradle" directory: "/modules/cassandra" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/clickhouse" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/couchbase" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/database-commons" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/dynalite" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/elasticsearch" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/influxdb" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/jdbc" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/jdbc-test" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/junit-jupiter" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/kafka" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/localstack" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/mariadb" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/mockserver" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/mssqlserver" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/mysql" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/neo4j" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/nginx" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/oracle-xe" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/postgresql" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/pulsar" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/selenium" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/spock" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/toxiproxy" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/vault" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 10 # Examples - package-ecosystem: "gradle" directory: "/examples" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 10 # GitHub Actions - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 10 From 137b00dfb9c72a3af1e0b27c442e8a5aa1a234c3 Mon Sep 17 00:00:00 2001 From: Mark Rotteveel Date: Wed, 19 May 2021 14:19:16 +0200 Subject: [PATCH 1858/5615] Only run update-* and release-drafter on original repository, not on forks (#4104) --- .github/workflows/release-drafter.yml | 1 + .github/workflows/update-docs-version.yml | 1 + .github/workflows/update-gradle-wrapper.yml | 1 + 3 files changed, 3 insertions(+) diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index e19e08b9f32..bb2afd986f1 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -7,6 +7,7 @@ on: jobs: update_release_draft: + if: github.repository == 'testcontainers/testcontainers-java' runs-on: ubuntu-latest steps: - uses: release-drafter/release-drafter@fe52e97d262833ae07d05efaf1a239df3f1b5cd4 # v5.15.0 diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index 64658c1be4a..ee11b83026a 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -6,6 +6,7 @@ on: jobs: build: + if: github.repository == 'testcontainers/testcontainers-java' runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/update-gradle-wrapper.yml b/.github/workflows/update-gradle-wrapper.yml index d34110f5a6d..329230640e7 100644 --- a/.github/workflows/update-gradle-wrapper.yml +++ b/.github/workflows/update-gradle-wrapper.yml @@ -6,6 +6,7 @@ on: jobs: update-gradle-wrapper: + if: github.repository == 'testcontainers/testcontainers-java' runs-on: ubuntu-latest steps: From 3858aff8d63ae0b28b92bd08c21255163ae75160 Mon Sep 17 00:00:00 2001 From: vcvitaly Date: Wed, 19 May 2021 15:23:26 +0300 Subject: [PATCH 1859/5615] Improve log messages when database container test query fails (#3015) Co-authored-by: Sergei Egorov --- modules/jdbc/build.gradle | 1 + .../containers/JdbcDatabaseContainer.java | 32 ++++----- .../containers/JdbcDatabaseContainerTest.java | 71 +++++++++++++++++++ 3 files changed, 88 insertions(+), 16 deletions(-) create mode 100644 modules/jdbc/src/test/java/org/testcontainers/containers/JdbcDatabaseContainerTest.java diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 8b509f54fdb..8f2d8954985 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -11,4 +11,5 @@ dependencies { testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'com.googlecode.junit-toolbox:junit-toolbox:2.4' testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testCompile 'org.assertj:assertj-core:3.16.1' } diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index 26d699a1813..cf5ae150575 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -1,7 +1,9 @@ package org.testcontainers.containers; import com.github.dockerjava.api.command.InspectContainerResponse; +import java.sql.Statement; import lombok.NonNull; +import lombok.SneakyThrows; import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.traits.LinkableContainer; @@ -130,25 +132,23 @@ public SELF withInitScript(String initScriptPath) { return self(); } + @SneakyThrows(InterruptedException.class) @Override protected void waitUntilContainerStarted() { logger().info("Waiting for database connection to become available at {} using query '{}'", getJdbcUrl(), getTestQueryString()); // Repeatedly try and open a connection to the DB and execute a test query long start = System.currentTimeMillis(); - try { - while (System.currentTimeMillis() < start + (1000 * startupTimeoutSeconds)) { - try { - if (!isRunning()) { - Thread.sleep(100L); - continue; // Don't attempt to connect yet - } - try (Connection connection = createConnection("")) { - boolean testQuerySucceeded = connection.createStatement().execute(this.getTestQueryString()); - if (testQuerySucceeded) { - break; - } + while (System.currentTimeMillis() < start + (1000 * startupTimeoutSeconds)) { + if (!isRunning()) { + Thread.sleep(100L); + } else { + try (Statement statement = createConnection("").createStatement()) { + boolean testQuerySucceeded = statement.execute(this.getTestQueryString()); + if (testQuerySucceeded) { + logger().info("Container is started (JDBC URL: {})", this.getJdbcUrl()); + return; } } catch (NoDriverFoundException e) { // we explicitly want this exception to fail fast without retries @@ -159,12 +159,12 @@ protected void waitUntilContainerStarted() { Thread.sleep(100L); } } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new ContainerLaunchException("Container startup wait was interrupted", e); } - logger().info("Container is started (JDBC URL: {})", JdbcDatabaseContainer.this.getJdbcUrl()); + throw new IllegalStateException( + String.format("Container is started, but cannot be accessed by (JDBC URL: %s), please check container logs", + this.getJdbcUrl()) + ); } @Override diff --git a/modules/jdbc/src/test/java/org/testcontainers/containers/JdbcDatabaseContainerTest.java b/modules/jdbc/src/test/java/org/testcontainers/containers/JdbcDatabaseContainerTest.java new file mode 100644 index 00000000000..9f0cf514558 --- /dev/null +++ b/modules/jdbc/src/test/java/org/testcontainers/containers/JdbcDatabaseContainerTest.java @@ -0,0 +1,71 @@ +package org.testcontainers.containers; + +import java.sql.Connection; +import java.sql.SQLException; +import lombok.NonNull; +import org.junit.Test; +import org.slf4j.Logger; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.Mockito.mock; + +public class JdbcDatabaseContainerTest { + + @Test + public void anExceptionIsThrownIfJdbcIsNotAvailable() { + JdbcDatabaseContainer jdbcContainer = new JdbcDatabaseContainerStub("mysql:latest") + .withStartupTimeoutSeconds(1); + + assertThatExceptionOfType(IllegalStateException.class).isThrownBy(jdbcContainer::waitUntilContainerStarted); + } + + static class JdbcDatabaseContainerStub extends JdbcDatabaseContainer { + + public JdbcDatabaseContainerStub(@NonNull String dockerImageName) { + super(dockerImageName); + } + + @Override + public String getDriverClassName() { + return null; + } + + @Override + public String getJdbcUrl() { + return null; + } + + @Override + public String getUsername() { + return null; + } + + @Override + public String getPassword() { + return null; + } + + @Override + protected String getTestQueryString() { + return null; + } + + @Override + public boolean isRunning() { + return true; + } + + @Override + public Connection createConnection(String queryString) throws SQLException, NoDriverFoundException { + throw new SQLException("Could not create new connection"); + } + + @Override + protected Logger logger() { + return mock(Logger.class); + } + + @Override + public void setDockerImageName(@NonNull String dockerImageName) {} + } +} From 9c759abbb60d0d9b72aa711f35fee008ec4aa679 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 13:44:24 +0100 Subject: [PATCH 1860/5615] Bump s3 from 2.16.34 to 2.16.62 in /modules/localstack (#4102) Bumps s3 from 2.16.34 to 2.16.62. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 6fb69b1ad85..61d9271bad4 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,6 +7,6 @@ dependencies { testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.929' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.986' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' - testCompile 'software.amazon.awssdk:s3:2.16.34' + testCompile 'software.amazon.awssdk:s3:2.16.62' testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From dda77d61aa2a7d6f3d74d28e660088f98c5a2dd4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 13:44:51 +0100 Subject: [PATCH 1861/5615] Bump aws-java-sdk-sqs from 1.11.986 to 1.11.1018 in /modules/localstack (#4101) Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.11.986 to 1.11.1018. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.986...1.11.1018) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 61d9271bad4..15f34e28d76 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.930' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.929' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.986' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.1018' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' testCompile 'software.amazon.awssdk:s3:2.16.62' testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From 8c9feb706db98f12f48f2714927b9ac916cb093e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 13:45:05 +0100 Subject: [PATCH 1862/5615] Bump mysql-connector-java from 8.0.22 to 8.0.25 in /modules/jdbc-test (#4095) Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.22 to 8.0.25. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.22...8.0.25) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 05a96aaf333..b7a81682e37 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -16,5 +16,5 @@ dependencies { compile 'org.apache.tomcat:tomcat-jdbc:9.0.40' compile 'org.vibur:vibur-dbcp:25.0' - compile 'mysql:mysql-connector-java:8.0.22' + compile 'mysql:mysql-connector-java:8.0.25' } From 264796da9d7bc567f8baacdbc4de2a620eba31a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 13:45:15 +0100 Subject: [PATCH 1863/5615] Bump pulsar-client-admin from 2.7.0 to 2.7.2 in /modules/pulsar (#4098) Bumps [pulsar-client-admin](https://github.com/apache/pulsar) from 2.7.0 to 2.7.2. - [Release notes](https://github.com/apache/pulsar/releases) - [Commits](https://github.com/apache/pulsar/compare/v2.7.0...v2.7.2) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index e189e7006eb..7604a6712a6 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -5,5 +5,5 @@ dependencies { testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.7.1' testCompile group: 'org.assertj', name: 'assertj-core', version: '3.18.1' - testCompile group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.7.0' + testCompile group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.7.2' } From eb62a375c4f7e221b83a34959d68ac6908613539 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 13:45:23 +0100 Subject: [PATCH 1864/5615] Bump mockito-core from 3.6.28 to 3.10.0 in /core (#4100) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.6.28 to 3.10.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.6.28...v3.10.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 5c8e9fe875d..e7a2710b796 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -210,7 +210,7 @@ dependencies { testCompile 'com.rabbitmq:amqp-client:5.9.0' testCompile 'org.mongodb:mongo-java-driver:3.12.7' - testCompile ('org.mockito:mockito-core:3.6.28') { + testCompile ('org.mockito:mockito-core:3.10.0') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From 0bfcc10c2dff95fd47478d84c3a8ecfbc5adf716 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 13:45:45 +0100 Subject: [PATCH 1865/5615] Bump mockito-core from 3.6.28 to 3.10.0 in /modules/junit-jupiter (#4094) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.6.28 to 3.10.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.6.28...v3.10.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 12d4321d10f..15a5d665ee0 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,7 +9,7 @@ dependencies { testCompile 'com.zaxxer:HikariCP:3.4.5' testCompile 'redis.clients:jedis:3.3.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.13' - testCompile ('org.mockito:mockito-core:3.6.28') { + testCompile ('org.mockito:mockito-core:3.10.0') { exclude(module: 'hamcrest-core') } testCompile 'org.assertj:assertj-core:3.18.1' From 83c4b823b93f16d03b99de3536c0c2da6415e136 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 13:45:53 +0100 Subject: [PATCH 1866/5615] Bump mysql-connector-java from 8.0.22 to 8.0.25 in /modules/spock (#4093) Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.22 to 8.0.25. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.22...8.0.25) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index a095bcb8a80..d0665614b17 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -16,7 +16,7 @@ dependencies { testCompile 'org.apache.httpcomponents:httpclient:4.5.13' testRuntime 'org.postgresql:postgresql:42.2.18' - testRuntime 'mysql:mysql-connector-java:8.0.22' + testRuntime 'mysql:mysql-connector-java:8.0.25' testCompileClasspath 'org.jetbrains:annotations:20.1.0' } From 3aa1b8a3b8bed2f92b21ebaa342c8114873887f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 13:46:01 +0100 Subject: [PATCH 1867/5615] Bump aws-java-sdk-dynamodb from 1.11.929 to 1.11.1018 in /modules/dynalite (#4091) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/dynalite/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 3ac35862e1a..ea982a2d9ea 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Dynalite" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.929' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.1018' testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.929' testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From 992322b3437c8fc51fbf59992da92f9b58009c7d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 13:46:22 +0100 Subject: [PATCH 1868/5615] Bump r2dbc-postgresql from 0.8.5.RELEASE to 0.8.8.RELEASE in /modules/postgresql (#4092) Bumps [r2dbc-postgresql](https://github.com/r2dbc/r2dbc-postgresql) from 0.8.5.RELEASE to 0.8.8.RELEASE. - [Release notes](https://github.com/r2dbc/r2dbc-postgresql/releases) - [Changelog](https://github.com/pgjdbc/r2dbc-postgresql/blob/v0.8.8.RELEASE/CHANGELOG) - [Commits](https://github.com/r2dbc/r2dbc-postgresql/compare/v0.8.5.RELEASE...v0.8.8.RELEASE) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index b16ceaea2a8..f666394d733 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -7,7 +7,7 @@ dependencies { compile project(':jdbc') compileOnly project(':r2dbc') - compileOnly 'io.r2dbc:r2dbc-postgresql:0.8.5.RELEASE' + compileOnly 'io.r2dbc:r2dbc-postgresql:0.8.8.RELEASE' testCompile project(':jdbc-test') testCompile project(':test-support') From 4ac8dedaae07c7051ea26a59dc3645298fb1d4a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 13:46:34 +0100 Subject: [PATCH 1869/5615] Bump mariadb-java-client from 2.7.0 to 2.7.3 in /modules/mariadb (#4090) Bumps [mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 2.7.0 to 2.7.3. - [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases) - [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/2.7.3/CHANGELOG.md) - [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/2.7.0...2.7.3) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 915e14eb7ee..1424d032020 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'org.mariadb:r2dbc-mariadb:1.0.0' testCompile project(':jdbc-test') - testCompile 'org.mariadb.jdbc:mariadb-java-client:2.7.0' + testCompile 'org.mariadb.jdbc:mariadb-java-client:2.7.3' testCompile testFixtures(project(':r2dbc')) testCompile 'org.mariadb:r2dbc-mariadb:1.0.0' From 24a2ec9db9090b406c642f82410c2ae257989452 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 12:46:58 +0000 Subject: [PATCH 1870/5615] Bump r2dbc-mariadb from 1.0.0 to 1.0.1 in /modules/mariadb Bumps [r2dbc-mariadb](https://github.com/mariadb-corporation/mariadb-connector-r2dbc) from 1.0.0 to 1.0.1. - [Release notes](https://github.com/mariadb-corporation/mariadb-connector-r2dbc/releases) - [Changelog](https://github.com/mariadb-corporation/mariadb-connector-r2dbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/mariadb-corporation/mariadb-connector-r2dbc/compare/1.0.0...1.0.1) Signed-off-by: dependabot[bot] --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 1424d032020..ae55575d421 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -7,7 +7,7 @@ dependencies { compile project(':jdbc') compileOnly project(':r2dbc') - compileOnly 'org.mariadb:r2dbc-mariadb:1.0.0' + compileOnly 'org.mariadb:r2dbc-mariadb:1.0.1' testCompile project(':jdbc-test') testCompile 'org.mariadb.jdbc:mariadb-java-client:2.7.3' From 10056934e384c9fd2b40287f3e6e8109b460bda0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 13:52:16 +0100 Subject: [PATCH 1871/5615] Bump pulsar-client from 2.7.1 to 2.7.2 in /modules/pulsar (#4097) Bumps [pulsar-client](https://github.com/apache/pulsar) from 2.7.1 to 2.7.2. - [Release notes](https://github.com/apache/pulsar/releases) - [Commits](https://github.com/apache/pulsar/compare/v2.7.1...v2.7.2) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 7604a6712a6..10fe90dadf9 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Pulsar" dependencies { compile project(':testcontainers') - testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.7.1' + testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.7.2' testCompile group: 'org.assertj', name: 'assertj-core', version: '3.18.1' testCompile group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.7.2' } From ad348c4f7b2ff0bdf00043f09ff9f608005e1d59 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 13:52:24 +0100 Subject: [PATCH 1872/5615] Bump r2dbc-mssql from 0.8.5.RELEASE to 0.8.6.RELEASE in /modules/mssqlserver (#4089) Bumps [r2dbc-mssql](https://github.com/r2dbc/r2dbc-mssql) from 0.8.5.RELEASE to 0.8.6.RELEASE. - [Release notes](https://github.com/r2dbc/r2dbc-mssql/releases) - [Changelog](https://github.com/r2dbc/r2dbc-mssql/blob/v0.8.6.RELEASE/CHANGELOG) - [Commits](https://github.com/r2dbc/r2dbc-mssql/compare/v0.8.5.RELEASE...v0.8.6.RELEASE) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/mssqlserver/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index c36f1e6ba84..5471a3e3f68 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -7,7 +7,7 @@ dependencies { compile project(':jdbc') compileOnly project(':r2dbc') - compileOnly 'io.r2dbc:r2dbc-mssql:0.8.5.RELEASE' + compileOnly 'io.r2dbc:r2dbc-mssql:0.8.6.RELEASE' testCompile project(':jdbc-test') testCompile 'com.microsoft.sqlserver:mssql-jdbc:9.1.1.jre8-preview' From 4bcfbd7a7136394c5dd0682ddd30785562cc3ca4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 13:52:31 +0100 Subject: [PATCH 1873/5615] Bump testcontainers from 1.15.2 to 1.15.3 in /core (#4067) Bumps [testcontainers](https://github.com/testcontainers/testcontainers-java) from 1.15.2 to 1.15.3. - [Release notes](https://github.com/testcontainers/testcontainers-java/releases) - [Changelog](https://github.com/testcontainers/testcontainers-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-java/compare/1.15.2...1.15.3) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index e7a2710b796..cc62e5581c8 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -165,7 +165,7 @@ configurations.all { } dependencies { - baseline 'org.testcontainers:testcontainers:1.15.2', { + baseline 'org.testcontainers:testcontainers:1.15.3', { exclude group: "*", module: "*" } From 52af32194393374c623dff14811547e595bb9360 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 13:52:38 +0100 Subject: [PATCH 1874/5615] Bump transport from 7.10.1 to 7.12.1 in /modules/elasticsearch (#4050) Bumps [transport](https://github.com/elastic/elasticsearch) from 7.10.1 to 7.12.1. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.10.1...v7.12.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index f50b38bb568..fd3b99b491a 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,6 +3,6 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.12.0" - testCompile "org.elasticsearch.client:transport:7.10.1" + testCompile "org.elasticsearch.client:transport:7.12.1" testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 16d330c28b0d68ee6f3bd8a1cf0ce43403f367f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 13:52:52 +0100 Subject: [PATCH 1875/5615] Bump junit-jupiter-api from 5.7.0 to 5.7.1 in /modules/junit-jupiter (#3770) Bumps [junit-jupiter-api](https://github.com/junit-team/junit5) from 5.7.0 to 5.7.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.7.0...r5.7.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 15a5d665ee0..dc4650ada94 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -2,7 +2,7 @@ description = "Testcontainers :: JUnit Jupiter Extension" dependencies { compile project(':testcontainers') - compile 'org.junit.jupiter:junit-jupiter-api:5.7.0' + compile 'org.junit.jupiter:junit-jupiter-api:5.7.1' testCompile project(':mysql') testCompile project(':postgresql') From e93cefd83cc775b116efc44f6963e8cf6819a82d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 13:52:59 +0100 Subject: [PATCH 1876/5615] Bump junit from 4.13.1 to 4.13.2 in /examples (#3794) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/mongodb-container/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/mongodb-container/build.gradle b/examples/mongodb-container/build.gradle index 57b578a1607..8a4c727f069 100644 --- a/examples/mongodb-container/build.gradle +++ b/examples/mongodb-container/build.gradle @@ -8,6 +8,6 @@ repositories { dependencies { testImplementation 'org.testcontainers:testcontainers' - testImplementation 'junit:junit:4.13.1' + testImplementation 'junit:junit:4.13.2' testCompileClasspath 'org.jetbrains:annotations:20.1.0' } diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 702ce023c6b..de1e2d8a0a8 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -12,7 +12,7 @@ dependencies { implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' - testImplementation 'junit:junit:4.13.1' + testImplementation 'junit:junit:4.13.2' testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 4b6b23906d95c88cb76dff4cbfccfd8eedb4006d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 15:28:59 +0100 Subject: [PATCH 1877/5615] Bump junit-jupiter-params from 5.7.0 to 5.7.1 in /modules/junit-jupiter (#3769) Bumps [junit-jupiter-params](https://github.com/junit-team/junit5) from 5.7.0 to 5.7.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.7.0...r5.7.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index dc4650ada94..7e1b17356ca 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -13,7 +13,7 @@ dependencies { exclude(module: 'hamcrest-core') } testCompile 'org.assertj:assertj-core:3.18.1' - testCompile 'org.junit.jupiter:junit-jupiter-params:5.7.0' + testCompile 'org.junit.jupiter:junit-jupiter-params:5.7.1' testRuntime 'org.postgresql:postgresql:42.2.18' testRuntime 'mysql:mysql-connector-java:8.0.22' From 6dd2475e88fbccb9976744425ad7749337b510c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 15:29:11 +0100 Subject: [PATCH 1878/5615] Bump org.jetbrains.kotlin.plugin.spring from 1.4.21-2 to 1.4.32 in /examples (#3946) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Richard North --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 8ffa1b46d2a..6810020f17d 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,7 +1,7 @@ plugins { id("org.springframework.boot") version "2.4.1" id("org.jetbrains.kotlin.jvm") version "1.4.32" - id("org.jetbrains.kotlin.plugin.spring") version "1.4.21-2" + id("org.jetbrains.kotlin.plugin.spring") version "1.4.32" } apply plugin: 'io.spring.dependency-management' From c7bea124d433ebf6a10ee20363babb0103c0e99a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 15:29:25 +0100 Subject: [PATCH 1879/5615] Bump auto-service from 1.0-rc7 to 1.0 in /modules/mariadb (#3989) Bumps [auto-service](https://github.com/google/auto) from 1.0-rc7 to 1.0. - [Release notes](https://github.com/google/auto/releases) - [Commits](https://github.com/google/auto/commits/auto-value-1.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/mariadb/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 1424d032020..c70dfb93bfd 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -1,8 +1,8 @@ description = "Testcontainers :: JDBC :: MariaDB" dependencies { - annotationProcessor 'com.google.auto.service:auto-service:1.0-rc7' - compileOnly 'com.google.auto.service:auto-service:1.0-rc6' + annotationProcessor 'com.google.auto.service:auto-service:1.0' + compileOnly 'com.google.auto.service:auto-service:1.0' compile project(':jdbc') From e48b3550e65ccd7e8216d7484532dbbf5ed341d8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 15:29:31 +0100 Subject: [PATCH 1880/5615] Bump auto-service from 1.0-rc7 to 1.0 in /modules/mysql (#3988) Bumps [auto-service](https://github.com/google/auto) from 1.0-rc7 to 1.0. - [Release notes](https://github.com/google/auto/releases) - [Commits](https://github.com/google/auto/commits/auto-value-1.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/mysql/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 172ca29070d..5ac1ba945da 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -1,8 +1,8 @@ description = "Testcontainers :: JDBC :: MySQL" dependencies { - annotationProcessor 'com.google.auto.service:auto-service:1.0-rc7' - compileOnly 'com.google.auto.service:auto-service:1.0-rc6' + annotationProcessor 'com.google.auto.service:auto-service:1.0' + compileOnly 'com.google.auto.service:auto-service:1.0' compile project(':jdbc') From 36e2035a0d8a84be8e3256918282f7ca6bb49ac8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 15:29:37 +0100 Subject: [PATCH 1881/5615] Bump auto-service from 1.0-rc7 to 1.0 in /modules/postgresql (#3984) Bumps [auto-service](https://github.com/google/auto) from 1.0-rc7 to 1.0. - [Release notes](https://github.com/google/auto/releases) - [Commits](https://github.com/google/auto/commits/auto-value-1.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/postgresql/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index f666394d733..e853387291f 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -1,8 +1,8 @@ description = "Testcontainers :: JDBC :: PostgreSQL" dependencies { - annotationProcessor 'com.google.auto.service:auto-service:1.0-rc7' - compileOnly 'com.google.auto.service:auto-service:1.0-rc6' + annotationProcessor 'com.google.auto.service:auto-service:1.0' + compileOnly 'com.google.auto.service:auto-service:1.0' compile project(':jdbc') From 2a0de4457314ea157e2e76346f743193343dfab5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 15:29:50 +0100 Subject: [PATCH 1882/5615] Bump lombok from 1.18.16 to 1.18.20 in /examples (#3956) Bumps [lombok](https://github.com/rzwitserloot/lombok) from 1.18.16 to 1.18.20. - [Release notes](https://github.com/rzwitserloot/lombok/releases) - [Changelog](https://github.com/rzwitserloot/lombok/blob/master/doc/changelog.markdown) - [Commits](https://github.com/rzwitserloot/lombok/compare/v1.18.16...v1.18.20) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/disque-job-queue/build.gradle | 4 ++-- examples/kafka-cluster/build.gradle | 2 +- examples/solr-container/build.gradle | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index 90bbdd6d6f9..82d1bb548ee 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -7,8 +7,8 @@ repositories { } dependencies { - compileOnly "org.projectlombok:lombok:1.18.16" - annotationProcessor "org.projectlombok:lombok:1.18.14" + compileOnly "org.projectlombok:lombok:1.18.20" + annotationProcessor "org.projectlombok:lombok:1.18.20" implementation 'biz.paluch.redis:spinach:0.3' implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.guava:guava:23.0' diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index db07c6859f3..491b5c2f214 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - testCompileOnly "org.projectlombok:lombok:1.18.10" + testCompileOnly "org.projectlombok:lombok:1.18.20" testAnnotationProcessor "org.projectlombok:lombok:1.18.10" testCompile 'org.testcontainers:kafka' testCompile 'org.apache.kafka:kafka-clients:2.3.1' diff --git a/examples/solr-container/build.gradle b/examples/solr-container/build.gradle index 8c9fc2fcc11..b01d66ea9a9 100644 --- a/examples/solr-container/build.gradle +++ b/examples/solr-container/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly "org.projectlombok:lombok:1.18.16" + compileOnly "org.projectlombok:lombok:1.18.20" annotationProcessor "org.projectlombok:lombok:1.18.16" implementation 'org.apache.solr:solr-solrj:8.7.0' From c69f6f8adb79b29cb4b4460a423f38f3757e0f8f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 15:29:57 +0100 Subject: [PATCH 1883/5615] Bump json from 20201115 to 20210307 in /examples (#3884) Bumps [json](https://github.com/douglascrockford/JSON-java) from 20201115 to 20210307. - [Release notes](https://github.com/douglascrockford/JSON-java/releases) - [Commits](https://github.com/douglascrockford/JSON-java/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 5c7279dd09a..d3055dd3aa8 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' implementation 'com.squareup.okhttp3:okhttp:4.9.0' - implementation 'org.json:json:20201115' + implementation 'org.json:json:20210307' testImplementation 'org.postgresql:postgresql:42.2.18' testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.testcontainers:postgresql' From 5a942431836957d92a6d7ab1686e83e18b5f7e7f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 15:30:04 +0100 Subject: [PATCH 1884/5615] Bump commons-lang3 from 3.11 to 3.12.0 in /modules/jdbc-test (#3856) Bumps commons-lang3 from 3.11 to 3.12.0. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index b7a81682e37..3dd0d52f2e8 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -3,7 +3,7 @@ dependencies { compile project(':test-support') compile 'com.google.guava:guava:30.1-jre' - compile 'org.apache.commons:commons-lang3:3.11' + compile 'org.apache.commons:commons-lang3:3.12.0' compile 'com.zaxxer:HikariCP-java6:2.3.13' compile 'commons-dbutils:commons-dbutils:1.7' From c06520dbdb45bf51bc1ff7f3ac5c5adc360d4b07 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 14:30:13 +0000 Subject: [PATCH 1885/5615] Bump kafka-clients from 2.6.0 to 2.8.0 in /modules/kafka Bumps kafka-clients from 2.6.0 to 2.8.0. Signed-off-by: dependabot[bot] --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index 4454823bda4..db400c0b557 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Kafka" dependencies { compile project(':testcontainers') - testCompile 'org.apache.kafka:kafka-clients:2.6.0' + testCompile 'org.apache.kafka:kafka-clients:2.8.0' testCompile 'org.assertj:assertj-core:3.18.1' testCompile 'com.google.guava:guava:23.0' } From 858b1aa06e865d10451f47dd0042cbd5e0025265 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 14:30:23 +0000 Subject: [PATCH 1886/5615] Bump testng from 7.3.0 to 7.4.0 in /examples Bumps [testng](https://github.com/cbeust/testng) from 7.3.0 to 7.4.0. - [Release notes](https://github.com/cbeust/testng/releases) - [Changelog](https://github.com/cbeust/testng/blob/master/CHANGES.txt) - [Commits](https://github.com/cbeust/testng/compare/7.3.0...7.4.0) Signed-off-by: dependabot[bot] --- examples/redis-backed-cache-testng/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index d297f026323..42553843710 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -13,7 +13,7 @@ dependencies { implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'ch.qos.logback:logback-classic:1.2.3' - testImplementation 'org.testng:testng:7.3.0' + testImplementation 'org.testng:testng:7.4.0' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From f5e88058f6bc775e73f63ee833734790aff706fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 15:30:33 +0100 Subject: [PATCH 1887/5615] Bump junit-jupiter-engine from 5.7.0 to 5.7.1 in /modules/junit-jupiter (#3768) Bumps [junit-jupiter-engine](https://github.com/junit-team/junit5) from 5.7.0 to 5.7.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.7.0...r5.7.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 7e1b17356ca..70ba308092b 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -17,7 +17,7 @@ dependencies { testRuntime 'org.postgresql:postgresql:42.2.18' testRuntime 'mysql:mysql-connector-java:8.0.22' - testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.7.0' + testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.7.1' } test { From 0b8b301893532c10ec75d9e408260f9d7bfbdd0b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 14:30:35 +0000 Subject: [PATCH 1888/5615] Bump okhttp from 4.9.0 to 4.9.1 in /examples Bumps [okhttp](https://github.com/square/okhttp) from 4.9.0 to 4.9.1. - [Release notes](https://github.com/square/okhttp/releases) - [Changelog](https://github.com/square/okhttp/blob/master/CHANGELOG.md) - [Commits](https://github.com/square/okhttp/compare/parent-4.9.0...parent-4.9.1) Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index d3055dd3aa8..b3d339d4db8 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' - implementation 'com.squareup.okhttp3:okhttp:4.9.0' + implementation 'com.squareup.okhttp3:okhttp:4.9.1' implementation 'org.json:json:20210307' testImplementation 'org.postgresql:postgresql:42.2.18' testImplementation 'ch.qos.logback:logback-classic:1.2.3' From 0a2dfc4c95fa73757d1741fb1e4ea79ac36da46d Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 20 May 2021 13:00:34 +0100 Subject: [PATCH 1889/5615] Allow DOCKER_HOST and related settings to be set in `~/.testcontainers.properties` (#4118) --- ...dSystemPropertyClientProviderStrategy.java | 29 +++++- .../utility/TestcontainersConfiguration.java | 23 ++++- ...temPropertyClientProviderStrategyTest.java | 91 +++++++++++++++++++ .../TestcontainersConfigurationTest.java | 23 ++++- docs/features/configuration.md | 15 ++- 5 files changed, 171 insertions(+), 10 deletions(-) create mode 100644 core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java diff --git a/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java index e0afba9af93..e9bdbd90bea 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java @@ -2,10 +2,19 @@ import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.core.DockerClientConfig; +import org.testcontainers.utility.TestcontainersConfiguration; + +import java.util.Optional; /** * Use environment variables and system properties (as supported by the underlying DockerClient DefaultConfigBuilder) * to try and locate a docker environment. + *

+ * Resolution order is: + *

    + *
  1. DOCKER_HOST env var
  2. + *
  3. docker.host in ~/.testcontainers.properties
  4. + *
* * @deprecated this class is used by the SPI and should not be used directly */ @@ -14,12 +23,26 @@ public final class EnvironmentAndSystemPropertyClientProviderStrategy extends Do public static final int PRIORITY = 100; - // Try using environment variables - private final DockerClientConfig dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder().build(); + private final DockerClientConfig dockerClientConfig; + + public EnvironmentAndSystemPropertyClientProviderStrategy() { + // use docker-java defaults if present, overridden if our own configuration is set + DefaultDockerClientConfig.Builder configBuilder = DefaultDockerClientConfig.createDefaultConfigBuilder(); + + getSetting("docker.host").ifPresent(configBuilder::withDockerHost); + getSetting("docker.tls.verify").ifPresent(configBuilder::withDockerTlsVerify); + getSetting("docker.cert.path").ifPresent(configBuilder::withDockerCertPath); + + dockerClientConfig = configBuilder.build(); + } + + private Optional getSetting(final String name) { + return Optional.ofNullable(TestcontainersConfiguration.getInstance().getEnvVarOrUserProperty(name, null)); + } @Override protected boolean isApplicable() { - return System.getenv("DOCKER_HOST") != null; + return getSetting("docker.host").isPresent(); } @Override diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 3b4d9f8f70c..9b2ab707a2e 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -168,7 +168,11 @@ public boolean environmentSupportsReuse() { } public String getDockerClientStrategyClassName() { - return getEnvVarOrUserProperty("docker.client.strategy", null); + // getConfigurable won't apply the TESTCONTAINERS_ prefix when looking for env vars if DOCKER_ appears at the beginning. + // Because of this overlap, and the desire to not change this specific TESTCONTAINERS_DOCKER_CLIENT_STRATEGY setting, + // we special-case the logic here so that docker.client.strategy is used when reading properties files and + // TESTCONTAINERS_DOCKER_CLIENT_STRATEGY is used when searching environment variables. + return getEnvVarOrUserProperty("docker.client.strategy", environment.get("TESTCONTAINERS_DOCKER_CLIENT_STRATEGY")); } public String getTransportType() { @@ -187,7 +191,7 @@ public String getImageSubstitutorClassName() { @Contract("_, !null, _ -> !null") private String getConfigurable(@NotNull final String propertyName, @Nullable final String defaultValue, Properties... propertiesSources) { String envVarName = propertyName.replaceAll("\\.", "_").toUpperCase(); - if (!envVarName.startsWith("TESTCONTAINERS_")) { + if (!envVarName.startsWith("TESTCONTAINERS_") && !envVarName.startsWith("DOCKER_")) { envVarName = "TESTCONTAINERS_" + envVarName; } @@ -208,6 +212,10 @@ private String getConfigurable(@NotNull final String propertyName, @Nullable fin * Gets a configured setting from an environment variable (if present) or a configuration file property otherwise. * The configuration file will be the .testcontainers.properties file in the user's home directory or * a testcontainers.properties found on the classpath. + *

+ * Note that when searching environment variables, the prefix `TESTCONTAINERS_` will usually be applied to the + * property name, which will be converted to upper-case with underscore separators. This prefix will not be added + * if the property name begins `docker.`. * * @param propertyName name of configuration file property (dot-separated lower case) * @return the found value, or null if not set @@ -220,6 +228,10 @@ public String getEnvVarOrProperty(@NotNull final String propertyName, @Nullable /** * Gets a configured setting from an environment variable (if present) or a configuration file property otherwise. * The configuration file will be the .testcontainers.properties file in the user's home directory. + *

+ * Note that when searching environment variables, the prefix `TESTCONTAINERS_` will usually be applied to the + * property name, which will be converted to upper-case with underscore separators. This prefix will not be added + * if the property name begins `docker.`. * * @param propertyName name of configuration file property (dot-separated lower case) * @return the found value, or null if not set @@ -230,8 +242,11 @@ public String getEnvVarOrUserProperty(@NotNull final String propertyName, @Nulla } /** - * Gets a configured setting from a the user's configuration file. - * The configuration file will be the .testcontainers.properties file in the user's home directory. + * Gets a configured setting from an environment variable. + *

+ * Note that when searching environment variables, the prefix `TESTCONTAINERS_` will usually be applied to the + * property name, which will be converted to upper-case with underscore separators. This prefix will not be added + * if the property name begins `docker.`. * * @param propertyName name of configuration file property (dot-separated lower case) * @return the found value, or null if not set diff --git a/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java b/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java new file mode 100644 index 00000000000..91f61848d69 --- /dev/null +++ b/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java @@ -0,0 +1,91 @@ +package org.testcontainers.dockerclient; + +import com.github.dockerjava.core.DefaultDockerClientConfig; +import com.github.dockerjava.core.LocalDirectorySSLConfig; +import com.github.dockerjava.transport.SSLConfig; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.testcontainers.utility.MockTestcontainersConfigurationRule; +import org.testcontainers.utility.TestcontainersConfiguration; + +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; + +/** + * Test that we can use Testcontainers configuration file to override settings. We assume that docker-java has test + * coverage for detection of environment variables (e.g. DOCKER_HOST) and its own properties config file. + */ +@RunWith(MockitoJUnitRunner.class) +public class EnvironmentAndSystemPropertyClientProviderStrategyTest { + + @Rule + public MockTestcontainersConfigurationRule mockConfig = new MockTestcontainersConfigurationRule(); + private URI defaultDockerHost; + private com.github.dockerjava.core.SSLConfig defaultSSLConfig; + + @Before + public void checkEnvironmentClear() { + // If docker-java picks up non-default settings from the environment, our test needs to know to expect those + DefaultDockerClientConfig defaultConfig = DefaultDockerClientConfig.createDefaultConfigBuilder().build(); + defaultDockerHost = defaultConfig.getDockerHost(); + defaultSSLConfig = defaultConfig.getSSLConfig(); + } + + @Test + public void testWhenConfigAbsent() { + Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.host"), isNull()); + Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.tls.verify"), isNull()); + Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.cert.path"), isNull()); + + EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(); + + TransportConfig transportConfig = strategy.getTransportConfig(); + assertEquals(defaultDockerHost, transportConfig.getDockerHost()); + assertEquals(defaultSSLConfig, transportConfig.getSslConfig()); + } + + @Test + public void testWhenDockerHostPresent() { + Mockito.doReturn("tcp://1.2.3.4:2375").when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.host"), isNull()); + Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.tls.verify"), isNull()); + Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.cert.path"), isNull()); + + EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(); + + TransportConfig transportConfig = strategy.getTransportConfig(); + assertEquals("tcp://1.2.3.4:2375", transportConfig.getDockerHost().toString()); + assertEquals(defaultSSLConfig, transportConfig.getSslConfig()); + } + + @Test + public void testWhenDockerHostAndSSLConfigPresent() throws IOException { + Path tempDir = Files.createTempDirectory("testcontainers-test"); + String tempDirPath = tempDir.toAbsolutePath().toString(); + + Mockito.doReturn("tcp://1.2.3.4:2375").when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.host"), isNull()); + Mockito.doReturn("1").when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.tls.verify"), isNull()); + Mockito.doReturn(tempDirPath).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.cert.path"), isNull()); + + EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(); + + TransportConfig transportConfig = strategy.getTransportConfig(); + assertEquals("tcp://1.2.3.4:2375", transportConfig.getDockerHost().toString()); + + SSLConfig sslConfig = transportConfig.getSslConfig(); + assertNotNull(sslConfig); + assertTrue(sslConfig instanceof LocalDirectorySSLConfig); + assertEquals(tempDirPath, ((LocalDirectorySSLConfig) sslConfig).getDockerCertPath()); + } +} diff --git a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java index e5300bce58d..0fca36f0f2f 100644 --- a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java +++ b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java @@ -113,6 +113,27 @@ public void shouldReadChecksFromEnvironment() { assertTrue("checks disabled via env var", newConfig().isDisableChecks()); } + @Test + public void shouldReadDockerSettingsFromEnvironmentWithoutTestcontainersPrefix() { + userProperties.remove("docker.foo"); + environment.put("DOCKER_FOO", "some value"); + assertEquals("reads unprefixed env vars for docker. settings", "some value", newConfig().getEnvVarOrUserProperty("docker.foo", "default")); + } + + @Test + public void shouldNotReadDockerSettingsFromEnvironmentWithTestcontainersPrefix() { + userProperties.remove("docker.foo"); + environment.put("TESTCONTAINERS_DOCKER_FOO", "some value"); + assertEquals("reads unprefixed env vars for docker. settings", "default", newConfig().getEnvVarOrUserProperty("docker.foo", "default")); + } + + @Test + public void shouldReadDockerSettingsFromUserProperties() { + environment.remove("DOCKER_FOO"); + userProperties.put("docker.foo", "some value"); + assertEquals("reads unprefixed user properties for docker. settings", "some value", newConfig().getEnvVarOrUserProperty("docker.foo", "default")); + } + @Test public void shouldNotReadDockerClientStrategyFromClasspathProperties() { String currentValue = newConfig().getDockerClientStrategyClassName(); @@ -129,7 +150,7 @@ public void shouldReadDockerClientStrategyFromUserProperties() { @Test public void shouldReadDockerClientStrategyFromEnvironment() { - userProperties.remove("docker.client.strategy"); + userProperties.remove("docker.client.strategy"); environment.put("TESTCONTAINERS_DOCKER_CLIENT_STRATEGY", "foo"); assertEquals("Docker client strategy is changed by env var", "foo", newConfig().getDockerClientStrategyClassName()); } diff --git a/docs/features/configuration.md b/docs/features/configuration.md index da7bbd82d60..2336ec840c5 100644 --- a/docs/features/configuration.md +++ b/docs/features/configuration.md @@ -96,9 +96,9 @@ but does not allow starting privileged containers, you can turn off the Ryuk con ## Customizing Docker host detection -Testcontainers will attempt to detect the Docker environment and configure everything. +Testcontainers will attempt to detect the Docker environment and configure everything to work automatically. -However, sometimes a customization is required. For that, you can provide the following environment variables: +However, sometimes customization is required. Testcontainers will respect the following **environment variables**: > **DOCKER_HOST** = unix:///var/run/docker.sock > See [Docker environment variables](https://docs.docker.com/engine/reference/commandline/cli/#environment-variables) @@ -110,3 +110,14 @@ However, sometimes a customization is required. For that, you can provide the fo > **TESTCONTAINERS_HOST_OVERRIDE** > Docker's host on which ports are exposed. > Example: `docker.svc.local` + +For advanced users, the Docker host connection can be configured **via configuration** in `~/.testcontainers.properties`. +Note that these settings require use of the `EnvironmentAndSystemPropertyClientProviderStrategy`. The example below +illustrates usage: + +```properties +docker.client.strategy=org.testcontainers.dockerclient.EnvironmentAndSystemPropertyClientProviderStrategy +docker.host=tcp\://my.docker.host\:1234 # Equivalent to the DOCKER_HOST environment variable. Colons should be escaped. +docker.tls.verify=1 # Equivalent to the DOCKER_TLS_VERIFY environment variable +docker.cert.path=/some/path # Equivalent to the DOCKER_CERT_PATH environment variable +``` From 426c6239187c596651b6e40842dd08665fff02a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 May 2021 10:54:02 +0100 Subject: [PATCH 1890/5615] Bump postgresql from 42.2.18 to 42.2.20 in /modules/junit-jupiter (#4032) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 70ba308092b..69e13138e2a 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -15,7 +15,7 @@ dependencies { testCompile 'org.assertj:assertj-core:3.18.1' testCompile 'org.junit.jupiter:junit-jupiter-params:5.7.1' - testRuntime 'org.postgresql:postgresql:42.2.18' + testRuntime 'org.postgresql:postgresql:42.2.20' testRuntime 'mysql:mysql-connector-java:8.0.22' testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.7.1' } From e2b0e3c0327e7c8d787925337fed5a0defc42d58 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 May 2021 19:57:43 +0100 Subject: [PATCH 1891/5615] Bump org.jetbrains.kotlin.plugin.spring from 1.4.32 to 1.5.0 in /examples (#4115) Bumps org.jetbrains.kotlin.plugin.spring from 1.4.32 to 1.5.0. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 6810020f17d..6fb7b4c1d24 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,7 +1,7 @@ plugins { id("org.springframework.boot") version "2.4.1" id("org.jetbrains.kotlin.jvm") version "1.4.32" - id("org.jetbrains.kotlin.plugin.spring") version "1.4.32" + id("org.jetbrains.kotlin.plugin.spring") version "1.5.0" } apply plugin: 'io.spring.dependency-management' From bdbe1a457248a1d1b7fab8f22ef24279e63dca9e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 May 2021 19:57:58 +0100 Subject: [PATCH 1892/5615] Bump elasticsearch-rest-client from 7.12.0 to 7.12.1 in /modules/elasticsearch (#4049) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index fd3b99b491a..b7fbda347a1 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,7 +2,7 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') - testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.12.0" + testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.12.1" testCompile "org.elasticsearch.client:transport:7.12.1" testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From f01d0e5f91da28cffaae4189eef00912a8bc0ade Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 May 2021 19:58:05 +0100 Subject: [PATCH 1893/5615] Bump mssql-jdbc from 9.1.1.jre8-preview to 9.3.1.jre8-preview in /modules/mssqlserver (#4048) Bumps [mssql-jdbc](https://github.com/Microsoft/mssql-jdbc) from 9.1.1.jre8-preview to 9.3.1.jre8-preview. - [Release notes](https://github.com/Microsoft/mssql-jdbc/releases) - [Changelog](https://github.com/microsoft/mssql-jdbc/blob/dev/CHANGELOG.md) - [Commits](https://github.com/Microsoft/mssql-jdbc/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/mssqlserver/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index 5471a3e3f68..10699e508e5 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -10,12 +10,12 @@ dependencies { compileOnly 'io.r2dbc:r2dbc-mssql:0.8.6.RELEASE' testCompile project(':jdbc-test') - testCompile 'com.microsoft.sqlserver:mssql-jdbc:9.1.1.jre8-preview' + testCompile 'com.microsoft.sqlserver:mssql-jdbc:9.3.1.jre8-preview' testCompile project(':r2dbc') testCompile 'io.r2dbc:r2dbc-mssql:0.8.5.RELEASE' // MSSQL's wait strategy requires the JDBC driver testCompile testFixtures(project(':r2dbc')) - testCompile 'com.microsoft.sqlserver:mssql-jdbc:9.1.1.jre8-preview' + testCompile 'com.microsoft.sqlserver:mssql-jdbc:9.3.1.jre8-preview' } From 4735b7441ecd975e4fcafb8e02ca54fd9946dad0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 May 2021 19:58:16 +0100 Subject: [PATCH 1894/5615] Bump jedis from 3.4.0 to 3.6.0 in /core (#4019) Bumps [jedis](https://github.com/redis/jedis) from 3.4.0 to 3.6.0. - [Release notes](https://github.com/redis/jedis/releases) - [Commits](https://github.com/redis/jedis/compare/jedis-3.4.0...jedis-3.6.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index cc62e5581c8..059202e1b41 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -206,7 +206,7 @@ dependencies { } testCompile 'org.apache.httpcomponents:httpclient:4.5.9' - testCompile 'redis.clients:jedis:3.4.0' + testCompile 'redis.clients:jedis:3.6.0' testCompile 'com.rabbitmq:amqp-client:5.9.0' testCompile 'org.mongodb:mongo-java-driver:3.12.7' From 5b4609a406ef1c353f0ba651c019c5f16ef283c4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 May 2021 19:58:29 +0100 Subject: [PATCH 1895/5615] Bump jedis from 3.3.0 to 3.6.0 in /modules/junit-jupiter (#4028) Bumps [jedis](https://github.com/redis/jedis) from 3.3.0 to 3.6.0. - [Release notes](https://github.com/redis/jedis/releases) - [Commits](https://github.com/redis/jedis/compare/jedis-3.3.0...jedis-3.6.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 69e13138e2a..99789bf2721 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -7,7 +7,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') testCompile 'com.zaxxer:HikariCP:3.4.5' - testCompile 'redis.clients:jedis:3.3.0' + testCompile 'redis.clients:jedis:3.6.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.13' testCompile ('org.mockito:mockito-core:3.10.0') { exclude(module: 'hamcrest-core') From e86454a7b71b2c4c6431fc3d604e913040e43361 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 May 2021 19:58:39 +0100 Subject: [PATCH 1896/5615] Bump auto-service from 1.0-rc7 to 1.0 in /modules/mssqlserver (#3983) Bumps [auto-service](https://github.com/google/auto) from 1.0-rc7 to 1.0. - [Release notes](https://github.com/google/auto/releases) - [Commits](https://github.com/google/auto/commits/auto-value-1.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/mssqlserver/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index 10699e508e5..143a6582ae1 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -1,8 +1,8 @@ description = "Testcontainers :: MS SQL Server" dependencies { - annotationProcessor 'com.google.auto.service:auto-service:1.0-rc7' - compileOnly 'com.google.auto.service:auto-service:1.0-rc6' + annotationProcessor 'com.google.auto.service:auto-service:1.0' + compileOnly 'com.google.auto.service:auto-service:1.0' compile project(':jdbc') From 1719cf5a5840bf3ee8f1575c4004d2b991e78fad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 May 2021 19:58:49 +0100 Subject: [PATCH 1897/5615] Bump solr-solrj from 8.7.0 to 8.8.2 in /examples (#4005) Bumps solr-solrj from 8.7.0 to 8.8.2. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/solr-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/solr-container/build.gradle b/examples/solr-container/build.gradle index b01d66ea9a9..e0a6f356a77 100644 --- a/examples/solr-container/build.gradle +++ b/examples/solr-container/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly "org.projectlombok:lombok:1.18.20" annotationProcessor "org.projectlombok:lombok:1.18.16" - implementation 'org.apache.solr:solr-solrj:8.7.0' + implementation 'org.apache.solr:solr-solrj:8.8.2' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.testcontainers:solr' From cd91b0e4f824e9bb215b5caffabbdf13125de078 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 May 2021 19:58:59 +0100 Subject: [PATCH 1898/5615] Bump HikariCP from 3.4.5 to 4.0.3 in /modules/spock (#3857) Bumps [HikariCP](https://github.com/brettwooldridge/HikariCP) from 3.4.5 to 4.0.3. - [Release notes](https://github.com/brettwooldridge/HikariCP/releases) - [Changelog](https://github.com/brettwooldridge/HikariCP/blob/dev/CHANGES) - [Commits](https://github.com/brettwooldridge/HikariCP/compare/HikariCP-3.4.5...HikariCP-4.0.3) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index d0665614b17..07ac5c7ce3b 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -12,7 +12,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') - testCompile 'com.zaxxer:HikariCP:3.4.5' + testCompile 'com.zaxxer:HikariCP:4.0.3' testCompile 'org.apache.httpcomponents:httpclient:4.5.13' testRuntime 'org.postgresql:postgresql:42.2.18' From e83895fde8dfd851d8c487e0595d7e4d17a95277 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 May 2021 19:59:18 +0100 Subject: [PATCH 1899/5615] Bump mysql-connector-java from 8.0.22 to 8.0.25 in /modules/mysql (#4086) Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.22 to 8.0.25. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.22...8.0.25) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 5ac1ba945da..c6823e247d4 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' testCompile project(':jdbc-test') - testCompile 'mysql:mysql-connector-java:8.0.22' + testCompile 'mysql:mysql-connector-java:8.0.25' testCompile testFixtures(project(':r2dbc')) testCompile 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' From 73d7fd0f28b03c434c24d57c4e0af16e98259c26 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 May 2021 20:00:48 +0100 Subject: [PATCH 1900/5615] Bump assertj-core from 3.18.1 to 3.19.0 in /modules/neo4j (#3716) Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.18.1 to 3.19.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.18.1...assertj-core-3.19.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 9aab6b18298..722e5474388 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -34,5 +34,5 @@ dependencies { compile project(":testcontainers") testCompile "org.neo4j.driver:neo4j-java-driver:1.7.5" - testCompile 'org.assertj:assertj-core:3.18.1' + testCompile 'org.assertj:assertj-core:3.19.0' } From 3167adc033ac3e1d2cf050e1ef83c2b9bc43afb6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 May 2021 20:00:55 +0100 Subject: [PATCH 1901/5615] Bump assertj-core from 3.18.1 to 3.19.0 in /modules/vault (#3722) Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.18.1 to 3.19.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.18.1...assertj-core-3.19.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index e75d1121131..72dc2811292 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -5,6 +5,6 @@ dependencies { testCompile 'com.bettercloud:vault-java-driver:5.1.0' testCompile 'io.rest-assured:rest-assured:4.3.3' - testCompile 'org.assertj:assertj-core:3.18.1' + testCompile 'org.assertj:assertj-core:3.19.0' } From 96b2065f27b259530766d968f12339e64b980bc9 Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 26 May 2021 06:55:18 +0100 Subject: [PATCH 1902/5615] Only publish exposed ports (#4122) --- .../containers/GenericContainer.java | 107 ++++++++++-------- .../containers/GenericContainerTest.java | 61 +++++++++- .../containers/ClickHouseContainer.java | 6 +- .../couchbase/CouchbaseContainer.java | 13 +++ .../containers/MariaDBContainer.java | 7 +- .../containers/MSSQLServerContainer.java | 6 +- .../containers/OracleContainer.java | 6 +- 7 files changed, 147 insertions(+), 59 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index e53af6240ab..10f8e1e7195 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -1,8 +1,5 @@ package org.testcontainers.containers; -import static com.google.common.collect.Lists.newArrayList; -import static org.testcontainers.utility.CommandLine.runShellCommand; - import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.SerializationFeature; @@ -16,6 +13,7 @@ import com.github.dockerjava.api.model.HostConfig; import com.github.dockerjava.api.model.Link; import com.github.dockerjava.api.model.PortBinding; +import com.github.dockerjava.api.model.Ports; import com.github.dockerjava.api.model.Volume; import com.github.dockerjava.api.model.VolumesFrom; import com.github.dockerjava.core.DefaultDockerClientConfig; @@ -23,39 +21,6 @@ import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; import com.google.common.hash.Hashing; -import java.io.File; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.UndeclaredThrowableException; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.time.Duration; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import java.util.zip.Adler32; -import java.util.zip.Checksum; import lombok.AccessLevel; import lombok.Data; import lombok.NonNull; @@ -97,6 +62,43 @@ import org.testcontainers.utility.ResourceReaper; import org.testcontainers.utility.TestcontainersConfiguration; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.UndeclaredThrowableException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.zip.Adler32; +import java.util.zip.Checksum; + +import static com.google.common.collect.Lists.newArrayList; +import static org.testcontainers.utility.CommandLine.runShellCommand; + /** * Base class for that allows a container to be launched and controlled. */ @@ -718,21 +720,28 @@ public Set getLivenessCheckPortNumbers() { private void applyConfiguration(CreateContainerCmd createCommand) { HostConfig hostConfig = buildHostConfig(); - createCommand.withHostConfig(hostConfig); - - // Set up exposed ports (where there are no host port bindings defined) - ExposedPort[] portArray = exposedPorts.stream() - .map(ExposedPort::new) - .toArray(ExposedPort[]::new); - createCommand.withExposedPorts(portArray); + // PortBindings must contain: + // * all exposed ports with a randomized host port (equivalent to -p CONTAINER_PORT) + // * all exposed ports with a fixed host port (equivalent to -p HOST_PORT:CONTAINER_PORT) + Map allPortBindings = new HashMap<>(); + // First collect all the randomized host ports from our 'exposedPorts' field + for (final Integer tcpPort : exposedPorts) { + ExposedPort exposedPort = ExposedPort.tcp(tcpPort); + allPortBindings.put(exposedPort, new PortBinding(Ports.Binding.empty(), exposedPort)); + } + // Next collect all the fixed host ports from our 'portBindings' field, overwriting any randomized ports so that + // we don't create two bindings for the same container port. + for (final String portBinding : portBindings) { + PortBinding parsedBinding = PortBinding.parse(portBinding); + allPortBindings.put(parsedBinding.getExposedPort(), parsedBinding); + } + hostConfig.withPortBindings(new ArrayList<>(allPortBindings.values())); - // Set up exposed ports that need host port bindings - PortBinding[] portBindingArray = portBindings.stream() - .map(PortBinding::parse) - .toArray(PortBinding[]::new); + // Next, ExposedPorts must be set up to publish all of the above ports, both randomized and fixed. + createCommand.withExposedPorts(new ArrayList<>(allPortBindings.keySet())); - createCommand.withPortBindings(portBindingArray); + createCommand.withHostConfig(hostConfig); if (commandParts != null) { createCommand.withCmd(commandParts); @@ -798,8 +807,6 @@ private void applyConfiguration(CreateContainerCmd createCommand) { createCommand.withNetworkMode(networkForLinks.get()); } - createCommand.withPublishAllPorts(true); - PortForwardingContainer.INSTANCE.getNetwork().ifPresent(it -> { withExtraHost(INTERNAL_HOST_HOSTNAME, it.getIpAddress()); }); diff --git a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java index e059a48709f..1e2cb041cf1 100644 --- a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java +++ b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java @@ -1,8 +1,12 @@ package org.testcontainers.containers; import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.command.InspectContainerResponse.ContainerState; +import com.github.dockerjava.api.model.ExposedPort; import com.github.dockerjava.api.model.Info; +import com.github.dockerjava.api.model.Ports; +import com.google.common.primitives.Ints; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.experimental.FieldDefaults; @@ -11,15 +15,23 @@ import org.assertj.core.api.Assumptions; import org.junit.Test; import org.rnorth.ducttape.unreliables.Unreliables; -import org.testcontainers.TestImages; import org.testcontainers.DockerClientFactory; +import org.testcontainers.TestImages; import org.testcontainers.containers.startupcheck.StartupCheckStrategy; import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy; +import org.testcontainers.images.builder.ImageFromDockerfile; +import java.util.Arrays; +import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; +import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; public class GenericContainerTest { @@ -60,6 +72,53 @@ public void shouldReportErrorAfterWait() { } } + @Test + public void shouldOnlyPublishExposedPorts() { + ImageFromDockerfile image = new ImageFromDockerfile("publish-multiple") + .withDockerfileFromBuilder(builder -> + builder + .from("testcontainers/helloworld:1.1.0") + .expose(8080, 8081) + .build() + ); + try (GenericContainer container = new GenericContainer<>(image).withExposedPorts(8080)) { + container.start(); + + InspectContainerResponse inspectedContainer = container.getContainerInfo(); + + List exposedPorts = Arrays.stream(inspectedContainer.getConfig().getExposedPorts()) + .map(ExposedPort::getPort) + .collect(Collectors.toList()); + + assertEquals( + "the exposed ports are all of those EXPOSEd by the image", + Ints.asList(8080, 8081), + exposedPorts + ); + + Map hostBindings = inspectedContainer.getHostConfig().getPortBindings().getBindings(); + assertEquals( + "only 1 port is bound on the host (published)", + 1, + hostBindings.size() + ); + + Integer mappedPort = container.getMappedPort(8080); + assertTrue( + "port 8080 is bound to a different port on the host", + mappedPort != 8080 + ); + + assertThrows( + "trying to get a non-bound port mapping fails", + IllegalArgumentException.class, + () -> { + container.getMappedPort(8081); + } + ); + } + } + static class NoopStartupCheckStrategy extends StartupCheckStrategy { @Override diff --git a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java index e26ca8c8416..9b5f130a323 100644 --- a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java +++ b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java @@ -1,9 +1,11 @@ package org.testcontainers.containers; +import com.google.common.collect.Sets; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.utility.DockerImageName; import java.time.Duration; +import java.util.Set; public class ClickHouseContainer extends JdbcDatabaseContainer { public static final String NAME = "clickhouse"; @@ -54,8 +56,8 @@ public ClickHouseContainer(final DockerImageName dockerImageName) { } @Override - protected Integer getLivenessCheckPort() { - return getMappedPort(HTTP_PORT); + public Set getLivenessCheckPortNumbers() { + return Sets.newHashSet(HTTP_PORT); } @Override diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index 3671608d324..8df5844f538 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -166,6 +166,19 @@ public String getConnectionString() { protected void configure() { super.configure(); + addExposedPorts( + MGMT_PORT, + MGMT_SSL_PORT, + VIEW_PORT, + VIEW_SSL_PORT, + QUERY_PORT, + QUERY_SSL_PORT, + SEARCH_PORT, + SEARCH_SSL_PORT, + KV_PORT, + KV_SSL_PORT + ); + WaitAllStrategy waitStrategy = new WaitAllStrategy(); // Makes sure that all nodes in the cluster are healthy. diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java index b43a5f237e5..2e28e5d0480 100644 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java @@ -1,7 +1,10 @@ package org.testcontainers.containers; +import com.google.common.collect.Sets; import org.testcontainers.utility.DockerImageName; +import java.util.Set; + /** * Container implementation for the MariaDB project. * @@ -51,8 +54,8 @@ public MariaDBContainer(final DockerImageName dockerImageName) { } @Override - protected Integer getLivenessCheckPort() { - return getMappedPort(MARIADB_PORT); + public Set getLivenessCheckPortNumbers() { + return Sets.newHashSet(MARIADB_PORT); } @Override diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java index a20c60fd2dc..ecccc320b28 100644 --- a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java @@ -1,8 +1,10 @@ package org.testcontainers.containers; +import com.google.common.collect.Sets; import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.LicenseAcceptance; +import java.util.Set; import java.util.regex.Pattern; import java.util.stream.Stream; @@ -60,8 +62,8 @@ public MSSQLServerContainer(final DockerImageName dockerImageName) { } @Override - protected Integer getLivenessCheckPort() { - return getMappedPort(MS_SQL_SERVER_PORT); + public Set getLivenessCheckPortNumbers() { + return Sets.newHashSet(MS_SQL_SERVER_PORT); } @Override diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java index 0a66ad6fd27..2573ef722e3 100644 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java @@ -1,8 +1,10 @@ package org.testcontainers.containers; +import com.google.common.collect.Sets; import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.TestcontainersConfiguration; +import java.util.Set; import java.util.concurrent.Future; /** @@ -61,8 +63,8 @@ private void preconfigure() { } @Override - protected Integer getLivenessCheckPort() { - return getMappedPort(ORACLE_PORT); + public Set getLivenessCheckPortNumbers() { + return Sets.newHashSet(ORACLE_PORT); } @Override From 841ce8e6bec55b1d9717c2c6e60a0df5290f4859 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 05:08:45 +0000 Subject: [PATCH 1903/5615] Bump org.jetbrains.kotlin.plugin.spring in /examples Bumps org.jetbrains.kotlin.plugin.spring from 1.5.0 to 1.5.10. Signed-off-by: dependabot[bot] --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 6fb7b4c1d24..6617e830e8b 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,7 +1,7 @@ plugins { id("org.springframework.boot") version "2.4.1" id("org.jetbrains.kotlin.jvm") version "1.4.32" - id("org.jetbrains.kotlin.plugin.spring") version "1.5.0" + id("org.jetbrains.kotlin.plugin.spring") version "1.5.10" } apply plugin: 'io.spring.dependency-management' From 8289f3a21d1f2262952faf302ed0950442c5b78f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 05:09:35 +0000 Subject: [PATCH 1904/5615] Bump rest-assured from 4.3.3 to 4.4.0 in /modules/vault Bumps [rest-assured](https://github.com/rest-assured/rest-assured) from 4.3.3 to 4.4.0. - [Release notes](https://github.com/rest-assured/rest-assured/releases) - [Changelog](https://github.com/rest-assured/rest-assured/blob/master/changelog.txt) - [Commits](https://github.com/rest-assured/rest-assured/compare/rest-assured-4.3.3...rest-assured-4.4.0) Signed-off-by: dependabot[bot] --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index 72dc2811292..17edbd3f3fd 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -4,7 +4,7 @@ dependencies { compile project(':testcontainers') testCompile 'com.bettercloud:vault-java-driver:5.1.0' - testCompile 'io.rest-assured:rest-assured:4.3.3' + testCompile 'io.rest-assured:rest-assured:4.4.0' testCompile 'org.assertj:assertj-core:3.19.0' } From d992d127fd8bcc1b5fda02a004ca132963ab4f03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 05:11:22 +0000 Subject: [PATCH 1905/5615] Bump aws-java-sdk-dynamodb in /modules/dynalite Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.11.1018 to 1.11.1030. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.1018...1.11.1030) Signed-off-by: dependabot[bot] --- modules/dynalite/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index ea982a2d9ea..58a16a0b62b 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Dynalite" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.1018' - testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.929' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.1030' + testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.1030' testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From ae2d57934e635df1ed8e20a5d89a02a521dd1fd9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 05:11:42 +0000 Subject: [PATCH 1906/5615] Bump elasticsearch-rest-client in /modules/elasticsearch Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 7.12.1 to 7.13.0. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.12.1...v7.13.0) Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index b7fbda347a1..21db8e69545 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,7 +2,7 @@ description = "TestContainers :: elasticsearch" dependencies { compile project(':testcontainers') - testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.12.1" + testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.13.0" testCompile "org.elasticsearch.client:transport:7.12.1" testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From b82fa09c00c8cfe40748e93b1a4ddc433da48206 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 05:11:49 +0000 Subject: [PATCH 1907/5615] Bump aws-java-sdk-s3 from 1.11.930 to 1.11.1030 in /modules/localstack Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.11.930 to 1.11.1030. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.930...1.11.1030) Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 15f34e28d76..e6a6340f686 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Localstack" dependencies { compile project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.930' - testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.929' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.1030' + testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.1030' testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.1018' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' testCompile 'software.amazon.awssdk:s3:2.16.62' From 371125469210d1de9d3e602dced904350ce8df35 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 05:30:06 +0000 Subject: [PATCH 1908/5615] Bump actions/setup-java from 1 to 2.1.0 Bumps [actions/setup-java](https://github.com/actions/setup-java) from 1 to 2.1.0. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v1...v2.1.0) Signed-off-by: dependabot[bot] --- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index cea7180566d..e3f540065e3 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -15,7 +15,7 @@ jobs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v1 + - uses: actions/setup-java@v2.1.0 with: java-version: '1.8' - name: Cache Gradle Home files @@ -42,7 +42,7 @@ jobs: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v1 + - uses: actions/setup-java@v2.1.0 with: java-version: '1.8' - name: Cache Gradle Home files diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f49b6426a2e..727c89f22f4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v1 + - uses: actions/setup-java@v2.1.0 with: java-version: '1.8' - name: Cache Gradle Home files @@ -40,7 +40,7 @@ jobs: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v1 + - uses: actions/setup-java@v2.1.0 with: java-version: '1.8' - name: Cache Gradle Home files From 69ead41b3d4fff61b4a1fe891b5f80335affae49 Mon Sep 17 00:00:00 2001 From: Oleg Nenashev Date: Mon, 7 Jun 2021 00:27:24 +0200 Subject: [PATCH 1909/5615] Add @oleg-nenashev as backer, reference @jenkinsci and @cloudbees as adopters (#4170) --- docs/index.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/index.md b/docs/index.md index e58a23bc330..139cbcd3f4e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -139,6 +139,7 @@ A huge thank you to our sponsors: * [Pascal Zwick (@pas2al)](https://github.com/pas2al) * [Nikita Zhevnitskiy (@zhenik)](https://github.com/zhenik) * [Bas Stoker (@bastoker)](https://github.com/bastoker) +* [Oleg Nenashev (@oleg-nenashev)](https://github.com/oleg-nenashev) ## Who is using Testcontainers? @@ -173,6 +174,9 @@ A huge thank you to our sponsors: * [Trino (formerly Presto SQL)](https://trino.io) - Integration testing all Trino core & connectors, including tests of multi-node deployments and security configurations. * Google - Various open source projects: [OpenTelemetry](https://github.com/GoogleCloudPlatform/opentelemetry-operations-java), [Universal Application Tool](https://github.com/seattle-uat/universal-application-tool), [CloudBowl](https://github.com/GoogleCloudPlatform/cloudbowl-microservice-game) * [Backbase](https://www.backbase.com/) - Unit, Integration and Acceptance testing for different the databases supported (Oracle, SQL Server, MySQL), the different messaging systems supported (Kafka, Rabbit, AMQ) and other microservices and HTTP mocks. +* [CloudBees](https://www.cloudbees.com/) - Integration testing of products, including but not limited to database and AWS/Localstack integration testing. +* [Jenkins](https://www.jenkins.io/) - Integration testing of multiple plugins and the Trilead SSH2 fork maintained by the Jenkins community + ([query](https://github.com/search?l=Maven+POM&q=org%3Ajenkinsci+testcontainers&type=Code)). ## License From b4bafc9a039aa0adf865c33293b89d7fdbe01846 Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 7 Jun 2021 09:16:15 +0100 Subject: [PATCH 1910/5615] Combined dependencies PR (#4135) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/database-commons/build.gradle | 2 +- modules/jdbc/build.gradle | 2 +- modules/junit-jupiter/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/database-commons/build.gradle b/modules/database-commons/build.gradle index dacd8a500ac..0eecc1844d0 100644 --- a/modules/database-commons/build.gradle +++ b/modules/database-commons/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: Database-Commons" dependencies { compile project(':testcontainers') - testCompile 'org.assertj:assertj-core:3.18.1' + testCompile 'org.assertj:assertj-core:3.19.0' } diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 8f2d8954985..992f7611de4 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -7,7 +7,7 @@ dependencies { compileOnly 'org.jetbrains:annotations:20.1.0' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.vibur:vibur-dbcp:25.0' - testCompile 'org.apache.tomcat:tomcat-jdbc:10.0.0' + testCompile 'org.apache.tomcat:tomcat-jdbc:10.0.6' testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'com.googlecode.junit-toolbox:junit-toolbox:2.4' testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 99789bf2721..393c4cac653 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -17,7 +17,7 @@ dependencies { testRuntime 'org.postgresql:postgresql:42.2.20' testRuntime 'mysql:mysql-connector-java:8.0.22' - testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.7.1' + testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.7.2' } test { From 57f4802095e73a25266b827dc357fbd71521f0aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Jun 2021 08:39:30 +0000 Subject: [PATCH 1911/5615] Bump annotations from 20.1.0 to 21.0.1 in /modules/spock Bumps [annotations](https://github.com/JetBrains/java-annotations) from 20.1.0 to 21.0.1. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/20.1.0...21.0.1) Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 07ac5c7ce3b..8c065e389cd 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -18,7 +18,7 @@ dependencies { testRuntime 'org.postgresql:postgresql:42.2.18' testRuntime 'mysql:mysql-connector-java:8.0.25' - testCompileClasspath 'org.jetbrains:annotations:20.1.0' + testCompileClasspath 'org.jetbrains:annotations:21.0.1' } sourceJar { From 1c1bc81de3810a1a5c1417c3cd00842a452b314b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Jun 2021 08:39:41 +0000 Subject: [PATCH 1912/5615] Bump annotations from 20.1.0 to 21.0.1 in /modules/mysql Bumps [annotations](https://github.com/JetBrains/java-annotations) from 20.1.0 to 21.0.1. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/20.1.0...21.0.1) Signed-off-by: dependabot[bot] --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index c6823e247d4..91a7d2c9918 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -15,5 +15,5 @@ dependencies { testCompile testFixtures(project(':r2dbc')) testCompile 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' - compileOnly 'org.jetbrains:annotations:20.1.0' + compileOnly 'org.jetbrains:annotations:21.0.1' } From 80491db9816e6afa8406b4aef1626ae23849c2fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Jun 2021 08:44:39 +0000 Subject: [PATCH 1913/5615] Bump assertj-core from 3.16.1 to 3.19.0 in /modules/jdbc Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.16.1 to 3.19.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.16.1...assertj-core-3.19.0) Signed-off-by: dependabot[bot] --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 992f7611de4..2741a348f34 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -11,5 +11,5 @@ dependencies { testCompile 'com.zaxxer:HikariCP-java6:2.3.13' testCompile 'com.googlecode.junit-toolbox:junit-toolbox:2.4' testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' - testCompile 'org.assertj:assertj-core:3.16.1' + testCompile 'org.assertj:assertj-core:3.19.0' } From 9a3bff0b3ecb55ca45eca292515c1410b2c606fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Jun 2021 14:29:12 +0100 Subject: [PATCH 1914/5615] Bump actions/cache from 2.1.3 to 2.1.6 (#4162) Bumps [actions/cache](https://github.com/actions/cache) from 2.1.3 to 2.1.6. - [Release notes](https://github.com/actions/cache/releases) - [Commits](https://github.com/actions/cache/compare/v2.1.3...v2.1.6) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index cea7180566d..d3b9f7811a2 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -19,7 +19,7 @@ jobs: with: java-version: '1.8' - name: Cache Gradle Home files - uses: actions/cache@v2.1.3 + uses: actions/cache@v2.1.6 continue-on-error: true with: path: ~/.gradle/caches @@ -46,7 +46,7 @@ jobs: with: java-version: '1.8' - name: Cache Gradle Home files - uses: actions/cache@v2.1.3 + uses: actions/cache@v2.1.6 continue-on-error: true with: path: ~/.gradle/caches diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f49b6426a2e..409aff3da98 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: with: java-version: '1.8' - name: Cache Gradle Home files - uses: actions/cache@v2.1.3 + uses: actions/cache@v2.1.6 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-home-testmatrix-${{ hashFiles('**/*.gradle') }} @@ -44,7 +44,7 @@ jobs: with: java-version: '1.8' - name: Cache Gradle Home files - uses: actions/cache@v2.1.3 + uses: actions/cache@v2.1.6 continue-on-error: true with: path: ~/.gradle/caches From 3844712431165501f9238cc30a6c6d7c34fb64fa Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 8 Jun 2021 14:31:27 +0100 Subject: [PATCH 1915/5615] Add `distribution` config to fix build --- .github/workflows/ci-examples.yml | 2 ++ .github/workflows/ci.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index e3f540065e3..cd696de7edd 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -18,6 +18,7 @@ jobs: - uses: actions/setup-java@v2.1.0 with: java-version: '1.8' + distribution: adopt-hotspot - name: Cache Gradle Home files uses: actions/cache@v2.1.3 continue-on-error: true @@ -45,6 +46,7 @@ jobs: - uses: actions/setup-java@v2.1.0 with: java-version: '1.8' + distribution: adopt-hotspot - name: Cache Gradle Home files uses: actions/cache@v2.1.3 continue-on-error: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 727c89f22f4..0bc9d517322 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,6 +18,7 @@ jobs: - uses: actions/setup-java@v2.1.0 with: java-version: '1.8' + distribution: adopt-hotspot - name: Cache Gradle Home files uses: actions/cache@v2.1.3 with: @@ -43,6 +44,7 @@ jobs: - uses: actions/setup-java@v2.1.0 with: java-version: '1.8' + distribution: adopt-hotspot - name: Cache Gradle Home files uses: actions/cache@v2.1.3 continue-on-error: true From 669cc53419c30b7d211f44fb93802a34729b8c20 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 8 Jun 2021 16:55:01 +0100 Subject: [PATCH 1916/5615] Change java-version format --- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index cd696de7edd..83c74833563 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -17,7 +17,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-java@v2.1.0 with: - java-version: '1.8' + java-version: '8.0.292+10' distribution: adopt-hotspot - name: Cache Gradle Home files uses: actions/cache@v2.1.3 @@ -45,7 +45,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-java@v2.1.0 with: - java-version: '1.8' + java-version: '8.0.292+10' distribution: adopt-hotspot - name: Cache Gradle Home files uses: actions/cache@v2.1.3 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0bc9d517322..a84b9fa6723 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-java@v2.1.0 with: - java-version: '1.8' + java-version: '8.0.292+10' distribution: adopt-hotspot - name: Cache Gradle Home files uses: actions/cache@v2.1.3 @@ -43,7 +43,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-java@v2.1.0 with: - java-version: '1.8' + java-version: '8.0.292+10' distribution: adopt-hotspot - name: Cache Gradle Home files uses: actions/cache@v2.1.3 From 24610cbae71b3d27f9df1ae64678ee0c6012331e Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 8 Jun 2021 17:11:21 +0100 Subject: [PATCH 1917/5615] Add combine-prs workflow (#4176) --- .github/workflows/combine-prs.yml | 139 ++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 .github/workflows/combine-prs.yml diff --git a/.github/workflows/combine-prs.yml b/.github/workflows/combine-prs.yml new file mode 100644 index 00000000000..b4a89055446 --- /dev/null +++ b/.github/workflows/combine-prs.yml @@ -0,0 +1,139 @@ +name: 'Combine PRs' + +# Controls when the action will run - in this case triggered manually +on: + workflow_dispatch: + inputs: + branchPrefix: + description: 'Branch prefix to find combinable PRs based on' + required: true + default: 'dependabot' + mustBeGreen: + description: 'Only combine PRs that are green (status is success)' + required: true + default: 'true' + combineBranchName: + description: 'Name of the branch to combine PRs into' + required: true + default: 'combine-prs-branch' + ignoreLabel: + description: 'Exclude PRs with this label' + required: true + default: 'nocombine' + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "combine-prs" + combine-prs: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + - uses: actions/github-script@v3 + id: fetch-branch-names + name: Fetch branch names + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + const pulls = await github.paginate('GET /repos/:owner/:repo/pulls', { + owner: context.repo.owner, + repo: context.repo.repo + }); + branches = []; + prs = []; + base_branch = null; + for (const pull of pulls) { + const branch = pull['head']['ref']; + console.log('Pull for branch: ' + branch); + if (branch.startsWith('${{ github.event.inputs.branchPrefix }}')) { + console.log('Branch matched: ' + branch); + statusOK = true; + if(${{ github.event.inputs.mustBeGreen }}) { + console.log('Checking green status: ' + branch); + const statuses = await github.paginate('GET /repos/{owner}/{repo}/commits/{ref}/status', { + owner: context.repo.owner, + repo: context.repo.repo, + ref: branch + }); + if(statuses.length > 0) { + const latest_status = statuses[0]['state']; + console.log('Validating status: ' + latest_status); + if(latest_status != 'success') { + console.log('Discarding ' + branch + ' with status ' + latest_status); + statusOK = false; + } + } + } + console.log('Checking labels: ' + branch); + const labels = pull['labels']; + for(const label of labels) { + const labelName = label['name']; + console.log('Checking label: ' + labelName); + if(labelName == '${{ github.event.inputs.ignoreLabel }}') { + console.log('Discarding ' + branch + ' with label ' + labelName); + statusOK = false; + } + } + if (statusOK) { + console.log('Adding branch to array: ' + branch); + branches.push(branch); + prs.push('#' + pull['number'] + ' ' + pull['title']); + base_branch = pull['base']['ref']; + } + } + } + + if (branches.length == 0) { + core.setFailed('No PRs/branches matched criteria'); + return; + } + + core.setOutput('base-branch', base_branch); + core.setOutput('prs-string', prs.join('\n')); + + combined = branches.join(' ') + console.log('Combined: ' + combined); + return combined + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2.3.3 + with: + fetch-depth: 0 + # Creates a branch with other PR branches merged together + - name: Created combined branch + env: + BASE_BRANCH: ${{ steps.fetch-branch-names.outputs.base-branch }} + BRANCHES_TO_COMBINE: ${{ steps.fetch-branch-names.outputs.result }} + COMBINE_BRANCH_NAME: ${{ github.event.inputs.combineBranchName }} + run: | + echo "${{steps.fetch-branch-names.outputs.result}}" + sourcebranches="${BRANCHES_TO_COMBINE%\"}" + sourcebranches="${sourcebranches#\"}" + + basebranch="${BASE_BRANCH%\"}" + basebranch="${basebranch#\"}" + + git config pull.rebase false + git config user.name github-actions + git config user.email github-actions@github.com + + git branch $COMBINE_BRANCH_NAME $basebranch + git checkout $COMBINE_BRANCH_NAME + git pull origin $sourcebranches --no-edit + git push origin $COMBINE_BRANCH_NAME + # Creates a PR with the new combined branch + - uses: actions/github-script@v3 + name: Create Combined Pull Request + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + const prString = `${{ steps.fetch-branch-names.outputs.prs-string }}`; + const body = 'This PR was created by the Combine PRs action by combining the following PRs:\n' + prString; + await github.pulls.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: 'Combined PR', + head: '${{ github.event.inputs.combineBranchName }}', + base: '${{ steps.fetch-branch-names.outputs.base-branch }}', + body: body + }); From ec37236bc28e18724f6e4cf6aca5c63224935fa2 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 8 Jun 2021 17:31:47 +0100 Subject: [PATCH 1918/5615] Tweak combine-prs.yml to use check-runs Copied from https://github.com/hrvey/combine-prs-workflow/pull/6 --- .github/workflows/combine-prs.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/combine-prs.yml b/.github/workflows/combine-prs.yml index b4a89055446..749cc648fb5 100644 --- a/.github/workflows/combine-prs.yml +++ b/.github/workflows/combine-prs.yml @@ -51,16 +51,15 @@ jobs: statusOK = true; if(${{ github.event.inputs.mustBeGreen }}) { console.log('Checking green status: ' + branch); - const statuses = await github.paginate('GET /repos/{owner}/{repo}/commits/{ref}/status', { + const checkRuns = await github.request('GET /repos/{owner}/{repo}/commits/{ref}/check-runs', { owner: context.repo.owner, repo: context.repo.repo, ref: branch }); - if(statuses.length > 0) { - const latest_status = statuses[0]['state']; - console.log('Validating status: ' + latest_status); - if(latest_status != 'success') { - console.log('Discarding ' + branch + ' with status ' + latest_status); + for await (const cr of checkRuns.data.check_runs) { + console.log('Validating check conclusion: ' + cr.conclusion); + if(cr.conclusion != 'success') { + console.log('Discarding ' + branch + ' with check conclusion ' + cr.conclusion); statusOK = false; } } From 3684a0b6b322d13c0a1e71934af5ca30c8d1e679 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Jun 2021 18:57:17 +0000 Subject: [PATCH 1919/5615] Bump HikariCP from 3.4.5 to 4.0.3 in /modules/junit-jupiter Bumps [HikariCP](https://github.com/brettwooldridge/HikariCP) from 3.4.5 to 4.0.3. - [Release notes](https://github.com/brettwooldridge/HikariCP/releases) - [Changelog](https://github.com/brettwooldridge/HikariCP/blob/dev/CHANGES) - [Commits](https://github.com/brettwooldridge/HikariCP/compare/HikariCP-3.4.5...HikariCP-4.0.3) Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 393c4cac653..2bc2a88e7b3 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -6,7 +6,7 @@ dependencies { testCompile project(':mysql') testCompile project(':postgresql') - testCompile 'com.zaxxer:HikariCP:3.4.5' + testCompile 'com.zaxxer:HikariCP:4.0.3' testCompile 'redis.clients:jedis:3.6.0' testCompile 'org.apache.httpcomponents:httpclient:4.5.13' testCompile ('org.mockito:mockito-core:3.10.0') { From 8cf5661c18cf3eb0fcc500476865f656c2190323 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Jun 2021 18:57:26 +0000 Subject: [PATCH 1920/5615] Bump mysql-connector-java in /modules/junit-jupiter Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.22 to 8.0.25. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.22...8.0.25) Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 393c4cac653..aed6deb0c10 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -16,7 +16,7 @@ dependencies { testCompile 'org.junit.jupiter:junit-jupiter-params:5.7.1' testRuntime 'org.postgresql:postgresql:42.2.20' - testRuntime 'mysql:mysql-connector-java:8.0.22' + testRuntime 'mysql:mysql-connector-java:8.0.25' testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.7.2' } From 160bb814e91b54050afd9a904059566c6f7d9df3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Jun 2021 18:57:34 +0000 Subject: [PATCH 1921/5615] Bump org.jetbrains.kotlin.jvm from 1.4.32 to 1.5.10 in /examples Bumps org.jetbrains.kotlin.jvm from 1.4.32 to 1.5.10. Signed-off-by: dependabot[bot] --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 6617e830e8b..61a74a3950e 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,6 +1,6 @@ plugins { id("org.springframework.boot") version "2.4.1" - id("org.jetbrains.kotlin.jvm") version "1.4.32" + id("org.jetbrains.kotlin.jvm") version "1.5.10" id("org.jetbrains.kotlin.plugin.spring") version "1.5.10" } From 4f573f2802b4a31c44cbb516d3bed2bc7a2ccd20 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Jun 2021 18:57:42 +0000 Subject: [PATCH 1922/5615] Bump annotations from 20.1.0 to 21.0.1 in /modules/postgresql Bumps [annotations](https://github.com/JetBrains/java-annotations) from 20.1.0 to 21.0.1. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/20.1.0...21.0.1) Signed-off-by: dependabot[bot] --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index e853387291f..c01cd67df48 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -16,5 +16,5 @@ dependencies { testCompile testFixtures(project(':r2dbc')) testCompile 'io.r2dbc:r2dbc-postgresql:0.8.5.RELEASE' - compileOnly 'org.jetbrains:annotations:20.1.0' + compileOnly 'org.jetbrains:annotations:21.0.1' } From ff462ae0ab0a9b3b671bc7391afc2af96ea52af9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Jun 2021 18:57:44 +0000 Subject: [PATCH 1923/5615] Bump annotations from 20.1.0 to 21.0.1 in /modules/selenium Bumps [annotations](https://github.com/JetBrains/java-annotations) from 20.1.0 to 21.0.1. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/20.1.0...21.0.1) Signed-off-by: dependabot[bot] --- modules/selenium/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/selenium/build.gradle b/modules/selenium/build.gradle index 7911b1ea991..e9e4bf2d4a5 100644 --- a/modules/selenium/build.gradle +++ b/modules/selenium/build.gradle @@ -12,6 +12,6 @@ dependencies { testCompile project(':nginx') testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' - compileOnly 'org.jetbrains:annotations:20.1.0' - testCompileClasspath 'org.jetbrains:annotations:20.0.0' + compileOnly 'org.jetbrains:annotations:21.0.1' + testCompileClasspath 'org.jetbrains:annotations:21.0.1' } From b820c4a07a8ccc415838851ada8e6e1bfa52466c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Jun 2021 18:57:47 +0000 Subject: [PATCH 1924/5615] Bump aws-java-sdk-sqs from 1.11.1018 to 1.12.1 in /modules/localstack Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.11.1018 to 1.12.1. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.1018...1.12.1) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-sqs dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index e6a6340f686..3948f1443b2 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.1030' testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.1030' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.11.1018' + testCompile 'com.amazonaws:aws-java-sdk-sqs:1.12.1' testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' testCompile 'software.amazon.awssdk:s3:2.16.62' testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From 5091c179d5636c2ccb8e80833a92535a564a418c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Jun 2021 18:57:48 +0000 Subject: [PATCH 1925/5615] Bump annotations from 20.1.0 to 21.0.1 in /modules/nginx Bumps [annotations](https://github.com/JetBrains/java-annotations) from 20.1.0 to 21.0.1. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/20.1.0...21.0.1) Signed-off-by: dependabot[bot] --- modules/nginx/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/nginx/build.gradle b/modules/nginx/build.gradle index 1e601312ab4..c0fe33a8258 100644 --- a/modules/nginx/build.gradle +++ b/modules/nginx/build.gradle @@ -2,6 +2,6 @@ description = "Testcontainers :: Nginx" dependencies { compile project(':testcontainers') - compileOnly 'org.jetbrains:annotations:20.1.0' + compileOnly 'org.jetbrains:annotations:21.0.1' testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From ef197a4d1b686e8c88e1883ac9c32e4153bb93af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Jun 2021 18:57:50 +0000 Subject: [PATCH 1926/5615] Bump annotations from 20.1.0 to 21.0.1 in /modules/jdbc Bumps [annotations](https://github.com/JetBrains/java-annotations) from 20.1.0 to 21.0.1. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/20.1.0...21.0.1) Signed-off-by: dependabot[bot] --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 2741a348f34..1152b2a5e93 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -4,7 +4,7 @@ dependencies { compile project(':database-commons') testCompile project(':test-support') - compileOnly 'org.jetbrains:annotations:20.1.0' + compileOnly 'org.jetbrains:annotations:21.0.1' testCompile 'commons-dbutils:commons-dbutils:1.7' testCompile 'org.vibur:vibur-dbcp:25.0' testCompile 'org.apache.tomcat:tomcat-jdbc:10.0.6' From 5fcbd6db51b92b6eeaf78efb6fb7487c46c47584 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 8 Jun 2021 20:16:43 +0100 Subject: [PATCH 1927/5615] Make release drafter observe PR events --- .github/workflows/release-drafter.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index bb2afd986f1..629280b1241 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -4,6 +4,8 @@ on: push: branches: - master + pull_request: + types: [opened, reopened, synchronize] jobs: update_release_draft: From 9a8df79f847dbcca833680e69f8b4ea1ace310bf Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 9 Jun 2021 17:48:37 +0200 Subject: [PATCH 1928/5615] Update to Gradle 7 (#4171) * Update to Gradle 7 * Add `checkPOMdependencies` task to test that no new dependencies were added * Update examples to Gradle 7 * Disable the filesystem watching * Try Gradle 7.0.2 * fix `provided` configuration * Extract `ComparePOMWithLatestReleasedTask` * Add missing file --- .gitignore | 1 + build.gradle | 19 +++--- buildSrc/build.gradle | 7 +++ .../ComparePOMWithLatestReleasedTask.groovy | 35 +++++++++++ core/build.gradle | 62 ++++++++----------- docs/examples/junit4/redis/build.gradle | 2 +- docs/examples/junit5/redis/build.gradle | 2 +- docs/examples/spock/redis/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- examples/kafka-cluster/build.gradle | 10 +-- gradle.properties | 1 + gradle/publishing.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- modules/cassandra/build.gradle | 4 +- modules/clickhouse/build.gradle | 8 +-- modules/cockroachdb/build.gradle | 6 +- modules/couchbase/build.gradle | 4 +- modules/database-commons/build.gradle | 4 +- modules/db2/build.gradle | 6 +- modules/dynalite/build.gradle | 6 +- modules/elasticsearch/build.gradle | 8 +-- modules/gcloud/build.gradle | 14 ++--- modules/influxdb/build.gradle | 4 +- modules/jdbc-test/build.gradle | 22 +++---- modules/jdbc/build.gradle | 18 +++--- modules/junit-jupiter/build.gradle | 26 ++++---- modules/kafka/build.gradle | 8 +-- modules/localstack/build.gradle | 12 ++-- modules/mariadb/build.gradle | 10 +-- modules/mockserver/build.gradle | 6 +- modules/mongodb/build.gradle | 4 +- modules/mssqlserver/build.gradle | 14 ++--- modules/mysql/build.gradle | 10 +-- modules/neo4j/build.gradle | 6 +- modules/nginx/build.gradle | 4 +- modules/oracle-xe/build.gradle | 6 +- modules/orientdb/build.gradle | 10 +-- modules/postgresql/build.gradle | 12 ++-- modules/presto/build.gradle | 6 +- modules/pulsar/build.gradle | 8 +-- modules/r2dbc/build.gradle | 14 ++--- modules/rabbitmq/build.gradle | 6 +- modules/selenium/build.gradle | 12 ++-- modules/solr/build.gradle | 4 +- modules/spock/build.gradle | 18 +++--- modules/toxiproxy/build.gradle | 10 +-- modules/trino/build.gradle | 6 +- modules/vault/build.gradle | 8 +-- 48 files changed, 254 insertions(+), 217 deletions(-) create mode 100644 buildSrc/build.gradle create mode 100644 buildSrc/src/main/groovy/org/testcontainers/build/ComparePOMWithLatestReleasedTask.groovy diff --git a/.gitignore b/.gitignore index 1b6070306a2..aa9fdf33f1f 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,7 @@ node_modules/ .gradle/ build/ +!buildSrc/src/main/groovy/org/testcontainers/build out/ *.class diff --git a/build.gradle b/build.gradle index c51b24909cb..40f5bfed399 100644 --- a/build.gradle +++ b/build.gradle @@ -1,15 +1,13 @@ plugins { - id 'io.franzbecker.gradle-lombok' version '3.1.0' - id 'nebula.provided-base' version '3.0.3' - id 'com.github.johnrengelman.shadow' version '5.2.0' + id 'io.franzbecker.gradle-lombok' version '4.0.0' + id 'com.github.johnrengelman.shadow' version '7.0.0' id "com.jfrog.bintray" version "1.8.4" apply false } apply from: "$rootDir/gradle/ci-support.gradle" subprojects { - apply plugin: 'nebula.provided-base' - apply plugin: 'java' + apply plugin: 'java-library' apply plugin: 'idea' apply plugin: 'io.franzbecker.gradle-lombok' apply plugin: 'com.github.johnrengelman.shadow' @@ -26,8 +24,13 @@ subprojects { mavenCentral() } + configurations { + provided + api.extendsFrom(provided) + } + lombok { - version = '1.18.12' + version = '1.18.20' } task delombok(type: io.franzbecker.gradle.lombok.task.DelombokTask) { @@ -98,7 +101,7 @@ subprojects { shadowJar { configurations = [] - classifier = null + archiveClassifier.set(null) doFirst { // See https://github.com/johnrengelman/shadow/blob/5.0.0/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ConfigureShadowRelocation.groovy @@ -128,6 +131,6 @@ subprojects { } dependencies { - testCompile 'ch.qos.logback:logback-classic:1.2.3' + testImplementation 'ch.qos.logback:logback-classic:1.2.3' } } diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle new file mode 100644 index 00000000000..13414d46a22 --- /dev/null +++ b/buildSrc/build.gradle @@ -0,0 +1,7 @@ +plugins { + id 'java-gradle-plugin' +} + +repositories { + mavenCentral() +} diff --git a/buildSrc/src/main/groovy/org/testcontainers/build/ComparePOMWithLatestReleasedTask.groovy b/buildSrc/src/main/groovy/org/testcontainers/build/ComparePOMWithLatestReleasedTask.groovy new file mode 100644 index 00000000000..1bb71ca8f52 --- /dev/null +++ b/buildSrc/src/main/groovy/org/testcontainers/build/ComparePOMWithLatestReleasedTask.groovy @@ -0,0 +1,35 @@ +package org.testcontainers.build + +import org.gradle.api.DefaultTask +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.TaskAction + +class ComparePOMWithLatestReleasedTask extends DefaultTask { + + @Input + Set ignore = [] + + @TaskAction + def doCompare() { + def rootNode = new XmlSlurper().parse(project.tasks.generatePomFileForMavenJavaPublication.destination) + + def artifactId = rootNode.artifactId.text() + + def latestRelease = new XmlSlurper() + .parse("https://repo1.maven.org/maven2/org/testcontainers/${artifactId}/maven-metadata.xml") + .versioning.release.text() + + def releasedRootNode = new XmlSlurper() + .parse("https://repo1.maven.org/maven2/org/testcontainers/${artifactId}/${latestRelease}/${artifactId}-${latestRelease}.pom") + + Set dependencies = releasedRootNode.dependencies.children() + .collect { "${it.groupId.text()}:${it.artifactId.text()}".toString() } + + for (dependency in rootNode.dependencies.children()) { + def coordinates = "${dependency.groupId.text()}:${dependency.artifactId.text()}".toString() + if (!dependencies.contains(coordinates) && !ignore.contains(coordinates)) { + throw new IllegalStateException("A new dependency '${coordinates}' has been added to 'org.testcontainers:${artifactId}' - if this was intentional please add it to the ignore list in ${project.buildFile}") + } + } + } +} diff --git a/core/build.gradle b/core/build.gradle index 059202e1b41..d260ae5317a 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -26,7 +26,7 @@ idea.module.testSourceDirs += sourceSets.jarFileTest.allSource.srcDirs configurations { baseline shaded - [runtime, compileOnly, testCompile]*.extendsFrom shaded + [api, compileOnly, testCompile]*.extendsFrom shaded } shadowJar { @@ -34,33 +34,16 @@ shadowJar { mergeServiceFiles() - exclude 'org/newsclub/**' - [ 'META-INF/NOTICE', 'META-INF/NOTICE.txt', 'META-INF/LICENSE', 'META-INF/LICENSE.txt', - 'META-INF/DEPENDENCIES', 'META-INF/maven/', 'META-INF/proguard/', 'META-INF/versions/*/module-info.class', - 'META-INF/services/README.md', - 'META-INF/services/com.fasterxml.jackson.core.*', - 'META-INF/services/com.github.dockerjava.api.command.*', - 'META-INF/services/javax.ws.rs.ext.*', 'META-INF/services/java.security.Provider', - 'mozilla/public-suffix-list.txt', - 'module-info.class', ].each { exclude(it) } - - project.afterEvaluate { - dependencies { - for (id in project.configurations.compile.resolvedConfiguration.resolvedArtifacts*.moduleVersion*.id) { - exclude(dependency("${id.group}:${id.name}")) - } - } - } } task jarFileTest(type: Test) { @@ -169,16 +152,16 @@ dependencies { exclude group: "*", module: "*" } - compile 'junit:junit:4.12' - compile 'org.slf4j:slf4j-api:1.7.30' + api 'junit:junit:4.12' + api 'org.slf4j:slf4j-api:1.7.30' compileOnly 'org.jetbrains:annotations:20.1.0' testCompileClasspath 'org.jetbrains:annotations:20.0.0' - compile 'org.apache.commons:commons-compress:1.20' - compile ('org.rnorth.duct-tape:duct-tape:1.0.8') { + api 'org.apache.commons:commons-compress:1.20' + api ('org.rnorth.duct-tape:duct-tape:1.0.8') { exclude(group: 'org.jetbrains', module: 'annotations') } - compile "com.github.docker-java:docker-java-api:3.2.8" + api "com.github.docker-java:docker-java-api:3.2.8" shaded ('com.github.docker-java:docker-java-core:3.2.8') { exclude(group: 'com.github.docker-java', module: 'docker-java-api') @@ -195,7 +178,7 @@ dependencies { exclude(group: 'org.slf4j') } - compile 'com.github.docker-java:docker-java-transport-zerodep:3.2.8' + api 'com.github.docker-java:docker-java-transport-zerodep:3.2.8' shaded "org.yaml:snakeyaml:1.27" @@ -205,24 +188,31 @@ dependencies { exclude(group: 'org.slf4j') } - testCompile 'org.apache.httpcomponents:httpclient:4.5.9' - testCompile 'redis.clients:jedis:3.6.0' - testCompile 'com.rabbitmq:amqp-client:5.9.0' - testCompile 'org.mongodb:mongo-java-driver:3.12.7' + testImplementation 'org.apache.httpcomponents:httpclient:4.5.9' + testImplementation 'redis.clients:jedis:3.6.0' + testImplementation 'com.rabbitmq:amqp-client:5.9.0' + testImplementation 'org.mongodb:mongo-java-driver:3.12.7' - testCompile ('org.mockito:mockito-core:3.10.0') { + testImplementation ('org.mockito:mockito-core:3.10.0') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest - testCompile files('testlib/repo/fakejar/fakejar/0/fakejar-0.jar') + testImplementation files('testlib/repo/fakejar/fakejar/0/fakejar-0.jar') - testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' - testCompile 'org.assertj:assertj-core:3.18.1' - testCompile project(':test-support') + testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.assertj:assertj-core:3.18.1' + testImplementation project(':test-support') jarFileTestCompileOnly "org.projectlombok:lombok:${lombok.version}" jarFileTestAnnotationProcessor "org.projectlombok:lombok:${lombok.version}" - jarFileTestCompile 'junit:junit:4.12' - jarFileTestCompile 'org.assertj:assertj-core:3.18.1' - jarFileTestCompile 'org.ow2.asm:asm-debug-all:5.2' + jarFileTestImplementation 'junit:junit:4.12' + jarFileTestImplementation 'org.assertj:assertj-core:3.18.1' + jarFileTestImplementation 'org.ow2.asm:asm-debug-all:5.2' } + +tasks.generatePomFileForMavenJavaPublication.finalizedBy( + tasks.register('checkPOMdependencies', org.testcontainers.build.ComparePOMWithLatestReleasedTask) { + ignore = [ + ] + } +) diff --git a/docs/examples/junit4/redis/build.gradle b/docs/examples/junit4/redis/build.gradle index bf203a8fefa..7551c6e578e 100644 --- a/docs/examples/junit4/redis/build.gradle +++ b/docs/examples/junit4/redis/build.gradle @@ -1,7 +1,7 @@ description = "Examples for docs" dependencies { - compile "io.lettuce:lettuce-core:5.1.1.RELEASE" + api "io.lettuce:lettuce-core:5.1.1.RELEASE" testImplementation "junit:junit:4.12" testImplementation project(":testcontainers") diff --git a/docs/examples/junit5/redis/build.gradle b/docs/examples/junit5/redis/build.gradle index 409b766b758..348f4788555 100644 --- a/docs/examples/junit5/redis/build.gradle +++ b/docs/examples/junit5/redis/build.gradle @@ -1,7 +1,7 @@ description = "Examples for docs" dependencies { - compile "io.lettuce:lettuce-core:5.1.1.RELEASE" + api "io.lettuce:lettuce-core:5.1.1.RELEASE" testImplementation "org.junit.jupiter:junit-jupiter-api:5.4.2" testImplementation "org.junit.jupiter:junit-jupiter-params:5.4.2" diff --git a/docs/examples/spock/redis/build.gradle b/docs/examples/spock/redis/build.gradle index 398a6d3fa1c..1bff637a7fb 100644 --- a/docs/examples/spock/redis/build.gradle +++ b/docs/examples/spock/redis/build.gradle @@ -4,7 +4,7 @@ plugins { } dependencies { - compile "io.lettuce:lettuce-core:5.2.0.RELEASE" + api "io.lettuce:lettuce-core:5.2.0.RELEASE" testImplementation 'org.spockframework:spock-core:1.2-groovy-2.5' testImplementation project(":spock") testImplementation 'ch.qos.logback:logback-classic:1.2.3' diff --git a/examples/gradle/wrapper/gradle-wrapper.properties b/examples/gradle/wrapper/gradle-wrapper.properties index 622ab64a3cb..0f80bbf516c 100644 --- a/examples/gradle/wrapper/gradle-wrapper.properties +++ b/examples/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index 491b5c2f214..6ba1a966dae 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -9,9 +9,9 @@ repositories { dependencies { testCompileOnly "org.projectlombok:lombok:1.18.20" testAnnotationProcessor "org.projectlombok:lombok:1.18.10" - testCompile 'org.testcontainers:kafka' - testCompile 'org.apache.kafka:kafka-clients:2.3.1' - testCompile 'org.assertj:assertj-core:3.14.0' - testCompile 'com.google.guava:guava:23.0' - testCompile 'org.slf4j:slf4j-simple:1.7.30' + testImplementation 'org.testcontainers:kafka' + testImplementation 'org.apache.kafka:kafka-clients:2.3.1' + testImplementation 'org.assertj:assertj-core:3.14.0' + testImplementation 'com.google.guava:guava:23.0' + testImplementation 'org.slf4j:slf4j-simple:1.7.30' } diff --git a/gradle.properties b/gradle.properties index dad41a4425a..397b832391e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,3 @@ org.gradle.parallel=false org.gradle.caching=true + diff --git a/gradle/publishing.gradle b/gradle/publishing.gradle index 3acdeb10c88..84fad9994e5 100644 --- a/gradle/publishing.gradle +++ b/gradle/publishing.gradle @@ -72,7 +72,7 @@ publishing { } } - project.configurations.compile.dependencies.each { addDependency(it, 'compile') } + project.configurations.api.dependencies.each { addDependency(it, 'compile') } project.configurations.provided.dependencies.each { addDependency(it, 'provided') } } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6fd47cc3de5..4a66b0f1980 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ #Sun Apr 05 19:28:23 BST 2020 -distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStorePath=wrapper/dists diff --git a/modules/cassandra/build.gradle b/modules/cassandra/build.gradle index 9704a05b69d..a5427aaea07 100644 --- a/modules/cassandra/build.gradle +++ b/modules/cassandra/build.gradle @@ -1,6 +1,6 @@ description = "TestContainers :: Cassandra" dependencies { - compile project(":database-commons") - compile "com.datastax.cassandra:cassandra-driver-core:3.7.1" + api project(":database-commons") + api "com.datastax.cassandra:cassandra-driver-core:3.7.1" } diff --git a/modules/clickhouse/build.gradle b/modules/clickhouse/build.gradle index 323475b1d1c..7eb68eb2e51 100644 --- a/modules/clickhouse/build.gradle +++ b/modules/clickhouse/build.gradle @@ -1,9 +1,9 @@ description = "Testcontainers :: JDBC :: ClickHouse" dependencies { - compile project(':testcontainers') - compile project(':jdbc') + api project(':testcontainers') + api project(':jdbc') - testCompile project(':jdbc-test') - testCompile 'ru.yandex.clickhouse:clickhouse-jdbc:0.2.4' + testImplementation project(':jdbc-test') + testImplementation 'ru.yandex.clickhouse:clickhouse-jdbc:0.2.4' } diff --git a/modules/cockroachdb/build.gradle b/modules/cockroachdb/build.gradle index a3927258679..5222208fae0 100644 --- a/modules/cockroachdb/build.gradle +++ b/modules/cockroachdb/build.gradle @@ -1,8 +1,8 @@ description = "Testcontainers :: JDBC :: CockroachDB" dependencies { - compile project(':jdbc') + api project(':jdbc') - testCompile project(':jdbc-test') - testCompile 'org.postgresql:postgresql:42.2.12' + testImplementation project(':jdbc-test') + testImplementation 'org.postgresql:postgresql:42.2.12' } diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 9cb778fd479..88eb537269d 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -1,7 +1,7 @@ description = "Testcontainers :: Couchbase" dependencies { - compile project(':testcontainers') + api project(':testcontainers') - testCompile 'com.couchbase.client:java-client:2.7.15' + testImplementation 'com.couchbase.client:java-client:2.7.15' } diff --git a/modules/database-commons/build.gradle b/modules/database-commons/build.gradle index 0eecc1844d0..7bba8548c95 100644 --- a/modules/database-commons/build.gradle +++ b/modules/database-commons/build.gradle @@ -1,7 +1,7 @@ description = "Testcontainers :: Database-Commons" dependencies { - compile project(':testcontainers') + api project(':testcontainers') - testCompile 'org.assertj:assertj-core:3.19.0' + testImplementation 'org.assertj:assertj-core:3.19.0' } diff --git a/modules/db2/build.gradle b/modules/db2/build.gradle index feaf5f790c5..9d5d8a61ab0 100644 --- a/modules/db2/build.gradle +++ b/modules/db2/build.gradle @@ -1,8 +1,8 @@ description = "Testcontainers :: JDBC :: DB2" dependencies { - compile project(':jdbc') + api project(':jdbc') - testCompile project(':jdbc-test') - testCompile 'com.ibm.db2:jcc:11.5.0.0' + testImplementation project(':jdbc-test') + testImplementation 'com.ibm.db2:jcc:11.5.0.0' } diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 58a16a0b62b..e3a49df5be2 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -1,10 +1,10 @@ description = "Testcontainers :: Dynalite" dependencies { - compile project(':testcontainers') + api project(':testcontainers') compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.1030' - testCompile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.1030' + testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.11.1030' - testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 21db8e69545..e66e045ead4 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -1,8 +1,8 @@ description = "TestContainers :: elasticsearch" dependencies { - compile project(':testcontainers') - testCompile "org.elasticsearch.client:elasticsearch-rest-client:7.13.0" - testCompile "org.elasticsearch.client:transport:7.12.1" - testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + api project(':testcontainers') + testImplementation "org.elasticsearch.client:elasticsearch-rest-client:7.13.0" + testImplementation "org.elasticsearch.client:transport:7.12.1" + testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index aa42d7dc560..11e68824c34 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -1,12 +1,12 @@ description = "Testcontainers :: GCloud" dependencies { - compile project(':testcontainers') + api project(':testcontainers') - testCompile 'com.google.cloud:google-cloud-datastore:1.102.4' - testCompile 'com.google.cloud:google-cloud-firestore:1.33.0' - testCompile 'com.google.cloud:google-cloud-pubsub:1.105.0' - testCompile 'com.google.cloud:google-cloud-spanner:1.50.0' - testCompile 'com.google.cloud:google-cloud-bigtable:1.17.3' - testCompile 'org.assertj:assertj-core:3.15.0' + testImplementation 'com.google.cloud:google-cloud-datastore:1.102.4' + testImplementation 'com.google.cloud:google-cloud-firestore:1.33.0' + testImplementation 'com.google.cloud:google-cloud-pubsub:1.105.0' + testImplementation 'com.google.cloud:google-cloud-spanner:1.50.0' + testImplementation 'com.google.cloud:google-cloud-bigtable:1.17.3' + testImplementation 'org.assertj:assertj-core:3.15.0' } diff --git a/modules/influxdb/build.gradle b/modules/influxdb/build.gradle index 2e735e1137a..704bf960724 100644 --- a/modules/influxdb/build.gradle +++ b/modules/influxdb/build.gradle @@ -1,8 +1,8 @@ description = "Testcontainers :: InfluxDB" dependencies { - compile project(':testcontainers') + api project(':testcontainers') compileOnly 'org.influxdb:influxdb-java:2.21' - testCompile 'org.influxdb:influxdb-java:2.21' + testImplementation 'org.influxdb:influxdb-java:2.21' } diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 3dd0d52f2e8..3da4eb0f691 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -1,20 +1,20 @@ dependencies { - compile project(':jdbc') - compile project(':test-support') + api project(':jdbc') + api project(':test-support') - compile 'com.google.guava:guava:30.1-jre' - compile 'org.apache.commons:commons-lang3:3.12.0' - compile 'com.zaxxer:HikariCP-java6:2.3.13' - compile 'commons-dbutils:commons-dbutils:1.7' + api 'com.google.guava:guava:30.1-jre' + api 'org.apache.commons:commons-lang3:3.12.0' + api 'com.zaxxer:HikariCP-java6:2.3.13' + api 'commons-dbutils:commons-dbutils:1.7' - compile 'com.googlecode.junit-toolbox:junit-toolbox:2.4' + api 'com.googlecode.junit-toolbox:junit-toolbox:2.4' - compile('org.rnorth.visible-assertions:visible-assertions:2.1.2') { + api('org.rnorth.visible-assertions:visible-assertions:2.1.2') { // Excluded in favor of jna-platform exclude(group: "net.java.dev.jna", module: "jna") } - compile 'org.apache.tomcat:tomcat-jdbc:9.0.40' - compile 'org.vibur:vibur-dbcp:25.0' - compile 'mysql:mysql-connector-java:8.0.25' + api 'org.apache.tomcat:tomcat-jdbc:9.0.40' + api 'org.vibur:vibur-dbcp:25.0' + api 'mysql:mysql-connector-java:8.0.25' } diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 1152b2a5e93..16b95af77bf 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -1,15 +1,15 @@ description = "Testcontainers :: JDBC" dependencies { - compile project(':database-commons') - testCompile project(':test-support') + api project(':database-commons') + testImplementation project(':test-support') compileOnly 'org.jetbrains:annotations:21.0.1' - testCompile 'commons-dbutils:commons-dbutils:1.7' - testCompile 'org.vibur:vibur-dbcp:25.0' - testCompile 'org.apache.tomcat:tomcat-jdbc:10.0.6' - testCompile 'com.zaxxer:HikariCP-java6:2.3.13' - testCompile 'com.googlecode.junit-toolbox:junit-toolbox:2.4' - testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' - testCompile 'org.assertj:assertj-core:3.19.0' + testImplementation 'commons-dbutils:commons-dbutils:1.7' + testImplementation 'org.vibur:vibur-dbcp:25.0' + testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.6' + testImplementation 'com.zaxxer:HikariCP-java6:2.3.13' + testImplementation 'com.googlecode.junit-toolbox:junit-toolbox:2.4' + testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.assertj:assertj-core:3.19.0' } diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index b06972371ff..ad74eb3ec26 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -1,23 +1,23 @@ description = "Testcontainers :: JUnit Jupiter Extension" dependencies { - compile project(':testcontainers') - compile 'org.junit.jupiter:junit-jupiter-api:5.7.1' + api project(':testcontainers') + api 'org.junit.jupiter:junit-jupiter-api:5.7.1' - testCompile project(':mysql') - testCompile project(':postgresql') - testCompile 'com.zaxxer:HikariCP:4.0.3' - testCompile 'redis.clients:jedis:3.6.0' - testCompile 'org.apache.httpcomponents:httpclient:4.5.13' - testCompile ('org.mockito:mockito-core:3.10.0') { + testImplementation project(':mysql') + testImplementation project(':postgresql') + testImplementation 'com.zaxxer:HikariCP:4.0.3' + testImplementation 'redis.clients:jedis:3.6.0' + testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' + testImplementation ('org.mockito:mockito-core:3.10.0') { exclude(module: 'hamcrest-core') } - testCompile 'org.assertj:assertj-core:3.18.1' - testCompile 'org.junit.jupiter:junit-jupiter-params:5.7.1' + testImplementation 'org.assertj:assertj-core:3.18.1' + testImplementation 'org.junit.jupiter:junit-jupiter-params:5.7.1' - testRuntime 'org.postgresql:postgresql:42.2.20' - testRuntime 'mysql:mysql-connector-java:8.0.25' - testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.7.2' + testRuntimeOnly 'org.postgresql:postgresql:42.2.20' + testRuntimeOnly 'mysql:mysql-connector-java:8.0.25' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.2' } test { diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index db400c0b557..56abadbce47 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -1,9 +1,9 @@ description = "Testcontainers :: Kafka" dependencies { - compile project(':testcontainers') + api project(':testcontainers') - testCompile 'org.apache.kafka:kafka-clients:2.8.0' - testCompile 'org.assertj:assertj-core:3.18.1' - testCompile 'com.google.guava:guava:23.0' + testImplementation 'org.apache.kafka:kafka-clients:2.8.0' + testImplementation 'org.assertj:assertj-core:3.18.1' + testImplementation 'com.google.guava:guava:23.0' } diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 3948f1443b2..503a3a7bf49 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -1,12 +1,12 @@ description = "Testcontainers :: Localstack" dependencies { - compile project(':testcontainers') + api project(':testcontainers') compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.1030' - testCompile 'com.amazonaws:aws-java-sdk-s3:1.11.1030' - testCompile 'com.amazonaws:aws-java-sdk-sqs:1.12.1' - testCompile 'com.amazonaws:aws-java-sdk-logs:1.11.807' - testCompile 'software.amazon.awssdk:s3:2.16.62' - testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'com.amazonaws:aws-java-sdk-s3:1.11.1030' + testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.1' + testImplementation 'com.amazonaws:aws-java-sdk-logs:1.11.807' + testImplementation 'software.amazon.awssdk:s3:2.16.62' + testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 17d912b826c..ca267a1fe7c 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -4,14 +4,14 @@ dependencies { annotationProcessor 'com.google.auto.service:auto-service:1.0' compileOnly 'com.google.auto.service:auto-service:1.0' - compile project(':jdbc') + api project(':jdbc') compileOnly project(':r2dbc') compileOnly 'org.mariadb:r2dbc-mariadb:1.0.1' - testCompile project(':jdbc-test') - testCompile 'org.mariadb.jdbc:mariadb-java-client:2.7.3' + testImplementation project(':jdbc-test') + testImplementation 'org.mariadb.jdbc:mariadb-java-client:2.7.3' - testCompile testFixtures(project(':r2dbc')) - testCompile 'org.mariadb:r2dbc-mariadb:1.0.0' + testImplementation testFixtures(project(':r2dbc')) + testImplementation 'org.mariadb:r2dbc-mariadb:1.0.0' } diff --git a/modules/mockserver/build.gradle b/modules/mockserver/build.gradle index e13343559f5..9ea504ca74a 100644 --- a/modules/mockserver/build.gradle +++ b/modules/mockserver/build.gradle @@ -1,8 +1,8 @@ description = "Testcontainers :: MockServer" dependencies { - compile project(':testcontainers') + api project(':testcontainers') - testCompile 'org.mock-server:mockserver-client-java:5.5.4' - testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.mock-server:mockserver-client-java:5.5.4' + testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/modules/mongodb/build.gradle b/modules/mongodb/build.gradle index 57db1be21de..e332badb68b 100644 --- a/modules/mongodb/build.gradle +++ b/modules/mongodb/build.gradle @@ -1,7 +1,7 @@ description = "Testcontainers :: MongoDB" dependencies { - compile project(':testcontainers') + api project(':testcontainers') - testCompile("org.mongodb:mongodb-driver-sync:4.0.2") + testImplementation("org.mongodb:mongodb-driver-sync:4.0.2") } diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index 143a6582ae1..74e77d474e9 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -4,18 +4,18 @@ dependencies { annotationProcessor 'com.google.auto.service:auto-service:1.0' compileOnly 'com.google.auto.service:auto-service:1.0' - compile project(':jdbc') + api project(':jdbc') compileOnly project(':r2dbc') compileOnly 'io.r2dbc:r2dbc-mssql:0.8.6.RELEASE' - testCompile project(':jdbc-test') - testCompile 'com.microsoft.sqlserver:mssql-jdbc:9.3.1.jre8-preview' + testImplementation project(':jdbc-test') + testImplementation 'com.microsoft.sqlserver:mssql-jdbc:9.3.1.jre8-preview' - testCompile project(':r2dbc') - testCompile 'io.r2dbc:r2dbc-mssql:0.8.5.RELEASE' + testImplementation project(':r2dbc') + testImplementation 'io.r2dbc:r2dbc-mssql:0.8.5.RELEASE' // MSSQL's wait strategy requires the JDBC driver - testCompile testFixtures(project(':r2dbc')) - testCompile 'com.microsoft.sqlserver:mssql-jdbc:9.3.1.jre8-preview' + testImplementation testFixtures(project(':r2dbc')) + testImplementation 'com.microsoft.sqlserver:mssql-jdbc:9.3.1.jre8-preview' } diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 91a7d2c9918..a14fa678280 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -4,16 +4,16 @@ dependencies { annotationProcessor 'com.google.auto.service:auto-service:1.0' compileOnly 'com.google.auto.service:auto-service:1.0' - compile project(':jdbc') + api project(':jdbc') compileOnly project(':r2dbc') compileOnly 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' - testCompile project(':jdbc-test') - testCompile 'mysql:mysql-connector-java:8.0.25' + testImplementation project(':jdbc-test') + testImplementation 'mysql:mysql-connector-java:8.0.25' - testCompile testFixtures(project(':r2dbc')) - testCompile 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' + testImplementation testFixtures(project(':r2dbc')) + testImplementation 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' compileOnly 'org.jetbrains:annotations:21.0.1' } diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 722e5474388..f556c96095a 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -31,8 +31,8 @@ test.dependsOn customNeo4jPluginJar dependencies { customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.9" - compile project(":testcontainers") + api project(":testcontainers") - testCompile "org.neo4j.driver:neo4j-java-driver:1.7.5" - testCompile 'org.assertj:assertj-core:3.19.0' + testImplementation "org.neo4j.driver:neo4j-java-driver:1.7.5" + testImplementation 'org.assertj:assertj-core:3.19.0' } diff --git a/modules/nginx/build.gradle b/modules/nginx/build.gradle index c0fe33a8258..5bfcfae910b 100644 --- a/modules/nginx/build.gradle +++ b/modules/nginx/build.gradle @@ -1,7 +1,7 @@ description = "Testcontainers :: Nginx" dependencies { - compile project(':testcontainers') + api project(':testcontainers') compileOnly 'org.jetbrains:annotations:21.0.1' - testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/modules/oracle-xe/build.gradle b/modules/oracle-xe/build.gradle index 026521e9a0c..5a6040ac81f 100644 --- a/modules/oracle-xe/build.gradle +++ b/modules/oracle-xe/build.gradle @@ -1,8 +1,8 @@ description = "Testcontainers :: JDBC :: Oracle XE" dependencies { - compile project(':jdbc') + api project(':jdbc') - testCompile project(':jdbc-test') - testCompile 'com.oracle.ojdbc:ojdbc8:19.3.0.0' + testImplementation project(':jdbc-test') + testImplementation 'com.oracle.ojdbc:ojdbc8:19.3.0.0' } diff --git a/modules/orientdb/build.gradle b/modules/orientdb/build.gradle index ffa1350172a..5be59938603 100644 --- a/modules/orientdb/build.gradle +++ b/modules/orientdb/build.gradle @@ -1,11 +1,11 @@ description = "TestContainers :: Orientdb" dependencies { - compile project(":testcontainers") + api project(":testcontainers") - compile "com.orientechnologies:orientdb-client:3.0.24" + api "com.orientechnologies:orientdb-client:3.0.24" - testCompile 'org.assertj:assertj-core:3.12.0' - testCompile 'org.apache.tinkerpop:gremlin-driver:3.3.4' - testCompile "com.orientechnologies:orientdb-gremlin:3.0.18" + testImplementation 'org.assertj:assertj-core:3.12.0' + testImplementation 'org.apache.tinkerpop:gremlin-driver:3.3.4' + testImplementation "com.orientechnologies:orientdb-gremlin:3.0.18" } diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index c01cd67df48..fe9e58820d8 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -4,17 +4,17 @@ dependencies { annotationProcessor 'com.google.auto.service:auto-service:1.0' compileOnly 'com.google.auto.service:auto-service:1.0' - compile project(':jdbc') + api project(':jdbc') compileOnly project(':r2dbc') compileOnly 'io.r2dbc:r2dbc-postgresql:0.8.8.RELEASE' - testCompile project(':jdbc-test') - testCompile project(':test-support') - testCompile 'org.postgresql:postgresql:42.2.18' + testImplementation project(':jdbc-test') + testImplementation project(':test-support') + testImplementation 'org.postgresql:postgresql:42.2.18' - testCompile testFixtures(project(':r2dbc')) - testCompile 'io.r2dbc:r2dbc-postgresql:0.8.5.RELEASE' + testImplementation testFixtures(project(':r2dbc')) + testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.5.RELEASE' compileOnly 'org.jetbrains:annotations:21.0.1' } diff --git a/modules/presto/build.gradle b/modules/presto/build.gradle index 4130fb74891..1db76199969 100644 --- a/modules/presto/build.gradle +++ b/modules/presto/build.gradle @@ -1,9 +1,9 @@ description = "Testcontainers :: JDBC :: Presto" dependencies { - compile project(':jdbc') + api project(':jdbc') - testCompile project(':jdbc-test') - testCompile 'io.prestosql:presto-jdbc:331' + testImplementation project(':jdbc-test') + testImplementation 'io.prestosql:presto-jdbc:331' compileOnly 'org.jetbrains:annotations:20.0.0' } diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 10fe90dadf9..92cf910f259 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -1,9 +1,9 @@ description = "Testcontainers :: Pulsar" dependencies { - compile project(':testcontainers') + api project(':testcontainers') - testCompile group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.7.2' - testCompile group: 'org.assertj', name: 'assertj-core', version: '3.18.1' - testCompile group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.7.2' + testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.7.2' + testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.18.1' + testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.7.2' } diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle index a7388ebc6d2..21d3c1894e4 100644 --- a/modules/r2dbc/build.gradle +++ b/modules/r2dbc/build.gradle @@ -8,13 +8,13 @@ dependencies { annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6' compileOnly 'com.google.auto.service:auto-service:1.0-rc6' - compile project(':testcontainers') - compile 'io.r2dbc:r2dbc-spi:0.8.1.RELEASE' + api project(':testcontainers') + api 'io.r2dbc:r2dbc-spi:0.8.1.RELEASE' - testCompile 'org.assertj:assertj-core:3.14.0' - testCompile 'io.r2dbc:r2dbc-postgresql:0.8.1.RELEASE' - testCompile project(':postgresql') + testImplementation 'org.assertj:assertj-core:3.14.0' + testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.1.RELEASE' + testImplementation project(':postgresql') - testFixturesCompile 'io.projectreactor:reactor-core:3.3.4.RELEASE' - testFixturesCompile 'org.assertj:assertj-core:3.14.0' + testFixturesImplementation 'io.projectreactor:reactor-core:3.3.4.RELEASE' + testFixturesImplementation 'org.assertj:assertj-core:3.14.0' } diff --git a/modules/rabbitmq/build.gradle b/modules/rabbitmq/build.gradle index 73524c2da18..13638b51b97 100644 --- a/modules/rabbitmq/build.gradle +++ b/modules/rabbitmq/build.gradle @@ -1,8 +1,8 @@ description = "TestContainers :: RabbitMQ" dependencies { - compile project(":testcontainers") - testCompile 'com.rabbitmq:amqp-client:5.7.0' - testCompile 'org.assertj:assertj-core:3.12.2' + api project(":testcontainers") + testImplementation 'com.rabbitmq:amqp-client:5.7.0' + testImplementation 'org.assertj:assertj-core:3.12.2' compileOnly 'org.jetbrains:annotations:20.0.0' } diff --git a/modules/selenium/build.gradle b/modules/selenium/build.gradle index e9e4bf2d4a5..f91bb361775 100644 --- a/modules/selenium/build.gradle +++ b/modules/selenium/build.gradle @@ -1,16 +1,16 @@ description = "Testcontainers :: Selenium" dependencies { - compile project(':testcontainers') + api project(':testcontainers') provided 'org.seleniumhq.selenium:selenium-remote-driver:3.141.59' provided 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' - testCompile 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' - testCompile 'org.seleniumhq.selenium:selenium-support:3.141.59' + testImplementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' + testImplementation 'org.seleniumhq.selenium:selenium-support:3.141.59' - testCompile 'org.mortbay.jetty:jetty:6.1.26' - testCompile project(':nginx') - testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.mortbay.jetty:jetty:6.1.26' + testImplementation project(':nginx') + testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' compileOnly 'org.jetbrains:annotations:21.0.1' testCompileClasspath 'org.jetbrains:annotations:21.0.1' diff --git a/modules/solr/build.gradle b/modules/solr/build.gradle index af3193be52a..76535a2b7c8 100644 --- a/modules/solr/build.gradle +++ b/modules/solr/build.gradle @@ -1,7 +1,7 @@ description = "Testcontainers :: Solr" dependencies { - compile project(':testcontainers') - testCompile 'org.apache.solr:solr-solrj:8.3.0' + api project(':testcontainers') + testImplementation 'org.apache.solr:solr-solrj:8.3.0' } diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 8c065e389cd..013a79dbc59 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -5,18 +5,18 @@ plugins { description = "Testcontainers :: Spock-Extension" dependencies { - compile project(':testcontainers') - compile 'org.spockframework:spock-core:1.3-groovy-2.5' + api project(':testcontainers') + api 'org.spockframework:spock-core:1.3-groovy-2.5' - testCompile project(':selenium') - testCompile project(':mysql') - testCompile project(':postgresql') + testImplementation project(':selenium') + testImplementation project(':mysql') + testImplementation project(':postgresql') - testCompile 'com.zaxxer:HikariCP:4.0.3' - testCompile 'org.apache.httpcomponents:httpclient:4.5.13' + testImplementation 'com.zaxxer:HikariCP:4.0.3' + testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' - testRuntime 'org.postgresql:postgresql:42.2.18' - testRuntime 'mysql:mysql-connector-java:8.0.25' + testRuntimeOnly 'org.postgresql:postgresql:42.2.18' + testRuntimeOnly 'mysql:mysql-connector-java:8.0.25' testCompileClasspath 'org.jetbrains:annotations:21.0.1' } diff --git a/modules/toxiproxy/build.gradle b/modules/toxiproxy/build.gradle index 47221d68e40..3d7d110609b 100644 --- a/modules/toxiproxy/build.gradle +++ b/modules/toxiproxy/build.gradle @@ -1,11 +1,11 @@ description = "Testcontainers :: Toxiproxy" dependencies { - compile project(':testcontainers') - compile 'eu.rekawek.toxiproxy:toxiproxy-java:2.1.4' + api project(':testcontainers') + api 'eu.rekawek.toxiproxy:toxiproxy-java:2.1.4' - testCompile 'redis.clients:jedis:3.0.1' - testCompile 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'redis.clients:jedis:3.0.1' + testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' - testCompile project(':test-support') + testImplementation project(':test-support') } diff --git a/modules/trino/build.gradle b/modules/trino/build.gradle index d726cbc8e76..5a3b733934f 100644 --- a/modules/trino/build.gradle +++ b/modules/trino/build.gradle @@ -1,9 +1,9 @@ description = "Testcontainers :: JDBC :: Trino" dependencies { - compile project(':jdbc') + api project(':jdbc') - testCompile project(':jdbc-test') - testCompile 'io.trino:trino-jdbc:352' + testImplementation project(':jdbc-test') + testImplementation 'io.trino:trino-jdbc:352' compileOnly 'org.jetbrains:annotations:20.0.0' } diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index 17edbd3f3fd..50797658fbb 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -1,10 +1,10 @@ description = "Testcontainers :: Vault" dependencies { - compile project(':testcontainers') + api project(':testcontainers') - testCompile 'com.bettercloud:vault-java-driver:5.1.0' - testCompile 'io.rest-assured:rest-assured:4.4.0' - testCompile 'org.assertj:assertj-core:3.19.0' + testImplementation 'com.bettercloud:vault-java-driver:5.1.0' + testImplementation 'io.rest-assured:rest-assured:4.4.0' + testImplementation 'org.assertj:assertj-core:3.19.0' } From 636b7f9c9eea5e98c3d8be9c902e9890cb0ae710 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 15:49:21 +0000 Subject: [PATCH 1929/5615] Bump guava from 30.1-jre to 30.1.1-jre in /core Bumps [guava](https://github.com/google/guava) from 30.1-jre to 30.1.1-jre. - [Release notes](https://github.com/google/guava/releases) - [Commits](https://github.com/google/guava/commits) Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index d260ae5317a..7f10e4847de 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -5,7 +5,7 @@ buildscript { dependencies { // https://github.com/melix/japicmp-gradle-plugin/issues/36 - classpath 'com.google.guava:guava:30.1-jre' + classpath 'com.google.guava:guava:30.1.1-jre' } } From d4e5687da81bab1d8f6352c261512b516b6a86f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 15:49:22 +0000 Subject: [PATCH 1930/5615] Bump guava from 30.1-jre to 30.1.1-jre in /modules/jdbc-test Bumps [guava](https://github.com/google/guava) from 30.1-jre to 30.1.1-jre. - [Release notes](https://github.com/google/guava/releases) - [Commits](https://github.com/google/guava/commits) Signed-off-by: dependabot[bot] --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 3da4eb0f691..98ee2ce0880 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -2,7 +2,7 @@ dependencies { api project(':jdbc') api project(':test-support') - api 'com.google.guava:guava:30.1-jre' + api 'com.google.guava:guava:30.1.1-jre' api 'org.apache.commons:commons-lang3:3.12.0' api 'com.zaxxer:HikariCP-java6:2.3.13' api 'commons-dbutils:commons-dbutils:1.7' From bb9ead01af722fa6c52d84b1461b0656b0ec21bf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 15:49:24 +0000 Subject: [PATCH 1931/5615] Bump amqp-client from 5.9.0 to 5.12.0 in /core Bumps [amqp-client](https://github.com/rabbitmq/rabbitmq-java-client) from 5.9.0 to 5.12.0. - [Release notes](https://github.com/rabbitmq/rabbitmq-java-client/releases) - [Commits](https://github.com/rabbitmq/rabbitmq-java-client/compare/v5.9.0...v5.12.0) Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index d260ae5317a..3d273fc62f0 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -190,7 +190,7 @@ dependencies { testImplementation 'org.apache.httpcomponents:httpclient:4.5.9' testImplementation 'redis.clients:jedis:3.6.0' - testImplementation 'com.rabbitmq:amqp-client:5.9.0' + testImplementation 'com.rabbitmq:amqp-client:5.12.0' testImplementation 'org.mongodb:mongo-java-driver:3.12.7' testImplementation ('org.mockito:mockito-core:3.10.0') { From aaad895f8ee1550a2e2d2fd7fb0ed5d43f52f87e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 15:49:25 +0000 Subject: [PATCH 1932/5615] Bump snakeyaml from 1.27 to 1.29 in /core Bumps [snakeyaml](https://bitbucket.org/asomov/snakeyaml) from 1.27 to 1.29. - [Commits](https://bitbucket.org/asomov/snakeyaml/branches/compare/snakeyaml-1.29..snakeyaml-1.27) --- updated-dependencies: - dependency-name: org.yaml:snakeyaml dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index d260ae5317a..7d05b168ea3 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -180,7 +180,7 @@ dependencies { api 'com.github.docker-java:docker-java-transport-zerodep:3.2.8' - shaded "org.yaml:snakeyaml:1.27" + shaded "org.yaml:snakeyaml:1.29" shaded 'org.glassfish.main.external:trilead-ssh2-repackaged:4.1.2' From 345b9f76bdceb0a4a7ef0dae880a3e2d4fc2e722 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 15:49:25 +0000 Subject: [PATCH 1933/5615] Bump kafka-clients from 2.3.1 to 2.8.0 in /examples Bumps kafka-clients from 2.3.1 to 2.8.0. Signed-off-by: dependabot[bot] --- examples/kafka-cluster/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index 6ba1a966dae..1ba81fda105 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -10,7 +10,7 @@ dependencies { testCompileOnly "org.projectlombok:lombok:1.18.20" testAnnotationProcessor "org.projectlombok:lombok:1.18.10" testImplementation 'org.testcontainers:kafka' - testImplementation 'org.apache.kafka:kafka-clients:2.3.1' + testImplementation 'org.apache.kafka:kafka-clients:2.8.0' testImplementation 'org.assertj:assertj-core:3.14.0' testImplementation 'com.google.guava:guava:23.0' testImplementation 'org.slf4j:slf4j-simple:1.7.30' From d60123822c81b77fda450cbac74840d85cb51237 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 15:49:26 +0000 Subject: [PATCH 1934/5615] Bump postgresql from 42.2.18 to 42.2.20 in /modules/spock Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.18 to 42.2.20. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/REL42.2.20/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.18...REL42.2.20) Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 013a79dbc59..214005466ce 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'com.zaxxer:HikariCP:4.0.3' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' - testRuntimeOnly 'org.postgresql:postgresql:42.2.18' + testRuntimeOnly 'org.postgresql:postgresql:42.2.20' testRuntimeOnly 'mysql:mysql-connector-java:8.0.25' testCompileClasspath 'org.jetbrains:annotations:21.0.1' From 0c775ca5517c6487683b164b25c2646f1046f76b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 15:49:27 +0000 Subject: [PATCH 1935/5615] Bump postgresql from 42.2.18 to 42.2.20 in /modules/postgresql Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.18 to 42.2.20. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/REL42.2.20/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.18...REL42.2.20) Signed-off-by: dependabot[bot] --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index fe9e58820d8..c44dbe7d009 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -11,7 +11,7 @@ dependencies { testImplementation project(':jdbc-test') testImplementation project(':test-support') - testImplementation 'org.postgresql:postgresql:42.2.18' + testImplementation 'org.postgresql:postgresql:42.2.20' testImplementation testFixtures(project(':r2dbc')) testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.5.RELEASE' From d2370c82ee22e0fab74776b55cf92f2fa77be0b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 15:49:34 +0000 Subject: [PATCH 1936/5615] Bump jedis from 3.4.0 to 3.6.0 in /examples Bumps [jedis](https://github.com/redis/jedis) from 3.4.0 to 3.6.0. - [Release notes](https://github.com/redis/jedis/releases) - [Commits](https://github.com/redis/jedis/compare/jedis-3.4.0...jedis-3.6.0) Signed-off-by: dependabot[bot] --- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 42553843710..c19c5b22f23 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' - implementation 'redis.clients:jedis:3.4.0' + implementation 'redis.clients:jedis:3.6.0' implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index de1e2d8a0a8..a968a6d2bf3 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' - implementation 'redis.clients:jedis:3.4.0' + implementation 'redis.clients:jedis:3.6.0' implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 54a16b25230..dfbe856ea62 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { - implementation 'redis.clients:jedis:3.4.0' + implementation 'redis.clients:jedis:3.6.0' implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.30' From 80a082661b470a526e89037c26a54d14a2a235ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 15:49:36 +0000 Subject: [PATCH 1937/5615] Bump tomcat-jdbc from 9.0.40 to 10.0.6 in /modules/jdbc-test Bumps tomcat-jdbc from 9.0.40 to 10.0.6. Signed-off-by: dependabot[bot] --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 3da4eb0f691..8ccf8ee5f88 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -14,7 +14,7 @@ dependencies { exclude(group: "net.java.dev.jna", module: "jna") } - api 'org.apache.tomcat:tomcat-jdbc:9.0.40' + api 'org.apache.tomcat:tomcat-jdbc:10.0.6' api 'org.vibur:vibur-dbcp:25.0' api 'mysql:mysql-connector-java:8.0.25' } From 6df46bf62622098c3a7286fa6d510bd9bb67867f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 15:49:39 +0000 Subject: [PATCH 1938/5615] Bump annotations from 20.1.0 to 21.0.1 in /examples Bumps [annotations](https://github.com/JetBrains/java-annotations) from 20.1.0 to 21.0.1. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/20.1.0...21.0.1) Signed-off-by: dependabot[bot] --- examples/mongodb-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/mongodb-container/build.gradle b/examples/mongodb-container/build.gradle index 8a4c727f069..8d8c8731c86 100644 --- a/examples/mongodb-container/build.gradle +++ b/examples/mongodb-container/build.gradle @@ -9,5 +9,5 @@ repositories { dependencies { testImplementation 'org.testcontainers:testcontainers' testImplementation 'junit:junit:4.13.2' - testCompileClasspath 'org.jetbrains:annotations:20.1.0' + testCompileClasspath 'org.jetbrains:annotations:21.0.1' } From 89a336843c555480ba9e8af2683bea1a464dff23 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 15:49:40 +0000 Subject: [PATCH 1939/5615] Bump spock-core from 1.3-groovy-2.5 to 2.0-groovy-3.0 in /modules/spock Bumps spock-core from 1.3-groovy-2.5 to 2.0-groovy-3.0. Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 013a79dbc59..e51bb311d4d 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -6,7 +6,7 @@ description = "Testcontainers :: Spock-Extension" dependencies { api project(':testcontainers') - api 'org.spockframework:spock-core:1.3-groovy-2.5' + api 'org.spockframework:spock-core:2.0-groovy-3.0' testImplementation project(':selenium') testImplementation project(':mysql') From 66ef00a5573c3e20890562c4dc2fb2b8023ee3c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 15:49:43 +0000 Subject: [PATCH 1940/5615] Bump junit-jupiter-api from 5.7.1 to 5.7.2 in /modules/junit-jupiter Bumps [junit-jupiter-api](https://github.com/junit-team/junit5) from 5.7.1 to 5.7.2. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.7.1...r5.7.2) Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index ad74eb3ec26..3bd78867f70 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -2,7 +2,7 @@ description = "Testcontainers :: JUnit Jupiter Extension" dependencies { api project(':testcontainers') - api 'org.junit.jupiter:junit-jupiter-api:5.7.1' + api 'org.junit.jupiter:junit-jupiter-api:5.7.2' testImplementation project(':mysql') testImplementation project(':postgresql') From 685997b28fa7d3c5eedb62db365f664bff5fdc24 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 15:49:44 +0000 Subject: [PATCH 1941/5615] Bump postgresql from 42.2.18 to 42.2.20 in /examples Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.18 to 42.2.20. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.18...REL42.2.20) Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index b3d339d4db8..edb545e79d3 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' implementation 'com.squareup.okhttp3:okhttp:4.9.1' implementation 'org.json:json:20210307' - testImplementation 'org.postgresql:postgresql:42.2.18' + testImplementation 'org.postgresql:postgresql:42.2.20' testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.testcontainers:postgresql' } From a7a6c18843c00756b56101b69c59f24c01f52791 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 15:49:45 +0000 Subject: [PATCH 1942/5615] Bump transport from 7.12.1 to 7.13.1 in /modules/elasticsearch Bumps [transport](https://github.com/elastic/elasticsearch) from 7.12.1 to 7.13.1. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.12.1...v7.13.1) --- updated-dependencies: - dependency-name: org.elasticsearch.client:transport dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index e66e045ead4..e1921e755f8 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,6 +3,6 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') testImplementation "org.elasticsearch.client:elasticsearch-rest-client:7.13.0" - testImplementation "org.elasticsearch.client:transport:7.12.1" + testImplementation "org.elasticsearch.client:transport:7.13.1" testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From a8156a0f413ec5094a03524ffff4c09abb1032db Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 15:49:45 +0000 Subject: [PATCH 1943/5615] Bump junit-jupiter-params from 5.7.1 to 5.7.2 in /modules/junit-jupiter Bumps [junit-jupiter-params](https://github.com/junit-team/junit5) from 5.7.1 to 5.7.2. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.7.1...r5.7.2) Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index ad74eb3ec26..5f242c549ae 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -13,7 +13,7 @@ dependencies { exclude(module: 'hamcrest-core') } testImplementation 'org.assertj:assertj-core:3.18.1' - testImplementation 'org.junit.jupiter:junit-jupiter-params:5.7.1' + testImplementation 'org.junit.jupiter:junit-jupiter-params:5.7.2' testRuntimeOnly 'org.postgresql:postgresql:42.2.20' testRuntimeOnly 'mysql:mysql-connector-java:8.0.25' From 7c7829072f34992b7607b211a94afc2b049c6eae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 15:49:46 +0000 Subject: [PATCH 1944/5615] Bump s3 from 2.16.62 to 2.16.80 in /modules/localstack Bumps s3 from 2.16.62 to 2.16.80. --- updated-dependencies: - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 503a3a7bf49..87cc407468d 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.amazonaws:aws-java-sdk-s3:1.11.1030' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.1' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.11.807' - testImplementation 'software.amazon.awssdk:s3:2.16.62' + testImplementation 'software.amazon.awssdk:s3:2.16.80' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 04baf3743197ec8cdfb8a70be66054d0a2376d07 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 15:49:48 +0000 Subject: [PATCH 1945/5615] Bump clickhouse-jdbc from 0.2.4 to 0.3.1-patch in /modules/clickhouse Bumps [clickhouse-jdbc](https://github.com/ClickHouse/clickhouse-jdbc) from 0.2.4 to 0.3.1-patch. - [Release notes](https://github.com/ClickHouse/clickhouse-jdbc/releases) - [Changelog](https://github.com/ClickHouse/clickhouse-jdbc/blob/master/CHANGELOG) - [Commits](https://github.com/ClickHouse/clickhouse-jdbc/compare/release_0.2.4...v0.3.1-patch) --- updated-dependencies: - dependency-name: ru.yandex.clickhouse:clickhouse-jdbc dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/clickhouse/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/clickhouse/build.gradle b/modules/clickhouse/build.gradle index 7eb68eb2e51..39b269d794b 100644 --- a/modules/clickhouse/build.gradle +++ b/modules/clickhouse/build.gradle @@ -5,5 +5,5 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'ru.yandex.clickhouse:clickhouse-jdbc:0.2.4' + testImplementation 'ru.yandex.clickhouse:clickhouse-jdbc:0.3.1-patch' } From b77e22f81fcb0308a99c9c9c6e54e9f9dbbb270f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 15:49:51 +0000 Subject: [PATCH 1946/5615] Bump annotations from 20.1.0 to 21.0.1 in /core Bumps [annotations](https://github.com/JetBrains/java-annotations) from 20.1.0 to 21.0.1. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/20.1.0...21.0.1) Signed-off-by: dependabot[bot] --- core/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index d260ae5317a..a02aad3ddf7 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -154,8 +154,8 @@ dependencies { api 'junit:junit:4.12' api 'org.slf4j:slf4j-api:1.7.30' - compileOnly 'org.jetbrains:annotations:20.1.0' - testCompileClasspath 'org.jetbrains:annotations:20.0.0' + compileOnly 'org.jetbrains:annotations:21.0.1' + testCompileClasspath 'org.jetbrains:annotations:21.0.1' api 'org.apache.commons:commons-compress:1.20' api ('org.rnorth.duct-tape:duct-tape:1.0.8') { exclude(group: 'org.jetbrains', module: 'annotations') From 4d93101cbbbe1dd97da88d86006d51a5a4ec9884 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 9 Jun 2021 22:21:44 +0200 Subject: [PATCH 1947/5615] Cleanup japicmp excludes (#4192) --- core/build.gradle | 59 +++-------------------------------------------- 1 file changed, 3 insertions(+), 56 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index d260ae5317a..353df75b0c4 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -72,64 +72,11 @@ task japicmp(type: me.champeau.gradle.japicmp.JapicmpTask) { "org.testcontainers.shaded.*", ] - // TODO remove once 1.15.0 is released - classExcludes = [ - "org.testcontainers.containers.AmbassadorContainer", - "org.testcontainers.containers.traits.VncService", - "org.testcontainers.containers.wait.HostPortWaitStrategy", - "org.testcontainers.containers.wait.HttpWaitStrategy", - "org.testcontainers.containers.wait.LogMessageWaitStrategy", - "org.testcontainers.containers.wait.Wait", - "org.testcontainers.containers.wait.WaitAllStrategy", - "org.testcontainers.containers.wait.WaitStrategy", - "org.testcontainers.dockerclient.AuditLoggingDockerClient", - "org.testcontainers.dockerclient.LogToStringContainerCallback", - "org.testcontainers.dockerclient.DockerMachineClientProviderStrategy", - "org.testcontainers.dockerclient.EnvironmentAndSystemPropertyClientProviderStrategy", - "org.testcontainers.dockerclient.NpipeSocketClientProviderStrategy", - "org.testcontainers.dockerclient.ProxiedUnixSocketClientProviderStrategy", - "org.testcontainers.dockerclient.WindowsClientProviderStrategy", - "org.testcontainers.dockerclient.UnixSocketClientProviderStrategy", - "org.testcontainers.dockerclient.auth.AuthDelegatingDockerClientConfig", - "org.testcontainers.containers.GenericContainer\$AbstractWaitStrategy", - "org.testcontainers.dockerclient.transport.okhttp.NamedPipeSocketFactory", - "org.testcontainers.dockerclient.transport.okhttp.OkHttpDockerCmdExecFactory", - "org.testcontainers.dockerclient.transport.okhttp.UnixSocketFactory", - "org.testcontainers.containers.output.FrameConsumerResultCallback", - "org.testcontainers.images.TimeLimitedLoggedPullImageResultCallback", - ] - - methodExcludes = [ - "org.testcontainers.dockerclient.DockerClientConfigUtils#getDetectedDockerHostIp()", - "org.testcontainers.dockerclient.DockerClientConfigUtils#getDockerHostIpAddress(com.github.dockerjava.core.DockerClientConfig)", - - "org.testcontainers.dockerclient.DockerClientProviderStrategy#checkOSType()", - "org.testcontainers.dockerclient.DockerClientProviderStrategy#getClientForConfig(com.github.dockerjava.core.DockerClientConfig)", - "org.testcontainers.dockerclient.DockerClientProviderStrategy#ping(com.github.dockerjava.api.DockerClient, int)", - "org.testcontainers.dockerclient.DockerClientProviderStrategy#test()", - - "org.testcontainers.dockerclient.UnixSocketClientProviderStrategy#tryConfiguration(java.lang.String)", - - "org.testcontainers.utility.ResourceReaper#start(java.lang.String, com.github.dockerjava.api.DockerClient, boolean)", + classExcludes = [] - "org.testcontainers.containers.Container#fetchDockerDaemonInfo()", - "org.testcontainers.containers.Container#getDockerDaemonInfo()", + methodExcludes = [] - "org.testcontainers.containers.GenericContainer#fetchDockerDaemonInfo()", - "org.testcontainers.containers.GenericContainer#getDockerDaemonInfo()", - - "org.testcontainers.containers.Container\$ExecResult#Container\$ExecResult(java.lang.String, java.lang.String)", - ] - - fieldExcludes = [ - "org.testcontainers.containers.GenericContainer#containerId", - "org.testcontainers.containers.GenericContainer#containerName", - "org.testcontainers.containers.GenericContainer#dockerDaemonInfo", - - "org.testcontainers.dockerclient.DockerClientProviderStrategy#LOGGER", - "org.testcontainers.dockerclient.DockerClientProviderStrategy#client", - "org.testcontainers.dockerclient.DockerClientProviderStrategy#config", - ] + fieldExcludes = [] onlyBinaryIncompatibleModified = true htmlOutputFile = file("$buildDir/reports/japi.html") From b86410e694f54c28ecf2c25448ff86c7b37c82ae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Jun 2021 08:19:18 +0000 Subject: [PATCH 1948/5615] Bump assertj-core from 3.18.1 to 3.19.0 in /modules/pulsar Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.18.1 to 3.19.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.18.1...assertj-core-3.19.0) Signed-off-by: dependabot[bot] --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 92cf910f259..61e1208381d 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':testcontainers') testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.7.2' - testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.18.1' + testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.19.0' testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.7.2' } From c903ae0b17c82f18153b99d0a65f163d0c88079c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Jun 2021 08:19:21 +0000 Subject: [PATCH 1949/5615] Bump assertj-core from 3.14.0 to 3.19.0 in /examples Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.14.0 to 3.19.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.14.0...assertj-core-3.19.0) Signed-off-by: dependabot[bot] --- examples/kafka-cluster/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index 1ba81fda105..cb1e9e31e7b 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -11,7 +11,7 @@ dependencies { testAnnotationProcessor "org.projectlombok:lombok:1.18.10" testImplementation 'org.testcontainers:kafka' testImplementation 'org.apache.kafka:kafka-clients:2.8.0' - testImplementation 'org.assertj:assertj-core:3.14.0' + testImplementation 'org.assertj:assertj-core:3.19.0' testImplementation 'com.google.guava:guava:23.0' testImplementation 'org.slf4j:slf4j-simple:1.7.30' } From c75a9dc96d523b119d4b513e8b6d9e9a23e999a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Jun 2021 08:19:24 +0000 Subject: [PATCH 1950/5615] Bump assertj-core from 3.18.1 to 3.19.0 in /modules/kafka Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.18.1 to 3.19.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.18.1...assertj-core-3.19.0) Signed-off-by: dependabot[bot] --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index 56abadbce47..bb112755352 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':testcontainers') testImplementation 'org.apache.kafka:kafka-clients:2.8.0' - testImplementation 'org.assertj:assertj-core:3.18.1' + testImplementation 'org.assertj:assertj-core:3.19.0' testImplementation 'com.google.guava:guava:23.0' } From ae8926f712815790cc80c5defd73ad44641de1cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Jun 2021 08:19:37 +0000 Subject: [PATCH 1951/5615] Bump assertj-core from 3.18.1 to 3.19.0 in /core Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.18.1 to 3.19.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.18.1...assertj-core-3.19.0) Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 1c469f379d1..adc97a6c69f 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -147,7 +147,7 @@ dependencies { testImplementation files('testlib/repo/fakejar/fakejar/0/fakejar-0.jar') testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' - testImplementation 'org.assertj:assertj-core:3.18.1' + testImplementation 'org.assertj:assertj-core:3.19.0' testImplementation project(':test-support') jarFileTestCompileOnly "org.projectlombok:lombok:${lombok.version}" From edbad59ef7e7874c17b2c976858bb742ea93864e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Jun 2021 08:19:41 +0000 Subject: [PATCH 1952/5615] Bump assertj-core from 3.18.1 to 3.19.0 in /modules/junit-jupiter Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.18.1 to 3.19.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.18.1...assertj-core-3.19.0) Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index a21a97824ed..ab2477ed883 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -12,7 +12,7 @@ dependencies { testImplementation ('org.mockito:mockito-core:3.10.0') { exclude(module: 'hamcrest-core') } - testImplementation 'org.assertj:assertj-core:3.18.1' + testImplementation 'org.assertj:assertj-core:3.19.0' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.7.2' testRuntimeOnly 'org.postgresql:postgresql:42.2.20' From eac7bbb963ee8233a90e537b495c2fa7e5a0ef86 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Jun 2021 08:33:37 +0000 Subject: [PATCH 1953/5615] Bump org.springframework.boot from 2.4.1 to 2.5.0 in /examples Bumps [org.springframework.boot](https://github.com/spring-projects/spring-boot) from 2.4.1 to 2.5.0. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.4.1...v2.5.0) Signed-off-by: dependabot[bot] --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot-kotlin-redis/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index a671c1832fe..bcd2f97100b 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.4.1' + id 'org.springframework.boot' version '2.5.0' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 61a74a3950e..3f3469f8548 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,5 +1,5 @@ plugins { - id("org.springframework.boot") version "2.4.1" + id("org.springframework.boot") version "2.5.0" id("org.jetbrains.kotlin.jvm") version "1.5.10" id("org.jetbrains.kotlin.plugin.spring") version "1.5.10" } diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index ce2c028f83a..33f5b9aa878 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.4.1' + id 'org.springframework.boot' version '2.5.0' } apply plugin: 'io.spring.dependency-management' From 618dd80ac383221c18022548bfd7a20ad7695ce0 Mon Sep 17 00:00:00 2001 From: Fedor Korotkov Date: Mon, 14 Jun 2021 05:37:17 -0400 Subject: [PATCH 1954/5615] Fixed NPE when `getContextClassLoader()` returns `null` (#4193) --- .../main/java/org/testcontainers/utility/MountableFile.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java index 1982538f45d..071d36631b0 100644 --- a/core/src/main/java/org/testcontainers/utility/MountableFile.java +++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java @@ -130,6 +130,10 @@ private static URL getClasspathResource(@NotNull final String resourcePath, @Not classLoadersToSearch.add(MountableFile.class.getClassLoader()); for (final ClassLoader classLoader : classLoadersToSearch) { + if (classLoader == null) { + continue; + } + URL resource = classLoader.getResource(resourcePath); if (resource != null) { return resource; From f2ec729b7fee7e7119a25e11269fbfadbc17e65d Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 17 Jun 2021 21:29:56 +0200 Subject: [PATCH 1955/5615] Update docker-java to 3.2.10 (#4202) --- core/build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index adc97a6c69f..f4cdfbea410 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -108,9 +108,9 @@ dependencies { exclude(group: 'org.jetbrains', module: 'annotations') } - api "com.github.docker-java:docker-java-api:3.2.8" + api "com.github.docker-java:docker-java-api:3.2.10" - shaded ('com.github.docker-java:docker-java-core:3.2.8') { + shaded ('com.github.docker-java:docker-java-core:3.2.10') { exclude(group: 'com.github.docker-java', module: 'docker-java-api') exclude(group: 'com.github.docker-java', module: 'docker-java-transport') exclude(group: 'com.fasterxml.jackson.core', module: 'jackson-annotations') @@ -119,13 +119,13 @@ dependencies { exclude(group: 'org.apache.commons', module: 'commons-compress') } - shaded ('com.github.docker-java:docker-java-transport-okhttp:3.2.8') { + shaded ('com.github.docker-java:docker-java-transport-okhttp:3.2.10') { exclude(group: 'com.github.docker-java', module: 'docker-java-core') exclude(group: 'net.java.dev.jna') exclude(group: 'org.slf4j') } - api 'com.github.docker-java:docker-java-transport-zerodep:3.2.8' + api 'com.github.docker-java:docker-java-transport-zerodep:3.2.10' shaded "org.yaml:snakeyaml:1.29" From f68994f4a2af1940b29c0709b720c5b69680cc0e Mon Sep 17 00:00:00 2001 From: gradle-update-robot Date: Thu, 24 Jun 2021 00:06:26 +0000 Subject: [PATCH 1956/5615] Update Gradle Wrapper from 7.0.2 to 7.1. Signed-off-by: gradle-update-robot --- examples/gradle/wrapper/gradle-wrapper.jar | Bin 58910 -> 59536 bytes .../gradle/wrapper/gradle-wrapper.properties | 3 +- examples/gradlew | 4 +- examples/gradlew.bat | 193 ++++++++---------- 4 files changed, 93 insertions(+), 107 deletions(-) diff --git a/examples/gradle/wrapper/gradle-wrapper.jar b/examples/gradle/wrapper/gradle-wrapper.jar index 62d4c053550b91381bbd28b1afc82d634bf73a8a..7454180f2ae8848c63b8b4dea2cb829da983f2fa 100644 GIT binary patch delta 18328 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 Y(TzS~Xdo8@nTUZrK}a{fL*HNQe=4o%4FCWD delta 17568 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 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega From ad264f0b0e47eba6005244d0ff58d26749002c42 Mon Sep 17 00:00:00 2001 From: gradle-update-robot Date: Thu, 24 Jun 2021 00:06:34 +0000 Subject: [PATCH 1957/5615] Update Gradle Wrapper from 7.0.2 to 7.1. Signed-off-by: gradle-update-robot --- gradle/wrapper/gradle-wrapper.jar | Bin 55616 -> 59536 bytes gradle/wrapper/gradle-wrapper.properties | 6 +++--- gradlew | 4 +++- gradlew.bat | 25 +++++++---------------- 4 files changed, 13 insertions(+), 22 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 5c2d1cf016b3885f6930543d57b744ea8c220a1a..7454180f2ae8848c63b8b4dea2cb829da983f2fa 100644 GIT binary patch delta 26482 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 delta 22605 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 4a66b0f1980..283500479a3 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sun Apr 05 19:28:23 BST 2020 -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -zipStorePath=wrapper/dists +distributionSha256Sum=a9e356a21595348b6f04b024ed0b08ac8aea6b2ac37e6c0ef58e51549cd7b9cb +distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-all.zip zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 2fe81a7d95e..744e882ed57 100755 --- a/gradlew +++ b/gradlew @@ -72,7 +72,7 @@ case "`uname`" in Darwin* ) darwin=true ;; - MINGW* ) + MSYS* | MINGW* ) msys=true ;; NONSTOP* ) @@ -82,6 +82,7 @@ esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -129,6 +130,7 @@ fi if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath diff --git a/gradlew.bat b/gradlew.bat index 24467a141f7..ac1b06f9382 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @@ -37,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -51,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -61,28 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell From 6756ed465ea89d870a16de6a291e29e19364175a Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 25 Jun 2021 16:39:50 +0100 Subject: [PATCH 1958/5615] Remove GitHub Sponsors button --- docs/index.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/index.md b/docs/index.md index 139cbcd3f4e..16f61899947 100644 --- a/docs/index.md +++ b/docs/index.md @@ -93,10 +93,6 @@ testImplementation('org.testcontainers:mysql') //no version specified ## Sponsors -Testcontainers is on Github Sponsors! Please consider sponsoring us to sustain the project and help us improve it further. - - - A huge thank you to our sponsors: ### Bronze sponsors From e01c5a3214a0903fc3bf0dfc4c1fc3d5bc24e028 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 29 Jun 2021 11:23:34 +0100 Subject: [PATCH 1959/5615] Remove stalebot (#4216) --- .github/stale.yml | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 .github/stale.yml diff --git a/.github/stale.yml b/.github/stale.yml deleted file mode 100644 index 5f903fb0189..00000000000 --- a/.github/stale.yml +++ /dev/null @@ -1,25 +0,0 @@ -# Number of days of inactivity before an issue becomes stale -daysUntilStale: 90 -# Number of days of inactivity before a stale issue is closed -daysUntilClose: 14 -# Issues with these labels will never be considered stale -exemptLabels: - - pinned - - security - - "resolution/acknowledged" - - "help wanted" - - "good first issue" -# Label to use when marking an issue as stale -staleLabel: stale -# Comment to post when marking an issue as stale. Set to `false` to disable -markComment: > - This issue has been automatically marked as stale because it has not had - recent activity. It will be closed if no further activity occurs. If you believe - this is a mistake, please reply to this comment to keep it open. If there isn't - one already, a PR to fix or at least reproduce the problem in a test case will - always help us get back on track to tackle this. -# Comment to post when closing a stale issue. Set to `false` to disable -closeComment: > - This issue has been automatically closed due to inactivity. We apologise if - this is still an active problem for you, and would ask you to re-open the - issue if this is the case. From 271ae2f6cd864d3963da9908b677a837263c9885 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 29 Jun 2021 14:52:55 +0200 Subject: [PATCH 1960/5615] Update `docker-java` to 3.2.11 (#4220) --- core/build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index f4cdfbea410..b27031af1bb 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -108,9 +108,9 @@ dependencies { exclude(group: 'org.jetbrains', module: 'annotations') } - api "com.github.docker-java:docker-java-api:3.2.10" + api "com.github.docker-java:docker-java-api:3.2.11" - shaded ('com.github.docker-java:docker-java-core:3.2.10') { + shaded ('com.github.docker-java:docker-java-core:3.2.11') { exclude(group: 'com.github.docker-java', module: 'docker-java-api') exclude(group: 'com.github.docker-java', module: 'docker-java-transport') exclude(group: 'com.fasterxml.jackson.core', module: 'jackson-annotations') @@ -119,13 +119,13 @@ dependencies { exclude(group: 'org.apache.commons', module: 'commons-compress') } - shaded ('com.github.docker-java:docker-java-transport-okhttp:3.2.10') { + shaded ('com.github.docker-java:docker-java-transport-okhttp:3.2.11') { exclude(group: 'com.github.docker-java', module: 'docker-java-core') exclude(group: 'net.java.dev.jna') exclude(group: 'org.slf4j') } - api 'com.github.docker-java:docker-java-transport-zerodep:3.2.10' + api 'com.github.docker-java:docker-java-transport-zerodep:3.2.11' shaded "org.yaml:snakeyaml:1.29" From 655110a6cefb5a7ea26628d15d27b8339b5001a2 Mon Sep 17 00:00:00 2001 From: Tobias Happ Date: Tue, 29 Jun 2021 18:25:45 +0200 Subject: [PATCH 1961/5615] Couchbase: Update to latest version, add flushEnabled flag (#4041) Co-authored-by: Richard North --- docs/modules/databases/couchbase.md | 7 +- modules/couchbase/AUTHORS | 1 + modules/couchbase/build.gradle | 3 +- .../couchbase/BucketDefinition.java | 16 ++++ .../couchbase/CouchbaseContainer.java | 1 + .../couchbase/CouchbaseContainerTest.java | 90 ++++++++++++------- 6 files changed, 78 insertions(+), 40 deletions(-) diff --git a/docs/modules/databases/couchbase.md b/docs/modules/databases/couchbase.md index f8ed7465dd9..e45a971f626 100644 --- a/docs/modules/databases/couchbase.md +++ b/docs/modules/databases/couchbase.md @@ -18,16 +18,11 @@ Running Couchbase as a stand-in in a test: [Container definition](../../../modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java) inside_block:container_definition -3. create an environment & cluster: +3. create an cluster: [Cluster creation](../../../modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java) inside_block:cluster_creation -4. authenticate: - - [Authentication](../../../modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java) inside_block:auth - - ## Adding this module to your project dependencies Add the following dependency to your `pom.xml`/`build.gradle` file: diff --git a/modules/couchbase/AUTHORS b/modules/couchbase/AUTHORS index db292de72f1..480841c2906 100644 --- a/modules/couchbase/AUTHORS +++ b/modules/couchbase/AUTHORS @@ -1 +1,2 @@ Tayeb Chlyah +Tobias Happ diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 88eb537269d..1ce51019607 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -3,5 +3,6 @@ description = "Testcontainers :: Couchbase" dependencies { api project(':testcontainers') - testImplementation 'com.couchbase.client:java-client:2.7.15' + testImplementation 'com.couchbase.client:java-client:3.1.6' + testImplementation 'org.awaitility:awaitility:3.0.0' } diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/BucketDefinition.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/BucketDefinition.java index 8c2b11403ad..3778833182e 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/BucketDefinition.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/BucketDefinition.java @@ -22,6 +22,7 @@ public class BucketDefinition { private final String name; + private boolean flushEnabled = false; private boolean queryPrimaryIndex = true; private int quota = 100; @@ -29,6 +30,17 @@ public BucketDefinition(final String name) { this.name = name; } + /** + * Enables flush for this bucket (disabled by default). + * + * @param flushEnabled if true, the bucket can be flushed. + * @return this {@link BucketDefinition} for chaining purposes. + */ + public BucketDefinition withFlushEnabled(final boolean flushEnabled) { + this.flushEnabled = flushEnabled; + return this; + } + /** * Sets a custom bucket quota (100MB by default). * @@ -58,6 +70,10 @@ public String getName() { return name; } + public boolean hasFlushEnabled() { + return flushEnabled; + } + public boolean hasPrimaryIndex() { return queryPrimaryIndex; } diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index 8df5844f538..64489f30531 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -368,6 +368,7 @@ private void createBuckets() { @Cleanup Response response = doHttpRequest(MGMT_PORT, "/pools/default/buckets", "POST", new FormBody.Builder() .add("name", bucket.getName()) .add("ramQuotaMB", Integer.toString(bucket.getQuota())) + .add("flushEnabled", bucket.hasFlushEnabled() ? "1" : "0") .build(), true); checkSuccessfulResponse(response, "Could not create bucket " + bucket.getName()); diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java index 3a4438c1fc6..57b726eb663 100644 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java @@ -18,16 +18,17 @@ import com.couchbase.client.java.Bucket; import com.couchbase.client.java.Cluster; -import com.couchbase.client.java.CouchbaseCluster; -import com.couchbase.client.java.document.JsonDocument; -import com.couchbase.client.java.document.json.JsonObject; -import com.couchbase.client.java.env.CouchbaseEnvironment; -import com.couchbase.client.java.env.DefaultCouchbaseEnvironment; +import com.couchbase.client.java.Collection; +import com.couchbase.client.java.json.JsonObject; import org.junit.Test; import org.testcontainers.utility.DockerImageName; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import java.time.Duration; +import java.util.function.Consumer; + +import static org.awaitility.Awaitility.await; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; public class CouchbaseContainerTest { @@ -45,37 +46,60 @@ public void testBasicContainerUsage() { .withBucket(bucketDefinition) // } ) { - container.start(); - - // cluster_creation { - CouchbaseEnvironment environment = DefaultCouchbaseEnvironment - .builder() - .bootstrapCarrierDirectPort(container.getBootstrapCarrierDirectPort()) - .bootstrapHttpDirectPort(container.getBootstrapHttpDirectPort()) - .build(); - - Cluster cluster = CouchbaseCluster.create( - environment, - container.getHost() - ); - // } + setUpClient(container, cluster -> { + Bucket bucket = cluster.bucket(bucketDefinition.getName()); + bucket.waitUntilReady(Duration.ofSeconds(10L)); + + Collection collection = bucket.defaultCollection(); + + collection.upsert("foo", JsonObject.create().put("key", "value")); + + JsonObject fooObject = collection.get("foo").contentAsObject(); + + assertEquals("value", fooObject.getString("key")); + }); + } + } + + @Test + public void testBucketIsFlushableIfEnabled() { + BucketDefinition bucketDefinition = new BucketDefinition("mybucket") + .withFlushEnabled(true); - try { - // auth { - cluster.authenticate(container.getUsername(), container.getPassword()); - // } + try ( + CouchbaseContainer container = new CouchbaseContainer(COUCHBASE_IMAGE) + .withBucket(bucketDefinition) + ) { + setUpClient(container, cluster -> { + Bucket bucket = cluster.bucket(bucketDefinition.getName()); + bucket.waitUntilReady(Duration.ofSeconds(10L)); + + Collection collection = bucket.defaultCollection(); - Bucket bucket = cluster.openBucket(bucketDefinition.getName()); + collection.upsert("foo", JsonObject.create().put("key", "value")); - bucket.upsert(JsonDocument.create("foo", JsonObject.empty())); + cluster.buckets().flushBucket(bucketDefinition.getName()); - assertTrue(bucket.exists("foo")); - assertNotNull(cluster.clusterManager().getBucket(bucketDefinition.getName())); - } finally { - cluster.disconnect(); - environment.shutdown(); - } + await().untilAsserted(() -> assertFalse(collection.exists("foo").exists())); + }); } } + private void setUpClient(CouchbaseContainer container, Consumer consumer) { + container.start(); + + // cluster_creation { + Cluster cluster = Cluster.connect( + container.getConnectionString(), + container.getUsername(), + container.getPassword() + ); + // } + + try { + consumer.accept(cluster); + } finally { + cluster.disconnect(); + } + } } From adae983eb93bb83c97e7a6b49a0b83344865f83a Mon Sep 17 00:00:00 2001 From: Dennis Federico Date: Wed, 30 Jun 2021 11:24:45 +0200 Subject: [PATCH 1962/5615] Fix KafkaContainer with multiple networks defined (#4213) Current implementation exposes only two listeners PLAINTEXT and BROKER. BROKER must be advertised to explicitly specified network or bridge. If a host port is exposed using `Testcontainers.exposeHostPorts` and a network is specified, the container will be "attached" to an additional network, creating to listeners with the same name Configure the BROKER listener using the network specified (withNetwork) or "bridge" if not specified, instead of iterating over all the networks in the container info. Additional test cases when a port of the host is exposed (proxy container started) and a external network is specified to the container placing multiple network entries in the container info, making additional Kafka Broker listener entries with the same name "BROKER" Co-authored-by: Sergei Egorov --- .../containers/KafkaContainer.java | 38 ++++++++++++++----- .../containers/KafkaContainerTest.java | 19 ++++++++++ 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java index b1bc3d735f9..d9c8e35e852 100644 --- a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java +++ b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java @@ -6,8 +6,7 @@ import org.testcontainers.utility.DockerImageName; import java.nio.charset.StandardCharsets; -import java.util.stream.Collectors; -import java.util.stream.Stream; +import java.util.Comparator; /** * This container wraps Confluent Kafka and Zookeeper (optionally) @@ -122,13 +121,8 @@ protected void containerIsStarting(InspectContainerResponse containerInfo, boole } command += "export KAFKA_ZOOKEEPER_CONNECT='" + zookeeperConnect + "'\n"; - command += "export KAFKA_ADVERTISED_LISTENERS='" + Stream - .concat( - Stream.of(getBootstrapServers()), - containerInfo.getNetworkSettings().getNetworks().values().stream() - .map(it -> "BROKER://" + it.getIpAddress() + ":9092") - ) - .collect(Collectors.joining(",")) + "'\n"; + + command += "export KAFKA_ADVERTISED_LISTENERS='" + String.join(",", getBootstrapServers(), brokerAdvertisedListener(containerInfo)) + "'\n"; command += ". /etc/confluent/docker/bash-config \n"; command += "/etc/confluent/docker/configure \n"; @@ -139,4 +133,30 @@ protected void containerIsStarting(InspectContainerResponse containerInfo, boole STARTER_SCRIPT ); } + + protected String brokerAdvertisedListener(InspectContainerResponse containerInfo) { + // Kafka supports only one INTER_BROKER listener, so we have to pick one. + // The current algorithm uses the following order of resolving the IP: + // 1. Custom network's IP set via `withNetwork` + // 2. Bridge network's IP + // 3. Best effort fallback to getNetworkSettings#ipAddress + String ipAddress = containerInfo.getNetworkSettings().getNetworks().entrySet() + .stream() + .filter(it -> it.getValue().getIpAddress() != null) + .max(Comparator.comparingInt(entry -> { + if (getNetwork() != null && getNetwork().getId().equals(entry.getValue().getNetworkID())) { + return 2; + } + + if ("bridge".equals(entry.getKey())) { + return 1; + } + + return 0; + })) + .map(it -> it.getValue().getIpAddress()) + .orElseGet(() -> containerInfo.getNetworkSettings().getIpAddress()); + + return String.format("BROKER://%s:%s", ipAddress, "9092"); + } } diff --git a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java index d219aa17b13..e16081d16d9 100644 --- a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java +++ b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java @@ -24,6 +24,7 @@ import org.junit.ClassRule; import org.junit.Test; import org.rnorth.ducttape.unreliables.Unreliables; +import org.testcontainers.Testcontainers; import org.testcontainers.utility.DockerImageName; public class KafkaContainerTest { @@ -113,6 +114,24 @@ public void testConfluentPlatformVersion6() throws Exception { } } + @Test + public void testWithHostExposedPort() throws Exception { + Testcontainers.exposeHostPorts(12345); + try (KafkaContainer kafka = new KafkaContainer(KAFKA_TEST_IMAGE)) { + kafka.start(); + testKafkaFunctionality(kafka.getBootstrapServers()); + } + } + + @Test + public void testWithHostExposedPortAndExternalNetwork() throws Exception { + Testcontainers.exposeHostPorts(12345); + try (KafkaContainer kafka = new KafkaContainer(KAFKA_TEST_IMAGE).withNetwork(Network.newNetwork())) { + kafka.start(); + testKafkaFunctionality(kafka.getBootstrapServers()); + } + } + protected void testKafkaFunctionality(String bootstrapServers) throws Exception { testKafkaFunctionality(bootstrapServers, 1, 1); } From 40807676491effb9878fce92c11fe26303218e4a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 22:00:27 +0000 Subject: [PATCH 1963/5615] Bump tomcat-jdbc from 10.0.6 to 10.0.7 in /modules/jdbc-test Bumps tomcat-jdbc from 10.0.6 to 10.0.7. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 87e33f804a0..99cba7dcb70 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -14,7 +14,7 @@ dependencies { exclude(group: "net.java.dev.jna", module: "jna") } - api 'org.apache.tomcat:tomcat-jdbc:10.0.6' + api 'org.apache.tomcat:tomcat-jdbc:10.0.7' api 'org.vibur:vibur-dbcp:25.0' api 'mysql:mysql-connector-java:8.0.25' } From 18a3efd8003bb0a9d8ffdd70f8b0ab0d7b129ea3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 22:00:28 +0000 Subject: [PATCH 1964/5615] Bump assertj-core from 3.19.0 to 3.20.2 in /modules/vault Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.19.0 to 3.20.2. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.19.0...assertj-core-3.20.2) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index 50797658fbb..18897f66908 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -5,6 +5,6 @@ dependencies { testImplementation 'com.bettercloud:vault-java-driver:5.1.0' testImplementation 'io.rest-assured:rest-assured:4.4.0' - testImplementation 'org.assertj:assertj-core:3.19.0' + testImplementation 'org.assertj:assertj-core:3.20.2' } From 36eaee53b29fb5841b286ac46e9e2ad80b848bae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 22:00:29 +0000 Subject: [PATCH 1965/5615] Bump awaitility from 3.0.0 to 4.1.0 in /modules/couchbase Bumps [awaitility](https://github.com/awaitility/awaitility) from 3.0.0 to 4.1.0. - [Release notes](https://github.com/awaitility/awaitility/releases) - [Changelog](https://github.com/awaitility/awaitility/blob/master/changelog.txt) - [Commits](https://github.com/awaitility/awaitility/compare/awaitility-3.0.0...awaitility-4.1.0) --- updated-dependencies: - dependency-name: org.awaitility:awaitility dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 1ce51019607..40f59128ec8 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -4,5 +4,5 @@ dependencies { api project(':testcontainers') testImplementation 'com.couchbase.client:java-client:3.1.6' - testImplementation 'org.awaitility:awaitility:3.0.0' + testImplementation 'org.awaitility:awaitility:4.1.0' } From b1b04bdc7734ee9015e3ccb6f4685718f069814f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 22:00:29 +0000 Subject: [PATCH 1966/5615] Bump jedis from 3.6.0 to 3.6.1 in /modules/junit-jupiter Bumps [jedis](https://github.com/redis/jedis) from 3.6.0 to 3.6.1. - [Release notes](https://github.com/redis/jedis/releases) - [Commits](https://github.com/redis/jedis/compare/jedis-3.6.0...jedis-3.6.1) --- updated-dependencies: - dependency-name: redis.clients:jedis dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index ab2477ed883..f33bff6398b 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -7,7 +7,7 @@ dependencies { testImplementation project(':mysql') testImplementation project(':postgresql') testImplementation 'com.zaxxer:HikariCP:4.0.3' - testImplementation 'redis.clients:jedis:3.6.0' + testImplementation 'redis.clients:jedis:3.6.1' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' testImplementation ('org.mockito:mockito-core:3.10.0') { exclude(module: 'hamcrest-core') From a6210dc258fa10a957941ed4a1dd6fce9614f5d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 22:00:30 +0000 Subject: [PATCH 1967/5615] Bump postgresql from 42.2.20 to 42.2.22 in /modules/postgresql Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.20 to 42.2.22. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/REL42.2.22/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.20...REL42.2.22) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index c44dbe7d009..9b7063e3a0c 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -11,7 +11,7 @@ dependencies { testImplementation project(':jdbc-test') testImplementation project(':test-support') - testImplementation 'org.postgresql:postgresql:42.2.20' + testImplementation 'org.postgresql:postgresql:42.2.22' testImplementation testFixtures(project(':r2dbc')) testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.5.RELEASE' From 50d4922384cc09139e5011f68729631972c981ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 22:00:30 +0000 Subject: [PATCH 1968/5615] Bump jedis from 3.6.0 to 3.6.1 in /core Bumps [jedis](https://github.com/redis/jedis) from 3.6.0 to 3.6.1. - [Release notes](https://github.com/redis/jedis/releases) - [Commits](https://github.com/redis/jedis/compare/jedis-3.6.0...jedis-3.6.1) --- updated-dependencies: - dependency-name: redis.clients:jedis dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index b27031af1bb..7cbabcd4783 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -136,7 +136,7 @@ dependencies { } testImplementation 'org.apache.httpcomponents:httpclient:4.5.9' - testImplementation 'redis.clients:jedis:3.6.0' + testImplementation 'redis.clients:jedis:3.6.1' testImplementation 'com.rabbitmq:amqp-client:5.12.0' testImplementation 'org.mongodb:mongo-java-driver:3.12.7' From cb12788039131c1a6c2d5a8f7b450c3d3cb7fcad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 22:00:30 +0000 Subject: [PATCH 1969/5615] Bump postgresql from 42.2.20 to 42.2.22 in /modules/spock Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.20 to 42.2.22. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/REL42.2.22/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.20...REL42.2.22) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index eb82bdcfc41..6ee936b7ca7 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'com.zaxxer:HikariCP:4.0.3' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' - testRuntimeOnly 'org.postgresql:postgresql:42.2.20' + testRuntimeOnly 'org.postgresql:postgresql:42.2.22' testRuntimeOnly 'mysql:mysql-connector-java:8.0.25' testCompileClasspath 'org.jetbrains:annotations:21.0.1' From 496728436f742052acbbfc2b191715bc1917c828 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 22:00:30 +0000 Subject: [PATCH 1970/5615] Bump aws-java-sdk-s3 from 1.11.1030 to 1.12.16 in /modules/localstack Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.11.1030 to 1.12.16. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.1030...1.12.16) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 87cc407468d..6d7b683200c 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.1030' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.16' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.11.1030' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.1' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.11.807' From 9974c8f02d36d8916349c5a53b76f5fcfb3de8ed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 22:00:30 +0000 Subject: [PATCH 1971/5615] Bump assertj-core from 3.19.0 to 3.20.2 in /modules/jdbc Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.19.0 to 3.20.2. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.19.0...assertj-core-3.20.2) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 16b95af77bf..2f776044169 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -11,5 +11,5 @@ dependencies { testImplementation 'com.zaxxer:HikariCP-java6:2.3.13' testImplementation 'com.googlecode.junit-toolbox:junit-toolbox:2.4' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' - testImplementation 'org.assertj:assertj-core:3.19.0' + testImplementation 'org.assertj:assertj-core:3.20.2' } From ce5c685d18229fafbd5a0356d676d82f841ba959 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 22:00:30 +0000 Subject: [PATCH 1972/5615] Bump assertj-core from 3.19.0 to 3.20.2 in /modules/kafka Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.19.0 to 3.20.2. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.19.0...assertj-core-3.20.2) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index bb112755352..c83859e0c20 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':testcontainers') testImplementation 'org.apache.kafka:kafka-clients:2.8.0' - testImplementation 'org.assertj:assertj-core:3.19.0' + testImplementation 'org.assertj:assertj-core:3.20.2' testImplementation 'com.google.guava:guava:23.0' } From 10768c88d4db87e0adf88f326fba4e909295e008 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 22:00:31 +0000 Subject: [PATCH 1973/5615] Bump assertj-core from 3.19.0 to 3.20.2 in /modules/junit-jupiter Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.19.0 to 3.20.2. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.19.0...assertj-core-3.20.2) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index ab2477ed883..b333b71c1b6 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -12,7 +12,7 @@ dependencies { testImplementation ('org.mockito:mockito-core:3.10.0') { exclude(module: 'hamcrest-core') } - testImplementation 'org.assertj:assertj-core:3.19.0' + testImplementation 'org.assertj:assertj-core:3.20.2' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.7.2' testRuntimeOnly 'org.postgresql:postgresql:42.2.20' From 5189aebedfc13b643c9b4daf65672ccef2bfc90f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 22:00:31 +0000 Subject: [PATCH 1974/5615] Bump aws-java-sdk-dynamodb in /modules/dynalite Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.11.1030 to 1.12.16. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.1030...1.12.16) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-dynamodb dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/dynalite/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index e3a49df5be2..c92b1969147 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Dynalite" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.11.1030' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.16' testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.11.1030' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From b1ada036d900e60b8e6c7e978d53c66d0e0d7431 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 22:00:32 +0000 Subject: [PATCH 1975/5615] Bump org.springframework.boot from 2.5.0 to 2.5.2 in /examples Bumps [org.springframework.boot](https://github.com/spring-projects/spring-boot) from 2.5.0 to 2.5.2. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.5.0...v2.5.2) --- updated-dependencies: - dependency-name: org.springframework.boot dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot-kotlin-redis/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index bcd2f97100b..70c4a2683b0 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.5.0' + id 'org.springframework.boot' version '2.5.2' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 3f3469f8548..d39de1a995d 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,5 +1,5 @@ plugins { - id("org.springframework.boot") version "2.5.0" + id("org.springframework.boot") version "2.5.2" id("org.jetbrains.kotlin.jvm") version "1.5.10" id("org.jetbrains.kotlin.plugin.spring") version "1.5.10" } diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index 33f5b9aa878..00f6f22728d 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.5.0' + id 'org.springframework.boot' version '2.5.2' } apply plugin: 'io.spring.dependency-management' From 5514570afcf8e6537174f2e8b725851c6c5ee5e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 22:00:33 +0000 Subject: [PATCH 1976/5615] Bump assertj-core from 3.19.0 to 3.20.2 in /modules/neo4j Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.19.0 to 3.20.2. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.19.0...assertj-core-3.20.2) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index f556c96095a..08eda371597 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -34,5 +34,5 @@ dependencies { api project(":testcontainers") testImplementation "org.neo4j.driver:neo4j-java-driver:1.7.5" - testImplementation 'org.assertj:assertj-core:3.19.0' + testImplementation 'org.assertj:assertj-core:3.20.2' } From 7ebefec50e0adaaaee3ab084dbca689479d3ecde Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 22:00:34 +0000 Subject: [PATCH 1977/5615] Bump assertj-core from 3.19.0 to 3.20.2 in /modules/pulsar Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.19.0 to 3.20.2. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.19.0...assertj-core-3.20.2) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 61e1208381d..478c47fff29 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':testcontainers') testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.7.2' - testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.19.0' + testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.20.2' testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.7.2' } From 3b8149d89f575378fa50959f63f35c1e2db5a464 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 22:00:34 +0000 Subject: [PATCH 1978/5615] Bump postgresql from 42.2.20 to 42.2.22 in /modules/junit-jupiter Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.20 to 42.2.22. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/REL42.2.22/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.20...REL42.2.22) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index ab2477ed883..8c97073a540 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'org.assertj:assertj-core:3.19.0' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.7.2' - testRuntimeOnly 'org.postgresql:postgresql:42.2.20' + testRuntimeOnly 'org.postgresql:postgresql:42.2.22' testRuntimeOnly 'mysql:mysql-connector-java:8.0.25' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.2' } From dd68c10acf0200c4640bb7c55b810902d695f98d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 22:00:35 +0000 Subject: [PATCH 1979/5615] Bump assertj-core from 3.19.0 to 3.20.2 in /examples Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.19.0 to 3.20.2. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.19.0...assertj-core-3.20.2) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/kafka-cluster/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index cb1e9e31e7b..79c14941c11 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -11,7 +11,7 @@ dependencies { testAnnotationProcessor "org.projectlombok:lombok:1.18.10" testImplementation 'org.testcontainers:kafka' testImplementation 'org.apache.kafka:kafka-clients:2.8.0' - testImplementation 'org.assertj:assertj-core:3.19.0' + testImplementation 'org.assertj:assertj-core:3.20.2' testImplementation 'com.google.guava:guava:23.0' testImplementation 'org.slf4j:slf4j-simple:1.7.30' } From 9eed46bf9e3290682b7a0528a043958925abe7e1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 22:00:35 +0000 Subject: [PATCH 1980/5615] Bump mockito-core from 3.10.0 to 3.11.2 in /core Bumps [mockito-core](https://github.com/mockito/mockito) from 3.10.0 to 3.11.2. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.10.0...v3.11.2) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index b27031af1bb..762e30054e7 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -140,7 +140,7 @@ dependencies { testImplementation 'com.rabbitmq:amqp-client:5.12.0' testImplementation 'org.mongodb:mongo-java-driver:3.12.7' - testImplementation ('org.mockito:mockito-core:3.10.0') { + testImplementation ('org.mockito:mockito-core:3.11.2') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From 17bdb7ce09e79b896501a1fe0513de49ddb9354b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 22:00:35 +0000 Subject: [PATCH 1981/5615] Bump transport from 7.13.1 to 7.13.2 in /modules/elasticsearch Bumps [transport](https://github.com/elastic/elasticsearch) from 7.13.1 to 7.13.2. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.13.1...v7.13.2) --- updated-dependencies: - dependency-name: org.elasticsearch.client:transport dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index e1921e755f8..67e03df2945 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,6 +3,6 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') testImplementation "org.elasticsearch.client:elasticsearch-rest-client:7.13.0" - testImplementation "org.elasticsearch.client:transport:7.13.1" + testImplementation "org.elasticsearch.client:transport:7.13.2" testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From a027522e063de7d43a06706e771f63df14327213 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 22:00:38 +0000 Subject: [PATCH 1982/5615] Bump mockito-core from 3.10.0 to 3.11.2 in /modules/junit-jupiter Bumps [mockito-core](https://github.com/mockito/mockito) from 3.10.0 to 3.11.2. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.10.0...v3.11.2) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index ab2477ed883..3203f13ca11 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,7 +9,7 @@ dependencies { testImplementation 'com.zaxxer:HikariCP:4.0.3' testImplementation 'redis.clients:jedis:3.6.0' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' - testImplementation ('org.mockito:mockito-core:3.10.0') { + testImplementation ('org.mockito:mockito-core:3.11.2') { exclude(module: 'hamcrest-core') } testImplementation 'org.assertj:assertj-core:3.19.0' From be077663330869adcad683faac658ae04a6078fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 22:00:40 +0000 Subject: [PATCH 1983/5615] Bump org.jetbrains.kotlin.plugin.spring in /examples Bumps org.jetbrains.kotlin.plugin.spring from 1.5.10 to 1.5.20. --- updated-dependencies: - dependency-name: org.jetbrains.kotlin.plugin.spring dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 3f3469f8548..bd1e474b411 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,7 +1,7 @@ plugins { id("org.springframework.boot") version "2.5.0" id("org.jetbrains.kotlin.jvm") version "1.5.10" - id("org.jetbrains.kotlin.plugin.spring") version "1.5.10" + id("org.jetbrains.kotlin.plugin.spring") version "1.5.20" } apply plugin: 'io.spring.dependency-management' From 58960f3a5c1b431424664fbde7a61c48d54c2f23 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 22:00:40 +0000 Subject: [PATCH 1984/5615] Bump solr-solrj from 8.8.2 to 8.9.0 in /examples Bumps solr-solrj from 8.8.2 to 8.9.0. --- updated-dependencies: - dependency-name: org.apache.solr:solr-solrj dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/solr-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/solr-container/build.gradle b/examples/solr-container/build.gradle index e0a6f356a77..d416c4d8c45 100644 --- a/examples/solr-container/build.gradle +++ b/examples/solr-container/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly "org.projectlombok:lombok:1.18.20" annotationProcessor "org.projectlombok:lombok:1.18.16" - implementation 'org.apache.solr:solr-solrj:8.8.2' + implementation 'org.apache.solr:solr-solrj:8.9.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.testcontainers:solr' From 47afef9c6bf274d8318a19317af08f47d0e98ae6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 22:00:41 +0000 Subject: [PATCH 1985/5615] Bump aws-java-sdk-logs from 1.11.807 to 1.12.16 in /modules/localstack Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.11.807 to 1.12.16. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.11.807...1.12.16) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-logs dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 87cc407468d..08e8202e09b 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,7 +6,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.11.1030' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.11.1030' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.1' - testImplementation 'com.amazonaws:aws-java-sdk-logs:1.11.807' + testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.16' testImplementation 'software.amazon.awssdk:s3:2.16.80' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From c797df1bd6b2cc6323ac4a8589399645b5157e54 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 22:00:47 +0000 Subject: [PATCH 1986/5615] Bump postgresql from 42.2.20 to 42.2.22 in /examples Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.20 to 42.2.22. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/REL42.2.22/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.20...REL42.2.22) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index edb545e79d3..c5706da277a 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' implementation 'com.squareup.okhttp3:okhttp:4.9.1' implementation 'org.json:json:20210307' - testImplementation 'org.postgresql:postgresql:42.2.20' + testImplementation 'org.postgresql:postgresql:42.2.22' testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.testcontainers:postgresql' } From 37bdb1cb6c42a12cd8bdb50749216b7c6e5787fe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 22:00:50 +0000 Subject: [PATCH 1987/5615] Bump gson from 2.8.6 to 2.8.7 in /examples Bumps [gson](https://github.com/google/gson) from 2.8.6 to 2.8.7. - [Release notes](https://github.com/google/gson/releases) - [Changelog](https://github.com/google/gson/blob/master/CHANGELOG.md) - [Commits](https://github.com/google/gson/compare/gson-parent-2.8.6...gson-parent-2.8.7) --- updated-dependencies: - dependency-name: com.google.code.gson:gson dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/disque-job-queue/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index 82d1bb548ee..04d8f249511 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly "org.projectlombok:lombok:1.18.20" annotationProcessor "org.projectlombok:lombok:1.18.20" implementation 'biz.paluch.redis:spinach:0.3' - implementation 'com.google.code.gson:gson:2.8.6' + implementation 'com.google.code.gson:gson:2.8.7' implementation 'com.google.guava:guava:23.0' testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.mockito:mockito-all:1.10.19' diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index c19c5b22f23..aad845a5d78 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -9,7 +9,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' implementation 'redis.clients:jedis:3.6.0' - implementation 'com.google.code.gson:gson:2.8.6' + implementation 'com.google.code.gson:gson:2.8.7' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'ch.qos.logback:logback-classic:1.2.3' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index a968a6d2bf3..f1c900708ae 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -9,7 +9,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' implementation 'redis.clients:jedis:3.6.0' - implementation 'com.google.code.gson:gson:2.8.6' + implementation 'com.google.code.gson:gson:2.8.7' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'junit:junit:4.13.2' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index dfbe856ea62..490f85abfeb 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -9,7 +9,7 @@ repositories { dependencies { implementation 'redis.clients:jedis:3.6.0' - implementation 'com.google.code.gson:gson:2.8.6' + implementation 'com.google.code.gson:gson:2.8.7' implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.30' From 149882905ddc7a1672ce8cfe8d5292eee1ed68bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 22:01:08 +0000 Subject: [PATCH 1988/5615] Bump actions/github-script from 3 to 4.0.2 Bumps [actions/github-script](https://github.com/actions/github-script) from 3 to 4.0.2. - [Release notes](https://github.com/actions/github-script/releases) - [Commits](https://github.com/actions/github-script/compare/v3...v4.0.2) --- updated-dependencies: - dependency-name: actions/github-script dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/combine-prs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/combine-prs.yml b/.github/workflows/combine-prs.yml index 749cc648fb5..e1d96a20895 100644 --- a/.github/workflows/combine-prs.yml +++ b/.github/workflows/combine-prs.yml @@ -30,7 +30,7 @@ jobs: # Steps represent a sequence of tasks that will be executed as part of the job steps: - - uses: actions/github-script@v3 + - uses: actions/github-script@v4.0.2 id: fetch-branch-names name: Fetch branch names with: @@ -121,7 +121,7 @@ jobs: git pull origin $sourcebranches --no-edit git push origin $COMBINE_BRANCH_NAME # Creates a PR with the new combined branch - - uses: actions/github-script@v3 + - uses: actions/github-script@v4.0.2 name: Create Combined Pull Request with: github-token: ${{secrets.GITHUB_TOKEN}} From 9cd1a5abe8f3a3a9056ed66561d71a6e0e3e1a27 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 22:01:13 +0000 Subject: [PATCH 1989/5615] Bump gradle-update/update-gradle-wrapper-action from 1.0.13 to 1.0.14 Bumps [gradle-update/update-gradle-wrapper-action](https://github.com/gradle-update/update-gradle-wrapper-action) from 1.0.13 to 1.0.14. - [Release notes](https://github.com/gradle-update/update-gradle-wrapper-action/releases) - [Changelog](https://github.com/gradle-update/update-gradle-wrapper-action/blob/master/CHANGELOG.md) - [Commits](https://github.com/gradle-update/update-gradle-wrapper-action/compare/af940e8bff73d435137caae9d08cf7a1badf5497...6356f411fa7c14aab1fb2ac2fd20762a629e2fbf) --- updated-dependencies: - dependency-name: gradle-update/update-gradle-wrapper-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/update-gradle-wrapper.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-gradle-wrapper.yml b/.github/workflows/update-gradle-wrapper.yml index 329230640e7..272594c26df 100644 --- a/.github/workflows/update-gradle-wrapper.yml +++ b/.github/workflows/update-gradle-wrapper.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v2 - name: Update Gradle Wrapper - uses: gradle-update/update-gradle-wrapper-action@af940e8bff73d435137caae9d08cf7a1badf5497 # v1.0.13 + uses: gradle-update/update-gradle-wrapper-action@6356f411fa7c14aab1fb2ac2fd20762a629e2fbf # v1.0.13 with: repo-token: ${{ secrets.GITHUB_TOKEN }} labels: dependencies From 5d87f611fb628a201f30690b9141188754da0699 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 8 Jul 2021 11:35:31 +0100 Subject: [PATCH 1990/5615] Switch from using Travis to GitHub Actions for release (#3434) --- .github/workflows/release.yml | 31 +++++++++++++++++++++++++++++++ .travis.yml | 24 ------------------------ README.md | 2 -- build.gradle | 16 ---------------- gradle/bintray.gradle | 24 ------------------------ gradle/publishing.gradle | 21 +++++++++++++++++---- 6 files changed, 48 insertions(+), 70 deletions(-) create mode 100644 .github/workflows/release.yml delete mode 100644 .travis.yml delete mode 100644 gradle/bintray.gradle diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000000..943dfcf005a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,31 @@ +name: Release + +on: + release: + types: [published] + +env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + +jobs: + release: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-java@v1 + with: + java-version: '1.8' + - name: Clear existing docker image cache + run: docker image prune -af + + - name: Run Gradle Build + run: ./gradlew build --scan --no-daemon -i + + - name: Run Gradle Publish + run: | + ./gradlew publish \ + -Pversion="${{github.event.release.tag_name}}" --scan --no-daemon -i + env: + OSSRH_USERNAME: ${{secrets.OSSRH_USERNAME}} + OSSRH_PASSWORD: ${{secrets.OSSRH_PASSWORD}} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ec7a5d343d9..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,24 +0,0 @@ -if: tag is present - -language: java -jdk: - - openjdk8 - -sudo: required -services: - - docker - -jobs: - include: - - - stage: deploy - sudo: false - services: [] - install: skip - script: skip - deploy: - provider: script - script: ./gradlew -Pversion=$TRAVIS_TAG release --scan --no-daemon -i - on: - tags: true - branch: master diff --git a/README.md b/README.md index c87138e7d1a..d3446cbb31e 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,6 @@ > Testcontainers is a Java library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container. -[![Build Status](https://travis-ci.org/testcontainers/testcontainers-java.svg?branch=master)](https://travis-ci.org/testcontainers/testcontainers-java)[ ![Download](https://api.bintray.com/packages/testcontainers/releases/testcontainers/images/download.svg) ](https://bintray.com/testcontainers/releases/testcontainers/_latestVersion) - ![Testcontainers logo](docs/logo.png) # [Read the documentation here](http://www.testcontainers.org) diff --git a/build.gradle b/build.gradle index 40f5bfed399..98b9b5a30bc 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,6 @@ plugins { id 'io.franzbecker.gradle-lombok' version '4.0.0' id 'com.github.johnrengelman.shadow' version '7.0.0' - id "com.jfrog.bintray" version "1.8.4" apply false } apply from: "$rootDir/gradle/ci-support.gradle" @@ -20,7 +19,6 @@ subprojects { compileTestJava.options.encoding = 'UTF-8' repositories { - jcenter() mavenCentral() } @@ -48,20 +46,6 @@ subprojects { // specific modules should be excluded from publication if ( ! ["test-support", "jdbc-test"].contains(it.name) && !it.path.startsWith(":docs:") && it != project(":docs") ) { apply from: "$rootDir/gradle/publishing.gradle" - apply from: "$rootDir/gradle/bintray.gradle" - - jar.archiveClassifier.set("original") - - publishing { - publications { - mavenJava(MavenPublication) { publication -> - artifacts.removeAll { it.classifier == jar.archiveClassifier.get() } - artifact project.tasks.shadowJar - } - } - } - - task release(dependsOn: bintrayUpload) } test { diff --git a/gradle/bintray.gradle b/gradle/bintray.gradle deleted file mode 100644 index 5ddfefd8e49..00000000000 --- a/gradle/bintray.gradle +++ /dev/null @@ -1,24 +0,0 @@ -apply plugin: "com.jfrog.bintray" - -bintray { - user = project.hasProperty('bintrayUser') ? project.property('bintrayUser') : System.getenv('BINTRAY_USER') - key = project.hasProperty('bintrayApiKey') ? project.property('bintrayApiKey') : System.getenv('BINTRAY_API_KEY') - - publications = ['mavenJava'] - - pkg { - userOrg = 'testcontainers' - repo = 'releases' - name = 'testcontainers' - - publish = true - override = true - - version { - name = project.version - vcsTag = project.version - released = new Date() - gpg.sign = true - } - } -} diff --git a/gradle/publishing.gradle b/gradle/publishing.gradle index 84fad9994e5..e6bf5d2acbd 100644 --- a/gradle/publishing.gradle +++ b/gradle/publishing.gradle @@ -1,24 +1,28 @@ apply plugin: 'maven-publish' task sourceJar(type: Jar) { - classifier = 'sources' + archiveClassifier.set( 'sources') from sourceSets.main.allJava } task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' + archiveClassifier.set('javadoc') from javadoc } +jar.archiveClassifier.set("original") + publishing { publications { mavenJava(MavenPublication) { publication -> artifactId = project.name - - artifact project.tasks.jar artifact sourceJar artifact javadocJar + artifact project.tasks.jar + artifacts.removeAll { it.classifier == jar.archiveClassifier.get() } + artifact project.tasks.shadowJar + pom.withXml { def rootNode = asNode() rootNode.children().last() + { @@ -77,4 +81,13 @@ publishing { } } } + repositories { + maven { + url("https://oss.sonatype.org/service/local/staging/deploy/maven2") + credentials { + username = System.getenv("OSSRH_USERNAME") + password = System.getenv("OSSRH_PASSWORD") + } + } + } } From c3429ac64c8cbf86180739e2ef4c09831a024c5d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 8 Jul 2021 11:42:19 +0100 Subject: [PATCH 1991/5615] Update Gradle Wrapper to 7.1.1 (#4266) Co-authored-by: gradle-update-robot --- examples/gradle/wrapper/gradle-wrapper.properties | 4 ++-- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/gradle/wrapper/gradle-wrapper.properties b/examples/gradle/wrapper/gradle-wrapper.properties index 234b6472e12..2957f437446 100644 --- a/examples/gradle/wrapper/gradle-wrapper.properties +++ b/examples/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=2debee19271e1b82c6e41137d78e44e6e841035230a1a169ca47fd3fb09ed87b -distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip +distributionSha256Sum=bf8b869948901d422e9bb7d1fa61da6a6e19411baa7ad6ee929073df85d6365d +distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 283500479a3..17ba19021b8 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=a9e356a21595348b6f04b024ed0b08ac8aea6b2ac37e6c0ef58e51549cd7b9cb -distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-all.zip +distributionSha256Sum=9bb8bc05f562f2d42bdf1ba8db62f6b6fa1c3bf6c392228802cc7cb0578fe7e0 +distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From a3f659f7a3bbcac1782dac8a8a0a9ec9bc66f416 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Jul 2021 12:03:29 +0000 Subject: [PATCH 1992/5615] Bump s3 from 2.16.80 to 2.16.97 in /modules/localstack Bumps s3 from 2.16.80 to 2.16.97. --- updated-dependencies: - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 3770c37f4e3..ca14658a287 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.amazonaws:aws-java-sdk-s3:1.11.1030' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.1' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.16' - testImplementation 'software.amazon.awssdk:s3:2.16.80' + testImplementation 'software.amazon.awssdk:s3:2.16.97' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 4a83864aab94dd8f9fe94849ffd2b5f30d1f582f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Jul 2021 12:03:31 +0000 Subject: [PATCH 1993/5615] Bump tomcat-jdbc from 10.0.6 to 10.0.8 in /modules/jdbc Bumps tomcat-jdbc from 10.0.6 to 10.0.8. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 2f776044169..4f6c6608b4d 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -7,7 +7,7 @@ dependencies { compileOnly 'org.jetbrains:annotations:21.0.1' testImplementation 'commons-dbutils:commons-dbutils:1.7' testImplementation 'org.vibur:vibur-dbcp:25.0' - testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.6' + testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.8' testImplementation 'com.zaxxer:HikariCP-java6:2.3.13' testImplementation 'com.googlecode.junit-toolbox:junit-toolbox:2.4' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From 590676316d27945ccf632b6fc0caadb5e7e1cd73 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Jul 2021 12:03:31 +0000 Subject: [PATCH 1994/5615] Bump assertj-core from 3.19.0 to 3.20.2 in /modules/database-commons Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.19.0 to 3.20.2. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.19.0...assertj-core-3.20.2) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/database-commons/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/database-commons/build.gradle b/modules/database-commons/build.gradle index 7bba8548c95..35578b55d68 100644 --- a/modules/database-commons/build.gradle +++ b/modules/database-commons/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: Database-Commons" dependencies { api project(':testcontainers') - testImplementation 'org.assertj:assertj-core:3.19.0' + testImplementation 'org.assertj:assertj-core:3.20.2' } From 30cbc6937e2b04f4607df26d65144c57a2a1ef64 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Jul 2021 12:03:41 +0000 Subject: [PATCH 1995/5615] Bump aws-java-sdk-sqs from 1.12.1 to 1.12.19 in /modules/localstack Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.12.1 to 1.12.19. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.1...1.12.19) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-sqs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 3770c37f4e3..802905df563 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.16' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.11.1030' - testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.1' + testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.19' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.16' testImplementation 'software.amazon.awssdk:s3:2.16.80' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From 6df76958c9bfad06f90169e699f199399ba01446 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Jul 2021 12:03:45 +0000 Subject: [PATCH 1996/5615] Bump assertj-core from 3.19.0 to 3.20.2 in /core Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.19.0 to 3.20.2. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.19.0...assertj-core-3.20.2) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index 2634bc4cae6..384c1caf1f2 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -147,13 +147,13 @@ dependencies { testImplementation files('testlib/repo/fakejar/fakejar/0/fakejar-0.jar') testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' - testImplementation 'org.assertj:assertj-core:3.19.0' + testImplementation 'org.assertj:assertj-core:3.20.2' testImplementation project(':test-support') jarFileTestCompileOnly "org.projectlombok:lombok:${lombok.version}" jarFileTestAnnotationProcessor "org.projectlombok:lombok:${lombok.version}" jarFileTestImplementation 'junit:junit:4.12' - jarFileTestImplementation 'org.assertj:assertj-core:3.18.1' + jarFileTestImplementation 'org.assertj:assertj-core:3.20.2' jarFileTestImplementation 'org.ow2.asm:asm-debug-all:5.2' } From 6d1c0d6847c3fc11cba09dc0cce2e186407b39cf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Jul 2021 12:03:49 +0000 Subject: [PATCH 1997/5615] Bump jedis from 3.6.0 to 3.6.1 in /examples Bumps [jedis](https://github.com/redis/jedis) from 3.6.0 to 3.6.1. - [Release notes](https://github.com/redis/jedis/releases) - [Commits](https://github.com/redis/jedis/compare/jedis-3.6.0...jedis-3.6.1) --- updated-dependencies: - dependency-name: redis.clients:jedis dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index aad845a5d78..58f31827717 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' - implementation 'redis.clients:jedis:3.6.0' + implementation 'redis.clients:jedis:3.6.1' implementation 'com.google.code.gson:gson:2.8.7' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index f1c900708ae..4fb93cb71b5 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' - implementation 'redis.clients:jedis:3.6.0' + implementation 'redis.clients:jedis:3.6.1' implementation 'com.google.code.gson:gson:2.8.7' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 490f85abfeb..e803f616998 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { - implementation 'redis.clients:jedis:3.6.0' + implementation 'redis.clients:jedis:3.6.1' implementation 'com.google.code.gson:gson:2.8.7' implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.30' From f4c1add362eda0ef684baf975aa17052ba8455f6 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 16 Jul 2021 12:00:59 +0200 Subject: [PATCH 1998/5615] Use Apache HttpClient5 transport as the default (#4287) --- .../org/testcontainers/utility/TestcontainersConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 9b2ab707a2e..2f3f4370e50 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -176,7 +176,7 @@ public String getDockerClientStrategyClassName() { } public String getTransportType() { - return getEnvVarOrProperty("transport.type", "okhttp"); + return getEnvVarOrProperty("transport.type", "httpclient5"); } public Integer getImagePullPauseTimeout() { From 014500b18964e9780355a8c5c7152632357124ef Mon Sep 17 00:00:00 2001 From: Tobias Happ Date: Fri, 16 Jul 2021 13:08:27 +0200 Subject: [PATCH 1999/5615] Add support for community edition of couchbase server (#4221) --- .../couchbase/CouchbaseContainer.java | 18 +++++++++- .../couchbase/CouchbaseContainerTest.java | 34 ++++++++++++++++--- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index 64489f30531..844917c053f 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -88,6 +88,8 @@ public class CouchbaseContainer extends GenericContainer { private final List buckets = new ArrayList<>(); + private boolean isEnterprise = false; + /** * Creates a new couchbase container with the default image and version. * @deprecated use {@link CouchbaseContainer(DockerImageName)} instead @@ -220,6 +222,7 @@ protected void containerIsStarting(final InspectContainerResponse containerInfo) logger().debug("Couchbase container is starting, performing configuration."); waitUntilNodeIsOnline(); + initializeIsEnterprise(); renameNode(); initializeServices(); configureAdminUser(); @@ -246,6 +249,19 @@ private void waitUntilNodeIsOnline() { .waitUntilReady(this); } + /** + * Fetches edition (enterprise or community) of started container. + */ + private void initializeIsEnterprise() { + @Cleanup Response response = doHttpRequest(MGMT_PORT, "/pools", "GET", null, true); + + try { + isEnterprise = MAPPER.readTree(response.body().string()).get("isEnterprise").asBoolean(); + } catch (IOException e) { + throw new IllegalStateException("Couchbase /pools did not return valid JSON"); + } + } + /** * Rebinds/renames the internal hostname. *

@@ -349,7 +365,7 @@ private void configureIndexer() { logger().debug("Configuring the indexer service"); @Cleanup Response response = doHttpRequest(MGMT_PORT, "/settings/indexes", "POST", new FormBody.Builder() - .add("storageMode", "memory_optimized") + .add("storageMode", isEnterprise ? "memory_optimized" : "forestdb") .build(), true ); diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java index 57b726eb663..fcebbe2bb62 100644 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java @@ -32,17 +32,20 @@ public class CouchbaseContainerTest { - private static final DockerImageName COUCHBASE_IMAGE = DockerImageName.parse("couchbase/server:6.5.1"); + private static final DockerImageName COUCHBASE_IMAGE_ENTERPRISE = + DockerImageName.parse("couchbase/server:enterprise-6.6.2"); + private static final DockerImageName COUCHBASE_IMAGE_COMMUNITY = + DockerImageName.parse("couchbase/server:community-6.6.0"); @Test - public void testBasicContainerUsage() { + public void testBasicContainerUsageForEnterpriseContainer() { // bucket_definition { BucketDefinition bucketDefinition = new BucketDefinition("mybucket"); // } try ( // container_definition { - CouchbaseContainer container = new CouchbaseContainer(COUCHBASE_IMAGE) + CouchbaseContainer container = new CouchbaseContainer(COUCHBASE_IMAGE_ENTERPRISE) .withBucket(bucketDefinition) // } ) { @@ -61,13 +64,36 @@ public void testBasicContainerUsage() { } } + @Test + public void testBasicContainerUsageForCommunityContainer() { + BucketDefinition bucketDefinition = new BucketDefinition("mybucket"); + + try ( + CouchbaseContainer container = new CouchbaseContainer(COUCHBASE_IMAGE_COMMUNITY) + .withBucket(bucketDefinition) + ) { + setUpClient(container, cluster -> { + Bucket bucket = cluster.bucket(bucketDefinition.getName()); + bucket.waitUntilReady(Duration.ofSeconds(10L)); + + Collection collection = bucket.defaultCollection(); + + collection.upsert("foo", JsonObject.create().put("key", "value")); + + JsonObject fooObject = collection.get("foo").contentAsObject(); + + assertEquals("value", fooObject.getString("key")); + }); + } + } + @Test public void testBucketIsFlushableIfEnabled() { BucketDefinition bucketDefinition = new BucketDefinition("mybucket") .withFlushEnabled(true); try ( - CouchbaseContainer container = new CouchbaseContainer(COUCHBASE_IMAGE) + CouchbaseContainer container = new CouchbaseContainer(COUCHBASE_IMAGE_ENTERPRISE) .withBucket(bucketDefinition) ) { setUpClient(container, cluster -> { From e89dce16e6471513772aa2a9dbb67430740b13b7 Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Fri, 16 Jul 2021 08:55:50 -0400 Subject: [PATCH 2000/5615] Allow for docker timestamps with timezone offsets (#4073) Docker prefers to use UTC time when making images/containers, but it's happy to parse non-UTC times too. It will then pass those on in its output. DateTimeFormatter.ISO_OFFSET_DATE_TIME accepts a superset of what DateTimeFormatter.ISO_INSTANT accepts, so replace use of ISO_INSTANT with ISO_OFFSET_DATE_TIME. Co-authored-by: Richard North --- .../testcontainers/utility/DockerStatus.java | 4 +- .../utility/DockerStatusTest.java | 53 ++++++++++++------- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/DockerStatus.java b/core/src/main/java/org/testcontainers/utility/DockerStatus.java index 63b19db410b..a410597dcfb 100644 --- a/core/src/main/java/org/testcontainers/utility/DockerStatus.java +++ b/core/src/main/java/org/testcontainers/utility/DockerStatus.java @@ -40,7 +40,7 @@ public static boolean isContainerRunning(InspectContainerResponse.ContainerState if (minimumRunningDuration == null) { return true; } - Instant startedAt = DateTimeFormatter.ISO_INSTANT.parse( + Instant startedAt = DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse( state.getStartedAt(), Instant::from); if (startedAt.isBefore(now.minus(minimumRunningDuration))) { @@ -75,7 +75,7 @@ public static boolean isDockerTimestampNonEmpty(String dockerTimestamp) { return dockerTimestamp != null && !dockerTimestamp.isEmpty() && !dockerTimestamp.equals(DOCKER_TIMESTAMP_ZERO) - && DateTimeFormatter.ISO_INSTANT.parse(dockerTimestamp, Instant::from).getEpochSecond() >= 0L; + && DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(dockerTimestamp, Instant::from).getEpochSecond() >= 0L; } public static boolean isContainerExitCodeSuccess(InspectContainerResponse.ContainerState state) { diff --git a/core/src/test/java/org/testcontainers/utility/DockerStatusTest.java b/core/src/test/java/org/testcontainers/utility/DockerStatusTest.java index 4b63719f928..1ef8e136a32 100644 --- a/core/src/test/java/org/testcontainers/utility/DockerStatusTest.java +++ b/core/src/test/java/org/testcontainers/utility/DockerStatusTest.java @@ -2,10 +2,13 @@ import com.github.dockerjava.api.command.InspectContainerResponse; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; import org.mockito.Mockito; import java.time.Duration; import java.time.Instant; +import java.time.ZoneId; import java.time.format.DateTimeFormatter; import static org.junit.Assert.assertFalse; @@ -15,33 +18,47 @@ /** * */ + @RunWith(Parameterized.class) public class DockerStatusTest { + private final DateTimeFormatter dateTimeFormatter; - private static Instant now = Instant.now(); + private static final Instant now = Instant.now(); - private static InspectContainerResponse.ContainerState running = - buildState(true, false, buildTimestamp(now.minusMillis(30)), DockerStatus.DOCKER_TIMESTAMP_ZERO); + private final InspectContainerResponse.ContainerState running; - private static InspectContainerResponse.ContainerState runningVariant = - buildState(true, false, buildTimestamp(now.minusMillis(30)), ""); + private final InspectContainerResponse.ContainerState runningVariant; - private static InspectContainerResponse.ContainerState shortRunning = - buildState(true, false, buildTimestamp(now.minusMillis(10)), DockerStatus.DOCKER_TIMESTAMP_ZERO); + private final InspectContainerResponse.ContainerState shortRunning; - private static InspectContainerResponse.ContainerState created = - buildState(false, false, DockerStatus.DOCKER_TIMESTAMP_ZERO, DockerStatus.DOCKER_TIMESTAMP_ZERO); + private final InspectContainerResponse.ContainerState created; // a container in the "created" state is not running, and has both startedAt and finishedAt empty. - private static InspectContainerResponse.ContainerState createdVariant = - buildState(false, false, null, null); + private final InspectContainerResponse.ContainerState createdVariant; - private static InspectContainerResponse.ContainerState exited = - buildState(false, false, buildTimestamp(now.minusMillis(100)), buildTimestamp(now.minusMillis(90))); + private final InspectContainerResponse.ContainerState exited; - private static InspectContainerResponse.ContainerState paused = - buildState(false, true, buildTimestamp(now.minusMillis(100)), DockerStatus.DOCKER_TIMESTAMP_ZERO); + private final InspectContainerResponse.ContainerState paused; - private static Duration minimumDuration = Duration.ofMillis(20); + private static final Duration minimumDuration = Duration.ofMillis(20); + + public DockerStatusTest(DateTimeFormatter dateTimeFormatter) { + this.dateTimeFormatter = dateTimeFormatter; + running = buildState(true, false, buildTimestamp(now.minusMillis(30)), DockerStatus.DOCKER_TIMESTAMP_ZERO); + runningVariant = buildState(true, false, buildTimestamp(now.minusMillis(30)), ""); + shortRunning = buildState(true, false, buildTimestamp(now.minusMillis(10)), DockerStatus.DOCKER_TIMESTAMP_ZERO); + created = buildState(false, false, DockerStatus.DOCKER_TIMESTAMP_ZERO, DockerStatus.DOCKER_TIMESTAMP_ZERO); + createdVariant = buildState(false, false, null, null); + exited = buildState(false, false, buildTimestamp(now.minusMillis(100)), buildTimestamp(now.minusMillis(90))); + paused = buildState(false, true, buildTimestamp(now.minusMillis(100)), DockerStatus.DOCKER_TIMESTAMP_ZERO); + } + + @Parameterized.Parameters + public static Object[][] parameters() { + return new Object[][] { + { DateTimeFormatter.ISO_INSTANT}, + { DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(ZoneId.of("America/New_York")) } + }; + } @Test public void testRunning() throws Exception { @@ -65,8 +82,8 @@ public void testStopped() throws Exception { assertFalse(DockerStatus.isContainerStopped(paused)); } - private static String buildTimestamp(Instant instant) { - return DateTimeFormatter.ISO_INSTANT.format(instant); + private String buildTimestamp(Instant instant) { + return dateTimeFormatter.format(instant); } // ContainerState is a non-static inner class, with private member variables, in a different package. From bfd61d7eda8502c2bd93542fa45a509fd6ecfea8 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 18 Jul 2021 15:53:53 +0100 Subject: [PATCH 2001/5615] Use implicit strategy when `docker.host` configuration is set (#4175) --- .../utility/TestcontainersConfiguration.java | 24 ++++++++++++++- .../TestcontainersConfigurationTest.java | 29 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 2f3f4370e50..fddeec9c9b7 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -13,6 +13,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.testcontainers.UnstableAPI; +import org.testcontainers.dockerclient.EnvironmentAndSystemPropertyClientProviderStrategy; import java.io.File; import java.io.FileNotFoundException; @@ -23,6 +24,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Properties; import java.util.concurrent.atomic.AtomicReference; @@ -172,7 +174,27 @@ public String getDockerClientStrategyClassName() { // Because of this overlap, and the desire to not change this specific TESTCONTAINERS_DOCKER_CLIENT_STRATEGY setting, // we special-case the logic here so that docker.client.strategy is used when reading properties files and // TESTCONTAINERS_DOCKER_CLIENT_STRATEGY is used when searching environment variables. - return getEnvVarOrUserProperty("docker.client.strategy", environment.get("TESTCONTAINERS_DOCKER_CLIENT_STRATEGY")); + + // looks for TESTCONTAINERS_ prefixed env var only + String prefixedEnvVarStrategy = environment.get("TESTCONTAINERS_DOCKER_CLIENT_STRATEGY"); + if (prefixedEnvVarStrategy != null) { + return prefixedEnvVarStrategy; + } + + // looks for unprefixed env var or unprefixed property + String unprefixedEnvVarOrProperty = getEnvVarOrUserProperty("docker.client.strategy", null); + if (unprefixedEnvVarOrProperty != null) { + return unprefixedEnvVarOrProperty; + } + + // If docker.host is set then EnvironmentAndSystemPropertyClientProviderStrategy is likely to work + String dockerHostProperty = getEnvVarOrUserProperty("docker.host", null); + if (dockerHostProperty != null) { + return EnvironmentAndSystemPropertyClientProviderStrategy.class.getCanonicalName(); + } + + // No value set, and no implicit value to use either + return null; } public String getTransportType() { diff --git a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java index 0fca36f0f2f..ff56f62e4e4 100644 --- a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java +++ b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java @@ -2,6 +2,7 @@ import org.junit.Before; import org.junit.Test; +import org.testcontainers.dockerclient.EnvironmentAndSystemPropertyClientProviderStrategy; import java.util.HashMap; import java.util.Map; @@ -155,6 +156,34 @@ public void shouldReadDockerClientStrategyFromEnvironment() { assertEquals("Docker client strategy is changed by env var", "foo", newConfig().getDockerClientStrategyClassName()); } + @Test + public void shouldUseImplicitDockerClientStrategyWhenDockerHostPropertyIsSet() { + userProperties.remove("docker.client.strategy"); + userProperties.put("docker.host", "tcp://1.2.3.4:5678"); + assertEquals("Docker client strategy is implicitly set when docker host property is set", EnvironmentAndSystemPropertyClientProviderStrategy.class.getCanonicalName(), newConfig().getDockerClientStrategyClassName()); + } + + @Test + public void shouldNotUseImplicitDockerClientStrategyWhenDockerHostAndStrategyAreBothSet() { + userProperties.put("docker.client.strategy", "foo"); + userProperties.put("docker.host", "tcp://1.2.3.4:5678"); + assertEquals("Docker client strategy is can be explicitly set", "foo", newConfig().getDockerClientStrategyClassName()); + + userProperties.remove("docker.client.strategy"); + + environment.put("TESTCONTAINERS_DOCKER_CLIENT_STRATEGY", "bar"); + userProperties.put("docker.client.strategy", "foo"); + assertEquals("Docker client strategy is can be explicitly set", "bar", newConfig().getDockerClientStrategyClassName()); + + environment.put("TESTCONTAINERS_DOCKER_CLIENT_STRATEGY", "bar"); + userProperties.remove("docker.client.strategy"); + assertEquals("Docker client strategy is can be explicitly set", "bar", newConfig().getDockerClientStrategyClassName()); + + environment.remove("TESTCONTAINERS_DOCKER_CLIENT_STRATEGY"); + userProperties.put("docker.client.strategy", "foo"); + assertEquals("Docker client strategy is can be explicitly set", "foo", newConfig().getDockerClientStrategyClassName()); + } + @Test public void shouldNotReadReuseFromClasspathProperties() { assertFalse("no reuse by default", newConfig().environmentSupportsReuse()); From b837bb15dbbe784155922cc4b13f0cbe8b296ddf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Lef=C3=A8vre?= Date: Mon, 19 Jul 2021 15:07:40 +0200 Subject: [PATCH 2002/5615] Spock Testcontainers annotation is now inherited (#4053) --- .../groovy/org/testcontainers/spock/Testcontainers.groovy | 4 +++- .../groovy/org/testcontainers/spock/TestHierarchyIT.groovy | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/spock/src/main/groovy/org/testcontainers/spock/Testcontainers.groovy b/modules/spock/src/main/groovy/org/testcontainers/spock/Testcontainers.groovy index 7bbd47a4629..675ed68c4a6 100644 --- a/modules/spock/src/main/groovy/org/testcontainers/spock/Testcontainers.groovy +++ b/modules/spock/src/main/groovy/org/testcontainers/spock/Testcontainers.groovy @@ -3,13 +3,15 @@ package org.testcontainers.spock import org.spockframework.runtime.extension.ExtensionAnnotation import java.lang.annotation.ElementType +import java.lang.annotation.Inherited import java.lang.annotation.Retention import java.lang.annotation.RetentionPolicy import java.lang.annotation.Target +@Inherited @Retention(RetentionPolicy.RUNTIME) @Target([ElementType.TYPE, ElementType.METHOD]) @ExtensionAnnotation(TestcontainersExtension) @interface Testcontainers { -} \ No newline at end of file +} diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/TestHierarchyIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/TestHierarchyIT.groovy index 7fb37446ca5..c28643910a4 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/TestHierarchyIT.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/TestHierarchyIT.groovy @@ -4,9 +4,9 @@ import org.testcontainers.containers.PostgreSQLContainer import spock.lang.Shared /** - * This test verifies that integration tests can subclass each other + * This test verifies that integration tests can subclass each other. + * Also verifies that the @Testcontainers annotation is inherited. */ -@Testcontainers class TestHierarchyIT extends MySqlContainerIT { @Shared From 71314ab78f7105a939d257e05ea2b335dfba7dbf Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 19 Jul 2021 20:34:50 +0200 Subject: [PATCH 2003/5615] Fallback to x86 image if image pulling fails (#4290) * Fallback to x86 image if image pulling fails * Add the fallback to `checkAndPullImage` --- .../testcontainers/DockerClientFactory.java | 18 +++++++++++++----- .../DockerClientProviderStrategy.java | 2 +- .../images/RemoteDockerImage.java | 19 +++++++++++++++---- docs/supported_docker_environment/index.md | 12 ++++++------ 4 files changed, 35 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 98e8249e4e5..411b5139de4 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -3,13 +3,13 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.async.ResultCallback; import com.github.dockerjava.api.command.CreateContainerCmd; -import com.github.dockerjava.api.command.ListImagesCmd; +import com.github.dockerjava.api.command.PullImageCmd; +import com.github.dockerjava.api.exception.DockerClientException; import com.github.dockerjava.api.exception.InternalServerErrorException; import com.github.dockerjava.api.exception.NotFoundException; import com.github.dockerjava.api.model.AccessMode; import com.github.dockerjava.api.model.Bind; import com.github.dockerjava.api.model.Frame; -import com.github.dockerjava.api.model.Image; import com.github.dockerjava.api.model.Info; import com.github.dockerjava.api.model.Version; import com.github.dockerjava.api.model.Volume; @@ -37,7 +37,6 @@ import java.io.InputStream; import java.net.URI; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Optional; @@ -330,8 +329,17 @@ private boolean checkMountableFile() { public void checkAndPullImage(DockerClient client, String image) { try { client.inspectImageCmd(image).exec(); - } catch (NotFoundException e) { - client.pullImageCmd(image).exec(new TimeLimitedLoggedPullImageResultCallback(log)).awaitCompletion(); + } catch (NotFoundException notFoundException) { + PullImageCmd pullImageCmd = client.pullImageCmd(image); + try { + pullImageCmd.exec(new TimeLimitedLoggedPullImageResultCallback(log)).awaitCompletion(); + } catch (DockerClientException e) { + // Try to fallback to x86 + pullImageCmd + .withPlatform("linux/amd64") + .exec(new TimeLimitedLoggedPullImageResultCallback(log)) + .awaitCompletion(); + } } } diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index 35e3b6eabb8..248e40790aa 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -240,7 +240,7 @@ public static DockerClient getClientForConfig(TransportConfig transportConfig) { DefaultDockerClientConfig.Builder configBuilder = DefaultDockerClientConfig.createDefaultConfigBuilder(); if (configBuilder.build().getApiVersion() == RemoteApiVersion.UNKNOWN_VERSION) { - configBuilder.withApiVersion(RemoteApiVersion.VERSION_1_30); + configBuilder.withApiVersion(RemoteApiVersion.VERSION_1_32); } return DockerClientImpl.getInstance( new AuthDelegatingDockerClientConfig( diff --git a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java index f4b04712913..4ce38124fe7 100644 --- a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java +++ b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java @@ -1,6 +1,7 @@ package org.testcontainers.images; import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.command.PullImageCmd; import com.github.dockerjava.api.exception.DockerClientException; import com.github.dockerjava.api.exception.InternalServerErrorException; import com.google.common.util.concurrent.Futures; @@ -75,11 +76,21 @@ protected final String resolve() { while (Instant.now().isBefore(lastRetryAllowed)) { try { - dockerClient + PullImageCmd pullImageCmd = dockerClient .pullImageCmd(imageName.getUnversionedPart()) - .withTag(imageName.getVersionPart()) - .exec(new TimeLimitedLoggedPullImageResultCallback(logger)) - .awaitCompletion(); + .withTag(imageName.getVersionPart()); + + try { + pullImageCmd + .exec(new TimeLimitedLoggedPullImageResultCallback(logger)) + .awaitCompletion(); + } catch (DockerClientException e) { + // Try to fallback to x86 + pullImageCmd + .withPlatform("linux/amd64") + .exec(new TimeLimitedLoggedPullImageResultCallback(logger)) + .awaitCompletion(); + } LocalImagesCache.INSTANCE.refreshCache(imageName); diff --git a/docs/supported_docker_environment/index.md b/docs/supported_docker_environment/index.md index 04cc0c9ee2b..62a2bbe0af2 100644 --- a/docs/supported_docker_environment/index.md +++ b/docs/supported_docker_environment/index.md @@ -4,15 +4,15 @@ | Host Operating System / Environment | Minimum recommended docker versions | Known issues / tips | |-------------------------------------|-----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Linux - general | Docker v1.10 | | -| Linux - Travis CI | Docker v1.10 | See [example .travis.yml](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/.travis.yml) for baseline Travis CI configuration | -| Linux - CircleCI (LXC driver) | Docker v1.9.1 | The `exec` feature is not compatible with CircleCI. See CircleCI configuration [example](./continuous_integration/circle_ci.md) | -| Linux - within a Docker container | Docker v1.12 | See [Running inside Docker](continuous_integration/dind_patterns.md) for Docker-in-Docker and Docker wormhole patterns | +| Linux - general | Docker v17.09 | | +| Linux - Travis CI | Docker v17.09 | See [example .travis.yml](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/.travis.yml) for baseline Travis CI configuration | +| Linux - CircleCI (LXC driver) | Docker v17.09 | The `exec` feature is not compatible with CircleCI. See CircleCI configuration [example](./continuous_integration/circle_ci.md) | +| Linux - within a Docker container | Docker v17.09 | See [Running inside Docker](continuous_integration/dind_patterns.md) for Docker-in-Docker and Docker wormhole patterns | | Mac OS X - Docker Toolbox | Docker Machine v0.8.0 | | -| Mac OS X - Docker for Mac | 1.12.0 | *Support is best-efforts at present*. `getTestHostIpAddress()` is [not currently supported](https://github.com/testcontainers/testcontainers-java/issues/166) due to limitations in Docker for Mac. | +| Mac OS X - Docker for Mac | v17.09 | *Support is best-efforts at present*. `getTestHostIpAddress()` is [not currently supported](https://github.com/testcontainers/testcontainers-java/issues/166) due to limitations in Docker for Mac. | | Windows - Docker Toolbox | | *Support is limited at present and this is not currently tested on a regular basis*. | | Windows - Docker for Windows | | *Support is best-efforts at present.* Only Linux Containers (LCOW) are supported at the moment. See [Windows Support](windows.md) | -| Windows - Windows Subsystem for Linux (WSL) | Docker v17.06 | *Support is best-efforts at present.* Only Linux Containers (LCOW) are supported at the moment. See [Windows Support](windows.md). | +| Windows - Windows Subsystem for Linux (WSL) | Docker v17.09 | *Support is best-efforts at present.* Only Linux Containers (LCOW) are supported at the moment. See [Windows Support](windows.md). | ## Docker environment discovery From 3c614f2538bce1564ea6fab5cd51e560721b2030 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 20 Jul 2021 08:34:49 +0100 Subject: [PATCH 2004/5615] Upgrade alpine images for better M1 compatibility (#4291) --- .../main/java/org/testcontainers/DockerClientFactory.java | 2 +- core/src/test/java/org/testcontainers/TestImages.java | 4 ++-- .../testcontainers/containers/DockerComposeFilesTest.java | 2 +- .../containers/ParsedDockerComposeFileValidationTest.java | 8 ++++---- .../containers/output/ToStringConsumerTest.java | 8 ++++---- .../org/testcontainers/dockerclient/ImagePullTest.java | 7 ++----- .../java/org/testcontainers/junit/DockerfileTest.java | 6 +++--- .../testcontainers/junit/FixedHostPortContainerTest.java | 2 +- .../junit/ParameterizedDockerfileContainerTest.java | 8 +++----- .../testcontainers/utility/DirectoryTarResourceTest.java | 6 +++--- core/src/test/resources/compose-dockerfile/Dockerfile | 4 ++-- .../resources/compose-scaling-multiple-containers.yml | 2 +- .../test/resources/dockerfile-build-invalid/Dockerfile | 2 +- core/src/test/resources/dockerfile-build-test/Dockerfile | 2 +- .../test/resources/dockerfile-build-test/Dockerfile-alt | 2 +- .../resources/dockerfile-build-test/Dockerfile-buildarg | 2 +- .../resources/dockerfile-build-test/Dockerfile-currentdir | 2 +- .../resources/health-wait-strategy-dockerfile/Dockerfile | 2 +- core/src/test/resources/mappable-dockerfile/Dockerfile | 4 ++-- .../src/test/java/generic/ContainerCreationTest.java | 2 +- .../generic/src/test/java/generic/ContainerLabelTest.java | 4 ++-- .../junit4/generic/src/test/java/generic/ExecTest.java | 2 +- docs/features/configuration.md | 2 +- docs/features/creating_images.md | 2 +- .../org/testcontainers/spock/SpockTestImages.groovy | 2 +- 25 files changed, 42 insertions(+), 47 deletions(-) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 411b5139de4..554860d8985 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -64,7 +64,7 @@ public class DockerClientFactory { TESTCONTAINERS_SESSION_ID_LABEL, SESSION_ID ); - private static final DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.5"); + private static final DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.14"); private static DockerClientFactory instance; // Cached client configuration diff --git a/core/src/test/java/org/testcontainers/TestImages.java b/core/src/test/java/org/testcontainers/TestImages.java index 7052f7e8f9d..3798f42af0d 100644 --- a/core/src/test/java/org/testcontainers/TestImages.java +++ b/core/src/test/java/org/testcontainers/TestImages.java @@ -6,7 +6,7 @@ public interface TestImages { DockerImageName REDIS_IMAGE = DockerImageName.parse("redis:3.0.2"); DockerImageName RABBITMQ_IMAGE = DockerImageName.parse("rabbitmq:3.5.3"); DockerImageName MONGODB_IMAGE = DockerImageName.parse("mongo:3.1.5"); - DockerImageName ALPINE_IMAGE = DockerImageName.parse("alpine:3.2"); + DockerImageName ALPINE_IMAGE = DockerImageName.parse("alpine:3.14"); DockerImageName DOCKER_REGISTRY_IMAGE = DockerImageName.parse("registry:2.7.0"); - DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.5"); + DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.14"); } diff --git a/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java b/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java index 8dbcde2181d..81586c8290b 100644 --- a/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java +++ b/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java @@ -20,6 +20,6 @@ public void shouldGetDependencyImagesWhenOverriding() { Lists.newArrayList(new File("src/test/resources/docker-compose-imagename-overriding-a.yml"), new File("src/test/resources/docker-compose-imagename-overriding-b.yml"))); Assertions.assertThat(dockerComposeFiles.getDependencyImages()) - .containsExactlyInAnyOrder("alpine:3.2", "redis:b", "mysql:b", "aservice"); + .containsExactlyInAnyOrder("alpine:3.14", "redis:b", "mysql:b", "aservice"); } } diff --git a/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java b/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java index bdceba343d9..ef70d06ada1 100644 --- a/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java +++ b/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java @@ -104,8 +104,8 @@ public void shouldObtainImageFromDockerfileBuild() { .contains( entry("mysql", Sets.newHashSet("mysql")), entry("redis", Sets.newHashSet("redis")), - entry("custom", Sets.newHashSet("alpine:3.2"))) - .as("all defined images are found"); // r/ redis, mysql from compose file, alpine:3.2 from Dockerfile build + entry("custom", Sets.newHashSet("alpine:3.14"))) + .as("all defined images are found"); // r/ redis, mysql from compose file, alpine:3.14 from Dockerfile build } @Test @@ -116,7 +116,7 @@ public void shouldObtainImageFromDockerfileBuildWithContext() { .contains( entry("mysql", Sets.newHashSet("mysql")), entry("redis", Sets.newHashSet("redis")), - entry("custom", Sets.newHashSet("alpine:3.2"))) - .as("all defined images are found"); // redis, mysql from compose file, alpine:3.2 from Dockerfile build + entry("custom", Sets.newHashSet("alpine:3.14"))) + .as("all defined images are found"); // redis, mysql from compose file, alpine:3.14 from Dockerfile build } } diff --git a/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java b/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java index 1610c688931..8da67e897e9 100644 --- a/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java +++ b/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java @@ -24,7 +24,7 @@ public class ToStringConsumerTest { @Test public void newlines_are_not_added_to_exec_output() throws Exception { - try (GenericContainer container = new GenericContainer<>("alpine:3.13")) { + try (GenericContainer container = new GenericContainer<>("alpine:3.14")) { container.withCommand("sleep", "2m"); container.start(); @@ -37,7 +37,7 @@ public void newlines_are_not_added_to_exec_output() throws Exception { @Test(timeout = 60_000L) public void newlines_are_not_added_to_exec_output_with_tty() throws Exception { - try (GenericContainer container = new GenericContainer<>("alpine:3.13")) { + try (GenericContainer container = new GenericContainer<>("alpine:3.14")) { container.withCreateContainerCmdModifier(cmd -> { cmd .withAttachStdin(true) @@ -56,7 +56,7 @@ public void newlines_are_not_added_to_exec_output_with_tty() throws Exception { @Test public void newlines_are_not_added_to_container_output() { - try (GenericContainer container = new GenericContainer<>("alpine:3.13")) { + try (GenericContainer container = new GenericContainer<>("alpine:3.14")) { container.withCommand("echo", "-n", LARGE_PAYLOAD); container.setStartupCheckStrategy(new OneShotStartupCheckStrategy()); container.start(); @@ -71,7 +71,7 @@ public void newlines_are_not_added_to_container_output() { @Test public void newlines_are_not_added_to_container_output_with_tty() { - try (GenericContainer container = new GenericContainer<>("alpine:3.13")) { + try (GenericContainer container = new GenericContainer<>("alpine:3.14")) { container.withCreateContainerCmdModifier(cmd -> { cmd.withTty(true); }); diff --git a/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java b/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java index fd936d26c19..8691e8f5f56 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java @@ -16,12 +16,9 @@ public class ImagePullTest { public static String[] parameters() { return new String[] { "alpine:latest", - "alpine:3.6", + "alpine:3.14", "alpine", // omitting the tag should work and default to latest - "alpine@sha256:8fd4b76819e1e5baac82bd0a3d03abfe3906e034cc5ee32100d12aaaf3956dc7", - "gliderlabs/alpine:latest", - "gliderlabs/alpine:3.5", - "gliderlabs/alpine@sha256:a19aa4a17a525c97e5a90a0c53a9f3329d2dc61b0a14df5447757a865671c085", + "alpine@sha256:1775bebec23e1f3ce486989bfc9ff3c4e951690df84aa9f926497d82f2ffca9d", "quay.io/testcontainers/ryuk:latest", "quay.io/testcontainers/ryuk:0.2.3", "quay.io/testcontainers/ryuk@sha256:bb5a635cac4bd96c93cc476969ce11dc56436238ec7cd028d0524462f4739dd9", diff --git a/core/src/test/java/org/testcontainers/junit/DockerfileTest.java b/core/src/test/java/org/testcontainers/junit/DockerfileTest.java index 7390b21fce2..2197d7be0e7 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerfileTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerfileTest.java @@ -40,7 +40,7 @@ protected void configure(BuildImageCmd buildImageCmd) { super.configure(buildImageCmd); List dockerfile = Arrays.asList( - "FROM alpine:3.2", + "FROM alpine:3.14", "RUN echo 'hello from Docker build process'", "CMD yes" ); @@ -59,7 +59,7 @@ public void dockerfileBuilderWorks() { .withFileFromClasspath("test.txt", "mappable-resource/test-resource.txt") .withFileFromString("folder/someFile.txt", "hello") .withDockerfileFromBuilder(builder -> builder - .from("alpine:3.2") + .from("alpine:3.14") .workDir("/app") .add("test.txt", "test file.txt") .run("ls", "-la", "/app/test file.txt") @@ -101,7 +101,7 @@ public int getFileMode() { }) .withDockerfileFromBuilder(builder -> builder - .from("alpine:3.2") + .from("alpine:3.14") .copy("someFile.txt", "/someFile.txt") .cmd("stat -c \"%a\" /someFile.txt") ); diff --git a/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java b/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java index 34c039b4fd3..02fad53fb1e 100644 --- a/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java @@ -24,7 +24,7 @@ */ public class FixedHostPortContainerTest { - private static final String TEST_IMAGE = "alpine:3.2"; + private static final String TEST_IMAGE = "alpine:3.14"; /** * Default http server port (just something different from default) diff --git a/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java b/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java index 4ea261e68fd..969d2836b2e 100644 --- a/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java @@ -35,11 +35,9 @@ public ParameterizedDockerfileContainerTest(String baseImage, String expectedVer @Parameterized.Parameters(name = "{0}") public static Object[][] data() { return new Object[][] { - { "alpine:3.2", "3.2"}, - { "alpine:3.3", "3.3"}, - { "alpine:3.4", "3.4"}, - { "alpine:3.5", "3.5"}, - { "alpine:3.6", "3.6"} + { "alpine:3.12", "3.12"}, + { "alpine:3.13", "3.13"}, + { "alpine:3.14", "3.14"} }; } diff --git a/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java b/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java index 3789a654c20..f439c55c3ea 100644 --- a/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java +++ b/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java @@ -26,7 +26,7 @@ public void simpleRecursiveFileTest() { GenericContainer container = new GenericContainer( new ImageFromDockerfile() .withDockerfileFromBuilder(builder -> - builder.from("alpine:3.3") + builder.from("alpine:3.14") .copy("/tmp/foo", "/foo") .cmd("cat /foo/test/resources/test-recursive-file.txt") .build() @@ -45,7 +45,7 @@ public void simpleRecursiveFileWithPermissionTest() { GenericContainer container = new GenericContainer( new ImageFromDockerfile() .withDockerfileFromBuilder(builder -> - builder.from("alpine:3.3") + builder.from("alpine:3.14") .copy("/tmp/foo", "/foo") .cmd("ls", "-al", "/") .build() @@ -68,7 +68,7 @@ public void simpleRecursiveClasspathResourceTest() { GenericContainer container = new GenericContainer( new ImageFromDockerfile() .withDockerfileFromBuilder(builder -> - builder.from("alpine:3.3") + builder.from("alpine:3.14") .copy("/tmp/foo", "/foo") .cmd("ls -lRt /foo") .build() diff --git a/core/src/test/resources/compose-dockerfile/Dockerfile b/core/src/test/resources/compose-dockerfile/Dockerfile index 64613d2cd32..7252932b104 100644 --- a/core/src/test/resources/compose-dockerfile/Dockerfile +++ b/core/src/test/resources/compose-dockerfile/Dockerfile @@ -1,5 +1,5 @@ -FROM alpine:3.2 +FROM alpine:3.14 ADD passthrough.sh /passthrough.sh -CMD /passthrough.sh \ No newline at end of file +CMD /passthrough.sh diff --git a/core/src/test/resources/compose-scaling-multiple-containers.yml b/core/src/test/resources/compose-scaling-multiple-containers.yml index da5ac6f3a07..3ea528d4574 100644 --- a/core/src/test/resources/compose-scaling-multiple-containers.yml +++ b/core/src/test/resources/compose-scaling-multiple-containers.yml @@ -3,5 +3,5 @@ services: redis: image: redis other: - image: alpine:3.5 + image: alpine:3.14 command: sleep 10000 diff --git a/core/src/test/resources/dockerfile-build-invalid/Dockerfile b/core/src/test/resources/dockerfile-build-invalid/Dockerfile index 6140f575ef7..b0198eb3e02 100644 --- a/core/src/test/resources/dockerfile-build-invalid/Dockerfile +++ b/core/src/test/resources/dockerfile-build-invalid/Dockerfile @@ -1 +1 @@ -FROM alpine:3.2 +FROM alpine:3.14 diff --git a/core/src/test/resources/dockerfile-build-test/Dockerfile b/core/src/test/resources/dockerfile-build-test/Dockerfile index eee182f21c1..9ae7dba8658 100644 --- a/core/src/test/resources/dockerfile-build-test/Dockerfile +++ b/core/src/test/resources/dockerfile-build-test/Dockerfile @@ -1,2 +1,2 @@ -FROM alpine:3.2 +FROM alpine:3.14 COPY localfile.txt /test.txt diff --git a/core/src/test/resources/dockerfile-build-test/Dockerfile-alt b/core/src/test/resources/dockerfile-build-test/Dockerfile-alt index f6938e5fc8d..43e5c556fe9 100644 --- a/core/src/test/resources/dockerfile-build-test/Dockerfile-alt +++ b/core/src/test/resources/dockerfile-build-test/Dockerfile-alt @@ -1,2 +1,2 @@ -FROM alpine:3.2 +FROM alpine:3.14 RUN echo "test4567" > /test.txt diff --git a/core/src/test/resources/dockerfile-build-test/Dockerfile-buildarg b/core/src/test/resources/dockerfile-build-test/Dockerfile-buildarg index 4ae26f50f77..b37f4193643 100644 --- a/core/src/test/resources/dockerfile-build-test/Dockerfile-buildarg +++ b/core/src/test/resources/dockerfile-build-test/Dockerfile-buildarg @@ -1,3 +1,3 @@ -FROM alpine:3.2 +FROM alpine:3.14 ARG CUSTOM_ARG RUN echo "${CUSTOM_ARG}" > /test.txt diff --git a/core/src/test/resources/dockerfile-build-test/Dockerfile-currentdir b/core/src/test/resources/dockerfile-build-test/Dockerfile-currentdir index 97f80bdfa65..e8456e1bc1f 100644 --- a/core/src/test/resources/dockerfile-build-test/Dockerfile-currentdir +++ b/core/src/test/resources/dockerfile-build-test/Dockerfile-currentdir @@ -1,2 +1,2 @@ -FROM alpine:3.2 +FROM alpine:3.14 COPY . / diff --git a/core/src/test/resources/health-wait-strategy-dockerfile/Dockerfile b/core/src/test/resources/health-wait-strategy-dockerfile/Dockerfile index ddb0f82bce7..c0abb7bb597 100644 --- a/core/src/test/resources/health-wait-strategy-dockerfile/Dockerfile +++ b/core/src/test/resources/health-wait-strategy-dockerfile/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.7 +FROM alpine:3.14 HEALTHCHECK --interval=1s CMD test -e /testfile diff --git a/core/src/test/resources/mappable-dockerfile/Dockerfile b/core/src/test/resources/mappable-dockerfile/Dockerfile index b1afd24a3ec..df186f6579c 100644 --- a/core/src/test/resources/mappable-dockerfile/Dockerfile +++ b/core/src/test/resources/mappable-dockerfile/Dockerfile @@ -1,6 +1,6 @@ -FROM alpine:3.2 +FROM alpine:3.14 ADD folder/someFile.txt /someFile.txt RUN cat /someFile.txt ADD test.txt /test.txt RUN cat /test.txt -CMD ping -c 5 www.google.com \ No newline at end of file +CMD ping -c 5 www.google.com diff --git a/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java b/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java index b0e8c98a0e4..83b45f0435d 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java @@ -20,7 +20,7 @@ public class ContainerCreationTest { .withExposedPorts(6379); // } - public static final DockerImageName ALPINE_IMAGE = DockerImageName.parse("alpine:3.2"); + public static final DockerImageName ALPINE_IMAGE = DockerImageName.parse("alpine:3.14"); // withOptions { // Set up a plain OS container and customize environment, diff --git a/docs/examples/junit4/generic/src/test/java/generic/ContainerLabelTest.java b/docs/examples/junit4/generic/src/test/java/generic/ContainerLabelTest.java index ca0878e89d4..08d57e31057 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/ContainerLabelTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/ContainerLabelTest.java @@ -8,7 +8,7 @@ public class ContainerLabelTest { // single_label { - public GenericContainer containerWithLabel = new GenericContainer(DockerImageName.parse("alpine:3.6")) + public GenericContainer containerWithLabel = new GenericContainer(DockerImageName.parse("alpine:3.14")) .withLabel("key", "value"); // } @@ -17,7 +17,7 @@ public class ContainerLabelTest { private Map mapOfLabels = new HashMap<>(); // populate map, e.g. mapOfLabels.put("key1", "value1"); - public GenericContainer containerWithMultipleLabels = new GenericContainer(DockerImageName.parse("alpine:3.6")) + public GenericContainer containerWithMultipleLabels = new GenericContainer(DockerImageName.parse("alpine:3.14")) .withLabels(mapOfLabels); // } } diff --git a/docs/examples/junit4/generic/src/test/java/generic/ExecTest.java b/docs/examples/junit4/generic/src/test/java/generic/ExecTest.java index e35ae18af70..2530cadb410 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/ExecTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/ExecTest.java @@ -13,7 +13,7 @@ public class ExecTest { @Rule - public GenericContainer container = new GenericContainer<>(DockerImageName.parse("alpine:3.6")) + public GenericContainer container = new GenericContainer<>(DockerImageName.parse("alpine:3.14")) .withCommand("top"); @Test diff --git a/docs/features/configuration.md b/docs/features/configuration.md index 2336ec840c5..ba4487f8722 100644 --- a/docs/features/configuration.md +++ b/docs/features/configuration.md @@ -50,7 +50,7 @@ Some companies disallow the usage of Docker Hub, but you can override `*.image` > **ryuk.container.image = testcontainers/ryuk:0.3.1** > Performs fail-safe cleanup of containers, and always required (unless [Ryuk is disabled](#disabling-ryuk)) -> **tinyimage.container.image = alpine:3.5** +> **tinyimage.container.image = alpine:3.14** > Used to check whether images can be pulled at startup, and always required (unless [startup checks are disabled](#disabling-the-startup-checks)) > **sshd.container.image = testcontainers/sshd:1.0.0** diff --git a/docs/features/creating_images.md b/docs/features/creating_images.md index 0539642e159..6a06627efa0 100644 --- a/docs/features/creating_images.md +++ b/docs/features/creating_images.md @@ -51,7 +51,7 @@ new GenericContainer( new ImageFromDockerfile() .withDockerfileFromBuilder(builder -> builder - .from("alpine:3.2") + .from("alpine:3.14") .run("apk add --update nginx") .cmd("nginx", "-g", "daemon off;") .build())) diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy index f18bc8dc96d..b9ef64fe475 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy @@ -6,5 +6,5 @@ interface SpockTestImages { DockerImageName MYSQL_IMAGE = DockerImageName.parse("mysql:5.7.34") DockerImageName POSTGRES_TEST_IMAGE = DockerImageName.parse("postgres:9.6.12") DockerImageName HTTPD_IMAGE = DockerImageName.parse("httpd:2.4-alpine") - DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.5") + DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.14") } From 034daa6e3d4a99c4ce71332ac49bf66957f903db Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Tue, 20 Jul 2021 10:37:37 +0200 Subject: [PATCH 2005/5615] Remove withPublishAllPorts from Ryuk and stabilize containerInfo content on start (#4263) `containerInfo` is queried until all ports expected by Testcontainers are mapped. Introduces Awaitility for retry logic. Removing `withPublishAllPorts` is especially important because else this can lead to publishing of excluded ports on Windows Co-authored-by: Sergei Egorov --- core/build.gradle | 2 + .../containers/GenericContainer.java | 35 +++++++++++++--- .../utility/DynamicPollInterval.java | 42 +++++++++++++++++++ .../utility/ResourceReaper.java | 34 +++++++++++++-- .../containers/GenericContainerTest.java | 27 ++++++++++++ .../containers/ReusabilityUnitTests.java | 7 +++- 6 files changed, 137 insertions(+), 10 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/utility/DynamicPollInterval.java diff --git a/core/build.gradle b/core/build.gradle index 384c1caf1f2..d7ccb9364f8 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -108,6 +108,8 @@ dependencies { exclude(group: 'org.jetbrains', module: 'annotations') } + shaded 'org.awaitility:awaitility:4.1.0' + api "com.github.docker-java:docker-java-api:3.2.11" shaded ('com.github.docker-java:docker-java-core:3.2.11') { diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 10f8e1e7195..56ca5345ecd 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -57,6 +57,7 @@ import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.DockerLoggerFactory; import org.testcontainers.utility.DockerMachineClient; +import org.testcontainers.utility.DynamicPollInterval; import org.testcontainers.utility.MountableFile; import org.testcontainers.utility.PathUtils; import org.testcontainers.utility.ResourceReaper; @@ -84,6 +85,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.ExecutionException; @@ -97,6 +99,7 @@ import java.util.zip.Checksum; import static com.google.common.collect.Lists.newArrayList; +import static org.awaitility.Awaitility.await; import static org.testcontainers.utility.CommandLine.runShellCommand; /** @@ -216,10 +219,10 @@ public class GenericContainer> private static final Set AVAILABLE_IMAGE_NAME_CACHE = new HashSet<>(); private static final RateLimiter DOCKER_CLIENT_RATE_LIMITER = RateLimiterBuilder - .newBuilder() - .withRate(1, TimeUnit.SECONDS) - .withConstantThroughput() - .build(); + .newBuilder() + .withRate(1, TimeUnit.SECONDS) + .withConstantThroughput() + .build(); @Nullable private Map tmpFsMapping; @@ -425,8 +428,30 @@ private void tryStart(Instant startedAt) { // For all registered output consumers, start following as close to container startup as possible this.logConsumers.forEach(this::followOutput); + // Wait until inspect container returns the mapped ports + containerInfo = await() + .atMost(5, TimeUnit.SECONDS) + .pollInterval(DynamicPollInterval.ofMillis(50)) + .pollInSameThread() + .until( + () -> dockerClient.inspectContainerCmd(containerId).exec(), + inspectContainerResponse -> { + Set exposedAndMappedPorts = inspectContainerResponse + .getNetworkSettings() + .getPorts() + .getBindings() + .entrySet() + .stream() + .filter(it -> Objects.nonNull(it.getValue())) // filter out exposed but not yet mapped + .map(Entry::getKey) + .map(ExposedPort::getPort) + .collect(Collectors.toSet()); + + return exposedAndMappedPorts.containsAll(exposedPorts); + } + ); + // Tell subclasses that we're starting - containerInfo = dockerClient.inspectContainerCmd(containerId).exec(); containerIsStarting(containerInfo, reused); // Wait until the container has reached the desired running state diff --git a/core/src/main/java/org/testcontainers/utility/DynamicPollInterval.java b/core/src/main/java/org/testcontainers/utility/DynamicPollInterval.java new file mode 100644 index 00000000000..1252fb2c3a1 --- /dev/null +++ b/core/src/main/java/org/testcontainers/utility/DynamicPollInterval.java @@ -0,0 +1,42 @@ +package org.testcontainers.utility; + +import org.awaitility.pollinterval.PollInterval; + +import java.time.Duration; +import java.time.Instant; + +/** + * Awaitility {@link org.awaitility.pollinterval.PollInterval} that takes execution time into consideration, + * to allow a constant poll-interval, as opposed to Awaitility's default poll-delay behaviour. + * + * @deprecated For internal usage only. + */ +@Deprecated +public class DynamicPollInterval implements PollInterval { + + final Duration interval; + Instant lastTimestamp; + + private DynamicPollInterval(Duration interval) { + this.interval = interval; + lastTimestamp = Instant.now(); + } + + public static DynamicPollInterval of(Duration duration) { + return new DynamicPollInterval(duration); + } + + public static DynamicPollInterval ofMillis(long millis) { + return DynamicPollInterval.of(Duration.ofMillis(millis)); + } + + @Override + public Duration next(int pollCount, Duration previousDuration) { + Instant now = Instant.now(); + Duration executionDuration = Duration.between(lastTimestamp, now); + + Duration result = interval.minusMillis(Math.min(interval.toMillis(), executionDuration.toMillis())); + lastTimestamp = now.plus(result); + return result; + } +} diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index 7adbc95bb4c..75c6e209960 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -9,6 +9,8 @@ import com.github.dockerjava.api.model.Frame; import com.github.dockerjava.api.model.HostConfig; import com.github.dockerjava.api.model.Network; +import com.github.dockerjava.api.model.PortBinding; +import com.github.dockerjava.api.model.Ports; import com.github.dockerjava.api.model.Volume; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Throwables; @@ -32,11 +34,13 @@ import java.net.Socket; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.time.Duration; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; @@ -45,6 +49,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.awaitility.Awaitility.await; + /** * Component that responsible for container removal and automatic cleanup of dead containers at JVM shutdown. */ @@ -96,10 +102,14 @@ public static String start(DockerClient client) { List binds = new ArrayList<>(); binds.add(new Bind(DockerClientFactory.instance().getRemoteDockerUnixSocketPath(), new Volume("/var/run/docker.sock"))); + ExposedPort ryukExposedPort = ExposedPort.tcp(8080); String ryukContainerId = client.createContainerCmd(ryukImage) - .withHostConfig(new HostConfig().withAutoRemove(true)) - .withExposedPorts(new ExposedPort(8080)) - .withPublishAllPorts(true) + .withHostConfig( + new HostConfig() + .withAutoRemove(true) + .withPortBindings(new PortBinding(Ports.Binding.empty(), ryukExposedPort)) + ) + .withExposedPorts(ryukExposedPort) .withName("testcontainers-ryuk-" + DockerClientFactory.SESSION_ID) .withLabels(Collections.singletonMap(DockerClientFactory.TESTCONTAINERS_LABEL, "true")) .withBinds(binds) @@ -125,7 +135,23 @@ public void onNext(Frame frame) { ContainerState containerState = new ContainerState() { - final InspectContainerResponse inspectedContainer = client.inspectContainerCmd(ryukContainerId).exec(); + // inspect container response might initially not contain the mapped port + final InspectContainerResponse inspectedContainer = await() + .atMost(5, TimeUnit.SECONDS) + .pollInterval(DynamicPollInterval.ofMillis(50)) + .pollInSameThread() + .until( + () -> client.inspectContainerCmd(ryukContainerId).exec(), + inspectContainerResponse -> { + return inspectContainerResponse + .getNetworkSettings() + .getPorts() + .getBindings() + .values() + .stream() + .anyMatch(Objects::nonNull); + } + ); @Override public List getExposedPorts() { diff --git a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java index 1e2cb041cf1..93a9cd96479 100644 --- a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java +++ b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java @@ -17,6 +17,7 @@ import org.rnorth.ducttape.unreliables.Unreliables; import org.testcontainers.DockerClientFactory; import org.testcontainers.TestImages; +import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import org.testcontainers.containers.startupcheck.StartupCheckStrategy; import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy; import org.testcontainers.images.builder.ImageFromDockerfile; @@ -29,9 +30,11 @@ import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.hamcrest.CoreMatchers.equalTo; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.testcontainers.TestImages.TINY_IMAGE; public class GenericContainerTest { @@ -119,6 +122,30 @@ public void shouldOnlyPublishExposedPorts() { } } + @Test + public void shouldWaitUntilExposedPortIsMapped() { + + ImageFromDockerfile image = new ImageFromDockerfile("publish-multiple") + .withDockerfileFromBuilder(builder -> + builder + .from("testcontainers/helloworld:1.1.0") + .expose(8080, 8081) // one additional port exposed in image + .build() + ); + + try ( + GenericContainer container = new GenericContainer<>(image) + .withExposedPorts(8080) + .withCreateContainerCmdModifier(it -> it.withExposedPorts(ExposedPort.tcp(8082))) // another port exposed by modifier + ) { + + container.start(); + + assertEquals("Only withExposedPort should be exposed", 1, container.getExposedPorts().size()); + assertTrue("withExposedPort should be exposed", container.getExposedPorts().contains(8080)); + } + } + static class NoopStartupCheckStrategy extends StartupCheckStrategy { @Override diff --git a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java index c212d04c33a..0540fcdba9d 100644 --- a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java +++ b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java @@ -10,6 +10,7 @@ import com.github.dockerjava.api.command.ListContainersCmd; import com.github.dockerjava.api.command.StartContainerCmd; import com.github.dockerjava.api.model.Container; +import com.github.dockerjava.api.model.NetworkSettings; import com.github.dockerjava.core.command.CreateContainerCmdImpl; import com.github.dockerjava.core.command.InspectContainerCmdImpl; import com.github.dockerjava.core.command.ListContainersCmdImpl; @@ -22,6 +23,8 @@ import org.junit.runner.RunWith; import org.junit.runners.BlockJUnit4ClassRunner; import org.junit.runners.Parameterized; +import org.mockito.Answers; +import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.stubbing.Answer; import org.rnorth.visibleassertions.VisibleAssertions; @@ -520,7 +523,9 @@ protected Answer startContainerAnswer() { protected Answer inspectContainerAnswer() { return invocation -> { InspectContainerCmd.Exec exec = command -> { - return new InspectContainerResponse(); + InspectContainerResponse stubResponse = Mockito.mock(InspectContainerResponse.class, Answers.RETURNS_DEEP_STUBS); + when(stubResponse.getNetworkSettings().getPorts().getBindings()).thenReturn(Collections.emptyMap()); + return stubResponse; }; return new InspectContainerCmdImpl(exec, invocation.getArgument(0)); }; From 3b3d130c823adf0364815f5bb87df256efbce786 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 20 Jul 2021 13:54:46 +0100 Subject: [PATCH 2006/5615] Split Windows CI into core and non-core steps (#4293) --- azure-pipelines.yml | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 1471f9948ca..247948901d8 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -18,10 +18,10 @@ jobs: publishJUnitResults: true testResultsFiles: '**/TEST-*.xml' - # Run all tests when running the Windows CI tests + # Run all core tests when running the Windows CI tests - task: Gradle@2 condition: eq(variables['Agent.OS'], 'Windows_NT') - displayName: Build & test (Windows - all modules) + displayName: Build & test (Windows - core) env: AWS_ACCESS_KEY_ID: $(aws.accessKeyId) AWS_SECRET_ACCESS_KEY: $(aws.secretAccessKey) @@ -29,6 +29,21 @@ jobs: gradleWrapperFile: 'gradlew' jdkVersionOption: '1.11' options: '--no-daemon --continue' - tasks: 'clean check' + tasks: 'clean testcontainers:check' + publishJUnitResults: true + testResultsFiles: '**/TEST-*.xml' + + # Run all non-core tests when running the Windows CI tests + - task: Gradle@2 + condition: eq(variables['Agent.OS'], 'Windows_NT') + displayName: Build & test (Windows - all non-core modules) + env: + AWS_ACCESS_KEY_ID: $(aws.accessKeyId) + AWS_SECRET_ACCESS_KEY: $(aws.secretAccessKey) + inputs: + gradleWrapperFile: 'gradlew' + jdkVersionOption: '1.11' + options: '--no-daemon --continue' + tasks: 'clean check -x testcontainers:test' publishJUnitResults: true testResultsFiles: '**/TEST-*.xml' From 38a443f098c3407a65330bc38ae016cbe4bae8e1 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Thu, 29 Jul 2021 16:56:49 +0200 Subject: [PATCH 2007/5615] Update RELEASING.md (#4295) Update the docs to reflect our current release process. --- RELEASING.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index aaba172d20e..34a6b55ae26 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -1,19 +1,23 @@ # Release process -Testcontainers' release process is automated as a Travis deployment. This describes the basic steps for a project member to perform a release. +Testcontainers' release process is semi-automated through GitHub Actions. This describes the basic steps for a project member to perform a release. ## Steps 1. Ensure that the master branch is building and that tests are passing. -1. Ensure that the [`CHANGELOG`](CHANGELOG.md) file is up to date and includes all merged features. 1. Create a new release on GitHub. **The tag name is used as the version**, so please keep the tag name plain (e.g. 1.2.3). -1. Check that the Travis build passed. -1. Release of published artifacts to Bintray is fully automated. -1. After successful publication to Bintray, the artifacts must be synced to Maven Central. +1. The release triggers a GitHub Action workflow, but it gets mostly build using results from the Gradle remote-cache. Therefore, this should be fairly fast. +1. Login to Sonatype to check the staging repository. +1. Get the staging url after GitHub Action workflow finished. +1. Manually test the release with the staging url as maven repository url (e.g. critical issues and features). +1. Run [TinSalver](https://github.com/bsideup/tinsalver) from GitHub using `npx` to sign artifact (see [TinsSalver README](https://github.com/bsideup/tinsalver/blob/main/README.md)). +1. Close the release in Sonatype. This will evaluate the release based on given Sontaype rules and afterwards automatically sync to Maven Central. +1. Handcraft and polish some of the release notes (e.g. substitute combinded dependency PRs and highlight certain features). 1. When available through Maven Central, poke [Richard North](https://github.com/rnorth) to announce the release on Twitter! ## Internal details -* The process is done with Gradle and Bintray. -* Bintray will automatically promote the release to Maven Central. -* Travis secrets hold Bintray username/passwords that are used for publishing. +* The process is done with GitHub Actions, TinSalver and Sonatype. +* Sonatype will automatically promote the staging release to Maven Central. +* Keybase needs to be installed on the developer machine. +* GPG key of signing developer needs to be uplodaed to the Ubuntu keyserver (or other server supported by Sonatype). From 520cfeb5345f4230a1611b9b1c7708c2b1d8353e Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Fri, 30 Jul 2021 23:08:49 +0200 Subject: [PATCH 2008/5615] Disable Chrome's GPU support in `BrowserWebDriverContainer` (#4315) * Trigger selenium build on CI * Try again to trigger build * Apply potential fix by disabling GPU * Also --disable-dev-shm-usage * Alwas add disable-gpu flag * Smaller check for hack * Smaller check for hack --- .../containers/BrowserWebDriverContainer.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index 4a25ae486cd..1209b7af019 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -166,6 +166,13 @@ protected void configure() { } } + // Hack for new selenium-chrome image that contains Chrome 92. + // If not disabled, container startup will fail in most cases and consume excessive amounts of CPU. + if (capabilities instanceof ChromeOptions) { + ChromeOptions options = (ChromeOptions) this.capabilities; + options.addArguments("--disable-gpu"); + } + if (recordingMode != VncRecordingMode.SKIP) { if (vncRecordingDirectory == null) { From 4df9b900e79e4a3033ef0975abacf9064a0e7353 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 30 Jul 2021 23:56:49 +0200 Subject: [PATCH 2009/5615] Update Kafka config dynamically (#4316) * Update Kafka config dynamically * Simplify further, fix the Kafka Cluster example --- .../containers/GenericContainer.java | 2 +- .../containers/KafkaContainer.java | 106 ++++++------------ 2 files changed, 37 insertions(+), 71 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 56ca5345ecd..50e4b927617 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -135,7 +135,7 @@ public class GenericContainer> @NonNull private String networkMode; - @NonNull + @Nullable private Network network; @NonNull diff --git a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java index d9c8e35e852..f62ca23eb57 100644 --- a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java +++ b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java @@ -2,12 +2,8 @@ import com.github.dockerjava.api.command.InspectContainerResponse; import lombok.SneakyThrows; -import org.testcontainers.images.builder.Transferable; import org.testcontainers.utility.DockerImageName; -import java.nio.charset.StandardCharsets; -import java.util.Comparator; - /** * This container wraps Confluent Kafka and Zookeeper (optionally) * @@ -17,20 +13,14 @@ public class KafkaContainer extends GenericContainer { private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("confluentinc/cp-kafka"); private static final String DEFAULT_TAG = "5.4.3"; - private static final String STARTER_SCRIPT = "/testcontainers_start.sh"; - public static final int KAFKA_PORT = 9093; public static final int ZOOKEEPER_PORT = 2181; private static final String DEFAULT_INTERNAL_TOPIC_RF = "1"; - private static final int PORT_NOT_ASSIGNED = -1; - protected String externalZookeeperConnect = null; - private int port = PORT_NOT_ASSIGNED; - /** * @deprecated use {@link KafkaContainer(DockerImageName)} instead */ @@ -80,83 +70,59 @@ public KafkaContainer withExternalZookeeper(String connectString) { } public String getBootstrapServers() { - if (port == PORT_NOT_ASSIGNED) { - throw new IllegalStateException("You should start Kafka container first"); - } - return String.format("PLAINTEXT://%s:%s", getHost(), port); - } - - @Override - protected void doStart() { - withCommand("sh", "-c", "while [ ! -f " + STARTER_SCRIPT + " ]; do sleep 0.1; done; " + STARTER_SCRIPT); - - if (externalZookeeperConnect == null) { - addExposedPort(ZOOKEEPER_PORT); - } - - super.doStart(); + return String.format("PLAINTEXT://%s:%s", getHost(), getMappedPort(KAFKA_PORT)); } @Override - @SneakyThrows - protected void containerIsStarting(InspectContainerResponse containerInfo, boolean reused) { - super.containerIsStarting(containerInfo, reused); - - port = getMappedPort(KAFKA_PORT); - - if (reused) { - return; - } + protected void configure() { + withEnv( + "KAFKA_ADVERTISED_LISTENERS", + String.format( + "BROKER://%s:9092", + getNetwork() != null + ? getNetworkAliases().get(0) + : "localhost" + ) + ); String command = "#!/bin/bash\n"; - final String zookeeperConnect; if (externalZookeeperConnect != null) { - zookeeperConnect = externalZookeeperConnect; + withEnv("KAFKA_ZOOKEEPER_CONNECT", externalZookeeperConnect); } else { - zookeeperConnect = "localhost:" + ZOOKEEPER_PORT; + addExposedPort(ZOOKEEPER_PORT); + withEnv("KAFKA_ZOOKEEPER_CONNECT", "localhost:" + ZOOKEEPER_PORT); command += "echo 'clientPort=" + ZOOKEEPER_PORT + "' > zookeeper.properties\n"; command += "echo 'dataDir=/var/lib/zookeeper/data' >> zookeeper.properties\n"; command += "echo 'dataLogDir=/var/lib/zookeeper/log' >> zookeeper.properties\n"; command += "zookeeper-server-start zookeeper.properties &\n"; } - command += "export KAFKA_ZOOKEEPER_CONNECT='" + zookeeperConnect + "'\n"; - - command += "export KAFKA_ADVERTISED_LISTENERS='" + String.join(",", getBootstrapServers(), brokerAdvertisedListener(containerInfo)) + "'\n"; - - command += ". /etc/confluent/docker/bash-config \n"; - command += "/etc/confluent/docker/configure \n"; - command += "/etc/confluent/docker/launch \n"; + // Optimization: skip the checks + command += "echo '' > /etc/confluent/docker/ensure \n"; + // Run the original command + command += "/etc/confluent/docker/run \n"; + withCommand("sh", "-c", command); + } - copyFileToContainer( - Transferable.of(command.getBytes(StandardCharsets.UTF_8), 0777), - STARTER_SCRIPT + @Override + @SneakyThrows + protected void containerIsStarted(InspectContainerResponse containerInfo) { + String brokerAdvertisedListener = brokerAdvertisedListener(containerInfo); + ExecResult result = execInContainer( + "kafka-configs", + "--alter", + "--bootstrap-server", brokerAdvertisedListener, + "--entity-type", "brokers", + "--entity-name", getEnvMap().get("KAFKA_BROKER_ID"), + "--add-config", + "advertised.listeners=[" + String.join(",", getBootstrapServers(), brokerAdvertisedListener) + "]" ); + if (result.getExitCode() != 0) { + throw new IllegalStateException(result.getStderr()); + } } protected String brokerAdvertisedListener(InspectContainerResponse containerInfo) { - // Kafka supports only one INTER_BROKER listener, so we have to pick one. - // The current algorithm uses the following order of resolving the IP: - // 1. Custom network's IP set via `withNetwork` - // 2. Bridge network's IP - // 3. Best effort fallback to getNetworkSettings#ipAddress - String ipAddress = containerInfo.getNetworkSettings().getNetworks().entrySet() - .stream() - .filter(it -> it.getValue().getIpAddress() != null) - .max(Comparator.comparingInt(entry -> { - if (getNetwork() != null && getNetwork().getId().equals(entry.getValue().getNetworkID())) { - return 2; - } - - if ("bridge".equals(entry.getKey())) { - return 1; - } - - return 0; - })) - .map(it -> it.getValue().getIpAddress()) - .orElseGet(() -> containerInfo.getNetworkSettings().getIpAddress()); - - return String.format("BROKER://%s:%s", ipAddress, "9092"); + return String.format("BROKER://%s:%s", containerInfo.getConfig().getHostName(), "9092"); } } From 6d257e682ad01f87645317c21c851fb8db952ff1 Mon Sep 17 00:00:00 2001 From: Martin Gjaldbaek Date: Sun, 1 Aug 2021 07:44:33 +0200 Subject: [PATCH 2010/5615] Fix potential injection vulnerability (#4314) See https://github.com/hrvey/combine-prs-workflow/releases/tag/1.2.0 --- .github/workflows/combine-prs.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/combine-prs.yml b/.github/workflows/combine-prs.yml index e1d96a20895..2e8177458a0 100644 --- a/.github/workflows/combine-prs.yml +++ b/.github/workflows/combine-prs.yml @@ -105,7 +105,7 @@ jobs: BRANCHES_TO_COMBINE: ${{ steps.fetch-branch-names.outputs.result }} COMBINE_BRANCH_NAME: ${{ github.event.inputs.combineBranchName }} run: | - echo "${{steps.fetch-branch-names.outputs.result}}" + echo "$BRANCHES_TO_COMBINE" sourcebranches="${BRANCHES_TO_COMBINE%\"}" sourcebranches="${sourcebranches#\"}" @@ -123,10 +123,12 @@ jobs: # Creates a PR with the new combined branch - uses: actions/github-script@v4.0.2 name: Create Combined Pull Request + env: + PRS_STRING: ${{ steps.fetch-branch-names.outputs.prs-string }} with: github-token: ${{secrets.GITHUB_TOKEN}} script: | - const prString = `${{ steps.fetch-branch-names.outputs.prs-string }}`; + const prString = process.env.PRS_STRING; const body = 'This PR was created by the Combine PRs action by combining the following PRs:\n' + prString; await github.pulls.create({ owner: context.repo.owner, From 289521d84555b7680af3edb15a623a210cf08def Mon Sep 17 00:00:00 2001 From: Richard North Date: Sun, 1 Aug 2021 06:46:26 +0100 Subject: [PATCH 2011/5615] Delete combine-prs.yml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removing because we’re now using an external script based on `gh` Which works pretty well for us. --- .github/workflows/combine-prs.yml | 140 ------------------------------ 1 file changed, 140 deletions(-) delete mode 100644 .github/workflows/combine-prs.yml diff --git a/.github/workflows/combine-prs.yml b/.github/workflows/combine-prs.yml deleted file mode 100644 index 2e8177458a0..00000000000 --- a/.github/workflows/combine-prs.yml +++ /dev/null @@ -1,140 +0,0 @@ -name: 'Combine PRs' - -# Controls when the action will run - in this case triggered manually -on: - workflow_dispatch: - inputs: - branchPrefix: - description: 'Branch prefix to find combinable PRs based on' - required: true - default: 'dependabot' - mustBeGreen: - description: 'Only combine PRs that are green (status is success)' - required: true - default: 'true' - combineBranchName: - description: 'Name of the branch to combine PRs into' - required: true - default: 'combine-prs-branch' - ignoreLabel: - description: 'Exclude PRs with this label' - required: true - default: 'nocombine' - -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - # This workflow contains a single job called "combine-prs" - combine-prs: - # The type of runner that the job will run on - runs-on: ubuntu-latest - - # Steps represent a sequence of tasks that will be executed as part of the job - steps: - - uses: actions/github-script@v4.0.2 - id: fetch-branch-names - name: Fetch branch names - with: - github-token: ${{secrets.GITHUB_TOKEN}} - script: | - const pulls = await github.paginate('GET /repos/:owner/:repo/pulls', { - owner: context.repo.owner, - repo: context.repo.repo - }); - branches = []; - prs = []; - base_branch = null; - for (const pull of pulls) { - const branch = pull['head']['ref']; - console.log('Pull for branch: ' + branch); - if (branch.startsWith('${{ github.event.inputs.branchPrefix }}')) { - console.log('Branch matched: ' + branch); - statusOK = true; - if(${{ github.event.inputs.mustBeGreen }}) { - console.log('Checking green status: ' + branch); - const checkRuns = await github.request('GET /repos/{owner}/{repo}/commits/{ref}/check-runs', { - owner: context.repo.owner, - repo: context.repo.repo, - ref: branch - }); - for await (const cr of checkRuns.data.check_runs) { - console.log('Validating check conclusion: ' + cr.conclusion); - if(cr.conclusion != 'success') { - console.log('Discarding ' + branch + ' with check conclusion ' + cr.conclusion); - statusOK = false; - } - } - } - console.log('Checking labels: ' + branch); - const labels = pull['labels']; - for(const label of labels) { - const labelName = label['name']; - console.log('Checking label: ' + labelName); - if(labelName == '${{ github.event.inputs.ignoreLabel }}') { - console.log('Discarding ' + branch + ' with label ' + labelName); - statusOK = false; - } - } - if (statusOK) { - console.log('Adding branch to array: ' + branch); - branches.push(branch); - prs.push('#' + pull['number'] + ' ' + pull['title']); - base_branch = pull['base']['ref']; - } - } - } - - if (branches.length == 0) { - core.setFailed('No PRs/branches matched criteria'); - return; - } - - core.setOutput('base-branch', base_branch); - core.setOutput('prs-string', prs.join('\n')); - - combined = branches.join(' ') - console.log('Combined: ' + combined); - return combined - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2.3.3 - with: - fetch-depth: 0 - # Creates a branch with other PR branches merged together - - name: Created combined branch - env: - BASE_BRANCH: ${{ steps.fetch-branch-names.outputs.base-branch }} - BRANCHES_TO_COMBINE: ${{ steps.fetch-branch-names.outputs.result }} - COMBINE_BRANCH_NAME: ${{ github.event.inputs.combineBranchName }} - run: | - echo "$BRANCHES_TO_COMBINE" - sourcebranches="${BRANCHES_TO_COMBINE%\"}" - sourcebranches="${sourcebranches#\"}" - - basebranch="${BASE_BRANCH%\"}" - basebranch="${basebranch#\"}" - - git config pull.rebase false - git config user.name github-actions - git config user.email github-actions@github.com - - git branch $COMBINE_BRANCH_NAME $basebranch - git checkout $COMBINE_BRANCH_NAME - git pull origin $sourcebranches --no-edit - git push origin $COMBINE_BRANCH_NAME - # Creates a PR with the new combined branch - - uses: actions/github-script@v4.0.2 - name: Create Combined Pull Request - env: - PRS_STRING: ${{ steps.fetch-branch-names.outputs.prs-string }} - with: - github-token: ${{secrets.GITHUB_TOKEN}} - script: | - const prString = process.env.PRS_STRING; - const body = 'This PR was created by the Combine PRs action by combining the following PRs:\n' + prString; - await github.pulls.create({ - owner: context.repo.owner, - repo: context.repo.repo, - title: 'Combined PR', - head: '${{ github.event.inputs.combineBranchName }}', - base: '${{ steps.fetch-branch-names.outputs.base-branch }}', - body: body - }); From 676845417c88ac136fd5df323e788f16beec817f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Aug 2021 22:00:35 +0000 Subject: [PATCH 2012/5615] Bump mysql-connector-java from 8.0.25 to 8.0.26 in /modules/jdbc-test Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.25 to 8.0.26. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.25...8.0.26) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 99cba7dcb70..17c44383f74 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -16,5 +16,5 @@ dependencies { api 'org.apache.tomcat:tomcat-jdbc:10.0.7' api 'org.vibur:vibur-dbcp:25.0' - api 'mysql:mysql-connector-java:8.0.25' + api 'mysql:mysql-connector-java:8.0.26' } From d5d91b5d80efa0d95ee5e0b52fd028951e539615 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Aug 2021 22:00:36 +0000 Subject: [PATCH 2013/5615] Bump java-client from 3.1.6 to 3.2.0 in /modules/couchbase Bumps [java-client](https://github.com/couchbase/couchbase-jvm-clients) from 3.1.6 to 3.2.0. - [Release notes](https://github.com/couchbase/couchbase-jvm-clients/releases) - [Commits](https://github.com/couchbase/couchbase-jvm-clients/compare/java-client-3.1.6...java-client-3.2.0) --- updated-dependencies: - dependency-name: com.couchbase.client:java-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 40f59128ec8..823b88a983e 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Couchbase" dependencies { api project(':testcontainers') - testImplementation 'com.couchbase.client:java-client:3.1.6' + testImplementation 'com.couchbase.client:java-client:3.2.0' testImplementation 'org.awaitility:awaitility:4.1.0' } From 42a4fddccf1de98a7b7d1b4b98f4e9a0f5a40100 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Aug 2021 22:00:37 +0000 Subject: [PATCH 2014/5615] Bump slf4j-api from 1.7.30 to 1.7.32 in /core Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 1.7.30 to 1.7.32. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_1.7.30...v_1.7.32) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index d7ccb9364f8..29c521a97e8 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -100,7 +100,7 @@ dependencies { } api 'junit:junit:4.12' - api 'org.slf4j:slf4j-api:1.7.30' + api 'org.slf4j:slf4j-api:1.7.32' compileOnly 'org.jetbrains:annotations:21.0.1' testCompileClasspath 'org.jetbrains:annotations:21.0.1' api 'org.apache.commons:commons-compress:1.20' From 41e7cede1a4bee67c1d72504168c2579b5e46d84 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Aug 2021 22:00:37 +0000 Subject: [PATCH 2015/5615] Bump postgresql from 42.2.22 to 42.2.23 in /modules/postgresql Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.22 to 42.2.23. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/REL42.2.23/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.22...REL42.2.23) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 9b7063e3a0c..410706429d5 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -11,7 +11,7 @@ dependencies { testImplementation project(':jdbc-test') testImplementation project(':test-support') - testImplementation 'org.postgresql:postgresql:42.2.22' + testImplementation 'org.postgresql:postgresql:42.2.23' testImplementation testFixtures(project(':r2dbc')) testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.5.RELEASE' From a622e0d06c3dbcf3086ecc4f21963f07362f6f2a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Aug 2021 22:00:37 +0000 Subject: [PATCH 2016/5615] Bump mysql-connector-java in /modules/junit-jupiter Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.25 to 8.0.26. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.25...8.0.26) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 9554f2c9e5b..2722f659bdd 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -16,7 +16,7 @@ dependencies { testImplementation 'org.junit.jupiter:junit-jupiter-params:5.7.2' testRuntimeOnly 'org.postgresql:postgresql:42.2.22' - testRuntimeOnly 'mysql:mysql-connector-java:8.0.25' + testRuntimeOnly 'mysql:mysql-connector-java:8.0.26' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.2' } From f120d5e64b1173bb2b30db9566e70fead0443286 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Aug 2021 22:00:37 +0000 Subject: [PATCH 2017/5615] Bump r2dbc-mariadb from 1.0.1 to 1.0.2 in /modules/mariadb Bumps [r2dbc-mariadb](https://github.com/mariadb-corporation/mariadb-connector-r2dbc) from 1.0.1 to 1.0.2. - [Release notes](https://github.com/mariadb-corporation/mariadb-connector-r2dbc/releases) - [Changelog](https://github.com/mariadb-corporation/mariadb-connector-r2dbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/mariadb-corporation/mariadb-connector-r2dbc/compare/1.0.1...1.0.2) --- updated-dependencies: - dependency-name: org.mariadb:r2dbc-mariadb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mariadb/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index ca267a1fe7c..e9080ce6131 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -7,11 +7,11 @@ dependencies { api project(':jdbc') compileOnly project(':r2dbc') - compileOnly 'org.mariadb:r2dbc-mariadb:1.0.1' + compileOnly 'org.mariadb:r2dbc-mariadb:1.0.2' testImplementation project(':jdbc-test') testImplementation 'org.mariadb.jdbc:mariadb-java-client:2.7.3' testImplementation testFixtures(project(':r2dbc')) - testImplementation 'org.mariadb:r2dbc-mariadb:1.0.0' + testImplementation 'org.mariadb:r2dbc-mariadb:1.0.2' } From 25880f94fd70c0443d83f493ef3c71ee2f9caa9c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Aug 2021 22:00:41 +0000 Subject: [PATCH 2018/5615] Bump aws-java-sdk-dynamodb from 1.12.16 to 1.12.37 in /modules/dynalite Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.12.16 to 1.12.37. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.16...1.12.37) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-dynamodb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/dynalite/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index c92b1969147..85c91332c12 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Dynalite" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.16' - testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.11.1030' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.37' + testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.37' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 2ef95ba8d7c1f171d9d4eefbdc639c91ceaddc65 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Aug 2021 22:00:42 +0000 Subject: [PATCH 2019/5615] Bump jedis from 3.6.1 to 3.6.3 in /modules/junit-jupiter Bumps [jedis](https://github.com/redis/jedis) from 3.6.1 to 3.6.3. - [Release notes](https://github.com/redis/jedis/releases) - [Commits](https://github.com/redis/jedis/compare/jedis-3.6.1...v3.6.3) --- updated-dependencies: - dependency-name: redis.clients:jedis dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 9554f2c9e5b..e48d28a5e5e 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -7,7 +7,7 @@ dependencies { testImplementation project(':mysql') testImplementation project(':postgresql') testImplementation 'com.zaxxer:HikariCP:4.0.3' - testImplementation 'redis.clients:jedis:3.6.1' + testImplementation 'redis.clients:jedis:3.6.3' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' testImplementation ('org.mockito:mockito-core:3.11.2') { exclude(module: 'hamcrest-core') From 1f84c2af02453e841297b306d61e3e234c9f102a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Aug 2021 22:00:43 +0000 Subject: [PATCH 2020/5615] Bump mysql-connector-java from 8.0.25 to 8.0.26 in /modules/mysql Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.25 to 8.0.26. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.25...8.0.26) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index a14fa678280..db2deac2d10 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' testImplementation project(':jdbc-test') - testImplementation 'mysql:mysql-connector-java:8.0.25' + testImplementation 'mysql:mysql-connector-java:8.0.26' testImplementation testFixtures(project(':r2dbc')) testImplementation 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' From 695c77f73e56406d55aeb18800fcf73f0901c858 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Aug 2021 22:00:43 +0000 Subject: [PATCH 2021/5615] Bump mongo-java-driver from 3.12.7 to 3.12.9 in /core Bumps [mongo-java-driver](https://github.com/mongodb/mongo-java-driver) from 3.12.7 to 3.12.9. - [Release notes](https://github.com/mongodb/mongo-java-driver/releases) - [Commits](https://github.com/mongodb/mongo-java-driver/compare/r3.12.7...r3.12.9) --- updated-dependencies: - dependency-name: org.mongodb:mongo-java-driver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index d7ccb9364f8..2677486fa58 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -140,7 +140,7 @@ dependencies { testImplementation 'org.apache.httpcomponents:httpclient:4.5.9' testImplementation 'redis.clients:jedis:3.6.1' testImplementation 'com.rabbitmq:amqp-client:5.12.0' - testImplementation 'org.mongodb:mongo-java-driver:3.12.7' + testImplementation 'org.mongodb:mongo-java-driver:3.12.9' testImplementation ('org.mockito:mockito-core:3.11.2') { exclude(module: 'hamcrest-core') From ab2ede992f69dab8379177832a4fe95b330db930 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Aug 2021 22:00:44 +0000 Subject: [PATCH 2022/5615] Bump HikariCP from 4.0.3 to 5.0.0 in /modules/spock Bumps [HikariCP](https://github.com/brettwooldridge/HikariCP) from 4.0.3 to 5.0.0. - [Release notes](https://github.com/brettwooldridge/HikariCP/releases) - [Changelog](https://github.com/brettwooldridge/HikariCP/blob/dev/CHANGES) - [Commits](https://github.com/brettwooldridge/HikariCP/compare/HikariCP-4.0.3...HikariCP-5.0.0) --- updated-dependencies: - dependency-name: com.zaxxer:HikariCP dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 6ee936b7ca7..56f35ffe695 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -12,7 +12,7 @@ dependencies { testImplementation project(':mysql') testImplementation project(':postgresql') - testImplementation 'com.zaxxer:HikariCP:4.0.3' + testImplementation 'com.zaxxer:HikariCP:5.0.0' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' testRuntimeOnly 'org.postgresql:postgresql:42.2.22' From d8737a9154b35ed3981803ac81a3f3ded6e16631 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Aug 2021 22:00:48 +0000 Subject: [PATCH 2023/5615] Bump transport from 7.13.2 to 7.13.4 in /modules/elasticsearch Bumps [transport](https://github.com/elastic/elasticsearch) from 7.13.2 to 7.13.4. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.13.2...v7.13.4) --- updated-dependencies: - dependency-name: org.elasticsearch.client:transport dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 67e03df2945..180f373bddd 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,6 +3,6 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') testImplementation "org.elasticsearch.client:elasticsearch-rest-client:7.13.0" - testImplementation "org.elasticsearch.client:transport:7.13.2" + testImplementation "org.elasticsearch.client:transport:7.13.4" testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 2e53d6deba8df7aba18502bc86b56f0ba510200f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Aug 2021 22:00:48 +0000 Subject: [PATCH 2024/5615] Bump aws-java-sdk-s3 from 1.12.16 to 1.12.37 in /modules/localstack Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.16 to 1.12.37. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.16...1.12.37) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 031a601bdc9..5158325fa92 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Localstack" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.16' - testImplementation 'com.amazonaws:aws-java-sdk-s3:1.11.1030' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.37' + testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.37' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.19' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.16' testImplementation 'software.amazon.awssdk:s3:2.16.97' From f8ff174917c480019cf2bd361eab01d33397d974 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Aug 2021 22:00:52 +0000 Subject: [PATCH 2025/5615] Bump slf4j-api from 1.7.30 to 1.7.32 in /examples Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 1.7.30 to 1.7.32. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_1.7.30...v_1.7.32) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index c5706da277a..6ca8b1ae664 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -6,7 +6,7 @@ repositories { jcenter() } dependencies { - compileOnly 'org.slf4j:slf4j-api:1.7.30' + compileOnly 'org.slf4j:slf4j-api:1.7.32' implementation 'com.squareup.okhttp3:okhttp:4.9.1' implementation 'org.json:json:20210307' testImplementation 'org.postgresql:postgresql:42.2.22' diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 58f31827717..56dab9c4951 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:1.7.30' + compileOnly 'org.slf4j:slf4j-api:1.7.32' implementation 'redis.clients:jedis:3.6.1' implementation 'com.google.code.gson:gson:2.8.7' implementation 'com.google.guava:guava:23.0' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 4fb93cb71b5..771f55551ea 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:1.7.30' + compileOnly 'org.slf4j:slf4j-api:1.7.32' implementation 'redis.clients:jedis:3.6.1' implementation 'com.google.code.gson:gson:2.8.7' implementation 'com.google.guava:guava:23.0' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index e803f616998..a806f483938 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -11,7 +11,7 @@ dependencies { implementation 'redis.clients:jedis:3.6.1' implementation 'com.google.code.gson:gson:2.8.7' implementation 'com.google.guava:guava:23.0' - compileOnly 'org.slf4j:slf4j-api:1.7.30' + compileOnly 'org.slf4j:slf4j-api:1.7.32' testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.testcontainers:testcontainers' From 3063021a6bc1d6739611339c0cfb0234ce78554d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Aug 2021 22:00:54 +0000 Subject: [PATCH 2026/5615] Bump postgresql from 42.2.22 to 42.2.23 in /modules/spock Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.22 to 42.2.23. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/REL42.2.23/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.22...REL42.2.23) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 6ee936b7ca7..7720067b620 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'com.zaxxer:HikariCP:4.0.3' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' - testRuntimeOnly 'org.postgresql:postgresql:42.2.22' + testRuntimeOnly 'org.postgresql:postgresql:42.2.23' testRuntimeOnly 'mysql:mysql-connector-java:8.0.25' testCompileClasspath 'org.jetbrains:annotations:21.0.1' From e2cb908c5f67180d4ee7ba5bd39ab44fcc5a3cc5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Aug 2021 22:00:55 +0000 Subject: [PATCH 2027/5615] Bump testcontainers from 1.15.3 to 1.16.0 in /core Bumps [testcontainers](https://github.com/testcontainers/testcontainers-java) from 1.15.3 to 1.16.0. - [Release notes](https://github.com/testcontainers/testcontainers-java/releases) - [Changelog](https://github.com/testcontainers/testcontainers-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-java/compare/1.15.3...1.16.0) --- updated-dependencies: - dependency-name: org.testcontainers:testcontainers dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index d7ccb9364f8..3ea1a33a7e3 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -95,7 +95,7 @@ configurations.all { } dependencies { - baseline 'org.testcontainers:testcontainers:1.15.3', { + baseline 'org.testcontainers:testcontainers:1.16.0', { exclude group: "*", module: "*" } From a6f300c4bb1ec0af91f722a14701c68a11c504a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Aug 2021 22:00:57 +0000 Subject: [PATCH 2028/5615] Bump aws-java-sdk-logs from 1.12.16 to 1.12.37 in /modules/localstack Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.12.16 to 1.12.37. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.16...1.12.37) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-logs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 031a601bdc9..21652d15fa6 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,7 +6,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.16' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.11.1030' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.19' - testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.16' + testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.37' testImplementation 'software.amazon.awssdk:s3:2.16.97' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From d489acff1bd25b95aadc0c8ef4188d1c971ef2d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Aug 2021 22:00:59 +0000 Subject: [PATCH 2029/5615] Bump slf4j-simple from 1.7.30 to 1.7.32 in /examples Bumps [slf4j-simple](https://github.com/qos-ch/slf4j) from 1.7.30 to 1.7.32. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_1.7.30...v_1.7.32) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-simple dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/kafka-cluster/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index 79c14941c11..2a4650930a4 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -13,5 +13,5 @@ dependencies { testImplementation 'org.apache.kafka:kafka-clients:2.8.0' testImplementation 'org.assertj:assertj-core:3.20.2' testImplementation 'com.google.guava:guava:23.0' - testImplementation 'org.slf4j:slf4j-simple:1.7.30' + testImplementation 'org.slf4j:slf4j-simple:1.7.32' } From f5f0dc65f5b872c60cccfc20911a39b89e39b03a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Aug 2021 22:01:01 +0000 Subject: [PATCH 2030/5615] Bump jedis from 3.6.1 to 3.6.3 in /core Bumps [jedis](https://github.com/redis/jedis) from 3.6.1 to 3.6.3. - [Release notes](https://github.com/redis/jedis/releases) - [Commits](https://github.com/redis/jedis/compare/jedis-3.6.1...v3.6.3) --- updated-dependencies: - dependency-name: redis.clients:jedis dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index d7ccb9364f8..8b4abdb11c4 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -138,7 +138,7 @@ dependencies { } testImplementation 'org.apache.httpcomponents:httpclient:4.5.9' - testImplementation 'redis.clients:jedis:3.6.1' + testImplementation 'redis.clients:jedis:3.6.3' testImplementation 'com.rabbitmq:amqp-client:5.12.0' testImplementation 'org.mongodb:mongo-java-driver:3.12.7' From 381fa285b6f296c76fb2d7a533a5666bec8db91d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Aug 2021 22:01:07 +0000 Subject: [PATCH 2031/5615] Bump postgresql from 42.2.22 to 42.2.23 in /examples Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.22 to 42.2.23. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/REL42.2.23/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.22...REL42.2.23) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index c5706da277a..2bb27ae83cf 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' implementation 'com.squareup.okhttp3:okhttp:4.9.1' implementation 'org.json:json:20210307' - testImplementation 'org.postgresql:postgresql:42.2.22' + testImplementation 'org.postgresql:postgresql:42.2.23' testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.testcontainers:postgresql' } From b3f10668ec911d55dd16a2b420f13301affc8a60 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Aug 2021 22:01:09 +0000 Subject: [PATCH 2032/5615] Bump org.jetbrains.kotlin.plugin.spring in /examples Bumps org.jetbrains.kotlin.plugin.spring from 1.5.20 to 1.5.21. --- updated-dependencies: - dependency-name: org.jetbrains.kotlin.plugin.spring dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 7e832956303..db7e4ebe957 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,7 +1,7 @@ plugins { id("org.springframework.boot") version "2.5.2" id("org.jetbrains.kotlin.jvm") version "1.5.10" - id("org.jetbrains.kotlin.plugin.spring") version "1.5.20" + id("org.jetbrains.kotlin.plugin.spring") version "1.5.21" } apply plugin: 'io.spring.dependency-management' From ed781dd45ecebb21b11de85d3ff9a79be66fe912 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Aug 2021 08:48:23 +0000 Subject: [PATCH 2033/5615] Bump aws-java-sdk-sqs from 1.12.19 to 1.12.37 in /modules/localstack Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.12.19 to 1.12.37. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.19...1.12.37) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-sqs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 20c2f18a968..3d250f86087 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.37' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.37' - testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.19' + testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.37' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.37' testImplementation 'software.amazon.awssdk:s3:2.16.97' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From 62c9be485de33406a121f541663ee192f79f1d22 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Aug 2021 08:48:23 +0000 Subject: [PATCH 2034/5615] Bump tomcat-jdbc from 10.0.7 to 10.0.8 in /modules/jdbc-test Bumps tomcat-jdbc from 10.0.7 to 10.0.8. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 17c44383f74..32b7429f1b6 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -14,7 +14,7 @@ dependencies { exclude(group: "net.java.dev.jna", module: "jna") } - api 'org.apache.tomcat:tomcat-jdbc:10.0.7' + api 'org.apache.tomcat:tomcat-jdbc:10.0.8' api 'org.vibur:vibur-dbcp:25.0' api 'mysql:mysql-connector-java:8.0.26' } From 226b5a0dcd81d95f6a998b78c101b5dd8b777e6c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Aug 2021 08:48:24 +0000 Subject: [PATCH 2035/5615] Bump s3 from 2.16.97 to 2.17.9 in /modules/localstack Bumps s3 from 2.16.97 to 2.17.9. --- updated-dependencies: - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 20c2f18a968..540ba35008d 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.37' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.19' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.37' - testImplementation 'software.amazon.awssdk:s3:2.16.97' + testImplementation 'software.amazon.awssdk:s3:2.17.9' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 8272e80ed53951c169b67cec272f83a799e556e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Aug 2021 08:48:34 +0000 Subject: [PATCH 2036/5615] Bump commons-compress from 1.20 to 1.21 in /core Bumps commons-compress from 1.20 to 1.21. --- updated-dependencies: - dependency-name: org.apache.commons:commons-compress dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index ca1fde00aae..7d62a9fb153 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -103,7 +103,7 @@ dependencies { api 'org.slf4j:slf4j-api:1.7.32' compileOnly 'org.jetbrains:annotations:21.0.1' testCompileClasspath 'org.jetbrains:annotations:21.0.1' - api 'org.apache.commons:commons-compress:1.20' + api 'org.apache.commons:commons-compress:1.21' api ('org.rnorth.duct-tape:duct-tape:1.0.8') { exclude(group: 'org.jetbrains', module: 'annotations') } From 97d0959b50dbad55ff1152f978da1b061d4bf660 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Aug 2021 08:48:35 +0000 Subject: [PATCH 2037/5615] Bump logback-classic from 1.2.3 to 1.2.5 in /examples Bumps logback-classic from 1.2.3 to 1.2.5. --- updated-dependencies: - dependency-name: ch.qos.logback:logback-classic dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/disque-job-queue/build.gradle | 2 +- examples/linked-container/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index 04d8f249511..1c01bc73a2e 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -12,7 +12,7 @@ dependencies { implementation 'biz.paluch.redis:spinach:0.3' implementation 'com.google.code.gson:gson:2.8.7' implementation 'com.google.guava:guava:23.0' - testImplementation 'ch.qos.logback:logback-classic:1.2.3' + testImplementation 'ch.qos.logback:logback-classic:1.2.5' testImplementation 'org.mockito:mockito-all:1.10.19' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 8169ea8cf12..ff1b2995e02 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'com.squareup.okhttp3:okhttp:4.9.1' implementation 'org.json:json:20210307' testImplementation 'org.postgresql:postgresql:42.2.23' - testImplementation 'ch.qos.logback:logback-classic:1.2.3' + testImplementation 'ch.qos.logback:logback-classic:1.2.5' testImplementation 'org.testcontainers:postgresql' } diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 56dab9c4951..aae31424f66 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -12,7 +12,7 @@ dependencies { implementation 'com.google.code.gson:gson:2.8.7' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' - testImplementation 'ch.qos.logback:logback-classic:1.2.3' + testImplementation 'ch.qos.logback:logback-classic:1.2.5' testImplementation 'org.testng:testng:7.4.0' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 771f55551ea..5da9e48a8e0 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -13,6 +13,6 @@ dependencies { implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'junit:junit:4.13.2' - testImplementation 'ch.qos.logback:logback-classic:1.2.3' + testImplementation 'ch.qos.logback:logback-classic:1.2.5' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index a806f483938..4635eceff7e 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -13,7 +13,7 @@ dependencies { implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.32' - testImplementation 'ch.qos.logback:logback-classic:1.2.3' + testImplementation 'ch.qos.logback:logback-classic:1.2.5' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From e58faa5c80bd0a242888d4911ea4ed73b22885da Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Aug 2021 08:48:35 +0000 Subject: [PATCH 2038/5615] Bump postgresql from 42.2.22 to 42.2.23 in /modules/junit-jupiter Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.22 to 42.2.23. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/REL42.2.23/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.22...REL42.2.23) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index a3e271b0d59..abd1ae9feeb 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'org.assertj:assertj-core:3.20.2' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.7.2' - testRuntimeOnly 'org.postgresql:postgresql:42.2.22' + testRuntimeOnly 'org.postgresql:postgresql:42.2.23' testRuntimeOnly 'mysql:mysql-connector-java:8.0.26' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.2' } From b5331f48342521b7c0b1f27135b275835a04c44d Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Mon, 2 Aug 2021 12:27:52 +0200 Subject: [PATCH 2039/5615] Fix failing `PublicBinaryAPITest` for `DynamicPollInterval` (#4353) Also change `jarFileTest` to take the shadowJar into consideration as input for the cache key. --- core/build.gradle | 1 + .../jarFileTest/java/org/testcontainers/PublicBinaryAPITest.java | 1 + 2 files changed, 2 insertions(+) diff --git a/core/build.gradle b/core/build.gradle index ca1fde00aae..824c1bb0c4e 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -50,6 +50,7 @@ task jarFileTest(type: Test) { testClassesDirs = sourceSets.jarFileTest.output.classesDirs classpath = sourceSets.jarFileTest.runtimeClasspath + file(shadowJar.outputs.files.singleFile) // input for correct caching systemProperty("jarFile", shadowJar.outputs.files.singleFile) dependsOn(shadowJar) diff --git a/core/src/jarFileTest/java/org/testcontainers/PublicBinaryAPITest.java b/core/src/jarFileTest/java/org/testcontainers/PublicBinaryAPITest.java index 6a5a766d24e..208c8702654 100644 --- a/core/src/jarFileTest/java/org/testcontainers/PublicBinaryAPITest.java +++ b/core/src/jarFileTest/java/org/testcontainers/PublicBinaryAPITest.java @@ -93,6 +93,7 @@ public void setUp() { case "org/testcontainers/dockerclient/UnixSocketClientProviderStrategy": case "org/testcontainers/dockerclient/DockerClientProviderStrategy": case "org/testcontainers/dockerclient/WindowsClientProviderStrategy": + case "org/testcontainers/utility/DynamicPollInterval": Assume.assumeTrue(false); } } From 12b41272fdf9983dc6e9aed9b3ffd9b617c1f499 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 3 Aug 2021 11:52:38 +0100 Subject: [PATCH 2040/5615] Upgrade ryuk image to 0.3.2 (#4359) --- .../main/java/org/testcontainers/utility/ResourceReaper.java | 2 +- .../utility/TestcontainersConfigurationTest.java | 4 ++-- docs/features/configuration.md | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index 75c6e209960..ca657675da4 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -95,7 +95,7 @@ public static String start(String hostIpAddress, DockerClient client) { @SneakyThrows(InterruptedException.class) public static String start(DockerClient client) { String ryukImage = ImageNameSubstitutor.instance() - .apply(DockerImageName.parse("testcontainers/ryuk:0.3.1")) + .apply(DockerImageName.parse("testcontainers/ryuk:0.3.2")) .asCanonicalNameString(); DockerClientFactory.instance().checkAndPullImage(client, ryukImage); diff --git a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java index ff56f62e4e4..cbb36baf580 100644 --- a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java +++ b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java @@ -210,8 +210,8 @@ public void shouldReadReuseFromEnvironment() { @Test public void shouldTrimImageNames() { - userProperties.setProperty("ryuk.container.image", " testcontainers/ryuk:0.3.1 "); - assertEquals("trailing whitespace was not removed from image name property", "testcontainers/ryuk:0.3.1",newConfig().getRyukImage()); + userProperties.setProperty("ryuk.container.image", " testcontainers/ryuk:0.3.2 "); + assertEquals("trailing whitespace was not removed from image name property", "testcontainers/ryuk:0.3.2",newConfig().getRyukImage()); } private TestcontainersConfiguration newConfig() { diff --git a/docs/features/configuration.md b/docs/features/configuration.md index ba4487f8722..821d0cc7629 100644 --- a/docs/features/configuration.md +++ b/docs/features/configuration.md @@ -47,7 +47,7 @@ It takes a couple of seconds, but if you want to speed up your tests, you can di Testcontainers uses public Docker images to perform different actions like startup checks, VNC recording and others. Some companies disallow the usage of Docker Hub, but you can override `*.image` properties with your own images from your private registry to workaround that. -> **ryuk.container.image = testcontainers/ryuk:0.3.1** +> **ryuk.container.image = testcontainers/ryuk:0.3.2** > Performs fail-safe cleanup of containers, and always required (unless [Ryuk is disabled](#disabling-ryuk)) > **tinyimage.container.image = alpine:3.14** @@ -74,7 +74,7 @@ Some companies disallow the usage of Docker Hub, but you can override `*.image` ## Customizing Ryuk resource reaper -> **ryuk.container.image = testcontainers/ryuk:0.3.1** +> **ryuk.container.image = testcontainers/ryuk:0.3.2** > The resource reaper is responsible for container removal and automatic cleanup of dead containers at JVM shutdown > **ryuk.container.privileged = false** From 611ebf5313a8148f4d7233227f2c0f1abaa5290a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Aug 2021 10:52:55 +0000 Subject: [PATCH 2041/5615] Bump mysql-connector-java from 8.0.25 to 8.0.26 in /modules/spock Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.25 to 8.0.26. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.25...8.0.26) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 1e8e8f88503..a883d6027d0 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -16,7 +16,7 @@ dependencies { testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' testRuntimeOnly 'org.postgresql:postgresql:42.2.23' - testRuntimeOnly 'mysql:mysql-connector-java:8.0.25' + testRuntimeOnly 'mysql:mysql-connector-java:8.0.26' testCompileClasspath 'org.jetbrains:annotations:21.0.1' } From c6f0eaa12d0b99d3ed5005b3220fedfbdcee3a28 Mon Sep 17 00:00:00 2001 From: pioorg Date: Tue, 3 Aug 2021 13:00:27 +0200 Subject: [PATCH 2042/5615] Remove accidental overwrite of user properties (#4311) `userProperties` shall be saved only after successful update of config file. Co-authored-by: Sergei Egorov --- .../org/testcontainers/utility/TestcontainersConfiguration.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index fddeec9c9b7..fd189a0ad59 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -306,8 +306,6 @@ public boolean updateUserConfig(@NonNull String prop, @NonNull String value) { return false; } - userProperties.setProperty(prop, value); - USER_CONFIG_FILE.createNewFile(); try (OutputStream outputStream = new FileOutputStream(USER_CONFIG_FILE)) { userProperties.store(outputStream, "Modified by Testcontainers"); From 1699e4708b15fcbf26b80f65fbbee48fa0b0c4d0 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 6 Aug 2021 14:49:53 +0200 Subject: [PATCH 2043/5615] Add japicmp to every module (#4364) --- build.gradle | 18 ++++++++++++++++++ core/build.gradle | 36 +----------------------------------- gradle/japicmp.gradle | 29 +++++++++++++++++++++++++++++ modules/kafka/build.gradle | 9 +++++++++ 4 files changed, 57 insertions(+), 35 deletions(-) create mode 100644 gradle/japicmp.gradle diff --git a/build.gradle b/build.gradle index 98b9b5a30bc..723c429271d 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,18 @@ +buildscript { + repositories { + mavenCentral() + } + + dependencies { + // https://github.com/melix/japicmp-gradle-plugin/issues/36 + classpath 'com.google.guava:guava:30.1.1-jre' + } +} + plugins { id 'io.franzbecker.gradle-lombok' version '4.0.0' id 'com.github.johnrengelman.shadow' version '7.0.0' + id 'me.champeau.gradle.japicmp' version '0.2.9' apply false } apply from: "$rootDir/gradle/ci-support.gradle" @@ -46,6 +58,12 @@ subprojects { // specific modules should be excluded from publication if ( ! ["test-support", "jdbc-test"].contains(it.name) && !it.path.startsWith(":docs:") && it != project(":docs") ) { apply from: "$rootDir/gradle/publishing.gradle" + + if (it.name != "bom") { + apply plugin: "me.champeau.gradle.japicmp" + tasks.register('japicmp', me.champeau.gradle.japicmp.JapicmpTask) + apply from: "$rootDir/gradle/japicmp.gradle" + } } test { diff --git a/core/build.gradle b/core/build.gradle index 824c1bb0c4e..00d9cfda688 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -1,18 +1,3 @@ -buildscript { - repositories { - jcenter() - } - - dependencies { - // https://github.com/melix/japicmp-gradle-plugin/issues/36 - classpath 'com.google.guava:guava:30.1.1-jre' - } -} - -plugins { - id 'me.champeau.gradle.japicmp' version '0.2.9' -} - apply plugin: 'com.github.johnrengelman.shadow' description = "Testcontainers Core" @@ -24,7 +9,6 @@ sourceSets { idea.module.testSourceDirs += sourceSets.jarFileTest.allSource.srcDirs configurations { - baseline shaded [api, compileOnly, testCompile]*.extendsFrom shaded } @@ -57,17 +41,7 @@ task jarFileTest(type: Test) { } project.tasks.check.dependsOn(jarFileTest) -task japicmp(type: me.champeau.gradle.japicmp.JapicmpTask) { - dependsOn(tasks.shadowJar) - - oldClasspath = configurations.baseline - newClasspath = shadowJar.outputs.files - ignoreMissingClasses = true - - accessModifier = "protected" - failOnModification = true - failOnSourceIncompatibility = true - +tasks.japicmp { packageExcludes = [ "com.github.dockerjava.*", "org.testcontainers.shaded.*", @@ -78,14 +52,6 @@ task japicmp(type: me.champeau.gradle.japicmp.JapicmpTask) { methodExcludes = [] fieldExcludes = [] - - onlyBinaryIncompatibleModified = true - htmlOutputFile = file("$buildDir/reports/japi.html") -} -// do not run on Windows by default -// TODO investigate zip issue on Windows -if (!org.gradle.internal.os.OperatingSystem.current().isWindows()) { - project.tasks.check.dependsOn(japicmp) } configurations.all { diff --git a/gradle/japicmp.gradle b/gradle/japicmp.gradle new file mode 100644 index 00000000000..58fbec9c0b7 --- /dev/null +++ b/gradle/japicmp.gradle @@ -0,0 +1,29 @@ +configurations { + baseline +} + +dependencies { + baseline "org.testcontainers:${project.name}:1.16.0", { + exclude group: "*", module: "*" + } +} + +tasks.japicmp { + dependsOn(tasks.shadowJar) + + oldClasspath = configurations.baseline + newClasspath = shadowJar.outputs.files + ignoreMissingClasses = true + + accessModifier = "protected" + failOnModification = true + failOnSourceIncompatibility = true + + onlyBinaryIncompatibleModified = true + htmlOutputFile = file("$buildDir/reports/japi.html") +} +// do not run on Windows by default +// TODO investigate zip issue on Windows +if (!org.gradle.internal.os.OperatingSystem.current().isWindows()) { + project.tasks.check.dependsOn(japicmp) +} diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index c83859e0c20..f7e9acda9a0 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -1,5 +1,14 @@ description = "Testcontainers :: Kafka" +tasks.japicmp { + // TODO clean after 1.17.0 is released + methodExcludes = [ + // Overrides removed, not a breaking change per se + "org.testcontainers.containers.KafkaContainer#containerIsStarting(com.github.dockerjava.api.command.InspectContainerResponse,boolean)", + "org.testcontainers.containers.KafkaContainer#doStart()", + ] +} + dependencies { api project(':testcontainers') From ef921c3c60d1c74d0eab9f9fed85fcd1ee8bf228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 12 Aug 2021 13:26:10 +0200 Subject: [PATCH 2044/5615] docs: add Elastic as user (#4369) * docs: adds Elastic as a user * docs: wording --- docs/index.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/index.md b/docs/index.md index 16f61899947..8894e6bec73 100644 --- a/docs/index.md +++ b/docs/index.md @@ -173,6 +173,8 @@ A huge thank you to our sponsors: * [CloudBees](https://www.cloudbees.com/) - Integration testing of products, including but not limited to database and AWS/Localstack integration testing. * [Jenkins](https://www.jenkins.io/) - Integration testing of multiple plugins and the Trilead SSH2 fork maintained by the Jenkins community ([query](https://github.com/search?l=Maven+POM&q=org%3Ajenkinsci+testcontainers&type=Code)). +* [Elastic](https://www.elastic.co) - Integration testing of the Java APM agent + ## License From 8638330ba6b3d53ce8e72ac898b8aef7d5f3fc57 Mon Sep 17 00:00:00 2001 From: Sankeerth Date: Tue, 17 Aug 2021 03:24:03 -0400 Subject: [PATCH 2045/5615] Adding Alkira to list of TC users (#4370) --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index 8894e6bec73..af5bb4edcdb 100644 --- a/docs/index.md +++ b/docs/index.md @@ -174,6 +174,7 @@ A huge thank you to our sponsors: * [Jenkins](https://www.jenkins.io/) - Integration testing of multiple plugins and the Trilead SSH2 fork maintained by the Jenkins community ([query](https://github.com/search?l=Maven+POM&q=org%3Ajenkinsci+testcontainers&type=Code)). * [Elastic](https://www.elastic.co) - Integration testing of the Java APM agent +* [Alkira](https://www.alkira.com/) - Testing of multiple micro-services using Kafka, PostgreSQL, Apache Zookeeper, Etcd and so on. From 5b660560edbacd6cbbb768e112ce0ca619693a33 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 18 Aug 2021 17:18:28 +0200 Subject: [PATCH 2046/5615] Update mkdocs.yml to 1.16.0 --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index f3cb11f7fe6..9929f5292c2 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -96,4 +96,4 @@ nav: - contributing_docs.md - bounty.md extra: - latest_version: 1.15.3 + latest_version: 1.16.0 From 99b91b89b6ee3f8f0e9545e86d9f0744b301db30 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Thu, 19 Aug 2021 17:03:28 +0200 Subject: [PATCH 2047/5615] Update `OracleContainer` and re-activate on CI (#4298) Will now use "gvenzl/oracle-xe:18.4.0-slim" by default. Co-authored-by: Piotr Co-authored-by: Sergei Egorov --- .../utility/TestcontainersConfiguration.java | 5 +- .../containers/OracleContainer.java | 48 +++++++++++-------- .../containers/OracleContainerProvider.java | 9 ++-- .../junit/oracle/SimpleOracleTest.java | 13 +++-- 4 files changed, 47 insertions(+), 28 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index fd189a0ad59..cbbe92dd76d 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -24,7 +24,6 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.Map; -import java.util.Objects; import java.util.Optional; import java.util.Properties; import java.util.concurrent.atomic.AtomicReference; @@ -61,6 +60,7 @@ public class TestcontainersConfiguration { private static final String PULSAR_IMAGE = "apachepulsar/pulsar"; private static final String LOCALSTACK_IMAGE = "localstack/localstack"; private static final String SSHD_IMAGE = "testcontainers/sshd"; + private static final String ORACLE_IMAGE = "gvenzl/oracle-xe"; private static final ImmutableMap CONTAINER_MAPPING = ImmutableMap.builder() .put(DockerImageName.parse(AMBASSADOR_IMAGE), "ambassador.container.image") @@ -73,6 +73,7 @@ public class TestcontainersConfiguration { .put(DockerImageName.parse(PULSAR_IMAGE), "pulsar.container.image") .put(DockerImageName.parse(LOCALSTACK_IMAGE), "localstack.container.image") .put(DockerImageName.parse(SSHD_IMAGE), "sshd.container.image") + .put(DockerImageName.parse(ORACLE_IMAGE), "oracle.container.image") .build(); @Getter(lazy = true) @@ -146,7 +147,7 @@ public String getKafkaImage() { @Deprecated public String getOracleImage() { - return getEnvVarOrProperty("oracle.container.image", null); + return getImage(ORACLE_IMAGE).asCanonicalNameString(); } @Deprecated diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java index 2573ef722e3..ccd705f5af9 100644 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java @@ -1,45 +1,39 @@ package org.testcontainers.containers; import com.google.common.collect.Sets; +import org.apache.commons.lang.StringUtils; import org.testcontainers.utility.DockerImageName; -import org.testcontainers.utility.TestcontainersConfiguration; +import java.util.Arrays; +import java.util.List; import java.util.Set; import java.util.concurrent.Future; -/** - * @author gusohal - */ public class OracleContainer extends JdbcDatabaseContainer { public static final String NAME = "oracle"; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("gvenzl/oracle-xe"); + + + static final String DEFAULT_TAG = "18.4.0-slim"; + static final String IMAGE = DEFAULT_IMAGE_NAME.getUnversionedPart(); private static final int ORACLE_PORT = 1521; private static final int APEX_HTTP_PORT = 8080; private static final int DEFAULT_STARTUP_TIMEOUT_SECONDS = 240; private static final int DEFAULT_CONNECT_TIMEOUT_SECONDS = 120; + private static final List ORACLE_SYSTEM_USERS = Arrays.asList("system", "sys"); - private String username = "system"; - private String password = "oracle"; - - private static String resolveImageName() { - String image = TestcontainersConfiguration.getInstance().getOracleImage(); - - if (image == null) { - throw new IllegalStateException("An image to use for Oracle containers must be configured. " + - "To do this, please place a file on the classpath named `testcontainers.properties`, " + - "containing `oracle.container.image=IMAGE`, where IMAGE is a suitable image name and tag."); - } - return image; - } + private String username = "test"; + private String password = "test"; /** * @deprecated use {@link OracleContainer(DockerImageName)} instead */ @Deprecated public OracleContainer() { - this(resolveImageName()); + this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); } public OracleContainer(String dockerImageName) { @@ -74,7 +68,7 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return "jdbc:oracle:thin:" + getUsername() + "/" + getPassword() + "@" + getHost() + ":" + getOraclePort() + ":" + getSid(); + return "jdbc:oracle:thin:" + getUsername() + "/" + getPassword() + "@" + getHost() + ":" + getOraclePort() + "/xepdb1"; } @Override @@ -89,12 +83,21 @@ public String getPassword() { @Override public OracleContainer withUsername(String username) { + if (StringUtils.isEmpty(username)) { + throw new IllegalArgumentException("Username cannot be null or empty"); + } + if (ORACLE_SYSTEM_USERS.contains(username.toLowerCase())) { + throw new IllegalArgumentException("Username cannot be one of " + ORACLE_SYSTEM_USERS); + } this.username = username; return self(); } @Override public OracleContainer withPassword(String password) { + if (StringUtils.isEmpty(password)) { + throw new IllegalArgumentException("Password cannot be null or empty"); + } this.password = password; return self(); } @@ -122,4 +125,11 @@ public Integer getWebPort() { public String getTestQueryString() { return "SELECT 1 FROM DUAL"; } + + @Override + protected void configure() { + withEnv("ORACLE_PASSWORD", password); + withEnv("APP_USER", username); + withEnv("APP_USER_PASSWORD", password); + } } diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainerProvider.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainerProvider.java index 853ffdaf17a..a298ae23359 100644 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainerProvider.java +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainerProvider.java @@ -1,5 +1,7 @@ package org.testcontainers.containers; +import org.testcontainers.utility.DockerImageName; + /** * Factory for Oracle containers. */ @@ -11,15 +13,14 @@ public boolean supports(String databaseType) { @Override public JdbcDatabaseContainer newInstance() { - return new OracleContainer(); + return newInstance(OracleContainer.DEFAULT_TAG); } @Override public JdbcDatabaseContainer newInstance(String tag) { if (tag != null) { - throw new UnsupportedOperationException("Oracle database tag should be set in the configured image name"); + return new OracleContainer(DockerImageName.parse(OracleContainer.IMAGE).withTag(tag)); } - - return new OracleContainer(); + return newInstance(); } } diff --git a/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java b/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java index 2415fed11f4..7d026924442 100644 --- a/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java +++ b/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java @@ -1,21 +1,28 @@ package org.testcontainers.junit.oracle; -import org.junit.Ignore; import org.junit.Test; import org.testcontainers.containers.OracleContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; +import org.testcontainers.utility.DockerImageName; import java.sql.ResultSet; import java.sql.SQLException; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -@Ignore public class SimpleOracleTest extends AbstractContainerDatabaseTest { + public static final DockerImageName ORACLE_DOCKER_IMAGE_NAME = DockerImageName.parse("gvenzl/oracle-xe:18.4.0-slim"); + @Test public void testSimple() throws SQLException { - try (OracleContainer oracle = new OracleContainer()) { + + try ( + OracleContainer oracle = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME) + .withUsername("baz") + .withPassword("bar") + ) { + oracle.start(); ResultSet resultSet = performQuery(oracle, "SELECT 1 FROM dual"); int resultSetInt = resultSet.getInt(1); From b1cdaf97c0ad110faab5c21cbd3e6412d5cff5b3 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 30 Aug 2021 13:57:14 +0200 Subject: [PATCH 2048/5615] Tune Cassandra options for faster startup (#4309) * Tune Cassandra options for faster startup * remove `setStartupAttempts` --- .../testcontainers/containers/CassandraContainer.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java b/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java index 597b1c9b017..e3ae084cb8a 100644 --- a/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java +++ b/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java @@ -58,8 +58,15 @@ public CassandraContainer(DockerImageName dockerImageName) { dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); addExposedPort(CQL_PORT); - setStartupAttempts(3); this.enableJmxReporting = false; + + withEnv("CASSANDRA_SNITCH", "GossipingPropertyFileSnitch"); + withEnv( + "JVM_OPTS", + "-Dcassandra.skip_wait_for_gossip_to_settle=0 -Dcassandra.initial_token=0" + ); + withEnv("HEAP_NEWSIZE", "128M"); + withEnv("MAX_HEAP_SIZE", "1024M"); } @Override From 9f77a6dff067df4f01b7edd91afe4444a9c9b1d3 Mon Sep 17 00:00:00 2001 From: pioorg Date: Wed, 1 Sep 2021 22:06:21 +0200 Subject: [PATCH 2049/5615] Shorten fallback to another strategy (#4386) * Removed double check of configured Docker client strategy. * Shortened the ping timeout (when choosing docker client) to 5 seconds. * Added `client.ping.timeout` configuration option. * Made relevant change in the docs. --- .../dockerclient/DockerClientProviderStrategy.java | 6 ++++-- .../testcontainers/utility/TestcontainersConfiguration.java | 4 ++++ docs/features/configuration.md | 5 +++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index 248e40790aa..5c797b3b782 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -111,10 +111,11 @@ public static DockerClientProviderStrategy getFirstValidStrategy(List configurationFailures = new ArrayList<>(); + String dockerClientStrategyClassName = TestcontainersConfiguration.getInstance().getDockerClientStrategyClassName(); return Stream .concat( Stream - .of(TestcontainersConfiguration.getInstance().getDockerClientStrategyClassName()) + .of(dockerClientStrategyClassName) .filter(Objects::nonNull) .flatMap(it -> { try { @@ -136,6 +137,7 @@ public static DockerClientProviderStrategy getFirstValidStrategy(List log.info("Loaded {} from ~/.testcontainers.properties, will try it first", strategy.getClass().getName())), strategies .stream() + .filter(it -> !it.getClass().getCanonicalName().equals(dockerClientStrategyClassName)) .filter(DockerClientProviderStrategy::isApplicable) .sorted(Comparator.comparing(DockerClientProviderStrategy::getPriority).reversed()) ) @@ -145,7 +147,7 @@ public static DockerClientProviderStrategy getFirstValidStrategy(List { + info = Unreliables.retryUntilSuccess(TestcontainersConfiguration.getInstance().getClientPingTimeout(), TimeUnit.SECONDS, () -> { return strategy.PING_RATE_LIMITER.getWhenReady(() -> { log.debug("Pinging docker daemon..."); return dockerClient.infoCmd().exec(); diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index cbbe92dd76d..91d42e45ba0 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -210,6 +210,10 @@ public String getImageSubstitutorClassName() { return getEnvVarOrProperty("image.substitutor", null); } + public Integer getClientPingTimeout() { + return Integer.parseInt(getEnvVarOrProperty("client.ping.timeout", "5")); + } + @Nullable @Contract("_, !null, _ -> !null") private String getConfigurable(@NotNull final String propertyName, @Nullable final String defaultValue, Properties... propertiesSources) { diff --git a/docs/features/configuration.md b/docs/features/configuration.md index 821d0cc7629..73876a51483 100644 --- a/docs/features/configuration.md +++ b/docs/features/configuration.md @@ -94,6 +94,11 @@ but does not allow starting privileged containers, you can turn off the Ryuk con > **pull.pause.timeout = 30** > By default Testcontainers will abort the pull of an image if the pull appears stalled (no data transferred) for longer than this duration (in seconds). +## Customizing client ping behaviour + +> **client.ping.timeout = 5** +> Specifies for how long Testcontainers will try to connect to the Docker client to obtain valid info about the client before giving up and trying next strategy, if applicable (in seconds). + ## Customizing Docker host detection Testcontainers will attempt to detect the Docker environment and configure everything to work automatically. From fd9d57387c01a83e448591de8a900e27994454e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:00:30 +0000 Subject: [PATCH 2050/5615] Bump tomcat-jdbc from 10.0.8 to 10.0.10 in /modules/jdbc-test Bumps tomcat-jdbc from 10.0.8 to 10.0.10. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 32b7429f1b6..8145855684f 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -14,7 +14,7 @@ dependencies { exclude(group: "net.java.dev.jna", module: "jna") } - api 'org.apache.tomcat:tomcat-jdbc:10.0.8' + api 'org.apache.tomcat:tomcat-jdbc:10.0.10' api 'org.vibur:vibur-dbcp:25.0' api 'mysql:mysql-connector-java:8.0.26' } From eccb15945cb0903f4fc6ea279a5d3de6648e868f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:00:31 +0000 Subject: [PATCH 2051/5615] Bump jedis from 3.6.3 to 3.7.0 in /modules/junit-jupiter Bumps [jedis](https://github.com/redis/jedis) from 3.6.3 to 3.7.0. - [Release notes](https://github.com/redis/jedis/releases) - [Commits](https://github.com/redis/jedis/compare/v3.6.3...v3.7.0) --- updated-dependencies: - dependency-name: redis.clients:jedis dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index a3e271b0d59..0ece8912384 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -7,7 +7,7 @@ dependencies { testImplementation project(':mysql') testImplementation project(':postgresql') testImplementation 'com.zaxxer:HikariCP:4.0.3' - testImplementation 'redis.clients:jedis:3.6.3' + testImplementation 'redis.clients:jedis:3.7.0' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' testImplementation ('org.mockito:mockito-core:3.11.2') { exclude(module: 'hamcrest-core') From b976691f0e8bba50555a68d7e003950d087b6016 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:00:31 +0000 Subject: [PATCH 2052/5615] Bump annotations from 21.0.1 to 22.0.0 in /modules/mysql Bumps [annotations](https://github.com/JetBrains/java-annotations) from 21.0.1 to 22.0.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/21.0.1...22.0.0) --- updated-dependencies: - dependency-name: org.jetbrains:annotations dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index db2deac2d10..e465acaaee3 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -15,5 +15,5 @@ dependencies { testImplementation testFixtures(project(':r2dbc')) testImplementation 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' - compileOnly 'org.jetbrains:annotations:21.0.1' + compileOnly 'org.jetbrains:annotations:22.0.0' } From a7e047f81bbd0a0897a87ead663123774068ce22 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:00:31 +0000 Subject: [PATCH 2053/5615] Bump mariadb-java-client from 2.7.3 to 2.7.4 in /modules/mariadb Bumps [mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 2.7.3 to 2.7.4. - [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases) - [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md) - [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/2.7.3...2.7.4) --- updated-dependencies: - dependency-name: org.mariadb.jdbc:mariadb-java-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index e9080ce6131..037573f6143 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'org.mariadb:r2dbc-mariadb:1.0.2' testImplementation project(':jdbc-test') - testImplementation 'org.mariadb.jdbc:mariadb-java-client:2.7.3' + testImplementation 'org.mariadb.jdbc:mariadb-java-client:2.7.4' testImplementation testFixtures(project(':r2dbc')) testImplementation 'org.mariadb:r2dbc-mariadb:1.0.2' From cdd1033d3ea98fa06a52009f9947610154ace342 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:00:31 +0000 Subject: [PATCH 2054/5615] Bump annotations from 21.0.1 to 22.0.0 in /modules/jdbc Bumps [annotations](https://github.com/JetBrains/java-annotations) from 21.0.1 to 22.0.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/21.0.1...22.0.0) --- updated-dependencies: - dependency-name: org.jetbrains:annotations dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 4f6c6608b4d..c7ff4a2e76f 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':database-commons') testImplementation project(':test-support') - compileOnly 'org.jetbrains:annotations:21.0.1' + compileOnly 'org.jetbrains:annotations:22.0.0' testImplementation 'commons-dbutils:commons-dbutils:1.7' testImplementation 'org.vibur:vibur-dbcp:25.0' testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.8' From ae23f9ef82e3a9ed041d6948060d36d400b80031 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:00:31 +0000 Subject: [PATCH 2055/5615] Bump jedis from 3.6.3 to 3.7.0 in /core Bumps [jedis](https://github.com/redis/jedis) from 3.6.3 to 3.7.0. - [Release notes](https://github.com/redis/jedis/releases) - [Commits](https://github.com/redis/jedis/compare/v3.6.3...v3.7.0) --- updated-dependencies: - dependency-name: redis.clients:jedis dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 00d9cfda688..7f1a21ae28d 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -105,7 +105,7 @@ dependencies { } testImplementation 'org.apache.httpcomponents:httpclient:4.5.9' - testImplementation 'redis.clients:jedis:3.6.3' + testImplementation 'redis.clients:jedis:3.7.0' testImplementation 'com.rabbitmq:amqp-client:5.12.0' testImplementation 'org.mongodb:mongo-java-driver:3.12.9' From 713d324612d09dc5ad2be301e21cca3e31bfbb89 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:00:31 +0000 Subject: [PATCH 2056/5615] Bump annotations from 21.0.1 to 22.0.0 in /modules/spock Bumps [annotations](https://github.com/JetBrains/java-annotations) from 21.0.1 to 22.0.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/21.0.1...22.0.0) --- updated-dependencies: - dependency-name: org.jetbrains:annotations dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 1e8e8f88503..5fcdeb5a2c6 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -18,7 +18,7 @@ dependencies { testRuntimeOnly 'org.postgresql:postgresql:42.2.23' testRuntimeOnly 'mysql:mysql-connector-java:8.0.25' - testCompileClasspath 'org.jetbrains:annotations:21.0.1' + testCompileClasspath 'org.jetbrains:annotations:22.0.0' } sourceJar { From 5774925e060c3a7b9861de72dfced5c4cc9f1579 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:00:31 +0000 Subject: [PATCH 2057/5615] Bump annotations from 21.0.1 to 22.0.0 in /modules/nginx Bumps [annotations](https://github.com/JetBrains/java-annotations) from 21.0.1 to 22.0.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/21.0.1...22.0.0) --- updated-dependencies: - dependency-name: org.jetbrains:annotations dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/nginx/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/nginx/build.gradle b/modules/nginx/build.gradle index 5bfcfae910b..b8f45d394c1 100644 --- a/modules/nginx/build.gradle +++ b/modules/nginx/build.gradle @@ -2,6 +2,6 @@ description = "Testcontainers :: Nginx" dependencies { api project(':testcontainers') - compileOnly 'org.jetbrains:annotations:21.0.1' + compileOnly 'org.jetbrains:annotations:22.0.0' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From e3c118925ec47904f77285e951c013628932e5ec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:00:32 +0000 Subject: [PATCH 2058/5615] Bump annotations from 21.0.1 to 22.0.0 in /modules/selenium Bumps [annotations](https://github.com/JetBrains/java-annotations) from 21.0.1 to 22.0.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/21.0.1...22.0.0) --- updated-dependencies: - dependency-name: org.jetbrains:annotations dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/selenium/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/selenium/build.gradle b/modules/selenium/build.gradle index f91bb361775..5cff4c544b3 100644 --- a/modules/selenium/build.gradle +++ b/modules/selenium/build.gradle @@ -12,6 +12,6 @@ dependencies { testImplementation project(':nginx') testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' - compileOnly 'org.jetbrains:annotations:21.0.1' + compileOnly 'org.jetbrains:annotations:22.0.0' testCompileClasspath 'org.jetbrains:annotations:21.0.1' } From bc766614dc994e932c5b76d623b053519e249e84 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:00:32 +0000 Subject: [PATCH 2059/5615] Bump aws-java-sdk-dynamodb from 1.12.37 to 1.12.60 in /modules/dynalite Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.12.37 to 1.12.60. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.37...1.12.60) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-dynamodb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/dynalite/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 85c91332c12..4cf9b02f160 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Dynalite" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.37' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.60' testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.37' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From b86dc29bb1137a76711b41ddce6c9c9be449567f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:00:32 +0000 Subject: [PATCH 2060/5615] Bump tomcat-jdbc from 10.0.8 to 10.0.10 in /modules/jdbc Bumps tomcat-jdbc from 10.0.8 to 10.0.10. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 4f6c6608b4d..d08b9b68dd8 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -7,7 +7,7 @@ dependencies { compileOnly 'org.jetbrains:annotations:21.0.1' testImplementation 'commons-dbutils:commons-dbutils:1.7' testImplementation 'org.vibur:vibur-dbcp:25.0' - testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.8' + testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.10' testImplementation 'com.zaxxer:HikariCP-java6:2.3.13' testImplementation 'com.googlecode.junit-toolbox:junit-toolbox:2.4' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From e48e366577227395c312cc869aa64a058f2fed56 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:00:32 +0000 Subject: [PATCH 2061/5615] Bump mssql-jdbc in /modules/mssqlserver Bumps [mssql-jdbc](https://github.com/Microsoft/mssql-jdbc) from 9.3.1.jre8-preview to 9.4.0.jre8. - [Release notes](https://github.com/Microsoft/mssql-jdbc/releases) - [Changelog](https://github.com/microsoft/mssql-jdbc/blob/dev/CHANGELOG.md) - [Commits](https://github.com/Microsoft/mssql-jdbc/commits) --- updated-dependencies: - dependency-name: com.microsoft.sqlserver:mssql-jdbc dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/mssqlserver/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index 74e77d474e9..cd797a9807f 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -10,12 +10,12 @@ dependencies { compileOnly 'io.r2dbc:r2dbc-mssql:0.8.6.RELEASE' testImplementation project(':jdbc-test') - testImplementation 'com.microsoft.sqlserver:mssql-jdbc:9.3.1.jre8-preview' + testImplementation 'com.microsoft.sqlserver:mssql-jdbc:9.4.0.jre8' testImplementation project(':r2dbc') testImplementation 'io.r2dbc:r2dbc-mssql:0.8.5.RELEASE' // MSSQL's wait strategy requires the JDBC driver testImplementation testFixtures(project(':r2dbc')) - testImplementation 'com.microsoft.sqlserver:mssql-jdbc:9.3.1.jre8-preview' + testImplementation 'com.microsoft.sqlserver:mssql-jdbc:9.4.0.jre8' } From eeb375ee265b28ecd9a83763484639d0c630dfe5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:00:33 +0000 Subject: [PATCH 2062/5615] Bump java-client from 3.2.0 to 3.2.1 in /modules/couchbase Bumps [java-client](https://github.com/couchbase/couchbase-jvm-clients) from 3.2.0 to 3.2.1. - [Release notes](https://github.com/couchbase/couchbase-jvm-clients/releases) - [Commits](https://github.com/couchbase/couchbase-jvm-clients/compare/java-client-3.2.0...java-client-3.2.1) --- updated-dependencies: - dependency-name: com.couchbase.client:java-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 823b88a983e..99cf927173e 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Couchbase" dependencies { api project(':testcontainers') - testImplementation 'com.couchbase.client:java-client:3.2.0' + testImplementation 'com.couchbase.client:java-client:3.2.1' testImplementation 'org.awaitility:awaitility:4.1.0' } From 1a754ff370c5fece493464d49e19caf158141418 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:00:33 +0000 Subject: [PATCH 2063/5615] Bump annotations from 21.0.1 to 22.0.0 in /modules/postgresql Bumps [annotations](https://github.com/JetBrains/java-annotations) from 21.0.1 to 22.0.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/21.0.1...22.0.0) --- updated-dependencies: - dependency-name: org.jetbrains:annotations dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 410706429d5..1842a0e7dd7 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -16,5 +16,5 @@ dependencies { testImplementation testFixtures(project(':r2dbc')) testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.5.RELEASE' - compileOnly 'org.jetbrains:annotations:21.0.1' + compileOnly 'org.jetbrains:annotations:22.0.0' } From 4955e7ad871c8fb2fd623f2832a914c94fbf6937 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:00:35 +0000 Subject: [PATCH 2064/5615] Bump mockito-core from 3.11.2 to 3.12.4 in /modules/junit-jupiter Bumps [mockito-core](https://github.com/mockito/mockito) from 3.11.2 to 3.12.4. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.11.2...v3.12.4) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index a3e271b0d59..6c520397d5a 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,7 +9,7 @@ dependencies { testImplementation 'com.zaxxer:HikariCP:4.0.3' testImplementation 'redis.clients:jedis:3.6.3' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' - testImplementation ('org.mockito:mockito-core:3.11.2') { + testImplementation ('org.mockito:mockito-core:3.12.4') { exclude(module: 'hamcrest-core') } testImplementation 'org.assertj:assertj-core:3.20.2' From 6f8bebfcb260f6c26994c9b7dfa4fa9206bfca20 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:00:36 +0000 Subject: [PATCH 2065/5615] Bump mockito-core from 3.11.2 to 3.12.4 in /core Bumps [mockito-core](https://github.com/mockito/mockito) from 3.11.2 to 3.12.4. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.11.2...v3.12.4) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 00d9cfda688..37fb9e1bae1 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -109,7 +109,7 @@ dependencies { testImplementation 'com.rabbitmq:amqp-client:5.12.0' testImplementation 'org.mongodb:mongo-java-driver:3.12.9' - testImplementation ('org.mockito:mockito-core:3.11.2') { + testImplementation ('org.mockito:mockito-core:3.12.4') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From faff9872c638dab04e40d601020fb151ed2f5ad3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:00:37 +0000 Subject: [PATCH 2066/5615] Bump elasticsearch-rest-client in /modules/elasticsearch Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 7.13.0 to 7.14.1. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.13.0...v7.14.1) --- updated-dependencies: - dependency-name: org.elasticsearch.client:elasticsearch-rest-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 180f373bddd..a27d2b2ecab 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,7 +2,7 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') - testImplementation "org.elasticsearch.client:elasticsearch-rest-client:7.13.0" + testImplementation "org.elasticsearch.client:elasticsearch-rest-client:7.14.1" testImplementation "org.elasticsearch.client:transport:7.13.4" testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 34a912b7da3ea62a10d80e34cf03a6a4ca51892f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:00:38 +0000 Subject: [PATCH 2067/5615] Bump aws-java-sdk-logs from 1.12.37 to 1.12.60 in /modules/localstack Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.12.37 to 1.12.60. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.37...1.12.60) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-logs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 9c3fc24d658..3a307cac1b6 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,7 +6,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.37' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.37' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.37' - testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.37' + testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.60' testImplementation 'software.amazon.awssdk:s3:2.17.9' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From e03d8c5c038270dec66f5416ea049570d4e5becc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:00:38 +0000 Subject: [PATCH 2068/5615] Bump cucumber-junit from 6.9.1 to 6.11.0 in /examples Bumps [cucumber-junit](https://github.com/cucumber/cucumber-jvm) from 6.9.1 to 6.11.0. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v6.9.1...v6.11.0) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-junit dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index ddffea03dfe..6d3cfc66955 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' testImplementation 'io.cucumber:cucumber-java:6.9.1' - testImplementation 'io.cucumber:cucumber-junit:6.9.1' + testImplementation 'io.cucumber:cucumber-junit:6.11.0' testImplementation 'org.testcontainers:selenium' } From fd594634f95b0e9d824e3873c8d7b5bf7960cde1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:00:46 +0000 Subject: [PATCH 2069/5615] Bump annotations from 21.0.1 to 22.0.0 in /examples Bumps [annotations](https://github.com/JetBrains/java-annotations) from 21.0.1 to 22.0.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/21.0.1...22.0.0) --- updated-dependencies: - dependency-name: org.jetbrains:annotations dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- examples/mongodb-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/mongodb-container/build.gradle b/examples/mongodb-container/build.gradle index 8d8c8731c86..86b3e978019 100644 --- a/examples/mongodb-container/build.gradle +++ b/examples/mongodb-container/build.gradle @@ -9,5 +9,5 @@ repositories { dependencies { testImplementation 'org.testcontainers:testcontainers' testImplementation 'junit:junit:4.13.2' - testCompileClasspath 'org.jetbrains:annotations:21.0.1' + testCompileClasspath 'org.jetbrains:annotations:22.0.0' } From ebc61e7438d1c8c0b7a3bfcb3af6f5db086a50a9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:00:47 +0000 Subject: [PATCH 2070/5615] Bump mongo-java-driver from 3.12.9 to 3.12.10 in /core Bumps [mongo-java-driver](https://github.com/mongodb/mongo-java-driver) from 3.12.9 to 3.12.10. - [Release notes](https://github.com/mongodb/mongo-java-driver/releases) - [Commits](https://github.com/mongodb/mongo-java-driver/compare/r3.12.9...r3.12.10) --- updated-dependencies: - dependency-name: org.mongodb:mongo-java-driver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 00d9cfda688..df1d8c1579d 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -107,7 +107,7 @@ dependencies { testImplementation 'org.apache.httpcomponents:httpclient:4.5.9' testImplementation 'redis.clients:jedis:3.6.3' testImplementation 'com.rabbitmq:amqp-client:5.12.0' - testImplementation 'org.mongodb:mongo-java-driver:3.12.9' + testImplementation 'org.mongodb:mongo-java-driver:3.12.10' testImplementation ('org.mockito:mockito-core:3.11.2') { exclude(module: 'hamcrest-core') From ec4cd8bbec86463deb79cb39990fc261a271e566 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:00:51 +0000 Subject: [PATCH 2071/5615] Bump org.springframework.boot from 2.5.2 to 2.5.4 in /examples Bumps [org.springframework.boot](https://github.com/spring-projects/spring-boot) from 2.5.2 to 2.5.4. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.5.2...v2.5.4) --- updated-dependencies: - dependency-name: org.springframework.boot dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot-kotlin-redis/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index 70c4a2683b0..49893845273 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.5.2' + id 'org.springframework.boot' version '2.5.4' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index db7e4ebe957..e32a1446651 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,5 +1,5 @@ plugins { - id("org.springframework.boot") version "2.5.2" + id("org.springframework.boot") version "2.5.4" id("org.jetbrains.kotlin.jvm") version "1.5.10" id("org.jetbrains.kotlin.plugin.spring") version "1.5.21" } diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index 00f6f22728d..3f93d3c153c 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.5.2' + id 'org.springframework.boot' version '2.5.4' } apply plugin: 'io.spring.dependency-management' From bb67ee0f411fc28b38268bfb9815f74df0dfadc4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:00:53 +0000 Subject: [PATCH 2072/5615] Bump org.jetbrains.kotlin.plugin.spring in /examples Bumps org.jetbrains.kotlin.plugin.spring from 1.5.21 to 1.5.30. --- updated-dependencies: - dependency-name: org.jetbrains.kotlin.plugin.spring dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index db7e4ebe957..9cf3680a2aa 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,7 +1,7 @@ plugins { id("org.springframework.boot") version "2.5.2" id("org.jetbrains.kotlin.jvm") version "1.5.10" - id("org.jetbrains.kotlin.plugin.spring") version "1.5.21" + id("org.jetbrains.kotlin.plugin.spring") version "1.5.30" } apply plugin: 'io.spring.dependency-management' From c7dcb7a149df3bbbdff127e7447a83677b5bcb39 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:00:54 +0000 Subject: [PATCH 2073/5615] Bump gson from 2.8.7 to 2.8.8 in /examples Bumps [gson](https://github.com/google/gson) from 2.8.7 to 2.8.8. - [Release notes](https://github.com/google/gson/releases) - [Changelog](https://github.com/google/gson/blob/master/CHANGELOG.md) - [Commits](https://github.com/google/gson/compare/gson-parent-2.8.7...gson-parent-2.8.8) --- updated-dependencies: - dependency-name: com.google.code.gson:gson dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/disque-job-queue/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index 04d8f249511..c869fd9cdc0 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly "org.projectlombok:lombok:1.18.20" annotationProcessor "org.projectlombok:lombok:1.18.20" implementation 'biz.paluch.redis:spinach:0.3' - implementation 'com.google.code.gson:gson:2.8.7' + implementation 'com.google.code.gson:gson:2.8.8' implementation 'com.google.guava:guava:23.0' testImplementation 'ch.qos.logback:logback-classic:1.2.3' testImplementation 'org.mockito:mockito-all:1.10.19' diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 56dab9c4951..541d55839ec 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -9,7 +9,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' implementation 'redis.clients:jedis:3.6.1' - implementation 'com.google.code.gson:gson:2.8.7' + implementation 'com.google.code.gson:gson:2.8.8' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'ch.qos.logback:logback-classic:1.2.3' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 771f55551ea..21e35752540 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -9,7 +9,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' implementation 'redis.clients:jedis:3.6.1' - implementation 'com.google.code.gson:gson:2.8.7' + implementation 'com.google.code.gson:gson:2.8.8' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'junit:junit:4.13.2' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index a806f483938..1cd86d10bd4 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -9,7 +9,7 @@ repositories { dependencies { implementation 'redis.clients:jedis:3.6.1' - implementation 'com.google.code.gson:gson:2.8.7' + implementation 'com.google.code.gson:gson:2.8.8' implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.32' From 239fc52a798e25d6ba45ba2a92db98175bf6d335 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:01:11 +0000 Subject: [PATCH 2074/5615] Bump gradle-update/update-gradle-wrapper-action from 1.0.14 to 1.0.15 Bumps [gradle-update/update-gradle-wrapper-action](https://github.com/gradle-update/update-gradle-wrapper-action) from 1.0.14 to 1.0.15. - [Release notes](https://github.com/gradle-update/update-gradle-wrapper-action/releases) - [Changelog](https://github.com/gradle-update/update-gradle-wrapper-action/blob/master/CHANGELOG.md) - [Commits](https://github.com/gradle-update/update-gradle-wrapper-action/compare/6356f411fa7c14aab1fb2ac2fd20762a629e2fbf...a116d8b533be6c4e71a8b3c3871b7344ca72bf12) --- updated-dependencies: - dependency-name: gradle-update/update-gradle-wrapper-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/update-gradle-wrapper.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-gradle-wrapper.yml b/.github/workflows/update-gradle-wrapper.yml index 272594c26df..f9f1ec85662 100644 --- a/.github/workflows/update-gradle-wrapper.yml +++ b/.github/workflows/update-gradle-wrapper.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v2 - name: Update Gradle Wrapper - uses: gradle-update/update-gradle-wrapper-action@6356f411fa7c14aab1fb2ac2fd20762a629e2fbf # v1.0.13 + uses: gradle-update/update-gradle-wrapper-action@a116d8b533be6c4e71a8b3c3871b7344ca72bf12 # v1.0.13 with: repo-token: ${{ secrets.GITHUB_TOKEN }} labels: dependencies From b588ad8f38da48e0af867a174e2a1e8786b364f6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:01:15 +0000 Subject: [PATCH 2075/5615] Bump actions/setup-java from 2.1.0 to 2.3.0 Bumps [actions/setup-java](https://github.com/actions/setup-java) from 2.1.0 to 2.3.0. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v2.1.0...v2.3.0) --- updated-dependencies: - dependency-name: actions/setup-java dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- .github/workflows/release.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index 789c4fdd577..88587a6ab61 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -15,7 +15,7 @@ jobs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.1.0 + - uses: actions/setup-java@v2.3.0 with: java-version: '8.0.292+10' distribution: adopt-hotspot @@ -43,7 +43,7 @@ jobs: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.1.0 + - uses: actions/setup-java@v2.3.0 with: java-version: '8.0.292+10' distribution: adopt-hotspot diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 627d69befad..d44cd414fab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.1.0 + - uses: actions/setup-java@v2.3.0 with: java-version: '8.0.292+10' distribution: adopt-hotspot @@ -41,7 +41,7 @@ jobs: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.1.0 + - uses: actions/setup-java@v2.3.0 with: java-version: '8.0.292+10' distribution: adopt-hotspot diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 943dfcf005a..73b3b2152c9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v1 + - uses: actions/setup-java@v2.3.0 with: java-version: '1.8' - name: Clear existing docker image cache From 36a842728bc0ab73b6c038f29c9155eb3a6d8cbb Mon Sep 17 00:00:00 2001 From: pioorg Date: Sat, 4 Sep 2021 13:28:28 +0200 Subject: [PATCH 2076/5615] Reverted saving user properties. (#4442) --- .../org/testcontainers/utility/TestcontainersConfiguration.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 91d42e45ba0..85b25ca8e43 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -311,6 +311,8 @@ public boolean updateUserConfig(@NonNull String prop, @NonNull String value) { return false; } + userProperties.setProperty(prop, value); + USER_CONFIG_FILE.createNewFile(); try (OutputStream outputStream = new FileOutputStream(USER_CONFIG_FILE)) { userProperties.store(outputStream, "Modified by Testcontainers"); From 8f99905899cca2df5cc70a174de7ec696127dc2d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Sep 2021 13:25:57 +0000 Subject: [PATCH 2077/5615] Bump transport from 7.13.4 to 7.14.1 in /modules/elasticsearch Bumps [transport](https://github.com/elastic/elasticsearch) from 7.13.4 to 7.14.1. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.13.4...v7.14.1) --- updated-dependencies: - dependency-name: org.elasticsearch.client:transport dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index a27d2b2ecab..183b2428ba8 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,6 +3,6 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') testImplementation "org.elasticsearch.client:elasticsearch-rest-client:7.14.1" - testImplementation "org.elasticsearch.client:transport:7.13.4" + testImplementation "org.elasticsearch.client:transport:7.14.1" testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 21e7b0e207c2e6217bb671c5ead7076d338de70d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Sep 2021 13:26:07 +0000 Subject: [PATCH 2078/5615] Bump aws-java-sdk-s3 from 1.12.37 to 1.12.62 in /modules/localstack Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.37 to 1.12.62. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.37...1.12.62) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 3a307cac1b6..5220306a3f1 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.37' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.62' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.37' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.37' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.60' From bf6fa3830f47c0a2930107fc283b4e4cb94870b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Sep 2021 13:26:08 +0000 Subject: [PATCH 2079/5615] Bump amqp-client from 5.12.0 to 5.13.1 in /core Bumps [amqp-client](https://github.com/rabbitmq/rabbitmq-java-client) from 5.12.0 to 5.13.1. - [Release notes](https://github.com/rabbitmq/rabbitmq-java-client/releases) - [Commits](https://github.com/rabbitmq/rabbitmq-java-client/compare/v5.12.0...v5.13.1) --- updated-dependencies: - dependency-name: com.rabbitmq:amqp-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index dcf83d1a6f6..fa0e18e40e8 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -106,7 +106,7 @@ dependencies { testImplementation 'org.apache.httpcomponents:httpclient:4.5.9' testImplementation 'redis.clients:jedis:3.7.0' - testImplementation 'com.rabbitmq:amqp-client:5.12.0' + testImplementation 'com.rabbitmq:amqp-client:5.13.1' testImplementation 'org.mongodb:mongo-java-driver:3.12.10' testImplementation ('org.mockito:mockito-core:3.12.4') { From d2e31c172ce1154a229ea5bfeb9a422adbd4ec6b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Sep 2021 13:26:12 +0000 Subject: [PATCH 2080/5615] Bump cucumber-java from 6.9.1 to 6.11.0 in /examples Bumps [cucumber-java](https://github.com/cucumber/cucumber-jvm) from 6.9.1 to 6.11.0. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v6.9.1...v6.11.0) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-java dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 6d3cfc66955..6febeeec483 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' - testImplementation 'io.cucumber:cucumber-java:6.9.1' + testImplementation 'io.cucumber:cucumber-java:6.11.0' testImplementation 'io.cucumber:cucumber-junit:6.11.0' testImplementation 'org.testcontainers:selenium' } From 077c648a0e1bb3e3803c99851d711ede9797ab77 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Sep 2021 13:26:13 +0000 Subject: [PATCH 2081/5615] Bump org.jetbrains.kotlin.jvm from 1.5.10 to 1.5.30 in /examples Bumps org.jetbrains.kotlin.jvm from 1.5.10 to 1.5.30. --- updated-dependencies: - dependency-name: org.jetbrains.kotlin.jvm dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 744883204bc..6096fcb37b4 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,6 +1,6 @@ plugins { id("org.springframework.boot") version "2.5.4" - id("org.jetbrains.kotlin.jvm") version "1.5.10" + id("org.jetbrains.kotlin.jvm") version "1.5.30" id("org.jetbrains.kotlin.plugin.spring") version "1.5.30" } From c15212d73e4467e8c123245eb644333008e03aca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Sep 2021 13:26:13 +0000 Subject: [PATCH 2082/5615] Bump annotations from 21.0.1 to 22.0.0 in /core Bumps [annotations](https://github.com/JetBrains/java-annotations) from 21.0.1 to 22.0.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/21.0.1...22.0.0) --- updated-dependencies: - dependency-name: org.jetbrains:annotations dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index dcf83d1a6f6..e5a33c49ade 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -68,7 +68,7 @@ dependencies { api 'junit:junit:4.12' api 'org.slf4j:slf4j-api:1.7.32' - compileOnly 'org.jetbrains:annotations:21.0.1' + compileOnly 'org.jetbrains:annotations:22.0.0' testCompileClasspath 'org.jetbrains:annotations:21.0.1' api 'org.apache.commons:commons-compress:1.21' api ('org.rnorth.duct-tape:duct-tape:1.0.8') { From f829069b8dded7aa3115f4067fa15ee15a9227e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Sep 2021 13:26:21 +0000 Subject: [PATCH 2083/5615] Bump aws-java-sdk-sqs from 1.12.37 to 1.12.62 in /modules/localstack Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.12.37 to 1.12.62. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.37...1.12.62) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-sqs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 3a307cac1b6..a569f7dbf2b 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.37' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.37' - testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.37' + testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.62' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.60' testImplementation 'software.amazon.awssdk:s3:2.17.9' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From a9f6c44a81e5c36aef961c7f10eed026791b6fba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Sep 2021 13:26:23 +0000 Subject: [PATCH 2084/5615] Bump s3 from 2.17.9 to 2.17.34 in /modules/localstack Bumps s3 from 2.17.9 to 2.17.34. --- updated-dependencies: - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 3a307cac1b6..908055ddfd1 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.37' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.37' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.60' - testImplementation 'software.amazon.awssdk:s3:2.17.9' + testImplementation 'software.amazon.awssdk:s3:2.17.34' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From cbed98a8101f23b65a85f8f183ccf18b366a89b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Sep 2021 13:26:28 +0000 Subject: [PATCH 2085/5615] Bump jedis from 3.6.1 to 3.7.0 in /examples Bumps [jedis](https://github.com/redis/jedis) from 3.6.1 to 3.7.0. - [Release notes](https://github.com/redis/jedis/releases) - [Commits](https://github.com/redis/jedis/compare/jedis-3.6.1...v3.7.0) --- updated-dependencies: - dependency-name: redis.clients:jedis dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index b0555f1b38b..fe269d58e77 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' - implementation 'redis.clients:jedis:3.6.1' + implementation 'redis.clients:jedis:3.7.0' implementation 'com.google.code.gson:gson:2.8.8' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index bba7d9bfc51..2d9f50b86bd 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' - implementation 'redis.clients:jedis:3.6.1' + implementation 'redis.clients:jedis:3.7.0' implementation 'com.google.code.gson:gson:2.8.8' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 1199b908048..f3a18e685f3 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { - implementation 'redis.clients:jedis:3.6.1' + implementation 'redis.clients:jedis:3.7.0' implementation 'com.google.code.gson:gson:2.8.8' implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.32' From 475e6192010493e3208d69db8d9637d730df8fd4 Mon Sep 17 00:00:00 2001 From: Oguzhan Ozdemir Date: Tue, 7 Sep 2021 11:07:09 +0300 Subject: [PATCH 2086/5615] Add Thundra Foresight Integration to the CI Build (#4313) --- .github/workflows/ci.yml | 22 ++++++++++++++++++++-- README.md | 2 ++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d44cd414fab..a6ae55e16b4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,6 +9,14 @@ env: AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} jobs: + thundra_test_initializer: + runs-on: ubuntu-latest + outputs: + thundra_agent_testrun_id: ${{ steps.thundra_test_initializer.outputs.thundra_agent_testrun_id }} + steps: + - uses: actions/checkout@v2 + - id: thundra_test_initializer + uses: thundra-io/thundra-test-init-action@v1 find_gradle_jobs: runs-on: ubuntu-18.04 outputs: @@ -34,7 +42,7 @@ jobs: echo $TASKS echo "::set-output name=matrix::{\"gradle_args\":$TASKS}" check: - needs: find_gradle_jobs + needs: [find_gradle_jobs, thundra_test_initializer] strategy: fail-fast: false matrix: ${{ fromJson(needs.find_gradle_jobs.outputs.matrix) }} @@ -53,9 +61,19 @@ jobs: key: ${{ runner.os }}-gradle-home-${{matrix.gradle_args}}_check-${{ hashFiles('**/*.gradle') }} - name: Clear existing docker image cache run: docker image prune -af + - name: Thundra Gradle Test Instrumentation + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + uses: thundra-io/thundra-gradle-test-action@v1 + with: + apikey: ${{ secrets.THUNDRA_API_KEY }} + project_id: ${{ secrets.THUNDRA_PROJECT_ID }} - name: Build and test with Gradle (${{matrix.gradle_args}}) run: | - ./gradlew --no-daemon --continue --scan ${{matrix.gradle_args}} + ./gradlew --no-daemon --continue --scan ${{matrix.gradle_args}} \ + $($THUNDRA_GRADLE_INIT_SCRIPT_PATH || echo "--init-script $THUNDRA_GRADLE_INIT_SCRIPT_PATH") + env: + THUNDRA_AGENT_TEST_RUN_ID: ${{ needs.thundra_test_initializer.outputs.thundra_agent_testrun_id }} + THUNDRA_AGENT_REPORT_REST_BASEURL: https://collector.thundra.us/v1 httpclient5_test: runs-on: ubuntu-18.04 steps: diff --git a/README.md b/README.md index d3446cbb31e..752c501e91f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Testcontainers +[![Thundra Foresight](https://thundra-assets-prod.s3.us-west-2.amazonaws.com/images/badges/thundra-foresight-badge-enabled.svg)](https://foresight.thundra.live/testRuns/5a49cc30-f063-11eb-9a03-0242ac130003) + > Testcontainers is a Java library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container. ![Testcontainers logo](docs/logo.png) From 8746f20aec2f1f0140a5ae66639e690c3388a4b1 Mon Sep 17 00:00:00 2001 From: Galen S <56940276+schmidt-galen-heb@users.noreply.github.com> Date: Wed, 8 Sep 2021 06:49:34 -0500 Subject: [PATCH 2087/5615] Use POSIX mode for large numbers in tar archives (#4384) (#4388) * Use POSIX mode for large numbers in tar archives (#4384) * PR feedback --- .../main/java/org/testcontainers/containers/ContainerState.java | 1 + .../org/testcontainers/images/builder/ImageFromDockerfile.java | 1 + 2 files changed, 2 insertions(+) diff --git a/core/src/main/java/org/testcontainers/containers/ContainerState.java b/core/src/main/java/org/testcontainers/containers/ContainerState.java index da87dbdf724..5de9ef7c566 100644 --- a/core/src/main/java/org/testcontainers/containers/ContainerState.java +++ b/core/src/main/java/org/testcontainers/containers/ContainerState.java @@ -272,6 +272,7 @@ default void copyFileToContainer(Transferable transferable, String containerPath TarArchiveOutputStream tarArchive = new TarArchiveOutputStream(byteArrayOutputStream) ) { tarArchive.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); + tarArchive.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX); transferable.transferTo(tarArchive, containerPath); tarArchive.finish(); diff --git a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java index b36779f1b9e..05bb6911c3e 100644 --- a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java +++ b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java @@ -123,6 +123,7 @@ public void onNext(BuildResponseItem item) { // To build an image, we have to send the context to Docker in TAR archive format try (TarArchiveOutputStream tarArchive = new TarArchiveOutputStream(new GZIPOutputStream(out))) { tarArchive.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); + tarArchive.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX); for (Map.Entry entry : transferables.entrySet()) { Transferable transferable = entry.getValue(); From 79b064bcb0e1ffba6058fc2e7347cb41934adc00 Mon Sep 17 00:00:00 2001 From: Denis Zavedeev <13610290+cac03@users.noreply.github.com> Date: Tue, 14 Sep 2021 22:59:40 +0300 Subject: [PATCH 2088/5615] Fix `CmdModifierTest#testMemoryLimitModified` in cgroup2 environment. (#4375) * Fix `CmdModifierTest#testMemoryLimitModified` in cgroup2 environment. The `testMemoryLimitModified` was failing in cgroup2 environment because of the missing `/sys/fs/cgroup/memory/memory.limit_in_bytes` file. In the cgroup2 environment a new file should be checked instead -- `/sys/fs/cgroup/memory.max`. This commit intrdocues changes to `cat` both files: 1. `/sys/fs/cgroup/memory/memory.limit_in_bytes` from the first version of cgroup 2. `/sys/fs/cgroup/memory.max` from the second Similar issues: 1. https://github.com/oracle/docker-images/issues/1939 2. https://github.com/apify/apify-js/issues/693 * Use `docker info` to distinguish cgroup version * Use `DockerClientFactory.instance().client()` instead of `memoryLimitedRedis.getDockerClient()` --- .../src/test/java/generic/CmdModifierTest.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java b/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java index 73338ba38ee..5306426070f 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java @@ -1,12 +1,16 @@ package generic; +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.model.Info; import org.junit.Rule; import org.junit.Test; +import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.Container; import org.testcontainers.containers.GenericContainer; import org.testcontainers.utility.DockerImageName; import java.io.IOException; +import java.util.Objects; import static org.junit.Assert.assertEquals; @@ -39,7 +43,18 @@ public void testHostnameModified() throws IOException, InterruptedException { @Test public void testMemoryLimitModified() throws IOException, InterruptedException { - final Container.ExecResult execResult = memoryLimitedRedis.execInContainer("cat", "/sys/fs/cgroup/memory/memory.limit_in_bytes"); + final Container.ExecResult execResult = memoryLimitedRedis.execInContainer("cat", getMemoryLimitFilePath()); assertEquals(String.valueOf(memoryInBytes), execResult.getStdout().trim()); } + + private String getMemoryLimitFilePath() { + DockerClient dockerClient = DockerClientFactory.instance().client(); + Info info = dockerClient.infoCmd().exec(); + Object cgroupVersion = info.getRawValues().get("CgroupVersion"); + boolean cgroup2 = Objects.equals("2", cgroupVersion); + if (cgroup2) { + return "/sys/fs/cgroup/memory.max"; + } + return "/sys/fs/cgroup/memory/memory.limit_in_bytes"; + } } From 5e88d744dcdf26965a8a4176f52c94e19be204f4 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 15 Sep 2021 09:05:30 +0200 Subject: [PATCH 2089/5615] Update to `docker-java` 3.2.12 (#4459) * Try `docker-java` 3.2.12 * use shaded configuration * Update build.gradle --- core/build.gradle | 10 ++++++---- .../testcontainers/DockerClientFactory.java | 19 +++++++++++++++++-- .../org/testcontainers/LazyDockerClient.java | 16 ---------------- .../AuthDelegatingDockerClientConfig.java | 16 +++++----------- 4 files changed, 28 insertions(+), 33 deletions(-) delete mode 100644 core/src/main/java/org/testcontainers/LazyDockerClient.java diff --git a/core/build.gradle b/core/build.gradle index ed1e82271ed..f77461a06fc 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -77,9 +77,11 @@ dependencies { shaded 'org.awaitility:awaitility:4.1.0' - api "com.github.docker-java:docker-java-api:3.2.11" + shaded platform('com.github.docker-java:docker-java-bom:3.2.12') - shaded ('com.github.docker-java:docker-java-core:3.2.11') { + api "com.github.docker-java:docker-java-api" + + shaded ('com.github.docker-java:docker-java-core') { exclude(group: 'com.github.docker-java', module: 'docker-java-api') exclude(group: 'com.github.docker-java', module: 'docker-java-transport') exclude(group: 'com.fasterxml.jackson.core', module: 'jackson-annotations') @@ -88,13 +90,13 @@ dependencies { exclude(group: 'org.apache.commons', module: 'commons-compress') } - shaded ('com.github.docker-java:docker-java-transport-okhttp:3.2.11') { + shaded ('com.github.docker-java:docker-java-transport-okhttp') { exclude(group: 'com.github.docker-java', module: 'docker-java-core') exclude(group: 'net.java.dev.jna') exclude(group: 'org.slf4j') } - api 'com.github.docker-java:docker-java-transport-zerodep:3.2.11' + api 'com.github.docker-java:docker-java-transport-zerodep' shaded "org.yaml:snakeyaml:1.29" diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 554860d8985..36c4c717b76 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -1,6 +1,7 @@ package org.testcontainers; import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.DockerClientDelegate; import com.github.dockerjava.api.async.ResultCallback; import com.github.dockerjava.api.command.CreateContainerCmd; import com.github.dockerjava.api.command.PullImageCmd; @@ -94,7 +95,17 @@ public class DockerClientFactory { } public static DockerClient lazyClient() { - return LazyDockerClient.INSTANCE; + return new DockerClientDelegate() { + @Override + protected DockerClient getDockerClient() { + return instance().client(); + } + + @Override + public String toString() { + return "LazyDockerClient"; + } + }; } /** @@ -177,7 +188,11 @@ public DockerClient client() { final DockerClientProviderStrategy strategy = getOrInitializeStrategy(); log.info("Docker host IP address is {}", strategy.getDockerHostIpAddress()); - final DockerClient client = new DelegatingDockerClient(strategy.getDockerClient()) { + final DockerClient client = new DockerClientDelegate() { + + @Getter + final DockerClient dockerClient = strategy.getDockerClient(); + @Override public void close() { throw new IllegalStateException("You should never close the global DockerClient!"); diff --git a/core/src/main/java/org/testcontainers/LazyDockerClient.java b/core/src/main/java/org/testcontainers/LazyDockerClient.java deleted file mode 100644 index 49ebf9330a7..00000000000 --- a/core/src/main/java/org/testcontainers/LazyDockerClient.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.testcontainers; - -import com.github.dockerjava.api.DockerClient; -import lombok.ToString; -import lombok.experimental.Delegate; - -@ToString -enum LazyDockerClient implements DockerClient { - - INSTANCE; - - @Delegate - final DockerClient getDockerClient() { - return DockerClientFactory.instance().client(); - } -} diff --git a/core/src/main/java/org/testcontainers/dockerclient/AuthDelegatingDockerClientConfig.java b/core/src/main/java/org/testcontainers/dockerclient/AuthDelegatingDockerClientConfig.java index 5acb7ab8835..1f534eb59e5 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/AuthDelegatingDockerClientConfig.java +++ b/core/src/main/java/org/testcontainers/dockerclient/AuthDelegatingDockerClientConfig.java @@ -2,7 +2,7 @@ import com.github.dockerjava.api.model.AuthConfig; import com.github.dockerjava.core.DockerClientConfig; -import lombok.experimental.Delegate; +import com.github.dockerjava.core.DockerClientConfigDelegate; import lombok.extern.slf4j.Slf4j; import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.RegistryAuthLocator; @@ -18,20 +18,18 @@ * TODO move to docker-java */ @Slf4j -class AuthDelegatingDockerClientConfig implements DockerClientConfig { - - @Delegate(excludes = DelegateExclusions.class) - private DockerClientConfig delegate; +class AuthDelegatingDockerClientConfig extends DockerClientConfigDelegate { public AuthDelegatingDockerClientConfig(DockerClientConfig delegate) { - this.delegate = delegate; + super(delegate); } + @Override public AuthConfig effectiveAuthConfig(String imageName) { // allow docker-java auth config to be used as a fallback AuthConfig fallbackAuthConfig; try { - fallbackAuthConfig = delegate.effectiveAuthConfig(imageName); + fallbackAuthConfig = super.effectiveAuthConfig(imageName); } catch (Exception e) { log.debug("Delegate call to effectiveAuthConfig failed with cause: '{}'. " + "Resolution of auth config will continue using RegistryAuthLocator.", @@ -47,8 +45,4 @@ public AuthConfig effectiveAuthConfig(String imageName) { log.debug("Effective auth config [{}]", toSafeString(effectiveAuthConfig)); return effectiveAuthConfig; } - - private interface DelegateExclusions { - AuthConfig effectiveAuthConfig(String imageName); - } } From 6cde564776d9af26e4b69f80e1344f1ea7d0f678 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Thu, 16 Sep 2021 13:45:24 +0300 Subject: [PATCH 2090/5615] Allow to disable configuration of `EnvironmentAndSystemPropertyClientProviderStrategy` through Testcontainers (#4387) Adds the `dockerconfig.source` property that can be used to change the way `EnvironmentAndSystemPropertyClientProviderStrategy` is configured. Additionally, `EnvironmentAndSystemPropertyClientProviderStrategy` is not persistable anymore. Currently supports the following modes: * `auto` (which is the default), meaning that the Testcontainers configuration mechanisms can be used to configure docker-java * `autoIgnoringUserProperties`, same as `auto`, but ignoring values set in the user properties --- ...dSystemPropertyClientProviderStrategy.java | 38 ++++++++++++++----- .../utility/TestcontainersConfiguration.java | 17 +-------- ...temPropertyClientProviderStrategyTest.java | 30 +++++++++++++++ .../TestcontainersConfigurationTest.java | 8 ---- docs/features/configuration.md | 4 ++ 5 files changed, 65 insertions(+), 32 deletions(-) diff --git a/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java index e9bdbd90bea..06fdf387d6a 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java @@ -2,6 +2,7 @@ import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.core.DockerClientConfig; +import lombok.Getter; import org.testcontainers.utility.TestcontainersConfiguration; import java.util.Optional; @@ -25,13 +26,32 @@ public final class EnvironmentAndSystemPropertyClientProviderStrategy extends Do private final DockerClientConfig dockerClientConfig; + @Getter + private final boolean applicable; + public EnvironmentAndSystemPropertyClientProviderStrategy() { // use docker-java defaults if present, overridden if our own configuration is set - DefaultDockerClientConfig.Builder configBuilder = DefaultDockerClientConfig.createDefaultConfigBuilder(); + this(DefaultDockerClientConfig.createDefaultConfigBuilder()); + } + + EnvironmentAndSystemPropertyClientProviderStrategy(DefaultDockerClientConfig.Builder configBuilder) { + String dockerConfigSource = TestcontainersConfiguration.getInstance() + .getEnvVarOrProperty("dockerconfig.source", "auto"); - getSetting("docker.host").ifPresent(configBuilder::withDockerHost); - getSetting("docker.tls.verify").ifPresent(configBuilder::withDockerTlsVerify); - getSetting("docker.cert.path").ifPresent(configBuilder::withDockerCertPath); + switch (dockerConfigSource) { + case "auto": + Optional dockerHost = getSetting("docker.host"); + dockerHost.ifPresent(configBuilder::withDockerHost); + applicable = dockerHost.isPresent(); + getSetting("docker.tls.verify").ifPresent(configBuilder::withDockerTlsVerify); + getSetting("docker.cert.path").ifPresent(configBuilder::withDockerCertPath); + break; + case "autoIgnoringUserProperties": + applicable = configBuilder.isDockerHostSetExplicitly(); + break; + default: + throw new InvalidConfigurationException("Invalid value for dockerconfig.source: " + dockerConfigSource); + } dockerClientConfig = configBuilder.build(); } @@ -40,11 +60,6 @@ private Optional getSetting(final String name) { return Optional.ofNullable(TestcontainersConfiguration.getInstance().getEnvVarOrUserProperty(name, null)); } - @Override - protected boolean isApplicable() { - return getSetting("docker.host").isPresent(); - } - @Override public TransportConfig getTransportConfig() { return TransportConfig.builder() @@ -62,4 +77,9 @@ protected int getPriority() { public String getDescription() { return "Environment variables, system properties and defaults. Resolved dockerHost=" + dockerClientConfig.getDockerHost(); } + + @Override + protected boolean isPersistable() { + return false; + } } diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index 85b25ca8e43..bb3febd6b3f 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -13,7 +13,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.testcontainers.UnstableAPI; -import org.testcontainers.dockerclient.EnvironmentAndSystemPropertyClientProviderStrategy; import java.io.File; import java.io.FileNotFoundException; @@ -182,20 +181,8 @@ public String getDockerClientStrategyClassName() { return prefixedEnvVarStrategy; } - // looks for unprefixed env var or unprefixed property - String unprefixedEnvVarOrProperty = getEnvVarOrUserProperty("docker.client.strategy", null); - if (unprefixedEnvVarOrProperty != null) { - return unprefixedEnvVarOrProperty; - } - - // If docker.host is set then EnvironmentAndSystemPropertyClientProviderStrategy is likely to work - String dockerHostProperty = getEnvVarOrUserProperty("docker.host", null); - if (dockerHostProperty != null) { - return EnvironmentAndSystemPropertyClientProviderStrategy.class.getCanonicalName(); - } - - // No value set, and no implicit value to use either - return null; + // looks for unprefixed env var or unprefixed property, or null if the strategy is not set at all + return getEnvVarOrUserProperty("docker.client.strategy", null); } public String getTransportType() { diff --git a/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java b/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java index 91f61848d69..c8eec1d38d8 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java @@ -16,10 +16,16 @@ import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; @@ -45,6 +51,7 @@ public void checkEnvironmentClear() { @Test public void testWhenConfigAbsent() { + Mockito.doReturn("auto").when(TestcontainersConfiguration.getInstance()).getEnvVarOrProperty(eq("dockerconfig.source"), anyString()); Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.host"), isNull()); Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.tls.verify"), isNull()); Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.cert.path"), isNull()); @@ -58,6 +65,7 @@ public void testWhenConfigAbsent() { @Test public void testWhenDockerHostPresent() { + Mockito.doReturn("auto").when(TestcontainersConfiguration.getInstance()).getEnvVarOrProperty(eq("dockerconfig.source"), anyString()); Mockito.doReturn("tcp://1.2.3.4:2375").when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.host"), isNull()); Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.tls.verify"), isNull()); Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.cert.path"), isNull()); @@ -74,6 +82,7 @@ public void testWhenDockerHostAndSSLConfigPresent() throws IOException { Path tempDir = Files.createTempDirectory("testcontainers-test"); String tempDirPath = tempDir.toAbsolutePath().toString(); + Mockito.doReturn("auto").when(TestcontainersConfiguration.getInstance()).getEnvVarOrProperty(eq("dockerconfig.source"), anyString()); Mockito.doReturn("tcp://1.2.3.4:2375").when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.host"), isNull()); Mockito.doReturn("1").when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.tls.verify"), isNull()); Mockito.doReturn(tempDirPath).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.cert.path"), isNull()); @@ -88,4 +97,25 @@ public void testWhenDockerHostAndSSLConfigPresent() throws IOException { assertTrue(sslConfig instanceof LocalDirectorySSLConfig); assertEquals(tempDirPath, ((LocalDirectorySSLConfig) sslConfig).getDockerCertPath()); } + + @Test + public void applicableWhenIgnoringUserPropertiesAndConfigured() { + Mockito.doReturn("autoIgnoringUserProperties").when(TestcontainersConfiguration.getInstance()).getEnvVarOrProperty(eq("dockerconfig.source"), anyString()); + + DefaultDockerClientConfig.Builder configBuilder = DefaultDockerClientConfig.createDefaultConfigBuilder() + .withDockerHost("tcp://1.2.3.4:2375"); + + EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(configBuilder); + + assertTrue(strategy.isApplicable()); + } + + @Test + public void notApplicableWhenIgnoringUserPropertiesAndNotConfigured() { + Mockito.doReturn("autoIgnoringUserProperties").when(TestcontainersConfiguration.getInstance()).getEnvVarOrProperty(eq("dockerconfig.source"), anyString()); + + EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(); + + assertFalse(strategy.isApplicable()); + } } diff --git a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java index cbb36baf580..7ca656e1eaf 100644 --- a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java +++ b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java @@ -2,7 +2,6 @@ import org.junit.Before; import org.junit.Test; -import org.testcontainers.dockerclient.EnvironmentAndSystemPropertyClientProviderStrategy; import java.util.HashMap; import java.util.Map; @@ -156,13 +155,6 @@ public void shouldReadDockerClientStrategyFromEnvironment() { assertEquals("Docker client strategy is changed by env var", "foo", newConfig().getDockerClientStrategyClassName()); } - @Test - public void shouldUseImplicitDockerClientStrategyWhenDockerHostPropertyIsSet() { - userProperties.remove("docker.client.strategy"); - userProperties.put("docker.host", "tcp://1.2.3.4:5678"); - assertEquals("Docker client strategy is implicitly set when docker host property is set", EnvironmentAndSystemPropertyClientProviderStrategy.class.getCanonicalName(), newConfig().getDockerClientStrategyClassName()); - } - @Test public void shouldNotUseImplicitDockerClientStrategyWhenDockerHostAndStrategyAreBothSet() { userProperties.put("docker.client.strategy", "foo"); diff --git a/docs/features/configuration.md b/docs/features/configuration.md index 73876a51483..8024dbb15ef 100644 --- a/docs/features/configuration.md +++ b/docs/features/configuration.md @@ -126,3 +126,7 @@ docker.host=tcp\://my.docker.host\:1234 # Equivalent to the DOCKER_HOST envi docker.tls.verify=1 # Equivalent to the DOCKER_TLS_VERIFY environment variable docker.cert.path=/some/path # Equivalent to the DOCKER_CERT_PATH environment variable ``` +In addition, you can deactivate this behaviour by specifying: +```properties +dockerconfig.source=autoIgnoringUserProperties # 'auto' by default +``` From 5e6dbc791b6184f0de6d1f9d184a78324c1ec945 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 16 Sep 2021 16:14:45 +0200 Subject: [PATCH 2091/5615] Retry internal port checking in the same exec (#4460) * Retry internal port checking in the same exec * Use just grep * Reduce the changeset --- .../InternalCommandPortListeningCheck.java | 5 +++-- .../InternalCommandPortListeningCheckTest.java | 17 ++++------------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java index a3b7a5d4dc7..379840a9d7e 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java +++ b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java @@ -24,18 +24,19 @@ public class InternalCommandPortListeningCheck implements java.util.concurrent.C @Override public Boolean call() { - StringBuilder command = new StringBuilder("true"); + StringBuilder command = new StringBuilder("while true; do ( true "); for (int internalPort : internalPorts) { command.append(" && "); command.append(" ("); - command.append(format("cat /proc/net/tcp* | awk '{print $2}' | grep -i ':0*%x'", internalPort)); + command.append(format("grep -i ':0*%x' /proc/net/tcp*", internalPort)); command.append(" || "); command.append(format("nc -vz -w 1 localhost %d", internalPort)); command.append(" || "); command.append(format("/bin/bash -c ' Date: Mon, 20 Sep 2021 10:44:02 +0200 Subject: [PATCH 2092/5615] Use distinct with strategies (#4467) --- .../DockerClientProviderStrategy.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index 5c797b3b782..52700df22f3 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -25,10 +25,13 @@ import java.net.URI; import java.util.ArrayList; import java.util.Comparator; +import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Predicate; import java.util.stream.Stream; /** @@ -137,10 +140,18 @@ public static DockerClientProviderStrategy getFirstValidStrategy(List log.info("Loaded {} from ~/.testcontainers.properties, will try it first", strategy.getClass().getName())), strategies .stream() - .filter(it -> !it.getClass().getCanonicalName().equals(dockerClientStrategyClassName)) - .filter(DockerClientProviderStrategy::isApplicable) .sorted(Comparator.comparing(DockerClientProviderStrategy::getPriority).reversed()) ) + .filter(new Predicate() { + + final Set> classes = new HashSet<>(); + + @Override + public boolean test(DockerClientProviderStrategy dockerClientProviderStrategy) { + return classes.add(dockerClientProviderStrategy.getClass()); + } + }) + .filter(DockerClientProviderStrategy::isApplicable) .flatMap(strategy -> { try { DockerClient dockerClient = strategy.getDockerClient(); From c7fa8e22df3b022c883a49199803cbbf00e879c4 Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Mon, 20 Sep 2021 13:49:46 +0200 Subject: [PATCH 2093/5615] Update JUnit 4.12 to 4.13.2. (#4167) Co-authored-by: Kevin Wittek --- core/build.gradle | 4 ++-- docs/examples/junit4/generic/build.gradle | 2 +- docs/examples/junit4/redis/build.gradle | 2 +- test-support/build.gradle | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index f77461a06fc..35cecd76ce8 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -66,7 +66,7 @@ dependencies { exclude group: "*", module: "*" } - api 'junit:junit:4.12' + api 'junit:junit:4.13.2' api 'org.slf4j:slf4j-api:1.7.32' compileOnly 'org.jetbrains:annotations:22.0.0' testCompileClasspath 'org.jetbrains:annotations:21.0.1' @@ -123,7 +123,7 @@ dependencies { jarFileTestCompileOnly "org.projectlombok:lombok:${lombok.version}" jarFileTestAnnotationProcessor "org.projectlombok:lombok:${lombok.version}" - jarFileTestImplementation 'junit:junit:4.12' + jarFileTestImplementation 'junit:junit:4.13.2' jarFileTestImplementation 'org.assertj:assertj-core:3.20.2' jarFileTestImplementation 'org.ow2.asm:asm-debug-all:5.2' } diff --git a/docs/examples/junit4/generic/build.gradle b/docs/examples/junit4/generic/build.gradle index 1021bfeffc0..da4697a4543 100644 --- a/docs/examples/junit4/generic/build.gradle +++ b/docs/examples/junit4/generic/build.gradle @@ -1,7 +1,7 @@ description = "Examples for docs" dependencies { - testImplementation "junit:junit:4.12" + testImplementation "junit:junit:4.13.2" testImplementation project(":testcontainers") testImplementation project(":selenium") testImplementation project(":mysql") diff --git a/docs/examples/junit4/redis/build.gradle b/docs/examples/junit4/redis/build.gradle index 7551c6e578e..ea36761b716 100644 --- a/docs/examples/junit4/redis/build.gradle +++ b/docs/examples/junit4/redis/build.gradle @@ -3,6 +3,6 @@ description = "Examples for docs" dependencies { api "io.lettuce:lettuce-core:5.1.1.RELEASE" - testImplementation "junit:junit:4.12" + testImplementation "junit:junit:4.13.2" testImplementation project(":testcontainers") } diff --git a/test-support/build.gradle b/test-support/build.gradle index 4c6fdd2bed1..bd85b585b0f 100644 --- a/test-support/build.gradle +++ b/test-support/build.gradle @@ -1,4 +1,4 @@ dependencies { - implementation 'junit:junit:4.12' + implementation 'junit:junit:4.13.2' implementation 'org.slf4j:slf4j-api:1.7.30' } From 28d74da529427813accd5d14fd2c522796d4fa2a Mon Sep 17 00:00:00 2001 From: Michael Nitschinger Date: Mon, 20 Sep 2021 16:26:34 +0200 Subject: [PATCH 2094/5615] [couchbase] add debug-level phase timings. (#4466) --- .../couchbase/CouchbaseContainer.java | 66 ++++++++++++------- 1 file changed, 43 insertions(+), 23 deletions(-) diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index 844917c053f..e9247b8c84a 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -221,20 +221,22 @@ protected void configure() { protected void containerIsStarting(final InspectContainerResponse containerInfo) { logger().debug("Couchbase container is starting, performing configuration."); - waitUntilNodeIsOnline(); - initializeIsEnterprise(); - renameNode(); - initializeServices(); - configureAdminUser(); - configureExternalPorts(); + timePhase("waitUntilNodeIsOnline", this::waitUntilNodeIsOnline); + timePhase("initializeIsEnterprise", this::initializeIsEnterprise); + timePhase("renameNode", this::renameNode); + timePhase("initializeServices", this::initializeServices); + timePhase("configureAdminUser", this::configureAdminUser); + timePhase("configureExternalPorts", this::configureExternalPorts); + if (enabledServices.contains(CouchbaseService.INDEX)) { - configureIndexer(); + timePhase("configureIndexer", this::configureIndexer); } } @Override protected void containerIsStarted(InspectContainerResponse containerInfo) { - createBuckets(); + timePhase("createBuckets", this::createBuckets); + logger().info("Couchbase container is ready! UI available at http://{}:{}", getHost(), getMappedPort(MGMT_PORT)); } @@ -389,30 +391,34 @@ private void createBuckets() { checkSuccessfulResponse(response, "Could not create bucket " + bucket.getName()); - new HttpWaitStrategy() + timePhase("createBucket:" + bucket.getName() + ":waitForAllServicesEnabled", () -> + new HttpWaitStrategy() .forPath("/pools/default/b/" + bucket.getName()) .forPort(MGMT_PORT) .withBasicCredentials(username, password) .forStatusCode(200) .forResponsePredicate(new AllServicesEnabledPredicate()) - .waitUntilReady(this); + .waitUntilReady(this) + ); if (enabledServices.contains(CouchbaseService.QUERY)) { // If the query service is enabled, make sure that we only proceed if the query engine also // knows about the bucket in its metadata configuration. - Unreliables.retryUntilTrue(1, TimeUnit.MINUTES, () -> { - @Cleanup Response queryResponse = doHttpRequest(QUERY_PORT, "/query/service", "POST", new FormBody.Builder() - .add("statement", "SELECT COUNT(*) > 0 as present FROM system:keyspaces WHERE name = \"" + bucket.getName() + "\"") - .build(), true); - - String body = queryResponse.body() != null ? queryResponse.body().string() : null; - checkSuccessfulResponse(queryResponse, "Could not poll query service state for bucket: " + bucket.getName()); - - return Optional.of(MAPPER.readTree(body)) - .map(n -> n.at("/results/0/present")) - .map(JsonNode::asBoolean) - .orElse(false); - }); + timePhase( + "createBucket:" + bucket.getName() + ":queryKeyspacePresent", + () -> Unreliables.retryUntilTrue(1, TimeUnit.MINUTES, () -> { + @Cleanup Response queryResponse = doHttpRequest(QUERY_PORT, "/query/service", "POST", new FormBody.Builder() + .add("statement", "SELECT COUNT(*) > 0 as present FROM system:keyspaces WHERE name = \"" + bucket.getName() + "\"") + .build(), true); + + String body = queryResponse.body() != null ? queryResponse.body().string() : null; + checkSuccessfulResponse(queryResponse, "Could not poll query service state for bucket: " + bucket.getName()); + + return Optional.of(MAPPER.readTree(body)) + .map(n -> n.at("/results/0/present")) + .map(JsonNode::asBoolean) + .orElse(false); + })); } if (bucket.hasPrimaryIndex()) { @@ -501,6 +507,20 @@ private Response doHttpRequest(final int port, final String path, final String m } } + /** + * Helper method which times an individual phase and logs it for debugging and optimization purposes. + * + * @param name the name of the phase. + * @param toTime the runnable that should be timed. + */ + private void timePhase(final String name, final Runnable toTime) { + long start = System.nanoTime(); + toTime.run(); + long end = System.nanoTime(); + + logger().debug("Phase {} took {}ms", name, TimeUnit.NANOSECONDS.toMillis(end - start)); + } + /** * In addition to getting a 200, we need to make sure that all services we need are enabled and available on * the bucket. From 0ad8154f198cfe6519a845e4ddb4ec564122f85c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 22 Sep 2021 10:11:15 +0100 Subject: [PATCH 2095/5615] Update Gradle Wrapper to 7.2 (#4379) Co-authored-by: gradle-update-robot Co-authored-by: Richard North --- .../gradle/wrapper/gradle-wrapper.properties | 4 +- examples/gradlew | 257 +++++++++++------- gradle/wrapper/gradle-wrapper.properties | 4 +- gradlew | 257 +++++++++++------- 4 files changed, 310 insertions(+), 212 deletions(-) diff --git a/examples/gradle/wrapper/gradle-wrapper.properties b/examples/gradle/wrapper/gradle-wrapper.properties index 2957f437446..a2e01c0dffe 100644 --- a/examples/gradle/wrapper/gradle-wrapper.properties +++ b/examples/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=bf8b869948901d422e9bb7d1fa61da6a6e19411baa7ad6ee929073df85d6365d -distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip +distributionSha256Sum=f581709a9c35e9cb92e16f585d2c4bc99b2b1a5f85d2badbd3dc6bff59e1e6dd +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/examples/gradlew b/examples/gradlew index 744e882ed57..1b6c787337f 100755 --- a/examples/gradlew +++ b/examples/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,67 +17,101 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +APP_BASE_NAME=${0##*/} # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MSYS* | MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -106,80 +140,95 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 17ba19021b8..a37233c5e2e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=9bb8bc05f562f2d42bdf1ba8db62f6b6fa1c3bf6c392228802cc7cb0578fe7e0 -distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-all.zip +distributionSha256Sum=a8da5b02437a60819cad23e10fc7e9cf32bcb57029d9cb277e26eeff76ce014b +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 744e882ed57..1b6c787337f 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,67 +17,101 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +APP_BASE_NAME=${0##*/} # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MSYS* | MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -106,80 +140,95 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" From 8712457efea69e0daf4f9add52478d48eb32d68f Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 23 Sep 2021 07:19:45 +0100 Subject: [PATCH 2096/5615] Refactor JDBC URL parsing to support different Oracle URLs (#4476) Detects SID `:` or service name `/` in the supplied TC JDBC URL. In addition, the affected regex patterns have been refactored to use named groups. Co-authored-by: Justin Wyer --- .../testcontainers/jdbc/ConnectionUrl.java | 73 ++++++++++++++----- .../jdbc/ConnectionUrlDriversTests.java | 11 ++- 2 files changed, 61 insertions(+), 23 deletions(-) diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java index 4f339b43fe5..3354ed45c39 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java @@ -7,9 +7,9 @@ import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collector; import java.util.stream.Collectors; import java.util.stream.Stream; + import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -97,25 +97,25 @@ private void parseUrl() { throw new IllegalArgumentException("JDBC URL matches jdbc:tc: prefix but the database or tag name could not be identified"); } } - databaseType = urlMatcher.group(1); + databaseType = urlMatcher.group("databaseType"); - imageTag = Optional.ofNullable(urlMatcher.group(3)); + imageTag = Optional.ofNullable(urlMatcher.group("imageTag")); //String like hostname:port/database name, which may vary based on target database. //Clients can further parse it as needed. - dbHostString = urlMatcher.group(4); + dbHostString = urlMatcher.group("dbHostString"); //In case it matches to the default pattern Matcher dbInstanceMatcher = Patterns.DB_INSTANCE_MATCHING_PATTERN.matcher(dbHostString); if (dbInstanceMatcher.matches()) { - databaseHost = Optional.of(dbInstanceMatcher.group(1)); - databasePort = Optional.ofNullable(dbInstanceMatcher.group(3)).map(value -> Integer.valueOf(value)); - databaseName = Optional.of(dbInstanceMatcher.group(4)); + databaseHost = Optional.of(dbInstanceMatcher.group("databaseHost")); + databasePort = Optional.ofNullable(dbInstanceMatcher.group("databasePort")).map(Integer::valueOf); + databaseName = Optional.of(dbInstanceMatcher.group("databaseName")); } queryParameters = Collections.unmodifiableMap( parseQueryParameters( - Optional.ofNullable(urlMatcher.group(5)).orElse(""))); + Optional.ofNullable(urlMatcher.group("queryParameters")).orElse(""))); String query = queryParameters .entrySet() @@ -123,7 +123,7 @@ private void parseUrl() { .map(e -> e.getKey() + "=" + e.getValue()) .collect(Collectors.joining("&")); - if (query == null || query.trim().length() == 0) { + if (query.trim().length() == 0) { queryString = Optional.empty(); } else { queryString = Optional.of("?" + query); @@ -149,7 +149,7 @@ private void parseUrl() { } private Map parseTmpfsOptions(Map containerParameters) { - if(!containerParameters.containsKey("TC_TMPFS")){ + if (!containerParameters.containsKey("TC_TMPFS")) { return Collections.emptyMap(); } @@ -209,16 +209,49 @@ public Map getTmpfsOptions() { * @author manikmagar */ public interface Patterns { - Pattern URL_MATCHING_PATTERN = Pattern.compile("jdbc:tc:([a-z0-9]+)(:([^:]+))?://([^\\?]+)(\\?.*)?"); - - Pattern ORACLE_URL_MATCHING_PATTERN = Pattern.compile("jdbc:tc:([a-z]+)(:([^(thin:)]+))?:thin:@([^\\?]+)(\\?.*)?"); + Pattern URL_MATCHING_PATTERN = Pattern.compile( + "jdbc:tc:" + + "(?[a-z0-9]+)" + + "(:(?[^:]+))?" + + "://" + + "(?[^?]+)" + + "(?\\?.*)?" + ); + + Pattern ORACLE_URL_MATCHING_PATTERN = Pattern.compile( + "jdbc:tc:" + + "(?[a-z]+)" + + "(:(?(?!thin).+))?:thin:(//)?" + + "(" + + "(?[^:" + + "?^/]+)/(?[^?^/]+)" + + ")?" + + "@" + + "(?[^?]+)" + + "(?\\?.*)?" + ); //Matches to part of string - hostname:port/databasename - Pattern DB_INSTANCE_MATCHING_PATTERN = Pattern.compile("([^:]+)(:([0-9]+))?/([^\\\\?]+)"); - - Pattern DAEMON_MATCHING_PATTERN = Pattern.compile(".*([\\?&]?)TC_DAEMON=([^\\?&]+).*"); - Pattern INITSCRIPT_MATCHING_PATTERN = Pattern.compile(".*([\\?&]?)TC_INITSCRIPT=([^\\?&]+).*"); - Pattern INITFUNCTION_MATCHING_PATTERN = Pattern.compile(".*([\\?&]?)TC_INITFUNCTION=" + + Pattern DB_INSTANCE_MATCHING_PATTERN = Pattern.compile( + "(?[^:]+)" + + "(:(?[0-9]+))?" + + "(" + + "(?[:/])" + + "|" + + ";databaseName=" + + ")" + + "(?[^\\\\?]+)" + ); + + Pattern DAEMON_MATCHING_PATTERN = Pattern.compile(".*([?&]?)TC_DAEMON=([^?&]+).*"); + + /** + * @deprecated for removal + */ + @Deprecated + Pattern INITSCRIPT_MATCHING_PATTERN = Pattern.compile(".*([?&]?)TC_INITSCRIPT=([^?&]+).*"); + + Pattern INITFUNCTION_MATCHING_PATTERN = Pattern.compile(".*([?&]?)TC_INITFUNCTION=" + "((\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*\\.)*\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)" + "::" + "(\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)" + @@ -226,9 +259,9 @@ public interface Patterns { String TC_PARAM_NAME_PATTERN = "(TC_[A-Z_]+)"; - Pattern TC_PARAM_MATCHING_PATTERN = Pattern.compile(TC_PARAM_NAME_PATTERN + "=([^\\?&]+)"); + Pattern TC_PARAM_MATCHING_PATTERN = Pattern.compile(TC_PARAM_NAME_PATTERN + "=([^?&]+)"); - Pattern QUERY_PARAM_MATCHING_PATTERN = Pattern.compile("([^\\?&=]+)=([^\\?&]*)"); + Pattern QUERY_PARAM_MATCHING_PATTERN = Pattern.compile("([^?&=]+)=([^?&]*)"); } diff --git a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java index 920fab04016..12ca5c28c3d 100644 --- a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java +++ b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java @@ -37,12 +37,17 @@ public static Iterable data() { {"jdbc:tc:mysql://hostname/test", "mysql", Optional.empty(), "hostname/test", "test"}, {"jdbc:tc:postgresql:1.2.3://hostname/test", "postgresql", Optional.of("1.2.3"), "hostname/test", "test"}, {"jdbc:tc:postgresql://hostname/test", "postgresql", Optional.empty(), "hostname/test", "test"}, - {"jdbc:tc:sqlserver:1.2.3://localhost;instance=SQLEXPRESS:1433;databaseName=test", "sqlserver", Optional.of("1.2.3"), "localhost;instance=SQLEXPRESS:1433;databaseName=test", ""}, - {"jdbc:tc:sqlserver://localhost;instance=SQLEXPRESS:1433;databaseName=test", "sqlserver", Optional.empty(), "localhost;instance=SQLEXPRESS:1433;databaseName=test", ""}, + {"jdbc:tc:sqlserver:1.2.3://localhost;instance=SQLEXPRESS:1433;databaseName=test", "sqlserver", Optional.of("1.2.3"), "localhost;instance=SQLEXPRESS:1433;databaseName=test", "test"}, + {"jdbc:tc:sqlserver://localhost;instance=SQLEXPRESS:1433;databaseName=test", "sqlserver", Optional.empty(), "localhost;instance=SQLEXPRESS:1433;databaseName=test", "test"}, {"jdbc:tc:mariadb:1.2.3://localhost:3306/test", "mariadb", Optional.of("1.2.3"), "localhost:3306/test", "test"}, {"jdbc:tc:mariadb://localhost:3306/test", "mariadb", Optional.empty(), "localhost:3306/test", "test"}, + {"jdbc:tc:oracle:1.2.3:thin://@localhost:1521/test", "oracle", Optional.of("1.2.3"), "localhost:1521/test", "test"}, {"jdbc:tc:oracle:1.2.3:thin:@localhost:1521/test", "oracle", Optional.of("1.2.3"), "localhost:1521/test", "test"}, - {"jdbc:tc:oracle:thin:@localhost:1521/test", "oracle", Optional.empty(), "localhost:1521/test", "test"} + {"jdbc:tc:oracle:thin:@localhost:1521/test", "oracle", Optional.empty(), "localhost:1521/test", "test"}, + {"jdbc:tc:oracle:1.2.3:thin:@localhost:1521:test", "oracle", Optional.of("1.2.3"), "localhost:1521:test", "test"}, + {"jdbc:tc:oracle:1.2.3:thin://@localhost:1521:test", "oracle", Optional.of("1.2.3"), "localhost:1521:test", "test"}, + {"jdbc:tc:oracle:1.2.3-anything:thin://@localhost:1521:test", "oracle", Optional.of("1.2.3-anything"), "localhost:1521:test", "test"}, + {"jdbc:tc:oracle:thin:@localhost:1521:test", "oracle", Optional.empty(), "localhost:1521:test", "test"} }); } From d512ca59802c7154dc2d117ff80e2583fe4c1c8f Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 23 Sep 2021 07:02:42 -0400 Subject: [PATCH 2097/5615] Tidy up core's build file (#4207) * Tidy up core's build file * post-merge fixes * Extract shading config * fix modules * fix tests * fix merge conflicts, simplify shading.gradle * cleanup * Workaround BOM dependency * Use `resolvedConfiguration` --- build.gradle | 33 +---------------------- core/build.gradle | 30 +++++---------------- gradle/publishing.gradle | 37 ++++++++++++++----------- gradle/shading.gradle | 58 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 71 deletions(-) create mode 100644 gradle/shading.gradle diff --git a/build.gradle b/build.gradle index 723c429271d..aac0648ec06 100644 --- a/build.gradle +++ b/build.gradle @@ -21,7 +21,7 @@ subprojects { apply plugin: 'java-library' apply plugin: 'idea' apply plugin: 'io.franzbecker.gradle-lombok' - apply plugin: 'com.github.johnrengelman.shadow' + apply from: "$rootDir/gradle/shading.gradle" group = "org.testcontainers" @@ -101,37 +101,6 @@ subprojects { source = delombok.outputs } - shadowJar { - configurations = [] - archiveClassifier.set(null) - - doFirst { - // See https://github.com/johnrengelman/shadow/blob/5.0.0/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ConfigureShadowRelocation.groovy - Set packages = [] - // Always read from core's configurations - for (configuration in tasks.getByPath(":testcontainers:shadowJar").configurations) { - for (jar in configuration.files) { - def jf = new java.util.jar.JarFile(jar) - for (entry in jf.entries()) { - def name = entry.name - if (name.endsWith(".class")) { - def index = name.lastIndexOf('/') - if (index != -1) { - packages.add(name.substring(0, index)) - } - } - } - jf.close() - } - } - for (pkg in packages) { - pkg = pkg.replaceAll('/', '.') - - tasks.shadowJar.relocate(pkg, "org.testcontainers.shaded.${pkg}") - } - } - } - dependencies { testImplementation 'ch.qos.logback:logback-classic:1.2.3' } diff --git a/core/build.gradle b/core/build.gradle index 35cecd76ce8..fca331e9d3f 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -8,16 +8,7 @@ sourceSets { idea.module.testSourceDirs += sourceSets.jarFileTest.allSource.srcDirs -configurations { - shaded - [api, compileOnly, testCompile]*.extendsFrom shaded -} - shadowJar { - configurations = [project.configurations.shaded] - - mergeServiceFiles() - [ 'META-INF/NOTICE', 'META-INF/NOTICE.txt', @@ -77,23 +68,16 @@ dependencies { shaded 'org.awaitility:awaitility:4.1.0' + api platform('com.github.docker-java:docker-java-bom:3.2.12') shaded platform('com.github.docker-java:docker-java-bom:3.2.12') api "com.github.docker-java:docker-java-api" - shaded ('com.github.docker-java:docker-java-core') { - exclude(group: 'com.github.docker-java', module: 'docker-java-api') - exclude(group: 'com.github.docker-java', module: 'docker-java-transport') - exclude(group: 'com.fasterxml.jackson.core', module: 'jackson-annotations') - exclude(group: 'com.google.code.findbug') - exclude(group: 'org.slf4j') - exclude(group: 'org.apache.commons', module: 'commons-compress') - } + shaded 'com.github.docker-java:docker-java-core' - shaded ('com.github.docker-java:docker-java-transport-okhttp') { - exclude(group: 'com.github.docker-java', module: 'docker-java-core') - exclude(group: 'net.java.dev.jna') - exclude(group: 'org.slf4j') + shaded 'com.github.docker-java:docker-java-transport-okhttp', { + // TODO remove unused `net.java.dev.jna:jna-platform` from `docker-java-transport-okhttp` + exclude(group: 'net.java.dev.jna', module: 'jna-platform') } api 'com.github.docker-java:docker-java-transport-zerodep' @@ -102,9 +86,7 @@ dependencies { shaded 'org.glassfish.main.external:trilead-ssh2-repackaged:4.1.2' - shaded 'org.zeroturnaround:zt-exec:1.12', { - exclude(group: 'org.slf4j') - } + shaded 'org.zeroturnaround:zt-exec:1.12' testImplementation 'org.apache.httpcomponents:httpclient:4.5.9' testImplementation 'redis.clients:jedis:3.7.0' diff --git a/gradle/publishing.gradle b/gradle/publishing.gradle index e6bf5d2acbd..ffeb3cb38d4 100644 --- a/gradle/publishing.gradle +++ b/gradle/publishing.gradle @@ -57,27 +57,34 @@ publishing { def dependenciesNode = rootNode.appendNode('dependencies') - def addDependency = { dependency, scope -> - dependenciesNode.appendNode('dependency').with { - appendNode('groupId', dependency.group) - appendNode('artifactId', dependency.name) - appendNode('version', dependency.version) - appendNode('scope', scope) + def addDependencies = { Configuration configuration, scope -> + for (dependency in configuration.resolvedConfiguration.firstLevelModuleDependencies) { + if (dependency.configuration.startsWith("platform-")) { + continue + } + dependenciesNode.appendNode('dependency').with { + if (!dependency.moduleGroup || !dependency.moduleName || !dependency.moduleVersion) { + throw new IllegalStateException("Wrong dependency: $dependency") + } + appendNode('groupId', dependency.moduleGroup) + appendNode('artifactId', dependency.moduleName) + appendNode('version', dependency.moduleVersion) + appendNode('scope', scope) - if (dependency instanceof ModuleDependency && !dependency.excludeRules.empty) { - def excludesNode = appendNode('exclusions') - for (rule in dependency.excludeRules) { - excludesNode.appendNode('exclusion').with { - appendNode('groupId', rule.group) - appendNode('artifactId', rule.module) + if (dependency instanceof ModuleDependency && !dependency.excludeRules.empty) { + def excludesNode = appendNode('exclusions') + for (rule in dependency.excludeRules) { + excludesNode.appendNode('exclusion').with { + appendNode('groupId', rule.group) + appendNode('artifactId', rule.module) + } } } } } } - - project.configurations.api.dependencies.each { addDependency(it, 'compile') } - project.configurations.provided.dependencies.each { addDependency(it, 'provided') } + addDependencies(project.configurations.api, 'compile') + addDependencies(project.configurations.provided, 'provided') } } } diff --git a/gradle/shading.gradle b/gradle/shading.gradle new file mode 100644 index 00000000000..909989caf84 --- /dev/null +++ b/gradle/shading.gradle @@ -0,0 +1,58 @@ +import java.util.jar.JarFile + +apply plugin: 'com.github.johnrengelman.shadow' + +configurations { + shaded + [apiElements, implementation, compileOnly, testCompile]*.extendsFrom shaded +} +configurations.api.canBeResolved = true + +shadowJar { + configurations = [project.configurations.shaded] + archiveClassifier.set(null) + + mergeServiceFiles() +} + +project.afterEvaluate { + Set apiDependencies = project.configurations.api.resolvedConfiguration.resolvedArtifacts*.moduleVersion*.id*.module + + // Exclude `api` dependencies, to prevent them being included into the final JAR + shadowJar.dependencies { + for (id in apiDependencies) { + exclude(dependency("${id.group}:${id.name}")) + } + } + + // Inherit dependencies' relocators + shadowJar.relocators = configurations.api.dependencies.withType(ProjectDependency).collectMany { + return it.dependencyProject.tasks.findByName("shadowJar")?.relocators ?: [] + } + + // See https://github.com/johnrengelman/shadow/blob/5.0.0/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ConfigureShadowRelocation.groovy + Set packages = [] + + for (artifact in project.configurations.shaded.resolvedConfiguration.resolvedArtifacts) { + if (apiDependencies.contains(artifact.moduleVersion.id.module)) { + continue + } + + try (def jf = new JarFile(artifact.file)) { + for (entry in jf.entries()) { + def name = entry.name + if (name.endsWith(".class")) { + def index = name.lastIndexOf('/') + if (index != -1) { + packages.add(name.substring(0, index)) + } + } + } + } + } + for (pkg in packages) { + pkg = pkg.replaceAll('/', '.') + + tasks.shadowJar.relocate(pkg, "org.testcontainers.shaded.${pkg}") + } +} From f178e91623d3648ba3b61ee440f5bfc6fd92646f Mon Sep 17 00:00:00 2001 From: Simon Schneider <10846939+raynigon@users.noreply.github.com> Date: Thu, 23 Sep 2021 13:11:23 +0200 Subject: [PATCH 2098/5615] Make TimescaleDB available with JDBC syntax (#3891) --- docs/modules/databases/jdbc.md | 4 ++ .../jdbc/AbstractJDBCDriverTest.java | 4 +- .../TimescaleDBContainerProvider.java | 39 +++++++++++++ ...s.containers.JdbcDatabaseContainerProvider | 1 + .../containers/TimescaleDBContainerTest.java | 58 +++++++++++++++++++ .../TimescaleDBJDBCDriverTest.java | 22 +++++++ .../resources/somepath/init_timescaledb.sql | 13 +++++ 7 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 modules/postgresql/src/main/java/org/testcontainers/containers/TimescaleDBContainerProvider.java create mode 100644 modules/postgresql/src/test/java/org/testcontainers/containers/TimescaleDBContainerTest.java create mode 100644 modules/postgresql/src/test/java/org/testcontainers/jdbc/timescaledb/TimescaleDBJDBCDriverTest.java create mode 100644 modules/postgresql/src/test/resources/somepath/init_timescaledb.sql diff --git a/docs/modules/databases/jdbc.md b/docs/modules/databases/jdbc.md index 7adaf2f5e70..d402b5983b4 100644 --- a/docs/modules/databases/jdbc.md +++ b/docs/modules/databases/jdbc.md @@ -39,6 +39,10 @@ Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database `jdbc:tc:postgis:9.6-2.5:///databasename` +#### Using TimescaleDB + +`jdbc:tc:timescaldb:2.1.0-pg13:///databasename` + #### Using Trino `jdbc:tc:trino:352://localhost/memory/default` diff --git a/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java b/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java index 38ca2237567..580aad513e3 100644 --- a/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java +++ b/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java @@ -117,7 +117,9 @@ private void performTestForJDBCParamUsage(HikariDataSource dataSource) throws SQ String databaseQuery = "SELECT DATABASE()"; // Postgres does not have Database() as a function String databaseType = ConnectionUrl.newInstance(jdbcUrl).getDatabaseType(); - if (databaseType.equalsIgnoreCase("postgresql") || databaseType.equalsIgnoreCase("postgis")) { + if (databaseType.equalsIgnoreCase("postgresql") || + databaseType.equalsIgnoreCase("postgis") || + databaseType.equalsIgnoreCase("timescaledb")) { databaseQuery = "SELECT CURRENT_DATABASE()"; } diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/TimescaleDBContainerProvider.java b/modules/postgresql/src/main/java/org/testcontainers/containers/TimescaleDBContainerProvider.java new file mode 100644 index 00000000000..c9b81fccfe4 --- /dev/null +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/TimescaleDBContainerProvider.java @@ -0,0 +1,39 @@ +package org.testcontainers.containers; + +import org.testcontainers.jdbc.ConnectionUrl; +import org.testcontainers.utility.DockerImageName; + +/** + * Factory for TimescaleDB containers, which are a special flavour of PostgreSQL. + * + * @see https://docs.timescale.com/latest/introduction + */ +public class TimescaleDBContainerProvider extends JdbcDatabaseContainerProvider { + + private static final String NAME = "timescaledb"; + private static final String DEFAULT_TAG = "2.1.0-pg11"; + private static final DockerImageName DEFAULT_IMAGE = DockerImageName.parse("timescale/timescaledb").asCompatibleSubstituteFor("postgres"); + public static final String USER_PARAM = "user"; + public static final String PASSWORD_PARAM = "password"; + + + @Override + public boolean supports(String databaseType) { + return databaseType.equals(NAME); + } + + @Override + public JdbcDatabaseContainer newInstance() { + return newInstance(DEFAULT_TAG); + } + + @Override + public JdbcDatabaseContainer newInstance(String tag) { + return new PostgreSQLContainer(DEFAULT_IMAGE.withTag(tag)); + } + + @Override + public JdbcDatabaseContainer newInstance(ConnectionUrl connectionUrl) { + return newInstanceFromConnectionUrl(connectionUrl, USER_PARAM, PASSWORD_PARAM); + } +} diff --git a/modules/postgresql/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider b/modules/postgresql/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider index 79b2f29340a..05df9054522 100644 --- a/modules/postgresql/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider +++ b/modules/postgresql/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider @@ -1,2 +1,3 @@ org.testcontainers.containers.PostgreSQLContainerProvider org.testcontainers.containers.PostgisContainerProvider +org.testcontainers.containers.TimescaleDBContainerProvider diff --git a/modules/postgresql/src/test/java/org/testcontainers/containers/TimescaleDBContainerTest.java b/modules/postgresql/src/test/java/org/testcontainers/containers/TimescaleDBContainerTest.java new file mode 100644 index 00000000000..f3c73a4111b --- /dev/null +++ b/modules/postgresql/src/test/java/org/testcontainers/containers/TimescaleDBContainerTest.java @@ -0,0 +1,58 @@ +package org.testcontainers.containers; + +import org.junit.Test; +import org.testcontainers.db.AbstractContainerDatabaseTest; + +import java.sql.ResultSet; +import java.sql.SQLException; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; + +public class TimescaleDBContainerTest extends AbstractContainerDatabaseTest { + + @Test + public void testSimple() throws SQLException { + try (JdbcDatabaseContainer postgres = new TimescaleDBContainerProvider().newInstance()) { + postgres.start(); + + ResultSet resultSet = performQuery(postgres, "SELECT 1"); + int resultSetInt = resultSet.getInt(1); + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + } + } + + @Test + public void testCommandOverride() throws SQLException { + try (GenericContainer postgres = new TimescaleDBContainerProvider().newInstance().withCommand("postgres -c max_connections=42")) { + postgres.start(); + + ResultSet resultSet = performQuery((JdbcDatabaseContainer) postgres, "SELECT current_setting('max_connections')"); + String result = resultSet.getString(1); + assertEquals("max_connections should be overriden", "42", result); + } + } + + @Test + public void testUnsetCommand() throws SQLException { + try (GenericContainer postgres = new TimescaleDBContainerProvider().newInstance().withCommand("postgres -c max_connections=42").withCommand()) { + postgres.start(); + + ResultSet resultSet = performQuery((JdbcDatabaseContainer) postgres, "SELECT current_setting('max_connections')"); + String result = resultSet.getString(1); + assertNotEquals("max_connections should not be overriden", "42", result); + } + } + + @Test + public void testExplicitInitScript() throws SQLException { + try (JdbcDatabaseContainer postgres = new TimescaleDBContainerProvider().newInstance().withInitScript("somepath/init_timescaledb.sql")) { + postgres.start(); + + ResultSet resultSet = performQuery(postgres, "SELECT foo FROM bar"); + + String firstColumnValue = resultSet.getString(1); + assertEquals("Value from init script should equal real value", "hello world", firstColumnValue); + } + } +} diff --git a/modules/postgresql/src/test/java/org/testcontainers/jdbc/timescaledb/TimescaleDBJDBCDriverTest.java b/modules/postgresql/src/test/java/org/testcontainers/jdbc/timescaledb/TimescaleDBJDBCDriverTest.java new file mode 100644 index 00000000000..8a6eaa3d04b --- /dev/null +++ b/modules/postgresql/src/test/java/org/testcontainers/jdbc/timescaledb/TimescaleDBJDBCDriverTest.java @@ -0,0 +1,22 @@ +package org.testcontainers.jdbc.timescaledb; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.testcontainers.jdbc.AbstractJDBCDriverTest; + +import java.util.EnumSet; + +import static java.util.Arrays.asList; + +@RunWith(Parameterized.class) +public class TimescaleDBJDBCDriverTest extends AbstractJDBCDriverTest { + + @Parameterized.Parameters(name = "{index} - {0}") + public static Iterable data() { + return asList( + new Object[][]{ + {"jdbc:tc:timescaledb://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, + {"jdbc:tc:timescaledb:2.1.0-pg13://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, + }); + } +} diff --git a/modules/postgresql/src/test/resources/somepath/init_timescaledb.sql b/modules/postgresql/src/test/resources/somepath/init_timescaledb.sql new file mode 100644 index 00000000000..da11604cf10 --- /dev/null +++ b/modules/postgresql/src/test/resources/somepath/init_timescaledb.sql @@ -0,0 +1,13 @@ +-- Extend the database with TimescaleDB +CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE; + +CREATE TABLE bar +( + foo VARCHAR(255), + time TIMESTAMPTZ NOT NULL +); + +SELECT create_hypertable('bar', 'time'); + +INSERT INTO bar (time, foo) +VALUES (CURRENT_TIMESTAMP, 'hello world'); From 4dd08729a9ee0472cd45bb307631e728571cf4f8 Mon Sep 17 00:00:00 2001 From: PROgrm_JARvis Date: Thu, 23 Sep 2021 14:15:12 +0300 Subject: [PATCH 2099/5615] Fix dangling constructor reference in deprecation note (#4443) --- .../java/org/testcontainers/containers/PostgreSQLContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index 6c16bb8fe61..2b905bd3d37 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -35,7 +35,7 @@ public class PostgreSQLContainer> extends private static final String QUERY_PARAM_SEPARATOR = "&"; /** - * @deprecated use {@link PostgreSQLContainer(DockerImageName)} instead + * @deprecated use {@link #PostgreSQLContainer(DockerImageName)} or {@link #PostgreSQLContainer(String)} instead */ @Deprecated public PostgreSQLContainer() { From b88d136762b77cb4ac4b1db98ab4e1ddc711b274 Mon Sep 17 00:00:00 2001 From: John Athanaselis Date: Thu, 23 Sep 2021 16:57:41 +0300 Subject: [PATCH 2100/5615] Randomize naming for createVolumeDirectory method (#4195) Also marks createVolumeDirectory method as deprecated Co-authored-by: Richard North --- .../java/org/testcontainers/containers/GenericContainer.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 50e4b927617..fb82832675b 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -88,6 +88,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -647,8 +648,9 @@ protected Logger logger() { * @param temporary is the volume directory temporary? If true, the directory will be deleted on JVM shutdown. * @return path to the volume directory */ + @Deprecated protected Path createVolumeDirectory(boolean temporary) { - Path directory = new File(".tmp-volume-" + System.currentTimeMillis()).toPath(); + Path directory = new File(".tmp-volume-" + UUID.randomUUID()).toPath(); PathUtils.mkdirp(directory); if (temporary) Runtime.getRuntime().addShutdownHook(new Thread(DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, () -> { From 8f312b4fb3f29c8d37cc55f3640693afb912523f Mon Sep 17 00:00:00 2001 From: Piotr Findeisen Date: Thu, 23 Sep 2021 16:00:25 +0200 Subject: [PATCH 2101/5615] Avoid clobbering already configured path (#2957) --- .../java/org/testcontainers/containers/GenericContainer.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index fb82832675b..6d9551b8d6e 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -1284,6 +1284,9 @@ public SELF withWorkingDirectory(String workDir) { */ @Override public SELF withCopyFileToContainer(MountableFile mountableFile, String containerPath) { + if (copyToFileContainerPathMap.containsKey(mountableFile)) { + throw new IllegalStateException("Path already configured for copy: " + mountableFile); + } copyToFileContainerPathMap.put(mountableFile, containerPath); return self(); } From 29a438d84e31dd193bd36614f98b76b6a0f89b90 Mon Sep 17 00:00:00 2001 From: Kyle Aure Date: Mon, 27 Sep 2021 03:55:59 -0500 Subject: [PATCH 2102/5615] Modernize Oracle Container Logic (#4402) --- modules/oracle-xe/build.gradle | 2 + .../containers/OracleContainer.java | 87 ++++++++++-- .../containers/jdbc/OracleJDBCDriverTest.java | 4 - .../junit/oracle/SimpleOracleTest.java | 133 ++++++++++++++++-- 4 files changed, 203 insertions(+), 23 deletions(-) diff --git a/modules/oracle-xe/build.gradle b/modules/oracle-xe/build.gradle index 5a6040ac81f..38146d4dd01 100644 --- a/modules/oracle-xe/build.gradle +++ b/modules/oracle-xe/build.gradle @@ -5,4 +5,6 @@ dependencies { testImplementation project(':jdbc-test') testImplementation 'com.oracle.ojdbc:ojdbc8:19.3.0.0' + + compileOnly 'org.jetbrains:annotations:21.0.1' } diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java index ccd705f5af9..c833689e692 100644 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java @@ -2,19 +2,24 @@ import com.google.common.collect.Sets; import org.apache.commons.lang.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.utility.DockerImageName; +import java.time.Duration; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.concurrent.Future; +import static java.time.temporal.ChronoUnit.SECONDS; +import static java.util.Collections.singleton; + public class OracleContainer extends JdbcDatabaseContainer { public static final String NAME = "oracle"; private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("gvenzl/oracle-xe"); - static final String DEFAULT_TAG = "18.4.0-slim"; static final String IMAGE = DEFAULT_IMAGE_NAME.getUnversionedPart(); @@ -23,10 +28,24 @@ public class OracleContainer extends JdbcDatabaseContainer { private static final int DEFAULT_STARTUP_TIMEOUT_SECONDS = 240; private static final int DEFAULT_CONNECT_TIMEOUT_SECONDS = 120; - private static final List ORACLE_SYSTEM_USERS = Arrays.asList("system", "sys"); - private String username = "test"; - private String password = "test"; + // Container defaults + static final String DEFAULT_DATABASE_NAME = "xepdb1"; + static final String DEFAULT_SID = "xe"; + static final String DEFAULT_SYSTEM_USER = "system"; + static final String DEFAULT_SYS_USER = "sys"; + + // Test container defaults + static final String APP_USER = "test"; + static final String APP_USER_PASSWORD = "test"; + + // Restricted user and database names + private static final List ORACLE_SYSTEM_USERS = Arrays.asList(DEFAULT_SYSTEM_USER, DEFAULT_SYS_USER); + + private String databaseName = DEFAULT_DATABASE_NAME; + private String username = APP_USER; + private String password = APP_USER_PASSWORD; + private boolean usingSid = false; /** * @deprecated use {@link OracleContainer(DockerImageName)} instead @@ -42,6 +61,7 @@ public OracleContainer(String dockerImageName) { public OracleContainer(final DockerImageName dockerImageName) { super(dockerImageName); + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); preconfigure(); } @@ -51,14 +71,24 @@ public OracleContainer(Future dockerImageName) { } private void preconfigure() { - withStartupTimeoutSeconds(DEFAULT_STARTUP_TIMEOUT_SECONDS); + this.waitStrategy = new LogMessageWaitStrategy() + .withRegEx(".*DATABASE IS READY TO USE!.*\\s") + .withTimes(1) + .withStartupTimeout(Duration.of(DEFAULT_STARTUP_TIMEOUT_SECONDS, SECONDS)); + withConnectTimeoutSeconds(DEFAULT_CONNECT_TIMEOUT_SECONDS); addExposedPorts(ORACLE_PORT, APEX_HTTP_PORT); } + @Override + protected void waitUntilContainerStarted() { + getWaitStrategy().waitUntilReady(this); + } + + @NotNull @Override public Set getLivenessCheckPortNumbers() { - return Sets.newHashSet(ORACLE_PORT); + return singleton(getMappedPort(ORACLE_PORT)); } @Override @@ -68,12 +98,15 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return "jdbc:oracle:thin:" + getUsername() + "/" + getPassword() + "@" + getHost() + ":" + getOraclePort() + "/xepdb1"; + return isUsingSid() ? + "jdbc:oracle:thin:" + "@" + getHost() + ":" + getOraclePort() + ":" + getSid() : + "jdbc:oracle:thin:" + "@" + getHost() + ":" + getOraclePort() + "/" + getDatabaseName(); } @Override public String getUsername() { - return username; + // An application user is tied to the database, and therefore not authenticated to connect to SID. + return isUsingSid() ? DEFAULT_SYSTEM_USER : username; } @Override @@ -81,6 +114,15 @@ public String getPassword() { return password; } + @Override + public String getDatabaseName() { + return databaseName; + } + + protected boolean isUsingSid() { + return usingSid; + } + @Override public OracleContainer withUsername(String username) { if (StringUtils.isEmpty(username)) { @@ -102,14 +144,33 @@ public OracleContainer withPassword(String password) { return self(); } + @Override + public OracleContainer withDatabaseName(String databaseName) { + if (StringUtils.isEmpty(databaseName)) { + throw new IllegalArgumentException("Database name cannot be null or empty"); + } + + if (DEFAULT_DATABASE_NAME.equals(databaseName.toLowerCase())) { + throw new IllegalArgumentException("Database name cannot be set to " + DEFAULT_DATABASE_NAME); + } + + this.databaseName = databaseName; + return self(); + } + + public OracleContainer usingSid() { + this.usingSid = true; + return self(); + } + @Override public OracleContainer withUrlParam(String paramName, String paramValue) { - throw new UnsupportedOperationException("The OracleDb does not support this"); + throw new UnsupportedOperationException("The Oracle Database driver does not support this"); } @SuppressWarnings("SameReturnValue") public String getSid() { - return "xe"; + return DEFAULT_SID; } public Integer getOraclePort() { @@ -129,6 +190,12 @@ public String getTestQueryString() { @Override protected void configure() { withEnv("ORACLE_PASSWORD", password); + + // Only set ORACLE_DATABASE if different than the default. + if(databaseName != DEFAULT_DATABASE_NAME) { + withEnv("ORACLE_DATABASE", databaseName); + } + withEnv("APP_USER", username); withEnv("APP_USER_PASSWORD", password); } diff --git a/modules/oracle-xe/src/test/java/org/testcontainers/containers/jdbc/OracleJDBCDriverTest.java b/modules/oracle-xe/src/test/java/org/testcontainers/containers/jdbc/OracleJDBCDriverTest.java index fbe5ce95a05..79dc35deb64 100644 --- a/modules/oracle-xe/src/test/java/org/testcontainers/containers/jdbc/OracleJDBCDriverTest.java +++ b/modules/oracle-xe/src/test/java/org/testcontainers/containers/jdbc/OracleJDBCDriverTest.java @@ -12,10 +12,6 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -/** - * @author gusohal - */ -@Ignore public class OracleJDBCDriverTest { @Test diff --git a/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java b/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java index 7d026924442..2dd82c437a4 100644 --- a/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java +++ b/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java @@ -1,5 +1,8 @@ package org.testcontainers.junit.oracle; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + import org.junit.Test; import org.testcontainers.containers.OracleContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; @@ -8,26 +11,138 @@ import java.sql.ResultSet; import java.sql.SQLException; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; - public class SimpleOracleTest extends AbstractContainerDatabaseTest { public static final DockerImageName ORACLE_DOCKER_IMAGE_NAME = DockerImageName.parse("gvenzl/oracle-xe:18.4.0-slim"); + private void runTest(OracleContainer container, String databaseName, String username, String password) throws SQLException { + //Test config was honored + assertEquals(databaseName, container.getDatabaseName()); + assertEquals(username, container.getUsername()); + assertEquals(password, container.getPassword()); + + //Test we can get a connection + container.start(); + ResultSet resultSet = performQuery(container, "SELECT 1 FROM dual"); + int resultSetInt = resultSet.getInt(1); + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + } + + @Test + public void testDefaultSettings() throws SQLException { + try ( + OracleContainer oracle = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME); + ) { + runTest(oracle, "xepdb1", "test", "test"); + + // Match against the last '/' + String urlSuffix = oracle.getJdbcUrl().split("(\\/)(?!.*\\/)", 2)[1]; + assertEquals("xepdb1", urlSuffix); + } + } + + @Test + public void testPluggableDatabase() throws SQLException { + try ( + OracleContainer oracle = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME) + .withDatabaseName("testDB") + ) { + runTest(oracle, "testDB", "test", "test"); + } + } + + @Test + public void testPluggableDatabaseAndCustomUser() throws SQLException { + try ( + OracleContainer oracle = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME) + .withDatabaseName("testDB") + .withUsername("testUser") + .withPassword("testPassword") + ) { + runTest(oracle, "testDB", "testUser", "testPassword"); + } + } + @Test - public void testSimple() throws SQLException { + public void testCustomUser() throws SQLException { + try ( + OracleContainer oracle = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME) + .withUsername("testUser") + .withPassword("testPassword") + ) { + runTest(oracle, "xepdb1", "testUser", "testPassword"); + } + } + @Test + public void testSID() throws SQLException { try ( OracleContainer oracle = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME) - .withUsername("baz") - .withPassword("bar") + .usingSid(); ) { - oracle.start(); - ResultSet resultSet = performQuery(oracle, "SELECT 1 FROM dual"); + runTest(oracle, "xepdb1", "system", "test"); + + // Match against the last ':' + String urlSuffix = oracle.getJdbcUrl().split("(\\:)(?!.*\\:)", 2)[1]; + assertEquals("xe", urlSuffix); + } + } + + @Test + public void testSIDAndCustomPassword() throws SQLException { + try ( + OracleContainer oracle = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME) + .usingSid() + .withPassword("testPassword"); + ) { + runTest(oracle, "xepdb1", "system", "testPassword"); + } + } + + @Test + public void testErrorPaths() throws SQLException { + try (OracleContainer oracle = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME)) { + try { + oracle.withDatabaseName("XEPDB1"); + fail("Should not have been able to set database name to xepdb1."); + } catch (IllegalArgumentException e) { + //expected + } + + try { + oracle.withDatabaseName(""); + fail("Should not have been able to set database name to nothing."); + } catch (IllegalArgumentException e) { + //expected + } + + try { + oracle.withUsername("SYSTEM"); + fail("Should not have been able to set username to system."); + } catch (IllegalArgumentException e) { + //expected + } + + try { + oracle.withUsername("SYS"); + fail("Should not have been able to set username to sys."); + } catch (IllegalArgumentException e) { + //expected + } - int resultSetInt = resultSet.getInt(1); + try { + oracle.withUsername(""); + fail("Should not have been able to set username to nothing."); + } catch (IllegalArgumentException e) { + //expected + } - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + try { + oracle.withPassword(""); + fail("Should not have been able to set password to nothing."); + } catch (IllegalArgumentException e) { + //expected + } } } } From 532c0b711bef4443180ae3a41da9d1d6dfb825d1 Mon Sep 17 00:00:00 2001 From: Bennet Schulz Date: Tue, 28 Sep 2021 12:00:28 +0200 Subject: [PATCH 2103/5615] Add togglz as testcontainers user (#4484) --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index af5bb4edcdb..65d417db2af 100644 --- a/docs/index.md +++ b/docs/index.md @@ -175,7 +175,7 @@ A huge thank you to our sponsors: ([query](https://github.com/search?l=Maven+POM&q=org%3Ajenkinsci+testcontainers&type=Code)). * [Elastic](https://www.elastic.co) - Integration testing of the Java APM agent * [Alkira](https://www.alkira.com/) - Testing of multiple micro-services using Kafka, PostgreSQL, Apache Zookeeper, Etcd and so on. - +* [Togglz](https://www.togglz.org/) - Feature Flags for the Java platform ## License From dd9372515faaea7b8e5ab914fa8010d7afa29024 Mon Sep 17 00:00:00 2001 From: pioorg Date: Mon, 4 Oct 2021 11:51:18 +0200 Subject: [PATCH 2104/5615] Switch from AdoptOpenJDK to Temurin and latest JDK 8. (#4537) * Switched from AdoptOpenJDK to Temurin and JDK version 8.0.302. --- .github/workflows/ci-examples.yml | 8 ++++---- .github/workflows/ci.yml | 8 ++++---- .github/workflows/release.yml | 3 ++- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index 88587a6ab61..3fbdea447b9 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -17,8 +17,8 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-java@v2.3.0 with: - java-version: '8.0.292+10' - distribution: adopt-hotspot + java-version: '8.0.302' + distribution: temurin - name: Cache Gradle Home files uses: actions/cache@v2.1.6 continue-on-error: true @@ -45,8 +45,8 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-java@v2.3.0 with: - java-version: '8.0.292+10' - distribution: adopt-hotspot + java-version: '8.0.302' + distribution: temurin - name: Cache Gradle Home files uses: actions/cache@v2.1.6 continue-on-error: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a6ae55e16b4..c3a85500ff1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,8 +25,8 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-java@v2.3.0 with: - java-version: '8.0.292+10' - distribution: adopt-hotspot + java-version: '8.0.302' + distribution: temurin - name: Cache Gradle Home files uses: actions/cache@v2.1.6 with: @@ -51,8 +51,8 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-java@v2.3.0 with: - java-version: '8.0.292+10' - distribution: adopt-hotspot + java-version: '8.0.302' + distribution: temurin - name: Cache Gradle Home files uses: actions/cache@v2.1.6 continue-on-error: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 73b3b2152c9..7b9d480ec37 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,7 +15,8 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-java@v2.3.0 with: - java-version: '1.8' + java-version: '8.0.302' + distribution: temurin - name: Clear existing docker image cache run: docker image prune -af From cad5cda62859c408962caa1ed4b3fdd756979922 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Oct 2021 09:52:47 +0000 Subject: [PATCH 2105/5615] Bump actions/setup-java from 2.3.0 to 2.3.1 Bumps [actions/setup-java](https://github.com/actions/setup-java) from 2.3.0 to 2.3.1. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v2.3.0...v2.3.1) --- updated-dependencies: - dependency-name: actions/setup-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- .github/workflows/release.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index 3fbdea447b9..a1f81f49bef 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -15,7 +15,7 @@ jobs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.3.0 + - uses: actions/setup-java@v2.3.1 with: java-version: '8.0.302' distribution: temurin @@ -43,7 +43,7 @@ jobs: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.3.0 + - uses: actions/setup-java@v2.3.1 with: java-version: '8.0.302' distribution: temurin diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c3a85500ff1..2cbfc92b372 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.3.0 + - uses: actions/setup-java@v2.3.1 with: java-version: '8.0.302' distribution: temurin @@ -49,7 +49,7 @@ jobs: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.3.0 + - uses: actions/setup-java@v2.3.1 with: java-version: '8.0.302' distribution: temurin diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7b9d480ec37..4e1e9d7c5a1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.3.0 + - uses: actions/setup-java@v2.3.1 with: java-version: '8.0.302' distribution: temurin From d44e61637b5c9834d9abbfa1c614ebff884fc470 Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 4 Oct 2021 12:34:46 +0100 Subject: [PATCH 2106/5615] Prevent Compose image pre-fetching from pulling all tags when tag is absent (#4538) --- .../containers/DockerComposeFiles.java | 24 +++++++++---------- .../containers/DockerComposeFilesTest.java | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeFiles.java b/core/src/main/java/org/testcontainers/containers/DockerComposeFiles.java index 15d035e3673..76754df5118 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeFiles.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeFiles.java @@ -1,43 +1,43 @@ package org.testcontainers.containers; +import org.testcontainers.utility.DockerImageName; + import java.io.File; +import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Set; import java.util.stream.Collectors; public class DockerComposeFiles { - private List parsedComposeFiles; + private final List parsedComposeFiles; public DockerComposeFiles(List composeFiles) { this.parsedComposeFiles = composeFiles.stream().map(ParsedDockerComposeFile::new).collect(Collectors.toList()); } public Set getDependencyImages() { - Map> mergedServiceNameToImageNames = mergeServiceDependencyImageNames(); return getImageNames(mergedServiceNameToImageNames); } private Map> mergeServiceDependencyImageNames() { - Map> mergedServiceNameToImageNames = new HashMap(); + Map> mergedServiceNameToImageNames = new HashMap<>(); for (ParsedDockerComposeFile parsedComposeFile : parsedComposeFiles) { - for (Entry> entry : parsedComposeFile.getServiceNameToImageNames().entrySet()) { - mergedServiceNameToImageNames.put(entry.getKey(), entry.getValue()); - } + mergedServiceNameToImageNames.putAll(parsedComposeFile.getServiceNameToImageNames()); } return mergedServiceNameToImageNames; } private Set getImageNames(Map> serviceToImageNames) { - Set imageNames = new HashSet<>(); - serviceToImageNames.values().stream().forEach(imageNames::addAll); - return imageNames; + return serviceToImageNames.values().stream() + .flatMap(Collection::stream) + // Pass through DockerImageName to convert image names to canonical form (e.g. making implicit latest tag explicit) + .map(DockerImageName::parse) + .map(DockerImageName::asCanonicalNameString) + .collect(Collectors.toSet()); } - } diff --git a/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java b/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java index 81586c8290b..c47db0c4b66 100644 --- a/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java +++ b/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java @@ -11,7 +11,7 @@ public class DockerComposeFilesTest { public void shouldGetDependencyImages() { DockerComposeFiles dockerComposeFiles = new DockerComposeFiles(Lists.newArrayList(new File("src/test/resources/docker-compose-imagename-parsing-v2.yml"))); Assertions.assertThat(dockerComposeFiles.getDependencyImages()) - .containsExactlyInAnyOrder("postgres", "redis", "mysql"); + .containsExactlyInAnyOrder("postgres:latest", "redis:latest", "mysql:latest"); } @Test @@ -20,6 +20,6 @@ public void shouldGetDependencyImagesWhenOverriding() { Lists.newArrayList(new File("src/test/resources/docker-compose-imagename-overriding-a.yml"), new File("src/test/resources/docker-compose-imagename-overriding-b.yml"))); Assertions.assertThat(dockerComposeFiles.getDependencyImages()) - .containsExactlyInAnyOrder("alpine:3.14", "redis:b", "mysql:b", "aservice"); + .containsExactlyInAnyOrder("alpine:3.14", "redis:b", "mysql:b", "aservice:latest"); } } From 3a5793d2543568ced9a6202fc2c8db06c73e1bdd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Oct 2021 08:23:56 +0000 Subject: [PATCH 2107/5615] Bump okhttp from 4.9.1 to 4.9.2 in /examples Bumps [okhttp](https://github.com/square/okhttp) from 4.9.1 to 4.9.2. - [Release notes](https://github.com/square/okhttp/releases) - [Changelog](https://github.com/square/okhttp/blob/master/CHANGELOG.md) - [Commits](https://github.com/square/okhttp/compare/parent-4.9.1...parent-4.9.2) --- updated-dependencies: - dependency-name: com.squareup.okhttp3:okhttp dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index ff1b2995e02..604f04594c0 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' - implementation 'com.squareup.okhttp3:okhttp:4.9.1' + implementation 'com.squareup.okhttp3:okhttp:4.9.2' implementation 'org.json:json:20210307' testImplementation 'org.postgresql:postgresql:42.2.23' testImplementation 'ch.qos.logback:logback-classic:1.2.5' From 472e7c8b808779cf14362ce4c65425b7bb0cf467 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Oct 2021 08:23:58 +0000 Subject: [PATCH 2108/5615] Bump junit-jupiter-api from 5.7.2 to 5.8.1 in /modules/junit-jupiter Bumps [junit-jupiter-api](https://github.com/junit-team/junit5) from 5.7.2 to 5.8.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.7.2...r5.8.1) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-api dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 03abb675f3b..798d295c1f0 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -2,7 +2,7 @@ description = "Testcontainers :: JUnit Jupiter Extension" dependencies { api project(':testcontainers') - api 'org.junit.jupiter:junit-jupiter-api:5.7.2' + api 'org.junit.jupiter:junit-jupiter-api:5.8.1' testImplementation project(':mysql') testImplementation project(':postgresql') From bbe2b6e854db1698531e30511137e9faa9161d75 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Oct 2021 08:24:04 +0000 Subject: [PATCH 2109/5615] Bump org.jetbrains.kotlin.plugin.spring in /examples Bumps org.jetbrains.kotlin.plugin.spring from 1.5.30 to 1.5.31. --- updated-dependencies: - dependency-name: org.jetbrains.kotlin.plugin.spring dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 6096fcb37b4..fe0ed6553c3 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,7 +1,7 @@ plugins { id("org.springframework.boot") version "2.5.4" id("org.jetbrains.kotlin.jvm") version "1.5.30" - id("org.jetbrains.kotlin.plugin.spring") version "1.5.30" + id("org.jetbrains.kotlin.plugin.spring") version "1.5.31" } apply plugin: 'io.spring.dependency-management' From d2e2ef25d9a6ebf3dba17d36d9f39d7f012a6ea1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Oct 2021 08:24:08 +0000 Subject: [PATCH 2110/5615] Bump postgresql from 42.2.23 to 42.2.24 in /modules/junit-jupiter Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.23 to 42.2.24. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.23...REL42.2.24) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 03abb675f3b..f4f9bdd2570 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'org.assertj:assertj-core:3.20.2' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.7.2' - testRuntimeOnly 'org.postgresql:postgresql:42.2.23' + testRuntimeOnly 'org.postgresql:postgresql:42.2.24' testRuntimeOnly 'mysql:mysql-connector-java:8.0.26' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.2' } From f5bdb1bc4c5838e5448586051ff1aed4db02111d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Oct 2021 08:24:09 +0000 Subject: [PATCH 2111/5615] Bump org.springframework.boot from 2.5.4 to 2.5.5 in /examples Bumps [org.springframework.boot](https://github.com/spring-projects/spring-boot) from 2.5.4 to 2.5.5. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.5.4...v2.5.5) --- updated-dependencies: - dependency-name: org.springframework.boot dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot-kotlin-redis/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index 49893845273..48f9ed91b17 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.5.4' + id 'org.springframework.boot' version '2.5.5' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 6096fcb37b4..c48fcb9ad65 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,5 +1,5 @@ plugins { - id("org.springframework.boot") version "2.5.4" + id("org.springframework.boot") version "2.5.5" id("org.jetbrains.kotlin.jvm") version "1.5.30" id("org.jetbrains.kotlin.plugin.spring") version "1.5.30" } diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index 3f93d3c153c..20ec4efd463 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.5.4' + id 'org.springframework.boot' version '2.5.5' } apply plugin: 'io.spring.dependency-management' From e44062ccaa4841deb511fd8b2f2217f7d7b481cf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Oct 2021 08:24:10 +0000 Subject: [PATCH 2112/5615] Bump postgresql from 42.2.23 to 42.2.24 in /modules/spock Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.23 to 42.2.24. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.23...REL42.2.24) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index f510836c4b5..3fc86d7877b 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'com.zaxxer:HikariCP:5.0.0' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' - testRuntimeOnly 'org.postgresql:postgresql:42.2.23' + testRuntimeOnly 'org.postgresql:postgresql:42.2.24' testRuntimeOnly 'mysql:mysql-connector-java:8.0.26' testCompileClasspath 'org.jetbrains:annotations:22.0.0' From cb0d8eca5cfa9330a7054a7951805b026128b7e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Oct 2021 08:24:16 +0000 Subject: [PATCH 2113/5615] Bump r2dbc-mariadb from 1.0.2 to 1.0.3 in /modules/mariadb Bumps [r2dbc-mariadb](https://github.com/mariadb-corporation/mariadb-connector-r2dbc) from 1.0.2 to 1.0.3. - [Release notes](https://github.com/mariadb-corporation/mariadb-connector-r2dbc/releases) - [Changelog](https://github.com/mariadb-corporation/mariadb-connector-r2dbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/mariadb-corporation/mariadb-connector-r2dbc/commits) --- updated-dependencies: - dependency-name: org.mariadb:r2dbc-mariadb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 037573f6143..e24fe293ff1 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -7,7 +7,7 @@ dependencies { api project(':jdbc') compileOnly project(':r2dbc') - compileOnly 'org.mariadb:r2dbc-mariadb:1.0.2' + compileOnly 'org.mariadb:r2dbc-mariadb:1.0.3' testImplementation project(':jdbc-test') testImplementation 'org.mariadb.jdbc:mariadb-java-client:2.7.4' From a1269586b1bfc5c3a68344950bafd7e5bfa3521b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Oct 2021 08:24:18 +0000 Subject: [PATCH 2114/5615] Bump solr-solrj from 8.9.0 to 8.10.0 in /examples Bumps solr-solrj from 8.9.0 to 8.10.0. --- updated-dependencies: - dependency-name: org.apache.solr:solr-solrj dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/solr-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/solr-container/build.gradle b/examples/solr-container/build.gradle index d416c4d8c45..373ece1df60 100644 --- a/examples/solr-container/build.gradle +++ b/examples/solr-container/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly "org.projectlombok:lombok:1.18.20" annotationProcessor "org.projectlombok:lombok:1.18.16" - implementation 'org.apache.solr:solr-solrj:8.9.0' + implementation 'org.apache.solr:solr-solrj:8.10.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.testcontainers:solr' From 6c45f2ef98720991e4decd6bec16c21c14b3c67e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Oct 2021 08:24:20 +0000 Subject: [PATCH 2115/5615] Bump transport from 7.14.1 to 7.15.0 in /modules/elasticsearch Bumps [transport](https://github.com/elastic/elasticsearch) from 7.14.1 to 7.15.0. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.14.1...v7.15.0) --- updated-dependencies: - dependency-name: org.elasticsearch.client:transport dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 183b2428ba8..c70fd363f55 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,6 +3,6 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') testImplementation "org.elasticsearch.client:elasticsearch-rest-client:7.14.1" - testImplementation "org.elasticsearch.client:transport:7.14.1" + testImplementation "org.elasticsearch.client:transport:7.15.0" testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 6ca83c0cb99784a7e636d093fd22ce2c91a4be63 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Oct 2021 08:24:23 +0000 Subject: [PATCH 2116/5615] Bump postgresql from 42.2.23 to 42.2.24 in /modules/postgresql Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.23 to 42.2.24. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.23...REL42.2.24) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 1842a0e7dd7..5c25e6243ef 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -11,7 +11,7 @@ dependencies { testImplementation project(':jdbc-test') testImplementation project(':test-support') - testImplementation 'org.postgresql:postgresql:42.2.23' + testImplementation 'org.postgresql:postgresql:42.2.24' testImplementation testFixtures(project(':r2dbc')) testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.5.RELEASE' From 8bfe5a61705eae285e948f740f8ef33b5bd6190f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Oct 2021 08:24:27 +0000 Subject: [PATCH 2117/5615] Bump aws-java-sdk-s3 from 1.12.62 to 1.12.80 in /modules/localstack Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.62 to 1.12.80. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.62...1.12.80) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 1ccc305595d..a4934425027 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Localstack" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.62' - testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.37' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.80' + testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.80' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.62' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.60' testImplementation 'software.amazon.awssdk:s3:2.17.34' From abf18468c2b3c8ec395f07fe91cd3afd449e28d2 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Tue, 5 Oct 2021 16:10:41 +0200 Subject: [PATCH 2118/5615] Give EnvironmentAndSystemPropertyClientProviderStrategy the highest priority (#4472) --- .../DockerClientProviderStrategy.java | 245 ++++++++++-------- 1 file changed, 133 insertions(+), 112 deletions(-) diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index 52700df22f3..b595ddba6ca 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -28,10 +28,12 @@ import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; +import java.util.stream.Collectors; import java.util.stream.Stream; /** @@ -113,120 +115,139 @@ public static DockerClientProviderStrategy getFirstValidStrategy(List configurationFailures = new ArrayList<>(); + List allStrategies = new ArrayList<>(); + + // The environment has the highest priority + allStrategies.add(new EnvironmentAndSystemPropertyClientProviderStrategy()); + + // Next strategy to try out is the one configured using the Testcontainers configuration mechanism + loadConfiguredStrategy().ifPresent(allStrategies::add); + + // Finally, add all other strategies ordered by their internal priority + strategies + .stream() + .sorted(Comparator.comparing(DockerClientProviderStrategy::getPriority).reversed()) + .collect(Collectors.toCollection(() -> allStrategies)); + + + Predicate distinctStrategyClassPredicate = new Predicate() { + final Set> classes = new HashSet<>(); + + @Override + public boolean test(DockerClientProviderStrategy dockerClientProviderStrategy) { + return classes.add(dockerClientProviderStrategy.getClass()); + } + }; + + return allStrategies + .stream() + .filter(distinctStrategyClassPredicate) + .filter(DockerClientProviderStrategy::isApplicable) + .filter(strategy -> tryOutStrategy(configurationFailures, strategy)) + .findFirst() + .orElseThrow(() -> { + log.error("Could not find a valid Docker environment. Please check configuration. Attempted configurations were:"); + for (String failureMessage : configurationFailures) { + log.error(" " + failureMessage); + } + log.error("As no valid configuration was found, execution cannot continue"); - String dockerClientStrategyClassName = TestcontainersConfiguration.getInstance().getDockerClientStrategyClassName(); - return Stream - .concat( - Stream - .of(dockerClientStrategyClassName) - .filter(Objects::nonNull) - .flatMap(it -> { - try { - Class strategyClass = (Class) Thread.currentThread().getContextClassLoader().loadClass(it); - return Stream.of(strategyClass.newInstance()); - } catch (ClassNotFoundException e) { - log.warn("Can't instantiate a strategy from {} (ClassNotFoundException). " + - "This probably means that cached configuration refers to a client provider " + - "class that is not available in this version of Testcontainers. Other " + - "strategies will be tried instead.", it); - return Stream.empty(); - } catch (InstantiationException | IllegalAccessException e) { - log.warn("Can't instantiate a strategy from {}", it, e); - return Stream.empty(); - } - }) - // Ignore persisted strategy if it's not persistable anymore - .filter(DockerClientProviderStrategy::isPersistable) - .peek(strategy -> log.info("Loaded {} from ~/.testcontainers.properties, will try it first", strategy.getClass().getName())), - strategies - .stream() - .sorted(Comparator.comparing(DockerClientProviderStrategy::getPriority).reversed()) - ) - .filter(new Predicate() { - - final Set> classes = new HashSet<>(); - - @Override - public boolean test(DockerClientProviderStrategy dockerClientProviderStrategy) { - return classes.add(dockerClientProviderStrategy.getClass()); - } - }) - .filter(DockerClientProviderStrategy::isApplicable) - .flatMap(strategy -> { - try { - DockerClient dockerClient = strategy.getDockerClient(); - - Info info; - try { - info = Unreliables.retryUntilSuccess(TestcontainersConfiguration.getInstance().getClientPingTimeout(), TimeUnit.SECONDS, () -> { - return strategy.PING_RATE_LIMITER.getWhenReady(() -> { - log.debug("Pinging docker daemon..."); - return dockerClient.infoCmd().exec(); - }); - }); - } catch (TimeoutException e) { - IOUtils.closeQuietly(dockerClient); - throw e; - } - log.info("Found Docker environment with {}", strategy.getDescription()); - log.debug( - "Transport type: '{}', Docker host: '{}'", - TestcontainersConfiguration.getInstance().getTransportType(), - strategy.getTransportConfig().getDockerHost() - ); - - log.debug("Checking Docker OS type for {}", strategy.getDescription()); - String osType = info.getOsType(); - if (StringUtils.isBlank(osType)) { - log.warn("Could not determine Docker OS type"); - } else if (!osType.equals("linux")) { - log.warn("{} is currently not supported", osType); - throw new InvalidConfigurationException(osType + " containers are currently not supported"); - } - - if (strategy.isPersistable()) { - TestcontainersConfiguration.getInstance().updateUserConfig("docker.client.strategy", strategy.getClass().getName()); - } - - return Stream.of(strategy); - } catch (Exception | ExceptionInInitializerError | NoClassDefFoundError e) { - @Nullable String throwableMessage = e.getMessage(); - @SuppressWarnings("ThrowableResultOfMethodCallIgnored") - Throwable rootCause = Throwables.getRootCause(e); - @Nullable String rootCauseMessage = rootCause.getMessage(); - - String failureDescription; - if (throwableMessage != null && throwableMessage.equals(rootCauseMessage)) { - failureDescription = String.format("%s: failed with exception %s (%s)", - strategy.getClass().getSimpleName(), - e.getClass().getSimpleName(), - throwableMessage); - } else { - failureDescription = String.format("%s: failed with exception %s (%s). Root cause %s (%s)", - strategy.getClass().getSimpleName(), - e.getClass().getSimpleName(), - throwableMessage, - rootCause.getClass().getSimpleName(), - rootCauseMessage - ); - } - configurationFailures.add(failureDescription); - - log.debug(failureDescription); - return Stream.empty(); - } - }) - .findAny() - .orElseThrow(() -> { - log.error("Could not find a valid Docker environment. Please check configuration. Attempted configurations were:"); - for (String failureMessage : configurationFailures) { - log.error(" " + failureMessage); - } - log.error("As no valid configuration was found, execution cannot continue"); - - FAIL_FAST_ALWAYS.set(true); - return new IllegalStateException("Could not find a valid Docker environment. Please see logs and check configuration"); + FAIL_FAST_ALWAYS.set(true); + return new IllegalStateException("Could not find a valid Docker environment. Please see logs and check configuration"); + }); + } + + private static boolean tryOutStrategy(List configurationFailures, DockerClientProviderStrategy strategy) { + try { + log.debug("Trying out strategy: {}", strategy.getClass().getSimpleName()); + DockerClient dockerClient = strategy.getDockerClient(); + + Info info; + try { + info = Unreliables.retryUntilSuccess(TestcontainersConfiguration.getInstance().getClientPingTimeout(), TimeUnit.SECONDS, () -> { + return strategy.PING_RATE_LIMITER.getWhenReady(() -> { + log.debug("Pinging docker daemon..."); + return dockerClient.infoCmd().exec(); + }); }); + } catch (TimeoutException e) { + IOUtils.closeQuietly(dockerClient); + throw e; + } + log.info("Found Docker environment with {}", strategy.getDescription()); + log.debug( + "Transport type: '{}', Docker host: '{}'", + TestcontainersConfiguration.getInstance().getTransportType(), + strategy.getTransportConfig().getDockerHost() + ); + + log.debug("Checking Docker OS type for {}", strategy.getDescription()); + String osType = info.getOsType(); + if (StringUtils.isBlank(osType)) { + log.warn("Could not determine Docker OS type"); + } else if (!osType.equals("linux")) { + log.warn("{} is currently not supported", osType); + throw new InvalidConfigurationException(osType + " containers are currently not supported"); + } + + if (strategy.isPersistable()) { + TestcontainersConfiguration.getInstance().updateUserConfig("docker.client.strategy", strategy.getClass().getName()); + } + + return true; + } catch (Exception | ExceptionInInitializerError | NoClassDefFoundError e) { + @Nullable String throwableMessage = e.getMessage(); + @SuppressWarnings("ThrowableResultOfMethodCallIgnored") + Throwable rootCause = Throwables.getRootCause(e); + @Nullable String rootCauseMessage = rootCause.getMessage(); + + String failureDescription; + if (throwableMessage != null && throwableMessage.equals(rootCauseMessage)) { + failureDescription = String.format("%s: failed with exception %s (%s)", + strategy.getClass().getSimpleName(), + e.getClass().getSimpleName(), + throwableMessage); + } else { + failureDescription = String.format("%s: failed with exception %s (%s). Root cause %s (%s)", + strategy.getClass().getSimpleName(), + e.getClass().getSimpleName(), + throwableMessage, + rootCause.getClass().getSimpleName(), + rootCauseMessage + ); + } + configurationFailures.add(failureDescription); + + log.debug(failureDescription); + return false; + } + } + + private static Optional loadConfiguredStrategy() { + String configuredDockerClientStrategyClassName = TestcontainersConfiguration.getInstance().getDockerClientStrategyClassName(); + + return Stream + .of(configuredDockerClientStrategyClassName) + .filter(Objects::nonNull) + .flatMap(it -> { + try { + Class strategyClass = (Class) Thread.currentThread().getContextClassLoader().loadClass(it); + return Stream.of(strategyClass.newInstance()); + } catch (ClassNotFoundException e) { + log.warn("Can't instantiate a strategy from {} (ClassNotFoundException). " + + "This probably means that cached configuration refers to a client provider " + + "class that is not available in this version of Testcontainers. Other " + + "strategies will be tried instead.", it); + return Stream.empty(); + } catch (InstantiationException | IllegalAccessException e) { + log.warn("Can't instantiate a strategy from {}", it, e); + return Stream.empty(); + } + }) + // Ignore persisted strategy if it's not persistable anymore + .filter(DockerClientProviderStrategy::isPersistable) + .peek(strategy -> log.info("Loaded {} from ~/.testcontainers.properties, will try it first", strategy.getClass().getName())) + .findFirst(); } public static DockerClient getClientForConfig(TransportConfig transportConfig) { From afc71788fa21828f0ec8fabdc81600696456dc7d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Oct 2021 18:04:42 +0000 Subject: [PATCH 2119/5615] Bump junit-jupiter-engine from 5.7.2 to 5.8.1 in /modules/junit-jupiter Bumps [junit-jupiter-engine](https://github.com/junit-team/junit5) from 5.7.2 to 5.8.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.7.2...r5.8.1) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-engine dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 03abb675f3b..20b2179f2d6 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -17,7 +17,7 @@ dependencies { testRuntimeOnly 'org.postgresql:postgresql:42.2.23' testRuntimeOnly 'mysql:mysql-connector-java:8.0.26' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.2' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' } test { From 56876dea783cf45db48f5e86c059ea8bf36386d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Oct 2021 18:09:41 +0000 Subject: [PATCH 2120/5615] Bump tomcat-jdbc from 10.0.10 to 10.0.12 in /modules/jdbc Bumps tomcat-jdbc from 10.0.10 to 10.0.12. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index b3f53e19333..ccf272554f7 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -7,7 +7,7 @@ dependencies { compileOnly 'org.jetbrains:annotations:22.0.0' testImplementation 'commons-dbutils:commons-dbutils:1.7' testImplementation 'org.vibur:vibur-dbcp:25.0' - testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.10' + testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.12' testImplementation 'com.zaxxer:HikariCP-java6:2.3.13' testImplementation 'com.googlecode.junit-toolbox:junit-toolbox:2.4' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From 3ffd00e635b5628bce953e81bc0b2933bf900404 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Oct 2021 18:15:10 +0000 Subject: [PATCH 2121/5615] Bump s3 from 2.17.34 to 2.17.52 in /modules/localstack Bumps s3 from 2.17.34 to 2.17.52. --- updated-dependencies: - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 1ccc305595d..e633fbbe05d 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.37' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.62' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.60' - testImplementation 'software.amazon.awssdk:s3:2.17.34' + testImplementation 'software.amazon.awssdk:s3:2.17.52' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 33ee416fb6d25ff2517f3c018ddafd24d6c5dbee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Oct 2021 18:20:05 +0000 Subject: [PATCH 2122/5615] Bump annotations from 21.0.1 to 22.0.0 in /modules/oracle-xe Bumps [annotations](https://github.com/JetBrains/java-annotations) from 21.0.1 to 22.0.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/21.0.1...22.0.0) --- updated-dependencies: - dependency-name: org.jetbrains:annotations dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/oracle-xe/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/oracle-xe/build.gradle b/modules/oracle-xe/build.gradle index 38146d4dd01..5c18b63bb9d 100644 --- a/modules/oracle-xe/build.gradle +++ b/modules/oracle-xe/build.gradle @@ -6,5 +6,5 @@ dependencies { testImplementation project(':jdbc-test') testImplementation 'com.oracle.ojdbc:ojdbc8:19.3.0.0' - compileOnly 'org.jetbrains:annotations:21.0.1' + compileOnly 'org.jetbrains:annotations:22.0.0' } From 5174d179f115c842acaa5323bd6a002ce534a494 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Oct 2021 18:51:26 +0000 Subject: [PATCH 2123/5615] Bump r2dbc-mssql in /modules/mssqlserver Bumps [r2dbc-mssql](https://github.com/r2dbc/r2dbc-mssql) from 0.8.6.RELEASE to 0.8.7.RELEASE. - [Release notes](https://github.com/r2dbc/r2dbc-mssql/releases) - [Changelog](https://github.com/r2dbc/r2dbc-mssql/blob/v0.8.7.RELEASE/CHANGELOG) - [Commits](https://github.com/r2dbc/r2dbc-mssql/compare/v0.8.6.RELEASE...v0.8.7.RELEASE) --- updated-dependencies: - dependency-name: io.r2dbc:r2dbc-mssql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mssqlserver/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index cd797a9807f..51e5bba3315 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -7,13 +7,13 @@ dependencies { api project(':jdbc') compileOnly project(':r2dbc') - compileOnly 'io.r2dbc:r2dbc-mssql:0.8.6.RELEASE' + compileOnly 'io.r2dbc:r2dbc-mssql:0.8.7.RELEASE' testImplementation project(':jdbc-test') testImplementation 'com.microsoft.sqlserver:mssql-jdbc:9.4.0.jre8' testImplementation project(':r2dbc') - testImplementation 'io.r2dbc:r2dbc-mssql:0.8.5.RELEASE' + testImplementation 'io.r2dbc:r2dbc-mssql:0.8.7.RELEASE' // MSSQL's wait strategy requires the JDBC driver testImplementation testFixtures(project(':r2dbc')) From dace7e4d750552bb3d57036b579318045e203931 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 7 Oct 2021 12:15:23 +0100 Subject: [PATCH 2124/5615] Fix flaky kafka cluster example (#4549) Co-authored-by: Kevin Wittek Co-authored-by: Sergei Egorov --- examples/kafka-cluster/build.gradle | 2 +- .../kafkacluster/KafkaContainerCluster.java | 8 +++++--- .../kafkacluster/KafkaContainerClusterTest.java | 2 +- .../src/test/resources/logback-test.xml | 14 ++++++++++++++ .../testcontainers/containers/KafkaContainer.java | 2 +- 5 files changed, 22 insertions(+), 6 deletions(-) create mode 100644 examples/kafka-cluster/src/test/resources/logback-test.xml diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index 2a4650930a4..8f23284a48e 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -13,5 +13,5 @@ dependencies { testImplementation 'org.apache.kafka:kafka-clients:2.8.0' testImplementation 'org.assertj:assertj-core:3.20.2' testImplementation 'com.google.guava:guava:23.0' - testImplementation 'org.slf4j:slf4j-simple:1.7.32' + testImplementation 'ch.qos.logback:logback-classic:1.2.5' } diff --git a/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerCluster.java b/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerCluster.java index fd5b1a0a0b6..398488fe3f9 100644 --- a/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerCluster.java +++ b/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerCluster.java @@ -10,6 +10,7 @@ import org.testcontainers.containers.KafkaContainer; import org.testcontainers.utility.DockerImageName; +import java.time.Duration; import java.util.Collection; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -56,7 +57,8 @@ public KafkaContainerCluster(String confluentPlatformVersion, int brokersNum, in .withEnv("KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR", internalTopicsRf + "") .withEnv("KAFKA_OFFSETS_TOPIC_NUM_PARTITIONS", internalTopicsRf + "") .withEnv("KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR", internalTopicsRf + "") - .withEnv("KAFKA_TRANSACTION_STATE_LOG_MIN_ISR", internalTopicsRf + ""); + .withEnv("KAFKA_TRANSACTION_STATE_LOG_MIN_ISR", internalTopicsRf + "") + .withStartupTimeout(Duration.ofMinutes(1)); }) .collect(Collectors.toList()); } @@ -81,8 +83,8 @@ private Stream> allContainers() { @Override @SneakyThrows public void start() { - Stream startables = this.brokers.stream().map(Startable.class::cast); - Startables.deepStart(startables).get(60, SECONDS); + // sequential start to avoid resource contention on CI systems with weaker hardware + brokers.forEach(GenericContainer::start); Unreliables.retryUntilTrue(30, TimeUnit.SECONDS, () -> { Container.ExecResult result = this.zookeeper.execInContainer( diff --git a/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerClusterTest.java b/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerClusterTest.java index 0756b24d58a..a431c14bba3 100644 --- a/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerClusterTest.java +++ b/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerClusterTest.java @@ -30,7 +30,7 @@ public class KafkaContainerClusterTest { @Test public void testKafkaContainerCluster() throws Exception { try ( - KafkaContainerCluster cluster = new KafkaContainerCluster("5.2.1", 3, 2) + KafkaContainerCluster cluster = new KafkaContainerCluster("6.2.1", 3, 2) ) { cluster.start(); String bootstrapServers = cluster.getBootstrapServers(); diff --git a/examples/kafka-cluster/src/test/resources/logback-test.xml b/examples/kafka-cluster/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..be4ebaed2c8 --- /dev/null +++ b/examples/kafka-cluster/src/test/resources/logback-test.xml @@ -0,0 +1,14 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + diff --git a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java index f62ca23eb57..8b1128efef0 100644 --- a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java +++ b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java @@ -118,7 +118,7 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { "advertised.listeners=[" + String.join(",", getBootstrapServers(), brokerAdvertisedListener) + "]" ); if (result.getExitCode() != 0) { - throw new IllegalStateException(result.getStderr()); + throw new IllegalStateException(result.toString()); } } From ce15717313b38a584951017e04ca791ed63bc2c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Oct 2021 11:16:09 +0000 Subject: [PATCH 2125/5615] Bump assertj-core from 3.20.2 to 3.21.0 in /modules/jdbc Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.20.2 to 3.21.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.20.2...assertj-core-3.21.0) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index b3f53e19333..977827488ce 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -11,5 +11,5 @@ dependencies { testImplementation 'com.zaxxer:HikariCP-java6:2.3.13' testImplementation 'com.googlecode.junit-toolbox:junit-toolbox:2.4' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' - testImplementation 'org.assertj:assertj-core:3.20.2' + testImplementation 'org.assertj:assertj-core:3.21.0' } From 0900fcb7098293a4dfcc491c81b1ace6358e7ad5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Oct 2021 11:16:13 +0000 Subject: [PATCH 2126/5615] Bump assertj-core from 3.20.2 to 3.21.0 in /core Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.20.2 to 3.21.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.20.2...assertj-core-3.21.0) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index fca331e9d3f..e5a36114afb 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -100,7 +100,7 @@ dependencies { testImplementation files('testlib/repo/fakejar/fakejar/0/fakejar-0.jar') testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' - testImplementation 'org.assertj:assertj-core:3.20.2' + testImplementation 'org.assertj:assertj-core:3.21.0' testImplementation project(':test-support') jarFileTestCompileOnly "org.projectlombok:lombok:${lombok.version}" From b77733d69a271a2a5d741a2f5cf9c19e5f6e748f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Oct 2021 11:16:25 +0000 Subject: [PATCH 2127/5615] Bump assertj-core from 3.20.2 to 3.21.0 in /modules/neo4j Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.20.2 to 3.21.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.20.2...assertj-core-3.21.0) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 08eda371597..2c25d148418 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -34,5 +34,5 @@ dependencies { api project(":testcontainers") testImplementation "org.neo4j.driver:neo4j-java-driver:1.7.5" - testImplementation 'org.assertj:assertj-core:3.20.2' + testImplementation 'org.assertj:assertj-core:3.21.0' } From a4feea019dfc658680efa8ffd60d58e63608eebd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Oct 2021 11:16:30 +0000 Subject: [PATCH 2128/5615] Bump guava from 30.1.1-jre to 31.0.1-jre in /modules/jdbc-test Bumps [guava](https://github.com/google/guava) from 30.1.1-jre to 31.0.1-jre. - [Release notes](https://github.com/google/guava/releases) - [Commits](https://github.com/google/guava/commits) --- updated-dependencies: - dependency-name: com.google.guava:guava dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 8145855684f..f24b9bd6df2 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -2,7 +2,7 @@ dependencies { api project(':jdbc') api project(':test-support') - api 'com.google.guava:guava:30.1.1-jre' + api 'com.google.guava:guava:31.0.1-jre' api 'org.apache.commons:commons-lang3:3.12.0' api 'com.zaxxer:HikariCP-java6:2.3.13' api 'commons-dbutils:commons-dbutils:1.7' From 06db7b6931ff0086808a80598344824c91624117 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Oct 2021 11:16:31 +0000 Subject: [PATCH 2129/5615] Bump kafka-clients from 2.8.0 to 3.0.0 in /modules/kafka Bumps kafka-clients from 2.8.0 to 3.0.0. --- updated-dependencies: - dependency-name: org.apache.kafka:kafka-clients dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index f7e9acda9a0..63f751665b0 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -12,7 +12,7 @@ tasks.japicmp { dependencies { api project(':testcontainers') - testImplementation 'org.apache.kafka:kafka-clients:2.8.0' + testImplementation 'org.apache.kafka:kafka-clients:3.0.0' testImplementation 'org.assertj:assertj-core:3.20.2' testImplementation 'com.google.guava:guava:23.0' } From 896f2200ebddaf0f625b6c7a3d8b0dcf771659fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Oct 2021 11:16:32 +0000 Subject: [PATCH 2130/5615] Bump assertj-core from 3.20.2 to 3.21.0 in /modules/pulsar Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.20.2 to 3.21.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.20.2...assertj-core-3.21.0) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 478c47fff29..d886be91591 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':testcontainers') testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.7.2' - testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.20.2' + testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.21.0' testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.7.2' } From 3ac377d2bcd61cd375f4ead4bde4e0a487f3673b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Oct 2021 11:16:35 +0000 Subject: [PATCH 2131/5615] Bump assertj-core from 3.20.2 to 3.21.0 in /modules/junit-jupiter Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.20.2 to 3.21.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.20.2...assertj-core-3.21.0) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 03abb675f3b..e99ccf32061 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -12,7 +12,7 @@ dependencies { testImplementation ('org.mockito:mockito-core:3.12.4') { exclude(module: 'hamcrest-core') } - testImplementation 'org.assertj:assertj-core:3.20.2' + testImplementation 'org.assertj:assertj-core:3.21.0' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.7.2' testRuntimeOnly 'org.postgresql:postgresql:42.2.23' From 97bae71b6903b2c2ffd49d7a08c6f7e3ead72b9c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Oct 2021 11:16:48 +0000 Subject: [PATCH 2132/5615] Bump kafka-clients from 2.8.0 to 3.0.0 in /examples Bumps kafka-clients from 2.8.0 to 3.0.0. --- updated-dependencies: - dependency-name: org.apache.kafka:kafka-clients dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- examples/kafka-cluster/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index 8f23284a48e..956292df2f8 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -10,7 +10,7 @@ dependencies { testCompileOnly "org.projectlombok:lombok:1.18.20" testAnnotationProcessor "org.projectlombok:lombok:1.18.10" testImplementation 'org.testcontainers:kafka' - testImplementation 'org.apache.kafka:kafka-clients:2.8.0' + testImplementation 'org.apache.kafka:kafka-clients:3.0.0' testImplementation 'org.assertj:assertj-core:3.20.2' testImplementation 'com.google.guava:guava:23.0' testImplementation 'ch.qos.logback:logback-classic:1.2.5' From 2fe8148fc3c6da51d91d1910a7d694c0b648115d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Oct 2021 11:16:54 +0000 Subject: [PATCH 2133/5615] Bump logback-classic from 1.2.5 to 1.2.6 in /examples Bumps logback-classic from 1.2.5 to 1.2.6. --- updated-dependencies: - dependency-name: ch.qos.logback:logback-classic dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/disque-job-queue/build.gradle | 2 +- examples/kafka-cluster/build.gradle | 2 +- examples/linked-container/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index c3b3e22fbc2..c44b4c7e7e1 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -12,7 +12,7 @@ dependencies { implementation 'biz.paluch.redis:spinach:0.3' implementation 'com.google.code.gson:gson:2.8.8' implementation 'com.google.guava:guava:23.0' - testImplementation 'ch.qos.logback:logback-classic:1.2.5' + testImplementation 'ch.qos.logback:logback-classic:1.2.6' testImplementation 'org.mockito:mockito-all:1.10.19' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index 8f23284a48e..c4f32682f0f 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -13,5 +13,5 @@ dependencies { testImplementation 'org.apache.kafka:kafka-clients:2.8.0' testImplementation 'org.assertj:assertj-core:3.20.2' testImplementation 'com.google.guava:guava:23.0' - testImplementation 'ch.qos.logback:logback-classic:1.2.5' + testImplementation 'ch.qos.logback:logback-classic:1.2.6' } diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index ff1b2995e02..f03ce765d24 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'com.squareup.okhttp3:okhttp:4.9.1' implementation 'org.json:json:20210307' testImplementation 'org.postgresql:postgresql:42.2.23' - testImplementation 'ch.qos.logback:logback-classic:1.2.5' + testImplementation 'ch.qos.logback:logback-classic:1.2.6' testImplementation 'org.testcontainers:postgresql' } diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index fe269d58e77..9b1424451ec 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -12,7 +12,7 @@ dependencies { implementation 'com.google.code.gson:gson:2.8.8' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' - testImplementation 'ch.qos.logback:logback-classic:1.2.5' + testImplementation 'ch.qos.logback:logback-classic:1.2.6' testImplementation 'org.testng:testng:7.4.0' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 2d9f50b86bd..917f1b0c5a4 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -13,6 +13,6 @@ dependencies { implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'junit:junit:4.13.2' - testImplementation 'ch.qos.logback:logback-classic:1.2.5' + testImplementation 'ch.qos.logback:logback-classic:1.2.6' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index f3a18e685f3..661f6efc125 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -13,7 +13,7 @@ dependencies { implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.32' - testImplementation 'ch.qos.logback:logback-classic:1.2.5' + testImplementation 'ch.qos.logback:logback-classic:1.2.6' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From a79418cdc1dbba3cf0ff6e74864be2a12d2b58b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Oct 2021 11:24:22 +0000 Subject: [PATCH 2134/5615] Bump gradle/wrapper-validation-action from 1.0.3 to 1.0.4 Bumps [gradle/wrapper-validation-action](https://github.com/gradle/wrapper-validation-action) from 1.0.3 to 1.0.4. - [Release notes](https://github.com/gradle/wrapper-validation-action/releases) - [Commits](https://github.com/gradle/wrapper-validation-action/compare/e2c57acffb2c9aa5a8dc6eda2bbae0b6e495bd4c...e6e38bacfdf1a337459f332974bb2327a31aaf4b) Signed-off-by: dependabot[bot] --- .github/workflows/update-gradle-wrapper.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-gradle-wrapper.yml b/.github/workflows/update-gradle-wrapper.yml index f9f1ec85662..08fbc91d00c 100644 --- a/.github/workflows/update-gradle-wrapper.yml +++ b/.github/workflows/update-gradle-wrapper.yml @@ -18,4 +18,4 @@ jobs: repo-token: ${{ secrets.GITHUB_TOKEN }} labels: dependencies - - uses: gradle/wrapper-validation-action@e2c57acffb2c9aa5a8dc6eda2bbae0b6e495bd4c # v1.0.3 + - uses: gradle/wrapper-validation-action@e6e38bacfdf1a337459f332974bb2327a31aaf4b # v1.0.3 From 1202161b52b938a49e399c0df5b689d7cb52430c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Oct 2021 11:29:14 +0000 Subject: [PATCH 2135/5615] Bump influxdb-java from 2.21 to 2.22 in /modules/influxdb Bumps [influxdb-java](https://github.com/influxdata/influxdb-java) from 2.21 to 2.22. - [Release notes](https://github.com/influxdata/influxdb-java/releases) - [Changelog](https://github.com/influxdata/influxdb-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/influxdata/influxdb-java/compare/influxdb-java-2.21...influxdb-java-2.22) --- updated-dependencies: - dependency-name: org.influxdb:influxdb-java dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/influxdb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/influxdb/build.gradle b/modules/influxdb/build.gradle index 704bf960724..7a028d4a19d 100644 --- a/modules/influxdb/build.gradle +++ b/modules/influxdb/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: InfluxDB" dependencies { api project(':testcontainers') - compileOnly 'org.influxdb:influxdb-java:2.21' + compileOnly 'org.influxdb:influxdb-java:2.22' testImplementation 'org.influxdb:influxdb-java:2.21' } From 3304899856be39657b5433de3f5545504a24a8ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Oct 2021 11:34:15 +0000 Subject: [PATCH 2136/5615] Bump tomcat-jdbc from 10.0.10 to 10.0.12 in /modules/jdbc-test Bumps tomcat-jdbc from 10.0.10 to 10.0.12. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 8145855684f..42fb9e45dae 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -14,7 +14,7 @@ dependencies { exclude(group: "net.java.dev.jna", module: "jna") } - api 'org.apache.tomcat:tomcat-jdbc:10.0.10' + api 'org.apache.tomcat:tomcat-jdbc:10.0.12' api 'org.vibur:vibur-dbcp:25.0' api 'mysql:mysql-connector-java:8.0.26' } From 600e751a73ae971c04cf654e3c5c968bad846b34 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Oct 2021 16:26:45 +0000 Subject: [PATCH 2137/5615] Bump assertj-core from 3.20.2 to 3.21.0 in /examples Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.20.2 to 3.21.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.20.2...assertj-core-3.21.0) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/kafka-cluster/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index f284af631e5..3679475d1bd 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -11,7 +11,7 @@ dependencies { testAnnotationProcessor "org.projectlombok:lombok:1.18.10" testImplementation 'org.testcontainers:kafka' testImplementation 'org.apache.kafka:kafka-clients:3.0.0' - testImplementation 'org.assertj:assertj-core:3.20.2' + testImplementation 'org.assertj:assertj-core:3.21.0' testImplementation 'com.google.guava:guava:23.0' testImplementation 'ch.qos.logback:logback-classic:1.2.6' } From 740c0aa85d5da6a603171ec4a751516387ef20c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Oct 2021 16:26:45 +0000 Subject: [PATCH 2138/5615] Bump junit-jupiter-params from 5.7.2 to 5.8.1 in /modules/junit-jupiter Bumps [junit-jupiter-params](https://github.com/junit-team/junit5) from 5.7.2 to 5.8.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.7.2...r5.8.1) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-params dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 7df16da0100..1a7c9a477b6 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -13,7 +13,7 @@ dependencies { exclude(module: 'hamcrest-core') } testImplementation 'org.assertj:assertj-core:3.21.0' - testImplementation 'org.junit.jupiter:junit-jupiter-params:5.7.2' + testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.1' testRuntimeOnly 'org.postgresql:postgresql:42.2.24' testRuntimeOnly 'mysql:mysql-connector-java:8.0.26' From 6f546524d44c968b2ae227bbebbc299b0b830ffa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Oct 2021 16:26:54 +0000 Subject: [PATCH 2139/5615] Bump postgresql from 42.2.23 to 42.2.24 in /examples Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.23 to 42.2.24. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.23...REL42.2.24) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 475f4032758..da10b960013 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' implementation 'com.squareup.okhttp3:okhttp:4.9.2' implementation 'org.json:json:20210307' - testImplementation 'org.postgresql:postgresql:42.2.23' + testImplementation 'org.postgresql:postgresql:42.2.24' testImplementation 'ch.qos.logback:logback-classic:1.2.6' testImplementation 'org.testcontainers:postgresql' } From 4b8ea7ee3ccdd5c67f92957b49057793c84e916e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Oct 2021 16:26:56 +0000 Subject: [PATCH 2140/5615] Bump assertj-core from 3.20.2 to 3.21.0 in /modules/vault Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.20.2 to 3.21.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.20.2...assertj-core-3.21.0) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index 18897f66908..4fdb274f999 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -5,6 +5,6 @@ dependencies { testImplementation 'com.bettercloud:vault-java-driver:5.1.0' testImplementation 'io.rest-assured:rest-assured:4.4.0' - testImplementation 'org.assertj:assertj-core:3.20.2' + testImplementation 'org.assertj:assertj-core:3.21.0' } From 878f441d52e65ba1932ab222155bdccd030648d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Oct 2021 16:26:59 +0000 Subject: [PATCH 2141/5615] Bump org.jetbrains.kotlin.jvm from 1.5.30 to 1.5.31 in /examples Bumps org.jetbrains.kotlin.jvm from 1.5.30 to 1.5.31. --- updated-dependencies: - dependency-name: org.jetbrains.kotlin.jvm dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 7e6989a1d93..e40e32a4397 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,6 +1,6 @@ plugins { id("org.springframework.boot") version "2.5.5" - id("org.jetbrains.kotlin.jvm") version "1.5.30" + id("org.jetbrains.kotlin.jvm") version "1.5.31" id("org.jetbrains.kotlin.plugin.spring") version "1.5.31" } From 34c76ff0aca7bcada960246baefa025cc7b2ca03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Oct 2021 16:27:02 +0000 Subject: [PATCH 2142/5615] Bump assertj-core from 3.20.2 to 3.21.0 in /modules/database-commons Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.20.2 to 3.21.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.20.2...assertj-core-3.21.0) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/database-commons/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/database-commons/build.gradle b/modules/database-commons/build.gradle index 35578b55d68..73735b0d03d 100644 --- a/modules/database-commons/build.gradle +++ b/modules/database-commons/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: Database-Commons" dependencies { api project(':testcontainers') - testImplementation 'org.assertj:assertj-core:3.20.2' + testImplementation 'org.assertj:assertj-core:3.21.0' } From 85368a9ee379c6b641305425fd4818024db3b968 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Oct 2021 16:27:02 +0000 Subject: [PATCH 2143/5615] Bump assertj-core from 3.20.2 to 3.21.0 in /modules/kafka Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.20.2 to 3.21.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.20.2...assertj-core-3.21.0) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index 63f751665b0..b37105f7463 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -13,6 +13,6 @@ dependencies { api project(':testcontainers') testImplementation 'org.apache.kafka:kafka-clients:3.0.0' - testImplementation 'org.assertj:assertj-core:3.20.2' + testImplementation 'org.assertj:assertj-core:3.21.0' testImplementation 'com.google.guava:guava:23.0' } From f83a4857ca9c9491beb69e3068791ca38ed18a00 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Oct 2021 16:27:19 +0000 Subject: [PATCH 2144/5615] Bump elasticsearch-rest-client in /modules/elasticsearch Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 7.14.1 to 7.15.0. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.14.1...v7.15.0) --- updated-dependencies: - dependency-name: org.elasticsearch.client:elasticsearch-rest-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index c70fd363f55..2897ecb5628 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,7 +2,7 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') - testImplementation "org.elasticsearch.client:elasticsearch-rest-client:7.14.1" + testImplementation "org.elasticsearch.client:elasticsearch-rest-client:7.15.0" testImplementation "org.elasticsearch.client:transport:7.15.0" testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From fe35c5c6bc87df8dd63c433187cf1e760c7733ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Oct 2021 16:27:20 +0000 Subject: [PATCH 2145/5615] Bump aws-java-sdk-sqs from 1.12.62 to 1.12.82 in /modules/localstack Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.12.62 to 1.12.82. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.62...1.12.82) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-sqs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 04583b6ecdd..0d089bdf95f 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.80' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.80' - testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.62' + testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.82' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.60' testImplementation 'software.amazon.awssdk:s3:2.17.52' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From aa273b5c0136d8bf8d9eb308040b30006ad98144 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Fri, 8 Oct 2021 11:21:48 +0200 Subject: [PATCH 2146/5615] Document Dependabot merge process (#4556) Co-authored-by: Richard North --- docs/contributing.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/contributing.md b/docs/contributing.md index 7d87945c704..4094fab14ef 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -90,3 +90,17 @@ New modules should have the following warning at the top of their documentation This module is INCUBATING. While it is ready for use and operational in the current version of Testcontainers, it is possible that it may receive breaking changes in the future. See [our contributing guidelines](/contributing/#incubating-modules) for more information on our incubating modules policy. We will evaluate incubating modules periodically, and remove the label when appropriate. + + +## Combining Dependabot PRs + +Since we generally get a lot of Dependabot PRs, we regularly combine them into single commits. +For this, we are using the [gh-combine-prs](https://github.com/rnorth/gh-combine-prs) extension for [GitHub CLI](https://cli.github.com/). + +The whole process is as follow: + +1. Check that all open Dependabot PRs did succeed their build. If they did not succeed, trigger a rerun if the cause were external factors or else document the reason if obvious. +2. Run the extension from an up-to-date local `master` branch: `gh combine-prs --query "author:app/dependabot"` +3. Merge conflicts might appear. Just ignore them, we will get those PRs in a future run. +4. Once the build of the combined PR did succeed, temporarily enable merge commits and merge the PR using a merge commit through the GitHub UI. +5. After the merge, disable merge commits again. From 3740f65ae22bab3d59f5bcae0b40c9b42af5cdb6 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 8 Oct 2021 07:22:53 -0400 Subject: [PATCH 2147/5615] Fix `AuthenticatedImagePullTest` (#4560) Previous implementation was assuming that Docker can pull from the gateway, which may not always be the case. This change makes the temporary registry run with host network, so that Docker can always pull from it. --- .../utility/AuthenticatedImagePullTest.java | 72 ++++++++++++++----- 1 file changed, 54 insertions(+), 18 deletions(-) diff --git a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java index c99770c4fbf..3243d219cc0 100644 --- a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java +++ b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java @@ -1,21 +1,10 @@ package org.testcontainers.utility; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.TestImages.DOCKER_REGISTRY_IMAGE; -import static org.testcontainers.TestImages.TINY_IMAGE; - import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.async.ResultCallback; import com.github.dockerjava.api.command.PullImageResultCallback; +import com.github.dockerjava.api.exception.NotFoundException; import com.github.dockerjava.api.model.AuthConfig; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.concurrent.TimeUnit; import org.intellij.lang.annotations.Language; import org.junit.AfterClass; import org.junit.Before; @@ -27,9 +16,27 @@ import org.testcontainers.containers.ContainerState; import org.testcontainers.containers.DockerComposeContainer; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; +import org.testcontainers.containers.output.FrameConsumerResultCallback; +import org.testcontainers.containers.output.OutputFrame; +import org.testcontainers.containers.output.WaitingConsumer; import org.testcontainers.images.builder.ImageFromDockerfile; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.testcontainers.TestImages.DOCKER_REGISTRY_IMAGE; +import static org.testcontainers.TestImages.TINY_IMAGE; + /** * This test checks the integration between Testcontainers and an authenticated registry, but uses * a mock instance of {@link RegistryAuthLocator} - the purpose of the test is solely to ensure that @@ -52,8 +59,10 @@ public class AuthenticatedImagePullTest { .env("REGISTRY_AUTH_HTPASSWD_PATH", "/htpasswd") .env("REGISTRY_AUTH_HTPASSWD_REALM", "Test"); })) - .withExposedPorts(5000) - .waitingFor(new HttpWaitStrategy()); + .withEnv("REGISTRY_HTTP_ADDR", "127.0.0.1:0") + .withCreateContainerCmdModifier(cmd -> { + cmd.getHostConfig().withNetworkMode("host"); + }); private static RegistryAuthLocator originalAuthLocatorSingleton; private static DockerClient client; @@ -62,11 +71,34 @@ public class AuthenticatedImagePullTest { private static RegistryAuthLocator mockAuthLocator; @BeforeClass - public static void setUp() throws InterruptedException { + public static void setUp() throws Exception { originalAuthLocatorSingleton = RegistryAuthLocator.instance(); client = DockerClientFactory.instance().client(); - String testRegistryAddress = authenticatedRegistry.getHost() + ":" + authenticatedRegistry.getFirstMappedPort(); + AtomicInteger port = new AtomicInteger(-1); + try (FrameConsumerResultCallback resultCallback = new FrameConsumerResultCallback()) { + WaitingConsumer waitingConsumer = new WaitingConsumer(); + resultCallback.addConsumer(OutputFrame.OutputType.STDERR, waitingConsumer); + + client.logContainerCmd(authenticatedRegistry.getContainerId()) + .withStdErr(true) + .withFollowStream(true) + .exec(resultCallback); + + Pattern pattern = Pattern.compile(".*listening on .*:(\\d+).*", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + waitingConsumer.waitUntil(it -> { + String s = it.getUtf8String(); + Matcher matcher = pattern.matcher(s); + if (matcher.matches()) { + port.set(Integer.parseInt(matcher.group(1))); + return true; + } else { + return false; + } + }, 10, TimeUnit.SECONDS); + } + + String testRegistryAddress = authenticatedRegistry.getHost() + ":" + port.get(); testImageName = testRegistryAddress + "/alpine"; final DockerImageName expectedName = DockerImageName.parse(testImageName); @@ -88,7 +120,11 @@ public static void setUp() throws InterruptedException { @Before public void removeImageFromLocalDocker() { // remove the image tag from local docker so that it must be pulled before use - client.removeImageCmd(testImageName).withForce(true).exec(); + try { + client.removeImageCmd(testImageName).withForce(true).exec(); + } catch (NotFoundException ignored) { + + } } @AfterClass From 33218e720158dfe2388bfdf9cad9fdfbfd40990f Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 11 Oct 2021 14:15:19 +0100 Subject: [PATCH 2148/5615] OrientDB: Use log wait strategy for more reliable readiness detection (#4471) --- .../containers/OrientDBContainer.java | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java b/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java index f6ce060b6ec..a68f55b60ef 100644 --- a/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java +++ b/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java @@ -9,20 +9,14 @@ import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; -import org.testcontainers.containers.wait.strategy.Wait; -import org.testcontainers.containers.wait.strategy.WaitAllStrategy; -import org.testcontainers.containers.wait.strategy.WaitStrategy; +import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.utility.DockerImageName; import java.io.IOException; import java.net.URL; import java.nio.charset.StandardCharsets; -import java.time.Duration; import java.util.Optional; -import static java.net.HttpURLConnection.HTTP_OK; - /** * @author robfrank */ @@ -68,14 +62,7 @@ public OrientDBContainer(final DockerImageName dockerImageName) { serverPassword = DEFAULT_SERVER_PASSWORD; databaseName = DEFAULT_DATABASE_NAME; - WaitStrategy waitForHttp = new HttpWaitStrategy() - .forPort(DEFAULT_HTTP_PORT) - .forStatusCodeMatching(response -> response == HTTP_OK); - - waitStrategy = new WaitAllStrategy() - .withStrategy(Wait.forListeningPort()) - .withStrategy(waitForHttp) - .withStartupTimeout(Duration.ofMinutes(2)); + waitStrategy = new LogMessageWaitStrategy().withRegEx(".*Gremlin started correctly.*"); addExposedPorts(DEFAULT_BINARY_PORT, DEFAULT_HTTP_PORT); } From f31ee7984093efe303b8326934a9f76b70f65f35 Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 11 Oct 2021 15:15:19 +0100 Subject: [PATCH 2149/5615] Skip japicmp for new modules (#4569) --- gradle/japicmp.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gradle/japicmp.gradle b/gradle/japicmp.gradle index 58fbec9c0b7..e74cdccd3af 100644 --- a/gradle/japicmp.gradle +++ b/gradle/japicmp.gradle @@ -11,6 +11,10 @@ dependencies { tasks.japicmp { dependsOn(tasks.shadowJar) + // Disable if baseline dependencies cannot be resolved - such as when developing a new module that doesn't + // have an existing published version. + enabled = ! configurations.baseline.copy().resolvedConfiguration.lenientConfiguration.getFiles().empty + oldClasspath = configurations.baseline newClasspath = shadowJar.outputs.files ignoreMissingClasses = true From 8d40cbd75c4bbb10797e8543d080ad6e046bc1ce Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Tue, 12 Oct 2021 09:27:34 +0200 Subject: [PATCH 2150/5615] Enable `JUnitPlatform` for Spock tests (#4568) Since Spock 2 is based on the JUnit platform, Gradle needs to be configured explicitly to discover and run the tests. Also, since HikariCP 5 requires JDK 11+, we have to switch back to 4.0.3. --- modules/spock/build.gradle | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 3fc86d7877b..1e280e88965 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -12,7 +12,7 @@ dependencies { testImplementation project(':mysql') testImplementation project(':postgresql') - testImplementation 'com.zaxxer:HikariCP:5.0.0' + testImplementation 'com.zaxxer:HikariCP:4.0.3' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' testRuntimeOnly 'org.postgresql:postgresql:42.2.24' @@ -26,3 +26,10 @@ sourceJar { allSource contains both .java and .groovy files */ from sourceSets.main.allSource } + +test { + useJUnitPlatform() + testLogging { + events "passed", "skipped", "failed" + } +} From 1ad2a053d858b2080c359a1c85288dd5481a2b78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20Ka=C4=9Fan=20=C3=96zcan?= Date: Tue, 12 Oct 2021 10:52:36 +0300 Subject: [PATCH 2151/5615] Add Azure Cosmos DB module: Introduce CosmosDBEmulatorContainer (#4303) Co-authored-by: Richard North --- docs/modules/azure.md | 64 +++++++++++ mkdocs.yml | 1 + modules/azure/build.gradle | 8 ++ .../containers/CosmosDBEmulatorContainer.java | 52 +++++++++ .../containers/KeyStoreBuilder.java | 105 ++++++++++++++++++ .../CosmosDBEmulatorContainerTest.java | 73 ++++++++++++ .../azure/src/test/resources/logback-test.xml | 16 +++ 7 files changed, 319 insertions(+) create mode 100644 docs/modules/azure.md create mode 100644 modules/azure/build.gradle create mode 100644 modules/azure/src/main/java/org/testcontainers/containers/CosmosDBEmulatorContainer.java create mode 100644 modules/azure/src/main/java/org/testcontainers/containers/KeyStoreBuilder.java create mode 100644 modules/azure/src/test/java/org/testcontainers/containers/CosmosDBEmulatorContainerTest.java create mode 100644 modules/azure/src/test/resources/logback-test.xml diff --git a/docs/modules/azure.md b/docs/modules/azure.md new file mode 100644 index 00000000000..529f058ea52 --- /dev/null +++ b/docs/modules/azure.md @@ -0,0 +1,64 @@ +# Azure Module + +!!! note +This module is INCUBATING. While it is ready for use and operational in the current version of Testcontainers, it is possible that it may receive breaking changes in the future. See [our contributing guidelines](/contributing/#incubating-modules) for more information on our incubating modules policy. + +Testcontainers module for the Microsoft Azure's [SDK](https://github.com/Azure/azure-sdk-for-java). + +Currently, the module supports `CosmosDB` emulator. In order to use it, you should use the following class: + +Class | Container Image +-|- +CosmosDBEmulatorContainer | [mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator](https://github.com/microsoft/containerregistry) + +## Usage example + +### CosmosDB + +Start Azure CosmosDB Emulator during a test: + + +[Starting a Azure CosmosDB Emulator container](../../modules/azure/src/test/java/org/testcontainers/containers/CosmosDBEmulatorContainerTest.java) inside_block:emulatorContainer + + +Prepare KeyStore to use for SSL. + + +[Building KeyStore from certificate within container](../../modules/azure/src/test/java/org/testcontainers/containers/CosmosDBEmulatorContainerTest.java) inside_block:buildAndSaveNewKeyStore + + +Set system trust-store parameters to use already built KeyStore: + + +[Set system trust-store parameters](../../modules/azure/src/test/java/org/testcontainers/containers/CosmosDBEmulatorContainerTest.java) inside_block:setSystemTrustStoreParameters + + +Build Azure CosmosDB client: + + +[Build Azure CosmosDB client](../../modules/azure/src/test/java/org/testcontainers/containers/CosmosDBEmulatorContainerTest.java) inside_block:buildClient + + +Test against the Emulator: + + +[Testing against Azure CosmosDB Emulator container](../../modules/azure/src/test/java/org/testcontainers/containers/CosmosDBEmulatorContainerTest.java) inside_block:testWithClientAgainstEmulatorContainer + + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testImplementation "org.testcontainers:azure:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + azure + {{latest_version}} + test + +``` + diff --git a/mkdocs.yml b/mkdocs.yml index 9929f5292c2..5d182b79590 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -61,6 +61,7 @@ nav: - modules/databases/postgres.md - modules/databases/presto.md - modules/databases/trino.md + - modules/azure.md - modules/docker_compose.md - modules/elasticsearch.md - modules/gcloud.md diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle new file mode 100644 index 00000000000..19d9b3458fd --- /dev/null +++ b/modules/azure/build.gradle @@ -0,0 +1,8 @@ +description = "Testcontainers :: Azure" + +dependencies { + api project(':testcontainers') + + testImplementation 'org.assertj:assertj-core:3.15.0' + testImplementation 'com.azure:azure-cosmos:4.16.0' +} diff --git a/modules/azure/src/main/java/org/testcontainers/containers/CosmosDBEmulatorContainer.java b/modules/azure/src/main/java/org/testcontainers/containers/CosmosDBEmulatorContainer.java new file mode 100644 index 00000000000..9328d881666 --- /dev/null +++ b/modules/azure/src/main/java/org/testcontainers/containers/CosmosDBEmulatorContainer.java @@ -0,0 +1,52 @@ +package org.testcontainers.containers; + +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.utility.DockerImageName; + +import java.security.KeyStore; + +/** + * An Azure CosmosDB container + */ +public class CosmosDBEmulatorContainer extends GenericContainer { + + private static final DockerImageName DEFAULT_IMAGE_NAME = + DockerImageName.parse("mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator"); + + private static final int PORT = 8081; + + /** + * @param dockerImageName specified docker image name to run + */ + public CosmosDBEmulatorContainer(final DockerImageName dockerImageName) { + super(dockerImageName); + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + withExposedPorts(PORT); + waitingFor(Wait.forLogMessage("(?s).*Started\\r\\n$", 1)); + } + + /** + * @return new KeyStore built with PKCS12 + */ + public KeyStore buildNewKeyStore() { + return KeyStoreBuilder.buildByDownloadingCertificate(getEmulatorEndpoint(), getEmulatorKey()); + } + + /** + * Emulator key is a known constant and specified in Azure Cosmos DB Documents. + * This key is also used as password for emulator certificate file. + * + * @return predefined emulator key + * @see Azure Cosmos DB Documents + */ + public String getEmulatorKey() { + return "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="; + } + + /** + * @return secure https emulator endpoint to send requests + */ + public String getEmulatorEndpoint() { + return "https://" + getHost() + ":" + getMappedPort(PORT); + } +} diff --git a/modules/azure/src/main/java/org/testcontainers/containers/KeyStoreBuilder.java b/modules/azure/src/main/java/org/testcontainers/containers/KeyStoreBuilder.java new file mode 100644 index 00000000000..0179e8d832a --- /dev/null +++ b/modules/azure/src/main/java/org/testcontainers/containers/KeyStoreBuilder.java @@ -0,0 +1,105 @@ +package org.testcontainers.containers; + +import okhttp3.Cache; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.io.InputStream; +import java.security.KeyStore; +import java.security.SecureRandom; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Objects; + +final class KeyStoreBuilder { + + static KeyStore buildByDownloadingCertificate(String endpoint, String keyStorePassword) { + OkHttpClient client = null; + Response response = null; + try { + TrustManager[] trustAllManagers = buildTrustAllManagers(); + client = buildTrustAllClient(trustAllManagers); + Request request = buildRequest(endpoint); + response = client.newCall(request).execute(); + return buildKeyStore(response.body().byteStream(), keyStorePassword); + } catch (Exception ex) { + throw new IllegalStateException(ex); + } finally { + closeResponseSilently(response); + closeClientSilently(client); + } + } + + private static TrustManager[] buildTrustAllManagers() { + return new TrustManager[] { + new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[]{}; + } + } + }; + } + + private static OkHttpClient buildTrustAllClient(TrustManager[] trustManagers) throws Exception { + SSLContext sslContext = SSLContext.getInstance("SSL"); + sslContext.init(null, trustManagers, new SecureRandom()); + SSLSocketFactory socketFactory = sslContext.getSocketFactory(); + return new OkHttpClient.Builder() + .sslSocketFactory(socketFactory, (X509TrustManager) trustManagers[0]) + .hostnameVerifier((s, sslSession) -> true) + .build(); + } + + private static Request buildRequest(String endpoint) { + return new Request.Builder() + .get() + .url(endpoint + "/_explorer/emulator.pem") + .build(); + } + + private static KeyStore buildKeyStore(InputStream certificateStream, String keyStorePassword) throws Exception { + Certificate certificate = CertificateFactory.getInstance("X.509").generateCertificate(certificateStream); + KeyStore keystore = KeyStore.getInstance("PKCS12"); + keystore.load(null, keyStorePassword.toCharArray()); + keystore.setCertificateEntry("azure-cosmos-emulator", certificate); + return keystore; + } + + private static void closeResponseSilently(Response response) { + try { + if (Objects.nonNull(response)) { + response.close(); + } + } catch (Exception ignored) { + } + } + + private static void closeClientSilently(OkHttpClient client) { + try { + if (Objects.nonNull(client)) { + client.dispatcher().executorService().shutdown(); + client.connectionPool().evictAll(); + Cache cache = client.cache(); + if (Objects.nonNull(cache)) { + cache.close(); + } + } + } catch (Exception ignored) { + } + } +} diff --git a/modules/azure/src/test/java/org/testcontainers/containers/CosmosDBEmulatorContainerTest.java b/modules/azure/src/test/java/org/testcontainers/containers/CosmosDBEmulatorContainerTest.java new file mode 100644 index 00000000000..dd9dc6b718f --- /dev/null +++ b/modules/azure/src/test/java/org/testcontainers/containers/CosmosDBEmulatorContainerTest.java @@ -0,0 +1,73 @@ +package org.testcontainers.containers; + +import com.azure.cosmos.CosmosAsyncClient; +import com.azure.cosmos.CosmosClientBuilder; +import com.azure.cosmos.models.CosmosContainerResponse; +import com.azure.cosmos.models.CosmosDatabaseResponse; +import org.assertj.core.api.Assertions; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.testcontainers.utility.DockerImageName; + +import java.io.FileOutputStream; +import java.nio.file.Path; +import java.security.KeyStore; +import java.util.Properties; + +public class CosmosDBEmulatorContainerTest { + + private static Properties originalSystemProperties; + + @BeforeClass + public static void captureOriginalSystemProperties() { + originalSystemProperties = (Properties) System.getProperties().clone(); + } + + @AfterClass + public static void restoreOriginalSystemProperties() { + System.setProperties(originalSystemProperties); + } + + @Rule + public TemporaryFolder tempFolder = TemporaryFolder.builder().assureDeletion().build(); + + @Rule + // emulatorContainer { + public CosmosDBEmulatorContainer emulator = new CosmosDBEmulatorContainer( + DockerImageName.parse("mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest") + ); + // } + + @Test + public void testWithCosmosClient() throws Exception { + // buildAndSaveNewKeyStore { + Path keyStoreFile = tempFolder.newFile("azure-cosmos-emulator.keystore").toPath(); + KeyStore keyStore = emulator.buildNewKeyStore(); + keyStore.store(new FileOutputStream(keyStoreFile.toFile()), emulator.getEmulatorKey().toCharArray()); + // } + // setSystemTrustStoreParameters { + System.setProperty("javax.net.ssl.trustStore", keyStoreFile.toString()); + System.setProperty("javax.net.ssl.trustStorePassword", emulator.getEmulatorKey()); + System.setProperty("javax.net.ssl.trustStoreType", "PKCS12"); + // } + // buildClient { + CosmosAsyncClient client = new CosmosClientBuilder() + .gatewayMode() + .endpointDiscoveryEnabled(false) + .endpoint(emulator.getEmulatorEndpoint()) + .key(emulator.getEmulatorKey()) + .buildAsyncClient(); + // } + // testWithClientAgainstEmulatorContainer { + CosmosDatabaseResponse databaseResponse = + client.createDatabaseIfNotExists("Azure").block(); + Assertions.assertThat(databaseResponse.getStatusCode()).isEqualTo(201); + CosmosContainerResponse containerResponse = + client.getDatabase("Azure").createContainerIfNotExists("ServiceContainer", "/name").block(); + Assertions.assertThat(containerResponse.getStatusCode()).isEqualTo(201); + // } + } +} diff --git a/modules/azure/src/test/resources/logback-test.xml b/modules/azure/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..83ef7a1a3ef --- /dev/null +++ b/modules/azure/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + From 92155d949f58080e7aa12538574e3b8252198f53 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 12 Oct 2021 10:27:22 +0100 Subject: [PATCH 2152/5615] Temporarily disable Thundra on master branch (#4570) --- .github/workflows/ci.yml | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2cbfc92b372..d27955d33e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,14 +9,6 @@ env: AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} jobs: - thundra_test_initializer: - runs-on: ubuntu-latest - outputs: - thundra_agent_testrun_id: ${{ steps.thundra_test_initializer.outputs.thundra_agent_testrun_id }} - steps: - - uses: actions/checkout@v2 - - id: thundra_test_initializer - uses: thundra-io/thundra-test-init-action@v1 find_gradle_jobs: runs-on: ubuntu-18.04 outputs: @@ -42,7 +34,7 @@ jobs: echo $TASKS echo "::set-output name=matrix::{\"gradle_args\":$TASKS}" check: - needs: [find_gradle_jobs, thundra_test_initializer] + needs: [find_gradle_jobs] strategy: fail-fast: false matrix: ${{ fromJson(needs.find_gradle_jobs.outputs.matrix) }} @@ -61,19 +53,9 @@ jobs: key: ${{ runner.os }}-gradle-home-${{matrix.gradle_args}}_check-${{ hashFiles('**/*.gradle') }} - name: Clear existing docker image cache run: docker image prune -af - - name: Thundra Gradle Test Instrumentation - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} - uses: thundra-io/thundra-gradle-test-action@v1 - with: - apikey: ${{ secrets.THUNDRA_API_KEY }} - project_id: ${{ secrets.THUNDRA_PROJECT_ID }} - name: Build and test with Gradle (${{matrix.gradle_args}}) run: | - ./gradlew --no-daemon --continue --scan ${{matrix.gradle_args}} \ - $($THUNDRA_GRADLE_INIT_SCRIPT_PATH || echo "--init-script $THUNDRA_GRADLE_INIT_SCRIPT_PATH") - env: - THUNDRA_AGENT_TEST_RUN_ID: ${{ needs.thundra_test_initializer.outputs.thundra_agent_testrun_id }} - THUNDRA_AGENT_REPORT_REST_BASEURL: https://collector.thundra.us/v1 + ./gradlew --no-daemon --continue --scan ${{matrix.gradle_args}} httpclient5_test: runs-on: ubuntu-18.04 steps: From b7e9bb6a69264a43bac37edf596a928c96ce5443 Mon Sep 17 00:00:00 2001 From: Ulrich Winter Date: Tue, 12 Oct 2021 17:54:26 +0200 Subject: [PATCH 2153/5615] Update GitLab CI docs: Add --tls=false to docker-in-docker to avoid startup delays (#4573) --- .../continuous_integration/gitlab_ci.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/supported_docker_environment/continuous_integration/gitlab_ci.md b/docs/supported_docker_environment/continuous_integration/gitlab_ci.md index e6daf84401f..5e701007ab0 100644 --- a/docs/supported_docker_environment/continuous_integration/gitlab_ci.md +++ b/docs/supported_docker_environment/continuous_integration/gitlab_ci.md @@ -1,14 +1,18 @@ # GitLab CI In order to use Testcontainers in a Gitlab CI pipeline, you need to run the job as a Docker container (see [Patterns for running inside Docker](dind_patterns.md)). -So edit your `.gitlab-ci.yml` to include the [Docker-In-Docker service](https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#use-docker-in-docker-workflow-with-docker-executor) (`docker:dind`) and set the `DOCKER_HOST` variable to `tcp://docker:2375` and `DOCKER_TLS_CERTDIR` to empty string. +So edit your `.gitlab-ci.yml` to include the [Docker-In-Docker service](https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#use-docker-in-docker-workflow-with-docker-executor) (`docker:dind`) and set the `DOCKER_HOST` variable to `tcp://docker:2375` and `DOCKER_TLS_CERTDIR` to empty string. + +Caveat: Current docker releases (verified for 20.10.9) intentionally delay the startup, if the docker api is bound to a network address but not TLS protected. To avoid this delay, the docker process needs to be started with the argument `--tls=false`. Otherwise jobs which access the docker api at the very beginning might fail. Here is a sample `.gitlab-ci.yml` that executes test with gradle: ```yml # DinD service is required for Testcontainers services: - - docker:dind + - name: docker:dind + # explicitly disable tls to avoid docker startup interruption + command: ["--tls=false"] variables: # Instruct Testcontainers to use the daemon of DinD. From b29716dbfafe24dd66e8a9c2405aa1330ea96f60 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 13 Oct 2021 06:25:24 -0400 Subject: [PATCH 2154/5615] Remove `httpclient5_test` (#4562) Co-authored-by: Richard North --- .github/workflows/ci.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d27955d33e3..7421b601c8d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,12 +56,3 @@ jobs: - name: Build and test with Gradle (${{matrix.gradle_args}}) run: | ./gradlew --no-daemon --continue --scan ${{matrix.gradle_args}} - httpclient5_test: - runs-on: ubuntu-18.04 - steps: - - uses: actions/checkout@v2 - - name: Build with Gradle - run: | - echo "transport.type=httpclient5" > $HOME/.testcontainers.properties - cat $HOME/.testcontainers.properties - ./gradlew --no-daemon --scan testcontainers:test --tests '*EventStreamTest' From 69cf13c307a3e898297bce08f7537b4ebabae7e2 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 13 Oct 2021 07:12:46 -0400 Subject: [PATCH 2155/5615] Perform port checks in parallel (#4463) The checks will now also incorporate Awaitility. --- .../wait/strategy/AbstractWaitStrategy.java | 16 ++++++ .../wait/strategy/HostPortWaitStrategy.java | 52 ++++++++++++++++--- 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/AbstractWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/AbstractWaitStrategy.java index dd9421cccf3..5656c21ae1d 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/AbstractWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/AbstractWaitStrategy.java @@ -6,10 +6,26 @@ import java.time.Duration; import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; public abstract class AbstractWaitStrategy implements WaitStrategy { + static final ExecutorService EXECUTOR = Executors.newCachedThreadPool(new ThreadFactory() { + + private final AtomicLong COUNTER = new AtomicLong(0); + + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r, "testcontainers-wait-" + COUNTER.getAndIncrement()); + thread.setDaemon(true); + return thread; + } + }); + private static final RateLimiter DOCKER_CLIENT_RATE_LIMITER = RateLimiterBuilder .newBuilder() .withRate(1, TimeUnit.SECONDS) diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/HostPortWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/HostPortWaitStrategy.java index a0ba1e9950c..ed6365566f1 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/HostPortWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/HostPortWaitStrategy.java @@ -1,16 +1,23 @@ package org.testcontainers.containers.wait.strategy; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import org.rnorth.ducttape.TimeoutException; -import org.rnorth.ducttape.unreliables.Unreliables; +import org.awaitility.Awaitility; import org.testcontainers.containers.ContainerLaunchException; import org.testcontainers.containers.wait.internal.ExternalPortListeningCheck; import org.testcontainers.containers.wait.internal.InternalCommandPortListeningCheck; +import java.time.Duration; +import java.time.Instant; +import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; /** @@ -22,6 +29,7 @@ public class HostPortWaitStrategy extends AbstractWaitStrategy { @Override + @SneakyThrows(InterruptedException.class) protected void waitUntilReady() { final Set externalLivenessCheckPorts = getLivenessCheckPorts(); if (externalLivenessCheckPorts.isEmpty()) { @@ -31,7 +39,6 @@ protected void waitUntilReady() { return; } - @SuppressWarnings("unchecked") List exposedPorts = waitStrategyTarget.getExposedPorts(); final Set internalPorts = getInternalPorts(externalLivenessCheckPorts, exposedPorts); @@ -41,10 +48,43 @@ protected void waitUntilReady() { Callable externalCheck = new ExternalPortListeningCheck(waitStrategyTarget, externalLivenessCheckPorts); try { - Unreliables.retryUntilTrue((int) startupTimeout.getSeconds(), TimeUnit.SECONDS, - () -> getRateLimiter().getWhenReady(() -> internalCheck.call() && externalCheck.call())); + List> futures = EXECUTOR.invokeAll(Arrays.asList( + // Blocking + () -> { + Instant now = Instant.now(); + Boolean result = internalCheck.call(); + log.debug( + "Internal port check {} for {} in {}", + Boolean.TRUE.equals(result) ? "passed" : "failed", + internalPorts, + Duration.between(now, Instant.now()) + ); + return result; + }, + // Polling + () -> { + Instant now = Instant.now(); + Awaitility.await() + .pollInSameThread() + .pollInterval(Duration.ofMillis(100)) + .pollDelay(Duration.ZERO) + .forever() + .until(externalCheck); - } catch (TimeoutException e) { + log.debug( + "External port check passed for {} mapped as {} in {}", + internalPorts, + externalLivenessCheckPorts, + Duration.between(now, Instant.now()) + ); + return true; + } + ), startupTimeout.getSeconds(), TimeUnit.SECONDS); + + for (Future future : futures) { + future.get(0, TimeUnit.SECONDS); + } + } catch (CancellationException | ExecutionException | TimeoutException e) { throw new ContainerLaunchException("Timed out waiting for container port to open (" + waitStrategyTarget.getHost() + " ports: " + From 737865fb964f6fd5dd84ff6dbe8ca42ab9efdc53 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 14 Oct 2021 11:39:27 +0100 Subject: [PATCH 2156/5615] Upgrade ryuk image to 0.3.3 (#4581) --- .../main/java/org/testcontainers/utility/ResourceReaper.java | 2 +- docs/features/configuration.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index ca657675da4..8c1f28378ef 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -95,7 +95,7 @@ public static String start(String hostIpAddress, DockerClient client) { @SneakyThrows(InterruptedException.class) public static String start(DockerClient client) { String ryukImage = ImageNameSubstitutor.instance() - .apply(DockerImageName.parse("testcontainers/ryuk:0.3.2")) + .apply(DockerImageName.parse("testcontainers/ryuk:0.3.3")) .asCanonicalNameString(); DockerClientFactory.instance().checkAndPullImage(client, ryukImage); diff --git a/docs/features/configuration.md b/docs/features/configuration.md index 8024dbb15ef..1761d1a2a71 100644 --- a/docs/features/configuration.md +++ b/docs/features/configuration.md @@ -47,7 +47,7 @@ It takes a couple of seconds, but if you want to speed up your tests, you can di Testcontainers uses public Docker images to perform different actions like startup checks, VNC recording and others. Some companies disallow the usage of Docker Hub, but you can override `*.image` properties with your own images from your private registry to workaround that. -> **ryuk.container.image = testcontainers/ryuk:0.3.2** +> **ryuk.container.image = testcontainers/ryuk:0.3.3** > Performs fail-safe cleanup of containers, and always required (unless [Ryuk is disabled](#disabling-ryuk)) > **tinyimage.container.image = alpine:3.14** @@ -74,7 +74,7 @@ Some companies disallow the usage of Docker Hub, but you can override `*.image` ## Customizing Ryuk resource reaper -> **ryuk.container.image = testcontainers/ryuk:0.3.2** +> **ryuk.container.image = testcontainers/ryuk:0.3.3** > The resource reaper is responsible for container removal and automatic cleanup of dead containers at JVM shutdown > **ryuk.container.privileged = false** From 2174928a5d2ac6e117643c12300171a03f8de7f8 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Thu, 14 Oct 2021 13:44:14 +0200 Subject: [PATCH 2157/5615] Remove unused `@ClassRule` container in `KafkaContainerTest` and use `cp-kafka:6.2.1` as default (#4564) --- docs/modules/kafka.md | 4 +-- .../containers/KafkaContainerTest.java | 34 ++++++++----------- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/docs/modules/kafka.md b/docs/modules/kafka.md index b0cd1742f6b..58f447d412e 100644 --- a/docs/modules/kafka.md +++ b/docs/modules/kafka.md @@ -10,9 +10,9 @@ More precisely Testcontainers uses the official Docker images for [Confluent OSS ## Example -The following field in your JUnit test class will prepare a container running Kafka: +Create a `KafkaContainer` to use it in your tests: -[JUnit Rule](../../modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java) inside_block:junitRule +[Creating a KafkaContainer](../../modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java) inside_block:constructorWithVersion The correspondence between Confluent Platform versions and Kafka versions can be seen [in Confluent documentation](https://docs.confluent.io/current/installation/versions-interoperability.html#cp-and-apache-kafka-compatibility) diff --git a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java index e16081d16d9..f0ef0db2067 100644 --- a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java +++ b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java @@ -1,14 +1,6 @@ package org.testcontainers.containers; -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.tuple; - import com.google.common.collect.ImmutableMap; -import java.time.Duration; -import java.util.Collection; -import java.util.UUID; -import java.util.concurrent.TimeUnit; import org.apache.kafka.clients.admin.AdminClient; import org.apache.kafka.clients.admin.AdminClientConfig; import org.apache.kafka.clients.admin.NewTopic; @@ -21,22 +13,25 @@ import org.apache.kafka.clients.producer.ProducerRecord; import org.apache.kafka.common.serialization.StringDeserializer; import org.apache.kafka.common.serialization.StringSerializer; -import org.junit.ClassRule; import org.junit.Test; import org.rnorth.ducttape.unreliables.Unreliables; import org.testcontainers.Testcontainers; import org.testcontainers.utility.DockerImageName; +import java.time.Duration; +import java.util.Collection; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + public class KafkaContainerTest { - private static final DockerImageName KAFKA_TEST_IMAGE = DockerImageName.parse("confluentinc/cp-kafka:5.4.3"); + private static final DockerImageName KAFKA_TEST_IMAGE = DockerImageName.parse("confluentinc/cp-kafka:6.2.1"); private static final DockerImageName ZOOKEEPER_TEST_IMAGE = DockerImageName.parse("confluentinc/cp-zookeeper:4.0.0"); - // junitRule { - @ClassRule - public static KafkaContainer kafka = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:5.4.3")); - // } - @Test public void testUsage() throws Exception { try (KafkaContainer kafka = new KafkaContainer(KAFKA_TEST_IMAGE)) { @@ -50,7 +45,7 @@ public void testUsage() throws Exception { public void testUsageWithSpecificImage() throws Exception { try ( // constructorWithVersion { - KafkaContainer kafka = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:5.4.3")) + KafkaContainer kafka = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:6.2.1")) // } ) { kafka.start(); @@ -62,11 +57,10 @@ public void testUsageWithSpecificImage() throws Exception { } } - @Test public void testUsageWithVersion() throws Exception { try ( - KafkaContainer kafka = new KafkaContainer("5.5.1") + KafkaContainer kafka = new KafkaContainer("6.2.1") ) { kafka.start(); testKafkaFunctionality(kafka.getBootstrapServers()); @@ -105,9 +99,9 @@ public void testExternalZookeeperWithExternalNetwork() throws Exception { } @Test - public void testConfluentPlatformVersion6() throws Exception { + public void testConfluentPlatformVersion5() throws Exception { try ( - KafkaContainer kafka = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:6.0.0")) + KafkaContainer kafka = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:5.4.3")) ) { kafka.start(); testKafkaFunctionality(kafka.getBootstrapServers()); From 81317a66024cb82f99d349c5b3247f8e3616c7ae Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 18 Oct 2021 07:15:46 -0400 Subject: [PATCH 2158/5615] Provide an API to force access to the host (#4584) --- .../containers/GenericContainer.java | 16 +++++++ .../containers/PortForwardingContainer.java | 16 +++++++ .../containers/ExposedHostTest.java | 47 ++++++++++++------- docs/features/networking.md | 3 +- 4 files changed, 63 insertions(+), 19 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 6d9551b8d6e..c2387cf5977 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -231,6 +231,7 @@ public class GenericContainer> @Setter(AccessLevel.NONE) private boolean shouldBeReused = false; + private boolean hostAccessible = false; public GenericContainer(@NonNull final DockerImageName dockerImageName) { this.image = new RemoteDockerImage(dockerImageName); @@ -834,6 +835,9 @@ private void applyConfiguration(CreateContainerCmd createCommand) { createCommand.withNetworkMode(networkForLinks.get()); } + if (hostAccessible) { + PortForwardingContainer.INSTANCE.start(); + } PortForwardingContainer.INSTANCE.getNetwork().ifPresent(it -> { withExtraHost(INTERNAL_HOST_HOSTNAME, it.getIpAddress()); }); @@ -1424,6 +1428,18 @@ public SELF withReuse(boolean reusable) { return self(); } + /** + * Forces access to the tests host machine. + * Use this method if you need to call {@link org.testcontainers.Testcontainers#exposeHostPorts(int...)} + * after you start this container. + * + * @return this + */ + public SELF withAccessToHost(boolean value) { + this.hostAccessible = value; + return self(); + } + @Override public boolean equals(Object o) { return this == o; diff --git a/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java b/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java index b3f020500a1..a854b1e55c3 100644 --- a/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java +++ b/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java @@ -15,6 +15,7 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicReference; public enum PortForwardingContainer { INSTANCE; @@ -68,9 +69,24 @@ public void exposeHostPort(int hostPort, int containerPort) { } } + void start() { + getSshConnection(); + } + Optional getNetwork() { return Optional.ofNullable(container) .map(GenericContainer::getContainerInfo) .flatMap(it -> it.getNetworkSettings().getNetworks().values().stream().findFirst()); } + + void reset() { + if (container != null) { + container.stop(); + } + container = null; + + ((AtomicReference) (Object) sshConnection).set(null); + + exposedPorts.clear(); + } } diff --git a/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java b/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java index d17b0709dfa..38005014662 100644 --- a/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java +++ b/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java @@ -3,6 +3,7 @@ import com.google.common.collect.ImmutableMap; import com.sun.net.httpserver.HttpServer; import lombok.SneakyThrows; +import org.junit.After; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -31,42 +32,52 @@ public static void setUpClass() throws Exception { }); server.start(); - Testcontainers.exposeHostPorts(server.getAddress().getPort()); - - Testcontainers.exposeHostPorts(ImmutableMap.of(server.getAddress().getPort(), 80)); - Testcontainers.exposeHostPorts(ImmutableMap.of(server.getAddress().getPort(), 81)); } @AfterClass - public static void tearDownClass() throws Exception { + public static void tearDownClass() { server.stop(0); } + @After + public void tearDown() { + PortForwardingContainer.INSTANCE.reset(); + } + + @Test + public void testExposedHostAfterContainerIsStarted() { + try ( + GenericContainer container = new GenericContainer<>(TINY_IMAGE) + .withCommand("top") + .withAccessToHost(true) + ) { + container.start(); + Testcontainers.exposeHostPorts(server.getAddress().getPort()); + assertResponse(container, server.getAddress().getPort()); + } + } + @Test public void testExposedHost() throws Exception { - assertResponse(new GenericContainer<>(TINY_IMAGE) - .withCommand("top"), - server.getAddress().getPort()); + Testcontainers.exposeHostPorts(server.getAddress().getPort()); + assertResponse(new GenericContainer<>(TINY_IMAGE).withCommand("top"), server.getAddress().getPort()); } @Test public void testExposedHostWithNetwork() throws Exception { + Testcontainers.exposeHostPorts(server.getAddress().getPort()); try (Network network = Network.newNetwork()) { - assertResponse(new GenericContainer<>(TINY_IMAGE) - .withNetwork(network) - .withCommand("top"), - server.getAddress().getPort()); + assertResponse(new GenericContainer<>(TINY_IMAGE).withNetwork(network).withCommand("top"), server.getAddress().getPort()); } } @Test public void testExposedHostPortOnFixedInternalPorts() throws Exception { - assertResponse(new GenericContainer<>(TINY_IMAGE) - .withCommand("top"), - 80); - assertResponse(new GenericContainer<>(TINY_IMAGE) - .withCommand("top"), - 81); + Testcontainers.exposeHostPorts(ImmutableMap.of(server.getAddress().getPort(), 80)); + Testcontainers.exposeHostPorts(ImmutableMap.of(server.getAddress().getPort(), 81)); + + assertResponse(new GenericContainer<>(TINY_IMAGE).withCommand("top"), 80); + assertResponse(new GenericContainer<>(TINY_IMAGE).withCommand("top"), 81); } @SneakyThrows diff --git a/docs/features/networking.md b/docs/features/networking.md index e7d8493483e..7ecf929da16 100644 --- a/docs/features/networking.md +++ b/docs/features/networking.md @@ -69,7 +69,8 @@ We need to tell Testcontainers to prepare to expose this port to containers: !!! warning - Note that the above command should be invoked _before_ containers are started, but _after_ the server on the host was started. + Note that the above command should be invoked _before_ containers are started, but _after_ the server on the host was started. + Alternatively, use `container.withAccessToHost(true)` to force the host access mechanism (you still need to call `exposeHostPorts` to make the port available). Having done so, we can now access this port from any containers that are launched. From a container's perspective, the hostname will be `host.testcontainers.internal` and the port will be the same value as `localServerPort`. From 31e55619a36f89afe27234b94e0250417dc5ff8f Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 20 Oct 2021 17:19:42 +0100 Subject: [PATCH 2159/5615] Remove Linux Azure pipelines build for Linux (#4452) This retains Windows build --- azure-pipelines.yml | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 247948901d8..91e33f202a9 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -3,21 +3,6 @@ jobs: - job: azure_pipeline_tests steps: - # Run a minimal set of tests in normal Linux pipeline builds - - task: Gradle@2 - condition: eq(variables['Agent.OS'], 'Linux') - displayName: Build & test (Linux - minimal core) - env: - AWS_ACCESS_KEY_ID: $(aws.accessKeyId) - AWS_SECRET_ACCESS_KEY: $(aws.secretAccessKey) - inputs: - gradleWrapperFile: 'gradlew' - jdkVersionOption: '1.11' - options: "--no-daemon --continue" - tasks: "clean testcontainers:test --tests GenericContainerRuleTest" - publishJUnitResults: true - testResultsFiles: '**/TEST-*.xml' - # Run all core tests when running the Windows CI tests - task: Gradle@2 condition: eq(variables['Agent.OS'], 'Windows_NT') From e122628ab3493967636e5f78498d594b12d7d3ff Mon Sep 17 00:00:00 2001 From: Oguzhan Ozdemir Date: Thu, 21 Oct 2021 14:33:09 +0300 Subject: [PATCH 2160/5615] Revert "Temporarily disable Thundra on master branch" (#4571) Co-authored-by: Kevin Wittek --- .github/workflows/ci.yml | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7421b601c8d..7336e0767e1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,6 +9,14 @@ env: AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} jobs: + thundra_test_initializer: + runs-on: ubuntu-latest + outputs: + thundra_agent_testrun_id: ${{ steps.thundra_test_initializer.outputs.thundra_agent_testrun_id }} + steps: + - uses: actions/checkout@v2 + - id: thundra_test_initializer + uses: thundra-io/thundra-test-init-action@v1 find_gradle_jobs: runs-on: ubuntu-18.04 outputs: @@ -34,7 +42,7 @@ jobs: echo $TASKS echo "::set-output name=matrix::{\"gradle_args\":$TASKS}" check: - needs: [find_gradle_jobs] + needs: [find_gradle_jobs, thundra_test_initializer] strategy: fail-fast: false matrix: ${{ fromJson(needs.find_gradle_jobs.outputs.matrix) }} @@ -53,6 +61,16 @@ jobs: key: ${{ runner.os }}-gradle-home-${{matrix.gradle_args}}_check-${{ hashFiles('**/*.gradle') }} - name: Clear existing docker image cache run: docker image prune -af + - name: Thundra Gradle Test Instrumentation + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + uses: thundra-io/thundra-gradle-test-action@v1 + with: + apikey: ${{ secrets.THUNDRA_API_KEY }} + project_id: ${{ secrets.THUNDRA_PROJECT_ID }} - name: Build and test with Gradle (${{matrix.gradle_args}}) run: | - ./gradlew --no-daemon --continue --scan ${{matrix.gradle_args}} + ./gradlew --no-daemon --continue --scan ${{matrix.gradle_args}} \ + $([[ -f $THUNDRA_GRADLE_INIT_SCRIPT_PATH ]] && echo "--init-script $THUNDRA_GRADLE_INIT_SCRIPT_PATH") + env: + THUNDRA_AGENT_TEST_RUN_ID: ${{ needs.thundra_test_initializer.outputs.thundra_agent_testrun_id }} + THUNDRA_AGENT_REPORT_REST_BASEURL: https://collector.thundra.us/v1 \ No newline at end of file From a5418861a0eb605e049ae4ae6a7d6fa08c0e2327 Mon Sep 17 00:00:00 2001 From: Michael Nitschinger Date: Thu, 21 Oct 2021 16:15:33 +0200 Subject: [PATCH 2161/5615] [couchbase] Add support for Couchbase Analytics. (#4592) Co-authored-by: Richard North --- .../couchbase/CouchbaseContainer.java | 48 ++++++++++++++++++- .../couchbase/CouchbaseService.java | 7 ++- .../couchbase/CouchbaseContainerTest.java | 16 +++++++ 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index e9247b8c84a..db8d8ec9666 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -68,6 +68,10 @@ public class CouchbaseContainer extends GenericContainer { private static final int SEARCH_SSL_PORT = 18094; + private static final int ANALYTICS_PORT = 8095; + + private static final int ANALYTICS_SSL_PORT = 18095; + private static final int KV_PORT = 11210; private static final int KV_SSL_PORT = 11207; @@ -84,7 +88,17 @@ public class CouchbaseContainer extends GenericContainer { private String password = "password"; - private Set enabledServices = EnumSet.allOf(CouchbaseService.class); + /** + * Enabled services does not include Analytics since most users likely do not need to test + * with it and is also a little heavy on memory and runtime requirements. Also, it is only + * available with the enterprise edition (EE). + */ + private Set enabledServices = EnumSet.of( + CouchbaseService.KV, + CouchbaseService.QUERY, + CouchbaseService.SEARCH, + CouchbaseService.INDEX + ); private final List buckets = new ArrayList<>(); @@ -144,6 +158,17 @@ public CouchbaseContainer withEnabledServices(final CouchbaseService... enabled) return this; } + /** + * Enables the analytics service which is not enabled by default. + * + * @return this {@link CouchbaseContainer} for chaining purposes. + */ + public CouchbaseContainer withAnalyticsService() { + checkNotRunning(); + this.enabledServices.add(CouchbaseService.ANALYTICS); + return this; + } + public final String getUsername() { return username; } @@ -177,6 +202,8 @@ protected void configure() { QUERY_SSL_PORT, SEARCH_PORT, SEARCH_SSL_PORT, + ANALYTICS_PORT, + ANALYTICS_SSL_PORT, KV_PORT, KV_SSL_PORT ); @@ -214,6 +241,16 @@ protected void configure() { ); } + if (enabledServices.contains(CouchbaseService.ANALYTICS)) { + waitStrategy = waitStrategy.withStrategy( + new HttpWaitStrategy() + .forPath("/admin/ping") + .forPort(ANALYTICS_PORT) + .withBasicCredentials(username, password) + .forStatusCode(200) + ); + } + waitingFor(waitStrategy); } @@ -262,6 +299,10 @@ private void initializeIsEnterprise() { } catch (IOException e) { throw new IllegalStateException("Couchbase /pools did not return valid JSON"); } + + if (!isEnterprise && enabledServices.contains(CouchbaseService.ANALYTICS)) { + throw new IllegalStateException("The Analytics Service is only supported with the Enterprise version"); + } } /** @@ -349,6 +390,11 @@ private void configureExternalPorts() { builder.add("ftsSSL", Integer.toString(getMappedPort(SEARCH_SSL_PORT))); } + if (enabledServices.contains(CouchbaseService.ANALYTICS)) { + builder.add("cbas", Integer.toString(getMappedPort(ANALYTICS_PORT))); + builder.add("cbasSSL", Integer.toString(getMappedPort(ANALYTICS_SSL_PORT))); + } + @Cleanup Response response = doHttpRequest( MGMT_PORT, "/node/controller/setupAlternateAddresses/external", diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseService.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseService.java index c60fc6b71d0..57d3e37fcf2 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseService.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseService.java @@ -39,7 +39,12 @@ public enum CouchbaseService { /** * Indexing service (needed if QUERY is also used!). */ - INDEX("index"); + INDEX("index"), + + /** + * Analytics service. + */ + ANALYTICS("cbas"); private final String identifier; diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java index fcebbe2bb62..5d2d7d9402a 100644 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java @@ -21,6 +21,7 @@ import com.couchbase.client.java.Collection; import com.couchbase.client.java.json.JsonObject; import org.junit.Test; +import org.testcontainers.containers.ContainerLaunchException; import org.testcontainers.utility.DockerImageName; import java.time.Duration; @@ -29,6 +30,7 @@ import static org.awaitility.Awaitility.await; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; public class CouchbaseContainerTest { @@ -111,6 +113,20 @@ public void testBucketIsFlushableIfEnabled() { } } + /** + * Make sure that the code fails fast if the Analytics service is enabled on the community + * edition which is not supported. + */ + @Test + public void testFailureIfCommunityUsedWithAnalytics() { + try ( + CouchbaseContainer container = new CouchbaseContainer(COUCHBASE_IMAGE_COMMUNITY) + .withEnabledServices(CouchbaseService.KV, CouchbaseService.ANALYTICS) + ) { + assertThrows(ContainerLaunchException.class, () -> setUpClient(container, cluster -> {})); + } + } + private void setUpClient(CouchbaseContainer container, Consumer consumer) { container.start(); From e257baca07068fdc34894060a1997383c32dc040 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Fri, 22 Oct 2021 13:04:22 +0200 Subject: [PATCH 2162/5615] Fix race condition in external port check (#4597) Makes Awaitility ignore exception (which are expected to occur) during checking external ports. --- .../containers/wait/strategy/HostPortWaitStrategy.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/HostPortWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/HostPortWaitStrategy.java index ed6365566f1..38ddcb44db6 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/HostPortWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/HostPortWaitStrategy.java @@ -68,6 +68,7 @@ protected void waitUntilReady() { .pollInSameThread() .pollInterval(Duration.ofMillis(100)) .pollDelay(Duration.ZERO) + .ignoreExceptions() .forever() .until(externalCheck); From 820161f7be7e43f36b48e2347e3e612bb905bc60 Mon Sep 17 00:00:00 2001 From: Tobias Stadler Date: Fri, 22 Oct 2021 14:17:56 +0200 Subject: [PATCH 2163/5615] Added started log message used in Selenium 4.0.0 (#4580) --- .../containers/BrowserWebDriverContainer.java | 4 ++-- .../junit/{Selenium3xTest.java => SeleniumStartTest.java} | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) rename modules/selenium/src/test/java/org/testcontainers/junit/{Selenium3xTest.java => SeleniumStartTest.java} (86%) diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index 1209b7af019..17544308691 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -78,7 +78,7 @@ public class BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>(imageName) .withCapabilities(new ChromeOptions())) { From ce57df66eff75e77c545ef840376816a19e54cb7 Mon Sep 17 00:00:00 2001 From: Alexander Lehmann Date: Fri, 22 Oct 2021 14:25:46 +0200 Subject: [PATCH 2164/5615] Fix double nested lambda in `getWithTimeout` (#4470) This fixes #4469, `getWithTimeou`t did only apply to the outer lambda, which returned immediately (the inner lambda). Instead, wrap getWithTimeout itself inside a lambda. --- .../testcontainers/containers/BrowserWebDriverContainer.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index 17544308691..7bc37e4701c 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -281,9 +281,8 @@ public int getPort() { @Override protected void containerIsStarted(InspectContainerResponse containerInfo) { driver = Unreliables.retryUntilSuccess(30, TimeUnit.SECONDS, - Timeouts.getWithTimeout(10, TimeUnit.SECONDS, - () -> - () -> new RemoteWebDriver(getSeleniumAddress(), capabilities))); + () -> Timeouts.getWithTimeout(10, TimeUnit.SECONDS, + () -> new RemoteWebDriver(getSeleniumAddress(), capabilities))); if (vncRecordingContainer != null) { LOGGER.debug("Starting VNC recording"); From 1469b22dc2d0cc4694d080f958e403f598632a55 Mon Sep 17 00:00:00 2001 From: Florent Biville <445792+fbiville@users.noreply.github.com> Date: Fri, 22 Oct 2021 14:27:14 +0200 Subject: [PATCH 2165/5615] Make Bolt log wait strategy more lenient (#4455) Fixes #4454 --- .../main/java/org/testcontainers/containers/Neo4jContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java index e93a840302a..7890bf5dcad 100644 --- a/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java +++ b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java @@ -91,7 +91,7 @@ public Neo4jContainer(final DockerImageName dockerImageName) { dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); WaitStrategy waitForBolt = new LogMessageWaitStrategy() - .withRegEx(String.format(".*Bolt enabled on 0\\.0\\.0\\.0:%d\\.\n", DEFAULT_BOLT_PORT)); + .withRegEx(String.format(".*Bolt enabled on .*:%d\\.\n", DEFAULT_BOLT_PORT)); WaitStrategy waitForHttp = new HttpWaitStrategy() .forPort(DEFAULT_HTTP_PORT) .forStatusCodeMatching(response -> response == HTTP_OK); From 902e45a5aca8416ede36b37ea5a84596b5b696ef Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Fri, 22 Oct 2021 14:28:39 +0200 Subject: [PATCH 2166/5615] Update RELEASING.md (#4590) Co-authored-by: Sergei Egorov Co-authored-by: Richard North --- RELEASING.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 34a6b55ae26..4ad91b56477 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -6,18 +6,21 @@ Testcontainers' release process is semi-automated through GitHub Actions. This d 1. Ensure that the master branch is building and that tests are passing. 1. Create a new release on GitHub. **The tag name is used as the version**, so please keep the tag name plain (e.g. 1.2.3). -1. The release triggers a GitHub Action workflow, but it gets mostly build using results from the Gradle remote-cache. Therefore, this should be fairly fast. -1. Login to Sonatype to check the staging repository. -1. Get the staging url after GitHub Action workflow finished. -1. Manually test the release with the staging url as maven repository url (e.g. critical issues and features). -1. Run [TinSalver](https://github.com/bsideup/tinsalver) from GitHub using `npx` to sign artifact (see [TinsSalver README](https://github.com/bsideup/tinsalver/blob/main/README.md)). -1. Close the release in Sonatype. This will evaluate the release based on given Sontaype rules and afterwards automatically sync to Maven Central. -1. Handcraft and polish some of the release notes (e.g. substitute combinded dependency PRs and highlight certain features). -1. When available through Maven Central, poke [Richard North](https://github.com/rnorth) to announce the release on Twitter! +1. The release triggers a GitHub Action workflow. +1. Log in to [Sonatype](https://oss.sonatype.org/) to check the staging repository. + * Getting access to Sonatype requires a Sonatype JIRA account and [raising an issue](https://issues.sonatype.org/browse/OSSRH-74229), requesting access. +3. Get the staging URL from Sonatype after GitHub Action workflow finished. The general URL format should be `https://oss.sonatype.org/service/local/repositories/$staging-repo-id/content/` +4. Manually test the release with the staging URL as maven repository URL (e.g. critical issues and features). +5. Run [TinSalver](https://github.com/bsideup/tinsalver) from GitHub using `npx` to sign artifact (see [TinSalver README](https://github.com/bsideup/tinsalver/blob/main/README.md)). + * For TinSalver to correctly work with keybase on WSL on Windows, you might need to disable pinentry: `keybase config set -b pinentry.disabled true`. +7. Close the release in Sonatype. This will evaluate the release based on given Sonatype rules. +8. After successful closing, the release button needs to be clicked and afterwards it is automatically synced to Maven Central. +9. Handcraft and polish some of the release notes (e.g. substitute combinded dependency PRs and highlight certain features). +10. When available through Maven Central, poke [Richard North](https://github.com/rnorth) to announce the release on Twitter! ## Internal details * The process is done with GitHub Actions, TinSalver and Sonatype. * Sonatype will automatically promote the staging release to Maven Central. * Keybase needs to be installed on the developer machine. -* GPG key of signing developer needs to be uplodaed to the Ubuntu keyserver (or other server supported by Sonatype). +* GPG key of signing developer needs to be uplodaed to the [Ubuntu keyserver](https://keyserver.ubuntu.com/) (or other server supported by Sonatype). From 76f11b344ce42ad920828b0628a7ec929a9d0eca Mon Sep 17 00:00:00 2001 From: Robert Munteanu Date: Fri, 22 Oct 2021 14:54:53 +0200 Subject: [PATCH 2167/5615] Upgrade to Docker Compose 1.29.2 (#4164) (#4461) --- .../org/testcontainers/containers/DockerComposeContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 89d6d692db7..961d066cb91 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -608,7 +608,7 @@ interface DockerCompose { class ContainerisedDockerCompose extends GenericContainer implements DockerCompose { public static final char UNIX_PATH_SEPERATOR = ':'; - public static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("docker/compose:1.24.1"); + public static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("docker/compose:1.29.2"); public ContainerisedDockerCompose(List composeFiles, String identifier) { From 436fde8cbe1a24b95b6e872bb2247d8018758e2d Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Fri, 22 Oct 2021 17:28:37 +0200 Subject: [PATCH 2168/5615] Don't run tests as part of the release build (#4600) --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4e1e9d7c5a1..3779af890ae 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,7 +21,7 @@ jobs: run: docker image prune -af - name: Run Gradle Build - run: ./gradlew build --scan --no-daemon -i + run: ./gradlew build --scan --no-daemon -i -x test - name: Run Gradle Publish run: | From 94a1a111d6369edead0ff6e051af26d7dc4799ef Mon Sep 17 00:00:00 2001 From: Oguzhan Ozdemir Date: Mon, 25 Oct 2021 13:19:01 +0300 Subject: [PATCH 2169/5615] Suggestion to add Thundra test summary badge (#4604) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 752c501e91f..9bcf6f002f8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Testcontainers [![Thundra Foresight](https://thundra-assets-prod.s3.us-west-2.amazonaws.com/images/badges/thundra-foresight-badge-enabled.svg)](https://foresight.thundra.live/testRuns/5a49cc30-f063-11eb-9a03-0242ac130003) +[![Thundra Foresight Test Results](https://foresight.service.thundra.us/public/api/v1/badge/test/5a49cc30-f063-11eb-9a03-0242ac130003)](https://foresight.thundra.live/testRuns/5a49cc30-f063-11eb-9a03-0242ac130003) > Testcontainers is a Java library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container. From 5cbf52a95b81d9a614bff473b617861673119f0b Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 27 Oct 2021 11:19:21 +0100 Subject: [PATCH 2170/5615] Update docs version --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 5d182b79590..be30f26a964 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -97,4 +97,4 @@ nav: - contributing_docs.md - bounty.md extra: - latest_version: 1.16.0 + latest_version: 1.16.2 From 0654ef6a9186eab85df3accd9ea52a37c68853b0 Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 27 Oct 2021 12:16:08 +0100 Subject: [PATCH 2171/5615] Use automated PR to update docs version (#4606) --- .github/workflows/update-docs-version.yml | 45 +++++++++++------------ 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index ee11b83026a..3311847da5d 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -1,28 +1,27 @@ name: Update docs version on: - release: - types: [published] + release: + types: [ published ] jobs: - build: - if: github.repository == 'testcontainers/testcontainers-java' - runs-on: ubuntu-18.04 - steps: - - uses: actions/checkout@v2 - with: - ref: master - - name: Update latest_version property in mkdocs.yml - run: | - sed -i "s/latest_version: .*/latest_version: ${GITHUB_REF##*/}/g" mkdocs.yml - git diff - - name: Commit changes - run: | - git config user.email "nobody@testcontainers.org" - git config user.name "GitHub Action - Update docs version" - git add mkdocs.yml - git commit -m "Update documentation version to ${GITHUB_REF##*/}" -m "skip-checks: true" || echo "not changed" - - name: Push changes - uses: ad-m/github-push-action@40bf560936a8022e68a3c00e7d2abefaf01305a6 # v0.6.0 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} + build: + if: github.repository == 'testcontainers/testcontainers-java' + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + with: + ref: master + - name: Update latest_version property in mkdocs.yml + run: | + sed -i "s/latest_version: .*/latest_version: ${GITHUB_REF##*/}/g" mkdocs.yml + git diff + - name: Create Pull Request + uses: peter-evans/create-pull-request@3c3d696d5b8aa2eb0e70f76c251e4b149122acf1 # v3.10.1 + with: + title: Update docs version to ${GITHUB_REF##*/} + body: | + Update docs version to ${GITHUB_REF##*/} + skip-checks: true + branch: update-docs-version + delete-branch: true From 70cc4e3b3df6062251d6aa8191a91b7f88b44f6f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 22:00:34 +0000 Subject: [PATCH 2172/5615] Bump mysql-connector-java from 8.0.26 to 8.0.27 in /modules/mysql Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.26 to 8.0.27. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.26...8.0.27) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index e465acaaee3..d84def3e7ba 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' testImplementation project(':jdbc-test') - testImplementation 'mysql:mysql-connector-java:8.0.26' + testImplementation 'mysql:mysql-connector-java:8.0.27' testImplementation testFixtures(project(':r2dbc')) testImplementation 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' From 659b7a5274b5db8c5d35c7c8952e35b27686d599 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 22:00:34 +0000 Subject: [PATCH 2173/5615] Bump neo4j from 3.5.9 to 3.5.29 in /modules/neo4j Bumps [neo4j](https://github.com/neo4j/neo4j) from 3.5.9 to 3.5.29. - [Release notes](https://github.com/neo4j/neo4j/releases) - [Commits](https://github.com/neo4j/neo4j/commits) --- updated-dependencies: - dependency-name: org.neo4j:neo4j dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 2c25d148418..51cc625dc9d 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -29,7 +29,7 @@ task customNeo4jPluginJar(type: Jar) { test.dependsOn customNeo4jPluginJar dependencies { - customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.9" + customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.29" api project(":testcontainers") From bf7f4e3ef7658e33ce02c84f139f41b658c5fb72 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 22:00:35 +0000 Subject: [PATCH 2174/5615] Bump testcontainers from 1.16.0 to 1.16.2 in /core Bumps [testcontainers](https://github.com/testcontainers/testcontainers-java) from 1.16.0 to 1.16.2. - [Release notes](https://github.com/testcontainers/testcontainers-java/releases) - [Changelog](https://github.com/testcontainers/testcontainers-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-java/compare/1.16.0...1.16.2) --- updated-dependencies: - dependency-name: org.testcontainers:testcontainers dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index e5a36114afb..ad0e189acec 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -53,7 +53,7 @@ configurations.all { } dependencies { - baseline 'org.testcontainers:testcontainers:1.16.0', { + baseline 'org.testcontainers:testcontainers:1.16.2', { exclude group: "*", module: "*" } From eb95474dc571679d049379861025e6250fac57d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 22:00:35 +0000 Subject: [PATCH 2175/5615] Bump mysql-connector-java from 8.0.26 to 8.0.27 in /modules/spock Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.26 to 8.0.27. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.26...8.0.27) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 1e280e88965..478f3bdd31c 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -16,7 +16,7 @@ dependencies { testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' testRuntimeOnly 'org.postgresql:postgresql:42.2.24' - testRuntimeOnly 'mysql:mysql-connector-java:8.0.26' + testRuntimeOnly 'mysql:mysql-connector-java:8.0.27' testCompileClasspath 'org.jetbrains:annotations:22.0.0' } From 3d3c63189af29f64e4f28d9380bc0127a1b44268 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 22:00:35 +0000 Subject: [PATCH 2176/5615] Bump aws-java-sdk-dynamodb from 1.12.60 to 1.12.100 in /modules/dynalite Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.12.60 to 1.12.100. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.60...1.12.100) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-dynamodb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/dynalite/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 4cf9b02f160..7dbadb27133 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Dynalite" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.60' - testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.37' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.100' + testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.100' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 652f433b06624c4967f730b90b64514af2457ac2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 22:00:35 +0000 Subject: [PATCH 2177/5615] Bump aws-java-sdk-s3 from 1.12.80 to 1.12.100 in /modules/localstack Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.80 to 1.12.100. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.80...1.12.100) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 0d089bdf95f..c1f29a861bc 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.80' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.100' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.80' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.82' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.60' From 0a27e65fd32e27093bb6f1a1777555a20d9266dd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 22:00:36 +0000 Subject: [PATCH 2178/5615] Bump mysql-connector-java from 8.0.26 to 8.0.27 in /modules/jdbc-test Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.26 to 8.0.27. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.26...8.0.27) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 6e03f3e1969..3cfed4d80c0 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -16,5 +16,5 @@ dependencies { api 'org.apache.tomcat:tomcat-jdbc:10.0.12' api 'org.vibur:vibur-dbcp:25.0' - api 'mysql:mysql-connector-java:8.0.26' + api 'mysql:mysql-connector-java:8.0.27' } From e22501afad14bfe77e673e2f7d14f8c81a8fb89e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 22:00:36 +0000 Subject: [PATCH 2179/5615] Bump pulsar-client from 2.7.2 to 2.7.3 in /modules/pulsar Bumps [pulsar-client](https://github.com/apache/pulsar) from 2.7.2 to 2.7.3. - [Release notes](https://github.com/apache/pulsar/releases) - [Commits](https://github.com/apache/pulsar/compare/v2.7.2...v2.7.3) --- updated-dependencies: - dependency-name: org.apache.pulsar:pulsar-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index d886be91591..ef3a4298128 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Pulsar" dependencies { api project(':testcontainers') - testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.7.2' + testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.7.3' testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.21.0' testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.7.2' } From 49f558b79bd93028030fe0aa5813e0cf82a673d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 22:00:39 +0000 Subject: [PATCH 2180/5615] Bump aws-java-sdk-sqs from 1.12.82 to 1.12.100 in /modules/localstack Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.12.82 to 1.12.100. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.82...1.12.100) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-sqs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 0d089bdf95f..254f4093667 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.80' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.80' - testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.82' + testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.100' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.60' testImplementation 'software.amazon.awssdk:s3:2.17.52' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From 4fc1d8a14cf749d66d473752a84e0be7381a2846 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 22:00:39 +0000 Subject: [PATCH 2181/5615] Bump mockito-core from 3.12.4 to 4.0.0 in /core Bumps [mockito-core](https://github.com/mockito/mockito) from 3.12.4 to 4.0.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.12.4...v4.0.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index e5a36114afb..995da71ffd4 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -93,7 +93,7 @@ dependencies { testImplementation 'com.rabbitmq:amqp-client:5.13.1' testImplementation 'org.mongodb:mongo-java-driver:3.12.10' - testImplementation ('org.mockito:mockito-core:3.12.4') { + testImplementation ('org.mockito:mockito-core:4.0.0') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From 63c3d68ba6f2501a2398c81fb90000cdc8d9b385 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 22:00:40 +0000 Subject: [PATCH 2182/5615] Bump java-client from 3.2.1 to 3.2.2 in /modules/couchbase Bumps [java-client](https://github.com/couchbase/couchbase-jvm-clients) from 3.2.1 to 3.2.2. - [Release notes](https://github.com/couchbase/couchbase-jvm-clients/releases) - [Commits](https://github.com/couchbase/couchbase-jvm-clients/compare/java-client-3.2.1...java-client-3.2.2) --- updated-dependencies: - dependency-name: com.couchbase.client:java-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 99cf927173e..02917b46045 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Couchbase" dependencies { api project(':testcontainers') - testImplementation 'com.couchbase.client:java-client:3.2.1' + testImplementation 'com.couchbase.client:java-client:3.2.2' testImplementation 'org.awaitility:awaitility:4.1.0' } From 82fd5d6de922147eba6c6d1502462d60d655caa6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 22:00:40 +0000 Subject: [PATCH 2183/5615] Bump transport from 7.15.0 to 7.15.1 in /modules/elasticsearch Bumps [transport](https://github.com/elastic/elasticsearch) from 7.15.0 to 7.15.1. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.15.0...v7.15.1) --- updated-dependencies: - dependency-name: org.elasticsearch.client:transport dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 2897ecb5628..1c3e66edf0d 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,6 +3,6 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') testImplementation "org.elasticsearch.client:elasticsearch-rest-client:7.15.0" - testImplementation "org.elasticsearch.client:transport:7.15.0" + testImplementation "org.elasticsearch.client:transport:7.15.1" testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From fc48919f1691d063cd5cfd247ad63a533bd5cea3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 22:00:41 +0000 Subject: [PATCH 2184/5615] Bump pulsar-client-admin from 2.7.2 to 2.7.3 in /modules/pulsar Bumps [pulsar-client-admin](https://github.com/apache/pulsar) from 2.7.2 to 2.7.3. - [Release notes](https://github.com/apache/pulsar/releases) - [Commits](https://github.com/apache/pulsar/compare/v2.7.2...v2.7.3) --- updated-dependencies: - dependency-name: org.apache.pulsar:pulsar-client-admin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index d886be91591..0a85bd4dc07 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -5,5 +5,5 @@ dependencies { testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.7.2' testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.21.0' - testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.7.2' + testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.7.3' } From a615b1e1fc8e3dbca584d5238a6c26e45e154cd2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 22:00:43 +0000 Subject: [PATCH 2185/5615] Bump postgresql from 42.2.24 to 42.3.1 in /modules/junit-jupiter Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.24 to 42.3.1. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.24...REL42.3.1) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 669a2e6b85d..7f127be30c1 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'org.assertj:assertj-core:3.21.0' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.1' - testRuntimeOnly 'org.postgresql:postgresql:42.2.24' + testRuntimeOnly 'org.postgresql:postgresql:42.3.1' testRuntimeOnly 'mysql:mysql-connector-java:8.0.26' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' } From 84161818095d53ec3d20447ab415ef5c5530a35e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 22:00:45 +0000 Subject: [PATCH 2186/5615] Bump solr-solrj from 8.10.0 to 8.10.1 in /examples Bumps solr-solrj from 8.10.0 to 8.10.1. --- updated-dependencies: - dependency-name: org.apache.solr:solr-solrj dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/solr-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/solr-container/build.gradle b/examples/solr-container/build.gradle index 373ece1df60..5f76ada58cf 100644 --- a/examples/solr-container/build.gradle +++ b/examples/solr-container/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly "org.projectlombok:lombok:1.18.20" annotationProcessor "org.projectlombok:lombok:1.18.16" - implementation 'org.apache.solr:solr-solrj:8.10.0' + implementation 'org.apache.solr:solr-solrj:8.10.1' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.testcontainers:solr' From f086d69041b6f14db9f83f62c894e8c6c7d76ab2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 22:00:47 +0000 Subject: [PATCH 2187/5615] Bump s3 from 2.17.52 to 2.17.72 in /modules/localstack Bumps s3 from 2.17.52 to 2.17.72. --- updated-dependencies: - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 0d089bdf95f..34b35ab8a1f 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.80' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.82' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.60' - testImplementation 'software.amazon.awssdk:s3:2.17.52' + testImplementation 'software.amazon.awssdk:s3:2.17.72' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 16368fc9a99eb901a78fced62e2c675d94356646 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 22:00:59 +0000 Subject: [PATCH 2188/5615] Bump cucumber-junit from 6.11.0 to 7.0.0 in /examples Bumps [cucumber-junit](https://github.com/cucumber/cucumber-jvm) from 6.11.0 to 7.0.0. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v6.11.0...v7.0.0) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-junit dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 6febeeec483..ae66a351335 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' testImplementation 'io.cucumber:cucumber-java:6.11.0' - testImplementation 'io.cucumber:cucumber-junit:6.11.0' + testImplementation 'io.cucumber:cucumber-junit:7.0.0' testImplementation 'org.testcontainers:selenium' } From 489c753d435383f145ca5d666b1463e90460d9c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 22:01:02 +0000 Subject: [PATCH 2189/5615] Bump gson from 2.8.8 to 2.8.9 in /examples Bumps [gson](https://github.com/google/gson) from 2.8.8 to 2.8.9. - [Release notes](https://github.com/google/gson/releases) - [Changelog](https://github.com/google/gson/blob/master/CHANGELOG.md) - [Commits](https://github.com/google/gson/compare/gson-parent-2.8.8...gson-parent-2.8.9) --- updated-dependencies: - dependency-name: com.google.code.gson:gson dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/disque-job-queue/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index c44b4c7e7e1..5d2876fd3b1 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly "org.projectlombok:lombok:1.18.20" annotationProcessor "org.projectlombok:lombok:1.18.20" implementation 'biz.paluch.redis:spinach:0.3' - implementation 'com.google.code.gson:gson:2.8.8' + implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' testImplementation 'ch.qos.logback:logback-classic:1.2.6' testImplementation 'org.mockito:mockito-all:1.10.19' diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 9b1424451ec..0771fbeec7f 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -9,7 +9,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' implementation 'redis.clients:jedis:3.7.0' - implementation 'com.google.code.gson:gson:2.8.8' + implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'ch.qos.logback:logback-classic:1.2.6' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 917f1b0c5a4..3651bbe6927 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -9,7 +9,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' implementation 'redis.clients:jedis:3.7.0' - implementation 'com.google.code.gson:gson:2.8.8' + implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'junit:junit:4.13.2' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 661f6efc125..44ce783694b 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -9,7 +9,7 @@ repositories { dependencies { implementation 'redis.clients:jedis:3.7.0' - implementation 'com.google.code.gson:gson:2.8.8' + implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.32' From 7ffce94eb5047b0ad6288ba62f77078baf5c2b9b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 22:01:11 +0000 Subject: [PATCH 2190/5615] Bump org.springframework.boot from 2.5.5 to 2.5.6 in /examples Bumps [org.springframework.boot](https://github.com/spring-projects/spring-boot) from 2.5.5 to 2.5.6. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.5.5...v2.5.6) --- updated-dependencies: - dependency-name: org.springframework.boot dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot-kotlin-redis/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index 48f9ed91b17..f1618268508 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.5.5' + id 'org.springframework.boot' version '2.5.6' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index e40e32a4397..c512e594a65 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,5 +1,5 @@ plugins { - id("org.springframework.boot") version "2.5.5" + id("org.springframework.boot") version "2.5.6" id("org.jetbrains.kotlin.jvm") version "1.5.31" id("org.jetbrains.kotlin.plugin.spring") version "1.5.31" } diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index 20ec4efd463..85cb38f3781 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.5.5' + id 'org.springframework.boot' version '2.5.6' } apply plugin: 'io.spring.dependency-management' From 59b0c3802d4c5721e4b0bd4529ddaf54e5d24f8a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 22:01:20 +0000 Subject: [PATCH 2191/5615] Bump toxiproxy-java from 2.1.4 to 2.1.5 in /modules/toxiproxy Bumps [toxiproxy-java](https://github.com/trekawek/toxiproxy-java) from 2.1.4 to 2.1.5. - [Release notes](https://github.com/trekawek/toxiproxy-java/releases) - [Changelog](https://github.com/trekawek/toxiproxy-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/trekawek/toxiproxy-java/compare/toxiproxy-java-2.1.4...toxiproxy-java-2.1.5) --- updated-dependencies: - dependency-name: eu.rekawek.toxiproxy:toxiproxy-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/toxiproxy/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/toxiproxy/build.gradle b/modules/toxiproxy/build.gradle index 3d7d110609b..cad4b6b71f3 100644 --- a/modules/toxiproxy/build.gradle +++ b/modules/toxiproxy/build.gradle @@ -2,7 +2,7 @@ description = "Testcontainers :: Toxiproxy" dependencies { api project(':testcontainers') - api 'eu.rekawek.toxiproxy:toxiproxy-java:2.1.4' + api 'eu.rekawek.toxiproxy:toxiproxy-java:2.1.5' testImplementation 'redis.clients:jedis:3.0.1' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From d69602467983709d8bdc5665c83f3a0502a189c3 Mon Sep 17 00:00:00 2001 From: Wim Deblauwe Date: Tue, 2 Nov 2021 16:13:00 +0100 Subject: [PATCH 2192/5615] Fix Documentation not displaying properly for 'Docker wormhole pattern' (#4658) Fixes #4656 --- .../continuous_integration/dind_patterns.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/supported_docker_environment/continuous_integration/dind_patterns.md b/docs/supported_docker_environment/continuous_integration/dind_patterns.md index e742c047afa..b6fc4e489ea 100644 --- a/docs/supported_docker_environment/continuous_integration/dind_patterns.md +++ b/docs/supported_docker_environment/continuous_integration/dind_patterns.md @@ -27,6 +27,7 @@ $ docker run -it --rm -v $PWD:$PWD -w $PWD -v /var/run/docker.sock:/var/run/dock ``` Where: + * `-v $PWD:$PWD` will add your current directory as a volume inside the container * `-w $PWD` will set the current directory to this volume * `-v /var/run/docker.sock:/var/run/docker.sock` will map the Docker socket From 12b08b9e7ba77fed5d0c5a5665588d1c27338d16 Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 8 Nov 2021 13:44:11 +0000 Subject: [PATCH 2193/5615] Fix formatting error --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index 65d417db2af..c8fb46f26d6 100644 --- a/docs/index.md +++ b/docs/index.md @@ -65,6 +65,7 @@ Using Maven you can add the following to `dependencyManagement` section in your ``` + and then use dependencies without specifying a version: ```xml tab='Maven' From 3ad90d78d0b3cffe8b225ea389c16e300d9949e4 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Tue, 9 Nov 2021 11:57:57 +0100 Subject: [PATCH 2194/5615] Add Gradle Enterprise (#4057) Co-authored-by: Richard North --- .github/workflows/ci-examples.yml | 1 + .github/workflows/ci.yml | 1 + settings.gradle | 12 +++++++++--- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index a1f81f49bef..286eb41606f 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -7,6 +7,7 @@ on: env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + GRADLE_ENTERPRISE_ACCESS_KEY: ge.testcontainers.org=${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} jobs: find_gradle_jobs: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7336e0767e1..91666644dfa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,7 @@ on: env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + GRADLE_ENTERPRISE_ACCESS_KEY: ge.testcontainers.org=${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} jobs: thundra_test_initializer: diff --git a/settings.gradle b/settings.gradle index 849a1ade06b..5a3966e3d25 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,12 +6,14 @@ buildscript { } dependencies { classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.10.0" - classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.2.1" + classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.6.1" + classpath "com.gradle:common-custom-user-data-gradle-plugin:1.2.1" } } apply plugin: 'ch.myniva.s3-build-cache' apply plugin: 'com.gradle.enterprise' +apply plugin: "com.gradle.common-custom-user-data-gradle-plugin" rootProject.name = 'testcontainers-java' @@ -56,7 +58,11 @@ buildCache { gradleEnterprise { buildScan { - termsOfServiceUrl = 'https://gradle.com/terms-of-service' - termsOfServiceAgree = 'yes' + server = "https://ge.testcontainers.org/" + publishAlways() + publishIfAuthenticated() + uploadInBackground = System.getenv("CI") == null + captureTaskInputFiles = true } + } From 484985b137d64533b5a564a2dd705d15293cd845 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Wed, 10 Nov 2021 10:15:45 +0100 Subject: [PATCH 2195/5615] Use key=value format for GRADLE_ENTERPRISE_ACCESS_KEY value (#4671) --- .github/workflows/ci-examples.yml | 2 +- .github/workflows/ci.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index 286eb41606f..e98c494a2c1 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -7,7 +7,7 @@ on: env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - GRADLE_ENTERPRISE_ACCESS_KEY: ge.testcontainers.org=${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} jobs: find_gradle_jobs: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 91666644dfa..93f8307178f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ on: env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - GRADLE_ENTERPRISE_ACCESS_KEY: ge.testcontainers.org=${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} jobs: thundra_test_initializer: @@ -74,4 +74,4 @@ jobs: $([[ -f $THUNDRA_GRADLE_INIT_SCRIPT_PATH ]] && echo "--init-script $THUNDRA_GRADLE_INIT_SCRIPT_PATH") env: THUNDRA_AGENT_TEST_RUN_ID: ${{ needs.thundra_test_initializer.outputs.thundra_agent_testrun_id }} - THUNDRA_AGENT_REPORT_REST_BASEURL: https://collector.thundra.us/v1 \ No newline at end of file + THUNDRA_AGENT_REPORT_REST_BASEURL: https://collector.thundra.us/v1 From 440179d3fe372ff076426b8d1ac9ad614f6eefce Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Wed, 10 Nov 2021 14:21:31 +0100 Subject: [PATCH 2196/5615] Update GE plugin in examples to 3.6.1 (#4672) This aligns the version between the examples and the project. --- examples/settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/settings.gradle b/examples/settings.gradle index ea988b4ada8..4a979449b07 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.10.0" - classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.2.1" + classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.6.1" } } From cd04914aa9bd4cdef921e77813d74987c2e1cb97 Mon Sep 17 00:00:00 2001 From: Per Lundberg Date: Tue, 16 Nov 2021 15:22:18 +0200 Subject: [PATCH 2197/5615] Fix typo in Timescale JDBC docs (#4678) --- docs/modules/databases/jdbc.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/databases/jdbc.md b/docs/modules/databases/jdbc.md index d402b5983b4..b2a1588ae22 100644 --- a/docs/modules/databases/jdbc.md +++ b/docs/modules/databases/jdbc.md @@ -41,7 +41,7 @@ Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database #### Using TimescaleDB -`jdbc:tc:timescaldb:2.1.0-pg13:///databasename` +`jdbc:tc:timescaledb:2.1.0-pg13:///databasename` #### Using Trino From ce7693e20bbc21a5e2baca58a8ee0de9223393d9 Mon Sep 17 00:00:00 2001 From: kmcgovern-apixio <66644089+kmcgovern-apixio@users.noreply.github.com> Date: Wed, 17 Nov 2021 03:16:33 -0800 Subject: [PATCH 2198/5615] Change external port check so it can timeout and retry (#4680) Co-authored-by: Sergei Egorov --- .../wait/internal/ExternalPortListeningCheck.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java b/core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java index b9b63eeee5c..d543d952bbd 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java +++ b/core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java @@ -4,6 +4,7 @@ import org.testcontainers.containers.ContainerState; import java.io.IOException; +import java.net.InetSocketAddress; import java.net.Socket; import java.util.Set; import java.util.concurrent.Callable; @@ -21,8 +22,9 @@ public Boolean call() { String address = containerState.getHost(); externalLivenessCheckPorts.parallelStream().forEach(externalPort -> { - try { - new Socket(address, externalPort).close(); + try (Socket socket = new Socket()) { + InetSocketAddress inetSocketAddress = new InetSocketAddress(address, externalPort); + socket.connect(inetSocketAddress, 1000); } catch (IOException e) { throw new IllegalStateException("Socket not listening yet: " + externalPort); } From ca244d855109962c72f96bee31804d5b2f39896a Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Thu, 25 Nov 2021 11:42:31 -0800 Subject: [PATCH 2199/5615] Add link to webdrivers module (#4699) --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index c8fb46f26d6..83e1a682540 100644 --- a/docs/index.md +++ b/docs/index.md @@ -10,7 +10,7 @@ Testcontainers make the following kinds of tests easier: * **Data access layer integration tests**: use a containerized instance of a MySQL, PostgreSQL or Oracle database to test your data access layer code for complete compatibility, but without requiring complex setup on developers' machines and safe in the knowledge that your tests will always start with a known DB state. Any other database type that can be containerized can also be used. * **Application integration tests**: for running your application in a short-lived test mode with dependencies, such as databases, message queues or web servers. -* **UI/Acceptance tests**: use containerized web browsers, compatible with Selenium, for conducting automated UI tests. Each test can get a fresh instance of the browser, with no browser state, plugin variations or automated browser upgrades to worry about. And you get a video recording of each test session, or just each session where tests failed. +* **UI/Acceptance tests**: use [containerized web browsers](https://www.testcontainers.org/modules/webdriver_containers/), compatible with Selenium, for conducting automated UI tests. Each test can get a fresh instance of the browser, with no browser state, plugin variations or automated browser upgrades to worry about. And you get a video recording of each test session, or just each session where tests failed. * **Much more!** Check out the various contributed modules or create your own custom container classes using [`GenericContainer`](features/creating_container.md) as a base. ## Prerequisites From c86be400c7ef2eda4e36c91b0432d0ba7c74d472 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 25 Nov 2021 14:46:54 -0500 Subject: [PATCH 2200/5615] Use a relative path to the Selenium module (#4700) Follow-up to #4699 --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 83e1a682540..4bfbc723a5a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -10,7 +10,7 @@ Testcontainers make the following kinds of tests easier: * **Data access layer integration tests**: use a containerized instance of a MySQL, PostgreSQL or Oracle database to test your data access layer code for complete compatibility, but without requiring complex setup on developers' machines and safe in the knowledge that your tests will always start with a known DB state. Any other database type that can be containerized can also be used. * **Application integration tests**: for running your application in a short-lived test mode with dependencies, such as databases, message queues or web servers. -* **UI/Acceptance tests**: use [containerized web browsers](https://www.testcontainers.org/modules/webdriver_containers/), compatible with Selenium, for conducting automated UI tests. Each test can get a fresh instance of the browser, with no browser state, plugin variations or automated browser upgrades to worry about. And you get a video recording of each test session, or just each session where tests failed. +* **UI/Acceptance tests**: use [containerized web browsers](modules/webdriver_containers.md), compatible with Selenium, for conducting automated UI tests. Each test can get a fresh instance of the browser, with no browser state, plugin variations or automated browser upgrades to worry about. And you get a video recording of each test session, or just each session where tests failed. * **Much more!** Check out the various contributed modules or create your own custom container classes using [`GenericContainer`](features/creating_container.md) as a base. ## Prerequisites From 368c182e9a133a9985483375c0e5718977faacb3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Nov 2021 09:42:09 +0000 Subject: [PATCH 2201/5615] Bump postgresql from 42.2.24 to 42.3.1 in /modules/postgresql Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.24 to 42.3.1. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.24...REL42.3.1) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 5c25e6243ef..57c7ed64b14 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -11,7 +11,7 @@ dependencies { testImplementation project(':jdbc-test') testImplementation project(':test-support') - testImplementation 'org.postgresql:postgresql:42.2.24' + testImplementation 'org.postgresql:postgresql:42.3.1' testImplementation testFixtures(project(':r2dbc')) testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.5.RELEASE' From eb1f0a2f17c19f7a9a98a4035a81d81388799111 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Nov 2021 09:42:10 +0000 Subject: [PATCH 2202/5615] Bump awaitility from 4.1.0 to 4.1.1 in /modules/couchbase Bumps [awaitility](https://github.com/awaitility/awaitility) from 4.1.0 to 4.1.1. - [Release notes](https://github.com/awaitility/awaitility/releases) - [Changelog](https://github.com/awaitility/awaitility/blob/master/changelog.txt) - [Commits](https://github.com/awaitility/awaitility/compare/awaitility-4.1.0...awaitility-4.1.1) --- updated-dependencies: - dependency-name: org.awaitility:awaitility dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 02917b46045..8539d19d2d6 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -4,5 +4,5 @@ dependencies { api project(':testcontainers') testImplementation 'com.couchbase.client:java-client:3.2.2' - testImplementation 'org.awaitility:awaitility:4.1.0' + testImplementation 'org.awaitility:awaitility:4.1.1' } From 15db1ce408e442e6a7bf25ff638abcf3deac8f44 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Nov 2021 09:42:18 +0000 Subject: [PATCH 2203/5615] Bump mockito-core from 3.12.4 to 4.1.0 in /modules/junit-jupiter Bumps [mockito-core](https://github.com/mockito/mockito) from 3.12.4 to 4.1.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.12.4...v4.1.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 7f127be30c1..cddedb59408 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,7 +9,7 @@ dependencies { testImplementation 'com.zaxxer:HikariCP:4.0.3' testImplementation 'redis.clients:jedis:3.7.0' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' - testImplementation ('org.mockito:mockito-core:3.12.4') { + testImplementation ('org.mockito:mockito-core:4.1.0') { exclude(module: 'hamcrest-core') } testImplementation 'org.assertj:assertj-core:3.21.0' From 01d59303b78fa190927c2d8918240798c124882a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Nov 2021 09:42:18 +0000 Subject: [PATCH 2204/5615] Bump postgresql from 42.2.24 to 42.3.1 in /modules/spock Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.24 to 42.3.1. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.24...REL42.3.1) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 478f3bdd31c..0f2843ea007 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'com.zaxxer:HikariCP:4.0.3' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' - testRuntimeOnly 'org.postgresql:postgresql:42.2.24' + testRuntimeOnly 'org.postgresql:postgresql:42.3.1' testRuntimeOnly 'mysql:mysql-connector-java:8.0.27' testCompileClasspath 'org.jetbrains:annotations:22.0.0' From ef9fa8a9ba511cd726dab1284c017fe832469af3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Nov 2021 09:42:20 +0000 Subject: [PATCH 2205/5615] Bump mysql-connector-java in /modules/junit-jupiter Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.26 to 8.0.27. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.26...8.0.27) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 7f127be30c1..f215680c524 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -16,7 +16,7 @@ dependencies { testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.1' testRuntimeOnly 'org.postgresql:postgresql:42.3.1' - testRuntimeOnly 'mysql:mysql-connector-java:8.0.26' + testRuntimeOnly 'mysql:mysql-connector-java:8.0.27' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' } From 6c86a5ef5070c7ad76eafa61e0923a7e18502856 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Nov 2021 09:42:23 +0000 Subject: [PATCH 2206/5615] Bump elasticsearch-rest-client in /modules/elasticsearch Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 7.15.0 to 7.15.2. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.15.0...v7.15.2) --- updated-dependencies: - dependency-name: org.elasticsearch.client:elasticsearch-rest-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 1c3e66edf0d..da2508085c0 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,7 +2,7 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') - testImplementation "org.elasticsearch.client:elasticsearch-rest-client:7.15.0" + testImplementation "org.elasticsearch.client:elasticsearch-rest-client:7.15.2" testImplementation "org.elasticsearch.client:transport:7.15.1" testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 8bcc7872fa8372069d43e8e6692bf53510a8958b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Nov 2021 09:42:25 +0000 Subject: [PATCH 2207/5615] Bump cucumber-java from 6.11.0 to 7.1.0 in /examples Bumps [cucumber-java](https://github.com/cucumber/cucumber-jvm) from 6.11.0 to 7.1.0. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v6.11.0...v7.1.0) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-java dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index ae66a351335..49b18404862 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' - testImplementation 'io.cucumber:cucumber-java:6.11.0' + testImplementation 'io.cucumber:cucumber-java:7.1.0' testImplementation 'io.cucumber:cucumber-junit:7.0.0' testImplementation 'org.testcontainers:selenium' } From 1982b714c5ce44e7de61437be546dc8b1b45810a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Nov 2021 09:42:26 +0000 Subject: [PATCH 2208/5615] Bump awaitility from 4.1.0 to 4.1.1 in /core Bumps [awaitility](https://github.com/awaitility/awaitility) from 4.1.0 to 4.1.1. - [Release notes](https://github.com/awaitility/awaitility/releases) - [Changelog](https://github.com/awaitility/awaitility/blob/master/changelog.txt) - [Commits](https://github.com/awaitility/awaitility/compare/awaitility-4.1.0...awaitility-4.1.1) --- updated-dependencies: - dependency-name: org.awaitility:awaitility dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 9ef9fab67e6..8a63834d03c 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -66,7 +66,7 @@ dependencies { exclude(group: 'org.jetbrains', module: 'annotations') } - shaded 'org.awaitility:awaitility:4.1.0' + shaded 'org.awaitility:awaitility:4.1.1' api platform('com.github.docker-java:docker-java-bom:3.2.12') shaded platform('com.github.docker-java:docker-java-bom:3.2.12') From d1c958858065b3038cde4343572003f8ba24ef58 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Nov 2021 09:42:29 +0000 Subject: [PATCH 2209/5615] Bump lombok from 1.18.20 to 1.18.22 in /examples Bumps [lombok](https://github.com/projectlombok/lombok) from 1.18.20 to 1.18.22. - [Release notes](https://github.com/projectlombok/lombok/releases) - [Changelog](https://github.com/projectlombok/lombok/blob/master/doc/changelog.markdown) - [Commits](https://github.com/projectlombok/lombok/compare/v1.18.20...v1.18.22) --- updated-dependencies: - dependency-name: org.projectlombok:lombok dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/disque-job-queue/build.gradle | 2 +- examples/kafka-cluster/build.gradle | 4 ++-- examples/solr-container/build.gradle | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index 5d2876fd3b1..c8362c42447 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly "org.projectlombok:lombok:1.18.20" + compileOnly "org.projectlombok:lombok:1.18.22" annotationProcessor "org.projectlombok:lombok:1.18.20" implementation 'biz.paluch.redis:spinach:0.3' implementation 'com.google.code.gson:gson:2.8.9' diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index 3679475d1bd..cef94112f25 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -7,8 +7,8 @@ repositories { } dependencies { - testCompileOnly "org.projectlombok:lombok:1.18.20" - testAnnotationProcessor "org.projectlombok:lombok:1.18.10" + testCompileOnly "org.projectlombok:lombok:1.18.22" + testAnnotationProcessor "org.projectlombok:lombok:1.18.22" testImplementation 'org.testcontainers:kafka' testImplementation 'org.apache.kafka:kafka-clients:3.0.0' testImplementation 'org.assertj:assertj-core:3.21.0' diff --git a/examples/solr-container/build.gradle b/examples/solr-container/build.gradle index 5f76ada58cf..965a40b35a8 100644 --- a/examples/solr-container/build.gradle +++ b/examples/solr-container/build.gradle @@ -7,8 +7,8 @@ repositories { } dependencies { - compileOnly "org.projectlombok:lombok:1.18.20" - annotationProcessor "org.projectlombok:lombok:1.18.16" + compileOnly "org.projectlombok:lombok:1.18.22" + annotationProcessor "org.projectlombok:lombok:1.18.22" implementation 'org.apache.solr:solr-solrj:8.10.1' From fc974928ccd7f6ad4e07e557b86f1c32315e3421 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Nov 2021 09:42:29 +0000 Subject: [PATCH 2210/5615] Bump postgresql from 42.2.24 to 42.3.1 in /examples Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.24 to 42.3.1. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.24...REL42.3.1) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index da10b960013..b5670402803 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' implementation 'com.squareup.okhttp3:okhttp:4.9.2' implementation 'org.json:json:20210307' - testImplementation 'org.postgresql:postgresql:42.2.24' + testImplementation 'org.postgresql:postgresql:42.3.1' testImplementation 'ch.qos.logback:logback-classic:1.2.6' testImplementation 'org.testcontainers:postgresql' } From 8261f3712b91c833657cb7ce5c08130bf87c7a48 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Nov 2021 09:42:35 +0000 Subject: [PATCH 2211/5615] Bump aws-java-sdk-logs from 1.12.60 to 1.12.119 in /modules/localstack Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.12.60 to 1.12.119. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.60...1.12.119) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-logs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 1d2297951e8..7d27bf40721 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,7 +6,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.100' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.80' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.100' - testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.60' + testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.119' testImplementation 'software.amazon.awssdk:s3:2.17.72' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From d1493484d657606a5250a5886d988686d489c0a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:00:35 +0000 Subject: [PATCH 2212/5615] Bump transport from 7.15.1 to 7.15.2 in /modules/elasticsearch Bumps [transport](https://github.com/elastic/elasticsearch) from 7.15.1 to 7.15.2. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.15.1...v7.15.2) --- updated-dependencies: - dependency-name: org.elasticsearch.client:transport dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index da2508085c0..ad1446a0f0f 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,6 +3,6 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') testImplementation "org.elasticsearch.client:elasticsearch-rest-client:7.15.2" - testImplementation "org.elasticsearch.client:transport:7.15.1" + testImplementation "org.elasticsearch.client:transport:7.15.2" testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From c1bd5954e829ffb90bf9659de32dcfa470a65443 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:00:35 +0000 Subject: [PATCH 2213/5615] Bump annotations from 22.0.0 to 23.0.0 in /modules/spock Bumps [annotations](https://github.com/JetBrains/java-annotations) from 22.0.0 to 23.0.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/22.0.0...23.0.0) --- updated-dependencies: - dependency-name: org.jetbrains:annotations dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 0f2843ea007..cec493cf77d 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -18,7 +18,7 @@ dependencies { testRuntimeOnly 'org.postgresql:postgresql:42.3.1' testRuntimeOnly 'mysql:mysql-connector-java:8.0.27' - testCompileClasspath 'org.jetbrains:annotations:22.0.0' + testCompileClasspath 'org.jetbrains:annotations:23.0.0' } sourceJar { From 2f07fdcca7716d7766ab69a28fb67575a7c50a39 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:00:35 +0000 Subject: [PATCH 2214/5615] Bump auto-service from 1.0 to 1.0.1 in /modules/mariadb Bumps [auto-service](https://github.com/google/auto) from 1.0 to 1.0.1. - [Release notes](https://github.com/google/auto/releases) - [Commits](https://github.com/google/auto/compare/auto-value-1.0...auto-common-1.0.1) --- updated-dependencies: - dependency-name: com.google.auto.service:auto-service dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index e24fe293ff1..af67719f8ad 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -1,7 +1,7 @@ description = "Testcontainers :: JDBC :: MariaDB" dependencies { - annotationProcessor 'com.google.auto.service:auto-service:1.0' + annotationProcessor 'com.google.auto.service:auto-service:1.0.1' compileOnly 'com.google.auto.service:auto-service:1.0' api project(':jdbc') From 68c27642b4c91a6959899977892bc54d03a04425 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:00:35 +0000 Subject: [PATCH 2215/5615] Bump tomcat-jdbc from 10.0.12 to 10.0.13 in /modules/jdbc-test Bumps tomcat-jdbc from 10.0.12 to 10.0.13. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 3cfed4d80c0..7dd1023b95a 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -14,7 +14,7 @@ dependencies { exclude(group: "net.java.dev.jna", module: "jna") } - api 'org.apache.tomcat:tomcat-jdbc:10.0.12' + api 'org.apache.tomcat:tomcat-jdbc:10.0.13' api 'org.vibur:vibur-dbcp:25.0' api 'mysql:mysql-connector-java:8.0.27' } From b975b886795e17d7b2905de9af9757ce560f0600 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:00:35 +0000 Subject: [PATCH 2216/5615] Bump annotations from 22.0.0 to 23.0.0 in /modules/postgresql Bumps [annotations](https://github.com/JetBrains/java-annotations) from 22.0.0 to 23.0.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/22.0.0...23.0.0) --- updated-dependencies: - dependency-name: org.jetbrains:annotations dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 57c7ed64b14..530b0c26eae 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -16,5 +16,5 @@ dependencies { testImplementation testFixtures(project(':r2dbc')) testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.5.RELEASE' - compileOnly 'org.jetbrains:annotations:22.0.0' + compileOnly 'org.jetbrains:annotations:23.0.0' } From d85235ae27884c26dddc9210638d7f9921edd65a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:00:35 +0000 Subject: [PATCH 2217/5615] Bump junit-jupiter-api from 5.8.1 to 5.8.2 in /modules/junit-jupiter Bumps [junit-jupiter-api](https://github.com/junit-team/junit5) from 5.8.1 to 5.8.2. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.8.1...r5.8.2) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 294bcfb48f0..1100219ed96 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -2,7 +2,7 @@ description = "Testcontainers :: JUnit Jupiter Extension" dependencies { api project(':testcontainers') - api 'org.junit.jupiter:junit-jupiter-api:5.8.1' + api 'org.junit.jupiter:junit-jupiter-api:5.8.2' testImplementation project(':mysql') testImplementation project(':postgresql') From f309c241761863e56355a9a67a02380a649c7e98 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:00:35 +0000 Subject: [PATCH 2218/5615] Bump annotations from 22.0.0 to 23.0.0 in /modules/jdbc Bumps [annotations](https://github.com/JetBrains/java-annotations) from 22.0.0 to 23.0.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/22.0.0...23.0.0) --- updated-dependencies: - dependency-name: org.jetbrains:annotations dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index e24690963f2..f5056daedf4 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':database-commons') testImplementation project(':test-support') - compileOnly 'org.jetbrains:annotations:22.0.0' + compileOnly 'org.jetbrains:annotations:23.0.0' testImplementation 'commons-dbutils:commons-dbutils:1.7' testImplementation 'org.vibur:vibur-dbcp:25.0' testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.12' From 5532ac65d38f4be3b1a041405915718388a7de03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:00:35 +0000 Subject: [PATCH 2219/5615] Bump annotations from 22.0.0 to 23.0.0 in /modules/oracle-xe Bumps [annotations](https://github.com/JetBrains/java-annotations) from 22.0.0 to 23.0.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/22.0.0...23.0.0) --- updated-dependencies: - dependency-name: org.jetbrains:annotations dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/oracle-xe/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/oracle-xe/build.gradle b/modules/oracle-xe/build.gradle index 5c18b63bb9d..107f8e66bdf 100644 --- a/modules/oracle-xe/build.gradle +++ b/modules/oracle-xe/build.gradle @@ -6,5 +6,5 @@ dependencies { testImplementation project(':jdbc-test') testImplementation 'com.oracle.ojdbc:ojdbc8:19.3.0.0' - compileOnly 'org.jetbrains:annotations:22.0.0' + compileOnly 'org.jetbrains:annotations:23.0.0' } From bb608169669b52acf05eee7f42ab96a3e144caf8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:00:35 +0000 Subject: [PATCH 2220/5615] Bump java-client from 3.2.2 to 3.2.3 in /modules/couchbase Bumps [java-client](https://github.com/couchbase/couchbase-jvm-clients) from 3.2.2 to 3.2.3. - [Release notes](https://github.com/couchbase/couchbase-jvm-clients/releases) - [Commits](https://github.com/couchbase/couchbase-jvm-clients/compare/java-client-3.2.2...java-client-3.2.3) --- updated-dependencies: - dependency-name: com.couchbase.client:java-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 8539d19d2d6..772876b7bf1 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Couchbase" dependencies { api project(':testcontainers') - testImplementation 'com.couchbase.client:java-client:3.2.2' + testImplementation 'com.couchbase.client:java-client:3.2.3' testImplementation 'org.awaitility:awaitility:4.1.1' } From caf16e136239d7cd6c0884056b796be29552fb36 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:00:35 +0000 Subject: [PATCH 2221/5615] Bump annotations from 22.0.0 to 23.0.0 in /modules/selenium Bumps [annotations](https://github.com/JetBrains/java-annotations) from 22.0.0 to 23.0.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/22.0.0...23.0.0) --- updated-dependencies: - dependency-name: org.jetbrains:annotations dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/selenium/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/selenium/build.gradle b/modules/selenium/build.gradle index 5cff4c544b3..5f0824f8b12 100644 --- a/modules/selenium/build.gradle +++ b/modules/selenium/build.gradle @@ -12,6 +12,6 @@ dependencies { testImplementation project(':nginx') testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' - compileOnly 'org.jetbrains:annotations:22.0.0' - testCompileClasspath 'org.jetbrains:annotations:21.0.1' + compileOnly 'org.jetbrains:annotations:23.0.0' + testCompileClasspath 'org.jetbrains:annotations:23.0.0' } From 1d14ec53359dd3dfc89ee9aa5662e786eb86c3f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:00:36 +0000 Subject: [PATCH 2222/5615] Bump auto-service from 1.0 to 1.0.1 in /modules/mssqlserver Bumps [auto-service](https://github.com/google/auto) from 1.0 to 1.0.1. - [Release notes](https://github.com/google/auto/releases) - [Commits](https://github.com/google/auto/compare/auto-value-1.0...auto-common-1.0.1) --- updated-dependencies: - dependency-name: com.google.auto.service:auto-service dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mssqlserver/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index 51e5bba3315..de691b47277 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -1,7 +1,7 @@ description = "Testcontainers :: MS SQL Server" dependencies { - annotationProcessor 'com.google.auto.service:auto-service:1.0' + annotationProcessor 'com.google.auto.service:auto-service:1.0.1' compileOnly 'com.google.auto.service:auto-service:1.0' api project(':jdbc') From 15452779f0865e8a010141d12ff81598816f720c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:00:36 +0000 Subject: [PATCH 2223/5615] Bump mockito-core from 4.0.0 to 4.1.0 in /core Bumps [mockito-core](https://github.com/mockito/mockito) from 4.0.0 to 4.1.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.0.0...v4.1.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 8a63834d03c..0d4c9d565ce 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -93,7 +93,7 @@ dependencies { testImplementation 'com.rabbitmq:amqp-client:5.13.1' testImplementation 'org.mongodb:mongo-java-driver:3.12.10' - testImplementation ('org.mockito:mockito-core:4.0.0') { + testImplementation ('org.mockito:mockito-core:4.1.0') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From 395129e29ff7cdbdb0b1e2bb9d8ca0d6688c373f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:00:37 +0000 Subject: [PATCH 2224/5615] Bump aws-java-sdk-dynamodb in /modules/dynalite Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.12.100 to 1.12.122. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.100...1.12.122) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-dynamodb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/dynalite/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 7dbadb27133..fb4edcf1576 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Dynalite" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.100' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.122' testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.100' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From fda92ef5cb1517c1e8183de34d04635450e4cb87 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:00:37 +0000 Subject: [PATCH 2225/5615] Bump annotations from 22.0.0 to 23.0.0 in /modules/nginx Bumps [annotations](https://github.com/JetBrains/java-annotations) from 22.0.0 to 23.0.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/22.0.0...23.0.0) --- updated-dependencies: - dependency-name: org.jetbrains:annotations dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/nginx/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/nginx/build.gradle b/modules/nginx/build.gradle index b8f45d394c1..288b45b027a 100644 --- a/modules/nginx/build.gradle +++ b/modules/nginx/build.gradle @@ -2,6 +2,6 @@ description = "Testcontainers :: Nginx" dependencies { api project(':testcontainers') - compileOnly 'org.jetbrains:annotations:22.0.0' + compileOnly 'org.jetbrains:annotations:23.0.0' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From a88fc5a2d4b5a6a0bcfb7e4daa6cd7b335d14a3f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:00:38 +0000 Subject: [PATCH 2226/5615] Bump junit-jupiter-params from 5.8.1 to 5.8.2 in /modules/junit-jupiter Bumps [junit-jupiter-params](https://github.com/junit-team/junit5) from 5.8.1 to 5.8.2. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.8.1...r5.8.2) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-params dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 294bcfb48f0..06f4ea7a846 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -13,7 +13,7 @@ dependencies { exclude(module: 'hamcrest-core') } testImplementation 'org.assertj:assertj-core:3.21.0' - testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.1' + testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2' testRuntimeOnly 'org.postgresql:postgresql:42.3.1' testRuntimeOnly 'mysql:mysql-connector-java:8.0.27' From 9055bc67eef4205d8c7586ae23a868e10502e6c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:00:38 +0000 Subject: [PATCH 2227/5615] Bump auto-service from 1.0 to 1.0.1 in /modules/postgresql Bumps [auto-service](https://github.com/google/auto) from 1.0 to 1.0.1. - [Release notes](https://github.com/google/auto/releases) - [Commits](https://github.com/google/auto/compare/auto-value-1.0...auto-common-1.0.1) --- updated-dependencies: - dependency-name: com.google.auto.service:auto-service dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 57c7ed64b14..bf18e22d8f6 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -1,7 +1,7 @@ description = "Testcontainers :: JDBC :: PostgreSQL" dependencies { - annotationProcessor 'com.google.auto.service:auto-service:1.0' + annotationProcessor 'com.google.auto.service:auto-service:1.0.1' compileOnly 'com.google.auto.service:auto-service:1.0' api project(':jdbc') From ba506700077bea6aad891f519e584bd0d8ac93b9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:00:41 +0000 Subject: [PATCH 2228/5615] Bump amqp-client from 5.13.1 to 5.14.0 in /core Bumps [amqp-client](https://github.com/rabbitmq/rabbitmq-java-client) from 5.13.1 to 5.14.0. - [Release notes](https://github.com/rabbitmq/rabbitmq-java-client/releases) - [Commits](https://github.com/rabbitmq/rabbitmq-java-client/compare/v5.13.1...v5.14.0) --- updated-dependencies: - dependency-name: com.rabbitmq:amqp-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 8a63834d03c..d238b908762 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -90,7 +90,7 @@ dependencies { testImplementation 'org.apache.httpcomponents:httpclient:4.5.9' testImplementation 'redis.clients:jedis:3.7.0' - testImplementation 'com.rabbitmq:amqp-client:5.13.1' + testImplementation 'com.rabbitmq:amqp-client:5.14.0' testImplementation 'org.mongodb:mongo-java-driver:3.12.10' testImplementation ('org.mockito:mockito-core:4.0.0') { From 23130b842cd687fe495282ed37a94e9dec414916 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:00:41 +0000 Subject: [PATCH 2229/5615] Bump junit-jupiter-engine from 5.8.1 to 5.8.2 in /modules/junit-jupiter Bumps [junit-jupiter-engine](https://github.com/junit-team/junit5) from 5.8.1 to 5.8.2. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.8.1...r5.8.2) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-engine dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 294bcfb48f0..b6c193e98bb 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -17,7 +17,7 @@ dependencies { testRuntimeOnly 'org.postgresql:postgresql:42.3.1' testRuntimeOnly 'mysql:mysql-connector-java:8.0.27' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2' } test { From 7d1bef98c819ec80a8bd58278f0a1d8d5a9165fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:00:42 +0000 Subject: [PATCH 2230/5615] Bump aws-java-sdk-s3 from 1.12.100 to 1.12.122 in /modules/localstack Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.100 to 1.12.122. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.100...1.12.122) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 7d27bf40721..39c5c342891 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Localstack" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.100' - testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.80' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.122' + testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.122' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.100' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.119' testImplementation 'software.amazon.awssdk:s3:2.17.72' From da8d136a388918fb7c529e999b55a0f6b10a525c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:00:43 +0000 Subject: [PATCH 2231/5615] Bump annotations from 22.0.0 to 23.0.0 in /core Bumps [annotations](https://github.com/JetBrains/java-annotations) from 22.0.0 to 23.0.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/22.0.0...23.0.0) --- updated-dependencies: - dependency-name: org.jetbrains:annotations dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- core/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index 8a63834d03c..f94c4115c5e 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -59,8 +59,8 @@ dependencies { api 'junit:junit:4.13.2' api 'org.slf4j:slf4j-api:1.7.32' - compileOnly 'org.jetbrains:annotations:22.0.0' - testCompileClasspath 'org.jetbrains:annotations:21.0.1' + compileOnly 'org.jetbrains:annotations:23.0.0' + testCompileClasspath 'org.jetbrains:annotations:23.0.0' api 'org.apache.commons:commons-compress:1.21' api ('org.rnorth.duct-tape:duct-tape:1.0.8') { exclude(group: 'org.jetbrains', module: 'annotations') From ae0dee7902629a578172971ff9b51c341ac34597 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:00:43 +0000 Subject: [PATCH 2232/5615] Bump cucumber-junit from 7.0.0 to 7.1.0 in /examples Bumps [cucumber-junit](https://github.com/cucumber/cucumber-jvm) from 7.0.0 to 7.1.0. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.0.0...v7.1.0) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-junit dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 49b18404862..edb62670134 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' testImplementation 'io.cucumber:cucumber-java:7.1.0' - testImplementation 'io.cucumber:cucumber-junit:7.0.0' + testImplementation 'io.cucumber:cucumber-junit:7.1.0' testImplementation 'org.testcontainers:selenium' } From 99cba6b46ef2a3ca5a4d47c764c6fb0cba13b381 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:00:45 +0000 Subject: [PATCH 2233/5615] Bump okhttp from 4.9.2 to 4.9.3 in /examples Bumps [okhttp](https://github.com/square/okhttp) from 4.9.2 to 4.9.3. - [Release notes](https://github.com/square/okhttp/releases) - [Changelog](https://github.com/square/okhttp/blob/master/CHANGELOG.md) - [Commits](https://github.com/square/okhttp/compare/parent-4.9.2...parent-4.9.3) --- updated-dependencies: - dependency-name: com.squareup.okhttp3:okhttp dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index b5670402803..f916f6abf81 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' - implementation 'com.squareup.okhttp3:okhttp:4.9.2' + implementation 'com.squareup.okhttp3:okhttp:4.9.3' implementation 'org.json:json:20210307' testImplementation 'org.postgresql:postgresql:42.3.1' testImplementation 'ch.qos.logback:logback-classic:1.2.6' From c669300c3654743d07a9cf4cb88519c6b6f5c82e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:00:46 +0000 Subject: [PATCH 2234/5615] Bump aws-java-sdk-logs from 1.12.119 to 1.12.122 in /modules/localstack Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.12.119 to 1.12.122. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.119...1.12.122) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-logs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 7d27bf40721..11a01bb66f6 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,7 +6,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.100' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.80' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.100' - testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.119' + testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.122' testImplementation 'software.amazon.awssdk:s3:2.17.72' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 721a7334a265cf4aff93aad7595782ff2954bae5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:00:53 +0000 Subject: [PATCH 2235/5615] Bump annotations from 22.0.0 to 23.0.0 in /examples Bumps [annotations](https://github.com/JetBrains/java-annotations) from 22.0.0 to 23.0.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/22.0.0...23.0.0) --- updated-dependencies: - dependency-name: org.jetbrains:annotations dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- examples/mongodb-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/mongodb-container/build.gradle b/examples/mongodb-container/build.gradle index 86b3e978019..23f193115ba 100644 --- a/examples/mongodb-container/build.gradle +++ b/examples/mongodb-container/build.gradle @@ -9,5 +9,5 @@ repositories { dependencies { testImplementation 'org.testcontainers:testcontainers' testImplementation 'junit:junit:4.13.2' - testCompileClasspath 'org.jetbrains:annotations:22.0.0' + testCompileClasspath 'org.jetbrains:annotations:23.0.0' } From aff143a43a107dc668509718f03ea32e19dd5a1d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:00:54 +0000 Subject: [PATCH 2236/5615] Bump logback-classic from 1.2.6 to 1.2.7 in /examples Bumps logback-classic from 1.2.6 to 1.2.7. --- updated-dependencies: - dependency-name: ch.qos.logback:logback-classic dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/disque-job-queue/build.gradle | 2 +- examples/kafka-cluster/build.gradle | 2 +- examples/linked-container/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index c8362c42447..051e82f67d7 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -12,7 +12,7 @@ dependencies { implementation 'biz.paluch.redis:spinach:0.3' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' - testImplementation 'ch.qos.logback:logback-classic:1.2.6' + testImplementation 'ch.qos.logback:logback-classic:1.2.7' testImplementation 'org.mockito:mockito-all:1.10.19' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index cef94112f25..290a4295368 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -13,5 +13,5 @@ dependencies { testImplementation 'org.apache.kafka:kafka-clients:3.0.0' testImplementation 'org.assertj:assertj-core:3.21.0' testImplementation 'com.google.guava:guava:23.0' - testImplementation 'ch.qos.logback:logback-classic:1.2.6' + testImplementation 'ch.qos.logback:logback-classic:1.2.7' } diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index b5670402803..c404e2e7363 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'com.squareup.okhttp3:okhttp:4.9.2' implementation 'org.json:json:20210307' testImplementation 'org.postgresql:postgresql:42.3.1' - testImplementation 'ch.qos.logback:logback-classic:1.2.6' + testImplementation 'ch.qos.logback:logback-classic:1.2.7' testImplementation 'org.testcontainers:postgresql' } diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 0771fbeec7f..8f30256e2da 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -12,7 +12,7 @@ dependencies { implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' - testImplementation 'ch.qos.logback:logback-classic:1.2.6' + testImplementation 'ch.qos.logback:logback-classic:1.2.7' testImplementation 'org.testng:testng:7.4.0' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 3651bbe6927..b6ac1f4ddd6 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -13,6 +13,6 @@ dependencies { implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'junit:junit:4.13.2' - testImplementation 'ch.qos.logback:logback-classic:1.2.6' + testImplementation 'ch.qos.logback:logback-classic:1.2.7' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 44ce783694b..a8b10171d99 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -13,7 +13,7 @@ dependencies { implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.32' - testImplementation 'ch.qos.logback:logback-classic:1.2.6' + testImplementation 'ch.qos.logback:logback-classic:1.2.7' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 0d6f00cf7173547de86fd8bd64c7024ae9d849c5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:01:14 +0000 Subject: [PATCH 2237/5615] Bump peter-evans/create-pull-request Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 3c3d696d5b8aa2eb0e70f76c251e4b149122acf1 to 3.11.0. This release includes the previously tagged commit. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/3c3d696d5b8aa2eb0e70f76c251e4b149122acf1...67df31e08a133c6a77008b89689677067fef169e) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .github/workflows/update-docs-version.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index 3311847da5d..790dad57188 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -17,7 +17,7 @@ jobs: sed -i "s/latest_version: .*/latest_version: ${GITHUB_REF##*/}/g" mkdocs.yml git diff - name: Create Pull Request - uses: peter-evans/create-pull-request@3c3d696d5b8aa2eb0e70f76c251e4b149122acf1 # v3.10.1 + uses: peter-evans/create-pull-request@67df31e08a133c6a77008b89689677067fef169e # v3.10.1 with: title: Update docs version to ${GITHUB_REF##*/} body: | From fedf1d379e2c47a283c387c9c9907f3d2cf14360 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:01:16 +0000 Subject: [PATCH 2238/5615] Bump actions/setup-java from 2.3.1 to 2.4.0 Bumps [actions/setup-java](https://github.com/actions/setup-java) from 2.3.1 to 2.4.0. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v2.3.1...v2.4.0) --- updated-dependencies: - dependency-name: actions/setup-java dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- .github/workflows/release.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index e98c494a2c1..c850d1acaaf 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -16,7 +16,7 @@ jobs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.3.1 + - uses: actions/setup-java@v2.4.0 with: java-version: '8.0.302' distribution: temurin @@ -44,7 +44,7 @@ jobs: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.3.1 + - uses: actions/setup-java@v2.4.0 with: java-version: '8.0.302' distribution: temurin diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 93f8307178f..a6837c31f43 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.3.1 + - uses: actions/setup-java@v2.4.0 with: java-version: '8.0.302' distribution: temurin @@ -50,7 +50,7 @@ jobs: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.3.1 + - uses: actions/setup-java@v2.4.0 with: java-version: '8.0.302' distribution: temurin diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3779af890ae..7b727567ecb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.3.1 + - uses: actions/setup-java@v2.4.0 with: java-version: '8.0.302' distribution: temurin From b85859cb6f51b164d584b3cbe71f3e2c86518e36 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:01:20 +0000 Subject: [PATCH 2239/5615] Bump actions/cache from 2.1.6 to 2.1.7 Bumps [actions/cache](https://github.com/actions/cache) from 2.1.6 to 2.1.7. - [Release notes](https://github.com/actions/cache/releases) - [Commits](https://github.com/actions/cache/compare/v2.1.6...v2.1.7) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index e98c494a2c1..e3cda845dde 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -21,7 +21,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v2.1.6 + uses: actions/cache@v2.1.7 continue-on-error: true with: path: ~/.gradle/caches @@ -49,7 +49,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v2.1.6 + uses: actions/cache@v2.1.7 continue-on-error: true with: path: ~/.gradle/caches diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 93f8307178f..d7ffc854dcb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v2.1.6 + uses: actions/cache@v2.1.7 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-home-testmatrix-${{ hashFiles('**/*.gradle') }} @@ -55,7 +55,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v2.1.6 + uses: actions/cache@v2.1.7 continue-on-error: true with: path: ~/.gradle/caches From ab4e058aafc1f9b625feec85a07ef9261c1de1ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 22:02:41 +0000 Subject: [PATCH 2240/5615] Bump org.jetbrains.kotlin.jvm from 1.5.31 to 1.6.0 in /examples Bumps org.jetbrains.kotlin.jvm from 1.5.31 to 1.6.0. --- updated-dependencies: - dependency-name: org.jetbrains.kotlin.jvm dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index c512e594a65..4c8d6d551fb 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,6 +1,6 @@ plugins { id("org.springframework.boot") version "2.5.6" - id("org.jetbrains.kotlin.jvm") version "1.5.31" + id("org.jetbrains.kotlin.jvm") version "1.6.0" id("org.jetbrains.kotlin.plugin.spring") version "1.5.31" } From 4aef1dc6d79aea30db9b30a3f776df31d8f39d4f Mon Sep 17 00:00:00 2001 From: mruemeli <88658860+mruemeli@users.noreply.github.com> Date: Fri, 3 Dec 2021 16:36:27 +0100 Subject: [PATCH 2241/5615] Execute Docker credential programm through shell on Windows (#4763) --- .../java/org/testcontainers/utility/RegistryAuthLocator.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java index 846b62f1f25..b373eedaa03 100644 --- a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java +++ b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java @@ -5,6 +5,7 @@ import com.github.dockerjava.api.model.AuthConfig; import com.google.common.annotations.VisibleForTesting; import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.SystemUtils; import org.slf4j.Logger; import org.zeroturnaround.exec.InvalidResultException; import org.zeroturnaround.exec.ProcessExecutor; @@ -337,8 +338,9 @@ private String extractCredentialProviderErrorMessage(InvalidResultException inva private String runCredentialProgram(String hostName, String credentialHelperName) throws InvalidResultException, InterruptedException, TimeoutException, IOException { + String[] command = SystemUtils.IS_OS_WINDOWS ? new String[] {"cmd", "/c", credentialHelperName, "get"} : new String[]{credentialHelperName, "get"}; return new ProcessExecutor() - .command(credentialHelperName, "get") + .command(command) .redirectInput(new ByteArrayInputStream(hostName.getBytes())) .readOutput(true) .exitValueNormal() From 4adabdc5c281c05a202ed2139eadf51006c37e49 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 3 Dec 2021 16:05:25 +0000 Subject: [PATCH 2242/5615] Update Gradle Wrapper to 7.3.1 (#4761) Co-authored-by: gradle-update-robot --- examples/gradle/wrapper/gradle-wrapper.properties | 4 ++-- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/gradle/wrapper/gradle-wrapper.properties b/examples/gradle/wrapper/gradle-wrapper.properties index a2e01c0dffe..93a3251ccfc 100644 --- a/examples/gradle/wrapper/gradle-wrapper.properties +++ b/examples/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=f581709a9c35e9cb92e16f585d2c4bc99b2b1a5f85d2badbd3dc6bff59e1e6dd -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionSha256Sum=9afb3ca688fc12c761a0e9e4321e4d24e977a4a8916c8a768b1fe05ddb4d6b66 +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a37233c5e2e..cf2b23094e4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=a8da5b02437a60819cad23e10fc7e9cf32bcb57029d9cb277e26eeff76ce014b -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip +distributionSha256Sum=b75392c5625a88bccd58a574552a5a323edca82dab5942d2d41097f809c6bcce +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From b4fcec8189af2b6c203725d47c2277076fe81867 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Thu, 9 Dec 2021 14:31:36 +0300 Subject: [PATCH 2243/5615] Adapt Gradle config according to Gradle Enterprise Trial experiments (#4705) We are now utilizing the Gradle Enterprise remote cache. --- .github/workflows/ci-examples.yml | 1 + .github/workflows/ci.yml | 1 + .gitignore | 3 +++ build.gradle | 7 ++++++- examples/settings.gradle | 2 +- gradle.properties | 2 +- settings.gradle | 31 ++++++++++++++----------------- 7 files changed, 27 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index b751f1e61a1..7403b53fd8e 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -8,6 +8,7 @@ env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }} jobs: find_gradle_jobs: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a6243237a41..990ffa666b9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,6 +8,7 @@ env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }} jobs: thundra_test_initializer: diff --git a/.gitignore b/.gitignore index aa9fdf33f1f..d6a9a697796 100644 --- a/.gitignore +++ b/.gitignore @@ -64,3 +64,6 @@ src/mkdocs-codeinclude-plugin src/pip-delete-this-directory.txt .DS_Store + +# Codespaces / VSCode +/.vscode/ \ No newline at end of file diff --git a/build.gradle b/build.gradle index aac0648ec06..8fb15a6566f 100644 --- a/build.gradle +++ b/build.gradle @@ -46,9 +46,14 @@ subprojects { task delombok(type: io.franzbecker.gradle.lombok.task.DelombokTask) { def outputDir = file("$buildDir/delombok") outputs.dir(outputDir) + outputs.cacheIf { + true + } for (srcDir in project.sourceSets.main.java.srcDirs) { + // TODO: named input and relative path for `srcDir` to make task more cacheable inputs.dir(srcDir) - args(srcDir, "-d", outputDir) + // TODO: `outputDir` as relative path to make task more cacheable + args(srcDir, "-d", outputDir, "-f", "generateDelombokComment:skip") } } delombok.onlyIf { diff --git a/examples/settings.gradle b/examples/settings.gradle index 4a979449b07..13645d5f5ff 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.10.0" - classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.6.1" + classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.7.2" } } diff --git a/gradle.properties b/gradle.properties index 397b832391e..a063988958c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ org.gradle.parallel=false org.gradle.caching=true - +org.gradle.configureondemand=true diff --git a/settings.gradle b/settings.gradle index 5a3966e3d25..db8f11f14c0 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,13 +5,11 @@ buildscript { } } dependencies { - classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.10.0" - classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.6.1" - classpath "com.gradle:common-custom-user-data-gradle-plugin:1.2.1" + classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.7.2" + classpath "com.gradle:common-custom-user-data-gradle-plugin:1.6.1" } } -apply plugin: 'ch.myniva.s3-build-cache' apply plugin: 'com.gradle.enterprise' apply plugin: "com.gradle.common-custom-user-data-gradle-plugin" @@ -38,21 +36,20 @@ ext.isMasterBuild = false || (System.getenv("GITHUB_REF") == "refs/heads/master") || (System.getenv("BUILD_SOURCEBRANCHNAME") == "master") +ext.isCI = System.getenv("CI") != null + buildCache { local { - enabled = !isMasterBuild + enabled = !isCI } - - remote(ch.myniva.gradle.caching.s3.AwsS3BuildCache) { - endpoint = 'fra1.digitaloceanspaces.com' - region = 'fra1' - bucket = 'testcontainers' - path = 'cache' - reducedRedundancy = false - push = isMasterBuild && !System.getenv("READ_ONLY_REMOTE_GRADLE_CACHE") - headers = [ - 'x-amz-acl': 'public-read' - ] + remote(HttpBuildCache) { + push = isCI + enabled = true + url = 'https://ge.testcontainers.org/cache/' + credentials { + username = 'ci' + password = System.getenv("GRADLE_ENTERPRISE_CACHE_PASSWORD") + } } } @@ -61,7 +58,7 @@ gradleEnterprise { server = "https://ge.testcontainers.org/" publishAlways() publishIfAuthenticated() - uploadInBackground = System.getenv("CI") == null + uploadInBackground = !isCI captureTaskInputFiles = true } From 02a8eed88ef22e5894acadb883dd967c2a88080e Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 9 Dec 2021 15:17:19 +0000 Subject: [PATCH 2244/5615] Combined dependencies PR (#4768) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot-kotlin-redis/build.gradle | 4 ++-- examples/spring-boot/build.gradle | 2 +- modules/jdbc/build.gradle | 2 +- modules/localstack/build.gradle | 2 +- modules/mssqlserver/build.gradle | 4 ++-- modules/mysql/build.gradle | 4 ++-- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index f1618268508..242cfcd3810 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.5.6' + id 'org.springframework.boot' version '2.6.1' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 4c8d6d551fb..a37b63e6557 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,7 +1,7 @@ plugins { - id("org.springframework.boot") version "2.5.6" + id("org.springframework.boot") version "2.6.1" id("org.jetbrains.kotlin.jvm") version "1.6.0" - id("org.jetbrains.kotlin.plugin.spring") version "1.5.31" + id("org.jetbrains.kotlin.plugin.spring") version "1.6.0" } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index 85cb38f3781..b5ddd11cc84 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.5.6' + id 'org.springframework.boot' version '2.6.1' } apply plugin: 'io.spring.dependency-management' diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index f5056daedf4..5a4ff7872d4 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -7,7 +7,7 @@ dependencies { compileOnly 'org.jetbrains:annotations:23.0.0' testImplementation 'commons-dbutils:commons-dbutils:1.7' testImplementation 'org.vibur:vibur-dbcp:25.0' - testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.12' + testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.13' testImplementation 'com.zaxxer:HikariCP-java6:2.3.13' testImplementation 'com.googlecode.junit-toolbox:junit-toolbox:2.4' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index b96bd49ed59..4b62d30742f 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.122' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.122' - testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.100' + testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.124' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.122' testImplementation 'software.amazon.awssdk:s3:2.17.72' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index de691b47277..1e554440939 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -10,12 +10,12 @@ dependencies { compileOnly 'io.r2dbc:r2dbc-mssql:0.8.7.RELEASE' testImplementation project(':jdbc-test') - testImplementation 'com.microsoft.sqlserver:mssql-jdbc:9.4.0.jre8' + testImplementation 'com.microsoft.sqlserver:mssql-jdbc:9.5.0.jre8-preview' testImplementation project(':r2dbc') testImplementation 'io.r2dbc:r2dbc-mssql:0.8.7.RELEASE' // MSSQL's wait strategy requires the JDBC driver testImplementation testFixtures(project(':r2dbc')) - testImplementation 'com.microsoft.sqlserver:mssql-jdbc:9.4.0.jre8' + testImplementation 'com.microsoft.sqlserver:mssql-jdbc:9.5.0.jre8-preview' } diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index d84def3e7ba..88a4473f56c 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -1,7 +1,7 @@ description = "Testcontainers :: JDBC :: MySQL" dependencies { - annotationProcessor 'com.google.auto.service:auto-service:1.0' + annotationProcessor 'com.google.auto.service:auto-service:1.0.1' compileOnly 'com.google.auto.service:auto-service:1.0' api project(':jdbc') @@ -15,5 +15,5 @@ dependencies { testImplementation testFixtures(project(':r2dbc')) testImplementation 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' - compileOnly 'org.jetbrains:annotations:22.0.0' + compileOnly 'org.jetbrains:annotations:23.0.0' } From 8a6833d2454e918dd7111351e7580446b74ecdf8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Dec 2021 15:18:11 +0000 Subject: [PATCH 2245/5615] Bump tomcat-jdbc from 10.0.12 to 10.0.14 in /modules/jdbc Bumps tomcat-jdbc from 10.0.12 to 10.0.14. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 5a4ff7872d4..973bc9b01df 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -7,7 +7,7 @@ dependencies { compileOnly 'org.jetbrains:annotations:23.0.0' testImplementation 'commons-dbutils:commons-dbutils:1.7' testImplementation 'org.vibur:vibur-dbcp:25.0' - testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.13' + testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.14' testImplementation 'com.zaxxer:HikariCP-java6:2.3.13' testImplementation 'com.googlecode.junit-toolbox:junit-toolbox:2.4' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From a71554855b4fe06549eb23398e69837617e41974 Mon Sep 17 00:00:00 2001 From: Michael Nitschinger Date: Fri, 10 Dec 2021 08:36:36 +0100 Subject: [PATCH 2246/5615] [couchbase] Ignore transient primary index creation error. (#4681) This changeset makes sure that when a primary index is created, a transient error is ignored since the indexing engine will automatically retry the statement later. Transient errors can happen because we just created the bucket and the indexer might not have caught up to that bucket just yet, but it will eventually. In addition, a check has been added afterwards to make sure that the primary index is online (so, ready for the user to query) before returning to the caller, improving the overall stability of queries. --- .../couchbase/CouchbaseContainer.java | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index db8d8ec9666..9ef8e99e19d 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -473,7 +473,30 @@ private void createBuckets() { .add("statement", "CREATE PRIMARY INDEX on `" + bucket.getName() + "`") .build(), true); - checkSuccessfulResponse(queryResponse, "Could not create primary index for bucket " + bucket.getName()); + try { + checkSuccessfulResponse(queryResponse, "Could not create primary index for bucket " + bucket.getName()); + } catch (IllegalStateException ex) { + // potentially ignore the error, the index will be eventually built. + if (!ex.getMessage().contains("Index creation will be retried in background")) { + throw ex; + } + } + + timePhase( + "createBucket:" + bucket.getName() + ":primaryIndexOnline", + () -> Unreliables.retryUntilTrue(1, TimeUnit.MINUTES, () -> { + @Cleanup Response stateResponse = doHttpRequest(QUERY_PORT, "/query/service", "POST", new FormBody.Builder() + .add("statement", "SELECT count(*) > 0 AS online FROM system:indexes where keyspace_id = \"" + bucket.getName() + "\" and is_primary = true and state = \"online\"") + .build(), true); + + String body = stateResponse.body() != null ? stateResponse.body().string() : null; + checkSuccessfulResponse(stateResponse, "Could not poll primary index state for bucket: " + bucket.getName()); + + return Optional.of(MAPPER.readTree(body)) + .map(n -> n.at("/results/0/online")) + .map(JsonNode::asBoolean) + .orElse(false); + })); } else { logger().info("Primary index creation for bucket {} ignored, since QUERY service is not present.", bucket.getName()); } @@ -508,7 +531,7 @@ private void checkSuccessfulResponse(final Response response, final String messa } } - throw new IllegalStateException(message + ": " + response.toString() + ", body=" + (body == null ? "" : body)); + throw new IllegalStateException(message + ": " + response + ", body=" + (body == null ? "" : body)); } } From 42e4905b15b0fa42ef3343939edd8ce7dc531465 Mon Sep 17 00:00:00 2001 From: Szymon Przebierowski Date: Wed, 22 Dec 2021 17:01:22 +0100 Subject: [PATCH 2247/5615] Remove hardcoded log level setting on mockserver (#4798) --- .../java/org/testcontainers/containers/MockServerContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java b/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java index 0502469208e..6ab1c541325 100644 --- a/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java +++ b/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java @@ -38,7 +38,7 @@ public MockServerContainer(DockerImageName dockerImageName) { waitingFor(Wait.forHttp("/mockserver/status").withMethod("PUT").forStatusCode(200)); - withCommand("-logLevel INFO -serverPort " + PORT); + withCommand("-serverPort " + PORT); addExposedPorts(PORT); } From 7b098bc6bbdf18cf1c212337ba3e39afef6212a5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 Dec 2021 10:10:58 +0100 Subject: [PATCH 2248/5615] Update Gradle Wrapper to 7.3.3 (#4800) * Update Gradle Wrapper from 7.3.1 to 7.3.3. Signed-off-by: gradle-update-robot Co-authored-by: gradle-update-robot --- examples/gradle/wrapper/gradle-wrapper.properties | 4 ++-- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/gradle/wrapper/gradle-wrapper.properties b/examples/gradle/wrapper/gradle-wrapper.properties index 93a3251ccfc..59250647c40 100644 --- a/examples/gradle/wrapper/gradle-wrapper.properties +++ b/examples/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=9afb3ca688fc12c761a0e9e4321e4d24e977a4a8916c8a768b1fe05ddb4d6b66 -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.1-bin.zip +distributionSha256Sum=b586e04868a22fd817c8971330fec37e298f3242eb85c374181b12d637f80302 +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cf2b23094e4..ee6ba9a3ac4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=b75392c5625a88bccd58a574552a5a323edca82dab5942d2d41097f809c6bcce -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.1-all.zip +distributionSha256Sum=c9490e938b221daf0094982288e4038deed954a3f12fb54cbf270ddf4e37d879 +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From a9883cfcc0e4dce5ddd6b584be3d39351b62ce23 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Dec 2021 09:05:39 +0000 Subject: [PATCH 2249/5615] Bump mkdocs from 1.0.4 to 1.2.3 (#4771) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Pipfile.lock | 382 +++++++++++++++++++++++++++++---------------------- 1 file changed, 217 insertions(+), 165 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 6ac609b04a9..7dad511e377 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -18,104 +18,133 @@ "default": { "click": { "hashes": [ - "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", - "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" + "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3", + "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b" ], - "version": "==7.1.2" + "markers": "python_version >= '3.6'", + "version": "==8.0.3" }, - "importlib-metadata": { + "ghp-import": { "hashes": [ - "sha256:742add720a20d0467df2f444ae41704000f50e1234f46174b51f9c6031a1bd71", - "sha256:b74159469b464a99cb8cc3e21973e4d96e05d3024d337313fedb618a6e86e6f4" + "sha256:5f8962b30b20652cdffa9c5a9812f7de6bcb56ec475acac579807719bf242c46", + "sha256:947b3771f11be850c852c64b561c600fdddf794bab363060854c1ee7ad05e071" ], - "markers": "python_version < '3.8'", - "version": "==3.7.3" + "version": "==2.0.2" }, - "jinja2": { + "importlib-metadata": { "hashes": [ - "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419", - "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6" + "sha256:53ccfd5c134223e497627b9815d5030edf77d2ed573922f7a0b8f8bb81a1c100", + "sha256:75bdec14c397f528724c1bfd9709d660b33a4d2e77387a3358f20b848bb5e5fb" ], - "index": "pypi", - "version": "==2.11.3" + "markers": "python_version >= '3.6'", + "version": "==4.8.2" }, - "livereload": { + "jinja2": { "hashes": [ - "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869" + "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8", + "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7" ], - "version": "==2.6.3" + "markers": "python_version >= '3.6'", + "version": "==3.0.3" }, "markdown": { "hashes": [ - "sha256:31b5b491868dcc87d6c24b7e3d19a0d730d59d3e46f4eea6430a321bed387a49", - "sha256:96c3ba1261de2f7547b46a00ea8463832c921d3f9d6aba3f255a6f71386db20c" + "sha256:76df8ae32294ec39dcf89340382882dfa12975f87f45c3ed1ecdb1e8cefc7006", + "sha256:9923332318f843411e9932237530df53162e29dc7a4e2b91e35764583c46c9a3" ], - "version": "==3.3.4" + "markers": "python_version >= '3.6'", + "version": "==3.3.6" }, "markupsafe": { "hashes": [ - "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", - "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", - "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", - "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", - "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", - "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f", - "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39", - "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", - "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", - "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014", - "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f", - "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", - "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", - "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", - "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", - "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b", - "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", - "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", - "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", - "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85", - "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1", - "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", - "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", - "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", - "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850", - "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0", - "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", - "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", - "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb", - "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", - "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", - "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", - "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1", - "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2", - "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", - "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", - "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", - "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7", - "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", - "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8", - "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", - "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193", - "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", - "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b", - "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", - "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2", - "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5", - "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c", - "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032", - "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", - "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be", - "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621" + "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298", + "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64", + "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b", + "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194", + "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567", + "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff", + "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724", + "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74", + "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646", + "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35", + "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6", + "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a", + "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6", + "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad", + "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26", + "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38", + "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac", + "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7", + "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6", + "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047", + "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75", + "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f", + "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b", + "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135", + "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8", + "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a", + "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a", + "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1", + "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9", + "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864", + "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914", + "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee", + "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f", + "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18", + "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8", + "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2", + "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d", + "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b", + "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b", + "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86", + "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6", + "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f", + "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb", + "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833", + "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28", + "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e", + "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415", + "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902", + "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f", + "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d", + "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9", + "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d", + "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145", + "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066", + "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c", + "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1", + "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a", + "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207", + "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f", + "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53", + "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd", + "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134", + "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85", + "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9", + "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5", + "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94", + "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509", + "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51", + "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872" ], - "version": "==1.1.1" + "markers": "python_version >= '3.6'", + "version": "==2.0.1" + }, + "mergedeep": { + "hashes": [ + "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", + "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307" + ], + "markers": "python_version >= '3.6'", + "version": "==1.3.4" }, "mkdocs": { "hashes": [ - "sha256:17d34329aad75d5de604b9ed4e31df3a4d235afefdc46ce7b1964fddb2e1e939", - "sha256:8cc8b38325456b9e942c981a209eaeb1e9f3f77b493ad755bfef889b9c8d356a" + "sha256:89f5a094764381cda656af4298727c9f53dc3e602983087e1fe96ea1df24f4c1", + "sha256:a1fa8c2d0c1305d7fc2b9d9f607c71778572a8b110fb26642aa00296c9e6d072" ], "index": "pypi", - "version": "==1.0.4" + "version": "==1.2.3" }, "mkdocs-codeinclude-plugin": { "hashes": [ @@ -140,122 +169,145 @@ "index": "pypi", "version": "==3.2.0" }, + "packaging": { + "hashes": [ + "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", + "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" + ], + "markers": "python_version >= '3.6'", + "version": "==21.3" + }, "pygments": { "hashes": [ - "sha256:2656e1a6edcdabf4275f9a3640db59fd5de107d88e8663c5d4e9a0fa62f77f94", - "sha256:534ef71d539ae97d4c3a4cf7d6f110f214b0e687e92f9cb9d2a3b0d3101289c8" + "sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380", + "sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6" ], - "version": "==2.8.1" + "markers": "python_version >= '3.5'", + "version": "==2.10.0" }, "pymdown-extensions": { "hashes": [ - "sha256:478b2c04513fbb2db61688d5f6e9030a92fb9be14f1f383535c43f7be9dff95b", - "sha256:632371fa3bf1b21a0e3f4063010da59b41db049f261f4c0b0872069a9b6d1735" + "sha256:74247f2c80f1d9e3c7242abe1c16317da36c6f26c7ad4b8a7f457f0ec20f0365", + "sha256:b03e66f91f33af4a6e7a0e20c740313522995f69a03d86316b1449766c473d0e" ], - "version": "==8.1.1" + "markers": "python_version >= '3.6'", + "version": "==9.1" + }, + "pyparsing": { + "hashes": [ + "sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4", + "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81" + ], + "markers": "python_version >= '3.6'", + "version": "==3.0.6" + }, + "python-dateutil": { + "hashes": [ + "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", + "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.8.2" }, "pyyaml": { "hashes": [ - "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf", - "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696", - "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393", - "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77", - "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922", - "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5", - "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8", - "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10", - "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc", - "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018", - "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e", - "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253", - "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347", - "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183", - "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541", - "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb", - "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185", - "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc", - "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db", - "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa", - "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46", - "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122", - "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b", - "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63", - "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df", - "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc", - "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247", - "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6", - "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0" + "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", + "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b", + "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57", + "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b", + "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4", + "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07", + "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba", + "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9", + "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287", + "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513", + "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0", + "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0", + "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92", + "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f", + "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2", + "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc", + "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c", + "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86", + "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4", + "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c", + "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34", + "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b", + "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c", + "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb", + "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737", + "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3", + "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d", + "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53", + "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78", + "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803", + "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a", + "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174", + "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5" ], - "version": "==5.4.1" + "markers": "python_version >= '3.6'", + "version": "==6.0" }, - "six": { + "pyyaml-env-tag": { "hashes": [ - "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", - "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" + "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb", + "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069" ], - "version": "==1.15.0" + "markers": "python_version >= '3.6'", + "version": "==0.1" }, - "tornado": { + "six": { "hashes": [ - "sha256:0a00ff4561e2929a2c37ce706cb8233b7907e0cdc22eab98888aca5dd3775feb", - "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c", - "sha256:1e8225a1070cd8eec59a996c43229fe8f95689cb16e552d130b9793cb570a288", - "sha256:20241b3cb4f425e971cb0a8e4ffc9b0a861530ae3c52f2b0434e6c1b57e9fd95", - "sha256:25ad220258349a12ae87ede08a7b04aca51237721f63b1808d39bdb4b2164558", - "sha256:33892118b165401f291070100d6d09359ca74addda679b60390b09f8ef325ffe", - "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791", - "sha256:3447475585bae2e77ecb832fc0300c3695516a47d46cefa0528181a34c5b9d3d", - "sha256:34ca2dac9e4d7afb0bed4677512e36a52f09caa6fded70b4e3e1c89dbd92c326", - "sha256:3e63498f680547ed24d2c71e6497f24bca791aca2fe116dbc2bd0ac7f191691b", - "sha256:548430be2740e327b3fe0201abe471f314741efcb0067ec4f2d7dcfb4825f3e4", - "sha256:6196a5c39286cc37c024cd78834fb9345e464525d8991c21e908cc046d1cc02c", - "sha256:61b32d06ae8a036a6607805e6720ef00a3c98207038444ba7fd3d169cd998910", - "sha256:6286efab1ed6e74b7028327365cf7346b1d777d63ab30e21a0f4d5b275fc17d5", - "sha256:65d98939f1a2e74b58839f8c4dab3b6b3c1ce84972ae712be02845e65391ac7c", - "sha256:66324e4e1beede9ac79e60f88de548da58b1f8ab4b2f1354d8375774f997e6c0", - "sha256:6c77c9937962577a6a76917845d06af6ab9197702a42e1346d8ae2e76b5e3675", - "sha256:70dec29e8ac485dbf57481baee40781c63e381bebea080991893cd297742b8fd", - "sha256:7250a3fa399f08ec9cb3f7b1b987955d17e044f1ade821b32e5f435130250d7f", - "sha256:748290bf9112b581c525e6e6d3820621ff020ed95af6f17fedef416b27ed564c", - "sha256:7da13da6f985aab7f6f28debab00c67ff9cbacd588e8477034c0652ac141feea", - "sha256:8f959b26f2634a091bb42241c3ed8d3cedb506e7c27b8dd5c7b9f745318ddbb6", - "sha256:9de9e5188a782be6b1ce866e8a51bc76a0fbaa0e16613823fc38e4fc2556ad05", - "sha256:a48900ecea1cbb71b8c71c620dee15b62f85f7c14189bdeee54966fbd9a0c5bd", - "sha256:b87936fd2c317b6ee08a5741ea06b9d11a6074ef4cc42e031bc6403f82a32575", - "sha256:c77da1263aa361938476f04c4b6c8916001b90b2c2fdd92d8d535e1af48fba5a", - "sha256:cb5ec8eead331e3bb4ce8066cf06d2dfef1bfb1b2a73082dfe8a161301b76e37", - "sha256:cc0ee35043162abbf717b7df924597ade8e5395e7b66d18270116f8745ceb795", - "sha256:d14d30e7f46a0476efb0deb5b61343b1526f73ebb5ed84f23dc794bdb88f9d9f", - "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32", - "sha256:d3d20ea5782ba63ed13bc2b8c291a053c8d807a8fa927d941bd718468f7b950c", - "sha256:d3f7594930c423fd9f5d1a76bee85a2c36fd8b4b16921cae7e965f22575e9c01", - "sha256:dcef026f608f678c118779cd6591c8af6e9b4155c44e0d1bc0c87c036fb8c8c4", - "sha256:e0791ac58d91ac58f694d8d2957884df8e4e2f6687cdf367ef7eb7497f79eaa2", - "sha256:e385b637ac3acaae8022e7e47dfa7b83d3620e432e3ecb9a3f7f58f150e50921", - "sha256:e519d64089b0876c7b467274468709dadf11e41d65f63bba207e04217f47c085", - "sha256:e7229e60ac41a1202444497ddde70a48d33909e484f96eb0da9baf8dc68541df", - "sha256:ed3ad863b1b40cd1d4bd21e7498329ccaece75db5a5bf58cd3c9f130843e7102", - "sha256:f0ba29bafd8e7e22920567ce0d232c26d4d47c8b5cf4ed7b562b5db39fa199c5", - "sha256:fa2ba70284fa42c2a5ecb35e322e68823288a4251f9ba9cc77be04ae15eada68", - "sha256:fba85b6cd9c39be262fcd23865652920832b61583de2a2ca907dbd8e8a8c81e5" + "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" ], - "version": "==6.1" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.16.0" }, "typing-extensions": { "hashes": [ - "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918", - "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c", - "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f" + "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e", + "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b" ], "markers": "python_version < '3.8'", - "version": "==3.7.4.3" + "version": "==4.0.1" + }, + "watchdog": { + "hashes": [ + "sha256:25fb5240b195d17de949588628fdf93032ebf163524ef08933db0ea1f99bd685", + "sha256:3386b367e950a11b0568062b70cc026c6f645428a698d33d39e013aaeda4cc04", + "sha256:3becdb380d8916c873ad512f1701f8a92ce79ec6978ffde92919fd18d41da7fb", + "sha256:4ae38bf8ba6f39d5b83f78661273216e7db5b00f08be7592062cb1fc8b8ba542", + "sha256:8047da932432aa32c515ec1447ea79ce578d0559362ca3605f8e9568f844e3c6", + "sha256:8f1c00aa35f504197561060ca4c21d3cc079ba29cf6dd2fe61024c70160c990b", + "sha256:922a69fa533cb0c793b483becaaa0845f655151e7256ec73630a1b2e9ebcb660", + "sha256:9693f35162dc6208d10b10ddf0458cc09ad70c30ba689d9206e02cd836ce28a3", + "sha256:a0f1c7edf116a12f7245be06120b1852275f9506a7d90227648b250755a03923", + "sha256:a36e75df6c767cbf46f61a91c70b3ba71811dfa0aca4a324d9407a06a8b7a2e7", + "sha256:aba5c812f8ee8a3ff3be51887ca2d55fb8e268439ed44110d3846e4229eb0e8b", + "sha256:ad6f1796e37db2223d2a3f302f586f74c72c630b48a9872c1e7ae8e92e0ab669", + "sha256:ae67501c95606072aafa865b6ed47343ac6484472a2f95490ba151f6347acfc2", + "sha256:b2fcf9402fde2672545b139694284dc3b665fd1be660d73eca6805197ef776a3", + "sha256:b52b88021b9541a60531142b0a451baca08d28b74a723d0c99b13c8c8d48d604", + "sha256:b7d336912853d7b77f9b2c24eeed6a5065d0a0cc0d3b6a5a45ad6d1d05fb8cd8", + "sha256:bd9ba4f332cf57b2c1f698be0728c020399ef3040577cde2939f2e045b39c1e5", + "sha256:be9be735f827820a06340dff2ddea1fb7234561fa5e6300a62fe7f54d40546a0", + "sha256:cca7741c0fcc765568350cb139e92b7f9f3c9a08c4f32591d18ab0a6ac9e71b6", + "sha256:d0d19fb2441947b58fbf91336638c2b9f4cc98e05e1045404d7a4cb7cddc7a65", + "sha256:e02794ac791662a5eafc6ffeaf9bcc149035a0e48eb0a9d40a8feb4622605a3d", + "sha256:e0f30db709c939cabf64a6dc5babb276e6d823fd84464ab916f9b9ba5623ca15", + "sha256:e92c2d33858c8f560671b448205a268096e17870dcf60a9bb3ac7bfbafb7f5f9" + ], + "markers": "python_version >= '3.6'", + "version": "==2.1.6" }, "zipp": { "hashes": [ - "sha256:3607921face881ba3e026887d8150cca609d517579abe052ac81fc5aeffdbd76", - "sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098" + "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832", + "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc" ], - "version": "==3.4.1" + "markers": "python_version >= '3.6'", + "version": "==3.6.0" } }, "develop": {} From cbaedff97aebc2465ec111d525e97c2ec7a6a56e Mon Sep 17 00:00:00 2001 From: Richard North Date: Wed, 29 Dec 2021 15:44:01 +0000 Subject: [PATCH 2250/5615] Update sponsors as of Dec 2021 (#4806) --- docs/index.md | 20 +++++++++++++++++++- docs/sponsor_logos/elastic.png | Bin 0 -> 23741 bytes docs/sponsor_logos/red_hat.png | Bin 0 -> 20118 bytes 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 docs/sponsor_logos/elastic.png create mode 100644 docs/sponsor_logos/red_hat.png diff --git a/docs/index.md b/docs/index.md index 4bfbc723a5a..136ae53d2ca 100644 --- a/docs/index.md +++ b/docs/index.md @@ -127,16 +127,34 @@ A huge thank you to our sponsors: +

+ +### Donors + + + ### Backers * [Philip Riecks (@rieckpil)](https://github.com/rieckpil) * [Karl Heinz Marbaise (@khmarbaise)](https://github.com/khmarbaise) * [Sascha Frinken (@sascha-frinken)](https://github.com/sascha-frinken) * [Christoph Dreis (@dreis2211)](https://github.com/dreis2211) -* [Pascal Zwick (@pas2al)](https://github.com/pas2al) * [Nikita Zhevnitskiy (@zhenik)](https://github.com/zhenik) * [Bas Stoker (@bastoker)](https://github.com/bastoker) * [Oleg Nenashev (@oleg-nenashev)](https://github.com/oleg-nenashev) +* [Rik Glover (@rikglover)](https://github.com/rikglover) +* [Amitosh Swain Mahapatra (@recrsn)](https://github.com/recrsn) +* [Paris Apostolopoulos](https://opencollective.com/paris-apostolopoulos) ## Who is using Testcontainers? diff --git a/docs/sponsor_logos/elastic.png b/docs/sponsor_logos/elastic.png new file mode 100644 index 0000000000000000000000000000000000000000..393e6711c45f58934ffc04bd8278b9872c44bf44 GIT binary patch literal 23741 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 literal 0 HcmV?d00001 diff --git a/docs/sponsor_logos/red_hat.png b/docs/sponsor_logos/red_hat.png new file mode 100644 index 0000000000000000000000000000000000000000..b8c28b5d7b094b0531ea8ac7ed068da700513dd2 GIT binary patch literal 20118 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 R_uw*6=R(4QI|Jjt{}&3q{`~*| literal 0 HcmV?d00001 From 7d44f6873dbdf56da3483971d1f33e06afbad5cd Mon Sep 17 00:00:00 2001 From: Harsh Mishra Date: Wed, 29 Dec 2021 21:15:24 +0530 Subject: [PATCH 2251/5615] DOCS: Remove references to Atlassian for LocalStack (#4796) Co-authored-by: Kevin Wittek Co-authored-by: Richard North --- docs/modules/localstack.md | 4 ++-- .../containers/localstack/LocalStackContainer.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/modules/localstack.md b/docs/modules/localstack.md index b6058508ddb..1585c504cff 100644 --- a/docs/modules/localstack.md +++ b/docs/modules/localstack.md @@ -1,6 +1,6 @@ -# Localstack Module +# LocalStack Module -Testcontainers module for the Atlassian's [LocalStack](https://github.com/localstack/localstack), 'a fully functional local AWS cloud stack'. +Testcontainers module for [LocalStack](http://localstack.cloud/), 'a fully functional local AWS cloud stack', to develop and test your cloud and serverless apps without actually using the cloud. ## Usage example diff --git a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java index fe3ea6ab34f..dd03a4206e0 100644 --- a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java +++ b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java @@ -25,7 +25,7 @@ import java.util.stream.Collectors; /** - *

Container for Atlassian Labs Localstack, 'A fully functional local AWS cloud stack'.

+ *

Container for LocalStack, 'A fully functional local AWS cloud stack'.

*

{@link LocalStackContainer#withServices(Service...)} should be used to select which services * are to be launched. See {@link Service} for available choices. It is advised that * {@link LocalStackContainer#getEndpointConfiguration(Service)} and From 5897437b33a385778e7e193f1eaed14524a2b8ed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jan 2022 22:00:26 +0000 Subject: [PATCH 2252/5615] Bump tomcat-jdbc from 10.0.13 to 10.0.14 in /modules/jdbc-test Bumps tomcat-jdbc from 10.0.13 to 10.0.14. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 7dd1023b95a..46088aa45ac 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -14,7 +14,7 @@ dependencies { exclude(group: "net.java.dev.jna", module: "jna") } - api 'org.apache.tomcat:tomcat-jdbc:10.0.13' + api 'org.apache.tomcat:tomcat-jdbc:10.0.14' api 'org.vibur:vibur-dbcp:25.0' api 'mysql:mysql-connector-java:8.0.27' } From e90885ee0c818b0d39a5d3ad82a1b1e02701e72b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jan 2022 22:00:27 +0000 Subject: [PATCH 2253/5615] Bump clickhouse-jdbc from 0.3.1-patch to 0.3.2 in /modules/clickhouse Bumps [clickhouse-jdbc](https://github.com/ClickHouse/clickhouse-jdbc) from 0.3.1-patch to 0.3.2. - [Release notes](https://github.com/ClickHouse/clickhouse-jdbc/releases) - [Changelog](https://github.com/ClickHouse/clickhouse-jdbc/blob/master/CHANGELOG) - [Commits](https://github.com/ClickHouse/clickhouse-jdbc/compare/v0.3.1-patch...v0.3.2) --- updated-dependencies: - dependency-name: ru.yandex.clickhouse:clickhouse-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/clickhouse/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/clickhouse/build.gradle b/modules/clickhouse/build.gradle index 39b269d794b..914ee7efbd4 100644 --- a/modules/clickhouse/build.gradle +++ b/modules/clickhouse/build.gradle @@ -5,5 +5,5 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'ru.yandex.clickhouse:clickhouse-jdbc:0.3.1-patch' + testImplementation 'ru.yandex.clickhouse:clickhouse-jdbc:0.3.2' } From 5c5d4df5772e193995916f0328d8747ecf14aea9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jan 2022 22:00:28 +0000 Subject: [PATCH 2254/5615] Bump jedis from 3.7.0 to 4.0.1 in /modules/junit-jupiter Bumps [jedis](https://github.com/redis/jedis) from 3.7.0 to 4.0.1. - [Release notes](https://github.com/redis/jedis/releases) - [Commits](https://github.com/redis/jedis/compare/v3.7.0...v4.0.1) --- updated-dependencies: - dependency-name: redis.clients:jedis dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 7907b30e386..ad0d8d3218a 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -7,7 +7,7 @@ dependencies { testImplementation project(':mysql') testImplementation project(':postgresql') testImplementation 'com.zaxxer:HikariCP:4.0.3' - testImplementation 'redis.clients:jedis:3.7.0' + testImplementation 'redis.clients:jedis:4.0.1' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' testImplementation ('org.mockito:mockito-core:4.1.0') { exclude(module: 'hamcrest-core') From 107bf88af7054f48f94b79287b479acd826d25e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jan 2022 22:00:28 +0000 Subject: [PATCH 2255/5615] Bump java-client from 3.2.3 to 3.2.4 in /modules/couchbase Bumps [java-client](https://github.com/couchbase/couchbase-jvm-clients) from 3.2.3 to 3.2.4. - [Release notes](https://github.com/couchbase/couchbase-jvm-clients/releases) - [Commits](https://github.com/couchbase/couchbase-jvm-clients/compare/java-client-3.2.3...java-client-3.2.4) --- updated-dependencies: - dependency-name: com.couchbase.client:java-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 772876b7bf1..ea720ca3725 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Couchbase" dependencies { api project(':testcontainers') - testImplementation 'com.couchbase.client:java-client:3.2.3' + testImplementation 'com.couchbase.client:java-client:3.2.4' testImplementation 'org.awaitility:awaitility:4.1.1' } From d7e28d23fffcfda2008a8963a615e20f4e3b1dcc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jan 2022 22:00:29 +0000 Subject: [PATCH 2256/5615] Bump mockito-core from 4.1.0 to 4.2.0 in /core Bumps [mockito-core](https://github.com/mockito/mockito) from 4.1.0 to 4.2.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.1.0...v4.2.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 950f73c1d7b..859bdf1fdde 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -93,7 +93,7 @@ dependencies { testImplementation 'com.rabbitmq:amqp-client:5.14.0' testImplementation 'org.mongodb:mongo-java-driver:3.12.10' - testImplementation ('org.mockito:mockito-core:4.1.0') { + testImplementation ('org.mockito:mockito-core:4.2.0') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From 05923501981e3006e02f61c2179963935407e86a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jan 2022 22:00:30 +0000 Subject: [PATCH 2257/5615] Bump aws-java-sdk-dynamodb in /modules/dynalite Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.12.122 to 1.12.131. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.122...1.12.131) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-dynamodb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/dynalite/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index fb4edcf1576..d891ed5b4d9 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Dynalite" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.122' - testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.100' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.131' + testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.131' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 97eb8edf78cb4756e2ad22547bdec68f7f2a862a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jan 2022 22:00:31 +0000 Subject: [PATCH 2258/5615] Bump mockito-core from 4.1.0 to 4.2.0 in /modules/junit-jupiter Bumps [mockito-core](https://github.com/mockito/mockito) from 4.1.0 to 4.2.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.1.0...v4.2.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 7907b30e386..01e971407b8 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,7 +9,7 @@ dependencies { testImplementation 'com.zaxxer:HikariCP:4.0.3' testImplementation 'redis.clients:jedis:3.7.0' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' - testImplementation ('org.mockito:mockito-core:4.1.0') { + testImplementation ('org.mockito:mockito-core:4.2.0') { exclude(module: 'hamcrest-core') } testImplementation 'org.assertj:assertj-core:3.21.0' From 38ca320ff7700675c07cf6b6b3cc9e137c0aedb2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jan 2022 22:00:32 +0000 Subject: [PATCH 2259/5615] Bump jedis from 3.7.0 to 4.0.1 in /core Bumps [jedis](https://github.com/redis/jedis) from 3.7.0 to 4.0.1. - [Release notes](https://github.com/redis/jedis/releases) - [Commits](https://github.com/redis/jedis/compare/v3.7.0...v4.0.1) --- updated-dependencies: - dependency-name: redis.clients:jedis dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 950f73c1d7b..6075f6d3089 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -89,7 +89,7 @@ dependencies { shaded 'org.zeroturnaround:zt-exec:1.12' testImplementation 'org.apache.httpcomponents:httpclient:4.5.9' - testImplementation 'redis.clients:jedis:3.7.0' + testImplementation 'redis.clients:jedis:4.0.1' testImplementation 'com.rabbitmq:amqp-client:5.14.0' testImplementation 'org.mongodb:mongo-java-driver:3.12.10' From 7be241be812c80b49bfbbf9a6f31b7f3f528431e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jan 2022 22:00:33 +0000 Subject: [PATCH 2260/5615] Bump elasticsearch-rest-client in /modules/elasticsearch Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 7.15.2 to 7.16.2. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.15.2...v7.16.2) --- updated-dependencies: - dependency-name: org.elasticsearch.client:elasticsearch-rest-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index ad1446a0f0f..7b92dc8279d 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,7 +2,7 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') - testImplementation "org.elasticsearch.client:elasticsearch-rest-client:7.15.2" + testImplementation "org.elasticsearch.client:elasticsearch-rest-client:7.16.2" testImplementation "org.elasticsearch.client:transport:7.15.2" testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 39322a75e1a02c79ab2110f78a13f0cbbf878592 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jan 2022 22:00:35 +0000 Subject: [PATCH 2261/5615] Bump snakeyaml from 1.29 to 1.30 in /core Bumps [snakeyaml](https://bitbucket.org/snakeyaml/snakeyaml) from 1.29 to 1.30. - [Commits](https://bitbucket.org/snakeyaml/snakeyaml/branches/compare/snakeyaml-1.30..snakeyaml-1.29) --- updated-dependencies: - dependency-name: org.yaml:snakeyaml dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 950f73c1d7b..776c519f6b2 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -82,7 +82,7 @@ dependencies { api 'com.github.docker-java:docker-java-transport-zerodep' - shaded "org.yaml:snakeyaml:1.29" + shaded "org.yaml:snakeyaml:1.30" shaded 'org.glassfish.main.external:trilead-ssh2-repackaged:4.1.2' From 86b37183cd103f6677d16626e5c03a02f2c4db4f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jan 2022 22:00:35 +0000 Subject: [PATCH 2262/5615] Bump s3 from 2.17.72 to 2.17.102 in /modules/localstack Bumps s3 from 2.17.72 to 2.17.102. --- updated-dependencies: - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 4b62d30742f..541a33405ca 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.122' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.124' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.122' - testImplementation 'software.amazon.awssdk:s3:2.17.72' + testImplementation 'software.amazon.awssdk:s3:2.17.102' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 514226ae9cae9aaecd9d8a65f54cc517a328c7f7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jan 2022 22:00:35 +0000 Subject: [PATCH 2263/5615] Bump aws-java-sdk-sqs from 1.12.124 to 1.12.131 in /modules/localstack Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.12.124 to 1.12.131. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.124...1.12.131) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-sqs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 4b62d30742f..4adb5264616 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.122' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.122' - testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.124' + testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.131' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.122' testImplementation 'software.amazon.awssdk:s3:2.17.72' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From fbb031eb93844a6d968eb326dd13da1dcf68ea50 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jan 2022 22:00:38 +0000 Subject: [PATCH 2264/5615] Bump aws-java-sdk-s3 from 1.12.122 to 1.12.131 in /modules/localstack Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.122 to 1.12.131. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.122...1.12.131) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 4b62d30742f..326778688ab 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.122' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.131' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.122' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.124' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.122' From a46d0a47bbd7a89a1f5162513a6adcf89c4f0f59 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jan 2022 22:00:56 +0000 Subject: [PATCH 2265/5615] Bump jedis from 3.0.1 to 4.0.1 in /modules/toxiproxy Bumps [jedis](https://github.com/redis/jedis) from 3.0.1 to 4.0.1. - [Release notes](https://github.com/redis/jedis/releases) - [Commits](https://github.com/redis/jedis/compare/jedis-3.0.1...v4.0.1) --- updated-dependencies: - dependency-name: redis.clients:jedis dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/toxiproxy/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/toxiproxy/build.gradle b/modules/toxiproxy/build.gradle index cad4b6b71f3..e055f5a52c5 100644 --- a/modules/toxiproxy/build.gradle +++ b/modules/toxiproxy/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':testcontainers') api 'eu.rekawek.toxiproxy:toxiproxy-java:2.1.5' - testImplementation 'redis.clients:jedis:3.0.1' + testImplementation 'redis.clients:jedis:4.0.1' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' testImplementation project(':test-support') From 3aabcf2fd9bc8a3fff29c08ed5cc3e204b51016c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jan 2022 22:00:59 +0000 Subject: [PATCH 2266/5615] Bump gradle-update/update-gradle-wrapper-action from 1.0.15 to 1.0.16 Bumps [gradle-update/update-gradle-wrapper-action](https://github.com/gradle-update/update-gradle-wrapper-action) from 1.0.15 to 1.0.16. - [Release notes](https://github.com/gradle-update/update-gradle-wrapper-action/releases) - [Changelog](https://github.com/gradle-update/update-gradle-wrapper-action/blob/master/CHANGELOG.md) - [Commits](https://github.com/gradle-update/update-gradle-wrapper-action/compare/a116d8b533be6c4e71a8b3c3871b7344ca72bf12...f31ab392a9d75029f9fd0883d8d6745c0139a006) --- updated-dependencies: - dependency-name: gradle-update/update-gradle-wrapper-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/update-gradle-wrapper.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-gradle-wrapper.yml b/.github/workflows/update-gradle-wrapper.yml index 08fbc91d00c..612aec0b406 100644 --- a/.github/workflows/update-gradle-wrapper.yml +++ b/.github/workflows/update-gradle-wrapper.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v2 - name: Update Gradle Wrapper - uses: gradle-update/update-gradle-wrapper-action@a116d8b533be6c4e71a8b3c3871b7344ca72bf12 # v1.0.13 + uses: gradle-update/update-gradle-wrapper-action@f31ab392a9d75029f9fd0883d8d6745c0139a006 # v1.0.13 with: repo-token: ${{ secrets.GITHUB_TOKEN }} labels: dependencies From cf2a23d4cf0c5ad151ac7ced12796edc075a51c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jan 2022 22:01:00 +0000 Subject: [PATCH 2267/5615] Bump solr-solrj from 8.10.1 to 8.11.1 in /examples Bumps solr-solrj from 8.10.1 to 8.11.1. --- updated-dependencies: - dependency-name: org.apache.solr:solr-solrj dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/solr-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/solr-container/build.gradle b/examples/solr-container/build.gradle index 965a40b35a8..c0c69300377 100644 --- a/examples/solr-container/build.gradle +++ b/examples/solr-container/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly "org.projectlombok:lombok:1.18.22" annotationProcessor "org.projectlombok:lombok:1.18.22" - implementation 'org.apache.solr:solr-solrj:8.10.1' + implementation 'org.apache.solr:solr-solrj:8.11.1' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.testcontainers:solr' From 132476d4d85a044321ab288296c2dea4808890c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jan 2022 22:01:03 +0000 Subject: [PATCH 2268/5615] Bump actions/setup-java from 2.4.0 to 2.5.0 Bumps [actions/setup-java](https://github.com/actions/setup-java) from 2.4.0 to 2.5.0. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v2.4.0...v2.5.0) --- updated-dependencies: - dependency-name: actions/setup-java dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- .github/workflows/release.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index 7403b53fd8e..d7351605e6d 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -17,7 +17,7 @@ jobs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.4.0 + - uses: actions/setup-java@v2.5.0 with: java-version: '8.0.302' distribution: temurin @@ -45,7 +45,7 @@ jobs: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.4.0 + - uses: actions/setup-java@v2.5.0 with: java-version: '8.0.302' distribution: temurin diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 990ffa666b9..0fd59f892f0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.4.0 + - uses: actions/setup-java@v2.5.0 with: java-version: '8.0.302' distribution: temurin @@ -51,7 +51,7 @@ jobs: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.4.0 + - uses: actions/setup-java@v2.5.0 with: java-version: '8.0.302' distribution: temurin diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7b727567ecb..d9026eb2257 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.4.0 + - uses: actions/setup-java@v2.5.0 with: java-version: '8.0.302' distribution: temurin From f0ddacf7150fe3d211b67ad97a9bb73abfa4361a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jan 2022 22:01:07 +0000 Subject: [PATCH 2269/5615] Bump peter-evans/create-pull-request from 3.11.0 to 3.12.0 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 3.11.0 to 3.12.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/67df31e08a133c6a77008b89689677067fef169e...dcd5fd746d53dd8de555c0f10bca6c35628be47a) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/update-docs-version.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index 790dad57188..418598f230d 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -17,7 +17,7 @@ jobs: sed -i "s/latest_version: .*/latest_version: ${GITHUB_REF##*/}/g" mkdocs.yml git diff - name: Create Pull Request - uses: peter-evans/create-pull-request@67df31e08a133c6a77008b89689677067fef169e # v3.10.1 + uses: peter-evans/create-pull-request@dcd5fd746d53dd8de555c0f10bca6c35628be47a # v3.10.1 with: title: Update docs version to ${GITHUB_REF##*/} body: | From 2c26b68bbc07d1f0706d97709e28eb6c13ed60d3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jan 2022 22:01:08 +0000 Subject: [PATCH 2270/5615] Bump jedis from 3.7.0 to 4.0.1 in /examples Bumps [jedis](https://github.com/redis/jedis) from 3.7.0 to 4.0.1. - [Release notes](https://github.com/redis/jedis/releases) - [Commits](https://github.com/redis/jedis/compare/v3.7.0...v4.0.1) --- updated-dependencies: - dependency-name: redis.clients:jedis dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 8f30256e2da..7f81bbf4c57 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' - implementation 'redis.clients:jedis:3.7.0' + implementation 'redis.clients:jedis:4.0.1' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index b6ac1f4ddd6..5a71ca3d2ff 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' - implementation 'redis.clients:jedis:3.7.0' + implementation 'redis.clients:jedis:4.0.1' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index a8b10171d99..35f7483113f 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { - implementation 'redis.clients:jedis:3.7.0' + implementation 'redis.clients:jedis:4.0.1' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.32' From 62fdbda74efd649278cee380d13d025ec8807457 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jan 2022 22:01:10 +0000 Subject: [PATCH 2271/5615] Bump com.gradle.enterprise.gradle.plugin from 3.7.2 to 3.8 in /examples Bumps com.gradle.enterprise.gradle.plugin from 3.7.2 to 3.8. --- updated-dependencies: - dependency-name: com.gradle.enterprise:com.gradle.enterprise.gradle.plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/settings.gradle b/examples/settings.gradle index 13645d5f5ff..22b279b1573 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.10.0" - classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.7.2" + classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.8" } } From a3e264a0dfe858c1417a32fc135a5cb16cf6f3c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jan 2022 22:01:16 +0000 Subject: [PATCH 2272/5615] Bump json from 20210307 to 20211205 in /examples Bumps [json](https://github.com/douglascrockford/JSON-java) from 20210307 to 20211205. - [Release notes](https://github.com/douglascrockford/JSON-java/releases) - [Changelog](https://github.com/stleary/JSON-java/blob/master/docs/RELEASES.md) - [Commits](https://github.com/douglascrockford/JSON-java/commits) --- updated-dependencies: - dependency-name: org.json:json dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 091a35ae042..dfdc749d191 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' implementation 'com.squareup.okhttp3:okhttp:4.9.3' - implementation 'org.json:json:20210307' + implementation 'org.json:json:20211205' testImplementation 'org.postgresql:postgresql:42.3.1' testImplementation 'ch.qos.logback:logback-classic:1.2.7' testImplementation 'org.testcontainers:postgresql' From 8374167877213329ccabdec1fb317c25592e2f1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jan 2022 22:01:19 +0000 Subject: [PATCH 2273/5615] Bump org.jetbrains.kotlin.jvm from 1.6.0 to 1.6.10 in /examples Bumps org.jetbrains.kotlin.jvm from 1.6.0 to 1.6.10. --- updated-dependencies: - dependency-name: org.jetbrains.kotlin.jvm dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index a37b63e6557..e0028d26b4a 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,6 +1,6 @@ plugins { id("org.springframework.boot") version "2.6.1" - id("org.jetbrains.kotlin.jvm") version "1.6.0" + id("org.jetbrains.kotlin.jvm") version "1.6.10" id("org.jetbrains.kotlin.plugin.spring") version "1.6.0" } From 23cbeb30d04a634c6c512e56446c37bf5c10664c Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Tue, 4 Jan 2022 09:27:50 +0100 Subject: [PATCH 2274/5615] Fix for API changes in Jedis Use `long` primitive instead of boxed type. --- .../junit/jupiter/inheritance/InheritedTests.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/InheritedTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/InheritedTests.java index 720c31cf794..adeaf58675c 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/InheritedTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/InheritedTests.java @@ -12,15 +12,15 @@ class InheritedTests extends AbstractTestBase { @Test void step1() { - assertEquals(1, redisPerClass.getJedis().incr("key").longValue()); - assertEquals(1, redisPerTest.getJedis().incr("key").longValue()); - assertEquals(1, myRedis.getJedis().incr("key").longValue()); + assertEquals(1, redisPerClass.getJedis().incr("key")); + assertEquals(1, redisPerTest.getJedis().incr("key")); + assertEquals(1, myRedis.getJedis().incr("key")); } @Test void step2() { - assertEquals(2, redisPerClass.getJedis().incr("key").longValue()); - assertEquals(1, redisPerTest.getJedis().incr("key").longValue()); - assertEquals(1, myRedis.getJedis().incr("key").longValue()); + assertEquals(2, redisPerClass.getJedis().incr("key")); + assertEquals(1, redisPerTest.getJedis().incr("key")); + assertEquals(1, myRedis.getJedis().incr("key")); } } From f89decebb35e6564495b084f6a5e826690678e95 Mon Sep 17 00:00:00 2001 From: Gerrit Meier Date: Tue, 4 Jan 2022 11:15:13 +0100 Subject: [PATCH 2275/5615] Upgrade Neo4j server and driver dependencies. (#4789) * Upgrade Neo4j Java Driver for testing to 4.4.2. * Upgrade Neo4j to 4.4.1. * Default to 4.4 instead concrete 4.4.1. * Throw exception if using database copy with Neo4j version !=3. * More specific javadoc and version check. * Adjust documentation for copy database functionality. * More robust check for defined image version. --- docs/modules/databases/neo4j.md | 3 ++ modules/neo4j/build.gradle | 6 +-- .../containers/Neo4jContainer.java | 29 +++++++++++- .../Neo4jContainerJUnitIntegrationTest.java | 8 ++-- .../containers/Neo4jContainerTest.java | 47 ++++++++++++++----- .../containers/Neo4jTestImages.java | 2 +- .../example-container-license-acceptance.txt | 2 +- 7 files changed, 76 insertions(+), 21 deletions(-) diff --git a/docs/modules/databases/neo4j.md b/docs/modules/databases/neo4j.md index 702f9cc40d7..0310d37d817 100644 --- a/docs/modules/databases/neo4j.md +++ b/docs/modules/databases/neo4j.md @@ -139,6 +139,9 @@ public class ExampleTest { } ``` +!!! note +The `withDatabase` method will only work with Neo4j 3.5 and throw an exception if used in combination with a newer version. + ## Choose your Neo4j license If you need the Neo4j enterprise license, you can declare your Neo4j container like this: diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 51cc625dc9d..cdc727921b4 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -18,8 +18,8 @@ sourceSets { task customNeo4jPluginJar(type: Jar) { from sourceSets.customNeo4jPlugin.output - archiveName = "hello-world.jar" - destinationDir = customNeo4jPluginDestinationDir + archiveFileName = "hello-world.jar" + destinationDirectory = customNeo4jPluginDestinationDir inputs.files(sourceSets.customNeo4jPlugin.java.srcDirs) outputs.cacheIf { true } @@ -33,6 +33,6 @@ dependencies { api project(":testcontainers") - testImplementation "org.neo4j.driver:neo4j-java-driver:1.7.5" + testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.2' testImplementation 'org.assertj:assertj-core:3.21.0' } diff --git a/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java index 7890bf5dcad..f844df3f945 100644 --- a/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java +++ b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java @@ -10,6 +10,7 @@ import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.containers.wait.strategy.WaitAllStrategy; import org.testcontainers.containers.wait.strategy.WaitStrategy; +import org.testcontainers.utility.ComparableVersion; import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.LicenseAcceptance; import org.testcontainers.utility.MountableFile; @@ -30,7 +31,7 @@ public class Neo4jContainer> extends GenericContaine /** * The default tag (version) to use. */ - private static final String DEFAULT_TAG = "3.5.0"; + private static final String DEFAULT_TAG = "4.4"; private static final String ENTERPRISE_TAG = DEFAULT_TAG + "-enterprise"; /** @@ -194,6 +195,8 @@ public S withoutAuthentication() { * If you want to map your database into the container instead of copying them, please use {@code #withClasspathResourceMapping}, * but this will only work when your test does not run in a container itself. *
+ * Note: This method only works with Neo4j 3.5. + *
* Mapping would work like this: *

      *      @Container
@@ -202,9 +205,14 @@ public S withoutAuthentication() {
      * 
* * @param graphDb The graph.db folder to copy into the container + * @throws IllegalArgumentException If the database version is not 3.5. * @return This container. */ public S withDatabase(MountableFile graphDb) { + if (!isNeo4jDatabaseVersionSupportingDbCopy()) { + throw new IllegalArgumentException( + "Copying database folder is not supported for Neo4j instances with version 4.0 or higher."); + } return withCopyFileToContainer(graphDb, "/data/databases/graph.db"); } @@ -250,4 +258,23 @@ private static String formatConfigurationKey(String plainConfigKey) { .replaceAll("_", "__") .replaceAll("\\.", "_")); } + + private boolean isNeo4jDatabaseVersionSupportingDbCopy() { + String usedImageVersion = DockerImageName.parse(getDockerImageName()).getVersionPart(); + ComparableVersion usedComparableVersion = new ComparableVersion(usedImageVersion); + + boolean versionSupportingDbCopy = + usedComparableVersion.isLessThan("4.0") && usedComparableVersion.isGreaterThanOrEqualTo("2"); + + if (versionSupportingDbCopy) { + return true; + } + if (!usedComparableVersion.isSemanticVersion()) { + logger().warn("Version {} is not a semantic version. The function \"withDatabase\" will fail.", usedImageVersion); + logger().warn("Copying databases is only supported for Neo4j versions 3.5.x"); + + } + + return false; + } } diff --git a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerJUnitIntegrationTest.java b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerJUnitIntegrationTest.java index 5a67674cbe8..ad6ccad8281 100644 --- a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerJUnitIntegrationTest.java +++ b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerJUnitIntegrationTest.java @@ -2,10 +2,10 @@ import org.junit.ClassRule; import org.junit.Test; -import org.neo4j.driver.v1.AuthTokens; -import org.neo4j.driver.v1.Driver; -import org.neo4j.driver.v1.GraphDatabase; -import org.neo4j.driver.v1.Session; +import org.neo4j.driver.AuthTokens; +import org.neo4j.driver.Driver; +import org.neo4j.driver.GraphDatabase; +import org.neo4j.driver.Session; import java.util.Collections; diff --git a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java index 6a32dac0af0..fcfda6f6873 100644 --- a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java +++ b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java @@ -1,13 +1,14 @@ package org.testcontainers.containers; +import org.assertj.core.api.Assertions; import org.junit.Test; -import org.neo4j.driver.v1.AuthToken; -import org.neo4j.driver.v1.AuthTokens; -import org.neo4j.driver.v1.Driver; -import org.neo4j.driver.v1.GraphDatabase; -import org.neo4j.driver.v1.Record; -import org.neo4j.driver.v1.Session; -import org.neo4j.driver.v1.StatementResult; +import org.neo4j.driver.AuthToken; +import org.neo4j.driver.AuthTokens; +import org.neo4j.driver.Driver; +import org.neo4j.driver.GraphDatabase; +import org.neo4j.driver.Record; +import org.neo4j.driver.Result; +import org.neo4j.driver.Session; import org.testcontainers.utility.MountableFile; import java.util.Collections; @@ -45,7 +46,7 @@ public void shouldDisableAuthentication() { @Test public void shouldCopyDatabase() { try ( - Neo4jContainer neo4jContainer = new Neo4jContainer<>(Neo4jTestImages.NEO4J_TEST_IMAGE) + Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:3.5.30") .withDatabase(MountableFile.forClasspathResource("/test-graph.db")) ) { neo4jContainer.start(); @@ -53,13 +54,37 @@ public void shouldCopyDatabase() { Driver driver = getDriver(neo4jContainer); Session session = driver.session() ) { - StatementResult result = session.run("MATCH (t:Thing) RETURN t"); + Result result = session.run("MATCH (t:Thing) RETURN t"); assertThat(result.list().stream().map(r -> r.get("t").get("name").asString())) .containsExactlyInAnyOrder("Thing", "Thing 2", "Thing 3", "A box"); } } } + @Test + public void shouldFailOnCopyDatabaseForDefaultNeo4j4Image() { + Assertions.assertThatIllegalArgumentException() + .isThrownBy(() -> new Neo4jContainer<>() + .withDatabase(MountableFile.forClasspathResource("/test-graph.db"))) + .withMessage("Copying database folder is not supported for Neo4j instances with version 4.0 or higher."); + } + + @Test + public void shouldFailOnCopyDatabaseForCustomNeo4j4Image() { + Assertions.assertThatIllegalArgumentException() + .isThrownBy(() -> new Neo4jContainer<>("neo4j:4.4.1") + .withDatabase(MountableFile.forClasspathResource("/test-graph.db"))) + .withMessage("Copying database folder is not supported for Neo4j instances with version 4.0 or higher."); + } + + @Test + public void shouldFailOnCopyDatabaseForCustomNonSemverNeo4j4Image() { + Assertions.assertThatIllegalArgumentException() + .isThrownBy(() -> new Neo4jContainer<>("neo4j:latest") + .withDatabase(MountableFile.forClasspathResource("/test-graph.db"))) + .withMessage("Copying database folder is not supported for Neo4j instances with version 4.0 or higher."); + } + @Test public void shouldCopyPlugins() { try ( @@ -93,7 +118,7 @@ public void shouldCopyPlugin() { } private static void assertThatCustomPluginWasCopied(Session session) { - StatementResult result = session.run("RETURN ac.simons.helloWorld('Testcontainers') AS greeting"); + Result result = session.run("RETURN ac.simons.helloWorld('Testcontainers') AS greeting"); Record singleRecord = result.single(); assertThat(singleRecord).isNotNull(); assertThat(singleRecord.get("greeting").asString()).isEqualTo("Hello, Testcontainers"); @@ -103,7 +128,7 @@ private static void assertThatCustomPluginWasCopied(Session session) { public void shouldCheckEnterpriseLicense() { assumeThat(Neo4jContainerTest.class.getResource(ACCEPTANCE_FILE_LOCATION)).isNull(); - String expectedImageName = "neo4j:3.5.0-enterprise"; + String expectedImageName = "neo4j:4.4-enterprise"; assertThatExceptionOfType(IllegalStateException.class) .isThrownBy(() -> new Neo4jContainer<>(Neo4jTestImages.NEO4J_TEST_IMAGE).withEnterpriseEdition()) diff --git a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jTestImages.java b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jTestImages.java index 2cab24baf2b..aee6f29fc5f 100644 --- a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jTestImages.java +++ b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jTestImages.java @@ -3,5 +3,5 @@ import org.testcontainers.utility.DockerImageName; public interface Neo4jTestImages { - DockerImageName NEO4J_TEST_IMAGE = DockerImageName.parse("neo4j:3.5.0"); + DockerImageName NEO4J_TEST_IMAGE = DockerImageName.parse("neo4j:4.4.1"); } diff --git a/modules/neo4j/src/test/resources/example-container-license-acceptance.txt b/modules/neo4j/src/test/resources/example-container-license-acceptance.txt index 9b92a79c737..e1f78f2d982 100644 --- a/modules/neo4j/src/test/resources/example-container-license-acceptance.txt +++ b/modules/neo4j/src/test/resources/example-container-license-acceptance.txt @@ -1 +1 @@ -neo4j:3.5.0-enterprise +neo4j:4.4.1-enterprise From 04d5247dc2d841dceceaa34e1e26a479ee73487e Mon Sep 17 00:00:00 2001 From: Michael Nitschinger Date: Tue, 4 Jan 2022 11:17:09 +0100 Subject: [PATCH 2276/5615] [couchbase] Explicitly configure service quotas (and allow customization). (#4802) This changeset explicitly configures the service memory quotas to their documented limits by default. This ensures that if a service is enabled, it does not consume more memory than absolutely needed. This is important because some services (like analytics) try to grab memory available which is good for production but of limited use in a testcontainers setting. A new API is also added which allows to customize the memory quota for each service should the test case require more than the minimum (which for most services is 256MB anyways). --- .../couchbase/CouchbaseContainer.java | 57 +++++++++++ .../couchbase/CouchbaseService.java | 97 ++++++++++++------- 2 files changed, 120 insertions(+), 34 deletions(-) diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index 9ef8e99e19d..5007a8d5ccf 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -36,8 +36,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.EnumSet; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -100,6 +102,11 @@ public class CouchbaseContainer extends GenericContainer { CouchbaseService.INDEX ); + /** + * Holds the custom service quotas if configured by the user. + */ + private final Map customServiceQuotas = new HashMap<>(); + private final List buckets = new ArrayList<>(); private boolean isEnterprise = false; @@ -158,6 +165,26 @@ public CouchbaseContainer withEnabledServices(final CouchbaseService... enabled) return this; } + /** + * Configures a custom memory quota for a given service. + * + * @param service the service to configure the quota for. + * @param quotaMb the memory quota in MB. + * @return this {@link CouchbaseContainer} for chaining purposes. + */ + public CouchbaseContainer withServiceQuota(final CouchbaseService service, final int quotaMb) { + checkNotRunning(); + if (!service.hasQuota()) { + throw new IllegalArgumentException("The provided service (" + service + ") has no quota to configure"); + } + if (quotaMb < service.getMinimumQuotaMb()) { + throw new IllegalArgumentException("The custom quota (" + quotaMb + ") must not be smaller than the " + + "minimum quota for the service (" + service.getMinimumQuotaMb() + ")"); + } + this.customServiceQuotas.put(service, quotaMb); + return this; + } + /** * Enables the analytics service which is not enabled by default. * @@ -262,6 +289,7 @@ protected void containerIsStarting(final InspectContainerResponse containerInfo) timePhase("initializeIsEnterprise", this::initializeIsEnterprise); timePhase("renameNode", this::renameNode); timePhase("initializeServices", this::initializeServices); + timePhase("setMemoryQuotas", this::setMemoryQuotas); timePhase("configureAdminUser", this::configureAdminUser); timePhase("configureExternalPorts", this::configureExternalPorts); @@ -341,6 +369,35 @@ private void initializeServices() { checkSuccessfulResponse(response, "Could not enable couchbase services"); } + /** + * Sets the memory quotas for each enabled service. + *

+ * If there is no explicit custom quota defined, the default minimum quota will be used. + */ + private void setMemoryQuotas() { + logger().debug("Custom service memory quotas: {}", customServiceQuotas); + + final FormBody.Builder quotaBuilder = new FormBody.Builder(); + for (CouchbaseService service : enabledServices) { + if (!service.hasQuota()) { + continue; + } + + int quota = customServiceQuotas.getOrDefault(service, service.getMinimumQuotaMb()); + if (CouchbaseService.KV.equals(service)) { + quotaBuilder.add("memoryQuota", Integer.toString(quota)); + } else { + quotaBuilder.add(service.getIdentifier() + "MemoryQuota", Integer.toString(quota)); + } + } + + @Cleanup Response response = doHttpRequest( + MGMT_PORT, "/pools/default", "POST", quotaBuilder.build(), false + ); + + checkSuccessfulResponse(response, "Could not configure service memory quotas"); + } + /** * Configures the admin user on the couchbase node. *

diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseService.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseService.java index 57d3e37fcf2..a314bd5ccfc 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseService.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseService.java @@ -21,38 +21,67 @@ */ public enum CouchbaseService { - /** - * Key-Value service. - */ - KV("kv"), - - /** - * Query (N1QL) service. - */ - QUERY("n1ql"), - - /** - * Search (FTS) service. - */ - SEARCH("fts"), - - /** - * Indexing service (needed if QUERY is also used!). - */ - INDEX("index"), - - /** - * Analytics service. - */ - ANALYTICS("cbas"); - - private final String identifier; - - CouchbaseService(String identifier) { - this.identifier = identifier; - } - - String getIdentifier() { - return identifier; - } + /** + * Key-Value service. + */ + KV("kv", 256), + + /** + * Query (N1QL) service. + *

+ * Note that the query service has no memory quota, so it is set to 0. + */ + QUERY("n1ql", 0), + + /** + * Search (FTS) service. + */ + SEARCH("fts", 256), + + /** + * Indexing service (needed if QUERY is also used!). + */ + INDEX("index", 256), + + /** + * Analytics service. + */ + ANALYTICS("cbas", 1024); + + private final String identifier; + + private final int minimumQuotaMb; + + CouchbaseService(final String identifier, final int minimumQuotaMb) { + this.identifier = identifier; + this.minimumQuotaMb = minimumQuotaMb; + } + + /** + * Returns the internal service identifier. + * + * @return the internal service identifier. + */ + String getIdentifier() { + return identifier; + } + + /** + * Returns the minimum quota for the service in MB. + * + * @return the minimum quota in MB. + */ + int getMinimumQuotaMb() { + return minimumQuotaMb; + } + + /** + * Returns true if the service has a quota that needs to be applied. + * + * @return true if its quota needs to be applied. + */ + boolean hasQuota() { + return minimumQuotaMb > 0; + } + } From f257ba3c1f628e01d6fe9612fd6f2dd29f9be521 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Jan 2022 11:44:24 +0000 Subject: [PATCH 2277/5615] Bump transport from 7.15.2 to 7.16.2 in /modules/elasticsearch Bumps [transport](https://github.com/elastic/elasticsearch) from 7.15.2 to 7.16.2. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.15.2...v7.16.2) --- updated-dependencies: - dependency-name: org.elasticsearch.client:transport dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 7b92dc8279d..3f517353f24 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,6 +3,6 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') testImplementation "org.elasticsearch.client:elasticsearch-rest-client:7.16.2" - testImplementation "org.elasticsearch.client:transport:7.15.2" + testImplementation "org.elasticsearch.client:transport:7.16.2" testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From f8d4f6ad51b7b4aad93b7989de0df0cac4d0700b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Jan 2022 11:44:29 +0000 Subject: [PATCH 2278/5615] Bump logback-classic from 1.2.7 to 1.2.10 in /examples Bumps logback-classic from 1.2.7 to 1.2.10. --- updated-dependencies: - dependency-name: ch.qos.logback:logback-classic dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/disque-job-queue/build.gradle | 2 +- examples/kafka-cluster/build.gradle | 2 +- examples/linked-container/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index 051e82f67d7..ff03cee54cc 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -12,7 +12,7 @@ dependencies { implementation 'biz.paluch.redis:spinach:0.3' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' - testImplementation 'ch.qos.logback:logback-classic:1.2.7' + testImplementation 'ch.qos.logback:logback-classic:1.2.10' testImplementation 'org.mockito:mockito-all:1.10.19' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index 290a4295368..a686fc171c2 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -13,5 +13,5 @@ dependencies { testImplementation 'org.apache.kafka:kafka-clients:3.0.0' testImplementation 'org.assertj:assertj-core:3.21.0' testImplementation 'com.google.guava:guava:23.0' - testImplementation 'ch.qos.logback:logback-classic:1.2.7' + testImplementation 'ch.qos.logback:logback-classic:1.2.10' } diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index dfdc749d191..1d6c29b16eb 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'com.squareup.okhttp3:okhttp:4.9.3' implementation 'org.json:json:20211205' testImplementation 'org.postgresql:postgresql:42.3.1' - testImplementation 'ch.qos.logback:logback-classic:1.2.7' + testImplementation 'ch.qos.logback:logback-classic:1.2.10' testImplementation 'org.testcontainers:postgresql' } diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 7f81bbf4c57..e1b1f5d639a 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -12,7 +12,7 @@ dependencies { implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' - testImplementation 'ch.qos.logback:logback-classic:1.2.7' + testImplementation 'ch.qos.logback:logback-classic:1.2.10' testImplementation 'org.testng:testng:7.4.0' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 5a71ca3d2ff..38b3804b4b8 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -13,6 +13,6 @@ dependencies { implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'junit:junit:4.13.2' - testImplementation 'ch.qos.logback:logback-classic:1.2.7' + testImplementation 'ch.qos.logback:logback-classic:1.2.10' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 35f7483113f..43d74d16244 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -13,7 +13,7 @@ dependencies { implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.32' - testImplementation 'ch.qos.logback:logback-classic:1.2.7' + testImplementation 'ch.qos.logback:logback-classic:1.2.10' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 8685bc3205ea95232f7dfbec67fcafc396b7229b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Jan 2022 11:44:32 +0000 Subject: [PATCH 2279/5615] Bump aws-java-sdk-logs from 1.12.122 to 1.12.132 in /modules/localstack Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.12.122 to 1.12.132. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.122...1.12.132) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-logs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index c0075ad198f..ad93e527043 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,7 +6,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.131' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.122' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.131' - testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.122' + testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.132' testImplementation 'software.amazon.awssdk:s3:2.17.102' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 63a892b357947d4814baa9f6070783003ccdf553 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Jan 2022 11:44:33 +0000 Subject: [PATCH 2280/5615] Bump org.springframework.boot from 2.6.1 to 2.6.2 in /examples Bumps [org.springframework.boot](https://github.com/spring-projects/spring-boot) from 2.6.1 to 2.6.2. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.6.1...v2.6.2) --- updated-dependencies: - dependency-name: org.springframework.boot dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot-kotlin-redis/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index 242cfcd3810..f79edec4998 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.6.1' + id 'org.springframework.boot' version '2.6.2' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index e0028d26b4a..767d6922863 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,5 +1,5 @@ plugins { - id("org.springframework.boot") version "2.6.1" + id("org.springframework.boot") version "2.6.2" id("org.jetbrains.kotlin.jvm") version "1.6.10" id("org.jetbrains.kotlin.plugin.spring") version "1.6.0" } diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index b5ddd11cc84..782f03b561e 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.6.1' + id 'org.springframework.boot' version '2.6.2' } apply plugin: 'io.spring.dependency-management' From 0e8faedaa234d3cefa5af96fa8e20ba5be3a5a79 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Jan 2022 11:44:33 +0000 Subject: [PATCH 2281/5615] Bump org.jetbrains.kotlin.plugin.spring in /examples Bumps org.jetbrains.kotlin.plugin.spring from 1.6.0 to 1.6.10. --- updated-dependencies: - dependency-name: org.jetbrains.kotlin.plugin.spring dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index e0028d26b4a..fa951bd3415 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,7 +1,7 @@ plugins { id("org.springframework.boot") version "2.6.1" id("org.jetbrains.kotlin.jvm") version "1.6.10" - id("org.jetbrains.kotlin.plugin.spring") version "1.6.0" + id("org.jetbrains.kotlin.plugin.spring") version "1.6.10" } apply plugin: 'io.spring.dependency-management' From e597f4ab445fad71130e535528d7ef4782c0f38e Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Tue, 4 Jan 2022 18:38:13 +0100 Subject: [PATCH 2282/5615] Rollback jedis to 3.0.1 (#4852) --- modules/toxiproxy/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/toxiproxy/build.gradle b/modules/toxiproxy/build.gradle index e055f5a52c5..cad4b6b71f3 100644 --- a/modules/toxiproxy/build.gradle +++ b/modules/toxiproxy/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':testcontainers') api 'eu.rekawek.toxiproxy:toxiproxy-java:2.1.5' - testImplementation 'redis.clients:jedis:4.0.1' + testImplementation 'redis.clients:jedis:3.0.1' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' testImplementation project(':test-support') From 3c30a267d1bb678dba13a17c5ccfd53eef4824e7 Mon Sep 17 00:00:00 2001 From: vincz7777 <96777535+vincz7777@users.noreply.github.com> Date: Wed, 5 Jan 2022 11:24:37 +0100 Subject: [PATCH 2283/5615] Display Ryuk logs when it fails to start (#4842) Co-authored-by: Sergei Egorov Co-authored-by: Vincent MIJOULE (contractor) Co-authored-by: Richard North --- .../utility/ResourceReaper.java | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index 8c1f28378ef..f43835fd01b 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -34,7 +34,6 @@ import java.net.Socket; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; -import java.time.Duration; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Collections; @@ -133,10 +132,10 @@ public void onNext(Frame frame) { } }); - ContainerState containerState = new ContainerState() { - + InspectContainerResponse inspectedContainer; + try { // inspect container response might initially not contain the mapped port - final InspectContainerResponse inspectedContainer = await() + inspectedContainer = await() .atMost(5, TimeUnit.SECONDS) .pollInterval(DynamicPollInterval.ofMillis(50)) .pollInSameThread() @@ -144,14 +143,20 @@ public void onNext(Frame frame) { () -> client.inspectContainerCmd(ryukContainerId).exec(), inspectContainerResponse -> { return inspectContainerResponse - .getNetworkSettings() - .getPorts() - .getBindings() - .values() - .stream() - .anyMatch(Objects::nonNull); + .getNetworkSettings() + .getPorts() + .getBindings() + .values() + .stream() + .anyMatch(Objects::nonNull); } ); + } catch (Exception e) { + log.warn("Ryuk container cannot be inspected and probably had a problem starting. Ryuk's logs:\n{}", ryukLog); + throw new IllegalStateException("Ryuk failed to start", e); + } + + ContainerState containerState = new ContainerState() { @Override public List getExposedPorts() { From 4fe304ab5128942df4cea708ed859f2e79b2b649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Prinet?= Date: Wed, 5 Jan 2022 11:54:19 +0100 Subject: [PATCH 2284/5615] Add Gradle Enterprise badge in README (#4850) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9bcf6f002f8..b1c0014c236 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Thundra Foresight](https://thundra-assets-prod.s3.us-west-2.amazonaws.com/images/badges/thundra-foresight-badge-enabled.svg)](https://foresight.thundra.live/testRuns/5a49cc30-f063-11eb-9a03-0242ac130003) [![Thundra Foresight Test Results](https://foresight.service.thundra.us/public/api/v1/badge/test/5a49cc30-f063-11eb-9a03-0242ac130003)](https://foresight.thundra.live/testRuns/5a49cc30-f063-11eb-9a03-0242ac130003) +[![Revved up by Gradle Enterprise](https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.testcontainers.org/scans) > Testcontainers is a Java library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container. From ac020bc7ae265ac6f7268c8ae3a054aaf1d84626 Mon Sep 17 00:00:00 2001 From: Piotr Kubowicz Date: Wed, 5 Jan 2022 11:54:44 +0100 Subject: [PATCH 2285/5615] Stop logging about starting when reusing (#4844) --- .../java/org/testcontainers/containers/GenericContainer.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index c2387cf5977..69d40a6821b 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -421,12 +421,10 @@ private void tryStart(Instant startedAt) { if (!reused) { containerIsCreated(containerId); - logger().info("Starting container with ID: {}", containerId); + logger().info("Container {} is starting: {}", dockerImageName, containerId); dockerClient.startContainerCmd(containerId).exec(); } - logger().info("Container {} is starting: {}", dockerImageName, containerId); - // For all registered output consumers, start following as close to container startup as possible this.logConsumers.forEach(this::followOutput); From e42a37d615d48cdc60181b4ab634c555898bf988 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Wed, 5 Jan 2022 14:46:26 +0100 Subject: [PATCH 2286/5615] Reinstate usage of `READ_ONLY_REMOTE_GRADLE_CACHE` to avoid wrong cache hits (#4853) --- settings.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/settings.gradle b/settings.gradle index db8f11f14c0..75359bcbf7d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -42,10 +42,10 @@ buildCache { local { enabled = !isCI } - remote(HttpBuildCache) { - push = isCI + remote(HttpBuildCache) { + push = isCI && !System.getenv("READ_ONLY_REMOTE_GRADLE_CACHE") enabled = true - url = 'https://ge.testcontainers.org/cache/' + url = 'https://ge.testcontainers.org/cache/' credentials { username = 'ci' password = System.getenv("GRADLE_ENTERPRISE_CACHE_PASSWORD") From e70782a01820a12e99b33e1ec701afd107421598 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Wed, 5 Jan 2022 18:48:32 +0100 Subject: [PATCH 2287/5615] Increase AzureCI timeout to 120 minutes instead of using the default of 60 minutes (#4854) --- azure-pipelines.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 91e33f202a9..53dabc6722b 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,6 +1,7 @@ jobs: - job: azure_pipeline_tests + timeoutInMinutes: 120 steps: # Run all core tests when running the Windows CI tests From ba3748e3edb51c01f896fa930daadc8eec22e65f Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Wed, 5 Jan 2022 19:12:22 +0100 Subject: [PATCH 2288/5615] Add GE remote cache to examples project (#4855) Als add GE build scans for examples project --- examples/settings.gradle | 36 ++++++++++++++++++++++-------------- settings.gradle | 8 ++------ 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/examples/settings.gradle b/examples/settings.gradle index 22b279b1573..8641c6134b1 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -7,11 +7,12 @@ buildscript { dependencies { classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.10.0" classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.8" + classpath "com.gradle:common-custom-user-data-gradle-plugin:1.6.2" } } -apply plugin: 'ch.myniva.s3-build-cache' apply plugin: 'com.gradle.enterprise' +apply plugin: "com.gradle.common-custom-user-data-gradle-plugin" rootProject.name = 'testcontainers-examples' @@ -32,23 +33,30 @@ include 'cucumber' include 'spring-boot-kotlin-redis' include 'spock' -ext.isMasterBuild = false || - (System.getenv("GITHUB_REF") == "refs/heads/master") || - (System.getenv("BUILD_SOURCEBRANCHNAME") == "master") +ext.isCI = System.getenv("CI") != null buildCache { local { - enabled = !isMasterBuild + enabled = !isCI } + remote(HttpBuildCache) { + push = isCI && !System.getenv("READ_ONLY_REMOTE_GRADLE_CACHE") + enabled = true + url = 'https://ge.testcontainers.org/cache/' + credentials { + username = 'ci' + password = System.getenv("GRADLE_ENTERPRISE_CACHE_PASSWORD") + } + } +} - remote(ch.myniva.gradle.caching.s3.AwsS3BuildCache) { - endpoint = 'fra1.digitaloceanspaces.com' - region = 'fra1' - bucket = 'testcontainers' - path = 'cache' - push = isMasterBuild && !System.getenv("READ_ONLY_REMOTE_GRADLE_CACHE") - headers = [ - 'x-amz-acl': 'public-read' - ] +gradleEnterprise { + buildScan { + server = "https://ge.testcontainers.org/" + publishAlways() + publishIfAuthenticated() + uploadInBackground = !isCI + captureTaskInputFiles = true } + } diff --git a/settings.gradle b/settings.gradle index 75359bcbf7d..57f18b531b1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,8 +5,8 @@ buildscript { } } dependencies { - classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.7.2" - classpath "com.gradle:common-custom-user-data-gradle-plugin:1.6.1" + classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.8" + classpath "com.gradle:common-custom-user-data-gradle-plugin:1.6.2" } } @@ -32,10 +32,6 @@ include ':docs:examples:spock:redis' include 'test-support' -ext.isMasterBuild = false || - (System.getenv("GITHUB_REF") == "refs/heads/master") || - (System.getenv("BUILD_SOURCEBRANCHNAME") == "master") - ext.isCI = System.getenv("CI") != null buildCache { From e6caada4f902c171e6db0dc606b4cc2b8ca2fe4e Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 6 Jan 2022 15:49:23 +0000 Subject: [PATCH 2289/5615] Tweak logging around reusable mode (#4858) --- .../java/org/testcontainers/containers/GenericContainer.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 69d40a6821b..3851cf42fca 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -423,6 +423,8 @@ private void tryStart(Instant startedAt) { logger().info("Container {} is starting: {}", dockerImageName, containerId); dockerClient.startContainerCmd(containerId).exec(); + } else { + logger().info("Reusing existing container ({}) and not creating a new one", containerId); } // For all registered output consumers, start following as close to container startup as possible From 74bea3a37cf259c4b8a1b40da07e06328e4a13ad Mon Sep 17 00:00:00 2001 From: Dan Dobrin <20207377+ddobrin@users.noreply.github.com> Date: Tue, 11 Jan 2022 05:29:57 -0500 Subject: [PATCH 2290/5615] Update gCloud dependencies (#4857) Fixes #4856 --- modules/gcloud/build.gradle | 12 ++++++------ .../containers/BigtableEmulatorContainerTest.java | 2 +- .../containers/DatastoreEmulatorContainerTest.java | 2 +- .../containers/FirestoreEmulatorContainerTest.java | 2 +- .../containers/PubSubEmulatorContainerTest.java | 2 +- .../containers/SpannerEmulatorContainerTest.java | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 11e68824c34..d20a9edbed0 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -3,10 +3,10 @@ description = "Testcontainers :: GCloud" dependencies { api project(':testcontainers') - testImplementation 'com.google.cloud:google-cloud-datastore:1.102.4' - testImplementation 'com.google.cloud:google-cloud-firestore:1.33.0' - testImplementation 'com.google.cloud:google-cloud-pubsub:1.105.0' - testImplementation 'com.google.cloud:google-cloud-spanner:1.50.0' - testImplementation 'com.google.cloud:google-cloud-bigtable:1.17.3' - testImplementation 'org.assertj:assertj-core:3.15.0' + testImplementation 'com.google.cloud:google-cloud-datastore:2.2.1' + testImplementation 'com.google.cloud:google-cloud-firestore:3.0.9' + testImplementation 'com.google.cloud:google-cloud-pubsub:1.115.0' + testImplementation 'com.google.cloud:google-cloud-spanner:6.17.3' + testImplementation 'com.google.cloud:google-cloud-bigtable:2.4.0' + testImplementation 'org.assertj:assertj-core:3.22.0' } diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/BigtableEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/BigtableEmulatorContainerTest.java index 0cc44afcc77..4ba59a75488 100644 --- a/modules/gcloud/src/test/java/org/testcontainers/containers/BigtableEmulatorContainerTest.java +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/BigtableEmulatorContainerTest.java @@ -43,7 +43,7 @@ public class BigtableEmulatorContainerTest { @Rule // emulatorContainer { public BigtableEmulatorContainer emulator = new BigtableEmulatorContainer( - DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:316.0.0-emulators") + DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:367.0.0-emulators") ); // } diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java index f7b35936197..ba998e402ce 100644 --- a/modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java @@ -17,7 +17,7 @@ public class DatastoreEmulatorContainerTest { @Rule // creatingDatastoreEmulatorContainer { public DatastoreEmulatorContainer emulator = new DatastoreEmulatorContainer( - DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:316.0.0-emulators") + DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:367.0.0-emulators") ); // } diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java index 138ff946f6f..04939ecc30e 100644 --- a/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java @@ -22,7 +22,7 @@ public class FirestoreEmulatorContainerTest { @Rule // emulatorContainer { public FirestoreEmulatorContainer emulator = new FirestoreEmulatorContainer( - DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:316.0.0-emulators") + DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:367.0.0-emulators") ); // } diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java index 2e692956418..8db49eb3649 100644 --- a/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java @@ -35,7 +35,7 @@ public class PubSubEmulatorContainerTest { @Rule // emulatorContainer { public PubSubEmulatorContainer emulator = new PubSubEmulatorContainer( - DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:316.0.0-emulators") + DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:367.0.0-emulators") ); // } diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java index f801ca5b547..64b81546499 100644 --- a/modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java @@ -27,7 +27,7 @@ public class SpannerEmulatorContainerTest { @Rule // emulatorContainer { public SpannerEmulatorContainer emulator = new SpannerEmulatorContainer( - DockerImageName.parse("gcr.io/cloud-spanner-emulator/emulator:1.1.0") + DockerImageName.parse("gcr.io/cloud-spanner-emulator/emulator:1.4.0") ); // } From d0c4ced72ddca93838c1107b66913b5141872906 Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 11 Jan 2022 11:21:06 +0000 Subject: [PATCH 2291/5615] Update dependabot rules for new modules (#4864) --- .github/dependabot.yml | 60 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 4caa6509af2..355d44f2bb4 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -7,6 +7,11 @@ updates: open-pull-requests-limit: 10 # Explicit entry for each module + - package-ecosystem: "gradle" + directory: "/modules/azure" + schedule: + interval: "monthly" + open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/cassandra" schedule: @@ -17,6 +22,11 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/cockroachdb" + schedule: + interval: "monthly" + open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/couchbase" schedule: @@ -27,6 +37,11 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/db2" + schedule: + interval: "monthly" + open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/dynalite" schedule: @@ -37,6 +52,11 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/gcloud" + schedule: + interval: "monthly" + open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/influxdb" schedule: @@ -57,6 +77,11 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/k3s" + schedule: + interval: "monthly" + open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/kafka" schedule: @@ -77,6 +102,11 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/mongodb" + schedule: + interval: "monthly" + open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/mssqlserver" schedule: @@ -102,21 +132,46 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/orientdb" + schedule: + interval: "monthly" + open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/postgresql" schedule: interval: "monthly" open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/presto" + schedule: + interval: "monthly" + open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/pulsar" schedule: interval: "monthly" open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/r2dbc" + schedule: + interval: "monthly" + open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/rabbitmq" + schedule: + interval: "monthly" + open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/selenium" schedule: interval: "monthly" open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/solr" + schedule: + interval: "monthly" + open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/spock" schedule: @@ -127,6 +182,11 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/trino" + schedule: + interval: "monthly" + open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/vault" schedule: From e681a4f854acde4a7e54fd2c6745714537eaeaa6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:21:30 +0000 Subject: [PATCH 2292/5615] Bump assertj-core from 3.21.0 to 3.22.0 in /modules/jdbc Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.21.0 to 3.22.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.21.0...assertj-core-3.22.0) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 973bc9b01df..a6f71690e1e 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -11,5 +11,5 @@ dependencies { testImplementation 'com.zaxxer:HikariCP-java6:2.3.13' testImplementation 'com.googlecode.junit-toolbox:junit-toolbox:2.4' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' - testImplementation 'org.assertj:assertj-core:3.21.0' + testImplementation 'org.assertj:assertj-core:3.22.0' } From 8fdae9c304bdfe3bf12ee88fdbc42a220f40ea9b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:21:30 +0000 Subject: [PATCH 2293/5615] Bump jcc from 11.5.0.0 to 11.5.7.0 in /modules/db2 Bumps jcc from 11.5.0.0 to 11.5.7.0. --- updated-dependencies: - dependency-name: com.ibm.db2:jcc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/db2/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/db2/build.gradle b/modules/db2/build.gradle index 9d5d8a61ab0..0c8cd323779 100644 --- a/modules/db2/build.gradle +++ b/modules/db2/build.gradle @@ -4,5 +4,5 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'com.ibm.db2:jcc:11.5.0.0' + testImplementation 'com.ibm.db2:jcc:11.5.7.0' } From 954529be0e1b6b8cc81895eac8d67c4358da49cc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:21:31 +0000 Subject: [PATCH 2294/5615] Bump assertj-core from 3.21.0 to 3.22.0 in /modules/database-commons Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.21.0 to 3.22.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.21.0...assertj-core-3.22.0) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/database-commons/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/database-commons/build.gradle b/modules/database-commons/build.gradle index 73735b0d03d..7a201d772fb 100644 --- a/modules/database-commons/build.gradle +++ b/modules/database-commons/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: Database-Commons" dependencies { api project(':testcontainers') - testImplementation 'org.assertj:assertj-core:3.21.0' + testImplementation 'org.assertj:assertj-core:3.22.0' } From d0a0f21ef89bdf03fb7667fee16aba89c5108559 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:21:31 +0000 Subject: [PATCH 2295/5615] Bump postgresql from 42.2.12 to 42.3.1 in /modules/cockroachdb Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.12 to 42.3.1. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.12...REL42.3.1) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/cockroachdb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cockroachdb/build.gradle b/modules/cockroachdb/build.gradle index 5222208fae0..ee6a7e2731e 100644 --- a/modules/cockroachdb/build.gradle +++ b/modules/cockroachdb/build.gradle @@ -4,5 +4,5 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'org.postgresql:postgresql:42.2.12' + testImplementation 'org.postgresql:postgresql:42.3.1' } From 78754f8ce6fb19054d5a43d46a513619d79b1ad4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:21:32 +0000 Subject: [PATCH 2296/5615] Bump aws-java-sdk-dynamodb in /modules/dynalite Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.12.131 to 1.12.137. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.131...1.12.137) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-dynamodb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/dynalite/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index d891ed5b4d9..52881514ac5 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Dynalite" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.131' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.137' testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.131' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From 0c6854077d2e550a21ef023bb8b84930c674e824 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:21:32 +0000 Subject: [PATCH 2297/5615] Bump assertj-core from 3.21.0 to 3.22.0 in /modules/junit-jupiter Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.21.0 to 3.22.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.21.0...assertj-core-3.22.0) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 8344db5aebb..77ff3f842d0 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -12,7 +12,7 @@ dependencies { testImplementation ('org.mockito:mockito-core:4.2.0') { exclude(module: 'hamcrest-core') } - testImplementation 'org.assertj:assertj-core:3.21.0' + testImplementation 'org.assertj:assertj-core:3.22.0' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2' testRuntimeOnly 'org.postgresql:postgresql:42.3.1' From 762251dd30f675e0bd4031d5d9bfe15f5232a181 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:21:34 +0000 Subject: [PATCH 2298/5615] Bump assertj-core from 3.21.0 to 3.22.0 in /modules/kafka Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.21.0 to 3.22.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.21.0...assertj-core-3.22.0) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index b37105f7463..b9157d9d539 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -13,6 +13,6 @@ dependencies { api project(':testcontainers') testImplementation 'org.apache.kafka:kafka-clients:3.0.0' - testImplementation 'org.assertj:assertj-core:3.21.0' + testImplementation 'org.assertj:assertj-core:3.22.0' testImplementation 'com.google.guava:guava:23.0' } From f3fed54fe55d672af31806c6327051a26d297c55 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:21:35 +0000 Subject: [PATCH 2299/5615] Bump assertj-core from 3.21.0 to 3.22.0 in /core Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.21.0 to 3.22.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.21.0...assertj-core-3.22.0) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index 3ca76eab47a..074273e8fc7 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -100,13 +100,13 @@ dependencies { testImplementation files('testlib/repo/fakejar/fakejar/0/fakejar-0.jar') testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' - testImplementation 'org.assertj:assertj-core:3.21.0' + testImplementation 'org.assertj:assertj-core:3.22.0' testImplementation project(':test-support') jarFileTestCompileOnly "org.projectlombok:lombok:${lombok.version}" jarFileTestAnnotationProcessor "org.projectlombok:lombok:${lombok.version}" jarFileTestImplementation 'junit:junit:4.13.2' - jarFileTestImplementation 'org.assertj:assertj-core:3.20.2' + jarFileTestImplementation 'org.assertj:assertj-core:3.22.0' jarFileTestImplementation 'org.ow2.asm:asm-debug-all:5.2' } From 28ada89db26aea589876a514f84b9d7807d47d07 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:21:35 +0000 Subject: [PATCH 2300/5615] Bump google-cloud-firestore from 3.0.9 to 3.0.10 in /modules/gcloud Bumps [google-cloud-firestore](https://github.com/googleapis/java-firestore) from 3.0.9 to 3.0.10. - [Release notes](https://github.com/googleapis/java-firestore/releases) - [Changelog](https://github.com/googleapis/java-firestore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-firestore/compare/v3.0.9...v3.0.10) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-firestore dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index d20a9edbed0..f1e473b8c35 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':testcontainers') testImplementation 'com.google.cloud:google-cloud-datastore:2.2.1' - testImplementation 'com.google.cloud:google-cloud-firestore:3.0.9' + testImplementation 'com.google.cloud:google-cloud-firestore:3.0.10' testImplementation 'com.google.cloud:google-cloud-pubsub:1.115.0' testImplementation 'com.google.cloud:google-cloud-spanner:6.17.3' testImplementation 'com.google.cloud:google-cloud-bigtable:2.4.0' From 18234abf497226469d9cc450b378cc40dc87be1b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:21:38 +0000 Subject: [PATCH 2301/5615] Bump azure-cosmos from 4.16.0 to 4.24.0 in /modules/azure Bumps [azure-cosmos](https://github.com/Azure/azure-sdk-for-java) from 4.16.0 to 4.24.0. - [Release notes](https://github.com/Azure/azure-sdk-for-java/releases) - [Commits](https://github.com/Azure/azure-sdk-for-java/compare/azure-cosmos_4.16.0...azure-cosmos_4.24.0) --- updated-dependencies: - dependency-name: com.azure:azure-cosmos dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/azure/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle index 19d9b3458fd..d3a902cf728 100644 --- a/modules/azure/build.gradle +++ b/modules/azure/build.gradle @@ -4,5 +4,5 @@ dependencies { api project(':testcontainers') testImplementation 'org.assertj:assertj-core:3.15.0' - testImplementation 'com.azure:azure-cosmos:4.16.0' + testImplementation 'com.azure:azure-cosmos:4.24.0' } From b5f534b09d971d58bda6da47063e524e6ec2faac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:21:38 +0000 Subject: [PATCH 2302/5615] Bump assertj-core from 3.21.0 to 3.22.0 in /modules/neo4j Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.21.0 to 3.22.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.21.0...assertj-core-3.22.0) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index cdc727921b4..fe99774538c 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -34,5 +34,5 @@ dependencies { api project(":testcontainers") testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.2' - testImplementation 'org.assertj:assertj-core:3.21.0' + testImplementation 'org.assertj:assertj-core:3.22.0' } From 6a7f32ba11e80a9fa5e2343d758f24a590cc61ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:21:39 +0000 Subject: [PATCH 2303/5615] Bump pulsar-client-admin from 2.7.3 to 2.7.4 in /modules/pulsar Bumps [pulsar-client-admin](https://github.com/apache/pulsar) from 2.7.3 to 2.7.4. - [Release notes](https://github.com/apache/pulsar/releases) - [Commits](https://github.com/apache/pulsar/compare/v2.7.3...v2.7.4) --- updated-dependencies: - dependency-name: org.apache.pulsar:pulsar-client-admin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 8bdabcd9dc3..7ad057e2e91 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -5,5 +5,5 @@ dependencies { testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.7.3' testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.21.0' - testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.7.3' + testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.7.4' } From 6604418e5a956a5eccde8f6609792fe8eecf3576 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:21:39 +0000 Subject: [PATCH 2304/5615] Bump reactor-core from 3.3.4.RELEASE to 3.4.14 in /modules/r2dbc Bumps [reactor-core](https://github.com/reactor/reactor-core) from 3.3.4.RELEASE to 3.4.14. - [Release notes](https://github.com/reactor/reactor-core/releases) - [Commits](https://github.com/reactor/reactor-core/compare/v3.3.4.RELEASE...v3.4.14) --- updated-dependencies: - dependency-name: io.projectreactor:reactor-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/r2dbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle index 21d3c1894e4..232714886dc 100644 --- a/modules/r2dbc/build.gradle +++ b/modules/r2dbc/build.gradle @@ -15,6 +15,6 @@ dependencies { testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.1.RELEASE' testImplementation project(':postgresql') - testFixturesImplementation 'io.projectreactor:reactor-core:3.3.4.RELEASE' + testFixturesImplementation 'io.projectreactor:reactor-core:3.4.14' testFixturesImplementation 'org.assertj:assertj-core:3.14.0' } From ee75985196ec9a483c905724d1bb8411b48ce941 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:21:40 +0000 Subject: [PATCH 2305/5615] Bump trino-jdbc from 352 to 367 in /modules/trino Bumps trino-jdbc from 352 to 367. --- updated-dependencies: - dependency-name: io.trino:trino-jdbc dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/trino/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/trino/build.gradle b/modules/trino/build.gradle index 5a3b733934f..fb154ba7266 100644 --- a/modules/trino/build.gradle +++ b/modules/trino/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'io.trino:trino-jdbc:352' + testImplementation 'io.trino:trino-jdbc:367' compileOnly 'org.jetbrains:annotations:20.0.0' } From 0ece0ce0851b72e8c5713611ab045f65e9cc0ec4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:21:41 +0000 Subject: [PATCH 2306/5615] Bump presto-jdbc from 331 to 350 in /modules/presto Bumps presto-jdbc from 331 to 350. --- updated-dependencies: - dependency-name: io.prestosql:presto-jdbc dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/presto/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/presto/build.gradle b/modules/presto/build.gradle index 1db76199969..2b6955efd8a 100644 --- a/modules/presto/build.gradle +++ b/modules/presto/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'io.prestosql:presto-jdbc:331' + testImplementation 'io.prestosql:presto-jdbc:350' compileOnly 'org.jetbrains:annotations:20.0.0' } From 2d83ea0bb119307d4db5dc764a1e3c66a54c0cb6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:21:41 +0000 Subject: [PATCH 2307/5615] Bump mongodb-driver-sync from 4.0.2 to 4.4.1 in /modules/mongodb Bumps [mongodb-driver-sync](https://github.com/mongodb/mongo-java-driver) from 4.0.2 to 4.4.1. - [Release notes](https://github.com/mongodb/mongo-java-driver/releases) - [Commits](https://github.com/mongodb/mongo-java-driver/compare/r4.0.2...r4.4.1) --- updated-dependencies: - dependency-name: org.mongodb:mongodb-driver-sync dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/mongodb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mongodb/build.gradle b/modules/mongodb/build.gradle index e332badb68b..95919c931d4 100644 --- a/modules/mongodb/build.gradle +++ b/modules/mongodb/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: MongoDB" dependencies { api project(':testcontainers') - testImplementation("org.mongodb:mongodb-driver-sync:4.0.2") + testImplementation("org.mongodb:mongodb-driver-sync:4.4.1") } From 0df60341337267cf40e376d18a94997ee7dba682 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:21:43 +0000 Subject: [PATCH 2308/5615] Bump assertj-core from 3.12.0 to 3.22.0 in /modules/orientdb Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.12.0 to 3.22.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.12.0...assertj-core-3.22.0) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/orientdb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/orientdb/build.gradle b/modules/orientdb/build.gradle index 5be59938603..820e96e114f 100644 --- a/modules/orientdb/build.gradle +++ b/modules/orientdb/build.gradle @@ -5,7 +5,7 @@ dependencies { api "com.orientechnologies:orientdb-client:3.0.24" - testImplementation 'org.assertj:assertj-core:3.12.0' + testImplementation 'org.assertj:assertj-core:3.22.0' testImplementation 'org.apache.tinkerpop:gremlin-driver:3.3.4' testImplementation "com.orientechnologies:orientdb-gremlin:3.0.18" } From e89b2e650259be9e3ec89c989505c44e3cd25448 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:21:45 +0000 Subject: [PATCH 2309/5615] Bump assertj-core from 3.14.0 to 3.22.0 in /modules/r2dbc Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.14.0 to 3.22.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.14.0...assertj-core-3.22.0) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/r2dbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle index 21d3c1894e4..94867299969 100644 --- a/modules/r2dbc/build.gradle +++ b/modules/r2dbc/build.gradle @@ -11,7 +11,7 @@ dependencies { api project(':testcontainers') api 'io.r2dbc:r2dbc-spi:0.8.1.RELEASE' - testImplementation 'org.assertj:assertj-core:3.14.0' + testImplementation 'org.assertj:assertj-core:3.22.0' testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.1.RELEASE' testImplementation project(':postgresql') From 88cbf80101deaec9a4593c5e42b5d578a2554888 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:21:45 +0000 Subject: [PATCH 2310/5615] Bump assertj-core from 3.12.2 to 3.22.0 in /modules/rabbitmq Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.12.2 to 3.22.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.12.2...assertj-core-3.22.0) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/rabbitmq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/rabbitmq/build.gradle b/modules/rabbitmq/build.gradle index 13638b51b97..de778979f0f 100644 --- a/modules/rabbitmq/build.gradle +++ b/modules/rabbitmq/build.gradle @@ -3,6 +3,6 @@ description = "TestContainers :: RabbitMQ" dependencies { api project(":testcontainers") testImplementation 'com.rabbitmq:amqp-client:5.7.0' - testImplementation 'org.assertj:assertj-core:3.12.2' + testImplementation 'org.assertj:assertj-core:3.22.0' compileOnly 'org.jetbrains:annotations:20.0.0' } From 6db0f46bc762b1d25cce6a0733c3934aa001f416 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:21:46 +0000 Subject: [PATCH 2311/5615] Bump testng from 7.4.0 to 7.5 in /examples Bumps [testng](https://github.com/cbeust/testng) from 7.4.0 to 7.5. - [Release notes](https://github.com/cbeust/testng/releases) - [Changelog](https://github.com/cbeust/testng/blob/master/CHANGES.txt) - [Commits](https://github.com/cbeust/testng/compare/7.4.0...7.5) --- updated-dependencies: - dependency-name: org.testng:testng dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/redis-backed-cache-testng/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index e1b1f5d639a..2046da278f2 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -13,7 +13,7 @@ dependencies { implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'ch.qos.logback:logback-classic:1.2.10' - testImplementation 'org.testng:testng:7.4.0' + testImplementation 'org.testng:testng:7.5' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 8f545a1362d3d9cefdc9b1884f20ce2c2897ae7d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:21:46 +0000 Subject: [PATCH 2312/5615] Bump google-cloud-spanner from 6.17.3 to 6.17.4 in /modules/gcloud Bumps [google-cloud-spanner](https://github.com/googleapis/java-spanner) from 6.17.3 to 6.17.4. - [Release notes](https://github.com/googleapis/java-spanner/releases) - [Changelog](https://github.com/googleapis/java-spanner/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-spanner/compare/v6.17.3...v6.17.4) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-spanner dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index d20a9edbed0..b9828fb1871 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -6,7 +6,7 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-datastore:2.2.1' testImplementation 'com.google.cloud:google-cloud-firestore:3.0.9' testImplementation 'com.google.cloud:google-cloud-pubsub:1.115.0' - testImplementation 'com.google.cloud:google-cloud-spanner:6.17.3' + testImplementation 'com.google.cloud:google-cloud-spanner:6.17.4' testImplementation 'com.google.cloud:google-cloud-bigtable:2.4.0' testImplementation 'org.assertj:assertj-core:3.22.0' } From 9e755a161546946d823d6be3bdbf0e5b565be387 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:21:46 +0000 Subject: [PATCH 2313/5615] Bump pulsar-client from 2.7.3 to 2.7.4 in /modules/pulsar Bumps [pulsar-client](https://github.com/apache/pulsar) from 2.7.3 to 2.7.4. - [Release notes](https://github.com/apache/pulsar/releases) - [Commits](https://github.com/apache/pulsar/compare/v2.7.3...v2.7.4) --- updated-dependencies: - dependency-name: org.apache.pulsar:pulsar-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 8bdabcd9dc3..cb03e560730 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Pulsar" dependencies { api project(':testcontainers') - testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.7.3' + testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.7.4' testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.21.0' testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.7.3' } From 1bcccbcd8a8f3afb8346f987ca5004b3e16ef09e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:21:48 +0000 Subject: [PATCH 2314/5615] Bump aws-java-sdk-s3 from 1.12.131 to 1.12.137 in /modules/localstack Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.131 to 1.12.137. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.131...1.12.137) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index ad93e527043..900fbfed541 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Localstack" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.131' - testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.122' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.137' + testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.137' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.131' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.132' testImplementation 'software.amazon.awssdk:s3:2.17.102' From 32fbe075ac3f1e63daa2d3ad34c6eb57d3f134a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:23:00 +0000 Subject: [PATCH 2315/5615] Bump solr-solrj from 8.3.0 to 8.11.1 in /modules/solr Bumps solr-solrj from 8.3.0 to 8.11.1. --- updated-dependencies: - dependency-name: org.apache.solr:solr-solrj dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/solr/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/solr/build.gradle b/modules/solr/build.gradle index 76535a2b7c8..b3dfc9b3f92 100644 --- a/modules/solr/build.gradle +++ b/modules/solr/build.gradle @@ -2,6 +2,6 @@ description = "Testcontainers :: Solr" dependencies { api project(':testcontainers') - testImplementation 'org.apache.solr:solr-solrj:8.3.0' + testImplementation 'org.apache.solr:solr-solrj:8.11.1' } From 65b73bbd348bf9a2a61eea68cb599d1da4c37c33 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:23:29 +0000 Subject: [PATCH 2316/5615] Bump s3 from 2.17.102 to 2.17.108 in /modules/localstack Bumps s3 from 2.17.102 to 2.17.108. --- updated-dependencies: - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index ad93e527043..4e5371b7bc6 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.122' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.131' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.132' - testImplementation 'software.amazon.awssdk:s3:2.17.102' + testImplementation 'software.amazon.awssdk:s3:2.17.108' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From a282f4185a00b8cd516d5dad32cbccd9e4b2d009 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:23:53 +0000 Subject: [PATCH 2317/5615] Bump auto-service from 1.0-rc6 to 1.0.1 in /modules/r2dbc Bumps [auto-service](https://github.com/google/auto) from 1.0-rc6 to 1.0.1. - [Release notes](https://github.com/google/auto/releases) - [Commits](https://github.com/google/auto/commits/auto-common-1.0.1) --- updated-dependencies: - dependency-name: com.google.auto.service:auto-service dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- modules/r2dbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle index 21d3c1894e4..e7011b1d32c 100644 --- a/modules/r2dbc/build.gradle +++ b/modules/r2dbc/build.gradle @@ -5,7 +5,7 @@ plugins { description = "Testcontainers :: R2DBC" dependencies { - annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6' + annotationProcessor 'com.google.auto.service:auto-service:1.0.1' compileOnly 'com.google.auto.service:auto-service:1.0-rc6' api project(':testcontainers') From 510a97507f242f6a23bad1b757c55d02c0b6f8f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:24:46 +0000 Subject: [PATCH 2318/5615] Bump cucumber-junit from 7.1.0 to 7.2.2 in /examples Bumps [cucumber-junit](https://github.com/cucumber/cucumber-jvm) from 7.1.0 to 7.2.2. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.1.0...v7.2.2) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-junit dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index edb62670134..3f37cbf45e4 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' testImplementation 'io.cucumber:cucumber-java:7.1.0' - testImplementation 'io.cucumber:cucumber-junit:7.1.0' + testImplementation 'io.cucumber:cucumber-junit:7.2.2' testImplementation 'org.testcontainers:selenium' } From 9cc0b82b5df8ff5f63db5435b784de5205c61598 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jan 2022 09:18:23 +0000 Subject: [PATCH 2319/5615] Bump assertj-core from 3.15.0 to 3.22.0 in /modules/azure Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.15.0 to 3.22.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.15.0...assertj-core-3.22.0) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/azure/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle index d3a902cf728..1dbad6a6698 100644 --- a/modules/azure/build.gradle +++ b/modules/azure/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Azure" dependencies { api project(':testcontainers') - testImplementation 'org.assertj:assertj-core:3.15.0' + testImplementation 'org.assertj:assertj-core:3.22.0' testImplementation 'com.azure:azure-cosmos:4.24.0' } From 9cefa10a9c83bfebcb6ae95bd065396fc9190327 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jan 2022 09:18:30 +0000 Subject: [PATCH 2320/5615] Bump google-cloud-datastore from 2.2.1 to 2.2.2 in /modules/gcloud Bumps [google-cloud-datastore](https://github.com/googleapis/java-datastore) from 2.2.1 to 2.2.2. - [Release notes](https://github.com/googleapis/java-datastore/releases) - [Changelog](https://github.com/googleapis/java-datastore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-datastore/compare/v2.2.1...v2.2.2) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-datastore dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 72041c6d14b..ab2966c3051 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: GCloud" dependencies { api project(':testcontainers') - testImplementation 'com.google.cloud:google-cloud-datastore:2.2.1' + testImplementation 'com.google.cloud:google-cloud-datastore:2.2.2' testImplementation 'com.google.cloud:google-cloud-firestore:3.0.10' testImplementation 'com.google.cloud:google-cloud-pubsub:1.115.0' testImplementation 'com.google.cloud:google-cloud-spanner:6.17.4' From 740479210f0fe0a7a7cfc6eb35f2e29df7ceb158 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jan 2022 09:18:36 +0000 Subject: [PATCH 2321/5615] Bump annotations from 20.0.0 to 23.0.0 in /modules/presto Bumps [annotations](https://github.com/JetBrains/java-annotations) from 20.0.0 to 23.0.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/20.0.0...23.0.0) --- updated-dependencies: - dependency-name: org.jetbrains:annotations dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/presto/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/presto/build.gradle b/modules/presto/build.gradle index 2b6955efd8a..fe0064bf199 100644 --- a/modules/presto/build.gradle +++ b/modules/presto/build.gradle @@ -5,5 +5,5 @@ dependencies { testImplementation project(':jdbc-test') testImplementation 'io.prestosql:presto-jdbc:350' - compileOnly 'org.jetbrains:annotations:20.0.0' + compileOnly 'org.jetbrains:annotations:23.0.0' } From 6c015557bf939b030277bf3107928e77dc7c9161 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jan 2022 09:18:41 +0000 Subject: [PATCH 2322/5615] Bump amqp-client from 5.7.0 to 5.14.0 in /modules/rabbitmq Bumps [amqp-client](https://github.com/rabbitmq/rabbitmq-java-client) from 5.7.0 to 5.14.0. - [Release notes](https://github.com/rabbitmq/rabbitmq-java-client/releases) - [Commits](https://github.com/rabbitmq/rabbitmq-java-client/compare/v5.7.0...v5.14.0) --- updated-dependencies: - dependency-name: com.rabbitmq:amqp-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/rabbitmq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/rabbitmq/build.gradle b/modules/rabbitmq/build.gradle index de778979f0f..005fcf3d97c 100644 --- a/modules/rabbitmq/build.gradle +++ b/modules/rabbitmq/build.gradle @@ -2,7 +2,7 @@ description = "TestContainers :: RabbitMQ" dependencies { api project(":testcontainers") - testImplementation 'com.rabbitmq:amqp-client:5.7.0' + testImplementation 'com.rabbitmq:amqp-client:5.14.0' testImplementation 'org.assertj:assertj-core:3.22.0' compileOnly 'org.jetbrains:annotations:20.0.0' } From 45eae3ef861e1a00cd12259d59dc97becf09953f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jan 2022 09:18:41 +0000 Subject: [PATCH 2323/5615] Bump google-cloud-bigtable from 2.4.0 to 2.5.1 in /modules/gcloud Bumps [google-cloud-bigtable](https://github.com/googleapis/java-bigtable) from 2.4.0 to 2.5.1. - [Release notes](https://github.com/googleapis/java-bigtable/releases) - [Changelog](https://github.com/googleapis/java-bigtable/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-bigtable/compare/v2.4.0...v2.5.1) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-bigtable dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 72041c6d14b..16f2b6e4fc2 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-firestore:3.0.10' testImplementation 'com.google.cloud:google-cloud-pubsub:1.115.0' testImplementation 'com.google.cloud:google-cloud-spanner:6.17.4' - testImplementation 'com.google.cloud:google-cloud-bigtable:2.4.0' + testImplementation 'com.google.cloud:google-cloud-bigtable:2.5.1' testImplementation 'org.assertj:assertj-core:3.22.0' } From 957ee23957ee93412d5d398d3a0e5458f069cf1c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jan 2022 09:18:41 +0000 Subject: [PATCH 2324/5615] Bump assertj-core from 3.21.0 to 3.22.0 in /modules/vault Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.21.0 to 3.22.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.21.0...assertj-core-3.22.0) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index 4fdb274f999..8aa0a2d689e 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -5,6 +5,6 @@ dependencies { testImplementation 'com.bettercloud:vault-java-driver:5.1.0' testImplementation 'io.rest-assured:rest-assured:4.4.0' - testImplementation 'org.assertj:assertj-core:3.21.0' + testImplementation 'org.assertj:assertj-core:3.22.0' } From 1378f53c4a498c790e2b5df942b94759ee6b7ab6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jan 2022 09:18:43 +0000 Subject: [PATCH 2325/5615] Bump annotations from 20.0.0 to 23.0.0 in /modules/rabbitmq Bumps [annotations](https://github.com/JetBrains/java-annotations) from 20.0.0 to 23.0.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/20.0.0...23.0.0) --- updated-dependencies: - dependency-name: org.jetbrains:annotations dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/rabbitmq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/rabbitmq/build.gradle b/modules/rabbitmq/build.gradle index de778979f0f..613ef37808f 100644 --- a/modules/rabbitmq/build.gradle +++ b/modules/rabbitmq/build.gradle @@ -4,5 +4,5 @@ dependencies { api project(":testcontainers") testImplementation 'com.rabbitmq:amqp-client:5.7.0' testImplementation 'org.assertj:assertj-core:3.22.0' - compileOnly 'org.jetbrains:annotations:20.0.0' + compileOnly 'org.jetbrains:annotations:23.0.0' } From fcd3892afaa56d6c30d4607d98973b29968b3869 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jan 2022 09:18:43 +0000 Subject: [PATCH 2326/5615] Bump assertj-core from 3.21.0 to 3.22.0 in /modules/pulsar Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.21.0 to 3.22.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.21.0...assertj-core-3.22.0) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 95b373eb5ef..cf11a36b411 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':testcontainers') testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.7.4' - testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.21.0' + testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.22.0' testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.7.4' } From aae6a044a1025662ea300ff913ee7f59cbfbf86b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jan 2022 09:18:44 +0000 Subject: [PATCH 2327/5615] Bump google-cloud-pubsub from 1.115.0 to 1.115.1 in /modules/gcloud Bumps [google-cloud-pubsub](https://github.com/googleapis/java-pubsub) from 1.115.0 to 1.115.1. - [Release notes](https://github.com/googleapis/java-pubsub/releases) - [Changelog](https://github.com/googleapis/java-pubsub/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-pubsub/compare/v1.115.0...v1.115.1) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-pubsub dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 72041c6d14b..0a6b351e8a0 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -5,7 +5,7 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-datastore:2.2.1' testImplementation 'com.google.cloud:google-cloud-firestore:3.0.10' - testImplementation 'com.google.cloud:google-cloud-pubsub:1.115.0' + testImplementation 'com.google.cloud:google-cloud-pubsub:1.115.1' testImplementation 'com.google.cloud:google-cloud-spanner:6.17.4' testImplementation 'com.google.cloud:google-cloud-bigtable:2.4.0' testImplementation 'org.assertj:assertj-core:3.22.0' From 6db619e8abf6b1bc4f450c3c311f75da2201496d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jan 2022 09:18:48 +0000 Subject: [PATCH 2328/5615] Bump assertj-core from 3.21.0 to 3.22.0 in /examples Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.21.0 to 3.22.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.21.0...assertj-core-3.22.0) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/kafka-cluster/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index a686fc171c2..3f28ca4ca9c 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -11,7 +11,7 @@ dependencies { testAnnotationProcessor "org.projectlombok:lombok:1.18.22" testImplementation 'org.testcontainers:kafka' testImplementation 'org.apache.kafka:kafka-clients:3.0.0' - testImplementation 'org.assertj:assertj-core:3.21.0' + testImplementation 'org.assertj:assertj-core:3.22.0' testImplementation 'com.google.guava:guava:23.0' testImplementation 'ch.qos.logback:logback-classic:1.2.10' } From 0a9122295062fd9634ea101e8bc848b704a5b0d2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jan 2022 09:18:54 +0000 Subject: [PATCH 2329/5615] Bump annotations from 20.0.0 to 23.0.0 in /modules/trino Bumps [annotations](https://github.com/JetBrains/java-annotations) from 20.0.0 to 23.0.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/20.0.0...23.0.0) --- updated-dependencies: - dependency-name: org.jetbrains:annotations dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/trino/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/trino/build.gradle b/modules/trino/build.gradle index fb154ba7266..f9223c174a5 100644 --- a/modules/trino/build.gradle +++ b/modules/trino/build.gradle @@ -5,5 +5,5 @@ dependencies { testImplementation project(':jdbc-test') testImplementation 'io.trino:trino-jdbc:367' - compileOnly 'org.jetbrains:annotations:20.0.0' + compileOnly 'org.jetbrains:annotations:23.0.0' } From 5d45556f02206678f3367c6fd6debc765d2160b3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jan 2022 09:19:06 +0000 Subject: [PATCH 2330/5615] Bump cucumber-java from 7.1.0 to 7.2.2 in /examples Bumps [cucumber-java](https://github.com/cucumber/cucumber-jvm) from 7.1.0 to 7.2.2. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.1.0...v7.2.2) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-java dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 3f37cbf45e4..b018d3df5fc 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' - testImplementation 'io.cucumber:cucumber-java:7.1.0' + testImplementation 'io.cucumber:cucumber-java:7.2.2' testImplementation 'io.cucumber:cucumber-junit:7.2.2' testImplementation 'org.testcontainers:selenium' } From d59b9e6eaa8979717672b385dd34da6381f02049 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jan 2022 09:19:10 +0000 Subject: [PATCH 2331/5615] Bump aws-java-sdk-sqs from 1.12.131 to 1.12.139 in /modules/localstack Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.12.131 to 1.12.139. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.131...1.12.139) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-sqs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 9ae7a3536b3..0e0ee5c7323 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.137' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.137' - testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.131' + testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.139' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.132' testImplementation 'software.amazon.awssdk:s3:2.17.108' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From 8526f53bd74d771d7896fcfa87194ebf7dde9b5e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jan 2022 09:19:22 +0000 Subject: [PATCH 2332/5615] Bump r2dbc-postgresql in /modules/r2dbc Bumps [r2dbc-postgresql](https://github.com/r2dbc/r2dbc-postgresql) from 0.8.1.RELEASE to 0.8.11.RELEASE. - [Release notes](https://github.com/r2dbc/r2dbc-postgresql/releases) - [Changelog](https://github.com/pgjdbc/r2dbc-postgresql/blob/v0.8.11.RELEASE/CHANGELOG) - [Commits](https://github.com/r2dbc/r2dbc-postgresql/compare/v0.8.1.RELEASE...v0.8.11.RELEASE) --- updated-dependencies: - dependency-name: io.r2dbc:r2dbc-postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/r2dbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle index 7844671959c..74ab1c9f7f0 100644 --- a/modules/r2dbc/build.gradle +++ b/modules/r2dbc/build.gradle @@ -12,7 +12,7 @@ dependencies { api 'io.r2dbc:r2dbc-spi:0.8.1.RELEASE' testImplementation 'org.assertj:assertj-core:3.22.0' - testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.1.RELEASE' + testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.11.RELEASE' testImplementation project(':postgresql') testFixturesImplementation 'io.projectreactor:reactor-core:3.4.14' From f305f8ec61fc0645c632641f72e3fa9a653df42b Mon Sep 17 00:00:00 2001 From: Anna Chernyshova Date: Thu, 13 Jan 2022 13:11:26 +0300 Subject: [PATCH 2333/5615] Update BrowserWebDriverContainer to add Selenium 4 support, drop Selenium 2 support (#4914) Fixes #4593 Co-authored-by: Tobias Stadler --- docs/modules/webdriver_containers.md | 2 +- .../containers/BrowserWebDriverContainer.java | 54 +++++++++---------- 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/docs/modules/webdriver_containers.md b/docs/modules/webdriver_containers.md index 34d3430830c..3c6b0ffae27 100644 --- a/docs/modules/webdriver_containers.md +++ b/docs/modules/webdriver_containers.md @@ -5,7 +5,7 @@ from SeleniumHQ's [docker-selenium](https://github.com/SeleniumHQ/docker-seleniu ## Benefits -* Fully compatible with Selenium 2/Webdriver tests, by providing a `RemoteWebDriver` instance +* Fully compatible with Selenium 3 & 4 tests, by providing a `RemoteWebDriver` instance * No need to have specific web browsers, or even a desktop environment, installed on test servers. The only dependency is a working Docker installation and your Java JUnit test suite. * Browsers are always launched from a fixed, clean image. This means no configuration drift from user changes or diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index 7bc37e4701c..bd38dead8ac 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -35,6 +35,7 @@ import org.testcontainers.containers.wait.strategy.WaitStrategy; import org.testcontainers.lifecycle.TestDescription; import org.testcontainers.lifecycle.TestLifecycleAware; +import org.testcontainers.utility.ComparableVersion; import org.testcontainers.utility.DockerImageName; /** @@ -44,13 +45,15 @@ */ public class BrowserWebDriverContainer> extends GenericContainer implements LinkableContainer, TestLifecycleAware { - private static final DockerImageName CHROME_IMAGE = DockerImageName.parse("selenium/standalone-chrome-debug"); - private static final DockerImageName FIREFOX_IMAGE = DockerImageName.parse("selenium/standalone-firefox-debug"); + private static final DockerImageName CHROME_IMAGE = DockerImageName.parse("selenium/standalone-chrome"); + private static final DockerImageName FIREFOX_IMAGE = DockerImageName.parse("selenium/standalone-firefox"); + private static final DockerImageName CHROME_DEBUG_IMAGE = DockerImageName.parse("selenium/standalone-chrome-debug"); + private static final DockerImageName FIREFOX_DEBUG_IMAGE = DockerImageName.parse("selenium/standalone-firefox-debug"); private static final DockerImageName[] COMPATIBLE_IMAGES = new DockerImageName[] { CHROME_IMAGE, FIREFOX_IMAGE, - DockerImageName.parse("selenium/standalone-chrome"), - DockerImageName.parse("selenium/standalone-firefox") + CHROME_DEBUG_IMAGE, + FIREFOX_DEBUG_IMAGE }; private static final String DEFAULT_PASSWORD = "secret"; @@ -156,23 +159,6 @@ protected void configure() { String seleniumVersion = SeleniumUtils.determineClasspathSeleniumVersion(); - if (capabilities == null) { - if (seleniumVersion.startsWith("2.")) { - logger().info("No capabilities provided, falling back to DesiredCapabilities.chrome()"); - capabilities = DesiredCapabilities.chrome(); - } else { - logger().info("No capabilities provided, falling back to ChromeOptions"); - capabilities = new ChromeOptions(); - } - } - - // Hack for new selenium-chrome image that contains Chrome 92. - // If not disabled, container startup will fail in most cases and consume excessive amounts of CPU. - if (capabilities instanceof ChromeOptions) { - ChromeOptions options = (ChromeOptions) this.capabilities; - options.addArguments("--disable-gpu"); - } - if (recordingMode != VncRecordingMode.SKIP) { if (vncRecordingDirectory == null) { @@ -244,12 +230,14 @@ public static String getDockerImageForCapabilities(Capabilities capabilities, St } private static DockerImageName getStandardImageForCapabilities(Capabilities capabilities, String seleniumVersion) { - String browserName = capabilities.getBrowserName(); + String browserName = capabilities == null ? BrowserType.CHROME : capabilities.getBrowserName(); + boolean supportsVncWithoutDebugImage = new ComparableVersion(seleniumVersion).isGreaterThanOrEqualTo("4"); + switch (browserName) { case BrowserType.CHROME: - return CHROME_IMAGE.withTag(seleniumVersion); + return (supportsVncWithoutDebugImage ? CHROME_IMAGE : CHROME_DEBUG_IMAGE).withTag(seleniumVersion); case BrowserType.FIREFOX: - return FIREFOX_IMAGE.withTag(seleniumVersion); + return (supportsVncWithoutDebugImage ? FIREFOX_IMAGE : FIREFOX_DEBUG_IMAGE).withTag(seleniumVersion); default: throw new UnsupportedOperationException("Browser name must be 'chrome' or 'firefox'; provided '" + browserName + "' is not supported"); } @@ -280,10 +268,6 @@ public int getPort() { @Override protected void containerIsStarted(InspectContainerResponse containerInfo) { - driver = Unreliables.retryUntilSuccess(30, TimeUnit.SECONDS, - () -> Timeouts.getWithTimeout(10, TimeUnit.SECONDS, - () -> new RemoteWebDriver(getSeleniumAddress(), capabilities))); - if (vncRecordingContainer != null) { LOGGER.debug("Starting VNC recording"); vncRecordingContainer.start(); @@ -298,7 +282,19 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { * * @return a new Remote Web Driver instance */ - public RemoteWebDriver getWebDriver() { + public synchronized RemoteWebDriver getWebDriver() { + if (driver == null) { + if (capabilities == null) { + logger().warn("No capabilities provided - this will cause an exception in future versions. Falling back to ChromeOptions"); + capabilities = new ChromeOptions(); + } + + driver = Unreliables.retryUntilSuccess(30, TimeUnit.SECONDS, () -> { + return Timeouts.getWithTimeout(10, TimeUnit.SECONDS, () -> { + return new RemoteWebDriver(getSeleniumAddress(), capabilities); + }); + }); + } return driver; } From 74173e51e0981bf3a5ea3fc617f385b4fedb6528 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jan 2022 13:03:38 +0000 Subject: [PATCH 2334/5615] Bump aws-java-sdk-logs from 1.12.132 to 1.12.139 in /modules/localstack Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.12.132 to 1.12.139. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.132...1.12.139) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-logs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 0e0ee5c7323..7bfec27ccaf 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,7 +6,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.137' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.137' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.139' - testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.132' + testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.139' testImplementation 'software.amazon.awssdk:s3:2.17.108' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 3a16a794162064effec0e805d7d34342c63f65ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jan 2022 13:03:39 +0000 Subject: [PATCH 2335/5615] Bump r2dbc-postgresql in /modules/postgresql Bumps [r2dbc-postgresql](https://github.com/r2dbc/r2dbc-postgresql) from 0.8.8.RELEASE to 0.8.11.RELEASE. - [Release notes](https://github.com/r2dbc/r2dbc-postgresql/releases) - [Changelog](https://github.com/pgjdbc/r2dbc-postgresql/blob/v0.8.11.RELEASE/CHANGELOG) - [Commits](https://github.com/r2dbc/r2dbc-postgresql/compare/v0.8.8.RELEASE...v0.8.11.RELEASE) --- updated-dependencies: - dependency-name: io.r2dbc:r2dbc-postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/postgresql/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index c38b9bc4fc2..a8a6495bfc1 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -7,14 +7,14 @@ dependencies { api project(':jdbc') compileOnly project(':r2dbc') - compileOnly 'io.r2dbc:r2dbc-postgresql:0.8.8.RELEASE' + compileOnly 'io.r2dbc:r2dbc-postgresql:0.8.11.RELEASE' testImplementation project(':jdbc-test') testImplementation project(':test-support') testImplementation 'org.postgresql:postgresql:42.3.1' testImplementation testFixtures(project(':r2dbc')) - testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.5.RELEASE' + testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.11.RELEASE' compileOnly 'org.jetbrains:annotations:23.0.0' } From 8186795449a3b32108e663e3208406b38fc61d7e Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 13 Jan 2022 15:27:22 +0000 Subject: [PATCH 2336/5615] Basic K3S module (#4582) --- docs/modules/k3s.md | 53 +++++++++++++++ mkdocs.yml | 1 + modules/k3s/build.gradle | 11 ++++ .../org/testcontainers/k3s/K3sContainer.java | 64 +++++++++++++++++++ .../k3s/Fabric8K3sContainerTest.java | 43 +++++++++++++ .../k3s/OfficialClientK3sContainerTest.java | 43 +++++++++++++ .../k3s/src/test/resources/logback-test.xml | 16 +++++ 7 files changed, 231 insertions(+) create mode 100644 docs/modules/k3s.md create mode 100644 modules/k3s/build.gradle create mode 100644 modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java create mode 100644 modules/k3s/src/test/java/org/testcontainers/k3s/Fabric8K3sContainerTest.java create mode 100644 modules/k3s/src/test/java/org/testcontainers/k3s/OfficialClientK3sContainerTest.java create mode 100644 modules/k3s/src/test/resources/logback-test.xml diff --git a/docs/modules/k3s.md b/docs/modules/k3s.md new file mode 100644 index 00000000000..a3c2c0fa754 --- /dev/null +++ b/docs/modules/k3s.md @@ -0,0 +1,53 @@ +# K3s Module + +!!! note + This module is INCUBATING. While it is ready for use and operational in the current version of Testcontainers, it is possible that it may receive breaking changes in the future. See [our contributing guidelines](/contributing/#incubating-modules) for more information on our incubating modules policy. + +Testcontainers module for Rancher's [K3s](https://rancher.com/products/k3s/) lightweight Kubernetes. +This module is intended to be used for testing components that interact with Kubernetes APIs - for example, operators. + +## Usage example + +Start a K3s server as follows: + + +[Starting a K3S server](../../modules/k3s/src/test/java/org/testcontainers/k3s/Fabric8K3sContainerTest.java) inside_block:starting_k3s + + +### Connecting to the server + +`K3sContainer` exposes a working Kubernetes client configuration, as a YAML String, via the `getKubeConfigYaml()` method. + +This may be used with Kubernetes clients - e.g. for the [official Java client](connecting_with_k8sio) and +[the Fabric8 Kubernetes client](https://github.com/fabric8io/kubernetes-client): + + +[Official Java client](../../modules/k3s/src/test/java/org/testcontainers/k3s/OfficialClientK3sContainerTest.java) inside_block:connecting_with_k8sio +[Fabric8 Kubernetes client](../../modules/k3s/src/test/java/org/testcontainers/k3s/Fabric8K3sContainerTest.java) inside_block:connecting_with_fabric8 + + +## Known limitations + +!!! warning + * K3sContainer runs as a privileged container and needs to be able to spawn its own containers. For these reasons, + K3sContainer will not work in certain rootless Docker, Docker-in-Docker, or other environments where privileged + containers are disallowed. + + * k3s containers may be unable to run on host machines where `/var/lib/docker` is on a BTRFS filesystem. See [k3s-io/k3s#4863](https://github.com/k3s-io/k3s/issues/4863) for an example. + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testImplementation "org.testcontainers:k3s:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + k3s + {{latest_version}} + test + +``` diff --git a/mkdocs.yml b/mkdocs.yml index be30f26a964..827ef6a836d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -65,6 +65,7 @@ nav: - modules/docker_compose.md - modules/elasticsearch.md - modules/gcloud.md + - modules/k3s.md - modules/kafka.md - modules/localstack.md - modules/mockserver.md diff --git a/modules/k3s/build.gradle b/modules/k3s/build.gradle new file mode 100644 index 00000000000..85ee36652b3 --- /dev/null +++ b/modules/k3s/build.gradle @@ -0,0 +1,11 @@ +description = "Testcontainers :: K3S" + +dependencies { + api project(":testcontainers") + + shaded 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.1' + + testImplementation 'io.fabric8:kubernetes-client:5.11.0' + testImplementation 'io.kubernetes:client-java:14.0.0' + testImplementation 'org.assertj:assertj-core:3.21.0' +} diff --git a/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java b/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java new file mode 100644 index 00000000000..1080563226a --- /dev/null +++ b/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java @@ -0,0 +1,64 @@ +package org.testcontainers.k3s; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.github.dockerjava.api.command.InspectContainerResponse; +import lombok.SneakyThrows; +import org.testcontainers.containers.BindMode; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; +import org.testcontainers.utility.DockerImageName; + +import java.util.HashMap; +import java.util.Map; + +public class K3sContainer extends GenericContainer { + + private String kubeConfigYaml; + + public K3sContainer(DockerImageName dockerImageName) { + super(dockerImageName); + dockerImageName.assertCompatibleWith(DockerImageName.parse("rancher/k3s")); + + addExposedPorts(6443, 8443); + setPrivilegedMode(true); + addFileSystemBind("/sys/fs/cgroup", "/sys/fs/cgroup", BindMode.READ_WRITE); + + Map tmpFsMapping = new HashMap<>(); + tmpFsMapping.put("/run", ""); + tmpFsMapping.put("/var/run", ""); + setTmpFsMapping(tmpFsMapping); + + setCommand( + "server", + "--no-deploy=traefik", + "--tls-san=" + this.getHost() + ); + setWaitStrategy(new LogMessageWaitStrategy().withRegEx(".*Node controller sync successful.*")); + } + + @SneakyThrows + @Override + protected void containerIsStarted(InspectContainerResponse containerInfo) { + ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory()); + + ObjectNode rawKubeConfig = copyFileFromContainer( + "/etc/rancher/k3s/k3s.yaml", + is -> objectMapper.readValue(is, ObjectNode.class) + ); + + ObjectNode clusterConfig = rawKubeConfig.at("/clusters/0/cluster").require(); + clusterConfig.replace("server", new TextNode("https://" + this.getHost() + ":" + this.getMappedPort(6443))); + + rawKubeConfig.set("current-context", new TextNode("default")); + + kubeConfigYaml = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(rawKubeConfig); + } + + public String getKubeConfigYaml() { + return kubeConfigYaml; + } +} diff --git a/modules/k3s/src/test/java/org/testcontainers/k3s/Fabric8K3sContainerTest.java b/modules/k3s/src/test/java/org/testcontainers/k3s/Fabric8K3sContainerTest.java new file mode 100644 index 00000000000..8e7b601c5b0 --- /dev/null +++ b/modules/k3s/src/test/java/org/testcontainers/k3s/Fabric8K3sContainerTest.java @@ -0,0 +1,43 @@ +package org.testcontainers.k3s; + +import io.fabric8.kubernetes.api.model.Node; +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.DefaultKubernetesClient; +import lombok.extern.slf4j.Slf4j; +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.utility.DockerImageName; + +import java.util.List; + +@Slf4j +public class Fabric8K3sContainerTest { + + @Test + public void shouldStartAndHaveListableNode() { + try ( + // starting_k3s { + K3sContainer k3s = new K3sContainer(DockerImageName.parse("rancher/k3s:v1.21.3-k3s1")) + .withLogConsumer(new Slf4jLogConsumer(log)) + // } + ) { + k3s.start(); + + // connecting_with_fabric8 { + // obtain a kubeconfig file which allows us to connect to k3s + String kubeConfigYaml = k3s.getKubeConfigYaml(); + + // requires io.fabric8:kubernetes-client:5.11.0 or higher + Config config = Config.fromKubeconfig(kubeConfigYaml); + + DefaultKubernetesClient client = new DefaultKubernetesClient(config); + + // interact with the running K3s server, e.g.: + List nodes = client.nodes().list().getItems(); + // } + + Assertions.assertThat(nodes).hasSize(1); + } + } +} diff --git a/modules/k3s/src/test/java/org/testcontainers/k3s/OfficialClientK3sContainerTest.java b/modules/k3s/src/test/java/org/testcontainers/k3s/OfficialClientK3sContainerTest.java new file mode 100644 index 00000000000..970e2390457 --- /dev/null +++ b/modules/k3s/src/test/java/org/testcontainers/k3s/OfficialClientK3sContainerTest.java @@ -0,0 +1,43 @@ +package org.testcontainers.k3s; + +import io.kubernetes.client.openapi.ApiClient; +import io.kubernetes.client.openapi.ApiException; +import io.kubernetes.client.openapi.apis.CoreV1Api; +import io.kubernetes.client.openapi.models.V1NodeList; +import io.kubernetes.client.util.Config; +import lombok.extern.slf4j.Slf4j; +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.utility.DockerImageName; + +import java.io.IOException; +import java.io.StringReader; + +@Slf4j +public class OfficialClientK3sContainerTest { + + @Test + public void shouldStartAndHaveListableNode() throws IOException, ApiException { + try ( + // starting_k3s { + K3sContainer k3s = new K3sContainer(DockerImageName.parse("rancher/k3s:v1.21.3-k3s1")) + .withLogConsumer(new Slf4jLogConsumer(log)) + // } + ) { + k3s.start(); + + // connecting_with_k8sio { + String kubeConfigYaml = k3s.getKubeConfigYaml(); + + ApiClient client = Config.fromConfig(new StringReader(kubeConfigYaml)); + CoreV1Api api = new CoreV1Api(client); + + // interact with the running K3s server, e.g.: + V1NodeList nodes = api.listNode(null, null, null, null, null, null, null, null, null, null); + // } + + Assertions.assertThat(nodes.getItems()).hasSize(1); + } + } +} diff --git a/modules/k3s/src/test/resources/logback-test.xml b/modules/k3s/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..535e406fc13 --- /dev/null +++ b/modules/k3s/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + From af7cec02ea6b658dfcb61f50da0ef5dbe08cacaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Prinet?= Date: Mon, 17 Jan 2022 10:58:06 +0100 Subject: [PATCH 2337/5615] Bump up Gradle Enterprise plugin dependencies (#4926) --- examples/settings.gradle | 2 +- settings.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/settings.gradle b/examples/settings.gradle index 8641c6134b1..f58ea83fb9b 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.10.0" - classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.8" + classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.8.1" classpath "com.gradle:common-custom-user-data-gradle-plugin:1.6.2" } } diff --git a/settings.gradle b/settings.gradle index 57f18b531b1..e4c65186fb8 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,7 +5,7 @@ buildscript { } } dependencies { - classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.8" + classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.8.1" classpath "com.gradle:common-custom-user-data-gradle-plugin:1.6.2" } } From f7dcfa8593625a490017135698ff6dbb1d794f6b Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Mon, 17 Jan 2022 14:34:28 +0100 Subject: [PATCH 2338/5615] Use jackson 2.8.8 in k3s module (#4928) To ensure same jackson version across TC core and modules, this commit manually aligns them. --- modules/k3s/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/k3s/build.gradle b/modules/k3s/build.gradle index 85ee36652b3..95d2d3bc97c 100644 --- a/modules/k3s/build.gradle +++ b/modules/k3s/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: K3S" dependencies { api project(":testcontainers") - shaded 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.1' + shaded 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.8.8' testImplementation 'io.fabric8:kubernetes-client:5.11.0' testImplementation 'io.kubernetes:client-java:14.0.0' From 19b58cdb7b8d2ca56c13743527bb0c674676b60d Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Mon, 17 Jan 2022 17:47:16 +0100 Subject: [PATCH 2339/5615] Use 2.8.8 compatible Jackson API (#4930) --- .../src/main/java/org/testcontainers/k3s/K3sContainer.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java b/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java index 1080563226a..800d80c67e1 100644 --- a/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java +++ b/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java @@ -50,7 +50,12 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { is -> objectMapper.readValue(is, ObjectNode.class) ); - ObjectNode clusterConfig = rawKubeConfig.at("/clusters/0/cluster").require(); + JsonNode clusterNode = rawKubeConfig.at("/clusters/0/cluster"); + if (!clusterNode.isObject()) { + throw new IllegalStateException("'/clusters/0/cluster' expected to be an object"); + } + ObjectNode clusterConfig = (ObjectNode) clusterNode; + clusterConfig.replace("server", new TextNode("https://" + this.getHost() + ":" + this.getMappedPort(6443))); rawKubeConfig.set("current-context", new TextNode("default")); From 04937b52434edfac91bfbcfb672621d68c327891 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Mon, 17 Jan 2022 18:24:31 +0100 Subject: [PATCH 2340/5615] Replace jcenter with mavenCentral in all examples (#4931) --- examples/build.gradle | 2 +- examples/cucumber/build.gradle | 2 +- examples/disque-job-queue/build.gradle | 2 +- examples/kafka-cluster/build.gradle | 2 +- examples/linked-container/build.gradle | 2 +- examples/mongodb-container/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/selenium-container/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- examples/solr-container/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/build.gradle b/examples/build.gradle index 83e196dac00..d340e297d33 100644 --- a/examples/build.gradle +++ b/examples/build.gradle @@ -5,6 +5,6 @@ subprojects { apply plugin:"java" repositories { - jcenter() + mavenCentral() } } diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index b018d3df5fc..4f462ed4740 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -3,7 +3,7 @@ plugins { } repositories { - jcenter() + mavenCentral() } dependencies { diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index ff03cee54cc..1639a091e91 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -3,7 +3,7 @@ plugins { } repositories { - jcenter() + mavenCentral() } dependencies { diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index 3f28ca4ca9c..6351be31488 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -3,7 +3,7 @@ plugins { } repositories { - jcenter() + mavenCentral() } dependencies { diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 1d6c29b16eb..5ca68b2c514 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -3,7 +3,7 @@ plugins { } repositories { - jcenter() + mavenCentral() } dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' diff --git a/examples/mongodb-container/build.gradle b/examples/mongodb-container/build.gradle index 23f193115ba..1ae43055790 100644 --- a/examples/mongodb-container/build.gradle +++ b/examples/mongodb-container/build.gradle @@ -3,7 +3,7 @@ plugins { } repositories { - jcenter() + mavenCentral() } dependencies { diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 2046da278f2..c60d5f08cf4 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -3,7 +3,7 @@ plugins { } repositories { - jcenter() + mavenCentral() } dependencies { diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 38b3804b4b8..dff9ae7024f 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -3,7 +3,7 @@ plugins { } repositories { - jcenter() + mavenCentral() } dependencies { diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index f79edec4998..495c279937a 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -5,7 +5,7 @@ plugins { apply plugin: 'io.spring.dependency-management' repositories { - jcenter() + mavenCentral() } dependencies { diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 43d74d16244..a56f269fa40 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -3,7 +3,7 @@ plugins { } repositories { - jcenter() + mavenCentral() } dependencies { diff --git a/examples/solr-container/build.gradle b/examples/solr-container/build.gradle index c0c69300377..cec4f59625b 100644 --- a/examples/solr-container/build.gradle +++ b/examples/solr-container/build.gradle @@ -3,7 +3,7 @@ plugins { } repositories { - jcenter() + mavenCentral() } dependencies { diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index 782f03b561e..09b82c34ea7 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -5,7 +5,7 @@ plugins { apply plugin: 'io.spring.dependency-management' repositories { - jcenter() + mavenCentral() } dependencies { From 34cfdc8e3fa86852a05131eb7f48b86966864c0a Mon Sep 17 00:00:00 2001 From: Richard North Date: Tue, 18 Jan 2022 13:12:32 +0000 Subject: [PATCH 2341/5615] Fix k3s for latest Docker for Mac (cgroupsv2) (#4937) --- .../org/testcontainers/k3s/K3sContainer.java | 6 +++ .../k3s/Fabric8K3sContainerTest.java | 42 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java b/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java index 800d80c67e1..511b309e0d5 100644 --- a/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java +++ b/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.databind.node.TextNode; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.model.DockerObjectAccessor; import lombok.SneakyThrows; import org.testcontainers.containers.BindMode; import org.testcontainers.containers.GenericContainer; @@ -25,6 +26,11 @@ public K3sContainer(DockerImageName dockerImageName) { addExposedPorts(6443, 8443); setPrivilegedMode(true); + withCreateContainerCmdModifier(it -> { + DockerObjectAccessor.overrideRawValue( + it.getHostConfig(), "CgroupnsMode", "host" + ); + }); addFileSystemBind("/sys/fs/cgroup", "/sys/fs/cgroup", BindMode.READ_WRITE); Map tmpFsMapping = new HashMap<>(); diff --git a/modules/k3s/src/test/java/org/testcontainers/k3s/Fabric8K3sContainerTest.java b/modules/k3s/src/test/java/org/testcontainers/k3s/Fabric8K3sContainerTest.java index 8e7b601c5b0..8b31c299634 100644 --- a/modules/k3s/src/test/java/org/testcontainers/k3s/Fabric8K3sContainerTest.java +++ b/modules/k3s/src/test/java/org/testcontainers/k3s/Fabric8K3sContainerTest.java @@ -1,15 +1,27 @@ package org.testcontainers.k3s; +import io.fabric8.kubernetes.api.model.ContainerBuilder; +import io.fabric8.kubernetes.api.model.ContainerPortBuilder; import io.fabric8.kubernetes.api.model.Node; +import io.fabric8.kubernetes.api.model.Pod; +import io.fabric8.kubernetes.api.model.PodBuilder; +import io.fabric8.kubernetes.api.model.PodSpec; +import io.fabric8.kubernetes.api.model.PodSpecBuilder; +import io.fabric8.kubernetes.api.model.ProbeBuilder; import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.DefaultKubernetesClient; +import io.fabric8.kubernetes.client.dsl.PodResource; +import io.fabric8.kubernetes.client.dsl.Readiable; import lombok.extern.slf4j.Slf4j; import org.assertj.core.api.Assertions; +import org.assertj.core.api.Condition; import org.junit.Test; import org.testcontainers.containers.output.Slf4jLogConsumer; import org.testcontainers.utility.DockerImageName; import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; @Slf4j public class Fabric8K3sContainerTest { @@ -38,6 +50,36 @@ public void shouldStartAndHaveListableNode() { // } Assertions.assertThat(nodes).hasSize(1); + + // verify that we can start a pod + Pod helloworld = dummyStartablePod(); + client.pods().create(helloworld); + client.pods().inNamespace("default").withName("helloworld").waitUntilReady(30, TimeUnit.SECONDS); + + Assertions.assertThat(client.pods().inNamespace("default").withName("helloworld")) + .extracting(Readiable::isReady) + .isEqualTo(true); } } + + private Pod dummyStartablePod() { + PodSpec podSpec = new PodSpecBuilder() + .withContainers( + new ContainerBuilder() + .withName("helloworld") + .withImage("testcontainers/helloworld:1.1.0") + .withPorts(new ContainerPortBuilder().withContainerPort(8080).build()) + .withReadinessProbe(new ProbeBuilder().withNewTcpSocket().withNewPort(8080).endTcpSocket().build()) + .build() + ) + .build(); + + return new PodBuilder() + .withNewMetadata() + .withName("helloworld") + .withNamespace("default") + .endMetadata() + .withSpec(podSpec) + .build(); + } } From 5fe437f0e7ef18d735ab31245d81aed710690778 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Wed, 19 Jan 2022 13:09:34 +0100 Subject: [PATCH 2342/5615] Use JDK 1.8 in Azure pipelines (#4942) --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 53dabc6722b..0f01bb7bcd2 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -13,7 +13,7 @@ jobs: AWS_SECRET_ACCESS_KEY: $(aws.secretAccessKey) inputs: gradleWrapperFile: 'gradlew' - jdkVersionOption: '1.11' + jdkVersionOption: '1.8' options: '--no-daemon --continue' tasks: 'clean testcontainers:check' publishJUnitResults: true @@ -28,7 +28,7 @@ jobs: AWS_SECRET_ACCESS_KEY: $(aws.secretAccessKey) inputs: gradleWrapperFile: 'gradlew' - jdkVersionOption: '1.11' + jdkVersionOption: '1.8' options: '--no-daemon --continue' tasks: 'clean check -x testcontainers:test' publishJUnitResults: true From e50ae1da134eb9ab986e068ba820eaa7fbd98dc0 Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Wed, 19 Jan 2022 16:51:41 +0100 Subject: [PATCH 2343/5615] Simplify dependency management for JUnit Jupiter (#4704) By using the junit-jupiter aggregator artifact. --- docs/quickstart/junit_5_quickstart.md | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/docs/quickstart/junit_5_quickstart.md b/docs/quickstart/junit_5_quickstart.md index eb43bf05797..b50272cb68d 100644 --- a/docs/quickstart/junit_5_quickstart.md +++ b/docs/quickstart/junit_5_quickstart.md @@ -22,10 +22,7 @@ Let's start from here, and see how to improve the test with Testcontainers: First, add Testcontainers as a dependency as follows: ```groovy tab='Gradle' -def junitJupiterVersion = '5.4.2' -testImplementation "org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion" -testImplementation "org.junit.jupiter:junit-jupiter-params:$junitJupiterVersion" -testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion" +testImplementation "org.junit.jupiter:junit-jupiter:5.8.1" testImplementation "org.testcontainers:testcontainers:{{latest_version}}" testImplementation "org.testcontainers:junit-jupiter:{{latest_version}}" ``` @@ -33,20 +30,8 @@ testImplementation "org.testcontainers:junit-jupiter:{{latest_version}}" ```xml tab='Maven' org.junit.jupiter - junit-jupiter-api - 5.4.2 - test - - - org.junit.jupiter - junit-jupiter-params - 5.4.2 - test - - - org.junit.jupiter - junit-jupiter-engine - 5.4.2 + junit-jupiter + 5.8.1 test From f9538d16b7f636420cc269ce15fd6b5742be278c Mon Sep 17 00:00:00 2001 From: Alex Stockinger Date: Mon, 24 Jan 2022 11:16:32 +0100 Subject: [PATCH 2344/5615] Remove some dead code from PostgreSQLContainer (#4948) --- .../java/org/testcontainers/containers/PostgreSQLContainer.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index 2b905bd3d37..3bfb508aa47 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -32,8 +32,6 @@ public class PostgreSQLContainer> extends private static final String FSYNC_OFF_OPTION = "fsync=off"; - private static final String QUERY_PARAM_SEPARATOR = "&"; - /** * @deprecated use {@link #PostgreSQLContainer(DockerImageName)} or {@link #PostgreSQLContainer(String)} instead */ From 639fe41a3ca71bf4b2401b82922bb8f20e2cf41d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 28 Jan 2022 12:32:54 +0100 Subject: [PATCH 2345/5615] Update docs version to 1.16.3 (#4932) --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 827ef6a836d..694fca299ca 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -98,4 +98,4 @@ nav: - contributing_docs.md - bounty.md extra: - latest_version: 1.16.2 + latest_version: 1.16.3 From 242dfde521020ea803a37941b6d043d6bafb56d1 Mon Sep 17 00:00:00 2001 From: Tayeb Chlyah Date: Mon, 31 Jan 2022 09:31:17 +0100 Subject: [PATCH 2346/5615] [Couchbase] Add support for Eventing service (#4953) --- .../couchbase/CouchbaseContainer.java | 43 +++++++++++++++++-- .../couchbase/CouchbaseService.java | 7 ++- .../couchbase/CouchbaseContainerTest.java | 18 +++++++- 3 files changed, 62 insertions(+), 6 deletions(-) diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index 5007a8d5ccf..c2becc68fc3 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -74,6 +74,10 @@ public class CouchbaseContainer extends GenericContainer { private static final int ANALYTICS_SSL_PORT = 18095; + private static final int EVENTING_PORT = 8096; + + private static final int EVENTING_SSL_PORT = 18096; + private static final int KV_PORT = 11210; private static final int KV_SSL_PORT = 11207; @@ -196,6 +200,17 @@ public CouchbaseContainer withAnalyticsService() { return this; } + /** + * Enables the eventing service which is not enabled by default. + * + * @return this {@link CouchbaseContainer} for chaining purposes. + */ + public CouchbaseContainer withEventingService() { + checkNotRunning(); + this.enabledServices.add(CouchbaseService.EVENTING); + return this; + } + public final String getUsername() { return username; } @@ -232,7 +247,9 @@ protected void configure() { ANALYTICS_PORT, ANALYTICS_SSL_PORT, KV_PORT, - KV_SSL_PORT + KV_SSL_PORT, + EVENTING_PORT, + EVENTING_SSL_PORT ); WaitAllStrategy waitStrategy = new WaitAllStrategy(); @@ -278,6 +295,16 @@ protected void configure() { ); } + if (enabledServices.contains(CouchbaseService.EVENTING)) { + waitStrategy = waitStrategy.withStrategy( + new HttpWaitStrategy() + .forPath("/api/v1/config") + .forPort(EVENTING_PORT) + .withBasicCredentials(username, password) + .forStatusCode(200) + ); + } + waitingFor(waitStrategy); } @@ -328,8 +355,13 @@ private void initializeIsEnterprise() { throw new IllegalStateException("Couchbase /pools did not return valid JSON"); } - if (!isEnterprise && enabledServices.contains(CouchbaseService.ANALYTICS)) { - throw new IllegalStateException("The Analytics Service is only supported with the Enterprise version"); + if (!isEnterprise) { + if (enabledServices.contains(CouchbaseService.ANALYTICS)) { + throw new IllegalStateException("The Analytics Service is only supported with the Enterprise version"); + } + if (enabledServices.contains(CouchbaseService.EVENTING)) { + throw new IllegalStateException("The Eventing Service is only supported with the Enterprise version"); + } } } @@ -452,6 +484,11 @@ private void configureExternalPorts() { builder.add("cbasSSL", Integer.toString(getMappedPort(ANALYTICS_SSL_PORT))); } + if (enabledServices.contains(CouchbaseService.EVENTING)) { + builder.add("eventingAdminPort", Integer.toString(getMappedPort(EVENTING_PORT))); + builder.add("eventingSSL", Integer.toString(getMappedPort(EVENTING_SSL_PORT))); + } + @Cleanup Response response = doHttpRequest( MGMT_PORT, "/node/controller/setupAlternateAddresses/external", diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseService.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseService.java index a314bd5ccfc..ffb0c9f7f1f 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseService.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseService.java @@ -46,7 +46,12 @@ public enum CouchbaseService { /** * Analytics service. */ - ANALYTICS("cbas", 1024); + ANALYTICS("cbas", 1024), + + /** + * Eventing service. + */ + EVENTING("eventing", 256); private final String identifier; diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java index 5d2d7d9402a..c91f3f12d7c 100644 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java @@ -35,9 +35,9 @@ public class CouchbaseContainerTest { private static final DockerImageName COUCHBASE_IMAGE_ENTERPRISE = - DockerImageName.parse("couchbase/server:enterprise-6.6.2"); + DockerImageName.parse("couchbase/server:enterprise-7.0.3"); private static final DockerImageName COUCHBASE_IMAGE_COMMUNITY = - DockerImageName.parse("couchbase/server:community-6.6.0"); + DockerImageName.parse("couchbase/server:community-7.0.2"); @Test public void testBasicContainerUsageForEnterpriseContainer() { @@ -127,6 +127,20 @@ public void testFailureIfCommunityUsedWithAnalytics() { } } + /** + * Make sure that the code fails fast if the Eventing service is enabled on the community + * edition which is not supported. + */ + @Test + public void testFailureIfCommunityUsedWithEventing() { + try ( + CouchbaseContainer container = new CouchbaseContainer(COUCHBASE_IMAGE_COMMUNITY) + .withEnabledServices(CouchbaseService.KV, CouchbaseService.EVENTING) + ) { + assertThrows(ContainerLaunchException.class, () -> setUpClient(container, cluster -> {})); + } + } + private void setUpClient(CouchbaseContainer container, Consumer consumer) { container.start(); From 205f21c242d067d67fc7f09d8d4738da28efcd08 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 31 Jan 2022 08:53:01 -0500 Subject: [PATCH 2347/5615] Prune from JVM hook if Ryuk is disabled (#4960) If Ryuk is disabled, singleton containers and other uncontrolled containers were remaining dangling. This change adds best-effort Ryuk-like functionality that will use a JVM hook to clean by labels. Co-authored-by: Richard North --- .../testcontainers/DockerClientFactory.java | 3 + .../containers/DockerComposeContainer.java | 6 +- .../utility/ResourceReaper.java | 97 +++++++++++++++---- 3 files changed, 82 insertions(+), 24 deletions(-) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 36c4c717b76..1ad7a0bec7e 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -225,6 +225,9 @@ public void close() { } else { log.debug("Ryuk is disabled"); ryukContainerId = null; + // best-efforts cleanup at JVM shutdown, without using the Ryuk container + //noinspection deprecation + ResourceReaper.instance().setHook(); } boolean checksEnabled = !TestcontainersConfiguration.getInstance().isDisableChecks(); diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 961d066cb91..b56c70cb959 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -41,7 +41,6 @@ import java.io.File; import java.time.Duration; -import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -56,7 +55,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; -import java.util.stream.Collectors; import java.util.stream.Stream; import static com.google.common.base.Preconditions.checkArgument; @@ -310,8 +308,8 @@ private void runWithCompose(String cmd) { } private void registerContainersForShutdown() { - ResourceReaper.instance().registerFilterForCleanup(Arrays.asList( - new SimpleEntry<>("label", "com.docker.compose.project=" + project) + ResourceReaper.instance().registerLabelsFilterForCleanup(Collections.singletonMap( + "com.docker.compose.project", project )); } diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index f43835fd01b..8c2cd3a6108 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -5,12 +5,14 @@ import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.exception.NotFoundException; import com.github.dockerjava.api.model.Bind; +import com.github.dockerjava.api.model.Container; import com.github.dockerjava.api.model.ExposedPort; import com.github.dockerjava.api.model.Frame; import com.github.dockerjava.api.model.HostConfig; import com.github.dockerjava.api.model.Network; import com.github.dockerjava.api.model.PortBinding; import com.github.dockerjava.api.model.Ports; +import com.github.dockerjava.api.model.PruneType; import com.github.dockerjava.api.model.Volume; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Throwables; @@ -36,6 +38,7 @@ import java.nio.charset.StandardCharsets; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; @@ -58,7 +61,14 @@ public final class ResourceReaper { private static final Logger LOGGER = LoggerFactory.getLogger(ResourceReaper.class); - private static final List>> DEATH_NOTE = new ArrayList<>(); + private static final List>> DEATH_NOTE = new ArrayList<>( + Arrays.asList( + DockerClientFactory.DEFAULT_LABELS.entrySet().stream() + .>map(it -> new SimpleEntry<>("label", it.getKey() + "=" + it.getValue())) + .collect(Collectors.toList()) + ) + ); + private static final RateLimiter RYUK_ACK_RATE_LIMITER = RateLimiterBuilder .newBuilder() .withRate(4, TimeUnit.SECONDS) @@ -66,16 +76,13 @@ public final class ResourceReaper { .build(); private static ResourceReaper instance; - private final DockerClient dockerClient; + private static AtomicBoolean ryukStarted = new AtomicBoolean(false); + private final DockerClient dockerClient = DockerClientFactory.lazyClient(); private Map registeredContainers = new ConcurrentHashMap<>(); private Set registeredNetworks = Sets.newConcurrentHashSet(); private Set registeredImages = Sets.newConcurrentHashSet(); private AtomicBoolean hookIsSet = new AtomicBoolean(false); - private ResourceReaper() { - dockerClient = DockerClientFactory.instance().client(); - } - /** * @@ -173,14 +180,6 @@ public InspectContainerResponse getContainerInfo() { CountDownLatch ryukScheduledLatch = new CountDownLatch(1); - synchronized (DEATH_NOTE) { - DEATH_NOTE.add( - DockerClientFactory.DEFAULT_LABELS.entrySet().stream() - .>map(it -> new SimpleEntry<>("label", it.getKey() + "=" + it.getValue())) - .collect(Collectors.toList()) - ); - } - String host = containerState.getHost(); Integer ryukPort = containerState.getFirstMappedPort(); Thread kiraThread = new Thread( @@ -238,6 +237,7 @@ public InspectContainerResponse getContainerInfo() { } } + ryukStarted.set(true); return ryukContainerId; } @@ -253,7 +253,7 @@ public synchronized static ResourceReaper instance() { * Perform a cleanup. */ public synchronized void performCleanup() { - registeredContainers.forEach(this::stopContainer); + registeredContainers.forEach(this::removeContainer); registeredNetworks.forEach(this::removeNetwork); registeredImages.forEach(this::removeImage); } @@ -262,7 +262,9 @@ public synchronized void performCleanup() { * Register a filter to be cleaned up. * * @param filter the filter + * @deprecated only label filter is supported by the prune API, use {@link #registerLabelsFilterForCleanup(Map)} */ + @Deprecated public void registerFilterForCleanup(List> filter) { synchronized (DEATH_NOTE) { DEATH_NOTE.add(filter); @@ -270,6 +272,19 @@ public void registerFilterForCleanup(List> filter) { } } + /** + * Register a label to be cleaned up. + * + * @param labels the filter + */ + public void registerLabelsFilterForCleanup(Map labels) { + registerFilterForCleanup( + labels.entrySet().stream() + .map(it -> new SimpleEntry<>("label", it.getKey() + "=" + it.getValue())) + .collect(Collectors.toList()) + ); + } + /** * Register a container to be cleaned up, either on explicit call to stopAndRemoveContainer, or at JVM shutdown. * @@ -287,7 +302,7 @@ public void registerContainerForCleanup(String containerId, String imageName) { * @param containerId the ID of the container */ public void stopAndRemoveContainer(String containerId) { - stopContainer(containerId, registeredContainers.get(containerId)); + removeContainer(containerId, registeredContainers.get(containerId)); registeredContainers.remove(containerId); } @@ -299,12 +314,12 @@ public void stopAndRemoveContainer(String containerId) { * @param imageName the image name of the container (used for logging) */ public void stopAndRemoveContainer(String containerId, String imageName) { - stopContainer(containerId, imageName); + removeContainer(containerId, imageName); registeredContainers.remove(containerId); } - private void stopContainer(String containerId, String imageName) { + private void removeContainer(String containerId, String imageName) { boolean running; try { InspectContainerResponse containerInfo = dockerClient.inspectContainerCmd(containerId).exec(); @@ -444,10 +459,52 @@ private void removeImage(String dockerImageName) { } } - private void setHook() { + private void prune(PruneType pruneType, List> filters) { + String[] labels = filters.stream() + .filter(it -> "label".equals(it.getKey())) + .map(Map.Entry::getValue) + .toArray(String[]::new); + switch (pruneType) { + // Docker only prunes stopped containers, so we have to do it manually + case CONTAINERS: + List containers = dockerClient.listContainersCmd() + .withFilter("label", Arrays.asList(labels)) + .withShowAll(true) + .exec(); + + containers.parallelStream().forEach(container -> { + removeContainer(container.getId(), container.getImage()); + }); + break; + default: + dockerClient.pruneCmd(pruneType).withLabelFilter(labels).exec(); + break; + } + } + + /** + * @deprecated internal API, not intended for public usage + */ + @Deprecated + public void setHook() { if (hookIsSet.compareAndSet(false, true)) { // If the JVM stops without containers being stopped, try and stop the container. - Runtime.getRuntime().addShutdownHook(new Thread(DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, this::performCleanup)); + Runtime.getRuntime().addShutdownHook( + new Thread(DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, + () -> { + performCleanup(); + + if (!ryukStarted.get()) { + synchronized (DEATH_NOTE) { + DEATH_NOTE.forEach(filters -> prune(PruneType.CONTAINERS, filters)); + DEATH_NOTE.forEach(filters -> prune(PruneType.NETWORKS, filters)); + DEATH_NOTE.forEach(filters -> prune(PruneType.VOLUMES, filters)); + DEATH_NOTE.forEach(filters -> prune(PruneType.IMAGES, filters)); + } + } + } + ) + ); } } From 53538f4898dad0a0989b40e6f7532a4178df326e Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Tue, 1 Feb 2022 11:18:05 +0100 Subject: [PATCH 2348/5615] Remove the INCUBATING label from CockroachDB module docs (#4963) --- docs/modules/databases/cockroachdb.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/modules/databases/cockroachdb.md b/docs/modules/databases/cockroachdb.md index 540f4820a84..17c5efae8f8 100644 --- a/docs/modules/databases/cockroachdb.md +++ b/docs/modules/databases/cockroachdb.md @@ -1,8 +1,5 @@ # CockroachDB Module -!!! note - This module is INCUBATING. While it is ready for use and operational in the current version of Testcontainers, it is possible that it may receive breaking changes in the future. See [our contributing guidelines](/contributing/#incubating-modules) for more information on our incubating modules policy. - See [Database containers](./index.md) for documentation and usage that is common to all relational database container types. ## Adding this module to your project dependencies From 0b12ed23ed757347c81d9e55847d73d9bc6de2dd Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 1 Feb 2022 05:38:11 -0500 Subject: [PATCH 2349/5615] Extract `RyukResourceReaper` (#4959) --- .../utility/ResourceReaper.java | 172 +--------------- .../utility/RyukResourceReaper.java | 188 ++++++++++++++++++ 2 files changed, 192 insertions(+), 168 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index 8c2cd3a6108..69c849b6c82 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -1,30 +1,18 @@ package org.testcontainers.utility; import com.github.dockerjava.api.DockerClient; -import com.github.dockerjava.api.async.ResultCallback; import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.exception.NotFoundException; -import com.github.dockerjava.api.model.Bind; import com.github.dockerjava.api.model.Container; -import com.github.dockerjava.api.model.ExposedPort; -import com.github.dockerjava.api.model.Frame; -import com.github.dockerjava.api.model.HostConfig; import com.github.dockerjava.api.model.Network; -import com.github.dockerjava.api.model.PortBinding; -import com.github.dockerjava.api.model.Ports; import com.github.dockerjava.api.model.PruneType; -import com.github.dockerjava.api.model.Volume; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Throwables; import com.google.common.collect.Sets; -import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import org.rnorth.ducttape.ratelimits.RateLimiter; -import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testcontainers.DockerClientFactory; -import org.testcontainers.containers.ContainerState; import java.io.BufferedReader; import java.io.IOException; @@ -32,26 +20,16 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.io.UnsupportedEncodingException; -import java.net.InetSocketAddress; -import java.net.Socket; import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.awaitility.Awaitility.await; /** * Component that responsible for container removal and automatic cleanup of dead containers at JVM shutdown. @@ -61,7 +39,7 @@ public final class ResourceReaper { private static final Logger LOGGER = LoggerFactory.getLogger(ResourceReaper.class); - private static final List>> DEATH_NOTE = new ArrayList<>( + static final List>> DEATH_NOTE = new ArrayList<>( Arrays.asList( DockerClientFactory.DEFAULT_LABELS.entrySet().stream() .>map(it -> new SimpleEntry<>("label", it.getKey() + "=" + it.getValue())) @@ -69,12 +47,6 @@ public final class ResourceReaper { ) ); - private static final RateLimiter RYUK_ACK_RATE_LIMITER = RateLimiterBuilder - .newBuilder() - .withRate(4, TimeUnit.SECONDS) - .withConstantThroughput() - .build(); - private static ResourceReaper instance; private static AtomicBoolean ryukStarted = new AtomicBoolean(false); private final DockerClient dockerClient = DockerClientFactory.lazyClient(); @@ -98,147 +70,11 @@ public static String start(String hostIpAddress, DockerClient client) { * @deprecated internal API */ @Deprecated - @SneakyThrows(InterruptedException.class) public static String start(DockerClient client) { - String ryukImage = ImageNameSubstitutor.instance() - .apply(DockerImageName.parse("testcontainers/ryuk:0.3.3")) - .asCanonicalNameString(); - DockerClientFactory.instance().checkAndPullImage(client, ryukImage); - - List binds = new ArrayList<>(); - binds.add(new Bind(DockerClientFactory.instance().getRemoteDockerUnixSocketPath(), new Volume("/var/run/docker.sock"))); - - ExposedPort ryukExposedPort = ExposedPort.tcp(8080); - String ryukContainerId = client.createContainerCmd(ryukImage) - .withHostConfig( - new HostConfig() - .withAutoRemove(true) - .withPortBindings(new PortBinding(Ports.Binding.empty(), ryukExposedPort)) - ) - .withExposedPorts(ryukExposedPort) - .withName("testcontainers-ryuk-" + DockerClientFactory.SESSION_ID) - .withLabels(Collections.singletonMap(DockerClientFactory.TESTCONTAINERS_LABEL, "true")) - .withBinds(binds) - .withPrivileged(TestcontainersConfiguration.getInstance().isRyukPrivileged()) - .exec() - .getId(); - - client.startContainerCmd(ryukContainerId).exec(); - - StringBuilder ryukLog = new StringBuilder(); - - ResultCallback.Adapter logCallback = client.logContainerCmd(ryukContainerId) - .withSince(0) - .withFollowStream(true) - .withStdOut(true) - .withStdErr(true) - .exec(new ResultCallback.Adapter() { - @Override - public void onNext(Frame frame) { - ryukLog.append(new String(frame.getPayload(), StandardCharsets.UTF_8)); - } - }); - - InspectContainerResponse inspectedContainer; - try { - // inspect container response might initially not contain the mapped port - inspectedContainer = await() - .atMost(5, TimeUnit.SECONDS) - .pollInterval(DynamicPollInterval.ofMillis(50)) - .pollInSameThread() - .until( - () -> client.inspectContainerCmd(ryukContainerId).exec(), - inspectContainerResponse -> { - return inspectContainerResponse - .getNetworkSettings() - .getPorts() - .getBindings() - .values() - .stream() - .anyMatch(Objects::nonNull); - } - ); - } catch (Exception e) { - log.warn("Ryuk container cannot be inspected and probably had a problem starting. Ryuk's logs:\n{}", ryukLog); - throw new IllegalStateException("Ryuk failed to start", e); - } - - ContainerState containerState = new ContainerState() { - - @Override - public List getExposedPorts() { - return Stream.of(getContainerInfo().getConfig().getExposedPorts()) - .map(ExposedPort::getPort) - .collect(Collectors.toList()); - } - - @Override - public InspectContainerResponse getContainerInfo() { - return inspectedContainer; - } - }; - - CountDownLatch ryukScheduledLatch = new CountDownLatch(1); - - String host = containerState.getHost(); - Integer ryukPort = containerState.getFirstMappedPort(); - Thread kiraThread = new Thread( - DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, - () -> { - while (true) { - RYUK_ACK_RATE_LIMITER.doWhenReady(() -> { - int index = 0; - // not set the read timeout, as Ryuk would not send anything unless a new filter is submitted, meaning that we would get a timeout exception pretty quick - try (Socket clientSocket = new Socket()) { - clientSocket.connect(new InetSocketAddress(host, ryukPort), 5 * 1000); - FilterRegistry registry = new FilterRegistry(clientSocket.getInputStream(), clientSocket.getOutputStream()); - - synchronized (DEATH_NOTE) { - while (true) { - if (DEATH_NOTE.size() <= index) { - try { - DEATH_NOTE.wait(1_000); - continue; - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - List> filters = DEATH_NOTE.get(index); - boolean isAcknowledged = registry.register(filters); - if (isAcknowledged) { - log.debug("Received 'ACK' from Ryuk"); - ryukScheduledLatch.countDown(); - index++; - } else { - log.debug("Didn't receive 'ACK' from Ryuk. Will retry to send filters."); - } - } - } - } catch (IOException e) { - log.warn("Can not connect to Ryuk at {}:{}", host, ryukPort, e); - } - }); - } - }, - "testcontainers-ryuk" - ); - kiraThread.setDaemon(true); - kiraThread.start(); - try { - // We need to wait before we can start any containers to make sure that we delete them - if (!ryukScheduledLatch.await(TestcontainersConfiguration.getInstance().getRyukTimeout(), TimeUnit.SECONDS)) { - log.error("Timed out waiting for Ryuk container to start. Ryuk's logs:\n{}", ryukLog); - throw new IllegalStateException(String.format("Could not connect to Ryuk at %s:%s", host, ryukPort)); - } - } finally { - try { - logCallback.close(); - } catch (IOException ignored) { - } - } - + RyukResourceReaper ryuk = new RyukResourceReaper(client); + String containerId = ryuk.getContainerId(); ryukStarted.set(true); - return ryukContainerId; + return containerId; } public synchronized static ResourceReaper instance() { diff --git a/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java b/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java new file mode 100644 index 00000000000..e8aed92fa9d --- /dev/null +++ b/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java @@ -0,0 +1,188 @@ +package org.testcontainers.utility; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.async.ResultCallback; +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.model.Bind; +import com.github.dockerjava.api.model.ExposedPort; +import com.github.dockerjava.api.model.Frame; +import com.github.dockerjava.api.model.HostConfig; +import com.github.dockerjava.api.model.PortBinding; +import com.github.dockerjava.api.model.Ports; +import com.github.dockerjava.api.model.Volume; +import lombok.Getter; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.rnorth.ducttape.ratelimits.RateLimiter; +import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; +import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.ContainerState; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.awaitility.Awaitility.await; + +@Slf4j +class RyukResourceReaper { + + private static final RateLimiter RYUK_ACK_RATE_LIMITER = RateLimiterBuilder + .newBuilder() + .withRate(4, TimeUnit.SECONDS) + .withConstantThroughput() + .build(); + + @Getter + private final String containerId; + + @SneakyThrows(InterruptedException.class) + RyukResourceReaper(DockerClient client) { + String ryukImage = ImageNameSubstitutor.instance() + .apply(DockerImageName.parse("testcontainers/ryuk:0.3.3")) + .asCanonicalNameString(); + DockerClientFactory.instance().checkAndPullImage(client, ryukImage); + + List binds = new ArrayList<>(); + binds.add(new Bind(DockerClientFactory.instance().getRemoteDockerUnixSocketPath(), new Volume("/var/run/docker.sock"))); + + ExposedPort ryukExposedPort = ExposedPort.tcp(8080); + containerId = client.createContainerCmd(ryukImage) + .withHostConfig( + new HostConfig() + .withAutoRemove(true) + .withPortBindings(new PortBinding(Ports.Binding.empty(), ryukExposedPort)) + ) + .withExposedPorts(ryukExposedPort) + .withName("testcontainers-ryuk-" + DockerClientFactory.SESSION_ID) + .withLabels(Collections.singletonMap(DockerClientFactory.TESTCONTAINERS_LABEL, "true")) + .withBinds(binds) + .withPrivileged(TestcontainersConfiguration.getInstance().isRyukPrivileged()) + .exec() + .getId(); + + client.startContainerCmd(containerId).exec(); + + StringBuilder ryukLog = new StringBuilder(); + + ResultCallback.Adapter logCallback = client.logContainerCmd(containerId) + .withSince(0) + .withFollowStream(true) + .withStdOut(true) + .withStdErr(true) + .exec(new ResultCallback.Adapter() { + @Override + public void onNext(Frame frame) { + ryukLog.append(new String(frame.getPayload(), StandardCharsets.UTF_8)); + } + }); + + InspectContainerResponse inspectedContainer; + try { + // inspect container response might initially not contain the mapped port + inspectedContainer = await() + .atMost(5, TimeUnit.SECONDS) + .pollInterval(DynamicPollInterval.ofMillis(50)) + .pollInSameThread() + .until( + () -> client.inspectContainerCmd(containerId).exec(), + inspectContainerResponse -> { + return inspectContainerResponse + .getNetworkSettings() + .getPorts() + .getBindings() + .values() + .stream() + .anyMatch(Objects::nonNull); + } + ); + } catch (Exception e) { + log.warn("Ryuk container cannot be inspected and probably had a problem starting. Ryuk's logs:\n{}", ryukLog); + throw new IllegalStateException("Ryuk failed to start", e); + } + + ContainerState containerState = new ContainerState() { + + @Override + public List getExposedPorts() { + return Stream.of(getContainerInfo().getConfig().getExposedPorts()) + .map(ExposedPort::getPort) + .collect(Collectors.toList()); + } + + @Override + public InspectContainerResponse getContainerInfo() { + return inspectedContainer; + } + }; + + CountDownLatch ryukScheduledLatch = new CountDownLatch(1); + + String host = containerState.getHost(); + Integer ryukPort = containerState.getFirstMappedPort(); + Thread kiraThread = new Thread( + DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, + () -> { + while (true) { + RYUK_ACK_RATE_LIMITER.doWhenReady(() -> { + int index = 0; + // not set the read timeout, as Ryuk would not send anything unless a new filter is submitted, meaning that we would get a timeout exception pretty quick + try (Socket clientSocket = new Socket()) { + clientSocket.connect(new InetSocketAddress(host, ryukPort), 5 * 1000); + ResourceReaper.FilterRegistry registry = new ResourceReaper.FilterRegistry(clientSocket.getInputStream(), clientSocket.getOutputStream()); + + synchronized (ResourceReaper.DEATH_NOTE) { + while (true) { + if (ResourceReaper.DEATH_NOTE.size() <= index) { + try { + ResourceReaper.DEATH_NOTE.wait(1_000); + continue; + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + List> filters = ResourceReaper.DEATH_NOTE.get(index); + boolean isAcknowledged = registry.register(filters); + if (isAcknowledged) { + log.debug("Received 'ACK' from Ryuk"); + ryukScheduledLatch.countDown(); + index++; + } else { + log.debug("Didn't receive 'ACK' from Ryuk. Will retry to send filters."); + } + } + } + } catch (IOException e) { + log.warn("Can not connect to Ryuk at {}:{}", host, ryukPort, e); + } + }); + } + }, + "testcontainers-ryuk" + ); + kiraThread.setDaemon(true); + kiraThread.start(); + try { + // We need to wait before we can start any containers to make sure that we delete them + if (!ryukScheduledLatch.await(TestcontainersConfiguration.getInstance().getRyukTimeout(), TimeUnit.SECONDS)) { + log.error("Timed out waiting for Ryuk container to start. Ryuk's logs:\n{}", ryukLog); + throw new IllegalStateException(String.format("Could not connect to Ryuk at %s:%s", host, ryukPort)); + } + } finally { + try { + logCallback.close(); + } catch (IOException ignored) { + } + } + } +} From fd6e581f9ab5e904ab30709e614c8ad2b8221871 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:00:34 +0000 Subject: [PATCH 2350/5615] Bump java-client from 3.2.4 to 3.2.5 in /modules/couchbase Bumps [java-client](https://github.com/couchbase/couchbase-jvm-clients) from 3.2.4 to 3.2.5. - [Release notes](https://github.com/couchbase/couchbase-jvm-clients/releases) - [Commits](https://github.com/couchbase/couchbase-jvm-clients/compare/java-client-3.2.4...java-client-3.2.5) --- updated-dependencies: - dependency-name: com.couchbase.client:java-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index ea720ca3725..2c68f163a57 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Couchbase" dependencies { api project(':testcontainers') - testImplementation 'com.couchbase.client:java-client:3.2.4' + testImplementation 'com.couchbase.client:java-client:3.2.5' testImplementation 'org.awaitility:awaitility:4.1.1' } From 865a5c1c132cdc9765f6fae06607870661429a90 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:00:35 +0000 Subject: [PATCH 2351/5615] Bump tomcat-jdbc from 10.0.14 to 10.0.16 in /modules/jdbc Bumps tomcat-jdbc from 10.0.14 to 10.0.16. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index a6f71690e1e..71940e0026a 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -7,7 +7,7 @@ dependencies { compileOnly 'org.jetbrains:annotations:23.0.0' testImplementation 'commons-dbutils:commons-dbutils:1.7' testImplementation 'org.vibur:vibur-dbcp:25.0' - testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.14' + testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.16' testImplementation 'com.zaxxer:HikariCP-java6:2.3.13' testImplementation 'com.googlecode.junit-toolbox:junit-toolbox:2.4' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From cc031c6b64666812a1c9f75f9d0ae65a647bae83 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:00:36 +0000 Subject: [PATCH 2352/5615] Bump tomcat-jdbc from 10.0.14 to 10.0.16 in /modules/jdbc-test Bumps tomcat-jdbc from 10.0.14 to 10.0.16. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 46088aa45ac..98570722e6a 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -14,7 +14,7 @@ dependencies { exclude(group: "net.java.dev.jna", module: "jna") } - api 'org.apache.tomcat:tomcat-jdbc:10.0.14' + api 'org.apache.tomcat:tomcat-jdbc:10.0.16' api 'org.vibur:vibur-dbcp:25.0' api 'mysql:mysql-connector-java:8.0.27' } From a080cc5f4e764aa5e7edbe20e4dd8841e6e9e834 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:00:37 +0000 Subject: [PATCH 2353/5615] Bump neo4j-java-driver from 4.4.2 to 4.4.3 in /modules/neo4j Bumps [neo4j-java-driver](https://github.com/neo4j/neo4j-java-driver) from 4.4.2 to 4.4.3. - [Release notes](https://github.com/neo4j/neo4j-java-driver/releases) - [Commits](https://github.com/neo4j/neo4j-java-driver/compare/4.4.2...4.4.3) --- updated-dependencies: - dependency-name: org.neo4j.driver:neo4j-java-driver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index fe99774538c..1d5cffc1955 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -33,6 +33,6 @@ dependencies { api project(":testcontainers") - testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.2' + testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.3' testImplementation 'org.assertj:assertj-core:3.22.0' } From 4cf33a40bf1ab7a36289234a6b27d65b200cfb94 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:00:38 +0000 Subject: [PATCH 2354/5615] Bump aws-java-sdk-dynamodb in /modules/dynalite Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.12.137 to 1.12.150. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.137...1.12.150) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-dynamodb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/dynalite/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 52881514ac5..ce09dde17e6 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Dynalite" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.137' - testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.131' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.150' + testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.150' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 38bd2919b94344113b1e782d3d580ac4992a824d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:00:38 +0000 Subject: [PATCH 2355/5615] Bump mariadb-java-client from 2.7.4 to 3.0.3 in /modules/mariadb Bumps [mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 2.7.4 to 3.0.3. - [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases) - [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md) - [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/2.7.4...3.0.3) --- updated-dependencies: - dependency-name: org.mariadb.jdbc:mariadb-java-client dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index af67719f8ad..8d65ed00deb 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'org.mariadb:r2dbc-mariadb:1.0.3' testImplementation project(':jdbc-test') - testImplementation 'org.mariadb.jdbc:mariadb-java-client:2.7.4' + testImplementation 'org.mariadb.jdbc:mariadb-java-client:3.0.3' testImplementation testFixtures(project(':r2dbc')) testImplementation 'org.mariadb:r2dbc-mariadb:1.0.2' From 04e88c03810a97c38d306223ba4f26b8d8d79ccd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:00:38 +0000 Subject: [PATCH 2356/5615] Bump aws-java-sdk-sqs from 1.12.139 to 1.12.150 in /modules/localstack Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.12.139 to 1.12.150. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.139...1.12.150) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-sqs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 7bfec27ccaf..6e0f620beed 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.137' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.137' - testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.139' + testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.150' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.139' testImplementation 'software.amazon.awssdk:s3:2.17.108' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From 7b8edbda16c4b9c962b568e71212159ffe93c0d3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:00:39 +0000 Subject: [PATCH 2357/5615] Bump testcontainers from 1.16.2 to 1.16.3 in /core Bumps [testcontainers](https://github.com/testcontainers/testcontainers-java) from 1.16.2 to 1.16.3. - [Release notes](https://github.com/testcontainers/testcontainers-java/releases) - [Changelog](https://github.com/testcontainers/testcontainers-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-java/compare/1.16.2...1.16.3) --- updated-dependencies: - dependency-name: org.testcontainers:testcontainers dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 074273e8fc7..a5e627a77aa 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -53,7 +53,7 @@ configurations.all { } dependencies { - baseline 'org.testcontainers:testcontainers:1.16.2', { + baseline 'org.testcontainers:testcontainers:1.16.3', { exclude group: "*", module: "*" } From cf2e9d83ccea626f1acb758cb8afde0effc9a633 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:00:40 +0000 Subject: [PATCH 2358/5615] Bump mysql-connector-java from 8.0.27 to 8.0.28 in /modules/spock Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.27 to 8.0.28. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.27...8.0.28) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index cec493cf77d..26158b6db57 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -16,7 +16,7 @@ dependencies { testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' testRuntimeOnly 'org.postgresql:postgresql:42.3.1' - testRuntimeOnly 'mysql:mysql-connector-java:8.0.27' + testRuntimeOnly 'mysql:mysql-connector-java:8.0.28' testCompileClasspath 'org.jetbrains:annotations:23.0.0' } From 293059233d4ae97bd5c852dc6e2b6d34843640d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:00:41 +0000 Subject: [PATCH 2359/5615] Bump jedis from 4.0.1 to 4.1.1 in /examples Bumps [jedis](https://github.com/redis/jedis) from 4.0.1 to 4.1.1. - [Release notes](https://github.com/redis/jedis/releases) - [Commits](https://github.com/redis/jedis/compare/v4.0.1...v4.1.1) --- updated-dependencies: - dependency-name: redis.clients:jedis dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index c60d5f08cf4..ec3d0bd134c 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' - implementation 'redis.clients:jedis:4.0.1' + implementation 'redis.clients:jedis:4.1.1' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index dff9ae7024f..32e35f07766 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' - implementation 'redis.clients:jedis:4.0.1' + implementation 'redis.clients:jedis:4.1.1' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index a56f269fa40..2ebaba5be50 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { - implementation 'redis.clients:jedis:4.0.1' + implementation 'redis.clients:jedis:4.1.1' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.32' From 98bf56bbfb27f21b9020f97ca6288a2cb0da6421 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:00:41 +0000 Subject: [PATCH 2360/5615] Bump mockito-core from 4.2.0 to 4.3.1 in /modules/junit-jupiter Bumps [mockito-core](https://github.com/mockito/mockito) from 4.2.0 to 4.3.1. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.2.0...v4.3.1) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 77ff3f842d0..2069545531d 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,7 +9,7 @@ dependencies { testImplementation 'com.zaxxer:HikariCP:4.0.3' testImplementation 'redis.clients:jedis:4.0.1' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' - testImplementation ('org.mockito:mockito-core:4.2.0') { + testImplementation ('org.mockito:mockito-core:4.3.1') { exclude(module: 'hamcrest-core') } testImplementation 'org.assertj:assertj-core:3.22.0' From ca9423cfe0b83417a795cfe05edb6ff6527c7d73 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:00:42 +0000 Subject: [PATCH 2361/5615] Bump transport from 7.16.2 to 7.17.0 in /modules/elasticsearch Bumps [transport](https://github.com/elastic/elasticsearch) from 7.16.2 to 7.17.0. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.16.2...v7.17.0) --- updated-dependencies: - dependency-name: org.elasticsearch.client:transport dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 3f517353f24..fca8ecfe9fb 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,6 +3,6 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') testImplementation "org.elasticsearch.client:elasticsearch-rest-client:7.16.2" - testImplementation "org.elasticsearch.client:transport:7.16.2" + testImplementation "org.elasticsearch.client:transport:7.17.0" testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 7aabc275a4b810ab5200104b665d10b6a9c06e91 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:00:42 +0000 Subject: [PATCH 2362/5615] Bump aws-java-sdk-s3 from 1.12.137 to 1.12.150 in /modules/localstack Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.137 to 1.12.150. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.137...1.12.150) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 7bfec27ccaf..54a3f71a769 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.137' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.150' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.137' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.139' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.139' From 5cd78705c86800e790e3662e1191db62f6ff8455 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:00:44 +0000 Subject: [PATCH 2363/5615] Bump slf4j-api from 1.7.32 to 1.7.35 in /core Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 1.7.32 to 1.7.35. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_1.7.32...v_1.7.35) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 074273e8fc7..f9b373083ad 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -58,7 +58,7 @@ dependencies { } api 'junit:junit:4.13.2' - api 'org.slf4j:slf4j-api:1.7.32' + api 'org.slf4j:slf4j-api:1.7.35' compileOnly 'org.jetbrains:annotations:23.0.0' testCompileClasspath 'org.jetbrains:annotations:23.0.0' api 'org.apache.commons:commons-compress:1.21' From 0f0398f4a78f86c43baff8fab62cab80112fc647 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:00:44 +0000 Subject: [PATCH 2364/5615] Bump org.springframework.boot from 2.6.2 to 2.6.3 in /examples Bumps [org.springframework.boot](https://github.com/spring-projects/spring-boot) from 2.6.2 to 2.6.3. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.6.2...v2.6.3) --- updated-dependencies: - dependency-name: org.springframework.boot dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot-kotlin-redis/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index 495c279937a..d728c13e3c0 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.6.2' + id 'org.springframework.boot' version '2.6.3' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 25d92d3ea16..5a9495d86bf 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,5 +1,5 @@ plugins { - id("org.springframework.boot") version "2.6.2" + id("org.springframework.boot") version "2.6.3" id("org.jetbrains.kotlin.jvm") version "1.6.10" id("org.jetbrains.kotlin.plugin.spring") version "1.6.10" } diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index 09b82c34ea7..ace47d12ebe 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.6.2' + id 'org.springframework.boot' version '2.6.3' } apply plugin: 'io.spring.dependency-management' From 8ed5e42c62f6b6ebc16f50de948f3f029ec7a95e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:00:44 +0000 Subject: [PATCH 2365/5615] Bump jedis from 4.0.1 to 4.1.1 in /modules/junit-jupiter Bumps [jedis](https://github.com/redis/jedis) from 4.0.1 to 4.1.1. - [Release notes](https://github.com/redis/jedis/releases) - [Commits](https://github.com/redis/jedis/compare/v4.0.1...v4.1.1) --- updated-dependencies: - dependency-name: redis.clients:jedis dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 77ff3f842d0..0c2ffd8d41d 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -7,7 +7,7 @@ dependencies { testImplementation project(':mysql') testImplementation project(':postgresql') testImplementation 'com.zaxxer:HikariCP:4.0.3' - testImplementation 'redis.clients:jedis:4.0.1' + testImplementation 'redis.clients:jedis:4.1.1' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' testImplementation ('org.mockito:mockito-core:4.2.0') { exclude(module: 'hamcrest-core') From b24418439fe7280f23a399cf4b1c25953daac580 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:00:46 +0000 Subject: [PATCH 2366/5615] Bump postgresql from 42.3.1 to 42.3.2 in /modules/junit-jupiter Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.1 to 42.3.2. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.1...REL42.3.2) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 77ff3f842d0..f450bd15a24 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'org.assertj:assertj-core:3.22.0' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2' - testRuntimeOnly 'org.postgresql:postgresql:42.3.1' + testRuntimeOnly 'org.postgresql:postgresql:42.3.2' testRuntimeOnly 'mysql:mysql-connector-java:8.0.27' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2' } From 8899d1ea4d4f245fb3f0ef5f0e6817e322ed94c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:00:47 +0000 Subject: [PATCH 2367/5615] Bump postgresql from 42.3.1 to 42.3.2 in /examples Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.1 to 42.3.2. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.1...REL42.3.2) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 5ca68b2c514..37fc1b71226 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' implementation 'com.squareup.okhttp3:okhttp:4.9.3' implementation 'org.json:json:20211205' - testImplementation 'org.postgresql:postgresql:42.3.1' + testImplementation 'org.postgresql:postgresql:42.3.2' testImplementation 'ch.qos.logback:logback-classic:1.2.10' testImplementation 'org.testcontainers:postgresql' } From fc2a95e0e66553a1970f04b3d85c200753d6d8ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:00:47 +0000 Subject: [PATCH 2368/5615] Bump mockito-core from 4.2.0 to 4.3.1 in /core Bumps [mockito-core](https://github.com/mockito/mockito) from 4.2.0 to 4.3.1. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.2.0...v4.3.1) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 074273e8fc7..e835eac8b38 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -93,7 +93,7 @@ dependencies { testImplementation 'com.rabbitmq:amqp-client:5.14.0' testImplementation 'org.mongodb:mongo-java-driver:3.12.10' - testImplementation ('org.mockito:mockito-core:4.2.0') { + testImplementation ('org.mockito:mockito-core:4.3.1') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From 2e520b2bd3a9e23019b4953ea3c1d7d1454c4be6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:00:50 +0000 Subject: [PATCH 2369/5615] Bump amqp-client from 5.14.0 to 5.14.1 in /core Bumps [amqp-client](https://github.com/rabbitmq/rabbitmq-java-client) from 5.14.0 to 5.14.1. - [Release notes](https://github.com/rabbitmq/rabbitmq-java-client/releases) - [Commits](https://github.com/rabbitmq/rabbitmq-java-client/compare/v5.14.0...v5.14.1) --- updated-dependencies: - dependency-name: com.rabbitmq:amqp-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 074273e8fc7..c5c5771f649 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -90,7 +90,7 @@ dependencies { testImplementation 'org.apache.httpcomponents:httpclient:4.5.9' testImplementation 'redis.clients:jedis:4.0.1' - testImplementation 'com.rabbitmq:amqp-client:5.14.0' + testImplementation 'com.rabbitmq:amqp-client:5.14.1' testImplementation 'org.mongodb:mongo-java-driver:3.12.10' testImplementation ('org.mockito:mockito-core:4.2.0') { From 7085b4f1494b4a491f3ac896b8416924f20cc536 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:00:50 +0000 Subject: [PATCH 2370/5615] Bump kafka-clients from 3.0.0 to 3.1.0 in /examples Bumps kafka-clients from 3.0.0 to 3.1.0. --- updated-dependencies: - dependency-name: org.apache.kafka:kafka-clients dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/kafka-cluster/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index 6351be31488..2e37410bcea 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -10,7 +10,7 @@ dependencies { testCompileOnly "org.projectlombok:lombok:1.18.22" testAnnotationProcessor "org.projectlombok:lombok:1.18.22" testImplementation 'org.testcontainers:kafka' - testImplementation 'org.apache.kafka:kafka-clients:3.0.0' + testImplementation 'org.apache.kafka:kafka-clients:3.1.0' testImplementation 'org.assertj:assertj-core:3.22.0' testImplementation 'com.google.guava:guava:23.0' testImplementation 'ch.qos.logback:logback-classic:1.2.10' From 3ac2016e162186c7383b2c73c54084a78d940ec7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:00:54 +0000 Subject: [PATCH 2371/5615] Bump cucumber-java from 7.2.2 to 7.2.3 in /examples Bumps [cucumber-java](https://github.com/cucumber/cucumber-jvm) from 7.2.2 to 7.2.3. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.2.2...v7.2.3) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 4f462ed4740..9a402b51b47 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' - testImplementation 'io.cucumber:cucumber-java:7.2.2' + testImplementation 'io.cucumber:cucumber-java:7.2.3' testImplementation 'io.cucumber:cucumber-junit:7.2.2' testImplementation 'org.testcontainers:selenium' } From eaa6a9f0fbffa61e9dfbce97262ec704a1a51459 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:01:23 +0000 Subject: [PATCH 2372/5615] Bump release-drafter/release-drafter from 5.15.0 to 5.17.6 Bumps [release-drafter/release-drafter](https://github.com/release-drafter/release-drafter) from 5.15.0 to 5.17.6. - [Release notes](https://github.com/release-drafter/release-drafter/releases) - [Commits](https://github.com/release-drafter/release-drafter/compare/fe52e97d262833ae07d05efaf1a239df3f1b5cd4...436492609a0c75979acf131d57f61b5321571950) --- updated-dependencies: - dependency-name: release-drafter/release-drafter dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/release-drafter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 629280b1241..aec34acb8ff 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -12,6 +12,6 @@ jobs: if: github.repository == 'testcontainers/testcontainers-java' runs-on: ubuntu-latest steps: - - uses: release-drafter/release-drafter@fe52e97d262833ae07d05efaf1a239df3f1b5cd4 # v5.15.0 + - uses: release-drafter/release-drafter@436492609a0c75979acf131d57f61b5321571950 # v5.15.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 9100051f213d5c8391ae4730a1011e797f833349 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:01:26 +0000 Subject: [PATCH 2373/5615] Bump gradle-update/update-gradle-wrapper-action from 1.0.16 to 1.0.17 Bumps [gradle-update/update-gradle-wrapper-action](https://github.com/gradle-update/update-gradle-wrapper-action) from 1.0.16 to 1.0.17. - [Release notes](https://github.com/gradle-update/update-gradle-wrapper-action/releases) - [Changelog](https://github.com/gradle-update/update-gradle-wrapper-action/blob/master/CHANGELOG.md) - [Commits](https://github.com/gradle-update/update-gradle-wrapper-action/compare/f31ab392a9d75029f9fd0883d8d6745c0139a006...5b865a8b4d09fdf6afaaa20397ba68455bb48f0d) --- updated-dependencies: - dependency-name: gradle-update/update-gradle-wrapper-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/update-gradle-wrapper.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-gradle-wrapper.yml b/.github/workflows/update-gradle-wrapper.yml index 612aec0b406..253e37c23eb 100644 --- a/.github/workflows/update-gradle-wrapper.yml +++ b/.github/workflows/update-gradle-wrapper.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v2 - name: Update Gradle Wrapper - uses: gradle-update/update-gradle-wrapper-action@f31ab392a9d75029f9fd0883d8d6745c0139a006 # v1.0.13 + uses: gradle-update/update-gradle-wrapper-action@5b865a8b4d09fdf6afaaa20397ba68455bb48f0d # v1.0.13 with: repo-token: ${{ secrets.GITHUB_TOKEN }} labels: dependencies From 15456fc10ffdd1116fde31c666bb91b4d7435163 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:02:38 +0000 Subject: [PATCH 2374/5615] Bump peter-evans/create-pull-request from 3.12.0 to 3.12.1 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 3.12.0 to 3.12.1. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/dcd5fd746d53dd8de555c0f10bca6c35628be47a...f22a7da129c901513876a2380e2dae9f8e145330) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/update-docs-version.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index 418598f230d..2c7626c7ec7 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -17,7 +17,7 @@ jobs: sed -i "s/latest_version: .*/latest_version: ${GITHUB_REF##*/}/g" mkdocs.yml git diff - name: Create Pull Request - uses: peter-evans/create-pull-request@dcd5fd746d53dd8de555c0f10bca6c35628be47a # v3.10.1 + uses: peter-evans/create-pull-request@f22a7da129c901513876a2380e2dae9f8e145330 # v3.10.1 with: title: Update docs version to ${GITHUB_REF##*/} body: | From 60725f6dfa4d88b0e8e526519d5ef7b1dc7b3a33 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:05:58 +0000 Subject: [PATCH 2375/5615] Bump trino-jdbc from 367 to 369 in /modules/trino Bumps trino-jdbc from 367 to 369. --- updated-dependencies: - dependency-name: io.trino:trino-jdbc dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/trino/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/trino/build.gradle b/modules/trino/build.gradle index f9223c174a5..9bf0e24f06b 100644 --- a/modules/trino/build.gradle +++ b/modules/trino/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'io.trino:trino-jdbc:367' + testImplementation 'io.trino:trino-jdbc:369' compileOnly 'org.jetbrains:annotations:23.0.0' } From f5e943f31e5c1f51dc01cf3312ed64d060fd9c8d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:05:59 +0000 Subject: [PATCH 2376/5615] Bump postgresql from 42.3.1 to 42.3.2 in /modules/cockroachdb Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.1 to 42.3.2. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.1...REL42.3.2) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/cockroachdb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cockroachdb/build.gradle b/modules/cockroachdb/build.gradle index ee6a7e2731e..71011c6534b 100644 --- a/modules/cockroachdb/build.gradle +++ b/modules/cockroachdb/build.gradle @@ -4,5 +4,5 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'org.postgresql:postgresql:42.3.1' + testImplementation 'org.postgresql:postgresql:42.3.2' } From 900ae910073066def9c422016b25bab310840ff3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:06:00 +0000 Subject: [PATCH 2377/5615] Bump google-cloud-firestore from 3.0.10 to 3.0.11 in /modules/gcloud Bumps [google-cloud-firestore](https://github.com/googleapis/java-firestore) from 3.0.10 to 3.0.11. - [Release notes](https://github.com/googleapis/java-firestore/releases) - [Changelog](https://github.com/googleapis/java-firestore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-firestore/compare/v3.0.10...v3.0.11) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-firestore dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 07d0c06a04b..9cda3994e8d 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':testcontainers') testImplementation 'com.google.cloud:google-cloud-datastore:2.2.2' - testImplementation 'com.google.cloud:google-cloud-firestore:3.0.10' + testImplementation 'com.google.cloud:google-cloud-firestore:3.0.11' testImplementation 'com.google.cloud:google-cloud-pubsub:1.115.1' testImplementation 'com.google.cloud:google-cloud-spanner:6.17.4' testImplementation 'com.google.cloud:google-cloud-bigtable:2.5.1' From 9502c445d7eaddf899043a699791f39957961958 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:06:01 +0000 Subject: [PATCH 2378/5615] Bump amqp-client from 5.14.0 to 5.14.1 in /modules/rabbitmq Bumps [amqp-client](https://github.com/rabbitmq/rabbitmq-java-client) from 5.14.0 to 5.14.1. - [Release notes](https://github.com/rabbitmq/rabbitmq-java-client/releases) - [Commits](https://github.com/rabbitmq/rabbitmq-java-client/compare/v5.14.0...v5.14.1) --- updated-dependencies: - dependency-name: com.rabbitmq:amqp-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/rabbitmq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/rabbitmq/build.gradle b/modules/rabbitmq/build.gradle index 4b437605785..9b680b12968 100644 --- a/modules/rabbitmq/build.gradle +++ b/modules/rabbitmq/build.gradle @@ -2,7 +2,7 @@ description = "TestContainers :: RabbitMQ" dependencies { api project(":testcontainers") - testImplementation 'com.rabbitmq:amqp-client:5.14.0' + testImplementation 'com.rabbitmq:amqp-client:5.14.1' testImplementation 'org.assertj:assertj-core:3.22.0' compileOnly 'org.jetbrains:annotations:23.0.0' } From 8256b642dd017b4718513db5af3c5e8fef6c396b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:06:04 +0000 Subject: [PATCH 2379/5615] Bump jackson-dataformat-yaml from 2.8.8 to 2.13.1 in /modules/k3s Bumps [jackson-dataformat-yaml](https://github.com/FasterXML/jackson-dataformats-text) from 2.8.8 to 2.13.1. - [Release notes](https://github.com/FasterXML/jackson-dataformats-text/releases) - [Commits](https://github.com/FasterXML/jackson-dataformats-text/commits/jackson-dataformats-text-2.13.1) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.dataformat:jackson-dataformat-yaml dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/k3s/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/k3s/build.gradle b/modules/k3s/build.gradle index 95d2d3bc97c..85ee36652b3 100644 --- a/modules/k3s/build.gradle +++ b/modules/k3s/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: K3S" dependencies { api project(":testcontainers") - shaded 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.8.8' + shaded 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.1' testImplementation 'io.fabric8:kubernetes-client:5.11.0' testImplementation 'io.kubernetes:client-java:14.0.0' From dcf6bd01d4225ddf54193fd158983251a38b4ff1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:06:07 +0000 Subject: [PATCH 2380/5615] Bump assertj-core from 3.21.0 to 3.22.0 in /modules/k3s Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.21.0 to 3.22.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.21.0...assertj-core-3.22.0) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/k3s/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/k3s/build.gradle b/modules/k3s/build.gradle index 95d2d3bc97c..0674ee2b24c 100644 --- a/modules/k3s/build.gradle +++ b/modules/k3s/build.gradle @@ -7,5 +7,5 @@ dependencies { testImplementation 'io.fabric8:kubernetes-client:5.11.0' testImplementation 'io.kubernetes:client-java:14.0.0' - testImplementation 'org.assertj:assertj-core:3.21.0' + testImplementation 'org.assertj:assertj-core:3.22.0' } From bc4b8ada94a93a5f8790d58098ce098bf51eb11c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 22:06:09 +0000 Subject: [PATCH 2381/5615] Bump google-cloud-bigtable from 2.5.1 to 2.5.2 in /modules/gcloud Bumps [google-cloud-bigtable](https://github.com/googleapis/java-bigtable) from 2.5.1 to 2.5.2. - [Release notes](https://github.com/googleapis/java-bigtable/releases) - [Changelog](https://github.com/googleapis/java-bigtable/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-bigtable/compare/v2.5.1...v2.5.2) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-bigtable dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 07d0c06a04b..81dd0e9dff1 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-firestore:3.0.10' testImplementation 'com.google.cloud:google-cloud-pubsub:1.115.1' testImplementation 'com.google.cloud:google-cloud-spanner:6.17.4' - testImplementation 'com.google.cloud:google-cloud-bigtable:2.5.1' + testImplementation 'com.google.cloud:google-cloud-bigtable:2.5.2' testImplementation 'org.assertj:assertj-core:3.22.0' } From f66a6d256fa5e5f7b9b2c358a41953b5ca5699ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Feb 2022 14:14:38 +0000 Subject: [PATCH 2382/5615] Bump mysql-connector-java from 8.0.27 to 8.0.28 in /modules/jdbc-test Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.27 to 8.0.28. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.27...8.0.28) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 98570722e6a..2ea623f75a1 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -16,5 +16,5 @@ dependencies { api 'org.apache.tomcat:tomcat-jdbc:10.0.16' api 'org.vibur:vibur-dbcp:25.0' - api 'mysql:mysql-connector-java:8.0.27' + api 'mysql:mysql-connector-java:8.0.28' } From c5bec3cee6de41c89c0270da0b42fc126141ec7f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Feb 2022 14:14:43 +0000 Subject: [PATCH 2383/5615] Bump cucumber-junit from 7.2.2 to 7.2.3 in /examples Bumps [cucumber-junit](https://github.com/cucumber/cucumber-jvm) from 7.2.2 to 7.2.3. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.2.2...v7.2.3) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-junit dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 9a402b51b47..78d042b95fd 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' testImplementation 'io.cucumber:cucumber-java:7.2.3' - testImplementation 'io.cucumber:cucumber-junit:7.2.2' + testImplementation 'io.cucumber:cucumber-junit:7.2.3' testImplementation 'org.testcontainers:selenium' } From 8de57a2c31db71a3375a70580f73a2c7d702f7ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Feb 2022 14:14:47 +0000 Subject: [PATCH 2384/5615] Bump mysql-connector-java from 8.0.27 to 8.0.28 in /modules/mysql Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.27 to 8.0.28. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.27...8.0.28) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 88a4473f56c..1796ff17ef0 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' testImplementation project(':jdbc-test') - testImplementation 'mysql:mysql-connector-java:8.0.27' + testImplementation 'mysql:mysql-connector-java:8.0.28' testImplementation testFixtures(project(':r2dbc')) testImplementation 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' From 2d09155faa529074f53b10a2e7f828bf531efa2d Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 3 Feb 2022 15:04:50 +0000 Subject: [PATCH 2385/5615] Upgrade mkdocs (#3443) Co-authored-by: Kevin Wittek --- docs/contributing.md | 2 +- docs/index.md | 57 ++++---- docs/modules/azure.md | 26 ++-- docs/modules/databases/cassandra.md | 79 +++++------ docs/modules/databases/clickhouse.md | 24 ++-- docs/modules/databases/cockroachdb.md | 24 ++-- docs/modules/databases/couchbase.md | 25 ++-- docs/modules/databases/db2.md | 25 ++-- docs/modules/databases/dynalite.md | 25 ++-- docs/modules/databases/influxdb.md | 25 ++-- docs/modules/databases/mariadb.md | 25 ++-- docs/modules/databases/mongodb.md | 25 ++-- docs/modules/databases/mssqlserver.md | 25 ++-- docs/modules/databases/mysql.md | 25 ++-- docs/modules/databases/neo4j.md | 146 +++++++++++---------- docs/modules/databases/oraclexe.md | 25 ++-- docs/modules/databases/orientdb.md | 88 +++++++------ docs/modules/databases/postgres.md | 25 ++-- docs/modules/databases/presto.md | 25 ++-- docs/modules/databases/trino.md | 26 ++-- docs/modules/docker_compose.md | 25 ++-- docs/modules/elasticsearch.md | 26 ++-- docs/modules/gcloud.md | 25 ++-- docs/modules/k3s.md | 25 ++-- docs/modules/kafka.md | 25 ++-- docs/modules/localstack.md | 25 ++-- docs/modules/mockserver.md | 25 ++-- docs/modules/nginx.md | 25 ++-- docs/modules/pulsar.md | 25 ++-- docs/modules/rabbitmq.md | 25 ++-- docs/modules/solr.md | 25 ++-- docs/modules/toxiproxy.md | 25 ++-- docs/modules/vault.md | 25 ++-- docs/modules/webdriver_containers.md | 47 +++---- docs/quickstart/junit_4_quickstart.md | 25 ++-- docs/quickstart/junit_5_quickstart.md | 53 ++++---- docs/quickstart/spock_quickstart.md | 34 ++--- docs/test_framework_integration/junit_5.md | 25 ++-- docs/test_framework_integration/spock.md | 25 ++-- mkdocs.yml | 2 + requirements.txt | 9 +- 41 files changed, 675 insertions(+), 618 deletions(-) diff --git a/docs/contributing.md b/docs/contributing.md index 4094fab14ef..6cd3e2719f8 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -35,7 +35,7 @@ If the answers to the above are all yes, then a new module may be a good approac Otherwise, it is entirely possible for you to: * publish a code snippet -* contribute an [example](../examples/README.md) to the Testcontainers repo +* contribute an example to the Testcontainers repo * publish your own third party library In any case, please contact us to help validate your proposal! diff --git a/docs/index.md b/docs/index.md index 136ae53d2ca..998722af50f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,7 @@ # Testcontainers -![Testcontainers logo](./logo.png) + +Testcontainers logo ## About @@ -31,18 +32,19 @@ Testcontainers is distributed as separate JARs with a common version number: For the core library, the latest Maven/Gradle dependency is as follows: -```groovy tab='Gradle' -testImplementation "org.testcontainers:testcontainers:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - testcontainers - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:testcontainers:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + testcontainers + {{latest_version}} + test + + ``` You can also [check the latest version available on Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.testcontainers%22). @@ -51,8 +53,8 @@ You can also [check the latest version available on Maven Central](https://searc To avoid specifying the version of each dependency, you can use a `BOM` or `Bill Of Materials`. Using Maven you can add the following to `dependencyManagement` section in your `pom.xml`: - -```xml tab='Maven' +=== "Maven" +```xml @@ -67,21 +69,22 @@ Using Maven you can add the following to `dependencyManagement` section in your ``` and then use dependencies without specifying a version: - -```xml tab='Maven' - - org.testcontainers - mysql - test - -``` +=== "Maven" + ```xml + + org.testcontainers + mysql + test + + ``` Using Gradle 5.0 or higher, you can add the following to the `dependencies` section in your `build.gradle`: -```groovy tab='Gradle' -implementation platform('org.testcontainers:testcontainers-bom:{{latest_version}}') //import bom -testImplementation('org.testcontainers:mysql') //no version specified -``` +=== "Gradle" + ```groovy + implementation platform('org.testcontainers:testcontainers-bom:{{latest_version}}') //import bom + testImplementation('org.testcontainers:mysql') //no version specified + ``` [JitPack](jitpack_dependencies.md) builds are available for pre-release versions. diff --git a/docs/modules/azure.md b/docs/modules/azure.md index 529f058ea52..5e80270e90c 100644 --- a/docs/modules/azure.md +++ b/docs/modules/azure.md @@ -49,16 +49,18 @@ Test against the Emulator: Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:azure:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - azure - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:azure:{{latest_version}}" + ``` + +=== "Maven" + ```xml + + org.testcontainers + azure + {{latest_version}} + test + + ``` diff --git a/docs/modules/databases/cassandra.md b/docs/modules/databases/cassandra.md index 63213d27343..f7ed3094886 100644 --- a/docs/modules/databases/cassandra.md +++ b/docs/modules/databases/cassandra.md @@ -4,48 +4,51 @@ This example connects to the Cassandra Cluster, creates a keyspaces and asserts that is has been created. -```java tab="JUnit 4 example" -public class SomeTest { - - @Rule - public CassandraContainer cassandra = new CassandraContainer(); - - - @Test - public void test(){ - Cluster cluster = cassandra.getCluster(); - - try(Session session = cluster.connect()) { - - session.execute("CREATE KEYSPACE IF NOT EXISTS test WITH replication = \n" + - "{'class':'SimpleStrategy','replication_factor':'1'};"); - - List keyspaces = session.getCluster().getMetadata().getKeyspaces(); - List filteredKeyspaces = keyspaces - .stream() - .filter(km -> km.getName().equals("test")) - .collect(Collectors.toList()); - - assertEquals(1, filteredKeyspaces.size()); +=== "JUnit 4 example" + ```java + public class SomeTest { + + @Rule + public CassandraContainer cassandra = new CassandraContainer(); + + + @Test + public void test(){ + Cluster cluster = cassandra.getCluster(); + + try(Session session = cluster.connect()) { + + session.execute("CREATE KEYSPACE IF NOT EXISTS test WITH replication = \n" + + "{'class':'SimpleStrategy','replication_factor':'1'};"); + + List keyspaces = session.getCluster().getMetadata().getKeyspaces(); + List filteredKeyspaces = keyspaces + .stream() + .filter(km -> km.getName().equals("test")) + .collect(Collectors.toList()); + + assertEquals(1, filteredKeyspaces.size()); + } } + } - -} -``` + ``` ## Adding this module to your project dependencies Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:cassandra:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - cassandra - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:cassandra:{{latest_version}}" + ``` + +=== "Maven" + ```xml + + org.testcontainers + cassandra + {{latest_version}} + test + + ``` diff --git a/docs/modules/databases/clickhouse.md b/docs/modules/databases/clickhouse.md index a52a75af51f..b3494867edf 100644 --- a/docs/modules/databases/clickhouse.md +++ b/docs/modules/databases/clickhouse.md @@ -4,18 +4,20 @@ Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:clickhouse:{{latest_version}}" -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:clickhouse:{{latest_version}}" + ``` -```xml tab='Maven' - - org.testcontainers - clickhouse - {{latest_version}} - test - -``` +=== "Maven" + ```xml + + org.testcontainers + clickhouse + {{latest_version}} + test + + ``` !!! hint Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency. diff --git a/docs/modules/databases/cockroachdb.md b/docs/modules/databases/cockroachdb.md index 17c5efae8f8..45e3f75b86c 100644 --- a/docs/modules/databases/cockroachdb.md +++ b/docs/modules/databases/cockroachdb.md @@ -6,18 +6,20 @@ See [Database containers](./index.md) for documentation and usage that is common Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:cockroachdb:{{latest_version}}" -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:cockroachdb:{{latest_version}}" + ``` -```xml tab='Maven' - - org.testcontainers - cockroachdb - {{latest_version}} - test - -``` +=== "Maven" + ```xml + + org.testcontainers + cockroachdb + {{latest_version}} + test + + ``` !!! hint Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency. diff --git a/docs/modules/databases/couchbase.md b/docs/modules/databases/couchbase.md index e45a971f626..5c1ff9f5580 100644 --- a/docs/modules/databases/couchbase.md +++ b/docs/modules/databases/couchbase.md @@ -27,15 +27,16 @@ Running Couchbase as a stand-in in a test: Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:couchbase:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - couchbase - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:couchbase:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + couchbase + {{latest_version}} + test + + ``` diff --git a/docs/modules/databases/db2.md b/docs/modules/databases/db2.md index 3872e7fa857..945536cac05 100644 --- a/docs/modules/databases/db2.md +++ b/docs/modules/databases/db2.md @@ -33,18 +33,19 @@ public class SomeTest { Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:db2:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - db2 - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:db2:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + db2 + {{latest_version}} + test + + ``` !!! hint Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency. diff --git a/docs/modules/databases/dynalite.md b/docs/modules/databases/dynalite.md index dfc32f35d2e..8807ad7f025 100644 --- a/docs/modules/databases/dynalite.md +++ b/docs/modules/databases/dynalite.md @@ -29,18 +29,19 @@ In part, because it's light and quick to run. Also, please see the [reasons give Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:dynalite:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - dynalite - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:dynalite:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + dynalite + {{latest_version}} + test + + ``` !!! hint Adding this Testcontainers library JAR will not automatically add an AWS SDK JAR to your project. You should ensure that your project also has a suitable AWS SDK JAR as a dependency. diff --git a/docs/modules/databases/influxdb.md b/docs/modules/databases/influxdb.md index 5de7d36182a..3f5fe0654db 100644 --- a/docs/modules/databases/influxdb.md +++ b/docs/modules/databases/influxdb.md @@ -22,18 +22,19 @@ public class SomeTest { Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:influxdb:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - influxdb - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:influxdb:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + influxdb + {{latest_version}} + test + + ``` !!! hint Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency. diff --git a/docs/modules/databases/mariadb.md b/docs/modules/databases/mariadb.md index 7ef14bed3cc..495965bee0a 100644 --- a/docs/modules/databases/mariadb.md +++ b/docs/modules/databases/mariadb.md @@ -23,18 +23,19 @@ public class SomeTest { Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:mariadb:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - mariadb - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:mariadb:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + mariadb + {{latest_version}} + test + + ``` !!! hint Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency. diff --git a/docs/modules/databases/mongodb.md b/docs/modules/databases/mongodb.md index 3d49ecc6809..9e81e28c39f 100644 --- a/docs/modules/databases/mongodb.md +++ b/docs/modules/databases/mongodb.md @@ -40,18 +40,19 @@ That's where the MongoDBContainer might come in handy. Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:mongodb:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - mongodb - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:mongodb:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + mongodb + {{latest_version}} + test + + ``` !!! hint Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency diff --git a/docs/modules/databases/mssqlserver.md b/docs/modules/databases/mssqlserver.md index 6dea62def25..7ddd5350a40 100644 --- a/docs/modules/databases/mssqlserver.md +++ b/docs/modules/databases/mssqlserver.md @@ -29,18 +29,19 @@ public class SomeTest { Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:mssqlserver:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - mssqlserver - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:mssqlserver:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + mssqlserver + {{latest_version}} + test + + ``` !!! hint diff --git a/docs/modules/databases/mysql.md b/docs/modules/databases/mysql.md index f5a428e1cdc..26fc62308ef 100644 --- a/docs/modules/databases/mysql.md +++ b/docs/modules/databases/mysql.md @@ -17,18 +17,19 @@ and will be able to override server settings when the container starts. Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:mysql:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - mysql - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:mysql:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + mysql + {{latest_version}} + test + + ``` !!! hint Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency. diff --git a/docs/modules/databases/neo4j.md b/docs/modules/databases/neo4j.md index 0310d37d817..17e1e154fc8 100644 --- a/docs/modules/databases/neo4j.md +++ b/docs/modules/databases/neo4j.md @@ -13,58 +13,59 @@ On the JVM you would most likely use the [Java driver](https://github.com/neo4j/ The following example uses the JUnit 5 extension `@Testcontainers` and demonstrates both the usage of the Java Driver and the REST endpoint: -```java tab="JUnit 5 example" -@Testcontainers -public class ExampleTest { - - @Container - private static Neo4jContainer neo4jContainer = new Neo4jContainer() - .withAdminPassword(null); // Disable password - - @Test - void testSomethingUsingBolt() { - - // Retrieve the Bolt URL from the container - String boltUrl = neo4jContainer.getBoltUrl(); - try ( - Driver driver = GraphDatabase.driver(boltUrl, AuthTokens.none()); - Session session = driver.session() - ) { - long one = session.run("RETURN 1", Collections.emptyMap()).next().get(0).asLong(); - assertThat(one, is(1L)); - } catch (Exception e) { - fail(e.getMessage()); - } - } - - @Test - void testSomethingUsingHttp() throws IOException { - - // Retrieve the HTTP URL from the container - String httpUrl = neo4jContainer.getHttpUrl(); - - URL url = new URL(httpUrl + "/db/data/transaction/commit"); - HttpURLConnection con = (HttpURLConnection) url.openConnection(); - - con.setRequestMethod("POST"); - con.setRequestProperty("Content-Type", "application/json"); - con.setDoOutput(true); - - try (Writer out = new OutputStreamWriter(con.getOutputStream())) { - out.write("{\"statements\":[{\"statement\":\"RETURN 1\"}]}"); - out.flush(); +=== "JUnit 5 example" + ```java + @Testcontainers + public class ExampleTest { + + @Container + private static Neo4jContainer neo4jContainer = new Neo4jContainer() + .withAdminPassword(null); // Disable password + + @Test + void testSomethingUsingBolt() { + + // Retrieve the Bolt URL from the container + String boltUrl = neo4jContainer.getBoltUrl(); + try ( + Driver driver = GraphDatabase.driver(boltUrl, AuthTokens.none()); + Session session = driver.session() + ) { + long one = session.run("RETURN 1", Collections.emptyMap()).next().get(0).asLong(); + assertThat(one, is(1L)); + } catch (Exception e) { + fail(e.getMessage()); + } } - - assertThat(con.getResponseCode(), is(HttpURLConnection.HTTP_OK)); - try (BufferedReader buffer = new BufferedReader(new InputStreamReader(con.getInputStream()))) { - String expectedResponse = - "{\"results\":[{\"columns\":[\"1\"],\"data\":[{\"row\":[1],\"meta\":[null]}]}],\"errors\":[]}"; - String response = buffer.lines().collect(Collectors.joining("\n")); - assertThat(response, is(expectedResponse)); + + @Test + void testSomethingUsingHttp() throws IOException { + + // Retrieve the HTTP URL from the container + String httpUrl = neo4jContainer.getHttpUrl(); + + URL url = new URL(httpUrl + "/db/data/transaction/commit"); + HttpURLConnection con = (HttpURLConnection) url.openConnection(); + + con.setRequestMethod("POST"); + con.setRequestProperty("Content-Type", "application/json"); + con.setDoOutput(true); + + try (Writer out = new OutputStreamWriter(con.getOutputStream())) { + out.write("{\"statements\":[{\"statement\":\"RETURN 1\"}]}"); + out.flush(); + } + + assertThat(con.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + try (BufferedReader buffer = new BufferedReader(new InputStreamReader(con.getInputStream()))) { + String expectedResponse = + "{\"results\":[{\"columns\":[\"1\"],\"data\":[{\"row\":[1],\"meta\":[null]}]}],\"errors\":[]}"; + String response = buffer.lines().collect(Collectors.joining("\n")); + assertThat(response, is(expectedResponse)); + } } } -} -``` + ``` You are not limited to Unit tests and can of course use an instance of the Neo4j Testcontainer in vanilla Java code as well. @@ -165,33 +166,36 @@ You'll find more information about licensing Neo4j here: [About Neo4j Licenses]( Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:neo4j:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - neo4j - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:neo4j:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + neo4j + {{latest_version}} + test + + ``` !!! hint Add the Neo4j Java driver if you plan to access the Testcontainer via Bolt: - ```groovy tab='Gradle' - compile "org.neo4j.driver:neo4j-java-driver:1.7.1" - ``` + === "Gradle" + ```groovy + compile "org.neo4j.driver:neo4j-java-driver:1.7.1" + ``` - ```xml tab='Maven' - - org.neo4j.driver - neo4j-java-driver - 1.7.1 - - ``` + === "Maven" + ```xml + + org.neo4j.driver + neo4j-java-driver + 1.7.1 + + ``` diff --git a/docs/modules/databases/oraclexe.md b/docs/modules/databases/oraclexe.md index 56eb57bc0e5..583d68f3c3d 100644 --- a/docs/modules/databases/oraclexe.md +++ b/docs/modules/databases/oraclexe.md @@ -30,18 +30,19 @@ containing `oracle.container.image=IMAGE`, where IMAGE is a suitable image name Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:oracle-xe:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - oracle-xe - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:oracle-xe:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + oracle-xe + {{latest_version}} + test + + ``` !!! hint Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency. diff --git a/docs/modules/databases/orientdb.md b/docs/modules/databases/orientdb.md index 4f0b3550e44..4beffefa6c2 100644 --- a/docs/modules/databases/orientdb.md +++ b/docs/modules/databases/orientdb.md @@ -16,27 +16,28 @@ On the JVM you would most likely use the [Java driver](https://github.com/). The following example uses the JUnit 5 extension `@Testcontainers` and demonstrates both the usage of the Java Client: -```java tab="JUnit 5 example" -@Testcontainers -public class ExampleTest { - - @Container - private static OrientDBContainer container = new OrientDBContainer(); - - @Test - void testDbCreation() { - - final ODatabaseSession session = container.getSession(); - - session.command("CREATE CLASS Person EXTENDS V"); - session.command("INSERT INTO Person set name='john'"); - session.command("INSERT INTO Person set name='jane'"); - - assertThat(session.query("SELECT FROM Person").stream()).hasSize(2); +=== "JUnit 5 example" + ```java + @Testcontainers + public class ExampleTest { + + @Container + private static OrientDBContainer container = new OrientDBContainer(); + + @Test + void testDbCreation() { + + final ODatabaseSession session = container.getSession(); + + session.command("CREATE CLASS Person EXTENDS V"); + session.command("INSERT INTO Person set name='john'"); + session.command("INSERT INTO Person set name='jane'"); + + assertThat(session.query("SELECT FROM Person").stream()).hasSize(2); + } + } - -} -``` + ``` You are not limited to Unit tests and can of course use an instance of the OrientDB Testcontainer in vanilla Java code as well. @@ -45,33 +46,36 @@ You are not limited to Unit tests and can of course use an instance of the Orien Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:orientdb:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - orientdb - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:orientdb:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + orientdb + {{latest_version}} + test + + ``` !!! hint Add the OrientDB Java client if you plan to access the Testcontainer: - ```groovy tab='Gradle' - compile "com.orientechnologies:orientdb-client:3.0.24" - ``` + === "Gradle" + ```groovy + compile "com.orientechnologies:orientdb-client:3.0.24" + ``` - ```xml tab='Maven' - - com.orientechnologies - orientdb-client - 3.0.24 - - ``` + === "Maven" + ```xml + + com.orientechnologies + orientdb-client + 3.0.24 + + ``` diff --git a/docs/modules/databases/postgres.md b/docs/modules/databases/postgres.md index 0777296b0c0..b7632d78293 100644 --- a/docs/modules/databases/postgres.md +++ b/docs/modules/databases/postgres.md @@ -6,18 +6,19 @@ See [Database containers](./index.md) for documentation and usage that is common Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:postgresql:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - postgresql - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:postgresql:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + postgresql + {{latest_version}} + test + + ``` !!! hint Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency. diff --git a/docs/modules/databases/presto.md b/docs/modules/databases/presto.md index 3b987796360..d0a47fc1d7a 100644 --- a/docs/modules/databases/presto.md +++ b/docs/modules/databases/presto.md @@ -67,18 +67,19 @@ public class SomeTest { Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:presto:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - presto - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:presto:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + presto + {{latest_version}} + test + + ``` !!! hint Adding this Testcontainers library JAR will not automatically add the Presto JDBC driver JAR to your project. diff --git a/docs/modules/databases/trino.md b/docs/modules/databases/trino.md index 5b65f45934a..b6aeddafb40 100644 --- a/docs/modules/databases/trino.md +++ b/docs/modules/databases/trino.md @@ -67,18 +67,20 @@ public class SomeTest { Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:trino:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - trino - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:trino:{{latest_version}}" + ``` + +=== "Maven" + ```xml + + org.testcontainers + trino + {{latest_version}} + test + + ``` !!! hint Adding this Testcontainers library JAR will not automatically add the Trino JDBC driver JAR to your project. diff --git a/docs/modules/docker_compose.md b/docs/modules/docker_compose.md index b789a0030a7..50bb946f311 100644 --- a/docs/modules/docker_compose.md +++ b/docs/modules/docker_compose.md @@ -177,16 +177,17 @@ There are 3 ways to specify location of the `config.json` for Docker Compose: Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:testcontainers:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - testcontainers - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:testcontainers:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + testcontainers + {{latest_version}} + test + + ``` diff --git a/docs/modules/elasticsearch.md b/docs/modules/elasticsearch.md index 738bf427a56..845ee0d2d1a 100644 --- a/docs/modules/elasticsearch.md +++ b/docs/modules/elasticsearch.md @@ -41,15 +41,17 @@ other new and advanced features), you can use this instead: Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:elasticsearch:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - elasticsearch - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:elasticsearch:{{latest_version}}" + ``` + +=== "Maven" + ```xml + + org.testcontainers + elasticsearch + {{latest_version}} + test + + ``` diff --git a/docs/modules/gcloud.md b/docs/modules/gcloud.md index aeab52fc932..7431ed0de0f 100644 --- a/docs/modules/gcloud.md +++ b/docs/modules/gcloud.md @@ -141,15 +141,16 @@ See more examples: Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:gcloud:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - gcloud - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:gcloud:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + gcloud + {{latest_version}} + test + + ``` diff --git a/docs/modules/k3s.md b/docs/modules/k3s.md index a3c2c0fa754..27d2f863122 100644 --- a/docs/modules/k3s.md +++ b/docs/modules/k3s.md @@ -39,15 +39,16 @@ This may be used with Kubernetes clients - e.g. for the [official Java client](c Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:k3s:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - k3s - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:k3s:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + k3s + {{latest_version}} + test + + ``` diff --git a/docs/modules/kafka.md b/docs/modules/kafka.md index 58f447d412e..9b222cef125 100644 --- a/docs/modules/kafka.md +++ b/docs/modules/kafka.md @@ -50,15 +50,16 @@ You will need to explicitly create a network and set it on the Kafka container a Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:kafka:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - kafka - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:kafka:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + kafka + {{latest_version}} + test + + ``` diff --git a/docs/modules/localstack.md b/docs/modules/localstack.md index 1585c504cff..b551536af74 100644 --- a/docs/modules/localstack.md +++ b/docs/modules/localstack.md @@ -66,15 +66,16 @@ Testcontainers will inform Localstack of the best hostname automatically, using Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:localstack:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - localstack - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:localstack:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + localstack + {{latest_version}} + test + + ``` diff --git a/docs/modules/mockserver.md b/docs/modules/mockserver.md index bf56046deda..c739278acff 100644 --- a/docs/modules/mockserver.md +++ b/docs/modules/mockserver.md @@ -20,18 +20,19 @@ And how to set a simple expectation using the Java MockServerClient. Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:mockserver:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - mockserver - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:mockserver:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + mockserver + {{latest_version}} + test + + ``` Additionally, don't forget to add a [client dependency `org.mock-server:mockserver-client-java`](https://search.maven.org/search?q=mockserver-client-java) to be able to set expectations, it's not provided by the testcontainers module. Client version should match to the version in a container tag. diff --git a/docs/modules/nginx.md b/docs/modules/nginx.md index 5169d3c5625..fb4270ba4a3 100644 --- a/docs/modules/nginx.md +++ b/docs/modules/nginx.md @@ -26,15 +26,16 @@ And how to query the Nginx server for the custom content added. Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:nginx:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - nginx - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:nginx:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + nginx + {{latest_version}} + test + + ``` diff --git a/docs/modules/pulsar.md b/docs/modules/pulsar.md index 92eda498f7c..6d16a248872 100644 --- a/docs/modules/pulsar.md +++ b/docs/modules/pulsar.md @@ -4,15 +4,16 @@ Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:pulsar:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - pulsar - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:pulsar:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + pulsar + {{latest_version}} + test + + ``` diff --git a/docs/modules/rabbitmq.md b/docs/modules/rabbitmq.md index dff8631eeca..8a9ae9d9b97 100644 --- a/docs/modules/rabbitmq.md +++ b/docs/modules/rabbitmq.md @@ -7,15 +7,16 @@ Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:rabbitmq:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - rabbitmq - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:rabbitmq:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + rabbitmq + {{latest_version}} + test + + ``` diff --git a/docs/modules/solr.md b/docs/modules/solr.md index bb2d18d6431..44be46c1a1a 100644 --- a/docs/modules/solr.md +++ b/docs/modules/solr.md @@ -20,15 +20,16 @@ You can start a solr container instance from any Java application by using: Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:solr:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - solr - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:solr:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + solr + {{latest_version}} + test + + ``` diff --git a/docs/modules/toxiproxy.md b/docs/modules/toxiproxy.md index a3403ccc377..aa6013284ed 100644 --- a/docs/modules/toxiproxy.md +++ b/docs/modules/toxiproxy.md @@ -72,18 +72,19 @@ Additionally we can disable the proxy to simulate a complete interruption to the Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:toxiproxy:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - toxiproxy - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:toxiproxy:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + toxiproxy + {{latest_version}} + test + + ``` ## Acknowledgements diff --git a/docs/modules/vault.md b/docs/modules/vault.md index 3140a6258bc..577ed91a32f 100644 --- a/docs/modules/vault.md +++ b/docs/modules/vault.md @@ -41,18 +41,19 @@ test how your application behaves with Vault by writing different test scenarios Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:vault:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - vault - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:vault:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + vault + {{latest_version}} + test + + ``` ## License diff --git a/docs/modules/webdriver_containers.md b/docs/modules/webdriver_containers.md index 3c6b0ffae27..d589aa18098 100644 --- a/docs/modules/webdriver_containers.md +++ b/docs/modules/webdriver_containers.md @@ -88,32 +88,35 @@ A few different examples are shown in [ChromeWebDriverContainerTest.java](https: Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:selenium:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - selenium - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:selenium:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + selenium + {{latest_version}} + test + + ``` !!! hint Adding this Testcontainers library JAR will not automatically add a Selenium Webdriver JAR to your project. You should ensure that your project also has suitable Selenium dependencies in place, for example: - ```groovy tab='Gradle' - compile "org.seleniumhq.selenium:selenium-remote-driver:3.141.59" - ``` + === "Gradle" + ```groovy + compile "org.seleniumhq.selenium:selenium-remote-driver:3.141.59" + ``` - ```xml tab='Maven' - - org.seleniumhq.selenium - selenium-remote-driver - 3.141.59 - - ``` + === "Maven" + ```xml + + org.seleniumhq.selenium + selenium-remote-driver + 3.141.59 + + ``` Testcontainers will try and match the version of the Dockerized browser to whichever version of Selenium is found on the classpath diff --git a/docs/quickstart/junit_4_quickstart.md b/docs/quickstart/junit_4_quickstart.md index 1a2cd84c77e..57946c1fb74 100644 --- a/docs/quickstart/junit_4_quickstart.md +++ b/docs/quickstart/junit_4_quickstart.md @@ -21,18 +21,19 @@ Let's start from here, and see how to improve the test with Testcontainers: First, add Testcontainers as a dependency as follows: -```groovy tab='Gradle' -testImplementation "org.testcontainers:testcontainers:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - testcontainers - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:testcontainers:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + testcontainers + {{latest_version}} + test + + ``` ## 2. Get Testcontainers to run a Redis container during our tests diff --git a/docs/quickstart/junit_5_quickstart.md b/docs/quickstart/junit_5_quickstart.md index b50272cb68d..24e9dd71a74 100644 --- a/docs/quickstart/junit_5_quickstart.md +++ b/docs/quickstart/junit_5_quickstart.md @@ -21,32 +21,33 @@ Let's start from here, and see how to improve the test with Testcontainers: First, add Testcontainers as a dependency as follows: -```groovy tab='Gradle' -testImplementation "org.junit.jupiter:junit-jupiter:5.8.1" -testImplementation "org.testcontainers:testcontainers:{{latest_version}}" -testImplementation "org.testcontainers:junit-jupiter:{{latest_version}}" -``` - -```xml tab='Maven' - - org.junit.jupiter - junit-jupiter - 5.8.1 - test - - - org.testcontainers - testcontainers - {{latest_version}} - test - - - org.testcontainers - junit-jupiter - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.junit.jupiter:junit-jupiter:5.8.1" + testImplementation "org.testcontainers:testcontainers:{{latest_version}}" + testImplementation "org.testcontainers:junit-jupiter:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.junit.jupiter + junit-jupiter + 5.8.1 + test + + + org.testcontainers + testcontainers + {{latest_version}} + test + + + org.testcontainers + junit-jupiter + {{latest_version}} + test + + ``` ## 2. Get Testcontainers to run a Redis container during our tests diff --git a/docs/quickstart/spock_quickstart.md b/docs/quickstart/spock_quickstart.md index 875c1976c93..267954e99ee 100644 --- a/docs/quickstart/spock_quickstart.md +++ b/docs/quickstart/spock_quickstart.md @@ -21,27 +21,29 @@ Let's start from here, and see how to improve the test with Testcontainers: First, add Testcontainers as a dependency as follows: -```groovy tab='Gradle' -testImplementation "org.testcontainers:spock:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - spock - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:spock:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + spock + {{latest_version}} + test + + ``` ## 2. Get Testcontainers to run a Redis container during our tests Annotate the Spock specification class with the Testcontainers extension: -```groovy tab='Spock Testcontainers annotation' -@org.testcontainers.spock.Testcontainers -class RedisBackedCacheIntTest extends Specification { -``` +=== "Spock Testcontainers annotation" + ```groovy + @org.testcontainers.spock.Testcontainers + class RedisBackedCacheIntTest extends Specification { + ``` And add the following field to the body of our test class: diff --git a/docs/test_framework_integration/junit_5.md b/docs/test_framework_integration/junit_5.md index 7f3689c0301..15597f90cdb 100644 --- a/docs/test_framework_integration/junit_5.md +++ b/docs/test_framework_integration/junit_5.md @@ -68,15 +68,16 @@ This extension has only be tested with sequential test execution. Using it with Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:junit-jupiter:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - junit-jupiter - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:junit-jupiter:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + junit-jupiter + {{latest_version}} + test + + ``` diff --git a/docs/test_framework_integration/spock.md b/docs/test_framework_integration/spock.md index 11ccf058767..b8c1d5b7eae 100644 --- a/docs/test_framework_integration/spock.md +++ b/docs/test_framework_integration/spock.md @@ -17,18 +17,19 @@ can be mixed with Spock's `@Shared` annotation to indicate, that containers shou Add the following dependency to your `pom.xml`/`build.gradle` file: -```groovy tab='Gradle' -testImplementation "org.testcontainers:spock:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - spock - {{latest_version}} - test - -``` +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:spock:{{latest_version}}" + ``` +=== "Maven" + ```xml + + org.testcontainers + spock + {{latest_version}} + test + + ``` ## Attributions diff --git a/mkdocs.yml b/mkdocs.yml index 694fca299ca..e4115625a7f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -21,6 +21,8 @@ markdown_extensions: - codehilite: linenums: False - pymdownx.superfences + - pymdownx.tabbed: + alternate_style: true - pymdownx.snippets nav: - Home: index.md diff --git a/requirements.txt b/requirements.txt index f962b36a44f..8f4a1a5f100 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,4 @@ -mkdocs==1.0.4 -mkdocs-codeinclude-plugin==0.1.0 -mkdocs-material==4.6.0 -mkdocs-markdownextradata-plugin==0.1.1 -markdown>=3.1,<3.2 +mkdocs==1.2.3 +mkdocs-codeinclude-plugin==0.2.0 +mkdocs-material==8.1.3 +mkdocs-markdownextradata-plugin==0.2.5 From 720c2ea81f6bf624ff7d8ad1b2b3f89824c3b006 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 9 Feb 2022 09:23:15 +0100 Subject: [PATCH 2386/5615] Update Gradle Wrapper to 7.4 (#5036) Signed-off-by: gradle-update-robot Co-authored-by: gradle-update-robot --- examples/gradle/wrapper/gradle-wrapper.jar | Bin 59536 -> 59821 bytes .../gradle/wrapper/gradle-wrapper.properties | 4 ++-- gradle/wrapper/gradle-wrapper.jar | Bin 59536 -> 59821 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++-- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/gradle/wrapper/gradle-wrapper.jar b/examples/gradle/wrapper/gradle-wrapper.jar index 7454180f2ae8848c63b8b4dea2cb829da983f2fa..41d9927a4d4fb3f96a785543079b8df6723c946b 100644 GIT binary patch delta 8958 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 diff --git a/examples/gradle/wrapper/gradle-wrapper.properties b/examples/gradle/wrapper/gradle-wrapper.properties index 59250647c40..1debed0d8fd 100644 --- a/examples/gradle/wrapper/gradle-wrapper.properties +++ b/examples/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=b586e04868a22fd817c8971330fec37e298f3242eb85c374181b12d637f80302 -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionSha256Sum=8cc27038d5dbd815759851ba53e70cf62e481b87494cc97cfd97982ada5ba634 +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180f2ae8848c63b8b4dea2cb829da983f2fa..41d9927a4d4fb3f96a785543079b8df6723c946b 100644 GIT binary patch delta 8958 zcmY+KWl$VIlZIh&f(Hri?gR<$?iyT!TL`X;1^2~W7YVSq1qtqM!JWlDxLm%}UESUM zndj}Uny%^UnjhVhFb!8V3s(a#fIy>`VW15{5nuy;_V&a5O#0S&!a4dSkUMz_VHu3S zGA@p9Q$T|Sj}tYGWdjH;Mpp8m&yu&YURcrt{K;R|kM~(*{v%QwrBJIUF+K1kX5ZmF zty3i{d`y0;DgE+de>vN@yYqFPe1Ud{!&G*Q?iUc^V=|H%4~2|N zW+DM)W!`b&V2mQ0Y4u_)uB=P@-2`v|Wm{>CxER1P^ z>c}ZPZ)xxdOCDu59{X^~2id7+6l6x)U}C4Em?H~F`uOxS1?}xMxTV|5@}PlN%Cg$( zwY6c}r60=z5ZA1L zTMe;84rLtYvcm?M(H~ZqU;6F7Evo{P7!LGcdwO|qf1w+)MsnvK5^c@Uzj<{ zUoej1>95tuSvDJ|5K6k%&UF*uE6kBn47QJw^yE&#G;u^Z9oYWrK(+oL97hBsUMc_^ z;-lmxebwlB`Er_kXp2$`&o+rPJAN<`WX3ws2K{q@qUp}XTfV{t%KrsZ5vM!Q#4{V& zq>iO$MCiLq#%wXj%`W$_%FRg_WR*quv65TdHhdpV&jlq<=K^K`&!Kl5mA6p4n~p3u zWE{20^hYpn1M}}VmSHBXl1*-)2MP=0_k)EPr#>EoZukiXFDz?Di1I>2@Z^P$pvaF+ zN+qUy63jek2m59;YG)`r^F3-O)0RDIXPhf)XOOdkmu`3SMMSW(g+`Ajt{=h1dt~ks ztrhhP|L4G%5x79N#kwAHh5N){@{fzE7n&%dnisCm65Za<8r_hKvfx4Bg*`%-*-Mvn zFvn~)VP@}1sAyD+B{{8l{EjD10Av&Mz9^Xff*t`lU=q=S#(|>ls520;n3<}X#pyh& z*{CJf7$*&~!9jMnw_D~ikUKJ2+UnXmN6qak{xx%W;BKuXt7@ky!LPI1qk?gDwG@@o zkY+BkIie>{{q==5)kXw(*t#I?__Kwi>`=+s?Gq6X+vtSsaAO&Tf+Bl$vKnzc&%BHM z=loWOQq~n}>l=EL(5&6((ESsQC3^@4jlO5Od{qN#sWV)vqXw}aA>*uvwZopNN(|-T zRTF%5Y_k1R$;(d-)n;hWex{;7b6KgdAVE@&0pd(*qDzBO#YZV%kh%pYt1`hnQ(Fa& zYiDrOTDqk5M7hzp9kI2h!PxNnuJ&xl*zF8sx6!67bA49R1bmUF5bpK&&{eI0U~cH}PM z3aW1$lRb|ItkG5~_eBNu$|I|vYIdAA9a!pVq<+UTx*M}fG`23zxXp&E=FfnY- zEzKj;Cu_s4v>leO7M2-mE(UzKHL4c$c`3dS*19OpLV^4NI*hWWnJQ9lvzP4c;c?do zqrcsKT*i~eIHl0D3r4N{)+RsB6XhrC^;sp2cf_Eq#6*CV;t8v=V!ISe>>9kPgh}NI z=1UZutslxcT$Ad;_P^;Oouoa(cs!Ctpvi>%aQ+Zp=1d|h{W9Wmf7JWxa(~<#tSZ?C%wu4_5F!fc!<@PIBeJ)Nr^$bB6!_Gic_7}c3J{QI~Gg5g5jTp9}V6KYgrgaX>pJt}7$!wOht&KO|+z{Iw@YL|@~D zMww}+lG}rm2^peNx>58ME||ZQxFQeVSX8iogHLq_vXb`>RnoEKaTWBF-$JD#Q4BMv zt2(2Qb*x-?ur1Y(NsW8AdtX0#rDB?O(Vs4_xA(u-o!-tBG03OI!pQD+2UytbL5>lG z*(F)KacHqMa4?dxa(Vcrw>IIAeB$3cx#;;5r2X;HE8|}eYdAgCw#tpXNy7C3w1q`9 zGxZ6;@1G%8shz9e+!K2MO*{_RjO}Jo6eL3{TSZ>nY7)Qs`Dhi5><@oh0r)gT7H-?3 zLDsd^@m%JvrS8sta5`QiZNs^*GT}Hiy^zjK2^Ni%`Z|ma)D2 zuyumbvw$M8$haCTI~6M%d4+P)uX%u{Sfg4Al+F7c6;O-*)DKI7E8izSOKB#FcV{M+ zEvY0FBkq!$J0EW$Cxl}3{JwV^ki-T?q6C30Y5e&p@8Rd?$ST-Ghn*-`tB{k54W<>F z5I)TFpUC!E9298=sk>m#FI4sUDy_!8?51FqqW!9LN1(zuDnB3$!pEUjL>N>RNgAG~-9Xm|1lqHseW(%v&6K(DZ3Pano(1-Qe?3%J&>0`~w^Q-p&@ zg@HjvhJk?*hpF7$9P|gkzz`zBz_5Z!C4_-%fCcAgiSilzFQef!@amHDrW!YZS@?7C zs2Y9~>yqO+rkih?kXztzvnB^6W=f52*iyuZPv$c42$WK7>PHb z6%MYIr5D32KPdwL1hJf{_#jn?`k(taW?mwmZVvrr=y~fNcV$`}v(8};o9AjOJumS4 z`889O91^pkF+|@$d9wVoZ3;^j;^sUs&Ubo_qD&MTL%O z&*SE0ujG~zm;?x)8TLC&ft))nyI zcg44@*Q{cYT+qGrA=In_X{NNCD+B0w#;@g)jvBU;_8od6U>;7HIo@F*=g8CQUo(u^ z3r4FJ7#<@)MXO&5+DgKE&^>^`r!loe7CWE*1k0*0wLFzSOV8jvlX~WOQ?$1v zk$Or}!;ix0g78^6W;+<=J>z@CBs!<<)HvF(Ls-&`matpesJ5kkjC)6nGB@b{ii6-Uoho$BT%iJgugTOeZ$5Xo4D7Pd< zC*LJh5V@2#5%aBZCgzlQi3@<_!VfiL07ywc)ZbwKPfcR|ElQoS(8x|a7#IR}7#Io= zwg4$8S{egr-NffD)Fg&X9bJSoM25pF&%hf>(T&9bI}=#dPQyNYz;ZZ7EZ=u1n701SWKkZ9n(-qU ztN`sdWL1uxQ1mKS@x11;O|@^AD9!NeoPx}?EKIr!2>1Qq4gjfGU)tr6?Z5l7JAS3j zZeq{vG{rb%DFE4%$szK}d2UzB{4>L?Tv+NAlE*&Nq6g+XauaSI+N2Y8PJLw+aNg1p zbxr|hI8wcMP&&+(Cu|%+Jq|r>+BHk@{AvfBXKiVldN)@}TBS0LdIpnANCVE26WL-} zV}HJ^?m&$Rkq;Zf*i-hoasnpJVyTH__dbGWrB_R55d*>pTyl6(?$EO@>RCmTX1Hzr zT2)rOng?D4FfZ_C49hjMV*UonG2DlG$^+k=Y%|?Dqae4}JOU=8=fgY4Uh!pa9eEqf zFX&WLPu!jArN*^(>|H>dj~g`ONZhaaD%h_HHrHkk%d~TR_RrX{&eM#P@3x=S^%_6h zh=A)A{id16$zEFq@-D7La;kTuE!oopx^9{uA3y<}9 z^bQ@U<&pJV6kq7LRF47&!UAvgkBx=)KS_X!NY28^gQr27P=gKh0+E>$aCx&^vj2uc}ycsfSEP zedhTgUwPx%?;+dESs!g1z}5q9EC+fol}tAH9#fhZQ?q1GjyIaR@}lGCSpM-014T~l zEwriqt~ftwz=@2tn$xP&-rJt?nn5sy8sJ5Roy;pavj@O+tm}d_qmAlvhG(&k>(arz z;e|SiTr+0<&6(-An0*4{7akwUk~Yf4M!!YKj^swp9WOa%al`%R>V7mi z+5+UodFAaPdi4(8_FO&O!Ymb#@yxkuVMrog(7gkj$G@FLA#ENMxG)4f<}S%Fn?Up$+C%{02AgMKa^ z4SFGWp6U>{Q6VRJV}yjxXT*e`1XaX}(dW1F&RNhpTzvCtzuu;LMhMfJ2LBEy?{^GHG!OF!! zDvs64TG)?MX&9NCE#H3(M0K>O>`ca0WT2YR>PTe&tn?~0FV!MRtdb@v?MAUG&Ef7v zW%7>H(;Mm)RJkt18GXv!&np z?RUxOrCfs;m{fBz5MVlq59idhov21di5>WXWD-594L-X5;|@kyWi@N+(jLuh=o+5l zGGTi~)nflP_G}Yg5Pi%pl88U4+^*ihDoMP&zA*^xJE_X*Ah!jODrijCqQ^{=&hD7& z^)qv3;cu?olaT3pc{)Kcy9jA2E8I)#Kn8qO>70SQ5P8YSCN=_+_&)qg)OYBg|-k^d3*@jRAeB?;yd-O1A0wJ z?K*RDm|wE<(PBz~+C%2CTtzCTUohxP2*1kE8Of~{KRAvMrO_}NN&@P7SUO{;zx0iK z@or9R8ydYOFZf(cHASCAatL%;62IL27~SmASr(7F&NMr+#gNw@z1VM z_ALFwo3)SoANEwRerBdRV`>y`t72#aF2ConmWQp(Xy|msN9$yxhZ1jAQ67lq{vbC5 zujj|MlGo`6Bfn0TfKgi(k=gq0`K~W+X(@GzYlPI4g0M;owH3yG14rhK>lG8lS{`!K z+Nc@glT-DGz?Ym?v#Hq|_mEdPAlHH5jZuh*6glq!+>Lk$S%ED2@+ea6CE@&1-9a?s znglt|fmIK}fg<9@XgHe4*q!aO<-;Xj$T?IzB-{&2`#eA6rdtCi80mpP&vw(Uytxu$#YzNI_cB>LS zmim>ys;ir;*Dzbr22ZDxO2s;671&J0U<9(n1yj)J zHFNz=ufPcQVEG+ePjB<5C;=H0{>Mi*xD>hQq8`Vi7TjJ$V04$`h3EZGL|}a07oQdR z?{cR(z+d>arn^AUug&voOzzi$ZqaS)blz-z3zr;10x;oP2)|Cyb^WtN2*wNn`YX!Y z+$Pji<7|!XyMCEw4so}xXLU)p)BA~2fl>y2Tt}o9*BPm?AXA8UE8a;>rOgyCwZBFa zyl42y`bc3}+hiZL_|L_LY29vVerM+BVE@YxK>TGm@dHi@Uw*7AIq?QA9?THL603J% zIBJ4y3n8OFzsOI;NH%DZ!MDwMl<#$)d9eVVeqVl(5ZX$PPbt*p_(_9VSXhaUPa9Qu z7)q4vqYKX7ieVSjOmVEbLj4VYtnDpe*0Y&+>0dS^bJ<8s*eHq3tjRAw^+Mu4W^-E= z4;&namG4G;3pVDyPkUw#0kWEO1;HI6M51(1<0|*pa(I!sj}F^)avrE`ShVMKBz}nE zzKgOPMSEp6M>hJzyTHHcjV%W*;Tdb}1xJjCP#=iQuBk_Eho6yCRVp&e!}4IBJ&?ksVc&u#g3+G$oNlJ?mWfADjeBS-Ph3`DKk-~Z70XugH8sq2eba@4 zIC1H_J$`9b$K`J)sGX3d!&>OmC@@rx1TL~NinQOYy72Q_+^&Mg>Ku(fTgaXdr$p_V z#gav1o{k~c>#)u3r@~6v^o)Lf=C{rAlL@!s457pq)pO;Cojx7U{urO4cvXP|E>+dV zmr2?!-5)tk-&*ap^D^2x7NG6nOop2zNFQ9v8-EZ{WCz-h36C)<^|f{V#R_WE^@(T0+d-at5hXX{U?zak*ac-XnyINo+yBD~~3O1I=a z99|CI>502&s-Qi5bv>^2#cQ%ut<4d7KgQ^kE|=%6#VlGiY8$rdJUH{sra;P~cyb_i zeX(kS%w0C?mjhJl9TZp8RS;N~y3(EXEz13oPhOSE4WaTljGkVXWd~|#)vsG6_76I)Kb z8ro?;{j^lxNsaxE-cfP;g(e;mhh3)&ba}li?woV2#7ByioiD>s%L_D;?#;C#z;a(N z-_WY<=SH42m9bFQ>Nb z@4K$@4l8pD7AKxCR>t0%`Qoy9=hA?<<^Vcj8;-E+oBe3ReW1`el8np8E$k{LgFQ}2 z2t8a`wOXFdJ9!5$&mEfD1CnJ)TB+RJih88-Zos9@HZ# zL#{qfbF0ARTXkR@G{lwlOH~nnL)1jcyu!qv2`57S&%oKz0}r{~l9U_UHaJ5!8#nrs z?2FrL`mxnzu&{bweD&62)ilz*?pYIvt`T!XFVVA78})p1YEy7 z8fK#s?b~Yo$n7&_a?EBdXH-_W)Z44?!;DFx6pZ?~RArtBI*Qm4~6nX6Z_T*i$bQPE;Qz?DAPstpGSqr-AJ zo%m9cA`oDDm?&dTaoh_>@F>a?!y4qt_;NGN9Z<%SS;fX-cSu|>+Pba22`CRb#|HZa z;{)yHE>M-pc1C0mrnT~80!u&dvVTYFV8xTQ#g;6{c<9d!FDqU%TK5T6h*w*p980D~ zUyCb`y3{-?(mJFP)0*-Nt;mI$-gc4VQumh|rs&j_^R{sgTPF`1Xja2YWstsKFuQ(d zmZMxV$p$|qQUXchu&8%J(9|)B?`~rIx&)LqDS>ob5%gTeTP#Sbny#y*rnJ&?(l=!( zoV~}LJ1DPLnF8oyM(2ScrQ0{Q4m4-BWnS4wilgCW-~~;}pw=&<+HggRD_3c@3RQIr z9+-%!%}u_{`YS=&>h%kPO3ce}>y!d-zqiniNR-b5r97u;+K6HA2tS>Z#cV{+eFI`* zd8RMGAUtX1KWfPV;q<-5JAykS+2sY$2~UX+4461a(%{P#{rwFPu0xpIuYlbgD{C7C z=U{FUarVTYX6ZUq3wE@G^QT4H2Re;n$Fz9cJ>hABl)9T8pozqbA1)H-%1=WKm^QMu zjnUZ&Pu>q+X&6Co*y#@pxc-4waKMInEPGmE_>3@Ym3S*dedSradmc5mlJn`i0vMW6 zhBnGQD^Z;&S0lnS0curqDO@({J7kTtRE+Ra?nl^HP9<)W&C>~`!258f$XDbyQOQXG zP8hhySnarOpgu8xv8@WlXnm(Uk~)_3$Sg0vTbU3 z{W!5B(L3{Yy3K5PN<@jEarAtja`}@KYva&zFRF*s+_%jIXh$T(S=an8?=Ry3H*NRqWgsM`&!#|@kf1>=4q%bFw7^Rhz!z5I zyI^zU8_R1WN9`88Z=n>pIZQ`Ixr~_9G%Q}@A7rd#*%y7G zXl^Id=^ZL?Rx}}gWXCqzj9C6;x(~mAH|$JteXa1MH<6UQig@!Hf~t}B%tP0I|H&;y zO6N0}svOa1a^PyP9N5?4W6VF%=Bj{qHUgc8@siw4bafT=UPFSoQqKgyUX>sXTBZ=x zOh^Ad!{kOM9v{%5y}`-8u*T&C7Vq6mD%GR}UeU(*epO&qgC-CkD;%=l)ZuinSzHM` z{@`j&_vC6dDe{Yb9k@1zeV_K6!l(@=6ucoI=R^cH=6{i71%4W3$J-?<8Qn#$-DMtA z6Qqi)t?4ifrt%3jSA#6ji#{f(($KBL-iQh-xrC||3U3lq`9>r)>X%oLvtimuHW-)} zy}>9~|M>w4eES`g7;iBM%Se5-OP%1U6gNWp3AZqT8C6OlFFfQ$|7LL;tBV)(qlp4K zruar^K8FnJN3@_}B;G`a~H`t|3+6d>q3#`ctTkE-D^1#d9NalQ04lH*qUW2!V zhk7#z8OwHhSl8w14;KctfO8ubZJ4$dEdpXE78wABz=n5*=q9ex3S}`e7x~~V-jmHOhtX2*n+pBslo3uosdE7xABK=V#-t{1Hd~?i z{i~%Bw6NYF+F$aK$M`r#xe=NxhA5=p%i7!$);sd>Q}#`G?Q~fygrMXmZw?0#5#17W}6Tj+&kFexG{!mYl5FoA99}3G9l;3lVQ^ z48^~gsVppE*x91WheqI(A%F0Z#$#1UJP1R12Mj9r)y(A?a+iquX+d8WD4WAQJ_!oq z9rTISr7bPd(GTP57xm$}C}&kjMivi;zi^Y9g3&X0A;ovdJ?{%_wHgt%%9P&N4H z^XzV(uNA4 zAP`hgP6BEN5`YXh|DF~6Pud?~gWfhUKoPX4>z|}0aocC&K+AoV%|SX*N!wGq3|y< zg4lP(04XIPmt6}$N!dTk+pZv>u;MTB{L4hp9uXk7>aS!6jqM2lVr%{)H3$O127TSZ z0x9hi0k-P?nWFdQ0K`pykqUIT&jD~B0tHP{ffS(}fZ(aW$oBWTSfHO!A^><6vA?qar%tzN-5NQO zL&|F{nGiQyzNJ+bM$Y`n=Lx^3wTG^o2bGB@cwr1eb+6c-1tN=U+Db;bc~eJ!hwM{SbI=#g?$!PjDB+) zPgU_2EIxocr*EOJG52-~!gml&|D|C2OQ3Y(zAhL}iae4-Ut0F*!z!VEdfw8#`LAi# zhJ_EM*~;S|FMV6y%-SduHjPOI3cFM(GpH|HES<}*=vqY+64%dJYc|k?n6Br7)D#~# zEqO(xepfaf2F{>{E2`xb=AO%A<7RtUq6kU_Iu0m?@0K(+<}u3gVw5fy=Y4CC*{IE3 zLP3YBJ7x+U(os5=&NT%gKi23bbaZ`@;%ln)wp4GpDUT$J8NtFDHJzIe_-t}{!HAsh zJ4<^WovY};)9IKAskSebdQiXv$y5}THuJZ}ouoElIZRui=6lrupV|_Jz=9^&;@HwL;J#@23k?A;k`0Bgf;ioO>W`IQ+4? z7A)eKoY4%+g%=w;=Vm8}H>@U*=*AWNtPqgWRqib#5RTGA@Q=43FrQn3J`GkTUV5yp0U`EOTqjfp+-9;0F8!dMEwwcK%(6`8sDD^aR04 zd6O5vh|Xk?&3dy4f|1QK&Ulf{h6Iq;d-&*ti#Ck>wZFG;GHwc?b;X~eBITx49>2d8 z4HcK&1&DvEGT6kXdzAm4oO8%c}8OBt~8H956_;YP-ss*uMf==a+%w~F>Qkm7r)IAuxuoX}h92$gHqbFUun#8m zWHdy`Zrm#=Pa98x8cO0vd@Tgkr*lm0{dky+Gocr0P8y%HGEI#c3qLqIRc`Oq_C%*; zG+QTr(#Q|yHKv6R@!DmLlwJQ3FAB)Yor-I4zyDyqM4yp5n2TrQH>gRt*Zw0+WI-Sj`EgmYHh=t9! zF6lz^xpqGGpo6!5`sc0a^FVhy_Uxq|@~(1@IIzV)nTpY9sY`CV!?8e&bB8=M&sYEb z2i}fvKdhp9Hs68Y-!QJ<=wE(iQ5+49tqt;Rh|jhYrI5VW-mIz|UY{h8E=rC5sh#DU z?wGgk-Tn!I?+Zer7pHlF_Z^!Kd1qkS3&lv#%s6-<5Y%jQL${cge5=G5Ab?D&|9$Y~ zf%rJC2+=2vg;y0-SJb3<@3%}BO$T$C66q$L_H33a`VUbgW~N(4B=v5(<=My|#|J7q z*Ox4wL4kbJd_~EjLTABSu4U7Jk#`y(6O*U6(k6XxM}CtGZB(H@3~kh*zaGRXM}Iwp zQ%xFk2>@wiZrVCV_G4G~v;NebCQ%T7{SDyPpSv&dT@Cn)Mx@IK*IdNrj{*4pkV4wv z)y0J538h>cpB7iPSzA~x24T`{dzNkpvGIqvt1Dvdq@o-`B=$hkczX8$yFMhsWNK-X zxr$kR$tMD0@W)Vxe1^t9qVmsg&K^F@u84)(n2dttIEAZFN6VD$&tskpG%SI7whGL3 z)DeRiwe&?8m7U{G`oW8!SCi*dM>oYL%UKQnKxV_0RXAEBQg1kStExGEUVwLJ0orGGwb7uv+kPDl7_E2*iD|J*=8A@;XCvwq0aw5oJYN*Yh&o=l} z2z8YKb-fIAH5spql4eXqp*)o2*b>#1@DSt?zZi{GPj0gH&Nm+EI<3^z0w%YTEV4xw zI6$+=Faa|Y4o5i0zm5lOg|&tmnJ806DBovU@Ll6XsA;NRrTK~t*AAJIAS=v-UZ%Pr z$oddI@NRir&erzCwq|)ciJemr-E061j{0Vc@Ys7K(mW|JYj*$+i1Q8XlIK8T?TYS(AXu$`2U zQ@fHxc=AVHl_}cRZQ)w0anMEoqRKKIvS^`<-aMf*FM`NsG&Uowneo+Ji$7DUDYc7*Hjg;-&aHM%3 zXO6cz$$G};Uqh+iY7Wpme>PHG4cu(q;xyskNLs$^uRRMfEg?8Cj~aE-ajM%CXkx0F z>C?g3tIA#9sBQOpe`J+04{q7^TqhFk^F1jFtk4JDRO*`d-fx`GYHb=&(JiaM1b?Y^ zO3Kj3sj76ieol|N$;>j@t#tKj=@*gP+mv}KwlTcPYgR$+)2(gk)2JNE=jSauPq!$< z<|?Sb%W)wS)b>b6i{8!x!^!xIdU3{CJFVnTcw0j{M%DUCF=_>eYYEUWnA-|B(+KYL z_W_`JI&&u^@t0})@DH^1LDuT0s3dMpCHIbYBgOT4Zh_4yHbSqRbtIKndeT4Q*Jg91 z@>rO!^t-G~*AIW;FQ$3J=b;oGg8?CTa~qNCb>&cgp@e;?0AqA&paz~(%PYO+QBo4( zp?}ZdSMWx0iJm7HVNk9A#^9Osa#GPJ!_pYEW}($8>&2}fbr@&ygZ?${A7_9?X$(&5 z#~-hxdPQwCNEpf=^+WH-3`2LxrrBMTa}~qJC9S;VzhG!On^JLyW6WkF{8aAE$sM+( zxr8xLW(KIjI`Rm(24r3OJBk<3GF=G!uSP0-G&AY32mLm8q=#Xom&Pqv=1C{d3>1^ zAjsmV@XZ%BKq^eUfBpa8KvO8ob|F3hAjJv*yo2Bhl0)KUus{qA9m8jf)KnOGGTa6~4>3@J_VzkL|vYPl*uL+Ot*Q7W!f5rJw5+AsjP_IfL+-S*2p| zB7!FhjvkUTxQkGWGSg{X;h~dK>gAJivW?88Nu!3o>ySDaABn$rAYt086#27fbjPQS zhq>55ASvm*60qRdVOY9=bU^+{Pi#!OaZwENN;zy5?EztOHK-Q5;rCuiFl}BSc1YaQ zC-S{=KsGDz@Ji9O5W;XxE0xI|@3o6(2~i4b8Ii9VT;^G$*dRw(V?=br)D&q^XkeBX z+gl~+R@rVD-Hwv@7RHV?Bip5KMI)aV^&snt?H<$Nt=OPx#VxF&BGi?2A2+lNOYywNUGMeGL;|(=UjGDtLG0sN&LpGx;|U;xa13s z;W_|SPk^G}!M9_^pO zA3bt3-tca%^42sHeDtfcC0S3w3H1ny!Bxpa=*k?XRPpx9Bb-gx1J9Yvx)4J(8cG+q z(iCPZ9dsf3#QVyZgD_MW#G#qgV)olu$59&3(PzQfw@%4uZ~<5J=ABvdY43(Qnp{;G zHg3>@T#>DbTuhFl3)fb3TFqdh)V2aq7!;&JOHseTWukvA7}(iGUq;v-{2J0iHSNHq z;+)h!p6Ok^+Sp8-jgL($n6Qu47xyE`cFO5SdZR6;R!FET`tm#0D37z339Suxjpv+s z*=%2-N$N?X&0?x_uut3erF@aBGj;9$k9?3FlbDO{RQa1_qtxrh4!4#fjp4x~akvdTp@ zos?^Q&XE;3N93s4rHQGPrV7+au1$$aB6$hLy*Yz_kN$~dweb9PcB!eYVQTGjFuJP> zZCEwBtb>TIgIO^qAzq@Bv-qud_ZD-2W<_at&ml-gv`tPt$@DF5`HlA zM>DmmMkpv&Zm-8)Y#0bLQf4MpD4_-7M8eu6rh(tL8dq8onHs#R9J~dGd2IaXXMC~h z91pKhnQa%Fsn29nAA1;x(%oC zhca~qQDJaMf?wFrl-Pj;e$bZMYmMF!Y3Lv&Sb?Sjn#!NVx&NDyc^$b4uYyo2OmERa zRz;yDGd@JTykzFLe|Wk-y7#3x`6$wt$zR8r48mdUvfbeL+4D|Z``~7$PrE@qc7rZe zVsIoIbCwzjLZ@_M1*bD{HaYn();Z1-q*-I{tEnTZ(}Zmk&%MXSNBX>o| z-u*RNkAyKC-Srp7c-=@5f)xMWg>o2WWl}j6j9=8+D8;T z>0*0q#;qw8%U8i;6s0fu#I*%(g*@@a2Er@@nyI}{=@W{Z-;`=wN4N~>6Xrh&z#g}l zN1g5}0-#(nHUTv_rl2{yUZ;h#t&Fd?tY!7L%ClY)>uH-Ny2ET$lW$S)IQiN79H)D^ zb&0AXYkupy0~w8)*>Sj_p9}4L?lGTq%VG|2p`nWGhnM^!g|j-|O{%9Q%swOq63|*W zw$(N_laI}`ilB+o!a-wl?er~;;3+)$_akSQ!8YO_&-e*SI7n^(QQ;X0ZE`{4f!gAl z5$d+9CKVNonM!NO_frREICIAxOv)wm>}-k?iRisM`R7;=lyo|E_YR~FpS&PS`Lg0f zl-ON<0S%Uix8J%#yZdkCz4YNhcec<|7*P(JsM#>-L>+tYg_71q9~70FAc^6KW5jql zw!crdgVLH1G_eET=|SEc977;)ezVC|{PJZfra|}@rD;0s&@61mTEBJtILllg{%{vN zfhb&lq0yChaLhnJ-Qb62MB7`>M;|_ceHKZAeeh@#8tbrK!ArP6oXIhMK;dhEJTY`@ z0Tq>MIe0`7tGv)N*F0IGYSJv0vN?Az8g+4K9S!pW2~9F4W(_U_T=jCZrzuZ3*|__T zONp_UWmyePv8C~rckc?Xji;Z5OEqg zC*Um)i;Wh4TEwqReQdVVbUKT^2>Tpi6z_^-uF*adUFug4i@JhzpWT^Sk&E>CyP2?H zWf6x}ehuTs6wvzCnTU&gYzT029Nz19(In1WC z`(1IGmi!O%2AR|BjQa4Q0~u)kM%}?xQyjWuQ16^Gp++;`vr7!k--UZWM*~7Zl|ceO@I3`OpaRhD;YoCuo5IC0uHx>9 z478hu@H|e0Zlo)Zj@01#;8BDs@991xe~^9uG2}UXLM(m7fa}AMwX*tjioBeV&Q8Gx zSq$6wZFkRBK`cMI>R(@W@+lo2t)L+4q-negWRLWZBz*|%=W4v62JrmzNuOtA*x)QE z5L%=OH#@KMdB%Jp^r?0tE}5-*6oP`-lO7Sf)0)n*e<{HA=&qhLR)oD8-+V}Z4=md) z+k9lKf64DB2hAT)UaCP~di?-V3~JBH7itYyk~L6hrnxM%?RKntqd`=!b|e7eFnAcu z3*V;g{xr7TSTm$}DY%~SMpl>m{Sj!We+WfxSEor?YeiAxYUy25pn(?T()E>ByP^c@ zipwvWrhIK((R((VU+;@LmOnDu)ZXB3YArzzin!Z^0;PyJWnlfflo|q8(QY;o1*5CO z##hnkO{uynTMdk`~DOC#1 zdiYxQoy}=@7(ke#A8$YZZVtk4wo$8x28&I;cY3Ro-|kW=*yiiHgCLZeAr)UtVx>Tu z|LvL0hq|1-jC0I4x#>&QZCfrVB=zT!nR|~Uz`9%~2 znl{uZ{VEszW`Fad^q_HB!K9*|U-stK%?~;g?&&+12A}Rq$z($Bzuk^2X(Y=hF?-dQ ztc3DsQKI;qhWIV`99Q#R3xnU0AvY!i*BECj-z9l74|%O=V@nlv|qqC^r^-~C?E zGW%c|uYgnfJ(gjsTm_cIqcv*mYM{+i+&@F@+69ZQOK&u#v4oxUSQJ=tvqQ3W=*m;| z>SkBi8LYb-qRY7Sthh*0%3XAC%$z1rhOJzuX=PkTOa=DlocZUpE#KxVNH5)_4n=T( zGi3YrH7e~sPNYVBd~Grcq#CF~rN{p9Zza-Ntnwfma@TB)=3g36*0lSZg#ixEjFe%+ zX=&LDZ5zqculZ`=RYc^ln(~;nN|Qh6gN=!6f9-N2h+3NWbIxYud&;4SX*tWf5slk4 z{q@@l71UAZgj~*6edXb57fBUxvAS7s(RI=X868JM0+^DCn2yC>;v%S;qPOjB>YVsz(Zx9a>>BK&M zIQK>7_n)4ud0X5YM}^i*keH{ehLsiy9@NvOpsFeQjdI6anLGvVbBw_*fU1TzdVS$i z*4j7z!I5RF#rSz|8ibi$;qE{4`aqWYik7QB5U&F5C*;TO_x+gtzPGpzNt!7~nsBT7)Ckc(K~%uv&{{6A`mmBJVAk-{s~52Vu|HbCH7_W1~ZCX^RflOakGg=jo2Z z<*s;5-J+2@^LRDZ-7EV&Pq+FTErw@pfFqvx^i%E7Fx#^n(E`m2(c>K-O5`M`Yek9el zzTGs5qD6*G;y#~xu3>qWuO?-amKYtvRA}I9z#UspEeM;wOERYeot_n_EUMJf$4_u?E!6X~?q)tPoZb^_;8Y_Ox2h1m<+Le-fsRd|T8db<8#$bqez zua^Z|>h%zdnuU^ww$#-dZ9NTM`FN+!IlLkz*FqWb!x^Z|C{KyGjZ+>G;;7Mb@LY|H zc+Gp`L((Dw7pnDlHNm&;SfHedhx*kad$I^uGz{`0BYelq0yEUHpNKSkvj$|dpvY3{7*YGyhXA^LP0&wOw9oNoC=QoVx1<2Dne8qqZL zm>nFh5DX(-RnQwvHCZQwn^#Z=E!SPVlaRJ78Bo@}!!9dRt^qZy?-*`Pt4WSmgucJv zV1yFkcjlEM^uz-;b#Q7ZCP@Lk)m}uPX={R4B=56k7WNh11BN~0T*vr@!!ow^B0hOR zQ)4)&(e%>bNNL%bm<&8H{*l_L7s0$2GUgX2Vd;=4d9Dm2v3TaL+;L>{K7h7 zV#k?xDPm(NDE31$ z<}|X)pEY6myjK+^gaIMk&Yj2~F0rSKemNqlsVm4c|N7mp_C*L01s;GNx#D-*&gk!qQr}^?_r@q!8fuXw!)fA7xkd} zb>vHvdx~H$5qqAWrow7}+8zBM65-JOt5z za=T6f7MK`XJuQog8kIEboPdhcaVJeHy)5z7EBLK5NRr()E|#K0L0N^JD@pUA^Czb` zbUZ_558y+vqAGeyHCbrvOvLD67Ph}06959VzQ_|>RrXQAqE+AQ(-AaKdxoWaF8hdt z{O3W@b^*o#-f1VuU>YMV03ELF7zkCN4Q&b#prz%3Nne0lSbRo@@ z^ihv%oIl~Qyl6Q;a#$*jOC%x0_;eis*)J7=f@Ct*)xF5 zo}u~@-I}2|$b%5L7>@+Z?4o+1r&v6ceIy+vroK&jCQ<4q&45HP2wCol4hVm3pZtjf zHz1D7oyaSKJ~T{Gx}7ONLA)D5k(%%`WswrDyzX*rn}i}}TB4^y#@mAwPzoC)`?rYv zHgx|trUN#mu*VzUV~8TnJM2Qh*ZM5B{x&y>5An`(M7=Z*Q>TdiH@j*2=moNuOtvpz z+G`@~-`%~+AgPKgke@XiRPgndh@bp*-HRsh;HTtz@-y_uhb%7ylVOTqG0#u?Vn5c5 zEp*XRo|8hcgG^$#{$O9CJ&NE;TrfRpSnLmes&MO{m=N%zc`}gb!eQ7odl$oy1%PI} z#AIxx%oRVy&{O~9xnK4$EY>(eQj}!HKIV$Fz*H=-=Kn)N0D6u`(;iO|VraI4fu_W` z;b5{7;Lyx4za}DU#+U7}=H0dAS#YJJ&g2!P@Htu-AL&w=-)*%P9h2{wR|@?Ff9~)b z^+e_3Hetq7W%ls{!?<6&Y$Z;NNB41pvrv)|MET6AZXFXJeFqbFW5@i5WGzl?bP+~? z*&_puH;wKv2)9T_d+P`bLvJFqX#j&xa*-;0nGBbQf0DC>o~=J_Wmtf*2SZQr?{i~X z9-IbRH8{iy?<0v9Ir1?$66+igy|yDQ5J~A9sFX@Pe<*kCY8+MwH?I z`P}zfQ6l^AO8ehZ=l^ZR;R%uu4;BK*=?W9t|0{+-at(MQZ(CtG=EJFNaFMlKCMXu30(gJUqj5+ z`GM|!keqcj;FKTa_qq;{*dHRXAq157hlB@kL#8%yAm2AgfU|*rDKX@FLlp=HL8ddv zAWLCHe@DcDeB2}fl7#=0+#<05c3=VqM*O3bkr@9X4nO|)q0hU;Gye{L8ZN*NH8Id@mP-u;Fmb8YuorjLrW&ndip8CN%_qp982r w1WEnz9^$&s1hkp_3#lPJQ~!HI7WYYjA7>z!`?f%npAh2%rB@vD|Lau$2O)#1n*aa+ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ee6ba9a3ac4..dcf5e2cb7b3 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=c9490e938b221daf0094982288e4038deed954a3f12fb54cbf270ddf4e37d879 -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip +distributionSha256Sum=cd5c2958a107ee7f0722004a12d0f8559b4564c34daad7df06cffd4d12a426d0 +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From affee6c23f2f5192813f1be84b4ec050116b56fd Mon Sep 17 00:00:00 2001 From: Stefan Decker Date: Thu, 10 Feb 2022 10:02:42 +0100 Subject: [PATCH 2387/5615] #5032 - MS SQL Server disable encrypt by default (#5033) This disables the JDBC driver encrypt option by default for MS SQL Server container connections. Since release 10.1.0 of the MS JDBC driver, the encrypt option is enabled by default (see https://github.com/microsoft/mssql-jdbc/releases/tag/v10.1.0). To be able to use the container without having to manually add params the encrypt option will be switched off if it is not set. Signed-off-by: Decker, Stefan Co-authored-by: Kevin Wittek --- modules/mssqlserver/build.gradle | 3 +-- .../containers/MSSQLServerContainer.java | 11 +++++++++++ .../junit/mssqlserver/SimpleMSSQLServerTest.java | 3 +-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index 1e554440939..1f754303ae1 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -10,12 +10,11 @@ dependencies { compileOnly 'io.r2dbc:r2dbc-mssql:0.8.7.RELEASE' testImplementation project(':jdbc-test') - testImplementation 'com.microsoft.sqlserver:mssql-jdbc:9.5.0.jre8-preview' + testImplementation 'com.microsoft.sqlserver:mssql-jdbc:10.2.0.jre8' testImplementation project(':r2dbc') testImplementation 'io.r2dbc:r2dbc-mssql:0.8.7.RELEASE' // MSSQL's wait strategy requires the JDBC driver testImplementation testFixtures(project(':r2dbc')) - testImplementation 'com.microsoft.sqlserver:mssql-jdbc:9.5.0.jre8-preview' } diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java index ecccc320b28..fe6270d04c7 100644 --- a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java @@ -91,6 +91,17 @@ public String getDriverClassName() { return "com.microsoft.sqlserver.jdbc.SQLServerDriver"; } + @Override + protected String constructUrlForConnection(String queryString) { + // The JDBC driver of MS SQL Server enables encryption by default for versions > 10.1.0. + // We need to disable it by default to be able to use the container without having to pass extra params. + // See https://github.com/microsoft/mssql-jdbc/releases/tag/v10.1.0 + if (urlParameters.keySet().stream().map(String::toLowerCase).noneMatch("encrypt"::equals)) { + urlParameters.put("encrypt", "false"); + } + return super.constructUrlForConnection(queryString); + } + @Override public String getJdbcUrl() { String additionalUrlParams = constructUrlParameters(";", ";"); diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java index 100b29b34f7..1530760b673 100644 --- a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java +++ b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java @@ -1,7 +1,6 @@ package org.testcontainers.junit.mssqlserver; import org.junit.Test; -import org.testcontainers.MSSQLServerTestImages; import org.testcontainers.containers.MSSQLServerContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; @@ -11,7 +10,7 @@ import java.sql.Statement; import static org.hamcrest.CoreMatchers.containsString; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.testcontainers.MSSQLServerTestImages.MSSQL_SERVER_IMAGE; From 71a073bc5ddfce38b2b5348d9d7d4eb63f296290 Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Thu, 17 Feb 2022 14:40:19 +0100 Subject: [PATCH 2388/5615] Polish code samples on container logs so that they are easier to copy & paste (#5038) --- docs/features/container_logs.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/features/container_logs.md b/docs/features/container_logs.md index d603981059a..a063e5329e4 100644 --- a/docs/features/container_logs.md +++ b/docs/features/container_logs.md @@ -40,19 +40,19 @@ By default both standard out and standard error will both be emitted at INFO lev Standard error may be emitted at ERROR level, if desired: ```java -Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(log).withSeparateOutputStreams() +Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(LOGGER).withSeparateOutputStreams(); ``` The [Mapped Diagnostic Context (MDC)](http://logback.qos.ch/manual/mdc.html) for emitted messages may be configured using the `withMdc(...)` option: ```java -Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(log).withMdc("key", "value") +Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(LOGGER).withMdc("key", "value"); ``` or using an existing map of key-value pairs: ```java -Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(log).withMdc(map) +Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(LOGGER).withMdc(map); ``` ### Capturing container output as a String From bfb8cecd69f852eb2ffa8ee93edbfb0edc54762a Mon Sep 17 00:00:00 2001 From: Yannick Weber Date: Thu, 17 Feb 2022 14:54:27 +0100 Subject: [PATCH 2389/5615] New Module: HiveMQ (#4797) Co-authored-by: Jochen Mader --- .github/dependabot.yml | 5 + .../strategy/MultiLogMessageWaitStrategy.java | 59 ++ docs/modules/hivemq.md | 235 +++++++ docs/modules_logos/hivemq-module.png | Bin 0 -> 165340 bytes mkdocs.yml | 1 + modules/hivemq/build.gradle | 37 ++ .../hivemq/HiveMQContainer.java | 573 ++++++++++++++++++ .../hivemq/HiveMQExtension.java | 287 +++++++++ .../org/testcontainers/hivemq/PathUtil.java | 26 + .../hivemq/ContainerWithControlCenterIT.java | 34 ++ .../hivemq/ContainerWithCustomConfigIT.java | 43 ++ ...ContainerWithExtensionFromDirectoryIT.java | 43 ++ .../hivemq/ContainerWithExtensionIT.java | 41 ++ .../ContainerWithExtensionSubclassIT.java | 35 ++ .../ContainerWithFileInExtensionHomeIT.java | 77 +++ .../hivemq/ContainerWithFileInHomeIT.java | 75 +++ .../hivemq/ContainerWithLicenseIT.java | 78 +++ .../ContainerWithoutPlatformExtensionsIT.java | 135 +++++ .../hivemq/CreateFileInCopiedDirectoryIT.java | 92 +++ .../CreateFileInExtensionDirectoryIT.java | 76 +++ ...DisableEnableExtensionFromDirectoryIT.java | 37 ++ .../hivemq/DisableEnableExtensionIT.java | 46 ++ .../hivemq/HiveMQExtensionTest.java | 13 + .../hivemq/HiveMQTestContainerCore.java | 82 +++ .../testcontainers/hivemq/PathUtilTest.java | 43 ++ .../hivemq/docs/DemoDisableExtensionsIT.java | 63 ++ .../hivemq/docs/DemoExtensionTestsIT.java | 57 ++ .../hivemq/docs/DemoFilesIT.java | 62 ++ .../hivemq/docs/DemoHiveMQContainerIT.java | 61 ++ .../hivemq/util/MyExtension.java | 37 ++ .../util/MyExtensionWithSubclasses.java | 26 + .../hivemq/util/PublishModifier.java | 16 + .../hivemq/util/TestPublishModifiedUtil.java | 48 ++ .../src/test/resources/additionalFile.txt | 0 modules/hivemq/src/test/resources/config.xml | 15 + .../src/test/resources/inMemoryConfig.xml | 6 + .../src/test/resources/logback-test.xml | 18 + .../hivemq-extension.xml | 8 + .../modifier-extension-1.0-SNAPSHOT.jar | Bin 0 -> 102179 bytes .../modifier-extension/hivemq-extension.xml | 8 + .../modifier-extension-1.0-SNAPSHOT.jar | Bin 0 -> 102179 bytes .../test/resources/myExtensionLicense.elic | 0 .../hivemq/src/test/resources/myLicense.lic | 0 43 files changed, 2598 insertions(+) create mode 100644 core/src/main/java/org/testcontainers/containers/wait/strategy/MultiLogMessageWaitStrategy.java create mode 100644 docs/modules/hivemq.md create mode 100644 docs/modules_logos/hivemq-module.png create mode 100644 modules/hivemq/build.gradle create mode 100644 modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java create mode 100644 modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQExtension.java create mode 100755 modules/hivemq/src/main/java/org/testcontainers/hivemq/PathUtil.java create mode 100644 modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithControlCenterIT.java create mode 100755 modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java create mode 100755 modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionFromDirectoryIT.java create mode 100755 modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionIT.java create mode 100755 modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionSubclassIT.java create mode 100755 modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInExtensionHomeIT.java create mode 100755 modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInHomeIT.java create mode 100755 modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithLicenseIT.java create mode 100644 modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java create mode 100755 modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java create mode 100755 modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInExtensionDirectoryIT.java create mode 100644 modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java create mode 100644 modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java create mode 100644 modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQExtensionTest.java create mode 100644 modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQTestContainerCore.java create mode 100755 modules/hivemq/src/test/java/org/testcontainers/hivemq/PathUtilTest.java create mode 100644 modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java create mode 100644 modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoExtensionTestsIT.java create mode 100644 modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java create mode 100644 modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java create mode 100755 modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtension.java create mode 100755 modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtensionWithSubclasses.java create mode 100755 modules/hivemq/src/test/java/org/testcontainers/hivemq/util/PublishModifier.java create mode 100755 modules/hivemq/src/test/java/org/testcontainers/hivemq/util/TestPublishModifiedUtil.java create mode 100755 modules/hivemq/src/test/resources/additionalFile.txt create mode 100755 modules/hivemq/src/test/resources/config.xml create mode 100644 modules/hivemq/src/test/resources/inMemoryConfig.xml create mode 100644 modules/hivemq/src/test/resources/logback-test.xml create mode 100755 modules/hivemq/src/test/resources/modifier-extension-wrong-name/hivemq-extension.xml create mode 100644 modules/hivemq/src/test/resources/modifier-extension-wrong-name/modifier-extension-1.0-SNAPSHOT.jar create mode 100755 modules/hivemq/src/test/resources/modifier-extension/hivemq-extension.xml create mode 100644 modules/hivemq/src/test/resources/modifier-extension/modifier-extension-1.0-SNAPSHOT.jar create mode 100755 modules/hivemq/src/test/resources/myExtensionLicense.elic create mode 100755 modules/hivemq/src/test/resources/myLicense.lic diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 355d44f2bb4..5d67f278219 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -57,6 +57,11 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/hivemq" + schedule: + interval: "monthly" + open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/influxdb" schedule: diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/MultiLogMessageWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/MultiLogMessageWaitStrategy.java new file mode 100644 index 00000000000..0d342f3050f --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/MultiLogMessageWaitStrategy.java @@ -0,0 +1,59 @@ +package org.testcontainers.containers.wait.strategy; + +import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.ContainerLaunchException; +import org.testcontainers.containers.output.OutputFrame; +import org.testcontainers.containers.output.WaitingConsumer; +import org.testcontainers.utility.LogUtils; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Predicate; + +/** + * This is a wait strategy to wait for multiple log patterns. + * The wait strategy will continue when every log pattern is matched at least once. + */ +public class MultiLogMessageWaitStrategy extends AbstractWaitStrategy { + + private final ConcurrentHashMap regexes = new ConcurrentHashMap<>(); + + @Override + protected void waitUntilReady() { + WaitingConsumer waitingConsumer = new WaitingConsumer(); + LogUtils.followOutput(DockerClientFactory.instance().client(), waitStrategyTarget.getContainerId(), waitingConsumer); + + Predicate waitPredicate = outputFrame -> { + if (regexes.isEmpty()) { + return true; + } + regexes.entrySet().forEach(regexHasMatched -> { + final boolean matched = outputFrame.getUtf8String().matches("(?s)" + regexHasMatched.getKey()); + if (matched) { + regexHasMatched.setValue(true); + } + }); + return regexes.values().stream().reduce(Boolean::logicalAnd).orElse(true); + }; + + try { + waitingConsumer.waitUntil(waitPredicate, startupTimeout.getSeconds(), TimeUnit.SECONDS, 1); + } catch (TimeoutException e) { + throw new ContainerLaunchException("Timed out waiting for log output matching '" + regexes + "'"); + } + } + + public MultiLogMessageWaitStrategy withRegEx(final String regEx) { + regexes.put(regEx, false); + return this; + } + + public MultiLogMessageWaitStrategy reset() { + for (final Map.Entry stringBooleanEntry : regexes.entrySet()) { + stringBooleanEntry.setValue(false); + } + return this; + } +} diff --git a/docs/modules/hivemq.md b/docs/modules/hivemq.md new file mode 100644 index 00000000000..1e09bac4fc0 --- /dev/null +++ b/docs/modules/hivemq.md @@ -0,0 +1,235 @@ +# HiveMQ Module + +drawing + +Automatic starting HiveMQ docker containers for JUnit4 and JUnit5 tests. +This enables testing MQTT client applications and integration testing of custom HiveMQ extensions. + +- Community forum: https://community.hivemq.com/ +- HiveMQ website: https://www.hivemq.com/ +- MQTT resources: + - [MQTT Essentials](https://www.hivemq.com/mqtt-essentials/) + - [MQTT 5 Essentials](https://www.hivemq.com/mqtt-5/) + +Please make sure to check out the hivemq-docs for the [Community Edition](https://github.com/hivemq/hivemq-community-edition/wiki/) +and the [Enterprise Edition](https://www.hivemq.com/docs/hivemq/4.7/user-guide/). + +## Using HiveMQ CE/EE + +HiveMQ provides different editions of on [Docker Hub](https://hub.docker.com/u/hivemq): + +- the open source [Community Edition](https://github.com/hivemq/hivemq-community-edition) which +is published as *hivemq/hivemq-ce*. +- the [Enterprise Edition](https://www.hivemq.com/docs/hivemq/4.7/user-guide/) which is published as *hivemq/hivemq4*. + +Both editions can be used directly: + +Using the Community Edition: + +[Community Edition HiveMQ image](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java) inside_block:ceVersion + + +Using the Enterprise Edition: + +[Enterprise Edition HiveMQ image](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java) inside_block:eeVersion + + +Using a specifc version is possible by using the tag: + +[Specific HiveMQ Version](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java) inside_block:specificVersion + + +## Test your MQTT 3 and MQTT 5 client application + +Using an Mqtt-client (e.g. the [HiveMQ-Mqtt-Client](https://github.com/hivemq/hivemq-mqtt-client)) you can start +testing directly. + + +[MQTT5 Client](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java) inside_block:mqtt5client + + +## Settings + +There are several things that can be adjusted before container setup. +The following example shows how to enable the Control Center (this is an enterprise feature), set the log level to DEBUG +and load a HiveMQ-config-file from the classpath. + + +[Config Examples](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java) inside_block:eeVersionWithControlCenter + + +--- +**Note:** +The Control Center of HiveMQ can be accessed via the URL presented in the output of the starting container: + +``` +2021-09-10 10:35:53,511 INFO - The HiveMQ Control Center is reachable under: http://localhost:55032 +``` + +Please be aware that the Control Center is a feature of the enterprise edition of HiveMQ and thus only available with +the enterprise image. + +--- + +## Testing HiveMQ extensions + +Using the [Extension SDK](https://github.com/hivemq/hivemq-extension-sdk) the functionality of all editions of HiveMQ +can be extended. +The HiveMQ module also supports testing your own custom extensions. + +### Wait Strategy + +The raw HiveMQ module is built to wait for certain startup log messages to signal readiness. +Since extensions are loaded dynamically they can be available a short while after the main container has started. +We therefore provide custom wait conditions for HiveMQ Extensions: + +The following will specify an extension to be loaded from **src/test/resources/modifier-extension** into the container and +wait for an extension named **'My Extension Name'** to be started: + + +[Custom Wait Strategy](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoExtensionTestsIT.java) inside_block:waitStrategy + + +Next up we have an example for using an extension directly from the classpath and waiting directly on the extension: + + +[Extension from Classpath](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoExtensionTestsIT.java) inside_block:extensionClasspath + + +--- +**Note** Debugging extensions + +Both examples contain ```.withDebugging()``` which enables remote debugging on the container. +With debugging enabled you can start putting breakpoints right into your extensions. + +--- + +### Testing extensions using Gradle + +In a Gradle based HiveMQ Extension project, testing is supported using the dedicated [HiveMQ Extension Gradle Plugin](https://github.com/hivemq/hivemq-extension-gradle-plugin/README.md). + +The plugin adds an `integrationTest` task which executes tests from the `integrationTest` source set. +- Integration test source files are defined in `src/integrationTest`. +- Integration test dependencies are defined via the `integrationTestImplementation`, `integrationTestRuntimeOnly`, etc. configurations. + +The `integrationTest` task builds the extension and unzips it to the `build/hivemq-extension-test` directory. +The tests can then load the built extension into the HiveMQ Testcontainer. + + +[Extension from filesystem](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java) inside_block:startFromFilesystem + + +### Enable/Disable an extension + +It is possible to enable and disable HiveMQ extensions during runtime. Extensions can also be disabled on startup. + +--- +**Note**: that disabling or enabling of extension during runtime is only supported in HiveMQ 4 Enterprise Edition Containers. + +--- + +The following example shows how to start a HiveMQ container with the extension called **my-extension** being disabled. + + + +[Disable Extension at startup](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java) inside_block:startDisabled + + +The following test then proceeds to enable and then disable the extension: + + +[Enable/Disable extension at runtime](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java) inside_block:runtimeEnable + + +## Enable/Disable an extension loaded from a folder + +Extensions loaded from an extension folder during runtime can also be enabled/disabled on the fly. +If the extension folder contains a DISABLED file, the extension will be disabled during startup. + +--- +**Note**: that disabling or enabling of extension during runtime is only supported in HiveMQ 4 Enterprise Edition Containers. + +--- + +We first load the extension from the filesytem: + +[Extension from filesystem](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java) inside_block:startFromFilesystem + + +Now we can enable/disable the extension using its name: + + +[Enable/Disable extension at runtime](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java) inside_block:runtimeEnableFilesystem + + +### Remove prepackaged HiveMQ Extensions + +Since HiveMQ's 4.4 release, HiveMQ Docker images come with the HiveMQ Extension for Kafka, the HiveMQ Enterprise Bridge Extension +and the HiveMQ Enterprise Security Extension. +These Extensions are disabled by default, but sometimes you my need to remove them before the container starts. + +Removing all extension is as simple as: + + +[Remove all extensions](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java) inside_block:noExtensions + + +A single extension (e.g. Kafka) can be removed as easily: + +[Remove a specific extension](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java) inside_block:noKafkaExtension + + +## Put files into the container + +### Put a file into HiveMQ home + + +[Put file into HiveMQ home](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java) inside_block:hivemqHome + + +### Put files into extension home + + +[Put file into HiveMQ-Extension home](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java) inside_block:extensionHome + + +### Put license files into the container + + +[Put license file into the HiveMQ container](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java) inside_block:withLicenses + + + +### Customize the Container further + +Since the `HiveMQContainer` extends from [Testcontainer's](https://github.com/testcontainers) `GenericContainer` the container +can be customized as desired. + +## Add to your project + +### Gradle + +Add to `build.gradle`: + +````groovy +testImplementation 'org.testcontainers:hivemq:{{latest_version}}' +```` + +Add to `build.gradle.kts`: + +````kotlin +testImplementation("org.testcontainers:hivemq:{{latest_version}}") +```` + +### Maven + +Add to `pom.xml`: + +```xml + + org.testcontainers + hivemq + {{latest_version}} + test + +``` diff --git a/docs/modules_logos/hivemq-module.png b/docs/modules_logos/hivemq-module.png new file mode 100644 index 0000000000000000000000000000000000000000..55c0ee1644ae05a5ff5b36f90ebdb1006b296f42 GIT binary patch literal 165340 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ⅈLGoj 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 E0rq9^#Q*>R literal 0 HcmV?d00001 diff --git a/mkdocs.yml b/mkdocs.yml index e4115625a7f..7ace7382e41 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -67,6 +67,7 @@ nav: - modules/docker_compose.md - modules/elasticsearch.md - modules/gcloud.md + - modules/hivemq.md - modules/k3s.md - modules/kafka.md - modules/localstack.md diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle new file mode 100644 index 00000000000..779ac2ec9d8 --- /dev/null +++ b/modules/hivemq/build.gradle @@ -0,0 +1,37 @@ +description = "TestContainers :: HiveMQ" + +dependencies { + api(project(":testcontainers")) + api("org.jetbrains:annotations:23.0.0") + + shaded("org.apache.commons:commons-lang3:3.12.0") + shaded("commons-io:commons-io:2.11.0") + shaded("org.javassist:javassist:3.28.0-GA") + shaded("org.jboss.shrinkwrap:shrinkwrap-api:1.2.6") + shaded("org.jboss.shrinkwrap:shrinkwrap-impl-base:1.2.6") + shaded("net.lingala.zip4j:zip4j:2.9.1") + + testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") + testImplementation(project(":junit-jupiter")) + testImplementation("com.hivemq:hivemq-extension-sdk:4.7.3") + testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") + testImplementation("org.apache.httpcomponents:httpclient:4.5.13") + testImplementation("ch.qos.logback:logback-classic:1.2.10") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + +test { + useJUnitPlatform() + testLogging { + events "passed", "skipped", "failed" + } + javaLauncher = javaToolchains.launcherFor { + languageVersion = JavaLanguageVersion.of(11) + } +} + +compileTestJava { + javaCompiler = javaToolchains.compilerFor { + languageVersion = JavaLanguageVersion.of(11) + } +} diff --git a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java new file mode 100644 index 00000000000..37f9a9259d1 --- /dev/null +++ b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java @@ -0,0 +1,573 @@ +package org.testcontainers.hivemq; + +import com.github.dockerjava.api.command.InspectContainerResponse; +import org.apache.commons.io.FileUtils; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.event.Level; +import org.testcontainers.containers.ContainerLaunchException; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.MultiLogMessageWaitStrategy; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +public class HiveMQContainer extends GenericContainer { + + private static final Logger LOGGER = LoggerFactory.getLogger(HiveMQContainer.class); + + private static final DockerImageName DEFAULT_HIVEMQ_EE_IMAGE_NAME = DockerImageName.parse("hivemq/hivemq4"); + private static final DockerImageName DEFAULT_HIVEMQ_CE_IMAGE_NAME = DockerImageName.parse("hivemq/hivemq-ce"); + + private static final int DEBUGGING_PORT = 9000; + private static final int MQTT_PORT = 1883; + private static final int CONTROL_CENTER_PORT = 8080; + @SuppressWarnings("OctalInteger") + private static final int MODE = 0777; + private static final @NotNull Pattern EXTENSION_ID_PATTERN = Pattern.compile("(.+?)"); + + private final @NotNull ConcurrentHashMap containerOutputLatches = new ConcurrentHashMap<>(); + private boolean controlCenterEnabled = false; + private boolean debugging = false; + + private final @NotNull Set prepackagedExtensionsToRemove = new HashSet<>(); + private boolean removeAllPrepackagedExtensions = false; + + private final @NotNull MultiLogMessageWaitStrategy waitStrategy = new MultiLogMessageWaitStrategy(); + + public HiveMQContainer(final @NotNull DockerImageName dockerImageName) { + super(dockerImageName); + + dockerImageName.assertCompatibleWith(DEFAULT_HIVEMQ_CE_IMAGE_NAME, DEFAULT_HIVEMQ_EE_IMAGE_NAME); + + addExposedPort(MQTT_PORT); + + waitStrategy.withRegEx("(.*)Started HiveMQ in(.*)"); + waitingFor(waitStrategy); + + withLogConsumer(outputFrame -> { + final String utf8String = outputFrame.getUtf8String(); + if (debugging && utf8String.startsWith("Listening for transport dt_socket at address:")) { + System.out.println("Listening for transport dt_socket at address: " + getMappedPort(DEBUGGING_PORT)); + } + if (!containerOutputLatches.isEmpty()) { + containerOutputLatches.forEach((regEx, latch) -> { + if (outputFrame.getUtf8String().matches("(?s)" + regEx)) { + LOGGER.debug("Container Output '{}' matched RegEx '{}'", utf8String, regEx); + latch.countDown(); + } else { + LOGGER.debug("Container Output '{}' did not match RegEx '{}'", utf8String, regEx); + } + }); + } + }); + } + + @Override + protected void configure() { + final String removeCommand; + withCreateContainerCmdModifier(it -> it.withEntrypoint("/bin/sh")); + + if (removeAllPrepackagedExtensions || !prepackagedExtensionsToRemove.isEmpty()) { + + if (removeAllPrepackagedExtensions) { + removeCommand = "rm -rf /opt/hivemq/extensions/** &&"; + } else { + removeCommand = prepackagedExtensionsToRemove.stream() + .map(extensionId -> "rm -rf /opt/hivemq/extensions/" + extensionId + "&&") + .collect(Collectors.joining()); + } + } else { + removeCommand = ""; + } + setCommand( + "-c", + removeCommand + + "cp -r '/opt/hivemq/temp-extensions/'* /opt/hivemq/extensions/ " + + "; rm -rf /opt/hivemq/temp-extensions/** " + + "&& chmod -R 777 /opt/hivemq/extensions " + + "&& /opt/docker-entrypoint.sh /opt/hivemq/bin/run.sh" + ); + } + + protected void containerIsStarted(final @NotNull InspectContainerResponse containerInfo) { + if (controlCenterEnabled) { + LOGGER.info("The HiveMQ Control Center is reachable under: http://{}:{}", getHost(), getMappedPort(CONTROL_CENTER_PORT)); + } + } + + /** + * Adds a wait condition for the extension with this name. + *

+ * Must be called before the container is started. + * + * @param extensionName the extension to wait for + * @return self + */ + public @NotNull HiveMQContainer waitForExtension(final @NotNull String extensionName) { + final String regEX = "(.*)Extension \"" + extensionName + "\" version (.*) started successfully(.*)"; + waitStrategy.withRegEx(regEX); + return self(); + } + + /** + * Adds a wait condition for this {@link HiveMQExtension} + *

+ * Must be called before the container is started. + * + * @param extension the extension to wait for + * @return self + */ + public @NotNull HiveMQContainer waitForExtension(final @NotNull HiveMQExtension extension) { + return this.waitForExtension(extension.getName()); + } + + /** + * Enables the possibility for remote debugging clients to connect. + *

+ * Must be called before the container is started. + * + * @return self + */ + public @NotNull HiveMQContainer withDebugging() { + debugging = true; + addExposedPorts(DEBUGGING_PORT); + withEnv("JAVA_OPTS", "-agentlib:jdwp=transport=dt_socket,address=0.0.0.0:" + DEBUGGING_PORT + ",server=y,suspend=y"); + return self(); + } + + /** + * Sets the logging {@link Level} inside the container. + *

+ * Must be called before the container is started. + * + * @param level the {@link Level} + * @return self + */ + public @NotNull HiveMQContainer withLogLevel(final @NotNull Level level) { + this.withEnv("HIVEMQ_LOG_LEVEL", level.name()); + return self(); + } + + /** + * Wraps the given class and all its subclasses into an extension + * and puts it into '/opt/hivemq/temp-extensions/{extension-id}' inside the container. + *

+ * Must be called before the container is started. + *

+ * The contents of the '/opt/hivemq/temp-extensions/' directory are copied to '/opt/hivemq/extensions/' before the container is started. + * + * @param hiveMQExtension the {@link HiveMQExtension} of the extension + * @return self + */ + public @NotNull HiveMQContainer withExtension(final @NotNull HiveMQExtension hiveMQExtension) { + try { + final File extension = hiveMQExtension.createExtension(hiveMQExtension); + final MountableFile mountableExtension = MountableFile.forHostPath(extension.getPath(), MODE); + withCopyFileToContainer(mountableExtension, "/opt/hivemq/temp-extensions/" + hiveMQExtension.getId()); + } catch (final Exception e) { + throw new ContainerLaunchException(e.getMessage() == null ? "" : e.getMessage(), e); + } + return self(); + } + + /** + * Puts the given extension folder into '/opt/hivemq/temp-extensions/{directory-name}' inside the container. + * It must at least contain a valid hivemq-extension.xml and a valid extension.jar in order to be executed. + * The directory-name is taken from the id defined in the hivemq-extension.xml. + *

+ * Must be called before the container is started. + *

+ * The contents of the '/opt/hivemq/temp-extensions/' directory are copied to '/opt/hivemq/extensions/' before the container is started. + * + * @param mountableExtension the extension folder on the host machine + * @return self + */ + public @NotNull HiveMQContainer withExtension(final @NotNull MountableFile mountableExtension) { + final File extensionDir = new File(mountableExtension.getResolvedPath()); + if (!extensionDir.exists()) { + throw new ContainerLaunchException("Extension '" + mountableExtension.getFilesystemPath() + "' could not be mounted. It does not exist."); + } + if (!extensionDir.isDirectory()) { + throw new ContainerLaunchException("Extension '" + mountableExtension.getFilesystemPath() + "' could not be mounted. It is not a directory."); + } + try { + final String extensionDirName = getExtensionDirectoryName(extensionDir); + final String containerPath = "/opt/hivemq/temp-extensions/" + extensionDirName; + withCopyFileToContainer(cloneWithFileMode(mountableExtension), containerPath); + LOGGER.info("Putting extension '{}' into '{}'", extensionDirName, containerPath); + } catch (final Exception e) { + throw new ContainerLaunchException(e.getMessage() == null ? "" : e.getMessage(), e); + } + return self(); + } + + private @NotNull String getExtensionDirectoryName(final @NotNull File extensionDirectory) throws IOException { + final File file = new File(extensionDirectory, "hivemq-extension.xml"); + final String xml = FileUtils.readFileToString(file, StandardCharsets.UTF_8); + final Matcher matcher = EXTENSION_ID_PATTERN.matcher(xml); + + if (!matcher.find()) { + throw new IllegalStateException("Could not parse extension id from '" + file.getAbsolutePath() + "'"); + } + return matcher.group(1); + } + + /** + * Removes the specified prepackaged extension folders from '/opt/hivemq/extensions' before the container is started. + *

+ * Must be called before the container is started. + * + * @param extensionIds the prepackaged extensions to remove + * @return self + */ + public @NotNull HiveMQContainer withoutPrepackagedExtensions(final @NotNull String... extensionIds) { + Collections.addAll(prepackagedExtensionsToRemove, extensionIds); + return self(); + } + + /** + * Removes all prepackaged extension folders from '/opt/hivemq/extensions' before the container is started. + *

+ * Must be called before the container is started. + * + * @return self + */ + public @NotNull HiveMQContainer withoutPrepackagedExtensions() { + removeAllPrepackagedExtensions = true; + return self(); + } + + /** + * Puts the given license into '/opt/hivemq/license/' inside the container. + * It must end with '.lic' or '.elic'. + *

+ * Must be called before the container is started. + * + * @param mountableLicense the license file on the host machine + * @return self + */ + public @NotNull HiveMQContainer withLicense(final @NotNull MountableFile mountableLicense) { + final File licenseFile = new File(mountableLicense.getResolvedPath()); + if (!licenseFile.exists()) { + throw new ContainerLaunchException("License file '" + mountableLicense.getFilesystemPath() + "' does not exist."); + } + if (!licenseFile.getName().endsWith(".lic") && !licenseFile.getName().endsWith(".elic")) { + throw new ContainerLaunchException("License file '" + mountableLicense.getFilesystemPath() + "' does not end wit '.lic' or '.elic'."); + } + final String containerPath = "/opt/hivemq/license/" + licenseFile.getName(); + withCopyFileToContainer(cloneWithFileMode(mountableLicense), containerPath); + LOGGER.info("Putting license '{}' into '{}'.", licenseFile.getAbsolutePath(), containerPath); + return self(); + } + + /** + * Overwrites the HiveMQ configuration in '/opt/hivemq/conf/' inside the container. + *

+ * Must be called before the container is started. + * + * @param mountableConfig the config file on the host machine + * @return self + */ + public @NotNull HiveMQContainer withHiveMQConfig(final @NotNull MountableFile mountableConfig) { + final File config = new File(mountableConfig.getResolvedPath()); + if (!config.exists()) { + throw new ContainerLaunchException("HiveMQ config file '" + mountableConfig.getFilesystemPath() + "' does not exist."); + } + final String containerPath = "/opt/hivemq/conf/config.xml"; + withCopyFileToContainer(cloneWithFileMode(mountableConfig), containerPath); + LOGGER.info("Putting '{}' into '{}'.", config.getAbsolutePath(), containerPath); + return self(); + } + + /** + * Puts the given file into the root of the extension's home '/opt/hivemq/temp-extensions/{extensionId}/'. + *

+ * Must be called before the container is started. + *

+ * The contents of the '/opt/hivemq/temp-extensions/' directory are copied to '/opt/hivemq/extensions/' before the container is started. + * + * @param file the file on the host machine + * @param extensionId the extension + * @return self + */ + public @NotNull HiveMQContainer withFileInExtensionHomeFolder( + final @NotNull MountableFile file, + final @NotNull String extensionId) { + + return withFileInExtensionHomeFolder(file, extensionId, ""); + } + + /** + * Puts the given file into given subdirectory of the extensions's home '/opt/hivemq/temp-extensions/{id}/{pathInExtensionHome}/' + *

+ * Must be called before the container is started. + *

+ * The contents of the '/opt/hivemq/temp-extensions/' directory are copied to '/opt/hivemq/extensions/' before the container is started. + * + * @param file the file on the host machine + * @param extensionId the extension + * @param pathInExtensionHome the path + * @return self + */ + public @NotNull HiveMQContainer withFileInExtensionHomeFolder( + final @NotNull MountableFile file, + final @NotNull String extensionId, + final @NotNull String pathInExtensionHome) { + + return withFileInHomeFolder(file, "/temp-extensions/" + extensionId + PathUtil.prepareAppendPath(pathInExtensionHome)); + } + + /** + * Puts the given file into the given subdirectory of the HiveMQ home folder '/opt/hivemq/{pathInHomeFolder}'. + *

+ * Must be called before the container is started. + * + * @param mountableFile the file on the host machine + * @param pathInHomeFolder the path + * @return self + */ + public @NotNull HiveMQContainer withFileInHomeFolder( + final @NotNull MountableFile mountableFile, + final @NotNull String pathInHomeFolder) { + + final File file = new File(mountableFile.getResolvedPath()); + + if (pathInHomeFolder.trim().isEmpty()) { + throw new ContainerLaunchException("pathInHomeFolder must not be empty"); + } + + if (!file.exists()) { + throw new ContainerLaunchException("File '" + mountableFile.getFilesystemPath() + "‘ does not exist."); + } + final String containerPath = "/opt/hivemq" + PathUtil.prepareAppendPath(pathInHomeFolder); + withCopyFileToContainer(cloneWithFileMode(mountableFile), containerPath); + LOGGER.info("Putting file '{}' into container path '{}'.", file.getAbsolutePath(), containerPath); + return self(); + } + + /** + * Disables the extension with the given name and extension directory name. + * This method blocks until the HiveMQ log for successful disabling is consumed or it times out after {timeOut}. + * Note: Disabling Extensions is a HiveMQ Enterprise feature, it will not work when using the HiveMQ Community Edition. + *

+ * This can only be called once the container is started. + * + * @param extensionName the name of the extension to disable + * @param extensionDirectory the name of the extension's directory + * @param timeout the timeout + * @throws TimeoutException if the extension was not disabled within the configured timeout + */ + public void disableExtension( + final @NotNull String extensionName, + final @NotNull String extensionDirectory, + final @NotNull Duration timeout) throws TimeoutException { + + final String regEX = "(.*)Extension \"" + extensionName + "\" version (.*) stopped successfully(.*)"; + try { + final String containerPath = "/opt/hivemq/extensions" + PathUtil.prepareInnerPath(extensionDirectory) + "DISABLED"; + + final CountDownLatch latch = new CountDownLatch(1); + containerOutputLatches.put(regEX, latch); + + execInContainer("touch", containerPath); + LOGGER.info("Putting DISABLED file into container path '{}'", containerPath); + + final boolean await = latch.await(timeout.getSeconds(), TimeUnit.SECONDS); + if (!await) { + throw new TimeoutException("Extension disabling timed out after '" + timeout.getSeconds() + "' seconds. " + + "Maybe you are using a HiveMQ Community Edition image, " + + "which does not support disabling of extensions"); + } + } catch (final InterruptedException | IOException e) { + throw new RuntimeException(e); + } finally { + containerOutputLatches.remove(regEX); + } + } + + /** + * Disables the extension with the given name and extension directory name. + * This method blocks until the HiveMQ log for successful disabling is consumed or it times out after 60 seconds. + * Note: Disabling Extensions is a HiveMQ Enterprise feature, it will not work when using the HiveMQ Community Edition. + *

+ * This can only be called once the container is started. + * + * @param extensionName the name of the extension to disable + * @param extensionDirectory the name of the extension's directory + * @throws TimeoutException if the extension was not disabled within 60 seconds + */ + public void disableExtension( + final @NotNull String extensionName, + final @NotNull String extensionDirectory) throws TimeoutException { + disableExtension(extensionName, extensionDirectory, Duration.ofSeconds(60)); + } + + /** + * Disables the extension. + * This method blocks until the HiveMQ log for successful disabling is consumed or it times out after {timeOut}. + * Note: Disabling Extensions is a HiveMQ Enterprise feature, it will not work when using the HiveMQ Community Edition. + *

+ * This can only be called once the container is started. + * + * @param hiveMQExtension the extension + * @param timeout the timeout + * @throws TimeoutException if the extension was not disabled within the configured timeout + */ + public void disableExtension( + final @NotNull HiveMQExtension hiveMQExtension, + final @NotNull Duration timeout) throws TimeoutException { + disableExtension(hiveMQExtension.getName(), hiveMQExtension.getId(), timeout); + } + + /** + * Disables the extension. + * This method blocks until the HiveMQ log for successful disabling is consumed or it times out after 60 seconds. + * Note: Disabling Extensions is a HiveMQ Enterprise feature, it will not work when using the HiveMQ Community Edition. + *

+ * This can only be called once the container is started. + * + * @param hiveMQExtension the extension + * @throws TimeoutException if the extension was not disabled within 60 seconds + */ + public void disableExtension(final @NotNull HiveMQExtension hiveMQExtension) throws TimeoutException { + disableExtension(hiveMQExtension, Duration.ofSeconds(60)); + } + + /** + * Enables the extension with the given name and extension directory name. + * This method blocks until the HiveMQ log for successful enabling is consumed or it times out after {timeOut}. + * Note: Enabling Extensions is a HiveMQ Enterprise feature, it will not work when using the HiveMQ Community Edition. + *

+ * This can only be called once the container is started. + * + * @param extensionName the name of the extension to disable + * @param extensionDirectory the name of the extension's directory + * @param timeout the timeout + * @throws TimeoutException if the extension was not enabled within the configured timeout + */ + public void enableExtension( + final @NotNull String extensionName, + final @NotNull String extensionDirectory, + final @NotNull Duration timeout) throws TimeoutException { + + final String regEX = "(.*)Extension \"" + extensionName + "\" version (.*) started successfully(.*)"; + try { + final String containerPath = "/opt/hivemq/extensions" + PathUtil.prepareInnerPath(extensionDirectory) + "DISABLED"; + + final CountDownLatch latch = new CountDownLatch(1); + containerOutputLatches.put(regEX, latch); + + execInContainer("rm", "-rf", containerPath); + LOGGER.info("Removing DISABLED file in container path '{}'", containerPath); + + final boolean await = latch.await(timeout.getSeconds(), TimeUnit.SECONDS); + if (!await) { + throw new TimeoutException("Extension enabling timed out after '" + timeout.getSeconds() + "' seconds. " + + "Maybe you are using a HiveMQ Community Edition image, " + + "which does not support disabling of extensions"); + } + } catch (final InterruptedException | IOException e) { + throw new RuntimeException(e); + } finally { + containerOutputLatches.remove(regEX); + } + } + + /** + * Enables the extension with the given name and extension directory name. + * This method blocks until the HiveMQ log for successful enabling is consumed or it times out after 60 seconds. + * Note: Enabling Extensions is a HiveMQ Enterprise feature, it will not work when using the HiveMQ Community Edition. + *

+ * This can only be called once the container is started. + * + * @param extensionName the name of the extension to disable + * @param extensionDirectory the name of the extension's directory + * @throws TimeoutException if the extension was not enabled within 60 seconds + */ + public void enableExtension( + final @NotNull String extensionName, + final @NotNull String extensionDirectory) throws TimeoutException { + enableExtension(extensionName, extensionDirectory, Duration.ofSeconds(60)); + } + + /** + * Enables the extension. + * This method blocks until the HiveMQ log for successful enabling is consumed or it times out after {timeOut}. + * Note: Enabling Extensions is a HiveMQ Enterprise feature, it will not work when using the HiveMQ Community Edition. + *

+ * This can only be called once the container is started. + * + * @param hiveMQExtension the extension + * @param timeout the timeout + * @throws TimeoutException if the extension was not enabled within the configured timeout + */ + public void enableExtension( + final @NotNull HiveMQExtension hiveMQExtension, + final @NotNull Duration timeout) throws TimeoutException { + enableExtension(hiveMQExtension.getName(), hiveMQExtension.getId(), timeout); + } + + /** + * Enables the extension. + * This method blocks until the HiveMQ log for successful enabling is consumed or it times out after {timeOut}. + * Note: Enabling Extensions is a HiveMQ Enterprise feature, it will not work when using the HiveMQ Community Edition. + *

+ * This can only be called once the container is started. + * + * @param hiveMQExtension the extension + * @throws TimeoutException if the extension was not enabled within 60 seconds + */ + public void enableExtension(final @NotNull HiveMQExtension hiveMQExtension) throws TimeoutException { + enableExtension(hiveMQExtension, Duration.ofSeconds(60)); + } + + /** + * Enables connection to the HiveMQ Control Center on host port 8080. + * Note: the control center is a HiveMQ 4 Enterprise feature. + *

+ * Must be called before the container is started. + * + * @return self + */ + public @NotNull HiveMQContainer withControlCenter() { + addExposedPorts(CONTROL_CENTER_PORT); + controlCenterEnabled = true; + return self(); + } + + /** + * Get the mapped port for the MQTT port of the container. + *

+ * Must be called after the container is started. + * + * @return the port on the host machine for mqtt clients to connect + */ + public int getMqttPort() { + return this.getMappedPort(MQTT_PORT); + } + + @Override + protected void containerIsStopping(final @NotNull InspectContainerResponse containerInfo) { + waitStrategy.reset(); + } + + private @NotNull MountableFile cloneWithFileMode(final @NotNull MountableFile mountableFile) { + return MountableFile.forHostPath(mountableFile.getResolvedPath(), HiveMQContainer.MODE); + } +} diff --git a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQExtension.java b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQExtension.java new file mode 100644 index 00000000000..a975439f4b3 --- /dev/null +++ b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQExtension.java @@ -0,0 +1,287 @@ +package org.testcontainers.hivemq; + +import javassist.ClassPool; +import javassist.NotFoundException; +import lombok.Getter; +import org.apache.commons.io.FileUtils; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.exporter.ZipExporter; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.ContainerLaunchException; + +import java.io.File; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +public class HiveMQExtension { + + private static final String VALID_EXTENSION_XML = + "" + // + " %s" + // + " %s" + // + " %s" + // + " %s" + // + " %s" + // + ""; + private static final String EXTENSION_MAIN_CLASS_NAME = "com.hivemq.extension.sdk.api.ExtensionMain"; + private static final Logger LOGGER = LoggerFactory.getLogger(HiveMQExtension.class); + + @Getter + private final @NotNull String id; + + @Getter + private final @NotNull String name; + + @Getter + private final @NotNull String version; + + @Getter + private final int priority; + + @Getter + private final int startPriority; + + @Getter + private final boolean disabledOnStartup; + + @Getter + private final @NotNull Class mainClass; + + private final @NotNull List> additionalClasses; + + private HiveMQExtension( + final @NotNull String id, + final @NotNull String name, + final @NotNull String version, + final int priority, + final int startPriority, + final boolean disabledOnStartup, + final @NotNull Class mainClass, + final @NotNull List> additionalClasses) { + + this.id = id; + this.name = name; + this.version = version; + this.priority = priority; + this.startPriority = startPriority; + this.disabledOnStartup = disabledOnStartup; + this.mainClass = mainClass; + this.additionalClasses = additionalClasses; + } + + @NotNull File createExtension(final @NotNull HiveMQExtension hiveMQExtension) + throws Exception { + + final File tempDir = Files.createTempDirectory("").toFile(); + + final File extensionDir = new File(tempDir, hiveMQExtension.getId()); + FileUtils.writeStringToFile(new File(extensionDir, "hivemq-extension.xml"), + String.format( + VALID_EXTENSION_XML, + hiveMQExtension.getId(), + hiveMQExtension.getName(), + hiveMQExtension.getVersion(), + hiveMQExtension.getPriority(), + hiveMQExtension.getStartPriority()), + Charset.defaultCharset()); + + if (hiveMQExtension.isDisabledOnStartup()) { + final File disabled = new File(extensionDir, "DISABLED"); + final boolean newFile = disabled.createNewFile(); + if (!newFile) { + throw new ContainerLaunchException("Could not create DISABLED file '" + disabled.getAbsolutePath() + "' on host machine."); + } + } + + final JavaArchive javaArchive = + ShrinkWrap.create(JavaArchive.class) + .addAsServiceProvider(EXTENSION_MAIN_CLASS_NAME, hiveMQExtension.getMainClass().getName()); + + putSubclassesIntoJar(hiveMQExtension.getId(), hiveMQExtension.getMainClass(), javaArchive); + for (final Class additionalClass : hiveMQExtension.getAdditionalClasses()) { + javaArchive.addClass(additionalClass); + putSubclassesIntoJar(hiveMQExtension.getId(), additionalClass, javaArchive); + } + + javaArchive.as(ZipExporter.class).exportTo(new File(extensionDir, "extension.jar")); + + return extensionDir; + } + + private void putSubclassesIntoJar( + final @NotNull String extensionId, + final @Nullable Class clazz, + final @NotNull JavaArchive javaArchive) throws NotFoundException { + + if (clazz != null) { + final Set subClassNames = + ClassPool.getDefault().get(clazz.getName()).getClassFile().getConstPool().getClassNames(); + for (final String subClassName : subClassNames) { + final String className = subClassName.replaceAll("/", "."); + + if (!className.startsWith("[L")) { + LOGGER.debug("Trying to package subclass '{}' into extension '{}'.", className, extensionId); + javaArchive.addClass(className); + } else { + LOGGER.debug("Class '{}' will be ignored.", className); + } + } + } + } + + public @NotNull List> getAdditionalClasses() { + return Collections.unmodifiableList(additionalClasses); + } + + public static @NotNull Builder builder() { + return new Builder(); + } + + public static final class Builder { + private @Nullable String id; + private @Nullable String name; + private @Nullable String version; + private int priority = 0; + private int startPriority = 0; + private boolean disabledOnStartup = false; + private @Nullable Class mainClass; + private final @NotNull LinkedList> additionalClasses = new LinkedList<>(); + + /** + * Builds the {@link HiveMQExtension} with the provided values or default values. + * @return the HiveMQ Extension + */ + public @NotNull HiveMQExtension build() { + if (id == null || id.isEmpty()) { + throw new IllegalArgumentException("extension id must not be null or empty"); + } + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException("extension name must not be null or empty"); + } + if (version == null || version.isEmpty()) { + throw new IllegalArgumentException("extension version must not be null or empty"); + } + if (mainClass == null) { + throw new IllegalArgumentException("extension main class must not be null"); + } + return new HiveMQExtension( + id, + name, + version, + priority, + startPriority, + disabledOnStartup, + mainClass, + additionalClasses + ); + } + + /** + * Sets the identifier of the {@link HiveMQExtension}. + * + * @param id the identifier, must not be empty + * @return the {@link Builder} + */ + public @NotNull Builder id(final @NotNull String id) { + this.id = id; + return this; + } + + /** + * Sets the name of the {@link HiveMQExtension}. + * + * @param name the identifier, must not be empty + * @return the {@link Builder} + */ + public @NotNull Builder name(final @NotNull String name) { + this.name = name; + return this; + } + + /** + * Sets the version of the {@link HiveMQExtension}. + * + * @param version the version, must not be empty + * @return the {@link Builder} + */ + public @NotNull Builder version(final @NotNull String version) { + this.version = version; + return this; + } + + /** + * Sets the priority of the {@link HiveMQExtension}. + * + * @param priority the priority + * @return the {@link Builder} + */ + public @NotNull Builder priority(final int priority) { + this.priority = priority; + return this; + } + + /** + * Sets the start-priority of the {@link HiveMQExtension}. + * + * @param startPriority the start-priority + * @return the {@link Builder} + */ + public @NotNull Builder startPriority(final int startPriority) { + this.startPriority = startPriority; + return this; + } + + /** + * Flag, that indicates whether the {@link HiveMQExtension} should be disabled when HiveMQ starts. + * Disabling on startup is achieved by placing a DISABLED file in the {@link HiveMQExtension}'s directory before coping it to the container. + * + * @param disabledOnStartup if the {@link HiveMQExtension} should be disabled when HiveMQ starts + * @return the {@link Builder} + */ + public @NotNull Builder disabledOnStartup(final boolean disabledOnStartup) { + this.disabledOnStartup = disabledOnStartup; + return this; + } + + /** + * The main class of the {@link HiveMQExtension}. + * This class MUST implement com.hivemq.extension.sdk.api.ExtensionMain. + * + * @param mainClass the main class + * @return the {@link Builder} + * @throws IllegalArgumentException if the provides class does not implement com.hivemq.extension.sdk.api.ExtensionMain} + * @throws IllegalStateException if com.hivemq.extension.sdk.api.ExtensionMain is not found in the classpath + */ + public @NotNull Builder mainClass(final @NotNull Class mainClass) { + try { + final Class extensionMain = Class.forName(EXTENSION_MAIN_CLASS_NAME); + if (!extensionMain.isAssignableFrom(mainClass)) { + throw new IllegalArgumentException("The provided class does not implement '" + EXTENSION_MAIN_CLASS_NAME + "'"); + } + this.mainClass = mainClass; + return this; + } catch (final ClassNotFoundException e) { + throw new IllegalStateException("The class '" + EXTENSION_MAIN_CLASS_NAME + "' was not found in the classpath."); + } + } + + /** + * Adds an additional class to the .jar file of the {@link HiveMQExtension}. + * + * @param clazz the additional class + * @return the {@link Builder} + */ + public @NotNull Builder addAdditionalClass(final @NotNull Class clazz) { + this.additionalClasses.add(clazz); + return this; + } + } +} diff --git a/modules/hivemq/src/main/java/org/testcontainers/hivemq/PathUtil.java b/modules/hivemq/src/main/java/org/testcontainers/hivemq/PathUtil.java new file mode 100755 index 00000000000..245052f46b8 --- /dev/null +++ b/modules/hivemq/src/main/java/org/testcontainers/hivemq/PathUtil.java @@ -0,0 +1,26 @@ +package org.testcontainers.hivemq; + +import org.jetbrains.annotations.NotNull; + +class PathUtil { + + static @NotNull String prepareInnerPath(@NotNull String innerPath) { + if ("/".equals(innerPath) || innerPath.isEmpty()) { + return "/"; + } + if (!innerPath.startsWith("/")) { + innerPath = "/" + innerPath; + } + if (!innerPath.endsWith("/")) { + innerPath += "/"; + } + return innerPath; + } + + static @NotNull String prepareAppendPath(@NotNull String appendPath) { + if (!appendPath.startsWith("/")) { + appendPath = "/" + appendPath; + } + return appendPath; + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithControlCenterIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithControlCenterIT.java new file mode 100644 index 00000000000..f88d60b22ed --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithControlCenterIT.java @@ -0,0 +1,34 @@ +package org.testcontainers.hivemq; + +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.testcontainers.utility.DockerImageName; + +import java.util.concurrent.TimeUnit; + +public class ContainerWithControlCenterIT { + + public static final int CONTROL_CENTER_PORT = 8080; + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + public void test() throws Exception { + + try (final HiveMQContainer hivemq = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withControlCenter()) { + + hivemq.start(); + + try (final CloseableHttpClient httpClient = HttpClientBuilder.create().build()) { + final HttpUriRequest request = new HttpGet("http://" + hivemq.getHost() + ":" + hivemq.getMappedPort(CONTROL_CENTER_PORT)); + httpClient.execute(request); + } + } + + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java new file mode 100755 index 00000000000..17f09d86adf --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java @@ -0,0 +1,43 @@ +package org.testcontainers.hivemq; + +import com.hivemq.client.mqtt.datatypes.MqttQos; +import com.hivemq.client.mqtt.exceptions.MqttSessionExpiredException; +import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; +import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class ContainerWithCustomConfigIT { + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test() throws Exception { + + try (final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withHiveMQConfig(MountableFile.forClasspathResource("/config.xml"))) { + + hivemq.start(); + + final Mqtt5BlockingClient publisher = Mqtt5Client.builder() + .identifier("publisher") + .serverPort(hivemq.getMqttPort()) + .buildBlocking(); + + publisher.connect(); + + assertThrows(MqttSessionExpiredException.class, () -> { + // this should fail since only QoS 0 is allowed by the configuration + publisher.publishWith() + .topic("test/topic") + .qos(MqttQos.EXACTLY_ONCE) + .send(); + }); + } + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionFromDirectoryIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionFromDirectoryIT.java new file mode 100755 index 00000000000..7672c70925d --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionFromDirectoryIT.java @@ -0,0 +1,43 @@ +package org.testcontainers.hivemq; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.slf4j.event.Level; +import org.testcontainers.hivemq.util.TestPublishModifiedUtil; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.util.concurrent.TimeUnit; + +public class ContainerWithExtensionFromDirectoryIT { + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test() throws Exception { + try ( + final HiveMQContainer hivemq = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withExtension(MountableFile.forClasspathResource("/modifier-extension")) + .waitForExtension("Modifier Extension") + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .withLogLevel(Level.DEBUG)) { + + hivemq.start(); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + } + } + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test_wrongDirectoryName() throws Exception { + try (final HiveMQContainer hivemq = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withExtension(MountableFile.forClasspathResource("/modifier-extension-wrong-name")) + .waitForExtension("Modifier Extension") + .withLogLevel(Level.DEBUG)) { + + hivemq.start(); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + } + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionIT.java new file mode 100755 index 00000000000..3ad7b26148a --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionIT.java @@ -0,0 +1,41 @@ +package org.testcontainers.hivemq; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.testcontainers.hivemq.util.MyExtension; +import org.testcontainers.hivemq.util.TestPublishModifiedUtil; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.util.concurrent.TimeUnit; + +public class ContainerWithExtensionIT { + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test() throws Exception { + final HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + .id("extension-1") + .name("my-extension") + .version("1.0") + .mainClass(MyExtension.class).build(); + + try (final HiveMQContainer hivemq = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .waitForExtension(hiveMQExtension) + .withExtension(hiveMQExtension)) { + + hivemq.start(); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + hivemq.stop(); + + hivemq.start(); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + hivemq.stop(); + + hivemq.start(); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + } + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionSubclassIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionSubclassIT.java new file mode 100755 index 00000000000..33880a800c9 --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionSubclassIT.java @@ -0,0 +1,35 @@ +package org.testcontainers.hivemq; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.slf4j.event.Level; +import org.testcontainers.hivemq.util.MyExtensionWithSubclasses; +import org.testcontainers.hivemq.util.TestPublishModifiedUtil; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.util.concurrent.TimeUnit; + +public class ContainerWithExtensionSubclassIT { + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test() throws Exception { + final HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + .id("extension-1") + .name("my-extension") + .version("1.0") + .mainClass(MyExtensionWithSubclasses.class).build(); + + try (final HiveMQContainer hivemq = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .waitForExtension(hiveMQExtension) + .withExtension(hiveMQExtension) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .withLogLevel(Level.DEBUG)) { + + hivemq.start(); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + } + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInExtensionHomeIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInExtensionHomeIT.java new file mode 100755 index 00000000000..1d2b1705eae --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInExtensionHomeIT.java @@ -0,0 +1,77 @@ +package org.testcontainers.hivemq; + +import com.hivemq.extension.sdk.api.ExtensionMain; +import com.hivemq.extension.sdk.api.interceptor.publish.PublishInboundInterceptor; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartOutput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopOutput; +import com.hivemq.extension.sdk.api.services.Services; +import com.hivemq.extension.sdk.api.services.intializer.ClientInitializer; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.testcontainers.hivemq.util.TestPublishModifiedUtil; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.io.File; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; + +public class ContainerWithFileInExtensionHomeIT { + + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test() throws Exception { + final HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + .id("extension-1") + .name("my-extension") + .version("1.0") + .mainClass(FileCheckerExtension.class).build(); + + try (final HiveMQContainer hivemq = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .withExtension(hiveMQExtension) + .waitForExtension(hiveMQExtension) + .withFileInExtensionHomeFolder( + MountableFile.forClasspathResource("/additionalFile.txt"), + "extension-1", + "/additionalFiles/my-file.txt")) { + + hivemq.start(); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + } + } + + public static class FileCheckerExtension implements ExtensionMain { + + @Override + public void extensionStart(final @NotNull ExtensionStartInput extensionStartInput, final @NotNull ExtensionStartOutput extensionStartOutput) { + + final PublishInboundInterceptor publishInboundInterceptor = (publishInboundInput, publishInboundOutput) -> { + + final File extensionHomeFolder = extensionStartInput.getExtensionInformation().getExtensionHomeFolder(); + + final File additionalFile = new File(extensionHomeFolder, "additionalFiles/my-file.txt"); + + if (additionalFile.exists()) { + publishInboundOutput.getPublishPacket().setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); + } + }; + + final ClientInitializer clientInitializer = (initializerInput, clientContext) -> clientContext.addPublishInboundInterceptor(publishInboundInterceptor); + + Services.initializerRegistry().setClientInitializer(clientInitializer); + } + + @Override + public void extensionStop(final @NotNull ExtensionStopInput extensionStopInput, final @NotNull ExtensionStopOutput extensionStopOutput) { + + } + } + +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInHomeIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInHomeIT.java new file mode 100755 index 00000000000..542c54789e8 --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInHomeIT.java @@ -0,0 +1,75 @@ +package org.testcontainers.hivemq; + +import com.hivemq.extension.sdk.api.ExtensionMain; +import com.hivemq.extension.sdk.api.interceptor.publish.PublishInboundInterceptor; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartOutput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopOutput; +import com.hivemq.extension.sdk.api.services.Services; +import com.hivemq.extension.sdk.api.services.intializer.ClientInitializer; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.testcontainers.hivemq.util.TestPublishModifiedUtil; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.io.File; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; + +public class ContainerWithFileInHomeIT { + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test() throws Exception { + final HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + .id("extension-1") + .name("my-extension") + .version("1.0") + .mainClass(FileCheckerExtension.class).build(); + + try (final HiveMQContainer hivemq = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .withExtension(hiveMQExtension) + .waitForExtension(hiveMQExtension) + .withFileInHomeFolder(MountableFile.forClasspathResource("/additionalFile.txt"), + "/additionalFiles/my-file.txt")) { + + + hivemq.start(); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + } + } + + public static class FileCheckerExtension implements ExtensionMain { + + @Override + public void extensionStart(@NotNull ExtensionStartInput extensionStartInput, @NotNull ExtensionStartOutput extensionStartOutput) { + + final PublishInboundInterceptor publishInboundInterceptor = (publishInboundInput, publishInboundOutput) -> { + + final File homeFolder = extensionStartInput.getServerInformation().getHomeFolder(); + + final File additionalFile = new File(homeFolder, "additionalFiles/my-file.txt"); + + if (additionalFile.exists()) { + publishInboundOutput.getPublishPacket().setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); + } + }; + + final ClientInitializer clientInitializer = (initializerInput, clientContext) -> clientContext.addPublishInboundInterceptor(publishInboundInterceptor); + + Services.initializerRegistry().setClientInitializer(clientInitializer); + } + + @Override + public void extensionStop(@NotNull ExtensionStopInput extensionStopInput, @NotNull ExtensionStopOutput extensionStopOutput) { + + } + } + +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithLicenseIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithLicenseIT.java new file mode 100755 index 00000000000..4fc27adf8c2 --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithLicenseIT.java @@ -0,0 +1,78 @@ +package org.testcontainers.hivemq; + +import com.hivemq.extension.sdk.api.ExtensionMain; +import com.hivemq.extension.sdk.api.interceptor.publish.PublishInboundInterceptor; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartOutput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopOutput; +import com.hivemq.extension.sdk.api.services.Services; +import com.hivemq.extension.sdk.api.services.intializer.ClientInitializer; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.testcontainers.hivemq.util.TestPublishModifiedUtil; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.io.File; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; + +public class ContainerWithLicenseIT { + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test() throws Exception { + final HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + .id("extension-1") + .name("my-extension") + .version("1.0") + .mainClass(LicenceCheckerExtension.class).build(); + + try (final HiveMQContainer hivemq = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .withExtension(hiveMQExtension) + .waitForExtension(hiveMQExtension) + .withLicense(MountableFile.forClasspathResource("/myLicense.lic")) + .withLicense(MountableFile.forClasspathResource("/myExtensionLicense.elic"))) { + + + hivemq.start(); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + } + } + + @SuppressWarnings("CodeBlock2Expr") + public static class LicenceCheckerExtension implements ExtensionMain { + + @Override + public void extensionStart(@NotNull ExtensionStartInput extensionStartInput, @NotNull ExtensionStartOutput extensionStartOutput) { + + final PublishInboundInterceptor publishInboundInterceptor = (publishInboundInput, publishInboundOutput) -> { + + final File homeFolder = extensionStartInput.getServerInformation().getHomeFolder(); + final File myLicence = new File(homeFolder, "license/myLicense.lic"); + final File myExtensionLicence = new File(homeFolder, "license/myExtensionLicense.elic"); + + if (myLicence.exists() && myExtensionLicence.exists()) { + publishInboundOutput.getPublishPacket().setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); + } + }; + + final ClientInitializer clientInitializer = (initializerInput, clientContext) -> { + clientContext.addPublishInboundInterceptor(publishInboundInterceptor); + }; + + Services.initializerRegistry().setClientInitializer(clientInitializer); + } + + @Override + public void extensionStop(@NotNull ExtensionStopInput extensionStopInput, @NotNull ExtensionStopOutput extensionStopOutput) { + + } + } + +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java new file mode 100644 index 00000000000..37b37dc0d59 --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java @@ -0,0 +1,135 @@ +package org.testcontainers.hivemq; + +import com.hivemq.client.mqtt.MqttClient; +import com.hivemq.client.mqtt.MqttGlobalPublishFilter; +import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; +import com.hivemq.client.mqtt.mqtt5.message.publish.Mqtt5Publish; +import com.hivemq.extension.sdk.api.ExtensionMain; +import com.hivemq.extension.sdk.api.auth.SimpleAuthenticator; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartOutput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopOutput; +import com.hivemq.extension.sdk.api.services.Services; +import com.hivemq.extension.sdk.api.services.builder.Builders; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.testcontainers.utility.DockerImageName; + +import java.io.File; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ContainerWithoutPlatformExtensionsIT { + + private final @NotNull HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + .name("MyExtension") + .id("my-extension") + .version("1.0.0") + .mainClass(CheckerExtension.class).build(); + + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + public void removeAllPlatformExtensions() throws InterruptedException { + + try (final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withExtension(hiveMQExtension) + .waitForExtension(hiveMQExtension) + .withoutPrepackagedExtensions()) { + + hivemq.start(); + + final Mqtt5BlockingClient client = MqttClient.builder() + .serverPort(hivemq.getMqttPort()) + .useMqttVersion5() + .buildBlocking(); + + client.connect(); + final Mqtt5BlockingClient.Mqtt5Publishes publishes = client.publishes(MqttGlobalPublishFilter.ALL); + client.subscribeWith().topicFilter("extensions").send(); + + final Mqtt5Publish receive = publishes.receive(); + assertTrue(receive.getPayload().isPresent()); + final String extensionInfo = new String(receive.getPayloadAsBytes()); + + assertFalse(extensionInfo.contains("hivemq-allow-all-extension")); + assertFalse(extensionInfo.contains("hivemq-kafka-extension")); + assertFalse(extensionInfo.contains("hivemq-bridge-extension")); + assertFalse(extensionInfo.contains("hivemq-enterprise-security-extension")); + + hivemq.start(); + } + } + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + public void removeKafkaExtension() throws InterruptedException { + + try (final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withExtension(hiveMQExtension) + .waitForExtension(hiveMQExtension) + .withoutPrepackagedExtensions("hivemq-kafka-extension")) { + + hivemq.start(); + + final Mqtt5BlockingClient client = MqttClient.builder() + .serverPort(hivemq.getMqttPort()) + .useMqttVersion5() + .buildBlocking(); + + client.connect(); + final Mqtt5BlockingClient.Mqtt5Publishes publishes = client.publishes(MqttGlobalPublishFilter.ALL); + client.subscribeWith().topicFilter("extensions").send(); + + final Mqtt5Publish receive = publishes.receive(); + assertTrue(receive.getPayload().isPresent()); + final String extensionInfo = new String(receive.getPayloadAsBytes()); + + assertTrue(extensionInfo.contains("hivemq-allow-all-extension")); + assertFalse(extensionInfo.contains("hivemq-kafka-extension")); + assertTrue(extensionInfo.contains("hivemq-bridge-extension")); + assertTrue(extensionInfo.contains("hivemq-enterprise-security-extension")); + } + } + + public static class CheckerExtension implements ExtensionMain { + + @Override + public void extensionStart( + final @NotNull ExtensionStartInput extensionStartInput, + final @NotNull ExtensionStartOutput extensionStartOutput) { + + final String extensionFolders = Arrays.stream(extensionStartInput.getServerInformation().getExtensionsFolder().listFiles()) + .filter(File::isDirectory) + .map(File::getName) + .collect(Collectors.joining("\n")); + + final byte[] bytes = extensionFolders.getBytes(StandardCharsets.UTF_8); + Services.publishService().publish(Builders.publish() + .topic("extensions") + .retain(true) + .payload(ByteBuffer.wrap(bytes)) + .build()); + + Services.securityRegistry().setAuthenticatorProvider(authenticatorProviderInput -> + (SimpleAuthenticator) (simpleAuthInput, simpleAuthOutput) -> + simpleAuthOutput.authenticateSuccessfully()); + + } + + @Override + public void extensionStop( + final @NotNull ExtensionStopInput extensionStopInput, + final @NotNull ExtensionStopOutput extensionStopOutput) { + + } + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java new file mode 100755 index 00000000000..85089374624 --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java @@ -0,0 +1,92 @@ +package org.testcontainers.hivemq; + +import com.hivemq.extension.sdk.api.ExtensionMain; +import com.hivemq.extension.sdk.api.interceptor.publish.PublishInboundInterceptor; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartOutput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopOutput; +import com.hivemq.extension.sdk.api.services.Services; +import com.hivemq.extension.sdk.api.services.intializer.ClientInitializer; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.testcontainers.hivemq.util.TestPublishModifiedUtil; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class CreateFileInCopiedDirectoryIT { + + private @NotNull MountableFile createDirectory() throws IOException { + final File directory = new File(Files.createTempDirectory("").toFile(), "directory"); + assertTrue(directory.mkdir()); + final File subdirectory = new File(directory, "sub-directory"); + assertTrue(subdirectory.mkdir()); + return MountableFile.forHostPath(directory.getPath()); + } + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test() throws Exception { + final HiveMQExtension extension = HiveMQExtension.builder() + .id("extension-1") + .name("my-extension") + .version("1.0") + .mainClass(FileCreatorExtension.class).build(); + + try (final HiveMQContainer hivemq = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .withExtension(extension) + .waitForExtension(extension) + .withFileInHomeFolder(createDirectory(), "directory")) { + + hivemq.start(); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + } + } + + public static class FileCreatorExtension implements ExtensionMain { + + @Override + public void extensionStart(@NotNull ExtensionStartInput extensionStartInput, @NotNull ExtensionStartOutput extensionStartOutput) { + + final PublishInboundInterceptor publishInboundInterceptor = (publishInboundInput, publishInboundOutput) -> { + + final File homeFolder = extensionStartInput.getServerInformation().getHomeFolder(); + + final File dir = new File(homeFolder, "directory"); + final File dirFile = new File(dir, "file.txt"); + final File subDir = new File(dir, "sub-directory"); + final File subDirFile = new File(subDir, "file.txt"); + + try { + if (dirFile.createNewFile() && subDirFile.createNewFile()) { + publishInboundOutput.getPublishPacket().setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); + } + } catch (IOException e) { + e.printStackTrace(); + } + }; + + final ClientInitializer clientInitializer = (initializerInput, clientContext) -> clientContext.addPublishInboundInterceptor(publishInboundInterceptor); + + Services.initializerRegistry().setClientInitializer(clientInitializer); + } + + @Override + public void extensionStop(@NotNull ExtensionStopInput extensionStopInput, @NotNull ExtensionStopOutput extensionStopOutput) { + + } + } + +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInExtensionDirectoryIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInExtensionDirectoryIT.java new file mode 100755 index 00000000000..a7756c69f1d --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInExtensionDirectoryIT.java @@ -0,0 +1,76 @@ +package org.testcontainers.hivemq; + +import com.hivemq.extension.sdk.api.ExtensionMain; +import com.hivemq.extension.sdk.api.interceptor.publish.PublishInboundInterceptor; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartOutput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopOutput; +import com.hivemq.extension.sdk.api.services.Services; +import com.hivemq.extension.sdk.api.services.intializer.ClientInitializer; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.testcontainers.hivemq.util.TestPublishModifiedUtil; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; + +public class CreateFileInExtensionDirectoryIT { + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test() throws Exception { + final HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + .id("extension-1") + .name("my-extension") + .version("1.0") + .mainClass(FileCreatorExtension.class).build(); + + try (final HiveMQContainer hivemq = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .waitForExtension(hiveMQExtension) + .withExtension(hiveMQExtension)) { + + hivemq.start(); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + } + } + + public static class FileCreatorExtension implements ExtensionMain { + + @Override + public void extensionStart(@NotNull ExtensionStartInput extensionStartInput, @NotNull ExtensionStartOutput extensionStartOutput) { + + final PublishInboundInterceptor publishInboundInterceptor = (publishInboundInput, publishInboundOutput) -> { + + final File extensionHomeFolder = extensionStartInput.getExtensionInformation().getExtensionHomeFolder(); + + final File file = new File(extensionHomeFolder, "myfile.txt"); + try { + if (file.createNewFile()) { + publishInboundOutput.getPublishPacket().setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); + } + } catch (IOException e) { + e.printStackTrace(); + } + }; + + final ClientInitializer clientInitializer = (initializerInput, clientContext) -> clientContext.addPublishInboundInterceptor(publishInboundInterceptor); + + Services.initializerRegistry().setClientInitializer(clientInitializer); + } + + @Override + public void extensionStop(@NotNull ExtensionStopInput extensionStopInput, @NotNull ExtensionStopOutput extensionStopOutput) { + + } + } + +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java new file mode 100644 index 00000000000..2248681a769 --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java @@ -0,0 +1,37 @@ +package org.testcontainers.hivemq; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.slf4j.event.Level; +import org.testcontainers.hivemq.util.TestPublishModifiedUtil; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class DisableEnableExtensionFromDirectoryIT { + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test() throws Exception { + try (final HiveMQContainer hivemq = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withExtension(MountableFile.forClasspathResource("/modifier-extension")) + .waitForExtension("Modifier Extension") + .withLogLevel(Level.DEBUG)) { + + hivemq.start(); + + + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + hivemq.disableExtension("Modifier Extension", "modifier-extension"); + assertThrows(ExecutionException.class, () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort())); + hivemq.enableExtension("Modifier Extension", "modifier-extension"); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + } + } + +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java new file mode 100644 index 00000000000..7cc943c5527 --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java @@ -0,0 +1,46 @@ +package org.testcontainers.hivemq; + +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.slf4j.event.Level; +import org.testcontainers.hivemq.util.MyExtension; +import org.testcontainers.hivemq.util.TestPublishModifiedUtil; +import org.testcontainers.utility.DockerImageName; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class DisableEnableExtensionIT { + + private final @NotNull HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + .id("extension-1") + .name("my-extension") + .version("1.0") + .disabledOnStartup(true) + .mainClass(MyExtension.class).build(); + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test() throws Exception { + try (final HiveMQContainer hivemq = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withExtension(hiveMQExtension) + .withLogLevel(Level.DEBUG)) { + + + hivemq.start(); + + assertThrows(ExecutionException.class, () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort())); + hivemq.enableExtension(hiveMQExtension); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + hivemq.disableExtension(hiveMQExtension); + assertThrows(ExecutionException.class, () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort())); + hivemq.enableExtension(hiveMQExtension); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + } + } + +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQExtensionTest.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQExtensionTest.java new file mode 100644 index 00000000000..d614a59bd8c --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQExtensionTest.java @@ -0,0 +1,13 @@ +package org.testcontainers.hivemq; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class HiveMQExtensionTest { + + @Test + void builder_classDoesNotImplementExtensionMain_exception() { + assertThrows(IllegalArgumentException.class, () -> HiveMQExtension.builder().mainClass(HiveMQExtensionTest.class)); + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQTestContainerCore.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQTestContainerCore.java new file mode 100644 index 00000000000..40820e33ded --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQTestContainerCore.java @@ -0,0 +1,82 @@ +package org.testcontainers.hivemq; + +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.testcontainers.containers.ContainerLaunchException; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.io.File; +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class HiveMQTestContainerCore { + + final @NotNull HiveMQContainer container = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")); + + @TempDir + File tempDir; + + @Test + void withExtension_fileDoesNotExist_Exception() { + final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); + assertThrows(ContainerLaunchException.class, () -> container.withExtension(mountableFile)); + } + + @Test + void withExtension_fileNoDirectory_Exception() throws IOException { + final File extension = new File(tempDir, "extension"); + assertTrue(extension.createNewFile()); + final MountableFile mountableFile = MountableFile.forHostPath(extension.getAbsolutePath()); + assertThrows(ContainerLaunchException.class, () -> container.withExtension(mountableFile)); + } + + @Test + void withLicense_fileDoesNotExist_Exception() { + final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); + assertThrows(ContainerLaunchException.class, () -> container.withLicense(mountableFile)); + } + + @Test + void withExtension_fileEndingWrong_Exception() throws IOException { + final File extension = new File(tempDir, "extension.wrong"); + assertTrue(extension.createNewFile()); + final MountableFile mountableFile = MountableFile.forHostPath(extension.getAbsolutePath()); + assertThrows(ContainerLaunchException.class, () -> container.withLicense(mountableFile)); + } + + @Test + void withHiveMQConfig_fileDoesNotExist_Exception() { + final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); + assertThrows(ContainerLaunchException.class, () -> container.withHiveMQConfig(mountableFile)); + } + + @Test + void withFileInHomeFolder_fileDoesNotExist_Exception() { + final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); + assertThrows(ContainerLaunchException.class, () -> container.withFileInHomeFolder(mountableFile, "some/path")); + } + + @Test + void withFileInHomeFolder_pathEmpty_Exception() { + final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); + assertThrows(ContainerLaunchException.class, () -> container.withFileInHomeFolder(mountableFile, "")); + } + + @Test + void withFileInExtensionHomeFolder_withPath_fileDoesNotExist_Exception() { + final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); + assertThrows(ContainerLaunchException.class, () -> container.withFileInExtensionHomeFolder(mountableFile, "my-extension", "some/path")); + } + + @Test + void withFileInExtensionHomeFolder_fileDoesNotExist_Exception() { + final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); + assertThrows(ContainerLaunchException.class, () -> { + final HiveMQContainer hiveMQContainer = container.withFileInExtensionHomeFolder(mountableFile, "my-extension"); + }); + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/PathUtilTest.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/PathUtilTest.java new file mode 100755 index 00000000000..f1e060e68d9 --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/PathUtilTest.java @@ -0,0 +1,43 @@ +package org.testcontainers.hivemq; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class PathUtilTest { + + @Test + void prepareInnerPath_emptyString() { + assertEquals("/", PathUtil.prepareInnerPath("")); + } + + @Test + void prepareInnerPath_onlyDelimiter() { + assertEquals("/", PathUtil.prepareInnerPath("/")); + } + + @Test + void prepareInnerPath_noDelimiter() { + assertEquals("/path/", PathUtil.prepareInnerPath("path")); + } + + @Test + void prepareInnerPath_delimiterAtEnd() { + assertEquals("/path/", PathUtil.prepareInnerPath("path/")); + } + + @Test + void prepareInnerPath_delimiterAtBeginning() { + assertEquals("/path/", PathUtil.prepareInnerPath("/path")); + } + + @Test + void prepareAppendPath_delimiterAtBeginning_noChange() { + assertEquals("/path", PathUtil.prepareAppendPath("/path")); + } + + @Test + void prepareAppendPath_delimiterAtBeginning_delimiterAdded() { + assertEquals("/path", PathUtil.prepareAppendPath("path")); + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java new file mode 100644 index 00000000000..e9d9463653e --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java @@ -0,0 +1,63 @@ +package org.testcontainers.hivemq.docs; + +import org.junit.jupiter.api.Test; +import org.testcontainers.hivemq.HiveMQContainer; +import org.testcontainers.hivemq.HiveMQExtension; +import org.testcontainers.hivemq.util.MyExtension; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +@Testcontainers +public class DemoDisableExtensionsIT { + + // noExtensions { + @Container + final HiveMQContainer hivemqNoExtensions = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withoutPrepackagedExtensions(); + // } + + // noKafkaExtension { + @Container + final HiveMQContainer hivemqNoKafkaExtension = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withoutPrepackagedExtensions("hivemq-kafka-extension"); + // } + + // startDisabled { + private final HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + .id("extension-1") + .name("my-extension") + .version("1.0") + .disabledOnStartup(true) + .mainClass(MyExtension.class).build(); + + @Container + final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withExtension(hiveMQExtension); + // } + + + // startFromFilesystem { + @Container + final HiveMQContainer hivemqExtensionFromFilesystem = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withExtension(MountableFile.forHostPath("src/test/resources/modifier-extension")); + // } + + + // runtimeEnable { + @Test + void test_disable_enable_extension() throws Exception { + hivemq.enableExtension(hiveMQExtension); + hivemq.disableExtension(hiveMQExtension); + } + // } + + // runtimeEnableFilesystem { + @Test + void test_disable_enable_extension_from_filesystem() throws Exception { + hivemqExtensionFromFilesystem.disableExtension("Modifier Extension", "modifier-extension"); + hivemqExtensionFromFilesystem.enableExtension("Modifier Extension", "modifier-extension"); + } + // } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoExtensionTestsIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoExtensionTestsIT.java new file mode 100644 index 00000000000..2627dca2028 --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoExtensionTestsIT.java @@ -0,0 +1,57 @@ +package org.testcontainers.hivemq.docs; + +import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; +import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.testcontainers.hivemq.HiveMQContainer; +import org.testcontainers.hivemq.HiveMQExtension; +import org.testcontainers.hivemq.util.MyExtensionWithSubclasses; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.util.concurrent.TimeUnit; + +@Testcontainers +public class DemoExtensionTestsIT { + + // waitStrategy { + @Container + final HiveMQContainer hivemqWithWaitStrategy = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withExtension(MountableFile.forClasspathResource("/modifier-extension")) + .waitForExtension("Modifier Extension"); + // } + + // extensionClasspath { + final HiveMQExtension hiveMQEClasspathxtension = HiveMQExtension.builder() + .id("extension-1") + .name("my-extension") + .version("1.0") + .mainClass(MyExtensionWithSubclasses.class).build(); + + @Container + final HiveMQContainer hivemqWithClasspathExtension = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .waitForExtension(hiveMQEClasspathxtension) + .withExtension(hiveMQEClasspathxtension) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")); + // } + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + public void test() throws Exception { + + // mqtt5client { + final Mqtt5BlockingClient client = Mqtt5Client.builder() + .serverPort(hivemqWithClasspathExtension.getMqttPort()) + .buildBlocking(); + + client.connect(); + client.disconnect(); + // } + + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java new file mode 100644 index 00000000000..46c24e8e51d --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java @@ -0,0 +1,62 @@ +package org.testcontainers.hivemq.docs; + +import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; +import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.testcontainers.hivemq.HiveMQContainer; +import org.testcontainers.hivemq.HiveMQExtension; +import org.testcontainers.hivemq.util.MyExtension; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.util.concurrent.TimeUnit; + +@Testcontainers +public class DemoFilesIT { + + // hivemqHome { + final HiveMQContainer hivemqFileInHome = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withFileInHomeFolder( + MountableFile.forHostPath("src/test/resources/additionalFile.txt"), + "/path/in/home/folder"); + // } + + // extensionHome { + @Container + final HiveMQContainer hivemqFileInExtensionHome = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withExtension(HiveMQExtension.builder() + .id("extension-1") + .name("my-extension") + .version("1.0") + .mainClass(MyExtension.class).build()) + .withFileInExtensionHomeFolder( + MountableFile.forHostPath("src/test/resources/additionalFile.txt"), + "extension-1", + "/path/in/extension/home"); + // } + + // withLicenses { + @Container + final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withLicense(MountableFile.forHostPath("src/test/resources/myLicense.lic")) + .withLicense(MountableFile.forHostPath("src/test/resources/myExtensionLicense.elic")); + // } + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + public void test() throws Exception { + + // mqtt5client { + final Mqtt5BlockingClient client = Mqtt5Client.builder() + .serverPort(hivemq.getMqttPort()) + .buildBlocking(); + + client.connect(); + client.disconnect(); + // } + + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java new file mode 100644 index 00000000000..60683c8d612 --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java @@ -0,0 +1,61 @@ +package org.testcontainers.hivemq.docs; + +import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; +import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.slf4j.event.Level; +import org.testcontainers.hivemq.HiveMQContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.util.concurrent.TimeUnit; + +@Testcontainers +public class DemoHiveMQContainerIT { + + // ceVersion { + @Container + final HiveMQContainer hivemqCe = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withLogLevel(Level.DEBUG); + // } + + // eeVersion { + @Container + final HiveMQContainer hivemqEe = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withLogLevel(Level.DEBUG); + // } + + // eeVersionWithControlCenter { + @Container + final HiveMQContainer hivemqEeWithControLCenter = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withLogLevel(Level.DEBUG) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .withControlCenter(); + // } + + // specificVersion { + @Container + final HiveMQContainer hivemqSpecificVersion = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce:2021.3")); + // } + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + public void test() throws Exception { + + // mqtt5client { + final Mqtt5BlockingClient client = Mqtt5Client.builder() + .serverPort(hivemqCe.getMqttPort()) + .buildBlocking(); + + client.connect(); + client.disconnect(); + // } + + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtension.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtension.java new file mode 100755 index 00000000000..e707859be77 --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtension.java @@ -0,0 +1,37 @@ +package org.testcontainers.hivemq.util; + +import com.hivemq.extension.sdk.api.ExtensionMain; +import org.jetbrains.annotations.NotNull; +import com.hivemq.extension.sdk.api.interceptor.publish.PublishInboundInterceptor; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartOutput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopOutput; +import com.hivemq.extension.sdk.api.services.Services; +import com.hivemq.extension.sdk.api.services.intializer.ClientInitializer; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +@SuppressWarnings("CodeBlock2Expr") +public class MyExtension implements ExtensionMain { + + @Override + public void extensionStart(final @NotNull ExtensionStartInput extensionStartInput, final @NotNull ExtensionStartOutput extensionStartOutput) { + + final PublishInboundInterceptor publishInboundInterceptor = (publishInboundInput, publishInboundOutput) -> { + publishInboundOutput.getPublishPacket().setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); + }; + + final ClientInitializer clientInitializer = (initializerInput, clientContext) -> { + clientContext.addPublishInboundInterceptor(publishInboundInterceptor); + }; + + Services.initializerRegistry().setClientInitializer(clientInitializer); + } + + @Override + public void extensionStop(final @NotNull ExtensionStopInput extensionStopInput, final @NotNull ExtensionStopOutput extensionStopOutput) { + + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtensionWithSubclasses.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtensionWithSubclasses.java new file mode 100755 index 00000000000..abc24585358 --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtensionWithSubclasses.java @@ -0,0 +1,26 @@ +package org.testcontainers.hivemq.util; + +import com.hivemq.extension.sdk.api.ExtensionMain; +import org.jetbrains.annotations.NotNull; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartOutput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopOutput; +import com.hivemq.extension.sdk.api.services.Services; +import com.hivemq.extension.sdk.api.services.intializer.ClientInitializer; + +public class MyExtensionWithSubclasses implements ExtensionMain { + + @Override + public void extensionStart(final @NotNull ExtensionStartInput extensionStartInput, final @NotNull ExtensionStartOutput extensionStartOutput) { + + final ClientInitializer clientInitializer = (initializerInput, clientContext) -> clientContext.addPublishInboundInterceptor(new PublishModifier()); + + Services.initializerRegistry().setClientInitializer(clientInitializer); + } + + @Override + public void extensionStop(final @NotNull ExtensionStopInput extensionStopInput, final @NotNull ExtensionStopOutput extensionStopOutput) { + + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/PublishModifier.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/PublishModifier.java new file mode 100755 index 00000000000..d33f29a9c10 --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/PublishModifier.java @@ -0,0 +1,16 @@ +package org.testcontainers.hivemq.util; + +import org.jetbrains.annotations.NotNull; +import com.hivemq.extension.sdk.api.interceptor.publish.PublishInboundInterceptor; +import com.hivemq.extension.sdk.api.interceptor.publish.parameter.PublishInboundInput; +import com.hivemq.extension.sdk.api.interceptor.publish.parameter.PublishInboundOutput; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +public class PublishModifier implements PublishInboundInterceptor { + @Override + public void onInboundPublish(final @NotNull PublishInboundInput publishInboundInput, final @NotNull PublishInboundOutput publishInboundOutput) { + publishInboundOutput.getPublishPacket().setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/TestPublishModifiedUtil.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/TestPublishModifiedUtil.java new file mode 100755 index 00000000000..b2712ef5a1d --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/TestPublishModifiedUtil.java @@ -0,0 +1,48 @@ +package org.testcontainers.hivemq.util; + +import com.hivemq.client.mqtt.MqttGlobalPublishFilter; +import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; +import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public class TestPublishModifiedUtil { + + public static void testPublishModified(final int mqttPort) throws InterruptedException, ExecutionException { + final CompletableFuture publishReceived = new CompletableFuture<>(); + + final Mqtt5BlockingClient publisher = Mqtt5Client.builder() + .serverPort(mqttPort) + .identifier("publisher") + .buildBlocking(); + publisher.connect(); + + final Mqtt5BlockingClient subscriber = Mqtt5Client.builder() + .serverPort(mqttPort) + .identifier("subscriber") + .buildBlocking(); + subscriber.connect(); + subscriber.subscribeWith().topicFilter("test/topic").send(); + subscriber.toAsync().publishes(MqttGlobalPublishFilter.ALL, publish -> { + if (Arrays.equals(publish.getPayloadAsBytes(), "modified".getBytes(StandardCharsets.UTF_8))) { + publishReceived.complete(null); + } else { + publishReceived.completeExceptionally(new IllegalArgumentException("unexpected payload: " + new String(publish.getPayloadAsBytes()))); + } + }); + + publisher.publishWith().topic("test/topic").payload("unmodified".getBytes(StandardCharsets.UTF_8)).send(); + + try { + publishReceived.get(); + } finally { + publisher.disconnect(); + subscriber.disconnect(); + } + } + + +} diff --git a/modules/hivemq/src/test/resources/additionalFile.txt b/modules/hivemq/src/test/resources/additionalFile.txt new file mode 100755 index 00000000000..e69de29bb2d diff --git a/modules/hivemq/src/test/resources/config.xml b/modules/hivemq/src/test/resources/config.xml new file mode 100755 index 00000000000..128015e2fee --- /dev/null +++ b/modules/hivemq/src/test/resources/config.xml @@ -0,0 +1,15 @@ + + + + + + 1883 + 0.0.0.0 + + + + + 0 + + + diff --git a/modules/hivemq/src/test/resources/inMemoryConfig.xml b/modules/hivemq/src/test/resources/inMemoryConfig.xml new file mode 100644 index 00000000000..33373359243 --- /dev/null +++ b/modules/hivemq/src/test/resources/inMemoryConfig.xml @@ -0,0 +1,6 @@ + + + + in-memory + + diff --git a/modules/hivemq/src/test/resources/logback-test.xml b/modules/hivemq/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..8a33fc398f0 --- /dev/null +++ b/modules/hivemq/src/test/resources/logback-test.xml @@ -0,0 +1,18 @@ + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + + + + + diff --git a/modules/hivemq/src/test/resources/modifier-extension-wrong-name/hivemq-extension.xml b/modules/hivemq/src/test/resources/modifier-extension-wrong-name/hivemq-extension.xml new file mode 100755 index 00000000000..6ca9c899df4 --- /dev/null +++ b/modules/hivemq/src/test/resources/modifier-extension-wrong-name/hivemq-extension.xml @@ -0,0 +1,8 @@ + + + modifier-extension + 1.0-SNAPSHOT + Modifier Extension + HiveMQ GmbH + 1000 + diff --git a/modules/hivemq/src/test/resources/modifier-extension-wrong-name/modifier-extension-1.0-SNAPSHOT.jar b/modules/hivemq/src/test/resources/modifier-extension-wrong-name/modifier-extension-1.0-SNAPSHOT.jar new file mode 100644 index 0000000000000000000000000000000000000000..18a7ea85f53dcf856d5280676386448b43d2d537 GIT binary patch literal 102179 zcmbTd19&A{vp*c$wrx9^*tX4yZQHhOn-kl%J#jL@B$NCzufKEeIp4i^KYKmT?%lnr zt5)gPRjcKtfI(0IARr(Bg1ubj0si9y2>=2hBcd!oDn zCjY0CQ9iHu*JK$1SqV`QC1pAp(R-Q6327->x;a=WTB_;EnMOs1MdsZj2O7!gQ5q>) zK?v}}BGn``>RuAJ_N+)nWGO`@r);Wf6dY)zL}o=)FXRcudz7djlMhj!=OvjPC3_o^ zkIE$oa0%j3bZV2V@<>HJz&t{_w7azb#_c-!{JYgBFaDq7-`s}$LDb*enp?P-SliQ@ zxH*~FI9k}+(79P#nJDVmW-_37pQvr-^I9oBi|1A(ml#@f6sQP8jaBe**#S!FBX1-L zFFbB|zKAcCbz$r{dR?`Tz+J^SgVvU@r0y^0boRhfD5l#hY_4}+20B^~Z1qF+aKwOq z7*J*=?u%ILj_2wLrEvY6WeUEj-2Q@;f6|LAr(iyrhl)wXKq|Oemy`mT-j`cXtliG% z^&RpNWNpV5EIQFD-;^9-p1(3)xGf}04Z=;jp0O9SsKJJ6S~z0XUI{Lm4AT|CJze;4 zn_-`sYi^eAV-&=6x@V-8rQ~JLU1z1;%I;E9#cgSHd0VMTduVK&@Z`jiTOofr?`GCP z#OPYC7t0}~f%m0a9b6HR8U@MJ__|AJQ@)>hmWdtri2c-98++)fx;^abBJub`JntqA zfLoeC(&`zLOo0hoIj8XQz@s(z#I6zYz`99NzuW(at0%T)j;mw~IOrDoEoT1}lK@mK z`ewDv2aIX_Rh|(WSsH;}`p|);7+czXS}8*og_artN}^VLR(>|GqW!8?Epf!F+Mt9xVkm=?(RJV4#?g$v$1xyC6^wZO^;O_)S(Z+4wsso{FfsM|+T*FG!9h zu}~crq|b`eD^PvNPjQ24%Hj@!Xwm|iRdFknR42#Mq0OgXy`DRo{!eQ^v47k6JYDu@|iNiM;oi) zc8QR|3unk#dN{5>8pCE^6{eMODfEGEvyWWCm;rYP#?&ZnKnM073yo#d=(L;2`b^L0 zG=>}vlZbKz1qK+?A~M~Ok9yOS$8v5`et68@GQLys7d$!fd&C1qjLBULWonV#5VPKD zj+2#-sUB?wOH^iC4tgd<@DWZ?TG6G)5rCm@9l(vcce)Jl9 zM`R{z^a!5@-V3WaHT~uV1BID!I}>t7#tT%fHlwzVLUT+}sG%y-?&PLy=ZKc{mN7PR zsGb8RV=Tvsh-$}Vage%i6Do1|Iuln%r%6$Ri(I}xUw_bT-B0?DH(Wd z#e8J*t$%O^Q{BRbbIFE0s_RMFsxQ8Nm7BqiXS!&0vRvTYElLG8C5FceHYNrr$K1}@El&sE+66_)j!J%D|FMBF)0@U22+KCDB6>UT zMgc`3!jL@&DbXkt6Y7450a?!Jk)MiH;q_w?XAv$}p^q9jVDBX6v5U<8>knn#1*{1{ z4uUmR)^7KD8i^817^SK678RZ|53-0;QF6D_`o|!>R)dfYnTT1r|NT6LhkPB zJ%A428>NP;{MS)xEdApL>Wq=2Q7K_jqVK0?g28qWh#fs*g9j*Mm<0?#chDvQaoW+M zb_{kL0j>67v)w_y;Gy9i_>4Gx3Wiw4KLng8M;Z0;p>5n!eL6cKylBE2y>JI^8P;P@ zSE`S{wz?CB9h$q_6C;VvbE8DM|Lqc4NQ4=I~4P8>x2Sw6=qdD{H(Hyq%MJZZuM z3II@m`2QEt|Akr{O&nY-j7%K=GNS(xN$`9|^#4l1$kv+f@A!i5Ux|Xw(b$sCz|MkB zv=YTRbIW0Mc+2OuAi;S z!4wA0emjMgTFwH?7fwAvr;oqJ+$S8$%4@wps}?ja<35j9N5C(xm*Yqv6zB%=yrKWX zDTLo7?qrcuxPy|HiSO+D!F*6}N||xM8j&tsPS6_!#ZrhUV0;uWOoT-oJCzu=ze;Q_ zVUh+pzLtiWIKzp|mW*+OL4l+`buJ!cQr`9sT`XNgW7)2q!J)Ka%u`1Mi@vj$FpQ{6G>izx;m(qk*b2C=oInbkU=JdMn^d}6W9WDe>We_zsze2^Pq}QcTYFr2sb4fB1<|vU zxOVJt=WG6tejc@#48mH~fE07o_M>|kS9c#D18=PDc7Bc#u;^grm-)-QeRzmkVHb~) zF6nP*igSE}lFuFsqjqFfow8(;In24j?%UjJ<<_i^Tv+IKDP+Jq{ueFPhWW7!sZJrh zkl6f?+vJ-B)R1@}9vc<7mR`E6aEr}*F&@{O8?WKkv>A832MF{M;dYX>tE6ho!;bqf zjuvHl5*vOO1w-X9n64St1+wYDRGKtAlHSJ>!&8#4UnZsQfi5=e)#DqK$;w&0$Foa! zN-%!HAMIFIw&)muO0|ng?R8(hufm)KOyaW%>TniCl!K`PvIl)QbDxqP6-YOqP%X9Knp#(=ZIrLE$yg{KSd%MY z@#9*HFLX{#g_O(VisYYGjJ;eidQtB*7AWH4_W*9r4!%~~y6v{VIFy1*=8f!Q!Ybw@ ze>}Q}%t7vHYooN+gOfADhfUhO%vifG1Nz75a%;bczoQa9j+IY$%Qv_0v>S_bv3!5^ zGdV11P@)b8N6rp~DAO#{YZ6#*FWzXTsfNEI! z6jA+{v0B7mk^DSE3%wPpVir+_t+El!io+w03<~0U+eU0Llz!>lp?C-weP?KPd1XBk zCY|W8pW5W%o|G8rnJ(`r@tL`X9a?IpozV__>*rE@TKc@VjuEx>_f=w{%E{d9br#0+ z^8K`W46E61u?ynII+|i9^=O(rHfGc=!uEkRTPaHu5gzqb-yjaPu&wfcj##|KkKdq$ z{Mh@l@k{?6O_y=g8GDC=cadP7Pne~w1)p@?bvt3K;1R{eJ#Q}sWCZJ)txl!}jST0i zkMzB1u872PE)08Bv3$U~t%!3uMrs$*y1fXpu_%b|a1(|P_SFmmDaNjR`;>DYGxDuK zDnHKd4&>)oiAC8kxe^NoV}`lTU$>#}>aa{hve2(`FPk`2@3?v1L(Ptp<+)bQu~Vfi zm2+_8#U36LXy>GxXK&!YpWGnjA-GRI^L#Ppe`dx%W@(oHRR*zhu(dOBaI!FQRC1A< zlA@KQk&sakuTZ{`p^=(8P<))40cMIsiHbsnk*SWFs)B(<&C(GT8v0o?Ao7vtl?8GrKaYT;qvU`%Ii;^bgq^w&`4kNKbJe@gTp z#A%Ie9Zdc*kw>PGN`Dgl*N6Y}*B{`)f6w;+=MKM<`oC2|J1zy)&wv0h>n+PT+bo1l zW1df~FxQNpMz+8;OJ$&+7XrO3df@w~oI4L0HcJv#_5CCOA>fHj^J===vRn_zH~tGQWX= zQ#oRhN|pLYtOxFOi~-WBKqvI-uLoR$)$oEc?b)7}sjk+}xSi|_ERQ??q059!r$hiw z;uM{BDW6|mdcKlZ5h~jr?j*I z&w}s+ad7x#S1yh1WA7LEKITS&mM(+^%tbo>tp-T=gZf#5zgA$jiDmny_dWI%b;yUH z9>%cil|OQxbflMfJd3MMYeeMerezZ4JmR={~Q%# zQ5Z7&$_qc$h+a&+^9{l|(`>>aCXv=%^?HhJ|LpQ`(?Dp*$8`68POQD=+ui;& ztJ>m0BPqQOCF+Dtw;*bUDHfQ(YX~p@I8e+tB;Qa;V2l@(Lkc{5VOM=cIT5tmhOV;g znnKbL2NC|~5~BJXoC`YiuNx~f(TxYgQq37GaTzVlQT$G<0`Xsih=&lsIe|6#gfQ9U zVW@rnR2Ld=>MP#DZ0dSEQ7qLQyvuX$(+Y077F5IjV~&xnv4OdP)nB{ZADtTYkCXo|k>NM2|IPKkIQSUQltSRlA>iJ_-1ug*439?m?) zzJ2`qZVRwIl&aAc`qYTg2dNJ*}njT|M-n3Reg3kNl_oAnxeFiTcY6~a}cEhwE}=3hv(TNWu- z);vzx{0Wx2jfH`A6G6t^)1%=HE4Kll!R3NS`m~;Foe_(HNYyaT7(p=rP z>vZm(i`exNYh89;LH+`V)mPG5&EPJ+v%cnp-RivRBrzEEDvuFC3#c-RJ0NLb_2r>e z>!WW5CsL~f44S51o#QJ@Es?aSXzO;WPm?^4_?vP*hD44{hd`El1Dg5CoK80?^R$ia zPlHMY6I!@3c`pJiq>#J8GAP&aAML3V^j$byZN=W+W|lsy&YM~}ez6ybrm7X`W$Mo; zD$wW(Nh3PYwFb%tKbK@ntuz{MxQ3-S8*ecATJ6wm)hYI@q1kHGIYRHzdibXVb?}}9 zDDOvPqLuZmRQKNYw(uSuntf@jLtdldv;;0<96iLgm8L~(1oMd|hKOTBa4dagT;DF= z!M5cR7iv9$2Du&*AKVeb+KM}`97+|Czo?WcN5Drx`l!nWwnBsXF6Q8uGZUFNDN5B) z6A?LX7iIT?`gp>e^G%D##MctzkFI~?KdQMRx<&3LxkEDIYvUgHh1_%(CFO*q#BZ!a z6cAB0k#@X+xvq+nF6>{ak7`tP9hRKH^+=cSn2d|@=vdSlEjPxqYok z;Fio;BgbgC#0zoJU;8rflkr@lEjwbd4GejOF$x>8lyBI4(!x|vcrh)SOKAXjH7M(c zdQK?~>E$%i$m!l8l3HV^5;b!BwJ$%_i}YJKPA=xwzzuRyl$qSeixIzh)~i2f5#x-J zLWvtYJKI>A7z;Q$+8S9HIDNKHq-|}?1RNX;+({V! z?*7S9GQX2^@XoVZbpUSw^or;ZbQlFh1hgE&LVolxNCK4`D>`1UP&ndK0Q00LjF;EU z$}d3u5SsuJfzY_Cts!b%i-DlNg05BMlY4}$`G$G5Z4Rx;?$p*f1zpnoZ%yNEdDitu z6;AE}Nen7iG)WUCl(3P^YPyvKKBFPXg0|MQufg$zb_B-w-x%i63|%=IDj*Jgo7Vt> zuzHkYBcEAkqqg{|O}TZkFnr#ZFDOY7Au}B6GD(GpqV5?JTHEbZ= zFu&v>3X+78>EgY~52tTQLR*Aa(>rpWOxn_MU)Jt)z5+Nj z_YxJ>v9v|lZkJq)TSLPf<$TQ&KmP<9^O^HX=!_RZjj~yZGG&$I@h-#j3wAi(X3}g$ zcjzohemblWQ#7gw6MIl((&ml~#Yz(siE{IDm1@)TisS}-%Oi`4ii!@!g_UN-CG{*e z8576Vo(Uz5@%0STBuL!c^u3PFMWP=9upXCSq&!6+w}wxYOzy|+Z1XfsK<&B+EV3dh ztP@z%MU6m-x4K%iWDzo|DH^`nbE@R2vLA9OAhx27hK5I;X1G)wTbg!}%)`^lGZI;` zSL^1OeQnXXmCG_Jo)!0xBC&f>G}PZCqLAZEg6tJUzi5#&5)h9X#)&|bs!L0`kuYp) zl>-ULCRb1l8=k-PH^%tbN$11;F99D&oqnB9TRDc*X<^};aV|g zW^iK!)Jp(Hk5RZ)2oBZk45ejgX;$qBetxtXxlf8KVY7fn>qf*>0;8QO z0xgBVF>n&m6r%3C==Fx7@ri$6{%&U55?R}Tatzpx4fdu1l31YFI+*L}ySW|Indl8N z*N^pq$U&D~E*l{@_?b0MNKDU@x6u~d!sk%N`CnzUjvTi1J&`^ZN*rR9_(xU(ANaqW zP2qr$9{4i`F@E-y|J>Qw{twRfcQ&o;w<@^YzeY!heV%X2Ch-#Y>@i)&Zh{#2C_6h%9+mlWhVjR;kBx z5~E9lymUv&MaEW)FTcNp8Hk6VzdeKXP}1z&8R6?L-dcaDsMiTeHO6?{GkNLRAE>66j^y*oAn0dm1zM? zmF7XD`bi?#nvAtdmbQ%qg$|x#;*`1+_{S&p@?=xFENT(lu`oZ`>$i_vV1gE>E10h7B`fAQDGr>3&9apIbqedeQTD~oOn6a zzy}Dl$(>?YB~E831N38Z8-{(UeChC(H=R`pR!xw`3AQgHHqJEo2mvt<3@WzLZ?QqDO0 zw@vGYcnzc7l+;uGr0FPHC5*@p94>R|gJ6PrLoBDff%R^Sk`@p-gAT>rq-*?Iil$@| zEf&b<2D#1XG8VMTnl)uKPgGGY_9P~t#sl$xJ>G^6fZNu zr=G|8nDa@Rvz#5uywVV*T+@)1oz>5rR9&2HX%JOPC`MEtWSe?9`;~8(sx+|R=##}g zr4@SL#O3@3#Kf}8Tfdk_J~G0Ov}zIAms-DcCIps;HFJ=W%DZEsAB(%OvgDM12TRcp zhG}wMFQ)?nu5O#>=SIVB1DXQsiEL#}$|<>EWy#h77>v5ceCFF`y8-Sp+SS4I344O+ zqHm0rr@_Z~M)avVAP4iIs`@f81N$ZzqBnk{p>4UV_B`7Qr2Um3cmT{NdI#f~c6<4j z8~ED@KFk~9_1SZx|Ca-LtP)>I6U=7_-{d{pu4Z)A@r&!MC3>bCcoLXRvW73Q^cZb` zmN0L~2EclQxL_?~>z5GUpHuyP58$?!I8muR*fbGUe|0g7TF`wzJY+KMhaowzA6rM( z3a^e>>c}JWI#yv^u}Q_I8UK+P4?&&pVRO*sNLgREIM`O@PGp&twzKL+7ocK`m+D*( zL07|x32_vQ8SOJpH{m-DSql%i(!jR1d(3anQz~`J0fR-p&gSFR{?xtaMXY%)e zixW{f8iSG+ms_TYhx`QiyB9BiPevh47&GQNgeh%nf1VY3;%;*wZ_bD!M+aS!?xgS5 zUw=`}TUBcO{EBijAv>8|oW)vR`ehdy_1FN#ou0bJ1n1Wq(9tX-GHwVui_W;Pb`B(7yyww8V7C#axj=lyE;}J`_}{?o+S$PbZ#fSLjld~HXLP#@M;+HFeaPS^L_&->;B#euLlla%iUIx*0Kx#Q9~x zrLqiRuHA>Dw+EaPxDGd1!dovTn3J9ywK^YE-yRusm0@?IdjC+!5oYCBRYmq1Y^Hp?N*&Xt~p z;Svre-;LWV>Vso~o{FkTp+!$~fk!rU*i)pWE+dC~02+Ev%+|+BIs!IFy2GmMj1uWi zM6`z|H_&Mjqk;&+q-t9`!ORegX=B=`32%=uSbeW>E5U~(cdOgLHCw^_X!3D3y_wL06n`(c1E1db z5J)|go}uRU*swQxUr(sIS31CsQ17??7BBonSMC`e{yO|aPfzfxVWBpB_ydKDK}WZs zmdimtMPfG?@LLXFh>PJ+H_6~z&5&}a+`cK8oK$UeCWa~P&gci#_p^$b7}xrNwxu(} z&~$|Fi>P;i?hDc(y633`o+@$0@f)7$7rG&ym6v!PC@)dXTemTJG}jbw$fu^^ua|3D zK7ii}7Hq*o;a8BaU(C|bXJp$>eys@}$-#?BMx9DlmX(n{zh(A-2fu+BugkaJOPpR2 zw&}D_@2V8!%(wvIO#vzFZAvjE4%?j6qu1nphiboQHCeK8SX4b)lyzPlPgZVsd@7~z zF$mbNsQTOY05x?z;$>r8p)0jb>Ot_x#8ZDUg*B<|tf%gp}ExXB<@$F0XTY(iimz$cVO9H~3s2mY9oG>}4rjW642m^-U9Iaj0l(Q_qx5 zjTmLpEZ}8m62eE5F4ce%LuyiRYQP# zPeIaTL}3BUEIjF>;zjph@P2$?w=ogc&O_ z7@N4Qqs~UV&QO+Mx9*#QbA_SLMHMRS*<&(Zr`wCle~_55ZY0MAyXs)QiAB8w%^BQM0KQ(%m7Ff~m(D+K4&i)VCH_*OJN_hG*tJxPFc1_58Shg$M974@v9u2!ri{+bcxdmGSfNlM(z;sPfV-tGr@- zcg(>IoQzzM$fDxD0%4g9TPKp{T}1p0&b2f|XwcE@DR9%V#*awFn_cl#STv(K$G)j? zVWOA@9dD^7m!WH4O`E@*sDU(LMNkv0U|$fSErp$m{0P~XOf}t%Jvdkd(O-BY z8a0N~gYL(dxTh)bN@)kDsWSMeF>~sE?lg6owQwpmkWyJAacrGg!>=Lb^uCNK@A_^= zV7i8w)X28gUP>ON-%>Vrwz(2jrM9tj?Q{aO~9Os-2fW6gDpn2hZoYZlchk zi$gMBel6Fjfih8F-h5UBe<4nzgBYl8zEBel?)8uJ&YGw2OnFrr>L8T!QXN!Lat!z~- z*9B38_oPfiVuB))&1(`2a0ZKZ6hVpb>{%2ca~yLCEyba25~!A>aYce|f$P?j$98Yq z8GpRaho+MU@CUIUPNt+$$Wq};soC)^kC|)fzf7NUeLvo?dojJRM`Wdk>Y-Z=i?(9+ zWV(jdvpGs`*(|RIZ|SZY*pNp&XEidT31fjQ1DZLPkB!v*`qqiypWG;h6#~XGiVZ1a zbh~Q%RTWfcEh60uFIK6cem1yQCXH)>dJE7;C`)j(#M777NO7KJ!cv~rX`78XqdXX+ z|1jY-Q7_MIG2r4HLGuWQjZn=&KymT}t1&`R(s+qA-MDA*mh0QrceThNZIm6M2)CFZ zNxO7rP?3s?VCn|NbrhXLYzZL#ZtWqB zRZo4(f2cE309vlE*8Ubi(I%pR@q^Z}UzhC;$IRBf&lzgj`+jXPDBZD&6m+~PARDx> zYG$5m<^VNxSYriIIiuP+$02|naRT$cqq0127@{9d6*>%be4(rCQEQO-T4vI z^@Xzd8EvB#>QIq-gVp$}f-h$ge#5$#z7)`f_agW@#L!afQGmy%w)*oPNBZ>sWeeoX}m#;yn9OUr8H9KMAme8 z-1^cg?wUyJHPOKn$QfBH9rl|l!rGFZA#5Zu@k5R2>i9f!$`tv#8FUzq;44k#CPRi6 zS5bh+#nkb-lw%*vINRdgE(?4hie%$N8R8+$$~1HZ!49bYt|qos95G2^vF|m0J6f@(mW}=_yU{hQZiF?k(~4S z9qnV3CNho_XNdOX24YDTgWDfF%`Mmp6(_-oh>jgvMK705nm|_{TYJ2rwHCI`AMIZr zdS7aTu7+cTqBM@$u7>JtxA+9nRc%;!?THf$Icj3f8V{Ohgs@$}B*&AjQnrO>bc(7C z;x3p`00C9F(n$eXgA)?&-pZn6>U_AFT5EqF!t z5_%k76|nyxb7D@GurxrEk^o6bXrm^bQG z9qgBT_&ZNYEn33bp;8kGc!jyldq@k_dZKS$Ayl%k87d>vEfw2bmg)h~JoHTQ~p?QOmq!xfTCgd{1x)c?*;h3>hQZWHV3ZoS8zs-t{IySzT&270xOv~U|2z< zV)cT#30d|zfUuzg4P%P>@!VMBvyX$HIi z2YO`$soDYO77Zl_u+jovl>>QnguqBl=#$-N%`mECKkxG(QMbfhr3ZPmgh(4Xw4Y~9 z*v?&GwfQEy&bl9teh0osT`ox8BZyLw zC*~f3JL9@aDBzO{=q@|s0qE=x;XUA*Lik;^MHY@H_|AChe8Ay{z)V3)Hk`D$V57^z z%y^M@dZ7!z*C5(O6wh+rLAz{pC+GLS?aI>sNlcjhxhu;Z;je-|)&DT){~h35YI=I1 zsQvh2#=e$r1qQJfPlEsvtlvNqBrga7xXuEJFj^_$R;<}^A)639WwV(rliw17ruXdA zq** zXP$B#XWCD;J*TCoyFF%r0qXE_@B10uGy>GRb_3K*+IE8vAl&U>;-S&eZ6F=H;zHXu zZ|r0eG8eV#QTS$ zbo!%_AXD#7O_);d9NVMNCE5ZisZD4S?NHV9$c@^AP_=*D&n~&EhpRBv#MiAuSK3vh zTYj!H+QCqi#>}B6COboQj3=8%Js!J#wZBEC$!333^=6y!uvGipX!L}!)xKzB6fKg? zy&y7kZuEx9W<0!9{U9`TS!jf6$aRu^D*8rUHa0Gki$h@~cIw3HR5YH=!+2XJwr5+t z+OYTL>P$2rX6^3q_x4*b%-X|YKpSmRBR_2LcElvcLD*#MUrcazMzheUtK&nOm;-NB ziMI$}TF2eea-*3hBcj44L32o<%Van8=J5jitvqhz^#R@C;O99qglnYz;ki zCx~kfg1s7pY~yb*0_L%zD{f1~3F76EA?8iS>`01+5lva0T^^iTnsHV));BwW0kzuc z(PYg1%UZ}0Br0qiI_W^&+V!@sPSUH@@i_s!muI`AVGr>Q3>Y5 ziGSS_cg{Q|{%R998j6_G1ZpI-)Td6tARcs1s1g|(7=BJxf;5Q@Wq#hG?nS|$fOa0m z@*_-j%jyxC>f0BzYgO=mNLn8h1J`!m8JAn>JuE65lEj+&u=Q%Vk^KxTJqz`aqC=J; z!J_G`7CYrjaX4J%gKq!8j)1DA9Dbfj>q4cNOk}Fed9P8!%z?SO7F7GSMV2~NuaTq|Ny~B4)RrMG5cii($3KW-4Pg;dWD1C^bLA6u z_tcf~chArx2&>Wp9!VrwS%k}SN@&fINx%{{DO{XcM9!lg&Fc-;%e^6n^6dh)=!>MR z7Rz|j7TqFeFwI1^Wif3$oj8s;}MCD&a_usg)aT~LVQKZC#FcFpWnu<=W?OvX>31n$qlvcB%o7k^i za!TzMFPZljiYkdYny14SVv<-vYSS%NYD$h-nE9;Ut@w^Trf1g|P7aJO&m0|E9v|G- zv^;-$(OAx$&jIxfu&Ck;AoZZ$=3R#5K0J%ZIFmL$jKcNHhX!jOd-W*KpR!te`8>JH-O z*pPZcGoY6d<64yhSF9s#yMnb0OaWV; zP(YqsZ3vd@Nw2oobI%*93!g8q7kp2MWSLGx?S`XA#~*$`48;q)CoyZYfvk|kD>dMH z7UH3Z)665trX7|WONREeJE~7IfA3)FT?Jc^1G1Fd|T^_&BU}N zI$XCeMZJwTMmq5>#Lvy>)MZdvfg{dNwTOfpg`g__zJa;a&zLQ%W~g1LK9Tau8Smu# zH}kZ`CiyT*PIx>;U>3=Iq2wfQT>-6omt9cgsL!ljWxMEUbCQW;aads~4lR>W%hehy zdKEFV#}dk^a}{2Q*YY<~ZlytbrhIw|H+6u7p@J1kIf2AB^O{1xtCdTo!*bQjNT)AiEs2WHzf<5f6~w)d|L z>CGqid^O_?ngPmFhlo14BKgN@PCS!q{R4S88}oe0;ac}qZGXkbdHmgMD86vlH3H{r zhB%D}rEuJTpSp~BdGbd;hhOAz(njJfjHlaQ*$ z_KPUN4VirxxNZ5DGNMxjtM{3O;|emz@0OP@C>r~3fdvGYJf_%tMUGqu$)Ov+X=gsj zq0akF2N5;iMP#FBre34ip8SlSL8e|cZ-;m#R_ODR6NND{Z4H7V z*9-}1E(BT{u}$D?Ayguqq^%wG7~rNZ4BRzXfJHa$Rl1TeD(d4e2!s*;DKcc_itRc&Lb0`%Yv6R_#v5LIJJ2yn(Ib`-6e!4bRK?|f z$DE1eWiyyP9b#*+7*m;pr%VKoUfe6IN^}V^Pe1-x7A`Z3_Oh$Ma`njMY-%dESE$s) z+}33^u9Jie!~$*{i@w_dUm+%k;POfHyemCDvVw<4W~SD2H_(hQG;1X>OIf8$_4WeY z)t9UC%B5;GwMtl*@+1ur)_BeK&U08vZ#I)cfweS|a(0Rz=}9;R2<7r|!*jjX@w=wv^#{0FP{d0qD)|s09eClu`yBq+A z{X}l)UDyb+@)Mxf{(`c<%5+E9a@`bxC~}fc=9uyCb_?x*qub&?6aXE{z;?UQwO@a6 zfL(QhSP^Ay(h35*+aDANj#)bsH@?s@_j^ausk8QNY-px_QOO{`q~v#BDv%c&0d z#h*$tm#ueZh^1yuq_1=r4M5=lGQX^Tole1MXsckj;v{UnbdTAXrp6o1hIFazk+VXg z;L*Je)>xqvS2jUV`xVG59_4r)_lA^dBfA8%?POx{9@QYCR&htiPLe(JHI*cIxKq1o zubaX03-6Ia8ZVfmXuf{p$n`yn6=q=j;w5Hq|B{&jUILWoui$&=in3fTG=i+)V2DhT zTqm@MIH#&TO~!tq-3^j^`CKnFG<(lm3YuHT${;PN5&g9>=veGYYS`oK_kP8`uK5)A zA>+vr{g*LlIc(|5Doj0+G}S5dVd`Q1Vpb!)m_F~^Z*|gkCpnmPdaPizQbxI!_4F%i zC(~ixL!aIBN6grzi#5*vM%z#*DqmQU8zP#(onESET;&0&L$MD~@0%SvjCb&u{*V6V z8_zbp_gtQr*d~z=5ThG2H<))Ixmhw#r0InmH_)6=weSH@WrR8qM72Wbl{{?Axxm!C zpbdrLfwa(-0(3cjz8?6OXROMK-fpW^+06Eb*X6tt%?k|UuR&jplpVv@F)P|dE@_q=gfH(yY& z=jmLbj1Sn-bDDn&ql)-^D4>H+P5zf~4Q?>3Sze8qZ;USrn`W;E;HEJkHjZB=s9hxJ z<~cx@FaUkCU$jfF(lKgXn4Bbye{ma1%K;O{uwolz>cD_o{7p)5$M~`P8-y2nj~JN! ztrW*WK-k<*hKi`C;5grO0}LZ%)@VUyY;zRNN6i)gIr49h*lopx_JpAZDoP;iGZo_aB%wj1Y-I~<5tn(&ocF+-`v_bzm#Z1e>(Q6lGL);C%ELpPtB|qA3Gh;s2_#~T=T!u( z+bV+7R2-FH>!Yo;Mr=C4R`q^0A8fV*u-xU+24_DQvO`-LXjF&x9fWmySTcaM8I-L; zx74brmHBF2{)hvLEAI6hRWK*)?mxBu)a=kD*x3ap6|Q7mwQ0;V$gK7mUAGM--#kZz z=3v=Vjn(X34bt*n1?rgtEPK`31ZWt@#t04yUmSkbofmZBB6R+OQy;el$G=5d+v3-W z>(}9mSMT?V@wwBr{J+OZh&v7UYzSm2VC0wJ5QJXA$Tid2Jk7YmrVk4cX}>pap4Wx({e%p z-MPCZ1-Vlt-V9y-)qxF<`<@6f5*GUA!gj)~Lt5mPd<3t&>6uV!l;`9Z*mwZAU*mf>}@B zO0~1iXN~@!-543$1%8B(ooL(*dmXGEP4mQ%`IM3peMl8;esOCOI2Au31=5(zA5>f8 z_z6b$wHyQjRQ`kTyJCcYN+qP}nUAAr8w$)|Zw%vt3 zwf5f6|L^lX-&!~4BHx?Lyoi|*Ip>TqVvI42=Wos+5nP0}h`%_Jf^oX^{m&DNDq(u* z#5)D_@k@}vCq)uUzhV8yml^a)0Zhq|>>^Z+d*R7tldf(HhsOvYkN08v1(<(E_6&132MQ}7ZP74wR zDhk&8K9Pf{mDSOz5avGTE@uvxXJW34B1?{Cit=LS#5-%fRO@>E<6lU}NGQ1K?Kjrb z@Q)KfME{sS7Pe;pP9Ux)yA@tgP*5aLSyxb3S5R0{(3rPwI%QMc4qa744ML(oYe(N2Uq@d_Ls3K73SV1G2?bNfdR}P+qQD>G z>Atdtx*a`!imH~55LiS&L_kPD;PeeYs`d2&>e1ExiIIS?zVjapyng0s^sh0u*zech z*Tdg8@tj;mEv!xC9qn8#{y&)ce+ldPJFI_)00fg;HPj&dIqB^a_md?kH8Cx<22Wct zDLvJ;I=td%ygwCtZxcBON`c-`dmHo+B>6bqgtP?BJP?JcvAo3e%#z%+oW!K~q{PyU z3=qmn%aDSM81-tjv&Y%1p=-597OaA@t|6cq~ zuKyFH3;qvCr`MFB3E~w*SOXKlMh;0UJc~5sdu??fwIfM0B(50pwkv^8X8*OHR>JLsCN?{Szt`QbZ&SZ+!synkzk9~$6;RLdrjooTWUO1IhPs9k(->NZWLFa!@B)*Sc@@wmN4xc*=cT&uDCy`p zAtjT6ZL&)Gxaek0m6xM(J-E+W89%{=h7tZ$+9qSev|)E+%~B z*?Bq9qJaIP@N(|)h-vg0gckdUdW{hZw;ZRhd_5aTiv2xF^GDRcf3;SL?h^fIhV zmKetX3)h7dbxi_D}PuVu$%d&emQZssaQdxS= zAh@AJx;;ZMdt5*t!uD6mkw{!Ez46RAS&Ur{cTd>^8 zZ-<@4Ou{AoTu3~4a6+<>>}zByUzC2UXVXsmFr{vX!JQ!+ptZb}z2sBPXqpTyPlA`b z=&A~>@pu}jbXsHxzi-LNrwz!39RMd+om#Z%dSa{?C8=4)v&(}qw5^E;LUP>@k z-oR@jMqNsfRhCjzpeYw=CjMdRavslomUs=y^BH0430e}(!;B>)(uD4p-y9Y;SB^4g z`r&Vmz+kyeAy}G^B+|5?B!=K1gzPYHPEoMn9$0h4lSNtZ-h7`_ba8nq}t610; z%x+r5+5?gp2)-)FN3gQwV?Uiu-=v&&Z!qbWv9U=i+DNHy~T zbmyP$OXiZV904j`o*xV2^fES}gSLqWlB@7^Wr!`pbteS$+Xf+9`A>^KMJn`3`&EVX zy}O`0r7{(PP%8sWAX1?G@3!<;)os#rK{g0hZNaPfer^>KRRCC32Kb15!(#g0UKr0m z@Kyqv)%tz$S7Qn2d+wpJ`UW-Y+)*yc0=gmn>uu4W!4I1Cw^ZtKA)ckcqlQQ7Af!R- zSemFRDPM*G|Ao#DxiA z6JDfuodT>gJaQPCLQ7UqvMM7M)Eda02#d0HS#bM}iWX5PE7F~a&Ey3w3l4>Mc!)17 za^UUW|25=-(yHh7@;&Ms1N%=q=ig7&{{Mfe|AD?Vyu26HlK03eZ9hJxpk zx-8~WeLcA4QrX$D-rT~gYGpFh-kYvxngAgo`NGGSJK27c<8{sP4S!v?pL{uf)yjE; z^l5VL3BvhS?}4D*-}1qJj0Ed;of_}kfpih?ngH}7-Fm_J+3&`}e$+xe+>sG{uKBAK zeMSP+a^9*5(6K`Z(~cKdwBu};2A8O>_p+$_4%R+q`jWdXI+J}dh~`&uqgD2nzxuL@?9 z*do#Aim|qrXUfXaEN)i%Q#IPP9lpSd#nPxSaYx|Fc3sAcxno*nMJZI&i>1Rqk4=P7 zw5o;;J{HR%poIkE8U}+wP{K#(;CO55>iq+zleN{x*3O2=J;MtS$bWpBzo>FPH!PS) zr#Wb#!<;w=To};{uv=nKfDQ3-s#>d+wG3iTUCpGc)yYQA#Hp!rBJ2*>j2xROAl&>C z-XMd50lV57`UR8Q{3aR{s5_*XDjodgUO9hkv26}748)x_AOBZXEBrAlSfNjHzd8Br zlytHWC>=F=ZB?Q|jv{gtT-c>f{VBitmGv4sQp20Er5k%rnCmd0PxPF+9=?D*QzJs7 zXSltdXe|?_6;p?Mg!Scm!=|RDa^`WR4)3+*deoPBR5Fc(N6?BrPlR*mGtJBG47Y(7?PL0uF-}gA`daD(lyXJ#p5murfpYk{Op>)!;~o6sR0MCIqPw z15rYmwHep7=@b&I-p~S?7Cl`o4M&W#U)=^ciC47zU><_BqxoC(DnGmV;&rxm7WK|x zj)Z*~JgVI3l;)ijetwIPI(@(In|nyYByNo8B*e39Gt$6O;wo(=qoNj}vaG{HkjCA$ zF(EP!x&&Oa=tsCYU+HCcS6tdUe10pnP@Ud0x+TQp%9kJf@GMA|dH87nXk&JI{}{Iv zvIb#0RorDl5GzwDIjanPMpRvL+OP?Z)Ai~S1e!38t!P$&eZWOxwVhd~F2h1NQ?wkh4it-^ z=ERlE7}>cBkOjbFy{xr{3Ql~bh6<0OB$dwZud6Q^W3LQax);hh@-HDbPf%;{OHF$u z=559aSZ?hM+JTbL@nuM>qj90-cBXMS=ulg0PL7Zu-n0m3Om#C5!E-Ls|KdH&%!-<{ zkg0|V6e!e-prSzUNLSJ}yf`|~cn7-A*pparsuC<@Gk?v;j{A{nvZs~qr?IyBL^pG% zT_C{={iUiIxd?$LR;eO<2vdVtr`d-Ipa!WWtS{Tu~5B;2M2#sSm+8xv|)^n4V`|Qv^GqV(-ZYgcfc7fg} zfD_1GbfAho>wvXgcHr6-E;SdAUv>do=K@C@Egq+q*vd?tr&RjmYQ+Hz*1m=bbd~IG zyx0LJGJ64 z=AA%RPGQahg#>kryK~$*GydcJU1CU#-9Ck^D%wOO=k%Uc7B^i^t&2B?9)?_Mhm>VtF(u~MPM;<;+hEm6D0Q`hWmV%&~E zSS#mJ_?@Z+9p_eAJA`|lfNqt(4d5=FzHY#6f?ZyK8)SPvoaHl}fNrfmFW|0J+}Sf9 zXnSC}hvvlfH)K{OMhnQ}1UpW~aIIt?7SCQ;vC}eHyttoUaj->&G+Y;rPz#I8#sz%o zyZTIkK4<+L>E+Cc0x>H>m^@VrU}r+}Z*@Oo;Z0*>HsYBg^zJPrk}e*>eUwXM!}D>A zV;`F9nR}QE7RXK$IdKZ*&K@oHvUmy0y17t!$cGzcW9Q5mSrZ)K&cyM@gkb8`nOko0 zYja)bq=nJ8T{}f{m(H9XJ!+Il$H_5yY8KE=+4UsbBU_wRW##axbJ&6IpEP7|qp+Vz zNi+5KAEkkiGRQmzWfa6NR~8SM|%aFOyeoB z)gukeWgu%FWL!}~RS^hJ8W~g;qMyrNrV4B9db&)y%#7{RlVS^^NvvW-I$gZUd74~n z#MbG@mj-teBEgwd))3I+CuioyePJZ$N@IG`I=x-IRG%wiiRVBI(zq75GBqqXD+3&= z7?r#v+69voUa~>w(Evzx41xv*R0iHYDg|Dt{ZCTF2B?$LmD3^{ae=DYG7seI;}YgEVe%VGVI(xlLd#Ew{lKpIUL5zFcHQR1W;#H|{53XMhsoERdL%DGQ> z&SS=wvI`legjy;om5?(EduAW+1LEB*kb&NV#8KHJ0!k@nNz}>Es1%Ap76QCy4uOec zgzyj`hWVV0GXapUt!&o^4b4p+&`uHIOS&9{JP!etIDRo@xx9>2Pu_?XJhMA?!fn1) zX^V-bhdW5H(`XSxte2@9?QDb$nL=UaXVRY=#$s#6xNPk?0qtG3Y7uYh*ImN(M%&5v zHzn*`4fODkZ;79u3`DqWtCCKPZ00)mma)Tyf8K^e-}UiFRLiFglIx|$K0mT~G%bzr zpJtdj_ANxD8fnqrUT|-$C;1NQz=TdweEPs|=I=A1SYKtVsra4p?LMvq>byIr=xd}G_OzT^rjA~1mYN8-dar_AwniXNdRqdA7$Km-?V&~V0CX(Q?&g~Z32W0#A(-;DukXVAnw zh3C^p+aE6vkk8nUH&pD7h_XL6x!I0|Cc?BDHdsJ6+)e6IJO-QXvLmpV2GBd}4MqxykqsIB!ixxd zD7_VUUeYc8I7nyIt;%Gz!*5du^sIjFvuQMIe|@OX`qS0m zo8;c*x5!Wjj+t8b>W5TlBZ`@qb=}IJCWCE)z{p*y049U#(}2XR)|9N69F#wE3&3ZL zV0=SNKd`G`>3Cmy^>6+pe7;ioK7L`&?1IGP?ChF!i3h*Zpd6TJM()hXe&o~)<5jh3 z45se`xrcRQHP#`aK09I_v|C`luS7Jla!0{ch+7SYqE9HThGI;pt%{*HQd}a8K*|fd zypr>d#6OeE-5i=TJo#e z=oh%;R^i*clXp@L_k|}7 z!V4SHNSUZxAC4mq=cR3IL)$q}k|Am20W54KK?(~jDPgCk%Fv}j@*;YbZ_8JuKyo5F zm1oP-q(HJF*!mlz>cbyU%?Y^77)}OE)CJY@62|3)Cg%8gFAGqt;~brpKoFY{L(36g z<|tv4hS>!x#R7ibWHrUdXo_-LVw{oZ93GAd^tq{Xf`1y;YCS~Wy1 z5wMJ_SBo1aG<9uMLLwz;u8LEh6{Wl=dSdLz(R`5P(%p!5255^Hs5sp`OG;h7FZ2Y} zjC?{zpgjx~Ov;`#$+}X-D~e~st?Vr}l-1|u^>%wV>4qis586XG{R<@k_pVOTvPL+& z=ni)?(`<(|-izkCHsC8YPLs~uz^%JBaQo?vw{BS?Tz_2M+*vm0pR@X02HSkmaad$4 z93URFYagheU7-&^XQGALB&0nS;J4|6e00esRt{55XFa0cVQsnDGQB~@(V^xM#z7SN ztndcDF1l(;={F{Vg>TW%EjM*jS9pLA(3eZilck+j=%2yXgsroJ+xvUv=KnkvwtrKh?OUrX1L;RrJHr_32NOVu^a$d&-bQO+cE7a z$?0!hLmPgDNc95a&^BvazHtmc>XRZroWIdfiRd49uVcimiUOmBnF@f_#>bWCKzs$G zMZP*ezX*#%uSk3tV7~nUrZd;hU{f42cKq*IHEw#yeUgrs*Y_Da+()Z^nqx z<<6vKTD>1zrNM}=rRreRXQ&1Y({tBf52n99cv69hddb(0xsd*VrX8LBu3ppxBr7*# zvFV7YGnz~#hRQn``P;5JU5}suQJnX;v1omu1y5K*N4qy0uesaTzj`MwX)R-?-zeNB z)j#!4{uXr*HgU9Y{g*h=f2)!y%E*1sM)_o>d9%~b62lx71~>6%<)kP7uYuOwaat-|m|G_ zo!Hz5pvlB7W+P~(xrnZib+Dy9tZl9klo#Y=sAE34=M@nS8)1F(u1xV7l=j7wJL#O8 z)^aWwoAGkSj0Yhf`%XM&$mpQiA^}e=RycIVP~O*?tG=o8m&Cc&8pWf?;(@L-qDtdj z_aTw3$L5WeUbfVpsBn`pYgmFQX7DDSFWdmY8-rQA6CRqhqU4UBl}n5%!E=_TS4-2Z zl%+Z!)c!0APWgz64`Z6puz6o!k3V1psHHknT?`E7q7&#u-R~x{kmx^b!*MKFJk_!r znLvdiC7<#G$S&&-(2~Cd3w?Fvd!$i94MPgHyMU~4H}M}LWi_g1{n80<6K+im%k-0F zn~`Of7~_m7yiCq(INPaYlJpcruSYvdk<;hRS!?6l#ZvZA*Jr|AY1?(An4^(7maC*y zD#By-{i-c2>yWx-e$QJ&*(JO_|7*T#30ZA>`5SY){zuFW@9$t65j7cs{{Xfs7fM(v z$bWS1VWr%E-IB`(Sx7aIQ<$p&Q~48I;^1id2WdL&(Z)d(qB#nt4N1dHzH12yaM)@UUWSdsj)Bnhi*<` zzP#qpU&<|Uzfg=UKvN7)H5#is25CxvO@&+|KG8!TB3Wgwx%=8t#cxa$HEaX}Z^d}2 zHB={H1Z1uWZxDK;2PQ~=YfgEDY#UQ*h!Z!YVmef&Hkcj17@k!>rRm@3H26Gh_cvq&9pV0<(j^Jd|vQSUis>jIB6v4eg#Ij-!Mp?ELvskihITmczSB9XCZ^{mrF-3JZ%eDw8wOK0-8KrrP2~yTNXeZou3}O-TGzzl{ zaT(eN2M?HK7H4(s#otaqj73{mc-_OU#4vue=~UEKwbCr?xiw>zwukMtL_E=1nPOGy zZ`&?1^H_w10nF`Wfe+Jr()@rVOuNr7;vbC&5b>_zA5IF;5I~KWzXjk`rVphE+4y7S zgDmWz)(F;?y9?DR)|r7ShP)m4_fd53-LoKz-R*9a6+IJ?kL;5jbnmab&y!))=lL~(294AFwc=s`<9vZq| z3$Z_V3*_(yc@hY3$703*Sk+~xo?-@FkZ_;eei8Rl=fXeDLr7xxm9DhE)wpQkx6af! zJ?LGuNGGL~T)9HBodKxui1E9yASesTI9Jx@UuG4ShR1Q9r*jJ3SRnbFOCx>K5vvn! z%cL_(AdHkHIRuF_MSubS=1fi;NC=Zs6-d1crj|>NV;G~^<<(AjN2I!qV-4Mza_PalielA&rYNWDZ)N20@sfQT)>K9N4&D96Q_#MLEM|Z-5x)raPWBs zo!fmuFD>+%D~xduJBy8S9~Z;SzXkGK4C5b^X$u3?ZurTC4DBura+wF$i9Uj)Syu1xkr#lFAF;GYVv&RA(SUN0vr6Xyd^9??zU%&jDnKJ^{rlHSJ zOpE%T$m#2#L*|gVXoPVF$@Qwq-Z!?taMs(alpWn~P~i3-)%X5Vjs0H{W)XLLJ6jW5 zXA1*sYY$-)BLk0bu7IM+U#10i7XM+L6stqHe~Vvz>6wz+ncgEl$JL1-lR%qS6z~i7 z#|;CAe+~R&E>KSA%^yr?2X;@x^92@I@xska=O9F@i~e2`EhJ%!f89M=Y6&NW@nquiXIe_ z8Q2E0A>6?dX0lpyFd^_-&%-~Z=IqT97Vx-hHNQzZwNl)!Ga7?GvI~oGne^!v;hfsH zMKjMczey!^4YlvfjW30pWSYv{S?nP`nu9++izdtmT7;4G1P8`|6dQ)-RqUZWYE-aB z7Wg^OO`Dk;cPxRk13$$U$c`cha!bQa1MAHteulyDURCuwCeK%mJ|}T_=E7az_YDXG zo}9$PC!lsHp7?{|&#w>p47{-fpoWLhfV%|XeKLWrD7B)ua%ex)p-RH;2(_U{E5j!j z9KMx1C>T*29iOLh`-$QBk!BanCQ+DR7FWWfGzha~jGG4TpDR)(raCUKT_KjVRZH|G z?X-$YI5nMZ#r}$e5%ezWiZE|m4GhHwvag7Z8&#){hIbKV*RO#f&7zYr)9fgsMuY|=R{N%uB(qyLK%NGkHk;B- zDvRqn7YC{;?3Ql!dX;OoBh?YG6A~i@tAUjK}H*=k4FRTU;A50Y!$X_tHIOTgmp!NhhF8IGseO)n%$WtFlcJ zQEBN#3y2rH&jUftW8uOc0gtO_GViP;FK3qf7G(EmBZ%Ke3Q)PXDwW6L5!<6P;x&!hK>%dQO*y?NNUL%(oSy+<3ul^~of%0GMx$(rPn6r7KN9?9G%3au z4njyLESohw>VmcgAH<)Tk()GRM+$~aRBcZfrA~+4Q}GfWM}&t6jWm@gwH3X1t}RHk z6=RWIR)9JrYnecf*39p)bz3&M4Zwja6D1neBtu)ExwbJe2H5)zBrm4QjL2A!;Y!8r z%tA;T9rupP21QgjeTxm8)dc5t?Z`5jYEYwM-LV1rp$-1&AQ3SbE8 zA|&)woN>%Vzm*WxVg$5J66^Zxh`AArH?$qbYgo{Ag|i@H2&=51cQ<~_N`#9|L4I=B zC|X#Fa%Pp460}VPJA=#v4A)J>Q>wax>RnUT==eO{LB{&Rd`^?srq)N@V026}v&xn% z=kKKn{U%MU$(ptU*ixJ;$?+~SPP@94H0mdL!z0{hqxQ2Vr7Z-hHneBOrAjhHmfH~5`wBa-Bq@lQqEdZ}yQcD)5-g;XNCL4|r@W(ya4F_(fT?K>5<+A&B{ zS3!@fDs8#2(#FV(mW@*}uyCV)YKayI7215@c_D%HiN{u;X ztT}ntB;mG;#-=zmqTSUsHOU;3ggzsi^CW@lqT;y6bLK?UM8xrVrBOV2-7j@lQC=64(jjk>+xiudwQ%l*foF9k>uesk6hryb^*A%Q2Mg@=IQhr0^)oB9Fh!`y@hf*j3@9P;fuhn%q)s*tZ}hbQV}IhoP-W8~RD zJ8Wa*#UNll+4EhY3kykxMa^+_7+ywdsH?1>e39|km7b0DpZJY@Xv=Vux><2Fsyi)653)rio+6j&O2K&teC1>3AG!ig)q4dkDakdlQ? zh~*z8G`CkGYZ(oQq)$PT-9v0)XP@kIy1R0ne)C=W4)Qo=7p@^5Z~-J=0+ZxR?+*9p z*=1-le6k+cOjOZ|g?L9KUzxTd!*#)pIU~#pzr0T^qHsFQPV(7KXD+kpDWOEjX*PQ! z!R{X#O|fvcL#`@b$_nWSI{V$dG=O~X{kYrx03YZxgEFM1>5dI%%;c9D(7TCmQ0J|w z0_~9$g(db~*(r^uHoIezWE`yFk0hNHPKZ(hZ6D(j8ry9XHQ7>^mL`5e2D%ytb`8Qb zh^H#v?=p@z?r{l&IpbukT+AsP6U1(hNaHZU#XI8`gPpr*U!M@v{z^F`#Z0A48=s9L z4WBX46TGIT$n0^!0?cnX14?V8!U{EPZVTPEF%0<;KU66 z;GS@ol!G;lP&@S`;l1{po03@LCK^oWW_OX{)F_3?Bcy&y|J-#vJH9-@nPZI<>y2%eL0AF7hRlT z5rU7@9^!t(bW=cGfQs#Se$Feq%!m$TfAB|uUxB5_39#0Mx|Kkw=6)-X4Lp)S7IV0E z!S${oHuX%`X8dHoFuhD<%s83Q6=Gf?sxZtguA-fldMpsg@`0*lK>6jt<*=qZ={DG* z|06lsf1aPOf-7=?aY^3&~7-qfzPS}u(V%b9zi-)kz3l$V6pkJU><30 zvHK!1uyuYg@5+yKha@c+$rZ=Y*BSnm6ex(L0J)|#sPN6%V_(oKGKAA+=~#Hx+W|9k zWIB!sQ=1pGGa`}?ADXFz7Ev=uLgH?qkAdd5p0EY#quw|x-GQJwl1)*6VB?#4Rf=`z zPiT-$B%gj0&{Z7wlzH{_Ek72z9&UXAzmqw2m3iC`o|nNJ~_nbFgK@9BU|sGuWqnYQUlG90PtY2>3!= zF4GzuEPYibrTCeukSC-6hw@zlyz5`K540T}Fq+>wSSbIfgT?fB2Y68ncN1e-gKr6} z|BV=%tYjs(Adl`l&AMEJiVoC|#c8}xTUSs}AQWZz+LQ-j3ZW<7@obDeUoEvkb6z`7 zr$rdf*N11~EvH3?MYNy#^2K{|m370{;|o+5oQioYFK;i`pAkX~iP_LbS6mR0aA*Pp zp{!h0xjHK!{B4ym3`D`Jd@J6c5- ztKsOf!k8U$22nmbqHIs-4O(d1ZZqg;^DyMxHh1h#M!mC&hS%n!-}08Tvx5ffchc@- z;8Z)3*W`{b1D!eiyL^oeW)E{-K9YI#vMEX^;H=5AcXS+5JNzbG5Y2XgD zOg65aT}Db{Qq@#B(zcxTL`Z{z4*R-le|$bi#4mbponzf_lyrw4EE29DvUQ%D80M)) z`yqV_IA%NZ`BI2wQ;9v3nFHCk0F{miR?!d<)rm7;qcNxvmI{Ap54!9_v)MDr@|Ls_ z-+f>piX}4<$A*X}uYjdt;%6&`LFd87zj23f;p`t5ZlbG6w$@?)erSv4N{Ua2C9io3 zDL@)(OwLN_Wc-93n)_UeW|`M02u!}o3S z?}^#?f0u~=)fn}O;7v0(#4639>qi8=E zWVi)*l?QL74kHA;Sd)|E)B1Y#_5!mHF^Jeg_M&)(_*5Lo(m@{RH|N&5r#gx8N#~Wbfyat|6g?=Azd3GT-$NDb>kI(m=sO&dOTTY$)*4B zA=!J)WkLeXFKjNJDWuJh%#jV3#VI=Xb@PE<&M?qonHz$=1^H{?LgI!7{?Z}(OL6P( z7moPv7Eav4$=S}#(ZJ?EEyIKhStNmPg7d;g%LJr403A3D{ygnCCGxO2b4U<)dD_o} zSBF*E@wo1|D_XnVgEaF267zkaSNRd9g<^Q6MLMSMoAmUJwxjRY?`I%>pc!fPG%AB1 zEI=+uYc&R$&XZIR{XcMB^`Op=(A|x9>-|N5q(CvDi^ZLVozcg`^4Fb(b{qy#cMfvw zy%EIbuh_LtaAz>?ZPS1m2DP1X@f^v*i^Tx^Glk>94MRus>2yjlK(AJ+Q{)kX(HP_I zq(aiydvBj`LN+Z|5ut@-Fv6yWc2MTLdM@G>Z9`~ZT-}M2vm@Hwp1KcX*(47m1t zD2X`C@6>}AmsN5;2wo5w*E$kdlZIH)BWw_AU9~2c&xxVq>TqrYr**~Gm8-Y;mfE3A z7+VL49fvcNL13RP)#ZU2=>trl2HU3T-}R81&`Y+#%j4y9~`ErX~ zqrcTY#`nwMWEDCEO)Dgk+XpRb6-BuOI$*i|_*5J-2Qr^_TR^UzM-JinIs*=dDl0#N zge=nyz~D*qhmbC*lZ4zLR#!za#v||(oFp=)G$S{4=*L;aWr?U&Vt6KTZ6BpY>a18( zN|RlSJRCfwbzLi-;V~4rkjgN2@lixnx=0*hV~+i;D3aZ8{CW>&aD>?Z?ZeT$^11U@ zx_0@Z*z0`PdDK7FxzyjKtAwqSv!jd6chAVgSj64L$i>;t@ms&uu&f<;4kR-x#yRkNC`RAaLwG#d}1vot2(YU zPo?ZNm*KRQR&cgvsjC%5K?}H{iaJHxJcI6peP$w6awFv#U4vzBSlwkk@T^zQo$(${ z*2+NQYr9l`*lTUE%Iadi8fIL9slb?>KKWGcH4m94W@L?j0tBNW+prYQNpDp^vubii zVyPQtI|)|e;x~{5C1Ke{mPlaKaa5*;tI6EV3-G@#S(?t7Qp~>09sQ%j7`ccjoB811q!=Y=bzbTf?sP5 zvbikJY!5~R_^l2f2}slXfV!%d+SZrfYZuPH;kZ3eb;4T}QlqZ3j_Yr(m>qll#Ax^+ zGm+4f%f2?{H(dRv?W(+4sKGXP8@JWXj)yCLBc>U;P^`bL-hqA~1##$_PiyHTp=c^FaU6RZVFXt;Zi$9-=@N7j5=#y%X20~MF zQ<)Uf`R>%3Kzrgkv`JC}$JQG(;U{*y^9qx2u02JB4tA#CgVxYp%x|wt*g=&9)5~w- z_o8w+IP8bvg?dACu)XqGAn?%pudIq!wVEOF$g@&-I(V(Vl8WWJA2ebtzY#M*NIB8q zdpi#c)tYIb=sR%bzUEv%V62*vl<=sA1Zgc!vH9#j#3#PxQR4c{vii_^)*>H2f(WKZ zIrNS-s_v{uNj&k4REHPxs@9Qbh>HSMl-AaIb(r<~?)-t?(kYG9lsEC`e3=4)m zFf*W=WOV~zv%f5e0m6-B)97|BqIr5YL05@2*cru)~3fq z3f(Mj96B&--o=*m;oD0jk|04fT2PzCal9ho(L2GOWVbIaMIC9NeKk|Pg;o;YrgCg_RdEgv{z~wpajtmE_~18O`{bu54+(OI^Qkr`s&*~NLAWXA!H!1hFW|Zrevh~nHR`-g zwyyyH(_-Z`_HLb8Y_4-k<;I^P)z8nTK(h%4SeU#VYMnB^fWIy*nNkwtusAyf{gfcn4BZXb^8Vtvsm|5a6(Ganw zIl(hlt6VqB&FY9iB5O0uPlJzk@Zn#_V;`5M==bk!^A+@;YVL0>M*e$i{2$xqzt4Tc z|3O)9j1$tObAG1jYZ$}OuZgPzLohIx03as*kl+tQ@lN-U!JX`q*y-}uw@Pl&KMQH5 zYH5yaT3&9+OF%DQzOL4}GS%w5dGGn{qch!pGnFuzIVQY&F*@39cg-{5H_h`6v^)&? z;d(&#adIm~M7x8*-5l=eaCdEldI6G;Q1ERJK6rWDZU121_4#<<#oflsYlZ`!4O8m! z>V?C5EM)l!w>QY0bv(@E+jJKT_OW_C%KGI!8yoWjcR&MA6iEGNJ)`x&Aa~TmZ5Z4M zpn5>;ZLHYDEjsV`Vr*kW?A`jP$15;g?%|#iuQ%sUOz#j0KCKX=S8O=B*V{Kn>|wOD z<4A7r@KCykTNK=!$GuAH84A3fPK4G;VPz(2n#~GP!gGFMB>8n1A*OG~ zQ|_8!GKD8%=)wGR+cV3Jl@1>@H$AOb{yL{#Px)#&bz9Tr3jHHd7sEE zPNR`sQUE{oNb|r75kb{9r{-w${2&x2FP4Wq-1)Fl<{b2}b5sI&^;wJvk#O!R*m8tw z-U^Kp+=vx7ixgAD@)_c&4hHhN+3Y%&6ng7<#dW+GbHxfZxJC_*pqDd+u{}64>7{w2 z5Ppe7+f&s>emB?+GOc(d)+wI#JXu}du5wE@0gLEhi1A6;t=SHwIe@9lMjzfzCR9bD zP8Fq-o#}!V%=xpl<+jKf?k?-r-!fvgefI&o5Sz{f)y*Oz%0_iPsjTGOMV}#<$;+l! z^JkWda5j3lldEzg#fLAM<>WMOh53z*gA)4Ho$zi*t;AJ96ovI_c+ARQ1a-c@5i=?> zCB!qSj7HX<{XaE-Wgtq2mk%Q$SQYeQ>-uvb2_4_sGg?Rd{%!7Mrle=Ys?|uvZ1~F# z6`{^xii40?!=R+;msR;45MX2pJdu2QE+K@uoi5RX6PmF@xh-}S&a5UO0%cITp_WlR z_*sD`!?)6t2wy!)W3~e?ks548y{G6ir1q8Min3A|4m8s&E8=`eI@$Fg8%T`YRq9z~ zy0zKDBCN6XhGSdxV0QM!9W7p{v9gXX&UH6UybDB<9V+WSML$tJ=KAxB6Ra0#!q{7HSqFy?9I> z)>;<*PBW_3#6-bDoX>Rtx#}iOmv-j4gbH_)%P)2}L{d zz`4~_H{WF}RV+K8^iBsP^G+LNXYl3i1P=KFp3ks_7lfuQq`2V|m&c4WlxnvAj%%`eg+LX}TsPV8?KY?p{vrDh%b>b4IS`LnTV9 zF$oySf%FapMiy7)NQCVQjRsl8)+YSb_}D4C;Y$)v4Uk5Na6=7%8D9TrT6;V4A;9a5 zLTzz!Ky8`nKc)V0I`^l|+U!mp(x^^*xjDzi1i7hLXw78`OGPiT?~P^6?KyouGn%T` zYFObkZk;b)8U~-@hc~b~jwyUGnKkic(hE^rJ zO(GZRgFQv)y=pDtQuK*13F;ttCl3Z-^;uUOIF!m$n6|JSx6eOHsD!6+9Bc5utmY() z&^{Fv0thqER8|&qgdF~$OPaom?)m*FE(DJ%utpw7r{xeL#&upm&%;pn4^|JqdTd@b z9fiFFK13TtxUU537M?o)jqM_bU{5!7cg`?bR>KjQzSQipEyhsUYyMo^4QKRBPRkW} z|7aN8&3-~1F&~*)N-pAg$ML-XZO>SOc2bPWV}X=1AZG*wpb@QDMWD1i-|KQ%O65Sb zJU9euq1g<>Vxq}yqPfWs-Ro+=*3N%Ra*W=NHSB1o`y@n;H|(8Znll*ib)LbX%@7le zTU0+8cWp)5YCWRODYwiudMtaE9qM~YuoiG5zHWrW^X5W*zx!B3v?0maa+K^>c(_XS zbm{asBo0TEbyHI6iX9nQR8Rmd!X-X89krU4tH`{gFr>$(aOy$Nzawc$B22{^RtKEV zfj>|)LK}~t5@VE31|`I2u$nlyil!e|RLb%&E77=9e0%_~9}o`mIyJWxQd1=QI9P=r0~3v|r66%)R6wcf*MLGz%`a zE-g-Wg>T>-2gPV}?Y{Fvt9tu+y~$rnZWqQ=iNeF0Y`H=*z0tnV%{q@NWV1SKF)Dy$ z^+~+vAh=>eg{`WjOWL)Q6-&65}fBzCc?E5YbI0dTPuM*wb_@Q zi+5!q#Rz(J<@VH*ruQ{`@B6WEqMl$yr~eOaUl|-{mMv*ZvY2Htqs0ssGcz-@#mtgL zC1z%3W|k~wW=4ydSzFybZ{E&EOz+#>w?C>P>R(kwetGkpdrl_Eg^{YV(J!ijG&N?1 zE=(IceOk)!r~EP#S^Y7W?F!nl+)pMFHBALXRw?0R*{3+Ha(RyP{^Z=mxLIi3%g@df z_tp|{)~#H%<`%hbpU1~9UlIOjmhgLK0=HN{eagrEJJqN3Uul*o{t;^HWch#6HEB>$ zSDlqZd@ro%15^(c?(`VUHxhhtK|?Cf*6S`PIKFyBobe!tCQGLd{g9YM(!#r4*WIR* zKPW8sOW3`h8XL>tWZGnl^O&XE;Y%ukQq}|MIXDjU5NJL$!a`hV^_&-QFx)kCgY8#B^gwhHpR6r7QQvPVmx*r8 z{|qV3J~&m8d!9P8p%yOg9FPdy3YbNzr&vX^UYOyQy{v@riR7x${cJ6{YAsi#!sfQLOp5ayRUtn*!8fOY!xC1MowUP0AQ z_AkP4Q4HeAZ%q!Q%Qc+r#9!obMH0k-?r$JbbaIIU&s3EBdoYUktb(AettV;%`^XKP}Py8hB}Cy zk3Ida$}s7o(qkueQ%B;A*z(vFJOOP&v8N=_13V@SKGhSYadm0LTacBH~lt<8zTi#D@<@DBIgewaLC4P z%ZcBOETS}~e=O$Qb!NU5T=bY$&W|Jmngyu$VgVysGJ*y0^$O@5hp@=;eE1ptPK`lJ zc!nOSXI7d0aG4btq96zCWqTzx<&PwjBp<1Kn=NWW{24ty-^BPssYX@;GU47k#@chW z`E<#dzM~F^xH=};#*!L#v-kP@>C57dcT=DYY@qc2Z39K?zwNP>1I~}z7?~UXYe%I) zWnB^IW%7|>6qjJkA0B`O6P&7n-1l=v;n$=>Mc>Z_zS4#j9cLSThGk>cwuK}f=jXMQ z`-Ujz<1{AHH`n5eciDu@HYj|if|@)*6Z;GH2hX<&_6I}V_SYM_Pbg|9d~{^6`B#~_ z8*KC;2wz!@^{|-0CwBdEX|b9EMl%k3Q9<(_<)BOwwunJh?WE=L%eLJ?#KzOUgSreSqHc*~bQ=XMY6`+D=>L$Ju4=Pl~9pElP z(IUTYvt8DqpBgwYz@}4rI8{tNEQ{+;mkg7_v&WSo)~in@2KbW>Ccx-hsXvE}Bt}!Z zB^Ij$T#$D*6qm$9y|odphf?mUY3f7mr=LjpG*TWr4X?Q9@K#v1@s&D{H_}%}qY&YE z2oD4_q!w1~i>XeHr&)CK3 z#dl0K?Xif?6F>MUaOXv*xnjSOfk(4lp+z1x8}Fd3vBq@E;~Jxcsm;hUT$Ri;`?qoM zFxeqwr45xNytwM^zzBOm-uGpp;y#4m2L-z`tSOIeggI3)sJJ z%(xR^y>Y|7Yl^$cbkeebm~8HGaKXP6qS0d({m?8u|C|X`1XyKXb`q#}->MKDj9f)( z`dukBajr^q!)O*KMq;eTvdz2p*+Is4*?{b;gAabRwo5Y(RcA3nYxg~tXMf;HBczc7 zlPZ1Rq%KyUjW!O2ljx;GqQWBR)NGzL!oJ}v{x1RxT*FduIPpxTrx-Z7cecLkA~wWH zQ{+MHDb{FLv=mt3NM=0UT+p;bvMorY$QubBjma&rjgXB=sjuJB@kn7(?3@n-cYXQJ0+?KkR|K!sHn?hwpy z+cyJSTf*Eq^pRPWjM+z|BKa+iNCvhqXEt}^jHOogH-;SUDd0yKn?@N%VF&h64ppoz z{F;=dEv9)_IJNR|hV4Ox?5^PjNw(m)AAizMagLSj9gu$Pfy(0F-x&V)sfz!Fc9bRm z(a0@VXC(myK|?|!#a%)YgmMXy`hk3=5%5hcGG;PHi`D*7$NwH5Im@jp`>@zC%Vg8p<@rSq(Lv|YR$c_IM<8@4I9F1H%{^d_iWt!O z<8hiU4-OQhndB!wW|Ez7Pjo;TG60ige9T0dxL+PoQI7vZm~Mjpk=k%v!u%U4H#Dxrx{m#f|ZrFwx_X4Q;F5YZryLj zoP}G*GgX7c@0KMEjUfe>U6Zl{_Yq)w^yF#lsSx&M07EG(G)YvFM0YozCRWG|7!`cC zAIz4*25;KIWJ%gJa=N&56ifh-qghkg$Y!8UR(yk6uO|(UMy8{L%^8tLzoTvvAf>wA zlUylnWG$-XOcBo}WjEiaiI6=aN*d~#Iz<&$Mii>>ri__9b(yWQ%CZE!_K zM$(76)|2n&U|=45Jp)2Fndv1ZXYp%@_K_qU;oiz1l!=j zi9Vb7)ZFa!IF(+qRa$t3(;$Y=GTfV&elGUA!;VRImHN499Aeus*HLhFcb4vv>pM7) zjBtqb?tAeeLnt(5-FptR>+v$%TFPk_+VpYi->bMeC-Cu z-e1BI0&pN;Ai2hejLy@lLGA+BVyUF-(sjHm4RB~);Hvc=*yl8Zm&_3?@UQSX?cbh2 zC!!a&zF+N9r+yYiCe+%C<*3+uv>Lv$zol4maE1OZA72U`jZD(2yG0_X`g!A}OL{L? zN&DwDScSuj-+EOOxfJ&o>>NbvATzpB^H)gBEXXIRC3ueNFg(w0#Ln2?@Y--~$%I}| znr)KhB6(R)SU*RL>;OUf#Ty zO1Vu{*VUN1J39=ton!r~&5xx4 zRZr1jy*gYIcPML5PWUo|Y>!vsw4FzJoioy{h({JT*huTqd)Tes1j67l#x#5+~BkuI9#Jxm6Q`!~T&i z4F#T{&9BU7jS!^VK~pT0_d`i9#A9x`v{D!(wTlxCUNi3-q`2d#c7-OZ_EMvhrs_qZ zweRsnSb}~5gNa$Uh!4qW)DefYnNqlz*bQ4_s@Trs~BAA^{f;judRJ3k8| zOsTIlaVf8f&@G-a{=zGz^hDTzb0D*SG2)es4~ftX$VfJ_61HGzj>Ws#F50}D6Bgx> zkO~X}B(*emH4QS1rr^!to17d=fjcOJHbcGGcN)GbG)B|$umLw~xEJv>qJtJqTm`uC zDZu;|kFQ@}v%c~-6%ZJJ(zdWX8Pl5bk*Du4YMmW z6*evjlP(ni+-|xd)8$1mvGIl3DSwKN=1XBB2@gyZxZ6}tOhwyZ3FMi%WolO zk#)|ts*fLMnLVswH|cX^jTM3ph^K!N*Qdns#et>_lp^N{whuRgQIZ0cvEIys3qS=R zHrv9Znw4nFnt__D&}1f?Ols(K#huJxba#Ix&pcdy0qksOv)s)kZgiTcoRlVZI?>Kr ztvl5mv!yP|=9RN}B#v1yv6G!gRt%U}1=L2T7`hbdEU7a!j){va`W0!yCvzHos}I{p zUc4^eY3^BNF`QCbCfkmH9|-3&qBCAO{SmqI>ME*gMEAv^0lOfsCo4etn^R-H!cRLd zjru{jYX1tvGv&Kzk?p=TG2dF%>2TMmmJ}rw*)opd7+CkvK5|d5G?CY1#K{PW9zj5Z zGmr0)7F7mR2I>ulgrh9@`?>|C2fyS6(i7WcW~?}lo}z@PI3Qi7LQ6Yhz;B>W%WAT~ zn26cep-{<$2sAYs>4SyBRCe&bFJ-beoktc1LG-@3qMP8Q+`?0n1cStCNIzAv*#(}? zJ$_bu1k(iP+r5RaqGF^)$*9qC%KrRp+xS?2VaQa;LQqS3%77Hpuy7;hMJh~#$q2Dv z!)_*WVYnJ+Ls4@=j8NpS7Kd6-6If@Q^&e9CsZ{jWKI+JdsJcWZU@E4F;V49v1j`-Z zu`18OQ47i?v_4P=$%}^M@7$X4gg5oXP~ylsz9$D9KcYwy#7avrGmO!SWK!$9U%qzf zMu?Q9rlx{Tl<|aOi;p`d^_RyKf^A%dFY-dTUP+qZ90s(2gz6^k@>!&69asDg%5eC3 z+_~tk`O5!da&i*VcWBVAz6U8n3_|hVV;CHyIksH=rC$i)|i@htircO z^x|KKFc4vkYs7GfcX4*epoL?oL;DSUpWrusT>B^wv;eJRet?!aExLeVzP&o-l>RxS^JyILat-H(ikMVrYoef4NM{hK-!p2n+= z*!_Bt2B1c1h))}1M}dCPXUs@D*k;rYp*qwY8&-CygLyFs(cByvhD{<*7-qclov}oX zc2{PNbl33lJ9^W|<5!;X{xe5>$-O2OZPj}_0n;G~b)^v0S~c;VeoA>7IUZxOc;SfR z1UvkDnDVhxrnFxaqL#H#)|O|VK;W33w}`5l33D@U3h}TRU6Z{Ekkk?svPpx$n?)+c z+E8Y>hgq!O`zqqjKZis*=kkfn93^JM!j-%d7Ib$c_3=KEI4v*x)y{#?oGvP}YFm7_ z1^h^6>w@a3hE)9^2aeT44&^dSIOUIqOX@efLYMLMhjss8UzM z1yp+U(_WuWO7G%w3gIAIXM6V}8eui-&Cz*80dvO>M;Letj&;AbC=&-s7cBNtT!+?G z?%_q#wA=bYZ4eqMr%+-}kp@X6CXAIMUq4^oRFx)&-}C9!RGF7fqgspKfznx)h zQ&zCn7Ab+J)YqL_7*rZOQGK=s#`~8ggS37lh@3fd?^)wkpS{@SR2yi6p_-hg3*wiC zJGe~I>S|t(>D){RyD@TTgk17zbxS8&p5Sz$gPMt%)mTqTnPTqg0bJ<^-~5J5~qbnvAC# zHRau~l2U5@s&GFpH!vOnJ^^}G1+R`J1seDAl#eInN9KVc{H_^dGESZT^+IsSkL6|4 zJZK)m73hi#a^nVDyGfQ!*rI5?D>t^gX@=Q-CUJ$lU+Zxs+NrUl2}dN%q>{~y1z;Ro zD=UuC^Gnu@lEr2igIAd(nD~yy49n^en5uCd0m}wyrn_Eq_i~dgw?(sc5s2A$hO3Fl z0@Hh+FbAR3#i#8+E8Zf=OlnwWyQxWOad1P#wlXa;#2%wVJToG22#xl&SU)d2cRbsb z56k#b?2R# zb6)xptoy41PgjaY;!MBu*m{6wx=WLsuE86PUlude#t)SC5ZD+=CuL~A6Z>R+@yHZ9 z*o@ENiqb4HLoTMEfjeYmhmZ8}clhU7 z(v`><(^GE8Uc&lY&C@~KW3(R?=!d!a_59IUbVUQ^AW=C^8ATgo1f+OOftF)E_naG^ zCGR{AsC0}a)y<~jH3xG32Q~0?Q^UK$rh^sT0!(qX`>R{Ut=%e4Jdxjyvzn3u&?m5b zD!4Zkrm*h5U^z9JkCZRUlWgNbJ#+$;8&h?`F5q9zEZ5nbD9uLg&qNny+NDaiP~*4e znU8+0LBX_t?S5Frh`5jZWs%9`W=QF51P;q8r z#PV7&9yRt`7%W*F8_y|F+DPYtJO$Zu;GVIqBj8v0*@mb|3F0yI^GLjqu5x2j9X(IW zaI+|72|JO_XAC|6X(N$?)?aY~^Xbzl&}H-A7w+Q!mAXR7%-&evNJQTdI6vw3FO!r1 zTCG$qz8C{*b07lXgvJGVNr)8F$%Ff4aIkVf*w@o%j>*1%SXMALAp zhPtD?&mIeQPr1Cfxgz%$B0}?~L2@PQ6Sj3@%gu-jJRGmYw=D@78q=3;6O^kKpW4NPx8k68WrDy;^F3@A5j z6ZN)Y6>7<<4(vKZOE-jswO9?_yz#xyixT~6&Co*Nd{QlC!D&`fJohweERRQ$Mq_UW z8X5sFV*~kDso@#Wl6Xt-=VRyyae<@lVGofLjB#;d9}p8yKZEjg0%x2rGI1Hfjf>I0 zIG(VZfu4X!;<5V{e0#%GUvu*T$4Fg8>LON{_}x6j-k~si{hfLN+X(s29n*>=JOtXf z0S8_9C~+d`;5Ge3(qR~+FY!|}JxvTjjEV9&!B)rnjI1>%M{`Fc6o*IY1vvudrO9zn zW15nxu#i`-uU|VkBUQ;=hM4^Zzk?|Eh&6RIF67l@YzIk(b76 z)xztQQ$=W|$b6$e-4*yLWBGy$7W2uMH`1nAsI}5HFRQ)yzn)7%QHuKf^vTaV7K zuHR1gZgIFUGRZXEpI963`gprT_?h-d7)g@YS4+KJ=|?WXs1oq1MLr5GQcW3&T2oGu z-&V9mDY3IiM^ZkDp<3-LU9*NhLW9ZHw=Ic@38hQX9B}qI|FXdk;!&ihiO5L2M-VEC z#B-2dtM(*wwYvH2>e#)vu>m)ZshD(i-+FrP*h#0z&{*H;O5X0w+t|S_gHU^8)_d+6 zZj%0{)$jyvk@TZ|pFJ_>0NqpG`E&)xU+Xtnl{sEN4$Q;Knp2C4_Kf4CT~=&R{%pz+ zJ;`XRVT`%*>bz8DQ}Y3qJ9pOz_c^p6&?Uh+f)LeO%@NC4uQa+EmJ@Q7*JcUel9uBs zf;Y*;5eGKxq{CF~DSN=E30sq^Sinpd50|EjwE*wJ=5IhY5?Ps|nQ9U2Ac=Y73|DGN z$mRJ^kxm_b+9-!P;6XHac{y*41;)tImbjUXe;E6=W;K4z#C=C4xVA3~LU5c86!&&* zBTd$T2?+?ZB{QGXfj09xw%IiRf~hrfgrsdw{BEfZt6+G-jt4f zgT4T4kI2FO@wojj^q&epHNy8jN3l#39p-Dm?X4O z){--7v9uEX_AA|w6lE_G=#30@v6BvrEd6eex|v7TZmJw^5Uzhn;uoNufX6M^kB3)= zfvzQ`2HSr#Z$^DOj6BpB`f%k7CfeUAw&QM7^|g2I36d*mb!7r?ksDw^hfjYjJ6*+k zXG)99C|aac!HHC1y>v?rO$+1fl;ZJzS2cx_qT?2mcFOJ>YC`U{5=|}Qk?aQ0By_~T zjr~k`tDfs}Q~)FW)KAxrH-^?Lbup3Qrp5Dc8^|NWY)ouAo8L4!vo^>KEB?$l3!59o zQ^SmP8z>treo4yGMrVHSnQ}GxW|Y+rRuShobBIIwY+$f?y=OA+EA)nT&y0Pax*SvpRti!Zwc~Ag15bU7;j*1BW9TiC%|7R$Q@}G$S@8FPeqqa11 z`~kV70csKG@fve}T9iWtU%(oaIZ04(#7*c^^v>SAbG&YIfL>S@S*=k(*L>Y^R&SQ< z(MCHDwXG_1B@U-YGj>o;w6K3`wY4do>NV>NwTuNxj_pT$t0yQAy#r@W@b`Qj87mxo>{J3$*cNf#kSC0F=$&LnE$Br-?u!f|GT zEY?|~E5|fkXu;OrgfR<4Hxm;RxdA0JHI@6=!8c#KI(UW6OL21w<;&pMWorWUo;~0< ziR0iz>TqAZRYhdJf=DDmPJ7-#x-iq|YIk_%w=^w9L=U20!IEGmVwNVBEi>vH%knXy zO-0h9juVu{k#}?y#h49>%**sv8UvPuZ3cU2cL}hvsYPe`nd&nRg{N=5SOxt z+cmXTLs|RohSYfn#|AQ1QqKNdQ25;psYxtx;;cmn7uO%Plj(tRKfdSbSYhy5to8T>TH$R$7*utq_F&yMTG{rN)QWqsztO=^4c0K{_f*mb*#;OkM>7 z=&lj~cGD|;i_jdDY;cMP&+*e0^L=Y0(!|3cPAUsY0ZBpFcK|*~O!JOtyH_jK@{eJ~ zX&t*?%YyeZ;vH|D5_<0*a#^$u;VwB89c|t1ObHdWv&9r5ikfP&ZW*GVt!93;W-^JC z&-tB#)|8aIQ!)PB$nYeT6dhTR--r%7{l21xfuE6vtGOkV=p*8+6vXAz9toTR8KlQVgiy)n>M3-Z9S;W%?Et*_V5MVf2Pihl8GgBf>tG$Qrm(;pZ1mmj=PWDI_nbq}wEIDVS925Y#oMVjQoGxcM@Sw=Or zqL7&=jEW`|Xn5@Vu6bCype;mY{CRm2kQfvf47B@4GCk%U8i?}_GbnrA=+j2* zO-H=2d98#F*kpZe5QbBH1#)LguxZd6SmwzQD>$f?FT|3=gQ(V_U5n;GHl_Qz)eoez zP5ocoWFqshfV=J?S%eR?gEH*{*;bPv^#`PdZ5)l1 z8`^!gfusX4vcny!?~@leWHkX4TEBTB)Y+ zb27=w2NO?E)V$+-KG^V#{z3NkGYz>Z)u(AZ)x4w=r29#6fJ2D!hk4L_LTA=d-{{xa!1-4v5#m55=ur- ziU|V_&wOXwR(v*M7B}Dtb8#%5cc>NWUoC>JXZ@4^!-qJ$}y@BCytr0rAJ+suMJlpAZOaCriqKRNYfGDZ3lvq z>RPaR#tPA`X8qYy0`39@_fLapne11&3s80qJC?JSN2rP>$pQkN1e?^Ro|^dyl=hV5 z<0gw2A)S~% z{LjfvQ}iO>WZ;l`Mtb0}Qn1v2-d2b6VnP}LzmPrf_3x{Of9stHJgR|TncorkZs6qj zkA1+1_!E^sV|)Z@9gqR^;XhHY6bh2FF{*_i10;WDn|(Ur`|(4QBLGRjp@uMLLFe>s zhxiuhRmo7Z?es@dmRA6e0Ddko$p=R}+w&*F%g0B?D9_8-^4_wK>^nGpz{_XX8mvAD zTSu+w8niy)z3=5W;Z*e0wp3!y{%(o{w9%$*r1X@RgsXnPlKEn+#0d!HOguF8w6=&F zQGKC&ts0em#eK0>aeXO#qn0<2#GpNU?kj)Q|0=TNMM*|_Og}>hsRMaf@}`JJehfWZ z1aSb_tbS7&biv|+8OFwPpKY;cNTsUcOq1!3KwJMonzb59D<-W%Q8{0Fo(z5=K-n_X zwombV_Ir6*32>=l`E%Dc+^`)<)F^xwFO;M+r68f-81A`e>-cWZK2%$Zy@vjA4g3-8 zcZX6;S+|4@JEu(jRy=Di>%78Bw#03w6k|thElYZf+tl$Jr%cYiwMB3MF~r7RMvQQM z;SH%vjf$27)#!wVuZUxc)lni~`mzjF1U_|BBFRC9a98O%UFG+EVmfg1KZfpi846a< zOFDhmWX>(d#<59eB}VlPHffPYnOth*+i_mOGeFa7t&$sO`GPo1s4|=KGQe$cs8YE^ zp(6SF63sVq)hXIVN_H38$p;G`^L{rO3%$&byh@BmJYKUycCjCzl8@6rBeGz%dW=i! z4%hT`Sm|Mp`E911B*1r0x%;`6L$=EBS! zcz{j>7xwkdenR#n$yT}oUmy+#PGh5!cgq8suF7Vm?wux7#1OUeW|>)5TPTJ}3lP)^ zRdL9<`iAk|`M#OW>ls5VRL6&I%pPjuII5MwJcy~f=e49AtIlY@xav3qXQb>UufVgs z_&{}=B*|G*#+)Vb?GoOamDK{lEZ!#T31y?4luKoc!7plxdiqj>23ZhU(<_mpMmqO* z6TG-((o&9Q1~P)Ar?i?ViL*G@kLL>O;Xx97Hm8$-y|`Fiwm>I|Yx7}llV9$F_EmdK zCR5Ookz(geX%%&`lj7%Wa3}41#e1LY`my3Y`LEMVhOlCBm^43fkz{elofkAkhbEqn z&FbpjIabcfbq4i0sg4h8>&W16iF0b=NSIwyN#sQ7pc-TY@>?PuNJMbuV-q=OqXUkE zm!q<|J*U-X>3=8L6fAJaNYBX1W@Njxgrd1eAU2xq(^j-ZvA#;dy?zS11UqvDz`qKf zUD9=!#<{<{TxHHZd_ep`tPPOGZ8E@uLiKM)`o8@=u>zyz{#jRKC~GSIF?=hdsYzZk zB`_U|9$jjsJR&XEZGi-gBbuAW+>n{|hszh2RuFlQL*STr{N>O4>xzy2TpYy8y!=qm z`Qdc=Vl;D|dpJrrX!A3*0hMrRG)Np#fB;St1F^7t=)@s=@q zZIy8mc@~TI2`70=$iW$-kaD_@9WjR}WY1}tuR^`gB--%?4olGnBeUbO_=lAVV`g|BHyg9|| zD|Vm|O0%}jCQGpX&SgK^YG7-?UFKQ|E*dS6^3)LofSX}KQ^nyd71sGtX`O9_0H zuX&;g5l~_I^^>1ZT&GS}?mV>DQA<$r`oM08(9DrEwka)u#;cQcLV*zrO2C!`oB;VM(a1P6fTB)Immt;wZ01RyRe;S zr~n1zhlGe2lZgB#D%2Pn_9SdC)7EvHmY9?bjrW3y;4sUu)8+xUFyWH$@_ zSiRL*#!tIgr?JznGKBz;(I_82+dKnZxQX!VnWe0?(Q`YW5(D>HvVHVEgn2T_z ze2;%-N+Ar74sSgNizPv&1EUCU1vB)$l%wNX{v!KwdBx}2jGlS!mjxWc%EONREV5&t z?3v0U5J-A#vW-oY1)@&Y0U6cbKpWeyb%IPwEEKNlhdG&UB<0Ld_(CrO&LkqsNY;v> z=!J1~{}?WrFBn^N&}WxqwYKWSzNHcC&-0@>)I7^@B%-JgN5S_B6;yXk{U{%r_{6VD ze^#4`Yf#cs4r}rECJNG8b~4WOt?R+3Td-K-271zEb5AC=oU!jM@zP!r*1`XfDv*URuxsLykvDok zeT~wRQK(GDpDEu$;JPT|p4CM+d6iYXmSvFfOnMge2R?KjB`n~{w>*I&JYcV?%>a<% zxqacTTtxgvAT7RaB{s=c(2v@gl``LhW?X`6FMAirDD^RVk3=MoE^Ypdn?zmIIunGe z&!&WMxf0o8iq%MwHc)qlYyDI->>^gT!29Tg>b-u{jOIhFR4FPOOGAlXEEkn;%$XT2 z?Y?f0g+<9>y&D+`J8^;`q9KY##X)PBs$ZKW4(H;e)y*B4kv)M^TnL-8p+z4rn*j^> zJq?XgiocZT_I{zp}!2a2M03Ck7NjF4_eY6(4&jl=;0C9 zr)FCo)l2rz(`9>*n)}dXzW!<0@`%u>UHe8UF*(|FBxJcFHq!fTSD| z#;AqM|4Duf8G;x=qKIPp$oFc$z_L!LEx7`}i%-PBAcE3;2(fGG6Mx06Eu753x^kViCq=DG{VNCIXHln@X|n?N}WkFo!dB(c;(LF z+RA=q^otxuoId^7y^&Q)kJ!qZZH1bB`+0)v>@S4%$(r7>K zh$52_{t2{k@?+MWp-?TQRN4y-5YnqN5sB5uwJ>8Nw3`R1{Z@(|I;$*>U0LBl%oe7B zU9>22P8yZgTxg7&f<;GY`6x=|qK($Ids} zb;*N1FKGt6AahH+Eh`#h)@D5EsU33?!j8T*(eyeZNGMGVTp(g%1)|)QAy2SQKJ=%r z|C;uDEobx@`yvX_YrB1cMtQCmE0a%Bd>U+2SL+Cbv?#}z!R;QskPY4R4-_OJ^NIL< z_-=`tpx}5O;%6YFVP}&G2yA~MVTpQlbbQ;#+%g z5HrI_hLE&ANAgWFR6ZL+7{5k@LZi}30m`-MRHs6HSB%&j$RFxQ=aIq89SCWf{}xCy z{R7fU+BP#l_b_2@l>!>3_iuE} zc1-U2?V7dNA2C=rB!>#diNw}U0UQ3jKP*0tY#}TS`Sk21Y1nL
= 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 z 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 + + modifier-extension + 1.0-SNAPSHOT + Modifier Extension + HiveMQ GmbH + 1000 + diff --git a/modules/hivemq/src/test/resources/modifier-extension/modifier-extension-1.0-SNAPSHOT.jar b/modules/hivemq/src/test/resources/modifier-extension/modifier-extension-1.0-SNAPSHOT.jar new file mode 100644 index 0000000000000000000000000000000000000000..18a7ea85f53dcf856d5280676386448b43d2d537 GIT binary patch literal 102179 zcmbTd19&A{vp*c$wrx9^*tX4yZQHhOn-kl%J#jL@B$NCzufKEeIp4i^KYKmT?%lnr zt5)gPRjcKtfI(0IARr(Bg1ubj0si9y2>=2hBcd!oDn zCjY0CQ9iHu*JK$1SqV`QC1pAp(R-Q6327->x;a=WTB_;EnMOs1MdsZj2O7!gQ5q>) zK?v}}BGn``>RuAJ_N+)nWGO`@r);Wf6dY)zL}o=)FXRcudz7djlMhj!=OvjPC3_o^ zkIE$oa0%j3bZV2V@<>HJz&t{_w7azb#_c-!{JYgBFaDq7-`s}$LDb*enp?P-SliQ@ zxH*~FI9k}+(79P#nJDVmW-_37pQvr-^I9oBi|1A(ml#@f6sQP8jaBe**#S!FBX1-L zFFbB|zKAcCbz$r{dR?`Tz+J^SgVvU@r0y^0boRhfD5l#hY_4}+20B^~Z1qF+aKwOq z7*J*=?u%ILj_2wLrEvY6WeUEj-2Q@;f6|LAr(iyrhl)wXKq|Oemy`mT-j`cXtliG% z^&RpNWNpV5EIQFD-;^9-p1(3)xGf}04Z=;jp0O9SsKJJ6S~z0XUI{Lm4AT|CJze;4 zn_-`sYi^eAV-&=6x@V-8rQ~JLU1z1;%I;E9#cgSHd0VMTduVK&@Z`jiTOofr?`GCP z#OPYC7t0}~f%m0a9b6HR8U@MJ__|AJQ@)>hmWdtri2c-98++)fx;^abBJub`JntqA zfLoeC(&`zLOo0hoIj8XQz@s(z#I6zYz`99NzuW(at0%T)j;mw~IOrDoEoT1}lK@mK z`ewDv2aIX_Rh|(WSsH;}`p|);7+czXS}8*og_artN}^VLR(>|GqW!8?Epf!F+Mt9xVkm=?(RJV4#?g$v$1xyC6^wZO^;O_)S(Z+4wsso{FfsM|+T*FG!9h zu}~crq|b`eD^PvNPjQ24%Hj@!Xwm|iRdFknR42#Mq0OgXy`DRo{!eQ^v47k6JYDu@|iNiM;oi) zc8QR|3unk#dN{5>8pCE^6{eMODfEGEvyWWCm;rYP#?&ZnKnM073yo#d=(L;2`b^L0 zG=>}vlZbKz1qK+?A~M~Ok9yOS$8v5`et68@GQLys7d$!fd&C1qjLBULWonV#5VPKD zj+2#-sUB?wOH^iC4tgd<@DWZ?TG6G)5rCm@9l(vcce)Jl9 zM`R{z^a!5@-V3WaHT~uV1BID!I}>t7#tT%fHlwzVLUT+}sG%y-?&PLy=ZKc{mN7PR zsGb8RV=Tvsh-$}Vage%i6Do1|Iuln%r%6$Ri(I}xUw_bT-B0?DH(Wd z#e8J*t$%O^Q{BRbbIFE0s_RMFsxQ8Nm7BqiXS!&0vRvTYElLG8C5FceHYNrr$K1}@El&sE+66_)j!J%D|FMBF)0@U22+KCDB6>UT zMgc`3!jL@&DbXkt6Y7450a?!Jk)MiH;q_w?XAv$}p^q9jVDBX6v5U<8>knn#1*{1{ z4uUmR)^7KD8i^817^SK678RZ|53-0;QF6D_`o|!>R)dfYnTT1r|NT6LhkPB zJ%A428>NP;{MS)xEdApL>Wq=2Q7K_jqVK0?g28qWh#fs*g9j*Mm<0?#chDvQaoW+M zb_{kL0j>67v)w_y;Gy9i_>4Gx3Wiw4KLng8M;Z0;p>5n!eL6cKylBE2y>JI^8P;P@ zSE`S{wz?CB9h$q_6C;VvbE8DM|Lqc4NQ4=I~4P8>x2Sw6=qdD{H(Hyq%MJZZuM z3II@m`2QEt|Akr{O&nY-j7%K=GNS(xN$`9|^#4l1$kv+f@A!i5Ux|Xw(b$sCz|MkB zv=YTRbIW0Mc+2OuAi;S z!4wA0emjMgTFwH?7fwAvr;oqJ+$S8$%4@wps}?ja<35j9N5C(xm*Yqv6zB%=yrKWX zDTLo7?qrcuxPy|HiSO+D!F*6}N||xM8j&tsPS6_!#ZrhUV0;uWOoT-oJCzu=ze;Q_ zVUh+pzLtiWIKzp|mW*+OL4l+`buJ!cQr`9sT`XNgW7)2q!J)Ka%u`1Mi@vj$FpQ{6G>izx;m(qk*b2C=oInbkU=JdMn^d}6W9WDe>We_zsze2^Pq}QcTYFr2sb4fB1<|vU zxOVJt=WG6tejc@#48mH~fE07o_M>|kS9c#D18=PDc7Bc#u;^grm-)-QeRzmkVHb~) zF6nP*igSE}lFuFsqjqFfow8(;In24j?%UjJ<<_i^Tv+IKDP+Jq{ueFPhWW7!sZJrh zkl6f?+vJ-B)R1@}9vc<7mR`E6aEr}*F&@{O8?WKkv>A832MF{M;dYX>tE6ho!;bqf zjuvHl5*vOO1w-X9n64St1+wYDRGKtAlHSJ>!&8#4UnZsQfi5=e)#DqK$;w&0$Foa! zN-%!HAMIFIw&)muO0|ng?R8(hufm)KOyaW%>TniCl!K`PvIl)QbDxqP6-YOqP%X9Knp#(=ZIrLE$yg{KSd%MY z@#9*HFLX{#g_O(VisYYGjJ;eidQtB*7AWH4_W*9r4!%~~y6v{VIFy1*=8f!Q!Ybw@ ze>}Q}%t7vHYooN+gOfADhfUhO%vifG1Nz75a%;bczoQa9j+IY$%Qv_0v>S_bv3!5^ zGdV11P@)b8N6rp~DAO#{YZ6#*FWzXTsfNEI! z6jA+{v0B7mk^DSE3%wPpVir+_t+El!io+w03<~0U+eU0Llz!>lp?C-weP?KPd1XBk zCY|W8pW5W%o|G8rnJ(`r@tL`X9a?IpozV__>*rE@TKc@VjuEx>_f=w{%E{d9br#0+ z^8K`W46E61u?ynII+|i9^=O(rHfGc=!uEkRTPaHu5gzqb-yjaPu&wfcj##|KkKdq$ z{Mh@l@k{?6O_y=g8GDC=cadP7Pne~w1)p@?bvt3K;1R{eJ#Q}sWCZJ)txl!}jST0i zkMzB1u872PE)08Bv3$U~t%!3uMrs$*y1fXpu_%b|a1(|P_SFmmDaNjR`;>DYGxDuK zDnHKd4&>)oiAC8kxe^NoV}`lTU$>#}>aa{hve2(`FPk`2@3?v1L(Ptp<+)bQu~Vfi zm2+_8#U36LXy>GxXK&!YpWGnjA-GRI^L#Ppe`dx%W@(oHRR*zhu(dOBaI!FQRC1A< zlA@KQk&sakuTZ{`p^=(8P<))40cMIsiHbsnk*SWFs)B(<&C(GT8v0o?Ao7vtl?8GrKaYT;qvU`%Ii;^bgq^w&`4kNKbJe@gTp z#A%Ie9Zdc*kw>PGN`Dgl*N6Y}*B{`)f6w;+=MKM<`oC2|J1zy)&wv0h>n+PT+bo1l zW1df~FxQNpMz+8;OJ$&+7XrO3df@w~oI4L0HcJv#_5CCOA>fHj^J===vRn_zH~tGQWX= zQ#oRhN|pLYtOxFOi~-WBKqvI-uLoR$)$oEc?b)7}sjk+}xSi|_ERQ??q059!r$hiw z;uM{BDW6|mdcKlZ5h~jr?j*I z&w}s+ad7x#S1yh1WA7LEKITS&mM(+^%tbo>tp-T=gZf#5zgA$jiDmny_dWI%b;yUH z9>%cil|OQxbflMfJd3MMYeeMerezZ4JmR={~Q%# zQ5Z7&$_qc$h+a&+^9{l|(`>>aCXv=%^?HhJ|LpQ`(?Dp*$8`68POQD=+ui;& ztJ>m0BPqQOCF+Dtw;*bUDHfQ(YX~p@I8e+tB;Qa;V2l@(Lkc{5VOM=cIT5tmhOV;g znnKbL2NC|~5~BJXoC`YiuNx~f(TxYgQq37GaTzVlQT$G<0`Xsih=&lsIe|6#gfQ9U zVW@rnR2Ld=>MP#DZ0dSEQ7qLQyvuX$(+Y077F5IjV~&xnv4OdP)nB{ZADtTYkCXo|k>NM2|IPKkIQSUQltSRlA>iJ_-1ug*439?m?) zzJ2`qZVRwIl&aAc`qYTg2dNJ*}njT|M-n3Reg3kNl_oAnxeFiTcY6~a}cEhwE}=3hv(TNWu- z);vzx{0Wx2jfH`A6G6t^)1%=HE4Kll!R3NS`m~;Foe_(HNYyaT7(p=rP z>vZm(i`exNYh89;LH+`V)mPG5&EPJ+v%cnp-RivRBrzEEDvuFC3#c-RJ0NLb_2r>e z>!WW5CsL~f44S51o#QJ@Es?aSXzO;WPm?^4_?vP*hD44{hd`El1Dg5CoK80?^R$ia zPlHMY6I!@3c`pJiq>#J8GAP&aAML3V^j$byZN=W+W|lsy&YM~}ez6ybrm7X`W$Mo; zD$wW(Nh3PYwFb%tKbK@ntuz{MxQ3-S8*ecATJ6wm)hYI@q1kHGIYRHzdibXVb?}}9 zDDOvPqLuZmRQKNYw(uSuntf@jLtdldv;;0<96iLgm8L~(1oMd|hKOTBa4dagT;DF= z!M5cR7iv9$2Du&*AKVeb+KM}`97+|Czo?WcN5Drx`l!nWwnBsXF6Q8uGZUFNDN5B) z6A?LX7iIT?`gp>e^G%D##MctzkFI~?KdQMRx<&3LxkEDIYvUgHh1_%(CFO*q#BZ!a z6cAB0k#@X+xvq+nF6>{ak7`tP9hRKH^+=cSn2d|@=vdSlEjPxqYok z;Fio;BgbgC#0zoJU;8rflkr@lEjwbd4GejOF$x>8lyBI4(!x|vcrh)SOKAXjH7M(c zdQK?~>E$%i$m!l8l3HV^5;b!BwJ$%_i}YJKPA=xwzzuRyl$qSeixIzh)~i2f5#x-J zLWvtYJKI>A7z;Q$+8S9HIDNKHq-|}?1RNX;+({V! z?*7S9GQX2^@XoVZbpUSw^or;ZbQlFh1hgE&LVolxNCK4`D>`1UP&ndK0Q00LjF;EU z$}d3u5SsuJfzY_Cts!b%i-DlNg05BMlY4}$`G$G5Z4Rx;?$p*f1zpnoZ%yNEdDitu z6;AE}Nen7iG)WUCl(3P^YPyvKKBFPXg0|MQufg$zb_B-w-x%i63|%=IDj*Jgo7Vt> zuzHkYBcEAkqqg{|O}TZkFnr#ZFDOY7Au}B6GD(GpqV5?JTHEbZ= zFu&v>3X+78>EgY~52tTQLR*Aa(>rpWOxn_MU)Jt)z5+Nj z_YxJ>v9v|lZkJq)TSLPf<$TQ&KmP<9^O^HX=!_RZjj~yZGG&$I@h-#j3wAi(X3}g$ zcjzohemblWQ#7gw6MIl((&ml~#Yz(siE{IDm1@)TisS}-%Oi`4ii!@!g_UN-CG{*e z8576Vo(Uz5@%0STBuL!c^u3PFMWP=9upXCSq&!6+w}wxYOzy|+Z1XfsK<&B+EV3dh ztP@z%MU6m-x4K%iWDzo|DH^`nbE@R2vLA9OAhx27hK5I;X1G)wTbg!}%)`^lGZI;` zSL^1OeQnXXmCG_Jo)!0xBC&f>G}PZCqLAZEg6tJUzi5#&5)h9X#)&|bs!L0`kuYp) zl>-ULCRb1l8=k-PH^%tbN$11;F99D&oqnB9TRDc*X<^};aV|g zW^iK!)Jp(Hk5RZ)2oBZk45ejgX;$qBetxtXxlf8KVY7fn>qf*>0;8QO z0xgBVF>n&m6r%3C==Fx7@ri$6{%&U55?R}Tatzpx4fdu1l31YFI+*L}ySW|Indl8N z*N^pq$U&D~E*l{@_?b0MNKDU@x6u~d!sk%N`CnzUjvTi1J&`^ZN*rR9_(xU(ANaqW zP2qr$9{4i`F@E-y|J>Qw{twRfcQ&o;w<@^YzeY!heV%X2Ch-#Y>@i)&Zh{#2C_6h%9+mlWhVjR;kBx z5~E9lymUv&MaEW)FTcNp8Hk6VzdeKXP}1z&8R6?L-dcaDsMiTeHO6?{GkNLRAE>66j^y*oAn0dm1zM? zmF7XD`bi?#nvAtdmbQ%qg$|x#;*`1+_{S&p@?=xFENT(lu`oZ`>$i_vV1gE>E10h7B`fAQDGr>3&9apIbqedeQTD~oOn6a zzy}Dl$(>?YB~E831N38Z8-{(UeChC(H=R`pR!xw`3AQgHHqJEo2mvt<3@WzLZ?QqDO0 zw@vGYcnzc7l+;uGr0FPHC5*@p94>R|gJ6PrLoBDff%R^Sk`@p-gAT>rq-*?Iil$@| zEf&b<2D#1XG8VMTnl)uKPgGGY_9P~t#sl$xJ>G^6fZNu zr=G|8nDa@Rvz#5uywVV*T+@)1oz>5rR9&2HX%JOPC`MEtWSe?9`;~8(sx+|R=##}g zr4@SL#O3@3#Kf}8Tfdk_J~G0Ov}zIAms-DcCIps;HFJ=W%DZEsAB(%OvgDM12TRcp zhG}wMFQ)?nu5O#>=SIVB1DXQsiEL#}$|<>EWy#h77>v5ceCFF`y8-Sp+SS4I344O+ zqHm0rr@_Z~M)avVAP4iIs`@f81N$ZzqBnk{p>4UV_B`7Qr2Um3cmT{NdI#f~c6<4j z8~ED@KFk~9_1SZx|Ca-LtP)>I6U=7_-{d{pu4Z)A@r&!MC3>bCcoLXRvW73Q^cZb` zmN0L~2EclQxL_?~>z5GUpHuyP58$?!I8muR*fbGUe|0g7TF`wzJY+KMhaowzA6rM( z3a^e>>c}JWI#yv^u}Q_I8UK+P4?&&pVRO*sNLgREIM`O@PGp&twzKL+7ocK`m+D*( zL07|x32_vQ8SOJpH{m-DSql%i(!jR1d(3anQz~`J0fR-p&gSFR{?xtaMXY%)e zixW{f8iSG+ms_TYhx`QiyB9BiPevh47&GQNgeh%nf1VY3;%;*wZ_bD!M+aS!?xgS5 zUw=`}TUBcO{EBijAv>8|oW)vR`ehdy_1FN#ou0bJ1n1Wq(9tX-GHwVui_W;Pb`B(7yyww8V7C#axj=lyE;}J`_}{?o+S$PbZ#fSLjld~HXLP#@M;+HFeaPS^L_&->;B#euLlla%iUIx*0Kx#Q9~x zrLqiRuHA>Dw+EaPxDGd1!dovTn3J9ywK^YE-yRusm0@?IdjC+!5oYCBRYmq1Y^Hp?N*&Xt~p z;Svre-;LWV>Vso~o{FkTp+!$~fk!rU*i)pWE+dC~02+Ev%+|+BIs!IFy2GmMj1uWi zM6`z|H_&Mjqk;&+q-t9`!ORegX=B=`32%=uSbeW>E5U~(cdOgLHCw^_X!3D3y_wL06n`(c1E1db z5J)|go}uRU*swQxUr(sIS31CsQ17??7BBonSMC`e{yO|aPfzfxVWBpB_ydKDK}WZs zmdimtMPfG?@LLXFh>PJ+H_6~z&5&}a+`cK8oK$UeCWa~P&gci#_p^$b7}xrNwxu(} z&~$|Fi>P;i?hDc(y633`o+@$0@f)7$7rG&ym6v!PC@)dXTemTJG}jbw$fu^^ua|3D zK7ii}7Hq*o;a8BaU(C|bXJp$>eys@}$-#?BMx9DlmX(n{zh(A-2fu+BugkaJOPpR2 zw&}D_@2V8!%(wvIO#vzFZAvjE4%?j6qu1nphiboQHCeK8SX4b)lyzPlPgZVsd@7~z zF$mbNsQTOY05x?z;$>r8p)0jb>Ot_x#8ZDUg*B<|tf%gp}ExXB<@$F0XTY(iimz$cVO9H~3s2mY9oG>}4rjW642m^-U9Iaj0l(Q_qx5 zjTmLpEZ}8m62eE5F4ce%LuyiRYQP# zPeIaTL}3BUEIjF>;zjph@P2$?w=ogc&O_ z7@N4Qqs~UV&QO+Mx9*#QbA_SLMHMRS*<&(Zr`wCle~_55ZY0MAyXs)QiAB8w%^BQM0KQ(%m7Ff~m(D+K4&i)VCH_*OJN_hG*tJxPFc1_58Shg$M974@v9u2!ri{+bcxdmGSfNlM(z;sPfV-tGr@- zcg(>IoQzzM$fDxD0%4g9TPKp{T}1p0&b2f|XwcE@DR9%V#*awFn_cl#STv(K$G)j? zVWOA@9dD^7m!WH4O`E@*sDU(LMNkv0U|$fSErp$m{0P~XOf}t%Jvdkd(O-BY z8a0N~gYL(dxTh)bN@)kDsWSMeF>~sE?lg6owQwpmkWyJAacrGg!>=Lb^uCNK@A_^= zV7i8w)X28gUP>ON-%>Vrwz(2jrM9tj?Q{aO~9Os-2fW6gDpn2hZoYZlchk zi$gMBel6Fjfih8F-h5UBe<4nzgBYl8zEBel?)8uJ&YGw2OnFrr>L8T!QXN!Lat!z~- z*9B38_oPfiVuB))&1(`2a0ZKZ6hVpb>{%2ca~yLCEyba25~!A>aYce|f$P?j$98Yq z8GpRaho+MU@CUIUPNt+$$Wq};soC)^kC|)fzf7NUeLvo?dojJRM`Wdk>Y-Z=i?(9+ zWV(jdvpGs`*(|RIZ|SZY*pNp&XEidT31fjQ1DZLPkB!v*`qqiypWG;h6#~XGiVZ1a zbh~Q%RTWfcEh60uFIK6cem1yQCXH)>dJE7;C`)j(#M777NO7KJ!cv~rX`78XqdXX+ z|1jY-Q7_MIG2r4HLGuWQjZn=&KymT}t1&`R(s+qA-MDA*mh0QrceThNZIm6M2)CFZ zNxO7rP?3s?VCn|NbrhXLYzZL#ZtWqB zRZo4(f2cE309vlE*8Ubi(I%pR@q^Z}UzhC;$IRBf&lzgj`+jXPDBZD&6m+~PARDx> zYG$5m<^VNxSYriIIiuP+$02|naRT$cqq0127@{9d6*>%be4(rCQEQO-T4vI z^@Xzd8EvB#>QIq-gVp$}f-h$ge#5$#z7)`f_agW@#L!afQGmy%w)*oPNBZ>sWeeoX}m#;yn9OUr8H9KMAme8 z-1^cg?wUyJHPOKn$QfBH9rl|l!rGFZA#5Zu@k5R2>i9f!$`tv#8FUzq;44k#CPRi6 zS5bh+#nkb-lw%*vINRdgE(?4hie%$N8R8+$$~1HZ!49bYt|qos95G2^vF|m0J6f@(mW}=_yU{hQZiF?k(~4S z9qnV3CNho_XNdOX24YDTgWDfF%`Mmp6(_-oh>jgvMK705nm|_{TYJ2rwHCI`AMIZr zdS7aTu7+cTqBM@$u7>JtxA+9nRc%;!?THf$Icj3f8V{Ohgs@$}B*&AjQnrO>bc(7C z;x3p`00C9F(n$eXgA)?&-pZn6>U_AFT5EqF!t z5_%k76|nyxb7D@GurxrEk^o6bXrm^bQG z9qgBT_&ZNYEn33bp;8kGc!jyldq@k_dZKS$Ayl%k87d>vEfw2bmg)h~JoHTQ~p?QOmq!xfTCgd{1x)c?*;h3>hQZWHV3ZoS8zs-t{IySzT&270xOv~U|2z< zV)cT#30d|zfUuzg4P%P>@!VMBvyX$HIi z2YO`$soDYO77Zl_u+jovl>>QnguqBl=#$-N%`mECKkxG(QMbfhr3ZPmgh(4Xw4Y~9 z*v?&GwfQEy&bl9teh0osT`ox8BZyLw zC*~f3JL9@aDBzO{=q@|s0qE=x;XUA*Lik;^MHY@H_|AChe8Ay{z)V3)Hk`D$V57^z z%y^M@dZ7!z*C5(O6wh+rLAz{pC+GLS?aI>sNlcjhxhu;Z;je-|)&DT){~h35YI=I1 zsQvh2#=e$r1qQJfPlEsvtlvNqBrga7xXuEJFj^_$R;<}^A)639WwV(rliw17ruXdA zq** zXP$B#XWCD;J*TCoyFF%r0qXE_@B10uGy>GRb_3K*+IE8vAl&U>;-S&eZ6F=H;zHXu zZ|r0eG8eV#QTS$ zbo!%_AXD#7O_);d9NVMNCE5ZisZD4S?NHV9$c@^AP_=*D&n~&EhpRBv#MiAuSK3vh zTYj!H+QCqi#>}B6COboQj3=8%Js!J#wZBEC$!333^=6y!uvGipX!L}!)xKzB6fKg? zy&y7kZuEx9W<0!9{U9`TS!jf6$aRu^D*8rUHa0Gki$h@~cIw3HR5YH=!+2XJwr5+t z+OYTL>P$2rX6^3q_x4*b%-X|YKpSmRBR_2LcElvcLD*#MUrcazMzheUtK&nOm;-NB ziMI$}TF2eea-*3hBcj44L32o<%Van8=J5jitvqhz^#R@C;O99qglnYz;ki zCx~kfg1s7pY~yb*0_L%zD{f1~3F76EA?8iS>`01+5lva0T^^iTnsHV));BwW0kzuc z(PYg1%UZ}0Br0qiI_W^&+V!@sPSUH@@i_s!muI`AVGr>Q3>Y5 ziGSS_cg{Q|{%R998j6_G1ZpI-)Td6tARcs1s1g|(7=BJxf;5Q@Wq#hG?nS|$fOa0m z@*_-j%jyxC>f0BzYgO=mNLn8h1J`!m8JAn>JuE65lEj+&u=Q%Vk^KxTJqz`aqC=J; z!J_G`7CYrjaX4J%gKq!8j)1DA9Dbfj>q4cNOk}Fed9P8!%z?SO7F7GSMV2~NuaTq|Ny~B4)RrMG5cii($3KW-4Pg;dWD1C^bLA6u z_tcf~chArx2&>Wp9!VrwS%k}SN@&fINx%{{DO{XcM9!lg&Fc-;%e^6n^6dh)=!>MR z7Rz|j7TqFeFwI1^Wif3$oj8s;}MCD&a_usg)aT~LVQKZC#FcFpWnu<=W?OvX>31n$qlvcB%o7k^i za!TzMFPZljiYkdYny14SVv<-vYSS%NYD$h-nE9;Ut@w^Trf1g|P7aJO&m0|E9v|G- zv^;-$(OAx$&jIxfu&Ck;AoZZ$=3R#5K0J%ZIFmL$jKcNHhX!jOd-W*KpR!te`8>JH-O z*pPZcGoY6d<64yhSF9s#yMnb0OaWV; zP(YqsZ3vd@Nw2oobI%*93!g8q7kp2MWSLGx?S`XA#~*$`48;q)CoyZYfvk|kD>dMH z7UH3Z)665trX7|WONREeJE~7IfA3)FT?Jc^1G1Fd|T^_&BU}N zI$XCeMZJwTMmq5>#Lvy>)MZdvfg{dNwTOfpg`g__zJa;a&zLQ%W~g1LK9Tau8Smu# zH}kZ`CiyT*PIx>;U>3=Iq2wfQT>-6omt9cgsL!ljWxMEUbCQW;aads~4lR>W%hehy zdKEFV#}dk^a}{2Q*YY<~ZlytbrhIw|H+6u7p@J1kIf2AB^O{1xtCdTo!*bQjNT)AiEs2WHzf<5f6~w)d|L z>CGqid^O_?ngPmFhlo14BKgN@PCS!q{R4S88}oe0;ac}qZGXkbdHmgMD86vlH3H{r zhB%D}rEuJTpSp~BdGbd;hhOAz(njJfjHlaQ*$ z_KPUN4VirxxNZ5DGNMxjtM{3O;|emz@0OP@C>r~3fdvGYJf_%tMUGqu$)Ov+X=gsj zq0akF2N5;iMP#FBre34ip8SlSL8e|cZ-;m#R_ODR6NND{Z4H7V z*9-}1E(BT{u}$D?Ayguqq^%wG7~rNZ4BRzXfJHa$Rl1TeD(d4e2!s*;DKcc_itRc&Lb0`%Yv6R_#v5LIJJ2yn(Ib`-6e!4bRK?|f z$DE1eWiyyP9b#*+7*m;pr%VKoUfe6IN^}V^Pe1-x7A`Z3_Oh$Ma`njMY-%dESE$s) z+}33^u9Jie!~$*{i@w_dUm+%k;POfHyemCDvVw<4W~SD2H_(hQG;1X>OIf8$_4WeY z)t9UC%B5;GwMtl*@+1ur)_BeK&U08vZ#I)cfweS|a(0Rz=}9;R2<7r|!*jjX@w=wv^#{0FP{d0qD)|s09eClu`yBq+A z{X}l)UDyb+@)Mxf{(`c<%5+E9a@`bxC~}fc=9uyCb_?x*qub&?6aXE{z;?UQwO@a6 zfL(QhSP^Ay(h35*+aDANj#)bsH@?s@_j^ausk8QNY-px_QOO{`q~v#BDv%c&0d z#h*$tm#ueZh^1yuq_1=r4M5=lGQX^Tole1MXsckj;v{UnbdTAXrp6o1hIFazk+VXg z;L*Je)>xqvS2jUV`xVG59_4r)_lA^dBfA8%?POx{9@QYCR&htiPLe(JHI*cIxKq1o zubaX03-6Ia8ZVfmXuf{p$n`yn6=q=j;w5Hq|B{&jUILWoui$&=in3fTG=i+)V2DhT zTqm@MIH#&TO~!tq-3^j^`CKnFG<(lm3YuHT${;PN5&g9>=veGYYS`oK_kP8`uK5)A zA>+vr{g*LlIc(|5Doj0+G}S5dVd`Q1Vpb!)m_F~^Z*|gkCpnmPdaPizQbxI!_4F%i zC(~ixL!aIBN6grzi#5*vM%z#*DqmQU8zP#(onESET;&0&L$MD~@0%SvjCb&u{*V6V z8_zbp_gtQr*d~z=5ThG2H<))Ixmhw#r0InmH_)6=weSH@WrR8qM72Wbl{{?Axxm!C zpbdrLfwa(-0(3cjz8?6OXROMK-fpW^+06Eb*X6tt%?k|UuR&jplpVv@F)P|dE@_q=gfH(yY& z=jmLbj1Sn-bDDn&ql)-^D4>H+P5zf~4Q?>3Sze8qZ;USrn`W;E;HEJkHjZB=s9hxJ z<~cx@FaUkCU$jfF(lKgXn4Bbye{ma1%K;O{uwolz>cD_o{7p)5$M~`P8-y2nj~JN! ztrW*WK-k<*hKi`C;5grO0}LZ%)@VUyY;zRNN6i)gIr49h*lopx_JpAZDoP;iGZo_aB%wj1Y-I~<5tn(&ocF+-`v_bzm#Z1e>(Q6lGL);C%ELpPtB|qA3Gh;s2_#~T=T!u( z+bV+7R2-FH>!Yo;Mr=C4R`q^0A8fV*u-xU+24_DQvO`-LXjF&x9fWmySTcaM8I-L; zx74brmHBF2{)hvLEAI6hRWK*)?mxBu)a=kD*x3ap6|Q7mwQ0;V$gK7mUAGM--#kZz z=3v=Vjn(X34bt*n1?rgtEPK`31ZWt@#t04yUmSkbofmZBB6R+OQy;el$G=5d+v3-W z>(}9mSMT?V@wwBr{J+OZh&v7UYzSm2VC0wJ5QJXA$Tid2Jk7YmrVk4cX}>pap4Wx({e%p z-MPCZ1-Vlt-V9y-)qxF<`<@6f5*GUA!gj)~Lt5mPd<3t&>6uV!l;`9Z*mwZAU*mf>}@B zO0~1iXN~@!-543$1%8B(ooL(*dmXGEP4mQ%`IM3peMl8;esOCOI2Au31=5(zA5>f8 z_z6b$wHyQjRQ`kTyJCcYN+qP}nUAAr8w$)|Zw%vt3 zwf5f6|L^lX-&!~4BHx?Lyoi|*Ip>TqVvI42=Wos+5nP0}h`%_Jf^oX^{m&DNDq(u* z#5)D_@k@}vCq)uUzhV8yml^a)0Zhq|>>^Z+d*R7tldf(HhsOvYkN08v1(<(E_6&132MQ}7ZP74wR zDhk&8K9Pf{mDSOz5avGTE@uvxXJW34B1?{Cit=LS#5-%fRO@>E<6lU}NGQ1K?Kjrb z@Q)KfME{sS7Pe;pP9Ux)yA@tgP*5aLSyxb3S5R0{(3rPwI%QMc4qa744ML(oYe(N2Uq@d_Ls3K73SV1G2?bNfdR}P+qQD>G z>Atdtx*a`!imH~55LiS&L_kPD;PeeYs`d2&>e1ExiIIS?zVjapyng0s^sh0u*zech z*Tdg8@tj;mEv!xC9qn8#{y&)ce+ldPJFI_)00fg;HPj&dIqB^a_md?kH8Cx<22Wct zDLvJ;I=td%ygwCtZxcBON`c-`dmHo+B>6bqgtP?BJP?JcvAo3e%#z%+oW!K~q{PyU z3=qmn%aDSM81-tjv&Y%1p=-597OaA@t|6cq~ zuKyFH3;qvCr`MFB3E~w*SOXKlMh;0UJc~5sdu??fwIfM0B(50pwkv^8X8*OHR>JLsCN?{Szt`QbZ&SZ+!synkzk9~$6;RLdrjooTWUO1IhPs9k(->NZWLFa!@B)*Sc@@wmN4xc*=cT&uDCy`p zAtjT6ZL&)Gxaek0m6xM(J-E+W89%{=h7tZ$+9qSev|)E+%~B z*?Bq9qJaIP@N(|)h-vg0gckdUdW{hZw;ZRhd_5aTiv2xF^GDRcf3;SL?h^fIhV zmKetX3)h7dbxi_D}PuVu$%d&emQZssaQdxS= zAh@AJx;;ZMdt5*t!uD6mkw{!Ez46RAS&Ur{cTd>^8 zZ-<@4Ou{AoTu3~4a6+<>>}zByUzC2UXVXsmFr{vX!JQ!+ptZb}z2sBPXqpTyPlA`b z=&A~>@pu}jbXsHxzi-LNrwz!39RMd+om#Z%dSa{?C8=4)v&(}qw5^E;LUP>@k z-oR@jMqNsfRhCjzpeYw=CjMdRavslomUs=y^BH0430e}(!;B>)(uD4p-y9Y;SB^4g z`r&Vmz+kyeAy}G^B+|5?B!=K1gzPYHPEoMn9$0h4lSNtZ-h7`_ba8nq}t610; z%x+r5+5?gp2)-)FN3gQwV?Uiu-=v&&Z!qbWv9U=i+DNHy~T zbmyP$OXiZV904j`o*xV2^fES}gSLqWlB@7^Wr!`pbteS$+Xf+9`A>^KMJn`3`&EVX zy}O`0r7{(PP%8sWAX1?G@3!<;)os#rK{g0hZNaPfer^>KRRCC32Kb15!(#g0UKr0m z@Kyqv)%tz$S7Qn2d+wpJ`UW-Y+)*yc0=gmn>uu4W!4I1Cw^ZtKA)ckcqlQQ7Af!R- zSemFRDPM*G|Ao#DxiA z6JDfuodT>gJaQPCLQ7UqvMM7M)Eda02#d0HS#bM}iWX5PE7F~a&Ey3w3l4>Mc!)17 za^UUW|25=-(yHh7@;&Ms1N%=q=ig7&{{Mfe|AD?Vyu26HlK03eZ9hJxpk zx-8~WeLcA4QrX$D-rT~gYGpFh-kYvxngAgo`NGGSJK27c<8{sP4S!v?pL{uf)yjE; z^l5VL3BvhS?}4D*-}1qJj0Ed;of_}kfpih?ngH}7-Fm_J+3&`}e$+xe+>sG{uKBAK zeMSP+a^9*5(6K`Z(~cKdwBu};2A8O>_p+$_4%R+q`jWdXI+J}dh~`&uqgD2nzxuL@?9 z*do#Aim|qrXUfXaEN)i%Q#IPP9lpSd#nPxSaYx|Fc3sAcxno*nMJZI&i>1Rqk4=P7 zw5o;;J{HR%poIkE8U}+wP{K#(;CO55>iq+zleN{x*3O2=J;MtS$bWpBzo>FPH!PS) zr#Wb#!<;w=To};{uv=nKfDQ3-s#>d+wG3iTUCpGc)yYQA#Hp!rBJ2*>j2xROAl&>C z-XMd50lV57`UR8Q{3aR{s5_*XDjodgUO9hkv26}748)x_AOBZXEBrAlSfNjHzd8Br zlytHWC>=F=ZB?Q|jv{gtT-c>f{VBitmGv4sQp20Er5k%rnCmd0PxPF+9=?D*QzJs7 zXSltdXe|?_6;p?Mg!Scm!=|RDa^`WR4)3+*deoPBR5Fc(N6?BrPlR*mGtJBG47Y(7?PL0uF-}gA`daD(lyXJ#p5murfpYk{Op>)!;~o6sR0MCIqPw z15rYmwHep7=@b&I-p~S?7Cl`o4M&W#U)=^ciC47zU><_BqxoC(DnGmV;&rxm7WK|x zj)Z*~JgVI3l;)ijetwIPI(@(In|nyYByNo8B*e39Gt$6O;wo(=qoNj}vaG{HkjCA$ zF(EP!x&&Oa=tsCYU+HCcS6tdUe10pnP@Ud0x+TQp%9kJf@GMA|dH87nXk&JI{}{Iv zvIb#0RorDl5GzwDIjanPMpRvL+OP?Z)Ai~S1e!38t!P$&eZWOxwVhd~F2h1NQ?wkh4it-^ z=ERlE7}>cBkOjbFy{xr{3Ql~bh6<0OB$dwZud6Q^W3LQax);hh@-HDbPf%;{OHF$u z=559aSZ?hM+JTbL@nuM>qj90-cBXMS=ulg0PL7Zu-n0m3Om#C5!E-Ls|KdH&%!-<{ zkg0|V6e!e-prSzUNLSJ}yf`|~cn7-A*pparsuC<@Gk?v;j{A{nvZs~qr?IyBL^pG% zT_C{={iUiIxd?$LR;eO<2vdVtr`d-Ipa!WWtS{Tu~5B;2M2#sSm+8xv|)^n4V`|Qv^GqV(-ZYgcfc7fg} zfD_1GbfAho>wvXgcHr6-E;SdAUv>do=K@C@Egq+q*vd?tr&RjmYQ+Hz*1m=bbd~IG zyx0LJGJ64 z=AA%RPGQahg#>kryK~$*GydcJU1CU#-9Ck^D%wOO=k%Uc7B^i^t&2B?9)?_Mhm>VtF(u~MPM;<;+hEm6D0Q`hWmV%&~E zSS#mJ_?@Z+9p_eAJA`|lfNqt(4d5=FzHY#6f?ZyK8)SPvoaHl}fNrfmFW|0J+}Sf9 zXnSC}hvvlfH)K{OMhnQ}1UpW~aIIt?7SCQ;vC}eHyttoUaj->&G+Y;rPz#I8#sz%o zyZTIkK4<+L>E+Cc0x>H>m^@VrU}r+}Z*@Oo;Z0*>HsYBg^zJPrk}e*>eUwXM!}D>A zV;`F9nR}QE7RXK$IdKZ*&K@oHvUmy0y17t!$cGzcW9Q5mSrZ)K&cyM@gkb8`nOko0 zYja)bq=nJ8T{}f{m(H9XJ!+Il$H_5yY8KE=+4UsbBU_wRW##axbJ&6IpEP7|qp+Vz zNi+5KAEkkiGRQmzWfa6NR~8SM|%aFOyeoB z)gukeWgu%FWL!}~RS^hJ8W~g;qMyrNrV4B9db&)y%#7{RlVS^^NvvW-I$gZUd74~n z#MbG@mj-teBEgwd))3I+CuioyePJZ$N@IG`I=x-IRG%wiiRVBI(zq75GBqqXD+3&= z7?r#v+69voUa~>w(Evzx41xv*R0iHYDg|Dt{ZCTF2B?$LmD3^{ae=DYG7seI;}YgEVe%VGVI(xlLd#Ew{lKpIUL5zFcHQR1W;#H|{53XMhsoERdL%DGQ> z&SS=wvI`legjy;om5?(EduAW+1LEB*kb&NV#8KHJ0!k@nNz}>Es1%Ap76QCy4uOec zgzyj`hWVV0GXapUt!&o^4b4p+&`uHIOS&9{JP!etIDRo@xx9>2Pu_?XJhMA?!fn1) zX^V-bhdW5H(`XSxte2@9?QDb$nL=UaXVRY=#$s#6xNPk?0qtG3Y7uYh*ImN(M%&5v zHzn*`4fODkZ;79u3`DqWtCCKPZ00)mma)Tyf8K^e-}UiFRLiFglIx|$K0mT~G%bzr zpJtdj_ANxD8fnqrUT|-$C;1NQz=TdweEPs|=I=A1SYKtVsra4p?LMvq>byIr=xd}G_OzT^rjA~1mYN8-dar_AwniXNdRqdA7$Km-?V&~V0CX(Q?&g~Z32W0#A(-;DukXVAnw zh3C^p+aE6vkk8nUH&pD7h_XL6x!I0|Cc?BDHdsJ6+)e6IJO-QXvLmpV2GBd}4MqxykqsIB!ixxd zD7_VUUeYc8I7nyIt;%Gz!*5du^sIjFvuQMIe|@OX`qS0m zo8;c*x5!Wjj+t8b>W5TlBZ`@qb=}IJCWCE)z{p*y049U#(}2XR)|9N69F#wE3&3ZL zV0=SNKd`G`>3Cmy^>6+pe7;ioK7L`&?1IGP?ChF!i3h*Zpd6TJM()hXe&o~)<5jh3 z45se`xrcRQHP#`aK09I_v|C`luS7Jla!0{ch+7SYqE9HThGI;pt%{*HQd}a8K*|fd zypr>d#6OeE-5i=TJo#e z=oh%;R^i*clXp@L_k|}7 z!V4SHNSUZxAC4mq=cR3IL)$q}k|Am20W54KK?(~jDPgCk%Fv}j@*;YbZ_8JuKyo5F zm1oP-q(HJF*!mlz>cbyU%?Y^77)}OE)CJY@62|3)Cg%8gFAGqt;~brpKoFY{L(36g z<|tv4hS>!x#R7ibWHrUdXo_-LVw{oZ93GAd^tq{Xf`1y;YCS~Wy1 z5wMJ_SBo1aG<9uMLLwz;u8LEh6{Wl=dSdLz(R`5P(%p!5255^Hs5sp`OG;h7FZ2Y} zjC?{zpgjx~Ov;`#$+}X-D~e~st?Vr}l-1|u^>%wV>4qis586XG{R<@k_pVOTvPL+& z=ni)?(`<(|-izkCHsC8YPLs~uz^%JBaQo?vw{BS?Tz_2M+*vm0pR@X02HSkmaad$4 z93URFYagheU7-&^XQGALB&0nS;J4|6e00esRt{55XFa0cVQsnDGQB~@(V^xM#z7SN ztndcDF1l(;={F{Vg>TW%EjM*jS9pLA(3eZilck+j=%2yXgsroJ+xvUv=KnkvwtrKh?OUrX1L;RrJHr_32NOVu^a$d&-bQO+cE7a z$?0!hLmPgDNc95a&^BvazHtmc>XRZroWIdfiRd49uVcimiUOmBnF@f_#>bWCKzs$G zMZP*ezX*#%uSk3tV7~nUrZd;hU{f42cKq*IHEw#yeUgrs*Y_Da+()Z^nqx z<<6vKTD>1zrNM}=rRreRXQ&1Y({tBf52n99cv69hddb(0xsd*VrX8LBu3ppxBr7*# zvFV7YGnz~#hRQn``P;5JU5}suQJnX;v1omu1y5K*N4qy0uesaTzj`MwX)R-?-zeNB z)j#!4{uXr*HgU9Y{g*h=f2)!y%E*1sM)_o>d9%~b62lx71~>6%<)kP7uYuOwaat-|m|G_ zo!Hz5pvlB7W+P~(xrnZib+Dy9tZl9klo#Y=sAE34=M@nS8)1F(u1xV7l=j7wJL#O8 z)^aWwoAGkSj0Yhf`%XM&$mpQiA^}e=RycIVP~O*?tG=o8m&Cc&8pWf?;(@L-qDtdj z_aTw3$L5WeUbfVpsBn`pYgmFQX7DDSFWdmY8-rQA6CRqhqU4UBl}n5%!E=_TS4-2Z zl%+Z!)c!0APWgz64`Z6puz6o!k3V1psHHknT?`E7q7&#u-R~x{kmx^b!*MKFJk_!r znLvdiC7<#G$S&&-(2~Cd3w?Fvd!$i94MPgHyMU~4H}M}LWi_g1{n80<6K+im%k-0F zn~`Of7~_m7yiCq(INPaYlJpcruSYvdk<;hRS!?6l#ZvZA*Jr|AY1?(An4^(7maC*y zD#By-{i-c2>yWx-e$QJ&*(JO_|7*T#30ZA>`5SY){zuFW@9$t65j7cs{{Xfs7fM(v z$bWS1VWr%E-IB`(Sx7aIQ<$p&Q~48I;^1id2WdL&(Z)d(qB#nt4N1dHzH12yaM)@UUWSdsj)Bnhi*<` zzP#qpU&<|Uzfg=UKvN7)H5#is25CxvO@&+|KG8!TB3Wgwx%=8t#cxa$HEaX}Z^d}2 zHB={H1Z1uWZxDK;2PQ~=YfgEDY#UQ*h!Z!YVmef&Hkcj17@k!>rRm@3H26Gh_cvq&9pV0<(j^Jd|vQSUis>jIB6v4eg#Ij-!Mp?ELvskihITmczSB9XCZ^{mrF-3JZ%eDw8wOK0-8KrrP2~yTNXeZou3}O-TGzzl{ zaT(eN2M?HK7H4(s#otaqj73{mc-_OU#4vue=~UEKwbCr?xiw>zwukMtL_E=1nPOGy zZ`&?1^H_w10nF`Wfe+Jr()@rVOuNr7;vbC&5b>_zA5IF;5I~KWzXjk`rVphE+4y7S zgDmWz)(F;?y9?DR)|r7ShP)m4_fd53-LoKz-R*9a6+IJ?kL;5jbnmab&y!))=lL~(294AFwc=s`<9vZq| z3$Z_V3*_(yc@hY3$703*Sk+~xo?-@FkZ_;eei8Rl=fXeDLr7xxm9DhE)wpQkx6af! zJ?LGuNGGL~T)9HBodKxui1E9yASesTI9Jx@UuG4ShR1Q9r*jJ3SRnbFOCx>K5vvn! z%cL_(AdHkHIRuF_MSubS=1fi;NC=Zs6-d1crj|>NV;G~^<<(AjN2I!qV-4Mza_PalielA&rYNWDZ)N20@sfQT)>K9N4&D96Q_#MLEM|Z-5x)raPWBs zo!fmuFD>+%D~xduJBy8S9~Z;SzXkGK4C5b^X$u3?ZurTC4DBura+wF$i9Uj)Syu1xkr#lFAF;GYVv&RA(SUN0vr6Xyd^9??zU%&jDnKJ^{rlHSJ zOpE%T$m#2#L*|gVXoPVF$@Qwq-Z!?taMs(alpWn~P~i3-)%X5Vjs0H{W)XLLJ6jW5 zXA1*sYY$-)BLk0bu7IM+U#10i7XM+L6stqHe~Vvz>6wz+ncgEl$JL1-lR%qS6z~i7 z#|;CAe+~R&E>KSA%^yr?2X;@x^92@I@xska=O9F@i~e2`EhJ%!f89M=Y6&NW@nquiXIe_ z8Q2E0A>6?dX0lpyFd^_-&%-~Z=IqT97Vx-hHNQzZwNl)!Ga7?GvI~oGne^!v;hfsH zMKjMczey!^4YlvfjW30pWSYv{S?nP`nu9++izdtmT7;4G1P8`|6dQ)-RqUZWYE-aB z7Wg^OO`Dk;cPxRk13$$U$c`cha!bQa1MAHteulyDURCuwCeK%mJ|}T_=E7az_YDXG zo}9$PC!lsHp7?{|&#w>p47{-fpoWLhfV%|XeKLWrD7B)ua%ex)p-RH;2(_U{E5j!j z9KMx1C>T*29iOLh`-$QBk!BanCQ+DR7FWWfGzha~jGG4TpDR)(raCUKT_KjVRZH|G z?X-$YI5nMZ#r}$e5%ezWiZE|m4GhHwvag7Z8&#){hIbKV*RO#f&7zYr)9fgsMuY|=R{N%uB(qyLK%NGkHk;B- zDvRqn7YC{;?3Ql!dX;OoBh?YG6A~i@tAUjK}H*=k4FRTU;A50Y!$X_tHIOTgmp!NhhF8IGseO)n%$WtFlcJ zQEBN#3y2rH&jUftW8uOc0gtO_GViP;FK3qf7G(EmBZ%Ke3Q)PXDwW6L5!<6P;x&!hK>%dQO*y?NNUL%(oSy+<3ul^~of%0GMx$(rPn6r7KN9?9G%3au z4njyLESohw>VmcgAH<)Tk()GRM+$~aRBcZfrA~+4Q}GfWM}&t6jWm@gwH3X1t}RHk z6=RWIR)9JrYnecf*39p)bz3&M4Zwja6D1neBtu)ExwbJe2H5)zBrm4QjL2A!;Y!8r z%tA;T9rupP21QgjeTxm8)dc5t?Z`5jYEYwM-LV1rp$-1&AQ3SbE8 zA|&)woN>%Vzm*WxVg$5J66^Zxh`AArH?$qbYgo{Ag|i@H2&=51cQ<~_N`#9|L4I=B zC|X#Fa%Pp460}VPJA=#v4A)J>Q>wax>RnUT==eO{LB{&Rd`^?srq)N@V026}v&xn% z=kKKn{U%MU$(ptU*ixJ;$?+~SPP@94H0mdL!z0{hqxQ2Vr7Z-hHneBOrAjhHmfH~5`wBa-Bq@lQqEdZ}yQcD)5-g;XNCL4|r@W(ya4F_(fT?K>5<+A&B{ zS3!@fDs8#2(#FV(mW@*}uyCV)YKayI7215@c_D%HiN{u;X ztT}ntB;mG;#-=zmqTSUsHOU;3ggzsi^CW@lqT;y6bLK?UM8xrVrBOV2-7j@lQC=64(jjk>+xiudwQ%l*foF9k>uesk6hryb^*A%Q2Mg@=IQhr0^)oB9Fh!`y@hf*j3@9P;fuhn%q)s*tZ}hbQV}IhoP-W8~RD zJ8Wa*#UNll+4EhY3kykxMa^+_7+ywdsH?1>e39|km7b0DpZJY@Xv=Vux><2Fsyi)653)rio+6j&O2K&teC1>3AG!ig)q4dkDakdlQ? zh~*z8G`CkGYZ(oQq)$PT-9v0)XP@kIy1R0ne)C=W4)Qo=7p@^5Z~-J=0+ZxR?+*9p z*=1-le6k+cOjOZ|g?L9KUzxTd!*#)pIU~#pzr0T^qHsFQPV(7KXD+kpDWOEjX*PQ! z!R{X#O|fvcL#`@b$_nWSI{V$dG=O~X{kYrx03YZxgEFM1>5dI%%;c9D(7TCmQ0J|w z0_~9$g(db~*(r^uHoIezWE`yFk0hNHPKZ(hZ6D(j8ry9XHQ7>^mL`5e2D%ytb`8Qb zh^H#v?=p@z?r{l&IpbukT+AsP6U1(hNaHZU#XI8`gPpr*U!M@v{z^F`#Z0A48=s9L z4WBX46TGIT$n0^!0?cnX14?V8!U{EPZVTPEF%0<;KU66 z;GS@ol!G;lP&@S`;l1{po03@LCK^oWW_OX{)F_3?Bcy&y|J-#vJH9-@nPZI<>y2%eL0AF7hRlT z5rU7@9^!t(bW=cGfQs#Se$Feq%!m$TfAB|uUxB5_39#0Mx|Kkw=6)-X4Lp)S7IV0E z!S${oHuX%`X8dHoFuhD<%s83Q6=Gf?sxZtguA-fldMpsg@`0*lK>6jt<*=qZ={DG* z|06lsf1aPOf-7=?aY^3&~7-qfzPS}u(V%b9zi-)kz3l$V6pkJU><30 zvHK!1uyuYg@5+yKha@c+$rZ=Y*BSnm6ex(L0J)|#sPN6%V_(oKGKAA+=~#Hx+W|9k zWIB!sQ=1pGGa`}?ADXFz7Ev=uLgH?qkAdd5p0EY#quw|x-GQJwl1)*6VB?#4Rf=`z zPiT-$B%gj0&{Z7wlzH{_Ek72z9&UXAzmqw2m3iC`o|nNJ~_nbFgK@9BU|sGuWqnYQUlG90PtY2>3!= zF4GzuEPYibrTCeukSC-6hw@zlyz5`K540T}Fq+>wSSbIfgT?fB2Y68ncN1e-gKr6} z|BV=%tYjs(Adl`l&AMEJiVoC|#c8}xTUSs}AQWZz+LQ-j3ZW<7@obDeUoEvkb6z`7 zr$rdf*N11~EvH3?MYNy#^2K{|m370{;|o+5oQioYFK;i`pAkX~iP_LbS6mR0aA*Pp zp{!h0xjHK!{B4ym3`D`Jd@J6c5- ztKsOf!k8U$22nmbqHIs-4O(d1ZZqg;^DyMxHh1h#M!mC&hS%n!-}08Tvx5ffchc@- z;8Z)3*W`{b1D!eiyL^oeW)E{-K9YI#vMEX^;H=5AcXS+5JNzbG5Y2XgD zOg65aT}Db{Qq@#B(zcxTL`Z{z4*R-le|$bi#4mbponzf_lyrw4EE29DvUQ%D80M)) z`yqV_IA%NZ`BI2wQ;9v3nFHCk0F{miR?!d<)rm7;qcNxvmI{Ap54!9_v)MDr@|Ls_ z-+f>piX}4<$A*X}uYjdt;%6&`LFd87zj23f;p`t5ZlbG6w$@?)erSv4N{Ua2C9io3 zDL@)(OwLN_Wc-93n)_UeW|`M02u!}o3S z?}^#?f0u~=)fn}O;7v0(#4639>qi8=E zWVi)*l?QL74kHA;Sd)|E)B1Y#_5!mHF^Jeg_M&)(_*5Lo(m@{RH|N&5r#gx8N#~Wbfyat|6g?=Azd3GT-$NDb>kI(m=sO&dOTTY$)*4B zA=!J)WkLeXFKjNJDWuJh%#jV3#VI=Xb@PE<&M?qonHz$=1^H{?LgI!7{?Z}(OL6P( z7moPv7Eav4$=S}#(ZJ?EEyIKhStNmPg7d;g%LJr403A3D{ygnCCGxO2b4U<)dD_o} zSBF*E@wo1|D_XnVgEaF267zkaSNRd9g<^Q6MLMSMoAmUJwxjRY?`I%>pc!fPG%AB1 zEI=+uYc&R$&XZIR{XcMB^`Op=(A|x9>-|N5q(CvDi^ZLVozcg`^4Fb(b{qy#cMfvw zy%EIbuh_LtaAz>?ZPS1m2DP1X@f^v*i^Tx^Glk>94MRus>2yjlK(AJ+Q{)kX(HP_I zq(aiydvBj`LN+Z|5ut@-Fv6yWc2MTLdM@G>Z9`~ZT-}M2vm@Hwp1KcX*(47m1t zD2X`C@6>}AmsN5;2wo5w*E$kdlZIH)BWw_AU9~2c&xxVq>TqrYr**~Gm8-Y;mfE3A z7+VL49fvcNL13RP)#ZU2=>trl2HU3T-}R81&`Y+#%j4y9~`ErX~ zqrcTY#`nwMWEDCEO)Dgk+XpRb6-BuOI$*i|_*5J-2Qr^_TR^UzM-JinIs*=dDl0#N zge=nyz~D*qhmbC*lZ4zLR#!za#v||(oFp=)G$S{4=*L;aWr?U&Vt6KTZ6BpY>a18( zN|RlSJRCfwbzLi-;V~4rkjgN2@lixnx=0*hV~+i;D3aZ8{CW>&aD>?Z?ZeT$^11U@ zx_0@Z*z0`PdDK7FxzyjKtAwqSv!jd6chAVgSj64L$i>;t@ms&uu&f<;4kR-x#yRkNC`RAaLwG#d}1vot2(YU zPo?ZNm*KRQR&cgvsjC%5K?}H{iaJHxJcI6peP$w6awFv#U4vzBSlwkk@T^zQo$(${ z*2+NQYr9l`*lTUE%Iadi8fIL9slb?>KKWGcH4m94W@L?j0tBNW+prYQNpDp^vubii zVyPQtI|)|e;x~{5C1Ke{mPlaKaa5*;tI6EV3-G@#S(?t7Qp~>09sQ%j7`ccjoB811q!=Y=bzbTf?sP5 zvbikJY!5~R_^l2f2}slXfV!%d+SZrfYZuPH;kZ3eb;4T}QlqZ3j_Yr(m>qll#Ax^+ zGm+4f%f2?{H(dRv?W(+4sKGXP8@JWXj)yCLBc>U;P^`bL-hqA~1##$_PiyHTp=c^FaU6RZVFXt;Zi$9-=@N7j5=#y%X20~MF zQ<)Uf`R>%3Kzrgkv`JC}$JQG(;U{*y^9qx2u02JB4tA#CgVxYp%x|wt*g=&9)5~w- z_o8w+IP8bvg?dACu)XqGAn?%pudIq!wVEOF$g@&-I(V(Vl8WWJA2ebtzY#M*NIB8q zdpi#c)tYIb=sR%bzUEv%V62*vl<=sA1Zgc!vH9#j#3#PxQR4c{vii_^)*>H2f(WKZ zIrNS-s_v{uNj&k4REHPxs@9Qbh>HSMl-AaIb(r<~?)-t?(kYG9lsEC`e3=4)m zFf*W=WOV~zv%f5e0m6-B)97|BqIr5YL05@2*cru)~3fq z3f(Mj96B&--o=*m;oD0jk|04fT2PzCal9ho(L2GOWVbIaMIC9NeKk|Pg;o;YrgCg_RdEgv{z~wpajtmE_~18O`{bu54+(OI^Qkr`s&*~NLAWXA!H!1hFW|Zrevh~nHR`-g zwyyyH(_-Z`_HLb8Y_4-k<;I^P)z8nTK(h%4SeU#VYMnB^fWIy*nNkwtusAyf{gfcn4BZXb^8Vtvsm|5a6(Ganw zIl(hlt6VqB&FY9iB5O0uPlJzk@Zn#_V;`5M==bk!^A+@;YVL0>M*e$i{2$xqzt4Tc z|3O)9j1$tObAG1jYZ$}OuZgPzLohIx03as*kl+tQ@lN-U!JX`q*y-}uw@Pl&KMQH5 zYH5yaT3&9+OF%DQzOL4}GS%w5dGGn{qch!pGnFuzIVQY&F*@39cg-{5H_h`6v^)&? z;d(&#adIm~M7x8*-5l=eaCdEldI6G;Q1ERJK6rWDZU121_4#<<#oflsYlZ`!4O8m! z>V?C5EM)l!w>QY0bv(@E+jJKT_OW_C%KGI!8yoWjcR&MA6iEGNJ)`x&Aa~TmZ5Z4M zpn5>;ZLHYDEjsV`Vr*kW?A`jP$15;g?%|#iuQ%sUOz#j0KCKX=S8O=B*V{Kn>|wOD z<4A7r@KCykTNK=!$GuAH84A3fPK4G;VPz(2n#~GP!gGFMB>8n1A*OG~ zQ|_8!GKD8%=)wGR+cV3Jl@1>@H$AOb{yL{#Px)#&bz9Tr3jHHd7sEE zPNR`sQUE{oNb|r75kb{9r{-w${2&x2FP4Wq-1)Fl<{b2}b5sI&^;wJvk#O!R*m8tw z-U^Kp+=vx7ixgAD@)_c&4hHhN+3Y%&6ng7<#dW+GbHxfZxJC_*pqDd+u{}64>7{w2 z5Ppe7+f&s>emB?+GOc(d)+wI#JXu}du5wE@0gLEhi1A6;t=SHwIe@9lMjzfzCR9bD zP8Fq-o#}!V%=xpl<+jKf?k?-r-!fvgefI&o5Sz{f)y*Oz%0_iPsjTGOMV}#<$;+l! z^JkWda5j3lldEzg#fLAM<>WMOh53z*gA)4Ho$zi*t;AJ96ovI_c+ARQ1a-c@5i=?> zCB!qSj7HX<{XaE-Wgtq2mk%Q$SQYeQ>-uvb2_4_sGg?Rd{%!7Mrle=Ys?|uvZ1~F# z6`{^xii40?!=R+;msR;45MX2pJdu2QE+K@uoi5RX6PmF@xh-}S&a5UO0%cITp_WlR z_*sD`!?)6t2wy!)W3~e?ks548y{G6ir1q8Min3A|4m8s&E8=`eI@$Fg8%T`YRq9z~ zy0zKDBCN6XhGSdxV0QM!9W7p{v9gXX&UH6UybDB<9V+WSML$tJ=KAxB6Ra0#!q{7HSqFy?9I> z)>;<*PBW_3#6-bDoX>Rtx#}iOmv-j4gbH_)%P)2}L{d zz`4~_H{WF}RV+K8^iBsP^G+LNXYl3i1P=KFp3ks_7lfuQq`2V|m&c4WlxnvAj%%`eg+LX}TsPV8?KY?p{vrDh%b>b4IS`LnTV9 zF$oySf%FapMiy7)NQCVQjRsl8)+YSb_}D4C;Y$)v4Uk5Na6=7%8D9TrT6;V4A;9a5 zLTzz!Ky8`nKc)V0I`^l|+U!mp(x^^*xjDzi1i7hLXw78`OGPiT?~P^6?KyouGn%T` zYFObkZk;b)8U~-@hc~b~jwyUGnKkic(hE^rJ zO(GZRgFQv)y=pDtQuK*13F;ttCl3Z-^;uUOIF!m$n6|JSx6eOHsD!6+9Bc5utmY() z&^{Fv0thqER8|&qgdF~$OPaom?)m*FE(DJ%utpw7r{xeL#&upm&%;pn4^|JqdTd@b z9fiFFK13TtxUU537M?o)jqM_bU{5!7cg`?bR>KjQzSQipEyhsUYyMo^4QKRBPRkW} z|7aN8&3-~1F&~*)N-pAg$ML-XZO>SOc2bPWV}X=1AZG*wpb@QDMWD1i-|KQ%O65Sb zJU9euq1g<>Vxq}yqPfWs-Ro+=*3N%Ra*W=NHSB1o`y@n;H|(8Znll*ib)LbX%@7le zTU0+8cWp)5YCWRODYwiudMtaE9qM~YuoiG5zHWrW^X5W*zx!B3v?0maa+K^>c(_XS zbm{asBo0TEbyHI6iX9nQR8Rmd!X-X89krU4tH`{gFr>$(aOy$Nzawc$B22{^RtKEV zfj>|)LK}~t5@VE31|`I2u$nlyil!e|RLb%&E77=9e0%_~9}o`mIyJWxQd1=QI9P=r0~3v|r66%)R6wcf*MLGz%`a zE-g-Wg>T>-2gPV}?Y{Fvt9tu+y~$rnZWqQ=iNeF0Y`H=*z0tnV%{q@NWV1SKF)Dy$ z^+~+vAh=>eg{`WjOWL)Q6-&65}fBzCc?E5YbI0dTPuM*wb_@Q zi+5!q#Rz(J<@VH*ruQ{`@B6WEqMl$yr~eOaUl|-{mMv*ZvY2Htqs0ssGcz-@#mtgL zC1z%3W|k~wW=4ydSzFybZ{E&EOz+#>w?C>P>R(kwetGkpdrl_Eg^{YV(J!ijG&N?1 zE=(IceOk)!r~EP#S^Y7W?F!nl+)pMFHBALXRw?0R*{3+Ha(RyP{^Z=mxLIi3%g@df z_tp|{)~#H%<`%hbpU1~9UlIOjmhgLK0=HN{eagrEJJqN3Uul*o{t;^HWch#6HEB>$ zSDlqZd@ro%15^(c?(`VUHxhhtK|?Cf*6S`PIKFyBobe!tCQGLd{g9YM(!#r4*WIR* zKPW8sOW3`h8XL>tWZGnl^O&XE;Y%ukQq}|MIXDjU5NJL$!a`hV^_&-QFx)kCgY8#B^gwhHpR6r7QQvPVmx*r8 z{|qV3J~&m8d!9P8p%yOg9FPdy3YbNzr&vX^UYOyQy{v@riR7x${cJ6{YAsi#!sfQLOp5ayRUtn*!8fOY!xC1MowUP0AQ z_AkP4Q4HeAZ%q!Q%Qc+r#9!obMH0k-?r$JbbaIIU&s3EBdoYUktb(AettV;%`^XKP}Py8hB}Cy zk3Ida$}s7o(qkueQ%B;A*z(vFJOOP&v8N=_13V@SKGhSYadm0LTacBH~lt<8zTi#D@<@DBIgewaLC4P z%ZcBOETS}~e=O$Qb!NU5T=bY$&W|Jmngyu$VgVysGJ*y0^$O@5hp@=;eE1ptPK`lJ zc!nOSXI7d0aG4btq96zCWqTzx<&PwjBp<1Kn=NWW{24ty-^BPssYX@;GU47k#@chW z`E<#dzM~F^xH=};#*!L#v-kP@>C57dcT=DYY@qc2Z39K?zwNP>1I~}z7?~UXYe%I) zWnB^IW%7|>6qjJkA0B`O6P&7n-1l=v;n$=>Mc>Z_zS4#j9cLSThGk>cwuK}f=jXMQ z`-Ujz<1{AHH`n5eciDu@HYj|if|@)*6Z;GH2hX<&_6I}V_SYM_Pbg|9d~{^6`B#~_ z8*KC;2wz!@^{|-0CwBdEX|b9EMl%k3Q9<(_<)BOwwunJh?WE=L%eLJ?#KzOUgSreSqHc*~bQ=XMY6`+D=>L$Ju4=Pl~9pElP z(IUTYvt8DqpBgwYz@}4rI8{tNEQ{+;mkg7_v&WSo)~in@2KbW>Ccx-hsXvE}Bt}!Z zB^Ij$T#$D*6qm$9y|odphf?mUY3f7mr=LjpG*TWr4X?Q9@K#v1@s&D{H_}%}qY&YE z2oD4_q!w1~i>XeHr&)CK3 z#dl0K?Xif?6F>MUaOXv*xnjSOfk(4lp+z1x8}Fd3vBq@E;~Jxcsm;hUT$Ri;`?qoM zFxeqwr45xNytwM^zzBOm-uGpp;y#4m2L-z`tSOIeggI3)sJJ z%(xR^y>Y|7Yl^$cbkeebm~8HGaKXP6qS0d({m?8u|C|X`1XyKXb`q#}->MKDj9f)( z`dukBajr^q!)O*KMq;eTvdz2p*+Is4*?{b;gAabRwo5Y(RcA3nYxg~tXMf;HBczc7 zlPZ1Rq%KyUjW!O2ljx;GqQWBR)NGzL!oJ}v{x1RxT*FduIPpxTrx-Z7cecLkA~wWH zQ{+MHDb{FLv=mt3NM=0UT+p;bvMorY$QubBjma&rjgXB=sjuJB@kn7(?3@n-cYXQJ0+?KkR|K!sHn?hwpy z+cyJSTf*Eq^pRPWjM+z|BKa+iNCvhqXEt}^jHOogH-;SUDd0yKn?@N%VF&h64ppoz z{F;=dEv9)_IJNR|hV4Ox?5^PjNw(m)AAizMagLSj9gu$Pfy(0F-x&V)sfz!Fc9bRm z(a0@VXC(myK|?|!#a%)YgmMXy`hk3=5%5hcGG;PHi`D*7$NwH5Im@jp`>@zC%Vg8p<@rSq(Lv|YR$c_IM<8@4I9F1H%{^d_iWt!O z<8hiU4-OQhndB!wW|Ez7Pjo;TG60ige9T0dxL+PoQI7vZm~Mjpk=k%v!u%U4H#Dxrx{m#f|ZrFwx_X4Q;F5YZryLj zoP}G*GgX7c@0KMEjUfe>U6Zl{_Yq)w^yF#lsSx&M07EG(G)YvFM0YozCRWG|7!`cC zAIz4*25;KIWJ%gJa=N&56ifh-qghkg$Y!8UR(yk6uO|(UMy8{L%^8tLzoTvvAf>wA zlUylnWG$-XOcBo}WjEiaiI6=aN*d~#Iz<&$Mii>>ri__9b(yWQ%CZE!_K zM$(76)|2n&U|=45Jp)2Fndv1ZXYp%@_K_qU;oiz1l!=j zi9Vb7)ZFa!IF(+qRa$t3(;$Y=GTfV&elGUA!;VRImHN499Aeus*HLhFcb4vv>pM7) zjBtqb?tAeeLnt(5-FptR>+v$%TFPk_+VpYi->bMeC-Cu z-e1BI0&pN;Ai2hejLy@lLGA+BVyUF-(sjHm4RB~);Hvc=*yl8Zm&_3?@UQSX?cbh2 zC!!a&zF+N9r+yYiCe+%C<*3+uv>Lv$zol4maE1OZA72U`jZD(2yG0_X`g!A}OL{L? zN&DwDScSuj-+EOOxfJ&o>>NbvATzpB^H)gBEXXIRC3ueNFg(w0#Ln2?@Y--~$%I}| znr)KhB6(R)SU*RL>;OUf#Ty zO1Vu{*VUN1J39=ton!r~&5xx4 zRZr1jy*gYIcPML5PWUo|Y>!vsw4FzJoioy{h({JT*huTqd)Tes1j67l#x#5+~BkuI9#Jxm6Q`!~T&i z4F#T{&9BU7jS!^VK~pT0_d`i9#A9x`v{D!(wTlxCUNi3-q`2d#c7-OZ_EMvhrs_qZ zweRsnSb}~5gNa$Uh!4qW)DefYnNqlz*bQ4_s@Trs~BAA^{f;judRJ3k8| zOsTIlaVf8f&@G-a{=zGz^hDTzb0D*SG2)es4~ftX$VfJ_61HGzj>Ws#F50}D6Bgx> zkO~X}B(*emH4QS1rr^!to17d=fjcOJHbcGGcN)GbG)B|$umLw~xEJv>qJtJqTm`uC zDZu;|kFQ@}v%c~-6%ZJJ(zdWX8Pl5bk*Du4YMmW z6*evjlP(ni+-|xd)8$1mvGIl3DSwKN=1XBB2@gyZxZ6}tOhwyZ3FMi%WolO zk#)|ts*fLMnLVswH|cX^jTM3ph^K!N*Qdns#et>_lp^N{whuRgQIZ0cvEIys3qS=R zHrv9Znw4nFnt__D&}1f?Ols(K#huJxba#Ix&pcdy0qksOv)s)kZgiTcoRlVZI?>Kr ztvl5mv!yP|=9RN}B#v1yv6G!gRt%U}1=L2T7`hbdEU7a!j){va`W0!yCvzHos}I{p zUc4^eY3^BNF`QCbCfkmH9|-3&qBCAO{SmqI>ME*gMEAv^0lOfsCo4etn^R-H!cRLd zjru{jYX1tvGv&Kzk?p=TG2dF%>2TMmmJ}rw*)opd7+CkvK5|d5G?CY1#K{PW9zj5Z zGmr0)7F7mR2I>ulgrh9@`?>|C2fyS6(i7WcW~?}lo}z@PI3Qi7LQ6Yhz;B>W%WAT~ zn26cep-{<$2sAYs>4SyBRCe&bFJ-beoktc1LG-@3qMP8Q+`?0n1cStCNIzAv*#(}? zJ$_bu1k(iP+r5RaqGF^)$*9qC%KrRp+xS?2VaQa;LQqS3%77Hpuy7;hMJh~#$q2Dv z!)_*WVYnJ+Ls4@=j8NpS7Kd6-6If@Q^&e9CsZ{jWKI+JdsJcWZU@E4F;V49v1j`-Z zu`18OQ47i?v_4P=$%}^M@7$X4gg5oXP~ylsz9$D9KcYwy#7avrGmO!SWK!$9U%qzf zMu?Q9rlx{Tl<|aOi;p`d^_RyKf^A%dFY-dTUP+qZ90s(2gz6^k@>!&69asDg%5eC3 z+_~tk`O5!da&i*VcWBVAz6U8n3_|hVV;CHyIksH=rC$i)|i@htircO z^x|KKFc4vkYs7GfcX4*epoL?oL;DSUpWrusT>B^wv;eJRet?!aExLeVzP&o-l>RxS^JyILat-H(ikMVrYoef4NM{hK-!p2n+= z*!_Bt2B1c1h))}1M}dCPXUs@D*k;rYp*qwY8&-CygLyFs(cByvhD{<*7-qclov}oX zc2{PNbl33lJ9^W|<5!;X{xe5>$-O2OZPj}_0n;G~b)^v0S~c;VeoA>7IUZxOc;SfR z1UvkDnDVhxrnFxaqL#H#)|O|VK;W33w}`5l33D@U3h}TRU6Z{Ekkk?svPpx$n?)+c z+E8Y>hgq!O`zqqjKZis*=kkfn93^JM!j-%d7Ib$c_3=KEI4v*x)y{#?oGvP}YFm7_ z1^h^6>w@a3hE)9^2aeT44&^dSIOUIqOX@efLYMLMhjss8UzM z1yp+U(_WuWO7G%w3gIAIXM6V}8eui-&Cz*80dvO>M;Letj&;AbC=&-s7cBNtT!+?G z?%_q#wA=bYZ4eqMr%+-}kp@X6CXAIMUq4^oRFx)&-}C9!RGF7fqgspKfznx)h zQ&zCn7Ab+J)YqL_7*rZOQGK=s#`~8ggS37lh@3fd?^)wkpS{@SR2yi6p_-hg3*wiC zJGe~I>S|t(>D){RyD@TTgk17zbxS8&p5Sz$gPMt%)mTqTnPTqg0bJ<^-~5J5~qbnvAC# zHRau~l2U5@s&GFpH!vOnJ^^}G1+R`J1seDAl#eInN9KVc{H_^dGESZT^+IsSkL6|4 zJZK)m73hi#a^nVDyGfQ!*rI5?D>t^gX@=Q-CUJ$lU+Zxs+NrUl2}dN%q>{~y1z;Ro zD=UuC^Gnu@lEr2igIAd(nD~yy49n^en5uCd0m}wyrn_Eq_i~dgw?(sc5s2A$hO3Fl z0@Hh+FbAR3#i#8+E8Zf=OlnwWyQxWOad1P#wlXa;#2%wVJToG22#xl&SU)d2cRbsb z56k#b?2R# zb6)xptoy41PgjaY;!MBu*m{6wx=WLsuE86PUlude#t)SC5ZD+=CuL~A6Z>R+@yHZ9 z*o@ENiqb4HLoTMEfjeYmhmZ8}clhU7 z(v`><(^GE8Uc&lY&C@~KW3(R?=!d!a_59IUbVUQ^AW=C^8ATgo1f+OOftF)E_naG^ zCGR{AsC0}a)y<~jH3xG32Q~0?Q^UK$rh^sT0!(qX`>R{Ut=%e4Jdxjyvzn3u&?m5b zD!4Zkrm*h5U^z9JkCZRUlWgNbJ#+$;8&h?`F5q9zEZ5nbD9uLg&qNny+NDaiP~*4e znU8+0LBX_t?S5Frh`5jZWs%9`W=QF51P;q8r z#PV7&9yRt`7%W*F8_y|F+DPYtJO$Zu;GVIqBj8v0*@mb|3F0yI^GLjqu5x2j9X(IW zaI+|72|JO_XAC|6X(N$?)?aY~^Xbzl&}H-A7w+Q!mAXR7%-&evNJQTdI6vw3FO!r1 zTCG$qz8C{*b07lXgvJGVNr)8F$%Ff4aIkVf*w@o%j>*1%SXMALAp zhPtD?&mIeQPr1Cfxgz%$B0}?~L2@PQ6Sj3@%gu-jJRGmYw=D@78q=3;6O^kKpW4NPx8k68WrDy;^F3@A5j z6ZN)Y6>7<<4(vKZOE-jswO9?_yz#xyixT~6&Co*Nd{QlC!D&`fJohweERRQ$Mq_UW z8X5sFV*~kDso@#Wl6Xt-=VRyyae<@lVGofLjB#;d9}p8yKZEjg0%x2rGI1Hfjf>I0 zIG(VZfu4X!;<5V{e0#%GUvu*T$4Fg8>LON{_}x6j-k~si{hfLN+X(s29n*>=JOtXf z0S8_9C~+d`;5Ge3(qR~+FY!|}JxvTjjEV9&!B)rnjI1>%M{`Fc6o*IY1vvudrO9zn zW15nxu#i`-uU|VkBUQ;=hM4^Zzk?|Eh&6RIF67l@YzIk(b76 z)xztQQ$=W|$b6$e-4*yLWBGy$7W2uMH`1nAsI}5HFRQ)yzn)7%QHuKf^vTaV7K zuHR1gZgIFUGRZXEpI963`gprT_?h-d7)g@YS4+KJ=|?WXs1oq1MLr5GQcW3&T2oGu z-&V9mDY3IiM^ZkDp<3-LU9*NhLW9ZHw=Ic@38hQX9B}qI|FXdk;!&ihiO5L2M-VEC z#B-2dtM(*wwYvH2>e#)vu>m)ZshD(i-+FrP*h#0z&{*H;O5X0w+t|S_gHU^8)_d+6 zZj%0{)$jyvk@TZ|pFJ_>0NqpG`E&)xU+Xtnl{sEN4$Q;Knp2C4_Kf4CT~=&R{%pz+ zJ;`XRVT`%*>bz8DQ}Y3qJ9pOz_c^p6&?Uh+f)LeO%@NC4uQa+EmJ@Q7*JcUel9uBs zf;Y*;5eGKxq{CF~DSN=E30sq^Sinpd50|EjwE*wJ=5IhY5?Ps|nQ9U2Ac=Y73|DGN z$mRJ^kxm_b+9-!P;6XHac{y*41;)tImbjUXe;E6=W;K4z#C=C4xVA3~LU5c86!&&* zBTd$T2?+?ZB{QGXfj09xw%IiRf~hrfgrsdw{BEfZt6+G-jt4f zgT4T4kI2FO@wojj^q&epHNy8jN3l#39p-Dm?X4O z){--7v9uEX_AA|w6lE_G=#30@v6BvrEd6eex|v7TZmJw^5Uzhn;uoNufX6M^kB3)= zfvzQ`2HSr#Z$^DOj6BpB`f%k7CfeUAw&QM7^|g2I36d*mb!7r?ksDw^hfjYjJ6*+k zXG)99C|aac!HHC1y>v?rO$+1fl;ZJzS2cx_qT?2mcFOJ>YC`U{5=|}Qk?aQ0By_~T zjr~k`tDfs}Q~)FW)KAxrH-^?Lbup3Qrp5Dc8^|NWY)ouAo8L4!vo^>KEB?$l3!59o zQ^SmP8z>treo4yGMrVHSnQ}GxW|Y+rRuShobBIIwY+$f?y=OA+EA)nT&y0Pax*SvpRti!Zwc~Ag15bU7;j*1BW9TiC%|7R$Q@}G$S@8FPeqqa11 z`~kV70csKG@fve}T9iWtU%(oaIZ04(#7*c^^v>SAbG&YIfL>S@S*=k(*L>Y^R&SQ< z(MCHDwXG_1B@U-YGj>o;w6K3`wY4do>NV>NwTuNxj_pT$t0yQAy#r@W@b`Qj87mxo>{J3$*cNf#kSC0F=$&LnE$Br-?u!f|GT zEY?|~E5|fkXu;OrgfR<4Hxm;RxdA0JHI@6=!8c#KI(UW6OL21w<;&pMWorWUo;~0< ziR0iz>TqAZRYhdJf=DDmPJ7-#x-iq|YIk_%w=^w9L=U20!IEGmVwNVBEi>vH%knXy zO-0h9juVu{k#}?y#h49>%**sv8UvPuZ3cU2cL}hvsYPe`nd&nRg{N=5SOxt z+cmXTLs|RohSYfn#|AQ1QqKNdQ25;psYxtx;;cmn7uO%Plj(tRKfdSbSYhy5to8T>TH$R$7*utq_F&yMTG{rN)QWqsztO=^4c0K{_f*mb*#;OkM>7 z=&lj~cGD|;i_jdDY;cMP&+*e0^L=Y0(!|3cPAUsY0ZBpFcK|*~O!JOtyH_jK@{eJ~ zX&t*?%YyeZ;vH|D5_<0*a#^$u;VwB89c|t1ObHdWv&9r5ikfP&ZW*GVt!93;W-^JC z&-tB#)|8aIQ!)PB$nYeT6dhTR--r%7{l21xfuE6vtGOkV=p*8+6vXAz9toTR8KlQVgiy)n>M3-Z9S;W%?Et*_V5MVf2Pihl8GgBf>tG$Qrm(;pZ1mmj=PWDI_nbq}wEIDVS925Y#oMVjQoGxcM@Sw=Or zqL7&=jEW`|Xn5@Vu6bCype;mY{CRm2kQfvf47B@4GCk%U8i?}_GbnrA=+j2* zO-H=2d98#F*kpZe5QbBH1#)LguxZd6SmwzQD>$f?FT|3=gQ(V_U5n;GHl_Qz)eoez zP5ocoWFqshfV=J?S%eR?gEH*{*;bPv^#`PdZ5)l1 z8`^!gfusX4vcny!?~@leWHkX4TEBTB)Y+ zb27=w2NO?E)V$+-KG^V#{z3NkGYz>Z)u(AZ)x4w=r29#6fJ2D!hk4L_LTA=d-{{xa!1-4v5#m55=ur- ziU|V_&wOXwR(v*M7B}Dtb8#%5cc>NWUoC>JXZ@4^!-qJ$}y@BCytr0rAJ+suMJlpAZOaCriqKRNYfGDZ3lvq z>RPaR#tPA`X8qYy0`39@_fLapne11&3s80qJC?JSN2rP>$pQkN1e?^Ro|^dyl=hV5 z<0gw2A)S~% z{LjfvQ}iO>WZ;l`Mtb0}Qn1v2-d2b6VnP}LzmPrf_3x{Of9stHJgR|TncorkZs6qj zkA1+1_!E^sV|)Z@9gqR^;XhHY6bh2FF{*_i10;WDn|(Ur`|(4QBLGRjp@uMLLFe>s zhxiuhRmo7Z?es@dmRA6e0Ddko$p=R}+w&*F%g0B?D9_8-^4_wK>^nGpz{_XX8mvAD zTSu+w8niy)z3=5W;Z*e0wp3!y{%(o{w9%$*r1X@RgsXnPlKEn+#0d!HOguF8w6=&F zQGKC&ts0em#eK0>aeXO#qn0<2#GpNU?kj)Q|0=TNMM*|_Og}>hsRMaf@}`JJehfWZ z1aSb_tbS7&biv|+8OFwPpKY;cNTsUcOq1!3KwJMonzb59D<-W%Q8{0Fo(z5=K-n_X zwombV_Ir6*32>=l`E%Dc+^`)<)F^xwFO;M+r68f-81A`e>-cWZK2%$Zy@vjA4g3-8 zcZX6;S+|4@JEu(jRy=Di>%78Bw#03w6k|thElYZf+tl$Jr%cYiwMB3MF~r7RMvQQM z;SH%vjf$27)#!wVuZUxc)lni~`mzjF1U_|BBFRC9a98O%UFG+EVmfg1KZfpi846a< zOFDhmWX>(d#<59eB}VlPHffPYnOth*+i_mOGeFa7t&$sO`GPo1s4|=KGQe$cs8YE^ zp(6SF63sVq)hXIVN_H38$p;G`^L{rO3%$&byh@BmJYKUycCjCzl8@6rBeGz%dW=i! z4%hT`Sm|Mp`E911B*1r0x%;`6L$=EBS! zcz{j>7xwkdenR#n$yT}oUmy+#PGh5!cgq8suF7Vm?wux7#1OUeW|>)5TPTJ}3lP)^ zRdL9<`iAk|`M#OW>ls5VRL6&I%pPjuII5MwJcy~f=e49AtIlY@xav3qXQb>UufVgs z_&{}=B*|G*#+)Vb?GoOamDK{lEZ!#T31y?4luKoc!7plxdiqj>23ZhU(<_mpMmqO* z6TG-((o&9Q1~P)Ar?i?ViL*G@kLL>O;Xx97Hm8$-y|`Fiwm>I|Yx7}llV9$F_EmdK zCR5Ookz(geX%%&`lj7%Wa3}41#e1LY`my3Y`LEMVhOlCBm^43fkz{elofkAkhbEqn z&FbpjIabcfbq4i0sg4h8>&W16iF0b=NSIwyN#sQ7pc-TY@>?PuNJMbuV-q=OqXUkE zm!q<|J*U-X>3=8L6fAJaNYBX1W@Njxgrd1eAU2xq(^j-ZvA#;dy?zS11UqvDz`qKf zUD9=!#<{<{TxHHZd_ep`tPPOGZ8E@uLiKM)`o8@=u>zyz{#jRKC~GSIF?=hdsYzZk zB`_U|9$jjsJR&XEZGi-gBbuAW+>n{|hszh2RuFlQL*STr{N>O4>xzy2TpYy8y!=qm z`Qdc=Vl;D|dpJrrX!A3*0hMrRG)Np#fB;St1F^7t=)@s=@q zZIy8mc@~TI2`70=$iW$-kaD_@9WjR}WY1}tuR^`gB--%?4olGnBeUbO_=lAVV`g|BHyg9|| zD|Vm|O0%}jCQGpX&SgK^YG7-?UFKQ|E*dS6^3)LofSX}KQ^nyd71sGtX`O9_0H zuX&;g5l~_I^^>1ZT&GS}?mV>DQA<$r`oM08(9DrEwka)u#;cQcLV*zrO2C!`oB;VM(a1P6fTB)Immt;wZ01RyRe;S zr~n1zhlGe2lZgB#D%2Pn_9SdC)7EvHmY9?bjrW3y;4sUu)8+xUFyWH$@_ zSiRL*#!tIgr?JznGKBz;(I_82+dKnZxQX!VnWe0?(Q`YW5(D>HvVHVEgn2T_z ze2;%-N+Ar74sSgNizPv&1EUCU1vB)$l%wNX{v!KwdBx}2jGlS!mjxWc%EONREV5&t z?3v0U5J-A#vW-oY1)@&Y0U6cbKpWeyb%IPwEEKNlhdG&UB<0Ld_(CrO&LkqsNY;v> z=!J1~{}?WrFBn^N&}WxqwYKWSzNHcC&-0@>)I7^@B%-JgN5S_B6;yXk{U{%r_{6VD ze^#4`Yf#cs4r}rECJNG8b~4WOt?R+3Td-K-271zEb5AC=oU!jM@zP!r*1`XfDv*URuxsLykvDok zeT~wRQK(GDpDEu$;JPT|p4CM+d6iYXmSvFfOnMge2R?KjB`n~{w>*I&JYcV?%>a<% zxqacTTtxgvAT7RaB{s=c(2v@gl``LhW?X`6FMAirDD^RVk3=MoE^Ypdn?zmIIunGe z&!&WMxf0o8iq%MwHc)qlYyDI->>^gT!29Tg>b-u{jOIhFR4FPOOGAlXEEkn;%$XT2 z?Y?f0g+<9>y&D+`J8^;`q9KY##X)PBs$ZKW4(H;e)y*B4kv)M^TnL-8p+z4rn*j^> zJq?XgiocZT_I{zp}!2a2M03Ck7NjF4_eY6(4&jl=;0C9 zr)FCo)l2rz(`9>*n)}dXzW!<0@`%u>UHe8UF*(|FBxJcFHq!fTSD| z#;AqM|4Duf8G;x=qKIPp$oFc$z_L!LEx7`}i%-PBAcE3;2(fGG6Mx06Eu753x^kViCq=DG{VNCIXHln@X|n?N}WkFo!dB(c;(LF z+RA=q^otxuoId^7y^&Q)kJ!qZZH1bB`+0)v>@S4%$(r7>K zh$52_{t2{k@?+MWp-?TQRN4y-5YnqN5sB5uwJ>8Nw3`R1{Z@(|I;$*>U0LBl%oe7B zU9>22P8yZgTxg7&f<;GY`6x=|qK($Ids} zb;*N1FKGt6AahH+Eh`#h)@D5EsU33?!j8T*(eyeZNGMGVTp(g%1)|)QAy2SQKJ=%r z|C;uDEobx@`yvX_YrB1cMtQCmE0a%Bd>U+2SL+Cbv?#}z!R;QskPY4R4-_OJ^NIL< z_-=`tpx}5O;%6YFVP}&G2yA~MVTpQlbbQ;#+%g z5HrI_hLE&ANAgWFR6ZL+7{5k@LZi}30m`-MRHs6HSB%&j$RFxQ=aIq89SCWf{}xCy z{R7fU+BP#l_b_2@l>!>3_iuE} zc1-U2?V7dNA2C=rB!>#diNw}U0UQ3jKP*0tY#}TS`Sk21Y1nL
= zx@$H`RiI>ZR}|e1SlUgx@a|20(0zCLto^@_vZOtMKfzbf^Cbmeu zZf#zL4NjZc61>G%^L$-kuC!2`732yYNc|CX2s)!<|eCsnF z<N{I(9tJMU93FZ3yRXPi)?32zK9B-|JL}-ZFf%6gG^QoS~1C zhznqnQ)S}Rvs5vf5xotEO%qHU|87dzOomYi^U)+_hY#cULc(7lLK27c8^o+|Wx5|V zk1mYj*&{+lSqQO!y&^HPdoICHXs#;Qn@k>TmYEC^GTi_$fEkPQbzQ#TA_>J8jb;w73_nR%?WcDk4K+xIQOkdFcvx|kb(l6E`#98j_*x1i)}{NhGfhD zp87_~(O4`$aHDS?Ma*9({oV};!Q3F8)3-IOUvI*YB6_2jk<2_H&yu2ElC-aX)5Bym#+>$EMxw+V43-!SpG|JbRJ6#?jz1o;EzOC2txm-;6MTj4(&zDAV(T= z&hU?67-_IP^Z^SWs1UYykX?^f@WFWFJn6~7)dR1MNeA!Occ9?FjEZXx@WTjYjFlVA z&L+_pAJC>AC0QUT7gI~99>~TyR@peg!BJat1lpEh_~<{=vi$%O^lGD3E2`uJ*Q_DA zDqK+ori`G?QDR90Xnm1WgHD?C`O|sZt9mRPAYgMx$2(A8AHyrBdf4jRhM}DTw!jXl zDYOl?CUlk_b4J-ZjgIrI_2~T2DU4*f)bMbj^4z`#A&>zHymv=#VdSgG4C|(IKP*$= z&c~gzE2miOSclc>jngWDq66se2Kl*sW@vvp&rS3tKXm~S%^}? z$edC}1NzVA|lRssS{?Hp<_Dm$p1oiZ!#=8ZEBpVMUAIDcB9Cso; z%qG7WWrb7$LK=tCTHJuSi+=Ga^s8txkrtHV)ntTyE;NuMTxx@K750%tfi@)pt=DzY z!ncZmzubD-FEPkm{@d`(^1tBu9|AhplU%UT_p5C%G~u)`+&=`g>AJX!*kWvB?_*Z` zFCls$H{Xkv0L3#{fboBb=L7aGZ=a9P*?!+BwD{%W3YZnh5d;R)0Z+{5hJ?~U@tg}F zg&T!^#%qG{28!pzp4rQv`iBXST;>bJK=BNm)lg1*>Ab|GWSOQ(j3cpt9nL0w>mllu;(eCT*XCec(sR{4mE2{tPH4o~QEsBZsAV|{Ud(V7 zbiHuxB@^%5PhH#C$y9m~!%+Q0YZzMv_K30X2&#_J%Ts4mJjA+C^KaGPvf=V>MT!>| zx({@ytQ6uj5bs}Q$^nLNsPPFEoXpv|4qc7dXA^i%#M(+YewCkHa$7I0)r7M};Oc{; z4?7FhZBX69p?a>LxfZ$-DrW}CD$tNa6pKyb&N_P?sol2yW@S-wEZ@qB#Hl>S5Kh}A#!^vA+GY7QKPW*6aJR%OAoFS_|v?YJ#OPp#Xoj9K_1XCS6V38dyLwejNs zyK*2kZ@_WNDT$K5Oe(ob(}#ckut}L=Ej+vz_mzlHqb(ZwG9}fJ7e*zci`RSmfK^c% ztp9W0((U|HIhW5*wI?cYo;Y#<5>^}-*t=f*fc!tWSvB+zZjOLd?d6W9@Zmqj-ht(f zS>hERG$72__#)yM{=DZbR4Za;EHV0RA3o;QQ9_$@Kf-x00$?Zg^LJ)xWq5G-P@2#a z;>vppzf*^p5VIXnJFoXUen+QAk+{%D6RgL|18V0{lKK8kubKL;IH`|6wKJ$3H-Y2- z7R>)PZ{V+H@IR};zdrsW;QgPqq0*}LEFU8GZmXSv7FKNI*F!ig0t#f67OsL)FeI@A z5V5byeoYZ|5{>o?v2+CQArP+Sq|uL`c%T_h)B+1hbJa8^PfyuRT};~Ffg?{YpQ5UN z_jITFJE5a3XjyI*h9O(CFF8)x+6Kw3$ZHg|1*Djq{M2*F?IamuQSA~(mg8`=!J>W&Rt==1|=LTrsKT^mm7mG zb2>OsCDcAvH@*drbYhvsm@r&Ww*~EVTngquyq9g{z|P$F-)yF2o>q#x@Q&>8@u(Ie z-OU<2j3WV^VH^=fLS{g9@I<^2P@4l1%va#g-PQPJ1>u}}dOyr?b2!vNAZU8-0~@{v zZc02o2&q|S(o~xt?DH1id?CMzAc%i*U*c-U<7(^6oJ?Gru*UW8tTyc4br}Y$pxj@2 z82AKKwT(v5O40cAE9D7Y-R>UYDU&F#&-ky`P(Gi-zI2V7g;gllo#S@DGxO_C97!6A z@w06pSNfI8J`#+Y)_(6f-J_To?HOpmal&P@Z}Xey`T?!z&$*etdOE4Q>GR1In$w?I zg6uq5ZGym80UUe;Q^}UiU$WVBr!%B91QjsXs>HaWC#Gca0y`|-MGCA2OZrx=izxga z+K*yLH!pF|&u;jRFLD?#8;pnOUqpm_9d(=w7Lh;uH{``bvU1}A{2H@ZaJmdRy~CPX zzMaaz!k_uhR@xe4eCZg$j2zkJ5Yz~f1*Ph>Z(z+dPHP9=#o^iy4cUqYBB{dntCY%c zHtM|awYUII!xVF1`^Ag0h-N%yl(~+<9cx#=?}@J6H`|}Nj086)X2AT0xgoW`j|Q%U|Xi4Q`HPz9rbAe*8vRQ;aV#Pd0r0MT`e*9 z%MRL#O$%S~8M1|vsNs$g)(1#vDA~QnV@=|WNK93p^)|&AYy&qateDZFN!PJHMfc)} z7mj{+^p-Jlr}F^W-2w~o=ZWtItk+0r?p$Kk%ow@DC0TQ_|^pl>8@#fIt%6Yw+*s}$~BfP|vq;CS4DrDta1f?&v zRj#Zv_4ae@I~T4P4)X}QPEkVf4Rn>>9p28(js4SDtgCap!wCMS!lGp zBP<^sayp`8P&zVLV%1~cI)$`HQxkJ`*n!-q;K1;7;G)!nj749d|3PrIW0rE_-eP7< zs!z}-XHbPa5m#&&q<@W*5EzTnS z426Sbk7>}@iw<+h)_eD4i8sZ|*;o-A{gllS>VvP0*9YJJDA4CftdGxu1gG}@0aX7i zasE0tPgy2MrGNfS#%c~S~c4NyIYe0eETH6ho+Z3J}17|c&cB85l09IN2##`zl_nXfCc6UzQXp?fY&HhiZFKSq(low+;WgI4@DJ+*e`@&GaV z%_Xp@GrwenPijKeR(_5BKAwX@Rfvt+Vy}5!H_{?H(q*^KS* z1j~&_UBub_4e=C$9bC&@lf#UeL9mk(WQ)T(+SuJ6`kG1Lq-BN<10R9m&MW+5=WCba zSk|CGw++$?frVDus_ zHuZsg2R|y7a%jTkr@F*<TON& z^~XW9)x^uO2!xJI*AbpQfc>?7!_{J?Aj!x#op zz)Q{nSJQ#pM9aZOdRA8_Z~?yC0V>LfzL>q7FV2=gFudCk=wm6y;7YkI2fzW<77*`2 z>Xir!>a^;>T9(OrftBcWYSDEWBiQ;Z*to&N%Ou004X~fR2DJXZ8;>gNz1t?Gn=Z@N|&e{XaN+tKdkMWm{0p%&ZnOGq#wS zSuJK}ZgGp5nVFfHxy0CFR*RWNd!KXPvwLUunKxrrX;x)f9~t4E9udBlFR!hzY+zP! z)>?!ku`I`D-T20;8}Y|RK5GoXKg(056@9U05JgmEd5JaHzD#jyIq&+sYQD19ZSa7% zR;CiQx0mMGbQt_Rwtzp6e|)h8-qK&1Tg`Dp_puhux>W7D&lp|;$?bL)4DT;%lQ{%p1mgSTZ}X*C%{GRvEO& z4Zz5lJa%*HZ*5`X_%FwclJAoOWkSnb za9yryQseWu$qjwj4Y*qf|0a~DfZQJO$jWael}@*6J*0oT4f&+FyG|c)rvMk^!IJiP zcyRW*hbllwL4+Xe3`Q(%hq#}m26D8yC1Nt6VKQ^0iW8oa5z1Q)C+te$X7x=SuK>8l#Z6g)O1dK3r_=s&amutEg?d%d3a_9mH(D-?OWb zBI_(IkH?CIp=;WDy5l2_i@K89#A0|U!7DC2geV%k+Rget!$othP@^i6oMLthL(9l& zY!f%_XmBj+Vg^d*jf4UTsWgu_pT0Jf`xc@B2c)`!dhi2+gcTdq6_1hN+9v$TvNZ&{k;Nq^3 zgu#UN7bJE|T`DjJ&>wN`C6=~r+hG|{!JwB4#Uo3*qz zt2@?g&(@pIvRp#ZUiOIFc>GK9%IUf@G3R@s6Rgh`eySl0BsIT=U{ zoXZB*^eV+X(1XO1IXp<68(I`4Cvg3`yF(Lzw^nRef(r4JP}DecU1lau(nJGsf=;7^ zsk$k^qfx_C6`-9zr=;04>A%>xA$T+&4wvoeFH@RqTExl*(4m~7wosc0XG@#Zu41(U zS2HV#VAVPjXrMd;)tcd3`v%1UlLd~mXl4lOBa$u)V3nAslugJ=*BrZ3RD2u?ux|LN zU1*VH8o{bvw3-}75>hLmK)Y+My10E=;}Z(Cdg4QHs6Gql}`O#yYAD}QO~e{ui6a$X5-VBJ3?4q+0Y*KCp#%9 z62q?KH~uNCCzN))hLX|bb=!MZ5}nNW@3#6py4k7Q^-W#doDzU>E@0dGfI4sI_929K zlDmP8D_jgT(8JvYuGEw?D1$GHmu%(tk9-v{z}p|&!_NS~&g2Ci0yQpzA)1T7s~A&R z-`OMUz02hdnD%CeH{6&FRAZZu_NHJ>XNJIOl0T=pbw4zsT$pf5RRfLnc!0q`agv9c zn)K*qbEr$zFN@afT4F8c10YFQ({+-0Xs!W9q?^$3rQfK@c9!S+I3>p(2aN1s*|ZcV zf3gW;da`QGTeJEBBZ5syQS*BUAy|S@%a`y~#D&S!=A{!80dx|>x~ZMIR5^=MlvH%m zwrl`L42j0sQTvNd(9-(47d)74qgw;mSV!GeB%qwp#9V~HV5h>?Oit6*|8kk zW~zc zI2+}n;cjr|u|v&ci!y7EuJeg)wv2~|E&HTSaxHZEJMOv=7PEvKH_N%{mzedVw+WVJ zYK!7fQs~7Qa2H4<6WlpNR-4Km4&CnJdmA&6v$hM{=$4C6Vb?KhT;7lUiQVlTUR0~i z$vKNO(^Ih+R$cK1$H3rTz^Uen7m(2)M4VcY=8mF2N*~$WBH&Lejp9vCh#WP4)U41o z(z2#v7uwo7fo8)ViL($sT}c*T+_&HuFS~gnaoAU<;1VmN%>pV>WV8GAJ-$(ngkCo* z(T?(MNdqrlMvD5V2R>_z$JxPsDMd49%|$ z;yIx=T1TSwFN>e>3Cf4MF%X$aq_V!*+0a_^F1to23+UHQ4Se)Fn2akRV?c9PN1$xj zoIZi^m+i4{RL`S37m3+sxy~hVzt}g)*(_8$Ri17`FHp&*M0Z?S;QN9>nLyJMdA(%3 zuLo?!mat~W{X%*MFHPHY5yERCG*OwH`U( z&P)l%3Qq|XlO#F502{Gg(rnUVGH%@@EaeIZj##wfBFSm(_`-a8I+{O9hwE&&h1II^Czynd3zST1$*e7<$IbZ?>gy6g&zL6?SZQ$ z^0O2!FKC-(dks%`dG({~RvudV3>|vn-!%5=EgxN--HnaVf?vFNMg0-xzlYWh2*ChA zxi>|W>JE^E==q8_f2k?HIT5dBCebX#~Ji+z8k`2fsda&4PTA#-trX_H) z>C?13SBDGQ_@#E{5z#6P%vBo#5>kLvoILy<#7afpfXaFsJHcmn+5RV6q34CSF)+b@ zX73WtrEJ+Vny|=aKv8U*Q+-3aW$-pa{F8Ptv_HFB!AJa}S`T8-dys{-+mW--nv=56 zBwPn`fz8gsQn#fo*xo`ZssD+-AvNB}$Vb}hHe{;lt1P9Kcg_}>{lfbQ1+juP7=S-yV~VZ%shHGy96^kiEhw}5rv~V2{U+T)k)rtX>$1< z2kzC=hxkE&%XO5u?irkd|RW_9X>HLMY5C~fyld^$z?aNE}ws)J# z>U`uZx$B&rM;+8^yraZuS8M45eAE;hlR8doX`7T|fqcj}>V&j|=iS086P>ePh7F*; z4TFqvw30ZIPfDV*G6+vq{rw_|-ch)1vq&oK<;!%w@tEVS3XF=Sj)s$zk)T~LypEeD zBHL8LuG&vM7_DN^P<7X=>y1e+Flp8nTObEw7IPq`885#eDEaAn3(f76d{tC%oVb7S z(fy$3o&(1~pc+lW$$aGl>d_s#)G^e)B%&R>P#R#_#|gnRN*$g-9}!%SAug)x_kb%q zkT($YNI1Jbuqd0b@o4UBgp55JZ%|b&QAaDheUfG8c0Ozh2QI`vY${usu*vIZ0d<`e z!7wF)Y)Dfh(s|SA$9uRfMYVvF9^u!Spi?%Hs|HuOEq zRnA^Hzz@1!yof-t%Dv3PB7`8R( zqdS#Qr~PC>d8Wp>4`f_EZihl;c!8FTD8Tc}LT1L75N~=c0wT6XhB%x;*Gw3oCYE;B^-6s z&hZ_J8Kh{4!-ZqvDV^W$S7$G#A4~ zY@y#1?}1P8Wu1#M!f<6=WtpN;D}8yrzDow*#<$XkB8QFiO;wQZf@-$;=eMk68Y_#$ z8NVJNN5G&`_od+K6hm(lO{vyr(by5Q21#|Fdm_eY+LeN)n=*jKGiRgH9s|39F0(Cp z*YTQdHv6%qB9% zbptu|+i6;z{GD$g6|=?@95a_@fz)hyoDauHphnaKuMN@Wd37Xi>%ydFMPTWMBNT_L zKT=WhR#Eb>WA?&ISuH2I`x+i-Bu*`40QQatR7vLDF@&kRaOu!SBoQoWNm>a3HqfNn zSaFk@wvxusj%b(!+_&D9c&ej?MfP*dIwBKw{?je{_6}Nb6LoX(MkLvThz0wMrojiJ zBH_$qW6Gu5eI>L^^vq`@MJcX&oW`ENp@#59`9)3LOp7aU{ERTXww6mh_>|r87#>tM8b7n%0jR+R*Qwshb35_Bq{^L$PZuOgrrJ zbtm|*JG<_go0(u{J^NcuVVvt&5!cf}nS3=@gFBU^1ZIzg(g8ESSI6=#xcA?Zm9vcjCZUC(!Nefik!-`S!U(k|nzICn3On2wh)* zG3F8~GYNMMeQirhkO~P4Vtn%xn-WzUe}UEwI&(Q*G^jMn<3C8;53(s?86ouyT*{(L zvNNh>GpZBxewI3wPQCC>HT{^lS>jwM2q zH)e8L-fL2B3Hz!ds3qDarCB5PB|5SpHd8DAhDLTrJAQ1ov&0B#txTI3;{fec51%ZKJm;qE{{KElD zgZHaov^C71%;3k^w`N#-F3>E4Tqh(RFiwt_A1w(jVnaam_`N2F2w}hUhN59!z*%3& zZjQMo^N&2V-(Vt6z$=UOZ=0Xs&u&D|3sbg>e($Pzt}4M#7*XxBvm{B)DEzt;kt{P+ zOv>g+`ZT>A)iz?+@(=~S_3^+f=;{~qN{V{mVPCtP37*>;<{@{wOcR5}7MpN%plv4( zPC8?PD+W@B$z5bj25ALXFvF4pc7@IzXN(7FEQ;I|@G}PO3c>Tt+FQoeA?_aA?H(f9 zu~C`l(J}0rIM~gp4gLtw^JEwoAd-HHCG#F-R*;|?V^)|p{y+uAAM7v{Ff3)B^^Kmm zKndnGrK~?+IH=!^4VS}HTtF8|Yv8?Ls$9vY79iPf#M>LqGKIk3b2QwcY~W;Nb2M+~ zQPP`^1(Ng|0^uHVxCYe`BHZQX4#ZXZG1djNuEL1|f8N4cOX$bUR;!=&Npgf}^Z}jC zke(XeI6rC-${BT!lbqAf8^$%~241s0qc%*&vgeq~9}=A^lNO)Mxc3q4_gUe2X@K~B z`KA>!YbEQ?&zPLe6KowYT)%@ur@ZG(GybF0nFEz@KgpRVzrV7@^E|*3=g2@nhhI?Y zKc|rXv5U=rhwo*+DBUKuCXS>+22TI3>&i;B`2tyyM$A0W!j-Cus8wGIm1gy{k7?3z z$mY^B)02xYQ;eK+1~$_=3M9X)>8CyQBW{N>PfzRCU{S(${~$Cy`hL6Krs@0n{0s|} zK07Il;@VJ~68uifie-#IHv$ulMOv|b1gB)vLV<Z*QbWU6Yin>1-xf}?I%z1l3<=uGkGYPYDD$6ea6p`L^wdPvr#QK z9aJEoC#CDf*J)=QZPVZas;Dj6gqs)3^LR5eMh_wu(;aDXTV>@>QDy&45NDjxIzG4m zRlXyesT|e`rOXl(2b`Xan`teUS>(>Fb0py$8=tK>79@+&pcKg1y?l0tmEB$4%{*#2Qtbf5@XDf}{xp1K|Qu&DI&n(NJ zxHP+k#|`Rw94okla7f>uPI=Ux+Y6I!7lt5Y#?l#NvL*!Rx3j*ZuuoxFP7=sy%?%4q zmdyDk!rp3!xSw8}rlT4^@uK#DJX&K|Fo433$%ZJNq@~u`=xl>UQL2MO;}ohfc8Ic! zK6_ChJn%wOl1&;Dp$xzu<+m*c-38dOj~Zx;RDSSqJ=tf$?)Op@=0}rk4k$V z%MPh%+^JI~`ji!KDhD90t;;>w(RsD6l!k7##%^JJNaae<@*J` z9Jx+OP(puM3$umV(_bU66EvwA~z4gMOJx=->{=Jp>+WVFSUCfksyy}igA`*6!hdtcg$wlE!m?JNR?N2Bgbz|F7%*eWMFhlTne~1PwL)QN%UPH<%_@i$@0x#CI<_NB8v*r-NBZ^_yQXeM+qr0 zAqxk5hPYBjT+rLLeB3k1xI?odGsb^$sp7szUQgB{2VR9eadqlo%PE^AD_dRL@nY(h z#LLwt*Htv$q^ywfbbGaL_1jBH0_~POmTmJ|>RZxnTl;dwr%T38s-Dds2<%2q%|@@c z`%9!1w2vG%j)&8eDdv=Zs_Z=07}J=Xsim;aHBa>qFKHVH<|y4A{Rncxx!?6mZO2;b zHxlIvyc;Lj%g|f)78g*pOG;uq$X#sl9>K(B3P<~qs?A&NhytB3Uf6hhBZD<3q3#Hr z)HkA$Iqa3D_o6o|X=eKI2pqGq@h7!M4ex4Rj?T2p4`Jax#Z#F=mc%X|>UD~To~3rs zDVo=%r$d_cKM@?gw7HyZe}$I3L4(Z|hrL66ugg?DEpz zc*pAB{&Iknz!$2FIzU8v`B6@fY%~eX!hp(PAEPPAfPZ33)a}f7+!Vr?uQNXc8WhpS zClVgaC-VKnHkC<5J!}AtDS%*nVM<(MlsOwN>BBK;8l%*D6)33_g5 z!)(xXc7m)s(75{O9bxxf;?E6ZypvlR=0{+Mgg==pcFFPBcZdmy4aHrT0iS`lI^pk( zSW9=EAi(eWaRW>Ub4{*5(x?{&Jo61RePzF{`90;^IZZ|@3&wxa1~CP4!C{Ge?8TPk z?lb(o2dR=9jsA+-zy2c?g6yAO_y6tY|K;Ela{U5md?dkAWdVrZ+o0FbwC7^#SOr3? zBp`LJ+=7_W~HqGT3Ohnnbv9=MNz9#5dnXV-r6M1qxE}v;+`o zCN;O-tjq3fT@{NaeG#!8EBbV?_uun(H>Clise8#0v7K)+;0d%#eGiz#F9$ZjB+0Ah+$p8;3!dvqTm@gVq10ez5VEFv7l}Pb#q(1aK_#d*)!Lv(IN?<-$Hn zu74z)3>;GLXfGUT8kWZ2Paf*X*kJMXT&`c&|Bv-a$;9caKeC<0zhP~g=sK9MorQ>5 zLTahvH7r<1;WZAAmmo42=)=*01igrSyfn=BseZ((g29N~B;|9)^L3N#npe z0r&3-^-2lSD_GcA*r`|oKjVVq{L}ow2MvFKe~DXI|7}~?WZs_Upr+JeWc!%SK&|s3_6-55F z5SUj$DuNA}ENL0RbTH(Vg5h8hu373QwtMH%*#}T<|2Hwb0(l8$FPHF^B-4IQWwImn zspmL`CAM=6GmnIzxnW8hACquCj-n|e(xA5bCI$ACq6Jz@Tb-iLCq-5j}U)Z828U&P5IXt zum9sL{G(p}|9uwZ3~Wq{|C_|+zpS67YUPBgisdVpZklkB9HJ2}s4y7bmeg2E83Iy7 zhGi{fW7*{1H$O$R8h<`KC3Yfzoj~|r&cbeBJRCE9Z09)mxLHU>MwQ^e%w;y?<+HWU z<@fpeN(i(SSV9tw%RRka972X~)QV0tULTFVvBVV98IASlD&A~(1P6wj+@2+}qtg#% znEF!GwA!L|gnTLOhU(1vN@>?5A!UZuyLtx}ejQ{Fec6HyOs?k7_@zlR`Gu5g!zs09 z_dsOk&zwRKsRU!PO^dgY=n{RDu<&(P_H~T90R?8#+Z(S!n~Bo;FAz zy2=+xw{V{6+(5^q=bbkA!V7<_kLy;CnJUN0*e*Jsq6urNnaG-)g?pz@S&vngJ605r zce+93b=CQklsbk^#<7u{s=<{=!Ayon~0%xl3W?i%s)StPf#?KfLxaL(mdXa8>btim5H>gfc!@vgUI$ZF9R${*&KQq zxRl%ko9fo?%_Q8Yj-d(!{WhImH?locJ{Vd}Z{=5$FtiY!I zy`S46vfrbtYm(%BCnGEV$eGIk+AqTO1XfF7v#fUeg?gAsvbR+kH1{*Np5;50thL(w zPT-y+l(CfPacCuO5Xx*UJpVv1i~5v^K;(1+;)%xyYt}^Bw>E>v381oN!a}Tv%s?Fi zHXnBvS>z3}4xL(A^+o8goTPwW&GB(*I0mxlFpCuh#FwrK>=@Rh7Gl-Sx;CiCG*&_L zCEbagL_pc9UpuFkLD|XP9mHIe*o=52#V@@QEX35Fd5Ky65FmLP$tl1I16UZyzA`?T zk~~v0{`t4om}?3sbmg?~d1h;0sO3I%zm3R4Uc&64E2NlpeH5z2%Sf@g1xb5MLeI;? z@8Wwnp~i0EB{O7Eka-CJL-4x99|FmIm10tq>>ArALk<64hy5)LfqkJt|sRo{?$bth%lS2 z^c}NFg-!9S+6`gZ_dqzJRkL zf4+M@K+5xj|A_FE#TwuwnzEC=+Cv1%rJ&TZJkK|xK-7<1tmr~x3G4coZ#O{;`KS@7lL+`OX(SYW_@6}v$S@8c3 zV%WATe9mY5SZ9phxh#D4viJ~8_e(s?F}|11KS?QiHUs@6hWk{#duw5I-TQ6wl*HJ) zTVdc0ub=Y%oAF9^^{NV~do$>>*AM5ZhOs;Fll|(;sY?AXah|gMFL9sr!JSAG>YRiv zXmVO^Vd!2fsdzlDB&M6=J1T?D_D#`FM;rA-$}5S>El8iAB?Fl!+-v% z3t|!d>KQpGweF+boFPqiBzAW{C)pWQ@>mjfk$}Ps%J0XuXJ6-gCEPHfjsb ztg@f)U-X~Q+;zJ6Ek_^ct+}ilj8X$Hy`r)v4#-YtM;H{9xw5CeSQDwdbu7OCWVHHt z)Wn(g2wfK*o23M)$XvQ>wIniLc|pwvQ=1 z#!0Ci{T>SHx-^z#4{wa60dtnDAiA^q*}~X0`U_Ph$Eyv?sYK(pEhUN#ok}*+O8@wX zWwpMB0oSc+aIQ;DEe8Irw-+`fATKfR6i&59Wpe^8B|V{SdpXvv3h!3e%(^ke8Fg~4 zEiu7ZkMfw-!^zs2s@~%YpVFAw5i323>Y2@?$ZgHSN=&JyrL?vlCroG=Q12`@dA451 zPafEg_AGjIo#4frLSw8e$Yfx4;QH+{L}YEu$%?T=IY)?_olto!M6zA!GsZC9vs_qp z2!7$zv!klN5bs#pka-^sEHh=?6tQ49ZBkR2Aqn~YzQ~HCI__4d%L3qv_=Nd3t|8y# z`K@1;2Qv{K*>jDF=V%5nWXR*C!4VSw5xHACwoXCZN>TE?Lizbxdcp2aLCxMuWQ#1K z6}>JyJhP;>$?=aUnjK9l4Qeo}Y6j?T$cmDpDm;bg#TrUwi>_CR&kA+nra{jremGsR zM)`xb&J(@7wsL5^T8f;4?lN=gMF*7=fz{IMbYlv>5NKGVdR_2HmJq!G3+gS<8m1~+ zMzDX_??EqFyguBbF>LA|Iw^aT)!2WH&-vQr-D{b#_iYRw=WZDXQ*%*w!%rI0=|kD$3N# z*^yu8i!DpIEYa(WwW_#fT!OWXN}Gz?m2``0%Wn!@(UeXKvsf0n(b*O}m35;+WfsfY z(b#Px^iDhT$5fcp9X2Rcv!$++3~6e^h=0d{VODWr^xBRNygQ-&Wo*DG*)H2AhlV3iL}2bq&bt5t1*;>?Uga zkZQS1Qgxw>INZwH9$K0gBHDvL=SXw_fg2ly;i-ArhOu)n>ahjH~IPsBk9kiHNPXi&z z^njy4h0CY`sOI1b+Lrpeu?Y&hQHXaS^kxcBn{uvQ;Z(a$(fd06E6}Z;65rx!&xaD* zQBoyAqVGW(L)aX6;qtP}x7WVLtTgAC<5R03>F55uPRycqX<; z&zO6Nip^~59~d`H;f%msu!bsr z?7~nb0{Lp1U9S_PH-FT3FX<<*yi5dO!_=7cAk^%ApD^`%gQ#wxTz=JXK4Yk`e&;)n zqPTB`2gbOeQra3)#DEBRV;}M2uswxxX0_%8b-{6=U|TM*9(W7D5iONZ@46^@PZffg z<(pGK$rgroX{TskINKsM0dr;;(+f=g5R-p_6G}uB83*tL_|KQtp!s4=jEMzZQrSEP zi+k@N>$?wP5?lw1^X|-=ZpfjEL}iu?JFqGt=eo#6vG2i^N(mQHKO&8IHBm!Kk#M@*kRuG3=st1C$gM#g-Sf+&XHu=yeOxmf^5fR?d?DdCnz z=i+&ieYvgH)na?n_2|j;m|*NpxV6tUSL*sDc(_!xt|H=9bkvyu(IqX6zK9g$T`KfM zB~}XP;y{h`{Id!*>{Ut7@@yxr@Wg3=ox5!~n*&cCSEFtEYFK zZCZ2hC{9XGJ(x47mHUf^2Hr1{UuB_wPmgw5)K1fNFa`hGn8ss28H_5!x&8qd2ai0& z@KN(Bi9YL!s*)$d90<`Ux3N}lCptW-LVoAe={S=!s@ffSQ`DMrH$53EgV;0tS5EKf z3f?ugHLc-1^afTAmY3EKKZw39Sc)Pyj12UcI^N3f5#8Jyb6z`OLS9}B*1Uf44P-sxt3{sKOU{;qM}*%T!)O!MDI zem;Zwdu4BX$7g;QWq4!d@(Uq))^z;feLsVB|K45dZ@b(g%rfwBfwP6^9y`HQ9dgYl zbIv%s{^CQ+!dUO!)9GCY&$`|1ZE3-t< z(X;?Z*wEzayUL)8)8^SxJIAC;%cP4P?JoLlURA+{pef;%U&}}Gw~bt<8THl|)$~U; z=iRfFNV^|2-C@Afn*ny${qGOFQQ}9yfRJ{~(Mq&+l6YpYsGnH3KmB_6B@Qotl+$1@ z9~In!CHoDEQ&1*QYk;Q#`Q}kyk4x>^tM2L#GI$Jv>QM$&6S>^JPaC1%O}yXgryBTT z_w?Tb`8IJ=+P((+TVKpYlay0cS3JDbz3L9=%;~E9UIu?Lm~d3&k`fXWq**3|4Hv zPXYId@E<$z00Tw$Mk;_*xya8zyFa=yr)0_8}9SV_9GxID$O z-?Y?XDh(_d$sA`0!*v@RHrIM4t(6SV`hUNc`J{Z*8Dwew7lYxNvv+?J9ta5Q3lIF~ zWb8j;zRKnXjwZ(cjmKbN;`FbxW0IQ9U;UVU>48z=zZgQI((sh=n2gG@`vsI7*eF$T5Uu44w@dox zr4G5b>sz#uGSwyImB5ML&0Zd1OaNAYJ>hp6^aBRpW01c4p$zF0o-^qP3xB7wkr!n? z&AKUX03~`*RvWNCt=BmvT&EeCTQJb##ma}oTuHI7Rs)AG3vL85OT;i1zHj-6D zVa#r5uNWugG9lvtJ@cf=q2svvuub&iOa_x<4_*_D1FfQ3vMQbzwe0k6m9g1dHoi5X)sf+l{6+pPL`Gr1E_U{!3uD8 zCCs1DNPMj=T5}2DbQbKfvEfnHLCn@AB!$&h{laQkWsJlIGS?1%z+`&J-zxDlGh>^<;p)Ab2H@-B4;QKsxj9X1bHs!VfWguW7C^w9v3a+C2L0@O$5>7k8-(Ve!Q$R5G zWvQ_^=KeHKFpaE-AlUnE7C-^ReIj_7x7TKro7;hZpn!T#T1@G+0M|CXHx2E=u;My(r2(wLb7>aU^!g0aL*M4Jb=c&EsU)8Eq z9Ip!r5`MeC4NhPzSqgrRo}^kgf+4@g@g>~)rogB2&>G0B(lDqBDSA=8W0n0_iup6* z5!Z&B%BZQ)lN(Kk`Wj;Z63Y4yQ7cZ)hPb)SDP22xh4P}vqSY2wSsqEhwHO07K6#JX z=#}OTk6b&sMwb-HG|&K?Z&u%;EcB0Lo}4{xX}ZadyQqY<7VlHh5uV6bp}khMPU&=vl=s8N~ku6b_w~c0InE-O$s0m6N&f(X2h3_-9Q*O z>=ZcK?jdVM_%cUt)dxXe8cf%;oTjs@0zOZSw^@9p!c&s}m+x@qNC56_wvr;kd4V0- z2ZjNKLJtgD4FOD3-#vlm`u;8GrKj_55Gs(|PoTk`V-oGi`eV^mXK|z0q^|y6T)67N zPay$hb@b2yV~K3zjAl*6X;{>|(cAvJSEkWTc)MTpTuCgGz#;A?{2tK1f#n=I&Jn!! z1S}MK`oV3v`8w_i|H{uzqDDf&d=+;d!TzT}`;W5U%GRc=mj4qK$%_A5gJa|q6c)C% zNu#2uYE5eyqtdlVkZ=L3P8TjNV)f!6Pkw;8f46>}7S@-P&|3-BC~^n%DKF9tDkL`5 zeRVSP^Nrkz-pjQ6^NAZmq2CvRtFkz*Pb*#R45g;5m3}9L6&GMF<)l2643o7JXWQdK zQlfoy)Q);*(d?1pC3G1<>73s2GoC!GmtKq}OQ;mlqAyDTj33d_(;HDm#G#L0)l za~x>~Qw}RJFqaMs!>XDAXTVoFNWAl4Zd!@XzSXT?SpCM?sV}pKm*>=Ab>)9Ks8O!%A!38mJ5J<`82b+m1!>$RkEl8!Feb z3sYkgVt{11k}v{i@kd&DkUr2l;JN~OxDk5T8#NqGYw=Okefzpk01m6Zfptnvo)dXs zDF>7Z%P-ER`(TZiTzms}97gK7U-N>nfX`F{PxAmYA@7N>+Y+f+H^(c%t0Xj61viO& z%wpA2!Fawt9pfbL`Rz&6H9_wH+_Q)_Bf*VSU1EnAL$X1{XwU*2&kq>aZ^B#Xya4Zd zMj`Q#hmwIJl~iU@flOwaQM7cLQ8qPw8u(*T0tZ4-7`>eqt<|ZZ^wCCQ9VlX8v$Q zf@JoeC?Z4Uy7AKWSgt3gfp<2)s~$Ej*Y&wCWX!nd<#;TkoQ>DM=lUs^%sbIrfyE0j zE1Uk|d)ydto9=qdyrh4A{SD~{yb@#Qvl2bQf8GnLXL-_ubx#xxMiWp`r$wq++{wr| zT}87Ynf%?pKi<^`xP0mw5w(m z$xTU4w~p*;&ky3on76Ku;WbO~>h27{FdAl!i9S>IB(2IAlZgB=wQsOh`{x%+@ebt* zH%GQhBrDgC;f7{9846}&nNjG@G?Cc1wOd97dyPwlV>1BJpUfeTC2fh0;ptb-v2n z;&~}3aCpT5wD;98MFYf>0t+HUrs|SVm-Qc;7nKf55S!7S#8!oZCWn zz`uwJyHebJR0ceOXfN1XPWoYcQ=z`W5O3A~^c=c{ar4r{#TjJiIF_AKX7#c1^eK!D zV1X*;&O?+O=;ts=4Y3#tQ%w6*>*?hEJ~`a^G2Ce9G|L8V;6zI!cznJ^@;QkoWxJr@ z?*~P`T+Lc!f-16?d*w;&T3f{9tEov%djWsqJ&gO@VIfmPggGY{LvaFDB)xYH%UY{< zAou~burXFE5g6dJNPYb1gjP>rHaJ>sFNqbdjb%?V14o>>a}iRDm@5TDUqXxSN(0KA zr90BzMLXoSROSo=??l=$5#Y;!*47C8Pc1=hZ1b+e)FzY$sr#yi1d6kwp^jLMa{e%?O@6V0N zUmzp__*jnVO{wgQ6B7QuEXDJ@AWizAcb_62q;bTf9APKxW6;^P;<6E2XPkA2-BHww zu^Xb=_C7Bo8)C6kAl#BX;4oGB8a-ZC8 zn&A&i;8ejMdt|+w^jMXcGZ>(^7(cndlIehl`-XnZ5NW|xRs)^GgLJ5YM3N--y@UrY zV*b>nsKq7gUDK_7KW2bfQUG#I=(SA(+S4(p1vI26LX+V=ZIz72*!fq zJM6`t`gr#e+>FZctkp|`Dw{@8_AKl6{|JrMA(W;ZOQ(hac35VZ%MlSf;Q zNjGjCkz8M=a%*aBL2I=u*WoqsNp^6eaAQ^vFolk5Zlf_pb2-&EZh ziJt?tF|Rs1=OP>r$acTYx;{}P0rih8q5g)jkGvy_H?y>Khz7>7I`ITeh4{=z>807$ zR{?Q{e~Xa%X_)1w2)!-)^zpaa#is19c9AbV(u*(d{6C*M|52apzi);_OblJjWbDk$ zOdJ&)?c6>7O^ry9wfmC23;8XJR}z-RPTJ_5IMXbUAmEd$14SJQf)byHPL%{x64l^> z|K`7h5J2)73X6h54e1N?9fqmni(mcC|4QmCD{J*J4ZG$`5&Z$bmz`TRwN*(FTAfa3 zz=f?UsZMbi9^zII333%-;HEf3;zr3nbM50sRo}dwf zQNv^)^iJ>4K7!pdMrB5$Jd)@Sa4nVhj^<4$<5PA%oT+A7y@mPRMt?g{?AKl1L7~5H zE-Pz$z2{vq>(*eOfHwTp3F;gPi*zsU?15%hX}uGV`^7?C`wZz54h0ygvt0a9y~mYz zAD*`+Ee1f92iZJGi?*hc#5JK^DczI7Zn3t`w$X0_ zqWbU#!{hxxdq{LP4>%umWBwnkai|GG9nflbiPA2NEK%$t2e?y*1~`T2yU!+#xW-@V zc^)>;JOmv#-eX1Ta3aD=*&s%|3S8B{fGJ(t;gwab2>UA!~6|TJZ~TbFvDQr1#=Q`(^dVzfujqD55zew#PDe#kZC)?jO@A$4?eyF76tbvOIZTc`=daO$leCV&pX|U~cN0iS>S6p#vv05<1 zIwNdzMZxssqw=Kmz3aMu%zsI{2W%iZM6ee(aLw1F8E1d#RJmf%y0Ka{qt-hISmv$7=CD~57n z+<Z^q1^1eN__Qi5SQ{xrU2@2SNVa{K%_8M)`-*dZi~8Q1$lwfa#7O zr8q<{xGE)=NE4s4*zJ4OpZ|gx#-zjhmrt1O{3E6P?b_l04zvFWvA9vmUIEnLk6AH8 z!v@VtSHZp@3^VQKx}72_$SZ#|jMnrb!}N4=rm#Hj@54cmbY4INk~|5mCC)@lb{y;- zHy>kz2dmtEv^1Iw3@QTnv*ac+W1kV)OAm#_iEHXlvdFr&06g{+757#Y5y4g|it2a# zY8}K#&9%h9JRFhh$Ew-bV{I0pv~3sq%}qDK^R#kYn&|v? zn6mC1UQ{!J-nEQln_e-bAto8JT_{7$5I&V25wX-UgEUHy2v$RIl7XqeCRHlEEja8x zd%2xIf7QR2L#+Qh!~W+N{cI1GHg++#`nMEP`qy?C_>%>j#Ofbip73o;z|&pk*L?01FL&+52e0opZy0_w z&*HspM!lM^i&4$KM9V) zqkB?cj$Dk`x^6IwXK3M|`6Kinqn{<0&r2o=>aNiqa~lWf-|fPMe~XrB8c#A-I?DJh zMdrI{f;o*hxnaS6f2y~-=4k#6J%}0tb}!M#2tOm;(!+T?Cta5}r9-g1`CO~0%@Q2` zV#=Dt-K2NFLyFd{v3-}b(mD8Zjkr~jD`lJW;nb-@Z>gtAIi7r>d^mSv`<_dJ6mN@h z_4?3vt!FOtFf1SLnp3!ti0B8f>dR*;n;UA5eBzizrjlxxaW<;`E3tkM9%aU82{ffO zX~%>u@-T_u?RSksY>ldmxPma{R?~@*jW}z7_#U9)%sWMlDA19aCehHq9sVyo35PN~5ske-Dix9O^Bz3Nf< z>38<~dI=EQYMUEw;kF&cd3h@m5z>0h1djNF4@g>JuG&s>jT5r6#7a_X(3*{??HIQyg6*J zSC|gO_#i{f1-StB4rG{@QNf)k;ei12^s7D1Z=a!AW%)kdMJeJzDe%|8ke)CRau9lF z59klYb{Qk`-wSz?Dc5j-(yNFgCe-0kJKx5{V(kv1Z0?^%OiJ$KYj68@1zg>$ zNVp|u#IU5;jyaSDXmLLG%bMx`ie&3u|2U-To#4+*=hhq7xa+6ymLEY4)aMjuwb8sC z*i>-hm2xtiR{~Y)pV20=L;%D7w-PG{E@M%fr}Ko5C6yx=(2%VR8M zD65ZpX%5Yp1cWpG+e+q(Dm=wjd~|1|u?M@EN)LJN9RIJdjDeZI2BKhAIdT8g$A6am z|4SLBrte_$KVX*h-(c22+Q2hXPdh~_FGcaye zBZYQstUTxCm&gAQVzz(9s;{~N;Wd;q*f;tkPiTC)9Scor!~Dny*!?I%_&V$0JsI}6 zj)dTstbe=9mO;H!(oc7J2wvOF{^>6He!s=K9NX+%*~N?sEI$>+;ek?$^6xO=N1TrQ zp0@;lk;R2+LAho@L!Gk!Me5LQ)P>+iktuG(bg5Wee2CC@BVrpO%&r@{HC^#%M=Q(z@1qfDhK}nZ2<#yMXd_|6KJ~EyjJ&u zQjG)MD8ty%`e_Ao7GQ&{HNhwxrAmB3^SF-8a?3NR!kA!Q=>pPZU3{{8Rx&_;kk$~* zA3$c` zDwBYInAA8-en$RlwjrodVm7g$bh#nbf5G}X-b%uycXb_61ocv&8$=XIIWW&JM*t~Z zz9tkR+)sOFn23;2PY{*I4|u}s;!KKdxgn$RImOqgBhCG0{BHcg`(|Um!}X#WrVDxH zR1C$ZV*9I2!FDm-+U@}QD@{fkKZaek-}mOLFKa0QFINKaK4B1!c2M}pmo>rP92vfJ z&RnKOb}n4vOh0JBbtYUrV80XY?qdIpy^?^F8-D!8UbJnF@pJmJ0>&44!Li3gFVs&mUPwi?n+rRzIbJNcF^@H^d>^A}sl#}Kop;<2|By$|%uBEP1SyOy9W4D9{^h?h|ih9#_;~P@HxGW8Z*m(-q za381QmUeG07&i>IM%}OY`IVF1{3vkQ z(d>Sgw=K&&g%YP@qrO%_QSDC%7Pfj&vGIsrCS&5M5}z*9s_eX@cZcm#cwTZ_(X=C< zejFn?JK%<$Sd|}9H68lm{B*6u|;crm7`K66AHhU0j>WKyvge2qI5 z@cQN>$-&9W6~?Y`N2%`3P+mASDs2sr5lW3%)nc_Jfc}GQJ2QRi1Pk`auL%qH3TdYe z`_XX{Qg&l#DluyA#+~$7+4gCq_l{gqX7#fZZjzD-terPfvUuaNR7WNezqS!Kue>f~ zWtk$(u0A~FiXWuOh9m*9=5J`gRA4m7HR@f+U!=fjP-?WhpueaDx|dQOu-=3inq+hn z4V8=a+yo)VL^Cv--xj^o^)i>lemAPXXap;IQ(qG0a>U%(8T_~zcoSDj2ycyxu3ycWij4j=BM5e zHptt887Pey>B@%9?Q;8l&iI*5#~gO~&MvfPMR<%#^iRBNAwz|vg_^md3I+U&&?|g- z<<}tc35m~LCRuHfY(KmJK4QQqG#> znR^XF#Z+IY!IWXt$@BnztiZCUcJY3I2K8cR|4Iz`ULbbE7+C-?n>=3bcI*?b=^C!0 zp|zAXbNA~eJ<8?}oRc?N8Rv0JoR-W17r*MNI~gr@YW{@KJFr&6U^MCVl}cwoU?~av zR1Ix=901GPA^F9rtXPICE0PzR^{_c|DQY={AMp`Yk`9$0DFie^iL>&NPfpP4_ab#q z9;!*yV{6h%0n+zQ#T(n-i?wqmQLi3i>Kq-^@mo^%JyYyWuete6KeNH-QbNZE&%vAM znqHKYQ$4ahnyESWxIZBI$c-UL07ULb7GI?)vDQ-(ZRDC~GSy(8R!Gz`Hn(mZ728qj zjbP1J98oZ7@STzi2nBgMlzG0LK#Q>9ywPOo5(hI2g^OF=RtR%X`#VrXwDl&yRpDZg ztp2{N;S%e-DPt@*s))(#)n#)FZdFf0$$&%&mSztW&N?mRN=@hDU?j^3_9Tzd=)DSI z(SKUTgtZ^-E}t_^oyLBN-tm*NkhDauDoMAK_I^w2k;4|NE4McO;d3vE$k&;)T0x>k z9Mian>5aZoGGVVvJ&-~lFAyjV^U-19?w&$rHYa#2PtecQ0bHNa|G}eJ~JJ!z6JQz2RRRT}84Cn#mZf z@nb`mr0+#fFuo~anjTR&yGLibrk{9BV%v;RnT#=ZMGD3w7fK7LfaJS}(!g~guL_B4 z3C}(ujt-=Mjg(J{5V)b=i);bF!yNI*GgY$p2)2GM!TivqL`(5dP@MOGZe@W12;=6* z@WcLE3Kjd3C1sMi8EA4xh$jT+is!r=rWu2N2S_&$wp|(ub5J5 zOJA`@<(G;vM~x6F9U3ZpR%)NI29JZH=Ob{-vjLSqLnb*J`7V~8{S8kH1{W}v7gCmz z5zYfrtG45tBI5$HiK5a)E5h1##-!v`kM@^I#o~{ehRk+W)c)%C)$Mx zQ*A4TDEH`wjG@a)a~nm+!r&@mIOGcNduYtC`$0!!5*if>%c45O3GYL-ED?RJ zg^_X_JFqYH1sV;8&pq?H1*&#D-xOfn5W(8$VQP=k#1(8Zr7io^4 zu|p{|-IjGP>7kqAfDIg3pn zQ4q3ecYvG)9m|nO9n@8IF!~w~5yb*ltnkIf9+t?*2OOmL`uB1;_D#guGD#1-t`mGJK5dBOj zk*gs>-eQVQpp?JuvEUw5Mtq3tckS-1`*5@{Mv)R*BNYPBVBl=l!01-V65qUks3#~NN z8Yc733j2t8BsuIRY&?*$6p=pQ)U#z?c zn*5u^dYZ>oommGnd-DgiR6BAggjvPX=dq`0!!%nsBR1_mhl}s@s@I?0n#b!j2@t@~ z76t=WKQF!Pfca-PyO7WS*cE?XE(=XMc;J48Xwxi zY^J-h!p(?F@$=7g>OWAqsp14-hke9exP>jA1xfp{ojZJf)zPt`0JoA8oQ^+93!aQ4 zt^O2Wnd@TZv~~Jh{lm<*$d|)gGGIC3{0zG?GtA^h*G08A*=d2gpiXxpz4qFrOb?1GZ$)(#RATJn%T4XYDO@n(;g~mf;@Uuad`>bVFr^9yLAbLq=aNM1p zRe7({fMI_KP{jd7R2ZL{yyrd{eV-WjMe>gN<-xEU zdy>1?Vsb4zVP8!4;odJbjSGlqO_#^NmlR-S-B65rhHV$xS*=ScKO)Z@iZ}i z*-L%fZGieoqiqsNn|mYoT^DIaU|Pg4J`ChAk;r<70HgprgH}nzrF#zBKiHWotPVLKO;(0;FvWQAIj!Iv#@cpXy1Ly z{!>tjGt(7VM`GV+4j;CwbVE3?>19-yt$mMxhsC`ZzV~%;-VUQ6&Lz8`Whn%_4PjTZXmjLG$wU$=u$)>?&-S^ytvRcw;CX zlNxflvt3P86oFDywQQ3PHtGl2UDO=nT;|^DSX}M*fmwnx0(^q;oFMuuHomLXry<)O z_|xAW7zA%B4CWU;y+uXZ)d^xs%sAi1&vLWTRBk%#HmYA$#_WLz>`rr%#89~o+?b}{ zVH(IPJPNJ}Pzg*41!=VGL(3~eDK7NAN_@h1j>%;z`FfDfCHE&7ZcyHHhHMwa9iZZj z@;e~CzbMe5BTDfF+3Q28z?8@~enm(6jxehsK>Oy5p=@>ymDlK&Qe*PPa()1Lfi~wI zt;6~^aNXi?XYlFCaA#WTee4sZ13tN9ca}2jH>&18FQK(M3nlw>7N4OlNYgN;ga~yx zf%%A~%S$+CF1P)h)_$77;3hMUXcYaQ{G|beczV&#qAalsQO9kYy^uFmI(?oY`HAL+ zu2RCc=Lih95K|-}k#GaN=wdo+6iP6KD^3^vN^yjF0;dM{0z;ON9=!BcJrW}`qC4wr z7u&M*&(p;~E{I|%qb(`F^CKCsR{M#O)J4<3{6sNttX=kK4wo0tNxr(9tWj8$v+z~2 zh|+iwa?mL|75EkF9zKpS;?_iydsNKcfL)+!sykc8*Y1+Uldj(y(k@Ae2jk2+%z5v@ z@6<$kdffJ)-@qe4gVaQUOM(J7Ey}VfTtW1_8-UTNcDo#yN@asKksJ46M8-HEHK?(2 zleAD`Fbhwy5cLWKfp$WQcBa6EBnhdMSfc;Rd^&Y!^G07$7+`B0`sx$PLl(r7Mp&lV z#(jkeB}9|r995P}xc;1@xTprOQXQmc95;q`3cA)NdzVl(2xlGrwJ=hOsE~NC5w!L@ zfw3TV(GUjbdo0X`MTJN7`sdm?amNCG=O#&zIbJa-p4V3&v7I;z?QUcxu(Dk@Sv0fT zzdD|iuUr9bpJ)AT(EmT5YW+uv_>bz4q^hO%rxz6*8GMHjQap$X2@p@1u4$`WB18;{ zDIo71_4qX zC-`1I$KNj;W#8LxRxf~}4K_lMBA?}d!0?^+s)LzgsPLQwG}Sc!?sat2?Kh_XoefOn zEf_>cGw59+qA0FtKu!z0Z7sx+idsimTktcCT8OQZn06o%0j?&{pO%lg=elfZ$VL!8 zI(4t#w9)_SK)JnShYV~!PMu|i;35W27arl^{vMW6{uxbK%!>NpFqt{8_(rT$pj6M& zpycCqPh?YGuALs8H(ww^#IrR-6%GQ`E#Y%6zf#YX#oSa#g1b=e$ebjRF|#6VEQr{k zDIgh3cyg~^Y@jAH)4%Z$&07^=506;}hdOQ2qy;G)wfM^1k=$ZG^X3`0l6q&qppd&k zjiDK8cEaCAvx!_-VKx0!y^T-wz}T@UsqO$ZvG^`CfdMR|>sioga3XbBz6rMgBo2t% zmeXn0T(ihgvU{Ve>v;i+*$`H#n1my*3#C@cNC}OV){r3^i@grYn@2`MW={6_2#MD+ z96)``p+r9G;`T;5IBPP}Kpym=Fl;^ zDnthdx~yjWl$Ll73I5|t?NJumTfa{Pw}(SInFEwf8cSw#;pFcks~#(OP)GNQs(x3tG?`MBy~MDyXWws^L0QY0_;;xBX{c zZ05J=z`4G=Gz7>{O7=3nqi&+TP)=cT(!GMXic`;*Z)HS*gDUN1+pO(Xgbl+MmK^?U zU@rt!uB^9)0zka^pZQZo+s%)#&}-|EmTn**R2y@b=o<@{@Ea;MwmLjk;ZWQe8kAAN z50@QwFF%42MEzneYlA_|W-pYm-BhJf zU&&dG1fdhn)3L@+^N_vu9NIQ{LpHeUKvO&c)o}Ads;1B}jUU~i`q8Z=pRC{@ypoN| z6y0TGU?CQgnIvic^=Eu1nHQ5kv1zegdRvcU%E^;w2_ipNPvt0uwe=-U&#VW|My+uo zt&lbE;ddc-JgG%Mg(?#3^w(4fgq9afPwtirqLya{D4aL)BclWTo{I0sK+`n&jdvI&QYA0?GIY^1`>Bz6Xin=C z(8L_O&n=R8NF)LTtWry|l4{iZokYfq^n=e@ZheyQMx@MFrOaCk+_$JkMgY=?_G#^%pHf$rGQO zEUp7vQaEhLvd>E`E%)0O%5R%=DlmTF&FzCFK6yVwRm)#5dZyHzSSHSU`f5KuR1>#} zC0?x#zMp(;b3DDGXuIXP!J=NM%yj1b+A?2zke8XHSe>qEU6-<=6X@?wcK7jPa~I0< zeSpn>e|OveJwOZCNvNQ8R8Bb^>3&lLg@|p@^Pp2SVo0*S!tX=vbf0_J`Ij1WdZFGI z?(jPQ9em2~w2SyXU^}46dcYxR9Z2!GWN|PwYoh1ct>r{0bWZdNe6*An(gV!#2DIgD zq?FJGXcR30O87Cw(O-5%6=`SSYcUZ8l4>q1u_Wrm31KOp|t5B$Zp z;Lki*Ao5QO^7kDRe?PWp^;d2Am-YD1x}q#2hoXSWTb<={=#--l3Q8qUGbKxV2SVm! zBQ0$PO)LsaM29~c$7f|~#}(8EAb*8tx6(EvHb~SX4ukOxd5t?vD-E{_q^!v zoaB8N%X)e`7^U;GUzR?r2@bCaW}qt{D#cWaGo7+TuV&XSH(A!r zBC1i>gG`g{y~o5n>vavHZU5Zc=fT7*Hwsd0#n>0#(^!N9AREovl~TFxi-;aBzga`O zUUQ<-6-BhDGUEcYA0@g6sa1N7Uu)|x+!U5kz<55D){IU(SEw|tv3_fIrKqISRbb07 zf-GV->W!LbdLY$nCDaI9a03Vq9@gz8S#@YubM&#NO}ysIGY96R5wh@SxB-%RY&ueR z%K3EATEnSfHHXcci|*uy;GZYRIU9Qxo1-zSjzfhOr^DIM7M-*Qo1^Kp^~AFz_QGg? za@(AR#sxNa$G#zWW3Cuqc_pomK7Es-PL`;A$cD0`D&8eEt=i!_3AqTFBV%2aJuhlj zBXX5lB6Vn$9~!b3r;k4rp8*vWxF+;#wn`7CC44DTA@YSiGn$+}V)Sr`lU@0dc!M%L3-#DW=Y(RVXt5I5xt9WBFz^lZ2y zO)eP6b#LR~S#-p=3{XBGreUYK^u2`4zG}j5NEx|q8VsqYzW>zHBUH@#ylys6GE|BwF`kdxX^C>Z+kCRa_>_uf2GX8c%EvFBQmAysS zKO^U7(B@r+B2&h)HRq3NJT)lqLy|{43{N4eFireW<~2?ge2=tI438Nkfv4#ESHo*c zGs{)m=QBzDe~bhD`*cAYeLF`p+y4sxjpw&T5k&0YB%fGjj6TIgfkYH~^mHMFKnfKR zRELBSF@lVb!64|tn@+y%zO@n=zedheJUttp zo~~xcZu)7ejO>%TTPQ(FZ5Sil=GlbzWtY%k`-@=vdX1)#rVTfa5)^HM`7WB=NuBwS znajQ!20s-M!1M5*UgKZD*b*v}zAagTd0{>X0S01Mk;}$k!c5l00No5N)qR>}d>X}w zqdNN7E!hhMp;faxbs-D`aVXjkynY;Dh&77X(Q5Cx-)pZ;D_#qNI<5C6HJ^mhebz^^>#vp2JMQS^F6@5pm+gvPPfz3R z%`52k=CvHe+qb>q{9#$Bp$D~>&%QLgf5Z~;-yQP&Io)yiuLMWue`2bCOQz#VCf46aAH_8;y>xqDMHnM0hXr}#RroF-jD3lbF+d}@fAwpB zYznl>Zo*8y!3WNq4mNnLZjdXi4){>fJ1bo3TKFo3LS%8^r-NwCjRqF+Lsh?{zY`$o$rCfwD{uM*Z;1(pM0VVBis( zRm$H`9l{=y!`^-Hsp){mgmn2k+a-?E4&s6GmCmSd|4G}5Yj@zM7IJ#zI z8T#m>#NPLp9!L1sBu;ZM2T#^#xsdpu=Ov<|9du6_2kcN=*K$ZQcXoD zr22P1Gix^ruyTMvEt^@=bq!)=Ekau$7<&A=Q+cSZ=X+ zlU8nX*>Xn<$&(@#DLIq2Z!HbkxgN5UEBh6NMOd0|DssPB#eXrSF(ttKP&L$+v`_(Q z&0P(EMVr27hTTr(tYoyporbd>laMo4)y|TfmV66M``l`&EOp>$%V?z}{ey1DLld-v z`g{c2M2a^sPByJt$b-f%~Hlv4UoLsn89tG%EE9yz);_rPoo-BI7K|$ z+x#QHnXq!3aJ5q$_9+svP4(D&4=k`4DzoEiWgv>m^biHh2sM|WNm-a#D=Wdds(!1t zH;n2j$Tg{RBzIp%L)Fj?T6A+tolW>EGbm{>=&Wm57wX0TReJuya(hW}fTe`csVroV zh_;mOWWZ`0i26_j!`6e$D}RfrV=o~9cGbUHqRL*h`(~*;2M-T+eD6X6jGmBcn;T5c zUZ$IcPhVRztBOAW1b1Y~@j5wgKh8IL`%OoQ;?yNbFnZ~;6^_tD-n4jMO(QRM_ z?R$<<-&&JY+JcGlS7XoF{T#(HeiPa+F+InH7n5whIHbDHplL`aCiFNDc3wQG1&B#?o#WdJ{&+oTzPL``$$A0E1ww@*rw z!0kT0c4r3YNBhu_#29h|R-JxUH- zJb1ak4Z|;1o*#Po*}*3jiW!7X6{E1v{HF2{dprd4&S%s=&oQM7V88-h;c_U+n<@}d zgO7VL>Nw#u(kzj1+Rs@V3|8TnDJgIXjLk2}r*9C8R3esz%C4*e_)oG;ekHhS2sHD8 zr1#utiDHOpH~c;dZQ;12K0+?2kWF1jgF!RO5vdkvoQAweKf~>(R(g<3pcJVx_u#S? z(C*)wpM_8xmP^03P1|7c+uHI`_WNEvb&}kWZJ-+fI@>xA0psc?(Gl+OL2bgb>akk> z39ZOtgdlEiC~@BtYdEtAPRgb#ooAL9BepHL=SVklIvH$GHz{tf{dW8|TqfcXhs3Ua zyF@;r#oY%CfGl2@^ocCbC9WU^xHKv;m+knQ^;rHsGB$>%^GLbB*6+-=3rgaKwC%L5 z4}o25f4OsLK7bRp;D_q88YJJUNHAT(E%b1;0e4m0EtP$Zo3iag9VE-%*fHD)2$OB|$zBQI8=f%LXN?eNybZrc;q6jA{A?)ud!K2f9EY ztd+XpY$fvRII&lOU$N-~#P?tRv`tc%oliTH;M>{M{U7HMa-iEy$vYpn{n;=*w$%Nb z_x-a8x?S}`nEsaxdQDjO%`UHhNy~Rq$2aM z^+UkuYxQ=SfY*AZM-jkibmi=W96v!58aA_(7mpV+u}Dqta}UwfbQs-_?GHvKJPD_h z=Ag*v)n=0M)aW+#+1hAUI2iS)zC#mWInM&HplX7b2hMV<~ z26pQUu*HYTU=5e-KVS#F8kI6#$7(yS3LOA13DxVJ?R~36_=;Xit50Ws{t<2n7qcIJ zz7R;~$PO&GD2ldK3hx8cGeylAdXZb$qYb6UP{fn|O5P3jReE$$%X|gJi#t;>U#ge} z^-_1SKG+_64A~rk$!?I3@1gSOrv%hi&v^t-!LXBXbkYvYc*I_jkY`TvHg6e-@jKrV zR>9AmG_;^B9%|v$1w~=_f1MKcQ%{AAYJZimOMs0pO}Ub|cMFS*cVR0?(B+cX5B0$z zw#6zkL-glu&tnnVC_(H*-Fc9VHe{K+4=mX9{$NfQ`jD8MPTXs2$_+J=y-*8 zHy?;wEXx=~a>_4w$nv)Qx=hF9SQCPuA#9E~fm3YN>A+T|d_=nL28k|`luZ2^GmIMn z4gRS*g`<<#!5>EMBo?C-N}YH_pOrX7bb6=+fgiS$-Q=4pQ5h%Yw&3~JX$pu%f zv=!7!>^EF`U@+7bl^YIuK%3W@?H|*ME`8*H@@ZIc{&9=d-v;gcn`@N*+c`*-nwBfd zBHH`Fj9JCi5+=?v`u2`p{T|E?I?ifha%%Z5OjIppEDKga?dJyK(CHz8c(#IOvO;S! zsZS=d79wziq=~OuXf9;+J{I;7CU{<&ALMg(u3UJVh@-(WY^`f>qI)_0HQC|iIXRZ{ z@z#s$r}HQ>c*CI)l*NH2#;iMiSr$#$QMt?7P6w_uXimyP%Bs%gl!@9aLZ)c&6E&cc zB$AnHg1AyiRICCQt}56H^I+>Jl(5(k?N#zOt2ETxLI1lyCY^2g?^=7XZXb4YN_9pv?gcwO z$Cc~owBp%Sd->JLapv43@9s4QP|<9j0F$t{4Y@ z+Wi_UF*I>ZsM;*f(RT+aq1ZN|rz+uB(G$;*rQ&mVhMg>&Lm}55qf4!A=l4WE!gR_~ z-qbxV!M_{tTP9+r7G?EP6V?`Vo0H`%%6=%Rc%!yNxl+`Ug9~k8GJe`6Ub)S6_O;@t zJh(0r#v_(q1FxeaDOi!6Y3<@X``N@Z@noes)}8qVjs;T5D8}5LL;W?e-{@Q%n~7dL zI;irDVydr)3D3PtYp&EE4mu6E#Y?Prz`sL*zhyH$ucLsMv73@4h^knGAuF(kD{azf zbg5?jQG8=^pYl+{MC=*c?xw?EJZ!tfl>6O&@}4#(4z4A>GjXjDo29){fWY}iw}*?O zx6T5Jp%I!BB*SjFi~BR_8?NU${p+sH78AT#D%+*E6VBt8Dl@mmE(Cj(?qH@9q83!1`OlB`=h~HQdxH>cVF>Mp7&LdoTFKfJ zc>|>4Y`4hynCxH(aGIl}V45VN4HJHP>Jlz@(O*%6Wpqe>p;B+7(R%WlcR;HV(A|j* z#GlT6I_H~Y*7${F;L`F8SDe;q5I2`>8>%(=L`PBaZH7OD_c=q!@ws~~>g=B{yF zmW9I>0@LpjFGPMaBtfO#`?kf*B#pwQmCbef;`^G^p+z|Tv`~!o5?DWKhU*L5C|txZ z5%G1VidyO|)9%LI{hT2+Ok#FagD*(ttGUPUdylSDM6Dsj_f;7}IKF;uqr5NN*$TCj z$w>S2DTU!^G>#OjA&@@aCjwUHgNCW~z?(c9^^uFCzj^K>b#uvQY@#(P*b;^n8U-dt z_GnD1&1+YX8_lt^E5=_zyb!}0yOJjE$gRY{8top)vI7Mt+E(#}%s+UkP+bn zS54s7xJ_3_D^Yi5WZ!T&i3|Fhp+hsKqu~Xr*m4pURP5KnIcV3!$^<31t+>^=n{5R8 zc~n4ISuB@I^)ayC$8o-IX@IpB_^#sy`R=yo3k^YjtgNganB-0aL>JV=PfmtJZoO+9ITj_rMAo`xS1K|2oH~1948ly|h8I{hQO6cbnl+@~quH^F z8WT}H98o$>HZPxhZU(V6or9;c&gVm+YYnX_GS+#DLpQI()vSl5FQdn#gQCH4Bigl4 zV*K~|SP6c&U!REWG)F+HhlD|%e9jl|pK+=hxnJM{px+)bv_L$ihOd1UH@0|GL$9lY zj;xtWxV4!HDSlZfXPrQUwSM-hL#=l|mtarttS7-Gsc9+ldQ?c*&Poo! zsjcAyB(g-kmrL0+(k^apwm^g~!HL`x06r1>K4U(g*}cC+X}tZByfpovVPLx%vZjXt zLakInJgQEBWOKZ@ss)8>GN?~-$J>)kI%g-AXPvwVkG1PO{QwX5N>Ha}iGqoAK})OV z?V)lB9SXbO%-JTwCde}v$4VxG6bo?FvB;rDg%X|PUc!VE6S}gg%%{VCMN!R$WHGCE zvvC5qjFt<*4DNJ!2aAiws`wSb`>T6`BIEA4ZAmw$0m`~}dTV#XF0Hak?IQ9xUIr!6 z?C}cYRzFoN8VMfB<0sVwoy>TB^X3j;zZ_K)>bB74N2K-xWWozgx#w?RfvU-&JHHFx z?3Cwsmqr34xu}~A%znmYo$4g)QPvG^UC?QJL}8rVVLD&!UMjc~$=Wrht}LI}UZVGd zFj8;EY-Ms2(HgLQcNx)bZPoWCBVUfIzZxG+t0)&CAc27V;s43X{B7f-nz6a5nUk^6 zf9VU-|N9Sr);j&y+E)b~Dd5jjxa+sa|~~&N=8I1GO|>JZC((;I;6! zFg-ot;Mas$$FD_t0^NnT#$KXmXWKc<`a|~tSZK-LW-yw<*wMCmvs)7xzx^0rhe9X} z?~GqwibE1UA}U0!XM9MevR!-ZfPA5gAr|~NAmP5G;ZC&=Y3sQb!Ik8$>rmkPE+&hZ z{aVnXM-d%B+RzVBE&y!anZk7JfQOr54+*o^{P2$k3EuGijY$CnR8bY29bKO5Q5 zh&^(Y{b(mm@_)5X;z@44rmxAl&Ia&CS^ z)0zCFmswb=)KnL?Xn~9@|L23Ob|2j1Oe#vE#?y2wno`yj9mdJL!ZrJ-!xI*h>P3!Xyn_XXAZGCc#$(wJAM^~jy+TctvM|SjBnP^u>6V{O> zm}{r|PLZfni?8j>t&32K-Rq5E&kM3^fP7|a?dLhNKjm|Zu%6dFPdJu1N4T5kYM^e- zcE#O8y-M5%&P+HgSpU?VWm)f}uH!Xd~aeQn;$jFZM`W#=gpIZOb6yG;rfqB z&9N>HMEmUaf|EKaLFR((&-4ZAL*JTQGt|iT@^63>z{ej*y*_qAQO>z2nO|u{^F2pK zr8-t(tl3PP7#+Ur0{%mnH6*DE*jpTqd^D?AZ}G1j*cF zs@%L##iQ-oi&lxfix%08M;6sU?H#Nn)(79)$YVtf!W#T zqYkHSS=?`}DA?P=8K-1(C6ATaM?~DRb$#0o9r+d6<=I)QxLNfo`eulQ?kkYl+GI4w z>&%s){NZiS;Uk+Pm1dM2;YzV;@I04xHk-fO-jAg3(99QNV?W5}d&?%b&3Vz1!Y0+x z@*4b#>XB^czWryrlLB+vUCULu*KRoL6pnlU*3g((8v7+~8<%IigJ6-8?K*>sw&C4g zS^B4jY!~pJkNKE+N8(^yGf$iNp!>dO$veBwtu2Uh8&2Dq5?4OJl~!|{Pch9xb3^h_ z zJBI)~>!^Hzn|fb+!&&T#z&MGxG($b+m>VW*?v=Lj#kh*&`qW@o@mwBR71o=wT^n8P zZ${lOEi3C*m~Vy;;?CLAUx*LlC%Ol5x~Y0LK9+vcrWk2D_qBBWhAW4RulTWVNbB&P zPh>3~+Sj9H<$?1Mi#sdDSLxrk+9n}7P;K+&%&3^4^;g9>BCC|!j2qcxvYv_$ za@rcXb(U*r^cTIc|2(5Hz-W!E#?gcS9yDHvuTgih51G20kkOxbT2)cwQI||-k)zhj z^7DcDcY3De&a>^kIrASsH|xt~>o5DqWNvEC>&rc^uz3FSH#WwtIU$d<4~{)9IXx7q zX=k;6MYEWAXV9AsRl^-aEbOmuR2cJbgrw~aChSzWs+FnHj;_3AD&k=qw)w57exVUu7So&6 z_i6r?Blm0hQbRjWyD97KPGoO6@NmJ(qbuhoIi#{z6n#(~4S%%DepstF-$v_D-y`w< z1zhDLYGJ~}*@w5U(oeYiVN;9l#^;qg@+*4s>O$YmFRh&=_~7f8{*&dUX0@BX@XU0w z>DDb$-COsiAiXxE+huQedxpW?eP5UJ+@D2btY2iL5@@l%&T{GPHQikk_gA+Lo$T+rUYR;N>jJ-c-emFo^*-TD z=|LP@mbZRfSi57B6kbKCGaj=toY23#u6WlYomy$tqc-&iEQgr-^7jOOEI)8x(oNf^ zcGh0Wyi}!+OIy{BS8jIf^_X>EljB0HK(2I(wWoE@*t?vBH1H{j5kcLHn6 zwhq^`rxOxt6r3WHpLeD%-FTLF`OQaM7WV5zS5?2>N93zD5#?OtA;a;|Ahp7GS-sEf z)1@)V-8WlrVwc3>YSvHY3=8ysA?)wd@a$<7Q7VYKL@;`1byuoWS76*Kk`+#PSc-|j zcN)z(sMx5G7iHZO)#PZIar}!Q2+@UD?kij1A;3Sk}75_ccm>^f8Wb zit1zb6&sa1FZz*1o9kgob{c!nt>c7ceT~JLm0UQBG`4}z#8mbWyOggU@@vDTw#^Aj zi9V6hD`FjTxaD8xECC#k)-wJc#m4zdOq-%FTJb(;N_#24!BkBv&AL_FMMeC?bcpT%c$-%n=So4q6kV!niaY|R$4ztQd7Z|3L!w3BHLSl?pJCr4B zUcJ8;Dxn#6eE#-&!Qo=Jd!Zsz6B=b@)=Y=-nC9 z(26a58u3D1IQiVv)d}g)3#lB{3#wwdvvfYGaj&yh&Ao)K``1TU-{`h zgsGe0bT1^r8$fW!2jIMXA9N+vIC0FHnVlUnuAu_z0C1o2DE?>Cb-RM2Bb>Rt~W;HNpKy95Gr2k35& zIg4gL*fVPh1|vr)66P4gnBJgIs;)*hOFfIiAORqKppp-qn=V}oP}_?lE*aXJMD)gy z{2+A*J%xx(!^71X=$=#>#S`US04We3f+e}g|4b@O4^kNciGkt55abVNIcFjXhtPLS zl9)->z6`bPfEy2W6$0PsKjG6>kO#1B>W|Yu2mZng#wPjftm(2Idf?x;2F23UJsar} zUAVvR4Wg%JNH9ZDl7HOsEB!OQTj75xN(c3=w2ZuJUOx9RP>CSC1wsxaULG2Ine?#E z?l_mVSbw)4%P`bprSmSER}8|igSyGbM@^U6ytFXg{R!J}E~|ZgiOzVeA9!yQBEjw3 z$F4s&MpCm_JQG%!1(GX)V???-0lxphqDPhh-6>}!u{W=0#bE3fVKC~`je)Befxl-$ z=P;>L-y{S2dI6Q3_ItYMNYmo*Bsy;s-2ig|d3Tu}`8RZIOxwzPzJOZ{GYiuDG{7VCCvXh>{BqNfY)M>2V8Te!DRh93fkW?&GS79Flqz?0HuK$rJpTh5JKxR*SU%OcQ1 z6lkFl3BE*69ChLkYB{%l5)nQEExdpJFf(gNF6vYQR z4!RzYlA~+n+Fll$cc9<%(BH|^`*aasOB2^0_Se#|S8?lDBiZ#HfGRQ#23M{|^zh(Q zbaVlGrYct*f!nV`MK)aBm=>UKfC1hU2SEtQZV8cuC|0 zy9ZATEXbQkz!CiLSWnO3H8^K%Fmy1g-AmL%Ed4=Oq$Ic*ACOxGA+v!$4H^S{5YEL6 z3qF#OE*F(hUq|*lWREF?AZQJwK|n{vH4)t2TR_}c5LXm|#u7{e%@FVFM|2}$z0mc% z!Qv>v7&-zG<~7-cy-uu*F*47k;zXz{|@f_dt!90h6s#i zZ2+w{fL5(3bc(_B#WKKQMGlGlJ^vT==4N=U9|H+U(9+Ea391wRza%gRuK>@r&h+(- zUw5Fg8w@=HhDPc_1AP1+4b6})=u42n(@^jRe92Alpeqr&NKO79BtSR)F{A4{4WYRl z5EvLC$VM|6AhUKS;jk_SSZ5ei!HD5Rzm2R_n?t3*EBMT%j>^vG{4Fhd!&u`MpV$wL z8UO|%M8mYt(qMoLm9%08-(iCE9Y9$NesL)Rjq5x;G@J{%bg^&(unXXjpb8`K)JkaM zk?_vwaAhpH)n=ehTNuE|Ir1faxYd4sB)pTqAB~3@Cf+rcOfW11VUCnQh#7x@u9y(q z(9|VHv!oyxx||4%Fc}1Be>GiDOPm|b4$#k((6!(&q2wL{z;eFWDHmh9axgXRh=A+lOrc& zpfh%zF<=;F(2swFk+e)HE${tQ4CKH28;wcxf1#ma*t?>H*Mra+yWeOYwEPzu8fHFm z(La3zl|~Mn3hJElT}!`uJW^>ijPCWUjh~#-F9h7)GQ_}&YX1*3=p8^@ELWBd+R^|O zxxO*g@i!z^{@&i6u*8hc(ylqf@GS?d2tXf`LNvwaK0_?%uygg^WO{&x?tni-E@qUz zVhGj*91xBGAFS$PeQEg2en(L;cTi3M_)R5*=sVy36$g3~7PVAdD+gG9pb$mC8x7Kh zx5EAS+>|=BZ7(ouiU+0agF%4w#}-305G~>1iuf(?7yWjLE-F8dMnfiuX$_hXLx{S3 zF;LnKsPm2NK=KgH6!S1;b zI3Q!qn;#g)CO=)eAR<9@<58v&mz{0}T&?`fC>c63W!_xmm?NZNNeBGVwC3(Yl4(#xgdR?oF07&)RT>MhW Date: Thu, 17 Feb 2022 13:54:54 +0000 Subject: [PATCH 2390/5615] Bump spock-core from 2.0-groovy-3.0 to 2.1-groovy-3.0 in /modules/spock Bumps spock-core from 2.0-groovy-3.0 to 2.1-groovy-3.0. --- updated-dependencies: - dependency-name: org.spockframework:spock-core dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 26158b6db57..e745bc23ed6 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -6,7 +6,7 @@ description = "Testcontainers :: Spock-Extension" dependencies { api project(':testcontainers') - api 'org.spockframework:spock-core:2.0-groovy-3.0' + api 'org.spockframework:spock-core:2.1-groovy-3.0' testImplementation project(':selenium') testImplementation project(':mysql') From 40f3e3f0125e1307a6f0af844e36f4a8c783b55a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 13:54:55 +0000 Subject: [PATCH 2391/5615] Bump postgresql from 42.3.1 to 42.3.3 in /modules/postgresql Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.1 to 42.3.3. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.1...REL42.3.3) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index a8a6495bfc1..754d5dde289 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -11,7 +11,7 @@ dependencies { testImplementation project(':jdbc-test') testImplementation project(':test-support') - testImplementation 'org.postgresql:postgresql:42.3.1' + testImplementation 'org.postgresql:postgresql:42.3.3' testImplementation testFixtures(project(':r2dbc')) testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.11.RELEASE' From b5575455cba038b579289c18d966921835e90676 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 13:54:55 +0000 Subject: [PATCH 2392/5615] Bump postgresql from 42.3.2 to 42.3.3 in /modules/cockroachdb Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.2 to 42.3.3. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.2...REL42.3.3) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/cockroachdb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cockroachdb/build.gradle b/modules/cockroachdb/build.gradle index 71011c6534b..4d15e4df1c1 100644 --- a/modules/cockroachdb/build.gradle +++ b/modules/cockroachdb/build.gradle @@ -4,5 +4,5 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'org.postgresql:postgresql:42.3.2' + testImplementation 'org.postgresql:postgresql:42.3.3' } From 08d1c9c180f7fd38d0c0e3d67dd90ec36c10f81a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 13:54:55 +0000 Subject: [PATCH 2393/5615] Bump postgresql from 42.3.2 to 42.3.3 in /modules/junit-jupiter Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.2 to 42.3.3. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.2...REL42.3.3) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index ba0083a25a7..749bf72690d 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'org.assertj:assertj-core:3.22.0' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2' - testRuntimeOnly 'org.postgresql:postgresql:42.3.2' + testRuntimeOnly 'org.postgresql:postgresql:42.3.3' testRuntimeOnly 'mysql:mysql-connector-java:8.0.27' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2' } From bb76a74e71b5a102cc1082b654b6a27a23872a87 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 13:54:55 +0000 Subject: [PATCH 2394/5615] Bump hivemq-extension-sdk from 4.7.3 to 4.7.4 in /modules/hivemq Bumps [hivemq-extension-sdk](https://github.com/hivemq/hivemq-extension-sdk) from 4.7.3 to 4.7.4. - [Release notes](https://github.com/hivemq/hivemq-extension-sdk/releases) - [Commits](https://github.com/hivemq/hivemq-extension-sdk/compare/4.7.3...4.7.4) --- updated-dependencies: - dependency-name: com.hivemq:hivemq-extension-sdk dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index 779ac2ec9d8..f0c3aaa2e69 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -13,7 +13,7 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") testImplementation(project(":junit-jupiter")) - testImplementation("com.hivemq:hivemq-extension-sdk:4.7.3") + testImplementation("com.hivemq:hivemq-extension-sdk:4.7.4") testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") testImplementation("org.apache.httpcomponents:httpclient:4.5.13") testImplementation("ch.qos.logback:logback-classic:1.2.10") From 80e64855928f4a70b95fbfbc68377700aedafe53 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 13:54:55 +0000 Subject: [PATCH 2395/5615] Bump mongodb-driver-sync from 4.4.1 to 4.5.0 in /modules/mongodb Bumps [mongodb-driver-sync](https://github.com/mongodb/mongo-java-driver) from 4.4.1 to 4.5.0. - [Release notes](https://github.com/mongodb/mongo-java-driver/releases) - [Commits](https://github.com/mongodb/mongo-java-driver/compare/r4.4.1...r4.5.0) --- updated-dependencies: - dependency-name: org.mongodb:mongodb-driver-sync dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/mongodb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mongodb/build.gradle b/modules/mongodb/build.gradle index 95919c931d4..73dba45dbbf 100644 --- a/modules/mongodb/build.gradle +++ b/modules/mongodb/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: MongoDB" dependencies { api project(':testcontainers') - testImplementation("org.mongodb:mongodb-driver-sync:4.4.1") + testImplementation("org.mongodb:mongodb-driver-sync:4.5.0") } From 30b09ba593698c1a8934bf0b6068a29839efebdf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 13:54:56 +0000 Subject: [PATCH 2396/5615] Bump rest-assured from 4.4.0 to 4.5.1 in /modules/vault Bumps [rest-assured](https://github.com/rest-assured/rest-assured) from 4.4.0 to 4.5.1. - [Release notes](https://github.com/rest-assured/rest-assured/releases) - [Changelog](https://github.com/rest-assured/rest-assured/blob/master/changelog.txt) - [Commits](https://github.com/rest-assured/rest-assured/compare/rest-assured-4.4.0...rest-assured-4.5.1) --- updated-dependencies: - dependency-name: io.rest-assured:rest-assured dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index 8aa0a2d689e..ce000685a10 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':testcontainers') testImplementation 'com.bettercloud:vault-java-driver:5.1.0' - testImplementation 'io.rest-assured:rest-assured:4.4.0' + testImplementation 'io.rest-assured:rest-assured:4.5.1' testImplementation 'org.assertj:assertj-core:3.22.0' } From 232771603089399249f4436cbac505feb3232cb7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 13:54:56 +0000 Subject: [PATCH 2397/5615] Bump reactor-core from 3.4.14 to 3.4.15 in /modules/r2dbc Bumps [reactor-core](https://github.com/reactor/reactor-core) from 3.4.14 to 3.4.15. - [Release notes](https://github.com/reactor/reactor-core/releases) - [Commits](https://github.com/reactor/reactor-core/compare/v3.4.14...v3.4.15) --- updated-dependencies: - dependency-name: io.projectreactor:reactor-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/r2dbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle index 74ab1c9f7f0..2fa066c21b3 100644 --- a/modules/r2dbc/build.gradle +++ b/modules/r2dbc/build.gradle @@ -15,6 +15,6 @@ dependencies { testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.11.RELEASE' testImplementation project(':postgresql') - testFixturesImplementation 'io.projectreactor:reactor-core:3.4.14' + testFixturesImplementation 'io.projectreactor:reactor-core:3.4.15' testFixturesImplementation 'org.assertj:assertj-core:3.14.0' } From ca2dbdd74ec0f61895c7af69c7af1f2731db66fe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 13:54:57 +0000 Subject: [PATCH 2398/5615] Bump amqp-client from 5.14.1 to 5.14.2 in /modules/rabbitmq Bumps [amqp-client](https://github.com/rabbitmq/rabbitmq-java-client) from 5.14.1 to 5.14.2. - [Release notes](https://github.com/rabbitmq/rabbitmq-java-client/releases) - [Commits](https://github.com/rabbitmq/rabbitmq-java-client/compare/v5.14.1...v5.14.2) --- updated-dependencies: - dependency-name: com.rabbitmq:amqp-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/rabbitmq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/rabbitmq/build.gradle b/modules/rabbitmq/build.gradle index 9b680b12968..967b9eb4ed9 100644 --- a/modules/rabbitmq/build.gradle +++ b/modules/rabbitmq/build.gradle @@ -2,7 +2,7 @@ description = "TestContainers :: RabbitMQ" dependencies { api project(":testcontainers") - testImplementation 'com.rabbitmq:amqp-client:5.14.1' + testImplementation 'com.rabbitmq:amqp-client:5.14.2' testImplementation 'org.assertj:assertj-core:3.22.0' compileOnly 'org.jetbrains:annotations:23.0.0' } From cd9d2b46dde52a3cf4fe42fa54ebe487dc08f124 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 13:55:00 +0000 Subject: [PATCH 2399/5615] Bump azure-cosmos from 4.24.0 to 4.26.0 in /modules/azure Bumps [azure-cosmos](https://github.com/Azure/azure-sdk-for-java) from 4.24.0 to 4.26.0. - [Release notes](https://github.com/Azure/azure-sdk-for-java/releases) - [Commits](https://github.com/Azure/azure-sdk-for-java/compare/azure-cosmos_4.24.0...azure-cosmos_4.26.0) --- updated-dependencies: - dependency-name: com.azure:azure-cosmos dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/azure/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle index 1dbad6a6698..7e5bc336526 100644 --- a/modules/azure/build.gradle +++ b/modules/azure/build.gradle @@ -4,5 +4,5 @@ dependencies { api project(':testcontainers') testImplementation 'org.assertj:assertj-core:3.22.0' - testImplementation 'com.azure:azure-cosmos:4.24.0' + testImplementation 'com.azure:azure-cosmos:4.26.0' } From 9202c67fe321c9e6ca96d9eb4cc86c157e2e1ff5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 13:55:01 +0000 Subject: [PATCH 2400/5615] Bump amqp-client from 5.14.1 to 5.14.2 in /core Bumps [amqp-client](https://github.com/rabbitmq/rabbitmq-java-client) from 5.14.1 to 5.14.2. - [Release notes](https://github.com/rabbitmq/rabbitmq-java-client/releases) - [Commits](https://github.com/rabbitmq/rabbitmq-java-client/compare/v5.14.1...v5.14.2) --- updated-dependencies: - dependency-name: com.rabbitmq:amqp-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 0d704cecd81..1b54ca4ff1b 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -90,7 +90,7 @@ dependencies { testImplementation 'org.apache.httpcomponents:httpclient:4.5.9' testImplementation 'redis.clients:jedis:4.0.1' - testImplementation 'com.rabbitmq:amqp-client:5.14.1' + testImplementation 'com.rabbitmq:amqp-client:5.14.2' testImplementation 'org.mongodb:mongo-java-driver:3.12.10' testImplementation ('org.mockito:mockito-core:4.3.1') { From cc3e076f546c53629e62f0c3b266d705d79c4881 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 13:55:04 +0000 Subject: [PATCH 2401/5615] Bump release-drafter/release-drafter from 5.17.6 to 5.18.1 Bumps [release-drafter/release-drafter](https://github.com/release-drafter/release-drafter) from 5.17.6 to 5.18.1. - [Release notes](https://github.com/release-drafter/release-drafter/releases) - [Commits](https://github.com/release-drafter/release-drafter/compare/436492609a0c75979acf131d57f61b5321571950...2f7ebf8ab5ef7f9835ee4b0b1eebaa2a14ca1669) --- updated-dependencies: - dependency-name: release-drafter/release-drafter dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/release-drafter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index aec34acb8ff..05485ac57fa 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -12,6 +12,6 @@ jobs: if: github.repository == 'testcontainers/testcontainers-java' runs-on: ubuntu-latest steps: - - uses: release-drafter/release-drafter@436492609a0c75979acf131d57f61b5321571950 # v5.15.0 + - uses: release-drafter/release-drafter@2f7ebf8ab5ef7f9835ee4b0b1eebaa2a14ca1669 # v5.15.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From a78133cc8dbe6dcd839d4d264d3d5dae647edf22 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 13:55:08 +0000 Subject: [PATCH 2402/5615] Bump postgresql from 42.3.2 to 42.3.3 in /examples Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.2 to 42.3.3. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.2...REL42.3.3) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 37fc1b71226..1cb3a0285f2 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' implementation 'com.squareup.okhttp3:okhttp:4.9.3' implementation 'org.json:json:20211205' - testImplementation 'org.postgresql:postgresql:42.3.2' + testImplementation 'org.postgresql:postgresql:42.3.3' testImplementation 'ch.qos.logback:logback-classic:1.2.10' testImplementation 'org.testcontainers:postgresql' } From 5e016c9432c71f263493d144214f5fb047268d84 Mon Sep 17 00:00:00 2001 From: Yannick Weber Date: Fri, 18 Feb 2022 13:30:23 +0100 Subject: [PATCH 2403/5615] Use WaitAllStrategy for HiveMQ (#5093) Also, remove MultiLogMessageWaitStrategy. --- .../strategy/MultiLogMessageWaitStrategy.java | 59 ------------------- .../hivemq/HiveMQContainer.java | 14 ++--- 2 files changed, 5 insertions(+), 68 deletions(-) delete mode 100644 core/src/main/java/org/testcontainers/containers/wait/strategy/MultiLogMessageWaitStrategy.java diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/MultiLogMessageWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/MultiLogMessageWaitStrategy.java deleted file mode 100644 index 0d342f3050f..00000000000 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/MultiLogMessageWaitStrategy.java +++ /dev/null @@ -1,59 +0,0 @@ -package org.testcontainers.containers.wait.strategy; - -import org.testcontainers.DockerClientFactory; -import org.testcontainers.containers.ContainerLaunchException; -import org.testcontainers.containers.output.OutputFrame; -import org.testcontainers.containers.output.WaitingConsumer; -import org.testcontainers.utility.LogUtils; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.function.Predicate; - -/** - * This is a wait strategy to wait for multiple log patterns. - * The wait strategy will continue when every log pattern is matched at least once. - */ -public class MultiLogMessageWaitStrategy extends AbstractWaitStrategy { - - private final ConcurrentHashMap regexes = new ConcurrentHashMap<>(); - - @Override - protected void waitUntilReady() { - WaitingConsumer waitingConsumer = new WaitingConsumer(); - LogUtils.followOutput(DockerClientFactory.instance().client(), waitStrategyTarget.getContainerId(), waitingConsumer); - - Predicate waitPredicate = outputFrame -> { - if (regexes.isEmpty()) { - return true; - } - regexes.entrySet().forEach(regexHasMatched -> { - final boolean matched = outputFrame.getUtf8String().matches("(?s)" + regexHasMatched.getKey()); - if (matched) { - regexHasMatched.setValue(true); - } - }); - return regexes.values().stream().reduce(Boolean::logicalAnd).orElse(true); - }; - - try { - waitingConsumer.waitUntil(waitPredicate, startupTimeout.getSeconds(), TimeUnit.SECONDS, 1); - } catch (TimeoutException e) { - throw new ContainerLaunchException("Timed out waiting for log output matching '" + regexes + "'"); - } - } - - public MultiLogMessageWaitStrategy withRegEx(final String regEx) { - regexes.put(regEx, false); - return this; - } - - public MultiLogMessageWaitStrategy reset() { - for (final Map.Entry stringBooleanEntry : regexes.entrySet()) { - stringBooleanEntry.setValue(false); - } - return this; - } -} diff --git a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java index 37f9a9259d1..342903e24af 100644 --- a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java +++ b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java @@ -8,7 +8,8 @@ import org.slf4j.event.Level; import org.testcontainers.containers.ContainerLaunchException; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.wait.strategy.MultiLogMessageWaitStrategy; +import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; +import org.testcontainers.containers.wait.strategy.WaitAllStrategy; import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MountableFile; @@ -48,7 +49,7 @@ public class HiveMQContainer extends GenericContainer { private final @NotNull Set prepackagedExtensionsToRemove = new HashSet<>(); private boolean removeAllPrepackagedExtensions = false; - private final @NotNull MultiLogMessageWaitStrategy waitStrategy = new MultiLogMessageWaitStrategy(); + private final @NotNull WaitAllStrategy waitStrategy = new WaitAllStrategy(); public HiveMQContainer(final @NotNull DockerImageName dockerImageName) { super(dockerImageName); @@ -57,7 +58,7 @@ public HiveMQContainer(final @NotNull DockerImageName dockerImageName) { addExposedPort(MQTT_PORT); - waitStrategy.withRegEx("(.*)Started HiveMQ in(.*)"); + waitStrategy.withStrategy(new LogMessageWaitStrategy().withRegEx("(.*)Started HiveMQ in(.*)")); waitingFor(waitStrategy); withLogConsumer(outputFrame -> { @@ -121,7 +122,7 @@ protected void containerIsStarted(final @NotNull InspectContainerResponse contai */ public @NotNull HiveMQContainer waitForExtension(final @NotNull String extensionName) { final String regEX = "(.*)Extension \"" + extensionName + "\" version (.*) started successfully(.*)"; - waitStrategy.withRegEx(regEX); + waitStrategy.withStrategy(new LogMessageWaitStrategy().withRegEx(regEX)); return self(); } @@ -562,11 +563,6 @@ public int getMqttPort() { return this.getMappedPort(MQTT_PORT); } - @Override - protected void containerIsStopping(final @NotNull InspectContainerResponse containerInfo) { - waitStrategy.reset(); - } - private @NotNull MountableFile cloneWithFileMode(final @NotNull MountableFile mountableFile) { return MountableFile.forHostPath(mountableFile.getResolvedPath(), HiveMQContainer.MODE); } From 3ff0bb229b204c0839c256b44d15430080b55dc0 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 21 Feb 2022 07:30:16 -0500 Subject: [PATCH 2404/5615] Fix tests on Windows (#5096) * Fix tests on Windows * Fix race condition in `StartupCheckStrategyTest` --- azure-pipelines.yml | 7 +- .../DockerRegistryContainer.java | 68 +++++++++++++++++++ .../images/ImagePullPolicyTest.java | 7 +- .../utility/AuthenticatedImagePullTest.java | 40 ++--------- .../StartupCheckStrategyTest.java | 4 +- 5 files changed, 82 insertions(+), 44 deletions(-) create mode 100644 core/src/test/java/org/testcontainers/DockerRegistryContainer.java diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0f01bb7bcd2..9279b3233f8 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,7 +1,7 @@ jobs: -- job: azure_pipeline_tests - timeoutInMinutes: 120 +- job: core_tests + timeoutInMinutes: 60 steps: # Run all core tests when running the Windows CI tests @@ -19,6 +19,9 @@ jobs: publishJUnitResults: true testResultsFiles: '**/TEST-*.xml' +- job: other_tests + timeoutInMinutes: 120 + steps: # Run all non-core tests when running the Windows CI tests - task: Gradle@2 condition: eq(variables['Agent.OS'], 'Windows_NT') diff --git a/core/src/test/java/org/testcontainers/DockerRegistryContainer.java b/core/src/test/java/org/testcontainers/DockerRegistryContainer.java new file mode 100644 index 00000000000..641b1553d72 --- /dev/null +++ b/core/src/test/java/org/testcontainers/DockerRegistryContainer.java @@ -0,0 +1,68 @@ +package org.testcontainers; + +import com.github.dockerjava.api.command.InspectContainerResponse; +import lombok.Getter; +import lombok.NonNull; +import lombok.SneakyThrows; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.FrameConsumerResultCallback; +import org.testcontainers.containers.output.OutputFrame; +import org.testcontainers.containers.output.WaitingConsumer; + +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class DockerRegistryContainer extends GenericContainer { + + @Getter + String endpoint; + + public DockerRegistryContainer() { + super(TestImages.DOCKER_REGISTRY_IMAGE); + } + + public DockerRegistryContainer(@NonNull Future image) { + super(image); + } + + @Override + protected void configure() { + super.configure(); + withEnv("REGISTRY_HTTP_ADDR", "127.0.0.1:0"); + withCreateContainerCmdModifier(cmd -> { + cmd.getHostConfig().withNetworkMode("host"); + }); + } + + @Override + @SneakyThrows + protected void containerIsStarting(InspectContainerResponse containerInfo) { + AtomicInteger port = new AtomicInteger(-1); + try (FrameConsumerResultCallback resultCallback = new FrameConsumerResultCallback()) { + WaitingConsumer waitingConsumer = new WaitingConsumer(); + resultCallback.addConsumer(OutputFrame.OutputType.STDERR, waitingConsumer); + + dockerClient.logContainerCmd(containerInfo.getId()) + .withStdErr(true) + .withFollowStream(true) + .exec(resultCallback); + + Pattern pattern = Pattern.compile(".*listening on .*:(\\d+).*", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + waitingConsumer.waitUntil(it -> { + String s = it.getUtf8String(); + Matcher matcher = pattern.matcher(s); + if (matcher.matches()) { + port.set(Integer.parseInt(matcher.group(1))); + return true; + } else { + return false; + } + }, 10, TimeUnit.SECONDS); + } + + endpoint = getHost() + ":" + port.get(); + } +} diff --git a/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java b/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java index 9b22f5e6166..85c2e785652 100644 --- a/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java +++ b/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java @@ -12,6 +12,7 @@ import org.mockito.Mockito; import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.DockerClientFactory; +import org.testcontainers.DockerRegistryContainer; import org.testcontainers.containers.ContainerLaunchException; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; @@ -22,19 +23,17 @@ import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; -import static org.testcontainers.TestImages.DOCKER_REGISTRY_IMAGE; public class ImagePullPolicyTest { @ClassRule - public static GenericContainer registry = new GenericContainer<>(DOCKER_REGISTRY_IMAGE) - .withExposedPorts(5000); + public static DockerRegistryContainer registry = new DockerRegistryContainer(); private static DockerImageName imageName; @BeforeClass public static void beforeClass() throws Exception { - String testRegistryAddress = registry.getHost() + ":" + registry.getFirstMappedPort(); + String testRegistryAddress = registry.getEndpoint(); String testImageName = testRegistryAddress + "/image-pull-policy-test"; String tag = UUID.randomUUID().toString(); imageName = DockerImageName.parse(testImageName).withTag(tag); diff --git a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java index 3243d219cc0..c57c1d7c0b6 100644 --- a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java +++ b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java @@ -13,12 +13,10 @@ import org.junit.Test; import org.mockito.Mockito; import org.testcontainers.DockerClientFactory; +import org.testcontainers.DockerRegistryContainer; import org.testcontainers.containers.ContainerState; import org.testcontainers.containers.DockerComposeContainer; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.output.FrameConsumerResultCallback; -import org.testcontainers.containers.output.OutputFrame; -import org.testcontainers.containers.output.WaitingConsumer; import org.testcontainers.images.builder.ImageFromDockerfile; import java.io.IOException; @@ -26,9 +24,6 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -51,18 +46,14 @@ public class AuthenticatedImagePullTest { * Containerised docker image registry, with simple hardcoded credentials */ @ClassRule - public static GenericContainer authenticatedRegistry = new GenericContainer<>(new ImageFromDockerfile() + public static DockerRegistryContainer authenticatedRegistry = new DockerRegistryContainer(new ImageFromDockerfile() .withDockerfileFromBuilder(builder -> { builder.from(DOCKER_REGISTRY_IMAGE.asCanonicalNameString()) .run("htpasswd -Bbn testuser notasecret > /htpasswd") .env("REGISTRY_AUTH", "htpasswd") .env("REGISTRY_AUTH_HTPASSWD_PATH", "/htpasswd") .env("REGISTRY_AUTH_HTPASSWD_REALM", "Test"); - })) - .withEnv("REGISTRY_HTTP_ADDR", "127.0.0.1:0") - .withCreateContainerCmdModifier(cmd -> { - cmd.getHostConfig().withNetworkMode("host"); - }); + })); private static RegistryAuthLocator originalAuthLocatorSingleton; private static DockerClient client; @@ -75,30 +66,7 @@ public static void setUp() throws Exception { originalAuthLocatorSingleton = RegistryAuthLocator.instance(); client = DockerClientFactory.instance().client(); - AtomicInteger port = new AtomicInteger(-1); - try (FrameConsumerResultCallback resultCallback = new FrameConsumerResultCallback()) { - WaitingConsumer waitingConsumer = new WaitingConsumer(); - resultCallback.addConsumer(OutputFrame.OutputType.STDERR, waitingConsumer); - - client.logContainerCmd(authenticatedRegistry.getContainerId()) - .withStdErr(true) - .withFollowStream(true) - .exec(resultCallback); - - Pattern pattern = Pattern.compile(".*listening on .*:(\\d+).*", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - waitingConsumer.waitUntil(it -> { - String s = it.getUtf8String(); - Matcher matcher = pattern.matcher(s); - if (matcher.matches()) { - port.set(Integer.parseInt(matcher.group(1))); - return true; - } else { - return false; - } - }, 10, TimeUnit.SECONDS); - } - - String testRegistryAddress = authenticatedRegistry.getHost() + ":" + port.get(); + String testRegistryAddress = authenticatedRegistry.getEndpoint(); testImageName = testRegistryAddress + "/alpine"; final DockerImageName expectedName = DockerImageName.parse(testImageName); diff --git a/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java b/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java index 0e72efb5723..b60225010a1 100644 --- a/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java +++ b/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java @@ -84,9 +84,9 @@ public static class MinimumDurationStrategyTest { @SneakyThrows @Test public void testCommandIsExecuted() { - waitForHello(bboxWithMinimumDuration); - assertThat(bboxWithMinimumDuration.isRunning()).isTrue(); + + waitForHello(bboxWithMinimumDuration); } } } From 257c89bea35901e4318704e883d27c08437d8911 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 21 Feb 2022 08:39:53 -0500 Subject: [PATCH 2405/5615] Start Ryuk lazily when in the reusable mode (#4938) * Lazy Ryuk * Add `ResourceReaper#init` --- core/build.gradle | 7 +- .../testcontainers/DockerClientFactory.java | 56 +++---- .../containers/GenericContainer.java | 5 +- .../testcontainers/containers/Network.java | 2 + .../images/builder/ImageFromDockerfile.java | 2 + .../utility/JVMHookResourceReaper.java | 57 +++++++ .../utility/ResourceReaper.java | 156 +++++++----------- .../utility/RyukResourceReaper.java | 43 ++++- .../DockerClientFactoryTest.java | 20 +-- 9 files changed, 201 insertions(+), 147 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/utility/JVMHookResourceReaper.java diff --git a/core/build.gradle b/core/build.gradle index 0d704cecd81..7b014d1ad24 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -40,7 +40,12 @@ tasks.japicmp { classExcludes = [] - methodExcludes = [] + methodExcludes = [ + "org.testcontainers.utility.ResourceReaper#start(java.lang.String,com.github.dockerjava.api.DockerClient)", + "org.testcontainers.utility.ResourceReaper#start(com.github.dockerjava.api.DockerClient)", + "org.testcontainers.utility.ResourceReaper#registerNetworkForCleanup(java.lang.String)", + "org.testcontainers.utility.ResourceReaper#removeNetworks(java.lang.String)", + ] fieldExcludes = [] } diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 1ad7a0bec7e..d0a4e4a4fb3 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -31,6 +31,7 @@ import org.testcontainers.utility.ImageNameSubstitutor; import org.testcontainers.utility.MountableFile; import org.testcontainers.utility.ResourceReaper; +import org.testcontainers.utility.RyukResourceReaper; import org.testcontainers.utility.TestcontainersConfiguration; import java.io.ByteArrayOutputStream; @@ -38,6 +39,7 @@ import java.io.InputStream; import java.net.URI; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -61,8 +63,7 @@ public class DockerClientFactory { public static final String SESSION_ID = UUID.randomUUID().toString(); public static final Map DEFAULT_LABELS = ImmutableMap.of( - TESTCONTAINERS_LABEL, "true", - TESTCONTAINERS_SESSION_ID_LABEL, SESSION_ID + TESTCONTAINERS_LABEL, "true" ); private static final DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.14"); @@ -73,7 +74,7 @@ public class DockerClientFactory { DockerClientProviderStrategy strategy; @VisibleForTesting - DockerClient dockerClient; + DockerClient client; @VisibleForTesting RuntimeException cachedClientFailure; @@ -174,21 +175,20 @@ public String getRemoteDockerUnixSocketPath() { */ @Synchronized public DockerClient client() { - - if (dockerClient != null) { - return dockerClient; - } - // fail-fast if checks have failed previously if (cachedClientFailure != null) { log.debug("There is a cached checks failure - throwing", cachedClientFailure); throw cachedClientFailure; } + if (client != null) { + return client; + } + final DockerClientProviderStrategy strategy = getOrInitializeStrategy(); log.info("Docker host IP address is {}", strategy.getDockerHostIpAddress()); - final DockerClient client = new DockerClientDelegate() { + client = new DockerClientDelegate() { @Getter final DockerClient dockerClient = strategy.getDockerClient(); @@ -209,25 +209,14 @@ public void close() { " Operating System: " + dockerInfo.getOperatingSystem() + "\n" + " Total Memory: " + dockerInfo.getMemTotal() / (1024 * 1024) + " MB"); - final String ryukContainerId; - - boolean useRyuk = !Boolean.parseBoolean(System.getenv("TESTCONTAINERS_RYUK_DISABLED")); - if (useRyuk) { - log.debug("Ryuk is enabled"); - try { - //noinspection deprecation - ryukContainerId = ResourceReaper.start(client); - } catch (RuntimeException e) { - cachedClientFailure = e; - throw e; - } - log.info("Ryuk started - will monitor and terminate Testcontainers containers on JVM exit"); - } else { - log.debug("Ryuk is disabled"); - ryukContainerId = null; - // best-efforts cleanup at JVM shutdown, without using the Ryuk container + final ResourceReaper resourceReaper; + try { + resourceReaper = ResourceReaper.instance(); //noinspection deprecation - ResourceReaper.instance().setHook(); + resourceReaper.init(); + } catch (RuntimeException e) { + cachedClientFailure = e; + throw e; } boolean checksEnabled = !TestcontainersConfiguration.getInstance().isDisableChecks(); @@ -237,6 +226,12 @@ public void close() { try { log.info("Checking the system..."); checkDockerVersion(version.getVersion()); + + //noinspection deprecation + String ryukContainerId = resourceReaper instanceof RyukResourceReaper + ? ((RyukResourceReaper) resourceReaper).getContainerId() + : null; + if (ryukContainerId != null) { checkDiskSpace(client, ryukContainerId); } else { @@ -261,8 +256,7 @@ public void close() { log.debug("Checks are disabled"); } - dockerClient = client; - return dockerClient; + return client; } private void checkDockerVersion(String dockerVersion) { @@ -378,8 +372,10 @@ private T runInsideDocker(DockerClient client, Consumer final String tinyImage = ImageNameSubstitutor.instance().apply(TINY_IMAGE).asCanonicalNameString(); checkAndPullImage(client, tinyImage); + HashMap labels = new HashMap<>(DEFAULT_LABELS); + labels.putAll(ResourceReaper.instance().getLabels()); CreateContainerCmd createContainerCmd = client.createContainerCmd(tinyImage) - .withLabels(DEFAULT_LABELS); + .withLabels(labels); createContainerCmdConsumer.accept(createContainerCmd); String id = createContainerCmd.exec().getId(); diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 3851cf42fca..aca543ef7ec 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -364,7 +364,7 @@ private void tryStart(Instant startedAt) { CreateContainerCmd createCommand = dockerClient.createContainerCmd(dockerImageName); applyConfiguration(createCommand); - createCommand.getLabels().put(DockerClientFactory.TESTCONTAINERS_LABEL, "true"); + createCommand.getLabels().putAll(DockerClientFactory.DEFAULT_LABELS); boolean reused = false; final boolean reusable; @@ -406,7 +406,8 @@ private void tryStart(Instant startedAt) { } if (!reusable) { - createCommand.getLabels().put(DockerClientFactory.TESTCONTAINERS_SESSION_ID_LABEL, DockerClientFactory.SESSION_ID); + //noinspection deprecation + createCommand.getLabels().putAll(ResourceReaper.instance().getLabels()); } if (!reused) { diff --git a/core/src/main/java/org/testcontainers/containers/Network.java b/core/src/main/java/org/testcontainers/containers/Network.java index 2fda5ad1a8d..046133815b6 100644 --- a/core/src/main/java/org/testcontainers/containers/Network.java +++ b/core/src/main/java/org/testcontainers/containers/Network.java @@ -87,6 +87,8 @@ private String create() { Map labels = createNetworkCmd.getLabels(); labels = new HashMap<>(labels != null ? labels : Collections.emptyMap()); labels.putAll(DockerClientFactory.DEFAULT_LABELS); + //noinspection deprecation + labels.putAll(ResourceReaper.instance().getLabels()); createNetworkCmd.withLabels(labels); return createNetworkCmd.exec().getId(); diff --git a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java index 05bb6911c3e..a434dee2b41 100644 --- a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java +++ b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java @@ -112,6 +112,8 @@ public void onNext(BuildResponseItem item) { labels.putAll(buildImageCmd.getLabels()); } labels.putAll(DockerClientFactory.DEFAULT_LABELS); + //noinspection deprecation + labels.putAll(ResourceReaper.instance().getLabels()); buildImageCmd.withLabels(labels); prePullDependencyImages(dependencyImageNames); diff --git a/core/src/main/java/org/testcontainers/utility/JVMHookResourceReaper.java b/core/src/main/java/org/testcontainers/utility/JVMHookResourceReaper.java new file mode 100644 index 00000000000..173cf28f97c --- /dev/null +++ b/core/src/main/java/org/testcontainers/utility/JVMHookResourceReaper.java @@ -0,0 +1,57 @@ +package org.testcontainers.utility; + +import com.github.dockerjava.api.model.Container; +import com.github.dockerjava.api.model.PruneType; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * A {@link ResourceReaper} implementation that uses {@link Runtime#addShutdownHook(Thread)} + * to cleanup containers. + */ +class JVMHookResourceReaper extends ResourceReaper { + + @Override + public void init() { + setHook(); + } + + @Override + public synchronized void performCleanup() { + super.performCleanup(); + synchronized (DEATH_NOTE) { + DEATH_NOTE.forEach(filters -> prune(PruneType.CONTAINERS, filters)); + DEATH_NOTE.forEach(filters -> prune(PruneType.NETWORKS, filters)); + DEATH_NOTE.forEach(filters -> prune(PruneType.VOLUMES, filters)); + DEATH_NOTE.forEach(filters -> prune(PruneType.IMAGES, filters)); + } + } + + private void prune(PruneType pruneType, List> filters) { + String[] labels = filters.stream() + .filter(it -> "label".equals(it.getKey())) + .map(Map.Entry::getValue) + .toArray(String[]::new); + switch (pruneType) { + // Docker only prunes stopped containers, so we have to do it manually + case CONTAINERS: + List containers = dockerClient.listContainersCmd() + .withFilter("label", Arrays.asList(labels)) + .withShowAll(true) + .exec(); + + containers.parallelStream().forEach(container -> { + dockerClient.removeContainerCmd(container.getId()) + .withForce(true) + .withRemoveVolumes(true) + .exec(); + }); + break; + default: + dockerClient.pruneCmd(pruneType).withLabelFilter(labels).exec(); + break; + } + } +} diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index 69c849b6c82..44f9456ce4c 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -3,9 +3,7 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.exception.NotFoundException; -import com.github.dockerjava.api.model.Container; import com.github.dockerjava.api.model.Network; -import com.github.dockerjava.api.model.PruneType; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Throwables; import com.google.common.collect.Sets; @@ -24,62 +22,62 @@ import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Component that responsible for container removal and automatic cleanup of dead containers at JVM shutdown. */ @Slf4j -public final class ResourceReaper { +public class ResourceReaper { private static final Logger LOGGER = LoggerFactory.getLogger(ResourceReaper.class); + private static final Map MARKER_LABELS = Collections.singletonMap( + DockerClientFactory.TESTCONTAINERS_SESSION_ID_LABEL, + DockerClientFactory.SESSION_ID + ); + static final List>> DEATH_NOTE = new ArrayList<>( Arrays.asList( - DockerClientFactory.DEFAULT_LABELS.entrySet().stream() + Stream + .concat( + DockerClientFactory.DEFAULT_LABELS.entrySet().stream(), + MARKER_LABELS.entrySet().stream() + ) .>map(it -> new SimpleEntry<>("label", it.getKey() + "=" + it.getValue())) .collect(Collectors.toList()) ) ); private static ResourceReaper instance; - private static AtomicBoolean ryukStarted = new AtomicBoolean(false); - private final DockerClient dockerClient = DockerClientFactory.lazyClient(); + final DockerClient dockerClient = DockerClientFactory.lazyClient(); private Map registeredContainers = new ConcurrentHashMap<>(); private Set registeredNetworks = Sets.newConcurrentHashSet(); private Set registeredImages = Sets.newConcurrentHashSet(); private AtomicBoolean hookIsSet = new AtomicBoolean(false); - /** - * - * @deprecated internal API + * Internal constructor to avoid custom implementations */ - @Deprecated - public static String start(String hostIpAddress, DockerClient client) { - return start(client); - } - - /** - * - * @deprecated internal API - */ - @Deprecated - public static String start(DockerClient client) { - RyukResourceReaper ryuk = new RyukResourceReaper(client); - String containerId = ryuk.getContainerId(); - ryukStarted.set(true); - return containerId; + ResourceReaper() { } public synchronized static ResourceReaper instance() { if (instance == null) { - instance = new ResourceReaper(); + boolean useRyuk = !Boolean.parseBoolean(System.getenv("TESTCONTAINERS_RYUK_DISABLED")); + if (useRyuk) { + //noinspection deprecation + instance = new RyukResourceReaper(); + } else { + instance = new JVMHookResourceReaper(); + } } return instance; @@ -87,8 +85,10 @@ public synchronized static ResourceReaper instance() { /** * Perform a cleanup. + * @deprecated no longer supported API, use {@link DockerClient} directly */ - public synchronized void performCleanup() { + @Deprecated + public void performCleanup() { registeredContainers.forEach(this::removeContainer); registeredNetworks.forEach(this::removeNetwork); registeredImages.forEach(this::removeImage); @@ -126,7 +126,9 @@ public void registerLabelsFilterForCleanup(Map labels) { * * @param containerId the ID of the container * @param imageName the image name of the container (used for logging) + * @deprecated no longer supported API */ + @Deprecated public void registerContainerForCleanup(String containerId, String imageName) { setHook(); registeredContainers.put(containerId, imageName); @@ -136,7 +138,9 @@ public void registerContainerForCleanup(String containerId, String imageName) { * Stop a potentially running container and remove it, including associated volumes. * * @param containerId the ID of the container + * @deprecated use {@link DockerClient} directly */ + @Deprecated public void stopAndRemoveContainer(String containerId) { removeContainer(containerId, registeredContainers.get(containerId)); @@ -148,7 +152,9 @@ public void stopAndRemoveContainer(String containerId) { * * @param containerId the ID of the container * @param imageName the image name of the container (used for logging) + * @deprecated use {@link DockerClient} directly */ + @Deprecated public void stopAndRemoveContainer(String containerId, String imageName) { removeContainer(containerId, imageName); @@ -204,46 +210,24 @@ private void removeContainer(String containerId, String imageName) { * Register a network to be cleaned up at JVM shutdown. * * @param id the ID of the network + * @deprecated no longer supported API */ + @Deprecated public void registerNetworkIdForCleanup(String id) { setHook(); registeredNetworks.add(id); } - /** - * @param networkName the name of the network - * @deprecated see {@link ResourceReaper#registerNetworkIdForCleanup(String)} - */ - @Deprecated - public void registerNetworkForCleanup(String networkName) { - try { - // Try to find the network by name, so that we can register its ID for later deletion - dockerClient.listNetworksCmd() - .withNameFilter(networkName) - .exec() - .forEach(network -> registerNetworkIdForCleanup(network.getId())); - } catch (Exception e) { - LOGGER.trace("Error encountered when looking up network (name: {})", networkName); - } - } - /** * Removes a network by ID. * @param id + * @deprecated use {@link DockerClient} directly */ + @Deprecated public void removeNetworkById(String id) { removeNetwork(id); } - /** - * Removes a network by ID. - * @param identifier - * @deprecated see {@link ResourceReaper#removeNetworkById(String)} - */ - @Deprecated - public void removeNetworks(String identifier) { - removeNetworkById(identifier); - } private void removeNetwork(String id) { try { @@ -273,14 +257,26 @@ private void removeNetwork(String id) { } } + /** + * @deprecated no longer supported API + */ + @Deprecated public void unregisterNetwork(String identifier) { registeredNetworks.remove(identifier); } + /** + * @deprecated no longer supported API + */ + @Deprecated public void unregisterContainer(String identifier) { registeredContainers.remove(identifier); } + /** + * @deprecated no longer supported API + */ + @Deprecated public void registerImageForCleanup(String dockerImageName) { setHook(); registeredImages.add(dockerImageName); @@ -295,53 +291,27 @@ private void removeImage(String dockerImageName) { } } - private void prune(PruneType pruneType, List> filters) { - String[] labels = filters.stream() - .filter(it -> "label".equals(it.getKey())) - .map(Map.Entry::getValue) - .toArray(String[]::new); - switch (pruneType) { - // Docker only prunes stopped containers, so we have to do it manually - case CONTAINERS: - List containers = dockerClient.listContainersCmd() - .withFilter("label", Arrays.asList(labels)) - .withShowAll(true) - .exec(); - - containers.parallelStream().forEach(container -> { - removeContainer(container.getId(), container.getImage()); - }); - break; - default: - dockerClient.pruneCmd(pruneType).withLabelFilter(labels).exec(); - break; + void setHook() { + if (hookIsSet.compareAndSet(false, true)) { + // If the JVM stops without containers being stopped, try and stop the container. + Runtime.getRuntime().addShutdownHook(new Thread(DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, this::performCleanup)); } } /** - * @deprecated internal API, not intended for public usage + * + * @deprecated internal API */ @Deprecated - public void setHook() { - if (hookIsSet.compareAndSet(false, true)) { - // If the JVM stops without containers being stopped, try and stop the container. - Runtime.getRuntime().addShutdownHook( - new Thread(DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, - () -> { - performCleanup(); - - if (!ryukStarted.get()) { - synchronized (DEATH_NOTE) { - DEATH_NOTE.forEach(filters -> prune(PruneType.CONTAINERS, filters)); - DEATH_NOTE.forEach(filters -> prune(PruneType.NETWORKS, filters)); - DEATH_NOTE.forEach(filters -> prune(PruneType.VOLUMES, filters)); - DEATH_NOTE.forEach(filters -> prune(PruneType.IMAGES, filters)); - } - } - } - ) - ); - } + public Map getLabels() { + return MARKER_LABELS; + } + + /** + * @deprecated internal API + */ + @Deprecated + public void init() { } static class FilterRegistry { diff --git a/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java b/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java index e8aed92fa9d..743b33856cf 100644 --- a/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java @@ -29,13 +29,21 @@ import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; import java.util.stream.Stream; import static org.awaitility.Awaitility.await; +/** + * Ryuk-based {@link ResourceReaper} implementation. + * + * @see
moby-ryuk + * @deprecated internal API + */ +@Deprecated @Slf4j -class RyukResourceReaper { +public class RyukResourceReaper extends ResourceReaper { private static final RateLimiter RYUK_ACK_RATE_LIMITER = RateLimiterBuilder .newBuilder() @@ -43,11 +51,40 @@ class RyukResourceReaper { .withConstantThroughput() .build(); + private final AtomicBoolean started = new AtomicBoolean(false); + @Getter - private final String containerId; + private String containerId = null; + + @Override + public void init() { + if (!TestcontainersConfiguration.getInstance().environmentSupportsReuse()) { + log.debug("Ryuk is enabled"); + maybeStart(); + log.info("Ryuk started - will monitor and terminate Testcontainers containers on JVM exit"); + } else { + log.debug("Ryuk is enabled but will be started on demand"); + } + } + + @Override + public void registerLabelsFilterForCleanup(Map labels) { + maybeStart(); + super.registerLabelsFilterForCleanup(labels); + } + + @Override + public Map getLabels() { + maybeStart(); + return super.getLabels(); + } @SneakyThrows(InterruptedException.class) - RyukResourceReaper(DockerClient client) { + private synchronized void maybeStart() { + if (!started.compareAndSet(false, true)) { + return; + } + DockerClient client = DockerClientFactory.lazyClient(); String ryukImage = ImageNameSubstitutor.instance() .apply(DockerImageName.parse("testcontainers/ryuk:0.3.3")) .asCanonicalNameString(); diff --git a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java index d484cc92eaf..16d25b2f625 100644 --- a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java +++ b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java @@ -4,17 +4,13 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.testcontainers.TestImages.TINY_IMAGE; -import com.github.dockerjava.api.DockerClient; -import com.github.dockerjava.api.exception.DockerException; import com.github.dockerjava.api.exception.NotFoundException; import org.junit.Rule; import org.junit.Test; -import org.mockito.Mockito; import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.DockerClientFactory.DiskSpaceUsage; import org.testcontainers.dockerclient.LogToStringContainerCallback; import org.testcontainers.utility.MockTestcontainersConfigurationRule; -import org.testcontainers.utility.TestcontainersConfiguration; /** * Test for {@link DockerClientFactory}. @@ -65,27 +61,15 @@ public void dockerHostIpAddress() { @Test public void failedChecksFailFast() { - Mockito.doReturn(false).when(TestcontainersConfiguration.getInstance()).isDisableChecks(); - - // Make sure that Ryuk is started - assertThat(DockerClientFactory.instance().client()).isNotNull(); - - DockerClientFactory instance = new DockerClientFactory(); - DockerClient dockerClient = instance.dockerClient; + DockerClientFactory instance = DockerClientFactory.instance(); + assertThat(instance.client()).isNotNull(); assertThat(instance.cachedClientFailure).isNull(); try { - // Remove cached client to force the initialization logic - instance.dockerClient = null; - - // Ryuk should fail to start twice due to the name conflict (equal to the session id) - assertThatThrownBy(instance::client).isInstanceOf(DockerException.class); - RuntimeException failure = new IllegalStateException("Boom!"); instance.cachedClientFailure = failure; // Fail fast assertThatThrownBy(instance::client).isEqualTo(failure); } finally { - instance.dockerClient = dockerClient; instance.cachedClientFailure = null; } } From 55a131545b758c04afa1bb0af4319d505ac3abde Mon Sep 17 00:00:00 2001 From: Yannick Weber Date: Tue, 22 Feb 2022 13:15:20 +0100 Subject: [PATCH 2406/5615] Every HiveMQ IT now uses `getHost()` (#5106) Ensures better portability across different Docker environments. --- .../hivemq/ContainerWithCustomConfigIT.java | 1 + .../ContainerWithExtensionFromDirectoryIT.java | 4 ++-- .../hivemq/ContainerWithExtensionIT.java | 6 +++--- .../ContainerWithExtensionSubclassIT.java | 2 +- .../ContainerWithFileInExtensionHomeIT.java | 2 +- .../hivemq/ContainerWithFileInHomeIT.java | 2 +- .../hivemq/ContainerWithLicenseIT.java | 2 +- .../ContainerWithoutPlatformExtensionsIT.java | 2 ++ .../hivemq/CreateFileInCopiedDirectoryIT.java | 2 +- .../CreateFileInExtensionDirectoryIT.java | 2 +- .../DisableEnableExtensionFromDirectoryIT.java | 6 +++--- .../hivemq/DisableEnableExtensionIT.java | 8 ++++---- .../hivemq/docs/DemoExtensionTestsIT.java | 1 + .../hivemq/docs/DemoFilesIT.java | 1 + .../hivemq/docs/DemoHiveMQContainerIT.java | 1 + .../hivemq/util/TestPublishModifiedUtil.java | 18 ++++++++++-------- 16 files changed, 34 insertions(+), 26 deletions(-) diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java index 17f09d86adf..57d3b75bc24 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java @@ -27,6 +27,7 @@ void test() throws Exception { final Mqtt5BlockingClient publisher = Mqtt5Client.builder() .identifier("publisher") .serverPort(hivemq.getMqttPort()) + .serverHost(hivemq.getHost()) .buildBlocking(); publisher.connect(); diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionFromDirectoryIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionFromDirectoryIT.java index 7672c70925d..4b89e418b05 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionFromDirectoryIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionFromDirectoryIT.java @@ -23,7 +23,7 @@ void test() throws Exception { .withLogLevel(Level.DEBUG)) { hivemq.start(); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } } @@ -37,7 +37,7 @@ void test_wrongDirectoryName() throws Exception { .withLogLevel(Level.DEBUG)) { hivemq.start(); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionIT.java index 3ad7b26148a..8979d2c17b0 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionIT.java @@ -27,15 +27,15 @@ void test() throws Exception { .withExtension(hiveMQExtension)) { hivemq.start(); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); hivemq.stop(); hivemq.start(); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); hivemq.stop(); hivemq.start(); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionSubclassIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionSubclassIT.java index 33880a800c9..53714123aff 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionSubclassIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionSubclassIT.java @@ -29,7 +29,7 @@ void test() throws Exception { .withLogLevel(Level.DEBUG)) { hivemq.start(); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInExtensionHomeIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInExtensionHomeIT.java index 1d2b1705eae..559737c8868 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInExtensionHomeIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInExtensionHomeIT.java @@ -43,7 +43,7 @@ void test() throws Exception { "/additionalFiles/my-file.txt")) { hivemq.start(); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInHomeIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInHomeIT.java index 542c54789e8..efdf6ad298d 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInHomeIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInHomeIT.java @@ -41,7 +41,7 @@ void test() throws Exception { hivemq.start(); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithLicenseIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithLicenseIT.java index 4fc27adf8c2..ba2dc472cdb 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithLicenseIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithLicenseIT.java @@ -41,7 +41,7 @@ void test() throws Exception { hivemq.start(); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java index 37b37dc0d59..eaec72cb038 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java @@ -49,6 +49,7 @@ public void removeAllPlatformExtensions() throws InterruptedException { final Mqtt5BlockingClient client = MqttClient.builder() .serverPort(hivemq.getMqttPort()) + .serverHost(hivemq.getHost()) .useMqttVersion5() .buildBlocking(); @@ -82,6 +83,7 @@ public void removeKafkaExtension() throws InterruptedException { final Mqtt5BlockingClient client = MqttClient.builder() .serverPort(hivemq.getMqttPort()) + .serverHost(hivemq.getHost()) .useMqttVersion5() .buildBlocking(); diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java index 85089374624..58b70c3d1d9 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java @@ -51,7 +51,7 @@ void test() throws Exception { .withFileInHomeFolder(createDirectory(), "directory")) { hivemq.start(); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInExtensionDirectoryIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInExtensionDirectoryIT.java index a7756c69f1d..c41137f58c3 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInExtensionDirectoryIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInExtensionDirectoryIT.java @@ -39,7 +39,7 @@ void test() throws Exception { .withExtension(hiveMQExtension)) { hivemq.start(); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java index 2248681a769..f6b1b9b5cd7 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java @@ -26,11 +26,11 @@ void test() throws Exception { hivemq.start(); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); hivemq.disableExtension("Modifier Extension", "modifier-extension"); - assertThrows(ExecutionException.class, () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort())); + assertThrows(ExecutionException.class, () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost())); hivemq.enableExtension("Modifier Extension", "modifier-extension"); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java index 7cc943c5527..5b3c6ab107f 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java @@ -33,13 +33,13 @@ void test() throws Exception { hivemq.start(); - assertThrows(ExecutionException.class, () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort())); + assertThrows(ExecutionException.class, () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost())); hivemq.enableExtension(hiveMQExtension); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); hivemq.disableExtension(hiveMQExtension); - assertThrows(ExecutionException.class, () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort())); + assertThrows(ExecutionException.class, () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost())); hivemq.enableExtension(hiveMQExtension); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoExtensionTestsIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoExtensionTestsIT.java index 2627dca2028..3a8ccc1bebe 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoExtensionTestsIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoExtensionTestsIT.java @@ -47,6 +47,7 @@ public void test() throws Exception { // mqtt5client { final Mqtt5BlockingClient client = Mqtt5Client.builder() .serverPort(hivemqWithClasspathExtension.getMqttPort()) + .serverHost(hivemqWithClasspathExtension.getHost()) .buildBlocking(); client.connect(); diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java index 46c24e8e51d..7ec777d02b8 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java @@ -52,6 +52,7 @@ public void test() throws Exception { // mqtt5client { final Mqtt5BlockingClient client = Mqtt5Client.builder() .serverPort(hivemq.getMqttPort()) + .serverHost(hivemq.getHost()) .buildBlocking(); client.connect(); diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java index 60683c8d612..51e3a43d9e5 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java @@ -51,6 +51,7 @@ public void test() throws Exception { // mqtt5client { final Mqtt5BlockingClient client = Mqtt5Client.builder() .serverPort(hivemqCe.getMqttPort()) + .serverHost(hivemqCe.getHost()) .buildBlocking(); client.connect(); diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/TestPublishModifiedUtil.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/TestPublishModifiedUtil.java index b2712ef5a1d..f2f1cfd692f 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/TestPublishModifiedUtil.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/TestPublishModifiedUtil.java @@ -3,27 +3,29 @@ import com.hivemq.client.mqtt.MqttGlobalPublishFilter; import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; +import org.jetbrains.annotations.NotNull; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; public class TestPublishModifiedUtil { - public static void testPublishModified(final int mqttPort) throws InterruptedException, ExecutionException { + public static void testPublishModified(final int mqttPort, final @NotNull String host) throws Exception { final CompletableFuture publishReceived = new CompletableFuture<>(); final Mqtt5BlockingClient publisher = Mqtt5Client.builder() - .serverPort(mqttPort) - .identifier("publisher") - .buildBlocking(); + .serverPort(mqttPort) + .serverHost(host) + .identifier("publisher") + .buildBlocking(); publisher.connect(); final Mqtt5BlockingClient subscriber = Mqtt5Client.builder() - .serverPort(mqttPort) - .identifier("subscriber") - .buildBlocking(); + .serverPort(mqttPort) + .serverHost(host) + .identifier("subscriber") + .buildBlocking(); subscriber.connect(); subscriber.subscribeWith().topicFilter("test/topic").send(); subscriber.toAsync().publishes(MqttGlobalPublishFilter.ALL, publish -> { From fdb58fb3d3dd82dbbd7cf6a2be52ecbbcdbc25db Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 22 Feb 2022 08:33:06 -0500 Subject: [PATCH 2407/5615] Do not mount Selenium's SHM on Windows (#5105) It looks like mounting /dev/shm did not play well on WSL2. This change uses Docker's SHM support on Windows. --- .../containers/BrowserWebDriverContainer.java | 9 ++++++++- .../junit/BrowserWebDriverContainerTest.java | 3 +++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index bd38dead8ac..e47c832e2ff 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -16,6 +16,9 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang.SystemUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.openqa.selenium.Capabilities; @@ -205,7 +208,11 @@ protected void configure() { setCommand("/opt/bin/entry_point.sh"); if (getShmSize() == null) { - this.getBinds().add(new Bind("/dev/shm", new Volume("/dev/shm"), AccessMode.rw)); + if (SystemUtils.IS_OS_WINDOWS) { + withSharedMemorySize(512 * FileUtils.ONE_MB); + } else { + this.getBinds().add(new Bind("/dev/shm", new Volume("/dev/shm"), AccessMode.rw)); + } } /* diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java index 7b7d3a46733..0589d764573 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java @@ -2,6 +2,8 @@ import com.github.dockerjava.api.command.InspectContainerResponse; import org.apache.commons.io.FileUtils; +import org.apache.commons.lang.SystemUtils; +import org.junit.Assume; import org.junit.Test; import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.firefox.FirefoxOptions; @@ -50,6 +52,7 @@ public void provideDefaultNoProxyEnvironmentIfNotSet() { @Test public void createContainerWithShmVolume() { + Assume.assumeFalse("SHM isn't mounted on Windows", SystemUtils.IS_OS_WINDOWS); try ( BrowserWebDriverContainer webDriverContainer = new BrowserWebDriverContainer() .withCapabilities(new FirefoxOptions()) From 14eff07a7cae8d48b07b80e309bba601f164ae66 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 22 Feb 2022 09:50:27 -0500 Subject: [PATCH 2408/5615] Update to `docker-java` 3.2.13 (#5045) * Try `docker-java` 3.2.13 * Fix imports * Update build.gradle * Update SimplePostgreSQLTest.java * Use `withCgroupnsMode` * fix `BrowserWebDriverContainer` * FML --- core/build.gradle | 10 +++------- .../java/org/testcontainers/DockerClientFactory.java | 4 ++-- .../org/testcontainers/containers/ContainerState.java | 2 +- .../containers/DockerComposeContainer.java | 4 ++-- .../testcontainers/containers/GenericContainer.java | 4 ++-- .../dockerclient/DockerClientConfigUtils.java | 2 +- .../dockerclient/DockerClientProviderStrategy.java | 2 +- .../NpipeSocketClientProviderStrategy.java | 2 +- .../RootlessDockerClientProviderStrategy.java | 4 ++-- .../dockerclient/UnixSocketClientProviderStrategy.java | 2 +- .../images/builder/ImageFromDockerfile.java | 2 +- .../images/builder/traits/StringsTrait.java | 2 +- .../java/org/testcontainers/utility/AuditLogger.java | 2 +- .../testcontainers/utility/DockerMachineClient.java | 2 +- .../java/org/testcontainers/utility/MountableFile.java | 2 +- .../testcontainers/utility/RegistryAuthLocator.java | 6 +++--- .../utility/TestcontainersConfiguration.java | 2 +- .../containers/output/ToStringConsumerTest.java | 2 +- .../dockerfile/statement/AbstractStatementTest.java | 6 +++--- .../utility/RegistryAuthLocatorTest.java | 2 +- .../main/java/org/testcontainers/ext/ScriptUtils.java | 2 +- .../testcontainers/jdbc/AbstractJDBCDriverTest.java | 2 +- .../containers/JdbcDatabaseContainer.java | 2 +- .../main/java/org/testcontainers/k3s/K3sContainer.java | 4 +--- .../junit/mariadb/SimpleMariaDBTest.java | 2 +- .../mssqlserver/CustomPasswordMSSQLServerTest.java | 2 +- .../testcontainers/junit/mysql/SimpleMySQLTest.java | 2 +- .../org/testcontainers/containers/OracleContainer.java | 10 +++++----- .../containers/BrowserWebDriverContainer.java | 2 +- .../junit/BrowserWebDriverContainerTest.java | 2 +- .../junit/LocalServerWebDriverContainerTest.java | 2 +- .../org/testcontainers/containers/SolrContainer.java | 2 +- 32 files changed, 46 insertions(+), 52 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index 7b014d1ad24..a471aa4816e 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -73,17 +73,13 @@ dependencies { shaded 'org.awaitility:awaitility:4.1.1' - api platform('com.github.docker-java:docker-java-bom:3.2.12') - shaded platform('com.github.docker-java:docker-java-bom:3.2.12') + api platform('com.github.docker-java:docker-java-bom:3.2.13') + shaded platform('com.github.docker-java:docker-java-bom:3.2.13') api "com.github.docker-java:docker-java-api" shaded 'com.github.docker-java:docker-java-core' - - shaded 'com.github.docker-java:docker-java-transport-okhttp', { - // TODO remove unused `net.java.dev.jna:jna-platform` from `docker-java-transport-okhttp` - exclude(group: 'net.java.dev.jna', module: 'jna-platform') - } + shaded 'com.github.docker-java:docker-java-transport-okhttp' api 'com.github.docker-java:docker-java-transport-zerodep' diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index d0a4e4a4fb3..9a6165ef55f 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -20,8 +20,8 @@ import lombok.SneakyThrows; import lombok.Synchronized; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; import org.testcontainers.dockerclient.DockerClientProviderStrategy; import org.testcontainers.dockerclient.DockerMachineClientProviderStrategy; import org.testcontainers.dockerclient.TransportConfig; diff --git a/core/src/main/java/org/testcontainers/containers/ContainerState.java b/core/src/main/java/org/testcontainers/containers/ContainerState.java index 5de9ef7c566..7fe90bd57a4 100644 --- a/core/src/main/java/org/testcontainers/containers/ContainerState.java +++ b/core/src/main/java/org/testcontainers/containers/ContainerState.java @@ -12,7 +12,7 @@ import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.apache.commons.compress.utils.IOUtils; -import org.apache.commons.lang.math.NumberUtils; +import org.apache.commons.lang3.math.NumberUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testcontainers.DockerClientFactory; diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index b56c70cb959..b8913e8ac7c 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -12,8 +12,8 @@ import com.google.common.util.concurrent.Uninterruptibles; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; import org.junit.runner.Description; import org.junit.runners.model.Statement; import org.slf4j.Logger; diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index aca543ef7ec..22a4f0ba739 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -27,8 +27,8 @@ import lombok.Setter; import lombok.SneakyThrows; import org.apache.commons.io.FileUtils; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.junit.runner.Description; diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java index b494eb087f6..e393e59419f 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java @@ -2,7 +2,7 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.testcontainers.DockerClientFactory; import java.io.File; diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index b595ddba6ca..89383698adf 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -14,7 +14,7 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.Nullable; import org.rnorth.ducttape.TimeoutException; import org.rnorth.ducttape.ratelimits.RateLimiter; diff --git a/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java index fc3c65fd3dd..7f73e1d7675 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java @@ -1,6 +1,6 @@ package org.testcontainers.dockerclient; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.SystemUtils; import java.net.URI; diff --git a/core/src/main/java/org/testcontainers/dockerclient/RootlessDockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/RootlessDockerClientProviderStrategy.java index 3ee58f4910a..36d6b5f6123 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/RootlessDockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/RootlessDockerClientProviderStrategy.java @@ -4,8 +4,8 @@ import com.sun.jna.Native; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; import org.jetbrains.annotations.Nullable; import java.net.URI; diff --git a/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java index 6f030ada279..4a85394155d 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java @@ -1,6 +1,6 @@ package org.testcontainers.dockerclient; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.SystemUtils; import java.io.IOException; import java.net.URI; diff --git a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java index a434dee2b41..abf13f8ef02 100644 --- a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java +++ b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java @@ -9,7 +9,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.apache.commons.io.FileUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.testcontainers.DockerClientFactory; import org.testcontainers.images.ParsedDockerfile; diff --git a/core/src/main/java/org/testcontainers/images/builder/traits/StringsTrait.java b/core/src/main/java/org/testcontainers/images/builder/traits/StringsTrait.java index 51423068ec9..2b23551ee58 100644 --- a/core/src/main/java/org/testcontainers/images/builder/traits/StringsTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/traits/StringsTrait.java @@ -1,7 +1,7 @@ package org.testcontainers.images.builder.traits; import lombok.Getter; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.testcontainers.images.builder.Transferable; /** diff --git a/core/src/main/java/org/testcontainers/utility/AuditLogger.java b/core/src/main/java/org/testcontainers/utility/AuditLogger.java index 567132da05d..f6c4559485d 100644 --- a/core/src/main/java/org/testcontainers/utility/AuditLogger.java +++ b/core/src/main/java/org/testcontainers/utility/AuditLogger.java @@ -5,7 +5,7 @@ import com.github.dockerjava.api.command.DockerCmd; import lombok.experimental.UtilityClass; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.MDC; diff --git a/core/src/main/java/org/testcontainers/utility/DockerMachineClient.java b/core/src/main/java/org/testcontainers/utility/DockerMachineClient.java index 021baf270d9..6f1e2c9d50b 100644 --- a/core/src/main/java/org/testcontainers/utility/DockerMachineClient.java +++ b/core/src/main/java/org/testcontainers/utility/DockerMachineClient.java @@ -1,7 +1,7 @@ package org.testcontainers.utility; import lombok.NonNull; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.SystemUtils; import org.slf4j.Logger; import java.util.List; diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java index 071d36631b0..19dca4c134c 100644 --- a/core/src/main/java/org/testcontainers/utility/MountableFile.java +++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java @@ -7,7 +7,7 @@ import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.apache.commons.compress.archivers.tar.TarConstants; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.SystemUtils; import org.jetbrains.annotations.NotNull; import org.testcontainers.DockerClientFactory; import org.testcontainers.UnstableAPI; diff --git a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java index b373eedaa03..d88bdccd76a 100644 --- a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java +++ b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java @@ -4,8 +4,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.github.dockerjava.api.model.AuthConfig; import com.google.common.annotations.VisibleForTesting; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; import org.slf4j.Logger; import org.zeroturnaround.exec.InvalidResultException; import org.zeroturnaround.exec.ProcessExecutor; @@ -22,7 +22,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import static org.apache.commons.lang.StringUtils.isBlank; +import static org.apache.commons.lang3.StringUtils.isBlank; import static org.slf4j.LoggerFactory.getLogger; import static org.testcontainers.utility.AuthConfigUtil.toSafeString; diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index bb3febd6b3f..c6ce7496dd9 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -8,7 +8,7 @@ import lombok.SneakyThrows; import lombok.Synchronized; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang.exception.ExceptionUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java b/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java index 8da67e897e9..1760a8031b2 100644 --- a/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java +++ b/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java @@ -2,7 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.apache.commons.lang.RandomStringUtils; +import org.apache.commons.lang3.RandomStringUtils; import org.assertj.core.api.Assertions; import org.junit.Test; import org.testcontainers.containers.Container.ExecResult; diff --git a/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/AbstractStatementTest.java b/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/AbstractStatementTest.java index 35d325c5f40..15c73cadb5f 100644 --- a/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/AbstractStatementTest.java +++ b/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/AbstractStatementTest.java @@ -1,8 +1,8 @@ package org.testcontainers.images.builder.dockerfile.statement; import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.exception.ExceptionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.junit.Rule; import org.junit.rules.TestName; import org.rnorth.ducttape.Preconditions; @@ -31,7 +31,7 @@ protected void assertStatement(Statement statement) { IOUtils.closeQuietly(inputStream); expectedLines = StringUtils.chomp(content.replaceAll("\r\n", "\n").trim()).split("\n"); } catch (Exception e) { - fail("can't load fixture '" + testName.getMethodName() + "'\n" + ExceptionUtils.getFullStackTrace(e)); + fail("can't load fixture '" + testName.getMethodName() + "'\n" + ExceptionUtils.getStackTrace(e)); } StringBuilder builder = new StringBuilder(); diff --git a/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java b/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java index a5e5ab4260d..5db2583958a 100644 --- a/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java +++ b/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java @@ -2,7 +2,7 @@ import com.github.dockerjava.api.model.AuthConfig; import com.google.common.io.Resources; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.SystemUtils; import org.jetbrains.annotations.NotNull; import org.junit.Test; diff --git a/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java b/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java index d30064af28e..76f0cb90ea0 100644 --- a/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java +++ b/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java @@ -17,7 +17,7 @@ package org.testcontainers.ext; import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testcontainers.delegate.DatabaseDelegate; diff --git a/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java b/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java index 580aad513e3..4e5eb03bb49 100644 --- a/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java +++ b/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java @@ -3,7 +3,7 @@ import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import org.apache.commons.dbutils.QueryRunner; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.SystemUtils; import org.junit.AfterClass; import org.junit.Test; import org.junit.runners.Parameterized.Parameter; diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index cf5ae150575..c4bacf3c8ff 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -4,7 +4,7 @@ import java.sql.Statement; import lombok.NonNull; import lombok.SneakyThrows; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.traits.LinkableContainer; import org.testcontainers.delegate.DatabaseDelegate; diff --git a/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java b/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java index 511b309e0d5..54cb2ff500f 100644 --- a/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java +++ b/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java @@ -27,9 +27,7 @@ public K3sContainer(DockerImageName dockerImageName) { addExposedPorts(6443, 8443); setPrivilegedMode(true); withCreateContainerCmdModifier(it -> { - DockerObjectAccessor.overrideRawValue( - it.getHostConfig(), "CgroupnsMode", "host" - ); + it.getHostConfig().withCgroupnsMode("host"); }); addFileSystemBind("/sys/fs/cgroup", "/sys/fs/cgroup", BindMode.READ_WRITE); diff --git a/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java b/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java index 4ea98bbe1fe..2dcdbafd9e3 100644 --- a/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java +++ b/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java @@ -1,6 +1,6 @@ package org.testcontainers.junit.mariadb; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.SystemUtils; import org.junit.Test; import org.testcontainers.containers.MariaDBContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java index 3df8ff0fa3d..6ebd3624577 100644 --- a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java +++ b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java @@ -1,6 +1,6 @@ package org.testcontainers.junit.mssqlserver; -import org.apache.commons.lang.RandomStringUtils; +import org.apache.commons.lang3.RandomStringUtils; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java index 1b6143b3b34..90973bc4b56 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java @@ -1,6 +1,6 @@ package org.testcontainers.junit.mysql; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.SystemUtils; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java index c833689e692..689646f0006 100644 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java @@ -1,7 +1,7 @@ package org.testcontainers.containers; import com.google.common.collect.Sets; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.utility.DockerImageName; @@ -34,7 +34,7 @@ public class OracleContainer extends JdbcDatabaseContainer { static final String DEFAULT_SID = "xe"; static final String DEFAULT_SYSTEM_USER = "system"; static final String DEFAULT_SYS_USER = "sys"; - + // Test container defaults static final String APP_USER = "test"; static final String APP_USER_PASSWORD = "test"; @@ -75,7 +75,7 @@ private void preconfigure() { .withRegEx(".*DATABASE IS READY TO USE!.*\\s") .withTimes(1) .withStartupTimeout(Duration.of(DEFAULT_STARTUP_TIMEOUT_SECONDS, SECONDS)); - + withConnectTimeoutSeconds(DEFAULT_CONNECT_TIMEOUT_SECONDS); addExposedPorts(ORACLE_PORT, APEX_HTTP_PORT); } @@ -98,7 +98,7 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return isUsingSid() ? + return isUsingSid() ? "jdbc:oracle:thin:" + "@" + getHost() + ":" + getOraclePort() + ":" + getSid() : "jdbc:oracle:thin:" + "@" + getHost() + ":" + getOraclePort() + "/" + getDatabaseName(); } @@ -195,7 +195,7 @@ protected void configure() { if(databaseName != DEFAULT_DATABASE_NAME) { withEnv("ORACLE_DATABASE", databaseName); } - + withEnv("APP_USER", username); withEnv("APP_USER_PASSWORD", password); } diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index e47c832e2ff..12bbbc07de5 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -18,7 +18,7 @@ import java.util.concurrent.TimeUnit; import org.apache.commons.io.FileUtils; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.SystemUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.openqa.selenium.Capabilities; diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java index 0589d764573..a53fa532f99 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java @@ -2,7 +2,7 @@ import com.github.dockerjava.api.command.InspectContainerResponse; import org.apache.commons.io.FileUtils; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.SystemUtils; import org.junit.Assume; import org.junit.Test; import org.openqa.selenium.chrome.ChromeOptions; diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java index f7d626c15b5..772193e2d43 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java @@ -10,7 +10,7 @@ import org.testcontainers.containers.BrowserWebDriverContainer; import org.testcontainers.utility.TestEnvironment; -import static org.apache.commons.lang.SystemUtils.IS_OS_MAC_OSX; +import static org.apache.commons.lang3.SystemUtils.IS_OS_MAC_OSX; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; /** diff --git a/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java b/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java index 4bc2f19ec86..4d0b5cd1ae1 100644 --- a/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java +++ b/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java @@ -2,7 +2,7 @@ import com.github.dockerjava.api.command.InspectContainerResponse; import lombok.SneakyThrows; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.utility.DockerImageName; From 244c77c618bfdd9ba5dad464a22b4b56789550c2 Mon Sep 17 00:00:00 2001 From: Gerrit Meier Date: Fri, 25 Feb 2022 12:07:11 +0100 Subject: [PATCH 2409/5615] [Neo4j] Include code from tests in docs (#5120) --- docs/modules/databases/neo4j.md | 149 ++++-------------- examples/neo4j-container/build.gradle | 14 ++ .../containers/Neo4jExampleTest.java | 77 +++++++++ examples/settings.gradle | 1 + .../Neo4jContainerJUnitIntegrationTest.java | 2 +- .../containers/Neo4jContainerTest.java | 27 +++- .../containers/Neo4jTestImages.java | 7 - 7 files changed, 143 insertions(+), 134 deletions(-) create mode 100644 examples/neo4j-container/build.gradle create mode 100644 examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java delete mode 100644 modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jTestImages.java diff --git a/docs/modules/databases/neo4j.md b/docs/modules/databases/neo4j.md index 17e1e154fc8..87a5d9157d1 100644 --- a/docs/modules/databases/neo4j.md +++ b/docs/modules/databases/neo4j.md @@ -6,68 +6,18 @@ Note that it's based on the [official Docker image](https://hub.docker.com/_/neo ## Usage example -Declare your Testcontainer as a `@ClassRule` or `@Rule` in a JUnit 4 test or as static or member attribute of a JUnit 5 test annotated with `@Container` as you would with other Testcontainers. -You can either use call `getHttpUrl()` or `getBoltUrl()` on the Neo4j container. -`getHttpUrl()` gives you the HTTP-address of the transactional HTTP endpoint while `getBoltUrl()` is meant to be used with one of the [official Bolt drivers](https://neo4j.com/developer/language-guides/). +Declare your Testcontainers as a `@ClassRule` or `@Rule` in a JUnit 4 test or as static or member attribute of a JUnit 5 test annotated with `@Container` as you would with other Testcontainers. +You can either use call `getBoltUrl()` or `getHttpUrl()` on the Neo4j container. +`getBoltUrl()` is meant to be used with one of the [official Bolt drivers](https://neo4j.com/developer/language-guides/) while `getHttpUrl()` gives you the HTTP-address of the transactional HTTP endpoint. On the JVM you would most likely use the [Java driver](https://github.com/neo4j/neo4j-java-driver). The following example uses the JUnit 5 extension `@Testcontainers` and demonstrates both the usage of the Java Driver and the REST endpoint: -=== "JUnit 5 example" - ```java - @Testcontainers - public class ExampleTest { - - @Container - private static Neo4jContainer neo4jContainer = new Neo4jContainer() - .withAdminPassword(null); // Disable password - - @Test - void testSomethingUsingBolt() { - - // Retrieve the Bolt URL from the container - String boltUrl = neo4jContainer.getBoltUrl(); - try ( - Driver driver = GraphDatabase.driver(boltUrl, AuthTokens.none()); - Session session = driver.session() - ) { - long one = session.run("RETURN 1", Collections.emptyMap()).next().get(0).asLong(); - assertThat(one, is(1L)); - } catch (Exception e) { - fail(e.getMessage()); - } - } - - @Test - void testSomethingUsingHttp() throws IOException { - - // Retrieve the HTTP URL from the container - String httpUrl = neo4jContainer.getHttpUrl(); - - URL url = new URL(httpUrl + "/db/data/transaction/commit"); - HttpURLConnection con = (HttpURLConnection) url.openConnection(); - - con.setRequestMethod("POST"); - con.setRequestProperty("Content-Type", "application/json"); - con.setDoOutput(true); - - try (Writer out = new OutputStreamWriter(con.getOutputStream())) { - out.write("{\"statements\":[{\"statement\":\"RETURN 1\"}]}"); - out.flush(); - } - - assertThat(con.getResponseCode(), is(HttpURLConnection.HTTP_OK)); - try (BufferedReader buffer = new BufferedReader(new InputStreamReader(con.getInputStream()))) { - String expectedResponse = - "{\"results\":[{\"columns\":[\"1\"],\"data\":[{\"row\":[1],\"meta\":[null]}]}],\"errors\":[]}"; - String response = buffer.lines().collect(Collectors.joining("\n")); - assertThat(response, is(expectedResponse)); - } - } - } - ``` + +[JUnit 5 example](../../../examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java) inside_block:junitExample + -You are not limited to Unit tests and can of course use an instance of the Neo4j Testcontainer in vanilla Java code as well. +You are not limited to Unit tests and can of course use an instance of the Neo4j Testcontainers in vanilla Java code as well. ## Additional features @@ -75,70 +25,40 @@ You are not limited to Unit tests and can of course use an instance of the Neo4j Authentication can be disabled: -```java -@Testcontainers -public class ExampleTest { - - @Container - Neo4jContainer neo4jContainer = new Neo4jContainer() - .withoutAuthentication(); -} -``` + +[Disable authentication](../../../modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java) inside_block:withoutAuthentication + ### Neo4j-Configuration Neo4j's Docker image needs Neo4j configuration options in a dedicated format. -The container takes care of that and you can configure the database with standard options like the following: - -```java -@Testcontainers -public class ExampleTest { +The container takes care of that, and you can configure the database with standard options like the following: - @Container - Neo4jContainer neo4jContainer = new Neo4jContainer() - .withNeo4jConfig("dbms.security.procedures.unrestricted", "apoc.*,algo.*"); -} -``` + +[Neo4j configuration](../../../modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java) inside_block:neo4jConfiguration + ### Add custom plugins Custom plugins, like APOC, can be copied over to the container from any classpath or host resource like this: -```java -@Testcontainers -public class ExampleTest { - - @Container - Neo4jContainer neo4jContainer = new Neo4jContainer() - .withPlugins(MountableFile.forClasspathResource("/apoc-3.5.0.1-all.jar")); -} -``` + +[Plugin jar](../../../modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java) inside_block:registerPluginsJar + Whole directories work as well: -```java -@Testcontainers -public class ExampleTest { - - @Container - Neo4jContainer neo4jContainer = new Neo4jContainer() - .withPlugins(MountableFile.forClasspathResource("/my-plugins")); -} -``` + +[Plugin folder](../../../modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java) inside_block:registerPluginsPath + ### Start the container with a predefined database If you have an existing database (`graph.db`) you want to work with, copy it over to the container like this: -```java -@Testcontainers -public class ExampleTest { - - @Container - Neo4jContainer neo4jContainer = new Neo4jContainer() - .withDatabase(MountableFile.forClasspathResource("/test-graph.db")); -} -``` + +[Copy database](../../../modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java) inside_block:copyDatabase + !!! note The `withDatabase` method will only work with Neo4j 3.5 and throw an exception if used in combination with a newer version. @@ -147,16 +67,11 @@ The `withDatabase` method will only work with Neo4j 3.5 and throw an exception i If you need the Neo4j enterprise license, you can declare your Neo4j container like this: -```java -@Testcontainers -public class ExampleTest { - @ClassRule - public static Neo4jContainer neo4jContainer = new Neo4jContainer() - .withEnterpriseEdition(); -} -``` + +[Enterprise edition](../../../modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java) inside_block:enterpriseEdition + -This creates a Testcontainer based on the Docker image build with the Enterprise version of Neo4j. +This creates a Testcontainers based on the Docker image build with the Enterprise version of Neo4j. The call to `withEnterpriseEdition` adds the required environment variable that you accepted the terms and condition of the enterprise version. You accept those by adding a file named `container-license-acceptance.txt` to the root of your classpath containing the text `neo4j:3.5.0-enterprise` in one line. You'll find more information about licensing Neo4j here: [About Neo4j Licenses](https://neo4j.com/licensing/). @@ -181,11 +96,11 @@ Add the following dependency to your `pom.xml`/`build.gradle` file: ``` !!! hint - Add the Neo4j Java driver if you plan to access the Testcontainer via Bolt: + Add the Neo4j Java driver if you plan to access the Testcontainers via Bolt: === "Gradle" ```groovy - compile "org.neo4j.driver:neo4j-java-driver:1.7.1" + compile "org.neo4j.driver:neo4j-java-driver:4.4.3" ``` === "Maven" @@ -193,10 +108,6 @@ Add the following dependency to your `pom.xml`/`build.gradle` file: org.neo4j.driver neo4j-java-driver - 1.7.1 + 4.4.3 ``` - - - - diff --git a/examples/neo4j-container/build.gradle b/examples/neo4j-container/build.gradle new file mode 100644 index 00000000000..718adc3183a --- /dev/null +++ b/examples/neo4j-container/build.gradle @@ -0,0 +1,14 @@ +plugins { + id 'java' +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation 'org.assertj:assertj-core:3.22.0' + testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.3' + testImplementation 'org.testcontainers:neo4j' + testImplementation 'org.testcontainers:junit-jupiter' +} diff --git a/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java b/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java new file mode 100644 index 00000000000..7730d4fa8d0 --- /dev/null +++ b/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java @@ -0,0 +1,77 @@ +package org.testcontainers.containers; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Collections; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; +import org.neo4j.driver.AuthTokens; +import org.neo4j.driver.Driver; +import org.neo4j.driver.GraphDatabase; +import org.neo4j.driver.Session; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.fail; + +// junitExample { +@Testcontainers +public class Neo4jExampleTest { + + @Container + private static Neo4jContainer neo4jContainer = + new Neo4jContainer<>(DockerImageName.parse("neo4j:4.4")) + .withAdminPassword(null); // Disable password + + @Test + void testSomethingUsingBolt() { + + // Retrieve the Bolt URL from the container + String boltUrl = neo4jContainer.getBoltUrl(); + try ( + Driver driver = GraphDatabase.driver(boltUrl, AuthTokens.none()); + Session session = driver.session() + ) { + long one = session.run("RETURN 1", Collections.emptyMap()).next().get(0).asLong(); + assertThat(one).isEqualTo(1L); + } catch (Exception e) { + fail(e.getMessage()); + } + } + + @Test + void testSomethingUsingHttp() throws IOException { + + // Retrieve the HTTP URL from the container + String httpUrl = neo4jContainer.getHttpUrl(); + + URL url = new URL(httpUrl + "/db/data/transaction/commit"); + HttpURLConnection con = (HttpURLConnection) url.openConnection(); + + con.setRequestMethod("POST"); + con.setRequestProperty("Content-Type", "application/json"); + con.setDoOutput(true); + + try (Writer out = new OutputStreamWriter(con.getOutputStream())) { + out.write("{\"statements\":[{\"statement\":\"RETURN 1\"}]}"); + out.flush(); + } + + assertThat(con.getResponseCode()).isEqualTo(HttpURLConnection.HTTP_OK); + try (BufferedReader buffer = new BufferedReader(new InputStreamReader(con.getInputStream()))) { + String expectedResponse = + "{\"results\":[{\"columns\":[\"1\"],\"data\":[{\"row\":[1],\"meta\":[null]}]}],\"errors\":[]}"; + String response = buffer.lines().collect(Collectors.joining("\n")); + assertThat(response).isEqualTo(expectedResponse); + } + } +} +// } diff --git a/examples/settings.gradle b/examples/settings.gradle index f58ea83fb9b..75f739e9005 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -23,6 +23,7 @@ include 'disque-job-queue' include 'kafka-cluster' include 'linked-container' include 'mongodb-container' +include 'neo4j-container' include 'redis-backed-cache' include 'redis-backed-cache-testng' include 'selenium-container' diff --git a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerJUnitIntegrationTest.java b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerJUnitIntegrationTest.java index ad6ccad8281..6832dfff0c7 100644 --- a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerJUnitIntegrationTest.java +++ b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerJUnitIntegrationTest.java @@ -20,7 +20,7 @@ public class Neo4jContainerJUnitIntegrationTest { @ClassRule - public static Neo4jContainer neo4jContainer = new Neo4jContainer<>(Neo4jTestImages.NEO4J_TEST_IMAGE); + public static Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4"); @Test public void shouldStart() { diff --git a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java index fcfda6f6873..25924fdd6b4 100644 --- a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java +++ b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java @@ -9,6 +9,7 @@ import org.neo4j.driver.Record; import org.neo4j.driver.Result; import org.neo4j.driver.Session; +import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MountableFile; import java.util.Collections; @@ -31,7 +32,10 @@ public class Neo4jContainerTest { public void shouldDisableAuthentication() { try ( - Neo4jContainer neo4jContainer = new Neo4jContainer<>(Neo4jTestImages.NEO4J_TEST_IMAGE).withoutAuthentication() + // withoutAuthentication { + Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") + .withoutAuthentication() + // } ) { neo4jContainer.start(); try (Driver driver = getDriver(neo4jContainer); @@ -46,8 +50,10 @@ public void shouldDisableAuthentication() { @Test public void shouldCopyDatabase() { try ( + // copyDatabase { Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:3.5.30") .withDatabase(MountableFile.forClasspathResource("/test-graph.db")) + // } ) { neo4jContainer.start(); try ( @@ -88,8 +94,10 @@ public void shouldFailOnCopyDatabaseForCustomNonSemverNeo4j4Image() { @Test public void shouldCopyPlugins() { try ( - Neo4jContainer neo4jContainer = new Neo4jContainer<>(Neo4jTestImages.NEO4J_TEST_IMAGE) + // registerPluginsPath { + Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") .withPlugins(MountableFile.forClasspathResource("/custom-plugins")) + // } ) { neo4jContainer.start(); try ( @@ -104,8 +112,10 @@ public void shouldCopyPlugins() { @Test public void shouldCopyPlugin() { try ( - Neo4jContainer neo4jContainer = new Neo4jContainer<>(Neo4jTestImages.NEO4J_TEST_IMAGE) + // registerPluginsJar { + Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") .withPlugins(MountableFile.forClasspathResource("/custom-plugins/hello-world.jar")) + // } ) { neo4jContainer.start(); try ( @@ -131,7 +141,7 @@ public void shouldCheckEnterpriseLicense() { String expectedImageName = "neo4j:4.4-enterprise"; assertThatExceptionOfType(IllegalStateException.class) - .isThrownBy(() -> new Neo4jContainer<>(Neo4jTestImages.NEO4J_TEST_IMAGE).withEnterpriseEdition()) + .isThrownBy(() -> new Neo4jContainer<>("neo4j:4.4").withEnterpriseEdition()) .withMessageContaining("The image " + expectedImageName + " requires you to accept a license agreement."); } @@ -140,8 +150,10 @@ public void shouldRunEnterprise() { assumeThat(Neo4jContainerTest.class.getResource(ACCEPTANCE_FILE_LOCATION)).isNotNull(); try ( - Neo4jContainer neo4jContainer = new Neo4jContainer<>(Neo4jTestImages.NEO4J_TEST_IMAGE) + // enterpriseEdition { + Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") .withEnterpriseEdition() + // } .withAdminPassword("Picard123") ) { neo4jContainer.start(); @@ -159,10 +171,11 @@ public void shouldRunEnterprise() { @Test public void shouldAddConfigToEnvironment() { - - Neo4jContainer neo4jContainer = new Neo4jContainer<>(Neo4jTestImages.NEO4J_TEST_IMAGE) + // neo4jConfiguration { + Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") .withNeo4jConfig("dbms.security.procedures.unrestricted", "apoc.*,algo.*") .withNeo4jConfig("dbms.tx_log.rotation.size", "42M"); + // } assertThat(neo4jContainer.getEnvMap()) .containsEntry("NEO4J_dbms_security_procedures_unrestricted", "apoc.*,algo.*"); diff --git a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jTestImages.java b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jTestImages.java deleted file mode 100644 index aee6f29fc5f..00000000000 --- a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jTestImages.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.testcontainers.containers; - -import org.testcontainers.utility.DockerImageName; - -public interface Neo4jTestImages { - DockerImageName NEO4J_TEST_IMAGE = DockerImageName.parse("neo4j:4.4.1"); -} From cbccd499fbc9f882a8d4da0f7254796a1ea9d28f Mon Sep 17 00:00:00 2001 From: hellozepp Date: Tue, 1 Mar 2022 00:54:49 +0800 Subject: [PATCH 2410/5615] Add the using organization `Byzer` (#5125) --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index 998722af50f..fdbad94b851 100644 --- a/docs/index.md +++ b/docs/index.md @@ -198,6 +198,7 @@ A huge thank you to our sponsors: * [Elastic](https://www.elastic.co) - Integration testing of the Java APM agent * [Alkira](https://www.alkira.com/) - Testing of multiple micro-services using Kafka, PostgreSQL, Apache Zookeeper, Etcd and so on. * [Togglz](https://www.togglz.org/) - Feature Flags for the Java platform +* [Byzer](https://www.byzer.org/home) - Integration tests for Data and AI platforms are based on multiple versions of Byzer, Ray and Apache Spark. ## License From d21a29e7a4de2c0d82cf2ae4b6c9eb7d85533103 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Mar 2022 22:08:33 +0000 Subject: [PATCH 2411/5615] Bump aws-java-sdk-s3 from 1.12.150 to 1.12.169 in /modules/localstack Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.150 to 1.12.169. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.150...1.12.169) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 8dc6dc2671f..7a076881a96 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Localstack" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.150' - testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.137' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.169' + testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.169' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.150' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.139' testImplementation 'software.amazon.awssdk:s3:2.17.108' From 43f807dc8ba58c5cbab61880e24c0ce2fd3595a9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Mar 2022 22:08:50 +0000 Subject: [PATCH 2412/5615] Bump neo4j from 3.5.29 to 3.5.31 in /modules/neo4j Bumps [neo4j](https://github.com/neo4j/neo4j) from 3.5.29 to 3.5.31. - [Release notes](https://github.com/neo4j/neo4j/releases) - [Commits](https://github.com/neo4j/neo4j/commits) --- updated-dependencies: - dependency-name: org.neo4j:neo4j dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 1d5cffc1955..27bf38b9f23 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -29,7 +29,7 @@ task customNeo4jPluginJar(type: Jar) { test.dependsOn customNeo4jPluginJar dependencies { - customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.29" + customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.31" api project(":testcontainers") From 0343dacda34bccd074700810c4f7c53d0ab74c53 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Mar 2022 22:08:51 +0000 Subject: [PATCH 2413/5615] Bump aws-java-sdk-logs from 1.12.139 to 1.12.169 in /modules/localstack Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.12.139 to 1.12.169. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.139...1.12.169) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-logs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 8dc6dc2671f..fda140b12da 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,7 +6,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.150' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.137' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.150' - testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.139' + testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.169' testImplementation 'software.amazon.awssdk:s3:2.17.108' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 7c75b0a3040721b35b592a747c2097fa1abf977b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Mar 2022 22:09:32 +0000 Subject: [PATCH 2414/5615] Bump guava from 31.0.1-jre to 31.1-jre in /modules/jdbc-test Bumps [guava](https://github.com/google/guava) from 31.0.1-jre to 31.1-jre. - [Release notes](https://github.com/google/guava/releases) - [Commits](https://github.com/google/guava/commits) --- updated-dependencies: - dependency-name: com.google.guava:guava dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 98570722e6a..314b3ab6a2d 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -2,7 +2,7 @@ dependencies { api project(':jdbc') api project(':test-support') - api 'com.google.guava:guava:31.0.1-jre' + api 'com.google.guava:guava:31.1-jre' api 'org.apache.commons:commons-lang3:3.12.0' api 'com.zaxxer:HikariCP-java6:2.3.13' api 'commons-dbutils:commons-dbutils:1.7' From e17bc43e70dc3eaee7224eb1b4ace75c905194a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Mar 2022 22:09:45 +0000 Subject: [PATCH 2415/5615] Bump transport from 7.17.0 to 7.17.1 in /modules/elasticsearch Bumps [transport](https://github.com/elastic/elasticsearch) from 7.17.0 to 7.17.1. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Changelog](https://github.com/elastic/elasticsearch/blob/master/CHANGELOG.md) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.17.0...v7.17.1) --- updated-dependencies: - dependency-name: org.elasticsearch.client:transport dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index fca8ecfe9fb..d669f0fbd05 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,6 +3,6 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') testImplementation "org.elasticsearch.client:elasticsearch-rest-client:7.16.2" - testImplementation "org.elasticsearch.client:transport:7.17.0" + testImplementation "org.elasticsearch.client:transport:7.17.1" testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From d7cb81c9a97deb39cd6d7c100853cb7ccb358476 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Mar 2022 22:09:46 +0000 Subject: [PATCH 2416/5615] Bump org.springframework.boot from 2.6.3 to 2.6.4 in /examples Bumps [org.springframework.boot](https://github.com/spring-projects/spring-boot) from 2.6.3 to 2.6.4. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.6.3...v2.6.4) --- updated-dependencies: - dependency-name: org.springframework.boot dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot-kotlin-redis/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index d728c13e3c0..7677e82eefb 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.6.3' + id 'org.springframework.boot' version '2.6.4' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 5a9495d86bf..45cee0ec20b 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,5 +1,5 @@ plugins { - id("org.springframework.boot") version "2.6.3" + id("org.springframework.boot") version "2.6.4" id("org.jetbrains.kotlin.jvm") version "1.6.10" id("org.jetbrains.kotlin.plugin.spring") version "1.6.10" } diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index ace47d12ebe..e3b3376e28f 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.6.3' + id 'org.springframework.boot' version '2.6.4' } apply plugin: 'io.spring.dependency-management' From 99578c4b2a2e45b1b7efe4638eb1af4449cbb667 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Mar 2022 22:10:10 +0000 Subject: [PATCH 2417/5615] Bump aws-java-sdk-dynamodb in /modules/dynalite Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.12.150 to 1.12.169. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.150...1.12.169) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-dynamodb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/dynalite/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index ce09dde17e6..2fa5978772e 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Dynalite" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.150' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.169' testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.150' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From 6904d8e21b5a248420c2373d0f8efcefd6a7000d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Mar 2022 22:10:28 +0000 Subject: [PATCH 2418/5615] Bump actions/setup-java from 2.5.0 to 3 Bumps [actions/setup-java](https://github.com/actions/setup-java) from 2.5.0 to 3. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v2.5.0...v3) --- updated-dependencies: - dependency-name: actions/setup-java dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- .github/workflows/release.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index d7351605e6d..3fb2467a295 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -17,7 +17,7 @@ jobs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.5.0 + - uses: actions/setup-java@v3 with: java-version: '8.0.302' distribution: temurin @@ -45,7 +45,7 @@ jobs: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.5.0 + - uses: actions/setup-java@v3 with: java-version: '8.0.302' distribution: temurin diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0fd59f892f0..db7232fffa8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.5.0 + - uses: actions/setup-java@v3 with: java-version: '8.0.302' distribution: temurin @@ -51,7 +51,7 @@ jobs: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.5.0 + - uses: actions/setup-java@v3 with: java-version: '8.0.302' distribution: temurin diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d9026eb2257..f6df0dd46f4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.5.0 + - uses: actions/setup-java@v3 with: java-version: '8.0.302' distribution: temurin From 5d2806f3bc4a01660cc9f550a37a9213946e62f6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Mar 2022 22:18:26 +0000 Subject: [PATCH 2419/5615] Bump google-cloud-spanner from 6.17.4 to 6.20.0 in /modules/gcloud Bumps [google-cloud-spanner](https://github.com/googleapis/java-spanner) from 6.17.4 to 6.20.0. - [Release notes](https://github.com/googleapis/java-spanner/releases) - [Changelog](https://github.com/googleapis/java-spanner/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-spanner/compare/v6.17.4...v6.20.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-spanner dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 72e304029ce..0e5ebee97dd 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -6,7 +6,7 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-datastore:2.2.2' testImplementation 'com.google.cloud:google-cloud-firestore:3.0.11' testImplementation 'com.google.cloud:google-cloud-pubsub:1.115.1' - testImplementation 'com.google.cloud:google-cloud-spanner:6.17.4' + testImplementation 'com.google.cloud:google-cloud-spanner:6.20.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.5.2' testImplementation 'org.assertj:assertj-core:3.22.0' } From 03b5a75d391600f716c5a514d58eb79c985baa42 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Mar 2022 22:18:30 +0000 Subject: [PATCH 2420/5615] Bump google-cloud-firestore from 3.0.11 to 3.0.14 in /modules/gcloud Bumps [google-cloud-firestore](https://github.com/googleapis/java-firestore) from 3.0.11 to 3.0.14. - [Release notes](https://github.com/googleapis/java-firestore/releases) - [Changelog](https://github.com/googleapis/java-firestore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-firestore/compare/v3.0.11...v3.0.14) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-firestore dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 72e304029ce..7d17a689ecf 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':testcontainers') testImplementation 'com.google.cloud:google-cloud-datastore:2.2.2' - testImplementation 'com.google.cloud:google-cloud-firestore:3.0.11' + testImplementation 'com.google.cloud:google-cloud-firestore:3.0.14' testImplementation 'com.google.cloud:google-cloud-pubsub:1.115.1' testImplementation 'com.google.cloud:google-cloud-spanner:6.17.4' testImplementation 'com.google.cloud:google-cloud-bigtable:2.5.2' From 30e8635e7e5b8160e66f014d4055a20b628e8499 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 3 Mar 2022 06:55:50 -0500 Subject: [PATCH 2421/5615] Add the session header to every Docker request (#5145) To improve tracing and other operations, we add current session's id to every Docker request that we are making. --- .../DockerClientProviderStrategy.java | 7 ++++- .../HeadersAddingDockerHttpClient.java | 27 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/org/testcontainers/dockerclient/HeadersAddingDockerHttpClient.java diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index 89383698adf..c65887b6af6 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -20,10 +20,12 @@ import org.rnorth.ducttape.ratelimits.RateLimiter; import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; import org.rnorth.ducttape.unreliables.Unreliables; +import org.testcontainers.DockerClientFactory; import org.testcontainers.utility.TestcontainersConfiguration; import java.net.URI; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; @@ -282,7 +284,10 @@ public static DockerClient getClientForConfig(TransportConfig transportConfig) { .withDockerHost(transportConfig.getDockerHost().toString()) .build() ), - dockerHttpClient + new HeadersAddingDockerHttpClient( + dockerHttpClient, + Collections.singletonMap("x-tc-sid", DockerClientFactory.SESSION_ID) + ) ); } diff --git a/core/src/main/java/org/testcontainers/dockerclient/HeadersAddingDockerHttpClient.java b/core/src/main/java/org/testcontainers/dockerclient/HeadersAddingDockerHttpClient.java new file mode 100644 index 00000000000..59a51e19a80 --- /dev/null +++ b/core/src/main/java/org/testcontainers/dockerclient/HeadersAddingDockerHttpClient.java @@ -0,0 +1,27 @@ +package org.testcontainers.dockerclient; + +import com.github.dockerjava.transport.DockerHttpClient; +import lombok.RequiredArgsConstructor; +import lombok.ToString; +import lombok.experimental.Delegate; + +import java.io.Closeable; +import java.util.Map; + +@RequiredArgsConstructor +@ToString +class HeadersAddingDockerHttpClient implements DockerHttpClient { + + @Delegate(types = Closeable.class) + final DockerHttpClient delegate; + + final Map headers; + + @Override + public Response execute(Request request) { + request = Request.builder().from(request) + .putAllHeaders(headers) + .build(); + return delegate.execute(request); + } +} From 71a0679a675f5474f8f6c869827de6402d1819b9 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 3 Mar 2022 14:45:15 -0500 Subject: [PATCH 2422/5615] Run tests using shaded dependencies (#5146) * Test shaded JARs * Find shaded projects recursively * Add missing space * fix core tests * fix HiveMQ module * Simplify the HiveMQ workaround * Add a comment that explains why we pin Jackson version in k3s --- ...temPropertyClientProviderStrategyTest.java | 26 +++++++++---------- gradle/shading.gradle | 12 +++++++++ .../hivemq/HiveMQExtension.java | 16 +++++++++--- modules/k3s/build.gradle | 5 +++- 4 files changed, 42 insertions(+), 17 deletions(-) diff --git a/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java b/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java index c8eec1d38d8..a6bd68be745 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java @@ -1,8 +1,8 @@ package org.testcontainers.dockerclient; import com.github.dockerjava.core.DefaultDockerClientConfig; -import com.github.dockerjava.core.LocalDirectorySSLConfig; import com.github.dockerjava.transport.SSLConfig; +import org.assertj.core.api.Assertions; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -16,15 +16,12 @@ import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import java.util.Properties; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; @@ -93,21 +90,24 @@ public void testWhenDockerHostAndSSLConfigPresent() throws IOException { assertEquals("tcp://1.2.3.4:2375", transportConfig.getDockerHost().toString()); SSLConfig sslConfig = transportConfig.getSslConfig(); - assertNotNull(sslConfig); - assertTrue(sslConfig instanceof LocalDirectorySSLConfig); - assertEquals(tempDirPath, ((LocalDirectorySSLConfig) sslConfig).getDockerCertPath()); + Assertions.assertThat(sslConfig) + .extracting("dockerCertPath") + .isEqualTo(tempDirPath); } @Test public void applicableWhenIgnoringUserPropertiesAndConfigured() { Mockito.doReturn("autoIgnoringUserProperties").when(TestcontainersConfiguration.getInstance()).getEnvVarOrProperty(eq("dockerconfig.source"), anyString()); - DefaultDockerClientConfig.Builder configBuilder = DefaultDockerClientConfig.createDefaultConfigBuilder() - .withDockerHost("tcp://1.2.3.4:2375"); + Properties oldProperties = System.getProperties(); + try { + System.setProperty("DOCKER_HOST", "tcp://1.2.3.4:2375"); + EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(); - EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(configBuilder); - - assertTrue(strategy.isApplicable()); + assertTrue(strategy.isApplicable()); + } finally { + System.setProperties(oldProperties); + } } @Test diff --git a/gradle/shading.gradle b/gradle/shading.gradle index 909989caf84..fc7f6587b3e 100644 --- a/gradle/shading.gradle +++ b/gradle/shading.gradle @@ -55,4 +55,16 @@ project.afterEvaluate { tasks.shadowJar.relocate(pkg, "org.testcontainers.shaded.${pkg}") } + + // Add shaded JARs first + tasks.test.classpath = findShadedProjects(project) + .plus(project) + .sum { it.tasks.findByName("shadowJar").outputs.files } + .plus(sourceSets.test.runtimeClasspath) +} + +static Set findShadedProjects(Project project) { + Set dependencies = project.configurations.api.dependencies.withType(ProjectDependency)*.dependencyProject + + return dependencies + dependencies.collectMany { it -> findShadedProjects(it) } } diff --git a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQExtension.java b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQExtension.java index a975439f4b3..934dfd0c29c 100644 --- a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQExtension.java +++ b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQExtension.java @@ -4,9 +4,12 @@ import javassist.NotFoundException; import lombok.Getter; import org.apache.commons.io.FileUtils; +import org.jboss.shrinkwrap.api.ExtensionLoader; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.exporter.ZipExporter; import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.jboss.shrinkwrap.impl.base.exporter.zip.ZipExporterImpl; +import org.jboss.shrinkwrap.impl.base.spec.JavaArchiveImpl; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @@ -101,9 +104,16 @@ private HiveMQExtension( } } - final JavaArchive javaArchive = - ShrinkWrap.create(JavaArchive.class) - .addAsServiceProvider(EXTENSION_MAIN_CLASS_NAME, hiveMQExtension.getMainClass().getName()); + // Shadow Gradle plugin doesn't know how to handle ShrinkWrap's SPI definitions + // This workaround creates the mappings programmatically + // TODO write a custom Gradle Shadow transformer? + ExtensionLoader extensionLoader = ShrinkWrap.getDefaultDomain().getConfiguration().getExtensionLoader(); + extensionLoader.addOverride(JavaArchive.class, JavaArchiveImpl.class); + extensionLoader.addOverride(ZipExporter.class, ZipExporterImpl.class); + + final JavaArchive javaArchive = ShrinkWrap + .create(JavaArchive.class) + .addAsServiceProvider(EXTENSION_MAIN_CLASS_NAME, hiveMQExtension.getMainClass().getName()); putSubclassesIntoJar(hiveMQExtension.getId(), hiveMQExtension.getMainClass(), javaArchive); for (final Class additionalClass : hiveMQExtension.getAdditionalClasses()) { diff --git a/modules/k3s/build.gradle b/modules/k3s/build.gradle index e36dc67bd9c..5007c832e7f 100644 --- a/modules/k3s/build.gradle +++ b/modules/k3s/build.gradle @@ -3,7 +3,10 @@ description = "Testcontainers :: K3S" dependencies { api project(":testcontainers") - shaded 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.1' + // https://youtu.be/otCpCn0l4Wo + // The core module depends on jackson-databind 2.8.x for backward compatibility. + // Any >2.8 version here is not compatible with jackson-databind 2.8.x. + shaded 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.8.8' testImplementation 'io.fabric8:kubernetes-client:5.11.0' testImplementation 'io.kubernetes:client-java:14.0.0' From 947b37e296e883a6eaf62423eb6981aa5c7cfa6a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Mar 2022 19:46:16 +0000 Subject: [PATCH 2423/5615] Bump kubernetes-client from 5.11.0 to 5.12.1 in /modules/k3s Bumps [kubernetes-client](https://github.com/fabric8io/kubernetes-client) from 5.11.0 to 5.12.1. - [Release notes](https://github.com/fabric8io/kubernetes-client/releases) - [Changelog](https://github.com/fabric8io/kubernetes-client/blob/master/CHANGELOG.md) - [Commits](https://github.com/fabric8io/kubernetes-client/compare/v5.11.0...v5.12.1) --- updated-dependencies: - dependency-name: io.fabric8:kubernetes-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/k3s/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/k3s/build.gradle b/modules/k3s/build.gradle index 5007c832e7f..075078f36d9 100644 --- a/modules/k3s/build.gradle +++ b/modules/k3s/build.gradle @@ -8,7 +8,7 @@ dependencies { // Any >2.8 version here is not compatible with jackson-databind 2.8.x. shaded 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.8.8' - testImplementation 'io.fabric8:kubernetes-client:5.11.0' + testImplementation 'io.fabric8:kubernetes-client:5.12.1' testImplementation 'io.kubernetes:client-java:14.0.0' testImplementation 'org.assertj:assertj-core:3.22.0' } From 6dea9789db1f970da20fc04d11eb3c5323a8bd4f Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 4 Mar 2022 10:51:32 +0000 Subject: [PATCH 2424/5615] Fix formatting in docs for image override configuration (#5147) --- docs/features/image_name_substitution.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/features/image_name_substitution.md b/docs/features/image_name_substitution.md index 7ead811a250..f3c78c1f309 100644 --- a/docs/features/image_name_substitution.md +++ b/docs/features/image_name_substitution.md @@ -112,9 +112,10 @@ To do this, create or modify a file on the classpath named `testcontainers.prope For example: -```text tab="src/test/resources/testcontainers.properties" -image.substitutor=com.mycompany.testcontainers.ExampleImageNameSubstitutor -``` +=== "`src/test/resources/testcontainers.properties`" + ```text + image.substitutor=com.mycompany.testcontainers.ExampleImageNameSubstitutor + ``` Note that it is also possible to provide this same configuration property: @@ -130,7 +131,7 @@ Please see [the documentation on configuration mechanisms](./configuration.md) f !!! note This approach is discouraged and deprecated, but is documented for completeness. Please consider one of the other approaches outlined in this page instead. - Overriding individual image names via configuration may be removed in 2021. + Overriding individual image names via configuration may be removed in the future. Consider this if: From e12b63692d9f3922b53705041d81a9f529bc479f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Mar 2022 13:59:20 +0000 Subject: [PATCH 2425/5615] Bump actions/checkout from 2 to 3 Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci-docker-wormhole.yml | 2 +- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci-rootless.yml | 2 +- .github/workflows/ci.yml | 6 +++--- .github/workflows/release.yml | 2 +- .github/workflows/update-docs-version.yml | 2 +- .github/workflows/update-gradle-wrapper.yml | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci-docker-wormhole.yml b/.github/workflows/ci-docker-wormhole.yml index d750d8b39e0..b93d26d98bb 100644 --- a/.github/workflows/ci-docker-wormhole.yml +++ b/.github/workflows/ci-docker-wormhole.yml @@ -8,7 +8,7 @@ jobs: in-docker_test: runs-on: ubuntu-18.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Build with Gradle run: | docker run -i --rm \ diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index 3fb2467a295..a594f936a84 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -16,7 +16,7 @@ jobs: outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: actions/setup-java@v3 with: java-version: '8.0.302' @@ -44,7 +44,7 @@ jobs: matrix: ${{ fromJson(needs.find_gradle_jobs.outputs.matrix) }} runs-on: ubuntu-18.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: actions/setup-java@v3 with: java-version: '8.0.302' diff --git a/.github/workflows/ci-rootless.yml b/.github/workflows/ci-rootless.yml index 71ded85cf53..45c58c5e2d0 100644 --- a/.github/workflows/ci-rootless.yml +++ b/.github/workflows/ci-rootless.yml @@ -16,7 +16,7 @@ jobs: env: XDG_RUNTIME_DIR: ${{ matrix.XDG_RUNTIME_DIR }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: debug run: id -u; whoami - name: uninstall rootful Docker diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index db7232fffa8..d0f57d88667 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: outputs: thundra_agent_testrun_id: ${{ steps.thundra_test_initializer.outputs.thundra_agent_testrun_id }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - id: thundra_test_initializer uses: thundra-io/thundra-test-init-action@v1 find_gradle_jobs: @@ -24,7 +24,7 @@ jobs: outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: actions/setup-java@v3 with: java-version: '8.0.302' @@ -50,7 +50,7 @@ jobs: matrix: ${{ fromJson(needs.find_gradle_jobs.outputs.matrix) }} runs-on: ubuntu-18.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: actions/setup-java@v3 with: java-version: '8.0.302' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f6df0dd46f4..5c786d440c5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ jobs: release: runs-on: ubuntu-18.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: actions/setup-java@v3 with: java-version: '8.0.302' diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index 2c7626c7ec7..ef039182317 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -9,7 +9,7 @@ jobs: if: github.repository == 'testcontainers/testcontainers-java' runs-on: ubuntu-18.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: ref: master - name: Update latest_version property in mkdocs.yml diff --git a/.github/workflows/update-gradle-wrapper.yml b/.github/workflows/update-gradle-wrapper.yml index 253e37c23eb..e2013d0f1b7 100644 --- a/.github/workflows/update-gradle-wrapper.yml +++ b/.github/workflows/update-gradle-wrapper.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Update Gradle Wrapper uses: gradle-update/update-gradle-wrapper-action@5b865a8b4d09fdf6afaaa20397ba68455bb48f0d # v1.0.13 From 5f7ded3ac202fd379f5b92bbf196b2c4e30cc6b4 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 8 Mar 2022 09:08:19 -0500 Subject: [PATCH 2426/5615] Deprecate `getTestHostIpAddress` and `getContainerIpAddress()` (#5149) * Deprecate `getTestHostIpAddress` * Update docs/modules/webdriver_containers.md Co-authored-by: Richard North Co-authored-by: Richard North --- .../testcontainers/containers/Container.java | 2 ++ .../containers/ContainerState.java | 1 + .../containers/GenericContainer.java | 1 + docs/modules/webdriver_containers.md | 3 +-- .../LocalServerWebDriverContainerTest.java | 20 ++++++------------- 5 files changed, 11 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/Container.java b/core/src/main/java/org/testcontainers/containers/Container.java index 9c1cb7f8034..d9291fc9fc8 100644 --- a/core/src/main/java/org/testcontainers/containers/Container.java +++ b/core/src/main/java/org/testcontainers/containers/Container.java @@ -365,7 +365,9 @@ default SELF withClasspathResourceMapping(final String resourcePath, final Strin * from inside the container is not going to work, since the container has its own IP address. * * @return the IP address of the host machine + * @deprecated use {@link org.testcontainers.Testcontainers#exposeHostPorts(int...)} */ + @Deprecated String getTestHostIpAddress(); /** diff --git a/core/src/main/java/org/testcontainers/containers/ContainerState.java b/core/src/main/java/org/testcontainers/containers/ContainerState.java index 7fe90bd57a4..e5903b0b806 100644 --- a/core/src/main/java/org/testcontainers/containers/ContainerState.java +++ b/core/src/main/java/org/testcontainers/containers/ContainerState.java @@ -44,6 +44,7 @@ public interface ContainerState { * Get the IP address that this container may be reached on (may not be the local machine). * * @return an IP address + * @deprecated use {@link #getHost()} * @see #getHost() */ default String getContainerIpAddress() { diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 22a4f0ba739..70e080711a7 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -1332,6 +1332,7 @@ public String getDockerImageName() { * {@inheritDoc} */ @Override + @Deprecated public String getTestHostIpAddress() { if (DockerMachineClient.instance().isInstalled()) { try { diff --git a/docs/modules/webdriver_containers.md b/docs/modules/webdriver_containers.md index d589aa18098..ec1dc2a407f 100644 --- a/docs/modules/webdriver_containers.md +++ b/docs/modules/webdriver_containers.md @@ -36,8 +36,7 @@ test methods: You can then use this driver instance like a regular WebDriver. Note that, if you want to test a **web application running on the host machine** (the machine the JUnit tests are -running on - which is quite likely), you'll need to replace any references to `localhost` with an IP address that the -Docker container can reach. Use the `getTestHostIpAddress()` method, e.g.: +running on - which is quite likely), you'll need to use [the host exposing](../features/networking.md#exposing-host-ports-to-the-container) feature of Testcontainers, e.g.: [Open Web Page](../../modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java) inside_block:getPage diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java index 772193e2d43..3c071984dfb 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java @@ -7,10 +7,9 @@ import org.openqa.selenium.By; import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.remote.RemoteWebDriver; +import org.testcontainers.Testcontainers; import org.testcontainers.containers.BrowserWebDriverContainer; -import org.testcontainers.utility.TestEnvironment; -import static org.apache.commons.lang3.SystemUtils.IS_OS_MAC_OSX; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; /** @@ -20,18 +19,11 @@ public class LocalServerWebDriverContainerTest { @Rule - public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer().withCapabilities(new ChromeOptions()); + public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() + .withAccessToHost(true) + .withCapabilities(new ChromeOptions()); private int localPort; - /** - * The getTestHostIpAddress() method is only implemented for OS X running docker-machine. Skip JUnit execution elsewhere. - */ - @BeforeClass - public static void checkOS() { - Assume.assumeTrue("These tests are currently only applicable to OS X", IS_OS_MAC_OSX); - Assume.assumeTrue("These tests are only applicable to docker machine", TestEnvironment.dockerIsDockerMachine()); - } - @Before public void setupLocalServer() throws Exception { @@ -55,8 +47,8 @@ public void testConnection() { // Construct a URL that the browser container can access // getPage { - String hostIpAddress = chrome.getTestHostIpAddress(); - driver.get("http://" + hostIpAddress + ":" + localPort); + Testcontainers.exposeHostPorts(localPort); + driver.get("http://host.testcontainers.internal:" + localPort); // } String headingText = driver.findElement(By.cssSelector("h1")).getText().trim(); From affaf194f08bb1409e23996f03ad05cb3eabe943 Mon Sep 17 00:00:00 2001 From: Yannick Weber Date: Tue, 8 Mar 2022 16:24:15 +0100 Subject: [PATCH 2427/5615] replaced non-ascii single quote with ascii single quote (#5102) --- .../main/java/org/testcontainers/hivemq/HiveMQContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java index 342903e24af..996caad1eb8 100644 --- a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java +++ b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java @@ -354,7 +354,7 @@ protected void containerIsStarted(final @NotNull InspectContainerResponse contai } if (!file.exists()) { - throw new ContainerLaunchException("File '" + mountableFile.getFilesystemPath() + "‘ does not exist."); + throw new ContainerLaunchException("File '" + mountableFile.getFilesystemPath() + "' does not exist."); } final String containerPath = "/opt/hivemq" + PathUtil.prepareAppendPath(pathInHomeFolder); withCopyFileToContainer(cloneWithFileMode(mountableFile), containerPath); From 40341d3912a2fe623adb83651c52a490734f3ab9 Mon Sep 17 00:00:00 2001 From: Gerrit Meier Date: Fri, 11 Mar 2022 14:16:15 +0100 Subject: [PATCH 2428/5615] Mention that Spring Data Neo4j uses Testcontainers in the docs (#5169) * Fix Neo4j-OGM usage description. * Add Spring Data Neo4j to the "who is using"-part. --- docs/index.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index fdbad94b851..8749e3678cf 100644 --- a/docs/index.md +++ b/docs/index.md @@ -176,7 +176,8 @@ A huge thank you to our sponsors: * [Instana](https://www.instana.com) - Testing agents and stream processing backends * [eBay Marketing](https://www.ebay.com) - Testing for MySQL, Cassandra, Redis, Couchbase, Kafka, etc. * [Skyscanner](https://www.skyscanner.net/) - Integration testing against HTTP service mocks and various data stores -* [Neo4j-OGM](https://neo4j.com/developer/neo4j-ogm/) - Testing new, reactive client implementations +* [Neo4j-OGM](https://neo4j.com/developer/neo4j-ogm/) - Testing with Neo4j +* [Spring Data Neo4j](https://github.com/spring-projects/spring-data-neo4j/) - Testing imperative and reactive implementations with Neo4j * [Lightbend](https://www.lightbend.com/) - Testing [Alpakka Kafka](https://doc.akka.io/docs/alpakka-kafka/current/) and support in [Alpakka Kafka Testkit](https://doc.akka.io/docs/alpakka-kafka/current/testing.html#testing-with-kafka-in-docker) * [Zalando SE](https://corporate.zalando.com/en) - Testing core business services * [Europace AG](https://tech.europace.de/) - Integration testing for databases and micro services From eea61bd9a794842aae7865e505be72a938e2a7e0 Mon Sep 17 00:00:00 2001 From: Joseph Petersen Date: Thu, 17 Mar 2022 12:48:25 +0100 Subject: [PATCH 2429/5615] Use collapse-after for dependencies category (#5157) Bump release-drafter.yml --- .github/release-drafter.yml | 1 + .github/workflows/release-drafter.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 4da341055c2..07376824bf7 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -23,3 +23,4 @@ categories: - type/test-improvement - title: 📦 Dependency updates label: dependencies + collapse-after: 5 diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 05485ac57fa..839fb43df4d 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -12,6 +12,6 @@ jobs: if: github.repository == 'testcontainers/testcontainers-java' runs-on: ubuntu-latest steps: - - uses: release-drafter/release-drafter@2f7ebf8ab5ef7f9835ee4b0b1eebaa2a14ca1669 # v5.15.0 + - uses: release-drafter/release-drafter@e9ee02fbac03d922bac6212003695e8358dd88b0 # v5.19.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From e450586648f613abae00e29cb8d290a987e43a25 Mon Sep 17 00:00:00 2001 From: Gerrit Meier Date: Fri, 25 Mar 2022 13:28:48 +0100 Subject: [PATCH 2430/5615] Enable Neo4j labs plugin definition. (#5035) --- docs/modules/databases/neo4j.md | 17 +++++- .../containers/Neo4jContainer.java | 41 +++++++++++++ .../containers/Neo4jLabsPlugin.java | 21 +++++++ .../containers/Neo4jContainerTest.java | 58 ++++++++++++++++++- 4 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jLabsPlugin.java diff --git a/docs/modules/databases/neo4j.md b/docs/modules/databases/neo4j.md index 87a5d9157d1..b6a04d2195c 100644 --- a/docs/modules/databases/neo4j.md +++ b/docs/modules/databases/neo4j.md @@ -52,6 +52,21 @@ Whole directories work as well: [Plugin folder](../../../modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java) inside_block:registerPluginsPath +### Add Neo4j Docker Labs plugins + +Add any Neo4j Labs plugin from the [Neo4j Docker Labs plugin list](https://neo4j.com/docs/operations-manual/4.4/docker/operations/#docker-neo4jlabs-plugins). + +!!! note + At the moment only the plugins available from the list Neo4j Docker 4.4 are supported by type. + If you want to register another supported Neo4j Labs plugin, you have to add it manually + by using the method `withLabsPlugins(String... neo4jLabsPlugins)`. + Please refer to the list of [supported Docker image plugins](https://neo4j.com/docs/operations-manual/current/docker/operations/#docker-neo4jlabs-plugins). + + +[Configure Neo4j Labs Plugins](../../../modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java) inside_block:configureLabsPlugins + + + ### Start the container with a predefined database If you have an existing database (`graph.db`) you want to work with, copy it over to the container like this: @@ -61,7 +76,7 @@ If you have an existing database (`graph.db`) you want to work with, copy it ove !!! note -The `withDatabase` method will only work with Neo4j 3.5 and throw an exception if used in combination with a newer version. + The `withDatabase` method will only work with Neo4j 3.5 and throw an exception if used in combination with a newer version. ## Choose your Neo4j license diff --git a/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java index f844df3f945..6f0cf10ded7 100644 --- a/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java +++ b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java @@ -4,7 +4,11 @@ import static java.util.stream.Collectors.toSet; import java.time.Duration; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import java.util.stream.Stream; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; @@ -60,6 +64,8 @@ public class Neo4jContainer> extends GenericContaine private String adminPassword = DEFAULT_ADMIN_PASSWORD; + private final Set labsPlugins = new HashSet<>(); + /** * Creates a Neo4jContainer using the official Neo4j docker image. * @deprecated use {@link Neo4jContainer(DockerImageName)} instead @@ -119,6 +125,15 @@ protected void configure() { boolean emptyAdminPassword = this.adminPassword == null || this.adminPassword.isEmpty(); String neo4jAuth = emptyAdminPassword ? "none" : String.format(AUTH_FORMAT, this.adminPassword); addEnv("NEO4J_AUTH", neo4jAuth); + + if (!this.labsPlugins.isEmpty()) { + String enabledPlugins = this.labsPlugins.stream() + .map(pluginName -> "\"" + pluginName + "\"") + .collect(Collectors.joining(",")); + + addEnv("NEO4JLABS_PLUGINS", "[" + enabledPlugins + "]"); + } + } /** @@ -251,6 +266,32 @@ public String getAdminPassword() { return adminPassword; } + /** + * Registers one or more {@link Neo4jLabsPlugin} for download and server startup. + + * @param neo4jLabsPlugins The Neo4j plugins that should get started with the server. + * @return This container. + */ + public S withLabsPlugins(Neo4jLabsPlugin... neo4jLabsPlugins) { + List pluginNames = Arrays.stream(neo4jLabsPlugins) + .map(plugin -> plugin.pluginName) + .collect(Collectors.toList()); + + this.labsPlugins.addAll(pluginNames); + return self(); + } + + /** + * Registers one or more {@link Neo4jLabsPlugin} for download and server startup. + + * @param neo4jLabsPlugins The Neo4j plugins that should get started with the server. + * @return This container. + */ + public S withLabsPlugins(String... neo4jLabsPlugins) { + this.labsPlugins.addAll(Arrays.asList(neo4jLabsPlugins)); + return self(); + } + private static String formatConfigurationKey(String plainConfigKey) { final String prefix = "NEO4J_"; diff --git a/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jLabsPlugin.java b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jLabsPlugin.java new file mode 100644 index 00000000000..96c8e987c85 --- /dev/null +++ b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jLabsPlugin.java @@ -0,0 +1,21 @@ +package org.testcontainers.containers; + +/** + * Reflects a plugin from the official Neo4j 4.4. + * Neo4j Labs Plugin list. + * There might be plugins not supported by your selected version of Neo4j. + */ +public enum Neo4jLabsPlugin { + APOC("apoc"), + APOC_CORE("apoc-core"), + BLOOM("bloom"), + STREAMS("streams"), + GRAPH_DATA_SCIENCE("graph-data-science"), + NEO_SEMANTICS("n10s"); + + final String pluginName; + + Neo4jLabsPlugin(String pluginName) { + this.pluginName = pluginName; + } +} diff --git a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java index 25924fdd6b4..0d3f04ef570 100644 --- a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java +++ b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java @@ -9,7 +9,6 @@ import org.neo4j.driver.Record; import org.neo4j.driver.Result; import org.neo4j.driver.Session; -import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MountableFile; import java.util.Collections; @@ -183,6 +182,63 @@ public void shouldAddConfigToEnvironment() { .containsEntry("NEO4J_dbms_tx__log_rotation_size", "42M"); } + @Test + public void shouldConfigureSingleLabsPlugin() { + try (Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") + .withLabsPlugins(Neo4jLabsPlugin.APOC)) { + + + // needs to get called explicitly for setup + neo4jContainer.configure(); + + assertThat(neo4jContainer.getEnvMap()) + .containsEntry("NEO4JLABS_PLUGINS", "[\"apoc\"]"); + } + } + + @Test + public void shouldConfigureMultipleLabsPlugins() { + try( + // configureLabsPlugins { + Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") + .withLabsPlugins(Neo4jLabsPlugin.APOC, Neo4jLabsPlugin.BLOOM); + // } + ) { + // needs to get called explicitly for setup + neo4jContainer.configure(); + + assertThat(neo4jContainer.getEnvMap().get("NEO4JLABS_PLUGINS")) + .containsAnyOf("[\"apoc\",\"bloom\"]", "[\"bloom\",\"apoc\"]"); + } + } + + @Test + public void shouldConfigureSingleLabsPluginWithString() { + try (Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") + .withLabsPlugins("myApoc")) { + + // needs to get called explicitly for setup + neo4jContainer.configure(); + + assertThat(neo4jContainer.getEnvMap()) + .containsEntry("NEO4JLABS_PLUGINS", "[\"myApoc\"]"); + } + } + + @Test + public void shouldConfigureMultipleLabsPluginsWithString() { + + try (Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") + .withLabsPlugins("myApoc", "myBloom")) { + + // needs to get called explicitly for setup + neo4jContainer.configure(); + + assertThat(neo4jContainer.getEnvMap().get("NEO4JLABS_PLUGINS")) + .containsAnyOf("[\"myApoc\",\"myBloom\"]", "[\"myBloom\",\"myApoc\"]"); + } + } + private static Driver getDriver(Neo4jContainer container) { AuthToken authToken = AuthTokens.none(); From 0545c0e1c61ba87d7bf7350ab4f9d719c46150d4 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 28 Mar 2022 11:01:43 -0400 Subject: [PATCH 2431/5615] Remove OkHttp transport (#5113) * Remove OkHttp transport * Fix OkHttp dependencies --- core/build.gradle | 1 - .../dockerclient/DockerClientProviderStrategy.java | 7 ------- modules/azure/build.gradle | 2 ++ modules/couchbase/build.gradle | 2 ++ modules/solr/build.gradle | 3 +++ 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index 89028d0f9c3..558584a0d7c 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -79,7 +79,6 @@ dependencies { api "com.github.docker-java:docker-java-api" shaded 'com.github.docker-java:docker-java-core' - shaded 'com.github.docker-java:docker-java-transport-okhttp' api 'com.github.docker-java:docker-java-transport-zerodep' diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index c65887b6af6..2ae066d897d 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -6,7 +6,6 @@ import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.core.DockerClientImpl; import com.github.dockerjava.core.RemoteApiVersion; -import com.github.dockerjava.okhttp.OkDockerHttpClient; import com.github.dockerjava.transport.DockerHttpClient; import com.github.dockerjava.zerodep.ZerodepDockerHttpClient; import com.google.common.annotations.VisibleForTesting; @@ -257,12 +256,6 @@ public static DockerClient getClientForConfig(TransportConfig transportConfig) { String transportType = TestcontainersConfiguration.getInstance().getTransportType(); switch (transportType) { - case "okhttp": - dockerHttpClient = new OkDockerHttpClient.Builder() - .dockerHost(transportConfig.getDockerHost()) - .sslConfig(transportConfig.getSslConfig()) - .build(); - break; case "httpclient5": dockerHttpClient = new ZerodepDockerHttpClient.Builder() .dockerHost(transportConfig.getDockerHost()) diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle index 7e5bc336526..b4dab636eb9 100644 --- a/modules/azure/build.gradle +++ b/modules/azure/build.gradle @@ -2,6 +2,8 @@ description = "Testcontainers :: Azure" dependencies { api project(':testcontainers') + // TODO use JDK's HTTP client and/or Apache HttpClient5 + shaded 'com.squareup.okhttp3:okhttp:3.14.9' testImplementation 'org.assertj:assertj-core:3.22.0' testImplementation 'com.azure:azure-cosmos:4.26.0' diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 2c68f163a57..0245a5935cb 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -2,6 +2,8 @@ description = "Testcontainers :: Couchbase" dependencies { api project(':testcontainers') + // TODO use JDK's HTTP client and/or Apache HttpClient5 + shaded 'com.squareup.okhttp3:okhttp:3.14.9' testImplementation 'com.couchbase.client:java-client:3.2.5' testImplementation 'org.awaitility:awaitility:4.1.1' diff --git a/modules/solr/build.gradle b/modules/solr/build.gradle index b3dfc9b3f92..49b2a9970f3 100644 --- a/modules/solr/build.gradle +++ b/modules/solr/build.gradle @@ -2,6 +2,9 @@ description = "Testcontainers :: Solr" dependencies { api project(':testcontainers') + // TODO use JDK's HTTP client and/or Apache HttpClient5 + shaded 'com.squareup.okhttp3:okhttp:3.14.9' + testImplementation 'org.apache.solr:solr-solrj:8.11.1' } From 6414df1288b0ec9c1e707aeb34c200a191cca74e Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Mon, 28 Mar 2022 17:12:09 +0200 Subject: [PATCH 2432/5615] Update mkdocs to 1.3.0 (#5186) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8f4a1a5f100..f77d057b599 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -mkdocs==1.2.3 +mkdocs==1.3.0 mkdocs-codeinclude-plugin==0.2.0 mkdocs-material==8.1.3 mkdocs-markdownextradata-plugin==0.2.5 From 02782d9cb711baebaa504bc00669acdd84e10e51 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 28 Mar 2022 11:13:47 -0400 Subject: [PATCH 2433/5615] Replace deprecated `checkAndPullImage` with `RemoteDockerImage` (#5148) * Replace deprecated `checkAndPullImage` with `RemoteDockerImage` * fix `runCommandInsideDockerShouldNotFailIfImageDoesNotExistsLocally` * Remove `LoggerFactory` usage * avoid name substitution in `DockerComposeContainer`/`ImageFromDockerfile` * fix `AuthenticatedImagePullTest` * remove accidential `@Ignore` * fix indentation * put `clearCache` after `catch()` --- .../testcontainers/DockerClientFactory.java | 22 +++++++++---------- .../containers/DockerComposeContainer.java | 6 ++++- .../images/LocalImagesCache.java | 3 ++- .../images/RemoteDockerImage.java | 9 +++++--- .../images/builder/ImageFromDockerfile.java | 7 +++++- .../utility/ImageNameSubstitutor.java | 17 ++++++++++++++ .../utility/RyukResourceReaper.java | 8 +++---- .../DockerClientFactoryTest.java | 10 +++++++-- .../images/LocalImagesCacheAccessor.java | 9 ++++++++ .../utility/AuthenticatedImagePullTest.java | 2 ++ .../containers/localstack/LegacyModeTest.java | 9 ++------ 11 files changed, 70 insertions(+), 32 deletions(-) create mode 100644 core/src/test/java/org/testcontainers/images/LocalImagesCacheAccessor.java diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 9a6165ef55f..4cbe81f9e2d 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -25,10 +25,10 @@ import org.testcontainers.dockerclient.DockerClientProviderStrategy; import org.testcontainers.dockerclient.DockerMachineClientProviderStrategy; import org.testcontainers.dockerclient.TransportConfig; +import org.testcontainers.images.RemoteDockerImage; import org.testcontainers.images.TimeLimitedLoggedPullImageResultCallback; import org.testcontainers.utility.ComparableVersion; import org.testcontainers.utility.DockerImageName; -import org.testcontainers.utility.ImageNameSubstitutor; import org.testcontainers.utility.MountableFile; import org.testcontainers.utility.ResourceReaper; import org.testcontainers.utility.RyukResourceReaper; @@ -236,7 +236,6 @@ public void close() { checkDiskSpace(client, ryukContainerId); } else { runInsideDocker( - client, createContainerCmd -> { createContainerCmd.withName("testcontainers-checks-" + SESSION_ID); createContainerCmd.getHostConfig().withAutoRemove(true); @@ -335,9 +334,12 @@ private boolean checkMountableFile() { } /** - * Check whether the image is available locally and pull it otherwise - */ + * Check whether the image is available locally and pull it otherwise + * + * @deprecated use {@link RemoteDockerImage} + */ @SneakyThrows + @Deprecated public void checkAndPullImage(DockerClient client, String image) { try { client.inspectImageCmd(image).exec(); @@ -363,18 +365,14 @@ public String dockerHostIpAddress() { } public T runInsideDocker(Consumer createContainerCmdConsumer, BiFunction block) { - // We can't use client() here because it might create an infinite loop - return runInsideDocker(getOrInitializeStrategy().getDockerClient(), createContainerCmdConsumer, block); + return runInsideDocker(TINY_IMAGE, createContainerCmdConsumer, block); } - private T runInsideDocker(DockerClient client, Consumer createContainerCmdConsumer, BiFunction block) { - - final String tinyImage = ImageNameSubstitutor.instance().apply(TINY_IMAGE).asCanonicalNameString(); - - checkAndPullImage(client, tinyImage); + T runInsideDocker(DockerImageName imageName, Consumer createContainerCmdConsumer, BiFunction block) { + RemoteDockerImage dockerImage = new RemoteDockerImage(imageName); HashMap labels = new HashMap<>(DEFAULT_LABELS); labels.putAll(ResourceReaper.instance().getLabels()); - CreateContainerCmd createContainerCmd = client.createContainerCmd(tinyImage) + CreateContainerCmd createContainerCmd = client.createContainerCmd(dockerImage.get()) .withLabels(labels); createContainerCmdConsumer.accept(createContainerCmd); String id = createContainerCmd.exec().getId(); diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index b8913e8ac7c..4db9361a618 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -25,12 +25,14 @@ import org.testcontainers.containers.wait.strategy.WaitAllStrategy; import org.testcontainers.containers.wait.strategy.WaitStrategy; import org.testcontainers.dockerclient.TransportConfig; +import org.testcontainers.images.RemoteDockerImage; import org.testcontainers.lifecycle.Startable; import org.testcontainers.utility.AuditLogger; import org.testcontainers.utility.Base58; import org.testcontainers.utility.CommandLine; import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.DockerLoggerFactory; +import org.testcontainers.utility.ImageNameSubstitutor; import org.testcontainers.utility.LogUtils; import org.testcontainers.utility.MountableFile; import org.testcontainers.utility.PathUtils; @@ -186,7 +188,9 @@ private void pullImages() { .forEach(imageName -> { try { log.info("Preemptively checking local images for '{}', referenced via a compose file or transitive Dockerfile. If not available, it will be pulled.", imageName); - DockerClientFactory.instance().checkAndPullImage(dockerClient, imageName); + new RemoteDockerImage(DockerImageName.parse(imageName)) + .withImageNameSubstitutor(ImageNameSubstitutor.noop()) + .get(); } catch (Exception e) { log.warn("Unable to pre-fetch an image ({}) depended upon by Docker Compose build - startup will continue but may fail. Exception message was: {}", imageName, e.getMessage()); } diff --git a/core/src/main/java/org/testcontainers/images/LocalImagesCache.java b/core/src/main/java/org/testcontainers/images/LocalImagesCache.java index ec371ef830e..ec38721a229 100644 --- a/core/src/main/java/org/testcontainers/images/LocalImagesCache.java +++ b/core/src/main/java/org/testcontainers/images/LocalImagesCache.java @@ -21,7 +21,8 @@ enum LocalImagesCache { INSTANCE; - private final AtomicBoolean initialized = new AtomicBoolean(false); + @VisibleForTesting + final AtomicBoolean initialized = new AtomicBoolean(false); @VisibleForTesting final Map cache = new ConcurrentHashMap<>(); diff --git a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java index 4ce38124fe7..3bf2243549a 100644 --- a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java +++ b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java @@ -10,7 +10,7 @@ import lombok.NonNull; import lombok.SneakyThrows; import lombok.ToString; -import lombok.experimental.Wither; +import lombok.With; import org.slf4j.Logger; import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.ContainerFetchException; @@ -34,9 +34,12 @@ public class RemoteDockerImage extends LazyFuture { @ToString.Exclude private Future imageNameFuture; - @Wither + @With private ImagePullPolicy imagePullPolicy = PullPolicy.defaultPolicy(); + @With + private ImageNameSubstitutor imageNameSubstitutor = ImageNameSubstitutor.instance(); + @ToString.Exclude private DockerClient dockerClient = DockerClientFactory.lazyClient(); @@ -115,7 +118,7 @@ private DockerImageName getImageName() throws InterruptedException, ExecutionExc final DockerImageName specifiedImageName = imageNameFuture.get(); // Allow the image name to be substituted - return ImageNameSubstitutor.instance().apply(specifiedImageName); + return imageNameSubstitutor.apply(specifiedImageName); } @ToString.Include(name = "imageName", rank = 1) diff --git a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java index abf13f8ef02..281a74feaba 100644 --- a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java +++ b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java @@ -13,13 +13,16 @@ import org.slf4j.Logger; import org.testcontainers.DockerClientFactory; import org.testcontainers.images.ParsedDockerfile; +import org.testcontainers.images.RemoteDockerImage; import org.testcontainers.images.builder.traits.BuildContextBuilderTrait; import org.testcontainers.images.builder.traits.ClasspathTrait; import org.testcontainers.images.builder.traits.DockerfileTrait; import org.testcontainers.images.builder.traits.FilesTrait; import org.testcontainers.images.builder.traits.StringsTrait; import org.testcontainers.utility.Base58; +import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.DockerLoggerFactory; +import org.testcontainers.utility.ImageNameSubstitutor; import org.testcontainers.utility.LazyFuture; import org.testcontainers.utility.ResourceReaper; @@ -172,7 +175,9 @@ private void prePullDependencyImages(Set imagesToPull) { imagesToPull.forEach(imageName -> { try { log.info("Pre-emptively checking local images for '{}', referenced via a Dockerfile. If not available, it will be pulled.", imageName); - DockerClientFactory.instance().checkAndPullImage(dockerClient, imageName); + new RemoteDockerImage(DockerImageName.parse(imageName)) + .withImageNameSubstitutor(ImageNameSubstitutor.noop()) + .get(); } catch (Exception e) { log.warn("Unable to pre-fetch an image ({}) depended upon by Dockerfile - image build will continue but may fail. Exception message was: {}", imageName, e.getMessage()); } diff --git a/core/src/main/java/org/testcontainers/utility/ImageNameSubstitutor.java b/core/src/main/java/org/testcontainers/utility/ImageNameSubstitutor.java index 645b717a562..a1cb2492204 100644 --- a/core/src/main/java/org/testcontainers/utility/ImageNameSubstitutor.java +++ b/core/src/main/java/org/testcontainers/utility/ImageNameSubstitutor.java @@ -53,6 +53,10 @@ public synchronized static ImageNameSubstitutor instance() { return instance; } + public static ImageNameSubstitutor noop() { + return new NoopImageNameSubstitutor(); + } + private static ImageNameSubstitutor wrapWithLogging(final ImageNameSubstitutor wrappedInstance) { return new LogWrappedImageNameSubstitutor(wrappedInstance); } @@ -127,4 +131,17 @@ protected String getDescription() { ); } } + + private static class NoopImageNameSubstitutor extends ImageNameSubstitutor { + + @Override + public DockerImageName apply(DockerImageName original) { + return original; + } + + @Override + protected String getDescription() { + return "No-op substitutor"; + } + } } diff --git a/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java b/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java index 743b33856cf..9efbf5ed972 100644 --- a/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java @@ -17,6 +17,7 @@ import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.ContainerState; +import org.testcontainers.images.RemoteDockerImage; import java.io.IOException; import java.net.InetSocketAddress; @@ -85,16 +86,13 @@ private synchronized void maybeStart() { return; } DockerClient client = DockerClientFactory.lazyClient(); - String ryukImage = ImageNameSubstitutor.instance() - .apply(DockerImageName.parse("testcontainers/ryuk:0.3.3")) - .asCanonicalNameString(); - DockerClientFactory.instance().checkAndPullImage(client, ryukImage); + RemoteDockerImage ryukImage = new RemoteDockerImage(DockerImageName.parse("testcontainers/ryuk:0.3.3")); List binds = new ArrayList<>(); binds.add(new Bind(DockerClientFactory.instance().getRemoteDockerUnixSocketPath(), new Volume("/var/run/docker.sock"))); ExposedPort ryukExposedPort = ExposedPort.tcp(8080); - containerId = client.createContainerCmd(ryukImage) + containerId = client.createContainerCmd(ryukImage.get()) .withHostConfig( new HostConfig() .withAutoRemove(true) diff --git a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java index 16d25b2f625..4dcfc26349f 100644 --- a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java +++ b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java @@ -2,7 +2,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.testcontainers.TestImages.TINY_IMAGE; import com.github.dockerjava.api.exception.NotFoundException; import org.junit.Rule; @@ -10,6 +9,8 @@ import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.DockerClientFactory.DiskSpaceUsage; import org.testcontainers.dockerclient.LogToStringContainerCallback; +import org.testcontainers.images.LocalImagesCacheAccessor; +import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MockTestcontainersConfigurationRule; /** @@ -24,16 +25,21 @@ public class DockerClientFactoryTest { public void runCommandInsideDockerShouldNotFailIfImageDoesNotExistsLocally() { final DockerClientFactory dockFactory = DockerClientFactory.instance(); + + DockerImageName imageName = DockerImageName.parse("testcontainers/helloworld:1.1.0"); + try { //remove tiny image, so it will be pulled during next command run dockFactory.client() - .removeImageCmd(TINY_IMAGE.asCanonicalNameString()) + .removeImageCmd(imageName.asCanonicalNameString()) .withForce(true).exec(); } catch (NotFoundException ignored) { // Do not fail if it's not pulled yet } + LocalImagesCacheAccessor.clearCache(); dockFactory.runInsideDocker( + imageName, cmd -> cmd.withCmd("sh", "-c", "echo 'SUCCESS'"), (client, id) -> client.logContainerCmd(id) diff --git a/core/src/test/java/org/testcontainers/images/LocalImagesCacheAccessor.java b/core/src/test/java/org/testcontainers/images/LocalImagesCacheAccessor.java new file mode 100644 index 00000000000..4e7a28c535a --- /dev/null +++ b/core/src/test/java/org/testcontainers/images/LocalImagesCacheAccessor.java @@ -0,0 +1,9 @@ +package org.testcontainers.images; + +public final class LocalImagesCacheAccessor { + + public static synchronized void clearCache() { + LocalImagesCache.INSTANCE.cache.clear(); + LocalImagesCache.INSTANCE.initialized.set(false); + } +} diff --git a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java index c57c1d7c0b6..412a945b204 100644 --- a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java +++ b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java @@ -17,6 +17,7 @@ import org.testcontainers.containers.ContainerState; import org.testcontainers.containers.DockerComposeContainer; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.images.LocalImagesCacheAccessor; import org.testcontainers.images.builder.ImageFromDockerfile; import java.io.IOException; @@ -93,6 +94,7 @@ public void removeImageFromLocalDocker() { } catch (NotFoundException ignored) { } + LocalImagesCacheAccessor.clearCache(); } @AfterClass diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java index b8f2ec1aae0..b34f43f79ed 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java @@ -8,6 +8,7 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.testcontainers.DockerClientFactory; +import org.testcontainers.images.RemoteDockerImage; import org.testcontainers.utility.DockerImageName; import java.util.Arrays; @@ -72,15 +73,9 @@ public static class On { @BeforeClass public static void createCustomTag() { DockerClient dockerClient = DockerClientFactory.instance().client(); - DockerClientFactory - .instance() - .checkAndPullImage( - dockerClient, - LOCALSTACK_0_12_IMAGE.asCanonicalNameString() - ); dockerClient .tagImageCmd( - LOCALSTACK_0_12_IMAGE.asCanonicalNameString(), + new RemoteDockerImage(LOCALSTACK_0_12_IMAGE).get(), LOCALSTACK_CUSTOM_TAG.getRepository(), LOCALSTACK_CUSTOM_TAG.getVersionPart() ) From 5bbffb6c9962807fc0c54180d29f2aa4d3deaa86 Mon Sep 17 00:00:00 2001 From: Yannick Weber Date: Tue, 29 Mar 2022 14:08:46 +0200 Subject: [PATCH 2434/5615] Improved jitpack dependencies page (#5189) --- docs/jitpack_dependencies.md | 61 ++++++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/docs/jitpack_dependencies.md b/docs/jitpack_dependencies.md index 7689772de92..23a4c7353ad 100644 --- a/docs/jitpack_dependencies.md +++ b/docs/jitpack_dependencies.md @@ -3,22 +3,51 @@ If you like to live on the bleeding edge, [jitpack.io](https://jitpack.io) can be used to obtain SNAPSHOT versions. Use the following dependency description instead: -```xml - - com.github.testcontainers.testcontainers-java - --artifact name-- - -SNAPSHOT - -``` +=== "Gradle" + ```groovy + testImplementation "com.github.testcontainers.testcontainers-java:--artifact name--:master-SNAPSHOT" + ``` +=== "Maven" + ```xml + + com.github.testcontainers.testcontainers-java + --artifact name-- + master-SNAPSHOT + + ``` -A specific git revision (such as `093a3a4628`) can be used as a fixed version instead. The JitPack maven repository must also be declared, e.g.: +A specific git revision (such as `02782d9`) can be used as a fixed version instead: -```xml - - - jitpack.io - https://jitpack.io - - -``` +=== "Gradle" + ```groovy + testImplementation "com.github.testcontainers.testcontainers-java:--artifact name--:02782d9" + ``` +=== "Maven" + ```xml + + com.github.testcontainers.testcontainers-java + --artifact name-- + 02782d9 + + ``` + +The JitPack maven repository must also be declared, e.g.: + +=== "Gradle" + ```groovy + repositories { + maven { + url "https://jitpack.io" + } + } + ``` +=== "Maven" + ```xml + + + jitpack.io + https://jitpack.io + + + ``` From 04ecb68363596d8feb37fd2d4fcf3233031582eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Prinet?= Date: Wed, 30 Mar 2022 19:36:41 +0200 Subject: [PATCH 2435/5615] Improve Gradle build cacheability (#5188) * Bump up Gradle plugins version * Allow cache relocatability for Delombok task * Declare Delombok task inputs/outputs inside DelombokArgumentProvider --- build.gradle | 12 +++------ .../build/DelombokArgumentProvider.groovy | 26 +++++++++++++++++++ settings.gradle | 4 +-- 3 files changed, 32 insertions(+), 10 deletions(-) create mode 100644 buildSrc/src/main/groovy/org/testcontainers/build/DelombokArgumentProvider.groovy diff --git a/build.gradle b/build.gradle index 8fb15a6566f..309743477c1 100644 --- a/build.gradle +++ b/build.gradle @@ -43,18 +43,14 @@ subprojects { version = '1.18.20' } + task delombok(type: io.franzbecker.gradle.lombok.task.DelombokTask) { - def outputDir = file("$buildDir/delombok") - outputs.dir(outputDir) outputs.cacheIf { true } - for (srcDir in project.sourceSets.main.java.srcDirs) { - // TODO: named input and relative path for `srcDir` to make task more cacheable - inputs.dir(srcDir) - // TODO: `outputDir` as relative path to make task more cacheable - args(srcDir, "-d", outputDir, "-f", "generateDelombokComment:skip") - } + argumentProviders.addAll( + new org.testcontainers.build.DelombokArgumentProvider(srcDirs: project.sourceSets.main.java.srcDirs, outputDir: file("$buildDir/delombok")) + ) } delombok.onlyIf { project.sourceSets.main.java.srcDirs.find { it.exists() } diff --git a/buildSrc/src/main/groovy/org/testcontainers/build/DelombokArgumentProvider.groovy b/buildSrc/src/main/groovy/org/testcontainers/build/DelombokArgumentProvider.groovy new file mode 100644 index 00000000000..2cb4931eb58 --- /dev/null +++ b/buildSrc/src/main/groovy/org/testcontainers/build/DelombokArgumentProvider.groovy @@ -0,0 +1,26 @@ +package org.testcontainers.build + +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.process.CommandLineArgumentProvider + +/** + * Allows build cache relocatability for Delombok task + */ +class DelombokArgumentProvider implements CommandLineArgumentProvider { + + @InputFiles + @PathSensitive(PathSensitivity.RELATIVE) + Set srcDirs + + @OutputDirectory + File outputDir + + @Override + Iterable asArguments() { + return [srcDirs.collect { it.absolutePath }.join(" "), "-d", outputDir.absolutePath, "-f", "generateDelombokComment:skip"] + } + +} diff --git a/settings.gradle b/settings.gradle index e4c65186fb8..64f2aa5d70a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,8 +5,8 @@ buildscript { } } dependencies { - classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.8.1" - classpath "com.gradle:common-custom-user-data-gradle-plugin:1.6.2" + classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.9" + classpath "com.gradle:common-custom-user-data-gradle-plugin:1.6.5" } } From 457b583df82bc5e778b95276aaf6ad1fc0297dfe Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 31 Mar 2022 11:29:13 +0200 Subject: [PATCH 2436/5615] Update Gradle Wrapper to 7.4.1 (#5168) Signed-off-by: gradle-update-robot Co-authored-by: gradle-update-robot --- examples/gradle/wrapper/gradle-wrapper.properties | 4 ++-- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/gradle/wrapper/gradle-wrapper.properties b/examples/gradle/wrapper/gradle-wrapper.properties index 1debed0d8fd..ffe61b45c92 100644 --- a/examples/gradle/wrapper/gradle-wrapper.properties +++ b/examples/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=8cc27038d5dbd815759851ba53e70cf62e481b87494cc97cfd97982ada5ba634 -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionSha256Sum=e5444a57cda4a95f90b0c9446a9e1b47d3d7f69057765bfb54bd4f482542d548 +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index dcf5e2cb7b3..db3bccc1f9b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=cd5c2958a107ee7f0722004a12d0f8559b4564c34daad7df06cffd4d12a426d0 -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip +distributionSha256Sum=a9a7b7baba105f6557c9dcf9c3c6e8f7e57e6b49889c5f1d133f015d0727e4be +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 0408db806f6d57b8117881adcd10b6bd6c9dcd9c Mon Sep 17 00:00:00 2001 From: Joseph Petersen Date: Thu, 31 Mar 2022 11:30:10 +0200 Subject: [PATCH 2437/5615] Add virtual environment to git ignore (#5156) GitHub Codespaces auto detects Pipfile and starts installing dependencies into .venv/ Signed-off-by: Joseph Petersen --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d6a9a697796..95fc6e52000 100644 --- a/.gitignore +++ b/.gitignore @@ -66,4 +66,7 @@ src/pip-delete-this-directory.txt .DS_Store # Codespaces / VSCode -/.vscode/ \ No newline at end of file +/.vscode/ + +# Python virtual environment +/.venv/ From 9712d25aa899bc306287daffe77bdf28b64c57b4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 08:47:22 +0200 Subject: [PATCH 2438/5615] Update Gradle Wrapper to 7.4.2 (#5193) Signed-off-by: gradle-update-robot Co-authored-by: gradle-update-robot --- examples/gradle/wrapper/gradle-wrapper.properties | 4 ++-- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/gradle/wrapper/gradle-wrapper.properties b/examples/gradle/wrapper/gradle-wrapper.properties index ffe61b45c92..5c51a4acc9d 100644 --- a/examples/gradle/wrapper/gradle-wrapper.properties +++ b/examples/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=e5444a57cda4a95f90b0c9446a9e1b47d3d7f69057765bfb54bd4f482542d548 -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip +distributionSha256Sum=29e49b10984e585d8118b7d0bc452f944e386458df27371b49b4ac1dec4b7fda +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index db3bccc1f9b..e1e0c8dc42b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=a9a7b7baba105f6557c9dcf9c3c6e8f7e57e6b49889c5f1d133f015d0727e4be -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-all.zip +distributionSha256Sum=e6d864e3b5bc05cc62041842b306383fc1fefcec359e70cebb1d470a6094ca82 +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 297621fe4ab0a775d8dc2bf4f869fbed61dac95e Mon Sep 17 00:00:00 2001 From: Yannick Weber Date: Fri, 1 Apr 2022 13:50:18 +0200 Subject: [PATCH 2439/5615] Added tmpFs usage for HiveMQ module (#5109) Improves performance for IO heavy operations --- .../org/testcontainers/hivemq/HiveMQContainer.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java index 996caad1eb8..5bb8935c39e 100644 --- a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java +++ b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java @@ -18,6 +18,7 @@ import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -77,6 +78,16 @@ public HiveMQContainer(final @NotNull DockerImageName dockerImageName) { }); } }); + + final HashMap tmpFs = new HashMap<>(); + if (dockerImageName.isCompatibleWith(DEFAULT_HIVEMQ_EE_IMAGE_NAME)) { + tmpFs.put("/opt/hivemq/audit", "rw"); + tmpFs.put("/opt/hivemq/backup", "rw"); + } + + tmpFs.put("/opt/hivemq/log", "rw"); + tmpFs.put("/opt/hivemq/data", "rw"); + withTmpFs(tmpFs); } @Override From 498dac062d628a63c9755492bcdbc13afbddc040 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:00:38 +0000 Subject: [PATCH 2440/5615] Bump tomcat-jdbc from 10.0.16 to 10.0.20 in /modules/jdbc-test Bumps tomcat-jdbc from 10.0.16 to 10.0.20. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index bf90d9603a2..5c10742b7f7 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -14,7 +14,7 @@ dependencies { exclude(group: "net.java.dev.jna", module: "jna") } - api 'org.apache.tomcat:tomcat-jdbc:10.0.16' + api 'org.apache.tomcat:tomcat-jdbc:10.0.20' api 'org.vibur:vibur-dbcp:25.0' api 'mysql:mysql-connector-java:8.0.28' } From 913efe9e73ecf7a7a332e4f1ef33cd6214b56ced Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:00:39 +0000 Subject: [PATCH 2441/5615] Bump tomcat-jdbc from 10.0.16 to 10.0.20 in /modules/jdbc Bumps tomcat-jdbc from 10.0.16 to 10.0.20. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 71940e0026a..84b927ac4a9 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -7,7 +7,7 @@ dependencies { compileOnly 'org.jetbrains:annotations:23.0.0' testImplementation 'commons-dbutils:commons-dbutils:1.7' testImplementation 'org.vibur:vibur-dbcp:25.0' - testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.16' + testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.20' testImplementation 'com.zaxxer:HikariCP-java6:2.3.13' testImplementation 'com.googlecode.junit-toolbox:junit-toolbox:2.4' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From 95fec38a2cac0f4fe9e5cbdfdad6dbb46e68e6d8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:00:41 +0000 Subject: [PATCH 2442/5615] Bump aws-java-sdk-s3 from 1.12.169 to 1.12.191 in /modules/localstack Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.169 to 1.12.191. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.169...1.12.191) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index c16baad9a22..ccd322f0952 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.169' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.191' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.169' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.150' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.169' From a4587abe04ce78e3f54abe28b95927d69ceb23de Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:00:41 +0000 Subject: [PATCH 2443/5615] Bump neo4j-java-driver from 4.4.3 to 4.4.5 in /modules/neo4j Bumps [neo4j-java-driver](https://github.com/neo4j/neo4j-java-driver) from 4.4.3 to 4.4.5. - [Release notes](https://github.com/neo4j/neo4j-java-driver/releases) - [Commits](https://github.com/neo4j/neo4j-java-driver/compare/4.4.3...4.4.5) --- updated-dependencies: - dependency-name: org.neo4j.driver:neo4j-java-driver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 27bf38b9f23..6187d665098 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -33,6 +33,6 @@ dependencies { api project(":testcontainers") - testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.3' + testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.5' testImplementation 'org.assertj:assertj-core:3.22.0' } From 9216980323e5fbc4bd81f82058025a876d4240c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:00:42 +0000 Subject: [PATCH 2444/5615] Bump rest-assured from 4.5.1 to 5.0.0 in /modules/vault Bumps [rest-assured](https://github.com/rest-assured/rest-assured) from 4.5.1 to 5.0.0. - [Release notes](https://github.com/rest-assured/rest-assured/releases) - [Changelog](https://github.com/rest-assured/rest-assured/blob/master/changelog.txt) - [Commits](https://github.com/rest-assured/rest-assured/compare/rest-assured-4.5.1...rest-assured-5.0.0) --- updated-dependencies: - dependency-name: io.rest-assured:rest-assured dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index ce000685a10..68daf4ed26f 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':testcontainers') testImplementation 'com.bettercloud:vault-java-driver:5.1.0' - testImplementation 'io.rest-assured:rest-assured:4.5.1' + testImplementation 'io.rest-assured:rest-assured:5.0.0' testImplementation 'org.assertj:assertj-core:3.22.0' } From e7afcec5a09789bade7e4a808edf6f6eaf1dbc5b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:00:43 +0000 Subject: [PATCH 2445/5615] Bump java-client from 3.2.5 to 3.2.6 in /modules/couchbase Bumps [java-client](https://github.com/couchbase/couchbase-jvm-clients) from 3.2.5 to 3.2.6. - [Release notes](https://github.com/couchbase/couchbase-jvm-clients/releases) - [Commits](https://github.com/couchbase/couchbase-jvm-clients/compare/java-client-3.2.5...java-client-3.2.6) --- updated-dependencies: - dependency-name: com.couchbase.client:java-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 0245a5935cb..a3cb0fcbc70 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -5,6 +5,6 @@ dependencies { // TODO use JDK's HTTP client and/or Apache HttpClient5 shaded 'com.squareup.okhttp3:okhttp:3.14.9' - testImplementation 'com.couchbase.client:java-client:3.2.5' + testImplementation 'com.couchbase.client:java-client:3.2.6' testImplementation 'org.awaitility:awaitility:4.1.1' } From 3ff87f5bdeee8256195fb7b87a801a3eef7a35c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:00:44 +0000 Subject: [PATCH 2446/5615] Bump jedis from 4.1.1 to 4.2.1 in /examples Bumps [jedis](https://github.com/redis/jedis) from 4.1.1 to 4.2.1. - [Release notes](https://github.com/redis/jedis/releases) - [Commits](https://github.com/redis/jedis/compare/v4.1.1...v4.2.1) --- updated-dependencies: - dependency-name: redis.clients:jedis dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index ec3d0bd134c..38a8715364a 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' - implementation 'redis.clients:jedis:4.1.1' + implementation 'redis.clients:jedis:4.2.1' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 32e35f07766..9583fe99147 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' - implementation 'redis.clients:jedis:4.1.1' + implementation 'redis.clients:jedis:4.2.1' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 2ebaba5be50..e8d470f0f23 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { - implementation 'redis.clients:jedis:4.1.1' + implementation 'redis.clients:jedis:4.2.1' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.32' From cce236c67ff09ca88aacf4435bd740ebac4f166b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:00:45 +0000 Subject: [PATCH 2447/5615] Bump mockito-core from 4.3.1 to 4.4.0 in /modules/junit-jupiter Bumps [mockito-core](https://github.com/mockito/mockito) from 4.3.1 to 4.4.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.3.1...v4.4.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 749bf72690d..89882481dc1 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,7 +9,7 @@ dependencies { testImplementation 'com.zaxxer:HikariCP:4.0.3' testImplementation 'redis.clients:jedis:4.1.1' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' - testImplementation ('org.mockito:mockito-core:4.3.1') { + testImplementation ('org.mockito:mockito-core:4.4.0') { exclude(module: 'hamcrest-core') } testImplementation 'org.assertj:assertj-core:3.22.0' From 156971e69538d73d6a3d4ec1c0bd992b636ee3ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:00:46 +0000 Subject: [PATCH 2448/5615] Bump transport from 7.17.1 to 7.17.2 in /modules/elasticsearch Bumps [transport](https://github.com/elastic/elasticsearch) from 7.17.1 to 7.17.2. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Changelog](https://github.com/elastic/elasticsearch/blob/master/CHANGELOG.md) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.17.1...v7.17.2) --- updated-dependencies: - dependency-name: org.elasticsearch.client:transport dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index d669f0fbd05..a8d718af3c4 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,6 +3,6 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') testImplementation "org.elasticsearch.client:elasticsearch-rest-client:7.16.2" - testImplementation "org.elasticsearch.client:transport:7.17.1" + testImplementation "org.elasticsearch.client:transport:7.17.2" testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 03fa998e2ea9cfabe5d9921f76c96dc82c0be2e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:00:47 +0000 Subject: [PATCH 2449/5615] Bump logback-classic from 1.2.10 to 1.2.11 in /examples Bumps logback-classic from 1.2.10 to 1.2.11. --- updated-dependencies: - dependency-name: ch.qos.logback:logback-classic dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/disque-job-queue/build.gradle | 2 +- examples/kafka-cluster/build.gradle | 2 +- examples/linked-container/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index 1639a091e91..2352eace899 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -12,7 +12,7 @@ dependencies { implementation 'biz.paluch.redis:spinach:0.3' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' - testImplementation 'ch.qos.logback:logback-classic:1.2.10' + testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.mockito:mockito-all:1.10.19' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index 2e37410bcea..7ba974d3a6d 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -13,5 +13,5 @@ dependencies { testImplementation 'org.apache.kafka:kafka-clients:3.1.0' testImplementation 'org.assertj:assertj-core:3.22.0' testImplementation 'com.google.guava:guava:23.0' - testImplementation 'ch.qos.logback:logback-classic:1.2.10' + testImplementation 'ch.qos.logback:logback-classic:1.2.11' } diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 1cb3a0285f2..c61ce36e473 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'com.squareup.okhttp3:okhttp:4.9.3' implementation 'org.json:json:20211205' testImplementation 'org.postgresql:postgresql:42.3.3' - testImplementation 'ch.qos.logback:logback-classic:1.2.10' + testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.testcontainers:postgresql' } diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index ec3d0bd134c..bc1d30d9599 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -12,7 +12,7 @@ dependencies { implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' - testImplementation 'ch.qos.logback:logback-classic:1.2.10' + testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.testng:testng:7.5' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 32e35f07766..f2d5830404d 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -13,6 +13,6 @@ dependencies { implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'junit:junit:4.13.2' - testImplementation 'ch.qos.logback:logback-classic:1.2.10' + testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 2ebaba5be50..2879ddb4fdd 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -13,7 +13,7 @@ dependencies { implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.32' - testImplementation 'ch.qos.logback:logback-classic:1.2.10' + testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From cb58298c4ef96968ce6e0f654d9f87d2a7b56126 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:00:47 +0000 Subject: [PATCH 2450/5615] Bump org.jetbrains.kotlin.plugin.spring in /examples Bumps org.jetbrains.kotlin.plugin.spring from 1.6.10 to 1.6.20. --- updated-dependencies: - dependency-name: org.jetbrains.kotlin.plugin.spring dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 45cee0ec20b..1d98c3e3eff 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,7 +1,7 @@ plugins { id("org.springframework.boot") version "2.6.4" id("org.jetbrains.kotlin.jvm") version "1.6.10" - id("org.jetbrains.kotlin.plugin.spring") version "1.6.10" + id("org.jetbrains.kotlin.plugin.spring") version "1.6.20" } apply plugin: 'io.spring.dependency-management' From 58f70290f0a77c11fa3538cab671dd94b149afdb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:00:48 +0000 Subject: [PATCH 2451/5615] Bump awaitility from 4.1.1 to 4.2.0 in /core Bumps [awaitility](https://github.com/awaitility/awaitility) from 4.1.1 to 4.2.0. - [Release notes](https://github.com/awaitility/awaitility/releases) - [Changelog](https://github.com/awaitility/awaitility/blob/master/changelog.txt) - [Commits](https://github.com/awaitility/awaitility/compare/awaitility-4.1.1...awaitility-4.2.0) --- updated-dependencies: - dependency-name: org.awaitility:awaitility dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 558584a0d7c..c17169977cc 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -71,7 +71,7 @@ dependencies { exclude(group: 'org.jetbrains', module: 'annotations') } - shaded 'org.awaitility:awaitility:4.1.1' + shaded 'org.awaitility:awaitility:4.2.0' api platform('com.github.docker-java:docker-java-bom:3.2.13') shaded platform('com.github.docker-java:docker-java-bom:3.2.13') From 02dec3edf4408a43e5060ce0a639f5ab0043dc54 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:00:51 +0000 Subject: [PATCH 2452/5615] Bump s3 from 2.17.108 to 2.17.162 in /modules/localstack Bumps s3 from 2.17.108 to 2.17.162. --- updated-dependencies: - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index c16baad9a22..401230dbb61 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.169' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.150' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.169' - testImplementation 'software.amazon.awssdk:s3:2.17.108' + testImplementation 'software.amazon.awssdk:s3:2.17.162' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 285ce0efe024f2c33d62069d2e89eeb277b7a272 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:00:52 +0000 Subject: [PATCH 2453/5615] Bump neo4j-java-driver from 4.4.3 to 4.4.5 in /examples Bumps [neo4j-java-driver](https://github.com/neo4j/neo4j-java-driver) from 4.4.3 to 4.4.5. - [Release notes](https://github.com/neo4j/neo4j-java-driver/releases) - [Commits](https://github.com/neo4j/neo4j-java-driver/compare/4.4.3...4.4.5) --- updated-dependencies: - dependency-name: org.neo4j.driver:neo4j-java-driver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/neo4j-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/neo4j-container/build.gradle b/examples/neo4j-container/build.gradle index 718adc3183a..652937d8b33 100644 --- a/examples/neo4j-container/build.gradle +++ b/examples/neo4j-container/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { testImplementation 'org.assertj:assertj-core:3.22.0' - testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.3' + testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.5' testImplementation 'org.testcontainers:neo4j' testImplementation 'org.testcontainers:junit-jupiter' } From 16fb27f651a7d1cd66881238cfa99e2433e483eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:01:23 +0000 Subject: [PATCH 2454/5615] Bump actions/cache from 2.1.7 to 3.0.1 Bumps [actions/cache](https://github.com/actions/cache) from 2.1.7 to 3.0.1. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v2.1.7...v3.0.1) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index 3fb2467a295..7b9a9e5c3a1 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -22,7 +22,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v2.1.7 + uses: actions/cache@v3.0.1 continue-on-error: true with: path: ~/.gradle/caches @@ -50,7 +50,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v2.1.7 + uses: actions/cache@v3.0.1 continue-on-error: true with: path: ~/.gradle/caches diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index db7232fffa8..4fad892230d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v2.1.7 + uses: actions/cache@v3.0.1 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-home-testmatrix-${{ hashFiles('**/*.gradle') }} @@ -56,7 +56,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v2.1.7 + uses: actions/cache@v3.0.1 continue-on-error: true with: path: ~/.gradle/caches From 6f2eb5c6105d2cf5fb9b0e927223f594fdcf9ab8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:01:29 +0000 Subject: [PATCH 2455/5615] Bump peter-evans/create-pull-request from 3.12.1 to 4.0.1 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 3.12.1 to 4.0.1. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/f22a7da129c901513876a2380e2dae9f8e145330...f1a7646cead32c950d90344a4fb5d4e926972a8f) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/update-docs-version.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index 2c7626c7ec7..ceb2385e986 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -17,7 +17,7 @@ jobs: sed -i "s/latest_version: .*/latest_version: ${GITHUB_REF##*/}/g" mkdocs.yml git diff - name: Create Pull Request - uses: peter-evans/create-pull-request@f22a7da129c901513876a2380e2dae9f8e145330 # v3.10.1 + uses: peter-evans/create-pull-request@f1a7646cead32c950d90344a4fb5d4e926972a8f # v3.10.1 with: title: Update docs version to ${GITHUB_REF##*/} body: | From a4695120eb1c3f49a030f686e5747c7506251e69 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:04:58 +0000 Subject: [PATCH 2456/5615] Bump r2dbc-postgresql in /modules/r2dbc Bumps [r2dbc-postgresql](https://github.com/r2dbc/r2dbc-postgresql) from 0.8.11.RELEASE to 0.8.12.RELEASE. - [Release notes](https://github.com/r2dbc/r2dbc-postgresql/releases) - [Changelog](https://github.com/pgjdbc/r2dbc-postgresql/blob/v0.8.12.RELEASE/CHANGELOG) - [Commits](https://github.com/r2dbc/r2dbc-postgresql/compare/v0.8.11.RELEASE...v0.8.12.RELEASE) --- updated-dependencies: - dependency-name: io.r2dbc:r2dbc-postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/r2dbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle index 2fa066c21b3..ee5f5844e56 100644 --- a/modules/r2dbc/build.gradle +++ b/modules/r2dbc/build.gradle @@ -12,7 +12,7 @@ dependencies { api 'io.r2dbc:r2dbc-spi:0.8.1.RELEASE' testImplementation 'org.assertj:assertj-core:3.22.0' - testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.11.RELEASE' + testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.12.RELEASE' testImplementation project(':postgresql') testFixturesImplementation 'io.projectreactor:reactor-core:3.4.15' From b00d7c2ced538f9b1ff004a4f67e6a01ac25a32d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:04:59 +0000 Subject: [PATCH 2457/5615] Bump trino-jdbc from 369 to 375 in /modules/trino Bumps trino-jdbc from 369 to 375. --- updated-dependencies: - dependency-name: io.trino:trino-jdbc dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/trino/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/trino/build.gradle b/modules/trino/build.gradle index 9bf0e24f06b..f99951dc7fa 100644 --- a/modules/trino/build.gradle +++ b/modules/trino/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'io.trino:trino-jdbc:369' + testImplementation 'io.trino:trino-jdbc:375' compileOnly 'org.jetbrains:annotations:23.0.0' } From 0bc42f61c149e5d79cdbccec95ad19638cc056d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:04:59 +0000 Subject: [PATCH 2458/5615] Bump okhttp from 3.14.9 to 4.9.3 in /modules/solr Bumps [okhttp](https://github.com/square/okhttp) from 3.14.9 to 4.9.3. - [Release notes](https://github.com/square/okhttp/releases) - [Changelog](https://github.com/square/okhttp/blob/master/CHANGELOG.md) - [Commits](https://github.com/square/okhttp/compare/parent-3.14.9...parent-4.9.3) --- updated-dependencies: - dependency-name: com.squareup.okhttp3:okhttp dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/solr/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/solr/build.gradle b/modules/solr/build.gradle index 49b2a9970f3..5a6326f3844 100644 --- a/modules/solr/build.gradle +++ b/modules/solr/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Solr" dependencies { api project(':testcontainers') // TODO use JDK's HTTP client and/or Apache HttpClient5 - shaded 'com.squareup.okhttp3:okhttp:3.14.9' + shaded 'com.squareup.okhttp3:okhttp:4.9.3' testImplementation 'org.apache.solr:solr-solrj:8.11.1' From 1cdac6a748e3a80802d3c0432efc512c45491b77 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:05:00 +0000 Subject: [PATCH 2459/5615] Bump client-java from 14.0.0 to 15.0.0 in /modules/k3s Bumps [client-java](https://github.com/kubernetes-client/java) from 14.0.0 to 15.0.0. - [Release notes](https://github.com/kubernetes-client/java/releases) - [Changelog](https://github.com/kubernetes-client/java/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes-client/java/compare/client-java-parent-14.0.0...v15.0.0) --- updated-dependencies: - dependency-name: io.kubernetes:client-java dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/k3s/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/k3s/build.gradle b/modules/k3s/build.gradle index 075078f36d9..a373d8115d7 100644 --- a/modules/k3s/build.gradle +++ b/modules/k3s/build.gradle @@ -9,6 +9,6 @@ dependencies { shaded 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.8.8' testImplementation 'io.fabric8:kubernetes-client:5.12.1' - testImplementation 'io.kubernetes:client-java:14.0.0' + testImplementation 'io.kubernetes:client-java:15.0.0' testImplementation 'org.assertj:assertj-core:3.22.0' } From 5a3881995369330b216773af9fcc1ebf9d98e9ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:05:01 +0000 Subject: [PATCH 2460/5615] Bump google-cloud-bigtable from 2.5.2 to 2.6.1 in /modules/gcloud Bumps [google-cloud-bigtable](https://github.com/googleapis/java-bigtable) from 2.5.2 to 2.6.1. - [Release notes](https://github.com/googleapis/java-bigtable/releases) - [Changelog](https://github.com/googleapis/java-bigtable/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-bigtable/compare/v2.5.2...v2.6.1) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-bigtable dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index e0b232ac949..93111ef3f16 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-firestore:3.0.14' testImplementation 'com.google.cloud:google-cloud-pubsub:1.115.1' testImplementation 'com.google.cloud:google-cloud-spanner:6.20.0' - testImplementation 'com.google.cloud:google-cloud-bigtable:2.5.2' + testImplementation 'com.google.cloud:google-cloud-bigtable:2.6.1' testImplementation 'org.assertj:assertj-core:3.22.0' } From 0cc5b67ef8ce32ec0978eaffbf94628824fd7476 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:05:01 +0000 Subject: [PATCH 2461/5615] Bump mongodb-driver-sync from 4.5.0 to 4.5.1 in /modules/mongodb Bumps [mongodb-driver-sync](https://github.com/mongodb/mongo-java-driver) from 4.5.0 to 4.5.1. - [Release notes](https://github.com/mongodb/mongo-java-driver/releases) - [Commits](https://github.com/mongodb/mongo-java-driver/compare/r4.5.0...r4.5.1) --- updated-dependencies: - dependency-name: org.mongodb:mongodb-driver-sync dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mongodb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mongodb/build.gradle b/modules/mongodb/build.gradle index 73dba45dbbf..6bca2cb892b 100644 --- a/modules/mongodb/build.gradle +++ b/modules/mongodb/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: MongoDB" dependencies { api project(':testcontainers') - testImplementation("org.mongodb:mongodb-driver-sync:4.5.0") + testImplementation("org.mongodb:mongodb-driver-sync:4.5.1") } From b9adf8717210a5bc42e850f03722970fbe692409 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:05:02 +0000 Subject: [PATCH 2462/5615] Bump reactor-core from 3.4.15 to 3.4.16 in /modules/r2dbc Bumps [reactor-core](https://github.com/reactor/reactor-core) from 3.4.15 to 3.4.16. - [Release notes](https://github.com/reactor/reactor-core/releases) - [Commits](https://github.com/reactor/reactor-core/compare/v3.4.15...v3.4.16) --- updated-dependencies: - dependency-name: io.projectreactor:reactor-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/r2dbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle index 2fa066c21b3..56e910910d1 100644 --- a/modules/r2dbc/build.gradle +++ b/modules/r2dbc/build.gradle @@ -15,6 +15,6 @@ dependencies { testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.11.RELEASE' testImplementation project(':postgresql') - testFixturesImplementation 'io.projectreactor:reactor-core:3.4.15' + testFixturesImplementation 'io.projectreactor:reactor-core:3.4.16' testFixturesImplementation 'org.assertj:assertj-core:3.14.0' } From 8973288c81fb249f80c308e1ec6fbe466b52d237 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:05:12 +0000 Subject: [PATCH 2463/5615] Bump google-cloud-firestore from 3.0.14 to 3.0.21 in /modules/gcloud Bumps [google-cloud-firestore](https://github.com/googleapis/java-firestore) from 3.0.14 to 3.0.21. - [Release notes](https://github.com/googleapis/java-firestore/releases) - [Changelog](https://github.com/googleapis/java-firestore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-firestore/compare/v3.0.14...v3.0.21) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-firestore dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index e0b232ac949..cdc07d77c37 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':testcontainers') testImplementation 'com.google.cloud:google-cloud-datastore:2.2.2' - testImplementation 'com.google.cloud:google-cloud-firestore:3.0.14' + testImplementation 'com.google.cloud:google-cloud-firestore:3.0.21' testImplementation 'com.google.cloud:google-cloud-pubsub:1.115.1' testImplementation 'com.google.cloud:google-cloud-spanner:6.20.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.5.2' From fe7782c028875eb166982387a2011520aae5ebf7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:05:33 +0000 Subject: [PATCH 2464/5615] Bump hivemq-extension-sdk from 4.7.4 to 4.7.5 in /modules/hivemq Bumps [hivemq-extension-sdk](https://github.com/hivemq/hivemq-extension-sdk) from 4.7.4 to 4.7.5. - [Release notes](https://github.com/hivemq/hivemq-extension-sdk/releases) - [Commits](https://github.com/hivemq/hivemq-extension-sdk/compare/4.7.4...4.7.5) --- updated-dependencies: - dependency-name: com.hivemq:hivemq-extension-sdk dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index f0c3aaa2e69..17be5658992 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -13,7 +13,7 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") testImplementation(project(":junit-jupiter")) - testImplementation("com.hivemq:hivemq-extension-sdk:4.7.4") + testImplementation("com.hivemq:hivemq-extension-sdk:4.7.5") testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") testImplementation("org.apache.httpcomponents:httpclient:4.5.13") testImplementation("ch.qos.logback:logback-classic:1.2.10") From 64f96781c518b75fddc98bbad05b234faec7169f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:05:35 +0000 Subject: [PATCH 2465/5615] Bump zip4j from 2.9.1 to 2.10.0 in /modules/hivemq Bumps [zip4j](https://github.com/srikanth-lingala/zip4j) from 2.9.1 to 2.10.0. - [Release notes](https://github.com/srikanth-lingala/zip4j/releases) - [Commits](https://github.com/srikanth-lingala/zip4j/compare/v2.9.1...v2.10.0) --- updated-dependencies: - dependency-name: net.lingala.zip4j:zip4j dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index f0c3aaa2e69..79c59bba0d2 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -9,7 +9,7 @@ dependencies { shaded("org.javassist:javassist:3.28.0-GA") shaded("org.jboss.shrinkwrap:shrinkwrap-api:1.2.6") shaded("org.jboss.shrinkwrap:shrinkwrap-impl-base:1.2.6") - shaded("net.lingala.zip4j:zip4j:2.9.1") + shaded("net.lingala.zip4j:zip4j:2.10.0") testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") testImplementation(project(":junit-jupiter")) From 06fc95f5233b0c11d2ba319b4b02587db0b5028b Mon Sep 17 00:00:00 2001 From: Falko Modler Date: Wed, 6 Apr 2022 17:01:03 +0200 Subject: [PATCH 2466/5615] Fix ScriptUtils.runInitScript() in hierarchical classloader setup by using TCCL (#5167) --- .../main/java/org/testcontainers/ext/ScriptUtils.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java b/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java index 76f0cb90ea0..1b94b84f368 100644 --- a/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java +++ b/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java @@ -293,10 +293,13 @@ public static boolean containsSqlScriptDelimiters(String script, String delim) { */ public static void runInitScript(DatabaseDelegate databaseDelegate, String initScriptPath) { try { - URL resource = ScriptUtils.class.getClassLoader().getResource(initScriptPath); + URL resource = Thread.currentThread().getContextClassLoader().getResource(initScriptPath); if (resource == null) { - LOGGER.warn("Could not load classpath init script: {}", initScriptPath); - throw new ScriptLoadException("Could not load classpath init script: " + initScriptPath + ". Resource not found."); + resource = ScriptUtils.class.getClassLoader().getResource(initScriptPath); + if (resource == null) { + LOGGER.warn("Could not load classpath init script: {}", initScriptPath); + throw new ScriptLoadException("Could not load classpath init script: " + initScriptPath + ". Resource not found."); + } } String scripts = IOUtils.toString(resource, StandardCharsets.UTF_8); executeDatabaseScript(databaseDelegate, initScriptPath, scripts); From 734678f4be9e9a439b4391265d5f369ed1bab4b5 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Thu, 7 Apr 2022 09:29:04 +0200 Subject: [PATCH 2467/5615] Deperecate `getContainerIpAddress` (#5247) --- .../main/java/org/testcontainers/containers/ContainerState.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/org/testcontainers/containers/ContainerState.java b/core/src/main/java/org/testcontainers/containers/ContainerState.java index e5903b0b806..ef2c19a06c9 100644 --- a/core/src/main/java/org/testcontainers/containers/ContainerState.java +++ b/core/src/main/java/org/testcontainers/containers/ContainerState.java @@ -47,6 +47,7 @@ public interface ContainerState { * @deprecated use {@link #getHost()} * @see #getHost() */ + @Deprecated default String getContainerIpAddress() { return getHost(); } From 53e80aa4115b60eee11823b5aa7d3616065a9421 Mon Sep 17 00:00:00 2001 From: Matthew de Detrich Date: Thu, 7 Apr 2022 18:03:27 +0200 Subject: [PATCH 2468/5615] Fix incorrect path for RABBITMQ_CONFIG_FILE (#5184) Co-authored-by: Kevin Wittek --- .../containers/RabbitMQContainer.java | 2 +- .../containers/RabbitMQContainerTest.java | 24 +++++++------------ .../src/test/resources/logback-test.xml | 16 +++++++++++++ .../src/test/resources/rabbitmq-custom.conf | 3 +-- .../src/test/resources/rabbitmq-custom.config | 18 ++++++-------- 5 files changed, 34 insertions(+), 29 deletions(-) create mode 100644 modules/rabbitmq/src/test/resources/logback-test.xml diff --git a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java index edbffcf3174..37dc2fbe7ca 100644 --- a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java +++ b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java @@ -396,7 +396,7 @@ public RabbitMQContainer withRabbitMQConfig(MountableFile rabbitMQConf) { * @return This container. */ public RabbitMQContainer withRabbitMQConfigSysctl(MountableFile rabbitMQConf) { - withEnv("RABBITMQ_CONFIG_FILE", "/etc/rabbitmq/rabbitmq-custom"); + withEnv("RABBITMQ_CONFIG_FILE", "/etc/rabbitmq/rabbitmq-custom.conf"); return withCopyFileToContainer(rabbitMQConf, "/etc/rabbitmq/rabbitmq-custom.conf"); } diff --git a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java index 8be97deb710..474d613cb8b 100644 --- a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java +++ b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java @@ -14,6 +14,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; @@ -21,6 +22,8 @@ import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.util.Collections; +import java.util.Objects; +import java.util.Scanner; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; @@ -115,43 +118,34 @@ public void shouldCreateRabbitMQContainerWithQueues() throws IOException, Interr } @Test - public void shouldMountConfigurationFile() - { + public void shouldMountConfigurationFile() { try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { - container.withRabbitMQConfig(MountableFile.forClasspathResource("/rabbitmq-custom.conf")); container.start(); - assertThat(container.getLogs()).contains("config file(s) : /etc/rabbitmq/rabbitmq-custom.conf"); - assertThat(container.getLogs()).doesNotContain(" (not found)"); + assertThat(container.getLogs()).contains("debug"); // config file changes log level to `debug` } } @Test - public void shouldMountConfigurationFileErlang() - { + public void shouldMountConfigurationFileErlang() { try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { - container.withRabbitMQConfigErlang(MountableFile.forClasspathResource("/rabbitmq-custom.config")); container.start(); - assertThat(container.getLogs()).contains("config file(s) : /etc/rabbitmq/rabbitmq-custom.config"); - assertThat(container.getLogs()).doesNotContain(" (not found)"); + assertThat(container.getLogs()).contains("debug"); // config file changes log level to `debug` } } @Test - public void shouldMountConfigurationFileSysctl() - { + public void shouldMountConfigurationFileSysctl() { try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { - container.withRabbitMQConfigSysctl(MountableFile.forClasspathResource("/rabbitmq-custom.conf")); container.start(); - assertThat(container.getLogs()).contains("config file(s) : /etc/rabbitmq/rabbitmq-custom.conf"); - assertThat(container.getLogs()).doesNotContain(" (not found)"); + assertThat(container.getLogs()).contains("debug"); // config file changes log level to `debug` } } diff --git a/modules/rabbitmq/src/test/resources/logback-test.xml b/modules/rabbitmq/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..535e406fc13 --- /dev/null +++ b/modules/rabbitmq/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + diff --git a/modules/rabbitmq/src/test/resources/rabbitmq-custom.conf b/modules/rabbitmq/src/test/resources/rabbitmq-custom.conf index 293829a353b..a2994c78602 100644 --- a/modules/rabbitmq/src/test/resources/rabbitmq-custom.conf +++ b/modules/rabbitmq/src/test/resources/rabbitmq-custom.conf @@ -1,2 +1 @@ -loopback_users.guest = false -listeners.tcp.default = 5555 +log.console.level = debug diff --git a/modules/rabbitmq/src/test/resources/rabbitmq-custom.config b/modules/rabbitmq/src/test/resources/rabbitmq-custom.config index a1c581542ce..353a854fc42 100644 --- a/modules/rabbitmq/src/test/resources/rabbitmq-custom.config +++ b/modules/rabbitmq/src/test/resources/rabbitmq-custom.config @@ -1,13 +1,9 @@ [ -{ rabbit, [ -{ loopback_users, [ ] }, - { tcp_listeners, [ 5555 ] }, - { ssl_listeners, [ ] }, - { default_vhost, <<"vhost">> }, - { hipe_compile, false } - ] }, - { rabbitmq_management, [ { listener, [ - { port, 15672 }, - { ssl, false } - ] } ] } +{rabbit, + [ + {log, + [{console, [{level, debug}]}] + } + ] +} ]. From c7449edda49ff780921067c4a03014ce68b2aa41 Mon Sep 17 00:00:00 2001 From: Alexander Reelsen Date: Fri, 8 Apr 2022 09:16:53 +0200 Subject: [PATCH 2469/5615] Elasticsearch: Ensure Elasticsearch 8 works OOTB secure as default (#5099) Since Elasticsearch 8.0 the default is to enable security, meaning TLS and authentication. This adds a check for Elasticsearch 8.0 to change the default behaviour to properly support this change, but you can still run Elasticsearch with security features disabled, if you want. Co-authored-by: Kevin Wittek Co-authored-by: Sergei Egorov --- .../elasticsearch/ElasticsearchContainer.java | 96 +++++++++++++++---- .../ElasticsearchContainerTest.java | 38 ++++++-- 2 files changed, 111 insertions(+), 23 deletions(-) diff --git a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java index acbca15aaf5..84881b2e87d 100644 --- a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java +++ b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java @@ -1,21 +1,33 @@ package org.testcontainers.elasticsearch; -import static java.net.HttpURLConnection.HTTP_OK; -import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED; - -import java.net.InetSocketAddress; -import java.time.Duration; +import com.github.dockerjava.api.command.InspectContainerResponse; +import org.apache.commons.io.IOUtils; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; +import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.utility.Base58; +import org.testcontainers.utility.ComparableVersion; import org.testcontainers.utility.DockerImageName; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; +import java.io.ByteArrayInputStream; +import java.net.InetSocketAddress; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.util.Optional; + /** * Represents an elasticsearch docker instance which exposes by default port 9200 and 9300 (transport.tcp.port) * The docker image is by default fetched from docker.elastic.co/elasticsearch/elasticsearch */ public class ElasticsearchContainer extends GenericContainer { + /** + * Elasticsearch Default Password for Elasticsearch >= 8 + */ + public static final String ELASTICSEARCH_DEFAULT_PASSWORD = "changeme"; + /** * Elasticsearch Default HTTP port */ @@ -39,7 +51,10 @@ public class ElasticsearchContainer extends GenericContainer caCertAsBytes = Optional.empty(); /** * @deprecated use {@link ElasticsearchContainer(DockerImageName)} instead @@ -65,23 +80,67 @@ public ElasticsearchContainer(final DockerImageName dockerImageName) { super(dockerImageName); dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME, DEFAULT_OSS_IMAGE_NAME); - - if (dockerImageName.isCompatibleWith(DEFAULT_OSS_IMAGE_NAME)) { - this.isOss = true; - } + this.isOss = dockerImageName.isCompatibleWith(DEFAULT_OSS_IMAGE_NAME); logger().info("Starting an elasticsearch container using [{}]", dockerImageName); withNetworkAliases("elasticsearch-" + Base58.randomString(6)); withEnv("discovery.type", "single-node"); addExposedPorts(ELASTICSEARCH_DEFAULT_PORT, ELASTICSEARCH_DEFAULT_TCP_PORT); - setWaitStrategy(new HttpWaitStrategy() - .forPort(ELASTICSEARCH_DEFAULT_PORT) - .forStatusCodeMatching(response -> response == HTTP_OK || response == HTTP_UNAUTHORIZED) - .withStartupTimeout(Duration.ofMinutes(2))); + this.isAtLeastMajorVersion8 = new ComparableVersion(dockerImageName.getVersionPart()).isGreaterThanOrEqualTo("8.0.0"); + // regex that + // matches 8.0 JSON logging with no whitespace between message field and content + // matches 7.x JSON logging with whitespace between message field and content + // matches 6.x text logging with node name in brackets and just a 'started' message till the end of the line + String regex = ".*(\"message\":\\s?\"started\".*|] started\n$)"; + setWaitStrategy(new LogMessageWaitStrategy().withRegEx(regex)); + if (isAtLeastMajorVersion8) { + withPassword(ELASTICSEARCH_DEFAULT_PASSWORD); + } + } + + @Override + protected void containerIsStarted(InspectContainerResponse containerInfo) { + if (isAtLeastMajorVersion8) { + byte[] bytes = copyFileFromContainer("/usr/share/elasticsearch/config/certs/http_ca.crt", IOUtils::toByteArray); + if (bytes.length > 0) { + this.caCertAsBytes = Optional.of(bytes); + } + } + } + + /** + * If this is running above Elasticsearch 8, this will return the probably self-signed CA cert that has been extracted + * + * @return byte array optional containing the CA cert extracted from the docker container + */ + public Optional caCertAsBytes() { + return caCertAsBytes; } /** - * Define the Elasticsearch password to set. It enables security behind the scene. + * A SSL context based on the self signed CA, so that using this SSL Context allows to connect to the Elasticsearch service + * @return a customized SSL Context + */ + public SSLContext createSslContextFromCa() { + try { + CertificateFactory factory = CertificateFactory.getInstance("X.509"); + Certificate trustedCa = factory.generateCertificate(new ByteArrayInputStream(caCertAsBytes.get())); + KeyStore trustStore = KeyStore.getInstance("pkcs12"); + trustStore.load(null, null); + trustStore.setCertificateEntry("ca", trustedCa); + + final SSLContext sslContext = SSLContext.getInstance("TLSv1.3"); + TrustManagerFactory tmfactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmfactory.init(trustStore); + sslContext.init(null, tmfactory.getTrustManagers(), null); + return sslContext; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Define the Elasticsearch password to set. It enables security behind the scene for major version below 8.0.0. * It's not possible to use security with the oss image. * @param password Password to set * @return this @@ -92,7 +151,10 @@ public ElasticsearchContainer withPassword(String password) { "Please switch to the default distribution"); } withEnv("ELASTIC_PASSWORD", password); - withEnv("xpack.security.enabled", "true"); + if (!isAtLeastMajorVersion8) { + // major version 8 is secure by default and does not need this to enable authentication + withEnv("xpack.security.enabled", "true"); + } return this; } diff --git a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java index 859587645d7..956c794135b 100644 --- a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java +++ b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java @@ -1,11 +1,5 @@ package org.testcontainers.elasticsearch; -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; - -import java.io.IOException; import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; @@ -25,6 +19,13 @@ import org.junit.Test; import org.testcontainers.utility.DockerImageName; +import java.io.IOException; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; + public class ElasticsearchContainerTest { /** @@ -249,6 +250,31 @@ public void incompatibleSettingsTest() { ); } + @Test + public void testElasticsearch8SecureByDefault() throws Exception { + try (ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:8.0.0")) { + // Start the container. This step might take some time... + container.start(); + + // Create the secured client. + final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials(AuthScope.ANY, + new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ElasticsearchContainer.ELASTICSEARCH_DEFAULT_PASSWORD)); + + client = RestClient.builder(HttpHost.create("https://" + container.getHttpHostAddress())) + .setHttpClientConfigCallback(httpClientBuilder -> { + httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); + httpClientBuilder.setSSLContext(container.createSslContextFromCa()); + return httpClientBuilder; + }) + .build(); + + Response response = client.performRequest(new Request("GET", "/_cluster/health")); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + assertThat(EntityUtils.toString(response.getEntity()), containsString("cluster_name")); + } + } + private RestClient getClient(ElasticsearchContainer container) { if (client == null) { final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); From d93dceae969b63645b07881e80a61c57e3ca564b Mon Sep 17 00:00:00 2001 From: Thomas Darimont Date: Fri, 8 Apr 2022 16:02:00 +0200 Subject: [PATCH 2470/5615] Allow to create a file in container from a Transferable (#3814) (#3815) Co-authored-by: Alex Stockinger Co-authored-by: Sergei Egorov Co-authored-by: Kevin Wittek --- core/build.gradle | 2 + .../testcontainers/containers/Container.java | 15 +++- .../containers/GenericContainer.java | 53 ++++++----- .../images/builder/Transferable.java | 15 ++++ .../testcontainers/utility/MountableFile.java | 26 ++++++ .../containers/GenericContainerTest.java | 36 ++++++++ .../containers/ReusabilityUnitTests.java | 87 +++++++++++++++---- 7 files changed, 193 insertions(+), 41 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index c17169977cc..3f1ad71731d 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -45,6 +45,8 @@ tasks.japicmp { "org.testcontainers.utility.ResourceReaper#start(com.github.dockerjava.api.DockerClient)", "org.testcontainers.utility.ResourceReaper#registerNetworkForCleanup(java.lang.String)", "org.testcontainers.utility.ResourceReaper#removeNetworks(java.lang.String)", + "org.testcontainers.images.builder.Transferable#of(java.lang.String)", + "org.testcontainers.images.builder.Transferable#updateChecksum(java.util.zip.Checksum)" ] fieldExcludes = [] diff --git a/core/src/main/java/org/testcontainers/containers/Container.java b/core/src/main/java/org/testcontainers/containers/Container.java index d9291fc9fc8..8fd53fc5a0e 100644 --- a/core/src/main/java/org/testcontainers/containers/Container.java +++ b/core/src/main/java/org/testcontainers/containers/Container.java @@ -12,6 +12,7 @@ import org.testcontainers.containers.startupcheck.StartupCheckStrategy; import org.testcontainers.containers.traits.LinkableContainer; import org.testcontainers.containers.wait.strategy.WaitStrategy; +import org.testcontainers.images.builder.Transferable; import org.testcontainers.utility.LogUtils; import org.testcontainers.utility.MountableFile; @@ -169,11 +170,23 @@ default SELF withFileSystemBind(String hostPath, String containerPath) { * Set the file to be copied before starting a created container * * @param mountableFile a Mountable file with path of source file / folder on host machine - * @param containerPath a destination path on conatiner to which the files / folders to be copied + * @param containerPath a destination path on container to which the files / folders to be copied * @return this + * + * @deprecated Use {@link #withCopyToContainer(Transferable, String)} instead */ + @Deprecated SELF withCopyFileToContainer(MountableFile mountableFile, String containerPath); + /** + * Set the content to be copied before starting a created container + * + * @param transferable a Transferable + * @param containerPath a destination path on container to which the files / folders to be copied + * @return this + */ + SELF withCopyToContainer(Transferable transferable, String containerPath); + /** * Add an environment variable to be passed to the container. * diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 70e080711a7..d47bc6cfc5e 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -23,6 +23,7 @@ import com.google.common.hash.Hashing; import lombok.AccessLevel; import lombok.Data; +import lombok.Getter; import lombok.NonNull; import lombok.Setter; import lombok.SneakyThrows; @@ -49,6 +50,7 @@ import org.testcontainers.containers.wait.strategy.WaitStrategyTarget; import org.testcontainers.images.ImagePullPolicy; import org.testcontainers.images.RemoteDockerImage; +import org.testcontainers.images.builder.Transferable; import org.testcontainers.lifecycle.Startable; import org.testcontainers.lifecycle.Startables; import org.testcontainers.lifecycle.TestDescription; @@ -186,8 +188,15 @@ public class GenericContainer> private Long shmSize; // Maintain order in which entries are added, as earlier target location may be a prefix of a later location. + @Deprecated private Map copyToFileContainerPathMap = new LinkedHashMap<>(); + // Maintain order in which entries are added, as earlier target location may be a prefix of a later location. + @Setter(AccessLevel.NONE) + @Getter(AccessLevel.MODULE) + @VisibleForTesting + private Map copyToTransferableContainerPathMap = new LinkedHashMap<>(); + protected final Set dependencies = new HashSet<>(); /** @@ -415,6 +424,8 @@ private void tryStart(Instant startedAt) { // TODO use single "copy" invocation (and calculate an hash of the resulting tar archive) copyToFileContainerPathMap.forEach(this::copyFileToContainer); + + copyToTransferableContainerPathMap.forEach(this::copyFileToContainer); } connectToPortForwardingNetwork(createCommand.getNetworkMode()); @@ -530,33 +541,18 @@ private void tryStart(Instant startedAt) { @VisibleForTesting Checksum hashCopiedFiles() { Checksum checksum = new Adler32(); - copyToFileContainerPathMap.entrySet().stream().sorted(Entry.comparingByValue()).forEach(entry -> { - byte[] pathBytes = entry.getValue().getBytes(); - // Add path to the hash - checksum.update(pathBytes, 0, pathBytes.length); - - File file = new File(entry.getKey().getResolvedPath()); - checksumFile(file, checksum); - }); + Stream.of(copyToFileContainerPathMap, copyToTransferableContainerPathMap) + .flatMap(it -> it.entrySet().stream()) + .sorted(Entry.comparingByValue()).forEach(entry -> { + byte[] pathBytes = entry.getValue().getBytes(); + // Add path to the hash + checksum.update(pathBytes, 0, pathBytes.length); + + entry.getKey().updateChecksum(checksum); + }); return checksum; } - @VisibleForTesting - @SneakyThrows(IOException.class) - void checksumFile(File file, Checksum checksum) { - Path path = file.toPath(); - checksum.update(MountableFile.getUnixFileMode(path)); - if (file.isDirectory()) { - try (Stream stream = Files.walk(path)) { - stream.filter(it -> it != path).forEach(it -> { - checksumFile(it.toFile(), checksum); - }); - } - } else { - FileUtils.checksum(file, checksum); - } - } - @UnstableAPI @SneakyThrows(JsonProcessingException.class) final String hash(CreateContainerCmd createCommand) { @@ -1296,6 +1292,15 @@ public SELF withCopyFileToContainer(MountableFile mountableFile, String containe return self(); } + /** + * {@inheritDoc} + */ + @Override + public SELF withCopyToContainer(Transferable transferable, String containerPath) { + copyToTransferableContainerPathMap.put(transferable, containerPath); + return self(); + } + /** * Get the IP address that this container may be reached on (may not be the local machine). * diff --git a/core/src/main/java/org/testcontainers/images/builder/Transferable.java b/core/src/main/java/org/testcontainers/images/builder/Transferable.java index 3f068f98a18..8ec26c8c3da 100644 --- a/core/src/main/java/org/testcontainers/images/builder/Transferable.java +++ b/core/src/main/java/org/testcontainers/images/builder/Transferable.java @@ -5,12 +5,18 @@ import org.apache.commons.io.IOUtils; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.zip.Checksum; public interface Transferable { int DEFAULT_FILE_MODE = 0100644; int DEFAULT_DIR_MODE = 040755; + static Transferable of(String string) { + return of(string.getBytes(StandardCharsets.UTF_8)); + } + static Transferable of(byte[] bytes) { return of(bytes, DEFAULT_FILE_MODE); } @@ -27,6 +33,11 @@ public byte[] getBytes() { return bytes; } + @Override + public void updateChecksum(Checksum checksum) { + checksum.update(bytes, 0, bytes.length); + } + @Override public int getFileMode() { return fileMode; @@ -78,4 +89,8 @@ default byte[] getBytes() { default String getDescription() { return ""; } + + default void updateChecksum(Checksum checksum) { + throw new UnsupportedOperationException("Provide implementation in subclass"); + } } diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java index 19dca4c134c..81cc37adeab 100644 --- a/core/src/main/java/org/testcontainers/utility/MountableFile.java +++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java @@ -1,12 +1,15 @@ package org.testcontainers.utility; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Charsets; import lombok.Getter; import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.apache.commons.compress.archivers.tar.TarConstants; +import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.SystemUtils; import org.jetbrains.annotations.NotNull; import org.testcontainers.DockerClientFactory; @@ -28,6 +31,8 @@ import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; +import java.util.stream.Stream; +import java.util.zip.Checksum; import static lombok.AccessLevel.PACKAGE; import static org.testcontainers.utility.PathUtils.recursiveDeleteDir; @@ -361,6 +366,27 @@ public String getDescription() { return this.getResolvedPath(); } + @Override + public void updateChecksum(Checksum checksum) { + File file = new File(getResolvedPath()); + checksumFile(file, checksum); + } + + @SneakyThrows(IOException.class) + private void checksumFile(File file, Checksum checksum) { + Path path = file.toPath(); + checksum.update(MountableFile.getUnixFileMode(path)); + if (file.isDirectory()) { + try (Stream stream = Files.walk(path)) { + stream.filter(it -> it != path).forEach(it -> { + checksumFile(it.toFile(), checksum); + }); + } + } else { + FileUtils.checksum(file, checksum); + } + } + @Override public int getFileMode() { return getUnixFileMode(this.getResolvedPath()); diff --git a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java index 93a9cd96479..0c30d522cd9 100644 --- a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java +++ b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java @@ -20,7 +20,11 @@ import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import org.testcontainers.containers.startupcheck.StartupCheckStrategy; import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy; +import org.testcontainers.images.builder.Transferable; + +import java.nio.charset.StandardCharsets; import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.utility.MountableFile; import java.util.Arrays; import java.util.List; @@ -75,6 +79,38 @@ public void shouldReportErrorAfterWait() { } } + @Test + public void shouldCopyTransferableAsFile() { + try ( + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) + .withStartupCheckStrategy(new NoopStartupCheckStrategy()) + .withCopyToContainer(Transferable.of("test"), "/tmp/test") + .waitingFor(new WaitForExitedState(state -> state.getExitCodeLong() > 0)) + .withCommand("sh", "-c", "grep -q test /tmp/test && exit 100") + ) { + assertThatThrownBy(container::start) + .hasStackTraceContaining("Container exited with code 100"); + } + } + + @Test + public void shouldCopyTransferableAfterMountableFile() { + try ( + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) + .withStartupCheckStrategy(new NoopStartupCheckStrategy()) + .withCopyFileToContainer( + MountableFile.forClasspathResource("test_copy_to_container.txt"), + "/tmp/test" + ) + .withCopyToContainer(Transferable.of("test"), "/tmp/test") + .waitingFor(new WaitForExitedState(state -> state.getExitCodeLong() > 0)) + .withCommand("sh", "-c", "grep -q test /tmp/test && exit 100") + ) { + assertThatThrownBy(container::start) + .hasStackTraceContaining("Container exited with code 100"); + } + } + @Test public void shouldOnlyPublishExposedPorts() { ImageFromDockerfile image = new ImageFromDockerfile("publish-multiple") diff --git a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java index 0540fcdba9d..9aad97d7e7b 100644 --- a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java +++ b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java @@ -10,7 +10,6 @@ import com.github.dockerjava.api.command.ListContainersCmd; import com.github.dockerjava.api.command.StartContainerCmd; import com.github.dockerjava.api.model.Container; -import com.github.dockerjava.api.model.NetworkSettings; import com.github.dockerjava.core.command.CreateContainerCmdImpl; import com.github.dockerjava.core.command.InspectContainerCmdImpl; import com.github.dockerjava.core.command.ListContainersCmdImpl; @@ -24,13 +23,13 @@ import org.junit.runners.BlockJUnit4ClassRunner; import org.junit.runners.Parameterized; import org.mockito.Answers; -import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.stubbing.Answer; import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.startupcheck.StartupCheckStrategy; import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy; +import org.testcontainers.images.builder.Transferable; import org.testcontainers.utility.MockTestcontainersConfigurationRule; import org.testcontainers.utility.MountableFile; import org.testcontainers.utility.TestcontainersConfiguration; @@ -39,12 +38,14 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; +import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -297,11 +298,65 @@ public void shouldHashCopiedFiles() { } } - @RunWith(BlockJUnit4ClassRunner.class) + @RunWith(Parameterized.class) @FieldDefaults(makeFinal = true) public static class CopyFilesHashTest { + private final TestStrategy strategy; + + interface TestStrategy { + void withCopyFileToContainer(MountableFile mountableFile, String path); + + void clear(); + } + + private static class MountableFileTestStrategy implements TestStrategy { + private final GenericContainer container; + + private MountableFileTestStrategy(GenericContainer container) { + this.container = container; + } + + @Override + public void withCopyFileToContainer(MountableFile mountableFile, String path) { + container.withCopyFileToContainer(mountableFile, path); + } + + @Override + public void clear() { + container.getCopyToFileContainerPathMap().clear(); + } + } + + private static class TransferableTestStrategy implements TestStrategy { + private final GenericContainer container; + + private TransferableTestStrategy(GenericContainer container) { + this.container = container; + } + + @Override + public void withCopyFileToContainer(MountableFile mountableFile, String path) { + container.withCopyToContainer(mountableFile, path); + } + + @Override + public void clear() { + container.getCopyToTransferableContainerPathMap().clear(); + } + } + + @Parameterized.Parameters + public static List, TestStrategy>> strategies() { + return Arrays.asList(MountableFileTestStrategy::new, TransferableTestStrategy::new); + } + + GenericContainer container = new GenericContainer<>(TINY_IMAGE); + public CopyFilesHashTest(Function, TestStrategy> strategyFactory) { + this.strategy = strategyFactory.apply(container); + } + @Test public void empty() { assertThat(container.hashCopiedFiles()).isNotNull(); @@ -311,7 +366,7 @@ public void empty() { public void oneFile() { long emptyHash = container.hashCopiedFiles().getValue(); - container.withCopyFileToContainer( + strategy.withCopyFileToContainer( MountableFile.forClasspathResource("test_copy_to_container.txt"), "/foo/bar" ); @@ -322,13 +377,13 @@ public void oneFile() { @Test public void differentPath() { MountableFile mountableFile = MountableFile.forClasspathResource("test_copy_to_container.txt"); - container.withCopyFileToContainer(mountableFile, "/foo/bar"); + strategy.withCopyFileToContainer(mountableFile, "/foo/bar"); long hash1 = container.hashCopiedFiles().getValue(); - container.getCopyToFileContainerPathMap().clear(); + strategy.clear(); - container.withCopyFileToContainer(mountableFile, "/foo/baz"); + strategy.withCopyFileToContainer(mountableFile, "/foo/baz"); assertThat(container.hashCopiedFiles().getValue()).isNotEqualTo(hash1); } @@ -337,7 +392,7 @@ public void differentPath() { public void detectsChangesInFile() throws Exception { Path path = File.createTempFile("reusable_test", ".txt").toPath(); MountableFile mountableFile = MountableFile.forHostPath(path); - container.withCopyFileToContainer(mountableFile, "/foo/bar"); + strategy.withCopyFileToContainer(mountableFile, "/foo/bar"); long hash1 = container.hashCopiedFiles().getValue(); @@ -348,13 +403,13 @@ public void detectsChangesInFile() throws Exception { @Test public void multipleFiles() { - container.withCopyFileToContainer( + strategy.withCopyFileToContainer( MountableFile.forClasspathResource("test_copy_to_container.txt"), "/foo/bar" ); long hash1 = container.hashCopiedFiles().getValue(); - container.withCopyFileToContainer( + strategy.withCopyFileToContainer( MountableFile.forClasspathResource("mappable-resource/test-resource.txt"), "/foo/baz" ); @@ -368,7 +423,7 @@ public void folder() throws Exception { Path tempDirectory = Files.createTempDirectory("reusable_test"); MountableFile mountableFile = MountableFile.forHostPath(tempDirectory); - container.withCopyFileToContainer(mountableFile, "/foo/bar/"); + strategy.withCopyFileToContainer(mountableFile, "/foo/bar/"); assertThat(container.hashCopiedFiles().getValue()).isNotEqualTo(emptyHash); } @@ -378,7 +433,7 @@ public void changesInFolder() throws Exception { Path tempDirectory = Files.createTempDirectory("reusable_test"); MountableFile mountableFile = MountableFile.forHostPath(tempDirectory); assertThat(new File(mountableFile.getResolvedPath())).isDirectory(); - container.withCopyFileToContainer(mountableFile, "/foo/bar/"); + strategy.withCopyFileToContainer(mountableFile, "/foo/bar/"); long hash1 = container.hashCopiedFiles().getValue(); @@ -397,11 +452,11 @@ public void folderAndFile() throws Exception { Path tempDirectory = Files.createTempDirectory("reusable_test"); MountableFile mountableFile = MountableFile.forHostPath(tempDirectory); assertThat(new File(mountableFile.getResolvedPath())).isDirectory(); - container.withCopyFileToContainer(mountableFile, "/foo/bar/"); + strategy.withCopyFileToContainer(mountableFile, "/foo/bar/"); long hash1 = container.hashCopiedFiles().getValue(); - container.withCopyFileToContainer( + strategy.withCopyFileToContainer( MountableFile.forClasspathResource("test_copy_to_container.txt"), "/foo/baz" ); @@ -414,7 +469,7 @@ public void filePermissions() throws Exception { Path path = File.createTempFile("reusable_test", ".txt").toPath(); path.toFile().setExecutable(false); MountableFile mountableFile = MountableFile.forHostPath(path); - container.withCopyFileToContainer(mountableFile, "/foo/bar"); + strategy.withCopyFileToContainer(mountableFile, "/foo/bar"); long hash1 = container.hashCopiedFiles().getValue(); @@ -432,7 +487,7 @@ public void folderPermissions() throws Exception { Path subDir = Files.createDirectory(tempDirectory.resolve("sub")); subDir.toFile().setWritable(false); assumeThat(subDir.toFile().canWrite()).isFalse(); - container.withCopyFileToContainer(mountableFile, "/foo/bar/"); + strategy.withCopyFileToContainer(mountableFile, "/foo/bar/"); long hash1 = container.hashCopiedFiles().getValue(); From 17407eff6ef3343ee0c2d52e9306d40708909c43 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Fri, 8 Apr 2022 16:45:17 +0200 Subject: [PATCH 2471/5615] Increase default client ping timeout from 5 to 10 seconds (#5254) --- .../org/testcontainers/utility/TestcontainersConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index c6ce7496dd9..c9dfd543ad6 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -198,7 +198,7 @@ public String getImageSubstitutorClassName() { } public Integer getClientPingTimeout() { - return Integer.parseInt(getEnvVarOrProperty("client.ping.timeout", "5")); + return Integer.parseInt(getEnvVarOrProperty("client.ping.timeout", "10")); } @Nullable From af835694bb617fb1ef8d5eb1aa438ced68bcab7b Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Tue, 12 Apr 2022 19:27:32 +0200 Subject: [PATCH 2472/5615] Update images and theme for testcontainers.org (#5258) --- docs/css/extra.css | 14 +++---- docs/favicon.ico | Bin 15086 -> 15406 bytes docs/logo.png | Bin 51964 -> 84934 bytes docs/logo.svg | 99 +++++++++++++++++++++++++++++++++++++++++---- mkdocs.yml | 7 ++-- 5 files changed, 102 insertions(+), 18 deletions(-) diff --git a/docs/css/extra.css b/docs/css/extra.css index d7e7e6b68a3..4c11e3df748 100644 --- a/docs/css/extra.css +++ b/docs/css/extra.css @@ -1,10 +1,10 @@ -@import url('https://fonts.googleapis.com/css?family=Inconsolata|Lato|Merriweather+Sans|Open+Sans|Source+Sans+Pro'); -body { - font-family: 'Lato', sans-serif; +@import url('https://fonts.googleapis.com/css?family=Rubik'); +h1, h2, h3, h4, h5, h6 { + font-family: 'Rubik', sans-serif; } -.md-typeset pre { - font-family: 'Inconsolata', monospace; - font-size: 100%; -} \ No newline at end of file +[data-md-color-scheme="testcontainers"] { + --md-primary-fg-color: #291A3F; + --md-accent-fg-color: #291A3F; +} diff --git a/docs/favicon.ico b/docs/favicon.ico index 004282385c3e9b51ece573d194b64693145dc2e1..311a0acaa373f197360673f8ac6bc1b3c38c7e7f 100644 GIT binary patch literal 15406 zcmeHO3vg7`8NQ;n1(Fc*fUvtaBoIQ9&1N6>-t|3=PDkqlQE+Rqe*fLv&Aq$#CcA7vI%a3? z+fLGTNLP*M_muNWu@%_#GDI=+_+!jVEj zs6ZVwL5o;9ZfX5OcJBpYQ8^lRD6a$^(s`*i4&=M-XtOmUD*v7A^wGp}AMtb8A#Vv5 zcx_hwOcY9f6c(ksQ1{dNQiblnxs>+)sy}UQ%%QtR52yN}3WY`gCt-BlFb;-KiG* zT?)Vk-ecRCdxei4Uss)kp?PgJ-Fi7}ne7L*7IMgAxvlmaWs~~B#_vWypRxTe8ts$Qn$>}N`F)#76R)YHGPO&%RCR{jJ&-U!;It9!bCtNuboZYO9S#`SHa z*blw?H9hRF8{(_9YL{twz)_V4J6a8TUukrc@5+}UQC@Lfu7_QGkNj!{;T+h^OxWl@ zjPw+mKXxR|y|hBLef>Ac=EIO(ZeSml35c-G`xDUpy}oaLo_Vp0o@g$h!>{$FR`~JN za|cs{r$*I3s{chU0);RB`L!WW*yu4y|5lwY@ z5_NI{^v}{TTq%_OP_OI8`NeebXg`Cj6KQwHza|I1x2(*jd+SQ#7?>7l_pze?^b1Bk zo6$~(K4aLtG`E+2$GGg3|Np=Jb+r!Fk3H?Vr|kdGYgotr(uQ?lWqd7|xQ^9m>hGD- z=RHgJ>)?^!iM*P6_~4Knsk4|Kh6($g?|R`*qlT{KOSAG$KHEGLm};ydpnuy);Vz>U5pRms#iuZG%oCEG@Hr<0b= zb*uYAeLJ4(LZZ?>*5MhENT<7Z>^H9BJ(j*de!lqviPkQ2#r?sWhg>w{JjtkI)C285 z)Y+uVyQ9<9_kd9K1H^W2#oF{=MjVLgjPlaFt7Cg_{WsA4XUHMVu7zE9)pt7I0!7|J z$YVX$(O>In>7UhaqVnkVc7J|4eWz15Z1;E~4&?>bt45ojVK2`G21~AV`pTp(#_J8B z`%Xmkza4b=4D0z!`u)DO*ZY_V;XIJD0?Y@byows1yZ;o^L>H|4d zd6o^%kPm^vsw|GP0rNiC-wACTz(;(BxI=qLl&<5r+Zh7a03%T7E8}+tZ#C8>E0>B zXwS3RbQtlpR>aYscp#VNUsXnvMkdecT0hVs_>dX{HmSI_2j_Rz8|SDZcI6Jp>n)95 z;(bE7L<`54(UvXwYWz`+!vMb)hiiQ#i<;&QqT8=1r}4v**F@Z(s!LJ+BmBT^V+tyV zFzrl!wU7hWj1lzv2K?dYYX6{P#IDfnS|@FOJfHTx+~1hb(&6X*BFD|PEzeQos?&Wg zOPgX^5mEkKxJU||OstpVZUL-k5cAv(yETt%!&aurZd$%Tr2R+CV{&Xin*6o>u6Ez{ za5mi+97FG&J`soHf^S<6S$|+9?8os%oQrhG64T<>FyKNuDSgqc9j5psm5M!eR@o|%R1G#VDPs-nz;lCZW5^Wz4&x@GJxT~^s<*(<7 z^DY|}<-w*n*U;(nSjMU1zpr6+z{He zw_ME+alWUmtsJ=)JNPi3OS8hQm7h-hd}d?si#X4=x6CxJrRJ@oBPjZKQOdeF)%aQ7 zhmrR@@X7#sYJaII?!;V(%16sEimuPY-Yj;$Pm_1z{Z!&tZ5+7UpLHOgR}s%yB;vJt zSI-u1#rR-8uvRm>P8I$`$NN+JbGDANkLl+?HJ;S5u@kvrK5MxqCy9ri#Lx2Pv!452 z%A?&DHm>1H_paJhE5^o#0)tKU6n-9?`(Mt3KO71>$vpRK#h=Ot`&;Itht~i5Ugli6 zn6a%`VNNWM*%bDD7#l5*;LKSD`zg-L<2<~ZI9Hz-+X8+*hn}>5?}(p#tlukZ&z$GJ z`9g_S-IH0~zdIfo`^tsL8J{l^)70o2?#R=d#ZOQsa4a6N<@XRL`o_>EzW+!3>KZR? ze59KA>_c~WQvKc9E4l!1MT*si|5$dw!FP&tfFYoG~ER|2O1Heo<%lx`8`>KGBQ^E(-{b zzI8<&1-X@jma#;{Z>Qo6{?a*a+R>W6E_Up2(gU;HG}Qy#7C9PV|C;lDL7UslF=Z>f zR^{BLj&nq!{5E9pMY1k9uB6bcANy$Cau+csmZ_E9@VAy0C-J$6nHQVqGE{ks$``PK zH^X-Ccu5e>wZfNbIiE2`fA^s8cg=b5(}Z%F7L4=K`iITOIAdSi=%PDr@Y47Z$#xR& z7qWuP?+oTui>cyEr5?xM#};}<;N12`HNOxa)19$Bwu8w|@v90RK_^ zM`QYTc($Mq|9=L&+-sSWG{f#d-qkMMH6~B}f6Zx=xtSicGMq!I0uSww>A%fvipy|) z%sI6{LCr|5-I;WcN;uKL?%|PTO}4dAy9@0q8e+rL6bPyTA+-wt%FVdqusQP zNgdwQLE}3okOFNOA@QB>Y)pu4g_SXlQMDawV;lMdI%wJwC@pk_62bz3B))x~ojWeh z`T3oFNuz2fUEbZj?|Yxm_r2%$y?^0LU!{K`c3pgmFdb#Q(|<37y)Qkp;U z!EsR%91l)JA0i3ilBd-9N%|1(^pt?-agD24C<$ffvhXK$aTRs(n5v?SALlC)7k&=D{brnv-^BW| z|4{g++LOdPonb(B$Rx6EPVja*(moDG`^W} z;D3gZKdFnP#5+ArO{Jf;(+^cuCq?3(UxC}TOn$lErG{DWTi~B+Pq2@*dP-;5;X_AU zxD6ctl8a}yc(K0xbRPWShqKqmJC_&YL-dd^AG}UyHjKDL#ae~jN@ zg(J;Y+BE0g@#Y^+2+cH3R^p_McfT{<{Lxqb*$PLRt+Z+G+vCsw`uv^7jU`TMoZB_t z{G%~X6AL#M8|mqJ$L-)x=G@G`-vs|G3pc9Gzn;7j2rfOY_^Jk_hS|po^}$O26OHp* zsoaLwhS_X(@Bj4)MXI@|NG?^orDqnB-$8%AepulTUR$zpqkivvYAD#Y@`%PcGn98M z?h(DEr}FW^691U6nab0o{CB_b#D|Jp`#~n>QSV~4Dm8xVs8t`V@DC^Wr2f zA<4vp^1Ee8FF(7GjM-O>epulT?)v<{{YCeMfc%4#EVy>s$UDA&K@>{$wVNgWG2TPU zf7jke{|!!8?c`-CFN_ub#<@pGJy_w7GWVwDE(Nv&OI#^f{-#AI(9il<=WlKD(mODJ z*>4xV4fz}n_*@QF{=|-GlCtD_1lS|yVK#8l=X4Bt4Je}P4^2&v1N zvcfC3t>1M-mTQ?chg@}|_+884!?|(rV-RJ&;km~9Cg#e-FD~uY03|K zbGXQSO$(D1>G%HW>9A*NI_xe=hwe$~Fg3^H)Fj7Mko5f=H=Tq%95QzVSnJ?R79#UO!*6k){%D@s;Th#;d1JkoPO8J zQ7cy!7qL%=_|$gl}LSn>vq^ItRr& z&a`^Ixk!s2%Md=V0=9mh2Kv3G6^_XsyXH&urMfA=mC5?ucmw}}QP040r(JvG8g@tA zQGox^Xnb9#@`bkCbxS_@dtTpg zD}0V{UTm_HE(LYho6QSptggu(I z$bZ0yL3@_GWme8{?A@{6@%e1^5sC3XqhXoFN%gaIEsW9*biER1CRb;}A8uprqKrYO#2KvM+&}``VYVSr@r9M zWv|Pv_n*gafPEj6f*(!rKQ;Q6?YV9LZ@UzK&H3x=nd&6=gJbY9sr#vwcV?0cTl<|CR-Zfq!ij2MT^9=2>CH4`MP4oBO$M`(*oEQZk`gd^QvAB6ntPWjdVVB zEx2pNYoqe9f@xywf94*te{`AF{@VV&wi5eTXYIz;^J`grUTU1TB3u7Yz89by<Pav@5 zi)MM|^DRR+p<(x+uX;Eij9h6_x(D_>P`;*pA@t6Dfx!J=&XrH0@ruti6CC1(dhR8A zLWSmQB|XZbNS11TgR$rIJ_bET{mi+qlfCsGSg}AChPcAK7Oxrj+Q?d98sRe z^h8HUrH^?ALg7{aME_yBui@XiuoGo>ndD&I2aR~b__-ZNs{bi3U*ApPU$?`UV7a_&zDTPtTEp97<~SlAyX z>v`;Ju0|fcxQ9>|b@}Yik})IK{wUEllk1@CPUpBD>=<+xaoqEiy9c|(#5P$$G*OQG EAH!e_G5`Po diff --git a/docs/logo.png b/docs/logo.png index 696e03ad6ff360ce03647928910996169e329b4f..3d8f03fa0cf0fb98578e83d9b2ff71de3ead11c0 100644 GIT binary patch literal 84934 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 Gg8vr^t7`)Q diff --git a/docs/logo.svg b/docs/logo.svg index 3a8db707fc8..bac0c391a59 100644 --- a/docs/logo.svg +++ b/docs/logo.svg @@ -1,9 +1,92 @@ - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mkdocs.yml b/mkdocs.yml index 7ace7382e41..16b2106bb3f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -7,9 +7,10 @@ theme: name: 'material' custom_dir: 'docs/theme' palette: - primary: 'blue' - accent: 'teal' - font: false + scheme: testcontainers + font: + text: Roboto + code: Roboto Mono logo: 'logo.svg' favicon: 'favicon.ico' extra_css: From cec49348c70615a3a0b5243d10b8f5c30d3b5697 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Wed, 13 Apr 2022 13:07:28 +0200 Subject: [PATCH 2473/5615] Update release docs to contain milestone rename step --- RELEASING.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RELEASING.md b/RELEASING.md index 4ad91b56477..f04839d9d41 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -16,7 +16,8 @@ Testcontainers' release process is semi-automated through GitHub Actions. This d 7. Close the release in Sonatype. This will evaluate the release based on given Sonatype rules. 8. After successful closing, the release button needs to be clicked and afterwards it is automatically synced to Maven Central. 9. Handcraft and polish some of the release notes (e.g. substitute combinded dependency PRs and highlight certain features). -10. When available through Maven Central, poke [Richard North](https://github.com/rnorth) to announce the release on Twitter! +10. Rename existing milestone corresponding to new release and close it. Then create a new `next` milstestone. +11. When available through Maven Central, poke [Richard North](https://github.com/rnorth) to announce the release on Twitter! ## Internal details From 66e4be2817f82aa80490c21e8df9e8f8a87fa792 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 13 Apr 2022 07:27:38 -0400 Subject: [PATCH 2474/5615] Fix `StackOverflowError` in `DockerClientFactory#client` (#5262) --- core/src/main/java/org/testcontainers/DockerClientFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 4cbe81f9e2d..6cf8f716704 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -187,7 +187,6 @@ public DockerClient client() { final DockerClientProviderStrategy strategy = getOrInitializeStrategy(); - log.info("Docker host IP address is {}", strategy.getDockerHostIpAddress()); client = new DockerClientDelegate() { @Getter @@ -198,6 +197,7 @@ public void close() { throw new IllegalStateException("You should never close the global DockerClient!"); } }; + log.info("Docker host IP address is {}", strategy.getDockerHostIpAddress()); Info dockerInfo = client.infoCmd().exec(); Version version = client.versionCmd().exec(); From 1113cd4cd4a7356c6e745a59c4a6b6711ac55458 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Tue, 19 Apr 2022 18:02:53 -0500 Subject: [PATCH 2475/5615] Rely on `TransportConfig` to read `DOCKER_HOST` env variable for `DockerComposeContainer` (#5276) `TransportConfig` is already aware of `DOCKER_HOST`. --- .../containers/DockerComposeContainer.java | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 4db9361a618..c0f84307c88 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -726,20 +726,17 @@ public void invoke() { final Map environment = Maps.newHashMap(env); environment.put(ENV_PROJECT_NAME, identifier); - String dockerHost = System.getenv("DOCKER_HOST"); - if (dockerHost == null) { - TransportConfig transportConfig = DockerClientFactory.instance().getTransportConfig(); - SSLConfig sslConfig = transportConfig.getSslConfig(); - if (sslConfig != null) { - if (sslConfig instanceof LocalDirectorySSLConfig) { - environment.put("DOCKER_CERT_PATH", ((LocalDirectorySSLConfig) sslConfig).getDockerCertPath()); - environment.put("DOCKER_TLS_VERIFY", "true"); - } else { - logger().warn("Couldn't set DOCKER_CERT_PATH. `sslConfig` is present but it's not LocalDirectorySSLConfig."); - } + TransportConfig transportConfig = DockerClientFactory.instance().getTransportConfig(); + SSLConfig sslConfig = transportConfig.getSslConfig(); + if (sslConfig != null) { + if (sslConfig instanceof LocalDirectorySSLConfig) { + environment.put("DOCKER_CERT_PATH", ((LocalDirectorySSLConfig) sslConfig).getDockerCertPath()); + environment.put("DOCKER_TLS_VERIFY", "true"); + } else { + logger().warn("Couldn't set DOCKER_CERT_PATH. `sslConfig` is present but it's not LocalDirectorySSLConfig."); } - dockerHost = transportConfig.getDockerHost().toString(); } + String dockerHost = transportConfig.getDockerHost().toString(); environment.put("DOCKER_HOST", dockerHost); final Stream absoluteDockerComposeFilePaths = composeFiles.stream() From bb6b5c5489825398461bf9e0e8d180fde9c15eec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Wed, 20 Apr 2022 08:13:04 -0500 Subject: [PATCH 2476/5615] Use `getHost` instead of `getContainerIpAddress()` (#5277) `getContainerIpAddress` is deprecated. --- .../src/test/java/DisqueBackedMailSenderTest.java | 2 +- .../src/test/java/SingleDisqueInstanceTest.java | 2 +- .../java/com/example/linkedcontainer/RedmineContainer.java | 2 +- .../org/testcontainers/containers/MongoDbContainerTest.java | 2 +- .../src/test/java/RedisBackedCacheTest.java | 2 +- .../src/test/java/RedisBackedCacheTest.java | 2 +- .../src/test/java/com/example/BarConcreteTestClass.java | 2 +- .../src/test/java/com/example/FooConcreteTestClass.java | 2 +- .../src/test/java/com/example/SolrQueryTest.java | 2 +- .../testcontainers/containers/DatastoreEmulatorContainer.java | 2 +- .../testcontainers/containers/FirestoreEmulatorContainer.java | 2 +- .../testcontainers/containers/PubSubEmulatorContainer.java | 2 +- .../testcontainers/containers/SpannerEmulatorContainer.java | 4 ++-- .../java/org/testcontainers/containers/MongoDBContainer.java | 2 +- .../org/testcontainers/containers/PostgreSQLContainer.java | 2 +- .../java/org/testcontainers/containers/SolrContainer.java | 4 ++-- .../java/org/testcontainers/containers/SolrContainerTest.java | 4 ++-- 17 files changed, 20 insertions(+), 20 deletions(-) diff --git a/examples/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java b/examples/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java index add59fd15ba..4da6201eecb 100644 --- a/examples/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java +++ b/examples/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java @@ -36,7 +36,7 @@ public class DisqueBackedMailSenderTest { @Before public void setup() { context(""); - disqueClient = new DisqueClient(DisqueURI.create(container.getContainerIpAddress(), container.getMappedPort(7711))); + disqueClient = new DisqueClient(DisqueURI.create(container.getHost(), container.getMappedPort(7711))); mockMailApiClient = mock(MailApiClient.class); service = new DisqueBackedMailSenderService(disqueClient, mockMailApiClient); diff --git a/examples/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java b/examples/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java index 3fa115bea20..7ded5d8da43 100644 --- a/examples/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java +++ b/examples/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java @@ -30,7 +30,7 @@ public class SingleDisqueInstanceTest { @Before public void setup() { context(""); - DisqueClient client = new DisqueClient(DisqueURI.create(container.getContainerIpAddress(), container.getMappedPort(7711))); + DisqueClient client = new DisqueClient(DisqueURI.create(container.getHost(), container.getMappedPort(7711))); connection = client.connect().sync(); retryAfter1Second = AddJobArgs.builder().retry(1, TimeUnit.SECONDS).build(); diff --git a/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java b/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java index a75867205ab..4303bbee682 100644 --- a/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java +++ b/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java @@ -29,7 +29,7 @@ public RedmineContainer withLinkToContainer(LinkableContainer otherContainer, St public String getRedmineUrl() { return String.format("http://%s:%d", - this.getContainerIpAddress(), + this.getHost(), this.getMappedPort(REDMINE_PORT)); } } diff --git a/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java b/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java index 7c87e46bccd..5581c76e675 100644 --- a/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java +++ b/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java @@ -19,7 +19,7 @@ public void containerStartsAndPublicPortIsAvailable() { private void assertThatPortIsAvailable(MongoDbContainer container) { try { - new Socket(container.getContainerIpAddress(), container.getPort()); + new Socket(container.getHost(), container.getPort()); } catch (IOException e) { throw new AssertionError("The expected port " + container.getPort() + " is not available!"); } diff --git a/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java b/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java index 5d204cf99c4..927f097aa5f 100644 --- a/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java +++ b/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java @@ -35,7 +35,7 @@ public static void stopContainer() { @BeforeMethod public void setUp() { - Jedis jedis = new Jedis(redis.getContainerIpAddress(), redis.getMappedPort(6379)); + Jedis jedis = new Jedis(redis.getHost(), redis.getMappedPort(6379)); cache = new RedisBackedCache(jedis, "test"); } diff --git a/examples/redis-backed-cache/src/test/java/RedisBackedCacheTest.java b/examples/redis-backed-cache/src/test/java/RedisBackedCacheTest.java index d9d93b8255e..aa4199684e8 100644 --- a/examples/redis-backed-cache/src/test/java/RedisBackedCacheTest.java +++ b/examples/redis-backed-cache/src/test/java/RedisBackedCacheTest.java @@ -25,7 +25,7 @@ public class RedisBackedCacheTest { @Before public void setUp() throws Exception { - Jedis jedis = new Jedis(redis.getContainerIpAddress(), redis.getMappedPort(6379)); + Jedis jedis = new Jedis(redis.getHost(), redis.getMappedPort(6379)); cache = new RedisBackedCache(jedis, "test"); } diff --git a/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java b/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java index 9923b08f1e0..0f4e6d672d8 100644 --- a/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java +++ b/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java @@ -17,7 +17,7 @@ public class BarConcreteTestClass extends AbstractIntegrationTest { @Before public void setUp() { - Jedis jedis = new Jedis(redis.getContainerIpAddress(), redis.getMappedPort(6379)); + Jedis jedis = new Jedis(redis.getHost(), redis.getMappedPort(6379)); cache = new RedisBackedCache(jedis, "bar"); } diff --git a/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java b/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java index a57b0d5d4a0..3a9e4e84f04 100644 --- a/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java +++ b/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java @@ -17,7 +17,7 @@ public class FooConcreteTestClass extends AbstractIntegrationTest { @Before public void setUp() throws Exception { - Jedis jedis = new Jedis(redis.getContainerIpAddress(), redis.getMappedPort(6379)); + Jedis jedis = new Jedis(redis.getHost(), redis.getMappedPort(6379)); cache = new RedisBackedCache(jedis, "foo"); } diff --git a/examples/solr-container/src/test/java/com/example/SolrQueryTest.java b/examples/solr-container/src/test/java/com/example/SolrQueryTest.java index 63ac67ac1cd..49f8cf99f33 100644 --- a/examples/solr-container/src/test/java/com/example/SolrQueryTest.java +++ b/examples/solr-container/src/test/java/com/example/SolrQueryTest.java @@ -30,7 +30,7 @@ public class SolrQueryTest { @BeforeClass public static void setUp() throws IOException, SolrServerException { solrContainer.start(); - solrClient = new Http2SolrClient.Builder("http://" + solrContainer.getContainerIpAddress() + ":" + solrContainer.getSolrPort() + "/solr").build(); + solrClient = new Http2SolrClient.Builder("http://" + solrContainer.getHost() + ":" + solrContainer.getSolrPort() + "/solr").build(); // Add Sample Data solrClient.add(COLLECTION_NAME, Collections.singletonList( diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java index fe0a9959f2d..58e5c023a0e 100644 --- a/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java @@ -33,6 +33,6 @@ public DatastoreEmulatorContainer(final DockerImageName dockerImageName) { * com.google.cloud.ServiceOptions.Builder#setHost(java.lang.String) method. */ public String getEmulatorEndpoint() { - return getContainerIpAddress() + ":" + getMappedPort(8081); + return getHost() + ":" + getMappedPort(8081); } } diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/FirestoreEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/FirestoreEmulatorContainer.java index 6353a679130..c36082dfeb6 100644 --- a/modules/gcloud/src/main/java/org/testcontainers/containers/FirestoreEmulatorContainer.java +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/FirestoreEmulatorContainer.java @@ -34,6 +34,6 @@ public FirestoreEmulatorContainer(final DockerImageName dockerImageName) { * com.google.cloud.ServiceOptions.Builder#setHost(java.lang.String) method. */ public String getEmulatorEndpoint() { - return getContainerIpAddress() + ":" + getMappedPort(8080); + return getHost() + ":" + getMappedPort(8080); } } diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/PubSubEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/PubSubEmulatorContainer.java index 90d77c837cf..231f7626044 100644 --- a/modules/gcloud/src/main/java/org/testcontainers/containers/PubSubEmulatorContainer.java +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/PubSubEmulatorContainer.java @@ -34,6 +34,6 @@ public PubSubEmulatorContainer(final DockerImageName dockerImageName) { * io.grpc.ManagedChannelBuilder#forTarget(java.lang.String) method. */ public String getEmulatorEndpoint() { - return getContainerIpAddress() + ":" + getMappedPort(PORT); + return getHost() + ":" + getMappedPort(PORT); } } diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/SpannerEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/SpannerEmulatorContainer.java index 8603b1f3437..0ae8e89dc06 100644 --- a/modules/gcloud/src/main/java/org/testcontainers/containers/SpannerEmulatorContainer.java +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/SpannerEmulatorContainer.java @@ -31,7 +31,7 @@ public SpannerEmulatorContainer(final DockerImageName dockerImageName) { * com.google.cloud.spanner.SpannerOptions.Builder#setEmulatorHost(java.lang.String) method. */ public String getEmulatorGrpcEndpoint() { - return getContainerIpAddress() + ":" + getMappedPort(GRPC_PORT); + return getHost() + ":" + getMappedPort(GRPC_PORT); } /** @@ -39,6 +39,6 @@ public String getEmulatorGrpcEndpoint() { * HTTP REST endpoint is reachable from the test host machine. */ public String getEmulatorHttpEndpoint() { - return getContainerIpAddress() + ":" + getMappedPort(HTTP_PORT); + return getHost() + ":" + getMappedPort(HTTP_PORT); } } diff --git a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java index 662c4b575f7..53336d4fbec 100644 --- a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java @@ -69,7 +69,7 @@ public String getReplicaSetUrl(final String databaseName) { } return String.format( "mongodb://%s:%d/%s", - getContainerIpAddress(), + getHost(), getMappedPort(MONGODB_INTERNAL_PORT), databaseName ); diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index 3bfb508aa47..741a8c4139a 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -81,7 +81,7 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { String additionalUrlParams = constructUrlParameters("?", "&"); - return "jdbc:postgresql://" + getContainerIpAddress() + ":" + getMappedPort(POSTGRESQL_PORT) + return "jdbc:postgresql://" + getHost() + ":" + getMappedPort(POSTGRESQL_PORT) + "/" + databaseName + additionalUrlParams; } diff --git a/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java b/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java index 4d0b5cd1ae1..e583c1f54ee 100644 --- a/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java +++ b/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java @@ -136,7 +136,7 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { if (StringUtils.isNotEmpty(configuration.getConfigurationName())) { SolrClientUtils.uploadConfiguration( - getContainerIpAddress(), + getHost(), getSolrPort(), configuration.getConfigurationName(), configuration.getSolrConfiguration(), @@ -144,7 +144,7 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { } SolrClientUtils.createCollection( - getContainerIpAddress(), + getHost(), getSolrPort(), configuration.getCollectionName(), configuration.getConfigurationName()); diff --git a/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java b/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java index c2d19a3e61c..9f82e2c3af9 100644 --- a/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java +++ b/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java @@ -60,7 +60,7 @@ public void solrCloudPingTest() throws IOException, SolrServerException { container.start(); // Do whatever you want with the client ... - SolrClient client = new Http2SolrClient.Builder("http://" + container.getContainerIpAddress() + ":" + container.getSolrPort() + "/solr").build(); + SolrClient client = new Http2SolrClient.Builder("http://" + container.getHost() + ":" + container.getSolrPort() + "/solr").build(); SolrPingResponse response = client.ping("dummy"); // Stop the container. @@ -70,7 +70,7 @@ public void solrCloudPingTest() throws IOException, SolrServerException { private SolrClient getClient(SolrContainer container) { if (client == null) { - client = new Http2SolrClient.Builder("http://" + container.getContainerIpAddress() + ":" + container.getSolrPort() + "/solr").build(); + client = new Http2SolrClient.Builder("http://" + container.getHost() + ":" + container.getSolrPort() + "/solr").build(); } return client; } From 600d235d220d5e507405921b4e241aa73c067a2a Mon Sep 17 00:00:00 2001 From: David Boreham Date: Thu, 21 Apr 2022 06:45:04 -0600 Subject: [PATCH 2477/5615] Use latest version of alpine/socat container image (#5280) --- .../main/java/org/testcontainers/containers/SocatContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/SocatContainer.java b/core/src/main/java/org/testcontainers/containers/SocatContainer.java index 7592d9f4d1f..bc9ced57845 100644 --- a/core/src/main/java/org/testcontainers/containers/SocatContainer.java +++ b/core/src/main/java/org/testcontainers/containers/SocatContainer.java @@ -15,7 +15,7 @@ public class SocatContainer extends GenericContainer { private final Map targets = new HashMap<>(); public SocatContainer() { - this(DockerImageName.parse("alpine/socat:1.7.3.4-r0")); + this(DockerImageName.parse("alpine/socat:1.7.4.3-r0")); } public SocatContainer(final DockerImageName dockerImageName) { From 96601379e0d1e01ab9743566545e8413e7155e81 Mon Sep 17 00:00:00 2001 From: Roman Pylypaka Date: Mon, 25 Apr 2022 08:24:22 +0100 Subject: [PATCH 2478/5615] Fix connection leak in JDBC waitUntilContainerStarted (#5281) Only the Statement was auto-closed, but not the Connection from which the Statement was created. Aside from resources leaking, this can cause problems when database rename transactions are executed by clients. --- .../org/testcontainers/containers/JdbcDatabaseContainer.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index c4bacf3c8ff..5e5342f1ee1 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -1,7 +1,6 @@ package org.testcontainers.containers; import com.github.dockerjava.api.command.InspectContainerResponse; -import java.sql.Statement; import lombok.NonNull; import lombok.SneakyThrows; import org.apache.commons.lang3.StringUtils; @@ -16,6 +15,7 @@ import java.sql.Connection; import java.sql.Driver; import java.sql.SQLException; +import java.sql.Statement; import java.util.HashMap; import java.util.Map; import java.util.Properties; @@ -144,7 +144,8 @@ protected void waitUntilContainerStarted() { if (!isRunning()) { Thread.sleep(100L); } else { - try (Statement statement = createConnection("").createStatement()) { + try (Connection connection = createConnection(""); + Statement statement = connection.createStatement()) { boolean testQuerySucceeded = statement.execute(this.getTestQueryString()); if (testQuerySucceeded) { logger().info("Container is started (JDBC URL: {})", this.getJdbcUrl()); From 42fa611a18d3433b820c60a1274ccc7217026016 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:44 +0000 Subject: [PATCH 2479/5615] Bump mssql-jdbc in /modules/mssqlserver Bumps [mssql-jdbc](https://github.com/Microsoft/mssql-jdbc) from 10.2.0.jre8 to 11.1.1.jre8-preview. - [Release notes](https://github.com/Microsoft/mssql-jdbc/releases) - [Changelog](https://github.com/microsoft/mssql-jdbc/blob/main/CHANGELOG.md) - [Commits](https://github.com/Microsoft/mssql-jdbc/commits) --- updated-dependencies: - dependency-name: com.microsoft.sqlserver:mssql-jdbc dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/mssqlserver/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index 1f754303ae1..dfa15ed1c2a 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'io.r2dbc:r2dbc-mssql:0.8.7.RELEASE' testImplementation project(':jdbc-test') - testImplementation 'com.microsoft.sqlserver:mssql-jdbc:10.2.0.jre8' + testImplementation 'com.microsoft.sqlserver:mssql-jdbc:11.1.1.jre8-preview' testImplementation project(':r2dbc') testImplementation 'io.r2dbc:r2dbc-mssql:0.8.7.RELEASE' From 52cf6a3505e13607a98e512f032a5ea436c2438a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:44 +0000 Subject: [PATCH 2480/5615] Bump postgresql from 42.3.3 to 42.3.4 in /modules/junit-jupiter Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.3 to 42.3.4. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.3...REL42.3.4) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 89882481dc1..009f62a8a16 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'org.assertj:assertj-core:3.22.0' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2' - testRuntimeOnly 'org.postgresql:postgresql:42.3.3' + testRuntimeOnly 'org.postgresql:postgresql:42.3.4' testRuntimeOnly 'mysql:mysql-connector-java:8.0.27' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2' } From a43ff9b9be66b1131638801083f7ca017c71a332 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:44 +0000 Subject: [PATCH 2481/5615] Bump s3 from 2.17.162 to 2.17.181 in /modules/localstack Bumps s3 from 2.17.162 to 2.17.181. --- updated-dependencies: - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index c3f2374e23f..59a5173fe73 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.169' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.150' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.169' - testImplementation 'software.amazon.awssdk:s3:2.17.162' + testImplementation 'software.amazon.awssdk:s3:2.17.181' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 244159750e6ba3da0fd59c61b352ea627915bead Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:44 +0000 Subject: [PATCH 2482/5615] Bump neo4j from 3.5.31 to 3.5.32 in /modules/neo4j Bumps [neo4j](https://github.com/neo4j/neo4j) from 3.5.31 to 3.5.32. - [Release notes](https://github.com/neo4j/neo4j/releases) - [Commits](https://github.com/neo4j/neo4j/compare/3.5.31...3.5.32) --- updated-dependencies: - dependency-name: org.neo4j:neo4j dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 6187d665098..0bdd9ba960c 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -29,7 +29,7 @@ task customNeo4jPluginJar(type: Jar) { test.dependsOn customNeo4jPluginJar dependencies { - customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.31" + customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.32" api project(":testcontainers") From bda535fa419cd6abf7891af00932b58f46b87dc2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:44 +0000 Subject: [PATCH 2483/5615] Bump mysql-connector-java from 8.0.28 to 8.0.29 in /modules/mysql Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.28 to 8.0.29. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.28...8.0.29) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 1796ff17ef0..e23920eaae9 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' testImplementation project(':jdbc-test') - testImplementation 'mysql:mysql-connector-java:8.0.28' + testImplementation 'mysql:mysql-connector-java:8.0.29' testImplementation testFixtures(project(':r2dbc')) testImplementation 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' From 6eddddb462011a1b178726c74798d55c495d498f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:45 +0000 Subject: [PATCH 2484/5615] Bump postgresql from 42.3.3 to 42.3.4 in /modules/postgresql Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.3 to 42.3.4. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.3...REL42.3.4) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 754d5dde289..f2c50c30754 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -11,7 +11,7 @@ dependencies { testImplementation project(':jdbc-test') testImplementation project(':test-support') - testImplementation 'org.postgresql:postgresql:42.3.3' + testImplementation 'org.postgresql:postgresql:42.3.4' testImplementation testFixtures(project(':r2dbc')) testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.11.RELEASE' From 00e366f4eb27425b3e37012f341c55efb828f365 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:46 +0000 Subject: [PATCH 2485/5615] Bump java-client from 3.2.6 to 3.3.0 in /modules/couchbase Bumps [java-client](https://github.com/couchbase/couchbase-jvm-clients) from 3.2.6 to 3.3.0. - [Release notes](https://github.com/couchbase/couchbase-jvm-clients/releases) - [Commits](https://github.com/couchbase/couchbase-jvm-clients/compare/java-client-3.2.6...java-client-3.3.0) --- updated-dependencies: - dependency-name: com.couchbase.client:java-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index a3cb0fcbc70..f1e41fcbe11 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -5,6 +5,6 @@ dependencies { // TODO use JDK's HTTP client and/or Apache HttpClient5 shaded 'com.squareup.okhttp3:okhttp:3.14.9' - testImplementation 'com.couchbase.client:java-client:3.2.6' + testImplementation 'com.couchbase.client:java-client:3.3.0' testImplementation 'org.awaitility:awaitility:4.1.1' } From 6b52e87ca78f61bd996391b4cbafa50917ee25a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:46 +0000 Subject: [PATCH 2486/5615] Bump mysql-connector-java from 8.0.28 to 8.0.29 in /modules/spock Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.28 to 8.0.29. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.28...8.0.29) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index e745bc23ed6..396c0071042 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -16,7 +16,7 @@ dependencies { testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' testRuntimeOnly 'org.postgresql:postgresql:42.3.1' - testRuntimeOnly 'mysql:mysql-connector-java:8.0.28' + testRuntimeOnly 'mysql:mysql-connector-java:8.0.29' testCompileClasspath 'org.jetbrains:annotations:23.0.0' } From 92d1b1b29377a18c8c7652bd39589463b27d8a4c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:47 +0000 Subject: [PATCH 2487/5615] Bump jedis from 4.2.1 to 4.2.2 in /examples Bumps [jedis](https://github.com/redis/jedis) from 4.2.1 to 4.2.2. - [Release notes](https://github.com/redis/jedis/releases) - [Commits](https://github.com/redis/jedis/compare/v4.2.1...v4.2.2) --- updated-dependencies: - dependency-name: redis.clients:jedis dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 9092cc86903..4faead13f3a 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' - implementation 'redis.clients:jedis:4.2.1' + implementation 'redis.clients:jedis:4.2.2' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 2df4ba7e6d6..b0d64f0524b 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' - implementation 'redis.clients:jedis:4.2.1' + implementation 'redis.clients:jedis:4.2.2' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 9a0af108840..2b7c3b7dda4 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { - implementation 'redis.clients:jedis:4.2.1' + implementation 'redis.clients:jedis:4.2.2' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.32' From f4ef7e7d28e6bc8d62c732325fa7ad76c71edfc5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:47 +0000 Subject: [PATCH 2488/5615] Bump testcontainers from 1.16.3 to 1.17.1 in /core Bumps [testcontainers](https://github.com/testcontainers/testcontainers-java) from 1.16.3 to 1.17.1. - [Release notes](https://github.com/testcontainers/testcontainers-java/releases) - [Changelog](https://github.com/testcontainers/testcontainers-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-java/compare/1.16.3...1.17.1) --- updated-dependencies: - dependency-name: org.testcontainers:testcontainers dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 3f1ad71731d..91ee725184f 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -60,7 +60,7 @@ configurations.all { } dependencies { - baseline 'org.testcontainers:testcontainers:1.16.3', { + baseline 'org.testcontainers:testcontainers:1.17.1', { exclude group: "*", module: "*" } From 338e9bde40b3b857b5403c42c59cfb583670ce08 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:47 +0000 Subject: [PATCH 2489/5615] Bump rest-assured from 5.0.0 to 5.0.1 in /modules/vault Bumps [rest-assured](https://github.com/rest-assured/rest-assured) from 5.0.0 to 5.0.1. - [Release notes](https://github.com/rest-assured/rest-assured/releases) - [Changelog](https://github.com/rest-assured/rest-assured/blob/master/changelog.txt) - [Commits](https://github.com/rest-assured/rest-assured/compare/rest-assured-5.0.0...rest-assured-5.0.1) --- updated-dependencies: - dependency-name: io.rest-assured:rest-assured dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index 68daf4ed26f..80b416ce235 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':testcontainers') testImplementation 'com.bettercloud:vault-java-driver:5.1.0' - testImplementation 'io.rest-assured:rest-assured:5.0.0' + testImplementation 'io.rest-assured:rest-assured:5.0.1' testImplementation 'org.assertj:assertj-core:3.22.0' } From c3583f45bb5931e21cff1a51409b82deeddc2a00 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:48 +0000 Subject: [PATCH 2490/5615] Bump mysql-connector-java from 8.0.28 to 8.0.29 in /modules/jdbc-test Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.28 to 8.0.29. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.28...8.0.29) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 5c10742b7f7..d77465c5616 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -16,5 +16,5 @@ dependencies { api 'org.apache.tomcat:tomcat-jdbc:10.0.20' api 'org.vibur:vibur-dbcp:25.0' - api 'mysql:mysql-connector-java:8.0.28' + api 'mysql:mysql-connector-java:8.0.29' } From dd899533f03c4f7b1df821833e3504cf22c6f559 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:49 +0000 Subject: [PATCH 2491/5615] Bump transport from 7.17.2 to 7.17.3 in /modules/elasticsearch Bumps [transport](https://github.com/elastic/elasticsearch) from 7.17.2 to 7.17.3. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Changelog](https://github.com/elastic/elasticsearch/blob/master/CHANGELOG.md) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.17.2...v7.17.3) --- updated-dependencies: - dependency-name: org.elasticsearch.client:transport dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index a8d718af3c4..3d8f7bff12d 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,6 +3,6 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') testImplementation "org.elasticsearch.client:elasticsearch-rest-client:7.16.2" - testImplementation "org.elasticsearch.client:transport:7.17.2" + testImplementation "org.elasticsearch.client:transport:7.17.3" testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From e70ca03da3577b8cefc52d3a06b88c2dcd057276 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:50 +0000 Subject: [PATCH 2492/5615] Bump aws-java-sdk-dynamodb in /modules/dynalite Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.12.169 to 1.12.210. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.169...1.12.210) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-dynamodb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/dynalite/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 2fa5978772e..6dabdec119b 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Dynalite" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.169' - testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.150' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.210' + testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.210' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 7425a05dbec152c741e2e35b16018f08bc998c36 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:51 +0000 Subject: [PATCH 2493/5615] Bump jedis from 4.1.1 to 4.2.2 in /modules/junit-jupiter Bumps [jedis](https://github.com/redis/jedis) from 4.1.1 to 4.2.2. - [Release notes](https://github.com/redis/jedis/releases) - [Commits](https://github.com/redis/jedis/compare/v4.1.1...v4.2.2) --- updated-dependencies: - dependency-name: redis.clients:jedis dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 89882481dc1..2685e7b7e40 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -7,7 +7,7 @@ dependencies { testImplementation project(':mysql') testImplementation project(':postgresql') testImplementation 'com.zaxxer:HikariCP:4.0.3' - testImplementation 'redis.clients:jedis:4.1.1' + testImplementation 'redis.clients:jedis:4.2.2' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' testImplementation ('org.mockito:mockito-core:4.4.0') { exclude(module: 'hamcrest-core') From e62826489090b6485e4668797eda3ffddf8075c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:55 +0000 Subject: [PATCH 2494/5615] Bump jedis from 4.0.1 to 4.2.2 in /core Bumps [jedis](https://github.com/redis/jedis) from 4.0.1 to 4.2.2. - [Release notes](https://github.com/redis/jedis/releases) - [Commits](https://github.com/redis/jedis/compare/v4.0.1...v4.2.2) --- updated-dependencies: - dependency-name: redis.clients:jedis dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 3f1ad71731d..527d8bd6153 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -91,7 +91,7 @@ dependencies { shaded 'org.zeroturnaround:zt-exec:1.12' testImplementation 'org.apache.httpcomponents:httpclient:4.5.9' - testImplementation 'redis.clients:jedis:4.0.1' + testImplementation 'redis.clients:jedis:4.2.2' testImplementation 'com.rabbitmq:amqp-client:5.14.2' testImplementation 'org.mongodb:mongo-java-driver:3.12.10' From 93504fdbef0d27cec7b7810704c99270ecf3e743 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:55 +0000 Subject: [PATCH 2495/5615] Bump toxiproxy-java from 2.1.5 to 2.1.7 in /modules/toxiproxy Bumps [toxiproxy-java](https://github.com/trekawek/toxiproxy-java) from 2.1.5 to 2.1.7. - [Release notes](https://github.com/trekawek/toxiproxy-java/releases) - [Changelog](https://github.com/trekawek/toxiproxy-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/trekawek/toxiproxy-java/compare/toxiproxy-java-2.1.5...toxiproxy-java-2.1.7) --- updated-dependencies: - dependency-name: eu.rekawek.toxiproxy:toxiproxy-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/toxiproxy/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/toxiproxy/build.gradle b/modules/toxiproxy/build.gradle index cad4b6b71f3..a1e5f58551a 100644 --- a/modules/toxiproxy/build.gradle +++ b/modules/toxiproxy/build.gradle @@ -2,7 +2,7 @@ description = "Testcontainers :: Toxiproxy" dependencies { api project(':testcontainers') - api 'eu.rekawek.toxiproxy:toxiproxy-java:2.1.5' + api 'eu.rekawek.toxiproxy:toxiproxy-java:2.1.7' testImplementation 'redis.clients:jedis:3.0.1' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From e54b4a3db1f8de8b80cfb0f3aed494ff21ba900e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:56 +0000 Subject: [PATCH 2496/5615] Bump com.gradle.enterprise.gradle.plugin from 3.8.1 to 3.10 in /examples Bumps com.gradle.enterprise.gradle.plugin from 3.8.1 to 3.10. --- updated-dependencies: - dependency-name: com.gradle.enterprise:com.gradle.enterprise.gradle.plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/settings.gradle b/examples/settings.gradle index 75f739e9005..ceb39498854 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.10.0" - classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.8.1" + classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10" classpath "com.gradle:common-custom-user-data-gradle-plugin:1.6.2" } } From ef3e752393d1720352bd72ad4c0e8b7064f6538f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:59 +0000 Subject: [PATCH 2497/5615] Bump aws-java-sdk-sqs from 1.12.150 to 1.12.210 in /modules/localstack Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.12.150 to 1.12.210. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.150...1.12.210) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-sqs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index c3f2374e23f..704a16204ae 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.191' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.169' - testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.150' + testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.210' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.169' testImplementation 'software.amazon.awssdk:s3:2.17.162' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From dcdad30f9e91cb87ab3c45a73b5fb9e264405399 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:59 +0000 Subject: [PATCH 2498/5615] Bump lombok from 1.18.22 to 1.18.24 in /examples Bumps [lombok](https://github.com/projectlombok/lombok) from 1.18.22 to 1.18.24. - [Release notes](https://github.com/projectlombok/lombok/releases) - [Changelog](https://github.com/projectlombok/lombok/blob/master/doc/changelog.markdown) - [Commits](https://github.com/projectlombok/lombok/compare/v1.18.22...v1.18.24) --- updated-dependencies: - dependency-name: org.projectlombok:lombok dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/disque-job-queue/build.gradle | 4 ++-- examples/kafka-cluster/build.gradle | 2 +- examples/solr-container/build.gradle | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index 2352eace899..bfb89f9cef2 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -7,8 +7,8 @@ repositories { } dependencies { - compileOnly "org.projectlombok:lombok:1.18.22" - annotationProcessor "org.projectlombok:lombok:1.18.20" + compileOnly "org.projectlombok:lombok:1.18.24" + annotationProcessor "org.projectlombok:lombok:1.18.24" implementation 'biz.paluch.redis:spinach:0.3' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index 7ba974d3a6d..ee8035ea51c 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - testCompileOnly "org.projectlombok:lombok:1.18.22" + testCompileOnly "org.projectlombok:lombok:1.18.24" testAnnotationProcessor "org.projectlombok:lombok:1.18.22" testImplementation 'org.testcontainers:kafka' testImplementation 'org.apache.kafka:kafka-clients:3.1.0' diff --git a/examples/solr-container/build.gradle b/examples/solr-container/build.gradle index cec4f59625b..3f3366f9d46 100644 --- a/examples/solr-container/build.gradle +++ b/examples/solr-container/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly "org.projectlombok:lombok:1.18.22" + compileOnly "org.projectlombok:lombok:1.18.24" annotationProcessor "org.projectlombok:lombok:1.18.22" implementation 'org.apache.solr:solr-solrj:8.11.1' From d3350c2e9de0a19d56a8d24e3750167e379d4ff9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:01:06 +0000 Subject: [PATCH 2499/5615] Bump actions/cache from 3.0.1 to 3.0.2 Bumps [actions/cache](https://github.com/actions/cache) from 3.0.1 to 3.0.2. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3.0.1...v3.0.2) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index c1b4ac7148e..90b493df481 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -22,7 +22,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.1 + uses: actions/cache@v3.0.2 continue-on-error: true with: path: ~/.gradle/caches @@ -50,7 +50,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.1 + uses: actions/cache@v3.0.2 continue-on-error: true with: path: ~/.gradle/caches diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16397815990..0c0e5eaf601 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.1 + uses: actions/cache@v3.0.2 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-home-testmatrix-${{ hashFiles('**/*.gradle') }} @@ -56,7 +56,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.1 + uses: actions/cache@v3.0.2 continue-on-error: true with: path: ~/.gradle/caches From 0bce9aac0621ab988552698a8c6111cc99a0e358 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:02:21 +0000 Subject: [PATCH 2500/5615] Bump peter-evans/create-pull-request from 4.0.1 to 4.0.2 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4.0.1 to 4.0.2. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/f1a7646cead32c950d90344a4fb5d4e926972a8f...bd72e1b7922d417764d27d30768117ad7da78a0e) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/update-docs-version.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index 3fbbda7153c..b5d672c092d 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -17,7 +17,7 @@ jobs: sed -i "s/latest_version: .*/latest_version: ${GITHUB_REF##*/}/g" mkdocs.yml git diff - name: Create Pull Request - uses: peter-evans/create-pull-request@f1a7646cead32c950d90344a4fb5d4e926972a8f # v3.10.1 + uses: peter-evans/create-pull-request@bd72e1b7922d417764d27d30768117ad7da78a0e # v3.10.1 with: title: Update docs version to ${GITHUB_REF##*/} body: | From 69c73c20274055ce92f39e3f29ffba41bddccf5e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:02:29 +0000 Subject: [PATCH 2501/5615] Bump org.jetbrains.kotlin.jvm from 1.6.10 to 1.6.21 in /examples Bumps org.jetbrains.kotlin.jvm from 1.6.10 to 1.6.21. --- updated-dependencies: - dependency-name: org.jetbrains.kotlin.jvm dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 1d98c3e3eff..b6dd68b33cd 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,6 +1,6 @@ plugins { id("org.springframework.boot") version "2.6.4" - id("org.jetbrains.kotlin.jvm") version "1.6.10" + id("org.jetbrains.kotlin.jvm") version "1.6.21" id("org.jetbrains.kotlin.plugin.spring") version "1.6.20" } From 9ecd399cb1c23d3130f4613ca6888d3e67dca9ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:03:47 +0000 Subject: [PATCH 2502/5615] Bump trino-jdbc from 375 to 379 in /modules/trino Bumps trino-jdbc from 375 to 379. --- updated-dependencies: - dependency-name: io.trino:trino-jdbc dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/trino/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/trino/build.gradle b/modules/trino/build.gradle index f99951dc7fa..d0b56af633f 100644 --- a/modules/trino/build.gradle +++ b/modules/trino/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'io.trino:trino-jdbc:375' + testImplementation 'io.trino:trino-jdbc:379' compileOnly 'org.jetbrains:annotations:23.0.0' } From b353a96a8ac2c260aa4ba30180e3f1495dc9835a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:03:58 +0000 Subject: [PATCH 2503/5615] Bump mongodb-driver-sync from 4.5.1 to 4.6.0 in /modules/mongodb Bumps [mongodb-driver-sync](https://github.com/mongodb/mongo-java-driver) from 4.5.1 to 4.6.0. - [Release notes](https://github.com/mongodb/mongo-java-driver/releases) - [Commits](https://github.com/mongodb/mongo-java-driver/compare/r4.5.1...r4.6.0) --- updated-dependencies: - dependency-name: org.mongodb:mongodb-driver-sync dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/mongodb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mongodb/build.gradle b/modules/mongodb/build.gradle index 6bca2cb892b..d8ef34fbb40 100644 --- a/modules/mongodb/build.gradle +++ b/modules/mongodb/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: MongoDB" dependencies { api project(':testcontainers') - testImplementation("org.mongodb:mongodb-driver-sync:4.5.1") + testImplementation("org.mongodb:mongodb-driver-sync:4.6.0") } From e3525704d983221c94d7af7a87b3e8452ca81933 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:04:00 +0000 Subject: [PATCH 2504/5615] Bump azure-cosmos from 4.26.0 to 4.29.1 in /modules/azure Bumps [azure-cosmos](https://github.com/Azure/azure-sdk-for-java) from 4.26.0 to 4.29.1. - [Release notes](https://github.com/Azure/azure-sdk-for-java/releases) - [Commits](https://github.com/Azure/azure-sdk-for-java/compare/azure-cosmos_4.26.0...azure-cosmos_4.29.1) --- updated-dependencies: - dependency-name: com.azure:azure-cosmos dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/azure/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle index b4dab636eb9..8ab7612c18b 100644 --- a/modules/azure/build.gradle +++ b/modules/azure/build.gradle @@ -6,5 +6,5 @@ dependencies { shaded 'com.squareup.okhttp3:okhttp:3.14.9' testImplementation 'org.assertj:assertj-core:3.22.0' - testImplementation 'com.azure:azure-cosmos:4.26.0' + testImplementation 'com.azure:azure-cosmos:4.29.1' } From 9d1f038121cb0f8bf42611a9092af1f2b3857748 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:04:05 +0000 Subject: [PATCH 2505/5615] Bump kubernetes-client from 5.12.1 to 5.12.2 in /modules/k3s Bumps [kubernetes-client](https://github.com/fabric8io/kubernetes-client) from 5.12.1 to 5.12.2. - [Release notes](https://github.com/fabric8io/kubernetes-client/releases) - [Changelog](https://github.com/fabric8io/kubernetes-client/blob/master/CHANGELOG.md) - [Commits](https://github.com/fabric8io/kubernetes-client/compare/v5.12.1...v5.12.2) --- updated-dependencies: - dependency-name: io.fabric8:kubernetes-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/k3s/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/k3s/build.gradle b/modules/k3s/build.gradle index a373d8115d7..0ab368d60c2 100644 --- a/modules/k3s/build.gradle +++ b/modules/k3s/build.gradle @@ -8,7 +8,7 @@ dependencies { // Any >2.8 version here is not compatible with jackson-databind 2.8.x. shaded 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.8.8' - testImplementation 'io.fabric8:kubernetes-client:5.12.1' + testImplementation 'io.fabric8:kubernetes-client:5.12.2' testImplementation 'io.kubernetes:client-java:15.0.0' testImplementation 'org.assertj:assertj-core:3.22.0' } From 3cd4f25f6825def6c6dfeeadf40c09fe95be01d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:04:12 +0000 Subject: [PATCH 2506/5615] Bump postgresql from 42.3.3 to 42.3.4 in /modules/cockroachdb Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.3 to 42.3.4. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.3...REL42.3.4) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/cockroachdb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cockroachdb/build.gradle b/modules/cockroachdb/build.gradle index 4d15e4df1c1..ad1007a4744 100644 --- a/modules/cockroachdb/build.gradle +++ b/modules/cockroachdb/build.gradle @@ -4,5 +4,5 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'org.postgresql:postgresql:42.3.3' + testImplementation 'org.postgresql:postgresql:42.3.4' } From 9035c934cd87b8bcb051d4f1533fa0fc1d9fee0c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:04:16 +0000 Subject: [PATCH 2507/5615] Bump google-cloud-pubsub from 1.115.1 to 1.116.4 in /modules/gcloud Bumps [google-cloud-pubsub](https://github.com/googleapis/java-pubsub) from 1.115.1 to 1.116.4. - [Release notes](https://github.com/googleapis/java-pubsub/releases) - [Changelog](https://github.com/googleapis/java-pubsub/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-pubsub/compare/v1.115.1...v1.116.4) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-pubsub dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 6a51c854552..475cfb56c45 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -5,7 +5,7 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-datastore:2.2.2' testImplementation 'com.google.cloud:google-cloud-firestore:3.0.21' - testImplementation 'com.google.cloud:google-cloud-pubsub:1.115.1' + testImplementation 'com.google.cloud:google-cloud-pubsub:1.116.4' testImplementation 'com.google.cloud:google-cloud-spanner:6.20.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.6.1' testImplementation 'org.assertj:assertj-core:3.22.0' From f9abd766159b2d1f595f106fae9fb6325ef0e1b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:04:26 +0000 Subject: [PATCH 2508/5615] Bump reactor-core from 3.4.16 to 3.4.17 in /modules/r2dbc Bumps [reactor-core](https://github.com/reactor/reactor-core) from 3.4.16 to 3.4.17. - [Release notes](https://github.com/reactor/reactor-core/releases) - [Commits](https://github.com/reactor/reactor-core/compare/v3.4.16...v3.4.17) --- updated-dependencies: - dependency-name: io.projectreactor:reactor-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/r2dbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle index d1ddfc0455a..2a4e3958af8 100644 --- a/modules/r2dbc/build.gradle +++ b/modules/r2dbc/build.gradle @@ -15,6 +15,6 @@ dependencies { testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.12.RELEASE' testImplementation project(':postgresql') - testFixturesImplementation 'io.projectreactor:reactor-core:3.4.16' + testFixturesImplementation 'io.projectreactor:reactor-core:3.4.17' testFixturesImplementation 'org.assertj:assertj-core:3.14.0' } From 6a56e04b436b14e406df2ebb2e571dcce87f6bfa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:04:29 +0000 Subject: [PATCH 2509/5615] Bump google-cloud-datastore from 2.2.2 to 2.4.0 in /modules/gcloud Bumps [google-cloud-datastore](https://github.com/googleapis/java-datastore) from 2.2.2 to 2.4.0. - [Release notes](https://github.com/googleapis/java-datastore/releases) - [Changelog](https://github.com/googleapis/java-datastore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-datastore/compare/v2.2.2...v2.4.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-datastore dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 6a51c854552..ab571447bcc 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: GCloud" dependencies { api project(':testcontainers') - testImplementation 'com.google.cloud:google-cloud-datastore:2.2.2' + testImplementation 'com.google.cloud:google-cloud-datastore:2.4.0' testImplementation 'com.google.cloud:google-cloud-firestore:3.0.21' testImplementation 'com.google.cloud:google-cloud-pubsub:1.115.1' testImplementation 'com.google.cloud:google-cloud-spanner:6.20.0' From bf9a26680a6a4a1394cf1fc230895112e0d53e45 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:04:34 +0000 Subject: [PATCH 2510/5615] Bump google-cloud-bigtable from 2.6.1 to 2.6.2 in /modules/gcloud Bumps [google-cloud-bigtable](https://github.com/googleapis/java-bigtable) from 2.6.1 to 2.6.2. - [Release notes](https://github.com/googleapis/java-bigtable/releases) - [Changelog](https://github.com/googleapis/java-bigtable/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-bigtable/compare/v2.6.1...v2.6.2) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-bigtable dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 6a51c854552..2aea93f27e5 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-firestore:3.0.21' testImplementation 'com.google.cloud:google-cloud-pubsub:1.115.1' testImplementation 'com.google.cloud:google-cloud-spanner:6.20.0' - testImplementation 'com.google.cloud:google-cloud-bigtable:2.6.1' + testImplementation 'com.google.cloud:google-cloud-bigtable:2.6.2' testImplementation 'org.assertj:assertj-core:3.22.0' } From f63f9619329a2d6e25c9832c51bba3af06c7c86a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:05:33 +0000 Subject: [PATCH 2511/5615] Bump hivemq-extension-sdk from 4.7.5 to 4.8.0 in /modules/hivemq Bumps [hivemq-extension-sdk](https://github.com/hivemq/hivemq-extension-sdk) from 4.7.5 to 4.8.0. - [Release notes](https://github.com/hivemq/hivemq-extension-sdk/releases) - [Commits](https://github.com/hivemq/hivemq-extension-sdk/compare/4.7.5...4.8.0) --- updated-dependencies: - dependency-name: com.hivemq:hivemq-extension-sdk dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index dfd01981894..bfdfecb1761 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -13,7 +13,7 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") testImplementation(project(":junit-jupiter")) - testImplementation("com.hivemq:hivemq-extension-sdk:4.7.5") + testImplementation("com.hivemq:hivemq-extension-sdk:4.8.0") testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") testImplementation("org.apache.httpcomponents:httpclient:4.5.13") testImplementation("ch.qos.logback:logback-classic:1.2.10") From 9635a350d34035c204e1a2e451c77e617e778055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Prinet?= Date: Mon, 2 May 2022 16:51:33 +0200 Subject: [PATCH 2512/5615] Manage Gradle Enterprise plugins with Dependabot (#5342) * Add dependabot configuration to monitor Gradle plugins * Restrict registries to Gradle Plugin Portal --- .github/dependabot.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5d67f278219..3e2d53fed65 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,10 +1,25 @@ version: 2 +registries: + gradle-plugin-portal: + type: maven-repository + url: https://plugins.gradle.org/m2 + username: dummy # Required by dependabot + password: dummy # Required by dependabot updates: - package-ecosystem: "gradle" directory: "/core" schedule: interval: "monthly" open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/" + allow: + - dependency-name: "com.gradle*" + registries: + - gradle-plugin-portal + schedule: + interval: "monthly" + open-pull-requests-limit: 10 # Explicit entry for each module - package-ecosystem: "gradle" From e21164a893141b5a7be817a4f6c43485a9f930e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 May 2022 20:10:23 +0200 Subject: [PATCH 2513/5615] Bump com.gradle.enterprise.gradle.plugin from 3.9 to 3.10 (#5343) Bumps com.gradle.enterprise.gradle.plugin from 3.9 to 3.10. --- updated-dependencies: - dependency-name: com.gradle.enterprise:com.gradle.enterprise.gradle.plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index 64f2aa5d70a..6b80183bc34 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,7 +5,7 @@ buildscript { } } dependencies { - classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.9" + classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10" classpath "com.gradle:common-custom-user-data-gradle-plugin:1.6.5" } } From 46ba812ab2315626ca3d90c7432a2a0bbb544f5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Fri, 6 May 2022 16:10:29 -0500 Subject: [PATCH 2514/5615] Add testcontainers/java-team in CODEOWNERS (#5353) --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index add6048a437..effc21c4f1e 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -3,7 +3,7 @@ # These owners will be the default reviewers for everything in # the repo. -* @rnorth @bsideup @kiview +* @testcontainers/java-team # The last matching pattern takes the most # precedence. From b3b2a4694f2f919b2d1a7b3fa447e4e03125d56d Mon Sep 17 00:00:00 2001 From: Alexander Reelsen Date: Sun, 8 May 2022 10:02:27 +0200 Subject: [PATCH 2515/5615] Elasticsearch: Don't throw exception on missing CA cert file (#5265) The current check throws an exception if the version is supposed to be greater than 8 and the ca cert file is missing. This posed two problems: 1. A custom docker image might have a 'latest' version, and thus is newer than version 8 2. A user may have configured their own CA in a different path In order to accomodate for this, a missing copy of a the ca crt file will log a warning, but not throw an exception and leave the container unstarted. There is also another public method, allowing to read the cert, called `extractCert`. Closes #5264 --- .../elasticsearch/ElasticsearchContainer.java | 31 +++++++- .../ElasticsearchContainerTest.java | 79 +++++++++++++++---- .../src/test/resources/http_ca.crt | 31 ++++++++ 3 files changed, 123 insertions(+), 18 deletions(-) create mode 100644 modules/elasticsearch/src/test/resources/http_ca.crt diff --git a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java index 84881b2e87d..ac0b367f403 100644 --- a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java +++ b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java @@ -1,7 +1,10 @@ package org.testcontainers.elasticsearch; import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.exception.NotFoundException; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.utility.Base58; @@ -21,6 +24,7 @@ * Represents an elasticsearch docker instance which exposes by default port 9200 and 9300 (transport.tcp.port) * The docker image is by default fetched from docker.elastic.co/elasticsearch/elasticsearch */ +@Slf4j public class ElasticsearchContainer extends GenericContainer { /** @@ -55,6 +59,7 @@ public class ElasticsearchContainer extends GenericContainer caCertAsBytes = Optional.empty(); + private String certPath = "/usr/share/elasticsearch/config/certs/http_ca.crt"; /** * @deprecated use {@link ElasticsearchContainer(DockerImageName)} instead @@ -100,10 +105,17 @@ public ElasticsearchContainer(final DockerImageName dockerImageName) { @Override protected void containerIsStarted(InspectContainerResponse containerInfo) { - if (isAtLeastMajorVersion8) { - byte[] bytes = copyFileFromContainer("/usr/share/elasticsearch/config/certs/http_ca.crt", IOUtils::toByteArray); - if (bytes.length > 0) { - this.caCertAsBytes = Optional.of(bytes); + if (isAtLeastMajorVersion8 && StringUtils.isNotEmpty(certPath)) { + try { + byte[] bytes = copyFileFromContainer(certPath, IOUtils::toByteArray); + if (bytes.length > 0) { + this.caCertAsBytes = Optional.of(bytes); + } + } catch (NotFoundException e) { + // just emit an error message, but do not throw an exception + // this might be ok, if the docker image is accidentally looking like version 8 or latest + // can happen if Elasticsearch is repackaged, i.e. with custom plugins + log.warn("CA cert under " + certPath + " not found."); } } } @@ -158,6 +170,17 @@ public ElasticsearchContainer withPassword(String password) { return this; } + /** + * Configure a CA cert path that is not the default + * + * @param certPath Path to the CA certificate within the Docker container to extract it from after start up + * @return this + */ + public ElasticsearchContainer withCertPath(String certPath) { + this.certPath = certPath; + return this; + } + public String getHttpHostAddress() { return getHost() + ":" + getMappedPort(ELASTICSEARCH_DEFAULT_PORT); } diff --git a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java index 956c794135b..f8131268e61 100644 --- a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java +++ b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java @@ -1,5 +1,6 @@ package org.testcontainers.elasticsearch; +import com.github.dockerjava.api.DockerClient; import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; @@ -17,8 +18,12 @@ import org.elasticsearch.transport.client.PreBuiltTransportClient; import org.junit.After; import org.junit.Test; +import org.testcontainers.DockerClientFactory; +import org.testcontainers.images.RemoteDockerImage; import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; +import javax.net.ssl.SSLHandshakeException; import java.io.IOException; import static org.hamcrest.CoreMatchers.containsString; @@ -252,29 +257,75 @@ public void incompatibleSettingsTest() { @Test public void testElasticsearch8SecureByDefault() throws Exception { - try (ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:8.0.0")) { + try (ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:8.1.2")) { // Start the container. This step might take some time... container.start(); - // Create the secured client. - final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); - credentialsProvider.setCredentials(AuthScope.ANY, - new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ElasticsearchContainer.ELASTICSEARCH_DEFAULT_PASSWORD)); - - client = RestClient.builder(HttpHost.create("https://" + container.getHttpHostAddress())) - .setHttpClientConfigCallback(httpClientBuilder -> { - httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); - httpClientBuilder.setSSLContext(container.createSslContextFromCa()); - return httpClientBuilder; - }) - .build(); + Response response = getClusterHealth(container); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + assertThat(EntityUtils.toString(response.getEntity()), containsString("cluster_name")); + } + } - Response response = client.performRequest(new Request("GET", "/_cluster/health")); + @Test + public void testElasticsearch8SecureByDefaultCustomCaCertFails() throws Exception { + final MountableFile mountableFile = MountableFile.forClasspathResource("http_ca.crt"); + String caPath = "/tmp/http_ca.crt"; + try (ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:8.1.2") + .withCopyToContainer(mountableFile, caPath) + .withCertPath(caPath)) { + + container.start(); + + // this is expected, as a different cert is used for creating the SSL context + assertThrows("PKIX path validation failed: java.security.cert.CertPathValidatorException: Path does not chain with any of the trust anchors", SSLHandshakeException.class, () -> getClusterHealth(container)); + } + } + + @Test + public void testElasticsearch8SecureByDefaultFailsSilentlyOnLatestImages() throws Exception { + // this test exists for custom images by users that use the `latest` tag + // even though the version might be older than version 8 + // this tags an old 7.x version as :latest + tagImage("docker.elastic.co/elasticsearch/elasticsearch:7.9.2", "elasticsearch-tc-older-release", "latest"); + DockerImageName image = DockerImageName.parse("elasticsearch-tc-older-release:latest").asCompatibleSubstituteFor("docker.elastic.co/elasticsearch/elasticsearch"); + + try (ElasticsearchContainer container = new ElasticsearchContainer(image)) { + container.start(); + + Response response = getClient(container).performRequest(new Request("GET", "/_cluster/health")); assertThat(response.getStatusLine().getStatusCode(), is(200)); assertThat(EntityUtils.toString(response.getEntity()), containsString("cluster_name")); } } + private void tagImage(String sourceImage, String targetImage, String targetTag) throws InterruptedException { + DockerClient dockerClient = DockerClientFactory.instance().client(); + dockerClient.tagImageCmd( + new RemoteDockerImage(DockerImageName.parse(sourceImage)).get(), + targetImage, + targetTag + ) + .exec(); + } + + private Response getClusterHealth(ElasticsearchContainer container) throws IOException { + // Create the secured client. + final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials(AuthScope.ANY, + new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ElasticsearchContainer.ELASTICSEARCH_DEFAULT_PASSWORD)); + + client = RestClient.builder(HttpHost.create("https://" + container.getHttpHostAddress())) + .setHttpClientConfigCallback(httpClientBuilder -> { + httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); + httpClientBuilder.setSSLContext(container.createSslContextFromCa()); + return httpClientBuilder; + }) + .build(); + + return client.performRequest(new Request("GET", "/_cluster/health")); + } + private RestClient getClient(ElasticsearchContainer container) { if (client == null) { final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); diff --git a/modules/elasticsearch/src/test/resources/http_ca.crt b/modules/elasticsearch/src/test/resources/http_ca.crt new file mode 100644 index 00000000000..704e5a37aa6 --- /dev/null +++ b/modules/elasticsearch/src/test/resources/http_ca.crt @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIVAPVLz0Dwvzl66ZVpyJY/ntos+qQZMA0GCSqGSIb3DQEB +CwUAMDwxOjA4BgNVBAMTMUVsYXN0aWNzZWFyY2ggc2VjdXJpdHkgYXV0by1jb25m +aWd1cmF0aW9uIEhUVFAgQ0EwHhcNMjIwNDE0MDcyOTA4WhcNMjUwNDEzMDcyOTA4 +WjA8MTowOAYDVQQDEzFFbGFzdGljc2VhcmNoIHNlY3VyaXR5IGF1dG8tY29uZmln +dXJhdGlvbiBIVFRQIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA +ufHmvM04dPKNF6AulX3HrjNfcYy62pBO2oJIKhScetDzuRupv/qiXkO1gzGT3/jk +8uk57rzylDFoCgFNUyWdYQAXD+qsy5vbAytGVNCHOGSuWlNm1bbDYwZNTXjZTK6C +CKIY31lbFn9a4oM+Jp1kvIr8GSMQAYDisq+yrVloDLkRs1SPImnoaXsq8epxloBf +En0vo5V8PtOh+xQFpPP21pd5QohCSB+jMaxaizScWX+k7BijEaS1LCsc2w2PNvwn +/bEvtW7w9w+HnzRGZW2nVlt8eji1PHMmfM5Zugn4HAxsSvdI9VRFGAeT2moNiSLN +zxOWmEvQVl2MxRWiTkM1EM7CFDN40hLHtHej8UddeXTDDXyLoUjY3FmaB45rLKdE +k/rszepc3lmptEMh74iUowKaYZTS5jRqT0yIDzevP5je3JP+pe4aNkx7lWMhTReQ +6fs97nd71PfJBuMcHPEA14zwSzSRb/8mNqqaQLBb5H1DpDcZtzbBxb4wFSAa7Dd0 +pVl4A04iB4PS3DaWg/im2C5a83nUTld7Lvy+I6cO1MTaXdkzn6EWXtxkHj7P4VXX +sFTr+Z2A6g/novqderirQzq8aD87MBp2hLBgG59lVB3IXA+CJTzBRBZipU+FOSs2 +1enMlaEa84d8cb+GSpDkmsvamPMhhLeMw6QLmhQXoWMCAwEAAaNTMFEwHQYDVR0O +BBYEFOt9JC+RiqfFdGf/UT2vfmnV8f+kMB8GA1UdIwQYMBaAFOt9JC+RiqfFdGf/ +UT2vfmnV8f+kMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBALFX +r2CKtu2DcLFQaZft9LeRK67xU0rjN8w1+0MN9otSkU9avd8atPOI9p6r67HZyoTv +LDA47ajitzPo4zAXiy4GUXFVCjx3iPOQ4TTCm0YEf9IudLHLqGTbK+Pup6XlfAMb +RejkztcXkxw3Cy6SjIRq99xU8J6Y1jAggB162hqnp3u42nA1PgOJgo/biUYeVtu7 +Y01gKPdCEkiqmSFxfLiRPv5Z4WyYoKge6UyDYFHu0zyMY7zQ2hzrPMFsEhI4+g5D +W7ihilcOijhvfeeWIxcP5lRn2pGbf2GtwqtA7Bt9YKp+NQIBPzK7D1ymS0v/CAWh +3Bv1rqkqDK8TlBybZitTTB6MgGtXOBccouTPmBFBXSWydvW4GW6Dag/ogHHE2vG2 +xlXY6EC1QEzExcM5FZNJI6SOaK0nl+WKAv060U/1ZqcRIkhyctYdkrK4449n1JMy +wjtwcDW7QxhQspHp8GEXztLctokqGjnuMcgPjVoFdiF3w/IV0UUvVeFK4Oms0YbH +uFr3q44Fu/Fol68/1CUk1ytgLUS5anf0Q0WlJsmMUX156ATA29dVBfloJN63EYd7 +01uwbjoMJce7MiwTaLIetW75fxxZHlQK9TMNhaQwKUO8SRaNuE4wKURFoKPg/Dqu +yPhx9adseStlJ3oV6ziEWMwjOK2JmJf0bmIqQ7KR +-----END CERTIFICATE----- From 84a635efad8a2dc1726ee263950e013de9b51e58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Mon, 9 May 2022 09:11:20 -0500 Subject: [PATCH 2516/5615] Fix docs: use getHost (#5288) --- .../src/test/java/com/example/AbstractIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java b/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java index b6859563c69..dd1b7b6fb5a 100644 --- a/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java +++ b/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java @@ -23,7 +23,7 @@ public abstract class AbstractIntegrationTest { @DynamicPropertySource static void redisProperties(DynamicPropertyRegistry registry) { redis.start(); - registry.add("spring.redis.host", redis::getContainerIpAddress); + registry.add("spring.redis.host", redis::getHost); registry.add("spring.redis.port", redis::getFirstMappedPort); } } From 0b03b91c22fe79af25c8576c5c13195b76f3f619 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 9 May 2022 20:40:29 -0400 Subject: [PATCH 2517/5615] Fix deadlock in `LocalImagesCache` (#5356) Fixes #5282 --- .../java/org/testcontainers/images/LocalImagesCache.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/testcontainers/images/LocalImagesCache.java b/core/src/main/java/org/testcontainers/images/LocalImagesCache.java index ec38721a229..8791ecb6986 100644 --- a/core/src/main/java/org/testcontainers/images/LocalImagesCache.java +++ b/core/src/main/java/org/testcontainers/images/LocalImagesCache.java @@ -27,15 +27,14 @@ enum LocalImagesCache { @VisibleForTesting final Map cache = new ConcurrentHashMap<>(); - DockerClient dockerClient = DockerClientFactory.lazyClient(); - public ImageData get(DockerImageName imageName) { - maybeInitCache(); + maybeInitCache(DockerClientFactory.instance().client()); return cache.get(imageName); } public Optional refreshCache(DockerImageName imageName) { - if (!maybeInitCache()) { + DockerClient dockerClient = DockerClientFactory.instance().client(); + if (!maybeInitCache(dockerClient)) { // Cache may be stale, trying inspectImageCmd... InspectImageResponse response = null; @@ -57,7 +56,7 @@ public Optional refreshCache(DockerImageName imageName) { return Optional.ofNullable(cache.get(imageName)); } - private synchronized boolean maybeInitCache() { + private synchronized boolean maybeInitCache(DockerClient dockerClient) { if (!initialized.compareAndSet(false, true)) { return false; } From 3164d4ec1081a8f43982dcadc9db887ea6ac79b3 Mon Sep 17 00:00:00 2001 From: Felix Seifert Date: Tue, 10 May 2022 02:40:56 +0200 Subject: [PATCH 2518/5615] Correct usage of deprecated method `createSubscription()` (#5350) Call non-deprecated `createSubscription` method in `PubSubEmulatorTest` --- .../testcontainers/containers/PubSubEmulatorContainerTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java index 8db49eb3649..61d56a279d7 100644 --- a/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java @@ -20,6 +20,7 @@ import com.google.pubsub.v1.PullRequest; import com.google.pubsub.v1.PullResponse; import com.google.pubsub.v1.PushConfig; +import com.google.pubsub.v1.SubscriptionName; import com.google.pubsub.v1.TopicName; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; @@ -104,7 +105,7 @@ private void createSubscription(String subscriptionId, String topicId, Transport .setCredentialsProvider(credentialsProvider) .build(); SubscriptionAdminClient subscriptionAdminClient = SubscriptionAdminClient.create(subscriptionAdminSettings); - ProjectSubscriptionName subscriptionName = ProjectSubscriptionName.of(PROJECT_ID, subscriptionId); + SubscriptionName subscriptionName = SubscriptionName.of(PROJECT_ID, subscriptionId); subscriptionAdminClient.createSubscription(subscriptionName, TopicName.of(PROJECT_ID, topicId), PushConfig.getDefaultInstance(), 10); } // } From d4e7671a09f6312f96044f937f2d0973078860e3 Mon Sep 17 00:00:00 2001 From: Matthew de Detrich Date: Tue, 10 May 2022 08:11:31 +0200 Subject: [PATCH 2519/5615] Add getConnectionString() for MongoDBContainer (#5271) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Eddú Meléndez Gonzales Co-authored-by: Kevin Wittek --- .../containers/MongoDBContainer.java | 20 +++++++++++++------ .../containers/MongoDBContainerTest.java | 4 ++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java index 53336d4fbec..87db80846a8 100644 --- a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java @@ -48,6 +48,19 @@ public MongoDBContainer(final DockerImageName dockerImageName) { ); } + /** + * Gets a connection string url, unlike {@link #getReplicaSetUrl} this does point to a + * database + * @return a connection url pointing to a mongodb instance + */ + public String getConnectionString() { + return String.format( + "mongodb://%s:%d", + getHost(), + getMappedPort(MONGODB_INTERNAL_PORT) + ); + } + /** * Gets a replica set url for the default {@value #MONGODB_DATABASE_NAME_DEFAULT} database. * @@ -67,12 +80,7 @@ public String getReplicaSetUrl(final String databaseName) { if (!isRunning()) { throw new IllegalStateException("MongoDBContainer should be started first"); } - return String.format( - "mongodb://%s:%d/%s", - getHost(), - getMappedPort(MONGODB_INTERNAL_PORT), - databaseName - ); + return getConnectionString() + "/" + databaseName; } @Override diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java index ff5243acea7..532a86208c1 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java @@ -38,11 +38,15 @@ public void shouldExecuteTransactions() { final String mongoRsUrl = mongoDBContainer.getReplicaSetUrl(); assertNotNull(mongoRsUrl); + final String connectionString = mongoDBContainer.getConnectionString(); + final MongoClient mongoSyncClientBase = MongoClients.create(connectionString); final MongoClient mongoSyncClient = MongoClients.create(mongoRsUrl); mongoSyncClient.getDatabase("mydb1").getCollection("foo") .withWriteConcern(WriteConcern.MAJORITY).insertOne(new Document("abc", 0)); mongoSyncClient.getDatabase("mydb2").getCollection("bar") .withWriteConcern(WriteConcern.MAJORITY).insertOne(new Document("xyz", 0)); + mongoSyncClientBase.getDatabase("mydb3").getCollection("baz") + .withWriteConcern(WriteConcern.MAJORITY).insertOne(new Document("def", 0)); final ClientSession clientSession = mongoSyncClient.startSession(); final TransactionOptions txnOptions = TransactionOptions.builder() From 87126060b0e76ace2b4b9b344d92908fe4fb7cf5 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 10 May 2022 10:19:57 -0400 Subject: [PATCH 2520/5615] Test `DOCKER_HOST` in `DockerClientProviderStrategy` (#5357) * Test `DOCKER_HOST` in `DockerClientProviderStrategy` * Update DockerClientProviderStrategy.java --- .../DockerClientProviderStrategy.java | 101 +++++++++++++++--- 1 file changed, 88 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index 2ae066d897d..cf091e94328 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -7,6 +7,9 @@ import com.github.dockerjava.core.DockerClientImpl; import com.github.dockerjava.core.RemoteApiVersion; import com.github.dockerjava.transport.DockerHttpClient; +import com.github.dockerjava.transport.NamedPipeSocket; +import com.github.dockerjava.transport.SSLConfig; +import com.github.dockerjava.transport.UnixSocket; import com.github.dockerjava.zerodep.ZerodepDockerHttpClient; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Throwables; @@ -14,15 +17,28 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; +import org.awaitility.Awaitility; import org.jetbrains.annotations.Nullable; import org.rnorth.ducttape.TimeoutException; import org.rnorth.ducttape.ratelimits.RateLimiter; import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; import org.rnorth.ducttape.unreliables.Unreliables; import org.testcontainers.DockerClientFactory; +import org.testcontainers.UnstableAPI; import org.testcontainers.utility.TestcontainersConfiguration; +import javax.net.SocketFactory; +import java.io.File; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketTimeoutException; import java.net.URI; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.time.Duration; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -31,6 +47,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; @@ -104,6 +121,71 @@ public DockerClient getClient() { return dockerClient; } + /** + * TODO we should consider moving this to docker-java at some point + */ + @UnstableAPI + protected boolean test() { + TransportConfig transportConfig = getTransportConfig(); + URI dockerHost = transportConfig.getDockerHost(); + + Callable socketProvider; + SocketAddress socketAddress; + switch (dockerHost.getScheme()) { + case "tcp": + case "http": + case "https": + SocketFactory socketFactory = SocketFactory.getDefault(); + SSLConfig sslConfig = transportConfig.getSslConfig(); + if (sslConfig != null) { + try { + socketFactory = sslConfig.getSSLContext().getSocketFactory(); + } catch (KeyManagementException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException e) { + log.warn("Exception while creating SSLSocketFactory", e); + return false; + } + } + socketProvider = socketFactory::createSocket; + socketAddress = new InetSocketAddress(dockerHost.getHost(), dockerHost.getPort()); + break; + case "unix": + case "npipe": + if (!new File(dockerHost.getPath()).exists()) { + log.debug("DOCKER_HOST socket file '{}' does not exist", dockerHost.getPath()); + return false; + } + socketProvider = () -> { + switch (dockerHost.getScheme()) { + case "unix": + return UnixSocket.get(dockerHost.getPath()); + case "npipe": + return new NamedPipeSocket(dockerHost.getPath()); + default: + throw new IllegalStateException("Unexpected scheme " + dockerHost.getScheme()); + } + }; + socketAddress = new InetSocketAddress("localhost", 2375); + break; + default: + log.warn("Unknown DOCKER_HOST scheme {}, skipping the strategy test...", dockerHost.getScheme()); + return true; + } + + try (Socket socket = socketProvider.call()) { + Duration timeout = Duration.ofMillis(200); + Awaitility.await() + .atMost(TestcontainersConfiguration.getInstance().getClientPingTimeout(), TimeUnit.SECONDS) + .pollInterval(timeout) + .pollDelay(Duration.ofSeconds(0)) // start checking immediately + .ignoreExceptionsInstanceOf(SocketTimeoutException.class) + .untilAsserted(() -> socket.connect(socketAddress, (int) timeout.toMillis())); + return true; + } catch (Exception e) { + log.warn("DOCKER_HOST {} is not listening", dockerHost); + return false; + } + } + /** * Determine the right DockerClientConfig to use for building clients by trial-and-error. * @@ -161,20 +243,13 @@ public boolean test(DockerClientProviderStrategy dockerClientProviderStrategy) { private static boolean tryOutStrategy(List configurationFailures, DockerClientProviderStrategy strategy) { try { log.debug("Trying out strategy: {}", strategy.getClass().getSimpleName()); - DockerClient dockerClient = strategy.getDockerClient(); - - Info info; - try { - info = Unreliables.retryUntilSuccess(TestcontainersConfiguration.getInstance().getClientPingTimeout(), TimeUnit.SECONDS, () -> { - return strategy.PING_RATE_LIMITER.getWhenReady(() -> { - log.debug("Pinging docker daemon..."); - return dockerClient.infoCmd().exec(); - }); - }); - } catch (TimeoutException e) { - IOUtils.closeQuietly(dockerClient); - throw e; + + if (!strategy.test()) { + log.debug("strategy {} did not pass the test", strategy.getClass().getSimpleName()); + return false; } + + Info info = strategy.getDockerClient().infoCmd().exec(); log.info("Found Docker environment with {}", strategy.getDescription()); log.debug( "Transport type: '{}', Docker host: '{}'", From 7d54ff90a4e9514ded0ea0a09d234ae5d76cc10f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Wed, 11 May 2022 13:22:18 -0500 Subject: [PATCH 2521/5615] Allow to create exchanges, queues, bindings in the same vhost (#5348) --- .../containers/RabbitMQContainer.java | 39 +++++++++++++++++++ .../containers/RabbitMQContainerTest.java | 16 +++++--- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java index 37dc2fbe7ca..f5c63a267a9 100644 --- a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java +++ b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java @@ -220,8 +220,25 @@ public RabbitMQContainer withBinding(String source, String destination) { return self(); } + public RabbitMQContainer withBinding(String vhost, String source, String destination) { + values.add(asList("rabbitmqadmin", "--vhost=" + vhost, "declare", "binding", + "source=" + source, + "destination=" + destination)); + return self(); + } + public RabbitMQContainer withBinding(String source, String destination, Map arguments, String routingKey, String destinationType) { values.add(asList("rabbitmqadmin", "declare", "binding", + "source=" + source, + "destination=" + destination, + "routing_key=" + routingKey, + "destination_type=" + destinationType, + "arguments=" + toJson(arguments))); + return self(); + } + + public RabbitMQContainer withBinding(String vhost, String source, String destination, Map arguments, String routingKey, String destinationType) { + values.add(asList("rabbitmqadmin", "--vhost=" + vhost, "declare", "binding", "source=" + source, "destination=" + destination, "routing_key=" + routingKey, @@ -332,6 +349,12 @@ public RabbitMQContainer withVhostLimit(String vhost, String name, int value) { public RabbitMQContainer withQueue(String name) { values.add(asList("rabbitmqadmin", "declare", "queue", + "name=" + name)); + return self(); + } + + public RabbitMQContainer withQueue(String vhost, String name) { + values.add(asList("rabbitmqadmin", "--vhost=" + vhost, "declare", "queue", "name=" + name)); return self(); } @@ -345,6 +368,15 @@ public RabbitMQContainer withQueue(String name, boolean autoDelete, boolean dura return self(); } + public RabbitMQContainer withQueue(String vhost, String name, boolean autoDelete, boolean durable, Map arguments) { + values.add(asList("rabbitmqadmin", "--vhost=" + vhost, "declare", "queue", + "name=" + name, + "auto_delete=" + autoDelete, + "durable=" + durable, + "arguments=" + toJson(arguments))); + return self(); + } + public RabbitMQContainer withExchange(String name, String type) { values.add(asList("rabbitmqadmin", "declare", "exchange", "name=" + name, @@ -352,6 +384,13 @@ public RabbitMQContainer withExchange(String name, String type) { return self(); } + public RabbitMQContainer withExchange(String vhost, String name, String type) { + values.add(asList("rabbitmqadmin", "--vhost=" + vhost, "declare", "exchange", + "name=" + name, + "type=" + type)); + return self(); + } + public RabbitMQContainer withExchange(String name, String type, boolean autoDelete, boolean internal, boolean durable, Map arguments) { values.add(asList("rabbitmqadmin", "declare", "exchange", "name=" + name, diff --git a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java index 474d613cb8b..d82051a94db 100644 --- a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java +++ b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java @@ -14,7 +14,6 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStream; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; @@ -22,8 +21,6 @@ import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.util.Collections; -import java.util.Objects; -import java.util.Scanner; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; @@ -159,9 +156,16 @@ public void shouldStartTheWholeEnchilada() throws IOException, InterruptedExcept .withVhost("vhost2", true) .withExchange("direct-exchange", "direct") .withExchange("topic-exchange", "topic") + .withExchange("vhost1", "topic-exchange-2", "topic", false, false, true, Collections.emptyMap()) + .withExchange("vhost2", "topic-exchange-3", "topic") + .withExchange("topic-exchange-4", "topic", false, false, true, Collections.emptyMap()) .withQueue("queue1") .withQueue("queue2", true, false, ImmutableMap.of("x-message-ttl", 1000)) + .withQueue("vhost1", "queue3", true, false, ImmutableMap.of("x-message-ttl", 1000)) + .withQueue("vhost2", "queue4") .withBinding("direct-exchange", "queue1") + .withBinding("vhost1", "topic-exchange-2", "queue3") + .withBinding("vhost2", "topic-exchange-3", "queue4", Collections.emptyMap(), "ss7", "queue") .withUser("user1", "password1") .withUser("user2", "password2", ImmutableSet.of("administrator")) .withPermission("vhost1", "user1", ".*", ".*", ".*") @@ -175,15 +179,15 @@ public void shouldStartTheWholeEnchilada() throws IOException, InterruptedExcept assertThat(container.execInContainer("rabbitmqadmin", "list", "queues") .getStdout()) - .contains("queue1", "queue2"); + .contains("queue1", "queue2", "queue3", "queue4"); assertThat(container.execInContainer("rabbitmqadmin", "list", "exchanges") .getStdout()) - .contains("direct-exchange", "topic-exchange"); + .contains("direct-exchange", "topic-exchange", "topic-exchange-2", "topic-exchange-3", "topic-exchange-4"); assertThat(container.execInContainer("rabbitmqadmin", "list", "bindings") .getStdout()) - .contains("direct-exchange"); + .contains("direct-exchange", "topic-exchange-2", "topic-exchange-3"); assertThat(container.execInContainer("rabbitmqadmin", "list", "users") .getStdout()) From f41bdb2cda1b45e86db07507b18e8acbf8641cc6 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 13 May 2022 21:05:43 -0400 Subject: [PATCH 2522/5615] Read `indexServerAddress` from Docker's `/info` (#5347) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Read `indexServerAddress` from Docker's `/info` * remove `setIndexServerAddress` * Update fallback default registry name to `https://index.docker.io/v1/` * Fix build Co-authored-by: Kevin Wittek Co-authored-by: Eddú Meléndez Gonzales --- .../java/org/testcontainers/DockerClientFactory.java | 7 ++++++- .../dockerclient/DockerClientProviderStrategy.java | 7 +++++-- .../testcontainers/utility/RegistryAuthLocator.java | 12 ++++++++++-- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 6cf8f716704..cc85645b2b4 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -199,7 +199,7 @@ public void close() { }; log.info("Docker host IP address is {}", strategy.getDockerHostIpAddress()); - Info dockerInfo = client.infoCmd().exec(); + Info dockerInfo = strategy.getInfo(); Version version = client.versionCmd().exec(); activeApiVersion = version.getApiVersion(); activeExecutionDriver = dockerInfo.getExecutionDriver(); @@ -434,4 +434,9 @@ public String getActiveExecutionDriver() { public boolean isUsing(Class providerStrategyClass) { return strategy != null && providerStrategyClass.isAssignableFrom(this.strategy.getClass()); } + + @UnstableAPI + public Info getInfo() { + return getOrInitializeStrategy().getInfo(); + } } diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index cf091e94328..834e670438a 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -65,6 +65,9 @@ public abstract class DockerClientProviderStrategy { private String dockerHostIpAddress; + @Getter + private Info info; + private final RateLimiter PING_RATE_LIMITER = RateLimiterBuilder.newBuilder() .withRate(10, TimeUnit.SECONDS) .withConstantThroughput() @@ -249,7 +252,7 @@ private static boolean tryOutStrategy(List configurationFailures, Docker return false; } - Info info = strategy.getDockerClient().infoCmd().exec(); + strategy.info = strategy.getDockerClient().infoCmd().exec(); log.info("Found Docker environment with {}", strategy.getDescription()); log.debug( "Transport type: '{}', Docker host: '{}'", @@ -258,7 +261,7 @@ private static boolean tryOutStrategy(List configurationFailures, Docker ); log.debug("Checking Docker OS type for {}", strategy.getDescription()); - String osType = info.getOsType(); + String osType = strategy.getInfo().getOsType(); if (StringUtils.isBlank(osType)) { log.warn("Could not determine Docker OS type"); } else if (!osType.equals("linux")) { diff --git a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java index d88bdccd76a..e487bda686b 100644 --- a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java +++ b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java @@ -7,6 +7,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.SystemUtils; import org.slf4j.Logger; +import org.testcontainers.DockerClientFactory; import org.zeroturnaround.exec.InvalidResultException; import org.zeroturnaround.exec.ProcessExecutor; @@ -32,7 +33,7 @@ public class RegistryAuthLocator { private static final Logger log = getLogger(RegistryAuthLocator.class); - private static final String DEFAULT_REGISTRY_NAME = "index.docker.io"; + private static final String DEFAULT_REGISTRY_NAME = "https://index.docker.io/v1/"; private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static RegistryAuthLocator instance; @@ -282,7 +283,14 @@ private String getCredentialProgramName(String credHelper) { } private String effectiveRegistryName(DockerImageName dockerImageName) { - return StringUtils.defaultIfEmpty(dockerImageName.getRegistry(), DEFAULT_REGISTRY_NAME); + final String registry = dockerImageName.getRegistry(); + if (!StringUtils.isEmpty(registry)) { + return registry; + } + return StringUtils.defaultString( + DockerClientFactory.instance().getInfo().getIndexServerAddress(), + DEFAULT_REGISTRY_NAME + ); } private String getGenericCredentialsNotFoundMsg(String credentialHelperName) { From bf70b4093fd31850fe58d6d29af2039c1026ce23 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 15 May 2022 11:00:06 -0400 Subject: [PATCH 2523/5615] run `core` tests in parallel (#5365) * run `core` tests in parallel * ooops * Dummy change * dummy change (2) --- core/build.gradle | 2 + .../DockerClientFactoryTest.java | 38 +++++------- .../DockerRegistryContainer.java | 36 +++++++++++ .../dockerclient/AmbiguousImagePullTest.java | 31 ++++------ .../images/ImagePullPolicyTest.java | 59 +++--------------- .../utility/AuthenticatedImagePullTest.java | 62 +++---------------- 6 files changed, 79 insertions(+), 149 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index 3f1ad71731d..b519673de4b 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -6,6 +6,8 @@ sourceSets { jarFileTest } +test.maxParallelForks = 4 + idea.module.testSourceDirs += sourceSets.jarFileTest.allSource.srcDirs shadowJar { diff --git a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java index 4dcfc26349f..247a16a76a6 100644 --- a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java +++ b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java @@ -1,18 +1,16 @@ package org.testcontainers; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import com.github.dockerjava.api.exception.NotFoundException; import org.junit.Rule; import org.junit.Test; import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.DockerClientFactory.DiskSpaceUsage; import org.testcontainers.dockerclient.LogToStringContainerCallback; -import org.testcontainers.images.LocalImagesCacheAccessor; import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MockTestcontainersConfigurationRule; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + /** * Test for {@link DockerClientFactory}. */ @@ -23,30 +21,22 @@ public class DockerClientFactoryTest { @Test public void runCommandInsideDockerShouldNotFailIfImageDoesNotExistsLocally() { + try (DockerRegistryContainer registryContainer = new DockerRegistryContainer()) { + registryContainer.start(); + DockerImageName imageName = registryContainer.createImage(); - final DockerClientFactory dockFactory = DockerClientFactory.instance(); + DockerClientFactory dockFactory = DockerClientFactory.instance(); - DockerImageName imageName = DockerImageName.parse("testcontainers/helloworld:1.1.0"); - - try { - //remove tiny image, so it will be pulled during next command run - dockFactory.client() - .removeImageCmd(imageName.asCanonicalNameString()) - .withForce(true).exec(); - } catch (NotFoundException ignored) { - // Do not fail if it's not pulled yet - } - LocalImagesCacheAccessor.clearCache(); - - dockFactory.runInsideDocker( + dockFactory.runInsideDocker( imageName, cmd -> cmd.withCmd("sh", "-c", "echo 'SUCCESS'"), (client, id) -> - client.logContainerCmd(id) - .withStdOut(true) - .exec(new LogToStringContainerCallback()) - .toString() - ); + client.logContainerCmd(id) + .withStdOut(true) + .exec(new LogToStringContainerCallback()) + .toString() + ); + } } @Test diff --git a/core/src/test/java/org/testcontainers/DockerRegistryContainer.java b/core/src/test/java/org/testcontainers/DockerRegistryContainer.java index 641b1553d72..4018b2b954d 100644 --- a/core/src/test/java/org/testcontainers/DockerRegistryContainer.java +++ b/core/src/test/java/org/testcontainers/DockerRegistryContainer.java @@ -1,6 +1,9 @@ package org.testcontainers; +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.async.ResultCallback; import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.command.PullImageResultCallback; import lombok.Getter; import lombok.NonNull; import lombok.SneakyThrows; @@ -8,7 +11,10 @@ import org.testcontainers.containers.output.FrameConsumerResultCallback; import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.output.WaitingConsumer; +import org.testcontainers.utility.Base58; +import org.testcontainers.utility.DockerImageName; +import java.util.UUID; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -65,4 +71,34 @@ protected void containerIsStarting(InspectContainerResponse containerInfo) { endpoint = getHost() + ":" + port.get(); } + + public DockerImageName createImage() { + return createImage(UUID.randomUUID().toString()); + } + + public DockerImageName createImage(String tag) { + return createImage("testcontainers/helloworld:latest", tag); + } + + @SneakyThrows(InterruptedException.class) + public DockerImageName createImage(String originalImage, String tag) { + DockerClient client = getDockerClient(); + client.pullImageCmd(originalImage).exec(new PullImageResultCallback()).awaitCompletion(); + + String dummyImageId = client.inspectImageCmd(originalImage).exec().getId(); + + DockerImageName imageName = DockerImageName.parse(getEndpoint() + "/" + Base58.randomString(6).toLowerCase()).withTag(tag); + + // push the image to the registry + client.tagImageCmd(dummyImageId, imageName.asCanonicalNameString(), tag).exec(); + + client.pushImageCmd(imageName.asCanonicalNameString()) + .exec(new ResultCallback.Adapter<>()) + .awaitCompletion(1, TimeUnit.MINUTES); + + // Remove from local cache, tests should pull the image themselves + client.removeImageCmd(imageName.asCanonicalNameString()).exec(); + + return imageName; + } } diff --git a/core/src/test/java/org/testcontainers/dockerclient/AmbiguousImagePullTest.java b/core/src/test/java/org/testcontainers/dockerclient/AmbiguousImagePullTest.java index 9e62148c3bb..2e39b367c46 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/AmbiguousImagePullTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/AmbiguousImagePullTest.java @@ -1,32 +1,25 @@ package org.testcontainers.dockerclient; -import com.github.dockerjava.api.DockerClient; -import com.github.dockerjava.api.model.Image; import org.junit.Test; -import org.testcontainers.DockerClientFactory; +import org.testcontainers.DockerRegistryContainer; import org.testcontainers.containers.GenericContainer; import org.testcontainers.utility.DockerImageName; -import java.util.List; - public class AmbiguousImagePullTest { @Test(timeout = 30_000) public void testNotUsingParse() { - DockerClient client = DockerClientFactory.instance().client(); - List alpineImages = client.listImagesCmd() - .withImageNameFilter("testcontainers/helloworld:latest") - .exec(); - for (Image alpineImage : alpineImages) { - client.removeImageCmd(alpineImage.getId()).exec(); - } - - try ( - final GenericContainer container = new GenericContainer<>(DockerImageName.parse("testcontainers/helloworld")) - .withExposedPorts(8080) - ) { - container.start(); - // do nothing other than start and stop + try (DockerRegistryContainer registryContainer = new DockerRegistryContainer()) { + registryContainer.start(); + DockerImageName imageName = registryContainer.createImage("latest"); + String imageNameWithoutTag = imageName.getRegistry() + "/" + imageName.getRepository(); + try ( + final GenericContainer container = new GenericContainer<>(imageNameWithoutTag) + .withExposedPorts(8080) + ) { + container.start(); + // do nothing other than start and stop + } } } } diff --git a/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java b/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java index 85c2e785652..9d5ed400d32 100644 --- a/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java +++ b/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java @@ -1,12 +1,6 @@ package org.testcontainers.images; -import com.github.dockerjava.api.DockerClient; -import com.github.dockerjava.api.async.ResultCallback; -import com.github.dockerjava.api.command.PullImageResultCallback; import com.github.dockerjava.api.exception.NotFoundException; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; import org.mockito.Mockito; @@ -18,9 +12,6 @@ import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import org.testcontainers.utility.DockerImageName; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; @@ -29,47 +20,13 @@ public class ImagePullPolicyTest { @ClassRule public static DockerRegistryContainer registry = new DockerRegistryContainer(); - private static DockerImageName imageName; - - @BeforeClass - public static void beforeClass() throws Exception { - String testRegistryAddress = registry.getEndpoint(); - String testImageName = testRegistryAddress + "/image-pull-policy-test"; - String tag = UUID.randomUUID().toString(); - imageName = DockerImageName.parse(testImageName).withTag(tag); - - DockerClient client = DockerClientFactory.instance().client(); - String dummySourceImage = "hello-world:latest"; - client.pullImageCmd(dummySourceImage).exec(new PullImageResultCallback()).awaitCompletion(); - - String dummyImageId = client.inspectImageCmd(dummySourceImage).exec().getId(); - - // push the image to the registry - client.tagImageCmd(dummyImageId, testImageName, tag).exec(); - - client.pushImageCmd(imageName.asCanonicalNameString()) - .exec(new ResultCallback.Adapter<>()) - .awaitCompletion(1, TimeUnit.MINUTES); - } - - @AfterClass - public static void afterClass() { - removeImage(); - } - - @Before - public void setUp() { - // Clean up local cache - removeImage(); - - LocalImagesCache.INSTANCE.cache.remove(imageName); - } + private final DockerImageName imageName = registry.createImage(); @Test public void pullsByDefault() { try ( GenericContainer container = new GenericContainer<>(imageName) - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .withExposedPorts(8080) ) { container.start(); } @@ -79,7 +36,7 @@ public void pullsByDefault() { public void shouldAlwaysPull() { try ( GenericContainer container = new GenericContainer<>(imageName) - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .withExposedPorts(8080) ) { container.start(); } @@ -88,7 +45,7 @@ public void shouldAlwaysPull() { try ( GenericContainer container = new GenericContainer<>(imageName) - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .withExposedPorts(8080) ) { expectToFailWithNotFoundException(container); } @@ -99,7 +56,7 @@ public void shouldAlwaysPull() { .withImagePullPolicy(PullPolicy.alwaysPull()) // } ) { - container.withStartupCheckStrategy(new OneShotStartupCheckStrategy()); + container.withExposedPorts(8080); container.start(); } } @@ -117,7 +74,7 @@ protected boolean shouldPullCached(DockerImageName imageName, ImageData localIma }) // } ) { - container.withStartupCheckStrategy(new OneShotStartupCheckStrategy()); + container.withExposedPorts(8080); container.start(); } } @@ -133,7 +90,7 @@ protected boolean shouldPullCached(DockerImageName imageName, ImageData localIma try ( GenericContainer container = new GenericContainer<>(imageName) .withImagePullPolicy(policy) - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .withExposedPorts(8080) ) { container.start(); @@ -169,7 +126,7 @@ private void expectToFailWithNotFoundException(GenericContainer container) { } } - private static void removeImage() { + private void removeImage() { try { DockerClientFactory.instance().client() .removeImageCmd(imageName.asCanonicalNameString()) diff --git a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java index 412a945b204..a911675dfb3 100644 --- a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java +++ b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java @@ -1,37 +1,28 @@ package org.testcontainers.utility; -import com.github.dockerjava.api.DockerClient; -import com.github.dockerjava.api.async.ResultCallback; -import com.github.dockerjava.api.command.PullImageResultCallback; -import com.github.dockerjava.api.exception.NotFoundException; import com.github.dockerjava.api.model.AuthConfig; import org.intellij.lang.annotations.Language; import org.junit.AfterClass; -import org.junit.Before; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; import org.mockito.Mockito; -import org.testcontainers.DockerClientFactory; import org.testcontainers.DockerRegistryContainer; import org.testcontainers.containers.ContainerState; import org.testcontainers.containers.DockerComposeContainer; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.images.LocalImagesCacheAccessor; import org.testcontainers.images.builder.ImageFromDockerfile; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.concurrent.TimeUnit; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.when; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; import static org.testcontainers.TestImages.DOCKER_REGISTRY_IMAGE; -import static org.testcontainers.TestImages.TINY_IMAGE; /** * This test checks the integration between Testcontainers and an authenticated registry, but uses @@ -57,20 +48,15 @@ public class AuthenticatedImagePullTest { })); private static RegistryAuthLocator originalAuthLocatorSingleton; - private static DockerClient client; - private static String testImageName; - private static RegistryAuthLocator mockAuthLocator; + private final DockerImageName testImageName = authenticatedRegistry.createImage(); @BeforeClass - public static void setUp() throws Exception { + public static void beforeClass() throws Exception { originalAuthLocatorSingleton = RegistryAuthLocator.instance(); - client = DockerClientFactory.instance().client(); String testRegistryAddress = authenticatedRegistry.getEndpoint(); - testImageName = testRegistryAddress + "/alpine"; - final DockerImageName expectedName = DockerImageName.parse(testImageName); final AuthConfig authConfig = new AuthConfig() .withUsername("testuser") .withPassword("notasecret") @@ -79,22 +65,8 @@ public static void setUp() throws Exception { // Replace the RegistryAuthLocator singleton with our mock, for the duration of this test final RegistryAuthLocator mockAuthLocator = Mockito.mock(RegistryAuthLocator.class); RegistryAuthLocator.setInstance(mockAuthLocator); - when(mockAuthLocator.lookupAuthConfig(eq(expectedName), any())) + when(mockAuthLocator.lookupAuthConfig(argThat(argument -> testRegistryAddress.equals(argument.getRegistry())), any())) .thenReturn(authConfig); - - // a push will use the auth locator for authentication, although that isn't the goal of this test - putImageInRegistry(); - } - - @Before - public void removeImageFromLocalDocker() { - // remove the image tag from local docker so that it must be pulled before use - try { - client.removeImageCmd(testImageName).withForce(true).exec(); - } catch (NotFoundException ignored) { - - } - LocalImagesCacheAccessor.clearCache(); } @AfterClass @@ -105,7 +77,7 @@ public static void tearDown() { @Test public void testThatAuthLocatorIsUsedForContainerCreation() { // actually start a container, which will require an authenticated pull - try (final GenericContainer container = new GenericContainer<>(DockerImageName.parse(testImageName)) + try (final GenericContainer container = new GenericContainer<>(testImageName) .withCommand("/bin/sh", "-c", "sleep 10")) { container.start(); @@ -117,7 +89,7 @@ public void testThatAuthLocatorIsUsedForContainerCreation() { public void testThatAuthLocatorIsUsedForDockerfileBuild() throws IOException { // Prepare a simple temporary Dockerfile which requires our custom private image Path tempFile = getLocalTempFile(".Dockerfile"); - String dockerFileContent = "FROM " + testImageName; + String dockerFileContent = "FROM " + testImageName.asCanonicalNameString(); Files.write(tempFile, dockerFileContent.getBytes()); // Start a container built from a derived image, which will require an authenticated pull @@ -141,7 +113,7 @@ public void testThatAuthLocatorIsUsedForDockerComposePull() throws IOException { "services:\n" + " privateservice:\n" + " command: /bin/sh -c 'sleep 60'\n" + - " image: " + testImageName; + " image: " + testImageName.asCanonicalNameString(); Files.write(tempFile, composeFileContent.getBytes()); // Start the docker compose project, which will require an authenticated pull @@ -168,24 +140,4 @@ private Path getLocalTempFile(String s) throws IOException { return tempFile; } - - private static void putImageInRegistry() throws InterruptedException { - // It doesn't matter which image we use for this test, but use one that's likely to have been pulled already - final String dummySourceImage = TINY_IMAGE.asCanonicalNameString(); - - client.pullImageCmd(dummySourceImage) - .exec(new PullImageResultCallback()) - .awaitCompletion(1, TimeUnit.MINUTES); - - final String id = client.inspectImageCmd(dummySourceImage) - .exec() - .getId(); - - // push the image to the registry - client.tagImageCmd(id, testImageName, "").exec(); - - client.pushImageCmd(testImageName) - .exec(new ResultCallback.Adapter<>()) - .awaitCompletion(1, TimeUnit.MINUTES); - } } From c0183f1d81ae2637d8ed70634ce643e3c75263b0 Mon Sep 17 00:00:00 2001 From: Andriy Redko Date: Mon, 16 May 2022 11:57:23 -0400 Subject: [PATCH 2524/5615] Allow insecure HTTPS/TLS connection for HttpWaitStrategy (#4951) Signed-off-by: Andriy Redko --- .../wait/strategy/HttpWaitStrategy.java | 63 ++++++++++++++++++- .../wait/strategy/HttpWaitStrategyTest.java | 19 ++++++ .../ElasticsearchContainerTest.java | 23 +++++++ 3 files changed, 104 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java index c4751725707..d5d08ce6fd0 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java @@ -12,15 +12,26 @@ import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; +import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.time.Duration; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; import java.util.HashSet; import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + import static org.rnorth.ducttape.unreliables.Unreliables.retryUntilSuccess; @Slf4j @@ -47,6 +58,7 @@ public class HttpWaitStrategy extends AbstractWaitStrategy { private Predicate statusCodePredicate = null; private Optional livenessPort = Optional.empty(); private Duration readTimeout = Duration.ofSeconds(1); + private boolean allowInsecure; /** * Waits for the given status code. @@ -112,6 +124,16 @@ public HttpWaitStrategy withMethod(String method) { return this; } + /** + * Indicates that HTTPS connection could use untrusted (self signed) certificate chains. + * + * @return this + */ + public HttpWaitStrategy allowInsecure() { + this.allowInsecure = true; + return this; + } + /** * Authenticate with HTTP Basic Authorization credentials. * @@ -206,7 +228,7 @@ protected void waitUntilReady() { retryUntilSuccess((int) startupTimeout.getSeconds(), TimeUnit.SECONDS, () -> { getRateLimiter().doWhenReady(() -> { try { - final HttpURLConnection connection = (HttpURLConnection) new URL(uri).openConnection(); + final HttpURLConnection connection = openConnection(uri); connection.setReadTimeout(Math.toIntExact(readTimeout.toMillis())); // authenticate @@ -267,6 +289,45 @@ protected void waitUntilReady() { } } + private HttpURLConnection openConnection(final String uri) throws IOException, MalformedURLException { + if (tlsEnabled) { + final HttpsURLConnection connection = (HttpsURLConnection)new URL(uri).openConnection(); + if (allowInsecure) { + // Create a trust manager that does not validate certificate chains + // and trust all certificates + final TrustManager[] trustAllCerts = new TrustManager[] { + new X509TrustManager() { + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + + @Override + public void checkClientTrusted(final X509Certificate[] certs, final String authType) { + } + + @Override + public void checkServerTrusted(final X509Certificate[] certs, final String authType) { + } + } + }; + + try { + // Create custom SSL context and set the "trust all certificates" trust manager + final SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(new KeyManager[0], trustAllCerts, new SecureRandom()); + connection.setSSLSocketFactory(sc.getSocketFactory()); + } catch (final NoSuchAlgorithmException | KeyManagementException ex) { + throw new IOException("Unable to create custom SSL factory instance", ex); + } + } + + return connection; + } else { + return (HttpURLConnection) new URL(uri).openConnection(); + } + } + /** * Build the URI on which to check if the container is ready. * diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java index c5a39a4538c..82207b4537e 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java @@ -57,6 +57,17 @@ public void testWaitUntilReadyWithSuccessWithCustomHeaders() { assertThat(logs, containsString("baz: boo")); } + /** + * Ensures that HTTPS requests made with the HttpWaitStrategy can skip the + * certificate validation chains (to support self-signed certificates for example). + */ + @Test + public void testWaitUntilReadyWithTlsAndAllowUnsecure() { + waitUntilReadyAndSucceed(startContainerWithCommand(createHttpsShellCommand("200 OK", GOOD_RESPONSE_BODY, 8080), + createHttpWaitStrategy(ready).usingTls().allowInsecure() + )); + } + /** * Expects that the WaitStrategy returns successfully after receiving an HTTP 401 response from the container. * This 401 response is checked with a lambda using {@link HttpWaitStrategy#forStatusCodeMatching(Predicate)} @@ -206,4 +217,12 @@ private String createShellCommand(String header, String responseBody, int port) "Content-Length: " + length + NEWLINE + "\";" + " echo \"" + responseBody + "\";} | nc -lp " + port + "; done"; } + + private String createHttpsShellCommand(String header, String responseBody, int port) { + int length = responseBody.getBytes().length; + return "apk add nmap-ncat; while true; do { echo -e \"HTTP/1.1 " + header + NEWLINE + + "Content-Type: text/html" + NEWLINE + + "Content-Length: " + length + NEWLINE + "\";" + + " echo \"" + responseBody + "\";} | ncat -lp " + port + " --ssl; done"; + } } diff --git a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java index f8131268e61..5b6b5ce3194 100644 --- a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java +++ b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java @@ -19,6 +19,8 @@ import org.junit.After; import org.junit.Test; import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; +import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.images.RemoteDockerImage; import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MountableFile; @@ -282,6 +284,27 @@ public void testElasticsearch8SecureByDefaultCustomCaCertFails() throws Exceptio } } + @Test + public void testElasticsearch8SecureByDefaultHttpWaitStrategy() throws Exception { + final HttpWaitStrategy httpsWaitStrategy = Wait.forHttps("/") + .forPort(9200) + .forStatusCode(200) + .withBasicCredentials(ELASTICSEARCH_USERNAME, ELASTICSEARCH_PASSWORD) + // trusting self-signed certificate + .allowInsecure(); + + try (ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:8.1.2") + .waitingFor(httpsWaitStrategy)) { + + // Start the container. This step might take some time... + container.start(); + + Response response = getClusterHealth(container); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + assertThat(EntityUtils.toString(response.getEntity()), containsString("cluster_name")); + } + } + @Test public void testElasticsearch8SecureByDefaultFailsSilentlyOnLatestImages() throws Exception { // this test exists for custom images by users that use the `latest` tag From 98b60fbf87f4f65f96651bee919c8f8027fe0cc5 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 16 May 2022 13:27:39 -0400 Subject: [PATCH 2525/5615] Use `GenericContainer` in `RyukResourceReaper` (#5358) * Experiment: Use `GenericContainer` in `RyukResourceReaper` * reduce global `DockerClient` usage * revert indentation * Avoid bind check * Fix `japicmp` * Add `ResourceReaperTest` --- core/build.gradle | 10 +- .../ComposeServiceWaitStrategyTarget.java | 8 +- .../testcontainers/containers/Container.java | 6 +- .../containers/ContainerState.java | 16 ++- .../containers/DockerComposeContainer.java | 9 +- .../containers/ExecInContainerPattern.java | 36 ++++- .../containers/GenericContainer.java | 4 +- .../IsRunningStartupCheckStrategy.java | 17 ++- .../startupcheck/StartupCheckStrategy.java | 10 ++ .../InternalCommandPortListeningCheck.java | 2 +- .../wait/strategy/LogMessageWaitStrategy.java | 2 +- .../utility/ResourceReaper.java | 12 ++ .../testcontainers/utility/RyukContainer.java | 27 ++++ .../utility/RyukResourceReaper.java | 135 ++++-------------- .../utility/ResourceReaperTest.java | 109 ++++++++++++++ 15 files changed, 257 insertions(+), 146 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/utility/RyukContainer.java create mode 100644 core/src/test/java/org/testcontainers/utility/ResourceReaperTest.java diff --git a/core/build.gradle b/core/build.gradle index b519673de4b..1988063489e 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -43,12 +43,8 @@ tasks.japicmp { classExcludes = [] methodExcludes = [ - "org.testcontainers.utility.ResourceReaper#start(java.lang.String,com.github.dockerjava.api.DockerClient)", - "org.testcontainers.utility.ResourceReaper#start(com.github.dockerjava.api.DockerClient)", - "org.testcontainers.utility.ResourceReaper#registerNetworkForCleanup(java.lang.String)", - "org.testcontainers.utility.ResourceReaper#removeNetworks(java.lang.String)", - "org.testcontainers.images.builder.Transferable#of(java.lang.String)", - "org.testcontainers.images.builder.Transferable#updateChecksum(java.util.zip.Checksum)" + "org.testcontainers.containers.Container#getDockerClient()", + "org.testcontainers.containers.ContainerState#getDockerClient()", ] fieldExcludes = [] @@ -62,7 +58,7 @@ configurations.all { } dependencies { - baseline 'org.testcontainers:testcontainers:1.16.3', { + baseline 'org.testcontainers:testcontainers:1.17.1', { exclude group: "*", module: "*" } diff --git a/core/src/main/java/org/testcontainers/containers/ComposeServiceWaitStrategyTarget.java b/core/src/main/java/org/testcontainers/containers/ComposeServiceWaitStrategyTarget.java index 2fb953b4aee..fbb2a7cee6e 100644 --- a/core/src/main/java/org/testcontainers/containers/ComposeServiceWaitStrategyTarget.java +++ b/core/src/main/java/org/testcontainers/containers/ComposeServiceWaitStrategyTarget.java @@ -1,11 +1,11 @@ package org.testcontainers.containers; +import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.model.Container; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NonNull; -import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.wait.strategy.WaitStrategyTarget; import java.util.ArrayList; @@ -22,13 +22,15 @@ class ComposeServiceWaitStrategyTarget implements WaitStrategyTarget { private final Container container; private final GenericContainer proxyContainer; + private final DockerClient dockerClient; @NonNull private Map mappedPorts; @Getter(lazy=true) - private final InspectContainerResponse containerInfo = DockerClientFactory.instance().client().inspectContainerCmd(getContainerId()).exec(); + private final InspectContainerResponse containerInfo = dockerClient.inspectContainerCmd(getContainerId()).exec(); - ComposeServiceWaitStrategyTarget(Container container, GenericContainer proxyContainer, + ComposeServiceWaitStrategyTarget(DockerClient dockerClient, Container container, GenericContainer proxyContainer, @NonNull Map mappedPorts) { + this.dockerClient = dockerClient; this.container = container; this.proxyContainer = proxyContainer; this.mappedPorts = new HashMap<>(mappedPorts); diff --git a/core/src/main/java/org/testcontainers/containers/Container.java b/core/src/main/java/org/testcontainers/containers/Container.java index 8fd53fc5a0e..88fe4802945 100644 --- a/core/src/main/java/org/testcontainers/containers/Container.java +++ b/core/src/main/java/org/testcontainers/containers/Container.java @@ -389,7 +389,7 @@ default SELF withClasspathResourceMapping(final String resourcePath, final Strin * @param consumer consumer that the frames should be sent to */ default void followOutput(Consumer consumer) { - LogUtils.followOutput(DockerClientFactory.instance().client(), getContainerId(), consumer); + LogUtils.followOutput(getDockerClient(), getContainerId(), consumer); } /** @@ -400,7 +400,7 @@ default void followOutput(Consumer consumer) { * @param types types that should be followed (one or both of STDOUT, STDERR) */ default void followOutput(Consumer consumer, OutputFrame.OutputType... types) { - LogUtils.followOutput(DockerClientFactory.instance().client(), getContainerId(), consumer, types); + LogUtils.followOutput(getDockerClient(), getContainerId(), consumer, types); } @@ -439,8 +439,6 @@ default void followOutput(Consumer consumer, OutputFrame.OutputType @Deprecated Map getLinkedContainers(); - DockerClient getDockerClient(); - void setExposedPorts(List exposedPorts); void setPortBindings(List portBindings); diff --git a/core/src/main/java/org/testcontainers/containers/ContainerState.java b/core/src/main/java/org/testcontainers/containers/ContainerState.java index ef2c19a06c9..51eb4daddde 100644 --- a/core/src/main/java/org/testcontainers/containers/ContainerState.java +++ b/core/src/main/java/org/testcontainers/containers/ContainerState.java @@ -52,6 +52,10 @@ default String getContainerIpAddress() { return getHost(); } + default DockerClient getDockerClient() { + return DockerClientFactory.lazyClient(); + } + /** * Get the host that this container may be reached on (may not be the local machine). * @@ -115,7 +119,7 @@ default boolean isHealthy() { } default InspectContainerResponse getCurrentContainerInfo() { - return DockerClientFactory.instance().client().inspectContainerCmd(getContainerId()).exec(); + return getDockerClient().inspectContainerCmd(getContainerId()).exec(); } /** @@ -195,7 +199,7 @@ default List getBoundPortNumbers() { * @return all log output from the container from start until the current instant (both stdout and stderr) */ default String getLogs() { - return LogUtils.getOutput(DockerClientFactory.instance().client(), getContainerId()); + return LogUtils.getOutput(getDockerClient(), getContainerId()); } /** @@ -203,7 +207,7 @@ default String getLogs() { * @return all log output from the container from start until the current instant */ default String getLogs(OutputFrame.OutputType... types) { - return LogUtils.getOutput(DockerClientFactory.instance().client(), getContainerId(), types); + return LogUtils.getOutput(getDockerClient(), getContainerId(), types); } /** @@ -234,7 +238,7 @@ default Container.ExecResult execInContainer(String... command) throws Unsupport * @see ExecInContainerPattern#execInContainer(com.github.dockerjava.api.command.InspectContainerResponse, Charset, String...) */ default Container.ExecResult execInContainer(Charset outputCharset, String... command) throws UnsupportedOperationException, IOException, InterruptedException { - return ExecInContainerPattern.execInContainer(getContainerInfo(), outputCharset, command); + return ExecInContainerPattern.execInContainer(getDockerClient(), getContainerInfo(), outputCharset, command); } /** @@ -279,7 +283,7 @@ default void copyFileToContainer(Transferable transferable, String containerPath transferable.transferTo(tarArchive, containerPath); tarArchive.finish(); - DockerClientFactory.instance().client() + getDockerClient() .copyArchiveToContainerCmd(getContainerId()) .withTarInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray())) .withRemotePath("/") @@ -316,7 +320,7 @@ default T copyFileFromContainer(String containerPath, ThrowingFunction composeFiles) { - this.composeFiles = composeFiles; this.dockerComposeFiles = new DockerComposeFiles(composeFiles); @@ -132,7 +131,7 @@ public DockerComposeContainer(String identifier, List composeFiles) { this.identifier = identifier; this.project = randomProjectId(); - this.dockerClient = DockerClientFactory.instance().client(); + this.dockerClient = DockerClientFactory.lazyClient(); } @Override @@ -269,7 +268,7 @@ private void waitUntilServiceStarted() { private void createServiceInstance(Container container) { String serviceName = getServiceNameFromContainer(container); - final ComposeServiceWaitStrategyTarget containerInstance = new ComposeServiceWaitStrategyTarget(container, + final ComposeServiceWaitStrategyTarget containerInstance = new ComposeServiceWaitStrategyTarget(dockerClient, container, ambassadorContainer, ambassadorPortMappings.getOrDefault(serviceName, new HashMap<>())); String containerId = containerInstance.getContainerId(); @@ -559,7 +558,7 @@ public Optional getContainerByServiceName(String serviceName) { } private void followLogs(String containerId, Consumer consumer) { - LogUtils.followOutput(DockerClientFactory.instance().client(), containerId, consumer); + LogUtils.followOutput(dockerClient, containerId, consumer); } private SELF self() { @@ -659,7 +658,7 @@ public void invoke() { AuditLogger.doComposeLog(this.getCommandParts(), this.getEnv()); - final Integer exitCode = this.dockerClient.inspectContainerCmd(getContainerId()) + final Integer exitCode = getDockerClient().inspectContainerCmd(getContainerId()) .exec() .getState() .getExitCode(); diff --git a/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java b/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java index 9cd68d48ffb..0858476880f 100644 --- a/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java +++ b/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java @@ -14,6 +14,7 @@ import java.io.IOException; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; /** * Provides utility methods for executing commands in containers @@ -22,17 +23,41 @@ @Slf4j public class ExecInContainerPattern { + + /** + * + * @deprecated use {@link #execInContainer(DockerClient, InspectContainerResponse, String...)} + */ + @Deprecated + public Container.ExecResult execInContainer(InspectContainerResponse containerInfo, String... command) + throws UnsupportedOperationException, IOException, InterruptedException { + DockerClient dockerClient = DockerClientFactory.instance().client(); + return execInContainer(dockerClient, containerInfo, command); + } + + /** + * + * @deprecated use {@link #execInContainer(DockerClient, InspectContainerResponse, Charset, String...)} + */ + @Deprecated + public Container.ExecResult execInContainer(InspectContainerResponse containerInfo, Charset outputCharset, String... command) + throws UnsupportedOperationException, IOException, InterruptedException { + DockerClient dockerClient = DockerClientFactory.instance().client(); + return execInContainer(dockerClient, containerInfo, outputCharset, command); + } + /** * Run a command inside a running container, as though using "docker exec", and interpreting * the output as UTF8. *

+ * @param dockerClient the {@link DockerClient} * @param containerInfo the container info * @param command the command to execute - * @see #execInContainer(InspectContainerResponse, Charset, String...) + * @see #execInContainer(DockerClient, InspectContainerResponse, Charset, String...) */ - public Container.ExecResult execInContainer(InspectContainerResponse containerInfo, String... command) + public Container.ExecResult execInContainer(DockerClient dockerClient, InspectContainerResponse containerInfo, String... command) throws UnsupportedOperationException, IOException, InterruptedException { - return execInContainer(containerInfo, Charset.forName("UTF-8"), command); + return execInContainer(dockerClient, containerInfo, StandardCharsets.UTF_8, command); } /** @@ -40,6 +65,7 @@ public Container.ExecResult execInContainer(InspectContainerResponse containerIn *

* This functionality is not available on a docker daemon running the older "lxc" execution driver. At * the time of writing, CircleCI was using this driver. + * @param dockerClient the {@link DockerClient} * @param containerInfo the container info * @param outputCharset the character set used to interpret the output. * @param command the parts of the command to run @@ -48,7 +74,7 @@ public Container.ExecResult execInContainer(InspectContainerResponse containerIn * @throws InterruptedException if the thread waiting for the response is interrupted * @throws UnsupportedOperationException if the docker daemon you're connecting to doesn't support "exec". */ - public Container.ExecResult execInContainer(InspectContainerResponse containerInfo, Charset outputCharset, String... command) + public Container.ExecResult execInContainer(DockerClient dockerClient, InspectContainerResponse containerInfo, Charset outputCharset, String... command) throws UnsupportedOperationException, IOException, InterruptedException { if (!TestEnvironment.dockerExecutionDriverSupportsExec()) { // at time of writing, this is the expected result in CircleCI. @@ -64,8 +90,6 @@ public Container.ExecResult execInContainer(InspectContainerResponse containerIn String containerId = containerInfo.getId(); String containerName = containerInfo.getName(); - DockerClient dockerClient = DockerClientFactory.instance().client(); - log.debug("{}: Running \"exec\" command: {}", containerName, String.join(" ", command)); final ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(containerId) .withAttachStdout(true).withAttachStderr(true).withCmd(command).exec(); diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index d47bc6cfc5e..4e5e682738d 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -416,7 +416,7 @@ private void tryStart(Instant startedAt) { if (!reusable) { //noinspection deprecation - createCommand.getLabels().putAll(ResourceReaper.instance().getLabels()); + createCommand = ResourceReaper.instance().register(this, createCommand); } if (!reused) { @@ -469,7 +469,7 @@ private void tryStart(Instant startedAt) { containerIsStarting(containerInfo, reused); // Wait until the container has reached the desired running state - if (!this.startupCheckStrategy.waitUntilStartupSuccessful(dockerClient, containerId)) { + if (!this.startupCheckStrategy.waitUntilStartupSuccessful(this)) { // Bail out, don't wait for the port to start listening. // (Exception thrown here will be caught below and wrapped) throw new IllegalStateException("Container did not start correctly."); diff --git a/core/src/main/java/org/testcontainers/containers/startupcheck/IsRunningStartupCheckStrategy.java b/core/src/main/java/org/testcontainers/containers/startupcheck/IsRunningStartupCheckStrategy.java index 6a296e0329c..b4b20aad897 100644 --- a/core/src/main/java/org/testcontainers/containers/startupcheck/IsRunningStartupCheckStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/startupcheck/IsRunningStartupCheckStrategy.java @@ -2,6 +2,7 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.InspectContainerResponse; +import org.testcontainers.containers.GenericContainer; import org.testcontainers.utility.DockerStatus; /** @@ -10,10 +11,24 @@ */ public class IsRunningStartupCheckStrategy extends StartupCheckStrategy { + @SuppressWarnings("deprecation") + @Override + public boolean waitUntilStartupSuccessful(GenericContainer container) { + // Optimization: container already has the initial "after start" state, check it first + if (checkState(container.getContainerInfo().getState()) == StartupStatus.SUCCESSFUL) { + return true; + } + return super.waitUntilStartupSuccessful(container); + } + @Override public StartupStatus checkStartupState(DockerClient dockerClient, String containerId) { InspectContainerResponse.ContainerState state = getCurrentState(dockerClient, containerId); - if (state.getRunning()) { + return checkState(state); + } + + private StartupStatus checkState(InspectContainerResponse.ContainerState state) { + if (Boolean.TRUE.equals(state.getRunning())) { return StartupStatus.SUCCESSFUL; } else if (!DockerStatus.isContainerExitCodeSuccess(state)) { return StartupStatus.FAILED; diff --git a/core/src/main/java/org/testcontainers/containers/startupcheck/StartupCheckStrategy.java b/core/src/main/java/org/testcontainers/containers/startupcheck/StartupCheckStrategy.java index 1425123473b..d5931454212 100644 --- a/core/src/main/java/org/testcontainers/containers/startupcheck/StartupCheckStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/startupcheck/StartupCheckStrategy.java @@ -5,6 +5,7 @@ import org.rnorth.ducttape.ratelimits.RateLimiter; import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; import org.rnorth.ducttape.unreliables.Unreliables; +import org.testcontainers.containers.GenericContainer; import java.time.Duration; import java.util.concurrent.TimeUnit; @@ -30,6 +31,15 @@ public SELF withTimeout(Duration timeout) { return (SELF) this; } + /** + * + * @deprecated internal API + */ + @Deprecated + public boolean waitUntilStartupSuccessful(GenericContainer container) { + return waitUntilStartupSuccessful(container.getDockerClient(), container.getContainerId()); + } + public boolean waitUntilStartupSuccessful(DockerClient dockerClient, String containerId) { final Boolean[] startedOK = {null}; Unreliables.retryUntilTrue((int) timeout.toMillis(), TimeUnit.MILLISECONDS, () -> { diff --git a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java index 379840a9d7e..1eba8acbbd1 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java +++ b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java @@ -40,7 +40,7 @@ public Boolean call() { Instant before = Instant.now(); try { - ExecResult result = ExecInContainerPattern.execInContainer(waitStrategyTarget.getContainerInfo(), "/bin/sh", "-c", command.toString()); + ExecResult result = ExecInContainerPattern.execInContainer(waitStrategyTarget.getDockerClient(), waitStrategyTarget.getContainerInfo(), "/bin/sh", "-c", command.toString()); log.trace("Check for {} took {}. Result code '{}', stdout message: '{}'", internalPorts, Duration.between(before, Instant.now()), result.getExitCode(), result.getStdout()); int exitCode = result.getExitCode(); if (exitCode != 0 && exitCode != 1) { diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java index 9f7a0078f00..65370e51bfa 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java @@ -27,7 +27,7 @@ public class LogMessageWaitStrategy extends AbstractWaitStrategy { protected void waitUntilReady() { WaitingConsumer waitingConsumer = new WaitingConsumer(); - LogContainerCmd cmd = DockerClientFactory.instance().client().logContainerCmd(waitStrategyTarget.getContainerId()) + LogContainerCmd cmd = waitStrategyTarget.getDockerClient().logContainerCmd(waitStrategyTarget.getContainerId()) .withFollowStream(true) .withSince(0) .withStdOut(true) diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index 44f9456ce4c..d916579db35 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -1,6 +1,7 @@ package org.testcontainers.utility; import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.command.CreateContainerCmd; import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.exception.NotFoundException; import com.github.dockerjava.api.model.Network; @@ -11,6 +12,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.GenericContainer; import java.io.BufferedReader; import java.io.IOException; @@ -307,6 +309,16 @@ public Map getLabels() { return MARKER_LABELS; } + /** + * + * @deprecated internal API + */ + @Deprecated + public CreateContainerCmd register(GenericContainer container, CreateContainerCmd cmd) { + cmd.getLabels().putAll(getLabels()); + return cmd; + } + /** * @deprecated internal API */ diff --git a/core/src/main/java/org/testcontainers/utility/RyukContainer.java b/core/src/main/java/org/testcontainers/utility/RyukContainer.java new file mode 100644 index 00000000000..1e18c14157a --- /dev/null +++ b/core/src/main/java/org/testcontainers/utility/RyukContainer.java @@ -0,0 +1,27 @@ +package org.testcontainers.utility; + +import com.github.dockerjava.api.model.Bind; +import com.github.dockerjava.api.model.Volume; +import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; + +class RyukContainer extends GenericContainer { + + RyukContainer() { + super("testcontainers/ryuk:0.3.3"); + + withExposedPorts(8080); + withCreateContainerCmdModifier(cmd -> { + cmd.withName("testcontainers-ryuk-" + DockerClientFactory.SESSION_ID); + cmd.withHostConfig( + cmd.getHostConfig() + .withAutoRemove(true) + .withPrivileged(TestcontainersConfiguration.getInstance().isRyukPrivileged()) + .withBinds(new Bind(DockerClientFactory.instance().getRemoteDockerUnixSocketPath(), new Volume("/var/run/docker.sock"))) + ); + }); + + waitingFor(Wait.forLogMessage(".*Started.*", 1)); + } +} diff --git a/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java b/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java index 9efbf5ed972..c2e2cf4ef97 100644 --- a/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java @@ -1,40 +1,21 @@ package org.testcontainers.utility; -import com.github.dockerjava.api.DockerClient; -import com.github.dockerjava.api.async.ResultCallback; -import com.github.dockerjava.api.command.InspectContainerResponse; -import com.github.dockerjava.api.model.Bind; -import com.github.dockerjava.api.model.ExposedPort; -import com.github.dockerjava.api.model.Frame; -import com.github.dockerjava.api.model.HostConfig; -import com.github.dockerjava.api.model.PortBinding; -import com.github.dockerjava.api.model.Ports; -import com.github.dockerjava.api.model.Volume; -import lombok.Getter; +import com.github.dockerjava.api.command.CreateContainerCmd; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.rnorth.ducttape.ratelimits.RateLimiter; import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; import org.testcontainers.DockerClientFactory; -import org.testcontainers.containers.ContainerState; -import org.testcontainers.images.RemoteDockerImage; +import org.testcontainers.containers.GenericContainer; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.awaitility.Awaitility.await; /** * Ryuk-based {@link ResourceReaper} implementation. @@ -54,8 +35,11 @@ public class RyukResourceReaper extends ResourceReaper { private final AtomicBoolean started = new AtomicBoolean(false); - @Getter - private String containerId = null; + private final RyukContainer ryukContainer = new RyukContainer(); + + public String getContainerId() { + return ryukContainer.getContainerId(); + } @Override public void init() { @@ -80,91 +64,29 @@ public Map getLabels() { return super.getLabels(); } + @Override + public CreateContainerCmd register(GenericContainer container, CreateContainerCmd cmd) { + if (container == ryukContainer) { + // Do not register Ryuk container to avoid self-pruning + return cmd; + } + + maybeStart(); + return super.register(container, cmd); + } + @SneakyThrows(InterruptedException.class) private synchronized void maybeStart() { if (!started.compareAndSet(false, true)) { return; } - DockerClient client = DockerClientFactory.lazyClient(); - RemoteDockerImage ryukImage = new RemoteDockerImage(DockerImageName.parse("testcontainers/ryuk:0.3.3")); - - List binds = new ArrayList<>(); - binds.add(new Bind(DockerClientFactory.instance().getRemoteDockerUnixSocketPath(), new Volume("/var/run/docker.sock"))); - - ExposedPort ryukExposedPort = ExposedPort.tcp(8080); - containerId = client.createContainerCmd(ryukImage.get()) - .withHostConfig( - new HostConfig() - .withAutoRemove(true) - .withPortBindings(new PortBinding(Ports.Binding.empty(), ryukExposedPort)) - ) - .withExposedPorts(ryukExposedPort) - .withName("testcontainers-ryuk-" + DockerClientFactory.SESSION_ID) - .withLabels(Collections.singletonMap(DockerClientFactory.TESTCONTAINERS_LABEL, "true")) - .withBinds(binds) - .withPrivileged(TestcontainersConfiguration.getInstance().isRyukPrivileged()) - .exec() - .getId(); - - client.startContainerCmd(containerId).exec(); - - StringBuilder ryukLog = new StringBuilder(); - - ResultCallback.Adapter logCallback = client.logContainerCmd(containerId) - .withSince(0) - .withFollowStream(true) - .withStdOut(true) - .withStdErr(true) - .exec(new ResultCallback.Adapter() { - @Override - public void onNext(Frame frame) { - ryukLog.append(new String(frame.getPayload(), StandardCharsets.UTF_8)); - } - }); - - InspectContainerResponse inspectedContainer; - try { - // inspect container response might initially not contain the mapped port - inspectedContainer = await() - .atMost(5, TimeUnit.SECONDS) - .pollInterval(DynamicPollInterval.ofMillis(50)) - .pollInSameThread() - .until( - () -> client.inspectContainerCmd(containerId).exec(), - inspectContainerResponse -> { - return inspectContainerResponse - .getNetworkSettings() - .getPorts() - .getBindings() - .values() - .stream() - .anyMatch(Objects::nonNull); - } - ); - } catch (Exception e) { - log.warn("Ryuk container cannot be inspected and probably had a problem starting. Ryuk's logs:\n{}", ryukLog); - throw new IllegalStateException("Ryuk failed to start", e); - } - - ContainerState containerState = new ContainerState() { - - @Override - public List getExposedPorts() { - return Stream.of(getContainerInfo().getConfig().getExposedPorts()) - .map(ExposedPort::getPort) - .collect(Collectors.toList()); - } - @Override - public InspectContainerResponse getContainerInfo() { - return inspectedContainer; - } - }; + ryukContainer.start(); CountDownLatch ryukScheduledLatch = new CountDownLatch(1); - String host = containerState.getHost(); - Integer ryukPort = containerState.getFirstMappedPort(); + String host = ryukContainer.getHost(); + Integer ryukPort = ryukContainer.getFirstMappedPort(); Thread kiraThread = new Thread( DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, () -> { @@ -207,17 +129,10 @@ public InspectContainerResponse getContainerInfo() { ); kiraThread.setDaemon(true); kiraThread.start(); - try { - // We need to wait before we can start any containers to make sure that we delete them - if (!ryukScheduledLatch.await(TestcontainersConfiguration.getInstance().getRyukTimeout(), TimeUnit.SECONDS)) { - log.error("Timed out waiting for Ryuk container to start. Ryuk's logs:\n{}", ryukLog); - throw new IllegalStateException(String.format("Could not connect to Ryuk at %s:%s", host, ryukPort)); - } - } finally { - try { - logCallback.close(); - } catch (IOException ignored) { - } + // We need to wait before we can start any containers to make sure that we delete them + if (!ryukScheduledLatch.await(TestcontainersConfiguration.getInstance().getRyukTimeout(), TimeUnit.SECONDS)) { + log.error("Timed out waiting for Ryuk container to start. Ryuk's logs:\n{}", ryukContainer.getLogs()); + throw new IllegalStateException(String.format("Could not connect to Ryuk at %s:%s", host, ryukPort)); } } } diff --git a/core/src/test/java/org/testcontainers/utility/ResourceReaperTest.java b/core/src/test/java/org/testcontainers/utility/ResourceReaperTest.java new file mode 100644 index 00000000000..214b465c297 --- /dev/null +++ b/core/src/test/java/org/testcontainers/utility/ResourceReaperTest.java @@ -0,0 +1,109 @@ +package org.testcontainers.utility; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dockerjava.api.DockerClient; +import lombok.SneakyThrows; +import org.awaitility.Awaitility; +import org.awaitility.core.ConditionFactory; +import org.junit.Test; +import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.GenericContainer; +import org.zeroturnaround.exec.ProcessExecutor; +import org.zeroturnaround.exec.ProcessResult; + +import java.io.File; +import java.time.Duration; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; + +public class ResourceReaperTest { + + @Test + public void shouldCleanupWithRyuk() { + Map labels = runProcess(processExecutor -> {}); + + assertCleanup(labels); + } + + @Test + public void shouldCleanupWithJVM() { + Map labels = runProcess(processExecutor -> { + processExecutor.environment("TESTCONTAINERS_RYUK_DISABLED", "true"); + }); + + assertCleanup(labels); + } + + private void assertCleanup(Map labels) { + DockerClient client = DockerClientFactory.instance().client(); + ConditionFactory awaitFactory = Awaitility.await().atMost(Duration.ofMinutes(1)).pollInterval(Duration.ofSeconds(1)); + + List labelValues = labels.entrySet().stream() + .map(it -> it.getKey() + "=" + it.getValue()) + .collect(Collectors.toList()); + + awaitFactory.untilAsserted(() -> { + assertThat(client.listContainersCmd().withFilter("label", labelValues).withShowAll(true).exec()) + .isEmpty(); + }); + + awaitFactory.untilAsserted(() -> { + assertThat(client.listNetworksCmd().withFilter("label", labelValues).exec()) + .isEmpty(); + }); + + awaitFactory.untilAsserted(() -> { + assertThat(client.listVolumesCmd().withFilter("label", labelValues).exec().getVolumes()) + .isEmpty(); + }); + } + + @SneakyThrows + private Map runProcess(Consumer processExecutorConsumer) { + ProcessExecutor processExecutor = new ProcessExecutor( + new File(System.getProperty("java.home")).toPath().resolve("bin").resolve("java").toString(), + "-ea", + "-classpath", + System.getProperty("java.class.path"), + SimpleUsage.class.getName() + ); + processExecutor.readOutput(true); + processExecutor.redirectOutput(System.out); + processExecutor.redirectError(System.err); + processExecutorConsumer.accept(processExecutor); + + ProcessResult result = processExecutor.execute(); + assertEquals(0, result.getExitValue()); + + String labelsJson = Stream.of(result.outputUTF8().split("\n")) + .filter(it -> it.startsWith(SimpleUsage.LABELS_MARKER)) + .map(it -> it.substring(SimpleUsage.LABELS_MARKER.length())) + .findFirst() + .get(); + + return new ObjectMapper().readValue(labelsJson, Map.class); + } + + public static class SimpleUsage { + + static final String LABELS_MARKER = "LABELS:"; + + @SneakyThrows + @SuppressWarnings("deprecation") + public static void main(String[] args) { + System.out.println(LABELS_MARKER + new ObjectMapper().writeValueAsString(ResourceReaper.instance().getLabels())); + + GenericContainer container = new GenericContainer<>("testcontainers/helloworld:1.1.0") + .withNetwork(org.testcontainers.containers.Network.newNetwork()) + .withExposedPorts(8080); + + container.start(); + } + } +} From b517804acfdfe0c59f484054e2b66a37c057dd5d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 May 2022 21:07:23 +0000 Subject: [PATCH 2526/5615] Bump cucumber-junit from 7.2.3 to 7.3.4 in /examples Bumps [cucumber-junit](https://github.com/cucumber/cucumber-jvm) from 7.2.3 to 7.3.4. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.2.3...v7.3.4) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-junit dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 78d042b95fd..f23a2d9b7af 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' testImplementation 'io.cucumber:cucumber-java:7.2.3' - testImplementation 'io.cucumber:cucumber-junit:7.2.3' + testImplementation 'io.cucumber:cucumber-junit:7.3.4' testImplementation 'org.testcontainers:selenium' } From b3143fd8009ef82a4ebd9206b73e21da97e868de Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 May 2022 21:07:29 +0000 Subject: [PATCH 2527/5615] Bump r2dbc-mssql in /modules/mssqlserver Bumps [r2dbc-mssql](https://github.com/r2dbc/r2dbc-mssql) from 0.8.7.RELEASE to 0.9.0.RELEASE. - [Release notes](https://github.com/r2dbc/r2dbc-mssql/releases) - [Changelog](https://github.com/r2dbc/r2dbc-mssql/blob/main/CHANGELOG) - [Commits](https://github.com/r2dbc/r2dbc-mssql/compare/v0.8.7.RELEASE...v0.9.0.RELEASE) --- updated-dependencies: - dependency-name: io.r2dbc:r2dbc-mssql dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/mssqlserver/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index dfa15ed1c2a..e7a654e12f1 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -7,7 +7,7 @@ dependencies { api project(':jdbc') compileOnly project(':r2dbc') - compileOnly 'io.r2dbc:r2dbc-mssql:0.8.7.RELEASE' + compileOnly 'io.r2dbc:r2dbc-mssql:0.9.0.RELEASE' testImplementation project(':jdbc-test') testImplementation 'com.microsoft.sqlserver:mssql-jdbc:11.1.1.jre8-preview' From e9e602b3aac9a72c6c7b4d93af62e4cc7d3acd04 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 May 2022 21:07:47 +0000 Subject: [PATCH 2528/5615] Bump awaitility from 4.1.1 to 4.2.0 in /modules/couchbase Bumps [awaitility](https://github.com/awaitility/awaitility) from 4.1.1 to 4.2.0. - [Release notes](https://github.com/awaitility/awaitility/releases) - [Changelog](https://github.com/awaitility/awaitility/blob/master/changelog.txt) - [Commits](https://github.com/awaitility/awaitility/compare/awaitility-4.1.1...awaitility-4.2.0) --- updated-dependencies: - dependency-name: org.awaitility:awaitility dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index f1e41fcbe11..71844a30d20 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -6,5 +6,5 @@ dependencies { shaded 'com.squareup.okhttp3:okhttp:3.14.9' testImplementation 'com.couchbase.client:java-client:3.3.0' - testImplementation 'org.awaitility:awaitility:4.1.1' + testImplementation 'org.awaitility:awaitility:4.2.0' } From 07543ffad63bb842279abaf5dfecb9974ed2d667 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 May 2022 21:07:49 +0000 Subject: [PATCH 2529/5615] Bump org.jetbrains.kotlin.plugin.spring in /examples Bumps org.jetbrains.kotlin.plugin.spring from 1.6.20 to 1.6.21. --- updated-dependencies: - dependency-name: org.jetbrains.kotlin.plugin.spring dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index b6dd68b33cd..4f3a50de21a 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,7 +1,7 @@ plugins { id("org.springframework.boot") version "2.6.4" id("org.jetbrains.kotlin.jvm") version "1.6.21" - id("org.jetbrains.kotlin.plugin.spring") version "1.6.20" + id("org.jetbrains.kotlin.plugin.spring") version "1.6.21" } apply plugin: 'io.spring.dependency-management' From e94eaa11e65b984b6af7e21e98b1e02aade21282 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 May 2022 21:07:56 +0000 Subject: [PATCH 2530/5615] Bump r2dbc-postgresql in /modules/postgresql Bumps [r2dbc-postgresql](https://github.com/r2dbc/r2dbc-postgresql) from 0.8.11.RELEASE to 0.8.12.RELEASE. - [Release notes](https://github.com/r2dbc/r2dbc-postgresql/releases) - [Changelog](https://github.com/pgjdbc/r2dbc-postgresql/blob/v0.8.12.RELEASE/CHANGELOG) - [Commits](https://github.com/r2dbc/r2dbc-postgresql/compare/v0.8.11.RELEASE...v0.8.12.RELEASE) --- updated-dependencies: - dependency-name: io.r2dbc:r2dbc-postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index f2c50c30754..c0628afc63a 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -7,7 +7,7 @@ dependencies { api project(':jdbc') compileOnly project(':r2dbc') - compileOnly 'io.r2dbc:r2dbc-postgresql:0.8.11.RELEASE' + compileOnly 'io.r2dbc:r2dbc-postgresql:0.8.12.RELEASE' testImplementation project(':jdbc-test') testImplementation project(':test-support') From 7a957d5819a3fa6964db285a46eb77c0b9360163 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 17 May 2022 18:02:08 -0400 Subject: [PATCH 2531/5615] Remove the disk space check (#5381) --- core/build.gradle | 4 +- .../testcontainers/DockerClientFactory.java | 102 +----------------- .../utility/RyukResourceReaper.java | 8 +- .../DockerClientFactoryTest.java | 11 -- 4 files changed, 8 insertions(+), 117 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index 2a81eb8568a..efb08991f8d 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -40,7 +40,9 @@ tasks.japicmp { "org.testcontainers.shaded.*", ] - classExcludes = [] + classExcludes = [ + "org.testcontainers.utility.RyukResourceReaper", + ] methodExcludes = [ "org.testcontainers.containers.Container#getDockerClient()", diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index cc85645b2b4..c1e999d392a 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -2,7 +2,6 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.DockerClientDelegate; -import com.github.dockerjava.api.async.ResultCallback; import com.github.dockerjava.api.command.CreateContainerCmd; import com.github.dockerjava.api.command.PullImageCmd; import com.github.dockerjava.api.exception.DockerClientException; @@ -10,7 +9,6 @@ import com.github.dockerjava.api.exception.NotFoundException; import com.github.dockerjava.api.model.AccessMode; import com.github.dockerjava.api.model.Bind; -import com.github.dockerjava.api.model.Frame; import com.github.dockerjava.api.model.Info; import com.github.dockerjava.api.model.Version; import com.github.dockerjava.api.model.Volume; @@ -31,18 +29,14 @@ import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MountableFile; import org.testcontainers.utility.ResourceReaper; -import org.testcontainers.utility.RyukResourceReaper; import org.testcontainers.utility.TestcontainersConfiguration; -import java.io.ByteArrayOutputStream; -import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.ServiceLoader; import java.util.UUID; import java.util.function.BiFunction; @@ -80,16 +74,10 @@ public class DockerClientFactory { RuntimeException cachedClientFailure; private String activeApiVersion; - private String activeExecutionDriver; @Getter(lazy = true) private final boolean fileMountingSupported = checkMountableFile(); - - static { - System.setProperty("org.testcontainers.shaded.io.netty.packagePrefix", "org.testcontainers.shaded."); - } - @VisibleForTesting DockerClientFactory() { @@ -200,20 +188,19 @@ public void close() { log.info("Docker host IP address is {}", strategy.getDockerHostIpAddress()); Info dockerInfo = strategy.getInfo(); + log.debug("Docker info: {}", dockerInfo.getRawValues()); Version version = client.versionCmd().exec(); + log.debug("Docker version: {}", version.getRawValues()); activeApiVersion = version.getApiVersion(); - activeExecutionDriver = dockerInfo.getExecutionDriver(); log.info("Connected to docker: \n" + " Server Version: " + dockerInfo.getServerVersion() + "\n" + " API Version: " + activeApiVersion + "\n" + " Operating System: " + dockerInfo.getOperatingSystem() + "\n" + " Total Memory: " + dockerInfo.getMemTotal() / (1024 * 1024) + " MB"); - final ResourceReaper resourceReaper; try { - resourceReaper = ResourceReaper.instance(); //noinspection deprecation - resourceReaper.init(); + ResourceReaper.instance().init(); } catch (RuntimeException e) { cachedClientFailure = e; throw e; @@ -226,27 +213,6 @@ public void close() { try { log.info("Checking the system..."); checkDockerVersion(version.getVersion()); - - //noinspection deprecation - String ryukContainerId = resourceReaper instanceof RyukResourceReaper - ? ((RyukResourceReaper) resourceReaper).getContainerId() - : null; - - if (ryukContainerId != null) { - checkDiskSpace(client, ryukContainerId); - } else { - runInsideDocker( - createContainerCmd -> { - createContainerCmd.withName("testcontainers-checks-" + SESSION_ID); - createContainerCmd.getHostConfig().withAutoRemove(true); - createContainerCmd.withCmd("tail", "-f", "/dev/null"); - }, - (__, containerId) -> { - checkDiskSpace(client, containerId); - return ""; - } - ); - } } catch (RuntimeException e) { cachedClientFailure = e; throw e; @@ -263,43 +229,6 @@ private void checkDockerVersion(String dockerVersion) { check("Docker server version should be at least 1.6.0", versionIsSufficient); } - private void checkDiskSpace(DockerClient dockerClient, String id) { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - - try { - dockerClient - .execStartCmd(dockerClient.execCreateCmd(id).withAttachStdout(true).withCmd("df", "-P").exec().getId()) - .exec(new ResultCallback.Adapter() { - @Override - public void onNext(Frame frame) { - if (frame == null) { - return; - } - switch (frame.getStreamType()) { - case RAW: - case STDOUT: - try { - outputStream.write(frame.getPayload()); - outputStream.flush(); - } catch (IOException e) { - onError(e); - } - } - } - }) - .awaitCompletion(); - } catch (Exception e) { - log.debug("Can't exec disk checking command", e); - } - - DiskSpaceUsage df = parseAvailableDiskSpace(outputStream.toString()); - - check( - "Docker environment should have more than 2GB free disk space", - df.availableMB.map(it -> it >= 2048).orElse(true) - ); - } - private void check(String message, boolean isSuccessful) { if (isSuccessful) { log.info("\u2714\ufe0e {}", message); @@ -389,28 +318,6 @@ T runInsideDocker(DockerImageName imageName, Consumer cr } } - @VisibleForTesting - static class DiskSpaceUsage { - Optional availableMB = Optional.empty(); - Optional usedPercent = Optional.empty(); - } - - @VisibleForTesting - DiskSpaceUsage parseAvailableDiskSpace(String dfOutput) { - DiskSpaceUsage df = new DiskSpaceUsage(); - String[] lines = dfOutput.split("\n"); - for (String line : lines) { - String[] fields = line.split("\\s+"); - if (fields.length > 5 && fields[5].equals("/")) { - long availableKB = Long.parseLong(fields[3]); - df.availableMB = Optional.of(availableKB / 1024L); - df.usedPercent = Optional.of(Integer.valueOf(fields[4].replace("%", ""))); - break; - } - } - return df; - } - /** * @return the docker API version of the daemon that we have connected to */ @@ -423,8 +330,7 @@ public String getActiveApiVersion() { * @return the docker execution driver of the daemon that we have connected to */ public String getActiveExecutionDriver() { - client(); - return activeExecutionDriver; + return getInfo().getExecutionDriver(); } /** diff --git a/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java b/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java index c2e2cf4ef97..6ce9c372fd2 100644 --- a/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java @@ -21,11 +21,9 @@ * Ryuk-based {@link ResourceReaper} implementation. * * @see moby-ryuk - * @deprecated internal API */ -@Deprecated @Slf4j -public class RyukResourceReaper extends ResourceReaper { +class RyukResourceReaper extends ResourceReaper { private static final RateLimiter RYUK_ACK_RATE_LIMITER = RateLimiterBuilder .newBuilder() @@ -37,10 +35,6 @@ public class RyukResourceReaper extends ResourceReaper { private final RyukContainer ryukContainer = new RyukContainer(); - public String getContainerId() { - return ryukContainer.getContainerId(); - } - @Override public void init() { if (!TestcontainersConfiguration.getInstance().environmentSupportsReuse()) { diff --git a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java index 247a16a76a6..a889764579f 100644 --- a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java +++ b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java @@ -2,8 +2,6 @@ import org.junit.Rule; import org.junit.Test; -import org.rnorth.visibleassertions.VisibleAssertions; -import org.testcontainers.DockerClientFactory.DiskSpaceUsage; import org.testcontainers.dockerclient.LogToStringContainerCallback; import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MockTestcontainersConfigurationRule; @@ -39,15 +37,6 @@ public void runCommandInsideDockerShouldNotFailIfImageDoesNotExistsLocally() { } } - @Test - public void shouldHandleBigDiskSize() throws Exception { - String dfOutput = "/dev/disk1 2982480572 1491240286 2982480572 31% /"; - DiskSpaceUsage usage = DockerClientFactory.instance().parseAvailableDiskSpace(dfOutput); - - VisibleAssertions.assertEquals("Available MB is correct", 2982480572L / 1024L, usage.availableMB.orElse(0L)); - VisibleAssertions.assertEquals("Available percentage is correct", 31, usage.usedPercent.orElse(0)); - } - @Test public void dockerHostIpAddress() { DockerClientFactory instance = new DockerClientFactory(); From d9fa5130435d6bcd5112b864349924ad5d2cdbae Mon Sep 17 00:00:00 2001 From: Wenjun Ruan Date: Wed, 18 May 2022 06:56:00 +0800 Subject: [PATCH 2532/5615] Add SeaTunnel to who is using testcontainers (#5272) --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 8749e3678cf..b2c11d6d8e8 100644 --- a/docs/index.md +++ b/docs/index.md @@ -200,7 +200,7 @@ A huge thank you to our sponsors: * [Alkira](https://www.alkira.com/) - Testing of multiple micro-services using Kafka, PostgreSQL, Apache Zookeeper, Etcd and so on. * [Togglz](https://www.togglz.org/) - Feature Flags for the Java platform * [Byzer](https://www.byzer.org/home) - Integration tests for Data and AI platforms are based on multiple versions of Byzer, Ray and Apache Spark. - +* [Apache SeaTunnel](https://github.com/apache/incubator-seatunnel) - Integration testing with different datasource. ## License From 0073001e603ac8bd583a82f81c2a077251156c5d Mon Sep 17 00:00:00 2001 From: Denis <13610290+cac03@users.noreply.github.com> Date: Wed, 18 May 2022 02:24:11 +0300 Subject: [PATCH 2533/5615] Remove "Linux - Travis CI" from docs (#4374) --- docs/supported_docker_environment/index.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/supported_docker_environment/index.md b/docs/supported_docker_environment/index.md index 62a2bbe0af2..3b82cea2165 100644 --- a/docs/supported_docker_environment/index.md +++ b/docs/supported_docker_environment/index.md @@ -5,7 +5,6 @@ | Host Operating System / Environment | Minimum recommended docker versions | Known issues / tips | |-------------------------------------|-----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Linux - general | Docker v17.09 | | -| Linux - Travis CI | Docker v17.09 | See [example .travis.yml](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/.travis.yml) for baseline Travis CI configuration | | Linux - CircleCI (LXC driver) | Docker v17.09 | The `exec` feature is not compatible with CircleCI. See CircleCI configuration [example](./continuous_integration/circle_ci.md) | | Linux - within a Docker container | Docker v17.09 | See [Running inside Docker](continuous_integration/dind_patterns.md) for Docker-in-Docker and Docker wormhole patterns | | Mac OS X - Docker Toolbox | Docker Machine v0.8.0 | | From 55334479422b97fea8110a0a59bf64127e29a6ae Mon Sep 17 00:00:00 2001 From: John Athanaselis Date: Wed, 18 May 2022 12:53:27 +0300 Subject: [PATCH 2534/5615] K3sContainer: expose kubeConfig for docker network access (#5097) Co-authored-by: Sergei Egorov --- .../org/testcontainers/k3s/K3sContainer.java | 63 ++++++++++++++----- .../k3s/KubectlContainerTest.java | 45 +++++++++++++ 2 files changed, 93 insertions(+), 15 deletions(-) create mode 100644 modules/k3s/src/test/java/org/testcontainers/k3s/KubectlContainerTest.java diff --git a/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java b/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java index 54cb2ff500f..7f21ca7dbb8 100644 --- a/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java +++ b/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java @@ -8,23 +8,29 @@ import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.model.DockerObjectAccessor; import lombok.SneakyThrows; +import org.apache.commons.io.IOUtils; import org.testcontainers.containers.BindMode; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.utility.DockerImageName; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; public class K3sContainer extends GenericContainer { + public static int KUBE_SECURE_PORT = 6443; + + public static int RANCHER_WEBHOOK_PORT = 8443; + private String kubeConfigYaml; public K3sContainer(DockerImageName dockerImageName) { super(dockerImageName); dockerImageName.assertCompatibleWith(DockerImageName.parse("rancher/k3s")); - addExposedPorts(6443, 8443); + addExposedPorts(KUBE_SECURE_PORT, RANCHER_WEBHOOK_PORT); setPrivilegedMode(true); withCreateContainerCmdModifier(it -> { it.getHostConfig().withCgroupnsMode("host"); @@ -44,30 +50,57 @@ public K3sContainer(DockerImageName dockerImageName) { setWaitStrategy(new LogMessageWaitStrategy().withRegEx(".*Node controller sync successful.*")); } - @SneakyThrows @Override protected void containerIsStarted(InspectContainerResponse containerInfo) { + String rawKubeConfig = copyFileFromContainer("/etc/rancher/k3s/k3s.yaml", + is -> IOUtils.toString(is, StandardCharsets.UTF_8) + ); + String serverUrl = "https://" + this.getHost() + ":" + this.getMappedPort(KUBE_SECURE_PORT); + kubeConfigYaml = kubeConfigWithServerUrl(rawKubeConfig, serverUrl); + } + + /** + * Return the kubernetes client configuration to access k3s from the host machine. + * + * @return the kubeConfig yaml. + */ + public String getKubeConfigYaml() { + return kubeConfigYaml; + } + + /** + * Generate a kubernetes client configuration for use on a docker internal network. The kubeConfig can be used by + * another docker container running in the same network as the k3s container. For access from the host, use + * the {@link #getKubeConfigYaml()} method instead. + * + * @param networkAlias a valid network alias of the k3s container. + * @return the kubeConfig yaml. + */ + public String generateInternalKubeConfigYaml(String networkAlias) { + if (this.getNetworkAliases().contains(networkAlias)) { + String serverUrl = "https://" + networkAlias + ":" + KUBE_SECURE_PORT; + return kubeConfigWithServerUrl(kubeConfigYaml, serverUrl); + } else { + throw new IllegalArgumentException(networkAlias + " is not a network alias for k3s container"); + } + } + + @SneakyThrows + private String kubeConfigWithServerUrl(String kubeConfigYaml, String serverUrl) { ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory()); - ObjectNode rawKubeConfig = copyFileFromContainer( - "/etc/rancher/k3s/k3s.yaml", - is -> objectMapper.readValue(is, ObjectNode.class) - ); + ObjectNode kubeConfigObjectNode = objectMapper.readValue(kubeConfigYaml, ObjectNode.class); - JsonNode clusterNode = rawKubeConfig.at("/clusters/0/cluster"); + JsonNode clusterNode = kubeConfigObjectNode.at("/clusters/0/cluster"); if (!clusterNode.isObject()) { throw new IllegalStateException("'/clusters/0/cluster' expected to be an object"); } ObjectNode clusterConfig = (ObjectNode) clusterNode; + clusterConfig.replace("server", new TextNode(serverUrl)); - clusterConfig.replace("server", new TextNode("https://" + this.getHost() + ":" + this.getMappedPort(6443))); + kubeConfigObjectNode.set("current-context", new TextNode("default")); - rawKubeConfig.set("current-context", new TextNode("default")); - - kubeConfigYaml = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(rawKubeConfig); - } - - public String getKubeConfigYaml() { - return kubeConfigYaml; + return objectMapper.writerWithDefaultPrettyPrinter() + .writeValueAsString(kubeConfigObjectNode); } } diff --git a/modules/k3s/src/test/java/org/testcontainers/k3s/KubectlContainerTest.java b/modules/k3s/src/test/java/org/testcontainers/k3s/KubectlContainerTest.java new file mode 100644 index 00000000000..5c0d44ff0f1 --- /dev/null +++ b/modules/k3s/src/test/java/org/testcontainers/k3s/KubectlContainerTest.java @@ -0,0 +1,45 @@ +package org.testcontainers.k3s; + +import org.junit.ClassRule; +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.Network; +import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; +import org.testcontainers.images.builder.Transferable; +import org.testcontainers.utility.DockerImageName; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; + +public class KubectlContainerTest { + + public static Network network = Network.SHARED; + + @ClassRule + public static K3sContainer k3s = new K3sContainer(DockerImageName.parse("rancher/k3s:v1.21.3-k3s1")) + .withNetwork(network) + .withNetworkAliases("k3s"); + + @Test + public void shouldExposeKubeConfigForNetworkAlias() throws Exception { + String kubeConfigYaml = k3s.generateInternalKubeConfigYaml("k3s"); + + try ( + GenericContainer kubectlContainer = new GenericContainer<>("rancher/kubectl:v1.23.3") + .withNetwork(network) + .withCopyToContainer(Transferable.of(kubeConfigYaml), "/.kube/config") + .withCommand("get namespaces") + .withStartupCheckStrategy(new OneShotStartupCheckStrategy().withTimeout(Duration.ofSeconds(30))) + ) { + kubectlContainer.start(); + + assertThat(kubectlContainer.getLogs()).contains("kube-system"); + } + } + + @Test(expected = IllegalArgumentException.class) + public void shouldThrowAnExceptionForUnknownNetworkAlias() { + k3s.generateInternalKubeConfigYaml("not-set-network-alias"); + } +} From ada816c136d6a46c77d301b1a85968ae5d4ae430 Mon Sep 17 00:00:00 2001 From: edee111 <4160602+edee111@users.noreply.github.com> Date: Wed, 18 May 2022 11:59:44 +0200 Subject: [PATCH 2535/5615] Removal of containers from the cache is not synchronized (#4598) Co-authored-by: Eduard Tomek --- .../org/testcontainers/jdbc/ContainerDatabaseDriver.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java index c1b4fc29067..4b3a7ba98f7 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java @@ -162,8 +162,10 @@ private Connection wrapConnection(final Connection connection, final JdbcDatabas return new ConnectionWrapper(connection, () -> { finalConnections.remove(connection); if (!isDaemon && finalConnections.isEmpty()) { - container.stop(); - jdbcUrlContainerCache.remove(connectionUrl.getUrl()); + synchronized (jdbcUrlContainerCache) { + container.stop(); + jdbcUrlContainerCache.remove(connectionUrl.getUrl()); + } } }); } From b8d75b153b61af01127b0fb8a09405d6624a8ab8 Mon Sep 17 00:00:00 2001 From: Jurrian Fahner Date: Wed, 18 May 2022 18:30:34 +0200 Subject: [PATCH 2536/5615] Add docs for using Gitlab CI with Docker wormhole executor (#4770) --- .../continuous_integration/gitlab_ci.md | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/docs/supported_docker_environment/continuous_integration/gitlab_ci.md b/docs/supported_docker_environment/continuous_integration/gitlab_ci.md index 5e701007ab0..b4571403654 100644 --- a/docs/supported_docker_environment/continuous_integration/gitlab_ci.md +++ b/docs/supported_docker_environment/continuous_integration/gitlab_ci.md @@ -1,5 +1,36 @@ # GitLab CI +## Example using Docker socket +This applies if you have your own GitlabCI runner installed, use the Docker executor and you have `/var/run/docker.sock` mounted in the runner configuration. + +See below for an example runner configuration: +```toml +[[runners]] + name = "MACHINE_NAME" + url = "https://gitlab.com/" + token = "GENERATED_GITLAB_RUNNER_TOKEN" + executor = "docker" + [runners.docker] + tls_verify = false + image = "docker:latest" + privileged = false + disable_entrypoint_overwrite = false + oom_kill_disable = false + disable_cache = false + volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"] + shm_size = 0 +``` + +Please also include the following in your GitlabCI pipeline definitions (`.gitlab-ci.yml`) that use Testcontainers: +```yml +variables: + TESTCONTAINERS_HOST_OVERRIDE: "host.docker.internal" +``` + +The environment variable `TESTCONTAINERS_HOST_OVERRIDE` needs to be configured, otherwise, a wrong IP address would be used to resolve the Docker host, which will likely lead to failing tests. + +## Example using DinD (Docker-in-Docker) + In order to use Testcontainers in a Gitlab CI pipeline, you need to run the job as a Docker container (see [Patterns for running inside Docker](dind_patterns.md)). So edit your `.gitlab-ci.yml` to include the [Docker-In-Docker service](https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#use-docker-in-docker-workflow-with-docker-executor) (`docker:dind`) and set the `DOCKER_HOST` variable to `tcp://docker:2375` and `DOCKER_TLS_CERTDIR` to empty string. From 6dc05d30cccd8211728e6082f3aa11c6964d0967 Mon Sep 17 00:00:00 2001 From: Tobias Stadler Date: Wed, 18 May 2022 18:52:15 +0200 Subject: [PATCH 2537/5615] Reset driver and vncRecordingContainer when Selenium container stops (#5116) --- .../testcontainers/containers/BrowserWebDriverContainer.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index 12bbbc07de5..fc3778385cd 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -318,6 +318,7 @@ public void stop() { } catch (Exception e) { LOGGER.debug("Failed to quit the driver", e); } + driver = null; } if (vncRecordingContainer != null) { @@ -326,6 +327,7 @@ public void stop() { } catch (Exception e) { LOGGER.debug("Failed to stop vncRecordingContainer", e); } + vncRecordingContainer = null; } super.stop(); From ea7b29baac0422c0c7a58554a49b393b7ebcc8b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Thu, 19 May 2022 12:58:16 -0500 Subject: [PATCH 2538/5615] Upgrade lombok to be compatible against java 17 (#5388) --- build.gradle | 4 ++-- examples/kafka-cluster/build.gradle | 2 +- examples/solr-container/build.gradle | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 309743477c1..96e1940eba5 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ buildscript { } plugins { - id 'io.franzbecker.gradle-lombok' version '4.0.0' + id 'io.franzbecker.gradle-lombok' version '5.0.0' id 'com.github.johnrengelman.shadow' version '7.0.0' id 'me.champeau.gradle.japicmp' version '0.2.9' apply false } @@ -40,7 +40,7 @@ subprojects { } lombok { - version = '1.18.20' + version = '1.18.24' } diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index ee8035ea51c..db89b4335fe 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { testCompileOnly "org.projectlombok:lombok:1.18.24" - testAnnotationProcessor "org.projectlombok:lombok:1.18.22" + testAnnotationProcessor "org.projectlombok:lombok:1.18.24" testImplementation 'org.testcontainers:kafka' testImplementation 'org.apache.kafka:kafka-clients:3.1.0' testImplementation 'org.assertj:assertj-core:3.22.0' diff --git a/examples/solr-container/build.gradle b/examples/solr-container/build.gradle index 3f3366f9d46..abb7c12e0ed 100644 --- a/examples/solr-container/build.gradle +++ b/examples/solr-container/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly "org.projectlombok:lombok:1.18.24" - annotationProcessor "org.projectlombok:lombok:1.18.22" + annotationProcessor "org.projectlombok:lombok:1.18.24" implementation 'org.apache.solr:solr-solrj:8.11.1' From fffab559a25d2e0501ab721182bc9cbdcb722506 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Thu, 19 May 2022 13:32:08 -0500 Subject: [PATCH 2539/5615] Fix TestcontainersTestDescription filename (#5387) --- ...nerTestDescription.java => TestcontainersTestDescription.java} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/{TestcontainerTestDescription.java => TestcontainersTestDescription.java} (100%) diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainerTestDescription.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersTestDescription.java similarity index 100% rename from modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainerTestDescription.java rename to modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersTestDescription.java From b04540b2cafbb25a580595d2d7832781a1de9825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Fri, 20 May 2022 18:14:30 -0500 Subject: [PATCH 2540/5615] Add spotless and checkstyle (#5383) Co-authored-by: Sergei Egorov --- build.gradle | 31 +++++++++++++++++++++ config/checkstyle/checkstyle.xml | 47 ++++++++++++++++++++++++++++++++ docs/contributing.md | 1 + gradle/ci-support.gradle | 2 +- modules/neo4j/build.gradle | 2 +- 5 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 config/checkstyle/checkstyle.xml diff --git a/build.gradle b/build.gradle index 96e1940eba5..adbd1a47c4a 100644 --- a/build.gradle +++ b/build.gradle @@ -13,6 +13,7 @@ plugins { id 'io.franzbecker.gradle-lombok' version '5.0.0' id 'com.github.johnrengelman.shadow' version '7.0.0' id 'me.champeau.gradle.japicmp' version '0.2.9' apply false + id 'com.diffplug.spotless' version '6.3.0' apply false } apply from: "$rootDir/gradle/ci-support.gradle" @@ -22,6 +23,8 @@ subprojects { apply plugin: 'idea' apply plugin: 'io.franzbecker.gradle-lombok' apply from: "$rootDir/gradle/shading.gradle" + apply plugin: 'com.diffplug.spotless' + apply plugin: 'checkstyle' group = "org.testcontainers" @@ -105,4 +108,32 @@ subprojects { dependencies { testImplementation 'ch.qos.logback:logback-classic:1.2.3' } + + spotless { + enforceCheck false + java { + removeUnusedImports() + trimTrailingWhitespace() + endWithNewline() + + prettier(['prettier': '2.5.1', 'prettier-plugin-java': '1.6.1']) + .config([ + 'parser' : 'java', + 'tabWidth' : 4, + 'printWidth': 120 + ]) + + importOrder('', 'java', 'javax', '\\#') + } + groovyGradle { + target '**/*.groovy' + greclipse('4.19.0') + } + } + + checkstyle { + toolVersion = "9.3" + configFile = rootProject.file('config/checkstyle/checkstyle.xml') + ignoreFailures = true + } } diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml new file mode 100644 index 00000000000..39598dae2e4 --- /dev/null +++ b/config/checkstyle/checkstyle.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + diff --git a/docs/contributing.md b/docs/contributing.md index 6cd3e2719f8..01c4fce57b6 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -8,6 +8,7 @@ * follow the style, naming and structure conventions of the rest of the project. * make commits atomic and easy to merge. * when updating documentation, please see [our guidance for documentation contributions](contributing_docs.md). + * apply format running `./gradlew spotlessApply` * verify all tests are passing. Build the project with `./gradlew check` to do this. **N.B.** Gradle's Build Cache is enabled by default, but you can add `--no-build-cache` flag to disable it. diff --git a/gradle/ci-support.gradle b/gradle/ci-support.gradle index b878c107796..5b85837361e 100644 --- a/gradle/ci-support.gradle +++ b/gradle/ci-support.gradle @@ -17,7 +17,7 @@ task testMatrix { } } -// If we're executing the `taskMatrix` task, disable tests and other slow tasks +// If we're executing the `testMatrix` task, disable tests and other slow tasks // so that we can get a result quickly. gradle.taskGraph.whenReady { if (it.hasTask(tasks.testMatrix)) { diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 0bdd9ba960c..efba3dd0db5 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -26,7 +26,7 @@ task customNeo4jPluginJar(type: Jar) { outputs.dir(customNeo4jPluginDestinationDir) } -test.dependsOn customNeo4jPluginJar +processTestResources.dependsOn customNeo4jPluginJar dependencies { customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.32" From f60051c0b7f89bed30ddeffd09b8feed0500e342 Mon Sep 17 00:00:00 2001 From: Tobias Stadler Date: Tue, 24 May 2022 20:43:27 +0200 Subject: [PATCH 2541/5615] Use `TestInstances` API from JUnit Jupiter (#2719) Co-authored-by: Richard North --- .../jupiter/TestcontainersExtension.java | 27 +++++-------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java index 1ed5e954e44..990940be14d 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java @@ -12,7 +12,6 @@ import org.junit.jupiter.api.extension.ExtensionContext.Namespace; import org.junit.jupiter.api.extension.ExtensionContext.Store; import org.junit.jupiter.api.extension.ExtensionContext.Store.CloseableResource; -import org.junit.jupiter.api.extension.TestInstancePostProcessor; import org.junit.platform.commons.support.AnnotationSupport; import org.junit.platform.commons.util.AnnotationUtils; import org.junit.platform.commons.util.Preconditions; @@ -23,7 +22,8 @@ import org.testcontainers.lifecycle.TestLifecycleAware; import java.lang.reflect.Field; -import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Optional; @@ -34,20 +34,13 @@ import static java.util.stream.Collectors.toList; import static org.testcontainers.junit.jupiter.FilesystemFriendlyNameGenerator.filesystemFriendlyNameOf; -class TestcontainersExtension implements BeforeEachCallback, BeforeAllCallback, AfterEachCallback, AfterAllCallback, ExecutionCondition, TestInstancePostProcessor { +class TestcontainersExtension implements BeforeEachCallback, BeforeAllCallback, AfterEachCallback, AfterAllCallback, ExecutionCondition { private static final Namespace NAMESPACE = Namespace.create(TestcontainersExtension.class); - private static final String TEST_INSTANCE = "testInstance"; private static final String SHARED_LIFECYCLE_AWARE_CONTAINERS = "sharedLifecycleAwareContainers"; private static final String LOCAL_LIFECYCLE_AWARE_CONTAINERS = "localLifecycleAwareContainers"; - @Override - public void postProcessTestInstance(final Object testInstance, final ExtensionContext context) { - Store store = context.getStore(NAMESPACE); - store.put(TEST_INSTANCE, testInstance); - } - @Override public void beforeAll(ExtensionContext context) { Class testClass = context.getTestClass() @@ -156,17 +149,9 @@ boolean isDockerAvailable() { } private Set collectParentTestInstances(final ExtensionContext context) { - Set testInstances = new LinkedHashSet<>(); - Optional current = Optional.of(context); - while (current.isPresent()) { - ExtensionContext ctx = current.get(); - Object testInstance = ctx.getStore(NAMESPACE).remove(TEST_INSTANCE); - if (testInstance != null) { - testInstances.add(testInstance); - } - current = ctx.getParent(); - } - return testInstances; + List allInstances = new ArrayList<>(context.getRequiredTestInstances().getAllInstances()); + Collections.reverse(allInstances); + return new LinkedHashSet<>(allInstances); } private List findSharedContainers(Class testClass) { From 2bdbc8cd6d42cb5dc12165617e72abfa0dee2fec Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Tue, 24 May 2022 21:07:58 +0200 Subject: [PATCH 2542/5615] Don't delete images if `deleteOnExit` is false (#5391) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Eddú Meléndez Gonzales --- .../images/builder/ImageFromDockerfile.java | 10 +++++----- .../builder/ImageFromDockerfileTest.java | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java index 281a74feaba..000487bfa4d 100644 --- a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java +++ b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java @@ -87,9 +87,6 @@ protected final String resolve() { DockerClient dockerClient = DockerClientFactory.instance().client(); try { - if (deleteOnExit) { - ResourceReaper.instance().registerImageForCleanup(dockerImageName); - } BuildImageResultCallback resultCallback = new BuildImageResultCallback() { @Override @@ -114,9 +111,12 @@ public void onNext(BuildResponseItem item) { if (buildImageCmd.getLabels() != null) { labels.putAll(buildImageCmd.getLabels()); } + labels.putAll(DockerClientFactory.DEFAULT_LABELS); - //noinspection deprecation - labels.putAll(ResourceReaper.instance().getLabels()); + if (deleteOnExit) { + //noinspection deprecation + labels.putAll(ResourceReaper.instance().getLabels()); + } buildImageCmd.withLabels(labels); prePullDependencyImages(dependencyImageNames); diff --git a/core/src/test/java/org/testcontainers/images/builder/ImageFromDockerfileTest.java b/core/src/test/java/org/testcontainers/images/builder/ImageFromDockerfileTest.java index 93ec5cb5cd1..da3509cea7b 100644 --- a/core/src/test/java/org/testcontainers/images/builder/ImageFromDockerfileTest.java +++ b/core/src/test/java/org/testcontainers/images/builder/ImageFromDockerfileTest.java @@ -4,6 +4,7 @@ import com.github.dockerjava.api.command.InspectImageResponse; import org.junit.Test; import org.testcontainers.DockerClientFactory; +import org.testcontainers.utility.Base58; import static org.assertj.core.api.Assertions.assertThat; @@ -24,4 +25,22 @@ public void shouldAddDefaultLabels() { .containsAllEntriesOf(DockerClientFactory.DEFAULT_LABELS); } + @Test + public void shouldNotAddSessionLabelIfDeleteOnExitIsFalse() { + ImageFromDockerfile image = new ImageFromDockerfile("localhost/testcontainers/" + Base58.randomString(16).toLowerCase(), false) + .withDockerfileFromBuilder(it -> it.from("scratch")); + String imageId = image.resolve(); + DockerClient dockerClient = DockerClientFactory.instance().client(); + + try { + InspectImageResponse inspectImageResponse = dockerClient.inspectImageCmd(imageId).exec(); + assertThat(inspectImageResponse.getConfig().getLabels()) + .doesNotContainKey(DockerClientFactory.TESTCONTAINERS_SESSION_ID_LABEL); + } finally { + // ensure the image is deleted, even if the test fails + dockerClient.removeImageCmd(imageId).exec(); + } + + } + } From de1324ed2800eff4da326d0c23d281399d006bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Tue, 24 May 2022 23:31:37 -0500 Subject: [PATCH 2543/5615] Apply spotless and checkstyle (#5390) --- .github/workflows/ci-docker-wormhole.yml | 1 + build.gradle | 11 +- config/checkstyle/checkstyle.xml | 8 +- .../testcontainers/AbstractJarFileTest.java | 5 +- .../testcontainers/JarFileShadingTest.java | 24 +- .../testcontainers/PublicBinaryAPITest.java | 76 +- .../testcontainers/DockerClientFactory.java | 93 ++- .../org/testcontainers/Testcontainers.java | 15 +- .../java/org/testcontainers/UnstableAPI.java | 6 +- .../testcontainers/containers/BindMode.java | 3 +- .../ComposeServiceWaitStrategyTarget.java | 15 +- .../testcontainers/containers/Container.java | 22 +- .../containers/ContainerFetchException.java | 1 + .../containers/ContainerState.java | 42 +- .../containers/DockerComposeContainer.java | 236 ++++-- .../containers/DockerComposeFiles.java | 4 +- .../containers/ExecInContainerPattern.java | 38 +- .../FailureDetectingExternalResource.java | 16 +- .../FixedHostPortGenericContainer.java | 5 +- .../containers/FutureContainer.java | 1 + .../containers/GenericContainer.java | 323 ++++---- .../containers/InternetProtocol.java | 1 - .../testcontainers/containers/Network.java | 1 - .../containers/ParsedDockerComposeFile.java | 65 +- .../containers/PortForwardingContainer.java | 24 +- .../containers/SelinuxContext.java | 1 - .../containers/SocatContainer.java | 16 +- .../containers/VncRecordingContainer.java | 55 +- .../containers/output/BaseConsumer.java | 1 + .../output/FrameConsumerResultCallback.java | 4 +- .../containers/output/OutputFrame.java | 19 +- .../containers/output/Slf4jLogConsumer.java | 10 +- .../containers/output/ToStringConsumer.java | 1 + .../containers/output/WaitingConsumer.java | 4 +- ...finiteWaitOneShotStartupCheckStrategy.java | 1 + ...umDurationRunningStartupCheckStrategy.java | 1 - .../startupcheck/StartupCheckStrategy.java | 51 +- .../containers/traits/LinkableContainer.java | 1 - .../internal/ExternalPortListeningCheck.java | 20 +- .../InternalCommandPortListeningCheck.java | 25 +- .../wait/strategy/AbstractWaitStrategy.java | 21 +- .../DockerHealthcheckWaitStrategy.java | 7 +- .../wait/strategy/HostPortWaitStrategy.java | 98 ++- .../wait/strategy/HttpWaitStrategy.java | 207 ++--- .../wait/strategy/LogMessageWaitStrategy.java | 18 +- .../containers/wait/strategy/Wait.java | 9 +- .../wait/strategy/WaitAllStrategy.java | 29 +- .../wait/strategy/WaitStrategy.java | 1 - .../wait/strategy/WaitStrategyTarget.java | 8 +- .../AuditLoggingDockerClient.java | 127 +-- .../AuthDelegatingDockerClientConfig.java | 14 +- .../dockerclient/DockerClientConfigUtils.java | 27 +- .../DockerClientProviderStrategy.java | 139 ++-- .../DockerMachineClientProviderStrategy.java | 21 +- ...dSystemPropertyClientProviderStrategy.java | 11 +- .../HeadersAddingDockerHttpClient.java | 4 +- .../InvalidConfigurationException.java | 2 +- .../LogToStringContainerCallback.java | 1 + .../NpipeSocketClientProviderStrategy.java | 5 +- .../RootlessDockerClientProviderStrategy.java | 20 +- .../UnixSocketClientProviderStrategy.java | 11 +- .../images/AbstractImagePullPolicy.java | 3 +- .../org/testcontainers/images/ImageData.java | 7 +- .../images/ImagePullPolicy.java | 2 - .../images/LocalImagesCache.java | 8 +- .../images/LoggedPullImageResultCallback.java | 38 +- .../images/ParsedDockerfile.java | 17 +- .../org/testcontainers/images/PullPolicy.java | 5 +- .../images/RemoteDockerImage.java | 24 +- ...eLimitedLoggedPullImageResultCallback.java | 39 +- .../images/builder/ImageFromDockerfile.java | 54 +- .../images/builder/Transferable.java | 1 - .../builder/dockerfile/DockerfileBuilder.java | 5 +- .../dockerfile/statement/Statement.java | 2 +- .../dockerfile/traits/AddStatementTrait.java | 1 - .../dockerfile/traits/CmdStatementTrait.java | 1 - .../dockerfile/traits/CopyStatementTrait.java | 1 - .../traits/DockerfileBuilderTrait.java | 1 - .../traits/EntryPointStatementTrait.java | 1 - .../dockerfile/traits/EnvStatementTrait.java | 1 - .../traits/ExposeStatementTrait.java | 8 +- .../dockerfile/traits/FromStatementTrait.java | 1 - .../traits/LabelStatementTrait.java | 1 - .../dockerfile/traits/RunStatementTrait.java | 1 - .../dockerfile/traits/UserStatementTrait.java | 1 - .../traits/VolumeStatementTrait.java | 1 - .../traits/WorkdirStatementTrait.java | 1 - .../traits/BuildContextBuilderTrait.java | 1 - .../images/builder/traits/ClasspathTrait.java | 1 - .../builder/traits/DockerfileTrait.java | 38 +- .../images/builder/traits/FilesTrait.java | 1 - .../images/builder/traits/StringsTrait.java | 29 +- .../testcontainers/lifecycle/Startable.java | 1 - .../testcontainers/lifecycle/Startables.java | 36 +- .../lifecycle/TestDescription.java | 1 - .../lifecycle/TestLifecycleAware.java | 9 +- .../testcontainers/utility/AuditLogger.java | 45 +- .../utility/AuthConfigUtil.java | 9 +- .../org/testcontainers/utility/Base58.java | 3 +- .../utility/ClasspathScanner.java | 6 +- .../testcontainers/utility/CommandLine.java | 14 +- .../utility/ComparableVersion.java | 10 +- ...ConfigurationFileImageNameSubstitutor.java | 3 +- .../utility/DefaultImageNameSubstitutor.java | 13 +- .../utility/DockerImageName.java | 68 +- .../utility/DockerLoggerFactory.java | 1 - .../utility/DockerMachineClient.java | 45 +- .../testcontainers/utility/DockerStatus.java | 22 +- .../utility/DynamicPollInterval.java | 1 + .../utility/ImageNameSubstitutor.java | 40 +- .../utility/JVMHookResourceReaper.java | 21 +- .../testcontainers/utility/LazyFuture.java | 2 +- .../utility/LicenseAcceptance.java | 17 +- .../org/testcontainers/utility/LogUtils.java | 40 +- .../testcontainers/utility/MountableFile.java | 115 ++- .../org/testcontainers/utility/PathUtils.java | 34 +- .../PrefixingImageNameSubstitutor.java | 6 +- .../utility/RegistryAuthLocator.java | 141 ++-- .../utility/ResourceReaper.java | 66 +- .../testcontainers/utility/RyukContainer.java | 11 +- .../utility/RyukResourceReaper.java | 5 +- .../utility/TestEnvironment.java | 4 +- .../utility/TestcontainersConfiguration.java | 95 ++- .../utility/ThrowingFunction.java | 1 - .../testcontainers/utility/Versioning.java | 5 + .../OutOfPackageImagePullPolicyTest.java | 19 +- .../java/org/testcontainers/DaemonTest.java | 10 +- .../DockerClientFactoryTest.java | 8 +- .../DockerRegistryContainer.java | 39 +- .../containers/ContainerStateTest.java | 22 +- ...ockerComposeContainerWithServicesTest.java | 36 +- .../containers/DockerComposeFilesTest.java | 22 +- .../DockerComposeOverridesTest.java | 81 +- .../containers/ExposedHostTest.java | 36 +- .../FailureDetectingExternalResourceTest.java | 1 - .../containers/GenericContainerTest.java | 84 +- .../containers/NetworkTest.java | 73 +- ...ParsedDockerComposeFileValidationTest.java | 69 +- .../containers/ReusabilityUnitTests.java | 170 ++-- .../containers/output/ContainerLogsTest.java | 15 +- .../FrameConsumerResultCallbackTest.java | 100 ++- .../output/ToStringConsumerTest.java | 27 +- .../ExternalPortListeningCheckTest.java | 32 +- ...InternalCommandPortListeningCheckTest.java | 44 +- .../DockerHealthcheckWaitStrategyTest.java | 14 +- .../wait/strategy/WaitAllStrategyTest.java | 61 +- .../dockerclient/AmbiguousImagePullTest.java | 3 +- .../DockerClientConfigUtilsTest.java | 24 +- ...temPropertyClientProviderStrategyTest.java | 77 +- .../dockerclient/EventStreamTest.java | 30 +- .../dockerclient/ImagePullTest.java | 10 +- .../images/AgeBasedPullPolicyTest.java | 12 +- .../testcontainers/images/ImageDataTest.java | 8 +- .../images/ImagePullPolicyTest.java | 46 +- .../images/ParsedDockerfileTest.java | 90 ++- .../images/RemoteDockerImageTest.java | 10 +- .../images/builder/DockerfileBuildTest.java | 47 +- .../images/builder/DockerignoreTest.java | 30 +- .../builder/ImageFromDockerfileTest.java | 10 +- .../statement/AbstractStatementTest.java | 6 +- .../statement/KeyValuesStatementTest.java | 24 +- .../statement/MultiArgsStatementTest.java | 2 +- .../statement/RawStatementTest.java | 3 +- .../SingleArgumentStatementTest.java | 2 +- .../junit/BaseDockerComposeTest.java | 29 +- .../junit/CopyFileToContainerTest.java | 13 +- .../junit/DependenciesTest.java | 26 +- .../DockerComposeContainerScalingTest.java | 17 +- .../junit/DockerComposeContainerTest.java | 39 +- .../DockerComposeContainerWithBuildTest.java | 84 +- ...DockerComposeContainerWithOptionsTest.java | 53 +- .../junit/DockerComposeErrorHandlingTest.java | 14 +- .../junit/DockerComposeLocalImageTest.java | 4 +- .../junit/DockerComposeLogConsumerTest.java | 18 +- .../junit/DockerComposePassthroughTest.java | 20 +- .../junit/DockerComposeServiceTest.java | 13 +- .../junit/DockerComposeV2FormatTest.java | 6 +- .../junit/DockerComposeV2WithNetworkTest.java | 13 +- .../junit/DockerComposeWaitStrategyTest.java | 62 +- .../junit/DockerNetworkModeTest.java | 6 +- .../junit/DockerfileContainerTest.java | 31 +- .../testcontainers/junit/DockerfileTest.java | 68 +- .../junit/FileOperationsTest.java | 32 +- .../junit/FixedHostPortContainerTest.java | 32 +- .../junit/GenericContainerRuleTest.java | 269 ++++--- .../junit/NonExistentImagePullTest.java | 12 +- .../junit/OutputStreamTest.java | 57 +- .../junit/OutputStreamWithTTYTest.java | 43 +- .../ParameterizedDockerfileContainerTest.java | 30 +- .../junit/WorkingDirectoryTest.java | 11 +- .../strategy/AbstractWaitStrategyTest.java | 28 +- .../strategy/HostPortWaitStrategyTest.java | 3 +- .../wait/strategy/HttpWaitStrategyTest.java | 154 ++-- .../strategy/LogMessageWaitStrategyTest.java | 29 +- .../utility/AuthenticatedImagePullTest.java | 65 +- .../utility/ClasspathScannerTest.java | 83 +- .../utility/ComparableVersionTest.java | 24 +- .../DefaultImageNameSubstitutorTest.java | 2 + .../utility/DirectoryTarResourceTest.java | 81 +- .../DockerImageNameCompatibilityTest.java | 13 +- .../utility/DockerImageNameTest.java | 95 ++- .../utility/DockerStatusTest.java | 18 +- .../utility/FakeImageSubstitutor.java | 1 + .../utility/FilterRegistryTest.java | 12 +- .../utility/ImageNameSubstitutorTest.java | 12 +- .../utility/LazyFutureTest.java | 16 +- .../utility/MountableFileTest.java | 33 +- .../PrefixingImageNameSubstitutorTest.java | 51 +- .../utility/RegistryAuthLocatorTest.java | 76 +- .../utility/ResourceReaperTest.java | 27 +- .../TestcontainersConfigurationTest.java | 69 +- .../test/java/generic/CmdModifierTest.java | 15 +- .../src/test/java/generic/CommandsTest.java | 3 +- .../java/generic/ContainerCreationTest.java | 24 +- .../test/java/generic/ContainerLabelTest.java | 2 +- .../java/generic/HostPortExposedTest.java | 24 +- .../generic/ImageNameSubstitutionTest.java | 9 +- .../generic/MultiplePortsExposedTest.java | 13 +- .../test/java/generic/WaitStrategiesTest.java | 29 +- .../StartupCheckStrategyTest.java | 38 +- .../quickstart/RedisBackedCacheIntTest.java | 5 +- .../RedisBackedCacheIntTestStep0.java | 2 +- .../quickstart/RedisBackedCacheIntTest.java | 5 +- .../RedisBackedCacheIntTestStep0.java | 2 +- .../quickstart/RedisBackedCacheIntTest.groovy | 38 +- .../RedisBackedCacheIntTestStep0.groovy | 26 +- docs/modules/hivemq.md | 4 +- .../containers/CosmosDBEmulatorContainer.java | 5 +- .../containers/KeyStoreBuilder.java | 46 +- .../CosmosDBEmulatorContainerTest.java | 22 +- .../containers/CassandraContainer.java | 34 +- .../delegate/CassandraDatabaseDelegate.java | 11 +- .../wait/CassandraQueryWaitStrategy.java | 22 +- .../containers/CassandraContainerTest.java | 24 +- .../containers/ClickHouseContainer.java | 7 +- .../containers/ClickHouseProvider.java | 1 + .../clickhouse/ClickhouseJDBCDriverTest.java | 12 +- .../clickhouse/SimpleClickhouseTest.java | 4 +- .../containers/CockroachContainer.java | 32 +- .../CockroachContainerProvider.java | 1 + .../CockroachDBJDBCDriverTest.java | 12 +- .../cockroachdb/SimpleCockroachDBTest.java | 15 +- .../couchbase/BucketDefinition.java | 6 +- .../couchbase/CouchbaseContainer.java | 333 +++++--- .../couchbase/CouchbaseService.java | 2 - .../couchbase/CouchbaseContainerTest.java | 85 +- .../delegate/AbstractDatabaseDelegate.java | 7 +- .../delegate/DatabaseDelegate.java | 9 +- .../org/testcontainers/ext/ScriptUtils.java | 736 ++++++++++-------- .../ext/ScriptSplittingTest.java | 143 ++-- .../containers/Db2Container.java | 16 +- .../containers/Db2ContainerProvider.java | 1 + .../jdbc/db2/DB2JDBCDriverTest.java | 12 +- .../junit/db2/SimpleDb2Test.java | 17 +- .../dynamodb/DynaliteContainer.java | 22 +- .../dynamodb/DynaliteContainerTest.java | 30 +- .../elasticsearch/ElasticsearchContainer.java | 26 +- .../ElasticsearchContainerTest.java | 215 +++-- .../containers/BigtableEmulatorContainer.java | 9 +- .../DatastoreEmulatorContainer.java | 9 +- .../FirestoreEmulatorContainer.java | 9 +- .../containers/PubSubEmulatorContainer.java | 9 +- .../containers/SpannerEmulatorContainer.java | 9 +- .../BigtableEmulatorContainerTest.java | 61 +- .../DatastoreEmulatorContainerTest.java | 18 +- .../FirestoreEmulatorContainerTest.java | 24 +- .../PubSubEmulatorContainerTest.java | 85 +- .../SpannerEmulatorContainerTest.java | 68 +- .../hivemq/HiveMQContainer.java | 138 ++-- .../hivemq/HiveMQExtension.java | 98 ++- .../hivemq/ContainerWithControlCenterIT.java | 14 +- .../hivemq/ContainerWithCustomConfigIT.java | 25 +- ...ContainerWithExtensionFromDirectoryIT.java | 25 +- .../hivemq/ContainerWithExtensionIT.java | 22 +- .../ContainerWithExtensionSubclassIT.java | 22 +- .../ContainerWithFileInExtensionHomeIT.java | 55 +- .../hivemq/ContainerWithFileInHomeIT.java | 52 +- .../hivemq/ContainerWithLicenseIT.java | 46 +- .../ContainerWithoutPlatformExtensionsIT.java | 71 +- .../hivemq/CreateFileInCopiedDirectoryIT.java | 47 +- .../CreateFileInExtensionDirectoryIT.java | 45 +- ...DisableEnableExtensionFromDirectoryIT.java | 19 +- .../hivemq/DisableEnableExtensionIT.java | 29 +- .../hivemq/HiveMQExtensionTest.java | 5 +- .../hivemq/HiveMQTestContainerCore.java | 20 +- .../hivemq/docs/DemoDisableExtensionsIT.java | 27 +- .../hivemq/docs/DemoExtensionTestsIT.java | 32 +- .../hivemq/docs/DemoFilesIT.java | 35 +- .../hivemq/docs/DemoHiveMQContainerIT.java | 31 +- .../hivemq/util/MyExtension.java | 19 +- .../util/MyExtensionWithSubclasses.java | 19 +- .../hivemq/util/PublishModifier.java | 12 +- .../hivemq/util/TestPublishModifiedUtil.java | 31 +- .../containers/InfluxDBContainer.java | 14 +- .../containers/InfluxDBContainerTest.java | 8 +- .../InfluxDBContainerWithUserTest.java | 15 +- .../db/AbstractContainerDatabaseTest.java | 3 +- .../jdbc/AbstractJDBCDriverTest.java | 136 ++-- .../containers/JdbcDatabaseContainer.java | 49 +- .../JdbcDatabaseContainerProvider.java | 16 +- .../jdbc/ConnectionDelegate.java | 2 +- .../testcontainers/jdbc/ConnectionUrl.java | 89 +-- .../jdbc/ConnectionWrapper.java | 2 +- .../jdbc/ContainerDatabaseDriver.java | 63 +- .../jdbc/JdbcDatabaseDelegate.java | 18 +- .../testcontainers/jdbc/ext/ScriptUtils.java | 62 +- .../containers/JdbcDatabaseContainerTest.java | 5 +- .../jdbc/ConnectionUrlDriversTests.java | 109 ++- .../jdbc/ConnectionUrlTest.java | 41 +- .../jdbc/ContainerDatabaseDriverTest.java | 37 +- .../jdbc/MissingJdbcDriverTest.java | 2 +- .../FilesystemFriendlyNameGenerator.java | 10 +- .../junit/jupiter/Testcontainers.java | 1 - .../jupiter/TestcontainersExtension.java | 69 +- .../TestcontainersTestDescription.java | 2 + .../junit/jupiter/ComposeContainerTests.java | 3 +- .../FilesystemFriendlyNameGeneratorTest.java | 6 +- .../junit/jupiter/MixedLifecycleTests.java | 8 - .../junit/jupiter/PostgresContainerTests.java | 12 +- .../TestLifecycleAwareContainerMock.java | 10 +- ...tLifecycleAwareExceptionCapturingTest.java | 1 + .../jupiter/TestLifecycleAwareMethodTest.java | 17 +- .../jupiter/TestcontainersExtensionTests.java | 10 +- ...ntainersNestedRestartedContainerTests.java | 16 +- ...tcontainersNestedSharedContainerTests.java | 5 +- .../TestcontainersRestartBetweenTests.java | 10 +- .../TestcontainersSharedContainerTests.java | 6 +- .../jupiter/WrongAnnotationUsageTests.java | 3 - .../jupiter/inheritance/AbstractTestBase.java | 1 - .../org/testcontainers/k3s/K3sContainer.java | 13 +- .../k3s/Fabric8K3sContainerTest.java | 10 +- .../containers/KafkaContainer.java | 18 +- .../containers/KafkaContainerTest.java | 80 +- .../localstack/LocalStackContainer.java | 30 +- .../containers/localstack/LegacyModeTest.java | 70 +- .../localstack/LocalstackContainerTest.java | 160 ++-- .../containers/MariaDBContainer.java | 19 +- .../containers/MariaDBContainerProvider.java | 3 +- .../MariaDBR2DBCDatabaseContainer.java | 6 +- .../MariaDBR2DBCDatabaseContainerTest.java | 1 - .../jdbc/mariadb/MariaDBJDBCDriverTest.java | 49 +- .../junit/mariadb/SimpleMariaDBTest.java | 37 +- .../containers/MockServerContainer.java | 2 +- .../MockServerContainerRuleTest.java | 12 +- .../containers/MockServerContainerTest.java | 19 +- .../containers/SimpleHttpClient.java | 4 +- .../containers/MongoDBContainer.java | 45 +- .../containers/MongoDBContainerTest.java | 42 +- .../MSSQLR2DBCDatabaseContainer.java | 6 +- .../containers/MSSQLServerContainer.java | 21 +- .../MSSQLServerContainerProvider.java | 1 + .../MSSQLServerJDBCDriverTest.java | 15 +- .../CustomPasswordMSSQLServerTest.java | 60 +- .../CustomizableMSSQLServerTest.java | 9 +- .../mssqlserver/SimpleMSSQLServerTest.java | 25 +- .../containers/MySQLContainer.java | 17 +- .../containers/MySQLContainerProvider.java | 1 - .../MySQLR2DBCDatabaseContainer.java | 6 +- .../org/testcontainers/MySQLTestImages.java | 3 + .../MySQLR2DBCDatabaseContainerTest.java | 1 - .../containers/MySQLRootAccountTest.java | 7 +- .../jdbc/mysql/JDBCDriverWithPoolTest.java | 54 +- .../jdbc/mysql/MySQLJDBCDriverTest.java | 51 +- .../junit/mysql/CustomizableMysqlTest.java | 16 +- .../junit/mysql/MultiVersionMySQLTest.java | 20 +- .../junit/mysql/SimpleMySQLTest.java | 104 +-- .../neo4j/demos/plugins/HelloWorld.java | 1 - .../containers/Neo4jContainer.java | 71 +- .../Neo4jContainerJUnitIntegrationTest.java | 5 +- .../containers/Neo4jContainerTest.java | 93 +-- .../containers/NginxContainer.java | 7 +- .../testcontainers/junit/SimpleNginxTest.java | 14 +- .../containers/OracleContainer.java | 36 +- .../containers/OracleContainerProvider.java | 1 + .../containers/jdbc/OracleJDBCDriverTest.java | 24 +- .../junit/oracle/SimpleOracleTest.java | 29 +- .../containers/OrientDBContainer.java | 16 +- .../containers/OrientDBContainerTest.java | 13 +- .../containers/PostgisContainerProvider.java | 9 +- .../containers/PostgreSQLContainer.java | 29 +- .../PostgreSQLContainerProvider.java | 2 +- .../PostgreSQLR2DBCDatabaseContainer.java | 6 +- .../TimescaleDBContainerProvider.java | 9 +- .../PostgreSQLConnectionURLTest.java | 19 +- .../PostgreSQLR2DBCDatabaseContainerTest.java | 2 + .../containers/TimescaleDBContainerTest.java | 29 +- .../jdbc/DatabaseDriverShutdownTest.java | 1 + .../jdbc/DatabaseDriverTmpfsTest.java | 1 - .../jdbc/postgis/PostgisJDBCDriverTest.java | 20 +- .../postgresql/PostgreSQLJDBCDriverTest.java | 15 +- .../TimescaleDBJDBCDriverTest.java | 20 +- .../CustomizablePostgreSQLTest.java | 10 +- .../postgresql/SimplePostgreSQLTest.java | 30 +- .../containers/PrestoContainer.java | 26 +- .../containers/PrestoContainerProvider.java | 2 +- .../containers/PrestoContainerTest.java | 98 ++- .../jdbc/presto/PrestoJDBCDriverTest.java | 12 +- .../containers/PulsarContainer.java | 4 +- .../containers/PulsarContainerTest.java | 28 +- .../r2dbc/CancellableSubscription.java | 3 +- .../r2dbc/ConnectionPublisher.java | 10 +- .../r2dbc/EmptySubscription.java | 8 +- .../java/org/testcontainers/r2dbc/Hidden.java | 4 +- .../r2dbc/R2DBCDatabaseContainer.java | 1 - .../r2dbc/R2DBCDatabaseContainerProvider.java | 1 - .../TestcontainersR2DBCConnectionFactory.java | 45 +- ...tcontainersR2DBCConnectionFactoryTest.java | 19 +- .../AbstractR2DBCDatabaseContainerTest.java | 4 +- .../containers/RabbitMQContainer.java | 373 ++++++--- .../containers/RabbitMQContainerTest.java | 248 +++--- .../containers/BrowserWebDriverContainer.java | 125 ++- .../DefaultRecordingFileFactory.java | 23 +- .../containers/RecordingFileFactory.java | 15 +- .../containers/SeleniumUtils.java | 13 +- .../DefaultRecordingFileFactoryTest.java | 37 +- .../junit/BaseWebDriverContainerTest.java | 22 +- .../junit/BrowserWebDriverContainerTest.java | 18 +- ...ChromeRecordingWebDriverContainerTest.java | 102 +-- .../junit/ChromeWebDriverContainerTest.java | 2 +- .../ContainerWithoutCapabilitiesTest.java | 7 +- ...stomWaitTimeoutWebDriverContainerTest.java | 5 +- .../junit/FirefoxWebDriverContainerTest.java | 4 +- .../LocalServerWebDriverContainerTest.java | 8 +- .../junit/SeleniumStartTest.java | 14 +- .../junit/SeleniumUtilsTest.java | 2 - ...ecificImageNameWebDriverContainerTest.java | 5 +- .../containers/SolrClientUtils.java | 49 +- .../containers/SolrClientUtilsException.java | 1 + .../containers/SolrContainer.java | 23 +- .../SolrContainerConfiguration.java | 8 +- .../containers/SolrContainerTest.java | 15 +- .../spock/SpockTestDescription.groovy | 36 +- .../spock/Testcontainers.groovy | 1 - .../spock/TestcontainersExtension.groovy | 42 +- .../TestcontainersMethodInterceptor.groovy | 224 +++--- .../spock/ComposeContainerIT.groovy | 34 +- .../spock/MySqlContainerIT.groovy | 14 +- .../spock/PostgresContainerIT.groovy | 56 +- .../spock/SharedComposeContainerIT.groovy | 36 +- .../spock/SpockTestImages.groovy | 8 +- .../spock/TestHierarchyIT.groovy | 22 +- .../TestLifecycleAwareContainerMock.java | 10 +- .../spock/TestLifecycleAwareIT.groovy | 57 +- ...TestcontainersRestartBetweenTestsIT.groovy | 29 +- .../TestcontainersSharedContainerIT.groovy | 61 +- .../containers/ToxiproxyContainer.java | 50 +- .../containers/ToxiproxyTest.java | 78 +- .../containers/TrinoContainer.java | 17 +- .../containers/TrinoContainerProvider.java | 1 + .../containers/TrinoContainerTest.java | 38 +- .../jdbc/trino/TrinoJDBCDriverTest.java | 12 +- .../testcontainers/vault/VaultContainer.java | 46 +- .../testcontainers/vault/VaultLogLevel.java | 6 +- .../testcontainers/vault/VaultClientTest.java | 20 +- .../vault/VaultContainerTest.java | 74 +- .../org/testcontainers/testsupport/Flaky.java | 10 +- .../testsupport/FlakyTestJUnit4RetryRule.java | 14 +- .../testsupport/FlakyRuleTest.java | 60 +- 458 files changed, 8766 insertions(+), 5973 deletions(-) diff --git a/.github/workflows/ci-docker-wormhole.yml b/.github/workflows/ci-docker-wormhole.yml index b93d26d98bb..6d0df9a3c21 100644 --- a/.github/workflows/ci-docker-wormhole.yml +++ b/.github/workflows/ci-docker-wormhole.yml @@ -16,5 +16,6 @@ jobs: -v "$HOME:$HOME" \ -v "$PWD:$PWD" \ -w "$PWD" \ + -e AUTO_APPLY_GIT_HOOKS=false \ openjdk:8-jdk-alpine \ ./gradlew --no-daemon --continue --scan testcontainers:test --tests '*GenericContainerRuleTest' diff --git a/build.gradle b/build.gradle index adbd1a47c4a..1458b3db027 100644 --- a/build.gradle +++ b/build.gradle @@ -1,11 +1,13 @@ buildscript { repositories { mavenCentral() + maven { url 'https://jitpack.io' } } dependencies { // https://github.com/melix/japicmp-gradle-plugin/issues/36 classpath 'com.google.guava:guava:30.1.1-jre' + classpath 'com.github.kiview:captain-hook:76a1c11a16' } } @@ -17,6 +19,12 @@ plugins { } apply from: "$rootDir/gradle/ci-support.gradle" +apply plugin: 'com.github.tjni.captainhook' + +captainHook { + autoApplyGitHooks = Boolean.valueOf(System.getenv("AUTO_APPLY_GIT_HOOKS")) + preCommit = './gradlew spotlessApply' +} subprojects { apply plugin: 'java-library' @@ -110,8 +118,8 @@ subprojects { } spotless { - enforceCheck false java { + toggleOffOn() removeUnusedImports() trimTrailingWhitespace() endWithNewline() @@ -134,6 +142,5 @@ subprojects { checkstyle { toolVersion = "9.3" configFile = rootProject.file('config/checkstyle/checkstyle.xml') - ignoreFailures = true } } diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index 39598dae2e4..ccb8367404c 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -11,8 +11,12 @@ LITERAL_FOR, LITERAL_IF, LITERAL_WHILE, - LITERAL_CASE, - LITERAL_DEFAULT, + " + /> + + + diff --git a/core/src/jarFileTest/java/org/testcontainers/AbstractJarFileTest.java b/core/src/jarFileTest/java/org/testcontainers/AbstractJarFileTest.java index d6043422640..ae92aa371b0 100644 --- a/core/src/jarFileTest/java/org/testcontainers/AbstractJarFileTest.java +++ b/core/src/jarFileTest/java/org/testcontainers/AbstractJarFileTest.java @@ -5,8 +5,7 @@ import java.nio.file.FileSystems; import java.nio.file.Path; import java.nio.file.Paths; - -import static java.util.Collections.emptyMap; +import java.util.Collections; public abstract class AbstractJarFileTest { @@ -16,7 +15,7 @@ public abstract class AbstractJarFileTest { try { Path jarFilePath = Paths.get(System.getProperty("jarFile")); URI jarFileUri = new URI("jar", jarFilePath.toUri().toString(), null); - FileSystem fileSystem = FileSystems.newFileSystem(jarFileUri, emptyMap()); + FileSystem fileSystem = FileSystems.newFileSystem(jarFileUri, Collections.emptyMap()); root = fileSystem.getPath("/"); } catch (Exception e) { throw new RuntimeException(e); diff --git a/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java b/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java index 4cf7b3a9e9f..99fd1359c85 100644 --- a/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java +++ b/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java @@ -13,34 +13,26 @@ public class JarFileShadingTest extends AbstractJarFileTest { @Test public void testPackages() throws Exception { - assertThatFileList(root).containsOnly( - "org", - "META-INF" - ); - - assertThatFileList(root.resolve("org")).containsOnly( - "testcontainers" - ); + assertThatFileList(root).containsOnly("org", "META-INF"); + + assertThatFileList(root.resolve("org")).containsOnly("testcontainers"); } @Test public void testMetaInf() throws Exception { - assertThatFileList(root.resolve("META-INF")).containsOnly( - "MANIFEST.MF", - "services" - ); + assertThatFileList(root.resolve("META-INF")).containsOnly("MANIFEST.MF", "services"); } @Test public void testMetaInfServices() throws Exception { assertThatFileList(root.resolve("META-INF").resolve("services")) - .allMatch(it -> it.startsWith("org.testcontainers.")); + .allMatch(it -> it.startsWith("org.testcontainers.")); } private ListAssert assertThatFileList(Path path) throws IOException { return (ListAssert) assertThat(Files.list(path)) - .extracting(Path::getFileName) - .extracting(Path::toString) - .extracting(it -> it.endsWith("/") ? it.substring(0, it.length() - 1) : it); + .extracting(Path::getFileName) + .extracting(Path::toString) + .extracting(it -> it.endsWith("/") ? it.substring(0, it.length() - 1) : it); } } diff --git a/core/src/jarFileTest/java/org/testcontainers/PublicBinaryAPITest.java b/core/src/jarFileTest/java/org/testcontainers/PublicBinaryAPITest.java index 208c8702654..77156e77636 100644 --- a/core/src/jarFileTest/java/org/testcontainers/PublicBinaryAPITest.java +++ b/core/src/jarFileTest/java/org/testcontainers/PublicBinaryAPITest.java @@ -37,6 +37,7 @@ public class PublicBinaryAPITest extends AbstractJarFileTest { private static String SHADED_PACKAGE = "org.testcontainers.shaded."; + private static String SHADED_PACKAGE_PATH = SHADED_PACKAGE.replaceAll("\\.", "/"); static { @@ -49,36 +50,38 @@ public class PublicBinaryAPITest extends AbstractJarFileTest { public static List data() throws Exception { List result = new ArrayList<>(); - Files.walkFileTree(root, new SimpleFileVisitor() { - - @Override - public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException { - String fileName = path.toString(); + Files.walkFileTree( + root, + new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException { + String fileName = path.toString(); - if (!fileName.endsWith(".class")) { - return super.visitFile(path, attrs); - } + if (!fileName.endsWith(".class")) { + return super.visitFile(path, attrs); + } - if (!fileName.startsWith("/org/testcontainers/") ) { - return super.visitFile(path, attrs); - } + if (!fileName.startsWith("/org/testcontainers/")) { + return super.visitFile(path, attrs); + } - if (fileName.startsWith("/" + SHADED_PACKAGE_PATH)) { - return super.visitFile(path, attrs); - } + if (fileName.startsWith("/" + SHADED_PACKAGE_PATH)) { + return super.visitFile(path, attrs); + } - try(InputStream inputStream = Files.newInputStream(path)) { - ClassReader reader = new ClassReader(inputStream); - ClassNode node = new ClassNode(); - reader.accept(node, ClassReader.SKIP_CODE); - if ((node.access & Opcodes.ACC_PUBLIC) != 0) { - result.add(new Object[]{ fileName, node }); + try (InputStream inputStream = Files.newInputStream(path)) { + ClassReader reader = new ClassReader(inputStream); + ClassNode node = new ClassNode(); + reader.accept(node, ClassReader.SKIP_CODE); + if ((node.access & Opcodes.ACC_PUBLIC) != 0) { + result.add(new Object[] { fileName, node }); + } } - } - return super.visitFile(path, attrs); + return super.visitFile(path, attrs); + } } - }); + ); return result; } @@ -100,39 +103,36 @@ public void setUp() { @Test public void testSuperClass() { - assertThat(classNode.superName) - .doesNotStartWith(SHADED_PACKAGE_PATH); + assertThat(classNode.superName).doesNotStartWith(SHADED_PACKAGE_PATH); } @Test public void testInterfaces() { - assertThat(classNode.interfaces) - .allSatisfy(it -> assertThat(it).doesNotStartWith(SHADED_PACKAGE_PATH)); + assertThat(classNode.interfaces).allSatisfy(it -> assertThat(it).doesNotStartWith(SHADED_PACKAGE_PATH)); } @Test public void testMethodReturnTypes() { assertThat(classNode.methods) - .filteredOn(it -> (it.access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) != 0) - .allSatisfy(it -> assertThat(Type.getReturnType(it.desc).getClassName()).doesNotStartWith(SHADED_PACKAGE)); + .filteredOn(it -> (it.access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) != 0) + .allSatisfy(it -> assertThat(Type.getReturnType(it.desc).getClassName()).doesNotStartWith(SHADED_PACKAGE)); } @Test public void testMethodArguments() { assertThat(classNode.methods) - .filteredOn(it -> (it.access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) != 0) - .allSatisfy(method -> assertThat(Arrays.asList(Type.getArgumentTypes(method.desc))) - .extracting(Type::getClassName) - .allSatisfy(it -> assertThat(it).doesNotStartWith(SHADED_PACKAGE)) - ); + .filteredOn(it -> (it.access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) != 0) + .allSatisfy(method -> { + assertThat(Arrays.asList(Type.getArgumentTypes(method.desc))) + .extracting(Type::getClassName) + .allSatisfy(it -> assertThat(it).doesNotStartWith(SHADED_PACKAGE)); + }); } @Test public void testFields() { assertThat(classNode.fields) - .filteredOn(it -> (it.access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) != 0) - .allSatisfy(it -> assertThat(Type.getType(it.desc).getClassName()) - .doesNotStartWith(SHADED_PACKAGE) - ); + .filteredOn(it -> (it.access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) != 0) + .allSatisfy(it -> assertThat(Type.getType(it.desc).getClassName()).doesNotStartWith(SHADED_PACKAGE)); } } diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index c1e999d392a..04c20236763 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -51,16 +51,17 @@ public class DockerClientFactory { public static final ThreadGroup TESTCONTAINERS_THREAD_GROUP = new ThreadGroup("testcontainers"); + public static final String TESTCONTAINERS_LABEL = DockerClientFactory.class.getPackage().getName(); + public static final String TESTCONTAINERS_SESSION_ID_LABEL = TESTCONTAINERS_LABEL + ".sessionId"; public static final String SESSION_ID = UUID.randomUUID().toString(); - public static final Map DEFAULT_LABELS = ImmutableMap.of( - TESTCONTAINERS_LABEL, "true" - ); + public static final Map DEFAULT_LABELS = ImmutableMap.of(TESTCONTAINERS_LABEL, "true"); private static final DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.14"); + private static DockerClientFactory instance; // Cached client configuration @@ -79,9 +80,7 @@ public class DockerClientFactory { private final boolean fileMountingSupported = checkMountableFile(); @VisibleForTesting - DockerClientFactory() { - - } + DockerClientFactory() {} public static DockerClient lazyClient() { return new DockerClientDelegate() { @@ -102,7 +101,7 @@ public String toString() { * * @return the singleton instance of DockerClientFactory */ - public synchronized static DockerClientFactory instance() { + public static synchronized DockerClientFactory instance() { if (instance == null) { instance = new DockerClientFactory(); } @@ -112,6 +111,7 @@ public synchronized static DockerClientFactory instance() { /** * Checks whether Docker is accessible and {@link #client()} is able to produce a client. + * * @return true if Docker is available, false if not. */ public synchronized boolean isDockerAvailable() { @@ -149,16 +149,11 @@ public String getRemoteDockerUnixSocketPath() { } URI dockerHost = getTransportConfig().getDockerHost(); - String path = "unix".equals(dockerHost.getScheme()) - ? dockerHost.getRawPath() - : "/var/run/docker.sock"; - return SystemUtils.IS_OS_WINDOWS - ? "/" + path - : path; + String path = "unix".equals(dockerHost.getScheme()) ? dockerHost.getRawPath() : "/var/run/docker.sock"; + return SystemUtils.IS_OS_WINDOWS ? "/" + path : path; } /** - * * @return a new initialized Docker client */ @Synchronized @@ -175,16 +170,16 @@ public DockerClient client() { final DockerClientProviderStrategy strategy = getOrInitializeStrategy(); - client = new DockerClientDelegate() { - - @Getter - final DockerClient dockerClient = strategy.getDockerClient(); + client = + new DockerClientDelegate() { + @Getter + final DockerClient dockerClient = strategy.getDockerClient(); - @Override - public void close() { - throw new IllegalStateException("You should never close the global DockerClient!"); - } - }; + @Override + public void close() { + throw new IllegalStateException("You should never close the global DockerClient!"); + } + }; log.info("Docker host IP address is {}", strategy.getDockerHostIpAddress()); Info dockerInfo = strategy.getInfo(); @@ -192,11 +187,22 @@ public void close() { Version version = client.versionCmd().exec(); log.debug("Docker version: {}", version.getRawValues()); activeApiVersion = version.getApiVersion(); - log.info("Connected to docker: \n" + - " Server Version: " + dockerInfo.getServerVersion() + "\n" + - " API Version: " + activeApiVersion + "\n" + - " Operating System: " + dockerInfo.getOperatingSystem() + "\n" + - " Total Memory: " + dockerInfo.getMemTotal() / (1024 * 1024) + " MB"); + log.info( + "Connected to docker: \n" + + " Server Version: " + + dockerInfo.getServerVersion() + + "\n" + + " API Version: " + + activeApiVersion + + "\n" + + " Operating System: " + + dockerInfo.getOperatingSystem() + + "\n" + + " Total Memory: " + + dockerInfo.getMemTotal() / + (1024 * 1024) + + " MB" + ); try { //noinspection deprecation @@ -225,7 +231,8 @@ public void close() { } private void checkDockerVersion(String dockerVersion) { - boolean versionIsSufficient = new ComparableVersion(dockerVersion).compareTo(new ComparableVersion("1.6.0")) >= 0; + boolean versionIsSufficient = new ComparableVersion(dockerVersion).compareTo(new ComparableVersion("1.6.0")) >= + 0; check("Docker server version should be at least 1.6.0", versionIsSufficient); } @@ -241,14 +248,22 @@ private void check(String message, boolean isSuccessful) { private boolean checkMountableFile() { DockerClient dockerClient = client(); - MountableFile mountableFile = MountableFile.forClasspathResource(ResourceReaper.class.getName().replace(".", "/") + ".class"); + MountableFile mountableFile = MountableFile.forClasspathResource( + ResourceReaper.class.getName().replace(".", "/") + ".class" + ); Volume volume = new Volume("/dummy"); try { return runInsideDocker( - createContainerCmd -> createContainerCmd.withBinds(new Bind(mountableFile.getResolvedPath(), volume, AccessMode.ro)), + createContainerCmd -> { + createContainerCmd.withBinds(new Bind(mountableFile.getResolvedPath(), volume, AccessMode.ro)); + }, (__, containerId) -> { - try (InputStream stream = dockerClient.copyArchiveFromContainerCmd(containerId, volume.getPath()).exec()) { + try ( + InputStream stream = dockerClient + .copyArchiveFromContainerCmd(containerId, volume.getPath()) + .exec() + ) { stream.read(); return true; } catch (Exception e) { @@ -293,16 +308,22 @@ public String dockerHostIpAddress() { return getOrInitializeStrategy().getDockerHostIpAddress(); } - public T runInsideDocker(Consumer createContainerCmdConsumer, BiFunction block) { + public T runInsideDocker( + Consumer createContainerCmdConsumer, + BiFunction block + ) { return runInsideDocker(TINY_IMAGE, createContainerCmdConsumer, block); } - T runInsideDocker(DockerImageName imageName, Consumer createContainerCmdConsumer, BiFunction block) { + T runInsideDocker( + DockerImageName imageName, + Consumer createContainerCmdConsumer, + BiFunction block + ) { RemoteDockerImage dockerImage = new RemoteDockerImage(imageName); HashMap labels = new HashMap<>(DEFAULT_LABELS); labels.putAll(ResourceReaper.instance().getLabels()); - CreateContainerCmd createContainerCmd = client.createContainerCmd(dockerImage.get()) - .withLabels(labels); + CreateContainerCmd createContainerCmd = client.createContainerCmd(dockerImage.get()).withLabels(labels); createContainerCmdConsumer.accept(createContainerCmd); String id = createContainerCmd.exec().getId(); diff --git a/core/src/main/java/org/testcontainers/Testcontainers.java b/core/src/main/java/org/testcontainers/Testcontainers.java index 0f83725c55b..ab567e1fda7 100644 --- a/core/src/main/java/org/testcontainers/Testcontainers.java +++ b/core/src/main/java/org/testcontainers/Testcontainers.java @@ -1,12 +1,11 @@ package org.testcontainers; -import java.util.Map; -import java.util.Map.Entry; - import lombok.experimental.UtilityClass; - import org.testcontainers.containers.PortForwardingContainer; +import java.util.Map; +import java.util.Map.Entry; + @UtilityClass public class Testcontainers { @@ -15,10 +14,10 @@ public void exposeHostPorts(int... ports) { PortForwardingContainer.INSTANCE.exposeHostPort(port); } } - + public void exposeHostPorts(Map ports) { - for(Entry entry : ports.entrySet()) { - PortForwardingContainer.INSTANCE.exposeHostPort(entry.getKey(), entry.getValue()); - } + for (Entry entry : ports.entrySet()) { + PortForwardingContainer.INSTANCE.exposeHostPort(entry.getKey(), entry.getValue()); + } } } diff --git a/core/src/main/java/org/testcontainers/UnstableAPI.java b/core/src/main/java/org/testcontainers/UnstableAPI.java index 21fd2c3176c..01221738dd4 100644 --- a/core/src/main/java/org/testcontainers/UnstableAPI.java +++ b/core/src/main/java/org/testcontainers/UnstableAPI.java @@ -11,11 +11,7 @@ * a stable API. */ @Retention(RetentionPolicy.SOURCE) -@Target({ - ElementType.TYPE, - ElementType.METHOD, - ElementType.FIELD, -}) +@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD }) @Documented public @interface UnstableAPI { } diff --git a/core/src/main/java/org/testcontainers/containers/BindMode.java b/core/src/main/java/org/testcontainers/containers/BindMode.java index 579dc4d5eca..5fc9c610165 100644 --- a/core/src/main/java/org/testcontainers/containers/BindMode.java +++ b/core/src/main/java/org/testcontainers/containers/BindMode.java @@ -6,7 +6,8 @@ * Possible modes for binding storage volumes. */ public enum BindMode { - READ_ONLY(AccessMode.ro), READ_WRITE(AccessMode.rw); + READ_ONLY(AccessMode.ro), + READ_WRITE(AccessMode.rw); public final AccessMode accessMode; diff --git a/core/src/main/java/org/testcontainers/containers/ComposeServiceWaitStrategyTarget.java b/core/src/main/java/org/testcontainers/containers/ComposeServiceWaitStrategyTarget.java index fbb2a7cee6e..81525710700 100644 --- a/core/src/main/java/org/testcontainers/containers/ComposeServiceWaitStrategyTarget.java +++ b/core/src/main/java/org/testcontainers/containers/ComposeServiceWaitStrategyTarget.java @@ -13,7 +13,6 @@ import java.util.List; import java.util.Map; - /** * Class to provide a wait strategy target for services started through docker-compose */ @@ -21,15 +20,23 @@ class ComposeServiceWaitStrategyTarget implements WaitStrategyTarget { private final Container container; + private final GenericContainer proxyContainer; + private final DockerClient dockerClient; + @NonNull private Map mappedPorts; - @Getter(lazy=true) + + @Getter(lazy = true) private final InspectContainerResponse containerInfo = dockerClient.inspectContainerCmd(getContainerId()).exec(); - ComposeServiceWaitStrategyTarget(DockerClient dockerClient, Container container, GenericContainer proxyContainer, - @NonNull Map mappedPorts) { + ComposeServiceWaitStrategyTarget( + DockerClient dockerClient, + Container container, + GenericContainer proxyContainer, + @NonNull Map mappedPorts + ) { this.dockerClient = dockerClient; this.container = container; this.proxyContainer = proxyContainer; diff --git a/core/src/main/java/org/testcontainers/containers/Container.java b/core/src/main/java/org/testcontainers/containers/Container.java index 88fe4802945..7e6f492c284 100644 --- a/core/src/main/java/org/testcontainers/containers/Container.java +++ b/core/src/main/java/org/testcontainers/containers/Container.java @@ -1,17 +1,15 @@ package org.testcontainers.containers; -import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.model.Bind; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.NonNull; import lombok.Value; -import org.testcontainers.DockerClientFactory; -import org.testcontainers.images.ImagePullPolicy; import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.startupcheck.StartupCheckStrategy; import org.testcontainers.containers.traits.LinkableContainer; import org.testcontainers.containers.wait.strategy.WaitStrategy; +import org.testcontainers.images.ImagePullPolicy; import org.testcontainers.images.builder.Transferable; import org.testcontainers.utility.LogUtils; import org.testcontainers.utility.MountableFile; @@ -25,7 +23,6 @@ import java.util.function.Function; public interface Container> extends LinkableContainer, ContainerState { - /** * @return a reference to this container instance, cast to the expected generic type. */ @@ -40,8 +37,11 @@ default SELF self() { @Value @AllArgsConstructor(access = AccessLevel.MODULE) class ExecResult { + int exitCode; + String stdout; + String stderr; } @@ -298,7 +298,11 @@ default SELF withEnv(String key, Function, String> mapper) { * @param mode access mode for the file * @return this */ - default SELF withClasspathResourceMapping(final String resourcePath, final String containerPath, final BindMode mode) { + default SELF withClasspathResourceMapping( + final String resourcePath, + final String containerPath, + final BindMode mode + ) { withClasspathResourceMapping(resourcePath, containerPath, mode, SelinuxContext.NONE); return self(); } @@ -313,7 +317,12 @@ default SELF withClasspathResourceMapping(final String resourcePath, final Strin * @param selinuxContext selinux context argument to use for this file * @return this */ - SELF withClasspathResourceMapping(String resourcePath, String containerPath, BindMode mode, SelinuxContext selinuxContext); + SELF withClasspathResourceMapping( + String resourcePath, + String containerPath, + BindMode mode, + SelinuxContext selinuxContext + ); /** * Set the duration of waiting time until container treated as started. @@ -403,7 +412,6 @@ default void followOutput(Consumer consumer, OutputFrame.OutputType LogUtils.followOutput(getDockerClient(), getContainerId(), consumer, types); } - /** * Attach an output consumer at container startup, enabling stdout and stderr to be followed, waited on, etc. *

diff --git a/core/src/main/java/org/testcontainers/containers/ContainerFetchException.java b/core/src/main/java/org/testcontainers/containers/ContainerFetchException.java index 66aa1a493b0..66414434786 100644 --- a/core/src/main/java/org/testcontainers/containers/ContainerFetchException.java +++ b/core/src/main/java/org/testcontainers/containers/ContainerFetchException.java @@ -4,6 +4,7 @@ * Created by rnorth on 15/10/2015. */ public class ContainerFetchException extends RuntimeException { + public ContainerFetchException(String s, Exception e) { super(s, e); } diff --git a/core/src/main/java/org/testcontainers/containers/ContainerState.java b/core/src/main/java/org/testcontainers/containers/ContainerState.java index 51eb4daddde..b82b5448d2f 100644 --- a/core/src/main/java/org/testcontainers/containers/ContainerState.java +++ b/core/src/main/java/org/testcontainers/containers/ContainerState.java @@ -37,7 +37,6 @@ import java.util.stream.Collectors; public interface ContainerState { - String STATE_HEALTHY = "healthy"; /** @@ -109,7 +108,9 @@ default boolean isHealthy() { InspectContainerResponse inspectContainerResponse = getCurrentContainerInfo(); HealthState health = inspectContainerResponse.getState().getHealth(); if (health == null) { - throw new RuntimeException("This container's image does not have a healthcheck declared, so health cannot be determined. Either amend the image or use another approach to determine whether containers are healthy."); + throw new RuntimeException( + "This container's image does not have a healthcheck declared, so health cannot be determined. Either amend the image or use another approach to determine whether containers are healthy." + ); } return STATE_HEALTHY.equals(health.getStatus()); @@ -145,7 +146,10 @@ default Integer getFirstMappedPort() { * @return the port that the exposed port is mapped to, or null if it is not exposed */ default Integer getMappedPort(int originalPort) { - Preconditions.checkState(this.getContainerId() != null, "Mapped port can only be obtained after the container is started"); + Preconditions.checkState( + this.getContainerId() != null, + "Mapped port can only be obtained after the container is started" + ); Ports.Binding[] binding = new Ports.Binding[0]; final InspectContainerResponse containerInfo = this.getContainerInfo(); @@ -183,7 +187,8 @@ default List getPortBindings() { * @return the bound port numbers */ default List getBoundPortNumbers() { - return getPortBindings().stream() + return getPortBindings() + .stream() .map(PortBinding::parse) .map(PortBinding::getBinding) .map(Ports.Binding::getHostPortSpec) @@ -194,7 +199,6 @@ default List getBoundPortNumbers() { .collect(Collectors.toList()); } - /** * @return all log output from the container from start until the current instant (both stdout and stderr) */ @@ -228,7 +232,8 @@ default String getContainerId() { *

* @see ExecInContainerPattern#execInContainer(com.github.dockerjava.api.command.InspectContainerResponse, String...) */ - default Container.ExecResult execInContainer(String... command) throws UnsupportedOperationException, IOException, InterruptedException { + default Container.ExecResult execInContainer(String... command) + throws UnsupportedOperationException, IOException, InterruptedException { return execInContainer(StandardCharsets.UTF_8, command); } @@ -237,7 +242,8 @@ default Container.ExecResult execInContainer(String... command) throws Unsupport *

* @see ExecInContainerPattern#execInContainer(com.github.dockerjava.api.command.InspectContainerResponse, Charset, String...) */ - default Container.ExecResult execInContainer(Charset outputCharset, String... command) throws UnsupportedOperationException, IOException, InterruptedException { + default Container.ExecResult execInContainer(Charset outputCharset, String... command) + throws UnsupportedOperationException, IOException, InterruptedException { return ExecInContainerPattern.execInContainer(getDockerClient(), getContainerInfo(), outputCharset, command); } @@ -253,7 +259,9 @@ default void copyFileToContainer(MountableFile mountableFile, String containerPa if (containerPath.endsWith("/") && sourceFile.isFile()) { final Logger logger = LoggerFactory.getLogger(GenericContainer.class); - logger.warn("folder-like containerPath in copyFileToContainer is deprecated, please explicitly specify a file path"); + logger.warn( + "folder-like containerPath in copyFileToContainer is deprecated, please explicitly specify a file path" + ); copyFileToContainer((Transferable) mountableFile, containerPath + sourceFile.getName()); } else { copyFileToContainer((Transferable) mountableFile, containerPath); @@ -299,13 +307,17 @@ default void copyFileToContainer(Transferable transferable, String containerPath * @throws IOException if there's an issue communicating with Docker or receiving entry from TarArchiveInputStream * @throws InterruptedException if the thread waiting for the response is interrupted */ - default void copyFileFromContainer(String containerPath, String destinationPath) throws IOException, InterruptedException { - copyFileFromContainer(containerPath, inputStream -> { - try(FileOutputStream output = new FileOutputStream(destinationPath)) { - IOUtils.copy(inputStream, output); - return null; + default void copyFileFromContainer(String containerPath, String destinationPath) + throws IOException, InterruptedException { + copyFileFromContainer( + containerPath, + inputStream -> { + try (FileOutputStream output = new FileOutputStream(destinationPath)) { + IOUtils.copy(inputStream, output); + return null; + } } - }); + ); } /** @@ -315,7 +327,7 @@ default void copyFileFromContainer(String containerPath, String destinationPath) * @param function function that takes InputStream of the copied file */ @SneakyThrows - default T copyFileFromContainer(String containerPath, ThrowingFunction function) { + default T copyFileFromContainer(String containerPath, ThrowingFunction function) { if (getContainerId() == null) { throw new IllegalStateException("copyFileFromContainer can only be used when the Container is created."); } diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 6f0a3a086b6..86a98586c7f 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -6,7 +6,9 @@ import com.github.dockerjava.transport.SSLConfig; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; import com.google.common.base.Splitter; +import com.google.common.base.Strings; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.util.concurrent.Uninterruptibles; @@ -57,42 +59,52 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; +import java.util.stream.Collectors; import java.util.stream.Stream; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Strings.isNullOrEmpty; -import static java.util.stream.Collectors.joining; -import static java.util.stream.Collectors.toList; -import static org.testcontainers.containers.BindMode.READ_WRITE; - /** * Container which launches Docker Compose, for the purposes of launching a defined set of containers. */ @Slf4j -public class DockerComposeContainer> extends FailureDetectingExternalResource implements Startable { +public class DockerComposeContainer> + extends FailureDetectingExternalResource + implements Startable { /** * Random identifier which will become part of spawned containers names, so we can shut them down */ private final String identifier; + private final List composeFiles; + private DockerComposeFiles dockerComposeFiles; + private final Map scalingPreferences = new HashMap<>(); + private DockerClient dockerClient; + private boolean localCompose; + private boolean pull = true; + private boolean build = false; + private Set options = new HashSet<>(); + private boolean tailChildContainers; private String project; private final AtomicInteger nextAmbassadorPort = new AtomicInteger(2000); + private final Map> ambassadorPortMappings = new ConcurrentHashMap<>(); + private final Map serviceInstanceMap = new ConcurrentHashMap<>(); + private final Map waitStrategyMap = new ConcurrentHashMap<>(); + private final SocatContainer ambassadorContainer = new SocatContainer(); + private final Map>> logConsumers = new ConcurrentHashMap<>(); private static final Object MUTEX = new Object(); @@ -104,6 +116,7 @@ public class DockerComposeContainer> e * necessarily to containers that are spawned by Compose itself) */ private Map env = new HashMap<>(); + private RemoveImages removeImages; @Deprecated @@ -148,13 +161,11 @@ public void starting(Description description) { @Override @Deprecated - protected void succeeded(Description description) { - } + protected void succeeded(Description description) {} @Override @Deprecated - protected void failed(Throwable e, Description description) { - } + protected void failed(Throwable e, Description description) {} @Override @Deprecated @@ -183,15 +194,23 @@ private void pullImages() { // Pull images using our docker client rather than compose itself, // (a) as a workaround for https://github.com/docker/compose/issues/5854, which prevents authenticated image pulls being possible when credential helpers are in use // (b) so that credential helper-based auth still works when compose is running from within a container - dockerComposeFiles.getDependencyImages() + dockerComposeFiles + .getDependencyImages() .forEach(imageName -> { try { - log.info("Preemptively checking local images for '{}', referenced via a compose file or transitive Dockerfile. If not available, it will be pulled.", imageName); + log.info( + "Preemptively checking local images for '{}', referenced via a compose file or transitive Dockerfile. If not available, it will be pulled.", + imageName + ); new RemoteDockerImage(DockerImageName.parse(imageName)) .withImageNameSubstitutor(ImageNameSubstitutor.noop()) .get(); } catch (Exception e) { - log.warn("Unable to pre-fetch an image ({}) depended upon by Docker Compose build - startup will continue but may fail. Exception message was: {}", imageName, e.getMessage()); + log.warn( + "Unable to pre-fetch an image ({}) depended upon by Docker Compose build - startup will continue but may fail. Exception message was: {}", + imageName, + e.getMessage() + ); } }); } @@ -203,18 +222,21 @@ public SELF withServices(@NonNull String... services) { private void createServices() { // services that have been explicitly requested to be started. If empty, all services should be started. - final String serviceNameArgs = Stream.concat( - services.stream(), // services that have been specified with `withServices` - scalingPreferences.keySet().stream() // services that are implicitly needed via `withScaledService` - ) + final String serviceNameArgs = Stream + .concat( + services.stream(), // services that have been specified with `withServices` + scalingPreferences.keySet().stream() // services that are implicitly needed via `withScaledService` + ) .distinct() - .collect(joining(" ")); + .collect(Collectors.joining(" ")); // Apply scaling for the services specified using `withScaledService` - final String scalingOptions = scalingPreferences.entrySet().stream() + final String scalingOptions = scalingPreferences + .entrySet() + .stream() .map(entry -> "--scale " + entry.getKey() + "=" + entry.getValue()) .distinct() - .collect(joining(" ")); + .collect(Collectors.joining(" ")); String command = optionsAsString() + "up -d"; @@ -222,11 +244,11 @@ private void createServices() { command += " --build"; } - if (!isNullOrEmpty(scalingOptions)) { + if (!Strings.isNullOrEmpty(scalingOptions)) { command += " " + scalingOptions; } - if (!isNullOrEmpty(serviceNameArgs)) { + if (!Strings.isNullOrEmpty(serviceNameArgs)) { command += " " + serviceNameArgs; } @@ -235,10 +257,8 @@ private void createServices() { } private String optionsAsString() { - String optionsString = options - .stream() - .collect(joining(" ")); - if (optionsString.length() !=0 ) { + String optionsString = options.stream().collect(Collectors.joining(" ")); + if (optionsString.length() != 0) { // ensures that there is a space between the options and 'up' if options are passed. return optionsString + " "; } else { @@ -252,15 +272,17 @@ private void waitUntilServiceStarted() { Set servicesToWaitFor = waitStrategyMap.keySet(); Set instantiatedServices = serviceInstanceMap.keySet(); - Sets.SetView missingServiceInstances = - Sets.difference(servicesToWaitFor, instantiatedServices); + Sets.SetView missingServiceInstances = Sets.difference(servicesToWaitFor, instantiatedServices); if (!missingServiceInstances.isEmpty()) { throw new IllegalStateException( - "Services named " + missingServiceInstances + " " + - "do not exist, but wait conditions have been defined " + - "for them. This might mean that you misspelled " + - "the service name when defining the wait condition."); + "Services named " + + missingServiceInstances + + " " + + "do not exist, but wait conditions have been defined " + + "for them. This might mean that you misspelled " + + "the service name when defining the wait condition." + ); } serviceInstanceMap.forEach(this::waitUntilServiceStarted); @@ -268,15 +290,21 @@ private void waitUntilServiceStarted() { private void createServiceInstance(Container container) { String serviceName = getServiceNameFromContainer(container); - final ComposeServiceWaitStrategyTarget containerInstance = new ComposeServiceWaitStrategyTarget(dockerClient, container, - ambassadorContainer, ambassadorPortMappings.getOrDefault(serviceName, new HashMap<>())); + final ComposeServiceWaitStrategyTarget containerInstance = new ComposeServiceWaitStrategyTarget( + dockerClient, + container, + ambassadorContainer, + ambassadorPortMappings.getOrDefault(serviceName, new HashMap<>()) + ); String containerId = containerInstance.getContainerId(); if (tailChildContainers) { followLogs(containerId, new Slf4jLogConsumer(log).withPrefix(container.getNames()[0])); } //follow logs using registered consumers for this service - logConsumers.getOrDefault(serviceName, Collections.emptyList()).forEach(consumer -> followLogs(containerId, consumer)); + logConsumers + .getOrDefault(serviceName, Collections.emptyList()) + .forEach(consumer -> followLogs(containerId, consumer)); serviceInstanceMap.putIfAbsent(serviceName, containerInstance); } @@ -294,8 +322,8 @@ private String getServiceNameFromContainer(Container container) { } private void runWithCompose(String cmd) { - checkNotNull(composeFiles); - checkArgument(!composeFiles.isEmpty(), "No docker compose file have been provided"); + Preconditions.checkNotNull(composeFiles); + Preconditions.checkArgument(!composeFiles.isEmpty(), "No docker compose file have been provided"); final DockerCompose dockerCompose; if (localCompose) { @@ -304,26 +332,24 @@ private void runWithCompose(String cmd) { dockerCompose = new ContainerisedDockerCompose(composeFiles, project); } - dockerCompose - .withCommand(cmd) - .withEnv(env) - .invoke(); + dockerCompose.withCommand(cmd).withEnv(env).invoke(); } private void registerContainersForShutdown() { - ResourceReaper.instance().registerLabelsFilterForCleanup(Collections.singletonMap( - "com.docker.compose.project", project - )); + ResourceReaper + .instance() + .registerLabelsFilterForCleanup(Collections.singletonMap("com.docker.compose.project", project)); } @VisibleForTesting List listChildContainers() { - return dockerClient.listContainersCmd() + return dockerClient + .listContainersCmd() .withShowAll(true) - .exec().stream() - .filter(container -> Arrays.stream(container.getNames()).anyMatch(name -> - name.startsWith("/" + project))) - .collect(toList()); + .exec() + .stream() + .filter(container -> Arrays.stream(container.getNames()).anyMatch(name -> name.startsWith("/" + project))) + .collect(Collectors.toList()); } private void startAmbassadorContainers() { @@ -345,7 +371,6 @@ public void stop() { cmd += " --rmi " + removeImages.dockerRemoveImagesType(); } runWithCompose(cmd); - } finally { project = randomProjectId(); } @@ -360,12 +385,16 @@ public DockerComposeContainer withExposedService(String serviceName, int instanc return withExposedService(serviceName + "_" + instance, servicePort); } - public DockerComposeContainer withExposedService(String serviceName, int instance, int servicePort, WaitStrategy waitStrategy) { + public DockerComposeContainer withExposedService( + String serviceName, + int instance, + int servicePort, + WaitStrategy waitStrategy + ) { return withExposedService(serviceName + "_" + instance, servicePort, waitStrategy); } public SELF withExposedService(String serviceName, int servicePort, @NonNull WaitStrategy waitStrategy) { - String serviceInstanceName = getServiceInstanceName(serviceName); /* @@ -384,7 +413,9 @@ public SELF withExposedService(String serviceName, int servicePort, @NonNull Wai // Ambassador container will be started together after docker compose has started int ambassadorPort = nextAmbassadorPort.getAndIncrement(); - ambassadorPortMappings.computeIfAbsent(serviceInstanceName, __ -> new ConcurrentHashMap<>()).put(servicePort, ambassadorPort); + ambassadorPortMappings + .computeIfAbsent(serviceInstanceName, __ -> new ConcurrentHashMap<>()) + .put(servicePort, ambassadorPort); ambassadorContainer.withTarget(ambassadorPort, serviceInstanceName, servicePort); ambassadorContainer.addLink(new FutureContainer(this.project + "_" + serviceInstanceName), serviceInstanceName); addWaitStrategy(serviceInstanceName, waitStrategy); @@ -405,8 +436,13 @@ private String getServiceInstanceName(String serviceName) { * The WaitAllStrategy uses an long timeout, because timeouts should be handled by the inner strategies. */ private void addWaitStrategy(String serviceInstanceName, @NonNull WaitStrategy waitStrategy) { - final WaitAllStrategy waitAllStrategy = waitStrategyMap.computeIfAbsent(serviceInstanceName, __ -> - new WaitAllStrategy(WaitAllStrategy.Mode.WITH_MAXIMUM_OUTER_TIMEOUT).withStartupTimeout(Duration.ofMinutes(30))); + final WaitAllStrategy waitAllStrategy = waitStrategyMap.computeIfAbsent( + serviceInstanceName, + __ -> { + return new WaitAllStrategy(WaitAllStrategy.Mode.WITH_MAXIMUM_OUTER_TIMEOUT) + .withStartupTimeout(Duration.ofMinutes(30)); + } + ); waitAllStrategy.withStrategy(waitStrategy); } @@ -452,9 +488,17 @@ public Integer getServicePort(String serviceName, Integer servicePort) { Map portMap = ambassadorPortMappings.get(getServiceInstanceName(serviceName)); if (portMap == null) { - throw new IllegalArgumentException("Could not get a port for '" + serviceName + "'. " + - "Testcontainers does not have an exposed port configured for '" + serviceName + "'. " + - "To fix, please ensure that the service '" + serviceName + "' has ports exposed using .withExposedService(...)"); + throw new IllegalArgumentException( + "Could not get a port for '" + + serviceName + + "'. " + + "Testcontainers does not have an exposed port configured for '" + + serviceName + + "'. " + + "To fix, please ensure that the service '" + + serviceName + + "' has ports exposed using .withExposedService(...)" + ); } else { return ambassadorContainer.getMappedPort(portMap.get(servicePort)); } @@ -517,7 +561,8 @@ public SELF withTailChildContainers(boolean tailChildContainers) { */ public SELF withLogConsumer(String serviceName, Consumer consumer) { String serviceInstanceName = getServiceInstanceName(serviceName); - final List> consumers = this.logConsumers.getOrDefault(serviceInstanceName, new ArrayList<>()); + final List> consumers = + this.logConsumers.getOrDefault(serviceInstanceName, new ArrayList<>()); consumers.add(consumer); this.logConsumers.putIfAbsent(serviceInstanceName, consumers); return self(); @@ -609,34 +654,39 @@ interface DockerCompose { class ContainerisedDockerCompose extends GenericContainer implements DockerCompose { public static final char UNIX_PATH_SEPERATOR = ':'; + public static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("docker/compose:1.29.2"); public ContainerisedDockerCompose(List composeFiles, String identifier) { - super(DEFAULT_IMAGE_NAME); addEnv(ENV_PROJECT_NAME, identifier); // Map the docker compose file into the container final File dockerComposeBaseFile = composeFiles.get(0); final String pwd = dockerComposeBaseFile.getAbsoluteFile().getParentFile().getAbsolutePath(); - final String containerPwd = convertToUnixFilesystemPath(pwd); + final String containerPwd = convertToUnixFilesystemPath(pwd); - final List absoluteDockerComposeFiles = composeFiles.stream() + final List absoluteDockerComposeFiles = composeFiles + .stream() .map(File::getAbsolutePath) .map(MountableFile::forHostPath) .map(MountableFile::getFilesystemPath) .map(this::convertToUnixFilesystemPath) - .collect(toList()); + .collect(Collectors.toList()); final String composeFileEnvVariableValue = Joiner.on(UNIX_PATH_SEPERATOR).join(absoluteDockerComposeFiles); // we always need the UNIX path separator logger().debug("Set env COMPOSE_FILE={}", composeFileEnvVariableValue); addEnv(ENV_COMPOSE_FILE, composeFileEnvVariableValue); - addFileSystemBind(pwd, containerPwd, READ_WRITE); + addFileSystemBind(pwd, containerPwd, BindMode.READ_WRITE); // Ensure that compose can access docker. Since the container is assumed to be running on the same machine // as the docker daemon, just mapping the docker control socket is OK. // As there seems to be a problem with mapping to the /var/run directory in certain environments (e.g. CircleCI) // we map the socket file outside of /var/run, as just /docker.sock - addFileSystemBind(DockerClientFactory.instance().getRemoteDockerUnixSocketPath(), "/docker.sock", READ_WRITE); + addFileSystemBind( + DockerClientFactory.instance().getRemoteDockerUnixSocketPath(), + "/docker.sock", + BindMode.READ_WRITE + ); addEnv("DOCKER_HOST", "unix:///docker.sock"); setStartupCheckStrategy(new IndefiniteWaitOneShotStartupCheckStrategy()); setWorkingDirectory(containerPwd); @@ -649,7 +699,8 @@ public void invoke() { this.followOutput(new Slf4jLogConsumer(logger())); // wait for the compose container to stop, which should only happen after it has spawned all the service containers - logger().info("Docker Compose container is running for command: {}", Joiner.on(" ").join(this.getCommandParts())); + logger() + .info("Docker Compose container is running for command: {}", Joiner.on(" ").join(this.getCommandParts())); while (this.isRunning()) { logger().trace("Compose container is still running"); Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); @@ -658,7 +709,8 @@ public void invoke() { AuditLogger.doComposeLog(this.getCommandParts(), this.getEnv()); - final Integer exitCode = getDockerClient().inspectContainerCmd(getContainerId()) + final Integer exitCode = getDockerClient() + .inspectContainerCmd(getContainerId()) .exec() .getState() .getExitCode(); @@ -668,14 +720,13 @@ public void invoke() { "Containerised Docker Compose exited abnormally with code " + exitCode + " whilst running command: " + - StringUtils.join(this.getCommandParts(), ' ')); + StringUtils.join(this.getCommandParts(), ' ') + ); } } private String convertToUnixFilesystemPath(String path) { - return SystemUtils.IS_OS_WINDOWS - ? PathUtils.createMinGWPath(path).substring(1) - : path; + return SystemUtils.IS_OS_WINDOWS ? PathUtils.createMinGWPath(path).substring(1) : path; } } @@ -683,14 +734,20 @@ private String convertToUnixFilesystemPath(String path) { * Use local Docker Compose binary, if present. */ class LocalDockerCompose implements DockerCompose { + /** * Executable name for Docker Compose. */ - private static final String COMPOSE_EXECUTABLE = SystemUtils.IS_OS_WINDOWS ? "docker-compose.exe" : "docker-compose"; + private static final String COMPOSE_EXECUTABLE = SystemUtils.IS_OS_WINDOWS + ? "docker-compose.exe" + : "docker-compose"; private final List composeFiles; + private final String identifier; + private String cmd = ""; + private Map env = new HashMap<>(); public LocalDockerCompose(List composeFiles, String identifier) { @@ -719,7 +776,9 @@ static boolean executableExists() { public void invoke() { // bail out early if (!executableExists()) { - throw new ContainerLaunchException("Local Docker Compose not found. Is " + COMPOSE_EXECUTABLE + " on the PATH?"); + throw new ContainerLaunchException( + "Local Docker Compose not found. Is " + COMPOSE_EXECUTABLE + " on the PATH?" + ); } final Map environment = Maps.newHashMap(env); @@ -732,18 +791,23 @@ public void invoke() { environment.put("DOCKER_CERT_PATH", ((LocalDirectorySSLConfig) sslConfig).getDockerCertPath()); environment.put("DOCKER_TLS_VERIFY", "true"); } else { - logger().warn("Couldn't set DOCKER_CERT_PATH. `sslConfig` is present but it's not LocalDirectorySSLConfig."); + logger() + .warn( + "Couldn't set DOCKER_CERT_PATH. `sslConfig` is present but it's not LocalDirectorySSLConfig." + ); } } String dockerHost = transportConfig.getDockerHost().toString(); environment.put("DOCKER_HOST", dockerHost); - final Stream absoluteDockerComposeFilePaths = composeFiles.stream() + final Stream absoluteDockerComposeFilePaths = composeFiles + .stream() .map(File::getAbsolutePath) .map(Objects::toString); final String composeFileEnvVariableValue = absoluteDockerComposeFilePaths.collect( - joining(File.pathSeparator + "")); + Collectors.joining(File.pathSeparator + "") + ); logger().debug("Set env COMPOSE_FILE={}", composeFileEnvVariableValue); final File pwd = composeFiles.get(0).getAbsoluteFile().getParentFile().getAbsoluteFile(); @@ -751,12 +815,14 @@ public void invoke() { logger().info("Local Docker Compose is running command: {}", cmd); - final List command = Splitter.onPattern(" ") + final List command = Splitter + .onPattern(" ") .omitEmptyStrings() .splitToList(COMPOSE_EXECUTABLE + " " + cmd); try { - new ProcessExecutor().command(command) + new ProcessExecutor() + .command(command) .redirectOutput(Slf4jStream.of(logger()).asInfo()) .redirectError(Slf4jStream.of(logger()).asInfo()) // docker-compose will log pull information to stderr .environment(environment) @@ -765,11 +831,13 @@ public void invoke() { .executeNoTimeout(); logger().info("Docker Compose has finished running"); - } catch (InvalidExitValueException e) { - throw new ContainerLaunchException("Local Docker Compose exited abnormally with code " + - e.getExitValue() + " whilst running command: " + cmd); - + throw new ContainerLaunchException( + "Local Docker Compose exited abnormally with code " + + e.getExitValue() + + " whilst running command: " + + cmd + ); } catch (Exception e) { throw new ContainerLaunchException("Error running local Docker Compose command: " + cmd, e); } diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeFiles.java b/core/src/main/java/org/testcontainers/containers/DockerComposeFiles.java index 76754df5118..b96ebfb005e 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeFiles.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeFiles.java @@ -33,7 +33,9 @@ private Map> mergeServiceDependencyImageNames() { } private Set getImageNames(Map> serviceToImageNames) { - return serviceToImageNames.values().stream() + return serviceToImageNames + .values() + .stream() .flatMap(Collection::stream) // Pass through DockerImageName to convert image names to canonical form (e.g. making implicit latest tag explicit) .map(DockerImageName::parse) diff --git a/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java b/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java index 0858476880f..fe641618cc4 100644 --- a/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java +++ b/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java @@ -23,7 +23,6 @@ @Slf4j public class ExecInContainerPattern { - /** * * @deprecated use {@link #execInContainer(DockerClient, InspectContainerResponse, String...)} @@ -40,8 +39,11 @@ public Container.ExecResult execInContainer(InspectContainerResponse containerIn * @deprecated use {@link #execInContainer(DockerClient, InspectContainerResponse, Charset, String...)} */ @Deprecated - public Container.ExecResult execInContainer(InspectContainerResponse containerInfo, Charset outputCharset, String... command) - throws UnsupportedOperationException, IOException, InterruptedException { + public Container.ExecResult execInContainer( + InspectContainerResponse containerInfo, + Charset outputCharset, + String... command + ) throws UnsupportedOperationException, IOException, InterruptedException { DockerClient dockerClient = DockerClientFactory.instance().client(); return execInContainer(dockerClient, containerInfo, outputCharset, command); } @@ -55,8 +57,11 @@ public Container.ExecResult execInContainer(InspectContainerResponse containerIn * @param command the command to execute * @see #execInContainer(DockerClient, InspectContainerResponse, Charset, String...) */ - public Container.ExecResult execInContainer(DockerClient dockerClient, InspectContainerResponse containerInfo, String... command) - throws UnsupportedOperationException, IOException, InterruptedException { + public Container.ExecResult execInContainer( + DockerClient dockerClient, + InspectContainerResponse containerInfo, + String... command + ) throws UnsupportedOperationException, IOException, InterruptedException { return execInContainer(dockerClient, containerInfo, StandardCharsets.UTF_8, command); } @@ -74,13 +79,17 @@ public Container.ExecResult execInContainer(DockerClient dockerClient, InspectCo * @throws InterruptedException if the thread waiting for the response is interrupted * @throws UnsupportedOperationException if the docker daemon you're connecting to doesn't support "exec". */ - public Container.ExecResult execInContainer(DockerClient dockerClient, InspectContainerResponse containerInfo, Charset outputCharset, String... command) - throws UnsupportedOperationException, IOException, InterruptedException { + public Container.ExecResult execInContainer( + DockerClient dockerClient, + InspectContainerResponse containerInfo, + Charset outputCharset, + String... command + ) throws UnsupportedOperationException, IOException, InterruptedException { if (!TestEnvironment.dockerExecutionDriverSupportsExec()) { // at time of writing, this is the expected result in CircleCI. throw new UnsupportedOperationException( - "Your docker daemon is running the \"lxc\" driver, which doesn't support \"docker exec\"."); - + "Your docker daemon is running the \"lxc\" driver, which doesn't support \"docker exec\"." + ); } if (!isRunning(containerInfo)) { @@ -91,8 +100,12 @@ public Container.ExecResult execInContainer(DockerClient dockerClient, InspectCo String containerName = containerInfo.getName(); log.debug("{}: Running \"exec\" command: {}", containerName, String.join(" ", command)); - final ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(containerId) - .withAttachStdout(true).withAttachStderr(true).withCmd(command).exec(); + final ExecCreateCmdResponse execCreateCmdResponse = dockerClient + .execCreateCmd(containerId) + .withAttachStdout(true) + .withAttachStderr(true) + .withCmd(command) + .exec(); final ToStringConsumer stdoutConsumer = new ToStringConsumer(); final ToStringConsumer stderrConsumer = new ToStringConsumer(); @@ -108,7 +121,8 @@ public Container.ExecResult execInContainer(DockerClient dockerClient, InspectCo final Container.ExecResult result = new Container.ExecResult( exitCode, stdoutConsumer.toString(outputCharset), - stderrConsumer.toString(outputCharset)); + stderrConsumer.toString(outputCharset) + ); log.trace("{}: stdout: {}", containerName, result.getStdout()); log.trace("{}: stderr: {}", containerName, result.getStderr()); diff --git a/core/src/main/java/org/testcontainers/containers/FailureDetectingExternalResource.java b/core/src/main/java/org/testcontainers/containers/FailureDetectingExternalResource.java index 29a032d320b..71344267d32 100644 --- a/core/src/main/java/org/testcontainers/containers/FailureDetectingExternalResource.java +++ b/core/src/main/java/org/testcontainers/containers/FailureDetectingExternalResource.java @@ -16,13 +16,12 @@ * figures). */ public class FailureDetectingExternalResource implements TestRule { + @Override public Statement apply(Statement base, Description description) { - return new Statement() { @Override public void evaluate() throws Throwable { - List errors = new ArrayList(); try { @@ -41,16 +40,11 @@ public void evaluate() throws Throwable { }; } - protected void starting(Description description) { - - } + protected void starting(Description description) {} - protected void succeeded(Description description) { - } + protected void succeeded(Description description) {} - protected void failed(Throwable e, Description description) { - } + protected void failed(Throwable e, Description description) {} - protected void finished(Description description) { - } + protected void finished(Description description) {} } diff --git a/core/src/main/java/org/testcontainers/containers/FixedHostPortGenericContainer.java b/core/src/main/java/org/testcontainers/containers/FixedHostPortGenericContainer.java index 6477c28042e..db610ad57c2 100644 --- a/core/src/main/java/org/testcontainers/containers/FixedHostPortGenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/FixedHostPortGenericContainer.java @@ -11,7 +11,8 @@ *

Callers are responsible for ensuring that this fixed port is actually available; failure will occur if it is * not available - which could manifest as flaky or unstable tests.

*/ -public class FixedHostPortGenericContainer> extends GenericContainer { +public class FixedHostPortGenericContainer> + extends GenericContainer { /** * @deprecated it is highly recommended that {@link FixedHostPortGenericContainer} not be used, as it risks port conflicts. @@ -28,7 +29,6 @@ public FixedHostPortGenericContainer(@NotNull String dockerImageName) { * @return this container */ public SELF withFixedExposedPort(int hostPort, int containerPort) { - return withFixedExposedPort(hostPort, containerPort, InternetProtocol.TCP); } @@ -40,7 +40,6 @@ public SELF withFixedExposedPort(int hostPort, int containerPort) { * @return this container */ public SELF withFixedExposedPort(int hostPort, int containerPort, InternetProtocol protocol) { - super.addFixedExposedPort(hostPort, containerPort, protocol); return self(); diff --git a/core/src/main/java/org/testcontainers/containers/FutureContainer.java b/core/src/main/java/org/testcontainers/containers/FutureContainer.java index b2269b534c1..28cfbfff6c9 100644 --- a/core/src/main/java/org/testcontainers/containers/FutureContainer.java +++ b/core/src/main/java/org/testcontainers/containers/FutureContainer.java @@ -8,5 +8,6 @@ */ @Data public class FutureContainer implements LinkableContainer { + private final String containerName; } diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 4e5e682738d..7163ea40a93 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -20,6 +20,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; import com.google.common.hash.Hashing; import lombok.AccessLevel; import lombok.Data; @@ -27,7 +28,6 @@ import lombok.NonNull; import lombok.Setter; import lombok.SneakyThrows; -import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.SystemUtils; import org.jetbrains.annotations.NotNull; @@ -56,6 +56,7 @@ import org.testcontainers.lifecycle.TestDescription; import org.testcontainers.lifecycle.TestLifecycleAware; import org.testcontainers.utility.Base58; +import org.testcontainers.utility.CommandLine; import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.DockerLoggerFactory; import org.testcontainers.utility.DockerMachineClient; @@ -66,12 +67,10 @@ import org.testcontainers.utility.TestcontainersConfiguration; import java.io.File; -import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.UndeclaredThrowableException; import java.nio.charset.Charset; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; @@ -101,17 +100,15 @@ import java.util.zip.Adler32; import java.util.zip.Checksum; -import static com.google.common.collect.Lists.newArrayList; import static org.awaitility.Awaitility.await; -import static org.testcontainers.utility.CommandLine.runShellCommand; /** * Base class for that allows a container to be launched and controlled. */ @Data public class GenericContainer> - extends FailureDetectingExternalResource - implements Container, AutoCloseable, WaitStrategyTarget, Startable { + extends FailureDetectingExternalResource + implements Container, AutoCloseable, WaitStrategyTarget, Startable { private static final Charset UTF8 = Charset.forName("UTF-8"); @@ -142,9 +139,7 @@ public class GenericContainer> private Network network; @NonNull - private List networkAliases = new ArrayList<>(Arrays.asList( - "tc-" + Base58.randomString(8) - )); + private List networkAliases = new ArrayList<>(Arrays.asList("tc-" + Base58.randomString(8))); @NonNull private RemoteDockerImage image; @@ -228,6 +223,7 @@ public class GenericContainer> private final Set> createContainerCmdModifiers = new LinkedHashSet<>(); private static final Set AVAILABLE_IMAGE_NAME_CACHE = new HashSet<>(); + private static final RateLimiter DOCKER_CLIENT_RATE_LIMITER = RateLimiterBuilder .newBuilder() .withRate(1, TimeUnit.SECONDS) @@ -315,7 +311,7 @@ public String getContainerId() { * Starts the container using docker, pulling an image if necessary. */ @Override - @SneakyThrows({InterruptedException.class, ExecutionException.class}) + @SneakyThrows({ InterruptedException.class, ExecutionException.class }) public void start() { if (containerId != null) { return; @@ -335,12 +331,20 @@ protected void doStart() { logger().debug("Starting container: {}", getDockerImageName()); AtomicInteger attempt = new AtomicInteger(0); - Unreliables.retryUntilSuccess(startupAttempts, () -> { - logger().debug("Trying to start container: {} (attempt {}/{})", getDockerImageName(), attempt.incrementAndGet(), startupAttempts); - tryStart(startedAt); - return true; - }); - + Unreliables.retryUntilSuccess( + startupAttempts, + () -> { + logger() + .debug( + "Trying to start container: {} (attempt {}/{})", + getDockerImageName(), + attempt.incrementAndGet(), + startupAttempts + ); + tryStart(startedAt); + return true; + } + ); } catch (Exception e) { throw new ContainerLaunchException("Container startup failed", e); } @@ -383,10 +387,9 @@ private void tryStart(Instant startedAt) { } if (TestcontainersConfiguration.getInstance().environmentSupportsReuse()) { - createCommand.getLabels().put( - COPIED_FILES_HASH_LABEL, - Long.toHexString(hashCopiedFiles().getValue()) - ); + createCommand + .getLabels() + .put(COPIED_FILES_HASH_LABEL, Long.toHexString(hashCopiedFiles().getValue())); String hash = hash(createCommand); @@ -402,12 +405,13 @@ private void tryStart(Instant startedAt) { } reusable = true; } else { - logger().warn( - "" + + logger() + .warn( + "" + "Reuse was requested but the environment does not support the reuse of containers\n" + "To enable reuse of containers, you must set 'testcontainers.reuse.enable=true' in a file located at {}", - Paths.get(System.getProperty("user.home"), ".testcontainers.properties") - ); + Paths.get(System.getProperty("user.home"), ".testcontainers.properties") + ); reusable = false; } } else { @@ -443,27 +447,28 @@ private void tryStart(Instant startedAt) { this.logConsumers.forEach(this::followOutput); // Wait until inspect container returns the mapped ports - containerInfo = await() - .atMost(5, TimeUnit.SECONDS) - .pollInterval(DynamicPollInterval.ofMillis(50)) - .pollInSameThread() - .until( - () -> dockerClient.inspectContainerCmd(containerId).exec(), - inspectContainerResponse -> { - Set exposedAndMappedPorts = inspectContainerResponse - .getNetworkSettings() - .getPorts() - .getBindings() - .entrySet() - .stream() - .filter(it -> Objects.nonNull(it.getValue())) // filter out exposed but not yet mapped - .map(Entry::getKey) - .map(ExposedPort::getPort) - .collect(Collectors.toSet()); - - return exposedAndMappedPorts.containsAll(exposedPorts); - } - ); + containerInfo = + await() + .atMost(5, TimeUnit.SECONDS) + .pollInterval(DynamicPollInterval.ofMillis(50)) + .pollInSameThread() + .until( + () -> dockerClient.inspectContainerCmd(containerId).exec(), + inspectContainerResponse -> { + Set exposedAndMappedPorts = inspectContainerResponse + .getNetworkSettings() + .getPorts() + .getBindings() + .entrySet() + .stream() + .filter(it -> Objects.nonNull(it.getValue())) // filter out exposed but not yet mapped + .map(Entry::getKey) + .map(ExposedPort::getPort) + .collect(Collectors.toSet()); + + return exposedAndMappedPorts.containsAll(exposedPorts); + } + ); // Tell subclasses that we're starting containerIsStarting(containerInfo, reused); @@ -541,9 +546,11 @@ private void tryStart(Instant startedAt) { @VisibleForTesting Checksum hashCopiedFiles() { Checksum checksum = new Adler32(); - Stream.of(copyToFileContainerPathMap, copyToTransferableContainerPathMap) + Stream + .of(copyToFileContainerPathMap, copyToTransferableContainerPathMap) .flatMap(it -> it.entrySet().stream()) - .sorted(Entry.comparingByValue()).forEach(entry -> { + .sorted(Entry.comparingByValue()) + .forEach(entry -> { byte[] pathBytes = entry.getValue().getBytes(); // Add path to the hash checksum.update(pathBytes, 0, pathBytes.length); @@ -558,7 +565,8 @@ Checksum hashCopiedFiles() { final String hash(CreateContainerCmd createCommand) { DefaultDockerClientConfig dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder().build(); - byte[] commandJson = dockerClientConfig.getObjectMapper() + byte[] commandJson = dockerClientConfig + .getObjectMapper() .copy() .enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY) .enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS) @@ -571,7 +579,8 @@ final String hash(CreateContainerCmd createCommand) { @VisibleForTesting Optional findContainerForReuse(String hash) { // TODO locking - return dockerClient.listContainersCmd() + return dockerClient + .listContainersCmd() .withLabelFilter(ImmutableMap.of(HASH_LABEL, hash)) .withLimit(1) .withStatusFilter(Arrays.asList("running")) @@ -596,11 +605,14 @@ private HostConfig buildHostConfig() { } private void connectToPortForwardingNetwork(String networkMode) { - PortForwardingContainer.INSTANCE.getNetwork().map(ContainerNetwork::getNetworkID).ifPresent(networkId -> { - if (!Arrays.asList(networkId, "none", "host").contains(networkMode)) { - dockerClient.connectToNetworkCmd().withContainerId(containerId).withNetworkId(networkId).exec(); - } - }); + PortForwardingContainer.INSTANCE + .getNetwork() + .map(ContainerNetwork::getNetworkID) + .ifPresent(networkId -> { + if (!Arrays.asList(networkId, "none", "host").contains(networkMode)) { + dockerClient.connectToNetworkCmd().withContainerId(containerId).withNetworkId(networkId).exec(); + } + }); } /** @@ -608,7 +620,6 @@ private void connectToPortForwardingNetwork(String networkMode) { */ @Override public void stop() { - if (containerId == null) { return; } @@ -651,35 +662,40 @@ protected Path createVolumeDirectory(boolean temporary) { Path directory = new File(".tmp-volume-" + UUID.randomUUID()).toPath(); PathUtils.mkdirp(directory); - if (temporary) Runtime.getRuntime().addShutdownHook(new Thread(DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, () -> { - PathUtils.recursiveDeleteDir(directory); - })); + if (temporary) { + Runtime + .getRuntime() + .addShutdownHook( + new Thread( + DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, + () -> { + PathUtils.recursiveDeleteDir(directory); + } + ) + ); + } return directory; } - protected void configure() { - } + protected void configure() {} - @SuppressWarnings({"EmptyMethod", "UnusedParameters"}) - protected void containerIsCreated(String containerId) { - } + @SuppressWarnings({ "EmptyMethod", "UnusedParameters" }) + protected void containerIsCreated(String containerId) {} - @SuppressWarnings({"EmptyMethod", "UnusedParameters"}) - protected void containerIsStarting(InspectContainerResponse containerInfo) { - } + @SuppressWarnings({ "EmptyMethod", "UnusedParameters" }) + protected void containerIsStarting(InspectContainerResponse containerInfo) {} - @SuppressWarnings({"EmptyMethod", "UnusedParameters"}) + @SuppressWarnings({ "EmptyMethod", "UnusedParameters" }) @UnstableAPI protected void containerIsStarting(InspectContainerResponse containerInfo, boolean reused) { containerIsStarting(containerInfo); } - @SuppressWarnings({"EmptyMethod", "UnusedParameters"}) - protected void containerIsStarted(InspectContainerResponse containerInfo) { - } + @SuppressWarnings({ "EmptyMethod", "UnusedParameters" }) + protected void containerIsStarted(InspectContainerResponse containerInfo) {} - @SuppressWarnings({"EmptyMethod", "UnusedParameters"}) + @SuppressWarnings({ "EmptyMethod", "UnusedParameters" }) @UnstableAPI protected void containerIsStarted(InspectContainerResponse containerInfo, boolean reused) { containerIsStarted(containerInfo); @@ -690,18 +706,16 @@ protected void containerIsStarted(InspectContainerResponse containerInfo, boolea * Warning! This hook won't be executed if the container is terminated during * the JVM's shutdown hook or by Ryuk. */ - @SuppressWarnings({"EmptyMethod", "UnusedParameters"}) - protected void containerIsStopping(InspectContainerResponse containerInfo) { - } + @SuppressWarnings({ "EmptyMethod", "UnusedParameters" }) + protected void containerIsStopping(InspectContainerResponse containerInfo) {} /** * A hook that is executed after the container is stopped with {@link #stop()}. * Warning! This hook won't be executed if the container is terminated during * the JVM's shutdown hook or by Ryuk. */ - @SuppressWarnings({"EmptyMethod", "UnusedParameters"}) - protected void containerIsStopped(InspectContainerResponse containerInfo) { - } + @SuppressWarnings({ "EmptyMethod", "UnusedParameters" }) + protected void containerIsStopped(InspectContainerResponse containerInfo) {} /** * @return the port on which to check if the container is ready @@ -772,35 +786,36 @@ private void applyConfiguration(CreateContainerCmd createCommand) { createCommand.withCmd(commandParts); } - String[] envArray = env.entrySet().stream() - .filter(it -> it.getValue() != null) - .map(it -> it.getKey() + "=" + it.getValue()) - .toArray(String[]::new); + String[] envArray = env + .entrySet() + .stream() + .filter(it -> it.getValue() != null) + .map(it -> it.getKey() + "=" + it.getValue()) + .toArray(String[]::new); createCommand.withEnv(envArray); - boolean shouldCheckFileMountingSupport = binds.size() > 0 && !TestcontainersConfiguration.getInstance().isDisableChecks(); + boolean shouldCheckFileMountingSupport = + binds.size() > 0 && !TestcontainersConfiguration.getInstance().isDisableChecks(); if (shouldCheckFileMountingSupport) { if (!DockerClientFactory.instance().isFileMountingSupported()) { - logger().warn( - "Unable to mount a file from test host into a running container. " + + logger() + .warn( + "Unable to mount a file from test host into a running container. " + "This may be a misconfiguration or limitation of your Docker environment. " + "Some features might not work." - ); + ); } } - Bind[] bindsArray = binds.stream() - .toArray(Bind[]::new); + Bind[] bindsArray = binds.stream().toArray(Bind[]::new); createCommand.withBinds(bindsArray); - VolumesFrom[] volumesFromsArray = volumesFroms.stream() - .toArray(VolumesFrom[]::new); + VolumesFrom[] volumesFromsArray = volumesFroms.stream().toArray(VolumesFrom[]::new); createCommand.withVolumesFrom(volumesFromsArray); Set allLinks = new HashSet<>(); Set allLinkedContainerNetworks = new HashSet<>(); for (Entry linkEntries : linkedContainers.entrySet()) { - String alias = linkEntries.getKey(); LinkableContainer linkableContainer = linkEntries.getValue(); @@ -808,9 +823,11 @@ private void applyConfiguration(CreateContainerCmd createCommand) { allLinks.addAll(links); if (allLinks.size() == 0) { - throw new ContainerLaunchException("Aborting attempt to link to container " + - linkableContainer.getContainerName() + - " as it is not running"); + throw new ContainerLaunchException( + "Aborting attempt to link to container " + + linkableContainer.getContainerName() + + " as it is not running" + ); } Set linkedContainerNetworks = findAllNetworksForLinkedContainers(linkableContainer); @@ -821,9 +838,12 @@ private void applyConfiguration(CreateContainerCmd createCommand) { allLinkedContainerNetworks.remove("bridge"); if (allLinkedContainerNetworks.size() > 1) { - logger().warn("Container needs to be on more than one custom network to link to other " + - "containers - this is not currently supported. Required networks are: {}", - allLinkedContainerNetworks); + logger() + .warn( + "Container needs to be on more than one custom network to link to other " + + "containers - this is not currently supported. Required networks are: {}", + allLinkedContainerNetworks + ); } Optional networkForLinks = allLinkedContainerNetworks.stream().findFirst(); @@ -835,12 +855,13 @@ private void applyConfiguration(CreateContainerCmd createCommand) { if (hostAccessible) { PortForwardingContainer.INSTANCE.start(); } - PortForwardingContainer.INSTANCE.getNetwork().ifPresent(it -> { - withExtraHost(INTERNAL_HOST_HOSTNAME, it.getIpAddress()); - }); + PortForwardingContainer.INSTANCE + .getNetwork() + .ifPresent(it -> { + withExtraHost(INTERNAL_HOST_HOSTNAME, it.getIpAddress()); + }); - String[] extraHostsArray = extraHosts.stream() - .toArray(String[]::new); + String[] extraHostsArray = extraHosts.stream().toArray(String[]::new); createCommand.withExtraHosts(extraHostsArray); if (network != null) { @@ -870,23 +891,29 @@ private void applyConfiguration(CreateContainerCmd createCommand) { } private Set findLinksFromThisContainer(String alias, LinkableContainer linkableContainer) { - return dockerClient.listContainersCmd() - .withStatusFilter(Arrays.asList("running")) - .exec().stream() - .flatMap(container -> Stream.of(container.getNames())) - .filter(name -> name.endsWith(linkableContainer.getContainerName())) - .map(name -> new Link(name, alias)) - .collect(Collectors.toSet()); + return dockerClient + .listContainersCmd() + .withStatusFilter(Arrays.asList("running")) + .exec() + .stream() + .flatMap(container -> Stream.of(container.getNames())) + .filter(name -> name.endsWith(linkableContainer.getContainerName())) + .map(name -> new Link(name, alias)) + .collect(Collectors.toSet()); } private Set findAllNetworksForLinkedContainers(LinkableContainer linkableContainer) { - return dockerClient.listContainersCmd().exec().stream() - .filter(container -> container.getNames()[0].endsWith(linkableContainer.getContainerName())) - .filter(container -> container.getNetworkSettings() != null && - container.getNetworkSettings().getNetworks() != null) - .flatMap(container -> container.getNetworkSettings().getNetworks().keySet().stream()) - .distinct() - .collect(Collectors.toSet()); + return dockerClient + .listContainersCmd() + .exec() + .stream() + .filter(container -> container.getNames()[0].endsWith(linkableContainer.getContainerName())) + .filter(container -> { + return container.getNetworkSettings() != null && container.getNetworkSettings().getNetworks() != null; + }) + .flatMap(container -> container.getNetworkSettings().getNetworks().keySet().stream()) + .distinct() + .collect(Collectors.toSet()); } /** @@ -953,19 +980,12 @@ public Map getEnvMap() { */ @Override public List getEnv() { - return env.entrySet().stream() - .map(it -> it.getKey() + "=" + it.getValue()) - .collect(Collectors.toList()); + return env.entrySet().stream().map(it -> it.getKey() + "=" + it.getValue()).collect(Collectors.toList()); } @Override public void setEnv(List env) { - this.env = env.stream() - .map(it -> it.split("=")) - .collect(Collectors.toMap( - it -> it[0], - it -> it[1] - )); + this.env = env.stream().map(it -> it.split("=")).collect(Collectors.toMap(it -> it[0], it -> it[1])); } /** @@ -980,14 +1000,25 @@ public void addEnv(String key, String value) { * {@inheritDoc} */ @Override - public void addFileSystemBind(final String hostPath, final String containerPath, final BindMode mode, final SelinuxContext selinuxContext) { + public void addFileSystemBind( + final String hostPath, + final String containerPath, + final BindMode mode, + final SelinuxContext selinuxContext + ) { if (SystemUtils.IS_OS_WINDOWS && hostPath.startsWith("/")) { // e.g. Docker socket mount binds.add(new Bind(hostPath, new Volume(containerPath), mode.accessMode, selinuxContext.selContext)); - } else { final MountableFile mountableFile = MountableFile.forHostPath(hostPath); - binds.add(new Bind(mountableFile.getResolvedPath(), new Volume(containerPath), mode.accessMode, selinuxContext.selContext)); + binds.add( + new Bind( + mountableFile.getResolvedPath(), + new Volume(containerPath), + mode.accessMode, + selinuxContext.selContext + ) + ); } } @@ -1090,9 +1121,8 @@ protected void finished(Description description) { */ @Override public SELF withExposedPorts(Integer... ports) { - this.setExposedPorts(newArrayList(ports)); + this.setExposedPorts(Lists.newArrayList(ports)); return self(); - } /** @@ -1218,7 +1248,11 @@ public SELF withImagePullPolicy(ImagePullPolicy imagePullPolicy) { * {@inheritDoc} */ @Override - public SELF withClasspathResourceMapping(final String resourcePath, final String containerPath, final BindMode mode) { + public SELF withClasspathResourceMapping( + final String resourcePath, + final String containerPath, + final BindMode mode + ) { return withClasspathResourceMapping(resourcePath, containerPath, mode, SelinuxContext.NONE); } @@ -1226,7 +1260,12 @@ public SELF withClasspathResourceMapping(final String resourcePath, final String * {@inheritDoc} */ @Override - public SELF withClasspathResourceMapping(final String resourcePath, final String containerPath, final BindMode mode, final SelinuxContext selinuxContext) { + public SELF withClasspathResourceMapping( + final String resourcePath, + final String containerPath, + final BindMode mode, + final SelinuxContext selinuxContext + ) { final MountableFile mountableFile = MountableFile.forClasspathResource(resourcePath); if (mode == BindMode.READ_ONLY && selinuxContext == SelinuxContext.NONE) { @@ -1346,23 +1385,33 @@ public String getTestHostIpAddress() { throw new IllegalStateException("Could not find a default docker-machine instance"); } - String sshConnectionString = runShellCommand("docker-machine", "ssh", defaultMachine.get(), "echo $SSH_CONNECTION").trim(); + String sshConnectionString = CommandLine + .runShellCommand("docker-machine", "ssh", defaultMachine.get(), "echo $SSH_CONNECTION") + .trim(); if (Strings.isNullOrEmpty(sshConnectionString)) { - throw new IllegalStateException("Could not obtain SSH_CONNECTION environment variable for docker machine " + defaultMachine.get()); + throw new IllegalStateException( + "Could not obtain SSH_CONNECTION environment variable for docker machine " + + defaultMachine.get() + ); } String[] sshConnectionParts = sshConnectionString.split("\\s"); if (sshConnectionParts.length != 4) { - throw new IllegalStateException("Unexpected pattern for SSH_CONNECTION for docker machine - expected 'IP PORT IP PORT' pattern but found '" + sshConnectionString + "'"); + throw new IllegalStateException( + "Unexpected pattern for SSH_CONNECTION for docker machine - expected 'IP PORT IP PORT' pattern but found '" + + sshConnectionString + + "'" + ); } return sshConnectionParts[0]; } catch (Exception e) { throw new RuntimeException(e); } - } else { - throw new UnsupportedOperationException("getTestHostIpAddress() is only implemented for docker-machine right now"); + throw new UnsupportedOperationException( + "getTestHostIpAddress() is only implemented for docker-machine right now" + ); } } diff --git a/core/src/main/java/org/testcontainers/containers/InternetProtocol.java b/core/src/main/java/org/testcontainers/containers/InternetProtocol.java index 0df0480a1bf..6574a517cdf 100644 --- a/core/src/main/java/org/testcontainers/containers/InternetProtocol.java +++ b/core/src/main/java/org/testcontainers/containers/InternetProtocol.java @@ -4,7 +4,6 @@ * The IP protocols supported by Docker. */ public enum InternetProtocol { - TCP, UDP; diff --git a/core/src/main/java/org/testcontainers/containers/Network.java b/core/src/main/java/org/testcontainers/containers/Network.java index 046133815b6..fac7beff638 100644 --- a/core/src/main/java/org/testcontainers/containers/Network.java +++ b/core/src/main/java/org/testcontainers/containers/Network.java @@ -18,7 +18,6 @@ import java.util.function.Consumer; public interface Network extends AutoCloseable, TestRule { - Network SHARED = new NetworkImpl(false, null, Collections.emptySet(), null) { @Override public void close() { diff --git a/core/src/main/java/org/testcontainers/containers/ParsedDockerComposeFile.java b/core/src/main/java/org/testcontainers/containers/ParsedDockerComposeFile.java index e9457ba92bd..abb6deaab3d 100644 --- a/core/src/main/java/org/testcontainers/containers/ParsedDockerComposeFile.java +++ b/core/src/main/java/org/testcontainers/containers/ParsedDockerComposeFile.java @@ -26,7 +26,9 @@ class ParsedDockerComposeFile { private final Map composeFileContent; + private final String composeFileName; + private final File composeFile; @Getter @@ -57,14 +59,20 @@ private void parseAndValidate() { final Map servicesMap; if (composeFileContent.containsKey("version")) { if ("2.0".equals(composeFileContent.get("version"))) { - log.warn("Testcontainers may not be able to clean up networks spawned using Docker Compose v2.0 files. " + + log.warn( + "Testcontainers may not be able to clean up networks spawned using Docker Compose v2.0 files. " + "Please see https://github.com/testcontainers/moby-ryuk/issues/2, and specify 'version: \"2.1\"' or " + - "higher in {}", composeFileName); + "higher in {}", + composeFileName + ); } final Object servicesElement = composeFileContent.get("services"); if (servicesElement == null) { - log.debug("Compose file {} has an unknown format: 'version' is set but 'services' is not defined", composeFileName); + log.debug( + "Compose file {} has an unknown format: 'version' is set but 'services' is not defined", + composeFileName + ); return; } if (!(servicesElement instanceof Map)) { @@ -81,7 +89,11 @@ private void parseAndValidate() { String serviceName = entry.getKey(); Object serviceDefinition = entry.getValue(); if (!(serviceDefinition instanceof Map)) { - log.debug("Compose file {} has an unknown format: service '{}' is not Map", composeFileName, serviceName); + log.debug( + "Compose file {} has an unknown format: service '{}' is not Map", + composeFileName, + serviceName + ); break; } @@ -95,11 +107,13 @@ private void parseAndValidate() { private void validateNoContainerNameSpecified(String serviceName, Map serviceDefinitionMap) { if (serviceDefinitionMap.containsKey("container_name")) { - throw new IllegalStateException(String.format( - "Compose file %s has 'container_name' property set for service '%s' but this property is not supported by Testcontainers, consider removing it", - composeFileName, - serviceName - )); + throw new IllegalStateException( + String.format( + "Compose file %s has 'container_name' property set for service '%s' but this property is not supported by Testcontainers, consider removing it", + composeFileName, + serviceName + ) + ); } } @@ -120,28 +134,35 @@ private void findImageNamesInDockerfile(String serviceName, Map serviceDefinitio final Object dockerfileRelativePath = buildElement.get("dockerfile"); final Object contextRelativePath = buildElement.get("context"); if (dockerfileRelativePath instanceof String && contextRelativePath instanceof String) { - dockerfilePath = composeFile + dockerfilePath = + composeFile + .getAbsoluteFile() + .getParentFile() + .toPath() + .resolve((String) contextRelativePath) + .resolve((String) dockerfileRelativePath) + .normalize(); + } + } else if (buildNode instanceof String) { + dockerfilePath = + composeFile .getAbsoluteFile() .getParentFile() .toPath() - .resolve((String) contextRelativePath) - .resolve((String) dockerfileRelativePath) + .resolve((String) buildNode) + .resolve("./Dockerfile") .normalize(); - } - } else if (buildNode instanceof String) { - dockerfilePath = composeFile - .getAbsoluteFile() - .getParentFile() - .toPath() - .resolve((String) buildNode) - .resolve("./Dockerfile") - .normalize(); } if (dockerfilePath != null && Files.exists(dockerfilePath)) { Set resolvedImageNames = new ParsedDockerfile(dockerfilePath).getDependencyImageNames(); if (!resolvedImageNames.isEmpty()) { - log.debug("Resolved Dockerfile dependency images for Docker Compose in {} -> {}: {}", composeFileName, dockerfilePath, resolvedImageNames); + log.debug( + "Resolved Dockerfile dependency images for Docker Compose in {} -> {}: {}", + composeFileName, + dockerfilePath, + resolvedImageNames + ); this.serviceNameToImageNames.put(serviceName, resolvedImageNames); } } diff --git a/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java b/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java index a854b1e55c3..22d364c0762 100644 --- a/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java +++ b/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java @@ -30,15 +30,16 @@ public enum PortForwardingContainer { @SneakyThrows private Connection createSSHSession() { String password = UUID.randomUUID().toString(); - container = new GenericContainer<>(DockerImageName.parse("testcontainers/sshd:1.0.0")) - .withExposedPorts(22) - .withEnv("PASSWORD", password) - .withCommand( - "sh", - "-c", - // Disable ipv6 & Make it listen on all interfaces, not just localhost - "echo \"root:$PASSWORD\" | chpasswd && /usr/sbin/sshd -D -o PermitRootLogin=yes -o AddressFamily=inet -o GatewayPorts=yes" - ); + container = + new GenericContainer<>(DockerImageName.parse("testcontainers/sshd:1.0.0")) + .withExposedPorts(22) + .withEnv("PASSWORD", password) + .withCommand( + "sh", + "-c", + // Disable ipv6 & Make it listen on all interfaces, not just localhost + "echo \"root:$PASSWORD\" | chpasswd && /usr/sbin/sshd -D -o PermitRootLogin=yes -o AddressFamily=inet -o GatewayPorts=yes" + ); container.start(); Connection connection = new Connection(container.getHost(), container.getMappedPort(22)); @@ -64,7 +65,7 @@ public void exposeHostPort(int port) { @SneakyThrows public void exposeHostPort(int hostPort, int containerPort) { - if (exposedPorts.add(new AbstractMap.SimpleEntry<>(hostPort, containerPort))) { + if (exposedPorts.add(new AbstractMap.SimpleEntry<>(hostPort, containerPort))) { getSshConnection().requestRemotePortForwarding("", containerPort, "localhost", hostPort); } } @@ -74,7 +75,8 @@ void start() { } Optional getNetwork() { - return Optional.ofNullable(container) + return Optional + .ofNullable(container) .map(GenericContainer::getContainerInfo) .flatMap(it -> it.getNetworkSettings().getNetworks().values().stream().findFirst()); } diff --git a/core/src/main/java/org/testcontainers/containers/SelinuxContext.java b/core/src/main/java/org/testcontainers/containers/SelinuxContext.java index 735d3e3a277..ec814d5640a 100644 --- a/core/src/main/java/org/testcontainers/containers/SelinuxContext.java +++ b/core/src/main/java/org/testcontainers/containers/SelinuxContext.java @@ -13,5 +13,4 @@ public enum SelinuxContext { NONE(SELContext.none); public final SELContext selContext; - } diff --git a/core/src/main/java/org/testcontainers/containers/SocatContainer.java b/core/src/main/java/org/testcontainers/containers/SocatContainer.java index bc9ced57845..9455ac77227 100644 --- a/core/src/main/java/org/testcontainers/containers/SocatContainer.java +++ b/core/src/main/java/org/testcontainers/containers/SocatContainer.java @@ -1,10 +1,11 @@ package org.testcontainers.containers; +import org.testcontainers.utility.Base58; +import org.testcontainers.utility.DockerImageName; + import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; -import org.testcontainers.utility.Base58; -import org.testcontainers.utility.DockerImageName; /** * A socat container is used as a TCP proxy, enabling any TCP port of another container to be exposed @@ -36,10 +37,13 @@ public SocatContainer withTarget(int exposedPort, String host, int internalPort) @Override protected void configure() { - withCommand("-c", - targets.entrySet().stream() - .map(entry -> "socat TCP-LISTEN:" + entry.getKey() + ",fork,reuseaddr TCP:" + entry.getValue()) - .collect(Collectors.joining(" & ")) + withCommand( + "-c", + targets + .entrySet() + .stream() + .map(entry -> "socat TCP-LISTEN:" + entry.getKey() + ",fork,reuseaddr TCP:" + entry.getValue()) + .collect(Collectors.joining(" & ")) ); } } diff --git a/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java b/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java index be8a4ed735f..f629a141357 100644 --- a/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java +++ b/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java @@ -15,10 +15,9 @@ import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.time.Duration; +import java.time.temporal.ChronoUnit; import java.util.Base64; -import static java.time.temporal.ChronoUnit.SECONDS; - /** * 'Sidekick container' with the sole purpose of recording the VNC screen output from another container. * @@ -48,7 +47,9 @@ public class VncRecordingContainer extends GenericContainer targetContainer) { this( targetContainer.getNetwork(), - targetContainer.getNetworkAliases().stream() + targetContainer + .getNetworkAliases() + .stream() .findFirst() .orElseThrow(() -> new IllegalStateException("Target container must have a network alias")) ); @@ -57,14 +58,16 @@ public VncRecordingContainer(@NonNull GenericContainer targetContainer) { /** * Create a sidekick container and attach it to another container. The VNC output of that container will be recorded. */ - public VncRecordingContainer(@NonNull Network network, @NonNull String targetNetworkAlias) throws IllegalStateException { + public VncRecordingContainer(@NonNull Network network, @NonNull String targetNetworkAlias) + throws IllegalStateException { super(DockerImageName.parse("testcontainers/vnc-recorder:1.2.0")); - this.targetNetworkAlias = targetNetworkAlias; withNetwork(network); - waitingFor(new LogMessageWaitStrategy() - .withRegEx(".*Connected.*") - .withStartupTimeout(Duration.of(15, SECONDS))); + waitingFor( + new LogMessageWaitStrategy() + .withRegEx(".*Connected.*") + .withStartupTimeout(Duration.of(15, ChronoUnit.SECONDS)) + ); } public VncRecordingContainer withVncPassword(@NonNull String vncPassword) { @@ -95,8 +98,17 @@ protected void configure() { String encodedPassword = Base64.getEncoder().encodeToString(vncPassword.getBytes()); setCommand( "-c", - "echo '" + encodedPassword + "' | base64 -d > /vnc_password && " + - "flvrec.py -o " + ORIGINAL_RECORDING_FILE_NAME + " -d -r " + frameRate + " -P /vnc_password " + targetNetworkAlias + " " + vncPort + "echo '" + + encodedPassword + + "' | base64 -d > /vnc_password && " + + "flvrec.py -o " + + ORIGINAL_RECORDING_FILE_NAME + + " -d -r " + + frameRate + + " -P /vnc_password " + + targetNetworkAlias + + " " + + vncPort ); } @@ -122,7 +134,8 @@ public void saveRecordingToFile(@NonNull File file) { public enum VncRecordingFormat { FLV("flv") { @Override - String reencodeRecording(@NonNull VncRecordingContainer container, @NonNull String source) throws IOException, InterruptedException { + String reencodeRecording(@NonNull VncRecordingContainer container, @NonNull String source) + throws IOException, InterruptedException { String newFileOutput = "/newScreen.flv"; container.execInContainer("ffmpeg", "-i", source, "-vcodec", "libx264", newFileOutput); return newFileOutput; @@ -130,17 +143,29 @@ String reencodeRecording(@NonNull VncRecordingContainer container, @NonNull Stri }, MP4("mp4") { @Override - String reencodeRecording(@NonNull VncRecordingContainer container, @NonNull String source) throws IOException, InterruptedException { + String reencodeRecording(@NonNull VncRecordingContainer container, @NonNull String source) + throws IOException, InterruptedException { String newFileOutput = "/newScreen.mp4"; - container.execInContainer("ffmpeg", "-i", source, "-vcodec", "libx264", "-movflags", "faststart", "-pix_fmt", "yuv420p", newFileOutput); + container.execInContainer( + "ffmpeg", + "-i", + source, + "-vcodec", + "libx264", + "-movflags", + "faststart", + "-pix_fmt", + "yuv420p", + newFileOutput + ); return newFileOutput; } }; - abstract String reencodeRecording(VncRecordingContainer container, String source) throws IOException, InterruptedException; + abstract String reencodeRecording(VncRecordingContainer container, String source) + throws IOException, InterruptedException; @Getter private final String filenameExtension; } - } diff --git a/core/src/main/java/org/testcontainers/containers/output/BaseConsumer.java b/core/src/main/java/org/testcontainers/containers/output/BaseConsumer.java index 2967eb4d33a..7679ee8ab57 100644 --- a/core/src/main/java/org/testcontainers/containers/output/BaseConsumer.java +++ b/core/src/main/java/org/testcontainers/containers/output/BaseConsumer.java @@ -6,6 +6,7 @@ import java.util.function.Consumer; public abstract class BaseConsumer> implements Consumer { + @Getter @Setter private boolean removeColorCodes = true; diff --git a/core/src/main/java/org/testcontainers/containers/output/FrameConsumerResultCallback.java b/core/src/main/java/org/testcontainers/containers/output/FrameConsumerResultCallback.java index 8c7480320a8..75c24d1ac1d 100644 --- a/core/src/main/java/org/testcontainers/containers/output/FrameConsumerResultCallback.java +++ b/core/src/main/java/org/testcontainers/containers/output/FrameConsumerResultCallback.java @@ -1,6 +1,5 @@ package org.testcontainers.containers.output; - import com.github.dockerjava.api.async.ResultCallbackTemplate; import com.github.dockerjava.api.model.Frame; import com.github.dockerjava.api.model.StreamType; @@ -79,7 +78,7 @@ public void onError(Throwable throwable) { // Sink any errors try { close(); - } catch (IOException ignored) { } + } catch (IOException ignored) {} } @Override @@ -169,7 +168,6 @@ private String processAnsiColorCodes(String utf8String, Consumer co return utf8String; } - private byte[] merge(byte[] str1, byte[] str2) { byte[] mergedString = new byte[str1.length + str2.length]; System.arraycopy(str1, 0, mergedString, 0, str1.length); diff --git a/core/src/main/java/org/testcontainers/containers/output/OutputFrame.java b/core/src/main/java/org/testcontainers/containers/output/OutputFrame.java index d5ddcada8c8..f6eb5c18cbc 100644 --- a/core/src/main/java/org/testcontainers/containers/output/OutputFrame.java +++ b/core/src/main/java/org/testcontainers/containers/output/OutputFrame.java @@ -12,6 +12,7 @@ public class OutputFrame { public static final OutputFrame END = new OutputFrame(OutputType.END, null); private final OutputType type; + private final byte[] bytes; public OutputFrame(OutputType type, byte[] bytes) { @@ -28,7 +29,6 @@ public byte[] getBytes() { } public String getUtf8String() { - if (bytes == null) { return ""; } @@ -37,14 +37,20 @@ public String getUtf8String() { } public enum OutputType { - STDOUT, STDERR, END; + STDOUT, + STDERR, + END; public static OutputType forStreamType(StreamType streamType) { switch (streamType) { - case RAW: return STDOUT; - case STDOUT: return STDOUT; - case STDERR: return STDERR; - default: return null; + case RAW: + return STDOUT; + case STDOUT: + return STDOUT; + case STDERR: + return STDERR; + default: + return null; } } } @@ -56,5 +62,4 @@ public static OutputFrame forFrame(Frame frame) { } return new OutputFrame(outputType, frame.getPayload()); } - } diff --git a/core/src/main/java/org/testcontainers/containers/output/Slf4jLogConsumer.java b/core/src/main/java/org/testcontainers/containers/output/Slf4jLogConsumer.java index a274cd63004..ece7ab0e5b7 100644 --- a/core/src/main/java/org/testcontainers/containers/output/Slf4jLogConsumer.java +++ b/core/src/main/java/org/testcontainers/containers/output/Slf4jLogConsumer.java @@ -1,18 +1,22 @@ package org.testcontainers.containers.output; -import java.util.HashMap; -import java.util.Map; - import org.slf4j.Logger; import org.slf4j.MDC; +import java.util.HashMap; +import java.util.Map; + /** * A consumer for container output that logs output to an SLF4J logger. */ public class Slf4jLogConsumer extends BaseConsumer { + private final Logger logger; + private final Map mdc = new HashMap<>(); + private boolean separateOutputStreams; + private String prefix = ""; public Slf4jLogConsumer(Logger logger) { diff --git a/core/src/main/java/org/testcontainers/containers/output/ToStringConsumer.java b/core/src/main/java/org/testcontainers/containers/output/ToStringConsumer.java index 40ec60fb744..444434fa702 100644 --- a/core/src/main/java/org/testcontainers/containers/output/ToStringConsumer.java +++ b/core/src/main/java/org/testcontainers/containers/output/ToStringConsumer.java @@ -10,6 +10,7 @@ * Created by rnorth on 26/03/2016. */ public class ToStringConsumer extends BaseConsumer { + private ByteArrayOutputStream stringBuffer = new ByteArrayOutputStream(); @Override diff --git a/core/src/main/java/org/testcontainers/containers/output/WaitingConsumer.java b/core/src/main/java/org/testcontainers/containers/output/WaitingConsumer.java index 858f5a4b487..e9b34692319 100644 --- a/core/src/main/java/org/testcontainers/containers/output/WaitingConsumer.java +++ b/core/src/main/java/org/testcontainers/containers/output/WaitingConsumer.java @@ -71,14 +71,14 @@ public void waitUntil(Predicate predicate, int limit, TimeUnit limi * @param limitUnit maximum time to wait (units) * @param times number of times the predicate has to match */ - public void waitUntil(Predicate predicate, long limit, TimeUnit limitUnit, int times) throws TimeoutException { + public void waitUntil(Predicate predicate, long limit, TimeUnit limitUnit, int times) + throws TimeoutException { long expiry = limitUnit.toMillis(limit) + System.currentTimeMillis(); waitUntil(predicate, expiry, times); } private void waitUntil(Predicate predicate, long expiry, int times) throws TimeoutException { - int numberOfMatches = 0; while (System.currentTimeMillis() < expiry) { try { diff --git a/core/src/main/java/org/testcontainers/containers/startupcheck/IndefiniteWaitOneShotStartupCheckStrategy.java b/core/src/main/java/org/testcontainers/containers/startupcheck/IndefiniteWaitOneShotStartupCheckStrategy.java index f719cb1d476..a547b7885e3 100644 --- a/core/src/main/java/org/testcontainers/containers/startupcheck/IndefiniteWaitOneShotStartupCheckStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/startupcheck/IndefiniteWaitOneShotStartupCheckStrategy.java @@ -12,6 +12,7 @@ * It has to be assumed that the container will stop of its own accord, either with a success or failure exit code. */ public class IndefiniteWaitOneShotStartupCheckStrategy extends OneShotStartupCheckStrategy { + @Override public boolean waitUntilStartupSuccessful(DockerClient dockerClient, String containerId) { while (checkStartupState(dockerClient, containerId) == StartupStatus.NOT_YET_KNOWN) { diff --git a/core/src/main/java/org/testcontainers/containers/startupcheck/MinimumDurationRunningStartupCheckStrategy.java b/core/src/main/java/org/testcontainers/containers/startupcheck/MinimumDurationRunningStartupCheckStrategy.java index 3ce50446b77..ce29e0104e1 100644 --- a/core/src/main/java/org/testcontainers/containers/startupcheck/MinimumDurationRunningStartupCheckStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/startupcheck/MinimumDurationRunningStartupCheckStrategy.java @@ -35,5 +35,4 @@ public StartupStatus checkStartupState(DockerClient dockerClient, String contain } return StartupStatus.NOT_YET_KNOWN; } - } diff --git a/core/src/main/java/org/testcontainers/containers/startupcheck/StartupCheckStrategy.java b/core/src/main/java/org/testcontainers/containers/startupcheck/StartupCheckStrategy.java index d5931454212..50fdcdb8b9b 100644 --- a/core/src/main/java/org/testcontainers/containers/startupcheck/StartupCheckStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/startupcheck/StartupCheckStrategy.java @@ -10,20 +10,18 @@ import java.time.Duration; import java.util.concurrent.TimeUnit; -import static org.testcontainers.containers.GenericContainer.CONTAINER_RUNNING_TIMEOUT_SEC; - /** * Approach to determine whether a container has 'started up' correctly. */ public abstract class StartupCheckStrategy { private static final RateLimiter DOCKER_CLIENT_RATE_LIMITER = RateLimiterBuilder - .newBuilder() - .withRate(1, TimeUnit.SECONDS) - .withConstantThroughput() - .build(); + .newBuilder() + .withRate(1, TimeUnit.SECONDS) + .withConstantThroughput() + .build(); - private Duration timeout = Duration.ofSeconds(CONTAINER_RUNNING_TIMEOUT_SEC); + private Duration timeout = Duration.ofSeconds(GenericContainer.CONTAINER_RUNNING_TIMEOUT_SEC); @SuppressWarnings("unchecked") public SELF withTimeout(Duration timeout) { @@ -41,20 +39,27 @@ public boolean waitUntilStartupSuccessful(GenericContainer container) { } public boolean waitUntilStartupSuccessful(DockerClient dockerClient, String containerId) { - final Boolean[] startedOK = {null}; - Unreliables.retryUntilTrue((int) timeout.toMillis(), TimeUnit.MILLISECONDS, () -> { - //noinspection CodeBlock2Expr - return DOCKER_CLIENT_RATE_LIMITER.getWhenReady(() -> { - StartupStatus state = checkStartupState(dockerClient, containerId); - switch (state) { - case SUCCESSFUL: startedOK[0] = true; - return true; - case FAILED: startedOK[0] = false; - return true; - default: return false; - } - }); - }); + final Boolean[] startedOK = { null }; + Unreliables.retryUntilTrue( + (int) timeout.toMillis(), + TimeUnit.MILLISECONDS, + () -> { + //noinspection CodeBlock2Expr + return DOCKER_CLIENT_RATE_LIMITER.getWhenReady(() -> { + StartupStatus state = checkStartupState(dockerClient, containerId); + switch (state) { + case SUCCESSFUL: + startedOK[0] = true; + return true; + case FAILED: + startedOK[0] = false; + return true; + default: + return false; + } + }); + } + ); return startedOK[0]; } @@ -65,6 +70,8 @@ protected InspectContainerResponse.ContainerState getCurrentState(DockerClient d } public enum StartupStatus { - NOT_YET_KNOWN, SUCCESSFUL, FAILED + NOT_YET_KNOWN, + SUCCESSFUL, + FAILED, } } diff --git a/core/src/main/java/org/testcontainers/containers/traits/LinkableContainer.java b/core/src/main/java/org/testcontainers/containers/traits/LinkableContainer.java index 76e71378969..4973c4dc9c1 100644 --- a/core/src/main/java/org/testcontainers/containers/traits/LinkableContainer.java +++ b/core/src/main/java/org/testcontainers/containers/traits/LinkableContainer.java @@ -7,6 +7,5 @@ */ @Deprecated public interface LinkableContainer { - String getContainerName(); } diff --git a/core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java b/core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java index d543d952bbd..fbee9c2f9f4 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java +++ b/core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java @@ -14,21 +14,25 @@ */ @RequiredArgsConstructor public class ExternalPortListeningCheck implements Callable { + private final ContainerState containerState; + private final Set externalLivenessCheckPorts; @Override public Boolean call() { String address = containerState.getHost(); - externalLivenessCheckPorts.parallelStream().forEach(externalPort -> { - try (Socket socket = new Socket()) { - InetSocketAddress inetSocketAddress = new InetSocketAddress(address, externalPort); - socket.connect(inetSocketAddress, 1000); - } catch (IOException e) { - throw new IllegalStateException("Socket not listening yet: " + externalPort); - } - }); + externalLivenessCheckPorts + .parallelStream() + .forEach(externalPort -> { + try (Socket socket = new Socket()) { + InetSocketAddress inetSocketAddress = new InetSocketAddress(address, externalPort); + socket.connect(inetSocketAddress, 1000); + } catch (IOException e) { + throw new IllegalStateException("Socket not listening yet: " + externalPort); + } + }); return true; } } diff --git a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java index 1eba8acbbd1..aeedbea1e3f 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java +++ b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java @@ -10,8 +10,6 @@ import java.time.Instant; import java.util.Set; -import static java.lang.String.format; - /** * Mechanism for testing that a socket is listening when run from the container being checked. */ @@ -20,6 +18,7 @@ public class InternalCommandPortListeningCheck implements java.util.concurrent.Callable { private final WaitStrategyTarget waitStrategyTarget; + private final Set internalPorts; @Override @@ -29,19 +28,31 @@ public Boolean call() { for (int internalPort : internalPorts) { command.append(" && "); command.append(" ("); - command.append(format("grep -i ':0*%x' /proc/net/tcp*", internalPort)); + command.append(String.format("grep -i ':0*%x' /proc/net/tcp*", internalPort)); command.append(" || "); - command.append(format("nc -vz -w 1 localhost %d", internalPort)); + command.append(String.format("nc -vz -w 1 localhost %d", internalPort)); command.append(" || "); - command.append(format("/bin/bash -c ' externalLivenessCheckPorts = getLivenessCheckPorts(); if (externalLivenessCheckPorts.isEmpty()) { if (log.isDebugEnabled()) { - log.debug("Liveness check ports of {} is empty. Not waiting.", waitStrategyTarget.getContainerInfo().getName()); + log.debug( + "Liveness check ports of {} is empty. Not waiting.", + waitStrategyTarget.getContainerInfo().getName() + ); } return; } @@ -45,58 +48,69 @@ protected void waitUntilReady() { Callable internalCheck = new InternalCommandPortListeningCheck(waitStrategyTarget, internalPorts); - Callable externalCheck = new ExternalPortListeningCheck(waitStrategyTarget, externalLivenessCheckPorts); + Callable externalCheck = new ExternalPortListeningCheck( + waitStrategyTarget, + externalLivenessCheckPorts + ); try { - List> futures = EXECUTOR.invokeAll(Arrays.asList( - // Blocking - () -> { - Instant now = Instant.now(); - Boolean result = internalCheck.call(); - log.debug( - "Internal port check {} for {} in {}", - Boolean.TRUE.equals(result) ? "passed" : "failed", - internalPorts, - Duration.between(now, Instant.now()) - ); - return result; - }, - // Polling - () -> { - Instant now = Instant.now(); - Awaitility.await() - .pollInSameThread() - .pollInterval(Duration.ofMillis(100)) - .pollDelay(Duration.ZERO) - .ignoreExceptions() - .forever() - .until(externalCheck); + List> futures = EXECUTOR.invokeAll( + Arrays.asList( + // Blocking + () -> { + Instant now = Instant.now(); + Boolean result = internalCheck.call(); + log.debug( + "Internal port check {} for {} in {}", + Boolean.TRUE.equals(result) ? "passed" : "failed", + internalPorts, + Duration.between(now, Instant.now()) + ); + return result; + }, + // Polling + () -> { + Instant now = Instant.now(); + Awaitility + .await() + .pollInSameThread() + .pollInterval(Duration.ofMillis(100)) + .pollDelay(Duration.ZERO) + .ignoreExceptions() + .forever() + .until(externalCheck); - log.debug( - "External port check passed for {} mapped as {} in {}", - internalPorts, - externalLivenessCheckPorts, - Duration.between(now, Instant.now()) - ); - return true; - } - ), startupTimeout.getSeconds(), TimeUnit.SECONDS); + log.debug( + "External port check passed for {} mapped as {} in {}", + internalPorts, + externalLivenessCheckPorts, + Duration.between(now, Instant.now()) + ); + return true; + } + ), + startupTimeout.getSeconds(), + TimeUnit.SECONDS + ); for (Future future : futures) { future.get(0, TimeUnit.SECONDS); } } catch (CancellationException | ExecutionException | TimeoutException e) { - throw new ContainerLaunchException("Timed out waiting for container port to open (" + - waitStrategyTarget.getHost() + - " ports: " + - externalLivenessCheckPorts + - " should be listening)"); + throw new ContainerLaunchException( + "Timed out waiting for container port to open (" + + waitStrategyTarget.getHost() + + " ports: " + + externalLivenessCheckPorts + + " should be listening)" + ); } } private Set getInternalPorts(Set externalLivenessCheckPorts, List exposedPorts) { - return exposedPorts.stream() - .filter(it -> externalLivenessCheckPorts.contains(waitStrategyTarget.getMappedPort(it))) - .collect(Collectors.toSet()); + return exposedPorts + .stream() + .filter(it -> externalLivenessCheckPorts.contains(waitStrategyTarget.getMappedPort(it))) + .collect(Collectors.toSet()); } } diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java index d5d08ce6fd0..ccaef47a821 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java @@ -2,8 +2,6 @@ import com.google.common.base.Strings; import com.google.common.io.BaseEncoding; -import java.util.HashMap; -import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.rnorth.ducttape.TimeoutException; import org.testcontainers.containers.ContainerLaunchException; @@ -15,12 +13,14 @@ import java.net.MalformedURLException; import java.net.URI; import java.net.URL; -import java.time.Duration; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.cert.X509Certificate; +import java.time.Duration; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -48,16 +48,27 @@ public class HttpWaitStrategy extends AbstractWaitStrategy { private static final String AUTH_BASIC = "Basic "; private String path = "/"; + private String method = "GET"; + private Set statusCodes = new HashSet<>(); + private boolean tlsEnabled; + private String username; + private String password; + private final Map headers = new HashMap<>(); + private Predicate responsePredicate; + private Predicate statusCodePredicate = null; + private Optional livenessPort = Optional.empty(); + private Duration readTimeout = Duration.ofSeconds(1); + private boolean allowInsecure; /** @@ -196,14 +207,16 @@ public HttpWaitStrategy forResponsePredicate(Predicate responsePredicate protected void waitUntilReady() { final String containerName = waitStrategyTarget.getContainerInfo().getName(); - final Integer livenessCheckPort = livenessPort.map(waitStrategyTarget::getMappedPort).orElseGet(() -> { - final Set livenessCheckPorts = getLivenessCheckPorts(); - if (livenessCheckPorts == null || livenessCheckPorts.isEmpty()) { - log.warn("{}: No exposed ports or mapped ports - cannot wait for status", containerName); - return -1; - } - return livenessCheckPorts.iterator().next(); - }); + final Integer livenessCheckPort = livenessPort + .map(waitStrategyTarget::getMappedPort) + .orElseGet(() -> { + final Set livenessCheckPorts = getLivenessCheckPorts(); + if (livenessCheckPorts == null || livenessCheckPorts.isEmpty()) { + log.warn("{}: No exposed ports or mapped ports - cannot wait for status", containerName); + return -1; + } + return livenessCheckPorts.iterator().next(); + }); if (null == livenessCheckPort || -1 == livenessCheckPort) { return; @@ -213,11 +226,20 @@ protected void waitUntilReady() { try { // Un-map the port for logging - int originalPort = waitStrategyTarget.getExposedPorts().stream() + int originalPort = waitStrategyTarget + .getExposedPorts() + .stream() .filter(exposedPort -> rawUri.getPort() == waitStrategyTarget.getMappedPort(exposedPort)) .findFirst() .orElseThrow(() -> new IllegalStateException("Target port " + rawUri.getPort() + " is not exposed")); - log.info("{}: Waiting for {} seconds for URL: {} (where port {} maps to container port {})", containerName, startupTimeout.getSeconds(), uri, rawUri.getPort(), originalPort); + log.info( + "{}: Waiting for {} seconds for URL: {} (where port {} maps to container port {})", + containerName, + startupTimeout.getSeconds(), + uri, + rawUri.getPort(), + originalPort + ); } catch (RuntimeException e) { // do not allow a failure in logging to prevent progress, but log for diagnosis log.warn("Unexpected error occurred - will proceed to try to wait anyway", e); @@ -225,103 +247,114 @@ protected void waitUntilReady() { // try to connect to the URL try { - retryUntilSuccess((int) startupTimeout.getSeconds(), TimeUnit.SECONDS, () -> { - getRateLimiter().doWhenReady(() -> { - try { - final HttpURLConnection connection = openConnection(uri); - connection.setReadTimeout(Math.toIntExact(readTimeout.toMillis())); - - // authenticate - if (!Strings.isNullOrEmpty(username)) { - connection.setRequestProperty(HEADER_AUTHORIZATION, buildAuthString(username, password)); - connection.setUseCaches(false); - } - - // Add user configured headers - this.headers.forEach(connection::setRequestProperty); - connection.setRequestMethod(method); - connection.connect(); - - log.trace("Get response code {}", connection.getResponseCode()); - - // Choose the statusCodePredicate strategy depending on what we defined. - Predicate predicate; - if (statusCodes.isEmpty() && statusCodePredicate == null) { - // We have no status code and no predicate so we expect a 200 OK response code - predicate = responseCode -> HttpURLConnection.HTTP_OK == responseCode; - } else if (!statusCodes.isEmpty() && statusCodePredicate == null) { - // We use the default status predicate checker when we only have status codes - predicate = responseCode -> statusCodes.contains(responseCode); - } else if (statusCodes.isEmpty()) { - // We only have a predicate - predicate = statusCodePredicate; - } else { - // We have both predicate and status code - predicate = statusCodePredicate.or(responseCode -> statusCodes.contains(responseCode)); - } - if (!predicate.test(connection.getResponseCode())) { - throw new RuntimeException(String.format("HTTP response code was: %s", - connection.getResponseCode())); - } - - if(responsePredicate != null) { - String responseBody = getResponseBody(connection); - - log.trace("Get response {}", responseBody); - - if(!responsePredicate.test(responseBody)) { - throw new RuntimeException(String.format("Response: %s did not match predicate", - responseBody)); + retryUntilSuccess( + (int) startupTimeout.getSeconds(), + TimeUnit.SECONDS, + () -> { + getRateLimiter() + .doWhenReady(() -> { + try { + final HttpURLConnection connection = openConnection(uri); + connection.setReadTimeout(Math.toIntExact(readTimeout.toMillis())); + + // authenticate + if (!Strings.isNullOrEmpty(username)) { + connection.setRequestProperty( + HEADER_AUTHORIZATION, + buildAuthString(username, password) + ); + connection.setUseCaches(false); + } + + // Add user configured headers + this.headers.forEach(connection::setRequestProperty); + connection.setRequestMethod(method); + connection.connect(); + + log.trace("Get response code {}", connection.getResponseCode()); + + // Choose the statusCodePredicate strategy depending on what we defined. + Predicate predicate; + if (statusCodes.isEmpty() && statusCodePredicate == null) { + // We have no status code and no predicate so we expect a 200 OK response code + predicate = responseCode -> HttpURLConnection.HTTP_OK == responseCode; + } else if (!statusCodes.isEmpty() && statusCodePredicate == null) { + // We use the default status predicate checker when we only have status codes + predicate = responseCode -> statusCodes.contains(responseCode); + } else if (statusCodes.isEmpty()) { + // We only have a predicate + predicate = statusCodePredicate; + } else { + // We have both predicate and status code + predicate = + statusCodePredicate.or(responseCode -> statusCodes.contains(responseCode)); + } + if (!predicate.test(connection.getResponseCode())) { + throw new RuntimeException( + String.format("HTTP response code was: %s", connection.getResponseCode()) + ); + } + + if (responsePredicate != null) { + String responseBody = getResponseBody(connection); + + log.trace("Get response {}", responseBody); + + if (!responsePredicate.test(responseBody)) { + throw new RuntimeException( + String.format("Response: %s did not match predicate", responseBody) + ); + } + } + } catch (IOException e) { + throw new RuntimeException(e); } - } - - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - return true; - }); - + }); + return true; + } + ); } catch (TimeoutException e) { - throw new ContainerLaunchException(String.format( - "Timed out waiting for URL to be accessible (%s should return HTTP %s)", uri, statusCodes.isEmpty() ? - HttpURLConnection.HTTP_OK : statusCodes)); + throw new ContainerLaunchException( + String.format( + "Timed out waiting for URL to be accessible (%s should return HTTP %s)", + uri, + statusCodes.isEmpty() ? HttpURLConnection.HTTP_OK : statusCodes + ) + ); } } private HttpURLConnection openConnection(final String uri) throws IOException, MalformedURLException { if (tlsEnabled) { - final HttpsURLConnection connection = (HttpsURLConnection)new URL(uri).openConnection(); + final HttpsURLConnection connection = (HttpsURLConnection) new URL(uri).openConnection(); if (allowInsecure) { - // Create a trust manager that does not validate certificate chains + // Create a trust manager that does not validate certificate chains // and trust all certificates - final TrustManager[] trustAllCerts = new TrustManager[] { + final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { @Override - public X509Certificate[] getAcceptedIssuers() { + public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } - + @Override - public void checkClientTrusted(final X509Certificate[] certs, final String authType) { - } - + public void checkClientTrusted(final X509Certificate[] certs, final String authType) {} + @Override - public void checkServerTrusted(final X509Certificate[] certs, final String authType) { - } - } - }; + public void checkServerTrusted(final X509Certificate[] certs, final String authType) {} + }, + }; try { // Create custom SSL context and set the "trust all certificates" trust manager - final SSLContext sc = SSLContext.getInstance("SSL"); - sc.init(new KeyManager[0], trustAllCerts, new SecureRandom()); + final SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(new KeyManager[0], trustAllCerts, new SecureRandom()); connection.setSSLSocketFactory(sc.getSocketFactory()); } catch (final NoSuchAlgorithmException | KeyManagementException ex) { throw new IOException("Unable to create custom SSL factory instance", ex); } } - + return connection; } else { return (HttpURLConnection) new URL(uri).openConnection(); diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java index 65370e51bfa..7a095d888f2 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java @@ -2,7 +2,6 @@ import com.github.dockerjava.api.command.LogContainerCmd; import lombok.SneakyThrows; -import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.ContainerLaunchException; import org.testcontainers.containers.output.FrameConsumerResultCallback; import org.testcontainers.containers.output.OutputFrame; @@ -13,9 +12,6 @@ import java.util.concurrent.TimeoutException; import java.util.function.Predicate; -import static org.testcontainers.containers.output.OutputFrame.OutputType.STDERR; -import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; - public class LogMessageWaitStrategy extends AbstractWaitStrategy { private String regEx; @@ -27,22 +23,24 @@ public class LogMessageWaitStrategy extends AbstractWaitStrategy { protected void waitUntilReady() { WaitingConsumer waitingConsumer = new WaitingConsumer(); - LogContainerCmd cmd = waitStrategyTarget.getDockerClient().logContainerCmd(waitStrategyTarget.getContainerId()) + LogContainerCmd cmd = waitStrategyTarget + .getDockerClient() + .logContainerCmd(waitStrategyTarget.getContainerId()) .withFollowStream(true) .withSince(0) .withStdOut(true) .withStdErr(true); try (FrameConsumerResultCallback callback = new FrameConsumerResultCallback()) { - callback.addConsumer(STDOUT, waitingConsumer); - callback.addConsumer(STDERR, waitingConsumer); + callback.addConsumer(OutputFrame.OutputType.STDOUT, waitingConsumer); + callback.addConsumer(OutputFrame.OutputType.STDERR, waitingConsumer); cmd.exec(callback); - Predicate waitPredicate = outputFrame -> + Predicate waitPredicate = outputFrame -> { // (?s) enables line terminator matching (equivalent to Pattern.DOTALL) - outputFrame.getUtf8String().matches("(?s)" + regEx); - + return outputFrame.getUtf8String().matches("(?s)" + regEx); + }; try { waitingConsumer.waitUntil(waitPredicate, startupTimeout.getSeconds(), TimeUnit.SECONDS, times); } catch (TimeoutException e) { diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/Wait.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/Wait.java index 034a83a2949..6c3b9e86aec 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/Wait.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/Wait.java @@ -1,12 +1,11 @@ package org.testcontainers.containers.wait.strategy; -import java.net.HttpURLConnection; - /** * Convenience class with logic for building common {@link WaitStrategy} instances. * */ public class Wait { + /** * Convenience method to return the default WaitStrategy. * @@ -34,8 +33,7 @@ public static HostPortWaitStrategy forListeningPort() { * @see HttpWaitStrategy */ public static HttpWaitStrategy forHttp(String path) { - return new HttpWaitStrategy() - .forPath(path); + return new HttpWaitStrategy().forPath(path); } /** @@ -46,8 +44,7 @@ public static HttpWaitStrategy forHttp(String path) { * @see HttpWaitStrategy */ public static HttpWaitStrategy forHttps(String path) { - return forHttp(path) - .usingTls(); + return forHttp(path).usingTls(); } /** diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitAllStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitAllStrategy.java index cebacb375b6..5c7530fc67f 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitAllStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitAllStrategy.java @@ -1,16 +1,15 @@ package org.testcontainers.containers.wait.strategy; +import org.rnorth.ducttape.timeouts.Timeouts; + import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; -import org.rnorth.ducttape.timeouts.Timeouts; - public class WaitAllStrategy implements WaitStrategy { public enum Mode { - /** * This is the default mode: The timeout of the {@link WaitAllStrategy strategy} is applied to each individual * strategy, so that the container waits maximum for @@ -30,11 +29,13 @@ public enum Mode { * This is the original mode of this strategy: The inner strategies wait with their preconfigured timeout * individually and the wait all strategy kills them, if the outer limit is reached. */ - WITH_MAXIMUM_OUTER_TIMEOUT + WITH_MAXIMUM_OUTER_TIMEOUT, } private final Mode mode; + private final List strategies = new ArrayList<>(); + private Duration timeout = Duration.ofSeconds(30); public WaitAllStrategy() { @@ -50,9 +51,13 @@ public void waitUntilReady(WaitStrategyTarget waitStrategyTarget) { if (mode == Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY) { waitUntilNestedStrategiesAreReady(waitStrategyTarget); } else { - Timeouts.doWithTimeout((int) timeout.toMillis(), TimeUnit.MILLISECONDS, () -> { - waitUntilNestedStrategiesAreReady(waitStrategyTarget); - }); + Timeouts.doWithTimeout( + (int) timeout.toMillis(), + TimeUnit.MILLISECONDS, + () -> { + waitUntilNestedStrategiesAreReady(waitStrategyTarget); + } + ); } } @@ -63,7 +68,6 @@ private void waitUntilNestedStrategiesAreReady(WaitStrategyTarget waitStrategyTa } public WaitAllStrategy withStrategy(WaitStrategy strategy) { - if (mode == Mode.WITH_OUTER_TIMEOUT) { applyStartupTimeout(strategy); } @@ -74,10 +78,13 @@ public WaitAllStrategy withStrategy(WaitStrategy strategy) { @Override public WaitAllStrategy withStartupTimeout(Duration startupTimeout) { - if (mode == Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY) { - throw new IllegalStateException(String.format( - "Changing startup timeout is not supported with mode %s", Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY)); + throw new IllegalStateException( + String.format( + "Changing startup timeout is not supported with mode %s", + Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY + ) + ); } this.timeout = startupTimeout; diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitStrategy.java index 25703174502..ff371cf775f 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitStrategy.java @@ -3,7 +3,6 @@ import java.time.Duration; public interface WaitStrategy { - void waitUntilReady(WaitStrategyTarget waitStrategyTarget); WaitStrategy withStartupTimeout(Duration startupTimeout); diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitStrategyTarget.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitStrategyTarget.java index 49f88fb7711..778c252009b 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitStrategyTarget.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitStrategyTarget.java @@ -6,13 +6,15 @@ import java.util.stream.Collectors; public interface WaitStrategyTarget extends ContainerState { - /** * @return the ports on which to check if the container is ready */ default Set getLivenessCheckPortNumbers() { - final Set result = getExposedPorts().stream() - .map(this::getMappedPort).distinct().collect(Collectors.toSet()); + final Set result = getExposedPorts() + .stream() + .map(this::getMappedPort) + .distinct() + .collect(Collectors.toSet()); result.addAll(getBoundPortNumbers()); return result; } diff --git a/core/src/main/java/org/testcontainers/dockerclient/AuditLoggingDockerClient.java b/core/src/main/java/org/testcontainers/dockerclient/AuditLoggingDockerClient.java index 2dc88cad313..2b413aafaed 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/AuditLoggingDockerClient.java +++ b/core/src/main/java/org/testcontainers/dockerclient/AuditLoggingDockerClient.java @@ -5,13 +5,12 @@ import lombok.experimental.Delegate; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; +import org.testcontainers.utility.AuditLogger; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Proxy; import java.util.function.BiConsumer; -import static org.testcontainers.utility.AuditLogger.doLog; - /** * Wrapper for {@link DockerClient} to facilitate 'audit logging' of potentially destruction actions using * {@link org.testcontainers.utility.AuditLogger}. @@ -30,97 +29,117 @@ public AuditLoggingDockerClient(DockerClient wrappedClient) { @Override public CreateContainerCmd createContainerCmd(@NotNull String image) { - return wrappedCommand(CreateContainerCmd.class, - wrappedClient.createContainerCmd(image), - (cmd, res) -> doLog("CREATE", image, res.getId(), cmd), - (cmd, e) -> doLog("CREATE", image, null, cmd, e)); - + return wrappedCommand( + CreateContainerCmd.class, + wrappedClient.createContainerCmd(image), + (cmd, res) -> AuditLogger.doLog("CREATE", image, res.getId(), cmd), + (cmd, e) -> AuditLogger.doLog("CREATE", image, null, cmd, e) + ); } @Override public StartContainerCmd startContainerCmd(@NotNull String containerId) { - return wrappedCommand(StartContainerCmd.class, - wrappedClient.startContainerCmd(containerId), - (cmd, res) -> doLog("START", null, containerId, cmd), - (cmd, e) -> doLog("START", null, containerId, cmd, e)); + return wrappedCommand( + StartContainerCmd.class, + wrappedClient.startContainerCmd(containerId), + (cmd, res) -> AuditLogger.doLog("START", null, containerId, cmd), + (cmd, e) -> AuditLogger.doLog("START", null, containerId, cmd, e) + ); } @Override public RemoveContainerCmd removeContainerCmd(@NotNull String containerId) { - return wrappedCommand(RemoveContainerCmd.class, - wrappedClient.removeContainerCmd(containerId), - (cmd, res) -> doLog("REMOVE", null, containerId, cmd), - (cmd, e) -> doLog("REMOVE", null, containerId, cmd, e)); + return wrappedCommand( + RemoveContainerCmd.class, + wrappedClient.removeContainerCmd(containerId), + (cmd, res) -> AuditLogger.doLog("REMOVE", null, containerId, cmd), + (cmd, e) -> AuditLogger.doLog("REMOVE", null, containerId, cmd, e) + ); } @Override public StopContainerCmd stopContainerCmd(@NotNull String containerId) { - return wrappedCommand(StopContainerCmd.class, - wrappedClient.stopContainerCmd(containerId), - (cmd, res) -> doLog("STOP", null, containerId, cmd), - (cmd, e) -> doLog("STOP", null, containerId, cmd, e)); + return wrappedCommand( + StopContainerCmd.class, + wrappedClient.stopContainerCmd(containerId), + (cmd, res) -> AuditLogger.doLog("STOP", null, containerId, cmd), + (cmd, e) -> AuditLogger.doLog("STOP", null, containerId, cmd, e) + ); } @Override public KillContainerCmd killContainerCmd(@NotNull String containerId) { - return wrappedCommand(KillContainerCmd.class, - wrappedClient.killContainerCmd(containerId), - (cmd, res) -> doLog("KILL", null, containerId, cmd), - (cmd, e) -> doLog("KILL", null, containerId, cmd, e)); + return wrappedCommand( + KillContainerCmd.class, + wrappedClient.killContainerCmd(containerId), + (cmd, res) -> AuditLogger.doLog("KILL", null, containerId, cmd), + (cmd, e) -> AuditLogger.doLog("KILL", null, containerId, cmd, e) + ); } @Override public CreateNetworkCmd createNetworkCmd() { - return wrappedCommand(CreateNetworkCmd.class, - wrappedClient.createNetworkCmd(), - (cmd, res) -> doLog("CREATE_NETWORK", null, null, cmd), - (cmd, e) -> doLog("CREATE_NETWORK", null, null, cmd, e)); + return wrappedCommand( + CreateNetworkCmd.class, + wrappedClient.createNetworkCmd(), + (cmd, res) -> AuditLogger.doLog("CREATE_NETWORK", null, null, cmd), + (cmd, e) -> AuditLogger.doLog("CREATE_NETWORK", null, null, cmd, e) + ); } @Override public RemoveNetworkCmd removeNetworkCmd(@NotNull String networkId) { - return wrappedCommand(RemoveNetworkCmd.class, - wrappedClient.removeNetworkCmd(networkId), - (cmd, res) -> doLog("REMOVE_NETWORK", null, null, cmd), - (cmd, e) -> doLog("REMOVE_NETWORK", null, null, cmd, e)); + return wrappedCommand( + RemoveNetworkCmd.class, + wrappedClient.removeNetworkCmd(networkId), + (cmd, res) -> AuditLogger.doLog("REMOVE_NETWORK", null, null, cmd), + (cmd, e) -> AuditLogger.doLog("REMOVE_NETWORK", null, null, cmd, e) + ); } - private , R> T wrappedCommand(Class clazz, - T cmd, - BiConsumer successConsumer, - BiConsumer failureConsumer) { - + private , R> T wrappedCommand( + Class clazz, + T cmd, + BiConsumer successConsumer, + BiConsumer failureConsumer + ) { return (T) Proxy.newProxyInstance( - clazz.getClassLoader(), - new Class[]{clazz}, - (proxy, method, args) -> { - - if (method.getName().equals("exec")) { - try { - R r = (R) method.invoke(cmd, args); - successConsumer.accept(cmd, r); - return r; - } catch (Exception e) { - if (e instanceof InvocationTargetException && e.getCause() instanceof Exception) { - e = (Exception) e.getCause(); - } - failureConsumer.accept(cmd, e); - throw e; + clazz.getClassLoader(), + new Class[] { clazz }, + (proxy, method, args) -> { + if (method.getName().equals("exec")) { + try { + R r = (R) method.invoke(cmd, args); + successConsumer.accept(cmd, r); + return r; + } catch (Exception e) { + if (e instanceof InvocationTargetException && e.getCause() instanceof Exception) { + e = (Exception) e.getCause(); } - } else { - return method.invoke(cmd, args); + failureConsumer.accept(cmd, e); + throw e; } - }); + } else { + return method.invoke(cmd, args); + } + } + ); } @SuppressWarnings("unused") private interface InterceptedMethods { CreateContainerCmd createContainerCmd(String image); + StartContainerCmd startContainerCmd(String containerId); + RemoveContainerCmd removeContainerCmd(String containerId); + StopContainerCmd stopContainerCmd(String containerId); + KillContainerCmd killContainerCmd(String containerId); + CreateNetworkCmd createNetworkCmd(); + RemoveNetworkCmd removeNetworkCmd(String networkId); } } diff --git a/core/src/main/java/org/testcontainers/dockerclient/AuthDelegatingDockerClientConfig.java b/core/src/main/java/org/testcontainers/dockerclient/AuthDelegatingDockerClientConfig.java index 1f534eb59e5..5713d054e13 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/AuthDelegatingDockerClientConfig.java +++ b/core/src/main/java/org/testcontainers/dockerclient/AuthDelegatingDockerClientConfig.java @@ -4,11 +4,10 @@ import com.github.dockerjava.core.DockerClientConfig; import com.github.dockerjava.core.DockerClientConfigDelegate; import lombok.extern.slf4j.Slf4j; +import org.testcontainers.utility.AuthConfigUtil; import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.RegistryAuthLocator; -import static org.testcontainers.utility.AuthConfigUtil.toSafeString; - /** * Facade implementation for {@link DockerClientConfig} which overrides how authentication * configuration is obtained. A delegate {@link DockerClientConfig} will be called first @@ -31,18 +30,21 @@ public AuthConfig effectiveAuthConfig(String imageName) { try { fallbackAuthConfig = super.effectiveAuthConfig(imageName); } catch (Exception e) { - log.debug("Delegate call to effectiveAuthConfig failed with cause: '{}'. " + + log.debug( + "Delegate call to effectiveAuthConfig failed with cause: '{}'. " + "Resolution of auth config will continue using RegistryAuthLocator.", - e.getMessage()); + e.getMessage() + ); fallbackAuthConfig = new AuthConfig(); } // try and obtain more accurate auth config using our resolution final DockerImageName parsed = DockerImageName.parse(imageName); - final AuthConfig effectiveAuthConfig = RegistryAuthLocator.instance() + final AuthConfig effectiveAuthConfig = RegistryAuthLocator + .instance() .lookupAuthConfig(parsed, fallbackAuthConfig); - log.debug("Effective auth config [{}]", toSafeString(effectiveAuthConfig)); + log.debug("Effective auth config [{}]", AuthConfigUtil.toSafeString(effectiveAuthConfig)); return effectiveAuthConfig; } } diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java index e393e59419f..7206a7e1452 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java @@ -8,8 +8,7 @@ import java.io.File; import java.net.URI; import java.util.Optional; - -import static java.util.concurrent.TimeUnit.SECONDS; +import java.util.concurrent.TimeUnit; @Slf4j public class DockerClientConfigUtils { @@ -19,25 +18,31 @@ public class DockerClientConfigUtils { @Getter(lazy = true) private static final Optional defaultGateway = Optional - .ofNullable(DockerClientFactory.instance().runInsideDocker( + .ofNullable( + DockerClientFactory + .instance() + .runInsideDocker( cmd -> cmd.withCmd("sh", "-c", "ip route|awk '/default/ { print $3 }'"), (client, id) -> { try { LogToStringContainerCallback loggingCallback = new LogToStringContainerCallback(); - client.logContainerCmd(id).withStdOut(true) - .withFollowStream(true) - .exec(loggingCallback) - .awaitStarted(); - loggingCallback.awaitCompletion(3, SECONDS); + client + .logContainerCmd(id) + .withStdOut(true) + .withFollowStream(true) + .exec(loggingCallback) + .awaitStarted(); + loggingCallback.awaitCompletion(3, TimeUnit.SECONDS); return loggingCallback.toString(); } catch (Exception e) { log.warn("Can't parse the default gateway IP", e); return null; } } - )) - .map(StringUtils::trimToEmpty) - .filter(StringUtils::isNotBlank); + ) + ) + .map(StringUtils::trimToEmpty) + .filter(StringUtils::isNotBlank); /** * @deprecated use {@link DockerClientProviderStrategy#getDockerHostIpAddress()} diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index 834e670438a..0d2aa3d08ed 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -27,7 +27,6 @@ import org.testcontainers.UnstableAPI; import org.testcontainers.utility.TestcontainersConfiguration; -import javax.net.SocketFactory; import java.io.File; import java.net.InetSocketAddress; import java.net.Socket; @@ -54,6 +53,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.net.SocketFactory; + /** * Mechanism to find a viable Docker client configuration according to the host system environment. */ @@ -68,10 +69,11 @@ public abstract class DockerClientProviderStrategy { @Getter private Info info; - private final RateLimiter PING_RATE_LIMITER = RateLimiterBuilder.newBuilder() - .withRate(10, TimeUnit.SECONDS) - .withConstantThroughput() - .build(); + private final RateLimiter PING_RATE_LIMITER = RateLimiterBuilder + .newBuilder() + .withRate(10, TimeUnit.SECONDS) + .withConstantThroughput() + .build(); private static final AtomicBoolean FAIL_FAST_ALWAYS = new AtomicBoolean(false); @@ -109,14 +111,18 @@ protected int getPriority() { public DockerClient getClient() { DockerClient dockerClient = getDockerClient(); try { - Unreliables.retryUntilSuccess(30, TimeUnit.SECONDS, () -> { - return PING_RATE_LIMITER.getWhenReady(() -> { - log.debug("Pinging docker daemon..."); - dockerClient.pingCmd().exec(); - log.debug("Pinged"); - return true; - }); - }); + Unreliables.retryUntilSuccess( + 30, + TimeUnit.SECONDS, + () -> { + return PING_RATE_LIMITER.getWhenReady(() -> { + log.debug("Pinging docker daemon..."); + dockerClient.pingCmd().exec(); + log.debug("Pinged"); + return true; + }); + } + ); } catch (TimeoutException e) { IOUtils.closeQuietly(dockerClient); throw e; @@ -143,7 +149,12 @@ protected boolean test() { if (sslConfig != null) { try { socketFactory = sslConfig.getSSLContext().getSocketFactory(); - } catch (KeyManagementException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException e) { + } catch ( + KeyManagementException + | UnrecoverableKeyException + | NoSuchAlgorithmException + | KeyStoreException e + ) { log.warn("Exception while creating SSLSocketFactory", e); return false; } @@ -157,16 +168,17 @@ protected boolean test() { log.debug("DOCKER_HOST socket file '{}' does not exist", dockerHost.getPath()); return false; } - socketProvider = () -> { - switch (dockerHost.getScheme()) { - case "unix": - return UnixSocket.get(dockerHost.getPath()); - case "npipe": - return new NamedPipeSocket(dockerHost.getPath()); - default: - throw new IllegalStateException("Unexpected scheme " + dockerHost.getScheme()); - } - }; + socketProvider = + () -> { + switch (dockerHost.getScheme()) { + case "unix": + return UnixSocket.get(dockerHost.getPath()); + case "npipe": + return new NamedPipeSocket(dockerHost.getPath()); + default: + throw new IllegalStateException("Unexpected scheme " + dockerHost.getScheme()); + } + }; socketAddress = new InetSocketAddress("localhost", 2375); break; default: @@ -176,7 +188,8 @@ protected boolean test() { try (Socket socket = socketProvider.call()) { Duration timeout = Duration.ofMillis(200); - Awaitility.await() + Awaitility + .await() .atMost(TestcontainersConfiguration.getInstance().getClientPingTimeout(), TimeUnit.SECONDS) .pollInterval(timeout) .pollDelay(Duration.ofSeconds(0)) // start checking immediately @@ -195,9 +208,10 @@ protected boolean test() { * @return a working DockerClientConfig, as determined by successful execution of a ping command */ public static DockerClientProviderStrategy getFirstValidStrategy(List strategies) { - if (FAIL_FAST_ALWAYS.get()) { - throw new IllegalStateException("Previous attempts to find a Docker environment failed. Will not retry. Please see logs and check configuration"); + throw new IllegalStateException( + "Previous attempts to find a Docker environment failed. Will not retry. Please see logs and check configuration" + ); } List configurationFailures = new ArrayList<>(); @@ -215,7 +229,6 @@ public static DockerClientProviderStrategy getFirstValidStrategy(List allStrategies)); - Predicate distinctStrategyClassPredicate = new Predicate() { final Set> classes = new HashSet<>(); @@ -232,14 +245,18 @@ public boolean test(DockerClientProviderStrategy dockerClientProviderStrategy) { .filter(strategy -> tryOutStrategy(configurationFailures, strategy)) .findFirst() .orElseThrow(() -> { - log.error("Could not find a valid Docker environment. Please check configuration. Attempted configurations were:"); + log.error( + "Could not find a valid Docker environment. Please check configuration. Attempted configurations were:" + ); for (String failureMessage : configurationFailures) { log.error(" " + failureMessage); } log.error("As no valid configuration was found, execution cannot continue"); FAIL_FAST_ALWAYS.set(true); - return new IllegalStateException("Could not find a valid Docker environment. Please see logs and check configuration"); + return new IllegalStateException( + "Could not find a valid Docker environment. Please see logs and check configuration" + ); }); } @@ -270,30 +287,39 @@ private static boolean tryOutStrategy(List configurationFailures, Docker } if (strategy.isPersistable()) { - TestcontainersConfiguration.getInstance().updateUserConfig("docker.client.strategy", strategy.getClass().getName()); + TestcontainersConfiguration + .getInstance() + .updateUserConfig("docker.client.strategy", strategy.getClass().getName()); } return true; } catch (Exception | ExceptionInInitializerError | NoClassDefFoundError e) { - @Nullable String throwableMessage = e.getMessage(); + @Nullable + String throwableMessage = e.getMessage(); @SuppressWarnings("ThrowableResultOfMethodCallIgnored") Throwable rootCause = Throwables.getRootCause(e); - @Nullable String rootCauseMessage = rootCause.getMessage(); + @Nullable + String rootCauseMessage = rootCause.getMessage(); String failureDescription; if (throwableMessage != null && throwableMessage.equals(rootCauseMessage)) { - failureDescription = String.format("%s: failed with exception %s (%s)", + failureDescription = + String.format( + "%s: failed with exception %s (%s)", strategy.getClass().getSimpleName(), e.getClass().getSimpleName(), - throwableMessage); + throwableMessage + ); } else { - failureDescription = String.format("%s: failed with exception %s (%s). Root cause %s (%s)", + failureDescription = + String.format( + "%s: failed with exception %s (%s). Root cause %s (%s)", strategy.getClass().getSimpleName(), e.getClass().getSimpleName(), throwableMessage, rootCause.getClass().getSimpleName(), rootCauseMessage - ); + ); } configurationFailures.add(failureDescription); @@ -303,20 +329,28 @@ private static boolean tryOutStrategy(List configurationFailures, Docker } private static Optional loadConfiguredStrategy() { - String configuredDockerClientStrategyClassName = TestcontainersConfiguration.getInstance().getDockerClientStrategyClassName(); + String configuredDockerClientStrategyClassName = TestcontainersConfiguration + .getInstance() + .getDockerClientStrategyClassName(); return Stream .of(configuredDockerClientStrategyClassName) .filter(Objects::nonNull) .flatMap(it -> { try { - Class strategyClass = (Class) Thread.currentThread().getContextClassLoader().loadClass(it); + Class strategyClass = (Class) Thread + .currentThread() + .getContextClassLoader() + .loadClass(it); return Stream.of(strategyClass.newInstance()); } catch (ClassNotFoundException e) { - log.warn("Can't instantiate a strategy from {} (ClassNotFoundException). " + + log.warn( + "Can't instantiate a strategy from {} (ClassNotFoundException). " + "This probably means that cached configuration refers to a client provider " + "class that is not available in this version of Testcontainers. Other " + - "strategies will be tried instead.", it); + "strategies will be tried instead.", + it + ); return Stream.empty(); } catch (InstantiationException | IllegalAccessException e) { log.warn("Can't instantiate a strategy from {}", it, e); @@ -325,7 +359,12 @@ private static Optional loadConfiguredSt }) // Ignore persisted strategy if it's not persistable anymore .filter(DockerClientProviderStrategy::isPersistable) - .peek(strategy -> log.info("Loaded {} from ~/.testcontainers.properties, will try it first", strategy.getClass().getName())) + .peek(strategy -> { + log.info( + "Loaded {} from ~/.testcontainers.properties, will try it first", + strategy.getClass().getName() + ); + }) .findFirst(); } @@ -335,10 +374,11 @@ public static DockerClient getClientForConfig(TransportConfig transportConfig) { String transportType = TestcontainersConfiguration.getInstance().getTransportType(); switch (transportType) { case "httpclient5": - dockerHttpClient = new ZerodepDockerHttpClient.Builder() - .dockerHost(transportConfig.getDockerHost()) - .sslConfig(transportConfig.getSslConfig()) - .build(); + dockerHttpClient = + new ZerodepDockerHttpClient.Builder() + .dockerHost(transportConfig.getDockerHost()) + .sslConfig(transportConfig.getSslConfig()) + .build(); break; default: throw new IllegalArgumentException("Unknown transport type '" + transportType + "'"); @@ -351,9 +391,7 @@ public static DockerClient getClientForConfig(TransportConfig transportConfig) { } return DockerClientImpl.getInstance( new AuthDelegatingDockerClientConfig( - configBuilder - .withDockerHost(transportConfig.getDockerHost().toString()) - .build() + configBuilder.withDockerHost(transportConfig.getDockerHost().toString()).build() ), new HeadersAddingDockerHttpClient( dockerHttpClient, @@ -384,7 +422,8 @@ static String resolveDockerHostIpAddress(DockerClient client, URI dockerHost) { case "unix": case "npipe": if (DockerClientConfigUtils.IN_A_CONTAINER) { - return client.inspectNetworkCmd() + return client + .inspectNetworkCmd() .withNetworkId("bridge") .exec() .getIpam() diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java index 4de508ea808..7c05a11c198 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java @@ -1,6 +1,7 @@ package org.testcontainers.dockerclient; import com.github.dockerjava.core.LocalDirectorySSLConfig; +import com.google.common.base.Preconditions; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.testcontainers.utility.CommandLine; @@ -11,8 +12,6 @@ import java.util.Arrays; import java.util.Optional; -import static com.google.common.base.Preconditions.checkArgument; - /** * Use Docker machine (if available on the PATH) to locate a Docker environment. * @@ -27,10 +26,16 @@ public final class DockerMachineClientProviderStrategy extends DockerClientProvi private TransportConfig resolveTransportConfig() throws InvalidConfigurationException { boolean installed = DockerMachineClient.instance().isInstalled(); - checkArgument(installed, "docker-machine executable was not found on PATH (" + Arrays.toString(CommandLine.getSystemPath()) + ")"); + Preconditions.checkArgument( + installed, + "docker-machine executable was not found on PATH (" + Arrays.toString(CommandLine.getSystemPath()) + ")" + ); Optional machineNameOptional = DockerMachineClient.instance().getDefaultMachine(); - checkArgument(machineNameOptional.isPresent(), "docker-machine is installed but no default machine could be found"); + Preconditions.checkArgument( + machineNameOptional.isPresent(), + "docker-machine is installed but no default machine could be found" + ); String machineName = machineNameOptional.get(); log.info("Found docker-machine, and will use machine named {}", machineName); @@ -41,7 +46,8 @@ private TransportConfig resolveTransportConfig() throws InvalidConfigurationExce log.info("Docker daemon URL for docker machine {} is {}", machineName, dockerDaemonUrl); - return TransportConfig.builder() + return TransportConfig + .builder() .dockerHost(URI.create(dockerDaemonUrl)) .sslConfig( new LocalDirectorySSLConfig( @@ -55,7 +61,10 @@ private TransportConfig resolveTransportConfig() throws InvalidConfigurationExce protected boolean isApplicable() { boolean installed = DockerMachineClient.instance().isInstalled(); if (!installed) { - log.info("docker-machine executable was not found on PATH ({})", Arrays.toString(CommandLine.getSystemPath())); + log.info( + "docker-machine executable was not found on PATH ({})", + Arrays.toString(CommandLine.getSystemPath()) + ); return false; } diff --git a/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java index 06fdf387d6a..969610373d1 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java @@ -35,7 +35,8 @@ public EnvironmentAndSystemPropertyClientProviderStrategy() { } EnvironmentAndSystemPropertyClientProviderStrategy(DefaultDockerClientConfig.Builder configBuilder) { - String dockerConfigSource = TestcontainersConfiguration.getInstance() + String dockerConfigSource = TestcontainersConfiguration + .getInstance() .getEnvVarOrProperty("dockerconfig.source", "auto"); switch (dockerConfigSource) { @@ -62,7 +63,8 @@ private Optional getSetting(final String name) { @Override public TransportConfig getTransportConfig() { - return TransportConfig.builder() + return TransportConfig + .builder() .dockerHost(dockerClientConfig.getDockerHost()) .sslConfig(dockerClientConfig.getSSLConfig()) .build(); @@ -75,7 +77,10 @@ protected int getPriority() { @Override public String getDescription() { - return "Environment variables, system properties and defaults. Resolved dockerHost=" + dockerClientConfig.getDockerHost(); + return ( + "Environment variables, system properties and defaults. Resolved dockerHost=" + + dockerClientConfig.getDockerHost() + ); } @Override diff --git a/core/src/main/java/org/testcontainers/dockerclient/HeadersAddingDockerHttpClient.java b/core/src/main/java/org/testcontainers/dockerclient/HeadersAddingDockerHttpClient.java index 59a51e19a80..7ff1b0b620c 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/HeadersAddingDockerHttpClient.java +++ b/core/src/main/java/org/testcontainers/dockerclient/HeadersAddingDockerHttpClient.java @@ -19,9 +19,7 @@ class HeadersAddingDockerHttpClient implements DockerHttpClient { @Override public Response execute(Request request) { - request = Request.builder().from(request) - .putAllHeaders(headers) - .build(); + request = Request.builder().from(request).putAllHeaders(headers).build(); return delegate.execute(request); } } diff --git a/core/src/main/java/org/testcontainers/dockerclient/InvalidConfigurationException.java b/core/src/main/java/org/testcontainers/dockerclient/InvalidConfigurationException.java index ef834c9cec5..504378cb17e 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/InvalidConfigurationException.java +++ b/core/src/main/java/org/testcontainers/dockerclient/InvalidConfigurationException.java @@ -12,4 +12,4 @@ public InvalidConfigurationException(String s) { public InvalidConfigurationException(String message, Throwable cause) { super(message, cause); } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/testcontainers/dockerclient/LogToStringContainerCallback.java b/core/src/main/java/org/testcontainers/dockerclient/LogToStringContainerCallback.java index d376b685a91..2c0716b65f0 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/LogToStringContainerCallback.java +++ b/core/src/main/java/org/testcontainers/dockerclient/LogToStringContainerCallback.java @@ -9,6 +9,7 @@ */ @Deprecated public class LogToStringContainerCallback extends ResultCallback.Adapter { + private final StringBuffer log = new StringBuffer(); @Override diff --git a/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java index 7f73e1d7675..0e1f641fe8a 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java @@ -12,15 +12,14 @@ public final class NpipeSocketClientProviderStrategy extends DockerClientProviderStrategy { protected static final String DOCKER_SOCK_PATH = "//./pipe/docker_engine"; + private static final String SOCKET_LOCATION = "npipe://" + DOCKER_SOCK_PATH; public static final int PRIORITY = EnvironmentAndSystemPropertyClientProviderStrategy.PRIORITY - 20; @Override public TransportConfig getTransportConfig() { - return TransportConfig.builder() - .dockerHost(URI.create(SOCKET_LOCATION)) - .build(); + return TransportConfig.builder().dockerHost(URI.create(SOCKET_LOCATION)).build(); } @Override diff --git a/core/src/main/java/org/testcontainers/dockerclient/RootlessDockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/RootlessDockerClientProviderStrategy.java index 36d6b5f6123..a032c2b324d 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/RootlessDockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/RootlessDockerClientProviderStrategy.java @@ -29,13 +29,15 @@ public final class RootlessDockerClientProviderStrategy extends DockerClientProv private final Path socketPath = resolveSocketPath(); private Path resolveSocketPath() { - return tryEnv().orElseGet(() -> { - Path homePath = Paths.get(System.getProperty("user.home")).resolve(".docker").resolve("run"); - return tryFolder(homePath).orElseGet(() -> { - Path implicitPath = Paths.get("/run/user/" + LibC.INSTANCE.getuid()); - return tryFolder(implicitPath).orElse(null); + return tryEnv() + .orElseGet(() -> { + Path homePath = Paths.get(System.getProperty("user.home")).resolve(".docker").resolve("run"); + return tryFolder(homePath) + .orElseGet(() -> { + Path implicitPath = Paths.get("/run/user/" + LibC.INSTANCE.getuid()); + return tryFolder(implicitPath).orElse(null); + }); }); - }); } private Optional tryEnv() { @@ -72,9 +74,7 @@ private Optional tryFolder(Path path) { @Override public TransportConfig getTransportConfig() throws InvalidConfigurationException { - return TransportConfig.builder() - .dockerHost(URI.create("unix://" + getSocketPath().toString())) - .build(); + return TransportConfig.builder().dockerHost(URI.create("unix://" + getSocketPath().toString())).build(); } @Override @@ -93,10 +93,8 @@ protected int getPriority() { } private interface LibC extends Library { - LibC INSTANCE = Native.loadLibrary("c", LibC.class); int getuid(); } - } diff --git a/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java index 4a85394155d..c262bda1db5 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java @@ -14,8 +14,11 @@ */ @Deprecated public final class UnixSocketClientProviderStrategy extends DockerClientProviderStrategy { + protected static final String DOCKER_SOCK_PATH = "/var/run/docker.sock"; + private static final String SOCKET_LOCATION = "unix://" + DOCKER_SOCK_PATH; + private static final int SOCKET_FILE_MODE_MASK = 0xc000; public static final int PRIORITY = EnvironmentAndSystemPropertyClientProviderStrategy.PRIORITY - 20; @@ -31,12 +34,12 @@ public TransportConfig getTransportConfig() throws InvalidConfigurationException } if ((mode & 0xc000) != SOCKET_FILE_MODE_MASK) { - throw new InvalidConfigurationException("Found docker unix domain socket but file mode was not as expected (expected: srwxr-xr-x). This problem is possibly due to occurrence of this issue in the past: https://github.com/docker/docker/issues/13121"); + throw new InvalidConfigurationException( + "Found docker unix domain socket but file mode was not as expected (expected: srwxr-xr-x). This problem is possibly due to occurrence of this issue in the past: https://github.com/docker/docker/issues/13121" + ); } - return TransportConfig.builder() - .dockerHost(URI.create(SOCKET_LOCATION)) - .build(); + return TransportConfig.builder().dockerHost(URI.create(SOCKET_LOCATION)).build(); } @Override diff --git a/core/src/main/java/org/testcontainers/images/AbstractImagePullPolicy.java b/core/src/main/java/org/testcontainers/images/AbstractImagePullPolicy.java index f5d75ad9617..1f9583ef4cf 100644 --- a/core/src/main/java/org/testcontainers/images/AbstractImagePullPolicy.java +++ b/core/src/main/java/org/testcontainers/images/AbstractImagePullPolicy.java @@ -36,7 +36,6 @@ public boolean shouldPull(DockerImageName imageName) { log.debug("Using locally available and not pulling image: {}", imageName); return false; } - } /** @@ -45,5 +44,5 @@ public boolean shouldPull(DockerImageName imageName) { * * @return {@code true} to update the locally available image, {@code false} to use local instead */ - abstract protected boolean shouldPullCached(DockerImageName imageName, ImageData localImageData); + protected abstract boolean shouldPullCached(DockerImageName imageName, ImageData localImageData); } diff --git a/core/src/main/java/org/testcontainers/images/ImageData.java b/core/src/main/java/org/testcontainers/images/ImageData.java index 345b5939268..e57be2dbbbd 100644 --- a/core/src/main/java/org/testcontainers/images/ImageData.java +++ b/core/src/main/java/org/testcontainers/images/ImageData.java @@ -17,14 +17,13 @@ public class ImageData { Instant createdAt; static ImageData from(InspectImageResponse inspectImageResponse) { - return ImageData.builder() + return ImageData + .builder() .createdAt(ZonedDateTime.parse(inspectImageResponse.getCreated()).toInstant()) .build(); } static ImageData from(Image image) { - return ImageData.builder() - .createdAt(Instant.ofEpochMilli(image.getCreated())) - .build(); + return ImageData.builder().createdAt(Instant.ofEpochMilli(image.getCreated())).build(); } } diff --git a/core/src/main/java/org/testcontainers/images/ImagePullPolicy.java b/core/src/main/java/org/testcontainers/images/ImagePullPolicy.java index e821e511795..a090c3f7b9c 100644 --- a/core/src/main/java/org/testcontainers/images/ImagePullPolicy.java +++ b/core/src/main/java/org/testcontainers/images/ImagePullPolicy.java @@ -3,7 +3,5 @@ import org.testcontainers.utility.DockerImageName; public interface ImagePullPolicy { - boolean shouldPull(DockerImageName imageName); - } diff --git a/core/src/main/java/org/testcontainers/images/LocalImagesCache.java b/core/src/main/java/org/testcontainers/images/LocalImagesCache.java index 8791ecb6986..c98339e9f4c 100644 --- a/core/src/main/java/org/testcontainers/images/LocalImagesCache.java +++ b/core/src/main/java/org/testcontainers/images/LocalImagesCache.java @@ -79,14 +79,12 @@ private void populateFromList(List images) { } cache.putAll( - Stream.of(repoTags) + Stream + .of(repoTags) // Protection against some edge case where local image repository tags end up with duplicates // making toMap crash at merge time. .distinct() - .collect(Collectors.toMap( - DockerImageName::new, - it -> ImageData.from(image) - )) + .collect(Collectors.toMap(DockerImageName::new, it -> ImageData.from(image))) ); } } diff --git a/core/src/main/java/org/testcontainers/images/LoggedPullImageResultCallback.java b/core/src/main/java/org/testcontainers/images/LoggedPullImageResultCallback.java index 2ba8ba6df11..32e19090b5c 100644 --- a/core/src/main/java/org/testcontainers/images/LoggedPullImageResultCallback.java +++ b/core/src/main/java/org/testcontainers/images/LoggedPullImageResultCallback.java @@ -2,6 +2,7 @@ import com.github.dockerjava.api.command.PullImageResultCallback; import com.github.dockerjava.api.model.PullResponseItem; +import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import java.io.Closeable; @@ -9,21 +10,25 @@ import java.time.Instant; import java.util.*; -import static java.lang.String.format; -import static org.apache.commons.io.FileUtils.byteCountToDisplaySize; - /** * {@link PullImageResultCallback} with improved logging of pull progress. */ class LoggedPullImageResultCallback extends PullImageResultCallback { + private final Logger logger; private final Set allLayers = new HashSet<>(); + private final Set downloadedLayers = new HashSet<>(); + private final Set pulledLayers = new HashSet<>(); + private final Map totalSizes = new HashMap<>(); + private final Map currentSizes = new HashMap<>(); + private boolean completed; + private Instant start; LoggedPullImageResultCallback(final Logger logger) { @@ -69,8 +74,7 @@ public void onNext(final PullResponseItem item) { } } - if (statusLowercase.startsWith("pulling from" ) || statusLowercase.contains("complete" )) { - + if (statusLowercase.startsWith("pulling from") || statusLowercase.contains("complete")) { long totalSize = totalLayerSize(); long currentSize = downloadedLayerSize(); @@ -79,15 +83,17 @@ public void onNext(final PullResponseItem item) { if (pendingCount > 0) { friendlyTotalSize = "? MB"; } else { - friendlyTotalSize = byteCountToDisplaySize(totalSize); + friendlyTotalSize = FileUtils.byteCountToDisplaySize(totalSize); } - logger.info("Pulling image layers: {} pending, {} downloaded, {} extracted, ({}/{})", - format("%2d", pendingCount), - format("%2d", downloadedLayers.size()), - format("%2d", pulledLayers.size()), - byteCountToDisplaySize(currentSize), - friendlyTotalSize); + logger.info( + "Pulling image layers: {} pending, {} downloaded, {} extracted, ({}/{})", + String.format("%2d", pendingCount), + String.format("%2d", downloadedLayers.size()), + String.format("%2d", pulledLayers.size()), + FileUtils.byteCountToDisplaySize(currentSize), + friendlyTotalSize + ); } if (statusLowercase.contains("complete")) { @@ -103,11 +109,13 @@ public void onComplete() { final long duration = Duration.between(start, Instant.now()).getSeconds(); if (completed) { - logger.info("Pull complete. {} layers, pulled in {}s (downloaded {} at {}/s)", + logger.info( + "Pull complete. {} layers, pulled in {}s (downloaded {} at {}/s)", allLayers.size(), duration, - byteCountToDisplaySize(downloadedLayerSize), - byteCountToDisplaySize(downloadedLayerSize / duration)); + FileUtils.byteCountToDisplaySize(downloadedLayerSize), + FileUtils.byteCountToDisplaySize(downloadedLayerSize / duration) + ); } } diff --git a/core/src/main/java/org/testcontainers/images/ParsedDockerfile.java b/core/src/main/java/org/testcontainers/images/ParsedDockerfile.java index d4574d17c34..ed88c1a85ff 100644 --- a/core/src/main/java/org/testcontainers/images/ParsedDockerfile.java +++ b/core/src/main/java/org/testcontainers/images/ParsedDockerfile.java @@ -21,7 +21,10 @@ @Slf4j public class ParsedDockerfile { - private static final Pattern FROM_LINE_PATTERN = Pattern.compile("FROM (?--[^\\s]+\\s)*(?[^\\s]+).*", Pattern.CASE_INSENSITIVE); + private static final Pattern FROM_LINE_PATTERN = Pattern.compile( + "FROM (?--[^\\s]+\\s)*(?[^\\s]+).*", + Pattern.CASE_INSENSITIVE + ); private final Path dockerFilePath; @@ -54,11 +57,13 @@ private List read() { } private void parse(List lines) { - dependencyImageNames = lines.stream() - .map(FROM_LINE_PATTERN::matcher) - .filter(Matcher::matches) - .map(matcher -> matcher.group("image")) - .collect(Collectors.toSet()); + dependencyImageNames = + lines + .stream() + .map(FROM_LINE_PATTERN::matcher) + .filter(Matcher::matches) + .map(matcher -> matcher.group("image")) + .collect(Collectors.toSet()); if (!dependencyImageNames.isEmpty()) { log.debug("Found dependency images in Dockerfile {}: {}", dockerFilePath, dependencyImageNames); diff --git a/core/src/main/java/org/testcontainers/images/PullPolicy.java b/core/src/main/java/org/testcontainers/images/PullPolicy.java index 3dc2e452b8d..a8b8344a8c2 100644 --- a/core/src/main/java/org/testcontainers/images/PullPolicy.java +++ b/core/src/main/java/org/testcontainers/images/PullPolicy.java @@ -1,9 +1,9 @@ package org.testcontainers.images; -import java.time.Duration; - import lombok.experimental.UtilityClass; +import java.time.Duration; + /** * Convenience class with logic for building common {@link ImagePullPolicy} instances. * @@ -34,5 +34,4 @@ public static ImagePullPolicy alwaysPull() { public static ImagePullPolicy ageBased(Duration maxAge) { return new AgeBasedPullPolicy(maxAge); } - } diff --git a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java index 3bf2243549a..7dbd7adec0b 100644 --- a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java +++ b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java @@ -62,7 +62,7 @@ public RemoteDockerImage(@NonNull Future imageFuture) { } @Override - @SneakyThrows({InterruptedException.class, ExecutionException.class}) + @SneakyThrows({ InterruptedException.class, ExecutionException.class }) protected final String resolve() { final DockerImageName imageName = getImageName(); Logger logger = DockerLoggerFactory.getLogger(imageName.toString()); @@ -72,7 +72,10 @@ protected final String resolve() { } // The image is not available locally - pull it - logger.info("Pulling docker image: {}. Please be patient; this may take some time but only needs to be done once.", imageName); + logger.info( + "Pulling docker image: {}. Please be patient; this may take some time but only needs to be done once.", + imageName + ); Exception lastFailure = null; final Instant lastRetryAllowed = Instant.now().plus(PULL_RETRY_TIME_LIMIT); @@ -84,9 +87,7 @@ protected final String resolve() { .withTag(imageName.getVersionPart()); try { - pullImageCmd - .exec(new TimeLimitedLoggedPullImageResultCallback(logger)) - .awaitCompletion(); + pullImageCmd.exec(new TimeLimitedLoggedPullImageResultCallback(logger)).awaitCompletion(); } catch (DockerClientException e) { // Try to fallback to x86 pullImageCmd @@ -101,12 +102,19 @@ protected final String resolve() { } catch (InterruptedException | InternalServerErrorException e) { // these classes of exception often relate to timeout/connection errors so should be retried lastFailure = e; - logger.warn("Retrying pull for image: {} ({}s remaining)", + logger.warn( + "Retrying pull for image: {} ({}s remaining)", imageName, - Duration.between(Instant.now(), lastRetryAllowed).getSeconds()); + Duration.between(Instant.now(), lastRetryAllowed).getSeconds() + ); } } - logger.error("Failed to pull image: {}. Please check output of `docker pull {}`", imageName, imageName, lastFailure); + logger.error( + "Failed to pull image: {}. Please check output of `docker pull {}`", + imageName, + imageName, + lastFailure + ); throw new ContainerFetchException("Failed to pull image: " + imageName, lastFailure); } catch (DockerClientException e) { diff --git a/core/src/main/java/org/testcontainers/images/TimeLimitedLoggedPullImageResultCallback.java b/core/src/main/java/org/testcontainers/images/TimeLimitedLoggedPullImageResultCallback.java index b6d8539c3e8..88b8804dd8a 100644 --- a/core/src/main/java/org/testcontainers/images/TimeLimitedLoggedPullImageResultCallback.java +++ b/core/src/main/java/org/testcontainers/images/TimeLimitedLoggedPullImageResultCallback.java @@ -3,6 +3,7 @@ import com.github.dockerjava.api.command.PullImageResultCallback; import com.github.dockerjava.api.model.PullResponseItem; import org.slf4j.Logger; +import org.testcontainers.DockerClientFactory; import org.testcontainers.utility.TestcontainersConfiguration; import java.io.Closeable; @@ -16,8 +17,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import static org.testcontainers.DockerClientFactory.TESTCONTAINERS_THREAD_GROUP; - /** * {@link PullImageResultCallback} with improved logging of pull progress and a 'watchdog' which will abort the pull * if progress is not being made, to prevent a hanging test @@ -25,14 +24,21 @@ public class TimeLimitedLoggedPullImageResultCallback extends LoggedPullImageResultCallback { private static final AtomicInteger THREAD_ID = new AtomicInteger(0); - private static final ScheduledExecutorService PROGRESS_WATCHDOG_EXECUTOR = - Executors.newScheduledThreadPool(0, runnable -> { - Thread t = new Thread(TESTCONTAINERS_THREAD_GROUP, runnable); + + private static final ScheduledExecutorService PROGRESS_WATCHDOG_EXECUTOR = Executors.newScheduledThreadPool( + 0, + runnable -> { + Thread t = new Thread(DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, runnable); t.setDaemon(true); t.setName("testcontainers-pull-watchdog-" + THREAD_ID.incrementAndGet()); return t; - }); - private static final Duration PULL_PAUSE_TOLERANCE = Duration.ofSeconds(TestcontainersConfiguration.getInstance().getImagePullPauseTimeout()); + } + ); + + private static final Duration PULL_PAUSE_TOLERANCE = Duration.ofSeconds( + TestcontainersConfiguration.getInstance().getImagePullPauseTimeout() + ); + private final Logger logger; // A future which, if it ever fires, will kill the pull @@ -85,27 +91,30 @@ public void onComplete() { super.onComplete(); } - /* * This method schedules a future task which will interrupt the waiting waiting threads if ever fired. * Every time this method is called (from onStart or onNext), the task is cancelled and recreated 30s in the future, * ensuring that it will only fire if the method stops being called regularly (e.g. if the pull has hung). */ private void resetProgressWatchdog(boolean isFinished) { - if (nextCheckForProgress != null && ! nextCheckForProgress.isCancelled()) { + if (nextCheckForProgress != null && !nextCheckForProgress.isCancelled()) { nextCheckForProgress.cancel(false); } if (!isFinished) { - nextCheckForProgress = PROGRESS_WATCHDOG_EXECUTOR.schedule( - this::abortPull, - PULL_PAUSE_TOLERANCE.getSeconds(), - TimeUnit.SECONDS - ); + nextCheckForProgress = + PROGRESS_WATCHDOG_EXECUTOR.schedule( + this::abortPull, + PULL_PAUSE_TOLERANCE.getSeconds(), + TimeUnit.SECONDS + ); } } private void abortPull() { - logger.error("Docker image pull has not made progress in {}s - aborting pull", PULL_PAUSE_TOLERANCE.getSeconds()); + logger.error( + "Docker image pull has not made progress in {}s - aborting pull", + PULL_PAUSE_TOLERANCE.getSeconds() + ); // Interrupt any threads that are waiting, before closing streams, because the stream can take // an indeterminate amount of time to close waitingThreads.forEach(Thread::interrupt); diff --git a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java index 000487bfa4d..201cf0fd058 100644 --- a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java +++ b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java @@ -39,7 +39,9 @@ @Slf4j @Getter -public class ImageFromDockerfile extends LazyFuture implements +public class ImageFromDockerfile + extends LazyFuture + implements BuildContextBuilderTrait, ClasspathTrait, FilesTrait, @@ -51,9 +53,13 @@ public class ImageFromDockerfile extends LazyFuture implements private boolean deleteOnExit = true; private final Map transferables = new HashMap<>(); + private final Map buildArgs = new HashMap<>(); + private Optional dockerFilePath = Optional.empty(); + private Optional dockerfile = Optional.empty(); + private Set dependencyImageNames = Collections.emptySet(); public ImageFromDockerfile() { @@ -87,7 +93,6 @@ protected final String resolve() { DockerClient dockerClient = DockerClientFactory.instance().client(); try { - BuildImageResultCallback resultCallback = new BuildImageResultCallback() { @Override public void onNext(BuildResponseItem item) { @@ -102,8 +107,10 @@ public void onNext(BuildResponseItem item) { }; // We have to use pipes to avoid high memory consumption since users might want to build really big images - @Cleanup PipedInputStream in = new PipedInputStream(); - @Cleanup PipedOutputStream out = new PipedOutputStream(in); + @Cleanup + PipedInputStream in = new PipedInputStream(); + @Cleanup + PipedOutputStream out = new PipedOutputStream(in); BuildImageCmd buildImageCmd = dockerClient.buildImageCmd(in); configure(buildImageCmd); @@ -140,14 +147,17 @@ public void onNext(BuildResponseItem item) { } log.info("Transferred {} to Docker daemon", FileUtils.byteCountToDisplaySize(bytesToDockerDaemon)); - if (bytesToDockerDaemon > FileUtils.ONE_MB * 50) // warn if >50MB sent to docker daemon - log.warn("A large amount of data was sent to the Docker daemon ({}). Consider using a .dockerignore file for better performance.", - FileUtils.byteCountToDisplaySize(bytesToDockerDaemon)); + if (bytesToDockerDaemon > FileUtils.ONE_MB * 50) { + log.warn( // warn if >50MB sent to docker daemon + "A large amount of data was sent to the Docker daemon ({}). Consider using a .dockerignore file for better performance.", + FileUtils.byteCountToDisplaySize(bytesToDockerDaemon) + ); + } exec.awaitImageId(); return dockerImageName; - } catch(IOException e) { + } catch (IOException e) { throw new RuntimeException("Can't close DockerClient", e); } } @@ -156,15 +166,15 @@ protected void configure(BuildImageCmd buildImageCmd) { buildImageCmd.withTag(this.getDockerImageName()); this.dockerFilePath.ifPresent(buildImageCmd::withDockerfilePath); this.dockerfile.ifPresent(p -> { - buildImageCmd.withDockerfile(p.toFile()); - dependencyImageNames = new ParsedDockerfile(p).getDependencyImageNames(); + buildImageCmd.withDockerfile(p.toFile()); + dependencyImageNames = new ParsedDockerfile(p).getDependencyImageNames(); - if (dependencyImageNames.size() > 0) { - // if we'll be pre-pulling images, disable the built-in pull as it is not necessary and will fail for - // authenticated registries - buildImageCmd.withPull(false); - } - }); + if (dependencyImageNames.size() > 0) { + // if we'll be pre-pulling images, disable the built-in pull as it is not necessary and will fail for + // authenticated registries + buildImageCmd.withPull(false); + } + }); this.buildArgs.forEach(buildImageCmd::withBuildArg); } @@ -174,12 +184,19 @@ private void prePullDependencyImages(Set imagesToPull) { imagesToPull.forEach(imageName -> { try { - log.info("Pre-emptively checking local images for '{}', referenced via a Dockerfile. If not available, it will be pulled.", imageName); + log.info( + "Pre-emptively checking local images for '{}', referenced via a Dockerfile. If not available, it will be pulled.", + imageName + ); new RemoteDockerImage(DockerImageName.parse(imageName)) .withImageNameSubstitutor(ImageNameSubstitutor.noop()) .get(); } catch (Exception e) { - log.warn("Unable to pre-fetch an image ({}) depended upon by Dockerfile - image build will continue but may fail. Exception message was: {}", imageName, e.getMessage()); + log.warn( + "Unable to pre-fetch an image ({}) depended upon by Dockerfile - image build will continue but may fail. Exception message was: {}", + imageName, + e.getMessage() + ); } }); } @@ -218,5 +235,4 @@ public ImageFromDockerfile withDockerfile(Path dockerfile) { this.dockerfile = Optional.of(dockerfile); return this; } - } diff --git a/core/src/main/java/org/testcontainers/images/builder/Transferable.java b/core/src/main/java/org/testcontainers/images/builder/Transferable.java index 8ec26c8c3da..e98dbc2ad11 100644 --- a/core/src/main/java/org/testcontainers/images/builder/Transferable.java +++ b/core/src/main/java/org/testcontainers/images/builder/Transferable.java @@ -9,7 +9,6 @@ import java.util.zip.Checksum; public interface Transferable { - int DEFAULT_FILE_MODE = 0100644; int DEFAULT_DIR_MODE = 040755; diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/DockerfileBuilder.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/DockerfileBuilder.java index 17b13b3bac0..0ce794e778f 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/DockerfileBuilder.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/DockerfileBuilder.java @@ -10,7 +10,9 @@ @Data @Slf4j -public class DockerfileBuilder implements DockerfileBuilderTrait, +public class DockerfileBuilder + implements + DockerfileBuilderTrait, FromStatementTrait, AddStatementTrait, CopyStatementTrait, @@ -27,7 +29,6 @@ public class DockerfileBuilder implements DockerfileBuilderTrait statements = new ArrayList<>(); public String build() { - StringBuilder builder = new StringBuilder(); for (Statement statement : statements) { diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/statement/Statement.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/statement/Statement.java index b36811a974d..2b9b005a579 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/statement/Statement.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/statement/Statement.java @@ -7,5 +7,5 @@ public abstract class Statement { final String type; - abstract public void appendArguments(StringBuilder dockerfileStringBuilder); + public abstract void appendArguments(StringBuilder dockerfileStringBuilder); } diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/AddStatementTrait.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/AddStatementTrait.java index 44a35d68ba2..85b6b9ad6ea 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/AddStatementTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/AddStatementTrait.java @@ -3,7 +3,6 @@ import org.testcontainers.images.builder.dockerfile.statement.MultiArgsStatement; public interface AddStatementTrait & DockerfileBuilderTrait> { - default SELF add(String source, String destination) { return ((SELF) this).withStatement(new MultiArgsStatement("ADD", source, destination)); } diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/CmdStatementTrait.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/CmdStatementTrait.java index 054656af4d1..c9cc93c96c1 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/CmdStatementTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/CmdStatementTrait.java @@ -4,7 +4,6 @@ import org.testcontainers.images.builder.dockerfile.statement.SingleArgumentStatement; public interface CmdStatementTrait & DockerfileBuilderTrait> { - default SELF cmd(String command) { return ((SELF) this).withStatement(new SingleArgumentStatement("CMD", command)); } diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/CopyStatementTrait.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/CopyStatementTrait.java index 03e0cc7b461..0bdb2ef29ab 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/CopyStatementTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/CopyStatementTrait.java @@ -3,7 +3,6 @@ import org.testcontainers.images.builder.dockerfile.statement.MultiArgsStatement; public interface CopyStatementTrait & DockerfileBuilderTrait> { - default SELF copy(String source, String destination) { return ((SELF) this).withStatement(new MultiArgsStatement("COPY", source, destination)); } diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/DockerfileBuilderTrait.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/DockerfileBuilderTrait.java index 7a422da27d5..5fdc74ce1de 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/DockerfileBuilderTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/DockerfileBuilderTrait.java @@ -5,7 +5,6 @@ import java.util.List; public interface DockerfileBuilderTrait> { - List getStatements(); default SELF withStatement(Statement statement) { diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/EntryPointStatementTrait.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/EntryPointStatementTrait.java index c974e58e384..79a6e6315a0 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/EntryPointStatementTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/EntryPointStatementTrait.java @@ -4,7 +4,6 @@ import org.testcontainers.images.builder.dockerfile.statement.SingleArgumentStatement; public interface EntryPointStatementTrait & DockerfileBuilderTrait> { - default SELF entryPoint(String command) { return ((SELF) this).withStatement(new SingleArgumentStatement("ENTRYPOINT", command)); } diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/EnvStatementTrait.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/EnvStatementTrait.java index b02c8e71ec7..c1380304a34 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/EnvStatementTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/EnvStatementTrait.java @@ -6,7 +6,6 @@ import java.util.Map; public interface EnvStatementTrait & DockerfileBuilderTrait> { - default SELF env(String key, String value) { return env(Collections.singletonMap(key, value)); } diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/ExposeStatementTrait.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/ExposeStatementTrait.java index bebe243c8de..22dcbeee32b 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/ExposeStatementTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/ExposeStatementTrait.java @@ -6,8 +6,12 @@ import java.util.stream.Stream; public interface ExposeStatementTrait & DockerfileBuilderTrait> { - default SELF expose(Integer... ports) { - return ((SELF) this).withStatement(new SingleArgumentStatement("EXPOSE", Stream.of(ports).map(Object::toString).collect(Collectors.joining(" ")))); + return ((SELF) this).withStatement( + new SingleArgumentStatement( + "EXPOSE", + Stream.of(ports).map(Object::toString).collect(Collectors.joining(" ")) + ) + ); } } diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/FromStatementTrait.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/FromStatementTrait.java index 07077259df8..1ea411d206e 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/FromStatementTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/FromStatementTrait.java @@ -4,7 +4,6 @@ import org.testcontainers.utility.DockerImageName; public interface FromStatementTrait & DockerfileBuilderTrait> { - default SELF from(String dockerImageName) { DockerImageName.parse(dockerImageName).assertValid(); diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/LabelStatementTrait.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/LabelStatementTrait.java index 0b423453b33..4ed527051dd 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/LabelStatementTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/LabelStatementTrait.java @@ -6,7 +6,6 @@ import java.util.Map; public interface LabelStatementTrait & DockerfileBuilderTrait> { - default SELF label(String key, String value) { return label(Collections.singletonMap(key, value)); } diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/RunStatementTrait.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/RunStatementTrait.java index 74dfe1b00ab..f8fb064fb32 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/RunStatementTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/RunStatementTrait.java @@ -4,7 +4,6 @@ import org.testcontainers.images.builder.dockerfile.statement.SingleArgumentStatement; public interface RunStatementTrait & DockerfileBuilderTrait> { - default SELF run(String... commandParts) { return ((SELF) this).withStatement(new MultiArgsStatement("RUN", commandParts)); } diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/UserStatementTrait.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/UserStatementTrait.java index 57419ad7c5d..ca44d2d03be 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/UserStatementTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/UserStatementTrait.java @@ -3,7 +3,6 @@ import org.testcontainers.images.builder.dockerfile.statement.SingleArgumentStatement; public interface UserStatementTrait & DockerfileBuilderTrait> { - default SELF user(String user) { return ((SELF) this).withStatement(new SingleArgumentStatement("USER", user)); } diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/VolumeStatementTrait.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/VolumeStatementTrait.java index 2709adf32da..a8cd8621ee6 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/VolumeStatementTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/VolumeStatementTrait.java @@ -3,7 +3,6 @@ import org.testcontainers.images.builder.dockerfile.statement.MultiArgsStatement; public interface VolumeStatementTrait & DockerfileBuilderTrait> { - default SELF volume(String... volumes) { return ((SELF) this).withStatement(new MultiArgsStatement("VOLUME", volumes)); } diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/WorkdirStatementTrait.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/WorkdirStatementTrait.java index c4fd5386af8..ea2aaa1412c 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/WorkdirStatementTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/WorkdirStatementTrait.java @@ -3,7 +3,6 @@ import org.testcontainers.images.builder.dockerfile.statement.SingleArgumentStatement; public interface WorkdirStatementTrait & DockerfileBuilderTrait> { - default SELF workDir(String workdir) { return ((SELF) this).withStatement(new SingleArgumentStatement("WORKDIR", workdir)); } diff --git a/core/src/main/java/org/testcontainers/images/builder/traits/BuildContextBuilderTrait.java b/core/src/main/java/org/testcontainers/images/builder/traits/BuildContextBuilderTrait.java index 5ddee327cee..74ddf68b365 100644 --- a/core/src/main/java/org/testcontainers/images/builder/traits/BuildContextBuilderTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/traits/BuildContextBuilderTrait.java @@ -7,6 +7,5 @@ * */ public interface BuildContextBuilderTrait> { - SELF withFileFromTransferable(String path, Transferable transferable); } diff --git a/core/src/main/java/org/testcontainers/images/builder/traits/ClasspathTrait.java b/core/src/main/java/org/testcontainers/images/builder/traits/ClasspathTrait.java index 5617365f2cd..aadc3ccf7b9 100644 --- a/core/src/main/java/org/testcontainers/images/builder/traits/ClasspathTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/traits/ClasspathTrait.java @@ -9,7 +9,6 @@ * */ public interface ClasspathTrait & BuildContextBuilderTrait & FilesTrait> { - default SELF withFileFromClasspath(String path, String resourcePath) { final MountableFile mountableFile = MountableFile.forClasspathResource(resourcePath); diff --git a/core/src/main/java/org/testcontainers/images/builder/traits/DockerfileTrait.java b/core/src/main/java/org/testcontainers/images/builder/traits/DockerfileTrait.java index 07ed13ed0fb..293b76a7c5c 100644 --- a/core/src/main/java/org/testcontainers/images/builder/traits/DockerfileTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/traits/DockerfileTrait.java @@ -10,29 +10,31 @@ * BuildContextBuilder's trait for Dockerfile-based resources. * */ -public interface DockerfileTrait & BuildContextBuilderTrait & StringsTrait> { - +public interface DockerfileTrait< + SELF extends DockerfileTrait & BuildContextBuilderTrait & StringsTrait +> { default SELF withDockerfileFromBuilder(Consumer builderConsumer) { - DockerfileBuilder builder = new DockerfileBuilder(); builderConsumer.accept(builder); // return Transferable because we want to build Dockerfile's content lazily - return ((SELF) this).withFileFromTransferable("Dockerfile", new Transferable() { - - @Getter(lazy = true) - private final byte[] bytes = builder.build().getBytes(); - - @Override - public long getSize() { - return getBytes().length; - } - - @Override - public String getDescription() { - return "Dockerfile: " + builder; - } - }); + return ((SELF) this).withFileFromTransferable( + "Dockerfile", + new Transferable() { + @Getter(lazy = true) + private final byte[] bytes = builder.build().getBytes(); + + @Override + public long getSize() { + return getBytes().length; + } + + @Override + public String getDescription() { + return "Dockerfile: " + builder; + } + } + ); } } diff --git a/core/src/main/java/org/testcontainers/images/builder/traits/FilesTrait.java b/core/src/main/java/org/testcontainers/images/builder/traits/FilesTrait.java index 61524b59f8a..4c1204f752a 100644 --- a/core/src/main/java/org/testcontainers/images/builder/traits/FilesTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/traits/FilesTrait.java @@ -10,7 +10,6 @@ * */ public interface FilesTrait & BuildContextBuilderTrait> { - /** * Adds file to tarball copied into container. * @param path in tarball diff --git a/core/src/main/java/org/testcontainers/images/builder/traits/StringsTrait.java b/core/src/main/java/org/testcontainers/images/builder/traits/StringsTrait.java index 2b23551ee58..68de3d67c14 100644 --- a/core/src/main/java/org/testcontainers/images/builder/traits/StringsTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/traits/StringsTrait.java @@ -9,22 +9,23 @@ * */ public interface StringsTrait & BuildContextBuilderTrait> { - default SELF withFileFromString(String path, String content) { - return ((SELF) this).withFileFromTransferable(path, new Transferable() { - - @Getter - byte[] bytes = content.getBytes(); + return ((SELF) this).withFileFromTransferable( + path, + new Transferable() { + @Getter + byte[] bytes = content.getBytes(); - @Override - public long getSize() { - return bytes.length; - } + @Override + public long getSize() { + return bytes.length; + } - @Override - public String getDescription() { - return "String: " + StringUtils.abbreviate(content, 100); - } - }); + @Override + public String getDescription() { + return "String: " + StringUtils.abbreviate(content, 100); + } + } + ); } } diff --git a/core/src/main/java/org/testcontainers/lifecycle/Startable.java b/core/src/main/java/org/testcontainers/lifecycle/Startable.java index 3e73968d06e..2ffa423d2e3 100644 --- a/core/src/main/java/org/testcontainers/lifecycle/Startable.java +++ b/core/src/main/java/org/testcontainers/lifecycle/Startable.java @@ -4,7 +4,6 @@ import java.util.Set; public interface Startable extends AutoCloseable { - default Set getDependencies() { return Collections.emptySet(); } diff --git a/core/src/main/java/org/testcontainers/lifecycle/Startables.java b/core/src/main/java/org/testcontainers/lifecycle/Startables.java index 5ec46f1edb9..daa63931a88 100644 --- a/core/src/main/java/org/testcontainers/lifecycle/Startables.java +++ b/core/src/main/java/org/testcontainers/lifecycle/Startables.java @@ -1,8 +1,8 @@ package org.testcontainers.lifecycle; -import java.util.Arrays; import lombok.experimental.UtilityClass; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -17,17 +17,18 @@ @UtilityClass public class Startables { - private static final Executor EXECUTOR = Executors.newCachedThreadPool(new ThreadFactory() { - - private final AtomicLong COUNTER = new AtomicLong(0); + private static final Executor EXECUTOR = Executors.newCachedThreadPool( + new ThreadFactory() { + private final AtomicLong COUNTER = new AtomicLong(0); - @Override - public Thread newThread(Runnable r) { - Thread thread = new Thread(r, "testcontainers-lifecycle-" + COUNTER.getAndIncrement()); - thread.setDaemon(true); - return thread; + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r, "testcontainers-lifecycle-" + COUNTER.getAndIncrement()); + thread.setDaemon(true); + return thread; + } } - }); + ); /** * @see #deepStart(Stream) @@ -77,15 +78,22 @@ public CompletableFuture deepStart(Stream startables) * @param started an intermediate storage for already started {@link Startable}s to prevent multiple starts. * @param startables a {@link Stream} of {@link Startable}s to start and scan for transitive dependencies. */ - private CompletableFuture deepStart(Map> started, Stream startables) { + private CompletableFuture deepStart( + Map> started, + Stream startables + ) { CompletableFuture[] futures = startables .sequential() .map(it -> { // avoid a recursive update in `computeIfAbsent` Map> subStarted = new HashMap<>(started); - CompletableFuture future = started.computeIfAbsent(it, startable -> { - return deepStart(subStarted, startable.getDependencies().stream()).thenRunAsync(startable::start, EXECUTOR); - }); + CompletableFuture future = started.computeIfAbsent( + it, + startable -> { + return deepStart(subStarted, startable.getDependencies().stream()) + .thenRunAsync(startable::start, EXECUTOR); + } + ); started.putAll(subStarted); return future; }) diff --git a/core/src/main/java/org/testcontainers/lifecycle/TestDescription.java b/core/src/main/java/org/testcontainers/lifecycle/TestDescription.java index c27f59fb0fc..3787707509a 100644 --- a/core/src/main/java/org/testcontainers/lifecycle/TestDescription.java +++ b/core/src/main/java/org/testcontainers/lifecycle/TestDescription.java @@ -1,7 +1,6 @@ package org.testcontainers.lifecycle; public interface TestDescription { - String getTestId(); String getFilesystemFriendlyName(); diff --git a/core/src/main/java/org/testcontainers/lifecycle/TestLifecycleAware.java b/core/src/main/java/org/testcontainers/lifecycle/TestLifecycleAware.java index 178e081c083..1e5a0258f14 100644 --- a/core/src/main/java/org/testcontainers/lifecycle/TestLifecycleAware.java +++ b/core/src/main/java/org/testcontainers/lifecycle/TestLifecycleAware.java @@ -3,12 +3,7 @@ import java.util.Optional; public interface TestLifecycleAware { + default void beforeTest(TestDescription description) {} - default void beforeTest(TestDescription description) { - - } - - default void afterTest(TestDescription description, Optional throwable) { - - } + default void afterTest(TestDescription description, Optional throwable) {} } diff --git a/core/src/main/java/org/testcontainers/utility/AuditLogger.java b/core/src/main/java/org/testcontainers/utility/AuditLogger.java index f6c4559485d..518144c4302 100644 --- a/core/src/main/java/org/testcontainers/utility/AuditLogger.java +++ b/core/src/main/java/org/testcontainers/utility/AuditLogger.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.dockerjava.api.command.DockerCmd; +import com.google.common.base.Strings; import lombok.experimental.UtilityClass; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; @@ -12,8 +13,6 @@ import java.util.List; -import static com.google.common.base.Strings.nullToEmpty; - /** * Logger for tracking potentially destructive actions, intended for usage in a shared Docker environment where * traceability is needed. This class uses SLF4J, logging at TRACE level and capturing common fields as MDC fields. @@ -26,34 +25,36 @@ public class AuditLogger { private static final ObjectMapper objectMapper = new ObjectMapper(); - public static final String MDC_PREFIX = AuditLogger.class.getCanonicalName(); - public static void doLog(@NotNull String action, - @Nullable String image, - @Nullable String containerId, - @NotNull DockerCmd cmd) { + public static final String MDC_PREFIX = AuditLogger.class.getCanonicalName(); + public static void doLog( + @NotNull String action, + @Nullable String image, + @Nullable String containerId, + @NotNull DockerCmd cmd + ) { doLog(action, image, containerId, cmd, null); } - public static void doLog(@NotNull String action, - @Nullable String image, - @Nullable String containerId, - @NotNull DockerCmd cmd, - @Nullable Exception e) { - - if (! log.isTraceEnabled()) { + public static void doLog( + @NotNull String action, + @Nullable String image, + @Nullable String containerId, + @NotNull DockerCmd cmd, + @Nullable Exception e + ) { + if (!log.isTraceEnabled()) { return; } - MDC.put(MDC_PREFIX + ".Action", nullToEmpty(action)); - MDC.put(MDC_PREFIX + ".Image", nullToEmpty(image)); - MDC.put(MDC_PREFIX + ".ContainerId", nullToEmpty(containerId)); + MDC.put(MDC_PREFIX + ".Action", Strings.nullToEmpty(action)); + MDC.put(MDC_PREFIX + ".Image", Strings.nullToEmpty(image)); + MDC.put(MDC_PREFIX + ".ContainerId", Strings.nullToEmpty(containerId)); try { MDC.put(MDC_PREFIX + ".Command", objectMapper.writeValueAsString(cmd)); - } catch (JsonProcessingException ignored) { - } + } catch (JsonProcessingException ignored) {} if (e != null) { MDC.put(MDC_PREFIX + ".Exception", e.getLocalizedMessage()); @@ -69,10 +70,8 @@ public static void doLog(@NotNull String action, MDC.remove(MDC_PREFIX + ".Exception"); } - public static void doComposeLog(@NotNull String[] commandParts, - @Nullable List env) { - - if (! log.isTraceEnabled()) { + public static void doComposeLog(@NotNull String[] commandParts, @Nullable List env) { + if (!log.isTraceEnabled()) { return; } diff --git a/core/src/main/java/org/testcontainers/utility/AuthConfigUtil.java b/core/src/main/java/org/testcontainers/utility/AuthConfigUtil.java index 77b670d374b..8ed91a5e34e 100644 --- a/core/src/main/java/org/testcontainers/utility/AuthConfigUtil.java +++ b/core/src/main/java/org/testcontainers/utility/AuthConfigUtil.java @@ -2,22 +2,23 @@ import com.github.dockerjava.api.model.AuthConfig; import com.google.common.base.MoreObjects; +import com.google.common.base.Strings; import lombok.experimental.UtilityClass; import org.jetbrains.annotations.NotNull; -import static com.google.common.base.Strings.isNullOrEmpty; - /** * TODO: Javadocs */ @UtilityClass public class AuthConfigUtil { + public static String toSafeString(AuthConfig authConfig) { if (authConfig == null) { return "null"; } - return MoreObjects.toStringHelper(authConfig) + return MoreObjects + .toStringHelper(authConfig) .add("username", authConfig.getUsername()) .add("password", obfuscated(authConfig.getPassword())) .add("auth", obfuscated(authConfig.getAuth())) @@ -29,6 +30,6 @@ public static String toSafeString(AuthConfig authConfig) { @NotNull private static String obfuscated(String value) { - return isNullOrEmpty(value) ? "blank" : "hidden non-blank value"; + return Strings.isNullOrEmpty(value) ? "blank" : "hidden non-blank value"; } } diff --git a/core/src/main/java/org/testcontainers/utility/Base58.java b/core/src/main/java/org/testcontainers/utility/Base58.java index 1f56978c7cc..2cbfc751f4d 100644 --- a/core/src/main/java/org/testcontainers/utility/Base58.java +++ b/core/src/main/java/org/testcontainers/utility/Base58.java @@ -7,8 +7,7 @@ */ public class Base58 { - private static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" - .toCharArray(); + private static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray(); private static final SecureRandom RANDOM = new SecureRandom(); diff --git a/core/src/main/java/org/testcontainers/utility/ClasspathScanner.java b/core/src/main/java/org/testcontainers/utility/ClasspathScanner.java index 76716cfdb7c..0c8addc2874 100644 --- a/core/src/main/java/org/testcontainers/utility/ClasspathScanner.java +++ b/core/src/main/java/org/testcontainers/utility/ClasspathScanner.java @@ -40,11 +40,7 @@ private static Integer filesFileSchemeFirst(final URL t) { * context classloader. Results are currently alphabetically sorted. */ static Stream scanFor(final String name) { - return scanFor( - name, - ClasspathScanner.class.getClassLoader(), - Thread.currentThread().getContextClassLoader() - ); + return scanFor(name, ClasspathScanner.class.getClassLoader(), Thread.currentThread().getContextClassLoader()); } @Nullable diff --git a/core/src/main/java/org/testcontainers/utility/CommandLine.java b/core/src/main/java/org/testcontainers/utility/CommandLine.java index a217823c40f..1bb8f827148 100644 --- a/core/src/main/java/org/testcontainers/utility/CommandLine.java +++ b/core/src/main/java/org/testcontainers/utility/CommandLine.java @@ -2,6 +2,7 @@ import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.zeroturnaround.exec.InvalidExitValueException; import org.zeroturnaround.exec.ProcessExecutor; import org.zeroturnaround.exec.ProcessResult; @@ -14,14 +15,12 @@ import java.util.concurrent.TimeoutException; import java.util.regex.Pattern; -import static org.slf4j.LoggerFactory.getLogger; - /** * Process execution utility methods. */ public class CommandLine { - private static final Logger LOGGER = getLogger(CommandLine.class); + private static final Logger LOGGER = LoggerFactory.getLogger(CommandLine.class); /** * Run a shell command synchronously. @@ -30,16 +29,11 @@ public class CommandLine { * @return the stdout output of the command */ public static String runShellCommand(String... command) { - String joinedCommand = String.join(" ", command); LOGGER.debug("Executing shell command: `{}`", joinedCommand); try { - ProcessResult result = new ProcessExecutor() - .command(command) - .readOutput(true) - .exitValueNormal() - .execute(); + ProcessResult result = new ProcessExecutor().command(command).readOutput(true).exitValueNormal().execute(); return result.outputUTF8().trim(); } catch (IOException | InterruptedException | TimeoutException | InvalidExitValueException e) { @@ -55,7 +49,6 @@ public static String runShellCommand(String... command) { * @return whether the executable exists and is executable */ public static boolean executableExists(String executable) { - // First check if we've been given the full path already File directFile = new File(executable); if (directFile.exists() && directFile.canExecute()) { @@ -78,6 +71,7 @@ public static String[] getSystemPath() { } private static class ShellCommandException extends RuntimeException { + public ShellCommandException(String message, Exception e) { super(message, e); } diff --git a/core/src/main/java/org/testcontainers/utility/ComparableVersion.java b/core/src/main/java/org/testcontainers/utility/ComparableVersion.java index d3e3d9eb958..999c4b40b3b 100644 --- a/core/src/main/java/org/testcontainers/utility/ComparableVersion.java +++ b/core/src/main/java/org/testcontainers/utility/ComparableVersion.java @@ -1,12 +1,11 @@ package org.testcontainers.utility; -import java.util.ArrayList; -import java.util.List; - import com.google.common.annotations.VisibleForTesting; - import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; +import java.util.List; + public final class ComparableVersion implements Comparable { private final int[] parts; @@ -19,7 +18,7 @@ public ComparableVersion(String version) { @Override public int compareTo(@NotNull ComparableVersion other) { - for (int i=0; i otherPart) { @@ -71,5 +70,4 @@ static int[] parseVersion(final String version) { return ret; } - } diff --git a/core/src/main/java/org/testcontainers/utility/ConfigurationFileImageNameSubstitutor.java b/core/src/main/java/org/testcontainers/utility/ConfigurationFileImageNameSubstitutor.java index 530d5a76118..ee7ab1d95b6 100644 --- a/core/src/main/java/org/testcontainers/utility/ConfigurationFileImageNameSubstitutor.java +++ b/core/src/main/java/org/testcontainers/utility/ConfigurationFileImageNameSubstitutor.java @@ -28,7 +28,8 @@ public DockerImageName apply(final DockerImageName original) { .asCompatibleSubstituteFor(original); if (!result.equals(original)) { - log.warn("Image name {} was substituted by configuration to {}. This approach is deprecated and will be removed in the future", + log.warn( + "Image name {} was substituted by configuration to {}. This approach is deprecated and will be removed in the future", original, result ); diff --git a/core/src/main/java/org/testcontainers/utility/DefaultImageNameSubstitutor.java b/core/src/main/java/org/testcontainers/utility/DefaultImageNameSubstitutor.java index 840b8a77b40..a7755f8eea8 100644 --- a/core/src/main/java/org/testcontainers/utility/DefaultImageNameSubstitutor.java +++ b/core/src/main/java/org/testcontainers/utility/DefaultImageNameSubstitutor.java @@ -11,6 +11,7 @@ final class DefaultImageNameSubstitutor extends ImageNameSubstitutor { private final ConfigurationFileImageNameSubstitutor configurationFileImageNameSubstitutor; + private final PrefixingImageNameSubstitutor prefixingImageNameSubstitutor; public DefaultImageNameSubstitutor() { @@ -29,13 +30,17 @@ public DefaultImageNameSubstitutor() { @Override public DockerImageName apply(final DockerImageName original) { - return configurationFileImageNameSubstitutor - .andThen(prefixingImageNameSubstitutor) - .apply(original); + return configurationFileImageNameSubstitutor.andThen(prefixingImageNameSubstitutor).apply(original); } @Override protected String getDescription() { - return "DefaultImageNameSubstitutor (composite of '" + configurationFileImageNameSubstitutor.getDescription() + "' and '" + prefixingImageNameSubstitutor.getDescription() + "')"; + return ( + "DefaultImageNameSubstitutor (composite of '" + + configurationFileImageNameSubstitutor.getDescription() + + "' and '" + + prefixingImageNameSubstitutor.getDescription() + + "')" + ); } } diff --git a/core/src/main/java/org/testcontainers/utility/DockerImageName.java b/core/src/main/java/org/testcontainers/utility/DockerImageName.java index b9d266a41ca..9fca02aad6f 100644 --- a/core/src/main/java/org/testcontainers/utility/DockerImageName.java +++ b/core/src/main/java/org/testcontainers/utility/DockerImageName.java @@ -1,6 +1,5 @@ package org.testcontainers.utility; - import com.google.common.net.HostAndPort; import lombok.AccessLevel; import lombok.AllArgsConstructor; @@ -20,16 +19,29 @@ public final class DockerImageName { /* Regex patterns used for validation */ private static final String ALPHA_NUMERIC = "[a-z0-9]+"; + private static final String SEPARATOR = "([.]|_{1,2}|-+)"; + private static final String REPO_NAME_PART = ALPHA_NUMERIC + "(" + SEPARATOR + ALPHA_NUMERIC + ")*"; + private static final Pattern REPO_NAME = Pattern.compile(REPO_NAME_PART + "(/" + REPO_NAME_PART + ")*"); private final String rawName; - @With @Getter private final String registry; - @With @Getter private final String repository; - @NotNull @With(AccessLevel.PRIVATE) + + @With + @Getter + private final String registry; + + @With + @Getter + private final String repository; + + @NotNull + @With(AccessLevel.PRIVATE) private final Versioning versioning; - @Nullable @With(AccessLevel.PRIVATE) + + @Nullable + @With(AccessLevel.PRIVATE) private final DockerImageName compatibleSubstituteFor; /** @@ -57,10 +69,14 @@ public DockerImageName(String fullImageName) { final int slashIndex = fullImageName.indexOf('/'); String remoteName; - if (slashIndex == -1 || - (!fullImageName.substring(0, slashIndex).contains(".") && + if ( + slashIndex == -1 || + ( + !fullImageName.substring(0, slashIndex).contains(".") && !fullImageName.substring(0, slashIndex).contains(":") && - !fullImageName.substring(0, slashIndex).equals("localhost"))) { + !fullImageName.substring(0, slashIndex).equals("localhost") + ) + ) { registry = ""; remoteName = fullImageName; } else { @@ -99,10 +115,14 @@ public DockerImageName(String nameWithoutTag, @NotNull String version) { final int slashIndex = nameWithoutTag.indexOf('/'); String remoteName; - if (slashIndex == -1 || - (!nameWithoutTag.substring(0, slashIndex).contains(".") && + if ( + slashIndex == -1 || + ( + !nameWithoutTag.substring(0, slashIndex).contains(".") && !nameWithoutTag.substring(0, slashIndex).contains(":") && - !nameWithoutTag.substring(0, slashIndex).equals("localhost"))) { + !nameWithoutTag.substring(0, slashIndex).equals("localhost") + ) + ) { registry = ""; remoteName = nameWithoutTag; } else { @@ -163,7 +183,9 @@ public void assertValid() { throw new IllegalArgumentException(repository + " is not a valid Docker image name (in " + rawName + ")"); } if (!versioning.isValid()) { - throw new IllegalArgumentException(versioning + " is not a valid image versioning identifier (in " + rawName + ")"); + throw new IllegalArgumentException( + versioning + " is not a valid image versioning identifier (in " + rawName + ")" + ); } } @@ -246,18 +268,16 @@ public void assertCompatibleWith(DockerImageName... anyOthers) { throw new IllegalStateException( String.format( - "Failed to verify that image '%s' is a compatible substitute for '%s'. This generally means that " - + - "you are trying to use an image that Testcontainers has not been designed to use. If this is " - + - "deliberate, and if you are confident that the image is compatible, you should declare " - + - "compatibility in code using the `asCompatibleSubstituteFor` method. For example:\n" - + - " DockerImageName myImage = DockerImageName.parse(\"%s\").asCompatibleSubstituteFor(\"%s\");\n" - + - "and then use `myImage` instead.", - this.rawName, exampleOther.rawName, this.rawName, exampleOther.rawName + "Failed to verify that image '%s' is a compatible substitute for '%s'. This generally means that " + + "you are trying to use an image that Testcontainers has not been designed to use. If this is " + + "deliberate, and if you are confident that the image is compatible, you should declare " + + "compatibility in code using the `asCompatibleSubstituteFor` method. For example:\n" + + " DockerImageName myImage = DockerImageName.parse(\"%s\").asCompatibleSubstituteFor(\"%s\");\n" + + "and then use `myImage` instead.", + this.rawName, + exampleOther.rawName, + this.rawName, + exampleOther.rawName ) ); } diff --git a/core/src/main/java/org/testcontainers/utility/DockerLoggerFactory.java b/core/src/main/java/org/testcontainers/utility/DockerLoggerFactory.java index 69f7b998da2..c372c3cea3e 100644 --- a/core/src/main/java/org/testcontainers/utility/DockerLoggerFactory.java +++ b/core/src/main/java/org/testcontainers/utility/DockerLoggerFactory.java @@ -9,7 +9,6 @@ public final class DockerLoggerFactory { public static Logger getLogger(String dockerImageName) { - final String abbreviatedName; if (dockerImageName.contains("@sha256")) { abbreviatedName = dockerImageName.substring(0, dockerImageName.indexOf("@sha256") + 14) + "..."; diff --git a/core/src/main/java/org/testcontainers/utility/DockerMachineClient.java b/core/src/main/java/org/testcontainers/utility/DockerMachineClient.java index 6f1e2c9d50b..1a2b4aafe74 100644 --- a/core/src/main/java/org/testcontainers/utility/DockerMachineClient.java +++ b/core/src/main/java/org/testcontainers/utility/DockerMachineClient.java @@ -3,44 +3,42 @@ import lombok.NonNull; import org.apache.commons.lang3.SystemUtils; import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.Arrays; import java.util.List; import java.util.Optional; -import static java.util.Arrays.asList; -import static org.slf4j.LoggerFactory.getLogger; -import static org.testcontainers.utility.CommandLine.executableExists; -import static org.testcontainers.utility.CommandLine.runShellCommand; - /** * Created by rnorth on 27/10/2015. */ public class DockerMachineClient { private static DockerMachineClient instance; - private static final Logger LOGGER = getLogger(DockerMachineClient.class); + + private static final Logger LOGGER = LoggerFactory.getLogger(DockerMachineClient.class); private static final String executableName; + static { - if(SystemUtils.IS_OS_WINDOWS) { - executableName = "docker-machine.exe"; - } else { - executableName = "docker-machine"; - } + if (SystemUtils.IS_OS_WINDOWS) { + executableName = "docker-machine.exe"; + } else { + executableName = "docker-machine"; + } } /** * Private constructor */ - private DockerMachineClient() { - } + private DockerMachineClient() {} /** * Obtain an instance of the DockerMachineClient wrapper. * * @return the singleton instance of DockerMachineClient */ - public synchronized static DockerMachineClient instance() { + public static synchronized DockerMachineClient instance() { if (instance == null) { instance = new DockerMachineClient(); } @@ -49,12 +47,12 @@ public synchronized static DockerMachineClient instance() { } public boolean isInstalled() { - return executableExists(executableName); + return CommandLine.executableExists(executableName); } public Optional getDefaultMachine() { - String ls = runShellCommand(executableName, "ls", "-q"); - List machineNames = asList(ls.split("\n")); + String ls = CommandLine.runShellCommand(executableName, "ls", "-q"); + List machineNames = Arrays.asList(ls.split("\n")); String envMachineName = System.getenv("DOCKER_MACHINE_NAME"); @@ -65,7 +63,10 @@ public Optional getDefaultMachine() { LOGGER.debug("DOCKER_MACHINE_NAME is not set; Using 'default' docker-machine", envMachineName); return Optional.of("default"); } else if (machineNames.size() > 0) { - LOGGER.debug("DOCKER_MACHINE_NAME is not set and no machine named 'default' found; Using first machine found with `docker-machine ls`: {}", machineNames.get(0)); + LOGGER.debug( + "DOCKER_MACHINE_NAME is not set and no machine named 'default' found; Using first machine found with `docker-machine ls`: {}", + machineNames.get(0) + ); return Optional.of(machineNames.get(0)); } else { return Optional.empty(); @@ -75,7 +76,7 @@ public Optional getDefaultMachine() { public void ensureMachineRunning(@NonNull String machineName) { if (!isMachineRunning(machineName)) { LOGGER.info("Docker-machine '{}' is not running. Will start it now", machineName); - runShellCommand("docker-machine", "start", machineName); + CommandLine.runShellCommand("docker-machine", "start", machineName); } } @@ -84,15 +85,15 @@ public void ensureMachineRunning(@NonNull String machineName) { */ @Deprecated public String getDockerDaemonIpAddress(@NonNull String machineName) { - return runShellCommand(executableName, "ip", machineName); + return CommandLine.runShellCommand(executableName, "ip", machineName); } public String getDockerDaemonUrl(@NonNull String machineName) { - return runShellCommand(executableName, "url", machineName); + return CommandLine.runShellCommand(executableName, "url", machineName); } public boolean isMachineRunning(String machineName) { - String status = runShellCommand("docker-machine", "status", machineName); + String status = CommandLine.runShellCommand("docker-machine", "status", machineName); return status.trim().equalsIgnoreCase("running"); } diff --git a/core/src/main/java/org/testcontainers/utility/DockerStatus.java b/core/src/main/java/org/testcontainers/utility/DockerStatus.java index a410597dcfb..00f71249dd1 100644 --- a/core/src/main/java/org/testcontainers/utility/DockerStatus.java +++ b/core/src/main/java/org/testcontainers/utility/DockerStatus.java @@ -33,15 +33,16 @@ public class DockerStatus { * @param now the time to consider as the current time * @return true if we can conclude that the container is running, false otherwise */ - public static boolean isContainerRunning(InspectContainerResponse.ContainerState state, - Duration minimumRunningDuration, - Instant now) { + public static boolean isContainerRunning( + InspectContainerResponse.ContainerState state, + Duration minimumRunningDuration, + Instant now + ) { if (state.getRunning()) { if (minimumRunningDuration == null) { return true; } - Instant startedAt = DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse( - state.getStartedAt(), Instant::from); + Instant startedAt = DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(state.getStartedAt(), Instant::from); if (startedAt.isBefore(now.minus(minimumRunningDuration))) { return true; @@ -57,7 +58,6 @@ public static boolean isContainerRunning(InspectContainerResponse.ContainerState * @return true if we can conclude that the container has started but is now stopped, false otherwise. */ public static boolean isContainerStopped(InspectContainerResponse.ContainerState state) { - // get some preconditions out of the way if (state.getRunning() || state.getPaused()) { return false; @@ -72,10 +72,12 @@ public static boolean isContainerStopped(InspectContainerResponse.ContainerState public static boolean isDockerTimestampNonEmpty(String dockerTimestamp) { // This is a defensive approach. Current versions of Docker use the DOCKER_TIMESTAMP_ZERO value, but // that could change. - return dockerTimestamp != null - && !dockerTimestamp.isEmpty() - && !dockerTimestamp.equals(DOCKER_TIMESTAMP_ZERO) - && DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(dockerTimestamp, Instant::from).getEpochSecond() >= 0L; + return ( + dockerTimestamp != null && + !dockerTimestamp.isEmpty() && + !dockerTimestamp.equals(DOCKER_TIMESTAMP_ZERO) && + DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(dockerTimestamp, Instant::from).getEpochSecond() >= 0L + ); } public static boolean isContainerExitCodeSuccess(InspectContainerResponse.ContainerState state) { diff --git a/core/src/main/java/org/testcontainers/utility/DynamicPollInterval.java b/core/src/main/java/org/testcontainers/utility/DynamicPollInterval.java index 1252fb2c3a1..043befe04bb 100644 --- a/core/src/main/java/org/testcontainers/utility/DynamicPollInterval.java +++ b/core/src/main/java/org/testcontainers/utility/DynamicPollInterval.java @@ -15,6 +15,7 @@ public class DynamicPollInterval implements PollInterval { final Duration interval; + Instant lastTimestamp; private DynamicPollInterval(Duration interval) { diff --git a/core/src/main/java/org/testcontainers/utility/ImageNameSubstitutor.java b/core/src/main/java/org/testcontainers/utility/ImageNameSubstitutor.java index a1cb2492204..dae3f178230 100644 --- a/core/src/main/java/org/testcontainers/utility/ImageNameSubstitutor.java +++ b/core/src/main/java/org/testcontainers/utility/ImageNameSubstitutor.java @@ -24,7 +24,7 @@ public abstract class ImageNameSubstitutor implements Function> filters) { - String[] labels = filters.stream() + String[] labels = filters + .stream() .filter(it -> "label".equals(it.getKey())) .map(Map.Entry::getValue) .toArray(String[]::new); switch (pruneType) { // Docker only prunes stopped containers, so we have to do it manually case CONTAINERS: - List containers = dockerClient.listContainersCmd() + List containers = dockerClient + .listContainersCmd() .withFilter("label", Arrays.asList(labels)) .withShowAll(true) .exec(); - containers.parallelStream().forEach(container -> { - dockerClient.removeContainerCmd(container.getId()) - .withForce(true) - .withRemoveVolumes(true) - .exec(); - }); + containers + .parallelStream() + .forEach(container -> { + dockerClient + .removeContainerCmd(container.getId()) + .withForce(true) + .withRemoveVolumes(true) + .exec(); + }); break; default: dockerClient.pruneCmd(pruneType).withLabelFilter(labels).exec(); diff --git a/core/src/main/java/org/testcontainers/utility/LazyFuture.java b/core/src/main/java/org/testcontainers/utility/LazyFuture.java index 758082f5185..bde9feb69ec 100644 --- a/core/src/main/java/org/testcontainers/utility/LazyFuture.java +++ b/core/src/main/java/org/testcontainers/utility/LazyFuture.java @@ -17,7 +17,7 @@ public abstract class LazyFuture implements Future { @Getter(value = AccessLevel.MODULE, lazy = true) private final T resolvedValue = resolve(); - abstract protected T resolve(); + protected abstract T resolve(); @Override public boolean cancel(boolean mayInterruptIfRunning) { diff --git a/core/src/main/java/org/testcontainers/utility/LicenseAcceptance.java b/core/src/main/java/org/testcontainers/utility/LicenseAcceptance.java index 55037d8978c..9fe8d9db8e6 100644 --- a/core/src/main/java/org/testcontainers/utility/LicenseAcceptance.java +++ b/core/src/main/java/org/testcontainers/utility/LicenseAcceptance.java @@ -27,10 +27,17 @@ public static void assertLicenseAccepted(final String imageName) { // suppressed } - throw new IllegalStateException("The image " + imageName + " requires you to accept a license agreement. " + - "Please place a file at the root of the classpath named " + ACCEPTANCE_FILE_NAME + ", e.g. at " + - "src/test/resources/" + ACCEPTANCE_FILE_NAME + ". This file should contain the line:\n " + - imageName); - + throw new IllegalStateException( + "The image " + + imageName + + " requires you to accept a license agreement. " + + "Please place a file at the root of the classpath named " + + ACCEPTANCE_FILE_NAME + + ", e.g. at " + + "src/test/resources/" + + ACCEPTANCE_FILE_NAME + + ". This file should contain the line:\n " + + imageName + ); } } diff --git a/core/src/main/java/org/testcontainers/utility/LogUtils.java b/core/src/main/java/org/testcontainers/utility/LogUtils.java index ce27387490f..e5912563cd9 100644 --- a/core/src/main/java/org/testcontainers/utility/LogUtils.java +++ b/core/src/main/java/org/testcontainers/utility/LogUtils.java @@ -13,9 +13,6 @@ import java.io.IOException; import java.util.function.Consumer; -import static org.testcontainers.containers.output.OutputFrame.OutputType.STDERR; -import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; - /** * Provides utility methods for logging. */ @@ -31,11 +28,12 @@ public class LogUtils { * @param consumer a consumer of {@link OutputFrame}s * @param types types of {@link OutputFrame} to receive */ - public void followOutput(DockerClient dockerClient, - String containerId, - Consumer consumer, - OutputFrame.OutputType... types) { - + public void followOutput( + DockerClient dockerClient, + String containerId, + Consumer consumer, + OutputFrame.OutputType... types + ) { attachConsumer(dockerClient, containerId, consumer, true, types); } @@ -47,11 +45,8 @@ public void followOutput(DockerClient dockerClient, * @param containerId container ID to attach to * @param consumer a consumer of {@link OutputFrame}s */ - public void followOutput(DockerClient dockerClient, - String containerId, - Consumer consumer) { - - followOutput(dockerClient, containerId, consumer, STDOUT, STDERR); + public void followOutput(DockerClient dockerClient, String containerId, Consumer consumer) { + followOutput(dockerClient, containerId, consumer, OutputFrame.OutputType.STDOUT, OutputFrame.OutputType.STDERR); } /** @@ -63,16 +58,13 @@ public void followOutput(DockerClient dockerClient, * @return all previous output frames (stdout/stderr being separated by newline characters) */ @SneakyThrows(IOException.class) - public String getOutput(DockerClient dockerClient, - String containerId, - OutputFrame.OutputType... types) { - + public String getOutput(DockerClient dockerClient, String containerId, OutputFrame.OutputType... types) { if (containerId == null) { return ""; } if (types.length == 0) { - types = new OutputFrame.OutputType[] { STDOUT, STDERR }; + types = new OutputFrame.OutputType[] { OutputFrame.OutputType.STDOUT, OutputFrame.OutputType.STDERR }; } final ToStringConsumer consumer = new ToStringConsumer(); @@ -90,16 +82,20 @@ private static Closeable attachConsumer( boolean followStream, OutputFrame.OutputType... types ) { - - final LogContainerCmd cmd = dockerClient.logContainerCmd(containerId) + final LogContainerCmd cmd = dockerClient + .logContainerCmd(containerId) .withFollowStream(followStream) .withSince(0); final FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); for (OutputFrame.OutputType type : types) { callback.addConsumer(type, consumer); - if (type == STDOUT) cmd.withStdOut(true); - if (type == STDERR) cmd.withStdErr(true); + if (type == OutputFrame.OutputType.STDOUT) { + cmd.withStdOut(true); + } + if (type == OutputFrame.OutputType.STDERR) { + cmd.withStdErr(true); + } } return cmd.exec(callback); diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java index 81cc37adeab..a8a9f465a19 100644 --- a/core/src/main/java/org/testcontainers/utility/MountableFile.java +++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java @@ -1,7 +1,7 @@ package org.testcontainers.utility; -import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Charsets; +import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; @@ -34,23 +34,24 @@ import java.util.stream.Stream; import java.util.zip.Checksum; -import static lombok.AccessLevel.PACKAGE; -import static org.testcontainers.utility.PathUtils.recursiveDeleteDir; - /** * An abstraction over files and classpath resources aimed at encapsulating all the complexity of generating * a path that the Docker daemon is about to create a volume mount for. */ -@RequiredArgsConstructor(access = PACKAGE) +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) @Slf4j public class MountableFile implements Transferable { private static final String TESTCONTAINERS_TMP_DIR_PREFIX = ".testcontainers-tmp-"; + private static final String OS_MAC_TMP_DIR = "/tmp"; + private static final int BASE_FILE_MODE = 0100000; + private static final int BASE_DIR_MODE = 0040000; private final String path; + private final Integer forcedFileMode; @Getter(lazy = true) @@ -124,10 +125,11 @@ public static MountableFile forHostPath(final Path path, Integer mode) { return new MountableFile(path.toAbsolutePath().toString(), mode); } - @NotNull - private static URL getClasspathResource(@NotNull final String resourcePath, @NotNull final Set classLoaders) { - + private static URL getClasspathResource( + @NotNull final String resourcePath, + @NotNull final Set classLoaders + ) { final Set classLoadersToSearch = new HashSet<>(classLoaders); // try context and system classloaders as well classLoadersToSearch.add(Thread.currentThread().getContextClassLoader()); @@ -153,16 +155,22 @@ private static URL getClasspathResource(@NotNull final String resourcePath, @Not } } - throw new IllegalArgumentException("Resource with path " + resourcePath + " could not be found on any of these classloaders: " + classLoadersToSearch); + throw new IllegalArgumentException( + "Resource with path " + + resourcePath + + " could not be found on any of these classloaders: " + + classLoadersToSearch + ); } private static String unencodeResourceURIToFilePath(@NotNull final String resource) { try { // Convert any url-encoded characters (e.g. spaces) back into unencoded form - return URLDecoder.decode(resource.replaceAll("\\+", "%2B"), Charsets.UTF_8.name()) - .replaceFirst("jar:", "") - .replaceFirst("file:", "") - .replaceAll("!.*", ""); + return URLDecoder + .decode(resource.replaceAll("\\+", "%2B"), Charsets.UTF_8.name()) + .replaceFirst("jar:", "") + .replaceFirst("file:", "") + .replaceAll("!.*", ""); } catch (UnsupportedEncodingException e) { throw new IllegalStateException(e); } @@ -237,15 +245,19 @@ private String extractClassPathResourceToTempLocation(final String hostPath) { JarEntry entry = entries.nextElement(); final String name = entry.getName(); if (name.startsWith(internalPath)) { - log.debug("Copying classpath resource(s) from {} to {} to permit Docker to bind", - hostPath, - tmpLocation); + log.debug( + "Copying classpath resource(s) from {} to {} to permit Docker to bind", + hostPath, + tmpLocation + ); copyFromJarToLocation(jarFile, entry, internalPath, tmpLocation); } } - } catch (IOException e) { - throw new IllegalStateException("Failed to process JAR file when extracting classpath resource: " + hostPath, e); + throw new IllegalStateException( + "Failed to process JAR file when extracting classpath resource: " + hostPath, + e + ); } // Mark temporary files/dirs for deletion at JVM shutdown @@ -264,23 +276,22 @@ private File createTempDirectory() { return Files.createTempDirectory(Paths.get(OS_MAC_TMP_DIR), TESTCONTAINERS_TMP_DIR_PREFIX).toFile(); } return Files.createTempDirectory(TESTCONTAINERS_TMP_DIR_PREFIX).toFile(); - } catch (IOException e) { + } catch (IOException e) { return new File(TESTCONTAINERS_TMP_DIR_PREFIX + Base58.randomString(5)); } } @SuppressWarnings("ResultOfMethodCallIgnored") - private void copyFromJarToLocation(final JarFile jarFile, - final JarEntry entry, - final String fromRoot, - final File toRoot) throws IOException { - + private void copyFromJarToLocation( + final JarFile jarFile, + final JarEntry entry, + final String fromRoot, + final File toRoot + ) throws IOException { String destinationName = entry.getName().replaceFirst(fromRoot, ""); File newFile = new File(toRoot, destinationName); - log.debug("Copying resource {} from JAR file {}", - fromRoot, - jarFile.getName()); + log.debug("Copying resource {} from JAR file {}", fromRoot, jarFile.getName()); if (!entry.isDirectory()) { // Create parent directories @@ -291,14 +302,21 @@ private void copyFromJarToLocation(final JarFile jarFile, try (InputStream is = jarFile.getInputStream(entry)) { Files.copy(is, newFile.toPath()); } catch (IOException e) { - log.error("Failed to extract classpath resource " + entry.getName() + " from JAR file " + jarFile.getName(), e); + log.error( + "Failed to extract classpath resource " + entry.getName() + " from JAR file " + jarFile.getName(), + e + ); throw e; } } } private void deleteOnExit(final Path path) { - Runtime.getRuntime().addShutdownHook(new Thread(DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, () -> recursiveDeleteDir(path))); + Runtime + .getRuntime() + .addShutdownHook( + new Thread(DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, () -> PathUtils.recursiveDeleteDir(path)) + ); } /** @@ -312,11 +330,20 @@ public void transferTo(final TarArchiveOutputStream outputStream, String destina /* * Recursively copies a file/directory into a TarArchiveOutputStream */ - private void recursiveTar(String entryFilename, String rootPath, String itemPath, TarArchiveOutputStream tarArchive) { + private void recursiveTar( + String entryFilename, + String rootPath, + String itemPath, + TarArchiveOutputStream tarArchive + ) { try { - final File sourceFile = new File(itemPath).getCanonicalFile(); // e.g. /foo/bar/baz - final File sourceRootFile = new File(rootPath).getCanonicalFile(); // e.g. /foo - final String relativePathToSourceFile = sourceRootFile.toPath().relativize(sourceFile.toPath()).toFile().toString(); // e.g. /bar/baz + final File sourceFile = new File(itemPath).getCanonicalFile(); // e.g. /foo/bar/baz + final File sourceRootFile = new File(rootPath).getCanonicalFile(); // e.g. /foo + final String relativePathToSourceFile = sourceRootFile + .toPath() + .relativize(sourceFile.toPath()) + .toFile() + .toString(); // e.g. /bar/baz final String tarEntryFilename; if (relativePathToSourceFile.isEmpty()) { @@ -341,7 +368,12 @@ private void recursiveTar(String entryFilename, String rootPath, String itemPath if (children != null) { // recurse into child files/directories for (final File child : children) { - recursiveTar(entryFilename, sourceRootFile.getCanonicalPath(), child.getCanonicalPath(), tarArchive); + recursiveTar( + entryFilename, + sourceRootFile.getCanonicalPath(), + child.getCanonicalPath(), + tarArchive + ); } } } catch (IOException e) { @@ -352,7 +384,6 @@ private void recursiveTar(String entryFilename, String rootPath, String itemPath @Override public long getSize() { - final File file = new File(this.getResolvedPath()); if (file.isFile()) { return file.length(); @@ -378,9 +409,11 @@ private void checksumFile(File file, Checksum checksum) { checksum.update(MountableFile.getUnixFileMode(path)); if (file.isDirectory()) { try (Stream stream = Files.walk(path)) { - stream.filter(it -> it != path).forEach(it -> { - checksumFile(it.toFile(), checksum); - }); + stream + .filter(it -> it != path) + .forEach(it -> { + checksumFile(it.toFile(), checksum); + }); } } else { FileUtils.checksum(file, checksum); @@ -405,9 +438,11 @@ public static int getUnixFileMode(final Path path) { try { int unixMode = (int) Files.readAttributes(path, "unix:mode").get("mode"); // Truncate mode bits for z/OS - if ("OS/390".equals(SystemUtils.OS_NAME) || + if ( + "OS/390".equals(SystemUtils.OS_NAME) || "z/OS".equals(SystemUtils.OS_NAME) || - "zOS".equals(SystemUtils.OS_NAME) ) { + "zOS".equals(SystemUtils.OS_NAME) + ) { unixMode &= TarConstants.MAXID; unixMode |= Files.isDirectory(path) ? 040000 : 0100000; } diff --git a/core/src/main/java/org/testcontainers/utility/PathUtils.java b/core/src/main/java/org/testcontainers/utility/PathUtils.java index 2dcf9db8a8d..7f8b5925689 100644 --- a/core/src/main/java/org/testcontainers/utility/PathUtils.java +++ b/core/src/main/java/org/testcontainers/utility/PathUtils.java @@ -23,21 +23,23 @@ public class PathUtils { */ public static void recursiveDeleteDir(final @NonNull Path directory) { try { - Files.walkFileTree(directory, new SimpleFileVisitor() { - @Override - public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { - Files.delete(dir); - return FileVisitResult.CONTINUE; - } + Files.walkFileTree( + directory, + new SimpleFileVisitor() { + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + Files.delete(dir); + return FileVisitResult.CONTINUE; + } - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - Files.delete(file); - return FileVisitResult.CONTINUE; + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Files.delete(file); + return FileVisitResult.CONTINUE; + } } - }); - } catch (IOException ignored) { - } + ); + } catch (IOException ignored) {} } /** @@ -66,8 +68,10 @@ public static String createMinGWPath(String path) { } // drive-letter must be lower case - mingwPath = "//" + Character.toLowerCase(mingwPath.charAt(driveLetterIndex)) + - mingwPath.substring(driveLetterIndex + 1); + mingwPath = + "//" + + Character.toLowerCase(mingwPath.charAt(driveLetterIndex)) + + mingwPath.substring(driveLetterIndex + 1); mingwPath = mingwPath.replace(":", ""); return mingwPath; } diff --git a/core/src/main/java/org/testcontainers/utility/PrefixingImageNameSubstitutor.java b/core/src/main/java/org/testcontainers/utility/PrefixingImageNameSubstitutor.java index c720416deac..7cf082000a4 100644 --- a/core/src/main/java/org/testcontainers/utility/PrefixingImageNameSubstitutor.java +++ b/core/src/main/java/org/testcontainers/utility/PrefixingImageNameSubstitutor.java @@ -38,11 +38,7 @@ public DockerImageName apply(DockerImageName original) { return original; } - log.debug( - "Applying changes to image name {}: applying prefix '{}'", - original, - configuredPrefix - ); + log.debug("Applying changes to image name {}: applying prefix '{}'", original, configuredPrefix); DockerImageName prefixAsImage = DockerImageName.parse(configuredPrefix); diff --git a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java index e487bda686b..0001b926059 100644 --- a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java +++ b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java @@ -7,6 +7,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.SystemUtils; import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.testcontainers.DockerClientFactory; import org.zeroturnaround.exec.InvalidResultException; import org.zeroturnaround.exec.ProcessExecutor; @@ -23,23 +24,23 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import static org.apache.commons.lang3.StringUtils.isBlank; -import static org.slf4j.LoggerFactory.getLogger; -import static org.testcontainers.utility.AuthConfigUtil.toSafeString; - /** * Utility to look up registry authentication information for an image. */ public class RegistryAuthLocator { - private static final Logger log = getLogger(RegistryAuthLocator.class); + private static final Logger log = LoggerFactory.getLogger(RegistryAuthLocator.class); + private static final String DEFAULT_REGISTRY_NAME = "https://index.docker.io/v1/"; + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static RegistryAuthLocator instance; private final String commandPathPrefix; + private final String commandExtension; + private final File configFile; private final Map> cache = new ConcurrentHashMap<>(); @@ -51,8 +52,12 @@ public class RegistryAuthLocator { private final Map CREDENTIALS_HELPERS_NOT_FOUND_MESSAGE_CACHE; @VisibleForTesting - RegistryAuthLocator(File configFile, String commandPathPrefix, String commandExtension, - Map notFoundMessageHolderReference) { + RegistryAuthLocator( + File configFile, + String commandPathPrefix, + String commandExtension, + Map notFoundMessageHolderReference + ) { this.configFile = configFile; this.commandPathPrefix = commandPathPrefix; this.commandExtension = commandExtension; @@ -63,8 +68,9 @@ public class RegistryAuthLocator { /** */ protected RegistryAuthLocator() { - final String dockerConfigLocation = System.getenv().getOrDefault("DOCKER_CONFIG", - System.getProperty("user.home") + "/.docker"); + final String dockerConfigLocation = System + .getenv() + .getOrDefault("DOCKER_CONFIG", System.getProperty("user.home") + "/.docker"); this.configFile = new File(dockerConfigLocation + "/config.json"); this.commandPathPrefix = ""; this.commandExtension = ""; @@ -72,7 +78,7 @@ protected RegistryAuthLocator() { this.CREDENTIALS_HELPERS_NOT_FOUND_MESSAGE_CACHE = new HashMap<>(); } - public synchronized static RegistryAuthLocator instance() { + public static synchronized RegistryAuthLocator instance() { if (instance == null) { instance = new RegistryAuthLocator(); } @@ -106,23 +112,31 @@ public AuthConfig lookupAuthConfig(DockerImageName dockerImageName, AuthConfig d final String registryName = effectiveRegistryName(dockerImageName); log.debug("Looking up auth config for image: {} at registry: {}", dockerImageName, registryName); - final Optional cachedAuth = cache.computeIfAbsent(registryName, __ -> lookupUncachedAuthConfig(registryName, dockerImageName)); + final Optional cachedAuth = cache.computeIfAbsent( + registryName, + __ -> lookupUncachedAuthConfig(registryName, dockerImageName) + ); if (cachedAuth.isPresent()) { - log.debug("Cached auth found: [{}]", toSafeString(cachedAuth.get())); + log.debug("Cached auth found: [{}]", AuthConfigUtil.toSafeString(cachedAuth.get())); return cachedAuth.get(); } else { - log.debug("No matching Auth Configs - falling back to defaultAuthConfig [{}]", toSafeString(defaultAuthConfig)); + log.debug( + "No matching Auth Configs - falling back to defaultAuthConfig [{}]", + AuthConfigUtil.toSafeString(defaultAuthConfig) + ); // otherwise, defaultAuthConfig should already contain any credentials available return defaultAuthConfig; } } private Optional lookupUncachedAuthConfig(String registryName, DockerImageName dockerImageName) { - log.debug("RegistryAuthLocator has configFile: {} ({}) and commandPathPrefix: {}", + log.debug( + "RegistryAuthLocator has configFile: {} ({}) and commandPathPrefix: {}", configFile, configFile.exists() ? "exists" : "does not exist", - commandPathPrefix); + commandPathPrefix + ); try { final JsonNode config = OBJECT_MAPPER.readTree(configFile); @@ -131,32 +145,33 @@ private Optional lookupUncachedAuthConfig(String registryName, Docke // use helper preferentially (per https://docs.docker.com/engine/reference/commandline/cli/) final AuthConfig helperAuthConfig = authConfigUsingHelper(config, registryName); if (helperAuthConfig != null) { - log.debug("found helper auth config [{}]", toSafeString(helperAuthConfig)); + log.debug("found helper auth config [{}]", AuthConfigUtil.toSafeString(helperAuthConfig)); return Optional.of(helperAuthConfig); } // no credsHelper to use, using credsStore: final AuthConfig storeAuthConfig = authConfigUsingStore(config, registryName); if (storeAuthConfig != null) { - log.debug("found creds store auth config [{}]", toSafeString(storeAuthConfig)); + log.debug("found creds store auth config [{}]", AuthConfigUtil.toSafeString(storeAuthConfig)); return Optional.of(storeAuthConfig); } // fall back to base64 encoded auth hardcoded in config file final AuthConfig existingAuthConfig = findExistingAuthConfig(config, registryName); if (existingAuthConfig != null) { - log.debug("found existing auth config [{}]", toSafeString(existingAuthConfig)); + log.debug("found existing auth config [{}]", AuthConfigUtil.toSafeString(existingAuthConfig)); return Optional.of(existingAuthConfig); } } catch (Exception e) { - log.info("Failure when attempting to lookup auth config. Please ignore if you don't have images in an authenticated registry. Details: (dockerImageName: {}, configFile: {}. Falling back to docker-java default behaviour. Exception message: {}", + log.info( + "Failure when attempting to lookup auth config. Please ignore if you don't have images in an authenticated registry. Details: (dockerImageName: {}, configFile: {}. Falling back to docker-java default behaviour. Exception message: {}", dockerImageName, configFile, - e.getMessage()); + e.getMessage() + ); } return Optional.empty(); } private AuthConfig findExistingAuthConfig(final JsonNode config, final String reposName) throws Exception { - final Map.Entry entry = findAuthNode(config, reposName); if (entry != null && entry.getValue() != null && entry.getValue().size() > 0) { @@ -164,10 +179,11 @@ private AuthConfig findExistingAuthConfig(final JsonNode config, final String re .treeToValue(entry.getValue(), AuthConfig.class) .withRegistryAddress(entry.getKey()); - if (isBlank(deserializedAuth.getUsername()) && - isBlank(deserializedAuth.getPassword()) && - !isBlank(deserializedAuth.getAuth())) { - + if ( + StringUtils.isBlank(deserializedAuth.getUsername()) && + StringUtils.isBlank(deserializedAuth.getPassword()) && + !StringUtils.isBlank(deserializedAuth.getAuth()) + ) { final String rawAuth = new String(Base64.getDecoder().decode(deserializedAuth.getAuth())); final String[] splitRawAuth = rawAuth.split(":", 2); @@ -198,7 +214,7 @@ private AuthConfig authConfigUsingStore(final JsonNode config, final String repo final JsonNode credsStoreNode = config.get("credsStore"); if (credsStoreNode != null && !credsStoreNode.isMissingNode() && credsStoreNode.isTextual()) { final String credsStore = credsStoreNode.asText(); - if (isBlank(credsStore)) { + if (StringUtils.isBlank(credsStore)) { log.warn("Docker auth config credsStore field will be ignored, because value is blank"); return null; } @@ -222,8 +238,7 @@ private Map.Entry findAuthNode(final JsonNode config, final St } private AuthConfig runCredentialProvider(String hostName, String helperOrStoreName) throws Exception { - - if (isBlank(hostName)) { + if (StringUtils.isBlank(hostName)) { log.debug("There is no point in locating AuthConfig for blank hostName. Returning NULL to allow fallback"); return null; } @@ -231,28 +246,34 @@ private AuthConfig runCredentialProvider(String hostName, String helperOrStoreNa final String credentialProgramName = getCredentialProgramName(helperOrStoreName); final String data; - log.debug("Executing docker credential provider: {} to locate auth config for: {}", - credentialProgramName, hostName); + log.debug( + "Executing docker credential provider: {} to locate auth config for: {}", + credentialProgramName, + hostName + ); try { data = runCredentialProgram(hostName, credentialProgramName); } catch (InvalidResultException e) { - final String responseErrorMsg = extractCredentialProviderErrorMessage(e); - if (!isBlank(responseErrorMsg)) { + if (!StringUtils.isBlank(responseErrorMsg)) { String credentialsNotFoundMsg = getGenericCredentialsNotFoundMsg(credentialProgramName); if (credentialsNotFoundMsg != null && credentialsNotFoundMsg.equals(responseErrorMsg)) { - log.info("Credential helper/store ({}) does not have credentials for {}", + log.info( + "Credential helper/store ({}) does not have credentials for {}", credentialProgramName, - hostName); + hostName + ); return null; } - log.debug("Failure running docker credential helper/store ({}) with output '{}'", - credentialProgramName, responseErrorMsg); - + log.debug( + "Failure running docker credential helper/store ({}) with output '{}'", + credentialProgramName, + responseErrorMsg + ); } else { log.debug("Failure running docker credential helper/store ({})", credentialProgramName); } @@ -296,7 +317,7 @@ private String effectiveRegistryName(DockerImageName dockerImageName) { private String getGenericCredentialsNotFoundMsg(String credentialHelperName) { if (!CREDENTIALS_HELPERS_NOT_FOUND_MESSAGE_CACHE.containsKey(credentialHelperName)) { String credentialsNotFoundMsg = discoverCredentialsHelperNotFoundMessage(credentialHelperName); - if (!isBlank(credentialsNotFoundMsg)) { + if (!StringUtils.isBlank(credentialsNotFoundMsg)) { CREDENTIALS_HELPERS_NOT_FOUND_MESSAGE_CACHE.put(credentialHelperName, credentialsNotFoundMsg); } } @@ -317,19 +338,26 @@ private String discoverCredentialsHelperNotFoundMessage(String credentialHelperN runCredentialProgram(notExistentFakeHostName, credentialHelperName); // should not reach here - log.warn("Failure running docker credential helper ({}) with fake call, expected 'credentials not found' response", - credentialHelperName); - } catch(Exception e) { + log.warn( + "Failure running docker credential helper ({}) with fake call, expected 'credentials not found' response", + credentialHelperName + ); + } catch (Exception e) { if (e instanceof InvalidResultException) { - credentialsNotFoundMsg = extractCredentialProviderErrorMessage((InvalidResultException)e); + credentialsNotFoundMsg = extractCredentialProviderErrorMessage((InvalidResultException) e); } - if (isBlank(credentialsNotFoundMsg)) { - log.warn("Failure running docker credential helper ({}) with fake call, expected 'credentials not found' response. Exception message: {}", + if (StringUtils.isBlank(credentialsNotFoundMsg)) { + log.warn( + "Failure running docker credential helper ({}) with fake call, expected 'credentials not found' response. Exception message: {}", credentialHelperName, - e.getMessage()); + e.getMessage() + ); } else { - log.debug("Got credentials not found error message from docker credential helper - {}", credentialsNotFoundMsg); + log.debug( + "Got credentials not found error message from docker credential helper - {}", + credentialsNotFoundMsg + ); } } @@ -345,16 +373,17 @@ private String extractCredentialProviderErrorMessage(InvalidResultException inva private String runCredentialProgram(String hostName, String credentialHelperName) throws InvalidResultException, InterruptedException, TimeoutException, IOException { - - String[] command = SystemUtils.IS_OS_WINDOWS ? new String[] {"cmd", "/c", credentialHelperName, "get"} : new String[]{credentialHelperName, "get"}; + String[] command = SystemUtils.IS_OS_WINDOWS + ? new String[] { "cmd", "/c", credentialHelperName, "get" } + : new String[] { credentialHelperName, "get" }; return new ProcessExecutor() - .command(command) - .redirectInput(new ByteArrayInputStream(hostName.getBytes())) - .readOutput(true) - .exitValueNormal() - .timeout(30, TimeUnit.SECONDS) - .execute() - .outputUTF8() - .trim(); + .command(command) + .redirectInput(new ByteArrayInputStream(hostName.getBytes())) + .readOutput(true) + .exitValueNormal() + .timeout(30, TimeUnit.SECONDS) + .execute() + .outputUTF8() + .trim(); } } diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index d916579db35..ad0c949f6e7 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -49,29 +49,30 @@ public class ResourceReaper { static final List>> DEATH_NOTE = new ArrayList<>( Arrays.asList( Stream - .concat( - DockerClientFactory.DEFAULT_LABELS.entrySet().stream(), - MARKER_LABELS.entrySet().stream() - ) + .concat(DockerClientFactory.DEFAULT_LABELS.entrySet().stream(), MARKER_LABELS.entrySet().stream()) .>map(it -> new SimpleEntry<>("label", it.getKey() + "=" + it.getValue())) .collect(Collectors.toList()) ) ); private static ResourceReaper instance; + final DockerClient dockerClient = DockerClientFactory.lazyClient(); + private Map registeredContainers = new ConcurrentHashMap<>(); + private Set registeredNetworks = Sets.newConcurrentHashSet(); + private Set registeredImages = Sets.newConcurrentHashSet(); + private AtomicBoolean hookIsSet = new AtomicBoolean(false); /** * Internal constructor to avoid custom implementations */ - ResourceReaper() { - } + ResourceReaper() {} - public synchronized static ResourceReaper instance() { + public static synchronized ResourceReaper instance() { if (instance == null) { boolean useRyuk = !Boolean.parseBoolean(System.getenv("TESTCONTAINERS_RYUK_DISABLED")); if (useRyuk) { @@ -117,7 +118,9 @@ public void registerFilterForCleanup(List> filter) { */ public void registerLabelsFilterForCleanup(Map labels) { registerFilterForCleanup( - labels.entrySet().stream() + labels + .entrySet() + .stream() .map(it -> new SimpleEntry<>("label", it.getKey() + "=" + it.getValue())) .collect(Collectors.toList()) ); @@ -172,9 +175,11 @@ private void removeContainer(String containerId, String imageName) { LOGGER.trace("Was going to stop container but it apparently no longer exists: {}", containerId); return; } catch (Exception e) { - LOGGER.trace("Error encountered when checking container for shutdown (ID: {}) - it may not have been stopped, or may already be stopped. Root cause: {}", + LOGGER.trace( + "Error encountered when checking container for shutdown (ID: {}) - it may not have been stopped, or may already be stopped. Root cause: {}", containerId, - Throwables.getRootCause(e).getMessage()); + Throwables.getRootCause(e).getMessage() + ); return; } @@ -184,9 +189,11 @@ private void removeContainer(String containerId, String imageName) { dockerClient.killContainerCmd(containerId).exec(); LOGGER.trace("Stopped container: {}", imageName); } catch (Exception e) { - LOGGER.trace("Error encountered shutting down container (ID: {}) - it may not have been stopped, or may already be stopped. Root cause: {}", + LOGGER.trace( + "Error encountered shutting down container (ID: {}) - it may not have been stopped, or may already be stopped. Root cause: {}", containerId, - Throwables.getRootCause(e).getMessage()); + Throwables.getRootCause(e).getMessage() + ); } } @@ -202,9 +209,11 @@ private void removeContainer(String containerId, String imageName) { dockerClient.removeContainerCmd(containerId).withRemoveVolumes(true).withForce(true).exec(); LOGGER.debug("Removed container and associated volume(s): {}", imageName); } catch (Exception e) { - LOGGER.trace("Error encountered shutting down container (ID: {}) - it may not have been stopped, or may already be stopped. Root cause: {}", + LOGGER.trace( + "Error encountered shutting down container (ID: {}) - it may not have been stopped, or may already be stopped. Root cause: {}", containerId, - Throwables.getRootCause(e).getMessage()); + Throwables.getRootCause(e).getMessage() + ); } } @@ -227,10 +236,9 @@ public void registerNetworkIdForCleanup(String id) { */ @Deprecated public void removeNetworkById(String id) { - removeNetwork(id); + removeNetwork(id); } - private void removeNetwork(String id) { try { List networks; @@ -239,7 +247,10 @@ private void removeNetwork(String id) { // Listing by ID first prevents docker-java logging an error if we just go blindly into removeNetworkCmd networks = dockerClient.listNetworksCmd().withIdFilter(id).exec(); } catch (Exception e) { - LOGGER.trace("Error encountered when looking up network for removal (name: {}) - it may not have been removed", id); + LOGGER.trace( + "Error encountered when looking up network for removal (name: {}) - it may not have been removed", + id + ); return; } @@ -251,7 +262,10 @@ private void removeNetwork(String id) { registeredNetworks.remove(network.getId()); LOGGER.debug("Removed network: {}", id); } catch (Exception e) { - LOGGER.trace("Error encountered removing network (name: {}) - it may not have been removed", network.getName()); + LOGGER.trace( + "Error encountered removing network (name: {}) - it may not have been removed", + network.getName() + ); } } } finally { @@ -296,7 +310,9 @@ private void removeImage(String dockerImageName) { void setHook() { if (hookIsSet.compareAndSet(false, true)) { // If the JVM stops without containers being stopped, try and stop the container. - Runtime.getRuntime().addShutdownHook(new Thread(DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, this::performCleanup)); + Runtime + .getRuntime() + .addShutdownHook(new Thread(DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, this::performCleanup)); } } @@ -323,8 +339,7 @@ public CreateContainerCmd register(GenericContainer container, CreateContaine * @deprecated internal API */ @Deprecated - public void init() { - } + public void init() {} static class FilterRegistry { @@ -332,6 +347,7 @@ static class FilterRegistry { static final String ACKNOWLEDGMENT = "ACK"; private final BufferedReader in; + private final OutputStream out; FilterRegistry(InputStream ryukInputStream, OutputStream ryukOutputStream) { @@ -347,10 +363,13 @@ static class FilterRegistry { * @throws IOException if communication with Ryuk fails */ protected boolean register(List> filters) throws IOException { - String query = filters.stream() + String query = filters + .stream() .map(it -> { try { - return URLEncoder.encode(it.getKey(), "UTF-8") + "=" + URLEncoder.encode(it.getValue(), "UTF-8"); + return ( + URLEncoder.encode(it.getKey(), "UTF-8") + "=" + URLEncoder.encode(it.getValue(), "UTF-8") + ); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } @@ -372,6 +391,5 @@ private static boolean waitForAcknowledgment(BufferedReader in) throws IOExcepti } return ACKNOWLEDGMENT.equalsIgnoreCase(line); } - } } diff --git a/core/src/main/java/org/testcontainers/utility/RyukContainer.java b/core/src/main/java/org/testcontainers/utility/RyukContainer.java index 1e18c14157a..511e6ccac17 100644 --- a/core/src/main/java/org/testcontainers/utility/RyukContainer.java +++ b/core/src/main/java/org/testcontainers/utility/RyukContainer.java @@ -10,15 +10,20 @@ class RyukContainer extends GenericContainer { RyukContainer() { super("testcontainers/ryuk:0.3.3"); - withExposedPorts(8080); withCreateContainerCmdModifier(cmd -> { cmd.withName("testcontainers-ryuk-" + DockerClientFactory.SESSION_ID); cmd.withHostConfig( - cmd.getHostConfig() + cmd + .getHostConfig() .withAutoRemove(true) .withPrivileged(TestcontainersConfiguration.getInstance().isRyukPrivileged()) - .withBinds(new Bind(DockerClientFactory.instance().getRemoteDockerUnixSocketPath(), new Volume("/var/run/docker.sock"))) + .withBinds( + new Bind( + DockerClientFactory.instance().getRemoteDockerUnixSocketPath(), + new Volume("/var/run/docker.sock") + ) + ) ); }); diff --git a/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java b/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java index 6ce9c372fd2..999b1cbbffb 100644 --- a/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java @@ -90,7 +90,10 @@ private synchronized void maybeStart() { // not set the read timeout, as Ryuk would not send anything unless a new filter is submitted, meaning that we would get a timeout exception pretty quick try (Socket clientSocket = new Socket()) { clientSocket.connect(new InetSocketAddress(host, ryukPort), 5 * 1000); - ResourceReaper.FilterRegistry registry = new ResourceReaper.FilterRegistry(clientSocket.getInputStream(), clientSocket.getOutputStream()); + ResourceReaper.FilterRegistry registry = new ResourceReaper.FilterRegistry( + clientSocket.getInputStream(), + clientSocket.getOutputStream() + ); synchronized (ResourceReaper.DEATH_NOTE) { while (true) { diff --git a/core/src/main/java/org/testcontainers/utility/TestEnvironment.java b/core/src/main/java/org/testcontainers/utility/TestEnvironment.java index dbd8e45a98f..ee58199540b 100644 --- a/core/src/main/java/org/testcontainers/utility/TestEnvironment.java +++ b/core/src/main/java/org/testcontainers/utility/TestEnvironment.java @@ -8,8 +8,7 @@ */ public class TestEnvironment { - private TestEnvironment() { - } + private TestEnvironment() {} public static boolean dockerApiAtLeast(String minimumVersion) { ComparableVersion min = new ComparableVersion(minimumVersion); @@ -29,4 +28,3 @@ public static boolean dockerIsDockerMachine() { return DockerClientFactory.instance().isUsing(DockerMachineClientProviderStrategy.class); } } - diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index c9dfd543ad6..e647a8d50b3 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -50,18 +50,29 @@ public class TestcontainersConfiguration { private static File USER_CONFIG_FILE = new File(System.getProperty("user.home"), "." + PROPERTIES_FILE_NAME); private static final String AMBASSADOR_IMAGE = "richnorth/ambassador"; + private static final String SOCAT_IMAGE = "alpine/socat"; + private static final String VNC_RECORDER_IMAGE = "testcontainers/vnc-recorder"; + private static final String COMPOSE_IMAGE = "docker/compose"; + private static final String ALPINE_IMAGE = "alpine"; + private static final String RYUK_IMAGE = "testcontainers/ryuk"; + private static final String KAFKA_IMAGE = "confluentinc/cp-kafka"; + private static final String PULSAR_IMAGE = "apachepulsar/pulsar"; + private static final String LOCALSTACK_IMAGE = "localstack/localstack"; + private static final String SSHD_IMAGE = "testcontainers/sshd"; + private static final String ORACLE_IMAGE = "gvenzl/oracle-xe"; - private static final ImmutableMap CONTAINER_MAPPING = ImmutableMap.builder() + private static final ImmutableMap CONTAINER_MAPPING = ImmutableMap + .builder() .put(DockerImageName.parse(AMBASSADOR_IMAGE), "ambassador.container.image") .put(DockerImageName.parse(SOCAT_IMAGE), "socat.container.image") .put(DockerImageName.parse(VNC_RECORDER_IMAGE), "vncrecorder.container.image") @@ -78,7 +89,7 @@ public class TestcontainersConfiguration { @Getter(lazy = true) private static final TestcontainersConfiguration instance = loadConfiguration(); - @SuppressWarnings({"ConstantConditions", "unchecked", "rawtypes"}) + @SuppressWarnings({ "ConstantConditions", "unchecked", "rawtypes" }) @VisibleForTesting static AtomicReference getInstanceField() { // Lazy Getter from Lombok changes the field's type to AtomicReference @@ -86,10 +97,16 @@ static AtomicReference getInstanceField() { } private final Properties userProperties; + private final Properties classpathProperties; + private final Map environment; - TestcontainersConfiguration(Properties userProperties, Properties classpathProperties, final Map environment) { + TestcontainersConfiguration( + Properties userProperties, + Properties classpathProperties, + final Map environment + ) { this.userProperties = userProperties; this.classpathProperties = classpathProperties; this.environment = environment; @@ -121,8 +138,7 @@ public String getTinyImage() { } public boolean isRyukPrivileged() { - return Boolean - .parseBoolean(getEnvVarOrProperty("ryuk.container.privileged", "false")); + return Boolean.parseBoolean(getEnvVarOrProperty("ryuk.container.privileged", "false")); } @Deprecated @@ -203,7 +219,11 @@ public Integer getClientPingTimeout() { @Nullable @Contract("_, !null, _ -> !null") - private String getConfigurable(@NotNull final String propertyName, @Nullable final String defaultValue, Properties... propertiesSources) { + private String getConfigurable( + @NotNull final String propertyName, + @Nullable final String defaultValue, + Properties... propertiesSources + ) { String envVarName = propertyName.replaceAll("\\.", "_").toUpperCase(); if (!envVarName.startsWith("TESTCONTAINERS_") && !envVarName.startsWith("DOCKER_")) { envVarName = "TESTCONTAINERS_" + envVarName; @@ -279,11 +299,15 @@ public String getUserProperty(@NotNull final String propertyName, @Nullable fina */ @Deprecated public Properties getProperties() { - return Stream.of(userProperties, classpathProperties) - .reduce(new Properties(), (a, b) -> { - a.putAll(b); - return a; - }); + return Stream + .of(userProperties, classpathProperties) + .reduce( + new Properties(), + (a, b) -> { + a.putAll(b); + return a; + } + ); } @Deprecated @@ -318,15 +342,20 @@ public boolean updateUserConfig(@NonNull String prop, @NonNull String value) { private static TestcontainersConfiguration loadConfiguration() { return new TestcontainersConfiguration( readProperties(USER_CONFIG_FILE.toURI().toURL()), - ClasspathScanner.scanFor(PROPERTIES_FILE_NAME) + ClasspathScanner + .scanFor(PROPERTIES_FILE_NAME) .map(TestcontainersConfiguration::readProperties) - .reduce(new Properties(), (a, b) -> { - // first-write-wins merging - URLs appearing first on the classpath alphabetically will take priority. - // Note that this means that file: URLs will always take priority over jar: URLs. - b.putAll(a); - return b; - }), - System.getenv()); + .reduce( + new Properties(), + (a, b) -> { + // first-write-wins merging - URLs appearing first on the classpath alphabetically will take priority. + // Note that this means that file: URLs will always take priority over jar: URLs. + b.putAll(a); + return b; + } + ), + System.getenv() + ); } private static Properties readProperties(URL url) { @@ -335,9 +364,17 @@ private static Properties readProperties(URL url) { try (InputStream inputStream = url.openStream()) { properties.load(inputStream); } catch (FileNotFoundException e) { - log.warn("Attempted to read Testcontainers configuration file at {} but the file was not found. Exception message: {}", url, ExceptionUtils.getRootCauseMessage(e)); + log.warn( + "Attempted to read Testcontainers configuration file at {} but the file was not found. Exception message: {}", + url, + ExceptionUtils.getRootCauseMessage(e) + ); } catch (IOException e) { - log.warn("Attempted to read Testcontainers configuration file at {} but could it not be loaded. Exception message: {}", url, ExceptionUtils.getRootCauseMessage(e)); + log.warn( + "Attempted to read Testcontainers configuration file at {} but could it not be loaded. Exception message: {}", + url, + ExceptionUtils.getRootCauseMessage(e) + ); } return properties; } @@ -349,14 +386,14 @@ private DockerImageName getImage(final String defaultValue) { DockerImageName getConfiguredSubstituteImage(DockerImageName original) { for (final Map.Entry entry : CONTAINER_MAPPING.entrySet()) { if (original.isCompatibleWith(entry.getKey())) { - return - Optional.ofNullable(entry.getValue()) - .map(propertyName -> getEnvVarOrProperty(propertyName, null)) - .map(String::valueOf) - .map(String::trim) - .map(DockerImageName::parse) - .orElse(original) - .asCompatibleSubstituteFor(original); + return Optional + .ofNullable(entry.getValue()) + .map(propertyName -> getEnvVarOrProperty(propertyName, null)) + .map(String::valueOf) + .map(String::trim) + .map(DockerImageName::parse) + .orElse(original) + .asCompatibleSubstituteFor(original); } } return original; diff --git a/core/src/main/java/org/testcontainers/utility/ThrowingFunction.java b/core/src/main/java/org/testcontainers/utility/ThrowingFunction.java index 6b2cef907e1..a8138f9dda0 100644 --- a/core/src/main/java/org/testcontainers/utility/ThrowingFunction.java +++ b/core/src/main/java/org/testcontainers/utility/ThrowingFunction.java @@ -1,6 +1,5 @@ package org.testcontainers.utility; public interface ThrowingFunction { - R apply(T t) throws Exception; } diff --git a/core/src/main/java/org/testcontainers/utility/Versioning.java b/core/src/main/java/org/testcontainers/utility/Versioning.java index 4b1da407440..372a105518f 100644 --- a/core/src/main/java/org/testcontainers/utility/Versioning.java +++ b/core/src/main/java/org/testcontainers/utility/Versioning.java @@ -45,8 +45,11 @@ public int hashCode() { @EqualsAndHashCode class TagVersioning implements Versioning { + public static final String TAG_REGEX = "[\\w][\\w.\\-]{0,127}"; + static final TagVersioning LATEST = new TagVersioning("latest"); + private final String tag; TagVersioning(String tag) { @@ -71,7 +74,9 @@ public String toString() { @EqualsAndHashCode class Sha256Versioning implements Versioning { + public static final String HASH_REGEX = "[0-9a-fA-F]{32,}"; + private final String hash; Sha256Versioning(String hash) { diff --git a/core/src/test/java/alt/testcontainers/images/OutOfPackageImagePullPolicyTest.java b/core/src/test/java/alt/testcontainers/images/OutOfPackageImagePullPolicyTest.java index e4197c7c1f6..168932106fb 100644 --- a/core/src/test/java/alt/testcontainers/images/OutOfPackageImagePullPolicyTest.java +++ b/core/src/test/java/alt/testcontainers/images/OutOfPackageImagePullPolicyTest.java @@ -1,29 +1,30 @@ package alt.testcontainers.images; import org.junit.Test; +import org.testcontainers.TestImages; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import org.testcontainers.images.AbstractImagePullPolicy; import org.testcontainers.images.ImageData; import org.testcontainers.utility.DockerImageName; -import static org.testcontainers.TestImages.TINY_IMAGE; - public class OutOfPackageImagePullPolicyTest { + @Test public void shouldSupportCustomPoliciesOutOfTestContainersPackage() { try ( - GenericContainer container = new GenericContainer<>(TINY_IMAGE) - .withImagePullPolicy(new AbstractImagePullPolicy() { - @Override - protected boolean shouldPullCached(DockerImageName imageName, ImageData localImageData) { - return false; + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) + .withImagePullPolicy( + new AbstractImagePullPolicy() { + @Override + protected boolean shouldPullCached(DockerImageName imageName, ImageData localImageData) { + return false; + } } - }) + ) ) { container.withStartupCheckStrategy(new OneShotStartupCheckStrategy()); container.start(); } } - } diff --git a/core/src/test/java/org/testcontainers/DaemonTest.java b/core/src/test/java/org/testcontainers/DaemonTest.java index f08614c8cdc..c1a1c20d60a 100644 --- a/core/src/test/java/org/testcontainers/DaemonTest.java +++ b/core/src/test/java/org/testcontainers/DaemonTest.java @@ -10,7 +10,6 @@ import java.util.stream.Collectors; import static org.junit.Assert.assertEquals; -import static org.testcontainers.TestImages.TINY_IMAGE; /** * This test forks a new JVM, otherwise it's not possible to reliably diff the threads @@ -23,7 +22,7 @@ public static void main(String[] args) { GenericContainer genericContainer = null; try { - genericContainer = new GenericContainer<>(TINY_IMAGE).withCommand("top"); + genericContainer = new GenericContainer<>(TestImages.TINY_IMAGE).withCommand("top"); genericContainer.start(); Set threads = new HashSet<>(Thread.getAllStackTraces().keySet()); @@ -34,11 +33,14 @@ public static void main(String[] args) { if (nonDaemonThreads.isEmpty()) { VisibleAssertions.pass("All threads marked as daemon"); } else { - String nonDaemonThreadNames = nonDaemonThreads.stream() + String nonDaemonThreadNames = nonDaemonThreads + .stream() .map(Thread::getName) .collect(Collectors.joining("\n", "\n", "")); - VisibleAssertions.fail("Expected all threads to be daemons but the following are not:\n" + nonDaemonThreadNames); + VisibleAssertions.fail( + "Expected all threads to be daemons but the following are not:\n" + nonDaemonThreadNames + ); } } finally { if (genericContainer != null) { diff --git a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java index a889764579f..794bbbc37c9 100644 --- a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java +++ b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java @@ -28,11 +28,13 @@ public void runCommandInsideDockerShouldNotFailIfImageDoesNotExistsLocally() { dockFactory.runInsideDocker( imageName, cmd -> cmd.withCmd("sh", "-c", "echo 'SUCCESS'"), - (client, id) -> - client.logContainerCmd(id) + (client, id) -> { + return client + .logContainerCmd(id) .withStdOut(true) .exec(new LogToStringContainerCallback()) - .toString() + .toString(); + } ); } } diff --git a/core/src/test/java/org/testcontainers/DockerRegistryContainer.java b/core/src/test/java/org/testcontainers/DockerRegistryContainer.java index 4018b2b954d..a907c674386 100644 --- a/core/src/test/java/org/testcontainers/DockerRegistryContainer.java +++ b/core/src/test/java/org/testcontainers/DockerRegistryContainer.java @@ -51,22 +51,30 @@ protected void containerIsStarting(InspectContainerResponse containerInfo) { WaitingConsumer waitingConsumer = new WaitingConsumer(); resultCallback.addConsumer(OutputFrame.OutputType.STDERR, waitingConsumer); - dockerClient.logContainerCmd(containerInfo.getId()) + dockerClient + .logContainerCmd(containerInfo.getId()) .withStdErr(true) .withFollowStream(true) .exec(resultCallback); - Pattern pattern = Pattern.compile(".*listening on .*:(\\d+).*", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - waitingConsumer.waitUntil(it -> { - String s = it.getUtf8String(); - Matcher matcher = pattern.matcher(s); - if (matcher.matches()) { - port.set(Integer.parseInt(matcher.group(1))); - return true; - } else { - return false; - } - }, 10, TimeUnit.SECONDS); + Pattern pattern = Pattern.compile( + ".*listening on .*:(\\d+).*", + Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE + ); + waitingConsumer.waitUntil( + it -> { + String s = it.getUtf8String(); + Matcher matcher = pattern.matcher(s); + if (matcher.matches()) { + port.set(Integer.parseInt(matcher.group(1))); + return true; + } else { + return false; + } + }, + 10, + TimeUnit.SECONDS + ); } endpoint = getHost() + ":" + port.get(); @@ -87,12 +95,15 @@ public DockerImageName createImage(String originalImage, String tag) { String dummyImageId = client.inspectImageCmd(originalImage).exec().getId(); - DockerImageName imageName = DockerImageName.parse(getEndpoint() + "/" + Base58.randomString(6).toLowerCase()).withTag(tag); + DockerImageName imageName = DockerImageName + .parse(getEndpoint() + "/" + Base58.randomString(6).toLowerCase()) + .withTag(tag); // push the image to the registry client.tagImageCmd(dummyImageId, imageName.asCanonicalNameString(), tag).exec(); - client.pushImageCmd(imageName.asCanonicalNameString()) + client + .pushImageCmd(imageName.asCanonicalNameString()) .exec(new ResultCallback.Adapter<>()) .awaitCompletion(1, TimeUnit.MINUTES); diff --git a/core/src/test/java/org/testcontainers/containers/ContainerStateTest.java b/core/src/test/java/org/testcontainers/containers/ContainerStateTest.java index 52296678fd4..4909e908713 100644 --- a/core/src/test/java/org/testcontainers/containers/ContainerStateTest.java +++ b/core/src/test/java/org/testcontainers/containers/ContainerStateTest.java @@ -5,10 +5,9 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import java.util.Collections; import java.util.List; -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -18,21 +17,22 @@ public class ContainerStateTest { @Parameterized.Parameters(name = "{0} ({1} -> {2})") public static Object[][] params() { - return new Object[][]{ - new Object[]{"regular mapping", "80:8080/tcp", singletonList(80),}, - new Object[]{"regular mapping with host", "127.0.0.1:80:8080/tcp", singletonList(80),}, - new Object[]{"zero port without host", ":0:8080/tcp", emptyList(),}, - new Object[]{"missing port with host", "0.0.0.0:0:8080/tcp", emptyList(),}, - new Object[]{"zero port (synthetic case)", "0:8080/tcp", emptyList(),}, - new Object[]{"missing port", ":8080/tcp", emptyList(), - } + return new Object[][] { + new Object[] { "regular mapping", "80:8080/tcp", Collections.singletonList(80) }, + new Object[] { "regular mapping with host", "127.0.0.1:80:8080/tcp", Collections.singletonList(80) }, + new Object[] { "zero port without host", ":0:8080/tcp", Collections.emptyList() }, + new Object[] { "missing port with host", "0.0.0.0:0:8080/tcp", Collections.emptyList() }, + new Object[] { "zero port (synthetic case)", "0:8080/tcp", Collections.emptyList() }, + new Object[] { "missing port", ":8080/tcp", Collections.emptyList() }, }; } @Parameterized.Parameter(0) public String name; + @Parameterized.Parameter(1) public String testSet; + @Parameterized.Parameter(2) public List expectedResult; @@ -41,7 +41,7 @@ public void test() { ContainerState containerState = mock(ContainerState.class); doCallRealMethod().when(containerState).getBoundPortNumbers(); - when(containerState.getPortBindings()).thenReturn(singletonList(testSet)); + when(containerState.getPortBindings()).thenReturn(Collections.singletonList(testSet)); List result = containerState.getBoundPortNumbers(); Assertions.assertThat(result).hasSameElementsAs(expectedResult); diff --git a/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java b/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java index 8927972e642..8f056de98fa 100644 --- a/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java +++ b/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java @@ -9,17 +9,20 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; - public class DockerComposeContainerWithServicesTest { - public static final File SIMPLE_COMPOSE_FILE = new File("src/test/resources/compose-scaling-multiple-containers.yml"); - public static final File COMPOSE_FILE_WITH_INLINE_SCALE = new File("src/test/resources/compose-with-inline-scale-test.yml"); + public static final File SIMPLE_COMPOSE_FILE = new File( + "src/test/resources/compose-scaling-multiple-containers.yml" + ); + + public static final File COMPOSE_FILE_WITH_INLINE_SCALE = new File( + "src/test/resources/compose-with-inline-scale-test.yml" + ); @Test public void testDesiredSubsetOfServicesAreStarted() { try ( - DockerComposeContainer compose = new DockerComposeContainer<>(SIMPLE_COMPOSE_FILE) - .withServices("redis") + DockerComposeContainer compose = new DockerComposeContainer<>(SIMPLE_COMPOSE_FILE).withServices("redis") ) { compose.start(); @@ -67,9 +70,7 @@ public void testDesiredSubsetOfSpecifiedOrScaledServicesAreStarted() { @Test public void testAllServicesAreStartedIfNotSpecified() { - try ( - DockerComposeContainer compose = new DockerComposeContainer<>(SIMPLE_COMPOSE_FILE) - ) { + try (DockerComposeContainer compose = new DockerComposeContainer<>(SIMPLE_COMPOSE_FILE)) { compose.start(); verifyStartedContainers(compose, "redis_1", "other_1"); @@ -78,9 +79,7 @@ public void testAllServicesAreStartedIfNotSpecified() { @Test public void testScaleInComposeFileIsRespected() { - try ( - DockerComposeContainer compose = new DockerComposeContainer<>(COMPOSE_FILE_WITH_INLINE_SCALE) - ) { + try (DockerComposeContainer compose = new DockerComposeContainer<>(COMPOSE_FILE_WITH_INLINE_SCALE)) { compose.start(); // the compose file includes `scale: 3` for the redis container @@ -89,17 +88,20 @@ public void testScaleInComposeFileIsRespected() { } private void verifyStartedContainers(final DockerComposeContainer compose, final String... names) { - final List containerNames = compose.listChildContainers().stream() + final List containerNames = compose + .listChildContainers() + .stream() .flatMap(container -> Stream.of(container.getNames())) .collect(Collectors.toList()); - assertEquals("number of running services of docker-compose is the same as length of listOfServices", - names.length, containerNames.size()); + assertEquals( + "number of running services of docker-compose is the same as length of listOfServices", + names.length, + containerNames.size() + ); for (final String expectedName : names) { - final long matches = containerNames.stream() - .filter(foundName -> foundName.endsWith(expectedName)) - .count(); + final long matches = containerNames.stream().filter(foundName -> foundName.endsWith(expectedName)).count(); assertEquals("container with name starting '" + expectedName + "' should be running", 1L, matches); } diff --git a/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java b/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java index c47db0c4b66..6e1630f005f 100644 --- a/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java +++ b/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java @@ -1,25 +1,33 @@ package org.testcontainers.containers; - import com.google.common.collect.Lists; -import java.io.File; import org.assertj.core.api.Assertions; import org.junit.Test; +import java.io.File; + public class DockerComposeFilesTest { + @Test public void shouldGetDependencyImages() { - DockerComposeFiles dockerComposeFiles = new DockerComposeFiles(Lists.newArrayList(new File("src/test/resources/docker-compose-imagename-parsing-v2.yml"))); - Assertions.assertThat(dockerComposeFiles.getDependencyImages()) + DockerComposeFiles dockerComposeFiles = new DockerComposeFiles( + Lists.newArrayList(new File("src/test/resources/docker-compose-imagename-parsing-v2.yml")) + ); + Assertions + .assertThat(dockerComposeFiles.getDependencyImages()) .containsExactlyInAnyOrder("postgres:latest", "redis:latest", "mysql:latest"); } @Test public void shouldGetDependencyImagesWhenOverriding() { DockerComposeFiles dockerComposeFiles = new DockerComposeFiles( - Lists.newArrayList(new File("src/test/resources/docker-compose-imagename-overriding-a.yml"), - new File("src/test/resources/docker-compose-imagename-overriding-b.yml"))); - Assertions.assertThat(dockerComposeFiles.getDependencyImages()) + Lists.newArrayList( + new File("src/test/resources/docker-compose-imagename-overriding-a.yml"), + new File("src/test/resources/docker-compose-imagename-overriding-b.yml") + ) + ); + Assertions + .assertThat(dockerComposeFiles.getDependencyImages()) .containsExactlyInAnyOrder("alpine:3.14", "redis:b", "mysql:b", "aservice:latest"); } } diff --git a/core/src/test/java/org/testcontainers/containers/DockerComposeOverridesTest.java b/core/src/test/java/org/testcontainers/containers/DockerComposeOverridesTest.java index 9e9eb27b509..6207ae5ff45 100644 --- a/core/src/test/java/org/testcontainers/containers/DockerComposeOverridesTest.java +++ b/core/src/test/java/org/testcontainers/containers/DockerComposeOverridesTest.java @@ -8,7 +8,6 @@ import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import org.rnorth.ducttape.unreliables.Unreliables; -import org.testcontainers.containers.DockerComposeContainer; import java.io.BufferedReader; import java.io.File; @@ -24,14 +23,23 @@ public class DockerComposeOverridesTest { private static final File BASE_COMPOSE_FILE = new File("src/test/resources/docker-compose-base.yml"); + private static final String BASE_ENV_VAR = "bar=base"; - private static final File OVERRIDE_COMPOSE_FILE = new File("src/test/resources/docker-compose-non-default-override.yml"); + + private static final File OVERRIDE_COMPOSE_FILE = new File( + "src/test/resources/docker-compose-non-default-override.yml" + ); + private static final String OVERRIDE_ENV_VAR = "bar=overwritten"; + private static final int SERVICE_PORT = 3000; + private static final String SERVICE_NAME = "alpine_1"; private final boolean localMode; + private final String expectedEnvVar; + private final File[] composeFiles; public DockerComposeOverridesTest(boolean localMode, String expectedEnvVar, File... composeFiles) { @@ -42,18 +50,21 @@ public DockerComposeOverridesTest(boolean localMode, String expectedEnvVar, File @Parameters(name = "{index}: local[{0}], composeFiles[{2}], expectedEnvVar[{1}]") public static Iterable data() { - return Arrays.asList(new Object[][]{ - {true, BASE_ENV_VAR, new File[]{BASE_COMPOSE_FILE}}, - {true, OVERRIDE_ENV_VAR, new File[]{BASE_COMPOSE_FILE, OVERRIDE_COMPOSE_FILE}}, - {false, BASE_ENV_VAR, new File[]{BASE_COMPOSE_FILE}}, - {false, OVERRIDE_ENV_VAR, new File[]{BASE_COMPOSE_FILE, OVERRIDE_COMPOSE_FILE}}, - }); + return Arrays.asList( + new Object[][] { + { true, BASE_ENV_VAR, new File[] { BASE_COMPOSE_FILE } }, + { true, OVERRIDE_ENV_VAR, new File[] { BASE_COMPOSE_FILE, OVERRIDE_COMPOSE_FILE } }, + { false, BASE_ENV_VAR, new File[] { BASE_COMPOSE_FILE } }, + { false, OVERRIDE_ENV_VAR, new File[] { BASE_COMPOSE_FILE, OVERRIDE_COMPOSE_FILE } }, + } + ); } @Before public void setUp() { if (localMode) { - Assumptions.assumeThat(LocalDockerCompose.executableExists()) + Assumptions + .assumeThat(LocalDockerCompose.executableExists()) .as("docker-compose executable exists") .isTrue(); } @@ -61,33 +72,43 @@ public void setUp() { @Test public void test() { - try (DockerComposeContainer compose = - new DockerComposeContainer(composeFiles) - .withLocalCompose(localMode) - .withExposedService(SERVICE_NAME, SERVICE_PORT)) { - + try ( + DockerComposeContainer compose = new DockerComposeContainer(composeFiles) + .withLocalCompose(localMode) + .withExposedService(SERVICE_NAME, SERVICE_PORT) + ) { compose.start(); - BufferedReader br = Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, () -> { - Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); + BufferedReader br = Unreliables.retryUntilSuccess( + 10, + TimeUnit.SECONDS, + () -> { + Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); - Socket socket = new Socket(compose.getServiceHost(SERVICE_NAME, SERVICE_PORT), compose.getServicePort(SERVICE_NAME, SERVICE_PORT)); - return new BufferedReader(new InputStreamReader(socket.getInputStream())); - }); + Socket socket = new Socket( + compose.getServiceHost(SERVICE_NAME, SERVICE_PORT), + compose.getServicePort(SERVICE_NAME, SERVICE_PORT) + ); + return new BufferedReader(new InputStreamReader(socket.getInputStream())); + } + ); - Unreliables.retryUntilTrue(10, TimeUnit.SECONDS, () -> { - while (br.ready()) { - String line = br.readLine(); - if (line.contains(expectedEnvVar)) { - pass("Mapped environment variable was found"); - return true; + Unreliables.retryUntilTrue( + 10, + TimeUnit.SECONDS, + () -> { + while (br.ready()) { + String line = br.readLine(); + if (line.contains(expectedEnvVar)) { + pass("Mapped environment variable was found"); + return true; + } } + info("Mapped environment variable was not found yet - process probably not ready"); + Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); + return false; } - info("Mapped environment variable was not found yet - process probably not ready"); - Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); - return false; - }); + ); } } - } diff --git a/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java b/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java index 38005014662..4afddeaa5dd 100644 --- a/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java +++ b/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java @@ -7,13 +7,13 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; +import org.testcontainers.TestImages; import org.testcontainers.Testcontainers; import java.io.OutputStream; import java.net.InetSocketAddress; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.testcontainers.TestImages.TINY_IMAGE; public class ExposedHostTest { @@ -22,14 +22,17 @@ public class ExposedHostTest { @BeforeClass public static void setUpClass() throws Exception { server = HttpServer.create(new InetSocketAddress(0), 0); - server.createContext("/", exchange -> { - byte[] content = "Hello World!".getBytes(); - exchange.sendResponseHeaders(200, content.length); - try (OutputStream responseBody = exchange.getResponseBody()) { - responseBody.write(content); - responseBody.flush(); + server.createContext( + "/", + exchange -> { + byte[] content = "Hello World!".getBytes(); + exchange.sendResponseHeaders(200, content.length); + try (OutputStream responseBody = exchange.getResponseBody()) { + responseBody.write(content); + responseBody.flush(); + } } - }); + ); server.start(); } @@ -47,7 +50,7 @@ public void tearDown() { @Test public void testExposedHostAfterContainerIsStarted() { try ( - GenericContainer container = new GenericContainer<>(TINY_IMAGE) + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withCommand("top") .withAccessToHost(true) ) { @@ -60,14 +63,17 @@ public void testExposedHostAfterContainerIsStarted() { @Test public void testExposedHost() throws Exception { Testcontainers.exposeHostPorts(server.getAddress().getPort()); - assertResponse(new GenericContainer<>(TINY_IMAGE).withCommand("top"), server.getAddress().getPort()); + assertResponse(new GenericContainer<>(TestImages.TINY_IMAGE).withCommand("top"), server.getAddress().getPort()); } @Test public void testExposedHostWithNetwork() throws Exception { Testcontainers.exposeHostPorts(server.getAddress().getPort()); try (Network network = Network.newNetwork()) { - assertResponse(new GenericContainer<>(TINY_IMAGE).withNetwork(network).withCommand("top"), server.getAddress().getPort()); + assertResponse( + new GenericContainer<>(TestImages.TINY_IMAGE).withNetwork(network).withCommand("top"), + server.getAddress().getPort() + ); } } @@ -76,8 +82,8 @@ public void testExposedHostPortOnFixedInternalPorts() throws Exception { Testcontainers.exposeHostPorts(ImmutableMap.of(server.getAddress().getPort(), 80)); Testcontainers.exposeHostPorts(ImmutableMap.of(server.getAddress().getPort(), 81)); - assertResponse(new GenericContainer<>(TINY_IMAGE).withCommand("top"), 80); - assertResponse(new GenericContainer<>(TINY_IMAGE).withCommand("top"), 81); + assertResponse(new GenericContainer<>(TestImages.TINY_IMAGE).withCommand("top"), 80); + assertResponse(new GenericContainer<>(TestImages.TINY_IMAGE).withCommand("top"), 81); } @SneakyThrows @@ -85,7 +91,9 @@ protected void assertResponse(GenericContainer container, int port) { try { container.start(); - String response = container.execInContainer("wget", "-O", "-", "http://host.testcontainers.internal:" + port).getStdout(); + String response = container + .execInContainer("wget", "-O", "-", "http://host.testcontainers.internal:" + port) + .getStdout(); assertEquals("received response", "Hello World!", response); } finally { diff --git a/core/src/test/java/org/testcontainers/containers/FailureDetectingExternalResourceTest.java b/core/src/test/java/org/testcontainers/containers/FailureDetectingExternalResourceTest.java index 63993bc24ff..c743fcf6a43 100644 --- a/core/src/test/java/org/testcontainers/containers/FailureDetectingExternalResourceTest.java +++ b/core/src/test/java/org/testcontainers/containers/FailureDetectingExternalResourceTest.java @@ -25,5 +25,4 @@ public void finishedIsCalledForCleanupIfStartingThrows() throws Throwable { verify(res).starting(any()); verify(res).finished(any()); } - } diff --git a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java index 0c30d522cd9..fdd8ac1b146 100644 --- a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java +++ b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java @@ -17,13 +17,10 @@ import org.rnorth.ducttape.unreliables.Unreliables; import org.testcontainers.DockerClientFactory; import org.testcontainers.TestImages; -import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import org.testcontainers.containers.startupcheck.StartupCheckStrategy; import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy; -import org.testcontainers.images.builder.Transferable; - -import java.nio.charset.StandardCharsets; import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.images.builder.Transferable; import org.testcontainers.utility.MountableFile; import java.util.Arrays; @@ -34,11 +31,9 @@ import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.hamcrest.CoreMatchers.equalTo; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.TestImages.TINY_IMAGE; public class GenericContainerTest { @@ -52,7 +47,8 @@ public void shouldReportOOMAfterWait() { .withStartupCheckStrategy(new NoopStartupCheckStrategy()) .waitingFor(new WaitForExitedState(ContainerState::getOOMKilled)) .withCreateContainerCmdModifier(it -> { - it.getHostConfig() + it + .getHostConfig() .withMemory(20 * FileUtils.ONE_MB) .withMemorySwappiness(0L) .withMemorySwap(0L) @@ -61,8 +57,7 @@ public void shouldReportOOMAfterWait() { }) .withCommand("sh", "-c", "A='0123456789'; for i in $(seq 0 32); do A=$A$A; done; sleep 10m") ) { - assertThatThrownBy(container::start) - .hasStackTraceContaining("Container crashed with out-of-memory"); + assertThatThrownBy(container::start).hasStackTraceContaining("Container crashed with out-of-memory"); } } @@ -74,8 +69,7 @@ public void shouldReportErrorAfterWait() { .waitingFor(new WaitForExitedState(state -> state.getExitCode() > 0)) .withCommand("sh", "-c", "usleep 100; exit 123") ) { - assertThatThrownBy(container::start) - .hasStackTraceContaining("Container exited with code 123"); + assertThatThrownBy(container::start).hasStackTraceContaining("Container exited with code 123"); } } @@ -88,8 +82,7 @@ public void shouldCopyTransferableAsFile() { .waitingFor(new WaitForExitedState(state -> state.getExitCodeLong() > 0)) .withCommand("sh", "-c", "grep -q test /tmp/test && exit 100") ) { - assertThatThrownBy(container::start) - .hasStackTraceContaining("Container exited with code 100"); + assertThatThrownBy(container::start).hasStackTraceContaining("Container exited with code 100"); } } @@ -98,34 +91,31 @@ public void shouldCopyTransferableAfterMountableFile() { try ( GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withStartupCheckStrategy(new NoopStartupCheckStrategy()) - .withCopyFileToContainer( - MountableFile.forClasspathResource("test_copy_to_container.txt"), - "/tmp/test" - ) + .withCopyFileToContainer(MountableFile.forClasspathResource("test_copy_to_container.txt"), "/tmp/test") .withCopyToContainer(Transferable.of("test"), "/tmp/test") .waitingFor(new WaitForExitedState(state -> state.getExitCodeLong() > 0)) .withCommand("sh", "-c", "grep -q test /tmp/test && exit 100") ) { - assertThatThrownBy(container::start) - .hasStackTraceContaining("Container exited with code 100"); + assertThatThrownBy(container::start).hasStackTraceContaining("Container exited with code 100"); } } @Test public void shouldOnlyPublishExposedPorts() { ImageFromDockerfile image = new ImageFromDockerfile("publish-multiple") - .withDockerfileFromBuilder(builder -> + .withDockerfileFromBuilder(builder -> { builder - .from("testcontainers/helloworld:1.1.0") + .from("testcontainers/helloworld:1.1.0") // .expose(8080, 8081) - .build() - ); + .build(); + }); try (GenericContainer container = new GenericContainer<>(image).withExposedPorts(8080)) { container.start(); InspectContainerResponse inspectedContainer = container.getContainerInfo(); - List exposedPorts = Arrays.stream(inspectedContainer.getConfig().getExposedPorts()) + List exposedPorts = Arrays + .stream(inspectedContainer.getConfig().getExposedPorts()) .map(ExposedPort::getPort) .collect(Collectors.toList()); @@ -135,18 +125,14 @@ public void shouldOnlyPublishExposedPorts() { exposedPorts ); - Map hostBindings = inspectedContainer.getHostConfig().getPortBindings().getBindings(); - assertEquals( - "only 1 port is bound on the host (published)", - 1, - hostBindings.size() - ); + Map hostBindings = inspectedContainer + .getHostConfig() + .getPortBindings() + .getBindings(); + assertEquals("only 1 port is bound on the host (published)", 1, hostBindings.size()); Integer mappedPort = container.getMappedPort(8080); - assertTrue( - "port 8080 is bound to a different port on the host", - mappedPort != 8080 - ); + assertTrue("port 8080 is bound to a different port on the host", mappedPort != 8080); assertThrows( "trying to get a non-bound port mapping fails", @@ -160,21 +146,19 @@ public void shouldOnlyPublishExposedPorts() { @Test public void shouldWaitUntilExposedPortIsMapped() { - ImageFromDockerfile image = new ImageFromDockerfile("publish-multiple") - .withDockerfileFromBuilder(builder -> + .withDockerfileFromBuilder(builder -> { builder .from("testcontainers/helloworld:1.1.0") .expose(8080, 8081) // one additional port exposed in image - .build() - ); + .build(); + }); try ( GenericContainer container = new GenericContainer<>(image) .withExposedPorts(8080) .withCreateContainerCmdModifier(it -> it.withExposedPorts(ExposedPort.tcp(8082))) // another port exposed by modifier ) { - container.start(); assertEquals("Only withExposedPort should be exposed", 1, container.getExposedPorts().size()); @@ -200,16 +184,20 @@ static class WaitForExitedState extends AbstractWaitStrategy { @Override @SneakyThrows protected void waitUntilReady() { - Unreliables.retryUntilTrue(5, TimeUnit.SECONDS, () -> { - ContainerState state = waitStrategyTarget.getCurrentContainerInfo().getState(); - - log.debug("Current state: {}", state); - if (!"exited".equalsIgnoreCase(state.getStatus())) { - Thread.sleep(100); - return false; + Unreliables.retryUntilTrue( + 5, + TimeUnit.SECONDS, + () -> { + ContainerState state = waitStrategyTarget.getCurrentContainerInfo().getState(); + + log.debug("Current state: {}", state); + if (!"exited".equalsIgnoreCase(state.getStatus())) { + Thread.sleep(100); + return false; + } + return predicate.test(state); } - return predicate.test(state); - }); + ); throw new IllegalStateException("Nope!"); } diff --git a/core/src/test/java/org/testcontainers/containers/NetworkTest.java b/core/src/test/java/org/testcontainers/containers/NetworkTest.java index a39f0552a5a..d783f95e6d1 100644 --- a/core/src/test/java/org/testcontainers/containers/NetworkTest.java +++ b/core/src/test/java/org/testcontainers/containers/NetworkTest.java @@ -10,7 +10,6 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; -import static org.testcontainers.containers.Network.newNetwork; @RunWith(Enclosed.class) public class NetworkTest { @@ -18,18 +17,18 @@ public class NetworkTest { public static class WithRules { @Rule - public Network network = newNetwork(); + public Network network = Network.newNetwork(); @Rule public GenericContainer foo = new GenericContainer<>(TestImages.TINY_IMAGE) - .withNetwork(network) - .withNetworkAliases("foo") - .withCommand("/bin/sh", "-c", "while true ; do printf 'HTTP/1.1 200 OK\\n\\nyay' | nc -l -p 8080; done"); + .withNetwork(network) + .withNetworkAliases("foo") + .withCommand("/bin/sh", "-c", "while true ; do printf 'HTTP/1.1 200 OK\\n\\nyay' | nc -l -p 8080; done"); @Rule public GenericContainer bar = new GenericContainer<>(TestImages.TINY_IMAGE) - .withNetwork(network) - .withCommand("top"); + .withNetwork(network) + .withCommand("top"); @Test public void testNetworkSupport() throws Exception { @@ -44,16 +43,18 @@ public static class WithoutRules { public void testNetworkSupport() throws Exception { // useCustomNetwork { try ( - Network network = Network.newNetwork(); - - GenericContainer foo = new GenericContainer<>(TestImages.TINY_IMAGE) - .withNetwork(network) - .withNetworkAliases("foo") - .withCommand("/bin/sh", "-c", "while true ; do printf 'HTTP/1.1 200 OK\\n\\nyay' | nc -l -p 8080; done"); - - GenericContainer bar = new GenericContainer<>(TestImages.TINY_IMAGE) - .withNetwork(network) - .withCommand("top") + Network network = Network.newNetwork(); + GenericContainer foo = new GenericContainer<>(TestImages.TINY_IMAGE) + .withNetwork(network) + .withNetworkAliases("foo") + .withCommand( + "/bin/sh", + "-c", + "while true ; do printf 'HTTP/1.1 200 OK\\n\\nyay' | nc -l -p 8080; done" + ); + GenericContainer bar = new GenericContainer<>(TestImages.TINY_IMAGE) + .withNetwork(network) + .withCommand("top") ) { foo.start(); bar.start(); @@ -66,16 +67,12 @@ public void testNetworkSupport() throws Exception { @Test public void testBuilder() { - try ( - Network network = Network.builder() - .driver("macvlan") - .build() - ) { + try (Network network = Network.builder().driver("macvlan").build()) { String id = network.getId(); assertEquals( - "Flag is set", - "macvlan", - DockerClientFactory.instance().client().inspectNetworkCmd().withNetworkId(id).exec().getDriver() + "Flag is set", + "macvlan", + DockerClientFactory.instance().client().inspectNetworkCmd().withNetworkId(id).exec().getDriver() ); } } @@ -83,15 +80,13 @@ public void testBuilder() { @Test public void testModifiers() { try ( - Network network = Network.builder() - .createNetworkCmdModifier(cmd -> cmd.withDriver("macvlan")) - .build() + Network network = Network.builder().createNetworkCmdModifier(cmd -> cmd.withDriver("macvlan")).build() ) { String id = network.getId(); assertEquals( - "Flag is set", - "macvlan", - DockerClientFactory.instance().client().inspectNetworkCmd().withNetworkId(id).exec().getDriver() + "Flag is set", + "macvlan", + DockerClientFactory.instance().client().inspectNetworkCmd().withNetworkId(id).exec().getDriver() ); } } @@ -101,16 +96,22 @@ public void testReusability() { try (Network network = Network.newNetwork()) { String firstId = network.getId(); assertNotNull( - "Network exists", - DockerClientFactory.instance().client().inspectNetworkCmd().withNetworkId(firstId).exec() + "Network exists", + DockerClientFactory.instance().client().inspectNetworkCmd().withNetworkId(firstId).exec() ); network.close(); assertNotEquals( - "New network created", - firstId, - DockerClientFactory.instance().client().inspectNetworkCmd().withNetworkId(network.getId()).exec().getId() + "New network created", + firstId, + DockerClientFactory + .instance() + .client() + .inspectNetworkCmd() + .withNetworkId(network.getId()) + .exec() + .getId() ); } } diff --git a/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java b/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java index ef70d06ada1..61955de98ba 100644 --- a/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java +++ b/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java @@ -6,9 +6,9 @@ import org.junit.Test; import java.io.File; +import java.util.Collections; -import static java.util.Collections.emptyList; -import static java.util.Collections.emptyMap; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.entry; public class ParsedDockerComposeFileValidationTest { @@ -16,8 +16,7 @@ public class ParsedDockerComposeFileValidationTest { @Test public void shouldValidate() { File file = new File("src/test/resources/docker-compose-container-name-v1.yml"); - Assertions - .assertThatThrownBy(() -> { + assertThatThrownBy(() -> { new ParsedDockerComposeFile(file); }) .hasMessageContaining(file.getAbsolutePath()) @@ -26,25 +25,23 @@ public void shouldValidate() { @Test public void shouldRejectContainerNameV1() { - Assertions - .assertThatThrownBy(() -> { - new ParsedDockerComposeFile(ImmutableMap.of( - "redis", ImmutableMap.of("container_name", "redis") - )); + assertThatThrownBy(() -> { + new ParsedDockerComposeFile(ImmutableMap.of("redis", ImmutableMap.of("container_name", "redis"))); }) .hasMessageContaining("'container_name' property set for service 'redis'"); } @Test public void shouldRejectContainerNameV2() { - Assertions - .assertThatThrownBy(() -> { - new ParsedDockerComposeFile(ImmutableMap.of( - "version", "2", - "services", ImmutableMap.of( - "redis", ImmutableMap.of("container_name", "redis") + assertThatThrownBy(() -> { + new ParsedDockerComposeFile( + ImmutableMap.of( + "version", + "2", + "services", + ImmutableMap.of("redis", ImmutableMap.of("container_name", "redis")) ) - )); + ); }) .hasMessageContaining("'container_name' property set for service 'redis'"); } @@ -52,35 +49,29 @@ public void shouldRejectContainerNameV2() { @Test public void shouldIgnoreUnknownStructure() { // Everything is a list - new ParsedDockerComposeFile(emptyMap()); + new ParsedDockerComposeFile(Collections.emptyMap()); // services is not a map but List - new ParsedDockerComposeFile(ImmutableMap.of( - "version", "2", - "services", emptyList() - )); + new ParsedDockerComposeFile(ImmutableMap.of("version", "2", "services", Collections.emptyList())); // services is not a collection - new ParsedDockerComposeFile(ImmutableMap.of( - "version", "2", - "services", true - )); + new ParsedDockerComposeFile(ImmutableMap.of("version", "2", "services", true)); // no services while version is defined - new ParsedDockerComposeFile(ImmutableMap.of( - "version", "9000" - )); + new ParsedDockerComposeFile(ImmutableMap.of("version", "9000")); } @Test public void shouldObtainImageNamesV1() { File file = new File("src/test/resources/docker-compose-imagename-parsing-v1.yml"); ParsedDockerComposeFile parsedFile = new ParsedDockerComposeFile(file); - Assertions.assertThat(parsedFile.getServiceNameToImageNames()) + Assertions + .assertThat(parsedFile.getServiceNameToImageNames()) .contains( entry("mysql", Sets.newHashSet("mysql")), entry("redis", Sets.newHashSet("redis")), - entry("custom", Sets.newHashSet("postgres"))) + entry("custom", Sets.newHashSet("postgres")) + ) .as("all defined images are found"); // redis, mysql from compose file, postgres from Dockerfile build } @@ -88,11 +79,13 @@ public void shouldObtainImageNamesV1() { public void shouldObtainImageNamesV2() { File file = new File("src/test/resources/docker-compose-imagename-parsing-v2.yml"); ParsedDockerComposeFile parsedFile = new ParsedDockerComposeFile(file); - Assertions.assertThat(parsedFile.getServiceNameToImageNames()) + Assertions + .assertThat(parsedFile.getServiceNameToImageNames()) .contains( entry("mysql", Sets.newHashSet("mysql")), entry("redis", Sets.newHashSet("redis")), - entry("custom", Sets.newHashSet("postgres"))) + entry("custom", Sets.newHashSet("postgres")) + ) .as("all defined images are found"); } @@ -100,11 +93,13 @@ public void shouldObtainImageNamesV2() { public void shouldObtainImageFromDockerfileBuild() { File file = new File("src/test/resources/docker-compose-imagename-parsing-dockerfile.yml"); ParsedDockerComposeFile parsedFile = new ParsedDockerComposeFile(file); - Assertions.assertThat(parsedFile.getServiceNameToImageNames()) + Assertions + .assertThat(parsedFile.getServiceNameToImageNames()) .contains( entry("mysql", Sets.newHashSet("mysql")), entry("redis", Sets.newHashSet("redis")), - entry("custom", Sets.newHashSet("alpine:3.14"))) + entry("custom", Sets.newHashSet("alpine:3.14")) + ) .as("all defined images are found"); // r/ redis, mysql from compose file, alpine:3.14 from Dockerfile build } @@ -112,11 +107,13 @@ public void shouldObtainImageFromDockerfileBuild() { public void shouldObtainImageFromDockerfileBuildWithContext() { File file = new File("src/test/resources/docker-compose-imagename-parsing-dockerfile-with-context.yml"); ParsedDockerComposeFile parsedFile = new ParsedDockerComposeFile(file); - Assertions.assertThat(parsedFile.getServiceNameToImageNames()) + Assertions + .assertThat(parsedFile.getServiceNameToImageNames()) .contains( entry("mysql", Sets.newHashSet("mysql")), entry("redis", Sets.newHashSet("redis")), - entry("custom", Sets.newHashSet("alpine:3.14"))) + entry("custom", Sets.newHashSet("alpine:3.14")) + ) .as("all defined images are found"); // redis, mysql from compose file, alpine:3.14 from Dockerfile build } } diff --git a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java index 9aad97d7e7b..7aafb0149bd 100644 --- a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java +++ b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java @@ -27,9 +27,9 @@ import org.mockito.stubbing.Answer; import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.DockerClientFactory; +import org.testcontainers.TestImages; import org.testcontainers.containers.startupcheck.StartupCheckStrategy; import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy; -import org.testcontainers.images.builder.Transferable; import org.testcontainers.utility.MockTestcontainersConfigurationRule; import org.testcontainers.utility.MountableFile; import org.testcontainers.utility.TestcontainersConfiguration; @@ -53,7 +53,6 @@ import static org.assertj.core.api.Assumptions.assumeThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; -import static org.testcontainers.TestImages.TINY_IMAGE; @RunWith(Enclosed.class) public class ReusabilityUnitTests { @@ -66,8 +65,8 @@ public static class CanBeReusedTest { @Parameterized.Parameters(name = "{0}") public static Object[][] data() { return new Object[][] { - { "generic", new GenericContainer<>(TINY_IMAGE), true }, - { "anonymous generic", new GenericContainer(TINY_IMAGE) {}, true }, + { "generic", new GenericContainer<>(TestImages.TINY_IMAGE), true }, + { "anonymous generic", new GenericContainer(TestImages.TINY_IMAGE) {}, true }, { "custom", new CustomContainer(), true }, { "anonymous custom", new CustomContainer() {}, true }, { "custom with containerIsCreated", new CustomContainerWithContainerIsCreated(), false }, @@ -90,15 +89,17 @@ public void shouldBeReusable() { } static class CustomContainer extends GenericContainer { + CustomContainer() { - super(TINY_IMAGE); + super(TestImages.TINY_IMAGE); } } - static class CustomContainerWithContainerIsCreated extends GenericContainer { + static class CustomContainerWithContainerIsCreated + extends GenericContainer { CustomContainerWithContainerIsCreated() { - super(TINY_IMAGE); + super(TestImages.TINY_IMAGE); } @Override @@ -114,29 +115,30 @@ public static class HooksTest extends AbstractReusabilityTest { List script = new ArrayList<>(); - GenericContainer container = makeReusable(new GenericContainer(TINY_IMAGE) { - - @Override - protected boolean canBeReused() { - // Because we override "containerIsCreated" - return true; - } + GenericContainer container = makeReusable( + new GenericContainer(TestImages.TINY_IMAGE) { + @Override + protected boolean canBeReused() { + // Because we override "containerIsCreated" + return true; + } - @Override - protected void containerIsCreated(String containerId) { - script.add("containerIsCreated"); - } + @Override + protected void containerIsCreated(String containerId) { + script.add("containerIsCreated"); + } - @Override - protected void containerIsStarting(InspectContainerResponse containerInfo, boolean reused) { - script.add("containerIsStarting(reused=" + reused + ")"); - } + @Override + protected void containerIsStarting(InspectContainerResponse containerInfo, boolean reused) { + script.add("containerIsStarting(reused=" + reused + ")"); + } - @Override - protected void containerIsStarted(InspectContainerResponse containerInfo, boolean reused) { - script.add("containerIsStarted(reused=" + reused + ")"); + @Override + protected void containerIsStarted(InspectContainerResponse containerInfo, boolean reused) { + script.add("containerIsStarted(reused=" + reused + ")"); + } } - }); + ); @Test public void shouldSetLabelsIfEnvironmentDoesNotSupportReuse() { @@ -149,11 +151,12 @@ public void shouldSetLabelsIfEnvironmentDoesNotSupportReuse() { when(client.inspectContainerCmd(containerId)).then(inspectContainerAnswer()); container.start(); - assertThat(script).containsExactly( - "containerIsCreated", - "containerIsStarting(reused=false)", - "containerIsStarted(reused=false)" - ); + assertThat(script) + .containsExactly( + "containerIsCreated", + "containerIsStarting(reused=false)", + "containerIsStarted(reused=false)" + ); } @Test @@ -166,10 +169,7 @@ public void shouldCallHookIfReused() { when(client.inspectContainerCmd(existingContainerId)).then(inspectContainerAnswer()); container.start(); - assertThat(script).containsExactly( - "containerIsStarting(reused=true)", - "containerIsStarted(reused=true)" - ); + assertThat(script).containsExactly("containerIsStarting(reused=true)", "containerIsStarted(reused=true)"); } @Test @@ -181,11 +181,12 @@ public void shouldNotCallHookIfNotReused() { when(client.inspectContainerCmd(containerId)).then(inspectContainerAnswer()); container.start(); - assertThat(script).containsExactly( - "containerIsCreated", - "containerIsStarting(reused=false)", - "containerIsStarted(reused=false)" - ); + assertThat(script) + .containsExactly( + "containerIsCreated", + "containerIsStarting(reused=false)", + "containerIsStarted(reused=false)" + ); } } @@ -193,12 +194,14 @@ public void shouldNotCallHookIfNotReused() { @FieldDefaults(makeFinal = true) public static class HashTest extends AbstractReusabilityTest { - protected GenericContainer container = makeReusable(new GenericContainer(TINY_IMAGE) { - @Override - public void copyFileToContainer(MountableFile mountableFile, String containerPath) { - // NOOP + protected GenericContainer container = makeReusable( + new GenericContainer(TestImages.TINY_IMAGE) { + @Override + public void copyFileToContainer(MountableFile mountableFile, String containerPath) { + // NOOP + } } - }); + ); @Test public void shouldStartIfListReturnsEmpty() { @@ -260,8 +263,7 @@ public void shouldSetCopiedFilesHashLabel() { container.start(); assertThat(commandRef).isNotNull(); - assertThat(commandRef.get().getLabels()) - .containsKeys(GenericContainer.COPIED_FILES_HASH_LABEL); + assertThat(commandRef.get().getLabels()).containsKeys(GenericContainer.COPIED_FILES_HASH_LABEL); } @Test @@ -292,15 +294,20 @@ public void shouldHashCopiedFiles() { ); container.start(); - assertThat(commandRef.get().getLabels()).hasEntrySatisfying(GenericContainer.COPIED_FILES_HASH_LABEL, newHash -> { - assertThat(newHash).as("new hash").isNotEqualTo(oldHash); - }); + assertThat(commandRef.get().getLabels()) + .hasEntrySatisfying( + GenericContainer.COPIED_FILES_HASH_LABEL, + newHash -> { + assertThat(newHash).as("new hash").isNotEqualTo(oldHash); + } + ); } } @RunWith(Parameterized.class) @FieldDefaults(makeFinal = true) public static class CopyFilesHashTest { + private final TestStrategy strategy; interface TestStrategy { @@ -310,6 +317,7 @@ interface TestStrategy { } private static class MountableFileTestStrategy implements TestStrategy { + private final GenericContainer container; private MountableFileTestStrategy(GenericContainer container) { @@ -328,6 +336,7 @@ public void clear() { } private static class TransferableTestStrategy implements TestStrategy { + private final GenericContainer container; private TransferableTestStrategy(GenericContainer container) { @@ -350,8 +359,7 @@ public static List, TestStrategy>> strategies() { return Arrays.asList(MountableFileTestStrategy::new, TransferableTestStrategy::new); } - - GenericContainer container = new GenericContainer<>(TINY_IMAGE); + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE); public CopyFilesHashTest(Function, TestStrategy> strategyFactory) { this.strategy = strategyFactory.apply(container); @@ -499,7 +507,7 @@ public void folderPermissions() throws Exception { } @FieldDefaults(makeFinal = true) - public static abstract class AbstractReusabilityTest { + public abstract static class AbstractReusabilityTest { @Rule public MockTestcontainersConfigurationRule configurationMock = new MockTestcontainersConfigurationRule(); @@ -509,25 +517,26 @@ public static abstract class AbstractReusabilityTest { protected > T makeReusable(T container) { container.dockerClient = client; container.withNetworkMode("none"); // to disable the port forwarding - container.withStartupCheckStrategy(new StartupCheckStrategy() { - - @Override - public boolean waitUntilStartupSuccessful(DockerClient dockerClient, String containerId) { - // Skip DockerClient rate limiter - return true; + container.withStartupCheckStrategy( + new StartupCheckStrategy() { + @Override + public boolean waitUntilStartupSuccessful(DockerClient dockerClient, String containerId) { + // Skip DockerClient rate limiter + return true; + } + + @Override + public StartupStatus checkStartupState(DockerClient dockerClient, String containerId) { + return StartupStatus.SUCCESSFUL; + } } - - @Override - public StartupStatus checkStartupState(DockerClient dockerClient, String containerId) { - return StartupStatus.SUCCESSFUL; - } - }); - container.waitingFor(new AbstractWaitStrategy() { - @Override - protected void waitUntilReady() { - + ); + container.waitingFor( + new AbstractWaitStrategy() { + @Override + protected void waitUntilReady() {} } - }); + ); container.withReuse(true); return container; } @@ -539,12 +548,11 @@ protected String randomContainerId() { protected Answer listContainersAnswer(String... ids) { return invocation -> { ListContainersCmd.Exec exec = command -> { - return new ObjectMapper().convertValue( - Stream.of(ids) - .map(id -> Collections.singletonMap("Id", id)) - .collect(Collectors.toList()), - new TypeReference>() {} - ); + return new ObjectMapper() + .convertValue( + Stream.of(ids).map(id -> Collections.singletonMap("Id", id)).collect(Collectors.toList()), + new TypeReference>() {} + ); }; return new ListContainersCmdImpl(exec); }; @@ -554,7 +562,10 @@ protected Answer createContainerAnswer(String containerId) { return createContainerAnswer(containerId, command -> {}); } - protected Answer createContainerAnswer(String containerId, Consumer cmdConsumer) { + protected Answer createContainerAnswer( + String containerId, + Consumer cmdConsumer + ) { return invocation -> { CreateContainerCmd.Exec exec = command -> { cmdConsumer.accept(command); @@ -578,7 +589,10 @@ protected Answer startContainerAnswer() { protected Answer inspectContainerAnswer() { return invocation -> { InspectContainerCmd.Exec exec = command -> { - InspectContainerResponse stubResponse = Mockito.mock(InspectContainerResponse.class, Answers.RETURNS_DEEP_STUBS); + InspectContainerResponse stubResponse = Mockito.mock( + InspectContainerResponse.class, + Answers.RETURNS_DEEP_STUBS + ); when(stubResponse.getNetworkSettings().getPorts().getBindings()).thenReturn(Collections.emptyMap()); return stubResponse; }; diff --git a/core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java b/core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java index 05ef7373293..a860b7802ec 100644 --- a/core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java +++ b/core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java @@ -2,14 +2,12 @@ import org.junit.Ignore; import org.junit.Test; +import org.testcontainers.TestImages; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.TestImages.ALPINE_IMAGE; -import static org.testcontainers.containers.output.OutputFrame.OutputType.STDERR; -import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; public class ContainerLogsTest { @@ -43,7 +41,7 @@ public void getLogsReturnsStdOutToDate() { container.start(); // docsGetStdOut { - final String logs = container.getLogs(STDOUT); + final String logs = container.getLogs(OutputFrame.OutputType.STDOUT); // } assertTrue("stdout is reflected in the returned logs", logs.contains("stdout")); } @@ -55,7 +53,7 @@ public void getLogsReturnsStdErrToDate() { container.start(); // docsGetStdErr { - final String logs = container.getLogs(STDERR); + final String logs = container.getLogs(OutputFrame.OutputType.STDERR); // } assertTrue("stderr is reflected in the returned logs", logs.contains("stderr")); } @@ -68,19 +66,18 @@ public void getLogsForLongRunningContainer() throws InterruptedException { Thread.sleep(1000L); - final String logs = container.getLogs(STDOUT); + final String logs = container.getLogs(OutputFrame.OutputType.STDOUT); assertTrue("stdout is reflected in the returned logs for a running container", logs.contains("seq=0")); } } private static GenericContainer shortLivedContainer() { - return new GenericContainer<>(ALPINE_IMAGE) + return new GenericContainer<>(TestImages.ALPINE_IMAGE) .withCommand("/bin/sh", "-c", "echo -n 'stdout' && echo -n 'stderr' 1>&2") .withStartupCheckStrategy(new OneShotStartupCheckStrategy()); } private static GenericContainer longRunningContainer() { - return new GenericContainer<>(ALPINE_IMAGE) - .withCommand("ping -c 100 127.0.0.1"); + return new GenericContainer<>(TestImages.ALPINE_IMAGE).withCommand("ping -c 100 127.0.0.1"); } } diff --git a/core/src/test/java/org/testcontainers/containers/output/FrameConsumerResultCallbackTest.java b/core/src/test/java/org/testcontainers/containers/output/FrameConsumerResultCallbackTest.java index 325cccdd378..5282712b733 100644 --- a/core/src/test/java/org/testcontainers/containers/output/FrameConsumerResultCallbackTest.java +++ b/core/src/test/java/org/testcontainers/containers/output/FrameConsumerResultCallbackTest.java @@ -12,17 +12,19 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.testcontainers.containers.output.OutputFrame.OutputType; public class FrameConsumerResultCallbackTest { - private static final String FRAME_PAYLOAD = "\u001B[0;32mТест1\u001B[0m\n\u001B[1;33mTest2\u001B[0m\n\u001B[0;31mTest3\u001B[0m"; + + private static final String FRAME_PAYLOAD = + "\u001B[0;32mТест1\u001B[0m\n\u001B[1;33mTest2\u001B[0m\n\u001B[0;31mTest3\u001B[0m"; + private static final String LOG_RESULT = "Тест1\nTest2\nTest3"; @Test public void passStderrFrameWithoutColors() { FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); ToStringConsumer consumer = new ToStringConsumer(); - callback.addConsumer(OutputType.STDERR, consumer); + callback.addConsumer(OutputFrame.OutputType.STDERR, consumer); callback.onNext(new Frame(StreamType.STDERR, FRAME_PAYLOAD.getBytes())); assertEquals(LOG_RESULT, consumer.toUtf8String()); } @@ -31,7 +33,7 @@ public void passStderrFrameWithoutColors() { public void passStderrFrameWithColors() { FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); - callback.addConsumer(OutputType.STDERR, consumer); + callback.addConsumer(OutputFrame.OutputType.STDERR, consumer); callback.onNext(new Frame(StreamType.STDERR, FRAME_PAYLOAD.getBytes())); assertEquals(FRAME_PAYLOAD, consumer.toUtf8String()); } @@ -40,7 +42,7 @@ public void passStderrFrameWithColors() { public void passStdoutFrameWithoutColors() { FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); ToStringConsumer consumer = new ToStringConsumer(); - callback.addConsumer(OutputType.STDOUT, consumer); + callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, FRAME_PAYLOAD.getBytes())); assertEquals(LOG_RESULT, consumer.toUtf8String()); } @@ -49,7 +51,7 @@ public void passStdoutFrameWithoutColors() { public void passStdoutFrameWithColors() { FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); - callback.addConsumer(OutputType.STDOUT, consumer); + callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, FRAME_PAYLOAD.getBytes())); assertEquals(FRAME_PAYLOAD, consumer.toUtf8String()); } @@ -58,7 +60,7 @@ public void passStdoutFrameWithColors() { public void basicConsumer() { FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); BasicConsumer consumer = new BasicConsumer(); - callback.addConsumer(OutputType.STDOUT, consumer); + callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, FRAME_PAYLOAD.getBytes())); assertEquals(LOG_RESULT, consumer.toString()); } @@ -67,7 +69,7 @@ public void basicConsumer() { public void passStdoutNull() { FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); - callback.addConsumer(OutputType.STDOUT, consumer); + callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, null)); assertEquals("", consumer.toUtf8String()); } @@ -77,7 +79,7 @@ public void passStdoutEmptyLine() { String payload = ""; FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); - callback.addConsumer(OutputType.STDOUT, consumer); + callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, payload.getBytes())); assertEquals(payload, consumer.toUtf8String()); } @@ -87,7 +89,7 @@ public void passStdoutSingleLine() { String payload = "Test"; FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); - callback.addConsumer(OutputType.STDOUT, consumer); + callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, payload.getBytes())); assertEquals(payload, consumer.toUtf8String()); } @@ -97,7 +99,7 @@ public void passStdoutSingleLineWithNewline() { String payload = "Test\n"; FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); - callback.addConsumer(OutputType.STDOUT, consumer); + callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, payload.getBytes())); assertEquals(payload, consumer.toUtf8String()); } @@ -106,38 +108,90 @@ public void passStdoutSingleLineWithNewline() { public void passRawFrameWithoutColors() throws TimeoutException, IOException { FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); WaitingConsumer waitConsumer = new WaitingConsumer(); - callback.addConsumer(OutputType.STDOUT, waitConsumer); + callback.addConsumer(OutputFrame.OutputType.STDOUT, waitConsumer); callback.onNext(new Frame(StreamType.RAW, FRAME_PAYLOAD.getBytes())); - waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("Test2"), 1, TimeUnit.SECONDS); - waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("Тест1"), 1, TimeUnit.SECONDS); + waitConsumer.waitUntil( + frame -> frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().equals("Test2"), + 1, + TimeUnit.SECONDS + ); + waitConsumer.waitUntil( + frame -> frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().equals("Тест1"), + 1, + TimeUnit.SECONDS + ); Exception exception = null; try { - waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("Test3"), 1, TimeUnit.SECONDS); + waitConsumer.waitUntil( + frame -> frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().equals("Test3"), + 1, + TimeUnit.SECONDS + ); } catch (Exception e) { exception = e; } assertTrue(exception instanceof TimeoutException); callback.close(); - waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("Test3"), 1, TimeUnit.SECONDS); + waitConsumer.waitUntil( + frame -> frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().equals("Test3"), + 1, + TimeUnit.SECONDS + ); } @Test public void passRawFrameWithColors() throws TimeoutException, IOException { FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); WaitingConsumer waitConsumer = new WaitingConsumer().withRemoveAnsiCodes(false); - callback.addConsumer(OutputType.STDOUT, waitConsumer); + callback.addConsumer(OutputFrame.OutputType.STDOUT, waitConsumer); callback.onNext(new Frame(StreamType.RAW, FRAME_PAYLOAD.getBytes())); - waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("\u001B[1;33mTest2\u001B[0m"), 1, TimeUnit.SECONDS); - waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("\u001B[0;32mТест1\u001B[0m"), 1, TimeUnit.SECONDS); + waitConsumer.waitUntil( + frame -> { + return ( + frame.getType() == OutputFrame.OutputType.STDOUT && + frame.getUtf8String().equals("\u001B[1;33mTest2\u001B[0m") + ); + }, + 1, + TimeUnit.SECONDS + ); + waitConsumer.waitUntil( + frame -> { + return ( + frame.getType() == OutputFrame.OutputType.STDOUT && + frame.getUtf8String().equals("\u001B[0;32mТест1\u001B[0m") + ); + }, + 1, + TimeUnit.SECONDS + ); Exception exception = null; try { - waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("\u001B[0;31mTest3\u001B[0m"), 1, TimeUnit.SECONDS); + waitConsumer.waitUntil( + frame -> { + return ( + frame.getType() == OutputFrame.OutputType.STDOUT && + frame.getUtf8String().equals("\u001B[0;31mTest3\u001B[0m") + ); + }, + 1, + TimeUnit.SECONDS + ); } catch (Exception e) { exception = e; } assertTrue(exception instanceof TimeoutException); callback.close(); - waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("\u001B[0;31mTest3\u001B[0m"), 1, TimeUnit.SECONDS); + waitConsumer.waitUntil( + frame -> { + return ( + frame.getType() == OutputFrame.OutputType.STDOUT && + frame.getUtf8String().equals("\u001B[0;31mTest3\u001B[0m") + ); + }, + 1, + TimeUnit.SECONDS + ); } @Test @@ -150,7 +204,7 @@ public void reconstructBreakedUnicode() throws IOException { System.arraycopy(payloadBytes, bytes1.length, bytes2, 0, bytes2.length); FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); - callback.addConsumer(OutputType.STDOUT, consumer); + callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.RAW, bytes1)); callback.onNext(new Frame(StreamType.RAW, bytes2)); callback.close(); @@ -158,7 +212,9 @@ public void reconstructBreakedUnicode() throws IOException { } private static class BasicConsumer implements Consumer { + private boolean firstLine = true; + private StringBuilder input = new StringBuilder(); @Override diff --git a/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java b/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java index 1760a8031b2..08973f2d601 100644 --- a/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java +++ b/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java @@ -1,7 +1,5 @@ package org.testcontainers.containers.output; -import static org.assertj.core.api.Assertions.assertThat; - import org.apache.commons.lang3.RandomStringUtils; import org.assertj.core.api.Assertions; import org.junit.Test; @@ -9,12 +7,14 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; +import static org.assertj.core.api.Assertions.assertThat; + public class ToStringConsumerTest { private static final String LARGE_PAYLOAD; static { - StringBuilder builder = new StringBuilder(10_003 * 10);; + StringBuilder builder = new StringBuilder(10_003 * 10); for (int i = 0; i < 10; i++) { builder.append(' ').append(i).append(RandomStringUtils.randomAlphabetic(10000)); } @@ -29,9 +29,7 @@ public void newlines_are_not_added_to_exec_output() throws Exception { container.start(); ExecResult build = container.execInContainer("echo", "-n", LARGE_PAYLOAD); - Assertions.assertThat(build.getStdout()) - .doesNotContain("\n") - .isEqualTo(LARGE_PAYLOAD); + Assertions.assertThat(build.getStdout()).doesNotContain("\n").isEqualTo(LARGE_PAYLOAD); } } @@ -39,18 +37,13 @@ public void newlines_are_not_added_to_exec_output() throws Exception { public void newlines_are_not_added_to_exec_output_with_tty() throws Exception { try (GenericContainer container = new GenericContainer<>("alpine:3.14")) { container.withCreateContainerCmdModifier(cmd -> { - cmd - .withAttachStdin(true) - .withStdinOpen(true) - .withTty(true); + cmd.withAttachStdin(true).withStdinOpen(true).withTty(true); }); container.withCommand("sleep", "2m"); container.start(); ExecResult build = container.execInContainer("echo", "-n", LARGE_PAYLOAD); - assertThat(build.getStdout()) - .isEqualTo(LARGE_PAYLOAD) - .doesNotContain("\n"); + assertThat(build.getStdout()).isEqualTo(LARGE_PAYLOAD).doesNotContain("\n"); } } @@ -63,9 +56,7 @@ public void newlines_are_not_added_to_container_output() { container.getDockerClient().waitContainerCmd(container.getContainerId()).start().awaitStatusCode(); - assertThat(container.getLogs()) - .isEqualTo(LARGE_PAYLOAD) - .doesNotContain("\n"); + assertThat(container.getLogs()).isEqualTo(LARGE_PAYLOAD).doesNotContain("\n"); } } @@ -81,9 +72,7 @@ public void newlines_are_not_added_to_container_output_with_tty() { container.getDockerClient().waitContainerCmd(container.getContainerId()).start().awaitStatusCode(); - assertThat(container.getLogs()) - .isEqualTo(LARGE_PAYLOAD) - .doesNotContain("\n"); + assertThat(container.getLogs()).isEqualTo(LARGE_PAYLOAD).doesNotContain("\n"); } } } diff --git a/core/src/test/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheckTest.java b/core/src/test/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheckTest.java index 19f3a772d41..c76d2481c7c 100644 --- a/core/src/test/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheckTest.java +++ b/core/src/test/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheckTest.java @@ -16,8 +16,11 @@ public class ExternalPortListeningCheckTest { private ServerSocket listeningSocket1; + private ServerSocket listeningSocket2; + private ServerSocket nonListeningSocket; + private WaitStrategyTarget mockContainer; @Before @@ -34,8 +37,10 @@ public void setUp() throws Exception { @Test public void singleListening() { - - final ExternalPortListeningCheck check = new ExternalPortListeningCheck(mockContainer, ImmutableSet.of(listeningSocket1.getLocalPort())); + final ExternalPortListeningCheck check = new ExternalPortListeningCheck( + mockContainer, + ImmutableSet.of(listeningSocket1.getLocalPort()) + ); final Boolean result = check.call(); @@ -44,8 +49,10 @@ public void singleListening() { @Test public void multipleListening() { - - final ExternalPortListeningCheck check = new ExternalPortListeningCheck(mockContainer, ImmutableSet.of(listeningSocket1.getLocalPort(), listeningSocket2.getLocalPort())); + final ExternalPortListeningCheck check = new ExternalPortListeningCheck( + mockContainer, + ImmutableSet.of(listeningSocket1.getLocalPort(), listeningSocket2.getLocalPort()) + ); final Boolean result = check.call(); @@ -54,13 +61,16 @@ public void multipleListening() { @Test public void oneNotListening() { - - final ExternalPortListeningCheck check = new ExternalPortListeningCheck(mockContainer, ImmutableSet.of(listeningSocket1.getLocalPort(), nonListeningSocket.getLocalPort())); - - assertThrows("ExternalPortListeningCheck detects a non-listening port among many", - IllegalStateException.class, - (Runnable) check::call); - + final ExternalPortListeningCheck check = new ExternalPortListeningCheck( + mockContainer, + ImmutableSet.of(listeningSocket1.getLocalPort(), nonListeningSocket.getLocalPort()) + ); + + assertThrows( + "ExternalPortListeningCheck detects a non-listening port among many", + IllegalStateException.class, + (Runnable) check::call + ); } @After diff --git a/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java b/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java index 22014cd097a..6f2a5d3158c 100644 --- a/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java +++ b/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java @@ -11,36 +11,41 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.images.builder.ImageFromDockerfile; +import java.util.Arrays; import java.util.concurrent.TimeUnit; -import static java.util.Arrays.asList; - @RunWith(Parameterized.class) public class InternalCommandPortListeningCheckTest { @Parameterized.Parameters(name = "{index} - {0}") public static Iterable data() { - return asList( - new Object[][]{ - {"internal-port-check-dockerfile/Dockerfile-tcp"}, - {"internal-port-check-dockerfile/Dockerfile-nc"}, - {"internal-port-check-dockerfile/Dockerfile-bash"}, - }); + return Arrays.asList( + new Object[][] { + { "internal-port-check-dockerfile/Dockerfile-tcp" }, + { "internal-port-check-dockerfile/Dockerfile-nc" }, + { "internal-port-check-dockerfile/Dockerfile-bash" }, + } + ); } @Rule public GenericContainer container; public InternalCommandPortListeningCheckTest(String dockerfile) { - container = new GenericContainer(new ImageFromDockerfile() - .withFileFromClasspath("Dockerfile", dockerfile) - .withFileFromClasspath("nginx.conf", "internal-port-check-dockerfile/nginx.conf") - ); + container = + new GenericContainer( + new ImageFromDockerfile() + .withFileFromClasspath("Dockerfile", dockerfile) + .withFileFromClasspath("nginx.conf", "internal-port-check-dockerfile/nginx.conf") + ); } @Test public void singleListening() { - final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(container, ImmutableSet.of(8080)); + final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck( + container, + ImmutableSet.of(8080) + ); Unreliables.retryUntilTrue(5, TimeUnit.SECONDS, check); @@ -49,18 +54,23 @@ public void singleListening() { @Test public void nonListening() { - final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(container, ImmutableSet.of(8080, 1234)); + final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck( + container, + ImmutableSet.of(8080, 1234) + ); try { Unreliables.retryUntilTrue(5, TimeUnit.SECONDS, check); VisibleAssertions.fail("expected to fail"); - } catch (TimeoutException e) { - } + } catch (TimeoutException e) {} } @Test public void lowAndHighPortListening() { - final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(container, ImmutableSet.of(100, 8080)); + final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck( + container, + ImmutableSet.of(100, 8080) + ); Unreliables.retryUntilTrue(5, TimeUnit.SECONDS, check); diff --git a/core/src/test/java/org/testcontainers/containers/wait/strategy/DockerHealthcheckWaitStrategyTest.java b/core/src/test/java/org/testcontainers/containers/wait/strategy/DockerHealthcheckWaitStrategyTest.java index d867233f5d7..0d102def5a9 100644 --- a/core/src/test/java/org/testcontainers/containers/wait/strategy/DockerHealthcheckWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/containers/wait/strategy/DockerHealthcheckWaitStrategyTest.java @@ -17,10 +17,16 @@ public class DockerHealthcheckWaitStrategyTest { @Before public void setUp() { // Using a Dockerfile here, since Dockerfile builder DSL doesn't support HEALTHCHECK - container = new GenericContainer(new ImageFromDockerfile() - .withFileFromClasspath("write_file_and_loop.sh", "health-wait-strategy-dockerfile/write_file_and_loop.sh") - .withFileFromClasspath("Dockerfile", "health-wait-strategy-dockerfile/Dockerfile")) - .waitingFor(Wait.forHealthcheck().withStartupTimeout(Duration.ofSeconds(3))); + container = + new GenericContainer( + new ImageFromDockerfile() + .withFileFromClasspath( + "write_file_and_loop.sh", + "health-wait-strategy-dockerfile/write_file_and_loop.sh" + ) + .withFileFromClasspath("Dockerfile", "health-wait-strategy-dockerfile/Dockerfile") + ) + .waitingFor(Wait.forHealthcheck().withStartupTimeout(Duration.ofSeconds(3))); } @Test diff --git a/core/src/test/java/org/testcontainers/containers/wait/strategy/WaitAllStrategyTest.java b/core/src/test/java/org/testcontainers/containers/wait/strategy/WaitAllStrategyTest.java index 897293282bd..2b8ba7c33ec 100644 --- a/core/src/test/java/org/testcontainers/containers/wait/strategy/WaitAllStrategyTest.java +++ b/core/src/test/java/org/testcontainers/containers/wait/strategy/WaitAllStrategyTest.java @@ -19,10 +19,13 @@ public class WaitAllStrategyTest { @Mock private GenericContainer container; + @Mock private WaitStrategy strategy1; + @Mock private WaitStrategy strategy2; + @Mock private WaitStrategy strategy3; @@ -36,32 +39,25 @@ public void setUp() { */ @Test public void parentTimeoutApplies() { - DummyStrategy child1 = new DummyStrategy(Duration.ofMillis(10)); child1.withStartupTimeout(Duration.ofMillis(20)); assertEquals("withStartupTimeout directly sets the timeout", 20L, child1.startupTimeout.toMillis()); - new WaitAllStrategy() - .withStrategy(child1) - .withStartupTimeout(Duration.ofMillis(30)); + new WaitAllStrategy().withStrategy(child1).withStartupTimeout(Duration.ofMillis(30)); assertEquals("WaitAllStrategy overrides a child's timeout", 30L, child1.startupTimeout.toMillis()); } @Test public void parentTimeoutAppliesToMultipleChildren() { - Duration defaultInnerWait = Duration.ofMillis(2); Duration outerWait = Duration.ofMillis(6); DummyStrategy child1 = new DummyStrategy(defaultInnerWait); DummyStrategy child2 = new DummyStrategy(defaultInnerWait); - new WaitAllStrategy() - .withStrategy(child1) - .withStrategy(child2) - .withStartupTimeout(outerWait); + new WaitAllStrategy().withStrategy(child1).withStrategy(child2).withStartupTimeout(outerWait); assertEquals("WaitAllStrategy overrides a child's timeout (1st)", 6L, child1.startupTimeout.toMillis()); assertEquals("WaitAllStrategy overrides a child's timeout (2nd)", 6L, child2.startupTimeout.toMillis()); @@ -69,20 +65,20 @@ public void parentTimeoutAppliesToMultipleChildren() { @Test public void parentTimeoutAppliesToAdditionalChildren() { - Duration defaultInnerWait = Duration.ofMillis(2); Duration outerWait = Duration.ofMillis(20); DummyStrategy child1 = new DummyStrategy(defaultInnerWait); DummyStrategy child2 = new DummyStrategy(defaultInnerWait); - new WaitAllStrategy() - .withStrategy(child1) - .withStartupTimeout(outerWait) - .withStrategy(child2); + new WaitAllStrategy().withStrategy(child1).withStartupTimeout(outerWait).withStrategy(child2); assertEquals("WaitAllStrategy overrides a child's timeout (1st)", 20L, child1.startupTimeout.toMillis()); - assertEquals("WaitAllStrategy overrides a child's timeout (2nd, additional)", 20L, child2.startupTimeout.toMillis()); + assertEquals( + "WaitAllStrategy overrides a child's timeout (2nd, additional)", + 20L, + child2.startupTimeout.toMillis() + ); } /* @@ -90,10 +86,7 @@ public void parentTimeoutAppliesToAdditionalChildren() { */ @Test public void childExecutionTest() { - - final WaitStrategy underTest = new WaitAllStrategy() - .withStrategy(strategy1) - .withStrategy(strategy2); + final WaitStrategy underTest = new WaitAllStrategy().withStrategy(strategy1).withStrategy(strategy2); doNothing().when(strategy1).waitUntilReady(eq(container)); doNothing().when(strategy2).waitUntilReady(eq(container)); @@ -107,7 +100,6 @@ public void childExecutionTest() { @Test public void withoutOuterTimeoutShouldRelyOnInnerStrategies() { - final WaitStrategy underTest = new WaitAllStrategy(WaitAllStrategy.Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY) .withStrategy(strategy1) .withStrategy(strategy2) @@ -116,9 +108,13 @@ public void withoutOuterTimeoutShouldRelyOnInnerStrategies() { doNothing().when(strategy1).waitUntilReady(eq(container)); doThrow(TimeoutException.class).when(strategy2).waitUntilReady(eq(container)); - assertThrows("The outer strategy timeout applies", TimeoutException.class, () -> { - underTest.waitUntilReady(container); - }); + assertThrows( + "The outer strategy timeout applies", + TimeoutException.class, + () -> { + underTest.waitUntilReady(container); + } + ); InOrder inOrder = inOrder(strategy1, strategy2, strategy3); inOrder.verify(strategy1).waitUntilReady(any()); @@ -128,17 +124,19 @@ public void withoutOuterTimeoutShouldRelyOnInnerStrategies() { @Test public void timeoutChangeShouldNotBePossibleWithIndividualTimeoutMode() { - final WaitStrategy underTest = new WaitAllStrategy(WaitAllStrategy.Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY); - assertThrows("Cannot change timeout for individual timeouts", IllegalStateException.class, () -> { - underTest.withStartupTimeout(Duration.ofSeconds(42)); - }); + assertThrows( + "Cannot change timeout for individual timeouts", + IllegalStateException.class, + () -> { + underTest.withStartupTimeout(Duration.ofSeconds(42)); + } + ); } @Test public void shouldNotMessWithIndividualTimeouts() { - new WaitAllStrategy(WaitAllStrategy.Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY) .withStrategy(strategy1) .withStrategy(strategy2); @@ -149,18 +147,15 @@ public void shouldNotMessWithIndividualTimeouts() { @Test public void shouldOverwriteIndividualTimeouts() { - Duration someSeconds = Duration.ofSeconds(23); - new WaitAllStrategy() - .withStartupTimeout(someSeconds) - .withStrategy(strategy1) - .withStrategy(strategy2); + new WaitAllStrategy().withStartupTimeout(someSeconds).withStrategy(strategy1).withStrategy(strategy2); verify(strategy1).withStartupTimeout(someSeconds); verify(strategy1).withStartupTimeout(someSeconds); } static class DummyStrategy extends AbstractWaitStrategy { + DummyStrategy(Duration defaultInnerWait) { super.startupTimeout = defaultInnerWait; } diff --git a/core/src/test/java/org/testcontainers/dockerclient/AmbiguousImagePullTest.java b/core/src/test/java/org/testcontainers/dockerclient/AmbiguousImagePullTest.java index 2e39b367c46..f67ad212d77 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/AmbiguousImagePullTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/AmbiguousImagePullTest.java @@ -14,8 +14,7 @@ public void testNotUsingParse() { DockerImageName imageName = registryContainer.createImage("latest"); String imageNameWithoutTag = imageName.getRegistry() + "/" + imageName.getRepository(); try ( - final GenericContainer container = new GenericContainer<>(imageNameWithoutTag) - .withExposedPorts(8080) + final GenericContainer container = new GenericContainer<>(imageNameWithoutTag).withExposedPorts(8080) ) { container.start(); // do nothing other than start and stop diff --git a/core/src/test/java/org/testcontainers/dockerclient/DockerClientConfigUtilsTest.java b/core/src/test/java/org/testcontainers/dockerclient/DockerClientConfigUtilsTest.java index 442c2efd950..39e1338bc46 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/DockerClientConfigUtilsTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/DockerClientConfigUtilsTest.java @@ -17,29 +17,39 @@ public class DockerClientConfigUtilsTest { @Test public void getDockerHostIpAddressShouldReturnLocalhostWhenUnixSocket() { - Assumptions.assumeThat(DockerClientConfigUtils.IN_A_CONTAINER) - .as("in a container") - .isFalse(); + Assumptions.assumeThat(DockerClientConfigUtils.IN_A_CONTAINER).as("in a container").isFalse(); - String actual = DockerClientProviderStrategy.resolveDockerHostIpAddress(client, URI.create("unix:///var/run/docker.sock")); + String actual = DockerClientProviderStrategy.resolveDockerHostIpAddress( + client, + URI.create("unix:///var/run/docker.sock") + ); assertEquals("localhost", actual); } @Test public void getDockerHostIpAddressShouldReturnDockerHostIpWhenHttpsUri() { - String actual = DockerClientProviderStrategy.resolveDockerHostIpAddress(client, URI.create("http://12.23.34.45")); + String actual = DockerClientProviderStrategy.resolveDockerHostIpAddress( + client, + URI.create("http://12.23.34.45") + ); assertEquals("12.23.34.45", actual); } @Test public void getDockerHostIpAddressShouldReturnDockerHostIpWhenTcpUri() { - String actual = DockerClientProviderStrategy.resolveDockerHostIpAddress(client, URI.create("tcp://12.23.34.45")); + String actual = DockerClientProviderStrategy.resolveDockerHostIpAddress( + client, + URI.create("tcp://12.23.34.45") + ); assertEquals("12.23.34.45", actual); } @Test public void getDockerHostIpAddressShouldReturnNullWhenUnsupportedUriScheme() { - String actual = DockerClientProviderStrategy.resolveDockerHostIpAddress(client, URI.create("gopher://12.23.34.45")); + String actual = DockerClientProviderStrategy.resolveDockerHostIpAddress( + client, + URI.create("gopher://12.23.34.45") + ); assertNull(actual); } diff --git a/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java b/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java index a6bd68be745..b2ab27aad27 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java @@ -20,7 +20,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -35,7 +34,9 @@ public class EnvironmentAndSystemPropertyClientProviderStrategyTest { @Rule public MockTestcontainersConfigurationRule mockConfig = new MockTestcontainersConfigurationRule(); + private URI defaultDockerHost; + private com.github.dockerjava.core.SSLConfig defaultSSLConfig; @Before @@ -48,10 +49,22 @@ public void checkEnvironmentClear() { @Test public void testWhenConfigAbsent() { - Mockito.doReturn("auto").when(TestcontainersConfiguration.getInstance()).getEnvVarOrProperty(eq("dockerconfig.source"), anyString()); - Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.host"), isNull()); - Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.tls.verify"), isNull()); - Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.cert.path"), isNull()); + Mockito + .doReturn("auto") + .when(TestcontainersConfiguration.getInstance()) + .getEnvVarOrProperty(eq("dockerconfig.source"), anyString()); + Mockito + .doReturn(null) + .when(TestcontainersConfiguration.getInstance()) + .getEnvVarOrUserProperty(eq("docker.host"), isNull()); + Mockito + .doReturn(null) + .when(TestcontainersConfiguration.getInstance()) + .getEnvVarOrUserProperty(eq("docker.tls.verify"), isNull()); + Mockito + .doReturn(null) + .when(TestcontainersConfiguration.getInstance()) + .getEnvVarOrUserProperty(eq("docker.cert.path"), isNull()); EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(); @@ -62,10 +75,22 @@ public void testWhenConfigAbsent() { @Test public void testWhenDockerHostPresent() { - Mockito.doReturn("auto").when(TestcontainersConfiguration.getInstance()).getEnvVarOrProperty(eq("dockerconfig.source"), anyString()); - Mockito.doReturn("tcp://1.2.3.4:2375").when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.host"), isNull()); - Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.tls.verify"), isNull()); - Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.cert.path"), isNull()); + Mockito + .doReturn("auto") + .when(TestcontainersConfiguration.getInstance()) + .getEnvVarOrProperty(eq("dockerconfig.source"), anyString()); + Mockito + .doReturn("tcp://1.2.3.4:2375") + .when(TestcontainersConfiguration.getInstance()) + .getEnvVarOrUserProperty(eq("docker.host"), isNull()); + Mockito + .doReturn(null) + .when(TestcontainersConfiguration.getInstance()) + .getEnvVarOrUserProperty(eq("docker.tls.verify"), isNull()); + Mockito + .doReturn(null) + .when(TestcontainersConfiguration.getInstance()) + .getEnvVarOrUserProperty(eq("docker.cert.path"), isNull()); EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(); @@ -79,10 +104,22 @@ public void testWhenDockerHostAndSSLConfigPresent() throws IOException { Path tempDir = Files.createTempDirectory("testcontainers-test"); String tempDirPath = tempDir.toAbsolutePath().toString(); - Mockito.doReturn("auto").when(TestcontainersConfiguration.getInstance()).getEnvVarOrProperty(eq("dockerconfig.source"), anyString()); - Mockito.doReturn("tcp://1.2.3.4:2375").when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.host"), isNull()); - Mockito.doReturn("1").when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.tls.verify"), isNull()); - Mockito.doReturn(tempDirPath).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.cert.path"), isNull()); + Mockito + .doReturn("auto") + .when(TestcontainersConfiguration.getInstance()) + .getEnvVarOrProperty(eq("dockerconfig.source"), anyString()); + Mockito + .doReturn("tcp://1.2.3.4:2375") + .when(TestcontainersConfiguration.getInstance()) + .getEnvVarOrUserProperty(eq("docker.host"), isNull()); + Mockito + .doReturn("1") + .when(TestcontainersConfiguration.getInstance()) + .getEnvVarOrUserProperty(eq("docker.tls.verify"), isNull()); + Mockito + .doReturn(tempDirPath) + .when(TestcontainersConfiguration.getInstance()) + .getEnvVarOrUserProperty(eq("docker.cert.path"), isNull()); EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(); @@ -90,14 +127,15 @@ public void testWhenDockerHostAndSSLConfigPresent() throws IOException { assertEquals("tcp://1.2.3.4:2375", transportConfig.getDockerHost().toString()); SSLConfig sslConfig = transportConfig.getSslConfig(); - Assertions.assertThat(sslConfig) - .extracting("dockerCertPath") - .isEqualTo(tempDirPath); + Assertions.assertThat(sslConfig).extracting("dockerCertPath").isEqualTo(tempDirPath); } @Test public void applicableWhenIgnoringUserPropertiesAndConfigured() { - Mockito.doReturn("autoIgnoringUserProperties").when(TestcontainersConfiguration.getInstance()).getEnvVarOrProperty(eq("dockerconfig.source"), anyString()); + Mockito + .doReturn("autoIgnoringUserProperties") + .when(TestcontainersConfiguration.getInstance()) + .getEnvVarOrProperty(eq("dockerconfig.source"), anyString()); Properties oldProperties = System.getProperties(); try { @@ -112,7 +150,10 @@ public void applicableWhenIgnoringUserPropertiesAndConfigured() { @Test public void notApplicableWhenIgnoringUserPropertiesAndNotConfigured() { - Mockito.doReturn("autoIgnoringUserProperties").when(TestcontainersConfiguration.getInstance()).getEnvVarOrProperty(eq("dockerconfig.source"), anyString()); + Mockito + .doReturn("autoIgnoringUserProperties") + .when(TestcontainersConfiguration.getInstance()) + .getEnvVarOrProperty(eq("dockerconfig.source"), anyString()); EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(); diff --git a/core/src/test/java/org/testcontainers/dockerclient/EventStreamTest.java b/core/src/test/java/org/testcontainers/dockerclient/EventStreamTest.java index a43a001ca5b..642eb6d7390 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/EventStreamTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/EventStreamTest.java @@ -8,6 +8,7 @@ import org.junit.Test; import org.junit.rules.Timeout; import org.testcontainers.DockerClientFactory; +import org.testcontainers.TestImages; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; @@ -16,8 +17,6 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static org.testcontainers.TestImages.TINY_IMAGE; - /** * Test that event streaming from the {@link DockerClient} works correctly */ @@ -34,7 +33,7 @@ public void test() throws IOException, InterruptedException { CountDownLatch latch = new CountDownLatch(1); try ( - GenericContainer container = new GenericContainer<>(TINY_IMAGE) + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withCommand("true") .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) ) { @@ -43,24 +42,31 @@ public void test() throws IOException, InterruptedException { // Request all events between startTime and endTime for the container try ( - EventsResultCallback response = DockerClientFactory.instance().client().eventsCmd() + EventsResultCallback response = DockerClientFactory + .instance() + .client() + .eventsCmd() .withContainerFilter(container.getContainerId()) .withEventFilter("create") .withSince(Instant.parse(createdAt).getEpochSecond() + "") - .exec(new EventsResultCallback() { - @Override - public void onNext(@NotNull Event event) { - // Check that a create event for the container is received - if (event.getId().equals(container.getContainerId()) && event.getStatus().equals("create")) { - latch.countDown(); + .exec( + new EventsResultCallback() { + @Override + public void onNext(@NotNull Event event) { + // Check that a create event for the container is received + if ( + event.getId().equals(container.getContainerId()) && + event.getStatus().equals("create") + ) { + latch.countDown(); + } } } - }) + ) ) { response.awaitStarted(5, TimeUnit.SECONDS); latch.await(5, TimeUnit.SECONDS); } } } - } diff --git a/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java b/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java index 8691e8f5f56..730208c553f 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java @@ -22,7 +22,7 @@ public static String[] parameters() { "quay.io/testcontainers/ryuk:latest", "quay.io/testcontainers/ryuk:0.2.3", "quay.io/testcontainers/ryuk@sha256:bb5a635cac4bd96c93cc476969ce11dc56436238ec7cd028d0524462f4739dd9", -// "ibmcom/db2express-c", // Big image for testing with slow networks + // "ibmcom/db2express-c", // Big image for testing with slow networks }; } @@ -32,9 +32,11 @@ public ImagePullTest(String image) { @Test public void test() { - try (final GenericContainer container = new GenericContainer<>(DockerImageName.parse(image)) - .withCommand("/bin/sh", "-c", "sleep 0") - .withStartupCheckStrategy(new OneShotStartupCheckStrategy())) { + try ( + final GenericContainer container = new GenericContainer<>(DockerImageName.parse(image)) + .withCommand("/bin/sh", "-c", "sleep 0") + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + ) { container.start(); // do nothing other than start and stop } diff --git a/core/src/test/java/org/testcontainers/images/AgeBasedPullPolicyTest.java b/core/src/test/java/org/testcontainers/images/AgeBasedPullPolicyTest.java index e073fa0522a..96fdfa4010b 100644 --- a/core/src/test/java/org/testcontainers/images/AgeBasedPullPolicyTest.java +++ b/core/src/test/java/org/testcontainers/images/AgeBasedPullPolicyTest.java @@ -1,15 +1,15 @@ package org.testcontainers.images; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import org.junit.Test; +import org.testcontainers.utility.DockerImageName; import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.UUID; -import org.junit.Test; -import org.testcontainers.utility.DockerImageName; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; public class AgeBasedPullPolicyTest { @@ -17,9 +17,7 @@ public class AgeBasedPullPolicyTest { @Test public void shouldPull() { - ImageData imageData = ImageData.builder() - .createdAt(Instant.now().minus(2, ChronoUnit.HOURS)) - .build(); + ImageData imageData = ImageData.builder().createdAt(Instant.now().minus(2, ChronoUnit.HOURS)).build(); AgeBasedPullPolicy oneHour = new AgeBasedPullPolicy(Duration.of(1L, ChronoUnit.HOURS)); assertTrue(oneHour.shouldPullCached(imageName, imageData)); diff --git a/core/src/test/java/org/testcontainers/images/ImageDataTest.java b/core/src/test/java/org/testcontainers/images/ImageDataTest.java index 3b473cf7ffd..6ff75e9e355 100644 --- a/core/src/test/java/org/testcontainers/images/ImageDataTest.java +++ b/core/src/test/java/org/testcontainers/images/ImageDataTest.java @@ -1,12 +1,11 @@ package org.testcontainers.images; -import static org.assertj.core.api.Assertions.assertThat; +import com.github.dockerjava.api.command.InspectImageResponse; +import org.junit.Test; import java.time.Instant; -import org.junit.Test; - -import com.github.dockerjava.api.command.InspectImageResponse; +import static org.assertj.core.api.Assertions.assertThat; public class ImageDataTest { @@ -23,5 +22,4 @@ public void shouldReadTimestampWithOffsetFromInspectImageResponse() { final ImageData imageData = ImageData.from(new InspectImageResponse().withCreated(timestamp)); assertThat(imageData.getCreatedAt()).isEqualTo(Instant.parse("2020-07-27T16:23:31.365190246Z")); } - } diff --git a/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java b/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java index 9d5ed400d32..0b0ee5f1b70 100644 --- a/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java +++ b/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java @@ -24,29 +24,20 @@ public class ImagePullPolicyTest { @Test public void pullsByDefault() { - try ( - GenericContainer container = new GenericContainer<>(imageName) - .withExposedPorts(8080) - ) { + try (GenericContainer container = new GenericContainer<>(imageName).withExposedPorts(8080)) { container.start(); } } @Test public void shouldAlwaysPull() { - try ( - GenericContainer container = new GenericContainer<>(imageName) - .withExposedPorts(8080) - ) { + try (GenericContainer container = new GenericContainer<>(imageName).withExposedPorts(8080)) { container.start(); } removeImage(); - try ( - GenericContainer container = new GenericContainer<>(imageName) - .withExposedPorts(8080) - ) { + try (GenericContainer container = new GenericContainer<>(imageName).withExposedPorts(8080)) { expectToFailWithNotFoundException(container); } @@ -66,12 +57,14 @@ public void shouldSupportCustomPolicies() { try ( // custom_image_pull_policy { GenericContainer container = new GenericContainer<>(imageName) - .withImagePullPolicy(new AbstractImagePullPolicy() { - @Override - protected boolean shouldPullCached(DockerImageName imageName, ImageData localImageData) { - return System.getenv("ALWAYS_PULL_IMAGE") != null; + .withImagePullPolicy( + new AbstractImagePullPolicy() { + @Override + protected boolean shouldPullCached(DockerImageName imageName, ImageData localImageData) { + return System.getenv("ALWAYS_PULL_IMAGE") != null; + } } - }) + ) // } ) { container.withExposedPorts(8080); @@ -81,12 +74,14 @@ protected boolean shouldPullCached(DockerImageName imageName, ImageData localIma @Test public void shouldCheckPolicy() { - ImagePullPolicy policy = Mockito.spy(new AbstractImagePullPolicy() { - @Override - protected boolean shouldPullCached(DockerImageName imageName, ImageData localImageData) { - return false; + ImagePullPolicy policy = Mockito.spy( + new AbstractImagePullPolicy() { + @Override + protected boolean shouldPullCached(DockerImageName imageName, ImageData localImageData) { + return false; + } } - }); + ); try ( GenericContainer container = new GenericContainer<>(imageName) .withImagePullPolicy(policy) @@ -128,11 +123,12 @@ private void expectToFailWithNotFoundException(GenericContainer container) { private void removeImage() { try { - DockerClientFactory.instance().client() + DockerClientFactory + .instance() + .client() .removeImageCmd(imageName.asCanonicalNameString()) .withForce(true) .exec(); - } catch (NotFoundException ignored) { - } + } catch (NotFoundException ignored) {} } } diff --git a/core/src/test/java/org/testcontainers/images/ParsedDockerfileTest.java b/core/src/test/java/org/testcontainers/images/ParsedDockerfileTest.java index 52a22c83b00..cd4509965e6 100644 --- a/core/src/test/java/org/testcontainers/images/ParsedDockerfileTest.java +++ b/core/src/test/java/org/testcontainers/images/ParsedDockerfileTest.java @@ -4,69 +4,119 @@ import org.junit.Test; import java.nio.file.Paths; +import java.util.Arrays; -import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; public class ParsedDockerfileTest { @Test public void doesSimpleParsing() { - final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("FROM someimage", "RUN something")); - assertEquals("extracts a single image name", Sets.newHashSet("someimage"), parsedDockerfile.getDependencyImageNames()); + final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( + Arrays.asList("FROM someimage", "RUN something") + ); + assertEquals( + "extracts a single image name", + Sets.newHashSet("someimage"), + parsedDockerfile.getDependencyImageNames() + ); } @Test public void isCaseInsensitive() { - final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("from someimage", "RUN something")); - assertEquals("extracts a single image name", Sets.newHashSet("someimage"), parsedDockerfile.getDependencyImageNames()); + final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( + Arrays.asList("from someimage", "RUN something") + ); + assertEquals( + "extracts a single image name", + Sets.newHashSet("someimage"), + parsedDockerfile.getDependencyImageNames() + ); } @Test public void handlesTags() { - final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("FROM someimage:tag", "RUN something")); - assertEquals("retains tags in image names", Sets.newHashSet("someimage:tag"), parsedDockerfile.getDependencyImageNames()); + final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( + Arrays.asList("FROM someimage:tag", "RUN something") + ); + assertEquals( + "retains tags in image names", + Sets.newHashSet("someimage:tag"), + parsedDockerfile.getDependencyImageNames() + ); } @Test public void handlesDigests() { - final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("FROM someimage@sha256:abc123", "RUN something")); - assertEquals("retains digests in image names", Sets.newHashSet("someimage@sha256:abc123"), parsedDockerfile.getDependencyImageNames()); + final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( + Arrays.asList("FROM someimage@sha256:abc123", "RUN something") + ); + assertEquals( + "retains digests in image names", + Sets.newHashSet("someimage@sha256:abc123"), + parsedDockerfile.getDependencyImageNames() + ); } @Test public void ignoringCommentedFromLines() { - final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("FROM someimage", "#FROM somethingelse")); - assertEquals("ignores commented from lines", Sets.newHashSet("someimage"), parsedDockerfile.getDependencyImageNames()); + final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( + Arrays.asList("FROM someimage", "#FROM somethingelse") + ); + assertEquals( + "ignores commented from lines", + Sets.newHashSet("someimage"), + parsedDockerfile.getDependencyImageNames() + ); } @Test public void ignoringBuildStageNames() { - final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("FROM someimage --as=base", "RUN something", "FROM nextimage", "RUN something")); - assertEquals("ignores build stage names and allows multiple images to be extracted", Sets.newHashSet("someimage", "nextimage"), parsedDockerfile.getDependencyImageNames()); + final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( + Arrays.asList("FROM someimage --as=base", "RUN something", "FROM nextimage", "RUN something") + ); + assertEquals( + "ignores build stage names and allows multiple images to be extracted", + Sets.newHashSet("someimage", "nextimage"), + parsedDockerfile.getDependencyImageNames() + ); } @Test public void ignoringPlatformArgs() { - final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("FROM --platform=linux/amd64 someimage", "RUN something")); - assertEquals("ignores platform args", Sets.newHashSet("someimage"), parsedDockerfile.getDependencyImageNames()); + final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( + Arrays.asList("FROM --platform=linux/amd64 someimage", "RUN something") + ); + assertEquals("ignores platform args", Sets.newHashSet("someimage"), parsedDockerfile.getDependencyImageNames()); } @Test public void ignoringExtraPlatformArgs() { - final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("FROM --platform=linux/amd64 --somethingelse=value someimage", "RUN something")); - assertEquals("ignores platform args", Sets.newHashSet("someimage"), parsedDockerfile.getDependencyImageNames()); + final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( + Arrays.asList("FROM --platform=linux/amd64 --somethingelse=value someimage", "RUN something") + ); + assertEquals("ignores platform args", Sets.newHashSet("someimage"), parsedDockerfile.getDependencyImageNames()); } @Test public void handlesGracefullyIfNoFromLine() { - final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("RUN something", "# is this even a valid Dockerfile?")); - assertEquals("handles invalid Dockerfiles gracefully", Sets.newHashSet(), parsedDockerfile.getDependencyImageNames()); + final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( + Arrays.asList("RUN something", "# is this even a valid Dockerfile?") + ); + assertEquals( + "handles invalid Dockerfiles gracefully", + Sets.newHashSet(), + parsedDockerfile.getDependencyImageNames() + ); } @Test public void handlesGracefullyIfDockerfileNotFound() { final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(Paths.get("nonexistent.Dockerfile")); - assertEquals("handles missing Dockerfiles gracefully", Sets.newHashSet(), parsedDockerfile.getDependencyImageNames()); + assertEquals( + "handles missing Dockerfiles gracefully", + Sets.newHashSet(), + parsedDockerfile.getDependencyImageNames() + ); } } diff --git a/core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java b/core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java index d2ea3e7188a..592d46f33d1 100644 --- a/core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java +++ b/core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java @@ -9,7 +9,7 @@ import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; -import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; @@ -23,7 +23,7 @@ public void toStringContainsOnlyImageName() { } @Test - public void toStringWithExceptionContainsOnlyImageNameFuture() { + public void toStringWithExceptionContainsOnlyImageNameFuture() { CompletableFuture imageNameFuture = new CompletableFuture<>(); imageNameFuture.completeExceptionally(new RuntimeException("arbitrary")); @@ -31,8 +31,8 @@ public void toStringWithExceptionContainsOnlyImageNameFuture() { assertThat(remoteDockerImage.toString(), containsString("imageName=java.lang.RuntimeException: arbitrary")); } - @Test(timeout=5000L) - public void toStringDoesntResolveImageNameFuture() { + @Test(timeout = 5000L) + public void toStringDoesntResolveImageNameFuture() { CompletableFuture imageNameFuture = new CompletableFuture<>(); // verify that we've set up the test properly @@ -49,7 +49,7 @@ public void toStringDoesntResolveImageNameFuture() { assertThat(remoteDockerImage.toString(), containsString("imageName=" + imageName)); } - @Test(timeout=5000L) + @Test(timeout = 5000L) public void toStringDoesntResolveLazyFuture() throws Exception { String imageName = Base58.randomString(8).toLowerCase(); AtomicBoolean resolved = new AtomicBoolean(false); diff --git a/core/src/test/java/org/testcontainers/images/builder/DockerfileBuildTest.java b/core/src/test/java/org/testcontainers/images/builder/DockerfileBuildTest.java index 9a9d647d71b..29cf4fcf6a7 100644 --- a/core/src/test/java/org/testcontainers/images/builder/DockerfileBuildTest.java +++ b/core/src/test/java/org/testcontainers/images/builder/DockerfileBuildTest.java @@ -6,7 +6,6 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; -import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; @@ -18,39 +17,41 @@ public class DockerfileBuildTest { static final Path RESOURCE_PATH = Paths.get("src/test/resources/dockerfile-build-test"); public String expectedFileContent; + public ImageFromDockerfile image; @Parameterized.Parameters public static Object[][] parameters() { - return new Object[][]{ + return new Object[][] { // Dockerfile build without explicit per-file inclusion - new Object[]{"test1234", + new Object[] { + "test1234", + // spotless:off // docsShowRecursiveFileInclusion { new ImageFromDockerfile() - .withFileFromPath(".", RESOURCE_PATH) + .withFileFromPath(".", RESOURCE_PATH), // } + // spotless:on }, - // Dockerfile build using a non-standard Dockerfile - new Object[]{"test4567", - new ImageFromDockerfile() - .withFileFromPath(".", RESOURCE_PATH) - .withDockerfilePath("./Dockerfile-alt") + new Object[] { + "test4567", + new ImageFromDockerfile().withFileFromPath(".", RESOURCE_PATH).withDockerfilePath("./Dockerfile-alt"), }, - // Dockerfile build using build args - new Object[]{"test7890", + new Object[] { + "test7890", new ImageFromDockerfile() .withFileFromPath(".", RESOURCE_PATH) .withDockerfilePath("./Dockerfile-buildarg") - .withBuildArg("CUSTOM_ARG", "test7890") + .withBuildArg("CUSTOM_ARG", "test7890"), }, - - // Dockerfile build using withDockerfile(File) - new Object[]{"test4567", + // Dockerfile build using withDockerfile(File) + new Object[] { + "test4567", new ImageFromDockerfile() .withFileFromPath(".", RESOURCE_PATH) - .withDockerfile(RESOURCE_PATH.resolve("Dockerfile-alt")) + .withDockerfile(RESOURCE_PATH.resolve("Dockerfile-alt")), }, }; } @@ -62,14 +63,18 @@ public DockerfileBuildTest(String expectedFileContent, ImageFromDockerfile image @Test public void performTest() { - try (final GenericContainer container = new GenericContainer(image) - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) - .withCommand("cat", "/test.txt")) { + try ( + final GenericContainer container = new GenericContainer(image) + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .withCommand("cat", "/test.txt") + ) { container.start(); final String logs = container.getLogs(); - assertTrue("expected file content indicates that dockerfile build steps have been run", logs.contains(expectedFileContent)); + assertTrue( + "expected file content indicates that dockerfile build steps have been run", + logs.contains(expectedFileContent) + ); } } - } diff --git a/core/src/test/java/org/testcontainers/images/builder/DockerignoreTest.java b/core/src/test/java/org/testcontainers/images/builder/DockerignoreTest.java index b99a70a7650..513b1ce1784 100644 --- a/core/src/test/java/org/testcontainers/images/builder/DockerignoreTest.java +++ b/core/src/test/java/org/testcontainers/images/builder/DockerignoreTest.java @@ -24,10 +24,10 @@ public void testInvalidDockerignore() throws Exception { .withDockerfile(INVALID_DOCKERIGNORE_PATH.resolve("Dockerfile")) .get(); fail("Should not be able to build an image with an invalid .dockerignore file"); - } - catch (DockerClientException e) { - if (!e.getMessage().contains("Invalid pattern")) + } catch (DockerClientException e) { + if (!e.getMessage().contains("Invalid pattern")) { throw e; + } } } @@ -35,22 +35,24 @@ public void testInvalidDockerignore() throws Exception { @Test public void testValidDockerignore() throws Exception { ImageFromDockerfile img = new ImageFromDockerfile() - .withFileFromPath(".", DockerfileBuildTest.RESOURCE_PATH) - .withDockerfile(DockerfileBuildTest.RESOURCE_PATH.resolve("Dockerfile-currentdir")); - try( + .withFileFromPath(".", DockerfileBuildTest.RESOURCE_PATH) + .withDockerfile(DockerfileBuildTest.RESOURCE_PATH.resolve("Dockerfile-currentdir")); + try ( final GenericContainer container = new GenericContainer(DockerImageName.parse(img.get())) - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) - .withCommand("ls", "/") + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .withCommand("ls", "/") ) { - container.start(); final String logs = container.getLogs(); - assertTrue("Files in the container indicated the .dockerignore was not applied. Output was: " + logs, - logs.contains("should_not_be_ignored.txt")); - assertTrue("Files in the container indicated the .dockerignore was not applied. Output was: " + logs, - !logs.contains("should_be_ignored.txt")); + assertTrue( + "Files in the container indicated the .dockerignore was not applied. Output was: " + logs, + logs.contains("should_not_be_ignored.txt") + ); + assertTrue( + "Files in the container indicated the .dockerignore was not applied. Output was: " + logs, + !logs.contains("should_be_ignored.txt") + ); } } - } diff --git a/core/src/test/java/org/testcontainers/images/builder/ImageFromDockerfileTest.java b/core/src/test/java/org/testcontainers/images/builder/ImageFromDockerfileTest.java index da3509cea7b..abc05eb567c 100644 --- a/core/src/test/java/org/testcontainers/images/builder/ImageFromDockerfileTest.java +++ b/core/src/test/java/org/testcontainers/images/builder/ImageFromDockerfileTest.java @@ -12,8 +12,7 @@ public class ImageFromDockerfileTest { @Test public void shouldAddDefaultLabels() { - ImageFromDockerfile image = new ImageFromDockerfile() - .withDockerfileFromBuilder(it -> it.from("scratch")); + ImageFromDockerfile image = new ImageFromDockerfile().withDockerfileFromBuilder(it -> it.from("scratch")); String imageId = image.resolve(); @@ -27,7 +26,10 @@ public void shouldAddDefaultLabels() { @Test public void shouldNotAddSessionLabelIfDeleteOnExitIsFalse() { - ImageFromDockerfile image = new ImageFromDockerfile("localhost/testcontainers/" + Base58.randomString(16).toLowerCase(), false) + ImageFromDockerfile image = new ImageFromDockerfile( + "localhost/testcontainers/" + Base58.randomString(16).toLowerCase(), + false + ) .withDockerfileFromBuilder(it -> it.from("scratch")); String imageId = image.resolve(); DockerClient dockerClient = DockerClientFactory.instance().client(); @@ -40,7 +42,5 @@ public void shouldNotAddSessionLabelIfDeleteOnExitIsFalse() { // ensure the image is deleted, even if the test fails dockerClient.removeImageCmd(imageId).exec(); } - } - } diff --git a/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/AbstractStatementTest.java b/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/AbstractStatementTest.java index 15c73cadb5f..69d9a1b6840 100644 --- a/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/AbstractStatementTest.java +++ b/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/AbstractStatementTest.java @@ -19,7 +19,6 @@ public abstract class AbstractStatementTest { public TestName testName = new TestName(); protected void assertStatement(Statement statement) { - String[] expectedLines = new String[0]; try { String path = "fixtures/statements/" + getClass().getSimpleName() + "/" + testName.getMethodName(); @@ -39,7 +38,9 @@ protected void assertStatement(Statement statement) { String[] resultLines = StringUtils.chomp(builder.toString().trim()).split("\n"); if (expectedLines.length != resultLines.length) { - fail("number of lines is not the same. Expected " + expectedLines.length + " but got " + resultLines.length); + fail( + "number of lines is not the same. Expected " + expectedLines.length + " but got " + resultLines.length + ); } if (!Arrays.equals(expectedLines, resultLines)) { @@ -59,7 +60,6 @@ protected void assertStatement(Statement statement) { failureBuilder.append(expectedLine); failureBuilder.append(">\n"); } - } fail(failureBuilder.toString()); diff --git a/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/KeyValuesStatementTest.java b/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/KeyValuesStatementTest.java index 6add9d462f2..f446e48b42c 100644 --- a/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/KeyValuesStatementTest.java +++ b/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/KeyValuesStatementTest.java @@ -9,11 +9,12 @@ public class KeyValuesStatementTest extends AbstractStatementTest { @Test public void multilineTest() throws Exception { - ImmutableMap pairs = ImmutableMap.builder() - .put("line1", "1") - .put("line2", "2") - .put("line3", "3") - .build(); + ImmutableMap pairs = ImmutableMap + .builder() + .put("line1", "1") + .put("line2", "2") + .put("line3", "3") + .build(); assertStatement(new KeyValuesStatement("TEST", pairs)); } @@ -35,12 +36,13 @@ public void keyWithTabsTest() throws Exception { @Test public void valueIsEscapedTest() throws Exception { - ImmutableMap pairs = ImmutableMap.builder() - .put("1", "value with spaces") - .put("2", "value\nwith\nnewlines") - .put("3", "value\twith\ttab") - .build(); + ImmutableMap pairs = ImmutableMap + .builder() + .put("1", "value with spaces") + .put("2", "value\nwith\nnewlines") + .put("3", "value\twith\ttab") + .build(); assertStatement(new KeyValuesStatement("TEST", pairs)); } -} \ No newline at end of file +} diff --git a/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/MultiArgsStatementTest.java b/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/MultiArgsStatementTest.java index 3b36af8861e..50bc8bedd96 100644 --- a/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/MultiArgsStatementTest.java +++ b/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/MultiArgsStatementTest.java @@ -13,4 +13,4 @@ public void simpleTest() throws Exception { public void multilineTest() throws Exception { assertStatement(new MultiArgsStatement("TEST", "some\nmultiline\nargument")); } -} \ No newline at end of file +} diff --git a/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/RawStatementTest.java b/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/RawStatementTest.java index a600451bd71..70724a03f09 100644 --- a/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/RawStatementTest.java +++ b/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/RawStatementTest.java @@ -8,5 +8,4 @@ public class RawStatementTest extends AbstractStatementTest { public void simpleTest() throws Exception { assertStatement(new RawStatement("TEST", "value\nas\t\\\nis")); } - -} \ No newline at end of file +} diff --git a/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/SingleArgumentStatementTest.java b/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/SingleArgumentStatementTest.java index 75d290b240d..dbdabab9fff 100644 --- a/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/SingleArgumentStatementTest.java +++ b/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/SingleArgumentStatementTest.java @@ -13,4 +13,4 @@ public void simpleTest() throws Exception { public void multilineTest() throws Exception { assertStatement(new SingleArgumentStatement("TEST", "hello\nworld")); } -} \ No newline at end of file +} diff --git a/core/src/test/java/org/testcontainers/junit/BaseDockerComposeTest.java b/core/src/test/java/org/testcontainers/junit/BaseDockerComposeTest.java index 7f8b5d79953..dd62ace1fac 100644 --- a/core/src/test/java/org/testcontainers/junit/BaseDockerComposeTest.java +++ b/core/src/test/java/org/testcontainers/junit/BaseDockerComposeTest.java @@ -15,7 +15,7 @@ import java.util.List; import java.util.stream.Collectors; -import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.is; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; @@ -37,7 +37,10 @@ public static void checkVersion() { @Test public void simpleTest() { - Jedis jedis = new Jedis(getEnvironment().getServiceHost("redis_1", REDIS_PORT), getEnvironment().getServicePort("redis_1", REDIS_PORT)); + Jedis jedis = new Jedis( + getEnvironment().getServiceHost("redis_1", REDIS_PORT), + getEnvironment().getServicePort("redis_1", REDIS_PORT) + ); jedis.incr("test"); jedis.incr("test"); @@ -49,7 +52,10 @@ public void simpleTest() { @Test public void secondTest() { // used in manual checking for cleanup in between tests - Jedis jedis = new Jedis(getEnvironment().getServiceHost("redis_1", REDIS_PORT), getEnvironment().getServicePort("redis_1", REDIS_PORT)); + Jedis jedis = new Jedis( + getEnvironment().getServiceHost("redis_1", REDIS_PORT), + getEnvironment().getServicePort("redis_1", REDIS_PORT) + ); jedis.incr("test"); jedis.incr("test"); @@ -62,18 +68,23 @@ public void secondTest() { @Before public void captureNetworks() { - existingNetworks.addAll(findAllNetworks()); + existingNetworks.addAll(findAllNetworks()); } @After public void verifyNoNetworks() { - assertThat("The networks", findAllNetworks(), is(existingNetworks)); + assertThat("The networks", findAllNetworks(), is(existingNetworks)); } private List findAllNetworks() { - return DockerClientFactory.instance().client().listNetworksCmd().exec().stream() - .map(Network::getName) - .sorted() - .collect(Collectors.toList()); + return DockerClientFactory + .instance() + .client() + .listNetworksCmd() + .exec() + .stream() + .map(Network::getName) + .sorted() + .collect(Collectors.toList()); } } diff --git a/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java b/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java index b0ed90d9c9e..7041e74912a 100644 --- a/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java @@ -1,6 +1,7 @@ package org.testcontainers.junit; import org.junit.Test; +import org.testcontainers.TestImages; import org.testcontainers.containers.BindMode; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.SelinuxContext; @@ -11,16 +12,17 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.TestImages.TINY_IMAGE; public class CopyFileToContainerTest { + private static String containerPath = "/tmp/mappable-resource/"; + private static String fileName = "test-resource.txt"; @Test public void checkFileCopied() throws IOException, InterruptedException { try ( - GenericContainer container = new GenericContainer<>(TINY_IMAGE) + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withCommand("sleep", "3000") .withCopyFileToContainer(MountableFile.forClasspathResource("/mappable-resource/"), containerPath) ) { @@ -33,7 +35,7 @@ public void checkFileCopied() throws IOException, InterruptedException { @Test public void shouldUseCopyForReadOnlyClasspathResources() throws Exception { try ( - GenericContainer container = new GenericContainer<>(TINY_IMAGE) + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withCommand("sleep", "3000") .withClasspathResourceMapping("/mappable-resource/", containerPath, BindMode.READ_ONLY) ) { @@ -46,10 +48,9 @@ public void shouldUseCopyForReadOnlyClasspathResources() throws Exception { @Test public void shouldUseCopyOnlyWithReadOnlyClasspathResources() { String resource = "/test_copy_to_container.txt"; - GenericContainer container = new GenericContainer<>(TINY_IMAGE) + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withClasspathResourceMapping(resource, "/readOnly", BindMode.READ_ONLY) .withClasspathResourceMapping(resource, "/readOnlyNoSelinux", BindMode.READ_ONLY) - .withClasspathResourceMapping(resource, "/readOnlyShared", BindMode.READ_ONLY, SelinuxContext.SHARED) .withClasspathResourceMapping(resource, "/readWrite", BindMode.READ_WRITE); @@ -65,7 +66,7 @@ public void shouldUseCopyOnlyWithReadOnlyClasspathResources() { public void shouldCreateFoldersStructureWithCopy() throws Exception { String resource = "/test_copy_to_container.txt"; try ( - GenericContainer container = new GenericContainer<>(TINY_IMAGE) + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withCommand("sleep", "3000") .withClasspathResourceMapping(resource, "/a/b/c/file", BindMode.READ_ONLY) ) { diff --git a/core/src/test/java/org/testcontainers/junit/DependenciesTest.java b/core/src/test/java/org/testcontainers/junit/DependenciesTest.java index bd82c58a7f7..78e55335d9a 100644 --- a/core/src/test/java/org/testcontainers/junit/DependenciesTest.java +++ b/core/src/test/java/org/testcontainers/junit/DependenciesTest.java @@ -3,6 +3,7 @@ import lombok.Getter; import org.junit.Test; import org.rnorth.visibleassertions.VisibleAssertions; +import org.testcontainers.TestImages; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import org.testcontainers.lifecycle.Startable; @@ -16,8 +17,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.testcontainers.TestImages.TINY_IMAGE; - public class DependenciesTest { @Test @@ -25,7 +24,7 @@ public void shouldWorkWithSimpleDependency() { InvocationCountingStartable startable = new InvocationCountingStartable(); try ( - GenericContainer container = new GenericContainer<>(TINY_IMAGE) + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) .dependsOn(startable) ) { @@ -42,7 +41,7 @@ public void shouldWorkWithMutlipleDependencies() { InvocationCountingStartable startable2 = new InvocationCountingStartable(); try ( - GenericContainer container = new GenericContainer<>(TINY_IMAGE) + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) .dependsOn(startable1, startable2) ) { @@ -58,7 +57,7 @@ public void shouldStartEveryTime() { InvocationCountingStartable startable = new InvocationCountingStartable(); try ( - GenericContainer container = new GenericContainer<>(TINY_IMAGE) + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) .dependsOn(startable) ) { @@ -85,7 +84,7 @@ public void shouldStartTransitiveDependencies() { startable.getDependencies().add(transitiveStartable); try ( - GenericContainer container = new GenericContainer<>(TINY_IMAGE) + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) .dependsOn(startable) ) { @@ -94,8 +93,16 @@ public void shouldStartTransitiveDependencies() { } VisibleAssertions.assertEquals("Root started", 1, startable.getStartInvocationCount().intValue()); - VisibleAssertions.assertEquals("Transitive started", 1, transitiveStartable.getStartInvocationCount().intValue()); - VisibleAssertions.assertEquals("Transitive of transitive started", 1, transitiveOfTransitiveStartable.getStartInvocationCount().intValue()); + VisibleAssertions.assertEquals( + "Transitive started", + 1, + transitiveStartable.getStartInvocationCount().intValue() + ); + VisibleAssertions.assertEquals( + "Transitive of transitive started", + 1, + transitiveOfTransitiveStartable.getStartInvocationCount().intValue() + ); } @Test @@ -123,7 +130,8 @@ public void shouldHandleDiamondDependencies() throws Exception { @Test public void shouldHandleParallelStream() throws Exception { - List startables = Stream.generate(InvocationCountingStartable::new) + List startables = Stream + .generate(InvocationCountingStartable::new) .limit(10) .collect(Collectors.toList()); diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerScalingTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerScalingTest.java index a44d8de49f4..651696696c7 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerScalingTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerScalingTest.java @@ -24,25 +24,26 @@ public static void checkVersion() { } @Rule - public DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/scaled-compose-test.yml")) - .withScaledService("redis", 3) - .withExposedService("redis", REDIS_PORT) // implicit '_1' - .withExposedService("redis_2", REDIS_PORT) // explicit service index - .withExposedService("redis", 3, REDIS_PORT); // explicit service index via parameter + public DockerComposeContainer environment = new DockerComposeContainer( + new File("src/test/resources/scaled-compose-test.yml") + ) + .withScaledService("redis", 3) + .withExposedService("redis", REDIS_PORT) // implicit '_1' + .withExposedService("redis_2", REDIS_PORT) // explicit service index + .withExposedService("redis", 3, REDIS_PORT); // explicit service index via parameter @Before public void setupClients() { for (int i = 0; i < 3; i++) { - String name = String.format("redis_%d", i + 1); - clients[i] = new Jedis(environment.getServiceHost(name, REDIS_PORT), environment.getServicePort(name, REDIS_PORT)); + clients[i] = + new Jedis(environment.getServiceHost(name, REDIS_PORT), environment.getServicePort(name, REDIS_PORT)); } } @Test public void simpleTest() { - for (int i = 0; i < 3; i++) { clients[i].incr("somekey"); diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java index 17a9b665513..c49663a4e35 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java @@ -9,10 +9,9 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.util.Collections; import java.util.Optional; -import static java.lang.String.format; -import static java.util.Collections.singletonList; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; @@ -24,7 +23,9 @@ public class DockerComposeContainerTest extends BaseDockerComposeTest { @Rule - public DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/compose-test.yml")) + public DockerComposeContainer environment = new DockerComposeContainer( + new File("src/test/resources/compose-test.yml") + ) .withExposedService("redis_1", REDIS_PORT) .withExposedService("db_1", 3306); @@ -46,34 +47,46 @@ public void testGetServicePort() { public void shouldRetrieveContainerByServiceName() { String existingServiceName = "db_1"; Optional result = environment.getContainerByServiceName(existingServiceName); - assertTrue(format("Container should be found by service name %s", existingServiceName), result.isPresent()); - assertEquals("Mapped port for result container was wrong, probably wrong container found", result.get().getExposedPorts(), singletonList(3306)); + assertTrue( + String.format("Container should be found by service name %s", existingServiceName), + result.isPresent() + ); + assertEquals( + "Mapped port for result container was wrong, probably wrong container found", + result.get().getExposedPorts(), + Collections.singletonList(3306) + ); } @Test public void shouldReturnEmptyResultOnNoneExistingService() { String notExistingServiceName = "db_256"; Optional result = environment.getContainerByServiceName(notExistingServiceName); - assertFalse(format("No container should be found under service name %s", notExistingServiceName), result.isPresent()); + assertFalse( + String.format("No container should be found under service name %s", notExistingServiceName), + result.isPresent() + ); } @Test public void shouldCreateContainerWhenFileNotPrefixedWithPath() throws IOException { String validYaml = "version: '2.2'\n" + - "services:\n" + - " http:\n" + - " build: .\n" + - " image: python:latest\n" + - " ports:\n" + - " - 8080:8080"; + "services:\n" + + " http:\n" + + " build: .\n" + + " image: python:latest\n" + + " ports:\n" + + " - 8080:8080"; File filePathNotStartWithDotSlash = new File("docker-compose-test.yml"); filePathNotStartWithDotSlash.createNewFile(); filePathNotStartWithDotSlash.deleteOnExit(); Files.write(filePathNotStartWithDotSlash.toPath(), validYaml.getBytes(StandardCharsets.UTF_8)); - final DockerComposeContainer dockerComposeContainer = new DockerComposeContainer<>(filePathNotStartWithDotSlash); + final DockerComposeContainer dockerComposeContainer = new DockerComposeContainer<>( + filePathNotStartWithDotSlash + ); assertNotNull("Container could not be created using docker compose file", dockerComposeContainer); } } diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithBuildTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithBuildTest.java index 1b1efc878af..e0705a2c73b 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithBuildTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithBuildTest.java @@ -14,30 +14,32 @@ import java.util.stream.Stream; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.testcontainers.containers.DockerComposeContainer.RemoveImages.ALL; -import static org.testcontainers.containers.DockerComposeContainer.RemoveImages.LOCAL; @RunWith(Parameterized.class) public class DockerComposeContainerWithBuildTest { - public DockerComposeContainerWithBuildTest(final DockerComposeContainer.RemoveImages removeMode, - final boolean shouldBuiltImageBePresentAfterRunning, - final boolean shouldPulledImageBePresentAfterRunning) { + public DockerComposeContainerWithBuildTest( + final DockerComposeContainer.RemoveImages removeMode, + final boolean shouldBuiltImageBePresentAfterRunning, + final boolean shouldPulledImageBePresentAfterRunning + ) { this.removeMode = removeMode; this.shouldBuiltImageBePresentAfterRunning = shouldBuiltImageBePresentAfterRunning; this.shouldPulledImageBePresentAfterRunning = shouldPulledImageBePresentAfterRunning; } public final DockerComposeContainer.RemoveImages removeMode; + public final boolean shouldBuiltImageBePresentAfterRunning; + public final boolean shouldPulledImageBePresentAfterRunning; @Parameterized.Parameters(name = "removeMode = {0}") public static Object[][] params() { - return new Object[][]{ - {null, true, true}, - {LOCAL, false, true}, - {ALL, false, false}, + return new Object[][] { + { null, true, true }, + { DockerComposeContainer.RemoveImages.LOCAL, false, true }, + { DockerComposeContainer.RemoveImages.ALL, false, false }, }; } @@ -47,11 +49,12 @@ public void performTest() { final AtomicReference builtImageName = new AtomicReference<>(""); final AtomicReference pulledImageName = new AtomicReference<>(""); - try (DockerComposeContainer environment = new DockerComposeContainer<>(composeFile) - .withExposedService("customredis", 6379) - .withBuild(true) - .withRemoveImages(removeMode)) { - + try ( + DockerComposeContainer environment = new DockerComposeContainer<>(composeFile) + .withExposedService("customredis", 6379) + .withBuild(true) + .withRemoveImages(removeMode) + ) { environment.start(); builtImageName.set(imageNameForRunningContainer("_customredis_1")); @@ -63,21 +66,42 @@ public void performTest() { assertEquals("the pulled image is present while running", true, isPulledImagePresentWhileRunning); } - Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, () -> { - final boolean isBuiltImagePresentAfterRunning = isImagePresent(builtImageName.get()); - assertEquals("the built image is not present after running", shouldBuiltImageBePresentAfterRunning, isBuiltImagePresentAfterRunning); - return null; - }); - - Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, () -> { - final boolean isPulledImagePresentAfterRunning = isImagePresent(pulledImageName.get()); - assertEquals("the pulled image is present after running", shouldPulledImageBePresentAfterRunning, isPulledImagePresentAfterRunning); - return null; - }); + Unreliables.retryUntilSuccess( + 10, + TimeUnit.SECONDS, + () -> { + final boolean isBuiltImagePresentAfterRunning = isImagePresent(builtImageName.get()); + assertEquals( + "the built image is not present after running", + shouldBuiltImageBePresentAfterRunning, + isBuiltImagePresentAfterRunning + ); + return null; + } + ); + + Unreliables.retryUntilSuccess( + 10, + TimeUnit.SECONDS, + () -> { + final boolean isPulledImagePresentAfterRunning = isImagePresent(pulledImageName.get()); + assertEquals( + "the pulled image is present after running", + shouldPulledImageBePresentAfterRunning, + isPulledImagePresentAfterRunning + ); + return null; + } + ); } private String imageNameForRunningContainer(final String containerNameSuffix) { - return DockerClientFactory.instance().client().listContainersCmd().exec().stream() + return DockerClientFactory + .instance() + .client() + .listContainersCmd() + .exec() + .stream() .filter(it -> Stream.of(it.getNames()).anyMatch(name -> name.endsWith(containerNameSuffix))) .findFirst() .map(Container::getImage) @@ -85,7 +109,13 @@ private String imageNameForRunningContainer(final String containerNameSuffix) { } private boolean isImagePresent(final String imageName) { - return DockerClientFactory.instance().client().listImagesCmd().withImageNameFilter(imageName).exec().stream() + return DockerClientFactory + .instance() + .client() + .listImagesCmd() + .withImageNameFilter(imageName) + .exec() + .stream() .findFirst() .isPresent(); } diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithOptionsTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithOptionsTest.java index 375c9baa070..77e60580b39 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithOptionsTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithOptionsTest.java @@ -17,7 +17,12 @@ @RunWith(Parameterized.class) public class DockerComposeContainerWithOptionsTest { - public DockerComposeContainerWithOptionsTest(final File composeFile, final boolean local, final Set options, final boolean expectError) { + public DockerComposeContainerWithOptionsTest( + final File composeFile, + final boolean local, + final Set options, + final boolean expectError + ) { this.composeFile = composeFile; this.local = local; this.options = options; @@ -25,38 +30,60 @@ public DockerComposeContainerWithOptionsTest(final File composeFile, final boole } private final File composeFile; + private final boolean local; private final Set options; - private final boolean expectError; - @Parameterized.Parameters(name = "docker-compose test [compose file: {0}, local: {1}, options: {2}, expected result: {3}]") + private final boolean expectError; + @Parameterized.Parameters( + name = "docker-compose test [compose file: {0}, local: {1}, options: {2}, expected result: {3}]" + ) public static Object[][] params() { - return new Object[][]{ + return new Object[][] { // Test the happy day case. THe compatibility option should be accepted by docker-compose. - {new File("src/test/resources/compose-options-test/with-deploy-block.yml"), false, ImmutableSet.of("--compatibility"), false}, + { + new File("src/test/resources/compose-options-test/with-deploy-block.yml"), + false, + ImmutableSet.of("--compatibility"), + false, + }, // Test with flags absent. Docker compose will warn but continue, ignoring the deploy block. - {new File("src/test/resources/compose-options-test/with-deploy-block.yml"), false, ImmutableSet.of(""), false}, + { + new File("src/test/resources/compose-options-test/with-deploy-block.yml"), + false, + ImmutableSet.of(""), + false, + }, // Test with a bad option. Compose will complain. - {new File("src/test/resources/compose-options-test/with-deploy-block.yml"), false, ImmutableSet.of("--bad-option"), true}, + { + new File("src/test/resources/compose-options-test/with-deploy-block.yml"), + false, + ImmutableSet.of("--bad-option"), + true, + }, // Local compose - {new File("src/test/resources/compose-options-test/with-deploy-block.yml"), true, ImmutableSet.of("--compatibility"), false}, + { + new File("src/test/resources/compose-options-test/with-deploy-block.yml"), + true, + ImmutableSet.of("--compatibility"), + false, + }, }; } @Test public void performTest() { - - try (DockerComposeContainer environment = new DockerComposeContainer<>(composeFile) + try ( + DockerComposeContainer environment = new DockerComposeContainer<>(composeFile) .withOptions(options.stream().toArray(String[]::new)) - .withLocalCompose(local)) { + .withLocalCompose(local) + ) { environment.start(); assertThat(expectError).isEqualTo(false); } catch (Exception e) { assertThat(expectError).isEqualTo(true); } - } - } diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeErrorHandlingTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeErrorHandlingTest.java index b8e26ffd3f1..51f09bdb095 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeErrorHandlingTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeErrorHandlingTest.java @@ -10,11 +10,15 @@ public class DockerComposeErrorHandlingTest { @Test public void simpleTest() { - VisibleAssertions.assertThrows("starting with an invalid docker-compose file throws an exception", + VisibleAssertions.assertThrows( + "starting with an invalid docker-compose file throws an exception", IllegalArgumentException.class, - () -> { - DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/invalid-compose.yml")) - .withExposedService("something", 123); - }); + () -> { + DockerComposeContainer environment = new DockerComposeContainer( + new File("src/test/resources/invalid-compose.yml") + ) + .withExposedService("something", 123); + } + ); } } diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeLocalImageTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeLocalImageTest.java index 890fa34b0f8..dd2054e1e2e 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeLocalImageTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeLocalImageTest.java @@ -14,7 +14,9 @@ public class DockerComposeLocalImageTest { public void usesLocalImageEvenWhenPullFails() throws InterruptedException { tagImage("redis:4.0.10", "redis-local", "latest"); - DockerComposeContainer composeContainer = new DockerComposeContainer(new File("src/test/resources/local-compose-test.yml")) + DockerComposeContainer composeContainer = new DockerComposeContainer( + new File("src/test/resources/local-compose-test.yml") + ) .withExposedService("redis", 6379); composeContainer.start(); } diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeLogConsumerTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeLogConsumerTest.java index 6cd90d3cd9c..97e686a9b92 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeLogConsumerTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeLogConsumerTest.java @@ -3,26 +3,36 @@ import org.junit.Test; import org.junit.runner.Description; import org.testcontainers.containers.DockerComposeContainer; +import org.testcontainers.containers.output.OutputFrame.OutputType; import org.testcontainers.containers.output.WaitingConsumer; import java.io.File; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; - public class DockerComposeLogConsumerTest { @Test public void testLogConsumer() throws TimeoutException { WaitingConsumer logConsumer = new WaitingConsumer(); - DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/v2-compose-test.yml")) + DockerComposeContainer environment = new DockerComposeContainer( + new File("src/test/resources/v2-compose-test.yml") + ) .withExposedService("redis_1", 6379) .withLogConsumer("redis_1", logConsumer); try { environment.starting(Description.EMPTY); - logConsumer.waitUntil(frame -> frame.getType() == STDOUT && frame.getUtf8String().contains("Ready to accept connections"), 5, TimeUnit.SECONDS); + logConsumer.waitUntil( + frame -> { + return ( + frame.getType() == OutputType.STDOUT && + frame.getUtf8String().contains("Ready to accept connections") + ); + }, + 5, + TimeUnit.SECONDS + ); } finally { environment.finished(Description.EMPTY); } diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposePassthroughTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposePassthroughTest.java index cdeea00d201..c026bcaa741 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposePassthroughTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposePassthroughTest.java @@ -13,7 +13,7 @@ import java.util.Arrays; import java.util.Objects; -import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.Matchers.hasItem; import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; @@ -30,25 +30,27 @@ public static void checkVersion() { } @Rule - public DockerComposeContainer compose = - new DockerComposeContainer(new File("src/test/resources/v2-compose-test-passthrough.yml")) - .withEnv("foo", "bar") - .withExposedService("alpine_1", 3000, waitStrategy); - + public DockerComposeContainer compose = new DockerComposeContainer( + new File("src/test/resources/v2-compose-test-passthrough.yml") + ) + .withEnv("foo", "bar") + .withExposedService("alpine_1", 3000, waitStrategy); @Test public void testContainerInstanceProperties() { final ContainerState container = waitStrategy.getContainer(); //check environment variable was set - assertThat("Environment variable set correctly", Arrays.asList(Objects.requireNonNull(container.getContainerInfo() - .getConfig().getEnv())), hasItem("bar=bar")); + assertThat( + "Environment variable set correctly", + Arrays.asList(Objects.requireNonNull(container.getContainerInfo().getConfig().getEnv())), + hasItem("bar=bar") + ); //check other container properties assertNotNull("Container id is not null", container.getContainerId()); assertNotNull("Port mapped", container.getMappedPort(3000)); assertThat("Exposed Ports", container.getExposedPorts(), hasItem(3000)); - } /* diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeServiceTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeServiceTest.java index ec8c50d42ab..5e6ba4cc30f 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeServiceTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeServiceTest.java @@ -4,17 +4,18 @@ import org.junit.Test; import org.testcontainers.containers.DockerComposeContainer; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; - import java.io.File; +import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; + public class DockerComposeServiceTest extends BaseDockerComposeTest { @Rule - public DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/compose-test.yml")) - .withServices("redis") - .withExposedService("redis_1", REDIS_PORT); - + public DockerComposeContainer environment = new DockerComposeContainer( + new File("src/test/resources/compose-test.yml") + ) + .withServices("redis") + .withExposedService("redis_1", REDIS_PORT); @Override protected DockerComposeContainer getEnvironment() { diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeV2FormatTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeV2FormatTest.java index bed19bd0d09..f3dc2ee3cc8 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeV2FormatTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeV2FormatTest.java @@ -11,8 +11,10 @@ public class DockerComposeV2FormatTest extends BaseDockerComposeTest { @Rule - public DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/v2-compose-test.yml")) - .withExposedService("redis_1", REDIS_PORT); + public DockerComposeContainer environment = new DockerComposeContainer( + new File("src/test/resources/v2-compose-test.yml") + ) + .withExposedService("redis_1", REDIS_PORT); @Override protected DockerComposeContainer getEnvironment() { diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeV2WithNetworkTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeV2WithNetworkTest.java index 26764476687..b28dfc3ee51 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeV2WithNetworkTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeV2WithNetworkTest.java @@ -1,21 +1,20 @@ package org.testcontainers.junit; -import java.io.File; - import org.junit.Rule; import org.testcontainers.containers.DockerComposeContainer; +import java.io.File; + public class DockerComposeV2WithNetworkTest extends BaseDockerComposeTest { @Rule - public DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/v2-compose-test-with-network.yml")) - .withExposedService("redis_1", REDIS_PORT); + public DockerComposeContainer environment = new DockerComposeContainer( + new File("src/test/resources/v2-compose-test-with-network.yml") + ) + .withExposedService("redis_1", REDIS_PORT); @Override protected DockerComposeContainer getEnvironment() { return environment; } - - - } diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeWaitStrategyTest.java index 32465010700..a30c716964e 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeWaitStrategyTest.java @@ -1,10 +1,5 @@ package org.testcontainers.junit; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.catchThrowable; - -import java.io.File; -import java.time.Duration; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -15,6 +10,12 @@ import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.containers.wait.strategy.WaitStrategy; +import java.io.File; +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; + public class DockerComposeWaitStrategyTest { private static final int REDIS_PORT = 6379; @@ -23,8 +24,7 @@ public class DockerComposeWaitStrategyTest { @Before public final void setUp() { - environment = new DockerComposeContainer<>( - new File("src/test/resources/compose-test.yml")); + environment = new DockerComposeContainer<>(new File("src/test/resources/compose-test.yml")); } @After @@ -34,14 +34,15 @@ public final void cleanUp() { @Test public void testWaitOnListeningPort() { - environment - .withExposedService("redis_1", REDIS_PORT, Wait.forListeningPort()); + environment.withExposedService("redis_1", REDIS_PORT, Wait.forListeningPort()); try { environment.starting(Description.createTestDescription(Object.class, "name")); VisibleAssertions.pass("Docker compose should start after waiting for listening port"); } catch (RuntimeException e) { - VisibleAssertions.fail("Docker compose should start after waiting for listening port with failed with: " + e); + VisibleAssertions.fail( + "Docker compose should start after waiting for listening port with failed with: " + e + ); } } @@ -56,29 +57,45 @@ public void testWaitOnMultipleStrategiesPassing() { environment.starting(Description.createTestDescription(Object.class, "name")); VisibleAssertions.pass("Docker compose should start after waiting for listening port"); } catch (RuntimeException e) { - VisibleAssertions.fail("Docker compose should start after waiting for listening port with failed with: " + e); + VisibleAssertions.fail( + "Docker compose should start after waiting for listening port with failed with: " + e + ); } } @Test public void testWaitingFails() { - environment - .withExposedService("redis_1", REDIS_PORT, Wait.forHttp("/test").withStartupTimeout(Duration.ofSeconds(10))); - VisibleAssertions.assertThrows("waiting on an invalid http path times out", + environment.withExposedService( + "redis_1", + REDIS_PORT, + Wait.forHttp("/test").withStartupTimeout(Duration.ofSeconds(10)) + ); + VisibleAssertions.assertThrows( + "waiting on an invalid http path times out", RuntimeException.class, - () -> environment.starting(Description.createTestDescription(Object.class, "name"))); + () -> environment.starting(Description.createTestDescription(Object.class, "name")) + ); } @Test public void testWaitOnOneOfMultipleStrategiesFailing() { environment - .withExposedService("redis_1", REDIS_PORT, Wait.forListeningPort().withStartupTimeout(Duration.ofSeconds(10))) - .waitingFor("db_1", Wait.forLogMessage(".*test test test.*\\s", 1).withStartupTimeout(Duration.ofSeconds(10))) + .withExposedService( + "redis_1", + REDIS_PORT, + Wait.forListeningPort().withStartupTimeout(Duration.ofSeconds(10)) + ) + .waitingFor( + "db_1", + Wait.forLogMessage(".*test test test.*\\s", 1).withStartupTimeout(Duration.ofSeconds(10)) + ) .withTailChildContainers(true); - VisibleAssertions.assertThrows("waiting on one failing strategy to time out", + VisibleAssertions.assertThrows( + "waiting on one failing strategy to time out", RuntimeException.class, - () -> environment.starting(Description.createTestDescription(Object.class, "name"))); + () -> environment.starting(Description.createTestDescription(Object.class, "name")) + ); } @Test @@ -93,14 +110,11 @@ public void testWaitingForNonexistentServices() { .waitingFor(nonexistentServiceName1, someWaitStrategy) .waitingFor(nonexistentServiceName2, someWaitStrategy); - Throwable thrownWhenRequestedToWaitForNonexistentService = - catchThrowable(environment::start); + Throwable thrownWhenRequestedToWaitForNonexistentService = catchThrowable(environment::start); assertThat(thrownWhenRequestedToWaitForNonexistentService) .isInstanceOf(IllegalStateException.class) - .hasMessageContaining( - nonexistentServiceName1, - nonexistentServiceName2) + .hasMessageContaining(nonexistentServiceName1, nonexistentServiceName2) .hasMessageNotContaining(existentServiceName); } } diff --git a/core/src/test/java/org/testcontainers/junit/DockerNetworkModeTest.java b/core/src/test/java/org/testcontainers/junit/DockerNetworkModeTest.java index 59c2a63127c..7962b87e157 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerNetworkModeTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerNetworkModeTest.java @@ -3,12 +3,12 @@ import com.github.dockerjava.api.model.NetworkSettings; import lombok.extern.slf4j.Slf4j; import org.junit.Test; +import org.testcontainers.TestImages; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.TestImages.TINY_IMAGE; /** * Simple tests of named network modes - more may be possible, but may not be reproducible @@ -20,7 +20,7 @@ public class DockerNetworkModeTest { @Test public void testNoNetworkContainer() { try ( - GenericContainer container = new GenericContainer<>(TINY_IMAGE) + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) .withCommand("true") .withNetworkMode("none") @@ -36,7 +36,7 @@ public void testNoNetworkContainer() { @Test public void testHostNetworkContainer() { try ( - GenericContainer container = new GenericContainer<>(TINY_IMAGE) + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) .withCommand("true") .withNetworkMode("host") diff --git a/core/src/test/java/org/testcontainers/junit/DockerfileContainerTest.java b/core/src/test/java/org/testcontainers/junit/DockerfileContainerTest.java index 6ad7c3ea26e..715ba3f64a1 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerfileContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerfileContainerTest.java @@ -21,13 +21,16 @@ public class DockerfileContainerTest { @Rule public GenericContainer dslContainer = new GenericContainer( - new ImageFromDockerfile("tcdockerfile/nginx", false).withDockerfileFromBuilder(builder -> { - builder - .from("alpine:3.2") - .run("apk add --update nginx") - .cmd("nginx", "-g", "daemon off;") - .build(); })) - .withExposedPorts(80); + new ImageFromDockerfile("tcdockerfile/nginx", false) + .withDockerfileFromBuilder(builder -> { + builder + .from("alpine:3.2") // + .run("apk add --update nginx") + .cmd("nginx", "-g", "daemon off;") + .build(); + }) + ) + .withExposedPorts(80); @Test public void simpleDslTest() throws IOException { @@ -37,11 +40,15 @@ public void simpleDslTest() throws IOException { HttpGet get = new HttpGet(address); try (CloseableHttpResponse response = httpClient.execute(get)) { - assertEquals("A container built from a dockerfile can run nginx as expected, and returns a good status code", - 200, - response.getStatusLine().getStatusCode()); - assertTrue("A container built from a dockerfile can run nginx as expected, and returns an expected Server header", - response.getHeaders("Server")[0].getValue().contains("nginx")); + assertEquals( + "A container built from a dockerfile can run nginx as expected, and returns a good status code", + 200, + response.getStatusLine().getStatusCode() + ); + assertTrue( + "A container built from a dockerfile can run nginx as expected, and returns an expected Server header", + response.getHeaders("Server")[0].getValue().contains("nginx") + ); } } } diff --git a/core/src/test/java/org/testcontainers/junit/DockerfileTest.java b/core/src/test/java/org/testcontainers/junit/DockerfileTest.java index 2197d7be0e7..e121e7db5a4 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerfileTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerfileTest.java @@ -5,6 +5,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.output.WaitingConsumer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import org.testcontainers.images.builder.ImageFromDockerfile; @@ -16,7 +17,6 @@ import java.util.concurrent.TimeoutException; import static org.rnorth.visibleassertions.VisibleAssertions.pass; -import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; public class DockerfileTest { @@ -25,9 +25,9 @@ public class DockerfileTest { @Test public void simpleDockerfileWorks() { ImageFromDockerfile image = new ImageFromDockerfile() - .withFileFromString("folder/someFile.txt", "hello") - .withFileFromClasspath("test.txt", "mappable-resource/test-resource.txt") - .withFileFromClasspath("Dockerfile", "mappable-dockerfile/Dockerfile"); + .withFileFromString("folder/someFile.txt", "hello") + .withFileFromClasspath("test.txt", "mappable-resource/test-resource.txt") + .withFileFromClasspath("Dockerfile", "mappable-dockerfile/Dockerfile"); verifyImage(image); } @@ -40,9 +40,9 @@ protected void configure(BuildImageCmd buildImageCmd) { super.configure(buildImageCmd); List dockerfile = Arrays.asList( - "FROM alpine:3.14", - "RUN echo 'hello from Docker build process'", - "CMD yes" + "FROM alpine:3.14", + "RUN echo 'hello from Docker build process'", + "CMD yes" ); withFileFromString("Dockerfile", String.join("\n", dockerfile)); @@ -56,28 +56,30 @@ protected void configure(BuildImageCmd buildImageCmd) { @Test public void dockerfileBuilderWorks() { ImageFromDockerfile image = new ImageFromDockerfile() - .withFileFromClasspath("test.txt", "mappable-resource/test-resource.txt") - .withFileFromString("folder/someFile.txt", "hello") - .withDockerfileFromBuilder(builder -> builder - .from("alpine:3.14") - .workDir("/app") - .add("test.txt", "test file.txt") - .run("ls", "-la", "/app/test file.txt") - .copy("folder/someFile.txt", "/someFile.txt") - .expose(80, 8080) - .cmd("while true; do cat /someFile.txt | nc -l -p 80; done") - ); + .withFileFromClasspath("test.txt", "mappable-resource/test-resource.txt") + .withFileFromString("folder/someFile.txt", "hello") + .withDockerfileFromBuilder(builder -> { + builder + .from("alpine:3.14") + .workDir("/app") + .add("test.txt", "test file.txt") + .run("ls", "-la", "/app/test file.txt") + .copy("folder/someFile.txt", "/someFile.txt") + .expose(80, 8080) + .cmd("while true; do cat /someFile.txt | nc -l -p 80; done"); + }); verifyImage(image); } @Test public void filePermissions() throws TimeoutException { - WaitingConsumer consumer = new WaitingConsumer(); ImageFromDockerfile image = new ImageFromDockerfile() - .withFileFromTransferable("/someFile.txt", new Transferable() { + .withFileFromTransferable( + "/someFile.txt", + new Transferable() { @Override public long getSize() { return 0; @@ -97,23 +99,27 @@ public String getDescription() { public int getFileMode() { return 0123; } - - - }) - .withDockerfileFromBuilder(builder -> builder - .from("alpine:3.14") - .copy("someFile.txt", "/someFile.txt") - .cmd("stat -c \"%a\" /someFile.txt") - ); + } + ) + .withDockerfileFromBuilder(builder -> { + builder + .from("alpine:3.14") // + .copy("someFile.txt", "/someFile.txt") + .cmd("stat -c \"%a\" /someFile.txt"); + }); GenericContainer container = new GenericContainer(image) - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) - .withLogConsumer(consumer); + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .withLogConsumer(consumer); try { container.start(); - consumer.waitUntil(frame -> frame.getType() == STDOUT && frame.getUtf8String().contains("123"), 5, TimeUnit.SECONDS); + consumer.waitUntil( + frame -> frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().contains("123"), + 5, + TimeUnit.SECONDS + ); } finally { container.stop(); } diff --git a/core/src/test/java/org/testcontainers/junit/FileOperationsTest.java b/core/src/test/java/org/testcontainers/junit/FileOperationsTest.java index beca4a8a49f..e61721f80c4 100644 --- a/core/src/test/java/org/testcontainers/junit/FileOperationsTest.java +++ b/core/src/test/java/org/testcontainers/junit/FileOperationsTest.java @@ -7,6 +7,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.testcontainers.TestImages; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import org.testcontainers.utility.MountableFile; @@ -16,7 +17,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.TestImages.ALPINE_IMAGE; public class FileOperationsTest { @@ -26,7 +26,7 @@ public class FileOperationsTest { @Test public void copyFileToContainerFileTest() throws Exception { try ( - GenericContainer alpineCopyToContainer = new GenericContainer(ALPINE_IMAGE) + GenericContainer alpineCopyToContainer = new GenericContainer(TestImages.ALPINE_IMAGE) // .withCommand("top") ) { alpineCopyToContainer.start(); @@ -44,7 +44,7 @@ public void copyFileToContainerFileTest() throws Exception { @Test public void copyFileToContainerFolderTest() throws Exception { try ( - GenericContainer alpineCopyToContainer = new GenericContainer(ALPINE_IMAGE) + GenericContainer alpineCopyToContainer = new GenericContainer(TestImages.ALPINE_IMAGE) // .withCommand("top") ) { alpineCopyToContainer.start(); @@ -62,10 +62,9 @@ public void copyFileToContainerFolderTest() throws Exception { @Test public void copyFolderToContainerFolderTest() throws Exception { try ( - GenericContainer alpineCopyToContainer = new GenericContainer(ALPINE_IMAGE) + GenericContainer alpineCopyToContainer = new GenericContainer(TestImages.ALPINE_IMAGE) // .withCommand("top") ) { - alpineCopyToContainer.start(); final MountableFile mountableFile = MountableFile.forClasspathResource("mappable-resource/"); alpineCopyToContainer.copyFileToContainer(mountableFile, "/home/test/"); @@ -81,7 +80,7 @@ public void copyFolderToContainerFolderTest() throws Exception { @Test(expected = NotFoundException.class) public void copyFromContainerShouldFailBecauseNoFileTest() throws NotFoundException { try ( - GenericContainer alpineCopyToContainer = new GenericContainer(ALPINE_IMAGE) + GenericContainer alpineCopyToContainer = new GenericContainer(TestImages.ALPINE_IMAGE) // .withCommand("top") ) { alpineCopyToContainer.start(); @@ -92,10 +91,9 @@ public void copyFromContainerShouldFailBecauseNoFileTest() throws NotFoundExcept @Test public void shouldCopyFileFromContainerTest() throws IOException { try ( - GenericContainer alpineCopyToContainer = new GenericContainer(ALPINE_IMAGE) + GenericContainer alpineCopyToContainer = new GenericContainer(TestImages.ALPINE_IMAGE) // .withCommand("top") ) { - alpineCopyToContainer.start(); final MountableFile mountableFile = MountableFile.forClasspathResource("test_copy_to_container.txt"); alpineCopyToContainer.copyFileToContainer(mountableFile, "/home/"); @@ -110,7 +108,7 @@ public void shouldCopyFileFromContainerTest() throws IOException { @Test public void copyFileOperationsShouldFailWhenNotStartedTest() { - try (GenericContainer container = new GenericContainer<>(ALPINE_IMAGE).withCommand("top")) { + try (GenericContainer container = new GenericContainer<>(TestImages.ALPINE_IMAGE).withCommand("top")) { Assertions .assertThatThrownBy(() -> { MountableFile mountableFile = MountableFile.forClasspathResource("test_copy_to_container.txt"); @@ -121,10 +119,7 @@ public void copyFileOperationsShouldFailWhenNotStartedTest() { Assertions .assertThatThrownBy(() -> { - container.copyFileFromContainer( - "/home/test_copy_to_container.txt", - IOUtils::toByteArray - ); + container.copyFileFromContainer("/home/test_copy_to_container.txt", IOUtils::toByteArray); }) .isInstanceOf(IllegalStateException.class) .hasMessageContaining("can only be used when the Container is created"); @@ -134,18 +129,17 @@ public void copyFileOperationsShouldFailWhenNotStartedTest() { @Test public void shouldCopyFileFromExitedContainerTest() throws IOException { try ( - GenericContainer container = new GenericContainer<>(ALPINE_IMAGE) + GenericContainer container = new GenericContainer<>(TestImages.ALPINE_IMAGE) .withCommand("sh", "-c", "echo -n 'Hello!' > /home/file_in_container.txt") .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) ) { container.start(); - assertThat(container.getDockerClient().waitContainerCmd(container.getContainerId()).start().awaitStatusCode()) + assertThat( + container.getDockerClient().waitContainerCmd(container.getContainerId()).start().awaitStatusCode() + ) .isEqualTo(0); - container.copyFileFromContainer( - "/home/file_in_container.txt", - IOUtils::toByteArray - ); + container.copyFileFromContainer("/home/file_in_container.txt", IOUtils::toByteArray); container.copyFileToContainer( MountableFile.forClasspathResource("test_copy_to_container.txt"), diff --git a/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java b/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java index 02fad53fb1e..a6bb13d160c 100644 --- a/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java @@ -3,6 +3,7 @@ import com.google.common.util.concurrent.Uninterruptibles; import org.junit.Test; import org.rnorth.ducttape.unreliables.Unreliables; +import org.testcontainers.TestImages; import org.testcontainers.containers.FixedHostPortGenericContainer; import org.testcontainers.containers.GenericContainer; @@ -12,9 +13,8 @@ import java.net.Socket; import java.util.concurrent.TimeUnit; -import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.testcontainers.TestImages.TINY_IMAGE; +import static org.hamcrest.Matchers.equalTo; /** * Test of {@link FixedHostPortGenericContainer}. Note that this is not an example of typical use (usually, a container @@ -39,15 +39,18 @@ public class FixedHostPortContainerTest { /** * *nix pipe to fire test response on test port */ - private static final String HTTP_ECHO_CMD = - String.format("while true; do echo \"%s\" | nc -l -p %d; done", TEST_RESPONSE, TEST_PORT); + private static final String HTTP_ECHO_CMD = String.format( + "while true; do echo \"%s\" | nc -l -p %d; done", + TEST_RESPONSE, + TEST_PORT + ); @Test public void testFixedHostPortMapping() throws IOException { // first find a free port on the docker host that will work for testing final Integer unusedHostPort; try ( - final GenericContainer echoServer = new GenericContainer(TINY_IMAGE) + final GenericContainer echoServer = new GenericContainer(TestImages.TINY_IMAGE) .withExposedPorts(TEST_PORT) .withCommand("/bin/sh", "-c", HTTP_ECHO_CMD) ) { @@ -58,15 +61,18 @@ public void testFixedHostPortMapping() throws IOException { // now starting echo server container mapped to known-as-free host port try ( final GenericContainer echoServer = new FixedHostPortGenericContainer(TEST_IMAGE) - // using workaround for port bind+expose - .withFixedExposedPort(unusedHostPort, TEST_PORT) - .withExposedPorts(TEST_PORT) - .withCommand("/bin/sh", "-c", HTTP_ECHO_CMD) + // using workaround for port bind+expose + .withFixedExposedPort(unusedHostPort, TEST_PORT) + .withExposedPorts(TEST_PORT) + .withCommand("/bin/sh", "-c", HTTP_ECHO_CMD) ) { echoServer.start(); - assertThat("Port mapping does not seem to match given fixed port", - echoServer.getMappedPort(TEST_PORT), equalTo(unusedHostPort)); + assertThat( + "Port mapping does not seem to match given fixed port", + echoServer.getMappedPort(TEST_PORT), + equalTo(unusedHostPort) + ); final String content = this.readResponse(echoServer, unusedHostPort); assertThat("Returned echo from fixed port does not match expected", content, equalTo(TEST_RESPONSE)); @@ -83,7 +89,9 @@ public void testFixedHostPortMapping() throws IOException { */ private String readResponse(GenericContainer container, Integer port) throws IOException { try ( - final BufferedReader reader = Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, + final BufferedReader reader = Unreliables.retryUntilSuccess( + 10, + TimeUnit.SECONDS, () -> { Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); final Socket socket = new Socket(container.getHost(), port); diff --git a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java index c13421029e0..8c696585902 100644 --- a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java +++ b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java @@ -21,8 +21,11 @@ import org.junit.Test; import org.rnorth.ducttape.RetryCountExceededException; import org.rnorth.ducttape.unreliables.Unreliables; +import org.testcontainers.TestImages; +import org.testcontainers.containers.BindMode; import org.testcontainers.containers.Container; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.SelinuxContext; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import org.testcontainers.utility.Base58; import org.testcontainers.utility.TestEnvironment; @@ -37,27 +40,19 @@ import java.net.Socket; import java.time.Duration; import java.util.Arrays; +import java.util.Collections; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.regex.Matcher; import java.util.regex.Pattern; -import static java.util.Collections.singletonMap; -import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.Matchers.equalTo; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.TestImages.ALPINE_IMAGE; -import static org.testcontainers.TestImages.MONGODB_IMAGE; -import static org.testcontainers.TestImages.RABBITMQ_IMAGE; -import static org.testcontainers.TestImages.REDIS_IMAGE; -import static org.testcontainers.TestImages.TINY_IMAGE; -import static org.testcontainers.containers.BindMode.READ_ONLY; -import static org.testcontainers.containers.BindMode.READ_WRITE; -import static org.testcontainers.containers.SelinuxContext.SHARED; /** * Tests for GenericContainerRules @@ -65,12 +60,16 @@ public class GenericContainerRuleTest { private static final int REDIS_PORT = 6379; + private static final String RABBIQMQ_TEST_EXCHANGE = "TestExchange"; + private static final String RABBITMQ_TEST_ROUTING_KEY = "TestRoutingKey"; + private static final String RABBITMQ_TEST_MESSAGE = "Hello world"; + private static final int RABBITMQ_PORT = 5672; - private static final int MONGO_PORT = 27017; + private static final int MONGO_PORT = 27017; /* * Test data setup @@ -86,77 +85,79 @@ public static void setupContent() throws FileNotFoundException { * Redis */ @ClassRule - public static GenericContainer redis = new GenericContainer<>(REDIS_IMAGE) - .withExposedPorts(REDIS_PORT); + public static GenericContainer redis = new GenericContainer<>(TestImages.REDIS_IMAGE) + .withExposedPorts(REDIS_PORT); /** * RabbitMQ */ @ClassRule - public static GenericContainer rabbitMq = new GenericContainer<>(RABBITMQ_IMAGE) - .withExposedPorts(RABBITMQ_PORT); + public static GenericContainer rabbitMq = new GenericContainer<>(TestImages.RABBITMQ_IMAGE) + .withExposedPorts(RABBITMQ_PORT); + /** * MongoDB */ @ClassRule - public static GenericContainer mongo = new GenericContainer<>(MONGODB_IMAGE) - .withExposedPorts(MONGO_PORT); + public static GenericContainer mongo = new GenericContainer<>(TestImages.MONGODB_IMAGE) + .withExposedPorts(MONGO_PORT); + /** * Pass an environment variable to the container, then run a shell script that exposes the variable in a quick and * dirty way for testing. */ @ClassRule - public static GenericContainer alpineEnvVar = new GenericContainer<>(ALPINE_IMAGE) - .withExposedPorts(80) - .withEnv("MAGIC_NUMBER", "4") - .withEnv("MAGIC_NUMBER", oldValue -> oldValue.orElse("") + "2") - .withCommand("/bin/sh", "-c", "while true; do echo \"$MAGIC_NUMBER\" | nc -l -p 80; done"); + public static GenericContainer alpineEnvVar = new GenericContainer<>(TestImages.ALPINE_IMAGE) + .withExposedPorts(80) + .withEnv("MAGIC_NUMBER", "4") + .withEnv("MAGIC_NUMBER", oldValue -> oldValue.orElse("") + "2") + .withCommand("/bin/sh", "-c", "while true; do echo \"$MAGIC_NUMBER\" | nc -l -p 80; done"); /** * Pass environment variables to the container, then run a shell script that exposes the variables in a quick and * dirty way for testing. */ @ClassRule - public static GenericContainer alpineEnvVarFromMap = new GenericContainer<>(ALPINE_IMAGE) - .withExposedPorts(80) - .withEnv(ImmutableMap.of( - "FIRST", "42", - "SECOND", "50" - )) - .withCommand("/bin/sh", "-c", "while true; do echo \"$FIRST and $SECOND\" | nc -l -p 80; done"); + public static GenericContainer alpineEnvVarFromMap = new GenericContainer<>(TestImages.ALPINE_IMAGE) + .withExposedPorts(80) + .withEnv(ImmutableMap.of("FIRST", "42", "SECOND", "50")) + .withCommand("/bin/sh", "-c", "while true; do echo \"$FIRST and $SECOND\" | nc -l -p 80; done"); /** * Map a file on the classpath to a file in the container, and then expose the content for testing. */ @ClassRule - public static GenericContainer alpineClasspathResource = new GenericContainer<>(ALPINE_IMAGE) - .withExposedPorts(80) - .withClasspathResourceMapping("mappable-resource/test-resource.txt", "/content.txt", READ_ONLY) - .withCommand("/bin/sh", "-c", "while true; do cat /content.txt | nc -l -p 80; done"); + public static GenericContainer alpineClasspathResource = new GenericContainer<>(TestImages.ALPINE_IMAGE) + .withExposedPorts(80) + .withClasspathResourceMapping("mappable-resource/test-resource.txt", "/content.txt", BindMode.READ_ONLY) + .withCommand("/bin/sh", "-c", "while true; do cat /content.txt | nc -l -p 80; done"); /** * Map a file on the classpath to a file in the container, and then expose the content for testing. */ @ClassRule - public static GenericContainer alpineClasspathResourceSelinux = new GenericContainer<>(ALPINE_IMAGE) - .withExposedPorts(80) - .withClasspathResourceMapping("mappable-resource/test-resource.txt", "/content.txt", READ_WRITE, SHARED) - .withCommand("/bin/sh", "-c", "while true; do cat /content.txt | nc -l -p 80; done"); + public static GenericContainer alpineClasspathResourceSelinux = new GenericContainer<>(TestImages.ALPINE_IMAGE) + .withExposedPorts(80) + .withClasspathResourceMapping( + "mappable-resource/test-resource.txt", + "/content.txt", + BindMode.READ_WRITE, + SelinuxContext.SHARED + ) + .withCommand("/bin/sh", "-c", "while true; do cat /content.txt | nc -l -p 80; done"); /** * Create a container with an extra host entry and expose the content of /etc/hosts for testing. */ @ClassRule - public static GenericContainer alpineExtrahost = new GenericContainer<>(ALPINE_IMAGE) - .withExposedPorts(80) - .withExtraHost("somehost", "192.168.1.10") - .withCommand("/bin/sh", "-c", "while true; do cat /etc/hosts | nc -l -p 80; done"); + public static GenericContainer alpineExtrahost = new GenericContainer<>(TestImages.ALPINE_IMAGE) + .withExposedPorts(80) + .withExtraHost("somehost", "192.168.1.10") + .withCommand("/bin/sh", "-c", "while true; do cat /etc/hosts | nc -l -p 80; done"); @Test public void testIsRunning() { - try ( - GenericContainer container = new GenericContainer<>(TINY_IMAGE) - .withCommand("top")) { + try (GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE).withCommand("top")) { assertFalse("Container is not started and not running", container.isRunning()); container.start(); assertTrue("Container is started and running", container.isRunning()); @@ -166,16 +167,19 @@ public void testIsRunning() { @Test public void withTmpFsTest() throws Exception { try ( - GenericContainer container = new GenericContainer<>(TINY_IMAGE) + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withCommand("top") - .withTmpFs(singletonMap("/testtmpfs", "rw")) + .withTmpFs(Collections.singletonMap("/testtmpfs", "rw")) ) { container.start(); // check file doesn't exist String path = "/testtmpfs/test.file"; Container.ExecResult execResult = container.execInContainer("ls", path); - assertEquals("tmpfs inside container works fine", execResult.getStderr(), - "ls: /testtmpfs/test.file: No such file or directory\n"); + assertEquals( + "tmpfs inside container works fine", + execResult.getStderr(), + "ls: /testtmpfs/test.file: No such file or directory\n" + ); // touch && check file does exist container.execInContainer("touch", path); execResult = container.execInContainer("ls", path); @@ -183,7 +187,6 @@ public void withTmpFsTest() throws Exception { } } - @Test public void simpleRabbitMqTest() throws IOException, TimeoutException { ConnectionFactory factory = new ConnectionFactory(); @@ -198,23 +201,39 @@ public void simpleRabbitMqTest() throws IOException, TimeoutException { // Set up a consumer on the queue final boolean[] messageWasReceived = new boolean[1]; - channel.basicConsume(queueName, false, new DefaultConsumer(channel) { - @Override - public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { - messageWasReceived[0] = Arrays.equals(body, RABBITMQ_TEST_MESSAGE.getBytes()); + channel.basicConsume( + queueName, + false, + new DefaultConsumer(channel) { + @Override + public void handleDelivery( + String consumerTag, + Envelope envelope, + AMQP.BasicProperties properties, + byte[] body + ) throws IOException { + messageWasReceived[0] = Arrays.equals(body, RABBITMQ_TEST_MESSAGE.getBytes()); + } } - }); + ); // post a message channel.basicPublish(RABBIQMQ_TEST_EXCHANGE, RABBITMQ_TEST_ROUTING_KEY, null, RABBITMQ_TEST_MESSAGE.getBytes()); // check the message was received - assertTrue("The message was received", Unreliables.retryUntilSuccess(5, TimeUnit.SECONDS, () -> { - if (!messageWasReceived[0]) { - throw new IllegalStateException("Message not received yet"); - } - return true; - })); + assertTrue( + "The message was received", + Unreliables.retryUntilSuccess( + 5, + TimeUnit.SECONDS, + () -> { + if (!messageWasReceived[0]) { + throw new IllegalStateException("Message not received yet"); + } + return true; + } + ) + ); } @Test @@ -223,8 +242,7 @@ public void simpleMongoDbTest() { MongoDatabase database = mongoClient.getDatabase("test"); MongoCollection collection = database.getCollection("testCollection"); - Document doc = new Document("name", "foo") - .append("value", 1); + Document doc = new Document("name", "foo").append("value", 1); collection.insertOne(doc); Document doc2 = collection.find(new Document("name", "foo")).first(); @@ -247,10 +265,11 @@ public void environmentFromMapTest() throws IOException { @Test public void customLabelTest() { - try (final GenericContainer alpineCustomLabel = new GenericContainer<>(ALPINE_IMAGE) - .withLabel("our.custom", "label") - .withCommand("top")) { - + try ( + final GenericContainer alpineCustomLabel = new GenericContainer<>(TestImages.ALPINE_IMAGE) + .withLabel("our.custom", "label") + .withCommand("top") + ) { alpineCustomLabel.start(); Map labels = alpineCustomLabel.getCurrentContainerInfo().getConfig().getLabels(); @@ -262,11 +281,11 @@ public void customLabelTest() { @Test public void exceptionThrownWhenTryingToOverrideTestcontainersLabels() { - assertThrows("When trying to overwrite an 'org.testcontainers' label, withLabel() throws an exception", + assertThrows( + "When trying to overwrite an 'org.testcontainers' label, withLabel() throws an exception", IllegalArgumentException.class, () -> { - new GenericContainer<>(ALPINE_IMAGE) - .withLabel("org.testcontainers.foo", "false"); + new GenericContainer<>(TestImages.ALPINE_IMAGE).withLabel("org.testcontainers.foo", "false"); } ); } @@ -277,25 +296,36 @@ public void customClasspathResourceMappingTest() throws IOException { // in that case this test will fail. String line = getReaderForContainerPort80(alpineClasspathResource).readLine(); - assertEquals("Resource on the classpath can be mapped using calls to withClasspathResourceMapping", "FOOBAR", line); + assertEquals( + "Resource on the classpath can be mapped using calls to withClasspathResourceMapping", + "FOOBAR", + line + ); } @Test public void customClasspathResourceMappingWithSelinuxTest() throws IOException { String line = getReaderForContainerPort80(alpineClasspathResourceSelinux).readLine(); - assertEquals("Resource on the classpath can be mapped using calls to withClasspathResourceMappingSelinux", "FOOBAR", line); + assertEquals( + "Resource on the classpath can be mapped using calls to withClasspathResourceMappingSelinux", + "FOOBAR", + line + ); } @Test public void exceptionThrownWhenMappedPortNotFound() { - assertThrows("When the requested port is not mapped, getMappedPort() throws an exception", - IllegalArgumentException.class, - () -> { - return redis.getMappedPort(666); - }); + assertThrows( + "When the requested port is not mapped, getMappedPort() throws an exception", + IllegalArgumentException.class, + () -> { + return redis.getMappedPort(666); + } + ); } - protected static void writeStringToFile(File contentFolder, String filename, String string) throws FileNotFoundException { + protected static void writeStringToFile(File contentFolder, String filename, String string) + throws FileNotFoundException { File file = new File(contentFolder, filename); PrintStream printStream = new PrintStream(new FileOutputStream(file)); @@ -303,32 +333,34 @@ protected static void writeStringToFile(File contentFolder, String filename, Str printStream.close(); } - @Test @Ignore //TODO investigate intermittent failures + @Test + @Ignore //TODO investigate intermittent failures public void failFastWhenContainerHaltsImmediately() { - long startingTimeMs = System.currentTimeMillis(); - final GenericContainer failsImmediately = new GenericContainer<>(ALPINE_IMAGE) - .withCommand("/bin/sh", "-c", "return false") - .withMinimumRunningDuration(Duration.ofMillis(100)); + final GenericContainer failsImmediately = new GenericContainer<>(TestImages.ALPINE_IMAGE) + .withCommand("/bin/sh", "-c", "return false") + .withMinimumRunningDuration(Duration.ofMillis(100)); try { assertThrows( - "When we start a container that halts immediately, an exception is thrown", - RetryCountExceededException.class, - () -> { - failsImmediately.start(); - return null; - }); + "When we start a container that halts immediately, an exception is thrown", + RetryCountExceededException.class, + () -> { + failsImmediately.start(); + return null; + } + ); // Check how long it took, to verify that we ARE bailing out early. // Want to strike a balance here; too short and this test will fail intermittently // on slow systems and/or due to GC variation, too long and we won't properly test // what we're intending to test. - int allowedSecondsToFailure = - GenericContainer.CONTAINER_RUNNING_TIMEOUT_SEC / 2; + int allowedSecondsToFailure = GenericContainer.CONTAINER_RUNNING_TIMEOUT_SEC / 2; long completedTimeMs = System.currentTimeMillis(); - assertTrue("container should not take long to start up", - completedTimeMs - startingTimeMs < 1000L * allowedSecondsToFailure); + assertTrue( + "container should not take long to start up", + completedTimeMs - startingTimeMs < 1000L * allowedSecondsToFailure + ); } finally { failsImmediately.stop(); } @@ -336,19 +368,20 @@ public void failFastWhenContainerHaltsImmediately() { @Test public void testExecInContainer() throws Exception { - // The older "lxc" execution driver doesn't support "exec". At the time of writing (2016/03/29), // that's the case for CircleCI. // Once they resolve the issue, this clause can be removed. Assume.assumeTrue(TestEnvironment.dockerExecutionDriverSupportsExec()); final GenericContainer.ExecResult result = redis.execInContainer("redis-cli", "role"); - assertTrue("Output for \"redis-cli role\" command should start with \"master\"", result.getStdout().startsWith("master")); + assertTrue( + "Output for \"redis-cli role\" command should start with \"master\"", + result.getStdout().startsWith("master") + ); assertEquals("Stderr for \"redis-cli role\" command should be empty", "", result.getStderr()); // We expect to reach this point for modern Docker versions. } - @Test public void extraHostTest() throws IOException { BufferedReader br = getReaderForContainerPort80(alpineExtrahost); @@ -370,30 +403,37 @@ public void extraHostTest() throws IOException { public void createContainerCmdHookTest() { // Use random name to avoid the conflicts between the tests String randomName = Base58.randomString(5); - try( - GenericContainer container = new GenericContainer<>(REDIS_IMAGE) - .withCommand("redis-server", "--help") - .withCreateContainerCmdModifier(cmd -> cmd.withName("overrideMe")) - // Preserves the order - .withCreateContainerCmdModifier(cmd -> cmd.withName(randomName)) - // Allows to override pre-configured values by GenericContainer - .withCreateContainerCmdModifier(cmd -> cmd.withCmd("redis-server", "--port", "6379")) + try ( + GenericContainer container = new GenericContainer<>(TestImages.REDIS_IMAGE) + .withCommand("redis-server", "--help") + .withCreateContainerCmdModifier(cmd -> cmd.withName("overrideMe")) + // Preserves the order + .withCreateContainerCmdModifier(cmd -> cmd.withName(randomName)) + // Allows to override pre-configured values by GenericContainer + .withCreateContainerCmdModifier(cmd -> cmd.withCmd("redis-server", "--port", "6379")) ) { container.start(); assertEquals("Name is configured", "/" + randomName, container.getContainerInfo().getName()); - assertEquals("Command is configured", "[redis-server, --port, 6379]", Arrays.toString(container.getContainerInfo().getConfig().getCmd())); + assertEquals( + "Command is configured", + "[redis-server, --port, 6379]", + Arrays.toString(container.getContainerInfo().getConfig().getCmd()) + ); } } private BufferedReader getReaderForContainerPort80(GenericContainer container) { + return Unreliables.retryUntilSuccess( + 10, + TimeUnit.SECONDS, + () -> { + Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); - return Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, () -> { - Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); - - Socket socket = new Socket(container.getHost(), container.getFirstMappedPort()); - return new BufferedReader(new InputStreamReader(socket.getInputStream())); - }); + Socket socket = new Socket(container.getHost(), container.getFirstMappedPort()); + return new BufferedReader(new InputStreamReader(socket.getInputStream())); + } + ); } @Test @@ -415,10 +455,11 @@ public void addingExposedPortTwiceShouldNotFail() { @Test public void sharedMemorySetTest() { - try (GenericContainer containerWithSharedMemory = new GenericContainer<>(TINY_IMAGE) - .withSharedMemorySize(42L * FileUtils.ONE_MB) - .withStartupCheckStrategy(new OneShotStartupCheckStrategy())) { - + try ( + GenericContainer containerWithSharedMemory = new GenericContainer<>(TestImages.TINY_IMAGE) + .withSharedMemorySize(42L * FileUtils.ONE_MB) + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + ) { containerWithSharedMemory.start(); HostConfig hostConfig = containerWithSharedMemory.getContainerInfo().getHostConfig(); diff --git a/core/src/test/java/org/testcontainers/junit/NonExistentImagePullTest.java b/core/src/test/java/org/testcontainers/junit/NonExistentImagePullTest.java index 95047779551..33909e2ed27 100644 --- a/core/src/test/java/org/testcontainers/junit/NonExistentImagePullTest.java +++ b/core/src/test/java/org/testcontainers/junit/NonExistentImagePullTest.java @@ -14,10 +14,12 @@ public class NonExistentImagePullTest { @Test(timeout = 60_000L) public void pullingNonExistentImageFailsGracefully() { - - assertThrows("Pulling a nonexistent container will cause an exception to be thrown", - ContainerFetchException.class, () -> { - new GenericContainer<>(DockerImageName.parse("testcontainers/nonexistent:latest")).getDockerImageName(); - }); + assertThrows( + "Pulling a nonexistent container will cause an exception to be thrown", + ContainerFetchException.class, + () -> { + new GenericContainer<>(DockerImageName.parse("testcontainers/nonexistent:latest")).getDockerImageName(); + } + ); } } diff --git a/core/src/test/java/org/testcontainers/junit/OutputStreamTest.java b/core/src/test/java/org/testcontainers/junit/OutputStreamTest.java index 649a0b56fc4..104fb78fe48 100644 --- a/core/src/test/java/org/testcontainers/junit/OutputStreamTest.java +++ b/core/src/test/java/org/testcontainers/junit/OutputStreamTest.java @@ -4,6 +4,7 @@ import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.testcontainers.TestImages; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.output.Slf4jLogConsumer; @@ -17,8 +18,6 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.TestImages.ALPINE_IMAGE; -import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; /** * Simple test for following container output. @@ -26,61 +25,74 @@ public class OutputStreamTest { @Rule - public GenericContainer container = new GenericContainer(ALPINE_IMAGE) - .withCommand("ping -c 5 127.0.0.1"); + public GenericContainer container = new GenericContainer(TestImages.ALPINE_IMAGE) + .withCommand("ping -c 5 127.0.0.1"); private static final Logger LOGGER = LoggerFactory.getLogger(OutputStreamTest.class); @Test(timeout = 60_000L) public void testFetchStdout() throws TimeoutException { - WaitingConsumer consumer = new WaitingConsumer(); - container.followOutput(consumer, STDOUT); + container.followOutput(consumer, OutputFrame.OutputType.STDOUT); - consumer.waitUntil(frame -> frame.getType() == STDOUT && frame.getUtf8String().contains("seq=2"), - 30, TimeUnit.SECONDS); + consumer.waitUntil( + frame -> frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().contains("seq=2"), + 30, + TimeUnit.SECONDS + ); } @Test(timeout = 60_000L) public void testFetchStdoutWithTimeout() { - WaitingConsumer consumer = new WaitingConsumer(); - container.followOutput(consumer, STDOUT); - - assertThrows("a TimeoutException should be thrown", TimeoutException.class, () -> { - consumer.waitUntil(frame -> frame.getType() == STDOUT && frame.getUtf8String().contains("seq=5"), - 2, TimeUnit.SECONDS); - return true; - }); + container.followOutput(consumer, OutputFrame.OutputType.STDOUT); + + assertThrows( + "a TimeoutException should be thrown", + TimeoutException.class, + () -> { + consumer.waitUntil( + frame -> { + return ( + frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().contains("seq=5") + ); + }, + 2, + TimeUnit.SECONDS + ); + return true; + } + ); } @Test(timeout = 60_000L) public void testFetchStdoutWithNoLimit() throws TimeoutException { - WaitingConsumer consumer = new WaitingConsumer(); - container.followOutput(consumer, STDOUT); + container.followOutput(consumer, OutputFrame.OutputType.STDOUT); - consumer.waitUntil(frame -> frame.getType() == STDOUT && frame.getUtf8String().contains("seq=2")); + consumer.waitUntil(frame -> { + return frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().contains("seq=2"); + }); } @Test(timeout = 60_000L) public void testLogConsumer() throws TimeoutException { - WaitingConsumer waitingConsumer = new WaitingConsumer(); Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(LOGGER); Consumer composedConsumer = logConsumer.andThen(waitingConsumer); container.followOutput(composedConsumer); - waitingConsumer.waitUntil(frame -> frame.getType() == STDOUT && frame.getUtf8String().contains("seq=2")); + waitingConsumer.waitUntil(frame -> { + return frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().contains("seq=2"); + }); } @Test(timeout = 60_000L) public void testToStringConsumer() throws TimeoutException { - WaitingConsumer waitingConsumer = new WaitingConsumer(); ToStringConsumer toStringConsumer = new ToStringConsumer(); @@ -95,4 +107,3 @@ public void testToStringConsumer() throws TimeoutException { assertFalse("a non-expected value was found", utf8String.contains("seq=42")); } } - diff --git a/core/src/test/java/org/testcontainers/junit/OutputStreamWithTTYTest.java b/core/src/test/java/org/testcontainers/junit/OutputStreamWithTTYTest.java index b15f63f4158..a87dbef3990 100644 --- a/core/src/test/java/org/testcontainers/junit/OutputStreamWithTTYTest.java +++ b/core/src/test/java/org/testcontainers/junit/OutputStreamWithTTYTest.java @@ -4,6 +4,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.Timeout; +import org.testcontainers.TestImages; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.output.Slf4jLogConsumer; @@ -18,14 +19,12 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.TestImages.ALPINE_IMAGE; -import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; @Slf4j public class OutputStreamWithTTYTest { @Rule - public GenericContainer container = new GenericContainer<>(ALPINE_IMAGE) + public GenericContainer container = new GenericContainer<>(TestImages.ALPINE_IMAGE) .withCommand("ls -1") .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) .withCreateContainerCmdModifier(command -> command.withTty(true)); @@ -37,30 +36,44 @@ public class OutputStreamWithTTYTest { public void testFetchStdout() throws TimeoutException { WaitingConsumer consumer = new WaitingConsumer(); - container.followOutput(consumer, STDOUT); + container.followOutput(consumer, OutputFrame.OutputType.STDOUT); - consumer.waitUntil(frame -> frame.getType() == STDOUT && frame.getUtf8String().contains("home"), 4, TimeUnit.SECONDS); + consumer.waitUntil( + frame -> frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().contains("home"), + 4, + TimeUnit.SECONDS + ); } @Test public void testFetchStdoutWithTimeout() { WaitingConsumer consumer = new WaitingConsumer(); - container.followOutput(consumer, STDOUT); - - assertThrows("a TimeoutException should be thrown", TimeoutException.class, () -> { - consumer.waitUntil(frame -> frame.getType() == STDOUT && frame.getUtf8String().contains("qqq"), 1, TimeUnit.SECONDS); - return true; - }); + container.followOutput(consumer, OutputFrame.OutputType.STDOUT); + + assertThrows( + "a TimeoutException should be thrown", + TimeoutException.class, + () -> { + consumer.waitUntil( + frame -> frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().contains("qqq"), + 1, + TimeUnit.SECONDS + ); + return true; + } + ); } @Test public void testFetchStdoutWithNoLimit() throws TimeoutException { WaitingConsumer consumer = new WaitingConsumer(); - container.followOutput(consumer, STDOUT); + container.followOutput(consumer, OutputFrame.OutputType.STDOUT); - consumer.waitUntil(frame -> frame.getType() == STDOUT && frame.getUtf8String().contains("home")); + consumer.waitUntil(frame -> { + return frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().contains("home"); + }); } @Test @@ -71,7 +84,9 @@ public void testLogConsumer() throws TimeoutException { Consumer composedConsumer = logConsumer.andThen(waitingConsumer); container.followOutput(composedConsumer); - waitingConsumer.waitUntil(frame -> frame.getType() == STDOUT && frame.getUtf8String().contains("home")); + waitingConsumer.waitUntil(frame -> { + return frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().contains("home"); + }); } @Test diff --git a/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java b/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java index 969d2836b2e..62eccc2000c 100644 --- a/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java @@ -22,22 +22,27 @@ public class ParameterizedDockerfileContainerTest { public GenericContainer container; public ParameterizedDockerfileContainerTest(String baseImage, String expectedVersion) { - container = new GenericContainer(new ImageFromDockerfile().withDockerfileFromBuilder(builder -> { - builder - .from(baseImage) - // Could potentially customise the image here, e.g. adding files, running - // commands, etc. - .build(); - })).withCommand("top"); + container = + new GenericContainer( + new ImageFromDockerfile() + .withDockerfileFromBuilder(builder -> { + builder + .from(baseImage) + // Could potentially customise the image here, e.g. adding files, running + // commands, etc. + .build(); + }) + ) + .withCommand("top"); this.expectedVersion = expectedVersion; } @Parameterized.Parameters(name = "{0}") public static Object[][] data() { - return new Object[][] { - { "alpine:3.12", "3.12"}, - { "alpine:3.13", "3.13"}, - { "alpine:3.14", "3.14"} + return new Object[][] { // + { "alpine:3.12", "3.12" }, + { "alpine:3.13", "3.13" }, + { "alpine:3.14", "3.14" }, }; } @@ -45,7 +50,6 @@ public static Object[][] data() { public void simpleTest() throws Exception { final String release = container.execInContainer("cat", "/etc/alpine-release").getStdout(); - assertTrue("/etc/alpine-release starts with " + expectedVersion, - release.startsWith(expectedVersion)); + assertTrue("/etc/alpine-release starts with " + expectedVersion, release.startsWith(expectedVersion)); } } diff --git a/core/src/test/java/org/testcontainers/junit/WorkingDirectoryTest.java b/core/src/test/java/org/testcontainers/junit/WorkingDirectoryTest.java index b23ddf2ca1c..0be4022c171 100644 --- a/core/src/test/java/org/testcontainers/junit/WorkingDirectoryTest.java +++ b/core/src/test/java/org/testcontainers/junit/WorkingDirectoryTest.java @@ -2,11 +2,11 @@ import org.junit.ClassRule; import org.junit.Test; +import org.testcontainers.TestImages; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.TestImages.ALPINE_IMAGE; /** * Created by rnorth on 26/07/2016. @@ -14,10 +14,10 @@ public class WorkingDirectoryTest { @ClassRule - public static GenericContainer container = new GenericContainer(ALPINE_IMAGE) - .withWorkingDirectory("/etc") - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) - .withCommand("ls", "-al"); + public static GenericContainer container = new GenericContainer(TestImages.ALPINE_IMAGE) + .withWorkingDirectory("/etc") + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .withCommand("ls", "-al"); @Test public void checkOutput() { @@ -27,5 +27,4 @@ public void checkOutput() { assertTrue("Directory listing contains expected /etc content", listing.contains("init.d")); assertTrue("Directory listing contains expected /etc content", listing.contains("passwd")); } - } diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java index 480536a54ef..de16eb0e582 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java @@ -20,6 +20,7 @@ * @author Pete Cornish {@literal } */ public abstract class AbstractWaitStrategyTest { + static final long WAIT_TIMEOUT_MILLIS = 3000; /** @@ -65,12 +66,16 @@ protected GenericContainer startContainerWithCommand(String shellCommand, Wai return startContainerWithCommand(shellCommand, waitStrategy, 8080); } - protected GenericContainer startContainerWithCommand(String shellCommand, WaitStrategy waitStrategy, Integer... ports) { + protected GenericContainer startContainerWithCommand( + String shellCommand, + WaitStrategy waitStrategy, + Integer... ports + ) { // apply WaitStrategy to container return new GenericContainer<>(TestImages.ALPINE_IMAGE) - .withExposedPorts(ports) - .withCommand("sh", "-c", shellCommand) - .waitingFor(waitStrategy.withStartupTimeout(Duration.ofMillis(WAIT_TIMEOUT_MILLIS))); + .withExposedPorts(ports) + .withCommand("sh", "-c", shellCommand) + .waitingFor(waitStrategy.withStartupTimeout(Duration.ofMillis(WAIT_TIMEOUT_MILLIS))); } /** @@ -100,10 +105,11 @@ protected void waitUntilReadyAndTimeout(String shellCommand) { */ protected void waitUntilReadyAndTimeout(GenericContainer container) { // start() blocks until successful or timeout - VisibleAssertions.assertThrows("an exception is thrown when timeout occurs (" + WAIT_TIMEOUT_MILLIS + "ms)", - ContainerLaunchException.class, - container::start); - + VisibleAssertions.assertThrows( + "an exception is thrown when timeout occurs (" + WAIT_TIMEOUT_MILLIS + "ms)", + ContainerLaunchException.class, + container::start + ); } /** @@ -115,7 +121,9 @@ protected void waitUntilReadyAndSucceed(GenericContainer container) { // start() blocks until successful or timeout container.start(); - assertTrue(String.format("Expected container to be ready after timeout of %sms", - WAIT_TIMEOUT_MILLIS), ready.get()); + assertTrue( + String.format("Expected container to be ready after timeout of %sms", WAIT_TIMEOUT_MILLIS), + ready.get() + ); } } diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/HostPortWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/HostPortWaitStrategyTest.java index 5c7d37a7da3..4b5f43a4d4b 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/HostPortWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/HostPortWaitStrategyTest.java @@ -17,7 +17,8 @@ public class HostPortWaitStrategyTest { @ClassRule - public static GenericContainer container = new GenericContainer<>(TestImages.ALPINE_IMAGE).withExposedPorts() + public static GenericContainer container = new GenericContainer<>(TestImages.ALPINE_IMAGE) + .withExposedPorts() .withCommand("sh", "-c", "while true; do nc -lp 8080; done") .withExposedPorts(8080) .waitingFor(Wait.forListeningPort().withStartupTimeout(Duration.ofSeconds(10))); diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java index 82207b4537e..7014749115e 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java @@ -1,9 +1,5 @@ package org.testcontainers.junit.wait.strategy; -import static org.hamcrest.CoreMatchers.containsString; -import static org.junit.Assert.assertThat; - -import java.util.HashMap; import org.jetbrains.annotations.NotNull; import org.junit.Test; import org.rnorth.ducttape.RetryCountExceededException; @@ -11,15 +7,20 @@ import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import java.time.Duration; +import java.util.HashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertThat; + /** * Tests for {@link HttpWaitStrategy}. * * @author Pete Cornish {@literal } */ public class HttpWaitStrategyTest extends AbstractWaitStrategyTest { + /** * newline sequence indicating end of the HTTP header. */ @@ -44,10 +45,9 @@ public void testWaitUntilReadyWithSuccess() { public void testWaitUntilReadyWithSuccessWithCustomHeaders() { HashMap headers = new HashMap<>(); headers.put("baz", "boo"); - GenericContainer container = startContainerWithCommand(createShellCommand("200 OK", GOOD_RESPONSE_BODY), - createHttpWaitStrategy(ready) - .withHeader("foo", "bar") - .withHeaders(headers) + GenericContainer container = startContainerWithCommand( + createShellCommand("200 OK", GOOD_RESPONSE_BODY), + createHttpWaitStrategy(ready).withHeader("foo", "bar").withHeaders(headers) ); waitUntilReadyAndSucceed(container); @@ -58,14 +58,17 @@ public void testWaitUntilReadyWithSuccessWithCustomHeaders() { } /** - * Ensures that HTTPS requests made with the HttpWaitStrategy can skip the - * certificate validation chains (to support self-signed certificates for example). + * Ensures that HTTPS requests made with the HttpWaitStrategy can skip the + * certificate validation chains (to support self-signed certificates for example). */ @Test public void testWaitUntilReadyWithTlsAndAllowUnsecure() { - waitUntilReadyAndSucceed(startContainerWithCommand(createHttpsShellCommand("200 OK", GOOD_RESPONSE_BODY, 8080), - createHttpWaitStrategy(ready).usingTls().allowInsecure() - )); + waitUntilReadyAndSucceed( + startContainerWithCommand( + createHttpsShellCommand("200 OK", GOOD_RESPONSE_BODY, 8080), + createHttpWaitStrategy(ready).usingTls().allowInsecure() + ) + ); } /** @@ -74,10 +77,12 @@ public void testWaitUntilReadyWithTlsAndAllowUnsecure() { */ @Test public void testWaitUntilReadyWithUnauthorizedWithLambda() { - waitUntilReadyAndSucceed(startContainerWithCommand(createShellCommand("401 UNAUTHORIZED", GOOD_RESPONSE_BODY), - createHttpWaitStrategy(ready) - .forStatusCodeMatching(it -> it >= 200 && it < 300 || it == 401) - )); + waitUntilReadyAndSucceed( + startContainerWithCommand( + createShellCommand("401 UNAUTHORIZED", GOOD_RESPONSE_BODY), + createHttpWaitStrategy(ready).forStatusCodeMatching(it -> it >= 200 && it < 300 || it == 401) + ) + ); } /** @@ -86,12 +91,12 @@ public void testWaitUntilReadyWithUnauthorizedWithLambda() { */ @Test public void testWaitUntilReadyWithManyStatusCodes() { - waitUntilReadyAndSucceed(startContainerWithCommand(createShellCommand("401 UNAUTHORIZED", GOOD_RESPONSE_BODY), - createHttpWaitStrategy(ready) - .forStatusCode(300) - .forStatusCode(401) - .forStatusCode(500) - )); + waitUntilReadyAndSucceed( + startContainerWithCommand( + createShellCommand("401 UNAUTHORIZED", GOOD_RESPONSE_BODY), + createHttpWaitStrategy(ready).forStatusCode(300).forStatusCode(401).forStatusCode(500) + ) + ); } /** @@ -101,12 +106,15 @@ public void testWaitUntilReadyWithManyStatusCodes() { */ @Test public void testWaitUntilReadyWithManyStatusCodesAndLambda() { - waitUntilReadyAndSucceed(startContainerWithCommand(createShellCommand("401 UNAUTHORIZED", GOOD_RESPONSE_BODY), - createHttpWaitStrategy(ready) - .forStatusCode(300) - .forStatusCode(500) - .forStatusCodeMatching(it -> it == 401) - )); + waitUntilReadyAndSucceed( + startContainerWithCommand( + createShellCommand("401 UNAUTHORIZED", GOOD_RESPONSE_BODY), + createHttpWaitStrategy(ready) + .forStatusCode(300) + .forStatusCode(500) + .forStatusCodeMatching(it -> it == 401) + ) + ); } /** @@ -116,11 +124,12 @@ public void testWaitUntilReadyWithManyStatusCodesAndLambda() { */ @Test public void testWaitUntilReadyWithTimeoutAndWithManyStatusCodesAndLambda() { - waitUntilReadyAndTimeout(startContainerWithCommand(createShellCommand("401 UNAUTHORIZED", GOOD_RESPONSE_BODY), - createHttpWaitStrategy(ready) - .forStatusCode(300) - .forStatusCodeMatching(it -> it == 500) - )); + waitUntilReadyAndTimeout( + startContainerWithCommand( + createShellCommand("401 UNAUTHORIZED", GOOD_RESPONSE_BODY), + createHttpWaitStrategy(ready).forStatusCode(300).forStatusCodeMatching(it -> it == 500) + ) + ); } /** @@ -132,10 +141,12 @@ public void testWaitUntilReadyWithTimeoutAndWithManyStatusCodesAndLambda() { */ @Test public void testWaitUntilReadyWithTimeoutAndWithLambdaShouldNotMatchOk() { - waitUntilReadyAndTimeout(startContainerWithCommand(createShellCommand("200 OK", GOOD_RESPONSE_BODY), - createHttpWaitStrategy(ready) - .forStatusCodeMatching(it -> it >= 300) - )); + waitUntilReadyAndTimeout( + startContainerWithCommand( + createShellCommand("200 OK", GOOD_RESPONSE_BODY), + createHttpWaitStrategy(ready).forStatusCodeMatching(it -> it >= 300) + ) + ); } /** @@ -156,27 +167,31 @@ public void testWaitUntilReadyWithTimeoutAndBadResponseBody() { waitUntilReadyAndTimeout(createShellCommand("200 OK", "Bad Response")); } - /** * Expects the WaitStrategy probing the right port. */ @Test public void testWaitUntilReadyWithSpecificPort() { - waitUntilReadyAndSucceed(startContainerWithCommand( - createShellCommand("200 OK", GOOD_RESPONSE_BODY, 9090), - createHttpWaitStrategy(ready) - .forPort(9090), - 7070, 8080, 9090 - )); + waitUntilReadyAndSucceed( + startContainerWithCommand( + createShellCommand("200 OK", GOOD_RESPONSE_BODY, 9090), + createHttpWaitStrategy(ready).forPort(9090), + 7070, + 8080, + 9090 + ) + ); } @Test public void testWaitUntilReadyWithTimoutCausedByReadTimeout() { waitUntilReadyAndTimeout( - startContainerWithCommand(createShellCommand("0 Connection Refused", GOOD_RESPONSE_BODY, 9090), + startContainerWithCommand( + createShellCommand("0 Connection Refused", GOOD_RESPONSE_BODY, 9090), createHttpWaitStrategy(ready).forPort(9090).withReadTimeout(Duration.ofMillis(1)), 9090 - )); + ) + ); } /** @@ -185,8 +200,7 @@ public void testWaitUntilReadyWithTimoutCausedByReadTimeout() { */ @NotNull protected HttpWaitStrategy buildWaitStrategy(final AtomicBoolean ready) { - return createHttpWaitStrategy(ready) - .forResponsePredicate(s -> s.equals(GOOD_RESPONSE_BODY)); + return createHttpWaitStrategy(ready).forResponsePredicate(s -> s.equals(GOOD_RESPONSE_BODY)); } /** @@ -212,17 +226,41 @@ private String createShellCommand(String header, String responseBody) { private String createShellCommand(String header, String responseBody, int port) { int length = responseBody.getBytes().length; - return "while true; do { echo -e \"HTTP/1.1 " + header + NEWLINE + - "Content-Type: text/html" + NEWLINE + - "Content-Length: " + length + NEWLINE + "\";" - + " echo \"" + responseBody + "\";} | nc -lp " + port + "; done"; + return ( + "while true; do { echo -e \"HTTP/1.1 " + + header + + NEWLINE + + "Content-Type: text/html" + + NEWLINE + + "Content-Length: " + + length + + NEWLINE + + "\";" + + " echo \"" + + responseBody + + "\";} | nc -lp " + + port + + "; done" + ); } - + private String createHttpsShellCommand(String header, String responseBody, int port) { int length = responseBody.getBytes().length; - return "apk add nmap-ncat; while true; do { echo -e \"HTTP/1.1 " + header + NEWLINE + - "Content-Type: text/html" + NEWLINE + - "Content-Length: " + length + NEWLINE + "\";" - + " echo \"" + responseBody + "\";} | ncat -lp " + port + " --ssl; done"; + return ( + "apk add nmap-ncat; while true; do { echo -e \"HTTP/1.1 " + + header + + NEWLINE + + "Content-Type: text/html" + + NEWLINE + + "Content-Length: " + + length + + NEWLINE + + "\";" + + " echo \"" + + responseBody + + "\";} | ncat -lp " + + port + + " --ssl; done" + ); } } diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/LogMessageWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/LogMessageWaitStrategyTest.java index a6ef9f11ba4..5dd9623107b 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/LogMessageWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/LogMessageWaitStrategyTest.java @@ -19,9 +19,9 @@ public class LogMessageWaitStrategyTest extends AbstractWaitStrategyTest { - builder.from(DOCKER_REGISTRY_IMAGE.asCanonicalNameString()) - .run("htpasswd -Bbn testuser notasecret > /htpasswd") - .env("REGISTRY_AUTH", "htpasswd") - .env("REGISTRY_AUTH_HTPASSWD_PATH", "/htpasswd") - .env("REGISTRY_AUTH_HTPASSWD_REALM", "Test"); - })); + public static DockerRegistryContainer authenticatedRegistry = new DockerRegistryContainer( + new ImageFromDockerfile() + .withDockerfileFromBuilder(builder -> { + builder + .from(TestImages.DOCKER_REGISTRY_IMAGE.asCanonicalNameString()) + .run("htpasswd -Bbn testuser notasecret > /htpasswd") + .env("REGISTRY_AUTH", "htpasswd") + .env("REGISTRY_AUTH_HTPASSWD_PATH", "/htpasswd") + .env("REGISTRY_AUTH_HTPASSWD_REALM", "Test"); + }) + ); private static RegistryAuthLocator originalAuthLocatorSingleton; @@ -65,7 +68,12 @@ public static void beforeClass() throws Exception { // Replace the RegistryAuthLocator singleton with our mock, for the duration of this test final RegistryAuthLocator mockAuthLocator = Mockito.mock(RegistryAuthLocator.class); RegistryAuthLocator.setInstance(mockAuthLocator); - when(mockAuthLocator.lookupAuthConfig(argThat(argument -> testRegistryAddress.equals(argument.getRegistry())), any())) + when( + mockAuthLocator.lookupAuthConfig( + argThat(argument -> testRegistryAddress.equals(argument.getRegistry())), + any() + ) + ) .thenReturn(authConfig); } @@ -77,8 +85,10 @@ public static void tearDown() { @Test public void testThatAuthLocatorIsUsedForContainerCreation() { // actually start a container, which will require an authenticated pull - try (final GenericContainer container = new GenericContainer<>(testImageName) - .withCommand("/bin/sh", "-c", "sleep 10")) { + try ( + final GenericContainer container = new GenericContainer<>(testImageName) + .withCommand("/bin/sh", "-c", "sleep 10") + ) { container.start(); assertTrue("container started following an authenticated pull", container.isRunning()); @@ -93,11 +103,12 @@ public void testThatAuthLocatorIsUsedForDockerfileBuild() throws IOException { Files.write(tempFile, dockerFileContent.getBytes()); // Start a container built from a derived image, which will require an authenticated pull - try (final GenericContainer container = new GenericContainer<>( - new ImageFromDockerfile() - .withDockerfile(tempFile) - ) - .withCommand("/bin/sh", "-c", "sleep 10")) { + try ( + final GenericContainer container = new GenericContainer<>( + new ImageFromDockerfile().withDockerfile(tempFile) + ) + .withCommand("/bin/sh", "-c", "sleep 10") + ) { container.start(); assertTrue("container started following an authenticated pull", container.isRunning()); @@ -108,23 +119,23 @@ public void testThatAuthLocatorIsUsedForDockerfileBuild() throws IOException { public void testThatAuthLocatorIsUsedForDockerComposePull() throws IOException { // Prepare a simple temporary Docker Compose manifest which requires our custom private image Path tempFile = getLocalTempFile(".docker-compose.yml"); - @Language("yaml") String composeFileContent = + @Language("yaml") + String composeFileContent = "version: '2.0'\n" + - "services:\n" + - " privateservice:\n" + - " command: /bin/sh -c 'sleep 60'\n" + - " image: " + testImageName.asCanonicalNameString(); + "services:\n" + + " privateservice:\n" + + " command: /bin/sh -c 'sleep 60'\n" + + " image: " + + testImageName.asCanonicalNameString(); Files.write(tempFile, composeFileContent.getBytes()); // Start the docker compose project, which will require an authenticated pull try (final DockerComposeContainer compose = new DockerComposeContainer<>(tempFile.toFile())) { compose.start(); - assertTrue("container started following an authenticated pull", - compose - .getContainerByServiceName("privateservice_1") - .map(ContainerState::isRunning) - .orElse(false) + assertTrue( + "container started following an authenticated pull", + compose.getContainerByServiceName("privateservice_1").map(ContainerState::isRunning).orElse(false) ); } } diff --git a/core/src/test/java/org/testcontainers/utility/ClasspathScannerTest.java b/core/src/test/java/org/testcontainers/utility/ClasspathScannerTest.java index bdd71b0db9f..4ed86e3a8fa 100644 --- a/core/src/test/java/org/testcontainers/utility/ClasspathScannerTest.java +++ b/core/src/test/java/org/testcontainers/utility/ClasspathScannerTest.java @@ -5,11 +5,11 @@ import java.io.IOException; import java.net.URL; +import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; -import static java.util.Arrays.asList; -import static java.util.stream.Collectors.toList; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -18,9 +18,13 @@ public class ClasspathScannerTest { private static URL FILE_A; + private static URL FILE_B; + private static URL JAR_A; + private static URL JAR_B; + private static URL FILE_C; @BeforeClass @@ -35,7 +39,7 @@ public static void setUp() throws Exception { @Test public void realClassLoaderLookupOccurs() { // look for a resource that we know exists only once - final List foundURLs = ClasspathScanner.scanFor("expectedClasspathFile.txt").collect(toList()); + final List foundURLs = ClasspathScanner.scanFor("expectedClasspathFile.txt").collect(Collectors.toList()); assertEquals("Exactly one resource was found", 1, foundURLs.size()); } @@ -43,41 +47,27 @@ public void realClassLoaderLookupOccurs() { @Test public void multipleResultsOnOneClassLoaderAreFound() throws IOException { final ClassLoader firstMockClassLoader = mock(ClassLoader.class); - when(firstMockClassLoader.getResources(eq("someName"))).thenReturn( - Collections.enumeration( - asList( - FILE_A, - FILE_B - ) - ) - ); + when(firstMockClassLoader.getResources(eq("someName"))) + .thenReturn(Collections.enumeration(Arrays.asList(FILE_A, FILE_B))); - final List foundURLs = ClasspathScanner.scanFor("someName", firstMockClassLoader).collect(toList()); - assertEquals( - "The expected URLs are found", - asList(FILE_A, FILE_B), - foundURLs - ); + final List foundURLs = ClasspathScanner + .scanFor("someName", firstMockClassLoader) + .collect(Collectors.toList()); + assertEquals("The expected URLs are found", Arrays.asList(FILE_A, FILE_B), foundURLs); } @Test public void orderIsAlphabeticalForDeterminism() throws IOException { final ClassLoader firstMockClassLoader = mock(ClassLoader.class); - when(firstMockClassLoader.getResources(eq("someName"))).thenReturn( - Collections.enumeration( - asList( - FILE_B, - JAR_A, - JAR_B, - FILE_A - ) - ) - ); + when(firstMockClassLoader.getResources(eq("someName"))) + .thenReturn(Collections.enumeration(Arrays.asList(FILE_B, JAR_A, JAR_B, FILE_A))); - final List foundURLs = ClasspathScanner.scanFor("someName", firstMockClassLoader).collect(toList()); + final List foundURLs = ClasspathScanner + .scanFor("someName", firstMockClassLoader) + .collect(Collectors.toList()); assertEquals( "The expected URLs are found in the expected order", - asList(FILE_A, FILE_B, JAR_A, JAR_B), + Arrays.asList(FILE_A, FILE_B, JAR_A, JAR_B), foundURLs ); } @@ -85,30 +75,23 @@ public void orderIsAlphabeticalForDeterminism() throws IOException { @Test public void multipleClassLoadersAreQueried() throws IOException { final ClassLoader firstMockClassLoader = mock(ClassLoader.class); - when(firstMockClassLoader.getResources(eq("someName"))).thenReturn( - Collections.enumeration( - asList( - FILE_A, - FILE_B - ) - ) - ); + when(firstMockClassLoader.getResources(eq("someName"))) + .thenReturn(Collections.enumeration(Arrays.asList(FILE_A, FILE_B))); final ClassLoader secondMockClassLoader = mock(ClassLoader.class); - when(secondMockClassLoader.getResources(eq("someName"))).thenReturn( - Collections.enumeration( - asList( - FILE_B, // duplicate - FILE_C + when(secondMockClassLoader.getResources(eq("someName"))) + .thenReturn( + Collections.enumeration( + Arrays.asList( + FILE_B, // duplicate + FILE_C + ) ) - ) - ); + ); - final List foundURLs = ClasspathScanner.scanFor("someName", firstMockClassLoader, secondMockClassLoader).collect(toList()); + final List foundURLs = ClasspathScanner + .scanFor("someName", firstMockClassLoader, secondMockClassLoader) + .collect(Collectors.toList()); - assertEquals( - "The expected URLs are found", - asList(FILE_A, FILE_B, FILE_C), - foundURLs - ); + assertEquals("The expected URLs are found", Arrays.asList(FILE_A, FILE_B, FILE_C), foundURLs); } } diff --git a/core/src/test/java/org/testcontainers/utility/ComparableVersionTest.java b/core/src/test/java/org/testcontainers/utility/ComparableVersionTest.java index d5a2c63a5b8..fb3c7e05c0b 100644 --- a/core/src/test/java/org/testcontainers/utility/ComparableVersionTest.java +++ b/core/src/test/java/org/testcontainers/utility/ComparableVersionTest.java @@ -1,18 +1,19 @@ package org.testcontainers.utility; -import java.util.Arrays; - import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; +import java.util.Arrays; + import static org.junit.Assert.assertArrayEquals; @RunWith(Parameterized.class) public class ComparableVersionTest { private final int[] expected; + private final String given; public ComparableVersionTest(final String given, final int[] expected) { @@ -27,14 +28,15 @@ public void shouldParseVersions() { @Parameters(name = "Parsed version: {0}={1}") public static Iterable data() { - return Arrays.asList(new Object[][] { - {"1.2.3", new int[] {1, 2, 3}}, - {"", new int[0]}, - {"1", new int[] {1}}, - {"1.2.3.4.5.6.7", new int[] {1, 2, 3, 4, 5, 6, 7}}, - {"1.2-dev", new int[] {1, 2}}, - {"18.06.0-dev", new int[] {18, 6}}, - }); + return Arrays.asList( + new Object[][] { + { "1.2.3", new int[] { 1, 2, 3 } }, + { "", new int[0] }, + { "1", new int[] { 1 } }, + { "1.2.3.4.5.6.7", new int[] { 1, 2, 3, 4, 5, 6, 7 } }, + { "1.2-dev", new int[] { 1, 2 } }, + { "18.06.0-dev", new int[] { 18, 6 } }, + } + ); } - } diff --git a/core/src/test/java/org/testcontainers/utility/DefaultImageNameSubstitutorTest.java b/core/src/test/java/org/testcontainers/utility/DefaultImageNameSubstitutorTest.java index a6f7813c3d3..0978003f815 100644 --- a/core/src/test/java/org/testcontainers/utility/DefaultImageNameSubstitutorTest.java +++ b/core/src/test/java/org/testcontainers/utility/DefaultImageNameSubstitutorTest.java @@ -12,7 +12,9 @@ public class DefaultImageNameSubstitutorTest { public static final DockerImageName ORIGINAL_IMAGE = DockerImageName.parse("foo"); + public static final DockerImageName SUBSTITUTE_IMAGE = DockerImageName.parse("bar"); + private ConfigurationFileImageNameSubstitutor underTest; @Rule diff --git a/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java b/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java index f439c55c3ea..cbd35a62b52 100644 --- a/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java +++ b/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java @@ -11,8 +11,8 @@ import java.io.File; import java.util.Arrays; -import static org.hamcrest.CoreMatchers.allOf; -import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; @@ -24,41 +24,51 @@ public void simpleRecursiveFileTest() { File directory = new File("src"); GenericContainer container = new GenericContainer( - new ImageFromDockerfile() - .withDockerfileFromBuilder(builder -> - builder.from("alpine:3.14") - .copy("/tmp/foo", "/foo") - .cmd("cat /foo/test/resources/test-recursive-file.txt") - .build() - ).withFileFromFile("/tmp/foo", directory)) - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()); + new ImageFromDockerfile() + .withDockerfileFromBuilder(builder -> { + builder + .from("alpine:3.14") + .copy("/tmp/foo", "/foo") + .cmd("cat /foo/test/resources/test-recursive-file.txt") + .build(); + }) + .withFileFromFile("/tmp/foo", directory) + ) + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()); container.start(); final String results = container.getLogs(); - assertTrue("The container has a file that was copied in via a recursive copy", results.contains("Used for DirectoryTarResourceTest")); + assertTrue( + "The container has a file that was copied in via a recursive copy", + results.contains("Used for DirectoryTarResourceTest") + ); } @Test public void simpleRecursiveFileWithPermissionTest() { GenericContainer container = new GenericContainer( - new ImageFromDockerfile() - .withDockerfileFromBuilder(builder -> - builder.from("alpine:3.14") - .copy("/tmp/foo", "/foo") - .cmd("ls", "-al", "/") - .build() - ).withFileFromFile("/tmp/foo", new File("/mappable-resource/test-resource.txt"), - 0754)) - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()); + new ImageFromDockerfile() + .withDockerfileFromBuilder(builder -> { + builder + .from("alpine:3.14") // + .copy("/tmp/foo", "/foo") + .cmd("ls", "-al", "/") + .build(); + }) + .withFileFromFile("/tmp/foo", new File("/mappable-resource/test-resource.txt"), 0754) + ) + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()); container.start(); String listing = container.getLogs(); - assertThat("Listing shows that file is copied with mode requested.", - Arrays.asList(listing.split("\\n")), - exactlyNItems(1, allOf(containsString("-rwxr-xr--"), containsString("foo")))); + assertThat( + "Listing shows that file is copied with mode requested.", + Arrays.asList(listing.split("\\n")), + exactlyNItems(1, allOf(containsString("-rwxr-xr--"), containsString("foo"))) + ); } @Test @@ -66,21 +76,27 @@ public void simpleRecursiveClasspathResourceTest() { // This test combines the copying of classpath resources from JAR files with the recursive TAR approach, to allow JARed classpath resources to be copied in to an image GenericContainer container = new GenericContainer( - new ImageFromDockerfile() - .withDockerfileFromBuilder(builder -> - builder.from("alpine:3.14") - .copy("/tmp/foo", "/foo") - .cmd("ls -lRt /foo") - .build() - ).withFileFromClasspath("/tmp/foo", "/recursive/dir")) // here we use /org/junit as a directory that really should exist on the classpath - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()); + new ImageFromDockerfile() + .withDockerfileFromBuilder(builder -> { + builder + .from("alpine:3.14") // + .copy("/tmp/foo", "/foo") + .cmd("ls -lRt /foo") + .build(); + }) + .withFileFromClasspath("/tmp/foo", "/recursive/dir") + ) // here we use /org/junit as a directory that really should exist on the classpath + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()); container.start(); final String results = container.getLogs(); // ExternalResource.class is known to exist in a subdirectory of /org/junit so should be successfully copied in - assertTrue("The container has a file that was copied in via a recursive copy from a JAR resource", results.contains("content.txt")); + assertTrue( + "The container has a file that was copied in via a recursive copy from a JAR resource", + results.contains("content.txt") + ); } public static Matcher> exactlyNItems(final int n, Matcher elementMatcher) { @@ -91,7 +107,6 @@ protected boolean matchesSafely(Iterable collection, Description mism boolean isPastFirst = false; for (Object item : collection) { - if (elementMatcher.matches(item)) { count++; } diff --git a/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java b/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java index 22d67de5785..f2ca26b8947 100644 --- a/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java +++ b/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java @@ -1,13 +1,12 @@ package org.testcontainers.utility; -import static org.hamcrest.core.StringContains.containsString; -import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; - import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import static org.hamcrest.Matchers.containsString; +import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; public class DockerImageNameCompatibilityTest { @@ -20,6 +19,7 @@ public void testPlainImage() { assertFalse("image name foo != bar", subject.isCompatibleWith(DockerImageName.parse("bar"))); } + @Test public void testNoTagTreatedAsWildcard() { final DockerImageName subject = DockerImageName.parse("foo:4.5.6"); @@ -59,7 +59,10 @@ public void testImageWithClaimedCompatibilityAndVersion() { public void testImageWithClaimedCompatibilityForFullPath() { DockerImageName subject = DockerImageName.parse("foo").asCompatibleSubstituteFor("registry/repo/bar"); - assertTrue("foo(registry/repo/bar) ~= registry/repo/bar", subject.isCompatibleWith(DockerImageName.parse("registry/repo/bar"))); + assertTrue( + "foo(registry/repo/bar) ~= registry/repo/bar", + subject.isCompatibleWith(DockerImageName.parse("registry/repo/bar")) + ); assertFalse("foo(registry/repo/bar) != repo/bar", subject.isCompatibleWith(DockerImageName.parse("repo/bar"))); assertFalse("foo(registry/repo/bar) != bar", subject.isCompatibleWith(DockerImageName.parse("bar"))); } diff --git a/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java b/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java index e06407fbf56..98fd21064ed 100644 --- a/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java +++ b/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java @@ -16,7 +16,7 @@ public static class ValidNames { @Parameterized.Parameters(name = "{0}") public static String[] getNames() { - return new String[]{ + return new String[] { "myname:latest", "repo/my-name:1.0", "registry.foo.com:1234/my-name:1.0", @@ -27,7 +27,8 @@ public static String[] getNames() { "1.2.3.4/my-name:1.0", "1.2.3.4:1234/my-name:1.0", "1.2.3.4/repo-here/my-name:1.0", - "1.2.3.4:1234/repo-here/my-name:1.0"}; + "1.2.3.4:1234/repo-here/my-name:1.0", + }; } @Parameterized.Parameter @@ -44,11 +45,13 @@ public static class InvalidNames { @Parameterized.Parameters(name = "{0}") public static String[] getNames() { - return new String[]{":latest", + return new String[] { + ":latest", "/myname:latest", "/myname@sha256:latest", "/myname@sha256:gggggggggggggggggggggggggggggggg", - "repo:notaport/myname:latest"}; + "repo:notaport/myname:latest", + }; } @Parameterized.Parameter @@ -65,39 +68,43 @@ public static class Parsing { @Parameterized.Parameters(name = "{0}{1}{2}{3}{4}") public static String[][] getNames() { - return new String[][]{ - {"", "", "myname", ":", null}, - {"", "", "myname", ":", "latest"}, - {"", "", "repo/myname", ":", null}, - {"", "", "repo/myname", ":", "latest"}, - {"registry.foo.com:1234", "/", "my-name", ":", null}, - {"registry.foo.com:1234", "/", "my-name", ":", "1.0"}, - {"registry.foo.com", "/", "my-name", ":", "1.0"}, - {"registry.foo.com:1234", "/", "repo_here/my-name", ":", null}, - {"registry.foo.com:1234", "/", "repo_here/my-name", ":", "1.0"}, - {"1.2.3.4:1234", "/", "repo_here/my-name", ":", null}, - {"1.2.3.4:1234", "/", "repo_here/my-name", ":", "1.0"}, - {"1.2.3.4:1234", "/", "my-name", ":", null}, - {"1.2.3.4:1234", "/", "my-name", ":", "1.0"}, - {"", "", "myname", "@", "sha256:1234abcd1234abcd1234abcd1234abcd"}, - {"", "", "repo/myname", "@", "sha256:1234abcd1234abcd1234abcd1234abcd"}, - {"registry.foo.com:1234", "/", "repo-here/my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd"}, - {"registry.foo.com:1234", "/", "my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd"}, - {"1.2.3.4", "/", "my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd"}, - {"1.2.3.4:1234", "/", "my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd"}, - {"1.2.3.4", "/", "my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd"}, - {"1.2.3.4:1234", "/", "my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd"} + return new String[][] { + { "", "", "myname", ":", null }, + { "", "", "myname", ":", "latest" }, + { "", "", "repo/myname", ":", null }, + { "", "", "repo/myname", ":", "latest" }, + { "registry.foo.com:1234", "/", "my-name", ":", null }, + { "registry.foo.com:1234", "/", "my-name", ":", "1.0" }, + { "registry.foo.com", "/", "my-name", ":", "1.0" }, + { "registry.foo.com:1234", "/", "repo_here/my-name", ":", null }, + { "registry.foo.com:1234", "/", "repo_here/my-name", ":", "1.0" }, + { "1.2.3.4:1234", "/", "repo_here/my-name", ":", null }, + { "1.2.3.4:1234", "/", "repo_here/my-name", ":", "1.0" }, + { "1.2.3.4:1234", "/", "my-name", ":", null }, + { "1.2.3.4:1234", "/", "my-name", ":", "1.0" }, + { "", "", "myname", "@", "sha256:1234abcd1234abcd1234abcd1234abcd" }, + { "", "", "repo/myname", "@", "sha256:1234abcd1234abcd1234abcd1234abcd" }, + { "registry.foo.com:1234", "/", "repo-here/my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd" }, + { "registry.foo.com:1234", "/", "my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd" }, + { "1.2.3.4", "/", "my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd" }, + { "1.2.3.4:1234", "/", "my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd" }, + { "1.2.3.4", "/", "my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd" }, + { "1.2.3.4:1234", "/", "my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd" }, }; } @Parameterized.Parameter(0) public String registry; + @Parameterized.Parameter(1) public String registrySeparator; + @Parameterized.Parameter(2) public String repo; + @Parameterized.Parameter(3) public String versionSeparator; + @Parameterized.Parameter(4) public String version; @@ -120,21 +127,45 @@ public void testParsing() { final DockerImageName imageName = DockerImageName.parse(combined); assertEquals(combined + " has registry address: " + registry, registry, imageName.getRegistry()); - assertEquals(combined + " has unversioned part: " + unversionedPart, unversionedPart, imageName.getUnversionedPart()); + assertEquals( + combined + " has unversioned part: " + unversionedPart, + unversionedPart, + imageName.getUnversionedPart() + ); if (version != null) { assertEquals(combined + " has version part: " + version, version, imageName.getVersionPart()); } else { - assertEquals(combined + " has automatic 'latest' version specified", "latest", imageName.getVersionPart()); + assertEquals( + combined + " has automatic 'latest' version specified", + "latest", + imageName.getVersionPart() + ); } - assertEquals(combined + " has canonical name: " + canonicalName, canonicalName, imageName.asCanonicalNameString()); + assertEquals( + combined + " has canonical name: " + canonicalName, + canonicalName, + imageName.asCanonicalNameString() + ); if (version != null) { VisibleAssertions.context("Using two-arg constructor:", 2); final DockerImageName imageNameFromSecondaryConstructor = new DockerImageName(unversionedPart, version); - assertEquals(combined + " has registry address: " + registry, registry, imageNameFromSecondaryConstructor.getRegistry()); - assertEquals(combined + " has unversioned part: " + unversionedPart, unversionedPart, imageNameFromSecondaryConstructor.getUnversionedPart()); - assertEquals(combined + " has version part: " + version, version, imageNameFromSecondaryConstructor.getVersionPart()); + assertEquals( + combined + " has registry address: " + registry, + registry, + imageNameFromSecondaryConstructor.getRegistry() + ); + assertEquals( + combined + " has unversioned part: " + unversionedPart, + unversionedPart, + imageNameFromSecondaryConstructor.getUnversionedPart() + ); + assertEquals( + combined + " has version part: " + version, + version, + imageNameFromSecondaryConstructor.getVersionPart() + ); } } } diff --git a/core/src/test/java/org/testcontainers/utility/DockerStatusTest.java b/core/src/test/java/org/testcontainers/utility/DockerStatusTest.java index 1ef8e136a32..36196aaf4c1 100644 --- a/core/src/test/java/org/testcontainers/utility/DockerStatusTest.java +++ b/core/src/test/java/org/testcontainers/utility/DockerStatusTest.java @@ -8,7 +8,7 @@ import java.time.Duration; import java.time.Instant; -import java.time.ZoneId; +import java.time.ZoneId; import java.time.format.DateTimeFormatter; import static org.junit.Assert.assertFalse; @@ -18,8 +18,9 @@ /** * */ - @RunWith(Parameterized.class) +@RunWith(Parameterized.class) public class DockerStatusTest { + private final DateTimeFormatter dateTimeFormatter; private static final Instant now = Instant.now(); @@ -55,8 +56,8 @@ public DockerStatusTest(DateTimeFormatter dateTimeFormatter) { @Parameterized.Parameters public static Object[][] parameters() { return new Object[][] { - { DateTimeFormatter.ISO_INSTANT}, - { DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(ZoneId.of("America/New_York")) } + { DateTimeFormatter.ISO_INSTANT }, + { DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(ZoneId.of("America/New_York")) }, }; } @@ -88,9 +89,12 @@ private String buildTimestamp(Instant instant) { // ContainerState is a non-static inner class, with private member variables, in a different package. // It's simpler to mock it that to try to create one. - private static InspectContainerResponse.ContainerState buildState(boolean running, boolean paused, - String startedAt, String finishedAt) { - + private static InspectContainerResponse.ContainerState buildState( + boolean running, + boolean paused, + String startedAt, + String finishedAt + ) { InspectContainerResponse.ContainerState state = Mockito.mock(InspectContainerResponse.ContainerState.class); when(state.getRunning()).thenReturn(running); when(state.getPaused()).thenReturn(paused); diff --git a/core/src/test/java/org/testcontainers/utility/FakeImageSubstitutor.java b/core/src/test/java/org/testcontainers/utility/FakeImageSubstitutor.java index f74a6c942b3..50681ddc729 100644 --- a/core/src/test/java/org/testcontainers/utility/FakeImageSubstitutor.java +++ b/core/src/test/java/org/testcontainers/utility/FakeImageSubstitutor.java @@ -1,6 +1,7 @@ package org.testcontainers.utility; public class FakeImageSubstitutor extends ImageNameSubstitutor { + @Override public DockerImageName apply(final DockerImageName original) { return DockerImageName.parse("transformed-" + original.asCanonicalNameString()); diff --git a/core/src/test/java/org/testcontainers/utility/FilterRegistryTest.java b/core/src/test/java/org/testcontainers/utility/FilterRegistryTest.java index 49bc870e8d0..30367975d1e 100644 --- a/core/src/test/java/org/testcontainers/utility/FilterRegistryTest.java +++ b/core/src/test/java/org/testcontainers/utility/FilterRegistryTest.java @@ -9,22 +9,27 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.AbstractMap.SimpleEntry; +import java.util.Arrays; import java.util.List; import java.util.Map.Entry; -import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class FilterRegistryTest { - private static final List> FILTERS = asList( - new SimpleEntry<>("key1!", "value2?"), new SimpleEntry<>("key2#", "value2%") + private static final List> FILTERS = Arrays.asList( + new SimpleEntry<>("key1!", "value2?"), + new SimpleEntry<>("key2#", "value2%") ); + private static final String URL_ENCODED_FILTERS = "key1%21=value2%3F&key2%23=value2%25"; + private static final byte[] ACKNOWLEDGEMENT = FilterRegistry.ACKNOWLEDGMENT.getBytes(); + private static final byte[] NO_ACKNOWLEDGEMENT = "".getBytes(); + private static final String NEW_LINE = "\n"; @Test @@ -66,5 +71,4 @@ private static InputStream anyInputStream() { private static OutputStream anyOutputStream() { return new ByteArrayOutputStream(); } - } diff --git a/core/src/test/java/org/testcontainers/utility/ImageNameSubstitutorTest.java b/core/src/test/java/org/testcontainers/utility/ImageNameSubstitutorTest.java index 97ee0213f21..f7e319a5948 100644 --- a/core/src/test/java/org/testcontainers/utility/ImageNameSubstitutorTest.java +++ b/core/src/test/java/org/testcontainers/utility/ImageNameSubstitutorTest.java @@ -13,7 +13,9 @@ public class ImageNameSubstitutorTest { @Rule public MockTestcontainersConfigurationRule config = new MockTestcontainersConfigurationRule(); + private ImageNameSubstitutor originalInstance; + private ImageNameSubstitutor originalDefaultImplementation; @Before @@ -27,10 +29,7 @@ public void setUp() throws Exception { .doReturn(DockerImageName.parse("substituted-image")) .when(ImageNameSubstitutor.defaultImplementation) .apply(eq(DockerImageName.parse("original"))); - Mockito - .doReturn("default implementation") - .when(ImageNameSubstitutor.defaultImplementation) - .getDescription(); + Mockito.doReturn("default implementation").when(ImageNameSubstitutor.defaultImplementation).getDescription(); } @After @@ -58,10 +57,7 @@ public void simpleConfigurationTest() { @Test public void testWorksWithoutConfiguredImplementation() { - Mockito - .doReturn(null) - .when(TestcontainersConfiguration.getInstance()) - .getImageSubstitutorClassName(); + Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getImageSubstitutorClassName(); final ImageNameSubstitutor imageNameSubstitutor = ImageNameSubstitutor.instance(); diff --git a/core/src/test/java/org/testcontainers/utility/LazyFutureTest.java b/core/src/test/java/org/testcontainers/utility/LazyFutureTest.java index 81805106806..99443221805 100644 --- a/core/src/test/java/org/testcontainers/utility/LazyFutureTest.java +++ b/core/src/test/java/org/testcontainers/utility/LazyFutureTest.java @@ -8,9 +8,9 @@ import java.util.List; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; import java.util.stream.IntStream; -import static java.util.stream.Collectors.toList; import static org.rnorth.visibleassertions.VisibleAssertions.*; public class LazyFutureTest { @@ -64,9 +64,14 @@ protected Integer resolve() { } }; - Future> task = new ForkJoinPool(numOfThreads).submit(() -> { - return IntStream.rangeClosed(1, numOfThreads).parallel().mapToObj(i -> Futures.getUnchecked(lazyFuture)).collect(toList()); - }); + Future> task = new ForkJoinPool(numOfThreads) + .submit(() -> { + return IntStream + .rangeClosed(1, numOfThreads) + .parallel() + .mapToObj(i -> Futures.getUnchecked(lazyFuture)) + .collect(Collectors.toList()); + }); while (latch.getCount() > 0) { latch.countDown(); @@ -74,5 +79,4 @@ protected Integer resolve() { assertEquals("All threads receives the same result", Collections.nCopies(numOfThreads, 1), task.get()); } - -} \ No newline at end of file +} diff --git a/core/src/test/java/org/testcontainers/utility/MountableFileTest.java b/core/src/test/java/org/testcontainers/utility/MountableFileTest.java index c7dcaad2908..d3ff085c5de 100644 --- a/core/src/test/java/org/testcontainers/utility/MountableFileTest.java +++ b/core/src/test/java/org/testcontainers/utility/MountableFileTest.java @@ -20,7 +20,9 @@ public class MountableFileTest { private static final int TEST_FILE_MODE = 0532; + private static final int BASE_FILE_MODE = 0100000; + private static final int BASE_DIR_MODE = 0040000; @Test @@ -67,7 +69,10 @@ public void forHostPathWithSpaces() throws Exception { performChecks(mountableFile); assertTrue("The resolved path contains the original space", mountableFile.getResolvedPath().contains(" ")); - assertFalse("The resolved path does not contain an escaped space", mountableFile.getResolvedPath().contains("\\ ")); + assertFalse( + "The resolved path does not contain an escaped space", + mountableFile.getResolvedPath().contains("\\ ") + ); } @Test @@ -78,13 +83,18 @@ public void forHostPathWithPlus() throws Exception { performChecks(mountableFile); assertTrue("The resolved path contains the original space", mountableFile.getResolvedPath().contains("+")); - assertFalse("The resolved path does not contain an escaped space", mountableFile.getResolvedPath().contains(" ")); + assertFalse( + "The resolved path does not contain an escaped space", + mountableFile.getResolvedPath().contains(" ") + ); } @Test public void forClasspathResourceWithPermission() throws Exception { - final MountableFile mountableFile = MountableFile.forClasspathResource("mappable-resource/test-resource.txt", - TEST_FILE_MODE); + final MountableFile mountableFile = MountableFile.forClasspathResource( + "mappable-resource/test-resource.txt", + TEST_FILE_MODE + ); performChecks(mountableFile); assertEquals("Valid file mode.", BASE_FILE_MODE | TEST_FILE_MODE, mountableFile.getFileMode()); @@ -110,7 +120,8 @@ public void forHostDirPathWithPermission() throws Exception { public void noTrailingSlashesInTarEntryNames() throws Exception { final MountableFile mountableFile = MountableFile.forClasspathResource("mappable-resource/test-resource.txt"); - @Cleanup final TarArchiveInputStream tais = intoTarArchive((taos) -> { + @Cleanup + final TarArchiveInputStream tais = intoTarArchive(taos -> { mountableFile.transferTo(taos, "/some/path.txt"); mountableFile.transferTo(taos, "/path.txt"); mountableFile.transferTo(taos, "path.txt"); @@ -123,8 +134,10 @@ public void noTrailingSlashesInTarEntryNames() throws Exception { } private TarArchiveInputStream intoTarArchive(Consumer consumer) throws IOException { - @Cleanup final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - @Cleanup final TarArchiveOutputStream taos = new TarArchiveOutputStream(baos); + @Cleanup + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + @Cleanup + final TarArchiveOutputStream taos = new TarArchiveOutputStream(baos); consumer.accept(taos); taos.close(); @@ -151,7 +164,9 @@ private Path createTempDir() throws IOException { private void performChecks(final MountableFile mountableFile) { final String mountablePath = mountableFile.getResolvedPath(); assertTrue("The filesystem path '" + mountablePath + "' can be found", new File(mountablePath).exists()); - assertFalse("The filesystem path '" + mountablePath + "' does not contain any URL escaping", mountablePath.contains("%20")); + assertFalse( + "The filesystem path '" + mountablePath + "' does not contain any URL escaping", + mountablePath.contains("%20") + ); } - } diff --git a/core/src/test/java/org/testcontainers/utility/PrefixingImageNameSubstitutorTest.java b/core/src/test/java/org/testcontainers/utility/PrefixingImageNameSubstitutorTest.java index d2c33989deb..211f8e00536 100644 --- a/core/src/test/java/org/testcontainers/utility/PrefixingImageNameSubstitutorTest.java +++ b/core/src/test/java/org/testcontainers/utility/PrefixingImageNameSubstitutorTest.java @@ -8,11 +8,11 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.testcontainers.utility.PrefixingImageNameSubstitutor.PREFIX_PROPERTY_KEY; public class PrefixingImageNameSubstitutorTest { private TestcontainersConfiguration mockConfiguration; + private PrefixingImageNameSubstitutor underTest; @Before @@ -23,7 +23,8 @@ public void setUp() { @Test public void testHappyPath() { - when(mockConfiguration.getEnvVarOrProperty(eq(PREFIX_PROPERTY_KEY), any())).thenReturn("someregistry.com/our-mirror/"); + when(mockConfiguration.getEnvVarOrProperty(eq(PrefixingImageNameSubstitutor.PREFIX_PROPERTY_KEY), any())) + .thenReturn("someregistry.com/our-mirror/"); final DockerImageName result = underTest.apply(DockerImageName.parse("some/image:tag")); @@ -36,33 +37,28 @@ public void testHappyPath() { @Test public void hubIoRegistryIsNotChanged() { - when(mockConfiguration.getEnvVarOrProperty(eq(PREFIX_PROPERTY_KEY), any())).thenReturn("someregistry.com/our-mirror/"); + when(mockConfiguration.getEnvVarOrProperty(eq(PrefixingImageNameSubstitutor.PREFIX_PROPERTY_KEY), any())) + .thenReturn("someregistry.com/our-mirror/"); final DockerImageName result = underTest.apply(DockerImageName.parse("docker.io/some/image:tag")); - assertEquals( - "The prefix is applied", - "docker.io/some/image:tag", - result.asCanonicalNameString() - ); + assertEquals("The prefix is applied", "docker.io/some/image:tag", result.asCanonicalNameString()); } @Test public void hubComRegistryIsNotChanged() { - when(mockConfiguration.getEnvVarOrProperty(eq(PREFIX_PROPERTY_KEY), any())).thenReturn("someregistry.com/our-mirror/"); + when(mockConfiguration.getEnvVarOrProperty(eq(PrefixingImageNameSubstitutor.PREFIX_PROPERTY_KEY), any())) + .thenReturn("someregistry.com/our-mirror/"); final DockerImageName result = underTest.apply(DockerImageName.parse("registry.hub.docker.com/some/image:tag")); - assertEquals( - "The prefix is applied", - "registry.hub.docker.com/some/image:tag", - result.asCanonicalNameString() - ); + assertEquals("The prefix is applied", "registry.hub.docker.com/some/image:tag", result.asCanonicalNameString()); } @Test public void thirdPartyRegistriesNotAffected() { - when(mockConfiguration.getEnvVarOrProperty(eq(PREFIX_PROPERTY_KEY), any())).thenReturn("someregistry.com/our-mirror/"); + when(mockConfiguration.getEnvVarOrProperty(eq(PrefixingImageNameSubstitutor.PREFIX_PROPERTY_KEY), any())) + .thenReturn("someregistry.com/our-mirror/"); final DockerImageName result = underTest.apply(DockerImageName.parse("gcr.io/something/image:tag")); @@ -75,7 +71,8 @@ public void thirdPartyRegistriesNotAffected() { @Test public void testNoDoublePrefixing() { - when(mockConfiguration.getEnvVarOrProperty(eq(PREFIX_PROPERTY_KEY), any())).thenReturn("someregistry.com/our-mirror/"); + when(mockConfiguration.getEnvVarOrProperty(eq(PrefixingImageNameSubstitutor.PREFIX_PROPERTY_KEY), any())) + .thenReturn("someregistry.com/our-mirror/"); final DockerImageName result = underTest.apply(DockerImageName.parse("someregistry.com/some/image:tag")); @@ -88,7 +85,8 @@ public void testNoDoublePrefixing() { @Test public void testHandlesEmptyValue() { - when(mockConfiguration.getEnvVarOrProperty(eq(PREFIX_PROPERTY_KEY), any())).thenReturn(""); + when(mockConfiguration.getEnvVarOrProperty(eq(PrefixingImageNameSubstitutor.PREFIX_PROPERTY_KEY), any())) + .thenReturn(""); final DockerImageName result = underTest.apply(DockerImageName.parse("some/image:tag")); @@ -101,39 +99,38 @@ public void testHandlesEmptyValue() { @Test public void testHandlesRegistryOnlyWithTrailingSlash() { - when(mockConfiguration.getEnvVarOrProperty(eq(PREFIX_PROPERTY_KEY), any())).thenReturn("someregistry.com/"); + when(mockConfiguration.getEnvVarOrProperty(eq(PrefixingImageNameSubstitutor.PREFIX_PROPERTY_KEY), any())) + .thenReturn("someregistry.com/"); final DockerImageName result = underTest.apply(DockerImageName.parse("some/image:tag")); - assertEquals( - "The prefix is applied", - "someregistry.com/some/image:tag", - result.asCanonicalNameString() - ); + assertEquals("The prefix is applied", "someregistry.com/some/image:tag", result.asCanonicalNameString()); } @Test public void testCombinesLiterallyForRegistryOnlyWithoutTrailingSlash() { - when(mockConfiguration.getEnvVarOrProperty(eq(PREFIX_PROPERTY_KEY), any())).thenReturn("someregistry.com"); + when(mockConfiguration.getEnvVarOrProperty(eq(PrefixingImageNameSubstitutor.PREFIX_PROPERTY_KEY), any())) + .thenReturn("someregistry.com"); final DockerImageName result = underTest.apply(DockerImageName.parse("some/image:tag")); assertEquals( "The prefix is applied", - "someregistry.comsome/image:tag", // treating the prefix literally, for predictability + "someregistry.comsome/image:tag", // treating the prefix literally, for predictability result.asCanonicalNameString() ); } @Test public void testCombinesLiterallyForBothPartsWithoutTrailingSlash() { - when(mockConfiguration.getEnvVarOrProperty(eq(PREFIX_PROPERTY_KEY), any())).thenReturn("someregistry.com/our-mirror"); + when(mockConfiguration.getEnvVarOrProperty(eq(PrefixingImageNameSubstitutor.PREFIX_PROPERTY_KEY), any())) + .thenReturn("someregistry.com/our-mirror"); final DockerImageName result = underTest.apply(DockerImageName.parse("some/image:tag")); assertEquals( "The prefix is applied", - "someregistry.com/our-mirrorsome/image:tag", // treating the prefix literally, for predictability + "someregistry.com/our-mirrorsome/image:tag", // treating the prefix literally, for predictability result.asCanonicalNameString() ); } diff --git a/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java b/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java index 5db2583958a..4ddfad8454f 100644 --- a/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java +++ b/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java @@ -14,13 +14,21 @@ import static org.rnorth.visibleassertions.VisibleAssertions.*; public class RegistryAuthLocatorTest { + @Test public void lookupAuthConfigWithoutCredentials() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-empty.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(DockerImageName.parse("unauthenticated.registry.org/org/repo"), new AuthConfig()); + final AuthConfig authConfig = authLocator.lookupAuthConfig( + DockerImageName.parse("unauthenticated.registry.org/org/repo"), + new AuthConfig() + ); - assertEquals("Default docker registry URL is set on auth config", "https://index.docker.io/v1/", authConfig.getRegistryAddress()); + assertEquals( + "Default docker registry URL is set on auth config", + "https://index.docker.io/v1/", + authConfig.getRegistryAddress() + ); assertNull("No username is set", authConfig.getUsername()); assertNull("No password is set", authConfig.getPassword()); } @@ -29,9 +37,16 @@ public void lookupAuthConfigWithoutCredentials() throws URISyntaxException { public void lookupAuthConfigWithBasicAuthCredentials() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-basic-auth.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(DockerImageName.parse("registry.example.com/org/repo"), new AuthConfig()); + final AuthConfig authConfig = authLocator.lookupAuthConfig( + DockerImageName.parse("registry.example.com/org/repo"), + new AuthConfig() + ); - assertEquals("Default docker registry URL is set on auth config", "https://registry.example.com", authConfig.getRegistryAddress()); + assertEquals( + "Default docker registry URL is set on auth config", + "https://registry.example.com", + authConfig.getRegistryAddress() + ); assertEquals("Username is set", "user", authConfig.getUsername()); assertEquals("Password is set", "pass", authConfig.getPassword()); } @@ -40,9 +55,16 @@ public void lookupAuthConfigWithBasicAuthCredentials() throws URISyntaxException public void lookupAuthConfigWithJsonKeyCredentials() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-with-json-key.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(DockerImageName.parse("registry.example.com/org/repo"), new AuthConfig()); + final AuthConfig authConfig = authLocator.lookupAuthConfig( + DockerImageName.parse("registry.example.com/org/repo"), + new AuthConfig() + ); - assertEquals("Default docker registry URL is set on auth config", "https://registry.example.com", authConfig.getRegistryAddress()); + assertEquals( + "Default docker registry URL is set on auth config", + "https://registry.example.com", + authConfig.getRegistryAddress() + ); assertEquals("Username is set", "_json_key", authConfig.getUsername()); assertNotNull("Password is set", authConfig.getPassword()); } @@ -51,7 +73,10 @@ public void lookupAuthConfigWithJsonKeyCredentials() throws URISyntaxException { public void lookupAuthConfigUsingStore() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-with-store.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(DockerImageName.parse("registry.example.com/org/repo"), new AuthConfig()); + final AuthConfig authConfig = authLocator.lookupAuthConfig( + DockerImageName.parse("registry.example.com/org/repo"), + new AuthConfig() + ); assertEquals("Correct server URL is obtained from a credential store", "url", authConfig.getRegistryAddress()); assertEquals("Correct username is obtained from a credential store", "username", authConfig.getUsername()); @@ -62,7 +87,10 @@ public void lookupAuthConfigUsingStore() throws URISyntaxException { public void lookupAuthConfigUsingHelper() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-with-helper.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(DockerImageName.parse("registry.example.com/org/repo"), new AuthConfig()); + final AuthConfig authConfig = authLocator.lookupAuthConfig( + DockerImageName.parse("registry.example.com/org/repo"), + new AuthConfig() + ); assertEquals("Correct server URL is obtained from a credential store", "url", authConfig.getRegistryAddress()); assertEquals("Correct username is obtained from a credential store", "username", authConfig.getUsername()); @@ -73,16 +101,26 @@ public void lookupAuthConfigUsingHelper() throws URISyntaxException { public void lookupAuthConfigUsingHelperWithToken() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-with-helper-using-token.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(DockerImageName.parse("registrytoken.example.com/org/repo"), new AuthConfig()); + final AuthConfig authConfig = authLocator.lookupAuthConfig( + DockerImageName.parse("registrytoken.example.com/org/repo"), + new AuthConfig() + ); - assertEquals("Correct identitytoken is obtained from a credential store", "secret", authConfig.getIdentitytoken()); + assertEquals( + "Correct identitytoken is obtained from a credential store", + "secret", + authConfig.getIdentitytoken() + ); } @Test public void lookupUsingHelperEmptyAuth() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-empty-auth-with-helper.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(DockerImageName.parse("registry.example.com/org/repo"), new AuthConfig()); + final AuthConfig authConfig = authLocator.lookupAuthConfig( + DockerImageName.parse("registry.example.com/org/repo"), + new AuthConfig() + ); assertEquals("Correct server URL is obtained from a credential store", "url", authConfig.getRegistryAddress()); assertEquals("Correct username is obtained from a credential store", "username", authConfig.getUsername()); @@ -93,7 +131,10 @@ public void lookupUsingHelperEmptyAuth() throws URISyntaxException { public void lookupNonEmptyAuthWithHelper() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-existing-auth-with-helper.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(DockerImageName.parse("registry.example.com/org/repo"), new AuthConfig()); + final AuthConfig authConfig = authLocator.lookupAuthConfig( + DockerImageName.parse("registry.example.com/org/repo"), + new AuthConfig() + ); assertEquals("Correct server URL is obtained from a credential helper", "url", authConfig.getRegistryAddress()); assertEquals("Correct username is obtained from a credential helper", "username", authConfig.getUsername()); @@ -103,7 +144,10 @@ public void lookupNonEmptyAuthWithHelper() throws URISyntaxException { @Test public void lookupAuthConfigWithCredentialsNotFound() throws URISyntaxException { Map notFoundMessagesReference = new HashMap<>(); - final RegistryAuthLocator authLocator = createTestAuthLocator("config-with-store.json", notFoundMessagesReference); + final RegistryAuthLocator authLocator = createTestAuthLocator( + "config-with-store.json", + notFoundMessagesReference + ); DockerImageName dockerImageName = DockerImageName.parse("registry2.example.com/org/repo"); final AuthConfig authConfig = authLocator.lookupAuthConfig(dockerImageName, new AuthConfig()); @@ -117,7 +161,8 @@ public void lookupAuthConfigWithCredentialsNotFound() throws URISyntaxException assertEquals( "Not correct message discovered", "Fake credentials not found on credentials store 'https://not.a.real.registry/url'", - discoveredMessage); + discoveredMessage + ); } @Test @@ -136,7 +181,8 @@ private RegistryAuthLocator createTestAuthLocator(String configName) throws URIS } @NotNull - private RegistryAuthLocator createTestAuthLocator(String configName, Map notFoundMessagesReference) throws URISyntaxException { + private RegistryAuthLocator createTestAuthLocator(String configName, Map notFoundMessagesReference) + throws URISyntaxException { final File configFile = new File(Resources.getResource("auth-config/" + configName).toURI()); String commandPathPrefix = configFile.getParentFile().getAbsolutePath() + "/"; diff --git a/core/src/test/java/org/testcontainers/utility/ResourceReaperTest.java b/core/src/test/java/org/testcontainers/utility/ResourceReaperTest.java index 214b465c297..60f57a28b3e 100644 --- a/core/src/test/java/org/testcontainers/utility/ResourceReaperTest.java +++ b/core/src/test/java/org/testcontainers/utility/ResourceReaperTest.java @@ -42,25 +42,27 @@ public void shouldCleanupWithJVM() { private void assertCleanup(Map labels) { DockerClient client = DockerClientFactory.instance().client(); - ConditionFactory awaitFactory = Awaitility.await().atMost(Duration.ofMinutes(1)).pollInterval(Duration.ofSeconds(1)); - - List labelValues = labels.entrySet().stream() + ConditionFactory awaitFactory = Awaitility + .await() + .atMost(Duration.ofMinutes(1)) + .pollInterval(Duration.ofSeconds(1)); + + List labelValues = labels + .entrySet() + .stream() .map(it -> it.getKey() + "=" + it.getValue()) .collect(Collectors.toList()); awaitFactory.untilAsserted(() -> { - assertThat(client.listContainersCmd().withFilter("label", labelValues).withShowAll(true).exec()) - .isEmpty(); + assertThat(client.listContainersCmd().withFilter("label", labelValues).withShowAll(true).exec()).isEmpty(); }); awaitFactory.untilAsserted(() -> { - assertThat(client.listNetworksCmd().withFilter("label", labelValues).exec()) - .isEmpty(); + assertThat(client.listNetworksCmd().withFilter("label", labelValues).exec()).isEmpty(); }); awaitFactory.untilAsserted(() -> { - assertThat(client.listVolumesCmd().withFilter("label", labelValues).exec().getVolumes()) - .isEmpty(); + assertThat(client.listVolumesCmd().withFilter("label", labelValues).exec().getVolumes()).isEmpty(); }); } @@ -81,7 +83,8 @@ private Map runProcess(Consumer processExecutor ProcessResult result = processExecutor.execute(); assertEquals(0, result.getExitValue()); - String labelsJson = Stream.of(result.outputUTF8().split("\n")) + String labelsJson = Stream + .of(result.outputUTF8().split("\n")) .filter(it -> it.startsWith(SimpleUsage.LABELS_MARKER)) .map(it -> it.substring(SimpleUsage.LABELS_MARKER.length())) .findFirst() @@ -97,7 +100,9 @@ public static class SimpleUsage { @SneakyThrows @SuppressWarnings("deprecation") public static void main(String[] args) { - System.out.println(LABELS_MARKER + new ObjectMapper().writeValueAsString(ResourceReaper.instance().getLabels())); + System.out.println( + LABELS_MARKER + new ObjectMapper().writeValueAsString(ResourceReaper.instance().getLabels()) + ); GenericContainer container = new GenericContainer<>("testcontainers/helloworld:1.1.0") .withNetwork(org.testcontainers.containers.Network.newNetwork()) diff --git a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java index 7ca656e1eaf..56cbdb98a56 100644 --- a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java +++ b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java @@ -15,7 +15,9 @@ public class TestcontainersConfigurationTest { private Properties userProperties; + private Properties classpathProperties; + private Map environment; @Before @@ -117,21 +119,33 @@ public void shouldReadChecksFromEnvironment() { public void shouldReadDockerSettingsFromEnvironmentWithoutTestcontainersPrefix() { userProperties.remove("docker.foo"); environment.put("DOCKER_FOO", "some value"); - assertEquals("reads unprefixed env vars for docker. settings", "some value", newConfig().getEnvVarOrUserProperty("docker.foo", "default")); + assertEquals( + "reads unprefixed env vars for docker. settings", + "some value", + newConfig().getEnvVarOrUserProperty("docker.foo", "default") + ); } @Test public void shouldNotReadDockerSettingsFromEnvironmentWithTestcontainersPrefix() { userProperties.remove("docker.foo"); environment.put("TESTCONTAINERS_DOCKER_FOO", "some value"); - assertEquals("reads unprefixed env vars for docker. settings", "default", newConfig().getEnvVarOrUserProperty("docker.foo", "default")); + assertEquals( + "reads unprefixed env vars for docker. settings", + "default", + newConfig().getEnvVarOrUserProperty("docker.foo", "default") + ); } @Test public void shouldReadDockerSettingsFromUserProperties() { environment.remove("DOCKER_FOO"); userProperties.put("docker.foo", "some value"); - assertEquals("reads unprefixed user properties for docker. settings", "some value", newConfig().getEnvVarOrUserProperty("docker.foo", "default")); + assertEquals( + "reads unprefixed user properties for docker. settings", + "some value", + newConfig().getEnvVarOrUserProperty("docker.foo", "default") + ); } @Test @@ -139,41 +153,69 @@ public void shouldNotReadDockerClientStrategyFromClasspathProperties() { String currentValue = newConfig().getDockerClientStrategyClassName(); classpathProperties.setProperty("docker.client.strategy", UUID.randomUUID().toString()); - assertEquals("Docker client strategy is not affected by classpath properties", currentValue, newConfig().getDockerClientStrategyClassName()); + assertEquals( + "Docker client strategy is not affected by classpath properties", + currentValue, + newConfig().getDockerClientStrategyClassName() + ); } @Test public void shouldReadDockerClientStrategyFromUserProperties() { userProperties.setProperty("docker.client.strategy", "foo"); - assertEquals("Docker client strategy is changed by user property", "foo", newConfig().getDockerClientStrategyClassName()); + assertEquals( + "Docker client strategy is changed by user property", + "foo", + newConfig().getDockerClientStrategyClassName() + ); } @Test public void shouldReadDockerClientStrategyFromEnvironment() { userProperties.remove("docker.client.strategy"); environment.put("TESTCONTAINERS_DOCKER_CLIENT_STRATEGY", "foo"); - assertEquals("Docker client strategy is changed by env var", "foo", newConfig().getDockerClientStrategyClassName()); + assertEquals( + "Docker client strategy is changed by env var", + "foo", + newConfig().getDockerClientStrategyClassName() + ); } @Test public void shouldNotUseImplicitDockerClientStrategyWhenDockerHostAndStrategyAreBothSet() { userProperties.put("docker.client.strategy", "foo"); userProperties.put("docker.host", "tcp://1.2.3.4:5678"); - assertEquals("Docker client strategy is can be explicitly set", "foo", newConfig().getDockerClientStrategyClassName()); + assertEquals( + "Docker client strategy is can be explicitly set", + "foo", + newConfig().getDockerClientStrategyClassName() + ); userProperties.remove("docker.client.strategy"); environment.put("TESTCONTAINERS_DOCKER_CLIENT_STRATEGY", "bar"); userProperties.put("docker.client.strategy", "foo"); - assertEquals("Docker client strategy is can be explicitly set", "bar", newConfig().getDockerClientStrategyClassName()); + assertEquals( + "Docker client strategy is can be explicitly set", + "bar", + newConfig().getDockerClientStrategyClassName() + ); environment.put("TESTCONTAINERS_DOCKER_CLIENT_STRATEGY", "bar"); userProperties.remove("docker.client.strategy"); - assertEquals("Docker client strategy is can be explicitly set", "bar", newConfig().getDockerClientStrategyClassName()); + assertEquals( + "Docker client strategy is can be explicitly set", + "bar", + newConfig().getDockerClientStrategyClassName() + ); environment.remove("TESTCONTAINERS_DOCKER_CLIENT_STRATEGY"); userProperties.put("docker.client.strategy", "foo"); - assertEquals("Docker client strategy is can be explicitly set", "foo", newConfig().getDockerClientStrategyClassName()); + assertEquals( + "Docker client strategy is can be explicitly set", + "foo", + newConfig().getDockerClientStrategyClassName() + ); } @Test @@ -191,6 +233,7 @@ public void shouldReadReuseFromUserProperties() { userProperties.setProperty("testcontainers.reuse.enable", "true"); assertTrue("reuse enabled via user property", newConfig().environmentSupportsReuse()); } + @Test public void shouldReadReuseFromEnvironment() { assertFalse("no reuse by default", newConfig().environmentSupportsReuse()); @@ -203,7 +246,11 @@ public void shouldReadReuseFromEnvironment() { @Test public void shouldTrimImageNames() { userProperties.setProperty("ryuk.container.image", " testcontainers/ryuk:0.3.2 "); - assertEquals("trailing whitespace was not removed from image name property", "testcontainers/ryuk:0.3.2",newConfig().getRyukImage()); + assertEquals( + "trailing whitespace was not removed from image name property", + "testcontainers/ryuk:0.3.2", + newConfig().getRyukImage() + ); } private TestcontainersConfiguration newConfig() { diff --git a/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java b/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java index 5306426070f..218ed90e5e0 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java @@ -19,21 +19,26 @@ public class CmdModifierTest { // hostname { @Rule public GenericContainer theCache = new GenericContainer<>(DockerImageName.parse("redis:3.0.2")) - .withCreateContainerCmdModifier(cmd -> cmd.withHostName("the-cache")); + .withCreateContainerCmdModifier(cmd -> cmd.withHostName("the-cache")); + // } + // spotless:off // memory { private long memoryInBytes = 32 * 1024 * 1024; + private long memorySwapInBytes = 64 * 1024 * 1024; @Rule public GenericContainer memoryLimitedRedis = new GenericContainer<>(DockerImageName.parse("redis:3.0.2")) - .withCreateContainerCmdModifier(cmd -> cmd.getHostConfig() + .withCreateContainerCmdModifier(cmd -> { + cmd.getHostConfig() .withMemory(memoryInBytes) - .withMemorySwap(memorySwapInBytes) - ); - // } + .withMemorySwap(memorySwapInBytes); + }); + // } + // spotless:on @Test public void testHostnameModified() throws IOException, InterruptedException { diff --git a/docs/examples/junit4/generic/src/test/java/generic/CommandsTest.java b/docs/examples/junit4/generic/src/test/java/generic/CommandsTest.java index 23b265c26fa..80292465521 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/CommandsTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/CommandsTest.java @@ -8,11 +8,12 @@ import static org.junit.Assert.assertTrue; public class CommandsTest { + @Rule // startupCommand { public GenericContainer redisWithCustomPort = new GenericContainer(DockerImageName.parse("redis:5.0")) .withCommand("redis-server --port 7777") - // } + // } .withExposedPorts(7777); @Test diff --git a/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java b/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java index 83b45f0435d..bdd82968191 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java @@ -9,31 +9,33 @@ public class ContainerCreationTest { + // spotless:off // simple { public static final DockerImageName REDIS_IMAGE = DockerImageName.parse("redis:3.0.2"); - // } - // simple { @ClassRule - public static GenericContainer redis = - new GenericContainer<>(REDIS_IMAGE) - .withExposedPorts(6379); + public static GenericContainer redis = new GenericContainer<>(REDIS_IMAGE) + .withExposedPorts(6379); + // } + // spotless:on public static final DockerImageName ALPINE_IMAGE = DockerImageName.parse("alpine:3.14"); + // spotless:off // withOptions { // Set up a plain OS container and customize environment, // command and exposed ports. This just listens on port 80 // and always returns '42' @ClassRule - public static GenericContainer alpine = - new GenericContainer<>(ALPINE_IMAGE) - .withExposedPorts(80) - .withEnv("MAGIC_NUMBER", "42") - .withCommand("/bin/sh", "-c", - "while true; do echo \"$MAGIC_NUMBER\" | nc -l -p 80; done"); + public static GenericContainer alpine = new GenericContainer<>(ALPINE_IMAGE) + .withExposedPorts(80) + .withEnv("MAGIC_NUMBER", "42") + .withCommand("/bin/sh", "-c", + "while true; do echo \"$MAGIC_NUMBER\" | nc -l -p 80; done"); + // } + // spotless:on @Test public void testStartup() { diff --git a/docs/examples/junit4/generic/src/test/java/generic/ContainerLabelTest.java b/docs/examples/junit4/generic/src/test/java/generic/ContainerLabelTest.java index 08d57e31057..64618ad25a9 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/ContainerLabelTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/ContainerLabelTest.java @@ -7,12 +7,12 @@ import java.util.Map; public class ContainerLabelTest { + // single_label { public GenericContainer containerWithLabel = new GenericContainer(DockerImageName.parse("alpine:3.14")) .withLabel("key", "value"); // } - // multiple_labels { private Map mapOfLabels = new HashMap<>(); // populate map, e.g. mapOfLabels.put("key1", "value1"); diff --git a/docs/examples/junit4/generic/src/test/java/generic/HostPortExposedTest.java b/docs/examples/junit4/generic/src/test/java/generic/HostPortExposedTest.java index 1d15c2553b8..22bf0332a9d 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/HostPortExposedTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/HostPortExposedTest.java @@ -18,19 +18,23 @@ public class HostPortExposedTest { private static HttpServer server; + private static int localServerPort; @BeforeClass public static void setUp() throws Exception { server = HttpServer.create(new InetSocketAddress(0), 0); - server.createContext("/", exchange -> { - byte[] content = "Hello World!".getBytes(); - exchange.sendResponseHeaders(200, content.length); - try (OutputStream responseBody = exchange.getResponseBody()) { - responseBody.write(content); - responseBody.flush(); + server.createContext( + "/", + exchange -> { + byte[] content = "Hello World!".getBytes(); + exchange.sendResponseHeaders(200, content.length); + try (OutputStream responseBody = exchange.getResponseBody()) { + responseBody.write(content); + responseBody.flush(); + } } - }); + ); server.start(); localServerPort = server.getAddress().getPort(); @@ -46,14 +50,12 @@ public static void tearDown() throws Exception { } @Rule - public BrowserWebDriverContainer browser = new BrowserWebDriverContainer() - .withCapabilities(new ChromeOptions()); + public BrowserWebDriverContainer browser = new BrowserWebDriverContainer().withCapabilities(new ChromeOptions()); @Test public void testContainerRunningAgainstExposedHostPort() { // useHostExposedPort { - final String rootUrl = - String.format("http://host.testcontainers.internal:%d/", localServerPort); + final String rootUrl = String.format("http://host.testcontainers.internal:%d/", localServerPort); final RemoteWebDriver webDriver = browser.getWebDriver(); webDriver.get(rootUrl); diff --git a/docs/examples/junit4/generic/src/test/java/generic/ImageNameSubstitutionTest.java b/docs/examples/junit4/generic/src/test/java/generic/ImageNameSubstitutionTest.java index 65da67fd401..afbe38fe835 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/ImageNameSubstitutionTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/ImageNameSubstitutionTest.java @@ -1,6 +1,5 @@ package generic; - import generic.support.TestSpecificImageNameSubstitutor; import org.junit.Test; import org.testcontainers.containers.MySQLContainer; @@ -11,14 +10,15 @@ public class ImageNameSubstitutionTest { @Test public void simpleExample() { try ( + // spotless:off // directDockerHubReference { // Referring directly to an image on Docker Hub (mysql:8.0.24) final MySQLContainer mysql = new MySQLContainer<>( DockerImageName.parse("mysql:8.0.24") ) - // start the container and use it for testing // } + // spotless:on ) { mysql.start(); } @@ -32,15 +32,16 @@ public void simpleExample() { @Test public void substitutedExample() { try ( + // spotless:off // hardcodedMirror { // Referring directly to an image on a private registry - image name will vary final MySQLContainer mysql = new MySQLContainer<>( DockerImageName.parse("registry.mycompany.com/mirror/mysql:8.0.24") - .asCompatibleSubstituteFor("mysql") + .asCompatibleSubstituteFor("mysql") ) - // start the container and use it for testing // } + // spotless:on ) { mysql.start(); } diff --git a/docs/examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java b/docs/examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java index de2b56c6a63..49e62cff217 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java @@ -1,23 +1,25 @@ package generic; -import static org.slf4j.LoggerFactory.getLogger; - import org.junit.Rule; import org.junit.Test; import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.output.Slf4jLogConsumer; import org.testcontainers.utility.DockerImageName; public class MultiplePortsExposedTest { - private static final Logger log = getLogger(MultiplePortsExposedTest.class); + private static final Logger log = LoggerFactory.getLogger(MultiplePortsExposedTest.class); @Rule // rule { - public GenericContainer container = new GenericContainer<>(DockerImageName.parse("testcontainers/helloworld:1.1.0")) + public GenericContainer container = new GenericContainer<>( + DockerImageName.parse("testcontainers/helloworld:1.1.0") + ) .withExposedPorts(8080, 8081) .withLogConsumer(new Slf4jLogConsumer(log)); + // } @Test @@ -38,7 +40,6 @@ public void getHostOnly() { @Test public void getHostAndMappedPort() { - String address = - container.getHost() + ":" + container.getMappedPort(8080); + String address = container.getHost() + ":" + container.getMappedPort(8080); } } diff --git a/docs/examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java b/docs/examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java index d45b1e28c47..bdc0d3ced8a 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java @@ -14,8 +14,9 @@ public class WaitStrategiesTest { @Rule // waitForNetworkListening { - public GenericContainer nginx = new GenericContainer(DockerImageName.parse("nginx:1.9.4")) + public GenericContainer nginx = new GenericContainer(DockerImageName.parse("nginx:1.9.4")) // .withExposedPorts(80); + // } @Rule @@ -23,44 +24,48 @@ public class WaitStrategiesTest { public GenericContainer nginxWithHttpWait = new GenericContainer(DockerImageName.parse("nginx:1.9.4")) .withExposedPorts(80) .waitingFor(Wait.forHttp("/")); + // } @Rule // logMessageWait { public GenericContainer containerWithLogWait = new GenericContainer(DockerImageName.parse("redis:5.0.3")) .withExposedPorts(6379) - .waitingFor( - Wait.forLogMessage(".*Ready to accept connections.*\\n", 1) - ); + .waitingFor(Wait.forLogMessage(".*Ready to accept connections.*\\n", 1)); + // } private static final HttpWaitStrategy MULTI_CODE_HTTP_WAIT = + // spotless:off // waitForHttpWithMultipleStatusCodes { Wait.forHttp("/") .forStatusCode(200) - .forStatusCode(301) + .forStatusCode(301); // } - ; + // spotless:on private static final HttpWaitStrategy PREDICATE_HTTP_WAIT = + // spotless:off // waitForHttpWithStatusCodePredicate { Wait.forHttp("/all") - .forStatusCodeMatching(it -> it >= 200 && it < 300 || it == 401) + .forStatusCodeMatching(it -> it >= 200 && it < 300 || it == 401); // } - ; + // spotless:on private static final HttpWaitStrategy TLS_HTTP_WAIT = + // spotless:off // waitForHttpWithTls { Wait.forHttp("/all") - .usingTls() + .usingTls(); // } - ; + // spotless:on private static final WaitStrategy HEALTHCHECK_WAIT = + // spotless:off // healthcheckWait { - Wait.forHealthcheck() + Wait.forHealthcheck(); // } - ; + // spotless:on @Test public void testContainersAllStarted() { diff --git a/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java b/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java index b60225010a1..82e3d83ab24 100644 --- a/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java +++ b/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java @@ -6,6 +6,7 @@ import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.output.WaitingConsumer; import org.testcontainers.utility.DockerImageName; @@ -14,34 +15,39 @@ import java.util.concurrent.TimeoutException; import static org.assertj.core.api.Assertions.assertThat; -import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; -import static org.testcontainers.containers.startupcheck.StartupCheckStrategyTest.IndefiniteOneShotStrategyTest; -import static org.testcontainers.containers.startupcheck.StartupCheckStrategyTest.MinimumDurationStrategyTest; -import static org.testcontainers.containers.startupcheck.StartupCheckStrategyTest.OneShotStrategyTest; @RunWith(Suite.class) -@Suite.SuiteClasses({OneShotStrategyTest.class, IndefiniteOneShotStrategyTest.class, MinimumDurationStrategyTest.class}) +@Suite.SuiteClasses( + { + StartupCheckStrategyTest.OneShotStrategyTest.class, + StartupCheckStrategyTest.IndefiniteOneShotStrategyTest.class, + StartupCheckStrategyTest.MinimumDurationStrategyTest.class, + } +) public class StartupCheckStrategyTest { private static final String HELLO_TESTCONTAINERS = "Hello Testcontainers!"; private static void waitForHello(GenericContainer container) throws TimeoutException { WaitingConsumer consumer = new WaitingConsumer(); - container.followOutput(consumer, STDOUT); + container.followOutput(consumer, OutputFrame.OutputType.STDOUT); - consumer.waitUntil(frame -> - frame.getUtf8String().contains(HELLO_TESTCONTAINERS), 30, TimeUnit.SECONDS); + consumer.waitUntil(frame -> frame.getUtf8String().contains(HELLO_TESTCONTAINERS), 30, TimeUnit.SECONDS); } public static class OneShotStrategyTest { + @Rule + // spotless:off // withOneShotStrategy { public GenericContainer bboxWithOneShot = new GenericContainer<>(DockerImageName.parse("busybox:1.31.1")) .withCommand(String.format("echo %s", HELLO_TESTCONTAINERS)) .withStartupCheckStrategy( new OneShotStartupCheckStrategy().withTimeout(Duration.ofSeconds(3)) ); + // } + // spotless:on @SneakyThrows @Test @@ -53,14 +59,20 @@ public void testCommandIsExecuted() { } public static class IndefiniteOneShotStrategyTest { + @Rule + // spotless:off // withIndefiniteOneShotStrategy { - public GenericContainer bboxWithIndefiniteOneShot = new GenericContainer<>(DockerImageName.parse("busybox:1.31.1")) + public GenericContainer bboxWithIndefiniteOneShot = new GenericContainer<>( + DockerImageName.parse("busybox:1.31.1") + ) .withCommand("sh", "-c", String.format("sleep 5 && echo \"%s\"", HELLO_TESTCONTAINERS)) .withStartupCheckStrategy( new IndefiniteWaitOneShotStartupCheckStrategy() ); + // } + // spotless:on @SneakyThrows @Test @@ -72,14 +84,20 @@ public void testCommandIsExecuted() { } public static class MinimumDurationStrategyTest { + @Rule + // spotless:off // withMinimumDurationStrategy { - public GenericContainer bboxWithMinimumDuration = new GenericContainer<>(DockerImageName.parse("busybox:1.31.1")) + public GenericContainer bboxWithMinimumDuration = new GenericContainer<>( + DockerImageName.parse("busybox:1.31.1") + ) .withCommand("sh", "-c", String.format("sleep 5 && echo \"%s\"", HELLO_TESTCONTAINERS)) .withStartupCheckStrategy( new MinimumDurationRunningStartupCheckStrategy(Duration.ofSeconds(1)) ); + // } + // spotless:on @SneakyThrows @Test diff --git a/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java b/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java index cbb49b5fdb3..1d3d178975f 100644 --- a/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java +++ b/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java @@ -1,6 +1,5 @@ package quickstart; - import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -16,9 +15,9 @@ public class RedisBackedCacheIntTest { // rule { @Rule public GenericContainer redis = new GenericContainer(DockerImageName.parse("redis:5.0.3-alpine")) - .withExposedPorts(6379); - // } + .withExposedPorts(6379); + // } @Before public void setUp() { diff --git a/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java b/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java index 61a86ac3f8b..f837b3af0d5 100644 --- a/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java +++ b/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java @@ -1,6 +1,5 @@ package quickstart; - import org.junit.Before; import org.junit.Ignore; import org.junit.Test; @@ -9,6 +8,7 @@ @Ignore("This test class is deliberately invalid, as it relies on a non-existent local Redis") public class RedisBackedCacheIntTestStep0 { + private RedisBackedCache underTest; @Before diff --git a/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java b/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java index 9a3855ba23c..59d92210549 100644 --- a/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java +++ b/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java @@ -1,6 +1,5 @@ package quickstart; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.testcontainers.containers.GenericContainer; @@ -19,9 +18,9 @@ public class RedisBackedCacheIntTest { // container { @Container public GenericContainer redis = new GenericContainer(DockerImageName.parse("redis:5.0.3-alpine")) - .withExposedPorts(6379); - // } + .withExposedPorts(6379); + // } @BeforeEach public void setUp() { diff --git a/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java b/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java index c81a185a188..d9f889a8943 100644 --- a/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java +++ b/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java @@ -1,6 +1,5 @@ package quickstart; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -9,6 +8,7 @@ @Disabled("This test class is deliberately invalid, as it relies on a non-existent local Redis") public class RedisBackedCacheIntTestStep0 { + private RedisBackedCache underTest; @BeforeEach diff --git a/docs/examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy b/docs/examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy index 068c8079a7d..5cc04dc6b9d 100644 --- a/docs/examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy +++ b/docs/examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy @@ -7,30 +7,30 @@ import spock.lang.Specification @org.testcontainers.spock.Testcontainers class RedisBackedCacheIntTest extends Specification { - private RedisBackedCache underTest + private RedisBackedCache underTest - // init { - GenericContainer redis = new GenericContainer<>("redis:5.0.3-alpine") - .withExposedPorts(6379) - // } + // init { + GenericContainer redis = new GenericContainer<>("redis:5.0.3-alpine") + .withExposedPorts(6379) + // } - void setup() { - String address = redis.host - Integer port = redis.firstMappedPort + void setup() { + String address = redis.host + Integer port = redis.firstMappedPort - // Now we have an address and port for Redis, no matter where it is running - underTest = new RedisBackedCache(address, port) - } + // Now we have an address and port for Redis, no matter where it is running + underTest = new RedisBackedCache(address, port) + } - void testSimplePutAndGet() { - setup: - underTest.put("test", "example") + void testSimplePutAndGet() { + setup: + underTest.put("test", "example") - when: - String retrieved = underTest.get("test") + when: + String retrieved = underTest.get("test") - then: - retrieved == "example" - } + then: + retrieved == "example" + } } // } diff --git a/docs/examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTestStep0.groovy b/docs/examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTestStep0.groovy index 52897c30097..d28942420da 100644 --- a/docs/examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTestStep0.groovy +++ b/docs/examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTestStep0.groovy @@ -5,21 +5,21 @@ import spock.lang.Specification @Ignore("This test class is deliberately invalid, as it relies on a non-existent local Redis") class RedisBackedCacheIntTestStep0 extends Specification { - private RedisBackedCache underTest + private RedisBackedCache underTest - void setup() { - // Assume that we have Redis running locally? - underTest = new RedisBackedCache("localhost", 6379) - } + void setup() { + // Assume that we have Redis running locally? + underTest = new RedisBackedCache("localhost", 6379) + } - void testSimplePutAndGet() { - setup: - underTest.put("test", "example") + void testSimplePutAndGet() { + setup: + underTest.put("test", "example") - when: - String retrieved = underTest.get("test") + when: + String retrieved = underTest.get("test") - then: - retrieved == "example" - } + then: + retrieved == "example" + } } diff --git a/docs/modules/hivemq.md b/docs/modules/hivemq.md index 1e09bac4fc0..66f9670648c 100644 --- a/docs/modules/hivemq.md +++ b/docs/modules/hivemq.md @@ -31,7 +31,7 @@ Using the Community Edition: Using the Enterprise Edition: -[Enterprise Edition HiveMQ image](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java) inside_block:eeVersion +[Enterprise Edition HiveMQ image](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java) inside_block:hiveEEVersion Using a specifc version is possible by using the tag: @@ -138,7 +138,7 @@ The following example shows how to start a HiveMQ container with the extension c The following test then proceeds to enable and then disable the extension: -[Enable/Disable extension at runtime](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java) inside_block:runtimeEnable +[Enable/Disable extension at runtime](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java) inside_block:hiveRuntimeEnable ## Enable/Disable an extension loaded from a folder diff --git a/modules/azure/src/main/java/org/testcontainers/containers/CosmosDBEmulatorContainer.java b/modules/azure/src/main/java/org/testcontainers/containers/CosmosDBEmulatorContainer.java index 9328d881666..57af95ef106 100644 --- a/modules/azure/src/main/java/org/testcontainers/containers/CosmosDBEmulatorContainer.java +++ b/modules/azure/src/main/java/org/testcontainers/containers/CosmosDBEmulatorContainer.java @@ -10,8 +10,9 @@ */ public class CosmosDBEmulatorContainer extends GenericContainer { - private static final DockerImageName DEFAULT_IMAGE_NAME = - DockerImageName.parse("mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator"); + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse( + "mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator" + ); private static final int PORT = 8081; diff --git a/modules/azure/src/main/java/org/testcontainers/containers/KeyStoreBuilder.java b/modules/azure/src/main/java/org/testcontainers/containers/KeyStoreBuilder.java index 0179e8d832a..f7b6af370ac 100644 --- a/modules/azure/src/main/java/org/testcontainers/containers/KeyStoreBuilder.java +++ b/modules/azure/src/main/java/org/testcontainers/containers/KeyStoreBuilder.java @@ -5,10 +5,6 @@ import okhttp3.Request; import okhttp3.Response; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; import java.io.InputStream; import java.security.KeyStore; import java.security.SecureRandom; @@ -17,6 +13,11 @@ import java.security.cert.X509Certificate; import java.util.Objects; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + final class KeyStoreBuilder { static KeyStore buildByDownloadingCertificate(String endpoint, String keyStorePassword) { @@ -38,20 +39,18 @@ static KeyStore buildByDownloadingCertificate(String endpoint, String keyStorePa private static TrustManager[] buildTrustAllManagers() { return new TrustManager[] { - new X509TrustManager() { - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) { - } + new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) {} - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) { - } + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) {} - @Override - public X509Certificate[] getAcceptedIssuers() { - return new X509Certificate[]{}; - } + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[] {}; } + }, }; } @@ -60,16 +59,13 @@ private static OkHttpClient buildTrustAllClient(TrustManager[] trustManagers) th sslContext.init(null, trustManagers, new SecureRandom()); SSLSocketFactory socketFactory = sslContext.getSocketFactory(); return new OkHttpClient.Builder() - .sslSocketFactory(socketFactory, (X509TrustManager) trustManagers[0]) - .hostnameVerifier((s, sslSession) -> true) - .build(); + .sslSocketFactory(socketFactory, (X509TrustManager) trustManagers[0]) + .hostnameVerifier((s, sslSession) -> true) + .build(); } private static Request buildRequest(String endpoint) { - return new Request.Builder() - .get() - .url(endpoint + "/_explorer/emulator.pem") - .build(); + return new Request.Builder().get().url(endpoint + "/_explorer/emulator.pem").build(); } private static KeyStore buildKeyStore(InputStream certificateStream, String keyStorePassword) throws Exception { @@ -85,8 +81,7 @@ private static void closeResponseSilently(Response response) { if (Objects.nonNull(response)) { response.close(); } - } catch (Exception ignored) { - } + } catch (Exception ignored) {} } private static void closeClientSilently(OkHttpClient client) { @@ -99,7 +94,6 @@ private static void closeClientSilently(OkHttpClient client) { cache.close(); } } - } catch (Exception ignored) { - } + } catch (Exception ignored) {} } } diff --git a/modules/azure/src/test/java/org/testcontainers/containers/CosmosDBEmulatorContainerTest.java b/modules/azure/src/test/java/org/testcontainers/containers/CosmosDBEmulatorContainerTest.java index dd9dc6b718f..37422b30af7 100644 --- a/modules/azure/src/test/java/org/testcontainers/containers/CosmosDBEmulatorContainerTest.java +++ b/modules/azure/src/test/java/org/testcontainers/containers/CosmosDBEmulatorContainerTest.java @@ -37,8 +37,9 @@ public static void restoreOriginalSystemProperties() { @Rule // emulatorContainer { public CosmosDBEmulatorContainer emulator = new CosmosDBEmulatorContainer( - DockerImageName.parse("mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest") + DockerImageName.parse("mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest") ); + // } @Test @@ -55,18 +56,19 @@ public void testWithCosmosClient() throws Exception { // } // buildClient { CosmosAsyncClient client = new CosmosClientBuilder() - .gatewayMode() - .endpointDiscoveryEnabled(false) - .endpoint(emulator.getEmulatorEndpoint()) - .key(emulator.getEmulatorKey()) - .buildAsyncClient(); + .gatewayMode() + .endpointDiscoveryEnabled(false) + .endpoint(emulator.getEmulatorEndpoint()) + .key(emulator.getEmulatorKey()) + .buildAsyncClient(); // } // testWithClientAgainstEmulatorContainer { - CosmosDatabaseResponse databaseResponse = - client.createDatabaseIfNotExists("Azure").block(); + CosmosDatabaseResponse databaseResponse = client.createDatabaseIfNotExists("Azure").block(); Assertions.assertThat(databaseResponse.getStatusCode()).isEqualTo(201); - CosmosContainerResponse containerResponse = - client.getDatabase("Azure").createContainerIfNotExists("ServiceContainer", "/name").block(); + CosmosContainerResponse containerResponse = client + .getDatabase("Azure") + .createContainerIfNotExists("ServiceContainer", "/name") + .block(); Assertions.assertThat(containerResponse.getStatusCode()).isEqualTo(201); // } } diff --git a/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java b/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java index e3ae084cb8a..3c78743fb62 100644 --- a/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java +++ b/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java @@ -10,12 +10,13 @@ import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MountableFile; -import javax.script.ScriptException; import java.io.IOException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Optional; +import javax.script.ScriptException; + /** * Cassandra container * @@ -26,18 +27,24 @@ public class CassandraContainer> extends GenericContainer { private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("cassandra"); + private static final String DEFAULT_TAG = "3.11.2"; @Deprecated public static final String IMAGE = DEFAULT_IMAGE_NAME.getUnversionedPart(); public static final Integer CQL_PORT = 9042; + private static final String CONTAINER_CONFIG_LOCATION = "/etc/cassandra"; + private static final String USERNAME = "cassandra"; + private static final String PASSWORD = "cassandra"; private String configLocation; + private String initScriptPath; + private boolean enableJmxReporting; /** @@ -54,17 +61,13 @@ public CassandraContainer(String dockerImageName) { public CassandraContainer(DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); addExposedPort(CQL_PORT); this.enableJmxReporting = false; withEnv("CASSANDRA_SNITCH", "GossipingPropertyFileSnitch"); - withEnv( - "JVM_OPTS", - "-Dcassandra.skip_wait_for_gossip_to_settle=0 -Dcassandra.initial_token=0" - ); + withEnv("JVM_OPTS", "-Dcassandra.skip_wait_for_gossip_to_settle=0 -Dcassandra.initial_token=0"); withEnv("HEAP_NEWSIZE", "128M"); withEnv("MAX_HEAP_SIZE", "1024M"); } @@ -88,7 +91,9 @@ private void runInitScriptIfRequired() { URL resource = Thread.currentThread().getContextClassLoader().getResource(initScriptPath); if (resource == null) { logger().warn("Could not load classpath init script: {}", initScriptPath); - throw new ScriptLoadException("Could not load classpath init script: " + initScriptPath + ". Resource not found."); + throw new ScriptLoadException( + "Could not load classpath init script: " + initScriptPath + ". Resource not found." + ); } String cql = IOUtils.toString(resource, StandardCharsets.UTF_8); DatabaseDelegate databaseDelegate = getDatabaseDelegate(); @@ -98,7 +103,10 @@ private void runInitScriptIfRequired() { throw new ScriptLoadException("Could not load classpath init script: " + initScriptPath, e); } catch (ScriptException e) { logger().error("Error while executing init script: {}", initScriptPath, e); - throw new ScriptUtils.UncategorizedScriptException("Error while executing init script: " + initScriptPath, e); + throw new ScriptUtils.UncategorizedScriptException( + "Error while executing init script: " + initScriptPath, + e + ); } } } @@ -112,9 +120,10 @@ private void runInitScriptIfRequired() { * @param resourceLocation relative classpath to resource */ protected void optionallyMapResourceParameterAsVolume(String pathNameInContainer, String resourceLocation) { - Optional.ofNullable(resourceLocation) - .map(MountableFile::forClasspathResource) - .ifPresent(mountableFile -> withCopyFileToContainer(mountableFile, pathNameInContainer)); + Optional + .ofNullable(resourceLocation) + .map(MountableFile::forClasspathResource) + .ifPresent(mountableFile -> withCopyFileToContainer(mountableFile, pathNameInContainer)); } /** @@ -184,7 +193,8 @@ public Cluster getCluster() { } public static Cluster getCluster(ContainerState containerState, boolean enableJmxReporting) { - final Cluster.Builder builder = Cluster.builder() + final Cluster.Builder builder = Cluster + .builder() .addContactPoint(containerState.getHost()) .withPort(containerState.getMappedPort(CQL_PORT)); if (!enableJmxReporting) { diff --git a/modules/cassandra/src/main/java/org/testcontainers/containers/delegate/CassandraDatabaseDelegate.java b/modules/cassandra/src/main/java/org/testcontainers/containers/delegate/CassandraDatabaseDelegate.java index e55dd31b0ff..8bc72e7f154 100644 --- a/modules/cassandra/src/main/java/org/testcontainers/containers/delegate/CassandraDatabaseDelegate.java +++ b/modules/cassandra/src/main/java/org/testcontainers/containers/delegate/CassandraDatabaseDelegate.java @@ -25,8 +25,7 @@ public class CassandraDatabaseDelegate extends AbstractDatabaseDelegate @Override protected Session createNewConnection() { try { - return CassandraContainer.getCluster(container) - .newSession(); + return CassandraContainer.getCluster(container).newSession(); } catch (DriverException e) { log.error("Could not obtain cassandra connection"); throw new ConnectionCreationException("Could not obtain cassandra connection", e); @@ -34,7 +33,13 @@ protected Session createNewConnection() { } @Override - public void execute(String statement, String scriptPath, int lineNumber, boolean continueOnError, boolean ignoreFailedDrops) { + public void execute( + String statement, + String scriptPath, + int lineNumber, + boolean continueOnError, + boolean ignoreFailedDrops + ) { try { ResultSet result = getConnection().execute(statement); if (result.wasApplied()) { diff --git a/modules/cassandra/src/main/java/org/testcontainers/containers/wait/CassandraQueryWaitStrategy.java b/modules/cassandra/src/main/java/org/testcontainers/containers/wait/CassandraQueryWaitStrategy.java index cf7f3769985..aa2466cf7c4 100644 --- a/modules/cassandra/src/main/java/org/testcontainers/containers/wait/CassandraQueryWaitStrategy.java +++ b/modules/cassandra/src/main/java/org/testcontainers/containers/wait/CassandraQueryWaitStrategy.java @@ -18,20 +18,26 @@ public class CassandraQueryWaitStrategy extends AbstractWaitStrategy { private static final String SELECT_VERSION_QUERY = "SELECT release_version FROM system.local"; + private static final String TIMEOUT_ERROR = "Timed out waiting for Cassandra to be accessible for query execution"; @Override protected void waitUntilReady() { // execute select version query until success or timeout try { - retryUntilSuccess((int) startupTimeout.getSeconds(), TimeUnit.SECONDS, () -> { - getRateLimiter().doWhenReady(() -> { - try (DatabaseDelegate databaseDelegate = getDatabaseDelegate()) { - databaseDelegate.execute(SELECT_VERSION_QUERY, "", 1, false, false); - } - }); - return true; - }); + retryUntilSuccess( + (int) startupTimeout.getSeconds(), + TimeUnit.SECONDS, + () -> { + getRateLimiter() + .doWhenReady(() -> { + try (DatabaseDelegate databaseDelegate = getDatabaseDelegate()) { + databaseDelegate.execute(SELECT_VERSION_QUERY, "", 1, false, false); + } + }); + return true; + } + ); } catch (TimeoutException e) { throw new ContainerLaunchException(TIMEOUT_ERROR); } diff --git a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java index 76c8c8438d7..45cbba0abc7 100644 --- a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java +++ b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java @@ -36,7 +36,11 @@ public void testSimple() { @Test public void testSpecificVersion() { String cassandraVersion = "3.0.15"; - try (CassandraContainer cassandraContainer = new CassandraContainer<>(CASSANDRA_IMAGE.withTag(cassandraVersion))) { + try ( + CassandraContainer cassandraContainer = new CassandraContainer<>( + CASSANDRA_IMAGE.withTag(cassandraVersion) + ) + ) { cassandraContainer.start(); ResultSet resultSet = performQuery(cassandraContainer, "SELECT release_version FROM system.local"); assertTrue("Query was not applied", resultSet.wasApplied()); @@ -53,7 +57,11 @@ public void testConfigurationOverride() { cassandraContainer.start(); ResultSet resultSet = performQuery(cassandraContainer, "SELECT cluster_name FROM system.local"); assertTrue("Query was not applied", resultSet.wasApplied()); - assertEquals("Cassandra configuration is not overridden", TEST_CLUSTER_NAME_IN_CONF, resultSet.one().getString(0)); + assertEquals( + "Cassandra configuration is not overridden", + TEST_CLUSTER_NAME_IN_CONF, + resultSet.one().getString(0) + ); } } @@ -81,7 +89,9 @@ public void testInitScript() { @Test public void testInitScriptWithLegacyCassandra() { try ( - CassandraContainer cassandraContainer = new CassandraContainer<>(DockerImageName.parse("cassandra:2.2.11")) + CassandraContainer cassandraContainer = new CassandraContainer<>( + DockerImageName.parse("cassandra:2.2.11") + ) .withInitScript("initial.cql") ) { cassandraContainer.start(); @@ -107,7 +117,10 @@ public void testCassandraQueryWaitStrategy() { public void testCassandraGetCluster() { try (CassandraContainer cassandraContainer = new CassandraContainer<>()) { cassandraContainer.start(); - ResultSet resultSet = performQuery(cassandraContainer.getCluster(), "SELECT release_version FROM system.local"); + ResultSet resultSet = performQuery( + cassandraContainer.getCluster(), + "SELECT release_version FROM system.local" + ); assertTrue("Query was not applied", resultSet.wasApplied()); assertNotNull("Result set has no release_version", resultSet.one().getString(0)); } @@ -122,7 +135,8 @@ private void testInitScript(CassandraContainer cassandraContainer) { } private ResultSet performQuery(CassandraContainer cassandraContainer, String cql) { - Cluster explicitCluster = Cluster.builder() + Cluster explicitCluster = Cluster + .builder() .addContactPoint(cassandraContainer.getHost()) .withPort(cassandraContainer.getMappedPort(CassandraContainer.CQL_PORT)) .build(); diff --git a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java index 9b5f130a323..4528c48e935 100644 --- a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java +++ b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java @@ -8,6 +8,7 @@ import java.util.Set; public class ClickHouseContainer extends JdbcDatabaseContainer { + public static final String NAME = "clickhouse"; private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("yandex/clickhouse-server"); @@ -19,14 +20,19 @@ public class ClickHouseContainer extends JdbcDatabaseContainer { public static final String DEFAULT_TAG = "18.10.3"; public static final Integer HTTP_PORT = 8123; + public static final Integer NATIVE_PORT = 9000; private static final String DRIVER_CLASS_NAME = "ru.yandex.clickhouse.ClickHouseDriver"; + private static final String JDBC_URL_PREFIX = "jdbc:" + NAME + "://"; + private static final String TEST_QUERY = "SELECT 1"; private String databaseName = "default"; + private String username = "default"; + private String password = ""; /** @@ -43,7 +49,6 @@ public ClickHouseContainer(String dockerImageName) { public ClickHouseContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); withExposedPorts(HTTP_PORT, NATIVE_PORT); diff --git a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseProvider.java b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseProvider.java index cd2453b8349..80fb71bd5da 100644 --- a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseProvider.java +++ b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseProvider.java @@ -3,6 +3,7 @@ import org.testcontainers.utility.DockerImageName; public class ClickHouseProvider extends JdbcDatabaseContainerProvider { + @Override public boolean supports(String databaseType) { return databaseType.equals(ClickHouseContainer.NAME); diff --git a/modules/clickhouse/src/test/java/org/testcontainers/jdbc/clickhouse/ClickhouseJDBCDriverTest.java b/modules/clickhouse/src/test/java/org/testcontainers/jdbc/clickhouse/ClickhouseJDBCDriverTest.java index f3946c8a317..056aace7772 100644 --- a/modules/clickhouse/src/test/java/org/testcontainers/jdbc/clickhouse/ClickhouseJDBCDriverTest.java +++ b/modules/clickhouse/src/test/java/org/testcontainers/jdbc/clickhouse/ClickhouseJDBCDriverTest.java @@ -4,18 +4,18 @@ import org.junit.runners.Parameterized; import org.testcontainers.jdbc.AbstractJDBCDriverTest; +import java.util.Arrays; import java.util.EnumSet; -import static java.util.Arrays.asList; - @RunWith(Parameterized.class) public class ClickhouseJDBCDriverTest extends AbstractJDBCDriverTest { @Parameterized.Parameters(name = "{index} - {0}") public static Iterable data() { - return asList( - new Object[][]{ - {"jdbc:tc:clickhouse://hostname/databasename", EnumSet.of(Options.PmdKnownBroken)}, - }); + return Arrays.asList( + new Object[][] { // + { "jdbc:tc:clickhouse://hostname/databasename", EnumSet.of(Options.PmdKnownBroken) }, + } + ); } } diff --git a/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java b/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java index 8f3879673a0..d2375891f8c 100644 --- a/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java +++ b/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java @@ -1,6 +1,7 @@ package org.testcontainers.junit.clickhouse; import org.junit.Test; +import org.testcontainers.ClickhouseTestImages; import org.testcontainers.containers.ClickHouseContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; @@ -8,13 +9,12 @@ import java.sql.SQLException; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.testcontainers.ClickhouseTestImages.CLICKHOUSE_IMAGE; public class SimpleClickhouseTest extends AbstractContainerDatabaseTest { @Test public void testSimple() throws SQLException { - try (ClickHouseContainer clickhouse = new ClickHouseContainer(CLICKHOUSE_IMAGE)) { + try (ClickHouseContainer clickhouse = new ClickHouseContainer(ClickhouseTestImages.CLICKHOUSE_IMAGE)) { clickhouse.start(); ResultSet resultSet = performQuery(clickhouse, "SELECT 1"); diff --git a/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java b/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java index f32e6bce385..dfec3988f15 100644 --- a/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java +++ b/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java @@ -8,6 +8,7 @@ public class CockroachContainer extends JdbcDatabaseContainer { private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("cockroachdb/cockroach"); + private static final String DEFAULT_TAG = "v19.2.11"; public static final String NAME = "cockroach"; @@ -19,13 +20,19 @@ public class CockroachContainer extends JdbcDatabaseContainer data() { - return asList( - new Object[][]{ - {"jdbc:tc:cockroach://hostname/databasename", EnumSet.noneOf(Options.class)}, - }); + return Arrays.asList( + new Object[][] { // + { "jdbc:tc:cockroach://hostname/databasename", EnumSet.noneOf(Options.class) }, + } + ); } } diff --git a/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java b/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java index 3121ad82100..87e82fb30e3 100644 --- a/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java +++ b/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java @@ -1,6 +1,7 @@ package org.testcontainers.junit.cockroachdb; import org.junit.Test; +import org.testcontainers.CockroachDBTestImages; import org.testcontainers.containers.CockroachContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; @@ -9,13 +10,11 @@ import java.util.logging.Level; import java.util.logging.LogManager; -import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.testcontainers.CockroachDBTestImages.COCKROACHDB_IMAGE; public class SimpleCockroachDBTest extends AbstractContainerDatabaseTest { - static { // Postgres JDBC driver uses JUL; disable it to avoid annoying, irrelevant, stderr logs during connection testing LogManager.getLogManager().getLogger("").setLevel(Level.OFF); @@ -23,7 +22,7 @@ public class SimpleCockroachDBTest extends AbstractContainerDatabaseTest { @Test public void testSimple() throws SQLException { - try (CockroachContainer cockroach = new CockroachContainer(COCKROACHDB_IMAGE)) { + try (CockroachContainer cockroach = new CockroachContainer(CockroachDBTestImages.COCKROACHDB_IMAGE)) { cockroach.start(); ResultSet resultSet = performQuery(cockroach, "SELECT 1"); @@ -35,8 +34,10 @@ public void testSimple() throws SQLException { @Test public void testExplicitInitScript() throws SQLException { - try (CockroachContainer cockroach = new CockroachContainer(COCKROACHDB_IMAGE) - .withInitScript("somepath/init_postgresql.sql")) { // CockroachDB is expected to be compatible with Postgres + try ( + CockroachContainer cockroach = new CockroachContainer(CockroachDBTestImages.COCKROACHDB_IMAGE) + .withInitScript("somepath/init_postgresql.sql") + ) { // CockroachDB is expected to be compatible with Postgres cockroach.start(); ResultSet resultSet = performQuery(cockroach, "SELECT foo FROM bar"); @@ -48,7 +49,7 @@ public void testExplicitInitScript() throws SQLException { @Test public void testWithAdditionalUrlParamInJdbcUrl() { - CockroachContainer cockroach = new CockroachContainer(COCKROACHDB_IMAGE) + CockroachContainer cockroach = new CockroachContainer(CockroachDBTestImages.COCKROACHDB_IMAGE) .withUrlParam("sslmode", "disable") .withUrlParam("application_name", "cockroach"); diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/BucketDefinition.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/BucketDefinition.java index 3778833182e..ad666b4ffef 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/BucketDefinition.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/BucketDefinition.java @@ -22,8 +22,11 @@ public class BucketDefinition { private final String name; + private boolean flushEnabled = false; + private boolean queryPrimaryIndex = true; + private int quota = 100; public BucketDefinition(final String name) { @@ -49,7 +52,7 @@ public BucketDefinition withFlushEnabled(final boolean flushEnabled) { */ public BucketDefinition withQuota(final int quota) { if (quota < 100) { - throw new IllegalArgumentException("Bucket quota cannot be less than 100MB!"); + throw new IllegalArgumentException("Bucket quota cannot be less than 100MB!"); } this.quota = quota; return this; @@ -81,5 +84,4 @@ public boolean hasPrimaryIndex() { public int getQuota() { return quota; } - } diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index c2becc68fc3..4f190a5413c 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.testcontainers.couchbase; import com.fasterxml.jackson.databind.JsonNode; @@ -139,7 +140,6 @@ public CouchbaseContainer(final String dockerImageName) { */ public CouchbaseContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); } @@ -182,8 +182,14 @@ public CouchbaseContainer withServiceQuota(final CouchbaseService service, final throw new IllegalArgumentException("The provided service (" + service + ") has no quota to configure"); } if (quotaMb < service.getMinimumQuotaMb()) { - throw new IllegalArgumentException("The custom quota (" + quotaMb + ") must not be smaller than the " + - "minimum quota for the service (" + service.getMinimumQuotaMb() + ")"); + throw new IllegalArgumentException( + "The custom quota (" + + quotaMb + + ") must not be smaller than the " + + "minimum quota for the service (" + + service.getMinimumQuotaMb() + + ")" + ); } this.customServiceQuotas.put(service, quotaMb); return this; @@ -255,54 +261,59 @@ protected void configure() { WaitAllStrategy waitStrategy = new WaitAllStrategy(); // Makes sure that all nodes in the cluster are healthy. - waitStrategy = waitStrategy.withStrategy( - new HttpWaitStrategy() - .forPath("/pools/default") - .forPort(MGMT_PORT) - .withBasicCredentials(username, password) - .forStatusCode(200) - .forResponsePredicate(response -> { - try { - return Optional.of(MAPPER.readTree(response)) - .map(n -> n.at("/nodes/0/status")) - .map(JsonNode::asText) - .map("healthy"::equals) - .orElse(false); - } catch (IOException e) { - logger().error("Unable to parse response: {}", response, e); - return false; - } - }) - ); - - if (enabledServices.contains(CouchbaseService.QUERY)) { - waitStrategy = waitStrategy.withStrategy( + waitStrategy = + waitStrategy.withStrategy( new HttpWaitStrategy() - .forPath("/admin/ping") - .forPort(QUERY_PORT) + .forPath("/pools/default") + .forPort(MGMT_PORT) .withBasicCredentials(username, password) .forStatusCode(200) + .forResponsePredicate(response -> { + try { + return Optional + .of(MAPPER.readTree(response)) + .map(n -> n.at("/nodes/0/status")) + .map(JsonNode::asText) + .map("healthy"::equals) + .orElse(false); + } catch (IOException e) { + logger().error("Unable to parse response: {}", response, e); + return false; + } + }) ); + + if (enabledServices.contains(CouchbaseService.QUERY)) { + waitStrategy = + waitStrategy.withStrategy( + new HttpWaitStrategy() + .forPath("/admin/ping") + .forPort(QUERY_PORT) + .withBasicCredentials(username, password) + .forStatusCode(200) + ); } if (enabledServices.contains(CouchbaseService.ANALYTICS)) { - waitStrategy = waitStrategy.withStrategy( - new HttpWaitStrategy() - .forPath("/admin/ping") - .forPort(ANALYTICS_PORT) - .withBasicCredentials(username, password) - .forStatusCode(200) - ); + waitStrategy = + waitStrategy.withStrategy( + new HttpWaitStrategy() + .forPath("/admin/ping") + .forPort(ANALYTICS_PORT) + .withBasicCredentials(username, password) + .forStatusCode(200) + ); } if (enabledServices.contains(CouchbaseService.EVENTING)) { - waitStrategy = waitStrategy.withStrategy( - new HttpWaitStrategy() - .forPath("/api/v1/config") - .forPort(EVENTING_PORT) - .withBasicCredentials(username, password) - .forStatusCode(200) - ); + waitStrategy = + waitStrategy.withStrategy( + new HttpWaitStrategy() + .forPath("/api/v1/config") + .forPort(EVENTING_PORT) + .withBasicCredentials(username, password) + .forStatusCode(200) + ); } waitingFor(waitStrategy); @@ -329,25 +340,23 @@ protected void containerIsStarting(final InspectContainerResponse containerInfo) protected void containerIsStarted(InspectContainerResponse containerInfo) { timePhase("createBuckets", this::createBuckets); - logger().info("Couchbase container is ready! UI available at http://{}:{}", getHost(), getMappedPort(MGMT_PORT)); + logger() + .info("Couchbase container is ready! UI available at http://{}:{}", getHost(), getMappedPort(MGMT_PORT)); } /** * Before we can start configuring the host, we need to wait until the cluster manager is listening. */ private void waitUntilNodeIsOnline() { - new HttpWaitStrategy() - .forPort(MGMT_PORT) - .forPath("/pools") - .forStatusCode(200) - .waitUntilReady(this); + new HttpWaitStrategy().forPort(MGMT_PORT).forPath("/pools").forStatusCode(200).waitUntilReady(this); } /** * Fetches edition (enterprise or community) of started container. */ private void initializeIsEnterprise() { - @Cleanup Response response = doHttpRequest(MGMT_PORT, "/pools", "GET", null, true); + @Cleanup + Response response = doHttpRequest(MGMT_PORT, "/pools", "GET", null, true); try { isEnterprise = MAPPER.readTree(response.body().string()).get("isEnterprise").asBoolean(); @@ -374,9 +383,13 @@ private void initializeIsEnterprise() { private void renameNode() { logger().debug("Renaming Couchbase Node from localhost to {}", getHost()); - @Cleanup Response response = doHttpRequest(MGMT_PORT, "/node/controller/rename", "POST", new FormBody.Builder() - .add("hostname", getInternalIpAddress()) - .build(), false + @Cleanup + Response response = doHttpRequest( + MGMT_PORT, + "/node/controller/rename", + "POST", + new FormBody.Builder().add("hostname", getInternalIpAddress()).build(), + false ); checkSuccessfulResponse(response, "Could not rename couchbase node"); @@ -393,9 +406,13 @@ private void initializeServices() { .map(CouchbaseService::getIdentifier) .collect(Collectors.joining(",")); - @Cleanup Response response = doHttpRequest(MGMT_PORT, "/node/controller/setupServices", "POST", new FormBody.Builder() - .add("services", services) - .build(), false + @Cleanup + Response response = doHttpRequest( + MGMT_PORT, + "/node/controller/setupServices", + "POST", + new FormBody.Builder().add("services", services).build(), + false ); checkSuccessfulResponse(response, "Could not enable couchbase services"); @@ -423,9 +440,8 @@ private void setMemoryQuotas() { } } - @Cleanup Response response = doHttpRequest( - MGMT_PORT, "/pools/default", "POST", quotaBuilder.build(), false - ); + @Cleanup + Response response = doHttpRequest(MGMT_PORT, "/pools/default", "POST", quotaBuilder.build(), false); checkSuccessfulResponse(response, "Could not configure service memory quotas"); } @@ -438,11 +454,18 @@ private void setMemoryQuotas() { private void configureAdminUser() { logger().debug("Configuring couchbase admin user with username: \"{}\"", username); - @Cleanup Response response = doHttpRequest(MGMT_PORT, "/settings/web", "POST", new FormBody.Builder() - .add("username", username) - .add("password", password) - .add("port", Integer.toString(MGMT_PORT)) - .build(), false); + @Cleanup + Response response = doHttpRequest( + MGMT_PORT, + "/settings/web", + "POST", + new FormBody.Builder() + .add("username", username) + .add("password", password) + .add("port", Integer.toString(MGMT_PORT)) + .build(), + false + ); checkSuccessfulResponse(response, "Could not configure couchbase admin user"); } @@ -489,7 +512,8 @@ private void configureExternalPorts() { builder.add("eventingSSL", Integer.toString(getMappedPort(EVENTING_SSL_PORT))); } - @Cleanup Response response = doHttpRequest( + @Cleanup + Response response = doHttpRequest( MGMT_PORT, "/node/controller/setupAlternateAddresses/external", "PUT", @@ -506,9 +530,13 @@ private void configureExternalPorts() { private void configureIndexer() { logger().debug("Configuring the indexer service"); - @Cleanup Response response = doHttpRequest(MGMT_PORT, "/settings/indexes", "POST", new FormBody.Builder() - .add("storageMode", isEnterprise ? "memory_optimized" : "forestdb") - .build(), true + @Cleanup + Response response = doHttpRequest( + MGMT_PORT, + "/settings/indexes", + "POST", + new FormBody.Builder().add("storageMode", isEnterprise ? "memory_optimized" : "forestdb").build(), + true ); checkSuccessfulResponse(response, "Could not configure the indexing service"); @@ -523,22 +551,32 @@ private void createBuckets() { for (BucketDefinition bucket : buckets) { logger().debug("Creating bucket \"{}\"", bucket.getName()); - @Cleanup Response response = doHttpRequest(MGMT_PORT, "/pools/default/buckets", "POST", new FormBody.Builder() - .add("name", bucket.getName()) - .add("ramQuotaMB", Integer.toString(bucket.getQuota())) - .add("flushEnabled", bucket.hasFlushEnabled() ? "1" : "0") - .build(), true); + @Cleanup + Response response = doHttpRequest( + MGMT_PORT, + "/pools/default/buckets", + "POST", + new FormBody.Builder() + .add("name", bucket.getName()) + .add("ramQuotaMB", Integer.toString(bucket.getQuota())) + .add("flushEnabled", bucket.hasFlushEnabled() ? "1" : "0") + .build(), + true + ); checkSuccessfulResponse(response, "Could not create bucket " + bucket.getName()); - timePhase("createBucket:" + bucket.getName() + ":waitForAllServicesEnabled", () -> - new HttpWaitStrategy() - .forPath("/pools/default/b/" + bucket.getName()) - .forPort(MGMT_PORT) - .withBasicCredentials(username, password) - .forStatusCode(200) - .forResponsePredicate(new AllServicesEnabledPredicate()) - .waitUntilReady(this) + timePhase( + "createBucket:" + bucket.getName() + ":waitForAllServicesEnabled", + () -> { + new HttpWaitStrategy() + .forPath("/pools/default/b/" + bucket.getName()) + .forPort(MGMT_PORT) + .withBasicCredentials(username, password) + .forStatusCode(200) + .forResponsePredicate(new AllServicesEnabledPredicate()) + .waitUntilReady(this); + } ); if (enabledServices.contains(CouchbaseService.QUERY)) { @@ -546,29 +584,62 @@ private void createBuckets() { // knows about the bucket in its metadata configuration. timePhase( "createBucket:" + bucket.getName() + ":queryKeyspacePresent", - () -> Unreliables.retryUntilTrue(1, TimeUnit.MINUTES, () -> { - @Cleanup Response queryResponse = doHttpRequest(QUERY_PORT, "/query/service", "POST", new FormBody.Builder() - .add("statement", "SELECT COUNT(*) > 0 as present FROM system:keyspaces WHERE name = \"" + bucket.getName() + "\"") - .build(), true); - - String body = queryResponse.body() != null ? queryResponse.body().string() : null; - checkSuccessfulResponse(queryResponse, "Could not poll query service state for bucket: " + bucket.getName()); - - return Optional.of(MAPPER.readTree(body)) - .map(n -> n.at("/results/0/present")) - .map(JsonNode::asBoolean) - .orElse(false); - })); + () -> { + Unreliables.retryUntilTrue( + 1, + TimeUnit.MINUTES, + () -> { + @Cleanup + Response queryResponse = doHttpRequest( + QUERY_PORT, + "/query/service", + "POST", + new FormBody.Builder() + .add( + "statement", + "SELECT COUNT(*) > 0 as present FROM system:keyspaces WHERE name = \"" + + bucket.getName() + + "\"" + ) + .build(), + true + ); + + String body = queryResponse.body() != null ? queryResponse.body().string() : null; + checkSuccessfulResponse( + queryResponse, + "Could not poll query service state for bucket: " + bucket.getName() + ); + + return Optional + .of(MAPPER.readTree(body)) + .map(n -> n.at("/results/0/present")) + .map(JsonNode::asBoolean) + .orElse(false); + } + ); + } + ); } if (bucket.hasPrimaryIndex()) { if (enabledServices.contains(CouchbaseService.QUERY)) { - @Cleanup Response queryResponse = doHttpRequest(QUERY_PORT, "/query/service", "POST", new FormBody.Builder() - .add("statement", "CREATE PRIMARY INDEX on `" + bucket.getName() + "`") - .build(), true); + @Cleanup + Response queryResponse = doHttpRequest( + QUERY_PORT, + "/query/service", + "POST", + new FormBody.Builder() + .add("statement", "CREATE PRIMARY INDEX on `" + bucket.getName() + "`") + .build(), + true + ); try { - checkSuccessfulResponse(queryResponse, "Could not create primary index for bucket " + bucket.getName()); + checkSuccessfulResponse( + queryResponse, + "Could not create primary index for bucket " + bucket.getName() + ); } catch (IllegalStateException ex) { // potentially ignore the error, the index will be eventually built. if (!ex.getMessage().contains("Index creation will be retried in background")) { @@ -578,21 +649,48 @@ private void createBuckets() { timePhase( "createBucket:" + bucket.getName() + ":primaryIndexOnline", - () -> Unreliables.retryUntilTrue(1, TimeUnit.MINUTES, () -> { - @Cleanup Response stateResponse = doHttpRequest(QUERY_PORT, "/query/service", "POST", new FormBody.Builder() - .add("statement", "SELECT count(*) > 0 AS online FROM system:indexes where keyspace_id = \"" + bucket.getName() + "\" and is_primary = true and state = \"online\"") - .build(), true); - - String body = stateResponse.body() != null ? stateResponse.body().string() : null; - checkSuccessfulResponse(stateResponse, "Could not poll primary index state for bucket: " + bucket.getName()); - - return Optional.of(MAPPER.readTree(body)) - .map(n -> n.at("/results/0/online")) - .map(JsonNode::asBoolean) - .orElse(false); - })); + () -> { + Unreliables.retryUntilTrue( + 1, + TimeUnit.MINUTES, + () -> { + @Cleanup + Response stateResponse = doHttpRequest( + QUERY_PORT, + "/query/service", + "POST", + new FormBody.Builder() + .add( + "statement", + "SELECT count(*) > 0 AS online FROM system:indexes where keyspace_id = \"" + + bucket.getName() + + "\" and is_primary = true and state = \"online\"" + ) + .build(), + true + ); + + String body = stateResponse.body() != null ? stateResponse.body().string() : null; + checkSuccessfulResponse( + stateResponse, + "Could not poll primary index state for bucket: " + bucket.getName() + ); + + return Optional + .of(MAPPER.readTree(body)) + .map(n -> n.at("/results/0/online")) + .map(JsonNode::asBoolean) + .orElse(false); + } + ); + } + ); } else { - logger().info("Primary index creation for bucket {} ignored, since QUERY service is not present.", bucket.getName()); + logger() + .info( + "Primary index creation for bucket {} ignored, since QUERY service is not present.", + bucket.getName() + ); } } } @@ -602,7 +700,11 @@ private void createBuckets() { * Helper method to extract the internal IP address based on the network configuration. */ private String getInternalIpAddress() { - return getContainerInfo().getNetworkSettings().getNetworks().values().stream() + return getContainerInfo() + .getNetworkSettings() + .getNetworks() + .values() + .stream() .findFirst() .map(ContainerNetwork::getIpAddress) .orElseThrow(() -> new IllegalStateException("No network available to extract the internal IP from!")); @@ -648,8 +750,13 @@ private void checkNotRunning() { * @param auth if authentication with the admin user and password should be used. * @return the response of the request. */ - private Response doHttpRequest(final int port, final String path, final String method, final RequestBody body, - final boolean auth) { + private Response doHttpRequest( + final int port, + final String path, + final String method, + final RequestBody body, + final boolean auth + ) { try { Request.Builder requestBuilder = new Request.Builder() .url("http://" + getHost() + ":" + getMappedPort(port) + path); diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseService.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseService.java index ffb0c9f7f1f..d9a9d2ffe3a 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseService.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseService.java @@ -20,7 +20,6 @@ * Describes the different services that should be exposed on the container. */ public enum CouchbaseService { - /** * Key-Value service. */ @@ -88,5 +87,4 @@ int getMinimumQuotaMb() { boolean hasQuota() { return minimumQuotaMb > 0; } - } diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java index c91f3f12d7c..1bdbe2ab1db 100644 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java @@ -34,10 +34,13 @@ public class CouchbaseContainerTest { - private static final DockerImageName COUCHBASE_IMAGE_ENTERPRISE = - DockerImageName.parse("couchbase/server:enterprise-7.0.3"); - private static final DockerImageName COUCHBASE_IMAGE_COMMUNITY = - DockerImageName.parse("couchbase/server:community-7.0.2"); + private static final DockerImageName COUCHBASE_IMAGE_ENTERPRISE = DockerImageName.parse( + "couchbase/server:enterprise-7.0.3" + ); + + private static final DockerImageName COUCHBASE_IMAGE_COMMUNITY = DockerImageName.parse( + "couchbase/server:community-7.0.2" + ); @Test public void testBasicContainerUsageForEnterpriseContainer() { @@ -51,18 +54,21 @@ public void testBasicContainerUsageForEnterpriseContainer() { .withBucket(bucketDefinition) // } ) { - setUpClient(container, cluster -> { - Bucket bucket = cluster.bucket(bucketDefinition.getName()); - bucket.waitUntilReady(Duration.ofSeconds(10L)); + setUpClient( + container, + cluster -> { + Bucket bucket = cluster.bucket(bucketDefinition.getName()); + bucket.waitUntilReady(Duration.ofSeconds(10L)); - Collection collection = bucket.defaultCollection(); + Collection collection = bucket.defaultCollection(); - collection.upsert("foo", JsonObject.create().put("key", "value")); + collection.upsert("foo", JsonObject.create().put("key", "value")); - JsonObject fooObject = collection.get("foo").contentAsObject(); + JsonObject fooObject = collection.get("foo").contentAsObject(); - assertEquals("value", fooObject.getString("key")); - }); + assertEquals("value", fooObject.getString("key")); + } + ); } } @@ -74,42 +80,47 @@ public void testBasicContainerUsageForCommunityContainer() { CouchbaseContainer container = new CouchbaseContainer(COUCHBASE_IMAGE_COMMUNITY) .withBucket(bucketDefinition) ) { - setUpClient(container, cluster -> { - Bucket bucket = cluster.bucket(bucketDefinition.getName()); - bucket.waitUntilReady(Duration.ofSeconds(10L)); + setUpClient( + container, + cluster -> { + Bucket bucket = cluster.bucket(bucketDefinition.getName()); + bucket.waitUntilReady(Duration.ofSeconds(10L)); - Collection collection = bucket.defaultCollection(); + Collection collection = bucket.defaultCollection(); - collection.upsert("foo", JsonObject.create().put("key", "value")); + collection.upsert("foo", JsonObject.create().put("key", "value")); - JsonObject fooObject = collection.get("foo").contentAsObject(); + JsonObject fooObject = collection.get("foo").contentAsObject(); - assertEquals("value", fooObject.getString("key")); - }); + assertEquals("value", fooObject.getString("key")); + } + ); } } @Test public void testBucketIsFlushableIfEnabled() { - BucketDefinition bucketDefinition = new BucketDefinition("mybucket") - .withFlushEnabled(true); + BucketDefinition bucketDefinition = new BucketDefinition("mybucket").withFlushEnabled(true); try ( CouchbaseContainer container = new CouchbaseContainer(COUCHBASE_IMAGE_ENTERPRISE) .withBucket(bucketDefinition) ) { - setUpClient(container, cluster -> { - Bucket bucket = cluster.bucket(bucketDefinition.getName()); - bucket.waitUntilReady(Duration.ofSeconds(10L)); + setUpClient( + container, + cluster -> { + Bucket bucket = cluster.bucket(bucketDefinition.getName()); + bucket.waitUntilReady(Duration.ofSeconds(10L)); - Collection collection = bucket.defaultCollection(); + Collection collection = bucket.defaultCollection(); - collection.upsert("foo", JsonObject.create().put("key", "value")); + collection.upsert("foo", JsonObject.create().put("key", "value")); - cluster.buckets().flushBucket(bucketDefinition.getName()); + cluster.buckets().flushBucket(bucketDefinition.getName()); - await().untilAsserted(() -> assertFalse(collection.exists("foo").exists())); - }); + await().untilAsserted(() -> assertFalse(collection.exists("foo").exists())); + } + ); } } @@ -123,7 +134,12 @@ public void testFailureIfCommunityUsedWithAnalytics() { CouchbaseContainer container = new CouchbaseContainer(COUCHBASE_IMAGE_COMMUNITY) .withEnabledServices(CouchbaseService.KV, CouchbaseService.ANALYTICS) ) { - assertThrows(ContainerLaunchException.class, () -> setUpClient(container, cluster -> {})); + assertThrows( + ContainerLaunchException.class, + () -> { + setUpClient(container, cluster -> {}); + } + ); } } @@ -137,7 +153,12 @@ public void testFailureIfCommunityUsedWithEventing() { CouchbaseContainer container = new CouchbaseContainer(COUCHBASE_IMAGE_COMMUNITY) .withEnabledServices(CouchbaseService.KV, CouchbaseService.EVENTING) ) { - assertThrows(ContainerLaunchException.class, () -> setUpClient(container, cluster -> {})); + assertThrows( + ContainerLaunchException.class, + () -> { + setUpClient(container, cluster -> {}); + } + ); } } diff --git a/modules/database-commons/src/main/java/org/testcontainers/delegate/AbstractDatabaseDelegate.java b/modules/database-commons/src/main/java/org/testcontainers/delegate/AbstractDatabaseDelegate.java index 14572dec421..196eb485b5f 100644 --- a/modules/database-commons/src/main/java/org/testcontainers/delegate/AbstractDatabaseDelegate.java +++ b/modules/database-commons/src/main/java/org/testcontainers/delegate/AbstractDatabaseDelegate.java @@ -27,7 +27,12 @@ protected CONNECTION getConnection() { } @Override - public void execute(Collection statements, String scriptPath, boolean continueOnError, boolean ignoreFailedDrops) { + public void execute( + Collection statements, + String scriptPath, + boolean continueOnError, + boolean ignoreFailedDrops + ) { int lineNumber = 0; for (String statement : statements) { lineNumber++; diff --git a/modules/database-commons/src/main/java/org/testcontainers/delegate/DatabaseDelegate.java b/modules/database-commons/src/main/java/org/testcontainers/delegate/DatabaseDelegate.java index 55886d88143..eda8f197008 100644 --- a/modules/database-commons/src/main/java/org/testcontainers/delegate/DatabaseDelegate.java +++ b/modules/database-commons/src/main/java/org/testcontainers/delegate/DatabaseDelegate.java @@ -10,11 +10,16 @@ * @author Eugeny Karpov */ public interface DatabaseDelegate extends AutoCloseable { - /** * Execute statement by the implementation of the delegate */ - void execute(String statement, String scriptPath, int lineNumber, boolean continueOnError, boolean ignoreFailedDrops); + void execute( + String statement, + String scriptPath, + int lineNumber, + boolean continueOnError, + boolean ignoreFailedDrops + ); /** * Execute collection of statements diff --git a/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java b/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java index 1b94b84f368..eca4989c70e 100644 --- a/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java +++ b/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java @@ -22,13 +22,14 @@ import org.slf4j.LoggerFactory; import org.testcontainers.delegate.DatabaseDelegate; -import javax.script.ScriptException; import java.io.IOException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.LinkedList; import java.util.List; +import javax.script.ScriptException; + /** * This is a modified version of the Spring-JDBC ScriptUtils class, adapted to reduce * dependencies and slightly alter the API. @@ -48,278 +49,346 @@ */ public abstract class ScriptUtils { - private static final Logger LOGGER = LoggerFactory.getLogger(ScriptUtils.class); - - /** - * Default statement separator within SQL scripts. - */ - public static final String DEFAULT_STATEMENT_SEPARATOR = ";"; + private static final Logger LOGGER = LoggerFactory.getLogger(ScriptUtils.class); - /** - * Fallback statement separator within SQL scripts. - *

Used if neither a custom defined separator nor the - * {@link #DEFAULT_STATEMENT_SEPARATOR} is present in a given script. - */ - public static final String FALLBACK_STATEMENT_SEPARATOR = "\n"; + /** + * Default statement separator within SQL scripts. + */ + public static final String DEFAULT_STATEMENT_SEPARATOR = ";"; - /** - * Default prefix for line comments within SQL scripts. - */ - public static final String DEFAULT_COMMENT_PREFIX = "--"; + /** + * Fallback statement separator within SQL scripts. + *

Used if neither a custom defined separator nor the + * {@link #DEFAULT_STATEMENT_SEPARATOR} is present in a given script. + */ + public static final String FALLBACK_STATEMENT_SEPARATOR = "\n"; - /** - * Default start delimiter for block comments within SQL scripts. - */ - public static final String DEFAULT_BLOCK_COMMENT_START_DELIMITER = "/*"; + /** + * Default prefix for line comments within SQL scripts. + */ + public static final String DEFAULT_COMMENT_PREFIX = "--"; - /** - * Default end delimiter for block comments within SQL scripts. - */ - public static final String DEFAULT_BLOCK_COMMENT_END_DELIMITER = "*/"; + /** + * Default start delimiter for block comments within SQL scripts. + */ + public static final String DEFAULT_BLOCK_COMMENT_START_DELIMITER = "/*"; + /** + * Default end delimiter for block comments within SQL scripts. + */ + public static final String DEFAULT_BLOCK_COMMENT_END_DELIMITER = "*/"; - /** - * Prevent instantiation of this utility class. - */ - private ScriptUtils() { - /* no-op */ - } + /** + * Prevent instantiation of this utility class. + */ + private ScriptUtils() { + /* no-op */ + } /** - * Split an SQL script into separate statements delimited by the provided - * separator string. Each individual statement will be added to the provided - * {@code List}. - *

Within the script, the provided {@code commentPrefix} will be honored: - * any text beginning with the comment prefix and extending to the end of the - * line will be omitted from the output. Similarly, the provided - * {@code blockCommentStartDelimiter} and {@code blockCommentEndDelimiter} - * delimiters will be honored: any text enclosed in a block comment will be - * omitted from the output. In addition, multiple adjacent whitespace characters - * will be collapsed into a single space. - * @param resource the resource from which the script was read - * @param script the SQL script; never {@code null} or empty - * @param separator text separating each statement — typically a ';' or - * newline character; never {@code null} - * @param commentPrefix the prefix that identifies SQL line comments — - * typically "--"; never {@code null} or empty - * @param blockCommentStartDelimiter the start block comment delimiter; - * never {@code null} or empty - * @param blockCommentEndDelimiter the end block comment delimiter; - * never {@code null} or empty - * @param statements the list that will contain the individual statements - */ - public static void splitSqlScript(String resource, String script, String separator, String commentPrefix, - String blockCommentStartDelimiter, String blockCommentEndDelimiter, List statements) { - - checkArgument(StringUtils.isNotEmpty(script), "script must not be null or empty"); - checkArgument(separator != null, "separator must not be null"); - checkArgument(StringUtils.isNotEmpty(commentPrefix), "commentPrefix must not be null or empty"); - checkArgument(StringUtils.isNotEmpty(blockCommentStartDelimiter), "blockCommentStartDelimiter must not be null or empty"); - checkArgument(StringUtils.isNotEmpty(blockCommentEndDelimiter), "blockCommentEndDelimiter must not be null or empty"); - - StringBuilder sb = new StringBuilder(); - boolean inEscape = false; - boolean inLineComment = false; - boolean inBlockComment = false; - Character currentLiteralDelimiter = null; - - int compoundStatementDepth = 0; - final String lowerCaseScriptContent = script.toLowerCase(); - char[] content = script.toCharArray(); - for (int i = 0; i < script.length(); i++) { - char c = content[i]; - if (inEscape) { - inEscape = false; - sb.append(c); - continue; - } - // MySQL style escapes - if (c == '\\') { - inEscape = true; - sb.append(c); - continue; - } - // Determine whether we're entering/leaving a string literal - if (!inBlockComment && !inLineComment && (c == '\'' || c == '"' || c == '`')) { - if (currentLiteralDelimiter == null) { // ignore delimiters within an existing string literal - currentLiteralDelimiter = c; - } else if (currentLiteralDelimiter == c) { // find end of string literal - currentLiteralDelimiter = null; - } - } - final boolean inLiteral = currentLiteralDelimiter != null; - - if (!inLiteral && containsSubstringAtOffset(lowerCaseScriptContent, commentPrefix, i)) { - inLineComment = true; - } - if (inLineComment && c == '\n') { - inLineComment = false; - } - if (!inLiteral && containsSubstringAtOffset(lowerCaseScriptContent, blockCommentStartDelimiter, i)) { - inBlockComment = true; - } - if (!inLiteral && inBlockComment && containsSubstringAtOffset(lowerCaseScriptContent, blockCommentEndDelimiter, i)) { - inBlockComment = false; - } - final boolean inComment = inLineComment || inBlockComment; - - if (!inLiteral && !inComment && containsKeywordsAtOffset(lowerCaseScriptContent, "BEGIN", i, separator, commentPrefix, blockCommentStartDelimiter)) { - compoundStatementDepth++; - } - if (!inLiteral && !inComment && containsKeywordsAtOffset(lowerCaseScriptContent, "END", i, separator, commentPrefix, blockCommentStartDelimiter)) { - compoundStatementDepth--; - } - final boolean inCompoundStatement = compoundStatementDepth != 0; - - if (!inLiteral && !inCompoundStatement) { - if (script.startsWith(separator, i)) { - // we've reached the end of the current statement - sb = flushStringBuilder(sb, statements); - i += separator.length() - 1; - continue; - } - else if (script.startsWith(commentPrefix, i)) { - // skip over any content from the start of the comment to the EOL - int indexOfNextNewline = script.indexOf("\n", i); - if (indexOfNextNewline > i) { - i = indexOfNextNewline; - continue; - } - else { - // if there's no EOL, we must be at the end - // of the script, so stop here. - break; - } - } - else if (script.startsWith(blockCommentStartDelimiter, i)) { - // skip over any block comments - int indexOfCommentEnd = script.indexOf(blockCommentEndDelimiter, i); - if (indexOfCommentEnd > i) { - i = indexOfCommentEnd + blockCommentEndDelimiter.length() - 1; + * Split an SQL script into separate statements delimited by the provided + * separator string. Each individual statement will be added to the provided + * {@code List}. + *

Within the script, the provided {@code commentPrefix} will be honored: + * any text beginning with the comment prefix and extending to the end of the + * line will be omitted from the output. Similarly, the provided + * {@code blockCommentStartDelimiter} and {@code blockCommentEndDelimiter} + * delimiters will be honored: any text enclosed in a block comment will be + * omitted from the output. In addition, multiple adjacent whitespace characters + * will be collapsed into a single space. + * @param resource the resource from which the script was read + * @param script the SQL script; never {@code null} or empty + * @param separator text separating each statement — typically a ';' or + * newline character; never {@code null} + * @param commentPrefix the prefix that identifies SQL line comments — + * typically "--"; never {@code null} or empty + * @param blockCommentStartDelimiter the start block comment delimiter; + * never {@code null} or empty + * @param blockCommentEndDelimiter the end block comment delimiter; + * never {@code null} or empty + * @param statements the list that will contain the individual statements + */ + public static void splitSqlScript( + String resource, + String script, + String separator, + String commentPrefix, + String blockCommentStartDelimiter, + String blockCommentEndDelimiter, + List statements + ) { + checkArgument(StringUtils.isNotEmpty(script), "script must not be null or empty"); + checkArgument(separator != null, "separator must not be null"); + checkArgument(StringUtils.isNotEmpty(commentPrefix), "commentPrefix must not be null or empty"); + checkArgument( + StringUtils.isNotEmpty(blockCommentStartDelimiter), + "blockCommentStartDelimiter must not be null or empty" + ); + checkArgument( + StringUtils.isNotEmpty(blockCommentEndDelimiter), + "blockCommentEndDelimiter must not be null or empty" + ); + + StringBuilder sb = new StringBuilder(); + boolean inEscape = false; + boolean inLineComment = false; + boolean inBlockComment = false; + Character currentLiteralDelimiter = null; + + int compoundStatementDepth = 0; + final String lowerCaseScriptContent = script.toLowerCase(); + char[] content = script.toCharArray(); + for (int i = 0; i < script.length(); i++) { + char c = content[i]; + if (inEscape) { + inEscape = false; + sb.append(c); + continue; + } + // MySQL style escapes + if (c == '\\') { + inEscape = true; + sb.append(c); + continue; + } + // Determine whether we're entering/leaving a string literal + if (!inBlockComment && !inLineComment && (c == '\'' || c == '"' || c == '`')) { + if (currentLiteralDelimiter == null) { // ignore delimiters within an existing string literal + currentLiteralDelimiter = c; + } else if (currentLiteralDelimiter == c) { // find end of string literal + currentLiteralDelimiter = null; + } + } + final boolean inLiteral = currentLiteralDelimiter != null; + + if (!inLiteral && containsSubstringAtOffset(lowerCaseScriptContent, commentPrefix, i)) { + inLineComment = true; + } + if (inLineComment && c == '\n') { + inLineComment = false; + } + if (!inLiteral && containsSubstringAtOffset(lowerCaseScriptContent, blockCommentStartDelimiter, i)) { + inBlockComment = true; + } + if ( + !inLiteral && + inBlockComment && + containsSubstringAtOffset(lowerCaseScriptContent, blockCommentEndDelimiter, i) + ) { + inBlockComment = false; + } + final boolean inComment = inLineComment || inBlockComment; + + if ( + !inLiteral && + !inComment && + containsKeywordsAtOffset( + lowerCaseScriptContent, + "BEGIN", + i, + separator, + commentPrefix, + blockCommentStartDelimiter + ) + ) { + compoundStatementDepth++; + } + if ( + !inLiteral && + !inComment && + containsKeywordsAtOffset( + lowerCaseScriptContent, + "END", + i, + separator, + commentPrefix, + blockCommentStartDelimiter + ) + ) { + compoundStatementDepth--; + } + final boolean inCompoundStatement = compoundStatementDepth != 0; + + if (!inLiteral && !inCompoundStatement) { + if (script.startsWith(separator, i)) { + // we've reached the end of the current statement + sb = flushStringBuilder(sb, statements); + i += separator.length() - 1; + continue; + } else if (script.startsWith(commentPrefix, i)) { + // skip over any content from the start of the comment to the EOL + int indexOfNextNewline = script.indexOf("\n", i); + if (indexOfNextNewline > i) { + i = indexOfNextNewline; + continue; + } else { + // if there's no EOL, we must be at the end + // of the script, so stop here. + break; + } + } else if (script.startsWith(blockCommentStartDelimiter, i)) { + // skip over any block comments + int indexOfCommentEnd = script.indexOf(blockCommentEndDelimiter, i); + if (indexOfCommentEnd > i) { + i = indexOfCommentEnd + blockCommentEndDelimiter.length() - 1; inBlockComment = false; - continue; - } - else { - throw new ScriptParseException(String.format("Missing block comment end delimiter [%s].", - blockCommentEndDelimiter), resource); - } - } - else if (c == ' ' || c == '\n' || c == '\t' || c == '\r') { - // avoid multiple adjacent whitespace characters - if (sb.length() > 0 && sb.charAt(sb.length() - 1) != ' ') { - c = ' '; - } - else { - continue; - } - } - } - sb.append(c); - } - flushStringBuilder(sb, statements); - } - - private static StringBuilder flushStringBuilder(StringBuilder sb, List statements) { - if (sb.length() == 0) { - return sb; - } - - final String s = sb.toString().trim(); - if (StringUtils.isNotEmpty(s)) { - statements.add(s); - } - - return new StringBuilder(); - } - - private static boolean isSeperator(char c, String separator, String commentPrefix, - String blockCommentStartDelimiter) { - return c == ' ' || c == '\r' || c == '\n' || c == '\t' || - c == separator.charAt(0) || c == separator.charAt(separator.length() - 1) || - c == commentPrefix.charAt(0) || c == blockCommentStartDelimiter.charAt(0) || - c == blockCommentStartDelimiter.charAt(blockCommentStartDelimiter.length() - 1); + continue; + } else { + throw new ScriptParseException( + String.format("Missing block comment end delimiter [%s].", blockCommentEndDelimiter), + resource + ); + } + } else if (c == ' ' || c == '\n' || c == '\t' || c == '\r') { + // avoid multiple adjacent whitespace characters + if (sb.length() > 0 && sb.charAt(sb.length() - 1) != ' ') { + c = ' '; + } else { + continue; + } + } + } + sb.append(c); + } + flushStringBuilder(sb, statements); + } + + private static StringBuilder flushStringBuilder(StringBuilder sb, List statements) { + if (sb.length() == 0) { + return sb; + } + + final String s = sb.toString().trim(); + if (StringUtils.isNotEmpty(s)) { + statements.add(s); + } + + return new StringBuilder(); } - private static boolean containsSubstringAtOffset(String lowercaseString, String substring, int offset) { - String lowercaseSubstring = substring.toLowerCase(); + private static boolean isSeperator( + char c, + String separator, + String commentPrefix, + String blockCommentStartDelimiter + ) { + return ( + c == ' ' || + c == '\r' || + c == '\n' || + c == '\t' || + c == separator.charAt(0) || + c == separator.charAt(separator.length() - 1) || + c == commentPrefix.charAt(0) || + c == blockCommentStartDelimiter.charAt(0) || + c == blockCommentStartDelimiter.charAt(blockCommentStartDelimiter.length() - 1) + ); + } - return lowercaseString.startsWith(lowercaseSubstring, offset); - } + private static boolean containsSubstringAtOffset(String lowercaseString, String substring, int offset) { + String lowercaseSubstring = substring.toLowerCase(); - private static boolean containsKeywordsAtOffset(String lowercaseString, String keywords, int offset, - String separator, String commentPrefix, - String blockCommentStartDelimiter) { + return lowercaseString.startsWith(lowercaseSubstring, offset); + } + + private static boolean containsKeywordsAtOffset( + String lowercaseString, + String keywords, + int offset, + String separator, + String commentPrefix, + String blockCommentStartDelimiter + ) { String lowercaseKeywords = keywords.toLowerCase(); - boolean backSeperated = (offset == 0) || isSeperator(lowercaseString.charAt(offset - 1), - separator, commentPrefix, blockCommentStartDelimiter); - boolean frontSeperated = (offset >= (lowercaseString.length() - keywords.length())) || - isSeperator(lowercaseString.charAt(offset + keywords.length()), - separator, commentPrefix, blockCommentStartDelimiter); + boolean backSeperated = + (offset == 0) || + isSeperator(lowercaseString.charAt(offset - 1), separator, commentPrefix, blockCommentStartDelimiter); + boolean frontSeperated = + (offset >= (lowercaseString.length() - keywords.length())) || + isSeperator( + lowercaseString.charAt(offset + keywords.length()), + separator, + commentPrefix, + blockCommentStartDelimiter + ); return backSeperated && frontSeperated && lowercaseString.startsWith(lowercaseKeywords, offset); } - private static void checkArgument(boolean expression, String errorMessage) { - if (!expression) { - throw new IllegalArgumentException(errorMessage); - } - } - - /** - * Does the provided SQL script contain the specified delimiter? - * @param script the SQL script - * @param delim String delimiting each statement - typically a ';' character - */ - public static boolean containsSqlScriptDelimiters(String script, String delim) { - boolean inLiteral = false; - char[] content = script.toCharArray(); - for (int i = 0; i < script.length(); i++) { - if (content[i] == '\'') { - inLiteral = !inLiteral; - } - if (!inLiteral && script.startsWith(delim, i)) { - return true; - } - } - return false; - } - - /** - * Load script from classpath and apply it to the given database - * - * @param databaseDelegate database delegate for script execution - * @param initScriptPath the resource to load the init script from - */ - public static void runInitScript(DatabaseDelegate databaseDelegate, String initScriptPath) { - try { - URL resource = Thread.currentThread().getContextClassLoader().getResource(initScriptPath); - if (resource == null) { - resource = ScriptUtils.class.getClassLoader().getResource(initScriptPath); - if (resource == null) { - LOGGER.warn("Could not load classpath init script: {}", initScriptPath); - throw new ScriptLoadException("Could not load classpath init script: " + initScriptPath + ". Resource not found."); - } - } - String scripts = IOUtils.toString(resource, StandardCharsets.UTF_8); - executeDatabaseScript(databaseDelegate, initScriptPath, scripts); - } catch (IOException e) { - LOGGER.warn("Could not load classpath init script: {}", initScriptPath); - throw new ScriptLoadException("Could not load classpath init script: " + initScriptPath, e); - } catch (ScriptException e) { - LOGGER.error("Error while executing init script: {}", initScriptPath, e); - throw new UncategorizedScriptException("Error while executing init script: " + initScriptPath, e); - } - } - - public static void executeDatabaseScript(DatabaseDelegate databaseDelegate, String scriptPath, String script) throws ScriptException { - executeDatabaseScript(databaseDelegate, scriptPath, script, false, false, DEFAULT_COMMENT_PREFIX, DEFAULT_STATEMENT_SEPARATOR, DEFAULT_BLOCK_COMMENT_START_DELIMITER, DEFAULT_BLOCK_COMMENT_END_DELIMITER); + private static void checkArgument(boolean expression, String errorMessage) { + if (!expression) { + throw new IllegalArgumentException(errorMessage); + } + } + + /** + * Does the provided SQL script contain the specified delimiter? + * @param script the SQL script + * @param delim String delimiting each statement - typically a ';' character + */ + public static boolean containsSqlScriptDelimiters(String script, String delim) { + boolean inLiteral = false; + char[] content = script.toCharArray(); + for (int i = 0; i < script.length(); i++) { + if (content[i] == '\'') { + inLiteral = !inLiteral; + } + if (!inLiteral && script.startsWith(delim, i)) { + return true; + } + } + return false; + } + + /** + * Load script from classpath and apply it to the given database + * + * @param databaseDelegate database delegate for script execution + * @param initScriptPath the resource to load the init script from + */ + public static void runInitScript(DatabaseDelegate databaseDelegate, String initScriptPath) { + try { + URL resource = Thread.currentThread().getContextClassLoader().getResource(initScriptPath); + if (resource == null) { + resource = ScriptUtils.class.getClassLoader().getResource(initScriptPath); + if (resource == null) { + LOGGER.warn("Could not load classpath init script: {}", initScriptPath); + throw new ScriptLoadException( + "Could not load classpath init script: " + initScriptPath + ". Resource not found." + ); + } + } + String scripts = IOUtils.toString(resource, StandardCharsets.UTF_8); + executeDatabaseScript(databaseDelegate, initScriptPath, scripts); + } catch (IOException e) { + LOGGER.warn("Could not load classpath init script: {}", initScriptPath); + throw new ScriptLoadException("Could not load classpath init script: " + initScriptPath, e); + } catch (ScriptException e) { + LOGGER.error("Error while executing init script: {}", initScriptPath, e); + throw new UncategorizedScriptException("Error while executing init script: " + initScriptPath, e); + } + } + + public static void executeDatabaseScript(DatabaseDelegate databaseDelegate, String scriptPath, String script) + throws ScriptException { + executeDatabaseScript( + databaseDelegate, + scriptPath, + script, + false, + false, + DEFAULT_COMMENT_PREFIX, + DEFAULT_STATEMENT_SEPARATOR, + DEFAULT_BLOCK_COMMENT_START_DELIMITER, + DEFAULT_BLOCK_COMMENT_END_DELIMITER + ); } /** - * Execute the given database script. - *

Statement separators and comments will be removed before executing - * individual statements within the supplied script. + * Execute the given database script. + *

Statement separators and comments will be removed before executing + * individual statements within the supplied script. *

Do not use this method to execute DDL if you expect rollback. * @param databaseDelegate database delegate for script execution * @param scriptPath the resource (potentially associated with a specific encoding) @@ -328,85 +397,104 @@ public static void executeDatabaseScript(DatabaseDelegate databaseDelegate, Stri *@param continueOnError whether or not to continue without throwing an exception * in the event of an error * @param ignoreFailedDrops whether or not to continue in the event of specifically -* an error on a {@code DROP} statement + * an error on a {@code DROP} statement * @param commentPrefix the prefix that identifies comments in the SQL script — -* typically "--" + * typically "--" * @param separator the script statement separator; defaults to -* {@value #DEFAULT_STATEMENT_SEPARATOR} if not specified and falls back to -* {@value #FALLBACK_STATEMENT_SEPARATOR} as a last resort + * {@value #DEFAULT_STATEMENT_SEPARATOR} if not specified and falls back to + * {@value #FALLBACK_STATEMENT_SEPARATOR} as a last resort * @param blockCommentStartDelimiter the start block comment delimiter; never -* {@code null} or empty + * {@code null} or empty * @param blockCommentEndDelimiter the end block comment delimiter; never -* {@code null} or empty @throws ScriptException if an error occurred while executing the SQL script - */ - public static void executeDatabaseScript(DatabaseDelegate databaseDelegate, String scriptPath, String script, boolean continueOnError, - boolean ignoreFailedDrops, String commentPrefix, String separator, String blockCommentStartDelimiter, - String blockCommentEndDelimiter) throws ScriptException { - - try { - if (LOGGER.isInfoEnabled()) { - LOGGER.info("Executing database script from " + scriptPath); - } - - long startTime = System.currentTimeMillis(); - List statements = new LinkedList<>(); - - if (separator == null) { - separator = DEFAULT_STATEMENT_SEPARATOR; - } - if (!containsSqlScriptDelimiters(script, separator)) { - separator = FALLBACK_STATEMENT_SEPARATOR; - } - - splitSqlScript(scriptPath, script, separator, commentPrefix, blockCommentStartDelimiter, - blockCommentEndDelimiter, statements); - - try (DatabaseDelegate closeableDelegate = databaseDelegate) { - closeableDelegate.execute(statements, scriptPath, continueOnError, ignoreFailedDrops); - } - - long elapsedTime = System.currentTimeMillis() - startTime; - if (LOGGER.isInfoEnabled()) { - LOGGER.info("Executed database script from " + scriptPath + " in " + elapsedTime + " ms."); - } - } - catch (Exception ex) { - if (ex instanceof ScriptException) { - throw (ScriptException) ex; - } - - throw new UncategorizedScriptException( - "Failed to execute database script from resource [" + script + "]", ex); - } - } - - public static class ScriptLoadException extends RuntimeException { - public ScriptLoadException(String message) { - super(message); - } - - public ScriptLoadException(String message, Throwable cause) { - super(message, cause); - } - } - - public static class ScriptParseException extends RuntimeException { - public ScriptParseException(String format, String scriptPath) { - super(String.format(format, scriptPath)); - } - } - - public static class ScriptStatementFailedException extends RuntimeException { - public ScriptStatementFailedException(String statement, int lineNumber, String scriptPath) { - this(statement, lineNumber, scriptPath, null); - } - - public ScriptStatementFailedException(String statement, int lineNumber, String scriptPath, Exception ex) { - super(String.format("Script execution failed (%s:%d): %s", scriptPath, lineNumber, statement), ex); - } - } + * {@code null} or empty @throws ScriptException if an error occurred while executing the SQL script + */ + public static void executeDatabaseScript( + DatabaseDelegate databaseDelegate, + String scriptPath, + String script, + boolean continueOnError, + boolean ignoreFailedDrops, + String commentPrefix, + String separator, + String blockCommentStartDelimiter, + String blockCommentEndDelimiter + ) throws ScriptException { + try { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Executing database script from " + scriptPath); + } + + long startTime = System.currentTimeMillis(); + List statements = new LinkedList<>(); + + if (separator == null) { + separator = DEFAULT_STATEMENT_SEPARATOR; + } + if (!containsSqlScriptDelimiters(script, separator)) { + separator = FALLBACK_STATEMENT_SEPARATOR; + } + + splitSqlScript( + scriptPath, + script, + separator, + commentPrefix, + blockCommentStartDelimiter, + blockCommentEndDelimiter, + statements + ); + + try (DatabaseDelegate closeableDelegate = databaseDelegate) { + closeableDelegate.execute(statements, scriptPath, continueOnError, ignoreFailedDrops); + } + + long elapsedTime = System.currentTimeMillis() - startTime; + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Executed database script from " + scriptPath + " in " + elapsedTime + " ms."); + } + } catch (Exception ex) { + if (ex instanceof ScriptException) { + throw (ScriptException) ex; + } + + throw new UncategorizedScriptException( + "Failed to execute database script from resource [" + script + "]", + ex + ); + } + } + + public static class ScriptLoadException extends RuntimeException { + + public ScriptLoadException(String message) { + super(message); + } + + public ScriptLoadException(String message, Throwable cause) { + super(message, cause); + } + } + + public static class ScriptParseException extends RuntimeException { + + public ScriptParseException(String format, String scriptPath) { + super(String.format(format, scriptPath)); + } + } + + public static class ScriptStatementFailedException extends RuntimeException { + + public ScriptStatementFailedException(String statement, int lineNumber, String scriptPath) { + this(statement, lineNumber, scriptPath, null); + } + + public ScriptStatementFailedException(String statement, int lineNumber, String scriptPath, Exception ex) { + super(String.format("Script execution failed (%s:%d): %s", scriptPath, lineNumber, statement), ex); + } + } public static class UncategorizedScriptException extends RuntimeException { + public UncategorizedScriptException(String s, Exception ex) { super(s, ex); } diff --git a/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptSplittingTest.java b/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptSplittingTest.java index 202a3b995db..bc9476cc83d 100644 --- a/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptSplittingTest.java +++ b/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptSplittingTest.java @@ -4,11 +4,10 @@ import org.junit.Test; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; -import static java.util.Arrays.asList; import static org.junit.Assert.fail; -import static org.testcontainers.ext.ScriptUtils.*; public class ScriptSplittingTest { @@ -16,18 +15,15 @@ public class ScriptSplittingTest { public void testStringDemarcation() { String script = "SELECT 'foo `bar`'; SELECT 'foo -- `bar`'; SELECT 'foo /* `bar`';"; - List expected = asList( - "SELECT 'foo `bar`'", - "SELECT 'foo -- `bar`'", - "SELECT 'foo /* `bar`'" - ); + List expected = Arrays.asList("SELECT 'foo `bar`'", "SELECT 'foo -- `bar`'", "SELECT 'foo /* `bar`'"); splitAndCompare(script, expected); } @Test public void testIssue1547Case1() { - String script = "create database if not exists ttt;\n" + + String script = + "create database if not exists ttt;\n" + "\n" + "use ttt;\n" + "\n" + @@ -43,7 +39,7 @@ public void testIssue1547Case1() { " id bigint auto_increment primary key\n" + ") comment 'bbb';"; - List expected = asList( + List expected = Arrays.asList( "create database if not exists ttt", "use ttt", "create table aaa ( id bigint auto_increment primary key, end_time datetime null COMMENT 'end_time', data_status varchar(16) not null ) comment 'aaa'", @@ -55,14 +51,15 @@ public void testIssue1547Case1() { @Test public void testIssue1547Case2() { - String script = "CREATE TABLE bar (\n" + + String script = + "CREATE TABLE bar (\n" + " end_time VARCHAR(255)\n" + ");\n" + "CREATE TABLE bar (\n" + " end_time VARCHAR(255)\n" + ");"; - List expected = asList( + List expected = Arrays.asList( "CREATE TABLE bar ( end_time VARCHAR(255) )", "CREATE TABLE bar ( end_time VARCHAR(255) )" ); @@ -74,25 +71,17 @@ public void testIssue1547Case2() { public void testUnusualSemicolonPlacement() { String script = "SELECT 1;;;;;SELECT 2;\n;SELECT 3\n; SELECT 4;\n SELECT 5"; - List expected = asList( - "SELECT 1", - "SELECT 2", - "SELECT 3", - "SELECT 4", - "SELECT 5" - ); + List expected = Arrays.asList("SELECT 1", "SELECT 2", "SELECT 3", "SELECT 4", "SELECT 5"); splitAndCompare(script, expected); } @Test public void testCommentedSemicolon() { - String script = "CREATE TABLE bar (\n" + - " foo VARCHAR(255)\n" + - "); \nDROP PROCEDURE IF EXISTS -- ;\n" + - " count_foo"; + String script = + "CREATE TABLE bar (\n" + " foo VARCHAR(255)\n" + "); \nDROP PROCEDURE IF EXISTS -- ;\n" + " count_foo"; - List expected = asList( + List expected = Arrays.asList( "CREATE TABLE bar ( foo VARCHAR(255) )", "DROP PROCEDURE IF EXISTS count_foo" ); @@ -102,11 +91,12 @@ public void testCommentedSemicolon() { @Test public void testStringEscaping() { - String script = "SELECT \"a /* string literal containing comment characters like -- here\";\n" + + String script = + "SELECT \"a /* string literal containing comment characters like -- here\";\n" + "SELECT \"a 'quoting' \\\"scenario ` involving BEGIN keyword\\\" here\";\n" + "SELECT * from `bar`;"; - List expected = asList( + List expected = Arrays.asList( "SELECT \"a /* string literal containing comment characters like -- here\"", "SELECT \"a 'quoting' \\\"scenario ` involving BEGIN keyword\\\" here\"", "SELECT * from `bar`" @@ -119,18 +109,17 @@ public void testStringEscaping() { public void testBlockCommentExclusion() { String script = "INSERT INTO bar (foo) /* ; */ VALUES ('hello world');"; - List expected = asList( - "INSERT INTO bar (foo) VALUES ('hello world')" - ); + List expected = Arrays.asList("INSERT INTO bar (foo) VALUES ('hello world')"); splitAndCompare(script, expected); } @Test public void testBeginEndKeywordCorrectDetection() { - String script = "INSERT INTO something_end (begin_with_the_token, another_field) /*end*/ VALUES /* end */ (' begin ', `end`)-- begin\n;"; + String script = + "INSERT INTO something_end (begin_with_the_token, another_field) /*end*/ VALUES /* end */ (' begin ', `end`)-- begin\n;"; - List expected = asList( + List expected = Arrays.asList( "INSERT INTO something_end (begin_with_the_token, another_field) VALUES (' begin ', `end`)" ); @@ -139,16 +128,17 @@ public void testBeginEndKeywordCorrectDetection() { @Test public void testCommentInStrings() { - String script = "CREATE TABLE bar (foo VARCHAR(255));\n" + + String script = + "CREATE TABLE bar (foo VARCHAR(255));\n" + "\n" + "/* Insert Values */\n" + "INSERT INTO bar (foo) values ('--1');\n" + "INSERT INTO bar (foo) values ('--2');\n" + "INSERT INTO bar (foo) values ('/* something */');\n" + - "/* INSERT INTO bar (foo) values (' */'); -- '*/;\n" + // purposefully broken, to see if it breaks our splitting + "/* INSERT INTO bar (foo) values (' */'); -- '*/;\n" + // purposefully broken, to see if it breaks our splitting "INSERT INTO bar (foo) values ('foo');"; - List expected = asList( + List expected = Arrays.asList( "CREATE TABLE bar (foo VARCHAR(255))", "INSERT INTO bar (foo) values ('--1')", "INSERT INTO bar (foo) values ('--2')", @@ -162,7 +152,8 @@ public void testCommentInStrings() { @Test public void testMultipleBeginEndDetection() { - String script = "CREATE TABLE bar (foo VARCHAR(255));\n" + + String script = + "CREATE TABLE bar (foo VARCHAR(255));\n" + "\n" + "CREATE TABLE gender (gender VARCHAR(255));\n" + "CREATE TABLE ending (ending VARCHAR(255));\n" + @@ -187,24 +178,16 @@ public void testMultipleBeginEndDetection() { "\n" + "CREATE TABLE foo (bar VARCHAR(255));"; - List expected = asList( + List expected = Arrays.asList( "CREATE TABLE bar (foo VARCHAR(255))", "CREATE TABLE gender (gender VARCHAR(255))", "CREATE TABLE ending (ending VARCHAR(255))", "CREATE TABLE end2 (end2 VARCHAR(255))", "CREATE TABLE end_2 (end2 VARCHAR(255))", - "BEGIN\n" + - " INSERT INTO ending values ('ending');\n" + - "END", - "BEGIN\n" + - " INSERT INTO ending values ('ending');\n" + - "END", - "BEGIN--Hello\n" + - " INSERT INTO ending values ('ending');\n" + - "END", - "BEGIN\n" + - " INSERT INTO ending values ('ending');\n" + - "END", + "BEGIN\n" + " INSERT INTO ending values ('ending');\n" + "END", + "BEGIN\n" + " INSERT INTO ending values ('ending');\n" + "END", + "BEGIN--Hello\n" + " INSERT INTO ending values ('ending');\n" + "END", + "BEGIN\n" + " INSERT INTO ending values ('ending');\n" + "END", "CREATE TABLE foo (bar VARCHAR(255))" ); @@ -213,7 +196,8 @@ public void testMultipleBeginEndDetection() { @Test public void testProcedureBlock() { - String script = "CREATE PROCEDURE count_foo()\n" + + String script = + "CREATE PROCEDURE count_foo()\n" + " BEGIN\n" + "\n" + " BEGIN\n" + @@ -239,31 +223,31 @@ public void testProcedureBlock() { "\n" + " END /*; */;"; - List expected = asList( + List expected = Arrays.asList( "CREATE PROCEDURE count_foo() BEGIN\n" + - "\n" + - " BEGIN\n" + - " SELECT *\n" + - " FROM bar;\n" + - " SELECT 1\n" + - " FROM dual;\n" + - " END;\n" + - "\n" + - " BEGIN\n" + - " select * from bar;\n" + - " END;\n" + - "\n" + - " -- we can do comments\n" + - "\n" + - " /* including block\n" + - " comments\n" + - " */\n" + - "\n" + - " /* what if BEGIN appears inside a comment? */\n" + - "\n" + - " select \"or what if BEGIN appears inside a literal?\";\n" + - "\n" + - " END" + "\n" + + " BEGIN\n" + + " SELECT *\n" + + " FROM bar;\n" + + " SELECT 1\n" + + " FROM dual;\n" + + " END;\n" + + "\n" + + " BEGIN\n" + + " select * from bar;\n" + + " END;\n" + + "\n" + + " -- we can do comments\n" + + "\n" + + " /* including block\n" + + " comments\n" + + " */\n" + + "\n" + + " /* what if BEGIN appears inside a comment? */\n" + + "\n" + + " select \"or what if BEGIN appears inside a literal?\";\n" + + "\n" + + " END" ); splitAndCompare(script, expected); @@ -283,12 +267,13 @@ public void testUnclosedBlockComment() { @Test public void testIssue1452Case() { - String script = "create table test (text VARCHAR(255));\n" + + String script = + "create table test (text VARCHAR(255));\n" + "\n" + "/* some comment */\n" + "insert into `test` (`text`) values ('a b');"; - List expected = asList( + List expected = Arrays.asList( "create table test (text VARCHAR(255))", "insert into `test` (`text`) values ('a b')" ); @@ -303,7 +288,15 @@ private void splitAndCompare(String script, List expected) { private List doSplit(String script) { final List statements = new ArrayList<>(); - ScriptUtils.splitSqlScript("ignored", script, DEFAULT_STATEMENT_SEPARATOR, DEFAULT_COMMENT_PREFIX, DEFAULT_BLOCK_COMMENT_START_DELIMITER, DEFAULT_BLOCK_COMMENT_END_DELIMITER, statements); + ScriptUtils.splitSqlScript( + "ignored", + script, + ScriptUtils.DEFAULT_STATEMENT_SEPARATOR, + ScriptUtils.DEFAULT_COMMENT_PREFIX, + ScriptUtils.DEFAULT_BLOCK_COMMENT_START_DELIMITER, + ScriptUtils.DEFAULT_BLOCK_COMMENT_END_DELIMITER, + statements + ); return statements; } } diff --git a/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java b/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java index c1a252936ea..29e2ce8aa07 100644 --- a/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java +++ b/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java @@ -20,10 +20,13 @@ public class Db2Container extends JdbcDatabaseContainer { @Deprecated public static final String DEFAULT_TAG = "11.5.0.0a"; + public static final int DB2_PORT = 50000; private String databaseName = "test"; + private String username = "db2inst1"; + private String password = "foobar1234"; /** @@ -40,11 +43,11 @@ public Db2Container(String dockerImageName) { public Db2Container(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); withPrivilegedMode(true); - this.waitStrategy = new LogMessageWaitStrategy() + this.waitStrategy = + new LogMessageWaitStrategy() .withRegEx(".*Setup has completed\\..*") .withStartupTimeout(Duration.of(10, ChronoUnit.MINUTES)); @@ -69,10 +72,12 @@ protected void configure() { addEnv("DB2INST1_PASSWORD", password); // These settings help the DB2 container start faster - if (!getEnvMap().containsKey("AUTOCONFIG")) + if (!getEnvMap().containsKey("AUTOCONFIG")) { addEnv("AUTOCONFIG", "false"); - if (!getEnvMap().containsKey("ARCHIVE_LOGS")) + } + if (!getEnvMap().containsKey("ARCHIVE_LOGS")) { addEnv("ARCHIVE_LOGS", "false"); + } } /** @@ -92,8 +97,7 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { String additionalUrlParams = constructUrlParameters(":", ";", ";"); - return "jdbc:db2://" + getHost() + ":" + getMappedPort(DB2_PORT) + - "/" + databaseName + additionalUrlParams; + return "jdbc:db2://" + getHost() + ":" + getMappedPort(DB2_PORT) + "/" + databaseName + additionalUrlParams; } @Override diff --git a/modules/db2/src/main/java/org/testcontainers/containers/Db2ContainerProvider.java b/modules/db2/src/main/java/org/testcontainers/containers/Db2ContainerProvider.java index 618b97c3ca6..42ac21f271b 100644 --- a/modules/db2/src/main/java/org/testcontainers/containers/Db2ContainerProvider.java +++ b/modules/db2/src/main/java/org/testcontainers/containers/Db2ContainerProvider.java @@ -3,6 +3,7 @@ import org.testcontainers.utility.DockerImageName; public class Db2ContainerProvider extends JdbcDatabaseContainerProvider { + @Override public boolean supports(String databaseType) { return databaseType.equals(Db2Container.NAME); diff --git a/modules/db2/src/test/java/org/testcontainers/jdbc/db2/DB2JDBCDriverTest.java b/modules/db2/src/test/java/org/testcontainers/jdbc/db2/DB2JDBCDriverTest.java index 2c87a4a1a65..4a03f824a28 100644 --- a/modules/db2/src/test/java/org/testcontainers/jdbc/db2/DB2JDBCDriverTest.java +++ b/modules/db2/src/test/java/org/testcontainers/jdbc/db2/DB2JDBCDriverTest.java @@ -4,18 +4,18 @@ import org.junit.runners.Parameterized; import org.testcontainers.jdbc.AbstractJDBCDriverTest; +import java.util.Arrays; import java.util.EnumSet; -import static java.util.Arrays.asList; - @RunWith(Parameterized.class) public class DB2JDBCDriverTest extends AbstractJDBCDriverTest { @Parameterized.Parameters(name = "{index} - {0}") public static Iterable data() { - return asList( - new Object[][]{ - {"jdbc:tc:db2://hostname/databasename", EnumSet.noneOf(Options.class)} - }); + return Arrays.asList( + new Object[][] { // + { "jdbc:tc:db2://hostname/databasename", EnumSet.noneOf(Options.class) }, + } + ); } } diff --git a/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java b/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java index b7a1d5478e8..8f3489eae95 100644 --- a/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java +++ b/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java @@ -1,24 +1,22 @@ package org.testcontainers.junit.db2; import org.junit.Test; +import org.testcontainers.Db2TestImages; import org.testcontainers.containers.Db2Container; import org.testcontainers.db.AbstractContainerDatabaseTest; import java.sql.ResultSet; import java.sql.SQLException; -import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.testcontainers.Db2TestImages.DB2_IMAGE; public class SimpleDb2Test extends AbstractContainerDatabaseTest { @Test public void testSimple() throws SQLException { - try (Db2Container db2 = new Db2Container(DB2_IMAGE) - .acceptLicense()) { - + try (Db2Container db2 = new Db2Container(Db2TestImages.DB2_IMAGE).acceptLicense()) { db2.start(); ResultSet resultSet = performQuery(db2, "SELECT 1 FROM SYSIBM.SYSDUMMY1"); @@ -30,10 +28,11 @@ public void testSimple() throws SQLException { @Test public void testWithAdditionalUrlParamInJdbcUrl() { - try (Db2Container db2 = new Db2Container(DB2_IMAGE) - .withUrlParam("sslConnection", "false") - .acceptLicense()) { - + try ( + Db2Container db2 = new Db2Container(Db2TestImages.DB2_IMAGE) + .withUrlParam("sslConnection", "false") + .acceptLicense() + ) { db2.start(); String jdbcUrl = db2.getJdbcUrl(); diff --git a/modules/dynalite/src/main/java/org/testcontainers/dynamodb/DynaliteContainer.java b/modules/dynalite/src/main/java/org/testcontainers/dynamodb/DynaliteContainer.java index 8900964a8bb..401fa8782b1 100644 --- a/modules/dynalite/src/main/java/org/testcontainers/dynamodb/DynaliteContainer.java +++ b/modules/dynalite/src/main/java/org/testcontainers/dynamodb/DynaliteContainer.java @@ -15,7 +15,9 @@ public class DynaliteContainer extends GenericContainer { private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("quay.io/testcontainers/dynalite"); + private static final String DEFAULT_TAG = "v1.2.1-1"; + private static final int MAPPED_PORT = 4567; /** @@ -32,13 +34,11 @@ public DynaliteContainer(String dockerImageName) { public DynaliteContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); withExposedPorts(MAPPED_PORT); } - /** * Gets a preconfigured {@link AmazonDynamoDB} client object for connecting to this * container. @@ -46,10 +46,11 @@ public DynaliteContainer(final DockerImageName dockerImageName) { * @return preconfigured client */ public AmazonDynamoDB getClient() { - return AmazonDynamoDBClientBuilder.standard() - .withEndpointConfiguration(getEndpointConfiguration()) - .withCredentials(getCredentials()) - .build(); + return AmazonDynamoDBClientBuilder + .standard() + .withEndpointConfiguration(getEndpointConfiguration()) + .withCredentials(getCredentials()) + .build(); } /** @@ -59,9 +60,10 @@ public AmazonDynamoDB getClient() { * @return endpoint configuration */ public AwsClientBuilder.EndpointConfiguration getEndpointConfiguration() { - return new AwsClientBuilder.EndpointConfiguration("http://" + - this.getHost() + ":" + - this.getMappedPort(MAPPED_PORT), null); + return new AwsClientBuilder.EndpointConfiguration( + "http://" + this.getHost() + ":" + this.getMappedPort(MAPPED_PORT), + null + ); } /** @@ -72,6 +74,4 @@ public AwsClientBuilder.EndpointConfiguration getEndpointConfiguration() { public AWSCredentialsProvider getCredentials() { return new AWSStaticCredentialsProvider(new BasicAWSCredentials("dummy", "dummy")); } - - } diff --git a/modules/dynalite/src/test/java/org/testcontainers/dynamodb/DynaliteContainerTest.java b/modules/dynalite/src/test/java/org/testcontainers/dynamodb/DynaliteContainerTest.java index 0b440939139..ced5931de31 100644 --- a/modules/dynalite/src/test/java/org/testcontainers/dynamodb/DynaliteContainerTest.java +++ b/modules/dynalite/src/test/java/org/testcontainers/dynamodb/DynaliteContainerTest.java @@ -18,17 +18,20 @@ public class DynaliteContainerTest { - private static final DockerImageName DYNALITE_IMAGE = DockerImageName.parse("quay.io/testcontainers/dynalite:v1.2.1-1"); + private static final DockerImageName DYNALITE_IMAGE = DockerImageName.parse( + "quay.io/testcontainers/dynalite:v1.2.1-1" + ); @Rule public DynaliteContainer dynamoDB = new DynaliteContainer(DYNALITE_IMAGE); @Test public void simpleTestWithManualClientCreation() { - final AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard() - .withEndpointConfiguration(dynamoDB.getEndpointConfiguration()) - .withCredentials(dynamoDB.getCredentials()) - .build(); + final AmazonDynamoDB client = AmazonDynamoDBClientBuilder + .standard() + .withEndpointConfiguration(dynamoDB.getEndpointConfiguration()) + .withCredentials(dynamoDB.getCredentials()) + .build(); runTest(client); } @@ -42,13 +45,10 @@ public void simpleTestWithProvidedClient() { private void runTest(AmazonDynamoDB client) { CreateTableRequest request = new CreateTableRequest() - .withAttributeDefinitions(new AttributeDefinition( - "Name", ScalarAttributeType.S)) - .withKeySchema(new KeySchemaElement("Name", KeyType.HASH)) - .withProvisionedThroughput(new ProvisionedThroughput( - 10L, 10L)) - .withTableName("foo"); - + .withAttributeDefinitions(new AttributeDefinition("Name", ScalarAttributeType.S)) + .withKeySchema(new KeySchemaElement("Name", KeyType.HASH)) + .withProvisionedThroughput(new ProvisionedThroughput(10L, 10L)) + .withTableName("foo"); client.createTable(request); @@ -56,6 +56,10 @@ private void runTest(AmazonDynamoDB client) { assertNotNull("the description is not null", tableDescription); assertEquals("the table has the right name", "foo", tableDescription.getTableName()); - assertEquals("the name has the right primary key", "Name", tableDescription.getKeySchema().get(0).getAttributeName()); + assertEquals( + "the name has the right primary key", + "Name", + tableDescription.getKeySchema().get(0).getAttributeName() + ); } } diff --git a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java index ac0b367f403..5a64a5e67a2 100644 --- a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java +++ b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java @@ -11,8 +11,6 @@ import org.testcontainers.utility.ComparableVersion; import org.testcontainers.utility.DockerImageName; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManagerFactory; import java.io.ByteArrayInputStream; import java.net.InetSocketAddress; import java.security.KeyStore; @@ -20,6 +18,9 @@ import java.security.cert.CertificateFactory; import java.util.Optional; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; + /** * Represents an elasticsearch docker instance which exposes by default port 9200 and 9300 (transport.tcp.port) * The docker image is by default fetched from docker.elastic.co/elasticsearch/elasticsearch @@ -47,8 +48,13 @@ public class ElasticsearchContainer extends GenericContainer caCertAsBytes = Optional.empty(); + private String certPath = "/usr/share/elasticsearch/config/certs/http_ca.crt"; /** @@ -83,7 +92,6 @@ public ElasticsearchContainer(String dockerImageName) { */ public ElasticsearchContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME, DEFAULT_OSS_IMAGE_NAME); this.isOss = dockerImageName.isCompatibleWith(DEFAULT_OSS_IMAGE_NAME); @@ -91,7 +99,8 @@ public ElasticsearchContainer(final DockerImageName dockerImageName) { withNetworkAliases("elasticsearch-" + Base58.randomString(6)); withEnv("discovery.type", "single-node"); addExposedPorts(ELASTICSEARCH_DEFAULT_PORT, ELASTICSEARCH_DEFAULT_TCP_PORT); - this.isAtLeastMajorVersion8 = new ComparableVersion(dockerImageName.getVersionPart()).isGreaterThanOrEqualTo("8.0.0"); + this.isAtLeastMajorVersion8 = + new ComparableVersion(dockerImageName.getVersionPart()).isGreaterThanOrEqualTo("8.0.0"); // regex that // matches 8.0 JSON logging with no whitespace between message field and content // matches 7.x JSON logging with whitespace between message field and content @@ -159,8 +168,9 @@ public SSLContext createSslContextFromCa() { */ public ElasticsearchContainer withPassword(String password) { if (isOss) { - throw new IllegalArgumentException("You can not activate security on Elastic OSS Image. " + - "Please switch to the default distribution"); + throw new IllegalArgumentException( + "You can not activate security on Elastic OSS Image. " + "Please switch to the default distribution" + ); } withEnv("ELASTIC_PASSWORD", password); if (!isAtLeastMajorVersion8) { diff --git a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java index 5b6b5ce3194..4427163ca56 100644 --- a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java +++ b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java @@ -25,12 +25,13 @@ import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MountableFile; -import javax.net.ssl.SSLHandshakeException; import java.io.IOException; -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.is; +import javax.net.ssl.SSLHandshakeException; + import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; public class ElasticsearchContainerTest { @@ -39,10 +40,10 @@ public class ElasticsearchContainerTest { * Elasticsearch version which should be used for the Tests */ private static final String ELASTICSEARCH_VERSION = "7.9.2"; - private static final DockerImageName ELASTICSEARCH_IMAGE = - DockerImageName - .parse("docker.elastic.co/elasticsearch/elasticsearch") - .withTag(ELASTICSEARCH_VERSION); + + private static final DockerImageName ELASTICSEARCH_IMAGE = DockerImageName + .parse("docker.elastic.co/elasticsearch/elasticsearch") + .withTag(ELASTICSEARCH_VERSION); /** * Elasticsearch default username, when secured @@ -55,6 +56,7 @@ public class ElasticsearchContainerTest { private static final String ELASTICSEARCH_PASSWORD = "changeme"; private RestClient client = null; + private RestClient anonymousClient = null; @After @@ -74,8 +76,8 @@ public void stopRestClient() throws IOException { @Deprecated // We will remove this test in the future public void elasticsearchDeprecatedCtorTest() throws IOException { // Create the elasticsearch container. - try (ElasticsearchContainer container = new ElasticsearchContainer() - .withEnv("foo", "bar") // dummy env for compiler checking correct generics usage + try ( + ElasticsearchContainer container = new ElasticsearchContainer().withEnv("foo", "bar") // dummy env for compiler checking correct generics usage ) { // Start the container. This step might take some time... container.start(); @@ -96,8 +98,8 @@ public void elasticsearchDeprecatedCtorTest() throws IOException { @Test public void elasticsearchDefaultTest() throws IOException { // Create the elasticsearch container. - try (ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE) - .withEnv("foo", "bar") // dummy env for compiler checking correct generics usage + try ( + ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE).withEnv("foo", "bar") // dummy env for compiler checking correct generics usage ) { // Start the container. This step might take some time... container.start(); @@ -117,14 +119,18 @@ public void elasticsearchDefaultTest() throws IOException { @Test public void elasticsearchSecuredTest() throws IOException { - try (ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE) - .withPassword(ELASTICSEARCH_PASSWORD)) { + try ( + ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE) + .withPassword(ELASTICSEARCH_PASSWORD) + ) { container.start(); // The cluster should be secured so it must fail when we try to access / without credentials - assertThrows("We should not be able to access / URI with an anonymous client.", + assertThrows( + "We should not be able to access / URI with an anonymous client.", ResponseException.class, - () -> getAnonymousClient(container).performRequest(new Request("GET", "/"))); + () -> getAnonymousClient(container).performRequest(new Request("GET", "/")) + ); // But it should work when we try to access / with the proper login and password Response response = getClient(container).performRequest(new Request("GET", "/")); @@ -146,22 +152,24 @@ public void elasticsearchVersion() throws IOException { @Test public void elasticsearchOssImage() throws IOException { - try (ElasticsearchContainer container = - // ossContainer { - new ElasticsearchContainer( - DockerImageName - .parse("docker.elastic.co/elasticsearch/elasticsearch-oss") - .withTag(ELASTICSEARCH_VERSION) - ) - // } + try ( + // ossContainer { + ElasticsearchContainer container = new ElasticsearchContainer( + DockerImageName + .parse("docker.elastic.co/elasticsearch/elasticsearch-oss") + .withTag(ELASTICSEARCH_VERSION) + ) + // } ) { container.start(); Response response = getClient(container).performRequest(new Request("GET", "/")); assertThat(response.getStatusLine().getStatusCode(), is(200)); // The OSS image does not have any feature under Elastic License - assertThrows("We should not have /_xpack endpoint with an OSS License", + assertThrows( + "We should not have /_xpack endpoint with an OSS License", ResponseException.class, - () -> getClient(container).performRequest(new Request("GET", "/_xpack/"))); + () -> getClient(container).performRequest(new Request("GET", "/_xpack/")) + ); } } @@ -175,12 +183,18 @@ public void restClientClusterHealth() throws IOException { // Do whatever you want with the rest client ... final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); - credentialsProvider.setCredentials(AuthScope.ANY, - new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ELASTICSEARCH_PASSWORD)); - - client = RestClient.builder(HttpHost.create(container.getHttpHostAddress())) - .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)) - .build(); + credentialsProvider.setCredentials( + AuthScope.ANY, + new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ELASTICSEARCH_PASSWORD) + ); + + client = + RestClient + .builder(HttpHost.create(container.getHttpHostAddress())) + .setHttpClientConfigCallback(httpClientBuilder -> { + return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); + }) + .build(); Response response = client.performRequest(new Request("GET", "/_cluster/health")); // }} @@ -195,20 +209,28 @@ public void restClientClusterHealth() throws IOException { public void restClientSecuredClusterHealth() throws IOException { // httpClientSecuredContainer { // Create the elasticsearch container. - try (ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE) - // With a password - .withPassword(ELASTICSEARCH_PASSWORD)) { + try ( + ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE) + // With a password + .withPassword(ELASTICSEARCH_PASSWORD) + ) { // Start the container. This step might take some time... container.start(); // Create the secured client. final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); - credentialsProvider.setCredentials(AuthScope.ANY, - new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ELASTICSEARCH_PASSWORD)); - - client = RestClient.builder(HttpHost.create(container.getHttpHostAddress())) - .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)) - .build(); + credentialsProvider.setCredentials( + AuthScope.ANY, + new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ELASTICSEARCH_PASSWORD) + ); + + client = + RestClient + .builder(HttpHost.create(container.getHttpHostAddress())) + .setHttpClientConfigCallback(httpClientBuilder -> { + return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); + }) + .build(); Response response = client.performRequest(new Request("GET", "/_cluster/health")); // }} @@ -224,7 +246,7 @@ public void restClientSecuredClusterHealth() throws IOException { public void transportClientClusterHealth() { // transportClientContainer { // Create the elasticsearch container. - try (ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE)){ + try (ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE)) { // Start the container. This step might take some time... container.start(); @@ -232,8 +254,10 @@ public void transportClientClusterHealth() { TransportAddress transportAddress = new TransportAddress(container.getTcpHost()); String expectedClusterName = "docker-cluster"; Settings settings = Settings.builder().put("cluster.name", expectedClusterName).build(); - try (TransportClient transportClient = new PreBuiltTransportClient(settings) - .addTransportAddress(transportAddress)) { + try ( + TransportClient transportClient = new PreBuiltTransportClient(settings) + .addTransportAddress(transportAddress) + ) { ClusterHealthResponse healths = transportClient.admin().cluster().prepareHealth().get(); String clusterName = healths.getClusterName(); // }}} @@ -247,19 +271,27 @@ public void transportClientClusterHealth() { @Test public void incompatibleSettingsTest() { // The OSS image can not use security feature - assertThrows("We should not be able to activate security with an OSS License", + assertThrows( + "We should not be able to activate security with an OSS License", IllegalArgumentException.class, - () -> new ElasticsearchContainer( - DockerImageName - .parse("docker.elastic.co/elasticsearch/elasticsearch-oss") - .withTag(ELASTICSEARCH_VERSION)) - .withPassword("foo") + () -> { + new ElasticsearchContainer( + DockerImageName + .parse("docker.elastic.co/elasticsearch/elasticsearch-oss") + .withTag(ELASTICSEARCH_VERSION) + ) + .withPassword("foo"); + } ); } @Test public void testElasticsearch8SecureByDefault() throws Exception { - try (ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:8.1.2")) { + try ( + ElasticsearchContainer container = new ElasticsearchContainer( + "docker.elastic.co/elasticsearch/elasticsearch:8.1.2" + ) + ) { // Start the container. This step might take some time... container.start(); @@ -273,29 +305,40 @@ public void testElasticsearch8SecureByDefault() throws Exception { public void testElasticsearch8SecureByDefaultCustomCaCertFails() throws Exception { final MountableFile mountableFile = MountableFile.forClasspathResource("http_ca.crt"); String caPath = "/tmp/http_ca.crt"; - try (ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:8.1.2") - .withCopyToContainer(mountableFile, caPath) - .withCertPath(caPath)) { - + try ( + ElasticsearchContainer container = new ElasticsearchContainer( + "docker.elastic.co/elasticsearch/elasticsearch:8.1.2" + ) + .withCopyToContainer(mountableFile, caPath) + .withCertPath(caPath) + ) { container.start(); // this is expected, as a different cert is used for creating the SSL context - assertThrows("PKIX path validation failed: java.security.cert.CertPathValidatorException: Path does not chain with any of the trust anchors", SSLHandshakeException.class, () -> getClusterHealth(container)); + assertThrows( + "PKIX path validation failed: java.security.cert.CertPathValidatorException: Path does not chain with any of the trust anchors", + SSLHandshakeException.class, + () -> getClusterHealth(container) + ); } } @Test public void testElasticsearch8SecureByDefaultHttpWaitStrategy() throws Exception { - final HttpWaitStrategy httpsWaitStrategy = Wait.forHttps("/") + final HttpWaitStrategy httpsWaitStrategy = Wait + .forHttps("/") .forPort(9200) .forStatusCode(200) .withBasicCredentials(ELASTICSEARCH_USERNAME, ELASTICSEARCH_PASSWORD) // trusting self-signed certificate .allowInsecure(); - try (ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:8.1.2") - .waitingFor(httpsWaitStrategy)) { - + try ( + ElasticsearchContainer container = new ElasticsearchContainer( + "docker.elastic.co/elasticsearch/elasticsearch:8.1.2" + ) + .waitingFor(httpsWaitStrategy) + ) { // Start the container. This step might take some time... container.start(); @@ -311,7 +354,9 @@ public void testElasticsearch8SecureByDefaultFailsSilentlyOnLatestImages() throw // even though the version might be older than version 8 // this tags an old 7.x version as :latest tagImage("docker.elastic.co/elasticsearch/elasticsearch:7.9.2", "elasticsearch-tc-older-release", "latest"); - DockerImageName image = DockerImageName.parse("elasticsearch-tc-older-release:latest").asCompatibleSubstituteFor("docker.elastic.co/elasticsearch/elasticsearch"); + DockerImageName image = DockerImageName + .parse("elasticsearch-tc-older-release:latest") + .asCompatibleSubstituteFor("docker.elastic.co/elasticsearch/elasticsearch"); try (ElasticsearchContainer container = new ElasticsearchContainer(image)) { container.start(); @@ -324,27 +369,31 @@ public void testElasticsearch8SecureByDefaultFailsSilentlyOnLatestImages() throw private void tagImage(String sourceImage, String targetImage, String targetTag) throws InterruptedException { DockerClient dockerClient = DockerClientFactory.instance().client(); - dockerClient.tagImageCmd( - new RemoteDockerImage(DockerImageName.parse(sourceImage)).get(), - targetImage, - targetTag - ) + dockerClient + .tagImageCmd(new RemoteDockerImage(DockerImageName.parse(sourceImage)).get(), targetImage, targetTag) .exec(); } private Response getClusterHealth(ElasticsearchContainer container) throws IOException { // Create the secured client. final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); - credentialsProvider.setCredentials(AuthScope.ANY, - new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ElasticsearchContainer.ELASTICSEARCH_DEFAULT_PASSWORD)); + credentialsProvider.setCredentials( + AuthScope.ANY, + new UsernamePasswordCredentials( + ELASTICSEARCH_USERNAME, + ElasticsearchContainer.ELASTICSEARCH_DEFAULT_PASSWORD + ) + ); - client = RestClient.builder(HttpHost.create("https://" + container.getHttpHostAddress())) - .setHttpClientConfigCallback(httpClientBuilder -> { - httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); - httpClientBuilder.setSSLContext(container.createSslContextFromCa()); - return httpClientBuilder; - }) - .build(); + client = + RestClient + .builder(HttpHost.create("https://" + container.getHttpHostAddress())) + .setHttpClientConfigCallback(httpClientBuilder -> { + httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); + httpClientBuilder.setSSLContext(container.createSslContextFromCa()); + return httpClientBuilder; + }) + .build(); return client.performRequest(new Request("GET", "/_cluster/health")); } @@ -352,12 +401,18 @@ private Response getClusterHealth(ElasticsearchContainer container) throws IOExc private RestClient getClient(ElasticsearchContainer container) { if (client == null) { final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); - credentialsProvider.setCredentials(AuthScope.ANY, - new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ELASTICSEARCH_PASSWORD)); - - client = RestClient.builder(HttpHost.create(container.getHttpHostAddress())) - .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)) - .build(); + credentialsProvider.setCredentials( + AuthScope.ANY, + new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ELASTICSEARCH_PASSWORD) + ); + + client = + RestClient + .builder(HttpHost.create(container.getHttpHostAddress())) + .setHttpClientConfigCallback(httpClientBuilder -> { + return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); + }) + .build(); } return client; diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/BigtableEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/BigtableEmulatorContainer.java index 4b81e3964ce..84515fc7244 100644 --- a/modules/gcloud/src/main/java/org/testcontainers/containers/BigtableEmulatorContainer.java +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/BigtableEmulatorContainer.java @@ -13,19 +13,20 @@ */ public class BigtableEmulatorContainer extends GenericContainer { - private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk"); + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse( + "gcr.io/google.com/cloudsdktool/cloud-sdk" + ); private static final String CMD = "gcloud beta emulators bigtable start --host-port 0.0.0.0:9000"; + private static final int PORT = 9000; public BigtableEmulatorContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); withExposedPorts(PORT); - setWaitStrategy(new LogMessageWaitStrategy() - .withRegEx("(?s).*running.*$")); + setWaitStrategy(new LogMessageWaitStrategy().withRegEx("(?s).*running.*$")); withCommand("/bin/sh", "-c", CMD); } diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java index 58e5c023a0e..b0716829875 100644 --- a/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java @@ -12,14 +12,17 @@ */ public class DatastoreEmulatorContainer extends GenericContainer { - private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk"); + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse( + "gcr.io/google.com/cloudsdktool/cloud-sdk" + ); + + private static final String CMD = + "gcloud beta emulators datastore start --project test-project --host-port 0.0.0.0:8081"; - private static final String CMD = "gcloud beta emulators datastore start --project test-project --host-port 0.0.0.0:8081"; private static final int HTTP_PORT = 8081; public DatastoreEmulatorContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); withExposedPorts(HTTP_PORT); diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/FirestoreEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/FirestoreEmulatorContainer.java index c36082dfeb6..a04c1e5831f 100644 --- a/modules/gcloud/src/main/java/org/testcontainers/containers/FirestoreEmulatorContainer.java +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/FirestoreEmulatorContainer.java @@ -12,19 +12,20 @@ */ public class FirestoreEmulatorContainer extends GenericContainer { - private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk"); + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse( + "gcr.io/google.com/cloudsdktool/cloud-sdk" + ); private static final String CMD = "gcloud beta emulators firestore start --host-port 0.0.0.0:8080"; + private static final int PORT = 8080; public FirestoreEmulatorContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); withExposedPorts(PORT); - setWaitStrategy(new LogMessageWaitStrategy() - .withRegEx("(?s).*running.*$")); + setWaitStrategy(new LogMessageWaitStrategy().withRegEx("(?s).*running.*$")); withCommand("/bin/sh", "-c", CMD); } diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/PubSubEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/PubSubEmulatorContainer.java index 231f7626044..8d9f76e44ae 100644 --- a/modules/gcloud/src/main/java/org/testcontainers/containers/PubSubEmulatorContainer.java +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/PubSubEmulatorContainer.java @@ -12,19 +12,20 @@ */ public class PubSubEmulatorContainer extends GenericContainer { - private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk"); + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse( + "gcr.io/google.com/cloudsdktool/cloud-sdk" + ); private static final String CMD = "gcloud beta emulators pubsub start --host-port 0.0.0.0:8085"; + private static final int PORT = 8085; public PubSubEmulatorContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); withExposedPorts(8085); - setWaitStrategy(new LogMessageWaitStrategy() - .withRegEx("(?s).*started.*$")); + setWaitStrategy(new LogMessageWaitStrategy().withRegEx("(?s).*started.*$")); withCommand("/bin/sh", "-c", CMD); } diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/SpannerEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/SpannerEmulatorContainer.java index 0ae8e89dc06..dc45b004c6c 100644 --- a/modules/gcloud/src/main/java/org/testcontainers/containers/SpannerEmulatorContainer.java +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/SpannerEmulatorContainer.java @@ -10,19 +10,20 @@ */ public class SpannerEmulatorContainer extends GenericContainer { - private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("gcr.io/cloud-spanner-emulator/emulator"); + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse( + "gcr.io/cloud-spanner-emulator/emulator" + ); private static final int GRPC_PORT = 9010; + private static final int HTTP_PORT = 9020; public SpannerEmulatorContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); withExposedPorts(GRPC_PORT, HTTP_PORT); - setWaitStrategy(new LogMessageWaitStrategy() - .withRegEx(".*Cloud Spanner emulator running\\..*")); + setWaitStrategy(new LogMessageWaitStrategy().withRegEx(".*Cloud Spanner emulator running\\..*")); } /** diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/BigtableEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/BigtableEmulatorContainerTest.java index 4ba59a75488..3e22231220b 100644 --- a/modules/gcloud/src/test/java/org/testcontainers/containers/BigtableEmulatorContainerTest.java +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/BigtableEmulatorContainerTest.java @@ -1,22 +1,13 @@ package org.testcontainers.containers; -import static org.assertj.core.api.Assertions.assertThat; - -import com.google.api.core.ApiFuture; -import com.google.api.gax.batching.Batcher; import com.google.api.gax.core.CredentialsProvider; import com.google.api.gax.core.NoCredentialsProvider; import com.google.api.gax.grpc.GrpcTransportChannel; import com.google.api.gax.rpc.FixedTransportChannelProvider; import com.google.api.gax.rpc.TransportChannelProvider; -import com.google.cloud.bigtable.admin.v2.BigtableInstanceAdminClient; import com.google.cloud.bigtable.admin.v2.BigtableTableAdminClient; -import com.google.cloud.bigtable.admin.v2.models.CreateInstanceRequest; import com.google.cloud.bigtable.admin.v2.models.CreateTableRequest; -import com.google.cloud.bigtable.admin.v2.models.Instance; import com.google.cloud.bigtable.admin.v2.models.Table; -import com.google.cloud.bigtable.admin.v2.stub.BigtableInstanceAdminStub; -import com.google.cloud.bigtable.admin.v2.stub.BigtableInstanceAdminStubSettings; import com.google.cloud.bigtable.admin.v2.stub.BigtableTableAdminStubSettings; import com.google.cloud.bigtable.admin.v2.stub.EnhancedBigtableTableAdminStub; import com.google.cloud.bigtable.data.v2.BigtableDataClient; @@ -24,20 +15,22 @@ import com.google.cloud.bigtable.data.v2.models.Row; import com.google.cloud.bigtable.data.v2.models.RowCell; import com.google.cloud.bigtable.data.v2.models.RowMutation; -import com.google.cloud.bigtable.data.v2.models.RowMutationEntry; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; -import java.io.IOException; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.ExecutionException; import org.junit.Rule; import org.junit.Test; import org.testcontainers.utility.DockerImageName; +import java.io.IOException; +import java.util.List; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; + public class BigtableEmulatorContainerTest { public static final String PROJECT_ID = "test-project"; + public static final String INSTANCE_ID = "test-instance"; @Rule @@ -45,28 +38,31 @@ public class BigtableEmulatorContainerTest { public BigtableEmulatorContainer emulator = new BigtableEmulatorContainer( DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:367.0.0-emulators") ); + // } @Test // testWithEmulatorContainer { public void testSimple() throws IOException, InterruptedException, ExecutionException { - ManagedChannel channel = ManagedChannelBuilder.forTarget(emulator.getEmulatorEndpoint()) - .usePlaintext().build(); + ManagedChannel channel = ManagedChannelBuilder.forTarget(emulator.getEmulatorEndpoint()).usePlaintext().build(); - TransportChannelProvider channelProvider = FixedTransportChannelProvider.create(GrpcTransportChannel.create(channel)); + TransportChannelProvider channelProvider = FixedTransportChannelProvider.create( + GrpcTransportChannel.create(channel) + ); NoCredentialsProvider credentialsProvider = NoCredentialsProvider.create(); try { - createTable(channelProvider, credentialsProvider, "test-table" ); + createTable(channelProvider, credentialsProvider, "test-table"); - BigtableDataClient client = BigtableDataClient.create(BigtableDataSettings - .newBuilderForEmulator(emulator.getHost(), emulator.getEmulatorPort()) - .setProjectId(PROJECT_ID) - .setInstanceId(INSTANCE_ID) - .build()); + BigtableDataClient client = BigtableDataClient.create( + BigtableDataSettings + .newBuilderForEmulator(emulator.getHost(), emulator.getEmulatorPort()) + .setProjectId(PROJECT_ID) + .setInstanceId(INSTANCE_ID) + .build() + ); - client.mutateRow(RowMutation.create("test-table", "1") - .setCell("name", "firstName", "Ray")); + client.mutateRow(RowMutation.create("test-table", "1").setCell("name", "firstName", "Ray")); Row row = client.readRow("test-table", "1"); List cells = row.getCells("name", "firstName"); @@ -77,15 +73,22 @@ public void testSimple() throws IOException, InterruptedException, ExecutionExce channel.shutdown(); } } + // } // createTable { - private void createTable(TransportChannelProvider channelProvider, CredentialsProvider credentialsProvider, String tableName) throws IOException { - EnhancedBigtableTableAdminStub stub = EnhancedBigtableTableAdminStub - .createEnhanced(BigtableTableAdminStubSettings.newBuilder() + private void createTable( + TransportChannelProvider channelProvider, + CredentialsProvider credentialsProvider, + String tableName + ) throws IOException { + EnhancedBigtableTableAdminStub stub = EnhancedBigtableTableAdminStub.createEnhanced( + BigtableTableAdminStubSettings + .newBuilder() .setTransportChannelProvider(channelProvider) .setCredentialsProvider(credentialsProvider) - .build()); + .build() + ); try (BigtableTableAdminClient client = BigtableTableAdminClient.create(PROJECT_ID, INSTANCE_ID, stub)) { Table table = client.createTable(CreateTableRequest.of(tableName).addFamily("name")); diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java index ba998e402ce..a7fe340791d 100644 --- a/modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java @@ -1,7 +1,5 @@ package org.testcontainers.containers; -import static org.assertj.core.api.Assertions.assertThat; - import com.google.cloud.NoCredentials; import com.google.cloud.ServiceOptions; import com.google.cloud.datastore.Datastore; @@ -12,6 +10,8 @@ import org.junit.Test; import org.testcontainers.utility.DockerImageName; +import static org.assertj.core.api.Assertions.assertThat; + public class DatastoreEmulatorContainerTest { @Rule @@ -19,17 +19,19 @@ public class DatastoreEmulatorContainerTest { public DatastoreEmulatorContainer emulator = new DatastoreEmulatorContainer( DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:367.0.0-emulators") ); + // } // startingDatastoreEmulatorContainer { @Test public void testSimple() { - DatastoreOptions options = DatastoreOptions.newBuilder() - .setHost(emulator.getEmulatorEndpoint()) - .setCredentials(NoCredentials.getInstance()) - .setRetrySettings(ServiceOptions.getNoRetrySettings()) - .setProjectId("test-project") - .build(); + DatastoreOptions options = DatastoreOptions + .newBuilder() + .setHost(emulator.getEmulatorEndpoint()) + .setCredentials(NoCredentials.getInstance()) + .setRetrySettings(ServiceOptions.getNoRetrySettings()) + .setProjectId("test-project") + .build(); Datastore datastore = options.getService(); Key key = datastore.newKeyFactory().setKind("Task").newKey("sample"); diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java index 04939ecc30e..c87f8e5176a 100644 --- a/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java @@ -1,7 +1,5 @@ package org.testcontainers.containers; -import static org.assertj.core.api.Assertions.assertThat; - import com.google.api.core.ApiFuture; import com.google.cloud.NoCredentials; import com.google.cloud.firestore.CollectionReference; @@ -10,13 +8,16 @@ import com.google.cloud.firestore.FirestoreOptions; import com.google.cloud.firestore.QuerySnapshot; import com.google.cloud.firestore.WriteResult; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ExecutionException; import org.junit.Rule; import org.junit.Test; import org.testcontainers.utility.DockerImageName; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; + public class FirestoreEmulatorContainerTest { @Rule @@ -24,16 +25,19 @@ public class FirestoreEmulatorContainerTest { public FirestoreEmulatorContainer emulator = new FirestoreEmulatorContainer( DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:367.0.0-emulators") ); + // } // testWithEmulatorContainer { @Test public void testSimple() throws ExecutionException, InterruptedException { - FirestoreOptions options = FirestoreOptions.getDefaultInstance().toBuilder() - .setHost(emulator.getEmulatorEndpoint()) - .setCredentials(NoCredentials.getInstance()) - .setProjectId("test-project") - .build(); + FirestoreOptions options = FirestoreOptions + .getDefaultInstance() + .toBuilder() + .setHost(emulator.getEmulatorEndpoint()) + .setCredentials(NoCredentials.getInstance()) + .setProjectId("test-project") + .build(); Firestore firestore = options.getService(); CollectionReference users = firestore.collection("users"); diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java index 61d56a279d7..aeb92bc4a69 100644 --- a/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java @@ -1,7 +1,5 @@ package org.testcontainers.containers; -import static org.assertj.core.api.Assertions.assertThat; - import com.google.api.gax.core.NoCredentialsProvider; import com.google.api.gax.grpc.GrpcTransportChannel; import com.google.api.gax.rpc.FixedTransportChannelProvider; @@ -24,11 +22,14 @@ import com.google.pubsub.v1.TopicName; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; -import java.io.IOException; import org.junit.Rule; import org.junit.Test; import org.testcontainers.utility.DockerImageName; +import java.io.IOException; + +import static org.assertj.core.api.Assertions.assertThat; + public class PubSubEmulatorContainerTest { public static final String PROJECT_ID = "my-project-id"; @@ -38,8 +39,8 @@ public class PubSubEmulatorContainerTest { public PubSubEmulatorContainer emulator = new PubSubEmulatorContainer( DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:367.0.0-emulators") ); - // } + // } @Test // testWithEmulatorContainer { @@ -47,8 +48,9 @@ public void testSimple() throws IOException { String hostport = emulator.getEmulatorEndpoint(); ManagedChannel channel = ManagedChannelBuilder.forTarget(hostport).usePlaintext().build(); try { - TransportChannelProvider channelProvider = - FixedTransportChannelProvider.create(GrpcTransportChannel.create(channel)); + TransportChannelProvider channelProvider = FixedTransportChannelProvider.create( + GrpcTransportChannel.create(channel) + ); NoCredentialsProvider credentialsProvider = NoCredentialsProvider.create(); String topicId = "my-topic-id"; @@ -57,56 +59,77 @@ public void testSimple() throws IOException { String subscriptionId = "my-subscription-id"; createSubscription(subscriptionId, topicId, channelProvider, credentialsProvider); - Publisher publisher = Publisher.newBuilder(TopicName.of(PROJECT_ID, topicId)) - .setChannelProvider(channelProvider) - .setCredentialsProvider(credentialsProvider) - .build(); + Publisher publisher = Publisher + .newBuilder(TopicName.of(PROJECT_ID, topicId)) + .setChannelProvider(channelProvider) + .setCredentialsProvider(credentialsProvider) + .build(); PubsubMessage message = PubsubMessage.newBuilder().setData(ByteString.copyFromUtf8("test message")).build(); publisher.publish(message); - SubscriberStubSettings subscriberStubSettings = - SubscriberStubSettings.newBuilder() - .setTransportChannelProvider(channelProvider) - .setCredentialsProvider(credentialsProvider) - .build(); + SubscriberStubSettings subscriberStubSettings = SubscriberStubSettings + .newBuilder() + .setTransportChannelProvider(channelProvider) + .setCredentialsProvider(credentialsProvider) + .build(); try (SubscriberStub subscriber = GrpcSubscriberStub.create(subscriberStubSettings)) { - PullRequest pullRequest = PullRequest.newBuilder() - .setMaxMessages(1) - .setSubscription(ProjectSubscriptionName.format(PROJECT_ID, subscriptionId)) - .build(); + PullRequest pullRequest = PullRequest + .newBuilder() + .setMaxMessages(1) + .setSubscription(ProjectSubscriptionName.format(PROJECT_ID, subscriptionId)) + .build(); PullResponse pullResponse = subscriber.pullCallable().call(pullRequest); assertThat(pullResponse.getReceivedMessagesList()).hasSize(1); - assertThat(pullResponse.getReceivedMessages(0).getMessage().getData().toStringUtf8()).isEqualTo("test message"); + assertThat(pullResponse.getReceivedMessages(0).getMessage().getData().toStringUtf8()) + .isEqualTo("test message"); } } finally { channel.shutdown(); } } + // } // createTopic { - private void createTopic(String topicId, TransportChannelProvider channelProvider, NoCredentialsProvider credentialsProvider) throws IOException { - TopicAdminSettings topicAdminSettings = TopicAdminSettings.newBuilder() - .setTransportChannelProvider(channelProvider) - .setCredentialsProvider(credentialsProvider) - .build(); + private void createTopic( + String topicId, + TransportChannelProvider channelProvider, + NoCredentialsProvider credentialsProvider + ) throws IOException { + TopicAdminSettings topicAdminSettings = TopicAdminSettings + .newBuilder() + .setTransportChannelProvider(channelProvider) + .setCredentialsProvider(credentialsProvider) + .build(); try (TopicAdminClient topicAdminClient = TopicAdminClient.create(topicAdminSettings)) { TopicName topicName = TopicName.of(PROJECT_ID, topicId); topicAdminClient.createTopic(topicName); } } + // } // createSubscription { - private void createSubscription(String subscriptionId, String topicId, TransportChannelProvider channelProvider, NoCredentialsProvider credentialsProvider) throws IOException { - SubscriptionAdminSettings subscriptionAdminSettings = SubscriptionAdminSettings.newBuilder() - .setTransportChannelProvider(channelProvider) - .setCredentialsProvider(credentialsProvider) - .build(); + private void createSubscription( + String subscriptionId, + String topicId, + TransportChannelProvider channelProvider, + NoCredentialsProvider credentialsProvider + ) throws IOException { + SubscriptionAdminSettings subscriptionAdminSettings = SubscriptionAdminSettings + .newBuilder() + .setTransportChannelProvider(channelProvider) + .setCredentialsProvider(credentialsProvider) + .build(); SubscriptionAdminClient subscriptionAdminClient = SubscriptionAdminClient.create(subscriptionAdminSettings); SubscriptionName subscriptionName = SubscriptionName.of(PROJECT_ID, subscriptionId); - subscriptionAdminClient.createSubscription(subscriptionName, TopicName.of(PROJECT_ID, topicId), PushConfig.getDefaultInstance(), 10); + subscriptionAdminClient.createSubscription( + subscriptionName, + TopicName.of(PROJECT_ID, topicId), + PushConfig.getDefaultInstance(), + 10 + ); } // } diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java index 64b81546499..b246e7e1f0d 100644 --- a/modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java @@ -1,7 +1,5 @@ package org.testcontainers.containers; -import static org.assertj.core.api.Assertions.assertThat; - import com.google.cloud.NoCredentials; import com.google.cloud.spanner.Database; import com.google.cloud.spanner.DatabaseAdminClient; @@ -16,12 +14,15 @@ import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.SpannerOptions; import com.google.cloud.spanner.Statement; -import java.util.Arrays; -import java.util.concurrent.ExecutionException; import org.junit.Rule; import org.junit.Test; import org.testcontainers.utility.DockerImageName; +import java.util.Arrays; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; + public class SpannerEmulatorContainerTest { @Rule @@ -29,20 +30,24 @@ public class SpannerEmulatorContainerTest { public SpannerEmulatorContainer emulator = new SpannerEmulatorContainer( DockerImageName.parse("gcr.io/cloud-spanner-emulator/emulator:1.4.0") ); + // } private static final String PROJECT_NAME = "test-project"; + private static final String INSTANCE_NAME = "test-instance"; + private static final String DATABASE_NAME = "test-database"; // testWithEmulatorContainer { @Test public void testSimple() throws ExecutionException, InterruptedException { - SpannerOptions options = SpannerOptions.newBuilder() - .setEmulatorHost(emulator.getEmulatorGrpcEndpoint()) - .setCredentials(NoCredentials.getInstance()) - .setProjectId(PROJECT_NAME) - .build(); + SpannerOptions options = SpannerOptions + .newBuilder() + .setEmulatorHost(emulator.getEmulatorGrpcEndpoint()) + .setCredentials(NoCredentials.getInstance()) + .setProjectId(PROJECT_NAME) + .build(); Spanner spanner = options.getService(); @@ -52,28 +57,38 @@ public void testSimple() throws ExecutionException, InterruptedException { DatabaseId databaseId = DatabaseId.of(instanceId, DATABASE_NAME); DatabaseClient dbClient = spanner.getDatabaseClient(databaseId); - dbClient.readWriteTransaction() - .run(tx -> { - String sql1 = "Delete from TestTable where 1=1"; - tx.executeUpdate(Statement.of(sql1)); - String sql = "INSERT INTO TestTable (Key, Value) VALUES (1, 'Java'), (2, 'Go')"; - tx.executeUpdate(Statement.of(sql)); - return null; - }); - - ResultSet resultSet = dbClient.readOnlyTransaction() - .executeQuery(Statement.of("select * from TestTable order by Key")); + dbClient + .readWriteTransaction() + .run(tx -> { + String sql1 = "Delete from TestTable where 1=1"; + tx.executeUpdate(Statement.of(sql1)); + String sql = "INSERT INTO TestTable (Key, Value) VALUES (1, 'Java'), (2, 'Go')"; + tx.executeUpdate(Statement.of(sql)); + return null; + }); + + ResultSet resultSet = dbClient + .readOnlyTransaction() + .executeQuery(Statement.of("select * from TestTable order by Key")); resultSet.next(); assertThat(resultSet.getLong(0)).isEqualTo(1); assertThat(resultSet.getString(1)).isEqualTo("Java"); } + // } // createDatabase { private void createDatabase(Spanner spanner) throws InterruptedException, ExecutionException { DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient(); - Database database = dbAdminClient.createDatabase(INSTANCE_NAME, DATABASE_NAME, Arrays.asList("CREATE TABLE TestTable (Key INT64, Value STRING(MAX)) PRIMARY KEY (Key)")).get(); + Database database = dbAdminClient + .createDatabase( + INSTANCE_NAME, + DATABASE_NAME, + Arrays.asList("CREATE TABLE TestTable (Key INT64, Value STRING(MAX)) PRIMARY KEY (Key)") + ) + .get(); } + // } // createInstance { @@ -81,7 +96,16 @@ private InstanceId createInstance(Spanner spanner) throws InterruptedException, InstanceConfigId instanceConfig = InstanceConfigId.of(PROJECT_NAME, "emulator-config"); InstanceId instanceId = InstanceId.of(PROJECT_NAME, INSTANCE_NAME); InstanceAdminClient insAdminClient = spanner.getInstanceAdminClient(); - Instance instance = insAdminClient.createInstance(InstanceInfo.newBuilder(instanceId).setNodeCount(1).setDisplayName("Test instance").setInstanceConfigId(instanceConfig).build()).get(); + Instance instance = insAdminClient + .createInstance( + InstanceInfo + .newBuilder(instanceId) + .setNodeCount(1) + .setDisplayName("Test instance") + .setInstanceConfigId(instanceConfig) + .build() + ) + .get(); return instanceId; } // } diff --git a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java index 5bb8935c39e..242cc7bdb43 100644 --- a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java +++ b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java @@ -34,27 +34,38 @@ public class HiveMQContainer extends GenericContainer { private static final Logger LOGGER = LoggerFactory.getLogger(HiveMQContainer.class); private static final DockerImageName DEFAULT_HIVEMQ_EE_IMAGE_NAME = DockerImageName.parse("hivemq/hivemq4"); + private static final DockerImageName DEFAULT_HIVEMQ_CE_IMAGE_NAME = DockerImageName.parse("hivemq/hivemq-ce"); private static final int DEBUGGING_PORT = 9000; + private static final int MQTT_PORT = 1883; + private static final int CONTROL_CENTER_PORT = 8080; + @SuppressWarnings("OctalInteger") private static final int MODE = 0777; - private static final @NotNull Pattern EXTENSION_ID_PATTERN = Pattern.compile("(.+?)"); - private final @NotNull ConcurrentHashMap containerOutputLatches = new ConcurrentHashMap<>(); + @NotNull + private static final Pattern EXTENSION_ID_PATTERN = Pattern.compile("(.+?)"); + + @NotNull + private final ConcurrentHashMap containerOutputLatches = new ConcurrentHashMap<>(); + private boolean controlCenterEnabled = false; + private boolean debugging = false; - private final @NotNull Set prepackagedExtensionsToRemove = new HashSet<>(); + @NotNull + private final Set prepackagedExtensionsToRemove = new HashSet<>(); + private boolean removeAllPrepackagedExtensions = false; - private final @NotNull WaitAllStrategy waitStrategy = new WaitAllStrategy(); + @NotNull + private final WaitAllStrategy waitStrategy = new WaitAllStrategy(); public HiveMQContainer(final @NotNull DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_HIVEMQ_CE_IMAGE_NAME, DEFAULT_HIVEMQ_EE_IMAGE_NAME); addExposedPort(MQTT_PORT); @@ -96,13 +107,14 @@ protected void configure() { withCreateContainerCmdModifier(it -> it.withEntrypoint("/bin/sh")); if (removeAllPrepackagedExtensions || !prepackagedExtensionsToRemove.isEmpty()) { - if (removeAllPrepackagedExtensions) { removeCommand = "rm -rf /opt/hivemq/extensions/** &&"; } else { - removeCommand = prepackagedExtensionsToRemove.stream() - .map(extensionId -> "rm -rf /opt/hivemq/extensions/" + extensionId + "&&") - .collect(Collectors.joining()); + removeCommand = + prepackagedExtensionsToRemove + .stream() + .map(extensionId -> "rm -rf /opt/hivemq/extensions/" + extensionId + "&&") + .collect(Collectors.joining()); } } else { removeCommand = ""; @@ -110,16 +122,20 @@ protected void configure() { setCommand( "-c", removeCommand + - "cp -r '/opt/hivemq/temp-extensions/'* /opt/hivemq/extensions/ " + - "; rm -rf /opt/hivemq/temp-extensions/** " + - "&& chmod -R 777 /opt/hivemq/extensions " + - "&& /opt/docker-entrypoint.sh /opt/hivemq/bin/run.sh" + "cp -r '/opt/hivemq/temp-extensions/'* /opt/hivemq/extensions/ " + + "; rm -rf /opt/hivemq/temp-extensions/** " + + "&& chmod -R 777 /opt/hivemq/extensions " + + "&& /opt/docker-entrypoint.sh /opt/hivemq/bin/run.sh" ); } protected void containerIsStarted(final @NotNull InspectContainerResponse containerInfo) { if (controlCenterEnabled) { - LOGGER.info("The HiveMQ Control Center is reachable under: http://{}:{}", getHost(), getMappedPort(CONTROL_CENTER_PORT)); + LOGGER.info( + "The HiveMQ Control Center is reachable under: http://{}:{}", + getHost(), + getMappedPort(CONTROL_CENTER_PORT) + ); } } @@ -159,7 +175,10 @@ protected void containerIsStarted(final @NotNull InspectContainerResponse contai public @NotNull HiveMQContainer withDebugging() { debugging = true; addExposedPorts(DEBUGGING_PORT); - withEnv("JAVA_OPTS", "-agentlib:jdwp=transport=dt_socket,address=0.0.0.0:" + DEBUGGING_PORT + ",server=y,suspend=y"); + withEnv( + "JAVA_OPTS", + "-agentlib:jdwp=transport=dt_socket,address=0.0.0.0:" + DEBUGGING_PORT + ",server=y,suspend=y" + ); return self(); } @@ -213,10 +232,16 @@ protected void containerIsStarted(final @NotNull InspectContainerResponse contai public @NotNull HiveMQContainer withExtension(final @NotNull MountableFile mountableExtension) { final File extensionDir = new File(mountableExtension.getResolvedPath()); if (!extensionDir.exists()) { - throw new ContainerLaunchException("Extension '" + mountableExtension.getFilesystemPath() + "' could not be mounted. It does not exist."); + throw new ContainerLaunchException( + "Extension '" + mountableExtension.getFilesystemPath() + "' could not be mounted. It does not exist." + ); } if (!extensionDir.isDirectory()) { - throw new ContainerLaunchException("Extension '" + mountableExtension.getFilesystemPath() + "' could not be mounted. It is not a directory."); + throw new ContainerLaunchException( + "Extension '" + + mountableExtension.getFilesystemPath() + + "' could not be mounted. It is not a directory." + ); } try { final String extensionDirName = getExtensionDirectoryName(extensionDir); @@ -277,10 +302,14 @@ protected void containerIsStarted(final @NotNull InspectContainerResponse contai public @NotNull HiveMQContainer withLicense(final @NotNull MountableFile mountableLicense) { final File licenseFile = new File(mountableLicense.getResolvedPath()); if (!licenseFile.exists()) { - throw new ContainerLaunchException("License file '" + mountableLicense.getFilesystemPath() + "' does not exist."); + throw new ContainerLaunchException( + "License file '" + mountableLicense.getFilesystemPath() + "' does not exist." + ); } if (!licenseFile.getName().endsWith(".lic") && !licenseFile.getName().endsWith(".elic")) { - throw new ContainerLaunchException("License file '" + mountableLicense.getFilesystemPath() + "' does not end wit '.lic' or '.elic'."); + throw new ContainerLaunchException( + "License file '" + mountableLicense.getFilesystemPath() + "' does not end wit '.lic' or '.elic'." + ); } final String containerPath = "/opt/hivemq/license/" + licenseFile.getName(); withCopyFileToContainer(cloneWithFileMode(mountableLicense), containerPath); @@ -299,7 +328,9 @@ protected void containerIsStarted(final @NotNull InspectContainerResponse contai public @NotNull HiveMQContainer withHiveMQConfig(final @NotNull MountableFile mountableConfig) { final File config = new File(mountableConfig.getResolvedPath()); if (!config.exists()) { - throw new ContainerLaunchException("HiveMQ config file '" + mountableConfig.getFilesystemPath() + "' does not exist."); + throw new ContainerLaunchException( + "HiveMQ config file '" + mountableConfig.getFilesystemPath() + "' does not exist." + ); } final String containerPath = "/opt/hivemq/conf/config.xml"; withCopyFileToContainer(cloneWithFileMode(mountableConfig), containerPath); @@ -320,8 +351,8 @@ protected void containerIsStarted(final @NotNull InspectContainerResponse contai */ public @NotNull HiveMQContainer withFileInExtensionHomeFolder( final @NotNull MountableFile file, - final @NotNull String extensionId) { - + final @NotNull String extensionId + ) { return withFileInExtensionHomeFolder(file, extensionId, ""); } @@ -340,9 +371,12 @@ protected void containerIsStarted(final @NotNull InspectContainerResponse contai public @NotNull HiveMQContainer withFileInExtensionHomeFolder( final @NotNull MountableFile file, final @NotNull String extensionId, - final @NotNull String pathInExtensionHome) { - - return withFileInHomeFolder(file, "/temp-extensions/" + extensionId + PathUtil.prepareAppendPath(pathInExtensionHome)); + final @NotNull String pathInExtensionHome + ) { + return withFileInHomeFolder( + file, + "/temp-extensions/" + extensionId + PathUtil.prepareAppendPath(pathInExtensionHome) + ); } /** @@ -356,8 +390,8 @@ protected void containerIsStarted(final @NotNull InspectContainerResponse contai */ public @NotNull HiveMQContainer withFileInHomeFolder( final @NotNull MountableFile mountableFile, - final @NotNull String pathInHomeFolder) { - + final @NotNull String pathInHomeFolder + ) { final File file = new File(mountableFile.getResolvedPath()); if (pathInHomeFolder.trim().isEmpty()) { @@ -388,11 +422,12 @@ protected void containerIsStarted(final @NotNull InspectContainerResponse contai public void disableExtension( final @NotNull String extensionName, final @NotNull String extensionDirectory, - final @NotNull Duration timeout) throws TimeoutException { - + final @NotNull Duration timeout + ) throws TimeoutException { final String regEX = "(.*)Extension \"" + extensionName + "\" version (.*) stopped successfully(.*)"; try { - final String containerPath = "/opt/hivemq/extensions" + PathUtil.prepareInnerPath(extensionDirectory) + "DISABLED"; + final String containerPath = + "/opt/hivemq/extensions" + PathUtil.prepareInnerPath(extensionDirectory) + "DISABLED"; final CountDownLatch latch = new CountDownLatch(1); containerOutputLatches.put(regEX, latch); @@ -402,9 +437,13 @@ public void disableExtension( final boolean await = latch.await(timeout.getSeconds(), TimeUnit.SECONDS); if (!await) { - throw new TimeoutException("Extension disabling timed out after '" + timeout.getSeconds() + "' seconds. " + + throw new TimeoutException( + "Extension disabling timed out after '" + + timeout.getSeconds() + + "' seconds. " + "Maybe you are using a HiveMQ Community Edition image, " + - "which does not support disabling of extensions"); + "which does not support disabling of extensions" + ); } } catch (final InterruptedException | IOException e) { throw new RuntimeException(e); @@ -424,9 +463,8 @@ public void disableExtension( * @param extensionDirectory the name of the extension's directory * @throws TimeoutException if the extension was not disabled within 60 seconds */ - public void disableExtension( - final @NotNull String extensionName, - final @NotNull String extensionDirectory) throws TimeoutException { + public void disableExtension(final @NotNull String extensionName, final @NotNull String extensionDirectory) + throws TimeoutException { disableExtension(extensionName, extensionDirectory, Duration.ofSeconds(60)); } @@ -441,9 +479,8 @@ public void disableExtension( * @param timeout the timeout * @throws TimeoutException if the extension was not disabled within the configured timeout */ - public void disableExtension( - final @NotNull HiveMQExtension hiveMQExtension, - final @NotNull Duration timeout) throws TimeoutException { + public void disableExtension(final @NotNull HiveMQExtension hiveMQExtension, final @NotNull Duration timeout) + throws TimeoutException { disableExtension(hiveMQExtension.getName(), hiveMQExtension.getId(), timeout); } @@ -476,11 +513,12 @@ public void disableExtension(final @NotNull HiveMQExtension hiveMQExtension) thr public void enableExtension( final @NotNull String extensionName, final @NotNull String extensionDirectory, - final @NotNull Duration timeout) throws TimeoutException { - + final @NotNull Duration timeout + ) throws TimeoutException { final String regEX = "(.*)Extension \"" + extensionName + "\" version (.*) started successfully(.*)"; try { - final String containerPath = "/opt/hivemq/extensions" + PathUtil.prepareInnerPath(extensionDirectory) + "DISABLED"; + final String containerPath = + "/opt/hivemq/extensions" + PathUtil.prepareInnerPath(extensionDirectory) + "DISABLED"; final CountDownLatch latch = new CountDownLatch(1); containerOutputLatches.put(regEX, latch); @@ -490,9 +528,13 @@ public void enableExtension( final boolean await = latch.await(timeout.getSeconds(), TimeUnit.SECONDS); if (!await) { - throw new TimeoutException("Extension enabling timed out after '" + timeout.getSeconds() + "' seconds. " + + throw new TimeoutException( + "Extension enabling timed out after '" + + timeout.getSeconds() + + "' seconds. " + "Maybe you are using a HiveMQ Community Edition image, " + - "which does not support disabling of extensions"); + "which does not support disabling of extensions" + ); } } catch (final InterruptedException | IOException e) { throw new RuntimeException(e); @@ -512,9 +554,8 @@ public void enableExtension( * @param extensionDirectory the name of the extension's directory * @throws TimeoutException if the extension was not enabled within 60 seconds */ - public void enableExtension( - final @NotNull String extensionName, - final @NotNull String extensionDirectory) throws TimeoutException { + public void enableExtension(final @NotNull String extensionName, final @NotNull String extensionDirectory) + throws TimeoutException { enableExtension(extensionName, extensionDirectory, Duration.ofSeconds(60)); } @@ -529,9 +570,8 @@ public void enableExtension( * @param timeout the timeout * @throws TimeoutException if the extension was not enabled within the configured timeout */ - public void enableExtension( - final @NotNull HiveMQExtension hiveMQExtension, - final @NotNull Duration timeout) throws TimeoutException { + public void enableExtension(final @NotNull HiveMQExtension hiveMQExtension, final @NotNull Duration timeout) + throws TimeoutException { enableExtension(hiveMQExtension.getName(), hiveMQExtension.getId(), timeout); } diff --git a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQExtension.java b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQExtension.java index 934dfd0c29c..91e984835d6 100644 --- a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQExtension.java +++ b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQExtension.java @@ -1,7 +1,5 @@ package org.testcontainers.hivemq; -import javassist.ClassPool; -import javassist.NotFoundException; import lombok.Getter; import org.apache.commons.io.FileUtils; import org.jboss.shrinkwrap.api.ExtensionLoader; @@ -23,28 +21,35 @@ import java.util.LinkedList; import java.util.List; import java.util.Set; +import javassist.ClassPool; +import javassist.NotFoundException; public class HiveMQExtension { private static final String VALID_EXTENSION_XML = "" + // - " %s" + // - " %s" + // - " %s" + // - " %s" + // - " %s" + // - ""; + " %s" + // + " %s" + // + " %s" + // + " %s" + // + " %s" + // + ""; + private static final String EXTENSION_MAIN_CLASS_NAME = "com.hivemq.extension.sdk.api.ExtensionMain"; + private static final Logger LOGGER = LoggerFactory.getLogger(HiveMQExtension.class); @Getter - private final @NotNull String id; + @NotNull + private final String id; @Getter - private final @NotNull String name; + @NotNull + private final String name; @Getter - private final @NotNull String version; + @NotNull + private final String version; @Getter private final int priority; @@ -56,9 +61,11 @@ public class HiveMQExtension { private final boolean disabledOnStartup; @Getter - private final @NotNull Class mainClass; + @NotNull + private final Class mainClass; - private final @NotNull List> additionalClasses; + @NotNull + private final List> additionalClasses; private HiveMQExtension( final @NotNull String id, @@ -68,8 +75,8 @@ private HiveMQExtension( final int startPriority, final boolean disabledOnStartup, final @NotNull Class mainClass, - final @NotNull List> additionalClasses) { - + final @NotNull List> additionalClasses + ) { this.id = id; this.name = name; this.version = version; @@ -80,27 +87,31 @@ private HiveMQExtension( this.additionalClasses = additionalClasses; } - @NotNull File createExtension(final @NotNull HiveMQExtension hiveMQExtension) - throws Exception { - + @NotNull + File createExtension(final @NotNull HiveMQExtension hiveMQExtension) throws Exception { final File tempDir = Files.createTempDirectory("").toFile(); final File extensionDir = new File(tempDir, hiveMQExtension.getId()); - FileUtils.writeStringToFile(new File(extensionDir, "hivemq-extension.xml"), + FileUtils.writeStringToFile( + new File(extensionDir, "hivemq-extension.xml"), String.format( VALID_EXTENSION_XML, hiveMQExtension.getId(), hiveMQExtension.getName(), hiveMQExtension.getVersion(), hiveMQExtension.getPriority(), - hiveMQExtension.getStartPriority()), - Charset.defaultCharset()); + hiveMQExtension.getStartPriority() + ), + Charset.defaultCharset() + ); if (hiveMQExtension.isDisabledOnStartup()) { final File disabled = new File(extensionDir, "DISABLED"); final boolean newFile = disabled.createNewFile(); if (!newFile) { - throw new ContainerLaunchException("Could not create DISABLED file '" + disabled.getAbsolutePath() + "' on host machine."); + throw new ContainerLaunchException( + "Could not create DISABLED file '" + disabled.getAbsolutePath() + "' on host machine." + ); } } @@ -129,11 +140,15 @@ private HiveMQExtension( private void putSubclassesIntoJar( final @NotNull String extensionId, final @Nullable Class clazz, - final @NotNull JavaArchive javaArchive) throws NotFoundException { - + final @NotNull JavaArchive javaArchive + ) throws NotFoundException { if (clazz != null) { - final Set subClassNames = - ClassPool.getDefault().get(clazz.getName()).getClassFile().getConstPool().getClassNames(); + final Set subClassNames = ClassPool + .getDefault() + .get(clazz.getName()) + .getClassFile() + .getConstPool() + .getClassNames(); for (final String subClassName : subClassNames) { final String className = subClassName.replaceAll("/", "."); @@ -156,14 +171,27 @@ private void putSubclassesIntoJar( } public static final class Builder { - private @Nullable String id; - private @Nullable String name; - private @Nullable String version; + + @Nullable + private String id; + + @Nullable + private String name; + + @Nullable + private String version; + private int priority = 0; + private int startPriority = 0; + private boolean disabledOnStartup = false; - private @Nullable Class mainClass; - private final @NotNull LinkedList> additionalClasses = new LinkedList<>(); + + @Nullable + private Class mainClass; + + @NotNull + private final LinkedList> additionalClasses = new LinkedList<>(); /** * Builds the {@link HiveMQExtension} with the provided values or default values. @@ -274,12 +302,16 @@ public static final class Builder { try { final Class extensionMain = Class.forName(EXTENSION_MAIN_CLASS_NAME); if (!extensionMain.isAssignableFrom(mainClass)) { - throw new IllegalArgumentException("The provided class does not implement '" + EXTENSION_MAIN_CLASS_NAME + "'"); + throw new IllegalArgumentException( + "The provided class does not implement '" + EXTENSION_MAIN_CLASS_NAME + "'" + ); } this.mainClass = mainClass; return this; } catch (final ClassNotFoundException e) { - throw new IllegalStateException("The class '" + EXTENSION_MAIN_CLASS_NAME + "' was not found in the classpath."); + throw new IllegalStateException( + "The class '" + EXTENSION_MAIN_CLASS_NAME + "' was not found in the classpath." + ); } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithControlCenterIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithControlCenterIT.java index f88d60b22ed..a3e8a20216d 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithControlCenterIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithControlCenterIT.java @@ -17,18 +17,18 @@ public class ContainerWithControlCenterIT { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) public void test() throws Exception { - - try (final HiveMQContainer hivemq = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) - .withControlCenter()) { - + try ( + final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withControlCenter() + ) { hivemq.start(); try (final CloseableHttpClient httpClient = HttpClientBuilder.create().build()) { - final HttpUriRequest request = new HttpGet("http://" + hivemq.getHost() + ":" + hivemq.getMappedPort(CONTROL_CENTER_PORT)); + final HttpUriRequest request = new HttpGet( + "http://" + hivemq.getHost() + ":" + hivemq.getMappedPort(CONTROL_CENTER_PORT) + ); httpClient.execute(request); } } - } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java index 57d3b75bc24..fe119854c80 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java @@ -18,13 +18,14 @@ public class ContainerWithCustomConfigIT { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test() throws Exception { - - try (final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) - .withHiveMQConfig(MountableFile.forClasspathResource("/config.xml"))) { - + try ( + final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withHiveMQConfig(MountableFile.forClasspathResource("/config.xml")) + ) { hivemq.start(); - final Mqtt5BlockingClient publisher = Mqtt5Client.builder() + final Mqtt5BlockingClient publisher = Mqtt5Client + .builder() .identifier("publisher") .serverPort(hivemq.getMqttPort()) .serverHost(hivemq.getHost()) @@ -32,13 +33,13 @@ void test() throws Exception { publisher.connect(); - assertThrows(MqttSessionExpiredException.class, () -> { - // this should fail since only QoS 0 is allowed by the configuration - publisher.publishWith() - .topic("test/topic") - .qos(MqttQos.EXACTLY_ONCE) - .send(); - }); + assertThrows( + MqttSessionExpiredException.class, + () -> { + // this should fail since only QoS 0 is allowed by the configuration + publisher.publishWith().topic("test/topic").qos(MqttQos.EXACTLY_ONCE).send(); + } + ); } } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionFromDirectoryIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionFromDirectoryIT.java index 4b89e418b05..c1e6e72cc32 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionFromDirectoryIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionFromDirectoryIT.java @@ -15,13 +15,14 @@ public class ContainerWithExtensionFromDirectoryIT { @Timeout(value = 3, unit = TimeUnit.MINUTES) void test() throws Exception { try ( - final HiveMQContainer hivemq = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) - .withExtension(MountableFile.forClasspathResource("/modifier-extension")) - .waitForExtension("Modifier Extension") - .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) - .withLogLevel(Level.DEBUG)) { - + final HiveMQContainer hivemq = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3") + ) + .withExtension(MountableFile.forClasspathResource("/modifier-extension")) + .waitForExtension("Modifier Extension") + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .withLogLevel(Level.DEBUG) + ) { hivemq.start(); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } @@ -30,12 +31,14 @@ void test() throws Exception { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_wrongDirectoryName() throws Exception { - try (final HiveMQContainer hivemq = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + try ( + final HiveMQContainer hivemq = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3") + ) .withExtension(MountableFile.forClasspathResource("/modifier-extension-wrong-name")) .waitForExtension("Modifier Extension") - .withLogLevel(Level.DEBUG)) { - + .withLogLevel(Level.DEBUG) + ) { hivemq.start(); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionIT.java index 8979d2c17b0..01a67e0dcfb 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionIT.java @@ -14,18 +14,22 @@ public class ContainerWithExtensionIT { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test() throws Exception { - final HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + final HiveMQExtension hiveMQExtension = HiveMQExtension + .builder() .id("extension-1") .name("my-extension") .version("1.0") - .mainClass(MyExtension.class).build(); - - try (final HiveMQContainer hivemq = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) - .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) - .waitForExtension(hiveMQExtension) - .withExtension(hiveMQExtension)) { - + .mainClass(MyExtension.class) + .build(); + + try ( + final HiveMQContainer hivemq = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3") + ) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .waitForExtension(hiveMQExtension) + .withExtension(hiveMQExtension) + ) { hivemq.start(); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); hivemq.stop(); diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionSubclassIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionSubclassIT.java index 53714123aff..1f3d9de4aeb 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionSubclassIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionSubclassIT.java @@ -15,19 +15,23 @@ public class ContainerWithExtensionSubclassIT { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test() throws Exception { - final HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + final HiveMQExtension hiveMQExtension = HiveMQExtension + .builder() .id("extension-1") .name("my-extension") .version("1.0") - .mainClass(MyExtensionWithSubclasses.class).build(); - - try (final HiveMQContainer hivemq = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) - .waitForExtension(hiveMQExtension) - .withExtension(hiveMQExtension) - .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) - .withLogLevel(Level.DEBUG)) { + .mainClass(MyExtensionWithSubclasses.class) + .build(); + try ( + final HiveMQContainer hivemq = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3") + ) + .waitForExtension(hiveMQExtension) + .withExtension(hiveMQExtension) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .withLogLevel(Level.DEBUG) + ) { hivemq.start(); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInExtensionHomeIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInExtensionHomeIT.java index 559737c8868..a5b5238ce25 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInExtensionHomeIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInExtensionHomeIT.java @@ -22,26 +22,30 @@ public class ContainerWithFileInExtensionHomeIT { - @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test() throws Exception { - final HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + final HiveMQExtension hiveMQExtension = HiveMQExtension + .builder() .id("extension-1") .name("my-extension") .version("1.0") - .mainClass(FileCheckerExtension.class).build(); - - try (final HiveMQContainer hivemq = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) - .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) - .withExtension(hiveMQExtension) - .waitForExtension(hiveMQExtension) - .withFileInExtensionHomeFolder( - MountableFile.forClasspathResource("/additionalFile.txt"), - "extension-1", - "/additionalFiles/my-file.txt")) { - + .mainClass(FileCheckerExtension.class) + .build(); + + try ( + final HiveMQContainer hivemq = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3") + ) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .withExtension(hiveMQExtension) + .waitForExtension(hiveMQExtension) + .withFileInExtensionHomeFolder( + MountableFile.forClasspathResource("/additionalFile.txt"), + "extension-1", + "/additionalFiles/my-file.txt" + ) + ) { hivemq.start(); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } @@ -50,28 +54,33 @@ void test() throws Exception { public static class FileCheckerExtension implements ExtensionMain { @Override - public void extensionStart(final @NotNull ExtensionStartInput extensionStartInput, final @NotNull ExtensionStartOutput extensionStartOutput) { - + public void extensionStart( + final @NotNull ExtensionStartInput extensionStartInput, + final @NotNull ExtensionStartOutput extensionStartOutput + ) { final PublishInboundInterceptor publishInboundInterceptor = (publishInboundInput, publishInboundOutput) -> { - final File extensionHomeFolder = extensionStartInput.getExtensionInformation().getExtensionHomeFolder(); final File additionalFile = new File(extensionHomeFolder, "additionalFiles/my-file.txt"); if (additionalFile.exists()) { - publishInboundOutput.getPublishPacket().setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); + publishInboundOutput + .getPublishPacket() + .setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); } }; - final ClientInitializer clientInitializer = (initializerInput, clientContext) -> clientContext.addPublishInboundInterceptor(publishInboundInterceptor); + final ClientInitializer clientInitializer = (initializerInput, clientContext) -> { + clientContext.addPublishInboundInterceptor(publishInboundInterceptor); + }; Services.initializerRegistry().setClientInitializer(clientInitializer); } @Override - public void extensionStop(final @NotNull ExtensionStopInput extensionStopInput, final @NotNull ExtensionStopOutput extensionStopOutput) { - - } + public void extensionStop( + final @NotNull ExtensionStopInput extensionStopInput, + final @NotNull ExtensionStopOutput extensionStopOutput + ) {} } - } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInHomeIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInHomeIT.java index efdf6ad298d..2fd387f394c 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInHomeIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInHomeIT.java @@ -25,21 +25,26 @@ public class ContainerWithFileInHomeIT { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test() throws Exception { - final HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + final HiveMQExtension hiveMQExtension = HiveMQExtension + .builder() .id("extension-1") .name("my-extension") .version("1.0") - .mainClass(FileCheckerExtension.class).build(); - - try (final HiveMQContainer hivemq = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) - .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) - .withExtension(hiveMQExtension) - .waitForExtension(hiveMQExtension) - .withFileInHomeFolder(MountableFile.forClasspathResource("/additionalFile.txt"), - "/additionalFiles/my-file.txt")) { - - + .mainClass(FileCheckerExtension.class) + .build(); + + try ( + final HiveMQContainer hivemq = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3") + ) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .withExtension(hiveMQExtension) + .waitForExtension(hiveMQExtension) + .withFileInHomeFolder( + MountableFile.forClasspathResource("/additionalFile.txt"), + "/additionalFiles/my-file.txt" + ) + ) { hivemq.start(); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } @@ -48,28 +53,33 @@ void test() throws Exception { public static class FileCheckerExtension implements ExtensionMain { @Override - public void extensionStart(@NotNull ExtensionStartInput extensionStartInput, @NotNull ExtensionStartOutput extensionStartOutput) { - + public void extensionStart( + @NotNull ExtensionStartInput extensionStartInput, + @NotNull ExtensionStartOutput extensionStartOutput + ) { final PublishInboundInterceptor publishInboundInterceptor = (publishInboundInput, publishInboundOutput) -> { - final File homeFolder = extensionStartInput.getServerInformation().getHomeFolder(); final File additionalFile = new File(homeFolder, "additionalFiles/my-file.txt"); if (additionalFile.exists()) { - publishInboundOutput.getPublishPacket().setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); + publishInboundOutput + .getPublishPacket() + .setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); } }; - final ClientInitializer clientInitializer = (initializerInput, clientContext) -> clientContext.addPublishInboundInterceptor(publishInboundInterceptor); + final ClientInitializer clientInitializer = (initializerInput, clientContext) -> { + clientContext.addPublishInboundInterceptor(publishInboundInterceptor); + }; Services.initializerRegistry().setClientInitializer(clientInitializer); } @Override - public void extensionStop(@NotNull ExtensionStopInput extensionStopInput, @NotNull ExtensionStopOutput extensionStopOutput) { - - } + public void extensionStop( + @NotNull ExtensionStopInput extensionStopInput, + @NotNull ExtensionStopOutput extensionStopOutput + ) {} } - } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithLicenseIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithLicenseIT.java index ba2dc472cdb..9d82188c35c 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithLicenseIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithLicenseIT.java @@ -25,21 +25,24 @@ public class ContainerWithLicenseIT { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test() throws Exception { - final HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + final HiveMQExtension hiveMQExtension = HiveMQExtension + .builder() .id("extension-1") .name("my-extension") .version("1.0") - .mainClass(LicenceCheckerExtension.class).build(); - - try (final HiveMQContainer hivemq = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) - .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) - .withExtension(hiveMQExtension) - .waitForExtension(hiveMQExtension) - .withLicense(MountableFile.forClasspathResource("/myLicense.lic")) - .withLicense(MountableFile.forClasspathResource("/myExtensionLicense.elic"))) { - - + .mainClass(LicenceCheckerExtension.class) + .build(); + + try ( + final HiveMQContainer hivemq = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3") + ) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .withExtension(hiveMQExtension) + .waitForExtension(hiveMQExtension) + .withLicense(MountableFile.forClasspathResource("/myLicense.lic")) + .withLicense(MountableFile.forClasspathResource("/myExtensionLicense.elic")) + ) { hivemq.start(); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } @@ -49,16 +52,19 @@ void test() throws Exception { public static class LicenceCheckerExtension implements ExtensionMain { @Override - public void extensionStart(@NotNull ExtensionStartInput extensionStartInput, @NotNull ExtensionStartOutput extensionStartOutput) { - + public void extensionStart( + @NotNull ExtensionStartInput extensionStartInput, + @NotNull ExtensionStartOutput extensionStartOutput + ) { final PublishInboundInterceptor publishInboundInterceptor = (publishInboundInput, publishInboundOutput) -> { - final File homeFolder = extensionStartInput.getServerInformation().getHomeFolder(); final File myLicence = new File(homeFolder, "license/myLicense.lic"); final File myExtensionLicence = new File(homeFolder, "license/myExtensionLicense.elic"); if (myLicence.exists() && myExtensionLicence.exists()) { - publishInboundOutput.getPublishPacket().setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); + publishInboundOutput + .getPublishPacket() + .setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); } }; @@ -70,9 +76,9 @@ public void extensionStart(@NotNull ExtensionStartInput extensionStartInput, @No } @Override - public void extensionStop(@NotNull ExtensionStopInput extensionStopInput, @NotNull ExtensionStopOutput extensionStopOutput) { - - } + public void extensionStop( + @NotNull ExtensionStopInput extensionStopInput, + @NotNull ExtensionStopOutput extensionStopOutput + ) {} } - } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java index eaec72cb038..75d308e3f90 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java @@ -29,25 +29,28 @@ public class ContainerWithoutPlatformExtensionsIT { - private final @NotNull HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + @NotNull + private final HiveMQExtension hiveMQExtension = HiveMQExtension + .builder() .name("MyExtension") .id("my-extension") .version("1.0.0") - .mainClass(CheckerExtension.class).build(); - + .mainClass(CheckerExtension.class) + .build(); @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) public void removeAllPlatformExtensions() throws InterruptedException { - - try (final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) - .withExtension(hiveMQExtension) - .waitForExtension(hiveMQExtension) - .withoutPrepackagedExtensions()) { - + try ( + final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withExtension(hiveMQExtension) + .waitForExtension(hiveMQExtension) + .withoutPrepackagedExtensions() + ) { hivemq.start(); - final Mqtt5BlockingClient client = MqttClient.builder() + final Mqtt5BlockingClient client = MqttClient + .builder() .serverPort(hivemq.getMqttPort()) .serverHost(hivemq.getHost()) .useMqttVersion5() @@ -73,15 +76,16 @@ public void removeAllPlatformExtensions() throws InterruptedException { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) public void removeKafkaExtension() throws InterruptedException { - - try (final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) - .withExtension(hiveMQExtension) - .waitForExtension(hiveMQExtension) - .withoutPrepackagedExtensions("hivemq-kafka-extension")) { - + try ( + final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withExtension(hiveMQExtension) + .waitForExtension(hiveMQExtension) + .withoutPrepackagedExtensions("hivemq-kafka-extension") + ) { hivemq.start(); - final Mqtt5BlockingClient client = MqttClient.builder() + final Mqtt5BlockingClient client = MqttClient + .builder() .serverPort(hivemq.getMqttPort()) .serverHost(hivemq.getHost()) .useMqttVersion5() @@ -107,31 +111,32 @@ public static class CheckerExtension implements ExtensionMain { @Override public void extensionStart( final @NotNull ExtensionStartInput extensionStartInput, - final @NotNull ExtensionStartOutput extensionStartOutput) { - - final String extensionFolders = Arrays.stream(extensionStartInput.getServerInformation().getExtensionsFolder().listFiles()) + final @NotNull ExtensionStartOutput extensionStartOutput + ) { + final String extensionFolders = Arrays + .stream(extensionStartInput.getServerInformation().getExtensionsFolder().listFiles()) .filter(File::isDirectory) .map(File::getName) .collect(Collectors.joining("\n")); final byte[] bytes = extensionFolders.getBytes(StandardCharsets.UTF_8); - Services.publishService().publish(Builders.publish() - .topic("extensions") - .retain(true) - .payload(ByteBuffer.wrap(bytes)) - .build()); - - Services.securityRegistry().setAuthenticatorProvider(authenticatorProviderInput -> - (SimpleAuthenticator) (simpleAuthInput, simpleAuthOutput) -> - simpleAuthOutput.authenticateSuccessfully()); - + Services + .publishService() + .publish(Builders.publish().topic("extensions").retain(true).payload(ByteBuffer.wrap(bytes)).build()); + + Services + .securityRegistry() + .setAuthenticatorProvider(authenticatorProviderInput -> { + return (SimpleAuthenticator) (simpleAuthInput, simpleAuthOutput) -> { + simpleAuthOutput.authenticateSuccessfully(); + }; + }); } @Override public void extensionStop( final @NotNull ExtensionStopInput extensionStopInput, - final @NotNull ExtensionStopOutput extensionStopOutput) { - - } + final @NotNull ExtensionStopOutput extensionStopOutput + ) {} } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java index 58b70c3d1d9..191ea41f303 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java @@ -37,19 +37,23 @@ public class CreateFileInCopiedDirectoryIT { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test() throws Exception { - final HiveMQExtension extension = HiveMQExtension.builder() + final HiveMQExtension extension = HiveMQExtension + .builder() .id("extension-1") .name("my-extension") .version("1.0") - .mainClass(FileCreatorExtension.class).build(); - - try (final HiveMQContainer hivemq = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) - .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) - .withExtension(extension) - .waitForExtension(extension) - .withFileInHomeFolder(createDirectory(), "directory")) { - + .mainClass(FileCreatorExtension.class) + .build(); + + try ( + final HiveMQContainer hivemq = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3") + ) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .withExtension(extension) + .waitForExtension(extension) + .withFileInHomeFolder(createDirectory(), "directory") + ) { hivemq.start(); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } @@ -58,10 +62,11 @@ void test() throws Exception { public static class FileCreatorExtension implements ExtensionMain { @Override - public void extensionStart(@NotNull ExtensionStartInput extensionStartInput, @NotNull ExtensionStartOutput extensionStartOutput) { - + public void extensionStart( + @NotNull ExtensionStartInput extensionStartInput, + @NotNull ExtensionStartOutput extensionStartOutput + ) { final PublishInboundInterceptor publishInboundInterceptor = (publishInboundInput, publishInboundOutput) -> { - final File homeFolder = extensionStartInput.getServerInformation().getHomeFolder(); final File dir = new File(homeFolder, "directory"); @@ -71,22 +76,26 @@ public void extensionStart(@NotNull ExtensionStartInput extensionStartInput, @No try { if (dirFile.createNewFile() && subDirFile.createNewFile()) { - publishInboundOutput.getPublishPacket().setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); + publishInboundOutput + .getPublishPacket() + .setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); } } catch (IOException e) { e.printStackTrace(); } }; - final ClientInitializer clientInitializer = (initializerInput, clientContext) -> clientContext.addPublishInboundInterceptor(publishInboundInterceptor); + final ClientInitializer clientInitializer = (initializerInput, clientContext) -> { + clientContext.addPublishInboundInterceptor(publishInboundInterceptor); + }; Services.initializerRegistry().setClientInitializer(clientInitializer); } @Override - public void extensionStop(@NotNull ExtensionStopInput extensionStopInput, @NotNull ExtensionStopOutput extensionStopOutput) { - - } + public void extensionStop( + @NotNull ExtensionStopInput extensionStopInput, + @NotNull ExtensionStopOutput extensionStopOutput + ) {} } - } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInExtensionDirectoryIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInExtensionDirectoryIT.java index c41137f58c3..44f5f4f632d 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInExtensionDirectoryIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInExtensionDirectoryIT.java @@ -26,18 +26,22 @@ public class CreateFileInExtensionDirectoryIT { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test() throws Exception { - final HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + final HiveMQExtension hiveMQExtension = HiveMQExtension + .builder() .id("extension-1") .name("my-extension") .version("1.0") - .mainClass(FileCreatorExtension.class).build(); - - try (final HiveMQContainer hivemq = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) - .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) - .waitForExtension(hiveMQExtension) - .withExtension(hiveMQExtension)) { - + .mainClass(FileCreatorExtension.class) + .build(); + + try ( + final HiveMQContainer hivemq = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3") + ) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .waitForExtension(hiveMQExtension) + .withExtension(hiveMQExtension) + ) { hivemq.start(); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } @@ -46,31 +50,36 @@ void test() throws Exception { public static class FileCreatorExtension implements ExtensionMain { @Override - public void extensionStart(@NotNull ExtensionStartInput extensionStartInput, @NotNull ExtensionStartOutput extensionStartOutput) { - + public void extensionStart( + @NotNull ExtensionStartInput extensionStartInput, + @NotNull ExtensionStartOutput extensionStartOutput + ) { final PublishInboundInterceptor publishInboundInterceptor = (publishInboundInput, publishInboundOutput) -> { - final File extensionHomeFolder = extensionStartInput.getExtensionInformation().getExtensionHomeFolder(); final File file = new File(extensionHomeFolder, "myfile.txt"); try { if (file.createNewFile()) { - publishInboundOutput.getPublishPacket().setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); + publishInboundOutput + .getPublishPacket() + .setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); } } catch (IOException e) { e.printStackTrace(); } }; - final ClientInitializer clientInitializer = (initializerInput, clientContext) -> clientContext.addPublishInboundInterceptor(publishInboundInterceptor); + final ClientInitializer clientInitializer = (initializerInput, clientContext) -> { + clientContext.addPublishInboundInterceptor(publishInboundInterceptor); + }; Services.initializerRegistry().setClientInitializer(clientInitializer); } @Override - public void extensionStop(@NotNull ExtensionStopInput extensionStopInput, @NotNull ExtensionStopOutput extensionStopOutput) { - - } + public void extensionStop( + @NotNull ExtensionStopInput extensionStopInput, + @NotNull ExtensionStopOutput extensionStopOutput + ) {} } - } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java index f6b1b9b5cd7..9588e3404e6 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java @@ -17,21 +17,22 @@ public class DisableEnableExtensionFromDirectoryIT { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test() throws Exception { - try (final HiveMQContainer hivemq = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) - .withExtension(MountableFile.forClasspathResource("/modifier-extension")) - .waitForExtension("Modifier Extension") - .withLogLevel(Level.DEBUG)) { - + try ( + final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withExtension(MountableFile.forClasspathResource("/modifier-extension")) + .waitForExtension("Modifier Extension") + .withLogLevel(Level.DEBUG) + ) { hivemq.start(); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); hivemq.disableExtension("Modifier Extension", "modifier-extension"); - assertThrows(ExecutionException.class, () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost())); + assertThrows( + ExecutionException.class, + () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()) + ); hivemq.enableExtension("Modifier Extension", "modifier-extension"); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } } - } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java index 5b3c6ab107f..e50664f240e 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java @@ -15,32 +15,39 @@ public class DisableEnableExtensionIT { - private final @NotNull HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + @NotNull + private final HiveMQExtension hiveMQExtension = HiveMQExtension + .builder() .id("extension-1") .name("my-extension") .version("1.0") .disabledOnStartup(true) - .mainClass(MyExtension.class).build(); + .mainClass(MyExtension.class) + .build(); @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test() throws Exception { - try (final HiveMQContainer hivemq = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) - .withExtension(hiveMQExtension) - .withLogLevel(Level.DEBUG)) { - - + try ( + final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withExtension(hiveMQExtension) + .withLogLevel(Level.DEBUG) + ) { hivemq.start(); - assertThrows(ExecutionException.class, () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost())); + assertThrows( + ExecutionException.class, + () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()) + ); hivemq.enableExtension(hiveMQExtension); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); hivemq.disableExtension(hiveMQExtension); - assertThrows(ExecutionException.class, () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost())); + assertThrows( + ExecutionException.class, + () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()) + ); hivemq.enableExtension(hiveMQExtension); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } } - } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQExtensionTest.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQExtensionTest.java index d614a59bd8c..08e1df797a1 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQExtensionTest.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQExtensionTest.java @@ -8,6 +8,9 @@ class HiveMQExtensionTest { @Test void builder_classDoesNotImplementExtensionMain_exception() { - assertThrows(IllegalArgumentException.class, () -> HiveMQExtension.builder().mainClass(HiveMQExtensionTest.class)); + assertThrows( + IllegalArgumentException.class, + () -> HiveMQExtension.builder().mainClass(HiveMQExtensionTest.class) + ); } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQTestContainerCore.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQTestContainerCore.java index 40820e33ded..d4dd2be9621 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQTestContainerCore.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQTestContainerCore.java @@ -15,7 +15,8 @@ class HiveMQTestContainerCore { - final @NotNull HiveMQContainer container = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")); + @NotNull + final HiveMQContainer container = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")); @TempDir File tempDir; @@ -69,14 +70,23 @@ void withFileInHomeFolder_pathEmpty_Exception() { @Test void withFileInExtensionHomeFolder_withPath_fileDoesNotExist_Exception() { final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); - assertThrows(ContainerLaunchException.class, () -> container.withFileInExtensionHomeFolder(mountableFile, "my-extension", "some/path")); + assertThrows( + ContainerLaunchException.class, + () -> container.withFileInExtensionHomeFolder(mountableFile, "my-extension", "some/path") + ); } @Test void withFileInExtensionHomeFolder_fileDoesNotExist_Exception() { final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); - assertThrows(ContainerLaunchException.class, () -> { - final HiveMQContainer hiveMQContainer = container.withFileInExtensionHomeFolder(mountableFile, "my-extension"); - }); + assertThrows( + ContainerLaunchException.class, + () -> { + final HiveMQContainer hiveMQContainer = container.withFileInExtensionHomeFolder( + mountableFile, + "my-extension" + ); + } + ); } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java index e9d9463653e..97a1365ac91 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java @@ -14,43 +14,54 @@ public class DemoDisableExtensionsIT { // noExtensions { @Container - final HiveMQContainer hivemqNoExtensions = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + final HiveMQContainer hivemqNoExtensions = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4") + ) .withoutPrepackagedExtensions(); + // } // noKafkaExtension { @Container - final HiveMQContainer hivemqNoKafkaExtension = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + final HiveMQContainer hivemqNoKafkaExtension = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4") + ) .withoutPrepackagedExtensions("hivemq-kafka-extension"); + // } // startDisabled { - private final HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + private final HiveMQExtension hiveMQExtension = HiveMQExtension + .builder() .id("extension-1") .name("my-extension") .version("1.0") .disabledOnStartup(true) - .mainClass(MyExtension.class).build(); + .mainClass(MyExtension.class) + .build(); @Container final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) .withExtension(hiveMQExtension); - // } + // } // startFromFilesystem { @Container - final HiveMQContainer hivemqExtensionFromFilesystem = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + final HiveMQContainer hivemqExtensionFromFilesystem = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4") + ) .withExtension(MountableFile.forHostPath("src/test/resources/modifier-extension")); - // } + // } - // runtimeEnable { + // hiveRuntimeEnable { @Test void test_disable_enable_extension() throws Exception { hivemq.enableExtension(hiveMQExtension); hivemq.disableExtension(hiveMQExtension); } + // } // runtimeEnableFilesystem { diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoExtensionTestsIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoExtensionTestsIT.java index 3a8ccc1bebe..e5385369d3a 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoExtensionTestsIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoExtensionTestsIT.java @@ -19,33 +19,39 @@ public class DemoExtensionTestsIT { // waitStrategy { @Container - final HiveMQContainer hivemqWithWaitStrategy = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) - .withExtension(MountableFile.forClasspathResource("/modifier-extension")) - .waitForExtension("Modifier Extension"); + final HiveMQContainer hivemqWithWaitStrategy = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4") + ) + .withExtension(MountableFile.forClasspathResource("/modifier-extension")) + .waitForExtension("Modifier Extension"); + // } // extensionClasspath { - final HiveMQExtension hiveMQEClasspathxtension = HiveMQExtension.builder() + final HiveMQExtension hiveMQEClasspathxtension = HiveMQExtension + .builder() .id("extension-1") .name("my-extension") .version("1.0") - .mainClass(MyExtensionWithSubclasses.class).build(); + .mainClass(MyExtensionWithSubclasses.class) + .build(); @Container - final HiveMQContainer hivemqWithClasspathExtension = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) - .waitForExtension(hiveMQEClasspathxtension) - .withExtension(hiveMQEClasspathxtension) - .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")); + final HiveMQContainer hivemqWithClasspathExtension = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3") + ) + .waitForExtension(hiveMQEClasspathxtension) + .withExtension(hiveMQEClasspathxtension) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")); + // } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) public void test() throws Exception { - // mqtt5client { - final Mqtt5BlockingClient client = Mqtt5Client.builder() + final Mqtt5BlockingClient client = Mqtt5Client + .builder() .serverPort(hivemqWithClasspathExtension.getMqttPort()) .serverHost(hivemqWithClasspathExtension.getHost()) .buildBlocking(); diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java index 7ec777d02b8..d0bf6fc6b9c 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java @@ -18,24 +18,36 @@ public class DemoFilesIT { // hivemqHome { - final HiveMQContainer hivemqFileInHome = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + final HiveMQContainer hivemqFileInHome = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3") + ) .withFileInHomeFolder( MountableFile.forHostPath("src/test/resources/additionalFile.txt"), - "/path/in/home/folder"); + "/path/in/home/folder" + ); + // } // extensionHome { @Container - final HiveMQContainer hivemqFileInExtensionHome = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) - .withExtension(HiveMQExtension.builder() - .id("extension-1") - .name("my-extension") - .version("1.0") - .mainClass(MyExtension.class).build()) + final HiveMQContainer hivemqFileInExtensionHome = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3") + ) + .withExtension( + HiveMQExtension + .builder() + .id("extension-1") + .name("my-extension") + .version("1.0") + .mainClass(MyExtension.class) + .build() + ) .withFileInExtensionHomeFolder( MountableFile.forHostPath("src/test/resources/additionalFile.txt"), "extension-1", - "/path/in/extension/home"); + "/path/in/extension/home" + ); + // } // withLicenses { @@ -43,14 +55,15 @@ public class DemoFilesIT { final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) .withLicense(MountableFile.forHostPath("src/test/resources/myLicense.lic")) .withLicense(MountableFile.forHostPath("src/test/resources/myExtensionLicense.elic")); + // } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) public void test() throws Exception { - // mqtt5client { - final Mqtt5BlockingClient client = Mqtt5Client.builder() + final Mqtt5BlockingClient client = Mqtt5Client + .builder() .serverPort(hivemq.getMqttPort()) .serverHost(hivemq.getHost()) .buildBlocking(); diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java index 51e3a43d9e5..be39fe6d7f2 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java @@ -18,38 +18,41 @@ public class DemoHiveMQContainerIT { // ceVersion { @Container - final HiveMQContainer hivemqCe = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) - .withLogLevel(Level.DEBUG); + final HiveMQContainer hivemqCe = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withLogLevel(Level.DEBUG); + // } - // eeVersion { + // hiveEEVersion { @Container - final HiveMQContainer hivemqEe = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) - .withLogLevel(Level.DEBUG); + final HiveMQContainer hivemqEe = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withLogLevel(Level.DEBUG); + // } // eeVersionWithControlCenter { @Container - final HiveMQContainer hivemqEeWithControLCenter = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) - .withLogLevel(Level.DEBUG) - .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) - .withControlCenter(); + final HiveMQContainer hivemqEeWithControlCenter = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3") + ) + .withLogLevel(Level.DEBUG) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .withControlCenter(); + // } // specificVersion { @Container final HiveMQContainer hivemqSpecificVersion = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce:2021.3")); + // } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) public void test() throws Exception { - // mqtt5client { - final Mqtt5BlockingClient client = Mqtt5Client.builder() + final Mqtt5BlockingClient client = Mqtt5Client + .builder() .serverPort(hivemqCe.getMqttPort()) .serverHost(hivemqCe.getHost()) .buildBlocking(); diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtension.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtension.java index e707859be77..4c3991122d7 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtension.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtension.java @@ -1,7 +1,6 @@ package org.testcontainers.hivemq.util; import com.hivemq.extension.sdk.api.ExtensionMain; -import org.jetbrains.annotations.NotNull; import com.hivemq.extension.sdk.api.interceptor.publish.PublishInboundInterceptor; import com.hivemq.extension.sdk.api.parameter.ExtensionStartInput; import com.hivemq.extension.sdk.api.parameter.ExtensionStartOutput; @@ -9,6 +8,7 @@ import com.hivemq.extension.sdk.api.parameter.ExtensionStopOutput; import com.hivemq.extension.sdk.api.services.Services; import com.hivemq.extension.sdk.api.services.intializer.ClientInitializer; +import org.jetbrains.annotations.NotNull; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; @@ -17,10 +17,14 @@ public class MyExtension implements ExtensionMain { @Override - public void extensionStart(final @NotNull ExtensionStartInput extensionStartInput, final @NotNull ExtensionStartOutput extensionStartOutput) { - + public void extensionStart( + final @NotNull ExtensionStartInput extensionStartInput, + final @NotNull ExtensionStartOutput extensionStartOutput + ) { final PublishInboundInterceptor publishInboundInterceptor = (publishInboundInput, publishInboundOutput) -> { - publishInboundOutput.getPublishPacket().setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); + publishInboundOutput + .getPublishPacket() + .setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); }; final ClientInitializer clientInitializer = (initializerInput, clientContext) -> { @@ -31,7 +35,8 @@ public void extensionStart(final @NotNull ExtensionStartInput extensionStartInpu } @Override - public void extensionStop(final @NotNull ExtensionStopInput extensionStopInput, final @NotNull ExtensionStopOutput extensionStopOutput) { - - } + public void extensionStop( + final @NotNull ExtensionStopInput extensionStopInput, + final @NotNull ExtensionStopOutput extensionStopOutput + ) {} } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtensionWithSubclasses.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtensionWithSubclasses.java index abc24585358..9e4565b15c9 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtensionWithSubclasses.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtensionWithSubclasses.java @@ -1,26 +1,31 @@ package org.testcontainers.hivemq.util; import com.hivemq.extension.sdk.api.ExtensionMain; -import org.jetbrains.annotations.NotNull; import com.hivemq.extension.sdk.api.parameter.ExtensionStartInput; import com.hivemq.extension.sdk.api.parameter.ExtensionStartOutput; import com.hivemq.extension.sdk.api.parameter.ExtensionStopInput; import com.hivemq.extension.sdk.api.parameter.ExtensionStopOutput; import com.hivemq.extension.sdk.api.services.Services; import com.hivemq.extension.sdk.api.services.intializer.ClientInitializer; +import org.jetbrains.annotations.NotNull; public class MyExtensionWithSubclasses implements ExtensionMain { @Override - public void extensionStart(final @NotNull ExtensionStartInput extensionStartInput, final @NotNull ExtensionStartOutput extensionStartOutput) { - - final ClientInitializer clientInitializer = (initializerInput, clientContext) -> clientContext.addPublishInboundInterceptor(new PublishModifier()); + public void extensionStart( + final @NotNull ExtensionStartInput extensionStartInput, + final @NotNull ExtensionStartOutput extensionStartOutput + ) { + final ClientInitializer clientInitializer = (initializerInput, clientContext) -> { + clientContext.addPublishInboundInterceptor(new PublishModifier()); + }; Services.initializerRegistry().setClientInitializer(clientInitializer); } @Override - public void extensionStop(final @NotNull ExtensionStopInput extensionStopInput, final @NotNull ExtensionStopOutput extensionStopOutput) { - - } + public void extensionStop( + final @NotNull ExtensionStopInput extensionStopInput, + final @NotNull ExtensionStopOutput extensionStopOutput + ) {} } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/PublishModifier.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/PublishModifier.java index d33f29a9c10..ec5b63ee1e6 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/PublishModifier.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/PublishModifier.java @@ -1,16 +1,22 @@ package org.testcontainers.hivemq.util; -import org.jetbrains.annotations.NotNull; import com.hivemq.extension.sdk.api.interceptor.publish.PublishInboundInterceptor; import com.hivemq.extension.sdk.api.interceptor.publish.parameter.PublishInboundInput; import com.hivemq.extension.sdk.api.interceptor.publish.parameter.PublishInboundOutput; +import org.jetbrains.annotations.NotNull; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; public class PublishModifier implements PublishInboundInterceptor { + @Override - public void onInboundPublish(final @NotNull PublishInboundInput publishInboundInput, final @NotNull PublishInboundOutput publishInboundOutput) { - publishInboundOutput.getPublishPacket().setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); + public void onInboundPublish( + final @NotNull PublishInboundInput publishInboundInput, + final @NotNull PublishInboundOutput publishInboundOutput + ) { + publishInboundOutput + .getPublishPacket() + .setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/TestPublishModifiedUtil.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/TestPublishModifiedUtil.java index f2f1cfd692f..963570c07c4 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/TestPublishModifiedUtil.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/TestPublishModifiedUtil.java @@ -14,27 +14,38 @@ public class TestPublishModifiedUtil { public static void testPublishModified(final int mqttPort, final @NotNull String host) throws Exception { final CompletableFuture publishReceived = new CompletableFuture<>(); - final Mqtt5BlockingClient publisher = Mqtt5Client.builder() + final Mqtt5BlockingClient publisher = Mqtt5Client + .builder() .serverPort(mqttPort) .serverHost(host) .identifier("publisher") .buildBlocking(); publisher.connect(); - final Mqtt5BlockingClient subscriber = Mqtt5Client.builder() + final Mqtt5BlockingClient subscriber = Mqtt5Client + .builder() .serverPort(mqttPort) .serverHost(host) .identifier("subscriber") .buildBlocking(); subscriber.connect(); subscriber.subscribeWith().topicFilter("test/topic").send(); - subscriber.toAsync().publishes(MqttGlobalPublishFilter.ALL, publish -> { - if (Arrays.equals(publish.getPayloadAsBytes(), "modified".getBytes(StandardCharsets.UTF_8))) { - publishReceived.complete(null); - } else { - publishReceived.completeExceptionally(new IllegalArgumentException("unexpected payload: " + new String(publish.getPayloadAsBytes()))); - } - }); + subscriber + .toAsync() + .publishes( + MqttGlobalPublishFilter.ALL, + publish -> { + if (Arrays.equals(publish.getPayloadAsBytes(), "modified".getBytes(StandardCharsets.UTF_8))) { + publishReceived.complete(null); + } else { + publishReceived.completeExceptionally( + new IllegalArgumentException( + "unexpected payload: " + new String(publish.getPayloadAsBytes()) + ) + ); + } + } + ); publisher.publishWith().topic("test/topic").payload("unmodified".getBytes(StandardCharsets.UTF_8)).send(); @@ -45,6 +56,4 @@ public static void testPublishModified(final int mqttPort, final @NotNull String subscriber.disconnect(); } } - - } diff --git a/modules/influxdb/src/main/java/org/testcontainers/containers/InfluxDBContainer.java b/modules/influxdb/src/main/java/org/testcontainers/containers/InfluxDBContainer.java index 7ebfbff1a86..40eea721b57 100644 --- a/modules/influxdb/src/main/java/org/testcontainers/containers/InfluxDBContainer.java +++ b/modules/influxdb/src/main/java/org/testcontainers/containers/InfluxDBContainer.java @@ -17,17 +17,22 @@ public class InfluxDBContainer> extends Gen public static final Integer INFLUXDB_PORT = 8086; private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("influxdb"); + private static final String DEFAULT_TAG = "1.4.3"; @Deprecated public static final String VERSION = DEFAULT_TAG; private boolean authEnabled = true; + private String admin = "admin"; + private String adminPassword = "password"; private String database; + private String username = "any"; + private String password = "any"; /** @@ -48,12 +53,12 @@ public InfluxDBContainer(final String version) { public InfluxDBContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); - waitStrategy = new WaitAllStrategy() - .withStrategy(Wait.forHttp("/ping").withBasicCredentials(username, password).forStatusCode(204)) - .withStrategy(Wait.forListeningPort()); + waitStrategy = + new WaitAllStrategy() + .withStrategy(Wait.forHttp("/ping").withBasicCredentials(username, password).forStatusCode(204)) + .withStrategy(Wait.forListeningPort()); addExposedPort(INFLUXDB_PORT); } @@ -144,7 +149,6 @@ public SELF withPassword(final String password) { return self(); } - /** * @return a url to influxDb */ diff --git a/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerTest.java b/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerTest.java index 7acf030b5e4..c7fc71a3a19 100644 --- a/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerTest.java +++ b/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerTest.java @@ -4,14 +4,16 @@ import org.junit.ClassRule; import org.junit.Test; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; import static org.junit.Assert.assertThat; public class InfluxDBContainerTest { @ClassRule - public static InfluxDBContainer influxDBContainer = new InfluxDBContainer<>(InfluxDBTestImages.INFLUXDB_TEST_IMAGE); + public static InfluxDBContainer influxDBContainer = new InfluxDBContainer<>( + InfluxDBTestImages.INFLUXDB_TEST_IMAGE + ); @Test public void getUrl() { diff --git a/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerWithUserTest.java b/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerWithUserTest.java index b045ca297c1..a7ae347e7cc 100644 --- a/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerWithUserTest.java +++ b/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerWithUserTest.java @@ -9,17 +9,20 @@ import java.util.concurrent.TimeUnit; -import static org.hamcrest.CoreMatchers.hasItem; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThat; public class InfluxDBContainerWithUserTest { private static final String TEST_VERSION = InfluxDBTestImages.INFLUXDB_TEST_IMAGE.getVersionPart(); + private static final String DATABASE = "test"; + private static final String USER = "test-user"; + private static final String PASSWORD = "test-password"; @Rule @@ -52,7 +55,8 @@ public void checkVersion() { public void queryForWriteAndRead() { InfluxDB influxDB = influxDBContainer.getNewInfluxDB(); - Point point = Point.measurement("cpu") + Point point = Point + .measurement("cpu") .time(System.currentTimeMillis(), TimeUnit.MILLISECONDS) .addField("idle", 90L) .addField("user", 9L) @@ -67,6 +71,5 @@ public void queryForWriteAndRead() { assertThat(actual.getError(), nullValue()); assertThat(actual.getResults(), notNullValue()); assertThat(actual.getResults().size(), is(1)); - } } diff --git a/modules/jdbc-test/src/main/java/org/testcontainers/db/AbstractContainerDatabaseTest.java b/modules/jdbc-test/src/main/java/org/testcontainers/db/AbstractContainerDatabaseTest.java index 6a5ca5094d6..6d3080c7ee3 100644 --- a/modules/jdbc-test/src/main/java/org/testcontainers/db/AbstractContainerDatabaseTest.java +++ b/modules/jdbc-test/src/main/java/org/testcontainers/db/AbstractContainerDatabaseTest.java @@ -4,11 +4,12 @@ import com.zaxxer.hikari.HikariDataSource; import org.testcontainers.containers.JdbcDatabaseContainer; -import javax.sql.DataSource; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import javax.sql.DataSource; + public abstract class AbstractContainerDatabaseTest { protected ResultSet performQuery(JdbcDatabaseContainer container, String sql) throws SQLException { diff --git a/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java b/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java index 4e5eb03bb49..5566db6244e 100644 --- a/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java +++ b/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java @@ -25,22 +25,19 @@ protected enum Options { CharacterSet, CustomIniFile, JDBCParams, - PmdKnownBroken + PmdKnownBroken, } @Parameter public String jdbcUrl; + @Parameter(1) public EnumSet options; public static void sampleInitFunction(Connection connection) throws SQLException { - connection.createStatement().execute("CREATE TABLE bar (\n" + - " foo VARCHAR(255)\n" + - ");"); + connection.createStatement().execute("CREATE TABLE bar (\n" + " foo VARCHAR(255)\n" + ");"); connection.createStatement().execute("INSERT INTO bar (foo) VALUES ('hello world');"); - connection.createStatement().execute("CREATE TABLE my_counter (\n" + - " n INT\n" + - ");"); + connection.createStatement().execute("CREATE TABLE my_counter (\n" + " n INT\n" + ");"); } @AfterClass @@ -79,67 +76,99 @@ private void performSimpleTest(HikariDataSource dataSource) throws SQLException query = "SELECT 1 FROM SYSIBM.SYSDUMMY1"; } - boolean result = new QueryRunner(dataSource, options.contains(Options.PmdKnownBroken)).query(query, rs -> { - rs.next(); - int resultSetInt = rs.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); - return true; - }); + boolean result = new QueryRunner(dataSource, options.contains(Options.PmdKnownBroken)) + .query( + query, + rs -> { + rs.next(); + int resultSetInt = rs.getInt(1); + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + return true; + } + ); assertTrue("The database returned a record as expected", result); } private void performTestForScriptedSchema(HikariDataSource dataSource) throws SQLException { - boolean result = new QueryRunner(dataSource).query("SELECT foo FROM bar WHERE foo LIKE '%world'", rs -> { - rs.next(); - String resultSetString = rs.getString(1); - assertEquals("A basic SELECT query succeeds where the schema has been applied from a script", "hello world", resultSetString); - return true; - }); + boolean result = new QueryRunner(dataSource) + .query( + "SELECT foo FROM bar WHERE foo LIKE '%world'", + rs -> { + rs.next(); + String resultSetString = rs.getString(1); + assertEquals( + "A basic SELECT query succeeds where the schema has been applied from a script", + "hello world", + resultSetString + ); + return true; + } + ); assertTrue("The database returned a record as expected", result); } private void performTestForJDBCParamUsage(HikariDataSource dataSource) throws SQLException { - boolean result = new QueryRunner(dataSource).query("select CURRENT_USER", rs -> { - rs.next(); - String resultUser = rs.getString(1); - // Not all databases (eg. Postgres) return @% at the end of user name. We just need to make sure the user name matches. - if (resultUser.endsWith("@%")) { - resultUser = resultUser.substring(0, resultUser.length() - 2); - } - assertEquals("User from query param is created.", "someuser", resultUser); - return true; - }); + boolean result = new QueryRunner(dataSource) + .query( + "select CURRENT_USER", + rs -> { + rs.next(); + String resultUser = rs.getString(1); + // Not all databases (eg. Postgres) return @% at the end of user name. We just need to make sure the user name matches. + if (resultUser.endsWith("@%")) { + resultUser = resultUser.substring(0, resultUser.length() - 2); + } + assertEquals("User from query param is created.", "someuser", resultUser); + return true; + } + ); assertTrue("The database returned a record as expected", result); String databaseQuery = "SELECT DATABASE()"; // Postgres does not have Database() as a function String databaseType = ConnectionUrl.newInstance(jdbcUrl).getDatabaseType(); - if (databaseType.equalsIgnoreCase("postgresql") || + if ( + databaseType.equalsIgnoreCase("postgresql") || databaseType.equalsIgnoreCase("postgis") || - databaseType.equalsIgnoreCase("timescaledb")) { + databaseType.equalsIgnoreCase("timescaledb") + ) { databaseQuery = "SELECT CURRENT_DATABASE()"; } - result = new QueryRunner(dataSource).query(databaseQuery, rs -> { - rs.next(); - String resultDB = rs.getString(1); - assertEquals("Database name from URL String is used.", "databasename", resultDB); - return true; - }); + result = + new QueryRunner(dataSource) + .query( + databaseQuery, + rs -> { + rs.next(); + String resultDB = rs.getString(1); + assertEquals("Database name from URL String is used.", "databasename", resultDB); + return true; + } + ); assertTrue("The database returned a record as expected", result); } - private void performTestForCharacterEncodingForInitialScriptConnection(HikariDataSource dataSource) throws SQLException { - boolean result = new QueryRunner(dataSource).query("SELECT foo FROM bar WHERE foo LIKE '%мир'", rs -> { - rs.next(); - String resultSetString = rs.getString(1); - assertEquals("A SELECT query succeed and the correct charset has been applied for the init script", "привет мир", resultSetString); - return true; - }); + private void performTestForCharacterEncodingForInitialScriptConnection(HikariDataSource dataSource) + throws SQLException { + boolean result = new QueryRunner(dataSource) + .query( + "SELECT foo FROM bar WHERE foo LIKE '%мир'", + rs -> { + rs.next(); + String resultSetString = rs.getString(1); + assertEquals( + "A SELECT query succeed and the correct charset has been applied for the init script", + "привет мир", + resultSetString + ); + return true; + } + ); assertTrue("The database returned a record as expected", result); } @@ -160,12 +189,19 @@ private void performSimpleTestWithCharacterSet(String jdbcUrl) throws SQLExcepti private HikariDataSource verifyCharacterSet(String jdbcUrl) throws SQLException { HikariDataSource dataSource = getDataSource(jdbcUrl, 1); - boolean result = new QueryRunner(dataSource).query("SHOW VARIABLES LIKE 'character\\_set\\_connection'", rs -> { - rs.next(); - String resultSetString = rs.getString(2); - assertTrue("Passing query parameters to set DB connection encoding is successful", resultSetString.startsWith("utf8")); - return true; - }); + boolean result = new QueryRunner(dataSource) + .query( + "SHOW VARIABLES LIKE 'character\\_set\\_connection'", + rs -> { + rs.next(); + String resultSetString = rs.getString(2); + assertTrue( + "Passing query parameters to set DB connection encoding is successful", + resultSetString.startsWith("utf8") + ); + return true; + } + ); assertTrue("The database returned a record as expected", result); return dataSource; diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index 5e5342f1ee1..84d242a29c7 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -20,23 +20,29 @@ import java.util.Map; import java.util.Properties; import java.util.concurrent.Future; - -import static java.util.stream.Collectors.joining; +import java.util.stream.Collectors; /** * Base class for containers that expose a JDBC connection * * @author richardnorth */ -public abstract class JdbcDatabaseContainer> extends GenericContainer implements LinkableContainer { +public abstract class JdbcDatabaseContainer> + extends GenericContainer + implements LinkableContainer { private static final Object DRIVER_LOAD_MUTEX = new Object(); + private Driver driver; + private String initScriptPath; + protected Map parameters = new HashMap<>(); + protected Map urlParameters = new HashMap<>(); private int startupTimeoutSeconds = 120; + private int connectTimeoutSeconds = 120; private static final String QUERY_PARAM_SEPARATOR = "&"; @@ -135,7 +141,12 @@ public SELF withInitScript(String initScriptPath) { @SneakyThrows(InterruptedException.class) @Override protected void waitUntilContainerStarted() { - logger().info("Waiting for database connection to become available at {} using query '{}'", getJdbcUrl(), getTestQueryString()); + logger() + .info( + "Waiting for database connection to become available at {} using query '{}'", + getJdbcUrl(), + getTestQueryString() + ); // Repeatedly try and open a connection to the DB and execute a test query long start = System.currentTimeMillis(); @@ -144,8 +155,7 @@ protected void waitUntilContainerStarted() { if (!isRunning()) { Thread.sleep(100L); } else { - try (Connection connection = createConnection(""); - Statement statement = connection.createStatement()) { + try (Connection connection = createConnection(""); Statement statement = connection.createStatement()) { boolean testQuerySucceeded = statement.execute(this.getTestQueryString()); if (testQuerySucceeded) { logger().info("Container is started (JDBC URL: {})", this.getJdbcUrl()); @@ -163,8 +173,10 @@ protected void waitUntilContainerStarted() { } throw new IllegalStateException( - String.format("Container is started, but cannot be accessed by (JDBC URL: %s), please check container logs", - this.getJdbcUrl()) + String.format( + "Container is started, but cannot be accessed by (JDBC URL: %s), please check container logs", + this.getJdbcUrl() + ) ); } @@ -179,7 +191,6 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { * @return a JDBC Driver */ public Driver getJdbcDriverInstance() throws NoDriverFoundException { - synchronized (DRIVER_LOAD_MUTEX) { if (driver == null) { try { @@ -215,7 +226,13 @@ public Connection createConnection(String queryString) throws SQLException, NoDr // give up if we hit the time limit or the container stops running for some reason while (System.currentTimeMillis() < start + (1000 * connectTimeoutSeconds) && isRunning()) { try { - logger().debug("Trying to create JDBC connection using {} to {} with properties: {}", driver.getClass().getName(), url, info); + logger() + .debug( + "Trying to create JDBC connection using {} to {} with properties: {}", + driver.getClass().getName(), + url, + info + ); return jdbcDriverInstance.connect(url, info); } catch (SQLException e) { @@ -261,15 +278,18 @@ protected String constructUrlParameters(String startCharacter, String delimiter) protected String constructUrlParameters(String startCharacter, String delimiter, String endCharacter) { String urlParameters = ""; if (!this.urlParameters.isEmpty()) { - String additionalParameters = this.urlParameters.entrySet().stream() - .map(Object::toString) - .collect(joining(delimiter)); + String additionalParameters = + this.urlParameters.entrySet().stream().map(Object::toString).collect(Collectors.joining(delimiter)); urlParameters = startCharacter + additionalParameters + endCharacter; } return urlParameters; } - protected void optionallyMapResourceParameterAsVolume(@NotNull String paramName, @NotNull String pathNameInContainer, @NotNull String defaultResource) { + protected void optionallyMapResourceParameterAsVolume( + @NotNull String paramName, + @NotNull String pathNameInContainer, + @NotNull String defaultResource + ) { String resourceName = parameters.getOrDefault(paramName, defaultResource); if (resourceName != null) { @@ -319,6 +339,7 @@ protected DatabaseDelegate getDatabaseDelegate() { } public static class NoDriverFoundException extends RuntimeException { + public NoDriverFoundException(String message, Throwable e) { super(message, e); } diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainerProvider.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainerProvider.java index f8694306384..3cd74dc98d4 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainerProvider.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainerProvider.java @@ -1,8 +1,7 @@ package org.testcontainers.containers; -import org.testcontainers.jdbc.ConnectionUrl; - import lombok.extern.slf4j.Slf4j; +import org.testcontainers.jdbc.ConnectionUrl; import java.util.Objects; @@ -26,9 +25,12 @@ public abstract class JdbcDatabaseContainerProvider { * @return Instance of {@link JdbcDatabaseContainer} */ public JdbcDatabaseContainer newInstance() { - log.warn("No explicit version tag was provided in JDBC URL and this class ({}) does not " + + log.warn( + "No explicit version tag was provided in JDBC URL and this class ({}) does not " + "override newInstance() to set a default tag. `latest` will be used but results may " + - "be unreliable!", this.getClass().getCanonicalName()); + "be unreliable!", + this.getClass().getCanonicalName() + ); return this.newInstance("latest"); } @@ -55,7 +57,11 @@ public JdbcDatabaseContainer newInstance(ConnectionUrl url) { return result; } - protected JdbcDatabaseContainer newInstanceFromConnectionUrl(ConnectionUrl connectionUrl, final String userParamName, final String pwdParamName) { + protected JdbcDatabaseContainer newInstanceFromConnectionUrl( + ConnectionUrl connectionUrl, + final String userParamName, + final String pwdParamName + ) { Objects.requireNonNull(connectionUrl, "Connection URL cannot be null"); final String databaseName = connectionUrl.getDatabaseName().orElse("test"); diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionDelegate.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionDelegate.java index e7f59b2df4f..47d3cadc823 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionDelegate.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionDelegate.java @@ -1,13 +1,13 @@ package org.testcontainers.jdbc; import lombok.RequiredArgsConstructor; -import lombok.Value; import lombok.experimental.Delegate; import java.sql.Connection; @RequiredArgsConstructor class ConnectionDelegate implements Connection { + @Delegate private final Connection delegate; } diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java index 3354ed45c39..b4aef740da0 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java @@ -1,5 +1,10 @@ package org.testcontainers.jdbc; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import org.testcontainers.UnstableAPI; + import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -10,13 +15,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import lombok.AllArgsConstructor; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import org.testcontainers.UnstableAPI; - -import static java.util.stream.Collectors.toMap; - /** * This is an Immutable class holding JDBC Connection Url and its parsed components, used by {@link ContainerDatabaseDriver}. *

@@ -60,6 +58,7 @@ public class ConnectionUrl { private Map containerParameters; private Map queryParameters; + private Map tmpfsOptions = new HashMap<>(); public static ConnectionUrl newInstance(final String url) { @@ -94,7 +93,9 @@ private void parseUrl() { //Try for Oracle pattern urlMatcher = Patterns.ORACLE_URL_MATCHING_PATTERN.matcher(this.getUrl()); if (!urlMatcher.matches()) { - throw new IllegalArgumentException("JDBC URL matches jdbc:tc: prefix but the database or tag name could not be identified"); + throw new IllegalArgumentException( + "JDBC URL matches jdbc:tc: prefix but the database or tag name could not be identified" + ); } } databaseType = urlMatcher.group("databaseType"); @@ -113,9 +114,10 @@ private void parseUrl() { databaseName = Optional.of(dbInstanceMatcher.group("databaseName")); } - queryParameters = Collections.unmodifiableMap( - parseQueryParameters( - Optional.ofNullable(urlMatcher.group("queryParameters")).orElse(""))); + queryParameters = + Collections.unmodifiableMap( + parseQueryParameters(Optional.ofNullable(urlMatcher.group("queryParameters")).orElse("")) + ); String query = queryParameters .entrySet() @@ -133,7 +135,6 @@ private void parseUrl() { tmpfsOptions = parseTmpfsOptions(containerParameters); - initScriptPath = Optional.ofNullable(containerParameters.get("TC_INITSCRIPT")); reusable = Boolean.parseBoolean(containerParameters.get("TC_REUSABLE")); @@ -145,7 +146,6 @@ private void parseUrl() { Matcher daemonMatcher = Patterns.DAEMON_MATCHING_PATTERN.matcher(this.getUrl()); inDaemonMode = daemonMatcher.matches() && Boolean.parseBoolean(daemonMatcher.group(2)); - } private Map parseTmpfsOptions(Map containerParameters) { @@ -155,10 +155,9 @@ private Map parseTmpfsOptions(Map containerParam String tmpfsOptions = containerParameters.get("TC_TMPFS"); - return Stream.of(tmpfsOptions.split(",")) - .collect(toMap( - string -> string.split(":")[0], - string -> string.split(":")[1])); + return Stream + .of(tmpfsOptions.split(",")) + .collect(Collectors.toMap(string -> string.split(":")[0], string -> string.split(":")[1])); } /** @@ -167,7 +166,6 @@ private Map parseTmpfsOptions(Map containerParam * @return {@link Map} */ private Map parseContainerParameters() { - Map results = new HashMap<>(); Matcher matcher = Patterns.TC_PARAM_MATCHING_PATTERN.matcher(this.getUrl()); @@ -186,14 +184,14 @@ private Map parseContainerParameters() { * @return {@link Map} */ private Map parseQueryParameters(final String queryString) { - Map results = new HashMap<>(); Matcher matcher = Patterns.QUERY_PARAM_MATCHING_PATTERN.matcher(queryString); while (matcher.find()) { String key = matcher.group(1); String value = matcher.group(2); - if (!key.matches(Patterns.TC_PARAM_NAME_PATTERN)) + if (!key.matches(Patterns.TC_PARAM_NAME_PATTERN)) { results.put(key, value); + } } return results; @@ -211,36 +209,36 @@ public Map getTmpfsOptions() { public interface Patterns { Pattern URL_MATCHING_PATTERN = Pattern.compile( "jdbc:tc:" + - "(?[a-z0-9]+)" + - "(:(?[^:]+))?" + - "://" + - "(?[^?]+)" + - "(?\\?.*)?" + "(?[a-z0-9]+)" + + "(:(?[^:]+))?" + + "://" + + "(?[^?]+)" + + "(?\\?.*)?" ); Pattern ORACLE_URL_MATCHING_PATTERN = Pattern.compile( "jdbc:tc:" + - "(?[a-z]+)" + - "(:(?(?!thin).+))?:thin:(//)?" + - "(" + - "(?[^:" + - "?^/]+)/(?[^?^/]+)" + - ")?" + - "@" + - "(?[^?]+)" + - "(?\\?.*)?" + "(?[a-z]+)" + + "(:(?(?!thin).+))?:thin:(//)?" + + "(" + + "(?[^:" + + "?^/]+)/(?[^?^/]+)" + + ")?" + + "@" + + "(?[^?]+)" + + "(?\\?.*)?" ); //Matches to part of string - hostname:port/databasename Pattern DB_INSTANCE_MATCHING_PATTERN = Pattern.compile( "(?[^:]+)" + - "(:(?[0-9]+))?" + - "(" + - "(?[:/])" + - "|" + - ";databaseName=" + - ")" + - "(?[^\\\\?]+)" + "(:(?[0-9]+))?" + + "(" + + "(?[:/])" + + "|" + + ";databaseName=" + + ")" + + "(?[^\\\\?]+)" ); Pattern DAEMON_MATCHING_PATTERN = Pattern.compile(".*([?&]?)TC_DAEMON=([^?&]+).*"); @@ -251,24 +249,27 @@ public interface Patterns { @Deprecated Pattern INITSCRIPT_MATCHING_PATTERN = Pattern.compile(".*([?&]?)TC_INITSCRIPT=([^?&]+).*"); - Pattern INITFUNCTION_MATCHING_PATTERN = Pattern.compile(".*([?&]?)TC_INITFUNCTION=" + + Pattern INITFUNCTION_MATCHING_PATTERN = Pattern.compile( + ".*([?&]?)TC_INITFUNCTION=" + "((\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*\\.)*\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)" + "::" + "(\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)" + - ".*"); + ".*" + ); String TC_PARAM_NAME_PATTERN = "(TC_[A-Z_]+)"; Pattern TC_PARAM_MATCHING_PATTERN = Pattern.compile(TC_PARAM_NAME_PATTERN + "=([^?&]+)"); Pattern QUERY_PARAM_MATCHING_PATTERN = Pattern.compile("([^?&=]+)=([^?&]*)"); - } @Getter @AllArgsConstructor public class InitFunctionDef { + private String className; + private String methodName; } } diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionWrapper.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionWrapper.java index 3e1f67c5b86..4f245590f78 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionWrapper.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionWrapper.java @@ -17,7 +17,7 @@ public void close() throws SQLException { super.close(); try { closeCallback.run(); - } catch(Exception e) { + } catch (Exception e) { e.printStackTrace(); } } diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java index 4b3a7ba98f7..23ed9d78b25 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java @@ -7,7 +7,6 @@ import org.testcontainers.delegate.DatabaseDelegate; import org.testcontainers.ext.ScriptUtils; -import javax.script.ScriptException; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -17,6 +16,8 @@ import java.util.*; import java.util.logging.Logger; +import javax.script.ScriptException; + /** * Test Containers JDBC proxy driver. This driver will handle JDBC URLs of the form: *

@@ -38,9 +39,13 @@ public class ContainerDatabaseDriver implements Driver { private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(ContainerDatabaseDriver.class); private Driver delegate; + private static final Map> containerConnections = new HashMap<>(); + private static final Map jdbcUrlContainerCache = new HashMap<>(); + private static final Set initializedContainers = new HashSet<>(); + private static final String FILE_PATH_PREFIX = "file:"; static { @@ -62,7 +67,6 @@ public boolean acceptsURL(String url) throws SQLException { @Override public synchronized Connection connect(String url, final Properties info) throws SQLException { - /* The driver should return "null" if it realizes it is the wrong kind of driver to connect to the given URL. */ @@ -73,7 +77,6 @@ public synchronized Connection connect(String url, final Properties info) throws ConnectionUrl connectionUrl = ConnectionUrl.newInstance(url); synchronized (jdbcUrlContainerCache) { - String queryString = connectionUrl.getQueryString().orElse(""); /* If we already have a running container for this exact connection string, we want to connect @@ -81,7 +84,6 @@ public synchronized Connection connect(String url, final Properties info) throws */ JdbcDatabaseContainer container = jdbcUrlContainerCache.get(connectionUrl.getUrl()); if (container == null) { - LOGGER.debug("Container not found in cache, creating new instance"); Map parameters = connectionUrl.getContainerParameters(); @@ -89,7 +91,9 @@ public synchronized Connection connect(String url, final Properties info) throws /* Find a matching container type using ServiceLoader. */ - ServiceLoader databaseContainers = ServiceLoader.load(JdbcDatabaseContainerProvider.class); + ServiceLoader databaseContainers = ServiceLoader.load( + JdbcDatabaseContainerProvider.class + ); for (JdbcDatabaseContainerProvider candidateContainerType : databaseContainers) { if (candidateContainerType.supports(connectionUrl.getDatabaseType())) { container = candidateContainerType.newInstance(connectionUrl); @@ -98,7 +102,9 @@ public synchronized Connection connect(String url, final Properties info) throws } } if (container == null) { - throw new UnsupportedOperationException("Database name " + connectionUrl.getDatabaseType() + " not supported"); + throw new UnsupportedOperationException( + "Database name " + connectionUrl.getDatabaseType() + " not supported" + ); } /* @@ -138,7 +144,6 @@ public synchronized Connection connect(String url, final Properties info) throws } } - /** * Wrap the connection, setting up a callback to be called when the connection is closed. *

@@ -149,25 +154,34 @@ public synchronized Connection connect(String url, final Properties info) throws * @param connectionUrl {@link ConnectionUrl} instance representing JDBC Url for this connection * @return the connection, wrapped */ - private Connection wrapConnection(final Connection connection, final JdbcDatabaseContainer container, final ConnectionUrl connectionUrl) { - + private Connection wrapConnection( + final Connection connection, + final JdbcDatabaseContainer container, + final ConnectionUrl connectionUrl + ) { final boolean isDaemon = connectionUrl.isInDaemonMode() || connectionUrl.isReusable(); - Set connections = containerConnections.computeIfAbsent(container.getContainerId(), k -> new HashSet<>()); + Set connections = containerConnections.computeIfAbsent( + container.getContainerId(), + k -> new HashSet<>() + ); connections.add(connection); final Set finalConnections = connections; - return new ConnectionWrapper(connection, () -> { - finalConnections.remove(connection); - if (!isDaemon && finalConnections.isEmpty()) { - synchronized (jdbcUrlContainerCache) { - container.stop(); - jdbcUrlContainerCache.remove(connectionUrl.getUrl()); + return new ConnectionWrapper( + connection, + () -> { + finalConnections.remove(connection); + if (!isDaemon && finalConnections.isEmpty()) { + synchronized (jdbcUrlContainerCache) { + container.stop(); + jdbcUrlContainerCache.remove(connectionUrl.getUrl()); + } } } - }); + ); } /** @@ -177,7 +191,8 @@ private Connection wrapConnection(final Connection connection, final JdbcDatabas * @param databaseDelegate database delegate to apply init scripts to the database * @throws SQLException on script or DB error */ - private void runInitScriptIfRequired(final ConnectionUrl connectionUrl, DatabaseDelegate databaseDelegate) throws SQLException { + private void runInitScriptIfRequired(final ConnectionUrl connectionUrl, DatabaseDelegate databaseDelegate) + throws SQLException { if (connectionUrl.getInitScriptPath().isPresent()) { String initScriptPath = connectionUrl.getInitScriptPath().get(); try { @@ -191,7 +206,9 @@ private void runInitScriptIfRequired(final ConnectionUrl connectionUrl, Database } if (resource == null) { LOGGER.warn("Could not load classpath init script: {}", initScriptPath); - throw new SQLException("Could not load classpath init script: " + initScriptPath + ". Resource not found."); + throw new SQLException( + "Could not load classpath init script: " + initScriptPath + ". Resource not found." + ); } String sql = IOUtils.toString(resource, StandardCharsets.UTF_8); @@ -213,7 +230,8 @@ private void runInitScriptIfRequired(final ConnectionUrl connectionUrl, Database * @param connection JDBC connection to apply init functions to. * @throws SQLException on script or DB error */ - private void runInitFunctionIfRequired(final ConnectionUrl connectionUrl, Connection connection) throws SQLException { + private void runInitFunctionIfRequired(final ConnectionUrl connectionUrl, Connection connection) + throws SQLException { if (connectionUrl.getInitFunction().isPresent()) { String className = connectionUrl.getInitFunction().get().getClassName(); String methodName = connectionUrl.getInitFunction().get().getMethodName(); @@ -223,7 +241,9 @@ private void runInitFunctionIfRequired(final ConnectionUrl connectionUrl, Connec Method method = initFunctionClazz.getMethod(methodName, Connection.class); method.invoke(null, connection); - } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + } catch ( + ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e + ) { LOGGER.error("Error while executing init function: {}::{}", className, methodName, e); throw new SQLException("Error while executing init function: " + className + "::" + methodName, e); } @@ -270,7 +290,6 @@ public static void killContainers() { containerConnections.clear(); initializedContainers.clear(); } - } /** diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/JdbcDatabaseDelegate.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/JdbcDatabaseDelegate.java index 6173a8f6a37..7ed2f7ffde1 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/JdbcDatabaseDelegate.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/JdbcDatabaseDelegate.java @@ -18,6 +18,7 @@ public class JdbcDatabaseDelegate extends AbstractDatabaseDelegate { private JdbcDatabaseContainer container; + private String queryString; public JdbcDatabaseDelegate(JdbcDatabaseContainer container, String queryString) { @@ -35,16 +36,27 @@ protected Statement createNewConnection() { } } - @Override - public void execute(String statement, String scriptPath, int lineNumber, boolean continueOnError, boolean ignoreFailedDrops) { + public void execute( + String statement, + String scriptPath, + int lineNumber, + boolean continueOnError, + boolean ignoreFailedDrops + ) { try { boolean rowsAffected = getConnection().execute(statement); log.debug("{} returned as updateCount for SQL: {}", rowsAffected, statement); } catch (SQLException ex) { boolean dropStatement = statement.trim().toLowerCase().startsWith("drop"); if (continueOnError || (dropStatement && ignoreFailedDrops)) { - log.debug("Failed to execute SQL script statement at line {} of resource {}: {}", lineNumber, scriptPath, statement, ex); + log.debug( + "Failed to execute SQL script statement at line {} of resource {}: {}", + lineNumber, + scriptPath, + statement, + ex + ); } else { throw new ScriptUtils.ScriptStatementFailedException(statement, lineNumber, scriptPath, ex); } diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ext/ScriptUtils.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ext/ScriptUtils.java index 76b7a044c9d..c6a3c0fa3ed 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ext/ScriptUtils.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ext/ScriptUtils.java @@ -18,10 +18,11 @@ import org.testcontainers.jdbc.ContainerLessJdbcDelegate; -import javax.script.ScriptException; import java.sql.Connection; import java.util.List; +import javax.script.ScriptException; + /** * Wrapper for database-agnostic ScriptUtils * @@ -68,9 +69,24 @@ private ScriptUtils() { * @see org.testcontainers.ext.ScriptUtils * @deprecated Needed only to keep binary compatibility for this internal API. Consider using database-agnostic ScriptUtils */ - public static void splitSqlScript(String resource, String script, String separator, String commentPrefix, - String blockCommentStartDelimiter, String blockCommentEndDelimiter, List statements) { - org.testcontainers.ext.ScriptUtils.splitSqlScript(resource, script, separator, commentPrefix, blockCommentStartDelimiter, blockCommentEndDelimiter, statements); + public static void splitSqlScript( + String resource, + String script, + String separator, + String commentPrefix, + String blockCommentStartDelimiter, + String blockCommentEndDelimiter, + List statements + ) { + org.testcontainers.ext.ScriptUtils.splitSqlScript( + resource, + script, + separator, + commentPrefix, + blockCommentStartDelimiter, + blockCommentEndDelimiter, + statements + ); } /** @@ -85,18 +101,40 @@ public static boolean containsSqlScriptDelimiters(String script, String delim) { * @see org.testcontainers.ext.ScriptUtils * @deprecated Needed only to keep binary compatibility for this internal API. Consider using database-agnostic ScriptUtils */ - public static void executeSqlScript(Connection connection, String scriptPath, String script) throws ScriptException { - org.testcontainers.ext.ScriptUtils.executeDatabaseScript(new ContainerLessJdbcDelegate(connection), scriptPath, script); + public static void executeSqlScript(Connection connection, String scriptPath, String script) + throws ScriptException { + org.testcontainers.ext.ScriptUtils.executeDatabaseScript( + new ContainerLessJdbcDelegate(connection), + scriptPath, + script + ); } /** * @see org.testcontainers.ext.ScriptUtils * @deprecated Needed only to keep binary compatibility for this internal API. Consider using database-agnostic ScriptUtils */ - public static void executeSqlScript(Connection connection, String scriptPath, String script, boolean continueOnError, - boolean ignoreFailedDrops, String commentPrefix, String separator, String blockCommentStartDelimiter, - String blockCommentEndDelimiter) throws ScriptException { - org.testcontainers.ext.ScriptUtils.executeDatabaseScript(new ContainerLessJdbcDelegate(connection), scriptPath, - script, continueOnError, ignoreFailedDrops, commentPrefix, separator, blockCommentStartDelimiter, blockCommentEndDelimiter); + public static void executeSqlScript( + Connection connection, + String scriptPath, + String script, + boolean continueOnError, + boolean ignoreFailedDrops, + String commentPrefix, + String separator, + String blockCommentStartDelimiter, + String blockCommentEndDelimiter + ) throws ScriptException { + org.testcontainers.ext.ScriptUtils.executeDatabaseScript( + new ContainerLessJdbcDelegate(connection), + scriptPath, + script, + continueOnError, + ignoreFailedDrops, + commentPrefix, + separator, + blockCommentStartDelimiter, + blockCommentEndDelimiter + ); } -} \ No newline at end of file +} diff --git a/modules/jdbc/src/test/java/org/testcontainers/containers/JdbcDatabaseContainerTest.java b/modules/jdbc/src/test/java/org/testcontainers/containers/JdbcDatabaseContainerTest.java index 9f0cf514558..1dc6646bd17 100644 --- a/modules/jdbc/src/test/java/org/testcontainers/containers/JdbcDatabaseContainerTest.java +++ b/modules/jdbc/src/test/java/org/testcontainers/containers/JdbcDatabaseContainerTest.java @@ -1,11 +1,12 @@ package org.testcontainers.containers; -import java.sql.Connection; -import java.sql.SQLException; import lombok.NonNull; import org.junit.Test; import org.slf4j.Logger; +import java.sql.Connection; +import java.sql.SQLException; + import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.mockito.Mockito.mock; diff --git a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java index 12ca5c28c3d..9b88e6b503d 100644 --- a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java +++ b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java @@ -5,9 +5,9 @@ import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameter; +import java.util.Arrays; import java.util.Optional; -import static java.util.Arrays.asList; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; /** @@ -20,35 +20,106 @@ public class ConnectionUrlDriversTests { @Parameter public String jdbcUrl; + @Parameter(1) public String databaseType; + @Parameter(2) public Optional tag; + @Parameter(3) public String dbHostString; + @Parameter(4) public String databaseName; @Parameterized.Parameters(name = "{index} - {0}") public static Iterable data() { - return asList( - new Object[][]{ - {"jdbc:tc:mysql:5.7.34://hostname/test", "mysql", Optional.of("5.7.34"), "hostname/test", "test"}, - {"jdbc:tc:mysql://hostname/test", "mysql", Optional.empty(), "hostname/test", "test"}, - {"jdbc:tc:postgresql:1.2.3://hostname/test", "postgresql", Optional.of("1.2.3"), "hostname/test", "test"}, - {"jdbc:tc:postgresql://hostname/test", "postgresql", Optional.empty(), "hostname/test", "test"}, - {"jdbc:tc:sqlserver:1.2.3://localhost;instance=SQLEXPRESS:1433;databaseName=test", "sqlserver", Optional.of("1.2.3"), "localhost;instance=SQLEXPRESS:1433;databaseName=test", "test"}, - {"jdbc:tc:sqlserver://localhost;instance=SQLEXPRESS:1433;databaseName=test", "sqlserver", Optional.empty(), "localhost;instance=SQLEXPRESS:1433;databaseName=test", "test"}, - {"jdbc:tc:mariadb:1.2.3://localhost:3306/test", "mariadb", Optional.of("1.2.3"), "localhost:3306/test", "test"}, - {"jdbc:tc:mariadb://localhost:3306/test", "mariadb", Optional.empty(), "localhost:3306/test", "test"}, - {"jdbc:tc:oracle:1.2.3:thin://@localhost:1521/test", "oracle", Optional.of("1.2.3"), "localhost:1521/test", "test"}, - {"jdbc:tc:oracle:1.2.3:thin:@localhost:1521/test", "oracle", Optional.of("1.2.3"), "localhost:1521/test", "test"}, - {"jdbc:tc:oracle:thin:@localhost:1521/test", "oracle", Optional.empty(), "localhost:1521/test", "test"}, - {"jdbc:tc:oracle:1.2.3:thin:@localhost:1521:test", "oracle", Optional.of("1.2.3"), "localhost:1521:test", "test"}, - {"jdbc:tc:oracle:1.2.3:thin://@localhost:1521:test", "oracle", Optional.of("1.2.3"), "localhost:1521:test", "test"}, - {"jdbc:tc:oracle:1.2.3-anything:thin://@localhost:1521:test", "oracle", Optional.of("1.2.3-anything"), "localhost:1521:test", "test"}, - {"jdbc:tc:oracle:thin:@localhost:1521:test", "oracle", Optional.empty(), "localhost:1521:test", "test"} - }); + return Arrays.asList( + new Object[][] { + { "jdbc:tc:mysql:5.7.34://hostname/test", "mysql", Optional.of("5.7.34"), "hostname/test", "test" }, + { "jdbc:tc:mysql://hostname/test", "mysql", Optional.empty(), "hostname/test", "test" }, + { + "jdbc:tc:postgresql:1.2.3://hostname/test", + "postgresql", + Optional.of("1.2.3"), + "hostname/test", + "test", + }, + { "jdbc:tc:postgresql://hostname/test", "postgresql", Optional.empty(), "hostname/test", "test" }, + { + "jdbc:tc:sqlserver:1.2.3://localhost;instance=SQLEXPRESS:1433;databaseName=test", + "sqlserver", + Optional.of("1.2.3"), + "localhost;instance=SQLEXPRESS:1433;databaseName=test", + "test", + }, + { + "jdbc:tc:sqlserver://localhost;instance=SQLEXPRESS:1433;databaseName=test", + "sqlserver", + Optional.empty(), + "localhost;instance=SQLEXPRESS:1433;databaseName=test", + "test", + }, + { + "jdbc:tc:mariadb:1.2.3://localhost:3306/test", + "mariadb", + Optional.of("1.2.3"), + "localhost:3306/test", + "test", + }, + { "jdbc:tc:mariadb://localhost:3306/test", "mariadb", Optional.empty(), "localhost:3306/test", "test" }, + { + "jdbc:tc:oracle:1.2.3:thin://@localhost:1521/test", + "oracle", + Optional.of("1.2.3"), + "localhost:1521/test", + "test", + }, + { + "jdbc:tc:oracle:1.2.3:thin:@localhost:1521/test", + "oracle", + Optional.of("1.2.3"), + "localhost:1521/test", + "test", + }, + { + "jdbc:tc:oracle:thin:@localhost:1521/test", + "oracle", + Optional.empty(), + "localhost:1521/test", + "test", + }, + { + "jdbc:tc:oracle:1.2.3:thin:@localhost:1521:test", + "oracle", + Optional.of("1.2.3"), + "localhost:1521:test", + "test", + }, + { + "jdbc:tc:oracle:1.2.3:thin://@localhost:1521:test", + "oracle", + Optional.of("1.2.3"), + "localhost:1521:test", + "test", + }, + { + "jdbc:tc:oracle:1.2.3-anything:thin://@localhost:1521:test", + "oracle", + Optional.of("1.2.3-anything"), + "localhost:1521:test", + "test", + }, + { + "jdbc:tc:oracle:thin:@localhost:1521:test", + "oracle", + Optional.empty(), + "localhost:1521:test", + "test", + }, + } + ); } @Test diff --git a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java index 4fefd5d1e13..6888f881ec4 100644 --- a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java +++ b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java @@ -13,7 +13,6 @@ public class ConnectionUrlTest { @Rule public ExpectedException thrown = ExpectedException.none(); - @Test public void testConnectionUrl1() { String urlString = "jdbc:tc:mysql:5.7.34://somehostname:3306/databasename?a=b&c=d"; @@ -31,7 +30,6 @@ public void testConnectionUrl1() { assertEquals("Parameter c is captured", "d", url.getQueryParameters().get("c")); } - @Test public void testConnectionUrl2() { String urlString = "jdbc:tc:mysql://somehostname/databasename"; @@ -46,7 +44,6 @@ public void testConnectionUrl2() { assertEquals("Database Name value is as expected", "databasename", url.getDatabaseName().get()); assertTrue("Connection Parameters set is empty", url.getQueryParameters().isEmpty()); - } @Test @@ -62,40 +59,55 @@ public void testTmpfsOption() { assertTrue("Connection Parameters set is empty", url.getQueryParameters().isEmpty()); assertFalse("Container Parameters set is not empty", url.getContainerParameters().isEmpty()); - assertEquals("Container Parameter TC_TMPFS is true", "key:value,key1:value1", url.getContainerParameters().get("TC_TMPFS")); + assertEquals( + "Container Parameter TC_TMPFS is true", + "key:value,key1:value1", + url.getContainerParameters().get("TC_TMPFS") + ); assertTrue("tmpfs option key exists", url.getTmpfsOptions().containsKey("key")); - assertEquals("tmpfs option key has correct value", "value" , url.getTmpfsOptions().get("key")); + assertEquals("tmpfs option key has correct value", "value", url.getTmpfsOptions().get("key")); assertTrue("tmpfs option key1 exists", url.getTmpfsOptions().containsKey("key1")); - assertEquals("tmpfs option key1 has correct value", "value1" , url.getTmpfsOptions().get("key1")); + assertEquals("tmpfs option key1 has correct value", "value1", url.getTmpfsOptions().get("key1")); } - @Test public void testInitScriptPathCapture() { - String urlString = "jdbc:tc:mysql:5.7.34://somehostname:3306/databasename?a=b&c=d&TC_INITSCRIPT=somepath/init_mysql.sql"; + String urlString = + "jdbc:tc:mysql:5.7.34://somehostname:3306/databasename?a=b&c=d&TC_INITSCRIPT=somepath/init_mysql.sql"; ConnectionUrl url = ConnectionUrl.newInstance(urlString); assertEquals("Database Type value is as expected", "somepath/init_mysql.sql", url.getInitScriptPath().get()); assertEquals("Query String value is as expected", "?a=b&c=d", url.getQueryString().get()); - assertEquals("INIT SCRIPT Path exists in Container Parameters", "somepath/init_mysql.sql", url.getContainerParameters().get("TC_INITSCRIPT")); + assertEquals( + "INIT SCRIPT Path exists in Container Parameters", + "somepath/init_mysql.sql", + url.getContainerParameters().get("TC_INITSCRIPT") + ); //Parameter sets are unmodifiable thrown.expect(UnsupportedOperationException.class); url.getContainerParameters().remove("TC_INITSCRIPT"); url.getQueryParameters().remove("a"); - } @Test public void testInitFunctionCapture() { - String urlString = "jdbc:tc:mysql:5.7.34://somehostname:3306/databasename?a=b&c=d&TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction"; + String urlString = + "jdbc:tc:mysql:5.7.34://somehostname:3306/databasename?a=b&c=d&TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction"; ConnectionUrl url = ConnectionUrl.newInstance(urlString); assertTrue("Init Function parameter exists", url.getInitFunction().isPresent()); - assertEquals("Init function class is as expected", "org.testcontainers.jdbc.JDBCDriverTest", url.getInitFunction().get().getClassName()); - assertEquals("Init function class is as expected", "sampleInitFunction", url.getInitFunction().get().getMethodName()); - + assertEquals( + "Init function class is as expected", + "org.testcontainers.jdbc.JDBCDriverTest", + url.getInitFunction().get().getClassName() + ); + assertEquals( + "Init function class is as expected", + "sampleInitFunction", + url.getInitFunction().get().getMethodName() + ); } @Test @@ -104,6 +116,5 @@ public void testDaemonCapture() { ConnectionUrl url = ConnectionUrl.newInstance(urlString); assertTrue("Daemon flag is set to true.", url.isInDaemonMode()); - } } diff --git a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ContainerDatabaseDriverTest.java b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ContainerDatabaseDriverTest.java index d33e399db27..5b4d223cd36 100644 --- a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ContainerDatabaseDriverTest.java +++ b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ContainerDatabaseDriverTest.java @@ -13,23 +13,22 @@ public class ContainerDatabaseDriverTest { - private static final String PLAIN_POSTGRESQL_JDBC_URL = "jdbc:postgresql://localhost:5432/test"; - - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @Test - public void shouldNotTryToConnectToNonMatchingJdbcUrlDirectly() throws SQLException { - ContainerDatabaseDriver driver = new ContainerDatabaseDriver(); - Connection connection = driver.connect(PLAIN_POSTGRESQL_JDBC_URL, new Properties()); - Assert.assertNull(connection); - } - - @Test - public void shouldNotTryToConnectToNonMatchingJdbcUrlViaDriverManager() throws SQLException { - thrown.expect(SQLException.class); - thrown.expectMessage(CoreMatchers.startsWith("No suitable driver found for ")); - DriverManager.getConnection(PLAIN_POSTGRESQL_JDBC_URL); - } - + private static final String PLAIN_POSTGRESQL_JDBC_URL = "jdbc:postgresql://localhost:5432/test"; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void shouldNotTryToConnectToNonMatchingJdbcUrlDirectly() throws SQLException { + ContainerDatabaseDriver driver = new ContainerDatabaseDriver(); + Connection connection = driver.connect(PLAIN_POSTGRESQL_JDBC_URL, new Properties()); + Assert.assertNull(connection); + } + + @Test + public void shouldNotTryToConnectToNonMatchingJdbcUrlViaDriverManager() throws SQLException { + thrown.expect(SQLException.class); + thrown.expectMessage(CoreMatchers.startsWith("No suitable driver found for ")); + DriverManager.getConnection(PLAIN_POSTGRESQL_JDBC_URL); + } } diff --git a/modules/jdbc/src/test/java/org/testcontainers/jdbc/MissingJdbcDriverTest.java b/modules/jdbc/src/test/java/org/testcontainers/jdbc/MissingJdbcDriverTest.java index 46138d91347..68afe8692e8 100644 --- a/modules/jdbc/src/test/java/org/testcontainers/jdbc/MissingJdbcDriverTest.java +++ b/modules/jdbc/src/test/java/org/testcontainers/jdbc/MissingJdbcDriverTest.java @@ -17,7 +17,6 @@ public class MissingJdbcDriverTest { @Test public void shouldFailFastIfNoDriverFound() { - final MissingDriverContainer container = new MissingDriverContainer(); try { @@ -37,6 +36,7 @@ public void shouldFailFastIfNoDriverFound() { * Container class for the purposes of testing, with a known non-existent driver */ static class MissingDriverContainer extends JdbcDatabaseContainer { + private final AtomicInteger connectionAttempts = new AtomicInteger(); MissingDriverContainer() { diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGenerator.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGenerator.java index ef8560b3f19..a8612fc1516 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGenerator.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGenerator.java @@ -1,22 +1,22 @@ package org.testcontainers.junit.jupiter; import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.platform.commons.util.StringUtils; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; - -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.platform.commons.util.StringUtils.isBlank; +import java.nio.charset.StandardCharsets; class FilesystemFriendlyNameGenerator { + private static final String UNKNOWN_NAME = "unknown"; static String filesystemFriendlyNameOf(ExtensionContext context) { String contextId = context.getUniqueId(); try { - return (isBlank(contextId)) + return (StringUtils.isBlank(contextId)) ? UNKNOWN_NAME - : URLEncoder.encode(contextId, UTF_8.toString()); + : URLEncoder.encode(contextId, StandardCharsets.UTF_8.toString()); } catch (UnsupportedEncodingException e) { return UNKNOWN_NAME; } diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Testcontainers.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Testcontainers.java index db8273563a7..730339f0fd5 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Testcontainers.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Testcontainers.java @@ -59,7 +59,6 @@ @ExtendWith(TestcontainersExtension.class) @Inherited public @interface Testcontainers { - /** * Whether tests should be disabled (rather than failing) when Docker is not available. */ diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java index 990940be14d..47ed56d9ac5 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java @@ -29,33 +29,38 @@ import java.util.Optional; import java.util.Set; import java.util.function.Predicate; +import java.util.stream.Collectors; import java.util.stream.Stream; -import static java.util.stream.Collectors.toList; -import static org.testcontainers.junit.jupiter.FilesystemFriendlyNameGenerator.filesystemFriendlyNameOf; - -class TestcontainersExtension implements BeforeEachCallback, BeforeAllCallback, AfterEachCallback, AfterAllCallback, ExecutionCondition { +class TestcontainersExtension + implements BeforeEachCallback, BeforeAllCallback, AfterEachCallback, AfterAllCallback, ExecutionCondition { private static final Namespace NAMESPACE = Namespace.create(TestcontainersExtension.class); private static final String SHARED_LIFECYCLE_AWARE_CONTAINERS = "sharedLifecycleAwareContainers"; + private static final String LOCAL_LIFECYCLE_AWARE_CONTAINERS = "localLifecycleAwareContainers"; @Override public void beforeAll(ExtensionContext context) { - Class testClass = context.getTestClass() - .orElseThrow(() -> new ExtensionConfigurationException("TestcontainersExtension is only supported for classes.")); + Class testClass = context + .getTestClass() + .orElseThrow(() -> { + return new ExtensionConfigurationException("TestcontainersExtension is only supported for classes."); + }); Store store = context.getStore(NAMESPACE); List sharedContainersStoreAdapters = findSharedContainers(testClass); - sharedContainersStoreAdapters.forEach(adapter -> store.getOrComputeIfAbsent(adapter.getKey(), k -> adapter.start())); + sharedContainersStoreAdapters.forEach(adapter -> { + store.getOrComputeIfAbsent(adapter.getKey(), k -> adapter.start()); + }); List lifecycleAwareContainers = sharedContainersStoreAdapters .stream() .filter(this::isTestLifecycleAware) .map(lifecycleAwareAdapter -> (TestLifecycleAware) lifecycleAwareAdapter.container) - .collect(toList()); + .collect(Collectors.toList()); store.put(SHARED_LIFECYCLE_AWARE_CONTAINERS, lifecycleAwareContainers); signalBeforeTestToContainers(lifecycleAwareContainers, testDescriptionFrom(context)); @@ -70,12 +75,13 @@ public void afterAll(ExtensionContext context) { public void beforeEach(final ExtensionContext context) { Store store = context.getStore(NAMESPACE); - List lifecycleAwareContainers = collectParentTestInstances(context).parallelStream() + List lifecycleAwareContainers = collectParentTestInstances(context) + .parallelStream() .flatMap(this::findRestartContainers) .peek(adapter -> store.getOrComputeIfAbsent(adapter.getKey(), k -> adapter.start())) .filter(this::isTestLifecycleAware) .map(lifecycleAwareAdapter -> (TestLifecycleAware) lifecycleAwareAdapter.container) - .collect(toList()); + .collect(Collectors.toList()); store.put(LOCAL_LIFECYCLE_AWARE_CONTAINERS, lifecycleAwareContainers); signalBeforeTestToContainers(lifecycleAwareContainers, testDescriptionFrom(context)); @@ -86,13 +92,17 @@ public void afterEach(ExtensionContext context) { signalAfterTestToContainersFor(LOCAL_LIFECYCLE_AWARE_CONTAINERS, context); } - private void signalBeforeTestToContainers(List lifecycleAwareContainers, TestDescription testDescription) { + private void signalBeforeTestToContainers( + List lifecycleAwareContainers, + TestDescription testDescription + ) { lifecycleAwareContainers.forEach(container -> container.beforeTest(testDescription)); } private void signalAfterTestToContainersFor(String storeKey, ExtensionContext context) { - List lifecycleAwareContainers = - (List) context.getStore(NAMESPACE).get(storeKey); + List lifecycleAwareContainers = (List) context + .getStore(NAMESPACE) + .get(storeKey); if (lifecycleAwareContainers != null) { TestDescription description = testDescriptionFrom(context); Optional throwable = context.getExecutionException(); @@ -103,7 +113,7 @@ private void signalAfterTestToContainersFor(String storeKey, ExtensionContext co private TestDescription testDescriptionFrom(ExtensionContext context) { return new TestcontainersTestDescription( context.getUniqueId(), - filesystemFriendlyNameOf(context) + FilesystemFriendlyNameGenerator.filesystemFriendlyNameOf(context) ); } @@ -113,14 +123,18 @@ private boolean isTestLifecycleAware(StoreAdapter adapter) { @Override public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { - return findTestcontainers(context).map(this::evaluate) + return findTestcontainers(context) + .map(this::evaluate) .orElseThrow(() -> new ExtensionConfigurationException("@Testcontainers not found")); } private Optional findTestcontainers(ExtensionContext context) { Optional current = Optional.of(context); while (current.isPresent()) { - Optional testcontainers = AnnotationUtils.findAnnotation(current.get().getRequiredTestClass(), Testcontainers.class); + Optional testcontainers = AnnotationUtils.findAnnotation( + current.get().getRequiredTestClass(), + Testcontainers.class + ); if (testcontainers.isPresent()) { return testcontainers; } @@ -155,13 +169,11 @@ private Set collectParentTestInstances(final ExtensionContext context) { } private List findSharedContainers(Class testClass) { - return ReflectionUtils.findFields( - testClass, - isSharedContainer(), - ReflectionUtils.HierarchyTraversalMode.TOP_DOWN) + return ReflectionUtils + .findFields(testClass, isSharedContainer(), ReflectionUtils.HierarchyTraversalMode.TOP_DOWN) .stream() .map(f -> getContainerInstance(null, f)) - .collect(toList()); + .collect(Collectors.toList()); } private Predicate isSharedContainer() { @@ -169,10 +181,8 @@ private Predicate isSharedContainer() { } private Stream findRestartContainers(Object testInstance) { - return ReflectionUtils.findFields( - testInstance.getClass(), - isRestartContainer(), - ReflectionUtils.HierarchyTraversalMode.TOP_DOWN) + return ReflectionUtils + .findFields(testInstance.getClass(), isRestartContainer(), ReflectionUtils.HierarchyTraversalMode.TOP_DOWN) .stream() .map(f -> getContainerInstance(testInstance, f)); } @@ -188,7 +198,9 @@ private static Predicate isContainer() { boolean isStartable = Startable.class.isAssignableFrom(field.getType()); if (!isStartable) { - throw new ExtensionConfigurationException(String.format("FieldName: %s does not implement Startable", field.getName())); + throw new ExtensionConfigurationException( + String.format("FieldName: %s does not implement Startable", field.getName()) + ); } return true; } @@ -199,7 +211,10 @@ private static Predicate isContainer() { private static StoreAdapter getContainerInstance(final Object testInstance, final Field field) { try { field.setAccessible(true); - Startable containerInstance = Preconditions.notNull((Startable) field.get(testInstance), "Container " + field.getName() + " needs to be initialized"); + Startable containerInstance = Preconditions.notNull( + (Startable) field.get(testInstance), + "Container " + field.getName() + " needs to be initialized" + ); return new StoreAdapter(field.getDeclaringClass(), field.getName(), containerInstance); } catch (IllegalAccessException e) { throw new ExtensionConfigurationException("Can not access container defined in field " + field.getName()); diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersTestDescription.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersTestDescription.java index 568d228e08c..3cf466a5708 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersTestDescription.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersTestDescription.java @@ -5,6 +5,8 @@ @Value class TestcontainersTestDescription implements TestDescription { + String testId; + String filesystemFriendlyName; } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/ComposeContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/ComposeContainerTests.java index 11b9027b392..f49aa8732f5 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/ComposeContainerTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/ComposeContainerTests.java @@ -18,7 +18,8 @@ class ComposeContainerTests { @Container private DockerComposeContainer composeContainer = new DockerComposeContainer( - new File("src/test/resources/docker-compose.yml")) + new File("src/test/resources/docker-compose.yml") + ) .withExposedService("whoami_1", 80, Wait.forHttp("/")); private String host; diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGeneratorTest.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGeneratorTest.java index 96617b0e063..9050df6f257 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGeneratorTest.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGeneratorTest.java @@ -10,7 +10,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; -import static org.testcontainers.junit.jupiter.FilesystemFriendlyNameGenerator.filesystemFriendlyNameOf; class FilesystemFriendlyNameGeneratorTest { @@ -18,10 +17,9 @@ class FilesystemFriendlyNameGeneratorTest { @MethodSource("provideDisplayNamesAndFilesystemFriendlyNames") void should_generate_filesystem_friendly_name(String displayName, String expectedName) { ExtensionContext context = mock(ExtensionContext.class); - doReturn(displayName) - .when(context).getUniqueId(); + doReturn(displayName).when(context).getUniqueId(); - String filesystemFriendlyName = filesystemFriendlyNameOf(context); + String filesystemFriendlyName = FilesystemFriendlyNameGenerator.filesystemFriendlyNameOf(context); assertThat(filesystemFriendlyName).isEqualTo(expectedName); } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/MixedLifecycleTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/MixedLifecycleTests.java index 791c887cc19..6c44d978b69 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/MixedLifecycleTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/MixedLifecycleTests.java @@ -1,19 +1,11 @@ package org.testcontainers.junit.jupiter; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.HttpClientBuilder; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.testcontainers.containers.DockerComposeContainer; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.containers.PostgreSQLContainer; -import org.testcontainers.containers.wait.strategy.Wait; import static org.junit.jupiter.api.Assertions.assertTrue; - // testClass { @Testcontainers class MixedLifecycleTests { diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/PostgresContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/PostgresContainerTests.java index 4cbc331e64b..186d4095b0d 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/PostgresContainerTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/PostgresContainerTests.java @@ -9,16 +9,17 @@ import java.sql.Statement; import static org.junit.Assert.assertEquals; -import static org.testcontainers.junit.jupiter.JUnitJupiterTestImages.POSTGRES_IMAGE; @Testcontainers class PostgresContainerTests { @Container - private static final PostgreSQLContainer POSTGRE_SQL_CONTAINER = new PostgreSQLContainer<>(POSTGRES_IMAGE) - .withDatabaseName("foo") - .withUsername("foo") - .withPassword("secret"); + private static final PostgreSQLContainer POSTGRE_SQL_CONTAINER = new PostgreSQLContainer<>( + JUnitJupiterTestImages.POSTGRES_IMAGE + ) + .withDatabaseName("foo") + .withUsername("foo") + .withPassword("secret"); @Test void waits_until_postgres_accepts_jdbc_connections() throws Exception { @@ -37,5 +38,4 @@ void waits_until_postgres_accepts_jdbc_connections() throws Exception { assertEquals(1, resultSetInt); } } - } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java index 38751c8789a..a8217d60840 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java @@ -11,9 +11,11 @@ public class TestLifecycleAwareContainerMock implements Startable, TestLifecycleAware { static final String BEFORE_TEST = "beforeTest"; + static final String AFTER_TEST = "afterTest"; private final List lifecycleMethodCalls = new ArrayList<>(); + private final List lifecycleFilesystemFriendlyNames = new ArrayList<>(); private Throwable capturedThrowable; @@ -43,12 +45,8 @@ public List getLifecycleFilesystemFriendlyNames() { } @Override - public void start() { - - } + public void start() {} @Override - public void stop() { - - } + public void stop() {} } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareExceptionCapturingTest.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareExceptionCapturingTest.java index 85d837a7880..09b816be56e 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareExceptionCapturingTest.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareExceptionCapturingTest.java @@ -14,6 +14,7 @@ @Testcontainers @TestMethodOrder(OrderAnnotation.class) class TestLifecycleAwareExceptionCapturingTest { + @Container private final TestLifecycleAwareContainerMock testContainer = new TestLifecycleAwareContainerMock(); diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java index cecdff490ef..a77242e1d3a 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java @@ -10,14 +10,13 @@ import org.junit.jupiter.api.extension.ExtensionContext; import static org.assertj.core.api.Assertions.assertThat; -import static org.testcontainers.junit.jupiter.TestLifecycleAwareContainerMock.AFTER_TEST; -import static org.testcontainers.junit.jupiter.TestLifecycleAwareContainerMock.BEFORE_TEST; // The order of @ExtendsWith and @Testcontainers is crucial for the tests -@ExtendWith({TestLifecycleAwareMethodTest.SharedContainerAfterAllTestExtension.class}) +@ExtendWith({ TestLifecycleAwareMethodTest.SharedContainerAfterAllTestExtension.class }) @Testcontainers @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class TestLifecycleAwareMethodTest { + @Container private final TestLifecycleAwareContainerMock testContainer = new TestLifecycleAwareContainerMock(); @@ -28,8 +27,8 @@ class TestLifecycleAwareMethodTest { @BeforeAll static void beforeAll() { - assertThat(SHARED_CONTAINER.getLifecycleMethodCalls()).containsExactly(BEFORE_TEST); - + assertThat(SHARED_CONTAINER.getLifecycleMethodCalls()) + .containsExactly(TestLifecycleAwareContainerMock.BEFORE_TEST); } @Test @@ -43,7 +42,7 @@ void should_prepare_before_and_after_test() { @Order(2) void should_call_beforeTest_first_afterTest_later_with_filesystem_friendly_name() { assertThat(startedTestContainer.getLifecycleMethodCalls()) - .containsExactly(BEFORE_TEST, AFTER_TEST); + .containsExactly(TestLifecycleAwareContainerMock.BEFORE_TEST, TestLifecycleAwareContainerMock.AFTER_TEST); } @Test @@ -63,11 +62,15 @@ void static_container_should_have_a_filesystem_friendly_name_after_container_has } static class SharedContainerAfterAllTestExtension implements AfterAllCallback { + // Unfortunately it's not possible to write a @Test that is run after all tests @Override public void afterAll(ExtensionContext context) { assertThat(SHARED_CONTAINER.getLifecycleMethodCalls()) - .containsExactly(BEFORE_TEST, AFTER_TEST); + .containsExactly( + TestLifecycleAwareContainerMock.BEFORE_TEST, + TestLifecycleAwareContainerMock.AFTER_TEST + ); } } } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersExtensionTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersExtensionTests.java index f3aea9bb850..526038853c9 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersExtensionTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersExtensionTests.java @@ -46,14 +46,10 @@ private ExtensionContext extensionContext(Class clazz) { } @Testcontainers(disabledWithoutDocker = true) - static final class DisabledWithoutDocker { - - } + static final class DisabledWithoutDocker {} @Testcontainers - static final class EnabledWithoutDocker { - - } + static final class EnabledWithoutDocker {} static final class TestTestcontainersExtension extends TestcontainersExtension { @@ -66,7 +62,5 @@ private TestTestcontainersExtension(boolean dockerAvailable) { boolean isDockerAvailable() { return dockerAvailable; } - } - } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java index 6e02e31b774..c41b184defa 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java @@ -6,15 +6,15 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.testcontainers.junit.jupiter.JUnitJupiterTestImages.HTTPD_IMAGE; // testClass { @Testcontainers class TestcontainersNestedRestartedContainerTests { @Container - private final GenericContainer topLevelContainer = new GenericContainer<>(HTTPD_IMAGE) + private final GenericContainer topLevelContainer = new GenericContainer<>(JUnitJupiterTestImages.HTTPD_IMAGE) .withExposedPorts(80); + // }} private static String topLevelContainerId; @@ -25,16 +25,16 @@ class TestcontainersNestedRestartedContainerTests { @Test void top_level_container_should_be_running() { assertTrue(topLevelContainer.isRunning()); -// }} + // }} topLevelContainerId = topLevelContainer.getContainerId(); -// testClass {{ + // testClass {{ } @Nested class NestedTestCase { @Container - private final GenericContainer nestedContainer = new GenericContainer<>(HTTPD_IMAGE) + private final GenericContainer nestedContainer = new GenericContainer<>(JUnitJupiterTestImages.HTTPD_IMAGE) .withExposedPorts(80); @Test @@ -43,13 +43,13 @@ void both_containers_should_be_running() { assertTrue(topLevelContainer.isRunning()); // nested containers are only available inside their nested class assertTrue(nestedContainer.isRunning()); -// }}} + // }}} if (nestedContainerId == null) { nestedContainerId = nestedContainer.getContainerId(); } else { assertNotEquals(nestedContainerId, nestedContainer.getContainerId()); } -// testClass {{ + // testClass {{ } // } @@ -74,7 +74,7 @@ void ids_should_not_change() { assertNotEquals(nestedContainerId, nestedContainer.getContainerId()); } } -// testClass {{{ + // testClass {{{ } } // } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedSharedContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedSharedContainerTests.java index cf59e0338a2..c00e66c2d9d 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedSharedContainerTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedSharedContainerTests.java @@ -6,13 +6,14 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.testcontainers.junit.jupiter.JUnitJupiterTestImages.HTTPD_IMAGE; @Testcontainers class TestcontainersNestedSharedContainerTests { @Container - private static final GenericContainer TOP_LEVEL_CONTAINER = new GenericContainer<>(HTTPD_IMAGE) + private static final GenericContainer TOP_LEVEL_CONTAINER = new GenericContainer<>( + JUnitJupiterTestImages.HTTPD_IMAGE + ) .withExposedPorts(80); private static String topLevelContainerId; diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersRestartBetweenTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersRestartBetweenTests.java index eeb63b72380..46c6045984f 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersRestartBetweenTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersRestartBetweenTests.java @@ -4,14 +4,13 @@ import org.testcontainers.containers.GenericContainer; import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.testcontainers.junit.jupiter.JUnitJupiterTestImages.HTTPD_IMAGE; @Testcontainers class TestcontainersRestartBetweenTests { @Container - private GenericContainer genericContainer = new GenericContainer<>(HTTPD_IMAGE) - .withExposedPorts(80); + private GenericContainer genericContainer = new GenericContainer<>(JUnitJupiterTestImages.HTTPD_IMAGE) + .withExposedPorts(80); private static String lastContainerId; @@ -19,7 +18,7 @@ class TestcontainersRestartBetweenTests { void first_test() { if (lastContainerId == null) { lastContainerId = genericContainer.getContainerId(); - } else { + } else { assertNotEquals(lastContainerId, genericContainer.getContainerId()); } } @@ -28,9 +27,8 @@ void first_test() { void second_test() { if (lastContainerId == null) { lastContainerId = genericContainer.getContainerId(); - } else { + } else { assertNotEquals(lastContainerId, genericContainer.getContainerId()); } } - } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersSharedContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersSharedContainerTests.java index 60094994f69..b23b2fe0e53 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersSharedContainerTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersSharedContainerTests.java @@ -6,13 +6,14 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.testcontainers.junit.jupiter.JUnitJupiterTestImages.HTTPD_IMAGE; @Testcontainers class TestcontainersSharedContainerTests { @Container - private static final GenericContainer GENERIC_CONTAINER = new GenericContainer<>(HTTPD_IMAGE) + private static final GenericContainer GENERIC_CONTAINER = new GenericContainer<>( + JUnitJupiterTestImages.HTTPD_IMAGE + ) .withExposedPorts(80); private static String lastContainerId; @@ -39,5 +40,4 @@ void second_test() { assertEquals(lastContainerId, GENERIC_CONTAINER.getContainerId()); } } - } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/WrongAnnotationUsageTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/WrongAnnotationUsageTests.java index bec99894307..9fe9893d9ee 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/WrongAnnotationUsageTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/WrongAnnotationUsageTests.java @@ -1,10 +1,8 @@ package org.testcontainers.junit.jupiter; - import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; - @Disabled @Testcontainers class WrongAnnotationUsageTests { @@ -16,5 +14,4 @@ class WrongAnnotationUsageTests { void extension_throws_exception() { assert true; } - } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/AbstractTestBase.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/AbstractTestBase.java index 3766c3f7a60..70614d3dbc7 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/AbstractTestBase.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/AbstractTestBase.java @@ -11,5 +11,4 @@ abstract class AbstractTestBase { @Container RedisContainer redisPerTest = new RedisContainer(); - } diff --git a/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java b/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java index 7f21ca7dbb8..c9dad93a06e 100644 --- a/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java +++ b/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java @@ -6,7 +6,6 @@ import com.fasterxml.jackson.databind.node.TextNode; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.github.dockerjava.api.command.InspectContainerResponse; -import com.github.dockerjava.api.model.DockerObjectAccessor; import lombok.SneakyThrows; import org.apache.commons.io.IOUtils; import org.testcontainers.containers.BindMode; @@ -42,17 +41,14 @@ public K3sContainer(DockerImageName dockerImageName) { tmpFsMapping.put("/var/run", ""); setTmpFsMapping(tmpFsMapping); - setCommand( - "server", - "--no-deploy=traefik", - "--tls-san=" + this.getHost() - ); + setCommand("server", "--no-deploy=traefik", "--tls-san=" + this.getHost()); setWaitStrategy(new LogMessageWaitStrategy().withRegEx(".*Node controller sync successful.*")); } @Override protected void containerIsStarted(InspectContainerResponse containerInfo) { - String rawKubeConfig = copyFileFromContainer("/etc/rancher/k3s/k3s.yaml", + String rawKubeConfig = copyFileFromContainer( + "/etc/rancher/k3s/k3s.yaml", is -> IOUtils.toString(is, StandardCharsets.UTF_8) ); String serverUrl = "https://" + this.getHost() + ":" + this.getMappedPort(KUBE_SECURE_PORT); @@ -100,7 +96,6 @@ private String kubeConfigWithServerUrl(String kubeConfigYaml, String serverUrl) kubeConfigObjectNode.set("current-context", new TextNode("default")); - return objectMapper.writerWithDefaultPrettyPrinter() - .writeValueAsString(kubeConfigObjectNode); + return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(kubeConfigObjectNode); } } diff --git a/modules/k3s/src/test/java/org/testcontainers/k3s/Fabric8K3sContainerTest.java b/modules/k3s/src/test/java/org/testcontainers/k3s/Fabric8K3sContainerTest.java index 8b31c299634..6f9550eab2a 100644 --- a/modules/k3s/src/test/java/org/testcontainers/k3s/Fabric8K3sContainerTest.java +++ b/modules/k3s/src/test/java/org/testcontainers/k3s/Fabric8K3sContainerTest.java @@ -10,19 +10,17 @@ import io.fabric8.kubernetes.api.model.ProbeBuilder; import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.DefaultKubernetesClient; -import io.fabric8.kubernetes.client.dsl.PodResource; import io.fabric8.kubernetes.client.dsl.Readiable; import lombok.extern.slf4j.Slf4j; -import org.assertj.core.api.Assertions; -import org.assertj.core.api.Condition; import org.junit.Test; import org.testcontainers.containers.output.Slf4jLogConsumer; import org.testcontainers.utility.DockerImageName; import java.util.List; -import java.util.Map; import java.util.concurrent.TimeUnit; +import static org.assertj.core.api.Assertions.assertThat; + @Slf4j public class Fabric8K3sContainerTest { @@ -49,14 +47,14 @@ public void shouldStartAndHaveListableNode() { List nodes = client.nodes().list().getItems(); // } - Assertions.assertThat(nodes).hasSize(1); + assertThat(nodes).hasSize(1); // verify that we can start a pod Pod helloworld = dummyStartablePod(); client.pods().create(helloworld); client.pods().inNamespace("default").withName("helloworld").waitUntilReady(30, TimeUnit.SECONDS); - Assertions.assertThat(client.pods().inNamespace("default").withName("helloworld")) + assertThat(client.pods().inNamespace("default").withName("helloworld")) .extracting(Readiable::isReady) .isEqualTo(true); } diff --git a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java index 8b1128efef0..02342351b83 100644 --- a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java +++ b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java @@ -11,6 +11,7 @@ public class KafkaContainer extends GenericContainer { private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("confluentinc/cp-kafka"); + private static final String DEFAULT_TAG = "5.4.3"; public static final int KAFKA_PORT = 9093; @@ -39,7 +40,6 @@ public KafkaContainer(String confluentPlatformVersion) { public KafkaContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); withExposedPorts(KAFKA_PORT); @@ -77,12 +77,7 @@ public String getBootstrapServers() { protected void configure() { withEnv( "KAFKA_ADVERTISED_LISTENERS", - String.format( - "BROKER://%s:9092", - getNetwork() != null - ? getNetworkAliases().get(0) - : "localhost" - ) + String.format("BROKER://%s:9092", getNetwork() != null ? getNetworkAliases().get(0) : "localhost") ); String command = "#!/bin/bash\n"; @@ -111,9 +106,12 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { ExecResult result = execInContainer( "kafka-configs", "--alter", - "--bootstrap-server", brokerAdvertisedListener, - "--entity-type", "brokers", - "--entity-name", getEnvMap().get("KAFKA_BROKER_ID"), + "--bootstrap-server", + brokerAdvertisedListener, + "--entity-type", + "brokers", + "--entity-name", + getEnvMap().get("KAFKA_BROKER_ID"), "--add-config", "advertised.listeners=[" + String.join(",", getBootstrapServers(), brokerAdvertisedListener) + "]" ); diff --git a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java index f0ef0db2067..380df00e9e9 100644 --- a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java +++ b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java @@ -20,17 +20,20 @@ import java.time.Duration; import java.util.Collection; +import java.util.Collections; import java.util.UUID; import java.util.concurrent.TimeUnit; -import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; public class KafkaContainerTest { private static final DockerImageName KAFKA_TEST_IMAGE = DockerImageName.parse("confluentinc/cp-kafka:6.2.1"); - private static final DockerImageName ZOOKEEPER_TEST_IMAGE = DockerImageName.parse("confluentinc/cp-zookeeper:4.0.0"); + + private static final DockerImageName ZOOKEEPER_TEST_IMAGE = DockerImageName.parse( + "confluentinc/cp-zookeeper:4.0.0" + ); @Test public void testUsage() throws Exception { @@ -40,7 +43,6 @@ public void testUsage() throws Exception { } } - @Test public void testUsageWithSpecificImage() throws Exception { try ( @@ -50,18 +52,16 @@ public void testUsageWithSpecificImage() throws Exception { ) { kafka.start(); testKafkaFunctionality( - // getBootstrapServers { - kafka.getBootstrapServers() - // } + // getBootstrapServers { + kafka.getBootstrapServers() + // } ); } } @Test public void testUsageWithVersion() throws Exception { - try ( - KafkaContainer kafka = new KafkaContainer("6.2.1") - ) { + try (KafkaContainer kafka = new KafkaContainer("6.2.1")) { kafka.start(); testKafkaFunctionality(kafka.getBootstrapServers()); } @@ -71,7 +71,6 @@ public void testUsageWithVersion() throws Exception { public void testExternalZookeeperWithExternalNetwork() throws Exception { try ( Network network = Network.newNetwork(); - // withExternalZookeeper { KafkaContainer kafka = new KafkaContainer(KAFKA_TEST_IMAGE) .withNetwork(network) @@ -82,11 +81,10 @@ public void testExternalZookeeperWithExternalNetwork() throws Exception { .withNetwork(network) .withNetworkAliases("zookeeper") .withEnv("ZOOKEEPER_CLIENT_PORT", "2181"); - // withKafkaNetwork { GenericContainer application = new GenericContainer<>(DockerImageName.parse("alpine")) .withNetwork(network) - // } + // } .withNetworkAliases("dummy") .withCommand("sleep 10000") ) { @@ -100,9 +98,7 @@ public void testExternalZookeeperWithExternalNetwork() throws Exception { @Test public void testConfluentPlatformVersion5() throws Exception { - try ( - KafkaContainer kafka = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:5.4.3")) - ) { + try (KafkaContainer kafka = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:5.4.3"))) { kafka.start(); testKafkaFunctionality(kafka.getBootstrapServers()); } @@ -132,24 +128,27 @@ protected void testKafkaFunctionality(String bootstrapServers) throws Exception protected void testKafkaFunctionality(String bootstrapServers, int partitions, int rf) throws Exception { try ( - AdminClient adminClient = AdminClient.create(ImmutableMap.of( - AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers - )); - + AdminClient adminClient = AdminClient.create( + ImmutableMap.of(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers) + ); KafkaProducer producer = new KafkaProducer<>( ImmutableMap.of( - ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers, - ProducerConfig.CLIENT_ID_CONFIG, UUID.randomUUID().toString() + ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, + bootstrapServers, + ProducerConfig.CLIENT_ID_CONFIG, + UUID.randomUUID().toString() ), new StringSerializer(), new StringSerializer() ); - KafkaConsumer consumer = new KafkaConsumer<>( ImmutableMap.of( - ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers, - ConsumerConfig.GROUP_ID_CONFIG, "tc-" + UUID.randomUUID(), - ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest" + ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, + bootstrapServers, + ConsumerConfig.GROUP_ID_CONFIG, + "tc-" + UUID.randomUUID(), + ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, + "earliest" ), new StringDeserializer(), new StringDeserializer() @@ -157,30 +156,33 @@ protected void testKafkaFunctionality(String bootstrapServers, int partitions, i ) { String topicName = "messages-" + UUID.randomUUID(); - Collection topics = singletonList(new NewTopic(topicName, partitions, (short) rf)); + Collection topics = Collections.singletonList(new NewTopic(topicName, partitions, (short) rf)); adminClient.createTopics(topics).all().get(30, TimeUnit.SECONDS); - consumer.subscribe(singletonList(topicName)); + consumer.subscribe(Collections.singletonList(topicName)); producer.send(new ProducerRecord<>(topicName, "testcontainers", "rulezzz")).get(); - Unreliables.retryUntilTrue(10, TimeUnit.SECONDS, () -> { - ConsumerRecords records = consumer.poll(Duration.ofMillis(100)); + Unreliables.retryUntilTrue( + 10, + TimeUnit.SECONDS, + () -> { + ConsumerRecords records = consumer.poll(Duration.ofMillis(100)); - if (records.isEmpty()) { - return false; - } + if (records.isEmpty()) { + return false; + } - assertThat(records) - .hasSize(1) - .extracting(ConsumerRecord::topic, ConsumerRecord::key, ConsumerRecord::value) - .containsExactly(tuple(topicName, "testcontainers", "rulezzz")); + assertThat(records) + .hasSize(1) + .extracting(ConsumerRecord::topic, ConsumerRecord::key, ConsumerRecord::value) + .containsExactly(tuple(topicName, "testcontainers", "rulezzz")); - return true; - }); + return true; + } + ); consumer.unsubscribe(); } } - } diff --git a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java index dd03a4206e0..8bd6f14d3fd 100644 --- a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java +++ b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java @@ -36,10 +36,13 @@ public class LocalStackContainer extends GenericContainer { static final int PORT = 4566; + private static final String HOSTNAME_EXTERNAL_ENV_VAR = "HOSTNAME_EXTERNAL"; + private final List services = new ArrayList<>(); private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("localstack/localstack"); + private static final String DEFAULT_TAG = "0.11.2"; @Deprecated @@ -88,7 +91,6 @@ public LocalStackContainer(final DockerImageName dockerImageName) { */ public LocalStackContainer(final DockerImageName dockerImageName, boolean useLegacyMode) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); this.legacyMode = useLegacyMode; @@ -109,7 +111,9 @@ private static boolean shouldRunInLegacyMode(String version) { } log.warn("Version {} is not a semantic version, LocalStack will run in legacy mode.", version); - log.warn("Consider using \"LocalStackContainer(DockerImageName dockerImageName, boolean legacyMode)\" constructor if you want to disable legacy mode."); + log.warn( + "Consider using \"LocalStackContainer(DockerImageName dockerImageName, boolean legacyMode)\" constructor if you want to disable legacy mode." + ); return true; } @@ -126,22 +130,25 @@ protected void configure() { // do nothing hostnameExternalReason = "explicitly as environment variable"; } else if (getNetwork() != null && getNetworkAliases() != null && getNetworkAliases().size() >= 1) { - withEnv(HOSTNAME_EXTERNAL_ENV_VAR, getNetworkAliases().get(getNetworkAliases().size() - 1)); // use the last network alias set + withEnv(HOSTNAME_EXTERNAL_ENV_VAR, getNetworkAliases().get(getNetworkAliases().size() - 1)); // use the last network alias set hostnameExternalReason = "to match last network alias on container with non-default network"; } else { withEnv(HOSTNAME_EXTERNAL_ENV_VAR, getHost()); hostnameExternalReason = "to match host-routable address for container"; } - logger().info("{} environment variable set to {} ({})", HOSTNAME_EXTERNAL_ENV_VAR, getEnvMap().get(HOSTNAME_EXTERNAL_ENV_VAR), hostnameExternalReason); + logger() + .info( + "{} environment variable set to {} ({})", + HOSTNAME_EXTERNAL_ENV_VAR, + getEnvMap().get(HOSTNAME_EXTERNAL_ENV_VAR), + hostnameExternalReason + ); exposePorts(); } private void exposePorts() { - services.stream() - .map(this::getServicePort) - .distinct() - .forEach(this::addExposedPort); + services.stream().map(this::getServicePort).distinct().forEach(this::addExposedPort); } public LocalStackContainer withServices(Service... services) { @@ -218,10 +225,7 @@ public URI getEndpointOverride(EnabledService service) { String ipAddress = address; // resolve IP address and use that as the endpoint so that path-style access is automatically used for S3 ipAddress = InetAddress.getByName(address).getHostAddress(); - return new URI("http://" + - ipAddress + - ":" + - getMappedPort(getServicePort(service))); + return new URI("http://" + ipAddress + ":" + getMappedPort(getServicePort(service))); } catch (UnknownHostException | URISyntaxException e) { throw new IllegalStateException("Cannot obtain endpoint URL", e); } @@ -332,7 +336,7 @@ public enum Service implements EnabledService { DYNAMODB("dynamodb", 4569), DYNAMODB_STREAMS("dynamodbstreams", 4570), // TODO: Clarify usage for ELASTICSEARCH and ELASTICSEARCH_SERVICE -// ELASTICSEARCH("es", 4571), + // ELASTICSEARCH("es", 4571), S3("s3", 4572), FIREHOSE("firehose", 4573), LAMBDA("lambda", 4574), diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java index b34f43f79ed..a757c11e928 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java @@ -16,48 +16,51 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.containers.localstack.LocalStackContainer.Service.S3; -import static org.testcontainers.containers.localstack.LocalStackContainer.Service.SQS; -import static org.testcontainers.containers.localstack.LocalstackTestImages.LOCALSTACK_0_10_IMAGE; -import static org.testcontainers.containers.localstack.LocalstackTestImages.LOCALSTACK_0_11_IMAGE; -import static org.testcontainers.containers.localstack.LocalstackTestImages.LOCALSTACK_0_12_IMAGE; -import static org.testcontainers.containers.localstack.LocalstackTestImages.LOCALSTACK_0_7_IMAGE; -import static org.testcontainers.containers.localstack.LocalstackTestImages.LOCALSTACK_IMAGE; @RunWith(Enclosed.class) public class LegacyModeTest { - private static DockerImageName LOCALSTACK_CUSTOM_TAG = LOCALSTACK_IMAGE.withTag("custom"); + + private static DockerImageName LOCALSTACK_CUSTOM_TAG = LocalstackTestImages.LOCALSTACK_IMAGE.withTag("custom"); @RunWith(Parameterized.class) @AllArgsConstructor public static class Off { + private final String description; + private final LocalStackContainer localstack; @Parameterized.Parameters(name = "{0}") public static Iterable constructors() { - return Arrays.asList(new Object[][]{ - {"0.12", new LocalStackContainer(LOCALSTACK_0_12_IMAGE)}, - {"0.11", new LocalStackContainer(LOCALSTACK_0_11_IMAGE)}, - {"0.7 with legacy = off", new LocalStackContainer(LOCALSTACK_0_7_IMAGE, false)} - }); + return Arrays.asList( + new Object[][] { + { "0.12", new LocalStackContainer(LocalstackTestImages.LOCALSTACK_0_12_IMAGE) }, + { "0.11", new LocalStackContainer(LocalstackTestImages.LOCALSTACK_0_11_IMAGE) }, + { + "0.7 with legacy = off", + new LocalStackContainer(LocalstackTestImages.LOCALSTACK_0_7_IMAGE, false), + }, + } + ); } @Test public void samePortIsExposedForAllServices() { - localstack.withServices(S3, SQS); + localstack.withServices(LocalStackContainer.Service.S3, LocalStackContainer.Service.SQS); localstack.start(); try { assertTrue("A single port is exposed", localstack.getExposedPorts().size() == 1); assertEquals( "Endpoint overrides are different", - localstack.getEndpointOverride(S3).toString(), - localstack.getEndpointOverride(SQS).toString()); + localstack.getEndpointOverride(LocalStackContainer.Service.S3).toString(), + localstack.getEndpointOverride(LocalStackContainer.Service.SQS).toString() + ); assertEquals( "Endpoint configuration have different endpoints", - localstack.getEndpointConfiguration(S3).getServiceEndpoint(), - localstack.getEndpointConfiguration(SQS).getServiceEndpoint()); + localstack.getEndpointConfiguration(LocalStackContainer.Service.S3).getServiceEndpoint(), + localstack.getEndpointConfiguration(LocalStackContainer.Service.SQS).getServiceEndpoint() + ); } finally { localstack.stop(); } @@ -67,7 +70,9 @@ public void samePortIsExposedForAllServices() { @RunWith(Parameterized.class) @AllArgsConstructor public static class On { + private final String description; + private final LocalStackContainer localstack; @BeforeClass @@ -75,7 +80,7 @@ public static void createCustomTag() { DockerClient dockerClient = DockerClientFactory.instance().client(); dockerClient .tagImageCmd( - new RemoteDockerImage(LOCALSTACK_0_12_IMAGE).get(), + new RemoteDockerImage(LocalstackTestImages.LOCALSTACK_0_12_IMAGE).get(), LOCALSTACK_CUSTOM_TAG.getRepository(), LOCALSTACK_CUSTOM_TAG.getVersionPart() ) @@ -84,28 +89,35 @@ public static void createCustomTag() { @Parameterized.Parameters(name = "{0}") public static Iterable constructors() { - return Arrays.asList(new Object[][]{ - {"0.10", new LocalStackContainer(LOCALSTACK_0_10_IMAGE)}, - {"custom", new LocalStackContainer(LOCALSTACK_CUSTOM_TAG)}, - {"0.11 with legacy = on", new LocalStackContainer(LOCALSTACK_0_11_IMAGE, true)} - }); + return Arrays.asList( + new Object[][] { + { "0.10", new LocalStackContainer(LocalstackTestImages.LOCALSTACK_0_10_IMAGE) }, + { "custom", new LocalStackContainer(LOCALSTACK_CUSTOM_TAG) }, + { + "0.11 with legacy = on", + new LocalStackContainer(LocalstackTestImages.LOCALSTACK_0_11_IMAGE, true), + }, + } + ); } @Test public void differentPortsAreExposed() { - localstack.withServices(S3, SQS); + localstack.withServices(LocalStackContainer.Service.S3, LocalStackContainer.Service.SQS); localstack.start(); try { assertTrue("Multiple ports are exposed", localstack.getExposedPorts().size() > 1); assertNotEquals( "Endpoint overrides are different", - localstack.getEndpointOverride(S3).toString(), - localstack.getEndpointOverride(SQS).toString()); + localstack.getEndpointOverride(LocalStackContainer.Service.S3).toString(), + localstack.getEndpointOverride(LocalStackContainer.Service.SQS).toString() + ); assertNotEquals( "Endpoint configuration have different endpoints", - localstack.getEndpointConfiguration(S3).getServiceEndpoint(), - localstack.getEndpointConfiguration(SQS).getServiceEndpoint()); + localstack.getEndpointConfiguration(LocalStackContainer.Service.S3).getServiceEndpoint(), + localstack.getEndpointConfiguration(LocalStackContainer.Service.SQS).getServiceEndpoint() + ); } finally { localstack.stop(); } diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java index 6b32ba2c102..cf4e1a813a4 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java @@ -1,17 +1,5 @@ package org.testcontainers.containers.localstack; - -import static org.hamcrest.CoreMatchers.containsString; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.containers.localstack.LocalStackContainer.PORT; -import static org.testcontainers.containers.localstack.LocalStackContainer.Service.CLOUDWATCHLOGS; -import static org.testcontainers.containers.localstack.LocalStackContainer.Service.KMS; -import static org.testcontainers.containers.localstack.LocalStackContainer.Service.S3; -import static org.testcontainers.containers.localstack.LocalStackContainer.Service.SQS; -import static org.testcontainers.containers.localstack.LocalstackTestImages.LOCALSTACK_IMAGE; - import com.amazonaws.services.kms.AWSKMS; import com.amazonaws.services.kms.AWSKMSClientBuilder; import com.amazonaws.services.kms.model.CreateKeyRequest; @@ -29,10 +17,6 @@ import com.amazonaws.services.sqs.AmazonSQS; import com.amazonaws.services.sqs.AmazonSQSClientBuilder; import com.amazonaws.services.sqs.model.CreateQueueResult; -import java.io.IOException; -import java.nio.charset.Charset; -import java.util.List; -import java.util.Optional; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.junit.Assert; @@ -44,12 +28,22 @@ import org.testcontainers.containers.Container; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; -import org.testcontainers.utility.DockerImageName; +import org.testcontainers.containers.localstack.LocalStackContainer.Service; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3Client; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.List; +import java.util.Optional; + +import static org.hamcrest.Matchers.containsString; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; + /** * Tests for Localstack Container, used both in bridge network (exposed to host) and docker network modes. *

@@ -65,15 +59,22 @@ public static class WithoutNetwork { // without_network { @ClassRule - public static LocalStackContainer localstack = new LocalStackContainer(LOCALSTACK_IMAGE) - .withServices(S3, SQS, CLOUDWATCHLOGS, KMS, LocalStackContainer.EnabledService.named("events")); + public static LocalStackContainer localstack = new LocalStackContainer(LocalstackTestImages.LOCALSTACK_IMAGE) + .withServices( + Service.S3, + Service.SQS, + Service.CLOUDWATCHLOGS, + Service.KMS, + LocalStackContainer.EnabledService.named("events") + ); + // } @Test public void s3TestOverBridgeNetwork() throws IOException { AmazonS3 s3 = AmazonS3ClientBuilder .standard() - .withEndpointConfiguration(localstack.getEndpointConfiguration(S3)) + .withEndpointConfiguration(localstack.getEndpointConfiguration(Service.S3)) .withCredentials(localstack.getDefaultCredentialsProvider()) .build(); @@ -82,7 +83,10 @@ public void s3TestOverBridgeNetwork() throws IOException { s3.putObject(bucketName, "bar", "baz"); final List buckets = s3.listBuckets(); - final Optional maybeBucket = buckets.stream().filter(b -> b.getName().equals(bucketName)).findFirst(); + final Optional maybeBucket = buckets + .stream() + .filter(b -> b.getName().equals(bucketName)) + .findFirst(); assertTrue("The created bucket is present", maybeBucket.isPresent()); final Bucket bucket = maybeBucket.get(); @@ -100,32 +104,49 @@ public void s3TestOverBridgeNetwork() throws IOException { public void s3TestUsingAwsSdkV2() { S3Client s3 = S3Client .builder() - .endpointOverride(localstack.getEndpointOverride(LocalStackContainer.Service.S3)) - .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create( - localstack.getAccessKey(), localstack.getSecretKey() - ))) + .endpointOverride(localstack.getEndpointOverride(Service.S3)) + .credentialsProvider( + StaticCredentialsProvider.create( + AwsBasicCredentials.create(localstack.getAccessKey(), localstack.getSecretKey()) + ) + ) .region(Region.of(localstack.getRegion())) .build(); final String bucketName = "foov2"; s3.createBucket(b -> b.bucket(bucketName)); - assertTrue("New bucket was created", s3.listBuckets().buckets().stream().anyMatch(b -> b.name().equals(bucketName))); + assertTrue( + "New bucket was created", + s3.listBuckets().buckets().stream().anyMatch(b -> b.name().equals(bucketName)) + ); } @Test public void sqsTestOverBridgeNetwork() { - AmazonSQS sqs = AmazonSQSClientBuilder.standard() - .withEndpointConfiguration(localstack.getEndpointConfiguration(SQS)) + AmazonSQS sqs = AmazonSQSClientBuilder + .standard() + .withEndpointConfiguration(localstack.getEndpointConfiguration(Service.SQS)) .withCredentials(localstack.getDefaultCredentialsProvider()) .build(); CreateQueueResult queueResult = sqs.createQueue("baz"); String fooQueueUrl = queueResult.getQueueUrl(); - assertThat("Created queue has external hostname URL", fooQueueUrl, - containsString("http://" + DockerClientFactory.instance().dockerHostIpAddress() + ":" + localstack.getMappedPort(PORT))); + assertThat( + "Created queue has external hostname URL", + fooQueueUrl, + containsString( + "http://" + + DockerClientFactory.instance().dockerHostIpAddress() + + ":" + + localstack.getMappedPort(LocalStackContainer.PORT) + ) + ); sqs.sendMessage(fooQueueUrl, "test"); - final long messageCount = sqs.receiveMessage(fooQueueUrl).getMessages().stream() + final long messageCount = sqs + .receiveMessage(fooQueueUrl) + .getMessages() + .stream() .filter(message -> message.getBody().equals("test")) .count(); assertEquals("the sent message can be received", 1L, messageCount); @@ -133,9 +154,11 @@ public void sqsTestOverBridgeNetwork() { @Test public void cloudWatchLogsTestOverBridgeNetwork() { - AWSLogs logs = AWSLogsClientBuilder.standard() - .withEndpointConfiguration(localstack.getEndpointConfiguration(CLOUDWATCHLOGS)) - .withCredentials(localstack.getDefaultCredentialsProvider()).build(); + AWSLogs logs = AWSLogsClientBuilder + .standard() + .withEndpointConfiguration(localstack.getEndpointConfiguration(Service.CLOUDWATCHLOGS)) + .withCredentials(localstack.getDefaultCredentialsProvider()) + .build(); logs.createLogGroup(new CreateLogGroupRequest("foo")); @@ -146,8 +169,9 @@ public void cloudWatchLogsTestOverBridgeNetwork() { @Test public void kmsKeyCreationTest() { - AWSKMS awskms = AWSKMSClientBuilder.standard() - .withEndpointConfiguration(localstack.getEndpointConfiguration(KMS)) + AWSKMS awskms = AWSKMSClientBuilder + .standard() + .withEndpointConfiguration(localstack.getEndpointConfiguration(Service.KMS)) .withCredentials(localstack.getDefaultCredentialsProvider()) .build(); @@ -156,7 +180,11 @@ public void kmsKeyCreationTest() { CreateKeyRequest req = new CreateKeyRequest().withDescription(desc).withTags(createdByTag); CreateKeyResult key = awskms.createKey(req); - assertEquals("AWS KMS Customer Managed Key should be created ", key.getKeyMetadata().getDescription(), desc); + assertEquals( + "AWS KMS Customer Managed Key should be created ", + key.getKeyMetadata().getDescription(), + desc + ); } @Test @@ -164,35 +192,42 @@ public void samePortIsExposedForAllServices() { assertTrue("A single port is exposed", localstack.getExposedPorts().size() == 1); assertEquals( "Endpoint overrides are different", - localstack.getEndpointOverride(S3).toString(), - localstack.getEndpointOverride(SQS).toString()); + localstack.getEndpointOverride(Service.S3).toString(), + localstack.getEndpointOverride(Service.SQS).toString() + ); assertEquals( "Endpoint configuration have different endpoints", - localstack.getEndpointConfiguration(S3).getServiceEndpoint(), - localstack.getEndpointConfiguration(SQS).getServiceEndpoint()); + localstack.getEndpointConfiguration(Service.S3).getServiceEndpoint(), + localstack.getEndpointConfiguration(Service.SQS).getServiceEndpoint() + ); } } public static class WithNetwork { + // with_network { private static Network network = Network.newNetwork(); @ClassRule - public static LocalStackContainer localstackInDockerNetwork = new LocalStackContainer(LOCALSTACK_IMAGE) + public static LocalStackContainer localstackInDockerNetwork = new LocalStackContainer( + LocalstackTestImages.LOCALSTACK_IMAGE + ) .withNetwork(network) - .withNetworkAliases("notthis", "localstack") // the last alias is used for HOSTNAME_EXTERNAL - .withServices(S3, SQS, CLOUDWATCHLOGS); + .withNetworkAliases("notthis", "localstack") // the last alias is used for HOSTNAME_EXTERNAL + .withServices(Service.S3, Service.SQS, Service.CLOUDWATCHLOGS); + // } @ClassRule - public static GenericContainer awsCliInDockerNetwork = new GenericContainer<>(LocalstackTestImages.AWS_CLI_IMAGE) + public static GenericContainer awsCliInDockerNetwork = new GenericContainer<>( + LocalstackTestImages.AWS_CLI_IMAGE + ) .withNetwork(network) .withCreateContainerCmdModifier(cmd -> cmd.withEntrypoint("top")) .withEnv("AWS_ACCESS_KEY_ID", "accesskey") .withEnv("AWS_SECRET_ACCESS_KEY", "secretkey") .withEnv("AWS_REGION", "eu-west-1"); - @Test public void s3TestOverDockerNetwork() throws Exception { runAwsCliAgainstDockerNetworkContainer("s3api create-bucket --bucket foo"); @@ -202,15 +237,30 @@ public void s3TestOverDockerNetwork() throws Exception { @Test public void sqsTestOverDockerNetwork() throws Exception { - final String queueCreationResponse = runAwsCliAgainstDockerNetworkContainer("sqs create-queue --queue-name baz"); + final String queueCreationResponse = runAwsCliAgainstDockerNetworkContainer( + "sqs create-queue --queue-name baz" + ); - assertThat("Created queue has external hostname URL", queueCreationResponse, - containsString("http://localstack:" + PORT)); + assertThat( + "Created queue has external hostname URL", + queueCreationResponse, + containsString("http://localstack:" + LocalStackContainer.PORT) + ); runAwsCliAgainstDockerNetworkContainer( - String.format("sqs send-message --endpoint http://localstack:%d --queue-url http://localstack:%d/queue/baz --message-body test", PORT, PORT)); + String.format( + "sqs send-message --endpoint http://localstack:%d --queue-url http://localstack:%d/queue/baz --message-body test", + LocalStackContainer.PORT, + LocalStackContainer.PORT + ) + ); final String message = runAwsCliAgainstDockerNetworkContainer( - String.format("sqs receive-message --endpoint http://localstack:%d --queue-url http://localstack:%d/queue/baz", PORT, PORT)); + String.format( + "sqs receive-message --endpoint http://localstack:%d --queue-url http://localstack:%d/queue/baz", + LocalStackContainer.PORT, + LocalStackContainer.PORT + ) + ); assertTrue("the sent message can be received", message.contains("\"Body\": \"test\"")); } @@ -221,7 +271,13 @@ public void cloudWatchLogsTestOverDockerNetwork() throws Exception { } private String runAwsCliAgainstDockerNetworkContainer(String command) throws Exception { - final String[] commandParts = String.format("/usr/bin/aws --region eu-west-1 %s --endpoint-url http://localstack:%d --no-verify-ssl", command, PORT).split(" "); + final String[] commandParts = String + .format( + "/usr/bin/aws --region eu-west-1 %s --endpoint-url http://localstack:%d --no-verify-ssl", + command, + LocalStackContainer.PORT + ) + .split(" "); final Container.ExecResult execResult = awsCliInDockerNetwork.execInContainer(commandParts); Assert.assertEquals(0, execResult.getExitCode()); diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java index 2e28e5d0480..b0944a6d3c0 100644 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java @@ -27,10 +27,15 @@ public class MariaDBContainer> extends JdbcD static final String DEFAULT_PASSWORD = "test"; static final Integer MARIADB_PORT = 3306; + private String databaseName = "test"; + private String username = DEFAULT_USER; + private String password = DEFAULT_PASSWORD; + private static final String MARIADB_ROOT_USER = "root"; + private static final String MY_CNF_CONFIG_OVERRIDE_PARAM_NAME = "TC_MY_CNF"; /** @@ -47,7 +52,6 @@ public MariaDBContainer(String dockerImageName) { public MariaDBContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); addExposedPort(MARIADB_PORT); @@ -60,7 +64,11 @@ public Set getLivenessCheckPortNumbers() { @Override protected void configure() { - optionallyMapResourceParameterAsVolume(MY_CNF_CONFIG_OVERRIDE_PARAM_NAME, "/etc/mysql/conf.d", "mariadb-default-conf"); + optionallyMapResourceParameterAsVolume( + MY_CNF_CONFIG_OVERRIDE_PARAM_NAME, + "/etc/mysql/conf.d", + "mariadb-default-conf" + ); addEnv("MYSQL_DATABASE", databaseName); addEnv("MYSQL_USER", username); @@ -83,13 +91,14 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { String additionalUrlParams = constructUrlParameters("?", "&"); - return "jdbc:mariadb://" + getHost() + ":" + getMappedPort(MARIADB_PORT) + - "/" + databaseName + additionalUrlParams; + return ( + "jdbc:mariadb://" + getHost() + ":" + getMappedPort(MARIADB_PORT) + "/" + databaseName + additionalUrlParams + ); } @Override public String getDatabaseName() { - return databaseName; + return databaseName; } @Override diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java index 4330465a9c3..6697255ca79 100644 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java @@ -29,7 +29,6 @@ public JdbcDatabaseContainer newInstance(String tag) { @Override public JdbcDatabaseContainer newInstance(ConnectionUrl connectionUrl) { - return newInstanceFromConnectionUrl(connectionUrl, USER_PARAM, PASSWORD_PARAM); + return newInstanceFromConnectionUrl(connectionUrl, USER_PARAM, PASSWORD_PARAM); } - } diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainer.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainer.java index e37e9b0b6bf..99041e83e28 100644 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainer.java +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainer.java @@ -13,7 +13,8 @@ public class MariaDBR2DBCDatabaseContainer implements R2DBCDatabaseContainer { private final MariaDBContainer container; public static ConnectionFactoryOptions getOptions(MariaDBContainer container) { - ConnectionFactoryOptions options = ConnectionFactoryOptions.builder() + ConnectionFactoryOptions options = ConnectionFactoryOptions + .builder() .option(ConnectionFactoryOptions.DRIVER, MariaDBR2DBCDatabaseContainerProvider.DRIVER) .build(); @@ -22,7 +23,8 @@ public static ConnectionFactoryOptions getOptions(MariaDBContainer container) @Override public ConnectionFactoryOptions configure(ConnectionFactoryOptions options) { - return options.mutate() + return options + .mutate() .option(ConnectionFactoryOptions.HOST, container.getHost()) .option(ConnectionFactoryOptions.PORT, container.getMappedPort(MariaDBContainer.MARIADB_PORT)) .option(ConnectionFactoryOptions.DATABASE, container.getDatabaseName()) diff --git a/modules/mariadb/src/test/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainerTest.java b/modules/mariadb/src/test/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainerTest.java index b6fb4dd659e..f2339b0e5f9 100644 --- a/modules/mariadb/src/test/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainerTest.java +++ b/modules/mariadb/src/test/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainerTest.java @@ -20,5 +20,4 @@ protected String createR2DBCUrl() { protected MariaDBContainer createContainer() { return new MariaDBContainer<>(DockerImageName.parse("mariadb:10.3.6")); } - } diff --git a/modules/mariadb/src/test/java/org/testcontainers/jdbc/mariadb/MariaDBJDBCDriverTest.java b/modules/mariadb/src/test/java/org/testcontainers/jdbc/mariadb/MariaDBJDBCDriverTest.java index ef1dfef2703..ffb26f19734 100644 --- a/modules/mariadb/src/test/java/org/testcontainers/jdbc/mariadb/MariaDBJDBCDriverTest.java +++ b/modules/mariadb/src/test/java/org/testcontainers/jdbc/mariadb/MariaDBJDBCDriverTest.java @@ -4,26 +4,47 @@ import org.junit.runners.Parameterized; import org.testcontainers.jdbc.AbstractJDBCDriverTest; +import java.util.Arrays; import java.util.EnumSet; -import static java.util.Arrays.asList; - @RunWith(Parameterized.class) public class MariaDBJDBCDriverTest extends AbstractJDBCDriverTest { @Parameterized.Parameters(name = "{index} - {0}") public static Iterable data() { - return asList( - new Object[][]{ - {"jdbc:tc:mariadb://hostname/databasename", EnumSet.noneOf(Options.class)}, - {"jdbc:tc:mariadb://hostname/databasename?user=someuser&TC_INITSCRIPT=somepath/init_mariadb.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename", EnumSet.noneOf(Options.class)}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITSCRIPT=somepath/init_unicode_mariadb.sql&useUnicode=yes&characterEncoding=utf8", EnumSet.of(Options.CharacterSet)}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&TC_INITSCRIPT=somepath/init_mariadb.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&TC_INITFUNCTION=org.testcontainers.jdbc.AbstractJDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=somepath/init_mariadb.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&password=somepwd&TC_INITFUNCTION=org.testcontainers.jdbc.AbstractJDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_MY_CNF=somepath/mariadb_conf_override", EnumSet.of(Options.CustomIniFile)}, - }); + return Arrays.asList( + new Object[][] { + { "jdbc:tc:mariadb://hostname/databasename", EnumSet.noneOf(Options.class) }, + { + "jdbc:tc:mariadb://hostname/databasename?user=someuser&TC_INITSCRIPT=somepath/init_mariadb.sql", + EnumSet.of(Options.ScriptedSchema, Options.JDBCParams), + }, + { "jdbc:tc:mariadb:10.2.14://hostname/databasename", EnumSet.noneOf(Options.class) }, + { + "jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITSCRIPT=somepath/init_unicode_mariadb.sql&useUnicode=yes&characterEncoding=utf8", + EnumSet.of(Options.CharacterSet), + }, + { + "jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&TC_INITSCRIPT=somepath/init_mariadb.sql", + EnumSet.of(Options.ScriptedSchema, Options.JDBCParams), + }, + { + "jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&TC_INITFUNCTION=org.testcontainers.jdbc.AbstractJDBCDriverTest::sampleInitFunction", + EnumSet.of(Options.ScriptedSchema, Options.JDBCParams), + }, + { + "jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=somepath/init_mariadb.sql", + EnumSet.of(Options.ScriptedSchema, Options.JDBCParams), + }, + { + "jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&password=somepwd&TC_INITFUNCTION=org.testcontainers.jdbc.AbstractJDBCDriverTest::sampleInitFunction", + EnumSet.of(Options.ScriptedSchema, Options.JDBCParams), + }, + { + "jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_MY_CNF=somepath/mariadb_conf_override", + EnumSet.of(Options.CustomIniFile), + }, + } + ); } } diff --git a/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java b/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java index 2dcdbafd9e3..21a48b6e73c 100644 --- a/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java +++ b/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java @@ -2,26 +2,24 @@ import org.apache.commons.lang3.SystemUtils; import org.junit.Test; +import org.testcontainers.MariaDBTestImages; import org.testcontainers.containers.MariaDBContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; import java.sql.ResultSet; import java.sql.SQLException; -import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertThat; import static org.junit.Assume.assumeFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.MariaDBTestImages.MARIADB_IMAGE; - public class SimpleMariaDBTest extends AbstractContainerDatabaseTest { @Test public void testSimple() throws SQLException { - try (MariaDBContainer mariadb = new MariaDBContainer<>(MARIADB_IMAGE)) { - + try (MariaDBContainer mariadb = new MariaDBContainer<>(MariaDBTestImages.MARIADB_IMAGE)) { mariadb.start(); ResultSet resultSet = performQuery(mariadb, "SELECT 1"); @@ -33,14 +31,20 @@ public void testSimple() throws SQLException { @Test public void testSpecificVersion() throws SQLException { - try (MariaDBContainer mariadbOldVersion = new MariaDBContainer<>(MARIADB_IMAGE.withTag("5.5.51"))) { - + try ( + MariaDBContainer mariadbOldVersion = new MariaDBContainer<>( + MariaDBTestImages.MARIADB_IMAGE.withTag("5.5.51") + ) + ) { mariadbOldVersion.start(); ResultSet resultSet = performQuery(mariadbOldVersion, "SELECT VERSION()"); String resultSetString = resultSet.getString(1); - assertTrue("The database version can be set using a container rule parameter", resultSetString.startsWith("5.5.51")); + assertTrue( + "The database version can be set using a container rule parameter", + resultSetString.startsWith("5.5.51") + ); } } @@ -48,8 +52,12 @@ public void testSpecificVersion() throws SQLException { public void testMariaDBWithCustomIniFile() throws SQLException { assumeFalse(SystemUtils.IS_OS_WINDOWS); - try (MariaDBContainer mariadbCustomConfig = new MariaDBContainer<>(MARIADB_IMAGE.withTag("10.1.16")) - .withConfigurationOverride("somepath/mariadb_conf_override")) { + try ( + MariaDBContainer mariadbCustomConfig = new MariaDBContainer<>( + MariaDBTestImages.MARIADB_IMAGE.withTag("10.1.16") + ) + .withConfigurationOverride("somepath/mariadb_conf_override") + ) { mariadbCustomConfig.start(); ResultSet resultSet = performQuery(mariadbCustomConfig, "SELECT @@GLOBAL.innodb_file_format"); @@ -61,9 +69,10 @@ public void testMariaDBWithCustomIniFile() throws SQLException { @Test public void testMariaDBWithCommandOverride() throws SQLException { - - try (MariaDBContainer mariadbCustomConfig = new MariaDBContainer<>(MARIADB_IMAGE) - .withCommand("mysqld --auto_increment_increment=10")) { + try ( + MariaDBContainer mariadbCustomConfig = new MariaDBContainer<>(MariaDBTestImages.MARIADB_IMAGE) + .withCommand("mysqld --auto_increment_increment=10") + ) { mariadbCustomConfig.start(); ResultSet resultSet = performQuery(mariadbCustomConfig, "show variables like 'auto_increment_increment'"); String result = resultSet.getString("Value"); @@ -74,7 +83,7 @@ public void testMariaDBWithCommandOverride() throws SQLException { @Test public void testWithAdditionalUrlParamInJdbcUrl() { - MariaDBContainer mariaDBContainer = new MariaDBContainer<>(MARIADB_IMAGE) + MariaDBContainer mariaDBContainer = new MariaDBContainer<>(MariaDBTestImages.MARIADB_IMAGE) .withUrlParam("connectTimeout", "40000") .withUrlParam("rewriteBatchedStatements", "true"); diff --git a/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java b/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java index 6ab1c541325..8b39d5d3763 100644 --- a/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java +++ b/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java @@ -8,6 +8,7 @@ public class MockServerContainer extends GenericContainer { private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("jamesdbloom/mockserver"); + private static final String DEFAULT_TAG = "mockserver-5.5.4"; @Deprecated @@ -33,7 +34,6 @@ public MockServerContainer(String version) { public MockServerContainer(DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME, DockerImageName.parse("mockserver/mockserver")); waitingFor(Wait.forHttp("/mockserver/status").withMethod("PUT").forStatusCode(200)); diff --git a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java index d0c290ad46a..3f34d000603 100644 --- a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java +++ b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java @@ -5,22 +5,26 @@ import org.mockserver.client.MockServerClient; import org.testcontainers.utility.DockerImageName; -import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.Matchers.containsString; import static org.mockserver.model.HttpRequest.request; import static org.mockserver.model.HttpResponse.response; import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; public class MockServerContainerRuleTest { - public static final DockerImageName MOCKSERVER_IMAGE = DockerImageName.parse("jamesdbloom/mockserver:mockserver-5.5.4"); + public static final DockerImageName MOCKSERVER_IMAGE = DockerImageName.parse( + "jamesdbloom/mockserver:mockserver-5.5.4" + ); // creatingProxy { @Rule public MockServerContainer mockServer = new MockServerContainer(MOCKSERVER_IMAGE); + // } @Test public void shouldReturnExpectation() throws Exception { + // spotless:off // testSimpleExpectation { new MockServerClient(mockServer.getHost(), mockServer.getServerPort()) .when(request() @@ -31,8 +35,10 @@ public void shouldReturnExpectation() throws Exception { // ...a GET request to '/person?name=peter' returns "Peter the person!" // } + // spotless:on - assertThat("Expectation returns expected response body", + assertThat( + "Expectation returns expected response body", SimpleHttpClient.responseFromMockserver(mockServer, "/person?name=peter"), containsString("Peter the person") ); diff --git a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java index 61a5c33c630..d038b111824 100644 --- a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java +++ b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java @@ -4,7 +4,7 @@ import org.mockserver.client.MockServerClient; import org.testcontainers.utility.DockerImageName; -import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.Matchers.equalTo; import static org.mockserver.model.HttpRequest.request; import static org.mockserver.model.HttpResponse.response; import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; @@ -12,12 +12,18 @@ public class MockServerContainerTest { - public static final DockerImageName MOCKSERVER_IMAGE = DockerImageName.parse("jamesdbloom/mockserver:mockserver-5.5.4"); + public static final DockerImageName MOCKSERVER_IMAGE = DockerImageName.parse( + "jamesdbloom/mockserver:mockserver-5.5.4" + ); @Test public void shouldCallActualMockserverVersion() throws Exception { String actualVersion = MockServerClient.class.getPackage().getImplementationVersion(); - try (MockServerContainer mockServer = new MockServerContainer(MOCKSERVER_IMAGE.withTag("mockserver-" + actualVersion))) { + try ( + MockServerContainer mockServer = new MockServerContainer( + MOCKSERVER_IMAGE.withTag("mockserver-" + actualVersion) + ) + ) { mockServer.start(); String expectedBody = "Hello World!"; @@ -26,11 +32,10 @@ public void shouldCallActualMockserverVersion() throws Exception { assertTrue("Mockserver running", client.isRunning()); - client - .when(request().withPath("/hello")) - .respond(response().withBody(expectedBody)); + client.when(request().withPath("/hello")).respond(response().withBody(expectedBody)); - assertThat("MockServer returns correct result", + assertThat( + "MockServer returns correct result", SimpleHttpClient.responseFromMockserver(mockServer, "/hello"), equalTo(expectedBody) ); diff --git a/modules/mockserver/src/test/java/org/testcontainers/containers/SimpleHttpClient.java b/modules/mockserver/src/test/java/org/testcontainers/containers/SimpleHttpClient.java index cd5cb25cabb..da60ca9396a 100644 --- a/modules/mockserver/src/test/java/org/testcontainers/containers/SimpleHttpClient.java +++ b/modules/mockserver/src/test/java/org/testcontainers/containers/SimpleHttpClient.java @@ -9,9 +9,11 @@ import java.net.URLConnection; public class SimpleHttpClient { + public static String responseFromMockserver(MockServerContainer mockServer, String path) throws IOException { URLConnection urlConnection = new URL(mockServer.getEndpoint() + path).openConnection(); - @Cleanup BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); + @Cleanup + BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); return reader.readLine(); } } diff --git a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java index 87db80846a8..81bb59fd69d 100644 --- a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java @@ -18,10 +18,15 @@ public class MongoDBContainer extends GenericContainer { private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("mongo"); + private static final String DEFAULT_TAG = "4.0.10"; + private static final int CONTAINER_EXIT_CODE_OK = 0; + private static final int MONGODB_INTERNAL_PORT = 27017; + private static final int AWAIT_INIT_REPLICA_SET_ATTEMPTS = 60; + private static final String MONGODB_DATABASE_NAME_DEFAULT = "test"; /** @@ -38,14 +43,11 @@ public MongoDBContainer(@NonNull final String dockerImageName) { public MongoDBContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); withExposedPorts(MONGODB_INTERNAL_PORT); withCommand("--replSet", "docker-rs"); - waitingFor( - Wait.forLogMessage("(?i).*waiting for connections.*", 1) - ); + waitingFor(Wait.forLogMessage("(?i).*waiting for connections.*", 1)); } /** @@ -54,11 +56,7 @@ public MongoDBContainer(final DockerImageName dockerImageName) { * @return a connection url pointing to a mongodb instance */ public String getConnectionString() { - return String.format( - "mongodb://%s:%d", - getHost(), - getMappedPort(MONGODB_INTERNAL_PORT) - ); + return String.format("mongodb://%s:%d", getHost(), getMappedPort(MONGODB_INTERNAL_PORT)); } /** @@ -89,7 +87,7 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { } private String[] buildMongoEvalCommand(final String command) { - return new String[]{"mongo", "--eval", command}; + return new String[] { "mongo", "--eval", command }; } private void checkMongoNodeExitCode(final Container.ExecResult execResult) { @@ -103,21 +101,19 @@ private void checkMongoNodeExitCode(final Container.ExecResult execResult) { private String buildMongoWaitCommand() { return String.format( "var attempt = 0; " + - "while" + - "(%s) " + - "{ " + - "if (attempt > %d) {quit(1);} " + - "print('%s ' + attempt); sleep(100); attempt++; " + - " }", + "while" + + "(%s) " + + "{ " + + "if (attempt > %d) {quit(1);} " + + "print('%s ' + attempt); sleep(100); attempt++; " + + " }", "db.runCommand( { isMaster: 1 } ).ismaster==false", AWAIT_INIT_REPLICA_SET_ATTEMPTS, "An attempt to await for a single node replica set initialization:" ); } - private void checkMongoNodeExitCodeAfterWaiting( - final Container.ExecResult execResultWaitForMaster - ) { + private void checkMongoNodeExitCodeAfterWaiting(final Container.ExecResult execResultWaitForMaster) { if (execResultWaitForMaster.getExitCode() != CONTAINER_EXIT_CODE_OK) { final String errorMessage = String.format( "A single node replica set was not initialized in a set timeout: %d attempts", @@ -128,12 +124,10 @@ private void checkMongoNodeExitCodeAfterWaiting( } } - @SneakyThrows(value = {IOException.class, InterruptedException.class}) + @SneakyThrows(value = { IOException.class, InterruptedException.class }) private void initReplicaSet() { log.debug("Initializing a single node node replica set..."); - final ExecResult execResultInitRs = execInContainer( - buildMongoEvalCommand("rs.initiate();") - ); + final ExecResult execResultInitRs = execInContainer(buildMongoEvalCommand("rs.initiate();")); log.debug(execResultInitRs.getStdout()); checkMongoNodeExitCode(execResultInitRs); @@ -141,15 +135,14 @@ private void initReplicaSet() { "Awaiting for a single node replica set initialization up to {} attempts", AWAIT_INIT_REPLICA_SET_ATTEMPTS ); - final ExecResult execResultWaitForMaster = execInContainer( - buildMongoEvalCommand(buildMongoWaitCommand()) - ); + final ExecResult execResultWaitForMaster = execInContainer(buildMongoEvalCommand(buildMongoWaitCommand())); log.debug(execResultWaitForMaster.getStdout()); checkMongoNodeExitCodeAfterWaiting(execResultWaitForMaster); } public static class ReplicaSetInitializationException extends RuntimeException { + ReplicaSetInitializationException(final String errorMessage) { super(errorMessage); } diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java index 532a86208c1..6c98978f20a 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java @@ -13,12 +13,11 @@ import org.junit.Test; import org.testcontainers.utility.DockerImageName; -import static org.hamcrest.CoreMatchers.endsWith; +import static org.hamcrest.Matchers.endsWith; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; - public class MongoDBContainerTest { /** @@ -31,7 +30,6 @@ public void shouldExecuteTransactions() { final MongoDBContainer mongoDBContainer = new MongoDBContainer(DockerImageName.parse("mongo:4.0.10")) // } ) { - // startingMongoDBContainer { mongoDBContainer.start(); // } @@ -41,15 +39,25 @@ public void shouldExecuteTransactions() { final String connectionString = mongoDBContainer.getConnectionString(); final MongoClient mongoSyncClientBase = MongoClients.create(connectionString); final MongoClient mongoSyncClient = MongoClients.create(mongoRsUrl); - mongoSyncClient.getDatabase("mydb1").getCollection("foo") - .withWriteConcern(WriteConcern.MAJORITY).insertOne(new Document("abc", 0)); - mongoSyncClient.getDatabase("mydb2").getCollection("bar") - .withWriteConcern(WriteConcern.MAJORITY).insertOne(new Document("xyz", 0)); - mongoSyncClientBase.getDatabase("mydb3").getCollection("baz") - .withWriteConcern(WriteConcern.MAJORITY).insertOne(new Document("def", 0)); + mongoSyncClient + .getDatabase("mydb1") + .getCollection("foo") + .withWriteConcern(WriteConcern.MAJORITY) + .insertOne(new Document("abc", 0)); + mongoSyncClient + .getDatabase("mydb2") + .getCollection("bar") + .withWriteConcern(WriteConcern.MAJORITY) + .insertOne(new Document("xyz", 0)); + mongoSyncClientBase + .getDatabase("mydb3") + .getCollection("baz") + .withWriteConcern(WriteConcern.MAJORITY) + .insertOne(new Document("def", 0)); final ClientSession clientSession = mongoSyncClient.startSession(); - final TransactionOptions txnOptions = TransactionOptions.builder() + final TransactionOptions txnOptions = TransactionOptions + .builder() .readPreference(ReadPreference.primary()) .readConcern(ReadConcern.LOCAL) .writeConcern(WriteConcern.MAJORITY) @@ -58,10 +66,8 @@ public void shouldExecuteTransactions() { final String trxResult = "Inserted into collections in different databases"; TransactionBody txnBody = () -> { - final MongoCollection coll1 = - mongoSyncClient.getDatabase("mydb1").getCollection("foo"); - final MongoCollection coll2 = - mongoSyncClient.getDatabase("mydb2").getCollection("bar"); + final MongoCollection coll1 = mongoSyncClient.getDatabase("mydb1").getCollection("foo"); + final MongoCollection coll2 = mongoSyncClient.getDatabase("mydb2").getCollection("bar"); coll1.insertOne(clientSession, new Document("abc", 1)); coll2.insertOne(clientSession, new Document("xyz", 999)); @@ -82,18 +88,14 @@ public void shouldExecuteTransactions() { @Test public void supportsMongoDB_4_4() { - try ( - final MongoDBContainer mongoDBContainer = new MongoDBContainer(DockerImageName.parse("mongo:4.4")) - ) { + try (final MongoDBContainer mongoDBContainer = new MongoDBContainer(DockerImageName.parse("mongo:4.4"))) { mongoDBContainer.start(); } } @Test public void shouldTestDatabaseName() { - try ( - final MongoDBContainer mongoDBContainer = new MongoDBContainer(DockerImageName.parse("mongo:4.0.10")) - ) { + try (final MongoDBContainer mongoDBContainer = new MongoDBContainer(DockerImageName.parse("mongo:4.0.10"))) { mongoDBContainer.start(); final String databaseName = "my-db"; assertThat(mongoDBContainer.getReplicaSetUrl(databaseName), endsWith(databaseName)); diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainer.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainer.java index 59c3ed0de68..6a58b3488ff 100644 --- a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainer.java +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainer.java @@ -13,7 +13,8 @@ public class MSSQLR2DBCDatabaseContainer implements R2DBCDatabaseContainer { private final MSSQLServerContainer container; public static ConnectionFactoryOptions getOptions(MSSQLServerContainer container) { - ConnectionFactoryOptions options = ConnectionFactoryOptions.builder() + ConnectionFactoryOptions options = ConnectionFactoryOptions + .builder() .option(ConnectionFactoryOptions.DRIVER, MSSQLR2DBCDatabaseContainerProvider.DRIVER) .build(); @@ -22,7 +23,8 @@ public static ConnectionFactoryOptions getOptions(MSSQLServerContainer contai @Override public ConnectionFactoryOptions configure(ConnectionFactoryOptions options) { - return options.mutate() + return options + .mutate() .option(ConnectionFactoryOptions.HOST, container.getHost()) .option(ConnectionFactoryOptions.PORT, container.getMappedPort(MSSQLServerContainer.MS_SQL_SERVER_PORT)) // TODO enable if/when MSSQLServerContainer adds support for customizing the DB name diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java index fe6270d04c7..532316fb967 100644 --- a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java @@ -14,6 +14,7 @@ public class MSSQLServerContainer> extends JdbcDatabaseContainer { private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("mcr.microsoft.com/mssql/server"); + @Deprecated public static final String DEFAULT_TAG = "2017-CU12"; @@ -30,13 +31,14 @@ public class MSSQLServerContainer> exten private String password = DEFAULT_PASSWORD; private static final int DEFAULT_STARTUP_TIMEOUT_SECONDS = 240; + private static final int DEFAULT_CONNECT_TIMEOUT_SECONDS = 240; - private static final Pattern[] PASSWORD_CATEGORY_VALIDATION_PATTERNS = new Pattern[]{ + private static final Pattern[] PASSWORD_CATEGORY_VALIDATION_PATTERNS = new Pattern[] { Pattern.compile("[A-Z]+"), Pattern.compile("[a-z]+"), Pattern.compile("[0-9]+"), - Pattern.compile("[^a-zA-Z0-9]+", Pattern.CASE_INSENSITIVE) + Pattern.compile("[^a-zA-Z0-9]+", Pattern.CASE_INSENSITIVE), }; /** @@ -53,7 +55,6 @@ public MSSQLServerContainer(final String dockerImageName) { public MSSQLServerContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); withStartupTimeoutSeconds(DEFAULT_STARTUP_TIMEOUT_SECONDS); @@ -131,7 +132,6 @@ public SELF withPassword(final String password) { } private void checkPasswordStrength(String password) { - if (password == null) { throw new IllegalArgumentException("Null password is not allowed"); } @@ -144,18 +144,19 @@ private void checkPasswordStrength(String password) { throw new IllegalArgumentException("Password can be up to 128 characters long"); } - long satisfiedCategories = Stream.of(PASSWORD_CATEGORY_VALIDATION_PATTERNS) + long satisfiedCategories = Stream + .of(PASSWORD_CATEGORY_VALIDATION_PATTERNS) .filter(p -> p.matcher(password).find()) .count(); if (satisfiedCategories < 3) { throw new IllegalArgumentException( "Password must contain characters from three of the following four categories:\n" + - " - Latin uppercase letters (A through Z)\n" + - " - Latin lowercase letters (a through z)\n" + - " - Base 10 digits (0 through 9)\n" + - " - Non-alphanumeric characters such as: exclamation point (!), dollar sign ($), number sign (#), " + - "or percent (%)." + " - Latin uppercase letters (A through Z)\n" + + " - Latin lowercase letters (a through z)\n" + + " - Base 10 digits (0 through 9)\n" + + " - Non-alphanumeric characters such as: exclamation point (!), dollar sign ($), number sign (#), " + + "or percent (%)." ); } } diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainerProvider.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainerProvider.java index c3495c8a3a6..27e8ab75225 100644 --- a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainerProvider.java +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainerProvider.java @@ -6,6 +6,7 @@ * Factory for MS SQL Server containers. */ public class MSSQLServerContainerProvider extends JdbcDatabaseContainerProvider { + @Override public boolean supports(String databaseType) { return databaseType.equals(MSSQLServerContainer.NAME); diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/jdbc/mssqlserver/MSSQLServerJDBCDriverTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/jdbc/mssqlserver/MSSQLServerJDBCDriverTest.java index 00e22199b46..bdb623136fb 100644 --- a/modules/mssqlserver/src/test/java/org/testcontainers/jdbc/mssqlserver/MSSQLServerJDBCDriverTest.java +++ b/modules/mssqlserver/src/test/java/org/testcontainers/jdbc/mssqlserver/MSSQLServerJDBCDriverTest.java @@ -4,18 +4,21 @@ import org.junit.runners.Parameterized; import org.testcontainers.jdbc.AbstractJDBCDriverTest; +import java.util.Arrays; import java.util.EnumSet; -import static java.util.Arrays.asList; - @RunWith(Parameterized.class) public class MSSQLServerJDBCDriverTest extends AbstractJDBCDriverTest { @Parameterized.Parameters(name = "{index} - {0}") public static Iterable data() { - return asList( - new Object[][]{ - {"jdbc:tc:sqlserver:2017-CU12://hostname:hostport;databaseName=databasename", EnumSet.noneOf(Options.class)}, - }); + return Arrays.asList( + new Object[][] { + { + "jdbc:tc:sqlserver:2017-CU12://hostname:hostport;databaseName=databasename", + EnumSet.noneOf(Options.class), + }, + } + ); } } diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java index 6ebd3624577..49bae13d9e3 100644 --- a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java +++ b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java @@ -22,11 +22,15 @@ public class CustomPasswordMSSQLServerTest { private static String UPPER_CASE_LETTERS = "ABCDE"; + private static String LOWER_CASE_LETTERS = "abcde"; + private static String NUMBERS = "12345"; + private static String SPECIAL_CHARS = "_(!)_"; private String password; + private Boolean valid; public CustomPasswordMSSQLServerTest(String password, Boolean valid) { @@ -36,45 +40,41 @@ public CustomPasswordMSSQLServerTest(String password, Boolean valid) { @Parameterized.Parameters public static Collection data() { - return Arrays.asList(new Object[][]{ - new Object[]{null, false}, - // too short - {"abc123", false}, - - // too long - {RandomStringUtils.randomAlphabetic(129), false}, - - // only 2 categories - {UPPER_CASE_LETTERS + NUMBERS, false}, - {UPPER_CASE_LETTERS + SPECIAL_CHARS, false}, - {LOWER_CASE_LETTERS + NUMBERS, false}, - {LOWER_CASE_LETTERS + SPECIAL_CHARS, false}, - {NUMBERS + SPECIAL_CHARS, false}, - - // 3 categories - {UPPER_CASE_LETTERS + LOWER_CASE_LETTERS + NUMBERS, true}, - {UPPER_CASE_LETTERS + LOWER_CASE_LETTERS + SPECIAL_CHARS, true}, - {UPPER_CASE_LETTERS + NUMBERS + SPECIAL_CHARS, true}, - {LOWER_CASE_LETTERS + NUMBERS + SPECIAL_CHARS, true}, - - // 4 categories - {UPPER_CASE_LETTERS + LOWER_CASE_LETTERS + NUMBERS + SPECIAL_CHARS, true}, - - - }); + return Arrays.asList( + new Object[][] { + new Object[] { null, false }, + // too short + { "abc123", false }, + // too long + { RandomStringUtils.randomAlphabetic(129), false }, + // only 2 categories + { UPPER_CASE_LETTERS + NUMBERS, false }, + { UPPER_CASE_LETTERS + SPECIAL_CHARS, false }, + { LOWER_CASE_LETTERS + NUMBERS, false }, + { LOWER_CASE_LETTERS + SPECIAL_CHARS, false }, + { NUMBERS + SPECIAL_CHARS, false }, + // 3 categories + { UPPER_CASE_LETTERS + LOWER_CASE_LETTERS + NUMBERS, true }, + { UPPER_CASE_LETTERS + LOWER_CASE_LETTERS + SPECIAL_CHARS, true }, + { UPPER_CASE_LETTERS + NUMBERS + SPECIAL_CHARS, true }, + { LOWER_CASE_LETTERS + NUMBERS + SPECIAL_CHARS, true }, + // 4 categories + { UPPER_CASE_LETTERS + LOWER_CASE_LETTERS + NUMBERS + SPECIAL_CHARS, true }, + } + ); } @Test public void runPasswordTests() { try { new MSSQLServerContainer<>(MSSQLServerTestImages.MSSQL_SERVER_IMAGE).withPassword(this.password); - if (!valid) + if (!valid) { fail("Password " + this.password + " is not valid. Expected exception"); + } } catch (IllegalArgumentException e) { - if (valid) + if (valid) { fail("Password " + this.password + " should have been validated"); + } } } - - } diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomizableMSSQLServerTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomizableMSSQLServerTest.java index d3c387e7b4a..21ea5380801 100644 --- a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomizableMSSQLServerTest.java +++ b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomizableMSSQLServerTest.java @@ -16,9 +16,12 @@ public class CustomizableMSSQLServerTest extends AbstractContainerDatabaseTest { @Test public void testSqlServerConnection() throws SQLException { - try (MSSQLServerContainer mssqlServerContainer = new MSSQLServerContainer<>(DockerImageName.parse("mcr.microsoft.com/mssql/server:2017-CU12")) - .withPassword(STRONG_PASSWORD)) { - + try ( + MSSQLServerContainer mssqlServerContainer = new MSSQLServerContainer<>( + DockerImageName.parse("mcr.microsoft.com/mssql/server:2017-CU12") + ) + .withPassword(STRONG_PASSWORD) + ) { mssqlServerContainer.start(); ResultSet resultSet = performQuery(mssqlServerContainer, mssqlServerContainer.getTestQueryString()); diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java index 1530760b673..36d215c8757 100644 --- a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java +++ b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java @@ -1,24 +1,27 @@ package org.testcontainers.junit.mssqlserver; import org.junit.Test; +import org.testcontainers.MSSQLServerTestImages; import org.testcontainers.containers.MSSQLServerContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; -import javax.sql.DataSource; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import static org.hamcrest.CoreMatchers.containsString; +import javax.sql.DataSource; + import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.testcontainers.MSSQLServerTestImages.MSSQL_SERVER_IMAGE; public class SimpleMSSQLServerTest extends AbstractContainerDatabaseTest { @Test public void testSimple() throws SQLException { - try (MSSQLServerContainer mssqlServer = new MSSQLServerContainer<>(MSSQL_SERVER_IMAGE)) { + try ( + MSSQLServerContainer mssqlServer = new MSSQLServerContainer<>(MSSQLServerTestImages.MSSQL_SERVER_IMAGE) + ) { mssqlServer.start(); ResultSet resultSet = performQuery(mssqlServer, "SELECT 1"); @@ -29,10 +32,11 @@ public void testSimple() throws SQLException { @Test public void testWithAdditionalUrlParamInJdbcUrl() { - try (MSSQLServerContainer mssqlServer = new MSSQLServerContainer<>(MSSQL_SERVER_IMAGE) - .withUrlParam("integratedSecurity", "false") - .withUrlParam("applicationName", "MyApp")) { - + try ( + MSSQLServerContainer mssqlServer = new MSSQLServerContainer<>(MSSQLServerTestImages.MSSQL_SERVER_IMAGE) + .withUrlParam("integratedSecurity", "false") + .withUrlParam("applicationName", "MyApp") + ) { mssqlServer.start(); String jdbcUrl = mssqlServer.getJdbcUrl(); @@ -40,10 +44,11 @@ public void testWithAdditionalUrlParamInJdbcUrl() { } } - @Test public void testSetupDatabase() throws SQLException { - try (MSSQLServerContainer mssqlServer = new MSSQLServerContainer<>(MSSQL_SERVER_IMAGE)) { + try ( + MSSQLServerContainer mssqlServer = new MSSQLServerContainer<>(MSSQLServerTestImages.MSSQL_SERVER_IMAGE) + ) { mssqlServer.start(); DataSource ds = getDataSource(mssqlServer); Statement statement = ds.getConnection().createStatement(); diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java index 20be08c2aec..14206018954 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java @@ -26,10 +26,15 @@ public class MySQLContainer> extends JdbcDatab static final String DEFAULT_PASSWORD = "test"; private static final String MY_CNF_CONFIG_OVERRIDE_PARAM_NAME = "TC_MY_CNF"; + public static final Integer MYSQL_PORT = 3306; + private String databaseName = "test"; + private String username = DEFAULT_USER; + private String password = DEFAULT_PASSWORD; + private static final String MYSQL_ROOT_USER = "root"; /** @@ -46,13 +51,11 @@ public MySQLContainer(String dockerImageName) { public MySQLContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); addExposedPort(MYSQL_PORT); } - @NotNull @Override protected Set getLivenessCheckPorts() { @@ -61,8 +64,11 @@ protected Set getLivenessCheckPorts() { @Override protected void configure() { - optionallyMapResourceParameterAsVolume(MY_CNF_CONFIG_OVERRIDE_PARAM_NAME, "/etc/mysql/conf.d", - "mysql-default-conf"); + optionallyMapResourceParameterAsVolume( + MY_CNF_CONFIG_OVERRIDE_PARAM_NAME, + "/etc/mysql/conf.d", + "mysql-default-conf" + ); addEnv("MYSQL_DATABASE", databaseName); if (!MYSQL_ROOT_USER.equalsIgnoreCase(username)) { @@ -92,8 +98,7 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { String additionalUrlParams = constructUrlParameters("?", "&"); - return "jdbc:mysql://" + getHost() + ":" + getMappedPort(MYSQL_PORT) + - "/" + databaseName + additionalUrlParams; + return "jdbc:mysql://" + getHost() + ":" + getMappedPort(MYSQL_PORT) + "/" + databaseName + additionalUrlParams; } @Override diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainerProvider.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainerProvider.java index 18d126635b1..621001d7b57 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainerProvider.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainerProvider.java @@ -35,5 +35,4 @@ public JdbcDatabaseContainer newInstance(String tag) { public JdbcDatabaseContainer newInstance(ConnectionUrl connectionUrl) { return newInstanceFromConnectionUrl(connectionUrl, USER_PARAM, PASSWORD_PARAM); } - } diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainer.java index 274224352a8..4bbb542fec8 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainer.java @@ -13,7 +13,8 @@ public class MySQLR2DBCDatabaseContainer implements R2DBCDatabaseContainer { private final MySQLContainer container; public static ConnectionFactoryOptions getOptions(MySQLContainer container) { - ConnectionFactoryOptions options = ConnectionFactoryOptions.builder() + ConnectionFactoryOptions options = ConnectionFactoryOptions + .builder() .option(ConnectionFactoryOptions.DRIVER, MySQLR2DBCDatabaseContainerProvider.DRIVER) .build(); @@ -22,7 +23,8 @@ public static ConnectionFactoryOptions getOptions(MySQLContainer container) { @Override public ConnectionFactoryOptions configure(ConnectionFactoryOptions options) { - return options.mutate() + return options + .mutate() .option(ConnectionFactoryOptions.HOST, container.getHost()) .option(ConnectionFactoryOptions.PORT, container.getMappedPort(MySQLContainer.MYSQL_PORT)) .option(ConnectionFactoryOptions.DATABASE, container.getDatabaseName()) diff --git a/modules/mysql/src/test/java/org/testcontainers/MySQLTestImages.java b/modules/mysql/src/test/java/org/testcontainers/MySQLTestImages.java index 833dc1448cf..81de83be0b9 100644 --- a/modules/mysql/src/test/java/org/testcontainers/MySQLTestImages.java +++ b/modules/mysql/src/test/java/org/testcontainers/MySQLTestImages.java @@ -3,7 +3,10 @@ import org.testcontainers.utility.DockerImageName; public class MySQLTestImages { + public static final DockerImageName MYSQL_56_IMAGE = DockerImageName.parse("mysql:5.6.51"); + public static final DockerImageName MYSQL_57_IMAGE = DockerImageName.parse("mysql:5.7.34"); + public static final DockerImageName MYSQL_80_IMAGE = DockerImageName.parse("mysql:8.0.24"); } diff --git a/modules/mysql/src/test/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerTest.java b/modules/mysql/src/test/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerTest.java index 334fabb0a49..42a852ad1f1 100644 --- a/modules/mysql/src/test/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerTest.java @@ -20,5 +20,4 @@ protected String createR2DBCUrl() { protected MySQLContainer createContainer() { return new MySQLContainer<>(MySQLTestImages.MYSQL_80_IMAGE); } - } diff --git a/modules/mysql/src/test/java/org/testcontainers/containers/MySQLRootAccountTest.java b/modules/mysql/src/test/java/org/testcontainers/containers/MySQLRootAccountTest.java index 86b13a0d1bf..0320eee9255 100644 --- a/modules/mysql/src/test/java/org/testcontainers/containers/MySQLRootAccountTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/containers/MySQLRootAccountTest.java @@ -18,13 +18,10 @@ public class MySQLRootAccountTest { @Parameterized.Parameters(name = "{0}") public static DockerImageName[] params() { - return new DockerImageName[]{ - MySQLTestImages.MYSQL_80_IMAGE, - MySQLTestImages.MYSQL_57_IMAGE - }; + return new DockerImageName[] { MySQLTestImages.MYSQL_80_IMAGE, MySQLTestImages.MYSQL_57_IMAGE }; } - @Parameterized.Parameter() + @Parameterized.Parameter public DockerImageName image; @Test diff --git a/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/JDBCDriverWithPoolTest.java b/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/JDBCDriverWithPoolTest.java index 3b9f9842095..a30f3f59888 100644 --- a/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/JDBCDriverWithPoolTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/JDBCDriverWithPoolTest.java @@ -11,15 +11,16 @@ import org.testcontainers.jdbc.ContainerDatabaseDriver; import org.vibur.dbcp.ViburDBCPDataSource; -import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; +import java.util.Arrays; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; -import static java.util.Arrays.asList; +import javax.sql.DataSource; + import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; /** @@ -31,12 +32,14 @@ @RunWith(Parameterized.class) public class JDBCDriverWithPoolTest { - public static final String URL = "jdbc:tc:mysql:5.7.34://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.mysql.JDBCDriverWithPoolTest::sampleInitFunction"; + public static final String URL = + "jdbc:tc:mysql:5.7.34://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.mysql.JDBCDriverWithPoolTest::sampleInitFunction"; + private final DataSource dataSource; @Parameterized.Parameters public static Iterable> dataSourceSuppliers() { - return asList( + return Arrays.asList( JDBCDriverWithPoolTest::getTomcatDataSourceWithDriverClassName, JDBCDriverWithPoolTest::getTomcatDataSource, JDBCDriverWithPoolTest::getHikariDataSourceWithDriverClassName, @@ -54,13 +57,12 @@ public JDBCDriverWithPoolTest(Supplier dataSourceSupplier) { @Test public void testMySQLWithConnectionPoolUsingSameContainer() throws SQLException, InterruptedException { - // Populate the database with some data in multiple threads, so that multiple connections from the pool will be used for (int i = 0; i < 100; i++) { executorService.submit(() -> { try { - new QueryRunner(dataSource).insert("INSERT INTO my_counter (n) VALUES (5)", - (ResultSetHandler) rs -> true); + new QueryRunner(dataSource) + .insert("INSERT INTO my_counter (n) VALUES (5)", (ResultSetHandler) rs -> true); } catch (SQLException e) { e.printStackTrace(); } @@ -72,22 +74,28 @@ public void testMySQLWithConnectionPoolUsingSameContainer() throws SQLException, executorService.awaitTermination(5, TimeUnit.MINUTES); // compare to expected results - int count = new QueryRunner(dataSource).query("SELECT COUNT(1) FROM my_counter", rs -> { - rs.next(); - return rs.getInt(1); - }); + int count = new QueryRunner(dataSource) + .query( + "SELECT COUNT(1) FROM my_counter", + rs -> { + rs.next(); + return rs.getInt(1); + } + ); assertEquals("Reuse of a datasource points to the same DB container", 100, count); - - int sum = new QueryRunner(dataSource).query("SELECT SUM(n) FROM my_counter", rs -> { - rs.next(); - return rs.getInt(1); - }); + int sum = new QueryRunner(dataSource) + .query( + "SELECT SUM(n) FROM my_counter", + rs -> { + rs.next(); + return rs.getInt(1); + } + ); // 100 records * 5 = 500 expected assertEquals("Reuse of a datasource points to the same DB container", 500, sum); } - private static DataSource getTomcatDataSourceWithDriverClassName() { PoolProperties poolProperties = new PoolProperties(); poolProperties.setUrl(URL + ";TEST=TOMCAT_WITH_CLASSNAME"); // append a dummy URL element to ensure different DB per test @@ -134,7 +142,7 @@ private static DataSource getViburDataSourceWithDriverClassName() { ViburDBCPDataSource ds = new ViburDBCPDataSource(); ds.setJdbcUrl(URL + ";TEST=VIBUR_WITH_CLASSNAME"); - ds.setUsername("any"); // Recent versions of Vibur require a username, even though it will not be used + ds.setUsername("any"); // Recent versions of Vibur require a username, even though it will not be used ds.setPassword(""); ds.setPoolInitialSize(3); ds.setPoolMaxSize(10); @@ -149,7 +157,7 @@ private static DataSource getViburDataSourceWithDriverClassName() { private static DataSource getViburDataSource() { ViburDBCPDataSource ds = new ViburDBCPDataSource(); ds.setJdbcUrl(URL + ";TEST=VIBUR"); - ds.setUsername("any"); // Recent versions of Vibur require a username, even though it will not be used + ds.setUsername("any"); // Recent versions of Vibur require a username, even though it will not be used ds.setPassword(""); ds.setPoolInitialSize(3); ds.setPoolMaxSize(10); @@ -162,12 +170,8 @@ private static DataSource getViburDataSource() { @SuppressWarnings("SqlNoDataSourceInspection") public static void sampleInitFunction(Connection connection) throws SQLException { - connection.createStatement().execute("CREATE TABLE bar (\n" + - " foo VARCHAR(255)\n" + - ");"); + connection.createStatement().execute("CREATE TABLE bar (\n" + " foo VARCHAR(255)\n" + ");"); connection.createStatement().execute("INSERT INTO bar (foo) VALUES ('hello world');"); - connection.createStatement().execute("CREATE TABLE my_counter (\n" + - " n INT\n" + - ");"); + connection.createStatement().execute("CREATE TABLE my_counter (\n" + " n INT\n" + ");"); } } diff --git a/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/MySQLJDBCDriverTest.java b/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/MySQLJDBCDriverTest.java index 302621879eb..752fbca8e07 100644 --- a/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/MySQLJDBCDriverTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/MySQLJDBCDriverTest.java @@ -4,27 +4,48 @@ import org.junit.runners.Parameterized; import org.testcontainers.jdbc.AbstractJDBCDriverTest; +import java.util.Arrays; import java.util.EnumSet; -import static java.util.Arrays.asList; - @RunWith(Parameterized.class) public class MySQLJDBCDriverTest extends AbstractJDBCDriverTest { @Parameterized.Parameters(name = "{index} - {0}") public static Iterable data() { - return asList( - new Object[][]{ - {"jdbc:tc:mysql://hostname/databasename", EnumSet.noneOf(Options.class)}, - {"jdbc:tc:mysql://hostname/databasename?user=someuser&TC_INITSCRIPT=somepath/init_mysql.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mysql:5.7.34://hostname/databasename?user=someuser&TC_INITFUNCTION=org.testcontainers.jdbc.AbstractJDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mysql:5.7.34://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=somepath/init_mysql.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mysql:5.7.34://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=file:sql/init_mysql.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mysql:5.7.34://hostname/databasename?user=someuser&password=somepwd&TC_INITFUNCTION=org.testcontainers.jdbc.AbstractJDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mysql:5.7.34://hostname/databasename?TC_INITSCRIPT=somepath/init_unicode_mysql.sql&useUnicode=yes&characterEncoding=utf8", EnumSet.of(Options.CharacterSet)}, - {"jdbc:tc:mysql:5.7.34://hostname/databasename", EnumSet.noneOf(Options.class)}, - {"jdbc:tc:mysql:5.7.34://hostname/databasename?useSSL=false", EnumSet.noneOf(Options.class)}, - {"jdbc:tc:mysql:5.6.51://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override", EnumSet.of(Options.CustomIniFile)}, - }); + return Arrays.asList( + new Object[][] { + { "jdbc:tc:mysql://hostname/databasename", EnumSet.noneOf(Options.class) }, + { + "jdbc:tc:mysql://hostname/databasename?user=someuser&TC_INITSCRIPT=somepath/init_mysql.sql", + EnumSet.of(Options.ScriptedSchema, Options.JDBCParams), + }, + { + "jdbc:tc:mysql:5.7.34://hostname/databasename?user=someuser&TC_INITFUNCTION=org.testcontainers.jdbc.AbstractJDBCDriverTest::sampleInitFunction", + EnumSet.of(Options.ScriptedSchema, Options.JDBCParams), + }, + { + "jdbc:tc:mysql:5.7.34://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=somepath/init_mysql.sql", + EnumSet.of(Options.ScriptedSchema, Options.JDBCParams), + }, + { + "jdbc:tc:mysql:5.7.34://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=file:sql/init_mysql.sql", + EnumSet.of(Options.ScriptedSchema, Options.JDBCParams), + }, + { + "jdbc:tc:mysql:5.7.34://hostname/databasename?user=someuser&password=somepwd&TC_INITFUNCTION=org.testcontainers.jdbc.AbstractJDBCDriverTest::sampleInitFunction", + EnumSet.of(Options.ScriptedSchema, Options.JDBCParams), + }, + { + "jdbc:tc:mysql:5.7.34://hostname/databasename?TC_INITSCRIPT=somepath/init_unicode_mysql.sql&useUnicode=yes&characterEncoding=utf8", + EnumSet.of(Options.CharacterSet), + }, + { "jdbc:tc:mysql:5.7.34://hostname/databasename", EnumSet.noneOf(Options.class) }, + { "jdbc:tc:mysql:5.7.34://hostname/databasename?useSSL=false", EnumSet.noneOf(Options.class) }, + { + "jdbc:tc:mysql:5.6.51://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override", + EnumSet.of(Options.CustomIniFile), + }, + } + ); } } diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java index 0b72ce083d2..20b49d19201 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java @@ -11,19 +11,23 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; public class CustomizableMysqlTest extends AbstractContainerDatabaseTest { + private static final String DB_NAME = "foo"; + private static final String USER = "bar"; + private static final String PWD = "baz"; @Test public void testSimple() throws SQLException { // Add MYSQL_ROOT_HOST environment so that we can root login from anywhere for testing purposes - try (MySQLContainer mysql = new MySQLContainer<>(MySQLTestImages.MYSQL_57_IMAGE) - .withDatabaseName(DB_NAME) - .withUsername(USER) - .withPassword(PWD) - .withEnv("MYSQL_ROOT_HOST", "%")) { - + try ( + MySQLContainer mysql = new MySQLContainer<>(MySQLTestImages.MYSQL_57_IMAGE) + .withDatabaseName(DB_NAME) + .withUsername(USER) + .withPassword(PWD) + .withEnv("MYSQL_ROOT_HOST", "%") + ) { mysql.start(); ResultSet resultSet = performQuery(mysql, "SELECT 1"); diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java index 32dee3cd056..735af251236 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java @@ -3,6 +3,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import org.testcontainers.MySQLTestImages; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; import org.testcontainers.utility.DockerImageName; @@ -11,23 +12,20 @@ import java.sql.SQLException; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.testcontainers.MySQLTestImages.MYSQL_56_IMAGE; -import static org.testcontainers.MySQLTestImages.MYSQL_57_IMAGE; -import static org.testcontainers.MySQLTestImages.MYSQL_80_IMAGE; @RunWith(Parameterized.class) public class MultiVersionMySQLTest extends AbstractContainerDatabaseTest { @Parameterized.Parameters(name = "{0}") public static DockerImageName[] params() { - return new DockerImageName[]{ - MYSQL_56_IMAGE, - MYSQL_57_IMAGE, - MYSQL_80_IMAGE + return new DockerImageName[] { + MySQLTestImages.MYSQL_56_IMAGE, + MySQLTestImages.MYSQL_57_IMAGE, + MySQLTestImages.MYSQL_80_IMAGE, }; } - @Parameterized.Parameter() + @Parameterized.Parameter public DockerImageName dockerImageName; @Test @@ -37,7 +35,11 @@ public void versionCheckTest() throws SQLException { final ResultSet resultSet = performQuery(mysql, "SELECT VERSION()"); final String resultSetString = resultSet.getString(1); - assertEquals("The database version can be set using a container rule parameter", dockerImageName.getVersionPart(), resultSetString); + assertEquals( + "The database version can be set using a container rule parameter", + dockerImageName.getVersionPart(), + resultSetString + ); } } } diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java index 90973bc4b56..6441724c443 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java @@ -4,6 +4,7 @@ import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.testcontainers.MySQLTestImages; import org.testcontainers.containers.ContainerLaunchException; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.containers.output.Slf4jLogConsumer; @@ -18,15 +19,12 @@ import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; -import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertThat; import static org.junit.Assume.assumeFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; import static org.rnorth.visibleassertions.VisibleAssertions.fail; -import static org.testcontainers.MySQLTestImages.MYSQL_56_IMAGE; -import static org.testcontainers.MySQLTestImages.MYSQL_57_IMAGE; - public class SimpleMySQLTest extends AbstractContainerDatabaseTest { @@ -51,10 +49,11 @@ public class SimpleMySQLTest extends AbstractContainerDatabaseTest { @Test public void testSimple() throws SQLException { - try (MySQLContainer mysql = new MySQLContainer<>(MYSQL_57_IMAGE) - .withConfigurationOverride("somepath/mysql_conf_override") - .withLogConsumer(new Slf4jLogConsumer(logger))) { - + try ( + MySQLContainer mysql = new MySQLContainer<>(MySQLTestImages.MYSQL_57_IMAGE) + .withConfigurationOverride("somepath/mysql_conf_override") + .withLogConsumer(new Slf4jLogConsumer(logger)) + ) { mysql.start(); ResultSet resultSet = performQuery(mysql, "SELECT 1"); @@ -66,16 +65,20 @@ public void testSimple() throws SQLException { @Test public void testSpecificVersion() throws SQLException { - try (MySQLContainer mysqlOldVersion = new MySQLContainer<>(MYSQL_56_IMAGE) - .withConfigurationOverride("somepath/mysql_conf_override") - .withLogConsumer(new Slf4jLogConsumer(logger))) { - + try ( + MySQLContainer mysqlOldVersion = new MySQLContainer<>(MySQLTestImages.MYSQL_56_IMAGE) + .withConfigurationOverride("somepath/mysql_conf_override") + .withLogConsumer(new Slf4jLogConsumer(logger)) + ) { mysqlOldVersion.start(); ResultSet resultSet = performQuery(mysqlOldVersion, "SELECT VERSION()"); String resultSetString = resultSet.getString(1); - assertTrue("The database version can be set using a container rule parameter", resultSetString.startsWith("5.6")); + assertTrue( + "The database version can be set using a container rule parameter", + resultSetString.startsWith("5.6") + ); } } @@ -83,9 +86,10 @@ public void testSpecificVersion() throws SQLException { public void testMySQLWithCustomIniFile() throws SQLException { assumeFalse(SystemUtils.IS_OS_WINDOWS); - try (MySQLContainer mysqlCustomConfig = new MySQLContainer<>(MYSQL_56_IMAGE) - .withConfigurationOverride("somepath/mysql_conf_override")) { - + try ( + MySQLContainer mysqlCustomConfig = new MySQLContainer<>(MySQLTestImages.MYSQL_56_IMAGE) + .withConfigurationOverride("somepath/mysql_conf_override") + ) { mysqlCustomConfig.start(); ResultSet resultSet = performQuery(mysqlCustomConfig, "SELECT @@GLOBAL.innodb_file_format"); @@ -97,9 +101,10 @@ public void testMySQLWithCustomIniFile() throws SQLException { @Test public void testCommandOverride() throws SQLException { - try (MySQLContainer mysqlCustomConfig = new MySQLContainer<>(MYSQL_57_IMAGE) - .withCommand("mysqld --auto_increment_increment=42")) { - + try ( + MySQLContainer mysqlCustomConfig = new MySQLContainer<>(MySQLTestImages.MYSQL_57_IMAGE) + .withCommand("mysqld --auto_increment_increment=42") + ) { mysqlCustomConfig.start(); ResultSet resultSet = performQuery(mysqlCustomConfig, "show variables like 'auto_increment_increment'"); @@ -111,9 +116,11 @@ public void testCommandOverride() throws SQLException { @Test public void testExplicitInitScript() throws SQLException { - try (MySQLContainer container = new MySQLContainer<>(MYSQL_57_IMAGE) - .withInitScript("somepath/init_mysql.sql") - .withLogConsumer(new Slf4jLogConsumer(logger))) { + try ( + MySQLContainer container = new MySQLContainer<>(MySQLTestImages.MYSQL_57_IMAGE) + .withInitScript("somepath/init_mysql.sql") + .withLogConsumer(new Slf4jLogConsumer(logger)) + ) { container.start(); ResultSet resultSet = performQuery(container, "SELECT foo FROM bar"); @@ -125,11 +132,13 @@ public void testExplicitInitScript() throws SQLException { @Test(expected = ContainerLaunchException.class) public void testEmptyPasswordWithNonRootUser() { - try (MySQLContainer container = new MySQLContainer<>(MYSQL_56_IMAGE) - .withDatabaseName("TEST") - .withUsername("test") - .withPassword("") - .withEnv("MYSQL_ROOT_HOST", "%")){ + try ( + MySQLContainer container = new MySQLContainer<>(MySQLTestImages.MYSQL_56_IMAGE) + .withDatabaseName("TEST") + .withUsername("test") + .withPassword("") + .withEnv("MYSQL_ROOT_HOST", "%") + ) { container.start(); fail("ContainerLaunchException expected to be thrown"); } @@ -138,12 +147,13 @@ public void testEmptyPasswordWithNonRootUser() { @Test public void testEmptyPasswordWithRootUser() throws SQLException { // Add MYSQL_ROOT_HOST environment so that we can root login from anywhere for testing purposes - try (MySQLContainer mysql = new MySQLContainer<>(MYSQL_56_IMAGE) - .withDatabaseName("foo") - .withUsername("root") - .withPassword("") - .withEnv("MYSQL_ROOT_HOST", "%")) { - + try ( + MySQLContainer mysql = new MySQLContainer<>(MySQLTestImages.MYSQL_56_IMAGE) + .withDatabaseName("foo") + .withUsername("root") + .withPassword("") + .withEnv("MYSQL_ROOT_HOST", "%") + ) { mysql.start(); ResultSet resultSet = performQuery(mysql, "SELECT 1"); @@ -155,13 +165,13 @@ public void testEmptyPasswordWithRootUser() throws SQLException { @Test public void testWithAdditionalUrlParamTimeZone() throws SQLException { - MySQLContainer mysql = new MySQLContainer<>(MYSQL_57_IMAGE) + MySQLContainer mysql = new MySQLContainer<>(MySQLTestImages.MYSQL_57_IMAGE) .withUrlParam("serverTimezone", "Europe/Zurich") .withEnv("TZ", "Europe/Zurich") .withLogConsumer(new Slf4jLogConsumer(logger)); mysql.start(); - try(Connection connection = mysql.createConnection("")) { + try (Connection connection = mysql.createConnection("")) { Statement statement = connection.createStatement(); statement.execute("SELECT NOW();"); try (ResultSet resultSet = statement.getResultSet()) { @@ -169,13 +179,18 @@ public void testWithAdditionalUrlParamTimeZone() throws SQLException { // checking that the time_zone MySQL is Europe/Zurich LocalDateTime localDateTime = resultSet.getObject(1, LocalDateTime.class); - ZonedDateTime actualDateTime = localDateTime.atZone(ZoneId.of("Europe/Zurich")) + ZonedDateTime actualDateTime = localDateTime + .atZone(ZoneId.of("Europe/Zurich")) .truncatedTo(ChronoUnit.MINUTES); - ZonedDateTime expectedDateTime = ZonedDateTime.now(ZoneId.of("Europe/Zurich")) + ZonedDateTime expectedDateTime = ZonedDateTime + .now(ZoneId.of("Europe/Zurich")) .truncatedTo(ChronoUnit.MINUTES); - String message = String.format("MySQL time zone is not Europe/Zurich. MySQL date:%s, current date:%s", - actualDateTime, expectedDateTime); + String message = String.format( + "MySQL time zone is not Europe/Zurich. MySQL date:%s, current date:%s", + actualDateTime, + expectedDateTime + ); assertTrue(message, actualDateTime.equals(expectedDateTime)); } } finally { @@ -185,19 +200,20 @@ public void testWithAdditionalUrlParamTimeZone() throws SQLException { @Test public void testWithAdditionalUrlParamMultiQueries() throws SQLException { - MySQLContainer mysql = new MySQLContainer<>(MYSQL_57_IMAGE) + MySQLContainer mysql = new MySQLContainer<>(MySQLTestImages.MYSQL_57_IMAGE) .withUrlParam("allowMultiQueries", "true") .withLogConsumer(new Slf4jLogConsumer(logger)); mysql.start(); - try(Connection connection = mysql.createConnection("")) { + try (Connection connection = mysql.createConnection("")) { Statement statement = connection.createStatement(); - String multiQuery = "DROP TABLE IF EXISTS bar; " + + String multiQuery = + "DROP TABLE IF EXISTS bar; " + "CREATE TABLE bar (foo VARCHAR(20)); " + "INSERT INTO bar (foo) VALUES ('hello world');"; statement.execute(multiQuery); statement.execute("SELECT foo FROM bar;"); - try(ResultSet resultSet = statement.getResultSet()) { + try (ResultSet resultSet = statement.getResultSet()) { resultSet.next(); String firstColumnValue = resultSet.getString(1); assertEquals("Value from bar should equal real value", "hello world", firstColumnValue); @@ -209,7 +225,7 @@ public void testWithAdditionalUrlParamMultiQueries() throws SQLException { @Test public void testWithAdditionalUrlParamInJdbcUrl() { - MySQLContainer mysql = new MySQLContainer<>(MYSQL_57_IMAGE) + MySQLContainer mysql = new MySQLContainer<>(MySQLTestImages.MYSQL_57_IMAGE) .withUrlParam("allowMultiQueries", "true") .withUrlParam("rewriteBatchedStatements", "true") .withLogConsumer(new Slf4jLogConsumer(logger)); diff --git a/modules/neo4j/src/custom-neo4j-plugin/java/ac/simons/neo4j/demos/plugins/HelloWorld.java b/modules/neo4j/src/custom-neo4j-plugin/java/ac/simons/neo4j/demos/plugins/HelloWorld.java index cbfcdc03a55..8948c98d53c 100644 --- a/modules/neo4j/src/custom-neo4j-plugin/java/ac/simons/neo4j/demos/plugins/HelloWorld.java +++ b/modules/neo4j/src/custom-neo4j-plugin/java/ac/simons/neo4j/demos/plugins/HelloWorld.java @@ -9,7 +9,6 @@ public class HelloWorld { @UserFunction("ac.simons.helloWorld") @Description("Simple Hello World") public String helloWorld(@Name("name") String name) { - return "Hello, " + name; } } diff --git a/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java index 6f0cf10ded7..94b4263fe8c 100644 --- a/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java +++ b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java @@ -1,15 +1,5 @@ package org.testcontainers.containers; -import static java.net.HttpURLConnection.HTTP_OK; -import static java.util.stream.Collectors.toSet; - -import java.time.Duration; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.containers.wait.strategy.WaitAllStrategy; @@ -19,6 +9,15 @@ import org.testcontainers.utility.LicenseAcceptance; import org.testcontainers.utility.MountableFile; +import java.net.HttpURLConnection; +import java.time.Duration; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + /** * Testcontainer for Neo4j. * @@ -36,6 +35,7 @@ public class Neo4jContainer> extends GenericContaine * The default tag (version) to use. */ private static final String DEFAULT_TAG = "4.4"; + private static final String ENTERPRISE_TAG = DEFAULT_TAG + "-enterprise"; /** @@ -91,9 +91,7 @@ public Neo4jContainer(String dockerImageName) { */ public Neo4jContainer(final DockerImageName dockerImageName) { super(dockerImageName); - - this.standardImage = dockerImageName.getUnversionedPart() - .equals(DEFAULT_IMAGE_NAME.getUnversionedPart()); + this.standardImage = dockerImageName.getUnversionedPart().equals(DEFAULT_IMAGE_NAME.getUnversionedPart()); dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); @@ -101,39 +99,37 @@ public Neo4jContainer(final DockerImageName dockerImageName) { .withRegEx(String.format(".*Bolt enabled on .*:%d\\.\n", DEFAULT_BOLT_PORT)); WaitStrategy waitForHttp = new HttpWaitStrategy() .forPort(DEFAULT_HTTP_PORT) - .forStatusCodeMatching(response -> response == HTTP_OK); + .forStatusCodeMatching(response -> response == HttpURLConnection.HTTP_OK); - this.waitStrategy = new WaitAllStrategy() - .withStrategy(waitForBolt) - .withStrategy(waitForHttp) - .withStartupTimeout(Duration.ofMinutes(2)); + this.waitStrategy = + new WaitAllStrategy() + .withStrategy(waitForBolt) + .withStrategy(waitForHttp) + .withStartupTimeout(Duration.ofMinutes(2)); addExposedPorts(DEFAULT_BOLT_PORT, DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT); } @Override public Set getLivenessCheckPortNumbers() { - - return Stream.of(DEFAULT_BOLT_PORT, DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT) + return Stream + .of(DEFAULT_BOLT_PORT, DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT) .map(this::getMappedPort) - .collect(toSet()); + .collect(Collectors.toSet()); } @Override protected void configure() { - boolean emptyAdminPassword = this.adminPassword == null || this.adminPassword.isEmpty(); String neo4jAuth = emptyAdminPassword ? "none" : String.format(AUTH_FORMAT, this.adminPassword); addEnv("NEO4J_AUTH", neo4jAuth); if (!this.labsPlugins.isEmpty()) { - String enabledPlugins = this.labsPlugins.stream() - .map(pluginName -> "\"" + pluginName + "\"") - .collect(Collectors.joining(",")); + String enabledPlugins = + this.labsPlugins.stream().map(pluginName -> "\"" + pluginName + "\"").collect(Collectors.joining(",")); addEnv("NEO4JLABS_PLUGINS", "[" + enabledPlugins + "]"); } - } /** @@ -169,8 +165,8 @@ public String getHttpsUrl() { public S withEnterpriseEdition() { if (!standardImage) { throw new IllegalStateException( - String.format("Cannot use enterprise version with alternative image %s.", - getDockerImageName())); + String.format("Cannot use enterprise version with alternative image %s.", getDockerImageName()) + ); } setDockerImageName(DEFAULT_IMAGE_NAME.withTag(ENTERPRISE_TAG).asCanonicalNameString()); @@ -189,7 +185,6 @@ public S withEnterpriseEdition() { * @return This container. */ public S withAdminPassword(final String adminPassword) { - this.adminPassword = adminPassword; return self(); } @@ -226,7 +221,8 @@ public S withoutAuthentication() { public S withDatabase(MountableFile graphDb) { if (!isNeo4jDatabaseVersionSupportingDbCopy()) { throw new IllegalArgumentException( - "Copying database folder is not supported for Neo4j instances with version 4.0 or higher."); + "Copying database folder is not supported for Neo4j instances with version 4.0 or higher." + ); } return withCopyFileToContainer(graphDb, "/data/databases/graph.db"); } @@ -254,7 +250,6 @@ public S withPlugins(MountableFile plugins) { * @return This container. */ public S withNeo4jConfig(String key, String value) { - addEnv(formatConfigurationKey(key), value); return self(); } @@ -273,7 +268,8 @@ public String getAdminPassword() { * @return This container. */ public S withLabsPlugins(Neo4jLabsPlugin... neo4jLabsPlugins) { - List pluginNames = Arrays.stream(neo4jLabsPlugins) + List pluginNames = Arrays + .stream(neo4jLabsPlugins) .map(plugin -> plugin.pluginName) .collect(Collectors.toList()); @@ -295,9 +291,7 @@ public S withLabsPlugins(String... neo4jLabsPlugins) { private static String formatConfigurationKey(String plainConfigKey) { final String prefix = "NEO4J_"; - return String.format("%s%s", prefix, plainConfigKey - .replaceAll("_", "__") - .replaceAll("\\.", "_")); + return String.format("%s%s", prefix, plainConfigKey.replaceAll("_", "__").replaceAll("\\.", "_")); } private boolean isNeo4jDatabaseVersionSupportingDbCopy() { @@ -311,9 +305,12 @@ private boolean isNeo4jDatabaseVersionSupportingDbCopy() { return true; } if (!usedComparableVersion.isSemanticVersion()) { - logger().warn("Version {} is not a semantic version. The function \"withDatabase\" will fail.", usedImageVersion); + logger() + .warn( + "Version {} is not a semantic version. The function \"withDatabase\" will fail.", + usedImageVersion + ); logger().warn("Copying databases is only supported for Neo4j versions 3.5.x"); - } return false; diff --git a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerJUnitIntegrationTest.java b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerJUnitIntegrationTest.java index 6832dfff0c7..3a2df1f7d0c 100644 --- a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerJUnitIntegrationTest.java +++ b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerJUnitIntegrationTest.java @@ -24,12 +24,11 @@ public class Neo4jContainerJUnitIntegrationTest { @Test public void shouldStart() { - boolean actual = neo4jContainer.isRunning(); assertThat(actual).isTrue(); - try (Driver driver = GraphDatabase - .driver(neo4jContainer.getBoltUrl(), AuthTokens.basic("neo4j", "password")); + try ( + Driver driver = GraphDatabase.driver(neo4jContainer.getBoltUrl(), AuthTokens.basic("neo4j", "password")); Session session = driver.session() ) { long one = session.run("RETURN 1", Collections.emptyMap()).next().get(0).asLong(); diff --git a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java index 0d3f04ef570..20cd38dc70c 100644 --- a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java +++ b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java @@ -29,17 +29,16 @@ public class Neo4jContainerTest { @Test public void shouldDisableAuthentication() { - try ( + // spotless:off // withoutAuthentication { Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") .withoutAuthentication() // } + // spotless:on ) { neo4jContainer.start(); - try (Driver driver = getDriver(neo4jContainer); - Session session = driver.session() - ) { + try (Driver driver = getDriver(neo4jContainer); Session session = driver.session()) { long one = session.run("RETURN 1", Collections.emptyMap()).next().get(0).asLong(); assertThat(one).isEqualTo(1L); } @@ -55,10 +54,7 @@ public void shouldCopyDatabase() { // } ) { neo4jContainer.start(); - try ( - Driver driver = getDriver(neo4jContainer); - Session session = driver.session() - ) { + try (Driver driver = getDriver(neo4jContainer); Session session = driver.session()) { Result result = session.run("MATCH (t:Thing) RETURN t"); assertThat(result.list().stream().map(r -> r.get("t").get("name").asString())) .containsExactlyInAnyOrder("Thing", "Thing 2", "Thing 3", "A box"); @@ -68,25 +64,29 @@ public void shouldCopyDatabase() { @Test public void shouldFailOnCopyDatabaseForDefaultNeo4j4Image() { - Assertions.assertThatIllegalArgumentException() - .isThrownBy(() -> new Neo4jContainer<>() - .withDatabase(MountableFile.forClasspathResource("/test-graph.db"))) + Assertions + .assertThatIllegalArgumentException() + .isThrownBy(() -> new Neo4jContainer<>().withDatabase(MountableFile.forClasspathResource("/test-graph.db"))) .withMessage("Copying database folder is not supported for Neo4j instances with version 4.0 or higher."); } @Test public void shouldFailOnCopyDatabaseForCustomNeo4j4Image() { - Assertions.assertThatIllegalArgumentException() - .isThrownBy(() -> new Neo4jContainer<>("neo4j:4.4.1") - .withDatabase(MountableFile.forClasspathResource("/test-graph.db"))) + Assertions + .assertThatIllegalArgumentException() + .isThrownBy(() -> { + new Neo4jContainer<>("neo4j:4.4.1").withDatabase(MountableFile.forClasspathResource("/test-graph.db")); + }) .withMessage("Copying database folder is not supported for Neo4j instances with version 4.0 or higher."); } @Test public void shouldFailOnCopyDatabaseForCustomNonSemverNeo4j4Image() { - Assertions.assertThatIllegalArgumentException() - .isThrownBy(() -> new Neo4jContainer<>("neo4j:latest") - .withDatabase(MountableFile.forClasspathResource("/test-graph.db"))) + Assertions + .assertThatIllegalArgumentException() + .isThrownBy(() -> { + new Neo4jContainer<>("neo4j:latest").withDatabase(MountableFile.forClasspathResource("/test-graph.db")); + }) .withMessage("Copying database folder is not supported for Neo4j instances with version 4.0 or higher."); } @@ -99,10 +99,7 @@ public void shouldCopyPlugins() { // } ) { neo4jContainer.start(); - try ( - Driver driver = getDriver(neo4jContainer); - Session session = driver.session() - ) { + try (Driver driver = getDriver(neo4jContainer); Session session = driver.session()) { assertThatCustomPluginWasCopied(session); } } @@ -117,10 +114,7 @@ public void shouldCopyPlugin() { // } ) { neo4jContainer.start(); - try ( - Driver driver = getDriver(neo4jContainer); - Session session = driver.session() - ) { + try (Driver driver = getDriver(neo4jContainer); Session session = driver.session()) { assertThatCustomPluginWasCopied(session); } } @@ -152,17 +146,16 @@ public void shouldRunEnterprise() { // enterpriseEdition { Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") .withEnterpriseEdition() - // } + // } .withAdminPassword("Picard123") ) { neo4jContainer.start(); - try ( - Driver driver = getDriver(neo4jContainer); - Session session = driver.session() - ) { + try (Driver driver = getDriver(neo4jContainer); Session session = driver.session()) { String edition = session .run("CALL dbms.components() YIELD edition RETURN edition", Collections.emptyMap()) - .next().get(0).asString(); + .next() + .get(0) + .asString(); assertThat(edition).isEqualTo("enterprise"); } } @@ -178,31 +171,28 @@ public void shouldAddConfigToEnvironment() { assertThat(neo4jContainer.getEnvMap()) .containsEntry("NEO4J_dbms_security_procedures_unrestricted", "apoc.*,algo.*"); - assertThat(neo4jContainer.getEnvMap()) - .containsEntry("NEO4J_dbms_tx__log_rotation_size", "42M"); + assertThat(neo4jContainer.getEnvMap()).containsEntry("NEO4J_dbms_tx__log_rotation_size", "42M"); } @Test public void shouldConfigureSingleLabsPlugin() { - try (Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") - .withLabsPlugins(Neo4jLabsPlugin.APOC)) { - - + try ( + Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4").withLabsPlugins(Neo4jLabsPlugin.APOC) + ) { // needs to get called explicitly for setup neo4jContainer.configure(); - assertThat(neo4jContainer.getEnvMap()) - .containsEntry("NEO4JLABS_PLUGINS", "[\"apoc\"]"); + assertThat(neo4jContainer.getEnvMap()).containsEntry("NEO4JLABS_PLUGINS", "[\"apoc\"]"); } } @Test public void shouldConfigureMultipleLabsPlugins() { - try( - // configureLabsPlugins { - Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") - .withLabsPlugins(Neo4jLabsPlugin.APOC, Neo4jLabsPlugin.BLOOM); - // } + try ( + // configureLabsPlugins { + Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") + .withLabsPlugins(Neo4jLabsPlugin.APOC, Neo4jLabsPlugin.BLOOM); + // } ) { // needs to get called explicitly for setup neo4jContainer.configure(); @@ -214,23 +204,19 @@ public void shouldConfigureMultipleLabsPlugins() { @Test public void shouldConfigureSingleLabsPluginWithString() { - try (Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") - .withLabsPlugins("myApoc")) { - + try (Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4").withLabsPlugins("myApoc")) { // needs to get called explicitly for setup neo4jContainer.configure(); - assertThat(neo4jContainer.getEnvMap()) - .containsEntry("NEO4JLABS_PLUGINS", "[\"myApoc\"]"); + assertThat(neo4jContainer.getEnvMap()).containsEntry("NEO4JLABS_PLUGINS", "[\"myApoc\"]"); } } @Test public void shouldConfigureMultipleLabsPluginsWithString() { - - try (Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") - .withLabsPlugins("myApoc", "myBloom")) { - + try ( + Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4").withLabsPlugins("myApoc", "myBloom") + ) { // needs to get called explicitly for setup neo4jContainer.configure(); @@ -240,7 +226,6 @@ public void shouldConfigureMultipleLabsPluginsWithString() { } private static Driver getDriver(Neo4jContainer container) { - AuthToken authToken = AuthTokens.none(); if (container.getAdminPassword() != null) { authToken = AuthTokens.basic("neo4j", container.getAdminPassword()); diff --git a/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java b/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java index 296d82b7997..3e7a2062fd0 100644 --- a/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java +++ b/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java @@ -12,10 +12,14 @@ /** * @author richardnorth */ -public class NginxContainer> extends GenericContainer implements LinkableContainer { +public class NginxContainer> + extends GenericContainer + implements LinkableContainer { private static final int NGINX_DEFAULT_PORT = 80; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("nginx"); + private static final String DEFAULT_TAG = "1.9.4"; /** @@ -32,7 +36,6 @@ public NginxContainer(String dockerImageName) { public NginxContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); addExposedPort(NGINX_DEFAULT_PORT); diff --git a/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java b/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java index e322804bf75..c1ba3d30684 100644 --- a/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java +++ b/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java @@ -12,7 +12,7 @@ import java.net.URL; import java.net.URLConnection; -import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.Matchers.containsString; import static org.rnorth.visibleassertions.VisibleAssertions.*; /** @@ -29,9 +29,10 @@ public class SimpleNginxTest { public NginxContainer nginx = new NginxContainer<>(NGINX_IMAGE) .withCustomContent(tmpDirectory) .waitingFor(new HttpWaitStrategy()); + // } - @SuppressWarnings({"Duplicates", "ResultOfMethodCallIgnored"}) + @SuppressWarnings({ "Duplicates", "ResultOfMethodCallIgnored" }) @BeforeClass public static void setupContent() throws Exception { // addCustomContent { @@ -43,7 +44,8 @@ public static void setupContent() throws Exception { // And "hello world" HTTP file File indexFile = new File(contentFolder, "index.html"); indexFile.deleteOnExit(); - @Cleanup PrintStream printStream = new PrintStream(new FileOutputStream(indexFile)); + @Cleanup + PrintStream printStream = new PrintStream(new FileOutputStream(indexFile)); printStream.println("Hello World!"); // } } @@ -53,7 +55,8 @@ public void testSimple() throws Exception { // getFromNginxServer { URL baseUrl = nginx.getBaseUrl("http", 80); - assertThat("An HTTP GET from the Nginx server returns the index.html from the custom content directory", + assertThat( + "An HTTP GET from the Nginx server returns the index.html from the custom content directory", responseFromNginx(baseUrl), containsString("Hello World!") ); @@ -62,7 +65,8 @@ public void testSimple() throws Exception { private static String responseFromNginx(URL baseUrl) throws IOException { URLConnection urlConnection = baseUrl.openConnection(); - @Cleanup BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); + @Cleanup + BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); return reader.readLine(); } } diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java index 689646f0006..bd5d9aeb7ca 100644 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java @@ -1,50 +1,59 @@ package org.testcontainers.containers; -import com.google.common.collect.Sets; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.utility.DockerImageName; import java.time.Duration; +import java.time.temporal.ChronoUnit; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Set; import java.util.concurrent.Future; -import static java.time.temporal.ChronoUnit.SECONDS; -import static java.util.Collections.singleton; - public class OracleContainer extends JdbcDatabaseContainer { public static final String NAME = "oracle"; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("gvenzl/oracle-xe"); static final String DEFAULT_TAG = "18.4.0-slim"; + static final String IMAGE = DEFAULT_IMAGE_NAME.getUnversionedPart(); private static final int ORACLE_PORT = 1521; + private static final int APEX_HTTP_PORT = 8080; private static final int DEFAULT_STARTUP_TIMEOUT_SECONDS = 240; + private static final int DEFAULT_CONNECT_TIMEOUT_SECONDS = 120; // Container defaults static final String DEFAULT_DATABASE_NAME = "xepdb1"; + static final String DEFAULT_SID = "xe"; + static final String DEFAULT_SYSTEM_USER = "system"; + static final String DEFAULT_SYS_USER = "sys"; // Test container defaults static final String APP_USER = "test"; + static final String APP_USER_PASSWORD = "test"; // Restricted user and database names private static final List ORACLE_SYSTEM_USERS = Arrays.asList(DEFAULT_SYSTEM_USER, DEFAULT_SYS_USER); private String databaseName = DEFAULT_DATABASE_NAME; + private String username = APP_USER; + private String password = APP_USER_PASSWORD; + private boolean usingSid = false; /** @@ -71,10 +80,11 @@ public OracleContainer(Future dockerImageName) { } private void preconfigure() { - this.waitStrategy = new LogMessageWaitStrategy() - .withRegEx(".*DATABASE IS READY TO USE!.*\\s") - .withTimes(1) - .withStartupTimeout(Duration.of(DEFAULT_STARTUP_TIMEOUT_SECONDS, SECONDS)); + this.waitStrategy = + new LogMessageWaitStrategy() + .withRegEx(".*DATABASE IS READY TO USE!.*\\s") + .withTimes(1) + .withStartupTimeout(Duration.of(DEFAULT_STARTUP_TIMEOUT_SECONDS, ChronoUnit.SECONDS)); withConnectTimeoutSeconds(DEFAULT_CONNECT_TIMEOUT_SECONDS); addExposedPorts(ORACLE_PORT, APEX_HTTP_PORT); @@ -88,7 +98,7 @@ protected void waitUntilContainerStarted() { @NotNull @Override public Set getLivenessCheckPortNumbers() { - return singleton(getMappedPort(ORACLE_PORT)); + return Collections.singleton(getMappedPort(ORACLE_PORT)); } @Override @@ -98,9 +108,9 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return isUsingSid() ? - "jdbc:oracle:thin:" + "@" + getHost() + ":" + getOraclePort() + ":" + getSid() : - "jdbc:oracle:thin:" + "@" + getHost() + ":" + getOraclePort() + "/" + getDatabaseName(); + return isUsingSid() + ? "jdbc:oracle:thin:" + "@" + getHost() + ":" + getOraclePort() + ":" + getSid() + : "jdbc:oracle:thin:" + "@" + getHost() + ":" + getOraclePort() + "/" + getDatabaseName(); } @Override @@ -192,7 +202,7 @@ protected void configure() { withEnv("ORACLE_PASSWORD", password); // Only set ORACLE_DATABASE if different than the default. - if(databaseName != DEFAULT_DATABASE_NAME) { + if (databaseName != DEFAULT_DATABASE_NAME) { withEnv("ORACLE_DATABASE", databaseName); } diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainerProvider.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainerProvider.java index a298ae23359..f92f0ec40be 100644 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainerProvider.java +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainerProvider.java @@ -6,6 +6,7 @@ * Factory for Oracle containers. */ public class OracleContainerProvider extends JdbcDatabaseContainerProvider { + @Override public boolean supports(String databaseType) { return databaseType.equals(OracleContainer.NAME); diff --git a/modules/oracle-xe/src/test/java/org/testcontainers/containers/jdbc/OracleJDBCDriverTest.java b/modules/oracle-xe/src/test/java/org/testcontainers/containers/jdbc/OracleJDBCDriverTest.java index 79dc35deb64..003941f4dfc 100644 --- a/modules/oracle-xe/src/test/java/org/testcontainers/containers/jdbc/OracleJDBCDriverTest.java +++ b/modules/oracle-xe/src/test/java/org/testcontainers/containers/jdbc/OracleJDBCDriverTest.java @@ -4,7 +4,6 @@ import com.zaxxer.hikari.HikariDataSource; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.ResultSetHandler; -import org.junit.Ignore; import org.junit.Test; import java.sql.ResultSet; @@ -19,18 +18,21 @@ public void testOracleWithNoSpecifiedVersion() throws SQLException { performSimpleTest("jdbc:tc:oracle://hostname/databasename"); } - private void performSimpleTest(String jdbcUrl) throws SQLException { HikariDataSource dataSource = getDataSource(jdbcUrl, 1); - new QueryRunner(dataSource).query("SELECT 1 FROM dual", new ResultSetHandler() { - @Override - public Object handle(ResultSet rs) throws SQLException { - rs.next(); - int resultSetInt = rs.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); - return true; - } - }); + new QueryRunner(dataSource) + .query( + "SELECT 1 FROM dual", + new ResultSetHandler() { + @Override + public Object handle(ResultSet rs) throws SQLException { + rs.next(); + int resultSetInt = rs.getInt(1); + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + return true; + } + } + ); dataSource.close(); } diff --git a/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java b/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java index 2dd82c437a4..2c514062415 100644 --- a/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java +++ b/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java @@ -1,8 +1,5 @@ package org.testcontainers.junit.oracle; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - import org.junit.Test; import org.testcontainers.containers.OracleContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; @@ -11,16 +8,22 @@ import java.sql.ResultSet; import java.sql.SQLException; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + public class SimpleOracleTest extends AbstractContainerDatabaseTest { - public static final DockerImageName ORACLE_DOCKER_IMAGE_NAME = DockerImageName.parse("gvenzl/oracle-xe:18.4.0-slim"); + public static final DockerImageName ORACLE_DOCKER_IMAGE_NAME = DockerImageName.parse( + "gvenzl/oracle-xe:18.4.0-slim" + ); - private void runTest(OracleContainer container, String databaseName, String username, String password) throws SQLException { + private void runTest(OracleContainer container, String databaseName, String username, String password) + throws SQLException { //Test config was honored assertEquals(databaseName, container.getDatabaseName()); assertEquals(username, container.getUsername()); assertEquals(password, container.getPassword()); - + //Test we can get a connection container.start(); ResultSet resultSet = performQuery(container, "SELECT 1 FROM dual"); @@ -30,9 +33,7 @@ private void runTest(OracleContainer container, String databaseName, String user @Test public void testDefaultSettings() throws SQLException { - try ( - OracleContainer oracle = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME); - ) { + try (OracleContainer oracle = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME);) { runTest(oracle, "xepdb1", "test", "test"); // Match against the last '/' @@ -43,10 +44,7 @@ public void testDefaultSettings() throws SQLException { @Test public void testPluggableDatabase() throws SQLException { - try ( - OracleContainer oracle = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME) - .withDatabaseName("testDB") - ) { + try (OracleContainer oracle = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME).withDatabaseName("testDB")) { runTest(oracle, "testDB", "test", "test"); } } @@ -76,10 +74,7 @@ public void testCustomUser() throws SQLException { @Test public void testSID() throws SQLException { - try ( - OracleContainer oracle = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME) - .usingSid(); - ) { + try (OracleContainer oracle = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME).usingSid();) { runTest(oracle, "xepdb1", "system", "test"); // Match against the last ':' diff --git a/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java b/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java index a68f55b60ef..2f606475dc0 100644 --- a/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java +++ b/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java @@ -21,25 +21,33 @@ * @author robfrank */ public class OrientDBContainer extends GenericContainer { + private static final Logger LOGGER = LoggerFactory.getLogger(OrientDBContainer.class); private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("orientdb"); + private static final String DEFAULT_TAG = "3.0.24-tp3"; private static final String DEFAULT_USERNAME = "admin"; + private static final String DEFAULT_PASSWORD = "admin"; + private static final String DEFAULT_SERVER_PASSWORD = "root"; private static final String DEFAULT_DATABASE_NAME = "testcontainers"; private static final int DEFAULT_BINARY_PORT = 2424; + private static final int DEFAULT_HTTP_PORT = 2480; private String databaseName; + private String serverPassword; + private Optional scriptPath = Optional.empty(); private OrientDB orientDB; + private ODatabaseSession session; /** @@ -56,13 +64,12 @@ public OrientDBContainer(@NonNull String dockerImageName) { public OrientDBContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); serverPassword = DEFAULT_SERVER_PASSWORD; databaseName = DEFAULT_DATABASE_NAME; - waitStrategy = new LogMessageWaitStrategy().withRegEx(".*Gremlin started correctly.*"); + waitStrategy = new LogMessageWaitStrategy().withRegEx(".*Gremlin started correctly.*"); addExposedPorts(DEFAULT_BINARY_PORT, DEFAULT_HTTP_PORT); } @@ -133,7 +140,9 @@ private void loadScript(String path, ODatabaseSession session) { if (resource == null) { LOGGER.warn("Could not load classpath init script: {}", scriptPath); - throw new RuntimeException("Could not load classpath init script: " + scriptPath + ". Resource not found."); + throw new RuntimeException( + "Could not load classpath init script: " + scriptPath + ". Resource not found." + ); } String script = IOUtils.toString(resource, StandardCharsets.UTF_8); @@ -147,5 +156,4 @@ private void loadScript(String path, ODatabaseSession session) { throw new RuntimeException("Error while executing init script: " + scriptPath, e); } } - } diff --git a/modules/orientdb/src/test/java/org/testcontainers/containers/OrientDBContainerTest.java b/modules/orientdb/src/test/java/org/testcontainers/containers/OrientDBContainerTest.java index 60d7b172436..675218637ce 100644 --- a/modules/orientdb/src/test/java/org/testcontainers/containers/OrientDBContainerTest.java +++ b/modules/orientdb/src/test/java/org/testcontainers/containers/OrientDBContainerTest.java @@ -42,7 +42,6 @@ public void shouldInitializeWithCommands() { @Test public void shouldQueryWithGremlin() { - try (OrientDBContainer container = new OrientDBContainer(ORIENTDB_IMAGE)) { container.start(); @@ -52,17 +51,17 @@ public void shouldQueryWithGremlin() { session.command("INSERT INTO Person set name='john'"); session.command("INSERT INTO Person set name='jane'"); - assertThat(session.execute("gremlin", - "g.V().hasLabel('Person')").stream()).hasSize(2); + assertThat(session.execute("gremlin", "g.V().hasLabel('Person')").stream()).hasSize(2); } } @Test public void shouldInitializeDatabaseFromScript() { - try (OrientDBContainer container = new OrientDBContainer(ORIENTDB_IMAGE) - .withScriptPath("initscript.osql") - .withDatabaseName("persons")) { - + try ( + OrientDBContainer container = new OrientDBContainer(ORIENTDB_IMAGE) + .withScriptPath("initscript.osql") + .withDatabaseName("persons") + ) { container.start(); assertThat(container.getDbUrl()) diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java index e41a349365c..0c30ebbb246 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java @@ -9,11 +9,16 @@ public class PostgisContainerProvider extends JdbcDatabaseContainerProvider { private static final String NAME = "postgis"; - private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("postgis/postgis").asCompatibleSubstituteFor("postgres"); + + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName + .parse("postgis/postgis") + .asCompatibleSubstituteFor("postgres"); + private static final String DEFAULT_TAG = "12-3.0"; + public static final String USER_PARAM = "user"; - public static final String PASSWORD_PARAM = "password"; + public static final String PASSWORD_PARAM = "password"; @Override public boolean supports(String databaseType) { diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index 741a8c4139a..3b36b61ea77 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -5,19 +5,21 @@ import org.testcontainers.utility.DockerImageName; import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.util.Collections; import java.util.Set; -import static java.time.temporal.ChronoUnit.SECONDS; -import static java.util.Collections.singleton; - /** * @author richardnorth */ public class PostgreSQLContainer> extends JdbcDatabaseContainer { + public static final String NAME = "postgresql"; + public static final String IMAGE = "postgres"; public static final String DEFAULT_TAG = "9.6.12"; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("postgres"); public static final Integer POSTGRESQL_PORT = 5432; @@ -27,7 +29,9 @@ public class PostgreSQLContainer> extends static final String DEFAULT_PASSWORD = "test"; private String databaseName = "test"; + private String username = "test"; + private String password = "test"; private static final String FSYNC_OFF_OPTION = "fsync=off"; @@ -46,13 +50,13 @@ public PostgreSQLContainer(final String dockerImageName) { public PostgreSQLContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); - this.waitStrategy = new LogMessageWaitStrategy() + this.waitStrategy = + new LogMessageWaitStrategy() .withRegEx(".*database system is ready to accept connections.*\\s") .withTimes(2) - .withStartupTimeout(Duration.of(60, SECONDS)); + .withStartupTimeout(Duration.of(60, ChronoUnit.SECONDS)); this.setCommand("postgres", "-c", FSYNC_OFF_OPTION); addExposedPort(POSTGRESQL_PORT); @@ -61,7 +65,7 @@ public PostgreSQLContainer(final DockerImageName dockerImageName) { @NotNull @Override protected Set getLivenessCheckPorts() { - return singleton(getMappedPort(POSTGRESQL_PORT)); + return Collections.singleton(getMappedPort(POSTGRESQL_PORT)); } @Override @@ -81,8 +85,15 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { String additionalUrlParams = constructUrlParameters("?", "&"); - return "jdbc:postgresql://" + getHost() + ":" + getMappedPort(POSTGRESQL_PORT) - + "/" + databaseName + additionalUrlParams; + return ( + "jdbc:postgresql://" + + getHost() + + ":" + + getMappedPort(POSTGRESQL_PORT) + + "/" + + databaseName + + additionalUrlParams + ); } @Override diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainerProvider.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainerProvider.java index 5e7580c75eb..de98885510c 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainerProvider.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainerProvider.java @@ -9,6 +9,7 @@ public class PostgreSQLContainerProvider extends JdbcDatabaseContainerProvider { public static final String USER_PARAM = "user"; + public static final String PASSWORD_PARAM = "password"; @Override @@ -30,5 +31,4 @@ public JdbcDatabaseContainer newInstance(String tag) { public JdbcDatabaseContainer newInstance(ConnectionUrl connectionUrl) { return newInstanceFromConnectionUrl(connectionUrl, USER_PARAM, PASSWORD_PARAM); } - } diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainer.java index 88b5497f5d3..d2b933694c7 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainer.java @@ -13,7 +13,8 @@ public final class PostgreSQLR2DBCDatabaseContainer implements R2DBCDatabaseCont private final PostgreSQLContainer container; public static ConnectionFactoryOptions getOptions(PostgreSQLContainer container) { - ConnectionFactoryOptions options = ConnectionFactoryOptions.builder() + ConnectionFactoryOptions options = ConnectionFactoryOptions + .builder() .option(ConnectionFactoryOptions.DRIVER, PostgreSQLR2DBCDatabaseContainerProvider.DRIVER) .build(); @@ -22,7 +23,8 @@ public static ConnectionFactoryOptions getOptions(PostgreSQLContainer contain @Override public ConnectionFactoryOptions configure(ConnectionFactoryOptions options) { - return options.mutate() + return options + .mutate() .option(ConnectionFactoryOptions.HOST, container.getHost()) .option(ConnectionFactoryOptions.PORT, container.getMappedPort(PostgreSQLContainer.POSTGRESQL_PORT)) .option(ConnectionFactoryOptions.DATABASE, container.getDatabaseName()) diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/TimescaleDBContainerProvider.java b/modules/postgresql/src/main/java/org/testcontainers/containers/TimescaleDBContainerProvider.java index c9b81fccfe4..86fcca2c039 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/TimescaleDBContainerProvider.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/TimescaleDBContainerProvider.java @@ -11,11 +11,16 @@ public class TimescaleDBContainerProvider extends JdbcDatabaseContainerProvider { private static final String NAME = "timescaledb"; + private static final String DEFAULT_TAG = "2.1.0-pg11"; - private static final DockerImageName DEFAULT_IMAGE = DockerImageName.parse("timescale/timescaledb").asCompatibleSubstituteFor("postgres"); + + private static final DockerImageName DEFAULT_IMAGE = DockerImageName + .parse("timescale/timescaledb") + .asCompatibleSubstituteFor("postgres"); + public static final String USER_PARAM = "user"; - public static final String PASSWORD_PARAM = "password"; + public static final String PASSWORD_PARAM = "password"; @Override public boolean supports(String databaseType) { diff --git a/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java index c18f19a109e..3be7685ea27 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java @@ -16,7 +16,10 @@ public void shouldCorrectlyAppendQueryString() { String connectionUrl = postgres.constructUrlForConnection("?stringtype=unspecified&stringtype=unspecified"); String queryString = connectionUrl.substring(connectionUrl.indexOf('?')); - assertTrue("Query String contains expected params", queryString.contains("?stringtype=unspecified&stringtype=unspecified")); + assertTrue( + "Query String contains expected params", + queryString.contains("?stringtype=unspecified&stringtype=unspecified") + ); assertEquals("Query String starts with '?'", 0, queryString.indexOf('?')); assertFalse("Query String does not contain extra '?'", queryString.substring(1).contains("?")); } @@ -27,7 +30,10 @@ public void shouldCorrectlyAppendQueryStringWhenNoBaseParams() { String connectionUrl = postgres.constructUrlForConnection("?stringtype=unspecified&stringtype=unspecified"); String queryString = connectionUrl.substring(connectionUrl.indexOf('?')); - assertTrue("Query String contains expected params", queryString.contains("?stringtype=unspecified&stringtype=unspecified")); + assertTrue( + "Query String contains expected params", + queryString.contains("?stringtype=unspecified&stringtype=unspecified") + ); assertEquals("Query String starts with '?'", 0, queryString.indexOf('?')); assertFalse("Query String does not contain extra '?'", queryString.substring(1).contains("?")); } @@ -42,11 +48,15 @@ public void shouldReturnOriginalURLWhenEmptyQueryString() { @Test public void shouldRejectInvalidQueryString() { - assertThrows("Fails when invalid query string provided", IllegalArgumentException.class, - () -> new NoParamsUrlPostgreSQLContainer().constructUrlForConnection("stringtype=unspecified")); + assertThrows( + "Fails when invalid query string provided", + IllegalArgumentException.class, + () -> new NoParamsUrlPostgreSQLContainer().constructUrlForConnection("stringtype=unspecified") + ); } static class FixedJdbcUrlPostgreSQLContainer extends PostgreSQLContainer { + public FixedJdbcUrlPostgreSQLContainer() { super(PostgreSQLTestImages.POSTGRES_TEST_IMAGE); } @@ -63,6 +73,7 @@ public Integer getMappedPort(int originalPort) { } static class NoParamsUrlPostgreSQLContainer extends PostgreSQLContainer { + public NoParamsUrlPostgreSQLContainer() { super(PostgreSQLTestImages.POSTGRES_TEST_IMAGE); } diff --git a/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainerTest.java b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainerTest.java index 46e439aaf66..fb503bfd659 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainerTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainerTest.java @@ -13,11 +13,13 @@ protected PostgreSQLContainer createContainer() { @Override protected ConnectionFactoryOptions getOptions(PostgreSQLContainer container) { + // spotless:off // get_options { ConnectionFactoryOptions options = PostgreSQLR2DBCDatabaseContainer.getOptions( container ); // } + // spotless:on return options; } diff --git a/modules/postgresql/src/test/java/org/testcontainers/containers/TimescaleDBContainerTest.java b/modules/postgresql/src/test/java/org/testcontainers/containers/TimescaleDBContainerTest.java index f3c73a4111b..74d60b2d32d 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/containers/TimescaleDBContainerTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/containers/TimescaleDBContainerTest.java @@ -24,10 +24,17 @@ public void testSimple() throws SQLException { @Test public void testCommandOverride() throws SQLException { - try (GenericContainer postgres = new TimescaleDBContainerProvider().newInstance().withCommand("postgres -c max_connections=42")) { + try ( + GenericContainer postgres = new TimescaleDBContainerProvider() + .newInstance() + .withCommand("postgres -c max_connections=42") + ) { postgres.start(); - ResultSet resultSet = performQuery((JdbcDatabaseContainer) postgres, "SELECT current_setting('max_connections')"); + ResultSet resultSet = performQuery( + (JdbcDatabaseContainer) postgres, + "SELECT current_setting('max_connections')" + ); String result = resultSet.getString(1); assertEquals("max_connections should be overriden", "42", result); } @@ -35,10 +42,18 @@ public void testCommandOverride() throws SQLException { @Test public void testUnsetCommand() throws SQLException { - try (GenericContainer postgres = new TimescaleDBContainerProvider().newInstance().withCommand("postgres -c max_connections=42").withCommand()) { + try ( + GenericContainer postgres = new TimescaleDBContainerProvider() + .newInstance() + .withCommand("postgres -c max_connections=42") + .withCommand() + ) { postgres.start(); - ResultSet resultSet = performQuery((JdbcDatabaseContainer) postgres, "SELECT current_setting('max_connections')"); + ResultSet resultSet = performQuery( + (JdbcDatabaseContainer) postgres, + "SELECT current_setting('max_connections')" + ); String result = resultSet.getString(1); assertNotEquals("max_connections should not be overriden", "42", result); } @@ -46,7 +61,11 @@ public void testUnsetCommand() throws SQLException { @Test public void testExplicitInitScript() throws SQLException { - try (JdbcDatabaseContainer postgres = new TimescaleDBContainerProvider().newInstance().withInitScript("somepath/init_timescaledb.sql")) { + try ( + JdbcDatabaseContainer postgres = new TimescaleDBContainerProvider() + .newInstance() + .withInitScript("somepath/init_timescaledb.sql") + ) { postgres.start(); ResultSet resultSet = performQuery(postgres, "SELECT foo FROM bar"); diff --git a/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java b/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java index 9e3e0b03413..5acbd875c23 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java @@ -19,6 +19,7 @@ * TODO: Move to the jdbc module and either (a) implement a barebones {@link org.testcontainers.containers.JdbcDatabaseContainerProvider} for testing, or (b) refactor into a unit test. */ public class DatabaseDriverShutdownTest { + @AfterClass public static void testCleanup() { ContainerDatabaseDriver.killContainers(); diff --git a/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java b/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java index a2b1fd5da8b..4c8d6afeef0 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java @@ -22,7 +22,6 @@ public class DatabaseDriverTmpfsTest { public void testDatabaseHasTmpFsViaConnectionString() throws Exception { final String jdbcUrl = "jdbc:tc:postgresql:9.6.8://hostname/databasename?TC_TMPFS=/testtmpfs:rw"; try (Connection ignored = DriverManager.getConnection(jdbcUrl)) { - JdbcDatabaseContainer container = ContainerDatabaseDriver.getContainer(jdbcUrl); // check file doesn't exist String path = "/testtmpfs/test.file"; diff --git a/modules/postgresql/src/test/java/org/testcontainers/jdbc/postgis/PostgisJDBCDriverTest.java b/modules/postgresql/src/test/java/org/testcontainers/jdbc/postgis/PostgisJDBCDriverTest.java index b1d01441016..abc0a3b76bc 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/jdbc/postgis/PostgisJDBCDriverTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/jdbc/postgis/PostgisJDBCDriverTest.java @@ -4,19 +4,25 @@ import org.junit.runners.Parameterized; import org.testcontainers.jdbc.AbstractJDBCDriverTest; +import java.util.Arrays; import java.util.EnumSet; -import static java.util.Arrays.asList; - @RunWith(Parameterized.class) public class PostgisJDBCDriverTest extends AbstractJDBCDriverTest { @Parameterized.Parameters(name = "{index} - {0}") public static Iterable data() { - return asList( - new Object[][]{ - {"jdbc:tc:postgis://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, - {"jdbc:tc:postgis:9.6-2.5://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, - }); + return Arrays.asList( + new Object[][] { + { + "jdbc:tc:postgis://hostname/databasename?user=someuser&password=somepwd", + EnumSet.of(Options.JDBCParams), + }, + { + "jdbc:tc:postgis:9.6-2.5://hostname/databasename?user=someuser&password=somepwd", + EnumSet.of(Options.JDBCParams), + }, + } + ); } } diff --git a/modules/postgresql/src/test/java/org/testcontainers/jdbc/postgresql/PostgreSQLJDBCDriverTest.java b/modules/postgresql/src/test/java/org/testcontainers/jdbc/postgresql/PostgreSQLJDBCDriverTest.java index c63eb9ffb9d..d42f17f3ec2 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/jdbc/postgresql/PostgreSQLJDBCDriverTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/jdbc/postgresql/PostgreSQLJDBCDriverTest.java @@ -4,18 +4,21 @@ import org.junit.runners.Parameterized; import org.testcontainers.jdbc.AbstractJDBCDriverTest; +import java.util.Arrays; import java.util.EnumSet; -import static java.util.Arrays.asList; - @RunWith(Parameterized.class) public class PostgreSQLJDBCDriverTest extends AbstractJDBCDriverTest { @Parameterized.Parameters(name = "{index} - {0}") public static Iterable data() { - return asList( - new Object[][]{ - {"jdbc:tc:postgresql:9.6.8://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, - }); + return Arrays.asList( + new Object[][] { + { + "jdbc:tc:postgresql:9.6.8://hostname/databasename?user=someuser&password=somepwd", + EnumSet.of(Options.JDBCParams), + }, + } + ); } } diff --git a/modules/postgresql/src/test/java/org/testcontainers/jdbc/timescaledb/TimescaleDBJDBCDriverTest.java b/modules/postgresql/src/test/java/org/testcontainers/jdbc/timescaledb/TimescaleDBJDBCDriverTest.java index 8a6eaa3d04b..2a50b46c901 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/jdbc/timescaledb/TimescaleDBJDBCDriverTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/jdbc/timescaledb/TimescaleDBJDBCDriverTest.java @@ -4,19 +4,25 @@ import org.junit.runners.Parameterized; import org.testcontainers.jdbc.AbstractJDBCDriverTest; +import java.util.Arrays; import java.util.EnumSet; -import static java.util.Arrays.asList; - @RunWith(Parameterized.class) public class TimescaleDBJDBCDriverTest extends AbstractJDBCDriverTest { @Parameterized.Parameters(name = "{index} - {0}") public static Iterable data() { - return asList( - new Object[][]{ - {"jdbc:tc:timescaledb://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, - {"jdbc:tc:timescaledb:2.1.0-pg13://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, - }); + return Arrays.asList( + new Object[][] { + { + "jdbc:tc:timescaledb://hostname/databasename?user=someuser&password=somepwd", + EnumSet.of(Options.JDBCParams), + }, + { + "jdbc:tc:timescaledb:2.1.0-pg13://hostname/databasename?user=someuser&password=somepwd", + EnumSet.of(Options.JDBCParams), + }, + } + ); } } diff --git a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/CustomizablePostgreSQLTest.java b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/CustomizablePostgreSQLTest.java index 8fd523434dd..1b256c0fd6b 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/CustomizablePostgreSQLTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/CustomizablePostgreSQLTest.java @@ -14,17 +14,21 @@ * @author richardnorth */ public class CustomizablePostgreSQLTest extends AbstractContainerDatabaseTest { + private static final String DB_NAME = "foo"; + private static final String USER = "bar"; + private static final String PWD = "baz"; @Test public void testSimple() throws SQLException { - try (PostgreSQLContainer postgres = new PostgreSQLContainer<>(PostgreSQLTestImages.POSTGRES_TEST_IMAGE) + try ( + PostgreSQLContainer postgres = new PostgreSQLContainer<>(PostgreSQLTestImages.POSTGRES_TEST_IMAGE) .withDatabaseName(DB_NAME) .withUsername(USER) - .withPassword(PWD)) { - + .withPassword(PWD) + ) { postgres.start(); ResultSet resultSet = performQuery(postgres, "SELECT 1"); diff --git a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java index 95e1fdf10fd..2a0dd351ad6 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java @@ -1,6 +1,7 @@ package org.testcontainers.junit.postgresql; import org.junit.Test; +import org.testcontainers.PostgreSQLTestImages; import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; @@ -9,14 +10,12 @@ import java.util.logging.Level; import java.util.logging.LogManager; -import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; -import static org.testcontainers.PostgreSQLTestImages.POSTGRES_TEST_IMAGE; public class SimplePostgreSQLTest extends AbstractContainerDatabaseTest { - static { // Postgres JDBC driver uses JUL; disable it to avoid annoying, irrelevant, stderr logs during connection testing LogManager.getLogManager().getLogger("").setLevel(Level.OFF); @@ -24,7 +23,7 @@ public class SimplePostgreSQLTest extends AbstractContainerDatabaseTest { @Test public void testSimple() throws SQLException { - try (PostgreSQLContainer postgres = new PostgreSQLContainer<>(POSTGRES_TEST_IMAGE)) { + try (PostgreSQLContainer postgres = new PostgreSQLContainer<>(PostgreSQLTestImages.POSTGRES_TEST_IMAGE)) { postgres.start(); ResultSet resultSet = performQuery(postgres, "SELECT 1"); @@ -35,7 +34,10 @@ public void testSimple() throws SQLException { @Test public void testCommandOverride() throws SQLException { - try (PostgreSQLContainer postgres = new PostgreSQLContainer<>(POSTGRES_TEST_IMAGE).withCommand("postgres -c max_connections=42")) { + try ( + PostgreSQLContainer postgres = new PostgreSQLContainer<>(PostgreSQLTestImages.POSTGRES_TEST_IMAGE) + .withCommand("postgres -c max_connections=42") + ) { postgres.start(); ResultSet resultSet = performQuery(postgres, "SELECT current_setting('max_connections')"); @@ -46,7 +48,11 @@ public void testCommandOverride() throws SQLException { @Test public void testUnsetCommand() throws SQLException { - try (PostgreSQLContainer postgres = new PostgreSQLContainer<>(POSTGRES_TEST_IMAGE).withCommand("postgres -c max_connections=42").withCommand()) { + try ( + PostgreSQLContainer postgres = new PostgreSQLContainer<>(PostgreSQLTestImages.POSTGRES_TEST_IMAGE) + .withCommand("postgres -c max_connections=42") + .withCommand() + ) { postgres.start(); ResultSet resultSet = performQuery(postgres, "SELECT current_setting('max_connections')"); @@ -57,7 +63,10 @@ public void testUnsetCommand() throws SQLException { @Test public void testExplicitInitScript() throws SQLException { - try (PostgreSQLContainer postgres = new PostgreSQLContainer<>(POSTGRES_TEST_IMAGE).withInitScript("somepath/init_postgresql.sql")) { + try ( + PostgreSQLContainer postgres = new PostgreSQLContainer<>(PostgreSQLTestImages.POSTGRES_TEST_IMAGE) + .withInitScript("somepath/init_postgresql.sql") + ) { postgres.start(); ResultSet resultSet = performQuery(postgres, "SELECT foo FROM bar"); @@ -69,9 +78,10 @@ public void testExplicitInitScript() throws SQLException { @Test public void testWithAdditionalUrlParamInJdbcUrl() { - try (PostgreSQLContainer postgres = new PostgreSQLContainer<>(POSTGRES_TEST_IMAGE) - .withUrlParam("charSet", "UNICODE")) { - + try ( + PostgreSQLContainer postgres = new PostgreSQLContainer<>(PostgreSQLTestImages.POSTGRES_TEST_IMAGE) + .withUrlParam("charSet", "UNICODE") + ) { postgres.start(); String jdbcUrl = postgres.getJdbcUrl(); assertThat(jdbcUrl, containsString("?")); diff --git a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java index 30d77c1a469..e73f6cc0f6b 100644 --- a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java +++ b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java @@ -1,5 +1,6 @@ package org.testcontainers.containers; +import com.google.common.base.Strings; import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.utility.DockerImageName; @@ -7,26 +8,28 @@ import java.sql.Connection; import java.sql.SQLException; import java.time.Duration; +import java.time.temporal.ChronoUnit; import java.util.HashSet; import java.util.Set; -import static com.google.common.base.Strings.nullToEmpty; -import static java.lang.String.format; -import static java.time.temporal.ChronoUnit.SECONDS; - /** * @deprecated Use {@code TrinoContainer} instead. */ @Deprecated public class PrestoContainer> extends JdbcDatabaseContainer { + public static final String NAME = "presto"; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("ghcr.io/trinodb/presto"); + public static final String IMAGE = "ghcr.io/trinodb/presto"; + public static final String DEFAULT_TAG = "344"; public static final Integer PRESTO_PORT = 8080; private String username = "test"; + private String catalog = null; /** @@ -43,12 +46,12 @@ public PrestoContainer(final String dockerImageName) { public PrestoContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); - this.waitStrategy = new LogMessageWaitStrategy() - .withRegEx(".*======== SERVER STARTED ========.*") - .withStartupTimeout(Duration.of(60, SECONDS)); + this.waitStrategy = + new LogMessageWaitStrategy() + .withRegEx(".*======== SERVER STARTED ========.*") + .withStartupTimeout(Duration.of(60, ChronoUnit.SECONDS)); addExposedPort(PRESTO_PORT); } @@ -66,7 +69,12 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return format("jdbc:presto://%s:%s/%s", getHost(), getMappedPort(PRESTO_PORT), nullToEmpty(catalog)); + return String.format( + "jdbc:presto://%s:%s/%s", + getHost(), + getMappedPort(PRESTO_PORT), + Strings.nullToEmpty(catalog) + ); } @Override diff --git a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainerProvider.java b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainerProvider.java index 8408aab4517..f37efb9438e 100644 --- a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainerProvider.java +++ b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainerProvider.java @@ -9,6 +9,7 @@ public class PrestoContainerProvider extends JdbcDatabaseContainerProvider { public static final String USER_PARAM = "user"; + public static final String PASSWORD_PARAM = "password"; @Override @@ -30,5 +31,4 @@ public JdbcDatabaseContainer newInstance(String tag) { public JdbcDatabaseContainer newInstance(ConnectionUrl connectionUrl) { return newInstanceFromConnectionUrl(connectionUrl, USER_PARAM, PASSWORD_PARAM); } - } diff --git a/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java b/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java index d5a4d9587a6..a54108ea1a2 100644 --- a/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java +++ b/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java @@ -3,7 +3,6 @@ import org.junit.Assert; import org.junit.Test; import org.testcontainers.PrestoTestImages; -import org.testcontainers.utility.DockerImageName; import java.sql.Connection; import java.sql.DriverManager; @@ -14,8 +13,6 @@ import java.util.Arrays; import java.util.List; -import static java.lang.Integer.parseInt; -import static java.lang.String.format; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -29,9 +26,11 @@ public class PrestoContainerTest { public void testSimple() throws Exception { try (PrestoContainer prestoSql = new PrestoContainer<>(PrestoTestImages.PRESTO_TEST_IMAGE)) { prestoSql.start(); - try (Connection connection = prestoSql.createConnection(); - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes")) { + try ( + Connection connection = prestoSql.createConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes") + ) { assertTrue("No result", resultSet.next()); assertEquals("Presto version", PrestoContainer.DEFAULT_TAG, resultSet.getString("node_version")); } @@ -40,13 +39,21 @@ public void testSimple() throws Exception { @Test public void testSpecificVersion() throws Exception { - try (PrestoContainer prestoSql = new PrestoContainer<>(PrestoTestImages.PRESTO_PREVIOUS_VERSION_TEST_IMAGE)) { + try ( + PrestoContainer prestoSql = new PrestoContainer<>(PrestoTestImages.PRESTO_PREVIOUS_VERSION_TEST_IMAGE) + ) { prestoSql.start(); - try (Connection connection = prestoSql.createConnection(); - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes")) { + try ( + Connection connection = prestoSql.createConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes") + ) { assertTrue("No result", resultSet.next()); - assertEquals("Presto version", PrestoTestImages.PRESTO_PREVIOUS_VERSION_TEST_IMAGE.getVersionPart(), resultSet.getString("node_version")); + assertEquals( + "Presto version", + PrestoTestImages.PRESTO_PREVIOUS_VERSION_TEST_IMAGE.getVersionPart(), + resultSet.getString("node_version") + ); } } } @@ -55,18 +62,26 @@ public void testSpecificVersion() throws Exception { public void testQueryMemoryAndTpch() throws SQLException { try (PrestoContainer prestoSql = new PrestoContainer<>(PrestoTestImages.PRESTO_TEST_IMAGE)) { prestoSql.start(); - try (Connection connection = prestoSql.createConnection(); - Statement statement = connection.createStatement()) { + try ( + Connection connection = prestoSql.createConnection(); + Statement statement = connection.createStatement() + ) { // Prepare data - statement.execute("CREATE TABLE memory.default.table_with_array AS SELECT 1 id, ARRAY[1, 42, 2, 42, 4, 42] my_array"); + statement.execute( + "CREATE TABLE memory.default.table_with_array AS SELECT 1 id, ARRAY[1, 42, 2, 42, 4, 42] my_array" + ); // Query Presto using newly created table and a builtin connector - try (ResultSet resultSet = statement.executeQuery("" + - "SELECT nationkey, element " + - "FROM tpch.tiny.nation " + - "JOIN memory.default.table_with_array twa ON nationkey = twa.id " + - "LEFT JOIN UNNEST(my_array) a(element) ON true " + - "ORDER BY element OFFSET 1 FETCH NEXT 3 ROWS WITH TIES ")) { + try ( + ResultSet resultSet = statement.executeQuery( + "" + + "SELECT nationkey, element " + + "FROM tpch.tiny.nation " + + "JOIN memory.default.table_with_array twa ON nationkey = twa.id " + + "LEFT JOIN UNNEST(my_array) a(element) ON true " + + "ORDER BY element OFFSET 1 FETCH NEXT 3 ROWS WITH TIES " + ) + ) { List actualElements = new ArrayList<>(); while (resultSet.next()) { actualElements.add(resultSet.getInt("element")); @@ -82,9 +97,11 @@ public void testInitScript() throws Exception { try (PrestoContainer prestoSql = new PrestoContainer<>(PrestoTestImages.PRESTO_TEST_IMAGE)) { prestoSql.withInitScript("initial.sql"); prestoSql.start(); - try (Connection connection = prestoSql.createConnection(); - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("SELECT a FROM memory.default.test_table")) { + try ( + Connection connection = prestoSql.createConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT a FROM memory.default.test_table") + ) { assertTrue("No result", resultSet.next()); assertEquals("Value", 12345678909324L, resultSet.getObject("a")); assertFalse("Too many result", resultSet.next()); @@ -94,19 +111,38 @@ public void testInitScript() throws Exception { @Test public void testTcJdbcUri() throws Exception { - try (Connection connection = DriverManager.getConnection(format("jdbc:tc:presto:%s://hostname/", PrestoContainer.DEFAULT_TAG))) { + try ( + Connection connection = DriverManager.getConnection( + String.format("jdbc:tc:presto:%s://hostname/", PrestoContainer.DEFAULT_TAG) + ) + ) { // Verify metadata with tc: JDBC connection URI - assertEquals(connection.getMetaData().getDatabaseMajorVersion(), parseInt(PrestoContainer.DEFAULT_TAG)); + assertEquals( + connection.getMetaData().getDatabaseMajorVersion(), + Integer.parseInt(PrestoContainer.DEFAULT_TAG) + ); // Verify transactions with tc: JDBC connection URI assertTrue("Is autocommit", connection.getAutoCommit()); connection.setAutoCommit(false); assertFalse("Is autocommit", connection.getAutoCommit()); - assertEquals("Transaction isolation", Connection.TRANSACTION_READ_UNCOMMITTED, connection.getTransactionIsolation()); + assertEquals( + "Transaction isolation", + Connection.TRANSACTION_READ_UNCOMMITTED, + connection.getTransactionIsolation() + ); try (Statement statement = connection.createStatement()) { - assertEquals("Update result", 0, statement.executeUpdate("CREATE TABLE memory.default.test_tc(a bigint)")); - try (ResultSet resultSet = statement.executeQuery("SELECT sum(cast(node_version AS bigint)) AS v FROM system.runtime.nodes")) { + assertEquals( + "Update result", + 0, + statement.executeUpdate("CREATE TABLE memory.default.test_tc(a bigint)") + ); + try ( + ResultSet resultSet = statement.executeQuery( + "SELECT sum(cast(node_version AS bigint)) AS v FROM system.runtime.nodes" + ) + ) { assertTrue(resultSet.next()); assertEquals(PrestoContainer.DEFAULT_TAG, resultSet.getString("v")); assertFalse(resultSet.next()); @@ -117,7 +153,11 @@ public void testTcJdbcUri() throws Exception { } connection.setAutoCommit(true); assertTrue("Is autocommit", connection.getAutoCommit()); - assertEquals("Transaction isolation should be retained", Connection.TRANSACTION_READ_UNCOMMITTED, connection.getTransactionIsolation()); + assertEquals( + "Transaction isolation should be retained", + Connection.TRANSACTION_READ_UNCOMMITTED, + connection.getTransactionIsolation() + ); } } } diff --git a/modules/presto/src/test/java/org/testcontainers/jdbc/presto/PrestoJDBCDriverTest.java b/modules/presto/src/test/java/org/testcontainers/jdbc/presto/PrestoJDBCDriverTest.java index d1a64c92aaa..9c5dd712104 100644 --- a/modules/presto/src/test/java/org/testcontainers/jdbc/presto/PrestoJDBCDriverTest.java +++ b/modules/presto/src/test/java/org/testcontainers/jdbc/presto/PrestoJDBCDriverTest.java @@ -4,18 +4,18 @@ import org.junit.runners.Parameterized; import org.testcontainers.jdbc.AbstractJDBCDriverTest; +import java.util.Arrays; import java.util.EnumSet; -import static java.util.Arrays.asList; - @RunWith(Parameterized.class) public class PrestoJDBCDriverTest extends AbstractJDBCDriverTest { @Parameterized.Parameters(name = "{index} - {0}") public static Iterable data() { - return asList( - new Object[][]{ - {"jdbc:tc:presto:344://hostname/", EnumSet.of(Options.PmdKnownBroken)}, - }); + return Arrays.asList( + new Object[][] { // + { "jdbc:tc:presto:344://hostname/", EnumSet.of(Options.PmdKnownBroken) }, + } + ); } } diff --git a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java index b3c836b56a5..9b14e90d736 100644 --- a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java +++ b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java @@ -10,10 +10,13 @@ public class PulsarContainer extends GenericContainer { public static final int BROKER_PORT = 6650; + public static final int BROKER_HTTP_PORT = 8080; + public static final String METRICS_ENDPOINT = "/metrics"; private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("apachepulsar/pulsar"); + @Deprecated private static final String DEFAULT_TAG = "2.2.0"; @@ -37,7 +40,6 @@ public PulsarContainer(String pulsarVersion) { public PulsarContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DockerImageName.parse("apachepulsar/pulsar")); withExposedPorts(BROKER_PORT, BROKER_HTTP_PORT); diff --git a/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java b/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java index f2354d98ef7..6d739dfbf98 100644 --- a/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java +++ b/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java @@ -18,6 +18,7 @@ public class PulsarContainerTest { public static final String TEST_TOPIC = "test_topic"; + private static final DockerImageName PULSAR_IMAGE = DockerImageName.parse("apachepulsar/pulsar:2.2.0"); @Test @@ -33,9 +34,7 @@ public void shouldNotEnableFunctionsWorkerByDefault() throws Exception { try (PulsarContainer pulsar = new PulsarContainer("2.5.1")) { pulsar.start(); - PulsarAdmin pulsarAdmin = PulsarAdmin.builder() - .serviceHttpUrl(pulsar.getHttpServiceUrl()) - .build(); + PulsarAdmin pulsarAdmin = PulsarAdmin.builder().serviceHttpUrl(pulsar.getHttpServiceUrl()).build(); assertThatThrownBy(() -> pulsarAdmin.functions().getFunctions("public", "default")) .isInstanceOf(PulsarAdminException.class); @@ -47,36 +46,23 @@ public void shouldWaitForFunctionsWorkerStarted() throws Exception { try (PulsarContainer pulsar = new PulsarContainer("2.5.1").withFunctionsWorker()) { pulsar.start(); - PulsarAdmin pulsarAdmin = PulsarAdmin.builder() - .serviceHttpUrl(pulsar.getHttpServiceUrl()) - .build(); + PulsarAdmin pulsarAdmin = PulsarAdmin.builder().serviceHttpUrl(pulsar.getHttpServiceUrl()).build(); assertThat(pulsarAdmin.functions().getFunctions("public", "default")).hasSize(0); } } protected void testPulsarFunctionality(String pulsarBrokerUrl) throws Exception { - try ( - PulsarClient client = PulsarClient.builder() - .serviceUrl(pulsarBrokerUrl) - .build(); - Consumer consumer = client.newConsumer() - .topic(TEST_TOPIC) - .subscriptionName("test-subs") - .subscribe(); - Producer producer = client.newProducer() - .topic(TEST_TOPIC) - .create() + PulsarClient client = PulsarClient.builder().serviceUrl(pulsarBrokerUrl).build(); + Consumer consumer = client.newConsumer().topic(TEST_TOPIC).subscriptionName("test-subs").subscribe(); + Producer producer = client.newProducer().topic(TEST_TOPIC).create() ) { - producer.send("test containers".getBytes()); CompletableFuture future = consumer.receiveAsync(); Message message = future.get(5, TimeUnit.SECONDS); - assertThat(new String(message.getData())) - .isEqualTo("test containers"); + assertThat(new String(message.getData())).isEqualTo("test containers"); } } - } diff --git a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/CancellableSubscription.java b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/CancellableSubscription.java index e64d19a9f49..3f4fd7e4b1e 100644 --- a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/CancellableSubscription.java +++ b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/CancellableSubscription.java @@ -9,8 +9,7 @@ class CancellableSubscription implements Subscription { private final AtomicBoolean cancelled = new AtomicBoolean(); @Override - public void request(long n) { - } + public void request(long n) {} @Override public void cancel() { diff --git a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/ConnectionPublisher.java b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/ConnectionPublisher.java index 489d545429d..d1b1c0e43f3 100644 --- a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/ConnectionPublisher.java +++ b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/ConnectionPublisher.java @@ -56,8 +56,8 @@ synchronized void transitionTo(SubscriptionState newState) { } abstract class SubscriptionState implements Subscription { - void enter() { - } + + void enter() {} } class WaitRequestSubscriptionState extends SubscriptionState { @@ -68,8 +68,7 @@ public void request(long n) { } @Override - public void cancel() { - } + public void cancel() {} } class WaitFutureCompletionSubscriptionState extends SubscriptionState { @@ -93,8 +92,7 @@ void enter() { } @Override - public void request(long n) { - } + public void request(long n) {} @Override public void cancel() { diff --git a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/EmptySubscription.java b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/EmptySubscription.java index 7828d121c6f..5cd2ffbd894 100644 --- a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/EmptySubscription.java +++ b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/EmptySubscription.java @@ -6,12 +6,8 @@ enum EmptySubscription implements Subscription { INSTANCE; @Override - public void request(long n) { - - } + public void request(long n) {} @Override - public void cancel() { - - } + public void cancel() {} } diff --git a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/Hidden.java b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/Hidden.java index f642a60d67c..1eecb22a2c7 100644 --- a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/Hidden.java +++ b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/Hidden.java @@ -52,7 +52,8 @@ private ConnectionFactoryOptions removeProxying(ConnectionFactoryOptions options // when protocol does NOT contain COLON, the length becomes 1 String protocolDelegate = protocols.length == 2 ? protocols[1] : ""; - return ConnectionFactoryOptions.builder() + return ConnectionFactoryOptions + .builder() .from(options) .option(ConnectionFactoryOptions.DRIVER, driverDelegate) .option(ConnectionFactoryOptions.PROTOCOL, protocolDelegate) @@ -68,6 +69,5 @@ public boolean supports(ConnectionFactoryOptions options) { public String getDriver() { return DRIVER; } - } } diff --git a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/R2DBCDatabaseContainer.java b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/R2DBCDatabaseContainer.java index a303cbafff8..8bf0401fe69 100644 --- a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/R2DBCDatabaseContainer.java +++ b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/R2DBCDatabaseContainer.java @@ -4,6 +4,5 @@ import org.testcontainers.lifecycle.Startable; public interface R2DBCDatabaseContainer extends Startable { - ConnectionFactoryOptions configure(ConnectionFactoryOptions options); } diff --git a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/R2DBCDatabaseContainerProvider.java b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/R2DBCDatabaseContainerProvider.java index 287407dc15e..9d3efc17ac7 100644 --- a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/R2DBCDatabaseContainerProvider.java +++ b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/R2DBCDatabaseContainerProvider.java @@ -9,7 +9,6 @@ import javax.annotation.Nullable; public interface R2DBCDatabaseContainerProvider { - Option REUSABLE_OPTION = Option.valueOf("TC_REUSABLE"); Option IMAGE_TAG_OPTION = Option.valueOf("TC_IMAGE_TAG"); diff --git a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactory.java b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactory.java index 691775a94f8..bada076eec9 100644 --- a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactory.java +++ b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactory.java @@ -36,34 +36,36 @@ class TestcontainersR2DBCConnectionFactory implements ConnectionFactory, Closeab TestcontainersR2DBCConnectionFactory(ConnectionFactoryOptions options) { this.options = options; - containerProvider = StreamSupport.stream(ServiceLoader.load(R2DBCDatabaseContainerProvider.class).spliterator(), false) - .filter(it -> it.supports(options)) - .findAny() - .orElseThrow(() -> new IllegalArgumentException("Missing provider for " + options)); + containerProvider = + StreamSupport + .stream(ServiceLoader.load(R2DBCDatabaseContainerProvider.class).spliterator(), false) + .filter(it -> it.supports(options)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("Missing provider for " + options)); } @Override public Publisher create() { - return new ConnectionPublisher( - () -> { - if (future == null) { - synchronized (this) { - if (future == null) { - future = CompletableFuture.supplyAsync(() -> { - R2DBCDatabaseContainer container = containerProvider.createContainer(options); - container.start(); - return container; - }, EXECUTOR); - } + return new ConnectionPublisher(() -> { + if (future == null) { + synchronized (this) { + if (future == null) { + future = + CompletableFuture.supplyAsync( + () -> { + R2DBCDatabaseContainer container = containerProvider.createContainer(options); + container.start(); + return container; + }, + EXECUTOR + ); } } - return future.thenApply(it -> { - return ConnectionFactories.find( - it.configure(options) - ); - }); } - ); + return future.thenApply(it -> { + return ConnectionFactories.find(it.configure(options)); + }); + }); } @Override @@ -99,5 +101,4 @@ public Publisher close() { } }; } - } diff --git a/modules/r2dbc/src/test/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactoryTest.java b/modules/r2dbc/src/test/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactoryTest.java index 6fad93f0e2a..3772e6d49a6 100644 --- a/modules/r2dbc/src/test/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactoryTest.java +++ b/modules/r2dbc/src/test/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactoryTest.java @@ -48,16 +48,15 @@ public void reusesUntilConnectionFactoryIsClosed() { assertThat(updated).isEqualTo(1); - Flux select = Flux - .usingWhen( - Flux.defer(connectionFactory::create), - connection -> { - return Flux - .from(connection.createStatement("SELECT COUNT(*) FROM test").execute()) - .flatMap(it -> it.map((row, meta) -> (Long) row.get(0))); - }, - Connection::close - ); + Flux select = Flux.usingWhen( + Flux.defer(connectionFactory::create), + connection -> { + return Flux + .from(connection.createStatement("SELECT COUNT(*) FROM test").execute()) + .flatMap(it -> it.map((row, meta) -> (Long) row.get(0))); + }, + Connection::close + ); Long rows = select.blockFirst(); diff --git a/modules/r2dbc/src/testFixtures/java/org/testcontainers/r2dbc/AbstractR2DBCDatabaseContainerTest.java b/modules/r2dbc/src/testFixtures/java/org/testcontainers/r2dbc/AbstractR2DBCDatabaseContainerTest.java index c21c7078410..63603379e40 100644 --- a/modules/r2dbc/src/testFixtures/java/org/testcontainers/r2dbc/AbstractR2DBCDatabaseContainerTest.java +++ b/modules/r2dbc/src/testFixtures/java/org/testcontainers/r2dbc/AbstractR2DBCDatabaseContainerTest.java @@ -60,9 +60,7 @@ protected void runTestQuery(ConnectionFactory connectionFactory) { .flatMap(it -> it.map((row, meta) -> (Number) row.get(0))) .blockFirst(); - assertThat(result) - .isNotNull() - .returns(expected, Number::intValue); + assertThat(result).isNotNull().returns(expected, Number::intValue); } finally { if (connectionFactory instanceof Closeable) { Mono.from(((Closeable) connectionFactory).close()).block(); diff --git a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java index f5c63a267a9..c13dfdf1a24 100644 --- a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java +++ b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java @@ -11,13 +11,11 @@ import java.io.IOException; import java.time.Duration; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; -import static java.lang.String.join; -import static java.util.Arrays.asList; - /** * Testcontainer for RabbitMQ. * @@ -29,15 +27,21 @@ public class RabbitMQContainer extends GenericContainer { * The image defaults to the official RabbitmQ image: RabbitMQ. */ private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("rabbitmq"); + private static final String DEFAULT_TAG = "3.7.25-management-alpine"; private static final int DEFAULT_AMQP_PORT = 5672; + private static final int DEFAULT_AMQPS_PORT = 5671; + private static final int DEFAULT_HTTPS_PORT = 15671; + private static final int DEFAULT_HTTP_PORT = 15672; private String adminPassword = "guest"; + private String adminUsername = "guest"; + private final List> values = new ArrayList<>(); /** @@ -60,14 +64,12 @@ public RabbitMQContainer(String dockerImageName) { public RabbitMQContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); addExposedPorts(DEFAULT_AMQP_PORT, DEFAULT_AMQPS_PORT, DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT); - this.waitStrategy = Wait. - forLogMessage(".*Server startup complete.*", 1) - .withStartupTimeout(Duration.ofSeconds(60)); + this.waitStrategy = + Wait.forLogMessage(".*Server startup complete.*", 1).withStartupTimeout(Duration.ofSeconds(60)); } @Override @@ -79,7 +81,6 @@ protected void configure() { @Override protected void containerIsStarted(InspectContainerResponse containerInfo) { - values.forEach(command -> { try { ExecResult execResult = execInContainer(command.toArray(new String[0])); @@ -159,257 +160,394 @@ public RabbitMQContainer withAdminPassword(final String adminPassword) { } public enum SslVerification { - VERIFY_NONE("verify_none"), VERIFY_PEER("verify_peer"); + VERIFY_NONE("verify_none"), + VERIFY_PEER("verify_peer"); SslVerification(String value) { this.value = value; } - private final String value; + private final String value; } public RabbitMQContainer withSSL( - final MountableFile keyFile, - final MountableFile certFile, - final MountableFile caFile, - final SslVerification verify, - boolean failIfNoCert, - int verificationDepth) { - + final MountableFile keyFile, + final MountableFile certFile, + final MountableFile caFile, + final SslVerification verify, + boolean failIfNoCert, + int verificationDepth + ) { return withSSL(keyFile, certFile, caFile, verify, failIfNoCert) - .withEnv("RABBITMQ_SSL_DEPTH", String.valueOf(verificationDepth)); + .withEnv("RABBITMQ_SSL_DEPTH", String.valueOf(verificationDepth)); } public RabbitMQContainer withSSL( - final MountableFile keyFile, - final MountableFile certFile, - final MountableFile caFile, - final SslVerification verify, - boolean failIfNoCert) { - + final MountableFile keyFile, + final MountableFile certFile, + final MountableFile caFile, + final SslVerification verify, + boolean failIfNoCert + ) { return withSSL(keyFile, certFile, caFile, verify) - .withEnv("RABBITMQ_SSL_FAIL_IF_NO_PEER_CERT", String.valueOf(failIfNoCert)); + .withEnv("RABBITMQ_SSL_FAIL_IF_NO_PEER_CERT", String.valueOf(failIfNoCert)); } public RabbitMQContainer withSSL( - final MountableFile keyFile, - final MountableFile certFile, - final MountableFile caFile, - final SslVerification verify) { - + final MountableFile keyFile, + final MountableFile certFile, + final MountableFile caFile, + final SslVerification verify + ) { return withEnv("RABBITMQ_SSL_CACERTFILE", "/etc/rabbitmq/ca_cert.pem") - .withEnv("RABBITMQ_SSL_CERTFILE", "/etc/rabbitmq/rabbitmq_cert.pem") - .withEnv("RABBITMQ_SSL_KEYFILE", "/etc/rabbitmq/rabbitmq_key.pem") - .withEnv("RABBITMQ_SSL_VERIFY", verify.value) - .withCopyFileToContainer(certFile, "/etc/rabbitmq/rabbitmq_cert.pem") - .withCopyFileToContainer(caFile, "/etc/rabbitmq/ca_cert.pem") - .withCopyFileToContainer(keyFile, "/etc/rabbitmq/rabbitmq_key.pem"); + .withEnv("RABBITMQ_SSL_CERTFILE", "/etc/rabbitmq/rabbitmq_cert.pem") + .withEnv("RABBITMQ_SSL_KEYFILE", "/etc/rabbitmq/rabbitmq_key.pem") + .withEnv("RABBITMQ_SSL_VERIFY", verify.value) + .withCopyFileToContainer(certFile, "/etc/rabbitmq/rabbitmq_cert.pem") + .withCopyFileToContainer(caFile, "/etc/rabbitmq/ca_cert.pem") + .withCopyFileToContainer(keyFile, "/etc/rabbitmq/rabbitmq_key.pem"); } public RabbitMQContainer withPluginsEnabled(String... pluginNames) { - List command = new ArrayList<>(asList("rabbitmq-plugins", "enable")); - command.addAll(asList(pluginNames)); + List command = new ArrayList<>(Arrays.asList("rabbitmq-plugins", "enable")); + command.addAll(Arrays.asList(pluginNames)); values.add(command); return self(); } public RabbitMQContainer withBinding(String source, String destination) { - values.add(asList("rabbitmqadmin", "declare", "binding", - "source=" + source, - "destination=" + destination)); + values.add( + Arrays.asList("rabbitmqadmin", "declare", "binding", "source=" + source, "destination=" + destination) + ); return self(); } public RabbitMQContainer withBinding(String vhost, String source, String destination) { - values.add(asList("rabbitmqadmin", "--vhost=" + vhost, "declare", "binding", - "source=" + source, - "destination=" + destination)); + values.add( + Arrays.asList( + "rabbitmqadmin", + "--vhost=" + vhost, + "declare", + "binding", + "source=" + source, + "destination=" + destination + ) + ); return self(); } - public RabbitMQContainer withBinding(String source, String destination, Map arguments, String routingKey, String destinationType) { - values.add(asList("rabbitmqadmin", "declare", "binding", - "source=" + source, - "destination=" + destination, - "routing_key=" + routingKey, - "destination_type=" + destinationType, - "arguments=" + toJson(arguments))); + public RabbitMQContainer withBinding( + String source, + String destination, + Map arguments, + String routingKey, + String destinationType + ) { + values.add( + Arrays.asList( + "rabbitmqadmin", + "declare", + "binding", + "source=" + source, + "destination=" + destination, + "routing_key=" + routingKey, + "destination_type=" + destinationType, + "arguments=" + toJson(arguments) + ) + ); return self(); } - public RabbitMQContainer withBinding(String vhost, String source, String destination, Map arguments, String routingKey, String destinationType) { - values.add(asList("rabbitmqadmin", "--vhost=" + vhost, "declare", "binding", + public RabbitMQContainer withBinding( + String vhost, + String source, + String destination, + Map arguments, + String routingKey, + String destinationType + ) { + values.add( + Arrays.asList( + "rabbitmqadmin", + "--vhost=" + vhost, + "declare", + "binding", "source=" + source, "destination=" + destination, "routing_key=" + routingKey, "destination_type=" + destinationType, - "arguments=" + toJson(arguments))); + "arguments=" + toJson(arguments) + ) + ); return self(); } public RabbitMQContainer withParameter(String component, String name, String value) { - values.add(asList("rabbitmqadmin", "declare", "parameter", + values.add( + Arrays.asList( + "rabbitmqadmin", + "declare", + "parameter", "component=" + component, "name=" + name, - "value=" + value)); + "value=" + value + ) + ); return self(); } public RabbitMQContainer withPermission(String vhost, String user, String configure, String write, String read) { - values.add(asList("rabbitmqadmin", "declare", "permission", + values.add( + Arrays.asList( + "rabbitmqadmin", + "declare", + "permission", "vhost=" + vhost, "user=" + user, "configure=" + configure, "write=" + write, - "read=" + read)); + "read=" + read + ) + ); return self(); } public RabbitMQContainer withUser(String name, String password) { - values.add(asList("rabbitmqadmin", "declare", "user", - "name=" + name, - "password=" + password, - "tags=")); + values.add(Arrays.asList("rabbitmqadmin", "declare", "user", "name=" + name, "password=" + password, "tags=")); return self(); } public RabbitMQContainer withUser(String name, String password, Set tags) { - values.add(asList("rabbitmqadmin", "declare", "user", + values.add( + Arrays.asList( + "rabbitmqadmin", + "declare", + "user", "name=" + name, "password=" + password, - "tags=" + join(",", tags))); + "tags=" + String.join(",", tags) + ) + ); return self(); } public RabbitMQContainer withPolicy(String name, String pattern, Map definition) { - values.add(asList("rabbitmqadmin", "declare", "policy", + values.add( + Arrays.asList( + "rabbitmqadmin", + "declare", + "policy", "name=" + name, "pattern=" + pattern, - "definition=" + toJson(definition))); + "definition=" + toJson(definition) + ) + ); return self(); } public RabbitMQContainer withPolicy(String vhost, String name, String pattern, Map definition) { - values.add(asList("rabbitmqadmin", "declare", "policy", + values.add( + Arrays.asList( + "rabbitmqadmin", + "declare", + "policy", "--vhost=" + vhost, "name=" + name, "pattern=" + pattern, - "definition=" + toJson(definition))); + "definition=" + toJson(definition) + ) + ); return self(); } - public RabbitMQContainer withPolicy(String name, String pattern, Map definition, int priority, String applyTo) { - values.add(asList("rabbitmqadmin", "declare", "policy", + public RabbitMQContainer withPolicy( + String name, + String pattern, + Map definition, + int priority, + String applyTo + ) { + values.add( + Arrays.asList( + "rabbitmqadmin", + "declare", + "policy", "name=" + name, "pattern=" + pattern, "priority=" + priority, "apply-to=" + applyTo, - "definition=" + toJson(definition))); + "definition=" + toJson(definition) + ) + ); return self(); } public RabbitMQContainer withOperatorPolicy(String name, String pattern, Map definition) { - values.add(new ArrayList<>(asList("rabbitmqadmin", "declare", "operator_policy", - "name=" + name, - "pattern=" + pattern, - "definition=" + toJson(definition)))); + values.add( + new ArrayList<>( + Arrays.asList( + "rabbitmqadmin", + "declare", + "operator_policy", + "name=" + name, + "pattern=" + pattern, + "definition=" + toJson(definition) + ) + ) + ); return self(); } - public RabbitMQContainer withOperatorPolicy(String name, String pattern, Map definition, int priority, String applyTo) { - values.add(asList("rabbitmqadmin", "declare", "operator_policy", + public RabbitMQContainer withOperatorPolicy( + String name, + String pattern, + Map definition, + int priority, + String applyTo + ) { + values.add( + Arrays.asList( + "rabbitmqadmin", + "declare", + "operator_policy", "name=" + name, "pattern=" + pattern, "priority=" + priority, "apply-to=" + applyTo, - "definition=" + toJson(definition))); + "definition=" + toJson(definition) + ) + ); return self(); } public RabbitMQContainer withVhost(String name) { - values.add(asList("rabbitmqadmin", "declare", "vhost", - "name=" + name)); + values.add(Arrays.asList("rabbitmqadmin", "declare", "vhost", "name=" + name)); return self(); } public RabbitMQContainer withVhost(String name, boolean tracing) { - values.add(asList("rabbitmqadmin", "declare", "vhost", - "name=" + name, - "tracing=" + tracing)); + values.add(Arrays.asList("rabbitmqadmin", "declare", "vhost", "name=" + name, "tracing=" + tracing)); return self(); } public RabbitMQContainer withVhostLimit(String vhost, String name, int value) { - values.add(asList("rabbitmqadmin", "declare", "vhost_limit", - "vhost=" + vhost, - "name=" + name, - "value=" + value)); + values.add( + Arrays.asList("rabbitmqadmin", "declare", "vhost_limit", "vhost=" + vhost, "name=" + name, "value=" + value) + ); return self(); } public RabbitMQContainer withQueue(String name) { - values.add(asList("rabbitmqadmin", "declare", "queue", - "name=" + name)); + values.add(Arrays.asList("rabbitmqadmin", "declare", "queue", "name=" + name)); return self(); } public RabbitMQContainer withQueue(String vhost, String name) { - values.add(asList("rabbitmqadmin", "--vhost=" + vhost, "declare", "queue", - "name=" + name)); + values.add(Arrays.asList("rabbitmqadmin", "--vhost=" + vhost, "declare", "queue", "name=" + name)); return self(); } - public RabbitMQContainer withQueue(String name, boolean autoDelete, boolean durable, Map arguments) { - values.add(asList("rabbitmqadmin", "declare", "queue", + public RabbitMQContainer withQueue( + String name, + boolean autoDelete, + boolean durable, + Map arguments + ) { + values.add( + Arrays.asList( + "rabbitmqadmin", + "declare", + "queue", "name=" + name, "auto_delete=" + autoDelete, "durable=" + durable, - "arguments=" + toJson(arguments))); + "arguments=" + toJson(arguments) + ) + ); return self(); } - public RabbitMQContainer withQueue(String vhost, String name, boolean autoDelete, boolean durable, Map arguments) { - values.add(asList("rabbitmqadmin", "--vhost=" + vhost, "declare", "queue", - "name=" + name, - "auto_delete=" + autoDelete, - "durable=" + durable, - "arguments=" + toJson(arguments))); + public RabbitMQContainer withQueue( + String vhost, + String name, + boolean autoDelete, + boolean durable, + Map arguments + ) { + values.add( + Arrays.asList( + "rabbitmqadmin", + "--vhost=" + vhost, + "declare", + "queue", + "name=" + name, + "auto_delete=" + autoDelete, + "durable=" + durable, + "arguments=" + toJson(arguments) + ) + ); return self(); } public RabbitMQContainer withExchange(String name, String type) { - values.add(asList("rabbitmqadmin", "declare", "exchange", - "name=" + name, - "type=" + type)); + values.add(Arrays.asList("rabbitmqadmin", "declare", "exchange", "name=" + name, "type=" + type)); return self(); } public RabbitMQContainer withExchange(String vhost, String name, String type) { - values.add(asList("rabbitmqadmin", "--vhost=" + vhost, "declare", "exchange", - "name=" + name, - "type=" + type)); + values.add( + Arrays.asList("rabbitmqadmin", "--vhost=" + vhost, "declare", "exchange", "name=" + name, "type=" + type) + ); return self(); } - public RabbitMQContainer withExchange(String name, String type, boolean autoDelete, boolean internal, boolean durable, Map arguments) { - values.add(asList("rabbitmqadmin", "declare", "exchange", - "name=" + name, - "type=" + type, - "auto_delete=" + autoDelete, - "internal=" + internal, - "durable=" + durable, - "arguments=" + toJson(arguments))); + public RabbitMQContainer withExchange( + String name, + String type, + boolean autoDelete, + boolean internal, + boolean durable, + Map arguments + ) { + values.add( + Arrays.asList( + "rabbitmqadmin", + "declare", + "exchange", + "name=" + name, + "type=" + type, + "auto_delete=" + autoDelete, + "internal=" + internal, + "durable=" + durable, + "arguments=" + toJson(arguments) + ) + ); return self(); } - public RabbitMQContainer withExchange(String vhost, String name, String type, boolean autoDelete, boolean internal, boolean durable, Map arguments) { - values.add(asList("rabbitmqadmin", "--vhost=" + vhost, "declare", "exchange", + public RabbitMQContainer withExchange( + String vhost, + String name, + String type, + boolean autoDelete, + boolean internal, + boolean durable, + Map arguments + ) { + values.add( + Arrays.asList( + "rabbitmqadmin", + "--vhost=" + vhost, + "declare", + "exchange", "name=" + name, "type=" + type, "auto_delete=" + autoDelete, "internal=" + internal, "durable=" + durable, - "arguments=" + toJson(arguments))); + "arguments=" + toJson(arguments) + ) + ); return self(); } @@ -420,7 +558,6 @@ public RabbitMQContainer withExchange(String vhost, String name, String type, bo * @return This container. */ public RabbitMQContainer withRabbitMQConfig(MountableFile rabbitMQConf) { - return withRabbitMQConfigSysctl(rabbitMQConf); } diff --git a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java index d82051a94db..6bc935b4e6c 100644 --- a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java +++ b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java @@ -6,11 +6,9 @@ import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import org.junit.Test; +import org.testcontainers.containers.RabbitMQContainer.SslVerification; import org.testcontainers.utility.MountableFile; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManagerFactory; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -22,10 +20,12 @@ import java.security.cert.CertificateException; import java.util.Collections; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; + import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; -import static org.testcontainers.containers.RabbitMQContainer.SslVerification.VERIFY_PEER; -import static org.testcontainers.utility.MountableFile.forClasspathResource; /** * @author Martin Greber @@ -33,46 +33,55 @@ public class RabbitMQContainerTest { public static final int DEFAULT_AMQPS_PORT = 5671; + public static final int DEFAULT_AMQP_PORT = 5672; + public static final int DEFAULT_HTTPS_PORT = 15671; + public static final int DEFAULT_HTTP_PORT = 15672; @Test - public void shouldCreateRabbitMQContainer() - { + public void shouldCreateRabbitMQContainer() { try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { - assertThat(container.getAdminPassword()).isEqualTo("guest"); assertThat(container.getAdminUsername()).isEqualTo("guest"); container.start(); - assertThat(container.getAmqpsUrl()).isEqualTo( - String.format("amqps://%s:%d", container.getHost(), container.getMappedPort(DEFAULT_AMQPS_PORT))); - assertThat(container.getAmqpUrl()).isEqualTo( - String.format("amqp://%s:%d", container.getHost(), container.getMappedPort(DEFAULT_AMQP_PORT))); - assertThat(container.getHttpsUrl()).isEqualTo( - String.format("https://%s:%d", container.getHost(), container.getMappedPort(DEFAULT_HTTPS_PORT))); - assertThat(container.getHttpUrl()).isEqualTo( - String.format("http://%s:%d", container.getHost(), container.getMappedPort(DEFAULT_HTTP_PORT))); + assertThat(container.getAmqpsUrl()) + .isEqualTo( + String.format("amqps://%s:%d", container.getHost(), container.getMappedPort(DEFAULT_AMQPS_PORT)) + ); + assertThat(container.getAmqpUrl()) + .isEqualTo( + String.format("amqp://%s:%d", container.getHost(), container.getMappedPort(DEFAULT_AMQP_PORT)) + ); + assertThat(container.getHttpsUrl()) + .isEqualTo( + String.format("https://%s:%d", container.getHost(), container.getMappedPort(DEFAULT_HTTPS_PORT)) + ); + assertThat(container.getHttpUrl()) + .isEqualTo( + String.format("http://%s:%d", container.getHost(), container.getMappedPort(DEFAULT_HTTP_PORT)) + ); assertThat(container.getHttpsPort()).isEqualTo(container.getMappedPort(DEFAULT_HTTPS_PORT)); assertThat(container.getHttpPort()).isEqualTo(container.getMappedPort(DEFAULT_HTTP_PORT)); assertThat(container.getAmqpsPort()).isEqualTo(container.getMappedPort(DEFAULT_AMQPS_PORT)); assertThat(container.getAmqpPort()).isEqualTo(container.getMappedPort(DEFAULT_AMQP_PORT)); - assertThat(container.getLivenessCheckPortNumbers()).containsExactlyInAnyOrder( - container.getMappedPort(DEFAULT_AMQP_PORT), - container.getMappedPort(DEFAULT_AMQPS_PORT), - container.getMappedPort(DEFAULT_HTTP_PORT), - container.getMappedPort(DEFAULT_HTTPS_PORT) - ); + assertThat(container.getLivenessCheckPortNumbers()) + .containsExactlyInAnyOrder( + container.getMappedPort(DEFAULT_AMQP_PORT), + container.getMappedPort(DEFAULT_AMQPS_PORT), + container.getMappedPort(DEFAULT_HTTP_PORT), + container.getMappedPort(DEFAULT_HTTPS_PORT) + ); } } @Test - public void shouldCreateRabbitMQContainerWithExchange() throws IOException, InterruptedException - { + public void shouldCreateRabbitMQContainerWithExchange() throws IOException, InterruptedException { try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { container.withExchange("test-exchange", "direct"); @@ -84,11 +93,18 @@ public void shouldCreateRabbitMQContainerWithExchange() throws IOException, Inte } @Test - public void shouldCreateRabbitMQContainerWithExchangeInVhost() throws IOException, InterruptedException - { + public void shouldCreateRabbitMQContainerWithExchangeInVhost() throws IOException, InterruptedException { try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { container.withVhost("test-vhost"); - container.withExchange("test-vhost", "test-exchange", "direct", false, false, false, Collections.emptyMap()); + container.withExchange( + "test-vhost", + "test-exchange", + "direct", + false, + false, + false, + Collections.emptyMap() + ); container.start(); @@ -98,11 +114,10 @@ public void shouldCreateRabbitMQContainerWithExchangeInVhost() throws IOExceptio } @Test - public void shouldCreateRabbitMQContainerWithQueues() throws IOException, InterruptedException - { + public void shouldCreateRabbitMQContainerWithQueues() throws IOException, InterruptedException { try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { - - container.withQueue("queue-one") + container + .withQueue("queue-one") .withQueue("queue-two", false, true, ImmutableMap.of("x-message-ttl", 1000)); container.start(); @@ -124,7 +139,6 @@ public void shouldMountConfigurationFile() { } } - @Test public void shouldMountConfigurationFileErlang() { try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { @@ -135,7 +149,6 @@ public void shouldMountConfigurationFileErlang() { } } - @Test public void shouldMountConfigurationFileSysctl() { try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { @@ -147,132 +160,143 @@ public void shouldMountConfigurationFileSysctl() { } @Test - public void shouldStartTheWholeEnchilada() throws IOException, InterruptedException - { + public void shouldStartTheWholeEnchilada() throws IOException, InterruptedException { try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { container - .withVhost("vhost1") - .withVhostLimit("vhost1", "max-connections", 1) - .withVhost("vhost2", true) - .withExchange("direct-exchange", "direct") - .withExchange("topic-exchange", "topic") - .withExchange("vhost1", "topic-exchange-2", "topic", false, false, true, Collections.emptyMap()) - .withExchange("vhost2", "topic-exchange-3", "topic") - .withExchange("topic-exchange-4", "topic", false, false, true, Collections.emptyMap()) - .withQueue("queue1") - .withQueue("queue2", true, false, ImmutableMap.of("x-message-ttl", 1000)) - .withQueue("vhost1", "queue3", true, false, ImmutableMap.of("x-message-ttl", 1000)) - .withQueue("vhost2", "queue4") - .withBinding("direct-exchange", "queue1") - .withBinding("vhost1", "topic-exchange-2", "queue3") - .withBinding("vhost2", "topic-exchange-3", "queue4", Collections.emptyMap(), "ss7", "queue") - .withUser("user1", "password1") - .withUser("user2", "password2", ImmutableSet.of("administrator")) - .withPermission("vhost1", "user1", ".*", ".*", ".*") - .withPolicy("max length policy", "^dog", ImmutableMap.of("max-length", 1), 1, "queues") - .withPolicy("alternate exchange policy", "^direct-exchange", ImmutableMap.of("alternate-exchange", "amq.direct")) - .withPolicy("vhost2", "ha-all", ".*", ImmutableMap.of("ha-mode", "all", "ha-sync-mode", "automatic")) - .withOperatorPolicy("operator policy 1", "^queue1", ImmutableMap.of("message-ttl", 1000), 1, "queues") - .withPluginsEnabled("rabbitmq_shovel", "rabbitmq_random_exchange"); + .withVhost("vhost1") + .withVhostLimit("vhost1", "max-connections", 1) + .withVhost("vhost2", true) + .withExchange("direct-exchange", "direct") + .withExchange("topic-exchange", "topic") + .withExchange("vhost1", "topic-exchange-2", "topic", false, false, true, Collections.emptyMap()) + .withExchange("vhost2", "topic-exchange-3", "topic") + .withExchange("topic-exchange-4", "topic", false, false, true, Collections.emptyMap()) + .withQueue("queue1") + .withQueue("queue2", true, false, ImmutableMap.of("x-message-ttl", 1000)) + .withQueue("vhost1", "queue3", true, false, ImmutableMap.of("x-message-ttl", 1000)) + .withQueue("vhost2", "queue4") + .withBinding("direct-exchange", "queue1") + .withBinding("vhost1", "topic-exchange-2", "queue3") + .withBinding("vhost2", "topic-exchange-3", "queue4", Collections.emptyMap(), "ss7", "queue") + .withUser("user1", "password1") + .withUser("user2", "password2", ImmutableSet.of("administrator")) + .withPermission("vhost1", "user1", ".*", ".*", ".*") + .withPolicy("max length policy", "^dog", ImmutableMap.of("max-length", 1), 1, "queues") + .withPolicy( + "alternate exchange policy", + "^direct-exchange", + ImmutableMap.of("alternate-exchange", "amq.direct") + ) + .withPolicy("vhost2", "ha-all", ".*", ImmutableMap.of("ha-mode", "all", "ha-sync-mode", "automatic")) + .withOperatorPolicy("operator policy 1", "^queue1", ImmutableMap.of("message-ttl", 1000), 1, "queues") + .withPluginsEnabled("rabbitmq_shovel", "rabbitmq_random_exchange"); container.start(); - assertThat(container.execInContainer("rabbitmqadmin", "list", "queues") - .getStdout()) + assertThat(container.execInContainer("rabbitmqadmin", "list", "queues").getStdout()) .contains("queue1", "queue2", "queue3", "queue4"); - assertThat(container.execInContainer("rabbitmqadmin", "list", "exchanges") - .getStdout()) - .contains("direct-exchange", "topic-exchange", "topic-exchange-2", "topic-exchange-3", "topic-exchange-4"); + assertThat(container.execInContainer("rabbitmqadmin", "list", "exchanges").getStdout()) + .contains( + "direct-exchange", + "topic-exchange", + "topic-exchange-2", + "topic-exchange-3", + "topic-exchange-4" + ); - assertThat(container.execInContainer("rabbitmqadmin", "list", "bindings") - .getStdout()) + assertThat(container.execInContainer("rabbitmqadmin", "list", "bindings").getStdout()) .contains("direct-exchange", "topic-exchange-2", "topic-exchange-3"); - assertThat(container.execInContainer("rabbitmqadmin", "list", "users") - .getStdout()) + assertThat(container.execInContainer("rabbitmqadmin", "list", "users").getStdout()) .contains("user1", "user2"); - assertThat(container.execInContainer("rabbitmqadmin", "list", "policies") - .getStdout()) + assertThat(container.execInContainer("rabbitmqadmin", "list", "policies").getStdout()) .contains("max length policy", "alternate exchange policy"); - assertThat(container.execInContainer("rabbitmqadmin", "list", "policies", "--vhost=vhost2") - .getStdout()) - .contains("ha-all", "ha-sync-mode"); + assertThat(container.execInContainer("rabbitmqadmin", "list", "policies", "--vhost=vhost2").getStdout()) + .contains("ha-all", "ha-sync-mode"); - assertThat(container.execInContainer("rabbitmqadmin", "list", "operator_policies") - .getStdout()) + assertThat(container.execInContainer("rabbitmqadmin", "list", "operator_policies").getStdout()) .contains("operator policy 1"); - assertThat(container.execInContainer("rabbitmq-plugins", "is_enabled", "rabbitmq_shovel", "--quiet") - .getStdout()) + assertThat( + container.execInContainer("rabbitmq-plugins", "is_enabled", "rabbitmq_shovel", "--quiet").getStdout() + ) .contains("rabbitmq_shovel is enabled"); - assertThat(container.execInContainer("rabbitmq-plugins", "is_enabled", "rabbitmq_random_exchange", "--quiet") - .getStdout()) + assertThat( + container + .execInContainer("rabbitmq-plugins", "is_enabled", "rabbitmq_random_exchange", "--quiet") + .getStdout() + ) .contains("rabbitmq_random_exchange is enabled"); } } @Test - public void shouldThrowExceptionForDodgyJson() - { + public void shouldThrowExceptionForDodgyJson() { try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { - - assertThatCode(() -> - container.withQueue( - "queue2", - true, - false, - ImmutableMap.of("x-message-ttl", container)) - ).hasMessageStartingWith("Failed to convert arguments into json"); - + assertThatCode(() -> container.withQueue("queue2", true, false, ImmutableMap.of("x-message-ttl", container)) + ) + .hasMessageStartingWith("Failed to convert arguments into json"); } } @Test - public void shouldWorkWithSSL() - { + public void shouldWorkWithSSL() { try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { container.withSSL( - forClasspathResource("/certs/server_key.pem", 0644), - forClasspathResource("/certs/server_certificate.pem", 0644), - forClasspathResource("/certs/ca_certificate.pem", 0644), - VERIFY_PEER, + MountableFile.forClasspathResource("/certs/server_key.pem", 0644), + MountableFile.forClasspathResource("/certs/server_certificate.pem", 0644), + MountableFile.forClasspathResource("/certs/ca_certificate.pem", 0644), + SslVerification.VERIFY_PEER, true ); container.start(); assertThatCode(() -> { - ConnectionFactory connectionFactory = new ConnectionFactory(); - connectionFactory.useSslProtocol(createSslContext( - "certs/client_key.p12", "password", - "certs/truststore.jks", "password")); - connectionFactory.enableHostnameVerification(); - connectionFactory.setUri(container.getAmqpsUrl()); - connectionFactory.setPassword(container.getAdminPassword()); - Connection connection = connectionFactory.newConnection(); - Channel channel = connection.openChannel().orElseThrow(() -> new RuntimeException("Failed to Open channel")); - channel.close(); - connection.close(); - }).doesNotThrowAnyException(); + ConnectionFactory connectionFactory = new ConnectionFactory(); + connectionFactory.useSslProtocol( + createSslContext("certs/client_key.p12", "password", "certs/truststore.jks", "password") + ); + connectionFactory.enableHostnameVerification(); + connectionFactory.setUri(container.getAmqpsUrl()); + connectionFactory.setPassword(container.getAdminPassword()); + Connection connection = connectionFactory.newConnection(); + Channel channel = connection + .openChannel() + .orElseThrow(() -> new RuntimeException("Failed to Open channel")); + channel.close(); + connection.close(); + }) + .doesNotThrowAnyException(); } } - private SSLContext createSslContext(String keystoreFile, String keystorePassword, String truststoreFile, String truststorePassword) - throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException, KeyManagementException - { + private SSLContext createSslContext( + String keystoreFile, + String keystorePassword, + String truststoreFile, + String truststorePassword + ) + throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException, KeyManagementException { ClassLoader classLoader = getClass().getClassLoader(); KeyStore ks = KeyStore.getInstance("PKCS12"); - ks.load(new FileInputStream(new File(classLoader.getResource(keystoreFile).getFile())), keystorePassword.toCharArray()); + ks.load( + new FileInputStream(new File(classLoader.getResource(keystoreFile).getFile())), + keystorePassword.toCharArray() + ); KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); kmf.init(ks, "password".toCharArray()); KeyStore trustStore = KeyStore.getInstance("PKCS12"); - trustStore.load(new FileInputStream(new File(classLoader.getResource(truststoreFile).getFile())), truststorePassword.toCharArray()); + trustStore.load( + new FileInputStream(new File(classLoader.getResource(truststoreFile).getFile())), + truststorePassword.toCharArray() + ); TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); tmf.init(trustStore); diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index fc3778385cd..08d7ab053c6 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -1,22 +1,10 @@ package org.testcontainers.containers; -import static java.time.temporal.ChronoUnit.SECONDS; - import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.model.AccessMode; import com.github.dockerjava.api.model.Bind; import com.github.dockerjava.api.model.Volume; import com.google.common.collect.ImmutableSet; -import java.io.File; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.file.Files; -import java.time.Duration; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.TimeUnit; - import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.SystemUtils; import org.jetbrains.annotations.NotNull; @@ -41,40 +29,67 @@ import org.testcontainers.utility.ComparableVersion; import org.testcontainers.utility.DockerImageName; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Files; +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.TimeUnit; + /** * A chrome/firefox/custom container based on SeleniumHQ's standalone container sets. *

* The container should expose Selenium remote control protocol and VNC. */ -public class BrowserWebDriverContainer> extends GenericContainer implements LinkableContainer, TestLifecycleAware { +public class BrowserWebDriverContainer> + extends GenericContainer + implements LinkableContainer, TestLifecycleAware { private static final DockerImageName CHROME_IMAGE = DockerImageName.parse("selenium/standalone-chrome"); + private static final DockerImageName FIREFOX_IMAGE = DockerImageName.parse("selenium/standalone-firefox"); + private static final DockerImageName CHROME_DEBUG_IMAGE = DockerImageName.parse("selenium/standalone-chrome-debug"); - private static final DockerImageName FIREFOX_DEBUG_IMAGE = DockerImageName.parse("selenium/standalone-firefox-debug"); + + private static final DockerImageName FIREFOX_DEBUG_IMAGE = DockerImageName.parse( + "selenium/standalone-firefox-debug" + ); + private static final DockerImageName[] COMPATIBLE_IMAGES = new DockerImageName[] { CHROME_IMAGE, FIREFOX_IMAGE, CHROME_DEBUG_IMAGE, - FIREFOX_DEBUG_IMAGE + FIREFOX_DEBUG_IMAGE, }; private static final String DEFAULT_PASSWORD = "secret"; + private static final int SELENIUM_PORT = 4444; + private static final int VNC_PORT = 5900; private static final String NO_PROXY_KEY = "no_proxy"; + private static final String TC_TEMP_DIR_PREFIX = "tc"; @Nullable private Capabilities capabilities; + private DockerImageName customImageName = null; @Nullable private RemoteWebDriver driver; + private VncRecordingMode recordingMode = VncRecordingMode.RECORD_FAILING; + private VncRecordingFormat recordingFormat; + private RecordingFileFactory recordingFileFactory; + private File vncRecordingDirectory; private VncRecordingContainer vncRecordingContainer = null; @@ -84,13 +99,16 @@ public class BrowserWebDriverContainer getLivenessCheckPorts() { @Override protected void configure() { - String seleniumVersion = SeleniumUtils.determineClasspathSeleniumVersion(); if (recordingMode != VncRecordingMode.SKIP) { - if (vncRecordingDirectory == null) { try { vncRecordingDirectory = Files.createTempDirectory(TC_TEMP_DIR_PREFIX).toFile(); @@ -178,7 +196,8 @@ protected void configure() { withNetwork(Network.SHARED); } - vncRecordingContainer = new VncRecordingContainer(this) + vncRecordingContainer = + new VncRecordingContainer(this) .withVncPassword(DEFAULT_PASSWORD) .withVncPort(VNC_PORT) .withVideoFormat(recordingFormat); @@ -188,7 +207,10 @@ protected void configure() { customImageName.assertCompatibleWith(COMPATIBLE_IMAGES); super.setDockerImageName(customImageName.asCanonicalNameString()); } else { - DockerImageName standardImageForCapabilities = getStandardImageForCapabilities(capabilities, seleniumVersion); + DockerImageName standardImageForCapabilities = getStandardImageForCapabilities( + capabilities, + seleniumVersion + ); super.setDockerImageName(standardImageForCapabilities.asCanonicalNameString()); } @@ -246,7 +268,9 @@ private static DockerImageName getStandardImageForCapabilities(Capabilities capa case BrowserType.FIREFOX: return (supportsVncWithoutDebugImage ? FIREFOX_IMAGE : FIREFOX_DEBUG_IMAGE).withTag(seleniumVersion); default: - throw new UnsupportedOperationException("Browser name must be 'chrome' or 'firefox'; provided '" + browserName + "' is not supported"); + throw new UnsupportedOperationException( + "Browser name must be 'chrome' or 'firefox'; provided '" + browserName + "' is not supported" + ); } } @@ -254,7 +278,7 @@ public URL getSeleniumAddress() { try { return new URL("http", getHost(), getMappedPort(SELENIUM_PORT), "/wd/hub"); } catch (MalformedURLException e) { - e.printStackTrace();// TODO + e.printStackTrace(); // TODO return null; } } @@ -292,15 +316,27 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { public synchronized RemoteWebDriver getWebDriver() { if (driver == null) { if (capabilities == null) { - logger().warn("No capabilities provided - this will cause an exception in future versions. Falling back to ChromeOptions"); + logger() + .warn( + "No capabilities provided - this will cause an exception in future versions. Falling back to ChromeOptions" + ); capabilities = new ChromeOptions(); } - driver = Unreliables.retryUntilSuccess(30, TimeUnit.SECONDS, () -> { - return Timeouts.getWithTimeout(10, TimeUnit.SECONDS, () -> { - return new RemoteWebDriver(getSeleniumAddress(), capabilities); - }); - }); + driver = + Unreliables.retryUntilSuccess( + 30, + TimeUnit.SECONDS, + () -> { + return Timeouts.getWithTimeout( + 10, + TimeUnit.SECONDS, + () -> { + return new RemoteWebDriver(getSeleniumAddress(), capabilities); + } + ); + } + ); } return driver; } @@ -348,7 +384,12 @@ private void retainRecordingIfNeeded(String prefix, boolean succeeded) { } if (shouldRecord) { - File recordingFile = recordingFileFactory.recordingFileForTest(vncRecordingDirectory, prefix, succeeded, vncRecordingContainer.getVideoFormat()); + File recordingFile = recordingFileFactory.recordingFileForTest( + vncRecordingDirectory, + prefix, + succeeded, + vncRecordingContainer.getVideoFormat() + ); LOGGER.info("Screen recordings for test {} will be stored at: {}", prefix, recordingFile); vncRecordingContainer.saveRecordingToFile(recordingFile); @@ -375,7 +416,11 @@ public SELF withRecordingMode(VncRecordingMode recordingMode, File vncRecordingD return withRecordingMode(recordingMode, vncRecordingDirectory, null); } - public SELF withRecordingMode(VncRecordingMode recordingMode, File vncRecordingDirectory, VncRecordingFormat recordingFormat) { + public SELF withRecordingMode( + VncRecordingMode recordingMode, + File vncRecordingDirectory, + VncRecordingFormat recordingFormat + ) { this.recordingMode = recordingMode; this.vncRecordingDirectory = vncRecordingDirectory; this.recordingFormat = recordingFormat; @@ -388,6 +433,8 @@ public SELF withRecordingFileFactory(RecordingFileFactory recordingFileFactory) } public enum VncRecordingMode { - SKIP, RECORD_ALL, RECORD_FAILING + SKIP, + RECORD_ALL, + RECORD_FAILING, } } diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/DefaultRecordingFileFactory.java b/modules/selenium/src/main/java/org/testcontainers/containers/DefaultRecordingFileFactory.java index 62a1abd38db..3815f5de3e1 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/DefaultRecordingFileFactory.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/DefaultRecordingFileFactory.java @@ -4,25 +4,36 @@ import java.text.SimpleDateFormat; import java.util.Date; -import static org.testcontainers.containers.VncRecordingContainer.VncRecordingFormat; - public class DefaultRecordingFileFactory implements RecordingFileFactory { private static final SimpleDateFormat filenameDateFormat = new SimpleDateFormat("YYYYMMdd-HHmmss"); + private static final String PASSED = "PASSED"; + private static final String FAILED = "FAILED"; - private static final String FILENAME_FORMAT = "%s-%s-%s.%s"; + private static final String FILENAME_FORMAT = "%s-%s-%s.%s"; @Override public File recordingFileForTest(File vncRecordingDirectory, String prefix, boolean succeeded) { - return recordingFileForTest(vncRecordingDirectory, prefix, succeeded, VncRecordingContainer.DEFAULT_RECORDING_FORMAT); + return recordingFileForTest( + vncRecordingDirectory, + prefix, + succeeded, + VncRecordingContainer.DEFAULT_RECORDING_FORMAT + ); } @Override - public File recordingFileForTest(File vncRecordingDirectory, String prefix, boolean succeeded, VncRecordingFormat recordingFormat) { + public File recordingFileForTest( + File vncRecordingDirectory, + String prefix, + boolean succeeded, + VncRecordingContainer.VncRecordingFormat recordingFormat + ) { final String resultMarker = succeeded ? PASSED : FAILED; - final String fileName = String.format(FILENAME_FORMAT, + final String fileName = String.format( + FILENAME_FORMAT, resultMarker, prefix, filenameDateFormat.format(new Date()), diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/RecordingFileFactory.java b/modules/selenium/src/main/java/org/testcontainers/containers/RecordingFileFactory.java index aff607cf23d..31946dac59e 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/RecordingFileFactory.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/RecordingFileFactory.java @@ -6,16 +6,23 @@ import java.io.File; public interface RecordingFileFactory { - @Deprecated default File recordingFileForTest(File vncRecordingDirectory, Description description, boolean succeeded) { - return recordingFileForTest(vncRecordingDirectory, description.getTestClass().getSimpleName() + "-" + description.getMethodName(), succeeded); + return recordingFileForTest( + vncRecordingDirectory, + description.getTestClass().getSimpleName() + "-" + description.getMethodName(), + succeeded + ); } - default File recordingFileForTest(File vncRecordingDirectory, String prefix, boolean succeeded, VncRecordingFormat recordingFormat) { + default File recordingFileForTest( + File vncRecordingDirectory, + String prefix, + boolean succeeded, + VncRecordingFormat recordingFormat + ) { return recordingFileForTest(vncRecordingDirectory, prefix, succeeded); } File recordingFileForTest(File vncRecordingDirectory, String prefix, boolean succeeded); - } diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/SeleniumUtils.java b/modules/selenium/src/main/java/org/testcontainers/containers/SeleniumUtils.java index 131f5a953ea..4fc0765cc86 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/SeleniumUtils.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/SeleniumUtils.java @@ -45,19 +45,24 @@ public static String determineClasspathSeleniumVersion() { } } } - } catch (Exception e) { LOGGER.debug("Failed to determine Selenium-Version from selenium-api JAR Manifest", e); } if (seleniumVersions.size() == 0) { - LOGGER.warn("Failed to determine Selenium version from classpath - will use default version of {}", DEFAULT_SELENIUM_VERSION); + LOGGER.warn( + "Failed to determine Selenium version from classpath - will use default version of {}", + DEFAULT_SELENIUM_VERSION + ); return DEFAULT_SELENIUM_VERSION; } String foundVersion = seleniumVersions.iterator().next(); if (seleniumVersions.size() > 1) { - LOGGER.warn("Multiple versions of Selenium API found on classpath - will select {}, but this may not be reliable", foundVersion); + LOGGER.warn( + "Multiple versions of Selenium API found on classpath - will select {}, but this may not be reliable", + foundVersion + ); } return foundVersion; @@ -76,7 +81,7 @@ public static String getSeleniumVersionFromManifest(Manifest manifest) { } // Compatibility Selenium > 3.X - if(seleniumVersion == null) { + if (seleniumVersion == null) { Attributes seleniumInfo = manifest.getAttributes("Selenium"); if (seleniumInfo != null) { seleniumVersion = seleniumInfo.getValue("Selenium-Version"); diff --git a/modules/selenium/src/test/java/org/testcontainers/containers/DefaultRecordingFileFactoryTest.java b/modules/selenium/src/test/java/org/testcontainers/containers/DefaultRecordingFileFactoryTest.java index 9abea56c5f2..0d8daaad219 100644 --- a/modules/selenium/src/test/java/org/testcontainers/containers/DefaultRecordingFileFactoryTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/containers/DefaultRecordingFileFactoryTest.java @@ -8,19 +8,15 @@ import java.io.File; import java.nio.file.Files; +import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; -import static java.lang.Boolean.FALSE; -import static java.lang.Boolean.TRUE; -import static java.lang.String.format; -import static java.time.LocalDateTime.now; -import static org.hamcrest.core.IsCollectionContaining.hasItem; +import static org.hamcrest.Matchers.hasItem; import static org.junit.Assert.assertThat; -import static org.junit.runner.Description.createTestDescription; @RunWith(Parameterized.class) @Value @@ -29,30 +25,47 @@ public class DefaultRecordingFileFactoryTest { private static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("YYYYMMdd-HHmmss"); private final DefaultRecordingFileFactory factory = new DefaultRecordingFileFactory(); + private final String methodName; + private final String prefix; + private final boolean success; @Parameterized.Parameters public static Collection data() { Collection args = new ArrayList<>(); - args.add(new Object[]{"testMethod1", "FAILED", FALSE}); - args.add(new Object[]{"testMethod2", "PASSED", TRUE}); + args.add(new Object[] { "testMethod1", "FAILED", Boolean.FALSE }); + args.add(new Object[] { "testMethod2", "PASSED", Boolean.TRUE }); return args; } @Test public void recordingFileThatShouldDescribeTheTestResultAtThePresentTime() throws Exception { File vncRecordingDirectory = Files.createTempDirectory("recording").toFile(); - Description description = createTestDescription(getClass().getCanonicalName(), methodName, Test.class); + Description description = Description.createTestDescription( + getClass().getCanonicalName(), + methodName, + Test.class + ); File recordingFile = factory.recordingFileForTest(vncRecordingDirectory, description, success); - String expectedFilePrefix = format("%s-%s-%s", prefix, getClass().getSimpleName(), methodName); + String expectedFilePrefix = String.format("%s-%s-%s", prefix, getClass().getSimpleName(), methodName); List expectedPossibleFileNames = Arrays.asList( - new File(vncRecordingDirectory, format("%s-%s.flv", expectedFilePrefix, now().format(DATETIME_FORMATTER))), - new File(vncRecordingDirectory, format("%s-%s.flv", expectedFilePrefix, now().minusSeconds(1L).format(DATETIME_FORMATTER))) + new File( + vncRecordingDirectory, + String.format("%s-%s.flv", expectedFilePrefix, LocalDateTime.now().format(DATETIME_FORMATTER)) + ), + new File( + vncRecordingDirectory, + String.format( + "%s-%s.flv", + expectedFilePrefix, + LocalDateTime.now().minusSeconds(1L).format(DATETIME_FORMATTER) + ) + ) ); assertThat(expectedPossibleFileNames, hasItem(recordingFile)); diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java index db369824442..d0865a06b09 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java @@ -1,10 +1,5 @@ package org.testcontainers.junit; -import static java.lang.String.format; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; - -import java.util.concurrent.TimeUnit; import org.jetbrains.annotations.NotNull; import org.junit.ClassRule; import org.openqa.selenium.By; @@ -16,6 +11,11 @@ import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.utility.DockerImageName; +import java.util.concurrent.TimeUnit; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; + /** * */ @@ -25,7 +25,9 @@ public class BaseWebDriverContainerTest { public static Network NETWORK = Network.newNetwork(); @ClassRule - public static GenericContainer HELLO_WORLD = new GenericContainer<>(DockerImageName.parse("testcontainers/helloworld:1.1.0")) + public static GenericContainer HELLO_WORLD = new GenericContainer<>( + DockerImageName.parse("testcontainers/helloworld:1.1.0") + ) .withNetwork(NETWORK) .withNetworkAliases("helloworld") .withExposedPorts(8080, 8081) @@ -39,17 +41,13 @@ protected static void doSimpleExplore(BrowserWebDriverContainer rule) { driver.get("http://helloworld:8080"); WebElement title = driver.findElement(By.tagName("h1")); - assertEquals("the index page contains the title 'Hello world'", - "Hello world", - title.getText().trim() - ); + assertEquals("the index page contains the title 'Hello world'", "Hello world", title.getText().trim()); } protected void assertBrowserNameIs(BrowserWebDriverContainer rule, String expectedName) { RemoteWebDriver driver = setupDriverFromRule(rule); String actual = driver.getCapabilities().getBrowserName(); - assertTrue(format("actual browser name is %s", actual), - actual.equals(expectedName)); + assertTrue(String.format("actual browser name is %s", actual), actual.equals(expectedName)); } @NotNull diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java index a53fa532f99..60d113dc290 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java @@ -9,10 +9,10 @@ import org.openqa.selenium.firefox.FirefoxOptions; import org.testcontainers.containers.BrowserWebDriverContainer; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; -import static java.util.Collections.emptyList; -import static java.util.stream.Collectors.toList; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; public class BrowserWebDriverContainerTest { @@ -40,7 +40,6 @@ public void provideDefaultNoProxyEnvironmentIfNotSet() { try ( BrowserWebDriverContainer chromeWithoutNoProxySet = new BrowserWebDriverContainer() .withCapabilities(new ChromeOptions()) - ) { chromeWithoutNoProxySet.start(); @@ -49,7 +48,6 @@ public void provideDefaultNoProxyEnvironmentIfNotSet() { } } - @Test public void createContainerWithShmVolume() { Assume.assumeFalse("SHM isn't mounted on Windows", SystemUtils.IS_OS_WINDOWS); @@ -76,19 +74,19 @@ public void createContainerWithoutShmVolume() { ) { webDriverContainer.start(); - assertEquals("Shared memory size is configured", - 512 * FileUtils.ONE_MB, - webDriverContainer.getShmSize()); + assertEquals("Shared memory size is configured", 512 * FileUtils.ONE_MB, webDriverContainer.getShmSize()); - assertEquals("No shm mounts present", emptyList(), shmVolumes(webDriverContainer)); + assertEquals("No shm mounts present", Collections.emptyList(), shmVolumes(webDriverContainer)); } } private List shmVolumes(final BrowserWebDriverContainer container) { - return container.getContainerInfo().getMounts() + return container + .getContainerInfo() + .getMounts() .stream() // destination path is always /dev/shm .filter(m -> m.getDestination().getPath().equals("/dev/shm")) - .collect(toList()); + .collect(Collectors.toList()); } } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java index fdf56aa0134..b2b7d8d6b1c 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java @@ -1,6 +1,5 @@ package org.testcontainers.junit; - import com.google.common.io.PatternFilenameFilter; import org.junit.Rule; import org.junit.Test; @@ -9,6 +8,7 @@ import org.junit.runner.RunWith; import org.openqa.selenium.chrome.ChromeOptions; import org.testcontainers.containers.BrowserWebDriverContainer; +import org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode; import org.testcontainers.containers.DefaultRecordingFileFactory; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.VncRecordingContainer; @@ -21,14 +21,12 @@ import java.io.File; import java.io.IOException; import java.time.Duration; +import java.time.temporal.ChronoUnit; import java.util.Optional; import java.util.concurrent.TimeUnit; -import static java.time.temporal.ChronoUnit.SECONDS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode.RECORD_ALL; -import static org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode.RECORD_FAILING; @RunWith(Enclosed.class) public class ChromeRecordingWebDriverContainerTest extends BaseWebDriverContainerTest { @@ -52,7 +50,7 @@ public void recordingTestThatShouldBeRecordedAndRetainedInFlvFormatAsDefault() t // To do this, simply add extra parameters to the rule constructor, so video will default to FLV format: BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() .withCapabilities(new ChromeOptions()) - .withRecordingMode(RECORD_ALL, target) + .withRecordingMode(VncRecordingMode.RECORD_ALL, target) // } .withRecordingFileFactory(new DefaultRecordingFileFactory()) .withNetwork(NETWORK) @@ -62,22 +60,26 @@ public void recordingTestThatShouldBeRecordedAndRetainedInFlvFormatAsDefault() t } } - private File[] runSimpleExploreInContainer(BrowserWebDriverContainer container, String fileNamePattern) throws InterruptedException { + private File[] runSimpleExploreInContainer(BrowserWebDriverContainer container, String fileNamePattern) + throws InterruptedException { container.start(); TimeUnit.MILLISECONDS.sleep(MINIMUM_VIDEO_DURATION_MILLISECONDS); doSimpleExplore(container); - container.afterTest(new TestDescription() { - @Override - public String getTestId() { - return getFilesystemFriendlyName(); - } - - @Override - public String getFilesystemFriendlyName() { - return "ChromeThatRecordsAllTests-recordingTestThatShouldBeRecordedAndRetained"; - } - }, Optional.empty()); + container.afterTest( + new TestDescription() { + @Override + public String getTestId() { + return getFilesystemFriendlyName(); + } + + @Override + public String getFilesystemFriendlyName() { + return "ChromeThatRecordsAllTests-recordingTestThatShouldBeRecordedAndRetained"; + } + }, + Optional.empty() + ); return vncRecordingDirectory.getRoot().listFiles(new PatternFilenameFilter(fileNamePattern)); } @@ -90,7 +92,7 @@ public void recordingTestShouldHaveFlvExtension() throws InterruptedException { // Set (explicitly) FLV format for recorded video: BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() .withCapabilities(new ChromeOptions()) - .withRecordingMode(RECORD_ALL, target, VncRecordingFormat.FLV) + .withRecordingMode(VncRecordingMode.RECORD_ALL, target, VncRecordingFormat.FLV) // } .withRecordingFileFactory(new DefaultRecordingFileFactory()) .withNetwork(NETWORK) @@ -108,7 +110,7 @@ public void recordingTestShouldHaveMp4Extension() throws InterruptedException { // Set MP4 format for recorded video: BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() .withCapabilities(new ChromeOptions()) - .withRecordingMode(RECORD_ALL, target, VncRecordingFormat.MP4) + .withRecordingMode(VncRecordingMode.RECORD_ALL, target, VncRecordingFormat.MP4) // } .withRecordingFileFactory(new DefaultRecordingFileFactory()) .withNetwork(NETWORK) @@ -124,7 +126,7 @@ public void recordingTestThatShouldHaveCorrectDuration() throws IOException, Int try ( BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() .withCapabilities(new ChromeOptions()) - .withRecordingMode(RECORD_ALL, vncRecordingDirectory.getRoot()) + .withRecordingMode(VncRecordingMode.RECORD_ALL, vncRecordingDirectory.getRoot()) .withRecordingFileFactory(new DefaultRecordingFileFactory()) .withNetwork(NETWORK) ) { @@ -132,19 +134,28 @@ public void recordingTestThatShouldHaveCorrectDuration() throws IOException, Int mountableFile = MountableFile.forHostPath(recordedFiles[0].getCanonicalPath()); } - try (GenericContainer container = new GenericContainer<>(DockerImageName.parse("testcontainers/vnc-recorder:1.2.0"))) { + try ( + GenericContainer container = new GenericContainer<>( + DockerImageName.parse("testcontainers/vnc-recorder:1.2.0") + ) + ) { String recordFileContainerPath = "/tmp/chromeTestRecord.flv"; - container.withCopyFileToContainer(mountableFile, recordFileContainerPath) - .withCreateContainerCmdModifier(createContainerCmd -> createContainerCmd.withEntrypoint("ffmpeg")) - .withCommand("-i", recordFileContainerPath, "-f", "null", "-") - .waitingFor(new LogMessageWaitStrategy() - .withRegEx(".*Duration.*") - .withStartupTimeout(Duration.of(60, SECONDS))) - .start(); + container + .withCopyFileToContainer(mountableFile, recordFileContainerPath) + .withCreateContainerCmdModifier(createContainerCmd -> createContainerCmd.withEntrypoint("ffmpeg")) + .withCommand("-i", recordFileContainerPath, "-f", "null", "-") + .waitingFor( + new LogMessageWaitStrategy() + .withRegEx(".*Duration.*") + .withStartupTimeout(Duration.of(60, ChronoUnit.SECONDS)) + ) + .start(); String ffmpegOutput = container.getLogs(); - assertTrue("Duration is incorrect in:\n " + ffmpegOutput, - ffmpegOutput.contains("Duration: 00:") && !(ffmpegOutput.contains("Duration: 00:00:00.00"))); + assertTrue( + "Duration is incorrect in:\n " + ffmpegOutput, + ffmpegOutput.contains("Duration: 00:") && !(ffmpegOutput.contains("Duration: 00:00:00.00")) + ); } } } @@ -180,7 +191,7 @@ public void recordingTestThatShouldBeRecordedAndRetained() throws InterruptedExc // or if you only want videos for test failures: BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() .withCapabilities(new ChromeOptions()) - .withRecordingMode(RECORD_FAILING, target) + .withRecordingMode(VncRecordingMode.RECORD_FAILING, target) // } .withRecordingFileFactory(new DefaultRecordingFileFactory()) .withNetwork(NETWORK) @@ -189,25 +200,26 @@ public void recordingTestThatShouldBeRecordedAndRetained() throws InterruptedExc TimeUnit.MILLISECONDS.sleep(MINIMUM_VIDEO_DURATION_MILLISECONDS); doSimpleExplore(chrome); - chrome.afterTest(new TestDescription() { - @Override - public String getTestId() { - return getFilesystemFriendlyName(); - } - - @Override - public String getFilesystemFriendlyName() { - return "ChromeThatRecordsFailingTests-recordingTestThatShouldBeRecordedAndRetained"; - } - }, Optional.of(new RuntimeException("Force writing of video file."))); + chrome.afterTest( + new TestDescription() { + @Override + public String getTestId() { + return getFilesystemFriendlyName(); + } + + @Override + public String getFilesystemFriendlyName() { + return "ChromeThatRecordsFailingTests-recordingTestThatShouldBeRecordedAndRetained"; + } + }, + Optional.of(new RuntimeException("Force writing of video file.")) + ); String[] files = vncRecordingDirectory.getRoot().list(new PatternFilenameFilter("FAILED-.*\\.flv")); assertEquals("Recorded file not found", 1, files.length); } - } - private static class CustomRecordingFileFactory extends DefaultRecordingFileFactory { - } + private static class CustomRecordingFileFactory extends DefaultRecordingFileFactory {} } } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java index bea1909a145..eb9872b06e9 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java @@ -15,7 +15,7 @@ public class ChromeWebDriverContainerTest extends BaseWebDriverContainerTest { @Rule public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() .withCapabilities(new ChromeOptions()) - // } + // } .withNetwork(NETWORK); @Before diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/ContainerWithoutCapabilitiesTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/ContainerWithoutCapabilitiesTest.java index b1214d18b60..d20f8c3aad8 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/ContainerWithoutCapabilitiesTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/ContainerWithoutCapabilitiesTest.java @@ -4,11 +4,10 @@ import org.junit.Test; import org.testcontainers.containers.BrowserWebDriverContainer; -public class ContainerWithoutCapabilitiesTest extends BaseWebDriverContainerTest{ +public class ContainerWithoutCapabilitiesTest extends BaseWebDriverContainerTest { @Rule - public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() - .withNetwork(NETWORK); + public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>().withNetwork(NETWORK); @Test public void chromeIsStartedIfNoCapabilitiesProvided() { @@ -16,7 +15,7 @@ public void chromeIsStartedIfNoCapabilitiesProvided() { } @Test - public void simpleExploreTestWhenNoCapabilitiesProvided(){ + public void simpleExploreTestWhenNoCapabilitiesProvided() { doSimpleExplore(chrome); } } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/CustomWaitTimeoutWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/CustomWaitTimeoutWebDriverContainerTest.java index 8448dc40d53..362be6550e4 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/CustomWaitTimeoutWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/CustomWaitTimeoutWebDriverContainerTest.java @@ -6,8 +6,7 @@ import org.testcontainers.containers.BrowserWebDriverContainer; import java.time.Duration; - -import static java.time.temporal.ChronoUnit.SECONDS; +import java.time.temporal.ChronoUnit; /** * @@ -17,7 +16,7 @@ public class CustomWaitTimeoutWebDriverContainerTest extends BaseWebDriverContai @Rule public BrowserWebDriverContainer chromeWithCustomTimeout = new BrowserWebDriverContainer<>() .withCapabilities(new ChromeOptions()) - .withStartupTimeout(Duration.of(30, SECONDS)) + .withStartupTimeout(Duration.of(30, ChronoUnit.SECONDS)) .withNetwork(NETWORK); @Test diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/FirefoxWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/FirefoxWebDriverContainerTest.java index 1cffec16bc6..e56b33a1704 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/FirefoxWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/FirefoxWebDriverContainerTest.java @@ -14,8 +14,8 @@ public class FirefoxWebDriverContainerTest extends BaseWebDriverContainerTest { // junitRule { @Rule public BrowserWebDriverContainer firefox = new BrowserWebDriverContainer<>() - .withCapabilities(new FirefoxOptions()) - // } + .withCapabilities(new FirefoxOptions()) + // } .withNetwork(NETWORK); @Before diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java index 3c071984dfb..2d2feb6aaff 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java @@ -22,11 +22,11 @@ public class LocalServerWebDriverContainerTest { public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() .withAccessToHost(true) .withCapabilities(new ChromeOptions()); + private int localPort; @Before public void setupLocalServer() throws Exception { - // Set up a local Jetty HTTP server Server server = new Server(); server.addConnector(new SocketConnector()); @@ -53,6 +53,10 @@ public void testConnection() { String headingText = driver.findElement(By.cssSelector("h1")).getText().trim(); - assertEquals("The hardcoded success message was found on a page fetched from a local server", "It worked", headingText); + assertEquals( + "The hardcoded success message was found on a page fetched from a local server", + "It worked", + headingText + ); } } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/SeleniumStartTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/SeleniumStartTest.java index a236a9696c1..04a31662099 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/SeleniumStartTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/SeleniumStartTest.java @@ -15,19 +15,19 @@ public class SeleniumStartTest { @Parameterized.Parameters(name = "tag: {0}") public static String[] data() { - return new String[]{"4.0.0", "3.4.0", "2.53.0", "2.45.0"}; + return new String[] { "4.0.0", "3.4.0", "2.53.0", "2.45.0" }; } - @Parameterized.Parameter() + @Parameterized.Parameter public String tag; @Test public void testAdditionalStartupString() { - final DockerImageName imageName = DockerImageName - .parse("selenium/standalone-chrome") - .withTag(tag); - try (BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>(imageName) - .withCapabilities(new ChromeOptions())) { + final DockerImageName imageName = DockerImageName.parse("selenium/standalone-chrome").withTag(tag); + try ( + BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>(imageName) + .withCapabilities(new ChromeOptions()) + ) { chrome.start(); } } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/SeleniumUtilsTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/SeleniumUtilsTest.java index d706d0fc390..4cbfca77d21 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/SeleniumUtilsTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/SeleniumUtilsTest.java @@ -8,7 +8,6 @@ import static org.rnorth.visibleassertions.VisibleAssertions.*; - /** * Created by Julien LAMY */ @@ -35,5 +34,4 @@ private void checkSeleniumVersionDetected(String urlManifest, String expectedVer String seleniumVersion = SeleniumUtils.getSeleniumVersionFromManifest(manifest); assertEquals("Check if Selenium Version detected is the correct one.", expectedVersion, seleniumVersion); } - } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java index 9a15c6b466d..e3db8ad5887 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java @@ -8,8 +8,9 @@ public class SpecificImageNameWebDriverContainerTest extends BaseWebDriverContainerTest { - private static final DockerImageName FIREFOX_IMAGE = DockerImageName - .parse("selenium/standalone-firefox:2.53.1-beryllium"); + private static final DockerImageName FIREFOX_IMAGE = DockerImageName.parse( + "selenium/standalone-firefox:2.53.1-beryllium" + ); @Rule public BrowserWebDriverContainer firefox = new BrowserWebDriverContainer<>(FIREFOX_IMAGE) diff --git a/modules/solr/src/main/java/org/testcontainers/containers/SolrClientUtils.java b/modules/solr/src/main/java/org/testcontainers/containers/SolrClientUtils.java index 4492ca0befe..f831cdd82e9 100644 --- a/modules/solr/src/main/java/org/testcontainers/containers/SolrClientUtils.java +++ b/modules/solr/src/main/java/org/testcontainers/containers/SolrClientUtils.java @@ -1,5 +1,13 @@ package org.testcontainers.containers; +import okhttp3.HttpUrl; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import org.apache.commons.io.IOUtils; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.URISyntaxException; @@ -11,15 +19,6 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; -import org.apache.commons.io.IOUtils; - -import okhttp3.HttpUrl; -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; - /** * Utils class which can create collections and configurations. * @@ -38,7 +37,13 @@ public class SolrClientUtils { * @param solrConfig the url under which the solrconfig.xml can be found * @param solrSchema the url under which the schema.xml can be found or null if the default schema should be used */ - public static void uploadConfiguration(String hostname, int port, String configurationName, URL solrConfig, URL solrSchema) throws URISyntaxException, IOException { + public static void uploadConfiguration( + String hostname, + int port, + String configurationName, + URL solrConfig, + URL solrSchema + ) throws URISyntaxException, IOException { Map parameters = new HashMap<>(); parameters.put("action", "UPLOAD"); parameters.put("name", configurationName); @@ -46,7 +51,6 @@ public static void uploadConfiguration(String hostname, int port, String configu byte[] configurationZipFile = generateConfigZipFile(solrConfig, solrSchema); executePost(url, configurationZipFile); - } /** @@ -58,7 +62,8 @@ public static void uploadConfiguration(String hostname, int port, String configu * @param configurationName the name of the configuration which should used to create the collection * or null if the default configuration should be used */ - public static void createCollection(String hostname, int port, String collectionName, String configurationName) throws URISyntaxException, IOException { + public static void createCollection(String hostname, int port, String collectionName, String configurationName) + throws URISyntaxException, IOException { Map parameters = new HashMap<>(); parameters.put("action", "CREATE"); parameters.put("name", collectionName); @@ -73,16 +78,11 @@ public static void createCollection(String hostname, int port, String collection } private static void executePost(HttpUrl url, byte[] data) throws IOException { + RequestBody requestBody = data == null + ? RequestBody.create(MediaType.parse("text/plain"), "") + : RequestBody.create(MediaType.parse("application/octet-stream"), data); + Request request = new Request.Builder().url(url).post(requestBody).build(); - RequestBody requestBody = data == null ? - RequestBody.create(MediaType.parse("text/plain"), "") : - RequestBody.create(MediaType.parse("application/octet-stream"), data); - ; - - Request request = new Request.Builder() - .url(url) - .post(requestBody) - .build(); Response response = httpClient.newCall(request).execute(); if (!response.isSuccessful()) { String responseBody = ""; @@ -97,7 +97,12 @@ private static void executePost(HttpUrl url, byte[] data) throws IOException { } } - private static HttpUrl generateSolrURL(String hostname, int port, List pathSegments, Map parameters) throws URISyntaxException { + private static HttpUrl generateSolrURL( + String hostname, + int port, + List pathSegments, + Map parameters + ) throws URISyntaxException { HttpUrl.Builder builder = new HttpUrl.Builder(); builder.scheme("http"); builder.host(hostname); diff --git a/modules/solr/src/main/java/org/testcontainers/containers/SolrClientUtilsException.java b/modules/solr/src/main/java/org/testcontainers/containers/SolrClientUtilsException.java index 5ba0865502c..a3b2cfd77c6 100644 --- a/modules/solr/src/main/java/org/testcontainers/containers/SolrClientUtilsException.java +++ b/modules/solr/src/main/java/org/testcontainers/containers/SolrClientUtilsException.java @@ -1,6 +1,7 @@ package org.testcontainers.containers; public class SolrClientUtilsException extends RuntimeException { + public SolrClientUtilsException(int statusCode, String msg) { super("Http Call Status: " + statusCode + "\n" + msg); } diff --git a/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java b/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java index e583c1f54ee..5161bce2738 100644 --- a/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java +++ b/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java @@ -8,11 +8,10 @@ import java.net.URL; import java.time.Duration; +import java.time.temporal.ChronoUnit; import java.util.HashSet; import java.util.Set; -import static java.time.temporal.ChronoUnit.SECONDS; - /** * SolrContainer allows a solr container to be launched and controlled. */ @@ -27,6 +26,7 @@ public class SolrContainer extends GenericContainer { public static final String DEFAULT_TAG = "8.3.0"; public static final Integer ZOOKEEPER_PORT = 9983; + public static final Integer SOLR_PORT = 8983; private SolrContainerConfiguration configuration; @@ -48,12 +48,12 @@ public SolrContainer(final String dockerImageName) { public SolrContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); - this.waitStrategy = new LogMessageWaitStrategy() - .withRegEx(".*o\\.e\\.j\\.s\\.Server Started.*") - .withStartupTimeout(Duration.of(60, SECONDS)); + this.waitStrategy = + new LogMessageWaitStrategy() + .withRegEx(".*o\\.e\\.j\\.s\\.Server Started.*") + .withStartupTimeout(Duration.of(60, ChronoUnit.SECONDS)); this.configuration = new SolrContainerConfiguration(); } @@ -129,7 +129,9 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { if (!configuration.isZookeeper()) { ExecResult result = execInContainer("solr", "create_core", "-c", configuration.getCollectionName()); if (result.getExitCode() != 0) { - throw new IllegalStateException("Unable to create solr core:\nStdout: " + result.getStdout() + "\nStderr:" + result.getStderr()); + throw new IllegalStateException( + "Unable to create solr core:\nStdout: " + result.getStdout() + "\nStderr:" + result.getStderr() + ); } return; } @@ -140,14 +142,15 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { getSolrPort(), configuration.getConfigurationName(), configuration.getSolrConfiguration(), - configuration.getSolrSchema()); + configuration.getSolrSchema() + ); } SolrClientUtils.createCollection( getHost(), getSolrPort(), configuration.getCollectionName(), - configuration.getConfigurationName()); + configuration.getConfigurationName() + ); } } - diff --git a/modules/solr/src/main/java/org/testcontainers/containers/SolrContainerConfiguration.java b/modules/solr/src/main/java/org/testcontainers/containers/SolrContainerConfiguration.java index 1f35ce2a810..698e956c8fe 100644 --- a/modules/solr/src/main/java/org/testcontainers/containers/SolrContainerConfiguration.java +++ b/modules/solr/src/main/java/org/testcontainers/containers/SolrContainerConfiguration.java @@ -1,9 +1,9 @@ package org.testcontainers.containers; -import java.net.URL; - import lombok.Data; +import java.net.URL; + /** * @author Simon Schneider */ @@ -11,8 +11,12 @@ public class SolrContainerConfiguration { private boolean zookeeper = true; + private String collectionName = "dummy"; + private String configurationName; + private URL solrConfiguration; + private URL solrSchema; } diff --git a/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java b/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java index 9f82e2c3af9..9b561e5cda9 100644 --- a/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java +++ b/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java @@ -10,9 +10,9 @@ import java.io.IOException; -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; /** * @author Simon Schneider @@ -20,6 +20,7 @@ public class SolrContainerTest { private static final DockerImageName SOLR_IMAGE = DockerImageName.parse("solr:8.3.0"); + private SolrClient client = null; @After @@ -60,7 +61,10 @@ public void solrCloudPingTest() throws IOException, SolrServerException { container.start(); // Do whatever you want with the client ... - SolrClient client = new Http2SolrClient.Builder("http://" + container.getHost() + ":" + container.getSolrPort() + "/solr").build(); + SolrClient client = new Http2SolrClient.Builder( + "http://" + container.getHost() + ":" + container.getSolrPort() + "/solr" + ) + .build(); SolrPingResponse response = client.ping("dummy"); // Stop the container. @@ -70,9 +74,10 @@ public void solrCloudPingTest() throws IOException, SolrServerException { private SolrClient getClient(SolrContainer container) { if (client == null) { - client = new Http2SolrClient.Builder("http://" + container.getHost() + ":" + container.getSolrPort() + "/solr").build(); + client = + new Http2SolrClient.Builder("http://" + container.getHost() + ":" + container.getSolrPort() + "/solr") + .build(); } return client; } - } diff --git a/modules/spock/src/main/groovy/org/testcontainers/spock/SpockTestDescription.groovy b/modules/spock/src/main/groovy/org/testcontainers/spock/SpockTestDescription.groovy index 85b58724000..6c71ac421c6 100644 --- a/modules/spock/src/main/groovy/org/testcontainers/spock/SpockTestDescription.groovy +++ b/modules/spock/src/main/groovy/org/testcontainers/spock/SpockTestDescription.groovy @@ -12,25 +12,25 @@ import org.testcontainers.lifecycle.TestDescription @PackageScope class SpockTestDescription implements TestDescription { - String specName - String featureName + String specName + String featureName - static SpockTestDescription fromTestDescription(IMethodInvocation invocation) { - return new SpockTestDescription([ - specName: invocation.spec.name, - featureName: invocation.feature.name - ]) - } + static SpockTestDescription fromTestDescription(IMethodInvocation invocation) { + return new SpockTestDescription([ + specName: invocation.spec.name, + featureName: invocation.feature.name + ]) + } - @Override - String getTestId() { - return getFilesystemFriendlyName() - } + @Override + String getTestId() { + return getFilesystemFriendlyName() + } - @Override - String getFilesystemFriendlyName() { - return [specName, featureName].collect { - URLEncoder.encode(it, 'UTF-8') - }.join('-') - } + @Override + String getFilesystemFriendlyName() { + return [specName, featureName].collect { + URLEncoder.encode(it, 'UTF-8') + }.join('-') + } } diff --git a/modules/spock/src/main/groovy/org/testcontainers/spock/Testcontainers.groovy b/modules/spock/src/main/groovy/org/testcontainers/spock/Testcontainers.groovy index 675ed68c4a6..2e70b6d589f 100644 --- a/modules/spock/src/main/groovy/org/testcontainers/spock/Testcontainers.groovy +++ b/modules/spock/src/main/groovy/org/testcontainers/spock/Testcontainers.groovy @@ -13,5 +13,4 @@ import java.lang.annotation.Target @Target([ElementType.TYPE, ElementType.METHOD]) @ExtensionAnnotation(TestcontainersExtension) @interface Testcontainers { - } diff --git a/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersExtension.groovy b/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersExtension.groovy index 1f66fd8f596..40392210f3a 100644 --- a/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersExtension.groovy +++ b/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersExtension.groovy @@ -7,26 +7,24 @@ import org.spockframework.runtime.model.SpecInfo class TestcontainersExtension extends AbstractAnnotationDrivenExtension { - @Override - void visitSpecAnnotation(Testcontainers annotation, SpecInfo spec) { - def listener = new ErrorListener() - def interceptor = new TestcontainersMethodInterceptor(spec, listener) - spec.addSetupSpecInterceptor(interceptor) - spec.addCleanupSpecInterceptor(interceptor) - spec.addSetupInterceptor(interceptor) - spec.addCleanupInterceptor(interceptor) - - spec.addListener(listener) - - } - - private class ErrorListener extends AbstractRunListener { - List errors = [] - - @Override - void error(ErrorInfo error) { - errors.add(error) - } - } - + @Override + void visitSpecAnnotation(Testcontainers annotation, SpecInfo spec) { + def listener = new ErrorListener() + def interceptor = new TestcontainersMethodInterceptor(spec, listener) + spec.addSetupSpecInterceptor(interceptor) + spec.addCleanupSpecInterceptor(interceptor) + spec.addSetupInterceptor(interceptor) + spec.addCleanupInterceptor(interceptor) + + spec.addListener(listener) + } + + private class ErrorListener extends AbstractRunListener { + List errors = [] + + @Override + void error(ErrorInfo error) { + errors.add(error) + } + } } diff --git a/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersMethodInterceptor.groovy b/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersMethodInterceptor.groovy index 16be002aeb1..2399a0a6e32 100644 --- a/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersMethodInterceptor.groovy +++ b/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersMethodInterceptor.groovy @@ -11,116 +11,116 @@ import org.testcontainers.spock.TestcontainersExtension.ErrorListener class TestcontainersMethodInterceptor extends AbstractMethodInterceptor { - private final SpecInfo spec - private final ErrorListener errorListener - - TestcontainersMethodInterceptor(SpecInfo spec, ErrorListener errorListener) { - this.spec = spec - this.errorListener = errorListener - } - - @Override - void interceptSetupSpecMethod(IMethodInvocation invocation) throws Throwable { - def containers = findAllContainers(true) - startContainers(containers, invocation) - - def compose = findAllComposeContainers(true) - startComposeContainers(compose, invocation) - - invocation.proceed() - } - - @Override - void interceptCleanupSpecMethod(IMethodInvocation invocation) throws Throwable { - def containers = findAllContainers(true) - stopContainers(containers, invocation) - - def compose = findAllComposeContainers(true) - stopComposeContainers(compose, invocation) - - invocation.proceed() - } - - @Override - void interceptSetupMethod(IMethodInvocation invocation) throws Throwable { - def containers = findAllContainers(false) - startContainers(containers, invocation) - - def compose = findAllComposeContainers(false) - startComposeContainers(compose, invocation) - - invocation.proceed() - } - - - @Override - void interceptCleanupMethod(IMethodInvocation invocation) throws Throwable { - def containers = findAllContainers(false) - stopContainers(containers, invocation) - - def compose = findAllComposeContainers(false) - stopComposeContainers(compose, invocation) - - invocation.proceed() - } - - private List findAllContainers(boolean shared) { - spec.allFields.findAll { FieldInfo f -> - GenericContainer.isAssignableFrom(f.type) && f.shared == shared - } - } - - private List findAllComposeContainers(boolean shared) { - spec.allFields.findAll { FieldInfo f -> - DockerComposeContainer.isAssignableFrom(f.type) && f.shared == shared - } - } - - private static void startContainers(List containers, IMethodInvocation invocation) { - containers.each { FieldInfo f -> - GenericContainer container = readContainerFromField(f, invocation) - if(!container.isRunning()){ - container.start() - } - - if (container instanceof TestLifecycleAware) { - def testDescription = SpockTestDescription.fromTestDescription(invocation) - (container as TestLifecycleAware).beforeTest(testDescription) - } - } - } - - private void stopContainers(List containers, IMethodInvocation invocation) { - containers.each { FieldInfo f -> - GenericContainer container = readContainerFromField(f, invocation) - - if (container instanceof TestLifecycleAware) { - // we assume first error is the one we want - def maybeException = Optional.ofNullable(errorListener.errors[0]?.exception) - def testDescription = SpockTestDescription.fromTestDescription(invocation) - (container as TestLifecycleAware).afterTest(testDescription, maybeException) - } - - container.stop() - } - } - - private static void startComposeContainers(List compose, IMethodInvocation invocation) { - compose.each { FieldInfo f -> - DockerComposeContainer c = f.readValue(invocation.instance) as DockerComposeContainer - c.start() - } - } - - private static void stopComposeContainers(List compose, IMethodInvocation invocation) { - compose.each { FieldInfo f -> - DockerComposeContainer c = f.readValue(invocation.instance) as DockerComposeContainer - c.stop() - } - } - - - private static GenericContainer readContainerFromField(FieldInfo f, IMethodInvocation invocation) { - f.readValue(invocation.instance) as GenericContainer - } + private final SpecInfo spec + private final ErrorListener errorListener + + TestcontainersMethodInterceptor(SpecInfo spec, ErrorListener errorListener) { + this.spec = spec + this.errorListener = errorListener + } + + @Override + void interceptSetupSpecMethod(IMethodInvocation invocation) throws Throwable { + def containers = findAllContainers(true) + startContainers(containers, invocation) + + def compose = findAllComposeContainers(true) + startComposeContainers(compose, invocation) + + invocation.proceed() + } + + @Override + void interceptCleanupSpecMethod(IMethodInvocation invocation) throws Throwable { + def containers = findAllContainers(true) + stopContainers(containers, invocation) + + def compose = findAllComposeContainers(true) + stopComposeContainers(compose, invocation) + + invocation.proceed() + } + + @Override + void interceptSetupMethod(IMethodInvocation invocation) throws Throwable { + def containers = findAllContainers(false) + startContainers(containers, invocation) + + def compose = findAllComposeContainers(false) + startComposeContainers(compose, invocation) + + invocation.proceed() + } + + + @Override + void interceptCleanupMethod(IMethodInvocation invocation) throws Throwable { + def containers = findAllContainers(false) + stopContainers(containers, invocation) + + def compose = findAllComposeContainers(false) + stopComposeContainers(compose, invocation) + + invocation.proceed() + } + + private List findAllContainers(boolean shared) { + spec.allFields.findAll { FieldInfo f -> + GenericContainer.isAssignableFrom(f.type) && f.shared == shared + } + } + + private List findAllComposeContainers(boolean shared) { + spec.allFields.findAll { FieldInfo f -> + DockerComposeContainer.isAssignableFrom(f.type) && f.shared == shared + } + } + + private static void startContainers(List containers, IMethodInvocation invocation) { + containers.each { FieldInfo f -> + GenericContainer container = readContainerFromField(f, invocation) + if(!container.isRunning()){ + container.start() + } + + if (container instanceof TestLifecycleAware) { + def testDescription = SpockTestDescription.fromTestDescription(invocation) + (container as TestLifecycleAware).beforeTest(testDescription) + } + } + } + + private void stopContainers(List containers, IMethodInvocation invocation) { + containers.each { FieldInfo f -> + GenericContainer container = readContainerFromField(f, invocation) + + if (container instanceof TestLifecycleAware) { + // we assume first error is the one we want + def maybeException = Optional.ofNullable(errorListener.errors[0]?.exception) + def testDescription = SpockTestDescription.fromTestDescription(invocation) + (container as TestLifecycleAware).afterTest(testDescription, maybeException) + } + + container.stop() + } + } + + private static void startComposeContainers(List compose, IMethodInvocation invocation) { + compose.each { FieldInfo f -> + DockerComposeContainer c = f.readValue(invocation.instance) as DockerComposeContainer + c.start() + } + } + + private static void stopComposeContainers(List compose, IMethodInvocation invocation) { + compose.each { FieldInfo f -> + DockerComposeContainer c = f.readValue(invocation.instance) as DockerComposeContainer + c.stop() + } + } + + + private static GenericContainer readContainerFromField(FieldInfo f, IMethodInvocation invocation) { + f.readValue(invocation.instance) as GenericContainer + } } diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/ComposeContainerIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/ComposeContainerIT.groovy index b952dd1c5dc..7294d534275 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/ComposeContainerIT.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/ComposeContainerIT.groovy @@ -9,27 +9,27 @@ import spock.lang.Specification @Testcontainers class ComposeContainerIT extends Specification { - DockerComposeContainer composeContainer = new DockerComposeContainer( - new File("src/test/resources/docker-compose.yml")) - .withExposedService("whoami_1", 80, Wait.forHttp("/")) + DockerComposeContainer composeContainer = new DockerComposeContainer( + new File("src/test/resources/docker-compose.yml")) + .withExposedService("whoami_1", 80, Wait.forHttp("/")) - String host + String host - int port + int port - def setup() { - host = composeContainer.getServiceHost("whoami_1", 80) - port = composeContainer.getServicePort("whoami_1", 80) - } + def setup() { + host = composeContainer.getServiceHost("whoami_1", 80) + port = composeContainer.getServicePort("whoami_1", 80) + } - def "running compose defined container is accessible on configured port"() { - given: "a http client" - def client = HttpClientBuilder.create().build() + def "running compose defined container is accessible on configured port"() { + given: "a http client" + def client = HttpClientBuilder.create().build() - when: "accessing web server" - def response = client.execute(new HttpGet("http://$host:$port")) + when: "accessing web server" + def response = client.execute(new HttpGet("http://$host:$port")) - then: "docker container is running and returns http status code 200" - response.statusLine.statusCode == 200 - } + then: "docker container is running and returns http status code 200" + response.statusLine.statusCode == 200 + } } diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/MySqlContainerIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/MySqlContainerIT.groovy index 639b46f5886..2f44408fed6 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/MySqlContainerIT.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/MySqlContainerIT.groovy @@ -14,13 +14,11 @@ import spock.lang.Specification @Testcontainers class MySqlContainerIT extends Specification { - @Shared - MySQLContainer mySQLContainer = new MySQLContainer(SpockTestImages.MYSQL_IMAGE) - - def "dummy test"() { - expect: - mySQLContainer.isRunning() - } - + @Shared + MySQLContainer mySQLContainer = new MySQLContainer(SpockTestImages.MYSQL_IMAGE) + def "dummy test"() { + expect: + mySQLContainer.isRunning() + } } diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/PostgresContainerIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/PostgresContainerIT.groovy index a886d552732..19edca4b459 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/PostgresContainerIT.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/PostgresContainerIT.groovy @@ -13,34 +13,34 @@ import java.sql.Statement @Testcontainers class PostgresContainerIT extends Specification { - @Shared - PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer(SpockTestImages.POSTGRES_TEST_IMAGE) - .withDatabaseName("foo") - .withUsername("foo") - .withPassword("secret") - - def "waits until postgres accepts jdbc connections"() { - - given: "a jdbc connection" - HikariConfig hikariConfig = new HikariConfig() - hikariConfig.setJdbcUrl(postgreSQLContainer.jdbcUrl) - hikariConfig.setUsername("foo") - hikariConfig.setPassword("secret") - HikariDataSource ds = new HikariDataSource(hikariConfig) - - when: "querying the database" - Statement statement = ds.getConnection().createStatement() - statement.execute("SELECT 1") - ResultSet resultSet = statement.getResultSet() - resultSet.next() - - then: "result is returned" - int resultSetInt = resultSet.getInt(1) - resultSetInt == 1 - - cleanup: - ds.close() - } + @Shared + PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer(SpockTestImages.POSTGRES_TEST_IMAGE) + .withDatabaseName("foo") + .withUsername("foo") + .withPassword("secret") + + def "waits until postgres accepts jdbc connections"() { + + given: "a jdbc connection" + HikariConfig hikariConfig = new HikariConfig() + hikariConfig.setJdbcUrl(postgreSQLContainer.jdbcUrl) + hikariConfig.setUsername("foo") + hikariConfig.setPassword("secret") + HikariDataSource ds = new HikariDataSource(hikariConfig) + + when: "querying the database" + Statement statement = ds.getConnection().createStatement() + statement.execute("SELECT 1") + ResultSet resultSet = statement.getResultSet() + resultSet.next() + + then: "result is returned" + int resultSetInt = resultSet.getInt(1) + resultSetInt == 1 + + cleanup: + ds.close() + } } // } diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/SharedComposeContainerIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/SharedComposeContainerIT.groovy index dd2ce9f5b3c..f728212278f 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/SharedComposeContainerIT.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/SharedComposeContainerIT.groovy @@ -10,28 +10,28 @@ import spock.lang.Specification @Testcontainers class SharedComposeContainerIT extends Specification { - @Shared - DockerComposeContainer composeContainer = new DockerComposeContainer( - new File("src/test/resources/docker-compose.yml")) - .withExposedService("whoami_1", 80, Wait.forHttp("/")) + @Shared + DockerComposeContainer composeContainer = new DockerComposeContainer( + new File("src/test/resources/docker-compose.yml")) + .withExposedService("whoami_1", 80, Wait.forHttp("/")) - String host + String host - int port + int port - def setup() { - host = composeContainer.getServiceHost("whoami_1", 80) - port = composeContainer.getServicePort("whoami_1", 80) - } + def setup() { + host = composeContainer.getServiceHost("whoami_1", 80) + port = composeContainer.getServicePort("whoami_1", 80) + } - def "running compose defined container is accessible on configured port"() { - given: "a http client" - def client = HttpClientBuilder.create().build() + def "running compose defined container is accessible on configured port"() { + given: "a http client" + def client = HttpClientBuilder.create().build() - when: "accessing web server" - def response = client.execute(new HttpGet("http://$host:$port")) + when: "accessing web server" + def response = client.execute(new HttpGet("http://$host:$port")) - then: "docker container is running and returns http status code 200" - response.statusLine.statusCode == 200 - } + then: "docker container is running and returns http status code 200" + response.statusLine.statusCode == 200 + } } diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy index b9ef64fe475..41432e5e514 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy @@ -3,8 +3,8 @@ package org.testcontainers.spock import org.testcontainers.utility.DockerImageName interface SpockTestImages { - DockerImageName MYSQL_IMAGE = DockerImageName.parse("mysql:5.7.34") - DockerImageName POSTGRES_TEST_IMAGE = DockerImageName.parse("postgres:9.6.12") - DockerImageName HTTPD_IMAGE = DockerImageName.parse("httpd:2.4-alpine") - DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.14") + DockerImageName MYSQL_IMAGE = DockerImageName.parse("mysql:5.7.34") + DockerImageName POSTGRES_TEST_IMAGE = DockerImageName.parse("postgres:9.6.12") + DockerImageName HTTPD_IMAGE = DockerImageName.parse("httpd:2.4-alpine") + DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.14") } diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/TestHierarchyIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/TestHierarchyIT.groovy index c28643910a4..567998281a7 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/TestHierarchyIT.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/TestHierarchyIT.groovy @@ -9,17 +9,15 @@ import spock.lang.Shared */ class TestHierarchyIT extends MySqlContainerIT { - @Shared - PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer(SpockTestImages.POSTGRES_TEST_IMAGE) - .withDatabaseName("foo") - .withUsername("foo") - .withPassword("secret") - - def "both containers are running"() { - expect: - postgreSQLContainer.isRunning() - mySQLContainer.isRunning() - - } + @Shared + PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer(SpockTestImages.POSTGRES_TEST_IMAGE) + .withDatabaseName("foo") + .withUsername("foo") + .withPassword("secret") + def "both containers are running"() { + expect: + postgreSQLContainer.isRunning() + mySQLContainer.isRunning() + } } diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareContainerMock.java b/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareContainerMock.java index eb628fd2f33..7502d097008 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareContainerMock.java +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareContainerMock.java @@ -8,20 +8,22 @@ import java.util.List; import java.util.Optional; -import static org.testcontainers.spock.SpockTestImages.TINY_IMAGE; - -public class TestLifecycleAwareContainerMock extends GenericContainer implements TestLifecycleAware { +public class TestLifecycleAwareContainerMock + extends GenericContainer + implements TestLifecycleAware { static final String BEFORE_TEST = "beforeTest"; + static final String AFTER_TEST = "afterTest"; final List lifecycleMethodCalls = new ArrayList<>(); + final List lifecycleFilesystemFriendlyNames = new ArrayList<>(); Throwable capturedThrowable; public TestLifecycleAwareContainerMock() { - super(TINY_IMAGE); + super(SpockTestImages.TINY_IMAGE); } @Override diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareIT.groovy index 18f7517ea3f..fae834ea422 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareIT.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareIT.groovy @@ -7,12 +7,12 @@ import spock.util.EmbeddedSpecRunner class TestLifecycleAwareIT extends Specification { - @Unroll("When failing test is #fails, afterTest receives '#filesystemFriendlyNames' and throwable starting with message '#errorMessageStartsWith'") - def "lifecycle awareness"() { - given: + @Unroll("When failing test is #fails, afterTest receives '#filesystemFriendlyNames' and throwable starting with message '#errorMessageStartsWith'") + def "lifecycle awareness"() { + given: - @Language("groovy") - String myTest = """ + @Language("groovy") + String myTest = """ import org.testcontainers.spock.Testcontainers import org.testcontainers.containers.GenericContainer import spock.lang.Specification @@ -28,28 +28,27 @@ class TestLifecycleAwareIT extends Specification { } } """ - and: - def container = new TestLifecycleAwareContainerMock() - System.properties["org.testcontainers.container"] = container - System.properties["org.testcontainers.shouldFail"] = fails - - when: "executing the test" - def runner = new EmbeddedSpecRunner(throwFailure: false) - runner.run(myTest) - - then: "mock container received lifecycle calls as expected" - container.lifecycleMethodCalls == ["beforeTest", "afterTest"] - container.lifecycleFilesystemFriendlyNames.join(",") == filesystemFriendlyNames - if (errorMessageStartsWith) { - assert container.capturedThrowable.message.startsWith(errorMessageStartsWith) - } else { - assert container.capturedThrowable == null - } - - where: - fails | filesystemFriendlyNames | errorMessageStartsWith - false | 'TestLifecycleAwareIT-perform+test' | null - true | 'TestLifecycleAwareIT-perform+test' | "Condition not satisfied:" - } - + and: + def container = new TestLifecycleAwareContainerMock() + System.properties["org.testcontainers.container"] = container + System.properties["org.testcontainers.shouldFail"] = fails + + when: "executing the test" + def runner = new EmbeddedSpecRunner(throwFailure: false) + runner.run(myTest) + + then: "mock container received lifecycle calls as expected" + container.lifecycleMethodCalls == ["beforeTest", "afterTest"] + container.lifecycleFilesystemFriendlyNames.join(",") == filesystemFriendlyNames + if (errorMessageStartsWith) { + assert container.capturedThrowable.message.startsWith(errorMessageStartsWith) + } else { + assert container.capturedThrowable == null + } + + where: + fails | filesystemFriendlyNames | errorMessageStartsWith + false | 'TestLifecycleAwareIT-perform+test' | null + true | 'TestLifecycleAwareIT-perform+test' | "Condition not satisfied:" + } } diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/TestcontainersRestartBetweenTestsIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/TestcontainersRestartBetweenTestsIT.groovy index 81ed0eea724..ebbb1de2474 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/TestcontainersRestartBetweenTestsIT.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/TestcontainersRestartBetweenTestsIT.groovy @@ -9,23 +9,22 @@ import spock.lang.Stepwise @Testcontainers class TestcontainersRestartBetweenTestsIT extends Specification { - GenericContainer genericContainer = new GenericContainer(SpockTestImages.HTTPD_IMAGE) - .withExposedPorts(80) + GenericContainer genericContainer = new GenericContainer(SpockTestImages.HTTPD_IMAGE) + .withExposedPorts(80) - @Shared - String lastContainerId + @Shared + String lastContainerId - def "retrieving first id"() { - when: - lastContainerId = genericContainer.containerId + def "retrieving first id"() { + when: + lastContainerId = genericContainer.containerId - then: - true - } - - def "containers is recreated between tests"() { - expect: - genericContainer.containerId != lastContainerId - } + then: + true + } + def "containers is recreated between tests"() { + expect: + genericContainer.containerId != lastContainerId + } } diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/TestcontainersSharedContainerIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/TestcontainersSharedContainerIT.groovy index 23b761fcc38..dd2f8443772 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/TestcontainersSharedContainerIT.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/TestcontainersSharedContainerIT.groovy @@ -13,35 +13,34 @@ import spock.lang.Stepwise @Testcontainers class TestcontainersSharedContainerIT extends Specification { - @Shared - GenericContainer genericContainer = new GenericContainer(SpockTestImages.HTTPD_IMAGE) - .withExposedPorts(80) - - @Shared - String lastContainerId - - def "starts accessible docker container"() { - given: "a http client" - def client = HttpClientBuilder.create().build() - lastContainerId = genericContainer.containerId - - when: "accessing web server" - CloseableHttpResponse response = performHttpRequest(client) - - then: "docker container is running and returns http status code 200" - response.statusLine.statusCode == 200 - } - - def "containers keeps on running between features"() { - expect: - genericContainer.containerId == lastContainerId - } - - private CloseableHttpResponse performHttpRequest(CloseableHttpClient client) { - String ip = genericContainer.host - String port = genericContainer.getMappedPort(80) - def response = client.execute(new HttpGet("http://$ip:$port")) - response - } - + @Shared + GenericContainer genericContainer = new GenericContainer(SpockTestImages.HTTPD_IMAGE) + .withExposedPorts(80) + + @Shared + String lastContainerId + + def "starts accessible docker container"() { + given: "a http client" + def client = HttpClientBuilder.create().build() + lastContainerId = genericContainer.containerId + + when: "accessing web server" + CloseableHttpResponse response = performHttpRequest(client) + + then: "docker container is running and returns http status code 200" + response.statusLine.statusCode == 200 + } + + def "containers keeps on running between features"() { + expect: + genericContainer.containerId == lastContainerId + } + + private CloseableHttpResponse performHttpRequest(CloseableHttpClient client) { + String ip = genericContainer.host + String port = genericContainer.getMappedPort(80) + def response = client.execute(new HttpGet("http://$ip:$port")) + response + } } diff --git a/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java b/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java index e22cc1fdd55..ed8413a2a31 100644 --- a/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java +++ b/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java @@ -22,13 +22,19 @@ public class ToxiproxyContainer extends GenericContainer { private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("shopify/toxiproxy"); + private static final String DEFAULT_TAG = "2.1.0"; + private static final int TOXIPROXY_CONTROL_PORT = 8474; + private static final int FIRST_PROXIED_PORT = 8666; + private static final int LAST_PROXIED_PORT = 8666 + 31; private ToxiproxyClient client; + private final Map proxies = new HashMap<>(); + private final AtomicInteger nextPort = new AtomicInteger(FIRST_PROXIED_PORT); /** @@ -45,7 +51,6 @@ public ToxiproxyContainer(String dockerImageName) { public ToxiproxyContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); addExposedPorts(TOXIPROXY_CONTROL_PORT); @@ -99,42 +104,55 @@ public ContainerProxy getProxy(GenericContainer container, int port) { public ContainerProxy getProxy(String hostname, int port) { String upstream = hostname + ":" + port; - return proxies.computeIfAbsent(upstream, __ -> { - try { - final int toxiPort = nextPort.getAndIncrement(); - if (toxiPort > LAST_PROXIED_PORT) { - throw new IllegalStateException("Maximum number of proxies exceeded"); + return proxies.computeIfAbsent( + upstream, + __ -> { + try { + final int toxiPort = nextPort.getAndIncrement(); + if (toxiPort > LAST_PROXIED_PORT) { + throw new IllegalStateException("Maximum number of proxies exceeded"); + } + + final Proxy proxy = client.createProxy(upstream, "0.0.0.0:" + toxiPort, upstream); + final int mappedPort = getMappedPort(toxiPort); + return new ContainerProxy(proxy, getHost(), mappedPort, toxiPort); + } catch (IOException e) { + throw new RuntimeException("Proxy could not be created", e); } - - final Proxy proxy = client.createProxy(upstream, "0.0.0.0:" + toxiPort, upstream); - final int mappedPort = getMappedPort(toxiPort); - return new ContainerProxy(proxy, getHost(), mappedPort, toxiPort); - } catch (IOException e) { - throw new RuntimeException("Proxy could not be created", e); } - }); + ); } @RequiredArgsConstructor(access = AccessLevel.PROTECTED) public static class ContainerProxy { + private static final String CUT_CONNECTION_DOWNSTREAM = "CUT_CONNECTION_DOWNSTREAM"; + private static final String CUT_CONNECTION_UPSTREAM = "CUT_CONNECTION_UPSTREAM"; + private final Proxy toxi; + /** * The IP address that this proxy container may be reached on from the host machine. */ - @Getter private final String containerIpAddress; + @Getter + private final String containerIpAddress; + /** * The mapped port of this proxy. This is a port of the host machine. It can be used to * access the Toxiproxy container from the host machine. */ - @Getter private final int proxyPort; + @Getter + private final int proxyPort; + /** * The original (exposed) port of this proxy. This is a port of the Toxiproxy Docker * container. It can be used to access this container from a different Docker container * on the same network. */ - @Getter private final int originalProxyPort; + @Getter + private final int originalProxyPort; + private boolean isCurrentlyCut; public String getName() { diff --git a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java index 3177fb2e8e8..c5982bc45c1 100644 --- a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java +++ b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java @@ -10,7 +10,6 @@ import java.io.IOException; import java.time.Duration; -import static java.lang.String.format; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; @@ -19,6 +18,7 @@ public class ToxiproxyTest { private static final Duration JEDIS_TIMEOUT = Duration.ofSeconds(10); + // spotless:off // creatingProxy { // An alias that can be used to resolve the Toxiproxy container by name in the network it is connected to. // It can be used as a hostname of the Toxiproxy container by other containers in the same network. @@ -29,6 +29,7 @@ public class ToxiproxyTest { public Network network = Network.newNetwork(); private static final DockerImageName REDIS_IMAGE = DockerImageName.parse("redis:5.0.4"); + // The target container - this could be anything @Rule public GenericContainer redis = new GenericContainer<>(REDIS_IMAGE) @@ -36,12 +37,15 @@ public class ToxiproxyTest { .withNetwork(network); private static final DockerImageName TOXIPROXY_IMAGE = DockerImageName.parse("shopify/toxiproxy:2.1.0"); + // Toxiproxy container, which will be used as a TCP proxy @Rule public ToxiproxyContainer toxiproxy = new ToxiproxyContainer(TOXIPROXY_IMAGE) .withNetwork(network) .withNetworkAliases(TOXIPROXY_NETWORK_ALIAS); + // } + // spotless:on @Test public void testDirect() { @@ -68,12 +72,14 @@ public void testLatencyViaProxy() throws IOException { checkCallWithLatency(jedis, "without interference", 0, 250); + // spotless:off // addingLatency { proxy.toxics() .latency("latency", ToxicDirection.DOWNSTREAM, 1_100) .setJitter(100); // from now on the connection latency should be from 1000-1200 ms. // } + // spotless:on checkCallWithLatency(jedis, "with interference", 1_000, 1_500); } @@ -84,30 +90,42 @@ public void testConnectionCut() { final Jedis jedis = createJedis(proxy.getContainerIpAddress(), proxy.getProxyPort()); jedis.set("somekey", "somevalue"); - assertEquals("access to the container works OK before cutting the connection", "somevalue", jedis.get("somekey")); + assertEquals( + "access to the container works OK before cutting the connection", + "somevalue", + jedis.get("somekey") + ); // disableProxy { proxy.setConnectionCut(true); // for example, expect failure when the connection is cut - assertThrows("calls fail when the connection is cut", - JedisConnectionException.class, () -> { + assertThrows( + "calls fail when the connection is cut", + JedisConnectionException.class, + () -> { jedis.get("somekey"); - }); + } + ); proxy.setConnectionCut(false); // and with the connection re-established, expect success - assertEquals("access to the container works OK after re-establishing the connection", "somevalue", jedis.get("somekey")); + assertEquals( + "access to the container works OK after re-establishing the connection", + "somevalue", + jedis.get("somekey") + ); // } } @Test public void testMultipleProxiesCanBeCreated() { - try (GenericContainer secondRedis = new GenericContainer<>(REDIS_IMAGE) - .withExposedPorts(6379) - .withNetwork(network)) { - + try ( + GenericContainer secondRedis = new GenericContainer<>(REDIS_IMAGE) + .withExposedPorts(6379) + .withNetwork(network) + ) { secondRedis.start(); final ToxiproxyContainer.ContainerProxy firstProxy = toxiproxy.getProxy(redis, 6379); @@ -121,10 +139,13 @@ public void testMultipleProxiesCanBeCreated() { firstProxy.setConnectionCut(true); - assertThrows("calls fail when the connection is cut, for only the relevant proxy", - JedisConnectionException.class, () -> { + assertThrows( + "calls fail when the connection is cut, for only the relevant proxy", + JedisConnectionException.class, + () -> { firstJedis.get("somekey"); - }); + } + ); assertEquals("access via a different proxy is OK", "somevalue", secondJedis.get("somekey")); } @@ -142,11 +163,19 @@ public void testOriginalAndMappedPorts() { final ToxiproxyContainer.ContainerProxy proxy1 = toxiproxy.getProxy("hostname1", 8080); assertEquals("original port is correct", 8667, proxy1.getOriginalProxyPort()); - assertEquals("mapped port is correct", toxiproxy.getMappedPort(proxy1.getOriginalProxyPort()), proxy1.getProxyPort()); + assertEquals( + "mapped port is correct", + toxiproxy.getMappedPort(proxy1.getOriginalProxyPort()), + proxy1.getProxyPort() + ); final ToxiproxyContainer.ContainerProxy proxy2 = toxiproxy.getProxy("hostname2", 9090); assertEquals("original port is correct", 8668, proxy2.getOriginalProxyPort()); - assertEquals("mapped port is correct", toxiproxy.getMappedPort(proxy2.getOriginalProxyPort()), proxy2.getProxyPort()); + assertEquals( + "mapped port is correct", + toxiproxy.getMappedPort(proxy2.getOriginalProxyPort()), + proxy2.getProxyPort() + ); } @Test @@ -163,15 +192,26 @@ public void testControlPort() { assertEquals("control port is mapped from port 8474", toxiproxy.getMappedPort(8474), controlPort); } - private void checkCallWithLatency(Jedis jedis, final String description, int expectedMinLatency, long expectedMaxLatency) { + private void checkCallWithLatency( + Jedis jedis, + final String description, + int expectedMinLatency, + long expectedMaxLatency + ) { final long start = System.currentTimeMillis(); String s = jedis.get("somekey"); final long end = System.currentTimeMillis(); final long duration = end - start; - assertEquals(format("access to the container %s works OK", description), "somevalue", s); - assertTrue(format("%s there is at least %dms latency", description, expectedMinLatency), duration >= expectedMinLatency); - assertTrue(format("%s there is no more than %dms latency", description, expectedMaxLatency), duration < expectedMaxLatency); + assertEquals(String.format("access to the container %s works OK", description), "somevalue", s); + assertTrue( + String.format("%s there is at least %dms latency", description, expectedMinLatency), + duration >= expectedMinLatency + ); + assertTrue( + String.format("%s there is no more than %dms latency", description, expectedMaxLatency), + duration < expectedMaxLatency + ); } private static Jedis createJedis(String host, int port) { diff --git a/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainer.java b/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainer.java index 825340459f3..9ed18e96d28 100644 --- a/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainer.java +++ b/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainer.java @@ -1,5 +1,6 @@ package org.testcontainers.containers; +import com.google.common.base.Strings; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.VisibleForTesting; import org.testcontainers.utility.DockerImageName; @@ -9,19 +10,21 @@ import java.util.HashSet; import java.util.Set; -import static com.google.common.base.Strings.nullToEmpty; -import static java.lang.String.format; - public class TrinoContainer extends JdbcDatabaseContainer { + static final String NAME = "trino"; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("trinodb/trino"); + static final String IMAGE = "trinodb/trino"; + @VisibleForTesting static final String DEFAULT_TAG = "352"; private static final int TRINO_PORT = 8080; private String username = "test"; + private String catalog = null; public TrinoContainer(final String dockerImageName) { @@ -30,7 +33,6 @@ public TrinoContainer(final String dockerImageName) { public TrinoContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); addExposedPort(TRINO_PORT); } @@ -48,7 +50,12 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return format("jdbc:trino://%s:%s/%s", getHost(), getMappedPort(TRINO_PORT), nullToEmpty(catalog)); + return String.format( + "jdbc:trino://%s:%s/%s", + getHost(), + getMappedPort(TRINO_PORT), + Strings.nullToEmpty(catalog) + ); } @Override diff --git a/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainerProvider.java b/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainerProvider.java index 233fc1b9b07..30d877c9e4a 100644 --- a/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainerProvider.java +++ b/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainerProvider.java @@ -6,6 +6,7 @@ * Factory for Trino containers. */ public class TrinoContainerProvider extends JdbcDatabaseContainerProvider { + @Override public boolean supports(String databaseType) { return databaseType.equals(TrinoContainer.NAME); diff --git a/modules/trino/src/test/java/org/testcontainers/containers/TrinoContainerTest.java b/modules/trino/src/test/java/org/testcontainers/containers/TrinoContainerTest.java index d7f8014ca9a..97bbdf6dedd 100644 --- a/modules/trino/src/test/java/org/testcontainers/containers/TrinoContainerTest.java +++ b/modules/trino/src/test/java/org/testcontainers/containers/TrinoContainerTest.java @@ -1,20 +1,12 @@ package org.testcontainers.containers; -import org.junit.Assert; import org.junit.Test; import org.testcontainers.TrinoTestImages; import java.sql.Connection; -import java.sql.DriverManager; import java.sql.ResultSet; -import java.sql.SQLException; import java.sql.Statement; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import static java.lang.Integer.parseInt; -import static java.lang.String.format; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -25,9 +17,11 @@ public class TrinoContainerTest { public void testSimple() throws Exception { try (TrinoContainer trino = new TrinoContainer(TrinoTestImages.TRINO_TEST_IMAGE)) { trino.start(); - try (Connection connection = trino.createConnection(); - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes")) { + try ( + Connection connection = trino.createConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes") + ) { assertTrue("No result", resultSet.next()); assertEquals("Trino version", TrinoContainer.DEFAULT_TAG, resultSet.getString("node_version")); } @@ -38,11 +32,17 @@ public void testSimple() throws Exception { public void testSpecificVersion() throws Exception { try (TrinoContainer trino = new TrinoContainer(TrinoTestImages.TRINO_PREVIOUS_VERSION_TEST_IMAGE)) { trino.start(); - try (Connection connection = trino.createConnection(); - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes")) { + try ( + Connection connection = trino.createConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes") + ) { assertTrue("No result", resultSet.next()); - assertEquals("Trino version", TrinoTestImages.TRINO_PREVIOUS_VERSION_TEST_IMAGE.getVersionPart(), resultSet.getString("node_version")); + assertEquals( + "Trino version", + TrinoTestImages.TRINO_PREVIOUS_VERSION_TEST_IMAGE.getVersionPart(), + resultSet.getString("node_version") + ); } } } @@ -52,9 +52,11 @@ public void testInitScript() throws Exception { try (TrinoContainer trino = new TrinoContainer(TrinoTestImages.TRINO_TEST_IMAGE)) { trino.withInitScript("initial.sql"); trino.start(); - try (Connection connection = trino.createConnection(); - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("SELECT a FROM memory.default.test_table")) { + try ( + Connection connection = trino.createConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT a FROM memory.default.test_table") + ) { assertTrue("No result", resultSet.next()); assertEquals("Value", 12345678909324L, resultSet.getObject("a")); assertFalse("Too many result", resultSet.next()); diff --git a/modules/trino/src/test/java/org/testcontainers/jdbc/trino/TrinoJDBCDriverTest.java b/modules/trino/src/test/java/org/testcontainers/jdbc/trino/TrinoJDBCDriverTest.java index 8f06aef7278..9ec98c2e6d7 100644 --- a/modules/trino/src/test/java/org/testcontainers/jdbc/trino/TrinoJDBCDriverTest.java +++ b/modules/trino/src/test/java/org/testcontainers/jdbc/trino/TrinoJDBCDriverTest.java @@ -4,18 +4,18 @@ import org.junit.runners.Parameterized; import org.testcontainers.jdbc.AbstractJDBCDriverTest; +import java.util.Arrays; import java.util.EnumSet; -import static java.util.Arrays.asList; - @RunWith(Parameterized.class) public class TrinoJDBCDriverTest extends AbstractJDBCDriverTest { @Parameterized.Parameters(name = "{index} - {0}") public static Iterable data() { - return asList( - new Object[][]{ - {"jdbc:tc:trino:352://hostname/", EnumSet.of(Options.PmdKnownBroken)}, - }); + return Arrays.asList( + new Object[][] { // + { "jdbc:tc:trino:352://hostname/", EnumSet.of(Options.PmdKnownBroken) }, + } + ); } } diff --git a/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java b/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java index 37e78a14f95..31bb1f31d9f 100644 --- a/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java +++ b/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java @@ -1,6 +1,7 @@ package org.testcontainers.vault; import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.model.Capability; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.utility.DockerImageName; @@ -14,9 +15,6 @@ import java.util.function.Consumer; import java.util.stream.Collectors; -import static com.github.dockerjava.api.model.Capability.IPC_LOCK; - - /** * GenericContainer subclass for Vault specific configuration and features. The main feature is the * withSecretInVault method, where users can specify which secrets to be pre-loaded into Vault for @@ -27,11 +25,13 @@ public class VaultContainer> extends GenericContainer { private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("vault"); + private static final String DEFAULT_TAG = "1.1.3"; private static final int VAULT_PORT = 8200; private Map> secretsMap = new HashMap<>(); + private List initCommands = new ArrayList<>(); private int port = VAULT_PORT; @@ -50,13 +50,12 @@ public VaultContainer(String dockerImageName) { public VaultContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); // Use the vault healthcheck endpoint to check for readiness, per https://www.vaultproject.io/api/system/health.html setWaitStrategy(Wait.forHttp("/v1/sys/health").forStatusCode(200)); - withCreateContainerCmdModifier(cmd -> cmd.withCapAdd(IPC_LOCK)); + withCreateContainerCmdModifier(cmd -> cmd.withCapAdd(Capability.IPC_LOCK)); withEnv("VAULT_ADDR", "http://0.0.0.0:" + port); withExposedPorts(port); } @@ -72,7 +71,12 @@ private void addSecrets() { try { this.execInContainer(buildExecCommand(secretsMap)).getStdout().contains("Success"); } catch (IOException | InterruptedException e) { - logger().error("Failed to add these secrets {} into Vault via exec command. Exception message: {}", secretsMap, e.getMessage()); + logger() + .error( + "Failed to add these secrets {} into Vault via exec command. Exception message: {}", + secretsMap, + e.getMessage() + ); } } } @@ -83,22 +87,34 @@ private String[] buildExecCommand(Map> map) { stringBuilder.append(" && vault kv put " + path); secrets.forEach(item -> stringBuilder.append(" " + item)); }); - return new String[]{"/bin/sh", "-c", stringBuilder.toString().substring(4)}; + return new String[] { "/bin/sh", "-c", stringBuilder.toString().substring(4) }; } private void runInitCommands() { if (!initCommands.isEmpty()) { - String commands = initCommands.stream() - .map(command -> "vault " + command) - .collect(Collectors.joining(" && ")); + String commands = initCommands + .stream() + .map(command -> "vault " + command) + .collect(Collectors.joining(" && ")); try { - ExecResult execResult = this.execInContainer(new String[]{"/bin/sh", "-c", commands}); + ExecResult execResult = this.execInContainer(new String[] { "/bin/sh", "-c", commands }); if (execResult.getExitCode() != 0) { - logger().error("Failed to execute these init commands {}. Exit code {}. Stdout {}. Stderr {}", - initCommands, execResult.getExitCode(), execResult.getStdout(), execResult.getStderr()); + logger() + .error( + "Failed to execute these init commands {}. Exit code {}. Stdout {}. Stderr {}", + initCommands, + execResult.getExitCode(), + execResult.getStdout(), + execResult.getStderr() + ); } } catch (IOException | InterruptedException e) { - logger().error("Failed to execute these init commands {}. Exception message: {}", initCommands, e.getMessage()); + logger() + .error( + "Failed to execute these init commands {}. Exception message: {}", + initCommands, + e.getMessage() + ); } } } @@ -166,7 +182,7 @@ public SELF withSecretInVault(String path, String firstSecret, String... remaini /** * Run initialization commands using the vault cli. - * + * * Useful for enableing more secret engines like: *

      *     .withInitCommand("secrets enable pki")
diff --git a/modules/vault/src/main/java/org/testcontainers/vault/VaultLogLevel.java b/modules/vault/src/main/java/org/testcontainers/vault/VaultLogLevel.java
index 58b22f975ff..e5f0db784a8 100644
--- a/modules/vault/src/main/java/org/testcontainers/vault/VaultLogLevel.java
+++ b/modules/vault/src/main/java/org/testcontainers/vault/VaultLogLevel.java
@@ -4,7 +4,11 @@
  * Vault preset of logging levels.
  */
 public enum VaultLogLevel {
-    Trace("trace"), Debug("debug"), Info("info"), Warn("warn"), Error("err");
+    Trace("trace"),
+    Debug("debug"),
+    Info("info"),
+    Warn("warn"),
+    Error("err");
 
     public final String config;
 
diff --git a/modules/vault/src/test/java/org/testcontainers/vault/VaultClientTest.java b/modules/vault/src/test/java/org/testcontainers/vault/VaultClientTest.java
index 4a1a800da17..cd1f1a8acea 100644
--- a/modules/vault/src/test/java/org/testcontainers/vault/VaultClientTest.java
+++ b/modules/vault/src/test/java/org/testcontainers/vault/VaultClientTest.java
@@ -10,7 +10,6 @@
 import java.util.Map;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.testcontainers.vault.VaultTestImages.VAULT_IMAGE;
 
 public class VaultClientTest {
 
@@ -19,10 +18,9 @@ public class VaultClientTest {
     @Test
     public void writeAndReadMultipleValues() throws VaultException {
         try (
-            VaultContainer vaultContainer = new VaultContainer<>(VAULT_IMAGE)
-                    .withVaultToken(VAULT_TOKEN)
+            VaultContainer vaultContainer = new VaultContainer<>(VaultTestImages.VAULT_IMAGE)
+                .withVaultToken(VAULT_TOKEN)
         ) {
-
             vaultContainer.start();
 
             final VaultConfig config = new VaultConfig()
@@ -37,22 +35,14 @@ public void writeAndReadMultipleValues() throws VaultException {
             secrets.put("other_value", "another world");
 
             // Write operation
-            final LogicalResponse writeResponse = vault.logical()
-                .write("secret/hello", secrets);
+            final LogicalResponse writeResponse = vault.logical().write("secret/hello", secrets);
 
             assertThat(writeResponse.getRestResponse().getStatus()).isEqualTo(200);
 
             // Read operation
-            final Map value = vault.logical()
-                .read("secret/hello")
-                .getData();
-
-
-            assertThat(value)
-                .containsEntry("value", "world")
-                .containsEntry("other_value", "another world");
+            final Map value = vault.logical().read("secret/hello").getData();
 
+            assertThat(value).containsEntry("value", "world").containsEntry("other_value", "another world");
         }
-
     }
 }
diff --git a/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java b/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java
index 9dee4c21416..22310bf7a23 100644
--- a/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java
+++ b/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java
@@ -7,7 +7,7 @@
 import java.io.IOException;
 
 import static io.restassured.RestAssured.given;
-import static org.hamcrest.CoreMatchers.*;
+import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.assertThat;
 
 /**
@@ -22,24 +22,38 @@ public class VaultContainerTest {
     public static VaultContainer vaultContainer = new VaultContainer<>(VaultTestImages.VAULT_IMAGE)
         .withVaultToken(VAULT_TOKEN)
         .withSecretInVault("secret/testing1", "top_secret=password123")
-        .withSecretInVault("secret/testing2",
+        .withSecretInVault(
+            "secret/testing2",
             "secret_one=password1",
             "secret_two=password2",
             "secret_three=password3",
             "secret_three=password3",
-            "secret_four=password4")
+            "secret_four=password4"
+        )
         .withInitCommand("secrets enable transit", "write -f transit/keys/my-key");
 
     @Test
     public void readFirstSecretPathWithCli() throws IOException, InterruptedException {
-        GenericContainer.ExecResult result = vaultContainer.execInContainer("vault", "kv", "get", "-format=json", "secret/testing1");
+        GenericContainer.ExecResult result = vaultContainer.execInContainer(
+            "vault",
+            "kv",
+            "get",
+            "-format=json",
+            "secret/testing1"
+        );
         final String output = result.getStdout().replaceAll("\\r?\\n", "");
         assertThat(output, containsString("password123"));
     }
 
     @Test
     public void readSecondSecretPathWithCli() throws IOException, InterruptedException {
-        GenericContainer.ExecResult result = vaultContainer.execInContainer("vault", "kv", "get", "-format=json", "secret/testing2");
+        GenericContainer.ExecResult result = vaultContainer.execInContainer(
+            "vault",
+            "kv",
+            "get",
+            "-format=json",
+            "secret/testing2"
+        );
         final String output = result.getStdout().replaceAll("\\r?\\n", "");
         System.out.println("output = " + output);
         assertThat(output, containsString("password1"));
@@ -50,40 +64,44 @@ public void readSecondSecretPathWithCli() throws IOException, InterruptedExcepti
 
     @Test
     public void readFirstSecretPathOverHttpApi() throws InterruptedException {
-        given().
-            header("X-Vault-Token", VAULT_TOKEN).
-            when().
-            get("http://" + getHostAndPort() + "/v1/secret/data/testing1").
-            then().
-            assertThat().body("data.data.top_secret", equalTo("password123"));
+        given()
+            .header("X-Vault-Token", VAULT_TOKEN)
+            .when()
+            .get("http://" + getHostAndPort() + "/v1/secret/data/testing1")
+            .then()
+            .assertThat()
+            .body("data.data.top_secret", equalTo("password123"));
     }
 
     @Test
     public void readSecondSecretPathOverHttpApi() throws InterruptedException {
-        given().
-            header("X-Vault-Token", VAULT_TOKEN).
-            when().
-            get("http://" + getHostAndPort() + "/v1/secret/data/testing2").
-            then().
-            assertThat().body("data.data.secret_one", containsString("password1")).
-            assertThat().body("data.data.secret_two", containsString("password2")).
-            assertThat().body("data.data.secret_three", hasItem("password3")).
-            assertThat().body("data.data.secret_four", containsString("password4"));
+        given()
+            .header("X-Vault-Token", VAULT_TOKEN)
+            .when()
+            .get("http://" + getHostAndPort() + "/v1/secret/data/testing2")
+            .then()
+            .assertThat()
+            .body("data.data.secret_one", containsString("password1"))
+            .assertThat()
+            .body("data.data.secret_two", containsString("password2"))
+            .assertThat()
+            .body("data.data.secret_three", hasItem("password3"))
+            .assertThat()
+            .body("data.data.secret_four", containsString("password4"));
     }
 
     @Test
     public void readTransitKeyOverHttpApi() throws InterruptedException {
-        given().
-            header("X-Vault-Token", VAULT_TOKEN).
-            when().
-            get("http://" + getHostAndPort() + "/v1/transit/keys/my-key").
-            then().
-            assertThat().body("data.name", equalTo("my-key"));
+        given()
+            .header("X-Vault-Token", VAULT_TOKEN)
+            .when()
+            .get("http://" + getHostAndPort() + "/v1/transit/keys/my-key")
+            .then()
+            .assertThat()
+            .body("data.name", equalTo("my-key"));
     }
 
     private String getHostAndPort() {
         return vaultContainer.getHost() + ":" + vaultContainer.getMappedPort(8200);
     }
-
-
 }
diff --git a/test-support/src/main/java/org/testcontainers/testsupport/Flaky.java b/test-support/src/main/java/org/testcontainers/testsupport/Flaky.java
index cc5f6f947fa..83e18bfa161 100644
--- a/test-support/src/main/java/org/testcontainers/testsupport/Flaky.java
+++ b/test-support/src/main/java/org/testcontainers/testsupport/Flaky.java
@@ -1,19 +1,17 @@
 package org.testcontainers.testsupport;
 
+import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
 /**
  * Annotation for test methods that should be retried in the event of failure. See {@link FlakyTestJUnit4RetryRule} for
  * more details.
  */
-@Retention(RUNTIME)
-@Target({METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD })
 public @interface Flaky {
-
     /**
      * @return a URL for a GitHub issue where this flaky test can be discussed, and where actions to resolve it can be
      * coordinated.
diff --git a/test-support/src/main/java/org/testcontainers/testsupport/FlakyTestJUnit4RetryRule.java b/test-support/src/main/java/org/testcontainers/testsupport/FlakyTestJUnit4RetryRule.java
index aa1a3ed3c5f..759e23ebbca 100644
--- a/test-support/src/main/java/org/testcontainers/testsupport/FlakyTestJUnit4RetryRule.java
+++ b/test-support/src/main/java/org/testcontainers/testsupport/FlakyTestJUnit4RetryRule.java
@@ -31,7 +31,6 @@ public class FlakyTestJUnit4RetryRule implements TestRule {
 
     @Override
     public Statement apply(Statement base, Description description) {
-
         final Flaky annotation = description.getAnnotation(Flaky.class);
 
         if (annotation == null) {
@@ -53,11 +52,13 @@ public Statement apply(Statement base, Description description) {
         try {
             reviewDate = LocalDate.parse(annotation.reviewDate());
         } catch (DateTimeParseException e) {
-            throw new IllegalArgumentException("@Flaky reviewDate could not be parsed. Please provide a date in yyyy-mm-dd format");
+            throw new IllegalArgumentException(
+                "@Flaky reviewDate could not be parsed. Please provide a date in yyyy-mm-dd format"
+            );
         }
 
         // the annotation should only have an effect before the review date, to encourage review and resolution
-        if ( LocalDate.now().isBefore(reviewDate) ) {
+        if (LocalDate.now().isBefore(reviewDate)) {
             return new RetryingStatement(base, description, maxTries);
         } else {
             return base;
@@ -65,8 +66,11 @@ public Statement apply(Statement base, Description description) {
     }
 
     private static class RetryingStatement extends Statement {
+
         private final Statement base;
+
         private final Description description;
+
         private final int maxTries;
 
         RetryingStatement(Statement base, Description description, int maxTries) {
@@ -77,7 +81,6 @@ private static class RetryingStatement extends Statement {
 
         @Override
         public void evaluate() {
-
             int attempts = 0;
             final List causes = new ArrayList<>();
 
@@ -93,7 +96,8 @@ public void evaluate() {
 
             throw new IllegalStateException(
                 "@Flaky-annotated test failed despite retries.",
-                new MultipleFailureException(causes));
+                new MultipleFailureException(causes)
+            );
         }
     }
 }
diff --git a/test-support/src/test/java/org/testcontainers/testsupport/FlakyRuleTest.java b/test-support/src/test/java/org/testcontainers/testsupport/FlakyRuleTest.java
index 07b0ffe1fa8..14a3d4c018c 100644
--- a/test-support/src/test/java/org/testcontainers/testsupport/FlakyRuleTest.java
+++ b/test-support/src/test/java/org/testcontainers/testsupport/FlakyRuleTest.java
@@ -12,11 +12,15 @@
 public class FlakyRuleTest {
 
     private static final String VALID_URL = "http://some.url/here";
+
     private static final String INVALID_URL = "";
 
     private static final String VALID_DATE_IN_FAR_FUTURE = "2063-04-05";
+
     private static final String VALID_DATE_IN_PAST = "1991-08-16";
+
     private static final String INVALID_DATE = "91-01-45";
+
     private static final int DEFAULT_TRIES = 3;
 
     private FlakyTestJUnit4RetryRule rule = new FlakyTestJUnit4RetryRule();
@@ -28,9 +32,7 @@ public void testIgnoresMethodWithoutAnnotation() throws Throwable {
         try {
             rule.apply(statement, description).evaluate();
             fail("Should not reach here");
-        } catch (Exception ignored) {
-
-        }
+        } catch (Exception ignored) {}
         assertEquals("The statement should only be invoked once, even if it throws", 1, statement.invocationCount);
     }
 
@@ -42,9 +44,7 @@ public void testRetriesMethodWithAnnotationUntilFailure() throws Throwable {
         try {
             rule.apply(statement, description).evaluate();
             fail("Should not reach here");
-        } catch (Exception ignored) {
-
-        }
+        } catch (Exception ignored) {}
         assertEquals("The statement should be invoked three times", 3, statement.invocationCount);
     }
 
@@ -56,9 +56,7 @@ public void testCustomRetryCount() throws Throwable {
         try {
             rule.apply(statement, description).evaluate();
             fail("Should not reach here");
-        } catch (Exception ignored) {
-
-        }
+        } catch (Exception ignored) {}
         assertEquals("The statement should be invoked ten times", 10, statement.invocationCount);
     }
 
@@ -70,7 +68,11 @@ public void testRetriesMethodWithAnnotationUntilSuccess() throws Throwable {
 
         rule.apply(statement, description).evaluate();
 
-        assertEquals("The statement should be invoked three times, and succeed the third time", 3, statement.invocationCount);
+        assertEquals(
+            "The statement should be invoked three times, and succeed the third time",
+            3,
+            statement.invocationCount
+        );
     }
 
     @Test
@@ -91,9 +93,7 @@ public void testTreatsExpiredAnnotationAsNoAnnotation() throws Throwable {
         try {
             rule.apply(statement, description).evaluate();
             fail("Should not reach here");
-        } catch (Exception ignored) {
-
-        }
+        } catch (Exception ignored) {}
         assertEquals("The statement should only be invoked once, even if it throws", 1, statement.invocationCount);
     }
 
@@ -104,9 +104,7 @@ public void testThrowsOnInvalidDateFormat() throws Throwable {
         try {
             rule.apply(statement, description).evaluate();
             fail("Should not reach here");
-        } catch (IllegalArgumentException ignored) {
-
-        }
+        } catch (IllegalArgumentException ignored) {}
         assertEquals("The statement should not be invoked if the annotation is invalid", 0, statement.invocationCount);
     }
 
@@ -117,14 +115,16 @@ public void testThrowsOnInvalidGitHubUrl() throws Throwable {
         try {
             rule.apply(statement, description).evaluate();
             fail("Should not reach here");
-        } catch (IllegalArgumentException ignored) {
-
-        }
+        } catch (IllegalArgumentException ignored) {}
         assertEquals("The statement should not be invoked if the annotation is invalid", 0, statement.invocationCount);
     }
 
     private Description newDescriptionWithAnnotation(String reviewDate, String gitHubUrl) {
-        return Description.createTestDescription("SomeTestClass", "someMethod", newAnnotation(reviewDate, gitHubUrl, DEFAULT_TRIES));
+        return Description.createTestDescription(
+            "SomeTestClass",
+            "someMethod",
+            newAnnotation(reviewDate, gitHubUrl, DEFAULT_TRIES)
+        );
     }
 
     private Description newDescriptionWithoutAnnotation() {
@@ -132,15 +132,27 @@ private Description newDescriptionWithoutAnnotation() {
     }
 
     private Description newDescriptionWithAnnotation() {
-        return Description.createTestDescription("SomeTestClass", "someMethod", newAnnotation(VALID_DATE_IN_FAR_FUTURE, VALID_URL, DEFAULT_TRIES));
+        return Description.createTestDescription(
+            "SomeTestClass",
+            "someMethod",
+            newAnnotation(VALID_DATE_IN_FAR_FUTURE, VALID_URL, DEFAULT_TRIES)
+        );
     }
 
     private Description newDescriptionWithAnnotationAndCustomTries(int maxTries) {
-        return Description.createTestDescription("SomeTestClass", "someMethod", newAnnotation(VALID_DATE_IN_FAR_FUTURE, VALID_URL, maxTries));
+        return Description.createTestDescription(
+            "SomeTestClass",
+            "someMethod",
+            newAnnotation(VALID_DATE_IN_FAR_FUTURE, VALID_URL, maxTries)
+        );
     }
 
     private Description newDescriptionWithExpiredAnnotation() {
-        return Description.createTestDescription("SomeTestClass", "someMethod", newAnnotation(VALID_DATE_IN_PAST, VALID_URL, DEFAULT_TRIES));
+        return Description.createTestDescription(
+            "SomeTestClass",
+            "someMethod",
+            newAnnotation(VALID_DATE_IN_PAST, VALID_URL, DEFAULT_TRIES)
+        );
     }
 
     private Flaky newAnnotation(final String reviewDate, String gitHubUrl, int tries) {
@@ -174,7 +186,9 @@ private DummyStatement newStatement(int timesToThrow) {
     }
 
     private static class DummyStatement extends Statement {
+
         int shouldThrowTimes = -1;
+
         int invocationCount = 0;
 
         @Override

From 3a82068bf2b20ff7dfce801fc5ca063698c3e0c9 Mon Sep 17 00:00:00 2001
From: Sergei Egorov 
Date: Thu, 26 May 2022 09:39:03 -0400
Subject: [PATCH 2544/5615] Update mkdocs.yml

---
 mkdocs.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mkdocs.yml b/mkdocs.yml
index 16b2106bb3f..f6daad68155 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -102,4 +102,4 @@ nav:
           - contributing_docs.md
           - bounty.md
 extra:
-    latest_version: 1.16.3
+    latest_version: 1.17.2

From 5d3349ffd6a376f89b92e530b9b7048c826e16b2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?=
 
Date: Thu, 26 May 2022 10:13:11 -0500
Subject: [PATCH 2545/5615] Add try-with-resources to HttpWaitStrategyTest
 (#5397)

Co-authored-by: Sergei Egorov 
---
 .../strategy/AbstractWaitStrategyTest.java    |  8 +-
 .../wait/strategy/HttpWaitStrategyTest.java   | 85 ++++++++++++-------
 2 files changed, 58 insertions(+), 35 deletions(-)

diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java
index de16eb0e582..530638f0f70 100644
--- a/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java
+++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java
@@ -84,7 +84,9 @@ protected GenericContainer startContainerWithCommand(
      * @param shellCommand the shell command to execute
      */
     protected void waitUntilReadyAndSucceed(String shellCommand) {
-        waitUntilReadyAndSucceed(startContainerWithCommand(shellCommand));
+        try (GenericContainer container = startContainerWithCommand(shellCommand)) {
+            waitUntilReadyAndSucceed(container);
+        }
     }
 
     /**
@@ -94,7 +96,9 @@ protected void waitUntilReadyAndSucceed(String shellCommand) {
      * @param shellCommand the shell command to execute
      */
     protected void waitUntilReadyAndTimeout(String shellCommand) {
-        waitUntilReadyAndTimeout(startContainerWithCommand(shellCommand));
+        try (GenericContainer container = startContainerWithCommand(shellCommand)) {
+            waitUntilReadyAndTimeout(container);
+        }
     }
 
     /**
diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java
index 7014749115e..972bf2ab66a 100644
--- a/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java
+++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java
@@ -11,8 +11,8 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Predicate;
 
+import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.containsString;
-import static org.junit.Assert.assertThat;
 
 /**
  * Tests for {@link HttpWaitStrategy}.
@@ -45,16 +45,19 @@ public void testWaitUntilReadyWithSuccess() {
     public void testWaitUntilReadyWithSuccessWithCustomHeaders() {
         HashMap headers = new HashMap<>();
         headers.put("baz", "boo");
-        GenericContainer container = startContainerWithCommand(
-            createShellCommand("200 OK", GOOD_RESPONSE_BODY),
-            createHttpWaitStrategy(ready).withHeader("foo", "bar").withHeaders(headers)
-        );
-        waitUntilReadyAndSucceed(container);
+        try (
+            GenericContainer container = startContainerWithCommand(
+                createShellCommand("200 OK", GOOD_RESPONSE_BODY),
+                createHttpWaitStrategy(ready).withHeader("foo", "bar").withHeaders(headers)
+            )
+        ) {
+            waitUntilReadyAndSucceed(container);
 
-        String logs = container.getLogs();
+            String logs = container.getLogs();
 
-        assertThat(logs, containsString("foo: bar"));
-        assertThat(logs, containsString("baz: boo"));
+            assertThat(logs, containsString("foo: bar"));
+            assertThat(logs, containsString("baz: boo"));
+        }
     }
 
     /**
@@ -63,12 +66,14 @@ public void testWaitUntilReadyWithSuccessWithCustomHeaders() {
      */
     @Test
     public void testWaitUntilReadyWithTlsAndAllowUnsecure() {
-        waitUntilReadyAndSucceed(
-            startContainerWithCommand(
+        try (
+            GenericContainer container = startContainerWithCommand(
                 createHttpsShellCommand("200 OK", GOOD_RESPONSE_BODY, 8080),
                 createHttpWaitStrategy(ready).usingTls().allowInsecure()
             )
-        );
+        ) {
+            waitUntilReadyAndSucceed(container);
+        }
     }
 
     /**
@@ -77,12 +82,14 @@ public void testWaitUntilReadyWithTlsAndAllowUnsecure() {
      */
     @Test
     public void testWaitUntilReadyWithUnauthorizedWithLambda() {
-        waitUntilReadyAndSucceed(
-            startContainerWithCommand(
+        try (
+            GenericContainer container = startContainerWithCommand(
                 createShellCommand("401 UNAUTHORIZED", GOOD_RESPONSE_BODY),
                 createHttpWaitStrategy(ready).forStatusCodeMatching(it -> it >= 200 && it < 300 || it == 401)
             )
-        );
+        ) {
+            waitUntilReadyAndSucceed(container);
+        }
     }
 
     /**
@@ -91,12 +98,14 @@ public void testWaitUntilReadyWithUnauthorizedWithLambda() {
      */
     @Test
     public void testWaitUntilReadyWithManyStatusCodes() {
-        waitUntilReadyAndSucceed(
-            startContainerWithCommand(
+        try (
+            GenericContainer container = startContainerWithCommand(
                 createShellCommand("401 UNAUTHORIZED", GOOD_RESPONSE_BODY),
                 createHttpWaitStrategy(ready).forStatusCode(300).forStatusCode(401).forStatusCode(500)
             )
-        );
+        ) {
+            waitUntilReadyAndSucceed(container);
+        }
     }
 
     /**
@@ -106,15 +115,17 @@ public void testWaitUntilReadyWithManyStatusCodes() {
      */
     @Test
     public void testWaitUntilReadyWithManyStatusCodesAndLambda() {
-        waitUntilReadyAndSucceed(
-            startContainerWithCommand(
+        try (
+            GenericContainer container = startContainerWithCommand(
                 createShellCommand("401 UNAUTHORIZED", GOOD_RESPONSE_BODY),
                 createHttpWaitStrategy(ready)
                     .forStatusCode(300)
                     .forStatusCode(500)
                     .forStatusCodeMatching(it -> it == 401)
             )
-        );
+        ) {
+            waitUntilReadyAndSucceed(container);
+        }
     }
 
     /**
@@ -124,12 +135,14 @@ public void testWaitUntilReadyWithManyStatusCodesAndLambda() {
      */
     @Test
     public void testWaitUntilReadyWithTimeoutAndWithManyStatusCodesAndLambda() {
-        waitUntilReadyAndTimeout(
-            startContainerWithCommand(
+        try (
+            GenericContainer container = startContainerWithCommand(
                 createShellCommand("401 UNAUTHORIZED", GOOD_RESPONSE_BODY),
                 createHttpWaitStrategy(ready).forStatusCode(300).forStatusCodeMatching(it -> it == 500)
             )
-        );
+        ) {
+            waitUntilReadyAndTimeout(container);
+        }
     }
 
     /**
@@ -141,12 +154,14 @@ public void testWaitUntilReadyWithTimeoutAndWithManyStatusCodesAndLambda() {
      */
     @Test
     public void testWaitUntilReadyWithTimeoutAndWithLambdaShouldNotMatchOk() {
-        waitUntilReadyAndTimeout(
-            startContainerWithCommand(
+        try (
+            GenericContainer container = startContainerWithCommand(
                 createShellCommand("200 OK", GOOD_RESPONSE_BODY),
                 createHttpWaitStrategy(ready).forStatusCodeMatching(it -> it >= 300)
             )
-        );
+        ) {
+            waitUntilReadyAndTimeout(container);
+        }
     }
 
     /**
@@ -172,26 +187,30 @@ public void testWaitUntilReadyWithTimeoutAndBadResponseBody() {
      */
     @Test
     public void testWaitUntilReadyWithSpecificPort() {
-        waitUntilReadyAndSucceed(
-            startContainerWithCommand(
+        try (
+            GenericContainer container = startContainerWithCommand(
                 createShellCommand("200 OK", GOOD_RESPONSE_BODY, 9090),
                 createHttpWaitStrategy(ready).forPort(9090),
                 7070,
                 8080,
                 9090
             )
-        );
+        ) {
+            waitUntilReadyAndSucceed(container);
+        }
     }
 
     @Test
     public void testWaitUntilReadyWithTimoutCausedByReadTimeout() {
-        waitUntilReadyAndTimeout(
-            startContainerWithCommand(
+        try (
+            GenericContainer container = startContainerWithCommand(
                 createShellCommand("0 Connection Refused", GOOD_RESPONSE_BODY, 9090),
                 createHttpWaitStrategy(ready).forPort(9090).withReadTimeout(Duration.ofMillis(1)),
                 9090
             )
-        );
+        ) {
+            waitUntilReadyAndTimeout(container);
+        }
     }
 
     /**

From aeab7a55c4564e7199efe531705f4c9268839f43 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:40 +0000
Subject: [PATCH 2546/5615] Bump tomcat-jdbc from 10.0.20 to 10.0.21 in
 /modules/jdbc-test

Bumps tomcat-jdbc from 10.0.20 to 10.0.21.

---
updated-dependencies:
- dependency-name: org.apache.tomcat:tomcat-jdbc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/jdbc-test/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle
index d77465c5616..5674f235c7f 100644
--- a/modules/jdbc-test/build.gradle
+++ b/modules/jdbc-test/build.gradle
@@ -14,7 +14,7 @@ dependencies {
         exclude(group: "net.java.dev.jna", module: "jna")
     }
 
-    api 'org.apache.tomcat:tomcat-jdbc:10.0.20'
+    api 'org.apache.tomcat:tomcat-jdbc:10.0.21'
     api 'org.vibur:vibur-dbcp:25.0'
     api 'mysql:mysql-connector-java:8.0.29'
 }

From af64f653ecced7237ee1035504ebaa8d07b430de Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:42 +0000
Subject: [PATCH 2547/5615] Bump neo4j from 3.5.32 to 3.5.33 in /modules/neo4j

Bumps [neo4j](https://github.com/neo4j/neo4j) from 3.5.32 to 3.5.33.
- [Release notes](https://github.com/neo4j/neo4j/releases)
- [Commits](https://github.com/neo4j/neo4j/compare/3.5.32...3.5.33)

---
updated-dependencies:
- dependency-name: org.neo4j:neo4j
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/neo4j/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle
index efba3dd0db5..da996db865f 100644
--- a/modules/neo4j/build.gradle
+++ b/modules/neo4j/build.gradle
@@ -29,7 +29,7 @@ task customNeo4jPluginJar(type: Jar) {
 processTestResources.dependsOn customNeo4jPluginJar
 
 dependencies {
-    customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.32"
+    customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.33"
 
     api project(":testcontainers")
 

From 443f9d250456be62802b7d8c11bdf8f77e37dac9 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:43 +0000
Subject: [PATCH 2548/5615] Bump assertj-core from 3.22.0 to 3.23.1 in
 /modules/jdbc

Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1.
- [Release notes](https://github.com/assertj/assertj-core/releases)
- [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1)

---
updated-dependencies:
- dependency-name: org.assertj:assertj-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/jdbc/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle
index 84b927ac4a9..54023b6bed4 100644
--- a/modules/jdbc/build.gradle
+++ b/modules/jdbc/build.gradle
@@ -11,5 +11,5 @@ dependencies {
     testImplementation 'com.zaxxer:HikariCP-java6:2.3.13'
     testImplementation 'com.googlecode.junit-toolbox:junit-toolbox:2.4'
     testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2'
-    testImplementation 'org.assertj:assertj-core:3.22.0'
+    testImplementation 'org.assertj:assertj-core:3.23.1'
 }

From a615e261b90cc2d1f1e177f593b529865d5fcac8 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:43 +0000
Subject: [PATCH 2549/5615] Bump mockito-core from 4.3.1 to 4.6.0 in /core

Bumps [mockito-core](https://github.com/mockito/mockito) from 4.3.1 to 4.6.0.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v4.3.1...v4.6.0)

---
updated-dependencies:
- dependency-name: org.mockito:mockito-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 core/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/core/build.gradle b/core/build.gradle
index efb08991f8d..3b148503c51 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -95,7 +95,7 @@ dependencies {
     testImplementation 'com.rabbitmq:amqp-client:5.14.2'
     testImplementation 'org.mongodb:mongo-java-driver:3.12.10'
 
-    testImplementation ('org.mockito:mockito-core:4.3.1') {
+    testImplementation ('org.mockito:mockito-core:4.6.0') {
         exclude(module: 'hamcrest-core')
     }
     // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest

From 84f5d0cebb278b058d3b00bc3e2f3f6008ab154d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:43 +0000
Subject: [PATCH 2550/5615] Bump postgresql from 42.3.4 to 42.3.6 in
 /modules/postgresql

Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.4 to 42.3.6.
- [Release notes](https://github.com/pgjdbc/pgjdbc/releases)
- [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md)
- [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.4...REL42.3.6)

---
updated-dependencies:
- dependency-name: org.postgresql:postgresql
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/postgresql/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle
index f2c50c30754..d849ad408df 100644
--- a/modules/postgresql/build.gradle
+++ b/modules/postgresql/build.gradle
@@ -11,7 +11,7 @@ dependencies {
 
     testImplementation project(':jdbc-test')
     testImplementation project(':test-support')
-    testImplementation 'org.postgresql:postgresql:42.3.4'
+    testImplementation 'org.postgresql:postgresql:42.3.6'
 
     testImplementation testFixtures(project(':r2dbc'))
     testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.11.RELEASE'

From c36cdcad20c666f83b42de1ef9b7e0bb2950d5b7 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:43 +0000
Subject: [PATCH 2551/5615] Bump mariadb-java-client from 3.0.3 to 3.0.5 in
 /modules/mariadb

Bumps [mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 3.0.3 to 3.0.5.
- [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases)
- [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/3.0.3...3.0.5)

---
updated-dependencies:
- dependency-name: org.mariadb.jdbc:mariadb-java-client
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/mariadb/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle
index 8d65ed00deb..fd534915ba1 100644
--- a/modules/mariadb/build.gradle
+++ b/modules/mariadb/build.gradle
@@ -10,7 +10,7 @@ dependencies {
     compileOnly 'org.mariadb:r2dbc-mariadb:1.0.3'
 
     testImplementation project(':jdbc-test')
-    testImplementation 'org.mariadb.jdbc:mariadb-java-client:3.0.3'
+    testImplementation 'org.mariadb.jdbc:mariadb-java-client:3.0.5'
 
     testImplementation testFixtures(project(':r2dbc'))
     testImplementation 'org.mariadb:r2dbc-mariadb:1.0.2'

From fab88d0d60f90fa9e08f2f6c2f083b3323dbc145 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:44 +0000
Subject: [PATCH 2552/5615] Bump assertj-core from 3.22.0 to 3.23.1 in
 /modules/neo4j

Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1.
- [Release notes](https://github.com/assertj/assertj-core/releases)
- [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1)

---
updated-dependencies:
- dependency-name: org.assertj:assertj-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/neo4j/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle
index efba3dd0db5..555d55e3a72 100644
--- a/modules/neo4j/build.gradle
+++ b/modules/neo4j/build.gradle
@@ -34,5 +34,5 @@ dependencies {
     api project(":testcontainers")
 
     testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.5'
-    testImplementation 'org.assertj:assertj-core:3.22.0'
+    testImplementation 'org.assertj:assertj-core:3.23.1'
 }

From af16f640661406c78c2a591f6dcc80ba981203d6 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:44 +0000
Subject: [PATCH 2553/5615] Bump assertj-core from 3.22.0 to 3.23.1 in
 /modules/kafka

Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1.
- [Release notes](https://github.com/assertj/assertj-core/releases)
- [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1)

---
updated-dependencies:
- dependency-name: org.assertj:assertj-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/kafka/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle
index b9157d9d539..a1022ae1720 100644
--- a/modules/kafka/build.gradle
+++ b/modules/kafka/build.gradle
@@ -13,6 +13,6 @@ dependencies {
     api project(':testcontainers')
 
     testImplementation 'org.apache.kafka:kafka-clients:3.0.0'
-    testImplementation 'org.assertj:assertj-core:3.22.0'
+    testImplementation 'org.assertj:assertj-core:3.23.1'
     testImplementation 'com.google.guava:guava:23.0'
 }

From 670f523cee9bdcc1bdf258a83905071ea0b5a417 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:44 +0000
Subject: [PATCH 2554/5615] Bump assertj-core from 3.22.0 to 3.23.1 in
 /modules/pulsar

Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1.
- [Release notes](https://github.com/assertj/assertj-core/releases)
- [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1)

---
updated-dependencies:
- dependency-name: org.assertj:assertj-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/pulsar/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle
index cf11a36b411..69c63cc3033 100644
--- a/modules/pulsar/build.gradle
+++ b/modules/pulsar/build.gradle
@@ -4,6 +4,6 @@ dependencies {
     api project(':testcontainers')
 
     testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.7.4'
-    testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.22.0'
+    testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.23.1'
     testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.7.4'
 }

From 43feb287518abb1d777cb10a46d839f03fd9e10b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:45 +0000
Subject: [PATCH 2555/5615] Bump tomcat-jdbc from 10.0.20 to 10.0.21 in
 /modules/jdbc

Bumps tomcat-jdbc from 10.0.20 to 10.0.21.

---
updated-dependencies:
- dependency-name: org.apache.tomcat:tomcat-jdbc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/jdbc/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle
index 84b927ac4a9..3c9865b5246 100644
--- a/modules/jdbc/build.gradle
+++ b/modules/jdbc/build.gradle
@@ -7,7 +7,7 @@ dependencies {
     compileOnly 'org.jetbrains:annotations:23.0.0'
     testImplementation 'commons-dbutils:commons-dbutils:1.7'
     testImplementation 'org.vibur:vibur-dbcp:25.0'
-    testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.20'
+    testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.21'
     testImplementation 'com.zaxxer:HikariCP-java6:2.3.13'
     testImplementation 'com.googlecode.junit-toolbox:junit-toolbox:2.4'
     testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2'

From e267d78335346416aaf065d04b9c5a39757d78e9 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:46 +0000
Subject: [PATCH 2556/5615] Bump aws-java-sdk-s3 from 1.12.191 to 1.12.231 in
 /modules/localstack

Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.191 to 1.12.231.
- [Release notes](https://github.com/aws/aws-sdk-java/releases)
- [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.191...1.12.231)

---
updated-dependencies:
- dependency-name: com.amazonaws:aws-java-sdk-s3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/localstack/build.gradle | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle
index 496b4e23983..8a12cd75327 100644
--- a/modules/localstack/build.gradle
+++ b/modules/localstack/build.gradle
@@ -3,8 +3,8 @@ description = "Testcontainers :: Localstack"
 dependencies {
     api project(':testcontainers')
 
-    compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.191'
-    testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.169'
+    compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.231'
+    testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.231'
     testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.210'
     testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.169'
     testImplementation 'software.amazon.awssdk:s3:2.17.181'

From a67d40a1e1fa8447b021ad6e98350fe4edc898b1 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:47 +0000
Subject: [PATCH 2557/5615] Bump aws-java-sdk-dynamodb in /modules/dynalite

Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.12.210 to 1.12.232.
- [Release notes](https://github.com/aws/aws-sdk-java/releases)
- [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.210...1.12.232)

---
updated-dependencies:
- dependency-name: com.amazonaws:aws-java-sdk-dynamodb
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/dynalite/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle
index 6dabdec119b..09cb51e10a4 100644
--- a/modules/dynalite/build.gradle
+++ b/modules/dynalite/build.gradle
@@ -3,7 +3,7 @@ description = "Testcontainers :: Dynalite"
 dependencies {
     api project(':testcontainers')
 
-    compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.210'
+    compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.232'
     testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.210'
 
     testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2'

From 7fb15fbac3912e999aedd63431461142c61a0fb1 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:48 +0000
Subject: [PATCH 2558/5615] Bump assertj-core from 3.22.0 to 3.23.1 in
 /modules/junit-jupiter

Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1.
- [Release notes](https://github.com/assertj/assertj-core/releases)
- [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1)

---
updated-dependencies:
- dependency-name: org.assertj:assertj-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/junit-jupiter/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle
index 6685eddf15a..7efd4fe3afa 100644
--- a/modules/junit-jupiter/build.gradle
+++ b/modules/junit-jupiter/build.gradle
@@ -12,7 +12,7 @@ dependencies {
     testImplementation ('org.mockito:mockito-core:4.4.0') {
         exclude(module: 'hamcrest-core')
     }
-    testImplementation 'org.assertj:assertj-core:3.22.0'
+    testImplementation 'org.assertj:assertj-core:3.23.1'
     testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2'
 
     testRuntimeOnly 'org.postgresql:postgresql:42.3.4'

From 6f33590747b80490d0f7c873fa366e559e9e9ffc Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:49 +0000
Subject: [PATCH 2559/5615] Bump assertj-core from 3.22.0 to 3.23.1 in
 /modules/vault

Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1.
- [Release notes](https://github.com/assertj/assertj-core/releases)
- [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1)

---
updated-dependencies:
- dependency-name: org.assertj:assertj-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/vault/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle
index 80b416ce235..f6e94f2139d 100644
--- a/modules/vault/build.gradle
+++ b/modules/vault/build.gradle
@@ -5,6 +5,6 @@ dependencies {
 
     testImplementation 'com.bettercloud:vault-java-driver:5.1.0'
     testImplementation 'io.rest-assured:rest-assured:5.0.1'
-    testImplementation 'org.assertj:assertj-core:3.22.0'
+    testImplementation 'org.assertj:assertj-core:3.23.1'
 
 }

From 0f2f843d5b205a8dbda4be9ca5840722bf9777f8 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:49 +0000
Subject: [PATCH 2560/5615] Bump common-custom-user-data-gradle-plugin in
 /examples

Bumps common-custom-user-data-gradle-plugin from 1.6.2 to 1.7.

---
updated-dependencies:
- dependency-name: com.gradle:common-custom-user-data-gradle-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 examples/settings.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/examples/settings.gradle b/examples/settings.gradle
index ceb39498854..2046a7d282e 100644
--- a/examples/settings.gradle
+++ b/examples/settings.gradle
@@ -7,7 +7,7 @@ buildscript {
     dependencies {
         classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.10.0"
         classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10"
-        classpath "com.gradle:common-custom-user-data-gradle-plugin:1.6.2"
+        classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7"
     }
 }
 

From 761e70a06761036246647dd0a1313d7f2ff9d1bc Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:52 +0000
Subject: [PATCH 2561/5615] Bump assertj-core from 3.22.0 to 3.23.1 in /core

Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1.
- [Release notes](https://github.com/assertj/assertj-core/releases)
- [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1)

---
updated-dependencies:
- dependency-name: org.assertj:assertj-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 core/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/core/build.gradle b/core/build.gradle
index efb08991f8d..3b870a59e5c 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -102,7 +102,7 @@ dependencies {
     testImplementation files('testlib/repo/fakejar/fakejar/0/fakejar-0.jar')
 
     testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2'
-    testImplementation 'org.assertj:assertj-core:3.22.0'
+    testImplementation 'org.assertj:assertj-core:3.23.1'
     testImplementation project(':test-support')
 
     jarFileTestCompileOnly "org.projectlombok:lombok:${lombok.version}"

From b8466947c2451bebc2760397293e57334f101697 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:52 +0000
Subject: [PATCH 2562/5615] Bump postgresql from 42.3.1 to 42.3.6 in
 /modules/spock

Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.1 to 42.3.6.
- [Release notes](https://github.com/pgjdbc/pgjdbc/releases)
- [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md)
- [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.1...REL42.3.6)

---
updated-dependencies:
- dependency-name: org.postgresql:postgresql
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/spock/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle
index 396c0071042..e609733d398 100644
--- a/modules/spock/build.gradle
+++ b/modules/spock/build.gradle
@@ -15,7 +15,7 @@ dependencies {
     testImplementation 'com.zaxxer:HikariCP:4.0.3'
     testImplementation 'org.apache.httpcomponents:httpclient:4.5.13'
 
-    testRuntimeOnly 'org.postgresql:postgresql:42.3.1'
+    testRuntimeOnly 'org.postgresql:postgresql:42.3.6'
     testRuntimeOnly 'mysql:mysql-connector-java:8.0.29'
 
     testCompileClasspath 'org.jetbrains:annotations:23.0.0'

From a76a9ca56f20070a0bf96b73dc155eec6e07c817 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:53 +0000
Subject: [PATCH 2563/5615] Bump jedis from 4.2.2 to 4.2.3 in /core

Bumps [jedis](https://github.com/redis/jedis) from 4.2.2 to 4.2.3.
- [Release notes](https://github.com/redis/jedis/releases)
- [Commits](https://github.com/redis/jedis/compare/v4.2.2...v4.2.3)

---
updated-dependencies:
- dependency-name: redis.clients:jedis
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 core/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/core/build.gradle b/core/build.gradle
index efb08991f8d..833831f0aed 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -91,7 +91,7 @@ dependencies {
     shaded 'org.zeroturnaround:zt-exec:1.12'
 
     testImplementation 'org.apache.httpcomponents:httpclient:4.5.9'
-    testImplementation 'redis.clients:jedis:4.2.2'
+    testImplementation 'redis.clients:jedis:4.2.3'
     testImplementation 'com.rabbitmq:amqp-client:5.14.2'
     testImplementation 'org.mongodb:mongo-java-driver:3.12.10'
 

From e97b0c2073f1ec59ccf89890442ed50047fbf7f2 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:56 +0000
Subject: [PATCH 2564/5615] Bump postgresql from 42.3.4 to 42.3.6 in
 /modules/junit-jupiter

Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.4 to 42.3.6.
- [Release notes](https://github.com/pgjdbc/pgjdbc/releases)
- [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md)
- [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.4...REL42.3.6)

---
updated-dependencies:
- dependency-name: org.postgresql:postgresql
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/junit-jupiter/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle
index 6685eddf15a..0b1a21a3970 100644
--- a/modules/junit-jupiter/build.gradle
+++ b/modules/junit-jupiter/build.gradle
@@ -15,7 +15,7 @@ dependencies {
     testImplementation 'org.assertj:assertj-core:3.22.0'
     testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2'
 
-    testRuntimeOnly 'org.postgresql:postgresql:42.3.4'
+    testRuntimeOnly 'org.postgresql:postgresql:42.3.6'
     testRuntimeOnly 'mysql:mysql-connector-java:8.0.27'
     testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2'
 }

From 553405f0c0e28ade15637578922f0bd6f7936ed0 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:57 +0000
Subject: [PATCH 2565/5615] Bump transport from 7.17.3 to 7.17.4 in
 /modules/elasticsearch

Bumps [transport](https://github.com/elastic/elasticsearch) from 7.17.3 to 7.17.4.
- [Release notes](https://github.com/elastic/elasticsearch/releases)
- [Changelog](https://github.com/elastic/elasticsearch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/elastic/elasticsearch/compare/v7.17.3...v7.17.4)

---
updated-dependencies:
- dependency-name: org.elasticsearch.client:transport
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/elasticsearch/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle
index 3d8f7bff12d..4bff8106f0b 100644
--- a/modules/elasticsearch/build.gradle
+++ b/modules/elasticsearch/build.gradle
@@ -3,6 +3,6 @@ description = "TestContainers :: elasticsearch"
 dependencies {
     api project(':testcontainers')
     testImplementation "org.elasticsearch.client:elasticsearch-rest-client:7.16.2"
-    testImplementation "org.elasticsearch.client:transport:7.17.3"
+    testImplementation "org.elasticsearch.client:transport:7.17.4"
     testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2'
 }

From dd539ed0409658cd278ee26f4c795e472bbd0c67 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:57 +0000
Subject: [PATCH 2566/5615] Bump assertj-core from 3.22.0 to 3.23.1 in
 /modules/database-commons

Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1.
- [Release notes](https://github.com/assertj/assertj-core/releases)
- [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1)

---
updated-dependencies:
- dependency-name: org.assertj:assertj-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/database-commons/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/database-commons/build.gradle b/modules/database-commons/build.gradle
index 7a201d772fb..33a6d1e9826 100644
--- a/modules/database-commons/build.gradle
+++ b/modules/database-commons/build.gradle
@@ -3,5 +3,5 @@ description = "Testcontainers :: Database-Commons"
 dependencies {
     api project(':testcontainers')
 
-    testImplementation 'org.assertj:assertj-core:3.22.0'
+    testImplementation 'org.assertj:assertj-core:3.23.1'
 }

From 4f6fea6c5feaf1c5c90ecbec8b823aa09fec708b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:57 +0000
Subject: [PATCH 2567/5615] Bump aws-java-sdk-logs from 1.12.169 to 1.12.232 in
 /modules/localstack

Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.12.169 to 1.12.232.
- [Release notes](https://github.com/aws/aws-sdk-java/releases)
- [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.169...1.12.232)

---
updated-dependencies:
- dependency-name: com.amazonaws:aws-java-sdk-logs
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/localstack/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle
index 496b4e23983..6917199b470 100644
--- a/modules/localstack/build.gradle
+++ b/modules/localstack/build.gradle
@@ -6,7 +6,7 @@ dependencies {
     compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.191'
     testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.169'
     testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.210'
-    testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.169'
+    testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.232'
     testImplementation 'software.amazon.awssdk:s3:2.17.181'
     testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2'
 }

From 62d34bd7899fadbc9c4ba2874d5cdc2e2f0ef7f5 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:01:01 +0000
Subject: [PATCH 2568/5615] Bump postgresql from 42.3.3 to 42.3.6 in /examples

Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.3 to 42.3.6.
- [Release notes](https://github.com/pgjdbc/pgjdbc/releases)
- [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md)
- [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.3...REL42.3.6)

---
updated-dependencies:
- dependency-name: org.postgresql:postgresql
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 examples/linked-container/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle
index c61ce36e473..05c5b241b51 100644
--- a/examples/linked-container/build.gradle
+++ b/examples/linked-container/build.gradle
@@ -9,7 +9,7 @@ dependencies {
     compileOnly 'org.slf4j:slf4j-api:1.7.32'
     implementation 'com.squareup.okhttp3:okhttp:4.9.3'
     implementation 'org.json:json:20211205'
-    testImplementation 'org.postgresql:postgresql:42.3.3'
+    testImplementation 'org.postgresql:postgresql:42.3.6'
     testImplementation 'ch.qos.logback:logback-classic:1.2.11'
     testImplementation 'org.testcontainers:postgresql'
 }

From ce9b09a1fdbc28509d53485371089339416ed44c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:01:25 +0000
Subject: [PATCH 2569/5615] Bump peter-evans/create-pull-request from 4.0.2 to
 4.0.3

Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4.0.2 to 4.0.3.
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](https://github.com/peter-evans/create-pull-request/compare/bd72e1b7922d417764d27d30768117ad7da78a0e...f094b77505fb89581e68a1163fbd2fffece39da1)

---
updated-dependencies:
- dependency-name: peter-evans/create-pull-request
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 .github/workflows/update-docs-version.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml
index b5d672c092d..b4d4d250346 100644
--- a/.github/workflows/update-docs-version.yml
+++ b/.github/workflows/update-docs-version.yml
@@ -17,7 +17,7 @@ jobs:
           sed -i "s/latest_version: .*/latest_version: ${GITHUB_REF##*/}/g" mkdocs.yml
           git diff
       - name: Create Pull Request
-        uses: peter-evans/create-pull-request@bd72e1b7922d417764d27d30768117ad7da78a0e # v3.10.1
+        uses: peter-evans/create-pull-request@f094b77505fb89581e68a1163fbd2fffece39da1 # v3.10.1
         with:
           title: Update docs version to ${GITHUB_REF##*/}
           body: |

From 76381504e6c5cf4083ec1e42a1ffd4f241820793 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:01:30 +0000
Subject: [PATCH 2570/5615] Bump actions/cache from 3.0.2 to 3.0.3

Bumps [actions/cache](https://github.com/actions/cache) from 3.0.2 to 3.0.3.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3.0.2...v3.0.3)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 .github/workflows/ci-examples.yml | 4 ++--
 .github/workflows/ci.yml          | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml
index 90b493df481..9a7b314c67f 100644
--- a/.github/workflows/ci-examples.yml
+++ b/.github/workflows/ci-examples.yml
@@ -22,7 +22,7 @@ jobs:
           java-version: '8.0.302'
           distribution: temurin
       - name: Cache Gradle Home files
-        uses: actions/cache@v3.0.2
+        uses: actions/cache@v3.0.3
         continue-on-error: true
         with:
           path: ~/.gradle/caches
@@ -50,7 +50,7 @@ jobs:
           java-version: '8.0.302'
           distribution: temurin
       - name: Cache Gradle Home files
-        uses: actions/cache@v3.0.2
+        uses: actions/cache@v3.0.3
         continue-on-error: true
         with:
           path: ~/.gradle/caches
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0c0e5eaf601..56bb4e0799d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -30,7 +30,7 @@ jobs:
           java-version: '8.0.302'
           distribution: temurin
       - name: Cache Gradle Home files
-        uses: actions/cache@v3.0.2
+        uses: actions/cache@v3.0.3
         with:
           path: ~/.gradle/caches
           key: ${{ runner.os }}-gradle-home-testmatrix-${{ hashFiles('**/*.gradle') }}
@@ -56,7 +56,7 @@ jobs:
           java-version: '8.0.302'
           distribution: temurin
       - name: Cache Gradle Home files
-        uses: actions/cache@v3.0.2
+        uses: actions/cache@v3.0.3
         continue-on-error: true
         with:
           path: ~/.gradle/caches

From c3e8979012144fa559629f11ad8bad9d742839d6 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:01:35 +0000
Subject: [PATCH 2571/5615] Bump release-drafter/release-drafter from 5.19.0 to
 5.20.0

Bumps [release-drafter/release-drafter](https://github.com/release-drafter/release-drafter) from 5.19.0 to 5.20.0.
- [Release notes](https://github.com/release-drafter/release-drafter/releases)
- [Commits](https://github.com/release-drafter/release-drafter/compare/e9ee02fbac03d922bac6212003695e8358dd88b0...ac463ffd9cc4c6ad5682af93dc3e3591c4657ee3)

---
updated-dependencies:
- dependency-name: release-drafter/release-drafter
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 .github/workflows/release-drafter.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml
index 839fb43df4d..132b5ffd0a5 100644
--- a/.github/workflows/release-drafter.yml
+++ b/.github/workflows/release-drafter.yml
@@ -12,6 +12,6 @@ jobs:
     if: github.repository == 'testcontainers/testcontainers-java'
     runs-on: ubuntu-latest
     steps:
-      - uses: release-drafter/release-drafter@e9ee02fbac03d922bac6212003695e8358dd88b0 # v5.19.0
+      - uses: release-drafter/release-drafter@ac463ffd9cc4c6ad5682af93dc3e3591c4657ee3 # v5.19.0
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

From b8c4ddf2a2c2eb1b56b6b566db9e29ff3e88991b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:05:19 +0000
Subject: [PATCH 2572/5615] Bump assertj-core from 3.22.0 to 3.23.1 in
 /modules/orientdb

Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1.
- [Release notes](https://github.com/assertj/assertj-core/releases)
- [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1)

---
updated-dependencies:
- dependency-name: org.assertj:assertj-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/orientdb/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/orientdb/build.gradle b/modules/orientdb/build.gradle
index 820e96e114f..05bdb380c28 100644
--- a/modules/orientdb/build.gradle
+++ b/modules/orientdb/build.gradle
@@ -5,7 +5,7 @@ dependencies {
 
     api "com.orientechnologies:orientdb-client:3.0.24"
 
-    testImplementation 'org.assertj:assertj-core:3.22.0'
+    testImplementation 'org.assertj:assertj-core:3.23.1'
     testImplementation 'org.apache.tinkerpop:gremlin-driver:3.3.4'
     testImplementation "com.orientechnologies:orientdb-gremlin:3.0.18"
 }

From 7e93a7e78e710589e14ef8da91a1b5d392274471 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:05:22 +0000
Subject: [PATCH 2573/5615] Bump assertj-core from 3.22.0 to 3.23.1 in
 /modules/rabbitmq

Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1.
- [Release notes](https://github.com/assertj/assertj-core/releases)
- [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1)

---
updated-dependencies:
- dependency-name: org.assertj:assertj-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/rabbitmq/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/rabbitmq/build.gradle b/modules/rabbitmq/build.gradle
index 967b9eb4ed9..6c2953909bb 100644
--- a/modules/rabbitmq/build.gradle
+++ b/modules/rabbitmq/build.gradle
@@ -3,6 +3,6 @@ description = "TestContainers :: RabbitMQ"
 dependencies {
     api project(":testcontainers")
     testImplementation 'com.rabbitmq:amqp-client:5.14.2'
-    testImplementation 'org.assertj:assertj-core:3.22.0'
+    testImplementation 'org.assertj:assertj-core:3.23.1'
     compileOnly 'org.jetbrains:annotations:23.0.0'
 }

From 46d75a20b081044fc341e7db3b4278d36450a67a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:05:25 +0000
Subject: [PATCH 2574/5615] Bump assertj-core from 3.22.0 to 3.23.1 in
 /modules/k3s

Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1.
- [Release notes](https://github.com/assertj/assertj-core/releases)
- [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1)

---
updated-dependencies:
- dependency-name: org.assertj:assertj-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/k3s/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/k3s/build.gradle b/modules/k3s/build.gradle
index 0ab368d60c2..1c7e6138b7f 100644
--- a/modules/k3s/build.gradle
+++ b/modules/k3s/build.gradle
@@ -10,5 +10,5 @@ dependencies {
 
     testImplementation 'io.fabric8:kubernetes-client:5.12.2'
     testImplementation 'io.kubernetes:client-java:15.0.0'
-    testImplementation 'org.assertj:assertj-core:3.22.0'
+    testImplementation 'org.assertj:assertj-core:3.23.1'
 }

From 0c7730eb1d1e4e034a872b9bb52dced44e65ece7 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:05:25 +0000
Subject: [PATCH 2575/5615] Bump trino-jdbc from 379 to 382 in /modules/trino

Bumps trino-jdbc from 379 to 382.

---
updated-dependencies:
- dependency-name: io.trino:trino-jdbc
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] 
---
 modules/trino/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/trino/build.gradle b/modules/trino/build.gradle
index d0b56af633f..8305e8e0629 100644
--- a/modules/trino/build.gradle
+++ b/modules/trino/build.gradle
@@ -4,6 +4,6 @@ dependencies {
     api project(':jdbc')
 
     testImplementation project(':jdbc-test')
-    testImplementation 'io.trino:trino-jdbc:379'
+    testImplementation 'io.trino:trino-jdbc:382'
     compileOnly 'org.jetbrains:annotations:23.0.0'
 }

From c0b0612ace8545533258f2295358fb750d65d6af Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:05:26 +0000
Subject: [PATCH 2576/5615] Bump google-cloud-pubsub from 1.116.4 to 1.119.0 in
 /modules/gcloud

Bumps [google-cloud-pubsub](https://github.com/googleapis/java-pubsub) from 1.116.4 to 1.119.0.
- [Release notes](https://github.com/googleapis/java-pubsub/releases)
- [Changelog](https://github.com/googleapis/java-pubsub/blob/main/CHANGELOG.md)
- [Commits](https://github.com/googleapis/java-pubsub/compare/v1.116.4...v1.119.0)

---
updated-dependencies:
- dependency-name: com.google.cloud:google-cloud-pubsub
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/gcloud/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle
index 031e68a7f76..c493020c61f 100644
--- a/modules/gcloud/build.gradle
+++ b/modules/gcloud/build.gradle
@@ -5,7 +5,7 @@ dependencies {
 
     testImplementation 'com.google.cloud:google-cloud-datastore:2.4.0'
     testImplementation 'com.google.cloud:google-cloud-firestore:3.0.21'
-    testImplementation 'com.google.cloud:google-cloud-pubsub:1.116.4'
+    testImplementation 'com.google.cloud:google-cloud-pubsub:1.119.0'
     testImplementation 'com.google.cloud:google-cloud-spanner:6.20.0'
     testImplementation 'com.google.cloud:google-cloud-bigtable:2.6.2'
     testImplementation 'org.assertj:assertj-core:3.22.0'

From c6beb9219642e0b054802e9cc221462dd3218751 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:05:27 +0000
Subject: [PATCH 2577/5615] Bump postgresql from 42.3.4 to 42.3.6 in
 /modules/cockroachdb

Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.4 to 42.3.6.
- [Release notes](https://github.com/pgjdbc/pgjdbc/releases)
- [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md)
- [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.4...REL42.3.6)

---
updated-dependencies:
- dependency-name: org.postgresql:postgresql
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/cockroachdb/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/cockroachdb/build.gradle b/modules/cockroachdb/build.gradle
index ad1007a4744..490b2d7fb11 100644
--- a/modules/cockroachdb/build.gradle
+++ b/modules/cockroachdb/build.gradle
@@ -4,5 +4,5 @@ dependencies {
     api project(':jdbc')
 
     testImplementation project(':jdbc-test')
-    testImplementation 'org.postgresql:postgresql:42.3.4'
+    testImplementation 'org.postgresql:postgresql:42.3.6'
 }

From 2e6ac22bcbf163329d171c5b4f57b3ecbb2e2ed8 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:05:31 +0000
Subject: [PATCH 2578/5615] Bump azure-cosmos from 4.29.1 to 4.30.0 in
 /modules/azure

Bumps [azure-cosmos](https://github.com/Azure/azure-sdk-for-java) from 4.29.1 to 4.30.0.
- [Release notes](https://github.com/Azure/azure-sdk-for-java/releases)
- [Commits](https://github.com/Azure/azure-sdk-for-java/compare/azure-cosmos_4.29.1...azure-cosmos_4.30.0)

---
updated-dependencies:
- dependency-name: com.azure:azure-cosmos
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/azure/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle
index 8ab7612c18b..f26028b8ede 100644
--- a/modules/azure/build.gradle
+++ b/modules/azure/build.gradle
@@ -6,5 +6,5 @@ dependencies {
     shaded 'com.squareup.okhttp3:okhttp:3.14.9'
 
     testImplementation 'org.assertj:assertj-core:3.22.0'
-    testImplementation 'com.azure:azure-cosmos:4.29.1'
+    testImplementation 'com.azure:azure-cosmos:4.30.0'
 }

From 110fe8e4f9e813949779a3d8473655f216b44375 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:05:44 +0000
Subject: [PATCH 2579/5615] Bump google-cloud-bigtable from 2.6.2 to 2.8.0 in
 /modules/gcloud

Bumps [google-cloud-bigtable](https://github.com/googleapis/java-bigtable) from 2.6.2 to 2.8.0.
- [Release notes](https://github.com/googleapis/java-bigtable/releases)
- [Changelog](https://github.com/googleapis/java-bigtable/blob/main/CHANGELOG.md)
- [Commits](https://github.com/googleapis/java-bigtable/compare/v2.6.2...v2.8.0)

---
updated-dependencies:
- dependency-name: com.google.cloud:google-cloud-bigtable
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/gcloud/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle
index 031e68a7f76..cdb09edd51f 100644
--- a/modules/gcloud/build.gradle
+++ b/modules/gcloud/build.gradle
@@ -7,6 +7,6 @@ dependencies {
     testImplementation 'com.google.cloud:google-cloud-firestore:3.0.21'
     testImplementation 'com.google.cloud:google-cloud-pubsub:1.116.4'
     testImplementation 'com.google.cloud:google-cloud-spanner:6.20.0'
-    testImplementation 'com.google.cloud:google-cloud-bigtable:2.6.2'
+    testImplementation 'com.google.cloud:google-cloud-bigtable:2.8.0'
     testImplementation 'org.assertj:assertj-core:3.22.0'
 }

From 4a66388f7f10e587280b135673c87958758db9fb Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:05:45 +0000
Subject: [PATCH 2580/5615] Bump reactor-core from 3.4.17 to 3.4.18 in
 /modules/r2dbc

Bumps [reactor-core](https://github.com/reactor/reactor-core) from 3.4.17 to 3.4.18.
- [Release notes](https://github.com/reactor/reactor-core/releases)
- [Commits](https://github.com/reactor/reactor-core/compare/v3.4.17...v3.4.18)

---
updated-dependencies:
- dependency-name: io.projectreactor:reactor-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/r2dbc/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle
index 2a4e3958af8..11e22e2bc9f 100644
--- a/modules/r2dbc/build.gradle
+++ b/modules/r2dbc/build.gradle
@@ -15,6 +15,6 @@ dependencies {
     testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.12.RELEASE'
     testImplementation project(':postgresql')
 
-    testFixturesImplementation 'io.projectreactor:reactor-core:3.4.17'
+    testFixturesImplementation 'io.projectreactor:reactor-core:3.4.18'
     testFixturesImplementation 'org.assertj:assertj-core:3.14.0'
 }

From eeba70a2266b7f20ce94a6566fffdb99f3950ee3 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:06:07 +0000
Subject: [PATCH 2581/5615] Bump hivemq-extension-sdk from 4.8.0 to 4.8.1 in
 /modules/hivemq

Bumps [hivemq-extension-sdk](https://github.com/hivemq/hivemq-extension-sdk) from 4.8.0 to 4.8.1.
- [Release notes](https://github.com/hivemq/hivemq-extension-sdk/releases)
- [Commits](https://github.com/hivemq/hivemq-extension-sdk/compare/4.8.0...4.8.1)

---
updated-dependencies:
- dependency-name: com.hivemq:hivemq-extension-sdk
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/hivemq/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle
index bfdfecb1761..972e07d1f8a 100644
--- a/modules/hivemq/build.gradle
+++ b/modules/hivemq/build.gradle
@@ -13,7 +13,7 @@ dependencies {
 
     testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2")
     testImplementation(project(":junit-jupiter"))
-    testImplementation("com.hivemq:hivemq-extension-sdk:4.8.0")
+    testImplementation("com.hivemq:hivemq-extension-sdk:4.8.1")
     testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0")
     testImplementation("org.apache.httpcomponents:httpclient:4.5.13")
     testImplementation("ch.qos.logback:logback-classic:1.2.10")

From 1bbdbb1deccfe70ef1c64de566963c7334ba8bc9 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:07:49 +0000
Subject: [PATCH 2582/5615] Bump common-custom-user-data-gradle-plugin from
 1.6.5 to 1.7

Bumps common-custom-user-data-gradle-plugin from 1.6.5 to 1.7.

---
updated-dependencies:
- dependency-name: com.gradle:common-custom-user-data-gradle-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 settings.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/settings.gradle b/settings.gradle
index 6b80183bc34..cf15e5ec31a 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -6,7 +6,7 @@ buildscript {
     }
     dependencies {
         classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10"
-        classpath "com.gradle:common-custom-user-data-gradle-plugin:1.6.5"
+        classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7"
     }
 }
 

From 69856364f1a06803c12719b45e6e693e2258404a Mon Sep 17 00:00:00 2001
From: adrianpozueco 
Date: Thu, 2 Jun 2022 20:35:29 +0200
Subject: [PATCH 2583/5615] [Localstack] Get region from env variables or
 default (#3556)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Pozueco Adrian 
Co-authored-by: Eddú Meléndez Gonzales 
---
 .../localstack/LocalStackContainer.java       |  4 ++-
 .../localstack/LocalstackContainerTest.java   | 26 +++++++++++++++++++
 2 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java
index 8bd6f14d3fd..d37c80d06fa 100644
--- a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java
+++ b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java
@@ -45,6 +45,8 @@ public class LocalStackContainer extends GenericContainer {
 
     private static final String DEFAULT_TAG = "0.11.2";
 
+    private static final String DEFAULT_REGION = "us-east-1";
+
     @Deprecated
     public static final String VERSION = DEFAULT_TAG;
 
@@ -311,7 +313,7 @@ public String getSecretKey() {
      * @return a default region
      */
     public String getRegion() {
-        return "us-east-1";
+        return this.getEnvMap().getOrDefault("DEFAULT_REGION", DEFAULT_REGION);
     }
 
     public interface EnabledService {
diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java
index cf4e1a813a4..3b85c69ca34 100644
--- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java
+++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java
@@ -1,5 +1,6 @@
 package org.testcontainers.containers.localstack;
 
+import com.amazonaws.client.builder.AwsClientBuilder;
 import com.amazonaws.services.kms.AWSKMS;
 import com.amazonaws.services.kms.AWSKMSClientBuilder;
 import com.amazonaws.services.kms.model.CreateKeyRequest;
@@ -286,4 +287,29 @@ private String runAwsCliAgainstDockerNetworkContainer(String command) throws Exc
             return logs;
         }
     }
+
+    public static class WithRegion {
+
+        // with_region {
+        private static String region = "eu-west-1";
+
+        @ClassRule
+        public static LocalStackContainer localstack = new LocalStackContainer(LocalstackTestImages.LOCALSTACK_IMAGE)
+            .withEnv("DEFAULT_REGION", region)
+            .withServices(Service.S3);
+
+        // }
+
+        @Test
+        public void s3EndpointHasProperRegion() {
+            final AwsClientBuilder.EndpointConfiguration endpointConfiguration = localstack.getEndpointConfiguration(
+                Service.S3
+            );
+            assertEquals(
+                "The endpoint configuration has right region",
+                region,
+                endpointConfiguration.getSigningRegion()
+            );
+        }
+    }
 }

From 74e87885ecd4737c392eb4c67ec41e7294977089 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?=
 
Date: Thu, 2 Jun 2022 14:05:48 -0500
Subject: [PATCH 2584/5615] Cleanup japicmp baselines (#5463)

Co-authored-by: Sergei Egorov 
---
 core/build.gradle          | 4 ----
 gradle/japicmp.gradle      | 2 +-
 modules/kafka/build.gradle | 9 ---------
 3 files changed, 1 insertion(+), 14 deletions(-)

diff --git a/core/build.gradle b/core/build.gradle
index efb08991f8d..7aac72e50ed 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -60,10 +60,6 @@ configurations.all {
 }
 
 dependencies {
-    baseline 'org.testcontainers:testcontainers:1.17.1', {
-        exclude group: "*", module: "*"
-    }
-
     api 'junit:junit:4.13.2'
     api 'org.slf4j:slf4j-api:1.7.35'
     compileOnly 'org.jetbrains:annotations:23.0.0'
diff --git a/gradle/japicmp.gradle b/gradle/japicmp.gradle
index e74cdccd3af..fe3f8881b1c 100644
--- a/gradle/japicmp.gradle
+++ b/gradle/japicmp.gradle
@@ -3,7 +3,7 @@ configurations {
 }
 
 dependencies {
-    baseline "org.testcontainers:${project.name}:1.16.0", {
+    baseline "org.testcontainers:${project.name}:1.17.2", {
         exclude group: "*", module: "*"
     }
 }
diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle
index b9157d9d539..6d1cf82e5c8 100644
--- a/modules/kafka/build.gradle
+++ b/modules/kafka/build.gradle
@@ -1,14 +1,5 @@
 description = "Testcontainers :: Kafka"
 
-tasks.japicmp {
-    // TODO clean after 1.17.0 is released
-    methodExcludes = [
-        // Overrides removed, not a breaking change per se
-        "org.testcontainers.containers.KafkaContainer#containerIsStarting(com.github.dockerjava.api.command.InspectContainerResponse,boolean)",
-        "org.testcontainers.containers.KafkaContainer#doStart()",
-    ]
-}
-
 dependencies {
     api project(':testcontainers')
 

From 7ea69bdd4e911c98003487b5ee444b47aa52432f Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:47:36 +0000
Subject: [PATCH 2585/5615] Bump neo4j-java-driver from 4.4.5 to 4.4.6 in
 /examples

Bumps [neo4j-java-driver](https://github.com/neo4j/neo4j-java-driver) from 4.4.5 to 4.4.6.
- [Release notes](https://github.com/neo4j/neo4j-java-driver/releases)
- [Commits](https://github.com/neo4j/neo4j-java-driver/compare/4.4.5...4.4.6)

---
updated-dependencies:
- dependency-name: org.neo4j.driver:neo4j-java-driver
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 examples/neo4j-container/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/examples/neo4j-container/build.gradle b/examples/neo4j-container/build.gradle
index 652937d8b33..745d3206953 100644
--- a/examples/neo4j-container/build.gradle
+++ b/examples/neo4j-container/build.gradle
@@ -8,7 +8,7 @@ repositories {
 
 dependencies {
     testImplementation 'org.assertj:assertj-core:3.22.0'
-    testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.5'
+    testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.6'
     testImplementation 'org.testcontainers:neo4j'
     testImplementation 'org.testcontainers:junit-jupiter'
 }

From 3e4d1213715d08886e5abeabf6df4a1b93eff883 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:47:54 +0000
Subject: [PATCH 2586/5615] Bump mongo-java-driver from 3.12.10 to 3.12.11 in
 /core

Bumps [mongo-java-driver](https://github.com/mongodb/mongo-java-driver) from 3.12.10 to 3.12.11.
- [Release notes](https://github.com/mongodb/mongo-java-driver/releases)
- [Commits](https://github.com/mongodb/mongo-java-driver/compare/r3.12.10...r3.12.11)

---
updated-dependencies:
- dependency-name: org.mongodb:mongo-java-driver
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 core/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/core/build.gradle b/core/build.gradle
index f6b336e9600..310b4f43ef0 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -89,7 +89,7 @@ dependencies {
     testImplementation 'org.apache.httpcomponents:httpclient:4.5.9'
     testImplementation 'redis.clients:jedis:4.2.3'
     testImplementation 'com.rabbitmq:amqp-client:5.14.2'
-    testImplementation 'org.mongodb:mongo-java-driver:3.12.10'
+    testImplementation 'org.mongodb:mongo-java-driver:3.12.11'
 
     testImplementation ('org.mockito:mockito-core:4.6.0') {
         exclude(module: 'hamcrest-core')

From deef741188cc4d659dfae0849cb2e0ea948b4c24 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:47:55 +0000
Subject: [PATCH 2587/5615] Bump mysql-connector-java in /modules/junit-jupiter

Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.27 to 8.0.29.
- [Release notes](https://github.com/mysql/mysql-connector-j/releases)
- [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES)
- [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.27...8.0.29)

---
updated-dependencies:
- dependency-name: mysql:mysql-connector-java
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/junit-jupiter/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle
index 5c42d57fcbb..386fc023902 100644
--- a/modules/junit-jupiter/build.gradle
+++ b/modules/junit-jupiter/build.gradle
@@ -16,7 +16,7 @@ dependencies {
     testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2'
 
     testRuntimeOnly 'org.postgresql:postgresql:42.3.6'
-    testRuntimeOnly 'mysql:mysql-connector-java:8.0.27'
+    testRuntimeOnly 'mysql:mysql-connector-java:8.0.29'
     testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2'
 }
 

From c22e1e7dbe84614b79cea59d970c882b97a4abe9 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:00 +0000
Subject: [PATCH 2588/5615] Bump json from 20211205 to 20220320 in /examples

Bumps [json](https://github.com/douglascrockford/JSON-java) from 20211205 to 20220320.
- [Release notes](https://github.com/douglascrockford/JSON-java/releases)
- [Changelog](https://github.com/stleary/JSON-java/blob/master/docs/RELEASES.md)
- [Commits](https://github.com/douglascrockford/JSON-java/commits)

---
updated-dependencies:
- dependency-name: org.json:json
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] 
---
 examples/linked-container/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle
index 05c5b241b51..a801e4f8588 100644
--- a/examples/linked-container/build.gradle
+++ b/examples/linked-container/build.gradle
@@ -8,7 +8,7 @@ repositories {
 dependencies {
     compileOnly 'org.slf4j:slf4j-api:1.7.32'
     implementation 'com.squareup.okhttp3:okhttp:4.9.3'
-    implementation 'org.json:json:20211205'
+    implementation 'org.json:json:20220320'
     testImplementation 'org.postgresql:postgresql:42.3.6'
     testImplementation 'ch.qos.logback:logback-classic:1.2.11'
     testImplementation 'org.testcontainers:postgresql'

From a8ae7c898c297b81d6d872865a823ef8b02ef106 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:02 +0000
Subject: [PATCH 2589/5615] Bump client-java from 15.0.0 to 15.0.1 in
 /modules/k3s

Bumps [client-java](https://github.com/kubernetes-client/java) from 15.0.0 to 15.0.1.
- [Release notes](https://github.com/kubernetes-client/java/releases)
- [Changelog](https://github.com/kubernetes-client/java/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kubernetes-client/java/compare/v15.0.0...v15.0.1)

---
updated-dependencies:
- dependency-name: io.kubernetes:client-java
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/k3s/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/k3s/build.gradle b/modules/k3s/build.gradle
index 1c7e6138b7f..bf51cbfefa9 100644
--- a/modules/k3s/build.gradle
+++ b/modules/k3s/build.gradle
@@ -9,6 +9,6 @@ dependencies {
     shaded 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.8.8'
 
     testImplementation 'io.fabric8:kubernetes-client:5.12.2'
-    testImplementation 'io.kubernetes:client-java:15.0.0'
+    testImplementation 'io.kubernetes:client-java:15.0.1'
     testImplementation 'org.assertj:assertj-core:3.23.1'
 }

From bbb31506cb890ef75c48461ac107c73615885e99 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:03 +0000
Subject: [PATCH 2590/5615] Bump slf4j-api from 1.7.35 to 1.7.36 in /core

Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 1.7.35 to 1.7.36.
- [Release notes](https://github.com/qos-ch/slf4j/releases)
- [Commits](https://github.com/qos-ch/slf4j/compare/v_1.7.35...v_1.7.36)

---
updated-dependencies:
- dependency-name: org.slf4j:slf4j-api
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 core/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/core/build.gradle b/core/build.gradle
index f6b336e9600..393272aabf3 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -61,7 +61,7 @@ configurations.all {
 
 dependencies {
     api 'junit:junit:4.13.2'
-    api 'org.slf4j:slf4j-api:1.7.35'
+    api 'org.slf4j:slf4j-api:1.7.36'
     compileOnly 'org.jetbrains:annotations:23.0.0'
     testCompileClasspath 'org.jetbrains:annotations:23.0.0'
     api 'org.apache.commons:commons-compress:1.21'

From e7b77ba2a640672e2893920351f374010c241df6 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:06 +0000
Subject: [PATCH 2591/5615] Bump logback-classic from 1.2.10 to 1.2.11 in
 /modules/hivemq

Bumps logback-classic from 1.2.10 to 1.2.11.

---
updated-dependencies:
- dependency-name: ch.qos.logback:logback-classic
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/hivemq/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle
index 972e07d1f8a..7add9af67ea 100644
--- a/modules/hivemq/build.gradle
+++ b/modules/hivemq/build.gradle
@@ -16,7 +16,7 @@ dependencies {
     testImplementation("com.hivemq:hivemq-extension-sdk:4.8.1")
     testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0")
     testImplementation("org.apache.httpcomponents:httpclient:4.5.13")
-    testImplementation("ch.qos.logback:logback-classic:1.2.10")
+    testImplementation("ch.qos.logback:logback-classic:1.2.11")
     testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2")
 }
 

From 0891ac2410871133c57b2931ac89bec97673224f Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:09 +0000
Subject: [PATCH 2592/5615] Bump kafka-clients from 3.0.0 to 3.2.0 in
 /modules/kafka

Bumps kafka-clients from 3.0.0 to 3.2.0.

---
updated-dependencies:
- dependency-name: org.apache.kafka:kafka-clients
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/kafka/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle
index cc081326932..79264b8207f 100644
--- a/modules/kafka/build.gradle
+++ b/modules/kafka/build.gradle
@@ -3,7 +3,7 @@ description = "Testcontainers :: Kafka"
 dependencies {
     api project(':testcontainers')
 
-    testImplementation 'org.apache.kafka:kafka-clients:3.0.0'
+    testImplementation 'org.apache.kafka:kafka-clients:3.2.0'
     testImplementation 'org.assertj:assertj-core:3.23.1'
     testImplementation 'com.google.guava:guava:23.0'
 }

From 231a01b52e7e97409f0ea2fdfa94e92c6725e624 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:16 +0000
Subject: [PATCH 2593/5615] Bump elasticsearch-rest-client in
 /modules/elasticsearch

Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 7.16.2 to 8.2.2.
- [Release notes](https://github.com/elastic/elasticsearch/releases)
- [Changelog](https://github.com/elastic/elasticsearch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/elastic/elasticsearch/compare/v7.16.2...v8.2.2)

---
updated-dependencies:
- dependency-name: org.elasticsearch.client:elasticsearch-rest-client
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] 
---
 modules/elasticsearch/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle
index 4bff8106f0b..6d2b5b8a667 100644
--- a/modules/elasticsearch/build.gradle
+++ b/modules/elasticsearch/build.gradle
@@ -2,7 +2,7 @@ description = "TestContainers :: elasticsearch"
 
 dependencies {
     api project(':testcontainers')
-    testImplementation "org.elasticsearch.client:elasticsearch-rest-client:7.16.2"
+    testImplementation "org.elasticsearch.client:elasticsearch-rest-client:8.2.2"
     testImplementation "org.elasticsearch.client:transport:7.17.4"
     testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2'
 }

From 0cab47b7b3fa3b8fb41b619a1ea4ea8c2c771bfe Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:18 +0000
Subject: [PATCH 2594/5615] Bump jedis from 4.2.2 to 4.2.3 in
 /modules/junit-jupiter

Bumps [jedis](https://github.com/redis/jedis) from 4.2.2 to 4.2.3.
- [Release notes](https://github.com/redis/jedis/releases)
- [Commits](https://github.com/redis/jedis/compare/v4.2.2...v4.2.3)

---
updated-dependencies:
- dependency-name: redis.clients:jedis
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/junit-jupiter/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle
index 5c42d57fcbb..371eb6ea4a8 100644
--- a/modules/junit-jupiter/build.gradle
+++ b/modules/junit-jupiter/build.gradle
@@ -7,7 +7,7 @@ dependencies {
     testImplementation project(':mysql')
     testImplementation project(':postgresql')
     testImplementation 'com.zaxxer:HikariCP:4.0.3'
-    testImplementation 'redis.clients:jedis:4.2.2'
+    testImplementation 'redis.clients:jedis:4.2.3'
     testImplementation 'org.apache.httpcomponents:httpclient:4.5.13'
     testImplementation ('org.mockito:mockito-core:4.4.0') {
         exclude(module: 'hamcrest-core')

From 74dd5f49055fbb5160426548ff6784336491caa7 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:23 +0000
Subject: [PATCH 2595/5615] Bump s3 from 2.17.181 to 2.17.204 in
 /modules/localstack

Bumps s3 from 2.17.181 to 2.17.204.

---
updated-dependencies:
- dependency-name: software.amazon.awssdk:s3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/localstack/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle
index f512b3d5fab..0b8aa654dd5 100644
--- a/modules/localstack/build.gradle
+++ b/modules/localstack/build.gradle
@@ -7,6 +7,6 @@ dependencies {
     testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.231'
     testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.210'
     testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.232'
-    testImplementation 'software.amazon.awssdk:s3:2.17.181'
+    testImplementation 'software.amazon.awssdk:s3:2.17.204'
     testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2'
 }

From 9ae45502f706067a7cb1a8b637c6769cf6235adc Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:23 +0000
Subject: [PATCH 2596/5615] Bump com.gradle.enterprise.gradle.plugin from 3.10
 to 3.10.1

Bumps com.gradle.enterprise.gradle.plugin from 3.10 to 3.10.1.

---
updated-dependencies:
- dependency-name: com.gradle.enterprise:com.gradle.enterprise.gradle.plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 settings.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/settings.gradle b/settings.gradle
index cf15e5ec31a..16b3fcdf98a 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -5,7 +5,7 @@ buildscript {
         }
     }
     dependencies {
-        classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10"
+        classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10.1"
         classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7"
     }
 }

From 97d436d90b463e5f6182b63e945ec2452f52ea03 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:27 +0000
Subject: [PATCH 2597/5615] Bump jedis from 4.2.2 to 4.2.3 in /examples

Bumps [jedis](https://github.com/redis/jedis) from 4.2.2 to 4.2.3.
- [Release notes](https://github.com/redis/jedis/releases)
- [Commits](https://github.com/redis/jedis/compare/v4.2.2...v4.2.3)

---
updated-dependencies:
- dependency-name: redis.clients:jedis
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 examples/redis-backed-cache-testng/build.gradle | 2 +-
 examples/redis-backed-cache/build.gradle        | 2 +-
 examples/singleton-container/build.gradle       | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle
index 4faead13f3a..ba883520719 100644
--- a/examples/redis-backed-cache-testng/build.gradle
+++ b/examples/redis-backed-cache-testng/build.gradle
@@ -8,7 +8,7 @@ repositories {
 
 dependencies {
     compileOnly 'org.slf4j:slf4j-api:1.7.32'
-    implementation 'redis.clients:jedis:4.2.2'
+    implementation 'redis.clients:jedis:4.2.3'
     implementation 'com.google.code.gson:gson:2.8.9'
     implementation 'com.google.guava:guava:23.0'
     testImplementation 'org.testcontainers:testcontainers'
diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle
index b0d64f0524b..87c54f81a8d 100644
--- a/examples/redis-backed-cache/build.gradle
+++ b/examples/redis-backed-cache/build.gradle
@@ -8,7 +8,7 @@ repositories {
 
 dependencies {
     compileOnly 'org.slf4j:slf4j-api:1.7.32'
-    implementation 'redis.clients:jedis:4.2.2'
+    implementation 'redis.clients:jedis:4.2.3'
     implementation 'com.google.code.gson:gson:2.8.9'
     implementation 'com.google.guava:guava:23.0'
     testImplementation 'org.testcontainers:testcontainers'
diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle
index 2b7c3b7dda4..0195c1704f8 100644
--- a/examples/singleton-container/build.gradle
+++ b/examples/singleton-container/build.gradle
@@ -8,7 +8,7 @@ repositories {
 
 dependencies {
 
-    implementation 'redis.clients:jedis:4.2.2'
+    implementation 'redis.clients:jedis:4.2.3'
     implementation 'com.google.code.gson:gson:2.8.9'
     implementation 'com.google.guava:guava:23.0'
     compileOnly 'org.slf4j:slf4j-api:1.7.32'

From f4281805934055ac969c6b9aedca4fd5620362f3 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:28 +0000
Subject: [PATCH 2598/5615] Bump assertj-core from 3.22.0 to 3.23.1 in
 /modules/r2dbc

Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1.
- [Release notes](https://github.com/assertj/assertj-core/releases)
- [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1)

---
updated-dependencies:
- dependency-name: org.assertj:assertj-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/r2dbc/build.gradle | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle
index 11e22e2bc9f..51199c9a53c 100644
--- a/modules/r2dbc/build.gradle
+++ b/modules/r2dbc/build.gradle
@@ -11,10 +11,10 @@ dependencies {
     api project(':testcontainers')
     api 'io.r2dbc:r2dbc-spi:0.8.1.RELEASE'
 
-    testImplementation 'org.assertj:assertj-core:3.22.0'
+    testImplementation 'org.assertj:assertj-core:3.23.1'
     testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.12.RELEASE'
     testImplementation project(':postgresql')
 
     testFixturesImplementation 'io.projectreactor:reactor-core:3.4.18'
-    testFixturesImplementation 'org.assertj:assertj-core:3.14.0'
+    testFixturesImplementation 'org.assertj:assertj-core:3.23.1'
 }

From 1fe3c1f17ef7f02bbb032fb3e1bc01ce92198b4c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:29 +0000
Subject: [PATCH 2599/5615] Bump google-cloud-datastore from 2.4.0 to 2.7.0 in
 /modules/gcloud

Bumps [google-cloud-datastore](https://github.com/googleapis/java-datastore) from 2.4.0 to 2.7.0.
- [Release notes](https://github.com/googleapis/java-datastore/releases)
- [Changelog](https://github.com/googleapis/java-datastore/blob/main/CHANGELOG.md)
- [Commits](https://github.com/googleapis/java-datastore/compare/v2.4.0...v2.7.0)

---
updated-dependencies:
- dependency-name: com.google.cloud:google-cloud-datastore
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/gcloud/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle
index 32854badc04..cc674a584b2 100644
--- a/modules/gcloud/build.gradle
+++ b/modules/gcloud/build.gradle
@@ -3,7 +3,7 @@ description = "Testcontainers :: GCloud"
 dependencies {
     api project(':testcontainers')
 
-    testImplementation 'com.google.cloud:google-cloud-datastore:2.4.0'
+    testImplementation 'com.google.cloud:google-cloud-datastore:2.7.0'
     testImplementation 'com.google.cloud:google-cloud-firestore:3.0.21'
     testImplementation 'com.google.cloud:google-cloud-pubsub:1.119.0'
     testImplementation 'com.google.cloud:google-cloud-spanner:6.20.0'

From 133233647c587993bc5214940240161a159914f8 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:36 +0000
Subject: [PATCH 2600/5615] Bump rest-assured from 5.0.1 to 5.1.0 in
 /modules/vault

Bumps [rest-assured](https://github.com/rest-assured/rest-assured) from 5.0.1 to 5.1.0.
- [Release notes](https://github.com/rest-assured/rest-assured/releases)
- [Changelog](https://github.com/rest-assured/rest-assured/blob/master/changelog.txt)
- [Commits](https://github.com/rest-assured/rest-assured/compare/rest-assured-5.0.1...rest-assured-5.1.0)

---
updated-dependencies:
- dependency-name: io.rest-assured:rest-assured
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/vault/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle
index f6e94f2139d..1ffc366987c 100644
--- a/modules/vault/build.gradle
+++ b/modules/vault/build.gradle
@@ -4,7 +4,7 @@ dependencies {
     api project(':testcontainers')
 
     testImplementation 'com.bettercloud:vault-java-driver:5.1.0'
-    testImplementation 'io.rest-assured:rest-assured:5.0.1'
+    testImplementation 'io.rest-assured:rest-assured:5.1.0'
     testImplementation 'org.assertj:assertj-core:3.23.1'
 
 }

From a8cbbdce72ba627410935824034ba82117a3bf5c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:39 +0000
Subject: [PATCH 2601/5615] Bump aws-java-sdk-sqs from 1.12.210 to 1.12.233 in
 /modules/localstack

Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.12.210 to 1.12.233.
- [Release notes](https://github.com/aws/aws-sdk-java/releases)
- [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.210...1.12.233)

---
updated-dependencies:
- dependency-name: com.amazonaws:aws-java-sdk-sqs
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/localstack/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle
index f512b3d5fab..5fc67214e69 100644
--- a/modules/localstack/build.gradle
+++ b/modules/localstack/build.gradle
@@ -5,7 +5,7 @@ dependencies {
 
     compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.231'
     testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.231'
-    testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.210'
+    testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.233'
     testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.232'
     testImplementation 'software.amazon.awssdk:s3:2.17.181'
     testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2'

From 0b15e6ecd3dbbb96bb9d9f4c2ccc12ba1b4dd5eb Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:40 +0000
Subject: [PATCH 2602/5615] Bump google-cloud-spanner from 6.20.0 to 6.25.5 in
 /modules/gcloud

Bumps [google-cloud-spanner](https://github.com/googleapis/java-spanner) from 6.20.0 to 6.25.5.
- [Release notes](https://github.com/googleapis/java-spanner/releases)
- [Changelog](https://github.com/googleapis/java-spanner/blob/main/CHANGELOG.md)
- [Commits](https://github.com/googleapis/java-spanner/compare/v6.20.0...v6.25.5)

---
updated-dependencies:
- dependency-name: com.google.cloud:google-cloud-spanner
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/gcloud/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle
index 32854badc04..29df32c0b33 100644
--- a/modules/gcloud/build.gradle
+++ b/modules/gcloud/build.gradle
@@ -6,7 +6,7 @@ dependencies {
     testImplementation 'com.google.cloud:google-cloud-datastore:2.4.0'
     testImplementation 'com.google.cloud:google-cloud-firestore:3.0.21'
     testImplementation 'com.google.cloud:google-cloud-pubsub:1.119.0'
-    testImplementation 'com.google.cloud:google-cloud-spanner:6.20.0'
+    testImplementation 'com.google.cloud:google-cloud-spanner:6.25.5'
     testImplementation 'com.google.cloud:google-cloud-bigtable:2.8.0'
     testImplementation 'org.assertj:assertj-core:3.22.0'
 }

From b544343eeb97ffa2e6ca4c4e4c53e0af4910e9fb Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:41 +0000
Subject: [PATCH 2603/5615] Bump assertj-core from 3.22.0 to 3.23.1 in
 /modules/gcloud

Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1.
- [Release notes](https://github.com/assertj/assertj-core/releases)
- [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1)

---
updated-dependencies:
- dependency-name: org.assertj:assertj-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/gcloud/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle
index 32854badc04..9b3bcd403de 100644
--- a/modules/gcloud/build.gradle
+++ b/modules/gcloud/build.gradle
@@ -8,5 +8,5 @@ dependencies {
     testImplementation 'com.google.cloud:google-cloud-pubsub:1.119.0'
     testImplementation 'com.google.cloud:google-cloud-spanner:6.20.0'
     testImplementation 'com.google.cloud:google-cloud-bigtable:2.8.0'
-    testImplementation 'org.assertj:assertj-core:3.22.0'
+    testImplementation 'org.assertj:assertj-core:3.23.1'
 }

From 4ad81aebc92107af531b970cc3db04eda00d27c7 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:56 +0000
Subject: [PATCH 2604/5615] Bump mockito-core from 4.4.0 to 4.6.1 in
 /modules/junit-jupiter

Bumps [mockito-core](https://github.com/mockito/mockito) from 4.4.0 to 4.6.1.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v4.4.0...v4.6.1)

---
updated-dependencies:
- dependency-name: org.mockito:mockito-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/junit-jupiter/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle
index 5c42d57fcbb..27d62be6b6d 100644
--- a/modules/junit-jupiter/build.gradle
+++ b/modules/junit-jupiter/build.gradle
@@ -9,7 +9,7 @@ dependencies {
     testImplementation 'com.zaxxer:HikariCP:4.0.3'
     testImplementation 'redis.clients:jedis:4.2.2'
     testImplementation 'org.apache.httpcomponents:httpclient:4.5.13'
-    testImplementation ('org.mockito:mockito-core:4.4.0') {
+    testImplementation ('org.mockito:mockito-core:4.6.1') {
         exclude(module: 'hamcrest-core')
     }
     testImplementation 'org.assertj:assertj-core:3.23.1'

From 979d74a785daa96fc380a346c64491d1710503ea Mon Sep 17 00:00:00 2001
From: Maxim Bartkov <84171296+MaxBartkov@users.noreply.github.com>
Date: Sat, 11 Jun 2022 00:29:07 +0300
Subject: [PATCH 2605/5615] Added the Bucket4j library to "Who is using
 Testcontainers?" (#5480)

---
 docs/index.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/docs/index.md b/docs/index.md
index b2c11d6d8e8..0ad72bcb400 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -201,6 +201,7 @@ A huge thank you to our sponsors:
 * [Togglz](https://www.togglz.org/) - Feature Flags for the Java platform
 * [Byzer](https://www.byzer.org/home) - Integration tests for Data and AI platforms are based on multiple versions of Byzer, Ray and Apache Spark.
 * [Apache SeaTunnel](https://github.com/apache/incubator-seatunnel) - Integration testing with different datasource.
+* [Bucket4j](https://github.com/bucket4j/bucket4j) - Java rate-limiting library based on the token-bucket algorithm.
 
 ## License
 

From e7cdfa31af08074aa9dbb3f3d21a622144995b9d Mon Sep 17 00:00:00 2001
From: anatoly0karyakin 
Date: Mon, 13 Jun 2022 14:02:09 +0300
Subject: [PATCH 2606/5615] Don't ignore sql test query exception, add it as
 cause exception (#5484)

---
 .../org/testcontainers/containers/JdbcDatabaseContainer.java | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java
index 84d242a29c7..0b80eaac406 100644
--- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java
+++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java
@@ -151,6 +151,7 @@ protected void waitUntilContainerStarted() {
         // Repeatedly try and open a connection to the DB and execute a test query
         long start = System.currentTimeMillis();
 
+        Exception lastConnectionException = null;
         while (System.currentTimeMillis() < start + (1000 * startupTimeoutSeconds)) {
             if (!isRunning()) {
                 Thread.sleep(100L);
@@ -165,6 +166,7 @@ protected void waitUntilContainerStarted() {
                     // we explicitly want this exception to fail fast without retries
                     throw e;
                 } catch (Exception e) {
+                    lastConnectionException = e;
                     // ignore so that we can try again
                     logger().debug("Failure when trying test query", e);
                     Thread.sleep(100L);
@@ -176,7 +178,8 @@ protected void waitUntilContainerStarted() {
             String.format(
                 "Container is started, but cannot be accessed by (JDBC URL: %s), please check container logs",
                 this.getJdbcUrl()
-            )
+            ),
+            lastConnectionException
         );
     }
 

From 7f9293b089bf4a2e27d28f12e91ef9b22963c473 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?=
 
Date: Mon, 13 Jun 2022 11:32:19 -0500
Subject: [PATCH 2607/5615] [LocalStack] Deprecate methods using aws sdk v1
 (#5489)

Currently, `LocalStackContainer` depends on aws sdk v1. We have received
feedback about moving to aws sdk v2. However, we think that the
class already provides the enough information to be used such as
`getEndpointOverride()`, `getRegion()`, `getAccessKey()` and `getSecretKey`.
So, we are deprecating `getDefaultCredentialsProvider()` and `getEndpointConfiguration`
which are going to be removed in the feature and `LocalStack` module will
not depend on any aws sdk.
---
 .../containers/localstack/LocalStackContainer.java            | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java
index d37c80d06fa..f3f905b84f6 100644
--- a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java
+++ b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java
@@ -193,7 +193,9 @@ public LocalStackContainer withServices(EnabledService... services) {
      *
      * @param service the service that is to be accessed
      * @return an {@link AwsClientBuilder.EndpointConfiguration}
+     * @deprecated {@link LocalStackContainer} will not depend on aws sdk.
      */
+    @Deprecated
     public AwsClientBuilder.EndpointConfiguration getEndpointConfiguration(Service service) {
         return new AwsClientBuilder.EndpointConfiguration(getEndpointOverride(service).toString(), getRegion());
     }
@@ -257,7 +259,9 @@ private int getServicePort(EnabledService service) {
              .build()
      
* @return an {@link AWSCredentialsProvider} + * @deprecated {@link LocalStackContainer} will not depend on aws sdk. */ + @Deprecated public AWSCredentialsProvider getDefaultCredentialsProvider() { return new AWSStaticCredentialsProvider(new BasicAWSCredentials(getAccessKey(), getSecretKey())); } From 2cd6b560bcb67087b12e172bbde6bf5902f853f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jun 2022 02:32:59 +0000 Subject: [PATCH 2608/5615] Bump gson from 2.8.9 to 2.9.0 in /examples Bumps [gson](https://github.com/google/gson) from 2.8.9 to 2.9.0. - [Release notes](https://github.com/google/gson/releases) - [Changelog](https://github.com/google/gson/blob/master/CHANGELOG.md) - [Commits](https://github.com/google/gson/compare/gson-parent-2.8.9...gson-parent-2.9.0) --- updated-dependencies: - dependency-name: com.google.code.gson:gson dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/disque-job-queue/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index bfb89f9cef2..1dc3497ec42 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly "org.projectlombok:lombok:1.18.24" annotationProcessor "org.projectlombok:lombok:1.18.24" implementation 'biz.paluch.redis:spinach:0.3' - implementation 'com.google.code.gson:gson:2.8.9' + implementation 'com.google.code.gson:gson:2.9.0' implementation 'com.google.guava:guava:23.0' testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.mockito:mockito-all:1.10.19' diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index ba883520719..56ba5934bdb 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -9,7 +9,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' implementation 'redis.clients:jedis:4.2.3' - implementation 'com.google.code.gson:gson:2.8.9' + implementation 'com.google.code.gson:gson:2.9.0' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'ch.qos.logback:logback-classic:1.2.11' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 87c54f81a8d..c563cc658d8 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -9,7 +9,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' implementation 'redis.clients:jedis:4.2.3' - implementation 'com.google.code.gson:gson:2.8.9' + implementation 'com.google.code.gson:gson:2.9.0' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'junit:junit:4.13.2' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 0195c1704f8..416f29de095 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -9,7 +9,7 @@ repositories { dependencies { implementation 'redis.clients:jedis:4.2.3' - implementation 'com.google.code.gson:gson:2.8.9' + implementation 'com.google.code.gson:gson:2.9.0' implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.32' From 2fec86109e9bedb6f18d5627a6e7074b019e02a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jun 2022 02:33:00 +0000 Subject: [PATCH 2609/5615] Bump slf4j-api from 1.7.32 to 1.7.36 in /examples Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 1.7.32 to 1.7.36. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_1.7.32...v_1.7.36) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index a801e4f8588..f25127f75eb 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -6,7 +6,7 @@ repositories { mavenCentral() } dependencies { - compileOnly 'org.slf4j:slf4j-api:1.7.32' + compileOnly 'org.slf4j:slf4j-api:1.7.36' implementation 'com.squareup.okhttp3:okhttp:4.9.3' implementation 'org.json:json:20220320' testImplementation 'org.postgresql:postgresql:42.3.6' diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index ba883520719..d7e174d55c1 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:1.7.32' + compileOnly 'org.slf4j:slf4j-api:1.7.36' implementation 'redis.clients:jedis:4.2.3' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 87c54f81a8d..11bec04a48b 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:1.7.32' + compileOnly 'org.slf4j:slf4j-api:1.7.36' implementation 'redis.clients:jedis:4.2.3' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 0195c1704f8..61a63f6fab5 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -11,7 +11,7 @@ dependencies { implementation 'redis.clients:jedis:4.2.3' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' - compileOnly 'org.slf4j:slf4j-api:1.7.32' + compileOnly 'org.slf4j:slf4j-api:1.7.36' testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.testcontainers:testcontainers' From a35a4e13eead2a10e13bd9a62264256eb13508c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jun 2022 02:33:28 +0000 Subject: [PATCH 2610/5615] Bump google-cloud-firestore from 3.0.21 to 3.2.0 in /modules/gcloud Bumps [google-cloud-firestore](https://github.com/googleapis/java-firestore) from 3.0.21 to 3.2.0. - [Release notes](https://github.com/googleapis/java-firestore/releases) - [Changelog](https://github.com/googleapis/java-firestore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-firestore/compare/v3.0.21...v3.2.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-firestore dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index f1060b8ec83..cdea13f5fac 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':testcontainers') testImplementation 'com.google.cloud:google-cloud-datastore:2.7.0' - testImplementation 'com.google.cloud:google-cloud-firestore:3.0.21' + testImplementation 'com.google.cloud:google-cloud-firestore:3.2.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.119.0' testImplementation 'com.google.cloud:google-cloud-spanner:6.25.5' testImplementation 'com.google.cloud:google-cloud-bigtable:2.8.0' From 7831ef2bceb076d584c69ffd29eb5f2f3bb62e39 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jun 2022 02:33:28 +0000 Subject: [PATCH 2611/5615] Bump assertj-core from 3.22.0 to 3.23.1 in /modules/azure Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/azure/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle index f26028b8ede..6656445af09 100644 --- a/modules/azure/build.gradle +++ b/modules/azure/build.gradle @@ -5,6 +5,6 @@ dependencies { // TODO use JDK's HTTP client and/or Apache HttpClient5 shaded 'com.squareup.okhttp3:okhttp:3.14.9' - testImplementation 'org.assertj:assertj-core:3.22.0' + testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'com.azure:azure-cosmos:4.30.0' } From 731da03265983689c80d9c6faee96c77e910d2bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jun 2022 02:33:48 +0000 Subject: [PATCH 2612/5615] Bump common-custom-user-data-gradle-plugin in /examples Bumps common-custom-user-data-gradle-plugin from 1.7 to 1.7.2. --- updated-dependencies: - dependency-name: com.gradle:common-custom-user-data-gradle-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/settings.gradle b/examples/settings.gradle index 2046a7d282e..b9fb73f86c1 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -7,7 +7,7 @@ buildscript { dependencies { classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.10.0" classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10" - classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7" + classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7.2" } } From 4083228e0f72e9155a47b9a651c24ac896dd2a08 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Tue, 14 Jun 2022 18:30:46 +0200 Subject: [PATCH 2613/5615] Add section about MySQL root user password to docs (#5497) --- docs/modules/databases/mysql.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/modules/databases/mysql.md b/docs/modules/databases/mysql.md index 26fc62308ef..6e6265e91ba 100644 --- a/docs/modules/databases/mysql.md +++ b/docs/modules/databases/mysql.md @@ -12,6 +12,11 @@ is a directory on the classpath containing .cnf files, the following URL can be Any .cnf files in this classpath directory will be mapped into the database container's /etc/mysql/conf.d directory, and will be able to override server settings when the container starts. +## MySQL `root` user password + +If no custom password is specified, the container will use the default user password `test` for the `root` user as well. +When you specify a custom password for the database user, +this will also act as the password of the MySQL `root` user automatically. ## Adding this module to your project dependencies From d96137a6614f06c5288de6000d43c7dab726b024 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Tue, 14 Jun 2022 18:41:02 +0200 Subject: [PATCH 2614/5615] Add MariaDB `root` user password section to docs (#5498) --- docs/modules/databases/mariadb.md | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/docs/modules/databases/mariadb.md b/docs/modules/databases/mariadb.md index 495965bee0a..25367d629bd 100644 --- a/docs/modules/databases/mariadb.md +++ b/docs/modules/databases/mariadb.md @@ -2,22 +2,11 @@ See [Database containers](./index.md) for documentation and usage that is common to all relational database container types. -## Usage example +## MariaDB `root` user password -Running MariaDB as a stand-in for in a test: - -```java -public class SomeTest { - - @Rule - public MariaDBContainer mariaDB = new MariaDBContainer(); - - @Test - public void someTestMethod() { - String url = mariaDB.getJdbcUrl(); - - ... create a connection and run test as normal -``` +If no custom password is specified, the container will use the default user password `test` for the `root` user as well. +When you specify a custom password for the database user, +this will also act as the password of the MariaDB `root` user automatically. ## Adding this module to your project dependencies From b3f1b6676e934e67987fd564ddec701d6e0b6647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Tue, 14 Jun 2022 13:11:11 -0500 Subject: [PATCH 2615/5615] Add issue forms (#5490) Introducing issue forms for: bug report, enhacements and features. In case of help or question there is a link to the slack channel. Co-authored-by: Kevin Wittek --- .github/ISSUE_TEMPLATE/bug_report.yaml | 96 +++++++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 5 ++ .github/ISSUE_TEMPLATE/enhancement.yaml | 56 +++++++++++++++ .github/ISSUE_TEMPLATE/feature.yaml | 85 ++++++++++++++++++++++ 4 files changed, 242 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yaml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/enhancement.yaml create mode 100644 .github/ISSUE_TEMPLATE/feature.yaml diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml new file mode 100644 index 00000000000..80e0b47741d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -0,0 +1,96 @@ +name: Bug Report +description: File a bug report +title: "[Bug]: " +labels: ["type/bug"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + - type: dropdown + id: module + attributes: + label: Module + description: Which Testcontainers module are you using? + options: + - Core + - Azure + - Cassandra + - Clickhouse + - Couchbase + - DB2 + - Dynalite + - Elasticsearch + - GCloud + - HiveMQ + - InfluxDB + - K3S + - Kafka + - LocalStack + - MariaDB + - MockServer + - MongoDB + - MSSQLServer + - MySQL + - Neo4j + - NGINX + - Oracle-XE + - OrientDB + - PostgreSQL + - Presto + - Pulsar + - RabbitMQ + - Selenium + - Solr + - ToxiProxy + - Trino + - Vault + validations: + required: true + - type: input + id: tc-version + attributes: + label: Testcontainers version + description: Which Testcontainers version are you using? + placeholder: ex. 1.17.2 + validations: + required: true + - type: dropdown + id: latest-version + attributes: + label: Using the latest Testcontainers version? + description: If you are not using the latest version, can you update your project and try to reproduce the issue? Is it still happening? + options: + - 'Yes' + - 'No' + validations: + required: true + - type: textarea + id: docker-version + attributes: + label: Docker version + description: Please run `docker version` and copy and paste the output into this field. + render: shell + validations: + required: true + - type: textarea + id: what-happened + attributes: + label: What happened? + description: Provide the context and the expected result. + validations: + required: true + - type: textarea + id: logs + attributes: + label: Relevant log output + description: Please copy and paste any relevant log output. The content will be automatically formatted as code, so no need for backticks. + render: shell + - type: textarea + id: additional-information + attributes: + label: Additional Information + description: | + Any links or references to have more context about the issue. + + Tip: You can attach a minimal sample project to reproduce the issue or provide further log files by clicking into this area to focus it and then dragging files in. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000000..d27b0d086e9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Need help or have a question? + url: https://slack.testcontainers.org/ + about: Visit our slack channel. diff --git a/.github/ISSUE_TEMPLATE/enhancement.yaml b/.github/ISSUE_TEMPLATE/enhancement.yaml new file mode 100644 index 00000000000..f06ea99c8ba --- /dev/null +++ b/.github/ISSUE_TEMPLATE/enhancement.yaml @@ -0,0 +1,56 @@ +name: Enhancement +description: Suggest an enhancement +title: "[Enhancement]: " +labels: ["type/enhancement"] +body: + - type: markdown + attributes: + value: | + Please provide the following information + - type: dropdown + id: module + attributes: + label: Module + description: For which Testcontainers module does the enhancement proposal apply? + options: + - Core + - Azure + - Cassandra + - Clickhouse + - Couchbase + - DB2 + - Dynalite + - Elasticsearch + - GCloud + - HiveMQ + - InfluxDB + - K3S + - Kafka + - LocalStack + - MariaDB + - MockServer + - MongoDB + - MSSQLServer + - MySQL + - Neo4j + - NGINX + - Oracle-XE + - OrientDB + - PostgreSQL + - Presto + - Pulsar + - RabbitMQ + - Selenium + - Solr + - ToxiProxy + - Trino + - Vault + validations: + required: true + - type: textarea + id: proposal + attributes: + label: Proposal + description: What should be improved? What are the limitations of the current implications that would be solved by the proposal? + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/feature.yaml b/.github/ISSUE_TEMPLATE/feature.yaml new file mode 100644 index 00000000000..32edb0d25f5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature.yaml @@ -0,0 +1,85 @@ +name: Feature +description: Suggest a new feature +title: "[Feature]: " +labels: ["type/feature"] +body: + - type: markdown + attributes: + value: | + Please provide the following information + - type: dropdown + id: module + attributes: + label: Module + description: Is this feature related to any of the existing modules? + options: + - Core + - Azure + - Cassandra + - Clickhouse + - Couchbase + - DB2 + - Dynalite + - Elasticsearch + - GCloud + - HiveMQ + - InfluxDB + - K3S + - Kafka + - LocalStack + - MariaDB + - MockServer + - MongoDB + - MSSQLServer + - MySQL + - Neo4j + - NGINX + - Oracle-XE + - OrientDB + - PostgreSQL + - Presto + - Pulsar + - RabbitMQ + - Selenium + - Solr + - ToxiProxy + - Trino + - Vault + - New Module + - type: textarea + id: problem + attributes: + label: Problem + description: Is this feature related to a problem? Please describe it. + validations: + required: true + - type: textarea + id: solution + attributes: + label: Solution + description: What's the proposed solution for this feature? + validations: + required: true + - type: textarea + id: benefit + attributes: + label: Benefit + description: What's the benefit of addng this feature to the project? + validations: + required: true + - type: textarea + id: alternatives + attributes: + label: Benefit + description: Are there other alternatives? Please describe them. + validations: + required: true + - type: dropdown + id: contribute + attributes: + label: Would you like to help contributing this feature? + options: + - 'Yes' + - 'No' + validations: + required: true From c769e64d94a1d756245c53a62ab17670fcfff78c Mon Sep 17 00:00:00 2001 From: Hyeonmin Park Date: Thu, 16 Jun 2022 23:54:24 +0900 Subject: [PATCH 2616/5615] Remove free disk space check log in example (#5502) --- docs/features/configuration.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/features/configuration.md b/docs/features/configuration.md index 1761d1a2a71..cccae7a86df 100644 --- a/docs/features/configuration.md +++ b/docs/features/configuration.md @@ -30,7 +30,6 @@ Before running any containers Testcontainers will perform a set of startup check ``` ℹ︎ Checking the system... ✔ Docker version should be at least 1.6.0 - ✔ Docker environment should have more than 2GB free disk space ✔ File should be mountable ✔ A port exposed by a docker container should be accessible ``` From d57d7e940a02098b0f75c0599d4d4e8614e9c970 Mon Sep 17 00:00:00 2001 From: Cheng Pan Date: Fri, 17 Jun 2022 07:00:50 +0800 Subject: [PATCH 2617/5615] Docs: Add Spark ClickHouse Connector use case (#5270) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Eddú Meléndez Gonzales --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index 0ad72bcb400..3a99c88a5b1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -202,6 +202,7 @@ A huge thank you to our sponsors: * [Byzer](https://www.byzer.org/home) - Integration tests for Data and AI platforms are based on multiple versions of Byzer, Ray and Apache Spark. * [Apache SeaTunnel](https://github.com/apache/incubator-seatunnel) - Integration testing with different datasource. * [Bucket4j](https://github.com/bucket4j/bucket4j) - Java rate-limiting library based on the token-bucket algorithm. +* [Spark ClickHouse Connector](https://github.com/housepower/spark-clickhouse-connector) - Integration tests for Apache Spark with both single node ClickHouse instance and multi-node ClickHouse cluster. ## License From 5dc5c21b3f4ae29fcf05d7c6ba9b287b65cc2517 Mon Sep 17 00:00:00 2001 From: Cristian Briscaru <39033668+cristianbriscaru@users.noreply.github.com> Date: Fri, 17 Jun 2022 00:23:43 +0100 Subject: [PATCH 2618/5615] adds docs cockroach jdbc example (#5100) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Eddú Meléndez Gonzales --- docs/modules/databases/jdbc.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/modules/databases/jdbc.md b/docs/modules/databases/jdbc.md index b2a1588ae22..3c52dd1d8a8 100644 --- a/docs/modules/databases/jdbc.md +++ b/docs/modules/databases/jdbc.md @@ -47,6 +47,10 @@ Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database `jdbc:tc:trino:352://localhost/memory/default` +#### Using CockroachDB + +`jdbc:tc:cockroach:v21.2.3:///databasename` + ### Using a classpath init script Testcontainers can run an init script after the database container is started, but before your code is given a connection to it. The script must be on the classpath, and is referenced as follows: From 745bfc337071ffec7289758df0fe136c2c307430 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Mon, 20 Jun 2022 21:45:49 +0200 Subject: [PATCH 2619/5615] Add color for links (#5508) The rebranding of the docs removed any special rendering of links, so they became invisible to users. This change adds an explicit color for links (with the color taken from the TC logo). This also implicitly highlights the currently selected page in the side-navigation panel. --- docs/css/extra.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/css/extra.css b/docs/css/extra.css index 4c11e3df748..e97480c5341 100644 --- a/docs/css/extra.css +++ b/docs/css/extra.css @@ -5,6 +5,7 @@ h1, h2, h3, h4, h5, h6 { } [data-md-color-scheme="testcontainers"] { - --md-primary-fg-color: #291A3F; + --md-primary-fg-color: #291A3F; --md-accent-fg-color: #291A3F; + --md-typeset-a-color: #0C94AA; } From 2afe714517640b24a404124dce23fe49bbe910f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Boschi?= Date: Tue, 21 Jun 2022 01:41:15 +0200 Subject: [PATCH 2620/5615] Pulsar: add flag to enable transactions and set configuration (#5479) * Add `withTransactions()` method to enable transactions on Pulsar container * Set default version to latest released (2.10.0) * New docker command that enables the user to easily change configuration parameters * Added new tests to cover new methods * Improved the documentation with simple usage and new methods --- docs/modules/pulsar.md | 46 +++++++ modules/pulsar/build.gradle | 4 +- .../containers/PulsarContainer.java | 57 +++++++-- .../containers/PulsarContainerTest.java | 113 ++++++++++++++++-- 4 files changed, 195 insertions(+), 25 deletions(-) diff --git a/docs/modules/pulsar.md b/docs/modules/pulsar.md index 6d16a248872..f74c315cbdc 100644 --- a/docs/modules/pulsar.md +++ b/docs/modules/pulsar.md @@ -1,5 +1,51 @@ # Apache Pulsar Module +Testcontainers can be used to automatically create [Apache Pulsar](https://pulsar.apache.org) containers without external services. + +It's based on the official Apache Pulsar docker image, it is recommended to read the [official guide](https://pulsar.apache.org/docs/next/getting-started-docker/). + +## Example + +Create a `PulsarContainer` to use it in your tests: + + +[Create a Pulsar container](../../modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java) inside_block:constructorWithVersion + + +Then you can retrieve the broker and the admin url: + + +[Get broker and admin urls](../../modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java) inside_block:coordinates + + +## Options + +### Configuration +If you need to set Pulsar configuration variables you can use the native APIs and set each variable with `PULSAR_PREFIX_` as prefix. + +For example, if you want to enable `brokerDeduplicationEnabled`: + + +[Set configuration variables](../../modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java) inside_block:constructorWithEnv + + +### Pulsar IO + +If you need to test Pulsar IO framework you can enable the Pulsar Functions Worker: + + +[Create a Pulsar container with functions worker](../../modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java) inside_block:constructorWithFunctionsWorker + + +### Pulsar Transactions + +If you need to test Pulsar Transactions you can enable the transactions feature: + + +[Create a Pulsar container with transactions](../../modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java) inside_block:constructorWithTransactions + + + ## Adding this module to your project dependencies Add the following dependency to your `pom.xml`/`build.gradle` file: diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 69c63cc3033..a1b24efba37 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Pulsar" dependencies { api project(':testcontainers') - testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.7.4' + testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.10.0' testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.23.1' - testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.7.4' + testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.10.0' } diff --git a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java index 9b14e90d736..069fccaae65 100644 --- a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java +++ b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java @@ -2,8 +2,12 @@ import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.containers.wait.strategy.WaitAllStrategy; +import org.testcontainers.containers.wait.strategy.WaitStrategy; import org.testcontainers.utility.DockerImageName; +import java.util.ArrayList; +import java.util.List; + /** * This container wraps Apache Pulsar running in standalone mode */ @@ -15,13 +19,21 @@ public class PulsarContainer extends GenericContainer { public static final String METRICS_ENDPOINT = "/metrics"; + /** + * See SystemTopicNames. + */ + private static final String TRANSACTION_TOPIC_ENDPOINT = + "/admin/v2/persistent/pulsar/system/transaction_coordinator_assign/partitions"; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("apachepulsar/pulsar"); @Deprecated - private static final String DEFAULT_TAG = "2.2.0"; + private static final String DEFAULT_TAG = "2.10.0"; private boolean functionsWorkerEnabled = false; + private boolean transactionsEnabled = false; + /** * @deprecated use {@link PulsarContainer(DockerImageName)} instead */ @@ -41,24 +53,13 @@ public PulsarContainer(String pulsarVersion) { public PulsarContainer(final DockerImageName dockerImageName) { super(dockerImageName); dockerImageName.assertCompatibleWith(DockerImageName.parse("apachepulsar/pulsar")); - withExposedPorts(BROKER_PORT, BROKER_HTTP_PORT); - withCommand("/pulsar/bin/pulsar", "standalone", "--no-functions-worker", "-nss"); - waitingFor(Wait.forHttp(METRICS_ENDPOINT).forStatusCode(200).forPort(BROKER_HTTP_PORT)); } @Override protected void configure() { super.configure(); - - if (functionsWorkerEnabled) { - withCommand("/pulsar/bin/pulsar", "standalone"); - waitingFor( - new WaitAllStrategy() - .withStrategy(waitStrategy) - .withStrategy(Wait.forLogMessage(".*Function worker service started.*", 1)) - ); - } + setupCommandAndEnv(); } public PulsarContainer withFunctionsWorker() { @@ -66,6 +67,11 @@ public PulsarContainer withFunctionsWorker() { return this; } + public PulsarContainer withTransactions() { + transactionsEnabled = true; + return this; + } + public String getPulsarBrokerUrl() { return String.format("pulsar://%s:%s", getHost(), getMappedPort(BROKER_PORT)); } @@ -73,4 +79,29 @@ public String getPulsarBrokerUrl() { public String getHttpServiceUrl() { return String.format("http://%s:%s", getHost(), getMappedPort(BROKER_HTTP_PORT)); } + + protected void setupCommandAndEnv() { + String standaloneBaseCommand = + "/pulsar/bin/apply-config-from-env.py /pulsar/conf/standalone.conf " + "&& bin/pulsar standalone"; + + if (!functionsWorkerEnabled) { + standaloneBaseCommand += " --no-functions-worker -nss"; + } + + withCommand("/bin/bash", "-c", standaloneBaseCommand); + + List waitStrategies = new ArrayList<>(); + waitStrategies.add(Wait.defaultWaitStrategy()); + waitStrategies.add(Wait.forHttp(METRICS_ENDPOINT).forStatusCode(200).forPort(BROKER_HTTP_PORT)); + if (transactionsEnabled) { + withEnv("PULSAR_PREFIX_transactionCoordinatorEnabled", "true"); + waitStrategies.add(Wait.forHttp(TRANSACTION_TOPIC_ENDPOINT).forStatusCode(200).forPort(BROKER_HTTP_PORT)); + } + if (functionsWorkerEnabled) { + waitStrategies.add(Wait.forLogMessage(".*Function worker service started.*", 1)); + } + final WaitAllStrategy compoundedWaitStrategy = new WaitAllStrategy(); + waitStrategies.forEach(compoundedWaitStrategy::withStrategy); + waitingFor(compoundedWaitStrategy); + } } diff --git a/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java b/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java index 6d739dfbf98..91397d00c40 100644 --- a/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java +++ b/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java @@ -6,6 +6,9 @@ import org.apache.pulsar.client.api.Message; import org.apache.pulsar.client.api.Producer; import org.apache.pulsar.client.api.PulsarClient; +import org.apache.pulsar.client.api.Schema; +import org.apache.pulsar.client.api.SubscriptionInitialPosition; +import org.apache.pulsar.client.api.transaction.Transaction; import org.junit.Test; import org.testcontainers.utility.DockerImageName; @@ -19,11 +22,33 @@ public class PulsarContainerTest { public static final String TEST_TOPIC = "test_topic"; - private static final DockerImageName PULSAR_IMAGE = DockerImageName.parse("apachepulsar/pulsar:2.2.0"); + private static final DockerImageName PULSAR_IMAGE = DockerImageName.parse("apachepulsar/pulsar:2.10.0"); @Test public void testUsage() throws Exception { - try (PulsarContainer pulsar = new PulsarContainer(PULSAR_IMAGE)) { + try ( + // do not use PULSAR_IMAGE to make the doc looks easier + // constructorWithVersion { + PulsarContainer pulsar = new PulsarContainer(DockerImageName.parse("apachepulsar/pulsar:2.10.0")); + // } + ) { + pulsar.start(); + // coordinates { + final String pulsarBrokerUrl = pulsar.getPulsarBrokerUrl(); + final String httpServiceUrl = pulsar.getHttpServiceUrl(); + // } + testPulsarFunctionality(pulsarBrokerUrl); + } + } + + @Test + public void envVarsUsage() throws Exception { + try ( + // constructorWithEnv { + PulsarContainer pulsar = new PulsarContainer(PULSAR_IMAGE) + .withEnv("PULSAR_PREFIX_brokerDeduplicationEnabled", "true"); + // } + ) { pulsar.start(); testPulsarFunctionality(pulsar.getPulsarBrokerUrl()); } @@ -31,24 +56,69 @@ public void testUsage() throws Exception { @Test public void shouldNotEnableFunctionsWorkerByDefault() throws Exception { - try (PulsarContainer pulsar = new PulsarContainer("2.5.1")) { + try (PulsarContainer pulsar = new PulsarContainer(PULSAR_IMAGE)) { pulsar.start(); - PulsarAdmin pulsarAdmin = PulsarAdmin.builder().serviceHttpUrl(pulsar.getHttpServiceUrl()).build(); - - assertThatThrownBy(() -> pulsarAdmin.functions().getFunctions("public", "default")) - .isInstanceOf(PulsarAdminException.class); + try (PulsarAdmin pulsarAdmin = PulsarAdmin.builder().serviceHttpUrl(pulsar.getHttpServiceUrl()).build()) { + assertThatThrownBy(() -> pulsarAdmin.functions().getFunctions("public", "default")) + .isInstanceOf(PulsarAdminException.class); + } } } @Test public void shouldWaitForFunctionsWorkerStarted() throws Exception { - try (PulsarContainer pulsar = new PulsarContainer("2.5.1").withFunctionsWorker()) { + try ( + // constructorWithFunctionsWorker { + PulsarContainer pulsar = new PulsarContainer(PULSAR_IMAGE).withFunctionsWorker(); + // } + ) { + pulsar.start(); + + try (PulsarAdmin pulsarAdmin = PulsarAdmin.builder().serviceHttpUrl(pulsar.getHttpServiceUrl()).build()) { + assertThat(pulsarAdmin.functions().getFunctions("public", "default")).hasSize(0); + } + } + } + + @Test + public void testTransactions() throws Exception { + try ( + // constructorWithTransactions { + PulsarContainer pulsar = new PulsarContainer(PULSAR_IMAGE).withTransactions(); + // } + ) { pulsar.start(); - PulsarAdmin pulsarAdmin = PulsarAdmin.builder().serviceHttpUrl(pulsar.getHttpServiceUrl()).build(); + try (PulsarAdmin pulsarAdmin = PulsarAdmin.builder().serviceHttpUrl(pulsar.getHttpServiceUrl()).build()) { + assertThat( + pulsarAdmin + .topics() + .getList("pulsar/system") + .contains("persistent://pulsar/system/transaction_coordinator_assign-partition-0") + ) + .isTrue(); + } + testTransactionFunctionality(pulsar.getPulsarBrokerUrl()); + } + } + + @Test + public void testTransactionsAndFunctionsWorker() throws Exception { + try (PulsarContainer pulsar = new PulsarContainer(PULSAR_IMAGE).withTransactions().withFunctionsWorker()) { + pulsar.start(); - assertThat(pulsarAdmin.functions().getFunctions("public", "default")).hasSize(0); + try (PulsarAdmin pulsarAdmin = PulsarAdmin.builder().serviceHttpUrl(pulsar.getHttpServiceUrl()).build();) { + assertThat( + pulsarAdmin + .topics() + .getList("pulsar/system") + .contains("persistent://pulsar/system/transaction_coordinator_assign-partition-0") + ) + .isTrue(); + assertThat(pulsarAdmin.functions().getFunctions("public", "default")).hasSize(0); + } + testTransactionFunctionality(pulsar.getPulsarBrokerUrl()); } } @@ -65,4 +135,27 @@ protected void testPulsarFunctionality(String pulsarBrokerUrl) throws Exception assertThat(new String(message.getData())).isEqualTo("test containers"); } } + + protected void testTransactionFunctionality(String pulsarBrokerUrl) throws Exception { + try ( + PulsarClient client = PulsarClient.builder().serviceUrl(pulsarBrokerUrl).enableTransaction(true).build(); + Consumer consumer = client + .newConsumer(Schema.STRING) + .topic("transaction-topic") + .subscriptionInitialPosition(SubscriptionInitialPosition.Earliest) + .subscriptionName("test-transaction-sub") + .subscribe(); + Producer producer = client + .newProducer(Schema.STRING) + .sendTimeout(0, TimeUnit.SECONDS) + .topic("transaction-topic") + .create() + ) { + final Transaction transaction = client.newTransaction().build().get(); + producer.newMessage(transaction).value("first").send(); + transaction.commit(); + Message message = consumer.receive(); + assertThat(message.getValue()).isEqualTo("first"); + } + } } From a2acf3cc6f828ad81a92eb4cc624ee9ddd29c2e2 Mon Sep 17 00:00:00 2001 From: Daniel Shuy Date: Tue, 21 Jun 2022 07:43:11 +0800 Subject: [PATCH 2621/5615] Feature template: Fix typo in Alternatives section label (#5505) --- .github/ISSUE_TEMPLATE/feature.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature.yaml b/.github/ISSUE_TEMPLATE/feature.yaml index 32edb0d25f5..9e79553d357 100644 --- a/.github/ISSUE_TEMPLATE/feature.yaml +++ b/.github/ISSUE_TEMPLATE/feature.yaml @@ -70,7 +70,7 @@ body: - type: textarea id: alternatives attributes: - label: Benefit + label: Alternatives description: Are there other alternatives? Please describe them. validations: required: true From 00faa95834120b482117d7d0363e9b713adb18eb Mon Sep 17 00:00:00 2001 From: Yannick Weber Date: Wed, 22 Jun 2022 10:56:25 +0200 Subject: [PATCH 2622/5615] [HiveMQ] do not delete /opt/hivemq/temp-extensions in startup command (#5509) * do not delete /opt/hivemq/temp-extensions in startup command * command does not get stuck when '/opt/hivemq/temp-extensions' does not exist --- .../main/java/org/testcontainers/hivemq/HiveMQContainer.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java index 242cc7bdb43..8a2832f65bd 100644 --- a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java +++ b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java @@ -123,8 +123,7 @@ protected void configure() { "-c", removeCommand + "cp -r '/opt/hivemq/temp-extensions/'* /opt/hivemq/extensions/ " + - "; rm -rf /opt/hivemq/temp-extensions/** " + - "&& chmod -R 777 /opt/hivemq/extensions " + + "; chmod -R 777 /opt/hivemq/extensions " + "&& /opt/docker-entrypoint.sh /opt/hivemq/bin/run.sh" ); } From 1f7dc46e2f48d76e6770dce10479a2c63d30bad1 Mon Sep 17 00:00:00 2001 From: Jaromir Hamala Date: Wed, 22 Jun 2022 19:10:26 +0200 Subject: [PATCH 2623/5615] Use sshd container 1.1.0 (#5486) --- .../testcontainers/containers/PortForwardingContainer.java | 7 +++++-- docs/features/configuration.md | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java b/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java index 22d364c0762..4a99ccacd91 100644 --- a/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java +++ b/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java @@ -31,14 +31,17 @@ public enum PortForwardingContainer { private Connection createSSHSession() { String password = UUID.randomUUID().toString(); container = - new GenericContainer<>(DockerImageName.parse("testcontainers/sshd:1.0.0")) + new GenericContainer<>(DockerImageName.parse("testcontainers/sshd:1.1.0")) .withExposedPorts(22) .withEnv("PASSWORD", password) .withCommand( "sh", "-c", // Disable ipv6 & Make it listen on all interfaces, not just localhost - "echo \"root:$PASSWORD\" | chpasswd && /usr/sbin/sshd -D -o PermitRootLogin=yes -o AddressFamily=inet -o GatewayPorts=yes" + // Enable algorithms supported by our ssh client library + "echo \"root:$PASSWORD\" | chpasswd && /usr/sbin/sshd -D -o PermitRootLogin=yes " + + "-o AddressFamily=inet -o GatewayPorts=yes -o AllowAgentForwarding=yes -o AllowTcpForwarding=yes " + + "-o KexAlgorithms=+diffie-hellman-group1-sha1 -o HostkeyAlgorithms=+ssh-rsa " ); container.start(); diff --git a/docs/features/configuration.md b/docs/features/configuration.md index cccae7a86df..fbb369fe6a6 100644 --- a/docs/features/configuration.md +++ b/docs/features/configuration.md @@ -52,7 +52,7 @@ Some companies disallow the usage of Docker Hub, but you can override `*.image` > **tinyimage.container.image = alpine:3.14** > Used to check whether images can be pulled at startup, and always required (unless [startup checks are disabled](#disabling-the-startup-checks)) -> **sshd.container.image = testcontainers/sshd:1.0.0** +> **sshd.container.image = testcontainers/sshd:1.1.0** > Required if [exposing host ports to containers](./networking.md#exposing-host-ports-to-the-container) > **vncrecorder.container.image = testcontainers/vnc-recorder:1.1.0** From cfc64bd2997000f67c46fa891dc402fd4ddbc5b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Wed, 22 Jun 2022 12:56:20 -0500 Subject: [PATCH 2624/5615] Add clickhouse/clickhouse-server compatibility (#5488) `clickhouse` was initially developed by Yandex and now it is OpenSource. See more [here](https://clickhouse.com/blog/yandex-opensources-click-house/) For backward compatibility, we will be supporting the image provided by Yandex too. --- .../containers/ClickHouseContainer.java | 4 +++- .../testcontainers/ClickhouseTestImages.java | 3 ++- .../clickhouse/SimpleClickhouseTest.java | 20 ++++++++++++++++++- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java index 4528c48e935..16bbeef6c81 100644 --- a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java +++ b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java @@ -13,6 +13,8 @@ public class ClickHouseContainer extends JdbcDatabaseContainer { private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("yandex/clickhouse-server"); + private static final DockerImageName CLICKHOUSE_IMAGE_NAME = DockerImageName.parse("clickhouse/clickhouse-server"); + @Deprecated public static final String IMAGE = DEFAULT_IMAGE_NAME.getUnversionedPart(); @@ -49,7 +51,7 @@ public ClickHouseContainer(String dockerImageName) { public ClickHouseContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME, CLICKHOUSE_IMAGE_NAME); withExposedPorts(HTTP_PORT, NATIVE_PORT); waitingFor( diff --git a/modules/clickhouse/src/test/java/org/testcontainers/ClickhouseTestImages.java b/modules/clickhouse/src/test/java/org/testcontainers/ClickhouseTestImages.java index 961df045bd1..a6707705ff8 100644 --- a/modules/clickhouse/src/test/java/org/testcontainers/ClickhouseTestImages.java +++ b/modules/clickhouse/src/test/java/org/testcontainers/ClickhouseTestImages.java @@ -3,5 +3,6 @@ import org.testcontainers.utility.DockerImageName; public interface ClickhouseTestImages { - DockerImageName CLICKHOUSE_IMAGE = DockerImageName.parse("yandex/clickhouse-server:18.10.3"); + DockerImageName YANDEX_CLICKHOUSE_IMAGE = DockerImageName.parse("yandex/clickhouse-server:18.10.3"); + DockerImageName CLICKHOUSE_IMAGE = DockerImageName.parse("clickhouse/clickhouse-server:21.9.2-alpine"); } diff --git a/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java b/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java index d2375891f8c..2f6edcc722d 100644 --- a/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java +++ b/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java @@ -1,20 +1,38 @@ package org.testcontainers.junit.clickhouse; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; import org.testcontainers.ClickhouseTestImages; import org.testcontainers.containers.ClickHouseContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; +import org.testcontainers.utility.DockerImageName; import java.sql.ResultSet; import java.sql.SQLException; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +@RunWith(Parameterized.class) public class SimpleClickhouseTest extends AbstractContainerDatabaseTest { + private final DockerImageName imageName; + + public SimpleClickhouseTest(DockerImageName imageName) { + this.imageName = imageName; + } + + @Parameterized.Parameters(name = "{0}") + public static Object[][] data() { + return new Object[][] { // + { ClickhouseTestImages.CLICKHOUSE_IMAGE }, + { ClickhouseTestImages.YANDEX_CLICKHOUSE_IMAGE }, + }; + } + @Test public void testSimple() throws SQLException { - try (ClickHouseContainer clickhouse = new ClickHouseContainer(ClickhouseTestImages.CLICKHOUSE_IMAGE)) { + try (ClickHouseContainer clickhouse = new ClickHouseContainer(this.imageName)) { clickhouse.start(); ResultSet resultSet = performQuery(clickhouse, "SELECT 1"); From a9c2e9e148097f4996dc7a96f85fced1ee857ddc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Wed, 22 Jun 2022 13:30:09 -0500 Subject: [PATCH 2625/5615] [LocalStack] Remove usage of deprecated methods (#5491) Improve documentation and tests avoiding usage of deprecated methods. --- docs/modules/localstack.md | 35 +++------ .../localstack/LocalStackContainer.java | 5 +- .../containers/localstack/LegacyModeTest.java | 38 +++++++--- .../localstack/LocalstackContainerTest.java | 75 ++++++++++++++++--- 4 files changed, 102 insertions(+), 51 deletions(-) diff --git a/docs/modules/localstack.md b/docs/modules/localstack.md index b551536af74..dcc474e484e 100644 --- a/docs/modules/localstack.md +++ b/docs/modules/localstack.md @@ -12,33 +12,18 @@ DockerImageName localstackImage = DockerImageName.parse("localstack/localstack:0 @Rule public LocalStackContainer localstack = new LocalStackContainer(localstackImage) .withServices(S3); - -@Test -public void someTestMethod() { - // AWS SDK v1 - AmazonS3 s3 = AmazonS3ClientBuilder - .standard() - .withEndpointConfiguration(localstack.getEndpointConfiguration(S3)) - .withCredentials(localstack.getDefaultCredentialsProvider()) - .build(); - - s3.createBucket("foo"); - s3.putObject("foo", "bar", "baz"); - - // AWS SDK v2 - S3Client s3 = S3Client - .builder() - .endpointOverride(localstack.getEndpointOverride(LocalStackContainer.Service.S3)) - .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create( - localstack.getAccessKey(), localstack.getSecretKey() - ))) - .region(Region.of(localstack.getRegion())) - .build(); - - s3.createBucket(b -> b.bucket("foo")); - s3.putObject(b -> b.bucket("foo").key("bar"), RequestBody.fromBytes("baz".getBytes())); ``` +## Creating a client using AWS SDK + + +[AWS SDK V1](../../modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java) inside_block:with_aws_sdk_v1 + + + +[AWS SDK V2](../../modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java) inside_block:with_aws_sdk_v2 + + Environment variables listed in [Localstack's README](https://github.com/localstack/localstack#configurations) may be used to customize Localstack's configuration. Use the `.withEnv(key, value)` method on `LocalStackContainer` to apply configuration settings. diff --git a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java index f3f905b84f6..146cb3200f1 100644 --- a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java +++ b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java @@ -27,10 +27,7 @@ /** *

Container for LocalStack, 'A fully functional local AWS cloud stack'.

*

{@link LocalStackContainer#withServices(Service...)} should be used to select which services - * are to be launched. See {@link Service} for available choices. It is advised that - * {@link LocalStackContainer#getEndpointConfiguration(Service)} and - * {@link LocalStackContainer#getDefaultCredentialsProvider()} - * be used to obtain compatible endpoint configuration and credentials, respectively.

+ * are to be launched. See {@link Service} for available choices. */ @Slf4j public class LocalStackContainer extends GenericContainer { diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java index a757c11e928..c075d2805d6 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java @@ -1,5 +1,6 @@ package org.testcontainers.containers.localstack; +import com.amazonaws.client.builder.AwsClientBuilder; import com.github.dockerjava.api.DockerClient; import lombok.AllArgsConstructor; import org.junit.BeforeClass; @@ -8,6 +9,7 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.localstack.LocalStackContainer.Service; import org.testcontainers.images.RemoteDockerImage; import org.testcontainers.utility.DockerImageName; @@ -46,20 +48,28 @@ public static Iterable constructors() { @Test public void samePortIsExposedForAllServices() { - localstack.withServices(LocalStackContainer.Service.S3, LocalStackContainer.Service.SQS); + localstack.withServices(Service.S3, Service.SQS); localstack.start(); try { assertTrue("A single port is exposed", localstack.getExposedPorts().size() == 1); assertEquals( "Endpoint overrides are different", - localstack.getEndpointOverride(LocalStackContainer.Service.S3).toString(), - localstack.getEndpointOverride(LocalStackContainer.Service.SQS).toString() + localstack.getEndpointOverride(Service.S3).toString(), + localstack.getEndpointOverride(Service.SQS).toString() ); assertEquals( "Endpoint configuration have different endpoints", - localstack.getEndpointConfiguration(LocalStackContainer.Service.S3).getServiceEndpoint(), - localstack.getEndpointConfiguration(LocalStackContainer.Service.SQS).getServiceEndpoint() + new AwsClientBuilder.EndpointConfiguration( + localstack.getEndpointOverride(Service.S3).toString(), + localstack.getRegion() + ) + .getServiceEndpoint(), + new AwsClientBuilder.EndpointConfiguration( + localstack.getEndpointOverride(Service.SQS).toString(), + localstack.getRegion() + ) + .getServiceEndpoint() ); } finally { localstack.stop(); @@ -103,20 +113,28 @@ public static Iterable constructors() { @Test public void differentPortsAreExposed() { - localstack.withServices(LocalStackContainer.Service.S3, LocalStackContainer.Service.SQS); + localstack.withServices(Service.S3, Service.SQS); localstack.start(); try { assertTrue("Multiple ports are exposed", localstack.getExposedPorts().size() > 1); assertNotEquals( "Endpoint overrides are different", - localstack.getEndpointOverride(LocalStackContainer.Service.S3).toString(), - localstack.getEndpointOverride(LocalStackContainer.Service.SQS).toString() + localstack.getEndpointOverride(Service.S3).toString(), + localstack.getEndpointOverride(Service.SQS).toString() ); assertNotEquals( "Endpoint configuration have different endpoints", - localstack.getEndpointConfiguration(LocalStackContainer.Service.S3).getServiceEndpoint(), - localstack.getEndpointConfiguration(LocalStackContainer.Service.SQS).getServiceEndpoint() + new AwsClientBuilder.EndpointConfiguration( + localstack.getEndpointOverride(Service.S3).toString(), + localstack.getRegion() + ) + .getServiceEndpoint(), + new AwsClientBuilder.EndpointConfiguration( + localstack.getEndpointOverride(Service.SQS).toString(), + localstack.getRegion() + ) + .getServiceEndpoint() ); } finally { localstack.stop(); diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java index 3b85c69ca34..950f29866ba 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java @@ -1,5 +1,7 @@ package org.testcontainers.containers.localstack; +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; import com.amazonaws.client.builder.AwsClientBuilder; import com.amazonaws.services.kms.AWSKMS; import com.amazonaws.services.kms.AWSKMSClientBuilder; @@ -73,11 +75,22 @@ public static class WithoutNetwork { @Test public void s3TestOverBridgeNetwork() throws IOException { + // with_aws_sdk_v1 { AmazonS3 s3 = AmazonS3ClientBuilder .standard() - .withEndpointConfiguration(localstack.getEndpointConfiguration(Service.S3)) - .withCredentials(localstack.getDefaultCredentialsProvider()) + .withEndpointConfiguration( + new AwsClientBuilder.EndpointConfiguration( + localstack.getEndpointOverride(Service.S3).toString(), + localstack.getRegion() + ) + ) + .withCredentials( + new AWSStaticCredentialsProvider( + new BasicAWSCredentials(localstack.getAccessKey(), localstack.getSecretKey()) + ) + ) .build(); + // } final String bucketName = "foo"; s3.createBucket(bucketName); @@ -103,6 +116,7 @@ public void s3TestOverBridgeNetwork() throws IOException { @Test public void s3TestUsingAwsSdkV2() { + // with_aws_sdk_v2 { S3Client s3 = S3Client .builder() .endpointOverride(localstack.getEndpointOverride(Service.S3)) @@ -113,6 +127,7 @@ public void s3TestUsingAwsSdkV2() { ) .region(Region.of(localstack.getRegion())) .build(); + // } final String bucketName = "foov2"; s3.createBucket(b -> b.bucket(bucketName)); @@ -126,8 +141,17 @@ public void s3TestUsingAwsSdkV2() { public void sqsTestOverBridgeNetwork() { AmazonSQS sqs = AmazonSQSClientBuilder .standard() - .withEndpointConfiguration(localstack.getEndpointConfiguration(Service.SQS)) - .withCredentials(localstack.getDefaultCredentialsProvider()) + .withEndpointConfiguration( + new AwsClientBuilder.EndpointConfiguration( + localstack.getEndpointOverride(Service.SQS).toString(), + localstack.getRegion() + ) + ) + .withCredentials( + new AWSStaticCredentialsProvider( + new BasicAWSCredentials(localstack.getAccessKey(), localstack.getSecretKey()) + ) + ) .build(); CreateQueueResult queueResult = sqs.createQueue("baz"); @@ -157,8 +181,17 @@ public void sqsTestOverBridgeNetwork() { public void cloudWatchLogsTestOverBridgeNetwork() { AWSLogs logs = AWSLogsClientBuilder .standard() - .withEndpointConfiguration(localstack.getEndpointConfiguration(Service.CLOUDWATCHLOGS)) - .withCredentials(localstack.getDefaultCredentialsProvider()) + .withEndpointConfiguration( + new AwsClientBuilder.EndpointConfiguration( + localstack.getEndpointOverride(Service.CLOUDWATCHLOGS).toString(), + localstack.getRegion() + ) + ) + .withCredentials( + new AWSStaticCredentialsProvider( + new BasicAWSCredentials(localstack.getAccessKey(), localstack.getSecretKey()) + ) + ) .build(); logs.createLogGroup(new CreateLogGroupRequest("foo")); @@ -172,8 +205,17 @@ public void cloudWatchLogsTestOverBridgeNetwork() { public void kmsKeyCreationTest() { AWSKMS awskms = AWSKMSClientBuilder .standard() - .withEndpointConfiguration(localstack.getEndpointConfiguration(Service.KMS)) - .withCredentials(localstack.getDefaultCredentialsProvider()) + .withEndpointConfiguration( + new AwsClientBuilder.EndpointConfiguration( + localstack.getEndpointOverride(Service.KMS).toString(), + localstack.getRegion() + ) + ) + .withCredentials( + new AWSStaticCredentialsProvider( + new BasicAWSCredentials(localstack.getAccessKey(), localstack.getSecretKey()) + ) + ) .build(); String desc = String.format("AWS CMK Description"); @@ -198,8 +240,16 @@ public void samePortIsExposedForAllServices() { ); assertEquals( "Endpoint configuration have different endpoints", - localstack.getEndpointConfiguration(Service.S3).getServiceEndpoint(), - localstack.getEndpointConfiguration(Service.SQS).getServiceEndpoint() + new AwsClientBuilder.EndpointConfiguration( + localstack.getEndpointOverride(Service.S3).toString(), + localstack.getRegion() + ) + .getServiceEndpoint(), + new AwsClientBuilder.EndpointConfiguration( + localstack.getEndpointOverride(Service.SQS).toString(), + localstack.getRegion() + ) + .getServiceEndpoint() ); } } @@ -302,8 +352,9 @@ public static class WithRegion { @Test public void s3EndpointHasProperRegion() { - final AwsClientBuilder.EndpointConfiguration endpointConfiguration = localstack.getEndpointConfiguration( - Service.S3 + final AwsClientBuilder.EndpointConfiguration endpointConfiguration = new AwsClientBuilder.EndpointConfiguration( + localstack.getEndpointOverride(Service.S3).toString(), + localstack.getRegion() ); assertEquals( "The endpoint configuration has right region", From afde5d3cde1657625e890740c9f137344b360cee Mon Sep 17 00:00:00 2001 From: Andrei Nevedomskii Date: Mon, 27 Jun 2022 20:34:49 +0200 Subject: [PATCH 2626/5615] JdbcDatabaseContainer: fix wrong instance call (#5465) createConnection() method calls a wrong driver instance when logging --- .../org/testcontainers/containers/JdbcDatabaseContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index 0b80eaac406..aa397929497 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -232,7 +232,7 @@ public Connection createConnection(String queryString) throws SQLException, NoDr logger() .debug( "Trying to create JDBC connection using {} to {} with properties: {}", - driver.getClass().getName(), + jdbcDriverInstance.getClass().getName(), url, info ); From c0eb042dabe7b048130f33459d4a542075fb1355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Mon, 27 Jun 2022 19:52:38 -0500 Subject: [PATCH 2627/5615] Polish examples (#5520) --- .../examples/CucumberTest.java | 2 +- .../{testcontainsers => testcontainers}/examples/Stepdefs.java | 2 +- .../examples/is_search_possible.feature | 0 .../java/org/testcontainers/containers/Neo4jExampleTest.java | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename examples/cucumber/src/test/java/org/{testcontainsers => testcontainers}/examples/CucumberTest.java (84%) rename examples/cucumber/src/test/java/org/{testcontainsers => testcontainers}/examples/Stepdefs.java (98%) rename examples/cucumber/src/test/resources/org/{testcontainsers => testcontainers}/examples/is_search_possible.feature (100%) diff --git a/examples/cucumber/src/test/java/org/testcontainsers/examples/CucumberTest.java b/examples/cucumber/src/test/java/org/testcontainers/examples/CucumberTest.java similarity index 84% rename from examples/cucumber/src/test/java/org/testcontainsers/examples/CucumberTest.java rename to examples/cucumber/src/test/java/org/testcontainers/examples/CucumberTest.java index 0551b4dbf2e..f55919c0fe6 100644 --- a/examples/cucumber/src/test/java/org/testcontainsers/examples/CucumberTest.java +++ b/examples/cucumber/src/test/java/org/testcontainers/examples/CucumberTest.java @@ -1,4 +1,4 @@ -package org.testcontainsers.examples; +package org.testcontainers.examples; import io.cucumber.junit.Cucumber; import io.cucumber.junit.CucumberOptions; diff --git a/examples/cucumber/src/test/java/org/testcontainsers/examples/Stepdefs.java b/examples/cucumber/src/test/java/org/testcontainers/examples/Stepdefs.java similarity index 98% rename from examples/cucumber/src/test/java/org/testcontainsers/examples/Stepdefs.java rename to examples/cucumber/src/test/java/org/testcontainers/examples/Stepdefs.java index ca70173ae92..e1b9c7e81ef 100644 --- a/examples/cucumber/src/test/java/org/testcontainsers/examples/Stepdefs.java +++ b/examples/cucumber/src/test/java/org/testcontainers/examples/Stepdefs.java @@ -1,4 +1,4 @@ -package org.testcontainsers.examples; +package org.testcontainers.examples; import io.cucumber.java.After; import io.cucumber.java.Before; diff --git a/examples/cucumber/src/test/resources/org/testcontainsers/examples/is_search_possible.feature b/examples/cucumber/src/test/resources/org/testcontainers/examples/is_search_possible.feature similarity index 100% rename from examples/cucumber/src/test/resources/org/testcontainsers/examples/is_search_possible.feature rename to examples/cucumber/src/test/resources/org/testcontainers/examples/is_search_possible.feature diff --git a/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java b/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java index 7730d4fa8d0..d151e1a3f24 100644 --- a/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java +++ b/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java @@ -24,7 +24,7 @@ // junitExample { @Testcontainers -public class Neo4jExampleTest { +class Neo4jExampleTest { @Container private static Neo4jContainer neo4jContainer = From 56d0cf1941ad5faf77d1223766a4a0c495b29f8c Mon Sep 17 00:00:00 2001 From: Dave Maughan Date: Tue, 28 Jun 2022 02:16:44 +0100 Subject: [PATCH 2628/5615] Check admin endpoint instead of metrics for Pulsar WaitStrategy. (#5514) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #5513 Co-authored-by: Eddú Meléndez Gonzales Co-authored-by: Kevin Wittek --- .../testcontainers/containers/PulsarContainer.java | 13 ++++++++++++- .../containers/PulsarContainerTest.java | 11 +++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java index 069fccaae65..30c36db01d8 100644 --- a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java +++ b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java @@ -17,8 +17,14 @@ public class PulsarContainer extends GenericContainer { public static final int BROKER_HTTP_PORT = 8080; + /** + * @deprecated The metrics endpoint is no longer being used for the WaitStrategy. + */ + @Deprecated public static final String METRICS_ENDPOINT = "/metrics"; + private static final String ADMIN_CLUSTERS_ENDPOINT = "/admin/v2/clusters"; + /** * See SystemTopicNames. */ @@ -92,7 +98,12 @@ protected void setupCommandAndEnv() { List waitStrategies = new ArrayList<>(); waitStrategies.add(Wait.defaultWaitStrategy()); - waitStrategies.add(Wait.forHttp(METRICS_ENDPOINT).forStatusCode(200).forPort(BROKER_HTTP_PORT)); + waitStrategies.add( + Wait + .forHttp(ADMIN_CLUSTERS_ENDPOINT) + .forPort(BROKER_HTTP_PORT) + .forResponsePredicate("[\"standalone\"]"::equals) + ); if (transactionsEnabled) { withEnv("PULSAR_PREFIX_transactionCoordinatorEnabled", "true"); waitStrategies.add(Wait.forHttp(TRANSACTION_TOPIC_ENDPOINT).forStatusCode(200).forPort(BROKER_HTTP_PORT)); diff --git a/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java b/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java index 91397d00c40..29de6c4c872 100644 --- a/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java +++ b/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java @@ -122,6 +122,17 @@ public void testTransactionsAndFunctionsWorker() throws Exception { } } + @Test + public void testClusterFullyInitialized() throws Exception { + try (PulsarContainer pulsar = new PulsarContainer(PULSAR_IMAGE)) { + pulsar.start(); + + try (PulsarAdmin pulsarAdmin = PulsarAdmin.builder().serviceHttpUrl(pulsar.getHttpServiceUrl()).build()) { + assertThat(pulsarAdmin.clusters().getClusters()).hasSize(1).contains("standalone"); + } + } + } + protected void testPulsarFunctionality(String pulsarBrokerUrl) throws Exception { try ( PulsarClient client = PulsarClient.builder().serviceUrl(pulsarBrokerUrl).build(); From 5a2d76fa7f26b5ad1b57c98b2f1cac94ace397cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gonzalo=20Mu=C3=B1oz?= Date: Tue, 28 Jun 2022 06:58:54 +0200 Subject: [PATCH 2629/5615] Switch default image to ghcr.io and update default tag to 2.4.0 (#5173) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Eddú Meléndez --- .../org/testcontainers/containers/ToxiproxyContainer.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java b/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java index ed8413a2a31..9850d7a0918 100644 --- a/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java +++ b/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java @@ -25,6 +25,8 @@ public class ToxiproxyContainer extends GenericContainer { private static final String DEFAULT_TAG = "2.1.0"; + private static final DockerImageName GHCR_IMAGE_NAME = DockerImageName.parse("ghcr.io/shopify/toxiproxy"); + private static final int TOXIPROXY_CONTROL_PORT = 8474; private static final int FIRST_PROXIED_PORT = 8666; @@ -51,7 +53,7 @@ public ToxiproxyContainer(String dockerImageName) { public ToxiproxyContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME, GHCR_IMAGE_NAME); addExposedPorts(TOXIPROXY_CONTROL_PORT); setWaitStrategy(new HttpWaitStrategy().forPath("/version").forPort(TOXIPROXY_CONTROL_PORT)); From e23f378e68938f2a1f459f82d80c9485c7fcf006 Mon Sep 17 00:00:00 2001 From: Andrei Navrotski <6005070+anavrotski@users.noreply.github.com> Date: Tue, 28 Jun 2022 08:32:35 +0300 Subject: [PATCH 2630/5615] [clickhouse] Add new JDBC driver name (#5474) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also, fix the liveness port. Co-authored-by: Sergei Egorov Co-authored-by: Eddú Meléndez --- .../containers/ClickHouseContainer.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java index 16bbeef6c81..4e87c77ee5b 100644 --- a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java +++ b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java @@ -1,13 +1,13 @@ package org.testcontainers.containers; -import com.google.common.collect.Sets; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.utility.DockerImageName; import java.time.Duration; +import java.util.HashSet; import java.util.Set; -public class ClickHouseContainer extends JdbcDatabaseContainer { +public class ClickHouseContainer extends JdbcDatabaseContainer { public static final String NAME = "clickhouse"; @@ -53,23 +53,27 @@ public ClickHouseContainer(final DockerImageName dockerImageName) { super(dockerImageName); dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME, CLICKHOUSE_IMAGE_NAME); - withExposedPorts(HTTP_PORT, NATIVE_PORT); - waitingFor( + addExposedPorts(HTTP_PORT, NATIVE_PORT); + this.waitStrategy = new HttpWaitStrategy() .forStatusCode(200) - .forResponsePredicate(responseBody -> "Ok.".equals(responseBody)) - .withStartupTimeout(Duration.ofMinutes(1)) - ); + .forResponsePredicate("Ok."::equals) + .withStartupTimeout(Duration.ofMinutes(1)); } @Override public Set getLivenessCheckPortNumbers() { - return Sets.newHashSet(HTTP_PORT); + return new HashSet<>(getMappedPort(HTTP_PORT)); } @Override public String getDriverClassName() { - return DRIVER_CLASS_NAME; + try { + Class.forName(DRIVER_CLASS_NAME); + return DRIVER_CLASS_NAME; + } catch (ClassNotFoundException e) { + return "com.clickhouse.jdbc.ClickHouseDriver"; + } } @Override From 4a14eccad30b0090ea99db7bd1ffa49a94ba37a1 Mon Sep 17 00:00:00 2001 From: Alexander Reelsen Date: Wed, 29 Jun 2022 00:27:00 +0200 Subject: [PATCH 2631/5615] Elasticsearch: Fix startup check for 8.3 and above (#5521) --- .../elasticsearch/ElasticsearchContainer.java | 3 ++- .../ElasticsearchContainerTest.java | 21 ++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java index 5a64a5e67a2..f8b9f779519 100644 --- a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java +++ b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java @@ -102,10 +102,11 @@ public ElasticsearchContainer(final DockerImageName dockerImageName) { this.isAtLeastMajorVersion8 = new ComparableVersion(dockerImageName.getVersionPart()).isGreaterThanOrEqualTo("8.0.0"); // regex that + // matches 8.3 JSON logging with started message and some follow up content within the message field // matches 8.0 JSON logging with no whitespace between message field and content // matches 7.x JSON logging with whitespace between message field and content // matches 6.x text logging with node name in brackets and just a 'started' message till the end of the line - String regex = ".*(\"message\":\\s?\"started\".*|] started\n$)"; + String regex = ".*(\"message\":\\s?\"started[\\s?|\"].*|] started\n$)"; setWaitStrategy(new LogMessageWaitStrategy().withRegEx(regex)); if (isAtLeastMajorVersion8) { withPassword(ELASTICSEARCH_DEFAULT_PASSWORD); diff --git a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java index 4427163ca56..9fcfc45af76 100644 --- a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java +++ b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java @@ -150,6 +150,20 @@ public void elasticsearchVersion() throws IOException { } } + @Test + public void elasticsearchVersion83() throws IOException { + try ( + ElasticsearchContainer container = new ElasticsearchContainer( + "docker.elastic.co/elasticsearch/elasticsearch:8.3.0" + ) + ) { + container.start(); + Response response = getClient(container).performRequest(new Request("GET", "/")); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + assertThat(EntityUtils.toString(response.getEntity()), containsString("8.3.0")); + } + } + @Test public void elasticsearchOssImage() throws IOException { try ( @@ -406,10 +420,15 @@ private RestClient getClient(ElasticsearchContainer container) { new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ELASTICSEARCH_PASSWORD) ); + String protocol = container.caCertAsBytes().isPresent() ? "https://" : "http://"; + client = RestClient - .builder(HttpHost.create(container.getHttpHostAddress())) + .builder(HttpHost.create(protocol + container.getHttpHostAddress())) .setHttpClientConfigCallback(httpClientBuilder -> { + if (container.caCertAsBytes().isPresent()) { + httpClientBuilder.setSSLContext(container.createSslContextFromCa()); + } return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); }) .build(); From 3395ceba894b2f58e37524e1df4786f662c4aab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Wed, 29 Jun 2022 03:46:20 -0500 Subject: [PATCH 2632/5615] Add labeler action (#5516) Triage PRs. --- .github/labeler.yml | 79 +++++++++++++++++++++++++++++++++++ .github/workflows/labeler.yml | 14 +++++++ 2 files changed, 93 insertions(+) create mode 100644 .github/labeler.yml create mode 100644 .github/workflows/labeler.yml diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 00000000000..9293237182e --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,79 @@ +"github_actions": + - .github/workflows/* +"gradle-wrapper": + - gradle/wrapper/* + - gradlew + - gradlew.bat +"modules/azure": + - modules/azure/* +"modules/cassandra": + - modules/cassandra/* +"modules/clickhouse": + - modules/clickhouse/* +"modules/cockroachdb": + - modules/cockroachdb/* +"modules/couchbase": + - modules/couchbase/* +"modules/db2": + - modules/db2/* +"modules/dynalite": + - modules/dynalite/* +"modules/elasticsearch": + - modules/elasticsearch/* +"modules/gcloud": + - modules/gcloud/* +"modules/hivemq": + - modules/hivemq/* +"modules/influx": + - modules/influxdb/* +"modules/jdbc": + - modules/jdbc/* +"modules/jupiter": + - modules/junit-jupiter/* +"modules/k3s": + - modules/k3s/* +"modules/kafka": + - modules/kafka/* +"modules/localstack": + - modules/localstack/* +"modules/mariadb": + - modules/mariadb/* +"modules/mockserver": + - modules/mockserver/* +"modules/mongodb": + - modules/mongodb/* +"modules/sql-server": + - modules/mssqlserver/* +"modules/mysql": + - modules/mysql/* +"modules/neo4j": + - modules/neo4j/* +"modules/nginx": + - modules/nginx/* +"modules/oracle": + - modules/oracle-xe/* +"modules/orientdb": + - modules/orientdb/* +"modules/postgres": + - modules/postgresql/* +"modules/presto": + - modules/presto/* +"modules/pulsar": + - modules/pulsar/* +"modules/r2dbc": + - modules/r2dbc/* +"modules/rabbitmq": + - modules/rabbitmq/* +"modules/selenium": + - modules/selenium/* +"modules/solr": + - modules/solr/* +"modules/spock": + - modules/spock/* +"modules/toxiproxy": + - modules/toxiproxy/* +"modules/trino": + - modules/trino/* +"modules/vault": + - modules/vault/* +"type/docs": docs/**/* diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 00000000000..057208eda32 --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,14 @@ +name: "Pull Request Labeler" +on: +- pull_request_target + +jobs: + triage: + permissions: + contents: read + pull-requests: write + runs-on: ubuntu-latest + steps: + - uses: actions/labeler@v4 + with: + repo-token: "${{ secrets.GITHUB_TOKEN }}" From c562080f1ce487dfedf19e76f7d890e9871ddd03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Wed, 29 Jun 2022 03:51:47 -0500 Subject: [PATCH 2633/5615] Update latest version in mkdocs.yml --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index f6daad68155..21e157969e5 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -102,4 +102,4 @@ nav: - contributing_docs.md - bounty.md extra: - latest_version: 1.17.2 + latest_version: 1.17.3 From fbc5fe4935fd97747a1f7e18dade3132d474045f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Wed, 29 Jun 2022 03:52:58 -0500 Subject: [PATCH 2634/5615] Upgrade testcontainers version to 1.17.3 in japicmp --- gradle/japicmp.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/japicmp.gradle b/gradle/japicmp.gradle index fe3f8881b1c..3c438d340f0 100644 --- a/gradle/japicmp.gradle +++ b/gradle/japicmp.gradle @@ -3,7 +3,7 @@ configurations { } dependencies { - baseline "org.testcontainers:${project.name}:1.17.2", { + baseline "org.testcontainers:${project.name}:1.17.3", { exclude group: "*", module: "*" } } From 1fe0ef23f4851506b56b355a134fb60203554631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Wed, 29 Jun 2022 11:40:49 -0500 Subject: [PATCH 2635/5615] Improve docs (#5522) Add additional steps for linux and colima --- docs/supported_docker_environment/index.md | 27 ++++++++++++++-------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/docs/supported_docker_environment/index.md b/docs/supported_docker_environment/index.md index 3b82cea2165..aa6c7b1b867 100644 --- a/docs/supported_docker_environment/index.md +++ b/docs/supported_docker_environment/index.md @@ -2,16 +2,25 @@ ## Overview -| Host Operating System / Environment | Minimum recommended docker versions | Known issues / tips | -|-------------------------------------|-----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Linux - general | Docker v17.09 | | -| Linux - CircleCI (LXC driver) | Docker v17.09 | The `exec` feature is not compatible with CircleCI. See CircleCI configuration [example](./continuous_integration/circle_ci.md) | -| Linux - within a Docker container | Docker v17.09 | See [Running inside Docker](continuous_integration/dind_patterns.md) for Docker-in-Docker and Docker wormhole patterns | -| Mac OS X - Docker Toolbox | Docker Machine v0.8.0 | | +| Host Operating System / Environment | Minimum recommended docker versions | Known issues / tips | +|-------------------------------------|-----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Linux - general | Docker v17.09 | After docker installation, follow [post-installation steps](https://docs.docker.com/engine/install/linux-postinstall/). | +| Linux - CircleCI (LXC driver) | Docker v17.09 | The `exec` feature is not compatible with CircleCI. See CircleCI configuration [example](./continuous_integration/circle_ci.md) | +| Linux - within a Docker container | Docker v17.09 | See [Running inside Docker](continuous_integration/dind_patterns.md) for Docker-in-Docker and Docker wormhole patterns | +| Mac OS X - Docker Toolbox | Docker Machine v0.8.0 | | | Mac OS X - Docker for Mac | v17.09 | *Support is best-efforts at present*. `getTestHostIpAddress()` is [not currently supported](https://github.com/testcontainers/testcontainers-java/issues/166) due to limitations in Docker for Mac. | -| Windows - Docker Toolbox | | *Support is limited at present and this is not currently tested on a regular basis*. | -| Windows - Docker for Windows | | *Support is best-efforts at present.* Only Linux Containers (LCOW) are supported at the moment. See [Windows Support](windows.md) | -| Windows - Windows Subsystem for Linux (WSL) | Docker v17.09 | *Support is best-efforts at present.* Only Linux Containers (LCOW) are supported at the moment. See [Windows Support](windows.md). | +| Windows - Docker Toolbox | | *Support is limited at present and this is not currently tested on a regular basis*. | +| Windows - Docker for Windows | | *Support is best-efforts at present.* Only Linux Containers (LCOW) are supported at the moment. See [Windows Support](windows.md) | +| Windows - Windows Subsystem for Linux (WSL) | Docker v17.09 | *Support is best-efforts at present.* Only Linux Containers (LCOW) are supported at the moment. See [Windows Support](windows.md). | + +## Using Colima? + +In order to run testcontainers against [colima](https://github.com/abiosoft/colima) the env vars bellow should be set + +``` +export TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=/var/run/docker.sock +export DOCKER_HOST="unix://${HOME}/.colima/docker.sock" +``` ## Docker environment discovery From a2bf452bb5851a9278315b26741984038c66cd1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Fri, 1 Jul 2022 14:48:52 -0500 Subject: [PATCH 2636/5615] Add host os and arch in bug report template (#5525) Co-authored-by: Kevin Wittek --- .github/ISSUE_TEMPLATE/bug_report.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 80e0b47741d..dc86b416e46 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -65,6 +65,22 @@ body: - 'No' validations: required: true + - type: input + id: host-os + attributes: + label: Host OS + description: Which Operating System are you using? + placeholder: e.g. Linux, Windows + validations: + required: true + - type: input + id: host-arch + attributes: + label: Host Arch + description: Which architecture are you using? + placeholder: e.g. x86, ARM + validations: + required: true - type: textarea id: docker-version attributes: From 0fc238075546f56e22b0fd44ba985c2fcc2f8424 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:03:51 +0000 Subject: [PATCH 2637/5615] Bump neo4j-java-driver from 4.4.5 to 4.4.6 in /modules/neo4j Bumps [neo4j-java-driver](https://github.com/neo4j/neo4j-java-driver) from 4.4.5 to 4.4.6. - [Release notes](https://github.com/neo4j/neo4j-java-driver/releases) - [Commits](https://github.com/neo4j/neo4j-java-driver/compare/4.4.5...4.4.6) --- updated-dependencies: - dependency-name: org.neo4j.driver:neo4j-java-driver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index e0a51ea783e..feff7329185 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -33,6 +33,6 @@ dependencies { api project(":testcontainers") - testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.5' + testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.6' testImplementation 'org.assertj:assertj-core:3.23.1' } From 9d63e7fa477e050d2fe1998b6f8824768b667397 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:03:53 +0000 Subject: [PATCH 2638/5615] Bump neo4j from 3.5.33 to 3.5.34 in /modules/neo4j Bumps [neo4j](https://github.com/neo4j/neo4j) from 3.5.33 to 3.5.34. - [Release notes](https://github.com/neo4j/neo4j/releases) - [Commits](https://github.com/neo4j/neo4j/compare/3.5.33...3.5.34) --- updated-dependencies: - dependency-name: org.neo4j:neo4j dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index e0a51ea783e..779aab2bde8 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -29,7 +29,7 @@ task customNeo4jPluginJar(type: Jar) { processTestResources.dependsOn customNeo4jPluginJar dependencies { - customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.33" + customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.34" api project(":testcontainers") From 1748c8ed29ccaf6964614bb97ff0d326675bab31 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:04:32 +0000 Subject: [PATCH 2639/5615] Bump aws-java-sdk-dynamodb in /modules/dynalite Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.12.232 to 1.12.253. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.232...1.12.253) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-dynamodb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/dynalite/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 09cb51e10a4..acf379cda79 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Dynalite" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.232' - testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.210' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.253' + testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.253' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 80695194043f8824a28c0aae44659b2f4f5051fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:04:34 +0000 Subject: [PATCH 2640/5615] Bump mariadb-java-client from 3.0.5 to 3.0.6 in /modules/mariadb Bumps [mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 3.0.5 to 3.0.6. - [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases) - [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md) - [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/3.0.5...3.0.6) --- updated-dependencies: - dependency-name: org.mariadb.jdbc:mariadb-java-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index fd534915ba1..ed7f428ba3e 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'org.mariadb:r2dbc-mariadb:1.0.3' testImplementation project(':jdbc-test') - testImplementation 'org.mariadb.jdbc:mariadb-java-client:3.0.5' + testImplementation 'org.mariadb.jdbc:mariadb-java-client:3.0.6' testImplementation testFixtures(project(':r2dbc')) testImplementation 'org.mariadb:r2dbc-mariadb:1.0.2' From 37afe8e054c764ce2898ce3e7525b56c44fee4a9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:04:43 +0000 Subject: [PATCH 2641/5615] Bump transport from 7.17.4 to 7.17.5 in /modules/elasticsearch Bumps [transport](https://github.com/elastic/elasticsearch) from 7.17.4 to 7.17.5. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Changelog](https://github.com/elastic/elasticsearch/blob/master/CHANGELOG.md) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.17.4...v7.17.5) --- updated-dependencies: - dependency-name: org.elasticsearch.client:transport dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 6d2b5b8a667..29e6591034d 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,6 +3,6 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') testImplementation "org.elasticsearch.client:elasticsearch-rest-client:8.2.2" - testImplementation "org.elasticsearch.client:transport:7.17.4" + testImplementation "org.elasticsearch.client:transport:7.17.5" testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 72846fdf802e5d4a6c6bf11bd8a741049e480253 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:05:12 +0000 Subject: [PATCH 2642/5615] Bump java-client from 3.3.0 to 3.3.1 in /modules/couchbase Bumps [java-client](https://github.com/couchbase/couchbase-jvm-clients) from 3.3.0 to 3.3.1. - [Release notes](https://github.com/couchbase/couchbase-jvm-clients/releases) - [Commits](https://github.com/couchbase/couchbase-jvm-clients/compare/java-client-3.3.0...java-client-3.3.1) --- updated-dependencies: - dependency-name: com.couchbase.client:java-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 71844a30d20..df7f4cbc221 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -5,6 +5,6 @@ dependencies { // TODO use JDK's HTTP client and/or Apache HttpClient5 shaded 'com.squareup.okhttp3:okhttp:3.14.9' - testImplementation 'com.couchbase.client:java-client:3.3.0' + testImplementation 'com.couchbase.client:java-client:3.3.1' testImplementation 'org.awaitility:awaitility:4.2.0' } From 602d11d68f4b8e225e6d5635b85a640c3fcde92a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:05:32 +0000 Subject: [PATCH 2643/5615] Bump rest-assured from 5.1.0 to 5.1.1 in /modules/vault Bumps [rest-assured](https://github.com/rest-assured/rest-assured) from 5.1.0 to 5.1.1. - [Release notes](https://github.com/rest-assured/rest-assured/releases) - [Changelog](https://github.com/rest-assured/rest-assured/blob/master/changelog.txt) - [Commits](https://github.com/rest-assured/rest-assured/compare/rest-assured-5.1.0...rest-assured-5.1.1) --- updated-dependencies: - dependency-name: io.rest-assured:rest-assured dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index 1ffc366987c..499286ae72a 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':testcontainers') testImplementation 'com.bettercloud:vault-java-driver:5.1.0' - testImplementation 'io.rest-assured:rest-assured:5.1.0' + testImplementation 'io.rest-assured:rest-assured:5.1.1' testImplementation 'org.assertj:assertj-core:3.23.1' } From 49f680ead43656a758d03441254bc42448c89fa8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:05:36 +0000 Subject: [PATCH 2644/5615] Bump tomcat-jdbc from 10.0.21 to 10.0.22 in /modules/jdbc Bumps tomcat-jdbc from 10.0.21 to 10.0.22. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 043c2774d6c..012e9f410ec 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -7,7 +7,7 @@ dependencies { compileOnly 'org.jetbrains:annotations:23.0.0' testImplementation 'commons-dbutils:commons-dbutils:1.7' testImplementation 'org.vibur:vibur-dbcp:25.0' - testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.21' + testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.22' testImplementation 'com.zaxxer:HikariCP-java6:2.3.13' testImplementation 'com.googlecode.junit-toolbox:junit-toolbox:2.4' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From 81e19bf43be0927fb1ac598ad09a645eef59f415 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:05:38 +0000 Subject: [PATCH 2645/5615] Bump pulsar-client from 2.10.0 to 2.10.1 in /modules/pulsar Bumps [pulsar-client](https://github.com/apache/pulsar) from 2.10.0 to 2.10.1. - [Release notes](https://github.com/apache/pulsar/releases) - [Commits](https://github.com/apache/pulsar/compare/v2.10.0...v2.10.1) --- updated-dependencies: - dependency-name: org.apache.pulsar:pulsar-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index a1b24efba37..31524211f63 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Pulsar" dependencies { api project(':testcontainers') - testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.10.0' + testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.10.1' testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.23.1' testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.10.0' } From c4aa3db14fdbc189de6b368069f9871ab456891f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:05:39 +0000 Subject: [PATCH 2646/5615] Bump mssql-jdbc in /modules/mssqlserver Bumps [mssql-jdbc](https://github.com/Microsoft/mssql-jdbc) from 11.1.1.jre8-preview to 11.1.2.jre8-preview. - [Release notes](https://github.com/Microsoft/mssql-jdbc/releases) - [Changelog](https://github.com/microsoft/mssql-jdbc/blob/main/CHANGELOG.md) - [Commits](https://github.com/Microsoft/mssql-jdbc/commits) --- updated-dependencies: - dependency-name: com.microsoft.sqlserver:mssql-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mssqlserver/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index e7a654e12f1..30b9af2bd77 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'io.r2dbc:r2dbc-mssql:0.9.0.RELEASE' testImplementation project(':jdbc-test') - testImplementation 'com.microsoft.sqlserver:mssql-jdbc:11.1.1.jre8-preview' + testImplementation 'com.microsoft.sqlserver:mssql-jdbc:11.1.2.jre8-preview' testImplementation project(':r2dbc') testImplementation 'io.r2dbc:r2dbc-mssql:0.8.7.RELEASE' From b1c4fb664fa8d8b42c0e44b3a30555c3138994ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:05:43 +0000 Subject: [PATCH 2647/5615] Bump pulsar-client-admin from 2.10.0 to 2.10.1 in /modules/pulsar Bumps [pulsar-client-admin](https://github.com/apache/pulsar) from 2.10.0 to 2.10.1. - [Release notes](https://github.com/apache/pulsar/releases) - [Commits](https://github.com/apache/pulsar/compare/v2.10.0...v2.10.1) --- updated-dependencies: - dependency-name: org.apache.pulsar:pulsar-client-admin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index a1b24efba37..e49b46a85ed 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -5,5 +5,5 @@ dependencies { testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.10.0' testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.23.1' - testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.10.0' + testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.10.1' } From d20ddfbecc1d82ce3bda4f441d18d82fc7070290 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:05:45 +0000 Subject: [PATCH 2648/5615] Bump amqp-client from 5.14.2 to 5.15.0 in /core Bumps [amqp-client](https://github.com/rabbitmq/rabbitmq-java-client) from 5.14.2 to 5.15.0. - [Release notes](https://github.com/rabbitmq/rabbitmq-java-client/releases) - [Commits](https://github.com/rabbitmq/rabbitmq-java-client/compare/v5.14.2...v5.15.0) --- updated-dependencies: - dependency-name: com.rabbitmq:amqp-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index ebfd4f36bcb..b4c6d7ad4a2 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -88,7 +88,7 @@ dependencies { testImplementation 'org.apache.httpcomponents:httpclient:4.5.9' testImplementation 'redis.clients:jedis:4.2.3' - testImplementation 'com.rabbitmq:amqp-client:5.14.2' + testImplementation 'com.rabbitmq:amqp-client:5.15.0' testImplementation 'org.mongodb:mongo-java-driver:3.12.11' testImplementation ('org.mockito:mockito-core:4.6.0') { From f3fdbf6a94c5cf7082c2ea452ab93610044e2af6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:05:45 +0000 Subject: [PATCH 2649/5615] Bump aws-java-sdk-sqs from 1.12.233 to 1.12.253 in /modules/localstack Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.12.233 to 1.12.253. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.233...1.12.253) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-sqs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index b6fb4fd3111..e73bf61c19d 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.231' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.231' - testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.233' + testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.253' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.232' testImplementation 'software.amazon.awssdk:s3:2.17.204' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From 17086e24c6fe00f93fbec6f7653df62c78f3cc34 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:05:46 +0000 Subject: [PATCH 2650/5615] Bump s3 from 2.17.204 to 2.17.224 in /modules/localstack Bumps s3 from 2.17.204 to 2.17.224. --- updated-dependencies: - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index b6fb4fd3111..4a191afd82a 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.231' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.233' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.232' - testImplementation 'software.amazon.awssdk:s3:2.17.204' + testImplementation 'software.amazon.awssdk:s3:2.17.224' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 183500e91eb6cbb79e9e7b29bedea3664556b1af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:06:14 +0000 Subject: [PATCH 2651/5615] Bump peter-evans/create-pull-request from 4.0.3 to 4.0.4 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4.0.3 to 4.0.4. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/f094b77505fb89581e68a1163fbd2fffece39da1...923ad837f191474af6b1721408744feb989a4c27) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/update-docs-version.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index b4d4d250346..ed21bca4670 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -17,7 +17,7 @@ jobs: sed -i "s/latest_version: .*/latest_version: ${GITHUB_REF##*/}/g" mkdocs.yml git diff - name: Create Pull Request - uses: peter-evans/create-pull-request@f094b77505fb89581e68a1163fbd2fffece39da1 # v3.10.1 + uses: peter-evans/create-pull-request@923ad837f191474af6b1721408744feb989a4c27 # v3.10.1 with: title: Update docs version to ${GITHUB_REF##*/} body: | From d3ea1fca61c21e46f243b744e06ec78d73d9294f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:06:16 +0000 Subject: [PATCH 2652/5615] Bump gradle-update/update-gradle-wrapper-action from 1.0.17 to 1.0.18 Bumps [gradle-update/update-gradle-wrapper-action](https://github.com/gradle-update/update-gradle-wrapper-action) from 1.0.17 to 1.0.18. - [Release notes](https://github.com/gradle-update/update-gradle-wrapper-action/releases) - [Changelog](https://github.com/gradle-update/update-gradle-wrapper-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/gradle-update/update-gradle-wrapper-action/compare/5b865a8b4d09fdf6afaaa20397ba68455bb48f0d...981c551b17dbcf1940b1b4435afdb79babb7c13a) --- updated-dependencies: - dependency-name: gradle-update/update-gradle-wrapper-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/update-gradle-wrapper.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-gradle-wrapper.yml b/.github/workflows/update-gradle-wrapper.yml index e2013d0f1b7..5855e609fce 100644 --- a/.github/workflows/update-gradle-wrapper.yml +++ b/.github/workflows/update-gradle-wrapper.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v3 - name: Update Gradle Wrapper - uses: gradle-update/update-gradle-wrapper-action@5b865a8b4d09fdf6afaaa20397ba68455bb48f0d # v1.0.13 + uses: gradle-update/update-gradle-wrapper-action@981c551b17dbcf1940b1b4435afdb79babb7c13a # v1.0.13 with: repo-token: ${{ secrets.GITHUB_TOKEN }} labels: dependencies From 82cf76073948a2e278a28fc8fd1d42fb41b49c9b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:06:20 +0000 Subject: [PATCH 2653/5615] Bump actions/cache from 3.0.3 to 3.0.4 Bumps [actions/cache](https://github.com/actions/cache) from 3.0.3 to 3.0.4. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3.0.3...v3.0.4) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index 9a7b314c67f..b1466ba8621 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -22,7 +22,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.3 + uses: actions/cache@v3.0.4 continue-on-error: true with: path: ~/.gradle/caches @@ -50,7 +50,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.3 + uses: actions/cache@v3.0.4 continue-on-error: true with: path: ~/.gradle/caches diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 56bb4e0799d..70ee552b798 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.3 + uses: actions/cache@v3.0.4 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-home-testmatrix-${{ hashFiles('**/*.gradle') }} @@ -56,7 +56,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.3 + uses: actions/cache@v3.0.4 continue-on-error: true with: path: ~/.gradle/caches From 92c087da00e3c67a861601c7c15d490ac2ed3390 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:07:07 +0000 Subject: [PATCH 2654/5615] Bump cucumber-java from 7.2.3 to 7.4.1 in /examples Bumps [cucumber-java](https://github.com/cucumber/cucumber-jvm) from 7.2.3 to 7.4.1. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.2.3...v7.4.1) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-java dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index f23a2d9b7af..3e03e776ac4 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' - testImplementation 'io.cucumber:cucumber-java:7.2.3' + testImplementation 'io.cucumber:cucumber-java:7.4.1' testImplementation 'io.cucumber:cucumber-junit:7.3.4' testImplementation 'org.testcontainers:selenium' } From 87e734d9c5af84bd34838e8f2f84cccf5210a078 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:07:18 +0000 Subject: [PATCH 2655/5615] Bump assertj-core from 3.22.0 to 3.23.1 in /examples Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/kafka-cluster/build.gradle | 2 +- examples/neo4j-container/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index db89b4335fe..79e91ec5a68 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -11,7 +11,7 @@ dependencies { testAnnotationProcessor "org.projectlombok:lombok:1.18.24" testImplementation 'org.testcontainers:kafka' testImplementation 'org.apache.kafka:kafka-clients:3.1.0' - testImplementation 'org.assertj:assertj-core:3.22.0' + testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'com.google.guava:guava:23.0' testImplementation 'ch.qos.logback:logback-classic:1.2.11' } diff --git a/examples/neo4j-container/build.gradle b/examples/neo4j-container/build.gradle index 745d3206953..e937bd2954b 100644 --- a/examples/neo4j-container/build.gradle +++ b/examples/neo4j-container/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - testImplementation 'org.assertj:assertj-core:3.22.0' + testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.6' testImplementation 'org.testcontainers:neo4j' testImplementation 'org.testcontainers:junit-jupiter' From 76a7c3b24c754519ccd622e42d84118a864482d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:07:25 +0000 Subject: [PATCH 2656/5615] Bump postgresql from 42.3.6 to 42.4.0 in /examples Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.6 to 42.4.0. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.6...REL42.4.0) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index f25127f75eb..4bb216e6fb7 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.36' implementation 'com.squareup.okhttp3:okhttp:4.9.3' implementation 'org.json:json:20220320' - testImplementation 'org.postgresql:postgresql:42.3.6' + testImplementation 'org.postgresql:postgresql:42.4.0' testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.testcontainers:postgresql' } From cae5491705e415e9369eda6ce61a2224aae1631f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:07:27 +0000 Subject: [PATCH 2657/5615] Bump com.gradle.enterprise.gradle.plugin in /examples Bumps com.gradle.enterprise.gradle.plugin from 3.10 to 3.10.2. --- updated-dependencies: - dependency-name: com.gradle.enterprise:com.gradle.enterprise.gradle.plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/settings.gradle b/examples/settings.gradle index b9fb73f86c1..5a4ae8c0d2e 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.10.0" - classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10" + classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10.2" classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7.2" } } From 010ca107405a11d1e9674c635dfc965830fcfba9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:09:12 +0000 Subject: [PATCH 2658/5615] Bump reactor-core from 3.4.18 to 3.4.19 in /modules/r2dbc Bumps [reactor-core](https://github.com/reactor/reactor-core) from 3.4.18 to 3.4.19. - [Release notes](https://github.com/reactor/reactor-core/releases) - [Commits](https://github.com/reactor/reactor-core/compare/v3.4.18...v3.4.19) --- updated-dependencies: - dependency-name: io.projectreactor:reactor-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/r2dbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle index 51199c9a53c..692b5ac9ce5 100644 --- a/modules/r2dbc/build.gradle +++ b/modules/r2dbc/build.gradle @@ -15,6 +15,6 @@ dependencies { testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.12.RELEASE' testImplementation project(':postgresql') - testFixturesImplementation 'io.projectreactor:reactor-core:3.4.18' + testFixturesImplementation 'io.projectreactor:reactor-core:3.4.19' testFixturesImplementation 'org.assertj:assertj-core:3.23.1' } From 7ca244e24d036c64d957905580aee5ae818fff5e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:09:21 +0000 Subject: [PATCH 2659/5615] Bump hivemq-extension-sdk from 4.8.1 to 4.8.2 in /modules/hivemq Bumps [hivemq-extension-sdk](https://github.com/hivemq/hivemq-extension-sdk) from 4.8.1 to 4.8.2. - [Release notes](https://github.com/hivemq/hivemq-extension-sdk/releases) - [Commits](https://github.com/hivemq/hivemq-extension-sdk/compare/4.8.1...4.8.2) --- updated-dependencies: - dependency-name: com.hivemq:hivemq-extension-sdk dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index 7add9af67ea..aa566381d0c 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -13,7 +13,7 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") testImplementation(project(":junit-jupiter")) - testImplementation("com.hivemq:hivemq-extension-sdk:4.8.1") + testImplementation("com.hivemq:hivemq-extension-sdk:4.8.2") testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") testImplementation("org.apache.httpcomponents:httpclient:4.5.13") testImplementation("ch.qos.logback:logback-classic:1.2.11") From c130082dee8f2e3de1b48022ffc6675df5a9435e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:09:26 +0000 Subject: [PATCH 2660/5615] Bump trino-jdbc from 382 to 388 in /modules/trino Bumps trino-jdbc from 382 to 388. --- updated-dependencies: - dependency-name: io.trino:trino-jdbc dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/trino/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/trino/build.gradle b/modules/trino/build.gradle index 8305e8e0629..caf1f322c12 100644 --- a/modules/trino/build.gradle +++ b/modules/trino/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'io.trino:trino-jdbc:382' + testImplementation 'io.trino:trino-jdbc:388' compileOnly 'org.jetbrains:annotations:23.0.0' } From 30508fe29ed6aab8160efa17c5f67f3f8ecb9fe2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:09:36 +0000 Subject: [PATCH 2661/5615] Bump google-cloud-datastore from 2.7.0 to 2.10.0 in /modules/gcloud Bumps [google-cloud-datastore](https://github.com/googleapis/java-datastore) from 2.7.0 to 2.10.0. - [Release notes](https://github.com/googleapis/java-datastore/releases) - [Changelog](https://github.com/googleapis/java-datastore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-datastore/compare/v2.7.0...v2.10.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-datastore dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index cdea13f5fac..ddb634fbd79 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: GCloud" dependencies { api project(':testcontainers') - testImplementation 'com.google.cloud:google-cloud-datastore:2.7.0' + testImplementation 'com.google.cloud:google-cloud-datastore:2.10.0' testImplementation 'com.google.cloud:google-cloud-firestore:3.2.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.119.0' testImplementation 'com.google.cloud:google-cloud-spanner:6.25.5' From c0cc90ffc90d3d324e767d63ad0f1388cc8435ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:09:37 +0000 Subject: [PATCH 2662/5615] Bump client-java from 15.0.1 to 16.0.0 in /modules/k3s Bumps [client-java](https://github.com/kubernetes-client/java) from 15.0.1 to 16.0.0. - [Release notes](https://github.com/kubernetes-client/java/releases) - [Changelog](https://github.com/kubernetes-client/java/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes-client/java/compare/v15.0.1...v16.0.0) --- updated-dependencies: - dependency-name: io.kubernetes:client-java dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/k3s/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/k3s/build.gradle b/modules/k3s/build.gradle index bf51cbfefa9..2223909f051 100644 --- a/modules/k3s/build.gradle +++ b/modules/k3s/build.gradle @@ -9,6 +9,6 @@ dependencies { shaded 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.8.8' testImplementation 'io.fabric8:kubernetes-client:5.12.2' - testImplementation 'io.kubernetes:client-java:15.0.1' + testImplementation 'io.kubernetes:client-java:16.0.0' testImplementation 'org.assertj:assertj-core:3.23.1' } From d6dfc0b544c3cada52c8827d83d4851e782f25b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:09:40 +0000 Subject: [PATCH 2663/5615] Bump google-cloud-bigtable from 2.8.0 to 2.9.0 in /modules/gcloud Bumps [google-cloud-bigtable](https://github.com/googleapis/java-bigtable) from 2.8.0 to 2.9.0. - [Release notes](https://github.com/googleapis/java-bigtable/releases) - [Changelog](https://github.com/googleapis/java-bigtable/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-bigtable/compare/v2.8.0...v2.9.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-bigtable dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index cdea13f5fac..3debc0391cf 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-firestore:3.2.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.119.0' testImplementation 'com.google.cloud:google-cloud-spanner:6.25.5' - testImplementation 'com.google.cloud:google-cloud-bigtable:2.8.0' + testImplementation 'com.google.cloud:google-cloud-bigtable:2.9.0' testImplementation 'org.assertj:assertj-core:3.23.1' } From 183fce7a00e383de48e95ff672d8a0372a6c61d2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:09:45 +0000 Subject: [PATCH 2664/5615] Bump google-cloud-pubsub from 1.119.0 to 1.120.0 in /modules/gcloud Bumps [google-cloud-pubsub](https://github.com/googleapis/java-pubsub) from 1.119.0 to 1.120.0. - [Release notes](https://github.com/googleapis/java-pubsub/releases) - [Changelog](https://github.com/googleapis/java-pubsub/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-pubsub/compare/v1.119.0...v1.120.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-pubsub dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index cdea13f5fac..1369d741f1d 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -5,7 +5,7 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-datastore:2.7.0' testImplementation 'com.google.cloud:google-cloud-firestore:3.2.0' - testImplementation 'com.google.cloud:google-cloud-pubsub:1.119.0' + testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.0' testImplementation 'com.google.cloud:google-cloud-spanner:6.25.5' testImplementation 'com.google.cloud:google-cloud-bigtable:2.8.0' testImplementation 'org.assertj:assertj-core:3.23.1' From 1e7ed80c8be857bb80a1bd3e158221ff97269516 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:10:03 +0000 Subject: [PATCH 2665/5615] Bump amqp-client from 5.14.2 to 5.15.0 in /modules/rabbitmq Bumps [amqp-client](https://github.com/rabbitmq/rabbitmq-java-client) from 5.14.2 to 5.15.0. - [Release notes](https://github.com/rabbitmq/rabbitmq-java-client/releases) - [Commits](https://github.com/rabbitmq/rabbitmq-java-client/compare/v5.14.2...v5.15.0) --- updated-dependencies: - dependency-name: com.rabbitmq:amqp-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/rabbitmq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/rabbitmq/build.gradle b/modules/rabbitmq/build.gradle index 6c2953909bb..9825a27a499 100644 --- a/modules/rabbitmq/build.gradle +++ b/modules/rabbitmq/build.gradle @@ -2,7 +2,7 @@ description = "TestContainers :: RabbitMQ" dependencies { api project(":testcontainers") - testImplementation 'com.rabbitmq:amqp-client:5.14.2' + testImplementation 'com.rabbitmq:amqp-client:5.15.0' testImplementation 'org.assertj:assertj-core:3.23.1' compileOnly 'org.jetbrains:annotations:23.0.0' } From 889b990fb7dc7ae31a370c1bd28d6d7265c3fa6a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:10:09 +0000 Subject: [PATCH 2666/5615] Bump mongodb-driver-sync from 4.6.0 to 4.6.1 in /modules/mongodb Bumps [mongodb-driver-sync](https://github.com/mongodb/mongo-java-driver) from 4.6.0 to 4.6.1. - [Release notes](https://github.com/mongodb/mongo-java-driver/releases) - [Commits](https://github.com/mongodb/mongo-java-driver/compare/r4.6.0...r4.6.1) --- updated-dependencies: - dependency-name: org.mongodb:mongodb-driver-sync dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mongodb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mongodb/build.gradle b/modules/mongodb/build.gradle index d8ef34fbb40..297bcd21f3b 100644 --- a/modules/mongodb/build.gradle +++ b/modules/mongodb/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: MongoDB" dependencies { api project(':testcontainers') - testImplementation("org.mongodb:mongodb-driver-sync:4.6.0") + testImplementation("org.mongodb:mongodb-driver-sync:4.6.1") } From 10e5c4936911629cc805a8b9085e79cfa957cf27 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:10:14 +0000 Subject: [PATCH 2667/5615] Bump azure-cosmos from 4.30.0 to 4.32.1 in /modules/azure Bumps [azure-cosmos](https://github.com/Azure/azure-sdk-for-java) from 4.30.0 to 4.32.1. - [Release notes](https://github.com/Azure/azure-sdk-for-java/releases) - [Commits](https://github.com/Azure/azure-sdk-for-java/compare/azure-cosmos_4.30.0...azure-cosmos_4.32.1) --- updated-dependencies: - dependency-name: com.azure:azure-cosmos dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/azure/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle index 6656445af09..5f8c6837d79 100644 --- a/modules/azure/build.gradle +++ b/modules/azure/build.gradle @@ -6,5 +6,5 @@ dependencies { shaded 'com.squareup.okhttp3:okhttp:3.14.9' testImplementation 'org.assertj:assertj-core:3.23.1' - testImplementation 'com.azure:azure-cosmos:4.30.0' + testImplementation 'com.azure:azure-cosmos:4.32.1' } From 267df8d0bc5a2a8b16513b0b59b3a8ebe6c22150 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:10:32 +0000 Subject: [PATCH 2668/5615] Bump postgresql from 42.3.6 to 42.4.0 in /modules/cockroachdb Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.6 to 42.4.0. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.6...REL42.4.0) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/cockroachdb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cockroachdb/build.gradle b/modules/cockroachdb/build.gradle index 490b2d7fb11..fb9f53189d9 100644 --- a/modules/cockroachdb/build.gradle +++ b/modules/cockroachdb/build.gradle @@ -4,5 +4,5 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'org.postgresql:postgresql:42.3.6' + testImplementation 'org.postgresql:postgresql:42.4.0' } From d5d78d0f471462612ac080284423871d0808ec2a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:11:53 +0000 Subject: [PATCH 2669/5615] Bump com.gradle.enterprise.gradle.plugin from 3.10.1 to 3.10.2 Bumps com.gradle.enterprise.gradle.plugin from 3.10.1 to 3.10.2. --- updated-dependencies: - dependency-name: com.gradle.enterprise:com.gradle.enterprise.gradle.plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index 16b3fcdf98a..73cd6b977fd 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,7 +5,7 @@ buildscript { } } dependencies { - classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10.1" + classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10.2" classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7" } } From c8daeb689cf4b1f500869a8565ebe4cbcf35390a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 15:22:34 +0000 Subject: [PATCH 2670/5615] Bump postgresql from 42.3.6 to 42.4.0 in /modules/junit-jupiter Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.6 to 42.4.0. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.6...REL42.4.0) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 9a74389e8a9..73a8897800c 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2' - testRuntimeOnly 'org.postgresql:postgresql:42.3.6' + testRuntimeOnly 'org.postgresql:postgresql:42.4.0' testRuntimeOnly 'mysql:mysql-connector-java:8.0.29' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2' } From ea4f953dc06ea7e0751f0b2ac23230af3eef41f8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 15:22:34 +0000 Subject: [PATCH 2671/5615] Bump postgresql from 42.3.6 to 42.4.0 in /modules/spock Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.6 to 42.4.0. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.6...REL42.4.0) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index e609733d398..a9e3b7d09b1 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'com.zaxxer:HikariCP:4.0.3' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' - testRuntimeOnly 'org.postgresql:postgresql:42.3.6' + testRuntimeOnly 'org.postgresql:postgresql:42.4.0' testRuntimeOnly 'mysql:mysql-connector-java:8.0.29' testCompileClasspath 'org.jetbrains:annotations:23.0.0' From 7066793701465e01ecf4734a9fccb7b900995228 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 15:22:35 +0000 Subject: [PATCH 2672/5615] Bump postgresql from 42.3.6 to 42.4.0 in /modules/postgresql Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.6 to 42.4.0. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.6...REL42.4.0) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index f083d4ed949..87705f77d5d 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -11,7 +11,7 @@ dependencies { testImplementation project(':jdbc-test') testImplementation project(':test-support') - testImplementation 'org.postgresql:postgresql:42.3.6' + testImplementation 'org.postgresql:postgresql:42.4.0' testImplementation testFixtures(project(':r2dbc')) testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.11.RELEASE' From 1dec0ec57754e4ba2d201cd09f979a3d93b8519c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 15:22:39 +0000 Subject: [PATCH 2673/5615] Bump org.jetbrains.kotlin.jvm from 1.6.21 to 1.7.0 in /examples Bumps [org.jetbrains.kotlin.jvm](https://github.com/JetBrains/kotlin) from 1.6.21 to 1.7.0. - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.6.21...v1.7.0) --- updated-dependencies: - dependency-name: org.jetbrains.kotlin.jvm dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 4f3a50de21a..c0bd965ab79 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,6 +1,6 @@ plugins { id("org.springframework.boot") version "2.6.4" - id("org.jetbrains.kotlin.jvm") version "1.6.21" + id("org.jetbrains.kotlin.jvm") version "1.7.0" id("org.jetbrains.kotlin.plugin.spring") version "1.6.21" } From 770a9d86a721dc8fda92fe883bd4f97d49d20069 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 15:22:39 +0000 Subject: [PATCH 2674/5615] Bump tomcat-jdbc from 10.0.21 to 10.0.22 in /modules/jdbc-test Bumps tomcat-jdbc from 10.0.21 to 10.0.22. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 5674f235c7f..3649396773d 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -14,7 +14,7 @@ dependencies { exclude(group: "net.java.dev.jna", module: "jna") } - api 'org.apache.tomcat:tomcat-jdbc:10.0.21' + api 'org.apache.tomcat:tomcat-jdbc:10.0.22' api 'org.vibur:vibur-dbcp:25.0' api 'mysql:mysql-connector-java:8.0.29' } From 799f1dd49bbe835b65bc1f543df9a5d459fbee3a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 15:22:39 +0000 Subject: [PATCH 2675/5615] Bump elasticsearch-rest-client in /modules/elasticsearch Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 8.2.2 to 8.3.1. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Changelog](https://github.com/elastic/elasticsearch/blob/master/CHANGELOG.md) - [Commits](https://github.com/elastic/elasticsearch/compare/v8.2.2...v8.3.1) --- updated-dependencies: - dependency-name: org.elasticsearch.client:elasticsearch-rest-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 29e6591034d..a4a277596fc 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,7 +2,7 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') - testImplementation "org.elasticsearch.client:elasticsearch-rest-client:8.2.2" + testImplementation "org.elasticsearch.client:elasticsearch-rest-client:8.3.1" testImplementation "org.elasticsearch.client:transport:7.17.5" testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From f14382d24fb349d370a18c185100f27ba603e80d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 15:22:43 +0000 Subject: [PATCH 2676/5615] Bump mockito-core from 4.6.0 to 4.6.1 in /core Bumps [mockito-core](https://github.com/mockito/mockito) from 4.6.0 to 4.6.1. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.6.0...v4.6.1) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index b4c6d7ad4a2..d32e2fc43b2 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -91,7 +91,7 @@ dependencies { testImplementation 'com.rabbitmq:amqp-client:5.15.0' testImplementation 'org.mongodb:mongo-java-driver:3.12.11' - testImplementation ('org.mockito:mockito-core:4.6.0') { + testImplementation ('org.mockito:mockito-core:4.6.1') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From 4ce0ab4a855a81a0267d2a64ec3805633b0643ad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 15:22:46 +0000 Subject: [PATCH 2677/5615] Bump cucumber-junit from 7.3.4 to 7.4.1 in /examples Bumps [cucumber-junit](https://github.com/cucumber/cucumber-jvm) from 7.3.4 to 7.4.1. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.3.4...v7.4.1) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-junit dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 3e03e776ac4..1bf4e1f72cb 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' testImplementation 'io.cucumber:cucumber-java:7.4.1' - testImplementation 'io.cucumber:cucumber-junit:7.3.4' + testImplementation 'io.cucumber:cucumber-junit:7.4.1' testImplementation 'org.testcontainers:selenium' } From cd03ce9275d6f6a1289c91196bf21f23ba81f6c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 15:22:48 +0000 Subject: [PATCH 2678/5615] Bump aws-java-sdk-s3 from 1.12.231 to 1.12.253 in /modules/localstack Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.231 to 1.12.253. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.231...1.12.253) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index d6bd9781720..4ff9066b4c4 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.231' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.253' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.231' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.253' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.232' From 3e9202d8411bf077f617403e99b0c6c312e73041 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 15:22:51 +0000 Subject: [PATCH 2679/5615] Bump aws-java-sdk-logs from 1.12.232 to 1.12.253 in /modules/localstack Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.12.232 to 1.12.253. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.232...1.12.253) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-logs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index d6bd9781720..17e63142882 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,7 +6,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.231' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.231' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.253' - testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.232' + testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.253' testImplementation 'software.amazon.awssdk:s3:2.17.224' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 69948b3600aa71f9e1ffd8c7e73acbc4a5dd0f49 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 15:23:01 +0000 Subject: [PATCH 2680/5615] Bump okhttp from 4.9.3 to 4.10.0 in /examples Bumps [okhttp](https://github.com/square/okhttp) from 4.9.3 to 4.10.0. - [Release notes](https://github.com/square/okhttp/releases) - [Changelog](https://github.com/square/okhttp/blob/master/CHANGELOG.md) - [Commits](https://github.com/square/okhttp/compare/parent-4.9.3...parent-4.10.0) --- updated-dependencies: - dependency-name: com.squareup.okhttp3:okhttp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 4bb216e6fb7..c6272fc7522 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.36' - implementation 'com.squareup.okhttp3:okhttp:4.9.3' + implementation 'com.squareup.okhttp3:okhttp:4.10.0' implementation 'org.json:json:20220320' testImplementation 'org.postgresql:postgresql:42.4.0' testImplementation 'ch.qos.logback:logback-classic:1.2.11' From 5eed792d4679811cf84c1e58887dac7bb1b13901 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 15:23:03 +0000 Subject: [PATCH 2681/5615] Bump google-cloud-spanner from 6.25.5 to 6.25.7 in /modules/gcloud Bumps [google-cloud-spanner](https://github.com/googleapis/java-spanner) from 6.25.5 to 6.25.7. - [Release notes](https://github.com/googleapis/java-spanner/releases) - [Changelog](https://github.com/googleapis/java-spanner/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-spanner/compare/v6.25.5...v6.25.7) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-spanner dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 4af736c6cb8..2cabbc4cf30 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -6,7 +6,7 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-datastore:2.10.0' testImplementation 'com.google.cloud:google-cloud-firestore:3.2.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.0' - testImplementation 'com.google.cloud:google-cloud-spanner:6.25.5' + testImplementation 'com.google.cloud:google-cloud-spanner:6.25.7' testImplementation 'com.google.cloud:google-cloud-bigtable:2.9.0' testImplementation 'org.assertj:assertj-core:3.23.1' } From 2c0a004b5208edc57aed7c65d5241325b08b9cd9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 15:23:15 +0000 Subject: [PATCH 2682/5615] Bump common-custom-user-data-gradle-plugin from 1.7 to 1.7.2 Bumps common-custom-user-data-gradle-plugin from 1.7 to 1.7.2. --- updated-dependencies: - dependency-name: com.gradle:common-custom-user-data-gradle-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index 73cd6b977fd..d4605c2ad84 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10.2" - classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7" + classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7.2" } } From 0060e2b11ce5602f0b000d3deb9ce74b49c91b97 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 20:43:23 +0000 Subject: [PATCH 2683/5615] Bump org.jetbrains.kotlin.plugin.spring in /examples Bumps [org.jetbrains.kotlin.plugin.spring](https://github.com/JetBrains/kotlin) from 1.6.21 to 1.7.0. - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.6.21...v1.7.0) --- updated-dependencies: - dependency-name: org.jetbrains.kotlin.plugin.spring dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index c0bd965ab79..90338ff9959 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,7 +1,7 @@ plugins { id("org.springframework.boot") version "2.6.4" id("org.jetbrains.kotlin.jvm") version "1.7.0" - id("org.jetbrains.kotlin.plugin.spring") version "1.6.21" + id("org.jetbrains.kotlin.plugin.spring") version "1.7.0" } apply plugin: 'io.spring.dependency-management' From 46b48e60fc2685ceefe7384e3b95f5d8183cbbd1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 20:43:33 +0000 Subject: [PATCH 2684/5615] Bump kafka-clients from 3.1.0 to 3.2.0 in /examples Bumps kafka-clients from 3.1.0 to 3.2.0. --- updated-dependencies: - dependency-name: org.apache.kafka:kafka-clients dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/kafka-cluster/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index 79e91ec5a68..d69af5ceb68 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -10,7 +10,7 @@ dependencies { testCompileOnly "org.projectlombok:lombok:1.18.24" testAnnotationProcessor "org.projectlombok:lombok:1.18.24" testImplementation 'org.testcontainers:kafka' - testImplementation 'org.apache.kafka:kafka-clients:3.1.0' + testImplementation 'org.apache.kafka:kafka-clients:3.2.0' testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'com.google.guava:guava:23.0' testImplementation 'ch.qos.logback:logback-classic:1.2.11' From 364111407464cf4b50bf45f1d8638b1a49ed38e0 Mon Sep 17 00:00:00 2001 From: Naveen <172697+naveensrinivasan@users.noreply.github.com> Date: Thu, 7 Jul 2022 18:47:42 -0500 Subject: [PATCH 2685/5615] chore: Set permissions for GitHub actions (#5523) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restrict the GitHub token permissions only to the required ones; this way, even if the attackers will succeed in compromising your workflow, they won’t be able to do much. - Included permissions for the action. https://github.com/ossf/scorecard/blob/main/docs/checks.md#token-permissions https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs [Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/) Signed-off-by: naveen <172697+naveensrinivasan@users.noreply.github.com> --- .github/workflows/ci-docker-wormhole.yml | 3 +++ .github/workflows/ci-examples.yml | 3 +++ .github/workflows/ci-rootless.yml | 3 +++ .github/workflows/release-drafter.yml | 6 ++++++ .github/workflows/release.yml | 3 +++ .github/workflows/update-docs-version.yml | 6 ++++++ 6 files changed, 24 insertions(+) diff --git a/.github/workflows/ci-docker-wormhole.yml b/.github/workflows/ci-docker-wormhole.yml index 6d0df9a3c21..e27e4d8d611 100644 --- a/.github/workflows/ci-docker-wormhole.yml +++ b/.github/workflows/ci-docker-wormhole.yml @@ -4,6 +4,9 @@ on: pull_request: {} push: { branches: [ master ] } +permissions: + contents: read + jobs: in-docker_test: runs-on: ubuntu-18.04 diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index b1466ba8621..f24fd6f6916 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -10,6 +10,9 @@ env: GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }} +permissions: + contents: read + jobs: find_gradle_jobs: runs-on: ubuntu-18.04 diff --git a/.github/workflows/ci-rootless.yml b/.github/workflows/ci-rootless.yml index 45c58c5e2d0..348cf5aaaf7 100644 --- a/.github/workflows/ci-rootless.yml +++ b/.github/workflows/ci-rootless.yml @@ -4,6 +4,9 @@ on: pull_request: {} push: { branches: [ master ] } +permissions: + contents: read + jobs: test: runs-on: ubuntu-18.04 diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 132b5ffd0a5..fb0780d781f 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -7,8 +7,14 @@ on: pull_request: types: [opened, reopened, synchronize] +permissions: + contents: read + jobs: update_release_draft: + permissions: + contents: write # for release-drafter/release-drafter to create a github release + pull-requests: write # for release-drafter/release-drafter to add label to PR if: github.repository == 'testcontainers/testcontainers-java' runs-on: ubuntu-latest steps: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5c786d440c5..29a0b920922 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,6 +8,9 @@ env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} +permissions: + contents: read + jobs: release: runs-on: ubuntu-18.04 diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index ed21bca4670..b2736c42590 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -4,8 +4,14 @@ on: release: types: [ published ] +permissions: + contents: read + jobs: build: + permissions: + contents: write # for peter-evans/create-pull-request to create branch + pull-requests: write # for peter-evans/create-pull-request to create a PR if: github.repository == 'testcontainers/testcontainers-java' runs-on: ubuntu-18.04 steps: From 2f3178d04fb19dbdaa9d686380c6894e443b2377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Thu, 7 Jul 2022 18:49:12 -0500 Subject: [PATCH 2686/5615] Add read permission to ci.yml --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 70ee552b798..3287499c2fc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,9 @@ on: pull_request: {} push: { branches: [ master ] } +permissions: + contents: read + env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} From 84d3444600aaeeac6813505fe5bfbf9ffce760ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Thu, 7 Jul 2022 19:44:03 -0500 Subject: [PATCH 2687/5615] Add test-retry plugin (#5586) * Enable the plugin only in CI environment * Every test will retry up to 2 times. 3 runs per test in general * Disable retry if 5 tests (no retries are count) fail --- build.gradle | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/build.gradle b/build.gradle index 1458b3db027..8c956133b03 100644 --- a/build.gradle +++ b/build.gradle @@ -16,6 +16,7 @@ plugins { id 'com.github.johnrengelman.shadow' version '7.0.0' id 'me.champeau.gradle.japicmp' version '0.2.9' apply false id 'com.diffplug.spotless' version '6.3.0' apply false + id 'org.gradle.test-retry' version '1.4.0' } apply from: "$rootDir/gradle/ci-support.gradle" @@ -33,6 +34,7 @@ subprojects { apply from: "$rootDir/gradle/shading.gradle" apply plugin: 'com.diffplug.spotless' apply plugin: 'checkstyle' + apply plugin: 'org.gradle.test-retry' group = "org.testcontainers" @@ -86,6 +88,14 @@ subprojects { exceptionFormat = 'full' events "STARTED", "PASSED", "FAILED", "SKIPPED" } + ext.isCI = System.getenv("CI") != null + if (isCI) { + retry { + maxRetries = 2 + maxFailures = 5 + failOnPassedAfterRetry = false + } + } } tasks.withType(Test).all { From d1b13d716616ae359cb9c9b99bd097493d691da1 Mon Sep 17 00:00:00 2001 From: Josef Cacek Date: Tue, 12 Jul 2022 14:08:29 +0200 Subject: [PATCH 2688/5615] Fix javadoc comment in ToxiproxyContainer#getProxy(GenericContainer, int) (#5591) --- .../java/org/testcontainers/containers/ToxiproxyContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java b/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java index 9850d7a0918..5f496a38873 100644 --- a/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java +++ b/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java @@ -79,7 +79,7 @@ public int getControlPort() { /** * Obtain a {@link ContainerProxy} instance for target container that is managed by Testcontainers. The target - * container should be routable from this from this {@link ToxiproxyContainer} instance (e.g. on the same + * container should be routable from this {@link ToxiproxyContainer} instance (e.g. on the same * Docker {@link Network}). * * @param container target container From a76a7f52513426692f8d41cb01369f2aa10a3160 Mon Sep 17 00:00:00 2001 From: Quinn Bast Date: Wed, 13 Jul 2022 17:17:15 -0600 Subject: [PATCH 2689/5615] Add documentation for DependsOn (#5597) --- .../src/test/java/generic/DependsOnTest.java | 25 +++++++++++++++++++ docs/features/startup_and_waits.md | 8 ++++++ 2 files changed, 33 insertions(+) create mode 100644 docs/examples/junit4/generic/src/test/java/generic/DependsOnTest.java diff --git a/docs/examples/junit4/generic/src/test/java/generic/DependsOnTest.java b/docs/examples/junit4/generic/src/test/java/generic/DependsOnTest.java new file mode 100644 index 00000000000..6534a35a00f --- /dev/null +++ b/docs/examples/junit4/generic/src/test/java/generic/DependsOnTest.java @@ -0,0 +1,25 @@ +package generic; + +import org.junit.Rule; +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; + +import static org.junit.Assert.assertTrue; + +public class DependsOnTest { + + @Rule + // dependsOn { + public GenericContainer redis = new GenericContainer<>("redis:3.0.2").withExposedPorts(6379); + + @Rule + public GenericContainer nginx = new GenericContainer<>("nginx:1.9.4").dependsOn(redis).withExposedPorts(80); + + // } + + @Test + public void testContainersAllStarted() { + assertTrue(redis.isRunning()); + assertTrue(nginx.isRunning()); + } +} diff --git a/docs/features/startup_and_waits.md b/docs/features/startup_and_waits.md index 3cbec97af0d..e092919d496 100644 --- a/docs/features/startup_and_waits.md +++ b/docs/features/startup_and_waits.md @@ -123,3 +123,11 @@ If none of these options meet your requirements, you can create your own subclas /startupcheck/StartupCheckStrategy.html) with an appropriate startup check mechanism in `waitUntilStartupSuccessful()`. Or you can leave it as is and just implement the `checkStartupState(DockerClient dockerClient, String containerId)` if you still want to check state periodically. + +## Depending on another container + +Sometimes, a container relies on another container to be ready before it should start itself. An example of this might be a database that needs to be started before your application container can link to it. You can tell a container that it depends on another container by using the `dependsOn` method: + + +[Depending on another container](../examples/junit4/generic/src/test/java/generic/DependsOnTest.java) inside_block:dependsOn + From 7e364891eed1eda2fdc1cdfea7e5facaccc7b025 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 14 Jul 2022 19:31:32 -0500 Subject: [PATCH 2690/5615] Update Gradle Wrapper to 7.5 (#5598) Signed-off-by: gradle-update-robot Co-authored-by: gradle-update-robot --- examples/gradle/wrapper/gradle-wrapper.jar | Bin 59821 -> 60756 bytes .../gradle/wrapper/gradle-wrapper.properties | 4 ++-- examples/gradlew | 6 ++++++ examples/gradlew.bat | 14 ++++++++------ gradle/wrapper/gradle-wrapper.jar | Bin 59821 -> 60756 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++-- gradlew | 6 ++++++ gradlew.bat | 14 ++++++++------ 8 files changed, 32 insertions(+), 16 deletions(-) diff --git a/examples/gradle/wrapper/gradle-wrapper.jar b/examples/gradle/wrapper/gradle-wrapper.jar index 41d9927a4d4fb3f96a785543079b8df6723c946b..249e5832f090a2944b7473328c07c9755baa3196 100644 GIT binary patch delta 10197 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 diff --git a/examples/gradle/wrapper/gradle-wrapper.properties b/examples/gradle/wrapper/gradle-wrapper.properties index 5c51a4acc9d..012d6d90445 100644 --- a/examples/gradle/wrapper/gradle-wrapper.properties +++ b/examples/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=29e49b10984e585d8118b7d0bc452f944e386458df27371b49b4ac1dec4b7fda -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +distributionSha256Sum=cb87f222c5585bd46838ad4db78463a5c5f3d336e5e2b98dc7c0c586527351c2 +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/examples/gradlew b/examples/gradlew index 1b6c787337f..a69d9cb6c20 100755 --- a/examples/gradlew +++ b/examples/gradlew @@ -205,6 +205,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/examples/gradlew.bat b/examples/gradlew.bat index ac1b06f9382..53a6b238d41 100644 --- a/examples/gradlew.bat +++ b/examples/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 41d9927a4d4fb3f96a785543079b8df6723c946b..249e5832f090a2944b7473328c07c9755baa3196 100644 GIT binary patch delta 10197 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e1e0c8dc42b..ef80eb5051f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=e6d864e3b5bc05cc62041842b306383fc1fefcec359e70cebb1d470a6094ca82 -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip +distributionSha256Sum=97a52d145762adc241bad7fd18289bf7f6801e08ece6badf80402fe2b9f250b1 +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c787337f..a69d9cb6c20 100755 --- a/gradlew +++ b/gradlew @@ -205,6 +205,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index ac1b06f9382..53a6b238d41 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal From 55ec5f1c5311f7fbc959ae56a4b1e546915c87fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Tue, 19 Jul 2022 16:50:55 -0500 Subject: [PATCH 2691/5615] Use host instead of containerIpAddress for Kotlin sample --- .../test/kotlin/com/example/redis/AbstractIntegrationTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/src/test/kotlin/com/example/redis/AbstractIntegrationTest.kt b/examples/spring-boot-kotlin-redis/src/test/kotlin/com/example/redis/AbstractIntegrationTest.kt index 6350ace9bd9..98f56192087 100644 --- a/examples/spring-boot-kotlin-redis/src/test/kotlin/com/example/redis/AbstractIntegrationTest.kt +++ b/examples/spring-boot-kotlin-redis/src/test/kotlin/com/example/redis/AbstractIntegrationTest.kt @@ -26,7 +26,7 @@ abstract class AbstractIntegrationTest { redisContainer.start() TestPropertyValues.of( - "spring.redis.host=${redisContainer.containerIpAddress}", + "spring.redis.host=${redisContainer.host}", "spring.redis.port=${redisContainer.firstMappedPort}" ).applyTo(configurableApplicationContext.environment) } From 8c8bf2a818da7bbe1ae3dca2171eebef89746a2b Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Wed, 20 Jul 2022 19:11:35 +0200 Subject: [PATCH 2692/5615] Enable JUnitPlatform in Gradle for JUnit5 examples (#4130) --- docs/examples/junit5/redis/build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/examples/junit5/redis/build.gradle b/docs/examples/junit5/redis/build.gradle index 348f4788555..16f0d5bc598 100644 --- a/docs/examples/junit5/redis/build.gradle +++ b/docs/examples/junit5/redis/build.gradle @@ -9,3 +9,7 @@ dependencies { testImplementation project(":testcontainers") testImplementation project(":junit-jupiter") } + +test { + useJUnitPlatform() +} From c654018f93f5259897762c2c6a662b542a765fe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Thu, 21 Jul 2022 03:16:53 -0500 Subject: [PATCH 2693/5615] Update spotless version to 6.8.0 (#5605) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 8c956133b03..0ded70ad71a 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ plugins { id 'io.franzbecker.gradle-lombok' version '5.0.0' id 'com.github.johnrengelman.shadow' version '7.0.0' id 'me.champeau.gradle.japicmp' version '0.2.9' apply false - id 'com.diffplug.spotless' version '6.3.0' apply false + id 'com.diffplug.spotless' version '6.8.0' apply false id 'org.gradle.test-retry' version '1.4.0' } From d387d8518cf0976618c6580c09f68b53c4008283 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Thu, 21 Jul 2022 16:51:50 +0200 Subject: [PATCH 2694/5615] Update Oracle-XE docs and use code example from tests (#4385) Co-authored-by: Sergei Egorov --- docs/modules/databases/oraclexe.md | 25 +++---------------- .../junit/oracle/SimpleOracleTest.java | 4 ++- 2 files changed, 7 insertions(+), 22 deletions(-) diff --git a/docs/modules/databases/oraclexe.md b/docs/modules/databases/oraclexe.md index 583d68f3c3d..bfc06d9a106 100644 --- a/docs/modules/databases/oraclexe.md +++ b/docs/modules/databases/oraclexe.md @@ -4,27 +4,10 @@ See [Database containers](./index.md) for documentation and usage that is common ## Usage example -Running Oracle XE as a stand-in for in a test: - -```java -public class SomeTest { - - @Rule - public OracleContainer oracle = new OracleContainer("name_of_your_oracle_xe_image"); - - @Test - public void someTestMethod() { - String url = oracle.getJdbcUrl(); - - ... create a connection and run test as normal -``` - -## Specifying a docker image name via config - -If you do not pass an image name to the `OracleContainer` constructor, a suitable image name should be placed in -configuration instead. -To do this, please place a file on the classpath named `testcontainers.properties`, -containing `oracle.container.image=IMAGE`, where IMAGE is a suitable image name and tag. +You can use `OracleContainer` like any other JDBC container: + +[Container creation](../../../modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java) inside_block:constructor + ## Adding this module to your project dependencies diff --git a/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java b/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java index 2c514062415..b48bd67efc9 100644 --- a/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java +++ b/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java @@ -52,10 +52,12 @@ public void testPluggableDatabase() throws SQLException { @Test public void testPluggableDatabaseAndCustomUser() throws SQLException { try ( - OracleContainer oracle = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME) + // constructor { + OracleContainer oracle = new OracleContainer("gvenzl/oracle-xe:18.4.0-slim") .withDatabaseName("testDB") .withUsername("testUser") .withPassword("testPassword") + // } ) { runTest(oracle, "testDB", "testUser", "testPassword"); } From 29aa84d19739f3d20c485275180e0619dd4145b1 Mon Sep 17 00:00:00 2001 From: Tobias Stadler Date: Thu, 21 Jul 2022 16:54:40 +0200 Subject: [PATCH 2695/5615] Update alpine image to 3.16 (#4698) --- .../main/java/org/testcontainers/DockerClientFactory.java | 2 +- core/src/test/java/org/testcontainers/TestImages.java | 4 ++-- .../testcontainers/containers/DockerComposeFilesTest.java | 2 +- .../containers/ParsedDockerComposeFileValidationTest.java | 8 ++++---- .../containers/output/ToStringConsumerTest.java | 8 ++++---- .../org/testcontainers/dockerclient/ImagePullTest.java | 2 +- .../java/org/testcontainers/junit/DockerfileTest.java | 6 +++--- .../testcontainers/junit/FixedHostPortContainerTest.java | 2 +- .../junit/ParameterizedDockerfileContainerTest.java | 2 ++ .../testcontainers/utility/DirectoryTarResourceTest.java | 6 +++--- core/src/test/resources/compose-dockerfile/Dockerfile | 2 +- .../resources/compose-scaling-multiple-containers.yml | 2 +- .../test/resources/dockerfile-build-invalid/Dockerfile | 2 +- core/src/test/resources/dockerfile-build-test/Dockerfile | 2 +- .../test/resources/dockerfile-build-test/Dockerfile-alt | 2 +- .../resources/dockerfile-build-test/Dockerfile-buildarg | 2 +- .../resources/dockerfile-build-test/Dockerfile-currentdir | 2 +- .../resources/health-wait-strategy-dockerfile/Dockerfile | 2 +- core/src/test/resources/mappable-dockerfile/Dockerfile | 2 +- .../src/test/java/generic/ContainerCreationTest.java | 2 +- .../generic/src/test/java/generic/ContainerLabelTest.java | 4 ++-- .../junit4/generic/src/test/java/generic/ExecTest.java | 2 +- docs/features/configuration.md | 2 +- docs/features/creating_images.md | 2 +- .../org/testcontainers/spock/SpockTestImages.groovy | 2 +- 25 files changed, 38 insertions(+), 36 deletions(-) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 04c20236763..d4b40193ca2 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -60,7 +60,7 @@ public class DockerClientFactory { public static final Map DEFAULT_LABELS = ImmutableMap.of(TESTCONTAINERS_LABEL, "true"); - private static final DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.14"); + private static final DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.16"); private static DockerClientFactory instance; diff --git a/core/src/test/java/org/testcontainers/TestImages.java b/core/src/test/java/org/testcontainers/TestImages.java index 3798f42af0d..5308f341100 100644 --- a/core/src/test/java/org/testcontainers/TestImages.java +++ b/core/src/test/java/org/testcontainers/TestImages.java @@ -6,7 +6,7 @@ public interface TestImages { DockerImageName REDIS_IMAGE = DockerImageName.parse("redis:3.0.2"); DockerImageName RABBITMQ_IMAGE = DockerImageName.parse("rabbitmq:3.5.3"); DockerImageName MONGODB_IMAGE = DockerImageName.parse("mongo:3.1.5"); - DockerImageName ALPINE_IMAGE = DockerImageName.parse("alpine:3.14"); + DockerImageName ALPINE_IMAGE = DockerImageName.parse("alpine:3.16"); DockerImageName DOCKER_REGISTRY_IMAGE = DockerImageName.parse("registry:2.7.0"); - DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.14"); + DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.16"); } diff --git a/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java b/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java index 6e1630f005f..612d249449b 100644 --- a/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java +++ b/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java @@ -28,6 +28,6 @@ public void shouldGetDependencyImagesWhenOverriding() { ); Assertions .assertThat(dockerComposeFiles.getDependencyImages()) - .containsExactlyInAnyOrder("alpine:3.14", "redis:b", "mysql:b", "aservice:latest"); + .containsExactlyInAnyOrder("alpine:3.16", "redis:b", "mysql:b", "aservice:latest"); } } diff --git a/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java b/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java index 61955de98ba..f374018a8ef 100644 --- a/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java +++ b/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java @@ -98,9 +98,9 @@ public void shouldObtainImageFromDockerfileBuild() { .contains( entry("mysql", Sets.newHashSet("mysql")), entry("redis", Sets.newHashSet("redis")), - entry("custom", Sets.newHashSet("alpine:3.14")) + entry("custom", Sets.newHashSet("alpine:3.16")) ) - .as("all defined images are found"); // r/ redis, mysql from compose file, alpine:3.14 from Dockerfile build + .as("all defined images are found"); // r/ redis, mysql from compose file, alpine:3.16 from Dockerfile build } @Test @@ -112,8 +112,8 @@ public void shouldObtainImageFromDockerfileBuildWithContext() { .contains( entry("mysql", Sets.newHashSet("mysql")), entry("redis", Sets.newHashSet("redis")), - entry("custom", Sets.newHashSet("alpine:3.14")) + entry("custom", Sets.newHashSet("alpine:3.16")) ) - .as("all defined images are found"); // redis, mysql from compose file, alpine:3.14 from Dockerfile build + .as("all defined images are found"); // redis, mysql from compose file, alpine:3.16 from Dockerfile build } } diff --git a/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java b/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java index 08973f2d601..211b12080a0 100644 --- a/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java +++ b/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java @@ -24,7 +24,7 @@ public class ToStringConsumerTest { @Test public void newlines_are_not_added_to_exec_output() throws Exception { - try (GenericContainer container = new GenericContainer<>("alpine:3.14")) { + try (GenericContainer container = new GenericContainer<>("alpine:3.16")) { container.withCommand("sleep", "2m"); container.start(); @@ -35,7 +35,7 @@ public void newlines_are_not_added_to_exec_output() throws Exception { @Test(timeout = 60_000L) public void newlines_are_not_added_to_exec_output_with_tty() throws Exception { - try (GenericContainer container = new GenericContainer<>("alpine:3.14")) { + try (GenericContainer container = new GenericContainer<>("alpine:3.16")) { container.withCreateContainerCmdModifier(cmd -> { cmd.withAttachStdin(true).withStdinOpen(true).withTty(true); }); @@ -49,7 +49,7 @@ public void newlines_are_not_added_to_exec_output_with_tty() throws Exception { @Test public void newlines_are_not_added_to_container_output() { - try (GenericContainer container = new GenericContainer<>("alpine:3.14")) { + try (GenericContainer container = new GenericContainer<>("alpine:3.16")) { container.withCommand("echo", "-n", LARGE_PAYLOAD); container.setStartupCheckStrategy(new OneShotStartupCheckStrategy()); container.start(); @@ -62,7 +62,7 @@ public void newlines_are_not_added_to_container_output() { @Test public void newlines_are_not_added_to_container_output_with_tty() { - try (GenericContainer container = new GenericContainer<>("alpine:3.14")) { + try (GenericContainer container = new GenericContainer<>("alpine:3.16")) { container.withCreateContainerCmdModifier(cmd -> { cmd.withTty(true); }); diff --git a/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java b/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java index 730208c553f..e1c2a56e8a9 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java @@ -16,7 +16,7 @@ public class ImagePullTest { public static String[] parameters() { return new String[] { "alpine:latest", - "alpine:3.14", + "alpine:3.16", "alpine", // omitting the tag should work and default to latest "alpine@sha256:1775bebec23e1f3ce486989bfc9ff3c4e951690df84aa9f926497d82f2ffca9d", "quay.io/testcontainers/ryuk:latest", diff --git a/core/src/test/java/org/testcontainers/junit/DockerfileTest.java b/core/src/test/java/org/testcontainers/junit/DockerfileTest.java index e121e7db5a4..df7bc417688 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerfileTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerfileTest.java @@ -40,7 +40,7 @@ protected void configure(BuildImageCmd buildImageCmd) { super.configure(buildImageCmd); List dockerfile = Arrays.asList( - "FROM alpine:3.14", + "FROM alpine:3.16", "RUN echo 'hello from Docker build process'", "CMD yes" ); @@ -60,7 +60,7 @@ public void dockerfileBuilderWorks() { .withFileFromString("folder/someFile.txt", "hello") .withDockerfileFromBuilder(builder -> { builder - .from("alpine:3.14") + .from("alpine:3.16") .workDir("/app") .add("test.txt", "test file.txt") .run("ls", "-la", "/app/test file.txt") @@ -103,7 +103,7 @@ public int getFileMode() { ) .withDockerfileFromBuilder(builder -> { builder - .from("alpine:3.14") // + .from("alpine:3.16") // .copy("someFile.txt", "/someFile.txt") .cmd("stat -c \"%a\" /someFile.txt"); }); diff --git a/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java b/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java index a6bb13d160c..08545535e6b 100644 --- a/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java @@ -24,7 +24,7 @@ */ public class FixedHostPortContainerTest { - private static final String TEST_IMAGE = "alpine:3.14"; + private static final String TEST_IMAGE = "alpine:3.16"; /** * Default http server port (just something different from default) diff --git a/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java b/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java index 62eccc2000c..7c176f14a1a 100644 --- a/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java @@ -43,6 +43,8 @@ public static Object[][] data() { { "alpine:3.12", "3.12" }, { "alpine:3.13", "3.13" }, { "alpine:3.14", "3.14" }, + { "alpine:3.15", "3.15" }, + { "alpine:3.16", "3.16" }, }; } diff --git a/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java b/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java index cbd35a62b52..46fd6894a02 100644 --- a/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java +++ b/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java @@ -27,7 +27,7 @@ public void simpleRecursiveFileTest() { new ImageFromDockerfile() .withDockerfileFromBuilder(builder -> { builder - .from("alpine:3.14") + .from("alpine:3.16") .copy("/tmp/foo", "/foo") .cmd("cat /foo/test/resources/test-recursive-file.txt") .build(); @@ -52,7 +52,7 @@ public void simpleRecursiveFileWithPermissionTest() { new ImageFromDockerfile() .withDockerfileFromBuilder(builder -> { builder - .from("alpine:3.14") // + .from("alpine:3.16") // .copy("/tmp/foo", "/foo") .cmd("ls", "-al", "/") .build(); @@ -79,7 +79,7 @@ public void simpleRecursiveClasspathResourceTest() { new ImageFromDockerfile() .withDockerfileFromBuilder(builder -> { builder - .from("alpine:3.14") // + .from("alpine:3.16") // .copy("/tmp/foo", "/foo") .cmd("ls -lRt /foo") .build(); diff --git a/core/src/test/resources/compose-dockerfile/Dockerfile b/core/src/test/resources/compose-dockerfile/Dockerfile index 7252932b104..566428fa97f 100644 --- a/core/src/test/resources/compose-dockerfile/Dockerfile +++ b/core/src/test/resources/compose-dockerfile/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.14 +FROM alpine:3.16 ADD passthrough.sh /passthrough.sh diff --git a/core/src/test/resources/compose-scaling-multiple-containers.yml b/core/src/test/resources/compose-scaling-multiple-containers.yml index 3ea528d4574..fc498a9fd42 100644 --- a/core/src/test/resources/compose-scaling-multiple-containers.yml +++ b/core/src/test/resources/compose-scaling-multiple-containers.yml @@ -3,5 +3,5 @@ services: redis: image: redis other: - image: alpine:3.14 + image: alpine:3.16 command: sleep 10000 diff --git a/core/src/test/resources/dockerfile-build-invalid/Dockerfile b/core/src/test/resources/dockerfile-build-invalid/Dockerfile index b0198eb3e02..66663986d13 100644 --- a/core/src/test/resources/dockerfile-build-invalid/Dockerfile +++ b/core/src/test/resources/dockerfile-build-invalid/Dockerfile @@ -1 +1 @@ -FROM alpine:3.14 +FROM alpine:3.16 diff --git a/core/src/test/resources/dockerfile-build-test/Dockerfile b/core/src/test/resources/dockerfile-build-test/Dockerfile index 9ae7dba8658..3f341034fef 100644 --- a/core/src/test/resources/dockerfile-build-test/Dockerfile +++ b/core/src/test/resources/dockerfile-build-test/Dockerfile @@ -1,2 +1,2 @@ -FROM alpine:3.14 +FROM alpine:3.16 COPY localfile.txt /test.txt diff --git a/core/src/test/resources/dockerfile-build-test/Dockerfile-alt b/core/src/test/resources/dockerfile-build-test/Dockerfile-alt index 43e5c556fe9..fe36516c397 100644 --- a/core/src/test/resources/dockerfile-build-test/Dockerfile-alt +++ b/core/src/test/resources/dockerfile-build-test/Dockerfile-alt @@ -1,2 +1,2 @@ -FROM alpine:3.14 +FROM alpine:3.16 RUN echo "test4567" > /test.txt diff --git a/core/src/test/resources/dockerfile-build-test/Dockerfile-buildarg b/core/src/test/resources/dockerfile-build-test/Dockerfile-buildarg index b37f4193643..f137d39f0d2 100644 --- a/core/src/test/resources/dockerfile-build-test/Dockerfile-buildarg +++ b/core/src/test/resources/dockerfile-build-test/Dockerfile-buildarg @@ -1,3 +1,3 @@ -FROM alpine:3.14 +FROM alpine:3.16 ARG CUSTOM_ARG RUN echo "${CUSTOM_ARG}" > /test.txt diff --git a/core/src/test/resources/dockerfile-build-test/Dockerfile-currentdir b/core/src/test/resources/dockerfile-build-test/Dockerfile-currentdir index e8456e1bc1f..3a6bfd62f02 100644 --- a/core/src/test/resources/dockerfile-build-test/Dockerfile-currentdir +++ b/core/src/test/resources/dockerfile-build-test/Dockerfile-currentdir @@ -1,2 +1,2 @@ -FROM alpine:3.14 +FROM alpine:3.16 COPY . / diff --git a/core/src/test/resources/health-wait-strategy-dockerfile/Dockerfile b/core/src/test/resources/health-wait-strategy-dockerfile/Dockerfile index c0abb7bb597..ed404c1f0d8 100644 --- a/core/src/test/resources/health-wait-strategy-dockerfile/Dockerfile +++ b/core/src/test/resources/health-wait-strategy-dockerfile/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.14 +FROM alpine:3.16 HEALTHCHECK --interval=1s CMD test -e /testfile diff --git a/core/src/test/resources/mappable-dockerfile/Dockerfile b/core/src/test/resources/mappable-dockerfile/Dockerfile index df186f6579c..5b9f6a02783 100644 --- a/core/src/test/resources/mappable-dockerfile/Dockerfile +++ b/core/src/test/resources/mappable-dockerfile/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.14 +FROM alpine:3.16 ADD folder/someFile.txt /someFile.txt RUN cat /someFile.txt ADD test.txt /test.txt diff --git a/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java b/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java index bdd82968191..0f6269968dd 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java @@ -20,7 +20,7 @@ public class ContainerCreationTest { // } // spotless:on - public static final DockerImageName ALPINE_IMAGE = DockerImageName.parse("alpine:3.14"); + public static final DockerImageName ALPINE_IMAGE = DockerImageName.parse("alpine:3.16"); // spotless:off // withOptions { diff --git a/docs/examples/junit4/generic/src/test/java/generic/ContainerLabelTest.java b/docs/examples/junit4/generic/src/test/java/generic/ContainerLabelTest.java index 64618ad25a9..868b8ec44f7 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/ContainerLabelTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/ContainerLabelTest.java @@ -9,7 +9,7 @@ public class ContainerLabelTest { // single_label { - public GenericContainer containerWithLabel = new GenericContainer(DockerImageName.parse("alpine:3.14")) + public GenericContainer containerWithLabel = new GenericContainer(DockerImageName.parse("alpine:3.16")) .withLabel("key", "value"); // } @@ -17,7 +17,7 @@ public class ContainerLabelTest { private Map mapOfLabels = new HashMap<>(); // populate map, e.g. mapOfLabels.put("key1", "value1"); - public GenericContainer containerWithMultipleLabels = new GenericContainer(DockerImageName.parse("alpine:3.14")) + public GenericContainer containerWithMultipleLabels = new GenericContainer(DockerImageName.parse("alpine:3.16")) .withLabels(mapOfLabels); // } } diff --git a/docs/examples/junit4/generic/src/test/java/generic/ExecTest.java b/docs/examples/junit4/generic/src/test/java/generic/ExecTest.java index 2530cadb410..7548ae4a613 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/ExecTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/ExecTest.java @@ -13,7 +13,7 @@ public class ExecTest { @Rule - public GenericContainer container = new GenericContainer<>(DockerImageName.parse("alpine:3.14")) + public GenericContainer container = new GenericContainer<>(DockerImageName.parse("alpine:3.16")) .withCommand("top"); @Test diff --git a/docs/features/configuration.md b/docs/features/configuration.md index fbb369fe6a6..f93d0807d01 100644 --- a/docs/features/configuration.md +++ b/docs/features/configuration.md @@ -49,7 +49,7 @@ Some companies disallow the usage of Docker Hub, but you can override `*.image` > **ryuk.container.image = testcontainers/ryuk:0.3.3** > Performs fail-safe cleanup of containers, and always required (unless [Ryuk is disabled](#disabling-ryuk)) -> **tinyimage.container.image = alpine:3.14** +> **tinyimage.container.image = alpine:3.16** > Used to check whether images can be pulled at startup, and always required (unless [startup checks are disabled](#disabling-the-startup-checks)) > **sshd.container.image = testcontainers/sshd:1.1.0** diff --git a/docs/features/creating_images.md b/docs/features/creating_images.md index 6a06627efa0..e4e6315a405 100644 --- a/docs/features/creating_images.md +++ b/docs/features/creating_images.md @@ -51,7 +51,7 @@ new GenericContainer( new ImageFromDockerfile() .withDockerfileFromBuilder(builder -> builder - .from("alpine:3.14") + .from("alpine:3.16") .run("apk add --update nginx") .cmd("nginx", "-g", "daemon off;") .build())) diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy index 41432e5e514..bea0a974c89 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy @@ -6,5 +6,5 @@ interface SpockTestImages { DockerImageName MYSQL_IMAGE = DockerImageName.parse("mysql:5.7.34") DockerImageName POSTGRES_TEST_IMAGE = DockerImageName.parse("postgres:9.6.12") DockerImageName HTTPD_IMAGE = DockerImageName.parse("httpd:2.4-alpine") - DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.14") + DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.16") } From 6aab4d92aeaef5d1e6174f52267bf48edc3aaf8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Mon, 25 Jul 2022 03:05:10 -0500 Subject: [PATCH 2696/5615] Add pr label for area/docker-compose (#5610) --- .github/labeler.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/labeler.yml b/.github/labeler.yml index 9293237182e..84e21af744a 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,3 +1,6 @@ +"area/docker-compose": + - core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java + - core/src/main/java/org/testcontainers/containers/DockerComposeFiles.java "github_actions": - .github/workflows/* "gradle-wrapper": From d3cb25ec7d66332d124ad1a8fa36771a7090412d Mon Sep 17 00:00:00 2001 From: Erik Pragt <91307175+epragtbeamtree@users.noreply.github.com> Date: Wed, 27 Jul 2022 16:47:04 +1000 Subject: [PATCH 2697/5615] Update CircleCI docs for current Cloud/v2/v3 configuration (#5611) Co-authored-by: Kevin Wittek --- .../continuous_integration/circle_ci.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/docs/supported_docker_environment/continuous_integration/circle_ci.md b/docs/supported_docker_environment/continuous_integration/circle_ci.md index 64e7ea131f2..05f0776f6c1 100644 --- a/docs/supported_docker_environment/continuous_integration/circle_ci.md +++ b/docs/supported_docker_environment/continuous_integration/circle_ci.md @@ -1,19 +1,16 @@ -# CircleCI 2.0 +# CircleCI (Cloud, Server v2.x, and Server v3.x) -Your CircleCI configuration should use a dedicated VM for testcontainers to work. You can achieve this by specifying the -executor type in your `.circleci/config.yml` to be `machine` instead of the default `docker` executor ( see [Choosing an Executor Type](https://circleci.com/docs/2.0/executor-types/) for more info ). +Your CircleCI configuration should use a dedicated VM for Testcontainers to work. You can achieve this by specifying the +executor type in your `.circleci/config.yml` to be `machine` instead of the default `docker` executor (see [Choosing an Executor Type](https://circleci.com/docs/2.0/executor-types/) for more info). -Here is a sample CircleCI configuration that does a checkout of a project and runs maven: +Here is a sample CircleCI configuration that does a checkout of a project and runs Maven: ```yml -# Check https://circleci.com/docs/2.0/language-java/ for more details -# -version: 2 -machine: true jobs: build: + # Check https://circleci.com/docs/executor-intro#linux-vm for more details + machine: true steps: - checkout - - run: mvn -B clean install ``` From fd97b769a8b083a8e3a208d119377176fe88cb8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Wed, 27 Jul 2022 19:57:31 -0500 Subject: [PATCH 2698/5615] Add docs for AWS CodeBuild and Tekton (#5614) --- .../continuous_integration/aws_codebuild.md | 18 ++++ .../continuous_integration/tekton.md | 94 +++++++++++++++++++ mkdocs.yml | 2 + 3 files changed, 114 insertions(+) create mode 100644 docs/supported_docker_environment/continuous_integration/aws_codebuild.md create mode 100644 docs/supported_docker_environment/continuous_integration/tekton.md diff --git a/docs/supported_docker_environment/continuous_integration/aws_codebuild.md b/docs/supported_docker_environment/continuous_integration/aws_codebuild.md new file mode 100644 index 00000000000..c28854d9e0e --- /dev/null +++ b/docs/supported_docker_environment/continuous_integration/aws_codebuild.md @@ -0,0 +1,18 @@ +# AWS CodeBuild + +To enable access to Docker in AWS CodeBuild, go to `Privileged` section and check +`Enable this flag if you want to build Docker images or want your builds to get elevated privileges`. + +This is a sample `buildspec.yml` config: + +```yaml +version: 0.2 + +phases: + install: + runtime-versions: + java: corretto17 + build: + commands: + - ./mvnw test +``` diff --git a/docs/supported_docker_environment/continuous_integration/tekton.md b/docs/supported_docker_environment/continuous_integration/tekton.md new file mode 100644 index 00000000000..f8c6fb96adc --- /dev/null +++ b/docs/supported_docker_environment/continuous_integration/tekton.md @@ -0,0 +1,94 @@ +# Tekton + +o enable access to Docker in Tekton, a dind sidecar needs to be added. An example of it can be found +[here](https://github.com/tektoncd/pipeline/blob/main/examples/v1beta1/taskruns/dind-sidecar.yaml) + +This is an example + +```yaml +apiVersion: tekton.dev/v1beta1 +kind: Task +metadata: + name: run-tests + description: Run Tests +spec: + workspaces: + - name: source + steps: + - name: read + image: eclipse-temurin:17.0.3_7-jdk-alpine + workingDir: $(workspaces.source.path) + script: ./mvnw test + volumeMounts: + - mountPath: /var/run/ + name: dind-socket + sidecars: + - image: docker:20.10-dind + name: docker + securityContext: + privileged: true + volumeMounts: + - mountPath: /var/lib/docker + name: dind-storage + - mountPath: /var/run/ + name: dind-socket + volumes: + - name: dind-storage + emptyDir: { } + - name: dind-socket + emptyDir: { } +--- +apiVersion: tekton.dev/v1beta1 +kind: Pipeline +metadata: + name: testcontainers-demo +spec: + description: | + This pipeline clones a git repo, run testcontainers. + params: + - name: repo-url + type: string + description: The git repo URL to clone from. + workspaces: + - name: shared-data + description: | + This workspace contains the cloned repo files, so they can be read by the + next task. + tasks: + - name: fetch-source + taskRef: + name: git-clone + workspaces: + - name: output + workspace: shared-data + params: + - name: url + value: $(params.repo-url) + - name: run-tests + runAfter: ["fetch-source"] + taskRef: + name: run-tests + workspaces: + - name: source + workspace: shared-data +--- +apiVersion: tekton.dev/v1beta1 +kind: PipelineRun +metadata: + name: testcontainers-demo-run +spec: + pipelineRef: + name: testcontainers-demo + workspaces: + - name: shared-data + volumeClaimTemplate: + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi + params: + - name: repo-url + value: https://github.com/testcontainers/testcontainers-java-repro.git +``` diff --git a/mkdocs.yml b/mkdocs.yml index 21e157969e5..396986a955b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -88,11 +88,13 @@ nav: - System Requirements: - supported_docker_environment/index.md - Continuous Integration: + - supported_docker_environment/continuous_integration/aws_codebuild.md - supported_docker_environment/continuous_integration/dind_patterns.md - supported_docker_environment/continuous_integration/circle_ci.md - supported_docker_environment/continuous_integration/drone.md - supported_docker_environment/continuous_integration/gitlab_ci.md - supported_docker_environment/continuous_integration/bitbucket_pipelines.md + - supported_docker_environment/continuous_integration/tekton.md - supported_docker_environment/windows.md - supported_docker_environment/logging_config.md - supported_docker_environment/image_registry_rate_limiting.md From fca585db3920b127dae56600772d889e509a5dca Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 28 Jul 2022 03:10:56 -0400 Subject: [PATCH 2699/5615] Link to the docs on environment discovery failure (#5615) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Eddú Meléndez --- .../dockerclient/DockerClientProviderStrategy.java | 9 ++++----- docs/_redirects | 1 + 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index 0d2aa3d08ed..01fb60e4b5d 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -246,12 +246,11 @@ public boolean test(DockerClientProviderStrategy dockerClientProviderStrategy) { .findFirst() .orElseThrow(() -> { log.error( - "Could not find a valid Docker environment. Please check configuration. Attempted configurations were:" + "Could not find a valid Docker environment. Please check configuration. Attempted configurations were:\n" + + configurationFailures.stream().map(it -> "\t" + it).collect(Collectors.joining("\n")) + + "As no valid configuration was found, execution cannot continue.\n" + + "See https://www.testcontainers.org/on_failure.html for more details." ); - for (String failureMessage : configurationFailures) { - log.error(" " + failureMessage); - } - log.error("As no valid configuration was found, execution cannot continue"); FAIL_FAST_ALWAYS.set(true); return new IllegalStateException( diff --git a/docs/_redirects b/docs/_redirects index 99fe10d6900..5918fb853f2 100644 --- a/docs/_redirects +++ b/docs/_redirects @@ -12,6 +12,7 @@ /usage/database_containers.html /modules/databases/ /usage/neo4j_container.html /modules/databases/neo4j/ /compatibility.html /supported_docker_environment/ +/on_failure.html /supported_docker_environment/ # No great 1:1 mapping exists for the following, so redirect to somewhere where at least a sensible sidebar will be shown From 1419cf24eea3e145ab86f4fd17356143a5f9e9d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Thu, 28 Jul 2022 10:26:59 -0500 Subject: [PATCH 2700/5615] Improve Pulsar's wait strategy to rely on clusterName (#5613) `PulsarContainer` allow to override configuration using env vars, using `PULSAR_PREFIX_clusterName" will override the default's value "standalone". The endpoint response should adapt accordingly. --- .../testcontainers/containers/PulsarContainer.java | 8 ++++---- .../containers/PulsarContainerTest.java | 11 +++++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java index 30c36db01d8..10dc35dc4c8 100644 --- a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java +++ b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java @@ -96,13 +96,13 @@ protected void setupCommandAndEnv() { withCommand("/bin/bash", "-c", standaloneBaseCommand); + final String clusterName = getEnvMap().getOrDefault("PULSAR_PREFIX_clusterName", "standalone"); + final String response = String.format("[\"%s\"]", clusterName); + List waitStrategies = new ArrayList<>(); waitStrategies.add(Wait.defaultWaitStrategy()); waitStrategies.add( - Wait - .forHttp(ADMIN_CLUSTERS_ENDPOINT) - .forPort(BROKER_HTTP_PORT) - .forResponsePredicate("[\"standalone\"]"::equals) + Wait.forHttp(ADMIN_CLUSTERS_ENDPOINT).forPort(BROKER_HTTP_PORT).forResponsePredicate(response::equals) ); if (transactionsEnabled) { withEnv("PULSAR_PREFIX_transactionCoordinatorEnabled", "true"); diff --git a/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java b/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java index 29de6c4c872..bf9f3d2201f 100644 --- a/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java +++ b/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java @@ -54,6 +54,17 @@ public void envVarsUsage() throws Exception { } } + @Test + public void customClusterName() throws Exception { + try ( + PulsarContainer pulsar = new PulsarContainer(PULSAR_IMAGE) + .withEnv("PULSAR_PREFIX_clusterName", "tc-cluster"); + ) { + pulsar.start(); + testPulsarFunctionality(pulsar.getPulsarBrokerUrl()); + } + } + @Test public void shouldNotEnableFunctionsWorkerByDefault() throws Exception { try (PulsarContainer pulsar = new PulsarContainer(PULSAR_IMAGE)) { From bac4dfea7994c2f4993a98b02d50104825a6953e Mon Sep 17 00:00:00 2001 From: Henri Tremblay Date: Fri, 29 Jul 2022 06:33:04 -0400 Subject: [PATCH 2701/5615] Make outer maximum startup timeout in DockerComposeContainer configurable (#5588) --- .../containers/DockerComposeContainer.java | 16 ++++++++++++++-- ...DockerComposeContainerWithServicesTest.java | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 86a98586c7f..14124d88c28 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -103,6 +103,8 @@ public class DockerComposeContainer> private final Map waitStrategyMap = new ConcurrentHashMap<>(); + private Duration startupTimeout = Duration.ofMinutes(30); + private final SocatContainer ambassadorContainer = new SocatContainer(); private final Map>> logConsumers = new ConcurrentHashMap<>(); @@ -433,14 +435,14 @@ private String getServiceInstanceName(String serviceName) { /* * can have multiple wait strategies for a single container, e.g. if waiting on several ports * if no wait strategy is defined, the WaitAllStrategy will return immediately. - * The WaitAllStrategy uses an long timeout, because timeouts should be handled by the inner strategies. + * The WaitAllStrategy uses the startup timeout for everything as a global maximum, but we expect timeouts to be handled by the inner strategies. */ private void addWaitStrategy(String serviceInstanceName, @NonNull WaitStrategy waitStrategy) { final WaitAllStrategy waitAllStrategy = waitStrategyMap.computeIfAbsent( serviceInstanceName, __ -> { return new WaitAllStrategy(WaitAllStrategy.Mode.WITH_MAXIMUM_OUTER_TIMEOUT) - .withStartupTimeout(Duration.ofMinutes(30)); + .withStartupTimeout(startupTimeout); } ); waitAllStrategy.withStrategy(waitStrategy); @@ -598,6 +600,16 @@ public SELF withRemoveImages(RemoveImages removeImages) { return self(); } + /** + * Set the maximum startup timeout all the waits set are bounded to. + * + * @return this instance. for chaining + */ + public SELF withStartupTimeout(Duration startupTimeout) { + this.startupTimeout = startupTimeout; + return self(); + } + public Optional getContainerByServiceName(String serviceName) { return Optional.ofNullable(serviceInstanceMap.get(serviceName)); } diff --git a/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java b/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java index 8f056de98fa..305de496cc9 100644 --- a/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java +++ b/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java @@ -1,12 +1,15 @@ package org.testcontainers.containers; import org.junit.Test; +import org.testcontainers.containers.wait.strategy.Wait; import java.io.File; +import java.time.Duration; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; public class DockerComposeContainerWithServicesTest { @@ -87,6 +90,21 @@ public void testScaleInComposeFileIsRespected() { } } + @Test + public void testStartupTimeoutSetsTheHighestTimeout() { + assertThrows("We expect a timeout from the startup timeout", org.rnorth.ducttape.TimeoutException.class, + () -> { + try ( + DockerComposeContainer compose = new DockerComposeContainer<>(SIMPLE_COMPOSE_FILE) + .withServices("redis") + .withStartupTimeout(Duration.ofMillis(1)) + .withExposedService("redis", 80, Wait.forListeningPort().withStartupTimeout(Duration.ofMinutes(1))); + ) { + compose.start(); + } + }); + } + private void verifyStartedContainers(final DockerComposeContainer compose, final String... names) { final List containerNames = compose .listChildContainers() From 0b374b391d4aaa4ccd9c723b41e8bac9a55a5d4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Fri, 29 Jul 2022 16:19:02 -0500 Subject: [PATCH 2702/5615] Fix format --- .../DockerComposeContainerWithServicesTest.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java b/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java index 305de496cc9..b7b820a3011 100644 --- a/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java +++ b/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java @@ -9,8 +9,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; public class DockerComposeContainerWithServicesTest { @@ -92,17 +92,24 @@ public void testScaleInComposeFileIsRespected() { @Test public void testStartupTimeoutSetsTheHighestTimeout() { - assertThrows("We expect a timeout from the startup timeout", org.rnorth.ducttape.TimeoutException.class, + assertThrows( + "We expect a timeout from the startup timeout", + org.rnorth.ducttape.TimeoutException.class, () -> { try ( DockerComposeContainer compose = new DockerComposeContainer<>(SIMPLE_COMPOSE_FILE) .withServices("redis") .withStartupTimeout(Duration.ofMillis(1)) - .withExposedService("redis", 80, Wait.forListeningPort().withStartupTimeout(Duration.ofMinutes(1))); + .withExposedService( + "redis", + 80, + Wait.forListeningPort().withStartupTimeout(Duration.ofMinutes(1)) + ); ) { compose.start(); } - }); + } + ); } private void verifyStartedContainers(final DockerComposeContainer compose, final String... names) { From edee92250a03f54d3e2908de3589b0b7efd754eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:05:48 +0000 Subject: [PATCH 2703/5615] Bump kafka-clients from 3.2.0 to 3.2.1 in /modules/kafka Bumps kafka-clients from 3.2.0 to 3.2.1. --- updated-dependencies: - dependency-name: org.apache.kafka:kafka-clients dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index 79264b8207f..ebd9c84120c 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Kafka" dependencies { api project(':testcontainers') - testImplementation 'org.apache.kafka:kafka-clients:3.2.0' + testImplementation 'org.apache.kafka:kafka-clients:3.2.1' testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'com.google.guava:guava:23.0' } From 8b2d5d17f503e44b2713267648938942e086b108 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:05:51 +0000 Subject: [PATCH 2704/5615] Bump tomcat-jdbc from 10.0.22 to 10.0.23 in /modules/jdbc Bumps tomcat-jdbc from 10.0.22 to 10.0.23. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 012e9f410ec..3a6681aee13 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -7,7 +7,7 @@ dependencies { compileOnly 'org.jetbrains:annotations:23.0.0' testImplementation 'commons-dbutils:commons-dbutils:1.7' testImplementation 'org.vibur:vibur-dbcp:25.0' - testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.22' + testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.23' testImplementation 'com.zaxxer:HikariCP-java6:2.3.13' testImplementation 'com.googlecode.junit-toolbox:junit-toolbox:2.4' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From c83590275d4342eb10345e9a9451f3cb54c920a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:06:01 +0000 Subject: [PATCH 2705/5615] Bump neo4j-java-driver from 4.4.6 to 4.4.9 in /modules/neo4j Bumps [neo4j-java-driver](https://github.com/neo4j/neo4j-java-driver) from 4.4.6 to 4.4.9. - [Release notes](https://github.com/neo4j/neo4j-java-driver/releases) - [Commits](https://github.com/neo4j/neo4j-java-driver/compare/4.4.6...4.4.9) --- updated-dependencies: - dependency-name: org.neo4j.driver:neo4j-java-driver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 355dbe3f4ee..de42bca2d5d 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -33,6 +33,6 @@ dependencies { api project(":testcontainers") - testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.6' + testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.9' testImplementation 'org.assertj:assertj-core:3.23.1' } From 0d1ebaee2c19c3a2a2bc3538099b94072838ff5d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:06:06 +0000 Subject: [PATCH 2706/5615] Bump java-client from 3.3.1 to 3.3.2 in /modules/couchbase Bumps [java-client](https://github.com/couchbase/couchbase-jvm-clients) from 3.3.1 to 3.3.2. - [Release notes](https://github.com/couchbase/couchbase-jvm-clients/releases) - [Commits](https://github.com/couchbase/couchbase-jvm-clients/compare/java-client-3.3.1...java-client-3.3.2) --- updated-dependencies: - dependency-name: com.couchbase.client:java-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index df7f4cbc221..e59e346c29f 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -5,6 +5,6 @@ dependencies { // TODO use JDK's HTTP client and/or Apache HttpClient5 shaded 'com.squareup.okhttp3:okhttp:3.14.9' - testImplementation 'com.couchbase.client:java-client:3.3.1' + testImplementation 'com.couchbase.client:java-client:3.3.2' testImplementation 'org.awaitility:awaitility:4.2.0' } From 699938b9a3f4675adc07452d368ef504b1fa2dd8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:06:07 +0000 Subject: [PATCH 2707/5615] Bump mysql-connector-java from 8.0.29 to 8.0.30 in /modules/jdbc-test Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.29 to 8.0.30. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.29...8.0.30) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 3649396773d..a3c7dfdeefe 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -16,5 +16,5 @@ dependencies { api 'org.apache.tomcat:tomcat-jdbc:10.0.22' api 'org.vibur:vibur-dbcp:25.0' - api 'mysql:mysql-connector-java:8.0.29' + api 'mysql:mysql-connector-java:8.0.30' } From 26386e6ef2f41e0c403e24c2dcf4d87848c06d25 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:06:08 +0000 Subject: [PATCH 2708/5615] Bump tomcat-jdbc from 10.0.22 to 10.0.23 in /modules/jdbc-test Bumps tomcat-jdbc from 10.0.22 to 10.0.23. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 3649396773d..038cc110a1b 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -14,7 +14,7 @@ dependencies { exclude(group: "net.java.dev.jna", module: "jna") } - api 'org.apache.tomcat:tomcat-jdbc:10.0.22' + api 'org.apache.tomcat:tomcat-jdbc:10.0.23' api 'org.vibur:vibur-dbcp:25.0' api 'mysql:mysql-connector-java:8.0.29' } From 463a5c5d48b55f882f29b046bb6e5e5ecc5ae0b7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:06:12 +0000 Subject: [PATCH 2709/5615] Bump aws-java-sdk-dynamodb in /modules/dynalite Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.12.253 to 1.12.273. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.253...1.12.273) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-dynamodb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/dynalite/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index acf379cda79..5b9ff919da7 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Dynalite" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.253' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.273' testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.253' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From 85cc40b057c9c2e740180ce92dfb980c5430c9de Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:06:20 +0000 Subject: [PATCH 2710/5615] Bump mysql-connector-java from 8.0.29 to 8.0.30 in /modules/mysql Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.29 to 8.0.30. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.29...8.0.30) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index e23920eaae9..87887384973 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' testImplementation project(':jdbc-test') - testImplementation 'mysql:mysql-connector-java:8.0.29' + testImplementation 'mysql:mysql-connector-java:8.0.30' testImplementation testFixtures(project(':r2dbc')) testImplementation 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' From 33f08921d5996d71f41b353d5e5cc8b28494f00a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:07:10 +0000 Subject: [PATCH 2711/5615] Bump mysql-connector-java from 8.0.29 to 8.0.30 in /modules/spock Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.29 to 8.0.30. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.29...8.0.30) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index a9e3b7d09b1..3eadd33ea2a 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -16,7 +16,7 @@ dependencies { testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' testRuntimeOnly 'org.postgresql:postgresql:42.4.0' - testRuntimeOnly 'mysql:mysql-connector-java:8.0.29' + testRuntimeOnly 'mysql:mysql-connector-java:8.0.30' testCompileClasspath 'org.jetbrains:annotations:23.0.0' } From c43589ffb3ac30e564885df29168234ced5c6273 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:07:11 +0000 Subject: [PATCH 2712/5615] Bump elasticsearch-rest-client in /modules/elasticsearch Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 8.3.1 to 8.3.3. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Changelog](https://github.com/elastic/elasticsearch/blob/main/CHANGELOG.md) - [Commits](https://github.com/elastic/elasticsearch/compare/v8.3.1...v8.3.3) --- updated-dependencies: - dependency-name: org.elasticsearch.client:elasticsearch-rest-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index a4a277596fc..a3a47d11fab 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,7 +2,7 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') - testImplementation "org.elasticsearch.client:elasticsearch-rest-client:8.3.1" + testImplementation "org.elasticsearch.client:elasticsearch-rest-client:8.3.3" testImplementation "org.elasticsearch.client:transport:7.17.5" testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 17526d7085dc1044aad98c801df407d99b5d196d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:07:16 +0000 Subject: [PATCH 2713/5615] Bump junit-jupiter-api from 5.8.2 to 5.9.0 in /modules/junit-jupiter Bumps [junit-jupiter-api](https://github.com/junit-team/junit5) from 5.8.2 to 5.9.0. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.8.2...r5.9.0) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-api dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 73a8897800c..167529f9fea 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -2,7 +2,7 @@ description = "Testcontainers :: JUnit Jupiter Extension" dependencies { api project(':testcontainers') - api 'org.junit.jupiter:junit-jupiter-api:5.8.2' + api 'org.junit.jupiter:junit-jupiter-api:5.9.0' testImplementation project(':mysql') testImplementation project(':postgresql') From cc10355394942be97fc22aac1c882e88386f7ccf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:07:21 +0000 Subject: [PATCH 2714/5615] Bump junit-jupiter-params from 5.8.2 to 5.9.0 in /modules/junit-jupiter Bumps [junit-jupiter-params](https://github.com/junit-team/junit5) from 5.8.2 to 5.9.0. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.8.2...r5.9.0) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-params dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 73a8897800c..a7e23a2f65c 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -13,7 +13,7 @@ dependencies { exclude(module: 'hamcrest-core') } testImplementation 'org.assertj:assertj-core:3.23.1' - testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2' + testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.0' testRuntimeOnly 'org.postgresql:postgresql:42.4.0' testRuntimeOnly 'mysql:mysql-connector-java:8.0.29' From b9d49464dd9e0dc60643d38a11e4eeae478b9a31 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:07:30 +0000 Subject: [PATCH 2715/5615] Bump junit-jupiter-engine from 5.8.2 to 5.9.0 in /modules/junit-jupiter Bumps [junit-jupiter-engine](https://github.com/junit-team/junit5) from 5.8.2 to 5.9.0. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.8.2...r5.9.0) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-engine dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 73a8897800c..3dd6c7dd25f 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -17,7 +17,7 @@ dependencies { testRuntimeOnly 'org.postgresql:postgresql:42.4.0' testRuntimeOnly 'mysql:mysql-connector-java:8.0.29' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0' } test { From 8dfe6c5aaea81e6c7794acac22168ef874e9e826 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:08:21 +0000 Subject: [PATCH 2716/5615] Bump actions/cache from 3.0.4 to 3.0.5 Bumps [actions/cache](https://github.com/actions/cache) from 3.0.4 to 3.0.5. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3.0.4...v3.0.5) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index f24fd6f6916..9fa1f78e47b 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -25,7 +25,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.4 + uses: actions/cache@v3.0.5 continue-on-error: true with: path: ~/.gradle/caches @@ -53,7 +53,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.4 + uses: actions/cache@v3.0.5 continue-on-error: true with: path: ~/.gradle/caches diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3287499c2fc..55432b3e2c6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.4 + uses: actions/cache@v3.0.5 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-home-testmatrix-${{ hashFiles('**/*.gradle') }} @@ -59,7 +59,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.4 + uses: actions/cache@v3.0.5 continue-on-error: true with: path: ~/.gradle/caches From b687658cf70d842de21c000b5da409cf270cb1a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:08:29 +0000 Subject: [PATCH 2717/5615] Bump influxdb-java from 2.22 to 2.23 in /modules/influxdb Bumps [influxdb-java](https://github.com/influxdata/influxdb-java) from 2.22 to 2.23. - [Release notes](https://github.com/influxdata/influxdb-java/releases) - [Changelog](https://github.com/influxdata/influxdb-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/influxdata/influxdb-java/compare/influxdb-java-2.22...influxdb-java-2.23) --- updated-dependencies: - dependency-name: org.influxdb:influxdb-java dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/influxdb/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/influxdb/build.gradle b/modules/influxdb/build.gradle index 7a028d4a19d..a7ff4e66729 100644 --- a/modules/influxdb/build.gradle +++ b/modules/influxdb/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: InfluxDB" dependencies { api project(':testcontainers') - compileOnly 'org.influxdb:influxdb-java:2.22' - testImplementation 'org.influxdb:influxdb-java:2.21' + compileOnly 'org.influxdb:influxdb-java:2.23' + testImplementation 'org.influxdb:influxdb-java:2.23' } From 5cb65abe0014e2ef0a1cb7245589e789760e7f49 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:08:39 +0000 Subject: [PATCH 2718/5615] Bump s3 from 2.17.224 to 2.17.244 in /modules/localstack Bumps s3 from 2.17.224 to 2.17.244. --- updated-dependencies: - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 56776965f8a..9a29eb0e343 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.231' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.253' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.253' - testImplementation 'software.amazon.awssdk:s3:2.17.224' + testImplementation 'software.amazon.awssdk:s3:2.17.244' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 1cf31e61d347f0e95a13729f094b719c193bf43e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:09:23 +0000 Subject: [PATCH 2719/5615] Bump neo4j-java-driver from 4.4.6 to 4.4.9 in /examples Bumps [neo4j-java-driver](https://github.com/neo4j/neo4j-java-driver) from 4.4.6 to 4.4.9. - [Release notes](https://github.com/neo4j/neo4j-java-driver/releases) - [Commits](https://github.com/neo4j/neo4j-java-driver/compare/4.4.6...4.4.9) --- updated-dependencies: - dependency-name: org.neo4j.driver:neo4j-java-driver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/neo4j-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/neo4j-container/build.gradle b/examples/neo4j-container/build.gradle index e937bd2954b..e7e42ff00bd 100644 --- a/examples/neo4j-container/build.gradle +++ b/examples/neo4j-container/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { testImplementation 'org.assertj:assertj-core:3.23.1' - testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.6' + testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.9' testImplementation 'org.testcontainers:neo4j' testImplementation 'org.testcontainers:junit-jupiter' } From 95e917f31b8152fb84a2ebcf1dd5ef2f4e720785 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:09:23 +0000 Subject: [PATCH 2720/5615] Bump com.gradle.enterprise.gradle.plugin in /examples Bumps com.gradle.enterprise.gradle.plugin from 3.10.2 to 3.10.3. --- updated-dependencies: - dependency-name: com.gradle.enterprise:com.gradle.enterprise.gradle.plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/settings.gradle b/examples/settings.gradle index 5a4ae8c0d2e..cfb204f4b00 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.10.0" - classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10.2" + classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10.3" classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7.2" } } From 6436b1019cec2a27c029ecc28b8b53044c9e8dce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:09:25 +0000 Subject: [PATCH 2721/5615] Bump kafka-clients from 3.2.0 to 3.2.1 in /examples Bumps kafka-clients from 3.2.0 to 3.2.1. --- updated-dependencies: - dependency-name: org.apache.kafka:kafka-clients dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/kafka-cluster/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index d69af5ceb68..c3a79d4058f 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -10,7 +10,7 @@ dependencies { testCompileOnly "org.projectlombok:lombok:1.18.24" testAnnotationProcessor "org.projectlombok:lombok:1.18.24" testImplementation 'org.testcontainers:kafka' - testImplementation 'org.apache.kafka:kafka-clients:3.2.0' + testImplementation 'org.apache.kafka:kafka-clients:3.2.1' testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'com.google.guava:guava:23.0' testImplementation 'ch.qos.logback:logback-classic:1.2.11' From 6fcaeefcbf1bdeb3cfb5b7ec07255e3fba39580e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:09:29 +0000 Subject: [PATCH 2722/5615] Bump cucumber-junit from 7.4.1 to 7.5.0 in /examples Bumps [cucumber-junit](https://github.com/cucumber/cucumber-jvm) from 7.4.1 to 7.5.0. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.4.1...v7.5.0) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-junit dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 1bf4e1f72cb..5cf03f419fe 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' testImplementation 'io.cucumber:cucumber-java:7.4.1' - testImplementation 'io.cucumber:cucumber-junit:7.4.1' + testImplementation 'io.cucumber:cucumber-junit:7.5.0' testImplementation 'org.testcontainers:selenium' } From eb766900d2c12168e17e9af2fc9d667e49040207 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:09:39 +0000 Subject: [PATCH 2723/5615] Bump gson from 2.9.0 to 2.9.1 in /examples Bumps [gson](https://github.com/google/gson) from 2.9.0 to 2.9.1. - [Release notes](https://github.com/google/gson/releases) - [Changelog](https://github.com/google/gson/blob/master/CHANGELOG.md) - [Commits](https://github.com/google/gson/compare/gson-parent-2.9.0...gson-parent-2.9.1) --- updated-dependencies: - dependency-name: com.google.code.gson:gson dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/disque-job-queue/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index 1dc3497ec42..47bf340e2eb 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly "org.projectlombok:lombok:1.18.24" annotationProcessor "org.projectlombok:lombok:1.18.24" implementation 'biz.paluch.redis:spinach:0.3' - implementation 'com.google.code.gson:gson:2.9.0' + implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.mockito:mockito-all:1.10.19' diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 0c4de18f3c6..1e09fbc84f0 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -9,7 +9,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.36' implementation 'redis.clients:jedis:4.2.3' - implementation 'com.google.code.gson:gson:2.9.0' + implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'ch.qos.logback:logback-classic:1.2.11' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 6e9d7664384..94f445f5ffe 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -9,7 +9,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.36' implementation 'redis.clients:jedis:4.2.3' - implementation 'com.google.code.gson:gson:2.9.0' + implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'junit:junit:4.13.2' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 7ece47a44ea..fd603d515c7 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -9,7 +9,7 @@ repositories { dependencies { implementation 'redis.clients:jedis:4.2.3' - implementation 'com.google.code.gson:gson:2.9.0' + implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.36' From 57159ae6ede175c641e3afc5b7c8cd59ae15c031 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:10:19 +0000 Subject: [PATCH 2724/5615] Bump org.jetbrains.kotlin.plugin.spring in /examples Bumps [org.jetbrains.kotlin.plugin.spring](https://github.com/JetBrains/kotlin) from 1.7.0 to 1.7.10. - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/v1.7.10/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.7.0...v1.7.10) --- updated-dependencies: - dependency-name: org.jetbrains.kotlin.plugin.spring dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 90338ff9959..4a48c40d8bf 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,7 +1,7 @@ plugins { id("org.springframework.boot") version "2.6.4" id("org.jetbrains.kotlin.jvm") version "1.7.0" - id("org.jetbrains.kotlin.plugin.spring") version "1.7.0" + id("org.jetbrains.kotlin.plugin.spring") version "1.7.10" } apply plugin: 'io.spring.dependency-management' From 9b8d2391a3eb9f30f38d1be47eadf8b3081f528d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:11:31 +0000 Subject: [PATCH 2725/5615] Bump trino-jdbc from 388 to 391 in /modules/trino Bumps trino-jdbc from 388 to 391. --- updated-dependencies: - dependency-name: io.trino:trino-jdbc dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/trino/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/trino/build.gradle b/modules/trino/build.gradle index caf1f322c12..c25007bb223 100644 --- a/modules/trino/build.gradle +++ b/modules/trino/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'io.trino:trino-jdbc:388' + testImplementation 'io.trino:trino-jdbc:391' compileOnly 'org.jetbrains:annotations:23.0.0' } From 89283187e0b3a047a335da9d53399ba7a6c8c5e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:11:50 +0000 Subject: [PATCH 2726/5615] Bump mongodb-driver-sync from 4.6.1 to 4.7.1 in /modules/mongodb Bumps [mongodb-driver-sync](https://github.com/mongodb/mongo-java-driver) from 4.6.1 to 4.7.1. - [Release notes](https://github.com/mongodb/mongo-java-driver/releases) - [Commits](https://github.com/mongodb/mongo-java-driver/compare/r4.6.1...r4.7.1) --- updated-dependencies: - dependency-name: org.mongodb:mongodb-driver-sync dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/mongodb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mongodb/build.gradle b/modules/mongodb/build.gradle index 297bcd21f3b..7f9725bf0e8 100644 --- a/modules/mongodb/build.gradle +++ b/modules/mongodb/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: MongoDB" dependencies { api project(':testcontainers') - testImplementation("org.mongodb:mongodb-driver-sync:4.6.1") + testImplementation("org.mongodb:mongodb-driver-sync:4.7.1") } From bfd82f4536195abe12dde5ee4ca2c617a923603d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:11:58 +0000 Subject: [PATCH 2727/5615] Bump reactor-core from 3.4.19 to 3.4.21 in /modules/r2dbc Bumps [reactor-core](https://github.com/reactor/reactor-core) from 3.4.19 to 3.4.21. - [Release notes](https://github.com/reactor/reactor-core/releases) - [Commits](https://github.com/reactor/reactor-core/compare/v3.4.19...v3.4.21) --- updated-dependencies: - dependency-name: io.projectreactor:reactor-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/r2dbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle index 692b5ac9ce5..f51a13b9296 100644 --- a/modules/r2dbc/build.gradle +++ b/modules/r2dbc/build.gradle @@ -15,6 +15,6 @@ dependencies { testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.12.RELEASE' testImplementation project(':postgresql') - testFixturesImplementation 'io.projectreactor:reactor-core:3.4.19' + testFixturesImplementation 'io.projectreactor:reactor-core:3.4.21' testFixturesImplementation 'org.assertj:assertj-core:3.23.1' } From b303688bdedcd84e8f660993591dadcc9ea5e338 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:12:07 +0000 Subject: [PATCH 2728/5615] Bump azure-cosmos from 4.32.1 to 4.33.1 in /modules/azure Bumps [azure-cosmos](https://github.com/Azure/azure-sdk-for-java) from 4.32.1 to 4.33.1. - [Release notes](https://github.com/Azure/azure-sdk-for-java/releases) - [Commits](https://github.com/Azure/azure-sdk-for-java/compare/azure-cosmos_4.32.1...azure-cosmos_4.33.1) --- updated-dependencies: - dependency-name: com.azure:azure-cosmos dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/azure/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle index 5f8c6837d79..c6707401ff6 100644 --- a/modules/azure/build.gradle +++ b/modules/azure/build.gradle @@ -6,5 +6,5 @@ dependencies { shaded 'com.squareup.okhttp3:okhttp:3.14.9' testImplementation 'org.assertj:assertj-core:3.23.1' - testImplementation 'com.azure:azure-cosmos:4.32.1' + testImplementation 'com.azure:azure-cosmos:4.33.1' } From 9aeb0c78803af4ef061d81c3bccbc44ddb83756d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:12:15 +0000 Subject: [PATCH 2729/5615] Bump junit-jupiter-engine from 5.8.2 to 5.9.0 in /modules/hivemq Bumps [junit-jupiter-engine](https://github.com/junit-team/junit5) from 5.8.2 to 5.9.0. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.8.2...r5.9.0) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-engine dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index aa566381d0c..b62121ce9cc 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -17,7 +17,7 @@ dependencies { testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") testImplementation("org.apache.httpcomponents:httpclient:4.5.13") testImplementation("ch.qos.logback:logback-classic:1.2.11") - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.0") } test { From bb1769c468c9dd5ca88abd4a70bca913e60f0f9b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:12:21 +0000 Subject: [PATCH 2730/5615] Bump junit-jupiter-api from 5.8.2 to 5.9.0 in /modules/hivemq Bumps [junit-jupiter-api](https://github.com/junit-team/junit5) from 5.8.2 to 5.9.0. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.8.2...r5.9.0) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-api dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index aa566381d0c..a30a7d30517 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -11,7 +11,7 @@ dependencies { shaded("org.jboss.shrinkwrap:shrinkwrap-impl-base:1.2.6") shaded("net.lingala.zip4j:zip4j:2.10.0") - testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") + testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.0") testImplementation(project(":junit-jupiter")) testImplementation("com.hivemq:hivemq-extension-sdk:4.8.2") testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") From 909404b9be70970673c6e592aec72041fff9d49a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:13:32 +0000 Subject: [PATCH 2731/5615] Bump com.gradle.enterprise.gradle.plugin from 3.10.2 to 3.10.3 Bumps com.gradle.enterprise.gradle.plugin from 3.10.2 to 3.10.3. --- updated-dependencies: - dependency-name: com.gradle.enterprise:com.gradle.enterprise.gradle.plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index d4605c2ad84..b56048c1126 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,7 +5,7 @@ buildscript { } } dependencies { - classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10.2" + classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10.3" classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7.2" } } From 456bdcc6a22099e743d98459ce262a0a86b98ab1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:14:19 +0000 Subject: [PATCH 2732/5615] Bump google-cloud-datastore from 2.10.0 to 2.10.1 in /modules/gcloud Bumps [google-cloud-datastore](https://github.com/googleapis/java-datastore) from 2.10.0 to 2.10.1. - [Release notes](https://github.com/googleapis/java-datastore/releases) - [Changelog](https://github.com/googleapis/java-datastore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-datastore/compare/v2.10.0...v2.10.1) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-datastore dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 2cabbc4cf30..d885dc11023 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: GCloud" dependencies { api project(':testcontainers') - testImplementation 'com.google.cloud:google-cloud-datastore:2.10.0' + testImplementation 'com.google.cloud:google-cloud-datastore:2.10.1' testImplementation 'com.google.cloud:google-cloud-firestore:3.2.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.0' testImplementation 'com.google.cloud:google-cloud-spanner:6.25.7' From 7499443fae265f702fd15a718de25c63fbdd8d07 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:14:34 +0000 Subject: [PATCH 2733/5615] Bump google-cloud-pubsub from 1.120.0 to 1.120.6 in /modules/gcloud Bumps [google-cloud-pubsub](https://github.com/googleapis/java-pubsub) from 1.120.0 to 1.120.6. - [Release notes](https://github.com/googleapis/java-pubsub/releases) - [Changelog](https://github.com/googleapis/java-pubsub/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-pubsub/compare/v1.120.0...v1.120.6) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-pubsub dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 2cabbc4cf30..9c837ff5e0e 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -5,7 +5,7 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-datastore:2.10.0' testImplementation 'com.google.cloud:google-cloud-firestore:3.2.0' - testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.0' + testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.6' testImplementation 'com.google.cloud:google-cloud-spanner:6.25.7' testImplementation 'com.google.cloud:google-cloud-bigtable:2.9.0' testImplementation 'org.assertj:assertj-core:3.23.1' From 720024f1f9244b766165782ce1c1889ce35e13c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Aug 2022 10:30:50 +0000 Subject: [PATCH 2734/5615] Bump mysql-connector-java in /modules/junit-jupiter Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.29 to 8.0.30. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.29...8.0.30) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 5bf5aa4557a..790aa394849 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -16,7 +16,7 @@ dependencies { testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2' testRuntimeOnly 'org.postgresql:postgresql:42.4.0' - testRuntimeOnly 'mysql:mysql-connector-java:8.0.29' + testRuntimeOnly 'mysql:mysql-connector-java:8.0.30' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0' } From 521eb8d8a746531415d5043cf0a02e4ad697717f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Aug 2022 10:31:01 +0000 Subject: [PATCH 2735/5615] Bump aws-java-sdk-sqs from 1.12.253 to 1.12.273 in /modules/localstack Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.12.253 to 1.12.273. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.253...1.12.273) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-sqs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 9a29eb0e343..d754c676320 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.253' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.231' - testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.253' + testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.273' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.253' testImplementation 'software.amazon.awssdk:s3:2.17.244' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From 04842f1559b4f8e073ac430d7b73e9bc496f44a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Aug 2022 10:31:14 +0000 Subject: [PATCH 2736/5615] Bump org.jetbrains.kotlin.jvm from 1.7.0 to 1.7.10 in /examples Bumps [org.jetbrains.kotlin.jvm](https://github.com/JetBrains/kotlin) from 1.7.0 to 1.7.10. - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/v1.7.10/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.7.0...v1.7.10) --- updated-dependencies: - dependency-name: org.jetbrains.kotlin.jvm dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 4a48c40d8bf..383491eb32d 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,6 +1,6 @@ plugins { id("org.springframework.boot") version "2.6.4" - id("org.jetbrains.kotlin.jvm") version "1.7.0" + id("org.jetbrains.kotlin.jvm") version "1.7.10" id("org.jetbrains.kotlin.plugin.spring") version "1.7.10" } From 6805a75ce7747fc09bca48cb27f8b83c07146abb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Aug 2022 10:31:20 +0000 Subject: [PATCH 2737/5615] Bump google-cloud-spanner from 6.25.7 to 6.27.0 in /modules/gcloud Bumps [google-cloud-spanner](https://github.com/googleapis/java-spanner) from 6.25.7 to 6.27.0. - [Release notes](https://github.com/googleapis/java-spanner/releases) - [Changelog](https://github.com/googleapis/java-spanner/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-spanner/compare/v6.25.7...v6.27.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-spanner dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 395ef1372bf..2da6136c67b 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -6,7 +6,7 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-datastore:2.10.1' testImplementation 'com.google.cloud:google-cloud-firestore:3.2.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.6' - testImplementation 'com.google.cloud:google-cloud-spanner:6.25.7' + testImplementation 'com.google.cloud:google-cloud-spanner:6.27.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.9.0' testImplementation 'org.assertj:assertj-core:3.23.1' } From ada9da62207c4ffa0d04804cc6a04249d412bb2d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Aug 2022 10:41:10 +0000 Subject: [PATCH 2738/5615] Bump cucumber-java from 7.4.1 to 7.5.0 in /examples Bumps [cucumber-java](https://github.com/cucumber/cucumber-jvm) from 7.4.1 to 7.5.0. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.4.1...v7.5.0) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-java dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 5cf03f419fe..ba51bbe0d2c 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' - testImplementation 'io.cucumber:cucumber-java:7.4.1' + testImplementation 'io.cucumber:cucumber-java:7.5.0' testImplementation 'io.cucumber:cucumber-junit:7.5.0' testImplementation 'org.testcontainers:selenium' } From 485a743aecc0a65733d0dc2bbbb45727cf11810a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Aug 2022 15:00:47 +0000 Subject: [PATCH 2739/5615] Bump aws-java-sdk-logs from 1.12.253 to 1.12.273 in /modules/localstack Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.12.253 to 1.12.273. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.253...1.12.273) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-logs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index d754c676320..2c547febfb8 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,7 +6,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.253' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.231' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.273' - testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.253' + testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.273' testImplementation 'software.amazon.awssdk:s3:2.17.244' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 87265750993592ebe617be1ec885ed2e9d0580a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Aug 2022 15:00:48 +0000 Subject: [PATCH 2740/5615] Bump google-cloud-firestore from 3.2.0 to 3.3.0 in /modules/gcloud Bumps [google-cloud-firestore](https://github.com/googleapis/java-firestore) from 3.2.0 to 3.3.0. - [Release notes](https://github.com/googleapis/java-firestore/releases) - [Changelog](https://github.com/googleapis/java-firestore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-firestore/compare/v3.2.0...v3.3.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-firestore dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 2da6136c67b..eeca4d49fde 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':testcontainers') testImplementation 'com.google.cloud:google-cloud-datastore:2.10.1' - testImplementation 'com.google.cloud:google-cloud-firestore:3.2.0' + testImplementation 'com.google.cloud:google-cloud-firestore:3.3.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.6' testImplementation 'com.google.cloud:google-cloud-spanner:6.27.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.9.0' From 290818fdb13baf7d13cd9364d470ce90852c8f67 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Aug 2022 15:00:51 +0000 Subject: [PATCH 2741/5615] Bump aws-java-sdk-s3 from 1.12.253 to 1.12.273 in /modules/localstack Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.253 to 1.12.273. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.253...1.12.273) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index d754c676320..0ff951ad1b9 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Localstack" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.253' - testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.231' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.273' + testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.273' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.273' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.253' testImplementation 'software.amazon.awssdk:s3:2.17.244' From 1518bebe30d028a2d69014f080d4e61221aa9caa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Aug 2022 15:00:52 +0000 Subject: [PATCH 2742/5615] Bump google-cloud-bigtable from 2.9.0 to 2.10.1 in /modules/gcloud Bumps [google-cloud-bigtable](https://github.com/googleapis/java-bigtable) from 2.9.0 to 2.10.1. - [Release notes](https://github.com/googleapis/java-bigtable/releases) - [Changelog](https://github.com/googleapis/java-bigtable/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-bigtable/compare/v2.9.0...v2.10.1) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-bigtable dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 2da6136c67b..b96e9e5fd94 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-firestore:3.2.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.6' testImplementation 'com.google.cloud:google-cloud-spanner:6.27.0' - testImplementation 'com.google.cloud:google-cloud-bigtable:2.9.0' + testImplementation 'com.google.cloud:google-cloud-bigtable:2.10.1' testImplementation 'org.assertj:assertj-core:3.23.1' } From 7c3c00e02e8d93201b5934cd2a2803f530de404a Mon Sep 17 00:00:00 2001 From: Kishan B Date: Wed, 3 Aug 2022 21:22:33 +0530 Subject: [PATCH 2743/5615] Add syntax highlighting to colima usage doc (#5673) --- docs/supported_docker_environment/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/supported_docker_environment/index.md b/docs/supported_docker_environment/index.md index aa6c7b1b867..debca47e574 100644 --- a/docs/supported_docker_environment/index.md +++ b/docs/supported_docker_environment/index.md @@ -17,7 +17,7 @@ In order to run testcontainers against [colima](https://github.com/abiosoft/colima) the env vars bellow should be set -``` +```bash export TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=/var/run/docker.sock export DOCKER_HOST="unix://${HOME}/.colima/docker.sock" ``` From c38ba11e9bd018edcd2896e234cc277552ab2c7d Mon Sep 17 00:00:00 2001 From: Erik Merkle Date: Thu, 4 Aug 2022 07:37:49 -0500 Subject: [PATCH 2744/5615] Update Cassandra driver to 4.x (#2830) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also, deprecate Driver 3.x methods and remove some driver coupling. Co-authored-by: Kevin Wittek Co-authored-by: Eddú Meléndez --- docs/modules/databases/cassandra.md | 35 +++--------------- modules/cassandra/build.gradle | 9 ++++- .../containers/CassandraContainer.java | 34 +++++++++++++++++ .../containers/CassandraContainerTest.java | 13 +++---- .../containers/CassandraDriver3Test.java | 37 +++++++++++++++++++ .../containers/CassandraDriver4Test.java | 35 ++++++++++++++++++ 6 files changed, 126 insertions(+), 37 deletions(-) create mode 100644 modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver3Test.java create mode 100644 modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver4Test.java diff --git a/docs/modules/databases/cassandra.md b/docs/modules/databases/cassandra.md index f7ed3094886..73f93bf203a 100644 --- a/docs/modules/databases/cassandra.md +++ b/docs/modules/databases/cassandra.md @@ -4,35 +4,12 @@ This example connects to the Cassandra Cluster, creates a keyspaces and asserts that is has been created. -=== "JUnit 4 example" - ```java - public class SomeTest { - - @Rule - public CassandraContainer cassandra = new CassandraContainer(); - - - @Test - public void test(){ - Cluster cluster = cassandra.getCluster(); - - try(Session session = cluster.connect()) { - - session.execute("CREATE KEYSPACE IF NOT EXISTS test WITH replication = \n" + - "{'class':'SimpleStrategy','replication_factor':'1'};"); - - List keyspaces = session.getCluster().getMetadata().getKeyspaces(); - List filteredKeyspaces = keyspaces - .stream() - .filter(km -> km.getName().equals("test")) - .collect(Collectors.toList()); - - assertEquals(1, filteredKeyspaces.size()); - } - } - - } - ``` + +[Building CqlSession](../../../modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver3Test.java) inside_block:cassandra + + +!!! warning + All methods returning instances of the Cassandra Driver's Cluster object in `CassandraContainer` have been deprecated. Providing these methods unnecessarily couples the Container to the Driver and creates potential breaking changes if the driver is updated. ## Adding this module to your project dependencies diff --git a/modules/cassandra/build.gradle b/modules/cassandra/build.gradle index a5427aaea07..b00740d09fd 100644 --- a/modules/cassandra/build.gradle +++ b/modules/cassandra/build.gradle @@ -1,6 +1,13 @@ description = "TestContainers :: Cassandra" +configurations.all { + resolutionStrategy { + force 'io.dropwizard.metrics:metrics-core:3.2.4' + } +} + dependencies { api project(":database-commons") - api "com.datastax.cassandra:cassandra-driver-core:3.7.1" + api "com.datastax.cassandra:cassandra-driver-core:3.10.0" + testImplementation 'com.datastax.oss:java-driver-core:4.8.0' } diff --git a/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java b/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java index 3c78743fb62..a12ac03f18d 100644 --- a/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java +++ b/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java @@ -11,6 +11,7 @@ import org.testcontainers.utility.MountableFile; import java.io.IOException; +import java.net.InetSocketAddress; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Optional; @@ -35,6 +36,8 @@ public class CassandraContainer> extends G public static final Integer CQL_PORT = 9042; + private static final String DEFAULT_LOCAL_DATACENTER = "datacenter1"; + private static final String CONTAINER_CONFIG_LOCATION = "/etc/cassandra"; private static final String USERNAME = "cassandra"; @@ -70,6 +73,8 @@ public CassandraContainer(DockerImageName dockerImageName) { withEnv("JVM_OPTS", "-Dcassandra.skip_wait_for_gossip_to_settle=0 -Dcassandra.initial_token=0"); withEnv("HEAP_NEWSIZE", "128M"); withEnv("MAX_HEAP_SIZE", "1024M"); + withEnv("CASSANDRA_ENDPOINT_SNITCH", "GossipingPropertyFileSnitch"); + withEnv("CASSANDRA_DC", DEFAULT_LOCAL_DATACENTER); } @Override @@ -187,11 +192,20 @@ public String getPassword() { * Get configured Cluster * * Can be used to obtain connections to Cassandra in the container + * + * @deprecated For Cassandra driver 3.x, use {@link #getHost()} and {@link #getMappedPort(int)} with + * the driver's {@link Cluster#builder() Cluster.Builder} {@code addContactPoint(String)} and + * {@code withPort(int)} methods to create a Cluster object. For Cassandra driver 4.x, use + * {@link #getContactPoint()} and {@link #getLocalDatacenter()} with the driver's {@code CqlSession.builder()} + * {@code addContactPoint(InetSocketAddress)} and {@code withLocalDatacenter(String)} methods to create + * a Session Object. See https://docs.datastax.com/en/developer/java-driver/ for more on the driver. */ + @Deprecated public Cluster getCluster() { return getCluster(this, enableJmxReporting); } + @Deprecated public static Cluster getCluster(ContainerState containerState, boolean enableJmxReporting) { final Cluster.Builder builder = Cluster .builder() @@ -203,10 +217,30 @@ public static Cluster getCluster(ContainerState containerState, boolean enableJm return builder.build(); } + @Deprecated public static Cluster getCluster(ContainerState containerState) { return getCluster(containerState, false); } + /** + * Retrieve an {@link InetSocketAddress} for connecting to the Cassandra container via the driver. + * + * @return A InetSocketAddrss representation of this Cassandra container's host and port. + */ + public InetSocketAddress getContactPoint() { + return new InetSocketAddress(getHost(), getMappedPort(CQL_PORT)); + } + + /** + * Retrieve the Local Datacenter for connecting to the Cassandra container via the driver. + * + * @return The configured local Datacenter name. + */ + public String getLocalDatacenter() { + return getEnvMap().getOrDefault("CASSANDRA_DC", DEFAULT_LOCAL_DATACENTER); + } + + @Deprecated private DatabaseDelegate getDatabaseDelegate() { return new CassandraDatabaseDelegate(this); } diff --git a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java index 45cbba0abc7..d1ad581bcc0 100644 --- a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java +++ b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java @@ -23,11 +23,13 @@ public class CassandraContainerTest { private static final String TEST_CLUSTER_NAME_IN_CONF = "Test Cluster Integration Test"; + private static final String BASIC_QUERY = "SELECT release_version FROM system.local"; + @Test public void testSimple() { try (CassandraContainer cassandraContainer = new CassandraContainer<>(CASSANDRA_IMAGE)) { cassandraContainer.start(); - ResultSet resultSet = performQuery(cassandraContainer, "SELECT release_version FROM system.local"); + ResultSet resultSet = performQuery(cassandraContainer, BASIC_QUERY); assertTrue("Query was not applied", resultSet.wasApplied()); assertNotNull("Result set has no release_version", resultSet.one().getString(0)); } @@ -42,7 +44,7 @@ public void testSpecificVersion() { ) ) { cassandraContainer.start(); - ResultSet resultSet = performQuery(cassandraContainer, "SELECT release_version FROM system.local"); + ResultSet resultSet = performQuery(cassandraContainer, BASIC_QUERY); assertTrue("Query was not applied", resultSet.wasApplied()); assertEquals("Cassandra has wrong version", cassandraVersion, resultSet.one().getString(0)); } @@ -107,7 +109,7 @@ public void testCassandraQueryWaitStrategy() { .waitingFor(new CassandraQueryWaitStrategy()) ) { cassandraContainer.start(); - ResultSet resultSet = performQuery(cassandraContainer, "SELECT release_version FROM system.local"); + ResultSet resultSet = performQuery(cassandraContainer, BASIC_QUERY); assertTrue("Query was not applied", resultSet.wasApplied()); } } @@ -117,10 +119,7 @@ public void testCassandraQueryWaitStrategy() { public void testCassandraGetCluster() { try (CassandraContainer cassandraContainer = new CassandraContainer<>()) { cassandraContainer.start(); - ResultSet resultSet = performQuery( - cassandraContainer.getCluster(), - "SELECT release_version FROM system.local" - ); + ResultSet resultSet = performQuery(cassandraContainer.getCluster(), BASIC_QUERY); assertTrue("Query was not applied", resultSet.wasApplied()); assertNotNull("Result set has no release_version", resultSet.one().getString(0)); } diff --git a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver3Test.java b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver3Test.java new file mode 100644 index 00000000000..78f3fd2a664 --- /dev/null +++ b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver3Test.java @@ -0,0 +1,37 @@ +package org.testcontainers.containers; + +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata; +import org.junit.Rule; +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; + +public class CassandraDriver3Test { + + @Rule + public CassandraContainer cassandra = new CassandraContainer<>("cassandra:3.11.2"); + + @Test + public void testCassandraGetContactPoint() { + try ( + // cassandra { + CqlSession session = CqlSession + .builder() + .addContactPoint(this.cassandra.getContactPoint()) + .withLocalDatacenter(this.cassandra.getLocalDatacenter()) + .build() + // } + ) { + session.execute( + "CREATE KEYSPACE IF NOT EXISTS test WITH replication = \n" + + "{'class':'SimpleStrategy','replication_factor':'1'};" + ); + + KeyspaceMetadata keyspace = session.getMetadata().getKeyspaces().get(CqlIdentifier.fromCql("test")); + + assertNotNull("Failed to create test keyspace", keyspace); + } + } +} diff --git a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver4Test.java b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver4Test.java new file mode 100644 index 00000000000..3a05bc723bc --- /dev/null +++ b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver4Test.java @@ -0,0 +1,35 @@ +package org.testcontainers.containers; + +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata; +import org.junit.Rule; +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; + +public class CassandraDriver4Test { + + @Rule + public CassandraContainer cassandra = new CassandraContainer<>("cassandra:3.11.2"); + + @Test + public void testCassandraGetContactPoint() { + try ( + CqlSession session = CqlSession + .builder() + .addContactPoint(this.cassandra.getContactPoint()) + .withLocalDatacenter(this.cassandra.getLocalDatacenter()) + .build() + ) { + session.execute( + "CREATE KEYSPACE IF NOT EXISTS test WITH replication = \n" + + "{'class':'SimpleStrategy','replication_factor':'1'};" + ); + + KeyspaceMetadata keyspace = session.getMetadata().getKeyspaces().get(CqlIdentifier.fromCql("test")); + + assertNotNull("Failed to create test keyspace", keyspace); + } + } +} From 4d2b98fca08f13f4b847776aee104e94249f52d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Thu, 4 Aug 2022 22:40:06 -0500 Subject: [PATCH 2745/5615] Add cockroachdb to issue's template --- .github/ISSUE_TEMPLATE/bug_report.yaml | 1 + .github/ISSUE_TEMPLATE/enhancement.yaml | 1 + .github/ISSUE_TEMPLATE/feature.yaml | 1 + 3 files changed, 3 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index dc86b416e46..686086d076c 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -17,6 +17,7 @@ body: - Azure - Cassandra - Clickhouse + - CockroachDB - Couchbase - DB2 - Dynalite diff --git a/.github/ISSUE_TEMPLATE/enhancement.yaml b/.github/ISSUE_TEMPLATE/enhancement.yaml index f06ea99c8ba..9ef8eafaab2 100644 --- a/.github/ISSUE_TEMPLATE/enhancement.yaml +++ b/.github/ISSUE_TEMPLATE/enhancement.yaml @@ -17,6 +17,7 @@ body: - Azure - Cassandra - Clickhouse + - CockroachDB - Couchbase - DB2 - Dynalite diff --git a/.github/ISSUE_TEMPLATE/feature.yaml b/.github/ISSUE_TEMPLATE/feature.yaml index 9e79553d357..1ab2693f210 100644 --- a/.github/ISSUE_TEMPLATE/feature.yaml +++ b/.github/ISSUE_TEMPLATE/feature.yaml @@ -17,6 +17,7 @@ body: - Azure - Cassandra - Clickhouse + - CockroachDB - Couchbase - DB2 - Dynalite From ef50721d86f83c7a1c0769001f004673b12c11c2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 5 Aug 2022 19:28:53 -0500 Subject: [PATCH 2746/5615] Update Gradle Wrapper to 7.5.1 (#5676) Signed-off-by: gradle-update-robot --- examples/gradle/wrapper/gradle-wrapper.properties | 4 ++-- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/gradle/wrapper/gradle-wrapper.properties b/examples/gradle/wrapper/gradle-wrapper.properties index 012d6d90445..b916c04dbb2 100644 --- a/examples/gradle/wrapper/gradle-wrapper.properties +++ b/examples/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=cb87f222c5585bd46838ad4db78463a5c5f3d336e5e2b98dc7c0c586527351c2 -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip +distributionSha256Sum=f6b8596b10cce501591e92f229816aa4046424f3b24d771751b06779d58c8ec4 +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ef80eb5051f..f7189a776c1 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=97a52d145762adc241bad7fd18289bf7f6801e08ece6badf80402fe2b9f250b1 -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip +distributionSha256Sum=db9c8211ed63f61f60292c69e80d89196f9eb36665e369e7f00ac4cc841c2219 +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From cae12726c04ca80a7049b49245b8d005ac38e95d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Mon, 8 Aug 2022 08:07:13 -0500 Subject: [PATCH 2747/5615] Update ryuk version to 0.3.4 (#5619) --- .../src/main/java/org/testcontainers/utility/RyukContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/utility/RyukContainer.java b/core/src/main/java/org/testcontainers/utility/RyukContainer.java index 511e6ccac17..beed2e92c62 100644 --- a/core/src/main/java/org/testcontainers/utility/RyukContainer.java +++ b/core/src/main/java/org/testcontainers/utility/RyukContainer.java @@ -9,7 +9,7 @@ class RyukContainer extends GenericContainer { RyukContainer() { - super("testcontainers/ryuk:0.3.3"); + super("testcontainers/ryuk:0.3.4"); withExposedPorts(8080); withCreateContainerCmdModifier(cmd -> { cmd.withName("testcontainers-ryuk-" + DockerClientFactory.SESSION_ID); From e8d737513cc0c7f16ea757a6f4f2eeea7a5e602e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Mon, 8 Aug 2022 18:06:47 -0500 Subject: [PATCH 2748/5615] Move to assertj (#5679) --- config/checkstyle/checkstyle.xml | 1 - core/build.gradle | 1 - .../java/org/testcontainers/DaemonTest.java | 14 +- .../containers/ContainerStateTest.java | 4 +- ...ockerComposeContainerWithServicesTest.java | 29 +-- .../containers/DockerComposeFilesTest.java | 9 +- .../DockerComposeOverridesTest.java | 5 - .../containers/ExposedHostTest.java | 4 +- .../containers/GenericContainerTest.java | 30 +-- .../containers/NetworkTest.java | 39 ++-- ...ParsedDockerComposeFileValidationTest.java | 30 ++- .../containers/ReusabilityUnitTests.java | 5 +- .../containers/output/ContainerLogsTest.java | 15 +- .../output/ToStringConsumerTest.java | 5 +- .../ExternalPortListeningCheckTest.java | 16 +- ...InternalCommandPortListeningCheckTest.java | 9 +- .../DockerHealthcheckWaitStrategyTest.java | 7 +- .../wait/strategy/WaitAllStrategyTest.java | 51 ++--- ...temPropertyClientProviderStrategyTest.java | 21 +-- .../images/ImagePullPolicyTest.java | 6 +- .../images/builder/DockerfileBuildTest.java | 9 +- .../images/builder/DockerignoreTest.java | 18 +- .../statement/AbstractStatementTest.java | 5 +- .../junit/BaseDockerComposeTest.java | 10 +- .../junit/CopyFileToContainerTest.java | 17 +- .../junit/DependenciesTest.java | 39 ++-- .../DockerComposeContainerScalingTest.java | 4 +- .../junit/DockerComposeContainerTest.java | 35 ++-- .../DockerComposeContainerWithBuildTest.java | 24 ++- .../junit/DockerComposeErrorHandlingTest.java | 16 +- .../junit/DockerComposePassthroughTest.java | 18 +- .../junit/DockerComposeServiceTest.java | 4 +- .../junit/DockerComposeWaitStrategyTest.java | 28 +-- .../junit/DockerNetworkModeTest.java | 11 +- .../junit/DockerfileContainerTest.java | 20 +- .../testcontainers/junit/DockerfileTest.java | 4 - .../junit/FileOperationsTest.java | 19 +- .../junit/GenericContainerRuleTest.java | 134 ++++++-------- .../junit/NonExistentImagePullTest.java | 15 +- .../junit/OutputStreamTest.java | 24 ++- .../junit/OutputStreamWithTTYTest.java | 34 ++-- .../ParameterizedDockerfileContainerTest.java | 4 +- .../junit/WorkingDirectoryTest.java | 8 +- .../strategy/AbstractWaitStrategyTest.java | 11 +- .../strategy/HostPortWaitStrategyTest.java | 6 +- .../utility/AuthenticatedImagePullTest.java | 13 +- .../utility/ClasspathScannerTest.java | 16 +- .../utility/DirectoryTarResourceTest.java | 132 +++++-------- .../DockerImageNameCompatibilityTest.java | 52 ++++-- .../utility/DockerImageNameTest.java | 62 +++---- .../utility/ImageNameSubstitutorTest.java | 18 +- .../utility/LazyFutureTest.java | 22 ++- .../utility/MountableFileTest.java | 37 ++-- .../PrefixingImageNameSubstitutorTest.java | 60 +++--- .../utility/RegistryAuthLocatorTest.java | 114 +++++++----- .../utility/TestEnvironmentTest.java | 18 +- .../TestcontainersConfigurationTest.java | 174 +++++++----------- docs/examples/junit5/redis/build.gradle | 1 + .../quickstart/RedisBackedCacheIntTest.java | 4 +- .../RedisBackedCacheIntTestStep0.java | 4 +- examples/disque-job-queue/build.gradle | 2 +- .../test/java/DisqueBackedMailSenderTest.java | 26 +-- .../test/java/SingleDisqueInstanceTest.java | 31 +--- .../redis-backed-cache-testng/build.gradle | 2 +- .../src/test/java/RedisBackedCacheTest.java | 14 +- examples/redis-backed-cache/build.gradle | 2 +- .../src/test/java/RedisBackedCacheTest.java | 14 +- examples/selenium-container/build.gradle | 2 +- .../src/test/java/SeleniumContainerTest.java | 4 +- examples/settings.gradle | 1 - examples/singleton-container/build.gradle | 2 +- .../com/example/BarConcreteTestClass.java | 7 +- .../com/example/FooConcreteTestClass.java | 9 +- examples/solr-container/build.gradle | 2 +- .../test/java/com/example/SolrQueryTest.java | 12 +- examples/spring-boot/build.gradle | 1 - .../java/com/example/DemoControllerTest.java | 8 +- .../CosmosDBEmulatorContainerTest.java | 7 +- modules/clickhouse/build.gradle | 1 + .../clickhouse/SimpleClickhouseTest.java | 4 +- modules/cockroachdb/build.gradle | 1 + .../cockroachdb/SimpleCockroachDBTest.java | 16 +- .../ext/ScriptSplittingTest.java | 6 +- modules/db2/build.gradle | 1 + .../junit/db2/SimpleDb2Test.java | 8 +- modules/dynalite/build.gradle | 3 +- .../dynamodb/DynaliteContainerTest.java | 15 +- modules/elasticsearch/build.gradle | 2 +- .../ElasticsearchContainerTest.java | 96 +++++----- modules/hivemq/build.gradle | 1 + .../hivemq/ContainerWithCustomConfigIT.java | 10 +- .../ContainerWithoutPlatformExtensionsIT.java | 23 ++- .../hivemq/CreateFileInCopiedDirectoryIT.java | 6 +- ...DisableEnableExtensionFromDirectoryIT.java | 8 +- .../hivemq/DisableEnableExtensionIT.java | 14 +- .../hivemq/HiveMQExtensionTest.java | 8 +- .../hivemq/HiveMQTestContainerCore.java | 43 +++-- .../testcontainers/hivemq/PathUtilTest.java | 16 +- modules/jdbc-test/build.gradle | 5 +- .../jdbc/AbstractJDBCDriverTest.java | 48 +++-- modules/jdbc/build.gradle | 1 - .../jdbc/ConnectionUrlDriversTests.java | 10 +- .../jdbc/ConnectionUrlTest.java | 102 +++++----- .../jdbc/MissingJdbcDriverTest.java | 13 +- .../junit/jupiter/ComposeContainerTests.java | 4 +- .../junit/jupiter/MixedLifecycleTests.java | 6 +- .../jupiter/TestcontainersExtensionTests.java | 11 +- ...ntainersNestedRestartedContainerTests.java | 19 +- ...tcontainersNestedSharedContainerTests.java | 9 +- .../TestcontainersRestartBetweenTests.java | 6 +- .../TestcontainersSharedContainerTests.java | 9 +- .../jupiter/inheritance/InheritedTests.java | 14 +- .../k3s/OfficialClientK3sContainerTest.java | 5 +- modules/localstack/build.gradle | 2 +- .../containers/localstack/LegacyModeTest.java | 60 +++--- .../localstack/LocalstackContainerTest.java | 94 +++++----- .../junit/mariadb/SimpleMariaDBTest.java | 26 ++- modules/mockserver/build.gradle | 2 +- .../MockServerContainerRuleTest.java | 11 +- .../containers/MockServerContainerTest.java | 14 +- .../src/test/resources/logback-test.xml | 16 ++ .../CustomPasswordMSSQLServerTest.java | 2 +- .../CustomizableMSSQLServerTest.java | 4 +- .../mssqlserver/SimpleMSSQLServerTest.java | 10 +- .../jdbc/mysql/JDBCDriverWithPoolTest.java | 6 +- .../junit/mysql/CustomizableMysqlTest.java | 4 +- .../junit/mysql/MultiVersionMySQLTest.java | 10 +- .../junit/mysql/SimpleMySQLTest.java | 36 ++-- .../containers/Neo4jContainerTest.java | 13 +- modules/nginx/build.gradle | 2 +- .../testcontainers/junit/SimpleNginxTest.java | 11 +- .../containers/jdbc/OracleJDBCDriverTest.java | 4 +- .../containers/OrientDBContainerTest.java | 2 +- .../PostgreSQLConnectionURLTest.java | 42 ++--- .../containers/TimescaleDBContainerTest.java | 11 +- .../jdbc/DatabaseDriverShutdownTest.java | 12 +- .../jdbc/DatabaseDriverTmpfsTest.java | 9 +- .../CustomizablePostgreSQLTest.java | 4 +- .../postgresql/SimplePostgreSQLTest.java | 19 +- ...tcontainersR2DBCConnectionFactoryTest.java | 8 +- modules/selenium/build.gradle | 2 +- .../junit/BaseWebDriverContainerTest.java | 9 +- .../junit/BrowserWebDriverContainerTest.java | 18 +- .../LocalServerWebDriverContainerTest.java | 10 +- .../junit/SeleniumUtilsTest.java | 6 +- modules/toxiproxy/build.gradle | 2 +- .../containers/ToxiproxyTest.java | 93 +++++----- 147 files changed, 1265 insertions(+), 1544 deletions(-) create mode 100644 modules/mockserver/src/test/resources/logback-test.xml diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index ccb8367404c..6e73f4da4aa 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -39,7 +39,6 @@ org.mockserver.model.HttpRequest.*, org.mockserver.model.HttpResponse.*, org.rnorth.ducttape.unreliables.Unreliables.*, - org.rnorth.visibleassertions.VisibleAssertions.*, " /> diff --git a/core/build.gradle b/core/build.gradle index d32e2fc43b2..9febf2802e9 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -97,7 +97,6 @@ dependencies { // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest testImplementation files('testlib/repo/fakejar/fakejar/0/fakejar-0.jar') - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation project(':test-support') diff --git a/core/src/test/java/org/testcontainers/DaemonTest.java b/core/src/test/java/org/testcontainers/DaemonTest.java index c1a1c20d60a..dd35112f781 100644 --- a/core/src/test/java/org/testcontainers/DaemonTest.java +++ b/core/src/test/java/org/testcontainers/DaemonTest.java @@ -1,7 +1,6 @@ package org.testcontainers; import org.junit.Test; -import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.containers.GenericContainer; import java.io.File; @@ -9,7 +8,8 @@ import java.util.Set; import java.util.stream.Collectors; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; /** * This test forks a new JVM, otherwise it's not possible to reliably diff the threads @@ -30,17 +30,13 @@ public static void main(String[] args) { Set nonDaemonThreads = threads.stream().filter(it -> !it.isDaemon()).collect(Collectors.toSet()); - if (nonDaemonThreads.isEmpty()) { - VisibleAssertions.pass("All threads marked as daemon"); - } else { + if (!nonDaemonThreads.isEmpty()) { String nonDaemonThreadNames = nonDaemonThreads .stream() .map(Thread::getName) .collect(Collectors.joining("\n", "\n", "")); - VisibleAssertions.fail( - "Expected all threads to be daemons but the following are not:\n" + nonDaemonThreadNames - ); + fail("Expected all threads to be daemons but the following are not:\n" + nonDaemonThreadNames); } } finally { if (genericContainer != null) { @@ -59,6 +55,6 @@ public void testThatAllThreadsAreDaemons() throws Exception { DaemonTest.class.getCanonicalName() ); - assertEquals(0, processBuilder.inheritIO().start().waitFor()); + assertThat(processBuilder.inheritIO().start().waitFor()).isZero(); } } diff --git a/core/src/test/java/org/testcontainers/containers/ContainerStateTest.java b/core/src/test/java/org/testcontainers/containers/ContainerStateTest.java index 4909e908713..d3cedc11464 100644 --- a/core/src/test/java/org/testcontainers/containers/ContainerStateTest.java +++ b/core/src/test/java/org/testcontainers/containers/ContainerStateTest.java @@ -1,6 +1,5 @@ package org.testcontainers.containers; -import org.assertj.core.api.Assertions; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -8,6 +7,7 @@ import java.util.Collections; import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -44,6 +44,6 @@ public void test() { when(containerState.getPortBindings()).thenReturn(Collections.singletonList(testSet)); List result = containerState.getBoundPortNumbers(); - Assertions.assertThat(result).hasSameElementsAs(expectedResult); + assertThat(result).hasSameElementsAs(expectedResult); } } diff --git a/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java b/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java index b7b820a3011..029e8447a81 100644 --- a/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java +++ b/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java @@ -1,6 +1,7 @@ package org.testcontainers.containers; import org.junit.Test; +import org.rnorth.ducttape.TimeoutException; import org.testcontainers.containers.wait.strategy.Wait; import java.io.File; @@ -9,8 +10,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; public class DockerComposeContainerWithServicesTest { @@ -92,10 +93,8 @@ public void testScaleInComposeFileIsRespected() { @Test public void testStartupTimeoutSetsTheHighestTimeout() { - assertThrows( - "We expect a timeout from the startup timeout", - org.rnorth.ducttape.TimeoutException.class, - () -> { + assertThat( + catchThrowable(() -> { try ( DockerComposeContainer compose = new DockerComposeContainer<>(SIMPLE_COMPOSE_FILE) .withServices("redis") @@ -108,8 +107,10 @@ public void testStartupTimeoutSetsTheHighestTimeout() { ) { compose.start(); } - } - ); + }) + ) + .as("We expect a timeout from the startup timeout") + .isInstanceOf(TimeoutException.class); } private void verifyStartedContainers(final DockerComposeContainer compose, final String... names) { @@ -119,16 +120,16 @@ private void verifyStartedContainers(final DockerComposeContainer compose, fi .flatMap(container -> Stream.of(container.getNames())) .collect(Collectors.toList()); - assertEquals( - "number of running services of docker-compose is the same as length of listOfServices", - names.length, - containerNames.size() - ); + assertThat(containerNames) + .as("number of running services of docker-compose is the same as length of listOfServices") + .hasSize(names.length); for (final String expectedName : names) { final long matches = containerNames.stream().filter(foundName -> foundName.endsWith(expectedName)).count(); - assertEquals("container with name starting '" + expectedName + "' should be running", 1L, matches); + assertThat(matches) + .as("container with name starting '" + expectedName + "' should be running") + .isEqualTo(1L); } } } diff --git a/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java b/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java index 612d249449b..41870eb593d 100644 --- a/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java +++ b/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java @@ -1,11 +1,12 @@ package org.testcontainers.containers; import com.google.common.collect.Lists; -import org.assertj.core.api.Assertions; import org.junit.Test; import java.io.File; +import static org.assertj.core.api.Assertions.assertThat; + public class DockerComposeFilesTest { @Test @@ -13,8 +14,7 @@ public void shouldGetDependencyImages() { DockerComposeFiles dockerComposeFiles = new DockerComposeFiles( Lists.newArrayList(new File("src/test/resources/docker-compose-imagename-parsing-v2.yml")) ); - Assertions - .assertThat(dockerComposeFiles.getDependencyImages()) + assertThat(dockerComposeFiles.getDependencyImages()) .containsExactlyInAnyOrder("postgres:latest", "redis:latest", "mysql:latest"); } @@ -26,8 +26,7 @@ public void shouldGetDependencyImagesWhenOverriding() { new File("src/test/resources/docker-compose-imagename-overriding-b.yml") ) ); - Assertions - .assertThat(dockerComposeFiles.getDependencyImages()) + assertThat(dockerComposeFiles.getDependencyImages()) .containsExactlyInAnyOrder("alpine:3.16", "redis:b", "mysql:b", "aservice:latest"); } } diff --git a/core/src/test/java/org/testcontainers/containers/DockerComposeOverridesTest.java b/core/src/test/java/org/testcontainers/containers/DockerComposeOverridesTest.java index 6207ae5ff45..50fedd3cf0f 100644 --- a/core/src/test/java/org/testcontainers/containers/DockerComposeOverridesTest.java +++ b/core/src/test/java/org/testcontainers/containers/DockerComposeOverridesTest.java @@ -16,9 +16,6 @@ import java.util.Arrays; import java.util.concurrent.TimeUnit; -import static org.rnorth.visibleassertions.VisibleAssertions.info; -import static org.rnorth.visibleassertions.VisibleAssertions.pass; - @RunWith(Parameterized.class) public class DockerComposeOverridesTest { @@ -100,11 +97,9 @@ public void test() { while (br.ready()) { String line = br.readLine(); if (line.contains(expectedEnvVar)) { - pass("Mapped environment variable was found"); return true; } } - info("Mapped environment variable was not found yet - process probably not ready"); Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); return false; } diff --git a/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java b/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java index 4afddeaa5dd..5f88be90ad8 100644 --- a/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java +++ b/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java @@ -13,7 +13,7 @@ import java.io.OutputStream; import java.net.InetSocketAddress; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class ExposedHostTest { @@ -95,7 +95,7 @@ protected void assertResponse(GenericContainer container, int port) { .execInContainer("wget", "-O", "-", "http://host.testcontainers.internal:" + port) .getStdout(); - assertEquals("received response", "Hello World!", response); + assertThat(response).as("received response").isEqualTo("Hello World!"); } finally { container.stop(); } diff --git a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java index fdd8ac1b146..1967ad95135 100644 --- a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java +++ b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java @@ -6,7 +6,6 @@ import com.github.dockerjava.api.model.ExposedPort; import com.github.dockerjava.api.model.Info; import com.github.dockerjava.api.model.Ports; -import com.google.common.primitives.Ints; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.experimental.FieldDefaults; @@ -30,10 +29,9 @@ import java.util.function.Predicate; import java.util.stream.Collectors; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.catchThrowable; public class GenericContainerTest { @@ -119,28 +117,20 @@ public void shouldOnlyPublishExposedPorts() { .map(ExposedPort::getPort) .collect(Collectors.toList()); - assertEquals( - "the exposed ports are all of those EXPOSEd by the image", - Ints.asList(8080, 8081), - exposedPorts - ); + assertThat(exposedPorts).as("the exposed ports are all of those EXPOSEd by the image").contains(8080, 8081); Map hostBindings = inspectedContainer .getHostConfig() .getPortBindings() .getBindings(); - assertEquals("only 1 port is bound on the host (published)", 1, hostBindings.size()); + assertThat(hostBindings).as("only 1 port is bound on the host (published)").hasSize(1); Integer mappedPort = container.getMappedPort(8080); - assertTrue("port 8080 is bound to a different port on the host", mappedPort != 8080); + assertThat(mappedPort != 8080).as("port 8080 is bound to a different port on the host").isTrue(); - assertThrows( - "trying to get a non-bound port mapping fails", - IllegalArgumentException.class, - () -> { - container.getMappedPort(8081); - } - ); + assertThat(catchThrowable(() -> container.getMappedPort(8081))) + .as("trying to get a non-bound port mapping fails") + .isInstanceOf(IllegalArgumentException.class); } } @@ -161,8 +151,8 @@ public void shouldWaitUntilExposedPortIsMapped() { ) { container.start(); - assertEquals("Only withExposedPort should be exposed", 1, container.getExposedPorts().size()); - assertTrue("withExposedPort should be exposed", container.getExposedPorts().contains(8080)); + assertThat(container.getExposedPorts()).as("Only withExposedPort should be exposed").hasSize(1); + assertThat(container.getExposedPorts()).as("withExposedPort should be exposed").contains(8080); } } diff --git a/core/src/test/java/org/testcontainers/containers/NetworkTest.java b/core/src/test/java/org/testcontainers/containers/NetworkTest.java index d783f95e6d1..099bef20ed7 100644 --- a/core/src/test/java/org/testcontainers/containers/NetworkTest.java +++ b/core/src/test/java/org/testcontainers/containers/NetworkTest.java @@ -7,9 +7,7 @@ import org.testcontainers.DockerClientFactory; import org.testcontainers.TestImages; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; +import static org.assertj.core.api.Assertions.assertThat; @RunWith(Enclosed.class) public class NetworkTest { @@ -33,7 +31,7 @@ public static class WithRules { @Test public void testNetworkSupport() throws Exception { String response = bar.execInContainer("wget", "-O", "-", "http://foo:8080").getStdout(); - assertEquals("received response", "yay", response); + assertThat(response).as("received response").isEqualTo("yay"); } } @@ -60,7 +58,7 @@ public void testNetworkSupport() throws Exception { bar.start(); String response = bar.execInContainer("wget", "-O", "-", "http://foo:8080").getStdout(); - assertEquals("received response", "yay", response); + assertThat(response).as("received response").isEqualTo("yay"); } // } } @@ -69,11 +67,11 @@ public void testNetworkSupport() throws Exception { public void testBuilder() { try (Network network = Network.builder().driver("macvlan").build()) { String id = network.getId(); - assertEquals( - "Flag is set", - "macvlan", + assertThat( DockerClientFactory.instance().client().inspectNetworkCmd().withNetworkId(id).exec().getDriver() - ); + ) + .as("Flag is set") + .isEqualTo("macvlan"); } } @@ -83,11 +81,11 @@ public void testModifiers() { Network network = Network.builder().createNetworkCmdModifier(cmd -> cmd.withDriver("macvlan")).build() ) { String id = network.getId(); - assertEquals( - "Flag is set", - "macvlan", + assertThat( DockerClientFactory.instance().client().inspectNetworkCmd().withNetworkId(id).exec().getDriver() - ); + ) + .as("Flag is set") + .isEqualTo("macvlan"); } } @@ -95,16 +93,13 @@ public void testModifiers() { public void testReusability() { try (Network network = Network.newNetwork()) { String firstId = network.getId(); - assertNotNull( - "Network exists", - DockerClientFactory.instance().client().inspectNetworkCmd().withNetworkId(firstId).exec() - ); + assertThat(DockerClientFactory.instance().client().inspectNetworkCmd().withNetworkId(firstId).exec()) + .as("Network exists") + .isNotNull(); network.close(); - assertNotEquals( - "New network created", - firstId, + assertThat( DockerClientFactory .instance() .client() @@ -112,7 +107,9 @@ public void testReusability() { .withNetworkId(network.getId()) .exec() .getId() - ); + ) + .as("New network created") + .isNotEqualTo(firstId); } } } diff --git a/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java b/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java index f374018a8ef..8a41ac8d8a4 100644 --- a/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java +++ b/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java @@ -2,12 +2,12 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Sets; -import org.assertj.core.api.Assertions; import org.junit.Test; import java.io.File; import java.util.Collections; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.entry; @@ -65,55 +65,51 @@ public void shouldIgnoreUnknownStructure() { public void shouldObtainImageNamesV1() { File file = new File("src/test/resources/docker-compose-imagename-parsing-v1.yml"); ParsedDockerComposeFile parsedFile = new ParsedDockerComposeFile(file); - Assertions - .assertThat(parsedFile.getServiceNameToImageNames()) + assertThat(parsedFile.getServiceNameToImageNames()) + .as("all defined images are found") .contains( entry("mysql", Sets.newHashSet("mysql")), entry("redis", Sets.newHashSet("redis")), entry("custom", Sets.newHashSet("postgres")) - ) - .as("all defined images are found"); // redis, mysql from compose file, postgres from Dockerfile build + ); // redis, mysql from compose file, postgres from Dockerfile build } @Test public void shouldObtainImageNamesV2() { File file = new File("src/test/resources/docker-compose-imagename-parsing-v2.yml"); ParsedDockerComposeFile parsedFile = new ParsedDockerComposeFile(file); - Assertions - .assertThat(parsedFile.getServiceNameToImageNames()) + assertThat(parsedFile.getServiceNameToImageNames()) + .as("all defined images are found") .contains( entry("mysql", Sets.newHashSet("mysql")), entry("redis", Sets.newHashSet("redis")), entry("custom", Sets.newHashSet("postgres")) - ) - .as("all defined images are found"); + ); } @Test public void shouldObtainImageFromDockerfileBuild() { File file = new File("src/test/resources/docker-compose-imagename-parsing-dockerfile.yml"); ParsedDockerComposeFile parsedFile = new ParsedDockerComposeFile(file); - Assertions - .assertThat(parsedFile.getServiceNameToImageNames()) + assertThat(parsedFile.getServiceNameToImageNames()) + .as("all defined images are found") .contains( entry("mysql", Sets.newHashSet("mysql")), entry("redis", Sets.newHashSet("redis")), entry("custom", Sets.newHashSet("alpine:3.16")) - ) - .as("all defined images are found"); // r/ redis, mysql from compose file, alpine:3.16 from Dockerfile build + ); // r/ redis, mysql from compose file, alpine:3.16 from Dockerfile build } @Test public void shouldObtainImageFromDockerfileBuildWithContext() { File file = new File("src/test/resources/docker-compose-imagename-parsing-dockerfile-with-context.yml"); ParsedDockerComposeFile parsedFile = new ParsedDockerComposeFile(file); - Assertions - .assertThat(parsedFile.getServiceNameToImageNames()) + assertThat(parsedFile.getServiceNameToImageNames()) + .as("all defined images are found") .contains( entry("mysql", Sets.newHashSet("mysql")), entry("redis", Sets.newHashSet("redis")), entry("custom", Sets.newHashSet("alpine:3.16")) - ) - .as("all defined images are found"); // redis, mysql from compose file, alpine:3.16 from Dockerfile build + ); // redis, mysql from compose file, alpine:3.16 from Dockerfile build } } diff --git a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java index 7aafb0149bd..04f97fef514 100644 --- a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java +++ b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java @@ -25,7 +25,6 @@ import org.mockito.Answers; import org.mockito.Mockito; import org.mockito.stubbing.Answer; -import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.DockerClientFactory; import org.testcontainers.TestImages; import org.testcontainers.containers.startupcheck.StartupCheckStrategy; @@ -82,9 +81,9 @@ public static Object[][] data() { @Test public void shouldBeReusable() { if (reusable) { - VisibleAssertions.assertTrue("Is reusable", container.canBeReused()); + assertThat(container.canBeReused()).as("Is reusable").isTrue(); } else { - VisibleAssertions.assertFalse("Is not reusable", container.canBeReused()); + assertThat(container.canBeReused()).as("Is not reusable").isFalse(); } } diff --git a/core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java b/core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java index a860b7802ec..cc3a98dba47 100644 --- a/core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java +++ b/core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java @@ -6,8 +6,7 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class ContainerLogsTest { @@ -18,7 +17,7 @@ public void getLogsReturnsAllLogsToDate() { container.start(); final String logs = container.getLogs(); - assertEquals("stdout and stderr are reflected in the returned logs", "stdout\nstderr", logs); + assertThat(logs).as("stdout and stderr are reflected in the returned logs").isEqualTo("stdout\nstderr"); } } @@ -30,8 +29,8 @@ public void getLogsContainsBothOutputTypes() { // docsGetAllLogs { final String logs = container.getLogs(); // } - assertTrue("stdout is reflected in the returned logs", logs.contains("stdout")); - assertTrue("stderr is reflected in the returned logs", logs.contains("stderr")); + assertThat(logs).as("stdout is reflected in the returned logs").contains("stdout"); + assertThat(logs).as("stderr is reflected in the returned logs").contains("stderr"); } } @@ -43,7 +42,7 @@ public void getLogsReturnsStdOutToDate() { // docsGetStdOut { final String logs = container.getLogs(OutputFrame.OutputType.STDOUT); // } - assertTrue("stdout is reflected in the returned logs", logs.contains("stdout")); + assertThat(logs).as("stdout is reflected in the returned logs").contains("stdout"); } } @@ -55,7 +54,7 @@ public void getLogsReturnsStdErrToDate() { // docsGetStdErr { final String logs = container.getLogs(OutputFrame.OutputType.STDERR); // } - assertTrue("stderr is reflected in the returned logs", logs.contains("stderr")); + assertThat(logs).as("stderr is reflected in the returned logs").contains("stderr"); } } @@ -67,7 +66,7 @@ public void getLogsForLongRunningContainer() throws InterruptedException { Thread.sleep(1000L); final String logs = container.getLogs(OutputFrame.OutputType.STDOUT); - assertTrue("stdout is reflected in the returned logs for a running container", logs.contains("seq=0")); + assertThat(logs).as("stdout is reflected in the returned logs for a running container").contains("seq=0"); } } diff --git a/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java b/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java index 211b12080a0..2755f4142d4 100644 --- a/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java +++ b/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java @@ -1,7 +1,6 @@ package org.testcontainers.containers.output; import org.apache.commons.lang3.RandomStringUtils; -import org.assertj.core.api.Assertions; import org.junit.Test; import org.testcontainers.containers.Container.ExecResult; import org.testcontainers.containers.GenericContainer; @@ -19,7 +18,7 @@ public class ToStringConsumerTest { builder.append(' ').append(i).append(RandomStringUtils.randomAlphabetic(10000)); } LARGE_PAYLOAD = builder.toString(); - Assertions.assertThat(LARGE_PAYLOAD).doesNotContain("\n"); + assertThat(LARGE_PAYLOAD).doesNotContain("\n"); } @Test @@ -29,7 +28,7 @@ public void newlines_are_not_added_to_exec_output() throws Exception { container.start(); ExecResult build = container.execInContainer("echo", "-n", LARGE_PAYLOAD); - Assertions.assertThat(build.getStdout()).doesNotContain("\n").isEqualTo(LARGE_PAYLOAD); + assertThat(build.getStdout()).doesNotContain("\n").isEqualTo(LARGE_PAYLOAD); } } diff --git a/core/src/test/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheckTest.java b/core/src/test/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheckTest.java index c76d2481c7c..b7d9fd5a6fa 100644 --- a/core/src/test/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheckTest.java +++ b/core/src/test/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheckTest.java @@ -4,14 +4,14 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.containers.wait.strategy.WaitStrategyTarget; import java.net.ServerSocket; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; public class ExternalPortListeningCheckTest { @@ -44,7 +44,7 @@ public void singleListening() { final Boolean result = check.call(); - VisibleAssertions.assertTrue("ExternalPortListeningCheck identifies a single listening port", result); + assertThat(result).as("ExternalPortListeningCheck identifies a single listening port").isTrue(); } @Test @@ -56,7 +56,7 @@ public void multipleListening() { final Boolean result = check.call(); - VisibleAssertions.assertTrue("ExternalPortListeningCheck identifies multiple listening port", result); + assertThat(result).as("ExternalPortListeningCheck identifies multiple listening port").isTrue(); } @Test @@ -66,11 +66,9 @@ public void oneNotListening() { ImmutableSet.of(listeningSocket1.getLocalPort(), nonListeningSocket.getLocalPort()) ); - assertThrows( - "ExternalPortListeningCheck detects a non-listening port among many", - IllegalStateException.class, - (Runnable) check::call - ); + assertThat(catchThrowable(check::call)) + .as("ExternalPortListeningCheck detects a non-listening port among many") + .isInstanceOf(IllegalStateException.class); } @After diff --git a/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java b/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java index 6f2a5d3158c..d93eb90c339 100644 --- a/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java +++ b/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java @@ -7,13 +7,14 @@ import org.junit.runners.Parameterized; import org.rnorth.ducttape.TimeoutException; import org.rnorth.ducttape.unreliables.Unreliables; -import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.containers.GenericContainer; import org.testcontainers.images.builder.ImageFromDockerfile; import java.util.Arrays; import java.util.concurrent.TimeUnit; +import static org.assertj.core.api.Assertions.fail; + @RunWith(Parameterized.class) public class InternalCommandPortListeningCheckTest { @@ -48,8 +49,6 @@ public void singleListening() { ); Unreliables.retryUntilTrue(5, TimeUnit.SECONDS, check); - - VisibleAssertions.pass("InternalCommandPortListeningCheck identifies a single listening port"); } @Test @@ -61,7 +60,7 @@ public void nonListening() { try { Unreliables.retryUntilTrue(5, TimeUnit.SECONDS, check); - VisibleAssertions.fail("expected to fail"); + fail("expected to fail"); } catch (TimeoutException e) {} } @@ -73,7 +72,5 @@ public void lowAndHighPortListening() { ); Unreliables.retryUntilTrue(5, TimeUnit.SECONDS, check); - - VisibleAssertions.pass("InternalCommandPortListeningCheck identifies a low and a high port"); } } diff --git a/core/src/test/java/org/testcontainers/containers/wait/strategy/DockerHealthcheckWaitStrategyTest.java b/core/src/test/java/org/testcontainers/containers/wait/strategy/DockerHealthcheckWaitStrategyTest.java index 0d102def5a9..0cbf257e9b5 100644 --- a/core/src/test/java/org/testcontainers/containers/wait/strategy/DockerHealthcheckWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/containers/wait/strategy/DockerHealthcheckWaitStrategyTest.java @@ -8,7 +8,8 @@ import java.time.Duration; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; public class DockerHealthcheckWaitStrategyTest { @@ -37,6 +38,8 @@ public void startsOnceHealthy() { @Test public void containerStartFailsIfContainerIsUnhealthy() { container.withCommand("tail", "-f", "/dev/null"); - assertThrows("Container launch fails when unhealthy", ContainerLaunchException.class, container::start); + assertThat(catchThrowable(container::start)) + .as("Container launch fails when unhealthy") + .isInstanceOf(ContainerLaunchException.class); } } diff --git a/core/src/test/java/org/testcontainers/containers/wait/strategy/WaitAllStrategyTest.java b/core/src/test/java/org/testcontainers/containers/wait/strategy/WaitAllStrategyTest.java index 2b8ba7c33ec..36facd92329 100644 --- a/core/src/test/java/org/testcontainers/containers/wait/strategy/WaitAllStrategyTest.java +++ b/core/src/test/java/org/testcontainers/containers/wait/strategy/WaitAllStrategyTest.java @@ -10,10 +10,11 @@ import java.time.Duration; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; -import static org.rnorth.visibleassertions.VisibleAssertions.*; public class WaitAllStrategyTest { @@ -42,11 +43,11 @@ public void parentTimeoutApplies() { DummyStrategy child1 = new DummyStrategy(Duration.ofMillis(10)); child1.withStartupTimeout(Duration.ofMillis(20)); - assertEquals("withStartupTimeout directly sets the timeout", 20L, child1.startupTimeout.toMillis()); + assertThat(child1.startupTimeout.toMillis()).as("withStartupTimeout directly sets the timeout").isEqualTo(20L); new WaitAllStrategy().withStrategy(child1).withStartupTimeout(Duration.ofMillis(30)); - assertEquals("WaitAllStrategy overrides a child's timeout", 30L, child1.startupTimeout.toMillis()); + assertThat(child1.startupTimeout.toMillis()).as("WaitAllStrategy overrides a child's timeout").isEqualTo(30L); } @Test @@ -59,8 +60,12 @@ public void parentTimeoutAppliesToMultipleChildren() { new WaitAllStrategy().withStrategy(child1).withStrategy(child2).withStartupTimeout(outerWait); - assertEquals("WaitAllStrategy overrides a child's timeout (1st)", 6L, child1.startupTimeout.toMillis()); - assertEquals("WaitAllStrategy overrides a child's timeout (2nd)", 6L, child2.startupTimeout.toMillis()); + assertThat(child1.startupTimeout.toMillis()) + .as("WaitAllStrategy overrides a child's timeout (1st)") + .isEqualTo(6L); + assertThat(child2.startupTimeout.toMillis()) + .as("WaitAllStrategy overrides a child's timeout (2nd)") + .isEqualTo(6L); } @Test @@ -73,12 +78,12 @@ public void parentTimeoutAppliesToAdditionalChildren() { new WaitAllStrategy().withStrategy(child1).withStartupTimeout(outerWait).withStrategy(child2); - assertEquals("WaitAllStrategy overrides a child's timeout (1st)", 20L, child1.startupTimeout.toMillis()); - assertEquals( - "WaitAllStrategy overrides a child's timeout (2nd, additional)", - 20L, - child2.startupTimeout.toMillis() - ); + assertThat(child1.startupTimeout.toMillis()) + .as("WaitAllStrategy overrides a child's timeout (1st)") + .isEqualTo(20L); + assertThat(child2.startupTimeout.toMillis()) + .as("WaitAllStrategy overrides a child's timeout (2nd, additional)") + .isEqualTo(20L); } /* @@ -108,13 +113,13 @@ public void withoutOuterTimeoutShouldRelyOnInnerStrategies() { doNothing().when(strategy1).waitUntilReady(eq(container)); doThrow(TimeoutException.class).when(strategy2).waitUntilReady(eq(container)); - assertThrows( - "The outer strategy timeout applies", - TimeoutException.class, - () -> { + assertThat( + catchThrowable(() -> { underTest.waitUntilReady(container); - } - ); + }) + ) + .as("The outer strategy timeout applies") + .isInstanceOf(TimeoutException.class); InOrder inOrder = inOrder(strategy1, strategy2, strategy3); inOrder.verify(strategy1).waitUntilReady(any()); @@ -126,13 +131,13 @@ public void withoutOuterTimeoutShouldRelyOnInnerStrategies() { public void timeoutChangeShouldNotBePossibleWithIndividualTimeoutMode() { final WaitStrategy underTest = new WaitAllStrategy(WaitAllStrategy.Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY); - assertThrows( - "Cannot change timeout for individual timeouts", - IllegalStateException.class, - () -> { + assertThat( + catchThrowable(() -> { underTest.withStartupTimeout(Duration.ofSeconds(42)); - } - ); + }) + ) + .as("Cannot change timeout for individual timeouts") + .isInstanceOf(IllegalStateException.class); } @Test diff --git a/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java b/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java index b2ab27aad27..062c3d793a5 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java @@ -2,7 +2,6 @@ import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.transport.SSLConfig; -import org.assertj.core.api.Assertions; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -18,9 +17,7 @@ import java.nio.file.Path; import java.util.Properties; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; @@ -69,8 +66,8 @@ public void testWhenConfigAbsent() { EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(); TransportConfig transportConfig = strategy.getTransportConfig(); - assertEquals(defaultDockerHost, transportConfig.getDockerHost()); - assertEquals(defaultSSLConfig, transportConfig.getSslConfig()); + assertThat(transportConfig.getDockerHost()).isEqualTo(defaultDockerHost); + assertThat(transportConfig.getSslConfig()).isEqualTo(defaultSSLConfig); } @Test @@ -95,8 +92,8 @@ public void testWhenDockerHostPresent() { EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(); TransportConfig transportConfig = strategy.getTransportConfig(); - assertEquals("tcp://1.2.3.4:2375", transportConfig.getDockerHost().toString()); - assertEquals(defaultSSLConfig, transportConfig.getSslConfig()); + assertThat(transportConfig.getDockerHost().toString()).isEqualTo("tcp://1.2.3.4:2375"); + assertThat(transportConfig.getSslConfig()).isEqualTo(defaultSSLConfig); } @Test @@ -124,10 +121,10 @@ public void testWhenDockerHostAndSSLConfigPresent() throws IOException { EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(); TransportConfig transportConfig = strategy.getTransportConfig(); - assertEquals("tcp://1.2.3.4:2375", transportConfig.getDockerHost().toString()); + assertThat(transportConfig.getDockerHost().toString()).isEqualTo("tcp://1.2.3.4:2375"); SSLConfig sslConfig = transportConfig.getSslConfig(); - Assertions.assertThat(sslConfig).extracting("dockerCertPath").isEqualTo(tempDirPath); + assertThat(sslConfig).extracting("dockerCertPath").isEqualTo(tempDirPath); } @Test @@ -142,7 +139,7 @@ public void applicableWhenIgnoringUserPropertiesAndConfigured() { System.setProperty("DOCKER_HOST", "tcp://1.2.3.4:2375"); EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(); - assertTrue(strategy.isApplicable()); + assertThat(strategy.isApplicable()).isTrue(); } finally { System.setProperties(oldProperties); } @@ -157,6 +154,6 @@ public void notApplicableWhenIgnoringUserPropertiesAndNotConfigured() { EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(); - assertFalse(strategy.isApplicable()); + assertThat(strategy.isApplicable()).isFalse(); } } diff --git a/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java b/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java index 0b0ee5f1b70..a885c035e5c 100644 --- a/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java +++ b/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java @@ -4,7 +4,6 @@ import org.junit.ClassRule; import org.junit.Test; import org.mockito.Mockito; -import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.DockerClientFactory; import org.testcontainers.DockerRegistryContainer; import org.testcontainers.containers.ContainerLaunchException; @@ -12,7 +11,7 @@ import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import org.testcontainers.utility.DockerImageName; -import static org.junit.Assert.fail; +import static org.assertj.core.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; public class ImagePullPolicyTest { @@ -113,11 +112,10 @@ private void expectToFailWithNotFoundException(GenericContainer container) { while (throwable.getCause() != null) { throwable = throwable.getCause(); if (throwable.getCause() instanceof NotFoundException) { - VisibleAssertions.pass("Caused by NotFoundException"); return; } } - VisibleAssertions.fail("Caused by NotFoundException"); + fail("Caused by NotFoundException"); } } diff --git a/core/src/test/java/org/testcontainers/images/builder/DockerfileBuildTest.java b/core/src/test/java/org/testcontainers/images/builder/DockerfileBuildTest.java index 29cf4fcf6a7..c45233ea390 100644 --- a/core/src/test/java/org/testcontainers/images/builder/DockerfileBuildTest.java +++ b/core/src/test/java/org/testcontainers/images/builder/DockerfileBuildTest.java @@ -9,7 +9,7 @@ import java.nio.file.Path; import java.nio.file.Paths; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; @RunWith(Parameterized.class) public class DockerfileBuildTest { @@ -71,10 +71,9 @@ public void performTest() { container.start(); final String logs = container.getLogs(); - assertTrue( - "expected file content indicates that dockerfile build steps have been run", - logs.contains(expectedFileContent) - ); + assertThat(logs) + .as("expected file content indicates that dockerfile build steps have been run") + .contains(expectedFileContent); } } } diff --git a/core/src/test/java/org/testcontainers/images/builder/DockerignoreTest.java b/core/src/test/java/org/testcontainers/images/builder/DockerignoreTest.java index 513b1ce1784..8c6d288da99 100644 --- a/core/src/test/java/org/testcontainers/images/builder/DockerignoreTest.java +++ b/core/src/test/java/org/testcontainers/images/builder/DockerignoreTest.java @@ -9,8 +9,8 @@ import java.nio.file.Path; import java.nio.file.Paths; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.rnorth.visibleassertions.VisibleAssertions.fail; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; public class DockerignoreTest { @@ -45,14 +45,12 @@ public void testValidDockerignore() throws Exception { container.start(); final String logs = container.getLogs(); - assertTrue( - "Files in the container indicated the .dockerignore was not applied. Output was: " + logs, - logs.contains("should_not_be_ignored.txt") - ); - assertTrue( - "Files in the container indicated the .dockerignore was not applied. Output was: " + logs, - !logs.contains("should_be_ignored.txt") - ); + assertThat(logs) + .as("Files in the container indicated the .dockerignore was not applied. Output was: " + logs) + .contains("should_not_be_ignored.txt"); + assertThat(logs) + .as("Files in the container indicated the .dockerignore was not applied. Output was: " + logs) + .doesNotContain("should_be_ignored.txt"); } } } diff --git a/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/AbstractStatementTest.java b/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/AbstractStatementTest.java index 69d9a1b6840..0c47d556137 100644 --- a/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/AbstractStatementTest.java +++ b/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/AbstractStatementTest.java @@ -10,8 +10,7 @@ import java.io.InputStream; import java.util.Arrays; -import static org.rnorth.visibleassertions.VisibleAssertions.fail; -import static org.rnorth.visibleassertions.VisibleAssertions.pass; +import static org.assertj.core.api.Assertions.fail; public abstract class AbstractStatementTest { @@ -64,7 +63,5 @@ protected void assertStatement(Statement statement) { fail(failureBuilder.toString()); } - - pass(testName.getMethodName()); } } diff --git a/core/src/test/java/org/testcontainers/junit/BaseDockerComposeTest.java b/core/src/test/java/org/testcontainers/junit/BaseDockerComposeTest.java index dd62ace1fac..c1aee4d7f2b 100644 --- a/core/src/test/java/org/testcontainers/junit/BaseDockerComposeTest.java +++ b/core/src/test/java/org/testcontainers/junit/BaseDockerComposeTest.java @@ -15,9 +15,7 @@ import java.util.List; import java.util.stream.Collectors; -import static org.hamcrest.Matchers.is; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; +import static org.assertj.core.api.Assertions.assertThat; /** * Created by rnorth on 21/05/2016. @@ -46,7 +44,7 @@ public void simpleTest() { jedis.incr("test"); jedis.incr("test"); - assertEquals("A redis instance defined in compose can be used in isolation", "3", jedis.get("test")); + assertThat(jedis.get("test")).as("A redis instance defined in compose can be used in isolation").isEqualTo("3"); } @Test @@ -61,7 +59,7 @@ public void secondTest() { jedis.incr("test"); jedis.incr("test"); - assertEquals("Tests use fresh container instances", "3", jedis.get("test")); + assertThat(jedis.get("test")).as("Tests use fresh container instances").isEqualTo("3"); // if these end up using the same container one of the test methods will fail. // However, @Rule creates a separate DockerComposeContainer instance per test, so this just shouldn't happen } @@ -73,7 +71,7 @@ public void captureNetworks() { @After public void verifyNoNetworks() { - assertThat("The networks", findAllNetworks(), is(existingNetworks)); + assertThat(findAllNetworks()).as("The networks").isEqualTo(existingNetworks); } private List findAllNetworks() { diff --git a/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java b/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java index 7041e74912a..b139ece5410 100644 --- a/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java @@ -10,8 +10,7 @@ import java.io.IOException; import java.util.Map; -import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class CopyFileToContainerTest { @@ -28,7 +27,7 @@ public void checkFileCopied() throws IOException, InterruptedException { ) { container.start(); String filesList = container.execInContainer("ls", "/tmp/mappable-resource").getStdout(); - assertTrue("file list contains the file", filesList.contains(fileName)); + assertThat(filesList).as("file list contains the file").contains(fileName); } } @@ -41,7 +40,7 @@ public void shouldUseCopyForReadOnlyClasspathResources() throws Exception { ) { container.start(); String filesList = container.execInContainer("ls", "/tmp/mappable-resource").getStdout(); - assertTrue("file list contains the file", filesList.contains(fileName)); + assertThat(filesList).as("file list contains the file").contains(fileName); } } @@ -55,11 +54,11 @@ public void shouldUseCopyOnlyWithReadOnlyClasspathResources() { .withClasspathResourceMapping(resource, "/readWrite", BindMode.READ_WRITE); Map copyMap = container.getCopyToFileContainerPathMap(); - assertTrue("uses copy for read-only", copyMap.containsValue("/readOnly")); - assertTrue("uses copy for read-only and no Selinux", copyMap.containsValue("/readOnlyNoSelinux")); + assertThat(copyMap).as("uses copy for read-only").containsValue("/readOnly"); + assertThat(copyMap).as("uses copy for read-only and no Selinux").containsValue("/readOnlyNoSelinux"); - assertFalse("uses mount for read-only with Selinux", copyMap.containsValue("/readOnlyShared")); - assertFalse("uses mount for read-write", copyMap.containsValue("/readWrite")); + assertThat(copyMap).as("uses mount for read-only with Selinux").doesNotContainValue("/readOnlyShared"); + assertThat(copyMap).as("uses mount for read-write").doesNotContainValue("/readWrite"); } @Test @@ -72,7 +71,7 @@ public void shouldCreateFoldersStructureWithCopy() throws Exception { ) { container.start(); String filesList = container.execInContainer("ls", "/a/b/c/").getStdout(); - assertTrue("file list contains the file", filesList.contains("file")); + assertThat(filesList).as("file list contains the file").contains("file"); } } } diff --git a/core/src/test/java/org/testcontainers/junit/DependenciesTest.java b/core/src/test/java/org/testcontainers/junit/DependenciesTest.java index 78e55335d9a..fd58dc4335f 100644 --- a/core/src/test/java/org/testcontainers/junit/DependenciesTest.java +++ b/core/src/test/java/org/testcontainers/junit/DependenciesTest.java @@ -2,7 +2,6 @@ import lombok.Getter; import org.junit.Test; -import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.TestImages; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; @@ -17,6 +16,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.assertj.core.api.Assertions.assertThat; + public class DependenciesTest { @Test @@ -31,8 +32,8 @@ public void shouldWorkWithSimpleDependency() { container.start(); } - VisibleAssertions.assertEquals("Started once", 1, startable.getStartInvocationCount().intValue()); - VisibleAssertions.assertEquals("Does not trigger .stop()", 0, startable.getStopInvocationCount().intValue()); + assertThat(startable.getStartInvocationCount().intValue()).as("Started once").isEqualTo(1); + assertThat(startable.getStopInvocationCount().intValue()).as("Does not trigger .stop()").isZero(); } @Test @@ -48,8 +49,8 @@ public void shouldWorkWithMutlipleDependencies() { container.start(); } - VisibleAssertions.assertEquals("Startable1 started once", 1, startable1.getStartInvocationCount().intValue()); - VisibleAssertions.assertEquals("Startable2 started once", 1, startable2.getStartInvocationCount().intValue()); + assertThat(startable1.getStartInvocationCount().intValue()).as("Startable1 started once").isEqualTo(1); + assertThat(startable2.getStartInvocationCount().intValue()).as("Startable2 started once").isEqualTo(1); } @Test @@ -70,8 +71,8 @@ public void shouldStartEveryTime() { container.start(); } - VisibleAssertions.assertEquals("Started multiple times", 3, startable.getStartInvocationCount().intValue()); - VisibleAssertions.assertEquals("Does not trigger .stop()", 0, startable.getStopInvocationCount().intValue()); + assertThat(startable.getStartInvocationCount().intValue()).as("Started multiple times").isEqualTo(3); + assertThat(startable.getStopInvocationCount().intValue()).as("Does not trigger .stop()").isZero(); } @Test @@ -92,17 +93,11 @@ public void shouldStartTransitiveDependencies() { container.stop(); } - VisibleAssertions.assertEquals("Root started", 1, startable.getStartInvocationCount().intValue()); - VisibleAssertions.assertEquals( - "Transitive started", - 1, - transitiveStartable.getStartInvocationCount().intValue() - ); - VisibleAssertions.assertEquals( - "Transitive of transitive started", - 1, - transitiveOfTransitiveStartable.getStartInvocationCount().intValue() - ); + assertThat(startable.getStartInvocationCount().intValue()).as("Root started").isEqualTo(1); + assertThat(transitiveStartable.getStartInvocationCount().intValue()).as("Transitive started").isEqualTo(1); + assertThat(transitiveOfTransitiveStartable.getStartInvocationCount().intValue()) + .as("Transitive of transitive started") + .isEqualTo(1); } @Test @@ -122,10 +117,10 @@ public void shouldHandleDiamondDependencies() throws Exception { Startables.deepStart(Stream.of(d)).get(1, TimeUnit.SECONDS); - VisibleAssertions.assertEquals("A started", 1, a.getStartInvocationCount().intValue()); - VisibleAssertions.assertEquals("B started", 1, b.getStartInvocationCount().intValue()); - VisibleAssertions.assertEquals("C started", 1, c.getStartInvocationCount().intValue()); - VisibleAssertions.assertEquals("D started", 1, d.getStartInvocationCount().intValue()); + assertThat(a.getStartInvocationCount().intValue()).as("A started").isEqualTo(1); + assertThat(b.getStartInvocationCount().intValue()).as("B started").isEqualTo(1); + assertThat(c.getStartInvocationCount().intValue()).as("C started").isEqualTo(1); + assertThat(d.getStartInvocationCount().intValue()).as("D started").isEqualTo(1); } @Test diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerScalingTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerScalingTest.java index 651696696c7..dc0187f5251 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerScalingTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerScalingTest.java @@ -7,7 +7,7 @@ import java.io.File; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; /** * Created by rnorth on 08/08/2015. @@ -47,7 +47,7 @@ public void simpleTest() { for (int i = 0; i < 3; i++) { clients[i].incr("somekey"); - assertEquals("Each redis instance is separate", "1", clients[i].get("somekey")); + assertThat(clients[i].get("somekey")).as("Each redis instance is separate").isEqualTo("1"); } } } diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java index c49663a4e35..c610fc584af 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java @@ -12,10 +12,7 @@ import java.util.Collections; import java.util.Optional; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * Created by rnorth on 08/08/2015. @@ -37,35 +34,31 @@ protected DockerComposeContainer getEnvironment() { @Test public void testGetServicePort() { int serviceWithInstancePort = environment.getServicePort("redis_1", REDIS_PORT); - assertNotNull("Port is set for service with instance number", serviceWithInstancePort); + assertThat(serviceWithInstancePort).as("Port is set for service with instance number").isNotNull(); int serviceWithoutInstancePort = environment.getServicePort("redis", REDIS_PORT); - assertNotNull("Port is set for service with instance number", serviceWithoutInstancePort); - assertEquals("Service ports are the same", serviceWithInstancePort, serviceWithoutInstancePort); + assertThat(serviceWithoutInstancePort).as("Port is set for service with instance number").isNotNull(); + assertThat(serviceWithoutInstancePort).as("Service ports are the same").isEqualTo(serviceWithInstancePort); } @Test public void shouldRetrieveContainerByServiceName() { String existingServiceName = "db_1"; Optional result = environment.getContainerByServiceName(existingServiceName); - assertTrue( - String.format("Container should be found by service name %s", existingServiceName), - result.isPresent() - ); - assertEquals( - "Mapped port for result container was wrong, probably wrong container found", - result.get().getExposedPorts(), - Collections.singletonList(3306) - ); + assertThat(result) + .as(String.format("Container should be found by service name %s", existingServiceName)) + .isPresent(); + assertThat(Collections.singletonList(3306)) + .as("Mapped port for result container was wrong, probably wrong container found") + .isEqualTo(result.get().getExposedPorts()); } @Test public void shouldReturnEmptyResultOnNoneExistingService() { String notExistingServiceName = "db_256"; Optional result = environment.getContainerByServiceName(notExistingServiceName); - assertFalse( - String.format("No container should be found under service name %s", notExistingServiceName), - result.isPresent() - ); + assertThat(result) + .as(String.format("No container should be found under service name %s", notExistingServiceName)) + .isNotPresent(); } @Test @@ -87,6 +80,6 @@ public void shouldCreateContainerWhenFileNotPrefixedWithPath() throws IOExceptio final DockerComposeContainer dockerComposeContainer = new DockerComposeContainer<>( filePathNotStartWithDotSlash ); - assertNotNull("Container could not be created using docker compose file", dockerComposeContainer); + assertThat(dockerComposeContainer).as("Container could not be created using docker compose file").isNotNull(); } } diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithBuildTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithBuildTest.java index e0705a2c73b..ea7e35689d0 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithBuildTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithBuildTest.java @@ -13,7 +13,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Stream; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; @RunWith(Parameterized.class) public class DockerComposeContainerWithBuildTest { @@ -59,11 +59,13 @@ public void performTest() { builtImageName.set(imageNameForRunningContainer("_customredis_1")); final boolean isBuiltImagePresentWhileRunning = isImagePresent(builtImageName.get()); - assertEquals("the built image is present while running", true, isBuiltImagePresentWhileRunning); + assertThat(isBuiltImagePresentWhileRunning).as("the built image is present while running").isEqualTo(true); pulledImageName.set(imageNameForRunningContainer("_normalredis_1")); final boolean isPulledImagePresentWhileRunning = isImagePresent(pulledImageName.get()); - assertEquals("the pulled image is present while running", true, isPulledImagePresentWhileRunning); + assertThat(isPulledImagePresentWhileRunning) + .as("the pulled image is present while running") + .isEqualTo(true); } Unreliables.retryUntilSuccess( @@ -71,11 +73,9 @@ public void performTest() { TimeUnit.SECONDS, () -> { final boolean isBuiltImagePresentAfterRunning = isImagePresent(builtImageName.get()); - assertEquals( - "the built image is not present after running", - shouldBuiltImageBePresentAfterRunning, - isBuiltImagePresentAfterRunning - ); + assertThat(isBuiltImagePresentAfterRunning) + .as("the built image is not present after running") + .isEqualTo(shouldBuiltImageBePresentAfterRunning); return null; } ); @@ -85,11 +85,9 @@ public void performTest() { TimeUnit.SECONDS, () -> { final boolean isPulledImagePresentAfterRunning = isImagePresent(pulledImageName.get()); - assertEquals( - "the pulled image is present after running", - shouldPulledImageBePresentAfterRunning, - isPulledImagePresentAfterRunning - ); + assertThat(isPulledImagePresentAfterRunning) + .as("the pulled image is present after running") + .isEqualTo(shouldPulledImageBePresentAfterRunning); return null; } ); diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeErrorHandlingTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeErrorHandlingTest.java index 51f09bdb095..63383bb7175 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeErrorHandlingTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeErrorHandlingTest.java @@ -1,24 +1,26 @@ package org.testcontainers.junit; import org.junit.Test; -import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.containers.DockerComposeContainer; import java.io.File; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; + public class DockerComposeErrorHandlingTest { @Test public void simpleTest() { - VisibleAssertions.assertThrows( - "starting with an invalid docker-compose file throws an exception", - IllegalArgumentException.class, - () -> { + assertThat( + catchThrowable(() -> { DockerComposeContainer environment = new DockerComposeContainer( new File("src/test/resources/invalid-compose.yml") ) .withExposedService("something", 123); - } - ); + }) + ) + .as("starting with an invalid docker-compose file throws an exception") + .isInstanceOf(IllegalArgumentException.class); } } diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposePassthroughTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposePassthroughTest.java index c026bcaa741..10a26306248 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposePassthroughTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposePassthroughTest.java @@ -13,9 +13,7 @@ import java.util.Arrays; import java.util.Objects; -import static org.hamcrest.Matchers.hasItem; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; +import static org.assertj.core.api.Assertions.assertThat; /** * Created by rnorth on 11/06/2016. @@ -41,16 +39,14 @@ public void testContainerInstanceProperties() { final ContainerState container = waitStrategy.getContainer(); //check environment variable was set - assertThat( - "Environment variable set correctly", - Arrays.asList(Objects.requireNonNull(container.getContainerInfo().getConfig().getEnv())), - hasItem("bar=bar") - ); + assertThat(Arrays.asList(Objects.requireNonNull(container.getContainerInfo().getConfig().getEnv()))) + .as("Environment variable set correctly") + .containsOnlyOnce("bar=bar"); //check other container properties - assertNotNull("Container id is not null", container.getContainerId()); - assertNotNull("Port mapped", container.getMappedPort(3000)); - assertThat("Exposed Ports", container.getExposedPorts(), hasItem(3000)); + assertThat(container.getContainerId()).as("Container id is not null").isNotNull(); + assertThat(container.getMappedPort(3000)).as("Port mapped").isNotNull(); + assertThat(container.getExposedPorts()).containsExactly(3000); } /* diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeServiceTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeServiceTest.java index 5e6ba4cc30f..ccca85407f3 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeServiceTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeServiceTest.java @@ -6,7 +6,7 @@ import java.io.File; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; +import static org.assertj.core.api.Assertions.assertThat; public class DockerComposeServiceTest extends BaseDockerComposeTest { @@ -29,6 +29,6 @@ public void testDbIsNotStarting() { @Test public void testRedisIsStarting() { - assertNotNull("Redis server started", environment.getServicePort("redis_1", REDIS_PORT)); + assertThat(environment.getServicePort("redis_1", REDIS_PORT)).as("Redis server started").isNotNull(); } } diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeWaitStrategyTest.java index a30c716964e..b110e776285 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeWaitStrategyTest.java @@ -5,7 +5,6 @@ import org.junit.Test; import org.junit.runner.Description; import org.mockito.Mockito; -import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.containers.DockerComposeContainer; import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.containers.wait.strategy.WaitStrategy; @@ -15,6 +14,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; +import static org.assertj.core.api.Assertions.fail; public class DockerComposeWaitStrategyTest { @@ -38,11 +38,8 @@ public void testWaitOnListeningPort() { try { environment.starting(Description.createTestDescription(Object.class, "name")); - VisibleAssertions.pass("Docker compose should start after waiting for listening port"); } catch (RuntimeException e) { - VisibleAssertions.fail( - "Docker compose should start after waiting for listening port with failed with: " + e - ); + fail("Docker compose should start after waiting for listening port with failed with: " + e); } } @@ -55,11 +52,8 @@ public void testWaitOnMultipleStrategiesPassing() { try { environment.starting(Description.createTestDescription(Object.class, "name")); - VisibleAssertions.pass("Docker compose should start after waiting for listening port"); } catch (RuntimeException e) { - VisibleAssertions.fail( - "Docker compose should start after waiting for listening port with failed with: " + e - ); + fail("Docker compose should start after waiting for listening port with failed with: " + e); } } @@ -70,11 +64,9 @@ public void testWaitingFails() { REDIS_PORT, Wait.forHttp("/test").withStartupTimeout(Duration.ofSeconds(10)) ); - VisibleAssertions.assertThrows( - "waiting on an invalid http path times out", - RuntimeException.class, - () -> environment.starting(Description.createTestDescription(Object.class, "name")) - ); + assertThat(catchThrowable(() -> environment.starting(Description.createTestDescription(Object.class, "name")))) + .as("waiting on an invalid http path times out") + .isInstanceOf(RuntimeException.class); } @Test @@ -91,11 +83,9 @@ public void testWaitOnOneOfMultipleStrategiesFailing() { ) .withTailChildContainers(true); - VisibleAssertions.assertThrows( - "waiting on one failing strategy to time out", - RuntimeException.class, - () -> environment.starting(Description.createTestDescription(Object.class, "name")) - ); + assertThat(catchThrowable(() -> environment.starting(Description.createTestDescription(Object.class, "name")))) + .as("waiting on one failing strategy to time out") + .isInstanceOf(RuntimeException.class); } @Test diff --git a/core/src/test/java/org/testcontainers/junit/DockerNetworkModeTest.java b/core/src/test/java/org/testcontainers/junit/DockerNetworkModeTest.java index 7962b87e157..b048e4716fd 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerNetworkModeTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerNetworkModeTest.java @@ -7,8 +7,7 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * Simple tests of named network modes - more may be possible, but may not be reproducible @@ -28,8 +27,8 @@ public void testNoNetworkContainer() { container.start(); NetworkSettings networkSettings = container.getContainerInfo().getNetworkSettings(); - assertEquals("only one network is set", 1, networkSettings.getNetworks().size()); - assertTrue("network is 'none'", networkSettings.getNetworks().containsKey("none")); + assertThat(networkSettings.getNetworks()).as("only one network is set").hasSize(1); + assertThat(networkSettings.getNetworks()).as("network is 'none'").containsKey("none"); } } @@ -44,8 +43,8 @@ public void testHostNetworkContainer() { container.start(); NetworkSettings networkSettings = container.getContainerInfo().getNetworkSettings(); - assertEquals("only one network is set", 1, networkSettings.getNetworks().size()); - assertTrue("network is 'host'", networkSettings.getNetworks().containsKey("host")); + assertThat(networkSettings.getNetworks()).as("only one network is set").hasSize(1); + assertThat(networkSettings.getNetworks()).as("network is 'host'").containsKey("host"); } } } diff --git a/core/src/test/java/org/testcontainers/junit/DockerfileContainerTest.java b/core/src/test/java/org/testcontainers/junit/DockerfileContainerTest.java index 715ba3f64a1..9ed143aca9c 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerfileContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerfileContainerTest.java @@ -11,8 +11,7 @@ import java.io.IOException; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * Simple test case / demonstration of creating a fresh container image from a Dockerfile DSL @@ -40,15 +39,14 @@ public void simpleDslTest() throws IOException { HttpGet get = new HttpGet(address); try (CloseableHttpResponse response = httpClient.execute(get)) { - assertEquals( - "A container built from a dockerfile can run nginx as expected, and returns a good status code", - 200, - response.getStatusLine().getStatusCode() - ); - assertTrue( - "A container built from a dockerfile can run nginx as expected, and returns an expected Server header", - response.getHeaders("Server")[0].getValue().contains("nginx") - ); + assertThat(response.getStatusLine().getStatusCode()) + .as("A container built from a dockerfile can run nginx as expected, and returns a good status code") + .isEqualTo(200); + assertThat(response.getHeaders("Server")[0].getValue()) + .as( + "A container built from a dockerfile can run nginx as expected, and returns an expected Server header" + ) + .contains("nginx"); } } } diff --git a/core/src/test/java/org/testcontainers/junit/DockerfileTest.java b/core/src/test/java/org/testcontainers/junit/DockerfileTest.java index df7bc417688..86292bcf9ac 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerfileTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerfileTest.java @@ -16,8 +16,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import static org.rnorth.visibleassertions.VisibleAssertions.pass; - public class DockerfileTest { private static final Logger LOGGER = LoggerFactory.getLogger(DockerfileTest.class); @@ -130,8 +128,6 @@ protected void verifyImage(ImageFromDockerfile image) { try { container.start(); - - pass("Should start from Dockerfile"); } finally { container.stop(); } diff --git a/core/src/test/java/org/testcontainers/junit/FileOperationsTest.java b/core/src/test/java/org/testcontainers/junit/FileOperationsTest.java index e61721f80c4..d9655443480 100644 --- a/core/src/test/java/org/testcontainers/junit/FileOperationsTest.java +++ b/core/src/test/java/org/testcontainers/junit/FileOperationsTest.java @@ -3,7 +3,6 @@ import com.github.dockerjava.api.exception.NotFoundException; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; -import org.assertj.core.api.Assertions; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -16,7 +15,7 @@ import java.io.IOException; import static org.assertj.core.api.Assertions.assertThat; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThatThrownBy; public class FileOperationsTest { @@ -37,7 +36,7 @@ public void copyFileToContainerFileTest() throws Exception { alpineCopyToContainer.copyFileFromContainer("/test.txt", actualFile.getPath()); File expectedFile = new File(mountableFile.getResolvedPath()); - assertTrue("Files aren't same ", FileUtils.contentEquals(expectedFile, actualFile)); + assertThat(FileUtils.contentEquals(expectedFile, actualFile)).as("Files aren't same ").isTrue(); } } @@ -55,7 +54,7 @@ public void copyFileToContainerFolderTest() throws Exception { alpineCopyToContainer.copyFileFromContainer("/home/test_copy_to_container.txt", actualFile.getPath()); File expectedFile = new File(mountableFile.getResolvedPath()); - assertTrue("Files aren't same ", FileUtils.contentEquals(expectedFile, actualFile)); + assertThat(FileUtils.contentEquals(expectedFile, actualFile)).as("Files aren't same ").isTrue(); } } @@ -73,7 +72,7 @@ public void copyFolderToContainerFolderTest() throws Exception { alpineCopyToContainer.copyFileFromContainer("/home/test/test-resource.txt", actualFile.getPath()); File expectedFile = new File(mountableFile.getResolvedPath() + "/test-resource.txt"); - assertTrue("Files aren't same ", FileUtils.contentEquals(expectedFile, actualFile)); + assertThat(FileUtils.contentEquals(expectedFile, actualFile)).as("Files aren't same ").isTrue(); } } @@ -102,23 +101,21 @@ public void shouldCopyFileFromContainerTest() throws IOException { alpineCopyToContainer.copyFileFromContainer("/home/test_copy_to_container.txt", actualFile.getPath()); File expectedFile = new File(mountableFile.getResolvedPath()); - assertTrue("Files aren't same ", FileUtils.contentEquals(expectedFile, actualFile)); + assertThat(FileUtils.contentEquals(expectedFile, actualFile)).as("Files aren't same ").isTrue(); } } @Test public void copyFileOperationsShouldFailWhenNotStartedTest() { try (GenericContainer container = new GenericContainer<>(TestImages.ALPINE_IMAGE).withCommand("top")) { - Assertions - .assertThatThrownBy(() -> { + assertThatThrownBy(() -> { MountableFile mountableFile = MountableFile.forClasspathResource("test_copy_to_container.txt"); container.copyFileToContainer(mountableFile, "/home/test.txt"); }) .isInstanceOf(IllegalStateException.class) .hasMessageContaining("can only be used with created / running container"); - Assertions - .assertThatThrownBy(() -> { + assertThatThrownBy(() -> { container.copyFileFromContainer("/home/test_copy_to_container.txt", IOUtils::toByteArray); }) .isInstanceOf(IllegalStateException.class) @@ -137,7 +134,7 @@ public void shouldCopyFileFromExitedContainerTest() throws IOException { assertThat( container.getDockerClient().waitContainerCmd(container.getContainerId()).start().awaitStatusCode() ) - .isEqualTo(0); + .isZero(); container.copyFileFromContainer("/home/file_in_container.txt", IOUtils::toByteArray); diff --git a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java index 8c696585902..4ba491d83fd 100644 --- a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java +++ b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java @@ -47,12 +47,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import static org.hamcrest.Matchers.equalTo; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; /** * Tests for GenericContainerRules @@ -158,9 +154,9 @@ public static void setupContent() throws FileNotFoundException { @Test public void testIsRunning() { try (GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE).withCommand("top")) { - assertFalse("Container is not started and not running", container.isRunning()); + assertThat(container.isRunning()).as("Container is not started and not running").isFalse(); container.start(); - assertTrue("Container is started and running", container.isRunning()); + assertThat(container.isRunning()).as("Container is started and running").isTrue(); } } @@ -175,15 +171,13 @@ public void withTmpFsTest() throws Exception { // check file doesn't exist String path = "/testtmpfs/test.file"; Container.ExecResult execResult = container.execInContainer("ls", path); - assertEquals( - "tmpfs inside container works fine", - execResult.getStderr(), - "ls: /testtmpfs/test.file: No such file or directory\n" - ); + assertThat(execResult.getStderr()) + .as("tmpfs inside container works fine") + .isEqualTo("ls: /testtmpfs/test.file: No such file or directory\n"); // touch && check file does exist container.execInContainer("touch", path); execResult = container.execInContainer("ls", path); - assertEquals("tmpfs inside container works fine", execResult.getStdout(), path + "\n"); + assertThat(execResult.getStdout()).as("tmpfs inside container works fine").isEqualTo(path + "\n"); } } @@ -221,8 +215,7 @@ public void handleDelivery( channel.basicPublish(RABBIQMQ_TEST_EXCHANGE, RABBITMQ_TEST_ROUTING_KEY, null, RABBITMQ_TEST_MESSAGE.getBytes()); // check the message was received - assertTrue( - "The message was received", + assertThat( Unreliables.retryUntilSuccess( 5, TimeUnit.SECONDS, @@ -233,7 +226,9 @@ public void handleDelivery( return true; } ) - ); + ) + .as("The message was received") + .isTrue(); } @Test @@ -246,21 +241,21 @@ public void simpleMongoDbTest() { collection.insertOne(doc); Document doc2 = collection.find(new Document("name", "foo")).first(); - assertEquals("A record can be inserted into and retrieved from MongoDB", 1, doc2.get("value")); + assertThat(doc2.get("value")).as("A record can be inserted into and retrieved from MongoDB").isEqualTo(1); } @Test public void environmentAndCustomCommandTest() throws IOException { String line = getReaderForContainerPort80(alpineEnvVar).readLine(); - assertEquals("An environment variable can be passed into a command", "42", line); + assertThat(line).as("An environment variable can be passed into a command").isEqualTo("42"); } @Test public void environmentFromMapTest() throws IOException { String line = getReaderForContainerPort80(alpineEnvVarFromMap).readLine(); - assertEquals("Environment variables can be passed into a command from a map", "42 and 50", line); + assertThat(line).as("Environment variables can be passed into a command from a map").isEqualTo("42 and 50"); } @Test @@ -273,21 +268,21 @@ public void customLabelTest() { alpineCustomLabel.start(); Map labels = alpineCustomLabel.getCurrentContainerInfo().getConfig().getLabels(); - assertTrue("org.testcontainers label is present", labels.containsKey("org.testcontainers")); - assertTrue("our.custom label is present", labels.containsKey("our.custom")); - assertEquals("our.custom label value is label", labels.get("our.custom"), "label"); + assertThat(labels).as("org.testcontainers label is present").containsKey("org.testcontainers"); + assertThat(labels).as("our.custom label is present").containsKey("our.custom"); + assertThat(labels).as("our.custom label value is label").containsEntry("our.custom", "label"); } } @Test public void exceptionThrownWhenTryingToOverrideTestcontainersLabels() { - assertThrows( - "When trying to overwrite an 'org.testcontainers' label, withLabel() throws an exception", - IllegalArgumentException.class, - () -> { + assertThat( + catchThrowable(() -> { new GenericContainer<>(TestImages.ALPINE_IMAGE).withLabel("org.testcontainers.foo", "false"); - } - ); + }) + ) + .as("When trying to overwrite an 'org.testcontainers' label, withLabel() throws an exception") + .isInstanceOf(IllegalArgumentException.class); } @Test @@ -296,32 +291,24 @@ public void customClasspathResourceMappingTest() throws IOException { // in that case this test will fail. String line = getReaderForContainerPort80(alpineClasspathResource).readLine(); - assertEquals( - "Resource on the classpath can be mapped using calls to withClasspathResourceMapping", - "FOOBAR", - line - ); + assertThat(line) + .as("Resource on the classpath can be mapped using calls to withClasspathResourceMapping") + .isEqualTo("FOOBAR"); } @Test public void customClasspathResourceMappingWithSelinuxTest() throws IOException { String line = getReaderForContainerPort80(alpineClasspathResourceSelinux).readLine(); - assertEquals( - "Resource on the classpath can be mapped using calls to withClasspathResourceMappingSelinux", - "FOOBAR", - line - ); + assertThat(line) + .as("Resource on the classpath can be mapped using calls to withClasspathResourceMappingSelinux") + .isEqualTo("FOOBAR"); } @Test public void exceptionThrownWhenMappedPortNotFound() { - assertThrows( - "When the requested port is not mapped, getMappedPort() throws an exception", - IllegalArgumentException.class, - () -> { - return redis.getMappedPort(666); - } - ); + assertThat(catchThrowable(() -> redis.getMappedPort(666))) + .as("When the requested port is not mapped, getMappedPort() throws an exception") + .isInstanceOf(IllegalArgumentException.class); } protected static void writeStringToFile(File contentFolder, String filename, String string) @@ -342,14 +329,9 @@ public void failFastWhenContainerHaltsImmediately() { .withMinimumRunningDuration(Duration.ofMillis(100)); try { - assertThrows( - "When we start a container that halts immediately, an exception is thrown", - RetryCountExceededException.class, - () -> { - failsImmediately.start(); - return null; - } - ); + assertThat(catchThrowable(failsImmediately::start)) + .as("When we start a container that halts immediately, an exception is thrown") + .isInstanceOf(RetryCountExceededException.class); // Check how long it took, to verify that we ARE bailing out early. // Want to strike a balance here; too short and this test will fail intermittently @@ -357,10 +339,9 @@ public void failFastWhenContainerHaltsImmediately() { // what we're intending to test. int allowedSecondsToFailure = GenericContainer.CONTAINER_RUNNING_TIMEOUT_SEC / 2; long completedTimeMs = System.currentTimeMillis(); - assertTrue( - "container should not take long to start up", - completedTimeMs - startingTimeMs < 1000L * allowedSecondsToFailure - ); + assertThat(completedTimeMs - startingTimeMs < 1000L * allowedSecondsToFailure) + .as("container should not take long to start up") + .isTrue(); } finally { failsImmediately.stop(); } @@ -374,11 +355,10 @@ public void testExecInContainer() throws Exception { Assume.assumeTrue(TestEnvironment.dockerExecutionDriverSupportsExec()); final GenericContainer.ExecResult result = redis.execInContainer("redis-cli", "role"); - assertTrue( - "Output for \"redis-cli role\" command should start with \"master\"", - result.getStdout().startsWith("master") - ); - assertEquals("Stderr for \"redis-cli role\" command should be empty", "", result.getStderr()); + assertThat(result.getStdout()) + .as("Output for \"redis-cli role\" command should start with \"master\"") + .startsWith("master"); + assertThat(result.getStderr()).as("Stderr for \"redis-cli role\" command should be empty").isEmpty(); // We expect to reach this point for modern Docker versions. } @@ -396,7 +376,7 @@ public void extraHostTest() throws IOException { } Matcher matcher = Pattern.compile("^192.168.1.10\\s.*somehost", Pattern.MULTILINE).matcher(hosts.toString()); - assertTrue("The hosts file of container contains extra host", matcher.find()); + assertThat(matcher.find()).as("The hosts file of container contains extra host").isTrue(); } @Test @@ -414,12 +394,10 @@ public void createContainerCmdHookTest() { ) { container.start(); - assertEquals("Name is configured", "/" + randomName, container.getContainerInfo().getName()); - assertEquals( - "Command is configured", - "[redis-server, --port, 6379]", - Arrays.toString(container.getContainerInfo().getConfig().getCmd()) - ); + assertThat(container.getContainerInfo().getName()).as("Name is configured").isEqualTo("/" + randomName); + assertThat(Arrays.toString(container.getContainerInfo().getConfig().getCmd())) + .as("Command is configured") + .isEqualTo("[redis-server, --port, 6379]"); } } @@ -439,18 +417,18 @@ private BufferedReader getReaderForContainerPort80(GenericContainer container) { @Test public void addExposedPortAfterWithExposedPortsTest() { redis.addExposedPort(8987); - assertThat("Both ports should be exposed", redis.getExposedPorts().size(), equalTo(2)); - assertTrue("withExposedPort should be exposed", redis.getExposedPorts().contains(REDIS_PORT)); - assertTrue("addExposedPort should be exposed", redis.getExposedPorts().contains(8987)); + assertThat(redis.getExposedPorts()).as("Both ports should be exposed").hasSize(2); + assertThat(redis.getExposedPorts()).as("withExposedPort should be exposed").contains(REDIS_PORT); + assertThat(redis.getExposedPorts()).as("addExposedPort should be exposed").contains(8987); } @Test public void addingExposedPortTwiceShouldNotFail() { redis.addExposedPort(8987); redis.addExposedPort(8987); - assertThat("Both ports should be exposed", redis.getExposedPorts().size(), equalTo(2)); // 2 ports = de-duplicated port 8897 and original port 6379 - assertTrue("withExposedPort should be exposed", redis.getExposedPorts().contains(REDIS_PORT)); - assertTrue("addExposedPort should be exposed", redis.getExposedPorts().contains(8987)); + assertThat(redis.getExposedPorts()).as("Both ports should be exposed").hasSize(2); // 2 ports = de-duplicated port 8897 and original port 6379 + assertThat(redis.getExposedPorts()).as("withExposedPort should be exposed").contains(REDIS_PORT); + assertThat(redis.getExposedPorts()).as("addExposedPort should be exposed").contains(8987); } @Test @@ -463,7 +441,9 @@ public void sharedMemorySetTest() { containerWithSharedMemory.start(); HostConfig hostConfig = containerWithSharedMemory.getContainerInfo().getHostConfig(); - assertEquals("Shared memory not set on container", hostConfig.getShmSize(), 42L * FileUtils.ONE_MB); + assertThat(hostConfig.getShmSize()) + .as("Shared memory not set on container") + .isEqualTo(42L * FileUtils.ONE_MB); } } } diff --git a/core/src/test/java/org/testcontainers/junit/NonExistentImagePullTest.java b/core/src/test/java/org/testcontainers/junit/NonExistentImagePullTest.java index 33909e2ed27..aff9b29cbe7 100644 --- a/core/src/test/java/org/testcontainers/junit/NonExistentImagePullTest.java +++ b/core/src/test/java/org/testcontainers/junit/NonExistentImagePullTest.java @@ -5,7 +5,8 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.utility.DockerImageName; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; /** * Created by rnorth on 20/03/2016. @@ -14,12 +15,12 @@ public class NonExistentImagePullTest { @Test(timeout = 60_000L) public void pullingNonExistentImageFailsGracefully() { - assertThrows( - "Pulling a nonexistent container will cause an exception to be thrown", - ContainerFetchException.class, - () -> { + assertThat( + catchThrowable(() -> { new GenericContainer<>(DockerImageName.parse("testcontainers/nonexistent:latest")).getDockerImageName(); - } - ); + }) + ) + .as("Pulling a nonexistent container will cause an exception to be thrown") + .isInstanceOf(ContainerFetchException.class); } } diff --git a/core/src/test/java/org/testcontainers/junit/OutputStreamTest.java b/core/src/test/java/org/testcontainers/junit/OutputStreamTest.java index 104fb78fe48..17b1d247cbb 100644 --- a/core/src/test/java/org/testcontainers/junit/OutputStreamTest.java +++ b/core/src/test/java/org/testcontainers/junit/OutputStreamTest.java @@ -15,9 +15,8 @@ import java.util.concurrent.TimeoutException; import java.util.function.Consumer; -import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; /** * Simple test for following container output. @@ -49,10 +48,8 @@ public void testFetchStdoutWithTimeout() { container.followOutput(consumer, OutputFrame.OutputType.STDOUT); - assertThrows( - "a TimeoutException should be thrown", - TimeoutException.class, - () -> { + assertThat( + catchThrowable(() -> { consumer.waitUntil( frame -> { return ( @@ -62,9 +59,10 @@ public void testFetchStdoutWithTimeout() { 2, TimeUnit.SECONDS ); - return true; - } - ); + }) + ) + .as("a TimeoutException should be thrown") + .isInstanceOf(TimeoutException.class); } @Test(timeout = 60_000L) @@ -102,8 +100,8 @@ public void testToStringConsumer() throws TimeoutException { waitingConsumer.waitUntilEnd(30, TimeUnit.SECONDS); String utf8String = toStringConsumer.toUtf8String(); - assertTrue("the expected first value was found", utf8String.contains("seq=1")); - assertTrue("the expected last value was found", utf8String.contains("seq=4")); - assertFalse("a non-expected value was found", utf8String.contains("seq=42")); + assertThat(utf8String).as("the expected first value was found").contains("seq=1"); + assertThat(utf8String).as("the expected last value was found").contains("seq=4"); + assertThat(utf8String).as("a non-expected value was found").doesNotContain("seq=42"); } } diff --git a/core/src/test/java/org/testcontainers/junit/OutputStreamWithTTYTest.java b/core/src/test/java/org/testcontainers/junit/OutputStreamWithTTYTest.java index a87dbef3990..c340f26d3fb 100644 --- a/core/src/test/java/org/testcontainers/junit/OutputStreamWithTTYTest.java +++ b/core/src/test/java/org/testcontainers/junit/OutputStreamWithTTYTest.java @@ -16,9 +16,8 @@ import java.util.concurrent.TimeoutException; import java.util.function.Consumer; -import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; @Slf4j public class OutputStreamWithTTYTest { @@ -39,7 +38,9 @@ public void testFetchStdout() throws TimeoutException { container.followOutput(consumer, OutputFrame.OutputType.STDOUT); consumer.waitUntil( - frame -> frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().contains("home"), + frame -> { + return frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().contains("home"); + }, 4, TimeUnit.SECONDS ); @@ -51,18 +52,21 @@ public void testFetchStdoutWithTimeout() { container.followOutput(consumer, OutputFrame.OutputType.STDOUT); - assertThrows( - "a TimeoutException should be thrown", - TimeoutException.class, - () -> { + assertThat( + catchThrowable(() -> { consumer.waitUntil( - frame -> frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().contains("qqq"), + frame -> { + return ( + frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().contains("qqq") + ); + }, 1, TimeUnit.SECONDS ); - return true; - } - ); + }) + ) + .as("a TimeoutException should be thrown") + .isInstanceOf(TimeoutException.class); } @Test @@ -100,8 +104,8 @@ public void testToStringConsumer() throws TimeoutException { waitingConsumer.waitUntilEnd(4, TimeUnit.SECONDS); String utf8String = toStringConsumer.toUtf8String(); - assertTrue("the expected first value was found", utf8String.contains("home")); - assertTrue("the expected last value was found", utf8String.contains("media")); - assertFalse("a non-expected value was found", utf8String.contains("qqq")); + assertThat(utf8String).as("the expected first value was found").contains("home"); + assertThat(utf8String).as("the expected last value was found").contains("media"); + assertThat(utf8String).as("a non-expected value was found").doesNotContain("qqq"); } } diff --git a/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java b/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java index 7c176f14a1a..8437123f97a 100644 --- a/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java @@ -7,7 +7,7 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.images.builder.ImageFromDockerfile; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * Simple test case / demonstration of creating a fresh container image from a Dockerfile DSL when the test @@ -52,6 +52,6 @@ public static Object[][] data() { public void simpleTest() throws Exception { final String release = container.execInContainer("cat", "/etc/alpine-release").getStdout(); - assertTrue("/etc/alpine-release starts with " + expectedVersion, release.startsWith(expectedVersion)); + assertThat(release).as("/etc/alpine-release starts with " + expectedVersion).startsWith(expectedVersion); } } diff --git a/core/src/test/java/org/testcontainers/junit/WorkingDirectoryTest.java b/core/src/test/java/org/testcontainers/junit/WorkingDirectoryTest.java index 0be4022c171..05f65e1f493 100644 --- a/core/src/test/java/org/testcontainers/junit/WorkingDirectoryTest.java +++ b/core/src/test/java/org/testcontainers/junit/WorkingDirectoryTest.java @@ -6,7 +6,7 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * Created by rnorth on 26/07/2016. @@ -23,8 +23,8 @@ public class WorkingDirectoryTest { public void checkOutput() { String listing = container.getLogs(); - assertTrue("Directory listing contains expected /etc content", listing.contains("hostname")); - assertTrue("Directory listing contains expected /etc content", listing.contains("init.d")); - assertTrue("Directory listing contains expected /etc content", listing.contains("passwd")); + assertThat(listing).as("Directory listing contains expected /etc content").contains("hostname"); + assertThat(listing).as("Directory listing contains expected /etc content").contains("init.d"); + assertThat(listing).as("Directory listing contains expected /etc content").contains("passwd"); } } diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java index 530638f0f70..45809f95627 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java @@ -3,7 +3,6 @@ import org.jetbrains.annotations.NotNull; import org.junit.Before; import org.rnorth.ducttape.RetryCountExceededException; -import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.TestImages; import org.testcontainers.containers.ContainerLaunchException; import org.testcontainers.containers.GenericContainer; @@ -12,6 +11,8 @@ import java.time.Duration; import java.util.concurrent.atomic.AtomicBoolean; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; import static org.junit.Assert.assertTrue; /** @@ -109,11 +110,9 @@ protected void waitUntilReadyAndTimeout(String shellCommand) { */ protected void waitUntilReadyAndTimeout(GenericContainer container) { // start() blocks until successful or timeout - VisibleAssertions.assertThrows( - "an exception is thrown when timeout occurs (" + WAIT_TIMEOUT_MILLIS + "ms)", - ContainerLaunchException.class, - container::start - ); + assertThat(catchThrowable(container::start)) + .as("an exception is thrown when timeout occurs (" + WAIT_TIMEOUT_MILLIS + "ms)") + .isInstanceOf(ContainerLaunchException.class); } /** diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/HostPortWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/HostPortWaitStrategyTest.java index 4b5f43a4d4b..d5a0ba6929d 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/HostPortWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/HostPortWaitStrategyTest.java @@ -8,8 +8,6 @@ import java.time.Duration; -import static org.rnorth.visibleassertions.VisibleAssertions.pass; - /** * Test wait strategy with overloaded waitingFor methods. * Other implementations of WaitStrategy are tested through backwards compatible wait strategy tests @@ -24,7 +22,5 @@ public class HostPortWaitStrategyTest { .waitingFor(Wait.forListeningPort().withStartupTimeout(Duration.ofSeconds(10))); @Test - public void testWaiting() { - pass("Container starts after waiting"); - } + public void testWaiting() {} } diff --git a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java index bcf88d7be11..0e00dd87bf7 100644 --- a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java +++ b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java @@ -19,10 +19,10 @@ import java.nio.file.Path; import java.nio.file.Paths; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.when; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; /** * This test checks the integration between Testcontainers and an authenticated registry, but uses @@ -91,7 +91,7 @@ public void testThatAuthLocatorIsUsedForContainerCreation() { ) { container.start(); - assertTrue("container started following an authenticated pull", container.isRunning()); + assertThat(container.isRunning()).as("container started following an authenticated pull").isTrue(); } } @@ -111,7 +111,7 @@ public void testThatAuthLocatorIsUsedForDockerfileBuild() throws IOException { ) { container.start(); - assertTrue("container started following an authenticated pull", container.isRunning()); + assertThat(container.isRunning()).as("container started following an authenticated pull").isTrue(); } } @@ -133,10 +133,11 @@ public void testThatAuthLocatorIsUsedForDockerComposePull() throws IOException { try (final DockerComposeContainer compose = new DockerComposeContainer<>(tempFile.toFile())) { compose.start(); - assertTrue( - "container started following an authenticated pull", + assertThat( compose.getContainerByServiceName("privateservice_1").map(ContainerState::isRunning).orElse(false) - ); + ) + .as("container started following an authenticated pull") + .isTrue(); } } diff --git a/core/src/test/java/org/testcontainers/utility/ClasspathScannerTest.java b/core/src/test/java/org/testcontainers/utility/ClasspathScannerTest.java index 4ed86e3a8fa..e775a2cd492 100644 --- a/core/src/test/java/org/testcontainers/utility/ClasspathScannerTest.java +++ b/core/src/test/java/org/testcontainers/utility/ClasspathScannerTest.java @@ -10,10 +10,10 @@ import java.util.List; import java.util.stream.Collectors; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; public class ClasspathScannerTest { @@ -41,7 +41,7 @@ public void realClassLoaderLookupOccurs() { // look for a resource that we know exists only once final List foundURLs = ClasspathScanner.scanFor("expectedClasspathFile.txt").collect(Collectors.toList()); - assertEquals("Exactly one resource was found", 1, foundURLs.size()); + assertThat(foundURLs).as("Exactly one resource was found").hasSize(1); } @Test @@ -53,7 +53,7 @@ public void multipleResultsOnOneClassLoaderAreFound() throws IOException { final List foundURLs = ClasspathScanner .scanFor("someName", firstMockClassLoader) .collect(Collectors.toList()); - assertEquals("The expected URLs are found", Arrays.asList(FILE_A, FILE_B), foundURLs); + assertThat(foundURLs).as("The expected URLs are found").containsExactly(FILE_A, FILE_B); } @Test @@ -65,11 +65,9 @@ public void orderIsAlphabeticalForDeterminism() throws IOException { final List foundURLs = ClasspathScanner .scanFor("someName", firstMockClassLoader) .collect(Collectors.toList()); - assertEquals( - "The expected URLs are found in the expected order", - Arrays.asList(FILE_A, FILE_B, JAR_A, JAR_B), - foundURLs - ); + assertThat(foundURLs) + .as("The expected URLs are found in the expected order") + .containsExactly(FILE_A, FILE_B, JAR_A, JAR_B); } @Test @@ -92,6 +90,6 @@ public void multipleClassLoadersAreQueried() throws IOException { .scanFor("someName", firstMockClassLoader, secondMockClassLoader) .collect(Collectors.toList()); - assertEquals("The expected URLs are found", Arrays.asList(FILE_A, FILE_B, FILE_C), foundURLs); + assertThat(foundURLs).as("The expected URLs are found").containsExactly(FILE_A, FILE_B, FILE_C); } } diff --git a/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java b/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java index 46fd6894a02..6215f84520b 100644 --- a/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java +++ b/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java @@ -1,20 +1,15 @@ package org.testcontainers.utility; -import org.hamcrest.Description; -import org.hamcrest.Matcher; -import org.hamcrest.core.IsCollectionContaining; +import org.assertj.core.api.Condition; import org.junit.Test; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import org.testcontainers.images.builder.ImageFromDockerfile; import java.io.File; -import java.util.Arrays; +import java.util.function.Predicate; -import static org.hamcrest.Matchers.allOf; -import static org.hamcrest.Matchers.containsString; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class DirectoryTarResourceTest { @@ -40,88 +35,63 @@ public void simpleRecursiveFileTest() { final String results = container.getLogs(); - assertTrue( - "The container has a file that was copied in via a recursive copy", - results.contains("Used for DirectoryTarResourceTest") - ); + assertThat(results) + .as("The container has a file that was copied in via a recursive copy") + .contains("Used for DirectoryTarResourceTest"); } @Test public void simpleRecursiveFileWithPermissionTest() { - GenericContainer container = new GenericContainer( - new ImageFromDockerfile() - .withDockerfileFromBuilder(builder -> { - builder - .from("alpine:3.16") // - .copy("/tmp/foo", "/foo") - .cmd("ls", "-al", "/") - .build(); - }) - .withFileFromFile("/tmp/foo", new File("/mappable-resource/test-resource.txt"), 0754) - ) - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()); - - container.start(); - String listing = container.getLogs(); - - assertThat( - "Listing shows that file is copied with mode requested.", - Arrays.asList(listing.split("\\n")), - exactlyNItems(1, allOf(containsString("-rwxr-xr--"), containsString("foo"))) - ); + try ( + GenericContainer container = new GenericContainer( + new ImageFromDockerfile() + .withDockerfileFromBuilder(builder -> { + builder + .from("alpine:3.16") // + .copy("/tmp/foo", "/foo") + .cmd("ls", "-al", "/") + .build(); + }) + .withFileFromFile("/tmp/foo", new File("/mappable-resource/test-resource.txt"), 0754) + ) + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + ) { + container.start(); + String listing = container.getLogs(); + + Predicate condition = s -> s.contains("-rwxr-xr--") && s.contains("foo"); + assertThat(listing.split("\\n")) + .as("Listing shows that file is copied with mode requested.") + .haveAtLeastOne(new Condition<>(condition, "File not found in listing")); + } } @Test public void simpleRecursiveClasspathResourceTest() { // This test combines the copying of classpath resources from JAR files with the recursive TAR approach, to allow JARed classpath resources to be copied in to an image - GenericContainer container = new GenericContainer( - new ImageFromDockerfile() - .withDockerfileFromBuilder(builder -> { - builder - .from("alpine:3.16") // - .copy("/tmp/foo", "/foo") - .cmd("ls -lRt /foo") - .build(); - }) - .withFileFromClasspath("/tmp/foo", "/recursive/dir") - ) // here we use /org/junit as a directory that really should exist on the classpath - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()); - - container.start(); - - final String results = container.getLogs(); - - // ExternalResource.class is known to exist in a subdirectory of /org/junit so should be successfully copied in - assertTrue( - "The container has a file that was copied in via a recursive copy from a JAR resource", - results.contains("content.txt") - ); - } - - public static Matcher> exactlyNItems(final int n, Matcher elementMatcher) { - return new IsCollectionContaining(elementMatcher) { - @Override - protected boolean matchesSafely(Iterable collection, Description mismatchDescription) { - int count = 0; - boolean isPastFirst = false; - - for (Object item : collection) { - if (elementMatcher.matches(item)) { - count++; - } - if (isPastFirst) { - mismatchDescription.appendText(", "); - } - elementMatcher.describeMismatch(item, mismatchDescription); - isPastFirst = true; - } - - if (count != n) { - mismatchDescription.appendText(". Expected exactly " + n + " but got " + count); - } - return count == n; - } - }; + try ( + GenericContainer container = new GenericContainer( + new ImageFromDockerfile() + .withDockerfileFromBuilder(builder -> { + builder + .from("alpine:3.16") // + .copy("/tmp/foo", "/foo") + .cmd("ls -lRt /foo") + .build(); + }) + .withFileFromClasspath("/tmp/foo", "/recursive/dir") + ) // here we use /org/junit as a directory that really should exist on the classpath + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + ) { + container.start(); + + final String results = container.getLogs(); + + // ExternalResource.class is known to exist in a subdirectory of /org/junit so should be successfully copied in + assertThat(results) + .as("The container has a file that was copied in via a recursive copy from a JAR resource") + .contains("content.txt"); + } } } diff --git a/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java b/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java index f2ca26b8947..e9aac2ed475 100644 --- a/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java +++ b/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java @@ -4,9 +4,8 @@ import org.junit.Test; import org.junit.rules.ExpectedException; +import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.containsString; -import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; public class DockerImageNameCompatibilityTest { @@ -17,7 +16,7 @@ public class DockerImageNameCompatibilityTest { public void testPlainImage() { DockerImageName subject = DockerImageName.parse("foo"); - assertFalse("image name foo != bar", subject.isCompatibleWith(DockerImageName.parse("bar"))); + assertThat(subject.isCompatibleWith(DockerImageName.parse("bar"))).as("image name foo != bar").isFalse(); } @Test @@ -29,53 +28,66 @@ public void testNoTagTreatedAsWildcard() { The test is effectively making sure that 'no tag' is treated as a wildcard */ - assertFalse("foo:4.5.6 != foo:1.2.3", subject.isCompatibleWith(DockerImageName.parse("foo:1.2.3"))); - assertTrue("foo:4.5.6 ~= foo", subject.isCompatibleWith(DockerImageName.parse("foo"))); + assertThat(subject.isCompatibleWith(DockerImageName.parse("foo:1.2.3"))).as("foo:4.5.6 != foo:1.2.3").isFalse(); + assertThat(subject.isCompatibleWith(DockerImageName.parse("foo"))).as("foo:4.5.6 ~= foo").isTrue(); } @Test public void testImageWithAutomaticCompatibilityForFullPath() { DockerImageName subject = DockerImageName.parse("repo/foo:1.2.3"); - assertTrue("repo/foo:1.2.3 ~= repo/foo", subject.isCompatibleWith(DockerImageName.parse("repo/foo"))); + assertThat(subject.isCompatibleWith(DockerImageName.parse("repo/foo"))) + .as("repo/foo:1.2.3 ~= repo/foo") + .isTrue(); } @Test public void testImageWithClaimedCompatibility() { DockerImageName subject = DockerImageName.parse("foo").asCompatibleSubstituteFor("bar"); - assertTrue("foo(bar) ~= bar", subject.isCompatibleWith(DockerImageName.parse("bar"))); - assertFalse("foo(bar) != fizz", subject.isCompatibleWith(DockerImageName.parse("fizz"))); + assertThat(subject.isCompatibleWith(DockerImageName.parse("bar"))).as("foo(bar) ~= bar").isTrue(); + assertThat(subject.isCompatibleWith(DockerImageName.parse("fizz"))).as("foo(bar) != fizz").isFalse(); } @Test public void testImageWithClaimedCompatibilityAndVersion() { DockerImageName subject = DockerImageName.parse("foo:1.2.3").asCompatibleSubstituteFor("bar"); - assertTrue("foo:1.2.3(bar) ~= bar", subject.isCompatibleWith(DockerImageName.parse("bar"))); + assertThat(subject.isCompatibleWith(DockerImageName.parse("bar"))).as("foo:1.2.3(bar) ~= bar").isTrue(); } @Test public void testImageWithClaimedCompatibilityForFullPath() { DockerImageName subject = DockerImageName.parse("foo").asCompatibleSubstituteFor("registry/repo/bar"); - assertTrue( - "foo(registry/repo/bar) ~= registry/repo/bar", - subject.isCompatibleWith(DockerImageName.parse("registry/repo/bar")) - ); - assertFalse("foo(registry/repo/bar) != repo/bar", subject.isCompatibleWith(DockerImageName.parse("repo/bar"))); - assertFalse("foo(registry/repo/bar) != bar", subject.isCompatibleWith(DockerImageName.parse("bar"))); + assertThat(subject.isCompatibleWith(DockerImageName.parse("registry/repo/bar"))) + .as("foo(registry/repo/bar) ~= registry/repo/bar") + .isTrue(); + assertThat(subject.isCompatibleWith(DockerImageName.parse("repo/bar"))) + .as("foo(registry/repo/bar) != repo/bar") + .isFalse(); + assertThat(subject.isCompatibleWith(DockerImageName.parse("bar"))) + .as("foo(registry/repo/bar) != bar") + .isFalse(); } @Test public void testImageWithClaimedCompatibilityForVersion() { DockerImageName subject = DockerImageName.parse("foo").asCompatibleSubstituteFor("bar:1.2.3"); - assertTrue("foo(bar:1.2.3) ~= bar", subject.isCompatibleWith(DockerImageName.parse("bar"))); - assertTrue("foo(bar:1.2.3) ~= bar:1.2.3", subject.isCompatibleWith(DockerImageName.parse("bar:1.2.3"))); - assertFalse("foo(bar:1.2.3) != bar:0.0.1", subject.isCompatibleWith(DockerImageName.parse("bar:0.0.1"))); - assertFalse("foo(bar:1.2.3) != bar:2.0.0", subject.isCompatibleWith(DockerImageName.parse("bar:2.0.0"))); - assertFalse("foo(bar:1.2.3) != bar:1.2.4", subject.isCompatibleWith(DockerImageName.parse("bar:1.2.4"))); + assertThat(subject.isCompatibleWith(DockerImageName.parse("bar"))).as("foo(bar:1.2.3) ~= bar").isTrue(); + assertThat(subject.isCompatibleWith(DockerImageName.parse("bar:1.2.3"))) + .as("foo(bar:1.2.3) ~= bar:1.2.3") + .isTrue(); + assertThat(subject.isCompatibleWith(DockerImageName.parse("bar:0.0.1"))) + .as("foo(bar:1.2.3) != bar:0.0.1") + .isFalse(); + assertThat(subject.isCompatibleWith(DockerImageName.parse("bar:2.0.0"))) + .as("foo(bar:1.2.3) != bar:2.0.0") + .isFalse(); + assertThat(subject.isCompatibleWith(DockerImageName.parse("bar:1.2.4"))) + .as("foo(bar:1.2.3) != bar:1.2.4") + .isFalse(); } @Test diff --git a/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java b/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java index 98fd21064ed..b1a0cbc980f 100644 --- a/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java +++ b/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java @@ -4,9 +4,8 @@ import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import org.rnorth.visibleassertions.VisibleAssertions; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; @RunWith(Enclosed.class) public class DockerImageNameTest { @@ -122,50 +121,35 @@ public void testParsing() { canonicalName = unversionedPart + ":latest"; } - VisibleAssertions.context("For " + combined); - VisibleAssertions.context("Using single-arg constructor:", 2); - final DockerImageName imageName = DockerImageName.parse(combined); - assertEquals(combined + " has registry address: " + registry, registry, imageName.getRegistry()); - assertEquals( - combined + " has unversioned part: " + unversionedPart, - unversionedPart, - imageName.getUnversionedPart() - ); + assertThat(imageName.getRegistry()).as(combined + " has registry address: " + registry).isEqualTo(registry); + assertThat(imageName.getUnversionedPart()) + .as(combined + " has unversioned part: " + unversionedPart) + .isEqualTo(unversionedPart); if (version != null) { - assertEquals(combined + " has version part: " + version, version, imageName.getVersionPart()); + assertThat(imageName.getVersionPart()) + .as(combined + " has version part: " + version) + .isEqualTo(version); } else { - assertEquals( - combined + " has automatic 'latest' version specified", - "latest", - imageName.getVersionPart() - ); + assertThat(imageName.getVersionPart()) + .as(combined + " has automatic 'latest' version specified") + .isEqualTo("latest"); } - assertEquals( - combined + " has canonical name: " + canonicalName, - canonicalName, - imageName.asCanonicalNameString() - ); + assertThat(imageName.asCanonicalNameString()) + .as(combined + " has canonical name: " + canonicalName) + .isEqualTo(canonicalName); if (version != null) { - VisibleAssertions.context("Using two-arg constructor:", 2); - final DockerImageName imageNameFromSecondaryConstructor = new DockerImageName(unversionedPart, version); - assertEquals( - combined + " has registry address: " + registry, - registry, - imageNameFromSecondaryConstructor.getRegistry() - ); - assertEquals( - combined + " has unversioned part: " + unversionedPart, - unversionedPart, - imageNameFromSecondaryConstructor.getUnversionedPart() - ); - assertEquals( - combined + " has version part: " + version, - version, - imageNameFromSecondaryConstructor.getVersionPart() - ); + assertThat(imageNameFromSecondaryConstructor.getRegistry()) + .as(combined + " has registry address: " + registry) + .isEqualTo(registry); + assertThat(imageNameFromSecondaryConstructor.getUnversionedPart()) + .as(combined + " has unversioned part: " + unversionedPart) + .isEqualTo(unversionedPart); + assertThat(imageNameFromSecondaryConstructor.getVersionPart()) + .as(combined + " has version part: " + version) + .isEqualTo(version); } } } diff --git a/core/src/test/java/org/testcontainers/utility/ImageNameSubstitutorTest.java b/core/src/test/java/org/testcontainers/utility/ImageNameSubstitutorTest.java index f7e319a5948..5c65d9e5841 100644 --- a/core/src/test/java/org/testcontainers/utility/ImageNameSubstitutorTest.java +++ b/core/src/test/java/org/testcontainers/utility/ImageNameSubstitutorTest.java @@ -6,8 +6,8 @@ import org.junit.Test; import org.mockito.Mockito; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.eq; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; public class ImageNameSubstitutorTest { @@ -48,11 +48,9 @@ public void simpleConfigurationTest() { final ImageNameSubstitutor imageNameSubstitutor = ImageNameSubstitutor.instance(); DockerImageName result = imageNameSubstitutor.apply(DockerImageName.parse("original")); - assertEquals( - "the image has been substituted by default then configured implementations", - "transformed-substituted-image:latest", - result.asCanonicalNameString() - ); + assertThat(result.asCanonicalNameString()) + .as("the image has been substituted by default then configured implementations") + .isEqualTo("transformed-substituted-image:latest"); } @Test @@ -62,10 +60,8 @@ public void testWorksWithoutConfiguredImplementation() { final ImageNameSubstitutor imageNameSubstitutor = ImageNameSubstitutor.instance(); DockerImageName result = imageNameSubstitutor.apply(DockerImageName.parse("original")); - assertEquals( - "the image has been substituted by default then configured implementations", - "substituted-image:latest", - result.asCanonicalNameString() - ); + assertThat(result.asCanonicalNameString()) + .as("the image has been substituted by default then configured implementations") + .isEqualTo("substituted-image:latest"); } } diff --git a/core/src/test/java/org/testcontainers/utility/LazyFutureTest.java b/core/src/test/java/org/testcontainers/utility/LazyFutureTest.java index 99443221805..8631bf6de19 100644 --- a/core/src/test/java/org/testcontainers/utility/LazyFutureTest.java +++ b/core/src/test/java/org/testcontainers/utility/LazyFutureTest.java @@ -11,7 +11,8 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; -import static org.rnorth.visibleassertions.VisibleAssertions.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; public class LazyFutureTest { @@ -26,16 +27,16 @@ protected Integer resolve() { } }; - assertEquals("No resolve() invocations before get()", 0, counter.get()); - assertEquals("get() call returns proper result", 1, lazyFuture.get()); - assertEquals("resolve() was called only once after single get() call", 1, counter.get()); + assertThat(counter).as("No resolve() invocations before get()").hasValue(0); + assertThat(lazyFuture.get()).as("get() call returns proper result").isEqualTo(1); + assertThat(counter).as("resolve() was called only once after single get() call").hasValue(1); counter.incrementAndGet(); - assertEquals("result of resolve() must be cached", 1, lazyFuture.get()); + assertThat(lazyFuture.get()).as("result of resolve() must be cached").isEqualTo(1); } @Test(timeout = 5_000) - public void timeoutWorks() throws Exception { + public void timeoutWorks() { Future lazyFuture = new LazyFuture() { @Override @SneakyThrows(InterruptedException.class) @@ -45,8 +46,9 @@ protected Void resolve() { } }; - assertThrows("Should timeout", TimeoutException.class, () -> lazyFuture.get(10, TimeUnit.MILLISECONDS)); - pass("timeout works"); + assertThat(catchThrowable(() -> lazyFuture.get(10, TimeUnit.MILLISECONDS))) + .as("Should timeout") + .isInstanceOf(TimeoutException.class); } @Test(timeout = 5_000) @@ -77,6 +79,8 @@ protected Integer resolve() { latch.countDown(); } - assertEquals("All threads receives the same result", Collections.nCopies(numOfThreads, 1), task.get()); + assertThat(task.get()) + .as("All threads receives the same result") + .isEqualTo(Collections.nCopies(numOfThreads, 1)); } } diff --git a/core/src/test/java/org/testcontainers/utility/MountableFileTest.java b/core/src/test/java/org/testcontainers/utility/MountableFileTest.java index d3ff085c5de..fbfb537ef87 100644 --- a/core/src/test/java/org/testcontainers/utility/MountableFileTest.java +++ b/core/src/test/java/org/testcontainers/utility/MountableFileTest.java @@ -15,7 +15,7 @@ import java.nio.file.Path; import java.util.function.Consumer; -import static org.rnorth.visibleassertions.VisibleAssertions.*; +import static org.assertj.core.api.Assertions.assertThat; public class MountableFileTest { @@ -68,11 +68,10 @@ public void forHostPathWithSpaces() throws Exception { performChecks(mountableFile); - assertTrue("The resolved path contains the original space", mountableFile.getResolvedPath().contains(" ")); - assertFalse( - "The resolved path does not contain an escaped space", - mountableFile.getResolvedPath().contains("\\ ") - ); + assertThat(mountableFile.getResolvedPath()).as("The resolved path contains the original space").contains(" "); + assertThat(mountableFile.getResolvedPath()) + .as("The resolved path does not contain an escaped space") + .doesNotContain("\\ "); } @Test @@ -82,11 +81,10 @@ public void forHostPathWithPlus() throws Exception { performChecks(mountableFile); - assertTrue("The resolved path contains the original space", mountableFile.getResolvedPath().contains("+")); - assertFalse( - "The resolved path does not contain an escaped space", - mountableFile.getResolvedPath().contains(" ") - ); + assertThat(mountableFile.getResolvedPath()).as("The resolved path contains the original space").contains("+"); + assertThat(mountableFile.getResolvedPath()) + .as("The resolved path does not contain an escaped space") + .doesNotContain(" "); } @Test @@ -97,7 +95,7 @@ public void forClasspathResourceWithPermission() throws Exception { ); performChecks(mountableFile); - assertEquals("Valid file mode.", BASE_FILE_MODE | TEST_FILE_MODE, mountableFile.getFileMode()); + assertThat(mountableFile.getFileMode()).as("Valid file mode.").isEqualTo(BASE_FILE_MODE | TEST_FILE_MODE); } @Test @@ -105,7 +103,7 @@ public void forHostFilePathWithPermission() throws Exception { final Path file = createTempFile("somepath"); final MountableFile mountableFile = MountableFile.forHostPath(file.toString(), TEST_FILE_MODE); performChecks(mountableFile); - assertEquals("Valid file mode.", BASE_FILE_MODE | TEST_FILE_MODE, mountableFile.getFileMode()); + assertThat(mountableFile.getFileMode()).as("Valid file mode.").isEqualTo(BASE_FILE_MODE | TEST_FILE_MODE); } @Test @@ -113,7 +111,7 @@ public void forHostDirPathWithPermission() throws Exception { final Path dir = createTempDir(); final MountableFile mountableFile = MountableFile.forHostPath(dir.toString(), TEST_FILE_MODE); performChecks(mountableFile); - assertEquals("Valid dir mode.", BASE_DIR_MODE | TEST_FILE_MODE, mountableFile.getFileMode()); + assertThat(mountableFile.getFileMode()).as("Valid dir mode.").isEqualTo(BASE_DIR_MODE | TEST_FILE_MODE); } @Test @@ -129,7 +127,7 @@ public void noTrailingSlashesInTarEntryNames() throws Exception { ArchiveEntry entry; while ((entry = tais.getNextEntry()) != null) { - assertFalse("no entries should have a trailing slash", entry.getName().endsWith("/")); + assertThat(entry.getName()).as("no entries should have a trailing slash").doesNotEndWith("/"); } } @@ -163,10 +161,9 @@ private Path createTempDir() throws IOException { private void performChecks(final MountableFile mountableFile) { final String mountablePath = mountableFile.getResolvedPath(); - assertTrue("The filesystem path '" + mountablePath + "' can be found", new File(mountablePath).exists()); - assertFalse( - "The filesystem path '" + mountablePath + "' does not contain any URL escaping", - mountablePath.contains("%20") - ); + assertThat(new File(mountablePath)).as("The filesystem path '" + mountablePath + "' can be found").exists(); + assertThat(mountablePath) + .as("The filesystem path '" + mountablePath + "' does not contain any URL escaping") + .doesNotContain("%20"); } } diff --git a/core/src/test/java/org/testcontainers/utility/PrefixingImageNameSubstitutorTest.java b/core/src/test/java/org/testcontainers/utility/PrefixingImageNameSubstitutorTest.java index 211f8e00536..04786119e36 100644 --- a/core/src/test/java/org/testcontainers/utility/PrefixingImageNameSubstitutorTest.java +++ b/core/src/test/java/org/testcontainers/utility/PrefixingImageNameSubstitutorTest.java @@ -3,11 +3,11 @@ import org.junit.Before; import org.junit.Test; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; public class PrefixingImageNameSubstitutorTest { @@ -28,11 +28,9 @@ public void testHappyPath() { final DockerImageName result = underTest.apply(DockerImageName.parse("some/image:tag")); - assertEquals( - "The prefix is applied", - "someregistry.com/our-mirror/some/image:tag", - result.asCanonicalNameString() - ); + assertThat(result.asCanonicalNameString()) + .as("The prefix is applied") + .isEqualTo("someregistry.com/our-mirror/some/image:tag"); } @Test @@ -42,7 +40,7 @@ public void hubIoRegistryIsNotChanged() { final DockerImageName result = underTest.apply(DockerImageName.parse("docker.io/some/image:tag")); - assertEquals("The prefix is applied", "docker.io/some/image:tag", result.asCanonicalNameString()); + assertThat(result.asCanonicalNameString()).as("The prefix is applied").isEqualTo("docker.io/some/image:tag"); } @Test @@ -52,7 +50,9 @@ public void hubComRegistryIsNotChanged() { final DockerImageName result = underTest.apply(DockerImageName.parse("registry.hub.docker.com/some/image:tag")); - assertEquals("The prefix is applied", "registry.hub.docker.com/some/image:tag", result.asCanonicalNameString()); + assertThat(result.asCanonicalNameString()) + .as("The prefix is applied") + .isEqualTo("registry.hub.docker.com/some/image:tag"); } @Test @@ -62,11 +62,9 @@ public void thirdPartyRegistriesNotAffected() { final DockerImageName result = underTest.apply(DockerImageName.parse("gcr.io/something/image:tag")); - assertEquals( - "The prefix is not applied if a third party registry is used", - "gcr.io/something/image:tag", - result.asCanonicalNameString() - ); + assertThat(result.asCanonicalNameString()) + .as("The prefix is not applied if a third party registry is used") + .isEqualTo("gcr.io/something/image:tag"); } @Test @@ -76,11 +74,9 @@ public void testNoDoublePrefixing() { final DockerImageName result = underTest.apply(DockerImageName.parse("someregistry.com/some/image:tag")); - assertEquals( - "The prefix is not applied if already present", - "someregistry.com/some/image:tag", - result.asCanonicalNameString() - ); + assertThat(result.asCanonicalNameString()) + .as("The prefix is not applied if already present") + .isEqualTo("someregistry.com/some/image:tag"); } @Test @@ -90,11 +86,9 @@ public void testHandlesEmptyValue() { final DockerImageName result = underTest.apply(DockerImageName.parse("some/image:tag")); - assertEquals( - "The prefix is not applied if the env var is not set", - "some/image:tag", - result.asCanonicalNameString() - ); + assertThat(result.asCanonicalNameString()) + .as("The prefix is not applied if the env var is not set") + .isEqualTo("some/image:tag"); } @Test @@ -104,7 +98,9 @@ public void testHandlesRegistryOnlyWithTrailingSlash() { final DockerImageName result = underTest.apply(DockerImageName.parse("some/image:tag")); - assertEquals("The prefix is applied", "someregistry.com/some/image:tag", result.asCanonicalNameString()); + assertThat(result.asCanonicalNameString()) + .as("The prefix is applied") + .isEqualTo("someregistry.com/some/image:tag"); } @Test @@ -114,11 +110,9 @@ public void testCombinesLiterallyForRegistryOnlyWithoutTrailingSlash() { final DockerImageName result = underTest.apply(DockerImageName.parse("some/image:tag")); - assertEquals( - "The prefix is applied", - "someregistry.comsome/image:tag", // treating the prefix literally, for predictability - result.asCanonicalNameString() - ); + assertThat(result.asCanonicalNameString()) + .as("The prefix is applied") + .isEqualTo("someregistry.comsome/image:tag"); } @Test @@ -128,10 +122,8 @@ public void testCombinesLiterallyForBothPartsWithoutTrailingSlash() { final DockerImageName result = underTest.apply(DockerImageName.parse("some/image:tag")); - assertEquals( - "The prefix is applied", - "someregistry.com/our-mirrorsome/image:tag", // treating the prefix literally, for predictability - result.asCanonicalNameString() - ); + assertThat(result.asCanonicalNameString()) + .as("The prefix is applied") + .isEqualTo("someregistry.com/our-mirrorsome/image:tag"); } } diff --git a/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java b/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java index 4ddfad8454f..fea7f93ed5a 100644 --- a/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java +++ b/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java @@ -11,7 +11,7 @@ import java.util.HashMap; import java.util.Map; -import static org.rnorth.visibleassertions.VisibleAssertions.*; +import static org.assertj.core.api.Assertions.assertThat; public class RegistryAuthLocatorTest { @@ -24,13 +24,11 @@ public void lookupAuthConfigWithoutCredentials() throws URISyntaxException { new AuthConfig() ); - assertEquals( - "Default docker registry URL is set on auth config", - "https://index.docker.io/v1/", - authConfig.getRegistryAddress() - ); - assertNull("No username is set", authConfig.getUsername()); - assertNull("No password is set", authConfig.getPassword()); + assertThat(authConfig.getRegistryAddress()) + .as("Default docker registry URL is set on auth config") + .isEqualTo("https://index.docker.io/v1/"); + assertThat(authConfig.getUsername()).as("No username is set").isNull(); + assertThat(authConfig.getPassword()).as("No password is set").isNull(); } @Test @@ -42,13 +40,11 @@ public void lookupAuthConfigWithBasicAuthCredentials() throws URISyntaxException new AuthConfig() ); - assertEquals( - "Default docker registry URL is set on auth config", - "https://registry.example.com", - authConfig.getRegistryAddress() - ); - assertEquals("Username is set", "user", authConfig.getUsername()); - assertEquals("Password is set", "pass", authConfig.getPassword()); + assertThat(authConfig.getRegistryAddress()) + .as("Default docker registry URL is set on auth config") + .isEqualTo("https://registry.example.com"); + assertThat(authConfig.getUsername()).as("Username is set").isEqualTo("user"); + assertThat(authConfig.getPassword()).as("Password is set").isEqualTo("pass"); } @Test @@ -60,13 +56,11 @@ public void lookupAuthConfigWithJsonKeyCredentials() throws URISyntaxException { new AuthConfig() ); - assertEquals( - "Default docker registry URL is set on auth config", - "https://registry.example.com", - authConfig.getRegistryAddress() - ); - assertEquals("Username is set", "_json_key", authConfig.getUsername()); - assertNotNull("Password is set", authConfig.getPassword()); + assertThat(authConfig.getRegistryAddress()) + .as("Default docker registry URL is set on auth config") + .isEqualTo("https://registry.example.com"); + assertThat(authConfig.getUsername()).as("Username is set").isEqualTo("_json_key"); + assertThat(authConfig.getPassword()).as("Password is set").isNotNull(); } @Test @@ -78,9 +72,15 @@ public void lookupAuthConfigUsingStore() throws URISyntaxException { new AuthConfig() ); - assertEquals("Correct server URL is obtained from a credential store", "url", authConfig.getRegistryAddress()); - assertEquals("Correct username is obtained from a credential store", "username", authConfig.getUsername()); - assertEquals("Correct secret is obtained from a credential store", "secret", authConfig.getPassword()); + assertThat(authConfig.getRegistryAddress()) + .as("Correct server URL is obtained from a credential store") + .isEqualTo("url"); + assertThat(authConfig.getUsername()) + .as("Correct username is obtained from a credential store") + .isEqualTo("username"); + assertThat(authConfig.getPassword()) + .as("Correct secret is obtained from a credential store") + .isEqualTo("secret"); } @Test @@ -92,9 +92,15 @@ public void lookupAuthConfigUsingHelper() throws URISyntaxException { new AuthConfig() ); - assertEquals("Correct server URL is obtained from a credential store", "url", authConfig.getRegistryAddress()); - assertEquals("Correct username is obtained from a credential store", "username", authConfig.getUsername()); - assertEquals("Correct secret is obtained from a credential store", "secret", authConfig.getPassword()); + assertThat(authConfig.getRegistryAddress()) + .as("Correct server URL is obtained from a credential store") + .isEqualTo("url"); + assertThat(authConfig.getUsername()) + .as("Correct username is obtained from a credential store") + .isEqualTo("username"); + assertThat(authConfig.getPassword()) + .as("Correct secret is obtained from a credential store") + .isEqualTo("secret"); } @Test @@ -106,11 +112,9 @@ public void lookupAuthConfigUsingHelperWithToken() throws URISyntaxException { new AuthConfig() ); - assertEquals( - "Correct identitytoken is obtained from a credential store", - "secret", - authConfig.getIdentitytoken() - ); + assertThat(authConfig.getIdentitytoken()) + .as("Correct identitytoken is obtained from a credential store") + .isEqualTo("secret"); } @Test @@ -122,9 +126,15 @@ public void lookupUsingHelperEmptyAuth() throws URISyntaxException { new AuthConfig() ); - assertEquals("Correct server URL is obtained from a credential store", "url", authConfig.getRegistryAddress()); - assertEquals("Correct username is obtained from a credential store", "username", authConfig.getUsername()); - assertEquals("Correct secret is obtained from a credential store", "secret", authConfig.getPassword()); + assertThat(authConfig.getRegistryAddress()) + .as("Correct server URL is obtained from a credential store") + .isEqualTo("url"); + assertThat(authConfig.getUsername()) + .as("Correct username is obtained from a credential store") + .isEqualTo("username"); + assertThat(authConfig.getPassword()) + .as("Correct secret is obtained from a credential store") + .isEqualTo("secret"); } @Test @@ -136,9 +146,15 @@ public void lookupNonEmptyAuthWithHelper() throws URISyntaxException { new AuthConfig() ); - assertEquals("Correct server URL is obtained from a credential helper", "url", authConfig.getRegistryAddress()); - assertEquals("Correct username is obtained from a credential helper", "username", authConfig.getUsername()); - assertEquals("Correct password is obtained from a credential helper", "secret", authConfig.getPassword()); + assertThat(authConfig.getRegistryAddress()) + .as("Correct server URL is obtained from a credential helper") + .isEqualTo("url"); + assertThat(authConfig.getUsername()) + .as("Correct username is obtained from a credential helper") + .isEqualTo("username"); + assertThat(authConfig.getPassword()) + .as("Correct password is obtained from a credential helper") + .isEqualTo("secret"); } @Test @@ -152,17 +168,19 @@ public void lookupAuthConfigWithCredentialsNotFound() throws URISyntaxException DockerImageName dockerImageName = DockerImageName.parse("registry2.example.com/org/repo"); final AuthConfig authConfig = authLocator.lookupAuthConfig(dockerImageName, new AuthConfig()); - assertNull("No username should have been obtained from a credential store", authConfig.getUsername()); - assertNull("No secret should have been obtained from a credential store", authConfig.getPassword()); - assertEquals("Should have one 'credentials not found' message discovered", 1, notFoundMessagesReference.size()); + assertThat(authConfig.getUsername()) + .as("No username should have been obtained from a credential store") + .isNull(); + assertThat(authConfig.getPassword()).as("No secret should have been obtained from a credential store").isNull(); + assertThat(notFoundMessagesReference.size()) + .as("Should have one 'credentials not found' message discovered") + .isEqualTo(1); String discoveredMessage = notFoundMessagesReference.values().iterator().next(); - assertEquals( - "Not correct message discovered", - "Fake credentials not found on credentials store 'https://not.a.real.registry/url'", - discoveredMessage - ); + assertThat(discoveredMessage) + .as("Not correct message discovered") + .isEqualTo("Fake credentials not found on credentials store 'https://not.a.real.registry/url'"); } @Test @@ -172,7 +190,7 @@ public void lookupAuthConfigWithCredStoreEmpty() throws URISyntaxException { DockerImageName dockerImageName = DockerImageName.parse("registry2.example.com/org/repo"); final AuthConfig authConfig = authLocator.lookupAuthConfig(dockerImageName, new AuthConfig()); - assertNull("CredStore field will be ignored, because value is blank", authConfig.getAuth()); + assertThat(authConfig.getAuth()).as("CredStore field will be ignored, because value is blank").isNull(); } @NotNull diff --git a/core/src/test/java/org/testcontainers/utility/TestEnvironmentTest.java b/core/src/test/java/org/testcontainers/utility/TestEnvironmentTest.java index de97d100a39..4bd9e1b8edb 100644 --- a/core/src/test/java/org/testcontainers/utility/TestEnvironmentTest.java +++ b/core/src/test/java/org/testcontainers/utility/TestEnvironmentTest.java @@ -2,7 +2,7 @@ import org.junit.Test; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * Created by rnorth on 03/07/2016. @@ -11,21 +11,29 @@ public class TestEnvironmentTest { @Test public void testCompareVersionGreaterThanSameMajor() { - assertTrue("1.22 > 1.20", new ComparableVersion("1.22").compareTo(new ComparableVersion("1.20")) > 0); + assertThat(new ComparableVersion("1.22").compareTo(new ComparableVersion("1.20")) > 0) + .as("1.22 > 1.20") + .isTrue(); } @Test public void testCompareVersionEqual() { - assertTrue("1.20 == 1.20", new ComparableVersion("1.20").compareTo(new ComparableVersion("1.20")) == 0); + assertThat(new ComparableVersion("1.20")) + .as("1.20 == 1.20") + .isEqualByComparingTo(new ComparableVersion("1.20")); } @Test public void testCompareVersionGreaterThan() { - assertTrue("2.10 > 1.20", new ComparableVersion("2.10").compareTo(new ComparableVersion("1.20")) > 0); + assertThat(new ComparableVersion("2.10").compareTo(new ComparableVersion("1.20")) > 0) + .as("2.10 > 1.20") + .isTrue(); } @Test public void testCompareVersionIgnoresExcessLength() { - assertTrue("1.20 == 1.20.3", new ComparableVersion("1.20").compareTo(new ComparableVersion("1.20.3")) == 0); + assertThat(new ComparableVersion("1.20")) + .as("1.20 == 1.20.3") + .isEqualByComparingTo(new ComparableVersion("1.20.3")); } } diff --git a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java index 56cbdb98a56..d51bc89348c 100644 --- a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java +++ b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java @@ -8,9 +8,7 @@ import java.util.Properties; import java.util.UUID; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class TestcontainersConfigurationTest { @@ -30,122 +28,102 @@ public void setUp() { @Test public void shouldSubstituteImageNamesFromClasspathProperties() { classpathProperties.setProperty("ryuk.container.image", "foo:version"); - assertEquals( - "an image name can be pulled from classpath properties", - DockerImageName.parse("foo:version"), - newConfig().getConfiguredSubstituteImage(DockerImageName.parse("testcontainers/ryuk:any")) - ); + assertThat(newConfig().getConfiguredSubstituteImage(DockerImageName.parse("testcontainers/ryuk:any"))) + .as("an image name can be pulled from classpath properties") + .isEqualTo(DockerImageName.parse("foo:version")); } @Test public void shouldSubstituteImageNamesFromUserProperties() { userProperties.setProperty("ryuk.container.image", "foo:version"); - assertEquals( - "an image name can be pulled from user properties", - DockerImageName.parse("foo:version"), - newConfig().getConfiguredSubstituteImage(DockerImageName.parse("testcontainers/ryuk:any")) - ); + assertThat(newConfig().getConfiguredSubstituteImage(DockerImageName.parse("testcontainers/ryuk:any"))) + .as("an image name can be pulled from user properties") + .isEqualTo(DockerImageName.parse("foo:version")); } @Test public void shouldSubstituteImageNamesFromEnvironmentVariables() { environment.put("TESTCONTAINERS_RYUK_CONTAINER_IMAGE", "foo:version"); - assertEquals( - "an image name can be pulled from an environment variable", - DockerImageName.parse("foo:version"), - newConfig().getConfiguredSubstituteImage(DockerImageName.parse("testcontainers/ryuk:any")) - ); + assertThat(newConfig().getConfiguredSubstituteImage(DockerImageName.parse("testcontainers/ryuk:any"))) + .as("an image name can be pulled from an environment variable") + .isEqualTo(DockerImageName.parse("foo:version")); } @Test public void shouldApplySettingsInOrder() { - assertEquals( - "precedence order for multiple sources of the same value is correct", - "default", - newConfig().getEnvVarOrProperty("key", "default") - ); + assertThat(newConfig().getEnvVarOrProperty("key", "default")) + .as("precedence order for multiple sources of the same value is correct") + .isEqualTo("default"); classpathProperties.setProperty("key", "foo"); - assertEquals( - "precedence order for multiple sources of the same value is correct", - "foo", - newConfig().getEnvVarOrProperty("key", "default") - ); + assertThat(newConfig().getEnvVarOrProperty("key", "default")) + .as("precedence order for multiple sources of the same value is correct") + .isEqualTo("foo"); userProperties.setProperty("key", "bar"); - assertEquals( - "precedence order for multiple sources of the same value is correct", - "bar", - newConfig().getEnvVarOrProperty("key", "default") - ); + assertThat(newConfig().getEnvVarOrProperty("key", "default")) + .as("precedence order for multiple sources of the same value is correct") + .isEqualTo("bar"); environment.put("TESTCONTAINERS_KEY", "baz"); - assertEquals( - "precedence order for multiple sources of the same value is correct", - "baz", - newConfig().getEnvVarOrProperty("key", "default") - ); + assertThat(newConfig().getEnvVarOrProperty("key", "default")) + .as("precedence order for multiple sources of the same value is correct") + .isEqualTo("baz"); } @Test public void shouldNotReadChecksFromClasspathProperties() { - assertFalse("checks enabled by default", newConfig().isDisableChecks()); + assertThat(newConfig().isDisableChecks()).as("checks enabled by default").isFalse(); classpathProperties.setProperty("checks.disable", "true"); - assertFalse("checks are not affected by classpath properties", newConfig().isDisableChecks()); + assertThat(newConfig().isDisableChecks()).as("checks are not affected by classpath properties").isFalse(); } @Test public void shouldReadChecksFromUserProperties() { - assertFalse("checks enabled by default", newConfig().isDisableChecks()); + assertThat(newConfig().isDisableChecks()).as("checks enabled by default").isFalse(); userProperties.setProperty("checks.disable", "true"); - assertTrue("checks disabled via user properties", newConfig().isDisableChecks()); + assertThat(newConfig().isDisableChecks()).as("checks disabled via user properties").isTrue(); } @Test public void shouldReadChecksFromEnvironment() { - assertFalse("checks enabled by default", newConfig().isDisableChecks()); + assertThat(newConfig().isDisableChecks()).as("checks enabled by default").isFalse(); userProperties.remove("checks.disable"); environment.put("TESTCONTAINERS_CHECKS_DISABLE", "true"); - assertTrue("checks disabled via env var", newConfig().isDisableChecks()); + assertThat(newConfig().isDisableChecks()).as("checks disabled via env var").isTrue(); } @Test public void shouldReadDockerSettingsFromEnvironmentWithoutTestcontainersPrefix() { userProperties.remove("docker.foo"); environment.put("DOCKER_FOO", "some value"); - assertEquals( - "reads unprefixed env vars for docker. settings", - "some value", - newConfig().getEnvVarOrUserProperty("docker.foo", "default") - ); + assertThat(newConfig().getEnvVarOrUserProperty("docker.foo", "default")) + .as("reads unprefixed env vars for docker. settings") + .isEqualTo("some value"); } @Test public void shouldNotReadDockerSettingsFromEnvironmentWithTestcontainersPrefix() { userProperties.remove("docker.foo"); environment.put("TESTCONTAINERS_DOCKER_FOO", "some value"); - assertEquals( - "reads unprefixed env vars for docker. settings", - "default", - newConfig().getEnvVarOrUserProperty("docker.foo", "default") - ); + assertThat(newConfig().getEnvVarOrUserProperty("docker.foo", "default")) + .as("reads unprefixed env vars for docker. settings") + .isEqualTo("default"); } @Test public void shouldReadDockerSettingsFromUserProperties() { environment.remove("DOCKER_FOO"); userProperties.put("docker.foo", "some value"); - assertEquals( - "reads unprefixed user properties for docker. settings", - "some value", - newConfig().getEnvVarOrUserProperty("docker.foo", "default") - ); + assertThat(newConfig().getEnvVarOrUserProperty("docker.foo", "default")) + .as("reads unprefixed user properties for docker. settings") + .isEqualTo("some value"); } @Test @@ -153,104 +131,90 @@ public void shouldNotReadDockerClientStrategyFromClasspathProperties() { String currentValue = newConfig().getDockerClientStrategyClassName(); classpathProperties.setProperty("docker.client.strategy", UUID.randomUUID().toString()); - assertEquals( - "Docker client strategy is not affected by classpath properties", - currentValue, - newConfig().getDockerClientStrategyClassName() - ); + assertThat(newConfig().getDockerClientStrategyClassName()) + .as("Docker client strategy is not affected by classpath properties") + .isEqualTo(currentValue); } @Test public void shouldReadDockerClientStrategyFromUserProperties() { userProperties.setProperty("docker.client.strategy", "foo"); - assertEquals( - "Docker client strategy is changed by user property", - "foo", - newConfig().getDockerClientStrategyClassName() - ); + assertThat(newConfig().getDockerClientStrategyClassName()) + .as("Docker client strategy is changed by user property") + .isEqualTo("foo"); } @Test public void shouldReadDockerClientStrategyFromEnvironment() { userProperties.remove("docker.client.strategy"); environment.put("TESTCONTAINERS_DOCKER_CLIENT_STRATEGY", "foo"); - assertEquals( - "Docker client strategy is changed by env var", - "foo", - newConfig().getDockerClientStrategyClassName() - ); + assertThat(newConfig().getDockerClientStrategyClassName()) + .as("Docker client strategy is changed by env var") + .isEqualTo("foo"); } @Test public void shouldNotUseImplicitDockerClientStrategyWhenDockerHostAndStrategyAreBothSet() { userProperties.put("docker.client.strategy", "foo"); userProperties.put("docker.host", "tcp://1.2.3.4:5678"); - assertEquals( - "Docker client strategy is can be explicitly set", - "foo", - newConfig().getDockerClientStrategyClassName() - ); + assertThat(newConfig().getDockerClientStrategyClassName()) + .as("Docker client strategy is can be explicitly set") + .isEqualTo("foo"); userProperties.remove("docker.client.strategy"); environment.put("TESTCONTAINERS_DOCKER_CLIENT_STRATEGY", "bar"); userProperties.put("docker.client.strategy", "foo"); - assertEquals( - "Docker client strategy is can be explicitly set", - "bar", - newConfig().getDockerClientStrategyClassName() - ); + assertThat(newConfig().getDockerClientStrategyClassName()) + .as("Docker client strategy is can be explicitly set") + .isEqualTo("bar"); environment.put("TESTCONTAINERS_DOCKER_CLIENT_STRATEGY", "bar"); userProperties.remove("docker.client.strategy"); - assertEquals( - "Docker client strategy is can be explicitly set", - "bar", - newConfig().getDockerClientStrategyClassName() - ); + assertThat(newConfig().getDockerClientStrategyClassName()) + .as("Docker client strategy is can be explicitly set") + .isEqualTo("bar"); environment.remove("TESTCONTAINERS_DOCKER_CLIENT_STRATEGY"); userProperties.put("docker.client.strategy", "foo"); - assertEquals( - "Docker client strategy is can be explicitly set", - "foo", - newConfig().getDockerClientStrategyClassName() - ); + assertThat(newConfig().getDockerClientStrategyClassName()) + .as("Docker client strategy is can be explicitly set") + .isEqualTo("foo"); } @Test public void shouldNotReadReuseFromClasspathProperties() { - assertFalse("no reuse by default", newConfig().environmentSupportsReuse()); + assertThat(newConfig().environmentSupportsReuse()).as("no reuse by default").isFalse(); classpathProperties.setProperty("testcontainers.reuse.enable", "true"); - assertFalse("reuse is not affected by classpath properties", newConfig().environmentSupportsReuse()); + assertThat(newConfig().environmentSupportsReuse()) + .as("reuse is not affected by classpath properties") + .isFalse(); } @Test public void shouldReadReuseFromUserProperties() { - assertFalse("no reuse by default", newConfig().environmentSupportsReuse()); + assertThat(newConfig().environmentSupportsReuse()).as("no reuse by default").isFalse(); userProperties.setProperty("testcontainers.reuse.enable", "true"); - assertTrue("reuse enabled via user property", newConfig().environmentSupportsReuse()); + assertThat(newConfig().environmentSupportsReuse()).as("reuse enabled via user property").isTrue(); } @Test public void shouldReadReuseFromEnvironment() { - assertFalse("no reuse by default", newConfig().environmentSupportsReuse()); + assertThat(newConfig().environmentSupportsReuse()).as("no reuse by default").isFalse(); userProperties.remove("testcontainers.reuse.enable"); environment.put("TESTCONTAINERS_REUSE_ENABLE", "true"); - assertTrue("reuse enabled via env var", newConfig().environmentSupportsReuse()); + assertThat(newConfig().environmentSupportsReuse()).as("reuse enabled via env var").isTrue(); } @Test public void shouldTrimImageNames() { userProperties.setProperty("ryuk.container.image", " testcontainers/ryuk:0.3.2 "); - assertEquals( - "trailing whitespace was not removed from image name property", - "testcontainers/ryuk:0.3.2", - newConfig().getRyukImage() - ); + assertThat(newConfig().getRyukImage()) + .as("trailing whitespace was not removed from image name property") + .isEqualTo("testcontainers/ryuk:0.3.2"); } private TestcontainersConfiguration newConfig() { diff --git a/docs/examples/junit5/redis/build.gradle b/docs/examples/junit5/redis/build.gradle index 16f0d5bc598..72b943adc23 100644 --- a/docs/examples/junit5/redis/build.gradle +++ b/docs/examples/junit5/redis/build.gradle @@ -8,6 +8,7 @@ dependencies { testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.4.2" testImplementation project(":testcontainers") testImplementation project(":junit-jupiter") + testImplementation 'org.assertj:assertj-core:3.23.1' } test { diff --git a/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java b/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java index 59d92210549..2c5f4baef67 100644 --- a/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java +++ b/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java @@ -7,7 +7,7 @@ import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.utility.DockerImageName; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; // class { @Testcontainers @@ -36,7 +36,7 @@ public void testSimplePutAndGet() { underTest.put("test", "example"); String retrieved = underTest.get("test"); - assertEquals("example", retrieved); + assertThat(retrieved).isEqualTo("example"); } } // } diff --git a/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java b/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java index d9f889a8943..e1f3c3efbe8 100644 --- a/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java +++ b/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java @@ -4,7 +4,7 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; @Disabled("This test class is deliberately invalid, as it relies on a non-existent local Redis") public class RedisBackedCacheIntTestStep0 { @@ -22,6 +22,6 @@ public void testSimplePutAndGet() { underTest.put("test", "example"); String retrieved = underTest.get("test"); - assertEquals("example", retrieved); + assertThat(retrieved).isEqualTo("example"); } } diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index 47bf340e2eb..6c7a453c3e9 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -15,5 +15,5 @@ dependencies { testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.mockito:mockito-all:1.10.19' testImplementation 'org.testcontainers:testcontainers' - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/examples/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java b/examples/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java index 4da6201eecb..dfbab3f9e6a 100644 --- a/examples/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java +++ b/examples/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java @@ -6,15 +6,13 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.utility.DockerImageName; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.context; -import static org.rnorth.visibleassertions.VisibleAssertions.info; /** * Created by rnorth on 03/01/2016. @@ -35,14 +33,11 @@ public class DisqueBackedMailSenderTest { @Before public void setup() { - context(""); disqueClient = new DisqueClient(DisqueURI.create(container.getHost(), container.getMappedPort(7711))); mockMailApiClient = mock(MailApiClient.class); service = new DisqueBackedMailSenderService(disqueClient, mockMailApiClient); - info("Initialized a fresh Disque instance and service instance"); - dummyMessage1 = new MailMessage("Dummy Message 1", "bob@example.com", "Test body"); dummyMessage2 = new MailMessage("Dummy Message 2", "bob@example.com", "Test body"); dummyMessage3 = new MailMessage("Dummy Message 3", "bob@example.com", "Test body"); @@ -69,28 +64,23 @@ public void testRetryOnFailure() throws Exception { service.enqueueMessage(dummyMessage2); service.enqueueMessage(dummyMessage3); - info("Message 1 will fail to send on the first attempt"); when(mockMailApiClient.send(eq(dummyMessage1))).thenThrow(MailApiClient.MailSendException.class).thenReturn(true); when(mockMailApiClient.send(eq(dummyMessage2))).thenReturn(true); when(mockMailApiClient.send(eq(dummyMessage3))).thenReturn(true); MailSenderService.Result result; - context("Simulating sending messages"); - context("First sending attempt", 4); + result = service.doScheduledSend(); - assertEquals("2 messages were 'sent' successfully", 2, result.successfulCount); - assertEquals("1 messages failed", 1, result.failedCount); + assertThat(result.successfulCount).as("2 messages were 'sent' successfully").isEqualTo(2); + assertThat(result.failedCount).as("1 messages failed").isEqualTo(1); - context("Second attempt", 4); result = service.doScheduledSend(); - assertEquals("1 message was 'sent' successfully", 1, result.successfulCount); - assertEquals("0 messages failed", 0, result.failedCount); + assertThat(result.successfulCount).as("1 message was 'sent' successfully").isEqualTo(1); + assertThat(result.failedCount).as("0 messages failed").isZero(); - context("Third attempt", 4); - info("No messages should be due to send this time"); result = service.doScheduledSend(); - assertEquals("0 messages were 'sent' successfully", 0, result.successfulCount); - assertEquals("0 messages failed", 0, result.failedCount); + assertThat(result.successfulCount).as("0 messages were 'sent' successfully").isZero(); + assertThat(result.failedCount).as("0 messages failed").isZero(); verify(mockMailApiClient, times(2)).send(eq(dummyMessage1)); verify(mockMailApiClient).send(eq(dummyMessage2)); diff --git a/examples/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java b/examples/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java index 7ded5d8da43..09830614eb0 100644 --- a/examples/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java +++ b/examples/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java @@ -11,10 +11,7 @@ import java.util.concurrent.TimeUnit; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; -import static org.rnorth.visibleassertions.VisibleAssertions.context; -import static org.rnorth.visibleassertions.VisibleAssertions.info; +import static org.assertj.core.api.Assertions.assertThat; /** * Created by rnorth on 03/01/2016. @@ -29,54 +26,36 @@ public class SingleDisqueInstanceTest { @Before public void setup() { - context(""); DisqueClient client = new DisqueClient(DisqueURI.create(container.getHost(), container.getMappedPort(7711))); connection = client.connect().sync(); retryAfter1Second = AddJobArgs.builder().retry(1, TimeUnit.SECONDS).build(); - - info("Initialized a fresh Disque instance and client"); } @Test public void testJobStoreAndRetrieve() { - - info("Adding a job to the queue"); connection.addjob("main_queue", "body", 1, TimeUnit.MINUTES); - info("Getting a job from the queue"); Job job = connection.getjob("main_queue"); - assertEquals("The retrieved job is the same as the one that was added", - "body", - job.getBody()); + assertThat(job.getBody()).as("The retrieved job is the same as the one that was added").isEqualTo("body"); - info("Acknowledging the job to mark completion"); connection.ackjob(job.getId()); } @Test public void testFailureToAckJobBeforeTimeout() throws InterruptedException { - - info("Adding a job to the queue"); connection.addjob("main_queue", "body", 1, TimeUnit.MINUTES, retryAfter1Second); - info("Getting a job from the queue"); Job job = connection.getjob("main_queue"); - assertEquals("The retrieved job is the same as the one that was added", - "body", - job.getBody()); + assertThat(job.getBody()).as("The retrieved job is the same as the one that was added").isEqualTo("body"); - info("Simulating a failure to ack the job before the timeout (1s)"); TimeUnit.SECONDS.sleep(2); - info("Attempting to get another job from the queue - the RETRY setting for the job means it should have reappeared"); // The timeout specified here is how long the command will wait for a job to appear Job job2 = connection.getjob(5, TimeUnit.SECONDS, "main_queue"); - assertNotNull("After re-getting the original job is back on the queue", job2); - assertEquals("The retrieved job is the same as the one that was added", - "body", - job2.getBody()); + assertThat(job2).as("After re-getting the original job is back on the queue").isNotNull(); + assertThat(job2.getBody()).as("The retrieved job is the same as the one that was added").isEqualTo("body"); } } diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 1e09fbc84f0..9d0fd118f61 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -14,7 +14,7 @@ dependencies { testImplementation 'org.testcontainers:testcontainers' testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.testng:testng:7.5' - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.assertj:assertj-core:3.23.1' } test { diff --git a/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java b/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java index 927f097aa5f..e02b271f290 100644 --- a/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java +++ b/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java @@ -10,9 +10,7 @@ import java.util.Optional; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * Integration test for Redis-backed cache implementation. @@ -45,18 +43,14 @@ public void testFindingAnInsertedValue() { cache.put("foo", "FOO"); Optional foundObject = cache.get("foo", String.class); - assertTrue("When an object in the cache is retrieved, it can be found", - foundObject.isPresent()); - assertEquals("When we put a String in to the cache and retrieve it, the value is the same", - "FOO", - foundObject.get()); + assertThat(foundObject).as("When an object in the cache is retrieved, it can be found").isPresent(); + assertThat(foundObject).as("When we put a String in to the cache and retrieve it, the value is the same").contains("FOO"); } @Test public void testNotFindingAValueThatWasNotInserted() { Optional foundObject = cache.get("bar", String.class); - assertFalse("When an object that's not in the cache is retrieved, nothing is found", - foundObject.isPresent()); + assertThat(foundObject).as("When an object that's not in the cache is retrieved, nothing is found").isNotPresent(); } } diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 94f445f5ffe..c5f365b5502 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -14,5 +14,5 @@ dependencies { testImplementation 'org.testcontainers:testcontainers' testImplementation 'junit:junit:4.13.2' testImplementation 'ch.qos.logback:logback-classic:1.2.11' - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/examples/redis-backed-cache/src/test/java/RedisBackedCacheTest.java b/examples/redis-backed-cache/src/test/java/RedisBackedCacheTest.java index aa4199684e8..24a21723b15 100644 --- a/examples/redis-backed-cache/src/test/java/RedisBackedCacheTest.java +++ b/examples/redis-backed-cache/src/test/java/RedisBackedCacheTest.java @@ -9,9 +9,7 @@ import java.util.Optional; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * Integration test for Redis-backed cache implementation. @@ -35,18 +33,14 @@ public void testFindingAnInsertedValue() { cache.put("foo", "FOO"); Optional foundObject = cache.get("foo", String.class); - assertTrue("When an object in the cache is retrieved, it can be found", - foundObject.isPresent()); - assertEquals("When we put a String in to the cache and retrieve it, the value is the same", - "FOO", - foundObject.get()); + assertThat(foundObject.isPresent()).as("When an object in the cache is retrieved, it can be found").isTrue(); + assertThat(foundObject.get()).as("When we put a String in to the cache and retrieve it, the value is the same").isEqualTo("FOO"); } @Test public void testNotFindingAValueThatWasNotInserted() { Optional foundObject = cache.get("bar", String.class); - assertFalse("When an object that's not in the cache is retrieved, nothing is found", - foundObject.isPresent()); + assertThat(foundObject.isPresent()).as("When an object that's not in the cache is retrieved, nothing is found").isFalse(); } } diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index 7677e82eefb..dc10798a060 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -15,5 +15,5 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.testcontainers:selenium' - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/examples/selenium-container/src/test/java/SeleniumContainerTest.java b/examples/selenium-container/src/test/java/SeleniumContainerTest.java index c21d6bf4eca..32411b98ed9 100644 --- a/examples/selenium-container/src/test/java/SeleniumContainerTest.java +++ b/examples/selenium-container/src/test/java/SeleniumContainerTest.java @@ -19,7 +19,7 @@ import java.io.File; import java.util.List; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; import static org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode.RECORD_ALL; /** @@ -45,7 +45,7 @@ public void simplePlainSeleniumTest() { driver.get("http://host.testcontainers.internal:" + port + "/foo.html"); List hElement = driver.findElementsByTagName("h"); - assertTrue("The h element is found", hElement != null && hElement.size() > 0); + assertThat(hElement).as("The h element is found").isNotEmpty(); } public static class Initializer implements ApplicationContextInitializer { diff --git a/examples/settings.gradle b/examples/settings.gradle index cfb204f4b00..be455a78955 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -32,7 +32,6 @@ include 'solr-container' include 'spring-boot' include 'cucumber' include 'spring-boot-kotlin-redis' -include 'spock' ext.isCI = System.getenv("CI") != null diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index fd603d515c7..89f067d3a19 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -15,5 +15,5 @@ dependencies { testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.testcontainers:testcontainers' - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java b/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java index 0f4e6d672d8..fff6e1a4a3a 100644 --- a/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java +++ b/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java @@ -8,8 +8,7 @@ import java.util.Optional; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class BarConcreteTestClass extends AbstractIntegrationTest { @@ -27,8 +26,8 @@ public void testInsertValue() { cache.put("bar", "BAR"); Optional foundObject = cache.get("bar", String.class); - assertTrue("When inserting an object into the cache, it can be retrieved", foundObject.isPresent()); - assertEquals("When accessing the value of a retrieved object, the value must be the same", "BAR", foundObject.get()); + assertThat(foundObject).as("When inserting an object into the cache, it can be retrieved").isPresent(); + assertThat(foundObject).as("When accessing the value of a retrieved object, the value must be the same").contains("BAR"); } } diff --git a/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java b/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java index 3a9e4e84f04..11ad98a0580 100644 --- a/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java +++ b/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java @@ -8,15 +8,14 @@ import java.util.Optional; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class FooConcreteTestClass extends AbstractIntegrationTest { private Cache cache; @Before - public void setUp() throws Exception { + public void setUp() { Jedis jedis = new Jedis(redis.getHost(), redis.getMappedPort(6379)); cache = new RedisBackedCache(jedis, "foo"); @@ -27,7 +26,7 @@ public void testInsertValue() { cache.put("foo", "FOO"); Optional foundObject = cache.get("foo", String.class); - assertTrue("When inserting an object into the cache, it can be retrieved", foundObject.isPresent()); - assertEquals("When accessing the value of a retrieved object, the value must be the same", "FOO", foundObject.get()); + assertThat(foundObject).as("When inserting an object into the cache, it can be retrieved").isPresent(); + assertThat(foundObject).as("When accessing the value of a retrieved object, the value must be the same").contains("FOO"); } } diff --git a/examples/solr-container/build.gradle b/examples/solr-container/build.gradle index abb7c12e0ed..56fd297cf5e 100644 --- a/examples/solr-container/build.gradle +++ b/examples/solr-container/build.gradle @@ -14,6 +14,6 @@ dependencies { testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.testcontainers:solr' - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/examples/solr-container/src/test/java/com/example/SolrQueryTest.java b/examples/solr-container/src/test/java/com/example/SolrQueryTest.java index 49f8cf99f33..6443a19b869 100644 --- a/examples/solr-container/src/test/java/com/example/SolrQueryTest.java +++ b/examples/solr-container/src/test/java/com/example/SolrQueryTest.java @@ -16,7 +16,7 @@ import java.util.Map; import static com.example.SolrSearchEngine.COLLECTION_NAME; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class SolrQueryTest { @@ -55,8 +55,8 @@ public void testQueryForShoes() { SolrSearchEngine searchEngine = new SolrSearchEngine(solrClient); SearchResult result = searchEngine.search("shoes"); - assertEquals("When searching for shoes we expect one result", 1L, result.getTotalHits()); - assertEquals("The result should have the id 1", "1", result.getResults().get(0).get("id")); + assertThat(result.getTotalHits()).as("When searching for shoes we expect one result").isEqualTo(1L); + assertThat(result.getResults().get(0).get("id")).as("The result should have the id 1").isEqualTo("1"); } @Test @@ -64,8 +64,8 @@ public void testQueryForTShirt() { SolrSearchEngine searchEngine = new SolrSearchEngine(solrClient); SearchResult result = searchEngine.search("t-shirt"); - assertEquals("When searching for t-shirt we expect one result", 1L, result.getTotalHits()); - assertEquals("The result should have the id 2", "2", result.getResults().get(0).get("id")); + assertThat(result.getTotalHits()).as("When searching for t-shirt we expect one result").isEqualTo(1L); + assertThat(result.getResults().get(0).get("id")).as("The result should have the id 2").isEqualTo("2"); } @Test @@ -73,7 +73,7 @@ public void testQueryForAsterisk() { SolrSearchEngine searchEngine = new SolrSearchEngine(solrClient); SearchResult result = searchEngine.search("*"); - assertEquals("When searching for * we expect no results", 0L, result.getTotalHits()); + assertThat(result.getTotalHits()).as("When searching for * we expect no results").isEqualTo(0L); } private static SolrInputField createInputField(String key, String value) { diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index e3b3376e28f..650b5a60d98 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -17,5 +17,4 @@ dependencies { runtimeOnly 'org.postgresql:postgresql' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.testcontainers:postgresql' - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/examples/spring-boot/src/test/java/com/example/DemoControllerTest.java b/examples/spring-boot/src/test/java/com/example/DemoControllerTest.java index 286ce41ffbf..c305ae67811 100644 --- a/examples/spring-boot/src/test/java/com/example/DemoControllerTest.java +++ b/examples/spring-boot/src/test/java/com/example/DemoControllerTest.java @@ -4,8 +4,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.web.client.TestRestTemplate; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.info; +import static org.assertj.core.api.Assertions.assertThat; public class DemoControllerTest extends AbstractIntegrationTest { @@ -19,10 +18,9 @@ public class DemoControllerTest extends AbstractIntegrationTest { public void simpleTest() { String fooResource = "/foo"; - info("putting 'bar' to " + fooResource); restTemplate.put(fooResource, "bar"); - assertEquals("value is set", "bar", restTemplate.getForObject(fooResource, String.class)); + assertThat(restTemplate.getForObject(fooResource, String.class)).as("value is set").isEqualTo("bar"); } @Test @@ -33,7 +31,7 @@ public void simpleJPATest() { DemoEntity result = restTemplate.getForObject("/" + demoEntity.getId(), DemoEntity.class); - assertEquals("value is set", "Some value", result.getValue()); + assertThat(result.getValue()).as("value is set").isEqualTo("Some value"); } } diff --git a/modules/azure/src/test/java/org/testcontainers/containers/CosmosDBEmulatorContainerTest.java b/modules/azure/src/test/java/org/testcontainers/containers/CosmosDBEmulatorContainerTest.java index 37422b30af7..140bd64c39a 100644 --- a/modules/azure/src/test/java/org/testcontainers/containers/CosmosDBEmulatorContainerTest.java +++ b/modules/azure/src/test/java/org/testcontainers/containers/CosmosDBEmulatorContainerTest.java @@ -4,7 +4,6 @@ import com.azure.cosmos.CosmosClientBuilder; import com.azure.cosmos.models.CosmosContainerResponse; import com.azure.cosmos.models.CosmosDatabaseResponse; -import org.assertj.core.api.Assertions; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Rule; @@ -17,6 +16,8 @@ import java.security.KeyStore; import java.util.Properties; +import static org.assertj.core.api.Assertions.assertThat; + public class CosmosDBEmulatorContainerTest { private static Properties originalSystemProperties; @@ -64,12 +65,12 @@ public void testWithCosmosClient() throws Exception { // } // testWithClientAgainstEmulatorContainer { CosmosDatabaseResponse databaseResponse = client.createDatabaseIfNotExists("Azure").block(); - Assertions.assertThat(databaseResponse.getStatusCode()).isEqualTo(201); + assertThat(databaseResponse.getStatusCode()).isEqualTo(201); CosmosContainerResponse containerResponse = client .getDatabase("Azure") .createContainerIfNotExists("ServiceContainer", "/name") .block(); - Assertions.assertThat(containerResponse.getStatusCode()).isEqualTo(201); + assertThat(containerResponse.getStatusCode()).isEqualTo(201); // } } } diff --git a/modules/clickhouse/build.gradle b/modules/clickhouse/build.gradle index 914ee7efbd4..9f7df373b27 100644 --- a/modules/clickhouse/build.gradle +++ b/modules/clickhouse/build.gradle @@ -6,4 +6,5 @@ dependencies { testImplementation project(':jdbc-test') testImplementation 'ru.yandex.clickhouse:clickhouse-jdbc:0.3.2' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java b/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java index 2f6edcc722d..8f81c88bfc0 100644 --- a/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java +++ b/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java @@ -11,7 +11,7 @@ import java.sql.ResultSet; import java.sql.SQLException; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; @RunWith(Parameterized.class) public class SimpleClickhouseTest extends AbstractContainerDatabaseTest { @@ -38,7 +38,7 @@ public void testSimple() throws SQLException { ResultSet resultSet = performQuery(clickhouse, "SELECT 1"); int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); } } } diff --git a/modules/cockroachdb/build.gradle b/modules/cockroachdb/build.gradle index fb9f53189d9..80763e312b5 100644 --- a/modules/cockroachdb/build.gradle +++ b/modules/cockroachdb/build.gradle @@ -5,4 +5,5 @@ dependencies { testImplementation project(':jdbc-test') testImplementation 'org.postgresql:postgresql:42.4.0' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java b/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java index 87e82fb30e3..65c79441f32 100644 --- a/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java +++ b/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java @@ -10,9 +10,7 @@ import java.util.logging.Level; import java.util.logging.LogManager; -import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertThat; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class SimpleCockroachDBTest extends AbstractContainerDatabaseTest { static { @@ -28,7 +26,7 @@ public void testSimple() throws SQLException { ResultSet resultSet = performQuery(cockroach, "SELECT 1"); int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); } } @@ -43,7 +41,7 @@ public void testExplicitInitScript() throws SQLException { ResultSet resultSet = performQuery(cockroach, "SELECT foo FROM bar"); String firstColumnValue = resultSet.getString(1); - assertEquals("Value from init script should equal real value", "hello world", firstColumnValue); + assertThat(firstColumnValue).as("Value from init script should equal real value").isEqualTo("hello world"); } } @@ -56,10 +54,10 @@ public void testWithAdditionalUrlParamInJdbcUrl() { try { cockroach.start(); String jdbcUrl = cockroach.getJdbcUrl(); - assertThat(jdbcUrl, containsString("?")); - assertThat(jdbcUrl, containsString("&")); - assertThat(jdbcUrl, containsString("sslmode=disable")); - assertThat(jdbcUrl, containsString("application_name=cockroach")); + assertThat(jdbcUrl).contains("?"); + assertThat(jdbcUrl).contains("&"); + assertThat(jdbcUrl).contains("sslmode=disable"); + assertThat(jdbcUrl).contains("application_name=cockroach"); } finally { cockroach.stop(); } diff --git a/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptSplittingTest.java b/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptSplittingTest.java index bc9476cc83d..99ebb69a1e8 100644 --- a/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptSplittingTest.java +++ b/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptSplittingTest.java @@ -1,13 +1,13 @@ package org.testcontainers.ext; -import org.assertj.core.api.Assertions; import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import static org.junit.Assert.fail; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; public class ScriptSplittingTest { @@ -283,7 +283,7 @@ public void testIssue1452Case() { private void splitAndCompare(String script, List expected) { final List statements = doSplit(script); - Assertions.assertThat(statements).isEqualTo(expected); + assertThat(statements).isEqualTo(expected); } private List doSplit(String script) { diff --git a/modules/db2/build.gradle b/modules/db2/build.gradle index 0c8cd323779..804f51000d7 100644 --- a/modules/db2/build.gradle +++ b/modules/db2/build.gradle @@ -5,4 +5,5 @@ dependencies { testImplementation project(':jdbc-test') testImplementation 'com.ibm.db2:jcc:11.5.7.0' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java b/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java index 8f3489eae95..2c2c4cb2f09 100644 --- a/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java +++ b/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java @@ -8,9 +8,7 @@ import java.sql.ResultSet; import java.sql.SQLException; -import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertThat; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class SimpleDb2Test extends AbstractContainerDatabaseTest { @@ -22,7 +20,7 @@ public void testSimple() throws SQLException { ResultSet resultSet = performQuery(db2, "SELECT 1 FROM SYSIBM.SYSDUMMY1"); int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); } } @@ -36,7 +34,7 @@ public void testWithAdditionalUrlParamInJdbcUrl() { db2.start(); String jdbcUrl = db2.getJdbcUrl(); - assertThat(jdbcUrl, containsString(":sslConnection=false;")); + assertThat(jdbcUrl).contains(":sslConnection=false;"); } } } diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 5b9ff919da7..6abd9cd943a 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -5,6 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.273' testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.253' - - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/dynalite/src/test/java/org/testcontainers/dynamodb/DynaliteContainerTest.java b/modules/dynalite/src/test/java/org/testcontainers/dynamodb/DynaliteContainerTest.java index ced5931de31..10e02566d16 100644 --- a/modules/dynalite/src/test/java/org/testcontainers/dynamodb/DynaliteContainerTest.java +++ b/modules/dynalite/src/test/java/org/testcontainers/dynamodb/DynaliteContainerTest.java @@ -13,8 +13,7 @@ import org.junit.Test; import org.testcontainers.utility.DockerImageName; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; +import static org.assertj.core.api.Assertions.assertThat; public class DynaliteContainerTest { @@ -54,12 +53,10 @@ private void runTest(AmazonDynamoDB client) { final TableDescription tableDescription = client.describeTable("foo").getTable(); - assertNotNull("the description is not null", tableDescription); - assertEquals("the table has the right name", "foo", tableDescription.getTableName()); - assertEquals( - "the name has the right primary key", - "Name", - tableDescription.getKeySchema().get(0).getAttributeName() - ); + assertThat(tableDescription).as("the description is not null").isNotNull(); + assertThat(tableDescription.getTableName()).as("the table has the right name").isEqualTo("foo"); + assertThat(tableDescription.getKeySchema().get(0).getAttributeName()) + .as("the name has the right primary key") + .isEqualTo("Name"); } } diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index a3a47d11fab..0e6c5bd7b5d 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -4,5 +4,5 @@ dependencies { api project(':testcontainers') testImplementation "org.elasticsearch.client:elasticsearch-rest-client:8.3.3" testImplementation "org.elasticsearch.client:transport:7.17.5" - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java index 9fcfc45af76..1e1e70b5d75 100644 --- a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java +++ b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java @@ -29,10 +29,8 @@ import javax.net.ssl.SSLHandshakeException; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.is; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; public class ElasticsearchContainerTest { @@ -84,14 +82,14 @@ public void elasticsearchDeprecatedCtorTest() throws IOException { // Do whatever you want with the rest client ... Response response = getClient(container).performRequest(new Request("GET", "/")); - assertThat(response.getStatusLine().getStatusCode(), is(200)); - assertThat(EntityUtils.toString(response.getEntity()), containsString(ELASTICSEARCH_VERSION)); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); + assertThat(EntityUtils.toString(response.getEntity())).contains(ELASTICSEARCH_VERSION); // The default image is running with the features under Elastic License response = getClient(container).performRequest(new Request("GET", "/_xpack/")); - assertThat(response.getStatusLine().getStatusCode(), is(200)); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); // For now we test that we have the monitoring feature available - assertThat(EntityUtils.toString(response.getEntity()), containsString("monitoring")); + assertThat(EntityUtils.toString(response.getEntity())).contains("monitoring"); } } @@ -106,14 +104,14 @@ public void elasticsearchDefaultTest() throws IOException { // Do whatever you want with the rest client ... Response response = getClient(container).performRequest(new Request("GET", "/")); - assertThat(response.getStatusLine().getStatusCode(), is(200)); - assertThat(EntityUtils.toString(response.getEntity()), containsString(ELASTICSEARCH_VERSION)); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); + assertThat(EntityUtils.toString(response.getEntity())).contains(ELASTICSEARCH_VERSION); // The default image is running with the features under Elastic License response = getClient(container).performRequest(new Request("GET", "/_xpack/")); - assertThat(response.getStatusLine().getStatusCode(), is(200)); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); // For now we test that we have the monitoring feature available - assertThat(EntityUtils.toString(response.getEntity()), containsString("monitoring")); + assertThat(EntityUtils.toString(response.getEntity())).contains("monitoring"); } } @@ -126,16 +124,14 @@ public void elasticsearchSecuredTest() throws IOException { container.start(); // The cluster should be secured so it must fail when we try to access / without credentials - assertThrows( - "We should not be able to access / URI with an anonymous client.", - ResponseException.class, - () -> getAnonymousClient(container).performRequest(new Request("GET", "/")) - ); + assertThat(catchThrowable(() -> getAnonymousClient(container).performRequest(new Request("GET", "/")))) + .as("We should not be able to access / URI with an anonymous client.") + .isInstanceOf(ResponseException.class); // But it should work when we try to access / with the proper login and password Response response = getClient(container).performRequest(new Request("GET", "/")); - assertThat(response.getStatusLine().getStatusCode(), is(200)); - assertThat(EntityUtils.toString(response.getEntity()), containsString(ELASTICSEARCH_VERSION)); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); + assertThat(EntityUtils.toString(response.getEntity())).contains(ELASTICSEARCH_VERSION); } } @@ -144,9 +140,9 @@ public void elasticsearchVersion() throws IOException { try (ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE)) { container.start(); Response response = getClient(container).performRequest(new Request("GET", "/")); - assertThat(response.getStatusLine().getStatusCode(), is(200)); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); String responseAsString = EntityUtils.toString(response.getEntity()); - assertThat(responseAsString, containsString(ELASTICSEARCH_VERSION)); + assertThat(responseAsString).contains(ELASTICSEARCH_VERSION); } } @@ -159,8 +155,8 @@ public void elasticsearchVersion83() throws IOException { ) { container.start(); Response response = getClient(container).performRequest(new Request("GET", "/")); - assertThat(response.getStatusLine().getStatusCode(), is(200)); - assertThat(EntityUtils.toString(response.getEntity()), containsString("8.3.0")); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); + assertThat(EntityUtils.toString(response.getEntity())).contains("8.3.0"); } } @@ -177,13 +173,11 @@ public void elasticsearchOssImage() throws IOException { ) { container.start(); Response response = getClient(container).performRequest(new Request("GET", "/")); - assertThat(response.getStatusLine().getStatusCode(), is(200)); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); // The OSS image does not have any feature under Elastic License - assertThrows( - "We should not have /_xpack endpoint with an OSS License", - ResponseException.class, - () -> getClient(container).performRequest(new Request("GET", "/_xpack/")) - ); + assertThat(catchThrowable(() -> getClient(container).performRequest(new Request("GET", "/_xpack/")))) + .as("We should not have /_xpack endpoint with an OSS License") + .isInstanceOf(ResponseException.class); } } @@ -212,8 +206,8 @@ public void restClientClusterHealth() throws IOException { Response response = client.performRequest(new Request("GET", "/_cluster/health")); // }} - assertThat(response.getStatusLine().getStatusCode(), is(200)); - assertThat(EntityUtils.toString(response.getEntity()), containsString("cluster_name")); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); + assertThat(EntityUtils.toString(response.getEntity())).contains("cluster_name"); // httpClientContainer {{ } // } @@ -248,8 +242,8 @@ public void restClientSecuredClusterHealth() throws IOException { Response response = client.performRequest(new Request("GET", "/_cluster/health")); // }} - assertThat(response.getStatusLine().getStatusCode(), is(200)); - assertThat(EntityUtils.toString(response.getEntity()), containsString("cluster_name")); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); + assertThat(EntityUtils.toString(response.getEntity())).contains("cluster_name"); // httpClientSecuredContainer {{ } // } @@ -275,7 +269,7 @@ public void transportClientClusterHealth() { ClusterHealthResponse healths = transportClient.admin().cluster().prepareHealth().get(); String clusterName = healths.getClusterName(); // }}} - assertThat(clusterName, is(expectedClusterName)); + assertThat(clusterName).isEqualTo(expectedClusterName); // transportClientContainer {{{ } } @@ -285,18 +279,18 @@ public void transportClientClusterHealth() { @Test public void incompatibleSettingsTest() { // The OSS image can not use security feature - assertThrows( - "We should not be able to activate security with an OSS License", - IllegalArgumentException.class, - () -> { + assertThat( + catchThrowable(() -> { new ElasticsearchContainer( DockerImageName .parse("docker.elastic.co/elasticsearch/elasticsearch-oss") .withTag(ELASTICSEARCH_VERSION) ) .withPassword("foo"); - } - ); + }) + ) + .as("We should not be able to activate security with an OSS License") + .isInstanceOf(IllegalArgumentException.class); } @Test @@ -310,8 +304,8 @@ public void testElasticsearch8SecureByDefault() throws Exception { container.start(); Response response = getClusterHealth(container); - assertThat(response.getStatusLine().getStatusCode(), is(200)); - assertThat(EntityUtils.toString(response.getEntity()), containsString("cluster_name")); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); + assertThat(EntityUtils.toString(response.getEntity())).contains("cluster_name"); } } @@ -329,11 +323,11 @@ public void testElasticsearch8SecureByDefaultCustomCaCertFails() throws Exceptio container.start(); // this is expected, as a different cert is used for creating the SSL context - assertThrows( - "PKIX path validation failed: java.security.cert.CertPathValidatorException: Path does not chain with any of the trust anchors", - SSLHandshakeException.class, - () -> getClusterHealth(container) - ); + assertThat(catchThrowable(() -> getClusterHealth(container))) + .as( + "PKIX path validation failed: java.security.cert.CertPathValidatorException: Path does not chain with any of the trust anchors" + ) + .isInstanceOf(SSLHandshakeException.class); } } @@ -357,8 +351,8 @@ public void testElasticsearch8SecureByDefaultHttpWaitStrategy() throws Exception container.start(); Response response = getClusterHealth(container); - assertThat(response.getStatusLine().getStatusCode(), is(200)); - assertThat(EntityUtils.toString(response.getEntity()), containsString("cluster_name")); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); + assertThat(EntityUtils.toString(response.getEntity())).contains("cluster_name"); } } @@ -376,8 +370,8 @@ public void testElasticsearch8SecureByDefaultFailsSilentlyOnLatestImages() throw container.start(); Response response = getClient(container).performRequest(new Request("GET", "/_cluster/health")); - assertThat(response.getStatusLine().getStatusCode(), is(200)); - assertThat(EntityUtils.toString(response.getEntity()), containsString("cluster_name")); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); + assertThat(EntityUtils.toString(response.getEntity())).contains("cluster_name"); } } diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index 1651517dece..8336cfd8249 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -17,6 +17,7 @@ dependencies { testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") testImplementation("org.apache.httpcomponents:httpclient:4.5.13") testImplementation("ch.qos.logback:logback-classic:1.2.11") + testImplementation 'org.assertj:assertj-core:3.23.1' testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.0") } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java index fe119854c80..3a3f2d13bb7 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java @@ -11,7 +11,7 @@ import java.util.concurrent.TimeUnit; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; public class ContainerWithCustomConfigIT { @@ -33,13 +33,11 @@ void test() throws Exception { publisher.connect(); - assertThrows( - MqttSessionExpiredException.class, - () -> { + assertThatExceptionOfType(MqttSessionExpiredException.class) + .isThrownBy(() -> { // this should fail since only QoS 0 is allowed by the configuration publisher.publishWith().topic("test/topic").qos(MqttQos.EXACTLY_ONCE).send(); - } - ); + }); } } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java index 75d308e3f90..fde18e09285 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java @@ -24,8 +24,7 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class ContainerWithoutPlatformExtensionsIT { @@ -61,13 +60,13 @@ public void removeAllPlatformExtensions() throws InterruptedException { client.subscribeWith().topicFilter("extensions").send(); final Mqtt5Publish receive = publishes.receive(); - assertTrue(receive.getPayload().isPresent()); + assertThat(receive.getPayload()).isPresent(); final String extensionInfo = new String(receive.getPayloadAsBytes()); - assertFalse(extensionInfo.contains("hivemq-allow-all-extension")); - assertFalse(extensionInfo.contains("hivemq-kafka-extension")); - assertFalse(extensionInfo.contains("hivemq-bridge-extension")); - assertFalse(extensionInfo.contains("hivemq-enterprise-security-extension")); + assertThat(extensionInfo).doesNotContain("hivemq-allow-all-extension"); + assertThat(extensionInfo).doesNotContain("hivemq-kafka-extension"); + assertThat(extensionInfo).doesNotContain("hivemq-bridge-extension"); + assertThat(extensionInfo).doesNotContain("hivemq-enterprise-security-extension"); hivemq.start(); } @@ -96,13 +95,13 @@ public void removeKafkaExtension() throws InterruptedException { client.subscribeWith().topicFilter("extensions").send(); final Mqtt5Publish receive = publishes.receive(); - assertTrue(receive.getPayload().isPresent()); + assertThat(receive.getPayload().isPresent()).isTrue(); final String extensionInfo = new String(receive.getPayloadAsBytes()); - assertTrue(extensionInfo.contains("hivemq-allow-all-extension")); - assertFalse(extensionInfo.contains("hivemq-kafka-extension")); - assertTrue(extensionInfo.contains("hivemq-bridge-extension")); - assertTrue(extensionInfo.contains("hivemq-enterprise-security-extension")); + assertThat(extensionInfo).contains("hivemq-allow-all-extension"); + assertThat(extensionInfo).doesNotContain("hivemq-kafka-extension"); + assertThat(extensionInfo).contains("hivemq-bridge-extension"); + assertThat(extensionInfo).contains("hivemq-enterprise-security-extension"); } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java index 191ea41f303..cd83cb8a10d 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java @@ -22,15 +22,15 @@ import java.nio.file.Files; import java.util.concurrent.TimeUnit; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class CreateFileInCopiedDirectoryIT { private @NotNull MountableFile createDirectory() throws IOException { final File directory = new File(Files.createTempDirectory("").toFile(), "directory"); - assertTrue(directory.mkdir()); + assertThat(directory.mkdir()).isTrue(); final File subdirectory = new File(directory, "sub-directory"); - assertTrue(subdirectory.mkdir()); + assertThat(subdirectory.mkdir()).isTrue(); return MountableFile.forHostPath(directory.getPath()); } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java index 9588e3404e6..a2ee9edfbcf 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java @@ -10,7 +10,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; public class DisableEnableExtensionFromDirectoryIT { @@ -27,10 +27,8 @@ void test() throws Exception { TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); hivemq.disableExtension("Modifier Extension", "modifier-extension"); - assertThrows( - ExecutionException.class, - () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()) - ); + assertThatExceptionOfType(ExecutionException.class) + .isThrownBy(() -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost())); hivemq.enableExtension("Modifier Extension", "modifier-extension"); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java index e50664f240e..361af190a57 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java @@ -11,7 +11,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; public class DisableEnableExtensionIT { @@ -35,17 +35,13 @@ void test() throws Exception { ) { hivemq.start(); - assertThrows( - ExecutionException.class, - () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()) - ); + assertThatExceptionOfType(ExecutionException.class) + .isThrownBy(() -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost())); hivemq.enableExtension(hiveMQExtension); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); hivemq.disableExtension(hiveMQExtension); - assertThrows( - ExecutionException.class, - () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()) - ); + assertThatExceptionOfType(ExecutionException.class) + .isThrownBy(() -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost())); hivemq.enableExtension(hiveMQExtension); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQExtensionTest.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQExtensionTest.java index 08e1df797a1..38b8e918925 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQExtensionTest.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQExtensionTest.java @@ -2,15 +2,13 @@ import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; class HiveMQExtensionTest { @Test void builder_classDoesNotImplementExtensionMain_exception() { - assertThrows( - IllegalArgumentException.class, - () -> HiveMQExtension.builder().mainClass(HiveMQExtensionTest.class) - ); + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> HiveMQExtension.builder().mainClass(HiveMQExtensionTest.class)); } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQTestContainerCore.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQTestContainerCore.java index d4dd2be9621..32097e52641 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQTestContainerCore.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQTestContainerCore.java @@ -10,8 +10,8 @@ import java.io.File; import java.io.IOException; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; class HiveMQTestContainerCore { @@ -24,69 +24,72 @@ class HiveMQTestContainerCore { @Test void withExtension_fileDoesNotExist_Exception() { final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); - assertThrows(ContainerLaunchException.class, () -> container.withExtension(mountableFile)); + assertThatExceptionOfType(ContainerLaunchException.class) + .isThrownBy(() -> container.withExtension(mountableFile)); } @Test void withExtension_fileNoDirectory_Exception() throws IOException { final File extension = new File(tempDir, "extension"); - assertTrue(extension.createNewFile()); + assertThat(extension.createNewFile()).isTrue(); final MountableFile mountableFile = MountableFile.forHostPath(extension.getAbsolutePath()); - assertThrows(ContainerLaunchException.class, () -> container.withExtension(mountableFile)); + assertThatExceptionOfType(ContainerLaunchException.class) + .isThrownBy(() -> container.withExtension(mountableFile)); } @Test void withLicense_fileDoesNotExist_Exception() { final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); - assertThrows(ContainerLaunchException.class, () -> container.withLicense(mountableFile)); + assertThatExceptionOfType(ContainerLaunchException.class) + .isThrownBy(() -> container.withLicense(mountableFile)); } @Test void withExtension_fileEndingWrong_Exception() throws IOException { final File extension = new File(tempDir, "extension.wrong"); - assertTrue(extension.createNewFile()); + assertThat(extension.createNewFile()).isTrue(); final MountableFile mountableFile = MountableFile.forHostPath(extension.getAbsolutePath()); - assertThrows(ContainerLaunchException.class, () -> container.withLicense(mountableFile)); + assertThatExceptionOfType(ContainerLaunchException.class) + .isThrownBy(() -> container.withLicense(mountableFile)); } @Test void withHiveMQConfig_fileDoesNotExist_Exception() { final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); - assertThrows(ContainerLaunchException.class, () -> container.withHiveMQConfig(mountableFile)); + assertThatExceptionOfType(ContainerLaunchException.class) + .isThrownBy(() -> container.withHiveMQConfig(mountableFile)); } @Test void withFileInHomeFolder_fileDoesNotExist_Exception() { final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); - assertThrows(ContainerLaunchException.class, () -> container.withFileInHomeFolder(mountableFile, "some/path")); + assertThatExceptionOfType(ContainerLaunchException.class) + .isThrownBy(() -> container.withFileInHomeFolder(mountableFile, "some/path")); } @Test void withFileInHomeFolder_pathEmpty_Exception() { final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); - assertThrows(ContainerLaunchException.class, () -> container.withFileInHomeFolder(mountableFile, "")); + assertThatExceptionOfType(ContainerLaunchException.class) + .isThrownBy(() -> container.withFileInHomeFolder(mountableFile, "")); } @Test void withFileInExtensionHomeFolder_withPath_fileDoesNotExist_Exception() { final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); - assertThrows( - ContainerLaunchException.class, - () -> container.withFileInExtensionHomeFolder(mountableFile, "my-extension", "some/path") - ); + assertThatExceptionOfType(ContainerLaunchException.class) + .isThrownBy(() -> container.withFileInExtensionHomeFolder(mountableFile, "my-extension", "some/path")); } @Test void withFileInExtensionHomeFolder_fileDoesNotExist_Exception() { final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); - assertThrows( - ContainerLaunchException.class, - () -> { + assertThatExceptionOfType(ContainerLaunchException.class) + .isThrownBy(() -> { final HiveMQContainer hiveMQContainer = container.withFileInExtensionHomeFolder( mountableFile, "my-extension" ); - } - ); + }); } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/PathUtilTest.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/PathUtilTest.java index f1e060e68d9..d845acb6684 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/PathUtilTest.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/PathUtilTest.java @@ -2,42 +2,42 @@ import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; class PathUtilTest { @Test void prepareInnerPath_emptyString() { - assertEquals("/", PathUtil.prepareInnerPath("")); + assertThat(PathUtil.prepareInnerPath("")).isEqualTo("/"); } @Test void prepareInnerPath_onlyDelimiter() { - assertEquals("/", PathUtil.prepareInnerPath("/")); + assertThat(PathUtil.prepareInnerPath("/")).isEqualTo("/"); } @Test void prepareInnerPath_noDelimiter() { - assertEquals("/path/", PathUtil.prepareInnerPath("path")); + assertThat(PathUtil.prepareInnerPath("path")).isEqualTo("/path/"); } @Test void prepareInnerPath_delimiterAtEnd() { - assertEquals("/path/", PathUtil.prepareInnerPath("path/")); + assertThat(PathUtil.prepareInnerPath("path/")).isEqualTo("/path/"); } @Test void prepareInnerPath_delimiterAtBeginning() { - assertEquals("/path/", PathUtil.prepareInnerPath("/path")); + assertThat(PathUtil.prepareInnerPath("/path")).isEqualTo("/path/"); } @Test void prepareAppendPath_delimiterAtBeginning_noChange() { - assertEquals("/path", PathUtil.prepareAppendPath("/path")); + assertThat(PathUtil.prepareAppendPath("/path")).isEqualTo("/path"); } @Test void prepareAppendPath_delimiterAtBeginning_delimiterAdded() { - assertEquals("/path", PathUtil.prepareAppendPath("path")); + assertThat(PathUtil.prepareAppendPath("path")).isEqualTo("/path"); } } diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 0d8e2abedf9..f8b55d01bc5 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -9,10 +9,7 @@ dependencies { api 'com.googlecode.junit-toolbox:junit-toolbox:2.4' - api('org.rnorth.visible-assertions:visible-assertions:2.1.2') { - // Excluded in favor of jna-platform - exclude(group: "net.java.dev.jna", module: "jna") - } + api 'org.assertj:assertj-core:3.23.1' api 'org.apache.tomcat:tomcat-jdbc:10.0.23' api 'org.vibur:vibur-dbcp:25.0' diff --git a/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java b/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java index 5566db6244e..a152a1ae550 100644 --- a/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java +++ b/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java @@ -14,9 +14,8 @@ import java.sql.Statement; import java.util.EnumSet; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assume.assumeFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; public class AbstractJDBCDriverTest { @@ -82,12 +81,12 @@ private void performSimpleTest(HikariDataSource dataSource) throws SQLException rs -> { rs.next(); int resultSetInt = rs.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); return true; } ); - assertTrue("The database returned a record as expected", result); + assertThat(result).as("The database returned a record as expected").isTrue(); } private void performTestForScriptedSchema(HikariDataSource dataSource) throws SQLException { @@ -97,16 +96,14 @@ private void performTestForScriptedSchema(HikariDataSource dataSource) throws SQ rs -> { rs.next(); String resultSetString = rs.getString(1); - assertEquals( - "A basic SELECT query succeeds where the schema has been applied from a script", - "hello world", - resultSetString - ); + assertThat(resultSetString) + .as("A basic SELECT query succeeds where the schema has been applied from a script") + .isEqualTo("hello world"); return true; } ); - assertTrue("The database returned a record as expected", result); + assertThat(result).as("The database returned a record as expected").isTrue(); } private void performTestForJDBCParamUsage(HikariDataSource dataSource) throws SQLException { @@ -120,12 +117,12 @@ private void performTestForJDBCParamUsage(HikariDataSource dataSource) throws SQ if (resultUser.endsWith("@%")) { resultUser = resultUser.substring(0, resultUser.length() - 2); } - assertEquals("User from query param is created.", "someuser", resultUser); + assertThat(resultUser).as("User from query param is created.").isEqualTo("someuser"); return true; } ); - assertTrue("The database returned a record as expected", result); + assertThat(result).as("The database returned a record as expected").isTrue(); String databaseQuery = "SELECT DATABASE()"; // Postgres does not have Database() as a function @@ -145,12 +142,12 @@ private void performTestForJDBCParamUsage(HikariDataSource dataSource) throws SQ rs -> { rs.next(); String resultDB = rs.getString(1); - assertEquals("Database name from URL String is used.", "databasename", resultDB); + assertThat(resultDB).as("Database name from URL String is used.").isEqualTo("databasename"); return true; } ); - assertTrue("The database returned a record as expected", result); + assertThat(result).as("The database returned a record as expected").isTrue(); } private void performTestForCharacterEncodingForInitialScriptConnection(HikariDataSource dataSource) @@ -161,16 +158,14 @@ private void performTestForCharacterEncodingForInitialScriptConnection(HikariDat rs -> { rs.next(); String resultSetString = rs.getString(1); - assertEquals( - "A SELECT query succeed and the correct charset has been applied for the init script", - "привет мир", - resultSetString - ); + assertThat(resultSetString) + .as("A basic SELECT query succeeds where the schema has been applied from a script") + .isEqualTo("привет мир"); return true; } ); - assertTrue("The database returned a record as expected", result); + assertThat(result).as("The database returned a record as expected").isTrue(); } /** @@ -195,15 +190,14 @@ private HikariDataSource verifyCharacterSet(String jdbcUrl) throws SQLException rs -> { rs.next(); String resultSetString = rs.getString(2); - assertTrue( - "Passing query parameters to set DB connection encoding is successful", - resultSetString.startsWith("utf8") - ); + assertThat(resultSetString) + .as("Passing query parameters to set DB connection encoding is successful") + .startsWith("utf8"); return true; } ); - assertTrue("The database returned a record as expected", result); + assertThat(result).as("The database returned a record as expected").isTrue(); return dataSource; } @@ -213,10 +207,10 @@ private void performTestForCustomIniFile(HikariDataSource dataSource) throws SQL statement.execute("SELECT @@GLOBAL.innodb_file_format"); ResultSet resultSet = statement.getResultSet(); - assertTrue("The query returns a result", resultSet.next()); + assertThat(resultSet.next()).as("The query returns a result").isTrue(); String result = resultSet.getString(1); - assertEquals("The InnoDB file format has been set by the ini file content", "Barracuda", result); + assertThat(result).as("The InnoDB file format has been set by the ini file content").isEqualTo("Barracuda"); } private HikariDataSource getDataSource(String jdbcUrl, int poolSize) { diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 3a6681aee13..6047a3c0f3e 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -10,6 +10,5 @@ dependencies { testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.23' testImplementation 'com.zaxxer:HikariCP-java6:2.3.13' testImplementation 'com.googlecode.junit-toolbox:junit-toolbox:2.4' - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java index 9b88e6b503d..2ac8cf88b5d 100644 --- a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java +++ b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java @@ -8,7 +8,7 @@ import java.util.Arrays; import java.util.Optional; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; /** * This Test class validates that all supported JDBC URL's can be parsed by ConnectionUrl class. @@ -125,9 +125,9 @@ public static Iterable data() { @Test public void test() { ConnectionUrl url = ConnectionUrl.newInstance(jdbcUrl); - assertEquals("Database Type is as expected", databaseType, url.getDatabaseType()); - assertEquals("Image tag is as expected", tag, url.getImageTag()); - assertEquals("Database Host String is as expected", dbHostString, url.getDbHostString()); - assertEquals("Database Name is as expected", databaseName, url.getDatabaseName().orElse("")); + assertThat(url.getDatabaseType()).as("Database Type is as expected").isEqualTo(databaseType); + assertThat(url.getImageTag()).as("Image tag is as expected").isEqualTo(tag); + assertThat(url.getDbHostString()).as("Database Host String is as expected").isEqualTo(dbHostString); + assertThat(url.getDatabaseName().orElse("")).as("Database Name is as expected").isEqualTo(databaseName); } } diff --git a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java index 6888f881ec4..fa50dbbe76e 100644 --- a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java +++ b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java @@ -4,9 +4,7 @@ import org.junit.Test; import org.junit.rules.ExpectedException; -import java.util.Optional; - -import static org.rnorth.visibleassertions.VisibleAssertions.*; +import static org.assertj.core.api.Assertions.assertThat; public class ConnectionUrlTest { @@ -18,16 +16,18 @@ public void testConnectionUrl1() { String urlString = "jdbc:tc:mysql:5.7.34://somehostname:3306/databasename?a=b&c=d"; ConnectionUrl url = ConnectionUrl.newInstance(urlString); - assertEquals("Database Type value is as expected", "mysql", url.getDatabaseType()); - assertEquals("Database Image tag value is as expected", "5.7.34", url.getImageTag().get()); - assertEquals("Database Host String is as expected", "somehostname:3306/databasename", url.getDbHostString()); - assertEquals("Query String value is as expected", "?a=b&c=d", url.getQueryString().get()); - assertEquals("Database Host value is as expected", "somehostname", url.getDatabaseHost().get()); - assertEquals("Database Port value is as expected", 3306, url.getDatabasePort().get()); - assertEquals("Database Name value is as expected", "databasename", url.getDatabaseName().get()); - - assertEquals("Parameter a is captured", "b", url.getQueryParameters().get("a")); - assertEquals("Parameter c is captured", "d", url.getQueryParameters().get("c")); + assertThat(url.getDatabaseType()).as("Database Type value is as expected").isEqualTo("mysql"); + assertThat(url.getImageTag()).as("Database Image tag value is as expected").contains("5.7.34"); + assertThat(url.getDbHostString()) + .as("Database Host String is as expected") + .isEqualTo("somehostname:3306/databasename"); + assertThat(url.getQueryString()).as("Query String value is as expected").contains("?a=b&c=d"); + assertThat(url.getDatabaseHost()).as("Database Host value is as expected").contains("somehostname"); + assertThat(url.getDatabasePort()).as("Database Port value is as expected").contains(3306); + assertThat(url.getDatabaseName()).as("Database Name value is as expected").contains("databasename"); + + assertThat(url.getQueryParameters()).as("Parameter a is captured").containsEntry("a", "b"); + assertThat(url.getQueryParameters()).as("Parameter c is captured").containsEntry("c", "d"); } @Test @@ -35,21 +35,23 @@ public void testConnectionUrl2() { String urlString = "jdbc:tc:mysql://somehostname/databasename"; ConnectionUrl url = ConnectionUrl.newInstance(urlString); - assertEquals("Database Type value is as expected", "mysql", url.getDatabaseType()); - assertFalse("Database Image tag value is as expected", url.getImageTag().isPresent()); - assertEquals("Database Host String is as expected", "somehostname/databasename", url.getDbHostString()); - assertEquals("Query String value is as expected", Optional.empty(), url.getQueryString()); - assertEquals("Database Host value is as expected", "somehostname", url.getDatabaseHost().get()); - assertFalse("Database Port is null as expected", url.getDatabasePort().isPresent()); - assertEquals("Database Name value is as expected", "databasename", url.getDatabaseName().get()); - - assertTrue("Connection Parameters set is empty", url.getQueryParameters().isEmpty()); + assertThat(url.getDatabaseType()).as("Database Type value is as expected").isEqualTo("mysql"); + assertThat(url.getImageTag()).as("Database Image tag value is as expected").isNotPresent(); + assertThat(url.getDbHostString()) + .as("Database Host String is as expected") + .isEqualTo("somehostname/databasename"); + assertThat(url.getQueryString()).as("Query String value is as expected").isEmpty(); + assertThat(url.getDatabaseHost()).as("Database Host value is as expected").contains("somehostname"); + assertThat(url.getDatabasePort()).as("Database Port is null as expected").isNotPresent(); + assertThat(url.getDatabaseName()).as("Database Name value is as expected").contains("databasename"); + + assertThat(url.getQueryParameters().isEmpty()).as("Connection Parameters set is empty").isTrue(); } @Test public void testEmptyQueryParameter() { ConnectionUrl url = ConnectionUrl.newInstance("jdbc:tc:mysql://somehostname/databasename?key="); - assertEquals("'key' property value", "", url.getQueryParameters().get("key")); + assertThat(url.getQueryParameters().get("key")).as("'key' property value").isEqualTo(""); } @Test @@ -57,17 +59,13 @@ public void testTmpfsOption() { String urlString = "jdbc:tc:mysql://somehostname/databasename?TC_TMPFS=key:value,key1:value1"; ConnectionUrl url = ConnectionUrl.newInstance(urlString); - assertTrue("Connection Parameters set is empty", url.getQueryParameters().isEmpty()); - assertFalse("Container Parameters set is not empty", url.getContainerParameters().isEmpty()); - assertEquals( - "Container Parameter TC_TMPFS is true", - "key:value,key1:value1", - url.getContainerParameters().get("TC_TMPFS") - ); - assertTrue("tmpfs option key exists", url.getTmpfsOptions().containsKey("key")); - assertEquals("tmpfs option key has correct value", "value", url.getTmpfsOptions().get("key")); - assertTrue("tmpfs option key1 exists", url.getTmpfsOptions().containsKey("key1")); - assertEquals("tmpfs option key1 has correct value", "value1", url.getTmpfsOptions().get("key1")); + assertThat(url.getQueryParameters()).as("Connection Parameters set is empty").isEmpty(); + assertThat(url.getContainerParameters()).as("Container Parameters set is not empty").isNotEmpty(); + assertThat(url.getContainerParameters()) + .as("Container Parameter TC_TMPFS is true") + .containsEntry("TC_TMPFS", "key:value,key1:value1"); + assertThat(url.getTmpfsOptions()).as("tmpfs option key has correct value").containsEntry("key", "value"); + assertThat(url.getTmpfsOptions()).as("tmpfs option key1 has correct value").containsEntry("key1", "value1"); } @Test @@ -76,13 +74,13 @@ public void testInitScriptPathCapture() { "jdbc:tc:mysql:5.7.34://somehostname:3306/databasename?a=b&c=d&TC_INITSCRIPT=somepath/init_mysql.sql"; ConnectionUrl url = ConnectionUrl.newInstance(urlString); - assertEquals("Database Type value is as expected", "somepath/init_mysql.sql", url.getInitScriptPath().get()); - assertEquals("Query String value is as expected", "?a=b&c=d", url.getQueryString().get()); - assertEquals( - "INIT SCRIPT Path exists in Container Parameters", - "somepath/init_mysql.sql", - url.getContainerParameters().get("TC_INITSCRIPT") - ); + assertThat(url.getInitScriptPath()) + .as("Database Type value is as expected") + .contains("somepath/init_mysql.sql"); + assertThat(url.getQueryString()).as("Query String value is as expected").contains("?a=b&c=d"); + assertThat(url.getContainerParameters()) + .as("INIT SCRIPT Path exists in Container Parameters") + .containsEntry("TC_INITSCRIPT", "somepath/init_mysql.sql"); //Parameter sets are unmodifiable thrown.expect(UnsupportedOperationException.class); @@ -96,18 +94,14 @@ public void testInitFunctionCapture() { "jdbc:tc:mysql:5.7.34://somehostname:3306/databasename?a=b&c=d&TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction"; ConnectionUrl url = ConnectionUrl.newInstance(urlString); - assertTrue("Init Function parameter exists", url.getInitFunction().isPresent()); - - assertEquals( - "Init function class is as expected", - "org.testcontainers.jdbc.JDBCDriverTest", - url.getInitFunction().get().getClassName() - ); - assertEquals( - "Init function class is as expected", - "sampleInitFunction", - url.getInitFunction().get().getMethodName() - ); + assertThat(url.getInitFunction()).as("Init Function parameter exists").isPresent(); + + assertThat(url.getInitFunction().get().getClassName()) + .as("Init function class is as expected") + .isEqualTo("org.testcontainers.jdbc.JDBCDriverTest"); + assertThat(url.getInitFunction().get().getMethodName()) + .as("Init function class is as expected") + .isEqualTo("sampleInitFunction"); } @Test @@ -115,6 +109,6 @@ public void testDaemonCapture() { String urlString = "jdbc:tc:mysql:5.7.34://somehostname:3306/databasename?a=b&c=d&TC_DAEMON=true"; ConnectionUrl url = ConnectionUrl.newInstance(urlString); - assertTrue("Daemon flag is set to true.", url.isInDaemonMode()); + assertThat(url.isInDaemonMode()).as("Daemon flag is set to true.").isTrue(); } } diff --git a/modules/jdbc/src/test/java/org/testcontainers/jdbc/MissingJdbcDriverTest.java b/modules/jdbc/src/test/java/org/testcontainers/jdbc/MissingJdbcDriverTest.java index 68afe8692e8..379b262aa3a 100644 --- a/modules/jdbc/src/test/java/org/testcontainers/jdbc/MissingJdbcDriverTest.java +++ b/modules/jdbc/src/test/java/org/testcontainers/jdbc/MissingJdbcDriverTest.java @@ -9,9 +9,8 @@ import java.sql.SQLException; import java.util.concurrent.atomic.AtomicInteger; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.rnorth.visibleassertions.VisibleAssertions.fail; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; public class MissingJdbcDriverTest { @@ -24,12 +23,16 @@ public void shouldFailFastIfNoDriverFound() { fail("The container is expected to fail to start"); } catch (Exception e) { final Throwable rootCause = Throwables.getRootCause(e); - assertTrue("ClassNotFoundException is the root cause", rootCause instanceof ClassNotFoundException); + assertThat(rootCause) + .as("ClassNotFoundException is the root cause") + .isInstanceOf(ClassNotFoundException.class); } finally { container.stop(); } - assertEquals("only one connection attempt should have been made", 1, container.getConnectionAttempts()); + assertThat(container.getConnectionAttempts()) + .as("only one connection attempt should have been made") + .isEqualTo(1); } /** diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/ComposeContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/ComposeContainerTests.java index f49aa8732f5..866b76df301 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/ComposeContainerTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/ComposeContainerTests.java @@ -11,7 +11,7 @@ import java.io.File; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; @Testcontainers class ComposeContainerTests { @@ -38,6 +38,6 @@ void running_compose_defined_container_is_accessible_on_configured_port() throws HttpResponse response = client.execute(new HttpGet("http://" + host + ":" + port)); - assertEquals(200, response.getStatusLine().getStatusCode()); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); } } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/MixedLifecycleTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/MixedLifecycleTests.java index 6c44d978b69..e8ce714ab28 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/MixedLifecycleTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/MixedLifecycleTests.java @@ -4,7 +4,7 @@ import org.testcontainers.containers.MySQLContainer; import org.testcontainers.containers.PostgreSQLContainer; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; // testClass { @Testcontainers @@ -23,8 +23,8 @@ class MixedLifecycleTests { @Test void test() { - assertTrue(MY_SQL_CONTAINER.isRunning()); - assertTrue(postgresqlContainer.isRunning()); + assertThat(MY_SQL_CONTAINER.isRunning()).isTrue(); + assertThat(postgresqlContainer.isRunning()).isTrue(); } } // } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersExtensionTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersExtensionTests.java index 526038853c9..8852524d451 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersExtensionTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersExtensionTests.java @@ -4,8 +4,7 @@ import org.junit.jupiter.api.extension.ConditionEvaluationResult; import org.junit.jupiter.api.extension.ExtensionContext; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -15,28 +14,28 @@ public class TestcontainersExtensionTests { void whenDisabledWithoutDockerAndDockerIsAvailableTestsAreEnabled() { ConditionEvaluationResult result = new TestTestcontainersExtension(true) .evaluateExecutionCondition(extensionContext(DisabledWithoutDocker.class)); - assertFalse(result.isDisabled()); + assertThat(result.isDisabled()).isFalse(); } @Test void whenDisabledWithoutDockerAndDockerIsUnavailableTestsAreDisabled() { ConditionEvaluationResult result = new TestTestcontainersExtension(false) .evaluateExecutionCondition(extensionContext(DisabledWithoutDocker.class)); - assertTrue(result.isDisabled()); + assertThat(result.isDisabled()).isTrue(); } @Test void whenEnabledWithoutDockerAndDockerIsAvailableTestsAreEnabled() { ConditionEvaluationResult result = new TestTestcontainersExtension(true) .evaluateExecutionCondition(extensionContext(EnabledWithoutDocker.class)); - assertFalse(result.isDisabled()); + assertThat(result.isDisabled()).isFalse(); } @Test void whenEnabledWithoutDockerAndDockerIsUnavailableTestsAreEnabled() { ConditionEvaluationResult result = new TestTestcontainersExtension(false) .evaluateExecutionCondition(extensionContext(EnabledWithoutDocker.class)); - assertFalse(result.isDisabled()); + assertThat(result.isDisabled()).isFalse(); } private ExtensionContext extensionContext(Class clazz) { diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java index c41b184defa..f3d81a0dfcd 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java @@ -4,8 +4,7 @@ import org.junit.jupiter.api.Test; import org.testcontainers.containers.GenericContainer; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; // testClass { @Testcontainers @@ -24,7 +23,7 @@ class TestcontainersNestedRestartedContainerTests { // testClass { @Test void top_level_container_should_be_running() { - assertTrue(topLevelContainer.isRunning()); + assertThat(topLevelContainer.isRunning()).isTrue(); // }} topLevelContainerId = topLevelContainer.getContainerId(); // testClass {{ @@ -40,14 +39,14 @@ class NestedTestCase { @Test void both_containers_should_be_running() { // top level container is restarted for nested methods - assertTrue(topLevelContainer.isRunning()); + assertThat(topLevelContainer.isRunning()).isTrue(); // nested containers are only available inside their nested class - assertTrue(nestedContainer.isRunning()); + assertThat(nestedContainer.isRunning()).isTrue(); // }}} if (nestedContainerId == null) { nestedContainerId = nestedContainer.getContainerId(); } else { - assertNotEquals(nestedContainerId, nestedContainer.getContainerId()); + assertThat(nestedContainer.getContainerId()).isNotEqualTo(nestedContainerId); } // testClass {{ } @@ -55,23 +54,23 @@ void both_containers_should_be_running() { // } @Test void containers_should_not_be_the_same() { - assertNotEquals(topLevelContainer.getContainerId(), nestedContainer.getContainerId()); + assertThat(nestedContainer.getContainerId()).isNotEqualTo(topLevelContainer.getContainerId()); if (nestedContainerId == null) { nestedContainerId = nestedContainer.getContainerId(); } else { - assertNotEquals(nestedContainerId, nestedContainer.getContainerId()); + assertThat(nestedContainer.getContainerId()).isNotEqualTo(nestedContainerId); } } @Test void ids_should_not_change() { - assertNotEquals(topLevelContainerId, topLevelContainer.getContainerId()); + assertThat(topLevelContainer.getContainerId()).isNotEqualTo(topLevelContainerId); if (nestedContainerId == null) { nestedContainerId = nestedContainer.getContainerId(); } else { - assertNotEquals(nestedContainerId, nestedContainer.getContainerId()); + assertThat(nestedContainer.getContainerId()).isNotEqualTo(nestedContainerId); } } // testClass {{{ diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedSharedContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedSharedContainerTests.java index c00e66c2d9d..61a4df2c6ec 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedSharedContainerTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedSharedContainerTests.java @@ -4,8 +4,7 @@ import org.junit.jupiter.api.Test; import org.testcontainers.containers.GenericContainer; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; @Testcontainers class TestcontainersNestedSharedContainerTests { @@ -20,7 +19,7 @@ class TestcontainersNestedSharedContainerTests { @Test void top_level_container_should_be_running() { - assertTrue(TOP_LEVEL_CONTAINER.isRunning()); + assertThat(TOP_LEVEL_CONTAINER.isRunning()).isTrue(); topLevelContainerId = TOP_LEVEL_CONTAINER.getContainerId(); } @@ -29,12 +28,12 @@ class NestedTestCase { @Test void top_level_containers_should_be_running() { - assertTrue(TOP_LEVEL_CONTAINER.isRunning()); + assertThat(TOP_LEVEL_CONTAINER.isRunning()).isTrue(); } @Test void ids_should_not_change() { - assertEquals(topLevelContainerId, TOP_LEVEL_CONTAINER.getContainerId()); + assertThat(TOP_LEVEL_CONTAINER.getContainerId()).isEqualTo(topLevelContainerId); } } } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersRestartBetweenTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersRestartBetweenTests.java index 46c6045984f..53e6a0968a0 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersRestartBetweenTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersRestartBetweenTests.java @@ -3,7 +3,7 @@ import org.junit.jupiter.api.Test; import org.testcontainers.containers.GenericContainer; -import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.assertj.core.api.Assertions.assertThat; @Testcontainers class TestcontainersRestartBetweenTests { @@ -19,7 +19,7 @@ void first_test() { if (lastContainerId == null) { lastContainerId = genericContainer.getContainerId(); } else { - assertNotEquals(lastContainerId, genericContainer.getContainerId()); + assertThat(genericContainer.getContainerId()).isNotEqualTo(lastContainerId); } } @@ -28,7 +28,7 @@ void second_test() { if (lastContainerId == null) { lastContainerId = genericContainer.getContainerId(); } else { - assertNotEquals(lastContainerId, genericContainer.getContainerId()); + assertThat(genericContainer.getContainerId()).isNotEqualTo(lastContainerId); } } } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersSharedContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersSharedContainerTests.java index b23b2fe0e53..9bd23e8d20e 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersSharedContainerTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersSharedContainerTests.java @@ -4,8 +4,7 @@ import org.junit.jupiter.api.Test; import org.testcontainers.containers.GenericContainer; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; @Testcontainers class TestcontainersSharedContainerTests { @@ -20,7 +19,7 @@ class TestcontainersSharedContainerTests { @BeforeAll static void doSomethingWithAContainer() { - assertTrue(GENERIC_CONTAINER.isRunning()); + assertThat(GENERIC_CONTAINER.isRunning()).isTrue(); } @Test @@ -28,7 +27,7 @@ void first_test() { if (lastContainerId == null) { lastContainerId = GENERIC_CONTAINER.getContainerId(); } else { - assertEquals(lastContainerId, GENERIC_CONTAINER.getContainerId()); + assertThat(GENERIC_CONTAINER.getContainerId()).isEqualTo(lastContainerId); } } @@ -37,7 +36,7 @@ void second_test() { if (lastContainerId == null) { lastContainerId = GENERIC_CONTAINER.getContainerId(); } else { - assertEquals(lastContainerId, GENERIC_CONTAINER.getContainerId()); + assertThat(GENERIC_CONTAINER.getContainerId()).isEqualTo(lastContainerId); } } } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/InheritedTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/InheritedTests.java index adeaf58675c..f57fde6b419 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/InheritedTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/InheritedTests.java @@ -3,7 +3,7 @@ import org.junit.jupiter.api.Test; import org.testcontainers.junit.jupiter.Container; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; class InheritedTests extends AbstractTestBase { @@ -12,15 +12,15 @@ class InheritedTests extends AbstractTestBase { @Test void step1() { - assertEquals(1, redisPerClass.getJedis().incr("key")); - assertEquals(1, redisPerTest.getJedis().incr("key")); - assertEquals(1, myRedis.getJedis().incr("key")); + assertThat(redisPerClass.getJedis().incr("key")).isEqualTo(1); + assertThat(redisPerTest.getJedis().incr("key")).isEqualTo(1); + assertThat(myRedis.getJedis().incr("key")).isEqualTo(1); } @Test void step2() { - assertEquals(2, redisPerClass.getJedis().incr("key")); - assertEquals(1, redisPerTest.getJedis().incr("key")); - assertEquals(1, myRedis.getJedis().incr("key")); + assertThat(redisPerClass.getJedis().incr("key")).isEqualTo(2); + assertThat(redisPerTest.getJedis().incr("key")).isEqualTo(1); + assertThat(myRedis.getJedis().incr("key")).isEqualTo(1); } } diff --git a/modules/k3s/src/test/java/org/testcontainers/k3s/OfficialClientK3sContainerTest.java b/modules/k3s/src/test/java/org/testcontainers/k3s/OfficialClientK3sContainerTest.java index 970e2390457..c2504c27dbe 100644 --- a/modules/k3s/src/test/java/org/testcontainers/k3s/OfficialClientK3sContainerTest.java +++ b/modules/k3s/src/test/java/org/testcontainers/k3s/OfficialClientK3sContainerTest.java @@ -6,7 +6,6 @@ import io.kubernetes.client.openapi.models.V1NodeList; import io.kubernetes.client.util.Config; import lombok.extern.slf4j.Slf4j; -import org.assertj.core.api.Assertions; import org.junit.Test; import org.testcontainers.containers.output.Slf4jLogConsumer; import org.testcontainers.utility.DockerImageName; @@ -14,6 +13,8 @@ import java.io.IOException; import java.io.StringReader; +import static org.assertj.core.api.Assertions.assertThat; + @Slf4j public class OfficialClientK3sContainerTest { @@ -37,7 +38,7 @@ public void shouldStartAndHaveListableNode() throws IOException, ApiException { V1NodeList nodes = api.listNode(null, null, null, null, null, null, null, null, null, null); // } - Assertions.assertThat(nodes.getItems()).hasSize(1); + assertThat(nodes.getItems()).hasSize(1); } } } diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 72149520b6a..8702d2e1a83 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -8,5 +8,5 @@ dependencies { testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.273' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.273' testImplementation 'software.amazon.awssdk:s3:2.17.244' - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java index c075d2805d6..a535699438f 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java @@ -15,9 +15,7 @@ import java.util.Arrays; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; @RunWith(Enclosed.class) public class LegacyModeTest { @@ -52,25 +50,25 @@ public void samePortIsExposedForAllServices() { localstack.start(); try { - assertTrue("A single port is exposed", localstack.getExposedPorts().size() == 1); - assertEquals( - "Endpoint overrides are different", - localstack.getEndpointOverride(Service.S3).toString(), - localstack.getEndpointOverride(Service.SQS).toString() - ); - assertEquals( - "Endpoint configuration have different endpoints", - new AwsClientBuilder.EndpointConfiguration( - localstack.getEndpointOverride(Service.S3).toString(), - localstack.getRegion() - ) - .getServiceEndpoint(), + assertThat(localstack.getExposedPorts()).as("A single port is exposed").hasSize(1); + assertThat(localstack.getEndpointOverride(Service.SQS).toString()) + .as("Endpoint overrides are different") + .isEqualTo(localstack.getEndpointOverride(Service.S3).toString()); + assertThat( new AwsClientBuilder.EndpointConfiguration( localstack.getEndpointOverride(Service.SQS).toString(), localstack.getRegion() ) .getServiceEndpoint() - ); + ) + .as("Endpoint configuration have different endpoints") + .isEqualTo( + new AwsClientBuilder.EndpointConfiguration( + localstack.getEndpointOverride(Service.S3).toString(), + localstack.getRegion() + ) + .getServiceEndpoint() + ); } finally { localstack.stop(); } @@ -117,25 +115,25 @@ public void differentPortsAreExposed() { localstack.start(); try { - assertTrue("Multiple ports are exposed", localstack.getExposedPorts().size() > 1); - assertNotEquals( - "Endpoint overrides are different", - localstack.getEndpointOverride(Service.S3).toString(), - localstack.getEndpointOverride(Service.SQS).toString() - ); - assertNotEquals( - "Endpoint configuration have different endpoints", - new AwsClientBuilder.EndpointConfiguration( - localstack.getEndpointOverride(Service.S3).toString(), - localstack.getRegion() - ) - .getServiceEndpoint(), + assertThat(localstack.getExposedPorts()).as("Multiple ports are exposed").hasSizeGreaterThan(1); + assertThat(localstack.getEndpointOverride(Service.SQS).toString()) + .as("Endpoint overrides are different") + .isNotEqualTo(localstack.getEndpointOverride(Service.S3).toString()); + assertThat( new AwsClientBuilder.EndpointConfiguration( localstack.getEndpointOverride(Service.SQS).toString(), localstack.getRegion() ) .getServiceEndpoint() - ); + ) + .as("Endpoint configuration have different endpoints") + .isNotEqualTo( + new AwsClientBuilder.EndpointConfiguration( + localstack.getEndpointOverride(Service.S3).toString(), + localstack.getRegion() + ) + .getServiceEndpoint() + ); } finally { localstack.stop(); } diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java index 950f29866ba..a678d30eb80 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java @@ -38,14 +38,11 @@ import software.amazon.awssdk.services.s3.S3Client; import java.io.IOException; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Optional; -import static org.hamcrest.Matchers.containsString; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * Tests for Localstack Container, used both in bridge network (exposed to host) and docker network modes. @@ -101,17 +98,17 @@ public void s3TestOverBridgeNetwork() throws IOException { .stream() .filter(b -> b.getName().equals(bucketName)) .findFirst(); - assertTrue("The created bucket is present", maybeBucket.isPresent()); + assertThat(maybeBucket).as("The created bucket is present").isPresent(); final Bucket bucket = maybeBucket.get(); - assertEquals("The created bucket has the right name", bucketName, bucket.getName()); + assertThat(bucket.getName()).as("The created bucket has the right name").isEqualTo(bucketName); final ObjectListing objectListing = s3.listObjects(bucketName); - assertEquals("The created bucket has 1 item in it", 1, objectListing.getObjectSummaries().size()); + assertThat(objectListing.getObjectSummaries()).as("The created bucket has 1 item in it").hasSize(1); final S3Object object = s3.getObject(bucketName, "bar"); - final String content = IOUtils.toString(object.getObjectContent(), Charset.forName("UTF-8")); - assertEquals("The object can be retrieved", "baz", content); + final String content = IOUtils.toString(object.getObjectContent(), StandardCharsets.UTF_8); + assertThat(content).as("The object can be retrieved").isEqualTo("baz"); } @Test @@ -131,10 +128,9 @@ public void s3TestUsingAwsSdkV2() { final String bucketName = "foov2"; s3.createBucket(b -> b.bucket(bucketName)); - assertTrue( - "New bucket was created", - s3.listBuckets().buckets().stream().anyMatch(b -> b.name().equals(bucketName)) - ); + assertThat(s3.listBuckets().buckets().stream().anyMatch(b -> b.name().equals(bucketName))) + .as("New bucket was created") + .isTrue(); } @Test @@ -156,16 +152,14 @@ public void sqsTestOverBridgeNetwork() { CreateQueueResult queueResult = sqs.createQueue("baz"); String fooQueueUrl = queueResult.getQueueUrl(); - assertThat( - "Created queue has external hostname URL", - fooQueueUrl, - containsString( + assertThat(fooQueueUrl) + .as("Created queue has external hostname URL") + .contains( "http://" + DockerClientFactory.instance().dockerHostIpAddress() + ":" + localstack.getMappedPort(LocalStackContainer.PORT) - ) - ); + ); sqs.sendMessage(fooQueueUrl, "test"); final long messageCount = sqs @@ -174,7 +168,7 @@ public void sqsTestOverBridgeNetwork() { .stream() .filter(message -> message.getBody().equals("test")) .count(); - assertEquals("the sent message can be received", 1L, messageCount); + assertThat(messageCount).as("the sent message can be received").isEqualTo(1L); } @Test @@ -197,8 +191,8 @@ public void cloudWatchLogsTestOverBridgeNetwork() { logs.createLogGroup(new CreateLogGroupRequest("foo")); List groups = logs.describeLogGroups().getLogGroups(); - assertEquals("One log group should be created", 1, groups.size()); - assertEquals("Name of created log group is [foo]", "foo", groups.get(0).getLogGroupName()); + assertThat(groups).as("One log group should be created").hasSize(1); + assertThat(groups.get(0).getLogGroupName()).as("Name of created log group is [foo]").isEqualTo("foo"); } @Test @@ -223,34 +217,32 @@ public void kmsKeyCreationTest() { CreateKeyRequest req = new CreateKeyRequest().withDescription(desc).withTags(createdByTag); CreateKeyResult key = awskms.createKey(req); - assertEquals( - "AWS KMS Customer Managed Key should be created ", - key.getKeyMetadata().getDescription(), - desc - ); + assertThat(desc) + .as("AWS KMS Customer Managed Key should be created ") + .isEqualTo(key.getKeyMetadata().getDescription()); } @Test public void samePortIsExposedForAllServices() { - assertTrue("A single port is exposed", localstack.getExposedPorts().size() == 1); - assertEquals( - "Endpoint overrides are different", - localstack.getEndpointOverride(Service.S3).toString(), - localstack.getEndpointOverride(Service.SQS).toString() - ); - assertEquals( - "Endpoint configuration have different endpoints", - new AwsClientBuilder.EndpointConfiguration( - localstack.getEndpointOverride(Service.S3).toString(), - localstack.getRegion() - ) - .getServiceEndpoint(), + assertThat(localstack.getExposedPorts()).as("A single port is exposed").hasSize(1); + assertThat(localstack.getEndpointOverride(Service.SQS).toString()) + .as("Endpoint overrides are different") + .isEqualTo(localstack.getEndpointOverride(Service.S3).toString()); + assertThat( new AwsClientBuilder.EndpointConfiguration( localstack.getEndpointOverride(Service.SQS).toString(), localstack.getRegion() ) .getServiceEndpoint() - ); + ) + .as("Endpoint configuration have different endpoints") + .isEqualTo( + new AwsClientBuilder.EndpointConfiguration( + localstack.getEndpointOverride(Service.S3).toString(), + localstack.getRegion() + ) + .getServiceEndpoint() + ); } } @@ -292,11 +284,9 @@ public void sqsTestOverDockerNetwork() throws Exception { "sqs create-queue --queue-name baz" ); - assertThat( - "Created queue has external hostname URL", - queueCreationResponse, - containsString("http://localstack:" + LocalStackContainer.PORT) - ); + assertThat(queueCreationResponse) + .as("Created queue has external hostname URL") + .contains("http://localstack:" + LocalStackContainer.PORT); runAwsCliAgainstDockerNetworkContainer( String.format( @@ -313,7 +303,7 @@ public void sqsTestOverDockerNetwork() throws Exception { ) ); - assertTrue("the sent message can be received", message.contains("\"Body\": \"test\"")); + assertThat(message).as("the sent message can be received").contains("\"Body\": \"test\""); } @Test @@ -356,11 +346,9 @@ public void s3EndpointHasProperRegion() { localstack.getEndpointOverride(Service.S3).toString(), localstack.getRegion() ); - assertEquals( - "The endpoint configuration has right region", - region, - endpointConfiguration.getSigningRegion() - ); + assertThat(endpointConfiguration.getSigningRegion()) + .as("The endpoint configuration has right region") + .isEqualTo(region); } } } diff --git a/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java b/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java index 21a48b6e73c..e403a09b382 100644 --- a/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java +++ b/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java @@ -9,11 +9,8 @@ import java.sql.ResultSet; import java.sql.SQLException; -import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assume.assumeFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; public class SimpleMariaDBTest extends AbstractContainerDatabaseTest { @@ -25,7 +22,7 @@ public void testSimple() throws SQLException { ResultSet resultSet = performQuery(mariadb, "SELECT 1"); int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); } } @@ -41,10 +38,9 @@ public void testSpecificVersion() throws SQLException { ResultSet resultSet = performQuery(mariadbOldVersion, "SELECT VERSION()"); String resultSetString = resultSet.getString(1); - assertTrue( - "The database version can be set using a container rule parameter", - resultSetString.startsWith("5.5.51") - ); + assertThat(resultSetString) + .as("The database version can be set using a container rule parameter") + .startsWith("5.5.51"); } } @@ -63,7 +59,7 @@ public void testMariaDBWithCustomIniFile() throws SQLException { ResultSet resultSet = performQuery(mariadbCustomConfig, "SELECT @@GLOBAL.innodb_file_format"); String result = resultSet.getString(1); - assertEquals("The InnoDB file format has been set by the ini file content", "Barracuda", result); + assertThat(result).as("The InnoDB file format has been set by the ini file content").isEqualTo("Barracuda"); } } @@ -77,7 +73,7 @@ public void testMariaDBWithCommandOverride() throws SQLException { ResultSet resultSet = performQuery(mariadbCustomConfig, "show variables like 'auto_increment_increment'"); String result = resultSet.getString("Value"); - assertEquals("Auto increment increment should be overriden by command line", "10", result); + assertThat(result).as("Auto increment increment should be overriden by command line").isEqualTo("10"); } } @@ -90,10 +86,10 @@ public void testWithAdditionalUrlParamInJdbcUrl() { try { mariaDBContainer.start(); String jdbcUrl = mariaDBContainer.getJdbcUrl(); - assertThat(jdbcUrl, containsString("?")); - assertThat(jdbcUrl, containsString("&")); - assertThat(jdbcUrl, containsString("rewriteBatchedStatements=true")); - assertThat(jdbcUrl, containsString("connectTimeout=40000")); + assertThat(jdbcUrl).contains("?"); + assertThat(jdbcUrl).contains("&"); + assertThat(jdbcUrl).contains("rewriteBatchedStatements=true"); + assertThat(jdbcUrl).contains("connectTimeout=40000"); } finally { mariaDBContainer.stop(); } diff --git a/modules/mockserver/build.gradle b/modules/mockserver/build.gradle index 9ea504ca74a..2156ef045cd 100644 --- a/modules/mockserver/build.gradle +++ b/modules/mockserver/build.gradle @@ -4,5 +4,5 @@ dependencies { api project(':testcontainers') testImplementation 'org.mock-server:mockserver-client-java:5.5.4' - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java index 3f34d000603..84d472f5e75 100644 --- a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java +++ b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java @@ -5,10 +5,9 @@ import org.mockserver.client.MockServerClient; import org.testcontainers.utility.DockerImageName; -import static org.hamcrest.Matchers.containsString; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockserver.model.HttpRequest.request; import static org.mockserver.model.HttpResponse.response; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; public class MockServerContainerRuleTest { @@ -37,10 +36,8 @@ public void shouldReturnExpectation() throws Exception { // } // spotless:on - assertThat( - "Expectation returns expected response body", - SimpleHttpClient.responseFromMockserver(mockServer, "/person?name=peter"), - containsString("Peter the person") - ); + assertThat(SimpleHttpClient.responseFromMockserver(mockServer, "/person?name=peter")) + .as("Expectation returns expected response body") + .contains("Peter the person"); } } diff --git a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java index d038b111824..6ddce4b6cbf 100644 --- a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java +++ b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java @@ -4,11 +4,9 @@ import org.mockserver.client.MockServerClient; import org.testcontainers.utility.DockerImageName; -import static org.hamcrest.Matchers.equalTo; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockserver.model.HttpRequest.request; import static org.mockserver.model.HttpResponse.response; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; public class MockServerContainerTest { @@ -30,15 +28,13 @@ public void shouldCallActualMockserverVersion() throws Exception { MockServerClient client = new MockServerClient(mockServer.getHost(), mockServer.getServerPort()); - assertTrue("Mockserver running", client.isRunning()); + assertThat(client.isRunning()).as("Mockserver running").isTrue(); client.when(request().withPath("/hello")).respond(response().withBody(expectedBody)); - assertThat( - "MockServer returns correct result", - SimpleHttpClient.responseFromMockserver(mockServer, "/hello"), - equalTo(expectedBody) - ); + assertThat(SimpleHttpClient.responseFromMockserver(mockServer, "/hello")) + .as("MockServer returns correct result") + .isEqualTo(expectedBody); } } diff --git a/modules/mockserver/src/test/resources/logback-test.xml b/modules/mockserver/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..83ef7a1a3ef --- /dev/null +++ b/modules/mockserver/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java index 49bae13d9e3..97904c6b7d1 100644 --- a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java +++ b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java @@ -10,7 +10,7 @@ import java.util.Arrays; import java.util.Collection; -import static org.rnorth.visibleassertions.VisibleAssertions.fail; +import static org.assertj.core.api.Assertions.fail; /** * Tests if the password passed to the container satisfied the password policy described at diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomizableMSSQLServerTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomizableMSSQLServerTest.java index 21ea5380801..4cfc34f9e73 100644 --- a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomizableMSSQLServerTest.java +++ b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomizableMSSQLServerTest.java @@ -8,7 +8,7 @@ import java.sql.ResultSet; import java.sql.SQLException; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class CustomizableMSSQLServerTest extends AbstractContainerDatabaseTest { @@ -26,7 +26,7 @@ public void testSqlServerConnection() throws SQLException { ResultSet resultSet = performQuery(mssqlServerContainer, mssqlServerContainer.getTestQueryString()); int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); } } } diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java index 36d215c8757..15edbaa2ca1 100644 --- a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java +++ b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java @@ -11,9 +11,7 @@ import javax.sql.DataSource; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class SimpleMSSQLServerTest extends AbstractContainerDatabaseTest { @@ -26,7 +24,7 @@ public void testSimple() throws SQLException { ResultSet resultSet = performQuery(mssqlServer, "SELECT 1"); int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); } } @@ -40,7 +38,7 @@ public void testWithAdditionalUrlParamInJdbcUrl() { mssqlServer.start(); String jdbcUrl = mssqlServer.getJdbcUrl(); - assertThat(jdbcUrl, containsString(";integratedSecurity=false;applicationName=MyApp")); + assertThat(jdbcUrl).contains(";integratedSecurity=false;applicationName=MyApp"); } } @@ -63,7 +61,7 @@ public void testSetupDatabase() throws SQLException { resultSet.next(); int resultSetInt = resultSet.getInt("ID"); - assertEquals("A basic SELECT query succeeds", 3, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(3); } } } diff --git a/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/JDBCDriverWithPoolTest.java b/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/JDBCDriverWithPoolTest.java index a30f3f59888..254f58807b7 100644 --- a/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/JDBCDriverWithPoolTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/JDBCDriverWithPoolTest.java @@ -21,7 +21,7 @@ import javax.sql.DataSource; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; /** * This test belongs in the jdbc module, as it is focused on testing the behaviour of {@link org.testcontainers.containers.JdbcDatabaseContainer}. @@ -82,7 +82,7 @@ public void testMySQLWithConnectionPoolUsingSameContainer() throws SQLException, return rs.getInt(1); } ); - assertEquals("Reuse of a datasource points to the same DB container", 100, count); + assertThat(count).as("Reuse of a datasource points to the same DB container").isEqualTo(100); int sum = new QueryRunner(dataSource) .query( @@ -93,7 +93,7 @@ public void testMySQLWithConnectionPoolUsingSameContainer() throws SQLException, } ); // 100 records * 5 = 500 expected - assertEquals("Reuse of a datasource points to the same DB container", 500, sum); + assertThat(sum).as("Reuse of a datasource points to the same DB container").isEqualTo(500); } private static DataSource getTomcatDataSourceWithDriverClassName() { diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java index 20b49d19201..f0a4f5d24ea 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java @@ -8,7 +8,7 @@ import java.sql.ResultSet; import java.sql.SQLException; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class CustomizableMysqlTest extends AbstractContainerDatabaseTest { @@ -33,7 +33,7 @@ public void testSimple() throws SQLException { ResultSet resultSet = performQuery(mysql, "SELECT 1"); int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); } } } diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java index 735af251236..8a6c17c538e 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java @@ -11,7 +11,7 @@ import java.sql.ResultSet; import java.sql.SQLException; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; @RunWith(Parameterized.class) public class MultiVersionMySQLTest extends AbstractContainerDatabaseTest { @@ -35,11 +35,9 @@ public void versionCheckTest() throws SQLException { final ResultSet resultSet = performQuery(mysql, "SELECT VERSION()"); final String resultSetString = resultSet.getString(1); - assertEquals( - "The database version can be set using a container rule parameter", - dockerImageName.getVersionPart(), - resultSetString - ); + assertThat(resultSetString) + .as("The database version can be set using a container rule parameter") + .isEqualTo(dockerImageName.getVersionPart()); } } } diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java index 6441724c443..146010058ac 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java @@ -19,12 +19,9 @@ import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; -import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; import static org.junit.Assume.assumeFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.rnorth.visibleassertions.VisibleAssertions.fail; public class SimpleMySQLTest extends AbstractContainerDatabaseTest { @@ -59,7 +56,7 @@ public void testSimple() throws SQLException { ResultSet resultSet = performQuery(mysql, "SELECT 1"); int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); } } @@ -75,10 +72,9 @@ public void testSpecificVersion() throws SQLException { ResultSet resultSet = performQuery(mysqlOldVersion, "SELECT VERSION()"); String resultSetString = resultSet.getString(1); - assertTrue( - "The database version can be set using a container rule parameter", - resultSetString.startsWith("5.6") - ); + assertThat(resultSetString) + .as("The database version can be set using a container rule parameter") + .startsWith("5.6"); } } @@ -95,7 +91,7 @@ public void testMySQLWithCustomIniFile() throws SQLException { ResultSet resultSet = performQuery(mysqlCustomConfig, "SELECT @@GLOBAL.innodb_file_format"); String result = resultSet.getString(1); - assertEquals("The InnoDB file format has been set by the ini file content", "Barracuda", result); + assertThat(result).as("The InnoDB file format has been set by the ini file content").isEqualTo("Barracuda"); } } @@ -110,7 +106,7 @@ public void testCommandOverride() throws SQLException { ResultSet resultSet = performQuery(mysqlCustomConfig, "show variables like 'auto_increment_increment'"); String result = resultSet.getString("Value"); - assertEquals("Auto increment increment should be overriden by command line", "42", result); + assertThat(result).as("Auto increment increment should be overriden by command line").isEqualTo("42"); } } @@ -126,7 +122,7 @@ public void testExplicitInitScript() throws SQLException { ResultSet resultSet = performQuery(container, "SELECT foo FROM bar"); String firstColumnValue = resultSet.getString(1); - assertEquals("Value from init script should equal real value", "hello world", firstColumnValue); + assertThat(firstColumnValue).as("Value from init script should equal real value").isEqualTo("hello world"); } } @@ -159,7 +155,7 @@ public void testEmptyPasswordWithRootUser() throws SQLException { ResultSet resultSet = performQuery(mysql, "SELECT 1"); int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); } } @@ -191,7 +187,7 @@ public void testWithAdditionalUrlParamTimeZone() throws SQLException { actualDateTime, expectedDateTime ); - assertTrue(message, actualDateTime.equals(expectedDateTime)); + assertThat(actualDateTime).as(message).isEqualTo(expectedDateTime); } } finally { mysql.stop(); @@ -216,7 +212,7 @@ public void testWithAdditionalUrlParamMultiQueries() throws SQLException { try (ResultSet resultSet = statement.getResultSet()) { resultSet.next(); String firstColumnValue = resultSet.getString(1); - assertEquals("Value from bar should equal real value", "hello world", firstColumnValue); + assertThat(firstColumnValue).as("Value from bar should equal real value").isEqualTo("hello world"); } } finally { mysql.stop(); @@ -233,10 +229,10 @@ public void testWithAdditionalUrlParamInJdbcUrl() { try { mysql.start(); String jdbcUrl = mysql.getJdbcUrl(); - assertThat(jdbcUrl, containsString("?")); - assertThat(jdbcUrl, containsString("&")); - assertThat(jdbcUrl, containsString("rewriteBatchedStatements=true")); - assertThat(jdbcUrl, containsString("allowMultiQueries=true")); + assertThat(jdbcUrl).contains("?"); + assertThat(jdbcUrl).contains("&"); + assertThat(jdbcUrl).contains("rewriteBatchedStatements=true"); + assertThat(jdbcUrl).contains("allowMultiQueries=true"); } finally { mysql.stop(); } diff --git a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java index 20cd38dc70c..de5420afd88 100644 --- a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java +++ b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java @@ -1,6 +1,5 @@ package org.testcontainers.containers; -import org.assertj.core.api.Assertions; import org.junit.Test; import org.neo4j.driver.AuthToken; import org.neo4j.driver.AuthTokens; @@ -13,8 +12,7 @@ import java.util.Collections; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.*; import static org.assertj.core.api.Assumptions.assumeThat; /** @@ -64,16 +62,14 @@ public void shouldCopyDatabase() { @Test public void shouldFailOnCopyDatabaseForDefaultNeo4j4Image() { - Assertions - .assertThatIllegalArgumentException() + assertThatIllegalArgumentException() .isThrownBy(() -> new Neo4jContainer<>().withDatabase(MountableFile.forClasspathResource("/test-graph.db"))) .withMessage("Copying database folder is not supported for Neo4j instances with version 4.0 or higher."); } @Test public void shouldFailOnCopyDatabaseForCustomNeo4j4Image() { - Assertions - .assertThatIllegalArgumentException() + assertThatIllegalArgumentException() .isThrownBy(() -> { new Neo4jContainer<>("neo4j:4.4.1").withDatabase(MountableFile.forClasspathResource("/test-graph.db")); }) @@ -82,8 +78,7 @@ public void shouldFailOnCopyDatabaseForCustomNeo4j4Image() { @Test public void shouldFailOnCopyDatabaseForCustomNonSemverNeo4j4Image() { - Assertions - .assertThatIllegalArgumentException() + assertThatIllegalArgumentException() .isThrownBy(() -> { new Neo4jContainer<>("neo4j:latest").withDatabase(MountableFile.forClasspathResource("/test-graph.db")); }) diff --git a/modules/nginx/build.gradle b/modules/nginx/build.gradle index 288b45b027a..aee69e253ad 100644 --- a/modules/nginx/build.gradle +++ b/modules/nginx/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: Nginx" dependencies { api project(':testcontainers') compileOnly 'org.jetbrains:annotations:23.0.0' - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java b/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java index c1ba3d30684..24e913cf281 100644 --- a/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java +++ b/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java @@ -12,8 +12,7 @@ import java.net.URL; import java.net.URLConnection; -import static org.hamcrest.Matchers.containsString; -import static org.rnorth.visibleassertions.VisibleAssertions.*; +import static org.assertj.core.api.Assertions.assertThat; /** * @author richardnorth @@ -55,11 +54,9 @@ public void testSimple() throws Exception { // getFromNginxServer { URL baseUrl = nginx.getBaseUrl("http", 80); - assertThat( - "An HTTP GET from the Nginx server returns the index.html from the custom content directory", - responseFromNginx(baseUrl), - containsString("Hello World!") - ); + assertThat(responseFromNginx(baseUrl)) + .as("An HTTP GET from the Nginx server returns the index.html from the custom content directory") + .contains("Hello World!"); // } } diff --git a/modules/oracle-xe/src/test/java/org/testcontainers/containers/jdbc/OracleJDBCDriverTest.java b/modules/oracle-xe/src/test/java/org/testcontainers/containers/jdbc/OracleJDBCDriverTest.java index 003941f4dfc..5e44f0d368c 100644 --- a/modules/oracle-xe/src/test/java/org/testcontainers/containers/jdbc/OracleJDBCDriverTest.java +++ b/modules/oracle-xe/src/test/java/org/testcontainers/containers/jdbc/OracleJDBCDriverTest.java @@ -9,7 +9,7 @@ import java.sql.ResultSet; import java.sql.SQLException; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class OracleJDBCDriverTest { @@ -28,7 +28,7 @@ private void performSimpleTest(String jdbcUrl) throws SQLException { public Object handle(ResultSet rs) throws SQLException { rs.next(); int resultSetInt = rs.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); return true; } } diff --git a/modules/orientdb/src/test/java/org/testcontainers/containers/OrientDBContainerTest.java b/modules/orientdb/src/test/java/org/testcontainers/containers/OrientDBContainerTest.java index 675218637ce..6cf0e2e8ea9 100644 --- a/modules/orientdb/src/test/java/org/testcontainers/containers/OrientDBContainerTest.java +++ b/modules/orientdb/src/test/java/org/testcontainers/containers/OrientDBContainerTest.java @@ -4,7 +4,7 @@ import org.junit.Test; import org.testcontainers.utility.DockerImageName; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; /** * @author robfrank diff --git a/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java index 3be7685ea27..275517250b6 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java @@ -3,10 +3,8 @@ import org.junit.Test; import org.testcontainers.PostgreSQLTestImages; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; public class PostgreSQLConnectionURLTest { @@ -16,12 +14,11 @@ public void shouldCorrectlyAppendQueryString() { String connectionUrl = postgres.constructUrlForConnection("?stringtype=unspecified&stringtype=unspecified"); String queryString = connectionUrl.substring(connectionUrl.indexOf('?')); - assertTrue( - "Query String contains expected params", - queryString.contains("?stringtype=unspecified&stringtype=unspecified") - ); - assertEquals("Query String starts with '?'", 0, queryString.indexOf('?')); - assertFalse("Query String does not contain extra '?'", queryString.substring(1).contains("?")); + assertThat(queryString) + .as("Query String contains expected params") + .contains("?stringtype=unspecified&stringtype=unspecified"); + assertThat(queryString.indexOf('?')).as("Query String starts with '?'").isZero(); + assertThat(queryString.substring(1)).as("Query String does not contain extra '?'").doesNotContain("?"); } @Test @@ -30,12 +27,11 @@ public void shouldCorrectlyAppendQueryStringWhenNoBaseParams() { String connectionUrl = postgres.constructUrlForConnection("?stringtype=unspecified&stringtype=unspecified"); String queryString = connectionUrl.substring(connectionUrl.indexOf('?')); - assertTrue( - "Query String contains expected params", - queryString.contains("?stringtype=unspecified&stringtype=unspecified") - ); - assertEquals("Query String starts with '?'", 0, queryString.indexOf('?')); - assertFalse("Query String does not contain extra '?'", queryString.substring(1).contains("?")); + assertThat(queryString) + .as("Query String contains expected params") + .contains("?stringtype=unspecified&stringtype=unspecified"); + assertThat(queryString.indexOf('?')).as("Query String starts with '?'").isZero(); + assertThat(queryString.substring(1)).as("Query String does not contain extra '?'").doesNotContain("?"); } @Test @@ -43,16 +39,18 @@ public void shouldReturnOriginalURLWhenEmptyQueryString() { PostgreSQLContainer postgres = new FixedJdbcUrlPostgreSQLContainer(); String connectionUrl = postgres.constructUrlForConnection(""); - assertTrue("Query String remains unchanged", postgres.getJdbcUrl().equals(connectionUrl)); + assertThat(postgres.getJdbcUrl()).as("Query String remains unchanged").isEqualTo(connectionUrl); } @Test public void shouldRejectInvalidQueryString() { - assertThrows( - "Fails when invalid query string provided", - IllegalArgumentException.class, - () -> new NoParamsUrlPostgreSQLContainer().constructUrlForConnection("stringtype=unspecified") - ); + assertThat( + catchThrowable(() -> { + new NoParamsUrlPostgreSQLContainer().constructUrlForConnection("stringtype=unspecified"); + }) + ) + .as("Fails when invalid query string provided") + .isInstanceOf(IllegalArgumentException.class); } static class FixedJdbcUrlPostgreSQLContainer extends PostgreSQLContainer { diff --git a/modules/postgresql/src/test/java/org/testcontainers/containers/TimescaleDBContainerTest.java b/modules/postgresql/src/test/java/org/testcontainers/containers/TimescaleDBContainerTest.java index 74d60b2d32d..f49c7dc6aa7 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/containers/TimescaleDBContainerTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/containers/TimescaleDBContainerTest.java @@ -6,8 +6,7 @@ import java.sql.ResultSet; import java.sql.SQLException; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; +import static org.assertj.core.api.Assertions.assertThat; public class TimescaleDBContainerTest extends AbstractContainerDatabaseTest { @@ -18,7 +17,7 @@ public void testSimple() throws SQLException { ResultSet resultSet = performQuery(postgres, "SELECT 1"); int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); } } @@ -36,7 +35,7 @@ public void testCommandOverride() throws SQLException { "SELECT current_setting('max_connections')" ); String result = resultSet.getString(1); - assertEquals("max_connections should be overriden", "42", result); + assertThat(result).as("max_connections should be overriden").isEqualTo("42"); } } @@ -55,7 +54,7 @@ public void testUnsetCommand() throws SQLException { "SELECT current_setting('max_connections')" ); String result = resultSet.getString(1); - assertNotEquals("max_connections should not be overriden", "42", result); + assertThat(result).as("max_connections should not be overriden").isNotEqualTo("42"); } } @@ -71,7 +70,7 @@ public void testExplicitInitScript() throws SQLException { ResultSet resultSet = performQuery(postgres, "SELECT foo FROM bar"); String firstColumnValue = resultSet.getString(1); - assertEquals("Value from init script should equal real value", "hello world", firstColumnValue); + assertThat(firstColumnValue).as("Value from init script should equal real value").isEqualTo("hello world"); } } } diff --git a/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java b/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java index 5acbd875c23..ed519ed2e4b 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java @@ -8,9 +8,7 @@ import java.sql.DriverManager; import java.sql.SQLException; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNull; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * This test belongs in the jdbc module, as it is focused on testing the behaviour of {@link org.testcontainers.containers.JdbcDatabaseContainer}. @@ -32,7 +30,7 @@ public void shouldStopContainerWhenAllConnectionsClosed() throws SQLException { getConnectionAndClose(jdbcUrl); JdbcDatabaseContainer container = ContainerDatabaseDriver.getContainer(jdbcUrl); - assertNull("Database container instance is null as expected", container); + assertThat(container).as("Database container instance is null as expected").isNull(); } @Test @@ -42,13 +40,13 @@ public void shouldNotStopDaemonContainerWhenAllConnectionsClosed() throws SQLExc getConnectionAndClose(jdbcUrl); JdbcDatabaseContainer container = ContainerDatabaseDriver.getContainer(jdbcUrl); - assertNotNull("Database container instance is not null as expected", container); - assertTrue("Database container is running as expected", container.isRunning()); + assertThat(container).as("Database container instance is not null as expected").isNotNull(); + assertThat(container.isRunning()).as("Database container is running as expected").isTrue(); } private void getConnectionAndClose(String jdbcUrl) throws SQLException { try (Connection connection = DriverManager.getConnection(jdbcUrl)) { - assertNotNull("Obtained connection as expected", connection); + assertThat(connection).as("Obtained connection as expected").isNotNull(); } } } diff --git a/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java b/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java index 4c8d6afeef0..463981d2d59 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java @@ -7,8 +7,7 @@ import java.sql.Connection; import java.sql.DriverManager; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; +import static org.assertj.core.api.Assertions.assertThat; /** * This test belongs in the jdbc module, as it is focused on testing the behaviour of {@link org.testcontainers.containers.JdbcDatabaseContainer}. @@ -26,11 +25,13 @@ public void testDatabaseHasTmpFsViaConnectionString() throws Exception { // check file doesn't exist String path = "/testtmpfs/test.file"; Container.ExecResult execResult = container.execInContainer("ls", path); - assertNotEquals("tmpfs inside container doesn't have file that doesn't exist", 0, execResult.getExitCode()); + assertThat(execResult.getExitCode()) + .as("tmpfs inside container doesn't have file that doesn't exist") + .isNotZero(); // touch && check file does exist container.execInContainer("touch", path); execResult = container.execInContainer("ls", path); - assertEquals("tmpfs inside container has file that does exist", 0, execResult.getExitCode()); + assertThat(execResult.getExitCode()).as("tmpfs inside container has file that does exist").isZero(); } } } diff --git a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/CustomizablePostgreSQLTest.java b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/CustomizablePostgreSQLTest.java index 1b256c0fd6b..6dea67c9e26 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/CustomizablePostgreSQLTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/CustomizablePostgreSQLTest.java @@ -8,7 +8,7 @@ import java.sql.ResultSet; import java.sql.SQLException; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; /** * @author richardnorth @@ -34,7 +34,7 @@ public void testSimple() throws SQLException { ResultSet resultSet = performQuery(postgres, "SELECT 1"); int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); } } } diff --git a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java index 2a0dd351ad6..2a7db242421 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java @@ -10,10 +10,7 @@ import java.util.logging.Level; import java.util.logging.LogManager; -import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertThat; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; +import static org.assertj.core.api.Assertions.assertThat; public class SimplePostgreSQLTest extends AbstractContainerDatabaseTest { static { @@ -28,7 +25,7 @@ public void testSimple() throws SQLException { ResultSet resultSet = performQuery(postgres, "SELECT 1"); int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); } } @@ -42,7 +39,7 @@ public void testCommandOverride() throws SQLException { ResultSet resultSet = performQuery(postgres, "SELECT current_setting('max_connections')"); String result = resultSet.getString(1); - assertEquals("max_connections should be overriden", "42", result); + assertThat(result).as("max_connections should be overriden").isEqualTo("42"); } } @@ -57,7 +54,7 @@ public void testUnsetCommand() throws SQLException { ResultSet resultSet = performQuery(postgres, "SELECT current_setting('max_connections')"); String result = resultSet.getString(1); - assertNotEquals("max_connections should not be overriden", "42", result); + assertThat(result).as("max_connections should not be overriden").isNotEqualTo("42"); } } @@ -72,7 +69,7 @@ public void testExplicitInitScript() throws SQLException { ResultSet resultSet = performQuery(postgres, "SELECT foo FROM bar"); String firstColumnValue = resultSet.getString(1); - assertEquals("Value from init script should equal real value", "hello world", firstColumnValue); + assertThat(firstColumnValue).as("Value from init script should equal real value").isEqualTo("hello world"); } } @@ -84,9 +81,9 @@ public void testWithAdditionalUrlParamInJdbcUrl() { ) { postgres.start(); String jdbcUrl = postgres.getJdbcUrl(); - assertThat(jdbcUrl, containsString("?")); - assertThat(jdbcUrl, containsString("&")); - assertThat(jdbcUrl, containsString("charSet=UNICODE")); + assertThat(jdbcUrl).contains("?"); + assertThat(jdbcUrl).contains("&"); + assertThat(jdbcUrl).contains("charSet=UNICODE"); } } } diff --git a/modules/r2dbc/src/test/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactoryTest.java b/modules/r2dbc/src/test/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactoryTest.java index 3772e6d49a6..9ae49b00bd1 100644 --- a/modules/r2dbc/src/test/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactoryTest.java +++ b/modules/r2dbc/src/test/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactoryTest.java @@ -6,7 +6,6 @@ import io.r2dbc.spi.ConnectionFactories; import io.r2dbc.spi.ConnectionFactory; import io.r2dbc.spi.Result; -import org.assertj.core.api.Assertions; import org.junit.Test; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -14,14 +13,14 @@ import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; public class TestcontainersR2DBCConnectionFactoryTest { @Test public void failsOnUnknownProvider() { String nonExistingProvider = UUID.randomUUID().toString(); - Assertions - .assertThatThrownBy(() -> { + assertThatThrownBy(() -> { ConnectionFactories.get(String.format("r2dbc:tc:%s:///db", nonExistingProvider)); }) .hasMessageContaining("Missing provider") @@ -64,8 +63,7 @@ public void reusesUntilConnectionFactoryIsClosed() { close(connectionFactory); - Assertions - .assertThatThrownBy(select::blockFirst) + assertThatThrownBy(select::blockFirst) .isInstanceOf(PostgresqlException.class) // relation "X" does not exists // https://github.com/postgres/postgres/blob/REL_10_0/src/backend/utils/errcodes.txt#L349 diff --git a/modules/selenium/build.gradle b/modules/selenium/build.gradle index 5f0824f8b12..38fab63dc0a 100644 --- a/modules/selenium/build.gradle +++ b/modules/selenium/build.gradle @@ -10,7 +10,7 @@ dependencies { testImplementation 'org.mortbay.jetty:jetty:6.1.26' testImplementation project(':nginx') - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.assertj:assertj-core:3.23.1' compileOnly 'org.jetbrains:annotations:23.0.0' testCompileClasspath 'org.jetbrains:annotations:23.0.0' diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java index d0865a06b09..c49e24ba867 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java @@ -13,8 +13,7 @@ import java.util.concurrent.TimeUnit; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * @@ -41,13 +40,15 @@ protected static void doSimpleExplore(BrowserWebDriverContainer rule) { driver.get("http://helloworld:8080"); WebElement title = driver.findElement(By.tagName("h1")); - assertEquals("the index page contains the title 'Hello world'", "Hello world", title.getText().trim()); + assertThat(title.getText().trim()) + .as("the index page contains the title 'Hello world'") + .isEqualTo("Hello world"); } protected void assertBrowserNameIs(BrowserWebDriverContainer rule, String expectedName) { RemoteWebDriver driver = setupDriverFromRule(rule); String actual = driver.getCapabilities().getBrowserName(); - assertTrue(String.format("actual browser name is %s", actual), actual.equals(expectedName)); + assertThat(actual).as(String.format("actual browser name is %s", actual)).isEqualTo(expectedName); } @NotNull diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java index 60d113dc290..370d9cffedb 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java @@ -13,7 +13,7 @@ import java.util.List; import java.util.stream.Collectors; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class BrowserWebDriverContainerTest { @@ -31,7 +31,7 @@ public void honorPresetNoProxyEnvironment() { chromeWithNoProxySet.start(); Object noProxy = chromeWithNoProxySet.getEnvMap().get(NO_PROXY_KEY); - assertEquals("no_proxy should be preserved by the container rule", NO_PROXY_VALUE, noProxy); + assertThat(noProxy).as("no_proxy should be preserved by the container rule").isEqualTo(NO_PROXY_VALUE); } } @@ -44,7 +44,7 @@ public void provideDefaultNoProxyEnvironmentIfNotSet() { chromeWithoutNoProxySet.start(); Object noProxy = chromeWithoutNoProxySet.getEnvMap().get(NO_PROXY_KEY); - assertEquals("no_proxy should be set to default if not already present", "localhost", noProxy); + assertThat(noProxy).as("no_proxy should be set to default if not already present").isEqualTo("localhost"); } } @@ -59,9 +59,9 @@ public void createContainerWithShmVolume() { final List shmVolumes = shmVolumes(webDriverContainer); - assertEquals("Only one shm mount present", 1, shmVolumes.size()); - assertEquals("Shm mount source is correct", "/dev/shm", shmVolumes.get(0).getSource()); - assertEquals("Shm mount mode is correct", "rw", shmVolumes.get(0).getMode()); + assertThat(shmVolumes).as("Only one shm mount present").hasSize(1); + assertThat(shmVolumes.get(0).getSource()).as("Shm mount source is correct").isEqualTo("/dev/shm"); + assertThat(shmVolumes.get(0).getMode()).as("Shm mount mode is correct").isEqualTo("rw"); } } @@ -74,9 +74,11 @@ public void createContainerWithoutShmVolume() { ) { webDriverContainer.start(); - assertEquals("Shared memory size is configured", 512 * FileUtils.ONE_MB, webDriverContainer.getShmSize()); + assertThat(webDriverContainer.getShmSize()) + .as("Shared memory size is configured") + .isEqualTo(512 * FileUtils.ONE_MB); - assertEquals("No shm mounts present", Collections.emptyList(), shmVolumes(webDriverContainer)); + assertThat(shmVolumes(webDriverContainer)).as("No shm mounts present").isEqualTo(Collections.emptyList()); } } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java index 2d2feb6aaff..88b40f4fbbe 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java @@ -10,7 +10,7 @@ import org.testcontainers.Testcontainers; import org.testcontainers.containers.BrowserWebDriverContainer; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; /** * Test that a browser running in a container can access a web server hosted on the host machine (i.e. the one running @@ -53,10 +53,8 @@ public void testConnection() { String headingText = driver.findElement(By.cssSelector("h1")).getText().trim(); - assertEquals( - "The hardcoded success message was found on a page fetched from a local server", - "It worked", - headingText - ); + assertThat(headingText) + .as("The hardcoded success message was found on a page fetched from a local server") + .isEqualTo("It worked"); } } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/SeleniumUtilsTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/SeleniumUtilsTest.java index 4cbfca77d21..6208cb3a5bb 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/SeleniumUtilsTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/SeleniumUtilsTest.java @@ -6,7 +6,7 @@ import java.io.IOException; import java.util.jar.Manifest; -import static org.rnorth.visibleassertions.VisibleAssertions.*; +import static org.assertj.core.api.Assertions.assertThat; /** * Created by Julien LAMY @@ -32,6 +32,8 @@ private void checkSeleniumVersionDetected(String urlManifest, String expectedVer Manifest manifest = new Manifest(); manifest.read(this.getClass().getClassLoader().getResourceAsStream(urlManifest)); String seleniumVersion = SeleniumUtils.getSeleniumVersionFromManifest(manifest); - assertEquals("Check if Selenium Version detected is the correct one.", expectedVersion, seleniumVersion); + assertThat(seleniumVersion) + .as("Check if Selenium Version detected is the correct one.") + .isEqualTo(expectedVersion); } } diff --git a/modules/toxiproxy/build.gradle b/modules/toxiproxy/build.gradle index a1e5f58551a..afe76308ba6 100644 --- a/modules/toxiproxy/build.gradle +++ b/modules/toxiproxy/build.gradle @@ -5,7 +5,7 @@ dependencies { api 'eu.rekawek.toxiproxy:toxiproxy-java:2.1.7' testImplementation 'redis.clients:jedis:3.0.1' - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation project(':test-support') } diff --git a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java index c5982bc45c1..bfe488eaac1 100644 --- a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java +++ b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java @@ -10,9 +10,8 @@ import java.io.IOException; import java.time.Duration; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; public class ToxiproxyTest { @@ -53,7 +52,7 @@ public void testDirect() { jedis.set("somekey", "somevalue"); final String s = jedis.get("somekey"); - assertEquals("direct access to the container works OK", "somevalue", s); + assertThat(s).as("direct access to the container works OK").isEqualTo("somevalue"); } @Test @@ -90,32 +89,28 @@ public void testConnectionCut() { final Jedis jedis = createJedis(proxy.getContainerIpAddress(), proxy.getProxyPort()); jedis.set("somekey", "somevalue"); - assertEquals( - "access to the container works OK before cutting the connection", - "somevalue", - jedis.get("somekey") - ); + assertThat(jedis.get("somekey")) + .as("access to the container works OK before cutting the connection") + .isEqualTo("somevalue"); // disableProxy { proxy.setConnectionCut(true); // for example, expect failure when the connection is cut - assertThrows( - "calls fail when the connection is cut", - JedisConnectionException.class, - () -> { + assertThat( + catchThrowable(() -> { jedis.get("somekey"); - } - ); + }) + ) + .as("calls fail when the connection is cut") + .isInstanceOf(JedisConnectionException.class); proxy.setConnectionCut(false); // and with the connection re-established, expect success - assertEquals( - "access to the container works OK after re-establishing the connection", - "somevalue", - jedis.get("somekey") - ); + assertThat(jedis.get("somekey")) + .as("access to the container works OK after re-establishing the connection") + .isEqualTo("somevalue"); // } } @@ -139,15 +134,15 @@ public void testMultipleProxiesCanBeCreated() { firstProxy.setConnectionCut(true); - assertThrows( - "calls fail when the connection is cut, for only the relevant proxy", - JedisConnectionException.class, - () -> { + assertThat( + catchThrowable(() -> { firstJedis.get("somekey"); - } - ); + }) + ) + .as("calls fail when the connection is cut, for only the relevant proxy") + .isInstanceOf(JedisConnectionException.class); - assertEquals("access via a different proxy is OK", "somevalue", secondJedis.get("somekey")); + assertThat(secondJedis.get("somekey")).as("access via a different proxy is OK").isEqualTo("somevalue"); } } @@ -158,38 +153,34 @@ public void testOriginalAndMappedPorts() { final String hostViaToxiproxy = TOXIPROXY_NETWORK_ALIAS; final int portViaToxiproxy = proxy.getOriginalProxyPort(); // } - assertEquals("host is correct", TOXIPROXY_NETWORK_ALIAS, hostViaToxiproxy); - assertEquals("original port is correct", 8666, portViaToxiproxy); + assertThat(hostViaToxiproxy).as("host is correct").isEqualTo(TOXIPROXY_NETWORK_ALIAS); + assertThat(portViaToxiproxy).as("original port is correct").isEqualTo(8666); final ToxiproxyContainer.ContainerProxy proxy1 = toxiproxy.getProxy("hostname1", 8080); - assertEquals("original port is correct", 8667, proxy1.getOriginalProxyPort()); - assertEquals( - "mapped port is correct", - toxiproxy.getMappedPort(proxy1.getOriginalProxyPort()), - proxy1.getProxyPort() - ); + assertThat(proxy1.getOriginalProxyPort()).as("original port is correct").isEqualTo(8667); + assertThat(proxy1.getProxyPort()) + .as("mapped port is correct") + .isEqualTo(toxiproxy.getMappedPort(proxy1.getOriginalProxyPort())); final ToxiproxyContainer.ContainerProxy proxy2 = toxiproxy.getProxy("hostname2", 9090); - assertEquals("original port is correct", 8668, proxy2.getOriginalProxyPort()); - assertEquals( - "mapped port is correct", - toxiproxy.getMappedPort(proxy2.getOriginalProxyPort()), - proxy2.getProxyPort() - ); + assertThat(proxy2.getOriginalProxyPort()).as("original port is correct").isEqualTo(8668); + assertThat(proxy2.getProxyPort()) + .as("mapped port is correct") + .isEqualTo(toxiproxy.getMappedPort(proxy2.getOriginalProxyPort())); } @Test public void testProxyName() { final ToxiproxyContainer.ContainerProxy proxy = toxiproxy.getProxy("hostname", 7070); - assertEquals("proxy name is hostname and port", "hostname:7070", proxy.getName()); + assertThat(proxy.getName()).as("proxy name is hostname and port").isEqualTo("hostname:7070"); } @Test public void testControlPort() { final int controlPort = toxiproxy.getControlPort(); - assertEquals("control port is mapped from port 8474", toxiproxy.getMappedPort(8474), controlPort); + assertThat(controlPort).as("control port is mapped from port 8474").isEqualTo(toxiproxy.getMappedPort(8474)); } private void checkCallWithLatency( @@ -203,15 +194,13 @@ private void checkCallWithLatency( final long end = System.currentTimeMillis(); final long duration = end - start; - assertEquals(String.format("access to the container %s works OK", description), "somevalue", s); - assertTrue( - String.format("%s there is at least %dms latency", description, expectedMinLatency), - duration >= expectedMinLatency - ); - assertTrue( - String.format("%s there is no more than %dms latency", description, expectedMaxLatency), - duration < expectedMaxLatency - ); + assertThat(s).as(String.format("access to the container %s works OK", description)).isEqualTo("somevalue"); + assertThat(duration >= expectedMinLatency) + .as(String.format("%s there is at least %dms latency", description, expectedMinLatency)) + .isTrue(); + assertThat(duration < expectedMaxLatency) + .as(String.format("%s there is no more than %dms latency", description, expectedMaxLatency)) + .isTrue(); } private static Jedis createJedis(String host, int port) { From 7c70a8af559935fe6474848cd30241a2731d484f Mon Sep 17 00:00:00 2001 From: Filipe Roque Date: Tue, 9 Aug 2022 09:14:55 +0100 Subject: [PATCH 2749/5615] Removes unused module junit-toolbox (#5678) Co-authored-by: Kevin Wittek --- modules/jdbc/build.gradle | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 6047a3c0f3e..43ff0968715 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -9,6 +9,8 @@ dependencies { testImplementation 'org.vibur:vibur-dbcp:25.0' testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.23' testImplementation 'com.zaxxer:HikariCP-java6:2.3.13' - testImplementation 'com.googlecode.junit-toolbox:junit-toolbox:2.4' testImplementation 'org.assertj:assertj-core:3.23.1' + testImplementation ('org.mockito:mockito-core:4.6.1') { + exclude(module: 'hamcrest-core') + } } From 54d7f3931d8856e7a53b8d1b2646aff0faa3dbf2 Mon Sep 17 00:00:00 2001 From: Cheese Date: Mon, 15 Aug 2022 22:01:46 +0800 Subject: [PATCH 2750/5615] Add TiDB module (#5511) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sergei Egorov Co-authored-by: Kevin Wittek Co-authored-by: Eddú Meléndez Gonzales --- .github/ISSUE_TEMPLATE/bug_report.yaml | 1 + .github/ISSUE_TEMPLATE/enhancement.yaml | 1 + .github/ISSUE_TEMPLATE/feature.yaml | 1 + .github/dependabot.yml | 5 + .github/labeler.yml | 2 + docs/modules/databases/jdbc.md | 4 + docs/modules/databases/tidb.md | 25 ++++ mkdocs.yml | 1 + modules/tidb/build.gradle | 10 ++ modules/tidb/sql/init_mysql.sql | 5 + .../testcontainers/tidb/TiDBContainer.java | 126 ++++++++++++++++++ .../tidb/TiDBContainerProvider.java | 32 +++++ ...s.containers.JdbcDatabaseContainerProvider | 1 + .../org/testcontainers/TiDBTestImages.java | 8 ++ .../jdbc/tidb/TiDBJDBCDriverTest.java | 19 +++ .../junit/tidb/SimpleTiDBTest.java | 54 ++++++++ .../tidb/src/test/resources/logback-test.xml | 16 +++ .../src/test/resources/somepath/init_tidb.sql | 16 +++ 18 files changed, 327 insertions(+) create mode 100644 docs/modules/databases/tidb.md create mode 100644 modules/tidb/build.gradle create mode 100644 modules/tidb/sql/init_mysql.sql create mode 100644 modules/tidb/src/main/java/org/testcontainers/tidb/TiDBContainer.java create mode 100644 modules/tidb/src/main/java/org/testcontainers/tidb/TiDBContainerProvider.java create mode 100644 modules/tidb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider create mode 100644 modules/tidb/src/test/java/org/testcontainers/TiDBTestImages.java create mode 100644 modules/tidb/src/test/java/org/testcontainers/jdbc/tidb/TiDBJDBCDriverTest.java create mode 100644 modules/tidb/src/test/java/org/testcontainers/junit/tidb/SimpleTiDBTest.java create mode 100644 modules/tidb/src/test/resources/logback-test.xml create mode 100644 modules/tidb/src/test/resources/somepath/init_tidb.sql diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 686086d076c..07d944243cd 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -43,6 +43,7 @@ body: - RabbitMQ - Selenium - Solr + - TiDB - ToxiProxy - Trino - Vault diff --git a/.github/ISSUE_TEMPLATE/enhancement.yaml b/.github/ISSUE_TEMPLATE/enhancement.yaml index 9ef8eafaab2..ea190cefc80 100644 --- a/.github/ISSUE_TEMPLATE/enhancement.yaml +++ b/.github/ISSUE_TEMPLATE/enhancement.yaml @@ -43,6 +43,7 @@ body: - RabbitMQ - Selenium - Solr + - TiDB - ToxiProxy - Trino - Vault diff --git a/.github/ISSUE_TEMPLATE/feature.yaml b/.github/ISSUE_TEMPLATE/feature.yaml index 1ab2693f210..09810d3e56f 100644 --- a/.github/ISSUE_TEMPLATE/feature.yaml +++ b/.github/ISSUE_TEMPLATE/feature.yaml @@ -43,6 +43,7 @@ body: - RabbitMQ - Selenium - Solr + - TiDB - ToxiProxy - Trino - Vault diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 3e2d53fed65..2d877f664ab 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -197,6 +197,11 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/tidb" + schedule: + interval: "monthly" + open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/toxiproxy" schedule: diff --git a/.github/labeler.yml b/.github/labeler.yml index 84e21af744a..826e5c16215 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -73,6 +73,8 @@ - modules/solr/* "modules/spock": - modules/spock/* +"modules/tidb": + - modules/tidb/* "modules/toxiproxy": - modules/toxiproxy/* "modules/trino": diff --git a/docs/modules/databases/jdbc.md b/docs/modules/databases/jdbc.md index 3c52dd1d8a8..e3a077333cd 100644 --- a/docs/modules/databases/jdbc.md +++ b/docs/modules/databases/jdbc.md @@ -51,6 +51,10 @@ Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database `jdbc:tc:cockroach:v21.2.3:///databasename` +#### Using TiDB + +`jdbc:tc:tidb:v6.1.0:///databasename` + ### Using a classpath init script Testcontainers can run an init script after the database container is started, but before your code is given a connection to it. The script must be on the classpath, and is referenced as follows: diff --git a/docs/modules/databases/tidb.md b/docs/modules/databases/tidb.md new file mode 100644 index 00000000000..01c6b4cfcd4 --- /dev/null +++ b/docs/modules/databases/tidb.md @@ -0,0 +1,25 @@ +# TiDB Module + +See [Database containers](./index.md) for documentation and usage that is common to all relational database container types. + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:tidb:{{latest_version}}" + ``` + +=== "Maven" + ```xml + + org.testcontainers + tidb + {{latest_version}} + test + + ``` + +!!! hint + Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency. diff --git a/mkdocs.yml b/mkdocs.yml index 396986a955b..9491ebe6019 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -63,6 +63,7 @@ nav: - modules/databases/orientdb.md - modules/databases/postgres.md - modules/databases/presto.md + - modules/databases/tidb.md - modules/databases/trino.md - modules/azure.md - modules/docker_compose.md diff --git a/modules/tidb/build.gradle b/modules/tidb/build.gradle new file mode 100644 index 00000000000..51f15a6a798 --- /dev/null +++ b/modules/tidb/build.gradle @@ -0,0 +1,10 @@ +description = "Testcontainers :: JDBC :: TiDB" + +dependencies { + api project(':jdbc') + + testImplementation project(':jdbc-test') + testImplementation 'mysql:mysql-connector-java:8.0.29' + + compileOnly 'org.jetbrains:annotations:23.0.0' +} diff --git a/modules/tidb/sql/init_mysql.sql b/modules/tidb/sql/init_mysql.sql new file mode 100644 index 00000000000..2b00ee968b0 --- /dev/null +++ b/modules/tidb/sql/init_mysql.sql @@ -0,0 +1,5 @@ +CREATE TABLE bar ( + foo VARCHAR(255) +); + +INSERT INTO bar (foo) VALUES ('hello world'); \ No newline at end of file diff --git a/modules/tidb/src/main/java/org/testcontainers/tidb/TiDBContainer.java b/modules/tidb/src/main/java/org/testcontainers/tidb/TiDBContainer.java new file mode 100644 index 00000000000..f8030fd2422 --- /dev/null +++ b/modules/tidb/src/main/java/org/testcontainers/tidb/TiDBContainer.java @@ -0,0 +1,126 @@ +package org.testcontainers.tidb; + +import org.jetbrains.annotations.NotNull; +import org.testcontainers.containers.JdbcDatabaseContainer; +import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; +import org.testcontainers.utility.DockerImageName; + +import java.time.Duration; +import java.util.HashSet; +import java.util.Set; + +/** + * Testcontainers implementation for TiDB. + * + * @author Icemap + */ +public class TiDBContainer extends JdbcDatabaseContainer { + + static final String NAME = "tidb"; + + static final String DOCKER_IMAGE_NAME = "pingcap/tidb"; + + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse(DOCKER_IMAGE_NAME); + + private static final Integer TIDB_PORT = 4000; + + private static final int REST_API_PORT = 10080; + + private String databaseName = "test"; + + private String username = "root"; + + private String password = ""; + + public TiDBContainer(String dockerImageName) { + this(DockerImageName.parse(dockerImageName)); + } + + public TiDBContainer(final DockerImageName dockerImageName) { + super(dockerImageName); + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + + addExposedPorts(TIDB_PORT, REST_API_PORT); + + waitingFor( + new HttpWaitStrategy() + .forPath("/status") + .forPort(REST_API_PORT) + .forStatusCode(200) + .withStartupTimeout(Duration.ofMinutes(1)) + ); + } + + @NotNull + @Override + protected Set getLivenessCheckPorts() { + return new HashSet<>(getMappedPort(TIDB_PORT)); + } + + @Override + public String getDriverClassName() { + try { + Class.forName("com.mysql.cj.jdbc.Driver"); + return "com.mysql.cj.jdbc.Driver"; + } catch (ClassNotFoundException e) { + return "com.mysql.jdbc.Driver"; + } + } + + @Override + public String getJdbcUrl() { + String additionalUrlParams = constructUrlParameters("?", "&"); + return "jdbc:mysql://" + getHost() + ":" + getMappedPort(TIDB_PORT) + "/" + databaseName + additionalUrlParams; + } + + @Override + protected String constructUrlForConnection(String queryString) { + String url = super.constructUrlForConnection(queryString); + + if (!url.contains("useSSL=")) { + String separator = url.contains("?") ? "&" : "?"; + url = url + separator + "useSSL=false"; + } + + if (!url.contains("allowPublicKeyRetrieval=")) { + url = url + "&allowPublicKeyRetrieval=true"; + } + + return url; + } + + @Override + public String getDatabaseName() { + return databaseName; + } + + @Override + public String getUsername() { + return username; + } + + @Override + public String getPassword() { + return password; + } + + @Override + public String getTestQueryString() { + return "SELECT 1"; + } + + @Override + public TiDBContainer withDatabaseName(final String databaseName) { + throw new UnsupportedOperationException("The TiDB docker image does not currently support this"); + } + + @Override + public TiDBContainer withUsername(final String username) { + throw new UnsupportedOperationException("The TiDB docker image does not currently support this"); + } + + @Override + public TiDBContainer withPassword(final String password) { + throw new UnsupportedOperationException("The TiDB docker image does not currently support this"); + } +} diff --git a/modules/tidb/src/main/java/org/testcontainers/tidb/TiDBContainerProvider.java b/modules/tidb/src/main/java/org/testcontainers/tidb/TiDBContainerProvider.java new file mode 100644 index 00000000000..ee66771b42c --- /dev/null +++ b/modules/tidb/src/main/java/org/testcontainers/tidb/TiDBContainerProvider.java @@ -0,0 +1,32 @@ +package org.testcontainers.tidb; + +import org.testcontainers.containers.JdbcDatabaseContainer; +import org.testcontainers.containers.JdbcDatabaseContainerProvider; +import org.testcontainers.utility.DockerImageName; + +/** + * Factory for TiDB containers. + */ +public class TiDBContainerProvider extends JdbcDatabaseContainerProvider { + + private static final String DEFAULT_TAG = "v6.1.0"; + + @Override + public boolean supports(String databaseType) { + return databaseType.equals(TiDBContainer.NAME); + } + + @Override + public JdbcDatabaseContainer newInstance() { + return newInstance(DEFAULT_TAG); + } + + @Override + public JdbcDatabaseContainer newInstance(String tag) { + if (tag != null) { + return new TiDBContainer(DockerImageName.parse(TiDBContainer.DOCKER_IMAGE_NAME).withTag(tag)); + } else { + return newInstance(); + } + } +} diff --git a/modules/tidb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider b/modules/tidb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider new file mode 100644 index 00000000000..a4168b00e74 --- /dev/null +++ b/modules/tidb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider @@ -0,0 +1 @@ +org.testcontainers.tidb.TiDBContainerProvider diff --git a/modules/tidb/src/test/java/org/testcontainers/TiDBTestImages.java b/modules/tidb/src/test/java/org/testcontainers/TiDBTestImages.java new file mode 100644 index 00000000000..32fb57911a2 --- /dev/null +++ b/modules/tidb/src/test/java/org/testcontainers/TiDBTestImages.java @@ -0,0 +1,8 @@ +package org.testcontainers; + +import org.testcontainers.utility.DockerImageName; + +public class TiDBTestImages { + + public static final DockerImageName TIDB_IMAGE = DockerImageName.parse("pingcap/tidb:v6.1.0"); +} diff --git a/modules/tidb/src/test/java/org/testcontainers/jdbc/tidb/TiDBJDBCDriverTest.java b/modules/tidb/src/test/java/org/testcontainers/jdbc/tidb/TiDBJDBCDriverTest.java new file mode 100644 index 00000000000..566850fa629 --- /dev/null +++ b/modules/tidb/src/test/java/org/testcontainers/jdbc/tidb/TiDBJDBCDriverTest.java @@ -0,0 +1,19 @@ +package org.testcontainers.jdbc.tidb; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.testcontainers.jdbc.AbstractJDBCDriverTest; + +import java.util.Arrays; +import java.util.EnumSet; + +@RunWith(Parameterized.class) +public class TiDBJDBCDriverTest extends AbstractJDBCDriverTest { + + @Parameterized.Parameters(name = "{index} - {0}") + public static Iterable data() { + return Arrays.asList( + new Object[][] { { "jdbc:tc:tidb://hostname/databasename", EnumSet.noneOf(Options.class) } } + ); + } +} diff --git a/modules/tidb/src/test/java/org/testcontainers/junit/tidb/SimpleTiDBTest.java b/modules/tidb/src/test/java/org/testcontainers/junit/tidb/SimpleTiDBTest.java new file mode 100644 index 00000000000..c4833f14224 --- /dev/null +++ b/modules/tidb/src/test/java/org/testcontainers/junit/tidb/SimpleTiDBTest.java @@ -0,0 +1,54 @@ +package org.testcontainers.junit.tidb; + +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Test; +import org.testcontainers.TiDBTestImages; +import org.testcontainers.db.AbstractContainerDatabaseTest; +import org.testcontainers.tidb.TiDBContainer; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class SimpleTiDBTest extends AbstractContainerDatabaseTest { + + @Test + public void testSimple() throws SQLException { + try (TiDBContainer tidb = new TiDBContainer(TiDBTestImages.TIDB_IMAGE)) { + tidb.start(); + + ResultSet resultSet = performQuery(tidb, "SELECT 1"); + + int resultSetInt = resultSet.getInt(1); + Assert.assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + } + } + + @Test + public void testExplicitInitScript() throws SQLException { + try ( + TiDBContainer tidb = new TiDBContainer(TiDBTestImages.TIDB_IMAGE).withInitScript("somepath/init_tidb.sql") + ) { // TiDB is expected to be compatible with MySQL + tidb.start(); + + ResultSet resultSet = performQuery(tidb, "SELECT foo FROM bar"); + + String firstColumnValue = resultSet.getString(1); + Assert.assertEquals("Value from init script should equal real value", "hello world", firstColumnValue); + } + } + + @Test + public void testWithAdditionalUrlParamInJdbcUrl() { + TiDBContainer tidb = new TiDBContainer(TiDBTestImages.TIDB_IMAGE).withUrlParam("sslmode", "disable"); + + try { + tidb.start(); + String jdbcUrl = tidb.getJdbcUrl(); + Assert.assertThat(jdbcUrl, Matchers.containsString("?")); + Assert.assertThat(jdbcUrl, Matchers.containsString("sslmode=disable")); + } finally { + tidb.stop(); + } + } +} diff --git a/modules/tidb/src/test/resources/logback-test.xml b/modules/tidb/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..535e406fc13 --- /dev/null +++ b/modules/tidb/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + diff --git a/modules/tidb/src/test/resources/somepath/init_tidb.sql b/modules/tidb/src/test/resources/somepath/init_tidb.sql new file mode 100644 index 00000000000..73bc09151cb --- /dev/null +++ b/modules/tidb/src/test/resources/somepath/init_tidb.sql @@ -0,0 +1,16 @@ +CREATE TABLE bar ( + foo VARCHAR(255) +); + +SELECT "a /* string literal containing comment characters like -- here"; +SELECT "a 'quoting' \"scenario ` involving BEGIN keyword\" here"; +SELECT * from `bar`; + +-- What about a line comment containing imbalanced string delimiters? " + +/* or a block comment + containing imbalanced string delimiters? + ' " + */ + +INSERT INTO bar (foo) /* ; */ VALUES ('hello world'); From 89803a204af6c3d4ba8de80d50ac5b43bcd12ea3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:17 +0000 Subject: [PATCH 2751/5615] Bump mockito-core from 4.6.1 to 4.7.0 in /core Bumps [mockito-core](https://github.com/mockito/mockito) from 4.6.1 to 4.7.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.6.1...v4.7.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 9febf2802e9..c210ed48510 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -91,7 +91,7 @@ dependencies { testImplementation 'com.rabbitmq:amqp-client:5.15.0' testImplementation 'org.mongodb:mongo-java-driver:3.12.11' - testImplementation ('org.mockito:mockito-core:4.6.1') { + testImplementation ('org.mockito:mockito-core:4.7.0') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From 13cbb5d321216239524731476649acf9c976ea57 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:18 +0000 Subject: [PATCH 2752/5615] Bump java-client from 3.3.2 to 3.3.3 in /modules/couchbase Bumps [java-client](https://github.com/couchbase/couchbase-jvm-clients) from 3.3.2 to 3.3.3. - [Release notes](https://github.com/couchbase/couchbase-jvm-clients/releases) - [Commits](https://github.com/couchbase/couchbase-jvm-clients/compare/java-client-3.3.2...java-client-3.3.3) --- updated-dependencies: - dependency-name: com.couchbase.client:java-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index e59e346c29f..10970df542e 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -5,6 +5,6 @@ dependencies { // TODO use JDK's HTTP client and/or Apache HttpClient5 shaded 'com.squareup.okhttp3:okhttp:3.14.9' - testImplementation 'com.couchbase.client:java-client:3.3.2' + testImplementation 'com.couchbase.client:java-client:3.3.3' testImplementation 'org.awaitility:awaitility:4.2.0' } From 7dcb1d1705fff6c01fb21626954301b2b6c3ab76 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:19 +0000 Subject: [PATCH 2753/5615] Bump com.gradle.enterprise.gradle.plugin from 3.10.3 to 3.11.1 Bumps com.gradle.enterprise.gradle.plugin from 3.10.3 to 3.11.1. --- updated-dependencies: - dependency-name: com.gradle.enterprise:com.gradle.enterprise.gradle.plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index b56048c1126..116a4e26506 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,7 +5,7 @@ buildscript { } } dependencies { - classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10.3" + classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.11.1" classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7.2" } } From 3b2c90e71e72be89648e0b0a2b658ce1a6116638 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:19 +0000 Subject: [PATCH 2754/5615] Bump postgresql from 42.4.0 to 42.4.1 in /modules/cockroachdb Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.0 to 42.4.1. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.0...REL42.4.1) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/cockroachdb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cockroachdb/build.gradle b/modules/cockroachdb/build.gradle index 80763e312b5..1fc5dac8d8a 100644 --- a/modules/cockroachdb/build.gradle +++ b/modules/cockroachdb/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'org.postgresql:postgresql:42.4.0' + testImplementation 'org.postgresql:postgresql:42.4.1' testImplementation 'org.assertj:assertj-core:3.23.1' } From 981d47e0d3e45eb3ab726b08b421cf9cb3b50d91 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:19 +0000 Subject: [PATCH 2755/5615] Bump java-driver-core from 4.8.0 to 4.14.1 in /modules/cassandra Bumps [java-driver-core](https://github.com/datastax/java-driver) from 4.8.0 to 4.14.1. - [Release notes](https://github.com/datastax/java-driver/releases) - [Commits](https://github.com/datastax/java-driver/compare/4.8.0...4.14.1) --- updated-dependencies: - dependency-name: com.datastax.oss:java-driver-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/cassandra/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cassandra/build.gradle b/modules/cassandra/build.gradle index b00740d09fd..385001db8f8 100644 --- a/modules/cassandra/build.gradle +++ b/modules/cassandra/build.gradle @@ -9,5 +9,5 @@ configurations.all { dependencies { api project(":database-commons") api "com.datastax.cassandra:cassandra-driver-core:3.10.0" - testImplementation 'com.datastax.oss:java-driver-core:4.8.0' + testImplementation 'com.datastax.oss:java-driver-core:4.14.1' } From caee839e2b97fe7fee57b71e463a6464ff04faea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:22 +0000 Subject: [PATCH 2756/5615] Bump azure-cosmos from 4.33.1 to 4.34.0 in /modules/azure Bumps [azure-cosmos](https://github.com/Azure/azure-sdk-for-java) from 4.33.1 to 4.34.0. - [Release notes](https://github.com/Azure/azure-sdk-for-java/releases) - [Commits](https://github.com/Azure/azure-sdk-for-java/compare/azure-cosmos_4.33.1...azure-cosmos_4.34.0) --- updated-dependencies: - dependency-name: com.azure:azure-cosmos dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/azure/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle index c6707401ff6..f8a7193571e 100644 --- a/modules/azure/build.gradle +++ b/modules/azure/build.gradle @@ -6,5 +6,5 @@ dependencies { shaded 'com.squareup.okhttp3:okhttp:3.14.9' testImplementation 'org.assertj:assertj-core:3.23.1' - testImplementation 'com.azure:azure-cosmos:4.33.1' + testImplementation 'com.azure:azure-cosmos:4.34.0' } From 88773a7dfda704643972d8ca625f745b5afab4e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:23 +0000 Subject: [PATCH 2757/5615] Bump mariadb-java-client from 3.0.6 to 3.0.7 in /modules/mariadb Bumps [mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 3.0.6 to 3.0.7. - [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases) - [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md) - [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/3.0.6...3.0.7) --- updated-dependencies: - dependency-name: org.mariadb.jdbc:mariadb-java-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index ed7f428ba3e..1a11f0fbfb6 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'org.mariadb:r2dbc-mariadb:1.0.3' testImplementation project(':jdbc-test') - testImplementation 'org.mariadb.jdbc:mariadb-java-client:3.0.6' + testImplementation 'org.mariadb.jdbc:mariadb-java-client:3.0.7' testImplementation testFixtures(project(':r2dbc')) testImplementation 'org.mariadb:r2dbc-mariadb:1.0.2' From e0c1b3652258b8e44f27807fd3a5248417dbe306 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:25 +0000 Subject: [PATCH 2758/5615] Bump postgresql from 42.4.0 to 42.4.1 in /modules/junit-jupiter Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.0 to 42.4.1. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.0...REL42.4.1) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 57932ec84fd..7e14656f20f 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.0' - testRuntimeOnly 'org.postgresql:postgresql:42.4.0' + testRuntimeOnly 'org.postgresql:postgresql:42.4.1' testRuntimeOnly 'mysql:mysql-connector-java:8.0.30' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0' } From aa8a67964f2f2b6aea378081eeb7aa41f3a27c10 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:25 +0000 Subject: [PATCH 2759/5615] Bump mssql-jdbc in /modules/mssqlserver Bumps [mssql-jdbc](https://github.com/Microsoft/mssql-jdbc) from 11.1.2.jre8-preview to 11.2.0.jre8. - [Release notes](https://github.com/Microsoft/mssql-jdbc/releases) - [Changelog](https://github.com/microsoft/mssql-jdbc/blob/main/CHANGELOG.md) - [Commits](https://github.com/Microsoft/mssql-jdbc/commits) --- updated-dependencies: - dependency-name: com.microsoft.sqlserver:mssql-jdbc dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/mssqlserver/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index 30b9af2bd77..cc05b6ffea4 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'io.r2dbc:r2dbc-mssql:0.9.0.RELEASE' testImplementation project(':jdbc-test') - testImplementation 'com.microsoft.sqlserver:mssql-jdbc:11.1.2.jre8-preview' + testImplementation 'com.microsoft.sqlserver:mssql-jdbc:11.2.0.jre8' testImplementation project(':r2dbc') testImplementation 'io.r2dbc:r2dbc-mssql:0.8.7.RELEASE' From 4495b0c89efb355373faa986772cde94c157f29c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:26 +0000 Subject: [PATCH 2760/5615] Bump aws-java-sdk-dynamodb in /modules/dynalite Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.12.273 to 1.12.281. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.273...1.12.281) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-dynamodb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/dynalite/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 6abd9cd943a..81ec1f9deb5 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Dynalite" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.273' - testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.253' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.281' + testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.281' testImplementation 'org.assertj:assertj-core:3.23.1' } From 2c9e843d09868f3ec877fdb5a9e6916ae59e586e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:27 +0000 Subject: [PATCH 2761/5615] Bump hivemq-extension-sdk from 4.8.2 to 4.8.3 in /modules/hivemq Bumps [hivemq-extension-sdk](https://github.com/hivemq/hivemq-extension-sdk) from 4.8.2 to 4.8.3. - [Release notes](https://github.com/hivemq/hivemq-extension-sdk/releases) - [Commits](https://github.com/hivemq/hivemq-extension-sdk/compare/4.8.2...4.8.3) --- updated-dependencies: - dependency-name: com.hivemq:hivemq-extension-sdk dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index 8336cfd8249..a85288f9fbd 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -13,7 +13,7 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.0") testImplementation(project(":junit-jupiter")) - testImplementation("com.hivemq:hivemq-extension-sdk:4.8.2") + testImplementation("com.hivemq:hivemq-extension-sdk:4.8.3") testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") testImplementation("org.apache.httpcomponents:httpclient:4.5.13") testImplementation("ch.qos.logback:logback-classic:1.2.11") From c3586528904a4fcfc1de8cf1666606c248b53b3c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:28 +0000 Subject: [PATCH 2762/5615] Bump mockito-core from 4.6.1 to 4.7.0 in /modules/junit-jupiter Bumps [mockito-core](https://github.com/mockito/mockito) from 4.6.1 to 4.7.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.6.1...v4.7.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 57932ec84fd..03ae3a9a08c 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,7 +9,7 @@ dependencies { testImplementation 'com.zaxxer:HikariCP:4.0.3' testImplementation 'redis.clients:jedis:4.2.3' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' - testImplementation ('org.mockito:mockito-core:4.6.1') { + testImplementation ('org.mockito:mockito-core:4.7.0') { exclude(module: 'hamcrest-core') } testImplementation 'org.assertj:assertj-core:3.23.1' From 3f3987a1ee46c9f0101a055e2e4d4353fcad2fe6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:29 +0000 Subject: [PATCH 2763/5615] Bump postgresql from 42.4.0 to 42.4.1 in /modules/postgresql Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.0 to 42.4.1. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.0...REL42.4.1) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 87705f77d5d..f769bf9b07c 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -11,7 +11,7 @@ dependencies { testImplementation project(':jdbc-test') testImplementation project(':test-support') - testImplementation 'org.postgresql:postgresql:42.4.0' + testImplementation 'org.postgresql:postgresql:42.4.1' testImplementation testFixtures(project(':r2dbc')) testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.11.RELEASE' From 11ca75a76836b51b649d6464e03d9a2b8dff27be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:29 +0000 Subject: [PATCH 2764/5615] Bump trino-jdbc from 391 to 392 in /modules/trino Bumps trino-jdbc from 391 to 392. --- updated-dependencies: - dependency-name: io.trino:trino-jdbc dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/trino/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/trino/build.gradle b/modules/trino/build.gradle index c25007bb223..09b5b8cb16c 100644 --- a/modules/trino/build.gradle +++ b/modules/trino/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'io.trino:trino-jdbc:391' + testImplementation 'io.trino:trino-jdbc:392' compileOnly 'org.jetbrains:annotations:23.0.0' } From 7f036604c36b6f68bc938b9ad2695b31025c5a60 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:29 +0000 Subject: [PATCH 2765/5615] Bump neo4j from 3.5.34 to 3.5.35 in /modules/neo4j Bumps [neo4j](https://github.com/neo4j/neo4j) from 3.5.34 to 3.5.35. - [Release notes](https://github.com/neo4j/neo4j/releases) - [Commits](https://github.com/neo4j/neo4j/compare/3.5.34...3.5.35) --- updated-dependencies: - dependency-name: org.neo4j:neo4j dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index de42bca2d5d..aabb571edf6 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -29,7 +29,7 @@ task customNeo4jPluginJar(type: Jar) { processTestResources.dependsOn customNeo4jPluginJar dependencies { - customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.34" + customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.35" api project(":testcontainers") From 7bba5118cfd74b9bee5687e546801e56c7dd8779 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:30 +0000 Subject: [PATCH 2766/5615] Bump aws-java-sdk-s3 from 1.12.273 to 1.12.281 in /modules/localstack Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.273 to 1.12.281. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.273...1.12.281) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 8702d2e1a83..0b28ee848ae 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.273' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.281' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.273' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.273' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.273' From 3842d784ec323af87bfe958b7ff1422ecbdc2ae6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:30 +0000 Subject: [PATCH 2767/5615] Bump postgresql from 42.4.0 to 42.4.1 in /modules/spock Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.0 to 42.4.1. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.0...REL42.4.1) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 3eadd33ea2a..304e0b98dc4 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'com.zaxxer:HikariCP:4.0.3' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' - testRuntimeOnly 'org.postgresql:postgresql:42.4.0' + testRuntimeOnly 'org.postgresql:postgresql:42.4.1' testRuntimeOnly 'mysql:mysql-connector-java:8.0.30' testCompileClasspath 'org.jetbrains:annotations:23.0.0' From ae6d57c4c2999527d0c91ceb90d597432754ceb4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:30 +0000 Subject: [PATCH 2768/5615] Bump reactor-core from 3.4.21 to 3.4.22 in /modules/r2dbc Bumps [reactor-core](https://github.com/reactor/reactor-core) from 3.4.21 to 3.4.22. - [Release notes](https://github.com/reactor/reactor-core/releases) - [Commits](https://github.com/reactor/reactor-core/compare/v3.4.21...v3.4.22) --- updated-dependencies: - dependency-name: io.projectreactor:reactor-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/r2dbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle index f51a13b9296..f38803104a3 100644 --- a/modules/r2dbc/build.gradle +++ b/modules/r2dbc/build.gradle @@ -15,6 +15,6 @@ dependencies { testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.12.RELEASE' testImplementation project(':postgresql') - testFixturesImplementation 'io.projectreactor:reactor-core:3.4.21' + testFixturesImplementation 'io.projectreactor:reactor-core:3.4.22' testFixturesImplementation 'org.assertj:assertj-core:3.23.1' } From 6d873e0f9534706ee5a007eb6e125055f14c4b20 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:32 +0000 Subject: [PATCH 2769/5615] Bump mysql-connector-java from 8.0.29 to 8.0.30 in /modules/tidb Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.29 to 8.0.30. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.29...8.0.30) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/tidb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/tidb/build.gradle b/modules/tidb/build.gradle index 51f15a6a798..36f6acfe806 100644 --- a/modules/tidb/build.gradle +++ b/modules/tidb/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'mysql:mysql-connector-java:8.0.29' + testImplementation 'mysql:mysql-connector-java:8.0.30' compileOnly 'org.jetbrains:annotations:23.0.0' } From f4a68fe044779bef456e0b2cb917fc21f5217c4e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:35 +0000 Subject: [PATCH 2770/5615] Bump aws-java-sdk-logs from 1.12.273 to 1.12.281 in /modules/localstack Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.12.273 to 1.12.281. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.273...1.12.281) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-logs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 8702d2e1a83..eacdc411a73 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,7 +6,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.273' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.273' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.273' - testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.273' + testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.281' testImplementation 'software.amazon.awssdk:s3:2.17.244' testImplementation 'org.assertj:assertj-core:3.23.1' } From dd5dc8a813faa02c8d3bc5b8cf4a697fdf487307 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:35 +0000 Subject: [PATCH 2771/5615] Bump google-cloud-spanner from 6.27.0 to 6.28.0 in /modules/gcloud Bumps [google-cloud-spanner](https://github.com/googleapis/java-spanner) from 6.27.0 to 6.28.0. - [Release notes](https://github.com/googleapis/java-spanner/releases) - [Changelog](https://github.com/googleapis/java-spanner/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-spanner/compare/v6.27.0...v6.28.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-spanner dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index ee7f563c88e..bac92bebde9 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -6,7 +6,7 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-datastore:2.10.1' testImplementation 'com.google.cloud:google-cloud-firestore:3.3.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.6' - testImplementation 'com.google.cloud:google-cloud-spanner:6.27.0' + testImplementation 'com.google.cloud:google-cloud-spanner:6.28.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.10.1' testImplementation 'org.assertj:assertj-core:3.23.1' } From 93051c42ac1afaa24959b189cec1270ab7f55443 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:35 +0000 Subject: [PATCH 2772/5615] Bump actions/cache from 3.0.5 to 3.0.7 Bumps [actions/cache](https://github.com/actions/cache) from 3.0.5 to 3.0.7. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3.0.5...v3.0.7) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index 9fa1f78e47b..f23fc66e889 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -25,7 +25,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.5 + uses: actions/cache@v3.0.7 continue-on-error: true with: path: ~/.gradle/caches @@ -53,7 +53,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.5 + uses: actions/cache@v3.0.7 continue-on-error: true with: path: ~/.gradle/caches diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 55432b3e2c6..288e9329824 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.5 + uses: actions/cache@v3.0.7 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-home-testmatrix-${{ hashFiles('**/*.gradle') }} @@ -59,7 +59,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.5 + uses: actions/cache@v3.0.7 continue-on-error: true with: path: ~/.gradle/caches From 63e65926b2b97f7249ffe3f626978948df65327a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:38 +0000 Subject: [PATCH 2773/5615] Bump cucumber-java from 7.5.0 to 7.6.0 in /examples Bumps [cucumber-java](https://github.com/cucumber/cucumber-jvm) from 7.5.0 to 7.6.0. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.5.0...v7.6.0) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-java dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index ba51bbe0d2c..cc6b073413f 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' - testImplementation 'io.cucumber:cucumber-java:7.5.0' + testImplementation 'io.cucumber:cucumber-java:7.6.0' testImplementation 'io.cucumber:cucumber-junit:7.5.0' testImplementation 'org.testcontainers:selenium' } From a45bb987ed20e03fb441fc76da119eeccb04d744 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:39 +0000 Subject: [PATCH 2774/5615] Bump google-cloud-firestore from 3.3.0 to 3.4.0 in /modules/gcloud Bumps [google-cloud-firestore](https://github.com/googleapis/java-firestore) from 3.3.0 to 3.4.0. - [Release notes](https://github.com/googleapis/java-firestore/releases) - [Changelog](https://github.com/googleapis/java-firestore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-firestore/compare/v3.3.0...v3.4.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-firestore dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index ee7f563c88e..f28fb904be8 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':testcontainers') testImplementation 'com.google.cloud:google-cloud-datastore:2.10.1' - testImplementation 'com.google.cloud:google-cloud-firestore:3.3.0' + testImplementation 'com.google.cloud:google-cloud-firestore:3.4.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.6' testImplementation 'com.google.cloud:google-cloud-spanner:6.27.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.10.1' From 39e2005700fffeb88ed1d4f9788684e1d2b32044 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:44 +0000 Subject: [PATCH 2775/5615] Bump com.gradle.enterprise.gradle.plugin in /examples Bumps com.gradle.enterprise.gradle.plugin from 3.10.3 to 3.11.1. --- updated-dependencies: - dependency-name: com.gradle.enterprise:com.gradle.enterprise.gradle.plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/settings.gradle b/examples/settings.gradle index be455a78955..1d3956a88cb 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.10.0" - classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10.3" + classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.11.1" classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7.2" } } From 625947f89c7e2fcbebbf70462923987deded02b5 Mon Sep 17 00:00:00 2001 From: Julien Date: Mon, 15 Aug 2022 16:31:59 +0200 Subject: [PATCH 2776/5615] Add Consul module (#4683) Co-authored-by: Kevin Wittek --- .github/ISSUE_TEMPLATE/bug_report.yaml | 1 + .github/ISSUE_TEMPLATE/enhancement.yaml | 1 + .github/ISSUE_TEMPLATE/feature.yaml | 1 + .github/dependabot.yml | 5 + .github/labeler.yml | 2 + docs/modules/consul.md | 36 +++++++ mkdocs.yml | 1 + modules/consul/build.gradle | 9 ++ .../consul/ConsulContainer.java | 100 ++++++++++++++++++ .../consul/ConsulContainerTest.java | 78 ++++++++++++++ .../consul/ConsulTestImages.java | 7 ++ .../src/test/resources/logback-test.xml | 16 +++ 12 files changed, 257 insertions(+) create mode 100644 docs/modules/consul.md create mode 100644 modules/consul/build.gradle create mode 100644 modules/consul/src/main/java/org/testcontainers/consul/ConsulContainer.java create mode 100644 modules/consul/src/test/java/org/testcontainers/consul/ConsulContainerTest.java create mode 100644 modules/consul/src/test/java/org/testcontainers/consul/ConsulTestImages.java create mode 100644 modules/consul/src/test/resources/logback-test.xml diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 07d944243cd..5aca9e624b3 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -18,6 +18,7 @@ body: - Cassandra - Clickhouse - CockroachDB + - Consul - Couchbase - DB2 - Dynalite diff --git a/.github/ISSUE_TEMPLATE/enhancement.yaml b/.github/ISSUE_TEMPLATE/enhancement.yaml index ea190cefc80..ba5b1f88006 100644 --- a/.github/ISSUE_TEMPLATE/enhancement.yaml +++ b/.github/ISSUE_TEMPLATE/enhancement.yaml @@ -18,6 +18,7 @@ body: - Cassandra - Clickhouse - CockroachDB + - Consul - Couchbase - DB2 - Dynalite diff --git a/.github/ISSUE_TEMPLATE/feature.yaml b/.github/ISSUE_TEMPLATE/feature.yaml index 09810d3e56f..981f5613a1e 100644 --- a/.github/ISSUE_TEMPLATE/feature.yaml +++ b/.github/ISSUE_TEMPLATE/feature.yaml @@ -18,6 +18,7 @@ body: - Cassandra - Clickhouse - CockroachDB + - Consul - Couchbase - DB2 - Dynalite diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2d877f664ab..22437e15825 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -42,6 +42,11 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/consul" + schedule: + interval: "monthly" + open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/couchbase" schedule: diff --git a/.github/labeler.yml b/.github/labeler.yml index 826e5c16215..cf75af99d26 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -15,6 +15,8 @@ - modules/clickhouse/* "modules/cockroachdb": - modules/cockroachdb/* +"modules/consul": + - modules/consul/* "modules/couchbase": - modules/couchbase/* "modules/db2": diff --git a/docs/modules/consul.md b/docs/modules/consul.md new file mode 100644 index 00000000000..1580690cad7 --- /dev/null +++ b/docs/modules/consul.md @@ -0,0 +1,36 @@ +# Hashicorp Consul Module + +Testcontainers module for [Consul](https://github.com/hashicorp/consul). Consul is a tool for managing key value properties. More information on Consul [here](https://www.consul.io/). + +## Usage example + + +[Running Consul in your Junit tests](../../modules/consul/src/test/java/org/testcontainers/consul/ConsulContainerTest.java) + + +## Why Consul in Junit tests? + +With the increasing popularity of Consul and config externalization, applications are now needing to source properties from Consul. +This can prove challenging in the development phase without a running Consul instance readily on hand. This library +aims to solve your apps integration testing with Consul. You can also use it to +test how your application behaves with Consul by writing different test scenarios in Junit. + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testImplementation "org.testcontainers:consul:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + consul + {{latest_version}} + test + +``` + +See [AUTHORS](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/modules/consul/AUTHORS) for contributors. + diff --git a/mkdocs.yml b/mkdocs.yml index 9491ebe6019..a836e0a2ff7 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -66,6 +66,7 @@ nav: - modules/databases/tidb.md - modules/databases/trino.md - modules/azure.md + - modules/consul.md - modules/docker_compose.md - modules/elasticsearch.md - modules/gcloud.md diff --git a/modules/consul/build.gradle b/modules/consul/build.gradle new file mode 100644 index 00000000000..0d0d6e7e992 --- /dev/null +++ b/modules/consul/build.gradle @@ -0,0 +1,9 @@ +description = "Testcontainers :: Consul" + +dependencies { + api project(':testcontainers') + + testImplementation 'com.ecwid.consul:consul-api:1.4.5' + testImplementation 'io.rest-assured:rest-assured:4.4.0' + testImplementation 'org.assertj:assertj-core:3.21.0' +} diff --git a/modules/consul/src/main/java/org/testcontainers/consul/ConsulContainer.java b/modules/consul/src/main/java/org/testcontainers/consul/ConsulContainer.java new file mode 100644 index 00000000000..0c9d2607072 --- /dev/null +++ b/modules/consul/src/main/java/org/testcontainers/consul/ConsulContainer.java @@ -0,0 +1,100 @@ +package org.testcontainers.consul; + +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.model.Capability; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.utility.DockerImageName; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Testcontainers implementation for Consul. + */ +public class ConsulContainer extends GenericContainer { + + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("consul"); + + private static final int CONSUL_HTTP_PORT = 8500; + + private static final int CONSUL_GRPC_PORT = 8502; + + private List initCommands = new ArrayList<>(); + + private String[] startConsulCmd = new String[] { "agent", "-dev", "-client", "0.0.0.0" }; + + public ConsulContainer(String dockerImageName) { + this(DockerImageName.parse(dockerImageName)); + } + + public ConsulContainer(final DockerImageName dockerImageName) { + super(dockerImageName); + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + + // Use the status leader endpoint to verify if consul is running. + setWaitStrategy(Wait.forHttp("/v1/status/leader").forPort(CONSUL_HTTP_PORT).forStatusCode(200)); + + withCreateContainerCmdModifier(cmd -> cmd.getHostConfig().withCapAdd(Capability.IPC_LOCK)); + withEnv("CONSUL_ADDR", "http://0.0.0.0:" + CONSUL_HTTP_PORT); + withCommand(startConsulCmd); + withExposedPorts(CONSUL_HTTP_PORT, CONSUL_GRPC_PORT); + } + + @Override + protected void containerIsStarted(InspectContainerResponse containerInfo) { + runConsulCommands(); + } + + private void runConsulCommands() { + if (!initCommands.isEmpty()) { + String commands = initCommands + .stream() + .map(command -> "consul " + command) + .collect(Collectors.joining(" && ")); + try { + ExecResult execResult = this.execInContainer(new String[] { "/bin/sh", "-c", commands }); + if (execResult.getExitCode() != 0) { + logger() + .error( + "Failed to execute these init commands {}. Exit code {}. Stdout {}. Stderr {}", + initCommands, + execResult.getExitCode(), + execResult.getStdout(), + execResult.getStderr() + ); + } + } catch (IOException | InterruptedException e) { + logger() + .error( + "Failed to execute these init commands {}. Exception message: {}", + initCommands, + e.getMessage() + ); + } + } + } + + /** + * Run consul commands using the consul cli. + * + * Useful for enableing more secret engines like: + *
+     *     .withConsulCommand("secrets enable pki")
+     *     .withConsulCommand("secrets enable transit")
+     * 
+ * or register specific K/V like: + *
+     *     .withConsulCommand("kv put config/testing1 value123")
+     * 
+ * @param commands The commands to send to the consul cli + * @return this + */ + public ConsulContainer withConsulCommand(String... commands) { + initCommands.addAll(Arrays.asList(commands)); + return self(); + } +} diff --git a/modules/consul/src/test/java/org/testcontainers/consul/ConsulContainerTest.java b/modules/consul/src/test/java/org/testcontainers/consul/ConsulContainerTest.java new file mode 100644 index 00000000000..e29ff7ffbd8 --- /dev/null +++ b/modules/consul/src/test/java/org/testcontainers/consul/ConsulContainerTest.java @@ -0,0 +1,78 @@ +package org.testcontainers.consul; + +import com.ecwid.consul.v1.ConsulClient; +import com.ecwid.consul.v1.Response; +import com.ecwid.consul.v1.kv.model.GetValue; +import io.restassured.RestAssured; +import org.assertj.core.api.Assertions; +import org.hamcrest.CoreMatchers; +import org.hamcrest.MatcherAssert; +import org.junit.ClassRule; +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; + +/** + * This test shows the pattern to use the ConsulContainer @ClassRule for a junit test. It also has tests that ensure + * the properties were added correctly by reading from Consul with the CLI and over HTTP. + */ +public class ConsulContainerTest { + + @ClassRule + public static ConsulContainer consulContainer = new ConsulContainer(ConsulTestImages.CONSUL_IMAGE) + .withConsulCommand("kv put config/testing1 value123"); + + @Test + public void readFirstPropertyPathWithCli() throws IOException, InterruptedException { + GenericContainer.ExecResult result = consulContainer.execInContainer("consul", "kv", "get", "config/testing1"); + final String output = result.getStdout().replaceAll("\\r?\\n", ""); + MatcherAssert.assertThat(output, CoreMatchers.containsString("value123")); + } + + @Test + public void readFirstSecretPathOverHttpApi() throws InterruptedException { + RestAssured + .given() + .when() + .get("http://" + getHostAndPort() + "/v1/kv/config/testing1") + .then() + .assertThat() + .body( + "[0].Value", + CoreMatchers.equalTo(Base64.getEncoder().encodeToString("value123".getBytes(StandardCharsets.UTF_8))) + ); + } + + @Test + public void writeAndReadMultipleValuesUsingClient() { + final ConsulClient consulClient = new ConsulClient( + consulContainer.getHost(), + consulContainer.getFirstMappedPort() + ); + + final Map properties = new HashMap<>(); + properties.put("value", "world"); + properties.put("other_value", "another world"); + + // Write operation + properties.forEach((key, value) -> { + Response writeResponse = consulClient.setKVValue(key, value); + Assertions.assertThat(writeResponse.getValue()).isTrue(); + }); + + // Read operation + properties.forEach((key, value) -> { + Response readResponse = consulClient.getKVValue(key); + Assertions.assertThat(readResponse.getValue().getDecodedValue()).isEqualTo(value); + }); + } + + private String getHostAndPort() { + return consulContainer.getHost() + ":" + consulContainer.getMappedPort(8500); + } +} diff --git a/modules/consul/src/test/java/org/testcontainers/consul/ConsulTestImages.java b/modules/consul/src/test/java/org/testcontainers/consul/ConsulTestImages.java new file mode 100644 index 00000000000..de2b9702066 --- /dev/null +++ b/modules/consul/src/test/java/org/testcontainers/consul/ConsulTestImages.java @@ -0,0 +1,7 @@ +package org.testcontainers.consul; + +import org.testcontainers.utility.DockerImageName; + +public interface ConsulTestImages { + DockerImageName CONSUL_IMAGE = DockerImageName.parse("consul:1.10.12"); +} diff --git a/modules/consul/src/test/resources/logback-test.xml b/modules/consul/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..535e406fc13 --- /dev/null +++ b/modules/consul/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + From 84102fe2590c9128f218a3c65ac77e3a606d69bf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:32:43 +0000 Subject: [PATCH 2777/5615] Bump assertj-core from 3.21.0 to 3.23.1 in /modules/consul Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.21.0 to 3.23.1. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.21.0...assertj-core-3.23.1) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/consul/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/consul/build.gradle b/modules/consul/build.gradle index 0d0d6e7e992..49e9b404690 100644 --- a/modules/consul/build.gradle +++ b/modules/consul/build.gradle @@ -5,5 +5,5 @@ dependencies { testImplementation 'com.ecwid.consul:consul-api:1.4.5' testImplementation 'io.rest-assured:rest-assured:4.4.0' - testImplementation 'org.assertj:assertj-core:3.21.0' + testImplementation 'org.assertj:assertj-core:3.23.1' } From df2b1d1aed8b8f612059781ff9c620ef06eb2999 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 01:32:24 +0000 Subject: [PATCH 2778/5615] Bump google-cloud-bigtable from 2.10.1 to 2.10.3 in /modules/gcloud Bumps [google-cloud-bigtable](https://github.com/googleapis/java-bigtable) from 2.10.1 to 2.10.3. - [Release notes](https://github.com/googleapis/java-bigtable/releases) - [Changelog](https://github.com/googleapis/java-bigtable/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-bigtable/compare/v2.10.1...v2.10.3) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-bigtable dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 60778fff111..ce332830e07 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-firestore:3.4.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.6' testImplementation 'com.google.cloud:google-cloud-spanner:6.28.0' - testImplementation 'com.google.cloud:google-cloud-bigtable:2.10.1' + testImplementation 'com.google.cloud:google-cloud-bigtable:2.10.3' testImplementation 'org.assertj:assertj-core:3.23.1' } From 9b51b77f02d776583cca635e55e114b82b687f57 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 01:32:25 +0000 Subject: [PATCH 2779/5615] Bump common-custom-user-data-gradle-plugin from 1.7.2 to 1.8.0 Bumps common-custom-user-data-gradle-plugin from 1.7.2 to 1.8.0. --- updated-dependencies: - dependency-name: com.gradle:common-custom-user-data-gradle-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index 116a4e26506..bd51349bb05 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.11.1" - classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7.2" + classpath "com.gradle:common-custom-user-data-gradle-plugin:1.8.0" } } From b1cd2beca42b259813fa09884c279b79cf0f9b71 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 01:32:34 +0000 Subject: [PATCH 2780/5615] Bump aws-java-sdk-sqs from 1.12.273 to 1.12.282 in /modules/localstack Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.12.273 to 1.12.282. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.273...1.12.282) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-sqs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index ea528389c23..98027ef09b7 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.281' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.273' - testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.273' + testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.282' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.281' testImplementation 'software.amazon.awssdk:s3:2.17.244' testImplementation 'org.assertj:assertj-core:3.23.1' From c3ba559f13f32b44353efc59cc44d4b7f66faf8f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 01:32:49 +0000 Subject: [PATCH 2781/5615] Bump google-cloud-datastore from 2.10.1 to 2.11.0 in /modules/gcloud Bumps [google-cloud-datastore](https://github.com/googleapis/java-datastore) from 2.10.1 to 2.11.0. - [Release notes](https://github.com/googleapis/java-datastore/releases) - [Changelog](https://github.com/googleapis/java-datastore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-datastore/compare/v2.10.1...v2.11.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-datastore dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 60778fff111..5202c3c8fbe 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: GCloud" dependencies { api project(':testcontainers') - testImplementation 'com.google.cloud:google-cloud-datastore:2.10.1' + testImplementation 'com.google.cloud:google-cloud-datastore:2.11.0' testImplementation 'com.google.cloud:google-cloud-firestore:3.4.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.6' testImplementation 'com.google.cloud:google-cloud-spanner:6.28.0' From 04c7b0a49c470592cb8fa1f1344273987a87e598 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 01:32:49 +0000 Subject: [PATCH 2782/5615] Bump google-cloud-pubsub from 1.120.6 to 1.120.11 in /modules/gcloud Bumps [google-cloud-pubsub](https://github.com/googleapis/java-pubsub) from 1.120.6 to 1.120.11. - [Release notes](https://github.com/googleapis/java-pubsub/releases) - [Changelog](https://github.com/googleapis/java-pubsub/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-pubsub/compare/v1.120.6...v1.120.11) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-pubsub dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 60778fff111..db006563c74 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -5,7 +5,7 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-datastore:2.10.1' testImplementation 'com.google.cloud:google-cloud-firestore:3.4.0' - testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.6' + testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.11' testImplementation 'com.google.cloud:google-cloud-spanner:6.28.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.10.1' testImplementation 'org.assertj:assertj-core:3.23.1' From 115eca1a326bf7fc4e925461ddfdccc987e3a0c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 01:32:51 +0000 Subject: [PATCH 2783/5615] Bump postgresql from 42.4.0 to 42.4.1 in /examples Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.0 to 42.4.1. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.0...REL42.4.1) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index c6272fc7522..ab32d55df91 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.36' implementation 'com.squareup.okhttp3:okhttp:4.10.0' implementation 'org.json:json:20220320' - testImplementation 'org.postgresql:postgresql:42.4.0' + testImplementation 'org.postgresql:postgresql:42.4.1' testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.testcontainers:postgresql' } From 66e51c890f9992d4fbfdfb01138252821e97a09f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 01:32:51 +0000 Subject: [PATCH 2784/5615] Bump mockito-core from 4.6.1 to 4.7.0 in /modules/jdbc Bumps [mockito-core](https://github.com/mockito/mockito) from 4.6.1 to 4.7.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.6.1...v4.7.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 43ff0968715..ba0f845b47d 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -10,7 +10,7 @@ dependencies { testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.23' testImplementation 'com.zaxxer:HikariCP-java6:2.3.13' testImplementation 'org.assertj:assertj-core:3.23.1' - testImplementation ('org.mockito:mockito-core:4.6.1') { + testImplementation ('org.mockito:mockito-core:4.7.0') { exclude(module: 'hamcrest-core') } } From 5bc0a1c0c5f741ca63e595c69b47a44a165935ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 01:32:54 +0000 Subject: [PATCH 2785/5615] Bump s3 from 2.17.244 to 2.17.253 in /modules/localstack Bumps s3 from 2.17.244 to 2.17.253. --- updated-dependencies: - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index ea528389c23..d29dbda0cd2 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.273' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.273' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.281' - testImplementation 'software.amazon.awssdk:s3:2.17.244' + testImplementation 'software.amazon.awssdk:s3:2.17.253' testImplementation 'org.assertj:assertj-core:3.23.1' } From f92ff763362857e9f0854ed3c7a43673ff0dad5e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 01:33:04 +0000 Subject: [PATCH 2786/5615] Bump rest-assured from 4.4.0 to 5.1.1 in /modules/consul Bumps [rest-assured](https://github.com/rest-assured/rest-assured) from 4.4.0 to 5.1.1. - [Release notes](https://github.com/rest-assured/rest-assured/releases) - [Changelog](https://github.com/rest-assured/rest-assured/blob/master/changelog.txt) - [Commits](https://github.com/rest-assured/rest-assured/compare/rest-assured-4.4.0...rest-assured-5.1.1) --- updated-dependencies: - dependency-name: io.rest-assured:rest-assured dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/consul/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/consul/build.gradle b/modules/consul/build.gradle index 49e9b404690..acb97a47927 100644 --- a/modules/consul/build.gradle +++ b/modules/consul/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':testcontainers') testImplementation 'com.ecwid.consul:consul-api:1.4.5' - testImplementation 'io.rest-assured:rest-assured:4.4.0' + testImplementation 'io.rest-assured:rest-assured:5.1.1' testImplementation 'org.assertj:assertj-core:3.23.1' } From 159922275dd6ddb6ecac7f6ea7b0a923ab9e509c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 01:33:04 +0000 Subject: [PATCH 2787/5615] Bump common-custom-user-data-gradle-plugin in /examples Bumps common-custom-user-data-gradle-plugin from 1.7.2 to 1.8.0. --- updated-dependencies: - dependency-name: com.gradle:common-custom-user-data-gradle-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/settings.gradle b/examples/settings.gradle index 1d3956a88cb..83ba91e8321 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -7,7 +7,7 @@ buildscript { dependencies { classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.10.0" classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.11.1" - classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7.2" + classpath "com.gradle:common-custom-user-data-gradle-plugin:1.8.0" } } From ae2e71c82bc33922376f7272b9801d79025bd068 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 01:33:07 +0000 Subject: [PATCH 2788/5615] Bump cucumber-junit from 7.5.0 to 7.6.0 in /examples Bumps [cucumber-junit](https://github.com/cucumber/cucumber-jvm) from 7.5.0 to 7.6.0. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.5.0...v7.6.0) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-junit dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index cc6b073413f..ab0331f81f4 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' testImplementation 'io.cucumber:cucumber-java:7.6.0' - testImplementation 'io.cucumber:cucumber-junit:7.5.0' + testImplementation 'io.cucumber:cucumber-junit:7.6.0' testImplementation 'org.testcontainers:selenium' } From 6a3e649b296bba3c5723a2538b21d4eabb593cae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Tue, 16 Aug 2022 09:25:10 -0500 Subject: [PATCH 2789/5615] Move junit4 to assertj (#5685) Co-authored-by: Kevin Wittek --- config/checkstyle/checkstyle.xml | 4 - .../FrameConsumerResultCallbackTest.java | 27 ++++--- .../DockerClientConfigUtilsTest.java | 14 ++-- .../images/AgeBasedPullPolicyTest.java | 7 +- .../images/ParsedDockerfileTest.java | 74 ++++++++----------- .../images/RemoteDockerImageTest.java | 26 +++---- .../junit/FixedHostPortContainerTest.java | 13 ++-- .../strategy/AbstractWaitStrategyTest.java | 8 +- .../wait/strategy/HttpWaitStrategyTest.java | 7 +- .../utility/ComparableVersionTest.java | 4 +- .../DefaultImageNameSubstitutorTest.java | 7 +- .../DockerImageNameCompatibilityTest.java | 14 +--- .../utility/DockerStatusTest.java | 31 ++++---- .../utility/FilterRegistryTest.java | 10 +-- .../utility/ResourceReaperTest.java | 3 +- .../test/java/generic/CmdModifierTest.java | 6 +- .../src/test/java/generic/CommandsTest.java | 4 +- .../java/generic/ContainerCreationTest.java | 6 +- .../src/test/java/generic/DependsOnTest.java | 6 +- .../src/test/java/generic/ExecTest.java | 6 +- .../java/generic/HostPortExposedTest.java | 4 +- .../test/java/generic/WaitStrategiesTest.java | 8 +- docs/examples/junit4/redis/build.gradle | 1 + .../quickstart/RedisBackedCacheIntTest.java | 4 +- .../RedisBackedCacheIntTestStep0.java | 4 +- examples/linked-container/build.gradle | 1 + .../linkedcontainer/RedmineClientTest.java | 7 +- .../containers/Neo4jExampleTest.java | 2 +- modules/cassandra/build.gradle | 2 + .../containers/CassandraContainerTest.java | 34 ++++----- .../containers/CassandraDriver3Test.java | 4 +- .../containers/CassandraDriver4Test.java | 4 +- .../consul/ConsulContainerTest.java | 25 +++---- modules/couchbase/build.gradle | 1 + .../couchbase/CouchbaseContainerTest.java | 27 +++---- modules/influxdb/build.gradle | 1 + .../containers/InfluxDBContainerTest.java | 14 ++-- .../InfluxDBContainerWithUserTest.java | 26 +++---- .../jdbc/ContainerDatabaseDriverTest.java | 5 +- .../junit/jupiter/PostgresContainerTests.java | 4 +- ...tLifecycleAwareExceptionCapturingTest.java | 7 +- .../localstack/LocalstackContainerTest.java | 3 +- modules/mongodb/build.gradle | 1 + .../containers/MongoDBContainerTest.java | 11 +-- .../junit/oracle/SimpleOracleTest.java | 16 ++-- .../containers/PrestoContainerTest.java | 71 ++++++++---------- .../DefaultRecordingFileFactoryTest.java | 5 +- ...ChromeRecordingWebDriverContainerTest.java | 19 +++-- modules/solr/build.gradle | 2 +- .../containers/SolrContainerTest.java | 12 ++- .../junit/tidb/SimpleTiDBTest.java | 12 +-- .../containers/TrinoContainerTest.java | 26 +++---- .../vault/VaultContainerTest.java | 46 ++++++------ test-support/build.gradle | 1 + .../testsupport/FlakyRuleTest.java | 34 +++++---- 55 files changed, 325 insertions(+), 396 deletions(-) diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index 6e73f4da4aa..6e51c56c942 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -29,11 +29,7 @@ org.assertj.core.api.Assertions.*, org.assertj.core.api.Assumptions.*, org.awaitility.Awaitility.*, - org.hamcrest.Matchers.*, - org.hamcrest.MatcherAssert.*, - org.junit.Assert.*, org.junit.Assume.*, - org.junit.jupiter.api.Assertions.*, org.mockito.Mockito.*, org.mockito.ArgumentMatchers.*, org.mockserver.model.HttpRequest.*, diff --git a/core/src/test/java/org/testcontainers/containers/output/FrameConsumerResultCallbackTest.java b/core/src/test/java/org/testcontainers/containers/output/FrameConsumerResultCallbackTest.java index 5282712b733..298e7a981a0 100644 --- a/core/src/test/java/org/testcontainers/containers/output/FrameConsumerResultCallbackTest.java +++ b/core/src/test/java/org/testcontainers/containers/output/FrameConsumerResultCallbackTest.java @@ -10,8 +10,7 @@ import java.util.concurrent.TimeoutException; import java.util.function.Consumer; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class FrameConsumerResultCallbackTest { @@ -26,7 +25,7 @@ public void passStderrFrameWithoutColors() { ToStringConsumer consumer = new ToStringConsumer(); callback.addConsumer(OutputFrame.OutputType.STDERR, consumer); callback.onNext(new Frame(StreamType.STDERR, FRAME_PAYLOAD.getBytes())); - assertEquals(LOG_RESULT, consumer.toUtf8String()); + assertThat(consumer.toUtf8String()).isEqualTo(LOG_RESULT); } @Test @@ -35,7 +34,7 @@ public void passStderrFrameWithColors() { ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); callback.addConsumer(OutputFrame.OutputType.STDERR, consumer); callback.onNext(new Frame(StreamType.STDERR, FRAME_PAYLOAD.getBytes())); - assertEquals(FRAME_PAYLOAD, consumer.toUtf8String()); + assertThat(consumer.toUtf8String()).isEqualTo(FRAME_PAYLOAD); } @Test @@ -44,7 +43,7 @@ public void passStdoutFrameWithoutColors() { ToStringConsumer consumer = new ToStringConsumer(); callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, FRAME_PAYLOAD.getBytes())); - assertEquals(LOG_RESULT, consumer.toUtf8String()); + assertThat(consumer.toUtf8String()).isEqualTo(LOG_RESULT); } @Test @@ -53,7 +52,7 @@ public void passStdoutFrameWithColors() { ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, FRAME_PAYLOAD.getBytes())); - assertEquals(FRAME_PAYLOAD, consumer.toUtf8String()); + assertThat(consumer.toUtf8String()).isEqualTo(FRAME_PAYLOAD); } @Test @@ -62,7 +61,7 @@ public void basicConsumer() { BasicConsumer consumer = new BasicConsumer(); callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, FRAME_PAYLOAD.getBytes())); - assertEquals(LOG_RESULT, consumer.toString()); + assertThat(consumer.toString()).isEqualTo(LOG_RESULT); } @Test @@ -71,7 +70,7 @@ public void passStdoutNull() { ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, null)); - assertEquals("", consumer.toUtf8String()); + assertThat(consumer.toUtf8String()).isEqualTo(""); } @Test @@ -81,7 +80,7 @@ public void passStdoutEmptyLine() { ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, payload.getBytes())); - assertEquals(payload, consumer.toUtf8String()); + assertThat(consumer.toUtf8String()).isEqualTo(payload); } @Test @@ -91,7 +90,7 @@ public void passStdoutSingleLine() { ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, payload.getBytes())); - assertEquals(payload, consumer.toUtf8String()); + assertThat(consumer.toUtf8String()).isEqualTo(payload); } @Test @@ -101,7 +100,7 @@ public void passStdoutSingleLineWithNewline() { ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, payload.getBytes())); - assertEquals(payload, consumer.toUtf8String()); + assertThat(consumer.toUtf8String()).isEqualTo(payload); } @Test @@ -130,7 +129,7 @@ public void passRawFrameWithoutColors() throws TimeoutException, IOException { } catch (Exception e) { exception = e; } - assertTrue(exception instanceof TimeoutException); + assertThat(exception instanceof TimeoutException).isTrue(); callback.close(); waitConsumer.waitUntil( frame -> frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().equals("Test3"), @@ -180,7 +179,7 @@ public void passRawFrameWithColors() throws TimeoutException, IOException { } catch (Exception e) { exception = e; } - assertTrue(exception instanceof TimeoutException); + assertThat(exception instanceof TimeoutException).isTrue(); callback.close(); waitConsumer.waitUntil( frame -> { @@ -208,7 +207,7 @@ public void reconstructBreakedUnicode() throws IOException { callback.onNext(new Frame(StreamType.RAW, bytes1)); callback.onNext(new Frame(StreamType.RAW, bytes2)); callback.close(); - assertEquals(payload, consumer.toUtf8String()); + assertThat(consumer.toUtf8String()).isEqualTo(payload); } private static class BasicConsumer implements Consumer { diff --git a/core/src/test/java/org/testcontainers/dockerclient/DockerClientConfigUtilsTest.java b/core/src/test/java/org/testcontainers/dockerclient/DockerClientConfigUtilsTest.java index 39e1338bc46..d14ffda42cc 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/DockerClientConfigUtilsTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/DockerClientConfigUtilsTest.java @@ -7,9 +7,7 @@ import java.net.URI; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; +import static org.assertj.core.api.Assertions.assertThat; public class DockerClientConfigUtilsTest { @@ -23,7 +21,7 @@ public void getDockerHostIpAddressShouldReturnLocalhostWhenUnixSocket() { client, URI.create("unix:///var/run/docker.sock") ); - assertEquals("localhost", actual); + assertThat(actual).isEqualTo("localhost"); } @Test @@ -32,7 +30,7 @@ public void getDockerHostIpAddressShouldReturnDockerHostIpWhenHttpsUri() { client, URI.create("http://12.23.34.45") ); - assertEquals("12.23.34.45", actual); + assertThat(actual).isEqualTo("12.23.34.45"); } @Test @@ -41,7 +39,7 @@ public void getDockerHostIpAddressShouldReturnDockerHostIpWhenTcpUri() { client, URI.create("tcp://12.23.34.45") ); - assertEquals("12.23.34.45", actual); + assertThat(actual).isEqualTo("12.23.34.45"); } @Test @@ -50,11 +48,11 @@ public void getDockerHostIpAddressShouldReturnNullWhenUnsupportedUriScheme() { client, URI.create("gopher://12.23.34.45") ); - assertNull(actual); + assertThat(actual).isNull(); } @Test(timeout = 5_000) public void getDefaultGateway() { - assertNotNull(DockerClientConfigUtils.getDefaultGateway()); + assertThat(DockerClientConfigUtils.getDefaultGateway()).isNotNull(); } } diff --git a/core/src/test/java/org/testcontainers/images/AgeBasedPullPolicyTest.java b/core/src/test/java/org/testcontainers/images/AgeBasedPullPolicyTest.java index 96fdfa4010b..0bdf969688b 100644 --- a/core/src/test/java/org/testcontainers/images/AgeBasedPullPolicyTest.java +++ b/core/src/test/java/org/testcontainers/images/AgeBasedPullPolicyTest.java @@ -8,8 +8,7 @@ import java.time.temporal.ChronoUnit; import java.util.UUID; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class AgeBasedPullPolicyTest { @@ -20,9 +19,9 @@ public void shouldPull() { ImageData imageData = ImageData.builder().createdAt(Instant.now().minus(2, ChronoUnit.HOURS)).build(); AgeBasedPullPolicy oneHour = new AgeBasedPullPolicy(Duration.of(1L, ChronoUnit.HOURS)); - assertTrue(oneHour.shouldPullCached(imageName, imageData)); + assertThat(oneHour.shouldPullCached(imageName, imageData)).isTrue(); AgeBasedPullPolicy fiveHours = new AgeBasedPullPolicy(Duration.of(5L, ChronoUnit.HOURS)); - assertFalse(fiveHours.shouldPullCached(imageName, imageData)); + assertThat(fiveHours.shouldPullCached(imageName, imageData)).isFalse(); } } diff --git a/core/src/test/java/org/testcontainers/images/ParsedDockerfileTest.java b/core/src/test/java/org/testcontainers/images/ParsedDockerfileTest.java index cd4509965e6..f8c66860022 100644 --- a/core/src/test/java/org/testcontainers/images/ParsedDockerfileTest.java +++ b/core/src/test/java/org/testcontainers/images/ParsedDockerfileTest.java @@ -6,7 +6,7 @@ import java.nio.file.Paths; import java.util.Arrays; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class ParsedDockerfileTest { @@ -15,11 +15,9 @@ public void doesSimpleParsing() { final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( Arrays.asList("FROM someimage", "RUN something") ); - assertEquals( - "extracts a single image name", - Sets.newHashSet("someimage"), - parsedDockerfile.getDependencyImageNames() - ); + assertThat(parsedDockerfile.getDependencyImageNames()) + .as("extracts a single image name") + .isEqualTo(Sets.newHashSet("someimage")); } @Test @@ -27,11 +25,9 @@ public void isCaseInsensitive() { final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( Arrays.asList("from someimage", "RUN something") ); - assertEquals( - "extracts a single image name", - Sets.newHashSet("someimage"), - parsedDockerfile.getDependencyImageNames() - ); + assertThat(parsedDockerfile.getDependencyImageNames()) + .as("extracts a single image name") + .isEqualTo(Sets.newHashSet("someimage")); } @Test @@ -39,11 +35,9 @@ public void handlesTags() { final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( Arrays.asList("FROM someimage:tag", "RUN something") ); - assertEquals( - "retains tags in image names", - Sets.newHashSet("someimage:tag"), - parsedDockerfile.getDependencyImageNames() - ); + assertThat(parsedDockerfile.getDependencyImageNames()) + .as("retains tags in image names") + .isEqualTo(Sets.newHashSet("someimage:tag")); } @Test @@ -51,11 +45,9 @@ public void handlesDigests() { final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( Arrays.asList("FROM someimage@sha256:abc123", "RUN something") ); - assertEquals( - "retains digests in image names", - Sets.newHashSet("someimage@sha256:abc123"), - parsedDockerfile.getDependencyImageNames() - ); + assertThat(parsedDockerfile.getDependencyImageNames()) + .as("retains digests in image names") + .isEqualTo(Sets.newHashSet("someimage@sha256:abc123")); } @Test @@ -63,11 +55,9 @@ public void ignoringCommentedFromLines() { final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( Arrays.asList("FROM someimage", "#FROM somethingelse") ); - assertEquals( - "ignores commented from lines", - Sets.newHashSet("someimage"), - parsedDockerfile.getDependencyImageNames() - ); + assertThat(parsedDockerfile.getDependencyImageNames()) + .as("ignores commented from lines") + .isEqualTo(Sets.newHashSet("someimage")); } @Test @@ -75,11 +65,9 @@ public void ignoringBuildStageNames() { final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( Arrays.asList("FROM someimage --as=base", "RUN something", "FROM nextimage", "RUN something") ); - assertEquals( - "ignores build stage names and allows multiple images to be extracted", - Sets.newHashSet("someimage", "nextimage"), - parsedDockerfile.getDependencyImageNames() - ); + assertThat(parsedDockerfile.getDependencyImageNames()) + .as("ignores build stage names and allows multiple images to be extracted") + .isEqualTo(Sets.newHashSet("someimage", "nextimage")); } @Test @@ -87,7 +75,9 @@ public void ignoringPlatformArgs() { final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( Arrays.asList("FROM --platform=linux/amd64 someimage", "RUN something") ); - assertEquals("ignores platform args", Sets.newHashSet("someimage"), parsedDockerfile.getDependencyImageNames()); + assertThat(parsedDockerfile.getDependencyImageNames()) + .as("ignores platform args") + .isEqualTo(Sets.newHashSet("someimage")); } @Test @@ -95,7 +85,9 @@ public void ignoringExtraPlatformArgs() { final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( Arrays.asList("FROM --platform=linux/amd64 --somethingelse=value someimage", "RUN something") ); - assertEquals("ignores platform args", Sets.newHashSet("someimage"), parsedDockerfile.getDependencyImageNames()); + assertThat(parsedDockerfile.getDependencyImageNames()) + .as("ignores platform args") + .isEqualTo(Sets.newHashSet("someimage")); } @Test @@ -103,20 +95,16 @@ public void handlesGracefullyIfNoFromLine() { final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( Arrays.asList("RUN something", "# is this even a valid Dockerfile?") ); - assertEquals( - "handles invalid Dockerfiles gracefully", - Sets.newHashSet(), - parsedDockerfile.getDependencyImageNames() - ); + assertThat(parsedDockerfile.getDependencyImageNames()) + .as("handles invalid Dockerfiles gracefully") + .isEqualTo(Sets.newHashSet()); } @Test public void handlesGracefullyIfDockerfileNotFound() { final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(Paths.get("nonexistent.Dockerfile")); - assertEquals( - "handles missing Dockerfiles gracefully", - Sets.newHashSet(), - parsedDockerfile.getDependencyImageNames() - ); + assertThat(parsedDockerfile.getDependencyImageNames()) + .as("handles missing Dockerfiles gracefully") + .isEqualTo(Sets.newHashSet()); } } diff --git a/core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java b/core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java index 592d46f33d1..9e4ac66fb53 100644 --- a/core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java +++ b/core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java @@ -9,9 +9,7 @@ import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; -import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; public class RemoteDockerImageTest { @@ -19,7 +17,7 @@ public class RemoteDockerImageTest { public void toStringContainsOnlyImageName() { String imageName = Base58.randomString(8).toLowerCase(); RemoteDockerImage remoteDockerImage = new RemoteDockerImage(DockerImageName.parse(imageName)); - assertThat(remoteDockerImage.toString(), containsString("imageName=" + imageName)); + assertThat(remoteDockerImage.toString()).contains("imageName=" + imageName); } @Test @@ -28,7 +26,7 @@ public void toStringWithExceptionContainsOnlyImageNameFuture() { imageNameFuture.completeExceptionally(new RuntimeException("arbitrary")); RemoteDockerImage remoteDockerImage = new RemoteDockerImage(imageNameFuture); - assertThat(remoteDockerImage.toString(), containsString("imageName=java.lang.RuntimeException: arbitrary")); + assertThat(remoteDockerImage.toString()).contains("imageName=java.lang.RuntimeException: arbitrary"); } @Test(timeout = 5000L) @@ -36,17 +34,17 @@ public void toStringDoesntResolveImageNameFuture() { CompletableFuture imageNameFuture = new CompletableFuture<>(); // verify that we've set up the test properly - assertFalse(imageNameFuture.isDone()); + assertThat(imageNameFuture).isNotDone(); RemoteDockerImage remoteDockerImage = new RemoteDockerImage(imageNameFuture); - assertThat(remoteDockerImage.toString(), containsString("imageName=")); + assertThat(remoteDockerImage.toString()).contains("imageName="); // Make sure the act of calling toString doesn't resolve the imageNameFuture - assertFalse(imageNameFuture.isDone()); + assertThat(imageNameFuture).isNotDone(); String imageName = Base58.randomString(8).toLowerCase(); imageNameFuture.complete(imageName); - assertThat(remoteDockerImage.toString(), containsString("imageName=" + imageName)); + assertThat(remoteDockerImage.toString()).contains("imageName=" + imageName); } @Test(timeout = 5000L) @@ -62,17 +60,17 @@ protected String resolve() { }; // verify that we've set up the test properly - assertFalse(imageNameFuture.isDone()); + assertThat(imageNameFuture).isNotDone(); RemoteDockerImage remoteDockerImage = new RemoteDockerImage(imageNameFuture); - assertThat(remoteDockerImage.toString(), containsString("imageName=")); + assertThat(remoteDockerImage.toString()).contains("imageName="); // Make sure the act of calling toString doesn't resolve the imageNameFuture - assertFalse(imageNameFuture.isDone()); - assertFalse(resolved.get()); + assertThat(imageNameFuture).isNotDone(); + assertThat(resolved).isFalse(); // Trigger resolve imageNameFuture.get(); - assertThat(remoteDockerImage.toString(), containsString("imageName=" + imageName)); + assertThat(remoteDockerImage.toString()).contains("imageName=" + imageName); } } diff --git a/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java b/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java index 08545535e6b..45feb85b359 100644 --- a/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java @@ -13,8 +13,7 @@ import java.net.Socket; import java.util.concurrent.TimeUnit; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; +import static org.assertj.core.api.Assertions.assertThat; /** * Test of {@link FixedHostPortGenericContainer}. Note that this is not an example of typical use (usually, a container @@ -68,14 +67,12 @@ public void testFixedHostPortMapping() throws IOException { ) { echoServer.start(); - assertThat( - "Port mapping does not seem to match given fixed port", - echoServer.getMappedPort(TEST_PORT), - equalTo(unusedHostPort) - ); + assertThat(echoServer.getMappedPort(TEST_PORT)) + .as("Port mapping does not seem to match given fixed port") + .isEqualTo(unusedHostPort); final String content = this.readResponse(echoServer, unusedHostPort); - assertThat("Returned echo from fixed port does not match expected", content, equalTo(TEST_RESPONSE)); + assertThat(content).as("Returned echo from fixed port does not match expected").isEqualTo(TEST_RESPONSE); } } diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java index 45809f95627..23ad3802c1b 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java @@ -13,7 +13,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; -import static org.junit.Assert.assertTrue; /** * Common test methods for {@link WaitStrategy} implementations. @@ -124,9 +123,8 @@ protected void waitUntilReadyAndSucceed(GenericContainer container) { // start() blocks until successful or timeout container.start(); - assertTrue( - String.format("Expected container to be ready after timeout of %sms", WAIT_TIMEOUT_MILLIS), - ready.get() - ); + assertThat(ready) + .as(String.format("Expected container to be ready after timeout of %sms", WAIT_TIMEOUT_MILLIS)) + .isTrue(); } } diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java index 972bf2ab66a..37bbe985bac 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java @@ -11,8 +11,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; +import static org.assertj.core.api.Assertions.assertThat; /** * Tests for {@link HttpWaitStrategy}. @@ -55,8 +54,8 @@ public void testWaitUntilReadyWithSuccessWithCustomHeaders() { String logs = container.getLogs(); - assertThat(logs, containsString("foo: bar")); - assertThat(logs, containsString("baz: boo")); + assertThat(logs).contains("foo: bar"); + assertThat(logs).contains("baz: boo"); } } diff --git a/core/src/test/java/org/testcontainers/utility/ComparableVersionTest.java b/core/src/test/java/org/testcontainers/utility/ComparableVersionTest.java index fb3c7e05c0b..cd95d8a5f3d 100644 --- a/core/src/test/java/org/testcontainers/utility/ComparableVersionTest.java +++ b/core/src/test/java/org/testcontainers/utility/ComparableVersionTest.java @@ -7,7 +7,7 @@ import java.util.Arrays; -import static org.junit.Assert.assertArrayEquals; +import static org.assertj.core.api.Assertions.assertThat; @RunWith(Parameterized.class) public class ComparableVersionTest { @@ -23,7 +23,7 @@ public ComparableVersionTest(final String given, final int[] expected) { @Test public void shouldParseVersions() { - assertArrayEquals(expected, ComparableVersion.parseVersion(given)); + assertThat(ComparableVersion.parseVersion(given)).containsExactly(expected); } @Parameters(name = "Parsed version: {0}={1}") diff --git a/core/src/test/java/org/testcontainers/utility/DefaultImageNameSubstitutorTest.java b/core/src/test/java/org/testcontainers/utility/DefaultImageNameSubstitutorTest.java index 0978003f815..90f7745edd7 100644 --- a/core/src/test/java/org/testcontainers/utility/DefaultImageNameSubstitutorTest.java +++ b/core/src/test/java/org/testcontainers/utility/DefaultImageNameSubstitutorTest.java @@ -5,8 +5,7 @@ import org.junit.Test; import org.mockito.Mockito; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.eq; public class DefaultImageNameSubstitutorTest { @@ -34,7 +33,7 @@ public void testConfigurationLookup() { final DockerImageName substitute = underTest.apply(ORIGINAL_IMAGE); - assertEquals("match is found", SUBSTITUTE_IMAGE, substitute); - assertTrue("compatibility is automatically set", substitute.isCompatibleWith(ORIGINAL_IMAGE)); + assertThat(substitute).as("match is found").isEqualTo(SUBSTITUTE_IMAGE); + assertThat(substitute.isCompatibleWith(ORIGINAL_IMAGE)).as("compatibility is automatically set").isTrue(); } } diff --git a/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java b/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java index e9aac2ed475..0bb1b3f75ca 100644 --- a/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java +++ b/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java @@ -1,17 +1,12 @@ package org.testcontainers.utility; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.Matchers.containsString; +import static org.assertj.core.api.Assertions.assertThatThrownBy; public class DockerImageNameCompatibilityTest { - @Rule - public ExpectedException thrown = ExpectedException.none(); - @Test public void testPlainImage() { DockerImageName subject = DockerImageName.parse("foo"); @@ -98,10 +93,9 @@ public void testAssertMethodAcceptsCompatible() { @Test public void testAssertMethodRejectsIncompatible() { - thrown.expect(IllegalStateException.class); - thrown.expectMessage(containsString("Failed to verify that image 'foo' is a compatible substitute for 'bar'")); - DockerImageName subject = DockerImageName.parse("foo"); - subject.assertCompatibleWith(DockerImageName.parse("bar")); + assertThatThrownBy(() -> subject.assertCompatibleWith(DockerImageName.parse("bar"))) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("Failed to verify that image 'foo' is a compatible substitute for 'bar'"); } } diff --git a/core/src/test/java/org/testcontainers/utility/DockerStatusTest.java b/core/src/test/java/org/testcontainers/utility/DockerStatusTest.java index 36196aaf4c1..304bbc4190d 100644 --- a/core/src/test/java/org/testcontainers/utility/DockerStatusTest.java +++ b/core/src/test/java/org/testcontainers/utility/DockerStatusTest.java @@ -11,8 +11,7 @@ import java.time.ZoneId; import java.time.format.DateTimeFormatter; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; /** @@ -63,24 +62,24 @@ public static Object[][] parameters() { @Test public void testRunning() throws Exception { - assertTrue(DockerStatus.isContainerRunning(running, minimumDuration, now)); - assertTrue(DockerStatus.isContainerRunning(runningVariant, minimumDuration, now)); - assertFalse(DockerStatus.isContainerRunning(shortRunning, minimumDuration, now)); - assertFalse(DockerStatus.isContainerRunning(created, minimumDuration, now)); - assertFalse(DockerStatus.isContainerRunning(createdVariant, minimumDuration, now)); - assertFalse(DockerStatus.isContainerRunning(exited, minimumDuration, now)); - assertFalse(DockerStatus.isContainerRunning(paused, minimumDuration, now)); + assertThat(DockerStatus.isContainerRunning(running, minimumDuration, now)).isTrue(); + assertThat(DockerStatus.isContainerRunning(runningVariant, minimumDuration, now)).isTrue(); + assertThat(DockerStatus.isContainerRunning(shortRunning, minimumDuration, now)).isFalse(); + assertThat(DockerStatus.isContainerRunning(created, minimumDuration, now)).isFalse(); + assertThat(DockerStatus.isContainerRunning(createdVariant, minimumDuration, now)).isFalse(); + assertThat(DockerStatus.isContainerRunning(exited, minimumDuration, now)).isFalse(); + assertThat(DockerStatus.isContainerRunning(paused, minimumDuration, now)).isFalse(); } @Test public void testStopped() throws Exception { - assertFalse(DockerStatus.isContainerStopped(running)); - assertFalse(DockerStatus.isContainerStopped(runningVariant)); - assertFalse(DockerStatus.isContainerStopped(shortRunning)); - assertFalse(DockerStatus.isContainerStopped(created)); - assertFalse(DockerStatus.isContainerStopped(createdVariant)); - assertTrue(DockerStatus.isContainerStopped(exited)); - assertFalse(DockerStatus.isContainerStopped(paused)); + assertThat(DockerStatus.isContainerStopped(running)).isFalse(); + assertThat(DockerStatus.isContainerStopped(runningVariant)).isFalse(); + assertThat(DockerStatus.isContainerStopped(shortRunning)).isFalse(); + assertThat(DockerStatus.isContainerStopped(created)).isFalse(); + assertThat(DockerStatus.isContainerStopped(createdVariant)).isFalse(); + assertThat(DockerStatus.isContainerStopped(exited)).isTrue(); + assertThat(DockerStatus.isContainerStopped(paused)).isFalse(); } private String buildTimestamp(Instant instant) { diff --git a/core/src/test/java/org/testcontainers/utility/FilterRegistryTest.java b/core/src/test/java/org/testcontainers/utility/FilterRegistryTest.java index 30367975d1e..2741edb5cee 100644 --- a/core/src/test/java/org/testcontainers/utility/FilterRegistryTest.java +++ b/core/src/test/java/org/testcontainers/utility/FilterRegistryTest.java @@ -13,9 +13,7 @@ import java.util.List; import java.util.Map.Entry; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class FilterRegistryTest { @@ -38,7 +36,7 @@ public void registerReturnsTrueIfAcknowledgementIsReadFromInputStream() throws I boolean successful = registry.register(FILTERS); - assertTrue(successful); + assertThat(successful).isTrue(); } @Test @@ -47,7 +45,7 @@ public void registerReturnsFalseIfNoAcknowledgementIsReadFromInputStream() throw boolean successful = registry.register(FILTERS); - assertFalse(successful); + assertThat(successful).isFalse(); } @Test @@ -57,7 +55,7 @@ public void registerWritesUrlEncodedFiltersAndNewlineToOutputStream() throws IOE registry.register(FILTERS); - assertEquals(URL_ENCODED_FILTERS + NEW_LINE, new String(outputStream.toByteArray())); + assertThat(new String(outputStream.toByteArray())).isEqualTo(URL_ENCODED_FILTERS + NEW_LINE); } private static InputStream inputStream(byte[] bytes) { diff --git a/core/src/test/java/org/testcontainers/utility/ResourceReaperTest.java b/core/src/test/java/org/testcontainers/utility/ResourceReaperTest.java index 60f57a28b3e..77c5d528ba4 100644 --- a/core/src/test/java/org/testcontainers/utility/ResourceReaperTest.java +++ b/core/src/test/java/org/testcontainers/utility/ResourceReaperTest.java @@ -20,7 +20,6 @@ import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertEquals; public class ResourceReaperTest { @@ -81,7 +80,7 @@ private Map runProcess(Consumer processExecutor processExecutorConsumer.accept(processExecutor); ProcessResult result = processExecutor.execute(); - assertEquals(0, result.getExitValue()); + assertThat(result.getExitValue()).isEqualTo(0); String labelsJson = Stream .of(result.outputUTF8().split("\n")) diff --git a/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java b/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java index 218ed90e5e0..a32f23ca8c2 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java @@ -12,7 +12,7 @@ import java.io.IOException; import java.util.Objects; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class CmdModifierTest { @@ -43,13 +43,13 @@ public class CmdModifierTest { @Test public void testHostnameModified() throws IOException, InterruptedException { final Container.ExecResult execResult = theCache.execInContainer("hostname"); - assertEquals("the-cache", execResult.getStdout().trim()); + assertThat(execResult.getStdout().trim()).isEqualTo("the-cache"); } @Test public void testMemoryLimitModified() throws IOException, InterruptedException { final Container.ExecResult execResult = memoryLimitedRedis.execInContainer("cat", getMemoryLimitFilePath()); - assertEquals(String.valueOf(memoryInBytes), execResult.getStdout().trim()); + assertThat(execResult.getStdout().trim()).isEqualTo(String.valueOf(memoryInBytes)); } private String getMemoryLimitFilePath() { diff --git a/docs/examples/junit4/generic/src/test/java/generic/CommandsTest.java b/docs/examples/junit4/generic/src/test/java/generic/CommandsTest.java index 80292465521..3cab77770dc 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/CommandsTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/CommandsTest.java @@ -5,7 +5,7 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.utility.DockerImageName; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class CommandsTest { @@ -18,6 +18,6 @@ public class CommandsTest { @Test public void testStartupCommandOverrideApplied() { - assertTrue(redisWithCustomPort.isRunning()); // good enough to check that the container started listening + assertThat(redisWithCustomPort.isRunning()).isTrue(); // good enough to check that the container started listening } } diff --git a/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java b/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java index 0f6269968dd..d98e09ff036 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java @@ -5,7 +5,7 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.utility.DockerImageName; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class ContainerCreationTest { @@ -39,7 +39,7 @@ public class ContainerCreationTest { @Test public void testStartup() { - assertTrue(redis.isRunning()); // good enough to check that the container started listening - assertTrue(alpine.isRunning()); // good enough to check that the container started listening + assertThat(redis.isRunning()).isTrue(); // good enough to check that the container started listening + assertThat(alpine.isRunning()).isTrue(); // good enough to check that the container started listening } } diff --git a/docs/examples/junit4/generic/src/test/java/generic/DependsOnTest.java b/docs/examples/junit4/generic/src/test/java/generic/DependsOnTest.java index 6534a35a00f..7116f375283 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/DependsOnTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/DependsOnTest.java @@ -4,7 +4,7 @@ import org.junit.Test; import org.testcontainers.containers.GenericContainer; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class DependsOnTest { @@ -19,7 +19,7 @@ public class DependsOnTest { @Test public void testContainersAllStarted() { - assertTrue(redis.isRunning()); - assertTrue(nginx.isRunning()); + assertThat(redis.isRunning()).isTrue(); + assertThat(nginx.isRunning()).isTrue(); } } diff --git a/docs/examples/junit4/generic/src/test/java/generic/ExecTest.java b/docs/examples/junit4/generic/src/test/java/generic/ExecTest.java index 7548ae4a613..7ccb0f2cc54 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/ExecTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/ExecTest.java @@ -8,7 +8,7 @@ import java.io.IOException; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class ExecTest { @@ -26,8 +26,8 @@ public void testSimpleExec() throws IOException, InterruptedException { Container.ExecResult lsResult = container.execInContainer("ls", "-al", "/"); String stdout = lsResult.getStdout(); int exitCode = lsResult.getExitCode(); - assertTrue(stdout.contains("somefile.txt")); - assertTrue(exitCode == 0); + assertThat(stdout).contains("somefile.txt"); + assertThat(exitCode).isZero(); // } } } diff --git a/docs/examples/junit4/generic/src/test/java/generic/HostPortExposedTest.java b/docs/examples/junit4/generic/src/test/java/generic/HostPortExposedTest.java index 22bf0332a9d..3e80133411a 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/HostPortExposedTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/HostPortExposedTest.java @@ -13,7 +13,7 @@ import java.io.OutputStream; import java.net.InetSocketAddress; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class HostPortExposedTest { @@ -62,6 +62,6 @@ public void testContainerRunningAgainstExposedHostPort() { // } final String pageSource = webDriver.getPageSource(); - assertTrue(pageSource.contains("Hello World!")); + assertThat(pageSource).contains("Hello World!"); } } diff --git a/docs/examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java b/docs/examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java index bdc0d3ced8a..0b2b166d1d9 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java @@ -8,7 +8,7 @@ import org.testcontainers.containers.wait.strategy.WaitStrategy; import org.testcontainers.utility.DockerImageName; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class WaitStrategiesTest { @@ -69,8 +69,8 @@ public class WaitStrategiesTest { @Test public void testContainersAllStarted() { - assertTrue(nginx.isRunning()); - assertTrue(nginxWithHttpWait.isRunning()); - assertTrue(containerWithLogWait.isRunning()); + assertThat(nginx.isRunning()).isTrue(); + assertThat(nginxWithHttpWait.isRunning()).isTrue(); + assertThat(containerWithLogWait.isRunning()).isTrue(); } } diff --git a/docs/examples/junit4/redis/build.gradle b/docs/examples/junit4/redis/build.gradle index ea36761b716..7ab7cddc88f 100644 --- a/docs/examples/junit4/redis/build.gradle +++ b/docs/examples/junit4/redis/build.gradle @@ -5,4 +5,5 @@ dependencies { testImplementation "junit:junit:4.13.2" testImplementation project(":testcontainers") + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java b/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java index 1d3d178975f..0f219397bdc 100644 --- a/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java +++ b/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java @@ -6,7 +6,7 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.utility.DockerImageName; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class RedisBackedCacheIntTest { @@ -33,6 +33,6 @@ public void testSimplePutAndGet() { underTest.put("test", "example"); String retrieved = underTest.get("test"); - assertEquals("example", retrieved); + assertThat(retrieved).isEqualTo("example"); } } diff --git a/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java b/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java index f837b3af0d5..e3f52403c48 100644 --- a/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java +++ b/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java @@ -4,7 +4,7 @@ import org.junit.Ignore; import org.junit.Test; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; @Ignore("This test class is deliberately invalid, as it relies on a non-existent local Redis") public class RedisBackedCacheIntTestStep0 { @@ -22,6 +22,6 @@ public void testSimplePutAndGet() { underTest.put("test", "example"); String retrieved = underTest.get("test"); - assertEquals("example", retrieved); + assertThat(retrieved).isEqualTo("example"); } } diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index ab32d55df91..1865f70f801 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -12,5 +12,6 @@ dependencies { testImplementation 'org.postgresql:postgresql:42.4.1' testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.testcontainers:postgresql' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java b/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java index b84b2a9e2c9..6e4186698e7 100644 --- a/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java +++ b/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java @@ -5,7 +5,7 @@ import org.junit.rules.RuleChain; import org.testcontainers.containers.PostgreSQLContainer; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; /** * Tests for RedmineClient. @@ -33,9 +33,6 @@ public void canGetIssueCount() throws Exception { RedmineClient redmineClient = new RedmineClient( redmineContainer.getRedmineUrl()); - assertEquals( - "The issue count can be retrieved.", - 0, - redmineClient.getIssueCount()); + assertThat(redmineClient.getIssueCount()).as("The issue count can be retrieved.").isZero(); } } diff --git a/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java b/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java index d151e1a3f24..bc7aeed10bd 100644 --- a/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java +++ b/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java @@ -20,7 +20,7 @@ import org.testcontainers.utility.DockerImageName; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.fail; +import static org.assertj.core.api.Assertions.fail; // junitExample { @Testcontainers diff --git a/modules/cassandra/build.gradle b/modules/cassandra/build.gradle index 385001db8f8..ff110637ced 100644 --- a/modules/cassandra/build.gradle +++ b/modules/cassandra/build.gradle @@ -9,5 +9,7 @@ configurations.all { dependencies { api project(":database-commons") api "com.datastax.cassandra:cassandra-driver-core:3.10.0" + testImplementation 'com.datastax.oss:java-driver-core:4.14.1' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java index d1ad581bcc0..d8f21fc3068 100644 --- a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java +++ b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java @@ -9,9 +9,7 @@ import org.testcontainers.containers.wait.CassandraQueryWaitStrategy; import org.testcontainers.utility.DockerImageName; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * @author Eugeny Karpov @@ -30,8 +28,8 @@ public void testSimple() { try (CassandraContainer cassandraContainer = new CassandraContainer<>(CASSANDRA_IMAGE)) { cassandraContainer.start(); ResultSet resultSet = performQuery(cassandraContainer, BASIC_QUERY); - assertTrue("Query was not applied", resultSet.wasApplied()); - assertNotNull("Result set has no release_version", resultSet.one().getString(0)); + assertThat(resultSet.wasApplied()).as("Query was applied").isTrue(); + assertThat(resultSet.one().getString(0)).as("Result set has release_version").isNotNull(); } } @@ -45,8 +43,8 @@ public void testSpecificVersion() { ) { cassandraContainer.start(); ResultSet resultSet = performQuery(cassandraContainer, BASIC_QUERY); - assertTrue("Query was not applied", resultSet.wasApplied()); - assertEquals("Cassandra has wrong version", cassandraVersion, resultSet.one().getString(0)); + assertThat(resultSet.wasApplied()).as("Query was applied").isTrue(); + assertThat(resultSet.one().getString(0)).as("Cassandra has right version").isEqualTo(cassandraVersion); } } @@ -58,12 +56,10 @@ public void testConfigurationOverride() { ) { cassandraContainer.start(); ResultSet resultSet = performQuery(cassandraContainer, "SELECT cluster_name FROM system.local"); - assertTrue("Query was not applied", resultSet.wasApplied()); - assertEquals( - "Cassandra configuration is not overridden", - TEST_CLUSTER_NAME_IN_CONF, - resultSet.one().getString(0) - ); + assertThat(resultSet.wasApplied()).as("Query was applied").isTrue(); + assertThat(resultSet.one().getString(0)) + .as("Cassandra configuration is overridden") + .isEqualTo(TEST_CLUSTER_NAME_IN_CONF); } } @@ -110,7 +106,7 @@ public void testCassandraQueryWaitStrategy() { ) { cassandraContainer.start(); ResultSet resultSet = performQuery(cassandraContainer, BASIC_QUERY); - assertTrue("Query was not applied", resultSet.wasApplied()); + assertThat(resultSet.wasApplied()).as("Query was applied").isTrue(); } } @@ -120,17 +116,17 @@ public void testCassandraGetCluster() { try (CassandraContainer cassandraContainer = new CassandraContainer<>()) { cassandraContainer.start(); ResultSet resultSet = performQuery(cassandraContainer.getCluster(), BASIC_QUERY); - assertTrue("Query was not applied", resultSet.wasApplied()); - assertNotNull("Result set has no release_version", resultSet.one().getString(0)); + assertThat(resultSet.wasApplied()).as("Query was applied").isTrue(); + assertThat(resultSet.one().getString(0)).as("Result set has release_version").isNotNull(); } } private void testInitScript(CassandraContainer cassandraContainer) { ResultSet resultSet = performQuery(cassandraContainer, "SELECT * FROM keySpaceTest.catalog_category"); - assertTrue("Query was not applied", resultSet.wasApplied()); + assertThat(resultSet.wasApplied()).as("Query was applied").isTrue(); Row row = resultSet.one(); - assertEquals("Inserted row is not in expected state", 1, row.getLong(0)); - assertEquals("Inserted row is not in expected state", "test_category", row.getString(1)); + assertThat(row.getLong(0)).as("Inserted row is in expected state").isEqualTo(1); + assertThat(row.getString(1)).as("Inserted row is in expected state").isEqualTo("test_category"); } private ResultSet performQuery(CassandraContainer cassandraContainer, String cql) { diff --git a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver3Test.java b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver3Test.java index 78f3fd2a664..502f2ca8e67 100644 --- a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver3Test.java +++ b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver3Test.java @@ -6,7 +6,7 @@ import org.junit.Rule; import org.junit.Test; -import static org.junit.Assert.assertNotNull; +import static org.assertj.core.api.Assertions.assertThat; public class CassandraDriver3Test { @@ -31,7 +31,7 @@ public void testCassandraGetContactPoint() { KeyspaceMetadata keyspace = session.getMetadata().getKeyspaces().get(CqlIdentifier.fromCql("test")); - assertNotNull("Failed to create test keyspace", keyspace); + assertThat(keyspace).as("keyspace created").isNotNull(); } } } diff --git a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver4Test.java b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver4Test.java index 3a05bc723bc..a4fb8c87b8e 100644 --- a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver4Test.java +++ b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver4Test.java @@ -6,7 +6,7 @@ import org.junit.Rule; import org.junit.Test; -import static org.junit.Assert.assertNotNull; +import static org.assertj.core.api.Assertions.assertThat; public class CassandraDriver4Test { @@ -29,7 +29,7 @@ public void testCassandraGetContactPoint() { KeyspaceMetadata keyspace = session.getMetadata().getKeyspaces().get(CqlIdentifier.fromCql("test")); - assertNotNull("Failed to create test keyspace", keyspace); + assertThat(keyspace).as("test keyspace created").isNotNull(); } } } diff --git a/modules/consul/src/test/java/org/testcontainers/consul/ConsulContainerTest.java b/modules/consul/src/test/java/org/testcontainers/consul/ConsulContainerTest.java index e29ff7ffbd8..c0c022d31bb 100644 --- a/modules/consul/src/test/java/org/testcontainers/consul/ConsulContainerTest.java +++ b/modules/consul/src/test/java/org/testcontainers/consul/ConsulContainerTest.java @@ -4,9 +4,6 @@ import com.ecwid.consul.v1.Response; import com.ecwid.consul.v1.kv.model.GetValue; import io.restassured.RestAssured; -import org.assertj.core.api.Assertions; -import org.hamcrest.CoreMatchers; -import org.hamcrest.MatcherAssert; import org.junit.ClassRule; import org.junit.Test; import org.testcontainers.containers.GenericContainer; @@ -17,6 +14,8 @@ import java.util.HashMap; import java.util.Map; +import static org.assertj.core.api.Assertions.assertThat; + /** * This test shows the pattern to use the ConsulContainer @ClassRule for a junit test. It also has tests that ensure * the properties were added correctly by reading from Consul with the CLI and over HTTP. @@ -31,21 +30,19 @@ public class ConsulContainerTest { public void readFirstPropertyPathWithCli() throws IOException, InterruptedException { GenericContainer.ExecResult result = consulContainer.execInContainer("consul", "kv", "get", "config/testing1"); final String output = result.getStdout().replaceAll("\\r?\\n", ""); - MatcherAssert.assertThat(output, CoreMatchers.containsString("value123")); + assertThat(output).contains("value123"); } @Test - public void readFirstSecretPathOverHttpApi() throws InterruptedException { - RestAssured + public void readFirstSecretPathOverHttpApi() { + io.restassured.response.Response response = RestAssured .given() .when() .get("http://" + getHostAndPort() + "/v1/kv/config/testing1") - .then() - .assertThat() - .body( - "[0].Value", - CoreMatchers.equalTo(Base64.getEncoder().encodeToString("value123".getBytes(StandardCharsets.UTF_8))) - ); + .andReturn(); + + assertThat(response.body().jsonPath().getString("[0].Value")) + .isEqualTo(Base64.getEncoder().encodeToString("value123".getBytes(StandardCharsets.UTF_8))); } @Test @@ -62,13 +59,13 @@ public void writeAndReadMultipleValuesUsingClient() { // Write operation properties.forEach((key, value) -> { Response writeResponse = consulClient.setKVValue(key, value); - Assertions.assertThat(writeResponse.getValue()).isTrue(); + assertThat(writeResponse.getValue()).isTrue(); }); // Read operation properties.forEach((key, value) -> { Response readResponse = consulClient.getKVValue(key); - Assertions.assertThat(readResponse.getValue().getDecodedValue()).isEqualTo(value); + assertThat(readResponse.getValue().getDecodedValue()).isEqualTo(value); }); } diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 10970df542e..6cdacbcf77a 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -7,4 +7,5 @@ dependencies { testImplementation 'com.couchbase.client:java-client:3.3.3' testImplementation 'org.awaitility:awaitility:4.2.0' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java index 1bdbe2ab1db..691dc1307ef 100644 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java @@ -27,10 +27,9 @@ import java.time.Duration; import java.util.function.Consumer; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.awaitility.Awaitility.await; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThrows; public class CouchbaseContainerTest { @@ -66,7 +65,7 @@ public void testBasicContainerUsageForEnterpriseContainer() { JsonObject fooObject = collection.get("foo").contentAsObject(); - assertEquals("value", fooObject.getString("key")); + assertThat(fooObject.getString("key")).isEqualTo("value"); } ); } @@ -92,7 +91,7 @@ public void testBasicContainerUsageForCommunityContainer() { JsonObject fooObject = collection.get("foo").contentAsObject(); - assertEquals("value", fooObject.getString("key")); + assertThat(fooObject.getString("key")).isEqualTo("value"); } ); } @@ -118,7 +117,7 @@ public void testBucketIsFlushableIfEnabled() { cluster.buckets().flushBucket(bucketDefinition.getName()); - await().untilAsserted(() -> assertFalse(collection.exists("foo").exists())); + await().untilAsserted(() -> assertThat(collection.exists("foo").exists()).isFalse()); } ); } @@ -134,12 +133,10 @@ public void testFailureIfCommunityUsedWithAnalytics() { CouchbaseContainer container = new CouchbaseContainer(COUCHBASE_IMAGE_COMMUNITY) .withEnabledServices(CouchbaseService.KV, CouchbaseService.ANALYTICS) ) { - assertThrows( - ContainerLaunchException.class, - () -> { + assertThatThrownBy(() -> { setUpClient(container, cluster -> {}); - } - ); + }) + .isInstanceOf(ContainerLaunchException.class); } } @@ -153,12 +150,10 @@ public void testFailureIfCommunityUsedWithEventing() { CouchbaseContainer container = new CouchbaseContainer(COUCHBASE_IMAGE_COMMUNITY) .withEnabledServices(CouchbaseService.KV, CouchbaseService.EVENTING) ) { - assertThrows( - ContainerLaunchException.class, - () -> { + assertThatThrownBy(() -> { setUpClient(container, cluster -> {}); - } - ); + }) + .isInstanceOf(ContainerLaunchException.class); } } diff --git a/modules/influxdb/build.gradle b/modules/influxdb/build.gradle index a7ff4e66729..2a75c343653 100644 --- a/modules/influxdb/build.gradle +++ b/modules/influxdb/build.gradle @@ -5,4 +5,5 @@ dependencies { compileOnly 'org.influxdb:influxdb-java:2.23' testImplementation 'org.influxdb:influxdb-java:2.23' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerTest.java b/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerTest.java index c7fc71a3a19..bcf3d21f25b 100644 --- a/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerTest.java +++ b/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerTest.java @@ -4,9 +4,7 @@ import org.junit.ClassRule; import org.junit.Test; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; -import static org.junit.Assert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; public class InfluxDBContainerTest { @@ -19,28 +17,28 @@ public class InfluxDBContainerTest { public void getUrl() { String actual = influxDBContainer.getUrl(); - assertThat(actual, notNullValue()); + assertThat(actual).isNotNull(); } @Test public void getNewInfluxDB() { InfluxDB actual = influxDBContainer.getNewInfluxDB(); - assertThat(actual, notNullValue()); - assertThat(actual.ping(), notNullValue()); + assertThat(actual).isNotNull(); + assertThat(actual.ping()).isNotNull(); } @Test public void getLivenessCheckPort() { Integer actual = influxDBContainer.getLivenessCheckPort(); - assertThat(actual, notNullValue()); + assertThat(actual).isNotNull(); } @Test public void isRunning() { boolean actual = influxDBContainer.isRunning(); - assertThat(actual, is(true)); + assertThat(actual).isTrue(); } } diff --git a/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerWithUserTest.java b/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerWithUserTest.java index a7ae347e7cc..7a34bbb4fd6 100644 --- a/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerWithUserTest.java +++ b/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerWithUserTest.java @@ -9,11 +9,7 @@ import java.util.concurrent.TimeUnit; -import static org.hamcrest.Matchers.hasItem; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; public class InfluxDBContainerWithUserTest { @@ -35,20 +31,20 @@ public class InfluxDBContainerWithUserTest { public void describeDatabases() { InfluxDB actual = influxDBContainer.getNewInfluxDB(); - assertThat(actual, notNullValue()); - assertThat(actual.describeDatabases(), hasItem(DATABASE)); + assertThat(actual).isNotNull(); + assertThat(actual.describeDatabases()).contains(DATABASE); } @Test public void checkVersion() { InfluxDB actual = influxDBContainer.getNewInfluxDB(); - assertThat(actual, notNullValue()); + assertThat(actual).isNotNull(); - assertThat(actual.ping(), notNullValue()); - assertThat(actual.ping().getVersion(), is(TEST_VERSION)); + assertThat(actual.ping()).isNotNull(); + assertThat(actual.ping().getVersion()).isEqualTo(TEST_VERSION); - assertThat(actual.version(), is(TEST_VERSION)); + assertThat(actual.version()).isEqualTo(TEST_VERSION); } @Test @@ -67,9 +63,9 @@ public void queryForWriteAndRead() { Query query = new Query("SELECT idle FROM cpu", DATABASE); QueryResult actual = influxDB.query(query); - assertThat(actual, notNullValue()); - assertThat(actual.getError(), nullValue()); - assertThat(actual.getResults(), notNullValue()); - assertThat(actual.getResults().size(), is(1)); + assertThat(actual).isNotNull(); + assertThat(actual.getError()).isNull(); + assertThat(actual.getResults()).isNotNull(); + assertThat(actual.getResults()).hasSize(1); } } diff --git a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ContainerDatabaseDriverTest.java b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ContainerDatabaseDriverTest.java index 5b4d223cd36..4edd5d449ba 100644 --- a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ContainerDatabaseDriverTest.java +++ b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ContainerDatabaseDriverTest.java @@ -1,7 +1,6 @@ package org.testcontainers.jdbc; import org.hamcrest.CoreMatchers; -import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -11,6 +10,8 @@ import java.sql.SQLException; import java.util.Properties; +import static org.assertj.core.api.Assertions.assertThat; + public class ContainerDatabaseDriverTest { private static final String PLAIN_POSTGRESQL_JDBC_URL = "jdbc:postgresql://localhost:5432/test"; @@ -22,7 +23,7 @@ public class ContainerDatabaseDriverTest { public void shouldNotTryToConnectToNonMatchingJdbcUrlDirectly() throws SQLException { ContainerDatabaseDriver driver = new ContainerDatabaseDriver(); Connection connection = driver.connect(PLAIN_POSTGRESQL_JDBC_URL, new Properties()); - Assert.assertNull(connection); + assertThat(connection).isNull(); } @Test diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/PostgresContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/PostgresContainerTests.java index 186d4095b0d..ee6d1f1a841 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/PostgresContainerTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/PostgresContainerTests.java @@ -8,7 +8,7 @@ import java.sql.ResultSet; import java.sql.Statement; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; @Testcontainers class PostgresContainerTests { @@ -35,7 +35,7 @@ void waits_until_postgres_accepts_jdbc_connections() throws Exception { resultSet.next(); int resultSetInt = resultSet.getInt(1); - assertEquals(1, resultSetInt); + assertThat(resultSetInt).isEqualTo(1); } } } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareExceptionCapturingTest.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareExceptionCapturingTest.java index 09b816be56e..b64f2f3c328 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareExceptionCapturingTest.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareExceptionCapturingTest.java @@ -6,8 +6,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assume.assumeTrue; // The order of @ExtendsWith and @Testcontainers is crucial in order for the tests @@ -32,7 +31,7 @@ void failing_test_should_pass_throwable_to_testContainer() { @Order(2) void should_have_captured_thrownException() { Throwable capturedThrowable = startedTestContainer.getCapturedThrowable(); - assertTrue(capturedThrowable instanceof AssumptionViolatedException); - assertEquals("got: , expected: is ", capturedThrowable.getMessage()); + assertThat(capturedThrowable).isInstanceOf(AssumptionViolatedException.class); + assertThat(capturedThrowable.getMessage()).isEqualTo("got: , expected: is "); } } diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java index a678d30eb80..b879b27f0e6 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java @@ -22,7 +22,6 @@ import com.amazonaws.services.sqs.model.CreateQueueResult; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; -import org.junit.Assert; import org.junit.ClassRule; import org.junit.Test; import org.junit.experimental.runners.Enclosed; @@ -320,7 +319,7 @@ private String runAwsCliAgainstDockerNetworkContainer(String command) throws Exc ) .split(" "); final Container.ExecResult execResult = awsCliInDockerNetwork.execInContainer(commandParts); - Assert.assertEquals(0, execResult.getExitCode()); + assertThat(execResult.getExitCode()).isEqualTo(0); final String logs = execResult.getStdout() + execResult.getStderr(); log.info(logs); diff --git a/modules/mongodb/build.gradle b/modules/mongodb/build.gradle index 7f9725bf0e8..6e4b5208298 100644 --- a/modules/mongodb/build.gradle +++ b/modules/mongodb/build.gradle @@ -4,4 +4,5 @@ dependencies { api project(':testcontainers') testImplementation("org.mongodb:mongodb-driver-sync:4.7.1") + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java index 6c98978f20a..ff6d0d6ccba 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java @@ -13,10 +13,7 @@ import org.junit.Test; import org.testcontainers.utility.DockerImageName; -import static org.hamcrest.Matchers.endsWith; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; public class MongoDBContainerTest { @@ -35,7 +32,7 @@ public void shouldExecuteTransactions() { // } final String mongoRsUrl = mongoDBContainer.getReplicaSetUrl(); - assertNotNull(mongoRsUrl); + assertThat(mongoRsUrl).isNotNull(); final String connectionString = mongoDBContainer.getConnectionString(); final MongoClient mongoSyncClientBase = MongoClients.create(connectionString); final MongoClient mongoSyncClient = MongoClients.create(mongoRsUrl); @@ -76,7 +73,7 @@ public void shouldExecuteTransactions() { try { final String trxResultActual = clientSession.withTransaction(txnBody, txnOptions); - assertEquals(trxResult, trxResultActual); + assertThat(trxResultActual).isEqualTo(trxResult); } catch (RuntimeException re) { throw new IllegalStateException(re.getMessage(), re); } finally { @@ -98,7 +95,7 @@ public void shouldTestDatabaseName() { try (final MongoDBContainer mongoDBContainer = new MongoDBContainer(DockerImageName.parse("mongo:4.0.10"))) { mongoDBContainer.start(); final String databaseName = "my-db"; - assertThat(mongoDBContainer.getReplicaSetUrl(databaseName), endsWith(databaseName)); + assertThat(mongoDBContainer.getReplicaSetUrl(databaseName)).endsWith(databaseName); } } } diff --git a/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java b/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java index b48bd67efc9..b7afba041de 100644 --- a/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java +++ b/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java @@ -8,8 +8,8 @@ import java.sql.ResultSet; import java.sql.SQLException; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; public class SimpleOracleTest extends AbstractContainerDatabaseTest { @@ -20,15 +20,15 @@ public class SimpleOracleTest extends AbstractContainerDatabaseTest { private void runTest(OracleContainer container, String databaseName, String username, String password) throws SQLException { //Test config was honored - assertEquals(databaseName, container.getDatabaseName()); - assertEquals(username, container.getUsername()); - assertEquals(password, container.getPassword()); + assertThat(container.getDatabaseName()).isEqualTo(databaseName); + assertThat(container.getUsername()).isEqualTo(username); + assertThat(container.getPassword()).isEqualTo(password); //Test we can get a connection container.start(); ResultSet resultSet = performQuery(container, "SELECT 1 FROM dual"); int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); } @Test @@ -38,7 +38,7 @@ public void testDefaultSettings() throws SQLException { // Match against the last '/' String urlSuffix = oracle.getJdbcUrl().split("(\\/)(?!.*\\/)", 2)[1]; - assertEquals("xepdb1", urlSuffix); + assertThat(urlSuffix).isEqualTo("xepdb1"); } } @@ -81,7 +81,7 @@ public void testSID() throws SQLException { // Match against the last ':' String urlSuffix = oracle.getJdbcUrl().split("(\\:)(?!.*\\:)", 2)[1]; - assertEquals("xe", urlSuffix); + assertThat(urlSuffix).isEqualTo("xe"); } } diff --git a/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java b/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java index a54108ea1a2..721186807aa 100644 --- a/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java +++ b/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java @@ -1,6 +1,5 @@ package org.testcontainers.containers; -import org.junit.Assert; import org.junit.Test; import org.testcontainers.PrestoTestImages; @@ -13,9 +12,7 @@ import java.util.Arrays; import java.util.List; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * @author findepi @@ -31,8 +28,10 @@ public void testSimple() throws Exception { Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes") ) { - assertTrue("No result", resultSet.next()); - assertEquals("Presto version", PrestoContainer.DEFAULT_TAG, resultSet.getString("node_version")); + assertThat(resultSet.next()).as("has result").isTrue(); + assertThat(resultSet.getString("node_version")) + .as("Presto version") + .isEqualTo(PrestoContainer.DEFAULT_TAG); } } } @@ -48,12 +47,10 @@ public void testSpecificVersion() throws Exception { Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes") ) { - assertTrue("No result", resultSet.next()); - assertEquals( - "Presto version", - PrestoTestImages.PRESTO_PREVIOUS_VERSION_TEST_IMAGE.getVersionPart(), - resultSet.getString("node_version") - ); + assertThat(resultSet.next()).as("has result").isTrue(); + assertThat(resultSet.getString("node_version")) + .as("Presto version") + .isEqualTo(PrestoTestImages.PRESTO_PREVIOUS_VERSION_TEST_IMAGE.getVersionPart()); } } } @@ -86,7 +83,7 @@ public void testQueryMemoryAndTpch() throws SQLException { while (resultSet.next()) { actualElements.add(resultSet.getInt("element")); } - Assert.assertEquals(Arrays.asList(2, 4, 42, 42, 42), actualElements); + assertThat(actualElements).isEqualTo(Arrays.asList(2, 4, 42, 42, 42)); } } } @@ -102,9 +99,9 @@ public void testInitScript() throws Exception { Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("SELECT a FROM memory.default.test_table") ) { - assertTrue("No result", resultSet.next()); - assertEquals("Value", 12345678909324L, resultSet.getObject("a")); - assertFalse("Too many result", resultSet.next()); + assertThat(resultSet.next()).as("has result").isTrue(); + assertThat(resultSet.getObject("a")).as("Value").isEqualTo(12345678909324L); + assertThat(resultSet.next()).as("only has one result").isFalse(); } } } @@ -117,47 +114,39 @@ public void testTcJdbcUri() throws Exception { ) ) { // Verify metadata with tc: JDBC connection URI - assertEquals( - connection.getMetaData().getDatabaseMajorVersion(), - Integer.parseInt(PrestoContainer.DEFAULT_TAG) - ); + assertThat(Integer.parseInt(PrestoContainer.DEFAULT_TAG)) + .isEqualTo(connection.getMetaData().getDatabaseMajorVersion()); // Verify transactions with tc: JDBC connection URI - assertTrue("Is autocommit", connection.getAutoCommit()); + assertThat(connection.getAutoCommit()).as("Is autocommit").isTrue(); connection.setAutoCommit(false); - assertFalse("Is autocommit", connection.getAutoCommit()); - assertEquals( - "Transaction isolation", - Connection.TRANSACTION_READ_UNCOMMITTED, - connection.getTransactionIsolation() - ); + assertThat(connection.getAutoCommit()).as("Is autocommit").isFalse(); + assertThat(connection.getTransactionIsolation()) + .as("Transaction isolation") + .isEqualTo(Connection.TRANSACTION_READ_UNCOMMITTED); try (Statement statement = connection.createStatement()) { - assertEquals( - "Update result", - 0, - statement.executeUpdate("CREATE TABLE memory.default.test_tc(a bigint)") - ); + assertThat(statement.executeUpdate("CREATE TABLE memory.default.test_tc(a bigint)")) + .as("Update result") + .isEqualTo(0); try ( ResultSet resultSet = statement.executeQuery( "SELECT sum(cast(node_version AS bigint)) AS v FROM system.runtime.nodes" ) ) { - assertTrue(resultSet.next()); - assertEquals(PrestoContainer.DEFAULT_TAG, resultSet.getString("v")); - assertFalse(resultSet.next()); + assertThat(resultSet.next()).isTrue(); + assertThat(resultSet.getString("v")).isEqualTo(PrestoContainer.DEFAULT_TAG); + assertThat(resultSet.next()).isFalse(); } connection.commit(); } finally { connection.rollback(); } connection.setAutoCommit(true); - assertTrue("Is autocommit", connection.getAutoCommit()); - assertEquals( - "Transaction isolation should be retained", - Connection.TRANSACTION_READ_UNCOMMITTED, - connection.getTransactionIsolation() - ); + assertThat(connection.getAutoCommit()).as("Is autocommit").isTrue(); + assertThat(connection.getTransactionIsolation()) + .as("Transaction isolation should be retained") + .isEqualTo(Connection.TRANSACTION_READ_UNCOMMITTED); } } } diff --git a/modules/selenium/src/test/java/org/testcontainers/containers/DefaultRecordingFileFactoryTest.java b/modules/selenium/src/test/java/org/testcontainers/containers/DefaultRecordingFileFactoryTest.java index 0d8daaad219..aa894810930 100644 --- a/modules/selenium/src/test/java/org/testcontainers/containers/DefaultRecordingFileFactoryTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/containers/DefaultRecordingFileFactoryTest.java @@ -15,8 +15,7 @@ import java.util.Collection; import java.util.List; -import static org.hamcrest.Matchers.hasItem; -import static org.junit.Assert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; @RunWith(Parameterized.class) @Value @@ -68,6 +67,6 @@ public void recordingFileThatShouldDescribeTheTestResultAtThePresentTime() throw ) ); - assertThat(expectedPossibleFileNames, hasItem(recordingFile)); + assertThat(expectedPossibleFileNames).contains(recordingFile); } } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java index b2b7d8d6b1c..d0bdb796181 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java @@ -25,8 +25,7 @@ import java.util.Optional; import java.util.concurrent.TimeUnit; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; @RunWith(Enclosed.class) public class ChromeRecordingWebDriverContainerTest extends BaseWebDriverContainerTest { @@ -56,7 +55,7 @@ public void recordingTestThatShouldBeRecordedAndRetainedInFlvFormatAsDefault() t .withNetwork(NETWORK) ) { File[] files = runSimpleExploreInContainer(chrome, "PASSED-.*\\.flv"); - assertEquals("Recorded file not found", 1, files.length); + assertThat(files).as("Recorded file found").hasSize(1); } } @@ -98,7 +97,7 @@ public void recordingTestShouldHaveFlvExtension() throws InterruptedException { .withNetwork(NETWORK) ) { File[] files = runSimpleExploreInContainer(chrome, "PASSED-.*\\.flv"); - assertEquals("Recorded file not found", 1, files.length); + assertThat(files).as("Recorded file found").hasSize(1); } } @@ -116,7 +115,7 @@ public void recordingTestShouldHaveMp4Extension() throws InterruptedException { .withNetwork(NETWORK) ) { File[] files = runSimpleExploreInContainer(chrome, "PASSED-.*\\.mp4"); - assertEquals("Recorded file not found", 1, files.length); + assertThat(files).as("Recorded file found").hasSize(1); } } @@ -152,10 +151,10 @@ public void recordingTestThatShouldHaveCorrectDuration() throws IOException, Int .start(); String ffmpegOutput = container.getLogs(); - assertTrue( - "Duration is incorrect in:\n " + ffmpegOutput, - ffmpegOutput.contains("Duration: 00:") && !(ffmpegOutput.contains("Duration: 00:00:00.00")) - ); + assertThat(ffmpegOutput) + .as("Duration starts with 00:") + .contains("Duration: 00:") + .doesNotContain("Duration: 00:00:00.00"); } } } @@ -216,7 +215,7 @@ public String getFilesystemFriendlyName() { ); String[] files = vncRecordingDirectory.getRoot().list(new PatternFilenameFilter("FAILED-.*\\.flv")); - assertEquals("Recorded file not found", 1, files.length); + assertThat(files).as("recorded file count").hasSize(1); } } diff --git a/modules/solr/build.gradle b/modules/solr/build.gradle index 5a6326f3844..f8e2a99888d 100644 --- a/modules/solr/build.gradle +++ b/modules/solr/build.gradle @@ -6,5 +6,5 @@ dependencies { shaded 'com.squareup.okhttp3:okhttp:4.9.3' testImplementation 'org.apache.solr:solr-solrj:8.11.1' - + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java b/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java index 9b561e5cda9..94a02acf579 100644 --- a/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java +++ b/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java @@ -10,9 +10,7 @@ import java.io.IOException; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.is; +import static org.assertj.core.api.Assertions.assertThat; /** * @author Simon Schneider @@ -36,8 +34,8 @@ public void solrCloudTest() throws IOException, SolrServerException { try (SolrContainer container = new SolrContainer(SOLR_IMAGE)) { container.start(); SolrPingResponse response = getClient(container).ping("dummy"); - assertThat(response.getStatus(), is(0)); - assertThat(response.jsonStr(), containsString("zkConnected\":true")); + assertThat(response.getStatus()).isZero(); + assertThat(response.jsonStr()).contains("zkConnected\":true"); } } @@ -46,8 +44,8 @@ public void solrStandaloneTest() throws IOException, SolrServerException { try (SolrContainer container = new SolrContainer(SOLR_IMAGE).withZookeeper(false)) { container.start(); SolrPingResponse response = getClient(container).ping("dummy"); - assertThat(response.getStatus(), is(0)); - assertThat(response.jsonStr(), containsString("zkConnected\":null")); + assertThat(response.getStatus()).isZero(); + assertThat(response.jsonStr()).contains("zkConnected\":null"); } } diff --git a/modules/tidb/src/test/java/org/testcontainers/junit/tidb/SimpleTiDBTest.java b/modules/tidb/src/test/java/org/testcontainers/junit/tidb/SimpleTiDBTest.java index c4833f14224..714c9854f39 100644 --- a/modules/tidb/src/test/java/org/testcontainers/junit/tidb/SimpleTiDBTest.java +++ b/modules/tidb/src/test/java/org/testcontainers/junit/tidb/SimpleTiDBTest.java @@ -1,7 +1,5 @@ package org.testcontainers.junit.tidb; -import org.hamcrest.Matchers; -import org.junit.Assert; import org.junit.Test; import org.testcontainers.TiDBTestImages; import org.testcontainers.db.AbstractContainerDatabaseTest; @@ -10,6 +8,8 @@ import java.sql.ResultSet; import java.sql.SQLException; +import static org.assertj.core.api.Assertions.assertThat; + public class SimpleTiDBTest extends AbstractContainerDatabaseTest { @Test @@ -20,7 +20,7 @@ public void testSimple() throws SQLException { ResultSet resultSet = performQuery(tidb, "SELECT 1"); int resultSetInt = resultSet.getInt(1); - Assert.assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).isEqualTo(1); } } @@ -34,7 +34,7 @@ public void testExplicitInitScript() throws SQLException { ResultSet resultSet = performQuery(tidb, "SELECT foo FROM bar"); String firstColumnValue = resultSet.getString(1); - Assert.assertEquals("Value from init script should equal real value", "hello world", firstColumnValue); + assertThat(firstColumnValue).isEqualTo("hello world"); } } @@ -45,8 +45,8 @@ public void testWithAdditionalUrlParamInJdbcUrl() { try { tidb.start(); String jdbcUrl = tidb.getJdbcUrl(); - Assert.assertThat(jdbcUrl, Matchers.containsString("?")); - Assert.assertThat(jdbcUrl, Matchers.containsString("sslmode=disable")); + assertThat(jdbcUrl).contains("?"); + assertThat(jdbcUrl).contains("sslmode=disable"); } finally { tidb.stop(); } diff --git a/modules/trino/src/test/java/org/testcontainers/containers/TrinoContainerTest.java b/modules/trino/src/test/java/org/testcontainers/containers/TrinoContainerTest.java index 97bbdf6dedd..a58944946f0 100644 --- a/modules/trino/src/test/java/org/testcontainers/containers/TrinoContainerTest.java +++ b/modules/trino/src/test/java/org/testcontainers/containers/TrinoContainerTest.java @@ -7,9 +7,7 @@ import java.sql.ResultSet; import java.sql.Statement; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class TrinoContainerTest { @@ -22,8 +20,10 @@ public void testSimple() throws Exception { Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes") ) { - assertTrue("No result", resultSet.next()); - assertEquals("Trino version", TrinoContainer.DEFAULT_TAG, resultSet.getString("node_version")); + assertThat(resultSet.next()).as("results").isTrue(); + assertThat(resultSet.getString("node_version")) + .as("Trino version") + .isEqualTo(TrinoContainer.DEFAULT_TAG); } } } @@ -37,12 +37,10 @@ public void testSpecificVersion() throws Exception { Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes") ) { - assertTrue("No result", resultSet.next()); - assertEquals( - "Trino version", - TrinoTestImages.TRINO_PREVIOUS_VERSION_TEST_IMAGE.getVersionPart(), - resultSet.getString("node_version") - ); + assertThat(resultSet.next()).as("results").isTrue(); + assertThat(resultSet.getString("node_version")) + .as("Trino version") + .isEqualTo(TrinoTestImages.TRINO_PREVIOUS_VERSION_TEST_IMAGE.getVersionPart()); } } } @@ -57,9 +55,9 @@ public void testInitScript() throws Exception { Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("SELECT a FROM memory.default.test_table") ) { - assertTrue("No result", resultSet.next()); - assertEquals("Value", 12345678909324L, resultSet.getObject("a")); - assertFalse("Too many result", resultSet.next()); + assertThat(resultSet.next()).as("results").isTrue(); + assertThat(resultSet.getObject("a")).as("Value").isEqualTo(12345678909324L); + assertThat(resultSet.next()).as("results").isFalse(); } } } diff --git a/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java b/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java index 22310bf7a23..f7a7cd49eaf 100644 --- a/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java +++ b/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java @@ -1,5 +1,6 @@ package org.testcontainers.vault; +import io.restassured.response.Response; import org.junit.ClassRule; import org.junit.Test; import org.testcontainers.containers.GenericContainer; @@ -7,8 +8,7 @@ import java.io.IOException; import static io.restassured.RestAssured.given; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; /** * This test shows the pattern to use the VaultContainer @ClassRule for a junit test. It also has tests that ensure @@ -42,7 +42,7 @@ public void readFirstSecretPathWithCli() throws IOException, InterruptedExceptio "secret/testing1" ); final String output = result.getStdout().replaceAll("\\r?\\n", ""); - assertThat(output, containsString("password123")); + assertThat(output).contains("password123"); } @Test @@ -56,49 +56,45 @@ public void readSecondSecretPathWithCli() throws IOException, InterruptedExcepti ); final String output = result.getStdout().replaceAll("\\r?\\n", ""); System.out.println("output = " + output); - assertThat(output, containsString("password1")); - assertThat(output, containsString("password2")); - assertThat(output, containsString("password3")); - assertThat(output, containsString("password4")); + assertThat(output).contains("password1"); + assertThat(output).contains("password2"); + assertThat(output).contains("password3"); + assertThat(output).contains("password4"); } @Test public void readFirstSecretPathOverHttpApi() throws InterruptedException { - given() + Response response = given() .header("X-Vault-Token", VAULT_TOKEN) .when() .get("http://" + getHostAndPort() + "/v1/secret/data/testing1") - .then() - .assertThat() - .body("data.data.top_secret", equalTo("password123")); + .thenReturn(); + assertThat(response.body().jsonPath().getString("data.data.top_secret")).isEqualTo("password123"); } @Test public void readSecondSecretPathOverHttpApi() throws InterruptedException { - given() + Response response = given() .header("X-Vault-Token", VAULT_TOKEN) .when() .get("http://" + getHostAndPort() + "/v1/secret/data/testing2") - .then() - .assertThat() - .body("data.data.secret_one", containsString("password1")) - .assertThat() - .body("data.data.secret_two", containsString("password2")) - .assertThat() - .body("data.data.secret_three", hasItem("password3")) - .assertThat() - .body("data.data.secret_four", containsString("password4")); + .andReturn(); + + assertThat(response.body().jsonPath().getString("data.data.secret_one")).contains("password1"); + assertThat(response.body().jsonPath().getString("data.data.secret_two")).contains("password2"); + assertThat(response.body().jsonPath().getList("data.data.secret_three")).contains("password3"); + assertThat(response.body().jsonPath().getString("data.data.secret_four")).contains("password4"); } @Test public void readTransitKeyOverHttpApi() throws InterruptedException { - given() + Response response = given() .header("X-Vault-Token", VAULT_TOKEN) .when() .get("http://" + getHostAndPort() + "/v1/transit/keys/my-key") - .then() - .assertThat() - .body("data.name", equalTo("my-key")); + .thenReturn(); + + assertThat(response.body().jsonPath().getString("data.name")).isEqualTo("my-key"); } private String getHostAndPort() { diff --git a/test-support/build.gradle b/test-support/build.gradle index bd85b585b0f..ecf9a0bc60e 100644 --- a/test-support/build.gradle +++ b/test-support/build.gradle @@ -1,4 +1,5 @@ dependencies { implementation 'junit:junit:4.13.2' implementation 'org.slf4j:slf4j-api:1.7.30' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/test-support/src/test/java/org/testcontainers/testsupport/FlakyRuleTest.java b/test-support/src/test/java/org/testcontainers/testsupport/FlakyRuleTest.java index 14a3d4c018c..60a311922d0 100644 --- a/test-support/src/test/java/org/testcontainers/testsupport/FlakyRuleTest.java +++ b/test-support/src/test/java/org/testcontainers/testsupport/FlakyRuleTest.java @@ -6,8 +6,8 @@ import java.lang.annotation.Annotation; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; public class FlakyRuleTest { @@ -33,7 +33,9 @@ public void testIgnoresMethodWithoutAnnotation() throws Throwable { rule.apply(statement, description).evaluate(); fail("Should not reach here"); } catch (Exception ignored) {} - assertEquals("The statement should only be invoked once, even if it throws", 1, statement.invocationCount); + assertThat(statement.invocationCount) + .as("The statement should only be invoked once, even if it throws") + .isEqualTo(1); } @Test @@ -45,7 +47,7 @@ public void testRetriesMethodWithAnnotationUntilFailure() throws Throwable { rule.apply(statement, description).evaluate(); fail("Should not reach here"); } catch (Exception ignored) {} - assertEquals("The statement should be invoked three times", 3, statement.invocationCount); + assertThat(statement.invocationCount).as("The statement should be invoked three times").isEqualTo(3); } @Test @@ -57,7 +59,7 @@ public void testCustomRetryCount() throws Throwable { rule.apply(statement, description).evaluate(); fail("Should not reach here"); } catch (Exception ignored) {} - assertEquals("The statement should be invoked ten times", 10, statement.invocationCount); + assertThat(statement.invocationCount).as("The statement should be invoked ten times").isEqualTo(10); } @Test @@ -68,11 +70,9 @@ public void testRetriesMethodWithAnnotationUntilSuccess() throws Throwable { rule.apply(statement, description).evaluate(); - assertEquals( - "The statement should be invoked three times, and succeed the third time", - 3, - statement.invocationCount - ); + assertThat(statement.invocationCount) + .as("The statement should be invoked three times, and succeed the third time") + .isEqualTo(3); } @Test @@ -83,7 +83,7 @@ public void testDoesNotRetryMethodWithAnnotationIfNotThrowing() throws Throwable rule.apply(statement, description).evaluate(); - assertEquals("The statement should be invoked once", 1, statement.invocationCount); + assertThat(statement.invocationCount).as("The statement should be invoked once").isEqualTo(1); } @Test @@ -94,7 +94,9 @@ public void testTreatsExpiredAnnotationAsNoAnnotation() throws Throwable { rule.apply(statement, description).evaluate(); fail("Should not reach here"); } catch (Exception ignored) {} - assertEquals("The statement should only be invoked once, even if it throws", 1, statement.invocationCount); + assertThat(statement.invocationCount) + .as("The statement should only be invoked once, even if it throws") + .isEqualTo(1); } @Test @@ -105,7 +107,9 @@ public void testThrowsOnInvalidDateFormat() throws Throwable { rule.apply(statement, description).evaluate(); fail("Should not reach here"); } catch (IllegalArgumentException ignored) {} - assertEquals("The statement should not be invoked if the annotation is invalid", 0, statement.invocationCount); + assertThat(statement.invocationCount) + .as("The statement should not be invoked if the annotation is invalid") + .isEqualTo(0); } @Test @@ -116,7 +120,9 @@ public void testThrowsOnInvalidGitHubUrl() throws Throwable { rule.apply(statement, description).evaluate(); fail("Should not reach here"); } catch (IllegalArgumentException ignored) {} - assertEquals("The statement should not be invoked if the annotation is invalid", 0, statement.invocationCount); + assertThat(statement.invocationCount) + .as("The statement should not be invoked if the annotation is invalid") + .isEqualTo(0); } private Description newDescriptionWithAnnotation(String reviewDate, String gitHubUrl) { From 9275bc51e20e0e7bf937f48d2850a9865cb41948 Mon Sep 17 00:00:00 2001 From: Matt Hornung Date: Tue, 16 Aug 2022 10:06:59 -0500 Subject: [PATCH 2790/5615] Make JUnit5 TestcontainersExtension public (#5285) This allows more flexible and programmatic usage of the `TestcontainersExtension` Fixes #2045 --- .../testcontainers/junit/jupiter/TestcontainersExtension.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java index 47ed56d9ac5..5a11b927ec0 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java @@ -32,7 +32,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -class TestcontainersExtension +public class TestcontainersExtension implements BeforeEachCallback, BeforeAllCallback, AfterEachCallback, AfterAllCallback, ExecutionCondition { private static final Namespace NAMESPACE = Namespace.create(TestcontainersExtension.class); From df5e1eec19a133dd922498b0634fdd712bcdd153 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Tue, 16 Aug 2022 11:02:34 -0500 Subject: [PATCH 2791/5615] Drop usage of `org.junit.platform.commons.util` (#5729) Classes from `org.junit.platform.commons.util` are used for internal use in JUnit Jupiter. This commit introduces replacements. --- .../FilesystemFriendlyNameGenerator.java | 3 +- .../jupiter/TestcontainersExtension.java | 28 +++++++++---------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGenerator.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGenerator.java index a8612fc1516..fe5ddcf32e0 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGenerator.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGenerator.java @@ -1,7 +1,6 @@ package org.testcontainers.junit.jupiter; import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.platform.commons.util.StringUtils; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; @@ -14,7 +13,7 @@ class FilesystemFriendlyNameGenerator { static String filesystemFriendlyNameOf(ExtensionContext context) { String contextId = context.getUniqueId(); try { - return (StringUtils.isBlank(contextId)) + return (contextId == null || contextId.trim().isEmpty()) ? UNKNOWN_NAME : URLEncoder.encode(contextId, StandardCharsets.UTF_8.toString()); } catch (UnsupportedEncodingException e) { diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java index 5a11b927ec0..757083dc322 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java @@ -13,9 +13,9 @@ import org.junit.jupiter.api.extension.ExtensionContext.Store; import org.junit.jupiter.api.extension.ExtensionContext.Store.CloseableResource; import org.junit.platform.commons.support.AnnotationSupport; -import org.junit.platform.commons.util.AnnotationUtils; -import org.junit.platform.commons.util.Preconditions; -import org.junit.platform.commons.util.ReflectionUtils; +import org.junit.platform.commons.support.HierarchyTraversalMode; +import org.junit.platform.commons.support.ModifierSupport; +import org.junit.platform.commons.support.ReflectionSupport; import org.testcontainers.DockerClientFactory; import org.testcontainers.lifecycle.Startable; import org.testcontainers.lifecycle.TestDescription; @@ -131,7 +131,7 @@ public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext con private Optional findTestcontainers(ExtensionContext context) { Optional current = Optional.of(context); while (current.isPresent()) { - Optional testcontainers = AnnotationUtils.findAnnotation( + Optional testcontainers = AnnotationSupport.findAnnotation( current.get().getRequiredTestClass(), Testcontainers.class ); @@ -169,26 +169,26 @@ private Set collectParentTestInstances(final ExtensionContext context) { } private List findSharedContainers(Class testClass) { - return ReflectionUtils - .findFields(testClass, isSharedContainer(), ReflectionUtils.HierarchyTraversalMode.TOP_DOWN) + return ReflectionSupport + .findFields(testClass, isSharedContainer(), HierarchyTraversalMode.TOP_DOWN) .stream() .map(f -> getContainerInstance(null, f)) .collect(Collectors.toList()); } private Predicate isSharedContainer() { - return isContainer().and(ReflectionUtils::isStatic); + return isContainer().and(ModifierSupport::isStatic); } private Stream findRestartContainers(Object testInstance) { - return ReflectionUtils - .findFields(testInstance.getClass(), isRestartContainer(), ReflectionUtils.HierarchyTraversalMode.TOP_DOWN) + return ReflectionSupport + .findFields(testInstance.getClass(), isRestartContainer(), HierarchyTraversalMode.TOP_DOWN) .stream() .map(f -> getContainerInstance(testInstance, f)); } private Predicate isRestartContainer() { - return isContainer().and(ReflectionUtils::isNotStatic); + return isContainer().and(ModifierSupport::isNotStatic); } private static Predicate isContainer() { @@ -211,10 +211,10 @@ private static Predicate isContainer() { private static StoreAdapter getContainerInstance(final Object testInstance, final Field field) { try { field.setAccessible(true); - Startable containerInstance = Preconditions.notNull( - (Startable) field.get(testInstance), - "Container " + field.getName() + " needs to be initialized" - ); + Startable containerInstance = (Startable) field.get(testInstance); + if (containerInstance == null) { + throw new ExtensionConfigurationException("Container " + field.getName() + " needs to be initialized"); + } return new StoreAdapter(field.getDeclaringClass(), field.getName(), containerInstance); } catch (IllegalAccessException e) { throw new ExtensionConfigurationException("Can not access container defined in field " + field.getName()); From 18304642dcbbd5db229bc4390d431e9208be88d0 Mon Sep 17 00:00:00 2001 From: Tobias Gesellchen Date: Thu, 18 Aug 2022 15:01:50 +0200 Subject: [PATCH 2792/5615] Enforce UTF-8 as Javadoc encoding (#5738) * Use com.gradle:common-custom-user-data-gradle-plugin:1.8 (instead of 1.8.0) --- build.gradle | 1 + examples/settings.gradle | 2 +- settings.gradle | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 0ded70ad71a..a53bd19298d 100644 --- a/build.gradle +++ b/build.gradle @@ -42,6 +42,7 @@ subprojects { targetCompatibility = 1.8 compileJava.options.encoding = 'UTF-8' compileTestJava.options.encoding = 'UTF-8' + javadoc.options.encoding = 'UTF-8' repositories { mavenCentral() diff --git a/examples/settings.gradle b/examples/settings.gradle index 83ba91e8321..9ed079cd7b8 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -7,7 +7,7 @@ buildscript { dependencies { classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.10.0" classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.11.1" - classpath "com.gradle:common-custom-user-data-gradle-plugin:1.8.0" + classpath "com.gradle:common-custom-user-data-gradle-plugin:1.8" } } diff --git a/settings.gradle b/settings.gradle index bd51349bb05..2beb91dea01 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.11.1" - classpath "com.gradle:common-custom-user-data-gradle-plugin:1.8.0" + classpath "com.gradle:common-custom-user-data-gradle-plugin:1.8" } } From 9fb5db16a6af38257256fd04e452737e0f1ac15a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Thu, 18 Aug 2022 12:09:55 -0500 Subject: [PATCH 2793/5615] Add Pull Request Template (#5735) It provides some useful information about what to take into account when submitting a PR. Co-authored-by: Kevin Wittek --- .github/pull_request_template.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000000..643177f08a9 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,19 @@ + From 20fdee2da625aa3c12d1212ea0b370f299901e02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Thu, 18 Aug 2022 18:06:21 -0500 Subject: [PATCH 2794/5615] Add `Transferable.of(String, int)` (#5741) The new method adds similar capability of `Transferable.of(byte[], int)` but for `String`. --- core/build.gradle | 6 ++++++ .../testcontainers/images/builder/Transferable.java | 4 ++++ .../containers/GenericContainerTest.java | 13 +++++++++++++ 3 files changed, 23 insertions(+) diff --git a/core/build.gradle b/core/build.gradle index c210ed48510..aa4c6eabf22 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -113,3 +113,9 @@ tasks.generatePomFileForMavenJavaPublication.finalizedBy( ] } ) + +tasks.japicmp { + methodExcludes = [ + "org.testcontainers.images.builder.Transferable#of(java.lang.String,int)" + ] +} diff --git a/core/src/main/java/org/testcontainers/images/builder/Transferable.java b/core/src/main/java/org/testcontainers/images/builder/Transferable.java index e98dbc2ad11..292a70d6ac7 100644 --- a/core/src/main/java/org/testcontainers/images/builder/Transferable.java +++ b/core/src/main/java/org/testcontainers/images/builder/Transferable.java @@ -16,6 +16,10 @@ static Transferable of(String string) { return of(string.getBytes(StandardCharsets.UTF_8)); } + static Transferable of(String string, int fileMode) { + return of(string.getBytes(StandardCharsets.UTF_8), fileMode); + } + static Transferable of(byte[] bytes) { return of(bytes, DEFAULT_FILE_MODE); } diff --git a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java index 1967ad95135..12f5810bc27 100644 --- a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java +++ b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java @@ -84,6 +84,19 @@ public void shouldCopyTransferableAsFile() { } } + @Test + public void shouldCopyTransferableAsFileWithFileMode() { + try ( + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) + .withStartupCheckStrategy(new NoopStartupCheckStrategy()) + .withCopyToContainer(Transferable.of("test", 0777), "/tmp/test") + .waitingFor(new WaitForExitedState(state -> state.getExitCodeLong() > 0)) + .withCommand("sh", "-c", "ls -ll /tmp | grep '\\-rwxrwxrwx\\|test' && exit 100") + ) { + assertThatThrownBy(container::start).hasStackTraceContaining("Container exited with code 100"); + } + } + @Test public void shouldCopyTransferableAfterMountableFile() { try ( From 8b55a3e63ae2a5dfbf4f916d7fc9f6eb008b2f0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Thu, 18 Aug 2022 18:35:59 -0500 Subject: [PATCH 2795/5615] Add Redpanda module (#5740) New module uses `docker.redpanda.com/vectorized/redpanda:v22.2.1` docker image as a base giving the `--mode dev-container` flag added in that version. Co-authored-by: Sergei Egorov --- .github/ISSUE_TEMPLATE/bug_report.yaml | 1 + .github/ISSUE_TEMPLATE/enhancement.yaml | 1 + .github/ISSUE_TEMPLATE/feature.yaml | 1 + .github/dependabot.yml | 5 + .github/labeler.yml | 2 + docs/modules/redpanda.md | 38 +++++ mkdocs.yml | 1 + modules/redpanda/build.gradle | 8 ++ .../redpanda/RedpandaContainer.java | 60 ++++++++ .../redpanda/RedpandaContainerTest.java | 130 ++++++++++++++++++ .../src/test/resources/logback-test.xml | 16 +++ 11 files changed, 263 insertions(+) create mode 100644 docs/modules/redpanda.md create mode 100644 modules/redpanda/build.gradle create mode 100644 modules/redpanda/src/main/java/org/testcontainers/redpanda/RedpandaContainer.java create mode 100644 modules/redpanda/src/test/java/org/testcontainers/redpanda/RedpandaContainerTest.java create mode 100644 modules/redpanda/src/test/resources/logback-test.xml diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 5aca9e624b3..163c5455b1e 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -42,6 +42,7 @@ body: - Presto - Pulsar - RabbitMQ + - Redpanda - Selenium - Solr - TiDB diff --git a/.github/ISSUE_TEMPLATE/enhancement.yaml b/.github/ISSUE_TEMPLATE/enhancement.yaml index ba5b1f88006..b753332073b 100644 --- a/.github/ISSUE_TEMPLATE/enhancement.yaml +++ b/.github/ISSUE_TEMPLATE/enhancement.yaml @@ -42,6 +42,7 @@ body: - Presto - Pulsar - RabbitMQ + - Redpanda - Selenium - Solr - TiDB diff --git a/.github/ISSUE_TEMPLATE/feature.yaml b/.github/ISSUE_TEMPLATE/feature.yaml index 981f5613a1e..91031adfeee 100644 --- a/.github/ISSUE_TEMPLATE/feature.yaml +++ b/.github/ISSUE_TEMPLATE/feature.yaml @@ -42,6 +42,7 @@ body: - Presto - Pulsar - RabbitMQ + - Redpanda - Selenium - Solr - TiDB diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 22437e15825..4c270ef762c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -187,6 +187,11 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/redpanda" + schedule: + interval: "monthly" + open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/selenium" schedule: diff --git a/.github/labeler.yml b/.github/labeler.yml index cf75af99d26..492991a9947 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -69,6 +69,8 @@ - modules/r2dbc/* "modules/rabbitmq": - modules/rabbitmq/* +"modules/redpanda": + - modules/redpanda/* "modules/selenium": - modules/selenium/* "modules/solr": diff --git a/docs/modules/redpanda.md b/docs/modules/redpanda.md new file mode 100644 index 00000000000..8749c9c87f5 --- /dev/null +++ b/docs/modules/redpanda.md @@ -0,0 +1,38 @@ +# Redpanda + +Testcontainers can be used to automatically instantiate and manage [Redpanda](https://redpanda.com/) containers. +More precisely Testcontainers uses the official Docker images for [Redpanda](https://hub.docker.com/r/vectorized/redpanda/) + +!!! note + This module uses features provided in `docker.redpanda.com/vectorized/redpanda`. + +## Example + +Create a `Redpanda` to use it in your tests: + +[Creating a Redpanda](../../modules/redpanda/src/test/java/org/testcontainers/redpanda/RedpandaContainerTest.java) inside_block:constructorWithVersion + + +Now your tests or any other process running on your machine can get access to running Redpanda broker by using the following bootstrap server location: + + +[Bootstrap Servers](../../modules/redpanda/src/test/java/org/testcontainers/redpanda/RedpandaContainerTest.java) inside_block:getBootstrapServers + + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +=== "Gradle" +```groovy +testImplementation "org.testcontainers:redpanda:{{latest_version}}" +``` +=== "Maven" +```xml + + org.testcontainers + redpanda + {{latest_version}} + test + +``` diff --git a/mkdocs.yml b/mkdocs.yml index a836e0a2ff7..42011817a5c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -78,6 +78,7 @@ nav: - modules/nginx.md - modules/pulsar.md - modules/rabbitmq.md + - modules/redpanda.md - modules/solr.md - modules/toxiproxy.md - modules/vault.md diff --git a/modules/redpanda/build.gradle b/modules/redpanda/build.gradle new file mode 100644 index 00000000000..5750e873bf6 --- /dev/null +++ b/modules/redpanda/build.gradle @@ -0,0 +1,8 @@ +description = "Testcontainers :: Redpanda" + +dependencies { + api project(':testcontainers') + + testImplementation 'org.apache.kafka:kafka-clients:3.2.1' + testImplementation 'org.assertj:assertj-core:3.23.1' +} diff --git a/modules/redpanda/src/main/java/org/testcontainers/redpanda/RedpandaContainer.java b/modules/redpanda/src/main/java/org/testcontainers/redpanda/RedpandaContainer.java new file mode 100644 index 00000000000..cbb7db1e7c1 --- /dev/null +++ b/modules/redpanda/src/main/java/org/testcontainers/redpanda/RedpandaContainer.java @@ -0,0 +1,60 @@ +package org.testcontainers.redpanda; + +import com.github.dockerjava.api.command.InspectContainerResponse; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.images.builder.Transferable; +import org.testcontainers.utility.ComparableVersion; +import org.testcontainers.utility.DockerImageName; + +/** + * Testcontainers implementation for Redpanda. + */ +public class RedpandaContainer extends GenericContainer { + + private static final String REDPANDA_FULL_IMAGE_NAME = "docker.redpanda.com/vectorized/redpanda"; + + private static final DockerImageName REDPANDA_IMAGE = DockerImageName.parse(REDPANDA_FULL_IMAGE_NAME); + + private static final int REDPANDA_PORT = 9092; + + private static final String STARTER_SCRIPT = "/testcontainers_start.sh"; + + public RedpandaContainer(String image) { + this(DockerImageName.parse(image)); + } + + public RedpandaContainer(DockerImageName imageName) { + super(imageName); + imageName.assertCompatibleWith(REDPANDA_IMAGE); + + boolean isLessThanBaseVersion = new ComparableVersion(imageName.getVersionPart()).isLessThan("v22.2.1"); + if (REDPANDA_FULL_IMAGE_NAME.equals(imageName.getUnversionedPart()) && isLessThanBaseVersion) { + throw new IllegalArgumentException("Redpanda version must be >= v22.2.1"); + } + + withExposedPorts(REDPANDA_PORT); + withCreateContainerCmdModifier(cmd -> { + cmd.withEntrypoint("sh"); + }); + waitingFor(Wait.forLogMessage(".*Started Kafka API server.*", 1)); + withCommand("-c", "while [ ! -f " + STARTER_SCRIPT + " ]; do sleep 0.1; done; " + STARTER_SCRIPT); + } + + @Override + protected void containerIsStarting(InspectContainerResponse containerInfo) { + super.containerIsStarting(containerInfo); + + String command = "#!/bin/bash\n"; + + command += "/usr/bin/rpk redpanda start --mode dev-container "; + command += "--kafka-addr PLAINTEXT://0.0.0.0:29092,OUTSIDE://0.0.0.0:9092 "; + command += "--advertise-kafka-addr PLAINTEXT://kafka:29092,OUTSIDE://" + getHost() + ":" + getMappedPort(9092); + + copyFileToContainer(Transferable.of(command, 0777), STARTER_SCRIPT); + } + + public String getBootstrapServers() { + return String.format("PLAINTEXT://%s:%s", getHost(), getMappedPort(REDPANDA_PORT)); + } +} diff --git a/modules/redpanda/src/test/java/org/testcontainers/redpanda/RedpandaContainerTest.java b/modules/redpanda/src/test/java/org/testcontainers/redpanda/RedpandaContainerTest.java new file mode 100644 index 00000000000..086bf43364b --- /dev/null +++ b/modules/redpanda/src/test/java/org/testcontainers/redpanda/RedpandaContainerTest.java @@ -0,0 +1,130 @@ +package org.testcontainers.redpanda; + +import com.google.common.collect.ImmutableMap; +import org.apache.kafka.clients.admin.AdminClient; +import org.apache.kafka.clients.admin.AdminClientConfig; +import org.apache.kafka.clients.admin.NewTopic; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.apache.kafka.common.serialization.StringSerializer; +import org.junit.Test; +import org.rnorth.ducttape.unreliables.Unreliables; +import org.testcontainers.utility.DockerImageName; + +import java.time.Duration; +import java.util.Collection; +import java.util.Collections; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.Assertions.tuple; + +public class RedpandaContainerTest { + + private static final String REDPANDA_IMAGE = "docker.redpanda.com/vectorized/redpanda:v22.2.1"; + + private static final DockerImageName REDPANDA_DOCKER_IMAGE = DockerImageName.parse(REDPANDA_IMAGE); + + @Test + public void testUsage() throws Exception { + try (RedpandaContainer container = new RedpandaContainer(REDPANDA_DOCKER_IMAGE)) { + container.start(); + testKafkaFunctionality(container.getBootstrapServers()); + } + } + + @Test + public void testUsageWithStringImage() throws Exception { + try ( + // constructorWithVersion { + RedpandaContainer container = new RedpandaContainer("docker.redpanda.com/vectorized/redpanda:v22.2.1") + // } + ) { + container.start(); + testKafkaFunctionality( + // getBootstrapServers { + container.getBootstrapServers() + // } + ); + } + } + + @Test + public void testNotCompatibleVersion() { + assertThatThrownBy(() -> new RedpandaContainer("docker.redpanda.com/vectorized/redpanda:v21.11.19")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Redpanda version must be >= v22.2.1"); + } + + private void testKafkaFunctionality(String bootstrapServers) throws Exception { + testKafkaFunctionality(bootstrapServers, 1, 1); + } + + private void testKafkaFunctionality(String bootstrapServers, int partitions, int rf) throws Exception { + try ( + AdminClient adminClient = AdminClient.create( + ImmutableMap.of(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers) + ); + KafkaProducer producer = new KafkaProducer<>( + ImmutableMap.of( + ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, + bootstrapServers, + ProducerConfig.CLIENT_ID_CONFIG, + UUID.randomUUID().toString() + ), + new StringSerializer(), + new StringSerializer() + ); + KafkaConsumer consumer = new KafkaConsumer<>( + ImmutableMap.of( + ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, + bootstrapServers, + ConsumerConfig.GROUP_ID_CONFIG, + "tc-" + UUID.randomUUID(), + ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, + "earliest" + ), + new StringDeserializer(), + new StringDeserializer() + ); + ) { + String topicName = "messages-" + UUID.randomUUID(); + + Collection topics = Collections.singletonList(new NewTopic(topicName, partitions, (short) rf)); + adminClient.createTopics(topics).all().get(30, TimeUnit.SECONDS); + + consumer.subscribe(Collections.singletonList(topicName)); + + producer.send(new ProducerRecord<>(topicName, "testcontainers", "rulezzz")).get(); + + Unreliables.retryUntilTrue( + 10, + TimeUnit.SECONDS, + () -> { + ConsumerRecords records = consumer.poll(Duration.ofMillis(100)); + + if (records.isEmpty()) { + return false; + } + + assertThat(records) + .hasSize(1) + .extracting(ConsumerRecord::topic, ConsumerRecord::key, ConsumerRecord::value) + .containsExactly(tuple(topicName, "testcontainers", "rulezzz")); + + return true; + } + ); + + consumer.unsubscribe(); + } + } +} diff --git a/modules/redpanda/src/test/resources/logback-test.xml b/modules/redpanda/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..83ef7a1a3ef --- /dev/null +++ b/modules/redpanda/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + From 5f486d9c5e3e6a0c8ca7a270bd1c033cf0292c23 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 23:36:27 +0000 Subject: [PATCH 2796/5615] Bump postgresql from 42.4.1 to 42.4.2 in /modules/cockroachdb Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.1 to 42.4.2. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.1...REL42.4.2) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/cockroachdb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cockroachdb/build.gradle b/modules/cockroachdb/build.gradle index 1fc5dac8d8a..fbd2415e53c 100644 --- a/modules/cockroachdb/build.gradle +++ b/modules/cockroachdb/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'org.postgresql:postgresql:42.4.1' + testImplementation 'org.postgresql:postgresql:42.4.2' testImplementation 'org.assertj:assertj-core:3.23.1' } From e8f7c19278c3d12c84097c868a64bb2e969ca298 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 23:36:29 +0000 Subject: [PATCH 2797/5615] Bump postgresql from 42.4.1 to 42.4.2 in /modules/junit-jupiter Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.1 to 42.4.2. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.1...REL42.4.2) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 08afd67839d..dd1d0c7ed6e 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.0' - testRuntimeOnly 'org.postgresql:postgresql:42.4.1' + testRuntimeOnly 'org.postgresql:postgresql:42.4.2' testRuntimeOnly 'mysql:mysql-connector-java:8.0.30' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0' } From ba585921f607a12b770bacbda3d6bc1be7fa0f44 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 23:36:29 +0000 Subject: [PATCH 2798/5615] Bump google-cloud-pubsub from 1.120.11 to 1.120.12 in /modules/gcloud Bumps [google-cloud-pubsub](https://github.com/googleapis/java-pubsub) from 1.120.11 to 1.120.12. - [Release notes](https://github.com/googleapis/java-pubsub/releases) - [Changelog](https://github.com/googleapis/java-pubsub/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-pubsub/compare/v1.120.11...v1.120.12) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-pubsub dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index e7bc4065d82..bb79bfbbcc2 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -5,7 +5,7 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-datastore:2.11.0' testImplementation 'com.google.cloud:google-cloud-firestore:3.4.0' - testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.11' + testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.12' testImplementation 'com.google.cloud:google-cloud-spanner:6.28.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.10.3' testImplementation 'org.assertj:assertj-core:3.23.1' From 87b0a80d69b674d727b1cc216e13e3b0819e07aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 23:36:30 +0000 Subject: [PATCH 2799/5615] Bump aws-java-sdk-dynamodb in /modules/dynalite Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.12.281 to 1.12.285. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.281...1.12.285) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-dynamodb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/dynalite/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 81ec1f9deb5..8b548d5b91f 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Dynalite" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.281' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.285' testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.281' testImplementation 'org.assertj:assertj-core:3.23.1' } From 6c7b68c88dcb01a67f56dc9c7d701deca3b1b718 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 23:36:31 +0000 Subject: [PATCH 2800/5615] Bump trino-jdbc from 392 to 393 in /modules/trino Bumps trino-jdbc from 392 to 393. --- updated-dependencies: - dependency-name: io.trino:trino-jdbc dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/trino/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/trino/build.gradle b/modules/trino/build.gradle index 09b5b8cb16c..e5b7efee873 100644 --- a/modules/trino/build.gradle +++ b/modules/trino/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'io.trino:trino-jdbc:392' + testImplementation 'io.trino:trino-jdbc:393' compileOnly 'org.jetbrains:annotations:23.0.0' } From 42ddd6b86bcfd1e5f54b594b22f8b13e2ac86c24 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 23:36:32 +0000 Subject: [PATCH 2801/5615] Bump postgresql from 42.4.1 to 42.4.2 in /modules/postgresql Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.1 to 42.4.2. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.1...REL42.4.2) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index f769bf9b07c..0c5035df549 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -11,7 +11,7 @@ dependencies { testImplementation project(':jdbc-test') testImplementation project(':test-support') - testImplementation 'org.postgresql:postgresql:42.4.1' + testImplementation 'org.postgresql:postgresql:42.4.2' testImplementation testFixtures(project(':r2dbc')) testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.11.RELEASE' From c4462e6d05390988b871b9bce277b9e14733c179 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 23:36:37 +0000 Subject: [PATCH 2802/5615] Bump s3 from 2.17.253 to 2.17.256 in /modules/localstack Bumps s3 from 2.17.253 to 2.17.256. --- updated-dependencies: - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 76090d2f6dd..23f4d045cba 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.273' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.282' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.281' - testImplementation 'software.amazon.awssdk:s3:2.17.253' + testImplementation 'software.amazon.awssdk:s3:2.17.256' testImplementation 'org.assertj:assertj-core:3.23.1' } From 23d93d52d6414a02772e3768aa6bd70671e333f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 23:36:40 +0000 Subject: [PATCH 2803/5615] Bump release-drafter/release-drafter from 5.20.0 to 5.20.1 Bumps [release-drafter/release-drafter](https://github.com/release-drafter/release-drafter) from 5.20.0 to 5.20.1. - [Release notes](https://github.com/release-drafter/release-drafter/releases) - [Commits](https://github.com/release-drafter/release-drafter/compare/ac463ffd9cc4c6ad5682af93dc3e3591c4657ee3...06a49bf28488e030d35ca2ac6dbf7f408a481779) --- updated-dependencies: - dependency-name: release-drafter/release-drafter dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/release-drafter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index fb0780d781f..94af563bcda 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -18,6 +18,6 @@ jobs: if: github.repository == 'testcontainers/testcontainers-java' runs-on: ubuntu-latest steps: - - uses: release-drafter/release-drafter@ac463ffd9cc4c6ad5682af93dc3e3591c4657ee3 # v5.19.0 + - uses: release-drafter/release-drafter@06a49bf28488e030d35ca2ac6dbf7f408a481779 # v5.19.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From aa1512123425260157c313ccd4e8011a436dbc50 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 23:36:41 +0000 Subject: [PATCH 2804/5615] Bump aws-java-sdk-s3 from 1.12.281 to 1.12.285 in /modules/localstack Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.281 to 1.12.285. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.281...1.12.285) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 76090d2f6dd..65128d624bc 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Localstack" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.281' - testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.273' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.285' + testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.285' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.282' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.281' testImplementation 'software.amazon.awssdk:s3:2.17.253' From d100185f041aae612e19c11df7223d2f214f56ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 23:36:43 +0000 Subject: [PATCH 2805/5615] Bump peter-evans/create-pull-request from 4.0.4 to 4.1.1 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4.0.4 to 4.1.1. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/923ad837f191474af6b1721408744feb989a4c27...18f90432bedd2afd6a825469ffd38aa24712a91d) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/update-docs-version.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index b2736c42590..070fa2c55d4 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -23,7 +23,7 @@ jobs: sed -i "s/latest_version: .*/latest_version: ${GITHUB_REF##*/}/g" mkdocs.yml git diff - name: Create Pull Request - uses: peter-evans/create-pull-request@923ad837f191474af6b1721408744feb989a4c27 # v3.10.1 + uses: peter-evans/create-pull-request@18f90432bedd2afd6a825469ffd38aa24712a91d # v3.10.1 with: title: Update docs version to ${GITHUB_REF##*/} body: | From 1850fa7ca123cc7ee5e375d8867b2d80881b2b66 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 23:36:47 +0000 Subject: [PATCH 2806/5615] Bump postgresql from 42.4.1 to 42.4.2 in /examples Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.1 to 42.4.2. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.1...REL42.4.2) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 1865f70f801..5b690597eaf 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.36' implementation 'com.squareup.okhttp3:okhttp:4.10.0' implementation 'org.json:json:20220320' - testImplementation 'org.postgresql:postgresql:42.4.1' + testImplementation 'org.postgresql:postgresql:42.4.2' testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.testcontainers:postgresql' testImplementation 'org.assertj:assertj-core:3.23.1' From b82ae5c02cddb455d13c7525785799295b767b4c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Aug 2022 02:15:39 +0000 Subject: [PATCH 2807/5615] Bump aws-java-sdk-logs from 1.12.281 to 1.12.285 in /modules/localstack Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.12.281 to 1.12.285. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.281...1.12.285) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-logs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 6c19c73b3be..ed6f57fb6d2 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,7 +6,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.285' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.285' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.282' - testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.281' + testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.285' testImplementation 'software.amazon.awssdk:s3:2.17.256' testImplementation 'org.assertj:assertj-core:3.23.1' } From 3bb9afa1d42e968297c285ca46f80b49f0142a9f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Aug 2022 02:15:44 +0000 Subject: [PATCH 2808/5615] Bump postgresql from 42.4.1 to 42.4.2 in /modules/spock Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.1 to 42.4.2. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.1...REL42.4.2) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 304e0b98dc4..543824ea8f6 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'com.zaxxer:HikariCP:4.0.3' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' - testRuntimeOnly 'org.postgresql:postgresql:42.4.1' + testRuntimeOnly 'org.postgresql:postgresql:42.4.2' testRuntimeOnly 'mysql:mysql-connector-java:8.0.30' testCompileClasspath 'org.jetbrains:annotations:23.0.0' From 917f451cd04f313f923682fb19ddcc7255d403ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Aug 2022 14:48:41 +0000 Subject: [PATCH 2809/5615] Bump aws-java-sdk-sqs from 1.12.282 to 1.12.285 in /modules/localstack Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.12.282 to 1.12.285. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.282...1.12.285) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-sqs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index ed6f57fb6d2..ad43f6f9c08 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.285' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.285' - testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.282' + testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.285' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.285' testImplementation 'software.amazon.awssdk:s3:2.17.256' testImplementation 'org.assertj:assertj-core:3.23.1' From dcf416cdc31c59131819f73e92e382e0b5c368af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Fri, 19 Aug 2022 11:35:54 -0500 Subject: [PATCH 2810/5615] Fix type/docs label (#5759) Only consider files with `.md` extension. --- .github/labeler.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index 492991a9947..85144b7d7f9 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -85,4 +85,5 @@ - modules/trino/* "modules/vault": - modules/vault/* -"type/docs": docs/**/* +"type/docs": + - docs/**/*.md From 017d937dc7d2d847f2f412ba37c3db226c4623d5 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Mon, 22 Aug 2022 16:48:59 +0200 Subject: [PATCH 2811/5615] Update GHA runner image to latest Ubuntu (#5761) `ubuntu-latest` means `ubuntu-20.04` at the time of the commit. It should always be a LTS version. --- .github/workflows/ci-docker-wormhole.yml | 2 +- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci-rootless.yml | 2 +- .github/workflows/ci.yml | 4 ++-- .github/workflows/release.yml | 2 +- .github/workflows/update-docs-version.yml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci-docker-wormhole.yml b/.github/workflows/ci-docker-wormhole.yml index e27e4d8d611..ace645e1bb1 100644 --- a/.github/workflows/ci-docker-wormhole.yml +++ b/.github/workflows/ci-docker-wormhole.yml @@ -9,7 +9,7 @@ permissions: jobs: in-docker_test: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Build with Gradle diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index f23fc66e889..70917663e25 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -15,7 +15,7 @@ permissions: jobs: find_gradle_jobs: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: @@ -45,7 +45,7 @@ jobs: strategy: fail-fast: false matrix: ${{ fromJson(needs.find_gradle_jobs.outputs.matrix) }} - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-java@v3 diff --git a/.github/workflows/ci-rootless.yml b/.github/workflows/ci-rootless.yml index 348cf5aaaf7..334fe69e9c4 100644 --- a/.github/workflows/ci-rootless.yml +++ b/.github/workflows/ci-rootless.yml @@ -9,7 +9,7 @@ permissions: jobs: test: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest strategy: fail-fast: false matrix: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 288e9329824..ee1b64b6429 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: - id: thundra_test_initializer uses: thundra-io/thundra-test-init-action@v1 find_gradle_jobs: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: @@ -51,7 +51,7 @@ jobs: strategy: fail-fast: false matrix: ${{ fromJson(needs.find_gradle_jobs.outputs.matrix) }} - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-java@v3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 29a0b920922..fedef1c4a52 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ permissions: jobs: release: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-java@v3 diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index 070fa2c55d4..443ba11788f 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -13,7 +13,7 @@ jobs: contents: write # for peter-evans/create-pull-request to create branch pull-requests: write # for peter-evans/create-pull-request to create a PR if: github.repository == 'testcontainers/testcontainers-java' - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 with: From d568e9d2d865f9ec530839d8957941a764825118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Tue, 23 Aug 2022 13:18:24 -0500 Subject: [PATCH 2812/5615] Add samples section (#5763) List examples provided in testcontainers by providing an specific section in the docs. Co-authored-by: Kevin Wittek --- docs/examples.md | 16 ++++++++++++++++ mkdocs.yml | 1 + 2 files changed, 17 insertions(+) create mode 100644 docs/examples.md diff --git a/docs/examples.md b/docs/examples.md new file mode 100644 index 00000000000..abd5e410c21 --- /dev/null +++ b/docs/examples.md @@ -0,0 +1,16 @@ +# Examples + +Examples of different use cases provided by Testcontainers can be found below: + +- [Kafka Cluster with multiple brokers](https://github.com/testcontainers/testcontainers-java/tree/master/examples/kafka-cluster) +- [Linked containers](https://github.com/testcontainers/testcontainers-java/tree/master/examples/linked-container) +- [MongoDB](https://github.com/testcontainers/testcontainers-java/tree/master/examples/mongodb-container) +- [Neo4j](https://github.com/testcontainers/testcontainers-java/tree/master/examples/neo4j-container) +- [Redis](https://github.com/testcontainers/testcontainers-java/tree/master/examples/redis-backed-cache) +- [Selenium](https://github.com/testcontainers/testcontainers-java/tree/master/examples/cucumber) +- [Selenium Module with Cucumber](https://github.com/testcontainers/testcontainers-java/tree/master/examples/cucumber) +- [Singleton Container Pattern](https://github.com/testcontainers/testcontainers-java/tree/master/examples/singleton-container) +- [Solr](https://github.com/testcontainers/testcontainers-java/tree/master/examples/solr-container) +- [Spring Boot](https://github.com/testcontainers/testcontainers-java/tree/master/examples/spring-boot) +- [Spring Boot with Kotlin](https://github.com/testcontainers/testcontainers-java/tree/master/examples/spring-boot-kotlin-redis) +- [TestNG](https://github.com/testcontainers/testcontainers-java/tree/master/examples/redis-backed-cache-testng) diff --git a/mkdocs.yml b/mkdocs.yml index 42011817a5c..87b19de4f0a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -88,6 +88,7 @@ nav: - test_framework_integration/junit_5.md - test_framework_integration/spock.md - test_framework_integration/manual_lifecycle_control.md + - Examples: examples.md - System Requirements: - supported_docker_environment/index.md - Continuous Integration: From 7044cde703809ff1d2330923cd30ceecf6845f47 Mon Sep 17 00:00:00 2001 From: Michael Nitschinger Date: Tue, 23 Aug 2022 23:02:11 +0200 Subject: [PATCH 2813/5615] [couchbase] only expose ports for enabled services (#4595) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changeset is a minor cleanup, but it makes sense to only actually expose the ports for the services which are enabled and not all of them all the time. Co-authored-by: Eddú Meléndez --- .../couchbase/CouchbaseContainer.java | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index 4f190a5413c..8603a0b0787 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -241,22 +241,7 @@ public String getConnectionString() { protected void configure() { super.configure(); - addExposedPorts( - MGMT_PORT, - MGMT_SSL_PORT, - VIEW_PORT, - VIEW_SSL_PORT, - QUERY_PORT, - QUERY_SSL_PORT, - SEARCH_PORT, - SEARCH_SSL_PORT, - ANALYTICS_PORT, - ANALYTICS_SSL_PORT, - KV_PORT, - KV_SSL_PORT, - EVENTING_PORT, - EVENTING_SSL_PORT - ); + exposePorts(); WaitAllStrategy waitStrategy = new WaitAllStrategy(); @@ -319,6 +304,34 @@ protected void configure() { waitingFor(waitStrategy); } + /** + * Configures the exposed ports based on the enabled services. + *

+ * Note that the MGMT_PORTs are always enabled since there must always be a cluster + * manager. Also, the View engine ports are implicitly available on the same nodes + * where the KV service is enabled - it is not possible to configure them individually. + */ + private void exposePorts() { + addExposedPorts(MGMT_PORT, MGMT_SSL_PORT); + + if (enabledServices.contains(CouchbaseService.KV)) { + addExposedPorts(KV_PORT, KV_SSL_PORT); + addExposedPorts(VIEW_PORT, VIEW_SSL_PORT); + } + if (enabledServices.contains(CouchbaseService.ANALYTICS)) { + addExposedPorts(ANALYTICS_PORT, ANALYTICS_SSL_PORT); + } + if (enabledServices.contains(CouchbaseService.QUERY)) { + addExposedPorts(QUERY_PORT, QUERY_SSL_PORT); + } + if (enabledServices.contains(CouchbaseService.SEARCH)) { + addExposedPorts(SEARCH_PORT, SEARCH_SSL_PORT); + } + if (enabledServices.contains(CouchbaseService.EVENTING)) { + addExposedPorts(EVENTING_PORT, EVENTING_SSL_PORT); + } + } + @Override protected void containerIsStarting(final InspectContainerResponse containerInfo) { logger().debug("Couchbase container is starting, performing configuration."); From 62fc6a9cd3a1ab9af19d59612977968e50e9192e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Wed, 24 Aug 2022 04:20:13 -0500 Subject: [PATCH 2814/5615] Upgrade spring-boot version to 2.7.3 in examples (#5764) `driver.findElementsByTagName()` is not available anymore in 2.7.3 --- examples/selenium-container/build.gradle | 2 +- .../src/test/java/SeleniumContainerTest.java | 5 +++-- examples/spring-boot-kotlin-redis/build.gradle | 4 ++-- examples/spring-boot/build.gradle | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index dc10798a060..6183b89e1d6 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.6.4' + id 'org.springframework.boot' version '2.7.3' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/selenium-container/src/test/java/SeleniumContainerTest.java b/examples/selenium-container/src/test/java/SeleniumContainerTest.java index 32411b98ed9..c28c9bc54cb 100644 --- a/examples/selenium-container/src/test/java/SeleniumContainerTest.java +++ b/examples/selenium-container/src/test/java/SeleniumContainerTest.java @@ -2,12 +2,13 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.openqa.selenium.By; import org.openqa.selenium.WebElement; import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.remote.RemoteWebDriver; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.web.context.WebServerInitializedEvent; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; @@ -43,7 +44,7 @@ public void simplePlainSeleniumTest() { RemoteWebDriver driver = chrome.getWebDriver(); driver.get("http://host.testcontainers.internal:" + port + "/foo.html"); - List hElement = driver.findElementsByTagName("h"); + List hElement = driver.findElements(By.tagName("h")); assertThat(hElement).as("The h element is found").isNotEmpty(); } diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 383491eb32d..180d6780c8f 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,5 +1,5 @@ plugins { - id("org.springframework.boot") version "2.6.4" + id("org.springframework.boot") version "2.7.3" id("org.jetbrains.kotlin.jvm") version "1.7.10" id("org.jetbrains.kotlin.plugin.spring") version "1.7.10" } @@ -25,4 +25,4 @@ compileKotlin { jvmTarget = "1.8" freeCompilerArgs = ["-Xjsr305=strict"] } -} \ No newline at end of file +} diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index 650b5a60d98..52a4a63386a 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.6.4' + id 'org.springframework.boot' version '2.7.3' } apply plugin: 'io.spring.dependency-management' From d5ff8c362b36a7a3e1b9ee22b0b1cecce9675f43 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Wed, 24 Aug 2022 11:55:45 +0200 Subject: [PATCH 2815/5615] Revert "Update GHA runner image to latest Ubuntu (#5761)" (#5766) This reverts commit 017d937dc7d2d847f2f412ba37c3db226c4623d5. Necessary to fix CI for the Azure module. --- .github/workflows/ci-docker-wormhole.yml | 2 +- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci-rootless.yml | 2 +- .github/workflows/ci.yml | 4 ++-- .github/workflows/release.yml | 2 +- .github/workflows/update-docs-version.yml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci-docker-wormhole.yml b/.github/workflows/ci-docker-wormhole.yml index ace645e1bb1..e27e4d8d611 100644 --- a/.github/workflows/ci-docker-wormhole.yml +++ b/.github/workflows/ci-docker-wormhole.yml @@ -9,7 +9,7 @@ permissions: jobs: in-docker_test: - runs-on: ubuntu-latest + runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v3 - name: Build with Gradle diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index 70917663e25..f23fc66e889 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -15,7 +15,7 @@ permissions: jobs: find_gradle_jobs: - runs-on: ubuntu-latest + runs-on: ubuntu-18.04 outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: @@ -45,7 +45,7 @@ jobs: strategy: fail-fast: false matrix: ${{ fromJson(needs.find_gradle_jobs.outputs.matrix) }} - runs-on: ubuntu-latest + runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v3 - uses: actions/setup-java@v3 diff --git a/.github/workflows/ci-rootless.yml b/.github/workflows/ci-rootless.yml index 334fe69e9c4..348cf5aaaf7 100644 --- a/.github/workflows/ci-rootless.yml +++ b/.github/workflows/ci-rootless.yml @@ -9,7 +9,7 @@ permissions: jobs: test: - runs-on: ubuntu-latest + runs-on: ubuntu-18.04 strategy: fail-fast: false matrix: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ee1b64b6429..288e9329824 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: - id: thundra_test_initializer uses: thundra-io/thundra-test-init-action@v1 find_gradle_jobs: - runs-on: ubuntu-latest + runs-on: ubuntu-18.04 outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: @@ -51,7 +51,7 @@ jobs: strategy: fail-fast: false matrix: ${{ fromJson(needs.find_gradle_jobs.outputs.matrix) }} - runs-on: ubuntu-latest + runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v3 - uses: actions/setup-java@v3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fedef1c4a52..29a0b920922 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ permissions: jobs: release: - runs-on: ubuntu-latest + runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v3 - uses: actions/setup-java@v3 diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index 443ba11788f..070fa2c55d4 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -13,7 +13,7 @@ jobs: contents: write # for peter-evans/create-pull-request to create branch pull-requests: write # for peter-evans/create-pull-request to create a PR if: github.repository == 'testcontainers/testcontainers-java' - runs-on: ubuntu-latest + runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v3 with: From f6399608f53aece84a813386ee13d1c4360e813b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Thu, 25 Aug 2022 01:55:37 -0500 Subject: [PATCH 2816/5615] Add link to GH Discussions to Issue forms (#5767) --- .github/ISSUE_TEMPLATE/config.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index d27b0d086e9..418d5a62fe9 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -3,3 +3,6 @@ contact_links: - name: Need help or have a question? url: https://slack.testcontainers.org/ about: Visit our slack channel. + - name: Have a question or want to drive a Community conversation? + url: https://github.com/testcontainers/testcontainers-java/discussions/ + about: Visit our Discussions page. From 1b961d8518339ddcfbb44081c59466260a5aec42 Mon Sep 17 00:00:00 2001 From: Aidan Do <43259657+REslim30@users.noreply.github.com> Date: Fri, 26 Aug 2022 01:43:26 +0930 Subject: [PATCH 2817/5615] Set default elasticsearch heap size to 2GB (#5684) Co-authored-by: Kevin Wittek --- .../elasticsearch/ElasticsearchContainer.java | 8 +++ .../elasticsearch-default-memory-vm.options | 2 + .../ElasticsearchContainerTest.java | 50 +++++++++++++++++++ .../resources/test-custom-memory-jvm.options | 2 + 4 files changed, 62 insertions(+) create mode 100644 modules/elasticsearch/src/main/resources/elasticsearch-default-memory-vm.options create mode 100644 modules/elasticsearch/src/test/resources/test-custom-memory-jvm.options diff --git a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java index f8b9f779519..edef04e001e 100644 --- a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java +++ b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java @@ -5,6 +5,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; +import org.testcontainers.containers.BindMode; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.utility.Base58; @@ -98,6 +99,13 @@ public ElasticsearchContainer(final DockerImageName dockerImageName) { logger().info("Starting an elasticsearch container using [{}]", dockerImageName); withNetworkAliases("elasticsearch-" + Base58.randomString(6)); withEnv("discovery.type", "single-node"); + // Sets default memory of elasticsearch instance to 2GB + // Spaces are deliberate to allow user to define additional jvm options as elasticsearch resolves option files lexicographically + withClasspathResourceMapping( + "elasticsearch-default-memory-vm.options", + "/usr/share/elasticsearch/config/jvm.options.d/ elasticsearch-default-memory-vm.options", + BindMode.READ_ONLY + ); addExposedPorts(ELASTICSEARCH_DEFAULT_PORT, ELASTICSEARCH_DEFAULT_TCP_PORT); this.isAtLeastMajorVersion8 = new ComparableVersion(dockerImageName.getVersionPart()).isGreaterThanOrEqualTo("8.0.0"); diff --git a/modules/elasticsearch/src/main/resources/elasticsearch-default-memory-vm.options b/modules/elasticsearch/src/main/resources/elasticsearch-default-memory-vm.options new file mode 100644 index 00000000000..10db7b96157 --- /dev/null +++ b/modules/elasticsearch/src/main/resources/elasticsearch-default-memory-vm.options @@ -0,0 +1,2 @@ +-Xms2147483648 +-Xmx2147483648 diff --git a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java index 1e1e70b5d75..7ee81c82759 100644 --- a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java +++ b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java @@ -19,6 +19,7 @@ import org.junit.After; import org.junit.Test; import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.BindMode; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.images.RemoteDockerImage; @@ -375,6 +376,46 @@ public void testElasticsearch8SecureByDefaultFailsSilentlyOnLatestImages() throw } } + @Test + public void testElasticsearchDefaultMaxHeapSize() throws Exception { + long defaultHeapSize = 2147483648L; + + try (ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE)) { + container.start(); + assertElasticsearchContainerHasHeapSize(container, defaultHeapSize); + } + } + + @Test + public void testElasticsearchCustomMaxHeapSizeInEnvironmentVariable() throws Exception { + long customHeapSize = 1574961152; + + try ( + ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE) + .withEnv("ES_JAVA_OPTS", String.format("-Xms%d -Xmx%d", customHeapSize, customHeapSize)) + ) { + container.start(); + assertElasticsearchContainerHasHeapSize(container, customHeapSize); + } + } + + @Test + public void testElasticsearchCustomMaxHeapSizeInJvmOptionsFile() throws Exception { + long customHeapSize = 1574961152; + + try ( + ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE) + .withClasspathResourceMapping( + "test-custom-memory-jvm.options", + "/usr/share/elasticsearch/config/jvm.options.d/a-user-defined-jvm.options", + BindMode.READ_ONLY + ); + ) { + container.start(); + assertElasticsearchContainerHasHeapSize(container, customHeapSize); + } + } + private void tagImage(String sourceImage, String targetImage, String targetTag) throws InterruptedException { DockerClient dockerClient = DockerClientFactory.instance().client(); dockerClient @@ -438,4 +479,13 @@ private RestClient getAnonymousClient(ElasticsearchContainer container) { return anonymousClient; } + + private void assertElasticsearchContainerHasHeapSize(ElasticsearchContainer container, long heapSizeInBytes) + throws Exception { + Response response = getClient(container).performRequest(new Request("GET", "/_nodes/_all/jvm")); + String responseBody = EntityUtils.toString(response.getEntity()); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); + assertThat(responseBody).contains("\"heap_init_in_bytes\":" + heapSizeInBytes); + assertThat(responseBody).contains("\"heap_max_in_bytes\":" + heapSizeInBytes); + } } diff --git a/modules/elasticsearch/src/test/resources/test-custom-memory-jvm.options b/modules/elasticsearch/src/test/resources/test-custom-memory-jvm.options new file mode 100644 index 00000000000..e3fe586284e --- /dev/null +++ b/modules/elasticsearch/src/test/resources/test-custom-memory-jvm.options @@ -0,0 +1,2 @@ +-Xms1574961152 +-Xmx1574961152 From a18cab6cea2502c8bbb1f4eb7e4b11a6e6d4749d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Fri, 26 Aug 2022 01:07:43 -0600 Subject: [PATCH 2818/5615] Add .sdkmanrc with 8.0.345-tem (#5772) `.sdkmanrc` is used by sdkman to automatically switch to the version declared in the file via env command --- .sdkmanrc | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .sdkmanrc diff --git a/.sdkmanrc b/.sdkmanrc new file mode 100644 index 00000000000..95cd27dd1ad --- /dev/null +++ b/.sdkmanrc @@ -0,0 +1,3 @@ +# Enable auto-env through the sdkman_auto_env config +# Add key=value pairs of SDKs to use below +java=8.0.345-tem From bd22f1e0fbf9a165881d68f6c33e1e4813e2014f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Fri, 26 Aug 2022 09:46:51 -0600 Subject: [PATCH 2819/5615] Add compatibility with MongoDB 6 (#5771) MongoDB 6 removed `mongo` shell and `mongosh` is recommended. See [here](https://www.mongodb.com/docs/manual/release-notes/6.0-compatibility/#legacy-mongo-shell-removed) Closes #5768 --- .../org/testcontainers/containers/MongoDBContainer.java | 8 +++++++- .../testcontainers/containers/MongoDBContainerTest.java | 7 +++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java index 81bb59fd69d..cd2294e09ac 100644 --- a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java @@ -5,6 +5,7 @@ import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.utility.ComparableVersion; import org.testcontainers.utility.DockerImageName; import java.io.IOException; @@ -29,6 +30,8 @@ public class MongoDBContainer extends GenericContainer { private static final String MONGODB_DATABASE_NAME_DEFAULT = "test"; + private final boolean isAtLeastVersion6; + /** * @deprecated use {@link MongoDBContainer(DockerImageName)} instead */ @@ -45,6 +48,8 @@ public MongoDBContainer(final DockerImageName dockerImageName) { super(dockerImageName); dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + this.isAtLeastVersion6 = new ComparableVersion(dockerImageName.getVersionPart()).isGreaterThanOrEqualTo("6.0"); + withExposedPorts(MONGODB_INTERNAL_PORT); withCommand("--replSet", "docker-rs"); waitingFor(Wait.forLogMessage("(?i).*waiting for connections.*", 1)); @@ -87,7 +92,8 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { } private String[] buildMongoEvalCommand(final String command) { - return new String[] { "mongo", "--eval", command }; + String cmd = this.isAtLeastVersion6 ? "mongosh" : "mongo"; + return new String[] { cmd, "--eval", command }; } private void checkMongoNodeExitCode(final Container.ExecResult execResult) { diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java index ff6d0d6ccba..acaa6c8e5d8 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java @@ -98,4 +98,11 @@ public void shouldTestDatabaseName() { assertThat(mongoDBContainer.getReplicaSetUrl(databaseName)).endsWith(databaseName); } } + + @Test + public void supportsMongoDB_6() { + try (final MongoDBContainer mongoDBContainer = new MongoDBContainer("mongo:6.0.1")) { + mongoDBContainer.start(); + } + } } From 84e9a66d31caddc89cd123e6aeaa175d5d946e4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Sat, 27 Aug 2022 02:03:39 -0600 Subject: [PATCH 2820/5615] Exclude `org.testcontainers.shaded.*` package and upgrade deps (#5775) Add general japicmp's package exclusion. Also, shadow and test dependencies below are upgraded: * Upgrade okhttp version to 4.10.0 * Upgrade javassist version to 3.29.0-GA * Upgrade zip4j version to 2.11.1 * Upgrade kubernetes-client version to 6.0.0 --- gradle/japicmp.gradle | 3 +++ modules/azure/build.gradle | 2 +- modules/couchbase/build.gradle | 2 +- modules/hivemq/build.gradle | 4 ++-- modules/k3s/build.gradle | 2 +- .../java/org/testcontainers/k3s/Fabric8K3sContainerTest.java | 4 ++-- modules/mockserver/build.gradle | 2 +- .../containers/MockServerContainerRuleTest.java | 2 +- modules/solr/build.gradle | 2 +- 9 files changed, 13 insertions(+), 10 deletions(-) diff --git a/gradle/japicmp.gradle b/gradle/japicmp.gradle index 3c438d340f0..541736a8321 100644 --- a/gradle/japicmp.gradle +++ b/gradle/japicmp.gradle @@ -25,6 +25,9 @@ tasks.japicmp { onlyBinaryIncompatibleModified = true htmlOutputFile = file("$buildDir/reports/japi.html") + packageExcludes = [ + "org.testcontainers.shaded.*", + ] } // do not run on Windows by default // TODO investigate zip issue on Windows diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle index f8a7193571e..252eb895d37 100644 --- a/modules/azure/build.gradle +++ b/modules/azure/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Azure" dependencies { api project(':testcontainers') // TODO use JDK's HTTP client and/or Apache HttpClient5 - shaded 'com.squareup.okhttp3:okhttp:3.14.9' + shaded 'com.squareup.okhttp3:okhttp:4.10.0' testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'com.azure:azure-cosmos:4.34.0' diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 6cdacbcf77a..5950aac7655 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Couchbase" dependencies { api project(':testcontainers') // TODO use JDK's HTTP client and/or Apache HttpClient5 - shaded 'com.squareup.okhttp3:okhttp:3.14.9' + shaded 'com.squareup.okhttp3:okhttp:4.10.0' testImplementation 'com.couchbase.client:java-client:3.3.3' testImplementation 'org.awaitility:awaitility:4.2.0' diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index a85288f9fbd..9595b3e66b6 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -6,10 +6,10 @@ dependencies { shaded("org.apache.commons:commons-lang3:3.12.0") shaded("commons-io:commons-io:2.11.0") - shaded("org.javassist:javassist:3.28.0-GA") + shaded("org.javassist:javassist:3.29.0-GA") shaded("org.jboss.shrinkwrap:shrinkwrap-api:1.2.6") shaded("org.jboss.shrinkwrap:shrinkwrap-impl-base:1.2.6") - shaded("net.lingala.zip4j:zip4j:2.10.0") + shaded("net.lingala.zip4j:zip4j:2.11.1") testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.0") testImplementation(project(":junit-jupiter")) diff --git a/modules/k3s/build.gradle b/modules/k3s/build.gradle index 2223909f051..8c6792c42e0 100644 --- a/modules/k3s/build.gradle +++ b/modules/k3s/build.gradle @@ -8,7 +8,7 @@ dependencies { // Any >2.8 version here is not compatible with jackson-databind 2.8.x. shaded 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.8.8' - testImplementation 'io.fabric8:kubernetes-client:5.12.2' + testImplementation 'io.fabric8:kubernetes-client:6.0.0' testImplementation 'io.kubernetes:client-java:16.0.0' testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/k3s/src/test/java/org/testcontainers/k3s/Fabric8K3sContainerTest.java b/modules/k3s/src/test/java/org/testcontainers/k3s/Fabric8K3sContainerTest.java index 6f9550eab2a..42b08444555 100644 --- a/modules/k3s/src/test/java/org/testcontainers/k3s/Fabric8K3sContainerTest.java +++ b/modules/k3s/src/test/java/org/testcontainers/k3s/Fabric8K3sContainerTest.java @@ -10,7 +10,7 @@ import io.fabric8.kubernetes.api.model.ProbeBuilder; import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.DefaultKubernetesClient; -import io.fabric8.kubernetes.client.dsl.Readiable; +import io.fabric8.kubernetes.client.dsl.Resource; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.testcontainers.containers.output.Slf4jLogConsumer; @@ -55,7 +55,7 @@ public void shouldStartAndHaveListableNode() { client.pods().inNamespace("default").withName("helloworld").waitUntilReady(30, TimeUnit.SECONDS); assertThat(client.pods().inNamespace("default").withName("helloworld")) - .extracting(Readiable::isReady) + .extracting(Resource::isReady) .isEqualTo(true); } } diff --git a/modules/mockserver/build.gradle b/modules/mockserver/build.gradle index 2156ef045cd..df46295df21 100644 --- a/modules/mockserver/build.gradle +++ b/modules/mockserver/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: MockServer" dependencies { api project(':testcontainers') - testImplementation 'org.mock-server:mockserver-client-java:5.5.4' + testImplementation 'org.mock-server:mockserver-client-java:5.13.2' testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java index 84d472f5e75..0e4fa102b2e 100644 --- a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java +++ b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java @@ -12,7 +12,7 @@ public class MockServerContainerRuleTest { public static final DockerImageName MOCKSERVER_IMAGE = DockerImageName.parse( - "jamesdbloom/mockserver:mockserver-5.5.4" + "jamesdbloom/mockserver:mockserver-5.13.2" ); // creatingProxy { diff --git a/modules/solr/build.gradle b/modules/solr/build.gradle index f8e2a99888d..72ab2997dea 100644 --- a/modules/solr/build.gradle +++ b/modules/solr/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Solr" dependencies { api project(':testcontainers') // TODO use JDK's HTTP client and/or Apache HttpClient5 - shaded 'com.squareup.okhttp3:okhttp:4.9.3' + shaded 'com.squareup.okhttp3:okhttp:4.10.0' testImplementation 'org.apache.solr:solr-solrj:8.11.1' testImplementation 'org.assertj:assertj-core:3.23.1' From 42a7441f59bdaa6315af202dfe7afa7e7b579be0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Mon, 29 Aug 2022 09:05:39 -0600 Subject: [PATCH 2821/5615] Run mongosh or mongo if cmd exists (#5774) Previously, the command execution relied on the image version which is different when building custom images. Instead, `mongosh` or `mongo` is executed if it is present within the PATH. --- .../containers/MongoDBContainer.java | 12 +-- .../containers/MongoDBContainerTest.java | 92 ++++++++++--------- 2 files changed, 53 insertions(+), 51 deletions(-) diff --git a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java index cd2294e09ac..ae93e4121d3 100644 --- a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java @@ -5,7 +5,6 @@ import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.testcontainers.containers.wait.strategy.Wait; -import org.testcontainers.utility.ComparableVersion; import org.testcontainers.utility.DockerImageName; import java.io.IOException; @@ -30,8 +29,6 @@ public class MongoDBContainer extends GenericContainer { private static final String MONGODB_DATABASE_NAME_DEFAULT = "test"; - private final boolean isAtLeastVersion6; - /** * @deprecated use {@link MongoDBContainer(DockerImageName)} instead */ @@ -48,8 +45,6 @@ public MongoDBContainer(final DockerImageName dockerImageName) { super(dockerImageName); dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); - this.isAtLeastVersion6 = new ComparableVersion(dockerImageName.getVersionPart()).isGreaterThanOrEqualTo("6.0"); - withExposedPorts(MONGODB_INTERNAL_PORT); withCommand("--replSet", "docker-rs"); waitingFor(Wait.forLogMessage("(?i).*waiting for connections.*", 1)); @@ -92,8 +87,11 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { } private String[] buildMongoEvalCommand(final String command) { - String cmd = this.isAtLeastVersion6 ? "mongosh" : "mongo"; - return new String[] { cmd, "--eval", command }; + return new String[] { + "sh", + "-c", + "mongosh mongo --eval \"" + command + "\" || mongo --eval \"" + command + "\"", + }; } private void checkMongoNodeExitCode(final Container.ExecResult execResult) { diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java index acaa6c8e5d8..dd59420f937 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java @@ -30,56 +30,59 @@ public void shouldExecuteTransactions() { // startingMongoDBContainer { mongoDBContainer.start(); // } + executeTx(mongoDBContainer); + } + } - final String mongoRsUrl = mongoDBContainer.getReplicaSetUrl(); - assertThat(mongoRsUrl).isNotNull(); - final String connectionString = mongoDBContainer.getConnectionString(); - final MongoClient mongoSyncClientBase = MongoClients.create(connectionString); - final MongoClient mongoSyncClient = MongoClients.create(mongoRsUrl); - mongoSyncClient - .getDatabase("mydb1") - .getCollection("foo") - .withWriteConcern(WriteConcern.MAJORITY) - .insertOne(new Document("abc", 0)); - mongoSyncClient - .getDatabase("mydb2") - .getCollection("bar") - .withWriteConcern(WriteConcern.MAJORITY) - .insertOne(new Document("xyz", 0)); - mongoSyncClientBase - .getDatabase("mydb3") - .getCollection("baz") - .withWriteConcern(WriteConcern.MAJORITY) - .insertOne(new Document("def", 0)); + private void executeTx(MongoDBContainer mongoDBContainer) { + final String mongoRsUrl = mongoDBContainer.getReplicaSetUrl(); + assertThat(mongoRsUrl).isNotNull(); + final String connectionString = mongoDBContainer.getConnectionString(); + final MongoClient mongoSyncClientBase = MongoClients.create(connectionString); + final MongoClient mongoSyncClient = MongoClients.create(mongoRsUrl); + mongoSyncClient + .getDatabase("mydb1") + .getCollection("foo") + .withWriteConcern(WriteConcern.MAJORITY) + .insertOne(new Document("abc", 0)); + mongoSyncClient + .getDatabase("mydb2") + .getCollection("bar") + .withWriteConcern(WriteConcern.MAJORITY) + .insertOne(new Document("xyz", 0)); + mongoSyncClientBase + .getDatabase("mydb3") + .getCollection("baz") + .withWriteConcern(WriteConcern.MAJORITY) + .insertOne(new Document("def", 0)); - final ClientSession clientSession = mongoSyncClient.startSession(); - final TransactionOptions txnOptions = TransactionOptions - .builder() - .readPreference(ReadPreference.primary()) - .readConcern(ReadConcern.LOCAL) - .writeConcern(WriteConcern.MAJORITY) - .build(); + final ClientSession clientSession = mongoSyncClient.startSession(); + final TransactionOptions txnOptions = TransactionOptions + .builder() + .readPreference(ReadPreference.primary()) + .readConcern(ReadConcern.LOCAL) + .writeConcern(WriteConcern.MAJORITY) + .build(); - final String trxResult = "Inserted into collections in different databases"; + final String trxResult = "Inserted into collections in different databases"; - TransactionBody txnBody = () -> { - final MongoCollection coll1 = mongoSyncClient.getDatabase("mydb1").getCollection("foo"); - final MongoCollection coll2 = mongoSyncClient.getDatabase("mydb2").getCollection("bar"); + TransactionBody txnBody = () -> { + final MongoCollection coll1 = mongoSyncClient.getDatabase("mydb1").getCollection("foo"); + final MongoCollection coll2 = mongoSyncClient.getDatabase("mydb2").getCollection("bar"); - coll1.insertOne(clientSession, new Document("abc", 1)); - coll2.insertOne(clientSession, new Document("xyz", 999)); - return trxResult; - }; + coll1.insertOne(clientSession, new Document("abc", 1)); + coll2.insertOne(clientSession, new Document("xyz", 999)); + return trxResult; + }; - try { - final String trxResultActual = clientSession.withTransaction(txnBody, txnOptions); - assertThat(trxResultActual).isEqualTo(trxResult); - } catch (RuntimeException re) { - throw new IllegalStateException(re.getMessage(), re); - } finally { - clientSession.close(); - mongoSyncClient.close(); - } + try { + final String trxResultActual = clientSession.withTransaction(txnBody, txnOptions); + assertThat(trxResultActual).isEqualTo(trxResult); + } catch (RuntimeException re) { + throw new IllegalStateException(re.getMessage(), re); + } finally { + clientSession.close(); + mongoSyncClient.close(); } } @@ -103,6 +106,7 @@ public void shouldTestDatabaseName() { public void supportsMongoDB_6() { try (final MongoDBContainer mongoDBContainer = new MongoDBContainer("mongo:6.0.1")) { mongoDBContainer.start(); + executeTx(mongoDBContainer); } } } From b57cbce1ac6f35d698a0f663a61ec0096981e431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Mon, 29 Aug 2022 10:51:21 -0600 Subject: [PATCH 2822/5615] Remove `disque-job-queue` and `mongodb-container` examples (#5782) Examples should provided useful usage of technologies. In this case, `disque-job-queue` seems to provide a not up-to-date image and `mongodb-container` already provides its own module. --- docs/examples.md | 1 - examples/disque-job-queue/build.gradle | 19 ---- .../java/DisqueBackedMailSenderService.java | 73 --------------- .../src/main/java/MailApiClient.java | 11 --- .../src/main/java/MailMessage.java | 12 --- .../src/main/java/MailSenderService.java | 45 ---------- .../test/java/DisqueBackedMailSenderTest.java | 89 ------------------- .../test/java/SingleDisqueInstanceTest.java | 61 ------------- .../src/test/resources/logback-test.xml | 14 --- examples/mongodb-container/build.gradle | 13 --- .../containers/MongoDbContainer.java | 64 ------------- .../containers/MongoDbContainerTest.java | 28 ------ examples/settings.gradle | 2 - 13 files changed, 432 deletions(-) delete mode 100644 examples/disque-job-queue/build.gradle delete mode 100644 examples/disque-job-queue/src/main/java/DisqueBackedMailSenderService.java delete mode 100644 examples/disque-job-queue/src/main/java/MailApiClient.java delete mode 100644 examples/disque-job-queue/src/main/java/MailMessage.java delete mode 100644 examples/disque-job-queue/src/main/java/MailSenderService.java delete mode 100644 examples/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java delete mode 100644 examples/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java delete mode 100644 examples/disque-job-queue/src/test/resources/logback-test.xml delete mode 100644 examples/mongodb-container/build.gradle delete mode 100644 examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainer.java delete mode 100644 examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java diff --git a/docs/examples.md b/docs/examples.md index abd5e410c21..2c6036ab2ea 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -4,7 +4,6 @@ Examples of different use cases provided by Testcontainers can be found below: - [Kafka Cluster with multiple brokers](https://github.com/testcontainers/testcontainers-java/tree/master/examples/kafka-cluster) - [Linked containers](https://github.com/testcontainers/testcontainers-java/tree/master/examples/linked-container) -- [MongoDB](https://github.com/testcontainers/testcontainers-java/tree/master/examples/mongodb-container) - [Neo4j](https://github.com/testcontainers/testcontainers-java/tree/master/examples/neo4j-container) - [Redis](https://github.com/testcontainers/testcontainers-java/tree/master/examples/redis-backed-cache) - [Selenium](https://github.com/testcontainers/testcontainers-java/tree/master/examples/cucumber) diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle deleted file mode 100644 index 6c7a453c3e9..00000000000 --- a/examples/disque-job-queue/build.gradle +++ /dev/null @@ -1,19 +0,0 @@ -plugins { - id 'java' -} - -repositories { - mavenCentral() -} - -dependencies { - compileOnly "org.projectlombok:lombok:1.18.24" - annotationProcessor "org.projectlombok:lombok:1.18.24" - implementation 'biz.paluch.redis:spinach:0.3' - implementation 'com.google.code.gson:gson:2.9.1' - implementation 'com.google.guava:guava:23.0' - testImplementation 'ch.qos.logback:logback-classic:1.2.11' - testImplementation 'org.mockito:mockito-all:1.10.19' - testImplementation 'org.testcontainers:testcontainers' - testImplementation 'org.assertj:assertj-core:3.23.1' -} diff --git a/examples/disque-job-queue/src/main/java/DisqueBackedMailSenderService.java b/examples/disque-job-queue/src/main/java/DisqueBackedMailSenderService.java deleted file mode 100644 index d2096106500..00000000000 --- a/examples/disque-job-queue/src/main/java/DisqueBackedMailSenderService.java +++ /dev/null @@ -1,73 +0,0 @@ -import biz.paluch.spinach.DisqueClient; -import biz.paluch.spinach.api.Job; -import biz.paluch.spinach.api.sync.DisqueCommands; -import com.google.gson.Gson; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -/** - * Simple example of how a mail service can be backed by a Disque job queue. - * - * It's assumed that for actual sending (SMTP, HTTP API, etc), this service calls a {@link MailApiClient}. - * However, this can be expected to be quite unreliable, so use of Disque as a backing store helps keep track - * of sent/failed state. - */ -public class DisqueBackedMailSenderService implements MailSenderService { - - private final DisqueCommands disque; - private final Gson gson; - private final MailApiClient mailApiClient; - - public DisqueBackedMailSenderService(DisqueClient client, MailApiClient mailApiClient) { - this.mailApiClient = mailApiClient; - - // Obtain a Disque connection from DisqueClient - disque = client.connect().sync(); - - gson = new Gson(); - } - - @Override - public String enqueueMessage(MailMessage message) { - String messageAsJson = gson.toJson(message); - - // Enqueue the message as JSON, setting a TTL of 1 day (will expire off the queue if not sent by then) - String jobId = disque.addjob("mail", messageAsJson, 1, TimeUnit.DAYS); - - return jobId; - } - - @Override - public Result doScheduledSend() { - - Set succeededJobIds = new HashSet<>(); - Set failedJobIds = new HashSet<>(); - - // Wait up to 100ms for new messages to arrive on the queue - // Retrieve up to 10 messages - // (Ordinarily we'd make these configurable!) - List> jobs = disque.getjobs(100, TimeUnit.MILLISECONDS, 10, "mail"); - - for (Job job : jobs) { - String jsonBody = job.getBody(); - MailMessage message = gson.fromJson(jsonBody, MailMessage.class); - - try { - mailApiClient.send(message); - succeededJobIds.add(job.getId()); - } catch (MailApiClient.MailSendException e) { - failedJobIds.add(job.getId()); - } - } - - // For any failed messages, proactively return to the queue - disque.nack(failedJobIds.toArray(new String[failedJobIds.size()])); - // For any successful jobs, mark as completed - disque.ackjob(succeededJobIds.toArray(new String[succeededJobIds.size()])); - - return new Result(succeededJobIds.size(), failedJobIds.size()); - } -} diff --git a/examples/disque-job-queue/src/main/java/MailApiClient.java b/examples/disque-job-queue/src/main/java/MailApiClient.java deleted file mode 100644 index 61a90112f32..00000000000 --- a/examples/disque-job-queue/src/main/java/MailApiClient.java +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Created by rnorth on 03/01/2016. - */ -public interface MailApiClient { - - boolean send(MailMessage message) throws MailSendException; - - class MailSendException extends RuntimeException { - - } -} diff --git a/examples/disque-job-queue/src/main/java/MailMessage.java b/examples/disque-job-queue/src/main/java/MailMessage.java deleted file mode 100644 index de167199057..00000000000 --- a/examples/disque-job-queue/src/main/java/MailMessage.java +++ /dev/null @@ -1,12 +0,0 @@ -import lombok.AllArgsConstructor; -import lombok.Data; - -/** - * Created by rnorth on 03/01/2016. - */ -@Data @AllArgsConstructor -public class MailMessage { - public final String subject; - public final String recipient; - public final String body; -} diff --git a/examples/disque-job-queue/src/main/java/MailSenderService.java b/examples/disque-job-queue/src/main/java/MailSenderService.java deleted file mode 100644 index 95237bf2eec..00000000000 --- a/examples/disque-job-queue/src/main/java/MailSenderService.java +++ /dev/null @@ -1,45 +0,0 @@ -import lombok.Data; -import lombok.RequiredArgsConstructor; - -import java.util.concurrent.Callable; - -/** - * Created by rnorth on 03/01/2016. - */ -public interface MailSenderService extends Callable { - - /** - * Enqueue a message to be sent on a subsequent {@link DisqueBackedMailSenderService#doScheduledSend()} - * @param message mail message to be sent - * @return an ID for the enqueued message - */ - String enqueueMessage(MailMessage message); - - /** - * Trigger a scheduled send of mail messages. - * - * TODO: In a real implementation this could be called by a {@link java.util.concurrent.ScheduledExecutorService}, - * probably - * - * @return the result of sending queued messages. - */ - MailSenderService.Result doScheduledSend(); - - /** - * call() implementation to allow MailSenderService to be used as a Callable. Simply delegates to - * the {@link DisqueBackedMailSenderService#doScheduledSend()} method. - * - * @return the result of sending queued messages. - * @throws Exception - */ - default MailSenderService.Result call() throws Exception { - return doScheduledSend(); - } - - @Data - @RequiredArgsConstructor - class Result { - public final int successfulCount; - public final int failedCount; - } -} diff --git a/examples/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java b/examples/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java deleted file mode 100644 index dfbab3f9e6a..00000000000 --- a/examples/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java +++ /dev/null @@ -1,89 +0,0 @@ -import biz.paluch.spinach.DisqueClient; -import biz.paluch.spinach.DisqueURI; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.testcontainers.containers.GenericContainer; -import org.testcontainers.utility.DockerImageName; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -/** - * Created by rnorth on 03/01/2016. - */ -public class DisqueBackedMailSenderTest { - - @Rule - public GenericContainer container = new GenericContainer<>(DockerImageName.parse("richnorth/disque:1.0-rc1")) - .withExposedPorts(7711); - private DisqueClient disqueClient; - - private MailSenderService service; - private MailApiClient mockMailApiClient; - - private MailMessage dummyMessage1; - private MailMessage dummyMessage2; - private MailMessage dummyMessage3; - - @Before - public void setup() { - disqueClient = new DisqueClient(DisqueURI.create(container.getHost(), container.getMappedPort(7711))); - mockMailApiClient = mock(MailApiClient.class); - - service = new DisqueBackedMailSenderService(disqueClient, mockMailApiClient); - - dummyMessage1 = new MailMessage("Dummy Message 1", "bob@example.com", "Test body"); - dummyMessage2 = new MailMessage("Dummy Message 2", "bob@example.com", "Test body"); - dummyMessage3 = new MailMessage("Dummy Message 3", "bob@example.com", "Test body"); - } - - @Test - public void testSimpleSuccessfulSending() throws Exception { - service.enqueueMessage(dummyMessage1); - service.enqueueMessage(dummyMessage2); - service.enqueueMessage(dummyMessage3); - - when(mockMailApiClient.send(any(MailMessage.class))).thenReturn(true); - - service.doScheduledSend(); - - verify(mockMailApiClient).send(eq(dummyMessage1)); - verify(mockMailApiClient).send(eq(dummyMessage2)); - verify(mockMailApiClient).send(eq(dummyMessage3)); - } - - @Test - public void testRetryOnFailure() throws Exception { - service.enqueueMessage(dummyMessage1); - service.enqueueMessage(dummyMessage2); - service.enqueueMessage(dummyMessage3); - - when(mockMailApiClient.send(eq(dummyMessage1))).thenThrow(MailApiClient.MailSendException.class).thenReturn(true); - when(mockMailApiClient.send(eq(dummyMessage2))).thenReturn(true); - when(mockMailApiClient.send(eq(dummyMessage3))).thenReturn(true); - - MailSenderService.Result result; - - result = service.doScheduledSend(); - assertThat(result.successfulCount).as("2 messages were 'sent' successfully").isEqualTo(2); - assertThat(result.failedCount).as("1 messages failed").isEqualTo(1); - - result = service.doScheduledSend(); - assertThat(result.successfulCount).as("1 message was 'sent' successfully").isEqualTo(1); - assertThat(result.failedCount).as("0 messages failed").isZero(); - - result = service.doScheduledSend(); - assertThat(result.successfulCount).as("0 messages were 'sent' successfully").isZero(); - assertThat(result.failedCount).as("0 messages failed").isZero(); - - verify(mockMailApiClient, times(2)).send(eq(dummyMessage1)); - verify(mockMailApiClient).send(eq(dummyMessage2)); - verify(mockMailApiClient).send(eq(dummyMessage3)); - } -} diff --git a/examples/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java b/examples/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java deleted file mode 100644 index 09830614eb0..00000000000 --- a/examples/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java +++ /dev/null @@ -1,61 +0,0 @@ -import biz.paluch.spinach.DisqueClient; -import biz.paluch.spinach.DisqueURI; -import biz.paluch.spinach.api.AddJobArgs; -import biz.paluch.spinach.api.Job; -import biz.paluch.spinach.api.sync.DisqueCommands; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.testcontainers.containers.GenericContainer; -import org.testcontainers.utility.DockerImageName; - -import java.util.concurrent.TimeUnit; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Created by rnorth on 03/01/2016. - */ -public class SingleDisqueInstanceTest { - - @Rule - public GenericContainer container = new GenericContainer<>(DockerImageName.parse("richnorth/disque:1.0-rc1")) - .withExposedPorts(7711); - private DisqueCommands connection; - private AddJobArgs retryAfter1Second; - - @Before - public void setup() { - DisqueClient client = new DisqueClient(DisqueURI.create(container.getHost(), container.getMappedPort(7711))); - connection = client.connect().sync(); - retryAfter1Second = AddJobArgs.builder().retry(1, TimeUnit.SECONDS).build(); - } - - @Test - public void testJobStoreAndRetrieve() { - connection.addjob("main_queue", "body", 1, TimeUnit.MINUTES); - - Job job = connection.getjob("main_queue"); - - assertThat(job.getBody()).as("The retrieved job is the same as the one that was added").isEqualTo("body"); - - connection.ackjob(job.getId()); - } - - @Test - public void testFailureToAckJobBeforeTimeout() throws InterruptedException { - connection.addjob("main_queue", "body", 1, TimeUnit.MINUTES, retryAfter1Second); - - Job job = connection.getjob("main_queue"); - - assertThat(job.getBody()).as("The retrieved job is the same as the one that was added").isEqualTo("body"); - - TimeUnit.SECONDS.sleep(2); - - // The timeout specified here is how long the command will wait for a job to appear - Job job2 = connection.getjob(5, TimeUnit.SECONDS, "main_queue"); - - assertThat(job2).as("After re-getting the original job is back on the queue").isNotNull(); - assertThat(job2.getBody()).as("The retrieved job is the same as the one that was added").isEqualTo("body"); - } -} diff --git a/examples/disque-job-queue/src/test/resources/logback-test.xml b/examples/disque-job-queue/src/test/resources/logback-test.xml deleted file mode 100644 index be4ebaed2c8..00000000000 --- a/examples/disque-job-queue/src/test/resources/logback-test.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - %d{HH:mm:ss.SSS} %-5level %logger - %msg%n - - - - - - - diff --git a/examples/mongodb-container/build.gradle b/examples/mongodb-container/build.gradle deleted file mode 100644 index 1ae43055790..00000000000 --- a/examples/mongodb-container/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ -plugins { - id 'java' -} - -repositories { - mavenCentral() -} - -dependencies { - testImplementation 'org.testcontainers:testcontainers' - testImplementation 'junit:junit:4.13.2' - testCompileClasspath 'org.jetbrains:annotations:23.0.0' -} diff --git a/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainer.java b/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainer.java deleted file mode 100644 index c4c07c1f601..00000000000 --- a/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainer.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.testcontainers.containers; - -import org.jetbrains.annotations.NotNull; -import org.testcontainers.utility.DockerImageName; - - -/** - * This {@link Container} is based on the official MongoDb ({@code mongo}) image from - * DockerHub. If you need to use a custom MongoDB - * image, you can provide the full image name as well. - * - * @author Stefan Ludwig - */ -public class MongoDbContainer extends GenericContainer { - - /** - * This is the internal port on which MongoDB is running inside the container. - *

- * You can use this constant in case you want to map an explicit public port to it - * instead of the default random port. This can be done using methods like - * {@link #setPortBindings(java.util.List)}. - */ - public static final int MONGODB_PORT = 27017; - public static final String DEFAULT_IMAGE_AND_TAG = "mongo:4.0"; - - /** - * Creates a new {@link MongoDbContainer} with the {@value DEFAULT_IMAGE_AND_TAG} image. - * @deprecated use {@link MongoDbContainer(DockerImageName)} instead - */ - @Deprecated - public MongoDbContainer() { - this(DEFAULT_IMAGE_AND_TAG); - } - - /** - * Creates a new {@link MongoDbContainer} with the given {@code 'image'}. - * - * @param image the image (e.g. {@value DEFAULT_IMAGE_AND_TAG}) to use - * @deprecated use {@link MongoDbContainer(DockerImageName)} instead - */ - public MongoDbContainer(@NotNull String image) { - this(DockerImageName.parse(image)); - } - - /** - * Creates a new {@link MongoDbContainer} with the specified image. - */ - public MongoDbContainer(final DockerImageName dockerImageName) { - super(dockerImageName); - addExposedPort(MONGODB_PORT); - } - - /** - * Returns the actual public port of the internal MongoDB port ({@value MONGODB_PORT}). - * - * @return the public port of this container - * @see #getMappedPort(int) - */ - @NotNull - public Integer getPort() { - return getMappedPort(MONGODB_PORT); - } - -} diff --git a/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java b/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java deleted file mode 100644 index 5581c76e675..00000000000 --- a/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.testcontainers.containers; - -import org.junit.Test; -import org.testcontainers.utility.DockerImageName; - -import java.io.IOException; -import java.net.Socket; - - -public class MongoDbContainerTest { - - @Test - public void containerStartsAndPublicPortIsAvailable() { - try (MongoDbContainer container = new MongoDbContainer(DockerImageName.parse("mongo:4.0"))) { - container.start(); - assertThatPortIsAvailable(container); - } - } - - private void assertThatPortIsAvailable(MongoDbContainer container) { - try { - new Socket(container.getHost(), container.getPort()); - } catch (IOException e) { - throw new AssertionError("The expected port " + container.getPort() + " is not available!"); - } - } - -} diff --git a/examples/settings.gradle b/examples/settings.gradle index 9ed079cd7b8..7194134350d 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -19,10 +19,8 @@ rootProject.name = 'testcontainers-examples' includeBuild '..' // explicit include to allow Dependabot to autodiscover subprojects -include 'disque-job-queue' include 'kafka-cluster' include 'linked-container' -include 'mongodb-container' include 'neo4j-container' include 'redis-backed-cache' include 'redis-backed-cache-testng' From ffb88b3d80004ad569caf080c00304584352b9db Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Wed, 31 Aug 2022 16:05:28 +0200 Subject: [PATCH 2823/5615] Fix link for Selenium examples (#5786) --- docs/examples.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples.md b/docs/examples.md index 2c6036ab2ea..918688d86cb 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -6,7 +6,7 @@ Examples of different use cases provided by Testcontainers can be found below: - [Linked containers](https://github.com/testcontainers/testcontainers-java/tree/master/examples/linked-container) - [Neo4j](https://github.com/testcontainers/testcontainers-java/tree/master/examples/neo4j-container) - [Redis](https://github.com/testcontainers/testcontainers-java/tree/master/examples/redis-backed-cache) -- [Selenium](https://github.com/testcontainers/testcontainers-java/tree/master/examples/cucumber) +- [Selenium](https://github.com/testcontainers/testcontainers-java/tree/master/examples/selenium-container) - [Selenium Module with Cucumber](https://github.com/testcontainers/testcontainers-java/tree/master/examples/cucumber) - [Singleton Container Pattern](https://github.com/testcontainers/testcontainers-java/tree/master/examples/singleton-container) - [Solr](https://github.com/testcontainers/testcontainers-java/tree/master/examples/solr-container) From fc3d60099165c9f73689ee3269370c5347492513 Mon Sep 17 00:00:00 2001 From: Aidan Do <43259657+REslim30@users.noreply.github.com> Date: Thu, 1 Sep 2022 06:08:31 +0930 Subject: [PATCH 2824/5615] Allow `getContainerByServiceName` to receive serviceName without suffix (#5776) Fixes #5773 --- .../containers/DockerComposeContainer.java | 3 ++- .../junit/DockerComposeContainerTest.java | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 14124d88c28..683e9d9fdf5 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -611,7 +611,8 @@ public SELF withStartupTimeout(Duration startupTimeout) { } public Optional getContainerByServiceName(String serviceName) { - return Optional.ofNullable(serviceInstanceMap.get(serviceName)); + String serviceInstantName = getServiceInstanceName(serviceName); + return Optional.ofNullable(serviceInstanceMap.get(serviceInstantName)); } private void followLogs(String containerId, Consumer consumer) { diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java index c610fc584af..96989af90a4 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java @@ -44,6 +44,7 @@ public void testGetServicePort() { public void shouldRetrieveContainerByServiceName() { String existingServiceName = "db_1"; Optional result = environment.getContainerByServiceName(existingServiceName); + assertThat(result) .as(String.format("Container should be found by service name %s", existingServiceName)) .isPresent(); @@ -52,6 +53,19 @@ public void shouldRetrieveContainerByServiceName() { .isEqualTo(result.get().getExposedPorts()); } + @Test + public void shouldRetrieveContainerByServiceNameWithoutNumberedSuffix() { + String existingServiceName = "db"; + Optional result = environment.getContainerByServiceName(existingServiceName); + + assertThat(result) + .as(String.format("Container should be found by service name %s", existingServiceName)) + .isPresent(); + assertThat(result.get().getExposedPorts()) + .as("Mapped port for result container was wrong, perhaps wrong container was found") + .isEqualTo(Collections.singletonList(3306)); + } + @Test public void shouldReturnEmptyResultOnNoneExistingService() { String notExistingServiceName = "db_256"; From 3ef2c5177f412135d5c2410abb6bcf31619b20e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:05:15 +0000 Subject: [PATCH 2825/5615] Bump slf4j-api from 1.7.36 to 2.0.0 in /examples Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 1.7.36 to 2.0.0. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_1.7.36...v_2.0.0) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 5b690597eaf..46d038e4643 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -6,7 +6,7 @@ repositories { mavenCentral() } dependencies { - compileOnly 'org.slf4j:slf4j-api:1.7.36' + compileOnly 'org.slf4j:slf4j-api:2.0.0' implementation 'com.squareup.okhttp3:okhttp:4.10.0' implementation 'org.json:json:20220320' testImplementation 'org.postgresql:postgresql:42.4.2' diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 9d0fd118f61..799f3f89925 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:1.7.36' + compileOnly 'org.slf4j:slf4j-api:2.0.0' implementation 'redis.clients:jedis:4.2.3' implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index c5f365b5502..1d840f11871 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:1.7.36' + compileOnly 'org.slf4j:slf4j-api:2.0.0' implementation 'redis.clients:jedis:4.2.3' implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 89f067d3a19..d540a8386ab 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -11,7 +11,7 @@ dependencies { implementation 'redis.clients:jedis:4.2.3' implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' - compileOnly 'org.slf4j:slf4j-api:1.7.36' + compileOnly 'org.slf4j:slf4j-api:2.0.0' testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.testcontainers:testcontainers' From b83777389a000df475bd7b033551f20659d2820b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:05:22 +0000 Subject: [PATCH 2826/5615] Bump postgresql from 42.4.2 to 42.5.0 in /examples Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.2 to 42.5.0. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.2...REL42.5.0) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 5b690597eaf..694261f796a 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.36' implementation 'com.squareup.okhttp3:okhttp:4.10.0' implementation 'org.json:json:20220320' - testImplementation 'org.postgresql:postgresql:42.4.2' + testImplementation 'org.postgresql:postgresql:42.5.0' testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.testcontainers:postgresql' testImplementation 'org.assertj:assertj-core:3.23.1' From 3b006da54b3f4133564cc6a71eea152320b8ef88 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:05:24 +0000 Subject: [PATCH 2827/5615] Bump aws-java-sdk-s3 from 1.12.285 to 1.12.295 in /modules/localstack Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.285 to 1.12.295. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.285...1.12.295) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index ad43f6f9c08..22966424b31 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.285' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.295' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.285' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.285' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.285' From 605a769acdf745ed13bb9e8b0153b1c82f3b52d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:05:25 +0000 Subject: [PATCH 2828/5615] Bump slf4j-api from 1.7.36 to 2.0.0 in /core Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 1.7.36 to 2.0.0. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_1.7.36...v_2.0.0) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index aa4c6eabf22..f37a6478b88 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -61,7 +61,7 @@ configurations.all { dependencies { api 'junit:junit:4.13.2' - api 'org.slf4j:slf4j-api:1.7.36' + api 'org.slf4j:slf4j-api:2.0.0' compileOnly 'org.jetbrains:annotations:23.0.0' testCompileClasspath 'org.jetbrains:annotations:23.0.0' api 'org.apache.commons:commons-compress:1.21' From d8e674fb1ee4b351b33d3f598220a7af100c0eb5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:05:28 +0000 Subject: [PATCH 2829/5615] Bump snakeyaml from 1.30 to 1.31 in /core Bumps [snakeyaml](https://bitbucket.org/snakeyaml/snakeyaml) from 1.30 to 1.31. - [Commits](https://bitbucket.org/snakeyaml/snakeyaml/branches/compare/snakeyaml-1.31..snakeyaml-1.30) --- updated-dependencies: - dependency-name: org.yaml:snakeyaml dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index aa4c6eabf22..b2f1a71f643 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -80,7 +80,7 @@ dependencies { api 'com.github.docker-java:docker-java-transport-zerodep' - shaded "org.yaml:snakeyaml:1.30" + shaded "org.yaml:snakeyaml:1.31" shaded 'org.glassfish.main.external:trilead-ssh2-repackaged:4.1.2' From b6a066fea2d04dcee16f2da3bd9b63dfcf19a7c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:05:32 +0000 Subject: [PATCH 2830/5615] Bump aws-java-sdk-logs from 1.12.285 to 1.12.295 in /modules/localstack Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.12.285 to 1.12.295. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.285...1.12.295) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-logs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index ad43f6f9c08..2dc763c860f 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,7 +6,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.285' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.285' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.285' - testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.285' + testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.295' testImplementation 'software.amazon.awssdk:s3:2.17.256' testImplementation 'org.assertj:assertj-core:3.23.1' } From 05da3a213d12e3aad87379264c89de725c94c7f7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:05:43 +0000 Subject: [PATCH 2831/5615] Bump aws-java-sdk-dynamodb in /modules/dynalite Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.12.285 to 1.12.295. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.285...1.12.295) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-dynamodb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/dynalite/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 8b548d5b91f..ac9ff346bea 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Dynalite" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.285' - testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.281' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.295' + testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.295' testImplementation 'org.assertj:assertj-core:3.23.1' } From 19d604067a49d6a78771008b44d86cf28f12309a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:05:43 +0000 Subject: [PATCH 2832/5615] Bump postgresql from 42.4.2 to 42.5.0 in /modules/postgresql Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.2 to 42.5.0. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.2...REL42.5.0) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 0c5035df549..59515ed7549 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -11,7 +11,7 @@ dependencies { testImplementation project(':jdbc-test') testImplementation project(':test-support') - testImplementation 'org.postgresql:postgresql:42.4.2' + testImplementation 'org.postgresql:postgresql:42.5.0' testImplementation testFixtures(project(':r2dbc')) testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.11.RELEASE' From ea3b3ad40ccc19db00580b6911110c56e7b4065c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:05:48 +0000 Subject: [PATCH 2833/5615] Bump r2dbc-postgresql in /modules/postgresql Bumps [r2dbc-postgresql](https://github.com/r2dbc/r2dbc-postgresql) from 0.8.12.RELEASE to 0.8.13.RELEASE. - [Release notes](https://github.com/r2dbc/r2dbc-postgresql/releases) - [Changelog](https://github.com/pgjdbc/r2dbc-postgresql/blob/v0.8.13.RELEASE/CHANGELOG) - [Commits](https://github.com/r2dbc/r2dbc-postgresql/compare/v0.8.12.RELEASE...v0.8.13.RELEASE) --- updated-dependencies: - dependency-name: io.r2dbc:r2dbc-postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/postgresql/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 0c5035df549..15a39d97766 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -7,14 +7,14 @@ dependencies { api project(':jdbc') compileOnly project(':r2dbc') - compileOnly 'io.r2dbc:r2dbc-postgresql:0.8.12.RELEASE' + compileOnly 'io.r2dbc:r2dbc-postgresql:0.8.13.RELEASE' testImplementation project(':jdbc-test') testImplementation project(':test-support') testImplementation 'org.postgresql:postgresql:42.4.2' testImplementation testFixtures(project(':r2dbc')) - testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.11.RELEASE' + testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.13.RELEASE' compileOnly 'org.jetbrains:annotations:23.0.0' } From f6151a20abb84ab2c2310c7a81f1c29c908f76bf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:05:49 +0000 Subject: [PATCH 2834/5615] Bump postgresql from 42.4.2 to 42.5.0 in /modules/junit-jupiter Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.2 to 42.5.0. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.2...REL42.5.0) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index dd1d0c7ed6e..5b91bd181fe 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.0' - testRuntimeOnly 'org.postgresql:postgresql:42.4.2' + testRuntimeOnly 'org.postgresql:postgresql:42.5.0' testRuntimeOnly 'mysql:mysql-connector-java:8.0.30' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0' } From 236cdf97d814a34814315d215079dea77aad0d78 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:06:20 +0000 Subject: [PATCH 2835/5615] Bump transport from 7.17.5 to 7.17.6 in /modules/elasticsearch Bumps [transport](https://github.com/elastic/elasticsearch) from 7.17.5 to 7.17.6. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Changelog](https://github.com/elastic/elasticsearch/blob/main/CHANGELOG.md) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.17.5...v7.17.6) --- updated-dependencies: - dependency-name: org.elasticsearch.client:transport dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 0e6c5bd7b5d..8d8125eb1e1 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,6 +3,6 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') testImplementation "org.elasticsearch.client:elasticsearch-rest-client:8.3.3" - testImplementation "org.elasticsearch.client:transport:7.17.5" + testImplementation "org.elasticsearch.client:transport:7.17.6" testImplementation 'org.assertj:assertj-core:3.23.1' } From 4171dd86a8b95b8e991c7415be4d9ce724dec9cc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:06:41 +0000 Subject: [PATCH 2836/5615] Bump actions/cache from 3.0.7 to 3.0.8 Bumps [actions/cache](https://github.com/actions/cache) from 3.0.7 to 3.0.8. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3.0.7...v3.0.8) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index f23fc66e889..fe4610368aa 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -25,7 +25,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.7 + uses: actions/cache@v3.0.8 continue-on-error: true with: path: ~/.gradle/caches @@ -53,7 +53,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.7 + uses: actions/cache@v3.0.8 continue-on-error: true with: path: ~/.gradle/caches diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 288e9329824..9d7749134ce 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.7 + uses: actions/cache@v3.0.8 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-home-testmatrix-${{ hashFiles('**/*.gradle') }} @@ -59,7 +59,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.7 + uses: actions/cache@v3.0.8 continue-on-error: true with: path: ~/.gradle/caches From 6b716358bcdda694d1692d93c0b879e7920451a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:08:37 +0000 Subject: [PATCH 2837/5615] Bump r2dbc-postgresql in /modules/r2dbc Bumps [r2dbc-postgresql](https://github.com/r2dbc/r2dbc-postgresql) from 0.8.12.RELEASE to 0.8.13.RELEASE. - [Release notes](https://github.com/r2dbc/r2dbc-postgresql/releases) - [Changelog](https://github.com/pgjdbc/r2dbc-postgresql/blob/v0.8.13.RELEASE/CHANGELOG) - [Commits](https://github.com/r2dbc/r2dbc-postgresql/compare/v0.8.12.RELEASE...v0.8.13.RELEASE) --- updated-dependencies: - dependency-name: io.r2dbc:r2dbc-postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/r2dbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle index f38803104a3..c55b71ddcb2 100644 --- a/modules/r2dbc/build.gradle +++ b/modules/r2dbc/build.gradle @@ -12,7 +12,7 @@ dependencies { api 'io.r2dbc:r2dbc-spi:0.8.1.RELEASE' testImplementation 'org.assertj:assertj-core:3.23.1' - testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.12.RELEASE' + testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.13.RELEASE' testImplementation project(':postgresql') testFixturesImplementation 'io.projectreactor:reactor-core:3.4.22' From ee8c818c7ba68bf3024a47776351211b824e9e0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:08:38 +0000 Subject: [PATCH 2838/5615] Bump google-cloud-firestore from 3.4.0 to 3.4.2 in /modules/gcloud Bumps [google-cloud-firestore](https://github.com/googleapis/java-firestore) from 3.4.0 to 3.4.2. - [Release notes](https://github.com/googleapis/java-firestore/releases) - [Changelog](https://github.com/googleapis/java-firestore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-firestore/compare/v3.4.0...v3.4.2) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-firestore dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index bb79bfbbcc2..6b4d90d5de1 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':testcontainers') testImplementation 'com.google.cloud:google-cloud-datastore:2.11.0' - testImplementation 'com.google.cloud:google-cloud-firestore:3.4.0' + testImplementation 'com.google.cloud:google-cloud-firestore:3.4.2' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.12' testImplementation 'com.google.cloud:google-cloud-spanner:6.28.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.10.3' From 150b75de98f44c932d9303522ea9d5a4498f2fd0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:08:43 +0000 Subject: [PATCH 2839/5615] Bump google-cloud-spanner from 6.28.0 to 6.29.0 in /modules/gcloud Bumps [google-cloud-spanner](https://github.com/googleapis/java-spanner) from 6.28.0 to 6.29.0. - [Release notes](https://github.com/googleapis/java-spanner/releases) - [Changelog](https://github.com/googleapis/java-spanner/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-spanner/compare/v6.28.0...v6.29.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-spanner dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index bb79bfbbcc2..e0c20ca6c16 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -6,7 +6,7 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-datastore:2.11.0' testImplementation 'com.google.cloud:google-cloud-firestore:3.4.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.12' - testImplementation 'com.google.cloud:google-cloud-spanner:6.28.0' + testImplementation 'com.google.cloud:google-cloud-spanner:6.29.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.10.3' testImplementation 'org.assertj:assertj-core:3.23.1' } From f6ffb47903edf311a1b1aefcb501bcafb14cfe70 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:08:45 +0000 Subject: [PATCH 2840/5615] Bump logback-classic from 1.2.11 to 1.4.0 in /modules/hivemq Bumps [logback-classic](https://github.com/qos-ch/logback) from 1.2.11 to 1.4.0. - [Release notes](https://github.com/qos-ch/logback/releases) - [Commits](https://github.com/qos-ch/logback/compare/v_1.2.11...v_1.4.0) --- updated-dependencies: - dependency-name: ch.qos.logback:logback-classic dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index 9595b3e66b6..477d5ac98a3 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -16,7 +16,7 @@ dependencies { testImplementation("com.hivemq:hivemq-extension-sdk:4.8.3") testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") testImplementation("org.apache.httpcomponents:httpclient:4.5.13") - testImplementation("ch.qos.logback:logback-classic:1.2.11") + testImplementation("ch.qos.logback:logback-classic:1.4.0") testImplementation 'org.assertj:assertj-core:3.23.1' testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.0") } From d6f318c01c6e28e47e3ad25d858d8827e6276f29 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:08:58 +0000 Subject: [PATCH 2841/5615] Bump kubernetes-client from 6.0.0 to 6.1.1 in /modules/k3s Bumps [kubernetes-client](https://github.com/fabric8io/kubernetes-client) from 6.0.0 to 6.1.1. - [Release notes](https://github.com/fabric8io/kubernetes-client/releases) - [Changelog](https://github.com/fabric8io/kubernetes-client/blob/master/CHANGELOG.md) - [Commits](https://github.com/fabric8io/kubernetes-client/compare/v6.0.0...v6.1.1) --- updated-dependencies: - dependency-name: io.fabric8:kubernetes-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/k3s/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/k3s/build.gradle b/modules/k3s/build.gradle index 8c6792c42e0..0778419a3e2 100644 --- a/modules/k3s/build.gradle +++ b/modules/k3s/build.gradle @@ -8,7 +8,7 @@ dependencies { // Any >2.8 version here is not compatible with jackson-databind 2.8.x. shaded 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.8.8' - testImplementation 'io.fabric8:kubernetes-client:6.0.0' + testImplementation 'io.fabric8:kubernetes-client:6.1.1' testImplementation 'io.kubernetes:client-java:16.0.0' testImplementation 'org.assertj:assertj-core:3.23.1' } From bc76fc9086812d557c0e3d700f097f43f2355fbc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:08:59 +0000 Subject: [PATCH 2842/5615] Bump trino-jdbc from 393 to 394 in /modules/trino Bumps trino-jdbc from 393 to 394. --- updated-dependencies: - dependency-name: io.trino:trino-jdbc dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/trino/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/trino/build.gradle b/modules/trino/build.gradle index e5b7efee873..ca2619a8631 100644 --- a/modules/trino/build.gradle +++ b/modules/trino/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'io.trino:trino-jdbc:393' + testImplementation 'io.trino:trino-jdbc:394' compileOnly 'org.jetbrains:annotations:23.0.0' } From 9ef2d12be740b590a6f6a11fdfeff4e234a83fb9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:09:10 +0000 Subject: [PATCH 2843/5615] Bump postgresql from 42.4.2 to 42.5.0 in /modules/cockroachdb Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.2 to 42.5.0. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.2...REL42.5.0) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/cockroachdb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cockroachdb/build.gradle b/modules/cockroachdb/build.gradle index fbd2415e53c..638629ef22a 100644 --- a/modules/cockroachdb/build.gradle +++ b/modules/cockroachdb/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'org.postgresql:postgresql:42.4.2' + testImplementation 'org.postgresql:postgresql:42.5.0' testImplementation 'org.assertj:assertj-core:3.23.1' } From 1693da989b29deb265d58c279fc4de89b79ad7ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:09:19 +0000 Subject: [PATCH 2844/5615] Bump azure-cosmos from 4.34.0 to 4.35.1 in /modules/azure Bumps [azure-cosmos](https://github.com/Azure/azure-sdk-for-java) from 4.34.0 to 4.35.1. - [Release notes](https://github.com/Azure/azure-sdk-for-java/releases) - [Commits](https://github.com/Azure/azure-sdk-for-java/compare/azure-cosmos_4.34.0...azure-cosmos_4.35.1) --- updated-dependencies: - dependency-name: com.azure:azure-cosmos dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/azure/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle index 252eb895d37..8753606874c 100644 --- a/modules/azure/build.gradle +++ b/modules/azure/build.gradle @@ -6,5 +6,5 @@ dependencies { shaded 'com.squareup.okhttp3:okhttp:4.10.0' testImplementation 'org.assertj:assertj-core:3.23.1' - testImplementation 'com.azure:azure-cosmos:4.34.0' + testImplementation 'com.azure:azure-cosmos:4.35.1' } From efa0a8920e6a1d07a76e0eeac0a33127fdaaad7d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Sep 2022 03:13:19 +0000 Subject: [PATCH 2845/5615] Bump aws-java-sdk-sqs from 1.12.285 to 1.12.295 in /modules/localstack Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.12.285 to 1.12.295. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.285...1.12.295) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-sqs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 5f743a21160..2f727f9072a 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.295' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.285' - testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.285' + testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.295' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.295' testImplementation 'software.amazon.awssdk:s3:2.17.256' testImplementation 'org.assertj:assertj-core:3.23.1' From 4e7fc5a32949458478100284a20efa36dc9336a9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Sep 2022 03:13:23 +0000 Subject: [PATCH 2846/5615] Bump s3 from 2.17.256 to 2.17.266 in /modules/localstack Bumps s3 from 2.17.256 to 2.17.266. --- updated-dependencies: - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 5f743a21160..47d483fbc6e 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.285' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.285' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.295' - testImplementation 'software.amazon.awssdk:s3:2.17.256' + testImplementation 'software.amazon.awssdk:s3:2.17.266' testImplementation 'org.assertj:assertj-core:3.23.1' } From 2f7fe4070ffd22bb396aae1202864ce4ab449da6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Sep 2022 03:13:35 +0000 Subject: [PATCH 2847/5615] Bump elasticsearch-rest-client in /modules/elasticsearch Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 8.3.3 to 8.4.1. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Changelog](https://github.com/elastic/elasticsearch/blob/main/CHANGELOG.md) - [Commits](https://github.com/elastic/elasticsearch/compare/v8.3.3...v8.4.1) --- updated-dependencies: - dependency-name: org.elasticsearch.client:elasticsearch-rest-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 8d8125eb1e1..c5591d0d26a 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,7 +2,7 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') - testImplementation "org.elasticsearch.client:elasticsearch-rest-client:8.3.3" + testImplementation "org.elasticsearch.client:elasticsearch-rest-client:8.4.1" testImplementation "org.elasticsearch.client:transport:7.17.6" testImplementation 'org.assertj:assertj-core:3.23.1' } From d047a1ca3cce7b4f8146abe0faf7d70148b017af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Sep 2022 03:13:38 +0000 Subject: [PATCH 2848/5615] Bump postgresql from 42.4.2 to 42.5.0 in /modules/spock Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.2 to 42.5.0. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.2...REL42.5.0) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 543824ea8f6..a437ff5b502 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'com.zaxxer:HikariCP:4.0.3' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' - testRuntimeOnly 'org.postgresql:postgresql:42.4.2' + testRuntimeOnly 'org.postgresql:postgresql:42.5.0' testRuntimeOnly 'mysql:mysql-connector-java:8.0.30' testCompileClasspath 'org.jetbrains:annotations:23.0.0' From 463c45999235b93a1f07acf0984bb630edfc2230 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Sep 2022 03:13:41 +0000 Subject: [PATCH 2849/5615] Bump google-cloud-bigtable from 2.10.3 to 2.11.1 in /modules/gcloud Bumps [google-cloud-bigtable](https://github.com/googleapis/java-bigtable) from 2.10.3 to 2.11.1. - [Release notes](https://github.com/googleapis/java-bigtable/releases) - [Changelog](https://github.com/googleapis/java-bigtable/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-bigtable/compare/v2.10.3...v2.11.1) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-bigtable dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 6028ef7e7c2..c83d92e551a 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-firestore:3.4.2' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.12' testImplementation 'com.google.cloud:google-cloud-spanner:6.29.0' - testImplementation 'com.google.cloud:google-cloud-bigtable:2.10.3' + testImplementation 'com.google.cloud:google-cloud-bigtable:2.11.1' testImplementation 'org.assertj:assertj-core:3.23.1' } From caea94d6324aab22bacb522d615e5dc728ba85b3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Sep 2022 03:13:42 +0000 Subject: [PATCH 2850/5615] Bump google-cloud-pubsub from 1.120.12 to 1.120.13 in /modules/gcloud Bumps [google-cloud-pubsub](https://github.com/googleapis/java-pubsub) from 1.120.12 to 1.120.13. - [Release notes](https://github.com/googleapis/java-pubsub/releases) - [Changelog](https://github.com/googleapis/java-pubsub/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-pubsub/compare/v1.120.12...v1.120.13) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-pubsub dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 6028ef7e7c2..cc8187414be 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -5,7 +5,7 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-datastore:2.11.0' testImplementation 'com.google.cloud:google-cloud-firestore:3.4.2' - testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.12' + testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.13' testImplementation 'com.google.cloud:google-cloud-spanner:6.29.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.10.3' testImplementation 'org.assertj:assertj-core:3.23.1' From ebc22dd2eae53691470e5e4f16b23e3267ce6332 Mon Sep 17 00:00:00 2001 From: Alex <93376818+sashashura@users.noreply.github.com> Date: Fri, 2 Sep 2022 23:39:03 +0200 Subject: [PATCH 2851/5615] Update update-gradle-wrapper.yml (#5821) --- .github/workflows/update-gradle-wrapper.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/update-gradle-wrapper.yml b/.github/workflows/update-gradle-wrapper.yml index 5855e609fce..c2fe34cfe19 100644 --- a/.github/workflows/update-gradle-wrapper.yml +++ b/.github/workflows/update-gradle-wrapper.yml @@ -4,8 +4,13 @@ on: schedule: - cron: "0 0 * * *" +permissions: + contents: read + jobs: update-gradle-wrapper: + permissions: + pull-requests: write # for gradle-update/update-gradle-wrapper-action if: github.repository == 'testcontainers/testcontainers-java' runs-on: ubuntu-latest From a01d571ee52ca935021baf2c3d8904a596b99de6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Fri, 9 Sep 2022 14:08:38 +0200 Subject: [PATCH 2852/5615] docs: fix link to the bounty program (#5831) --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 83afa8a9ea8..93a87222781 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,4 +10,4 @@ Testcontainers is [in the GitHub Sponsors program](https://github.com/sponsors/t This repository is supported by our sponsors, meaning that issues are eligible to have a 'bounty' attached to them by sponsors. -Please see [the bounty policy page](https://testcontainers.org/bounty) if you are interested, either as a sponsor or as a contributor. \ No newline at end of file +Please see [the bounty policy page](https://www.testcontainers.org/bounty) if you are interested, either as a sponsor or as a contributor. From 0d368f72d898260fc2197564e78b2869d0400ca3 Mon Sep 17 00:00:00 2001 From: Neslihan Wittek <34006979+neslihanedes@users.noreply.github.com> Date: Sun, 11 Sep 2022 00:00:11 +0200 Subject: [PATCH 2853/5615] Fix typos on index.md (#5832) --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 3a99c88a5b1..8cc789e41a1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -92,7 +92,7 @@ Using Gradle 5.0 or higher, you can add the following to the `dependencies` sect !!! warning "Shaded dependencies" Testcontainers depends on other libraries (like docker-java) for it to work. Some of them (JUnit, docker-java-{api,transport} and its transitive dependencies, JNA, visible-assertions and others) are part of our public API. - But there are also "private", implementation detail dependencies (e.g. docker-java-core, Guava, OkHttp, etc etc) that are not exposed to public API but prone to conflicts with test code/application under test code. + But there are also "private", implementation detail dependencies (e.g., docker-java-core, Guava, OkHttp, etc.) that are not exposed to public API but prone to conflicts with test code/application under test code. As such, **these libraries are 'shaded' into the core testcontainers JAR** and relocated under `org.testcontainers.shaded` to prevent class conflicts. ## Sponsors From 6e46662311591312eebd59b9d7f22cb13479018b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Tue, 13 Sep 2022 10:00:21 -0500 Subject: [PATCH 2854/5615] Improve Issue Templates (#5836) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Be explicit about searching for an existing `bug`, `feature`, `enhancement` and avoid duplications. Co-authored-by: Manuel de la Peña --- .github/ISSUE_TEMPLATE/bug_report.yaml | 2 +- .github/ISSUE_TEMPLATE/enhancement.yaml | 2 +- .github/ISSUE_TEMPLATE/feature.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 163c5455b1e..9945297ea7a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -6,7 +6,7 @@ body: - type: markdown attributes: value: | - Thanks for taking the time to fill out this bug report! + Thanks for taking the time to fill out this bug report! Before submitting a `bug`, please make sure there is no existing issue for the one you encountered. - type: dropdown id: module attributes: diff --git a/.github/ISSUE_TEMPLATE/enhancement.yaml b/.github/ISSUE_TEMPLATE/enhancement.yaml index b753332073b..6d5edab909f 100644 --- a/.github/ISSUE_TEMPLATE/enhancement.yaml +++ b/.github/ISSUE_TEMPLATE/enhancement.yaml @@ -6,7 +6,7 @@ body: - type: markdown attributes: value: | - Please provide the following information + Before submitting an `enhancement`, please make sure there is no existing enhancement for the one you are requesting. If so, please provide the following information: - type: dropdown id: module attributes: diff --git a/.github/ISSUE_TEMPLATE/feature.yaml b/.github/ISSUE_TEMPLATE/feature.yaml index 91031adfeee..8abeb7d2131 100644 --- a/.github/ISSUE_TEMPLATE/feature.yaml +++ b/.github/ISSUE_TEMPLATE/feature.yaml @@ -6,7 +6,7 @@ body: - type: markdown attributes: value: | - Please provide the following information + Before submitting a `feature`, please make sure there is no existing feature for the one you are requesting. If so, please provide the following information: - type: dropdown id: module attributes: From 1f3a1f764f5c31a2f5715ed502b417475aacdd98 Mon Sep 17 00:00:00 2001 From: Michael Nitschinger Date: Wed, 14 Sep 2022 15:19:25 +0200 Subject: [PATCH 2855/5615] couchbase: allow to configure bucket replicas and default to 0. (#5840) This change allows the user to configure the number of bucket replicas and defaults it to 0 since most of the time the user ever only needs no replicas since it is a single container. Note: technically this is a behavioral change since before the number of replicas was 1 (if not defined) but this causes issues when testing transactions (as seen in #5835). Fixes #5835 --- .../couchbase/BucketDefinition.java | 30 +++++++++++++++++-- .../couchbase/CouchbaseContainer.java | 1 + 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/BucketDefinition.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/BucketDefinition.java index ad666b4ffef..c698855927c 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/BucketDefinition.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/BucketDefinition.java @@ -29,10 +29,32 @@ public class BucketDefinition { private int quota = 100; + private int numReplicas = 0; + public BucketDefinition(final String name) { this.name = name; } + /** + * Allows to configure the number of replicas on a bucket (defaults to 0). + *

+ * By default the bucket is initialized with 0 replicas since only a single container is launched. Modifying + * this value can still be useful in some test scenarios (i.e. to test failures with the wrong number of replicas + * and durability requirements on operations). + *

+ * Couchbase buckets can have a maximum of three replicas configured. + * + * @param numReplicas the number of replicas to configure. + * @return this {@link BucketDefinition} for chaining purposes. + */ + public BucketDefinition withReplicas(final int numReplicas) { + if (numReplicas < 0 || numReplicas > 3) { + throw new IllegalArgumentException("The number of replicas must be between 0 and 3 (inclusive)"); + } + this.numReplicas = numReplicas; + return this; + } + /** * Enables flush for this bucket (disabled by default). * @@ -45,9 +67,9 @@ public BucketDefinition withFlushEnabled(final boolean flushEnabled) { } /** - * Sets a custom bucket quota (100MB by default). + * Sets a custom bucket quota (100MiB by default). * - * @param quota the quota to set for the bucket. + * @param quota the quota to set for the bucket in mebibytes. * @return this {@link BucketDefinition} for chaining purposes. */ public BucketDefinition withQuota(final int quota) { @@ -84,4 +106,8 @@ public boolean hasPrimaryIndex() { public int getQuota() { return quota; } + + public int getNumReplicas() { + return numReplicas; + } } diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index 8603a0b0787..2817b79d71f 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -573,6 +573,7 @@ private void createBuckets() { .add("name", bucket.getName()) .add("ramQuotaMB", Integer.toString(bucket.getQuota())) .add("flushEnabled", bucket.hasFlushEnabled() ? "1" : "0") + .add("replicaNumber", Integer.toString(bucket.getNumReplicas())) .build(), true ); From 9540652fa46f10f4d4e724787aa5fdf6ca3fbea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Fri, 16 Sep 2022 09:09:48 -0500 Subject: [PATCH 2856/5615] Update localstack images in tests (#5783) `atlassian/pipelines-awscli` is deprecated and it recommends to use `amazon/aws-cli`. --- .../localstack/LocalstackContainerTest.java | 17 +++++++---------- .../localstack/LocalstackTestImages.java | 2 +- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java index b879b27f0e6..d44e98723b7 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java @@ -26,7 +26,6 @@ import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; -import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.Container; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; @@ -153,12 +152,7 @@ public void sqsTestOverBridgeNetwork() { String fooQueueUrl = queueResult.getQueueUrl(); assertThat(fooQueueUrl) .as("Created queue has external hostname URL") - .contains( - "http://" + - DockerClientFactory.instance().dockerHostIpAddress() + - ":" + - localstack.getMappedPort(LocalStackContainer.PORT) - ); + .contains("http://" + localstack.getHost() + ":" + localstack.getMappedPort(LocalStackContainer.PORT)); sqs.sendMessage(fooQueueUrl, "test"); final long messageCount = sqs @@ -265,14 +259,17 @@ public static class WithNetwork { LocalstackTestImages.AWS_CLI_IMAGE ) .withNetwork(network) - .withCreateContainerCmdModifier(cmd -> cmd.withEntrypoint("top")) + .withCreateContainerCmdModifier(cmd -> cmd.withEntrypoint("tail")) + .withCommand(" -f /dev/null") .withEnv("AWS_ACCESS_KEY_ID", "accesskey") .withEnv("AWS_SECRET_ACCESS_KEY", "secretkey") .withEnv("AWS_REGION", "eu-west-1"); @Test public void s3TestOverDockerNetwork() throws Exception { - runAwsCliAgainstDockerNetworkContainer("s3api create-bucket --bucket foo"); + runAwsCliAgainstDockerNetworkContainer( + "s3api create-bucket --bucket foo --create-bucket-configuration LocationConstraint=eu-west-1" + ); runAwsCliAgainstDockerNetworkContainer("s3api list-buckets"); runAwsCliAgainstDockerNetworkContainer("s3 ls s3://foo"); } @@ -313,7 +310,7 @@ public void cloudWatchLogsTestOverDockerNetwork() throws Exception { private String runAwsCliAgainstDockerNetworkContainer(String command) throws Exception { final String[] commandParts = String .format( - "/usr/bin/aws --region eu-west-1 %s --endpoint-url http://localstack:%d --no-verify-ssl", + "/usr/local/bin/aws --region eu-west-1 %s --endpoint-url http://localstack:%d --no-verify-ssl", command, LocalStackContainer.PORT ) diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackTestImages.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackTestImages.java index 491394bea8a..fe39b56521a 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackTestImages.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackTestImages.java @@ -8,5 +8,5 @@ public interface LocalstackTestImages { DockerImageName LOCALSTACK_0_10_IMAGE = LOCALSTACK_IMAGE.withTag("0.10.7"); DockerImageName LOCALSTACK_0_11_IMAGE = LOCALSTACK_IMAGE.withTag("0.11.3"); DockerImageName LOCALSTACK_0_12_IMAGE = LOCALSTACK_IMAGE.withTag("0.12.8"); - DockerImageName AWS_CLI_IMAGE = DockerImageName.parse("atlassian/pipelines-awscli:1.16.302"); + DockerImageName AWS_CLI_IMAGE = DockerImageName.parse("amazon/aws-cli:2.7.27"); } From 9e98addab9cfe076ace39a41c8cfdf4351649756 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Fri, 16 Sep 2022 16:42:12 +0200 Subject: [PATCH 2857/5615] Update slf4j in test-support to 2.0.0 (#5848) --- test-support/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-support/build.gradle b/test-support/build.gradle index ecf9a0bc60e..2fae2344a89 100644 --- a/test-support/build.gradle +++ b/test-support/build.gradle @@ -1,5 +1,5 @@ dependencies { implementation 'junit:junit:4.13.2' - implementation 'org.slf4j:slf4j-api:1.7.30' + implementation 'org.slf4j:slf4j-api:2.0.0' testImplementation 'org.assertj:assertj-core:3.23.1' } From 22aa85d24e91a2a380da6d96f21ca03947de9c91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Sat, 17 Sep 2022 12:22:45 -0500 Subject: [PATCH 2858/5615] Remove thundra from ci.yml (#5850) Using thundra breaks the gradle cache in `master` branch. --- .github/workflows/ci.yml | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9d7749134ce..e1eceab8702 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,14 +14,6 @@ env: GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }} jobs: - thundra_test_initializer: - runs-on: ubuntu-latest - outputs: - thundra_agent_testrun_id: ${{ steps.thundra_test_initializer.outputs.thundra_agent_testrun_id }} - steps: - - uses: actions/checkout@v3 - - id: thundra_test_initializer - uses: thundra-io/thundra-test-init-action@v1 find_gradle_jobs: runs-on: ubuntu-18.04 outputs: @@ -47,7 +39,7 @@ jobs: echo $TASKS echo "::set-output name=matrix::{\"gradle_args\":$TASKS}" check: - needs: [find_gradle_jobs, thundra_test_initializer] + needs: [find_gradle_jobs] strategy: fail-fast: false matrix: ${{ fromJson(needs.find_gradle_jobs.outputs.matrix) }} @@ -66,16 +58,6 @@ jobs: key: ${{ runner.os }}-gradle-home-${{matrix.gradle_args}}_check-${{ hashFiles('**/*.gradle') }} - name: Clear existing docker image cache run: docker image prune -af - - name: Thundra Gradle Test Instrumentation - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} - uses: thundra-io/thundra-gradle-test-action@v1 - with: - apikey: ${{ secrets.THUNDRA_API_KEY }} - project_id: ${{ secrets.THUNDRA_PROJECT_ID }} - name: Build and test with Gradle (${{matrix.gradle_args}}) run: | - ./gradlew --no-daemon --continue --scan ${{matrix.gradle_args}} \ - $([[ -f $THUNDRA_GRADLE_INIT_SCRIPT_PATH ]] && echo "--init-script $THUNDRA_GRADLE_INIT_SCRIPT_PATH") - env: - THUNDRA_AGENT_TEST_RUN_ID: ${{ needs.thundra_test_initializer.outputs.thundra_agent_testrun_id }} - THUNDRA_AGENT_REPORT_REST_BASEURL: https://collector.thundra.us/v1 + ./gradlew --no-daemon --continue --scan ${{matrix.gradle_args}} From 459d2f6b8915b9c0cbcd89b24825d412a6739838 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Mon, 19 Sep 2022 04:55:58 -0500 Subject: [PATCH 2859/5615] Use `testCompileOnly` instead of `testCompileClasspath` (#5849) `testCompileClasspath` is deprecated. Also, removing `org.jetbrains:annotations` from `selenium` module. --- core/build.gradle | 2 +- modules/selenium/build.gradle | 1 - .../org/testcontainers/junit/BaseWebDriverContainerTest.java | 2 -- modules/spock/build.gradle | 2 +- 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index c9dd720e2a3..4072c25db37 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -63,7 +63,7 @@ dependencies { api 'junit:junit:4.13.2' api 'org.slf4j:slf4j-api:2.0.0' compileOnly 'org.jetbrains:annotations:23.0.0' - testCompileClasspath 'org.jetbrains:annotations:23.0.0' + testCompileOnly 'org.jetbrains:annotations:23.0.0' api 'org.apache.commons:commons-compress:1.21' api ('org.rnorth.duct-tape:duct-tape:1.0.8') { exclude(group: 'org.jetbrains', module: 'annotations') diff --git a/modules/selenium/build.gradle b/modules/selenium/build.gradle index 38fab63dc0a..3e5a80a2dfa 100644 --- a/modules/selenium/build.gradle +++ b/modules/selenium/build.gradle @@ -13,5 +13,4 @@ dependencies { testImplementation 'org.assertj:assertj-core:3.23.1' compileOnly 'org.jetbrains:annotations:23.0.0' - testCompileClasspath 'org.jetbrains:annotations:23.0.0' } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java index c49e24ba867..0ce20ca9800 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java @@ -1,6 +1,5 @@ package org.testcontainers.junit; -import org.jetbrains.annotations.NotNull; import org.junit.ClassRule; import org.openqa.selenium.By; import org.openqa.selenium.WebElement; @@ -51,7 +50,6 @@ protected void assertBrowserNameIs(BrowserWebDriverContainer rule, String exp assertThat(actual).as(String.format("actual browser name is %s", actual)).isEqualTo(expectedName); } - @NotNull private static RemoteWebDriver setupDriverFromRule(BrowserWebDriverContainer rule) { RemoteWebDriver driver = rule.getWebDriver(); driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS); diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index a437ff5b502..a13a7049e13 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -18,7 +18,7 @@ dependencies { testRuntimeOnly 'org.postgresql:postgresql:42.5.0' testRuntimeOnly 'mysql:mysql-connector-java:8.0.30' - testCompileClasspath 'org.jetbrains:annotations:23.0.0' + testCompileOnly 'org.jetbrains:annotations:23.0.0' } sourceJar { From de1a77ed837ab5b5a542dbd51d40e08391edb129 Mon Sep 17 00:00:00 2001 From: Neslihan Wittek <34006979+neslihanedes@users.noreply.github.com> Date: Mon, 26 Sep 2022 12:04:51 +0200 Subject: [PATCH 2860/5615] Improve consistency of Testcontainers name in docs (#5866) --- docs/contributing_docs.md | 2 +- docs/index.md | 2 +- docs/modules/databases/orientdb.md | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/contributing_docs.md b/docs/contributing_docs.md index f6f9304a339..2d706dfe407 100644 --- a/docs/contributing_docs.md +++ b/docs/contributing_docs.md @@ -1,6 +1,6 @@ # Contributing to documentation -The testcontainers-java documentation is a static site built with [MkDocs](https://www.mkdocs.org/). +The Testcontainers-java documentation is a static site built with [MkDocs](https://www.mkdocs.org/). We use the [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) theme, which offers a number of useful extensions to MkDocs. In addition we use a [custom plugin](https://github.com/rnorth/mkdocs-codeinclude-plugin) for inclusion of code snippets. diff --git a/docs/index.md b/docs/index.md index 8cc789e41a1..8eb4ddeb2c4 100644 --- a/docs/index.md +++ b/docs/index.md @@ -93,7 +93,7 @@ Using Gradle 5.0 or higher, you can add the following to the `dependencies` sect Testcontainers depends on other libraries (like docker-java) for it to work. Some of them (JUnit, docker-java-{api,transport} and its transitive dependencies, JNA, visible-assertions and others) are part of our public API. But there are also "private", implementation detail dependencies (e.g., docker-java-core, Guava, OkHttp, etc.) that are not exposed to public API but prone to conflicts with test code/application under test code. - As such, **these libraries are 'shaded' into the core testcontainers JAR** and relocated under `org.testcontainers.shaded` to prevent class conflicts. + As such, **these libraries are 'shaded' into the core Testcontainers JAR** and relocated under `org.testcontainers.shaded` to prevent class conflicts. ## Sponsors diff --git a/docs/modules/databases/orientdb.md b/docs/modules/databases/orientdb.md index 4beffefa6c2..57839cd99f0 100644 --- a/docs/modules/databases/orientdb.md +++ b/docs/modules/databases/orientdb.md @@ -10,7 +10,7 @@ Note that it's based on the [official Docker image](https://hub.docker.com/_/ori ## Usage example -Declare your Testcontainer as a `@ClassRule` or `@Rule` in a JUnit 4 test or as static or member attribute of a JUnit 5 test annotated with `@Container` as you would with other Testcontainers. +Declare your Testcontainers instance as a `@ClassRule` or `@Rule` in a JUnit 4 test or as static or member attribute of a JUnit 5 test annotated with `@Container` as you would with other Testcontainers. You can call `getDbUrl()` OrientDB container and build the `ODatabaseSession` by your own, but a more useful `getSession()` method is provided. On the JVM you would most likely use the [Java driver](https://github.com/). @@ -39,7 +39,7 @@ The following example uses the JUnit 5 extension `@Testcontainers` and demonstra } ``` -You are not limited to Unit tests and can of course use an instance of the OrientDB Testcontainer in vanilla Java code as well. +You are not limited to Unit tests and can of course use an instance of the OrientDB Testcontainers implementation in vanilla Java code as well. ## Adding this module to your project dependencies From 9847d5930bb0faea9d14f606c583919d4e2d2113 Mon Sep 17 00:00:00 2001 From: Aidan Do <43259657+REslim30@users.noreply.github.com> Date: Mon, 26 Sep 2022 21:20:45 +0930 Subject: [PATCH 2861/5615] Fix: ContainerDatabaseDriver does not register Properties object (#5829) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Eddú Meléndez Gonzales --- .../containers/JdbcDatabaseContainer.java | 24 +++++++--- .../jdbc/ContainerDatabaseDriver.java | 2 +- .../MySQLDatabaseContainerDriverTest.java | 44 +++++++++++++++++++ 3 files changed, 64 insertions(+), 6 deletions(-) create mode 100644 modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/MySQLDatabaseContainerDriverTest.java diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index aa397929497..88c418e73cb 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -216,9 +216,23 @@ public Driver getJdbcDriverInstance() throws NoDriverFoundException { * @throws SQLException if there is a repeated failure to create the connection */ public Connection createConnection(String queryString) throws SQLException, NoDriverFoundException { - final Properties info = new Properties(); - info.put("user", this.getUsername()); - info.put("password", this.getPassword()); + return createConnection(queryString, new Properties()); + } + + /** + * Creates a connection to the underlying containerized database instance. + * + * @param queryString query string parameters that should be appended to the JDBC connection URL. + * The '?' character must be included + * @param info additional properties to be passed to the JDBC driver + * @return a Connection + * @throws SQLException if there is a repeated failure to create the connection + */ + public Connection createConnection(String queryString, Properties info) + throws SQLException, NoDriverFoundException { + Properties properties = new Properties(info); + properties.put("user", this.getUsername()); + properties.put("password", this.getPassword()); final String url = constructUrlForConnection(queryString); final Driver jdbcDriverInstance = getJdbcDriverInstance(); @@ -234,10 +248,10 @@ public Connection createConnection(String queryString) throws SQLException, NoDr "Trying to create JDBC connection using {} to {} with properties: {}", jdbcDriverInstance.getClass().getName(), url, - info + properties ); - return jdbcDriverInstance.connect(url, info); + return jdbcDriverInstance.connect(url, properties); } catch (SQLException e) { lastException = e; Thread.sleep(100L); diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java index 23ed9d78b25..a3fed34f24c 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java @@ -127,7 +127,7 @@ public synchronized Connection connect(String url, final Properties info) throws /* Create a connection using the delegated driver. The container must be ready to accept connections. */ - Connection connection = container.createConnection(queryString); + Connection connection = container.createConnection(queryString, info); /* If this container has not been initialized, AND diff --git a/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/MySQLDatabaseContainerDriverTest.java b/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/MySQLDatabaseContainerDriverTest.java new file mode 100644 index 00000000000..7155e49dd48 --- /dev/null +++ b/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/MySQLDatabaseContainerDriverTest.java @@ -0,0 +1,44 @@ +package org.testcontainers.jdbc.mysql; + +import org.junit.Test; +import org.testcontainers.jdbc.ContainerDatabaseDriver; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Properties; + +import static org.assertj.core.api.Assertions.assertThat; + +public class MySQLDatabaseContainerDriverTest { + + @Test + public void shouldRespectBothUrlPropertiesAndParameterProperties() throws SQLException { + ContainerDatabaseDriver driver = new ContainerDatabaseDriver(); + String url = "jdbc:tc:mysql:5.7.22://hostname/databasename?padCharsWithSpace=true"; + Properties properties = new Properties(); + properties.setProperty("maxRows", "1"); + + try (Connection connection = driver.connect(url, properties)) { + try (Statement statement = connection.createStatement()) { + statement.execute("CREATE TABLE arbitrary_table (length_5_string CHAR(5))"); + statement.execute("INSERT INTO arbitrary_table VALUES ('abc')"); + statement.execute("INSERT INTO arbitrary_table VALUES ('123')"); + + // Check that maxRows is set + try (ResultSet resultSet = statement.executeQuery("SELECT * FROM arbitrary_table")) { + resultSet.next(); + assertThat(resultSet.isFirst()).isTrue(); + assertThat(resultSet.isLast()).isTrue(); + } + + // Check that pad with chars is set + try (ResultSet resultSet = statement.executeQuery("SELECT * FROM arbitrary_table")) { + assertThat(resultSet.next()).isTrue(); + assertThat(resultSet.getString(1)).isEqualTo("abc "); + } + } + } + } +} From f54a29a48419b4fb14ef1fc024a656e918d994ae Mon Sep 17 00:00:00 2001 From: Aidan Do <43259657+REslim30@users.noreply.github.com> Date: Wed, 28 Sep 2022 19:40:08 +0930 Subject: [PATCH 2862/5615] `getLivenessCheckPortNumbers()` should return mapped port (#5734) Co-authored-by: Kevin Wittek --- .../org/testcontainers/containers/Db2Container.java | 8 ++++++-- .../org/testcontainers/junit/db2/SimpleDb2Test.java | 6 ++++++ .../containers/MSSQLServerContainer.java | 3 +-- .../junit/mssqlserver/SimpleMSSQLServerTest.java | 7 +++++++ .../org/testcontainers/containers/MySQLContainer.java | 8 ++++++-- .../testcontainers/junit/mysql/SimpleMySQLTest.java | 6 ++++++ .../org/testcontainers/containers/NginxContainer.java | 8 ++++++-- .../java/org/testcontainers/junit/SimpleNginxTest.java | 6 ++++++ .../testcontainers/containers/PostgreSQLContainer.java | 8 ++++++-- .../junit/postgresql/SimplePostgreSQLTest.java | 7 +++++++ .../org/testcontainers/containers/PrestoContainer.java | 8 ++++++-- .../testcontainers/containers/PrestoContainerTest.java | 7 +++++++ .../java/org/testcontainers/tidb/TiDBContainer.java | 8 ++++++-- .../org/testcontainers/junit/tidb/SimpleTiDBTest.java | 10 ++++++++++ .../org/testcontainers/containers/TrinoContainer.java | 8 ++++++-- .../testcontainers/containers/TrinoContainerTest.java | 6 ++++++ 16 files changed, 98 insertions(+), 16 deletions(-) diff --git a/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java b/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java index 29e2ce8aa07..333a2e709c3 100644 --- a/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java +++ b/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java @@ -6,7 +6,6 @@ import java.time.Duration; import java.time.temporal.ChronoUnit; -import java.util.HashSet; import java.util.Set; public class Db2Container extends JdbcDatabaseContainer { @@ -54,9 +53,14 @@ public Db2Container(final DockerImageName dockerImageName) { addExposedPort(DB2_PORT); } + /** + * @return the ports on which to check if the container is ready + * @deprecated use {@link #getLivenessCheckPortNumbers()} instead + */ @Override + @Deprecated protected Set getLivenessCheckPorts() { - return new HashSet<>(getMappedPort(DB2_PORT)); + return super.getLivenessCheckPorts(); } @Override diff --git a/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java b/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java index 2c2c4cb2f09..ae35a8ee77c 100644 --- a/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java +++ b/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java @@ -21,6 +21,7 @@ public void testSimple() throws SQLException { int resultSetInt = resultSet.getInt(1); assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); + assertHasCorrectExposedAndLivenessCheckPorts(db2); } } @@ -37,4 +38,9 @@ public void testWithAdditionalUrlParamInJdbcUrl() { assertThat(jdbcUrl).contains(":sslConnection=false;"); } } + + private void assertHasCorrectExposedAndLivenessCheckPorts(Db2Container db2) { + assertThat(db2.getExposedPorts()).containsExactly(Db2Container.DB2_PORT); + assertThat(db2.getLivenessCheckPortNumbers()).containsExactly(db2.getMappedPort(Db2Container.DB2_PORT)); + } } diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java index 532316fb967..8ab3c1113a2 100644 --- a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java @@ -1,6 +1,5 @@ package org.testcontainers.containers; -import com.google.common.collect.Sets; import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.LicenseAcceptance; @@ -64,7 +63,7 @@ public MSSQLServerContainer(final DockerImageName dockerImageName) { @Override public Set getLivenessCheckPortNumbers() { - return Sets.newHashSet(MS_SQL_SERVER_PORT); + return super.getLivenessCheckPortNumbers(); } @Override diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java index 15edbaa2ca1..2827544cade 100644 --- a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java +++ b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java @@ -25,6 +25,7 @@ public void testSimple() throws SQLException { int resultSetInt = resultSet.getInt(1); assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); + assertHasCorrectExposedAndLivenessCheckPorts(mssqlServer); } } @@ -64,4 +65,10 @@ public void testSetupDatabase() throws SQLException { assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(3); } } + + private void assertHasCorrectExposedAndLivenessCheckPorts(MSSQLServerContainer mssqlServer) { + assertThat(mssqlServer.getExposedPorts()).containsExactly(MSSQLServerContainer.MS_SQL_SERVER_PORT); + assertThat(mssqlServer.getLivenessCheckPortNumbers()) + .containsExactly(mssqlServer.getMappedPort(MSSQLServerContainer.MS_SQL_SERVER_PORT)); + } } diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java index 14206018954..316e709995c 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java @@ -3,7 +3,6 @@ import org.jetbrains.annotations.NotNull; import org.testcontainers.utility.DockerImageName; -import java.util.HashSet; import java.util.Set; /** @@ -56,10 +55,15 @@ public MySQLContainer(final DockerImageName dockerImageName) { addExposedPort(MYSQL_PORT); } + /** + * @return the ports on which to check if the container is ready + * @deprecated use {@link #getLivenessCheckPortNumbers()} instead + */ @NotNull @Override + @Deprecated protected Set getLivenessCheckPorts() { - return new HashSet<>(getMappedPort(MYSQL_PORT)); + return super.getLivenessCheckPorts(); } @Override diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java index 146010058ac..f60712e7123 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java @@ -57,6 +57,7 @@ public void testSimple() throws SQLException { int resultSetInt = resultSet.getInt(1); assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); + assertHasCorrectExposedAndLivenessCheckPorts(mysql); } } @@ -237,4 +238,9 @@ public void testWithAdditionalUrlParamInJdbcUrl() { mysql.stop(); } } + + private void assertHasCorrectExposedAndLivenessCheckPorts(MySQLContainer mysql) { + assertThat(mysql.getExposedPorts()).containsExactly(MySQLContainer.MYSQL_PORT); + assertThat(mysql.getLivenessCheckPortNumbers()).containsExactly(mysql.getMappedPort(MySQLContainer.MYSQL_PORT)); + } } diff --git a/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java b/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java index 3e7a2062fd0..b97992a5684 100644 --- a/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java +++ b/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java @@ -6,7 +6,6 @@ import java.net.MalformedURLException; import java.net.URL; -import java.util.Collections; import java.util.Set; /** @@ -42,10 +41,15 @@ public NginxContainer(final DockerImageName dockerImageName) { setCommand("nginx", "-g", "daemon off;"); } + /** + * @return the ports on which to check if the container is ready + * @deprecated use {@link #getLivenessCheckPortNumbers()} instead + */ @NotNull @Override + @Deprecated protected Set getLivenessCheckPorts() { - return Collections.singleton(getMappedPort(80)); + return super.getLivenessCheckPorts(); } public URL getBaseUrl(String scheme, int port) throws MalformedURLException { diff --git a/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java b/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java index 24e913cf281..abfbb663207 100644 --- a/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java +++ b/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java @@ -58,6 +58,12 @@ public void testSimple() throws Exception { .as("An HTTP GET from the Nginx server returns the index.html from the custom content directory") .contains("Hello World!"); // } + assertHasCorrectExposedAndLivenessCheckPorts(nginx); + } + + private void assertHasCorrectExposedAndLivenessCheckPorts(NginxContainer nginxContainer) throws Exception { + assertThat(nginxContainer.getExposedPorts()).containsExactly(80); + assertThat(nginxContainer.getLivenessCheckPortNumbers()).containsExactly(nginxContainer.getMappedPort(80)); } private static String responseFromNginx(URL baseUrl) throws IOException { diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index 3b36b61ea77..d3ff65ee351 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -6,7 +6,6 @@ import java.time.Duration; import java.time.temporal.ChronoUnit; -import java.util.Collections; import java.util.Set; /** @@ -62,10 +61,15 @@ public PostgreSQLContainer(final DockerImageName dockerImageName) { addExposedPort(POSTGRESQL_PORT); } + /** + * @return the ports on which to check if the container is ready + * @deprecated use {@link #getLivenessCheckPortNumbers()} instead + */ @NotNull @Override + @Deprecated protected Set getLivenessCheckPorts() { - return Collections.singleton(getMappedPort(POSTGRESQL_PORT)); + return super.getLivenessCheckPorts(); } @Override diff --git a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java index 2a7db242421..84beb779fa4 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java @@ -26,6 +26,7 @@ public void testSimple() throws SQLException { ResultSet resultSet = performQuery(postgres, "SELECT 1"); int resultSetInt = resultSet.getInt(1); assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); + assertHasCorrectExposedAndLivenessCheckPorts(postgres); } } @@ -86,4 +87,10 @@ public void testWithAdditionalUrlParamInJdbcUrl() { assertThat(jdbcUrl).contains("charSet=UNICODE"); } } + + private void assertHasCorrectExposedAndLivenessCheckPorts(PostgreSQLContainer postgres) { + assertThat(postgres.getExposedPorts()).containsExactly(PostgreSQLContainer.POSTGRESQL_PORT); + assertThat(postgres.getLivenessCheckPortNumbers()) + .containsExactly(postgres.getMappedPort(PostgreSQLContainer.POSTGRESQL_PORT)); + } } diff --git a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java index e73f6cc0f6b..5c89682643a 100644 --- a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java +++ b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java @@ -9,7 +9,6 @@ import java.sql.SQLException; import java.time.Duration; import java.time.temporal.ChronoUnit; -import java.util.HashSet; import java.util.Set; /** @@ -56,10 +55,15 @@ public PrestoContainer(final DockerImageName dockerImageName) { addExposedPort(PRESTO_PORT); } + /** + * @return the ports on which to check if the container is ready + * @deprecated use {@link #getLivenessCheckPortNumbers()} instead + */ @NotNull @Override + @Deprecated protected Set getLivenessCheckPorts() { - return new HashSet<>(getMappedPort(PRESTO_PORT)); + return super.getLivenessCheckPorts(); } @Override diff --git a/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java b/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java index 721186807aa..0e86b587c50 100644 --- a/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java +++ b/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java @@ -32,6 +32,7 @@ public void testSimple() throws Exception { assertThat(resultSet.getString("node_version")) .as("Presto version") .isEqualTo(PrestoContainer.DEFAULT_TAG); + assertHasCorrectExposedAndLivenessCheckPorts(prestoSql); } } } @@ -149,4 +150,10 @@ public void testTcJdbcUri() throws Exception { .isEqualTo(Connection.TRANSACTION_READ_UNCOMMITTED); } } + + private void assertHasCorrectExposedAndLivenessCheckPorts(PrestoContainer prestoSql) { + assertThat(prestoSql.getExposedPorts()).containsExactly(PrestoContainer.PRESTO_PORT); + assertThat(prestoSql.getLivenessCheckPortNumbers()) + .containsExactly(prestoSql.getMappedPort(PrestoContainer.PRESTO_PORT)); + } } diff --git a/modules/tidb/src/main/java/org/testcontainers/tidb/TiDBContainer.java b/modules/tidb/src/main/java/org/testcontainers/tidb/TiDBContainer.java index f8030fd2422..c78590d8393 100644 --- a/modules/tidb/src/main/java/org/testcontainers/tidb/TiDBContainer.java +++ b/modules/tidb/src/main/java/org/testcontainers/tidb/TiDBContainer.java @@ -6,7 +6,6 @@ import org.testcontainers.utility.DockerImageName; import java.time.Duration; -import java.util.HashSet; import java.util.Set; /** @@ -51,10 +50,15 @@ public TiDBContainer(final DockerImageName dockerImageName) { ); } + /** + * @return the ports on which to check if the container is ready + * @deprecated use {@link #getLivenessCheckPortNumbers()} instead + */ @NotNull @Override + @Deprecated protected Set getLivenessCheckPorts() { - return new HashSet<>(getMappedPort(TIDB_PORT)); + return super.getLivenessCheckPorts(); } @Override diff --git a/modules/tidb/src/test/java/org/testcontainers/junit/tidb/SimpleTiDBTest.java b/modules/tidb/src/test/java/org/testcontainers/junit/tidb/SimpleTiDBTest.java index 714c9854f39..6b4429afe75 100644 --- a/modules/tidb/src/test/java/org/testcontainers/junit/tidb/SimpleTiDBTest.java +++ b/modules/tidb/src/test/java/org/testcontainers/junit/tidb/SimpleTiDBTest.java @@ -21,6 +21,7 @@ public void testSimple() throws SQLException { int resultSetInt = resultSet.getInt(1); assertThat(resultSetInt).isEqualTo(1); + assertHasCorrectExposedAndLivenessCheckPorts(tidb); } } @@ -51,4 +52,13 @@ public void testWithAdditionalUrlParamInJdbcUrl() { tidb.stop(); } } + + private void assertHasCorrectExposedAndLivenessCheckPorts(TiDBContainer tidb) { + Integer tidbPort = 4000; + Integer restApiPort = 10080; + + assertThat(tidb.getExposedPorts()).containsExactlyInAnyOrder(tidbPort, restApiPort); + assertThat(tidb.getLivenessCheckPortNumbers()) + .containsExactlyInAnyOrder(tidb.getMappedPort(tidbPort), tidb.getMappedPort(restApiPort)); + } } diff --git a/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainer.java b/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainer.java index 9ed18e96d28..21171fd2c15 100644 --- a/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainer.java +++ b/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainer.java @@ -7,7 +7,6 @@ import java.sql.Connection; import java.sql.SQLException; -import java.util.HashSet; import java.util.Set; public class TrinoContainer extends JdbcDatabaseContainer { @@ -37,10 +36,15 @@ public TrinoContainer(final DockerImageName dockerImageName) { addExposedPort(TRINO_PORT); } + /** + * @return the ports on which to check if the container is ready + * @deprecated use {@link #getLivenessCheckPortNumbers()} instead + */ @NotNull @Override + @Deprecated protected Set getLivenessCheckPorts() { - return new HashSet<>(getMappedPort(TRINO_PORT)); + return super.getLivenessCheckPorts(); } @Override diff --git a/modules/trino/src/test/java/org/testcontainers/containers/TrinoContainerTest.java b/modules/trino/src/test/java/org/testcontainers/containers/TrinoContainerTest.java index a58944946f0..6ef733ca972 100644 --- a/modules/trino/src/test/java/org/testcontainers/containers/TrinoContainerTest.java +++ b/modules/trino/src/test/java/org/testcontainers/containers/TrinoContainerTest.java @@ -24,6 +24,7 @@ public void testSimple() throws Exception { assertThat(resultSet.getString("node_version")) .as("Trino version") .isEqualTo(TrinoContainer.DEFAULT_TAG); + assertContainerHasCorrectExposedAndLivenessCheckPorts(trino); } } } @@ -61,4 +62,9 @@ public void testInitScript() throws Exception { } } } + + private void assertContainerHasCorrectExposedAndLivenessCheckPorts(TrinoContainer trino) { + assertThat(trino.getExposedPorts()).containsExactly(8080); + assertThat(trino.getLivenessCheckPortNumbers()).containsExactly(trino.getMappedPort(8080)); + } } From 405ddb7e39443993f8988c4c5782727f0d0b5cc5 Mon Sep 17 00:00:00 2001 From: Dave Maughan Date: Thu, 29 Sep 2022 09:53:46 +0100 Subject: [PATCH 2863/5615] Allow Pulsar default WaitStrategy to honour startup timeout (#5674) --- .../containers/PulsarContainer.java | 22 ++++++++----------- .../containers/PulsarContainerTest.java | 9 ++++++++ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java index 10dc35dc4c8..950149e3226 100644 --- a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java +++ b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java @@ -2,12 +2,8 @@ import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.containers.wait.strategy.WaitAllStrategy; -import org.testcontainers.containers.wait.strategy.WaitStrategy; import org.testcontainers.utility.DockerImageName; -import java.util.ArrayList; -import java.util.List; - /** * This container wraps Apache Pulsar running in standalone mode */ @@ -36,6 +32,8 @@ public class PulsarContainer extends GenericContainer { @Deprecated private static final String DEFAULT_TAG = "2.10.0"; + private final WaitAllStrategy waitAllStrategy = new WaitAllStrategy(); + private boolean functionsWorkerEnabled = false; private boolean transactionsEnabled = false; @@ -60,6 +58,7 @@ public PulsarContainer(final DockerImageName dockerImageName) { super(dockerImageName); dockerImageName.assertCompatibleWith(DockerImageName.parse("apachepulsar/pulsar")); withExposedPorts(BROKER_PORT, BROKER_HTTP_PORT); + setWaitStrategy(waitAllStrategy); } @Override @@ -98,21 +97,18 @@ protected void setupCommandAndEnv() { final String clusterName = getEnvMap().getOrDefault("PULSAR_PREFIX_clusterName", "standalone"); final String response = String.format("[\"%s\"]", clusterName); - - List waitStrategies = new ArrayList<>(); - waitStrategies.add(Wait.defaultWaitStrategy()); - waitStrategies.add( + waitAllStrategy.withStrategy( Wait.forHttp(ADMIN_CLUSTERS_ENDPOINT).forPort(BROKER_HTTP_PORT).forResponsePredicate(response::equals) ); + if (transactionsEnabled) { withEnv("PULSAR_PREFIX_transactionCoordinatorEnabled", "true"); - waitStrategies.add(Wait.forHttp(TRANSACTION_TOPIC_ENDPOINT).forStatusCode(200).forPort(BROKER_HTTP_PORT)); + waitAllStrategy.withStrategy( + Wait.forHttp(TRANSACTION_TOPIC_ENDPOINT).forStatusCode(200).forPort(BROKER_HTTP_PORT) + ); } if (functionsWorkerEnabled) { - waitStrategies.add(Wait.forLogMessage(".*Function worker service started.*", 1)); + waitAllStrategy.withStrategy(Wait.forLogMessage(".*Function worker service started.*", 1)); } - final WaitAllStrategy compoundedWaitStrategy = new WaitAllStrategy(); - waitStrategies.forEach(compoundedWaitStrategy::withStrategy); - waitingFor(compoundedWaitStrategy); } } diff --git a/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java b/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java index bf9f3d2201f..b054c30d0ac 100644 --- a/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java +++ b/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java @@ -12,6 +12,7 @@ import org.junit.Test; import org.testcontainers.utility.DockerImageName; +import java.time.Duration; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; @@ -144,6 +145,14 @@ public void testClusterFullyInitialized() throws Exception { } } + @Test + public void testStartupTimeoutIsHonored() { + try (PulsarContainer pulsar = new PulsarContainer(PULSAR_IMAGE).withStartupTimeout(Duration.ZERO)) { + assertThatThrownBy(pulsar::start) + .hasRootCauseMessage("Precondition failed: timeout must be greater than zero"); + } + } + protected void testPulsarFunctionality(String pulsarBrokerUrl) throws Exception { try ( PulsarClient client = PulsarClient.builder().serviceUrl(pulsarBrokerUrl).build(); From 2215e219054ee034583c27f3631154d7ec1b908e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Thu, 29 Sep 2022 09:21:07 -0500 Subject: [PATCH 2864/5615] Add Testcontainers icon for JetBrains IDEs (#5870) --- .gitignore | 3 ++- .idea/icon.png | Bin 0 -> 5740 bytes 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 .idea/icon.png diff --git a/.gitignore b/.gitignore index 95fc6e52000..120181d6e1e 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,8 @@ dependency-reduced-pom.xml *.iml ## Directory-based project format: -.idea/ +.idea/* +!.idea/icon.png # if you remove the above rule, at least ignore the following: # User-specific stuff: diff --git a/.idea/icon.png b/.idea/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0f1d8ff443473bd28bed2d1a6a256a9cd76fd7fb GIT binary patch literal 5740 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 DUoGH2 literal 0 HcmV?d00001 From c28fbe029e843b69f9d260ae2d2a0bee01257cc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Thu, 29 Sep 2022 10:16:33 -0500 Subject: [PATCH 2865/5615] Remove thundra badges --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index b1c0014c236..d2fce2e9911 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # Testcontainers -[![Thundra Foresight](https://thundra-assets-prod.s3.us-west-2.amazonaws.com/images/badges/thundra-foresight-badge-enabled.svg)](https://foresight.thundra.live/testRuns/5a49cc30-f063-11eb-9a03-0242ac130003) -[![Thundra Foresight Test Results](https://foresight.service.thundra.us/public/api/v1/badge/test/5a49cc30-f063-11eb-9a03-0242ac130003)](https://foresight.thundra.live/testRuns/5a49cc30-f063-11eb-9a03-0242ac130003) [![Revved up by Gradle Enterprise](https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.testcontainers.org/scans) > Testcontainers is a Java library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container. From b6abcef31196682b48cebb26739d607ca9e39ec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Thu, 29 Sep 2022 10:18:54 -0500 Subject: [PATCH 2866/5615] Update latest version in mkdocs.yml --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 87b19de4f0a..ab57aeb11d6 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -108,4 +108,4 @@ nav: - contributing_docs.md - bounty.md extra: - latest_version: 1.17.3 + latest_version: 1.17.4 From 08f75a5217f601439ef426b059908164a02deba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Thu, 29 Sep 2022 10:21:11 -0500 Subject: [PATCH 2867/5615] Upgrade testcontainers version to 1.17.4 in japicmp --- gradle/japicmp.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/japicmp.gradle b/gradle/japicmp.gradle index 541736a8321..6d769bf9286 100644 --- a/gradle/japicmp.gradle +++ b/gradle/japicmp.gradle @@ -3,7 +3,7 @@ configurations { } dependencies { - baseline "org.testcontainers:${project.name}:1.17.3", { + baseline "org.testcontainers:${project.name}:1.17.4", { exclude group: "*", module: "*" } } From ce003ce6c42b423823501503e77d809508c1b502 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:18 +0000 Subject: [PATCH 2868/5615] Bump aws-java-sdk-dynamodb in /modules/dynalite Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.12.295 to 1.12.313. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.295...1.12.313) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-dynamodb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/dynalite/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index ac9ff346bea..e9cd2111f82 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Dynalite" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.295' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.313' testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.295' testImplementation 'org.assertj:assertj-core:3.23.1' } From d48fef2fab6f82d6707e18fbaaf591e7de606f99 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:18 +0000 Subject: [PATCH 2869/5615] Bump snakeyaml from 1.31 to 1.33 in /core Bumps [snakeyaml](https://bitbucket.org/snakeyaml/snakeyaml) from 1.31 to 1.33. - [Commits](https://bitbucket.org/snakeyaml/snakeyaml/branches/compare/snakeyaml-1.33..snakeyaml-1.31) --- updated-dependencies: - dependency-name: org.yaml:snakeyaml dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 4072c25db37..08ca6d777c5 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -80,7 +80,7 @@ dependencies { api 'com.github.docker-java:docker-java-transport-zerodep' - shaded "org.yaml:snakeyaml:1.31" + shaded "org.yaml:snakeyaml:1.33" shaded 'org.glassfish.main.external:trilead-ssh2-repackaged:4.1.2' From 052b44a76179a3760b6e2ec2b0a30c17d7e22b4e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:23 +0000 Subject: [PATCH 2870/5615] Bump java-driver-core from 4.14.1 to 4.15.0 in /modules/cassandra Bumps [java-driver-core](https://github.com/datastax/java-driver) from 4.14.1 to 4.15.0. - [Release notes](https://github.com/datastax/java-driver/releases) - [Commits](https://github.com/datastax/java-driver/compare/4.14.1...4.15.0) --- updated-dependencies: - dependency-name: com.datastax.oss:java-driver-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/cassandra/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cassandra/build.gradle b/modules/cassandra/build.gradle index ff110637ced..ae115c92a71 100644 --- a/modules/cassandra/build.gradle +++ b/modules/cassandra/build.gradle @@ -10,6 +10,6 @@ dependencies { api project(":database-commons") api "com.datastax.cassandra:cassandra-driver-core:3.10.0" - testImplementation 'com.datastax.oss:java-driver-core:4.14.1' + testImplementation 'com.datastax.oss:java-driver-core:4.15.0' testImplementation 'org.assertj:assertj-core:3.23.1' } From dcf8df081a3077d3300dfdd2f7998e6b6de52e11 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:27 +0000 Subject: [PATCH 2871/5615] Bump junit-jupiter-params from 5.9.0 to 5.9.1 in /modules/junit-jupiter Bumps [junit-jupiter-params](https://github.com/junit-team/junit5) from 5.9.0 to 5.9.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.9.0...r5.9.1) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-params dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 5b91bd181fe..0ef2ef20ab7 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -13,7 +13,7 @@ dependencies { exclude(module: 'hamcrest-core') } testImplementation 'org.assertj:assertj-core:3.23.1' - testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.0' + testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.1' testRuntimeOnly 'org.postgresql:postgresql:42.5.0' testRuntimeOnly 'mysql:mysql-connector-java:8.0.30' From d1220f453d1350488e65868a266f817980128f96 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:35 +0000 Subject: [PATCH 2872/5615] Bump mockito-core from 4.7.0 to 4.8.0 in /modules/junit-jupiter Bumps [mockito-core](https://github.com/mockito/mockito) from 4.7.0 to 4.8.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.7.0...v4.8.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 5b91bd181fe..085ad9f1893 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,7 +9,7 @@ dependencies { testImplementation 'com.zaxxer:HikariCP:4.0.3' testImplementation 'redis.clients:jedis:4.2.3' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' - testImplementation ('org.mockito:mockito-core:4.7.0') { + testImplementation ('org.mockito:mockito-core:4.8.0') { exclude(module: 'hamcrest-core') } testImplementation 'org.assertj:assertj-core:3.23.1' From 4c6a67dd0a6858d3c6985350f140e007201a7069 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:37 +0000 Subject: [PATCH 2873/5615] Bump release-drafter/release-drafter from 5.20.1 to 5.21.0 Bumps [release-drafter/release-drafter](https://github.com/release-drafter/release-drafter) from 5.20.1 to 5.21.0. - [Release notes](https://github.com/release-drafter/release-drafter/releases) - [Commits](https://github.com/release-drafter/release-drafter/compare/06a49bf28488e030d35ca2ac6dbf7f408a481779...df69d584deac33d8569990cb6413f82447181076) --- updated-dependencies: - dependency-name: release-drafter/release-drafter dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/release-drafter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 94af563bcda..9315b7c342f 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -18,6 +18,6 @@ jobs: if: github.repository == 'testcontainers/testcontainers-java' runs-on: ubuntu-latest steps: - - uses: release-drafter/release-drafter@06a49bf28488e030d35ca2ac6dbf7f408a481779 # v5.19.0 + - uses: release-drafter/release-drafter@df69d584deac33d8569990cb6413f82447181076 # v5.19.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From c8d841668bcbb2f8aaae2574a69ee20e699008c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:38 +0000 Subject: [PATCH 2874/5615] Bump mockito-core from 4.7.0 to 4.8.0 in /core Bumps [mockito-core](https://github.com/mockito/mockito) from 4.7.0 to 4.8.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.7.0...v4.8.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 4072c25db37..ed85a732f24 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -91,7 +91,7 @@ dependencies { testImplementation 'com.rabbitmq:amqp-client:5.15.0' testImplementation 'org.mongodb:mongo-java-driver:3.12.11' - testImplementation ('org.mockito:mockito-core:4.7.0') { + testImplementation ('org.mockito:mockito-core:4.8.0') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From 05862eb6321a3bc8bf7ae690106eb5c996cc54df Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:39 +0000 Subject: [PATCH 2875/5615] Bump aws-java-sdk-s3 from 1.12.295 to 1.12.313 in /modules/localstack Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.295 to 1.12.313. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.295...1.12.313) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 2b38b50f162..58be6dceae6 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Localstack" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.295' - testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.285' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.313' + testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.313' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.295' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.295' testImplementation 'software.amazon.awssdk:s3:2.17.266' From 5ecb572ee3c058d6a4c3253ec80832b32149b6ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:40 +0000 Subject: [PATCH 2876/5615] Bump junit-jupiter-engine from 5.9.0 to 5.9.1 in /modules/junit-jupiter Bumps [junit-jupiter-engine](https://github.com/junit-team/junit5) from 5.9.0 to 5.9.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.9.0...r5.9.1) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-engine dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 5b91bd181fe..e7456adc1e3 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -17,7 +17,7 @@ dependencies { testRuntimeOnly 'org.postgresql:postgresql:42.5.0' testRuntimeOnly 'mysql:mysql-connector-java:8.0.30' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.1' } test { From 5f4fbf85c585ea828adb8576740852defa9f858c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:42 +0000 Subject: [PATCH 2877/5615] Bump peter-evans/create-pull-request from 4.1.1 to 4.1.3 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4.1.1 to 4.1.3. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/18f90432bedd2afd6a825469ffd38aa24712a91d...671dc9c9e0c2d73f07fa45a3eb0220e1622f0c5f) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/update-docs-version.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index 070fa2c55d4..983f872c4f8 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -23,7 +23,7 @@ jobs: sed -i "s/latest_version: .*/latest_version: ${GITHUB_REF##*/}/g" mkdocs.yml git diff - name: Create Pull Request - uses: peter-evans/create-pull-request@18f90432bedd2afd6a825469ffd38aa24712a91d # v3.10.1 + uses: peter-evans/create-pull-request@671dc9c9e0c2d73f07fa45a3eb0220e1622f0c5f # v3.10.1 with: title: Update docs version to ${GITHUB_REF##*/} body: | From 912b017d04fc4c4204850dffd5b0d53225cd7063 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:43 +0000 Subject: [PATCH 2878/5615] Bump amqp-client from 5.15.0 to 5.16.0 in /core Bumps [amqp-client](https://github.com/rabbitmq/rabbitmq-java-client) from 5.15.0 to 5.16.0. - [Release notes](https://github.com/rabbitmq/rabbitmq-java-client/releases) - [Commits](https://github.com/rabbitmq/rabbitmq-java-client/compare/v5.15.0...v5.16.0) --- updated-dependencies: - dependency-name: com.rabbitmq:amqp-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 4072c25db37..c9f849a9265 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -88,7 +88,7 @@ dependencies { testImplementation 'org.apache.httpcomponents:httpclient:4.5.9' testImplementation 'redis.clients:jedis:4.2.3' - testImplementation 'com.rabbitmq:amqp-client:5.15.0' + testImplementation 'com.rabbitmq:amqp-client:5.16.0' testImplementation 'org.mongodb:mongo-java-driver:3.12.11' testImplementation ('org.mockito:mockito-core:4.7.0') { From 81619973fc9a66d98a0198d90158506905864efc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:46 +0000 Subject: [PATCH 2879/5615] Bump junit-jupiter-api from 5.9.0 to 5.9.1 in /modules/junit-jupiter Bumps [junit-jupiter-api](https://github.com/junit-team/junit5) from 5.9.0 to 5.9.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.9.0...r5.9.1) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 5b91bd181fe..575e367512c 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -2,7 +2,7 @@ description = "Testcontainers :: JUnit Jupiter Extension" dependencies { api project(':testcontainers') - api 'org.junit.jupiter:junit-jupiter-api:5.9.0' + api 'org.junit.jupiter:junit-jupiter-api:5.9.1' testImplementation project(':mysql') testImplementation project(':postgresql') From 785c08e6cba914d8092c8e00edf883e9ed390b76 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:48 +0000 Subject: [PATCH 2880/5615] Bump java-client from 3.3.3 to 3.3.4 in /modules/couchbase Bumps [java-client](https://github.com/couchbase/couchbase-jvm-clients) from 3.3.3 to 3.3.4. - [Release notes](https://github.com/couchbase/couchbase-jvm-clients/releases) - [Commits](https://github.com/couchbase/couchbase-jvm-clients/compare/java-client-3.3.3...java-client-3.3.4) --- updated-dependencies: - dependency-name: com.couchbase.client:java-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 5950aac7655..0542a5d68d0 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -5,7 +5,7 @@ dependencies { // TODO use JDK's HTTP client and/or Apache HttpClient5 shaded 'com.squareup.okhttp3:okhttp:4.10.0' - testImplementation 'com.couchbase.client:java-client:3.3.3' + testImplementation 'com.couchbase.client:java-client:3.3.4' testImplementation 'org.awaitility:awaitility:4.2.0' testImplementation 'org.assertj:assertj-core:3.23.1' } From aea8f74fee1b1da6f2a01f17741f8a7729f941ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:52 +0000 Subject: [PATCH 2881/5615] Bump tomcat-jdbc from 10.0.23 to 10.1.0 in /modules/jdbc Bumps tomcat-jdbc from 10.0.23 to 10.1.0. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index ba0f845b47d..14c4a431d00 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -7,7 +7,7 @@ dependencies { compileOnly 'org.jetbrains:annotations:23.0.0' testImplementation 'commons-dbutils:commons-dbutils:1.7' testImplementation 'org.vibur:vibur-dbcp:25.0' - testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.23' + testImplementation 'org.apache.tomcat:tomcat-jdbc:10.1.0' testImplementation 'com.zaxxer:HikariCP-java6:2.3.13' testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation ('org.mockito:mockito-core:4.7.0') { From fa7a2c3536a3006e86245bb3dc82a6d1a39f38df Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:55 +0000 Subject: [PATCH 2882/5615] Bump aws-java-sdk-logs from 1.12.295 to 1.12.313 in /modules/localstack Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.12.295 to 1.12.313. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.295...1.12.313) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-logs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 2b38b50f162..8ec3a442bc4 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,7 +6,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.295' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.285' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.295' - testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.295' + testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.313' testImplementation 'software.amazon.awssdk:s3:2.17.266' testImplementation 'org.assertj:assertj-core:3.23.1' } From fcd3f607bd3e2d0c07da6ef83d4d1687da93028d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:58 +0000 Subject: [PATCH 2883/5615] Bump common-custom-user-data-gradle-plugin in /examples Bumps common-custom-user-data-gradle-plugin from 1.8 to 1.8.1. --- updated-dependencies: - dependency-name: com.gradle:common-custom-user-data-gradle-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/settings.gradle b/examples/settings.gradle index 7194134350d..a4e4bddd880 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -7,7 +7,7 @@ buildscript { dependencies { classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.10.0" classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.11.1" - classpath "com.gradle:common-custom-user-data-gradle-plugin:1.8" + classpath "com.gradle:common-custom-user-data-gradle-plugin:1.8.1" } } From 92f072f3dea0790aebb5273429e9c7be31d1f10d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:02 +0000 Subject: [PATCH 2884/5615] Bump mockito-core from 4.7.0 to 4.8.0 in /modules/jdbc Bumps [mockito-core](https://github.com/mockito/mockito) from 4.7.0 to 4.8.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.7.0...v4.8.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index ba0f845b47d..bf590c08748 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -10,7 +10,7 @@ dependencies { testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.23' testImplementation 'com.zaxxer:HikariCP-java6:2.3.13' testImplementation 'org.assertj:assertj-core:3.23.1' - testImplementation ('org.mockito:mockito-core:4.7.0') { + testImplementation ('org.mockito:mockito-core:4.8.0') { exclude(module: 'hamcrest-core') } } From 502a2abf5808e4d102538146c8415dfcf64d9948 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:02 +0000 Subject: [PATCH 2885/5615] Bump trino-jdbc from 394 to 398 in /modules/trino Bumps trino-jdbc from 394 to 398. --- updated-dependencies: - dependency-name: io.trino:trino-jdbc dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/trino/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/trino/build.gradle b/modules/trino/build.gradle index ca2619a8631..c534f14bd2a 100644 --- a/modules/trino/build.gradle +++ b/modules/trino/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'io.trino:trino-jdbc:394' + testImplementation 'io.trino:trino-jdbc:398' compileOnly 'org.jetbrains:annotations:23.0.0' } From 9f2e2c14c6f7a8cf47e8ac73a0f3b2a40ed62d37 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:14 +0000 Subject: [PATCH 2886/5615] Bump google-cloud-pubsub from 1.120.13 to 1.120.18 in /modules/gcloud Bumps [google-cloud-pubsub](https://github.com/googleapis/java-pubsub) from 1.120.13 to 1.120.18. - [Release notes](https://github.com/googleapis/java-pubsub/releases) - [Changelog](https://github.com/googleapis/java-pubsub/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-pubsub/compare/v1.120.13...v1.120.18) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-pubsub dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 044d82cbb21..aa0b151f6f8 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -5,7 +5,7 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-datastore:2.11.0' testImplementation 'com.google.cloud:google-cloud-firestore:3.4.2' - testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.13' + testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.18' testImplementation 'com.google.cloud:google-cloud-spanner:6.29.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.11.1' testImplementation 'org.assertj:assertj-core:3.23.1' From 826657c84ff24b3a8a15ee5bd65fd1bd2a179fee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:16 +0000 Subject: [PATCH 2887/5615] Bump mssql-jdbc from 11.2.0.jre8 to 11.2.1.jre8 in /modules/mssqlserver Bumps [mssql-jdbc](https://github.com/Microsoft/mssql-jdbc) from 11.2.0.jre8 to 11.2.1.jre8. - [Release notes](https://github.com/Microsoft/mssql-jdbc/releases) - [Changelog](https://github.com/microsoft/mssql-jdbc/blob/main/CHANGELOG.md) - [Commits](https://github.com/Microsoft/mssql-jdbc/commits) --- updated-dependencies: - dependency-name: com.microsoft.sqlserver:mssql-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mssqlserver/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index cc05b6ffea4..ef91edc79d1 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'io.r2dbc:r2dbc-mssql:0.9.0.RELEASE' testImplementation project(':jdbc-test') - testImplementation 'com.microsoft.sqlserver:mssql-jdbc:11.2.0.jre8' + testImplementation 'com.microsoft.sqlserver:mssql-jdbc:11.2.1.jre8' testImplementation project(':r2dbc') testImplementation 'io.r2dbc:r2dbc-mssql:0.8.7.RELEASE' From 6f8c1a536c2f1f3b4242b75d2c590870ccf005f6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:20 +0000 Subject: [PATCH 2888/5615] Bump mariadb-java-client from 3.0.7 to 3.0.8 in /modules/mariadb Bumps [mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 3.0.7 to 3.0.8. - [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases) - [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md) - [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/3.0.7...3.0.8) --- updated-dependencies: - dependency-name: org.mariadb.jdbc:mariadb-java-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 1a11f0fbfb6..b4b3b3b4415 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'org.mariadb:r2dbc-mariadb:1.0.3' testImplementation project(':jdbc-test') - testImplementation 'org.mariadb.jdbc:mariadb-java-client:3.0.7' + testImplementation 'org.mariadb.jdbc:mariadb-java-client:3.0.8' testImplementation testFixtures(project(':r2dbc')) testImplementation 'org.mariadb:r2dbc-mariadb:1.0.2' From 50c03510cb4e11ad398c726437e0dee26cc2e3b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:23 +0000 Subject: [PATCH 2889/5615] Bump google-cloud-datastore from 2.11.0 to 2.11.4 in /modules/gcloud Bumps [google-cloud-datastore](https://github.com/googleapis/java-datastore) from 2.11.0 to 2.11.4. - [Release notes](https://github.com/googleapis/java-datastore/releases) - [Changelog](https://github.com/googleapis/java-datastore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-datastore/compare/v2.11.0...v2.11.4) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-datastore dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 044d82cbb21..2758d755c56 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: GCloud" dependencies { api project(':testcontainers') - testImplementation 'com.google.cloud:google-cloud-datastore:2.11.0' + testImplementation 'com.google.cloud:google-cloud-datastore:2.11.4' testImplementation 'com.google.cloud:google-cloud-firestore:3.4.2' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.13' testImplementation 'com.google.cloud:google-cloud-spanner:6.29.0' From 00f10e3d1388819a792efb0fc218ca40015ce6df Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:25 +0000 Subject: [PATCH 2890/5615] Bump azure-cosmos from 4.35.1 to 4.36.0 in /modules/azure Bumps [azure-cosmos](https://github.com/Azure/azure-sdk-for-java) from 4.35.1 to 4.36.0. - [Release notes](https://github.com/Azure/azure-sdk-for-java/releases) - [Commits](https://github.com/Azure/azure-sdk-for-java/compare/azure-cosmos_4.35.1...azure-cosmos_4.36.0) --- updated-dependencies: - dependency-name: com.azure:azure-cosmos dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/azure/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle index 8753606874c..43280db4a88 100644 --- a/modules/azure/build.gradle +++ b/modules/azure/build.gradle @@ -6,5 +6,5 @@ dependencies { shaded 'com.squareup.okhttp3:okhttp:4.10.0' testImplementation 'org.assertj:assertj-core:3.23.1' - testImplementation 'com.azure:azure-cosmos:4.35.1' + testImplementation 'com.azure:azure-cosmos:4.36.0' } From 54493fb3c9fe995682890c05997d8b831335678b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:31 +0000 Subject: [PATCH 2891/5615] Bump reactor-core from 3.4.22 to 3.4.23 in /modules/r2dbc Bumps [reactor-core](https://github.com/reactor/reactor-core) from 3.4.22 to 3.4.23. - [Release notes](https://github.com/reactor/reactor-core/releases) - [Commits](https://github.com/reactor/reactor-core/compare/v3.4.22...v3.4.23) --- updated-dependencies: - dependency-name: io.projectreactor:reactor-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/r2dbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle index c55b71ddcb2..9126ff0a347 100644 --- a/modules/r2dbc/build.gradle +++ b/modules/r2dbc/build.gradle @@ -15,6 +15,6 @@ dependencies { testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.13.RELEASE' testImplementation project(':postgresql') - testFixturesImplementation 'io.projectreactor:reactor-core:3.4.22' + testFixturesImplementation 'io.projectreactor:reactor-core:3.4.23' testFixturesImplementation 'org.assertj:assertj-core:3.23.1' } From 569ae4c8112b2c95ef7dab82e6f5db6c763bb51b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:32 +0000 Subject: [PATCH 2892/5615] Bump amqp-client from 5.15.0 to 5.16.0 in /modules/rabbitmq Bumps [amqp-client](https://github.com/rabbitmq/rabbitmq-java-client) from 5.15.0 to 5.16.0. - [Release notes](https://github.com/rabbitmq/rabbitmq-java-client/releases) - [Commits](https://github.com/rabbitmq/rabbitmq-java-client/compare/v5.15.0...v5.16.0) --- updated-dependencies: - dependency-name: com.rabbitmq:amqp-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/rabbitmq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/rabbitmq/build.gradle b/modules/rabbitmq/build.gradle index 9825a27a499..6cf03d4f984 100644 --- a/modules/rabbitmq/build.gradle +++ b/modules/rabbitmq/build.gradle @@ -2,7 +2,7 @@ description = "TestContainers :: RabbitMQ" dependencies { api project(":testcontainers") - testImplementation 'com.rabbitmq:amqp-client:5.15.0' + testImplementation 'com.rabbitmq:amqp-client:5.16.0' testImplementation 'org.assertj:assertj-core:3.23.1' compileOnly 'org.jetbrains:annotations:23.0.0' } From 73596740f3ece5f93af4f1b3e8a2f9e24595f227 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:35 +0000 Subject: [PATCH 2893/5615] Bump elasticsearch-rest-client in /modules/elasticsearch Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 8.4.1 to 8.4.2. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Changelog](https://github.com/elastic/elasticsearch/blob/main/CHANGELOG.md) - [Commits](https://github.com/elastic/elasticsearch/compare/v8.4.1...v8.4.2) --- updated-dependencies: - dependency-name: org.elasticsearch.client:elasticsearch-rest-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index c5591d0d26a..04521b974ea 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,7 +2,7 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') - testImplementation "org.elasticsearch.client:elasticsearch-rest-client:8.4.1" + testImplementation "org.elasticsearch.client:elasticsearch-rest-client:8.4.2" testImplementation "org.elasticsearch.client:transport:7.17.6" testImplementation 'org.assertj:assertj-core:3.23.1' } From 25af50d16d32e66a4c05299610a23077905828cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:35 +0000 Subject: [PATCH 2894/5615] Bump kafka-clients from 3.2.1 to 3.3.0 in /modules/redpanda Bumps kafka-clients from 3.2.1 to 3.3.0. --- updated-dependencies: - dependency-name: org.apache.kafka:kafka-clients dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/redpanda/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/redpanda/build.gradle b/modules/redpanda/build.gradle index 5750e873bf6..4d7221711a3 100644 --- a/modules/redpanda/build.gradle +++ b/modules/redpanda/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Redpanda" dependencies { api project(':testcontainers') - testImplementation 'org.apache.kafka:kafka-clients:3.2.1' + testImplementation 'org.apache.kafka:kafka-clients:3.3.0' testImplementation 'org.assertj:assertj-core:3.23.1' } From 9af2f2282caf9f57bfdb1e582405889782097c78 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:39 +0000 Subject: [PATCH 2895/5615] Bump common-custom-user-data-gradle-plugin from 1.8 to 1.8.1 Bumps common-custom-user-data-gradle-plugin from 1.8 to 1.8.1. --- updated-dependencies: - dependency-name: com.gradle:common-custom-user-data-gradle-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index 2beb91dea01..f0c1d306671 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.11.1" - classpath "com.gradle:common-custom-user-data-gradle-plugin:1.8" + classpath "com.gradle:common-custom-user-data-gradle-plugin:1.8.1" } } From ea4053bfa065616be0c2c84276279bd60fd16b77 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:43 +0000 Subject: [PATCH 2896/5615] Bump rest-assured from 5.1.1 to 5.2.0 in /modules/consul Bumps [rest-assured](https://github.com/rest-assured/rest-assured) from 5.1.1 to 5.2.0. - [Release notes](https://github.com/rest-assured/rest-assured/releases) - [Changelog](https://github.com/rest-assured/rest-assured/blob/master/changelog.txt) - [Commits](https://github.com/rest-assured/rest-assured/compare/rest-assured-5.1.1...rest-assured-5.2.0) --- updated-dependencies: - dependency-name: io.rest-assured:rest-assured dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/consul/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/consul/build.gradle b/modules/consul/build.gradle index acb97a47927..a72aefaab5f 100644 --- a/modules/consul/build.gradle +++ b/modules/consul/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':testcontainers') testImplementation 'com.ecwid.consul:consul-api:1.4.5' - testImplementation 'io.rest-assured:rest-assured:5.1.1' + testImplementation 'io.rest-assured:rest-assured:5.2.0' testImplementation 'org.assertj:assertj-core:3.23.1' } From 80ced857e13cad960b7c781f3b1dd70cb54332aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:45 +0000 Subject: [PATCH 2897/5615] Bump rest-assured from 5.1.1 to 5.2.0 in /modules/vault Bumps [rest-assured](https://github.com/rest-assured/rest-assured) from 5.1.1 to 5.2.0. - [Release notes](https://github.com/rest-assured/rest-assured/releases) - [Changelog](https://github.com/rest-assured/rest-assured/blob/master/changelog.txt) - [Commits](https://github.com/rest-assured/rest-assured/compare/rest-assured-5.1.1...rest-assured-5.2.0) --- updated-dependencies: - dependency-name: io.rest-assured:rest-assured dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index 499286ae72a..c9142936b63 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':testcontainers') testImplementation 'com.bettercloud:vault-java-driver:5.1.0' - testImplementation 'io.rest-assured:rest-assured:5.1.1' + testImplementation 'io.rest-assured:rest-assured:5.2.0' testImplementation 'org.assertj:assertj-core:3.23.1' } From 29b18d150c2c9f98fb15ec5a38a32f3b4b989833 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:52 +0000 Subject: [PATCH 2898/5615] Bump logback-classic from 1.4.0 to 1.4.1 in /modules/hivemq Bumps [logback-classic](https://github.com/qos-ch/logback) from 1.4.0 to 1.4.1. - [Release notes](https://github.com/qos-ch/logback/releases) - [Commits](https://github.com/qos-ch/logback/compare/v_1.4.0...v_1.4.1) --- updated-dependencies: - dependency-name: ch.qos.logback:logback-classic dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index 477d5ac98a3..0520440828f 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -16,7 +16,7 @@ dependencies { testImplementation("com.hivemq:hivemq-extension-sdk:4.8.3") testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") testImplementation("org.apache.httpcomponents:httpclient:4.5.13") - testImplementation("ch.qos.logback:logback-classic:1.4.0") + testImplementation("ch.qos.logback:logback-classic:1.4.1") testImplementation 'org.assertj:assertj-core:3.23.1' testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.0") } From e094aceeef5d76b57018b4dd453c686f4883ed0a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:56 +0000 Subject: [PATCH 2899/5615] Bump json from 20220320 to 20220924 in /examples Bumps [json](https://github.com/douglascrockford/JSON-java) from 20220320 to 20220924. - [Release notes](https://github.com/douglascrockford/JSON-java/releases) - [Changelog](https://github.com/stleary/JSON-java/blob/master/docs/RELEASES.md) - [Commits](https://github.com/douglascrockford/JSON-java/commits) --- updated-dependencies: - dependency-name: org.json:json dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 5d9fb93c20d..fe230f3a22e 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:2.0.0' implementation 'com.squareup.okhttp3:okhttp:4.10.0' - implementation 'org.json:json:20220320' + implementation 'org.json:json:20220924' testImplementation 'org.postgresql:postgresql:42.5.0' testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.testcontainers:postgresql' From 942923183b8946b8c4c7edb8d5a24bfbb5748611 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:59 +0000 Subject: [PATCH 2900/5615] Bump hivemq-extension-sdk from 4.8.3 to 4.9.0 in /modules/hivemq Bumps [hivemq-extension-sdk](https://github.com/hivemq/hivemq-extension-sdk) from 4.8.3 to 4.9.0. - [Release notes](https://github.com/hivemq/hivemq-extension-sdk/releases) - [Commits](https://github.com/hivemq/hivemq-extension-sdk/compare/4.8.3...4.9.0) --- updated-dependencies: - dependency-name: com.hivemq:hivemq-extension-sdk dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index 477d5ac98a3..b6b9a42c425 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -13,7 +13,7 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.0") testImplementation(project(":junit-jupiter")) - testImplementation("com.hivemq:hivemq-extension-sdk:4.8.3") + testImplementation("com.hivemq:hivemq-extension-sdk:4.9.0") testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") testImplementation("org.apache.httpcomponents:httpclient:4.5.13") testImplementation("ch.qos.logback:logback-classic:1.4.0") From 27bb1aef9800f8b33c413133b970c6dbecd4aa1c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:08:03 +0000 Subject: [PATCH 2901/5615] Bump junit-jupiter-api from 5.9.0 to 5.9.1 in /modules/hivemq Bumps [junit-jupiter-api](https://github.com/junit-team/junit5) from 5.9.0 to 5.9.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.9.0...r5.9.1) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index 477d5ac98a3..50b857d7ed6 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -11,7 +11,7 @@ dependencies { shaded("org.jboss.shrinkwrap:shrinkwrap-impl-base:1.2.6") shaded("net.lingala.zip4j:zip4j:2.11.1") - testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.0") + testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.1") testImplementation(project(":junit-jupiter")) testImplementation("com.hivemq:hivemq-extension-sdk:4.8.3") testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") From f7bd0350106a97eafb53a5ff722b63d63cc5ce67 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:08:06 +0000 Subject: [PATCH 2902/5615] Bump org.springframework.boot from 2.7.3 to 2.7.4 in /examples Bumps [org.springframework.boot](https://github.com/spring-projects/spring-boot) from 2.7.3 to 2.7.4. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.7.3...v2.7.4) --- updated-dependencies: - dependency-name: org.springframework.boot dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot-kotlin-redis/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index 6183b89e1d6..feb677dc64a 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.7.3' + id 'org.springframework.boot' version '2.7.4' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 180d6780c8f..6a98573172c 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,5 +1,5 @@ plugins { - id("org.springframework.boot") version "2.7.3" + id("org.springframework.boot") version "2.7.4" id("org.jetbrains.kotlin.jvm") version "1.7.10" id("org.jetbrains.kotlin.plugin.spring") version "1.7.10" } diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index 52a4a63386a..56cd62e34e6 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.7.3' + id 'org.springframework.boot' version '2.7.4' } apply plugin: 'io.spring.dependency-management' From 5c9d06d226bcb23c00eaa02ff94588dd83d582df Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:08:07 +0000 Subject: [PATCH 2903/5615] Bump kafka-clients from 3.2.1 to 3.3.0 in /examples Bumps kafka-clients from 3.2.1 to 3.3.0. --- updated-dependencies: - dependency-name: org.apache.kafka:kafka-clients dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/kafka-cluster/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index c3a79d4058f..08d3734a956 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -10,7 +10,7 @@ dependencies { testCompileOnly "org.projectlombok:lombok:1.18.24" testAnnotationProcessor "org.projectlombok:lombok:1.18.24" testImplementation 'org.testcontainers:kafka' - testImplementation 'org.apache.kafka:kafka-clients:3.2.1' + testImplementation 'org.apache.kafka:kafka-clients:3.3.0' testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'com.google.guava:guava:23.0' testImplementation 'ch.qos.logback:logback-classic:1.2.11' From 4ec5af3f90e6bb9da32297067e7519a89b61dcb3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:08:08 +0000 Subject: [PATCH 2904/5615] Bump junit-jupiter-engine from 5.9.0 to 5.9.1 in /modules/hivemq Bumps [junit-jupiter-engine](https://github.com/junit-team/junit5) from 5.9.0 to 5.9.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.9.0...r5.9.1) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-engine dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index 477d5ac98a3..511e504f8ef 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -18,7 +18,7 @@ dependencies { testImplementation("org.apache.httpcomponents:httpclient:4.5.13") testImplementation("ch.qos.logback:logback-classic:1.4.0") testImplementation 'org.assertj:assertj-core:3.23.1' - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.0") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.1") } test { From 21e09603751d887da68dc28b0ea431e1758f2beb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:08:16 +0000 Subject: [PATCH 2905/5615] Bump zip4j from 2.11.1 to 2.11.2 in /modules/hivemq Bumps [zip4j](https://github.com/srikanth-lingala/zip4j) from 2.11.1 to 2.11.2. - [Release notes](https://github.com/srikanth-lingala/zip4j/releases) - [Commits](https://github.com/srikanth-lingala/zip4j/compare/v2.11.1...v2.11.2) --- updated-dependencies: - dependency-name: net.lingala.zip4j:zip4j dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index 477d5ac98a3..0c336745f77 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -9,7 +9,7 @@ dependencies { shaded("org.javassist:javassist:3.29.0-GA") shaded("org.jboss.shrinkwrap:shrinkwrap-api:1.2.6") shaded("org.jboss.shrinkwrap:shrinkwrap-impl-base:1.2.6") - shaded("net.lingala.zip4j:zip4j:2.11.1") + shaded("net.lingala.zip4j:zip4j:2.11.2") testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.0") testImplementation(project(":junit-jupiter")) From ff13685d346a6329268b2b5de0f82b8323117087 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:08:17 +0000 Subject: [PATCH 2906/5615] Bump cucumber-junit from 7.6.0 to 7.8.0 in /examples Bumps [cucumber-junit](https://github.com/cucumber/cucumber-jvm) from 7.6.0 to 7.8.0. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.6.0...v7.8.0) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-junit dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index ab0331f81f4..9d0da7a5e33 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' testImplementation 'io.cucumber:cucumber-java:7.6.0' - testImplementation 'io.cucumber:cucumber-junit:7.6.0' + testImplementation 'io.cucumber:cucumber-junit:7.8.0' testImplementation 'org.testcontainers:selenium' } From 6b3c7b4fa132021bc443ca09e1322bcc5a2dbb62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Thu, 29 Sep 2022 18:09:45 -0500 Subject: [PATCH 2907/5615] Rename main branch (#5847) `main` is the new default branch name. --- .github/workflows/ci-docker-wormhole.yml | 2 +- .github/workflows/ci-examples.yml | 2 +- .github/workflows/ci-rootless.yml | 2 +- .github/workflows/ci.yml | 2 +- .github/workflows/release-drafter.yml | 2 +- .github/workflows/update-docs-version.yml | 2 +- RELEASING.md | 2 +- docs/contributing.md | 2 +- docs/examples.md | 22 +++++++++++----------- docs/index.md | 4 ++-- docs/jitpack_dependencies.md | 4 ++-- docs/modules/consul.md | 2 +- docs/modules/databases/jdbc.md | 4 ++-- docs/modules/databases/mssqlserver.md | 4 ++-- docs/modules/gcloud.md | 8 ++++---- docs/modules/vault.md | 4 ++-- docs/modules/webdriver_containers.md | 2 +- 17 files changed, 35 insertions(+), 35 deletions(-) diff --git a/.github/workflows/ci-docker-wormhole.yml b/.github/workflows/ci-docker-wormhole.yml index e27e4d8d611..061a004682e 100644 --- a/.github/workflows/ci-docker-wormhole.yml +++ b/.github/workflows/ci-docker-wormhole.yml @@ -2,7 +2,7 @@ name: CI-Docker-Wormhole on: pull_request: {} - push: { branches: [ master ] } + push: { branches: [ main ] } permissions: contents: read diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index fe4610368aa..2cdc9f8ed69 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -2,7 +2,7 @@ name: CI-Examples on: pull_request: {} - push: { branches: [ master ] } + push: { branches: [ main ] } env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} diff --git a/.github/workflows/ci-rootless.yml b/.github/workflows/ci-rootless.yml index 348cf5aaaf7..50db1655a4a 100644 --- a/.github/workflows/ci-rootless.yml +++ b/.github/workflows/ci-rootless.yml @@ -2,7 +2,7 @@ name: CI-Docker-Rootless on: pull_request: {} - push: { branches: [ master ] } + push: { branches: [ main ] } permissions: contents: read diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e1eceab8702..80f20fb0c0c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: CI on: pull_request: {} - push: { branches: [ master ] } + push: { branches: [ maing ] } permissions: contents: read diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 94af563bcda..46b1e261789 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -3,7 +3,7 @@ name: Release Drafter on: push: branches: - - master + - main pull_request: types: [opened, reopened, synchronize] diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index 070fa2c55d4..2c54f92efcf 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -17,7 +17,7 @@ jobs: steps: - uses: actions/checkout@v3 with: - ref: master + ref: main - name: Update latest_version property in mkdocs.yml run: | sed -i "s/latest_version: .*/latest_version: ${GITHUB_REF##*/}/g" mkdocs.yml diff --git a/RELEASING.md b/RELEASING.md index f04839d9d41..8027cb9cc98 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -4,7 +4,7 @@ Testcontainers' release process is semi-automated through GitHub Actions. This d ## Steps -1. Ensure that the master branch is building and that tests are passing. +1. Ensure that the `main` branch is building and that tests are passing. 1. Create a new release on GitHub. **The tag name is used as the version**, so please keep the tag name plain (e.g. 1.2.3). 1. The release triggers a GitHub Action workflow. 1. Log in to [Sonatype](https://oss.sonatype.org/) to check the staging repository. diff --git a/docs/contributing.md b/docs/contributing.md index 01c4fce57b6..1457839a5ec 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -101,7 +101,7 @@ For this, we are using the [gh-combine-prs](https://github.com/rnorth/gh-combine The whole process is as follow: 1. Check that all open Dependabot PRs did succeed their build. If they did not succeed, trigger a rerun if the cause were external factors or else document the reason if obvious. -2. Run the extension from an up-to-date local `master` branch: `gh combine-prs --query "author:app/dependabot"` +2. Run the extension from an up-to-date local `main` branch: `gh combine-prs --query "author:app/dependabot"` 3. Merge conflicts might appear. Just ignore them, we will get those PRs in a future run. 4. Once the build of the combined PR did succeed, temporarily enable merge commits and merge the PR using a merge commit through the GitHub UI. 5. After the merge, disable merge commits again. diff --git a/docs/examples.md b/docs/examples.md index 918688d86cb..14f43d21e23 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -2,14 +2,14 @@ Examples of different use cases provided by Testcontainers can be found below: -- [Kafka Cluster with multiple brokers](https://github.com/testcontainers/testcontainers-java/tree/master/examples/kafka-cluster) -- [Linked containers](https://github.com/testcontainers/testcontainers-java/tree/master/examples/linked-container) -- [Neo4j](https://github.com/testcontainers/testcontainers-java/tree/master/examples/neo4j-container) -- [Redis](https://github.com/testcontainers/testcontainers-java/tree/master/examples/redis-backed-cache) -- [Selenium](https://github.com/testcontainers/testcontainers-java/tree/master/examples/selenium-container) -- [Selenium Module with Cucumber](https://github.com/testcontainers/testcontainers-java/tree/master/examples/cucumber) -- [Singleton Container Pattern](https://github.com/testcontainers/testcontainers-java/tree/master/examples/singleton-container) -- [Solr](https://github.com/testcontainers/testcontainers-java/tree/master/examples/solr-container) -- [Spring Boot](https://github.com/testcontainers/testcontainers-java/tree/master/examples/spring-boot) -- [Spring Boot with Kotlin](https://github.com/testcontainers/testcontainers-java/tree/master/examples/spring-boot-kotlin-redis) -- [TestNG](https://github.com/testcontainers/testcontainers-java/tree/master/examples/redis-backed-cache-testng) +- [Kafka Cluster with multiple brokers](https://github.com/testcontainers/testcontainers-java/tree/main/examples/kafka-cluster) +- [Linked containers](https://github.com/testcontainers/testcontainers-java/tree/main/examples/linked-container) +- [Neo4j](https://github.com/testcontainers/testcontainers-java/tree/main/examples/neo4j-container) +- [Redis](https://github.com/testcontainers/testcontainers-java/tree/main/examples/redis-backed-cache) +- [Selenium](https://github.com/testcontainers/testcontainers-java/tree/main/examples/selenium-container) +- [Selenium Module with Cucumber](https://github.com/testcontainers/testcontainers-java/tree/main/examples/cucumber) +- [Singleton Container Pattern](https://github.com/testcontainers/testcontainers-java/tree/main/examples/singleton-container) +- [Solr](https://github.com/testcontainers/testcontainers-java/tree/main/examples/solr-container) +- [Spring Boot](https://github.com/testcontainers/testcontainers-java/tree/main/examples/spring-boot) +- [Spring Boot with Kotlin](https://github.com/testcontainers/testcontainers-java/tree/main/examples/spring-boot-kotlin-redis) +- [TestNG](https://github.com/testcontainers/testcontainers-java/tree/main/examples/redis-backed-cache-testng) diff --git a/docs/index.md b/docs/index.md index 8eb4ddeb2c4..383f42c0245 100644 --- a/docs/index.md +++ b/docs/index.md @@ -206,7 +206,7 @@ A huge thank you to our sponsors: ## License -See [LICENSE](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/LICENSE). +See [LICENSE](https://raw.githubusercontent.com/testcontainers/testcontainers-java/main/LICENSE). ## Attributions @@ -218,4 +218,4 @@ This project was initially inspired by a [gist](https://gist.github.com/mosheesh Copyright (c) 2015-2021 Richard North and other authors. -See [AUTHORS](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/AUTHORS) for contributors. +See [AUTHORS](https://raw.githubusercontent.com/testcontainers/testcontainers-java/main/AUTHORS) for contributors. diff --git a/docs/jitpack_dependencies.md b/docs/jitpack_dependencies.md index 23a4c7353ad..30687dcd3bc 100644 --- a/docs/jitpack_dependencies.md +++ b/docs/jitpack_dependencies.md @@ -5,14 +5,14 @@ Use the following dependency description instead: === "Gradle" ```groovy - testImplementation "com.github.testcontainers.testcontainers-java:--artifact name--:master-SNAPSHOT" + testImplementation "com.github.testcontainers.testcontainers-java:--artifact name--:main-SNAPSHOT" ``` === "Maven" ```xml com.github.testcontainers.testcontainers-java --artifact name-- - master-SNAPSHOT + main-SNAPSHOT ``` diff --git a/docs/modules/consul.md b/docs/modules/consul.md index 1580690cad7..d0eafd6b1a8 100644 --- a/docs/modules/consul.md +++ b/docs/modules/consul.md @@ -32,5 +32,5 @@ testImplementation "org.testcontainers:consul:{{latest_version}}" ``` -See [AUTHORS](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/modules/consul/AUTHORS) for contributors. +See [AUTHORS](https://raw.githubusercontent.com/testcontainers/testcontainers-java/main/modules/consul/AUTHORS) for contributors. diff --git a/docs/modules/databases/jdbc.md b/docs/modules/databases/jdbc.md index e3a077333cd..9e976d872ba 100644 --- a/docs/modules/databases/jdbc.md +++ b/docs/modules/databases/jdbc.md @@ -127,5 +127,5 @@ Note that if you use `@Rule`, you will be given an isolated container for each t Examples/Tests: - * [MySQL](https://github.com/testcontainers/testcontainers-java/blob/master/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java) - * [PostgreSQL](https://github.com/testcontainers/testcontainers-java/blob/master/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java) + * [MySQL](https://github.com/testcontainers/testcontainers-java/blob/main/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java) + * [PostgreSQL](https://github.com/testcontainers/testcontainers-java/blob/main/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java) diff --git a/docs/modules/databases/mssqlserver.md b/docs/modules/databases/mssqlserver.md index 7ddd5350a40..9d0912f2563 100644 --- a/docs/modules/databases/mssqlserver.md +++ b/docs/modules/databases/mssqlserver.md @@ -49,10 +49,10 @@ Add the following dependency to your `pom.xml`/`build.gradle` file: ## License -See [LICENSE](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/modules/mssqlserver/LICENSE). +See [LICENSE](https://raw.githubusercontent.com/testcontainers/testcontainers-java/main/modules/mssqlserver/LICENSE). ## Copyright Copyright (c) 2017 - 2019 G DATA Software AG and other authors. -See [AUTHORS](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/modules/mssqlserver/AUTHORS) for contributors. +See [AUTHORS](https://raw.githubusercontent.com/testcontainers/testcontainers-java/main/modules/mssqlserver/AUTHORS) for contributors. diff --git a/docs/modules/gcloud.md b/docs/modules/gcloud.md index 7431ed0de0f..807b81de433 100644 --- a/docs/modules/gcloud.md +++ b/docs/modules/gcloud.md @@ -53,7 +53,7 @@ And test against the Emulator: See more examples: - * [Full sample code](https://github.com/testcontainers/testcontainers-java/tree/master/modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java) + * [Full sample code](https://github.com/testcontainers/testcontainers-java/tree/main/modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java) * [With Spring Boot](https://github.com/saturnism/testcontainers-gcloud-examples/tree/main/springboot/datastore-example/src/test/java/com/example/springboot/datastore) ### Firestore @@ -72,7 +72,7 @@ And test against the Emulator: See more examples: - * [Full sample code](https://github.com/testcontainers/testcontainers-java/tree/master/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java) + * [Full sample code](https://github.com/testcontainers/testcontainers-java/tree/main/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java) * [With Spring Boot](https://github.com/saturnism/testcontainers-gcloud-examples/tree/main/springboot/firestore-example/src/test/java/com/example/springboot/firestore/FirestoreIntegrationTests.java) ### Spanner @@ -103,7 +103,7 @@ And test against the Emulator: See more examples: - * [Full sample code](https://github.com/testcontainers/testcontainers-java/tree/master/modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java) + * [Full sample code](https://github.com/testcontainers/testcontainers-java/tree/main/modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java) * [With Spring Boot](https://github.com/saturnism/testcontainers-gcloud-examples/tree/main/springboot/spanner-example/src/test/java/com/example/springboot/spanner/SpannerIntegrationTests.java) ### Pub/Sub @@ -134,7 +134,7 @@ And test against the Emulator: See more examples: - * [Full sample code](https://github.com/testcontainers/testcontainers-java/tree/master/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java) + * [Full sample code](https://github.com/testcontainers/testcontainers-java/tree/main/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java) * [With Spring Boot](https://github.com/saturnism/testcontainers-gcloud-examples/tree/main/springboot/pubsub-example/src/test/java/com/example/springboot/pubsub/PubSubIntegrationTests.java) ## Adding this module to your project dependencies diff --git a/docs/modules/vault.md b/docs/modules/vault.md index 577ed91a32f..4a232361464 100644 --- a/docs/modules/vault.md +++ b/docs/modules/vault.md @@ -57,11 +57,11 @@ Add the following dependency to your `pom.xml`/`build.gradle` file: ## License -See [LICENSE](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/modules/vault/LICENSE). +See [LICENSE](https://raw.githubusercontent.com/testcontainers/testcontainers-java/main/modules/vault/LICENSE). ## Copyright Copyright (c) 2017 Capital One Services, LLC and other authors. -See [AUTHORS](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/modules/vault/AUTHORS) for contributors. +See [AUTHORS](https://raw.githubusercontent.com/testcontainers/testcontainers-java/main/modules/vault/AUTHORS) for contributors. diff --git a/docs/modules/webdriver_containers.md b/docs/modules/webdriver_containers.md index ec1dc2a407f..7ff4d2d6304 100644 --- a/docs/modules/webdriver_containers.md +++ b/docs/modules/webdriver_containers.md @@ -81,7 +81,7 @@ Note the factory must implement `org.testcontainers.containers.RecordingFileFact ## More examples -A few different examples are shown in [ChromeWebDriverContainerTest.java](https://github.com/testcontainers/testcontainers-java/blob/master/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java). +A few different examples are shown in [ChromeWebDriverContainerTest.java](https://github.com/testcontainers/testcontainers-java/blob/main/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java). ## Adding this module to your project dependencies From 14687a6cd23d31df43e87216f8d558bb9eb4943d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Fri, 30 Sep 2022 09:23:29 -0500 Subject: [PATCH 2908/5615] Reference to GitHub Discussions (#5928) Take advantage of discussions to have previous conversations with contributors before investing time working on PRs or asking general questions. Co-authored-by: Kevin Wittek --- .github/ISSUE_TEMPLATE/bug_report.yaml | 2 +- .github/ISSUE_TEMPLATE/enhancement.yaml | 2 +- .github/ISSUE_TEMPLATE/feature.yaml | 2 +- docs/contributing.md | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 9945297ea7a..8196451e88f 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -6,7 +6,7 @@ body: - type: markdown attributes: value: | - Thanks for taking the time to fill out this bug report! Before submitting a `bug`, please make sure there is no existing issue for the one you encountered. + Thanks for taking the time to fill out this bug report! Before submitting a `bug`, please make sure there is no existing issue for the one you encountered and it has been discussed with the team via [discussions](https://github.com/testcontainers/testcontainers-java/discussions) or Slack. - type: dropdown id: module attributes: diff --git a/.github/ISSUE_TEMPLATE/enhancement.yaml b/.github/ISSUE_TEMPLATE/enhancement.yaml index 6d5edab909f..9d65e1f12b8 100644 --- a/.github/ISSUE_TEMPLATE/enhancement.yaml +++ b/.github/ISSUE_TEMPLATE/enhancement.yaml @@ -6,7 +6,7 @@ body: - type: markdown attributes: value: | - Before submitting an `enhancement`, please make sure there is no existing enhancement for the one you are requesting. If so, please provide the following information: + Before submitting an `enhancement`, please make sure there is no existing enhancement for the one you are requesting and it has been discussed with the team via [discussions](https://github.com/testcontainers/testcontainers-java/discussions) or Slack. If so, please provide the following information: - type: dropdown id: module attributes: diff --git a/.github/ISSUE_TEMPLATE/feature.yaml b/.github/ISSUE_TEMPLATE/feature.yaml index 8abeb7d2131..581870d976d 100644 --- a/.github/ISSUE_TEMPLATE/feature.yaml +++ b/.github/ISSUE_TEMPLATE/feature.yaml @@ -6,7 +6,7 @@ body: - type: markdown attributes: value: | - Before submitting a `feature`, please make sure there is no existing feature for the one you are requesting. If so, please provide the following information: + Before submitting a `feature`, please make sure there is no existing feature for the one you are requesting and it has been discussed with the team via [discussions](https://github.com/testcontainers/testcontainers-java/discussions) or Slack. If so, please provide the following information: - type: dropdown id: module attributes: diff --git a/docs/contributing.md b/docs/contributing.md index 1457839a5ec..c8eac36e03b 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -2,9 +2,9 @@ * Star the project on [Github](https://github.com/testcontainers/testcontainers-java) and help spread the word :) * Join our [Slack group](http://slack.testcontainers.org) -* [Post an issue](https://github.com/testcontainers/testcontainers-java/issues) if you find any bugs +* [Start a discussion](https://github.com/testcontainers/testcontainers-java/discussions) if you have an idea, find a possible bug or have a general question. * Contribute improvements or fixes using a [Pull Request](https://github.com/testcontainers/testcontainers-java/pulls). If you're going to contribute, thank you! Please just be sure to: - * discuss with the authors on an issue ticket prior to doing anything big. + * discuss with the authors prior to doing anything big. * follow the style, naming and structure conventions of the rest of the project. * make commits atomic and easy to merge. * when updating documentation, please see [our guidance for documentation contributions](contributing_docs.md). From f0dd61404fd570b91e71513317bde6a5a2599dfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Fri, 30 Sep 2022 11:59:54 -0500 Subject: [PATCH 2909/5615] Add banner when Ryuk is disabled (#5929) This banner will warn the user about unexpected behavior if Ryuk is disabled. Co-authored-by: Kevin Wittek --- .../java/org/testcontainers/utility/ResourceReaper.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index ad0c949f6e7..1b99cd2abb4 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -79,6 +79,15 @@ public static synchronized ResourceReaper instance() { //noinspection deprecation instance = new RyukResourceReaper(); } else { + String ryukDisabledMessage = + "\n" + + "********************************************************************************" + + "\n" + + "Ryuk has been disabled. This can cause unexpected behavior in your environment." + + "\n" + + "********************************************************************************"; + LOGGER.warn(ryukDisabledMessage); + instance = new JVMHookResourceReaper(); } } From e2aeb68a45d2a3579e9a5667977789c24463478f Mon Sep 17 00:00:00 2001 From: Hari Ohm Prasath Date: Fri, 30 Sep 2022 10:14:00 -0700 Subject: [PATCH 2910/5615] Test container example for ImmuDB (#5860) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test container example using ImmuDB, performing basic operations Fixes #5594 Co-authored-by: Eddú Meléndez Gonzales --- docs/examples.md | 23 +++---- examples/immudb/build.gradle | 14 +++++ examples/immudb/src/test/java/ImmuDbTest.java | 63 +++++++++++++++++++ examples/settings.gradle | 1 + 4 files changed, 90 insertions(+), 11 deletions(-) create mode 100644 examples/immudb/build.gradle create mode 100644 examples/immudb/src/test/java/ImmuDbTest.java diff --git a/docs/examples.md b/docs/examples.md index 14f43d21e23..cd85226e4be 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -2,14 +2,15 @@ Examples of different use cases provided by Testcontainers can be found below: -- [Kafka Cluster with multiple brokers](https://github.com/testcontainers/testcontainers-java/tree/main/examples/kafka-cluster) -- [Linked containers](https://github.com/testcontainers/testcontainers-java/tree/main/examples/linked-container) -- [Neo4j](https://github.com/testcontainers/testcontainers-java/tree/main/examples/neo4j-container) -- [Redis](https://github.com/testcontainers/testcontainers-java/tree/main/examples/redis-backed-cache) -- [Selenium](https://github.com/testcontainers/testcontainers-java/tree/main/examples/selenium-container) -- [Selenium Module with Cucumber](https://github.com/testcontainers/testcontainers-java/tree/main/examples/cucumber) -- [Singleton Container Pattern](https://github.com/testcontainers/testcontainers-java/tree/main/examples/singleton-container) -- [Solr](https://github.com/testcontainers/testcontainers-java/tree/main/examples/solr-container) -- [Spring Boot](https://github.com/testcontainers/testcontainers-java/tree/main/examples/spring-boot) -- [Spring Boot with Kotlin](https://github.com/testcontainers/testcontainers-java/tree/main/examples/spring-boot-kotlin-redis) -- [TestNG](https://github.com/testcontainers/testcontainers-java/tree/main/examples/redis-backed-cache-testng) +- [Kafka Cluster with multiple brokers](https://github.com/testcontainers/testcontainers-java/tree/master/examples/kafka-cluster) +- [Linked containers](https://github.com/testcontainers/testcontainers-java/tree/master/examples/linked-container) +- [Neo4j](https://github.com/testcontainers/testcontainers-java/tree/master/examples/neo4j-container) +- [Redis](https://github.com/testcontainers/testcontainers-java/tree/master/examples/redis-backed-cache) +- [Selenium](https://github.com/testcontainers/testcontainers-java/tree/master/examples/selenium-container) +- [Selenium Module with Cucumber](https://github.com/testcontainers/testcontainers-java/tree/master/examples/cucumber) +- [Singleton Container Pattern](https://github.com/testcontainers/testcontainers-java/tree/master/examples/singleton-container) +- [Solr](https://github.com/testcontainers/testcontainers-java/tree/master/examples/solr-container) +- [Spring Boot](https://github.com/testcontainers/testcontainers-java/tree/master/examples/spring-boot) +- [Spring Boot with Kotlin](https://github.com/testcontainers/testcontainers-java/tree/master/examples/spring-boot-kotlin-redis) +- [TestNG](https://github.com/testcontainers/testcontainers-java/tree/master/examples/redis-backed-cache-testng) +- [ImmuDb](https://github.com/testcontainers/testcontainers-java/tree/master/examples/immudb) diff --git a/examples/immudb/build.gradle b/examples/immudb/build.gradle new file mode 100644 index 00000000000..5ff55a2205a --- /dev/null +++ b/examples/immudb/build.gradle @@ -0,0 +1,14 @@ +plugins { + id 'java' +} + +repositories { + mavenCentral() +} + +dependencies { + compileOnly 'org.slf4j:slf4j-api:2.0.0' + implementation 'io.codenotary:immudb4j:0.9.10.2' + testImplementation 'org.testcontainers:testcontainers' + testImplementation 'org.assertj:assertj-core:3.23.1' +} diff --git a/examples/immudb/src/test/java/ImmuDbTest.java b/examples/immudb/src/test/java/ImmuDbTest.java new file mode 100644 index 00000000000..fd515ddd4aa --- /dev/null +++ b/examples/immudb/src/test/java/ImmuDbTest.java @@ -0,0 +1,63 @@ +import io.codenotary.immudb4j.Entry; +import io.codenotary.immudb4j.ImmuClient; +import io.codenotary.immudb4j.exceptions.CorruptedDataException; +import io.codenotary.immudb4j.exceptions.VerificationException; +import org.junit.Assert; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test class for the ImmuDbClient. + */ +public class ImmuDbTest { + + // Default port for the ImmuDb server + private static final int IMMUDB_PORT = 3322; + // Default username for the ImmuDb server + private final String IMMUDB_USER = "immudb"; + // Default password for the ImmuDb server + private final String IMMUDB_PASSWORD = "immudb"; + // Default database name for the ImmuDb server + private final String IMMUDB_DATABASE = "defaultdb"; + + // Test container for the ImmuDb database, with the latest version of the image and exposed port + @ClassRule + public static final GenericContainer immuDbContainer = new GenericContainer<>("codenotary/immudb:1.3") + .withExposedPorts(IMMUDB_PORT).waitingFor( + Wait.forLogMessage(".*Web API server enabled.*", 1) + ); + // ImmuClient used to interact with the DB + private ImmuClient immuClient; + + @Before + public void setUp() { + this.immuClient = ImmuClient.newBuilder() + .withServerUrl(immuDbContainer.getHost()) + .withServerPort(immuDbContainer.getMappedPort(IMMUDB_PORT)) + .build(); + this.immuClient.login(IMMUDB_USER, IMMUDB_PASSWORD); + this.immuClient.useDatabase(IMMUDB_DATABASE); + } + + @Test + public void testGetValue() { + try { + immuClient.set("test1", "test2".getBytes()); + + Entry entry = immuClient.verifiedGet("test1"); + + if (entry != null) { + byte[] value = entry.getValue(); + assertThat(new String(value)).isEqualTo("test2"); + } else { + Assert.fail(); + } + } catch (CorruptedDataException | VerificationException e) { + Assert.fail(); + } + } +} diff --git a/examples/settings.gradle b/examples/settings.gradle index 7194134350d..2a508013928 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -30,6 +30,7 @@ include 'solr-container' include 'spring-boot' include 'cucumber' include 'spring-boot-kotlin-redis' +include 'immudb' ext.isCI = System.getenv("CI") != null From a71b5310185ee69634557557761dfa1ec73a0bee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Sep 2022 17:15:08 +0000 Subject: [PATCH 2911/5615] Bump slf4j-api from 2.0.0 to 2.0.3 in /core Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 2.0.0 to 2.0.3. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.0...v_2.0.3) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 4072c25db37..e1e38eaeb18 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -61,7 +61,7 @@ configurations.all { dependencies { api 'junit:junit:4.13.2' - api 'org.slf4j:slf4j-api:2.0.0' + api 'org.slf4j:slf4j-api:2.0.3' compileOnly 'org.jetbrains:annotations:23.0.0' testCompileOnly 'org.jetbrains:annotations:23.0.0' api 'org.apache.commons:commons-compress:1.21' From a3d94ebf032eeaca9ed3bb5547de48fa405bec19 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 01:03:24 +0000 Subject: [PATCH 2912/5615] Bump s3 from 2.17.266 to 2.17.285 in /modules/localstack Bumps s3 from 2.17.266 to 2.17.285. --- updated-dependencies: - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 9759d193475..5ef16b2ca58 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.313' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.295' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.313' - testImplementation 'software.amazon.awssdk:s3:2.17.266' + testImplementation 'software.amazon.awssdk:s3:2.17.285' testImplementation 'org.assertj:assertj-core:3.23.1' } From 88f23b5fb5bbd25941617b37639f3c2cf852bb27 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 01:03:27 +0000 Subject: [PATCH 2913/5615] Bump aws-java-sdk-sqs from 1.12.295 to 1.12.314 in /modules/localstack Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.12.295 to 1.12.314. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.295...1.12.314) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-sqs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 9759d193475..2666932fbbc 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.313' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.313' - testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.295' + testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.314' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.313' testImplementation 'software.amazon.awssdk:s3:2.17.266' testImplementation 'org.assertj:assertj-core:3.23.1' From 3802c2934ded989bb5a433e5d48a5d036a9042f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 01:03:37 +0000 Subject: [PATCH 2914/5615] Bump google-cloud-firestore from 3.4.2 to 3.5.0 in /modules/gcloud Bumps [google-cloud-firestore](https://github.com/googleapis/java-firestore) from 3.4.2 to 3.5.0. - [Release notes](https://github.com/googleapis/java-firestore/releases) - [Changelog](https://github.com/googleapis/java-firestore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-firestore/compare/v3.4.2...v3.5.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-firestore dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 30c11325653..b52e6ea0dd0 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':testcontainers') testImplementation 'com.google.cloud:google-cloud-datastore:2.11.4' - testImplementation 'com.google.cloud:google-cloud-firestore:3.4.2' + testImplementation 'com.google.cloud:google-cloud-firestore:3.5.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.18' testImplementation 'com.google.cloud:google-cloud-spanner:6.29.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.11.1' From a2b1e15f1b008d100fe8b5761a59153a335627ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 01:03:38 +0000 Subject: [PATCH 2915/5615] Bump kafka-clients from 3.2.1 to 3.3.0 in /modules/kafka Bumps kafka-clients from 3.2.1 to 3.3.0. --- updated-dependencies: - dependency-name: org.apache.kafka:kafka-clients dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index ebd9c84120c..70907510afe 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Kafka" dependencies { api project(':testcontainers') - testImplementation 'org.apache.kafka:kafka-clients:3.2.1' + testImplementation 'org.apache.kafka:kafka-clients:3.3.0' testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'com.google.guava:guava:23.0' } From d5fe8813f043eda38a5e257f3066bd3520e91314 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 01:03:39 +0000 Subject: [PATCH 2916/5615] Bump cucumber-java from 7.6.0 to 7.8.0 in /examples Bumps [cucumber-java](https://github.com/cucumber/cucumber-jvm) from 7.6.0 to 7.8.0. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.6.0...v7.8.0) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-java dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 9d0da7a5e33..1b9fe04efd8 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' - testImplementation 'io.cucumber:cucumber-java:7.6.0' + testImplementation 'io.cucumber:cucumber-java:7.8.0' testImplementation 'io.cucumber:cucumber-junit:7.8.0' testImplementation 'org.testcontainers:selenium' } From 499f2ce17917e4ac181d08c748fab929014c7912 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 01:03:45 +0000 Subject: [PATCH 2917/5615] Bump google-cloud-spanner from 6.29.0 to 6.31.0 in /modules/gcloud Bumps [google-cloud-spanner](https://github.com/googleapis/java-spanner) from 6.29.0 to 6.31.0. - [Release notes](https://github.com/googleapis/java-spanner/releases) - [Changelog](https://github.com/googleapis/java-spanner/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-spanner/compare/v6.29.0...v6.31.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-spanner dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 30c11325653..8be3aab89a5 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -6,7 +6,7 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-datastore:2.11.4' testImplementation 'com.google.cloud:google-cloud-firestore:3.4.2' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.18' - testImplementation 'com.google.cloud:google-cloud-spanner:6.29.0' + testImplementation 'com.google.cloud:google-cloud-spanner:6.31.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.11.1' testImplementation 'org.assertj:assertj-core:3.23.1' } From 3aa35988d5e83628846e27f3197b568bde58c5ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 03:52:29 +0000 Subject: [PATCH 2918/5615] Bump google-cloud-bigtable from 2.11.1 to 2.13.0 in /modules/gcloud Bumps [google-cloud-bigtable](https://github.com/googleapis/java-bigtable) from 2.11.1 to 2.13.0. - [Release notes](https://github.com/googleapis/java-bigtable/releases) - [Changelog](https://github.com/googleapis/java-bigtable/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-bigtable/compare/v2.11.1...v2.13.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-bigtable dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 3258063f5dd..a5a72db0982 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-firestore:3.5.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.18' testImplementation 'com.google.cloud:google-cloud-spanner:6.31.0' - testImplementation 'com.google.cloud:google-cloud-bigtable:2.11.1' + testImplementation 'com.google.cloud:google-cloud-bigtable:2.13.0' testImplementation 'org.assertj:assertj-core:3.23.1' } From bd6abf933eb383b0d82a805ca49091fb6fdd2f24 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 22:00:34 +0000 Subject: [PATCH 2919/5615] Bump aws-java-sdk-s3 from 1.12.313 to 1.12.314 in /modules/localstack Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.313 to 1.12.314. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.313...1.12.314) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index b042ee575da..7d263e3e492 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.313' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.314' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.313' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.314' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.313' From 554ec64d4aea8aa6ca50fa0bc654ced8057ab195 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 22:00:37 +0000 Subject: [PATCH 2920/5615] Bump aws-java-sdk-dynamodb in /modules/dynalite Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.12.313 to 1.12.314. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.313...1.12.314) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-dynamodb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/dynalite/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index e9cd2111f82..18ec4a61ac6 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Dynalite" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.313' - testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.295' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.314' + testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.314' testImplementation 'org.assertj:assertj-core:3.23.1' } From f04648425bbc4a7fcf2d26d933e71e30a7886f18 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 22:00:39 +0000 Subject: [PATCH 2921/5615] Bump aws-java-sdk-logs from 1.12.313 to 1.12.314 in /modules/localstack Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.12.313 to 1.12.314. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.313...1.12.314) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-logs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index b042ee575da..d632c57cbfb 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,7 +6,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.313' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.313' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.314' - testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.313' + testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.314' testImplementation 'software.amazon.awssdk:s3:2.17.285' testImplementation 'org.assertj:assertj-core:3.23.1' } From a3889d18f8cd33e40d73bf65b11fd44c8364bdee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 22:01:00 +0000 Subject: [PATCH 2922/5615] Bump actions/cache from 3.0.8 to 3.0.9 Bumps [actions/cache](https://github.com/actions/cache) from 3.0.8 to 3.0.9. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3.0.8...v3.0.9) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index 2cdc9f8ed69..527c2eccfeb 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -25,7 +25,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.8 + uses: actions/cache@v3.0.9 continue-on-error: true with: path: ~/.gradle/caches @@ -53,7 +53,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.8 + uses: actions/cache@v3.0.9 continue-on-error: true with: path: ~/.gradle/caches diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 80f20fb0c0c..f55a5355c4e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.8 + uses: actions/cache@v3.0.9 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-home-testmatrix-${{ hashFiles('**/*.gradle') }} @@ -51,7 +51,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.8 + uses: actions/cache@v3.0.9 continue-on-error: true with: path: ~/.gradle/caches From d4c8de272e7a9c358e9066baed82cab097164b42 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 22:01:07 +0000 Subject: [PATCH 2923/5615] Bump slf4j-api from 2.0.0 to 2.0.3 in /examples Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 2.0.0 to 2.0.3. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.0...v_2.0.3) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/immudb/build.gradle | 2 +- examples/linked-container/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/immudb/build.gradle b/examples/immudb/build.gradle index 5ff55a2205a..1d1940cb85b 100644 --- a/examples/immudb/build.gradle +++ b/examples/immudb/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:2.0.0' + compileOnly 'org.slf4j:slf4j-api:2.0.3' implementation 'io.codenotary:immudb4j:0.9.10.2' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.assertj:assertj-core:3.23.1' diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index fe230f3a22e..50e38518d39 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -6,7 +6,7 @@ repositories { mavenCentral() } dependencies { - compileOnly 'org.slf4j:slf4j-api:2.0.0' + compileOnly 'org.slf4j:slf4j-api:2.0.3' implementation 'com.squareup.okhttp3:okhttp:4.10.0' implementation 'org.json:json:20220924' testImplementation 'org.postgresql:postgresql:42.5.0' diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 799f3f89925..c19dd3a012f 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:2.0.0' + compileOnly 'org.slf4j:slf4j-api:2.0.3' implementation 'redis.clients:jedis:4.2.3' implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 1d840f11871..82197ddc9b5 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:2.0.0' + compileOnly 'org.slf4j:slf4j-api:2.0.3' implementation 'redis.clients:jedis:4.2.3' implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index d540a8386ab..9c56d53faa3 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -11,7 +11,7 @@ dependencies { implementation 'redis.clients:jedis:4.2.3' implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' - compileOnly 'org.slf4j:slf4j-api:2.0.0' + compileOnly 'org.slf4j:slf4j-api:2.0.3' testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.testcontainers:testcontainers' From 38e6cc18d77d4cc7b58da1842508142539955795 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 22:04:24 +0000 Subject: [PATCH 2924/5615] Bump azure-cosmos from 4.36.0 to 4.37.0 in /modules/azure Bumps [azure-cosmos](https://github.com/Azure/azure-sdk-for-java) from 4.36.0 to 4.37.0. - [Release notes](https://github.com/Azure/azure-sdk-for-java/releases) - [Commits](https://github.com/Azure/azure-sdk-for-java/compare/azure-cosmos_4.36.0...azure-cosmos_4.37.0) --- updated-dependencies: - dependency-name: com.azure:azure-cosmos dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/azure/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle index 43280db4a88..78c6500d856 100644 --- a/modules/azure/build.gradle +++ b/modules/azure/build.gradle @@ -6,5 +6,5 @@ dependencies { shaded 'com.squareup.okhttp3:okhttp:4.10.0' testImplementation 'org.assertj:assertj-core:3.23.1' - testImplementation 'com.azure:azure-cosmos:4.36.0' + testImplementation 'com.azure:azure-cosmos:4.37.0' } From 2798d3403b9099b9bc4253f71ed2f4840a966e75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Mon, 3 Oct 2022 09:23:22 -0500 Subject: [PATCH 2925/5615] Bump kotlin plugins version to 1.7.20 (#5945) --- examples/spring-boot-kotlin-redis/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 6a98573172c..c0ec19f203c 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,7 +1,7 @@ plugins { id("org.springframework.boot") version "2.7.4" - id("org.jetbrains.kotlin.jvm") version "1.7.10" - id("org.jetbrains.kotlin.plugin.spring") version "1.7.10" + id("org.jetbrains.kotlin.jvm") version "1.7.20" + id("org.jetbrains.kotlin.plugin.spring") version "1.7.20" } apply plugin: 'io.spring.dependency-management' From 318bfc6833482c39f663cdbb0cceaecf46b87202 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Oct 2022 14:31:09 +0000 Subject: [PATCH 2926/5615] Bump javassist from 3.29.0-GA to 3.29.2-GA in /modules/hivemq Bumps [javassist](https://github.com/jboss-javassist/javassist) from 3.29.0-GA to 3.29.2-GA. - [Release notes](https://github.com/jboss-javassist/javassist/releases) - [Changelog](https://github.com/jboss-javassist/javassist/blob/master/Changes.md) - [Commits](https://github.com/jboss-javassist/javassist/commits) --- updated-dependencies: - dependency-name: org.javassist:javassist dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index a12b3866e4d..1da5ce69645 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -6,7 +6,7 @@ dependencies { shaded("org.apache.commons:commons-lang3:3.12.0") shaded("commons-io:commons-io:2.11.0") - shaded("org.javassist:javassist:3.29.0-GA") + shaded("org.javassist:javassist:3.29.2-GA") shaded("org.jboss.shrinkwrap:shrinkwrap-api:1.2.6") shaded("org.jboss.shrinkwrap:shrinkwrap-impl-base:1.2.6") shaded("net.lingala.zip4j:zip4j:2.11.2") From 03de1ce271fab9f4499097a9ed81cdee5e575450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Mon, 3 Oct 2022 12:44:00 -0500 Subject: [PATCH 2927/5615] Fix example docs links --- docs/examples.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/examples.md b/docs/examples.md index cd85226e4be..e7578c8b375 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -2,15 +2,15 @@ Examples of different use cases provided by Testcontainers can be found below: -- [Kafka Cluster with multiple brokers](https://github.com/testcontainers/testcontainers-java/tree/master/examples/kafka-cluster) -- [Linked containers](https://github.com/testcontainers/testcontainers-java/tree/master/examples/linked-container) -- [Neo4j](https://github.com/testcontainers/testcontainers-java/tree/master/examples/neo4j-container) -- [Redis](https://github.com/testcontainers/testcontainers-java/tree/master/examples/redis-backed-cache) -- [Selenium](https://github.com/testcontainers/testcontainers-java/tree/master/examples/selenium-container) -- [Selenium Module with Cucumber](https://github.com/testcontainers/testcontainers-java/tree/master/examples/cucumber) -- [Singleton Container Pattern](https://github.com/testcontainers/testcontainers-java/tree/master/examples/singleton-container) -- [Solr](https://github.com/testcontainers/testcontainers-java/tree/master/examples/solr-container) -- [Spring Boot](https://github.com/testcontainers/testcontainers-java/tree/master/examples/spring-boot) -- [Spring Boot with Kotlin](https://github.com/testcontainers/testcontainers-java/tree/master/examples/spring-boot-kotlin-redis) -- [TestNG](https://github.com/testcontainers/testcontainers-java/tree/master/examples/redis-backed-cache-testng) -- [ImmuDb](https://github.com/testcontainers/testcontainers-java/tree/master/examples/immudb) +- [Kafka Cluster with multiple brokers](https://github.com/testcontainers/testcontainers-java/tree/main/examples/kafka-cluster) +- [Linked containers](https://github.com/testcontainers/testcontainers-java/tree/main/examples/linked-container) +- [Neo4j](https://github.com/testcontainers/testcontainers-java/tree/main/examples/neo4j-container) +- [Redis](https://github.com/testcontainers/testcontainers-java/tree/main/examples/redis-backed-cache) +- [Selenium](https://github.com/testcontainers/testcontainers-java/tree/main/examples/selenium-container) +- [Selenium Module with Cucumber](https://github.com/testcontainers/testcontainers-java/tree/main/examples/cucumber) +- [Singleton Container Pattern](https://github.com/testcontainers/testcontainers-java/tree/main/examples/singleton-container) +- [Solr](https://github.com/testcontainers/testcontainers-java/tree/main/examples/solr-container) +- [Spring Boot](https://github.com/testcontainers/testcontainers-java/tree/main/examples/spring-boot) +- [Spring Boot with Kotlin](https://github.com/testcontainers/testcontainers-java/tree/main/examples/spring-boot-kotlin-redis) +- [TestNG](https://github.com/testcontainers/testcontainers-java/tree/main/examples/redis-backed-cache-testng) +- [ImmuDb](https://github.com/testcontainers/testcontainers-java/tree/main/examples/immudb) From a8ec3e6da1c236b25bfba8d071c27ebbe57e938a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Mon, 3 Oct 2022 12:47:25 -0500 Subject: [PATCH 2928/5615] Fix main branch name in ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f55a5355c4e..b406f256ae2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: CI on: pull_request: {} - push: { branches: [ maing ] } + push: { branches: [ main ] } permissions: contents: read From c2a741d3b77531fe196025b559d0ad2fb655993e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Mon, 3 Oct 2022 13:35:08 -0500 Subject: [PATCH 2929/5615] Bump logback-classic versions (#5948) `ch.qos.logback:logback-classic` 1.3.x is compatible with slf4j 2.x. Currently, logs are not displayed. --- build.gradle | 2 +- docs/examples/spock/redis/build.gradle | 2 +- examples/kafka-cluster/build.gradle | 2 +- examples/linked-container/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- modules/hivemq/build.gradle | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index a53bd19298d..9e6aa6020e4 100644 --- a/build.gradle +++ b/build.gradle @@ -125,7 +125,7 @@ subprojects { } dependencies { - testImplementation 'ch.qos.logback:logback-classic:1.2.3' + testImplementation 'ch.qos.logback:logback-classic:1.3.3' } spotless { diff --git a/docs/examples/spock/redis/build.gradle b/docs/examples/spock/redis/build.gradle index 1bff637a7fb..6699788d026 100644 --- a/docs/examples/spock/redis/build.gradle +++ b/docs/examples/spock/redis/build.gradle @@ -7,5 +7,5 @@ dependencies { api "io.lettuce:lettuce-core:5.2.0.RELEASE" testImplementation 'org.spockframework:spock-core:1.2-groovy-2.5' testImplementation project(":spock") - testImplementation 'ch.qos.logback:logback-classic:1.2.3' + testImplementation 'ch.qos.logback:logback-classic:1.3.3' } diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index 08d3734a956..5212bc33fa9 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -13,5 +13,5 @@ dependencies { testImplementation 'org.apache.kafka:kafka-clients:3.3.0' testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'com.google.guava:guava:23.0' - testImplementation 'ch.qos.logback:logback-classic:1.2.11' + testImplementation 'ch.qos.logback:logback-classic:1.3.3' } diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 50e38518d39..297bcb5143f 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'com.squareup.okhttp3:okhttp:4.10.0' implementation 'org.json:json:20220924' testImplementation 'org.postgresql:postgresql:42.5.0' - testImplementation 'ch.qos.logback:logback-classic:1.2.11' + testImplementation 'ch.qos.logback:logback-classic:1.3.3' testImplementation 'org.testcontainers:postgresql' testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index c19dd3a012f..8e3394723ba 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -12,7 +12,7 @@ dependencies { implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' - testImplementation 'ch.qos.logback:logback-classic:1.2.11' + testImplementation 'ch.qos.logback:logback-classic:1.3.3' testImplementation 'org.testng:testng:7.5' testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 82197ddc9b5..ed794088790 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -13,6 +13,6 @@ dependencies { implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'junit:junit:4.13.2' - testImplementation 'ch.qos.logback:logback-classic:1.2.11' + testImplementation 'ch.qos.logback:logback-classic:1.3.3' testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 9c56d53faa3..3ef81b866d4 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -13,7 +13,7 @@ dependencies { implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:2.0.3' - testImplementation 'ch.qos.logback:logback-classic:1.2.11' + testImplementation 'ch.qos.logback:logback-classic:1.3.3' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index a12b3866e4d..3d6b653f6d1 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -16,7 +16,7 @@ dependencies { testImplementation("com.hivemq:hivemq-extension-sdk:4.9.0") testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") testImplementation("org.apache.httpcomponents:httpclient:4.5.13") - testImplementation("ch.qos.logback:logback-classic:1.4.1") + testImplementation("ch.qos.logback:logback-classic:1.4.3") testImplementation 'org.assertj:assertj-core:3.23.1' testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.1") } From abf87abcae037e4ddf124c888c21e2a8a65180a8 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Tue, 4 Oct 2022 16:23:50 +0200 Subject: [PATCH 2930/5615] Rollback back to slf4j-api 1.7.36 (#5951) Reverts #5794, #5942, #5875, #5848, #5794, #5791 --- core/build.gradle | 2 +- examples/immudb/build.gradle | 2 +- examples/linked-container/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- test-support/build.gradle | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index c20ebc1d9b0..2967b35d4ff 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -61,7 +61,7 @@ configurations.all { dependencies { api 'junit:junit:4.13.2' - api 'org.slf4j:slf4j-api:2.0.3' + api 'org.slf4j:slf4j-api:1.7.36' compileOnly 'org.jetbrains:annotations:23.0.0' testCompileOnly 'org.jetbrains:annotations:23.0.0' api 'org.apache.commons:commons-compress:1.21' diff --git a/examples/immudb/build.gradle b/examples/immudb/build.gradle index 1d1940cb85b..b2e6fcdba43 100644 --- a/examples/immudb/build.gradle +++ b/examples/immudb/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:2.0.3' + compileOnly 'org.slf4j:slf4j-api:1.7.36' implementation 'io.codenotary:immudb4j:0.9.10.2' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.assertj:assertj-core:3.23.1' diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 297bcb5143f..cf3a3331cc3 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -6,7 +6,7 @@ repositories { mavenCentral() } dependencies { - compileOnly 'org.slf4j:slf4j-api:2.0.3' + compileOnly 'org.slf4j:slf4j-api:1.7.36' implementation 'com.squareup.okhttp3:okhttp:4.10.0' implementation 'org.json:json:20220924' testImplementation 'org.postgresql:postgresql:42.5.0' diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 8e3394723ba..2e4bda4fa74 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:2.0.3' + compileOnly 'org.slf4j:slf4j-api:1.7.36' implementation 'redis.clients:jedis:4.2.3' implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index ed794088790..eb684158731 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:2.0.3' + compileOnly 'org.slf4j:slf4j-api:1.7.36' implementation 'redis.clients:jedis:4.2.3' implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 3ef81b866d4..449fd9ae8e6 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -11,7 +11,7 @@ dependencies { implementation 'redis.clients:jedis:4.2.3' implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' - compileOnly 'org.slf4j:slf4j-api:2.0.3' + compileOnly 'org.slf4j:slf4j-api:1.7.36' testImplementation 'ch.qos.logback:logback-classic:1.3.3' testImplementation 'org.testcontainers:testcontainers' diff --git a/test-support/build.gradle b/test-support/build.gradle index 2fae2344a89..8247f97dbf4 100644 --- a/test-support/build.gradle +++ b/test-support/build.gradle @@ -1,5 +1,5 @@ dependencies { implementation 'junit:junit:4.13.2' - implementation 'org.slf4j:slf4j-api:2.0.0' + implementation 'org.slf4j:slf4j-api:1.7.36' testImplementation 'org.assertj:assertj-core:3.23.1' } From 320a6203227f93ed138ea905105fa422346c80fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Tue, 4 Oct 2022 11:14:25 -0500 Subject: [PATCH 2931/5615] Ignore dependencies in specific modules (#5946) Some dependencies require Java 11, 17 or have a breaking change such as r2dbc. --- .github/dependabot.yml | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 4c270ef762c..87ce5c86463 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -11,6 +11,9 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "org.slf4j:slf4j-api" + update-types: [ "version-update:semver-major" ] - package-ecosystem: "gradle" directory: "/" allow: @@ -32,6 +35,9 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "io.dropwizard.metrics:metrics-core" + update-types: [ "version-update:semver-major" ] - package-ecosystem: "gradle" directory: "/modules/clickhouse" schedule: @@ -97,6 +103,9 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "org.apache.tomcat:tomcat-jdbc" + update-types: [ "version-update:semver-minor" ] - package-ecosystem: "gradle" directory: "/modules/junit-jupiter" schedule: @@ -107,6 +116,9 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml" + update-types: [ "version-update:semver-minor" ] - package-ecosystem: "gradle" directory: "/modules/kafka" schedule: @@ -122,6 +134,9 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "org.mariadb:r2dbc-mariadb" + update-types: [ "version-update:semver-minor" ] - package-ecosystem: "gradle" directory: "/modules/mockserver" schedule: @@ -147,6 +162,9 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "org.neo4j.driver:neo4j-java-driver" + update-types: [ "version-update:semver-major" ] - package-ecosystem: "gradle" directory: "/modules/nginx" schedule: @@ -182,6 +200,9 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "io.r2dbc:r2dbc-spi" + versions: ["0.8.x"] - package-ecosystem: "gradle" directory: "/modules/rabbitmq" schedule: @@ -202,6 +223,9 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "org.apache.solr:solr-solrj" + update-types: [ "version-update:semver-major" ] - package-ecosystem: "gradle" directory: "/modules/spock" schedule: @@ -234,6 +258,17 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "ch.qos.logback:logback-classic" + update-types: [ "version-update:semver-minor" ] + - dependency-name: "org.apache.solr:solr-solrj" + update-types: [ "version-update:semver-major" ] + - dependency-name: "org.neo4j.driver:neo4j-java-driver" + update-types: [ "version-update:semver-major" ] + - dependency-name: "org.testng:testng" + update-types: [ "version-update:semver-minor" ] + - dependency-name: "org.slf4j:slf4j-api" + update-types: [ "version-update:semver-major" ] # GitHub Actions - package-ecosystem: "github-actions" From a6ceeba0b15d66e2f2b901ac56b43fc84e9815ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Oct 2022 16:14:55 +0000 Subject: [PATCH 2932/5615] Bump metrics-core from 3.2.4 to 3.2.6 in /modules/cassandra Bumps [metrics-core](https://github.com/dropwizard/metrics) from 3.2.4 to 3.2.6. - [Release notes](https://github.com/dropwizard/metrics/releases) - [Commits](https://github.com/dropwizard/metrics/compare/v3.2.4...v3.2.6) --- updated-dependencies: - dependency-name: io.dropwizard.metrics:metrics-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/cassandra/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cassandra/build.gradle b/modules/cassandra/build.gradle index ae115c92a71..7f9ab53c71b 100644 --- a/modules/cassandra/build.gradle +++ b/modules/cassandra/build.gradle @@ -2,7 +2,7 @@ description = "TestContainers :: Cassandra" configurations.all { resolutionStrategy { - force 'io.dropwizard.metrics:metrics-core:3.2.4' + force 'io.dropwizard.metrics:metrics-core:3.2.6' } } From 461c23d6c65b769f42b5d18077e9b38130dde0bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Oct 2022 16:14:56 +0000 Subject: [PATCH 2933/5615] Bump mongodb-driver-sync from 4.7.1 to 4.7.2 in /modules/mongodb Bumps [mongodb-driver-sync](https://github.com/mongodb/mongo-java-driver) from 4.7.1 to 4.7.2. - [Release notes](https://github.com/mongodb/mongo-java-driver/releases) - [Commits](https://github.com/mongodb/mongo-java-driver/compare/r4.7.1...r4.7.2) --- updated-dependencies: - dependency-name: org.mongodb:mongodb-driver-sync dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mongodb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mongodb/build.gradle b/modules/mongodb/build.gradle index 6e4b5208298..cc4a9288373 100644 --- a/modules/mongodb/build.gradle +++ b/modules/mongodb/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: MongoDB" dependencies { api project(':testcontainers') - testImplementation("org.mongodb:mongodb-driver-sync:4.7.1") + testImplementation("org.mongodb:mongodb-driver-sync:4.7.2") testImplementation 'org.assertj:assertj-core:3.23.1' } From c4658989068d3c555fdff9b398c490d300a9a5d3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Oct 2022 16:16:06 +0000 Subject: [PATCH 2934/5615] Bump solr-solrj from 8.11.1 to 8.11.2 in /modules/solr Bumps solr-solrj from 8.11.1 to 8.11.2. --- updated-dependencies: - dependency-name: org.apache.solr:solr-solrj dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/solr/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/solr/build.gradle b/modules/solr/build.gradle index 72ab2997dea..8d053fa4169 100644 --- a/modules/solr/build.gradle +++ b/modules/solr/build.gradle @@ -5,6 +5,6 @@ dependencies { // TODO use JDK's HTTP client and/or Apache HttpClient5 shaded 'com.squareup.okhttp3:okhttp:4.10.0' - testImplementation 'org.apache.solr:solr-solrj:8.11.1' + testImplementation 'org.apache.solr:solr-solrj:8.11.2' testImplementation 'org.assertj:assertj-core:3.23.1' } From c0758a2bf6c1f1ccf7154c9c337fc5aa1cdc9a52 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Oct 2022 16:16:08 +0000 Subject: [PATCH 2935/5615] Bump solr-solrj from 8.11.1 to 8.11.2 in /examples Bumps solr-solrj from 8.11.1 to 8.11.2. --- updated-dependencies: - dependency-name: org.apache.solr:solr-solrj dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/solr-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/solr-container/build.gradle b/examples/solr-container/build.gradle index 56fd297cf5e..dd0f7007689 100644 --- a/examples/solr-container/build.gradle +++ b/examples/solr-container/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly "org.projectlombok:lombok:1.18.24" annotationProcessor "org.projectlombok:lombok:1.18.24" - implementation 'org.apache.solr:solr-solrj:8.11.1' + implementation 'org.apache.solr:solr-solrj:8.11.2' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.testcontainers:solr' From c65fc31df0620347e7e4236e8714b18ba52c14d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Tue, 4 Oct 2022 13:43:29 -0500 Subject: [PATCH 2936/5615] Update latest version in mkdocs.yml --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index ab57aeb11d6..3a802d4ec91 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -108,4 +108,4 @@ nav: - contributing_docs.md - bounty.md extra: - latest_version: 1.17.4 + latest_version: 1.17.5 From b1fa4ece54a4d13e67d20a3db123c98230451f09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Tue, 4 Oct 2022 13:44:04 -0500 Subject: [PATCH 2937/5615] Upgrade testcontainers version to 1.17.5 in japicmp --- gradle/japicmp.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/japicmp.gradle b/gradle/japicmp.gradle index 6d769bf9286..ed4223324ab 100644 --- a/gradle/japicmp.gradle +++ b/gradle/japicmp.gradle @@ -3,7 +3,7 @@ configurations { } dependencies { - baseline "org.testcontainers:${project.name}:1.17.4", { + baseline "org.testcontainers:${project.name}:1.17.5", { exclude group: "*", module: "*" } } From f5e79b9aab308acc0211fca4bd0d00ab9b57cbd6 Mon Sep 17 00:00:00 2001 From: Jan-Hendrik Dolling Date: Tue, 4 Oct 2022 22:08:46 +0200 Subject: [PATCH 2938/5615] Remove unnecessary configuration from simple tests (#5973) * Remove mysql configuration from simple test * mysql custom ini file test also runs on Windows Co-authored-by: Kevin Wittek --- .../java/org/testcontainers/junit/mysql/SimpleMySQLTest.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java index f60712e7123..e39c55b85a9 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java @@ -1,6 +1,5 @@ package org.testcontainers.junit.mysql; -import org.apache.commons.lang3.SystemUtils; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,7 +20,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; -import static org.junit.Assume.assumeFalse; public class SimpleMySQLTest extends AbstractContainerDatabaseTest { @@ -48,7 +46,6 @@ public class SimpleMySQLTest extends AbstractContainerDatabaseTest { public void testSimple() throws SQLException { try ( MySQLContainer mysql = new MySQLContainer<>(MySQLTestImages.MYSQL_57_IMAGE) - .withConfigurationOverride("somepath/mysql_conf_override") .withLogConsumer(new Slf4jLogConsumer(logger)) ) { mysql.start(); @@ -81,8 +78,6 @@ public void testSpecificVersion() throws SQLException { @Test public void testMySQLWithCustomIniFile() throws SQLException { - assumeFalse(SystemUtils.IS_OS_WINDOWS); - try ( MySQLContainer mysqlCustomConfig = new MySQLContainer<>(MySQLTestImages.MYSQL_56_IMAGE) .withConfigurationOverride("somepath/mysql_conf_override") From 2de82d96b38bb163941b64326f6286e8a4dce02d Mon Sep 17 00:00:00 2001 From: "K. Siva Prasad Reddy" Date: Wed, 5 Oct 2022 05:47:34 +0530 Subject: [PATCH 2939/5615] Update MockServer Tests to use mockserver/mockserver image with 5.14.0 version (#5977) Fixes #5975 --- modules/mockserver/build.gradle | 2 +- .../containers/MockServerContainerRuleTest.java | 6 ++++-- .../testcontainers/containers/MockServerContainerTest.java | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/modules/mockserver/build.gradle b/modules/mockserver/build.gradle index df46295df21..85e7aaa181e 100644 --- a/modules/mockserver/build.gradle +++ b/modules/mockserver/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: MockServer" dependencies { api project(':testcontainers') - testImplementation 'org.mock-server:mockserver-client-java:5.13.2' + testImplementation 'org.mock-server:mockserver-client-java:5.14.0' testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java index 0e4fa102b2e..7fdacd7c580 100644 --- a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java +++ b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java @@ -12,12 +12,14 @@ public class MockServerContainerRuleTest { public static final DockerImageName MOCKSERVER_IMAGE = DockerImageName.parse( - "jamesdbloom/mockserver:mockserver-5.13.2" + "mockserver/mockserver:mockserver-5.14.0" ); // creatingProxy { @Rule - public MockServerContainer mockServer = new MockServerContainer(MOCKSERVER_IMAGE); + public MockServerContainer mockServer = new MockServerContainer( + MOCKSERVER_IMAGE.withTag("mockserver-" + MockServerClient.class.getPackage().getImplementationVersion()) + ); // } diff --git a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java index 6ddce4b6cbf..b59f14cc27b 100644 --- a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java +++ b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java @@ -11,7 +11,7 @@ public class MockServerContainerTest { public static final DockerImageName MOCKSERVER_IMAGE = DockerImageName.parse( - "jamesdbloom/mockserver:mockserver-5.5.4" + "mockserver/mockserver:mockserver-5.14.0" ); @Test @@ -40,7 +40,7 @@ public void shouldCallActualMockserverVersion() throws Exception { @Test public void newVersionStartsWithDefaultWaitStrategy() { - DockerImageName dockerImageName = DockerImageName.parse("mockserver/mockserver:mockserver-5.11.2"); + DockerImageName dockerImageName = DockerImageName.parse("mockserver/mockserver:mockserver-5.14.0"); try (MockServerContainer mockServer = new MockServerContainer(dockerImageName)) { mockServer.start(); } From 478b0492ea1526a2941ef714f9c9c7c4bcbb57a9 Mon Sep 17 00:00:00 2001 From: Etienne Dysli Metref Date: Thu, 6 Oct 2022 01:54:39 +0200 Subject: [PATCH 2940/5615] Update docs about logging configuration (#5976) * Recommend disabling Apache HTTP client wire logging * Recommend enabling debug logging only for org.testcontainers Fixes #4913 Co-authored-by: Kevin Wittek --- docs/supported_docker_environment/logging_config.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/supported_docker_environment/logging_config.md b/docs/supported_docker_environment/logging_config.md index 5c7abcd211c..3e4660e85ae 100644 --- a/docs/supported_docker_environment/logging_config.md +++ b/docs/supported_docker_environment/logging_config.md @@ -18,5 +18,14 @@ should be included in your classpath to show a reasonable level of log output: + ``` + +In order to troubleshoot issues with Testcontainers, increase the logging level of `org.testcontainers` to `DEBUG`: + +```xml + +``` + +Avoid changing the root logger's level to `DEBUG`, because this turns on debug logging for every package whose level isn't explicitly configured here, resulting in a large amount of log data. From 09c42a488b2ee1fb010213976fe9621c17d461c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Thu, 6 Oct 2022 13:13:13 -0500 Subject: [PATCH 2941/5615] Add reference to supported languages (#5979) Co-authored-by: Kevin Wittek --- docs/supported_languages.md | 9 +++++++++ mkdocs.yml | 1 + 2 files changed, 10 insertions(+) create mode 100644 docs/supported_languages.md diff --git a/docs/supported_languages.md b/docs/supported_languages.md new file mode 100644 index 00000000000..4990559f0e7 --- /dev/null +++ b/docs/supported_languages.md @@ -0,0 +1,9 @@ +# Other Supported Languages + +Besides *Testcontainers for Java,* you can find Testcontainers implementations for many other languages. +Please note that not all features that are available in *Testcontainers for Java* might be available in the implementations for other languages yet. + +* [Go](https://golang.testcontainers.org/) +* [.Net](https://github.com/testcontainers/testcontainers-dotnet) +* [Python](https://testcontainers-python.readthedocs.io/) +* [JavaScript/NodeJS](https://github.com/testcontainers/testcontainers-node) diff --git a/mkdocs.yml b/mkdocs.yml index 3a802d4ec91..70421761e02 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -102,6 +102,7 @@ nav: - supported_docker_environment/windows.md - supported_docker_environment/logging_config.md - supported_docker_environment/image_registry_rate_limiting.md + - Other Supported Languages: supported_languages.md - Getting help: getting_help.md - Contributing: - contributing.md From 914769f56a1b5b9c66d4419e6b1ca0e178cef399 Mon Sep 17 00:00:00 2001 From: Priyambada Roul <75459506+roulpriya@users.noreply.github.com> Date: Mon, 10 Oct 2022 23:06:01 +0530 Subject: [PATCH 2942/5615] Do not allow empty env vars (#5983) Fixes #5026 Signed-off-by: Priyambada Roul --- .../utility/TestcontainersConfiguration.java | 5 ++++- .../utility/TestcontainersConfigurationTest.java | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index e647a8d50b3..f3bb8f8319a 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -230,7 +230,10 @@ private String getConfigurable( } if (environment.containsKey(envVarName)) { - return environment.get(envVarName); + String value = environment.get(envVarName); + if (!value.isEmpty()) { + return value; + } } for (final Properties properties : propertiesSources) { diff --git a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java index d51bc89348c..fcc0a654ef5 100644 --- a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java +++ b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java @@ -126,6 +126,14 @@ public void shouldReadDockerSettingsFromUserProperties() { .isEqualTo("some value"); } + @Test + public void shouldNotReadSettingIfCorrespondingEnvironmentVarIsEmptyString() { + environment.put("DOCKER_FOO", ""); + assertThat(newConfig().getEnvVarOrUserProperty("docker.foo", "default")) + .as("reads unprefixed env vars for docker. settings") + .isEqualTo("default"); + } + @Test public void shouldNotReadDockerClientStrategyFromClasspathProperties() { String currentValue = newConfig().getDockerClientStrategyClassName(); From 75ac1d1ff5e14a0306f981288d5736d275970d88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Tue, 11 Oct 2022 20:56:26 -0500 Subject: [PATCH 2943/5615] Upgrade python version to 3.8 --- docker-compose.yml | 2 +- docs/contributing_docs.md | 2 +- runtime.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 1ce5532d0bd..bfb6bdc35fe 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.7" services: docs: - image: python:3.7 + image: python:3.8 command: sh -c "pip install -r requirements.txt && mkdocs serve -a 0.0.0.0:8000" working_dir: /docs volumes: diff --git a/docs/contributing_docs.md b/docs/contributing_docs.md index 2d706dfe407..c303fe6b06c 100644 --- a/docs/contributing_docs.md +++ b/docs/contributing_docs.md @@ -15,7 +15,7 @@ The root of the project contains a `docker-compose.yml` file. Simply run `docker ### Using Python locally -* Ensure that you have Python 3.7.0 or higher. +* Ensure that you have Python 3.8.0 or higher. * Set up a virtualenv and run `pip install -r requirements.txt` in the `testcontainers-java` root directory. * Once Python dependencies have been installed, run `mkdocs serve` to start a local auto-updating MkDocs server. diff --git a/runtime.txt b/runtime.txt index 475ba515c04..cc1923a40b1 100644 --- a/runtime.txt +++ b/runtime.txt @@ -1 +1 @@ -3.7 +3.8 From 2da2675c1b9b13d7fde985c00001105b800a84ce Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Wed, 12 Oct 2022 11:50:26 +0200 Subject: [PATCH 2944/5615] Add paragraph about Docker requirements to docs (#5987) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Eddú Meléndez Gonzales --- docs/supported_docker_environment/index.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/supported_docker_environment/index.md b/docs/supported_docker_environment/index.md index debca47e574..885325545ff 100644 --- a/docs/supported_docker_environment/index.md +++ b/docs/supported_docker_environment/index.md @@ -2,6 +2,15 @@ ## Overview +Testcontainers requires a Docker-API compatible container runtime. +During development, Testcontainers is actively tested against recent versions of Docker on Linux, as well as against Docker Desktop on Mac and Windows. +These Docker environments are automatically detected and used by Testcontainers without any additional configuration being necessary. + +It is possible to configure Testcontainers to work for other Docker setups, such as a remote Docker host or Docker alternatives. +However, these are not actively tested in the main development workflow, so not all Testcontainers features might be available and additional manual configuration might be necessary. +If you have further questions about configuration details for your setup or whether it supports running Testcontainers-based tests, +please contact the Testcontainers team and other users from the Testcontainers community on [Slack](https://slack.testcontainers.org/). + | Host Operating System / Environment | Minimum recommended docker versions | Known issues / tips | |-------------------------------------|-----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Linux - general | Docker v17.09 | After docker installation, follow [post-installation steps](https://docs.docker.com/engine/install/linux-postinstall/). | From 96acaf02721c5dbf543ff2b166366a190df57cfa Mon Sep 17 00:00:00 2001 From: Ammar Khaku Date: Fri, 14 Oct 2022 08:59:59 -0700 Subject: [PATCH 2945/5615] Catch and ignore more errors when reflecting into container subclass (#5990) With #2830 we added support for 4.x of the Cassandra driver. It was done in a way to allow a user to use either the 3.x or 4.x driver while excluding the other one. However if using 4.x and excluding 3.x, GenericContainer#canBeReused throws an exception during reflection since the Cluster class returned by CassandraContainer#getCluster is missing. This PR works around that issue by catching and ignoring the thrown NoClassDefFoundError. --- .../java/org/testcontainers/containers/GenericContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 7163ea40a93..bc65cb22040 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -360,7 +360,7 @@ protected boolean canBeReused() { logger().warn("{} can't be reused because it overrides {}", getClass(), method.getName()); return false; } - } catch (NoSuchMethodException e) { + } catch (NoSuchMethodException | NoClassDefFoundError e) { // ignore } } From 00c5e88562e50f3337d43a3d193669a5fdbac61e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Tue, 18 Oct 2022 08:12:30 -0500 Subject: [PATCH 2946/5615] Remove Transferable#of(java.lang.String,int) for japicmp (#5992) `Transferable#of(java.lang.String,int)` was introduced in version 1.17.4. Current builds will check against 1.17.5 and binary compatibility will be fine. --- core/build.gradle | 6 ------ 1 file changed, 6 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index 2967b35d4ff..364f51b6b70 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -113,9 +113,3 @@ tasks.generatePomFileForMavenJavaPublication.finalizedBy( ] } ) - -tasks.japicmp { - methodExcludes = [ - "org.testcontainers.images.builder.Transferable#of(java.lang.String,int)" - ] -} From 077c84405486e061a31d585b8ffa5c2d3ea0b7c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Wed, 19 Oct 2022 02:27:21 -0500 Subject: [PATCH 2947/5615] Deprecate `setCustomContent` and `withCustomContent` at `NginxContainer` (#5997) In order to make `NginxContainer` friendly with remote environments, we should avoid the use of `addFileSystemBind`. Example has been updated in order to show how to do it. --- .../java/org/testcontainers/containers/NginxContainer.java | 2 ++ .../test/java/org/testcontainers/junit/SimpleNginxTest.java | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java b/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java index b97992a5684..1bf951af7c8 100644 --- a/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java +++ b/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java @@ -56,10 +56,12 @@ public URL getBaseUrl(String scheme, int port) throws MalformedURLException { return new URL(scheme + "://" + getHost() + ":" + getMappedPort(port)); } + @Deprecated public void setCustomContent(String htmlContentPath) { addFileSystemBind(htmlContentPath, "/usr/share/nginx/html", BindMode.READ_ONLY); } + @Deprecated public SELF withCustomContent(String htmlContentPath) { this.setCustomContent(htmlContentPath); return self(); diff --git a/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java b/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java index abfbb663207..d338ebf180b 100644 --- a/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java +++ b/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java @@ -7,6 +7,7 @@ import org.testcontainers.containers.NginxContainer; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; import java.io.*; import java.net.URL; @@ -26,7 +27,7 @@ public class SimpleNginxTest { // creatingContainer { @Rule public NginxContainer nginx = new NginxContainer<>(NGINX_IMAGE) - .withCustomContent(tmpDirectory) + .withCopyFileToContainer(MountableFile.forHostPath(tmpDirectory), "/usr/share/nginx/html") .waitingFor(new HttpWaitStrategy()); // } From 0b0e559141c5b83eaeb8ee89b143b5d28cbc3136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Wed, 19 Oct 2022 02:27:36 -0500 Subject: [PATCH 2948/5615] Update toxiproxy image to ghcr.io/shopify/toxiproxy (#5996) `shopify/toxiproxy` has moved to `ghcr.io/shopify/toxiproxy`. Currently, our tests are using the prior and is display as part of the documentation. In order to keep our tests and docs up-to-date with latest images we are updating the image. Also, we are adding the proper configuration for logging in this module. --- .../testcontainers/containers/ToxiproxyTest.java | 2 +- .../src/test/resources/logback-test.xml | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 modules/toxiproxy/src/test/resources/logback-test.xml diff --git a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java index bfe488eaac1..25a0feccc48 100644 --- a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java +++ b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java @@ -35,7 +35,7 @@ public class ToxiproxyTest { .withExposedPorts(6379) .withNetwork(network); - private static final DockerImageName TOXIPROXY_IMAGE = DockerImageName.parse("shopify/toxiproxy:2.1.0"); + private static final DockerImageName TOXIPROXY_IMAGE = DockerImageName.parse("ghcr.io/shopify/toxiproxy:2.5.0"); // Toxiproxy container, which will be used as a TCP proxy @Rule diff --git a/modules/toxiproxy/src/test/resources/logback-test.xml b/modules/toxiproxy/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..83ef7a1a3ef --- /dev/null +++ b/modules/toxiproxy/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + From d1864a466b294296bca00368fd904d78c3a78057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Thu, 20 Oct 2022 07:54:15 -0500 Subject: [PATCH 2949/5615] Fix dependabot.yml ignore section (#5999) --- .github/dependabot.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 87ce5c86463..46c7046300d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -118,7 +118,7 @@ updates: open-pull-requests-limit: 10 ignore: - dependency-name: "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml" - update-types: [ "version-update:semver-minor" ] + update-types: [ "version-update:semver-minor", "version-update:semver-patch" ] - package-ecosystem: "gradle" directory: "/modules/kafka" schedule: @@ -202,7 +202,7 @@ updates: open-pull-requests-limit: 10 ignore: - dependency-name: "io.r2dbc:r2dbc-spi" - versions: ["0.8.x"] + update-types: [ "version-update:semver-major", "version-update:semver-minor" ] - package-ecosystem: "gradle" directory: "/modules/rabbitmq" schedule: From cf1573e4ffab1f302141d5bda20d23ecaba5b148 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 12:54:49 +0000 Subject: [PATCH 2950/5615] Bump tomcat-jdbc from 10.0.23 to 10.0.27 in /modules/jdbc-test Bumps tomcat-jdbc from 10.0.23 to 10.0.27. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index f8b55d01bc5..798508ffa22 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -11,7 +11,7 @@ dependencies { api 'org.assertj:assertj-core:3.23.1' - api 'org.apache.tomcat:tomcat-jdbc:10.0.23' + api 'org.apache.tomcat:tomcat-jdbc:10.0.27' api 'org.vibur:vibur-dbcp:25.0' api 'mysql:mysql-connector-java:8.0.30' } From 5ee6fd38cf3b4a2940cd399d4a3a662f3d5ec3e1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 12:54:49 +0000 Subject: [PATCH 2951/5615] Bump mysql-connector-java from 8.0.30 to 8.0.31 in /modules/mysql Bumps mysql-connector-java from 8.0.30 to 8.0.31. --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 87887384973..a18941ab9f7 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' testImplementation project(':jdbc-test') - testImplementation 'mysql:mysql-connector-java:8.0.30' + testImplementation 'mysql:mysql-connector-java:8.0.31' testImplementation testFixtures(project(':r2dbc')) testImplementation 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' From c8ec550ec6278d28cb3e5abcaf376292d2e9920c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 12:54:49 +0000 Subject: [PATCH 2952/5615] Bump tomcat-jdbc from 10.1.0 to 10.1.1 in /modules/jdbc Bumps tomcat-jdbc from 10.1.0 to 10.1.1. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 7a0fbf03fc1..92f72287fa2 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -7,7 +7,7 @@ dependencies { compileOnly 'org.jetbrains:annotations:23.0.0' testImplementation 'commons-dbutils:commons-dbutils:1.7' testImplementation 'org.vibur:vibur-dbcp:25.0' - testImplementation 'org.apache.tomcat:tomcat-jdbc:10.1.0' + testImplementation 'org.apache.tomcat:tomcat-jdbc:10.1.1' testImplementation 'com.zaxxer:HikariCP-java6:2.3.13' testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation ('org.mockito:mockito-core:4.8.0') { From ceee70954ad8f32252925ff415e6f616b0f3aff3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 12:54:51 +0000 Subject: [PATCH 2953/5615] Bump reactor-core from 3.4.23 to 3.4.24 in /modules/r2dbc Bumps [reactor-core](https://github.com/reactor/reactor-core) from 3.4.23 to 3.4.24. - [Release notes](https://github.com/reactor/reactor-core/releases) - [Commits](https://github.com/reactor/reactor-core/compare/v3.4.23...v3.4.24) --- updated-dependencies: - dependency-name: io.projectreactor:reactor-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/r2dbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle index 9126ff0a347..952c9f5b982 100644 --- a/modules/r2dbc/build.gradle +++ b/modules/r2dbc/build.gradle @@ -15,6 +15,6 @@ dependencies { testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.13.RELEASE' testImplementation project(':postgresql') - testFixturesImplementation 'io.projectreactor:reactor-core:3.4.23' + testFixturesImplementation 'io.projectreactor:reactor-core:3.4.24' testFixturesImplementation 'org.assertj:assertj-core:3.23.1' } From d47b1ca51898e01d3002e76ac6fd6a2f2c6a6502 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 12:54:51 +0000 Subject: [PATCH 2954/5615] Bump mysql-connector-java from 8.0.30 to 8.0.31 in /modules/spock Bumps mysql-connector-java from 8.0.30 to 8.0.31. --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index a13a7049e13..e202b07f411 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -16,7 +16,7 @@ dependencies { testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' testRuntimeOnly 'org.postgresql:postgresql:42.5.0' - testRuntimeOnly 'mysql:mysql-connector-java:8.0.30' + testRuntimeOnly 'mysql:mysql-connector-java:8.0.31' testCompileOnly 'org.jetbrains:annotations:23.0.0' } From b260332e1221363aa5b2bd3139460f6b65485ce0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 12:54:51 +0000 Subject: [PATCH 2955/5615] Bump jedis from 4.2.3 to 4.3.1 in /modules/junit-jupiter Bumps [jedis](https://github.com/redis/jedis) from 4.2.3 to 4.3.1. - [Release notes](https://github.com/redis/jedis/releases) - [Commits](https://github.com/redis/jedis/compare/v4.2.3...v4.3.1) --- updated-dependencies: - dependency-name: redis.clients:jedis dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index e801e54ab83..496c273fbde 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -7,7 +7,7 @@ dependencies { testImplementation project(':mysql') testImplementation project(':postgresql') testImplementation 'com.zaxxer:HikariCP:4.0.3' - testImplementation 'redis.clients:jedis:4.2.3' + testImplementation 'redis.clients:jedis:4.3.1' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' testImplementation ('org.mockito:mockito-core:4.8.0') { exclude(module: 'hamcrest-core') From 46a4edc16b065f9693a5c754670376e9e5d34467 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 12:54:52 +0000 Subject: [PATCH 2956/5615] Bump mysql-connector-java in /modules/junit-jupiter Bumps mysql-connector-java from 8.0.30 to 8.0.31. --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index e801e54ab83..2819985921d 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -16,7 +16,7 @@ dependencies { testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.1' testRuntimeOnly 'org.postgresql:postgresql:42.5.0' - testRuntimeOnly 'mysql:mysql-connector-java:8.0.30' + testRuntimeOnly 'mysql:mysql-connector-java:8.0.31' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.1' } From 5e23c5e8e680b6ddbda496d4b102bae08f7092ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 12:54:52 +0000 Subject: [PATCH 2957/5615] Bump pulsar-client-admin from 2.10.1 to 2.10.2 in /modules/pulsar Bumps [pulsar-client-admin](https://github.com/apache/pulsar) from 2.10.1 to 2.10.2. - [Release notes](https://github.com/apache/pulsar/releases) - [Commits](https://github.com/apache/pulsar/compare/v2.10.1...v2.10.2) --- updated-dependencies: - dependency-name: org.apache.pulsar:pulsar-client-admin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 8bc139a3a93..c15fc32730e 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -5,5 +5,5 @@ dependencies { testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.10.1' testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.23.1' - testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.10.1' + testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.10.2' } From aa597b8ed501b6dbda20c06fbe0b42c0b5264b89 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 12:54:54 +0000 Subject: [PATCH 2958/5615] Bump mysql-connector-java from 8.0.30 to 8.0.31 in /modules/tidb Bumps mysql-connector-java from 8.0.30 to 8.0.31. --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/tidb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/tidb/build.gradle b/modules/tidb/build.gradle index 36f6acfe806..92f4aa408d3 100644 --- a/modules/tidb/build.gradle +++ b/modules/tidb/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'mysql:mysql-connector-java:8.0.30' + testImplementation 'mysql:mysql-connector-java:8.0.31' compileOnly 'org.jetbrains:annotations:23.0.0' } From 070f670a0f7f9c1c4a61c6c1974e42cea41e15c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 12:54:58 +0000 Subject: [PATCH 2959/5615] Bump mysql-connector-java from 8.0.21 to 8.0.31 in /examples Bumps mysql-connector-java from 8.0.21 to 8.0.31. --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- docs/examples/junit4/generic/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/junit4/generic/build.gradle b/docs/examples/junit4/generic/build.gradle index da4697a4543..9c3edd6fe27 100644 --- a/docs/examples/junit4/generic/build.gradle +++ b/docs/examples/junit4/generic/build.gradle @@ -6,7 +6,7 @@ dependencies { testImplementation project(":selenium") testImplementation project(":mysql") - testImplementation 'mysql:mysql-connector-java:8.0.21' + testImplementation 'mysql:mysql-connector-java:8.0.31' testImplementation "org.seleniumhq.selenium:selenium-api:3.141.59" testImplementation 'org.assertj:assertj-core:3.15.0' } From 852ed7838b1a2d49bca12b6eaf3fcefd7bb82ad8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 12:54:59 +0000 Subject: [PATCH 2960/5615] Bump pulsar-client from 2.10.1 to 2.10.2 in /modules/pulsar Bumps [pulsar-client](https://github.com/apache/pulsar) from 2.10.1 to 2.10.2. - [Release notes](https://github.com/apache/pulsar/releases) - [Commits](https://github.com/apache/pulsar/compare/v2.10.1...v2.10.2) --- updated-dependencies: - dependency-name: org.apache.pulsar:pulsar-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 8bc139a3a93..fd015a9121f 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Pulsar" dependencies { api project(':testcontainers') - testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.10.1' + testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.10.2' testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.23.1' testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.10.1' } From 5e090c2984fb461fa26d76e14f54f46254dc7b6d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 12:55:00 +0000 Subject: [PATCH 2961/5615] Bump actions/cache from 3.0.9 to 3.0.11 Bumps [actions/cache](https://github.com/actions/cache) from 3.0.9 to 3.0.11. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3.0.9...v3.0.11) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index 527c2eccfeb..16b6d5249c7 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -25,7 +25,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.9 + uses: actions/cache@v3.0.11 continue-on-error: true with: path: ~/.gradle/caches @@ -53,7 +53,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.9 + uses: actions/cache@v3.0.11 continue-on-error: true with: path: ~/.gradle/caches diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b406f256ae2..25723b20ef0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.9 + uses: actions/cache@v3.0.11 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-home-testmatrix-${{ hashFiles('**/*.gradle') }} @@ -51,7 +51,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.9 + uses: actions/cache@v3.0.11 continue-on-error: true with: path: ~/.gradle/caches From c745c8f7256ca3852165b631e7919009bb926c5c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 12:55:03 +0000 Subject: [PATCH 2962/5615] Bump peter-evans/create-pull-request from 4.1.3 to 4.2.0 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4.1.3 to 4.2.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/671dc9c9e0c2d73f07fa45a3eb0220e1622f0c5f...b4d51739f96fca8047ad065eccef63442d8e99f7) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/update-docs-version.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index f40b5f41483..53ac0ae9b9a 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -23,7 +23,7 @@ jobs: sed -i "s/latest_version: .*/latest_version: ${GITHUB_REF##*/}/g" mkdocs.yml git diff - name: Create Pull Request - uses: peter-evans/create-pull-request@671dc9c9e0c2d73f07fa45a3eb0220e1622f0c5f # v3.10.1 + uses: peter-evans/create-pull-request@b4d51739f96fca8047ad065eccef63442d8e99f7 # v3.10.1 with: title: Update docs version to ${GITHUB_REF##*/} body: | From 7f39a39147eff17b5daed4f29419104cf21321b3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 13:55:48 +0000 Subject: [PATCH 2963/5615] Bump release-drafter/release-drafter from 5.21.0 to 5.21.1 Bumps [release-drafter/release-drafter](https://github.com/release-drafter/release-drafter) from 5.21.0 to 5.21.1. - [Release notes](https://github.com/release-drafter/release-drafter/releases) - [Commits](https://github.com/release-drafter/release-drafter/compare/df69d584deac33d8569990cb6413f82447181076...6df64e4ba4842c203c604c1f45246c5863410adb) --- updated-dependencies: - dependency-name: release-drafter/release-drafter dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/release-drafter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index fca7465637f..df1a05c8441 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -18,6 +18,6 @@ jobs: if: github.repository == 'testcontainers/testcontainers-java' runs-on: ubuntu-latest steps: - - uses: release-drafter/release-drafter@df69d584deac33d8569990cb6413f82447181076 # v5.19.0 + - uses: release-drafter/release-drafter@6df64e4ba4842c203c604c1f45246c5863410adb # v5.19.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 64bb21083001d41bb13b1c73fd2df4f44718a529 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 14:22:42 +0000 Subject: [PATCH 2964/5615] Bump junit-jupiter-params from 5.4.2 to 5.9.1 in /examples Bumps [junit-jupiter-params](https://github.com/junit-team/junit5) from 5.4.2 to 5.9.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.4.2...r5.9.1) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-params dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- docs/examples/junit5/redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/junit5/redis/build.gradle b/docs/examples/junit5/redis/build.gradle index 72b943adc23..3701d9699e9 100644 --- a/docs/examples/junit5/redis/build.gradle +++ b/docs/examples/junit5/redis/build.gradle @@ -4,7 +4,7 @@ dependencies { api "io.lettuce:lettuce-core:5.1.1.RELEASE" testImplementation "org.junit.jupiter:junit-jupiter-api:5.4.2" - testImplementation "org.junit.jupiter:junit-jupiter-params:5.4.2" + testImplementation "org.junit.jupiter:junit-jupiter-params:5.9.1" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.4.2" testImplementation project(":testcontainers") testImplementation project(":junit-jupiter") From f9edf87f7a02926c6ceaa1cf2ce05ac30842b195 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Fri, 21 Oct 2022 18:14:44 +0200 Subject: [PATCH 2965/5615] Remove unused Pipfiles (#6046) --- Pipfile | 15 --- Pipfile.lock | 314 --------------------------------------------------- 2 files changed, 329 deletions(-) delete mode 100644 Pipfile delete mode 100644 Pipfile.lock diff --git a/Pipfile b/Pipfile deleted file mode 100644 index 2236de86daf..00000000000 --- a/Pipfile +++ /dev/null @@ -1,15 +0,0 @@ -[[source]] -name = "pypi" -url = "https://pypi.org/simple" -verify_ssl = true - -[dev-packages] - -[packages] -mkdocs = "*" -mkdocs-codeinclude-plugin = "*" -mkdocs-material = "*" -mkdocs-markdownextradata-plugin = "*" - -[requires] -python_version = "3.7" diff --git a/Pipfile.lock b/Pipfile.lock deleted file mode 100644 index 7dad511e377..00000000000 --- a/Pipfile.lock +++ /dev/null @@ -1,314 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "d3830267ea93391a077a2ba5b93aa8218c0a38002694b404b56df11498da3e56" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.7" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "click": { - "hashes": [ - "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3", - "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b" - ], - "markers": "python_version >= '3.6'", - "version": "==8.0.3" - }, - "ghp-import": { - "hashes": [ - "sha256:5f8962b30b20652cdffa9c5a9812f7de6bcb56ec475acac579807719bf242c46", - "sha256:947b3771f11be850c852c64b561c600fdddf794bab363060854c1ee7ad05e071" - ], - "version": "==2.0.2" - }, - "importlib-metadata": { - "hashes": [ - "sha256:53ccfd5c134223e497627b9815d5030edf77d2ed573922f7a0b8f8bb81a1c100", - "sha256:75bdec14c397f528724c1bfd9709d660b33a4d2e77387a3358f20b848bb5e5fb" - ], - "markers": "python_version >= '3.6'", - "version": "==4.8.2" - }, - "jinja2": { - "hashes": [ - "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8", - "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7" - ], - "markers": "python_version >= '3.6'", - "version": "==3.0.3" - }, - "markdown": { - "hashes": [ - "sha256:76df8ae32294ec39dcf89340382882dfa12975f87f45c3ed1ecdb1e8cefc7006", - "sha256:9923332318f843411e9932237530df53162e29dc7a4e2b91e35764583c46c9a3" - ], - "markers": "python_version >= '3.6'", - "version": "==3.3.6" - }, - "markupsafe": { - "hashes": [ - "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298", - "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64", - "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b", - "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194", - "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567", - "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff", - "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724", - "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74", - "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646", - "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35", - "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6", - "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a", - "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6", - "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad", - "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26", - "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38", - "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac", - "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7", - "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6", - "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047", - "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75", - "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f", - "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b", - "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135", - "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8", - "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a", - "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a", - "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1", - "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9", - "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864", - "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914", - "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee", - "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f", - "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18", - "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8", - "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2", - "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d", - "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b", - "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b", - "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86", - "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6", - "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f", - "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb", - "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833", - "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28", - "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e", - "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415", - "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902", - "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f", - "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d", - "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9", - "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d", - "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145", - "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066", - "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c", - "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1", - "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a", - "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207", - "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f", - "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53", - "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd", - "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134", - "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85", - "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9", - "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5", - "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94", - "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509", - "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51", - "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872" - ], - "markers": "python_version >= '3.6'", - "version": "==2.0.1" - }, - "mergedeep": { - "hashes": [ - "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", - "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307" - ], - "markers": "python_version >= '3.6'", - "version": "==1.3.4" - }, - "mkdocs": { - "hashes": [ - "sha256:89f5a094764381cda656af4298727c9f53dc3e602983087e1fe96ea1df24f4c1", - "sha256:a1fa8c2d0c1305d7fc2b9d9f607c71778572a8b110fb26642aa00296c9e6d072" - ], - "index": "pypi", - "version": "==1.2.3" - }, - "mkdocs-codeinclude-plugin": { - "hashes": [ - "sha256:5d74bd4c1be8e64d71137c1ef1d27a0f9822d3d9ecb2af658d2af7664568a923", - "sha256:a94be142e27ef4a70d8ecc702c21d9596304e06d382d86b39a10ffe8b0375887" - ], - "index": "pypi", - "version": "==0.2.0" - }, - "mkdocs-markdownextradata-plugin": { - "hashes": [ - "sha256:64d1c966b288d653f51f7531c03204eb988d0d77e56055c9d703d99105259a36" - ], - "index": "pypi", - "version": "==0.0.5" - }, - "mkdocs-material": { - "hashes": [ - "sha256:524debb6ee8ee89cee08886f2a67c3c3875c0ee9579c598d7448cbd2607cd3b7", - "sha256:62ae84082fa9f077c86b7db63e7bedf392005041b451defc850f8d0887a11e91" - ], - "index": "pypi", - "version": "==3.2.0" - }, - "packaging": { - "hashes": [ - "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", - "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" - ], - "markers": "python_version >= '3.6'", - "version": "==21.3" - }, - "pygments": { - "hashes": [ - "sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380", - "sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6" - ], - "markers": "python_version >= '3.5'", - "version": "==2.10.0" - }, - "pymdown-extensions": { - "hashes": [ - "sha256:74247f2c80f1d9e3c7242abe1c16317da36c6f26c7ad4b8a7f457f0ec20f0365", - "sha256:b03e66f91f33af4a6e7a0e20c740313522995f69a03d86316b1449766c473d0e" - ], - "markers": "python_version >= '3.6'", - "version": "==9.1" - }, - "pyparsing": { - "hashes": [ - "sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4", - "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81" - ], - "markers": "python_version >= '3.6'", - "version": "==3.0.6" - }, - "python-dateutil": { - "hashes": [ - "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", - "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.2" - }, - "pyyaml": { - "hashes": [ - "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", - "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b", - "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57", - "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b", - "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4", - "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07", - "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba", - "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9", - "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287", - "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513", - "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0", - "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0", - "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92", - "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f", - "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2", - "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc", - "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c", - "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86", - "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4", - "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c", - "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34", - "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b", - "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c", - "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb", - "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737", - "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3", - "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d", - "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53", - "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78", - "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803", - "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a", - "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174", - "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5" - ], - "markers": "python_version >= '3.6'", - "version": "==6.0" - }, - "pyyaml-env-tag": { - "hashes": [ - "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb", - "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069" - ], - "markers": "python_version >= '3.6'", - "version": "==0.1" - }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" - }, - "typing-extensions": { - "hashes": [ - "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e", - "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b" - ], - "markers": "python_version < '3.8'", - "version": "==4.0.1" - }, - "watchdog": { - "hashes": [ - "sha256:25fb5240b195d17de949588628fdf93032ebf163524ef08933db0ea1f99bd685", - "sha256:3386b367e950a11b0568062b70cc026c6f645428a698d33d39e013aaeda4cc04", - "sha256:3becdb380d8916c873ad512f1701f8a92ce79ec6978ffde92919fd18d41da7fb", - "sha256:4ae38bf8ba6f39d5b83f78661273216e7db5b00f08be7592062cb1fc8b8ba542", - "sha256:8047da932432aa32c515ec1447ea79ce578d0559362ca3605f8e9568f844e3c6", - "sha256:8f1c00aa35f504197561060ca4c21d3cc079ba29cf6dd2fe61024c70160c990b", - "sha256:922a69fa533cb0c793b483becaaa0845f655151e7256ec73630a1b2e9ebcb660", - "sha256:9693f35162dc6208d10b10ddf0458cc09ad70c30ba689d9206e02cd836ce28a3", - "sha256:a0f1c7edf116a12f7245be06120b1852275f9506a7d90227648b250755a03923", - "sha256:a36e75df6c767cbf46f61a91c70b3ba71811dfa0aca4a324d9407a06a8b7a2e7", - "sha256:aba5c812f8ee8a3ff3be51887ca2d55fb8e268439ed44110d3846e4229eb0e8b", - "sha256:ad6f1796e37db2223d2a3f302f586f74c72c630b48a9872c1e7ae8e92e0ab669", - "sha256:ae67501c95606072aafa865b6ed47343ac6484472a2f95490ba151f6347acfc2", - "sha256:b2fcf9402fde2672545b139694284dc3b665fd1be660d73eca6805197ef776a3", - "sha256:b52b88021b9541a60531142b0a451baca08d28b74a723d0c99b13c8c8d48d604", - "sha256:b7d336912853d7b77f9b2c24eeed6a5065d0a0cc0d3b6a5a45ad6d1d05fb8cd8", - "sha256:bd9ba4f332cf57b2c1f698be0728c020399ef3040577cde2939f2e045b39c1e5", - "sha256:be9be735f827820a06340dff2ddea1fb7234561fa5e6300a62fe7f54d40546a0", - "sha256:cca7741c0fcc765568350cb139e92b7f9f3c9a08c4f32591d18ab0a6ac9e71b6", - "sha256:d0d19fb2441947b58fbf91336638c2b9f4cc98e05e1045404d7a4cb7cddc7a65", - "sha256:e02794ac791662a5eafc6ffeaf9bcc149035a0e48eb0a9d40a8feb4622605a3d", - "sha256:e0f30db709c939cabf64a6dc5babb276e6d823fd84464ab916f9b9ba5623ca15", - "sha256:e92c2d33858c8f560671b448205a268096e17870dcf60a9bb3ac7bfbafb7f5f9" - ], - "markers": "python_version >= '3.6'", - "version": "==2.1.6" - }, - "zipp": { - "hashes": [ - "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832", - "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc" - ], - "markers": "python_version >= '3.6'", - "version": "==3.6.0" - } - }, - "develop": {} -} From 643b815a1adb825b948dc5d97906123d90d4eefd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Wangryn?= Date: Fri, 21 Oct 2022 18:49:33 +0200 Subject: [PATCH 2966/5615] Add QuestDB module (#5995) Introduce Testcontainers implementation for `QuestDB`, `QuestDBContainer`. --- .github/ISSUE_TEMPLATE/bug_report.yaml | 1 + .github/ISSUE_TEMPLATE/enhancement.yaml | 1 + .github/ISSUE_TEMPLATE/feature.yaml | 1 + .github/dependabot.yml | 5 ++ .github/labeler.yml | 2 + docs/modules/databases/questdb.md | 29 ++++++ mkdocs.yml | 1 + modules/questdb/build.gradle | 13 +++ .../containers/QuestDBContainer.java | 88 +++++++++++++++++++ .../containers/QuestDBProvider.java | 14 +++ ...s.containers.JdbcDatabaseContainerProvider | 1 + .../org/testcontainers/QuestDBTestImages.java | 7 ++ .../jdbc/questdb/QuestDBJDBCDriverTest.java | 19 ++++ .../junit/questdb/SimpleQuestDBTest.java | 71 +++++++++++++++ 14 files changed, 253 insertions(+) create mode 100644 docs/modules/databases/questdb.md create mode 100644 modules/questdb/build.gradle create mode 100644 modules/questdb/src/main/java/org/testcontainers/containers/QuestDBContainer.java create mode 100644 modules/questdb/src/main/java/org/testcontainers/containers/QuestDBProvider.java create mode 100644 modules/questdb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider create mode 100644 modules/questdb/src/test/java/org/testcontainers/QuestDBTestImages.java create mode 100644 modules/questdb/src/test/java/org/testcontainers/jdbc/questdb/QuestDBJDBCDriverTest.java create mode 100644 modules/questdb/src/test/java/org/testcontainers/junit/questdb/SimpleQuestDBTest.java diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 8196451e88f..37609ce1424 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -41,6 +41,7 @@ body: - PostgreSQL - Presto - Pulsar + - QuestDB - RabbitMQ - Redpanda - Selenium diff --git a/.github/ISSUE_TEMPLATE/enhancement.yaml b/.github/ISSUE_TEMPLATE/enhancement.yaml index 9d65e1f12b8..2bbca5db3ae 100644 --- a/.github/ISSUE_TEMPLATE/enhancement.yaml +++ b/.github/ISSUE_TEMPLATE/enhancement.yaml @@ -41,6 +41,7 @@ body: - PostgreSQL - Presto - Pulsar + - QuestDB - RabbitMQ - Redpanda - Selenium diff --git a/.github/ISSUE_TEMPLATE/feature.yaml b/.github/ISSUE_TEMPLATE/feature.yaml index 581870d976d..d29d0178c39 100644 --- a/.github/ISSUE_TEMPLATE/feature.yaml +++ b/.github/ISSUE_TEMPLATE/feature.yaml @@ -39,6 +39,7 @@ body: - Oracle-XE - OrientDB - PostgreSQL + - QuestDB - Presto - Pulsar - RabbitMQ diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 46c7046300d..2e3afe8fa8a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -195,6 +195,11 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/questdb" + schedule: + interval: "monthly" + open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/r2dbc" schedule: diff --git a/.github/labeler.yml b/.github/labeler.yml index 85144b7d7f9..bda614305fa 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -65,6 +65,8 @@ - modules/presto/* "modules/pulsar": - modules/pulsar/* +"modules/questdb": + - modules/questdb/* "modules/r2dbc": - modules/r2dbc/* "modules/rabbitmq": diff --git a/docs/modules/databases/questdb.md b/docs/modules/databases/questdb.md new file mode 100644 index 00000000000..8783dd4b757 --- /dev/null +++ b/docs/modules/databases/questdb.md @@ -0,0 +1,29 @@ +# QuestDB Module + +Testcontainers module for [QuestDB](https://github.com/questdb/questdb). QuestDB is a high-performance, open-source SQL +database for applications in financial services, IoT, machine learning, DevOps and observability. + +See [Database containers](./index.md) for documentation and usage that is common to all relational database container +types. + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +=== "Gradle" + +```groovy +testImplementation "org.testcontainers:questdb:{{latest_version}}" +``` + +=== "Maven" + +```xml + + + org.testcontainers + questdb + {{latest_version}} + test + +``` diff --git a/mkdocs.yml b/mkdocs.yml index 70421761e02..8871267cace 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -63,6 +63,7 @@ nav: - modules/databases/orientdb.md - modules/databases/postgres.md - modules/databases/presto.md + - modules/databases/questdb.md - modules/databases/tidb.md - modules/databases/trino.md - modules/azure.md diff --git a/modules/questdb/build.gradle b/modules/questdb/build.gradle new file mode 100644 index 00000000000..79fc0793879 --- /dev/null +++ b/modules/questdb/build.gradle @@ -0,0 +1,13 @@ +description = "Testcontainers :: QuestDB" + +dependencies { + api project(':testcontainers') + api project(':jdbc') + + testImplementation 'org.postgresql:postgresql:42.5.0' + testImplementation project(':jdbc-test') + testImplementation 'org.assertj:assertj-core:3.23.1' + testImplementation 'org.questdb:questdb:6.4.3-jdk8' + testImplementation 'org.awaitility:awaitility:4.2.0' + testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' +} diff --git a/modules/questdb/src/main/java/org/testcontainers/containers/QuestDBContainer.java b/modules/questdb/src/main/java/org/testcontainers/containers/QuestDBContainer.java new file mode 100644 index 00000000000..dd933dd6c95 --- /dev/null +++ b/modules/questdb/src/main/java/org/testcontainers/containers/QuestDBContainer.java @@ -0,0 +1,88 @@ +package org.testcontainers.containers; + +import lombok.NonNull; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.utility.DockerImageName; + +/** + * Testcontainers implementation for QuestDB. + * + * @author vangreen + * @author jerrinot + */ +public class QuestDBContainer extends JdbcDatabaseContainer { + + static final String DATABASE_PROVIDER = "postgresql"; + + private static final String DEFAULT_DATABASE_NAME = "qdb"; + + private static final int DEFAULT_COMMIT_LAG_MS = 1000; + + private static final String DEFAULT_USERNAME = "admin"; + + private static final String DEFAULT_PASSWORD = "quest"; + + private static final Integer POSTGRES_PORT = 8812; + + private static final Integer REST_PORT = 9000; + + private static final Integer ILP_PORT = 9009; + + static final String TEST_QUERY = "SELECT 1"; + + static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("questdb/questdb"); + + public QuestDBContainer(@NonNull String dockerImageName) { + this(DockerImageName.parse(dockerImageName)); + } + + public QuestDBContainer(DockerImageName dockerImageName) { + super(dockerImageName); + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + withExposedPorts(POSTGRES_PORT, REST_PORT, ILP_PORT); + addEnv("QDB_CAIRO_COMMIT_LAG", String.valueOf(DEFAULT_COMMIT_LAG_MS)); + waitingFor(Wait.forLogMessage("(?i).*A server-main enjoy.*", 1)); + } + + @Override + public String getDriverClassName() { + return "org.postgresql.Driver"; + } + + @Override + public String getJdbcUrl() { + return String.format("jdbc:postgresql://%s:%d/%s", getHost(), getMappedPort(8812), getDefaultDatabaseName()); + } + + @Override + public String getUsername() { + return DEFAULT_USERNAME; + } + + @Override + public String getPassword() { + return DEFAULT_PASSWORD; + } + + @Override + public String getTestQueryString() { + return TEST_QUERY; + } + + @Override + protected void waitUntilContainerStarted() { + getWaitStrategy().waitUntilReady(this); + } + + public String getDefaultDatabaseName() { + return DEFAULT_DATABASE_NAME; + } + + public String getIlpUrl() { + return getHost() + ":" + getMappedPort(ILP_PORT); + } + + public String getHttpUrl() { + return "http://" + getHost() + ":" + getMappedPort(REST_PORT); + } +} diff --git a/modules/questdb/src/main/java/org/testcontainers/containers/QuestDBProvider.java b/modules/questdb/src/main/java/org/testcontainers/containers/QuestDBProvider.java new file mode 100644 index 00000000000..0d4e9c6eeeb --- /dev/null +++ b/modules/questdb/src/main/java/org/testcontainers/containers/QuestDBProvider.java @@ -0,0 +1,14 @@ +package org.testcontainers.containers; + +public class QuestDBProvider extends JdbcDatabaseContainerProvider { + + @Override + public boolean supports(String databaseType) { + return databaseType.equals(QuestDBContainer.DATABASE_PROVIDER); + } + + @Override + public JdbcDatabaseContainer newInstance(String tag) { + return new QuestDBContainer(QuestDBContainer.DEFAULT_IMAGE_NAME.withTag(tag)); + } +} diff --git a/modules/questdb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider b/modules/questdb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider new file mode 100644 index 00000000000..922a12a1328 --- /dev/null +++ b/modules/questdb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider @@ -0,0 +1 @@ +org.testcontainers.containers.QuestDBProvider diff --git a/modules/questdb/src/test/java/org/testcontainers/QuestDBTestImages.java b/modules/questdb/src/test/java/org/testcontainers/QuestDBTestImages.java new file mode 100644 index 00000000000..555b56f172c --- /dev/null +++ b/modules/questdb/src/test/java/org/testcontainers/QuestDBTestImages.java @@ -0,0 +1,7 @@ +package org.testcontainers; + +import org.testcontainers.utility.DockerImageName; + +public interface QuestDBTestImages { + DockerImageName QUESTDB_IMAGE = DockerImageName.parse("questdb/questdb:6.5.3"); +} diff --git a/modules/questdb/src/test/java/org/testcontainers/jdbc/questdb/QuestDBJDBCDriverTest.java b/modules/questdb/src/test/java/org/testcontainers/jdbc/questdb/QuestDBJDBCDriverTest.java new file mode 100644 index 00000000000..b19c2acc5fd --- /dev/null +++ b/modules/questdb/src/test/java/org/testcontainers/jdbc/questdb/QuestDBJDBCDriverTest.java @@ -0,0 +1,19 @@ +package org.testcontainers.jdbc.questdb; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.testcontainers.jdbc.AbstractJDBCDriverTest; + +import java.util.Arrays; +import java.util.EnumSet; + +@RunWith(Parameterized.class) +public class QuestDBJDBCDriverTest extends AbstractJDBCDriverTest { + + @Parameterized.Parameters(name = "{index} - {0}") + public static Iterable data() { + return Arrays.asList( + new Object[][] { { "jdbc:tc:postgresql://hostname/databasename", EnumSet.of(Options.PmdKnownBroken) } } + ); + } +} diff --git a/modules/questdb/src/test/java/org/testcontainers/junit/questdb/SimpleQuestDBTest.java b/modules/questdb/src/test/java/org/testcontainers/junit/questdb/SimpleQuestDBTest.java new file mode 100644 index 00000000000..c38524018c3 --- /dev/null +++ b/modules/questdb/src/test/java/org/testcontainers/junit/questdb/SimpleQuestDBTest.java @@ -0,0 +1,71 @@ +package org.testcontainers.junit.questdb; + +import io.questdb.client.Sender; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; +import org.junit.Test; +import org.testcontainers.QuestDBTestImages; +import org.testcontainers.containers.QuestDBContainer; +import org.testcontainers.db.AbstractContainerDatabaseTest; + +import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.sql.ResultSet; +import java.sql.SQLException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +public class SimpleQuestDBTest extends AbstractContainerDatabaseTest { + + private static final String TABLE_NAME = "mytable"; + + @Test + public void testSimple() throws SQLException { + try (QuestDBContainer questDB = new QuestDBContainer(QuestDBTestImages.QUESTDB_IMAGE)) { + questDB.start(); + + ResultSet resultSet = performQuery(questDB, questDB.getTestQueryString()); + + int resultSetInt = resultSet.getInt(1); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); + } + } + + @Test + public void testRest() throws IOException { + try (QuestDBContainer questdb = new QuestDBContainer(QuestDBTestImages.QUESTDB_IMAGE)) { + questdb.start(); + populateByInfluxLineProtocol(questdb, 1_000); + try (CloseableHttpClient client = HttpClientBuilder.create().build()) { + String encodedSql = URLEncoder.encode("select * from " + TABLE_NAME, "UTF-8"); + HttpGet httpGet = new HttpGet(questdb.getHttpUrl() + "/exec?query=" + encodedSql); + await() + .untilAsserted(() -> { + try (CloseableHttpResponse response = client.execute(httpGet)) { + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); + String json = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); + assertThat(json.contains("\"count\":1000")).isTrue(); + } + }); + } + } + } + + private static void populateByInfluxLineProtocol(QuestDBContainer questdb, int rowCount) { + try (Sender sender = Sender.builder().address(questdb.getIlpUrl()).build()) { + for (int i = 0; i < rowCount; i++) { + sender + .table(TABLE_NAME) + .symbol("sym", "sym1" + i) + .stringColumn("str", "str1" + i) + .longColumn("long", i) + .atNow(); + } + } + } +} From 323ddc00e0db862e156c7a3e75e02be46c615c49 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Oct 2022 16:50:10 +0000 Subject: [PATCH 2967/5615] Bump client-java from 16.0.0 to 16.0.1 in /modules/k3s Bumps [client-java](https://github.com/kubernetes-client/java) from 16.0.0 to 16.0.1. - [Release notes](https://github.com/kubernetes-client/java/releases) - [Changelog](https://github.com/kubernetes-client/java/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes-client/java/compare/v16.0.0...v16.0.1) --- updated-dependencies: - dependency-name: io.kubernetes:client-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/k3s/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/k3s/build.gradle b/modules/k3s/build.gradle index 0778419a3e2..e5fdb9f148b 100644 --- a/modules/k3s/build.gradle +++ b/modules/k3s/build.gradle @@ -9,6 +9,6 @@ dependencies { shaded 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.8.8' testImplementation 'io.fabric8:kubernetes-client:6.1.1' - testImplementation 'io.kubernetes:client-java:16.0.0' + testImplementation 'io.kubernetes:client-java:16.0.1' testImplementation 'org.assertj:assertj-core:3.23.1' } From dc341029aeb226891070e7a3a8c0681bbc6ce8c1 Mon Sep 17 00:00:00 2001 From: DanielHabenicht Date: Fri, 21 Oct 2022 23:05:19 +0200 Subject: [PATCH 2968/5615] Fix edit link in documentation (#6055) Previously, the edit links always showed a 404 as the `master` branch is not used anymore. Now, the link is updated to use the current `main` branch. --- mkdocs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/mkdocs.yml b/mkdocs.yml index 8871267cace..e8f2a1918b0 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -109,5 +109,6 @@ nav: - contributing.md - contributing_docs.md - bounty.md +edit_uri: edit/main/docs/ extra: latest_version: 1.17.5 From fa9b6b97fa76b2e951b8f8df8b559205a06c30e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Fri, 21 Oct 2022 17:53:45 -0500 Subject: [PATCH 2969/5615] Fix typo in tekton docs --- .../continuous_integration/tekton.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/supported_docker_environment/continuous_integration/tekton.md b/docs/supported_docker_environment/continuous_integration/tekton.md index f8c6fb96adc..82c9f1d9a71 100644 --- a/docs/supported_docker_environment/continuous_integration/tekton.md +++ b/docs/supported_docker_environment/continuous_integration/tekton.md @@ -1,6 +1,6 @@ # Tekton -o enable access to Docker in Tekton, a dind sidecar needs to be added. An example of it can be found +To enable access to Docker in Tekton, a dind sidecar needs to be added. An example of it can be found [here](https://github.com/tektoncd/pipeline/blob/main/examples/v1beta1/taskruns/dind-sidecar.yaml) This is an example From 263282b52900746e00b338f40dc9d32b502c2690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Fri, 21 Oct 2022 18:58:00 -0500 Subject: [PATCH 2970/5615] Add travis minimal configuration (#6056) Fixes #4373 --- .../continuous_integration/travis.md | 14 +++++++++++++- mkdocs.yml | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/supported_docker_environment/continuous_integration/travis.md b/docs/supported_docker_environment/continuous_integration/travis.md index 860c07f675d..f4ea31abaea 100644 --- a/docs/supported_docker_environment/continuous_integration/travis.md +++ b/docs/supported_docker_environment/continuous_integration/travis.md @@ -1,3 +1,15 @@ # Travis -> TODO +To run Testcontainers on TravisCI, docker needs to be installed. The configuration below +is the minimal required config. + +```yaml +language: java +jdk: +- openjdk8 + +services: +- docker + +script: ./mvnw verify +``` diff --git a/mkdocs.yml b/mkdocs.yml index e8f2a1918b0..47b000c89fb 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -100,6 +100,7 @@ nav: - supported_docker_environment/continuous_integration/gitlab_ci.md - supported_docker_environment/continuous_integration/bitbucket_pipelines.md - supported_docker_environment/continuous_integration/tekton.md + - supported_docker_environment/continuous_integration/travis.md - supported_docker_environment/windows.md - supported_docker_environment/logging_config.md - supported_docker_environment/image_registry_rate_limiting.md From 77f71b855d7684930fde15d515d76a5840c2f00d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Mon, 24 Oct 2022 10:23:32 -0500 Subject: [PATCH 2971/5615] Add support for flags in DatastoreEmulatorContainer (#5993) Datastore can start with additional flags such as `--consistency 1.0` which is recommended for testing. --- .../DatastoreEmulatorContainer.java | 23 +++++++++++-- .../DatastoreEmulatorContainerTest.java | 32 +++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java index b0716829875..db01001d5ec 100644 --- a/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java @@ -21,13 +21,32 @@ public class DatastoreEmulatorContainer extends GenericContainer e.contains("--consistency 1.0")); + assertThat(emulator.execInContainer("ls", "/root/.config/").getStdout()).contains("gcloud"); + } + } + + @Test + public void testWithMultipleFlags() throws IOException, InterruptedException { + try ( + DatastoreEmulatorContainer emulator = new DatastoreEmulatorContainer( + "gcr.io/google.com/cloudsdktool/cloud-sdk:367.0.0-emulators" + ) + .withFlags("--consistency 1.0 --data-dir /root/.config/test-gcloud") + ) { + emulator.start(); + + assertThat(emulator.getContainerInfo().getConfig().getCmd()).anyMatch(e -> e.contains("--consistency 1.0")); + assertThat(emulator.execInContainer("ls", "/root/.config/").getStdout()).contains("test-gcloud"); + } + } } From e97b58b65fcfca4628b996f3b120587346d19739 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Tue, 25 Oct 2022 08:51:58 -0500 Subject: [PATCH 2972/5615] Update set-output command in workflows (#6058) `set-output` command has been deprecated and warning messages are printed within the logs. See more [here](https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/) --- .github/workflows/ci-examples.yml | 2 +- .github/workflows/ci.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index 527c2eccfeb..425e9ebd970 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -39,7 +39,7 @@ jobs: run: | TASKS=$(./gradlew --no-daemon --parallel -q testMatrix) echo $TASKS - echo "::set-output name=matrix::{\"gradle_args\":$TASKS}" + echo "matrix={\"gradle_args\":$TASKS}" >> $GITHUB_OUTPUT check: needs: find_gradle_jobs strategy: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b406f256ae2..d240ca2ea79 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: run: | TASKS=$(./gradlew --no-daemon --parallel -q testMatrix) echo $TASKS - echo "::set-output name=matrix::{\"gradle_args\":$TASKS}" + echo "matrix={\"gradle_args\":$TASKS}" >> $GITHUB_OUTPUT check: needs: [find_gradle_jobs] strategy: From 84b1ad4efd55d53c068328fa823c1b02390831b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Tue, 25 Oct 2022 08:52:40 -0500 Subject: [PATCH 2973/5615] Apply spotless and checkstyle to examples (#6061) Currently, spotless and checkstyle has been applied to the core modules. Now, both will execute to core modules and examples. --- build.gradle | 24 +---- examples/build.gradle | 11 +++ examples/cucumber/build.gradle | 1 + .../testcontainers/examples/CucumberTest.java | 7 +- .../org/testcontainers/examples/Stepdefs.java | 36 +++---- examples/immudb/src/test/java/ImmuDbTest.java | 20 ++-- .../kafkacluster/KafkaContainerCluster.java | 93 ++++++++++--------- .../KafkaContainerClusterTest.java | 54 ++++++----- .../linkedcontainer/RedmineClient.java | 5 +- .../linkedcontainer/RedmineClientTest.java | 21 +++-- .../linkedcontainer/RedmineContainer.java | 4 +- .../containers/Neo4jExampleTest.java | 28 +++--- .../main/java/com/mycompany/cache/Cache.java | 3 +- .../com/mycompany/cache/RedisBackedCache.java | 2 + .../src/test/java/RedisBackedCacheTest.java | 13 ++- .../main/java/com/mycompany/cache/Cache.java | 3 +- .../com/mycompany/cache/RedisBackedCache.java | 2 + .../src/test/java/RedisBackedCacheTest.java | 11 ++- .../java/com/example/DemoApplication.java | 1 - .../src/test/java/SeleniumContainerTest.java | 18 ++-- .../main/java/com/example/cache/Cache.java | 3 +- .../com/example/cache/RedisBackedCache.java | 2 + .../com/example/AbstractIntegrationTest.java | 2 +- .../com/example/BarConcreteTestClass.java | 5 +- .../com/example/FooConcreteTestClass.java | 4 +- .../main/java/com/example/SearchEngine.java | 1 - .../main/java/com/example/SearchResult.java | 6 +- .../java/com/example/SolrSearchEngine.java | 14 +-- .../test/java/com/example/SolrQueryTest.java | 48 ++++++---- .../java/com/example/DemoApplication.java | 7 +- .../main/java/com/example/DemoController.java | 1 + .../src/main/java/com/example/DemoEntity.java | 1 - .../main/java/com/example/DemoRepository.java | 4 +- .../main/java/com/example/DemoService.java | 5 +- .../com/example/AbstractIntegrationTest.java | 8 +- .../java/com/example/DemoControllerTest.java | 1 - gradle/spotless.gradle | 23 +++++ 37 files changed, 268 insertions(+), 224 deletions(-) create mode 100644 gradle/spotless.gradle diff --git a/build.gradle b/build.gradle index 9e6aa6020e4..7405154491b 100644 --- a/build.gradle +++ b/build.gradle @@ -32,7 +32,7 @@ subprojects { apply plugin: 'idea' apply plugin: 'io.franzbecker.gradle-lombok' apply from: "$rootDir/gradle/shading.gradle" - apply plugin: 'com.diffplug.spotless' + apply from: "$rootDir/gradle/spotless.gradle" apply plugin: 'checkstyle' apply plugin: 'org.gradle.test-retry' @@ -128,28 +128,6 @@ subprojects { testImplementation 'ch.qos.logback:logback-classic:1.3.3' } - spotless { - java { - toggleOffOn() - removeUnusedImports() - trimTrailingWhitespace() - endWithNewline() - - prettier(['prettier': '2.5.1', 'prettier-plugin-java': '1.6.1']) - .config([ - 'parser' : 'java', - 'tabWidth' : 4, - 'printWidth': 120 - ]) - - importOrder('', 'java', 'javax', '\\#') - } - groovyGradle { - target '**/*.groovy' - greclipse('4.19.0') - } - } - checkstyle { toolVersion = "9.3" configFile = rootProject.file('config/checkstyle/checkstyle.xml') diff --git a/examples/build.gradle b/examples/build.gradle index d340e297d33..b1c31a29c7f 100644 --- a/examples/build.gradle +++ b/examples/build.gradle @@ -1,10 +1,21 @@ // empty build.gradle for dependabot +plugins { + id 'com.diffplug.spotless' version '6.8.0' apply false +} + apply from: "$rootDir/../gradle/ci-support.gradle" subprojects { apply plugin:"java" + apply from: "$rootDir/../gradle/spotless.gradle" + apply plugin: 'checkstyle' repositories { mavenCentral() } + + checkstyle { + toolVersion = "9.3" + configFile = rootProject.file('../config/checkstyle/checkstyle.xml') + } } diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 1b9fe04efd8..49dbd831cf5 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -13,4 +13,5 @@ dependencies { testImplementation 'io.cucumber:cucumber-java:7.8.0' testImplementation 'io.cucumber:cucumber-junit:7.8.0' testImplementation 'org.testcontainers:selenium' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/examples/cucumber/src/test/java/org/testcontainers/examples/CucumberTest.java b/examples/cucumber/src/test/java/org/testcontainers/examples/CucumberTest.java index f55919c0fe6..717c42351a8 100644 --- a/examples/cucumber/src/test/java/org/testcontainers/examples/CucumberTest.java +++ b/examples/cucumber/src/test/java/org/testcontainers/examples/CucumberTest.java @@ -5,8 +5,5 @@ import org.junit.runner.RunWith; @RunWith(Cucumber.class) -@CucumberOptions(plugin = {"pretty"}) -public class CucumberTest { - - -} +@CucumberOptions(plugin = { "pretty" }) +public class CucumberTest {} diff --git a/examples/cucumber/src/test/java/org/testcontainers/examples/Stepdefs.java b/examples/cucumber/src/test/java/org/testcontainers/examples/Stepdefs.java index e1b9c7e81ef..1378100f959 100644 --- a/examples/cucumber/src/test/java/org/testcontainers/examples/Stepdefs.java +++ b/examples/cucumber/src/test/java/org/testcontainers/examples/Stepdefs.java @@ -10,22 +10,23 @@ import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.remote.RemoteWebDriver; import org.testcontainers.containers.BrowserWebDriverContainer; +import org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode; import org.testcontainers.lifecycle.TestDescription; import java.io.File; import java.util.List; import java.util.Optional; -import static junit.framework.TestCase.assertEquals; -import static org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode.RECORD_ALL; +import static org.assertj.core.api.Assertions.assertThat; public class Stepdefs { private BrowserWebDriverContainer container = new BrowserWebDriverContainer() - .withCapabilities(new ChromeOptions()) - .withRecordingMode(RECORD_ALL, new File("build")); + .withCapabilities(new ChromeOptions()) + .withRecordingMode(VncRecordingMode.RECORD_ALL, new File("build")); private String location; + private String answer; @Before @@ -35,17 +36,20 @@ public void beforeScenario() { @After public void afterScenario(Scenario scenario) { - container.afterTest(new TestDescription() { - @Override - public String getTestId() { - return scenario.getId(); - } + container.afterTest( + new TestDescription() { + @Override + public String getTestId() { + return scenario.getId(); + } - @Override - public String getFilesystemFriendlyName() { - return scenario.getName(); - } - }, Optional.of(scenario).filter(Scenario::isFailed).map(__ -> new RuntimeException())); + @Override + public String getFilesystemFriendlyName() { + return scenario.getName(); + } + }, + Optional.of(scenario).filter(Scenario::isFailed).map(__ -> new RuntimeException()) + ); } @Given("^location is \"([^\"]*)\"$") @@ -63,8 +67,6 @@ public void iAskIsItPossibleToSearchHere() throws Exception { @Then("^I should be told \"([^\"]*)\"$") public void iShouldBeTold(String expected) throws Exception { - assertEquals(expected, answer); + assertThat(answer).isEqualTo(expected); } - - } diff --git a/examples/immudb/src/test/java/ImmuDbTest.java b/examples/immudb/src/test/java/ImmuDbTest.java index fd515ddd4aa..f8c208a1073 100644 --- a/examples/immudb/src/test/java/ImmuDbTest.java +++ b/examples/immudb/src/test/java/ImmuDbTest.java @@ -8,6 +8,7 @@ import org.junit.Test; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; + import static org.assertj.core.api.Assertions.assertThat; /** @@ -17,28 +18,33 @@ public class ImmuDbTest { // Default port for the ImmuDb server private static final int IMMUDB_PORT = 3322; + // Default username for the ImmuDb server private final String IMMUDB_USER = "immudb"; + // Default password for the ImmuDb server private final String IMMUDB_PASSWORD = "immudb"; + // Default database name for the ImmuDb server private final String IMMUDB_DATABASE = "defaultdb"; // Test container for the ImmuDb database, with the latest version of the image and exposed port @ClassRule public static final GenericContainer immuDbContainer = new GenericContainer<>("codenotary/immudb:1.3") - .withExposedPorts(IMMUDB_PORT).waitingFor( - Wait.forLogMessage(".*Web API server enabled.*", 1) - ); + .withExposedPorts(IMMUDB_PORT) + .waitingFor(Wait.forLogMessage(".*Web API server enabled.*", 1)); + // ImmuClient used to interact with the DB private ImmuClient immuClient; @Before public void setUp() { - this.immuClient = ImmuClient.newBuilder() - .withServerUrl(immuDbContainer.getHost()) - .withServerPort(immuDbContainer.getMappedPort(IMMUDB_PORT)) - .build(); + this.immuClient = + ImmuClient + .newBuilder() + .withServerUrl(immuDbContainer.getHost()) + .withServerPort(immuDbContainer.getMappedPort(IMMUDB_PORT)) + .build(); this.immuClient.login(IMMUDB_USER, IMMUDB_PASSWORD); this.immuClient.useDatabase(IMMUDB_DATABASE); } diff --git a/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerCluster.java b/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerCluster.java index 398488fe3f9..9ab3f25e0ff 100644 --- a/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerCluster.java +++ b/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerCluster.java @@ -4,10 +4,9 @@ import org.rnorth.ducttape.unreliables.Unreliables; import org.testcontainers.containers.Container; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.KafkaContainer; import org.testcontainers.containers.Network; import org.testcontainers.lifecycle.Startable; -import org.testcontainers.lifecycle.Startables; -import org.testcontainers.containers.KafkaContainer; import org.testcontainers.utility.DockerImageName; import java.time.Duration; @@ -17,16 +16,17 @@ import java.util.stream.IntStream; import java.util.stream.Stream; -import static java.util.concurrent.TimeUnit.SECONDS; - /** * Provides an easy way to launch a Kafka cluster with multiple brokers. */ public class KafkaContainerCluster implements Startable { private final int brokersNum; + private final Network network; + private final GenericContainer zookeeper; + private final Collection brokers; public KafkaContainerCluster(String confluentPlatformVersion, int brokersNum, int internalTopicsRf) { @@ -34,33 +34,39 @@ public KafkaContainerCluster(String confluentPlatformVersion, int brokersNum, in throw new IllegalArgumentException("brokersNum '" + brokersNum + "' must be greater than 0"); } if (internalTopicsRf < 0 || internalTopicsRf > brokersNum) { - throw new IllegalArgumentException("internalTopicsRf '" + internalTopicsRf + "' must be less than brokersNum and greater than 0"); + throw new IllegalArgumentException( + "internalTopicsRf '" + internalTopicsRf + "' must be less than brokersNum and greater than 0" + ); } this.brokersNum = brokersNum; this.network = Network.newNetwork(); - this.zookeeper = new GenericContainer<>(DockerImageName.parse("confluentinc/cp-zookeeper").withTag(confluentPlatformVersion)) - .withNetwork(network) - .withNetworkAliases("zookeeper") - .withEnv("ZOOKEEPER_CLIENT_PORT", String.valueOf(KafkaContainer.ZOOKEEPER_PORT)); - - this.brokers = IntStream - .range(0, this.brokersNum) - .mapToObj(brokerNum -> { - return new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka").withTag(confluentPlatformVersion)) - .withNetwork(this.network) - .withNetworkAliases("broker-" + brokerNum) - .dependsOn(this.zookeeper) - .withExternalZookeeper("zookeeper:" + KafkaContainer.ZOOKEEPER_PORT) - .withEnv("KAFKA_BROKER_ID", brokerNum + "") - .withEnv("KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR", internalTopicsRf + "") - .withEnv("KAFKA_OFFSETS_TOPIC_NUM_PARTITIONS", internalTopicsRf + "") - .withEnv("KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR", internalTopicsRf + "") - .withEnv("KAFKA_TRANSACTION_STATE_LOG_MIN_ISR", internalTopicsRf + "") - .withStartupTimeout(Duration.ofMinutes(1)); - }) - .collect(Collectors.toList()); + this.zookeeper = + new GenericContainer<>(DockerImageName.parse("confluentinc/cp-zookeeper").withTag(confluentPlatformVersion)) + .withNetwork(network) + .withNetworkAliases("zookeeper") + .withEnv("ZOOKEEPER_CLIENT_PORT", String.valueOf(KafkaContainer.ZOOKEEPER_PORT)); + + this.brokers = + IntStream + .range(0, this.brokersNum) + .mapToObj(brokerNum -> { + return new KafkaContainer( + DockerImageName.parse("confluentinc/cp-kafka").withTag(confluentPlatformVersion) + ) + .withNetwork(this.network) + .withNetworkAliases("broker-" + brokerNum) + .dependsOn(this.zookeeper) + .withExternalZookeeper("zookeeper:" + KafkaContainer.ZOOKEEPER_PORT) + .withEnv("KAFKA_BROKER_ID", brokerNum + "") + .withEnv("KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR", internalTopicsRf + "") + .withEnv("KAFKA_OFFSETS_TOPIC_NUM_PARTITIONS", internalTopicsRf + "") + .withEnv("KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR", internalTopicsRf + "") + .withEnv("KAFKA_TRANSACTION_STATE_LOG_MIN_ISR", internalTopicsRf + "") + .withStartupTimeout(Duration.ofMinutes(1)); + }) + .collect(Collectors.toList()); } public Collection getBrokers() { @@ -68,16 +74,11 @@ public Collection getBrokers() { } public String getBootstrapServers() { - return brokers.stream() - .map(KafkaContainer::getBootstrapServers) - .collect(Collectors.joining(",")); + return brokers.stream().map(KafkaContainer::getBootstrapServers).collect(Collectors.joining(",")); } private Stream> allContainers() { - return Stream.concat( - this.brokers.stream(), - Stream.of(this.zookeeper) - ); + return Stream.concat(this.brokers.stream(), Stream.of(this.zookeeper)); } @Override @@ -86,15 +87,23 @@ public void start() { // sequential start to avoid resource contention on CI systems with weaker hardware brokers.forEach(GenericContainer::start); - Unreliables.retryUntilTrue(30, TimeUnit.SECONDS, () -> { - Container.ExecResult result = this.zookeeper.execInContainer( - "sh", "-c", - "zookeeper-shell zookeeper:" + KafkaContainer.ZOOKEEPER_PORT + " ls /brokers/ids | tail -n 1" - ); - String brokers = result.getStdout(); - - return brokers != null && brokers.split(",").length == this.brokersNum; - }); + Unreliables.retryUntilTrue( + 30, + TimeUnit.SECONDS, + () -> { + Container.ExecResult result = + this.zookeeper.execInContainer( + "sh", + "-c", + "zookeeper-shell zookeeper:" + + KafkaContainer.ZOOKEEPER_PORT + + " ls /brokers/ids | tail -n 1" + ); + String brokers = result.getStdout(); + + return brokers != null && brokers.split(",").length == this.brokersNum; + } + ); } @Override diff --git a/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerClusterTest.java b/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerClusterTest.java index a431c14bba3..63253f4b309 100644 --- a/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerClusterTest.java +++ b/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerClusterTest.java @@ -29,9 +29,7 @@ public class KafkaContainerClusterTest { @Test public void testKafkaContainerCluster() throws Exception { - try ( - KafkaContainerCluster cluster = new KafkaContainerCluster("6.2.1", 3, 2) - ) { + try (KafkaContainerCluster cluster = new KafkaContainerCluster("6.2.1", 3, 2)) { cluster.start(); String bootstrapServers = cluster.getBootstrapServers(); @@ -43,24 +41,27 @@ public void testKafkaContainerCluster() throws Exception { protected void testKafkaFunctionality(String bootstrapServers, int partitions, int rf) throws Exception { try ( - AdminClient adminClient = AdminClient.create(ImmutableMap.of( - AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers - )); - + AdminClient adminClient = AdminClient.create( + ImmutableMap.of(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers) + ); KafkaProducer producer = new KafkaProducer<>( ImmutableMap.of( - ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers, - ProducerConfig.CLIENT_ID_CONFIG, UUID.randomUUID().toString() + ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, + bootstrapServers, + ProducerConfig.CLIENT_ID_CONFIG, + UUID.randomUUID().toString() ), new StringSerializer(), new StringSerializer() ); - KafkaConsumer consumer = new KafkaConsumer<>( ImmutableMap.of( - ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers, - ConsumerConfig.GROUP_ID_CONFIG, "tc-" + UUID.randomUUID(), - ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest" + ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, + bootstrapServers, + ConsumerConfig.GROUP_ID_CONFIG, + "tc-" + UUID.randomUUID(), + ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, + "earliest" ), new StringDeserializer(), new StringDeserializer() @@ -75,23 +76,26 @@ protected void testKafkaFunctionality(String bootstrapServers, int partitions, i producer.send(new ProducerRecord<>(topicName, "testcontainers", "rulezzz")).get(); - Unreliables.retryUntilTrue(10, TimeUnit.SECONDS, () -> { - ConsumerRecords records = consumer.poll(Duration.ofMillis(100)); + Unreliables.retryUntilTrue( + 10, + TimeUnit.SECONDS, + () -> { + ConsumerRecords records = consumer.poll(Duration.ofMillis(100)); - if (records.isEmpty()) { - return false; - } + if (records.isEmpty()) { + return false; + } - assertThat(records) - .hasSize(1) - .extracting(ConsumerRecord::topic, ConsumerRecord::key, ConsumerRecord::value) - .containsExactly(tuple(topicName, "testcontainers", "rulezzz")); + assertThat(records) + .hasSize(1) + .extracting(ConsumerRecord::topic, ConsumerRecord::key, ConsumerRecord::value) + .containsExactly(tuple(topicName, "testcontainers", "rulezzz")); - return true; - }); + return true; + } + ); consumer.unsubscribe(); } } - } diff --git a/examples/linked-container/src/main/java/com/example/linkedcontainer/RedmineClient.java b/examples/linked-container/src/main/java/com/example/linkedcontainer/RedmineClient.java index be62d932971..c95ec133e2f 100644 --- a/examples/linked-container/src/main/java/com/example/linkedcontainer/RedmineClient.java +++ b/examples/linked-container/src/main/java/com/example/linkedcontainer/RedmineClient.java @@ -13,6 +13,7 @@ public class RedmineClient { private String url; + private OkHttpClient client; public RedmineClient(String url) { @@ -21,9 +22,7 @@ public RedmineClient(String url) { } public int getIssueCount() throws IOException { - Request request = new Request.Builder() - .url(url + "/issues.json") - .build(); + Request request = new Request.Builder().url(url + "/issues.json").build(); Response response = client.newCall(request).execute(); JSONObject jsonObject = new JSONObject(response.body().string()); diff --git a/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java b/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java index 6e4186698e7..721c00835c9 100644 --- a/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java +++ b/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java @@ -13,25 +13,26 @@ public class RedmineClientTest { private static final String POSTGRES_USERNAME = "redmine"; + private static final String POSTGRES_PASSWORD = "secret"; - private PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer<>(LinkedContainerTestImages.POSTGRES_TEST_IMAGE) - .withUsername(POSTGRES_USERNAME) - .withPassword(POSTGRES_PASSWORD); + private PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer<>( + LinkedContainerTestImages.POSTGRES_TEST_IMAGE + ) + .withUsername(POSTGRES_USERNAME) + .withPassword(POSTGRES_PASSWORD); private RedmineContainer redmineContainer = new RedmineContainer(LinkedContainerTestImages.REDMINE_TEST_IMAGE) - .withLinkToContainer(postgreSQLContainer, "postgres") - .withEnv("POSTGRES_ENV_POSTGRES_USER", POSTGRES_USERNAME) - .withEnv("POSTGRES_ENV_POSTGRES_PASSWORD", POSTGRES_PASSWORD); + .withLinkToContainer(postgreSQLContainer, "postgres") + .withEnv("POSTGRES_ENV_POSTGRES_USER", POSTGRES_USERNAME) + .withEnv("POSTGRES_ENV_POSTGRES_PASSWORD", POSTGRES_PASSWORD); @Rule - public RuleChain chain = RuleChain.outerRule(postgreSQLContainer) - .around(redmineContainer); + public RuleChain chain = RuleChain.outerRule(postgreSQLContainer).around(redmineContainer); @Test public void canGetIssueCount() throws Exception { - RedmineClient redmineClient = new RedmineClient( - redmineContainer.getRedmineUrl()); + RedmineClient redmineClient = new RedmineClient(redmineContainer.getRedmineUrl()); assertThat(redmineClient.getIssueCount()).as("The issue count can be retrieved.").isZero(); } diff --git a/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java b/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java index 4303bbee682..0f0bea36c1a 100644 --- a/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java +++ b/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java @@ -28,8 +28,6 @@ public RedmineContainer withLinkToContainer(LinkableContainer otherContainer, St } public String getRedmineUrl() { - return String.format("http://%s:%d", - this.getHost(), - this.getMappedPort(REDMINE_PORT)); + return String.format("http://%s:%d", this.getHost(), this.getMappedPort(REDMINE_PORT)); } } diff --git a/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java b/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java index bc7aeed10bd..9ce3a3f078c 100644 --- a/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java +++ b/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java @@ -1,5 +1,14 @@ package org.testcontainers.containers; +import org.junit.jupiter.api.Test; +import org.neo4j.driver.AuthTokens; +import org.neo4j.driver.Driver; +import org.neo4j.driver.GraphDatabase; +import org.neo4j.driver.Session; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -10,15 +19,6 @@ import java.util.Collections; import java.util.stream.Collectors; -import org.junit.jupiter.api.Test; -import org.neo4j.driver.AuthTokens; -import org.neo4j.driver.Driver; -import org.neo4j.driver.GraphDatabase; -import org.neo4j.driver.Session; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; -import org.testcontainers.utility.DockerImageName; - import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; @@ -27,19 +27,14 @@ class Neo4jExampleTest { @Container - private static Neo4jContainer neo4jContainer = - new Neo4jContainer<>(DockerImageName.parse("neo4j:4.4")) + private static Neo4jContainer neo4jContainer = new Neo4jContainer<>(DockerImageName.parse("neo4j:4.4")) .withAdminPassword(null); // Disable password @Test void testSomethingUsingBolt() { - // Retrieve the Bolt URL from the container String boltUrl = neo4jContainer.getBoltUrl(); - try ( - Driver driver = GraphDatabase.driver(boltUrl, AuthTokens.none()); - Session session = driver.session() - ) { + try (Driver driver = GraphDatabase.driver(boltUrl, AuthTokens.none()); Session session = driver.session()) { long one = session.run("RETURN 1", Collections.emptyMap()).next().get(0).asLong(); assertThat(one).isEqualTo(1L); } catch (Exception e) { @@ -49,7 +44,6 @@ void testSomethingUsingBolt() { @Test void testSomethingUsingHttp() throws IOException { - // Retrieve the HTTP URL from the container String httpUrl = neo4jContainer.getHttpUrl(); diff --git a/examples/redis-backed-cache-testng/src/main/java/com/mycompany/cache/Cache.java b/examples/redis-backed-cache-testng/src/main/java/com/mycompany/cache/Cache.java index 996c31ecac7..f0c7710b355 100644 --- a/examples/redis-backed-cache-testng/src/main/java/com/mycompany/cache/Cache.java +++ b/examples/redis-backed-cache-testng/src/main/java/com/mycompany/cache/Cache.java @@ -6,7 +6,6 @@ * Cache, for storing data associated with keys. */ public interface Cache { - /** * Store a value object in the cache with no specific expiry time. The object may be evicted by the cache any time, * if necessary. @@ -24,4 +23,4 @@ public interface Cache { * @return the object if it was in the cache, or an empty Optional if not found. */ Optional get(String key, Class expectedClass); -} \ No newline at end of file +} diff --git a/examples/redis-backed-cache-testng/src/main/java/com/mycompany/cache/RedisBackedCache.java b/examples/redis-backed-cache-testng/src/main/java/com/mycompany/cache/RedisBackedCache.java index 2c80818c150..8349de77869 100644 --- a/examples/redis-backed-cache-testng/src/main/java/com/mycompany/cache/RedisBackedCache.java +++ b/examples/redis-backed-cache-testng/src/main/java/com/mycompany/cache/RedisBackedCache.java @@ -11,7 +11,9 @@ public class RedisBackedCache implements Cache { private final Jedis jedis; + private final String cacheName; + private final Gson gson; public RedisBackedCache(Jedis jedis, String cacheName) { diff --git a/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java b/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java index e02b271f290..45c482c098e 100644 --- a/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java +++ b/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java @@ -17,13 +17,14 @@ */ public class RedisBackedCacheTest { - private static GenericContainer redis = new GenericContainer<>(DockerImageName.parse("redis:3.0.6")).withExposedPorts(6379); + private static GenericContainer redis = new GenericContainer<>(DockerImageName.parse("redis:3.0.6")) + .withExposedPorts(6379); private Cache cache; @BeforeClass public static void startContainer() { - redis.start(); + redis.start(); } @AfterClass @@ -44,13 +45,17 @@ public void testFindingAnInsertedValue() { Optional foundObject = cache.get("foo", String.class); assertThat(foundObject).as("When an object in the cache is retrieved, it can be found").isPresent(); - assertThat(foundObject).as("When we put a String in to the cache and retrieve it, the value is the same").contains("FOO"); + assertThat(foundObject) + .as("When we put a String in to the cache and retrieve it, the value is the same") + .contains("FOO"); } @Test public void testNotFindingAValueThatWasNotInserted() { Optional foundObject = cache.get("bar", String.class); - assertThat(foundObject).as("When an object that's not in the cache is retrieved, nothing is found").isNotPresent(); + assertThat(foundObject) + .as("When an object that's not in the cache is retrieved, nothing is found") + .isNotPresent(); } } diff --git a/examples/redis-backed-cache/src/main/java/com/mycompany/cache/Cache.java b/examples/redis-backed-cache/src/main/java/com/mycompany/cache/Cache.java index 996c31ecac7..f0c7710b355 100644 --- a/examples/redis-backed-cache/src/main/java/com/mycompany/cache/Cache.java +++ b/examples/redis-backed-cache/src/main/java/com/mycompany/cache/Cache.java @@ -6,7 +6,6 @@ * Cache, for storing data associated with keys. */ public interface Cache { - /** * Store a value object in the cache with no specific expiry time. The object may be evicted by the cache any time, * if necessary. @@ -24,4 +23,4 @@ public interface Cache { * @return the object if it was in the cache, or an empty Optional if not found. */ Optional get(String key, Class expectedClass); -} \ No newline at end of file +} diff --git a/examples/redis-backed-cache/src/main/java/com/mycompany/cache/RedisBackedCache.java b/examples/redis-backed-cache/src/main/java/com/mycompany/cache/RedisBackedCache.java index 2c80818c150..8349de77869 100644 --- a/examples/redis-backed-cache/src/main/java/com/mycompany/cache/RedisBackedCache.java +++ b/examples/redis-backed-cache/src/main/java/com/mycompany/cache/RedisBackedCache.java @@ -11,7 +11,9 @@ public class RedisBackedCache implements Cache { private final Jedis jedis; + private final String cacheName; + private final Gson gson; public RedisBackedCache(Jedis jedis, String cacheName) { diff --git a/examples/redis-backed-cache/src/test/java/RedisBackedCacheTest.java b/examples/redis-backed-cache/src/test/java/RedisBackedCacheTest.java index 24a21723b15..e820646a0da 100644 --- a/examples/redis-backed-cache/src/test/java/RedisBackedCacheTest.java +++ b/examples/redis-backed-cache/src/test/java/RedisBackedCacheTest.java @@ -18,7 +18,8 @@ public class RedisBackedCacheTest { @Rule public GenericContainer redis = new GenericContainer<>(DockerImageName.parse("redis:3.0.6")) - .withExposedPorts(6379); + .withExposedPorts(6379); + private Cache cache; @Before @@ -34,13 +35,17 @@ public void testFindingAnInsertedValue() { Optional foundObject = cache.get("foo", String.class); assertThat(foundObject.isPresent()).as("When an object in the cache is retrieved, it can be found").isTrue(); - assertThat(foundObject.get()).as("When we put a String in to the cache and retrieve it, the value is the same").isEqualTo("FOO"); + assertThat(foundObject.get()) + .as("When we put a String in to the cache and retrieve it, the value is the same") + .isEqualTo("FOO"); } @Test public void testNotFindingAValueThatWasNotInserted() { Optional foundObject = cache.get("bar", String.class); - assertThat(foundObject.isPresent()).as("When an object that's not in the cache is retrieved, nothing is found").isFalse(); + assertThat(foundObject.isPresent()) + .as("When an object that's not in the cache is retrieved, nothing is found") + .isFalse(); } } diff --git a/examples/selenium-container/src/main/java/com/example/DemoApplication.java b/examples/selenium-container/src/main/java/com/example/DemoApplication.java index 909a1ec36e1..6c42baef27d 100644 --- a/examples/selenium-container/src/main/java/com/example/DemoApplication.java +++ b/examples/selenium-container/src/main/java/com/example/DemoApplication.java @@ -9,5 +9,4 @@ public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } - } diff --git a/examples/selenium-container/src/test/java/SeleniumContainerTest.java b/examples/selenium-container/src/test/java/SeleniumContainerTest.java index c28c9bc54cb..52871c4f37e 100644 --- a/examples/selenium-container/src/test/java/SeleniumContainerTest.java +++ b/examples/selenium-container/src/test/java/SeleniumContainerTest.java @@ -7,8 +7,8 @@ import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.remote.RemoteWebDriver; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.web.context.WebServerInitializedEvent; import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.boot.web.context.WebServerInitializedEvent; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; @@ -16,12 +16,12 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.testcontainers.Testcontainers; import org.testcontainers.containers.BrowserWebDriverContainer; +import org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode; import java.io.File; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; -import static org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode.RECORD_ALL; /** * Simple example of plain Selenium usage. @@ -36,8 +36,8 @@ public class SeleniumContainerTest { @Rule public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() - .withCapabilities(new ChromeOptions()) - .withRecordingMode(RECORD_ALL, new File("build")); + .withCapabilities(new ChromeOptions()) + .withRecordingMode(VncRecordingMode.RECORD_ALL, new File("build")); @Test public void simplePlainSeleniumTest() { @@ -50,12 +50,14 @@ public void simplePlainSeleniumTest() { } public static class Initializer implements ApplicationContextInitializer { + @Override public void initialize(ConfigurableApplicationContext applicationContext) { - applicationContext.addApplicationListener((ApplicationListener) event -> { - Testcontainers.exposeHostPorts(event.getWebServer().getPort()); - }); + applicationContext.addApplicationListener( + (ApplicationListener) event -> { + Testcontainers.exposeHostPorts(event.getWebServer().getPort()); + } + ); } } - } diff --git a/examples/singleton-container/src/main/java/com/example/cache/Cache.java b/examples/singleton-container/src/main/java/com/example/cache/Cache.java index 986838d20f9..8a0c5776d80 100644 --- a/examples/singleton-container/src/main/java/com/example/cache/Cache.java +++ b/examples/singleton-container/src/main/java/com/example/cache/Cache.java @@ -3,8 +3,7 @@ import java.util.Optional; public interface Cache { - void put(String key, Object value); Optional get(String key, Class expectedClass); -} \ No newline at end of file +} diff --git a/examples/singleton-container/src/main/java/com/example/cache/RedisBackedCache.java b/examples/singleton-container/src/main/java/com/example/cache/RedisBackedCache.java index 34553dde1f4..430afb2b1f5 100644 --- a/examples/singleton-container/src/main/java/com/example/cache/RedisBackedCache.java +++ b/examples/singleton-container/src/main/java/com/example/cache/RedisBackedCache.java @@ -8,7 +8,9 @@ public class RedisBackedCache implements Cache { private final Jedis jedis; + private final String cacheName; + private final Gson gson; public RedisBackedCache(Jedis jedis, String cacheName) { diff --git a/examples/singleton-container/src/test/java/com/example/AbstractIntegrationTest.java b/examples/singleton-container/src/test/java/com/example/AbstractIntegrationTest.java index d152540fb1c..729e9fb124d 100644 --- a/examples/singleton-container/src/test/java/com/example/AbstractIntegrationTest.java +++ b/examples/singleton-container/src/test/java/com/example/AbstractIntegrationTest.java @@ -6,7 +6,7 @@ public abstract class AbstractIntegrationTest { public static final GenericContainer redis = new GenericContainer<>(DockerImageName.parse("redis:3.0.6")) - .withExposedPorts(6379); + .withExposedPorts(6379); static { redis.start(); diff --git a/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java b/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java index fff6e1a4a3a..6b9050be277 100644 --- a/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java +++ b/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java @@ -27,7 +27,8 @@ public void testInsertValue() { Optional foundObject = cache.get("bar", String.class); assertThat(foundObject).as("When inserting an object into the cache, it can be retrieved").isPresent(); - assertThat(foundObject).as("When accessing the value of a retrieved object, the value must be the same").contains("BAR"); + assertThat(foundObject) + .as("When accessing the value of a retrieved object, the value must be the same") + .contains("BAR"); } - } diff --git a/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java b/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java index 11ad98a0580..ea071202dcd 100644 --- a/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java +++ b/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java @@ -27,6 +27,8 @@ public void testInsertValue() { Optional foundObject = cache.get("foo", String.class); assertThat(foundObject).as("When inserting an object into the cache, it can be retrieved").isPresent(); - assertThat(foundObject).as("When accessing the value of a retrieved object, the value must be the same").contains("FOO"); + assertThat(foundObject) + .as("When accessing the value of a retrieved object, the value must be the same") + .contains("FOO"); } } diff --git a/examples/solr-container/src/main/java/com/example/SearchEngine.java b/examples/solr-container/src/main/java/com/example/SearchEngine.java index c05bc23aad2..397327fe369 100644 --- a/examples/solr-container/src/main/java/com/example/SearchEngine.java +++ b/examples/solr-container/src/main/java/com/example/SearchEngine.java @@ -1,6 +1,5 @@ package com.example; public interface SearchEngine { - public SearchResult search(String term); } diff --git a/examples/solr-container/src/main/java/com/example/SearchResult.java b/examples/solr-container/src/main/java/com/example/SearchResult.java index 06544f0ba34..89722d1a5a4 100644 --- a/examples/solr-container/src/main/java/com/example/SearchResult.java +++ b/examples/solr-container/src/main/java/com/example/SearchResult.java @@ -1,13 +1,13 @@ package com.example; -import java.util.List; -import java.util.Map; - import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import java.util.List; +import java.util.Map; + @Data @Builder @NoArgsConstructor diff --git a/examples/solr-container/src/main/java/com/example/SolrSearchEngine.java b/examples/solr-container/src/main/java/com/example/SolrSearchEngine.java index 8a7f38e455d..3be96f76690 100644 --- a/examples/solr-container/src/main/java/com/example/SolrSearchEngine.java +++ b/examples/solr-container/src/main/java/com/example/SolrSearchEngine.java @@ -1,16 +1,15 @@ package com.example; -import java.util.stream.Collectors; - import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; - import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.client.solrj.util.ClientUtils; import org.apache.solr.common.SolrDocument; +import java.util.stream.Collectors; + @RequiredArgsConstructor public class SolrSearchEngine implements SearchEngine { @@ -20,7 +19,6 @@ public class SolrSearchEngine implements SearchEngine { @SneakyThrows public SearchResult search(String term) { - SolrQuery query = new SolrQuery(); query.setQuery("title:" + ClientUtils.escapeQueryChars(term)); QueryResponse response = client.query(COLLECTION_NAME, query); @@ -28,12 +26,10 @@ public SearchResult search(String term) { } private SearchResult createResult(QueryResponse response) { - return SearchResult.builder() + return SearchResult + .builder() .totalHits(response.getResults().getNumFound()) - .results(response.getResults() - .stream() - .map(SolrDocument::getFieldValueMap) - .collect(Collectors.toList())) + .results(response.getResults().stream().map(SolrDocument::getFieldValueMap).collect(Collectors.toList())) .build(); } } diff --git a/examples/solr-container/src/test/java/com/example/SolrQueryTest.java b/examples/solr-container/src/test/java/com/example/SolrQueryTest.java index 6443a19b869..e65504c0276 100644 --- a/examples/solr-container/src/test/java/com/example/SolrQueryTest.java +++ b/examples/solr-container/src/test/java/com/example/SolrQueryTest.java @@ -15,7 +15,6 @@ import java.util.HashMap; import java.util.Map; -import static com.example.SolrSearchEngine.COLLECTION_NAME; import static org.assertj.core.api.Assertions.assertThat; public class SolrQueryTest { @@ -23,31 +22,44 @@ public class SolrQueryTest { private static final DockerImageName SOLR_IMAGE = DockerImageName.parse("solr:8.3.0"); public static final SolrContainer solrContainer = new SolrContainer(SOLR_IMAGE) - .withCollection(COLLECTION_NAME); + .withCollection(SolrSearchEngine.COLLECTION_NAME); private static SolrClient solrClient; @BeforeClass public static void setUp() throws IOException, SolrServerException { solrContainer.start(); - solrClient = new Http2SolrClient.Builder("http://" + solrContainer.getHost() + ":" + solrContainer.getSolrPort() + "/solr").build(); + solrClient = + new Http2SolrClient.Builder( + "http://" + solrContainer.getHost() + ":" + solrContainer.getSolrPort() + "/solr" + ) + .build(); // Add Sample Data - solrClient.add(COLLECTION_NAME, Collections.singletonList( - new SolrInputDocument(createMap( - "id", createInputField("id", "1"), - "title", createInputField("title", "old skool - trainers - shoes") - )) - )); - - solrClient.add(COLLECTION_NAME, Collections.singletonList( - new SolrInputDocument(createMap( - "id", createInputField("id", "2"), - "title", createInputField("title", "print t-shirt") - )) - )); - - solrClient.commit(COLLECTION_NAME); + solrClient.add( + SolrSearchEngine.COLLECTION_NAME, + Collections.singletonList( + new SolrInputDocument( + createMap( + "id", + createInputField("id", "1"), + "title", + createInputField("title", "old skool - trainers - shoes") + ) + ) + ) + ); + + solrClient.add( + SolrSearchEngine.COLLECTION_NAME, + Collections.singletonList( + new SolrInputDocument( + createMap("id", createInputField("id", "2"), "title", createInputField("title", "print t-shirt")) + ) + ) + ); + + solrClient.commit(SolrSearchEngine.COLLECTION_NAME); } @Test diff --git a/examples/spring-boot/src/main/java/com/example/DemoApplication.java b/examples/spring-boot/src/main/java/com/example/DemoApplication.java index c968eb42b78..af96419ad6a 100644 --- a/examples/spring-boot/src/main/java/com/example/DemoApplication.java +++ b/examples/spring-boot/src/main/java/com/example/DemoApplication.java @@ -8,8 +8,7 @@ @EnableJpaRepositories public class DemoApplication { - public static void main(String[] args) { - SpringApplication.run(DemoApplication.class, args); - } - + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } } diff --git a/examples/spring-boot/src/main/java/com/example/DemoController.java b/examples/spring-boot/src/main/java/com/example/DemoController.java index 79f48711cad..90cea36804e 100644 --- a/examples/spring-boot/src/main/java/com/example/DemoController.java +++ b/examples/spring-boot/src/main/java/com/example/DemoController.java @@ -7,6 +7,7 @@ public class DemoController { private final StringRedisTemplate stringRedisTemplate; + private final DemoService demoService; public DemoController(StringRedisTemplate stringRedisTemplate, DemoService demoService) { diff --git a/examples/spring-boot/src/main/java/com/example/DemoEntity.java b/examples/spring-boot/src/main/java/com/example/DemoEntity.java index 47c0fa94f75..71fceb2c8e8 100644 --- a/examples/spring-boot/src/main/java/com/example/DemoEntity.java +++ b/examples/spring-boot/src/main/java/com/example/DemoEntity.java @@ -7,7 +7,6 @@ import javax.persistence.GeneratedValue; import javax.persistence.Id; - @Data @Entity public class DemoEntity { diff --git a/examples/spring-boot/src/main/java/com/example/DemoRepository.java b/examples/spring-boot/src/main/java/com/example/DemoRepository.java index 89f3775deb0..8ed3216cc18 100644 --- a/examples/spring-boot/src/main/java/com/example/DemoRepository.java +++ b/examples/spring-boot/src/main/java/com/example/DemoRepository.java @@ -1,7 +1,5 @@ package com.example; - import org.springframework.data.jpa.repository.JpaRepository; -public interface DemoRepository extends JpaRepository { -} +public interface DemoRepository extends JpaRepository {} diff --git a/examples/spring-boot/src/main/java/com/example/DemoService.java b/examples/spring-boot/src/main/java/com/example/DemoService.java index 18e8690a99f..78e8c41e907 100644 --- a/examples/spring-boot/src/main/java/com/example/DemoService.java +++ b/examples/spring-boot/src/main/java/com/example/DemoService.java @@ -4,15 +4,14 @@ @Service public class DemoService { + private final DemoRepository demoRepository; public DemoService(DemoRepository demoRepository) { this.demoRepository = demoRepository; } - public DemoEntity getDemoEntity(Long id) { - return demoRepository.findById(id) - .orElseThrow(() ->new RuntimeException("Entity not found")); + return demoRepository.findById(id).orElseThrow(() -> new RuntimeException("Entity not found")); } } diff --git a/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java b/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java index dd1b7b6fb5a..8a1a508c0b3 100644 --- a/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java +++ b/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java @@ -11,9 +11,11 @@ import org.testcontainers.utility.DockerImageName; @RunWith(SpringJUnit4ClassRunner.class) -@SpringBootTest(classes = DemoApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT, properties = { - "spring.datasource.url=jdbc:tc:postgresql:11-alpine:///databasename", -}) +@SpringBootTest( + classes = DemoApplication.class, + webEnvironment = WebEnvironment.RANDOM_PORT, + properties = { "spring.datasource.url=jdbc:tc:postgresql:11-alpine:///databasename" } +) @ActiveProfiles("test") public abstract class AbstractIntegrationTest { diff --git a/examples/spring-boot/src/test/java/com/example/DemoControllerTest.java b/examples/spring-boot/src/test/java/com/example/DemoControllerTest.java index c305ae67811..4282920b37c 100644 --- a/examples/spring-boot/src/test/java/com/example/DemoControllerTest.java +++ b/examples/spring-boot/src/test/java/com/example/DemoControllerTest.java @@ -33,5 +33,4 @@ public void simpleJPATest() { assertThat(result.getValue()).as("value is set").isEqualTo("Some value"); } - } diff --git a/gradle/spotless.gradle b/gradle/spotless.gradle new file mode 100644 index 00000000000..ce71829331e --- /dev/null +++ b/gradle/spotless.gradle @@ -0,0 +1,23 @@ +apply plugin: 'com.diffplug.spotless' + +spotless { + java { + toggleOffOn() + removeUnusedImports() + trimTrailingWhitespace() + endWithNewline() + + prettier(['prettier': '2.5.1', 'prettier-plugin-java': '1.6.1']) + .config([ + 'parser' : 'java', + 'tabWidth' : 4, + 'printWidth': 120 + ]) + + importOrder('', 'java', 'javax', '\\#') + } + groovyGradle { + target '**/*.groovy' + greclipse('4.19.0') + } +} From b9f48abdee62fea8e94ca39ff5f18016646be9b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Tue, 25 Oct 2022 10:23:05 -0500 Subject: [PATCH 2974/5615] Add zookeeper example (#6059) Demonstrate how to use Apache Zookeeper along with `GenericContainer`. Fixes #1514 --- docs/examples.md | 1 + examples/settings.gradle | 1 + examples/zookeeper/build.gradle | 14 +++++++ .../com/example/ZookeeperContainerTest.java | 41 +++++++++++++++++++ .../src/test/resources/logback-test.xml | 16 ++++++++ 5 files changed, 73 insertions(+) create mode 100644 examples/zookeeper/build.gradle create mode 100644 examples/zookeeper/src/test/java/com/example/ZookeeperContainerTest.java create mode 100644 examples/zookeeper/src/test/resources/logback-test.xml diff --git a/docs/examples.md b/docs/examples.md index e7578c8b375..b7b96ee52bb 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -14,3 +14,4 @@ Examples of different use cases provided by Testcontainers can be found below: - [Spring Boot with Kotlin](https://github.com/testcontainers/testcontainers-java/tree/main/examples/spring-boot-kotlin-redis) - [TestNG](https://github.com/testcontainers/testcontainers-java/tree/main/examples/redis-backed-cache-testng) - [ImmuDb](https://github.com/testcontainers/testcontainers-java/tree/main/examples/immudb) +- [Zookeeper](https://github.com/testcontainers/testcontainers-java/tree/main/examples/zookeper) diff --git a/examples/settings.gradle b/examples/settings.gradle index d26ecef5a98..35b79454204 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -31,6 +31,7 @@ include 'spring-boot' include 'cucumber' include 'spring-boot-kotlin-redis' include 'immudb' +include 'zookeeper' ext.isCI = System.getenv("CI") != null diff --git a/examples/zookeeper/build.gradle b/examples/zookeeper/build.gradle new file mode 100644 index 00000000000..f2c92eb3b1d --- /dev/null +++ b/examples/zookeeper/build.gradle @@ -0,0 +1,14 @@ +plugins { + id 'java' +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation 'org.apache.curator:curator-framework:5.3.0' + testImplementation 'org.testcontainers:testcontainers' + testImplementation 'org.assertj:assertj-core:3.23.1' + testImplementation 'ch.qos.logback:logback-classic:1.2.11' +} diff --git a/examples/zookeeper/src/test/java/com/example/ZookeeperContainerTest.java b/examples/zookeeper/src/test/java/com/example/ZookeeperContainerTest.java new file mode 100644 index 00000000000..a8aad36d201 --- /dev/null +++ b/examples/zookeeper/src/test/java/com/example/ZookeeperContainerTest.java @@ -0,0 +1,41 @@ +package com.example; + +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.retry.RetryOneTime; +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; + +import java.nio.charset.StandardCharsets; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ZookeeperContainerTest { + + private static final int ZOOKEEPER_PORT = 2181; + + @Test + public void test() throws Exception { + String path = "/messages/zk-tc"; + String content = "Running Zookeeper with Testcontainers"; + try ( + GenericContainer zookeeper = new GenericContainer<>("zookeeper:3.8.0").withExposedPorts(ZOOKEEPER_PORT) + ) { + zookeeper.start(); + + String connectionString = zookeeper.getHost() + ":" + zookeeper.getMappedPort(ZOOKEEPER_PORT); + CuratorFramework curatorFramework = CuratorFrameworkFactory + .builder() + .connectString(connectionString) + .retryPolicy(new RetryOneTime(100)) + .build(); + curatorFramework.start(); + curatorFramework.create().creatingParentsIfNeeded().forPath(path, content.getBytes()); + + byte[] bytes = curatorFramework.getData().forPath(path); + curatorFramework.close(); + + assertThat(new String(bytes, StandardCharsets.UTF_8)).isEqualTo(content); + } + } +} diff --git a/examples/zookeeper/src/test/resources/logback-test.xml b/examples/zookeeper/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..83ef7a1a3ef --- /dev/null +++ b/examples/zookeeper/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + From 0b617b4247f098ad538b58c3f012026c1558d59b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Oct 2022 15:24:44 +0000 Subject: [PATCH 2975/5615] Bump logback-classic from 1.4.3 to 1.4.4 in /modules/hivemq Bumps [logback-classic](https://github.com/qos-ch/logback) from 1.4.3 to 1.4.4. - [Release notes](https://github.com/qos-ch/logback/releases) - [Commits](https://github.com/qos-ch/logback/compare/v_1.4.3...v_1.4.4) --- updated-dependencies: - dependency-name: ch.qos.logback:logback-classic dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index 3d6b653f6d1..796099b3a62 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -16,7 +16,7 @@ dependencies { testImplementation("com.hivemq:hivemq-extension-sdk:4.9.0") testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") testImplementation("org.apache.httpcomponents:httpclient:4.5.13") - testImplementation("ch.qos.logback:logback-classic:1.4.3") + testImplementation("ch.qos.logback:logback-classic:1.4.4") testImplementation 'org.assertj:assertj-core:3.23.1' testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.1") } From 50ec3f9b1b2111388e9879bb973761f6a2a02330 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Oct 2022 15:25:10 +0000 Subject: [PATCH 2976/5615] Bump logback-classic from 1.3.3 to 1.3.4 in /examples Bumps [logback-classic](https://github.com/qos-ch/logback) from 1.3.3 to 1.3.4. - [Release notes](https://github.com/qos-ch/logback/releases) - [Commits](https://github.com/qos-ch/logback/compare/v_1.3.3...v_1.3.4) --- updated-dependencies: - dependency-name: ch.qos.logback:logback-classic dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- build.gradle | 2 +- docs/examples/spock/redis/build.gradle | 2 +- examples/kafka-cluster/build.gradle | 2 +- examples/linked-container/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- examples/zookeeper/build.gradle | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index 7405154491b..6c2b6a81c7b 100644 --- a/build.gradle +++ b/build.gradle @@ -125,7 +125,7 @@ subprojects { } dependencies { - testImplementation 'ch.qos.logback:logback-classic:1.3.3' + testImplementation 'ch.qos.logback:logback-classic:1.3.4' } checkstyle { diff --git a/docs/examples/spock/redis/build.gradle b/docs/examples/spock/redis/build.gradle index 6699788d026..390bc06be79 100644 --- a/docs/examples/spock/redis/build.gradle +++ b/docs/examples/spock/redis/build.gradle @@ -7,5 +7,5 @@ dependencies { api "io.lettuce:lettuce-core:5.2.0.RELEASE" testImplementation 'org.spockframework:spock-core:1.2-groovy-2.5' testImplementation project(":spock") - testImplementation 'ch.qos.logback:logback-classic:1.3.3' + testImplementation 'ch.qos.logback:logback-classic:1.3.4' } diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index 5212bc33fa9..46c562bac84 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -13,5 +13,5 @@ dependencies { testImplementation 'org.apache.kafka:kafka-clients:3.3.0' testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'com.google.guava:guava:23.0' - testImplementation 'ch.qos.logback:logback-classic:1.3.3' + testImplementation 'ch.qos.logback:logback-classic:1.3.4' } diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index cf3a3331cc3..08376fd9d3f 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'com.squareup.okhttp3:okhttp:4.10.0' implementation 'org.json:json:20220924' testImplementation 'org.postgresql:postgresql:42.5.0' - testImplementation 'ch.qos.logback:logback-classic:1.3.3' + testImplementation 'ch.qos.logback:logback-classic:1.3.4' testImplementation 'org.testcontainers:postgresql' testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 2e4bda4fa74..5dba21e1c26 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -12,7 +12,7 @@ dependencies { implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' - testImplementation 'ch.qos.logback:logback-classic:1.3.3' + testImplementation 'ch.qos.logback:logback-classic:1.3.4' testImplementation 'org.testng:testng:7.5' testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index eb684158731..0d19207f4ba 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -13,6 +13,6 @@ dependencies { implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'junit:junit:4.13.2' - testImplementation 'ch.qos.logback:logback-classic:1.3.3' + testImplementation 'ch.qos.logback:logback-classic:1.3.4' testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 449fd9ae8e6..8ebce1bfb77 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -13,7 +13,7 @@ dependencies { implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.36' - testImplementation 'ch.qos.logback:logback-classic:1.3.3' + testImplementation 'ch.qos.logback:logback-classic:1.3.4' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/examples/zookeeper/build.gradle b/examples/zookeeper/build.gradle index f2c92eb3b1d..d18be942ff0 100644 --- a/examples/zookeeper/build.gradle +++ b/examples/zookeeper/build.gradle @@ -10,5 +10,5 @@ dependencies { testImplementation 'org.apache.curator:curator-framework:5.3.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.assertj:assertj-core:3.23.1' - testImplementation 'ch.qos.logback:logback-classic:1.2.11' + testImplementation 'ch.qos.logback:logback-classic:1.3.4' } From 46b6cd9bf357a96e01d8e48cf241250c816bb1f3 Mon Sep 17 00:00:00 2001 From: Benjamin Manifold Date: Wed, 26 Oct 2022 06:50:12 -0400 Subject: [PATCH 2977/5615] Update gitlab_ci.md (#6063) Clarity around the port number for unencrypted docker host connections, as most gitlab DinD examples recommend 2736 without explaining why. --- .../continuous_integration/gitlab_ci.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/supported_docker_environment/continuous_integration/gitlab_ci.md b/docs/supported_docker_environment/continuous_integration/gitlab_ci.md index b4571403654..f2226f2d34f 100644 --- a/docs/supported_docker_environment/continuous_integration/gitlab_ci.md +++ b/docs/supported_docker_environment/continuous_integration/gitlab_ci.md @@ -46,7 +46,7 @@ services: command: ["--tls=false"] variables: - # Instruct Testcontainers to use the daemon of DinD. + # Instruct Testcontainers to use the daemon of DinD, use port 2735 for non-tls connections. DOCKER_HOST: "tcp://docker:2375" # Instruct Docker not to start over TLS. DOCKER_TLS_CERTDIR: "" From b0af2f41f98a65f06cb0245a1d7d431db1b7a22b Mon Sep 17 00:00:00 2001 From: Leo Date: Thu, 27 Oct 2022 12:22:53 +0100 Subject: [PATCH 2978/5615] Add language links to docs index (#6067) --- docs/css/extra.css | 39 ++++++++++++++++++++ docs/index.md | 16 +++++++- docs/language-logos/dotnet.svg | 7 ++++ docs/language-logos/go.svg | 10 +++++ docs/language-logos/java.svg | 17 +++++++++ docs/language-logos/javascript.svg | 4 ++ docs/language-logos/python.svg | 8 ++++ docs/language-logos/rust.svg | 57 +++++++++++++++++++++++++++++ docs/logo.png | Bin 84934 -> 67366 bytes 9 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 docs/language-logos/dotnet.svg create mode 100644 docs/language-logos/go.svg create mode 100644 docs/language-logos/java.svg create mode 100644 docs/language-logos/javascript.svg create mode 100644 docs/language-logos/python.svg create mode 100644 docs/language-logos/rust.svg diff --git a/docs/css/extra.css b/docs/css/extra.css index e97480c5341..a89eebc9b24 100644 --- a/docs/css/extra.css +++ b/docs/css/extra.css @@ -9,3 +9,42 @@ h1, h2, h3, h4, h5, h6 { --md-accent-fg-color: #291A3F; --md-typeset-a-color: #0C94AA; } + +.card-grid { + display: grid; + gap: 10px; +} + +@media (min-width: 680px) { + .card-grid { + grid-template-columns: repeat(3, 1fr); + } +} + +body .card-grid-item { + display: flex; + align-items: center; + gap: 20px; + border: 1px solid #C3BEDE; + border-radius: 6px; + padding: 16px; + font-weight: 600; + color: #9991B5; + background: #F2F4FE; +} + +body .card-grid-item:hover, +body .card-grid-item:focus { + color: #9991B5; +} + +.card-grid-item[href] { + color: var(--md-primary-fg-color); + background: transparent; +} + +.card-grid-item[href]:hover, +.card-grid-item[href]:focus { + background: #F2F4FE; + color: var(--md-primary-fg-color); +} \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index 383f42c0245..ed365b2acf3 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,7 +1,21 @@ # Testcontainers -Testcontainers logo +Testcontainers logo + +## Test dependencies as code for your entire stack +Get lightweight and throwaway containers during your tests that let you test against any container image (database, broker, browser, etc..) using one of the several supported languages. + + +

Select your language

+ ## About diff --git a/docs/language-logos/dotnet.svg b/docs/language-logos/dotnet.svg new file mode 100644 index 00000000000..2fb163d4d40 --- /dev/null +++ b/docs/language-logos/dotnet.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/docs/language-logos/go.svg b/docs/language-logos/go.svg new file mode 100644 index 00000000000..bfcca48ceda --- /dev/null +++ b/docs/language-logos/go.svg @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/docs/language-logos/java.svg b/docs/language-logos/java.svg new file mode 100644 index 00000000000..986704918f5 --- /dev/null +++ b/docs/language-logos/java.svg @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/docs/language-logos/javascript.svg b/docs/language-logos/javascript.svg new file mode 100644 index 00000000000..4c808b1bf68 --- /dev/null +++ b/docs/language-logos/javascript.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/docs/language-logos/python.svg b/docs/language-logos/python.svg new file mode 100644 index 00000000000..d06a313c96b --- /dev/null +++ b/docs/language-logos/python.svg @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/docs/language-logos/rust.svg b/docs/language-logos/rust.svg new file mode 100644 index 00000000000..89039333533 --- /dev/null +++ b/docs/language-logos/rust.svg @@ -0,0 +1,57 @@ + + + \ No newline at end of file diff --git a/docs/logo.png b/docs/logo.png index 3d8f03fa0cf0fb98578e83d9b2ff71de3ead11c0..88961b3e3f0c0520a48e91b06e353a73fce35594 100644 GIT binary patch literal 67366 zcmbTe2|SeT`!+tdP_h$A!&sA@Y{d|=B}5XHeJ5+#XU38xd&-`%)1s21q$~+#Us@5_ z8ZCqo#w`Eq9_s1&{=UEWeLw%_^XZ|Cxv%@auJb&P^Ei%klFUyS?B2O|CkzJLZDgo- z5(c9O-_qnW(Sk4g8!N5B7iNFMGeI!eE-vT~4J^Nq4+dj+;b~=jkb`2JCa&`B-pov&~+KdqLbkRhdK4dCy>aXkS;b|Bi=xP~$ z!pb?^+gaTOfz%Q@5~=~-z}Gd{Q7F{c=R%N1s3wB?z8c_r=%3{fLe!T8dut+epbHAw zn3@ae`UScQsmsdCILph+3#qEhDyS+rE2_FVI!OyD$}1|$$*ajJsK_WPYA7AjP*xDy z{tp2z80g}raZ>Nt_T|9eG!Y)b!TuU@av>ohvLQ;ceu3_C3hL_Wa`K9Dii$Ge6*56# z7lIu_WiAAX{C)>L*C6LWPyb*~zY9XpJ32b~T@2PlfW>azg|Gjgx4jUw{iMJ%lM8k9 zms60Hhwh5{LKo*huk*hc=tI4@i?f`okE^fih2S9YT7^Ha_4n`#_6zdx`(NJr&yW9g z0q}H9P5)fuzplmC_s=DQg7q(f+t_{}|Mk^DR$>0GawlDb{4NGMyXs#8%M^j`#$Q7> z(A6>6FVM=*&*%4Eng3q0kfO4zypW`+qqFA)=q3*Q^((G=j=`>)2x#AA6y#+TRjm{h zG!)e|6qF9iD{9EgZ@tvi&&AU%?7zKKNnS%q-|k@RZ9_aAXIS*Mnl7Fq^DyQ zTKN50SfSv{iz<;{`)*_o^ce1yF%+S38g7#+VenybsxxBY^KY7DAa*>>w3j&Hq&uaS z%oU1smZ%l$v|_btS@o(ZO+ff2ht;vGhaFBoK{3-A5naz7*ZcTbHQ+1r zCH^0UPFF=OlG@wN?J_x7<4BY3eWD}#LJCJUag#9i&ePM$eB)_cUHk48Oy3SX%OCyo zR6^L|dLZL_v>;>gGcC{K!*P3+m!5Y$5JQKJf5@=utTOL5+Ls59YsZ&HF$MfY&9ev_ zKlm9N%krv&&7zl|zm3>pqPLQ@92;kQdH%qL&V}$Mz3&OQxbDx4OI6m(v7zB7<3&fL z2*Ly{5u_f2VPaofhVe`%!Jj!Y+QN42RD$k_9 zE$Z<|OE3=kO#INdtK$!gDK;g$nxExx;QP}9-q)say&Dh66{87F9H$Tx29R_MttAte zI!~EK?poNDpcd`MxX+$7?O^ae%;2N?+SQw;<@}rU9}dMb7uzJ6pQe@1XVR}z*g@K` zFhpE4zb!+f_r@!Rd@{-WsP7=L_@QE^d4Yw{)M3e*fp*R%R6=E9f)_9ARD;EXq)?kG zJjy(}soL%HJ)_ezof@-W7?Ums^A5n_6$ccIf)TKchp-yDcXnWc`A006pojk1D71&QjdOo|8B;)s@!MGwdOnlfMwGWnIkiL#b$r z3g%u_UdmoO?8lP=u^Ym}Zc;SdvK+ifnsU`RBT;LQC$9NET-t_pq2W!Uns|d%rZ>F3 zY`2EFZuUyDFCi+BSvFUDYfd;1VBh&jPrZ77)Y>B)fxP#GgH-%r$uGjm*j;ji6*j~~ zP^@*9!edcd4|M5CC$FLv@AG}F#E=D#wM#RwPqTWzbj%DJ=pBtq7?5vH*X6_YpWXTF z(mp@U$x<{gAt6E|SbcJwy;{f8F6yNBFntemo{A-Nv47J2b!H&}T~53DO2yr`3`TVJ z%*+~i#*nM$j&2AG;L^SNXfxgENE=%Di2b)d?C)c9>vFrBY&>JIYRLEC16!jSQe9kJ z523={0Bh0mP#HnDR_Br*zjynJE`7;OZq?A!^(M-ty8gT4E&`I-v)uD;T**j$OalFcN5pmA|9`s$pG;KT6zmX^yLZNN#^8`UN5)3>b+{rk^?(`$w-GV2Fn_!pPUzyP z$(L5YPw0%yG7kC1hP;XS<+8{IV2U0PG@C>Jl@ymu zKbOzdB=RZ3Fz~2+U+0vtr#}Ysa;<%FenLU0Bo+Odv2*~dFjW@$^ND7#nmK;aIN$ag z*T(|!U2a;-+wmYxBa|dBpqsCT1WRSCg&XUvF*4(^neZ$6Ym8417A(metgq@-4mp>J zU36}MzqXjNRG8A|h1uk=R8yub6g2$<2(irpUx!Q3c}C*4DJy12PbcJb6l+VAziEww zP2PCAdR}S0NPYmBVcyQh@X;Z8a_S*S^rZ&>+)Pm~wkFiFM?+CROmlrmesX(D!% z`H}ug?2sot^P?-nTurJ5YhN}!u7p;*4HlmqASoDKXow72jnyxNpBsR48A()zSLbNg zNfXRQPnJ;d`QU%Sw}x$I2AlowaB;*RAjW;;4NC|_F$KN)^7SWfB8p=dN&P{F?jDIx zr`oj9Fprz5o__gbC%s>41y=T2(XvY%bsn(qvtHuDwEI{QTi9O74BPZw{A{PcmJSzF zE@vi)AS!tBZJgA*GyP5_nVk(1A(Tih&EGU7nv-dTcuDDe@w>8jdk&)AMkw5udMzn8 z5(k*mTLzFz6Y8ySP0S`Y>F`*KB!2C~;g*G2OAWT=0sBrGPE_0J;=Y@ibH6HIIp=pc zV^#l2S}x9pPTg;U=Ut3hM=N_c6hw=1J|o4U2pP@@?nxP`1URu8jB?lTR8GA_X!lesbA?SEs7SHBRUckC1nSs@3&2;>lVc0=(;26j5p`Nc=cudZ! z8XkLO7yg#?kv;g++7!?G5j*kNa($iL*GLw7q-Yu;dUQRcRkPt6oXH4A(hZ#JgPH$? z%ZJh4fk%_uX@pQoX0#`6r0hS9^`aX`_H$yNh__8iVDRzsM$}|k!;CS;YopHxfnu4w zV%f)u&3yq9>BBR<8?Q1K(!X-tdNj%gj+L1&;bEEu#;}Jz?zM}Y4sk;L2ZEOWsuKax zdo?SiL2@Nwx4CM`qON8vha-M5jxxP^Q8>cJ^5>QD)iuKU-WXSH80o|2j{O7)vdFWu z_{E#Si!92iHD(ul^An#S{ytmP4ux1Er?$~NnJC%A_9Ot6qJw^(4%O}YgoXaK+NTdI z?skE%Tg$2$Vq<4&rRJrbvtA$}g^eX%!y)OuHvde|l9wLJ`8@qtyWpL1Oi?sf%sI4m z3EPw|Zh~#iXNNg#vv>8qpx1WVFh_hHlI?MQ!mP?Mf322gSCUSnNG`8|0`Cf zwMFua#2pnIj)x<*hrbR#8YZ=#rIkDjo%4*`RAa= z@8*1UxkNZue?q9rBnI}HD@GgLc9fEFRO(LVTp9syp)x>HqLz=TRP-p9wo1Ho>1J8D zp5U@E%)?u|FC8i)vS1~PmM0pK4~{DFY*=U>eROG)ntx8ksakN2>AF|~PgGy`V zaA+L9R8^G7C>3pZ8Xy3>?+JkeV1MSi;Eig-D%mOWlw86p`=BcQTcA+fH< zU#?u4Pfwn`NulRb%o#4BC}(jqopTU;vJcBwm~qs2jcv0v+QI$fQqBDa+V`ATlp~6e zFE4n6nK@^@T>NM`I^=k&a(a!~{^1dhP38w)e-@?^FIw+o>bln|y*l<3b1@AR@6N&K zm)DaXTb`OyvaY!be=Mi10zITyet;tZBrp&SN9;FLAET+RqFH`We3lTHudg@64s{g+ z&AIF(17@|G8%WTcPAG{bs(r`Svs^@l_=KTM=jH0p!QwiH~gD zieo~&t(#c^d_!!yt4ow`>mz74dwAN$w;h+P%70R-D}OCPoejfg7qzqTPm)*dOQ$9g zPGLEtMgN;JcBUD7xcJRrM&WT@$#aaRh1ebCo89kUi%uEwzfNC}bG*$RjAph2h($N8 zL8C5_c+nGYmT}tim{EP(Ny>P~R3M?qlAZNC6Jd?v^V$)@TG(OK7Z^@SQ0U=3w(ksh zqZU?}^1EXIINDlFDH$|2r^xtjBY%})QpqGU>|N}8sd{*D0P3|7KtLe*JQD_8$gL-z ziN#4PmOC4fiBV~UlKoBB>auqXX04MSKkgbqO9ZOR3Y8vs5zuxKdG1W{EBh1c*84hz zQ!)oSniI%*o%jbiV}sv!)6B(TOxEtD-VU)2rtIT_4z5bPGPu;4sXB098QK*7jaGnB z5w(pV*-15`?|h73(N#B&oa5c#=*9bL9&>%5lV`%5Gc1j`8ee@T=X*SPb;>G!07)zR zESdN&K6{kyn3^c}P2HztvdKd2S@y5IBOAh>Q_(xuTPMhCi;Vk%D84mjd+m_V|0ABU z@arJOah=J4aqjk9)`6IMr_BrkKZOA8E@Qk+gw%4Bws=;WIO7oYV%752e;+<+&9^$LZy{UAgb&y!*% zJ?*vd?pBxk=>|BlT=`}LCjbC8um+Es^|A?!v(93P4k_AjeAt0msKXqE0#(310DuUa zzQ7qfakk)wriT?{_iiqcK!U-KND%{+t`IK@r={~BJZ$`cCxfu~bB_WZ&K|0U?>9c< z+xkvA_+ZOY9*N#@)qI~>=|eJ3kWQmr@G~q!d}wSiGKMFZlJYx^{1vUbpE+S})$dUQ z`fI1pmLwhKNm;-S)|`0m`tsMqylf%`Pc6OOh|lg3mu+-?ZooM&lg&j; zYfQ5`-llg}VI7rnVWnO_PQ+a{gLO%2a`0LyUQnMWFN1ik_ds=}F7$i9r6&z)jj_qA zW{V(=EdfJebN-{^EnfbUJCQC&+hz*hh-uXd=Z$@xvAb1$^*$_vVRq=BQ`k})vdCtE7^GGRhSdh$1_Y5p8h?N;h=&e5nGR z!7~}xr2c*LuV zZs2j4&@WxEM;RhtI{ENlHXY&vpS}TEb0vzJN8bNPxYb@&WoE}ER%Udotf^8S`_WXCPv zBgh$$&-5ChE`5nk&Ik_9Z<5yr14ddh=Xs$UN2anU55cTzFW2EF^Zg`7F|8LnZ}_7V zuA-ZCSk6r>8WDM7UR+;W+5`L47QL=YFF8IDG4~EvSv&o0*hg~dX6k}p#5R7T=j=^g zv7|zSe9-EH+lS&ujG=6ECI68A5v#g5c;Gbg{Y=7d440~pIvzB{k5RTCB34rle1C08 zDZ@>)Mj(>u9a;`EH`3@+SVdL`(H_&-S(2rzXy`3JdHw{}UE{J2I5KP!O6T{?Zi6J5 z%an|TJx~zs@ST-j%JZg`%!UUKk^ppFuo@(dy8)1NrX=qQ@$VDSCNOwEMXKq^aI!Rv zY1!CESVqGvnast_amP*y5BD5wwmqvovxk{I!=GrhewY;Z46S`;Q)_RZ>bUf~*8SDce=n=7t!-;@fLfubXDFfLw>hZN)f$mH| zye&INNlW>9N-vk?%lwK}Z$v5@+dFYyROH>@Srik$EPjcmIF0NaJ~@SFL{E=YX;Yj; z_DizGEj4+~EDC7gx@U0gUlK$INV=NOcw(m44>Hcx!%9-%3Gz97xJ8(@UDVKf!nKZ) zO_8f7he)a7ut6UH(X8tH%~2IHy<#w{bn`#Fp{dZ6o;tO)b@e$VrZqjHQVPE_N8Iks zLX)0!!8ayXC$BuTec~xuOi_NX>HM{si=(5Mfe7bJDsta2+#cF$?v-7jlLA)ikcS5G&@>mA7m42=&)Kh$TV z$%+d(dbl)uL#bKRtpG#5QNC8fdrnlFn~>0`aK8u<+PJgWfL`9(ta!vRo1K+zDK=X4 z@o5;V+M%J5Ps*Mkb9|TRy2XDSX7T6}wTA}btGCuk6e_7VhAEUuuf^&IqxDJ|3C^6! zQU(7|d;9{KqcRg`Nf}1ka|Lez1LzQPS=F)qcAc$d3FTpqPx#!PtM-wFeXA+!3GJZ6 ziHUH?p%Fd0=KgJMQ&Q{bs&FepIP{KR?k>HYT{WhH)A?10l*cE2K`Bf~K_HWQ{|uKT zFl`$;T+{@5)Az`BrzPfdJ!^qY{nJ-v-i@%{K$<6gO()(t5e`xh0|O!b>7-se>!)`~ zOyj5b5!Muaq6$YbSE>}u`FaabBV9cJpEwWZ9pU=K$-H5!U zvY-xowKK4i+@1GXg8wDHCxV8+$aH<(g@9?cgHJ~JNvD6GN6^L`~PZoVdS8?bRTQcL`|t;H1bTq5Rp0ki52#VHTK zWBG{lHbH`Q*A)o;g#x2d?WA(FbqAb=x))IXZFMe4h>U2G*EFGW^t zvHDOxzxif{5%gfgJ{(ZBF*uY=YB3oyKv|_5*4Vb+4mwIh$dTvd7dosrV3tWprwNbr zFPES(e)M^;nM?2`XYAPOQS)D&3~Qnfzrc_EA`8M2)W)u21mApAE_tR^A+>bVp_)b= znU9eEXz-m8Wq2X4vJlE*)i`p|$K!7|0KaqZgH3x@?WybQ<${V!x~HawEpPwq?0wo^ zr7c4PtFF?))4$;=`*dBrdXH0xbs;Nhc$dZT^dHik->U?5D8jxhE)tFhWgo8b?2v?) zUPPVs97t6|VKKxPV+Z|hy*g9xnbkdPcUPyjc7J0sU2%*PDDdNG`Xu=?R-nH;b$iCY z;EAkMCj5RcUz3PUbKj*3-f+o^v+EI-M*$#Xr{_eI;wKxk{>MgT}#w#J#u@Oo4Uk{_K&%zJ-Ha z#~gra3L7!%21}8t*z8V``a_edA!iL?msfNz6auysyBN#=x{J*s8YI={>EnNs>Pih= z=^k&nm&|*UP}SYXB>?=nXVlv`ape1nw#C}P;MgJ##X@-7uT~W@zh;Shgo53AD_!e8 z`_%3q0kl!lf|8;8a_P)P9tEu4{9&5KfDN7Lb)cP$cwYz|kwiCVfc?srrI*ftQ=*1U zta3L+w7+q4?h?-~i<9vUM&Hd+=sM_ru)*&66R<(Q+hDFOWQKS|0DGiAT$@LLJA`l$ zLPC=r8_^i@lSQ5C-1J=}=D=6?KJyo{unrT9=n0J7ES&)@p1%3=&%+uoH6n9OATb1B zkUy=IWgoN%?v#alkB@ShNV}^qgRGY1UH^)04vOVp?d48gUiDRTb~j#e3*~KW;bN{V zbg4^GZWB+UZfxd@=bw=Dk{Grc#(l+aq9q@@Gj0~b_k2j`-f$Mviq=|sSFstQZ00B4 z`57zpyT~4-ae#!5e~=*CYuNd<2Q4izu7^xJzqMeQZfXB$0bB3gHRY z8T;2(dEOeWtk2DNzUdGi?yuhxhW-tg3u^iUWWXJ z^&L*^-20;H{h7AT*L$xnz{-zOl6DIs24c^pq4*0$WzK$)Z0(E^9NBQR=Hn>e|F(5Z z*54I1ZWjNZD{M-xZ1`EKc7BJ7=>`11+44DjbiWO4hDlsgyE(*qJ}dwPS|FfUAkCAS z+E${NAgKwJx0X-arD%C!pMW1^`1lj3f0^6>`Lu{NF0?7l9>CM zLS*-~4nhT%1LiZ%>&Gu${k63bsq)ohC7WgE1{g3sVztUVg5{|u?%(e-)++COxVsZ31e!IL@;4s z*{*wNR2RUm_vb#IJUs)x{zw3Hj|itGl);MxKpRt@7D}YW6vsP#e>xQRgzLL$VfcQM zLBq<;dmBas^kwbu0YS8m^n^`KT^1B8f$L|YrZirH{TPM3pmlHHu*PvO!PR-7qb&6o z{pqQJ?xiSSzNG>H;g2Ip9eUb;bOWtXZS{k^+Ag`e3CI7H>uyiG584{<4i@1M7*M_1 z<5@XyB{>|8=oH?BJwc#vxNbI1o@S z-3CJ2mqT_ffhp23=@K${9D+>;A4ocsARpo6&k2T$5gp`&zH>PK8k4IbEFy`?fdnJ% z?@I}K#*wrw)b!17{hRZqM#h6gNCUa((L#?&1so*L_=IJFQTz1fwXB8oI|FzdmgM~Xu5|W6AcEN|S5~oA`_j&IlBrpj`;0*2AVN9CM zs`R912N?QE6|=-7zwVN5=3ntJm>OuJomNt=EMT4gmbVHxF=qiYX{U_524oq-JE#ox ziCdi5S}%b98jI0=Nk${DY-Ub3<>WHpJAL&@#TmIo23XQ!dh#%wm}^os|1Thbcg zlSnU=p9;wS^w(J+BClkFqsmxuJt@w%fYVWBQ;-^sYSjP@R@HNWCi_2-mw8Q8gT}Yd z4T{4YD#3=dz%> zuo(l=m**_3pC)u9l1k7aCPjM6b+`%rZwu`MjheF8Bv|kXlNWJk9JKI^7>h^sOT7{g zGp47IKF3UjX}bQD&y4o$nw!UCp~KKcZ!V3*m2QsaGfl@sgudSyeN<88K_%n&pgrX7 z{fnwT!c|`-2D&4&bF91clwD%Tv5a9Hi<)=o^(oh4;5?R^NBkKZ!`Ol|Cqta&qpEdt zIpRRY0AzlWa)>h4|EzChk3{cXPh(i*Ka|E_JrO7mM9C6iO`slvbsWEA9S~l6;$Lr4ItI#yKP+f zw1l61x{6^H##^-YINC4jj(jXgQ?-~H{&N@<1VCV3#k1yoeDH>zq+n4(h2=>r&QXYz zozl&3UR~^>GSa1+owsB(fKs)^`ACCg?dz@{UK-wG!+=jef0X7K3xO@H|5=L;T8cFl z!5%|qKhn^RhCk5xu-S8}&4)5Px`SqXVo!sL>Aus84@=wKsR-^PXNf+ucAGUImCWDL zV?20o_4Y$HlX{7^>Xv2TVE~jzht~x)qz6diF$-)?SXGsAY$iPd&x%R0Ze@>Q%AAnq z?@C?C3PPkj@BhCcDNa<^l@%_IpTODiQlyxiAYmr#5Xz?$-l!kL4(hZI zak!Ofd?KnF6VbSTvKW287e2C4a*)ZEyZTtZ##f1lDyB1)*DeC~bA7+cKLRtc8mz03 zvpqu`Il~AnP%2_aK?d4VOL=#1C&g~n(@2~4jX?&ILZ-3Pl-Rro@|Hol^`34E1%)Nf zBg+znG>MYe6uJx2$ghZ9FQaPMuVJ;<+*i5S2#mL3I|!R5mj_zy=M>Hh;};i}67FOa z@5en3uVVghaH{MejpTx)097;uyq0@5QyEoZ0y{F5FEB_|l55)F?;I|xCSSN%nL^BD zfZaWRogkM)t}T*XjQ=+0|M~_D7u#&skGt5W$t=|WYngY4sNPx?uOK5V&3o@V3*_L= zM)a}&;!7$K=ECTsON46L&h!AC0q2dIqnE&IP#)1jC3I_cXn*Ns%@{TO_Xpx*a_tX zr1-1UAFpC@L4S_YcA&Mv#{*I50g9TBzsc+L}u#y}R!^MdUTjWMUS?t zB}2B)tVm32#RRr(L(CAs=RBy812@jA*>$$f`hanylStsx*@D|tFB%Ar#$K<+W+xDw zrCsUiw1&knBXf%2oMvlHc-Hj2Vlge|R&^xV+waS#sN6mBn*{u$susbcxwT|X8h|p* z!*+Z9Se@>CQ}r@(=6JzPYP(A%udeHLd7lb+3|c%uicwF&v9*3_u#W80D$@nMVp_eB zQ6?3~czd3lE81XpqXU7g{7`ClV=n^v9L8=z;lF^6ab$YSaHr9*q7TP&>`ZmLgyy`- z(Hk!`b*rME`J&=ireSK@e&PdX zwrAsiWt_n;f6u6foq;7*Q!Mc5akgg1L(ATmJ=+&7*jkK!*e=0FiV`+kTN5ieyrax; z^Ko|HUzdZ3^r*cGG~9iV3YQ|@hT8<_oyW#>&474QVk+8LXb3nIdU-cX_64I)xJWM_ z{AALDnEG5_iZVAo-lWI9v0 zn%--6oA7uMkxoJ^C>As4cc^0Dzg4!FQ%bJ}+~rH-KlU+;-Zq_$>-7(hKnr>e6sots z^!RxGR|5bwEmyz&_1bM-1YKGJ+yHKy6G^z@#f`pTtscF+K+U+td6;mkx5VSG?AbFf zo-^gF$NhY7p42~Zvh}IpWQyN{k6->t5nxmqiQj1kx}*+-w0LdeilrDPS2%l9hiiO- z04)HBg#~X4vHMIS_X2xlJ=9=DIGt2*2?IIVCM^=KnTZ6xR|Cr6nhmnD0QrvnVuD`y zgA|ItD_kP-1V-!^d&&YF!4$1KRkVJ%QssHshMvhhS+VR2c3Wkrb@i4SE(Si4y^1v4 z-E07$uS6+B`bKXd{HclEEhrhs2^WwCFk68aeeFe1*;aES8q@4G!^R8aBZR~x_e5~= z9+P-cjJ{w_e~)YJXnDJY39OXFSHyN{XitpYj?7f_qQ0Q5czxkn2aq_bJd(_T!c{1y zRl!*j_fd+bJ>|g|+Zi!T^+##i%s%MWtv>YF%;bDnl;05+*4u$-UPX0@3a!YzYZo0f z0=$vl_e5}WlC^KC^~vtW*#shv%vlc@cHjX8$!{iftAiQs z3moCvc#;1t*lJ&KdMTQdueBJN!QSV7k6wTx?8rxW`uc{y+1h?s+k91QlrXal+dg-n zsMn7P4<8HzW2q;V)Io1jun%^GXX3Sa38Vm5WydBHH1sJJ2b`szqDPb8yfL&1HE^IHR(=3%6U4yqZbs*nrvo`G)Ark$w0V){lo~zc$aD$v-g_ObCuXC} zCFq0=MsRJwFp1z^3*Bw6LaGIuwa`@S_@0HfLHWYm-u$?U?mQ?Ws9xaz$rV=?BY{Z} zpYxww@gbYfy&waFOnggSI&nX3nhs^5I%B|52jzc2w1T3MxyynRJ#!H@Pj>=Izblp< zFR>6s*Xik<2J42HIFD}j>Cn385Ar0}zoigcX)(30FX7i#!|;o8yiB*T7k*@!+n?^b z#4H0=~dy z@A;tXFsFE%g@h(f%)@^+0$5T10}bhiitxP!3GzAHT-?}&jjK3*7-t>}4NQm$&(^$K z^L5TM9`+;e-`x;A-8~`9Axy*A7F^7P4{71I{FXZqSy~Z{&a-%F_FHfLQnin{#S3YZ z4HISYG>$V&AG>gIZ!9?y8#n}v`@pfcPfRN)nV9HC^|LJNIu8I8oymX^C4`*44RF^i zcm1&w-#9r-H+9$5{O<1;vgk9rMzl<@$g(|gK>k0hl405F(vc9_Xdm{bh8kWdh}mw= zIC5`nQ(!%Wd&=O;<(okW$S{3#{hwGnSWuA;D$=p<2_8H`^=54y$W#o+3FPNu9 zhJ!a&@SD9Wc1|E8#%o~20;^T8r9ZWj`uGo%xy9=0z5Gn|R*XXp+I>-)c7dU5f43pN z%en_}|Asr%U?bswGGJbmh9o|}#P=2b*w5p|Cpf#*JQsc`hwmCOcAN|5zmKuwZB@!W zvxb-jtOXTiTGO@w4g|8Yt-9>Bf;RWq2VnK(#I*h?wUAKfCbhgxA8s?bdM_aMM%RVK zb&e5;b)QZ==UV9dw97yDa|tK*1a6oioyoa~Fe^g#k4AS2_c&udq*4aUqlT(2LC^oY z6a5SD)Qe$UEU31*DZHMhVp3=^XJYVnM5V8k$$@bkpqO4+zeOyxwzsAWW$Dxlx zS?xzL$-p=V&@j@0jf7}&3oM*+mF|3IF)rY|Mn^1QGIPDZ8&uISGMVgd>MK+?>r5cvZm_pgU#tignsRJm2J-UeId*|6LeEOpIv7l%Z+EkaUvuN+tGnTZULNeD^WYUe%%3f z6{7);&qNr_74nUr3~PsU=L6gmku3)U&0x8-I%(5tzNT5?%Ki7REyyX|THo&aw^JAZ zvYT8On;Kx9d?$nci-h~fo_ItRG6;jAD8#xzG7y#4<<1LkYFt)FTH$sCOwolh8Imo+ z({&BkF>Sj{AM*#Ag4NzF%$}aHslo659*m(#EhvJ$r~J_n~8`6LBAvs zcy^6jv)^3rUALx+rdM-~jn?|rLAU@v%yv3lqU)LAwI7sf65A+Ybqrp3?x2-^dJ-Ea zTN{jKs-d^}iZ-9h(jx%Y5S$vma{>pKHjcfew&O?7F}HXixc`kp0EZm_+_;=Px}5*w zPOY=xz>0Fb4os3zUBth(%}9txb2e$~k|ibhO#9d77f^{un51b*t!SKFqDf?Yga+&3 z_5?CLV2Or1-&TpEANu4|J3tQ(@U-IeHxo(JnUQNs5cbqJgUum|Qe^&&lD)2Nci$cu zIki5$_s&nZ)tbH?H9zO9X%I~KHD&)z!g)qsLIi?yil~>fJB`lyn_W6l%zElb6nqg0TD=*ID&AbcupF?3_~?x262KU97=7k~Y|bx4 zG2HR;|M>B#8pU6D0>I0vQ9#WCi8ZIz6(EVO4kR6c)Gt7>u@9kVBRvz?Wjdf(8`hCq znKqN2tpjMbE7u{h@;~Kdt7`haG`su`*FP&NCfAD?xKD!!1PL*_sDu0VKL;zIQz$I^dRYZglQM6+|{}WCdY;t zyKnueWAnQmuZ{trGzWSZmIDlsj2;2S{ym=#a4YV2+#-k>fIAr$vG$hTCbPz+4P1_c zWN>J+WWPTO_a4`3kA1fo0<-=qe`9{RCE#PexS3Cp)Ks$5u|*Ri-)xEBQV% zb%?wjsAd+r$>t^rjdp_K2_V7n*hPI55Qb5b21)&62xKW0o+lF>(tznm>H+*K>n_r> zPg=DJ8_g5K#hZ-Z=p7cAQxMLFZ7Wr+lKQs{c%>g4bmwF>KL1edd-`+PlExvTpFBaF zs-QHkJ&7jkCY6cKN&Jfa)2pT4fR*q5&>nd0eg$xHUR2x>HtREc>A&-rZwXMN3|z`j zN!oL%%?GqM=wU52#6_v{L@d1lN)V3EV#52ySP1qGkLHUf_og-ddAyu;pXn_t)1P_1 zVmN(1zO<|~TIxluBQip%$14}Y<#izy*zF>8;}e0vRsnFcK5ESR7j*UJ z3AqocxlA!FHoU9I56s07&3fy z7bgx}xcAC}9HrNtrk_KlMAVD}x9D}L!tM(167p(Y%jdh&8+eQFOHipn~>e+|6wTSK!NKf8q5+LB~Vm z_Fk98oejA+mMo6yGOWKgCv%ve4xpn)fRV8N3$`%*@%|^(s@&7e3qE#<2Ja(&O2RS z@5xwL%wFey-(Bjv_<>TKl2*XC zsfWQ7@{5E;+vbJAPer41a%YtftSZeHp}t@DBM)bdhW|>C$lAZW9OYvS3)~u92b<7h zL=}1W-Up;TJ2=rx(7@m5%x_fQU%QMnRT7d1e(sF@3mJQ z_E+W~9gN&^7;KFt`E-rxOGHj(Cz};y?R`G6xm@}5?Y(cv$oElCJ%(mRzsMD;xE;6E zL=u11QC6qF>>#im()KiY?s^rHdY^K^B!K-QToAM@3SL0Pf!I~ZH2WS{t)jr3H`AMs zsM|_WGdYSbQp?_RM7?8~bsf*Te=Pb@-Z_q}&w(-DlP~11|9LXys`T=%g!cu0=ORDP zR$VcTA6-6o7}@Nlzc#>*_Iy>SO zGT^VzOrH^(I>0X_F%GvYnTO0<$MC=Hb~RwJeyf0H1bjk<+3}e>0gLosZvwTX#!T>? z@}nu@4Al^A#quasaP?Qg+<5Q`10rJZt&%eS-EC{>iAPE$W)VRv_C-o-jahExr=GjH zuCX&V`NhgK8k;o74{u$+^CC7mE6H$3%GFlbwuosFfB1 z9QEZ4#X(!03U$=EFlspdA_Q=Zb=(h%yb$s@8;=Y z^AZ@C=?OrO9JQ5D!H(gA(YLuuTIpLZ4gpD!<4Xca36JOJc5xd=lAXR~%RRr}Q3aAi z_u(qita{M!eil9)5^s+@w-W1z6nJJHLzNf$=!?CZMaqtgO#cK(g|i-XrFTA-4wJ%Y z+`8trCD?RM{;5NwKN2$OT^@uE$$(Lb*V*-Y9u~A2_pb|&Q!2&woCWtz8*$d|x%U@A zxg*V}iBAvdNN+!lMGku^@v!dj`8MDD4U3H=ES-kEz6xXSJ^E;4<9Db+GZp`10BO51 zP+WIxr^!33Ior+YX7Kj6NC)H5l^v&ms%v65Rp;)3OP0G6_OLIXm9lgCvl8Sb^Sl>M6OLzoAI!bcq#?t$Fi1njE}ag>L5}Q0G@b}+u*dXA{uiw(zxQZB8a(SqS263yh@Rk`_M5G49p;uBhE$B!pV zU_W0h%2N6_-?HH0JM|#DxL8}DIP5wY8RRgdj#Z|D=FZ9DvT7{YNHG3ERYX7r)iVQ@ zrBUGCpnm$W(U?k&a$@f4 zB?_l2X0P|8Ei4A2^nR7rfFQYdx<+1iEywRiZ?DN*VHmh-mEhx>-tW%Yhkbo=YTW3? zP#Fb~h$ZFBuzANIASwKx)Kz|f6k=;JM`9Q-t@P}t3M?jvXV$==j#T_7)O1rp_Ss_X zwPtTpV}$*uYhKr0L1DRFE?1U|P*ZRGB4>>j9blu}oET_M)9#_3&Ho*({ATij2^oC; zkFiI!14{mQo7K601(4HGVlk8-*d{1~KMlN<2EZ8>7f-%U*r%gd0K_yWA(TV{r z3*`xZW70tzMfD#5(C`kGP<#A^H`OgW)!xoF$OQ%gwguJpImWz|8c16JAJ+2WLcO~z zjXLS?=u!Ts2=4qI;Jb162G;&j8z;DE5WkKm8_0h@D6MNnq{W1{3FXk_jx+6jNIr+S zsH`bGLic|F6%QK_IG3n_BP_@1mjBN|Dt4;&{a)QLp}bgGAjdwstD&f*1f=@8!Si?h zQ98DNl{>-5Zl~5%pA$wZ@1BFrf$k7GO)!ZAV%wwCCz0AE<-$@;72D%EYmZ7iDj@#$%*wVe)@L``idJ zv+8(c^wFe#d!O;;mwVoF$1KQA+(mkJTvd0k)Z&x($02jHD6xw&U*w=6AE;tVZ-e|9 z;*8A{uoULhUI&zBY?2W|$K4t7xgm{5stSF~o~Iw8Q4fHU6Q4=k95ks>VjIM){c-?A zte-Bdxgdgnj?GE|T|B(+@BbVw8>V0?R>;&6n(YrxSP5}c-yi4mAKnU$YB`9=td#mtn)U4@J zbr3-F0b=95uwuMkCLDawh|6o}!(haGQbmP=cEP}>FM-bm0#d776LAFKxFfC~ZEB4w zp9E7t=)MP^;4o7UaHUxqCg20wmk<2Nbny&o=jgpbot;v#Z6U4#KU27fNkMC z8BQTL6=U1kKFvXSqWIv{Qq&#CoKit)Io|^f85bfUgIF$P8ldLlt}lSsjMe)omjIWZ zBK{OvHU@=y4(K zyG2s6bhe=UM1jdoy}H);vA?TaB@0OT+v(6sk_Q-s>qEW_+Gzt_~~`+c70kKc9GRY~!F zzm{{(eeU}{SK0xZ_~Su8#p(w8V8SaN2uNg#fwK2;fGWhZ7d9rZw93B;{#`x~H(6Cc_VEB{Q}4daI}B=oc@ISEUh{v^ z4rqC{3ma;P61_=*6I#*(M}pkEdpR3Xl$s%>g1+tljW~j^4wIbHNQUD0IAE249vVm_ zvbieWoGm@~KUm}aYcLbR=vgj9FD3@r0byiw0TKZaXg_db-Ap~6@0*|(?XGUX{SCkB z-}gkPKAUw{T6`25!i1s?g+a&5gV0roPHsjpFQUHF5lWBCr2OroC4MHj=$-hF3;z)` zim99>m$$TpfgT?O8kou5W}Uk_UHd))wXgTZM+_R4)dul@>%XwcfAwGf678Hzg6~~& zr{_YB(#flgs}Ql*jG8D{JUSY8pfd^LrLG7dz%_$T$Bo)Oc;rHOa{{BVd206J@js&h zhVxN3)58mr{s)7B*cZt4r4?%F-PPia0Pa)Y%VNYd-}+4o2_86jq`>r%nbcxe%3THX zX{+xe^xfs$^Z=QFCKI6oE|vPTDS*%|ChFq|ni)Y*AWVJ-%xZ2T)&2r+Jj6_JL8xVQ zaz0?QReK?F!Q=iS%+9L#$zSF56WK@MRi&RM5rg$9ZjCA~664@5Y$P-%YXkEHZTYnT^3bZDSuK0iPqN)*qCp3liiS~m_b;Pc&FmcH`L?|m}noD=)|+SyC#g0RSx!VVlkf0V$~Bq|m5 zO`nnUXKeUubbhJ*Y5Ro=3cB7Rznn3GTpxHRh*EFu-o8Gt&kQAB^e8%a{O&0|B%n!w zM@N?Q7vICNy%K?JjeLM+$3X_@;?dB&##gxv>FLS?hX7U0D?_|nMmfBES<>5bwa}8~ ztxraj==;_&1Zg`f%^n7Q<|#YO%IRFG(c^%QV-g2pXGANw2u%yY1*wV2_EnaS-a_Zk zbE9}+OmBYnSt6GU4Rdn+>PP<7{__nI?q83PlU>05T8CVXpSEc?nBcfh#cAm&Rn`<^ zgG(7F?>ql3H}9`}k+h__K8ri5emVMDFN31q2nq61+B`ciZ_A}ZKd(?e<3k<+78D|S z;+|$z;pr{E@LZ*}e3_YSe8>Q<6>-lrb4!(DfvuBSiYxguGI;k5*W;B-9VZzQlc*Wm z_lS}CjLjkGYhEKHZ@l}2&?w4jRJOWxMWV{BaW}OFoS4|t-u-R4E@Kr6eiF1I>%&AI zb#%L2l@V~K?#&gIe||!ivovqD!?tTg7tk4WhC8NW zK@%D7MgN-QP7UXzcT4xC$=i>o$O|7~GyZ3d#8^=u;JrtQb^%ckZ3w8Bp!5G3T-1Pv z&QSaT9io-L%5Yrb(*Jo|j#wkX{P#+;8oSjpP}J)8r4S94hJ5>a0Yw%vDJ>*C)uz2Q zO)Lw@`asaF6%xd>yZPja$Db>^HOLJD!QsX5{d1Hj41dH5b;Ld4S7#&o&Ku=l0z5;t zCe&$06>lRe`S^ZtWc+`8xlpwA*!zG&2dC9%Y*9Ls-X9Ta`y3zWA{9K2@2&gp5Bu!4 zcs%_d&%By=b0KK(2a(ZU-nR>?^1n;IqpNevjKL@R+oRDYoc**T=@zpYVD+(q8hprBXakiwxdzw^7Cj#@hAY=b@RV~D(L!-A-v9z+IQU#nhITvven(>G z*wdIwZZK$TV24LS{eEd?!qEHMuW`O}^x?ZX)C3W5r3aT6TPANvxZK|tEo)0Gyj!G- zU^L`GLHNM6ACLeb(hTh{pOB5FP`v$gxW@y(^3<1(S0&Yjy}S2Li8E>bxqNiOvau%n zmcpK(6ZGV;ZPv^6sk46?7EQJoF{ln?8V5S@r77l4{*Kz_WaS_KPp9$k4H;7^%t5o; z44GgWwX$y3688of%76VtTf}UJyT(doTI@UiC(avx0>8+gyxQzDcX4g<6A;RSGDpUi zff3lXiB~2q6p3!{z}J8|2ym#_|36s&KS<4Wmep1*OG2{E=MMF})r~*~?5fTpQ1FuG zqHwhqVdc~SxB3P)(~4E_Ed`%0(XG7x+n{A~9&Apm}(L{(R%||KeuP zUc^hr2?BQ%H779%*N1*p4!02{e_s!~e6}A~NooV=x4t!aXm8s>>@SgDa(CuCJ*enL zf7(77GV1I;E_-!uZ8Sk(BEubgJMaIR?%bc@9vOf2uTKDIV%nhH!%&@@t+9DBJ+wDU zkI>38e8ksxnriXa55?t9kW(-Xq440`MpJMEoDJ+C=ZfD)87okZ{Zpb0yU%{+_P;{V zi*CZWN*Dp;!7`f(C=9stH{}>M1Hd9sUuA)OQdcKS(CDZbbMhz5Uw4nV!;2asI0y9m z09W#s%{v7@TWd^H{B*(Ha#68F-oE&9W8vU$`g!#4N&QsaOmO5*nj_(+ zX5{ait&Jh~^X{Pp6!ud?dT2~(snDVmz)XU{1^WL1!2W9?sBz-{0$^8^a0!yF&Ol@u z&u06f^}LN}>?^is$Io2)yZYJNY{P2!gl1Ph8N5|6xfSC1z}1ymwWpDK*Irmz*c9Z* zr3ak-)o=W7t~wJ>pmZtlJPK^eTW_!243P7}66>IJY}kgQ?dYvP>@1*Sl{;#UT5mbmT|E^e_$eT&Is(FGr+a2J50#>iDrNv_n+8vDZ=l}a~f!} zUNWnt+`9sZ!od3U$Gx<=ixtMg5HBN!DV1=!U4FSh-bg@Bm7wqD&pGgY-oK3dBl)Mi zm+xf95S33jc(^JaRzqygGaQpnikl1CzyHX@PV9Yt%TX~wAvHQE9%IBFtoLFAe9c75 zN;x1<#E5?eg=|@9ispwYF^p0yETm<#2qz{D2-9ffv-tvUg+6g;-NHDEurM11!0T4X%D>z0gX<**#qe}|s|2Ki*g#-cv6j6Y z5k2yCX7iCOt>4GW3%<&}KD!590hh-iHsA4g03mHRv;WWqay!_!kr#p~8h4guVR+*05(FWWN$#q@wl zR}*)v-<=mO&N362uYhFB(0IFnxFwTt_`-#VyB=XBtih5?oImk;P0>vZ2cxiXnhRRa z#|UZG2{!mTbBlkkx-syXSsQhZ+lZT|;afJk6z2}m=+jz?S~}+V>-_&EZMeCWYwK^# zB@6g>?{#7rTG01D>kQ%vYK?x3o;4JR@m?Jg{=ih3yy1TBpBVpBa@#=>(ddb%+J?Qq zhlev;xXDLkzc&Tjv@7=LO>(nW?+>P(d1sw{aqE{%r?y#Sh{e|B`epW?Tz3e-^5agj z$)oaDJ0SqGTByBtW&&VEOIF4z@gy0?J33rxai`xUFo`MtwKOex0xg9S&W=gF6#y|b z#3q^ZO;HpYrs#%z&bfaL`gCu1Q5V0MlB1SrWB97%i`tjr%RY{3Aw4Y7W)XTo^IOA} zAtcPO78lmNQuG(Aiy$$&K-IGEuS!3seGRMNsfvha5(6zkZQwE#^UcASX9-(vBU9!; z`9iB&c-Iil;d^!JU%2Z5s?2N+`migdTo=%!WYFEj>DzWmkmgTPn;{pfayQgY3Vs;- z>s~#Qe#BxAclgYpjVb;4V}K86eL7G21LWB<{J0QvriXKEA;7g@@YOjBrJ$x{mhTMA zT`x~9(V~!bU9+LebKn`XYqvlk=ZP7s@9#gAI222J^>qmB%)$IhKLVcpLz|>9s2vyS zpo0xVTMbS*D9r2aUGaS$x;pwd^)P)INcYh-Zh1m~Q4drCZ6&?Pu(AzZ{L5!5u>